Browse Source

fix: create WebContents for webview on request (#13713)

Cheng Zhao 6 years ago
parent
commit
9d6f1a372e

+ 4 - 0
lib/browser/guest-view-manager.js

@@ -348,6 +348,10 @@ ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', function (event, params,
   event.sender.send(`ELECTRON_RESPONSE_${requestId}`, createGuest(event.sender, params))
 })
 
+ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST_SYNC', function (event, params) {
+  event.returnValue = createGuest(event.sender, params)
+})
+
 ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', function (event, elementInstanceId, guestInstanceId, params) {
   attachGuest(event, elementInstanceId, guestInstanceId, params)
 })

+ 3 - 0
lib/renderer/web-view/guest-view-internal.js

@@ -100,6 +100,9 @@ module.exports = {
     ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', params, requestId)
     ipcRenderer.once(`ELECTRON_RESPONSE_${requestId}`, callback)
   },
+  createGuestSync: function (params) {
+    return ipcRenderer.sendSync('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST_SYNC', params)
+  },
   attachGuest: function (elementInstanceId, guestInstanceId, params) {
     ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', elementInstanceId, guestInstanceId, params)
     webFrame.attachGuest(elementInstanceId)

+ 1 - 4
lib/renderer/web-view/web-view-attributes.js

@@ -230,7 +230,7 @@ class SrcAttribute extends WebViewAttribute {
   }
 
   parse () {
-    if (!this.webViewImpl.elementAttached || !this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_PARTITION].validPartitionId) {
+    if (!this.webViewImpl.elementAttached || !this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_PARTITION].validPartitionId || !this.getValue()) {
       return
     }
     if (this.webViewImpl.guestInstanceId == null) {
@@ -240,9 +240,6 @@ class SrcAttribute extends WebViewAttribute {
       }
       return
     }
-    if (!this.getValue()) {
-      return
-    }
 
     // Navigate to |this.src|.
     const opts = {}

+ 9 - 1
lib/renderer/web-view/web-view.js

@@ -174,6 +174,10 @@ class WebViewImpl {
     })
   }
 
+  createGuestSync () {
+    this.attachGuestInstance(guestViewInternal.createGuestSync(this.buildParams()))
+  }
+
   dispatchEvent (webViewEvent) {
     this.webviewNode.dispatchEvent(webViewEvent)
   }
@@ -422,7 +426,11 @@ const registerWebViewElement = function () {
 
   // WebContents associated with this webview.
   proto.getWebContents = function () {
-    return v8Util.getHiddenValue(this, 'internal').webContents
+    const internal = v8Util.getHiddenValue(this, 'internal')
+    if (!internal.webContents) {
+      internal.createGuestSync()
+    }
+    return internal.webContents
   }
 
   window.WebView = webFrame.registerEmbedderCustomElement('webview', {

+ 14 - 3
spec/webview-spec.js

@@ -696,7 +696,6 @@ describe('<webview> tag', function () {
     it('sets webContents of webview as devtools', async () => {
       const webview2 = new WebView()
       loadWebView(webview2)
-      await waitForEvent(webview2, 'did-attach')
 
       // Setup an event handler for further usage.
       const waitForDomReady = waitForEvent(webview2, 'dom-ready')
@@ -1180,6 +1179,18 @@ describe('<webview> tag', function () {
   })
 
   describe('will-attach-webview event', () => {
+    it('does not emit when src is not changed', (done) => {
+      loadWebView(webview)
+      setTimeout(() => {
+        const expectedErrorMessage =
+            'Cannot call stop because the webContents is unavailable. ' +
+            'The WebView must be attached to the DOM ' +
+            'and the dom-ready event emitted before this method can be called.'
+        expect(() => { webview.stop() }).to.throw(expectedErrorMessage)
+        done()
+      })
+    })
+
     it('supports changing the web preferences', async () => {
       ipcRenderer.send('disable-node-on-next-will-attach-webview')
       const message = await startLoadingWebViewAndWaitForMessage(webview, {
@@ -1368,11 +1379,11 @@ describe('<webview> tag', function () {
 
         const destroy1Listener = () => {
           assert.equal(webContents, webview2.getWebContents())
-          assert.equal(null, webview.getWebContents())
+          assert.notStrictEqual(webContents, webview.getWebContents())
 
           const destroy2Listener = () => {
             assert.equal(webContents, webview.getWebContents())
-            assert.equal(null, webview2.getWebContents())
+            assert.notStrictEqual(webContents, webview2.getWebContents())
 
             // Make sure that events are hooked up to the right webview now
             webview.addEventListener('console-message', (e) => {