forked from mirrors/gecko-dev
		
	 fcedebb912
			
		
	
	
		fcedebb912
		
	
	
	
	
		
			
			MozReview-Commit-ID: Fqlv5BRuuW8 --HG-- extra : rebase_source : 348f037abd9cecfa080183bc365e5f005eac1bd6 extra : amend_source : 05dbfd12f553fc3f2a93374402e34d271e26d767
		
			
				
	
	
		
			749 lines
		
	
	
	
		
			19 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			749 lines
		
	
	
	
		
			19 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 | |
| /* vim: set sts=2 sw=2 et tw=80: */
 | |
| "use strict";
 | |
| 
 | |
| ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
 | |
|                                "resource://gre/modules/PrivateBrowsingUtils.jsm");
 | |
| 
 | |
| /* globals TabBase, WindowBase, TabTrackerBase, WindowTrackerBase, TabManagerBase, WindowManagerBase */
 | |
| /* globals EventDispatcher */
 | |
| ChromeUtils.import("resource://gre/modules/Messaging.jsm");
 | |
| 
 | |
| ChromeUtils.import("resource://gre/modules/ExtensionCommon.jsm");
 | |
| ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");
 | |
| 
 | |
| var {
 | |
|   DefaultWeakMap,
 | |
|   ExtensionError,
 | |
| } = ExtensionUtils;
 | |
| 
 | |
| var {
 | |
|   defineLazyGetter,
 | |
| } = ExtensionCommon;
 | |
| 
 | |
| global.GlobalEventDispatcher = EventDispatcher.instance;
 | |
| 
 | |
| const BrowserStatusFilter = Components.Constructor(
 | |
|   "@mozilla.org/appshell/component/browser-status-filter;1", "nsIWebProgress",
 | |
|   "addProgressListener");
 | |
| 
 | |
| let tabTracker;
 | |
| let windowTracker;
 | |
| 
 | |
| /**
 | |
|  * A nsIWebProgressListener for a specific XUL browser, which delegates the
 | |
|  * events that it receives to a tab progress listener, and prepends the browser
 | |
|  * to their arguments list.
 | |
|  *
 | |
|  * @param {XULElement} browser
 | |
|  *        A XUL browser element.
 | |
|  * @param {object} listener
 | |
|  *        A tab progress listener object.
 | |
|  * @param {integer} flags
 | |
|  *        The web progress notification flags with which to filter events.
 | |
|  */
 | |
