1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 var EXPORTED_SYMBOLS = ["Targets"];
9 const { EventEmitter } = ChromeUtils.import(
10 "resource://gre/modules/EventEmitter.jsm"
12 const { MessagePromise } = ChromeUtils.import(
13 "chrome://remote/content/Sync.jsm"
15 const { TabTarget } = ChromeUtils.import(
16 "chrome://remote/content/targets/TabTarget.jsm"
18 const { MainProcessTarget } = ChromeUtils.import(
19 "chrome://remote/content/targets/MainProcessTarget.jsm"
21 const { TabObserver } = ChromeUtils.import(
22 "chrome://remote/content/targets/TabObserver.jsm"
27 // Target ID -> Target
28 this._targets = new Map();
30 EventEmitter.decorate(this);
34 * Start listing and listening for all the debuggable targets
36 async watchForTargets() {
37 await this.watchForTabs();
41 this.unwatchForTabs();
45 * Watch for all existing and new tabs being opened.
46 * So that we can create the related TabTarget instance for
49 async watchForTabs() {
50 if (this.tabObserver) {
51 throw new Error("Targets is already watching for new tabs");
53 this.tabObserver = new TabObserver({ registerExisting: true });
54 this.tabObserver.on("open", async (eventName, tab) => {
55 const browser = tab.linkedBrowser;
56 // The tab may just have been created and not fully initialized yet.
57 // Target class expects BrowserElement.browsingContext to be defined
58 // whereas it is asynchronously set by the custom element class.
59 // At least ensure that this property is set before instantiating the target.
60 if (!browser.browsingContext) {
61 await new MessagePromise(browser.messageManager, "Browser:Init");
63 const target = new TabTarget(this, browser);
64 this.registerTarget(target);
66 this.tabObserver.on("close", (eventName, tab) => {
67 const browser = tab.linkedBrowser;
68 // Ignore the browsers that haven't had time to initialize.
69 if (!browser.browsingContext) {
72 const target = this.getById(browser.browsingContext.id);
74 this.destroyTarget(target);
77 await this.tabObserver.start();
81 if (this.tabObserver) {
82 this.tabObserver.stop();
83 this.tabObserver = null;
88 * To be called right after instantiating a new Target instance.
89 * This will hold the new instance in the list and notify about
92 registerTarget(target) {
93 this._targets.set(target.id, target);
94 this.emit("target-created", target);
98 * To be called when the debuggable target has been destroy.
99 * So that we can notify it no longer exists and disconnect
100 * all connecting made to debug it.
102 destroyTarget(target) {
104 this._targets.delete(target.id);
105 this.emit("target-destroyed", target);
109 * Destroy all the registered target of all kinds.
110 * This will end up dropping all connections made to debug any of them.
113 for (const target of this) {
114 this.destroyTarget(target);
116 this._targets.clear();
117 if (this.mainProcessTarget) {
118 this.mainProcessTarget = null;
121 this.unwatchForTargets();
125 return this._targets.size;
129 * Get Target instance by target id
131 * @param int id Target id
134 return this._targets.get(id);
138 * Get the Target instance for the main process.
139 * This target is a singleton and only exposes a subset of domains.
141 getMainProcessTarget() {
142 if (!this.mainProcessTarget) {
143 this.mainProcessTarget = new MainProcessTarget(this);
144 this.registerTarget(this.mainProcessTarget);
146 return this.mainProcessTarget;
149 *[Symbol.iterator]() {
150 for (const target of this._targets.values()) {
160 return `[object Targets ${this.size}]`;