116 lines
		
	
	
	
		
			3.2 KiB
			
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			116 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()
 |