Merge autoland to central, a=merge

This commit is contained in:
Wes Kocher 2016-11-16 16:42:21 -08:00
commit aa3b64b5f5
119 changed files with 1306 additions and 1075 deletions

View File

@ -24,14 +24,6 @@
"unpack": true
},
{
"version": "cargo 0.13.0-nightly (eca9e15 2016-11-01) repack",
"size": 3027932,
"digest": "a5c99eeb12b3b9b49632c259c762e34ec13cf72dadf90a0608b8ab1dc66b36cb114c5b45f71d326e12d31d9e88a41b029e6a728ca64cef392c0a8d211c2fe191",
"algorithm": "sha512",
"filename": "cargo.tar.xz",
"unpack": true
},
{
"size": 167175,
"digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
"algorithm": "sha512",

View File

@ -529,7 +529,8 @@ toolbar:not(#TabsToolbar) > #personal-bookmarks {
/* Overlay a badge on top of the icon of additional open search providers
in the search panel. */
.addengine-item > .button-box > .button-icon {
.addengine-item > .button-box > .button-icon,
.addengine-item[type="menu"] > .button-box > .box-inherit > .button-icon {
-moz-binding: url("chrome://browser/content/search/search.xml#addengine-icon");
display: -moz-stack;
}

View File

@ -1,25 +1,15 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
add_task(function* () {
const PREF_TRIMURLS = "browser.urlbar.trimURLs";
function testVal(originalValue, targetValue) {
gURLBar.value = originalValue;
gURLBar.valueIsTyped = false;
is(gURLBar.textValue, targetValue || originalValue, "url bar value set");
}
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser);
function test() {
const prefname = "browser.urlbar.trimURLs";
gBrowser.selectedTab = gBrowser.addTab();
registerCleanupFunction(function () {
gBrowser.removeCurrentTab();
Services.prefs.clearUserPref(prefname);
registerCleanupFunction(function* () {
yield BrowserTestUtils.removeTab(tab);
Services.prefs.clearUserPref(PREF_TRIMURLS);
URLBarSetURI();
});
Services.prefs.setBoolPref(prefname, true);
Services.prefs.setBoolPref(PREF_TRIMURLS, true);
testVal("http://mozilla.org/", "mozilla.org");
testVal("https://mozilla.org/", "https://mozilla.org");
@ -71,35 +61,38 @@ function test() {
testVal("http://localhost/ foo bar baz");
testVal("http://localhost.localdomain/ foo bar baz", "localhost.localdomain/ foo bar baz");
Services.prefs.setBoolPref(prefname, false);
Services.prefs.setBoolPref(PREF_TRIMURLS, false);
testVal("http://mozilla.org/");
Services.prefs.setBoolPref(prefname, true);
waitForExplicitFinish();
gBrowser.selectedBrowser.addEventListener("load", function () {
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
is(gBrowser.currentURI.spec, "http://example.com/", "expected page should have loaded");
testCopy("example.com", "http://example.com/", function () {
SetPageProxyState("invalid");
gURLBar.valueIsTyped = true;
testCopy("example.com", "example.com", finish);
});
}, true);
Services.prefs.setBoolPref(PREF_TRIMURLS, true);
let promiseLoaded = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser,
false, "http://example.com/");
gBrowser.loadURI("http://example.com/");
yield promiseLoaded;
yield testCopy("example.com", "http://example.com/")
SetPageProxyState("invalid");
gURLBar.valueIsTyped = true;
yield testCopy("example.com", "example.com");
});
function testVal(originalValue, targetValue) {
gURLBar.value = originalValue;
gURLBar.valueIsTyped = false;
is(gURLBar.textValue, targetValue || originalValue, "url bar value set");
}
function testCopy(originalValue, targetValue, cb) {
waitForClipboard(targetValue, function () {
is(gURLBar.textValue, originalValue, "url bar copy value set");
function testCopy(originalValue, targetValue) {
return new Promise((resolve, reject) => {
waitForClipboard(targetValue, function () {
is(gURLBar.textValue, originalValue, "url bar copy value set");
gURLBar.focus();
gURLBar.select();
goDoCommand("cmd_copy");
}, cb, cb);
gURLBar.focus();
gURLBar.select();
goDoCommand("cmd_copy");
}, resolve, reject);
});
}

View File

@ -339,130 +339,6 @@ IE7FormPasswords.prototype = {
},
};
function Settings() {
}
Settings.prototype = {
type: MigrationUtils.resourceTypes.SETTINGS,
get exists() {
return true;
},
migrate: function S_migrate(aCallback) {
// Converts from yes/no to a boolean.
let yesNoToBoolean = v => v == "yes";
// Converts source format like "en-us,ar-kw;q=0.7,ar-om;q=0.3" into
// destination format like "en-us, ar-kw, ar-om".
// Final string is sorted by quality (q=) param.
function parseAcceptLanguageList(v) {
return v.match(/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/gi)
.sort(function(a, b) {
let qA = parseFloat(a.split(";q=")[1]) || 1.0;
let qB = parseFloat(b.split(";q=")[1]) || 1.0;
return qB - qA;
})
.map(a => a.split(";")[0]);
}
// For reference on some of the available IE Registry settings:
// * http://msdn.microsoft.com/en-us/library/cc980058%28v=prot.13%29.aspx
// * http://msdn.microsoft.com/en-us/library/cc980059%28v=prot.13%29.aspx
// Note that only settings exposed in our UI should be migrated.
this._set("Software\\Microsoft\\Internet Explorer\\International",
"AcceptLanguage",
"intl.accept_languages",
parseAcceptLanguageList);
// TODO (bug 745853): For now, only x-western font is translated.
this._set("Software\\Microsoft\\Internet Explorer\\International\\Scripts\\3",
"IEFixedFontName",
"font.name.monospace.x-western");
this._set(kMainKey,
"Use FormSuggest",
"browser.formfill.enable",
yesNoToBoolean);
this._set(kMainKey,
"FormSuggest Passwords",
"signon.rememberSignons",
yesNoToBoolean);
this._set(kMainKey,
"Anchor Underline",
"browser.underline_anchors",
yesNoToBoolean);
this._set(kMainKey,
"Display Inline Images",
"permissions.default.image",
v => yesNoToBoolean(v) ? 1 : 2);
this._set(kMainKey,
"Move System Caret",
"accessibility.browsewithcaret",
yesNoToBoolean);
this._set("Software\\Microsoft\\Internet Explorer\\Settings",
"Always Use My Colors",
"browser.display.document_color_use",
v => (!v ? 0 : 2));
this._set("Software\\Microsoft\\Internet Explorer\\Settings",
"Always Use My Font Face",
"browser.display.use_document_fonts",
v => !v);
this._set(kMainKey,
"SmoothScroll",
"general.smoothScroll",
Boolean);
this._set("Software\\Microsoft\\Internet Explorer\\TabbedBrowsing\\",
"WarnOnClose",
"browser.tabs.warnOnClose",
Boolean);
this._set("Software\\Microsoft\\Internet Explorer\\TabbedBrowsing\\",
"OpenInForeground",
"browser.tabs.loadInBackground",
v => !v);
aCallback(true);
},
/**
* Reads a setting from the Registry and stores the converted result into
* the appropriate Firefox preference.
*
* @param aPath
* Registry path under HKCU.
* @param aKey
* Name of the key.
* @param aPref
* Firefox preference.
* @param [optional] aTransformFn
* Conversion function from the Registry format to the pref format.
*/
_set: function S__set(aPath, aKey, aPref, aTransformFn) {
let value = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
aPath, aKey);
// Don't import settings that have never been flipped.
if (value === undefined)
return;
if (aTransformFn)
value = aTransformFn(value);
switch (typeof value) {
case "string":
Services.prefs.setCharPref(aPref, value);
break;
case "number":
Services.prefs.setIntPref(aPref, value);
break;
case "boolean":
Services.prefs.setBoolPref(aPref, value);
break;
default:
throw new Error("Unexpected value type: " + (typeof value));
}
}
};
function IEProfileMigrator()
{
this.wrappedJSObject = this; // export this to be able to use it in the unittest.
@ -475,7 +351,6 @@ IEProfileMigrator.prototype.getResources = function IE_getResources() {
MSMigrationUtils.getBookmarksMigrator(),
new History(),
MSMigrationUtils.getCookiesMigrator(),
new Settings(),
];
// Only support the form password migrator for Windows XP to 7.
if (AppConstants.isPlatformAndVersionAtMost("win", "6.1")) {

View File

@ -313,193 +313,6 @@ MainPreferencesPropertyList.prototype = {
}
};
function Preferences(aMainPreferencesPropertyListInstance) {
this._mainPreferencesPropertyList = aMainPreferencesPropertyListInstance;
}
Preferences.prototype = {
type: MigrationUtils.resourceTypes.SETTINGS,
migrate: function MPR_migrate(aCallback) {
this._mainPreferencesPropertyList.read(aDict => {
Task.spawn(function* () {
if (!aDict)
throw new Error("Could not read preferences file");
this._dict = aDict;
let invert = webkitVal => !webkitVal;
this._set("AutoFillPasswords", "signon.rememberSignons");
this._set("OpenNewTabsInFront", "browser.tabs.loadInBackground", invert);
this._set("WebKitJavaScriptCanOpenWindowsAutomatically",
"dom.disable_open_during_load", invert);
// layout.spellcheckDefault is a boolean stored as a number.
this._set("WebContinuousSpellCheckingEnabled",
"layout.spellcheckDefault", Number);
// Auto-load images
// Firefox has an elaborate set of Image preferences. The correlation is:
// Mode: Safari Firefox
// Blocked FALSE 2
// Allowed TRUE 1
// Allowed, originating site only -- 3
this._set("WebKitDisplayImagesKey", "permissions.default.image",
webkitVal => webkitVal ? 1 : 2);
this._migrateFontSettings();
yield this._migrateDownloadsFolder();
}.bind(this)).then(() => aCallback(true), ex => {
Cu.reportError(ex);
aCallback(false);
}).catch(Cu.reportError);
});
},
/**
* Attempts to migrates a preference from Safari. Returns whether the preference
* has been migrated.
* @param aSafariKey
* The dictionary key for the preference of Safari.
* @param aMozPref
* The gecko/firefox preference to which aSafariKey should be migrated
* @param [optional] aConvertFunction(aSafariValue)
* a function that converts the safari-preference value to the
* appropriate value for aMozPref. If it's not passed, then the
* Safari value is set as is.
* If aConvertFunction returns undefined, then aMozPref is not set
* at all.
* @return whether or not aMozPref was set.
*/
_set: function MPR_set(aSafariKey, aMozPref, aConvertFunction) {
if (this._dict.has(aSafariKey)) {
let safariVal = this._dict.get(aSafariKey);
let mozVal = aConvertFunction !== undefined ?
aConvertFunction(safariVal) : safariVal;
switch (typeof mozVal) {
case "string":
Services.prefs.setCharPref(aMozPref, mozVal);
break;
case "number":
Services.prefs.setIntPref(aMozPref, mozVal);
break;
case "boolean":
Services.prefs.setBoolPref(aMozPref, mozVal);
break;
case "undefined":
return false;
default:
throw new Error("Unexpected value type: " + (typeof mozVal));
}
}
return true;
},
// Fonts settings are quite problematic for migration, for a couple of
// reasons:
// (a) Every font preference in Gecko is set for a particular language.
// In Safari, each font preference applies to all languages.
// (b) The current underlying implementation of nsIFontEnumerator cannot
// really tell you anything about a font: no matter what language or type
// you try to enumerate with EnumerateFonts, you get an array of all
// fonts in the systems (This also breaks our fonts dialog).
// (c) In Gecko, each langauge has a distinct serif and sans-serif font
// preference. Safari has only one default font setting. It seems that
// it checks if it's a serif or sans serif font, and when a site
// explicitly asks to use serif/sans-serif font, it uses the default font
// only if it applies to this type.
// (d) The solution of guessing the lang-group out of the default charset (as
// done in the old Safari migrator) can only work when:
// (1) The default charset preference is set.
// (2) It's not a unicode charset.
// For now, we use the language implied by the system locale as the
// lang-group. The only exception is minimal font size, which is an
// accessibility preference in Safari (under the Advanced tab). If it is set,
// we set it for all languages.
// As for the font type of the default font (serif/sans-serif), the default
// type for the given language is used (set in font.default.LANGGROUP).
_migrateFontSettings: function MPR__migrateFontSettings() {
// If "Never use font sizes smaller than [ ] is set", migrate it for all
// languages.
if (this._dict.has("WebKitMinimumFontSize")) {
let minimumSize = this._dict.get("WebKitMinimumFontSize");
if (typeof minimumSize == "number") {
let prefs = Services.prefs.getChildList("font.minimum-size");
for (let pref of prefs) {
Services.prefs.setIntPref(pref, minimumSize);
}
}
else {
Cu.reportError("WebKitMinimumFontSize was set to an invalid value: " +
minimumSize);
}
}
// In theory, the lang group could be "x-unicode". This will result
// in setting the fonts for "Other Languages".
let lang = this._getLocaleLangGroup();
let anySet = false;
let fontType = Services.prefs.getCharPref("font.default." + lang);
anySet |= this._set("WebKitFixedFont", "font.name.monospace." + lang);
anySet |= this._set("WebKitDefaultFixedFontSize", "font.size.fixed." + lang);
anySet |= this._set("WebKitStandardFont",
"font.name." + fontType + "." + lang);
anySet |= this._set("WebKitDefaultFontSize", "font.size.variable." + lang);
// If we set font settings for a particular language, we'll also set the
// fonts dialog to open with the fonts settings for that langauge.
if (anySet)
Services.prefs.setCharPref("font.language.group", lang);
},
// Get the language group for the system locale.
_getLocaleLangGroup: function MPR__getLocaleLangGroup() {
let locale = Services.locale.getLocaleComponentForUserAgent();
// See nsLanguageAtomService::GetLanguageGroup
let localeLangGroup = "x-unicode";
let bundle = Services.strings.createBundle(
"resource://gre/res/langGroups.properties");
try {
localeLangGroup = bundle.GetStringFromName(locale);
}
catch (ex) {
let hyphenAt = locale.indexOf("-");
if (hyphenAt != -1) {
try {
localeLangGroup = bundle.GetStringFromName(locale.substr(0, hyphenAt));
}
catch (ex2) { }
}
}
return localeLangGroup;
},
_migrateDownloadsFolder: Task.async(function* () {
if (!this._dict.has("DownloadsPath"))
return;
let downloadsFolder = FileUtils.File(this._dict.get("DownloadsPath"));
// If the download folder is set to the Desktop or to ~/Downloads, set the
// folderList pref appropriately so that "Desktop"/Downloads is shown with
// pretty name in the preferences dialog.
let folderListVal = 2;
if (downloadsFolder.equals(FileUtils.getDir("Desk", []))) {
folderListVal = 0;
}
else {
let systemDownloadsPath = yield Downloads.getSystemDownloadsDirectory();
let systemDownloadsFolder = FileUtils.File(systemDownloadsPath);
if (downloadsFolder.equals(systemDownloadsFolder))
folderListVal = 1;
}
Services.prefs.setIntPref("browser.download.folderList", folderListVal);
Services.prefs.setComplexValue("browser.download.dir", Ci.nsILocalFile,
downloadsFolder);
}),
};
function SearchStrings(aMainPreferencesPropertyListInstance) {
this._mainPreferencesPropertyList = aMainPreferencesPropertyListInstance;
}
@ -526,39 +339,6 @@ SearchStrings.prototype = {
}
};
// On OS X, the cookie-accept policy preference is stored in a separate
// property list.
function WebFoundationCookieBehavior(aWebFoundationFile) {
this._file = aWebFoundationFile;
}
WebFoundationCookieBehavior.prototype = {
type: MigrationUtils.resourceTypes.SETTINGS,
migrate: function WFPL_migrate(aCallback) {
PropertyListUtils.read(this._file, MigrationUtils.wrapMigrateFunction(
function migrateCookieBehavior(aDict) {
if (!aDict)
throw new Error("Could not read com.apple.WebFoundation.plist");
if (aDict.has("NSHTTPAcceptCookies")) {
// Setting Safari Firefox
// Always Accept always 0
// Accept from Originating current page 1
// Never Accept never 2
let acceptCookies = aDict.get("NSHTTPAcceptCookies");
let cookieValue = 0;
if (acceptCookies == "never") {
cookieValue = 2;
} else if (acceptCookies == "current page") {
cookieValue = 1;
}
Services.prefs.setIntPref("network.cookie.cookieBehavior",
cookieValue);
}
}.bind(this), aCallback));
}
};
function SafariProfileMigrator() {
}
@ -589,14 +369,9 @@ SafariProfileMigrator.prototype.getResources = function SM_getResources() {
let prefs = this.mainPreferencesPropertyList;
if (prefs) {
resources.push(new Preferences(prefs));
resources.push(new SearchStrings(prefs));
}
let wfFile = FileUtils.getFile("UsrPrfs", ["com.apple.WebFoundation.plist"]);
if (wfFile.exists())
resources.push(new WebFoundationCookieBehavior(wfFile));
return resources;
};

View File

@ -752,6 +752,24 @@
</body>
</method>
<method name="handleEnter">
<parameter name="event"/>
<body><![CDATA[
// Toggle the open state of the add-engine menu button if it's
// selected. We're using handleEnter for this instead of listening
// for the command event because a command event isn't fired.
if (this.selectedButton &&
this.selectedButton.getAttribute("anonid") ==
"addengine-menu-button") {
this.selectedButton.open = !this.selectedButton.open;
return true;
}
// Otherwise, "call super": do what the autocomplete binding's
// handleEnter implementation does.
return this.mController.handleEnter(false, event || null);
]]></body>
</method>
<!-- override |onTextEntered| in autocomplete.xml -->
<method name="onTextEntered">
<parameter name="aEvent"/>
@ -1368,42 +1386,7 @@
// Handle opensearch items. This needs to be done before building the
// list of one off providers, as that code will return early if all the
// alternative engines are hidden.
let addEngineList =
document.getAnonymousElementByAttribute(this, "anonid", "add-engines");
while (addEngineList.firstChild)
addEngineList.firstChild.remove();
const kXULNS =
"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
// Add a button for each engine that the page in the selected browser
// offers. But not when the one-offs are compact. Compact one-offs
// are shown in the urlbar, and the add-engine buttons span the width
// of the popup, so if we added all the engines that a site offers, it
// could effectively break the urlbar popup by offering a ton of
// engines. We should probably make a smaller version of the buttons
// for compact one-offs.
if (!this.compact) {
for (let engine of gBrowser.selectedBrowser.engines || []) {
let button = document.createElementNS(kXULNS, "button");
let label = this.bundle.formatStringFromName("cmd_addFoundEngine",
[engine.title], 1);
button.id = this.telemetryOrigin + "-add-engine-" +
engine.title.replace(/ /g, '-');
button.setAttribute("class", "addengine-item");
button.setAttribute("label", label);
button.setAttribute("pack", "start");
button.setAttribute("crop", "end");
button.setAttribute("tooltiptext", engine.uri);
button.setAttribute("uri", engine.uri);
if (engine.icon) {
button.setAttribute("image", engine.icon);
}
button.setAttribute("title", engine.title);
addEngineList.appendChild(button);
}
}
this._rebuildAddEngineList();
let settingsButton =
document.getAnonymousElementByAttribute(this, "anonid",
@ -1461,6 +1444,9 @@
compactSettingsEl.id = this.telemetryOrigin +
"-anon-search-settings-compact";
const kXULNS =
"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
let dummyItems = enginesPerRow - (oneOffCount % enginesPerRow || enginesPerRow);
for (let i = 0; i < engines.length; ++i) {
let engine = engines[i];
@ -1517,11 +1503,127 @@
]]></body>
</method>
<!-- If a page offers more than this number of engines, the add-engines
menu button is shown, instead of showing the engines directly in the
popup. -->
<field name="_addEngineMenuThreshold">5</field>
<method name="_rebuildAddEngineList">
<body><![CDATA[
let list = document.getAnonymousElementByAttribute(this, "anonid",
"add-engines");
while (list.firstChild) {
list.firstChild.remove();
}
// Add a button for each engine that the page in the selected browser
// offers, but with the following exceptions:
//
// (1) Not when the one-offs are compact. Compact one-offs are shown in
// the urlbar, and the add-engine buttons span the width of the popup,
// so if we added all the engines that a page offers, it could break the
// urlbar popup by offering a ton of engines. We should probably make a
// smaller version of the buttons for compact one-offs.
//
// (2) Not when there are too many offered engines. The popup isn't
// designed to handle too many (by scrolling for example), so a page
// could break the popup by offering too many. Instead, add a single
// menu button with a submenu of all the engines.
if (this.compact || !gBrowser.selectedBrowser.engines) {
return;
}
const kXULNS =
"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
let engines = gBrowser.selectedBrowser.engines;
let tooManyEngines = engines.length > this._addEngineMenuThreshold;
if (tooManyEngines) {
// Make the top-level menu button.
let button = document.createElementNS(kXULNS, "button");
list.appendChild(button);
button.classList.add("addengine-item");
button.setAttribute("anonid", "addengine-menu-button");
button.setAttribute("type", "menu");
button.setAttribute("label",
this.bundle.GetStringFromName("cmd_addFoundEngineMenu"));
button.setAttribute("crop", "end");
button.setAttribute("pack", "start");
// Set the menu button's image to the image of the first engine. The
// offered engines may have differing images, so there's no perfect
// choice here.
let engine = engines[0];
if (engine.icon) {
button.setAttribute("image", engine.icon);
}
// Now make the button's child menupopup.
list = document.createElementNS(kXULNS, "menupopup");
button.appendChild(list);
list.setAttribute("anonid", "addengine-menu");
list.setAttribute("position", "topright topleft");
// Events from child menupopups bubble up to the autocomplete binding,
// which breaks it, so prevent these events from propagating.
let suppressEventTypes = [
"popupshowing",
"popuphiding",
"popupshown",
"popuphidden",
];
for (let type of suppressEventTypes) {
list.addEventListener(type, event => {
event.stopPropagation();
});
}
}
// Finally, add the engines to the list. If there aren't too many
// engines, the list is the add-engines vbox. Otherwise it's the
// menupopup created earlier. In the latter case, create menuitem
// elements instead of buttons, because buttons don't get keyboard
// handling for free inside menupopups.
let eltType = tooManyEngines ? "menuitem" : "button";
for (let engine of engines) {
let button = document.createElementNS(kXULNS, eltType);
button.classList.add("addengine-item");
button.id = this.telemetryOrigin + "-add-engine-" +
this._fixUpEngineNameForID(engine.title);
let label = this.bundle.formatStringFromName("cmd_addFoundEngine",
[engine.title], 1);
button.setAttribute("label", label);
button.setAttribute("crop", "end");
button.setAttribute("tooltiptext", engine.uri);
button.setAttribute("uri", engine.uri);
button.setAttribute("title", engine.title);
if (engine.icon) {
button.setAttribute("image", engine.icon);
}
if (tooManyEngines) {
button.classList.add("menuitem-iconic");
} else {
button.setAttribute("pack", "start");
}
list.appendChild(button);
}
]]></body>
</method>
<method name="_buttonIDForEngine">
<parameter name="engine"/>
<body><![CDATA[
return this.telemetryOrigin + "-engine-one-off-item-" +
engine.name.replace(/ /g, '-');
this._fixUpEngineNameForID(engine.name);
]]></body>
</method>
<method name="_fixUpEngineNameForID">
<parameter name="name"/>
<body><![CDATA[
return name.replace(/ /g, "-");
]]></body>
</method>
@ -1847,6 +1949,22 @@
}
}
// If the add-engine overflow menu item is selected and the user
// presses the right arrow key, open the submenu. Unfortunately
// handling the left arrow key -- to close the popup -- isn't
// straightforward. Once the popup is open, it consumes all key
// events. Setting ignorekeys=handled on it doesn't help, since the
// popup handles all arrow keys. Setting ignorekeys=true on it does
// mean that the popup no longer consumes the left arrow key, but then
// it no longer handles up/down keys to select items in the popup.
else if (this.selectedButton &&
this.selectedButton.getAttribute("anonid") ==
"addengine-menu-button" &&
event.keyCode == KeyEvent.DOM_VK_RIGHT) {
this.selectedButton.open = true;
stopEvent = true;
}
if (stopEvent) {
event.preventDefault();
event.stopPropagation();
@ -1920,11 +2038,37 @@
]]></body>
</method>
<!-- All this stuff is to make the add-engines menu button behave like an
actual menu. The add-engines menu button is shown when there are
many engines offered by the current site. -->
<field name="_addEngineMenuTimeoutMs">200</field>
<field name="_addEngineMenuTimeout">null</field>
<field name="_addEngineMenuShouldBeOpen">false</field>
<method name="_resetAddEngineMenuTimeout">
<body><![CDATA[
if (this._addEngineMenuTimeout) {
clearTimeout(this._addEngineMenuTimeout);
}
this._addEngineMenuTimeout = setTimeout(() => {
delete this._addEngineMenuTimeout;
let button = document.getAnonymousElementByAttribute(
this, "anonid", "addengine-menu-button"
);
button.open = this._addEngineMenuShouldBeOpen;
}, this._addEngineMenuTimeoutMs);
]]></body>
</method>
</implementation>
<handlers>
<handler event="mousedown"><![CDATA[
let target = event.originalTarget;
if (!target.classList.contains("searchbar-engine-one-off-item")) {
return;
}
// Required to receive click events from the buttons on Linux.
event.preventDefault();
]]></handler>
@ -1938,14 +2082,34 @@
if (this._ignoreMouseEvents)
return;
if ((target.classList.contains("searchbar-engine-one-off-item") &&
!target.classList.contains("dummy")) ||
let isOneOff =
target.classList.contains("searchbar-engine-one-off-item") &&
!target.classList.contains("dummy");
if (isOneOff ||
target.classList.contains("addengine-item") ||
target.classList.contains("search-setting-button")) {
this._changeVisuallySelectedButton(target);
}
]]></handler>
<handler event="mouseenter"><![CDATA[
let target = event.originalTarget;
if (target.getAttribute("anonid") == "addengine-menu-button") {
this._addEngineMenuShouldBeOpen = true;
this._resetAddEngineMenuTimeout();
return;
}
]]></handler>
<handler event="mouseleave"><![CDATA[
let target = event.originalTarget;
if (target.getAttribute("anonid") == "addengine-menu-button") {
this._addEngineMenuShouldBeOpen = false;
this._resetAddEngineMenuTimeout();
return;
}
]]></handler>
<handler event="mouseout"><![CDATA[
let target = event.originalTarget;
if (target.localName != "button") {

View File

@ -10,6 +10,7 @@ support-files =
testEngine_diacritics.xml
testEngine_dupe.xml
testEngine_mozsearch.xml
tooManyEnginesOffered.html
webapi.html
[browser_426329.js]
@ -42,3 +43,4 @@ skip-if = os == "linux" # Linux has different focus behaviours.
[browser_searchbar_keyboard_navigation.js]
[browser_searchbar_smallpanel_keyboard_navigation.js]
[browser_webapi.js]
[browser_tooManyEnginesOffered.js]

View File

@ -0,0 +1,88 @@
"use strict";
// This test makes sure that when a page offers many search engines, the search
// popup shows a submenu that lists them instead of showing them in the popup
// itself.
const searchbar = document.getElementById("searchbar");
const searchPopup = document.getElementById("PopupSearchAutoComplete");
const oneOffsContainer =
document.getAnonymousElementByAttribute(searchPopup, "anonid",
"search-one-off-buttons");
add_task(function* test() {
let rootDir = getRootDirectory(gTestPath);
let url = rootDir + "tooManyEnginesOffered.html";
yield BrowserTestUtils.openNewForegroundTab(gBrowser, url);
// Open the search popup.
let promise = promiseEvent(searchPopup, "popupshown");
info("Opening search panel");
searchbar.focus();
EventUtils.synthesizeKey("VK_DOWN", {});
yield promise;
// Make sure it has only one add-engine menu button item.
let items = getOpenSearchItems();
Assert.equal(items.length, 1, "A single button")
let menuButton = items[0];
Assert.equal(menuButton.type, "menu", "A menu button");
// Mouse over the menu button to open it.
let buttonPopup = menuButton.firstChild;
promise = promiseEvent(buttonPopup, "popupshown");
EventUtils.synthesizeMouse(menuButton, 5, 5, { type: "mouseover" });
yield promise;
Assert.ok(menuButton.open, "Submenu should be open");
// Check the engines inside the submenu.
Assert.equal(buttonPopup.childNodes.length, 6, "Expected number of engines");
for (let i = 0; i < buttonPopup.childNodes.length; i++) {
let item = buttonPopup.childNodes[i];
Assert.equal(item.getAttribute("title"), "engine" + (i + 1),
"Expected engine title");
}
// Mouse out of the menu button to close it.
promise = promiseEvent(buttonPopup, "popuphidden");
EventUtils.synthesizeMouse(searchbar, 5, 5, { type: "mousemove" });
yield promise;
Assert.ok(!menuButton.open, "Submenu should be closed");
// Key up until the menu button is selected.
for (let button = null;
button != menuButton;
button = searchbar.textbox.popup.oneOffButtons.selectedButton) {
EventUtils.synthesizeKey("VK_UP", {});
}
// Press the Right arrow key. The submenu should open.
promise = promiseEvent(buttonPopup, "popupshown");
EventUtils.synthesizeKey("VK_RIGHT", {});
yield promise;
Assert.ok(menuButton.open, "Submenu should be open");
// Press the Esc key. The submenu should close.
promise = promiseEvent(buttonPopup, "popuphidden");
EventUtils.synthesizeKey("VK_ESCAPE", {});
yield promise;
Assert.ok(!menuButton.open, "Submenu should be closed");
gBrowser.removeCurrentTab();
});
function getOpenSearchItems() {
let os = [];
let addEngineList =
document.getAnonymousElementByAttribute(oneOffsContainer, "anonid",
"add-engines");
for (let item = addEngineList.firstChild; item; item = item.nextSibling)
os.push(item);
return os;
}

View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="search" type="application/opensearchdescription+xml" title="engine1" href="http://mochi.test:8888/browser/browser/components/search/test/engine1.xml">
<link rel="search" type="application/opensearchdescription+xml" title="engine2" href="http://mochi.test:8888/browser/browser/components/search/test/engine2.xml">
<link rel="search" type="application/opensearchdescription+xml" title="engine3" href="http://mochi.test:8888/browser/browser/components/search/test/engine3.xml">
<link rel="search" type="application/opensearchdescription+xml" title="engine4" href="http://mochi.test:8888/browser/browser/components/search/test/engine4.xml">
<link rel="search" type="application/opensearchdescription+xml" title="engine5" href="http://mochi.test:8888/browser/browser/components/search/test/engine5.xml">
<link rel="search" type="application/opensearchdescription+xml" title="engine6" href="http://mochi.test:8888/browser/browser/components/search/test/engine6.xml">
</head>
<body></body>
</html>

View File

@ -24,14 +24,6 @@
"unpack": true
},
{
"version": "cargo 0.13.0-nightly (eca9e15 2016-11-01) repack",
"size": 3027932,
"digest": "a5c99eeb12b3b9b49632c259c762e34ec13cf72dadf90a0608b8ab1dc66b36cb114c5b45f71d326e12d31d9e88a41b029e6a728ca64cef392c0a8d211c2fe191",
"algorithm": "sha512",
"filename": "cargo.tar.xz",
"unpack": true
},
{
"size": 167175,
"digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
"algorithm": "sha512",

View File

@ -24,14 +24,6 @@
"unpack": true
},
{
"version": "cargo 0.13.0-nightly (eca9e15 2016-11-01) repack",
"size": 3027932,
"digest": "a5c99eeb12b3b9b49632c259c762e34ec13cf72dadf90a0608b8ab1dc66b36cb114c5b45f71d326e12d31d9e88a41b029e6a728ca64cef392c0a8d211c2fe191",
"algorithm": "sha512",
"filename": "cargo.tar.xz",
"unpack": true
},
{
"size": 167175,
"digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
"algorithm": "sha512",

View File

@ -24,14 +24,6 @@
"filename": "MacOSX10.7.sdk.tar.bz2"
},
{
"version": "cargo 0.13.0-nightly (eca9e15 2016-11-01) repack",
"size": 3027932,
"digest": "a5c99eeb12b3b9b49632c259c762e34ec13cf72dadf90a0608b8ab1dc66b36cb114c5b45f71d326e12d31d9e88a41b029e6a728ca64cef392c0a8d211c2fe191",
"algorithm": "sha512",
"filename": "cargo.tar.xz",
"unpack": true
},
{
"size": 167175,
"digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
"algorithm": "sha512",

View File

@ -16,14 +16,6 @@
"unpack": true
},
{
"version": "cargo 0.13.0-nightly (eca9e15 2016-11-01) repack",
"size": 2351877,
"digest": "76283ceda49015f66b03d18b3c28f70ed4baa09accdfc17b2ec935c7af3e9471d140256a33737563baa2545c34e556a125ade6d4858b9226b8c770dbd7c0756f",
"algorithm": "sha512",
"filename": "cargo.tar.bz2",
"unpack": true
},
{
"version": "cctools port from commit hash db1f8d906cb28, ld only",
"size": 634496,
"digest": "037f31fcf29e7bb7fada0d2bdd5e95c7d4cb2692f2a5c98ed6f6a7561b9d81622d015f0d12b291d3667719655f1369e8ce8a0a4a4773aa0ee4753e04a8821173",

View File

@ -14,14 +14,6 @@
"unpack": true
},
{
"version": "cargo 0.13.0-nightly (eca9e15 2016-11-01) repack",
"size": 2214397,
"digest": "4f378fc4178d72d9e0434fca3df342d9dd7619c7c524ec6aedeee78a19583f2a675dfc54224be87030d72a36cef77f997e5275fe1cebac065c38949fa464d842",
"algorithm": "sha512",
"filename": "cargo.tar.bz2",
"unpack": true
},
{
"size": 167175,
"digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
"algorithm": "sha512",

View File

@ -14,14 +14,6 @@
"unpack": true
},
{
"version": "cargo 0.13.0-nightly (eca9e15 2016-11-01) repack",
"size": 2214397,
"digest": "4f378fc4178d72d9e0434fca3df342d9dd7619c7c524ec6aedeee78a19583f2a675dfc54224be87030d72a36cef77f997e5275fe1cebac065c38949fa464d842",
"algorithm": "sha512",
"filename": "cargo.tar.bz2",
"unpack": true
},
{
"size": 167175,
"digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
"algorithm": "sha512",

View File

@ -15,14 +15,6 @@
"unpack": true
},
{
"version": "cargo 0.13.0-nightly (eca9e15 2016-11-01) repack",
"size": 2466539,
"digest": "78b129bd5c933d77c1f09a24c57a652c7cf228fc986000c162f892a46a53fc0f56b8fe1ac924c7e5aaefc4728fd07b679fbf149feb4ed5f2f30c4fd2f776ab7e",
"algorithm": "sha512",
"filename": "cargo.tar.bz2",
"unpack": true
},
{
"size": 167175,
"digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
"algorithm": "sha512",

View File

@ -5,15 +5,6 @@
"use strict";
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
/**
* This constant is chosen to be large enough for a portal recheck to complete,
* and small enough that the delay in opening a tab isn't too noticeable.
* Please see comments for _delayedAddCaptivePortalTab for more details.
*/
const PORTAL_RECHECK_DELAY_MS = 150;
// This is the value used to identify the captive portal notification.
const PORTAL_NOTIFICATION_VALUE = "captive-portal-detected";
this.EXPORTED_SYMBOLS = [ "CaptivePortalWatcher" ];
@ -27,6 +18,16 @@ XPCOMUtils.defineLazyServiceGetter(this, "cps",
"nsICaptivePortalService");
this.CaptivePortalWatcher = {
/**
* This constant is chosen to be large enough for a portal recheck to complete,
* and small enough that the delay in opening a tab isn't too noticeable.
* Please see comments for _delayedCaptivePortalDetected for more details.
*/
PORTAL_RECHECK_DELAY_MS: 150,
// This is the value used to identify the captive portal notification.
PORTAL_NOTIFICATION_VALUE: "captive-portal-detected",
// This holds a weak reference to the captive portal tab so that we
// don't leak it if the user closes it.
_captivePortalTab: null,
@ -38,11 +39,15 @@ this.CaptivePortalWatcher = {
/**
* If a portal is detected when we don't have focus, we first wait for focus
* and then add the tab after a small delay. This is set to true while we wait
* so that in the unlikely event that we receive another notification while
* waiting, we can avoid adding a second tab.
* and then add the tab if, after a recheck, the portal is still active. This
* is set to true while we wait so that in the unlikely event that we receive
* another notification while waiting, we don't do things twice.
*/
_waitingToAddTab: false,
_delayedCaptivePortalDetectedInProgress: false,
// In the situation above, this is set to true while we wait for the recheck.
// This flag exists so that tests can appropriately simulate a recheck.
_waitingForRecheck: false,
get canonicalURL() {
return Services.prefs.getCharPref("captivedetect.canonicalURL");
@ -82,13 +87,13 @@ this.CaptivePortalWatcher = {
this._captivePortalGone();
break;
case "xul-window-visible":
this._delayedAddCaptivePortalTab();
this._delayedCaptivePortalDetected();
break;
}
},
_captivePortalDetected() {
if (this._waitingToAddTab) {
if (this._delayedCaptivePortalDetectedInProgress) {
return;
}
@ -98,14 +103,11 @@ this.CaptivePortalWatcher = {
// focused, when the user (re-)focuses a browser window, we open the tab
// immediately in that window so they can login before continuing to browse.
if (!win || win != Services.ww.activeWindow) {
this._waitingToAddTab = true;
this._delayedCaptivePortalDetectedInProgress = true;
Services.obs.addObserver(this, "xul-window-visible", false);
return;
}
// The browser is in use - show a notification and add the tab without
// selecting it, unless the caller specifically requested selection.
this._ensureCaptivePortalTab(win);
this._showNotification(win);
},
@ -121,6 +123,7 @@ this.CaptivePortalWatcher = {
}
this._captivePortalTab = Cu.getWeakReference(tab);
win.gBrowser.selectedTab = tab;
return tab;
},
@ -129,8 +132,8 @@ this.CaptivePortalWatcher = {
* doesn't have focus. Triggers a portal recheck to reaffirm state, and adds
* the tab if needed after a short delay to allow the recheck to complete.
*/
_delayedAddCaptivePortalTab() {
if (!this._waitingToAddTab) {
_delayedCaptivePortalDetected() {
if (!this._delayedCaptivePortalDetectedInProgress) {
return;
}
@ -143,36 +146,35 @@ this.CaptivePortalWatcher = {
// Trigger a portal recheck. The user may have logged into the portal via
// another client, or changed networks.
let lastChecked = cps.lastChecked;
cps.recheckCaptivePortal();
this._waitingForRecheck = true;
let requestTime = Date.now();
// We wait for PORTAL_RECHECK_DELAY_MS after the trigger.
// - If the portal is no longer locked, we don't need to add a tab.
// - If it is, the delay is chosen to not be extremely noticeable.
setTimeout(() => {
this._waitingToAddTab = false;
let self = this;
Services.obs.addObserver(function observer() {
let time = Date.now() - requestTime;
Services.obs.removeObserver(observer, "captive-portal-check-complete");
self._waitingForRecheck = false;
self._delayedCaptivePortalDetectedInProgress = false;
if (cps.state != cps.LOCKED_PORTAL) {
// We're free of the portal!
return;
}
this._showNotification(win);
let tab = this._ensureCaptivePortalTab(win);
// Focus the tab only if the recheck has completed, i.e. we're sure
// that the portal is still locked. This way, if the recheck completes
// after we add the tab and we're free of the portal, the tab contents
// won't flicker.
if (cps.lastChecked != lastChecked) {
win.gBrowser.selectedTab = tab;
self._showNotification(win);
if (time <= self.PORTAL_RECHECK_DELAY_MS) {
// The amount of time elapsed since we requested a recheck (i.e. since
// the browser window was focused) was small enough that we can add and
// focus a tab with the login page with no noticeable delay.
self._ensureCaptivePortalTab(win);
}
}, PORTAL_RECHECK_DELAY_MS);
}, "captive-portal-check-complete", false);
},
_captivePortalGone() {
if (this._waitingToAddTab) {
if (this._delayedCaptivePortalDetectedInProgress) {
Services.obs.removeObserver(this, "xul-window-visible");
this._waitingToAddTab = false;
this._delayedCaptivePortalDetectedInProgress = false;
}
this._removeNotification();
@ -205,13 +207,6 @@ this.CaptivePortalWatcher = {
tabbrowser.removeTab(tab);
},
get _productName() {
delete this._productName;
return this._productName =
Services.strings.createBundle("chrome://branding/locale/brand.properties")
.GetStringFromName("brandShortName");
},
get _browserBundle() {
delete this._browserBundle;
return this._browserBundle =
@ -243,7 +238,7 @@ this.CaptivePortalWatcher = {
{
label: this._browserBundle.GetStringFromName("captivePortal.showLoginPage"),
callback: () => {
win.gBrowser.selectedTab = this._ensureCaptivePortalTab(win);
this._ensureCaptivePortalTab(win);
// Returning true prevents the notification from closing.
return true;
@ -252,8 +247,7 @@ this.CaptivePortalWatcher = {
},
];
let message = this._browserBundle.formatStringFromName("captivePortal.infoMessage",
[this._productName], 1);
let message = this._browserBundle.GetStringFromName("captivePortal.infoMessage2");
let closeHandler = (aEventName) => {
if (aEventName != "removed") {
@ -263,7 +257,7 @@ this.CaptivePortalWatcher = {
};
let nb = win.document.getElementById("high-priority-global-notificationbox");
let n = nb.appendNotification(message, PORTAL_NOTIFICATION_VALUE, "",
let n = nb.appendNotification(message, this.PORTAL_NOTIFICATION_VALUE, "",
nb.PRIORITY_INFO_MEDIUM, buttons, closeHandler);
this._captivePortalNotification = Cu.getWeakReference(n);

View File

@ -2,6 +2,13 @@
Components.utils.import("resource:///modules/RecentWindow.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "CaptivePortalWatcher",
"resource:///modules/CaptivePortalWatcher.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "cps",
"@mozilla.org/network/captive-portal-service;1",
"nsICaptivePortalService");
const CANONICAL_CONTENT = "success";
const CANONICAL_URL = "data:text/plain;charset=utf-8," + CANONICAL_CONTENT;
const CANONICAL_URL_REDIRECTED = "data:text/plain;charset=utf-8,redirected";
@ -22,30 +29,56 @@ add_task(function* setup() {
function* portalDetectedNoBrowserWindow() {
let getMostRecentBrowserWindow = RecentWindow.getMostRecentBrowserWindow;
RecentWindow.getMostRecentBrowserWindow = () => {};
Services.obs.notifyObservers(null, "captive-portal-login", null);
yield portalDetected();
RecentWindow.getMostRecentBrowserWindow = getMostRecentBrowserWindow;
}
function* openWindowAndWaitForPortalTabAndNotification() {
function* portalDetected() {
Services.obs.notifyObservers(null, "captive-portal-login", null);
yield BrowserTestUtils.waitForCondition(() => {
return cps.state == cps.LOCKED_PORTAL;
}, "Waiting for Captive Portal Service to update state after portal detected.");
}
function* freePortal(aSuccess) {
Services.obs.notifyObservers(null,
"captive-portal-login-" + (aSuccess ? "success" : "abort"), null);
yield BrowserTestUtils.waitForCondition(() => {
return cps.state != cps.LOCKED_PORTAL;
}, "Waiting for Captive Portal Service to update state after portal freed.");
}
function* openWindowAndWaitForPortalUI(aLongRecheck) {
// CaptivePortalWatcher triggers a recheck when a window gains focus. If
// the time taken for the check to complete is under PORTAL_RECHECK_DELAY_MS,
// a tab with the login page is opened and selected. If it took longer,
// no tab is opened. It's not reliable to time things in an async test,
// so use a delay threshold of -1 to simulate a long recheck (so that any
// amount of time is considered excessive), and a very large threshold to
// simulate a short recheck.
CaptivePortalWatcher.PORTAL_RECHECK_DELAY_MS = aLongRecheck ? -1 : 1000000;
let win = yield BrowserTestUtils.openNewBrowserWindow();
// Thanks to things being async, at this point we now have a new browser window
// but the portal notification and tab may or may not have opened. So first we
// check if there's already a portal notification, and if not, wait.
let notification = win.document.getElementById("high-priority-global-notificationbox")
.getNotificationWithValue(PORTAL_NOTIFICATION_VALUE);
if (!notification) {
notification =
yield BrowserTestUtils.waitForGlobalNotificationBar(win, PORTAL_NOTIFICATION_VALUE);
// After a new window is opened, CaptivePortalWatcher asks for a recheck, and
// waits for it to complete. We need to manually tell it a recheck completed.
yield BrowserTestUtils.waitForCondition(() => {
return CaptivePortalWatcher._waitingForRecheck;
}, "Waiting for CaptivePortalWatcher to trigger a recheck.");
Services.obs.notifyObservers(null, "captive-portal-check-complete", null);
let notification = ensurePortalNotification(win);
if (aLongRecheck) {
ensureNoPortalTab(win);
testShowLoginPageButtonVisibility(notification, "visible");
return win;
}
// Then we see if there's already a portal tab. If it's open, it'll be the second one.
let tab = win.gBrowser.tabs[1];
if (!tab || tab.linkedBrowser.currentURI.spec != CANONICAL_URL) {
// The tab either hasn't been opened yet or it hasn't loaded the portal URL.
// Waiting for a location change in the tabbrowser covers both cases.
if (tab.linkedBrowser.currentURI.spec != CANONICAL_URL) {
// The tab should load the canonical URL, wait for it.
yield BrowserTestUtils.waitForLocationChange(win.gBrowser, CANONICAL_URL);
// At this point the portal tab should be the second tab. If there is still
// no second tab, something is wrong, and the selectedTab test below will fail.
tab = win.gBrowser.tabs[1];
}
is(win.gBrowser.selectedTab, tab,
"The captive portal tab should be open and selected in the new window.");
@ -53,11 +86,6 @@ function* openWindowAndWaitForPortalTabAndNotification() {
return win;
}
function freePortal(aSuccess) {
Services.obs.notifyObservers(null,
"captive-portal-login-" + (aSuccess ? "success" : "abort"), null);
}
function ensurePortalTab(win) {
// For the tests that call this function, it's enough to ensure there
// are two tabs in the window - the default tab and the portal tab.
@ -124,15 +152,34 @@ function* closeWindowAndWaitForXulWindowVisible(win) {
// for login-abort (aSuccess set to true and false respectively).
let testCasesForBothSuccessAndAbort = [
/**
* A portal is detected when there's no browser window,
* then a browser window is opened, then the portal is freed.
* A portal is detected when there's no browser window, then a browser
* window is opened, then the portal is freed.
* The portal tab should be added and focused when the window is
* opened, and closed automatically when the success event is fired.
* The captive portal notification should be shown when the window is
* opened, and closed automatically when the success event is fired.
*/
function* test_detectedWithNoBrowserWindow_Open(aSuccess) {
yield portalDetectedNoBrowserWindow();
let win = yield openWindowAndWaitForPortalTabAndNotification();
freePortal(aSuccess);
let win = yield openWindowAndWaitForPortalUI();
yield freePortal(aSuccess);
ensureNoPortalTab(win);
ensureNoPortalNotification(win);
yield closeWindowAndWaitForXulWindowVisible(win);
},
/**
* A portal is detected when there's no browser window, then a browser
* window is opened, then the portal is freed.
* The recheck triggered when the browser window is opened takes a
* long time. No portal tab should be added.
* The captive portal notification should be shown when the window is
* opened, and closed automatically when the success event is fired.
*/
function* test_detectedWithNoBrowserWindow_LongRecheck(aSuccess) {
yield portalDetectedNoBrowserWindow();
let win = yield openWindowAndWaitForPortalUI(true);
yield freePortal(aSuccess);
ensureNoPortalTab(win);
ensureNoPortalNotification(win);
yield closeWindowAndWaitForXulWindowVisible(win);
@ -141,13 +188,13 @@ let testCasesForBothSuccessAndAbort = [
/**
* A portal is detected when there's no browser window, and the
* portal is freed before a browser window is opened. No portal
* tab should be added when a browser window is opened.
* UI should be shown when a browser window is opened.
*/
function* test_detectedWithNoBrowserWindow_GoneBeforeOpen(aSuccess) {
yield portalDetectedNoBrowserWindow();
freePortal(aSuccess);
yield freePortal(aSuccess);
let win = yield BrowserTestUtils.openNewBrowserWindow();
// Wait for a while to make sure no tab is opened.
// Wait for a while to make sure no UI is shown.
yield new Promise(resolve => {
setTimeout(resolve, 1000);
});
@ -157,41 +204,15 @@ let testCasesForBothSuccessAndAbort = [
},
/**
* A portal is detected when a browser window has focus. A portal tab should be
* opened in the background in the focused browser window. If the portal is
* freed when the tab isn't focused, the tab should be closed automatically.
* A portal is detected when a browser window has focus. No portal tab should
* be opened. A notification bar should be displayed in the focused window.
*/
function* test_detectedWithFocus(aSuccess) {
let win = RecentWindow.getMostRecentBrowserWindow();
let p = BrowserTestUtils.waitForNewTab(win.gBrowser, CANONICAL_URL);
Services.obs.notifyObservers(null, "captive-portal-login", null);
let tab = yield p;
ensurePortalTab(win);
ensurePortalNotification(win);
isnot(win.gBrowser.selectedTab, tab,
"The captive portal tab should be open in the background in the current window.");
freePortal(aSuccess);
yield portalDetected();
ensureNoPortalTab(win);
ensureNoPortalNotification(win);
},
/**
* A portal is detected when a browser window has focus. A portal tab should be
* opened in the background in the focused browser window. If the portal is
* freed when the tab has focus, the tab should be closed automatically.
*/
function* test_detectedWithFocus_selectedTab(aSuccess) {
let win = RecentWindow.getMostRecentBrowserWindow();
let p = BrowserTestUtils.waitForNewTab(win.gBrowser, CANONICAL_URL);
Services.obs.notifyObservers(null, "captive-portal-login", null);
let tab = yield p;
ensurePortalTab(win);
ensurePortalNotification(win);
isnot(win.gBrowser.selectedTab, tab,
"The captive portal tab should be open in the background in the current window.");
win.gBrowser.selectedTab = tab;
freePortal(aSuccess);
ensureNoPortalTab(win);
yield freePortal(aSuccess);
ensureNoPortalNotification(win);
},
];
@ -206,69 +227,18 @@ let singleRunTestCases = [
*/
function* test_detectedWithNoBrowserWindow_Redirect() {
yield portalDetectedNoBrowserWindow();
let win = yield openWindowAndWaitForPortalTabAndNotification();
let win = yield openWindowAndWaitForPortalUI();
let browser = win.gBrowser.selectedTab.linkedBrowser;
let loadPromise =
BrowserTestUtils.browserLoaded(browser, false, CANONICAL_URL_REDIRECTED);
BrowserTestUtils.loadURI(browser, CANONICAL_URL_REDIRECTED);
yield loadPromise;
freePortal(true);
yield freePortal(true);
ensurePortalTab(win);
ensureNoPortalNotification(win);
yield closeWindowAndWaitForXulWindowVisible(win);
},
/**
* A portal is detected when a browser window has focus. A portal tab should be
* opened in the background in the focused browser window. If the portal is
* freed when the tab isn't focused, the tab should be closed automatically,
* even if the portal has redirected to a URL other than CANONICAL_URL.
*/
function* test_detectedWithFocus_redirectUnselectedTab() {
let win = RecentWindow.getMostRecentBrowserWindow();
let p = BrowserTestUtils.waitForNewTab(win.gBrowser, CANONICAL_URL);
Services.obs.notifyObservers(null, "captive-portal-login", null);
let tab = yield p;
ensurePortalTab(win);
ensurePortalNotification(win);
isnot(win.gBrowser.selectedTab, tab,
"The captive portal tab should be open in the background in the current window.");
let browser = tab.linkedBrowser;
let loadPromise =
BrowserTestUtils.browserLoaded(browser, false, CANONICAL_URL_REDIRECTED);
BrowserTestUtils.loadURI(browser, CANONICAL_URL_REDIRECTED);
yield loadPromise;
freePortal(true);
ensureNoPortalTab(win);
ensureNoPortalNotification(win);
},
/**
* A portal is detected when a browser window has focus. A portal tab should be
* opened in the background in the focused browser window. If the portal is
* freed when the tab has focus, and it has redirected to another page, the
* tab should be kept open.
*/
function* test_detectedWithFocus_redirectSelectedTab() {
let win = RecentWindow.getMostRecentBrowserWindow();
let p = BrowserTestUtils.waitForNewTab(win.gBrowser, CANONICAL_URL);
Services.obs.notifyObservers(null, "captive-portal-login", null);
let tab = yield p;
ensurePortalNotification(win);
isnot(win.gBrowser.selectedTab, tab,
"The captive portal tab should be open in the background in the current window.");
win.gBrowser.selectedTab = tab;
let browser = tab.linkedBrowser;
let loadPromise =
BrowserTestUtils.browserLoaded(browser, false, CANONICAL_URL_REDIRECTED);
BrowserTestUtils.loadURI(browser, CANONICAL_URL_REDIRECTED);
yield loadPromise;
freePortal(true);
ensurePortalTab(win);
ensureNoPortalNotification(win);
yield BrowserTestUtils.removeTab(tab);
},
/**
* Test the various expected behaviors of the "Show Login Page" button
* in the captive portal notification. The button should be visible for
@ -277,12 +247,8 @@ let singleRunTestCases = [
*/
function* test_showLoginPageButton() {
let win = RecentWindow.getMostRecentBrowserWindow();
let p = BrowserTestUtils.waitForNewTab(win.gBrowser, CANONICAL_URL);
Services.obs.notifyObservers(null, "captive-portal-login", null);
let tab = yield p;
yield portalDetected();
let notification = ensurePortalNotification(win);
isnot(win.gBrowser.selectedTab, tab,
"The captive portal tab should be open in the background in the current window.");
testShowLoginPageButtonVisibility(notification, "visible");
function testPortalTabSelectedAndButtonNotVisible() {
@ -290,19 +256,18 @@ let singleRunTestCases = [
testShowLoginPageButtonVisibility(notification, "hidden");
}
// Select the captive portal tab. The button should hide.
let otherTab = win.gBrowser.selectedTab;
win.gBrowser.selectedTab = tab;
testShowLoginPageButtonVisibility(notification, "hidden");
// Select the other tab. The button should become visible.
win.gBrowser.selectedTab = otherTab;
testShowLoginPageButtonVisibility(notification, "visible");
// Simulate clicking the button. The portal tab should be selected and
// the button should hide.
let button = notification.querySelector("button.notification-button");
button.click();
function* clickButtonAndExpectNewPortalTab() {
let p = BrowserTestUtils.waitForNewTab(win.gBrowser, CANONICAL_URL);
button.click();
let tab = yield p;
is(win.gBrowser.selectedTab, tab, "The captive portal tab should be selected.");
return tab;
}
// Simulate clicking the button. The portal tab should be opened and
// selected and the button should hide.
let tab = yield clickButtonAndExpectNewPortalTab();
testPortalTabSelectedAndButtonNotVisible();
// Close the tab. The button should become visible.
@ -310,16 +275,9 @@ let singleRunTestCases = [
ensureNoPortalTab(win);
testShowLoginPageButtonVisibility(notification, "visible");
function* clickButtonAndExpectNewPortalTab() {
p = BrowserTestUtils.waitForNewTab(win.gBrowser, CANONICAL_URL);
button.click();
tab = yield p;
is(win.gBrowser.selectedTab, tab, "The captive portal tab should be selected.");
}
// When the button is clicked, a new portal tab should be opened and
// selected.
yield clickButtonAndExpectNewPortalTab();
tab = yield clickButtonAndExpectNewPortalTab();
// Open another arbitrary tab. The button should become visible. When it's clicked,
// the portal tab should be selected.
@ -333,10 +291,10 @@ let singleRunTestCases = [
yield BrowserTestUtils.removeTab(tab);
win.gBrowser.selectedTab = anotherTab;
testShowLoginPageButtonVisibility(notification, "visible");
yield clickButtonAndExpectNewPortalTab();
tab = yield clickButtonAndExpectNewPortalTab();
yield BrowserTestUtils.removeTab(anotherTab);
freePortal(true);
yield freePortal(true);
ensureNoPortalTab(win);
ensureNoPortalNotification(win);
},

View File

@ -266,6 +266,12 @@ menuitem[cmd="cmd_clearhistory"][disabled] {
color: HighlightText;
}
.addengine-item[type=menu][selected],
.addengine-item[type=menu][open] {
color: inherit;
background-color: var(--arrowpanel-dimmed-further);
}
.addengine-icon {
width: 16px;
}
@ -277,7 +283,8 @@ menuitem[cmd="cmd_clearhistory"][disabled] {
list-style-image: url("chrome://browser/skin/badge-add-engine.png");
}
.addengine-item > .button-box > .button-text {
.addengine-item > .button-box > .button-text,
.addengine-item[type=menu] > .button-box > .box-inherit > .button-text {
-moz-box-flex: 1;
text-align: start;
padding-inline-start: 10px;
@ -297,6 +304,12 @@ menuitem[cmd="cmd_clearhistory"][disabled] {
}
}
.addengine-item[type=menu] > .button-box > .button-menu-dropmarker {
display: -moz-box;
-moz-appearance: menuarrow !important;
list-style-image: none;
}
.search-panel-tree > .autocomplete-treebody::-moz-tree-cell {
border-top: none !important;
}

View File

@ -247,6 +247,12 @@
color: HighlightText;
}
.addengine-item[type=menu][selected],
.addengine-item[type=menu][open] {
color: inherit;
background-color: var(--arrowpanel-dimmed-further);
}
.addengine-icon {
width: 16px;
}
@ -258,7 +264,8 @@
list-style-image: url("chrome://browser/skin/badge-add-engine.png");
}
.addengine-item > .button-box > .button-text {
.addengine-item > .button-box > .button-text,
.addengine-item[type=menu] > .button-box > .box-inherit > .button-text {
-moz-box-flex: 1;
text-align: start;
padding-inline-start: 10px;
@ -278,6 +285,12 @@
}
}
.addengine-item[type=menu] > .button-box > .button-menu-dropmarker {
display: -moz-box;
-moz-appearance: menuarrow !important;
list-style-image: none;
}
.search-panel-tree > .autocomplete-treebody::-moz-tree-cell {
border-top: none !important;
}

View File

@ -259,6 +259,12 @@
color: HighlightText;
}
.addengine-item[type=menu][selected],
.addengine-item[type=menu][open] {
color: inherit;
background-color: var(--arrowpanel-dimmed-further);
}
.addengine-icon {
width: 16px;
}
@ -270,7 +276,8 @@
list-style-image: url("chrome://browser/skin/badge-add-engine.png");
}
.addengine-item > .button-box > .button-text {
.addengine-item > .button-box > .button-text,
.addengine-item[type=menu] > .button-box > .box-inherit > .button-text {
-moz-box-flex: 1;
text-align: start;
padding-inline-start: 10px;
@ -290,6 +297,12 @@
}
}
.addengine-item[type=menu] > .button-box > .button-menu-dropmarker {
display: -moz-box;
-moz-appearance: menuarrow !important;
list-style-image: none;
}
.search-panel-tree > .autocomplete-treebody::-moz-tree-cell {
border-top: none !important;
}

View File

@ -4,7 +4,7 @@
# Assume this is compiled with --enable-rpath so we don't
# have to set LD_LIBRARY_PATH.
RUSTC="$topsrcdir/rustc/bin/rustc"
CARGO="$topsrcdir/cargo/bin/cargo"
CARGO="$topsrcdir/rustc/bin/cargo"
# Enable rust in the build.
ac_add_options --enable-rust

View File

@ -232,7 +232,7 @@ var NetMonitorView = {
// • The response content size and request total time are necessary for
// populating the statistics view.
// • The response mime type is used for categorization.
yield whenDataAvailable(requestsView.attachments, [
yield whenDataAvailable(requestsView, [
"responseHeaders", "status", "contentSize", "mimeType", "totalTime"
]);
} catch (ex) {
@ -1189,18 +1189,19 @@ var $all = (selector, target = document) => target.querySelectorAll(selector);
* Makes sure certain properties are available on all objects in a data store.
*
* @param array dataStore
* A list of objects for which to check the availability of properties.
* The request view object from which to fetch the item list.
* @param array mandatoryFields
* A list of strings representing properties of objects in dataStore.
* @return object
* A promise resolved when all objects in dataStore contain the
* properties defined in mandatoryFields.
*/
function whenDataAvailable(dataStore, mandatoryFields) {
function whenDataAvailable(requestsView, mandatoryFields) {
let deferred = promise.defer();
let interval = setInterval(() => {
if (dataStore.every(item => {
const { attachments } = requestsView;
if (attachments.length > 0 && attachments.every(item => {
return mandatoryFields.every(field => field in item);
})) {
clearInterval(interval);

View File

@ -1161,6 +1161,68 @@ DebuggerClient.prototype = {
activeRequestsToReject.forEach(request => reject("active", request));
},
/**
* Search for all requests in process for this client, including those made via
* protocol.js and wait all of them to complete. Since the requests seen when this is
* first called may in turn trigger more requests, we keep recursing through this
* function until there is no more activity.
*
* This is a fairly heavy weight process, so it's only meant to be used in tests.
*
* @return Promise
* Resolved when all requests have settled.
*/
waitForRequestsToSettle() {
let requests = [];
// Gather all pending and active requests in this client
// The request object supports a Promise API for completion (it has .then())
this._pendingRequests.forEach(requestsForActor => {
// Each value is an array of pending requests
requests = requests.concat(requestsForActor);
});
this._activeRequests.forEach(requestForActor => {
// Each value is a single active request
requests = requests.concat(requestForActor);
});
// protocol.js
// Use a Set because some fronts (like domwalker) seem to have multiple parents.
let fronts = new Set();
let poolsToVisit = [...this._pools];
// With protocol.js, each front can potentially have it's own pools containing child
// fronts, forming a tree. Descend through all the pools to locate all child fronts.
while (poolsToVisit.length) {
let pool = poolsToVisit.shift();
fronts.add(pool);
for (let child of pool.poolChildren()) {
poolsToVisit.push(child);
}
}
// For each front, wait for its requests to settle
for (let front of fronts) {
if (front.hasRequests()) {
requests.push(front.waitForRequestsToSettle());
}
}
// Abort early if there are no requests
if (!requests.length) {
return Promise.resolve();
}
return DevToolsUtils.settleAll(requests).catch(() => {
// One of the requests might have failed, but ignore that situation here and pipe
// both success and failure through the same path. The important part is just that
// we waited.
}).then(() => {
// Repeat, more requests may have started in response to those we just waited for
return this.waitForRequestsToSettle();
});
},
registerClient: function (client) {
let actorID = client.actor;
if (!actorID) {

View File

@ -11,6 +11,7 @@ var {EventTarget} = require("sdk/event/target");
var events = require("sdk/event/core");
var object = require("sdk/util/object");
var {getStack, callFunctionWithAsyncStack} = require("devtools/shared/platform/stack");
var {settleAll} = require("devtools/shared/DevToolsUtils");
exports.emit = events.emit;
@ -794,6 +795,20 @@ var Pool = Class({
return !this.__poolMap || this._poolMap.size == 0;
},
// Generator that yields each non-self child of the pool.
poolChildren: function* () {
if (!this.__poolMap) {
return;
}
for (let actor of this.__poolMap.values()) {
// Self-owned actors are ok, but don't need visiting twice.
if (actor === this) {
continue;
}
yield actor;
}
},
/**
* Destroy this item, removing it from a parent if it has one,
* and destroying all children if necessary.
@ -1283,7 +1298,23 @@ var Front = Class({
deferred.resolve(packet);
}
}, stack, "DevTools RDP");
}
},
hasRequests() {
return !!this._requests.length;
},
/**
* Wait for all current requests from this front to settle. This is especially useful
* for tests and other utility environments that may not have events or mechanisms to
* await the completion of requests without this utility.
*
* @return Promise
* Resolved when all requests have settled.
*/
waitForRequestsToSettle() {
return settleAll(this._requests.map(({ deferred }) => deferred.promise));
},
});
exports.Front = Front;

View File

@ -7907,7 +7907,7 @@ struct GetSurfaceDataShmem
}
static BufferType
GetBuffer(ReturnType aReturnValue)
GetBuffer(const ReturnType& aReturnValue)
{
return aReturnValue.get<char>();
}
@ -8197,7 +8197,7 @@ nsContentUtils::SendKeyEvent(nsIWidget* aWidget,
}
nsresult
nsContentUtils::SendMouseEvent(nsCOMPtr<nsIPresShell> aPresShell,
nsContentUtils::SendMouseEvent(const nsCOMPtr<nsIPresShell>& aPresShell,
const nsAString& aType,
float aX,
float aY,

View File

@ -2550,7 +2550,7 @@ public:
* Synthesize a mouse event to the given widget
* (see nsIDOMWindowUtils.sendMouseEvent).
*/
static nsresult SendMouseEvent(nsCOMPtr<nsIPresShell> aPresShell,
static nsresult SendMouseEvent(const nsCOMPtr<nsIPresShell>& aPresShell,
const nsAString& aType,
float aX,
float aY,

View File

@ -1669,7 +1669,7 @@ nsDOMWindowUtils::SuppressEventHandling(bool aSuppress)
}
static nsresult
getScrollXYAppUnits(nsWeakPtr aWindow, bool aFlushLayout, nsPoint& aScrollPos) {
getScrollXYAppUnits(const nsWeakPtr& aWindow, bool aFlushLayout, nsPoint& aScrollPos) {
nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryReferent(aWindow);
nsCOMPtr<nsIDocument> doc = window ? window->GetExtantDoc() : nullptr;
NS_ENSURE_STATE(doc);

View File

@ -329,7 +329,7 @@ operator<(const RefPtr<nsPluginElement>& lhs,
}
static bool
PluginShouldBeHidden(nsCString aName) {
PluginShouldBeHidden(const nsCString& aName) {
// This only supports one hidden plugin
return Preferences::GetCString("plugins.navigator.hidden_ctp_plugin").Equals(aName);
}

View File

@ -68,7 +68,7 @@ class nsPropertyTable
* Get the value of the property |aPropertyName| for node |aObject|.
* |aResult|, if supplied, is filled in with a return status code.
**/
void* GetProperty(nsPropertyOwner aObject,
void* GetProperty(const nsPropertyOwner& aObject,
nsIAtom *aPropertyName,
nsresult *aResult = nullptr)
{
@ -92,7 +92,7 @@ class nsPropertyTable
* table changes too). If |aTransfer| is false the property will just be
* deleted instead.
*/
nsresult SetProperty(nsPropertyOwner aObject,
nsresult SetProperty(const nsPropertyOwner& aObject,
nsIAtom *aPropertyName,
void *aPropertyValue,
NSPropertyDtorFunc aDtor,
@ -116,7 +116,7 @@ class nsPropertyTable
* |aObject|, but do not call the property's destructor function. The
* property value is returned.
*/
void* UnsetProperty(nsPropertyOwner aObject,
void* UnsetProperty(const nsPropertyOwner& aObject,
nsIAtom *aPropertyName,
nsresult *aStatus = nullptr)
{

View File

@ -569,7 +569,7 @@ private:
void NotifyCompositorUpdated(RefPtr<layers::KnowsCompositor> aKnowsCompositor)
{
mKnowsCompositor = aKnowsCompositor;
mKnowsCompositor = aKnowsCompositor.forget();
}
void DoAudioSeek();

View File

@ -139,7 +139,7 @@ public:
* Called by the MediaStreamGraph as it appends a chunk with a different
* principal id than the current one.
*/
void SetLastPrincipalHandle(PrincipalHandle aLastPrincipalHandle)
void SetLastPrincipalHandle(const PrincipalHandle& aLastPrincipalHandle)
{
mLastPrincipalHandle = aLastPrincipalHandle;
}

View File

@ -288,7 +288,7 @@ RefPtr<GenericPromise> InvokeUntil(Work aWork, Condition aCondition) {
}
struct Helper {
static void Iteration(RefPtr<GenericPromise::Private> aPromise, Work aLocalWork, Condition aLocalCondition) {
static void Iteration(const RefPtr<GenericPromise::Private>& aPromise, Work aLocalWork, Condition aLocalCondition) {
if (!aLocalWork()) {
aPromise->Reject(NS_ERROR_FAILURE, __func__);
} else if (aLocalCondition()) {

View File

@ -47,7 +47,7 @@ WidevineDecryptor::SetCDM(RefPtr<CDMWrapper> aCDM, uint32_t aInstanceId)
{
mCDM = aCDM;
mInstanceId = aInstanceId;
sDecryptors[mInstanceId] = aCDM;
sDecryptors[mInstanceId] = aCDM.forget();
}
void

View File

@ -51,7 +51,7 @@ inline bool IsConditionalPunctuation(char16_t ch)
// mozInlineSpellWordUtil::Init
nsresult
mozInlineSpellWordUtil::Init(nsWeakPtr aWeakEditor)
mozInlineSpellWordUtil::Init(const nsWeakPtr& aWeakEditor)
{
nsresult rv;

View File

@ -61,7 +61,7 @@ public:
mSoftBegin(nullptr, 0), mSoftEnd(nullptr, 0),
mNextWordIndex(-1), mSoftTextValid(false) {}
nsresult Init(nsWeakPtr aWeakEditor);
nsresult Init(const nsWeakPtr& aWeakEditor);
nsresult SetEnd(nsINode* aEndNode, int32_t aEndOffset);

View File

@ -375,7 +375,7 @@ SkImageIsMask(const sk_sp<SkImage>& aImage)
}
static bool
ExtractAlphaBitmap(sk_sp<SkImage> aImage, SkBitmap* aResultBitmap)
ExtractAlphaBitmap(const sk_sp<SkImage>& aImage, SkBitmap* aResultBitmap)
{
SkImageInfo info = SkImageInfo::MakeA8(aImage->width(), aImage->height());
SkBitmap bitmap;

View File

@ -59,7 +59,7 @@ SourceSurfaceSkia::InitFromData(unsigned char* aData,
}
bool
SourceSurfaceSkia::InitFromImage(sk_sp<SkImage> aImage,
SourceSurfaceSkia::InitFromImage(const sk_sp<SkImage>& aImage,
SurfaceFormat aFormat,
DrawTargetSkia* aOwner)
{

View File

@ -35,7 +35,7 @@ public:
int32_t aStride,
SurfaceFormat aFormat);
bool InitFromImage(sk_sp<SkImage> aImage,
bool InitFromImage(const sk_sp<SkImage>& aImage,
SurfaceFormat aFormat = SurfaceFormat::UNKNOWN,
DrawTargetSkia* aOwner = nullptr);

View File

@ -214,7 +214,7 @@ PersistentBufferProviderShared::SetForwarder(ShadowLayerForwarder* aFwd)
}
TextureClient*
PersistentBufferProviderShared::GetTexture(Maybe<uint32_t> aIndex)
PersistentBufferProviderShared::GetTexture(const Maybe<uint32_t>& aIndex)
{
if (aIndex.isNothing() || !CheckIndex(aIndex.value())) {
return nullptr;

View File

@ -145,7 +145,7 @@ protected:
~PersistentBufferProviderShared();
TextureClient* GetTexture(Maybe<uint32_t> aIndex);
TextureClient* GetTexture(const Maybe<uint32_t>& aIndex);
bool CheckIndex(uint32_t aIndex) { return aIndex < mTextures.length(); }
void Destroy();

View File

@ -109,7 +109,7 @@ struct JS_PUBLIC_API(ShortestPaths)
}
bool
operator()(Traversal& traversal, JS::ubi::Node origin, JS::ubi::Edge& edge,
operator()(Traversal& traversal, const JS::ubi::Node& origin, JS::ubi::Edge& edge,
BackEdge* back, bool first)
{
MOZ_ASSERT(back);

View File

@ -4067,13 +4067,13 @@ Parser<FullParseHandler>::checkDestructuringName(ParseNode* expr, Maybe<Declarat
template <>
bool
Parser<FullParseHandler>::checkDestructuringPattern(ParseNode* pattern,
Maybe<DeclarationKind> maybeDecl,
const Maybe<DeclarationKind>& maybeDecl,
PossibleError* possibleError /* = nullptr */);
template <>
bool
Parser<FullParseHandler>::checkDestructuringObject(ParseNode* objectPattern,
Maybe<DeclarationKind> maybeDecl)
const Maybe<DeclarationKind>& maybeDecl)
{
MOZ_ASSERT(objectPattern->isKind(PNK_OBJECT));
@ -4108,7 +4108,7 @@ Parser<FullParseHandler>::checkDestructuringObject(ParseNode* objectPattern,
template <>
bool
Parser<FullParseHandler>::checkDestructuringArray(ParseNode* arrayPattern,
Maybe<DeclarationKind> maybeDecl)
const Maybe<DeclarationKind>& maybeDecl)
{
MOZ_ASSERT(arrayPattern->isKind(PNK_ARRAY));
@ -4178,7 +4178,7 @@ Parser<FullParseHandler>::checkDestructuringArray(ParseNode* arrayPattern,
template <>
bool
Parser<FullParseHandler>::checkDestructuringPattern(ParseNode* pattern,
Maybe<DeclarationKind> maybeDecl,
const Maybe<DeclarationKind>& maybeDecl,
PossibleError* possibleError /* = nullptr */)
{
if (pattern->isKind(PNK_ARRAYCOMP)) {
@ -4200,7 +4200,7 @@ Parser<FullParseHandler>::checkDestructuringPattern(ParseNode* pattern,
template <>
bool
Parser<SyntaxParseHandler>::checkDestructuringPattern(Node pattern,
Maybe<DeclarationKind> maybeDecl,
const Maybe<DeclarationKind>& maybeDecl,
PossibleError* possibleError /* = nullptr */)
{
return abortIfSyntaxParser();

View File

@ -1413,15 +1413,15 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
Node objectLiteral(YieldHandling yieldHandling, PossibleError* possibleError);
// Top-level entrypoint into destructuring pattern checking/name-analyzing.
bool checkDestructuringPattern(Node pattern, mozilla::Maybe<DeclarationKind> maybeDecl,
bool checkDestructuringPattern(Node pattern, const mozilla::Maybe<DeclarationKind>& maybeDecl,
PossibleError* possibleError = nullptr);
// Recursive methods for checking/name-analyzing subcomponents of a
// destructuring pattern. The array/object methods *must* be passed arrays
// or objects. The name method may be passed anything but will report an
// error if not passed a name.
bool checkDestructuringArray(Node arrayPattern, mozilla::Maybe<DeclarationKind> maybeDecl);
bool checkDestructuringObject(Node objectPattern, mozilla::Maybe<DeclarationKind> maybeDecl);
bool checkDestructuringArray(Node arrayPattern, const mozilla::Maybe<DeclarationKind>& maybeDecl);
bool checkDestructuringObject(Node objectPattern, const mozilla::Maybe<DeclarationKind>& maybeDecl);
bool checkDestructuringName(Node expr, mozilla::Maybe<DeclarationKind> maybeDecl);
bool checkAssignmentToCall(Node node, unsigned errnum);

View File

@ -1305,7 +1305,7 @@ class ABIArg
// clobbers all registers besides |unused|, but does not clobber floating point
// registers.
inline LiveGeneralRegisterSet
SavedNonVolatileRegisters(AllocatableGeneralRegisterSet unused)
SavedNonVolatileRegisters(const AllocatableGeneralRegisterSet& unused)
{
LiveGeneralRegisterSet result;

View File

@ -69,11 +69,11 @@ struct InternalBarrierMethods<TaggedProto>
static void readBarrier(const TaggedProto& proto);
static bool isMarkableTaggedPointer(TaggedProto proto) {
static bool isMarkableTaggedPointer(const TaggedProto& proto) {
return proto.isObject();
}
static bool isMarkable(TaggedProto proto) {
static bool isMarkable(const TaggedProto& proto) {
return proto.isObject();
}
};

View File

@ -447,7 +447,13 @@ public:
}
if (mVsyncChild) {
mVsyncRate = mVsyncChild->GetVsyncRate();
// VsyncChild::VsyncRate() is a simple getter for the cached
// hardware vsync rate. We depend on that
// VsyncChild::GetVsyncRate() being called in the constructor
// will result in a response with the actual vsync rate sooner
// or later. Until that happens VsyncChild::VsyncRate() returns
// TimeDuration::Forever() and we have to guess below.
mVsyncRate = mVsyncChild->VsyncRate();
}
// If hardware queries fail / are unsupported, we have to just guess.

View File

@ -38,7 +38,7 @@ friend class FrameChildListIterator;
bool operator==(FrameChildListIDs aOther) const {
return mIDs == aOther.mIDs;
}
bool operator!=(FrameChildListIDs aOther) const {
bool operator!=(const FrameChildListIDs& aOther) const {
return !(*this == aOther);
}
bool Contains(FrameChildListIDs aOther) const {
@ -106,7 +106,7 @@ operator|(mozilla::layout::FrameChildListID aLeftOp,
inline mozilla::layout::FrameChildListIDs
operator|(mozilla::layout::FrameChildListID aLeftOp,
mozilla::layout::FrameChildListIDs aRightOp)
const mozilla::layout::FrameChildListIDs& aRightOp)
{
return mozilla::layout::FrameChildListIDs(aLeftOp) | aRightOp;
}

View File

@ -283,16 +283,7 @@ StickyScrollContainer::GetScrollRanges(nsIFrame* aFrame, nsRect* aOuter,
aOuter->SetRect(nscoord_MIN/2, nscoord_MIN/2, nscoord_MAX, nscoord_MAX);
aInner->SetRect(nscoord_MIN/2, nscoord_MIN/2, nscoord_MAX, nscoord_MAX);
// Due to margin collapsing, |firstCont->GetNormalPosition()| can sometimes
// fall outside of |contain|. (This is because GetNormalPosition() returns
// the actual position after margin collapsing, while|contain| is
// calculated based on the frame's GetUsedMargin() which is pre-collapsing.)
// This can cause |aInner|, as computed below, to not be contained inside
// |aOuter|, which confuses the code that consumes these values.
// This is hard to fix properly (TODO), but clamping |normalPosition| to
// |contain| works around it.
const nsPoint normalPosition =
contain.ClampPoint(firstCont->GetNormalPosition());
const nsPoint normalPosition = firstCont->GetNormalPosition();
// Bottom and top
if (stick.YMost() != nscoord_MAX/2) {
@ -315,6 +306,17 @@ StickyScrollContainer::GetScrollRanges(nsIFrame* aFrame, nsRect* aOuter,
aInner->SetRightEdge(normalPosition.x - stick.x);
aOuter->SetRightEdge(contain.XMost() - stick.x);
}
// Make sure |inner| does not extend outside of |outer|. (The consumers of
// the Layers API, to which this information is propagated, expect this
// invariant to hold.) The calculated value of |inner| can sometimes extend
// outside of |outer|, for example due to margin collapsing, since
// GetNormalPosition() returns the actual position after margin collapsing,
// while |contain| is calculated based on the frame's GetUsedMargin() which
// is pre-collapsing.
// Note that this doesn't necessarily solve all problems stemming from
// comparing pre- and post-collapsing margins (TODO: find a proper solution).
*aInner = aInner->Intersect(*aOuter);
}
void

View File

@ -83,6 +83,12 @@ VsyncChild::GetVsyncRate()
return mVsyncRate;
}
TimeDuration
VsyncChild::VsyncRate()
{
return mVsyncRate;
}
mozilla::ipc::IPCResult
VsyncChild::RecvVsyncRate(const float& aVsyncRate)
{

View File

@ -38,7 +38,15 @@ public:
// Bind a VsyncObserver into VsyncChild after ipc channel connected.
void SetVsyncObserver(VsyncObserver* aVsyncObserver);
// GetVsyncRate is a getter for mVsyncRate which sends a requests to
// VsyncParent to retreive the hardware vsync rate if mVsyncRate
// hasn't already been set.
TimeDuration GetVsyncRate();
// VsyncRate is a getter for mVsyncRate which always returns
// mVsyncRate directly, potentially returning
// TimeDuration::Forever() if mVsyncRate hasn't been set by calling
// GetVsyncRate.
TimeDuration VsyncRate();
private:
VsyncChild();

View File

@ -26,6 +26,7 @@ skip-if(!asyncPan) == split-layers-multi-scrolling-1.html split-layers-multi-scr
fuzzy-if(skiaContent,2,240000) fuzzy-if(browserIsRemote&&!skiaContent&&(cocoaWidget||winWidget),1,240000) skip-if(!asyncPan) == split-opacity-layers-1.html split-opacity-layers-1-ref.html
skip-if(!asyncPan) == sticky-pos-scrollable-1.html sticky-pos-scrollable-1-ref.html
skip-if(!asyncPan) == sticky-pos-scrollable-2.html sticky-pos-scrollable-2-ref.html
skip-if(!asyncPan) == sticky-pos-scrollable-3.html sticky-pos-scrollable-3-ref.html
skip-if(!asyncPan) == fixed-pos-scrollable-1.html fixed-pos-scrollable-1-ref.html
skip-if(!asyncPan) == culling-1.html culling-1-ref.html
skip-if(!asyncPan) == position-fixed-iframe-1.html position-fixed-iframe-1-ref.html

View File

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<style>
#header {
position: fixed;
top: 0;
}
#header > div {
border: solid blue 2px;
height: 100px;
width: 100px;
}
</style>
<div id="section">
<div id="header">
<div></div>
</div>
</div>
</html>

View File

@ -0,0 +1,33 @@
<!DOCTYPE html>
<html reftest-async-scroll
reftest-displayport-x="0" reftest-displayport-y="0"
reftest-displayport-w="800" reftest-displayport-h="2000"
reftest-async-scroll-x="0" reftest-async-scroll-y="100">
<style>
html {
overflow: hidden;
}
#section {
padding-top: 1px;
}
#header {
position: sticky;
top: 0;
}
#header > div {
margin-top: -50px;
border: solid blue 2px;
height: 100px;
width: 100px;
}
#spacer {
height: 1500px;
}
</style>
<div id="section">
<div id="header">
<div></div>
</div>
<div id="spacer"></div>
</div>
</html>

View File

@ -1504,6 +1504,7 @@ static malloc_zone_t * szone = (malloc_zone_t*)(&l_szone);
static lion_malloc_introspection l_ozone_introspect;
static malloc_introspection_t * const ozone_introspect =
(malloc_introspection_t*)(&l_ozone_introspect);
static malloc_zone_t *get_default_zone();
static void szone2ozone(malloc_zone_t *zone, size_t size);
static size_t zone_version_size(int version);
#else
@ -6081,7 +6082,7 @@ MALLOC_OUT:
/*
* Overwrite the default memory allocator to use jemalloc everywhere.
*/
default_zone = malloc_default_zone();
default_zone = get_default_zone();
/*
* We only use jemalloc with MacOS 10.6 and 10.7. jemalloc is disabled
@ -7062,6 +7063,32 @@ zone_version_size(int version)
}
}
static malloc_zone_t *get_default_zone()
{
malloc_zone_t **zones = NULL;
unsigned int num_zones = 0;
/*
* On OSX 10.12, malloc_default_zone returns a special zone that is not
* present in the list of registered zones. That zone uses a "lite zone"
* if one is present (apparently enabled when malloc stack logging is
* enabled), or the first registered zone otherwise. In practice this
* means unless malloc stack logging is enabled, the first registered
* zone is the default.
* So get the list of zones to get the first one, instead of relying on
* malloc_default_zone.
*/
if (KERN_SUCCESS != malloc_get_all_zones(0, NULL, (vm_address_t**) &zones,
&num_zones)) {
/* Reset the value in case the failure happened after it was set. */
num_zones = 0;
}
if (num_zones) {
return zones[0];
}
return malloc_default_zone();
}
/*
* Overlay the default scalable zone (szone) such that existing allocations are
* drained, and further allocations come from jemalloc. This is necessary

View File

@ -549,10 +549,10 @@ pref("media.getusermedia.screensharing.enabled", true);
#endif
#ifdef RELEASE_OR_BETA
pref("media.getusermedia.screensharing.allowed_domains", "webex.com,*.webex.com,ciscospark.com,*.ciscospark.com,projectsquared.com,*.projectsquared.com,*.room.co,room.co,beta.talky.io,talky.io,*.clearslide.com,appear.in,*.appear.in,tokbox.com,*.tokbox.com,*.sso.francetelecom.fr,*.si.francetelecom.fr,*.sso.infra.ftgroup,*.multimedia-conference.orange-business.com,*.espacecollaboration.orange-business.com,free.gotomeeting.com,g2m.me,*.g2m.me,*.mypurecloud.com,*.mypurecloud.com.au,spreed.me,*.spreed.me,*.spreed.com,air.mozilla.org,*.circuit.com,*.yourcircuit.com,circuit.siemens.com,yourcircuit.siemens.com,circuitsandbox.net,*.unify.com,tandi.circuitsandbox.net,*.ericsson.net,*.cct.ericsson.net,*.opentok.com,*.conf.meetecho.com,meet.jit.si,*.meet.jit.si,web.stage.speakeasyapp.net,web.speakeasyapp.net,*.hipchat.me,*.beta-wspbx.com,*.wspbx.com,*.unifiedcloudit.com,*.smartboxuc.com,*.smartbox-uc.com,*.panterranetworks.com,pexipdemo.com,*.pexipdemo.com,pex.me,*.pex.me,*.rd.pexip.com,1click.io,*.1click.io,*.fuze.com,*.fuzemeeting.com,*.thinkingphones.com,gotomeeting.com,*.gotomeeting.com,gotowebinar.com,*.gotowebinar.com,gototraining.com,*.gototraining.com,citrix.com,*.citrix.com,expertcity.com,*.expertcity.com,citrixonline.com,*.citrixonline.com,g2m.me,*.g2m.me,gotomeet.me,*.gotomeet.me,gotomeet.at,*.gotomeet.at,miriadaxdes.miriadax.net,certificacion.miriadax.net,miriadax.net,*.wire.com,sylaps.com,*.sylaps.com,bluejeans.com,*.bluejeans.com,*.a.bluejeans.com");
pref("media.getusermedia.screensharing.allowed_domains", "webex.com,*.webex.com,ciscospark.com,*.ciscospark.com,projectsquared.com,*.projectsquared.com,*.room.co,room.co,beta.talky.io,talky.io,*.clearslide.com,appear.in,*.appear.in,tokbox.com,*.tokbox.com,*.sso.francetelecom.fr,*.si.francetelecom.fr,*.sso.infra.ftgroup,*.multimedia-conference.orange-business.com,*.espacecollaboration.orange-business.com,free.gotomeeting.com,g2m.me,*.g2m.me,*.mypurecloud.com,*.mypurecloud.com.au,spreed.me,*.spreed.me,*.spreed.com,air.mozilla.org,*.circuit.com,*.yourcircuit.com,circuit.siemens.com,yourcircuit.siemens.com,circuitsandbox.net,*.unify.com,tandi.circuitsandbox.net,*.ericsson.net,*.cct.ericsson.net,*.opentok.com,*.conf.meetecho.com,meet.jit.si,*.meet.jit.si,web.stage.speakeasyapp.net,web.speakeasyapp.net,*.hipchat.me,*.beta-wspbx.com,*.wspbx.com,*.unifiedcloudit.com,*.smartboxuc.com,*.smartbox-uc.com,*.panterranetworks.com,pexipdemo.com,*.pexipdemo.com,pex.me,*.pex.me,*.rd.pexip.com,1click.io,*.1click.io,*.fuze.com,*.fuzemeeting.com,*.thinkingphones.com,gotomeeting.com,*.gotomeeting.com,gotowebinar.com,*.gotowebinar.com,gototraining.com,*.gototraining.com,citrix.com,*.citrix.com,expertcity.com,*.expertcity.com,citrixonline.com,*.citrixonline.com,g2m.me,*.g2m.me,gotomeet.me,*.gotomeet.me,gotomeet.at,*.gotomeet.at,miriadaxdes.miriadax.net,certificacion.miriadax.net,miriadax.net,*.wire.com,sylaps.com,*.sylaps.com,bluejeans.com,*.bluejeans.com,*.a.bluejeans.com,*.bbcollab.com");
#else
// includes Mozilla's test domain: mozilla.github.io (not intended for release)
pref("media.getusermedia.screensharing.allowed_domains", "mozilla.github.io,webex.com,*.webex.com,ciscospark.com,*.ciscospark.com,projectsquared.com,*.projectsquared.com,*.room.co,room.co,beta.talky.io,talky.io,*.clearslide.com,appear.in,*.appear.in,tokbox.com,*.tokbox.com,*.sso.francetelecom.fr,*.si.francetelecom.fr,*.sso.infra.ftgroup,*.multimedia-conference.orange-business.com,*.espacecollaboration.orange-business.com,free.gotomeeting.com,g2m.me,*.g2m.me,*.mypurecloud.com,*.mypurecloud.com.au,spreed.me,*.spreed.me,*.spreed.com,air.mozilla.org,*.circuit.com,*.yourcircuit.com,circuit.siemens.com,yourcircuit.siemens.com,circuitsandbox.net,*.unify.com,tandi.circuitsandbox.net,*.ericsson.net,*.cct.ericsson.net,*.opentok.com,*.conf.meetecho.com,meet.jit.si,*.meet.jit.si,web.stage.speakeasyapp.net,web.speakeasyapp.net,*.hipchat.me,*.beta-wspbx.com,*.wspbx.com,*.unifiedcloudit.com,*.smartboxuc.com,*.smartbox-uc.com,*.panterranetworks.com,pexipdemo.com,*.pexipdemo.com,pex.me,*.pex.me,*.rd.pexip.com,1click.io,*.1click.io,*.fuze.com,*.fuzemeeting.com,*.thinkingphones.com,gotomeeting.com,*.gotomeeting.com,gotowebinar.com,*.gotowebinar.com,gototraining.com,*.gototraining.com,citrix.com,*.citrix.com,expertcity.com,*.expertcity.com,citrixonline.com,*.citrixonline.com,g2m.me,*.g2m.me,gotomeet.me,*.gotomeet.me,gotomeet.at,*.gotomeet.at,miriadaxdes.miriadax.net,certificacion.miriadax.net,miriadax.net,*.wire.com,sylaps.com,*.sylaps.com,bluejeans.com,*.bluejeans.com,*.a.bluejeans.com");
pref("media.getusermedia.screensharing.allowed_domains", "mozilla.github.io,webex.com,*.webex.com,ciscospark.com,*.ciscospark.com,projectsquared.com,*.projectsquared.com,*.room.co,room.co,beta.talky.io,talky.io,*.clearslide.com,appear.in,*.appear.in,tokbox.com,*.tokbox.com,*.sso.francetelecom.fr,*.si.francetelecom.fr,*.sso.infra.ftgroup,*.multimedia-conference.orange-business.com,*.espacecollaboration.orange-business.com,free.gotomeeting.com,g2m.me,*.g2m.me,*.mypurecloud.com,*.mypurecloud.com.au,spreed.me,*.spreed.me,*.spreed.com,air.mozilla.org,*.circuit.com,*.yourcircuit.com,circuit.siemens.com,yourcircuit.siemens.com,circuitsandbox.net,*.unify.com,tandi.circuitsandbox.net,*.ericsson.net,*.cct.ericsson.net,*.opentok.com,*.conf.meetecho.com,meet.jit.si,*.meet.jit.si,web.stage.speakeasyapp.net,web.speakeasyapp.net,*.hipchat.me,*.beta-wspbx.com,*.wspbx.com,*.unifiedcloudit.com,*.smartboxuc.com,*.smartbox-uc.com,*.panterranetworks.com,pexipdemo.com,*.pexipdemo.com,pex.me,*.pex.me,*.rd.pexip.com,1click.io,*.1click.io,*.fuze.com,*.fuzemeeting.com,*.thinkingphones.com,gotomeeting.com,*.gotomeeting.com,gotowebinar.com,*.gotowebinar.com,gototraining.com,*.gototraining.com,citrix.com,*.citrix.com,expertcity.com,*.expertcity.com,citrixonline.com,*.citrixonline.com,g2m.me,*.g2m.me,gotomeet.me,*.gotomeet.me,gotomeet.at,*.gotomeet.at,miriadaxdes.miriadax.net,certificacion.miriadax.net,miriadax.net,*.wire.com,sylaps.com,*.sylaps.com,bluejeans.com,*.bluejeans.com,*.a.bluejeans.com,*.bbcollab.com");
#endif
// OS/X 10.6 and XP have screen/window sharing off by default due to various issues - Caveat emptor
pref("media.getusermedia.screensharing.allow_on_old_platforms", false);

View File

@ -69,7 +69,7 @@ private:
WebSocketData():lock("Dashboard.webSocketData")
{
}
uint32_t IndexOf(nsCString hostname, uint32_t mSerial)
uint32_t IndexOf(const nsCString& hostname, uint32_t mSerial)
{
LogData temp(hostname, mSerial, false);
return data.IndexOf(temp);

View File

@ -3116,7 +3116,7 @@ void
nsCookieService::GetCookieStringInternal(nsIURI *aHostURI,
bool aIsForeign,
bool aHttpBound,
const NeckoOriginAttributes aOriginAttrs,
const NeckoOriginAttributes& aOriginAttrs,
bool aIsPrivate,
nsCString &aCookieString)
{

View File

@ -293,7 +293,7 @@ class nsCookieService final : public nsICookieService
nsresult GetBaseDomain(nsIURI *aHostURI, nsCString &aBaseDomain, bool &aRequireHostMatch);
nsresult GetBaseDomainFromHost(const nsACString &aHost, nsCString &aBaseDomain);
nsresult GetCookieStringCommon(nsIURI *aHostURI, nsIChannel *aChannel, bool aHttpBound, char** aCookie);
void GetCookieStringInternal(nsIURI *aHostURI, bool aIsForeign, bool aHttpBound, const NeckoOriginAttributes aOriginAttrs, bool aIsPrivate, nsCString &aCookie);
void GetCookieStringInternal(nsIURI *aHostURI, bool aIsForeign, bool aHttpBound, const NeckoOriginAttributes& aOriginAttrs, bool aIsPrivate, nsCString &aCookie);
nsresult SetCookieStringCommon(nsIURI *aHostURI, const char *aCookieHeader, const char *aServerTime, nsIChannel *aChannel, bool aFromHttp);
void SetCookieStringInternal(nsIURI *aHostURI, bool aIsForeign, nsDependentCString &aCookieHeader, const nsCString &aServerTime, bool aFromHttp, const NeckoOriginAttributes &aOriginAttrs, bool aIsPrivate, nsIChannel* aChannel);
bool SetCookieInternal(nsIURI *aHostURI, const nsCookieKey& aKey, bool aRequireHostMatch, CookieStatus aStatus, nsDependentCString &aCookieHeader, int64_t aServerTime, bool aFromHttp, nsIChannel* aChannel);

View File

@ -97,7 +97,7 @@ SpdyPushCache::~SpdyPushCache()
}
bool
SpdyPushCache::RegisterPushedStreamHttp2(nsCString key,
SpdyPushCache::RegisterPushedStreamHttp2(const nsCString& key,
Http2PushedStream *stream)
{
LOG3(("SpdyPushCache::RegisterPushedStreamHttp2 %s 0x%X\n",
@ -112,7 +112,7 @@ SpdyPushCache::RegisterPushedStreamHttp2(nsCString key,
}
Http2PushedStream *
SpdyPushCache::RemovePushedStreamHttp2(nsCString key)
SpdyPushCache::RemovePushedStreamHttp2(const nsCString& key)
{
Http2PushedStream *rv = mHashHttp2.Get(key);
LOG3(("SpdyPushCache::RemovePushedStreamHttp2 %s 0x%X\n",

View File

@ -240,7 +240,7 @@ AltSvcMapping::TTL()
}
void
AltSvcMapping::SyncString(nsCString str)
AltSvcMapping::SyncString(const nsCString& str)
{
MOZ_ASSERT(NS_IsMainThread());
mStorage->Put(HashKey(), str,

View File

@ -96,7 +96,7 @@ public:
private:
virtual ~AltSvcMapping() {};
void SyncString(nsCString val);
void SyncString(const nsCString& val);
RefPtr<DataStorage> mStorage;
int32_t mStorageEpoch;
void Serialize (nsCString &out);

View File

@ -43,9 +43,9 @@ public:
// The cache holds only weak pointers - no references
SpdyPushCache();
virtual ~SpdyPushCache();
bool RegisterPushedStreamHttp2(nsCString key,
bool RegisterPushedStreamHttp2(const nsCString& key,
Http2PushedStream *stream);
Http2PushedStream *RemovePushedStreamHttp2(nsCString key);
Http2PushedStream *RemovePushedStreamHttp2(const nsCString& key);
private:
nsDataHashtable<nsCStringHashKey, Http2PushedStream *> mHashHttp2;
};

View File

@ -255,7 +255,7 @@ private:
#endif
// Exists solely for proxying release of the TransportFlow to the STS thread
static void ReleaseTransportFlow(RefPtr<TransportFlow> aFlow) {}
static void ReleaseTransportFlow(const RefPtr<TransportFlow>& aFlow) {}
// Data:
// NOTE: while this array will auto-expand, increases in the number of

View File

@ -1,3 +1,21 @@
3.0.5
=====
- Fixed OverflowError with ProcessPoolExecutor on Windows (regression introduced in 3.0.4)
3.0.4
=====
- Fixed inability to forcibly terminate the process if there are pending workers
3.0.3
=====
- Fixed AttributeErrors on exit on Python 2.x
3.0.2
=====

View File

@ -1,6 +1,6 @@
Metadata-Version: 1.0
Name: futures
Version: 3.0.2
Version: 3.0.5
Summary: Backport of the concurrent.futures package from Python 3.2
Home-page: https://github.com/agronholm/pythonfutures
Author: Alex Gronholm

View File

@ -227,7 +227,8 @@ def as_completed(fs, timeout=None):
finally:
for f in fs:
f._waiters.remove(waiter)
with f._condition:
f._waiters.remove(waiter)
DoneAndNotDoneFutures = collections.namedtuple(
'DoneAndNotDoneFutures', 'done not_done')
@ -274,7 +275,8 @@ def wait(fs, timeout=None, return_when=ALL_COMPLETED):
waiter.event.wait(timeout)
for f in fs:
f._waiters.remove(waiter)
with f._condition:
f._waiters.remove(waiter)
done.update(waiter.finished_futures)
return DoneAndNotDoneFutures(done, set(fs) - done)

View File

@ -73,11 +73,11 @@ _shutdown = False
def _python_exit():
global _shutdown
_shutdown = True
items = list(_threads_queues.items())
items = list(_threads_queues.items()) if _threads_queues else ()
for t, q in items:
q.put(None)
for t, q in items:
t.join()
t.join(sys.maxint)
# Controls how many more calls than processes will be queued in the call queue.
# A smaller number will mean that processes spend more time idle waiting for
@ -347,7 +347,7 @@ class ProcessPoolExecutor(_base.Executor):
# Wake up queue management thread
self._result_queue.put(None)
if wait:
self._queue_management_thread.join()
self._queue_management_thread.join(sys.maxint)
# To reduce the risk of openning too many files, remove references to
# objects that use file descriptors.
self._queue_management_thread = None

View File

@ -32,11 +32,11 @@ _shutdown = False
def _python_exit():
global _shutdown
_shutdown = True
items = list(_threads_queues.items())
items = list(_threads_queues.items()) if _threads_queues else ()
for t, q in items:
q.put(None)
for t, q in items:
t.join()
t.join(sys.maxint)
atexit.register(_python_exit)
@ -130,5 +130,5 @@ class ThreadPoolExecutor(_base.Executor):
self._work_queue.put(None)
if wait:
for t in self._threads:
t.join()
t.join(sys.maxint)
shutdown.__doc__ = _base.Executor.shutdown.__doc__

View File

@ -112,7 +112,7 @@ And:
.. class:: ThreadPoolExecutor(max_workers)
Executes calls asynchronously using at pool of at most *max_workers* threads.
Executes calls asynchronously using a pool of at most *max_workers* threads.
.. _threadpoolexecutor-example:

View File

@ -1,6 +1,6 @@
Metadata-Version: 1.0
Name: futures
Version: 3.0.2
Version: 3.0.5
Summary: Backport of the concurrent.futures package from Python 3.2
Home-page: https://github.com/agronholm/pythonfutures
Author: Alex Gronholm

View File

@ -1 +1 @@
{"is_release": false, "git_version": "fdbc9c3"}
{"is_release": false, "git_version": "6532a74"}

View File

@ -1,4 +1,11 @@
#!/usr/bin/env python
from warnings import warn
import sys
if sys.version_info[0] > 2:
warn('This backport is meant only for Python 2.\n'
'Python 3 users do not need it, as the concurrent.futures '
'package is available in the standard library.')
extras = {}
try:
@ -8,7 +15,7 @@ except ImportError:
from distutils.core import setup
setup(name='futures',
version='3.0.2',
version='3.0.5',
description='Backport of the concurrent.futures package from Python 3.2',
author='Brian Quinlan',
author_email='brian@sweetapp.com',

View File

@ -7,6 +7,7 @@ import contextlib
import logging
import re
import time
import gc
from StringIO import StringIO
from test import test_support
@ -222,6 +223,7 @@ class ThreadPoolShutdownTest(ThreadPoolMixin, ExecutorShutdownTest):
executor.map(abs, range(-5, 5))
threads = executor._threads
del executor
gc.collect()
for t in threads:
t.join()
@ -257,6 +259,7 @@ class ProcessPoolShutdownTest(ProcessPoolMixin, ExecutorShutdownTest):
queue_management_thread = executor._queue_management_thread
processes = executor._processes
del executor
gc.collect()
queue_management_thread.join()
for p in processes:
@ -575,19 +578,19 @@ class FutureTests(unittest.TestCase):
def test_repr(self):
self.assertRegexpMatches(repr(PENDING_FUTURE),
'<Future at 0x[0-9a-f]+ state=pending>')
'<Future at 0x[0-9a-f]+L? state=pending>')
self.assertRegexpMatches(repr(RUNNING_FUTURE),
'<Future at 0x[0-9a-f]+ state=running>')
'<Future at 0x[0-9a-f]+L? state=running>')
self.assertRegexpMatches(repr(CANCELLED_FUTURE),
'<Future at 0x[0-9a-f]+ state=cancelled>')
'<Future at 0x[0-9a-f]+L? state=cancelled>')
self.assertRegexpMatches(repr(CANCELLED_AND_NOTIFIED_FUTURE),
'<Future at 0x[0-9a-f]+ state=cancelled>')
'<Future at 0x[0-9a-f]+L? state=cancelled>')
self.assertRegexpMatches(
repr(EXCEPTION_FUTURE),
'<Future at 0x[0-9a-f]+ state=finished raised IOError>')
'<Future at 0x[0-9a-f]+L? state=finished raised IOError>')
self.assertRegexpMatches(
repr(SUCCESSFUL_FUTURE),
'<Future at 0x[0-9a-f]+ state=finished returned int>')
'<Future at 0x[0-9a-f]+L? state=finished returned int>')
def test_cancel(self):
f1 = create_future(state=PENDING)

View File

@ -1,5 +1,5 @@
[tox]
envlist = py26,py27
envlist = py26,py27,pypy,jython
[testenv]
commands={envpython} test_futures.py []

View File

@ -4,12 +4,17 @@
from __future__ import absolute_import, print_function, unicode_literals
import __main__
import argparse
import logging
import mozpack.path as mozpath
import os
from concurrent.futures import (
ThreadPoolExecutor,
as_completed,
thread,
)
from mozbuild.base import (
MachCommandBase,
)
@ -53,6 +58,10 @@ class MachCommands(MachCommandBase):
action='store_true',
help=('Collect all tests under given path instead of default '
'test resolution. Supports pytest-style tests.'))
@CommandArgument('-j', '--jobs',
default=1,
type=int,
help='Number of concurrent jobs to run. Default is 1.')
@CommandArgument('tests', nargs='*',
metavar='TEST',
help=('Tests to run. Each test can be a single file or a directory. '
@ -63,7 +72,8 @@ class MachCommands(MachCommandBase):
subsuite=None,
verbose=False,
path_only=False,
stop=False):
stop=False,
jobs=1):
self._activate_virtualenv()
def find_tests_by_path():
@ -90,8 +100,6 @@ class MachCommands(MachCommandBase):
# launching Python multiple times. Most tests are run via mozunit,
# which produces output in the format Mozilla infrastructure expects.
# Some tests are run via pytest.
return_code = 0
found_tests = False
if test_objects is None:
# If we're not being called from `mach test`, do our own
# test resolution.
@ -113,46 +121,80 @@ class MachCommands(MachCommandBase):
# Otherwise just run everything in PYTHON_UNIT_TESTS
test_objects = resolver.resolve_tests(flavor='python')
for test in test_objects:
found_tests = True
f = test['path']
file_displayed_test = [] # Used as a boolean.
def _line_handler(line):
if not file_displayed_test:
output = ('Ran' in line or 'collected' in line or
line.startswith('TEST-'))
if output:
file_displayed_test.append(True)
inner_return_code = self.run_process(
[self.virtualenv_manager.python_path, f],
ensure_exit_code=False, # Don't throw on non-zero exit code.
log_name='python-test',
# subprocess requires native strings in os.environ on Windows
append_env={b'PYTHONDONTWRITEBYTECODE': str('1')},
line_handler=_line_handler)
return_code += inner_return_code
if not file_displayed_test:
self.log(logging.WARN, 'python-test', {'file': f},
'TEST-UNEXPECTED-FAIL | No test output (missing mozunit.main() call?): {file}')
if verbose:
if inner_return_code != 0:
self.log(logging.INFO, 'python-test', {'file': f},
'Test failed: {file}')
else:
self.log(logging.INFO, 'python-test', {'file': f},
'Test passed: {file}')
if stop and return_code > 0:
return 1
if not found_tests:
if not test_objects:
message = 'TEST-UNEXPECTED-FAIL | No tests collected'
if not path_only:
message += ' (Not in PYTHON_UNIT_TESTS? Try --path-only?)'
message += ' (Not in PYTHON_UNIT_TESTS? Try --path-only?)'
self.log(logging.WARN, 'python-test', {}, message)
return 1
return 0 if return_code == 0 else 1
self.jobs = jobs
self.terminate = False
self.verbose = verbose
return_code = 0
with ThreadPoolExecutor(max_workers=self.jobs) as executor:
futures = [executor.submit(self._run_python_test, test['path'])
for test in test_objects]
try:
for future in as_completed(futures):
output, ret = future.result()
for line in output:
self.log(logging.INFO, 'python-test', {'line': line.rstrip()}, '{line}')
return_code = return_code or ret
except KeyboardInterrupt:
# Hack to force stop currently running threads.
# https://gist.github.com/clchiou/f2608cbe54403edb0b13
executor._threads.clear()
thread._threads_queues.clear()
raise
return return_code
def _run_python_test(self, test_path):
from mozprocess import ProcessHandler
output = []
def _log(line):
# Buffer messages if more than one worker to avoid interleaving
if self.jobs > 1:
output.append(line)
else:
self.log(logging.INFO, 'python-test', {'line': line.rstrip()}, '{line}')
file_displayed_test = [] # used as boolean
def _line_handler(line):
if not file_displayed_test:
output = ('Ran' in line or 'collected' in line or
line.startswith('TEST-'))
if output:
file_displayed_test.append(True)
_log(line)
_log(test_path)
cmd = [self.virtualenv_manager.python_path, test_path]
env = os.environ.copy()
env[b'PYTHONDONTWRITEBYTECODE'] = b'1'
proc = ProcessHandler(cmd, env=env, processOutputLine=_line_handler, storeOutput=False)
proc.run()
return_code = proc.wait()
if not file_displayed_test:
_log('TEST-UNEXPECTED-FAIL | No test output (missing mozunit.main() '
'call?): {}'.format(test_path))
if self.verbose:
if return_code != 0:
_log('Test failed: {}'.format(test_path))
else:
_log('Test passed: {}'.format(test_path))
return output, return_code

View File

@ -68,9 +68,8 @@ RUN usermod -a -G video worker
RUN mkdir Documents; mkdir Pictures; mkdir Music; mkdir Videos; mkdir artifacts
# install a new enough npm, plus tc-vcs and tc-npm-cache
RUN npm install -g npm@^2.0.0 \
&& npm install -g taskcluster-vcs@2.3.12 \
# install tc-vcs and tc-npm-cache
RUN npm install -g taskcluster-vcs@2.3.12 \
&& npm install -g taskcluster-npm-cache@1.1.14 \
&& rm -rf ~/.npm
ENV PATH $PATH:/home/worker/bin

View File

@ -98,18 +98,9 @@ pip install --upgrade pip
pip install virtualenv
# Install node
tooltool_fetch <<'EOF'
[
{
"size": 5676610,
"digest": "ce27b788dfd141a5ba7674332825fc136fe2c4f49a319dd19b3a87c8fffa7a97d86cbb8535661c9a68c9122719aa969fc6a8c886458a0df9fc822eec99ed130b",
"algorithm": "sha512",
"filename": "node-v0.10.36-linux-x64.tar.gz"
}
]
EOF
tar -C /usr/local -xz --strip-components 1 < node-*.tar.gz
wget https://nodejs.org/dist/v6.9.1/node-v6.9.1-linux-x64.tar.gz
echo 'a9d9e6308931fa2a2b0cada070516d45b76d752430c31c9198933c78f8d54b17 node-v6.9.1-linux-x64.tar.gz' | sha256sum -c
tar -C /usr/local -xz --strip-components 1 < node-v6.9.1-linux-x64.tar.gz
node -v # verify
# Install custom-built Debian packages. These come from a set of repositories

View File

@ -4,7 +4,7 @@
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
const {interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/AddonManager.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");
@ -15,35 +15,51 @@ this.EXPORTED_SYMBOLS = ["addon"];
this.addon = {};
// from https://developer.mozilla.org/en-US/Add-ons/Add-on_Manager/AddonManager#AddonInstall_errors
addon.Errors = {
[-1]: "ERROR_NETWORK_FAILURE: A network error occured.",
[-2]: "ERROR_INCORECT_HASH: The downloaded file did not match the expected hash.",
[-3]: "ERROR_CORRUPT_FILE: The file appears to be corrupt.",
[-4]: "ERROR_FILE_ACCESS: There was an error accessing the filesystem.",
[-5]: "ERROR_SIGNEDSTATE_REQUIRED: The addon must be signed and isn't.",
};
function lookupError(code) {
let msg = addon.Errors[code];
return new UnknownError(msg);
}
/**
* Installs Firefox addon.
* Install a Firefox addon.
*
* If the addon is restartless, it can be used right away. Otherwise a
* restart is needed.
* If the addon is restartless, it can be used right away. Otherwise a
* restart is required.
*
* Temporary addons will automatically be unisntalled on shutdown and
* Temporary addons will automatically be uninstalled on shutdown and
* do not need to be signed, though they must be restartless.
*
* @param {string} path
* Full path to the extension package archive to be installed.
* Full path to the extension package archive.
* @param {boolean=} temporary
* Install the add-on temporarily if true.
* True to install the addon temporarily, false (default) otherwise.
*
* @return {Promise.<string>}
* Addon ID string of the newly installed addon.
* @return {Promise: string}
* Addon ID.
*
* @throws {AddonError}
* if installation fails
* @throws {UnknownError}
* If there is a problem installing the addon.
*/
addon.install = function(path, temporary = false) {
return new Promise((resolve, reject) => {
let file = new FileUtils.File(path);
let listener = {
onInstallEnded: function(install, addon) {
resolve(addon.id);
},
onInstallFailed: function(install) {
reject(new AddonError(install.error));
reject(lookupError(install.error));
},
onInstalled: function(addon) {
@ -52,23 +68,17 @@ addon.install = function(path, temporary = false) {
}
};
let file = new FileUtils.File(path);
// temporary addons
if (temp) {
AddonManager.addAddonListener(listener);
AddonManager.installTemporaryAddon(file);
}
// addons that require restart
else {
if (!temporary) {
AddonManager.getInstallForFile(file, function(aInstall) {
if (aInstall.error != 0) {
reject(new AddonError(aInstall.error));
if (aInstall.error !== 0) {
reject(lookupError(aInstall.error));
}
aInstall.addListener(listener);
aInstall.install();
});
} else {
AddonManager.addAddonListener(listener);
AddonManager.installTemporaryAddon(file);
}
});
};
@ -76,18 +86,19 @@ addon.install = function(path, temporary = false) {
/**
* Uninstall a Firefox addon.
*
* If the addon is restartless, it will be uninstalled right
* away. Otherwise a restart is necessary.
* If the addon is restartless it will be uninstalled right away.
* Otherwise, Firefox must be restarted for the change to take effect.
*
* @param {string} id
* Addon ID to uninstall.
* ID of the addon to uninstall.
*
* @return {Promise}
*/
addon.uninstall = function(id) {
return new Promise(resolve => {
AddonManager.getAddonByID(arguments[0], function(addon) {
AddonManager.getAddonByID(id, function(addon) {
addon.uninstall();
resolve();
});
});
};

View File

@ -2,44 +2,34 @@
# 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/.
from .errors import MarionetteException
from . import errors
__all__ = ['Addons', 'AddonInstallException']
__all__ = ["Addons", "AddonInstallException"]
# From https://developer.mozilla.org/en-US/Add-ons/Add-on_Manager/AddonManager#AddonInstall_errors
ADDON_INSTALL_ERRORS = {
-1: "ERROR_NETWORK_FAILURE: A network error occured.",
-2: "ERROR_INCORECT_HASH: The downloaded file did not match the expected hash.",
-3: "ERROR_CORRUPT_FILE: The file appears to be corrupt.",
-4: "ERROR_FILE_ACCESS: There was an error accessing the filesystem.",
-5: "ERROR_SIGNEDSTATE_REQUIRED: The addon must be signed and isn't.",
}
class AddonInstallException(MarionetteException):
class AddonInstallException(errors.MarionetteException):
pass
class Addons(object):
"""
An API for installing and inspecting addons during Gecko runtime. This
is a partially implemented wrapper around Gecko's `AddonManager API`_.
"""An API for installing and inspecting addons during Gecko
runtime. This is a partially implemented wrapper around Gecko's
`AddonManager API`_.
For example::
from marionette_driver.addons import Addons
addons = Addons(marionette)
addons.install('path/to/extension.xpi')
addons.install("/path/to/extension.xpi")
.. _AddonManager API: https://developer.mozilla.org/en-US/Add-ons/Add-on_Manager
"""
"""
def __init__(self, marionette):
self._mn = marionette
def install(self, path, temp=False):
"""Install an addon.
"""Install a Firefox addon.
If the addon is restartless, it can be used right away. Otherwise
a restart using :func:`~marionette_driver.marionette.Marionette.restart`
@ -49,55 +39,20 @@ class Addons(object):
:param temp: Install a temporary addon. Temporary addons will
automatically be uninstalled on shutdown and do not need
to be signed, though they must be restartless.
:returns: The addon ID string of the newly installed addon.
:raises: :exc:`AddonInstallException`
"""
with self._mn.using_context('chrome'):
addon_id, status = self._mn.execute_async_script("""
let fileUtils = Components.utils.import("resource://gre/modules/FileUtils.jsm");
let FileUtils = fileUtils.FileUtils;
Components.utils.import("resource://gre/modules/AddonManager.jsm");
let listener = {
onInstallEnded: function(install, addon) {
marionetteScriptFinished([addon.id, 0]);
},
onInstallFailed: function(install) {
marionetteScriptFinished([null, install.error]);
},
onInstalled: function(addon) {
AddonManager.removeAddonListener(listener);
marionetteScriptFinished([addon.id, 0]);
}
}
let file = new FileUtils.File(arguments[0]);
let temp = arguments[1];
if (!temp) {
AddonManager.getInstallForFile(file, function(aInstall) {
if (aInstall.error != 0) {
marionetteScriptFinished([null, aInstall.error]);
}
aInstall.addListener(listener);
aInstall.install();
});
} else {
AddonManager.addAddonListener(listener);
AddonManager.installTemporaryAddon(file);
}
""", script_args=[path, temp], debug_script=True)
if status:
if status in ADDON_INSTALL_ERRORS:
raise AddonInstallException(ADDON_INSTALL_ERRORS[status])
raise AddonInstallException(
"Addon failed to install with return code: {}".format(status))
return addon_id
body = {"path": path, "temporary": temp}
try:
return self._mn._send_message("addon:install", body, key="value")
except errors.UnknownException as e:
raise AddonInstallException(e)
def uninstall(self, addon_id):
"""Uninstall an addon.
"""Uninstall a Firefox addon.
If the addon is restartless, it will be uninstalled right away.
Otherwise a restart using :func:`~marionette_driver.marionette.Marionette.restart`
@ -109,12 +64,7 @@ class Addons(object):
Python.
:param addon_id: The addon ID string to uninstall.
"""
with self._mn.using_context('chrome'):
return self._mn.execute_async_script("""
Components.utils.import("resource://gre/modules/AddonManager.jsm");
AddonManager.getAddonByID(arguments[0], function(addon) {
addon.uninstall();
marionetteScriptFinished(0);
});
""", script_args=[addon_id])
body = {"id": addon_id}
self._mn._send_message("addon:uninstall", body)

View File

@ -18,6 +18,8 @@ XPCOMUtils.defineLazyServiceGetter(
this, "cookieManager", "@mozilla.org/cookiemanager;1", "nsICookieManager2");
Cu.import("chrome://marionette/content/accessibility.js");
Cu.import("chrome://marionette/content/action.js");
Cu.import("chrome://marionette/content/addon.js");
Cu.import("chrome://marionette/content/assert.js");
Cu.import("chrome://marionette/content/atom.js");
Cu.import("chrome://marionette/content/browser.js");
@ -2605,6 +2607,34 @@ GeckoDriver.prototype.quitApplication = function(cmd, resp) {
Services.startup.quit(flags);
};
GeckoDriver.prototype.installAddon = function(cmd, resp) {
if (this.appName != "Firefox") {
throw new UnsupportedOperationError();
}
let path = cmd.parameters.path;
let temp = cmd.parameters.temporary || false;
if (typeof path == "undefined" || typeof path != "string" ||
typeof temp != "boolean") {
throw InvalidArgumentError();
}
return addon.install(path, temp);
};
GeckoDriver.prototype.uninstallAddon = function(cmd, resp) {
if (this.appName != "Firefox") {
throw new UnsupportedOperationError();
}
let id = cmd.parameters.id;
if (typeof id == "undefined" || typeof id != "string") {
throw new InvalidArgumentError();
}
return addon.uninstall(id);
};
/**
* Helper function to convert an outerWindowID into a UID that Marionette
* tracks.
@ -2860,4 +2890,7 @@ GeckoDriver.prototype.commands = {
"localization:l10n:localizeEntity": GeckoDriver.prototype.localizeEntity,
"localization:l10n:localizeProperty": GeckoDriver.prototype.localizeProperty,
"addon:install": GeckoDriver.prototype.installAddon,
"addon:uninstall": GeckoDriver.prototype.uninstallAddon,
};

View File

@ -29,6 +29,7 @@ marionette.jar:
content/navigate.js (navigate.js)
content/l10n.js (l10n.js)
content/assert.js (assert.js)
content/addon.js (addon.js)
#ifdef ENABLE_TESTS
content/test.xul (harness/marionette/chrome/test.xul)
content/test2.xul (harness/marionette/chrome/test2.xul)

View File

@ -4,6 +4,7 @@
from __future__ import absolute_import
import errno
import os
import signal
import subprocess
@ -157,10 +158,10 @@ class ProcessHandlerMixin(object):
try:
os.killpg(pid, sig)
except BaseException as e:
# Error 3 is a "no such process" failure, which is fine because the
# ESRCH is a "no such process" failure, which is fine because the
# application might already have been terminated itself. Any other
# error would indicate a problem in killing the process.
if getattr(e, "errno", None) != 3:
if getattr(e, "errno", None) != errno.ESRCH:
print >> sys.stderr, "Could not terminate process: %s" % self.pid
raise
else:
@ -739,7 +740,7 @@ falling back to not using job objects for managing child processes"""
if isPosix:
# Keep track of the initial process group in case the process detaches itself
self.proc.pgid = os.getpgid(self.proc.pid)
self.proc.pgid = self._getpgid(self.proc.pid)
self.proc.detached_pid = None
self.processOutput(timeout=timeout, outputTimeout=outputTimeout)
@ -850,6 +851,15 @@ falling back to not using job objects for managing child processes"""
def pid(self):
return self.proc.pid
@classmethod
def _getpgid(cls, pid):
try:
return os.getpgid(pid)
except OSError as e:
# Do not raise for "No such process"
if e.errno != errno.ESRCH:
raise
def check_for_detached(self, new_pid):
"""Check if the current process has been detached and mark it appropriately.
@ -864,13 +874,7 @@ falling back to not using job objects for managing child processes"""
return
if isPosix:
new_pgid = None
try:
new_pgid = os.getpgid(new_pid)
except OSError as e:
# Do not consume errors except "No such process"
if e.errno != 3:
raise
new_pgid = self._getpgid(new_pid)
if new_pgid and new_pgid != self.proc.pgid:
self.proc.detached_pid = new_pid

View File

@ -33,6 +33,7 @@ config = {
'enable_talos_sendchange': False,
# allows triggering of test jobs when --artifact try syntax is detected on buildbot
'enable_unittest_sendchange': True,
'perfherder_extra_options': ['artifact'],
#########################################################################

View File

@ -37,6 +37,7 @@ config = {
'enable_talos_sendchange': False,
# allows triggering of test jobs when --artifact try syntax is detected on buildbot
'enable_unittest_sendchange': True,
'perfherder_extra_options': ['artifact'],
#########################################################################

View File

@ -27,6 +27,7 @@ config = {
'enable_talos_sendchange': False,
# allows triggering of test jobs when --artifact try syntax is detected on buildbot
'enable_unittest_sendchange': True,
'perfherder_extra_options': ['artifact'],
#########################################################################

View File

@ -31,6 +31,7 @@ config = {
'enable_talos_sendchange': False,
# allows triggering of test jobs when --artifact try syntax is detected on buildbot
'enable_unittest_sendchange': True,
'perfherder_extra_options': ['artifact'],
#########################################################################

View File

@ -23,6 +23,7 @@ clang.manifest",
'enable_signing': False,
'enable_talos_sendchange': False,
'enable_unittest_sendchange': False,
'perfherder_extra_options': ['static-analysis'],
#### 64 bit build specific #####
'env': {
'MOZBUILD_STATE_PATH': os.path.join(os.getcwd(), '.mozbuild'),

View File

@ -25,6 +25,7 @@ config = {
'enable_count_ctors': True,
'enable_talos_sendchange': False,
'enable_unittest_sendchange': False,
'perfherder_extra_options': ['static-analysis'],
#########################################################################

View File

@ -24,6 +24,7 @@ releng.manifest",
'platform_supports_post_upload_to_latest': False,
'enable_signing': False,
'enable_talos_sendchange': False,
'perfherder_extra_options': ['valgrind'],
#### 64 bit build specific #####
'env': {
'MOZBUILD_STATE_PATH': os.path.join(os.getcwd(), '.mozbuild'),

View File

@ -30,6 +30,7 @@ config = {
'enable_talos_sendchange': False,
# allows triggering of test jobs when --artifact try syntax is detected on buildbot
'enable_unittest_sendchange': True,
'perfherder_extra_options': ['artifact'],
#########################################################################

View File

@ -31,6 +31,7 @@ config = {
'enable_talos_sendchange': False,
# allows triggering of test jobs when --artifact try syntax is detected on buildbot
'enable_unittest_sendchange': True,
'perfherder_extra_options': ['artifact'],
#########################################################################

View File

@ -24,6 +24,7 @@ clang.manifest",
'enable_talos_sendchange': False,
'enable_unittest_sendchange': False,
'objdir': MOZ_OBJDIR,
'perfherder_extra_options': ['static-analysis'],
#### 64 bit build specific #####
'env': {
'MOZBUILD_STATE_PATH': os.path.join(os.getcwd(), '.mozbuild'),

View File

@ -49,6 +49,7 @@ config = {
# allows triggering of test jobs when --artifact try syntax is detected on buildbot
'enable_unittest_sendchange': True,
'max_build_output_timeout': 60 * 80,
'perfherder_extra_options': ['artifact'],
#########################################################################

View File

@ -53,6 +53,7 @@ config = {
# allows triggering of test jobs when --artifact try syntax is detected on buildbot
'enable_unittest_sendchange': True,
'max_build_output_timeout': 60 * 80,
'perfherder_extra_options': ['artifact'],
#########################################################################

View File

@ -21,6 +21,7 @@ config = {
clang.manifest",
'platform_supports_post_upload_to_latest': False,
'objdir': MOZ_OBJDIR,
'perfherder_extra_options': ['static-analysis'],
#### 32 bit build specific #####
'env': {
'BINSCOPE': 'C:/Program Files (x86)/Microsoft/SDL BinScope/BinScope.exe',

View File

@ -516,7 +516,7 @@ class PerfherderResourceOptionsMixin(ScriptMixin):
# This file should exist on Linux in EC2.
with open('/etc/instance_metadata.json', 'rb') as fh:
im = json.load(fh)
instance = im['aws_instance_type'].encode('ascii')
instance = im.get('aws_instance_type', u'unknown').encode('ascii')
except IOError as e:
if e.errno != errno.ENOENT:
raise
@ -528,6 +528,9 @@ class PerfherderResourceOptionsMixin(ScriptMixin):
opts.append('buildbot-%s' % instance)
# Allow configs to specify their own values.
opts.extend(self.config.get('perfherder_extra_options', []))
return opts

View File

@ -73,18 +73,17 @@ Damp.prototype = {
});
},
closeToolbox: function() {
closeToolbox: Task.async(function*() {
let tab = getActiveTab(getMostRecentBrowserWindow());
let target = devtools.TargetFactory.forTab(tab);
yield target.client.waitForRequestsToSettle();
let startRecordTimestamp = performance.now();
let closePromise = gDevTools.closeToolbox(target);
return closePromise.then(() => {
let stopRecordTimestamp = performance.now();
return {
time: stopRecordTimestamp - startRecordTimestamp
};
});
},
yield gDevTools.closeToolbox(target);
let stopRecordTimestamp = performance.now();
return {
time: stopRecordTimestamp - startRecordTimestamp
};
}),
saveHeapSnapshot: function(label) {
let tab = getActiveTab(getMostRecentBrowserWindow());

View File

@ -313,7 +313,8 @@ stage-extensions: make-stage-dir
check::
@$(topsrcdir)/mach --log-no-times python-test
$(eval cores=$(shell $(PYTHON) -c 'import multiprocessing; print(multiprocessing.cpu_count())'))
@$(topsrcdir)/mach --log-no-times python-test -j$(cores)
.PHONY: \

View File

@ -42,7 +42,6 @@ try:
except Exception:
HAVE_PSUTIL = False
from automation import Automation
from xpcshellcommandline import parser_desktop
SCRIPT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(__file__)))
@ -71,6 +70,7 @@ from manifestparser import TestManifest
from manifestparser.filters import chunk_by_slice, tags, pathprefix
from mozlog import commandline
import mozcrash
import mozfile
import mozinfo
from mozrunner.utils import get_stack_fixer_function
@ -118,6 +118,7 @@ class XPCShellTestThread(Thread):
self.appPath = kwargs.get('appPath')
self.xrePath = kwargs.get('xrePath')
self.utility_path = kwargs.get('utility_path')
self.testingModulesDir = kwargs.get('testingModulesDir')
self.debuggerInfo = kwargs.get('debuggerInfo')
self.jsDebuggerInfo = kwargs.get('jsDebuggerInfo')
@ -193,7 +194,7 @@ class XPCShellTestThread(Thread):
Simple wrapper to remove (recursively) a given directory.
On a remote system, we need to overload this to work on the remote filesystem.
"""
shutil.rmtree(dirname)
mozfile.remove(dirname)
def poll(self, proc):
"""
@ -272,9 +273,7 @@ class XPCShellTestThread(Thread):
self.log.info("%s | environment: %s" % (name, list(changedEnv)))
def killTimeout(self, proc):
Automation().killAndGetStackNoScreenshot(proc.pid,
self.appPath,
self.debuggerInfo)
mozcrash.kill_and_get_minidump(proc.pid, self.tempDir, utility_path=self.utility_path)
def postCheck(self, proc):
"""Checks for a still-running test process, kills it and fails the test if found.
@ -282,7 +281,13 @@ class XPCShellTestThread(Thread):
cause removeDir() to fail - so check for the process and kill it if needed.
"""
if proc and self.poll(proc) is None:
self.kill(proc)
if HAVE_PSUTIL:
try:
self.kill(proc)
except psutil.NoSuchProcess:
pass
else:
self.kill(proc)
message = "%s | Process still running after test!" % self.test_object['id']
if self.retry:
self.log.info(message)
@ -1181,6 +1186,7 @@ class XPCShellTests(object):
self.xpcshell = xpcshell
self.xrePath = xrePath
self.utility_path = utility_path
self.appPath = appPath
self.symbolsPath = symbolsPath
self.tempDir = os.path.normpath(tempDir or tempfile.gettempdir())
@ -1231,8 +1237,8 @@ class XPCShellTests(object):
mozinfo.update(self.mozInfo)
self.stack_fixer_function = None
if utility_path and os.path.exists(utility_path):
self.stack_fixer_function = get_stack_fixer_function(utility_path, self.symbolsPath)
if self.utility_path and os.path.exists(self.utility_path):
self.stack_fixer_function = get_stack_fixer_function(self.utility_path, self.symbolsPath)
# buildEnvironment() needs mozInfo, so we call it after mozInfo is initialized.
self.buildEnvironment()
@ -1264,6 +1270,7 @@ class XPCShellTests(object):
kwargs = {
'appPath': self.appPath,
'xrePath': self.xrePath,
'utility_path': self.utility_path,
'testingModulesDir': self.testingModulesDir,
'debuggerInfo': self.debuggerInfo,
'jsDebuggerInfo': self.jsDebuggerInfo,

Some files were not shown because too many files have changed in this diff Show More