From fb1c66966ca8a198ba34f56098f254f8a3dff386 Mon Sep 17 00:00:00 2001 From: Anders Kaseorg Date: Mon, 28 Feb 2022 20:17:50 -0800 Subject: [PATCH 1/7] fix: Capture Promise global to avoid userland mutation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This an equivalent of https://github.com/electron/electron/pull/20925. It’s needed for us to pass the test suite when we’re actually testing this module instead of Electron’s builtin remote. Signed-off-by: Anders Kaseorg --- src/main/server.ts | 2 ++ src/renderer/remote.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/main/server.ts b/src/main/server.ts index abdd4c9..c5799a3 100644 --- a/src/main/server.ts +++ b/src/main/server.ts @@ -6,6 +6,8 @@ import { ipcMain, WebContents, IpcMainEvent, app } from 'electron' import { IPC_MESSAGES } from '../common/ipc-messages'; import { getElectronBinding } from '../common/get-electron-binding' +const { Promise } = global + const v8Util = getElectronBinding('v8_util') const hasWebPrefsRemoteModuleAPI = (() => { diff --git a/src/renderer/remote.ts b/src/renderer/remote.ts index 445adab..f945dfa 100644 --- a/src/renderer/remote.ts +++ b/src/renderer/remote.ts @@ -6,6 +6,8 @@ import { browserModuleNames } from '../common/module-names' import { getElectronBinding } from '../common/get-electron-binding' import { IPC_MESSAGES } from '../common/ipc-messages'; +const { Promise } = global + const callbacksRegistry = new CallbacksRegistry() const remoteObjectCache = new Map() const finalizationRegistry = new FinalizationRegistry((id: number) => { From 205c5104ded72f7dff94a0bff8fc24d77af88bd1 Mon Sep 17 00:00:00 2001 From: Anders Kaseorg Date: Mon, 28 Feb 2022 20:58:29 -0800 Subject: [PATCH 2/7] test: Add missing semicolon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Semicolons are optional in JavaScript, except when they aren’t. Signed-off-by: Anders Kaseorg --- test/all.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/all.ts b/test/all.ts index 30dd789..b9081e6 100644 --- a/test/all.ts +++ b/test/all.ts @@ -1027,7 +1027,7 @@ describe('remote module', () => { const originalSendSync = ipcRenderer.sendSync.bind(ipcRenderer) as any ipcRenderer.sendSync = (...args: any[]): any => { const ret = originalSendSync(...args) - (window as any).gc() + ;(window as any).gc() return ret } From 6dfa6f0f27a7175b8683d83bc553f13ba56c6343 Mon Sep 17 00:00:00 2001 From: Anders Kaseorg Date: Mon, 28 Feb 2022 20:24:37 -0800 Subject: [PATCH 3/7] test: Test this module, not the builtin electron.remote Signed-off-by: Anders Kaseorg --- test/all.ts | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/test/all.ts b/test/all.ts index b9081e6..e6df9e7 100644 --- a/test/all.ts +++ b/test/all.ts @@ -369,7 +369,7 @@ describe('remote module', () => { await expect(remotely(() => { const emptyImage = require('electron').nativeImage.createEmpty() - return require('electron').remote.getGlobal('someFunction')(emptyImage) + return require('./renderer').getGlobal('someFunction')(emptyImage) })).to.eventually.be.true() }) @@ -380,7 +380,7 @@ describe('remote module', () => { }) await expect(remotely(() => { - const image = require('electron').remote.getGlobal('someFunction') + const image = require('./renderer').getGlobal('someFunction') return image.isEmpty() })).to.eventually.be.true() }) @@ -394,8 +394,9 @@ describe('remote module', () => { await expect(remotely(() => { const { nativeImage } = require('electron') + const remote = require('./renderer') const nonEmptyImage = nativeImage.createFromDataURL('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAFklEQVQYlWP8//8/AwMDEwMDAwMDAwAkBgMBBMzldwAAAABJRU5ErkJggg==') - return require('electron').remote.getGlobal('someFunction')(nonEmptyImage) + return remote.getGlobal('someFunction')(nonEmptyImage) })).to.eventually.deep.equal({ width: 2, height: 2 }) }) @@ -406,14 +407,15 @@ describe('remote module', () => { }) await expect(remotely(() => { - const image = require('electron').remote.getGlobal('someFunction') + const image = require('./renderer').getGlobal('someFunction') return image.getSize() })).to.eventually.deep.equal({ width: 2, height: 2 }) }) it('can properly create a menu with an nativeImage icon in the renderer', async () => { await expect(remotely(() => { - const { remote, nativeImage } = require('electron') + const { nativeImage } = require('electron') + const remote = require('./renderer') remote.Menu.buildFromTemplate([ { label: 'hello', @@ -639,7 +641,7 @@ describe('remote module', () => { const protocolKeys = Object.getOwnPropertyNames(protocol); remotely.it(protocolKeys)('remote.protocol returns all keys', (protocolKeys: [string]) => { - const protocol = require('electron').remote.protocol; + const protocol = require('./renderer').protocol; const remoteKeys = Object.getOwnPropertyNames(protocol); expect(remoteKeys).to.deep.equal(protocolKeys); for (const key of remoteKeys) { @@ -721,7 +723,7 @@ describe('remote module', () => { Foo.prototype.constructor = undefined as any event.returnValue = new Foo() }) - expect(await remotely(() => require('electron').remote.getGlobal('test').bar())).to.equal('bar') + expect(await remotely(() => require('./renderer').getGlobal('test').bar())).to.equal('bar') }) }) @@ -920,7 +922,7 @@ describe('remote module', () => { delete (global as any).returnAPromise }) remotely.it()('using a promise based method resolves correctly when global Promise is overridden', async () => { - const { remote } = require('electron') + const remote = require('./renderer') const original = global.Promise try { expect(await remote.getGlobal('returnAPromise')(123)).to.equal(123) @@ -1033,7 +1035,7 @@ describe('remote module', () => { for (let i = 0; i < 100; i++) { // eslint-disable-next-line - require('electron').remote.getGlobal('test').x + require('./renderer').getGlobal('test').x } }) }) From 4168d4dbdd992245ef03c9586630c416a5acfe0b Mon Sep 17 00:00:00 2001 From: Anders Kaseorg Date: Mon, 28 Feb 2022 21:33:28 -0800 Subject: [PATCH 4/7] test: Use enable() instead of enableRemoteModule: true Signed-off-by: Anders Kaseorg --- test/all.ts | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/test/all.ts b/test/all.ts index e6df9e7..d346892 100644 --- a/test/all.ts +++ b/test/all.ts @@ -1,5 +1,5 @@ /// -import { initialize } from '../src/main' +import { enable, initialize } from '../src/main' import { expect } from 'chai' import * as path from 'path' import { ipcMain, BrowserWindow, protocol, nativeImage, NativeImage, app, WebContents } from 'electron' @@ -52,7 +52,8 @@ function makeRemotely (windowGetter: () => BrowserWindow) { function makeWindow () { let w: BrowserWindow before(async () => { - w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false, enableRemoteModule: true } }) + w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } }) + enable(w.webContents) await w.loadURL('about:blank') await w.webContents.executeJavaScript(`{ const chai_1 = window.chai_1 = require('chai') @@ -68,7 +69,8 @@ function makeWindow () { function makeEachWindow () { let w: BrowserWindow beforeEach(async () => { - w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false, enableRemoteModule: true } }) + w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } }) + enable(w.webContents) await w.loadURL('about:blank') await w.webContents.executeJavaScript(`{ const chai_1 = window.chai_1 = require('chai') @@ -327,10 +329,10 @@ describe('remote module', () => { const w = new BrowserWindow({ show: false, webPreferences: { - preload, - enableRemoteModule: true + preload } }) + enable(w.webContents) w.loadURL('about:blank') await emittedOnce(ipcMain, 'done') }) @@ -342,10 +344,10 @@ describe('remote module', () => { show: false, webPreferences: { nodeIntegration: true, - contextIsolation: false, - enableRemoteModule: true + contextIsolation: false } }) + enable(w.webContents) ipcMain.once('error-message', (event, message) => { expect(message).to.match(/^Cannot call method 'getURL' on missing remote object/) @@ -434,10 +436,10 @@ describe('remote module', () => { show: false, webPreferences: { nodeIntegration: true, - contextIsolation: false, - enableRemoteModule: true + contextIsolation: false } }) + enable(w.webContents) await w.loadFile(path.join(fixtures, 'remote-event-handler.html')) w.webContents.reload() await emittedOnce(w.webContents, 'did-finish-load') From d2257e9a96d2025aa899d610a1d5708412bce167 Mon Sep 17 00:00:00 2001 From: Anders Kaseorg Date: Mon, 28 Feb 2022 21:31:26 -0800 Subject: [PATCH 5/7] =?UTF-8?q?test:=20Cast=20remote-get-global=20message?= =?UTF-8?q?=20for=20Electron=20=E2=89=A5=2014?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Anders Kaseorg --- test/all.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/all.ts b/test/all.ts index d346892..4c2377c 100644 --- a/test/all.ts +++ b/test/all.ts @@ -237,12 +237,12 @@ describe('remote module', () => { describe(name, () => { describe('remote.getGlobal', () => { it('can return custom values', async () => { - emitter().once('remote-get-global', returnFirstArg) + emitter().once('remote-get-global' as any, returnFirstArg) expect(await remotely(() => require('./renderer').getGlobal('test'))).to.equal('test') }) it('throws when no returnValue set', async () => { - emitter().once('remote-get-global', preventDefault) + emitter().once('remote-get-global' as any, preventDefault) await expect(remotely(() => require('./renderer').getGlobal('test'))).to.eventually.be.rejected(`Blocked remote.getGlobal('test')`) }) }) @@ -365,7 +365,7 @@ describe('remote module', () => { it('can serialize an empty nativeImage from renderer to main', async () => { const getImageEmpty = (img: NativeImage) => img.isEmpty() - w().webContents.once('remote-get-global', (event) => { + w().webContents.once('remote-get-global' as any, (event: any) => { event.returnValue = getImageEmpty }) @@ -376,7 +376,7 @@ describe('remote module', () => { }) it('can serialize an empty nativeImage from main to renderer', async () => { - w().webContents.once('remote-get-global', (event) => { + w().webContents.once('remote-get-global' as any, (event) => { const emptyImage = require('electron').nativeImage.createEmpty() event.returnValue = emptyImage }) @@ -390,7 +390,7 @@ describe('remote module', () => { it('can serialize a non-empty nativeImage from renderer to main', async () => { const getImageSize = (img: NativeImage) => img.getSize() - w().webContents.once('remote-get-global', (event) => { + w().webContents.once('remote-get-global' as any, (event: any) => { event.returnValue = getImageSize }) @@ -403,7 +403,7 @@ describe('remote module', () => { }) it('can serialize a non-empty nativeImage from main to renderer', async () => { - w().webContents.once('remote-get-global', (event) => { + w().webContents.once('remote-get-global' as any, (event: any) => { const nonEmptyImage = nativeImage.createFromDataURL('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAFklEQVQYlWP8//8/AwMDEwMDAwMDAwAkBgMBBMzldwAAAABJRU5ErkJggg==') event.returnValue = nonEmptyImage }) @@ -720,7 +720,7 @@ describe('remote module', () => { }) it('can handle objects without constructors', async () => { - win().webContents.once('remote-get-global', (event) => { + win().webContents.once('remote-get-global' as any, (event: any) => { class Foo { bar () { return 'bar'; } } Foo.prototype.constructor = undefined as any event.returnValue = new Foo() @@ -1023,7 +1023,7 @@ describe('remote module', () => { const remotely = makeRemotely(win) it('is resilient to gc happening between request and response', async () => { const obj = { x: 'y' } - win().webContents.on('remote-get-global', (event) => { + win().webContents.on('remote-get-global' as any, (event: any) => { event.returnValue = obj }) await remotely(() => { From 87eade525393c4a704f98c851e79f4cac44bd605 Mon Sep 17 00:00:00 2001 From: Anders Kaseorg Date: Mon, 28 Feb 2022 21:39:09 -0800 Subject: [PATCH 6/7] =?UTF-8?q?test:=20Skip=20error=20cause=20test=20for?= =?UTF-8?q?=20Electron=20=E2=89=A5=2014?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Anders Kaseorg --- test/all.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/all.ts b/test/all.ts index 4c2377c..07c05d8 100644 --- a/test/all.ts +++ b/test/all.ts @@ -1013,7 +1013,8 @@ describe('remote module', () => { expect.fail() } catch (e) { expect(e.message).to.match(/Could not call remote function/) - expect(e.cause.message).to.equal('error from main') + if (parseInt(process.versions.electron) < 14) // FIXME + expect(e.cause.message).to.equal('error from main') } }) }) From e0b9a7758066511ee0c9a896dbbea5693c441698 Mon Sep 17 00:00:00 2001 From: Anders Kaseorg Date: Mon, 28 Feb 2022 19:23:23 -0800 Subject: [PATCH 7/7] ci: Test with Electron 14, 15, 16, and 17 Signed-off-by: Anders Kaseorg --- .circleci/config.yml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 666516c..b9cff71 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -40,6 +40,34 @@ jobs: ELECTRON_VERSION: 12.x <<: *steps-test + test-electron-14: + docker: + - image: circleci/node:14-browsers + environment: + ELECTRON_VERSION: 14.x + <<: *steps-test + + test-electron-15: + docker: + - image: circleci/node:14-browsers + environment: + ELECTRON_VERSION: 15.x + <<: *steps-test + + test-electron-16: + docker: + - image: circleci/node:14-browsers + environment: + ELECTRON_VERSION: 16.x + <<: *steps-test + + test-electron-17: + docker: + - image: circleci/node:14-browsers + environment: + ELECTRON_VERSION: 17.x + <<: *steps-test + release: docker: - image: circleci/node:14-browsers @@ -55,6 +83,10 @@ workflows: - test-electron-13 - test-electron-11 - test-electron-12 + - test-electron-14 + - test-electron-15 + - test-electron-16 + - test-electron-17 - release: requires: - test-electron-13