Browse Source

fix: regressions introduced by adding world isolation to Chrome extension content scripts (#17422)

Milan Burda 6 years ago
parent
commit
53f4af7722

+ 3 - 1
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)) {

+ 4 - 3
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<Electron.InjectionBase>, 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) => {

+ 36 - 4
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)
+  })
 })

+ 20 - 0
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
+    }
+  }
+})

+ 16 - 3
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)

+ 4 - 0
spec/fixtures/extensions/chrome-api/manifest.json

@@ -8,5 +8,9 @@
       "run_at": "document_start"
     }
   ],
+  "background": {
+    "scripts": ["background.js"],
+    "persistent": false
+  },
   "manifest_version": 2
 }