tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

EveryWindow.sys.mjs (3561B)


      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/. */
      4 
      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 */
     26 
     27 var initialized = false;
     28 var callbacks = new Map();
     29 
     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  }
     37 }
     38 
     39 export const EveryWindow = {
     40  /**
     41   * The current list of all browser windows whose delayedStartupPromise has resolved
     42   */
     43  get readyWindows() {
     44    return Array.from(Services.wm.getEnumerator("navigator:browser")).filter(
     45      win => win.gBrowserInit?.delayedStartupFinished
     46    );
     47  },
     48 
     49  /**
     50   * Registers init and uninit functions to be called on every window.
     51   *
     52   * @param {string} id A unique identifier for the consumer, to be
     53   *   used for unregistration.
     54   * @param {function} init The function to be called on every currently
     55   *   existing window and every future window after delayed startup.
     56   * @param {function} uninit The function to be called on every window
     57   *   at the time of callback unregistration or after domwindowclosed.
     58   * @returns {boolean} Returns false if the id was taken, else true.
     59   */
     60  registerCallback: function EW_registerCallback(id, init, uninit) {
     61    if (callbacks.has(id)) {
     62      return false;
     63    }
     64 
     65    if (!initialized) {
     66      let addUnloadListener = win => {
     67        function observer(subject, topic) {
     68          if (topic == "domwindowclosed" && subject === win) {
     69            Services.ww.unregisterNotification(observer);
     70            for (let c of callbacks.values()) {
     71              c.uninit(win, true);
     72            }
     73          }
     74        }
     75        Services.ww.registerNotification(observer);
     76      };
     77 
     78      Services.obs.addObserver(win => {
     79        for (let c of callbacks.values()) {
     80          c.init(win);
     81        }
     82        addUnloadListener(win);
     83      }, "browser-delayed-startup-finished");
     84 
     85      callForEveryWindow(addUnloadListener);
     86 
     87      initialized = true;
     88    }
     89 
     90    callForEveryWindow(init);
     91    callbacks.set(id, { id, init, uninit });
     92 
     93    return true;
     94  },
     95 
     96  /**
     97   * Unregisters a previously registered consumer.
     98   *
     99   * @param {string} id The id to unregister.
    100   * @param {boolean} [callUninit=true] Whether to call the registered uninit
    101   *   function on every window.
    102   */
    103  unregisterCallback: function EW_unregisterCallback(id, callUninit = true) {
    104    if (!callbacks.has(id)) {
    105      return;
    106    }
    107 
    108    if (callUninit) {
    109      callForEveryWindow(callbacks.get(id).uninit);
    110    }
    111 
    112    callbacks.delete(id);
    113  },
    114 };