DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (c584e29f6ac2)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
/* 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/. */

"use strict";

var EXPORTED_SYMBOLS = ["EveryWindow"];

/*
 * 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`.
 */

const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");

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);
    });
  }
}

this.EveryWindow = {
  /**
   * 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);
  },
};