зеркало из https://github.com/mozilla/gecko-dev.git
Bug 514490 - Per Tab Network Prioritization. r=dao, r=bz
--HG-- extra : rebase_source : e80e9b87f9280251d873dafc69cf55ff4517aa1b
This commit is contained in:
Родитель
e4a407c78d
Коммит
e21ff412f5
|
@ -55,6 +55,7 @@ endif
|
|||
|
||||
EXTRA_JS_MODULES = \
|
||||
content/openLocationLastURL.jsm \
|
||||
content/NetworkPrioritizer.jsm \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
|
|
@ -0,0 +1,217 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Paul O’Shannessy <paul@oshannessy.com> (original author)
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/*
|
||||
* This module adjusts network priority for tabs in a way that gives 'important'
|
||||
* tabs a higher priority. There are 3 levels of priority. Each is listed below
|
||||
* with the priority adjustment used.
|
||||
*
|
||||
* Highest (-10): Selected tab in the focused window.
|
||||
* Medium (0): Background tabs in the focused window.
|
||||
* Selected tab in background windows.
|
||||
* Lowest (+10): Background tabs in background windows.
|
||||
*/
|
||||
|
||||
let EXPORTED_SYMBOLS = ["trackBrowserWindow"];
|
||||
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
|
||||
// Lazy getters
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "_focusManager",
|
||||
"@mozilla.org/focus-manager;1",
|
||||
"nsIFocusManager");
|
||||
|
||||
|
||||
// Constants
|
||||
const TAB_EVENTS = ["TabOpen", "TabSelect"];
|
||||
const WINDOW_EVENTS = ["activate", "unload"];
|
||||
// PRIORITY DELTA is -10 because lower priority value is actually a higher priority
|
||||
const PRIORITY_DELTA = -10;
|
||||
|
||||
|
||||
// Variables
|
||||
let _lastFocusedWindow = null;
|
||||
let _windows = [];
|
||||
|
||||
|
||||
// Exported symbol
|
||||
function trackBrowserWindow(aWindow) {
|
||||
WindowHelper.addWindow(aWindow);
|
||||
}
|
||||
|
||||
|
||||
// Global methods
|
||||
function _handleEvent(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case "TabOpen":
|
||||
BrowserHelper.onOpen(aEvent.target.linkedBrowser);
|
||||
break;
|
||||
case "TabSelect":
|
||||
BrowserHelper.onSelect(aEvent.target.linkedBrowser);
|
||||
break;
|
||||
case "activate":
|
||||
WindowHelper.onActivate(aEvent.target);
|
||||
break;
|
||||
case "unload":
|
||||
WindowHelper.removeWindow(aEvent.currentTarget);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Methods that impact a browser. Put into single object for organization.
|
||||
let BrowserHelper = {
|
||||
onOpen: function(aBrowser) {
|
||||
// If the tab is in the focused window, leave priority as it is
|
||||
if (aBrowser.ownerDocument.defaultView != _lastFocusedWindow)
|
||||
this.decreasePriority(aBrowser);
|
||||
},
|
||||
|
||||
onSelect: function(aBrowser) {
|
||||
let windowEntry = WindowHelper.getEntry(aBrowser.ownerDocument.defaultView);
|
||||
if (windowEntry.lastSelectedBrowser)
|
||||
this.decreasePriority(windowEntry.lastSelectedBrowser);
|
||||
this.increasePriority(aBrowser);
|
||||
|
||||
windowEntry.lastSelectedBrowser = aBrowser;
|
||||
},
|
||||
|
||||
// Auxiliary methods
|
||||
getLoadgroup: function(aBrowser) {
|
||||
return aBrowser.webNavigation.QueryInterface(Ci.nsIDocumentLoader)
|
||||
.loadGroup.QueryInterface(Ci.nsISupportsPriority);
|
||||
},
|
||||
|
||||
increasePriority: function(aBrowser) {
|
||||
this.getLoadgroup(aBrowser).adjustPriority(PRIORITY_DELTA);
|
||||
},
|
||||
|
||||
decreasePriority: function(aBrowser) {
|
||||
this.getLoadgroup(aBrowser).adjustPriority(PRIORITY_DELTA * -1);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Methods that impact a window. Put into single object for organization.
|
||||
let WindowHelper = {
|
||||
addWindow: function(aWindow) {
|
||||
// Build internal data object
|
||||
_windows.push({ window: aWindow, lastSelectedBrowser: null });
|
||||
|
||||
// Add event listeners
|
||||
TAB_EVENTS.forEach(function(event) {
|
||||
aWindow.gBrowser.tabContainer.addEventListener(event, _handleEvent, false);
|
||||
});
|
||||
WINDOW_EVENTS.forEach(function(event) {
|
||||
aWindow.addEventListener(event, _handleEvent, false);
|
||||
});
|
||||
|
||||
// This gets called AFTER activate event, so if this is the focused window
|
||||
// we want to activate it. Otherwise, deprioritize it.
|
||||
if (aWindow == _focusManager.activeWindow)
|
||||
this.handleFocusedWindow(aWindow);
|
||||
else
|
||||
this.decreasePriority(aWindow);
|
||||
|
||||
// Select the selected tab
|
||||
BrowserHelper.onSelect(aWindow.gBrowser.selectedBrowser);
|
||||
},
|
||||
|
||||
removeWindow: function(aWindow) {
|
||||
if (aWindow == _lastFocusedWindow)
|
||||
_lastFocusedWindow = null;
|
||||
|
||||
// Delete this window from our tracking
|
||||
_windows.splice(this.getEntryIndex(aWindow), 1);
|
||||
|
||||
// Remove the event listeners
|
||||
TAB_EVENTS.forEach(function(event) {
|
||||
aWindow.gBrowser.tabContainer.removeEventListener(event, _handleEvent, false);
|
||||
});
|
||||
WINDOW_EVENTS.forEach(function(event) {
|
||||
aWindow.removeEventListener(event, _handleEvent, false);
|
||||
});
|
||||
},
|
||||
|
||||
onActivate: function(aWindow, aHasFocus) {
|
||||
// If this window was the last focused window, we don't need to do anything
|
||||
if (aWindow == _lastFocusedWindow)
|
||||
return;
|
||||
|
||||
// handleFocusedWindow will deprioritize the current window
|
||||
this.handleFocusedWindow(aWindow);
|
||||
|
||||
// Lastly we should increase priority for this window
|
||||
this.increasePriority(aWindow);
|
||||
},
|
||||
|
||||
handleFocusedWindow: function(aWindow) {
|
||||
// If we have a last focused window, we need to deprioritize it first
|
||||
if (_lastFocusedWindow)
|
||||
this.decreasePriority(_lastFocusedWindow);
|
||||
|
||||
// aWindow is now focused
|
||||
_lastFocusedWindow = aWindow;
|
||||
},
|
||||
|
||||
// Auxiliary methods
|
||||
increasePriority: function(aWindow) {
|
||||
aWindow.gBrowser.browsers.forEach(function(aBrowser) {
|
||||
BrowserHelper.increasePriority(aBrowser);
|
||||
});
|
||||
},
|
||||
|
||||
decreasePriority: function(aWindow) {
|
||||
aWindow.gBrowser.browsers.forEach(function(aBrowser) {
|
||||
BrowserHelper.decreasePriority(aBrowser);
|
||||
});
|
||||
},
|
||||
|
||||
getEntry: function(aWindow) {
|
||||
return _windows[this.getEntryIndex(aWindow)];
|
||||
},
|
||||
|
||||
getEntryIndex: function(aWindow) {
|
||||
// Assumes that every object has a unique window & it's in the array
|
||||
for (let i = 0; i < _windows.length; i++)
|
||||
if (_windows[i].window == aWindow)
|
||||
return i;
|
||||
}
|
||||
};
|
||||
|
|
@ -1287,6 +1287,10 @@ function delayedStartup(isLoadingBlank, mustLoadSidebar) {
|
|||
Components.utils.reportError("Failed to init content pref service:\n" + ex);
|
||||
}
|
||||
|
||||
let NP = {};
|
||||
Cu.import("resource://gre/modules/NetworkPrioritizer.jsm", NP);
|
||||
NP.trackBrowserWindow(window);
|
||||
|
||||
// initialize the session-restore service (in case it's not already running)
|
||||
if (document.documentElement.getAttribute("windowtype") == "navigator:browser") {
|
||||
try {
|
||||
|
|
|
@ -140,6 +140,7 @@ _BROWSER_FILES = \
|
|||
browser_drag.js \
|
||||
browser_relatedTabs.js \
|
||||
browser_contextSearchTabPosition.js \
|
||||
browser_NetworkPrioritizer.js \
|
||||
$(NULL)
|
||||
|
||||
ifneq (cocoa,$(MOZ_WIDGET_TOOLKIT))
|
||||
|
|
|
@ -0,0 +1,187 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Paul O’Shannessy <paul@oshannessy.com> (original author)
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
function test() {
|
||||
/** Tests for NetworkPrioritizer.jsm (Bug 514490) **/
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
const PRIORITY_DELTA = -10; // same as in NetworkPrioritizer
|
||||
|
||||
// Test helper functions.
|
||||
// getPriority and setPriority can take a Tab or a Browser
|
||||
function getPriority(aBrowser) {
|
||||
// Assume we were passed a tab if it's not a browser
|
||||
if (!aBrowser.webNavigation)
|
||||
aBrowser = aBrowser.linkedBrowser;
|
||||
return aBrowser.webNavigation.QueryInterface(Ci.nsIDocumentLoader)
|
||||
.loadGroup.QueryInterface(Ci.nsISupportsPriority).priority;
|
||||
}
|
||||
function setPriority(aBrowser, aPriority) {
|
||||
if (!aBrowser.webNavigation)
|
||||
aBrowser = aBrowser.linkedBrowser;
|
||||
aBrowser.webNavigation.QueryInterface(Ci.nsIDocumentLoader)
|
||||
.loadGroup.QueryInterface(Ci.nsISupportsPriority).priority = aPriority;
|
||||
}
|
||||
|
||||
function isWindowState(aWindow, aTabPriorities) {
|
||||
let browsers = aWindow.gBrowser.browsers;
|
||||
// Make sure we have the right number of tabs & priorities
|
||||
is(browsers.length, aTabPriorities.length,
|
||||
"Window has expected number of tabs");
|
||||
// aState should be in format [ priority, priority, priority ]
|
||||
for (let i = 0; i < browsers.length; i++) {
|
||||
is(getPriority(browsers[i]), aTabPriorities[i],
|
||||
"Tab had expected priority");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// This is the real test. It creates multiple tabs & windows, changes focus,
|
||||
// closes windows/tabs to make sure we behave correctly.
|
||||
// This test assumes that no priorities have been adjusted and the loadgroup
|
||||
// priority starts at 0.
|
||||
function test_behavior() {
|
||||
|
||||
// Call window "window_A" to make the test easier to follow
|
||||
let window_A = window;
|
||||
|
||||
// Test 1 window, 1 tab case.
|
||||
isWindowState(window_A, [-10]);
|
||||
|
||||
// Exising tab is tab_A1
|
||||
let tab_A2 = window_A.gBrowser.addTab("http://example.com");
|
||||
let tab_A3 = window_A.gBrowser.addTab("about:config");
|
||||
tab_A3.linkedBrowser.addEventListener("load", function(aEvent) {
|
||||
tab_A3.removeEventListener("load", arguments.callee, true);
|
||||
|
||||
// tab_A2 isn't focused yet
|
||||
isWindowState(window_A, [-10, 0, 0]);
|
||||
|
||||
// focus tab_A2 & make sure priority got updated
|
||||
window_A.gBrowser.selectedTab = tab_A2;
|
||||
isWindowState(window_A, [0, -10, 0]);
|
||||
|
||||
window_A.gBrowser.removeTab(tab_A2);
|
||||
// Next tab is auto selected
|
||||
isWindowState(window_A, [0, -10]);
|
||||
|
||||
// Open another window then play with focus
|
||||
let window_B = openDialog(location, "_blank", "chrome,all,dialog=no", "http://example.com");
|
||||
window_B.addEventListener("load", function(aEvent) {
|
||||
window_B.removeEventListener("load", arguments.callee, false);
|
||||
window_B.gBrowser.addEventListener("load", function(aEvent) {
|
||||
window_B.gBrowser.removeEventListener("load", arguments.callee, true);
|
||||
|
||||
// On Linux, waitForFocus doesn't work if the window is already focused,
|
||||
// so focus window_A first.
|
||||
waitForFocus(function() {
|
||||
waitForFocus(function() {
|
||||
isWindowState(window_A, [10, 0]);
|
||||
isWindowState(window_B, [-10]);
|
||||
|
||||
waitForFocus(function() {
|
||||
isWindowState(window_A, [0, -10]);
|
||||
isWindowState(window_B, [0]);
|
||||
|
||||
waitForFocus(function() {
|
||||
isWindowState(window_A, [10, 0]);
|
||||
isWindowState(window_B, [-10]);
|
||||
|
||||
// And we're done. Cleanup & run the next test
|
||||
window_B.close();
|
||||
window_A.gBrowser.removeTab(tab_A3);
|
||||
executeSoon(runNextTest);
|
||||
}, window_B);
|
||||
}, window_A);
|
||||
}, window_B);
|
||||
}, window_A);
|
||||
|
||||
}, true);
|
||||
}, false);
|
||||
}, true);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// This is more a test of nsLoadGroup and how it handles priorities. But since
|
||||
// we depend on its behavior, it's good to test it. This is testing that there
|
||||
// are no errors if we adjust beyond nsISupportsPriority's bounds.
|
||||
function test_extremePriorities() {
|
||||
let tab_A1 = gBrowser.tabContainer.getItemAtIndex(0);
|
||||
let oldPriority = getPriority(tab_A1);
|
||||
|
||||
// Set the priority of tab_A1 to the lowest possible. Selecting the other tab
|
||||
// will try to lower it
|
||||
setPriority(tab_A1, Ci.nsISupportsPriority.PRIORITY_LOWEST);
|
||||
|
||||
let tab_A2 = gBrowser.addTab("http://example.com");
|
||||
tab_A2.linkedBrowser.addEventListener("load", function(aEvent) {
|
||||
tab_A2.removeEventListener("load", arguments.callee, true);
|
||||
gBrowser.selectedTab = tab_A2;
|
||||
is(getPriority(tab_A1), Ci.nsISupportsPriority.PRIORITY_LOWEST - PRIORITY_DELTA,
|
||||
"Can adjust priority beyond 'lowest'");
|
||||
|
||||
// Now set priority to "highest" and make sure that no errors occur.
|
||||
setPriority(tab_A1, Ci.nsISupportsPriority.PRIORITY_HIGHEST);
|
||||
gBrowser.selectedTab = tab_A1;
|
||||
|
||||
is(getPriority(tab_A1), Ci.nsISupportsPriority.PRIORITY_HIGHEST + PRIORITY_DELTA,
|
||||
"Can adjust priority beyond 'highest'");
|
||||
|
||||
// Cleanup, run next test
|
||||
gBrowser.removeTab(tab_A2);
|
||||
executeSoon(function() {
|
||||
setPriority(tab_A1, oldPriority);
|
||||
runNextTest();
|
||||
});
|
||||
|
||||
}, true);
|
||||
}
|
||||
|
||||
|
||||
let tests = [test_behavior, test_extremePriorities];
|
||||
function runNextTest() {
|
||||
if (tests.length) {
|
||||
// Linux has problems if window isn't focused. Should help prevent [orange].
|
||||
waitForFocus(tests.shift());
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
runNextTest();
|
||||
}
|
Загрузка…
Ссылка в новой задаче