|
@@ -14,8 +14,8 @@ class ObjectsRegistry {
|
|
|
// (id) => {object, count}
|
|
|
this.storage = {}
|
|
|
|
|
|
- // Stores the IDs of objects referenced by WebContents.
|
|
|
- // (ownerKey) => [id]
|
|
|
+ // Stores the IDs + refCounts of objects referenced by WebContents.
|
|
|
+ // (ownerKey) => { id: refCount }
|
|
|
this.owners = {}
|
|
|
}
|
|
|
|
|
@@ -29,14 +29,16 @@ class ObjectsRegistry {
|
|
|
const ownerKey = getOwnerKey(webContents, contextId)
|
|
|
let owner = this.owners[ownerKey]
|
|
|
if (!owner) {
|
|
|
- owner = this.owners[ownerKey] = new Set()
|
|
|
+ owner = this.owners[ownerKey] = new Map()
|
|
|
this.registerDeleteListener(webContents, contextId)
|
|
|
}
|
|
|
if (!owner.has(id)) {
|
|
|
- owner.add(id)
|
|
|
+ owner.set(id, 0)
|
|
|
// Increase reference count if not referenced before.
|
|
|
this.storage[id].count++
|
|
|
}
|
|
|
+
|
|
|
+ owner.set(id, owner.get(id) + 1)
|
|
|
return id
|
|
|
}
|
|
|
|
|
@@ -49,14 +51,30 @@ class ObjectsRegistry {
|
|
|
// Dereference an object according to its ID.
|
|
|
// Note that an object may be double-freed (cleared when page is reloaded, and
|
|
|
// then garbage collected in old page).
|
|
|
- remove (webContents, contextId, id) {
|
|
|
+ // rendererSideRefCount is the ref count that the renderer process reported
|
|
|
+ // at time of GC if this is different to the number of references we sent to
|
|
|
+ // the given owner then a GC occurred between a ref being sent and the value
|
|
|
+ // being pulled out of the weak map.
|
|
|
+ // In this case we decrement out ref count and do not delete the stored
|
|
|
+ // object
|
|
|
+ // For more details on why we do renderer side ref counting see
|
|
|
+ // https://github.com/electron/electron/pull/17464
|
|
|
+ remove (webContents, contextId, id, rendererSideRefCount) {
|
|
|
const ownerKey = getOwnerKey(webContents, contextId)
|
|
|
const owner = this.owners[ownerKey]
|
|
|
- if (owner) {
|
|
|
- // Remove the reference in owner.
|
|
|
- owner.delete(id)
|
|
|
- // Dereference from the storage.
|
|
|
- this.dereference(id)
|
|
|
+ if (owner && owner.has(id)) {
|
|
|
+ const newRefCount = owner.get(id) - rendererSideRefCount
|
|
|
+
|
|
|
+ // Only completely remove if the number of references GCed in the
|
|
|
+ // renderer is the same as the number of references we sent them
|
|
|
+ if (newRefCount <= 0) {
|
|
|
+ // Remove the reference in owner.
|
|
|
+ owner.delete(id)
|
|
|
+ // Dereference from the storage.
|
|
|
+ this.dereference(id)
|
|
|
+ } else {
|
|
|
+ owner.set(id, newRefCount)
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -66,7 +84,7 @@ class ObjectsRegistry {
|
|
|
const owner = this.owners[ownerKey]
|
|
|
if (!owner) return
|
|
|
|
|
|
- for (const id of owner) this.dereference(id)
|
|
|
+ for (const id of owner.keys()) this.dereference(id)
|
|
|
|
|
|
delete this.owners[ownerKey]
|
|
|
}
|