api-remote-spec.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. import * as path from 'path'
  2. import { expect } from 'chai'
  3. import { closeWindow } from './window-helpers'
  4. import { ifdescribe } from './spec-helpers';
  5. import { ipcMain, BrowserWindow, nativeImage } from 'electron'
  6. import { serialize, deserialize } from '../lib/common/type-utils';
  7. const features = process.electronBinding('features')
  8. ifdescribe(features.isRemoteModuleEnabled())('remote module', () => {
  9. const fixtures = path.join(__dirname, 'fixtures')
  10. let w = null as unknown as BrowserWindow
  11. beforeEach(async () => {
  12. w = new BrowserWindow({show: false, webPreferences: {nodeIntegration: true}})
  13. await w.loadURL('about:blank')
  14. })
  15. afterEach(async () => {
  16. await closeWindow(w)
  17. })
  18. async function remotely(script: string) {
  19. // executeJavaScript never returns if the script throws an error, so catch
  20. // any errors manually.
  21. const assembledScript = `(function() {
  22. try {
  23. return { result: ${script} }
  24. } catch (e) {
  25. return { error: e.message }
  26. }
  27. })()`
  28. const {result, error} = await w.webContents.executeJavaScript(assembledScript)
  29. if (error) {
  30. throw new Error(error)
  31. }
  32. return result
  33. }
  34. describe('typeUtils serialization/deserialization', () => {
  35. it('serializes and deserializes an empty NativeImage', () => {
  36. const image = nativeImage.createEmpty();
  37. const serializedImage = serialize(image);
  38. const empty = deserialize(serializedImage);
  39. expect(empty.isEmpty()).to.be.true();
  40. expect(empty.getAspectRatio()).to.equal(1);
  41. expect(empty.toDataURL()).to.equal('data:image/png;base64,');
  42. expect(empty.toDataURL({ scaleFactor: 2.0 })).to.equal('data:image/png;base64,');
  43. expect(empty.getSize()).to.deep.equal({ width: 0, height: 0 });
  44. expect(empty.getBitmap()).to.be.empty();
  45. expect(empty.getBitmap({ scaleFactor: 2.0 })).to.be.empty();
  46. expect(empty.toBitmap()).to.be.empty();
  47. expect(empty.toBitmap({ scaleFactor: 2.0 })).to.be.empty();
  48. expect(empty.toJPEG(100)).to.be.empty();
  49. expect(empty.toPNG()).to.be.empty();
  50. expect(empty.toPNG({ scaleFactor: 2.0 })).to.be.empty();
  51. });
  52. it('serializes and deserializes a non-empty NativeImage', () => {
  53. const dataURL = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAFklEQVQYlWP8//8/AwMDEwMDAwMDAwAkBgMBBMzldwAAAABJRU5ErkJggg==';
  54. const image = nativeImage.createFromDataURL(dataURL);
  55. const serializedImage = serialize(image);
  56. const nonEmpty = deserialize(serializedImage);
  57. expect(nonEmpty.isEmpty()).to.be.false();
  58. expect(nonEmpty.getAspectRatio()).to.equal(1);
  59. expect(nonEmpty.toDataURL()).to.not.be.empty();
  60. expect(nonEmpty.toDataURL({ scaleFactor: 1.0 })).to.equal(dataURL);
  61. expect(nonEmpty.getSize()).to.deep.equal({ width: 2, height: 2 });
  62. expect(nonEmpty.getBitmap()).to.not.be.empty();
  63. expect(nonEmpty.toPNG()).to.not.be.empty();
  64. });
  65. it('serializes and deserializes a non-empty NativeImage with multiple representations', () => {
  66. const image = nativeImage.createEmpty();
  67. const dataURL1 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQYlWNgAAIAAAUAAdafFs0AAAAASUVORK5CYII=';
  68. image.addRepresentation({ scaleFactor: 1.0, dataURL: dataURL1 });
  69. const dataURL2 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAFklEQVQYlWP8//8/AwMDEwMDAwMDAwAkBgMBBMzldwAAAABJRU5ErkJggg==';
  70. image.addRepresentation({ scaleFactor: 2.0, dataURL: dataURL2 });
  71. const serializedImage = serialize(image);
  72. const nonEmpty = deserialize(serializedImage);
  73. expect(nonEmpty.isEmpty()).to.be.false();
  74. expect(nonEmpty.getAspectRatio()).to.equal(1);
  75. expect(nonEmpty.getSize()).to.deep.equal({ width: 1, height: 1 });
  76. expect(nonEmpty.getBitmap()).to.not.be.empty();
  77. expect(nonEmpty.getBitmap({ scaleFactor: 1.0 })).to.not.be.empty();
  78. expect(nonEmpty.getBitmap({ scaleFactor: 2.0 })).to.not.be.empty();
  79. expect(nonEmpty.toBitmap()).to.not.be.empty();
  80. expect(nonEmpty.toBitmap({ scaleFactor: 1.0 })).to.not.be.empty();
  81. expect(nonEmpty.toBitmap({ scaleFactor: 2.0 })).to.not.be.empty();
  82. expect(nonEmpty.toPNG()).to.not.be.empty();
  83. expect(nonEmpty.toPNG({ scaleFactor: 1.0 })).to.not.be.empty();
  84. expect(nonEmpty.toPNG({ scaleFactor: 2.0 })).to.not.be.empty();
  85. expect(nonEmpty.toDataURL()).to.not.be.empty();
  86. expect(nonEmpty.toDataURL({ scaleFactor: 1.0 })).to.equal(dataURL1);
  87. expect(nonEmpty.toDataURL({ scaleFactor: 2.0 })).to.equal(dataURL2);
  88. });
  89. it('serializes and deserializes an Array', () => {
  90. const array = [1, 2, 3, 4, 5];
  91. const serialized = serialize(array);
  92. const deserialized = deserialize(serialized);
  93. expect(deserialized).to.deep.equal(array);
  94. });
  95. it('serializes and deserializes a Buffer', () => {
  96. const buffer = Buffer.from('hello world!', 'utf-8');
  97. const serialized = serialize(buffer);
  98. const deserialized = deserialize(serialized);
  99. expect(deserialized).to.deep.equal(buffer);
  100. });
  101. it('serializes and deserializes a Boolean', () => {
  102. const bool = true;
  103. const serialized = serialize(bool);
  104. const deserialized = deserialize(serialized);
  105. expect(deserialized).to.equal(bool);
  106. });
  107. it('serializes and deserializes a Date', () => {
  108. const date = new Date();
  109. const serialized = serialize(date);
  110. const deserialized = deserialize(serialized);
  111. expect(deserialized).to.equal(date);
  112. });
  113. it('serializes and deserializes a Number', () => {
  114. const number = 42;
  115. const serialized = serialize(number);
  116. const deserialized = deserialize(serialized);
  117. expect(deserialized).to.equal(number);
  118. });
  119. it('serializes and deserializes a Regexp', () => {
  120. const regex = new RegExp('ab+c');
  121. const serialized = serialize(regex);
  122. const deserialized = deserialize(serialized);
  123. expect(deserialized).to.equal(regex);
  124. });
  125. it('serializes and deserializes a String', () => {
  126. const str = 'hello world';
  127. const serialized = serialize(str);
  128. const deserialized = deserialize(serialized);
  129. expect(deserialized).to.equal(str);
  130. });
  131. it('serializes and deserializes an Error', () => {
  132. const err = new Error('oh crap');
  133. const serialized = serialize(err);
  134. const deserialized = deserialize(serialized);
  135. expect(deserialized).to.equal(err);
  136. });
  137. it('serializes and deserializes a simple Object', () => {
  138. const obj = { hello: 'world', 'answer-to-everything': 42 };
  139. const serialized = serialize(obj);
  140. const deserialized = deserialize(serialized);
  141. expect(deserialized).to.deep.equal(obj);
  142. });
  143. });
  144. describe('remote.getGlobal filtering', () => {
  145. it('can return custom values', async () => {
  146. w.webContents.once('remote-get-global', (event, name) => {
  147. event.returnValue = name
  148. })
  149. expect(await remotely(`require('electron').remote.getGlobal('test')`)).to.equal('test')
  150. })
  151. it('throws when no returnValue set', async () => {
  152. w.webContents.once('remote-get-global', (event, name) => {
  153. event.preventDefault()
  154. })
  155. await expect(remotely(`require('electron').remote.getGlobal('test')`)).to.eventually.be.rejected(`Blocked remote.getGlobal('test')`)
  156. })
  157. })
  158. describe('remote.getBuiltin filtering', () => {
  159. it('can return custom values', async () => {
  160. w.webContents.once('remote-get-builtin', (event, name) => {
  161. event.returnValue = name
  162. })
  163. expect(await remotely(`require('electron').remote.getBuiltin('test')`)).to.equal('test')
  164. })
  165. it('throws when no returnValue set', async () => {
  166. w.webContents.once('remote-get-builtin', (event, name) => {
  167. event.preventDefault()
  168. })
  169. await expect(remotely(`require('electron').remote.getBuiltin('test')`)).to.eventually.be.rejected(`Blocked remote.getGlobal('test')`)
  170. })
  171. })
  172. describe('remote.require filtering', () => {
  173. it('can return custom values', async () => {
  174. w.webContents.once('remote-require', (event, name) => {
  175. event.returnValue = name
  176. })
  177. expect(await remotely(`require('electron').remote.require('test')`)).to.equal('test')
  178. })
  179. it('throws when no returnValue set', async () => {
  180. w.webContents.once('remote-require', (event, name) => {
  181. event.preventDefault()
  182. })
  183. await expect(remotely(`require('electron').remote.require('test')`)).to.eventually.be.rejected(`Blocked remote.require('test')`)
  184. })
  185. })
  186. describe('remote.getCurrentWindow filtering', () => {
  187. it('can return custom value', async () => {
  188. w.webContents.once('remote-get-current-window', (e) => {
  189. e.returnValue = 'some window'
  190. })
  191. expect(await remotely(`require('electron').remote.getCurrentWindow()`)).to.equal('some window')
  192. })
  193. it('throws when no returnValue set', async () => {
  194. w.webContents.once('remote-get-current-window', (event) => {
  195. event.preventDefault()
  196. })
  197. await expect(remotely(`require('electron').remote.getCurrentWindow()`)).to.eventually.be.rejected(`Blocked remote.getCurrentWindow()`)
  198. })
  199. })
  200. describe('remote.getCurrentWebContents filtering', () => {
  201. it('can return custom value', async () => {
  202. w.webContents.once('remote-get-current-web-contents', (event) => {
  203. event.returnValue = 'some web contents'
  204. })
  205. expect(await remotely(`require('electron').remote.getCurrentWebContents()`)).to.equal('some web contents')
  206. })
  207. it('throws when no returnValue set', async () => {
  208. w.webContents.once('remote-get-current-web-contents', (event) => {
  209. event.preventDefault()
  210. })
  211. await expect(remotely(`require('electron').remote.getCurrentWebContents()`)).to.eventually.be.rejected(`Blocked remote.getCurrentWebContents()`)
  212. })
  213. })
  214. describe('remote references', () => {
  215. it('render-view-deleted is sent when page is destroyed', (done) => {
  216. w.webContents.once('render-view-deleted' as any, () => {
  217. done()
  218. })
  219. w.destroy()
  220. })
  221. // The ELECTRON_BROWSER_CONTEXT_RELEASE message relies on this to work.
  222. it('message can be sent on exit when page is being navigated', (done) => {
  223. after(() => { ipcMain.removeAllListeners('SENT_ON_EXIT') })
  224. ipcMain.once('SENT_ON_EXIT', () => {
  225. done()
  226. })
  227. w.webContents.once('did-finish-load', () => {
  228. w.webContents.loadURL('about:blank')
  229. })
  230. w.loadFile(path.join(fixtures, 'api', 'send-on-exit.html'))
  231. })
  232. })
  233. })