117 lines
3.2 KiB
JavaScript
117 lines
3.2 KiB
JavaScript
|
'use strict'
|
||
|
|
||
|
const v8Util = process.atomBinding('v8_util')
|
||
|
|
||
|
const getOwnerKey = (webContents, contextId) => {
|
||
|
return `${webContents.id}-${contextId}`
|
||
|
}
|
||
|
|
||
|
class ObjectsRegistry {
|
||
|
constructor () {
|
||
|
this.nextId = 0
|
||
|
|
||
|
// Stores all objects by ref-counting.
|
||
|
// (id) => {object, count}
|
||
|
this.storage = {}
|
||
|
|
||
|
// Stores the IDs of objects referenced by WebContents.
|
||
|
// (ownerKey) => [id]
|
||
|
this.owners = {}
|
||
|
}
|
||
|
|
||
|
// Register a new object and return its assigned ID. If the object is already
|
||
|
// registered then the already assigned ID would be returned.
|
||
|
add (webContents, contextId, obj) {
|
||
|
// Get or assign an ID to the object.
|
||
|
const id = this.saveToStorage(obj)
|
||
|
|
||
|
// Add object to the set of referenced objects.
|
||
|
const ownerKey = getOwnerKey(webContents, contextId)
|
||
|
let owner = this.owners[ownerKey]
|
||
|
if (!owner) {
|
||
|
owner = this.owners[ownerKey] = new Set()
|
||
|
this.registerDeleteListener(webContents, contextId)
|
||
|
}
|
||
|
if (!owner.has(id)) {
|
||
|
owner.add(id)
|
||
|
// Increase reference count if not referenced before.
|
||
|
this.storage[id].count++
|
||
|
}
|
||
|
return id
|
||
|
}
|
||
|
|
||
|
// Get an object according to its ID.
|
||
|
get (id) {
|
||
|
const pointer = this.storage[id]
|
||
|
if (pointer != null) return pointer.object
|
||
|
}
|
||
|
|
||
|
// 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) {
|
||
|
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)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Clear all references to objects refrenced by the WebContents.
|
||
|
clear (webContents, contextId) {
|
||
|
const ownerKey = getOwnerKey(webContents, contextId)
|
||
|
const owner = this.owners[ownerKey]
|
||
|
if (!owner) return
|
||
|
|
||
|
for (const id of owner) this.dereference(id)
|
||
|
|
||
|
delete this.owners[ownerKey]
|
||
|
}
|
||
|
|
||
|
// Private: Saves the object into storage and assigns an ID for it.
|
||
|
saveToStorage (object) {
|
||
|
let id = v8Util.getHiddenValue(object, 'atomId')
|
||
|
if (!id) {
|
||
|
id = ++this.nextId
|
||
|
this.storage[id] = {
|
||
|
count: 0,
|
||
|
object: object
|
||
|
}
|
||
|
v8Util.setHiddenValue(object, 'atomId', id)
|
||
|
}
|
||
|
return id
|
||
|
}
|
||
|
|
||
|
// Private: Dereference the object from store.
|
||
|
dereference (id) {
|
||
|
const pointer = this.storage[id]
|
||
|
if (pointer == null) {
|
||
|
return
|
||
|
}
|
||
|
pointer.count -= 1
|
||
|
if (pointer.count === 0) {
|
||
|
v8Util.deleteHiddenValue(pointer.object, 'atomId')
|
||
|
delete this.storage[id]
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Private: Clear the storage when renderer process is destroyed.
|
||
|
registerDeleteListener (webContents, contextId) {
|
||
|
// contextId => ${processHostId}-${contextCount}
|
||
|
const processHostId = contextId.split('-')[0]
|
||
|
const listener = (event, deletedProcessHostId) => {
|
||
|
if (deletedProcessHostId &&
|
||
|
deletedProcessHostId.toString() === processHostId) {
|
||
|
webContents.removeListener('render-view-deleted', listener)
|
||
|
this.clear(webContents, contextId)
|
||
|
}
|
||
|
}
|
||
|
webContents.on('render-view-deleted', listener)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
module.exports = new ObjectsRegistry()
|