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 = [
13 const {DOMContentLoadedPromise} = ChromeUtils.import("chrome://remote/content/Sync.jsm");
14 const {EventEmitter} = ChromeUtils.import("resource://gre/modules/EventEmitter.jsm");
15 const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
18 * The WindowManager provides tooling for application-agnostic
19 * observation of windows, tabs, and content browsers as they are
20 * created and destroyed.
25 // The DOM team is working on pulling browsing context related behaviour,
26 // such as window and tab handling, out of product code and into the platform.
27 // This will have implication for the remote agent,
28 // and as the platform gains support for product-independent events
29 // we can likely get rid of this entire module.
32 * Observes DOMWindows as they open and close.
34 * "open" fires when a window opens.
35 * "close" fires when a window closes.
37 class WindowObserver {
39 * @param {boolean?} [false] registerExisting
40 * Events will be despatched for the ChromeWindows that exist
41 * at the time the observer is started.
43 constructor({registerExisting = false} = {}) {
44 this.registerExisting = registerExisting;
45 EventEmitter.decorate(this);
49 if (this.registerExisting) {
50 for (const window of Services.wm.getEnumerator("navigator:browser")) {
51 await this.onOpenWindow(window);
55 Services.wm.addListener(this);
59 Services.wm.removeListener(this);
62 // nsIWindowMediatorListener
64 async onOpenWindow(xulWindow) {
65 const window = xulWindow
66 .QueryInterface(Ci.nsIInterfaceRequestor)
67 .getInterface(Ci.nsIDOMWindow);
68 await new DOMContentLoadedPromise(window);
69 this.emit("open", window);
72 onCloseWindow(xulWindow) {
73 const window = xulWindow
74 .QueryInterface(Ci.nsIInterfaceRequestor)
75 .getInterface(Ci.nsIDOMWindow);
76 this.emit("close", window);
81 get QueryInterface() {
82 return ChromeUtils.generateQI([Ci.nsIWindowMediatorListener]);
87 * Observe Firefox tabs as they open and close.
89 * "open" fires when a tab opens.
90 * "close" fires when a tab closes.
94 * @param {boolean?} [false] registerExisting
95 * Events will be fired for ChromeWIndows and their respective tabs
96 * at the time when the observer is started.
98 constructor({registerExisting = false} = {}) {
99 this.windows = new WindowObserver({registerExisting});
100 EventEmitter.decorate(this);
104 this.windows.on("open", this.onWindowOpen.bind(this));
105 this.windows.on("close", this.onWindowClose.bind(this));
106 await this.windows.start();
110 this.windows.off("open");
111 this.windows.off("close");
116 this.emit("open", tab);
120 this.emit("close", tab);
125 async onWindowOpen(eventName, window) {
126 if (!window.gBrowser) {
130 for (const tab of window.gBrowser.tabs) {
131 // a missing linkedBrowser means the tab is still initialising,
132 // and a TabOpen event will fire once it is ready
133 if (!tab.linkedBrowser) {
139 window.addEventListener("TabOpen", ({target}) => this.onTabOpen(target));
140 window.addEventListener("TabClose", ({target}) => this.onTabClose(target));
143 onWindowClose(window) {
144 // TODO(ato): Is TabClose fired when the window closes?
150 const window = Services.wm.getMostRecentWindow("navigator:browser");
151 const { gBrowser } = window;
152 const tab = gBrowser.addTab("about:blank", {
153 triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
155 gBrowser.selectedTab = tab;