| class BrowserProgressListener {
 | |
|   constructor(browser, listener, flags) {
 | |
|     this.listener = listener;
 | |
|     this.browser = browser;
 | |
|     this.filter = new BrowserStatusFilter(this, flags);
 | |
|     this.browser.addProgressListener(this.filter, flags);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Destroy the listener, and perform any necessary cleanup.
 | |
|    */
 | |
|   destroy() {
 | |
|     this.browser.removeProgressListener(this.filter);
 | |
|     this.filter.removeProgressListener(this);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Calls the appropriate listener in the wrapped tab progress listener, with
 | |
|    * the wrapped XUL browser object as its first argument, and the additional
 | |
|    * arguments in `args`.
 | |
|    *
 | |
|    * @param {string} method
 | |
|    *        The name of the nsIWebProgressListener method which is being
 | |
|    *        delegated.
 | |
|    * @param {*} args
 | |
|    *        The arguments to pass to the delegated listener.
 | |
|    * @private
 | |
|    */
 | |
|   delegate(method, ...args) {
 | |
|     if (this.listener[method]) {
 | |
|       this.listener[method](this.browser, ...args);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   onLocationChange(webProgress, request, locationURI, flags) {
 | |
|     this.delegate("onLocationChange", webProgress, request, locationURI, flags);
 | |
|   }
 | |
|   onStateChange(webProgress, request, stateFlags, status) {
 | |
|     this.delegate("onStateChange", webProgress, request, stateFlags, status);
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Handles wrapping a tab progress listener in browser-specific
 | |
|  * BrowserProgressListener instances, an attaching them to each tab in a given
 | |
|  * browser window.
 | |
|  *
 | |
|  * @param {DOMWindow} window
 | |
|  *        The browser window to which to attach the listeners.
 | |
|  * @param {object} listener
 | |
|  *        The tab progress listener to wrap.
 | |
|  */
 | |
| class ProgressListenerWrapper {
 | |
|   constructor(window, listener) {
 | |
|     this.window = window;
 | |
|     this.listener = listener;
 | |
|     this.listeners = new WeakMap();
 | |
| 
 | |
|     this.flags = Ci.nsIWebProgress.NOTIFY_STATE_ALL |
 | |
|                  Ci.nsIWebProgress.NOTIFY_LOCATION;
 | |
| 
 | |
|     for (let nativeTab of this.window.BrowserApp.tabs) {
 | |
|       this.addBrowserProgressListener(nativeTab.browser);
 | |
|     }
 | |
| 
 | |
|     this.window.BrowserApp.deck.addEventListener("TabOpen", this);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Destroy the wrapper, removing any remaining listeners it has added.
 | |
|    */
 | |
|   destroy() {
 | |
|     this.window.BrowserApp.deck.removeEventListener("TabOpen", this);
 | |
| 
 | |
|     for (let nativeTab of this.window.BrowserApp.tabs) {
 | |
|       this.removeProgressListener(nativeTab.browser);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Adds a progress listener to the given XUL browser element.
 | |
|    *
 | |
|    * @param {XULElement} browser
 | |
|    *        The XUL browser to add the listener to.
 | |
|    * @private
 | |
|    */
 | |
|   addBrowserProgressListener(browser) {
 | |
|     this.removeProgressListener(browser);
 | |
| 
 | |
|     let listener = new BrowserProgressListener(browser, this.listener, this.flags);
 | |
|     this.listeners.set(browser, listener);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Removes a progress listener from the given XUL browser element.
 | |
|    *
 | |
|    * @param {XULElement} browser
 | |
|    *        The XUL browser to remove the listener from.
 | |
|    * @private
 | |
|    */
 | |
|   removeProgressListener(browser) {
 | |
|     let listener = this.listeners.get(browser);
 | |
|     if (listener) {
 | |
|       listener.destroy();
 | |
|       this.listeners.delete(browser);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Handles tab open events, and adds the necessary progress listeners to the
 | |
|    * new tabs.
 | |
|    *
 | |
|    * @param {Event} event
 | |
|    *        The DOM event to handle.
 | |
|    * @private
 | |
|    */
 | |
|   handleEvent(event) {
 | |
|     if (event.type === "TabOpen") {
 | |
|       this.addBrowserProgressListener(event.originalTarget);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| class WindowTracker extends WindowTrackerBase {
 | |
|   constructor(...args) {
 | |
|     super(...args);
 | |
| 
 | |
|     this.progressListeners = new DefaultWeakMap(() => new WeakMap());
 | |
|   }
 | |
| 
 | |
|   addProgressListener(window, listener) {
 | |
|     let listeners = this.progressListeners.get(window);
 | |
|     if (!listeners.has(listener)) {
 | |
|       let wrapper = new ProgressListenerWrapper(window, listener);
 | |
|       listeners.set(listener, wrapper);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   removeProgressListener(window, listener) {
 | |
|     let listeners = this.progressListeners.get(window);
 | |
|     let wrapper = listeners.get(listener);
 | |
|     if (wrapper) {
 | |
|       wrapper.destroy();
 | |
|       listeners.delete(listener);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Helper to create an event manager which listens for an event in the Android
 | |
|  * global EventDispatcher, and calls the given listener function whenever the
 | |
|  * event is received. That listener function receives a `fire` object,
 | |
|  * which it can use to dispatch events to the extension, and an object
 | |
|  * detailing the EventDispatcher event that was received.
 | |
|  *
 | |
|  * @param {BaseContext} context
 | |
|  *        The extension context which the event manager belongs to.
 | |
|  * @param {string} name
 | |
|  *        The API name of the event manager, e.g.,"runtime.onMessage".
 | |
|  * @param {string} event
 | |
|  *        The name of the EventDispatcher event to listen for.
 | |
|  * @param {function} listener
 | |
|  *        The listener function to call when an EventDispatcher event is
 | |
|  *        recieved.
 | |
|  *
 | |
|  * @returns {object} An injectable api for the new event.
 | |
|  */
 | |
| global.makeGlobalEvent = function makeGlobalEvent(context, name, event, listener) {
 | |
|   return new EventManager({
 | |
|     context,
 | |
|     name,
 | |
|     register: fire => {
 | |
|       let listener2 = {
 | |
|         onEvent(event, data, callback) {
 | |
|           listener(fire, data);
 | |
|         },
 | |
|       };
 | |
| 
 | |
|       GlobalEventDispatcher.registerListener(listener2, [event]);
 | |
|       return () => {
 | |
|         GlobalEventDispatcher.unregisterListener(listener2, [event]);
 | |
|       };
 | |
|     },
 | |
|   }).api();
 | |
| };
 | |
| 
 | |
| class TabTracker extends TabTrackerBase {
 | |
|   constructor() {
 | |
|     super();
 | |
| 
 | |
|     // Keep track of the extension popup tab.
 | |
|     this._extensionPopupTabWeak = null;
 | |
|     // Keep track of the selected tabId
 | |
|     this._selectedTabId = null;
 | |
|   }
 | |
| 
 | |
|   init() {
 | |
|     if (this.initialized) {
 | |
|       return;
 | |
|     }
 | |
|     this.initialized = true;
 | |
| 
 | |
|     windowTracker.addListener("TabClose", this);
 | |
|     windowTracker.addListener("TabOpen", this);
 | |
| 
 | |
|     // Register a listener for the Tab:Selected global event,
 | |
|     // so that we can close the popup when a popup tab has been
 | |
|     // unselected.
 | |
|     GlobalEventDispatcher.registerListener(this, [
 | |
|       "Tab:Selected",
 | |
|     ]);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Returns the currently opened popup tab if any
 | |
|    */
 | |
|   get extensionPopupTab() {
 | |
|     if (this._extensionPopupTabWeak) {
 | |
|       const tab = this._extensionPopupTabWeak.get();
 | |
| 
 | |
|       // Return the native tab only if the tab has not been removed in the meantime.
 | |
|       if (tab.browser) {
 | |
|         return tab;
 | |
|       }
 | |
| 
 | |
|       // Clear the tracked popup tab if it has been closed in the meantime.
 | |
|       this._extensionPopupTabWeak = null;
 | |
|     }
 | |
| 
 | |
|     return undefined;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Open a pageAction/browserAction popup url in a tab and keep track of
 | |
|    * its weak reference (to be able to customize the activedTab using the tab parentId,
 | |
|    * to skip it in the tabs.query and to set the parent tab as active when the popup
 | |
|    * tab is currently selected).
 | |
|    *
 | |
|    * @param {string} popup
 | |
|    *   The popup url to open in a tab.
 | |
|    */
 | |
|   openExtensionPopupTab(popup) {
 | |
|     let win = windowTracker.topWindow;
 | |
|     if (!win) {
 | |
|       throw new ExtensionError(`Unable to open a popup without an active window`);
 | |
|     }
 | |
| 
 | |
|     if (this.extensionPopupTab) {
 | |
|       win.BrowserApp.closeTab(this.extensionPopupTab);
 | |
|     }
 | |
| 
 | |
|     this.init();
 | |
| 
 | |
|     this._extensionPopupTabWeak = Cu.getWeakReference(win.BrowserApp.addTab(popup, {
 | |
|       selected: true,
 | |
|       parentId: win.BrowserApp.selectedTab.id,
 | |
|     }));
 | |
|   }
 | |
| 
 | |
|   getId(nativeTab) {
 | |
|     return nativeTab.id;
 | |
|   }
 | |
| 
 | |
|   getTab(id, default_ = undefined) {
 | |
|     let win = windowTracker.topWindow;
 | |
|     if (win) {
 | |
|       let nativeTab = win.BrowserApp.getTabForId(id);
 | |
|       if (nativeTab) {
 | |
|         return nativeTab;
 | |
|       }
 | |
|     }
 | |
|     if (default_ !== undefined) {
 | |
|       return default_;
 | |
|     }
 | |
|     throw new ExtensionError(`Invalid tab ID: ${id}`);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Handles tab open and close events, and emits the appropriate internal
 | |
|    * events for them.
 | |
|    *
 | |
|    * @param {Event} event
 | |
|    *        A DOM event to handle.
 | |
|    * @private
 | |
|    */
 | |
|   handleEvent(event) {
 | |
|     const {BrowserApp} = event.target.ownerGlobal;
 | |
|     const nativeTab = BrowserApp.getTabForBrowser(event.target);
 | |
| 
 | |
|     switch (event.type) {
 | |
|       case "TabOpen":
 | |
|         this.emitCreated(nativeTab);
 | |
|         break;
 | |
| 
 | |
|       case "TabClose":
 | |
|         this.emitRemoved(nativeTab, false);
 | |
|         break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Required by the GlobalEventDispatcher module. This event will get
 | |
|    * called whenever one of the registered listeners fires.
 | |
|    * @param {string} event The event which fired.
 | |
|    * @param {object} data Information about the event which fired.
 | |
|    */
 | |
|   onEvent(event, data) {
 | |
|     const {BrowserApp} = windowTracker.topWindow;
 | |
| 
 | |
|     switch (event) {
 | |
|       case "Tab:Selected": {
 | |
|         this._selectedTabId = data.id;
 | |
| 
 | |
|         // If a new tab has been selected while an extension popup tab is still open,
 | |
|         // close it immediately.
 | |
|         const nativeTab = BrowserApp.getTabForId(data.id);
 | |
| 
 | |
|         const popupTab = tabTracker.extensionPopupTab;
 | |
|         if (popupTab && popupTab !== nativeTab) {
 | |
|           BrowserApp.closeTab(popupTab);
 | |
|         }
 | |
| 
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Emits a "tab-created" event for the given tab element.
 | |
|    *
 | |
|    * @param {NativeTab} nativeTab
 | |
|    *        The tab element which is being created.
 | |
|    * @private
 | |
|    */
 | |
|   emitCreated(nativeTab) {
 | |
|     this.emit("tab-created", {nativeTab});
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Emits a "tab-removed" event for the given tab element.
 | |
|    *
 | |
|    * @param {NativeTab} nativeTab
 | |
|    *        The tab element which is being removed.
 | |
|    * @param {boolean} isWindowClosing
 | |
|    *        True if the tab is being removed because the browser window is
 | |
|    *        closing.
 | |
|    * @private
 | |
|    */
 | |
|   emitRemoved(nativeTab, isWindowClosing) {
 | |
|     let windowId = windowTracker.getId(nativeTab.browser.ownerGlobal);
 | |
|     let tabId = this.getId(nativeTab);
 | |
| 
 | |
|     if (this.extensionPopupTab && this.extensionPopupTab === nativeTab) {
 | |
|       this._extensionPopupTabWeak = null;
 | |
| 
 | |
|       // Do not switch to the parent tab of the extension popup tab
 | |
|       // if the popup tab is not the selected tab.
 | |
|       if (this._selectedTabId !== tabId) {
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       // Select the parent tab when the closed tab was an extension popup tab.
 | |
|       const {BrowserApp} = windowTracker.topWindow;
 | |
|       const popupParentTab = BrowserApp.getTabForId(nativeTab.parentId);
 | |
|       if (popupParentTab) {
 | |
|         BrowserApp.selectTab(popupParentTab);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     Services.tm.dispatchToMainThread(() => {
 | |
|       this.emit("tab-removed", {nativeTab, tabId, windowId, isWindowClosing});
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   getBrowserData(browser) {
 | |
|     let result = {
 | |
|       tabId: -1,
 | |
|       windowId: -1,
 | |
|     };
 | |
| 
 | |
|     let {BrowserApp} = browser.ownerGlobal;
 | |
|     if (BrowserApp) {
 | |
|       result.windowId = windowTracker.getId(browser.ownerGlobal);
 | |
| 
 | |
|       let nativeTab = BrowserApp.getTabForBrowser(browser);
 | |
|       if (nativeTab) {
 | |
|         result.tabId = this.getId(nativeTab);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   get activeTab() {
 | |
|     let win = windowTracker.topWindow;
 | |
|     if (win && win.BrowserApp) {
 | |
|       const selectedTab = win.BrowserApp.selectedTab;
 | |
| 
 | |
|       // If the current tab is an extension popup tab, we use the parentId to retrieve
 | |
|       // and return the tab that was selected when the popup tab has been opened.
 | |
|       if (selectedTab === this.extensionPopupTab) {
 | |
|         return win.BrowserApp.getTabForId(selectedTab.parentId);
 | |
|       }
 | |
| 
 | |
|       return selectedTab;
 | |
|     }
 | |
| 
 | |
|     return null;
 | |
|   }
 | |
| }
 | |
| 
 | |
| windowTracker = new WindowTracker();
 | |
| tabTracker = new TabTracker();
 | |
| 
 | |
| Object.assign(global, {tabTracker, windowTracker});
 | |
| 
 | |
| class Tab extends TabBase {
 | |
|   get _favIconUrl() {
 | |
|     return undefined;
 | |
|   }
 | |
| 
 | |
|   get audible() {
 | |
|     return this.nativeTab.playingAudio;
 | |
|   }
 | |
| 
 | |
|   get browser() {
 | |
|     return this.nativeTab.browser;
 | |
|   }
 | |
| 
 | |
|   get discarded() {
 | |
|     return this.browser.getAttribute("pending") === "true";
 | |
|   }
 | |
| 
 | |
|   get cookieStoreId() {
 | |
|     return getCookieStoreIdForTab(this, this.nativeTab);
 | |
|   }
 | |
| 
 | |
|   get height() {
 | |
|     return this.browser.clientHeight;
 | |
|   }
 | |
| 
 | |
|   get incognito() {
 | |
|     return PrivateBrowsingUtils.isBrowserPrivate(this.browser);
 | |
|   }
 | |
| 
 | |
|   get index() {
 | |
|     return this.window.BrowserApp.tabs.indexOf(this.nativeTab);
 | |
|   }
 | |
| 
 | |
|   get mutedInfo() {
 | |
|     return {muted: false};
 | |
|   }
 | |
| 
 | |
|   get lastAccessed() {
 | |
|     return this.nativeTab.lastTouchedAt;
 | |
|   }
 | |
| 
 | |
|   get pinned() {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   get active() {
 | |
|     // If there is an extension popup tab and it is active,
 | |
|     // then the parent tab of the extension popup tab is active
 | |
|     // (while the extension popup tab will not be included in the
 | |
|     // tabs.query results).
 | |
|     if (tabTracker.extensionPopupTab) {
 | |
|       if (tabTracker.extensionPopupTab.getActive() &&
 | |
|           this.nativeTab.id === tabTracker.extensionPopupTab.parentId) {
 | |
|         return true;
 | |
|       }
 | |
| 
 | |
|       // Never return true for an active extension popup, e.g. so that
 | |
|       // the popup tab will not be part of the results of querying
 | |
|       // all the active tabs.
 | |
|       if (tabTracker.extensionPopupTab === this.nativeTab) {
 | |
|         return false;
 | |
|       }
 | |
|     }
 | |
|     return this.nativeTab.getActive();
 | |
|   }
 | |
| 
 | |
|   get highlighted() {
 | |
|     return this.active;
 | |
|   }
 | |
| 
 | |
|   get selected() {
 | |
|     return this.nativeTab.getActive();
 | |
|   }
 | |
| 
 | |
|   get status() {
 | |
|     if (this.browser.webProgress.isLoadingDocument) {
 | |
|       return "loading";
 | |
|     }
 | |
|     return "complete";
 | |
|   }
 | |
| 
 | |
|   get width() {
 | |
|     return this.browser.clientWidth;
 | |
|   }
 | |
| 
 | |
|   get window() {
 | |
|     return this.browser.ownerGlobal;
 | |
|   }
 | |
| 
 | |
|   get windowId() {
 | |
|     return windowTracker.getId(this.window);
 | |
|   }
 | |
| 
 | |
|   // TODO: Just return false for these until properly implemented on Android.
 | |
|   // https://bugzilla.mozilla.org/show_bug.cgi?id=1402924
 | |
|   get isArticle() {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   get isInReaderMode() {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   get hidden() {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   get sharingState() {
 | |
|     return {
 | |
|       screen: undefined,
 | |
|       microphone: false,
 | |
|       camera: false,
 | |
|     };
 | |
|   }
 | |
| }
 | |
| 
 | |
| // Manages tab-specific context data and dispatches tab select and close events.
 | |
| class TabContext extends EventEmitter {
 | |
|   constructor(getDefaultPrototype) {
 | |
|     super();
 | |
| 
 | |
|     this.getDefaultPrototype = getDefaultPrototype;
 | |
|     this.tabData = new Map();
 | |
| 
 | |
|     GlobalEventDispatcher.registerListener(this, [
 | |
|       "Tab:Selected",
 | |
|       "Tab:Closed",
 | |
|     ]);
 | |
|   }
 | |
| 
 | |
|   get(tabId) {
 | |
|     if (!this.tabData.has(tabId)) {
 | |
|       let data = Object.create(this.getDefaultPrototype(tabId));
 | |
|       this.tabData.set(tabId, data);
 | |
|     }
 | |
| 
 | |
|     return this.tabData.get(tabId);
 | |
|   }
 | |
| 
 | |
|   clear(tabId) {
 | |
|     this.tabData.delete(tabId);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Required by the GlobalEventDispatcher module. This event will get
 | |
|    * called whenever one of the registered listeners fires.
 | |
|    * @param {string} event The event which fired.
 | |
|    * @param {object} data Information about the event which fired.
 | |
|    */
 | |
|   onEvent(event, data) {
 | |
|     switch (event) {
 | |
|       case "Tab:Selected":
 | |
|         this.emit("tab-selected", data.id);
 | |
|         break;
 | |
|       case "Tab:Closed":
 | |
|         this.emit("tab-closed", data.tabId);
 | |
|         break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   shutdown() {
 | |
|     GlobalEventDispatcher.unregisterListener(this, [
 | |
|       "Tab:Selected",
 | |
|       "Tab:Closed",
 | |
|     ]);
 | |
|   }
 | |
| }
 | |
| 
 | |
| class Window extends WindowBase {
 | |
|   get focused() {
 | |
|     return this.window.document.hasFocus();
 | |
|   }
 | |
| 
 | |
|   get top() {
 | |
|     return this.window.screenY;
 | |
|   }
 | |
| 
 | |
|   get left() {
 | |
|     return this.window.screenX;
 | |
|   }
 | |
| 
 | |
|   get width() {
 | |
|     return this.window.outerWidth;
 | |
|   }
 | |
| 
 | |
|   get height() {
 | |
|     return this.window.outerHeight;
 | |
|   }
 | |
| 
 | |
|   get incognito() {
 | |
|     return PrivateBrowsingUtils.isWindowPrivate(this.window);
 | |
|   }
 | |
| 
 | |
|   get alwaysOnTop() {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   get isLastFocused() {
 | |
|     return this.window === windowTracker.topWindow;
 | |
|   }
 | |
| 
 | |
|   get state() {
 | |
|     return "fullscreen";
 | |
|   }
 | |
| 
 | |
|   * getTabs() {
 | |
|     let {tabManager} = this.extension;
 | |
| 
 | |
|     for (let nativeTab of this.window.BrowserApp.tabs) {
 | |
|       yield tabManager.getWrapper(nativeTab);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   * getHighlightedTabs() {
 | |
|     yield this.activeTab;
 | |
|   }
 | |
| 
 | |
|   get activeTab() {
 | |
|     let {BrowserApp} = this.window;
 | |
|     let {selectedTab} = BrowserApp;
 | |
| 
 | |
|     // If the current tab is an extension popup tab, we use the parentId to retrieve
 | |
|     // and return the tab that was selected when the popup tab has been opened.
 | |
|     if (selectedTab === tabTracker.extensionPopupTab) {
 | |
|       selectedTab = BrowserApp.getTabForId(selectedTab.parentId);
 | |
|     }
 | |
| 
 | |
|     let {tabManager} = this.extension;
 | |
|     return tabManager.getWrapper(selectedTab);
 | |
|   }
 | |
| 
 | |
|   getTabAtIndex(index) {
 | |
|     let nativeTab = this.window.BrowserApp.tabs[index];
 | |
|     if (nativeTab) {
 | |
|       return this.extension.tabManager.getWrapper(nativeTab);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| Object.assign(global, {Tab, TabContext, Window});
 | |
| 
 | |
| class TabManager extends TabManagerBase {
 | |
|   get(tabId, default_ = undefined) {
 | |
|     let nativeTab = tabTracker.getTab(tabId, default_);
 | |
| 
 | |
|     if (nativeTab) {
 | |
|       return this.getWrapper(nativeTab);
 | |
|     }
 | |
|     return default_;
 | |
|   }
 | |
| 
 | |
|   addActiveTabPermission(nativeTab = tabTracker.activeTab) {
 | |
|     return super.addActiveTabPermission(nativeTab);
 | |
|   }
 | |
| 
 | |
|   revokeActiveTabPermission(nativeTab = tabTracker.activeTab) {
 | |
|     return super.revokeActiveTabPermission(nativeTab);
 | |
|   }
 | |
| 
 | |
|   wrapTab(nativeTab) {
 | |
|     return new Tab(this.extension, nativeTab, nativeTab.id);
 | |
|   }
 | |
| }
 | |
| 
 | |
| class WindowManager extends WindowManagerBase {
 | |
|   get(windowId, context) {
 | |
|     let window = windowTracker.getWindow(windowId, context);
 | |
| 
 | |
|     return this.getWrapper(window);
 | |
|   }
 | |
| 
 | |
|   * getAll() {
 | |
|     for (let window of windowTracker.browserWindows()) {
 | |
|       yield this.getWrapper(window);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   wrapWindow(window) {
 | |
|     return new Window(this.extension, window, windowTracker.getId(window));
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| extensions.on("startup", (type, extension) => { // eslint-disable-line mozilla/balanced-listeners
 | |
|   defineLazyGetter(extension, "tabManager",
 | |
|                    () => new TabManager(extension));
 | |
|   defineLazyGetter(extension, "windowManager",
 | |
|                    () => new WindowManager(extension));
 | |
| });
 |