michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * This module adjusts network priority for tabs in a way that gives 'important' michael@0: * tabs a higher priority. There are 3 levels of priority. Each is listed below michael@0: * with the priority adjustment used. michael@0: * michael@0: * Highest (-10): Selected tab in the focused window. michael@0: * Medium (0): Background tabs in the focused window. michael@0: * Selected tab in background windows. michael@0: * Lowest (+10): Background tabs in background windows. michael@0: */ michael@0: michael@0: this.EXPORTED_SYMBOLS = ["trackBrowserWindow"]; michael@0: michael@0: const Ci = Components.interfaces; michael@0: michael@0: Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); michael@0: michael@0: michael@0: // Lazy getters michael@0: XPCOMUtils.defineLazyServiceGetter(this, "_focusManager", michael@0: "@mozilla.org/focus-manager;1", michael@0: "nsIFocusManager"); michael@0: michael@0: michael@0: // Constants michael@0: const TAB_EVENTS = ["TabOpen", "TabSelect"]; michael@0: const WINDOW_EVENTS = ["activate", "unload"]; michael@0: // PRIORITY DELTA is -10 because lower priority value is actually a higher priority michael@0: const PRIORITY_DELTA = -10; michael@0: michael@0: michael@0: // Variables michael@0: let _lastFocusedWindow = null; michael@0: let _windows = []; michael@0: michael@0: michael@0: // Exported symbol michael@0: this.trackBrowserWindow = function trackBrowserWindow(aWindow) { michael@0: WindowHelper.addWindow(aWindow); michael@0: } michael@0: michael@0: michael@0: // Global methods michael@0: function _handleEvent(aEvent) { michael@0: switch (aEvent.type) { michael@0: case "TabOpen": michael@0: BrowserHelper.onOpen(aEvent.target.linkedBrowser); michael@0: break; michael@0: case "TabSelect": michael@0: BrowserHelper.onSelect(aEvent.target.linkedBrowser); michael@0: break; michael@0: case "activate": michael@0: WindowHelper.onActivate(aEvent.target); michael@0: break; michael@0: case "unload": michael@0: WindowHelper.removeWindow(aEvent.currentTarget); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: michael@0: // Methods that impact a browser. Put into single object for organization. michael@0: let BrowserHelper = { michael@0: onOpen: function NP_BH_onOpen(aBrowser) { michael@0: // If the tab is in the focused window, leave priority as it is michael@0: if (aBrowser.ownerDocument.defaultView != _lastFocusedWindow) michael@0: this.decreasePriority(aBrowser); michael@0: }, michael@0: michael@0: onSelect: function NP_BH_onSelect(aBrowser) { michael@0: let windowEntry = WindowHelper.getEntry(aBrowser.ownerDocument.defaultView); michael@0: if (windowEntry.lastSelectedBrowser) michael@0: this.decreasePriority(windowEntry.lastSelectedBrowser); michael@0: this.increasePriority(aBrowser); michael@0: michael@0: windowEntry.lastSelectedBrowser = aBrowser; michael@0: }, michael@0: michael@0: increasePriority: function NP_BH_increasePriority(aBrowser) { michael@0: aBrowser.adjustPriority(PRIORITY_DELTA); michael@0: }, michael@0: michael@0: decreasePriority: function NP_BH_decreasePriority(aBrowser) { michael@0: aBrowser.adjustPriority(PRIORITY_DELTA * -1); michael@0: } michael@0: }; michael@0: michael@0: michael@0: // Methods that impact a window. Put into single object for organization. michael@0: let WindowHelper = { michael@0: addWindow: function NP_WH_addWindow(aWindow) { michael@0: // Build internal data object michael@0: _windows.push({ window: aWindow, lastSelectedBrowser: null }); michael@0: michael@0: // Add event listeners michael@0: TAB_EVENTS.forEach(function(event) { michael@0: aWindow.gBrowser.tabContainer.addEventListener(event, _handleEvent, false); michael@0: }); michael@0: WINDOW_EVENTS.forEach(function(event) { michael@0: aWindow.addEventListener(event, _handleEvent, false); michael@0: }); michael@0: michael@0: // This gets called AFTER activate event, so if this is the focused window michael@0: // we want to activate it. Otherwise, deprioritize it. michael@0: if (aWindow == _focusManager.activeWindow) michael@0: this.handleFocusedWindow(aWindow); michael@0: else michael@0: this.decreasePriority(aWindow); michael@0: michael@0: // Select the selected tab michael@0: BrowserHelper.onSelect(aWindow.gBrowser.selectedBrowser); michael@0: }, michael@0: michael@0: removeWindow: function NP_WH_removeWindow(aWindow) { michael@0: if (aWindow == _lastFocusedWindow) michael@0: _lastFocusedWindow = null; michael@0: michael@0: // Delete this window from our tracking michael@0: _windows.splice(this.getEntryIndex(aWindow), 1); michael@0: michael@0: // Remove the event listeners michael@0: TAB_EVENTS.forEach(function(event) { michael@0: aWindow.gBrowser.tabContainer.removeEventListener(event, _handleEvent, false); michael@0: }); michael@0: WINDOW_EVENTS.forEach(function(event) { michael@0: aWindow.removeEventListener(event, _handleEvent, false); michael@0: }); michael@0: }, michael@0: michael@0: onActivate: function NP_WH_onActivate(aWindow, aHasFocus) { michael@0: // If this window was the last focused window, we don't need to do anything michael@0: if (aWindow == _lastFocusedWindow) michael@0: return; michael@0: michael@0: // handleFocusedWindow will deprioritize the current window michael@0: this.handleFocusedWindow(aWindow); michael@0: michael@0: // Lastly we should increase priority for this window michael@0: this.increasePriority(aWindow); michael@0: }, michael@0: michael@0: handleFocusedWindow: function NP_WH_handleFocusedWindow(aWindow) { michael@0: // If we have a last focused window, we need to deprioritize it first michael@0: if (_lastFocusedWindow) michael@0: this.decreasePriority(_lastFocusedWindow); michael@0: michael@0: // aWindow is now focused michael@0: _lastFocusedWindow = aWindow; michael@0: }, michael@0: michael@0: // Auxiliary methods michael@0: increasePriority: function NP_WH_increasePriority(aWindow) { michael@0: aWindow.gBrowser.browsers.forEach(function(aBrowser) { michael@0: BrowserHelper.increasePriority(aBrowser); michael@0: }); michael@0: }, michael@0: michael@0: decreasePriority: function NP_WH_decreasePriority(aWindow) { michael@0: aWindow.gBrowser.browsers.forEach(function(aBrowser) { michael@0: BrowserHelper.decreasePriority(aBrowser); michael@0: }); michael@0: }, michael@0: michael@0: getEntry: function NP_WH_getEntry(aWindow) { michael@0: return _windows[this.getEntryIndex(aWindow)]; michael@0: }, michael@0: michael@0: getEntryIndex: function NP_WH_getEntryAtIndex(aWindow) { michael@0: // Assumes that every object has a unique window & it's in the array michael@0: for (let i = 0; i < _windows.length; i++) michael@0: if (_windows[i].window == aWindow) michael@0: return i; michael@0: } michael@0: }; michael@0: