mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 19:04:45 +00:00
Merge autoland to central, a=merge
This commit is contained in:
commit
aa3b64b5f5
@ -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",
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
@ -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")) {
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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") {
|
||||
|
@ -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]
|
||||
|
@ -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;
|
||||
}
|
13
browser/components/search/test/tooManyEnginesOffered.html
Normal file
13
browser/components/search/test/tooManyEnginesOffered.html
Normal 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>
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
},
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -569,7 +569,7 @@ private:
|
||||
|
||||
void NotifyCompositorUpdated(RefPtr<layers::KnowsCompositor> aKnowsCompositor)
|
||||
{
|
||||
mKnowsCompositor = aKnowsCompositor;
|
||||
mKnowsCompositor = aKnowsCompositor.forget();
|
||||
}
|
||||
|
||||
void DoAudioSeek();
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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()) {
|
||||
|
@ -47,7 +47,7 @@ WidevineDecryptor::SetCDM(RefPtr<CDMWrapper> aCDM, uint32_t aInstanceId)
|
||||
{
|
||||
mCDM = aCDM;
|
||||
mInstanceId = aInstanceId;
|
||||
sDecryptors[mInstanceId] = aCDM;
|
||||
sDecryptors[mInstanceId] = aCDM.forget();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -51,7 +51,7 @@ inline bool IsConditionalPunctuation(char16_t ch)
|
||||
// mozInlineSpellWordUtil::Init
|
||||
|
||||
nsresult
|
||||
mozInlineSpellWordUtil::Init(nsWeakPtr aWeakEditor)
|
||||
mozInlineSpellWordUtil::Init(const nsWeakPtr& aWeakEditor)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
};
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -83,6 +83,12 @@ VsyncChild::GetVsyncRate()
|
||||
return mVsyncRate;
|
||||
}
|
||||
|
||||
TimeDuration
|
||||
VsyncChild::VsyncRate()
|
||||
{
|
||||
return mVsyncRate;
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
VsyncChild::RecvVsyncRate(const float& aVsyncRate)
|
||||
{
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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>
|
33
layout/reftests/async-scrolling/sticky-pos-scrollable-3.html
Normal file
33
layout/reftests/async-scrolling/sticky-pos-scrollable-3.html
Normal 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>
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -3116,7 +3116,7 @@ void
|
||||
nsCookieService::GetCookieStringInternal(nsIURI *aHostURI,
|
||||
bool aIsForeign,
|
||||
bool aHttpBound,
|
||||
const NeckoOriginAttributes aOriginAttrs,
|
||||
const NeckoOriginAttributes& aOriginAttrs,
|
||||
bool aIsPrivate,
|
||||
nsCString &aCookieString)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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",
|
||||
|
@ -240,7 +240,7 @@ AltSvcMapping::TTL()
|
||||
}
|
||||
|
||||
void
|
||||
AltSvcMapping::SyncString(nsCString str)
|
||||
AltSvcMapping::SyncString(const nsCString& str)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mStorage->Put(HashKey(), str,
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
=====
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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__
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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
|
||||
|
@ -1 +1 @@
|
||||
{"is_release": false, "git_version": "fdbc9c3"}
|
||||
{"is_release": false, "git_version": "6532a74"}
|
@ -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',
|
||||
|
@ -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)
|
||||
|
@ -1,5 +1,5 @@
|
||||
[tox]
|
||||
envlist = py26,py27
|
||||
envlist = py26,py27,pypy,jython
|
||||
|
||||
[testenv]
|
||||
commands={envpython} test_futures.py []
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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'],
|
||||
#########################################################################
|
||||
|
||||
|
||||
|
@ -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'],
|
||||
#########################################################################
|
||||
|
||||
|
||||
|
@ -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'],
|
||||
#########################################################################
|
||||
|
||||
|
||||
|
@ -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'],
|
||||
#########################################################################
|
||||
|
||||
|
||||
|
@ -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'),
|
||||
|
@ -25,6 +25,7 @@ config = {
|
||||
'enable_count_ctors': True,
|
||||
'enable_talos_sendchange': False,
|
||||
'enable_unittest_sendchange': False,
|
||||
'perfherder_extra_options': ['static-analysis'],
|
||||
#########################################################################
|
||||
|
||||
|
||||
|
@ -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'),
|
||||
|
@ -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'],
|
||||
#########################################################################
|
||||
|
||||
|
||||
|
@ -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'],
|
||||
#########################################################################
|
||||
|
||||
|
||||
|
@ -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'),
|
||||
|
@ -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'],
|
||||
#########################################################################
|
||||
|
||||
|
||||
|
@ -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'],
|
||||
#########################################################################
|
||||
|
||||
|
||||
|
@ -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',
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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: \
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user