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 (a85b80d77adc)

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 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
/* 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 = [
  "TabManager",
  "TabObserver",
  "WindowObserver",
];

const {DOMContentLoadedPromise} = ChromeUtils.import("chrome://remote/content/Sync.jsm");
const {EventEmitter} = ChromeUtils.import("resource://gre/modules/EventEmitter.jsm");
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");

/**
 * The WindowManager provides tooling for application-agnostic
 * observation of windows, tabs, and content browsers as they are
 * created and destroyed.
 */

// TODO(ato):
//
// The DOM team is working on pulling browsing context related behaviour,
// such as window and tab handling, out of product code and into the platform.
// This will have implication for the remote agent,
// and as the platform gains support for product-independent events
// we can likely get rid of this entire module.

/**
 * Observes DOMWindows as they open and close.
 *
 * "open" fires when a window opens.
 * "close" fires when a window closes.
 */
class WindowObserver {
  /**
   * @param {boolean?} [false] registerExisting
   *     Events will be despatched for the ChromeWindows that exist
   *     at the time the observer is started.
   */
  constructor({registerExisting = false} = {}) {
    this.registerExisting = registerExisting;
    EventEmitter.decorate(this);
  }

  async start() {
    if (this.registerExisting) {
      for (const window of Services.wm.getEnumerator("navigator:browser")) {
        await this.onOpenWindow(window);
      }
    }

    Services.wm.addListener(this);
  }

  stop() {
    Services.wm.removeListener(this);
  }

  // nsIWindowMediatorListener

  async onOpenWindow(xulWindow) {
    const window = xulWindow
        .QueryInterface(Ci.nsIInterfaceRequestor)
        .getInterface(Ci.nsIDOMWindow);
    await new DOMContentLoadedPromise(window);
    this.emit("open", window);
  }

  onCloseWindow(xulWindow) {
    const window = xulWindow
        .QueryInterface(Ci.nsIInterfaceRequestor)
        .getInterface(Ci.nsIDOMWindow);
    this.emit("close", window);
  }

  // XPCOM

  get QueryInterface() {
    return ChromeUtils.generateQI([Ci.nsIWindowMediatorListener]);
  }
}

/**
 * Observe Firefox tabs as they open and close.
 *
 * "open" fires when a tab opens.
 * "close" fires when a tab closes.
 */
class TabObserver {
  /**
   * @param {boolean?} [false] registerExisting
   *     Events will be fired for ChromeWIndows and their respective tabs
   *     at the time when the observer is started.
   */
  constructor({registerExisting = false} = {}) {
    this.windows = new WindowObserver({registerExisting});
    EventEmitter.decorate(this);
  }

  async start() {
    this.windows.on("open", this.onWindowOpen.bind(this));
    this.windows.on("close", this.onWindowClose.bind(this));
    await this.windows.start();
  }

  stop() {
    this.windows.off("open");
    this.windows.off("close");
    this.windows.stop();
  }

  onTabOpen(tab) {
    this.emit("open", tab);
  }

  onTabClose(tab) {
    this.emit("close", tab);
  }

  // WindowObserver

  async onWindowOpen(eventName, window) {
    if (!window.gBrowser) {
      return;
    }

    for (const tab of window.gBrowser.tabs) {
      // a missing linkedBrowser means the tab is still initialising,
      // and a TabOpen event will fire once it is ready
      if (!tab.linkedBrowser) {
        continue;
      }
      this.onTabOpen(tab);
    }

    window.addEventListener("TabOpen", ({target}) => this.onTabOpen(target));
    window.addEventListener("TabClose", ({target}) => this.onTabClose(target));
  }

  onWindowClose(window) {
    // TODO(ato): Is TabClose fired when the window closes?
  }
}

var TabManager = {
  addTab() {
    const window = Services.wm.getMostRecentWindow("navigator:browser");
    const { gBrowser } = window;
    const tab = gBrowser.addTab("about:blank", {
      triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
    });
    gBrowser.selectedTab = tab;
    return tab;
  },
};