Source code

Revision control

Copy as Markdown

Other Tools

/* 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) {
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);
},
};