зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to inbound, a=merge
MozReview-Commit-ID: 7XpmaFg1k8N
This commit is contained in:
Коммит
a8e6d0bd2c
|
@ -467,10 +467,12 @@ pref("browser.tabs.drawInTitlebar", false);
|
|||
pref("browser.tabs.drawInTitlebar", true);
|
||||
#endif
|
||||
|
||||
// false - disable the tabbar session restore button
|
||||
// true - enable the tabbar session restore button
|
||||
// To be enabled with shield
|
||||
pref("browser.tabs.restorebutton", false);
|
||||
// 0 - Disable the tabbar session restore button.
|
||||
// 1 - Enable the tabbar session restore button.
|
||||
// 2 - Control group. The tabbar session restore button is disabled,
|
||||
// but we will record data on other session restore usage.
|
||||
// To be enabled with shield.
|
||||
pref("browser.tabs.restorebutton", 0);
|
||||
|
||||
// When tabs opened by links in other tabs via a combination of
|
||||
// browser.link.open_newwindow being set to 3 and target="_blank" etc are
|
||||
|
|
|
@ -492,7 +492,7 @@ var gSync = {
|
|||
|
||||
let broadcaster = document.getElementById("sync-status");
|
||||
broadcaster.setAttribute("syncstatus", "active");
|
||||
broadcaster.setAttribute("label", this.syncStrings.GetStringFromName("syncing2.label"));
|
||||
broadcaster.setAttribute("label", this.syncStrings.GetStringFromName("syncingtabs.label"));
|
||||
broadcaster.setAttribute("disabled", "true");
|
||||
},
|
||||
|
||||
|
|
|
@ -8285,15 +8285,22 @@ function switchToTabHavingURI(aURI, aOpenNew, aOpenParams = {}) {
|
|||
|
||||
var RestoreLastSessionObserver = {
|
||||
init() {
|
||||
let browser_tabs_restorebutton_pref = Services.prefs.getIntPref("browser.tabs.restorebutton");
|
||||
Services.telemetry.scalarSet("browser.session.restore.browser_tabs_restorebutton", browser_tabs_restorebutton_pref);
|
||||
Services.telemetry.scalarSet("browser.session.restore.browser_startup_page", Services.prefs.getIntPref("browser.startup.page"));
|
||||
if (SessionStore.canRestoreLastSession &&
|
||||
!PrivateBrowsingUtils.isWindowPrivate(window)) {
|
||||
if (Services.prefs.getBoolPref("browser.tabs.restorebutton")) {
|
||||
if (browser_tabs_restorebutton_pref == 1) {
|
||||
let {restoreTabsButton, restoreTabsButtonWrapperWidth} = gBrowser.tabContainer;
|
||||
let restoreTabsButtonWrapper = restoreTabsButton.parentNode;
|
||||
restoreTabsButtonWrapper.setAttribute("session-exists", "true");
|
||||
gBrowser.tabContainer.updateSessionRestoreVisibility();
|
||||
restoreTabsButton.style.maxWidth = `${restoreTabsButtonWrapperWidth}px`;
|
||||
gBrowser.tabContainer.addEventListener("TabOpen", this);
|
||||
Services.telemetry.scalarSet("browser.session.restore.tabbar_restore_available", true);
|
||||
restoreTabsButton.addEventListener("click", () => {
|
||||
Services.telemetry.scalarSet("browser.session.restore.tabbar_restore_clicked", true);
|
||||
});
|
||||
}
|
||||
Services.obs.addObserver(this, "sessionstore-last-session-cleared", true);
|
||||
goSetCommandEnabled("Browser:RestoreLastSession", true);
|
||||
|
|
|
@ -198,7 +198,7 @@ function checkSyncNowButton(buttonId, syncing, tooltip = null) {
|
|||
|
||||
is(remoteTabsButton.hasAttribute("disabled"), syncing, "disabled has the right value");
|
||||
if (syncing) {
|
||||
is(remoteTabsButton.getAttribute("label"), gSync.syncStrings.GetStringFromName("syncing2.label"), "label is set to the right value");
|
||||
is(remoteTabsButton.getAttribute("label"), gSync.syncStrings.GetStringFromName("syncingtabs.label"), "label is set to the right value");
|
||||
} else {
|
||||
is(remoteTabsButton.getAttribute("label"), gSync.syncStrings.GetStringFromName("syncnow.label"), "label is set to the right value");
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ skip-if = (os == 'win' && !debug) # bug 1352668
|
|||
[browser_ext_browsingData_localStorage.js]
|
||||
[browser_ext_browsingData_pluginData.js]
|
||||
[browser_ext_browsingData_serviceWorkers.js]
|
||||
[browser_ext_chrome_settings_overrides_home.js]
|
||||
[browser_ext_commands_execute_browser_action.js]
|
||||
[browser_ext_commands_execute_page_action.js]
|
||||
[browser_ext_commands_execute_sidebar_action.js]
|
||||
|
@ -157,7 +158,6 @@ skip-if = debug || asan # Bug 1354681
|
|||
[browser_ext_themes_icons.js]
|
||||
[browser_ext_themes_validation.js]
|
||||
[browser_ext_url_overrides_newtab.js]
|
||||
[browser_ext_url_overrides_home.js]
|
||||
[browser_ext_user_events.js]
|
||||
[browser_ext_webRequest.js]
|
||||
[browser_ext_webNavigation_frameId0.js]
|
||||
|
|
|
@ -13,33 +13,69 @@ const HOME_URI_2 = "http://example.com/";
|
|||
const HOME_URI_3 = "http://example.org/";
|
||||
const HOME_URI_4 = "http://example.net/";
|
||||
|
||||
const CONTROLLABLE = "controllable_by_this_extension";
|
||||
const CONTROLLED_BY_THIS = "controlled_by_this_extension";
|
||||
const CONTROLLED_BY_OTHER = "controlled_by_other_extensions";
|
||||
|
||||
add_task(async function test_multiple_extensions_overriding_home_page() {
|
||||
let defaultHomePage = Preferences.get("browser.startup.homepage");
|
||||
|
||||
let ext1 = ExtensionTestUtils.loadExtension({
|
||||
manifest: {"chrome_settings_overrides": {}},
|
||||
useAddonManager: "temporary",
|
||||
});
|
||||
function background() {
|
||||
browser.test.onMessage.addListener(async msg => {
|
||||
switch (msg) {
|
||||
case "checkHomepage":
|
||||
let homepage = await browser.browserSettings.homepageOverride.get({});
|
||||
browser.test.sendMessage("homepage", homepage);
|
||||
break;
|
||||
case "trySet":
|
||||
await browser.browserSettings.homepageOverride.set({value: "foo"});
|
||||
browser.test.sendMessage("homepageSet");
|
||||
break;
|
||||
case "tryClear":
|
||||
await browser.browserSettings.homepageOverride.clear({});
|
||||
browser.test.sendMessage("homepageCleared");
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let ext2 = ExtensionTestUtils.loadExtension({
|
||||
manifest: {"chrome_settings_overrides": {homepage: HOME_URI_2}},
|
||||
let extObj = {
|
||||
manifest: {
|
||||
"chrome_settings_overrides": {},
|
||||
permissions: ["browserSettings"],
|
||||
},
|
||||
useAddonManager: "temporary",
|
||||
});
|
||||
background,
|
||||
};
|
||||
|
||||
let ext3 = ExtensionTestUtils.loadExtension({
|
||||
manifest: {"chrome_settings_overrides": {homepage: HOME_URI_3}},
|
||||
useAddonManager: "temporary",
|
||||
});
|
||||
let ext1 = ExtensionTestUtils.loadExtension(extObj);
|
||||
|
||||
let ext4 = ExtensionTestUtils.loadExtension({
|
||||
manifest: {"chrome_settings_overrides": {homepage: HOME_URI_4}},
|
||||
useAddonManager: "temporary",
|
||||
});
|
||||
extObj.manifest.chrome_settings_overrides = {homepage: HOME_URI_2};
|
||||
let ext2 = ExtensionTestUtils.loadExtension(extObj);
|
||||
|
||||
extObj.manifest.chrome_settings_overrides = {homepage: HOME_URI_3};
|
||||
let ext3 = ExtensionTestUtils.loadExtension(extObj);
|
||||
|
||||
extObj.manifest.chrome_settings_overrides = {homepage: HOME_URI_4};
|
||||
let ext4 = ExtensionTestUtils.loadExtension(extObj);
|
||||
|
||||
extObj.manifest.chrome_settings_overrides = {};
|
||||
let ext5 = ExtensionTestUtils.loadExtension(extObj);
|
||||
|
||||
async function checkHomepageOverride(ext, expectedValue, expectedLevelOfControl) {
|
||||
ext.sendMessage("checkHomepage");
|
||||
let homepage = await ext.awaitMessage("homepage");
|
||||
is(homepage.value, expectedValue,
|
||||
`homepageOverride setting returns the expected value: ${expectedValue}.`);
|
||||
is(homepage.levelOfControl, expectedLevelOfControl,
|
||||
`homepageOverride setting returns the expected levelOfControl: ${expectedLevelOfControl}.`);
|
||||
}
|
||||
|
||||
await ext1.startup();
|
||||
|
||||
is(Preferences.get("browser.startup.homepage"), defaultHomePage,
|
||||
"Home url should be the default");
|
||||
await checkHomepageOverride(ext1, null, CONTROLLABLE);
|
||||
|
||||
// Because we are expecting the pref to change when we start or unload, we
|
||||
// need to wait on a pref change. This is because the pref management is
|
||||
|
@ -51,9 +87,22 @@ add_task(async function test_multiple_extensions_overriding_home_page() {
|
|||
ok(Preferences.get("browser.startup.homepage").endsWith(HOME_URI_2),
|
||||
"Home url should be overridden by the second extension.");
|
||||
|
||||
await checkHomepageOverride(ext1, HOME_URI_2, CONTROLLED_BY_OTHER);
|
||||
|
||||
// Verify that calling set and clear do nothing.
|
||||
ext2.sendMessage("trySet");
|
||||
await ext2.awaitMessage("homepageSet");
|
||||
await checkHomepageOverride(ext1, HOME_URI_2, CONTROLLED_BY_OTHER);
|
||||
|
||||
ext2.sendMessage("tryClear");
|
||||
await ext2.awaitMessage("homepageCleared");
|
||||
await checkHomepageOverride(ext1, HOME_URI_2, CONTROLLED_BY_OTHER);
|
||||
|
||||
// Because we are unloading an earlier extension, browser.startup.homepage won't change
|
||||
await ext1.unload();
|
||||
|
||||
await checkHomepageOverride(ext2, HOME_URI_2, CONTROLLED_BY_THIS);
|
||||
|
||||
ok(Preferences.get("browser.startup.homepage").endsWith(HOME_URI_2),
|
||||
"Home url should be overridden by the second extension.");
|
||||
|
||||
|
@ -64,12 +113,16 @@ add_task(async function test_multiple_extensions_overriding_home_page() {
|
|||
ok(Preferences.get("browser.startup.homepage").endsWith(HOME_URI_3),
|
||||
"Home url should be overridden by the third extension.");
|
||||
|
||||
await checkHomepageOverride(ext3, HOME_URI_3, CONTROLLED_BY_THIS);
|
||||
|
||||
// Because we are unloading an earlier extension, browser.startup.homepage won't change
|
||||
await ext2.unload();
|
||||
|
||||
ok(Preferences.get("browser.startup.homepage").endsWith(HOME_URI_3),
|
||||
"Home url should be overridden by the third extension.");
|
||||
|
||||
await checkHomepageOverride(ext3, HOME_URI_3, CONTROLLED_BY_THIS);
|
||||
|
||||
prefPromise = promisePrefChangeObserved("browser.startup.homepage");
|
||||
await ext4.startup();
|
||||
await prefPromise;
|
||||
|
@ -77,6 +130,7 @@ add_task(async function test_multiple_extensions_overriding_home_page() {
|
|||
ok(Preferences.get("browser.startup.homepage").endsWith(HOME_URI_4),
|
||||
"Home url should be overridden by the third extension.");
|
||||
|
||||
await checkHomepageOverride(ext3, HOME_URI_4, CONTROLLED_BY_OTHER);
|
||||
|
||||
prefPromise = promisePrefChangeObserved("browser.startup.homepage");
|
||||
await ext4.unload();
|
||||
|
@ -85,12 +139,18 @@ add_task(async function test_multiple_extensions_overriding_home_page() {
|
|||
ok(Preferences.get("browser.startup.homepage").endsWith(HOME_URI_3),
|
||||
"Home url should be overridden by the third extension.");
|
||||
|
||||
await checkHomepageOverride(ext3, HOME_URI_3, CONTROLLED_BY_THIS);
|
||||
|
||||
prefPromise = promisePrefChangeObserved("browser.startup.homepage");
|
||||
await ext3.unload();
|
||||
await prefPromise;
|
||||
|
||||
is(Preferences.get("browser.startup.homepage"), defaultHomePage,
|
||||
"Home url should be reset to default");
|
||||
|
||||
await ext5.startup();
|
||||
await checkHomepageOverride(ext5, null, CONTROLLABLE);
|
||||
await ext5.unload();
|
||||
});
|
||||
|
||||
const HOME_URI_1 = "http://example.com/";
|
|
@ -37,44 +37,92 @@ function awaitEvent(eventName) {
|
|||
}
|
||||
|
||||
add_task(async function test_multiple_extensions_overriding_newtab_page() {
|
||||
const NEWTAB_URI_1 = "webext-newtab-1.html";
|
||||
const NEWTAB_URI_2 = "webext-newtab-2.html";
|
||||
const NEWTAB_URI_2 = "webext-newtab-1.html";
|
||||
const NEWTAB_URI_3 = "webext-newtab-2.html";
|
||||
const EXT_2_ID = "ext2@tests.mozilla.org";
|
||||
const EXT_3_ID = "ext3@tests.mozilla.org";
|
||||
|
||||
const CONTROLLABLE = "controllable_by_this_extension";
|
||||
const CONTROLLED_BY_THIS = "controlled_by_this_extension";
|
||||
const CONTROLLED_BY_OTHER = "controlled_by_other_extensions";
|
||||
|
||||
function background() {
|
||||
browser.test.onMessage.addListener(async msg => {
|
||||
switch (msg) {
|
||||
case "checkNewTabPage":
|
||||
let newTabPage = await browser.browserSettings.newTabPageOverride.get({});
|
||||
browser.test.sendMessage("newTabPage", newTabPage);
|
||||
break;
|
||||
case "trySet":
|
||||
await browser.browserSettings.newTabPageOverride.set({value: "foo"});
|
||||
browser.test.sendMessage("newTabPageSet");
|
||||
break;
|
||||
case "tryClear":
|
||||
await browser.browserSettings.newTabPageOverride.clear({});
|
||||
browser.test.sendMessage("newTabPageCleared");
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function checkNewTabPageOverride(ext, expectedValue, expectedLevelOfControl) {
|
||||
ext.sendMessage("checkNewTabPage");
|
||||
let newTabPage = await ext.awaitMessage("newTabPage");
|
||||
|
||||
if (expectedValue) {
|
||||
ok(newTabPage.value.endsWith(expectedValue),
|
||||
`newTabPageOverride setting returns the expected value ending with: ${expectedValue}.`);
|
||||
} else {
|
||||
equal(newTabPage.value, expectedValue,
|
||||
`newTabPageOverride setting returns the expected value: ${expectedValue}.`);
|
||||
}
|
||||
equal(newTabPage.levelOfControl, expectedLevelOfControl,
|
||||
`newTabPageOverride setting returns the expected levelOfControl: ${expectedLevelOfControl}.`);
|
||||
}
|
||||
|
||||
let extObj = {
|
||||
manifest: {
|
||||
"chrome_url_overrides": {},
|
||||
permissions: ["browserSettings"],
|
||||
},
|
||||
useAddonManager: "temporary",
|
||||
background,
|
||||
};
|
||||
|
||||
let ext1 = ExtensionTestUtils.loadExtension(extObj);
|
||||
|
||||
extObj.manifest.chrome_url_overrides = {newtab: NEWTAB_URI_2};
|
||||
extObj.manifest.applications = {gecko: {id: EXT_2_ID}};
|
||||
let ext2 = ExtensionTestUtils.loadExtension(extObj);
|
||||
|
||||
extObj.manifest.chrome_url_overrides = {newtab: NEWTAB_URI_3};
|
||||
extObj.manifest.applications.gecko.id = EXT_3_ID;
|
||||
let ext3 = ExtensionTestUtils.loadExtension(extObj);
|
||||
|
||||
equal(aboutNewTabService.newTabURL, "about:newtab",
|
||||
"Default newtab url is about:newtab");
|
||||
|
||||
await promiseStartupManager();
|
||||
|
||||
let ext1 = ExtensionTestUtils.loadExtension({
|
||||
manifest: {"chrome_url_overrides": {}},
|
||||
useAddonManager: "temporary",
|
||||
});
|
||||
|
||||
let ext2 = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
"chrome_url_overrides": {newtab: NEWTAB_URI_1},
|
||||
applications: {
|
||||
gecko: {
|
||||
id: EXT_2_ID,
|
||||
},
|
||||
},
|
||||
},
|
||||
useAddonManager: "temporary",
|
||||
});
|
||||
|
||||
let ext3 = ExtensionTestUtils.loadExtension({
|
||||
manifest: {"chrome_url_overrides": {newtab: NEWTAB_URI_2}},
|
||||
useAddonManager: "temporary",
|
||||
});
|
||||
|
||||
await ext1.startup();
|
||||
equal(aboutNewTabService.newTabURL, "about:newtab",
|
||||
"Default newtab url is still about:newtab");
|
||||
|
||||
await checkNewTabPageOverride(ext1, null, CONTROLLABLE);
|
||||
|
||||
await ext2.startup();
|
||||
ok(aboutNewTabService.newTabURL.endsWith(NEWTAB_URI_1),
|
||||
ok(aboutNewTabService.newTabURL.endsWith(NEWTAB_URI_2),
|
||||
"Newtab url is overriden by the second extension.");
|
||||
await checkNewTabPageOverride(ext1, NEWTAB_URI_2, CONTROLLED_BY_OTHER);
|
||||
|
||||
// Verify that calling set and clear do nothing.
|
||||
ext2.sendMessage("trySet");
|
||||
await ext2.awaitMessage("newTabPageSet");
|
||||
await checkNewTabPageOverride(ext1, NEWTAB_URI_2, CONTROLLED_BY_OTHER);
|
||||
|
||||
ext2.sendMessage("tryClear");
|
||||
await ext2.awaitMessage("newTabPageCleared");
|
||||
await checkNewTabPageOverride(ext1, NEWTAB_URI_2, CONTROLLED_BY_OTHER);
|
||||
|
||||
// Disable the second extension.
|
||||
let addon = await AddonManager.getAddonByID(EXT_2_ID);
|
||||
|
@ -83,39 +131,46 @@ add_task(async function test_multiple_extensions_overriding_newtab_page() {
|
|||
await disabledPromise;
|
||||
equal(aboutNewTabService.newTabURL, "about:newtab",
|
||||
"Newtab url is about:newtab after second extension is disabled.");
|
||||
await checkNewTabPageOverride(ext1, null, CONTROLLABLE);
|
||||
|
||||
// Re-enable the second extension.
|
||||
let enabledPromise = awaitEvent("ready");
|
||||
addon.userDisabled = false;
|
||||
await enabledPromise;
|
||||
ok(aboutNewTabService.newTabURL.endsWith(NEWTAB_URI_1),
|
||||
ok(aboutNewTabService.newTabURL.endsWith(NEWTAB_URI_2),
|
||||
"Newtab url is overriden by the second extension.");
|
||||
await checkNewTabPageOverride(ext2, NEWTAB_URI_2, CONTROLLED_BY_THIS);
|
||||
|
||||
await ext1.unload();
|
||||
ok(aboutNewTabService.newTabURL.endsWith(NEWTAB_URI_1),
|
||||
ok(aboutNewTabService.newTabURL.endsWith(NEWTAB_URI_2),
|
||||
"Newtab url is still overriden by the second extension.");
|
||||
await checkNewTabPageOverride(ext2, NEWTAB_URI_2, CONTROLLED_BY_THIS);
|
||||
|
||||
await ext3.startup();
|
||||
ok(aboutNewTabService.newTabURL.endsWith(NEWTAB_URI_2),
|
||||
ok(aboutNewTabService.newTabURL.endsWith(NEWTAB_URI_3),
|
||||
"Newtab url is overriden by the third extension.");
|
||||
await checkNewTabPageOverride(ext2, NEWTAB_URI_3, CONTROLLED_BY_OTHER);
|
||||
|
||||
// Disable the second extension.
|
||||
disabledPromise = awaitEvent("shutdown");
|
||||
addon.userDisabled = true;
|
||||
await disabledPromise;
|
||||
ok(aboutNewTabService.newTabURL.endsWith(NEWTAB_URI_2),
|
||||
ok(aboutNewTabService.newTabURL.endsWith(NEWTAB_URI_3),
|
||||
"Newtab url is still overriden by the third extension.");
|
||||
await checkNewTabPageOverride(ext3, NEWTAB_URI_3, CONTROLLED_BY_THIS);
|
||||
|
||||
// Re-enable the second extension.
|
||||
enabledPromise = awaitEvent("ready");
|
||||
addon.userDisabled = false;
|
||||
await enabledPromise;
|
||||
ok(aboutNewTabService.newTabURL.endsWith(NEWTAB_URI_2),
|
||||
ok(aboutNewTabService.newTabURL.endsWith(NEWTAB_URI_3),
|
||||
"Newtab url is still overriden by the third extension.");
|
||||
await checkNewTabPageOverride(ext3, NEWTAB_URI_3, CONTROLLED_BY_THIS);
|
||||
|
||||
await ext3.unload();
|
||||
ok(aboutNewTabService.newTabURL.endsWith(NEWTAB_URI_1),
|
||||
ok(aboutNewTabService.newTabURL.endsWith(NEWTAB_URI_2),
|
||||
"Newtab url reverts to being overriden by the second extension.");
|
||||
await checkNewTabPageOverride(ext2, NEWTAB_URI_2, CONTROLLED_BY_THIS);
|
||||
|
||||
await ext2.unload();
|
||||
equal(aboutNewTabService.newTabURL, "about:newtab",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
|
||||
* vim: sw=2 ts=2 sts=2 et */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set sw=2 ts=2 sts=2 et */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
|
@ -131,7 +131,7 @@ FirefoxProfileMigrator.prototype._getResourcesInternal = function(sourceProfileD
|
|||
let favicons = getFileResource(types.HISTORY, ["favicons.sqlite"]);
|
||||
let cookies = getFileResource(types.COOKIES, ["cookies.sqlite"]);
|
||||
let passwords = getFileResource(types.PASSWORDS,
|
||||
["signons.sqlite", "logins.json", "key3.db",
|
||||
["signons.sqlite", "logins.json", "key3.db", "key4.db",
|
||||
"signedInUser.json"]);
|
||||
let formData = getFileResource(types.FORMDATA, [
|
||||
"formhistory.sqlite",
|
||||
|
|
|
@ -1018,6 +1018,11 @@ BrowserGlue.prototype = {
|
|||
|
||||
// All initial windows have opened.
|
||||
_onWindowsRestored: function BG__onWindowsRestored() {
|
||||
if (this._windowsWereRestored) {
|
||||
return;
|
||||
}
|
||||
this._windowsWereRestored = true;
|
||||
|
||||
BrowserUsageTelemetry.init();
|
||||
BrowserUITelemetry.init();
|
||||
|
||||
|
|
|
@ -3414,6 +3414,10 @@ var SessionStoreInternal = {
|
|||
tabstrip.smoothScroll = smoothScroll;
|
||||
|
||||
TelemetryStopwatch.finish("FX_SESSION_RESTORE_RESTORE_WINDOW_MS");
|
||||
if (Services.prefs.getIntPref("browser.tabs.restorebutton") != 0 ) {
|
||||
Services.telemetry.scalarAdd("browser.session.restore.number_of_tabs", winData.tabs.length);
|
||||
Services.telemetry.scalarAdd("browser.session.restore.number_of_win", 1);
|
||||
}
|
||||
|
||||
this._setWindowStateReady(aWindow);
|
||||
|
||||
|
|
|
@ -1573,6 +1573,7 @@ Function LaunchApp
|
|||
${If} $0 <> 0 ; integer comparison
|
||||
StrCpy $FirefoxLaunchCode "1"
|
||||
MessageBox MB_OK|MB_ICONQUESTION "$(WARN_MANUALLY_CLOSE_APP_LAUNCH)"
|
||||
Call SendPing
|
||||
Return
|
||||
${EndIf}
|
||||
!endif
|
||||
|
|
|
@ -1607,9 +1607,14 @@ Element::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
|||
// Unset this flag since we now really are in a document.
|
||||
UnsetFlags(NODE_FORCE_XBL_BINDINGS |
|
||||
// And clear the lazy frame construction bits.
|
||||
NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES);
|
||||
// And the restyle bits
|
||||
UnsetRestyleFlagsIfGecko();
|
||||
NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES |
|
||||
// And the restyle bits. These shouldn't even get set if we came
|
||||
// from a Servo-styled document, but they may be set if the
|
||||
// element comes from a Gecko-backed document, see bug 1394586.
|
||||
//
|
||||
// TODO(emilio): We can remove this and assert we don't have any
|
||||
// of them when we remove the old style system.
|
||||
ELEMENT_ALL_RESTYLE_FLAGS);
|
||||
} else if (IsInShadowTree()) {
|
||||
// We're not in a document, but we did get inserted into a shadow tree.
|
||||
// Since we won't have any restyle data in the document's restyle trackers,
|
||||
|
@ -1617,9 +1622,10 @@ Element::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
|||
//
|
||||
// Also clear all the other flags that are cleared above when we do get
|
||||
// inserted into a document.
|
||||
UnsetFlags(NODE_FORCE_XBL_BINDINGS |
|
||||
NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES);
|
||||
UnsetRestyleFlagsIfGecko();
|
||||
//
|
||||
// See the comment about the restyle bits above, it also applies.
|
||||
UnsetFlags(NODE_FORCE_XBL_BINDINGS | NODE_NEEDS_FRAME |
|
||||
NODE_DESCENDANTS_NEED_FRAMES | ELEMENT_ALL_RESTYLE_FLAGS);
|
||||
} else {
|
||||
// If we're not in the doc and not in a shadow tree,
|
||||
// update our subtree pointer.
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#define mozilla_mscom_VTableBuilder_h
|
||||
|
||||
#include <stdint.h>
|
||||
#include <Unknwn.h>
|
||||
#include <unknwn.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
|
|
|
@ -65,24 +65,31 @@ function test1Result() {
|
|||
let foundImport = false;
|
||||
let foundStyle = false;
|
||||
let styleFirstAddProcessor = function(event) {
|
||||
info("styleFirstAddProcessor: called");
|
||||
info("styleFirstAddProcessor: called with event "+ event.rule.cssText);
|
||||
if (event.rule.type == CSSRule.IMPORT_RULE) {
|
||||
foundImport = true;
|
||||
} else if (event.rule.type == CSSRule.STYLE_RULE) {
|
||||
foundStyle = true;
|
||||
is(foundImport, false, "Test 2: The style rule arrived before the import rule.");
|
||||
}
|
||||
}
|
||||
return foundImport;
|
||||
};
|
||||
|
||||
async function test2Setup() {
|
||||
info("test2Setup: called");
|
||||
|
||||
DOMUtils.parseStyleSheet(sheet, "@import url('imported_no_op.css'); p { color: purple; }");
|
||||
// Create a Promise to watch for two StyleRuleAdded events. The first invocation should
|
||||
// be the style rule, and the second should be the import rule. We use the same processor
|
||||
// for both events, but the processor will only return true (completing the Promise) when
|
||||
// the import rule has been processed.
|
||||
let gotAllStyleRuleAddedEvents = ContentTaskUtils.waitForEvent(document,
|
||||
"StyleRuleAdded", true, styleFirstAddProcessor);
|
||||
|
||||
DOMUtils.parseStyleSheet(sheet, "@import url('imported_no_op.css'); p {color: purple;}");
|
||||
is(sheet.cssRules.length, 2, "Test 2: Stylesheet now has 2 rules.");
|
||||
|
||||
// Await and then process the 2 events we expect to arrive.
|
||||
styleFirstAddProcessor(await ContentTaskUtils.waitForEvent(document, "StyleRuleAdded", true));
|
||||
styleFirstAddProcessor(await ContentTaskUtils.waitForEvent(document, "StyleRuleAdded", true));
|
||||
// Await and then process the events we expect to arrive.
|
||||
await gotAllStyleRuleAddedEvents;
|
||||
|
||||
is(foundStyle, true, "Test 2: Got the style rule.");
|
||||
is(foundImport, true, "Test 2: Got the import rule.");
|
||||
|
|
|
@ -2832,9 +2832,14 @@ Gecko_ReportUnexpectedCSSError(ErrorReporter* reporter,
|
|||
}
|
||||
}
|
||||
|
||||
nsDependentCSubstring paramValue(param, paramLen);
|
||||
nsAutoString wideParam = NS_ConvertUTF8toUTF16(paramValue);
|
||||
reporter->ReportUnexpectedUnescaped(message, wideParam);
|
||||
if (param) {
|
||||
nsDependentCSubstring paramValue(param, paramLen);
|
||||
nsAutoString wideParam = NS_ConvertUTF8toUTF16(paramValue);
|
||||
reporter->ReportUnexpectedUnescaped(message, wideParam);
|
||||
} else {
|
||||
reporter->ReportUnexpected(message);
|
||||
}
|
||||
|
||||
if (suffix) {
|
||||
reporter->ReportUnexpected(suffix);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset=UTF-8>
|
||||
<style>
|
||||
::-moz-selection{}
|
||||
</style>
|
||||
<script>
|
||||
window.onload = () => {
|
||||
r = document.createRange()
|
||||
document.getSelection().addRange(r)
|
||||
r.setEndAfter(document.body)
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>A</body>
|
||||
</html>
|
|
@ -199,6 +199,7 @@ load 1383981-2.html
|
|||
load 1383981-3.html
|
||||
load 1384824-1.html
|
||||
load 1384824-2.html
|
||||
load 1386773.html
|
||||
load 1387481-1.html
|
||||
load 1387499.html
|
||||
load 1388234.html
|
||||
|
|
|
@ -13,10 +13,12 @@
|
|||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::css;
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::net;
|
||||
|
||||
#define PARSING_REPETITIONS 20
|
||||
#define SETPROPERTY_REPETITIONS (1000 * 1000)
|
||||
#define GETPROPERTY_REPETITIONS (1000 * 1000)
|
||||
|
||||
#ifdef MOZ_STYLO
|
||||
|
||||
|
@ -91,4 +93,36 @@ MOZ_GTEST_BENCH(Stylo, Servo_DeclarationBlock_SetPropertyById_WithInitialSpace_B
|
|||
ServoSetPropertyByIdBench(NS_LITERAL_CSTRING(" 10px"));
|
||||
});
|
||||
|
||||
static void ServoGetPropertyValueById() {
|
||||
RefPtr<RawServoDeclarationBlock> block = Servo_DeclarationBlock_CreateEmpty().Consume();
|
||||
RefPtr<URLExtraData> data = new URLExtraData(
|
||||
NullPrincipalURI::Create(), nullptr, NullPrincipal::Create());
|
||||
NS_NAMED_LITERAL_CSTRING(css_, "10px");
|
||||
const nsACString& css = css_;
|
||||
Servo_DeclarationBlock_SetPropertyById(
|
||||
block,
|
||||
eCSSProperty_width,
|
||||
&css,
|
||||
/* is_important = */ false,
|
||||
data,
|
||||
ParsingMode::Default,
|
||||
eCompatibility_FullStandards,
|
||||
nullptr
|
||||
);
|
||||
|
||||
for (int i = 0; i < GETPROPERTY_REPETITIONS; i++) {
|
||||
DOMString value_;
|
||||
nsAString& value = value_;
|
||||
Servo_DeclarationBlock_GetPropertyValueById(
|
||||
block,
|
||||
eCSSProperty_width,
|
||||
&value
|
||||
);
|
||||
ASSERT_TRUE(value.EqualsLiteral("10px"));
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_GTEST_BENCH(Stylo, Servo_DeclarationBlock_GetPropertyById_Bench, ServoGetPropertyValueById);
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
@ -189,6 +189,7 @@ skip-if = toolkit == 'android' #bug 536603
|
|||
[test_css_escape_api.html]
|
||||
[test_css_function_mismatched_parenthesis.html]
|
||||
[test_css_loader_crossorigin_data_url.html]
|
||||
[test_css_parse_error_smoketest.html]
|
||||
[test_css_supports.html]
|
||||
[test_css_supports_variables.html]
|
||||
[test_default_bidi_css.html]
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for CSS parser reporting parsing errors with expected precision</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<style id="testbench"></style>
|
||||
<script>
|
||||
var tests = [
|
||||
{ css: "@unknown {}", error: "Unrecognized at-rule or error parsing at-rule ‘@unknown’." },
|
||||
|
||||
{ css: "x { color: invalid; }", error: "Expected color but found ‘invalid’. Error in parsing value for ‘color’. Declaration dropped." },
|
||||
|
||||
{ css: "::unknown {}", error: "Unknown pseudo-class or pseudo-element ‘unknown’. Ruleset ignored due to bad selector." },
|
||||
{ css: ":unknown {}", error: "Unknown pseudo-class or pseudo-element ‘unknown’. Ruleset ignored due to bad selector." },
|
||||
{ css: "::5 {}", error: "Expected identifier for pseudo-class or pseudo-element but found ‘5’. Ruleset ignored due to bad selector." },
|
||||
{ css: ": {}", error: "Expected identifier for pseudo-class or pseudo-element but found ‘ ’. Ruleset ignored due to bad selector." },
|
||||
|
||||
{ css: "x[a.]{}", error: "Unexpected token in attribute selector: ‘.’. Ruleset ignored due to bad selector." },
|
||||
{ css: "x[*a]{}", error: "Expected ‘|’ but found ‘a’. Ruleset ignored due to bad selector." },
|
||||
{ css: "x[a=5]{}", error: "Expected identifier or string for value in attribute selector but found ‘5’. Ruleset ignored due to bad selector." },
|
||||
{ css: "x[$] {}", error: "Expected attribute name or namespace but found ‘$’. Ruleset ignored due to bad selector." },
|
||||
{ css: "a[|5] {}", error: "Expected identifier for attribute name but found ‘5’. Ruleset ignored due to bad selector." },
|
||||
{ css: "a[x|] {}", error: "Unknown namespace prefix ‘x’. Ruleset ignored due to bad selector." },
|
||||
|
||||
{ css: "x| {}", error: "Unknown namespace prefix ‘x’. Ruleset ignored due to bad selector." },
|
||||
{ css: "a> {}", error: "Dangling combinator. Ruleset ignored due to bad selector." },
|
||||
{ css: "~ {}", error: "Selector expected. Ruleset ignored due to bad selector." },
|
||||
{ css: "| {}", error: "Expected element name or ‘*’ but found ‘ ’. Ruleset ignored due to bad selector." },
|
||||
{ css: ". {}", error: "Expected identifier for class selector but found ‘ ’. Ruleset ignored due to bad selector." },
|
||||
|
||||
{ css: ":not() {}", error: "Missing argument in negation pseudo-class ‘)’. Ruleset ignored due to bad selector." },
|
||||
];
|
||||
|
||||
var test = -1;
|
||||
function nextTest() {
|
||||
test++;
|
||||
if (test == tests.length) {
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
let {css, error} = tests[test];
|
||||
SimpleTest.expectConsoleMessages(function () { testbench.innerHTML = css },
|
||||
[{ errorMessage: error }],
|
||||
nextTest);
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
nextTest();
|
||||
</script>
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -19,14 +19,10 @@ from __future__ import absolute_import, unicode_literals
|
|||
import collections
|
||||
import os
|
||||
import sys
|
||||
import six
|
||||
from functools import wraps
|
||||
|
||||
if sys.version_info[0] == 3:
|
||||
from configparser import RawConfigParser, NoSectionError
|
||||
str_type = str
|
||||
else:
|
||||
from ConfigParser import RawConfigParser, NoSectionError
|
||||
str_type = basestring
|
||||
from six.moves.configparser import RawConfigParser, NoSectionError
|
||||
from six import string_types as str_type
|
||||
|
||||
|
||||
class ConfigException(Exception):
|
||||
|
@ -144,7 +140,7 @@ def reraise_attribute_error(func):
|
|||
return func(*args, **kwargs)
|
||||
except KeyError:
|
||||
exc_class, exc, tb = sys.exc_info()
|
||||
raise AttributeError().__class__, exc, tb
|
||||
six.reraise(AttributeError().__class__, exc, tb)
|
||||
return _
|
||||
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ setup(
|
|||
'blessings',
|
||||
'mozfile',
|
||||
'mozprocess',
|
||||
'six',
|
||||
],
|
||||
tests_require=['mock'],
|
||||
)
|
||||
|
|
|
@ -75,7 +75,7 @@ class ArchlinuxBootstrapper(StyloInstall, BaseBootstrapper):
|
|||
]
|
||||
|
||||
def __init__(self, version, dist_id, **kwargs):
|
||||
print 'Using an experimental bootstrapper for Archlinux.'
|
||||
print('Using an experimental bootstrapper for Archlinux.')
|
||||
BaseBootstrapper.__init__(self, **kwargs)
|
||||
|
||||
def install_system_packages(self):
|
||||
|
|
|
@ -45,7 +45,7 @@ class WindowsBootstrapper(BaseBootstrapper):
|
|||
if not self.which('pacman'):
|
||||
raise NotImplementedError('The Windows bootstrapper only works with msys2 with pacman. Get msys2 at '
|
||||
'http://msys2.github.io/')
|
||||
print 'Using an experimental bootstrapper for Windows.'
|
||||
print('Using an experimental bootstrapper for Windows.')
|
||||
|
||||
def which(self, name):
|
||||
return BaseBootstrapper.which(self, name + '.exe')
|
||||
|
|
|
@ -23,7 +23,7 @@ def archive_exe(pkg_dir, tagfile, sfx_package, package):
|
|||
with open(package, 'wb') as o:
|
||||
for i in [mozpath.join(tmpdir, '7zSD.sfx'), tagfile, mozpath.join(tmpdir, 'app.7z')]:
|
||||
shutil.copyfileobj(open(i, 'rb'), o)
|
||||
os.chmod(package, 0755)
|
||||
os.chmod(package, 0o0755)
|
||||
finally:
|
||||
if pkg_dir:
|
||||
shutil.move('core', pkg_dir)
|
||||
|
|
|
@ -44,7 +44,7 @@ def explode(aar, destdir):
|
|||
assets = mozpath.join(destdir, 'assets')
|
||||
try:
|
||||
os.rmdir(assets)
|
||||
except OSError, e:
|
||||
except OSError as e:
|
||||
if e.errno in (errno.ENOTEMPTY, errno.ENOENT):
|
||||
pass
|
||||
else:
|
||||
|
|
|
@ -28,4 +28,4 @@ for region, overrides in searchinfo["regionOverrides"].iteritems():
|
|||
engines.add(replacement)
|
||||
|
||||
# join() will take an iterable, not just a list.
|
||||
print '\n'.join(engines)
|
||||
print('\n'.join(engines))
|
||||
|
|
|
@ -372,9 +372,9 @@ def list_manifest(manifest_file):
|
|||
))
|
||||
return False
|
||||
for f in manifest.file_records:
|
||||
print "%s\t%s\t%s" % ("P" if f.present() else "-",
|
||||
print("%s\t%s\t%s" % ("P" if f.present() else "-",
|
||||
"V" if f.present() and f.validate() else "-",
|
||||
f.filename)
|
||||
f.filename))
|
||||
return True
|
||||
|
||||
|
||||
|
@ -674,7 +674,7 @@ def fetch_files(manifest_file, base_urls, filenames=[], cache_folder=None,
|
|||
try:
|
||||
if not os.path.exists(cache_folder):
|
||||
log.info("Creating cache in %s..." % cache_folder)
|
||||
os.makedirs(cache_folder, 0700)
|
||||
os.makedirs(cache_folder, 0o0700)
|
||||
shutil.copy(os.path.join(os.getcwd(), localfile.filename),
|
||||
os.path.join(cache_folder, localfile.digest))
|
||||
log.info("Local cache %s updated with %s" % (cache_folder,
|
||||
|
|
|
@ -59,4 +59,4 @@ def main(bin, compilation_unit):
|
|||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print main(*sys.argv[1:])
|
||||
print(main(*sys.argv[1:]))
|
||||
|
|
|
@ -76,6 +76,6 @@ def find_version(e):
|
|||
|
||||
if __name__ == '__main__':
|
||||
cxx_env = os.environ['CXX']
|
||||
print 'MOZ_LIBSTDCXX_TARGET_VERSION=%s' % find_version(cxx_env)
|
||||
print('MOZ_LIBSTDCXX_TARGET_VERSION=%s' % find_version(cxx_env))
|
||||
host_cxx_env = os.environ.get('HOST_CXX', cxx_env)
|
||||
print 'MOZ_LIBSTDCXX_HOST_VERSION=%s' % find_version(host_cxx_env)
|
||||
print('MOZ_LIBSTDCXX_HOST_VERSION=%s' % find_version(host_cxx_env))
|
||||
|
|
|
@ -30,7 +30,7 @@ def disassemble_as_iter(co):
|
|||
extended_arg = 0
|
||||
i += 2
|
||||
if op == dis.EXTENDED_ARG:
|
||||
extended_arg = arg * 65536L
|
||||
extended_arg = arg * 65536
|
||||
continue
|
||||
if op in dis.hasconst:
|
||||
yield opname, co.co_consts[arg]
|
||||
|
|
|
@ -212,7 +212,7 @@ class Doctor(object):
|
|||
fsutil_output = subprocess.check_output(command)
|
||||
status = 'GOOD, FIXED'
|
||||
desc = 'lastaccess disabled systemwide'
|
||||
except subprocess.CalledProcessError, e:
|
||||
except subprocess.CalledProcessError as e:
|
||||
desc = 'lastaccess enabled systemwide'
|
||||
if e.output.find('denied') != -1:
|
||||
status = 'BAD, FIX DENIED'
|
||||
|
|
|
@ -852,6 +852,18 @@ class BuildReader(object):
|
|||
self._execution_stack = []
|
||||
self._finder = finder
|
||||
|
||||
# Finder patterns to ignore when searching for moz.build files.
|
||||
ignores = {
|
||||
# Ignore fake moz.build files used for testing moz.build.
|
||||
'python/mozbuild/mozbuild/test',
|
||||
|
||||
# Ignore object directories.
|
||||
'obj*',
|
||||
}
|
||||
|
||||
self._relevant_mozbuild_finder = FileFinder(self.config.topsrcdir,
|
||||
ignore=ignores)
|
||||
|
||||
max_workers = cpu_count()
|
||||
self._gyp_worker_pool = ProcessPoolExecutor(max_workers=max_workers)
|
||||
self._gyp_processors = []
|
||||
|
@ -904,20 +916,10 @@ class BuildReader(object):
|
|||
# In the future, we may traverse moz.build files by looking
|
||||
# for DIRS references in the AST, even if a directory is added behind
|
||||
# a conditional. For now, just walk the filesystem.
|
||||
ignore = {
|
||||
# Ignore fake moz.build files used for testing moz.build.
|
||||
'python/mozbuild/mozbuild/test',
|
||||
|
||||
# Ignore object directories.
|
||||
'obj*',
|
||||
}
|
||||
|
||||
finder = FileFinder(self.config.topsrcdir, ignore=ignore)
|
||||
|
||||
# The root doesn't get picked up by FileFinder.
|
||||
yield 'moz.build'
|
||||
|
||||
for path, f in finder.find('**/moz.build'):
|
||||
for path, f in self._relevant_mozbuild_finder.find('**/moz.build'):
|
||||
yield path
|
||||
|
||||
def find_sphinx_variables(self):
|
||||
|
@ -1233,7 +1235,7 @@ class BuildReader(object):
|
|||
|
||||
@memoize
|
||||
def exists(path):
|
||||
return self._finder.get(path) is not None
|
||||
return self._relevant_mozbuild_finder.get(path) is not None
|
||||
|
||||
def itermozbuild(path):
|
||||
subpath = ''
|
||||
|
@ -1249,8 +1251,7 @@ class BuildReader(object):
|
|||
raise Exception('Path outside topsrcdir: %s' % path)
|
||||
path = mozpath.relpath(path, root)
|
||||
|
||||
result[path] = [p for p in itermozbuild(path)
|
||||
if exists(mozpath.join(root, p))]
|
||||
result[path] = [p for p in itermozbuild(path) if exists(p)]
|
||||
|
||||
return result
|
||||
|
||||
|
|
|
@ -372,7 +372,7 @@ class JarMaker(object):
|
|||
jarfilepath = jarfile + '.jar'
|
||||
try:
|
||||
os.makedirs(os.path.dirname(jarfilepath))
|
||||
except OSError, error:
|
||||
except OSError as error:
|
||||
if error.errno != errno.EEXIST:
|
||||
raise
|
||||
jf = ZipFile(jarfilepath, 'a', lock=True)
|
||||
|
@ -514,7 +514,7 @@ class JarMaker(object):
|
|||
# remove previous link or file
|
||||
try:
|
||||
os.remove(out)
|
||||
except OSError, e:
|
||||
except OSError as e:
|
||||
if e.errno != errno.ENOENT:
|
||||
raise
|
||||
return open(out, 'wb')
|
||||
|
@ -525,7 +525,7 @@ class JarMaker(object):
|
|||
if not os.path.isdir(outdir):
|
||||
try:
|
||||
os.makedirs(outdir)
|
||||
except OSError, error:
|
||||
except OSError as error:
|
||||
if error.errno != errno.EEXIST:
|
||||
raise
|
||||
return out
|
||||
|
@ -541,7 +541,7 @@ class JarMaker(object):
|
|||
# remove previous link or file
|
||||
try:
|
||||
os.remove(out)
|
||||
except OSError, e:
|
||||
except OSError as e:
|
||||
if e.errno != errno.ENOENT:
|
||||
raise
|
||||
if sys.platform != 'win32':
|
||||
|
|
|
@ -56,7 +56,7 @@ class Expression:
|
|||
self.__ignore_whitespace()
|
||||
self.e = self.__get_logical_or()
|
||||
if self.content:
|
||||
raise Expression.ParseError, self
|
||||
raise Expression.ParseError(self)
|
||||
|
||||
def __get_logical_or(self):
|
||||
"""
|
||||
|
@ -157,7 +157,7 @@ class Expression:
|
|||
if word_len:
|
||||
rv = Expression.__ASTLeaf('string', self.content[:word_len])
|
||||
else:
|
||||
raise Expression.ParseError, self
|
||||
raise Expression.ParseError(self)
|
||||
self.__strip(word_len)
|
||||
self.__ignore_whitespace()
|
||||
return rv
|
||||
|
@ -196,7 +196,7 @@ class Expression:
|
|||
return left and right
|
||||
elif tok[1].value == '||':
|
||||
return left or right
|
||||
raise Expression.ParseError, self
|
||||
raise Expression.ParseError(self)
|
||||
|
||||
# Mapping from token types to evaluator functions
|
||||
# Apart from (non-)equality, all these can be simple lambda forms.
|
||||
|
|
|
@ -110,7 +110,7 @@ class TestFileAvoidWrite(unittest.TestCase):
|
|||
'''
|
||||
def __call__(self, name, mode):
|
||||
if 'w' in mode:
|
||||
raise Exception, 'Unexpected open with write mode'
|
||||
raise Exception('Unexpected open with write mode')
|
||||
return MockedOpen.__call__(self, name, mode)
|
||||
|
||||
with MyMockedOpen({'file': 'content'}):
|
||||
|
|
|
@ -152,7 +152,7 @@ def ensureParentDir(path):
|
|||
if d and not os.path.exists(path):
|
||||
try:
|
||||
os.makedirs(d)
|
||||
except OSError, error:
|
||||
except OSError as error:
|
||||
if error.errno != errno.EEXIST:
|
||||
raise
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ from __future__ import absolute_import
|
|||
|
||||
import bz2
|
||||
import gzip
|
||||
import io
|
||||
import stat
|
||||
import tarfile
|
||||
|
||||
|
@ -37,7 +36,7 @@ def create_tar_from_files(fp, files):
|
|||
f = File(f)
|
||||
|
||||
ti = tarfile.TarInfo(archive_path)
|
||||
ti.mode = f.mode or 0644
|
||||
ti.mode = f.mode or 0o0644
|
||||
ti.type = tarfile.REGTYPE
|
||||
|
||||
if not ti.isreg():
|
||||
|
|
|
@ -145,13 +145,13 @@ class BaseFile(object):
|
|||
# - keep file type (e.g. S_IFREG)
|
||||
ret = stat.S_IFMT(mode)
|
||||
# - expand user read and execute permissions to everyone
|
||||
if mode & 0400:
|
||||
ret |= 0444
|
||||
if mode & 0100:
|
||||
ret |= 0111
|
||||
# - keep user write permissions
|
||||
if mode & 0200:
|
||||
ret |= 0200
|
||||
if mode & 0o0400:
|
||||
ret |= 0o0444
|
||||
if mode & 0o0100:
|
||||
ret |= 0o0111
|
||||
# - keep user write permissions
|
||||
if mode & 0o0200:
|
||||
ret |= 0o0200
|
||||
# - leave away sticky bit, setuid, setgid
|
||||
return ret
|
||||
|
||||
|
|
|
@ -401,7 +401,7 @@ class JarReader(object):
|
|||
xattr = entry['external_attr']
|
||||
# Skip directories
|
||||
if (host == 0 and xattr & 0x10) or (host == 3 and
|
||||
xattr & (040000 << 16)):
|
||||
xattr & (0o040000 << 16)):
|
||||
continue
|
||||
entries[entry['filename']] = entry
|
||||
if entry['offset'] < preload:
|
||||
|
@ -638,7 +638,7 @@ class JarWriter(object):
|
|||
# Set creator host system (upper byte of creator_version)
|
||||
# to 3 (Unix) so mode is honored when there is one.
|
||||
entry['creator_version'] |= 3 << 8
|
||||
entry['external_attr'] = (mode & 0xFFFF) << 16L
|
||||
entry['external_attr'] = (mode & 0xFFFF) << 16
|
||||
if deflater.compressed:
|
||||
entry['min_version'] = 20 # Version 2.0 supports deflated streams
|
||||
entry['general_flag'] = 2 # Max compression
|
||||
|
|
|
@ -9,8 +9,8 @@ import signal
|
|||
import sys
|
||||
import traceback
|
||||
from collections import defaultdict
|
||||
from multiprocessing import Manager, Pool, cpu_count
|
||||
from Queue import Empty
|
||||
from concurrent.futures import ProcessPoolExecutor
|
||||
from multiprocessing import cpu_count
|
||||
|
||||
from .errors import LintersNotConfigured
|
||||
from .parser import Parser
|
||||
|
@ -18,36 +18,25 @@ from .types import supported_types
|
|||
from .vcs import VCSHelper
|
||||
|
||||
|
||||
def _run_linters(queue, paths, **lintargs):
|
||||
def _run_linters(config, paths, **lintargs):
|
||||
results = defaultdict(list)
|
||||
failed = []
|
||||
|
||||
while True:
|
||||
try:
|
||||
# The astute reader may wonder what is preventing the worker from
|
||||
# grabbing the next linter config from the queue after a SIGINT.
|
||||
# Because this is a Manager.Queue(), it is itself in a child process
|
||||
# which also received SIGINT. By the time the worker gets back here,
|
||||
# the Queue is dead and IOError is raised.
|
||||
config = queue.get(False)
|
||||
except (Empty, IOError):
|
||||
return results, failed
|
||||
|
||||
func = supported_types[config['type']]
|
||||
res = func(paths, config, **lintargs) or []
|
||||
|
||||
if not isinstance(res, (list, tuple)):
|
||||
if res:
|
||||
failed.append(config['name'])
|
||||
continue
|
||||
func = supported_types[config['type']]
|
||||
res = func(paths, config, **lintargs) or []
|
||||
|
||||
if not isinstance(res, (list, tuple)):
|
||||
if res:
|
||||
failed.append(config['name'])
|
||||
else:
|
||||
for r in res:
|
||||
results[r.path].append(r)
|
||||
return results, failed
|
||||
|
||||
|
||||
def _run_worker(*args, **lintargs):
|
||||
def _run_worker(*args, **kwargs):
|
||||
try:
|
||||
return _run_linters(*args, **lintargs)
|
||||
return _run_linters(*args, **kwargs)
|
||||
except Exception:
|
||||
# multiprocessing seems to munge worker exceptions, print
|
||||
# it here so it isn't lost.
|
||||
|
@ -125,36 +114,23 @@ class LintRoller(object):
|
|||
# we're done adding to it.
|
||||
paths = map(os.path.abspath, paths)
|
||||
|
||||
# Set up multiprocessing
|
||||
m = Manager()
|
||||
queue = m.Queue()
|
||||
|
||||
for config in self.linters:
|
||||
queue.put(config)
|
||||
|
||||
num_procs = num_procs or cpu_count()
|
||||
num_procs = min(num_procs, len(self.linters))
|
||||
pool = Pool(num_procs)
|
||||
|
||||
all_results = defaultdict(list)
|
||||
workers = []
|
||||
for i in range(num_procs):
|
||||
workers.append(
|
||||
pool.apply_async(_run_worker, args=(queue, paths), kwds=self.lintargs))
|
||||
pool.close()
|
||||
|
||||
# ignore SIGINT in parent so we can still get partial results
|
||||
# from child processes. These should shutdown quickly anyway.
|
||||
orig_sigint = signal.signal(signal.SIGINT, signal.SIG_IGN)
|
||||
self.failed = []
|
||||
for worker in workers:
|
||||
# parent process blocks on worker.get()
|
||||
results, failed = worker.get()
|
||||
if failed:
|
||||
self.failed.extend(failed)
|
||||
for k, v in results.iteritems():
|
||||
all_results[k].extend(v)
|
||||
with ProcessPoolExecutor(num_procs) as executor:
|
||||
futures = [executor.submit(_run_worker, config, paths, **self.lintargs)
|
||||
for config in self.linters]
|
||||
# ignore SIGINT in parent so we can still get partial results
|
||||
# from child processes. These should shutdown quickly anyway.
|
||||
orig_sigint = signal.signal(signal.SIGINT, signal.SIG_IGN)
|
||||
self.failed = []
|
||||
for future in futures:
|
||||
results, failed = future.result()
|
||||
if failed:
|
||||
self.failed.extend(failed)
|
||||
for k, v in results.iteritems():
|
||||
all_results[k].extend(v)
|
||||
|
||||
signal.signal(signal.SIGINT, orig_sigint)
|
||||
m.shutdown()
|
||||
return all_results
|
||||
|
|
|
@ -186,9 +186,13 @@ static const char contentSandboxRules[] = R"(
|
|||
(global-name "com.apple.audio.coreaudiod")
|
||||
(global-name "com.apple.audio.audiohald"))
|
||||
|
||||
; bug 1376163
|
||||
(if (>= macosMinorVersion 13)
|
||||
(allow mach-lookup (global-name "com.apple.audio.AudioComponentRegistrar")))
|
||||
(allow mach-lookup
|
||||
; bug 1376163
|
||||
(global-name "com.apple.audio.AudioComponentRegistrar")
|
||||
; bug 1392988
|
||||
(xpc-service-name "com.apple.coremedia.videodecoder")
|
||||
(xpc-service-name "com.apple.coremedia.videoencoder")))
|
||||
|
||||
; bug 1312273
|
||||
(if (= macosMinorVersion 9)
|
||||
|
|
|
@ -13,4 +13,4 @@ lastSync2.label = Last sync: %S
|
|||
signInToSync.description = Sign In To Sync
|
||||
|
||||
syncnow.label = Sync Now
|
||||
syncing2.label = Syncing…
|
||||
syncingtabs.label = Syncing Tabs…
|
||||
|
|
|
@ -516,9 +516,9 @@ pub fn default_opts() -> Opts {
|
|||
bubble_inline_sizes_separately: false,
|
||||
show_debug_fragment_borders: false,
|
||||
show_debug_parallel_layout: false,
|
||||
enable_text_antialiasing: false,
|
||||
enable_subpixel_text_antialiasing: false,
|
||||
enable_canvas_antialiasing: false,
|
||||
enable_text_antialiasing: true,
|
||||
enable_subpixel_text_antialiasing: true,
|
||||
enable_canvas_antialiasing: true,
|
||||
trace_layout: false,
|
||||
debugger_port: None,
|
||||
devtools_port: None,
|
||||
|
|
|
@ -90,6 +90,12 @@ impl<Impl: SelectorImpl> SelectorBuilder<Impl> {
|
|||
self.simple_selectors.is_empty()
|
||||
}
|
||||
|
||||
/// Returns true if combinators have ever been pushed to this builder.
|
||||
#[inline(always)]
|
||||
pub fn has_combinators(&self) -> bool {
|
||||
!self.combinators.is_empty()
|
||||
}
|
||||
|
||||
/// Consumes the builder, producing a Selector.
|
||||
#[inline(always)]
|
||||
pub fn build(&mut self, parsed_pseudo: bool) -> ThinArc<SpecificityAndFlags, Component<Impl>> {
|
||||
|
|
|
@ -49,18 +49,23 @@ fn to_ascii_lowercase(s: &str) -> Cow<str> {
|
|||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum SelectorParseError<'i, T> {
|
||||
PseudoElementInComplexSelector,
|
||||
NoQualifiedNameInAttributeSelector,
|
||||
TooManyCompoundSelectorComponentsInNegation,
|
||||
NegationSelectorComponentNotNamespace,
|
||||
NegationSelectorComponentNotLocalName,
|
||||
NoQualifiedNameInAttributeSelector(Token<'i>),
|
||||
EmptySelector,
|
||||
DanglingCombinator,
|
||||
NonSimpleSelectorInNegation,
|
||||
UnexpectedTokenInAttributeSelector,
|
||||
PseudoElementExpectedColon,
|
||||
PseudoElementExpectedIdent,
|
||||
UnsupportedPseudoClass,
|
||||
UnexpectedTokenInAttributeSelector(Token<'i>),
|
||||
PseudoElementExpectedColon(Token<'i>),
|
||||
PseudoElementExpectedIdent(Token<'i>),
|
||||
NoIdentForPseudo(Token<'i>),
|
||||
UnsupportedPseudoClassOrElement(CowRcStr<'i>),
|
||||
UnexpectedIdent(CowRcStr<'i>),
|
||||
ExpectedNamespace(CowRcStr<'i>),
|
||||
ExpectedBarInAttr(Token<'i>),
|
||||
BadValueInAttr(Token<'i>),
|
||||
InvalidQualNameInAttr(Token<'i>),
|
||||
ExplicitNamespaceUnexpectedToken(Token<'i>),
|
||||
ClassNeedsIdent(Token<'i>),
|
||||
EmptyNegation,
|
||||
Custom(T),
|
||||
}
|
||||
|
||||
|
@ -136,7 +141,7 @@ pub trait Parser<'i> {
|
|||
fn parse_non_ts_pseudo_class(&self, name: CowRcStr<'i>)
|
||||
-> Result<<Self::Impl as SelectorImpl>::NonTSPseudoClass,
|
||||
ParseError<'i, SelectorParseError<'i, Self::Error>>> {
|
||||
Err(ParseError::Custom(SelectorParseError::UnexpectedIdent(name)))
|
||||
Err(ParseError::Custom(SelectorParseError::UnsupportedPseudoClassOrElement(name)))
|
||||
}
|
||||
|
||||
fn parse_non_ts_functional_pseudo_class<'t>
|
||||
|
@ -144,20 +149,20 @@ pub trait Parser<'i> {
|
|||
-> Result<<Self::Impl as SelectorImpl>::NonTSPseudoClass,
|
||||
ParseError<'i, SelectorParseError<'i, Self::Error>>>
|
||||
{
|
||||
Err(ParseError::Custom(SelectorParseError::UnexpectedIdent(name)))
|
||||
Err(ParseError::Custom(SelectorParseError::UnsupportedPseudoClassOrElement(name)))
|
||||
}
|
||||
|
||||
fn parse_pseudo_element(&self, name: CowRcStr<'i>)
|
||||
-> Result<<Self::Impl as SelectorImpl>::PseudoElement,
|
||||
ParseError<'i, SelectorParseError<'i, Self::Error>>> {
|
||||
Err(ParseError::Custom(SelectorParseError::UnexpectedIdent(name)))
|
||||
Err(ParseError::Custom(SelectorParseError::UnsupportedPseudoClassOrElement(name)))
|
||||
}
|
||||
|
||||
fn parse_functional_pseudo_element<'t>
|
||||
(&self, name: CowRcStr<'i>, _arguments: &mut CssParser<'i, 't>)
|
||||
-> Result<<Self::Impl as SelectorImpl>::PseudoElement,
|
||||
ParseError<'i, SelectorParseError<'i, Self::Error>>> {
|
||||
Err(ParseError::Custom(SelectorParseError::UnexpectedIdent(name)))
|
||||
Err(ParseError::Custom(SelectorParseError::UnsupportedPseudoClassOrElement(name)))
|
||||
}
|
||||
|
||||
fn default_namespace(&self) -> Option<<Self::Impl as SelectorImpl>::NamespaceUrl> {
|
||||
|
@ -1041,7 +1046,12 @@ fn parse_selector<'i, 't, P, E, Impl>(
|
|||
let mut parsed_pseudo_element;
|
||||
'outer_loop: loop {
|
||||
// Parse a sequence of simple selectors.
|
||||
parsed_pseudo_element = parse_compound_selector(parser, input, &mut builder)?;
|
||||
parsed_pseudo_element = match parse_compound_selector(parser, input, &mut builder) {
|
||||
Ok(result) => result,
|
||||
Err(ParseError::Custom(SelectorParseError::EmptySelector)) if builder.has_combinators() =>
|
||||
return Err(SelectorParseError::DanglingCombinator.into()),
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
if parsed_pseudo_element {
|
||||
break;
|
||||
}
|
||||
|
@ -1106,9 +1116,10 @@ fn parse_type_selector<'i, 't, P, E, Impl, S>(parser: &P, input: &mut CssParser<
|
|||
Impl: SelectorImpl,
|
||||
S: Push<Component<Impl>>,
|
||||
{
|
||||
match parse_qualified_name(parser, input, /* in_attr_selector = */ false)? {
|
||||
None => Ok(false),
|
||||
Some((namespace, local_name)) => {
|
||||
match parse_qualified_name(parser, input, /* in_attr_selector = */ false) {
|
||||
Err(ParseError::Basic(BasicParseError::EndOfInput)) |
|
||||
Ok(OptionalQName::None(_)) => Ok(false),
|
||||
Ok(OptionalQName::Some(namespace, local_name)) => {
|
||||
match namespace {
|
||||
QNamePrefix::ImplicitAnyNamespace => {}
|
||||
QNamePrefix::ImplicitDefaultNamespace(url) => {
|
||||
|
@ -1157,6 +1168,7 @@ fn parse_type_selector<'i, 't, P, E, Impl, S>(parser: &P, input: &mut CssParser<
|
|||
}
|
||||
Ok(true)
|
||||
}
|
||||
Err(e) => Err(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1176,13 +1188,19 @@ enum QNamePrefix<Impl: SelectorImpl> {
|
|||
ExplicitNamespace(Impl::NamespacePrefix, Impl::NamespaceUrl), // `prefix|foo`
|
||||
}
|
||||
|
||||
enum OptionalQName<'i, Impl: SelectorImpl> {
|
||||
Some(QNamePrefix<Impl>, Option<CowRcStr<'i>>),
|
||||
None(Token<'i>),
|
||||
}
|
||||
|
||||
/// * `Err(())`: Invalid selector, abort
|
||||
/// * `Ok(None)`: Not a simple selector, could be something else. `input` was not consumed.
|
||||
/// * `Ok(Some((namespace, local_name)))`: `None` for the local name means a `*` universal selector
|
||||
/// * `Ok(None(token))`: Not a simple selector, could be something else. `input` was not consumed,
|
||||
/// but the token is still returned.
|
||||
/// * `Ok(Some(namespace, local_name))`: `None` for the local name means a `*` universal selector
|
||||
fn parse_qualified_name<'i, 't, P, E, Impl>
|
||||
(parser: &P, input: &mut CssParser<'i, 't>,
|
||||
in_attr_selector: bool)
|
||||
-> Result<Option<(QNamePrefix<Impl>, Option<CowRcStr<'i>>)>,
|
||||
-> Result<OptionalQName<'i, Impl>,
|
||||
ParseError<'i, SelectorParseError<'i, E>>>
|
||||
where P: Parser<'i, Impl=Impl, Error=E>, Impl: SelectorImpl
|
||||
{
|
||||
|
@ -1191,18 +1209,19 @@ fn parse_qualified_name<'i, 't, P, E, Impl>
|
|||
Some(url) => QNamePrefix::ImplicitDefaultNamespace(url),
|
||||
None => QNamePrefix::ImplicitAnyNamespace,
|
||||
};
|
||||
Ok(Some((namespace, local_name)))
|
||||
Ok(OptionalQName::Some(namespace, local_name))
|
||||
};
|
||||
|
||||
let explicit_namespace = |input: &mut CssParser<'i, 't>, namespace| {
|
||||
match input.next_including_whitespace() {
|
||||
Ok(&Token::Delim('*')) if !in_attr_selector => {
|
||||
Ok(Some((namespace, None)))
|
||||
Ok(OptionalQName::Some(namespace, None))
|
||||
},
|
||||
Ok(&Token::Ident(ref local_name)) => {
|
||||
Ok(Some((namespace, Some(local_name.clone()))))
|
||||
Ok(OptionalQName::Some(namespace, Some(local_name.clone())))
|
||||
},
|
||||
Ok(t) => Err(ParseError::Basic(BasicParseError::UnexpectedToken(t.clone()))),
|
||||
Ok(t) if in_attr_selector => Err(SelectorParseError::InvalidQualNameInAttr(t.clone()).into()),
|
||||
Ok(t) => Err(SelectorParseError::ExplicitNamespaceUnexpectedToken(t.clone()).into()),
|
||||
Err(e) => Err(ParseError::Basic(e)),
|
||||
}
|
||||
};
|
||||
|
@ -1223,7 +1242,7 @@ fn parse_qualified_name<'i, 't, P, E, Impl>
|
|||
_ => {
|
||||
input.reset(&after_ident);
|
||||
if in_attr_selector {
|
||||
Ok(Some((QNamePrefix::ImplicitNoNamespace, Some(value))))
|
||||
Ok(OptionalQName::Some(QNamePrefix::ImplicitNoNamespace, Some(value)))
|
||||
} else {
|
||||
default_namespace(Some(value))
|
||||
}
|
||||
|
@ -1241,7 +1260,7 @@ fn parse_qualified_name<'i, 't, P, E, Impl>
|
|||
input.reset(&after_star);
|
||||
if in_attr_selector {
|
||||
match result {
|
||||
Ok(t) => Err(ParseError::Basic(BasicParseError::UnexpectedToken(t))),
|
||||
Ok(t) => Err(SelectorParseError::ExpectedBarInAttr(t).into()),
|
||||
Err(e) => Err(ParseError::Basic(e)),
|
||||
}
|
||||
} else {
|
||||
|
@ -1253,9 +1272,13 @@ fn parse_qualified_name<'i, 't, P, E, Impl>
|
|||
Ok(Token::Delim('|')) => {
|
||||
explicit_namespace(input, QNamePrefix::ExplicitNoNamespace)
|
||||
}
|
||||
_ => {
|
||||
Ok(t) => {
|
||||
input.reset(&start);
|
||||
Ok(None)
|
||||
Ok(OptionalQName::None(t))
|
||||
}
|
||||
Err(e) => {
|
||||
input.reset(&start);
|
||||
Err(e.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1269,9 +1292,10 @@ fn parse_attribute_selector<'i, 't, P, E, Impl>(parser: &P, input: &mut CssParse
|
|||
let namespace;
|
||||
let local_name;
|
||||
match parse_qualified_name(parser, input, /* in_attr_selector = */ true)? {
|
||||
None => return Err(ParseError::Custom(SelectorParseError::NoQualifiedNameInAttributeSelector)),
|
||||
Some((_, None)) => unreachable!(),
|
||||
Some((ns, Some(ln))) => {
|
||||
OptionalQName::None(t) =>
|
||||
return Err(ParseError::Custom(SelectorParseError::NoQualifiedNameInAttributeSelector(t))),
|
||||
OptionalQName::Some(_, None) => unreachable!(),
|
||||
OptionalQName::Some(ns, Some(ln)) => {
|
||||
local_name = ln;
|
||||
namespace = match ns {
|
||||
QNamePrefix::ImplicitNoNamespace |
|
||||
|
@ -1292,10 +1316,7 @@ fn parse_attribute_selector<'i, 't, P, E, Impl>(parser: &P, input: &mut CssParse
|
|||
}
|
||||
}
|
||||
|
||||
let operator;
|
||||
let value;
|
||||
let never_matches;
|
||||
match input.next() {
|
||||
let operator = match input.next() {
|
||||
// [foo]
|
||||
Err(_) => {
|
||||
let local_name_lower = to_ascii_lowercase(&local_name).as_ref().into();
|
||||
|
@ -1317,43 +1338,38 @@ fn parse_attribute_selector<'i, 't, P, E, Impl>(parser: &P, input: &mut CssParse
|
|||
}
|
||||
|
||||
// [foo=bar]
|
||||
Ok(&Token::Delim('=')) => {
|
||||
value = input.expect_ident_or_string()?.clone();
|
||||
never_matches = false;
|
||||
operator = AttrSelectorOperator::Equal;
|
||||
}
|
||||
Ok(&Token::Delim('=')) => AttrSelectorOperator::Equal,
|
||||
// [foo~=bar]
|
||||
Ok(&Token::IncludeMatch) => {
|
||||
value = input.expect_ident_or_string()?.clone();
|
||||
never_matches = value.is_empty() || value.contains(SELECTOR_WHITESPACE);
|
||||
operator = AttrSelectorOperator::Includes;
|
||||
}
|
||||
Ok(&Token::IncludeMatch) => AttrSelectorOperator::Includes,
|
||||
// [foo|=bar]
|
||||
Ok(&Token::DashMatch) => {
|
||||
value = input.expect_ident_or_string()?.clone();
|
||||
never_matches = false;
|
||||
operator = AttrSelectorOperator::DashMatch;
|
||||
}
|
||||
Ok(&Token::DashMatch) => AttrSelectorOperator::DashMatch,
|
||||
// [foo^=bar]
|
||||
Ok(&Token::PrefixMatch) => {
|
||||
value = input.expect_ident_or_string()?.clone();
|
||||
never_matches = value.is_empty();
|
||||
operator = AttrSelectorOperator::Prefix;
|
||||
}
|
||||
Ok(&Token::PrefixMatch) => AttrSelectorOperator::Prefix,
|
||||
// [foo*=bar]
|
||||
Ok(&Token::SubstringMatch) => {
|
||||
value = input.expect_ident_or_string()?.clone();
|
||||
never_matches = value.is_empty();
|
||||
operator = AttrSelectorOperator::Substring;
|
||||
}
|
||||
Ok(&Token::SubstringMatch) => AttrSelectorOperator::Substring,
|
||||
// [foo$=bar]
|
||||
Ok(&Token::SuffixMatch) => {
|
||||
value = input.expect_ident_or_string()?.clone();
|
||||
never_matches = value.is_empty();
|
||||
operator = AttrSelectorOperator::Suffix;
|
||||
Ok(&Token::SuffixMatch) => AttrSelectorOperator::Suffix,
|
||||
Ok(t) => return Err(SelectorParseError::UnexpectedTokenInAttributeSelector(t.clone()).into())
|
||||
};
|
||||
|
||||
let value = match input.expect_ident_or_string() {
|
||||
Ok(t) => t.clone(),
|
||||
Err(BasicParseError::UnexpectedToken(t)) =>
|
||||
return Err(SelectorParseError::BadValueInAttr(t.clone()).into()),
|
||||
Err(e) => return Err(e.into()),
|
||||
};
|
||||
let never_matches = match operator {
|
||||
AttrSelectorOperator::Equal |
|
||||
AttrSelectorOperator::DashMatch => false,
|
||||
|
||||
AttrSelectorOperator::Includes => {
|
||||
value.is_empty() || value.contains(SELECTOR_WHITESPACE)
|
||||
}
|
||||
_ => return Err(SelectorParseError::UnexpectedTokenInAttributeSelector.into())
|
||||
}
|
||||
|
||||
AttrSelectorOperator::Prefix |
|
||||
AttrSelectorOperator::Substring |
|
||||
AttrSelectorOperator::Suffix => value.is_empty()
|
||||
};
|
||||
|
||||
let mut case_sensitivity = parse_attribute_flags(input)?;
|
||||
|
||||
|
@ -1429,13 +1445,19 @@ fn parse_negation<'i, 't, P, E, Impl>(parser: &P,
|
|||
|
||||
// Get exactly one simple selector. The parse logic in the caller will verify
|
||||
// that there are no trailing tokens after we're done.
|
||||
if !parse_type_selector(parser, input, &mut sequence)? {
|
||||
let is_type_sel = match parse_type_selector(parser, input, &mut sequence) {
|
||||
Ok(result) => result,
|
||||
Err(ParseError::Basic(BasicParseError::EndOfInput)) =>
|
||||
return Err(SelectorParseError::EmptyNegation.into()),
|
||||
Err(e) => return Err(e.into()),
|
||||
};
|
||||
if !is_type_sel {
|
||||
match parse_one_simple_selector(parser, input, /* inside_negation = */ true)? {
|
||||
Some(SimpleSelectorParseResult::SimpleSelector(s)) => {
|
||||
sequence.push(s);
|
||||
},
|
||||
None => {
|
||||
return Err(ParseError::Custom(SelectorParseError::EmptySelector));
|
||||
return Err(ParseError::Custom(SelectorParseError::EmptyNegation));
|
||||
},
|
||||
Some(SimpleSelectorParseResult::PseudoElement(_)) => {
|
||||
return Err(ParseError::Custom(SelectorParseError::NonSimpleSelectorInNegation));
|
||||
|
@ -1492,20 +1514,21 @@ fn parse_compound_selector<'i, 't, P, E, Impl>(
|
|||
match input.next_including_whitespace() {
|
||||
Ok(&Token::Colon) => {},
|
||||
Ok(&Token::WhiteSpace(_)) | Err(_) => break,
|
||||
_ => return Err(SelectorParseError::PseudoElementExpectedColon.into()),
|
||||
Ok(t) =>
|
||||
return Err(SelectorParseError::PseudoElementExpectedColon(t.clone()).into()),
|
||||
}
|
||||
|
||||
// TODO(emilio): Functional pseudo-classes too?
|
||||
// We don't need it for now.
|
||||
let name = match input.next_including_whitespace() {
|
||||
Ok(&Token::Ident(ref name)) => name.clone(),
|
||||
_ => return Err(SelectorParseError::PseudoElementExpectedIdent.into()),
|
||||
let name = match input.next_including_whitespace()? {
|
||||
&Token::Ident(ref name) => name.clone(),
|
||||
t => return Err(SelectorParseError::NoIdentForPseudo(t.clone()).into()),
|
||||
};
|
||||
|
||||
let pseudo_class =
|
||||
P::parse_non_ts_pseudo_class(parser, name)?;
|
||||
P::parse_non_ts_pseudo_class(parser, name.clone())?;
|
||||
if !p.supports_pseudo_class(&pseudo_class) {
|
||||
return Err(SelectorParseError::UnsupportedPseudoClass.into());
|
||||
return Err(SelectorParseError::UnsupportedPseudoClassOrElement(name).into());
|
||||
}
|
||||
state_selectors.push(Component::NonTSPseudoClass(pseudo_class));
|
||||
}
|
||||
|
@ -1604,7 +1627,7 @@ fn parse_one_simple_selector<'i, 't, P, E, Impl>(parser: &P,
|
|||
let class = Component::Class(class.as_ref().into());
|
||||
Ok(Some(SimpleSelectorParseResult::SimpleSelector(class)))
|
||||
}
|
||||
ref t => Err(ParseError::Basic(BasicParseError::UnexpectedToken(t.clone()))),
|
||||
ref t => Err(SelectorParseError::ClassNeedsIdent(t.clone()).into()),
|
||||
}
|
||||
}
|
||||
Ok(Token::SquareBracketBlock) => {
|
||||
|
@ -1619,7 +1642,7 @@ fn parse_one_simple_selector<'i, 't, P, E, Impl>(parser: &P,
|
|||
let (name, is_functional) = match next_token {
|
||||
Token::Ident(name) => (name, false),
|
||||
Token::Function(name) => (name, true),
|
||||
t => return Err(ParseError::Basic(BasicParseError::UnexpectedToken(t))),
|
||||
t => return Err(SelectorParseError::PseudoElementExpectedIdent(t).into()),
|
||||
};
|
||||
let is_pseudo_element = !is_single_colon ||
|
||||
P::is_pseudo_element_allows_single_colon(&name);
|
||||
|
|
|
@ -308,7 +308,7 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
|
|||
keyword: [$(($k_css:expr, $k_name:ident, $k_gecko_type:tt, $k_state:tt, $k_flags:tt),)*]) => {
|
||||
match_ignore_ascii_case! { &name,
|
||||
$($css => NonTSPseudoClass::$name,)*
|
||||
_ => return Err(::selectors::parser::SelectorParseError::UnexpectedIdent(
|
||||
_ => return Err(::selectors::parser::SelectorParseError::UnsupportedPseudoClassOrElement(
|
||||
name.clone()).into())
|
||||
}
|
||||
}
|
||||
|
@ -317,7 +317,7 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
|
|||
if self.is_pseudo_class_enabled(&pseudo_class) {
|
||||
Ok(pseudo_class)
|
||||
} else {
|
||||
Err(SelectorParseError::UnexpectedIdent(name).into())
|
||||
Err(SelectorParseError::UnsupportedPseudoClassOrElement(name).into())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -354,7 +354,7 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
|
|||
}
|
||||
NonTSPseudoClass::MozAny(selectors.into_boxed_slice())
|
||||
}
|
||||
_ => return Err(SelectorParseError::UnexpectedIdent(name.clone()).into())
|
||||
_ => return Err(SelectorParseError::UnsupportedPseudoClassOrElement(name.clone()).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -362,13 +362,13 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
|
|||
if self.is_pseudo_class_enabled(&pseudo_class) {
|
||||
Ok(pseudo_class)
|
||||
} else {
|
||||
Err(SelectorParseError::UnexpectedIdent(name).into())
|
||||
Err(SelectorParseError::UnsupportedPseudoClassOrElement(name).into())
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_pseudo_element(&self, name: CowRcStr<'i>) -> Result<PseudoElement, ParseError<'i>> {
|
||||
PseudoElement::from_slice(&name, self.in_user_agent_stylesheet())
|
||||
.ok_or(SelectorParseError::UnexpectedIdent(name.clone()).into())
|
||||
.ok_or(SelectorParseError::UnsupportedPseudoClassOrElement(name.clone()).into())
|
||||
}
|
||||
|
||||
fn parse_functional_pseudo_element<'t>(&self, name: CowRcStr<'i>,
|
||||
|
@ -392,7 +392,7 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
|
|||
return Ok(pseudo);
|
||||
}
|
||||
}
|
||||
Err(SelectorParseError::UnexpectedIdent(name.clone()).into())
|
||||
Err(SelectorParseError::UnsupportedPseudoClassOrElement(name.clone()).into())
|
||||
}
|
||||
|
||||
fn default_namespace(&self) -> Option<Namespace> {
|
||||
|
|
|
@ -257,7 +257,7 @@ impl DocumentCascadeData {
|
|||
Some(pseudo) => {
|
||||
origin_cascade_data
|
||||
.pseudos_map
|
||||
.get_or_insert_with(&pseudo.canonical(), SelectorMap::new)
|
||||
.get_or_insert_with(&pseudo.canonical(), || Box::new(SelectorMap::new()))
|
||||
.expect("Unexpected tree pseudo-element?")
|
||||
}
|
||||
};
|
||||
|
@ -1797,7 +1797,11 @@ struct CascadeData {
|
|||
|
||||
/// Rules from stylesheets at this `CascadeData`'s origin that correspond
|
||||
/// to a given pseudo-element.
|
||||
pseudos_map: PerPseudoElementMap<SelectorMap<Rule>>,
|
||||
///
|
||||
/// FIXME(emilio): There are a bunch of wasted entries here in practice.
|
||||
/// Figure out a good way to do a `PerNonAnonBox` and `PerAnonBox` (for
|
||||
/// `precomputed_values_for_pseudo`) without duplicating a lot of code.
|
||||
pseudos_map: PerPseudoElementMap<Box<SelectorMap<Rule>>>,
|
||||
|
||||
/// A map with all the animations at this `CascadeData`'s origin, indexed
|
||||
/// by name.
|
||||
|
@ -1875,7 +1879,7 @@ impl CascadeData {
|
|||
#[inline]
|
||||
fn borrow_for_pseudo(&self, pseudo: Option<&PseudoElement>) -> Option<&SelectorMap<Rule>> {
|
||||
match pseudo {
|
||||
Some(pseudo) => self.pseudos_map.get(&pseudo.canonical()),
|
||||
Some(pseudo) => self.pseudos_map.get(&pseudo.canonical()).map(|p| &**p),
|
||||
None => Some(&self.element_map),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -161,7 +161,7 @@ fn token_to_str<'a>(t: Token<'a>) -> String {
|
|||
format!("{}{}", i, escape_css_ident(&*unit)),
|
||||
Token::Dimension { value, ref unit, .. } =>
|
||||
format!("{}{}", value, escape_css_ident(&*unit)),
|
||||
Token::WhiteSpace(_) => "whitespace".into(),
|
||||
Token::WhiteSpace(s) => s.into(),
|
||||
Token::Comment(_) => "comment".into(),
|
||||
Token::Colon => ":".into(),
|
||||
Token::Semicolon => ";".into(),
|
||||
|
@ -194,7 +194,7 @@ enum Action {
|
|||
|
||||
trait ErrorHelpers<'a> {
|
||||
fn error_data(self) -> (CowRcStr<'a>, ParseError<'a>);
|
||||
fn error_params(self) -> (ErrorString<'a>, Option<ErrorString<'a>>);
|
||||
fn error_params(self) -> ErrorParams<'a>;
|
||||
fn to_gecko_message(&self) -> (Option<&'static [u8]>, &'static [u8], Action);
|
||||
}
|
||||
|
||||
|
@ -203,7 +203,9 @@ fn extract_error_param<'a>(err: ParseError<'a>) -> Option<ErrorString<'a>> {
|
|||
CssParseError::Basic(BasicParseError::UnexpectedToken(t)) =>
|
||||
ErrorString::UnexpectedToken(t),
|
||||
|
||||
CssParseError::Basic(BasicParseError::AtRuleInvalid(i)) =>
|
||||
CssParseError::Basic(BasicParseError::AtRuleInvalid(i)) |
|
||||
CssParseError::Custom(SelectorParseError::Custom(
|
||||
StyleParseError::UnsupportedAtRule(i))) =>
|
||||
ErrorString::Snippet(format!("@{}", escape_css_ident(&i)).into()),
|
||||
|
||||
CssParseError::Custom(SelectorParseError::Custom(
|
||||
|
@ -214,9 +216,6 @@ fn extract_error_param<'a>(err: ParseError<'a>) -> Option<ErrorString<'a>> {
|
|||
CssParseError::Custom(SelectorParseError::UnexpectedIdent(ident)) =>
|
||||
ErrorString::Ident(ident),
|
||||
|
||||
CssParseError::Custom(SelectorParseError::ExpectedNamespace(namespace)) =>
|
||||
ErrorString::Ident(namespace),
|
||||
|
||||
CssParseError::Custom(SelectorParseError::Custom(
|
||||
StyleParseError::PropertyDeclaration(
|
||||
PropertyDeclarationParseError::UnknownProperty(property)))) =>
|
||||
|
@ -236,17 +235,54 @@ fn extract_value_error_param<'a>(err: ValueParseError<'a>) -> ErrorString<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
struct ErrorParams<'a> {
|
||||
prefix_param: Option<ErrorString<'a>>,
|
||||
main_param: Option<ErrorString<'a>>,
|
||||
}
|
||||
|
||||
/// If an error parameter is present in the given error, return it. Additionally return
|
||||
/// a second parameter if it exists, for use in the prefix for the eventual error message.
|
||||
fn extract_error_params<'a>(err: ParseError<'a>) -> Option<(ErrorString<'a>, Option<ErrorString<'a>>)> {
|
||||
match err {
|
||||
fn extract_error_params<'a>(err: ParseError<'a>) -> Option<ErrorParams<'a>> {
|
||||
let (main, prefix) = match err {
|
||||
CssParseError::Custom(SelectorParseError::Custom(
|
||||
StyleParseError::PropertyDeclaration(
|
||||
PropertyDeclarationParseError::InvalidValue(property, Some(e))))) =>
|
||||
Some((ErrorString::Snippet(property.into()), Some(extract_value_error_param(e)))),
|
||||
(Some(ErrorString::Snippet(property.into())), Some(extract_value_error_param(e))),
|
||||
|
||||
err => extract_error_param(err).map(|e| (e, None)),
|
||||
}
|
||||
CssParseError::Custom(SelectorParseError::UnexpectedTokenInAttributeSelector(t)) |
|
||||
CssParseError::Custom(SelectorParseError::BadValueInAttr(t)) |
|
||||
CssParseError::Custom(SelectorParseError::ExpectedBarInAttr(t)) |
|
||||
CssParseError::Custom(SelectorParseError::NoQualifiedNameInAttributeSelector(t)) |
|
||||
CssParseError::Custom(SelectorParseError::InvalidQualNameInAttr(t)) |
|
||||
CssParseError::Custom(SelectorParseError::ExplicitNamespaceUnexpectedToken(t)) |
|
||||
CssParseError::Custom(SelectorParseError::PseudoElementExpectedIdent(t)) |
|
||||
CssParseError::Custom(SelectorParseError::NoIdentForPseudo(t)) |
|
||||
CssParseError::Custom(SelectorParseError::ClassNeedsIdent(t)) |
|
||||
CssParseError::Custom(SelectorParseError::PseudoElementExpectedColon(t)) =>
|
||||
(None, Some(ErrorString::UnexpectedToken(t))),
|
||||
|
||||
CssParseError::Custom(SelectorParseError::ExpectedNamespace(namespace)) =>
|
||||
(None, Some(ErrorString::Ident(namespace))),
|
||||
|
||||
CssParseError::Custom(SelectorParseError::UnsupportedPseudoClassOrElement(p)) =>
|
||||
(None, Some(ErrorString::Ident(p))),
|
||||
|
||||
CssParseError::Custom(SelectorParseError::EmptySelector) |
|
||||
CssParseError::Custom(SelectorParseError::DanglingCombinator) =>
|
||||
(None, None),
|
||||
|
||||
CssParseError::Custom(SelectorParseError::EmptyNegation) =>
|
||||
(None, Some(ErrorString::Snippet(")".into()))),
|
||||
|
||||
err => match extract_error_param(err) {
|
||||
Some(e) => (Some(e), None),
|
||||
None => return None,
|
||||
}
|
||||
};
|
||||
Some(ErrorParams {
|
||||
main_param: main,
|
||||
prefix_param: prefix,
|
||||
})
|
||||
}
|
||||
|
||||
impl<'a> ErrorHelpers<'a> for ContextualParseError<'a> {
|
||||
|
@ -273,9 +309,12 @@ impl<'a> ErrorHelpers<'a> for ContextualParseError<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn error_params(self) -> (ErrorString<'a>, Option<ErrorString<'a>>) {
|
||||
fn error_params(self) -> ErrorParams<'a> {
|
||||
let (s, error) = self.error_data();
|
||||
extract_error_params(error).unwrap_or((ErrorString::Snippet(s), None))
|
||||
extract_error_params(error).unwrap_or_else(|| ErrorParams {
|
||||
main_param: Some(ErrorString::Snippet(s)),
|
||||
prefix_param: None
|
||||
})
|
||||
}
|
||||
|
||||
fn to_gecko_message(&self) -> (Option<&'static [u8]>, &'static [u8], Action) {
|
||||
|
@ -303,15 +342,52 @@ impl<'a> ErrorHelpers<'a> for ContextualParseError<'a> {
|
|||
(b"PEKeyframeBadName\0", Action::Nothing),
|
||||
ContextualParseError::UnsupportedKeyframePropertyDeclaration(..) =>
|
||||
(b"PEBadSelectorKeyframeRuleIgnored\0", Action::Nothing),
|
||||
ContextualParseError::InvalidRule(
|
||||
_, CssParseError::Custom(SelectorParseError::ExpectedNamespace(_))) =>
|
||||
(b"PEUnknownNamespacePrefix\0", Action::Nothing),
|
||||
ContextualParseError::InvalidRule(
|
||||
_, CssParseError::Custom(SelectorParseError::Custom(
|
||||
StyleParseError::UnexpectedTokenWithinNamespace(_)))) =>
|
||||
(b"PEAtNSUnexpected\0", Action::Nothing),
|
||||
ContextualParseError::InvalidRule(..) =>
|
||||
(b"PEBadSelectorRSIgnored\0", Action::Nothing),
|
||||
ContextualParseError::InvalidRule(
|
||||
_, CssParseError::Basic(BasicParseError::AtRuleInvalid(_))) |
|
||||
ContextualParseError::InvalidRule(
|
||||
_, CssParseError::Custom(SelectorParseError::Custom(
|
||||
StyleParseError::UnsupportedAtRule(_)))) =>
|
||||
(b"PEUnknownAtRule\0", Action::Nothing),
|
||||
ContextualParseError::InvalidRule(_, ref err) => {
|
||||
let prefix = match *err {
|
||||
CssParseError::Custom(SelectorParseError::UnexpectedTokenInAttributeSelector(_)) =>
|
||||
Some(&b"PEAttSelUnexpected\0"[..]),
|
||||
CssParseError::Custom(SelectorParseError::ExpectedBarInAttr(_)) =>
|
||||
Some(&b"PEAttSelNoBar\0"[..]),
|
||||
CssParseError::Custom(SelectorParseError::BadValueInAttr(_)) =>
|
||||
Some(&b"PEAttSelBadValue\0"[..]),
|
||||
CssParseError::Custom(SelectorParseError::NoQualifiedNameInAttributeSelector(_)) =>
|
||||
Some(&b"PEAttributeNameOrNamespaceExpected\0"[..]),
|
||||
CssParseError::Custom(SelectorParseError::InvalidQualNameInAttr(_)) =>
|
||||
Some(&b"PEAttributeNameExpected\0"[..]),
|
||||
CssParseError::Custom(SelectorParseError::ExplicitNamespaceUnexpectedToken(_)) =>
|
||||
Some(&b"PETypeSelNotType\0"[..]),
|
||||
CssParseError::Custom(SelectorParseError::ExpectedNamespace(_)) =>
|
||||
Some(&b"PEUnknownNamespacePrefix\0"[..]),
|
||||
CssParseError::Custom(SelectorParseError::EmptySelector) =>
|
||||
Some(&b"PESelectorGroupNoSelector\0"[..]),
|
||||
CssParseError::Custom(SelectorParseError::DanglingCombinator) =>
|
||||
Some(&b"PESelectorGroupExtraCombinator\0"[..]),
|
||||
CssParseError::Custom(SelectorParseError::UnsupportedPseudoClassOrElement(_)) =>
|
||||
Some(&b"PEPseudoSelUnknown\0"[..]),
|
||||
CssParseError::Custom(SelectorParseError::PseudoElementExpectedColon(_)) =>
|
||||
Some(&b"PEPseudoSelEndOrUserActionPC\0"[..]),
|
||||
CssParseError::Custom(SelectorParseError::NoIdentForPseudo(_)) =>
|
||||
Some(&b"PEPseudoClassArgNotIdent\0"[..]),
|
||||
CssParseError::Custom(SelectorParseError::PseudoElementExpectedIdent(_)) =>
|
||||
Some(&b"PEPseudoSelBadName\0"[..]),
|
||||
CssParseError::Custom(SelectorParseError::ClassNeedsIdent(_)) =>
|
||||
Some(&b"PEClassSelNotIdent\0"[..]),
|
||||
CssParseError::Custom(SelectorParseError::EmptyNegation) =>
|
||||
Some(&b"PENegationBadArg\0"[..]),
|
||||
_ => None,
|
||||
};
|
||||
return (prefix, b"PEBadSelectorRSIgnored\0", Action::Nothing);
|
||||
}
|
||||
ContextualParseError::UnsupportedRule(..) =>
|
||||
(b"PEDeclDropped\0", Action::Nothing),
|
||||
ContextualParseError::UnsupportedViewportDescriptorDeclaration(..) |
|
||||
|
@ -340,17 +416,20 @@ impl ParseErrorReporter for ErrorReporter {
|
|||
Action::Skip => b"PEDeclSkipped\0".as_ptr(),
|
||||
Action::Drop => b"PEDeclDropped\0".as_ptr(),
|
||||
};
|
||||
let (param, pre_param) = error.error_params();
|
||||
let param = param.into_str();
|
||||
let params = error.error_params();
|
||||
let param = params.main_param;
|
||||
let pre_param = params.prefix_param;
|
||||
let param = param.map(|p| p.into_str());
|
||||
let pre_param = pre_param.map(|p| p.into_str());
|
||||
let param_ptr = param.as_ref().map_or(ptr::null(), |p| p.as_ptr());
|
||||
let pre_param_ptr = pre_param.as_ref().map_or(ptr::null(), |p| p.as_ptr());
|
||||
// The CSS source text is unused and will be removed in bug 1381188.
|
||||
let source = "";
|
||||
unsafe {
|
||||
Gecko_ReportUnexpectedCSSError(self.0,
|
||||
name.as_ptr() as *const _,
|
||||
param.as_ptr() as *const _,
|
||||
param.len() as u32,
|
||||
param_ptr as *const _,
|
||||
param.as_ref().map_or(0, |p| p.len()) as u32,
|
||||
pre.map_or(ptr::null(), |p| p.as_ptr()) as *const _,
|
||||
pre_param_ptr as *const _,
|
||||
pre_param.as_ref().map_or(0, |p| p.len()) as u32,
|
||||
|
|
|
@ -148,3 +148,23 @@ mozlint:
|
|||
files-changed:
|
||||
- 'python/mozlint/**'
|
||||
- 'python/mach_commands.py'
|
||||
|
||||
python3:
|
||||
description: regression tests for python3 compatibility
|
||||
platform: linux64/opt
|
||||
treeherder:
|
||||
symbol: py(3)
|
||||
kind: test
|
||||
tier: 2
|
||||
worker-type: aws-provisioner-v1/gecko-t-linux-large
|
||||
worker:
|
||||
docker-image: {in-tree: "lint"}
|
||||
max-run-time: 3600
|
||||
run:
|
||||
using: run-task
|
||||
# this will evolve as we get closer and closer to python 3 compatibility. It may
|
||||
# eventually be a mach command or shell script.
|
||||
command: python3 -mcompileall python/ taskcluster/
|
||||
when:
|
||||
files-changed:
|
||||
- '**/*.py'
|
||||
|
|
|
@ -15,6 +15,8 @@ apt_packages+=('locales')
|
|||
apt_packages+=('git')
|
||||
apt_packages+=('python')
|
||||
apt_packages+=('python-pip')
|
||||
apt_packages+=('python3')
|
||||
apt_packages+=('python3-pip')
|
||||
apt_packages+=('sudo')
|
||||
apt_packages+=('wget')
|
||||
apt_packages+=('xz-utils')
|
||||
|
|
|
@ -28,10 +28,10 @@ payload:
|
|||
GECKO_HEAD_REPOSITORY: '{{{head_repository}}}'
|
||||
GECKO_HEAD_REF: '{{head_ref}}'
|
||||
GECKO_HEAD_REV: '{{head_rev}}'
|
||||
HG_STORE_PATH: /builds/worker/checkouts/hg-store
|
||||
HG_STORE_PATH: /home/worker/checkouts/hg-store
|
||||
|
||||
cache:
|
||||
level-{{level}}-checkouts: /builds/worker/checkouts
|
||||
level-{{level}}-checkouts: /home/worker/checkouts
|
||||
|
||||
features:
|
||||
taskclusterProxy: true
|
||||
|
@ -47,20 +47,20 @@ payload:
|
|||
maxRunTime: 1800
|
||||
|
||||
command:
|
||||
- /builds/worker/bin/run-task
|
||||
- '--vcs-checkout=/builds/worker/checkouts/gecko'
|
||||
- /home/worker/bin/run-task
|
||||
- '--vcs-checkout=/home/worker/checkouts/gecko'
|
||||
- '--'
|
||||
- bash
|
||||
- -cx
|
||||
- >
|
||||
cd /builds/worker/checkouts/gecko &&
|
||||
ln -s /builds/worker/artifacts artifacts &&
|
||||
cd /home/worker/checkouts/gecko &&
|
||||
ln -s /home/worker/artifacts artifacts &&
|
||||
./mach --log-no-times taskgraph {{action}} {{action_args}}
|
||||
|
||||
artifacts:
|
||||
'public':
|
||||
type: 'directory'
|
||||
path: '/builds/worker/artifacts'
|
||||
path: '/home/worker/artifacts'
|
||||
expires: '{{#from_now}}7 days{{/from_now}}'
|
||||
|
||||
extra:
|
||||
|
|
|
@ -17,38 +17,38 @@ from taskgraph.cron.util import (
|
|||
class TestMatchUtc(unittest.TestCase):
|
||||
|
||||
def test_hour_minute(self):
|
||||
params = {'time': datetime.datetime(2017, 01, 26, 16, 30, 0)}
|
||||
params = {'time': datetime.datetime(2017, 1, 26, 16, 30, 0)}
|
||||
self.assertFalse(match_utc(params, hour=4, minute=30))
|
||||
self.assertTrue(match_utc(params, hour=16, minute=30))
|
||||
self.assertFalse(match_utc(params, hour=16, minute=0))
|
||||
|
||||
def test_hour_only(self):
|
||||
params = {'time': datetime.datetime(2017, 01, 26, 16, 0, 0)}
|
||||
params = {'time': datetime.datetime(2017, 1, 26, 16, 0, 0)}
|
||||
self.assertFalse(match_utc(params, hour=0))
|
||||
self.assertFalse(match_utc(params, hour=4))
|
||||
self.assertTrue(match_utc(params, hour=16))
|
||||
params = {'time': datetime.datetime(2017, 01, 26, 16, 15, 0)}
|
||||
params = {'time': datetime.datetime(2017, 1, 26, 16, 15, 0)}
|
||||
self.assertFalse(match_utc(params, hour=0))
|
||||
self.assertFalse(match_utc(params, hour=4))
|
||||
self.assertTrue(match_utc(params, hour=16))
|
||||
params = {'time': datetime.datetime(2017, 01, 26, 16, 30, 0)}
|
||||
params = {'time': datetime.datetime(2017, 1, 26, 16, 30, 0)}
|
||||
self.assertFalse(match_utc(params, hour=0))
|
||||
self.assertFalse(match_utc(params, hour=4))
|
||||
self.assertTrue(match_utc(params, hour=16))
|
||||
params = {'time': datetime.datetime(2017, 01, 26, 16, 45, 0)}
|
||||
params = {'time': datetime.datetime(2017, 1, 26, 16, 45, 0)}
|
||||
self.assertFalse(match_utc(params, hour=0))
|
||||
self.assertFalse(match_utc(params, hour=4))
|
||||
self.assertTrue(match_utc(params, hour=16))
|
||||
|
||||
def test_minute_only(self):
|
||||
params = {'time': datetime.datetime(2017, 01, 26, 13, 0, 0)}
|
||||
params = {'time': datetime.datetime(2017, 1, 26, 13, 0, 0)}
|
||||
self.assertTrue(match_utc(params, minute=0))
|
||||
self.assertFalse(match_utc(params, minute=15))
|
||||
self.assertFalse(match_utc(params, minute=30))
|
||||
self.assertFalse(match_utc(params, minute=45))
|
||||
|
||||
def test_zeroes(self):
|
||||
params = {'time': datetime.datetime(2017, 01, 26, 0, 0, 0)}
|
||||
params = {'time': datetime.datetime(2017, 1, 26, 0, 0, 0)}
|
||||
self.assertTrue(match_utc(params, minute=0))
|
||||
self.assertTrue(match_utc(params, hour=0))
|
||||
self.assertFalse(match_utc(params, hour=1))
|
||||
|
@ -57,7 +57,7 @@ class TestMatchUtc(unittest.TestCase):
|
|||
self.assertFalse(match_utc(params, minute=45))
|
||||
|
||||
def test_invalid_minute(self):
|
||||
params = {'time': datetime.datetime(2017, 01, 26, 13, 0, 0)}
|
||||
params = {'time': datetime.datetime(2017, 1, 26, 13, 0, 0)}
|
||||
self.assertRaises(Exception, lambda:
|
||||
match_utc(params, minute=1))
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ from taskgraph.util.parameterization import (
|
|||
class TestTimestamps(unittest.TestCase):
|
||||
|
||||
def test_no_change(self):
|
||||
now = datetime.datetime(2018, 01, 01)
|
||||
now = datetime.datetime(2018, 1, 1)
|
||||
input = {
|
||||
"key": "value",
|
||||
"numeric": 10,
|
||||
|
@ -26,13 +26,13 @@ class TestTimestamps(unittest.TestCase):
|
|||
self.assertEqual(resolve_timestamps(now, input), input)
|
||||
|
||||
def test_buried_replacement(self):
|
||||
now = datetime.datetime(2018, 01, 01)
|
||||
now = datetime.datetime(2018, 1, 1)
|
||||
input = {"key": [{"key2": [{'relative-datestamp': '1 day'}]}]}
|
||||
self.assertEqual(resolve_timestamps(now, input),
|
||||
{"key": [{"key2": ['2018-01-02T00:00:00Z']}]})
|
||||
|
||||
def test_appears_with_other_keys(self):
|
||||
now = datetime.datetime(2018, 01, 01)
|
||||
now = datetime.datetime(2018, 1, 1)
|
||||
input = [{'relative-datestamp': '1 day', 'another-key': True}]
|
||||
self.assertEqual(resolve_timestamps(now, input),
|
||||
[{'relative-datestamp': '1 day', 'another-key': True}])
|
||||
|
|
|
@ -27,7 +27,7 @@ class TestValidateSchema(unittest.TestCase):
|
|||
try:
|
||||
validate_schema(schema, {'x': 'not-int'}, "pfx")
|
||||
self.fail("no exception raised")
|
||||
except Exception, e:
|
||||
except Exception as e:
|
||||
self.failUnless(str(e).startswith("pfx\n"))
|
||||
|
||||
|
||||
|
|
|
@ -7,9 +7,9 @@ from marionette_driver import Wait
|
|||
|
||||
|
||||
class TestBaseTabbarSessionRestoreButton(PuppeteerMixin, MarionetteTestCase):
|
||||
def setUp(self, prefValue=True):
|
||||
def setUp(self, restore_button_pref=1):
|
||||
super(TestBaseTabbarSessionRestoreButton, self).setUp()
|
||||
self.marionette.enforce_gecko_prefs({'browser.tabs.restorebutton': prefValue})
|
||||
self.marionette.enforce_gecko_prefs({'browser.tabs.restorebutton': restore_button_pref})
|
||||
|
||||
# Each list element represents a window of tabs loaded at
|
||||
# some testing URL, the URLS are arbitrary.
|
||||
|
@ -96,7 +96,7 @@ class TestTabbarSessionRestoreButton(TestBaseTabbarSessionRestoreButton):
|
|||
|
||||
class TestNoTabbarSessionRestoreButton(TestBaseTabbarSessionRestoreButton):
|
||||
def setUp(self):
|
||||
super(TestNoTabbarSessionRestoreButton, self).setUp(False)
|
||||
super(TestNoTabbarSessionRestoreButton, self).setUp(restore_button_pref=0)
|
||||
|
||||
def test_pref_off_button_does_not_show(self):
|
||||
wrapper = self.puppeteer.windows.current.tabbar.restore_tabs_button_wrapper
|
||||
|
|
|
@ -417,7 +417,10 @@ class MochitestServer(object):
|
|||
# get testing environment
|
||||
env = test_environment(xrePath=self._xrePath, log=self._log)
|
||||
env["XPCOM_DEBUG_BREAK"] = "warn"
|
||||
env["LD_LIBRARY_PATH"] = self._xrePath
|
||||
if "LD_LIBRARY_PATH" not in env or env["LD_LIBRARY_PATH"] is None:
|
||||
env["LD_LIBRARY_PATH"] = self._xrePath
|
||||
else:
|
||||
env["LD_LIBRARY_PATH"] = ":".join([self._xrePath, env["LD_LIBRARY_PATH"]])
|
||||
|
||||
# When running with an ASan build, our xpcshell server will also be ASan-enabled,
|
||||
# thus consuming too much resources when running together with the browser on
|
||||
|
|
|
@ -1500,9 +1500,7 @@ LocaleData.prototype = {
|
|||
|
||||
|
||||
get uiLocale() {
|
||||
// Return the browser locale, but convert it to a Chrome-style
|
||||
// locale code.
|
||||
return Services.locale.getAppLocaleAsBCP47().replace(/-/g, "_");
|
||||
return Services.locale.getAppLocaleAsBCP47();
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -296,6 +296,19 @@ this.ExtensionPreferencesManager = {
|
|||
await Promise.all(removePromises);
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the currently active value for a setting.
|
||||
*
|
||||
* @param {string} name
|
||||
* The unique id of the setting.
|
||||
*
|
||||
* @returns {Object} The current setting object.
|
||||
*/
|
||||
async getSetting(name) {
|
||||
await ExtensionSettingsStore.initialize();
|
||||
return ExtensionSettingsStore.getSetting(STORE_TYPE, name);
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the levelOfControl for a setting / extension combo.
|
||||
* This queries the levelOfControl from the ExtensionSettingsStore and also
|
||||
|
@ -305,17 +318,24 @@ this.ExtensionPreferencesManager = {
|
|||
* The extension for which levelOfControl is being requested.
|
||||
* @param {string} name
|
||||
* The unique id of the setting.
|
||||
* @param {string} storeType
|
||||
* The name of the store in ExtensionSettingsStore.
|
||||
* Defaults to STORE_TYPE.
|
||||
*
|
||||
* @returns {Promise}
|
||||
* Resolves to the level of control of the extension over the setting.
|
||||
*/
|
||||
async getLevelOfControl(extension, name) {
|
||||
for (let prefName of settingsMap.get(name).prefNames) {
|
||||
if (Preferences.locked(prefName)) {
|
||||
return "not_controllable";
|
||||
async getLevelOfControl(extension, name, storeType = STORE_TYPE) {
|
||||
// This could be called for a setting that isn't defined to the PreferencesManager,
|
||||
// in which case we simply defer to the SettingsStore.
|
||||
if (storeType === STORE_TYPE) {
|
||||
for (let prefName of settingsMap.get(name).prefNames) {
|
||||
if (Preferences.locked(prefName)) {
|
||||
return "not_controllable";
|
||||
}
|
||||
}
|
||||
}
|
||||
await ExtensionSettingsStore.initialize();
|
||||
return ExtensionSettingsStore.getLevelOfControl(extension, STORE_TYPE, name);
|
||||
return ExtensionSettingsStore.getLevelOfControl(extension, storeType, name);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -2,28 +2,38 @@
|
|||
/* vim: set sts=2 sw=2 et tw=80: */
|
||||
"use strict";
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionSettingsStore",
|
||||
"resource://gre/modules/ExtensionSettingsStore.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
|
||||
"resource://gre/modules/Preferences.jsm");
|
||||
|
||||
Cu.import("resource://gre/modules/ExtensionPreferencesManager.jsm");
|
||||
|
||||
const getSettingsAPI = (extension, name, callback) => {
|
||||
const HOMEPAGE_OVERRIDE_SETTING = "homepage_override";
|
||||
const URL_STORE_TYPE = "url_overrides";
|
||||
const NEW_TAB_OVERRIDE_SETTING = "newTabURL";
|
||||
|
||||
const getSettingsAPI = (extension, name, callback, storeType, readOnly = false) => {
|
||||
return {
|
||||
async get(details) {
|
||||
return {
|
||||
levelOfControl: details.incognito ?
|
||||
"not_controllable" :
|
||||
await ExtensionPreferencesManager.getLevelOfControl(
|
||||
extension, name),
|
||||
extension, name, storeType),
|
||||
value: await callback(),
|
||||
};
|
||||
},
|
||||
set(details) {
|
||||
return ExtensionPreferencesManager.setSetting(
|
||||
extension, name, details.value);
|
||||
if (!readOnly) {
|
||||
return ExtensionPreferencesManager.setSetting(
|
||||
extension, name, details.value);
|
||||
}
|
||||
},
|
||||
clear(details) {
|
||||
return ExtensionPreferencesManager.removeSetting(extension, name);
|
||||
if (!readOnly) {
|
||||
return ExtensionPreferencesManager.removeSetting(extension, name);
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
@ -73,6 +83,25 @@ this.browserSettings = class extends ExtensionAPI {
|
|||
return Preferences.get("browser.cache.disk.enable") &&
|
||||
Preferences.get("browser.cache.memory.enable");
|
||||
}),
|
||||
homepageOverride: getSettingsAPI(extension,
|
||||
HOMEPAGE_OVERRIDE_SETTING,
|
||||
async () => {
|
||||
let homepageSetting = await ExtensionPreferencesManager.getSetting(HOMEPAGE_OVERRIDE_SETTING);
|
||||
if (homepageSetting) {
|
||||
return homepageSetting.value;
|
||||
}
|
||||
return null;
|
||||
}, undefined, true),
|
||||
newTabPageOverride: getSettingsAPI(extension,
|
||||
NEW_TAB_OVERRIDE_SETTING,
|
||||
async () => {
|
||||
await ExtensionSettingsStore.initialize();
|
||||
let newTabPageSetting = ExtensionSettingsStore.getSetting(URL_STORE_TYPE, NEW_TAB_OVERRIDE_SETTING);
|
||||
if (newTabPageSetting) {
|
||||
return newTabPageSetting.value;
|
||||
}
|
||||
return null;
|
||||
}, URL_STORE_TYPE, true),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -29,6 +29,14 @@
|
|||
"cacheEnabled": {
|
||||
"$ref": "types.Setting",
|
||||
"description": "Enables or disables the browser cache."
|
||||
},
|
||||
"homepageOverride": {
|
||||
"$ref": "types.Setting",
|
||||
"description": "Returns the value of the overridden home page. Read-only."
|
||||
},
|
||||
"newTabPageOverride": {
|
||||
"$ref": "types.Setting",
|
||||
"description": "Returns the value of the overridden new tab page. Read-only."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -119,6 +119,9 @@ add_task(async function test_preference_manager() {
|
|||
"controlled_by_this_extension",
|
||||
"getLevelOfControl returns correct levelOfControl when a pref has been set.");
|
||||
|
||||
let checkSetting = await ExtensionPreferencesManager.getSetting(setting);
|
||||
equal(checkSetting.value, newValue1, "getSetting returns the expected value.");
|
||||
|
||||
let newValue2 = "newValue2";
|
||||
prefsChanged = await ExtensionPreferencesManager.setSetting(extensions[0], setting, newValue2);
|
||||
ok(!prefsChanged, "setSetting returns false when the pref(s) have not been set.");
|
||||
|
@ -166,6 +169,9 @@ add_task(async function test_preference_manager() {
|
|||
equal(Preferences.get(settingObj.prefNames[i]), settingObj.initalValues[i],
|
||||
"removeSetting sets the pref(s) to the initial value(s) when removing the last extension.");
|
||||
}
|
||||
|
||||
checkSetting = await ExtensionPreferencesManager.getSetting(setting);
|
||||
equal(checkSetting, null, "getSetting returns null when nothing has been set.");
|
||||
}
|
||||
|
||||
// Tests for unsetAll.
|
||||
|
|
|
@ -370,7 +370,7 @@ add_task(async function test_get_ui_language() {
|
|||
await extension.startup();
|
||||
await extension.awaitMessage("content-loaded");
|
||||
|
||||
extension.sendMessage(["expect-results", "en_US"]);
|
||||
extension.sendMessage(["expect-results", "en-US"]);
|
||||
|
||||
await extension.awaitMessage("background-done");
|
||||
await extension.awaitMessage("content-done");
|
||||
|
|
|
@ -115,7 +115,7 @@ async function test_i18n_css(options = {}) {
|
|||
cssURL = cssURL.replace(/foo.css$/, "locale.css");
|
||||
|
||||
css = await fetch(cssURL);
|
||||
equal(css, '* { content: "en_US ltr rtl left right" }', "CSS file localized in mochitest scope");
|
||||
equal(css, '* { content: "en-US ltr rtl left right" }', "CSS file localized in mochitest scope");
|
||||
|
||||
// We don't currently have a good way to mock this.
|
||||
if (false) {
|
||||
|
|
|
@ -277,6 +277,90 @@ browser.usage:
|
|||
- 'main'
|
||||
- 'content'
|
||||
|
||||
# The following section contains the session restore scalars.
|
||||
browser.session.restore:
|
||||
number_of_win:
|
||||
bug_numbers:
|
||||
- 1379226
|
||||
description: The count of windows open after a session has been restored.
|
||||
expires: never
|
||||
kind: uint
|
||||
notification_emails:
|
||||
- bwinton@mozilla.com
|
||||
release_channel_collection: opt-out
|
||||
record_in_processes:
|
||||
- 'main'
|
||||
|
||||
number_of_tabs:
|
||||
bug_numbers:
|
||||
- 1379226
|
||||
description: The count of tabs open after a session has been restored.
|
||||
expires: never
|
||||
kind: uint
|
||||
notification_emails:
|
||||
- bwinton@mozilla.com
|
||||
release_channel_collection: opt-out
|
||||
record_in_processes:
|
||||
- 'main'
|
||||
|
||||
tabbar_restore_available:
|
||||
bug_numbers:
|
||||
- 1379226
|
||||
description: >
|
||||
Recorded on startup. Boolean stating whether the tabbar session
|
||||
restore button was ever available.
|
||||
expires: never
|
||||
kind: boolean
|
||||
notification_emails:
|
||||
- bwinton@mozilla.com
|
||||
release_channel_collection: opt-out
|
||||
record_in_processes:
|
||||
- 'main'
|
||||
|
||||
tabbar_restore_clicked:
|
||||
bug_numbers:
|
||||
- 1379226
|
||||
description: >
|
||||
Recorded on click event. Boolean stating if the session restore button
|
||||
was clicked.
|
||||
expires: never
|
||||
kind: boolean
|
||||
notification_emails:
|
||||
- bwinton@mozilla.com
|
||||
release_channel_collection: opt-out
|
||||
record_in_processes:
|
||||
- 'main'
|
||||
|
||||
browser_startup_page:
|
||||
bug_numbers:
|
||||
- 1379226
|
||||
description: >
|
||||
The value of the browser.startup.page pref.
|
||||
This pref restores the tabs and windows automatically when set to 3.
|
||||
expires: never
|
||||
kind: uint
|
||||
notification_emails:
|
||||
- bwinton@mozilla.com
|
||||
release_channel_collection: opt-out
|
||||
record_in_processes:
|
||||
- 'main'
|
||||
|
||||
browser_tabs_restorebutton:
|
||||
bug_numbers:
|
||||
- 1379226
|
||||
description: >
|
||||
The value of the browser.tabs.restorebutton pref.
|
||||
0 - the user is not a part of the experiment
|
||||
1 - the user is a part of the experiment
|
||||
2 - the user is part of the control group
|
||||
expires: never
|
||||
kind: uint
|
||||
notification_emails:
|
||||
- bwinton@mozilla.com
|
||||
release_channel_collection: opt-out
|
||||
record_in_processes:
|
||||
- 'main'
|
||||
|
||||
# This section is for probes used to measure use of the Webextensions storage.sync API.
|
||||
storage.sync.api.usage:
|
||||
extensions_using:
|
||||
|
|
|
@ -148,7 +148,7 @@ class HgHelper(VCSHelper):
|
|||
@property
|
||||
def files_changed(self):
|
||||
return self.run(['hg', 'log', '-r', '::. and not public()',
|
||||
'--template', '{join(files, "\n")}\n'])
|
||||
'--template', '{join(files, "\n")}\n']).splitlines()
|
||||
|
||||
@property
|
||||
def has_uncommitted_changes(self):
|
||||
|
|
|
@ -477,7 +477,9 @@ SchedulerImpl::CreateQueue(nsIIdlePeriod* aIdlePeriod, nsThread** aThread)
|
|||
// Setup "main" thread
|
||||
mainThread = new nsThread(WrapNotNull(synchronizedQueue), nsThread::MAIN_THREAD, 0);
|
||||
|
||||
#ifndef RELEASE_OR_BETA
|
||||
prioritized->SetNextIdleDeadlineRef(mainThread->NextIdleDeadlineRef());
|
||||
#endif
|
||||
|
||||
mainThread.forget(aThread);
|
||||
return synchronizedQueue.forget();
|
||||
|
|
Загрузка…
Ссылка в новой задаче