Browse Source

Merge pull request #8110 from electron/track-render-view-by-process-id

Don't clear until render view is deleted for process id
Kevin Sawicki 8 years ago
parent
commit
c27633dff4
3 changed files with 63 additions and 7 deletions
  1. 16 7
      lib/browser/objects-registry.js
  2. 15 0
      spec/api-ipc-spec.js
  3. 32 0
      spec/fixtures/api/render-view-deleted.html

+ 16 - 7
lib/browser/objects-registry.js

@@ -19,17 +19,14 @@ class ObjectsRegistry {
   // registered then the already assigned ID would be returned.
   add (webContents, obj) {
     // Get or assign an ID to the object.
-    let id = this.saveToStorage(obj)
+    const id = this.saveToStorage(obj)
 
     // Add object to the set of referenced objects.
-    let webContentsId = webContents.getId()
+    const webContentsId = webContents.getId()
     let owner = this.owners[webContentsId]
     if (!owner) {
       owner = this.owners[webContentsId] = new Set()
-      // Clear the storage when webContents is reloaded/navigated.
-      webContents.once('render-view-deleted', () => {
-        this.clear(webContentsId)
-      })
+      this.registerDeleteListener(webContents, webContentsId)
     }
     if (!owner.has(id)) {
       owner.add(id)
@@ -90,8 +87,20 @@ class ObjectsRegistry {
     pointer.count -= 1
     if (pointer.count === 0) {
       v8Util.deleteHiddenValue(pointer.object, 'atomId')
-      return delete this.storage[id]
+      delete this.storage[id]
+    }
+  }
+
+  // Private: Clear the storage when webContents is reloaded/navigated.
+  registerDeleteListener (webContents, webContentsId) {
+    const processId = webContents.getProcessId()
+    const listener = (event, deletedProcessId) => {
+      if (deletedProcessId === processId) {
+        webContents.removeListener('render-view-deleted', listener)
+        this.clear(webContentsId)
+      }
     }
+    webContents.on('render-view-deleted', listener)
   }
 }
 

+ 15 - 0
spec/api-ipc-spec.js

@@ -517,4 +517,19 @@ describe('ipc module', function () {
     ipcRenderer.removeAllListeners('test-event')
     assert.equal(ipcRenderer.listenerCount('test-event'), 0)
   })
+
+  describe('remote objects registry', function () {
+    it('does not dereference until the render view is deleted (regression)', function (done) {
+      w = new BrowserWindow({
+        show: true
+      })
+
+      ipcMain.once('error-message', (event, message) => {
+        assert(message.startsWith('Cannot call function \'getURL\' on missing remote object'), message)
+        done()
+      })
+
+      w.loadURL('file://' + path.join(fixtures, 'api', 'render-view-deleted.html'))
+    })
+  })
 })

+ 32 - 0
spec/fixtures/api/render-view-deleted.html

@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title></title>
+    <script>
+      const {ipcRenderer, remote} = require('electron')
+
+      const contents = remote.getCurrentWebContents()
+
+      // This should not trigger a dereference and a remote getURL call should not fail
+      contents.emit('render-view-deleted', {}, 'not-a-process-id')
+      try {
+        contents.getURL()
+      } catch (error) {
+        ipcRenderer.send('error-message', 'Unexpected error on getURL call')
+      }
+
+      // This should trigger a dereference and a remote getURL call should fail
+      contents.emit('render-view-deleted', {}, contents.getProcessId())
+      try {
+        contents.getURL()
+        ipcRenderer.send('error-message', 'No error thrown')
+      } catch (error) {
+        ipcRenderer.send('error-message', error.message)
+      }
+    </script>
+  </head>
+  <body>
+
+  </body>
+</html>