no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / remote / cdp / observers / TargetObserver.sys.mjs
blobdfd9e2d9dc7711a002c226497351cf9c4db0fd73
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 const lazy = {};
7 ChromeUtils.defineESModuleGetters(lazy, {
8   EventEmitter: "resource://gre/modules/EventEmitter.sys.mjs",
10   EventPromise: "chrome://remote/content/shared/Sync.sys.mjs",
11 });
13 // TODO(ato):
15 // The DOM team is working on pulling browsing context related behaviour,
16 // such as window and tab handling, out of product code and into the platform.
17 // This will have implication for the remote agent,
18 // and as the platform gains support for product-independent events
19 // we can likely get rid of this entire module.
21 /**
22  * Observe Firefox tabs as they open and close.
23  *
24  * "open" fires when a tab opens.
25  * "close" fires when a tab closes.
26  */
27 export class TabObserver {
28   /**
29    * @param {boolean?} [false] registerExisting
30    *     Events will be fired for ChromeWIndows and their respective tabs
31    *     at the time when the observer is started.
32    */
33   constructor({ registerExisting = false } = {}) {
34     lazy.EventEmitter.decorate(this);
36     this.registerExisting = registerExisting;
38     this.onTabOpen = this.onTabOpen.bind(this);
39     this.onTabClose = this.onTabClose.bind(this);
40   }
42   async start() {
43     Services.wm.addListener(this);
45     if (this.registerExisting) {
46       // Start listening for events on already open windows
47       for (const win of Services.wm.getEnumerator("navigator:browser")) {
48         this._registerDOMWindow(win);
49       }
50     }
51   }
53   stop() {
54     Services.wm.removeListener(this);
56     // Stop listening for events on still opened windows
57     for (const win of Services.wm.getEnumerator("navigator:browser")) {
58       this._unregisterDOMWindow(win);
59     }
60   }
62   // Event emitters
64   onTabOpen({ target }) {
65     this.emit("open", target);
66   }
68   onTabClose({ target }) {
69     this.emit("close", target);
70   }
72   // Internal methods
74   _registerDOMWindow(win) {
75     for (const tab of win.gBrowser.tabs) {
76       // a missing linkedBrowser means the tab is still initialising,
77       // and a TabOpen event will fire once it is ready
78       if (!tab.linkedBrowser) {
79         continue;
80       }
82       this.onTabOpen({ target: tab });
83     }
85     win.gBrowser.tabContainer.addEventListener("TabOpen", this.onTabOpen);
86     win.gBrowser.tabContainer.addEventListener("TabClose", this.onTabClose);
87   }
89   _unregisterDOMWindow(win) {
90     for (const tab of win.gBrowser.tabs) {
91       // a missing linkedBrowser means the tab is still initialising
92       if (!tab.linkedBrowser) {
93         continue;
94       }
96       // Emulate custom "TabClose" events because that event is not
97       // fired for each of the tabs when the window closes.
98       this.onTabClose({ target: tab });
99     }
101     win.gBrowser.tabContainer.removeEventListener("TabOpen", this.onTabOpen);
102     win.gBrowser.tabContainer.removeEventListener("TabClose", this.onTabClose);
103   }
105   // nsIWindowMediatorListener
107   async onOpenWindow(xulWindow) {
108     const win = xulWindow.docShell.domWindow;
110     await new lazy.EventPromise(win, "load");
112     // Return early if it's not a browser window
113     if (
114       win.document.documentElement.getAttribute("windowtype") !=
115       "navigator:browser"
116     ) {
117       return;
118     }
120     this._registerDOMWindow(win);
121   }
123   onCloseWindow(xulWindow) {
124     const win = xulWindow.docShell.domWindow;
126     // Return early if it's not a browser window
127     if (
128       win.document.documentElement.getAttribute("windowtype") !=
129       "navigator:browser"
130     ) {
131       return;
132     }
134     this._unregisterDOMWindow(win);
135   }
137   // XPCOM
139   get QueryInterface() {
140     return ChromeUtils.generateQI(["nsIWindowMediatorListener"]);
141   }