| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209 | /* 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";module.metadata = {  "stability": "unstable"};const { Trait } = require("../deprecated/traits");const { List } = require("../deprecated/list");const { Tab } = require("../tabs/tab");const { EventEmitter } = require("../deprecated/events");const { EVENTS } = require("../tabs/events");const { getOwnerWindow, getActiveTab, getTabs,        openTab } = require("../tabs/utils");const { Options } = require("../tabs/common");const { observer: tabsObserver } = require("../tabs/observer");const { ignoreWindow } = require("../private-browsing/utils");const TAB_BROWSER = "tabbrowser";/** * This is a trait that is used in composition of window wrapper. Trait tracks * tab related events of the wrapped window in order to keep track of open * tabs and maintain their wrappers. Every new tab gets wrapped and jetpack * type event is emitted. */const WindowTabTracker = Trait.compose({  /**   * Chrome window whose tabs are tracked.   */  _window: Trait.required,  /**   * Function used to emit events.   */  _emit: EventEmitter.required,  _tabOptions: Trait.required,  /**   * Function to add event listeners.   */  on: EventEmitter.required,  removeListener: EventEmitter.required,  /**   * Initializes tab tracker for a browser window.   */  _initWindowTabTracker: function _initWindowTabTracker() {    // Ugly hack that we have to remove at some point (see Bug 658059). At this    // point it is necessary to invoke lazy `tabs` getter on the windows object    // which creates a `TabList` instance.    this.tabs;    // Binding all methods used as event listeners to the instance.    this._onTabReady = this._emitEvent.bind(this, "ready");    this._onTabLoad = this._emitEvent.bind(this, "load");    this._onTabPageShow = this._emitEvent.bind(this, "pageshow");    this._onTabOpen = this._onTabEvent.bind(this, "open");    this._onTabClose = this._onTabEvent.bind(this, "close");    this._onTabActivate = this._onTabEvent.bind(this, "activate");    this._onTabDeactivate = this._onTabEvent.bind(this, "deactivate");    this._onTabPinned = this._onTabEvent.bind(this, "pinned");    this._onTabUnpinned = this._onTabEvent.bind(this, "unpinned");    for each (let tab in getTabs(this._window)) {      // We emulate "open" events for all open tabs since gecko does not emits      // them on the tabs that new windows are open with. Also this is      // necessary to synchronize tabs lists with an actual state.      this._onTabOpen(tab);    }    // We also emulate "activate" event so that it's picked up by a tab list.    this._onTabActivate(getActiveTab(this._window));    // Setting up event listeners    tabsObserver.on("open", this._onTabOpen);    tabsObserver.on("close", this._onTabClose);    tabsObserver.on("activate", this._onTabActivate);    tabsObserver.on("deactivate", this._onTabDeactivate);    tabsObserver.on("pinned", this._onTabPinned);    tabsObserver.on("unpinned", this._onTabUnpinned);  },  _destroyWindowTabTracker: function _destroyWindowTabTracker() {    // We emulate close events on all tabs, since gecko does not emits such    // events by itself.    for each (let tab in this.tabs)      this._emitEvent("close", tab);    this._tabs._clear();    tabsObserver.removeListener("open", this._onTabOpen);    tabsObserver.removeListener("close", this._onTabClose);    tabsObserver.removeListener("activate", this._onTabActivate);    tabsObserver.removeListener("deactivate", this._onTabDeactivate);  },  _onTabEvent: function _onTabEvent(type, tab) {    // Accept only tabs for the watched window, and ignore private tabs    // if addon doesn't have private permission    if (this._window === getOwnerWindow(tab) && !ignoreWindow(this._window)) {      let options = this._tabOptions.shift() || {};      options.tab = tab;      options.window = this._public;      // Ignore zombie tabs on open that have already been removed      if (type == "open" && !tab.linkedBrowser)        return;      // Create a tab wrapper on open event, otherwise, just fetch existing      // tab object      let wrappedTab = Tab(options, type !== "open");      if (!wrappedTab)        return;      // Setting up an event listener for ready events.      if (type === "open") {        wrappedTab.on("ready", this._onTabReady);        wrappedTab.on("load", this._onTabLoad);        wrappedTab.on("pageshow", this._onTabPageShow);      }      this._emitEvent(type, wrappedTab);    }  },  _emitEvent: function _emitEvent(type, tag) {    // Slices additional arguments and passes them into exposed    // listener like other events (for pageshow)    let args = Array.slice(arguments);    // Notifies combined tab list that tab was added / removed.    tabs._emit.apply(tabs, args);    // Notifies contained tab list that window was added / removed.    this._tabs._emit.apply(this._tabs, args);  }});exports.WindowTabTracker = WindowTabTracker;/** * This trait is used to create live representation of open tab lists. Each * window wrapper's tab list is represented by an object created from this * trait. It is also used to represent list of all the open windows. Trait is * composed out of `EventEmitter` in order to emit 'TabOpen', 'TabClose' events. * **Please note** that objects created by this trait can't be exposed outside * instead you should expose it's `_public` property, see comments in * constructor for details. */const TabList = List.resolve({ constructor: "_init" }).compose(  // This is ugly, but necessary. Will be removed by #596248  EventEmitter.resolve({ toString: null }),  Trait.compose({    on: Trait.required,    _emit: Trait.required,    constructor: function TabList(options) {      this._window = options.window;      // Add new items to the list      this.on(EVENTS.open.name, this._add.bind(this));      // Remove closed items from the list      this.on(EVENTS.close.name, this._remove.bind(this));      // Set value whenever new tab becomes active.      this.on("activate", function onTabActivate(tab) {        this._activeTab = tab;      }.bind(this));      // Initialize list.      this._init();      // This list is not going to emit any events, object holding this list      // will do it instead, to make that possible we return a private API.      return this;    },    get activeTab() this._activeTab,    _activeTab: null,    open: function open(options) {      let window = this._window;      let chromeWindow = window._window;      options = Options(options);      // save the tab options      window._tabOptions.push(options);      // open the tab      let tab = openTab(chromeWindow, options.url, options);    }  // This is ugly, but necessary. Will be removed by #596248  }).resolve({ toString: null }));/** * Combined list of all open tabs on all the windows. * type {TabList} */var tabs = TabList({ window: null });exports.tabs = tabs._public;/** * Trait is a part of composition that represents window wrapper. This trait is * composed out of `WindowTabTracker` that allows it to keep track of open tabs * on the window being wrapped. */const WindowTabs = Trait.compose(  WindowTabTracker,  Trait.compose({    _window: Trait.required,    /**     * List of tabs     */    get tabs() (this._tabs || (this._tabs = TabList({ window: this })))._public,    _tabs: null,  }));exports.WindowTabs = WindowTabs;
 |