From 53f4af7722cade4f6df3d7e930e1434ae30ddd41 Mon Sep 17 00:00:00 2001 From: Milan Burda Date: Tue, 19 Mar 2019 14:45:48 +0100 Subject: [PATCH] fix: regressions introduced by adding world isolation to Chrome extension content scripts (#17422) --- lib/content_script/init.js | 4 +- lib/renderer/content-scripts-injector.ts | 7 ++-- spec/chrome-api-spec.js | 40 +++++++++++++++++-- .../extensions/chrome-api/background.js | 20 ++++++++++ spec/fixtures/extensions/chrome-api/main.js | 19 +++++++-- .../extensions/chrome-api/manifest.json | 4 ++ 6 files changed, 83 insertions(+), 11 deletions(-) create mode 100644 spec/fixtures/extensions/chrome-api/background.js diff --git a/lib/content_script/init.js b/lib/content_script/init.js index 2d97dcb519de..1949b5d0e406 100644 --- a/lib/content_script/init.js +++ b/lib/content_script/init.js @@ -7,9 +7,11 @@ const { EventEmitter } = require('events') process.electronBinding = require('@electron/internal/common/atom-binding-setup').electronBindingSetup(nodeProcess.binding, 'renderer') const v8Util = process.electronBinding('v8_util') + // The `lib/renderer/ipc-renderer-internal.js` module looks for the ipc object in the // "ipc-internal" hidden value -v8Util.setHiddenValue(global, 'ipc-internal', new EventEmitter()) +v8Util.setHiddenValue(global, 'ipc-internal', v8Util.getHiddenValue(isolatedWorld, 'ipc-internal')) + // The process object created by browserify is not an event emitter, fix it so // the API is more compatible with non-sandboxed renderers. for (const prop of Object.keys(EventEmitter.prototype)) { diff --git a/lib/renderer/content-scripts-injector.ts b/lib/renderer/content-scripts-injector.ts index 40613ccd3ea4..106688285524 100644 --- a/lib/renderer/content-scripts-injector.ts +++ b/lib/renderer/content-scripts-injector.ts @@ -45,7 +45,7 @@ const runContentScript = function (this: any, extensionId: string, url: string, }) const sources = [{ code, url }] - webFrame.executeJavaScriptInIsolatedWorld(worldId, sources) + return webFrame.executeJavaScriptInIsolatedWorld(worldId, sources) } const runAllContentScript = function (scripts: Array, extensionId: string) { @@ -102,8 +102,9 @@ ipcRendererInternal.on('CHROME_TABS_EXECUTESCRIPT', function ( url: string, code: string ) { - const result = runContentScript.call(window, extensionId, url, code) - ipcRendererInternal.sendToAll(senderWebContentsId, `CHROME_TABS_EXECUTESCRIPT_RESULT_${requestId}`, result) + runContentScript.call(window, extensionId, url, code).then(result => { + ipcRendererInternal.sendToAll(senderWebContentsId, `CHROME_TABS_EXECUTESCRIPT_RESULT_${requestId}`, result) + }) }) module.exports = (getRenderProcessPreferences: typeof process.getRenderProcessPreferences) => { diff --git a/spec/chrome-api-spec.js b/spec/chrome-api-spec.js index ddea70685727..eff5c02b89d9 100644 --- a/spec/chrome-api-spec.js +++ b/spec/chrome-api-spec.js @@ -35,14 +35,46 @@ describe('chrome api', () => { return JSON.parse(data) })() - w.loadURL('about:blank') + await w.loadURL('about:blank') - const p = emittedOnce(w.webContents, 'console-message') - w.webContents.executeJavaScript(`window.postMessage('getManifest', '*')`) - const [,, manifestString] = await p + const promise = emittedOnce(w.webContents, 'console-message') + + const message = { method: 'getManifest' } + w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`) + + const [,, manifestString] = await promise const manifest = JSON.parse(manifestString) expect(manifest.name).to.equal(actualManifest.name) expect(manifest.content_scripts.length).to.equal(actualManifest.content_scripts.length) }) + + it('chrome.tabs.sendMessage receives the response', async function () { + await w.loadURL('about:blank') + + const promise = emittedOnce(w.webContents, 'console-message') + + const message = { method: 'sendMessage', args: ['Hello World!'] } + w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`) + + const [,, responseString] = await promise + const response = JSON.parse(responseString) + + expect(response.message).to.equal('Hello World!') + expect(response.tabId).to.equal(w.webContents.id) + }) + + it('chrome.tabs.executeScript receives the response', async function () { + await w.loadURL('about:blank') + + const promise = emittedOnce(w.webContents, 'console-message') + + const message = { method: 'executeScript', args: ['1 + 2'] } + w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`) + + const [,, responseString] = await promise + const response = JSON.parse(responseString) + + expect(response).to.equal(3) + }) }) diff --git a/spec/fixtures/extensions/chrome-api/background.js b/spec/fixtures/extensions/chrome-api/background.js new file mode 100644 index 000000000000..c86ff51ddb9d --- /dev/null +++ b/spec/fixtures/extensions/chrome-api/background.js @@ -0,0 +1,20 @@ +/* global chrome */ + +chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { + const { method, args = [] } = message + const tabId = sender.tab.id + + switch (method) { + case 'sendMessage': { + const [message] = args + chrome.tabs.sendMessage(tabId, { message, tabId }, undefined, sendResponse) + break + } + + case 'executeScript': { + const [code] = args + chrome.tabs.executeScript(tabId, { code }, ([result]) => sendResponse(result)) + break + } + } +}) diff --git a/spec/fixtures/extensions/chrome-api/main.js b/spec/fixtures/extensions/chrome-api/main.js index 066b609e7917..2784e82c37fd 100644 --- a/spec/fixtures/extensions/chrome-api/main.js +++ b/spec/fixtures/extensions/chrome-api/main.js @@ -1,15 +1,28 @@ /* global chrome */ +chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { + sendResponse(message) +}) + const testMap = { getManifest () { const manifest = chrome.runtime.getManifest() console.log(JSON.stringify(manifest)) + }, + sendMessage (message) { + chrome.runtime.sendMessage({ method: 'sendMessage', args: [message] }, response => { + console.log(JSON.stringify(response)) + }) + }, + executeScript (code) { + chrome.runtime.sendMessage({ method: 'executeScript', args: [code] }, response => { + console.log(JSON.stringify(response)) + }) } } const dispatchTest = (event) => { - const testName = event.data - const test = testMap[testName] - test() + const { method, args = [] } = JSON.parse(event.data) + testMap[method](...args) } window.addEventListener('message', dispatchTest, false) diff --git a/spec/fixtures/extensions/chrome-api/manifest.json b/spec/fixtures/extensions/chrome-api/manifest.json index 4e52fb324841..fd4d88b91096 100644 --- a/spec/fixtures/extensions/chrome-api/manifest.json +++ b/spec/fixtures/extensions/chrome-api/manifest.json @@ -8,5 +8,9 @@ "run_at": "document_start" } ], + "background": { + "scripts": ["background.js"], + "persistent": false + }, "manifest_version": 2 }