summaryrefslogtreecommitdiffstats
path: root/browser/modules/EveryWindow.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'browser/modules/EveryWindow.sys.mjs')
-rw-r--r--browser/modules/EveryWindow.sys.mjs114
1 files changed, 114 insertions, 0 deletions
diff --git a/browser/modules/EveryWindow.sys.mjs b/browser/modules/EveryWindow.sys.mjs
new file mode 100644
index 0000000000..704240b54f
--- /dev/null
+++ b/browser/modules/EveryWindow.sys.mjs
@@ -0,0 +1,114 @@
+/* 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/. */
+
+/*
+ * This module enables consumers to register callbacks on every
+ * current and future browser window.
+ *
+ * Usage: EveryWindow.registerCallback(id, init, uninit);
+ * EveryWindow.unregisterCallback(id);
+ *
+ * id is expected to be a unique value that identifies the
+ * consumer, to be used for unregistration. If the id is already
+ * in use, registerCallback returns false without doing anything.
+ *
+ * Each callback will receive the window for which it is presently
+ * being called as the first argument.
+ *
+ * init is called on every existing window at the time of registration,
+ * and on all future windows at browser-delayed-startup-finished.
+ *
+ * uninit is called on every existing window if requested at the time
+ * of unregistration, and at the time of domwindowclosed.
+ * If the window is closing, a second argument is passed with value `true`.
+ */
+
+var initialized = false;
+var callbacks = new Map();
+
+function callForEveryWindow(callback) {
+ let windowList = Services.wm.getEnumerator("navigator:browser");
+ for (let win of windowList) {
+ win.delayedStartupPromise.then(() => {
+ callback(win);
+ });
+ }
+}
+
+export const EveryWindow = {
+ /**
+ * The current list of all browser windows whose delayedStartupPromise has resolved
+ */
+ get readyWindows() {
+ return Array.from(Services.wm.getEnumerator("navigator:browser")).filter(
+ win => win.gBrowserInit?.delayedStartupFinished
+ );
+ },
+
+ /**
+ * Registers init and uninit functions to be called on every window.
+ *
+ * @param {string} id A unique identifier for the consumer, to be
+ * used for unregistration.
+ * @param {function} init The function to be called on every currently
+ * existing window and every future window after delayed startup.
+ * @param {function} uninit The function to be called on every window
+ * at the time of callback unregistration or after domwindowclosed.
+ * @returns {boolean} Returns false if the id was taken, else true.
+ */
+ registerCallback: function EW_registerCallback(id, init, uninit) {
+ if (callbacks.has(id)) {
+ return false;
+ }
+
+ if (!initialized) {
+ let addUnloadListener = win => {
+ function observer(subject, topic, data) {
+ if (topic == "domwindowclosed" && subject === win) {
+ Services.ww.unregisterNotification(observer);
+ for (let c of callbacks.values()) {
+ c.uninit(win, true);
+ }
+ }
+ }
+ Services.ww.registerNotification(observer);
+ };
+
+ Services.obs.addObserver(win => {
+ for (let c of callbacks.values()) {
+ c.init(win);
+ }
+ addUnloadListener(win);
+ }, "browser-delayed-startup-finished");
+
+ callForEveryWindow(addUnloadListener);
+
+ initialized = true;
+ }
+
+ callForEveryWindow(init);
+ callbacks.set(id, { id, init, uninit });
+
+ return true;
+ },
+
+ /**
+ * Unregisters a previously registered consumer.
+ *
+ * @param {string} id The id to unregister.
+ * @param {boolean} [callUninit=true] Whether to call the registered uninit
+ * function on every window.
+ */
+ unregisterCallback: function EW_unregisterCallback(id, callUninit = true) {
+ if (!callbacks.has(id)) {
+ return;
+ }
+
+ if (callUninit) {
+ callForEveryWindow(callbacks.get(id).uninit);
+ }
+
+ callbacks.delete(id);
+ },
+};