Browse Source

fix: register webview in main world when using contextIsolation (#16067)

Cheng Zhao 6 years ago
parent
commit
8584c2f14b

+ 3 - 0
BUILD.gn

@@ -75,6 +75,9 @@ npm_action("atom_browserify_sandbox") {
   ]
 
   inputs = [
+    # FIXME(zcbenz): The dependencies of these files are not listed here, so
+    # the generated file will be out-dated when dependencies are modified.
+    # Use a script to generate all dependencies and put them here.
     "lib/sandboxed_renderer/init.js",
     "lib/sandboxed_renderer/api/exports/electron.js",
     "lib/sandboxed_renderer/api/exports/fs.js",

+ 2 - 0
atom/renderer/api/atom_api_web_frame.cc

@@ -189,8 +189,10 @@ void WebFrame::SetLayoutZoomLevelLimits(double min_level, double max_level) {
 }
 
 v8::Local<v8::Value> WebFrame::RegisterEmbedderCustomElement(
+    v8::Local<v8::Object> context,
     const base::string16& name,
     v8::Local<v8::Object> options) {
+  v8::Context::Scope context_scope(context->CreationContext());
   return web_frame_->GetDocument().RegisterEmbedderCustomElement(
       blink::WebString::FromUTF16(name), options);
 }

+ 1 - 0
atom/renderer/api/atom_api_web_frame.h

@@ -51,6 +51,7 @@ class WebFrame : public mate::Wrappable<WebFrame> {
   void SetLayoutZoomLevelLimits(double min_level, double max_level);
 
   v8::Local<v8::Value> RegisterEmbedderCustomElement(
+      v8::Local<v8::Object> context,
       const base::string16& name,
       v8::Local<v8::Object> options);
   int GetWebFrameId(v8::Local<v8::Value> content_window);

+ 7 - 0
lib/isolated_renderer/init.js

@@ -2,9 +2,16 @@
 
 /* global nodeProcess, isolatedWorld */
 
+window.isolatedWorld = isolatedWorld
+
 // Note: Don't use "process", as it will be replaced by browserify's one.
 const v8Util = nodeProcess.atomBinding('v8_util')
 
+const setupWebView = v8Util.getHiddenValue(isolatedWorld, 'setup-webview')
+if (setupWebView) {
+  setupWebView(window)
+}
+
 const isolatedWorldArgs = v8Util.getHiddenValue(isolatedWorld, 'isolated-world-args')
 const { ipcRenderer, guestInstanceId, hiddenPage, openerId, usesNativeWindowOpen } = isolatedWorldArgs
 

+ 6 - 2
lib/renderer/init.js

@@ -98,8 +98,12 @@ if (window.location.protocol === 'chrome-devtools:') {
 
   // Load webview tag implementation.
   if (webviewTag && guestInstanceId == null) {
-    require('@electron/internal/renderer/web-view/web-view')
-    require('@electron/internal/renderer/web-view/web-view-attributes')
+    const { setupWebView } = require('@electron/internal/renderer/web-view/web-view')
+    if (contextIsolation) {
+      v8Util.setHiddenValue(window, 'setup-webview', setupWebView)
+    } else {
+      setupWebView(window)
+    }
   }
 }
 

+ 3 - 3
lib/renderer/web-view/web-view-attributes.js

@@ -1,7 +1,7 @@
 'use strict'
 
 const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
-const WebViewImpl = require('@electron/internal/renderer/web-view/web-view')
+const { WebViewImpl } = require('@electron/internal/renderer/web-view/web-view')
 const webViewConstants = require('@electron/internal/renderer/web-view/web-view-constants')
 const errorUtils = require('@electron/internal/common/error-utils')
 
@@ -86,13 +86,13 @@ class PartitionAttribute extends WebViewAttribute {
 
     // The partition cannot change if the webview has already navigated.
     if (!this.webViewImpl.beforeFirstNavigation) {
-      window.console.error(webViewConstants.ERROR_MSG_ALREADY_NAVIGATED)
+      console.error(webViewConstants.ERROR_MSG_ALREADY_NAVIGATED)
       this.setValueIgnoreMutation(oldValue)
       return
     }
     if (newValue === 'persist:') {
       this.validPartitionId = false
-      window.console.error(webViewConstants.ERROR_MSG_INVALID_PARTITION_ATTRIBUTE)
+      console.error(webViewConstants.ERROR_MSG_INVALID_PARTITION_ATTRIBUTE)
     }
   }
 }

+ 14 - 13
lib/renderer/web-view/web-view.js

@@ -198,8 +198,8 @@ class WebViewImpl {
 }
 
 // Registers <webview> custom element.
-const registerWebViewElement = function () {
-  const proto = Object.create(HTMLObjectElement.prototype)
+const registerWebViewElement = (window) => {
+  const proto = Object.create(window.HTMLObjectElement.prototype)
   proto.createdCallback = function () {
     return new WebViewImpl(this)
   }
@@ -288,7 +288,7 @@ const registerWebViewElement = function () {
     this.contentWindow.focus()
   }
 
-  window.WebView = webFrame.registerEmbedderCustomElement('webview', {
+  window.WebView = webFrame.registerEmbedderCustomElement(window, 'webview', {
     prototype: proto
   })
 
@@ -300,16 +300,17 @@ const registerWebViewElement = function () {
   delete proto.attributeChangedCallback
 }
 
-const useCapture = true
+const setupWebView = (window) => {
+  require('@electron/internal/renderer/web-view/web-view-attributes')
 
-const listener = function (event) {
-  if (document.readyState === 'loading') {
-    return
-  }
-  registerWebViewElement()
-  window.removeEventListener(event.type, listener, useCapture)
+  const useCapture = true
+  window.addEventListener('readystatechange', function listener (event) {
+    if (document.readyState === 'loading') {
+      return
+    }
+    registerWebViewElement(window)
+    window.removeEventListener(event.type, listener, useCapture)
+  }, useCapture)
 }
 
-window.addEventListener('readystatechange', listener, true)
-
-module.exports = WebViewImpl
+module.exports = { setupWebView, WebViewImpl }

+ 1 - 2
lib/sandboxed_renderer/init.js

@@ -122,8 +122,7 @@ if (binding.guestInstanceId) {
 
 if (!process.guestInstanceId && preloadProcess.argv.includes('--webview-tag=true')) {
   // don't allow recursive `<webview>`
-  require('@electron/internal/renderer/web-view/web-view')
-  require('@electron/internal/renderer/web-view/web-view-attributes')
+  require('@electron/internal/renderer/web-view/web-view').setupWebView(window)
 }
 
 // Wrap the script into a function executed in global scope. It won't have

+ 2 - 0
spec/fixtures/module/isolated-ping.js

@@ -0,0 +1,2 @@
+const { ipcRenderer } = require('electron')
+ipcRenderer.send('pong')

+ 5 - 0
spec/fixtures/pages/webview-isolated.html

@@ -0,0 +1,5 @@
+<html>
+<body>
+<webview preload="../module/isolated-ping.js" src="about:blank"/>
+</body>
+</html>

+ 12 - 0
spec/webview-spec.js

@@ -70,6 +70,18 @@ describe('<webview> tag', function () {
     await emittedOnce(ipcMain, 'pong')
   })
 
+  it('works with contextIsolation', async () => {
+    const w = await openTheWindow({
+      show: false,
+      webPreferences: {
+        webviewTag: true,
+        contextIsolation: true
+      }
+    })
+    w.loadFile(path.join(fixtures, 'pages', 'webview-isolated.html'))
+    await emittedOnce(ipcMain, 'pong')
+  })
+
   it('is disabled when nodeIntegration is disabled', async () => {
     const w = await openTheWindow({
       show: false,