Backed out changeset 496886cb30a5 (bug 1867152) for bc failures on browser_user_input...
[gecko.git] / browser / modules / EveryWindow.sys.mjs
blob7df784a6e57ae1a704fdb9f3c18202f4a4b7187d
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 /*
6  * This module enables consumers to register callbacks on every
7  * current and future browser window.
8  *
9  * Usage: EveryWindow.registerCallback(id, init, uninit);
10  *        EveryWindow.unregisterCallback(id);
11  *
12  * id is expected to be a unique value that identifies the
13  * consumer, to be used for unregistration. If the id is already
14  * in use, registerCallback returns false without doing anything.
15  *
16  * Each callback will receive the window for which it is presently
17  * being called as the first argument.
18  *
19  * init is called on every existing window at the time of registration,
20  * and on all future windows at browser-delayed-startup-finished.
21  *
22  * uninit is called on every existing window if requested at the time
23  * of unregistration, and at the time of domwindowclosed.
24  * If the window is closing, a second argument is passed with value `true`.
25  */
27 var initialized = false;
28 var callbacks = new Map();
30 function callForEveryWindow(callback) {
31   let windowList = Services.wm.getEnumerator("navigator:browser");
32   for (let win of windowList) {
33     win.delayedStartupPromise.then(() => {
34       callback(win);
35     });
36   }
39 export const EveryWindow = {
40   /**
41    * Registers init and uninit functions to be called on every window.
42    *
43    * @param {string} id A unique identifier for the consumer, to be
44    *   used for unregistration.
45    * @param {function} init The function to be called on every currently
46    *   existing window and every future window after delayed startup.
47    * @param {function} uninit The function to be called on every window
48    *   at the time of callback unregistration or after domwindowclosed.
49    * @returns {boolean} Returns false if the id was taken, else true.
50    */
51   registerCallback: function EW_registerCallback(id, init, uninit) {
52     if (callbacks.has(id)) {
53       return false;
54     }
56     if (!initialized) {
57       let addUnloadListener = win => {
58         function observer(subject, topic, data) {
59           if (topic == "domwindowclosed" && subject === win) {
60             Services.ww.unregisterNotification(observer);
61             for (let c of callbacks.values()) {
62               c.uninit(win, true);
63             }
64           }
65         }
66         Services.ww.registerNotification(observer);
67       };
69       Services.obs.addObserver(win => {
70         for (let c of callbacks.values()) {
71           c.init(win);
72         }
73         addUnloadListener(win);
74       }, "browser-delayed-startup-finished");
76       callForEveryWindow(addUnloadListener);
78       initialized = true;
79     }
81     callForEveryWindow(init);
82     callbacks.set(id, { id, init, uninit });
84     return true;
85   },
87   /**
88    * Unregisters a previously registered consumer.
89    *
90    * @param {string} id The id to unregister.
91    * @param {boolean} [callUninit=true] Whether to call the registered uninit
92    *   function on every window.
93    */
94   unregisterCallback: function EW_unregisterCallback(id, callUninit = true) {
95     if (!callbacks.has(id)) {
96       return;
97     }
99     if (callUninit) {
100       callForEveryWindow(callbacks.get(id).uninit);
101     }
103     callbacks.delete(id);
104   },