Bug 1568151 - Replace `target.getInspector()` by `target.getFront("inspector")`....
[gecko.git] / remote / targets / Targets.jsm
blobe7a7fe4331b68f1b500dc42a6347deac128d4f42
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/. */
5 "use strict";
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"
25 class Targets {
26   constructor() {
27     // Target ID -> Target
28     this._targets = new Map();
30     EventEmitter.decorate(this);
31   }
33   /**
34    * Start listing and listening for all the debuggable targets
35    */
36   async watchForTargets() {
37     await this.watchForTabs();
38   }
40   unwatchForTargets() {
41     this.unwatchForTabs();
42   }
44   /**
45    * Watch for all existing and new tabs being opened.
46    * So that we can create the related TabTarget instance for
47    * each of them.
48    */
49   async watchForTabs() {
50     if (this.tabObserver) {
51       throw new Error("Targets is already watching for new tabs");
52     }
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");
62       }
63       const target = new TabTarget(this, browser);
64       this.registerTarget(target);
65     });
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) {
70         return;
71       }
72       const target = this.getById(browser.browsingContext.id);
73       if (target) {
74         this.destroyTarget(target);
75       }
76     });
77     await this.tabObserver.start();
78   }
80   unwatchForTabs() {
81     if (this.tabObserver) {
82       this.tabObserver.stop();
83       this.tabObserver = null;
84     }
85   }
87   /**
88    * To be called right after instantiating a new Target instance.
89    * This will hold the new instance in the list and notify about
90    * its creation.
91    */
92   registerTarget(target) {
93     this._targets.set(target.id, target);
94     this.emit("target-created", target);
95   }
97   /**
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.
101    */
102   destroyTarget(target) {
103     target.destructor();
104     this._targets.delete(target.id);
105     this.emit("target-destroyed", target);
106   }
108   /**
109    * Destroy all the registered target of all kinds.
110    * This will end up dropping all connections made to debug any of them.
111    */
112   destructor() {
113     for (const target of this) {
114       this.destroyTarget(target);
115     }
116     this._targets.clear();
117     if (this.mainProcessTarget) {
118       this.mainProcessTarget = null;
119     }
121     this.unwatchForTargets();
122   }
124   get size() {
125     return this._targets.size;
126   }
128   /**
129    * Get Target instance by target id
130    *
131    * @param int id Target id
132    */
133   getById(id) {
134     return this._targets.get(id);
135   }
137   /**
138    * Get the Target instance for the main process.
139    * This target is a singleton and only exposes a subset of domains.
140    */
141   getMainProcessTarget() {
142     if (!this.mainProcessTarget) {
143       this.mainProcessTarget = new MainProcessTarget(this);
144       this.registerTarget(this.mainProcessTarget);
145     }
146     return this.mainProcessTarget;
147   }
149   *[Symbol.iterator]() {
150     for (const target of this._targets.values()) {
151       yield target;
152     }
153   }
155   toJSON() {
156     return [...this];
157   }
159   toString() {
160     return `[object Targets ${this.size}]`;
161   }