diff options
Diffstat (limited to 'remote/cdp/targets/TargetList.sys.mjs')
-rw-r--r-- | remote/cdp/targets/TargetList.sys.mjs | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/remote/cdp/targets/TargetList.sys.mjs b/remote/cdp/targets/TargetList.sys.mjs new file mode 100644 index 0000000000..a68b36763f --- /dev/null +++ b/remote/cdp/targets/TargetList.sys.mjs @@ -0,0 +1,159 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + EventEmitter: "resource://gre/modules/EventEmitter.sys.mjs", + + MainProcessTarget: + "chrome://remote/content/cdp/targets/MainProcessTarget.sys.mjs", + TabManager: "chrome://remote/content/shared/TabManager.sys.mjs", + TabObserver: "chrome://remote/content/cdp/observers/TargetObserver.sys.mjs", + TabTarget: "chrome://remote/content/cdp/targets/TabTarget.sys.mjs", +}); + +export class TargetList { + constructor() { + // Target ID -> Target + this._targets = new Map(); + + lazy.EventEmitter.decorate(this); + } + + /** + * Start listing and listening for all the debuggable targets + */ + async watchForTargets() { + await this.watchForTabs(); + } + + unwatchForTargets() { + this.unwatchForTabs(); + } + + /** + * Watch for all existing and new tabs being opened. + * So that we can create the related TabTarget instance for + * each of them. + */ + async watchForTabs() { + if (this.tabObserver) { + throw new Error("Targets is already watching for new tabs"); + } + + this.tabObserver = new lazy.TabObserver({ registerExisting: true }); + + // Handle creation of tab targets for opened tabs. + this.tabObserver.on("open", async (eventName, tab) => { + const target = new lazy.TabTarget(this, tab.linkedBrowser); + this.registerTarget(target); + }); + + // Handle removal of tab targets when tabs are closed. + this.tabObserver.on("close", (eventName, tab) => { + const browser = tab.linkedBrowser; + + // Ignore unloaded tabs. + if (browser.browserId === 0) { + return; + } + + const id = lazy.TabManager.getIdForBrowser(browser); + const target = Array.from(this._targets.values()).find( + target => target.id == id + ); + if (target) { + this.destroyTarget(target); + } + }); + await this.tabObserver.start(); + } + + unwatchForTabs() { + if (this.tabObserver) { + this.tabObserver.stop(); + this.tabObserver = null; + } + } + + /** + * To be called right after instantiating a new Target instance. + * This will hold the new instance in the list and notify about + * its creation. + */ + registerTarget(target) { + this._targets.set(target.id, target); + this.emit("target-created", target); + } + + /** + * To be called when the debuggable target has been destroy. + * So that we can notify it no longer exists and disconnect + * all connecting made to debug it. + */ + destroyTarget(target) { + target.destructor(); + this._targets.delete(target.id); + this.emit("target-destroyed", target); + } + + /** + * Destroy all the registered target of all kinds. + * This will end up dropping all connections made to debug any of them. + */ + destructor() { + for (const target of this) { + this.destroyTarget(target); + } + this._targets.clear(); + if (this.mainProcessTarget) { + this.mainProcessTarget = null; + } + + this.unwatchForTargets(); + } + + get size() { + return this._targets.size; + } + + /** + * Get Target instance by target id + * + * @param {string} id + * Target id + * + * @returns {Target} + */ + getById(id) { + return this._targets.get(id); + } + + /** + * Get the Target instance for the main process. + * This target is a singleton and only exposes a subset of domains. + */ + getMainProcessTarget() { + if (!this.mainProcessTarget) { + this.mainProcessTarget = new lazy.MainProcessTarget(this); + this.registerTarget(this.mainProcessTarget); + } + return this.mainProcessTarget; + } + + *[Symbol.iterator]() { + for (const target of this._targets.values()) { + yield target; + } + } + + toJSON() { + return [...this]; + } + + toString() { + return `[object TargetList ${this.size}]`; + } +} |