MozReview-Commit-ID: 7XpmaFg1k8N
This commit is contained in:
Wes Kocher 2017-08-29 16:17:13 -07:00
Родитель 8e1e18faf2 2198196808
Коммит a8e6d0bd2c
68 изменённых файлов: 1215 добавлений и 696 удалений

Просмотреть файл

@ -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();