/* 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/. */ /* * This module tracks each browser window and informs network module * the current selected tab's content outer window ID. */ var EXPORTED_SYMBOLS = ["BrowserWindowTracker"]; const { XPCOMUtils } = ChromeUtils.import( "resource://gre/modules/XPCOMUtils.jsm" ); const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); // Lazy getters XPCOMUtils.defineLazyModuleGetters(this, { PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm", }); // Constants const TAB_EVENTS = ["TabBrowserInserted", "TabSelect"]; const WINDOW_EVENTS = ["activate", "unload"]; const DEBUG = false; // Variables var _lastTopLevelWindowID = 0; var _trackedWindows = []; // Global methods function debug(s) { if (DEBUG) { dump("-*- UpdateTopLevelContentWindowIDHelper: " + s + "\n"); } } function _updateCurrentContentOuterWindowID(browser) { if ( !browser.outerWindowID || browser.outerWindowID === _lastTopLevelWindowID || browser.ownerGlobal != _trackedWindows[0] ) { return; } debug( "Current window uri=" + (browser.currentURI && browser.currentURI.spec) + " id=" + browser.outerWindowID ); _lastTopLevelWindowID = browser.outerWindowID; let windowIDWrapper = Cc["@mozilla.org/supports-PRUint64;1"].createInstance( Ci.nsISupportsPRUint64 ); windowIDWrapper.data = _lastTopLevelWindowID; Services.obs.notifyObservers( windowIDWrapper, "net:current-toplevel-outer-content-windowid" ); } function _handleEvent(event) { switch (event.type) { case "TabBrowserInserted": if ( event.target.ownerGlobal.gBrowser.selectedBrowser === event.target.linkedBrowser ) { _updateCurrentContentOuterWindowID(event.target.linkedBrowser); } break; case "TabSelect": _updateCurrentContentOuterWindowID(event.target.linkedBrowser); break; case "activate": WindowHelper.onActivate(event.target); break; case "unload": WindowHelper.removeWindow(event.currentTarget); break; } } function _trackWindowOrder(window) { if (window.windowState == window.STATE_MINIMIZED) { let firstMinimizedWindow = _trackedWindows.findIndex( w => w.windowState == w.STATE_MINIMIZED ); if (firstMinimizedWindow == -1) { firstMinimizedWindow = _trackedWindows.length; } _trackedWindows.splice(firstMinimizedWindow, 0, window); } else { _trackedWindows.unshift(window); } } function _untrackWindowOrder(window) { let idx = _trackedWindows.indexOf(window); if (idx >= 0) { _trackedWindows.splice(idx, 1); } } // Methods that impact a window. Put into single object for organization. var WindowHelper = { addWindow(window) { // Add event listeners TAB_EVENTS.forEach(function(event) { window.gBrowser.tabContainer.addEventListener(event, _handleEvent); }); WINDOW_EVENTS.forEach(function(event) { window.addEventListener(event, _handleEvent); }); _trackWindowOrder(window); // Update the selected tab's content outer window ID. _updateCurrentContentOuterWindowID(window.gBrowser.selectedBrowser); }, removeWindow(window) { _untrackWindowOrder(window); // Remove the event listeners TAB_EVENTS.forEach(function(event) { window.gBrowser.tabContainer.removeEventListener(event, _handleEvent); }); WINDOW_EVENTS.forEach(function(event) { window.removeEventListener(event, _handleEvent); }); }, onActivate(window) { // If this window was the last focused window, we don't need to do anything if (window == _trackedWindows[0]) { return; } _untrackWindowOrder(window); _trackWindowOrder(window); _updateCurrentContentOuterWindowID(window.gBrowser.selectedBrowser); }, }; this.BrowserWindowTracker = { /** * Get the most recent browser window. * * @param options an object accepting the arguments for the search. * * private: true to restrict the search to private windows * only, false to restrict the search to non-private only. * Omit the property to search in both groups. * * allowPopups: true if popup windows are permissable. */ getTopWindow(options = {}) { for (let win of _trackedWindows) { if ( !win.closed && (options.allowPopups || win.toolbar.visible) && (!("private" in options) || PrivateBrowsingUtils.permanentPrivateBrowsing || PrivateBrowsingUtils.isWindowPrivate(win) == options.private) ) { return win; } } return null; }, windowCreated(browser) { if (browser === browser.ownerGlobal.gBrowser.selectedBrowser) { _updateCurrentContentOuterWindowID(browser); } }, /** * Number of currently open browser windows. */ get windowCount() { return _trackedWindows.length; }, /** * Array of browser windows ordered by z-index, in reverse order. * This means that the top-most browser window will be the first item. */ get orderedWindows() { // Clone the windows array immediately as it may change during iteration, // we'd rather have an outdated order than skip/revisit windows. return [..._trackedWindows]; }, getAllVisibleTabs() { let tabs = []; for (let win of BrowserWindowTracker.orderedWindows) { for (let tab of win.gBrowser.visibleTabs) { // Only use tabs which are not discarded / unrestored if (tab.linkedPanel) { let { contentTitle, browserId } = tab.linkedBrowser; tabs.push({ contentTitle, browserId }); } } } return tabs; }, track(window) { return WindowHelper.addWindow(window); }, };