summaryrefslogtreecommitdiffstats
path: root/remote/cdp/observers/TargetObserver.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'remote/cdp/observers/TargetObserver.sys.mjs')
-rw-r--r--remote/cdp/observers/TargetObserver.sys.mjs142
1 files changed, 142 insertions, 0 deletions
diff --git a/remote/cdp/observers/TargetObserver.sys.mjs b/remote/cdp/observers/TargetObserver.sys.mjs
new file mode 100644
index 0000000000..dfd9e2d9dc
--- /dev/null
+++ b/remote/cdp/observers/TargetObserver.sys.mjs
@@ -0,0 +1,142 @@
+/* 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",
+
+ EventPromise: "chrome://remote/content/shared/Sync.sys.mjs",
+});
+
+// TODO(ato):
+//
+// The DOM team is working on pulling browsing context related behaviour,
+// such as window and tab handling, out of product code and into the platform.
+// This will have implication for the remote agent,
+// and as the platform gains support for product-independent events
+// we can likely get rid of this entire module.
+
+/**
+ * Observe Firefox tabs as they open and close.
+ *
+ * "open" fires when a tab opens.
+ * "close" fires when a tab closes.
+ */
+export class TabObserver {
+ /**
+ * @param {boolean?} [false] registerExisting
+ * Events will be fired for ChromeWIndows and their respective tabs
+ * at the time when the observer is started.
+ */
+ constructor({ registerExisting = false } = {}) {
+ lazy.EventEmitter.decorate(this);
+
+ this.registerExisting = registerExisting;
+
+ this.onTabOpen = this.onTabOpen.bind(this);
+ this.onTabClose = this.onTabClose.bind(this);
+ }
+
+ async start() {
+ Services.wm.addListener(this);
+
+ if (this.registerExisting) {
+ // Start listening for events on already open windows
+ for (const win of Services.wm.getEnumerator("navigator:browser")) {
+ this._registerDOMWindow(win);
+ }
+ }
+ }
+
+ stop() {
+ Services.wm.removeListener(this);
+
+ // Stop listening for events on still opened windows
+ for (const win of Services.wm.getEnumerator("navigator:browser")) {
+ this._unregisterDOMWindow(win);
+ }
+ }
+
+ // Event emitters
+
+ onTabOpen({ target }) {
+ this.emit("open", target);
+ }
+
+ onTabClose({ target }) {
+ this.emit("close", target);
+ }
+
+ // Internal methods
+
+ _registerDOMWindow(win) {
+ for (const tab of win.gBrowser.tabs) {
+ // a missing linkedBrowser means the tab is still initialising,
+ // and a TabOpen event will fire once it is ready
+ if (!tab.linkedBrowser) {
+ continue;
+ }
+
+ this.onTabOpen({ target: tab });
+ }
+
+ win.gBrowser.tabContainer.addEventListener("TabOpen", this.onTabOpen);
+ win.gBrowser.tabContainer.addEventListener("TabClose", this.onTabClose);
+ }
+
+ _unregisterDOMWindow(win) {
+ for (const tab of win.gBrowser.tabs) {
+ // a missing linkedBrowser means the tab is still initialising
+ if (!tab.linkedBrowser) {
+ continue;
+ }
+
+ // Emulate custom "TabClose" events because that event is not
+ // fired for each of the tabs when the window closes.
+ this.onTabClose({ target: tab });
+ }
+
+ win.gBrowser.tabContainer.removeEventListener("TabOpen", this.onTabOpen);
+ win.gBrowser.tabContainer.removeEventListener("TabClose", this.onTabClose);
+ }
+
+ // nsIWindowMediatorListener
+
+ async onOpenWindow(xulWindow) {
+ const win = xulWindow.docShell.domWindow;
+
+ await new lazy.EventPromise(win, "load");
+
+ // Return early if it's not a browser window
+ if (
+ win.document.documentElement.getAttribute("windowtype") !=
+ "navigator:browser"
+ ) {
+ return;
+ }
+
+ this._registerDOMWindow(win);
+ }
+
+ onCloseWindow(xulWindow) {
+ const win = xulWindow.docShell.domWindow;
+
+ // Return early if it's not a browser window
+ if (
+ win.document.documentElement.getAttribute("windowtype") !=
+ "navigator:browser"
+ ) {
+ return;
+ }
+
+ this._unregisterDOMWindow(win);
+ }
+
+ // XPCOM
+
+ get QueryInterface() {
+ return ChromeUtils.generateQI(["nsIWindowMediatorListener"]);
+ }
+}