зеркало из https://github.com/mozilla/gecko-dev.git
merge mozilla-central to mozilla-inbound. r=merge a=merge
This commit is contained in:
Коммит
9975089d4d
|
@ -730,11 +730,7 @@ pref("browser.preferences.instantApply", true);
|
|||
pref("browser.preferences.search", true);
|
||||
|
||||
// Use the new in-content about:preferences in Nightly only for now
|
||||
#if defined(NIGHTLY_BUILD)
|
||||
pref("browser.preferences.useOldOrganization", false);
|
||||
#else
|
||||
pref("browser.preferences.useOldOrganization", true);
|
||||
#endif
|
||||
|
||||
// Once the Storage Management is completed.
|
||||
// (The Storage Management-related prefs are browser.storageManager.* )
|
||||
|
|
|
@ -890,7 +890,7 @@ var gPopupBlockerObserver = {
|
|||
allowVisible: true,
|
||||
prefilledHost: prefillValue,
|
||||
permissionType: "popup",
|
||||
windowTitle: bundlePreferences.getString("popuppermissionstitle"),
|
||||
windowTitle: bundlePreferences.getString("popuppermissionstitle2"),
|
||||
introText: bundlePreferences.getString("popuppermissionstext") };
|
||||
var existingWindow = Services.wm.getMostRecentWindow("Browser:Permissions");
|
||||
if (existingWindow) {
|
||||
|
|
|
@ -250,21 +250,21 @@ var gPrivacyPane = {
|
|||
let pkiBundle = document.getElementById("pkiBundle");
|
||||
appendSearchKeywords("passwordExceptions", [
|
||||
bundlePrefs.getString("savedLoginsExceptions_title"),
|
||||
bundlePrefs.getString("savedLoginsExceptions_desc"),
|
||||
bundlePrefs.getString("savedLoginsExceptions_desc2"),
|
||||
]);
|
||||
appendSearchKeywords("showPasswords", [
|
||||
signonBundle.getString("loginsDescriptionAll"),
|
||||
]);
|
||||
appendSearchKeywords("trackingProtectionExceptions", [
|
||||
bundlePrefs.getString("trackingprotectionpermissionstitle"),
|
||||
bundlePrefs.getString("trackingprotectionpermissionstext"),
|
||||
bundlePrefs.getString("trackingprotectionpermissionstext2"),
|
||||
]);
|
||||
appendSearchKeywords("changeBlockList", [
|
||||
bundlePrefs.getString("blockliststitle"),
|
||||
bundlePrefs.getString("blockliststext"),
|
||||
]);
|
||||
appendSearchKeywords("popupPolicyButton", [
|
||||
bundlePrefs.getString("popuppermissionstitle"),
|
||||
bundlePrefs.getString("popuppermissionstitle2"),
|
||||
bundlePrefs.getString("popuppermissionstext"),
|
||||
]);
|
||||
appendSearchKeywords("notificationsPolicyButton", [
|
||||
|
@ -272,14 +272,14 @@ var gPrivacyPane = {
|
|||
bundlePrefs.getString("notificationspermissionstext4"),
|
||||
]);
|
||||
appendSearchKeywords("addonExceptions", [
|
||||
bundlePrefs.getString("addons_permissions_title"),
|
||||
bundlePrefs.getString("addons_permissions_title2"),
|
||||
bundlePrefs.getString("addonspermissionstext"),
|
||||
]);
|
||||
appendSearchKeywords("viewSecurityDevicesButton", [
|
||||
pkiBundle.getString("enable_fips"),
|
||||
]);
|
||||
appendSearchKeywords("siteDataSettings", [
|
||||
bundlePrefs.getString("siteDataSettings.description"),
|
||||
bundlePrefs.getString("siteDataSettings2.description"),
|
||||
bundlePrefs.getString("removeAllCookies.label"),
|
||||
bundlePrefs.getString("removeSelectedCookies.label"),
|
||||
]);
|
||||
|
@ -574,7 +574,7 @@ var gPrivacyPane = {
|
|||
permissionType: "trackingprotection",
|
||||
hideStatusColumn: true,
|
||||
windowTitle: bundlePreferences.getString("trackingprotectionpermissionstitle"),
|
||||
introText: bundlePreferences.getString("trackingprotectionpermissionstext"),
|
||||
introText: bundlePreferences.getString("trackingprotectionpermissionstext2"),
|
||||
};
|
||||
gSubDialog.open("chrome://browser/content/preferences/permissions.xul",
|
||||
null, params);
|
||||
|
@ -830,7 +830,7 @@ var gPrivacyPane = {
|
|||
var bundlePreferences = document.getElementById("bundlePreferences");
|
||||
var params = { blockVisible: false, sessionVisible: false, allowVisible: true,
|
||||
prefilledHost: "", permissionType: "popup" }
|
||||
params.windowTitle = bundlePreferences.getString("popuppermissionstitle");
|
||||
params.windowTitle = bundlePreferences.getString("popuppermissionstitle2");
|
||||
params.introText = bundlePreferences.getString("popuppermissionstext");
|
||||
|
||||
gSubDialog.open("chrome://browser/content/preferences/permissions.xul",
|
||||
|
@ -875,7 +875,7 @@ var gPrivacyPane = {
|
|||
prefilledHost: "",
|
||||
permissionType: "login-saving",
|
||||
windowTitle: bundlePrefs.getString("savedLoginsExceptions_title"),
|
||||
introText: bundlePrefs.getString("savedLoginsExceptions_desc")
|
||||
introText: bundlePrefs.getString("savedLoginsExceptions_desc2")
|
||||
};
|
||||
|
||||
gSubDialog.open("chrome://browser/content/preferences/permissions.xul",
|
||||
|
@ -1073,7 +1073,7 @@ var gPrivacyPane = {
|
|||
|
||||
var params = this._addonParams;
|
||||
if (!params.windowTitle || !params.introText) {
|
||||
params.windowTitle = bundlePrefs.getString("addons_permissions_title");
|
||||
params.windowTitle = bundlePrefs.getString("addons_permissions_title2");
|
||||
params.introText = bundlePrefs.getString("addonspermissionstext");
|
||||
}
|
||||
|
||||
|
|
|
@ -185,7 +185,7 @@
|
|||
<vbox id="passwordSettings">
|
||||
<hbox id="savePasswordsBox">
|
||||
<checkbox id="savePasswords"
|
||||
label="&rememberLogins1.label;" accesskey="&rememberLogins1.accesskey;"
|
||||
label="&rememberLogins2.label;" accesskey="&rememberLogins2.accesskey;"
|
||||
preference="signon.rememberSignons"
|
||||
onsyncfrompreference="return gPrivacyPane.readSavePasswords();"
|
||||
flex="1" />
|
||||
|
@ -250,7 +250,7 @@
|
|||
<menuitem label="&historyHeader.custom.label;" value="custom" searchkeywords="&privateBrowsingPermanent2.label;
|
||||
&rememberHistory2.label;
|
||||
&rememberSearchForm.label;
|
||||
&acceptCookies.label;
|
||||
&acceptCookies2.label;
|
||||
&cookieExceptions.label;
|
||||
&acceptThirdParty.pre.label;
|
||||
&acceptThirdParty.always.label;
|
||||
|
@ -309,9 +309,9 @@
|
|||
accesskey="&rememberSearchForm.accesskey;"
|
||||
preference="browser.formfill.enable"/>
|
||||
<hbox id="cookiesBox">
|
||||
<checkbox id="acceptCookies" label="&acceptCookies.label;"
|
||||
<checkbox id="acceptCookies" label="&acceptCookies2.label;"
|
||||
preference="network.cookie.cookieBehavior"
|
||||
accesskey="&acceptCookies.accesskey;"
|
||||
accesskey="&acceptCookies2.accesskey;"
|
||||
onsyncfrompreference="return gPrivacyPane.readAcceptCookies();"
|
||||
onsynctopreference="return gPrivacyPane.writeAcceptCookies();"
|
||||
flex="1" />
|
||||
|
@ -497,8 +497,8 @@
|
|||
label="&trackingProtectionExceptions.label;"
|
||||
accesskey="&trackingProtectionExceptions.accesskey;"
|
||||
preference="pref.privacy.disable_button.tracking_protection_exceptions"
|
||||
searchkeywords="&removepermission.label;
|
||||
&removeallpermissions.label;
|
||||
searchkeywords="&removepermission2.label;
|
||||
&removeallpermissions2.label;
|
||||
&button.cancel.label;
|
||||
&button.ok.label;"/>
|
||||
</hbox>
|
||||
|
@ -542,7 +542,7 @@
|
|||
<rows>
|
||||
<row id="notificationsPolicyRow" align="center">
|
||||
<description flex="1">
|
||||
<label id="notificationsPolicy">¬ificationsPolicyDesc3.label;</label>
|
||||
<label id="notificationsPolicy">¬ificationsPolicyDesc4.label;</label>
|
||||
<label id="notificationsPolicyLearnMore"
|
||||
class="learnMore text-link">¬ificationsPolicyLearnMore.label;</label>
|
||||
</description>
|
||||
|
@ -551,8 +551,8 @@
|
|||
class="accessory-button"
|
||||
label="¬ificationsPolicyButton.label;"
|
||||
accesskey="¬ificationsPolicyButton.accesskey;"
|
||||
searchkeywords="&removepermission.label;
|
||||
&removeallpermissions.label;
|
||||
searchkeywords="&removepermission2.label;
|
||||
&removeallpermissions2.label;
|
||||
&button.cancel.label;
|
||||
&button.ok.label;"/>
|
||||
</hbox>
|
||||
|
@ -584,8 +584,8 @@
|
|||
|
||||
<hbox id="addonInstallBox">
|
||||
<checkbox id="warnAddonInstall"
|
||||
label="&warnOnAddonInstall.label;"
|
||||
accesskey="&warnOnAddonInstall.accesskey;"
|
||||
label="&warnOnAddonInstall2.label;"
|
||||
accesskey="&warnOnAddonInstall2.accesskey;"
|
||||
preference="xpinstall.whitelist.required"
|
||||
onsyncfrompreference="return gPrivacyPane.readWarnAddonInstall();"
|
||||
flex="1" />
|
||||
|
@ -597,8 +597,8 @@
|
|||
accesskey="&addonExceptions.accesskey;"
|
||||
searchkeywords="&address.label;
|
||||
&allow.label;
|
||||
&removepermission.label;
|
||||
&removeallpermissions.label;
|
||||
&removepermission2.label;
|
||||
&removeallpermissions2.label;
|
||||
&button.cancel.label;
|
||||
&button.ok.label;"/>
|
||||
</hbox>
|
||||
|
|
|
@ -144,7 +144,7 @@ var gContentPane = {
|
|||
var bundlePreferences = document.getElementById("bundlePreferences");
|
||||
var params = { blockVisible: false, sessionVisible: false, allowVisible: true,
|
||||
prefilledHost: "", permissionType: "popup" }
|
||||
params.windowTitle = bundlePreferences.getString("popuppermissionstitle");
|
||||
params.windowTitle = bundlePreferences.getString("popuppermissionstitle2");
|
||||
params.introText = bundlePreferences.getString("popuppermissionstext");
|
||||
|
||||
gSubDialog.open("chrome://browser/content/preferences/permissions.xul",
|
||||
|
|
|
@ -69,7 +69,7 @@
|
|||
<rows>
|
||||
<row id="notificationsPolicyRow" align="center">
|
||||
<hbox align="start">
|
||||
<label id="notificationsPolicy">¬ificationsPolicyDesc3.label;</label>
|
||||
<label id="notificationsPolicy">¬ificationsPolicyDesc4.label;</label>
|
||||
<label id="notificationsPolicyLearnMore"
|
||||
class="learnMore text-link"
|
||||
value="¬ificationsPolicyLearnMore.label;"/>
|
||||
|
|
|
@ -469,7 +469,7 @@ var gPrivacyPane = {
|
|||
permissionType: "trackingprotection",
|
||||
hideStatusColumn: true,
|
||||
windowTitle: bundlePreferences.getString("trackingprotectionpermissionstitle"),
|
||||
introText: bundlePreferences.getString("trackingprotectionpermissionstext"),
|
||||
introText: bundlePreferences.getString("trackingprotectionpermissionstext2"),
|
||||
};
|
||||
gSubDialog.open("chrome://browser/content/preferences/permissions.xul",
|
||||
null, params);
|
||||
|
|
|
@ -206,9 +206,9 @@
|
|||
preference="browser.formfill.enable"/>
|
||||
</vbox>
|
||||
<hbox id="cookiesBox">
|
||||
<checkbox id="acceptCookies" label="&acceptCookies.label;"
|
||||
<checkbox id="acceptCookies" label="&acceptCookies2.label;"
|
||||
preference="network.cookie.cookieBehavior"
|
||||
accesskey="&acceptCookies.accesskey;"
|
||||
accesskey="&acceptCookies2.accesskey;"
|
||||
onsyncfrompreference="return gPrivacyPane.readAcceptCookies();"
|
||||
onsynctopreference="return gPrivacyPane.writeAcceptCookies();"/>
|
||||
<spacer flex="1" />
|
||||
|
|
|
@ -70,7 +70,7 @@ var gSecurityPane = {
|
|||
|
||||
var params = this._addonParams;
|
||||
if (!params.windowTitle || !params.introText) {
|
||||
params.windowTitle = bundlePrefs.getString("addons_permissions_title");
|
||||
params.windowTitle = bundlePrefs.getString("addons_permissions_title2");
|
||||
params.introText = bundlePrefs.getString("addonspermissionstext");
|
||||
}
|
||||
|
||||
|
@ -132,7 +132,7 @@ var gSecurityPane = {
|
|||
prefilledHost: "",
|
||||
permissionType: "login-saving",
|
||||
windowTitle: bundlePrefs.getString("savedLoginsExceptions_title"),
|
||||
introText: bundlePrefs.getString("savedLoginsExceptions_desc")
|
||||
introText: bundlePrefs.getString("savedLoginsExceptions_desc2")
|
||||
};
|
||||
|
||||
gSubDialog.open("chrome://browser/content/preferences/permissions.xul",
|
||||
|
|
|
@ -62,8 +62,8 @@
|
|||
|
||||
<hbox id="addonInstallBox">
|
||||
<checkbox id="warnAddonInstall"
|
||||
label="&warnOnAddonInstall.label;"
|
||||
accesskey="&warnOnAddonInstall.accesskey;"
|
||||
label="&warnOnAddonInstall2.label;"
|
||||
accesskey="&warnOnAddonInstall2.accesskey;"
|
||||
preference="xpinstall.whitelist.required"
|
||||
onsyncfrompreference="return gSecurityPane.readWarnAddonInstall();"/>
|
||||
<spacer flex="1"/>
|
||||
|
@ -100,7 +100,7 @@
|
|||
<rows id="passwordRows">
|
||||
<row id="savePasswordsBox">
|
||||
<checkbox id="savePasswords"
|
||||
label="&rememberLogins.label;" accesskey="&rememberLogins.accesskey;"
|
||||
label="&rememberLogins2.label;" accesskey="&rememberLogins2.accesskey;"
|
||||
preference="signon.rememberSignons"
|
||||
onsyncfrompreference="return gSecurityPane.readSavePasswords();"/>
|
||||
<button id="passwordExceptions"
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
onkeypress="gPermissionManager.onPermissionKeyPress(event)"
|
||||
onselect="gPermissionManager.onPermissionSelected();">
|
||||
<treecols>
|
||||
<treecol id="siteCol" label="&treehead.sitename.label;" flex="3"
|
||||
<treecol id="siteCol" label="&treehead.sitename2.label;" flex="3"
|
||||
data-field-name="origin" persist="width"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol id="statusCol" label="&treehead.status.label;" flex="1"
|
||||
|
@ -64,12 +64,12 @@
|
|||
<vbox>
|
||||
<hbox class="actionButtons" align="left" flex="1">
|
||||
<button id="removePermission" disabled="true"
|
||||
accesskey="&removepermission.accesskey;"
|
||||
icon="remove" label="&removepermission.label;"
|
||||
accesskey="&removepermission2.accesskey;"
|
||||
icon="remove" label="&removepermission2.label;"
|
||||
oncommand="gPermissionManager.onPermissionDeleted();"/>
|
||||
<button id="removeAllPermissions"
|
||||
icon="clear" label="&removeallpermissions.label;"
|
||||
accesskey="&removeallpermissions.accesskey;"
|
||||
icon="clear" label="&removeallpermissions2.label;"
|
||||
accesskey="&removeallpermissions2.accesskey;"
|
||||
oncommand="gPermissionManager.onAllPermissionsDeleted();"/>
|
||||
</hbox>
|
||||
<spacer flex="1"/>
|
||||
|
|
|
@ -47,7 +47,7 @@ let gSiteDataSettings = {
|
|||
|
||||
let brandShortName = document.getElementById("bundle_brand").getString("brandShortName");
|
||||
let settingsDescription = document.getElementById("settingsDescription");
|
||||
settingsDescription.textContent = this._prefStrBundle.getFormattedString("siteDataSettings.description", [brandShortName]);
|
||||
settingsDescription.textContent = this._prefStrBundle.getFormattedString("siteDataSettings2.description", [brandShortName]);
|
||||
|
||||
setEventListener("hostCol", "click", this.onClickTreeCol);
|
||||
setEventListener("usageCol", "click", this.onClickTreeCol);
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
onkeypress="gTranslationExceptions.onSiteKeyPress(event)"
|
||||
onselect="gTranslationExceptions.onSiteSelected();">
|
||||
<treecols>
|
||||
<treecol id="siteCol" label="&treehead.siteName.label;" flex="1"/>
|
||||
<treecol id="siteCol" label="&treehead.siteName2.label;" flex="1"/>
|
||||
</treecols>
|
||||
<treechildren/>
|
||||
</tree>
|
||||
|
|
|
@ -141,8 +141,8 @@ function defineCohort() {
|
|||
// the proper prefs must already be set.
|
||||
setCohort("optedOut");
|
||||
} else if (userOptedIn.e10s) {
|
||||
setCohort("optedIn");
|
||||
eligibleForMulti = true;
|
||||
setCohort("optedIn");
|
||||
} else if (temporaryDisqualification != "") {
|
||||
// Users who are disqualified by the backend (from multiprocessBlockPolicy)
|
||||
// can be put into either the test or control groups, because e10s will
|
||||
|
@ -152,25 +152,25 @@ function defineCohort() {
|
|||
// For these volatile disqualification reasons, however, we must not try
|
||||
// to activate e10s because the backend doesn't know about it. E10S_STATUS
|
||||
// here will be accumulated as "2 - Disabled", which is fine too.
|
||||
setCohort(`temp-disqualified-${temporaryDisqualification}`);
|
||||
Services.prefs.clearUserPref(PREF_TOGGLE_E10S);
|
||||
Services.prefs.clearUserPref(PREF_E10S_PROCESSCOUNT + ".web");
|
||||
setCohort(`temp-disqualified-${temporaryDisqualification}`);
|
||||
} else if (!disqualified && testThreshold < 1.0 &&
|
||||
temporaryQualification != "") {
|
||||
// Users who are qualified for e10s and on channels where some population
|
||||
// would not receive e10s can be pushed into e10s anyway via a temporary
|
||||
// qualification which overrides the user sample value when non-empty.
|
||||
Services.prefs.setBoolPref.set(PREF_TOGGLE_E10S, true);
|
||||
eligibleForMulti = true;
|
||||
setCohort(`temp-qualified-${temporaryQualification}`);
|
||||
Services.prefs.setBoolPref(PREF_TOGGLE_E10S, true);
|
||||
eligibleForMulti = true;
|
||||
} else if (testGroup) {
|
||||
setCohort(`${cohortPrefix}test`);
|
||||
Services.prefs.setBoolPref(PREF_TOGGLE_E10S, true);
|
||||
eligibleForMulti = true;
|
||||
setCohort(`${cohortPrefix}test`);
|
||||
} else {
|
||||
setCohort(`${cohortPrefix}control`);
|
||||
Services.prefs.clearUserPref(PREF_TOGGLE_E10S);
|
||||
Services.prefs.clearUserPref(PREF_E10S_PROCESSCOUNT + ".web");
|
||||
setCohort(`${cohortPrefix}control`);
|
||||
}
|
||||
|
||||
// Now determine if this user should be in the e10s-multi experiment.
|
||||
|
@ -207,10 +207,9 @@ function defineCohort() {
|
|||
let multiUserSample = getUserSample(true);
|
||||
for (let sampleName of Object.getOwnPropertyNames(buckets)) {
|
||||
if (multiUserSample < buckets[sampleName]) {
|
||||
setCohort(`${cohortPrefix}multiBucket${sampleName}`);
|
||||
|
||||
// NB: Coerce sampleName to an integer because this is an integer pref.
|
||||
Services.prefs.setIntPref(PREF_E10S_PROCESSCOUNT + ".web", +sampleName);
|
||||
setCohort(`${cohortPrefix}multiBucket${sampleName}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>e10srollout@mozilla.org</em:id>
|
||||
<em:version>2.0</em:version>
|
||||
<em:version>2.1</em:version>
|
||||
<em:type>2</em:type>
|
||||
<em:bootstrap>true</em:bootstrap>
|
||||
<em:multiprocessCompatible>true</em:multiprocessCompatible>
|
||||
|
|
|
@ -155,6 +155,7 @@ FormAutofillHandler.prototype = {
|
|||
_cacheValue: {
|
||||
allFieldNames: null,
|
||||
oneLineStreetAddress: null,
|
||||
matchingSelectOption: null,
|
||||
},
|
||||
|
||||
get allFieldNames() {
|
||||
|
@ -198,9 +199,48 @@ FormAutofillHandler.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
_matchSelectOptions(profile) {
|
||||
if (!this._cacheValue.matchingSelectOption) {
|
||||
this._cacheValue.matchingSelectOption = new WeakMap();
|
||||
}
|
||||
|
||||
for (let fieldName in profile) {
|
||||
let fieldDetail = this.getFieldDetailByName(fieldName);
|
||||
if (!fieldDetail) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let element = fieldDetail.elementWeakRef.get();
|
||||
if (!(element instanceof Ci.nsIDOMHTMLSelectElement)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let cache = this._cacheValue.matchingSelectOption.get(element) || {};
|
||||
let value = profile[fieldName];
|
||||
if (cache[value] && cache[value].get()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let option = FormAutofillUtils.findSelectOption(element, profile, fieldName);
|
||||
if (option) {
|
||||
cache[value] = Cu.getWeakReference(option);
|
||||
this._cacheValue.matchingSelectOption.set(element, cache);
|
||||
} else {
|
||||
if (cache[value]) {
|
||||
delete cache[value];
|
||||
this._cacheValue.matchingSelectOption.set(element, cache);
|
||||
}
|
||||
// Delete the field so the phishing hint won't treat it as a "also fill"
|
||||
// field.
|
||||
delete profile[fieldName];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getAdaptedProfiles(originalProfiles) {
|
||||
for (let profile of originalProfiles) {
|
||||
this._addressTransformer(profile);
|
||||
this._matchSelectOptions(profile);
|
||||
}
|
||||
return originalProfiles;
|
||||
},
|
||||
|
@ -237,7 +277,8 @@ FormAutofillHandler.prototype = {
|
|||
}
|
||||
this.changeFieldState(fieldDetail, "AUTO_FILLED");
|
||||
} else if (element instanceof Ci.nsIDOMHTMLSelectElement) {
|
||||
let option = FormAutofillUtils.findSelectOption(element, profile, fieldDetail.fieldName);
|
||||
let cache = this._cacheValue.matchingSelectOption.get(element) || {};
|
||||
let option = cache[value] && cache[value].get();
|
||||
if (!option) {
|
||||
continue;
|
||||
}
|
||||
|
@ -326,17 +367,21 @@ FormAutofillHandler.prototype = {
|
|||
if (element instanceof Ci.nsIDOMHTMLSelectElement) {
|
||||
// Unlike text input, select element is always previewed even if
|
||||
// the option is already selected.
|
||||
let option = FormAutofillUtils.findSelectOption(element, profile, fieldDetail.fieldName);
|
||||
element.previewValue = option ? option.text : "";
|
||||
this.changeFieldState(fieldDetail, option ? "PREVIEW" : "NORMAL");
|
||||
} else {
|
||||
// Skip the field if it already has text entered
|
||||
if (element.value) {
|
||||
continue;
|
||||
if (value) {
|
||||
let cache = this._cacheValue.matchingSelectOption.get(element) || {};
|
||||
let option = cache[value] && cache[value].get();
|
||||
if (option) {
|
||||
value = option.text || "";
|
||||
} else {
|
||||
value = "";
|
||||
}
|
||||
}
|
||||
element.previewValue = value;
|
||||
this.changeFieldState(fieldDetail, value ? "PREVIEW" : "NORMAL");
|
||||
} else if (element.value) {
|
||||
// Skip the field if it already has text entered.
|
||||
continue;
|
||||
}
|
||||
element.previewValue = value;
|
||||
this.changeFieldState(fieldDetail, value ? "PREVIEW" : "NORMAL");
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -323,7 +323,7 @@ function do_test(testcases, testFn) {
|
|||
let handler = new FormAutofillHandler(formLike);
|
||||
let promises = [];
|
||||
|
||||
handler.address.fieldDetails = testcase.addressFieldDetails;
|
||||
handler.fieldDetails = handler.address.fieldDetails = testcase.addressFieldDetails;
|
||||
handler.address.fieldDetails.forEach((field, index) => {
|
||||
let element = doc.querySelectorAll("input, select")[index];
|
||||
field.elementWeakRef = Cu.getWeakReference(element);
|
||||
|
@ -335,7 +335,8 @@ function do_test(testcases, testFn) {
|
|||
promises.push(...testFn(testcase, element));
|
||||
});
|
||||
|
||||
handler.autofillFormFields(testcase.profileData);
|
||||
let [adaptedProfile] = handler.getAdaptedProfiles([testcase.profileData]);
|
||||
handler.autofillFormFields(adaptedProfile);
|
||||
Assert.equal(handler.address.filledRecordGUID, testcase.profileData.guid,
|
||||
"Check if filledRecordGUID is set correctly");
|
||||
await Promise.all(promises);
|
||||
|
|
|
@ -12,6 +12,8 @@ const DEFAULT_PROFILE = {
|
|||
"address-line1": "2 Harrison St",
|
||||
"address-line2": "line2",
|
||||
"address-line3": "line3",
|
||||
"address-level1": "CA",
|
||||
"country": "US",
|
||||
};
|
||||
|
||||
const TESTCASES = [
|
||||
|
@ -28,6 +30,8 @@ const TESTCASES = [
|
|||
"address-line1": "2 Harrison St",
|
||||
"address-line2": "line2",
|
||||
"address-line3": "line3",
|
||||
"address-level1": "CA",
|
||||
"country": "US",
|
||||
}],
|
||||
},
|
||||
{
|
||||
|
@ -46,6 +50,8 @@ const TESTCASES = [
|
|||
"address-line1": "2 Harrison St",
|
||||
"address-line2": "line2",
|
||||
"address-line3": "line3",
|
||||
"address-level1": "CA",
|
||||
"country": "US",
|
||||
}],
|
||||
},
|
||||
{
|
||||
|
@ -62,6 +68,8 @@ const TESTCASES = [
|
|||
"address-line1": "2 Harrison St line2 line3",
|
||||
"address-line2": "line2",
|
||||
"address-line3": "line3",
|
||||
"address-level1": "CA",
|
||||
"country": "US",
|
||||
}],
|
||||
},
|
||||
{
|
||||
|
@ -79,6 +87,8 @@ const TESTCASES = [
|
|||
"address-line1": "2 Harrison St",
|
||||
"address-line2": "line2 line3",
|
||||
"address-line3": "line3",
|
||||
"address-level1": "CA",
|
||||
"country": "US",
|
||||
}],
|
||||
},
|
||||
{
|
||||
|
@ -96,6 +106,119 @@ const TESTCASES = [
|
|||
"address-line1": "2 Harrison St",
|
||||
"address-line2": "line2 line3",
|
||||
"address-line3": "line3",
|
||||
"address-level1": "CA",
|
||||
"country": "US",
|
||||
}],
|
||||
},
|
||||
{
|
||||
description: "Form with exact matching options in select",
|
||||
document: `<form>
|
||||
<select autocomplete="address-level1">
|
||||
<option id="option-address-level1-XX" value="XX">Dummy</option>
|
||||
<option id="option-address-level1-CA" value="CA">California</option>
|
||||
</select>
|
||||
</form>`,
|
||||
profileData: [Object.assign({}, DEFAULT_PROFILE)],
|
||||
expectedResult: [{
|
||||
"guid": "123",
|
||||
"street-address": "2 Harrison St\nline2\nline3",
|
||||
"-moz-street-address-one-line": "2 Harrison St line2 line3",
|
||||
"address-line1": "2 Harrison St",
|
||||
"address-line2": "line2",
|
||||
"address-line3": "line3",
|
||||
"address-level1": "CA",
|
||||
"country": "US",
|
||||
}],
|
||||
expectedOptionElements: [{
|
||||
"address-level1": "option-address-level1-CA",
|
||||
}],
|
||||
},
|
||||
{
|
||||
description: "Form with inexact matching options in select",
|
||||
document: `<form>
|
||||
<select autocomplete="address-level1">
|
||||
<option id="option-address-level1-XX" value="XX">Dummy</option>
|
||||
<option id="option-address-level1-OO" value="OO">California</option>
|
||||
</select>
|
||||
</form>`,
|
||||
profileData: [Object.assign({}, DEFAULT_PROFILE)],
|
||||
expectedResult: [{
|
||||
"guid": "123",
|
||||
"street-address": "2 Harrison St\nline2\nline3",
|
||||
"-moz-street-address-one-line": "2 Harrison St line2 line3",
|
||||
"address-line1": "2 Harrison St",
|
||||
"address-line2": "line2",
|
||||
"address-line3": "line3",
|
||||
"address-level1": "CA",
|
||||
"country": "US",
|
||||
}],
|
||||
expectedOptionElements: [{
|
||||
"address-level1": "option-address-level1-OO",
|
||||
}],
|
||||
},
|
||||
{
|
||||
description: "Form with value-omitted options in select",
|
||||
document: `<form>
|
||||
<select autocomplete="address-level1">
|
||||
<option id="option-address-level1-1" value="">Dummy</option>
|
||||
<option id="option-address-level1-2" value="">California</option>
|
||||
</select>
|
||||
</form>`,
|
||||
profileData: [Object.assign({}, DEFAULT_PROFILE)],
|
||||
expectedResult: [{
|
||||
"guid": "123",
|
||||
"street-address": "2 Harrison St\nline2\nline3",
|
||||
"-moz-street-address-one-line": "2 Harrison St line2 line3",
|
||||
"address-line1": "2 Harrison St",
|
||||
"address-line2": "line2",
|
||||
"address-line3": "line3",
|
||||
"address-level1": "CA",
|
||||
"country": "US",
|
||||
}],
|
||||
expectedOptionElements: [{
|
||||
"address-level1": "option-address-level1-2",
|
||||
}],
|
||||
},
|
||||
{
|
||||
description: "Form with options with the same value in select",
|
||||
document: `<form>
|
||||
<select autocomplete="address-level1">
|
||||
<option id="option-address-level1-same1" value="same">Dummy</option>
|
||||
<option id="option-address-level1-same2" value="same">California</option>
|
||||
</select>
|
||||
</form>`,
|
||||
profileData: [Object.assign({}, DEFAULT_PROFILE)],
|
||||
expectedResult: [{
|
||||
"guid": "123",
|
||||
"street-address": "2 Harrison St\nline2\nline3",
|
||||
"-moz-street-address-one-line": "2 Harrison St line2 line3",
|
||||
"address-line1": "2 Harrison St",
|
||||
"address-line2": "line2",
|
||||
"address-line3": "line3",
|
||||
"address-level1": "CA",
|
||||
"country": "US",
|
||||
}],
|
||||
expectedOptionElements: [{
|
||||
"address-level1": "option-address-level1-same2",
|
||||
}],
|
||||
},
|
||||
{
|
||||
description: "Form without matching options in select",
|
||||
document: `<form>
|
||||
<select autocomplete="address-level1">
|
||||
<option id="option-address-level1-dummy1" value="">Dummy</option>
|
||||
<option id="option-address-level1-dummy2" value="">Dummy 2</option>
|
||||
</select>
|
||||
</form>`,
|
||||
profileData: [Object.assign({}, DEFAULT_PROFILE)],
|
||||
expectedResult: [{
|
||||
"guid": "123",
|
||||
"street-address": "2 Harrison St\nline2\nline3",
|
||||
"-moz-street-address-one-line": "2 Harrison St line2 line3",
|
||||
"address-line1": "2 Harrison St",
|
||||
"address-line2": "line2",
|
||||
"address-line3": "line3",
|
||||
"country": "US",
|
||||
}],
|
||||
},
|
||||
];
|
||||
|
@ -113,6 +236,22 @@ for (let testcase of TESTCASES) {
|
|||
handler.collectFormFields();
|
||||
let adaptedAddresses = handler.getAdaptedProfiles(testcase.profileData);
|
||||
Assert.deepEqual(adaptedAddresses, testcase.expectedResult);
|
||||
|
||||
if (testcase.expectedOptionElements) {
|
||||
testcase.expectedOptionElements.forEach((expectedOptionElement, i) => {
|
||||
for (let field in expectedOptionElement) {
|
||||
let select = form.querySelector(`[autocomplete=${field}]`);
|
||||
let expectedOption = doc.getElementById(expectedOptionElement[field]);
|
||||
Assert.notEqual(expectedOption, null);
|
||||
|
||||
let value = testcase.profileData[i][field];
|
||||
let cache = handler._cacheValue.matchingSelectOption.get(select);
|
||||
let targetOption = cache[value] && cache[value].get();
|
||||
Assert.notEqual(targetOption, null);
|
||||
|
||||
Assert.equal(targetOption, expectedOption);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DIRS += [
|
||||
'activity-stream',
|
||||
'aushelper',
|
||||
'clicktoplay-rollout',
|
||||
'e10srollout',
|
||||
|
@ -36,9 +37,3 @@ if CONFIG['MOZ_MORTAR']:
|
|||
DIRS += [
|
||||
'mortar',
|
||||
]
|
||||
|
||||
# Nightly-only system add-ons
|
||||
if CONFIG['NIGHTLY_BUILD']:
|
||||
DIRS += [
|
||||
'activity-stream',
|
||||
]
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
<!ENTITY notificationsPolicy.label "Notifications">
|
||||
<!ENTITY notificationsPolicyLearnMore.label "Learn more">
|
||||
<!ENTITY notificationsPolicyDesc3.label "Choose which sites are allowed to send you notifications">
|
||||
<!ENTITY notificationsPolicyDesc4.label "Choose which websites are allowed to send you notifications">
|
||||
<!ENTITY notificationsPolicyButton.accesskey "h">
|
||||
<!ENTITY notificationsPolicyButton.label "Choose…">
|
||||
<!ENTITY notificationsDoNotDisturb.label "Do not disturb me">
|
||||
|
|
|
@ -19,20 +19,20 @@ acceptVeryLargeMinimumFont=Keep my changes anyway
|
|||
|
||||
#### Permissions Manager
|
||||
|
||||
trackingprotectionpermissionstext=You have disabled Tracking Protection on these sites.
|
||||
trackingprotectionpermissionstext2=You have disabled Tracking Protection on these websites.
|
||||
trackingprotectionpermissionstitle=Exceptions - Tracking Protection
|
||||
cookiepermissionstext=You can specify which websites are always or never allowed to use cookies. Type the exact address of the site you want to manage and then click Block, Allow for Session, or Allow.
|
||||
cookiepermissionstitle=Exceptions - Cookies
|
||||
addonspermissionstext=You can specify which websites are allowed to install add-ons. Type the exact address of the site you want to allow and then click Allow.
|
||||
addons_permissions_title=Allowed Sites - Add-ons Installation
|
||||
addons_permissions_title2=Allowed Websites - Add-ons Installation
|
||||
popuppermissionstext=You can specify which websites are allowed to open pop-up windows. Type the exact address of the site you want to allow and then click Allow.
|
||||
popuppermissionstitle=Allowed Sites - Pop-ups
|
||||
popuppermissionstitle2=Allowed Websites - Pop-ups
|
||||
notificationspermissionstext4=Control which websites are always or never allowed to send you notifications. If you remove a site, it will need to request permission again.
|
||||
notificationspermissionstitle=Notification Permissions
|
||||
invalidURI=Please enter a valid hostname
|
||||
invalidURITitle=Invalid Hostname Entered
|
||||
savedLoginsExceptions_title=Exceptions - Saved Logins
|
||||
savedLoginsExceptions_desc=Logins for the following sites will not be saved:
|
||||
savedLoginsExceptions_desc2=Logins for the following websites will not be saved:
|
||||
|
||||
#### Block List Manager
|
||||
|
||||
|
@ -51,7 +51,7 @@ mozNameTemplate=%1$S %2$S
|
|||
mozstdName=Disconnect.me basic protection (Recommended).
|
||||
mozstdDesc=Allows some trackers so websites function properly.
|
||||
mozfullName=Disconnect.me strict protection.
|
||||
mozfullDesc=Blocks known trackers. Some sites may not function properly.
|
||||
mozfullDesc2=Blocks known trackers. Some websites may not function properly.
|
||||
# LOCALIZATION NOTE (blocklistChangeRequiresRestart): %S = brandShortName
|
||||
blocklistChangeRequiresRestart=%S must restart to change block lists.
|
||||
|
||||
|
@ -191,8 +191,8 @@ clearSiteDataNow=Clear Now
|
|||
persistent=Persistent
|
||||
siteUsage=%1$S %2$S
|
||||
acceptRemove=Remove
|
||||
# LOCALIZATION NOTE (siteDataSettings.description): %S = brandShortName
|
||||
siteDataSettings.description=The following websites store site data on your computer. %S keeps data from sites with persistent storage until you delete it, and deletes data from sites with non-persistent storage as space is needed.
|
||||
# LOCALIZATION NOTE (siteDataSettings2.description): %S = brandShortName
|
||||
siteDataSettings2.description=The following websites store site data on your computer. %S keeps data from websites with persistent storage until you delete it, and deletes data from websites with non-persistent storage as space is needed.
|
||||
# LOCALIZATION NOTE (removeAllSiteData, removeAllSiteDataShown):
|
||||
# removeAllSiteData and removeAllSiteDataShown are both used on the same one button,
|
||||
# never displayed together and can share the same accesskey.
|
||||
|
|
|
@ -43,8 +43,8 @@
|
|||
<!ENTITY suggestionSettings.label "Change preferences for search engine suggestions…">
|
||||
<!ENTITY suggestionSettings.accesskey "g">
|
||||
|
||||
<!ENTITY acceptCookies.label "Accept cookies from sites">
|
||||
<!ENTITY acceptCookies.accesskey "A">
|
||||
<!ENTITY acceptCookies2.label "Accept cookies from websites">
|
||||
<!ENTITY acceptCookies2.accesskey "A">
|
||||
|
||||
<!ENTITY acceptThirdParty.pre.label "Accept third-party cookies:">
|
||||
<!ENTITY acceptThirdParty.pre.accesskey "y">
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
<!ENTITY general.label "General">
|
||||
|
||||
<!ENTITY warnOnAddonInstall.label "Warn you when sites try to install add-ons">
|
||||
<!ENTITY warnOnAddonInstall.accesskey "W">
|
||||
<!ENTITY warnOnAddonInstall2.label "Warn you when websites try to install add-ons">
|
||||
<!ENTITY warnOnAddonInstall2.accesskey "W">
|
||||
|
||||
<!-- LOCALIZATION NOTE (enableSafeBrowsing.label, blockDownloads.label, blockUncommonUnwanted.label):
|
||||
It is important that wording follows the guidelines outlined on this page:
|
||||
|
@ -26,8 +26,8 @@
|
|||
|
||||
<!ENTITY logins.label "Logins">
|
||||
|
||||
<!ENTITY rememberLogins.label "Remember logins for sites">
|
||||
<!ENTITY rememberLogins.accesskey "R">
|
||||
<!ENTITY rememberLogins2.label "Remember logins for websites">
|
||||
<!ENTITY rememberLogins2.accesskey "R">
|
||||
<!ENTITY passwordExceptions.label "Exceptions…">
|
||||
<!ENTITY passwordExceptions.accesskey "x">
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<!ENTITY blockPopups.accesskey "B">
|
||||
|
||||
<!ENTITY notificationsPolicyLearnMore.label "Learn more">
|
||||
<!ENTITY notificationsPolicyDesc3.label "Choose which sites are allowed to send you notifications">
|
||||
<!ENTITY notificationsPolicyDesc4.label "Choose which websites are allowed to send you notifications">
|
||||
<!ENTITY notificationsPolicyButton.accesskey "h">
|
||||
<!ENTITY notificationsPolicyButton.label "Choose…">
|
||||
<!ENTITY notificationsDoNotDisturb.label "Do not disturb me">
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
<!ENTITY window.title "Exceptions">
|
||||
<!ENTITY window.width "45em">
|
||||
|
||||
<!ENTITY treehead.sitename.label "Site">
|
||||
<!ENTITY treehead.sitename2.label "Website">
|
||||
<!ENTITY treehead.status.label "Status">
|
||||
<!ENTITY removepermission.label "Remove Site">
|
||||
<!ENTITY removepermission.accesskey "R">
|
||||
<!ENTITY removeallpermissions.label "Remove All Sites">
|
||||
<!ENTITY removeallpermissions.accesskey "e">
|
||||
<!ENTITY removepermission2.label "Remove Website">
|
||||
<!ENTITY removepermission2.accesskey "R">
|
||||
<!ENTITY removeallpermissions2.label "Remove All Websites">
|
||||
<!ENTITY removeallpermissions2.accesskey "e">
|
||||
<!ENTITY address.label "Address of website:">
|
||||
<!ENTITY address.accesskey "d">
|
||||
<!ENTITY block.label "Block">
|
||||
|
|
|
@ -19,20 +19,20 @@ acceptVeryLargeMinimumFont=Keep my changes anyway
|
|||
|
||||
#### Permissions Manager
|
||||
|
||||
trackingprotectionpermissionstext=You have disabled Tracking Protection on these sites.
|
||||
trackingprotectionpermissionstext2=You have disabled Tracking Protection on these websites.
|
||||
trackingprotectionpermissionstitle=Exceptions - Tracking Protection
|
||||
cookiepermissionstext=You can specify which websites are always or never allowed to use cookies. Type the exact address of the site you want to manage and then click Block, Allow for Session, or Allow.
|
||||
cookiepermissionstitle=Exceptions - Cookies
|
||||
addonspermissionstext=You can specify which websites are allowed to install add-ons. Type the exact address of the site you want to allow and then click Allow.
|
||||
addons_permissions_title=Allowed Sites - Add-ons Installation
|
||||
addons_permissions_title2=Allowed Websites - Add-ons Installation
|
||||
popuppermissionstext=You can specify which websites are allowed to open pop-up windows. Type the exact address of the site you want to allow and then click Allow.
|
||||
popuppermissionstitle=Allowed Sites - Pop-ups
|
||||
popuppermissionstitle2=Allowed Websites - Pop-ups
|
||||
notificationspermissionstext4=Control which websites are always or never allowed to send you notifications. If you remove a site, it will need to request permission again.
|
||||
notificationspermissionstitle=Notification Permissions
|
||||
invalidURI=Please enter a valid hostname
|
||||
invalidURITitle=Invalid Hostname Entered
|
||||
savedLoginsExceptions_title=Exceptions - Saved Logins
|
||||
savedLoginsExceptions_desc=Logins for the following sites will not be saved:
|
||||
savedLoginsExceptions_desc2=Logins for the following websites will not be saved:
|
||||
|
||||
#### Block List Manager
|
||||
|
||||
|
@ -51,7 +51,7 @@ mozNameTemplate=%1$S %2$S
|
|||
mozstdName=Disconnect.me basic protection (Recommended).
|
||||
mozstdDesc=Allows some trackers so websites function properly.
|
||||
mozfullName=Disconnect.me strict protection.
|
||||
mozfullDesc=Blocks known trackers. Some sites may not function properly.
|
||||
mozfullDesc2=Blocks known trackers. Some websites may not function properly.
|
||||
# LOCALIZATION NOTE (blocklistChangeRequiresRestart): %S = brandShortName
|
||||
blocklistChangeRequiresRestart=%S must restart to change block lists.
|
||||
|
||||
|
@ -191,8 +191,8 @@ clearSiteDataNow=Clear Now
|
|||
persistent=Persistent
|
||||
siteUsage=%1$S %2$S
|
||||
acceptRemove=Remove
|
||||
# LOCALIZATION NOTE (siteDataSettings.description): %S = brandShortName
|
||||
siteDataSettings.description=The following websites store site data on your computer. %S keeps data from sites with persistent storage until you delete it, and deletes data from sites with non-persistent storage as space is needed.
|
||||
# LOCALIZATION NOTE (siteDataSettings2.description): %S = brandShortName
|
||||
siteDataSettings2.description=The following websites store site data on your computer. %S keeps data from websites with persistent storage until you delete it, and deletes data from websites with non-persistent storage as space is needed.
|
||||
# LOCALIZATION NOTE (removeAllSiteData, removeAllSiteDataShown):
|
||||
# removeAllSiteData and removeAllSiteDataShown are both used on the same one button,
|
||||
# never displayed together and can share the same accesskey.
|
||||
|
|
|
@ -46,8 +46,8 @@
|
|||
|
||||
<!ENTITY suggestionSettings2.label "Change preferences for search engine suggestions">
|
||||
|
||||
<!ENTITY acceptCookies.label "Accept cookies from sites">
|
||||
<!ENTITY acceptCookies.accesskey "A">
|
||||
<!ENTITY acceptCookies2.label "Accept cookies from websites">
|
||||
<!ENTITY acceptCookies2.accesskey "A">
|
||||
|
||||
<!ENTITY acceptThirdParty.pre.label "Accept third-party cookies:">
|
||||
<!ENTITY acceptThirdParty.pre.accesskey "y">
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
<!ENTITY security.label "Security">
|
||||
<!ENTITY phishingProtection.label "Phishing Protection">
|
||||
|
||||
<!ENTITY warnOnAddonInstall.label "Warn you when sites try to install add-ons">
|
||||
<!ENTITY warnOnAddonInstall.accesskey "W">
|
||||
<!ENTITY warnOnAddonInstall2.label "Warn you when websites try to install add-ons">
|
||||
<!ENTITY warnOnAddonInstall2.accesskey "W">
|
||||
|
||||
<!-- LOCALIZATION NOTE (enableSafeBrowsing.label, blockDownloads.label, blockUncommonUnwanted.label):
|
||||
It is important that wording follows the guidelines outlined on this page:
|
||||
|
@ -27,8 +27,8 @@
|
|||
|
||||
<!ENTITY formsAndPasswords.label "Forms & Passwords">
|
||||
|
||||
<!ENTITY rememberLogins1.label "Remember logins and passwords for sites">
|
||||
<!ENTITY rememberLogins1.accesskey "R">
|
||||
<!ENTITY rememberLogins2.label "Remember logins and passwords for websites">
|
||||
<!ENTITY rememberLogins2.accesskey "R">
|
||||
<!ENTITY passwordExceptions.label "Exceptions…">
|
||||
<!ENTITY passwordExceptions.accesskey "x">
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<!ENTITY removeAllLanguages.accesskey "e">
|
||||
|
||||
<!ENTITY noTranslationForSites.label "Translation will not be offered for the following sites:">
|
||||
<!ENTITY treehead.siteName.label "Sites">
|
||||
<!ENTITY treehead.siteName2.label "Websites">
|
||||
<!ENTITY removeSite.label "Remove Site">
|
||||
<!ENTITY removeSite.accesskey "S">
|
||||
<!ENTITY removeAllSites.label "Remove All Sites">
|
||||
|
|
|
@ -700,17 +700,18 @@ groupbox {
|
|||
|
||||
menulist[indicator=true] > menupopup menuitem:not([image]) > .menu-iconic-left {
|
||||
display: -moz-box;
|
||||
width: 8px;
|
||||
min-width: auto; /* Override the min-width defined in menu.css */
|
||||
height: 10px;
|
||||
margin-inline-end: 6px;
|
||||
}
|
||||
|
||||
menulist[indicator=true] > menupopup menuitem:not([image]) > .menu-iconic-left > .menu-iconic-icon {
|
||||
width: 8px;
|
||||
height: 10px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
menulist[indicator=true] > menupopup menuitem[indicator=true]:not([image]) > .menu-iconic-left > .menu-iconic-icon {
|
||||
list-style-image: url(chrome://browser/skin/preferences/in-content-new/search-arrow-indicator.svg);
|
||||
width: 8px;
|
||||
height: 10px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
menulist[indicator=true] > menupopup menuitem[indicator=true]:not([image]) > .menu-iconic-left > .menu-iconic-icon:-moz-locale-dir(rtl) {
|
||||
|
|
|
@ -1066,6 +1066,7 @@ Scanner.prototype = {
|
|||
// aToken.mIdent may be "url" at this point; clear that out
|
||||
aToken.mIdent.length = 0;
|
||||
|
||||
let hasString = false;
|
||||
let ch = this.Peek();
|
||||
// Do we have a string?
|
||||
if (ch == QUOTATION_MARK || ch == APOSTROPHE) {
|
||||
|
@ -1074,6 +1075,7 @@ Scanner.prototype = {
|
|||
aToken.mType = eCSSToken_Bad_URL;
|
||||
return;
|
||||
}
|
||||
hasString = true;
|
||||
} else {
|
||||
// Otherwise, this is the start of a non-quoted url (which may be empty).
|
||||
aToken.mSymbol = 0;
|
||||
|
@ -1092,6 +1094,25 @@ Scanner.prototype = {
|
|||
}
|
||||
} else {
|
||||
aToken.mType = eCSSToken_Bad_URL;
|
||||
if (!hasString) {
|
||||
// Consume until before the next right parenthesis, which follows
|
||||
// how <bad-url-token> is consumed in CSS Syntax 3 spec.
|
||||
// Note that, we only do this when "url(" is not followed by a
|
||||
// string, because in the spec, "url(" followed by a string is
|
||||
// handled as a url function rather than a <url-token>, so the
|
||||
// rest of content before ")" should be consumed in balance,
|
||||
// which will be done by the parser.
|
||||
// The closing ")" is not consumed here. It is left to the parser
|
||||
// so that the parser can handle both cases.
|
||||
do {
|
||||
if (IsVertSpace(ch)) {
|
||||
this.AdvanceLine();
|
||||
} else {
|
||||
this.Advance();
|
||||
}
|
||||
ch = this.Peek();
|
||||
} while (ch >= 0 && ch != RIGHT_PARENTHESIS);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -128,8 +128,7 @@ var LEX_TESTS = [
|
|||
["url:http://example.com"]],
|
||||
// In CSS Level 3, this is an ordinary URL, not a BAD_URL.
|
||||
["url(http://example.com", ["url:http://example.com"]],
|
||||
// See bug 1153981 to understand why this gets a SYMBOL token.
|
||||
["url(http://example.com @", ["bad_url:http://example.com", "symbol:@"]],
|
||||
["url(http://example.com @", ["bad_url:http://example.com"]],
|
||||
["quo\\ting", ["ident:quoting"]],
|
||||
["'bad string\n", ["bad_string:bad string", "whitespace"]],
|
||||
["~=", ["includes"]],
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
file_CrossSiteXHR_server.sjs
|
||||
file_CrossSiteXHR_inner.html
|
||||
file_cors_logging_test.html
|
||||
head.js
|
||||
|
||||
[browser_CORS-console-warnings.js]
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Description of the test:
|
||||
* Ensure that CORS warnings are printed to the web console.
|
||||
*
|
||||
* This test uses the same tests as the plain mochitest, but needs access to
|
||||
* the console.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
function console_observer(subject, topic, data) {
|
||||
var message = subject.wrappedJSObject.arguments[0];
|
||||
ok(false, message);
|
||||
};
|
||||
|
||||
var webconsole = null;
|
||||
var messages_seen = 0;
|
||||
var expected_messages = 50;
|
||||
|
||||
function on_new_message(event, new_messages) {
|
||||
for (let message of new_messages) {
|
||||
let elem = message.node;
|
||||
let text = elem.textContent;
|
||||
if (text.match('Cross-Origin Request Blocked:')) {
|
||||
ok(true, "message is: " + text);
|
||||
messages_seen++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function do_cleanup() {
|
||||
if (webconsole) {
|
||||
webconsole.ui.off("new-messages", on_new_message);
|
||||
}
|
||||
yield unsetCookiePref();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set e10s related preferences in the test environment.
|
||||
* @return {Promise} promise that resolves when preferences are set.
|
||||
*/
|
||||
function setCookiePref() {
|
||||
return new Promise(resolve =>
|
||||
// accept all cookies so that the CORS requests will send the right cookies
|
||||
SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["network.cookie.cookieBehavior", 0],
|
||||
]
|
||||
}, resolve));
|
||||
}
|
||||
|
||||
/**
|
||||
* Unset e10s related preferences in the test environment.
|
||||
* @return {Promise} promise that resolves when preferences are unset.
|
||||
*/
|
||||
function unsetCookiePref() {
|
||||
return new Promise(resolve => {
|
||||
SpecialPowers.popPrefEnv(resolve);
|
||||
});
|
||||
}
|
||||
|
||||
//jscs:disable
|
||||
add_task(function*() {
|
||||
//jscs:enable
|
||||
// A longer timeout is necessary for this test than the plain mochitests
|
||||
// due to opening a new tab with the web console.
|
||||
requestLongerTimeout(4);
|
||||
registerCleanupFunction(do_cleanup);
|
||||
yield setCookiePref();
|
||||
|
||||
let test_uri = "http://mochi.test:8888/browser/dom/security/test/cors/file_cors_logging_test.html";
|
||||
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
|
||||
|
||||
let toolbox = yield openToolboxForTab(tab, "webconsole");
|
||||
ok(toolbox, "Got toolbox");
|
||||
let hud = toolbox.getCurrentPanel().hud;
|
||||
ok(hud, "Got hud");
|
||||
|
||||
if (!webconsole) {
|
||||
registerCleanupFunction(do_cleanup);
|
||||
hud.ui.on("new-messages", on_new_message);
|
||||
webconsole = hud;
|
||||
}
|
||||
|
||||
BrowserTestUtils.loadURI(gBrowser, test_uri);
|
||||
|
||||
yield BrowserTestUtils.waitForLocationChange(gBrowser, test_uri+"#finished");
|
||||
|
||||
// Different OS combinations
|
||||
ok(messages_seen > 0, "Saw " + messages_seen + " messages.");
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
});
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,69 @@
|
|||
'use strict';
|
||||
|
||||
var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||
function scopedCuImport(path) {
|
||||
const scope = {};
|
||||
Cu.import(path, scope);
|
||||
return scope;
|
||||
}
|
||||
const {loader, require} = scopedCuImport("resource://devtools/shared/Loader.jsm");
|
||||
const {TargetFactory} = require("devtools/client/framework/target");
|
||||
const {Utils: WebConsoleUtils} =
|
||||
require("devtools/client/webconsole/utils");
|
||||
let { gDevTools } = require("devtools/client/framework/devtools");
|
||||
loader.lazyGetter(this, "HUDService", () => require("devtools/client/webconsole/webconsole"));
|
||||
loader.lazyGetter(this, "HUDService", () => require("devtools/client/webconsole/hudservice"));
|
||||
let promise = require("promise");
|
||||
|
||||
/**
|
||||
* Open the toolbox in a given tab.
|
||||
* @param {XULNode} tab The tab the toolbox should be opened in.
|
||||
* @param {String} toolId Optional. The ID of the tool to be selected.
|
||||
* @param {String} hostType Optional. The type of toolbox host to be used.
|
||||
* @return {Promise} Resolves with the toolbox, when it has been opened.
|
||||
*/
|
||||
var openToolboxForTab = Task.async(function* (tab, toolId, hostType) {
|
||||
info("Opening the toolbox");
|
||||
|
||||
let toolbox;
|
||||
let target = TargetFactory.forTab(tab);
|
||||
yield target.makeRemote();
|
||||
|
||||
// Check if the toolbox is already loaded.
|
||||
toolbox = gDevTools.getToolbox(target);
|
||||
if (toolbox) {
|
||||
if (!toolId || (toolId && toolbox.getPanel(toolId))) {
|
||||
info("Toolbox is already opened");
|
||||
return toolbox;
|
||||
}
|
||||
}
|
||||
|
||||
// If not, load it now.
|
||||
toolbox = yield gDevTools.showToolbox(target, toolId, hostType);
|
||||
|
||||
// Make sure that the toolbox frame is focused.
|
||||
yield new Promise(resolve => waitForFocus(resolve, toolbox.win));
|
||||
|
||||
info("Toolbox opened and focused");
|
||||
|
||||
return toolbox;
|
||||
});
|
||||
|
||||
/**
|
||||
* Find multiple messages in the output.
|
||||
*
|
||||
* @param object hud
|
||||
* The web console.
|
||||
* @param string text
|
||||
* A substring that can be found in the message.
|
||||
* @param selector [optional]
|
||||
* The selector to use in finding the message.
|
||||
*/
|
||||
function findMessages(hud, text, selector = ".message") {
|
||||
const messages = hud.ui.experimentalOutputNode.querySelectorAll(selector);
|
||||
const elements = Array.prototype.filter.call(
|
||||
messages,
|
||||
(el) => el.textContent.includes(text)
|
||||
);
|
||||
return elements;
|
||||
}
|
|
@ -28,6 +28,7 @@ MOCHITEST_CHROME_MANIFESTS += [
|
|||
]
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += [
|
||||
'cors/browser.ini',
|
||||
'csp/browser.ini',
|
||||
'general/browser.ini',
|
||||
'hsts/browser.ini',
|
||||
|
|
|
@ -1091,7 +1091,7 @@ PWebRenderBridgeChild*
|
|||
CompositorBridgeChild::AllocPWebRenderBridgeChild(const wr::PipelineId& aPipelineId,
|
||||
const LayoutDeviceIntSize&,
|
||||
TextureFactoryIdentifier*,
|
||||
uint32_t *aIdNamespace)
|
||||
wr::IdNamespace *aIdNamespace)
|
||||
{
|
||||
WebRenderBridgeChild* child = new WebRenderBridgeChild(aPipelineId);
|
||||
child->AddIPDLReference();
|
||||
|
|
|
@ -210,7 +210,7 @@ public:
|
|||
PWebRenderBridgeChild* AllocPWebRenderBridgeChild(const wr::PipelineId& aPipelineId,
|
||||
const LayoutDeviceIntSize&,
|
||||
TextureFactoryIdentifier*,
|
||||
uint32_t*) override;
|
||||
wr::IdNamespace*) override;
|
||||
bool DeallocPWebRenderBridgeChild(PWebRenderBridgeChild* aActor) override;
|
||||
|
||||
uint64_t DeviceResetSequenceNumber() const {
|
||||
|
|
|
@ -1678,7 +1678,7 @@ PWebRenderBridgeParent*
|
|||
CompositorBridgeParent::AllocPWebRenderBridgeParent(const wr::PipelineId& aPipelineId,
|
||||
const LayoutDeviceIntSize& aSize,
|
||||
TextureFactoryIdentifier* aTextureFactoryIdentifier,
|
||||
uint32_t* aIdNamespace)
|
||||
wr::IdNamespace* aIdNamespace)
|
||||
{
|
||||
#ifndef MOZ_BUILD_WEBRENDER
|
||||
// Extra guard since this in the parent process and we don't want a malicious
|
||||
|
@ -1699,15 +1699,14 @@ CompositorBridgeParent::AllocPWebRenderBridgeParent(const wr::PipelineId& aPipel
|
|||
new AsyncImagePipelineManager(WebRenderBridgeParent::AllocIdNameSpace());
|
||||
if (!api) {
|
||||
mWrBridge = WebRenderBridgeParent::CreateDestroyed();
|
||||
*aIdNamespace = mWrBridge->GetIdNameSpace();
|
||||
*aIdNamespace = mWrBridge->GetIdNamespace();
|
||||
*aTextureFactoryIdentifier = TextureFactoryIdentifier(LayersBackend::LAYERS_NONE);
|
||||
return mWrBridge;
|
||||
}
|
||||
MOZ_ASSERT(api); // TODO have a fallback
|
||||
api->SetRootPipeline(aPipelineId);
|
||||
RefPtr<CompositorAnimationStorage> animStorage = GetAnimationStorage();
|
||||
mWrBridge = new WebRenderBridgeParent(this, aPipelineId, mWidget, nullptr, Move(api), Move(asyncMgr), Move(animStorage));
|
||||
*aIdNamespace = mWrBridge->GetIdNameSpace();
|
||||
*aIdNamespace = mWrBridge->GetIdNamespace();
|
||||
|
||||
mCompositorScheduler = mWrBridge->CompositorScheduler();
|
||||
MOZ_ASSERT(mCompositorScheduler);
|
||||
|
|
|
@ -459,7 +459,7 @@ public:
|
|||
PWebRenderBridgeParent* AllocPWebRenderBridgeParent(const wr::PipelineId& aPipelineId,
|
||||
const LayoutDeviceIntSize& aSize,
|
||||
TextureFactoryIdentifier* aTextureFactoryIdentifier,
|
||||
uint32_t* aIdNamespace) override;
|
||||
wr::IdNamespace* aIdNamespace) override;
|
||||
bool DeallocPWebRenderBridgeParent(PWebRenderBridgeParent* aActor) override;
|
||||
RefPtr<WebRenderBridgeParent> GetWebRenderBridgeParent() const;
|
||||
Maybe<TimeStamp> GetTestingTimeStamp() const;
|
||||
|
|
|
@ -194,7 +194,7 @@ PWebRenderBridgeParent*
|
|||
CrossProcessCompositorBridgeParent::AllocPWebRenderBridgeParent(const wr::PipelineId& aPipelineId,
|
||||
const LayoutDeviceIntSize& aSize,
|
||||
TextureFactoryIdentifier* aTextureFactoryIdentifier,
|
||||
uint32_t *aIdNamespace)
|
||||
wr::IdNamespace *aIdNamespace)
|
||||
{
|
||||
#ifndef MOZ_BUILD_WEBRENDER
|
||||
// Extra guard since this in the parent process and we don't want a malicious
|
||||
|
@ -218,7 +218,7 @@ CrossProcessCompositorBridgeParent::AllocPWebRenderBridgeParent(const wr::Pipeli
|
|||
// This was observed during Tab move between different windows.
|
||||
NS_WARNING("Created child without a matching parent?");
|
||||
parent = WebRenderBridgeParent::CreateDestroyed();
|
||||
*aIdNamespace = parent->GetIdNameSpace();
|
||||
*aIdNamespace = parent->GetIdNamespace();
|
||||
*aTextureFactoryIdentifier = TextureFactoryIdentifier(LayersBackend::LAYERS_NONE);
|
||||
return parent;
|
||||
}
|
||||
|
@ -233,7 +233,7 @@ CrossProcessCompositorBridgeParent::AllocPWebRenderBridgeParent(const wr::Pipeli
|
|||
sIndirectLayerTrees[layersId].mCrossProcessParent = this;
|
||||
sIndirectLayerTrees[layersId].mWrBridge = parent;
|
||||
*aTextureFactoryIdentifier = parent->GetTextureFactoryIdentifier();
|
||||
*aIdNamespace = parent->GetIdNameSpace();
|
||||
*aIdNamespace = parent->GetIdNamespace();
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
|
|
@ -148,7 +148,7 @@ public:
|
|||
PWebRenderBridgeParent* AllocPWebRenderBridgeParent(const wr::PipelineId& aPipelineId,
|
||||
const LayoutDeviceIntSize& aSize,
|
||||
TextureFactoryIdentifier* aTextureFactoryIdentifier,
|
||||
uint32_t* aIdNamespace) override;
|
||||
wr::IdNamespace* aIdNamespace) override;
|
||||
bool DeallocPWebRenderBridgeParent(PWebRenderBridgeParent* aActor) override;
|
||||
|
||||
void ObserveLayerUpdate(uint64_t aLayersId, uint64_t aEpoch, bool aActive) override;
|
||||
|
|
|
@ -39,6 +39,7 @@ using class mozilla::layers::FrameUniformityData from "mozilla/layers/FrameUnifo
|
|||
using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h";
|
||||
using mozilla::layers::CompositorOptions from "mozilla/layers/CompositorOptions.h";
|
||||
using mozilla::wr::PipelineId from "mozilla/webrender/WebRenderTypes.h";
|
||||
using mozilla::wr::IdNamespace from "mozilla/webrender/WebRenderTypes.h";
|
||||
using base::ProcessId from "base/process.h";
|
||||
using mozilla::wr::MaybeExternalImageId from "mozilla/webrender/WebRenderTypes.h";
|
||||
|
||||
|
@ -250,7 +251,7 @@ parent:
|
|||
|
||||
// The pipelineId is the same as the layersId
|
||||
sync PWebRenderBridge(PipelineId pipelineId, LayoutDeviceIntSize aSize)
|
||||
returns (TextureFactoryIdentifier textureFactoryIdentifier, uint32_t idNamespace); //XXX: use the WrIdNamespace type
|
||||
returns (TextureFactoryIdentifier textureFactoryIdentifier, IdNamespace idNamespace);
|
||||
|
||||
sync CheckContentOnlyTDR(uint32_t sequenceNum)
|
||||
returns (bool isContentOnlyTDR);
|
||||
|
|
|
@ -24,6 +24,7 @@ using mozilla::wr::ImageKey from "mozilla/webrender/WebRenderTypes.h";
|
|||
using mozilla::wr::FontKey from "mozilla/webrender/WebRenderTypes.h";
|
||||
using mozilla::wr::PipelineId from "mozilla/webrender/WebRenderTypes.h";
|
||||
using mozilla::wr::BuiltDisplayListDescriptor from "mozilla/webrender/webrender_ffi.h";
|
||||
using mozilla::wr::IdNamespace from "mozilla/webrender/WebRenderTypes.h";
|
||||
using mozilla::layers::WebRenderScrollData from "mozilla/layers/WebRenderScrollData.h";
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -56,10 +57,10 @@ parent:
|
|||
async DPBegin(IntSize aSize);
|
||||
async DPEnd(IntSize aSize, WebRenderParentCommand[] commands, OpDestroy[] toDestroy, uint64_t fwdTransactionId, uint64_t transactionId,
|
||||
LayoutSize aContentSize, ByteBuffer aDL, BuiltDisplayListDescriptor aDLDesc,
|
||||
WebRenderScrollData aScrollData, uint32_t idNameSpace, TimeStamp fwdTime);
|
||||
WebRenderScrollData aScrollData, IdNamespace aIdNamespace, TimeStamp fwdTime);
|
||||
sync DPSyncEnd(IntSize aSize, WebRenderParentCommand[] commands, OpDestroy[] toDestroy, uint64_t fwdTransactionId, uint64_t transactionId,
|
||||
LayoutSize aContentSize, ByteBuffer aDL, BuiltDisplayListDescriptor aDLDesc,
|
||||
WebRenderScrollData aScrollData, uint32_t idNameSpace, TimeStamp fwdTime);
|
||||
WebRenderScrollData aScrollData, IdNamespace aIdNamespace, TimeStamp fwdTime);
|
||||
async ParentCommands(WebRenderParentCommand[] commands);
|
||||
sync DPGetSnapshot(PTexture texture);
|
||||
async AddPipelineIdForAsyncCompositable(PipelineId aImageId, CompositableHandle aHandle);
|
||||
|
@ -88,7 +89,7 @@ parent:
|
|||
async Shutdown();
|
||||
sync ShutdownSync();
|
||||
child:
|
||||
async WrUpdated(uint32_t newIdNameSpace);
|
||||
async WrUpdated(IdNamespace aNewIdNamespace);
|
||||
async __delete__();
|
||||
};
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ AsyncImagePipelineManager::AsyncImagePipeline::AsyncImagePipeline()
|
|||
, mMixBlendMode(wr::MixBlendMode::Normal)
|
||||
{}
|
||||
|
||||
AsyncImagePipelineManager::AsyncImagePipelineManager(uint32_t aIdNamespace)
|
||||
AsyncImagePipelineManager::AsyncImagePipelineManager(wr::IdNamespace aIdNamespace)
|
||||
: mIdNamespace(aIdNamespace)
|
||||
, mResourceId(0)
|
||||
, mAsyncImageEpoch(0)
|
||||
|
|
|
@ -34,7 +34,7 @@ class AsyncImagePipelineManager final
|
|||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AsyncImagePipelineManager)
|
||||
|
||||
explicit AsyncImagePipelineManager(uint32_t aIdNamespace);
|
||||
explicit AsyncImagePipelineManager(wr::IdNamespace aIdNamespace);
|
||||
|
||||
protected:
|
||||
~AsyncImagePipelineManager();
|
||||
|
@ -94,11 +94,11 @@ private:
|
|||
void DeleteOldAsyncImages(wr::WebRenderAPI* aApi);
|
||||
|
||||
uint32_t GetNextResourceId() { return ++mResourceId; }
|
||||
uint32_t GetNamespace() { return mIdNamespace; }
|
||||
wr::IdNamespace GetNamespace() { return mIdNamespace; }
|
||||
wr::ImageKey GenerateImageKey()
|
||||
{
|
||||
wr::ImageKey key;
|
||||
key.mNamespace.mHandle = GetNamespace();
|
||||
key.mNamespace = GetNamespace();
|
||||
key.mHandle = GetNextResourceId();
|
||||
return key;
|
||||
}
|
||||
|
@ -141,7 +141,7 @@ private:
|
|||
nsTArray<wr::ImageKey>& aKeys,
|
||||
nsTArray<wr::ImageKey>& aKeysToDelete);
|
||||
|
||||
uint32_t mIdNamespace;
|
||||
wr::IdNamespace mIdNamespace;
|
||||
uint32_t mResourceId;
|
||||
|
||||
nsClassHashtable<nsUint64HashKey, PipelineTexturesHolder> mPipelineTexturesHolders;
|
||||
|
|
|
@ -23,7 +23,7 @@ WebRenderBridgeChild::WebRenderBridgeChild(const wr::PipelineId& aPipelineId)
|
|||
: mReadLockSequenceNumber(0)
|
||||
, mIsInTransaction(false)
|
||||
, mIsInClearCachedResources(false)
|
||||
, mIdNamespace(0)
|
||||
, mIdNamespace{0}
|
||||
, mResourceId(0)
|
||||
, mPipelineId(aPipelineId)
|
||||
, mIPCOpen(false)
|
||||
|
@ -119,7 +119,7 @@ WebRenderBridgeChild::DPEnd(wr::DisplayListBuilder &aBuilder,
|
|||
|
||||
if (aIsSync) {
|
||||
this->SendDPSyncEnd(aSize, mParentCommands, mDestroyedActors, GetFwdTransactionId(), aTransactionId,
|
||||
contentSize, dlData, dl.dl_desc, aScrollData, mIdNamespace,fwdTime);
|
||||
contentSize, dlData, dl.dl_desc, aScrollData, mIdNamespace, fwdTime);
|
||||
} else {
|
||||
this->SendDPEnd(aSize, mParentCommands, mDestroyedActors, GetFwdTransactionId(), aTransactionId,
|
||||
contentSize, dlData, dl.dl_desc, aScrollData, mIdNamespace, fwdTime);
|
||||
|
@ -260,7 +260,7 @@ WebRenderBridgeChild::GetFontKeyForScaledFont(gfx::ScaledFont* aScaledFont)
|
|||
return key;
|
||||
}
|
||||
|
||||
key.mNamespace.mHandle = GetNamespace();
|
||||
key.mNamespace = GetNamespace();
|
||||
key.mHandle = GetNextResourceId();
|
||||
|
||||
SendAddRawFont(key, data.mFontBuffer, data.mFontIndex);
|
||||
|
@ -452,11 +452,11 @@ WebRenderBridgeChild::InForwarderThread()
|
|||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
WebRenderBridgeChild::RecvWrUpdated(const uint32_t& aNewIdNameSpace)
|
||||
WebRenderBridgeChild::RecvWrUpdated(const wr::IdNamespace& aNewIdNamespace)
|
||||
{
|
||||
// Update mIdNamespace to identify obsolete keys and messages by WebRenderBridgeParent.
|
||||
// Since usage of invalid keys could cause crash in webrender.
|
||||
mIdNamespace = aNewIdNameSpace;
|
||||
mIdNamespace = aNewIdNamespace;
|
||||
// Remove all FontKeys since they are removed by WebRenderBridgeParent
|
||||
for (auto iter = mFontKeys.Iter(); !iter.Done(); iter.Next()) {
|
||||
SendDeleteFont(iter.Data());
|
||||
|
|
|
@ -90,15 +90,15 @@ public:
|
|||
bool IsDestroyed() const { return mDestroyed; }
|
||||
|
||||
uint32_t GetNextResourceId() { return ++mResourceId; }
|
||||
uint32_t GetNamespace() { return mIdNamespace; }
|
||||
void SetNamespace(uint32_t aIdNamespace)
|
||||
wr::IdNamespace GetNamespace() { return mIdNamespace; }
|
||||
void SetNamespace(wr::IdNamespace aIdNamespace)
|
||||
{
|
||||
mIdNamespace = aIdNamespace;
|
||||
}
|
||||
|
||||
wr::WrImageKey GetNextImageKey()
|
||||
{
|
||||
return wr::WrImageKey{ wr::WrIdNamespace { GetNamespace() }, GetNextResourceId() };
|
||||
return wr::WrImageKey{ GetNamespace(), GetNextResourceId() };
|
||||
}
|
||||
|
||||
void PushGlyphs(wr::DisplayListBuilder& aBuilder, const nsTArray<GlyphArray>& aGlyphs,
|
||||
|
@ -144,7 +144,7 @@ private:
|
|||
|
||||
void ActorDestroy(ActorDestroyReason why) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvWrUpdated(const uint32_t& aNewIdNameSpace) override;
|
||||
virtual mozilla::ipc::IPCResult RecvWrUpdated(const wr::IdNamespace& aNewIdNamespace) override;
|
||||
|
||||
void AddIPDLReference() {
|
||||
MOZ_ASSERT(mIPCOpen == false);
|
||||
|
@ -166,7 +166,7 @@ private:
|
|||
uint64_t mReadLockSequenceNumber;
|
||||
bool mIsInTransaction;
|
||||
bool mIsInClearCachedResources;
|
||||
uint32_t mIdNamespace;
|
||||
wr::IdNamespace mIdNamespace;
|
||||
uint32_t mResourceId;
|
||||
wr::PipelineId mPipelineId;
|
||||
|
||||
|
|
|
@ -131,7 +131,7 @@ WebRenderBridgeParent::WebRenderBridgeParent(CompositorBridgeParentBase* aCompos
|
|||
, mChildLayerObserverEpoch(0)
|
||||
, mParentLayerObserverEpoch(0)
|
||||
, mWrEpoch(0)
|
||||
, mIdNameSpace(AllocIdNameSpace())
|
||||
, mIdNamespace(AllocIdNameSpace())
|
||||
, mPaused(false)
|
||||
, mDestroyed(false)
|
||||
, mForceRendering(false)
|
||||
|
@ -150,7 +150,7 @@ WebRenderBridgeParent::WebRenderBridgeParent()
|
|||
, mChildLayerObserverEpoch(0)
|
||||
, mParentLayerObserverEpoch(0)
|
||||
, mWrEpoch(0)
|
||||
, mIdNameSpace(AllocIdNameSpace())
|
||||
, mIdNamespace(AllocIdNameSpace())
|
||||
, mPaused(false)
|
||||
, mDestroyed(true)
|
||||
, mForceRendering(false)
|
||||
|
@ -234,7 +234,7 @@ WebRenderBridgeParent::RecvAddImage(const wr::ImageKey& aImageKey,
|
|||
}
|
||||
|
||||
// Check if key is obsoleted.
|
||||
if (aImageKey.mNamespace.mHandle != mIdNameSpace) {
|
||||
if (aImageKey.mNamespace != mIdNamespace) {
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
|
@ -261,7 +261,7 @@ WebRenderBridgeParent::RecvAddBlobImage(const wr::ImageKey& aImageKey,
|
|||
}
|
||||
|
||||
// Check if key is obsoleted.
|
||||
if (aImageKey.mNamespace.mHandle != mIdNameSpace) {
|
||||
if (aImageKey.mNamespace != mIdNamespace) {
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
|
@ -286,7 +286,7 @@ WebRenderBridgeParent::RecvAddRawFont(const wr::FontKey& aFontKey,
|
|||
}
|
||||
|
||||
// Check if key is obsoleted.
|
||||
if (aFontKey.mNamespace.mHandle != mIdNameSpace) {
|
||||
if (aFontKey.mNamespace != mIdNamespace) {
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
|
@ -309,7 +309,7 @@ WebRenderBridgeParent::RecvDeleteFont(const wr::FontKey& aFontKey)
|
|||
MOZ_ASSERT(mApi);
|
||||
|
||||
// Check if key is obsoleted.
|
||||
if (aFontKey.mNamespace.mHandle != mIdNameSpace) {
|
||||
if (aFontKey.mNamespace != mIdNamespace) {
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
|
@ -335,7 +335,7 @@ WebRenderBridgeParent::RecvUpdateImage(const wr::ImageKey& aImageKey,
|
|||
MOZ_ASSERT(mApi);
|
||||
|
||||
// Check if key is obsoleted.
|
||||
if (aImageKey.mNamespace.mHandle != mIdNameSpace) {
|
||||
if (aImageKey.mNamespace != mIdNamespace) {
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
|
@ -354,7 +354,7 @@ WebRenderBridgeParent::RecvDeleteImage(const wr::ImageKey& aImageKey)
|
|||
MOZ_ASSERT(mApi);
|
||||
|
||||
// Check if key is obsoleted.
|
||||
if (aImageKey.mNamespace.mHandle != mIdNameSpace) {
|
||||
if (aImageKey.mNamespace != mIdNamespace) {
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
|
@ -406,7 +406,7 @@ WebRenderBridgeParent::HandleDPEnd(const gfx::IntSize& aSize,
|
|||
const wr::ByteBuffer& dl,
|
||||
const wr::BuiltDisplayListDescriptor& dlDesc,
|
||||
const WebRenderScrollData& aScrollData,
|
||||
const uint32_t& aIdNameSpace,
|
||||
const wr::IdNamespace& aIdNamespace,
|
||||
const TimeStamp& aFwdTime)
|
||||
{
|
||||
AutoProfilerTracing tracing("Paint", "DPTransaction");
|
||||
|
@ -425,13 +425,13 @@ WebRenderBridgeParent::HandleDPEnd(const gfx::IntSize& aSize,
|
|||
|
||||
uint32_t wrEpoch = GetNextWrEpoch();
|
||||
ProcessWebRenderCommands(aSize, aCommands, wr::NewEpoch(wrEpoch),
|
||||
aContentSize, dl, dlDesc, aIdNameSpace);
|
||||
aContentSize, dl, dlDesc, aIdNamespace);
|
||||
HoldPendingTransactionId(wrEpoch, aTransactionId, aFwdTime);
|
||||
|
||||
mScrollData = aScrollData;
|
||||
UpdateAPZ();
|
||||
|
||||
if (mIdNameSpace != aIdNameSpace) {
|
||||
if (mIdNamespace != aIdNamespace) {
|
||||
// Pretend we composited since someone is wating for this event,
|
||||
// though DisplayList was not pushed to webrender.
|
||||
TimeStamp now = TimeStamp::Now();
|
||||
|
@ -520,14 +520,14 @@ WebRenderBridgeParent::RecvDPEnd(const gfx::IntSize& aSize,
|
|||
const wr::ByteBuffer& dl,
|
||||
const wr::BuiltDisplayListDescriptor& dlDesc,
|
||||
const WebRenderScrollData& aScrollData,
|
||||
const uint32_t& aIdNameSpace,
|
||||
const wr::IdNamespace& aIdNamespace,
|
||||
const TimeStamp& aFwdTime)
|
||||
{
|
||||
if (mDestroyed) {
|
||||
return IPC_OK();
|
||||
}
|
||||
HandleDPEnd(aSize, Move(aCommands), Move(aToDestroy), aFwdTransactionId, aTransactionId,
|
||||
aContentSize, dl, dlDesc, aScrollData, aIdNameSpace, aFwdTime);
|
||||
aContentSize, dl, dlDesc, aScrollData, aIdNamespace, aFwdTime);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
|
@ -541,14 +541,14 @@ WebRenderBridgeParent::RecvDPSyncEnd(const gfx::IntSize &aSize,
|
|||
const wr::ByteBuffer& dl,
|
||||
const wr::BuiltDisplayListDescriptor& dlDesc,
|
||||
const WebRenderScrollData& aScrollData,
|
||||
const uint32_t& aIdNameSpace,
|
||||
const wr::IdNamespace& aIdNamespace,
|
||||
const TimeStamp& aFwdTime)
|
||||
{
|
||||
if (mDestroyed) {
|
||||
return IPC_OK();
|
||||
}
|
||||
HandleDPEnd(aSize, Move(aCommands), Move(aToDestroy), aFwdTransactionId, aTransactionId,
|
||||
aContentSize, dl, dlDesc, aScrollData, aIdNameSpace, aFwdTime);
|
||||
aContentSize, dl, dlDesc, aScrollData, aIdNamespace, aFwdTime);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
|
@ -572,7 +572,7 @@ WebRenderBridgeParent::ProcessWebRenderParentCommands(InfallibleTArray<WebRender
|
|||
const OpAddExternalImage& op = cmd.get_OpAddExternalImage();
|
||||
Range<const wr::ImageKey> keys(&op.key(), 1);
|
||||
// Check if key is obsoleted.
|
||||
if (keys[0].mNamespace.mHandle != mIdNameSpace) {
|
||||
if (keys[0].mNamespace != mIdNamespace) {
|
||||
break;
|
||||
}
|
||||
MOZ_ASSERT(mExternalImageIds.Get(wr::AsUint64(op.externalImageId())).get());
|
||||
|
@ -663,14 +663,14 @@ WebRenderBridgeParent::ProcessWebRenderCommands(const gfx::IntSize &aSize,
|
|||
InfallibleTArray<WebRenderParentCommand>& aCommands, const wr::Epoch& aEpoch,
|
||||
const wr::LayoutSize& aContentSize, const wr::ByteBuffer& dl,
|
||||
const wr::BuiltDisplayListDescriptor& dlDesc,
|
||||
const uint32_t& aIdNameSpace)
|
||||
const wr::IdNamespace& aIdNamespace)
|
||||
{
|
||||
mAsyncImageManager->SetCompositionTime(TimeStamp::Now());
|
||||
ProcessWebRenderParentCommands(aCommands);
|
||||
|
||||
// The command is obsoleted.
|
||||
// Do not set the command to webrender since it causes crash in webrender.
|
||||
if (mIdNameSpace != aIdNameSpace) {
|
||||
if (mIdNamespace != aIdNamespace) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -881,7 +881,7 @@ WebRenderBridgeParent::UpdateWebRender(CompositorVsyncScheduler* aScheduler,
|
|||
|
||||
// Update id name space to identify obsoleted keys.
|
||||
// Since usage of invalid keys could cause crash in webrender.
|
||||
mIdNameSpace = AllocIdNameSpace();
|
||||
mIdNamespace = AllocIdNameSpace();
|
||||
// XXX Remove it when webrender supports sharing/moving Keys between different webrender instances.
|
||||
// XXX It requests client to update/reallocate webrender related resources,
|
||||
// but parent side does not wait end of the update.
|
||||
|
@ -890,7 +890,7 @@ WebRenderBridgeParent::UpdateWebRender(CompositorVsyncScheduler* aScheduler,
|
|||
// after new layers/webrender keys allocation.
|
||||
// Without client side's layout refactoring, we could not finish all old layers/webrender keys removals
|
||||
// before new layer/webrender keys allocation. In future, we could address the problem.
|
||||
Unused << SendWrUpdated(mIdNameSpace);
|
||||
Unused << SendWrUpdated(mIdNamespace);
|
||||
CompositorBridgeParentBase* cBridge = mCompositorBridge;
|
||||
// XXX Stop to clear resources if webreder supports resources sharing between different webrender instances.
|
||||
ClearResources();
|
||||
|
|
|
@ -102,7 +102,7 @@ public:
|
|||
const wr::ByteBuffer& dl,
|
||||
const wr::BuiltDisplayListDescriptor& dlDesc,
|
||||
const WebRenderScrollData& aScrollData,
|
||||
const uint32_t& aIdNameSpace,
|
||||
const wr::IdNamespace& aIdNamespace,
|
||||
const TimeStamp& aFwdTime) override;
|
||||
mozilla::ipc::IPCResult RecvDPSyncEnd(const gfx::IntSize& aSize,
|
||||
InfallibleTArray<WebRenderParentCommand>&& aCommands,
|
||||
|
@ -113,7 +113,7 @@ public:
|
|||
const wr::ByteBuffer& dl,
|
||||
const wr::BuiltDisplayListDescriptor& dlDesc,
|
||||
const WebRenderScrollData& aScrollData,
|
||||
const uint32_t& aIdNameSpace,
|
||||
const wr::IdNamespace& aIdNamespace,
|
||||
const TimeStamp& aFwdTime) override;
|
||||
mozilla::ipc::IPCResult RecvParentCommands(nsTArray<WebRenderParentCommand>&& commands) override;
|
||||
mozilla::ipc::IPCResult RecvDPGetSnapshot(PTextureParent* aTexture) override;
|
||||
|
@ -178,16 +178,16 @@ public:
|
|||
|
||||
void ExtractImageCompositeNotifications(nsTArray<ImageCompositeNotificationInfo>* aNotifications);
|
||||
|
||||
uint32_t GetIdNameSpace()
|
||||
wr::IdNamespace GetIdNamespace()
|
||||
{
|
||||
return mIdNameSpace;
|
||||
return mIdNamespace;
|
||||
}
|
||||
|
||||
void UpdateAPZ();
|
||||
const WebRenderScrollData& GetScrollData() const;
|
||||
|
||||
static uint32_t AllocIdNameSpace() {
|
||||
return ++sIdNameSpace;
|
||||
static wr::IdNamespace AllocIdNameSpace() {
|
||||
return wr::IdNamespace { ++sIdNameSpace };
|
||||
}
|
||||
|
||||
void FlushRendering(bool aIsSync);
|
||||
|
@ -212,7 +212,7 @@ private:
|
|||
const wr::LayoutSize& aContentSize,
|
||||
const wr::ByteBuffer& dl,
|
||||
const wr::BuiltDisplayListDescriptor& dlDesc,
|
||||
const uint32_t& aIdNameSpace);
|
||||
const wr::IdNamespace& aIdNamespace);
|
||||
void ClearResources();
|
||||
uint64_t GetChildLayerObserverEpoch() const { return mChildLayerObserverEpoch; }
|
||||
bool ShouldParentObserveEpoch();
|
||||
|
@ -225,7 +225,7 @@ private:
|
|||
const wr::ByteBuffer& dl,
|
||||
const wr::BuiltDisplayListDescriptor& dlDesc,
|
||||
const WebRenderScrollData& aScrollData,
|
||||
const uint32_t& aIdNameSpace,
|
||||
const wr::IdNamespace& aIdNamespace,
|
||||
const TimeStamp& aFwdTime);
|
||||
mozilla::ipc::IPCResult HandleShutdown();
|
||||
|
||||
|
@ -287,7 +287,7 @@ private:
|
|||
|
||||
std::queue<PendingTransactionId> mPendingTransactionIds;
|
||||
uint32_t mWrEpoch;
|
||||
uint32_t mIdNameSpace;
|
||||
wr::IdNamespace mIdNamespace;
|
||||
|
||||
bool mPaused;
|
||||
bool mDestroyed;
|
||||
|
|
|
@ -35,7 +35,7 @@ wr::WrImageKey
|
|||
WebRenderLayer::GenerateImageKey()
|
||||
{
|
||||
wr::WrImageKey key;
|
||||
key.mNamespace.mHandle = WrBridge()->GetNamespace();
|
||||
key.mNamespace = WrBridge()->GetNamespace();
|
||||
key.mHandle = WrBridge()->GetNextResourceId();
|
||||
return key;
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ WebRenderLayerManager::Initialize(PCompositorBridgeChild* aCBChild,
|
|||
|
||||
LayoutDeviceIntSize size = mWidget->GetClientSize();
|
||||
TextureFactoryIdentifier textureFactoryIdentifier;
|
||||
uint32_t id_namespace;
|
||||
wr::IdNamespace id_namespace;
|
||||
PWebRenderBridgeChild* bridge = aCBChild->SendPWebRenderBridgeConstructor(aLayersId,
|
||||
size,
|
||||
&textureFactoryIdentifier,
|
||||
|
|
|
@ -28,6 +28,7 @@ type WrEpoch = Epoch;
|
|||
/// cbindgen:field-names=[mHandle]
|
||||
/// cbindgen:derive-lt=true
|
||||
/// cbindgen:derive-lte=true
|
||||
/// cbindgen:derive-neq=true
|
||||
type WrIdNamespace = IdNamespace;
|
||||
|
||||
/// cbindgen:field-names=[mNamespace, mHandle]
|
||||
|
|
|
@ -168,6 +168,9 @@ struct IdNamespace {
|
|||
bool operator==(const IdNamespace& aOther) const {
|
||||
return mHandle == aOther.mHandle;
|
||||
}
|
||||
bool operator!=(const IdNamespace& aOther) const {
|
||||
return mHandle != aOther.mHandle;
|
||||
}
|
||||
bool operator<(const IdNamespace& aOther) const {
|
||||
return mHandle < aOther.mHandle;
|
||||
}
|
||||
|
|
|
@ -658,11 +658,13 @@ XPCJSRuntime::TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback& c
|
|||
if (val.isObject() && !JS::ObjectIsMarkedGray(&val.toObject()))
|
||||
continue;
|
||||
}
|
||||
cb.NoteXPCOMRoot(v);
|
||||
cb.NoteXPCOMRoot(v,
|
||||
XPCTraceableVariant::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant());
|
||||
}
|
||||
|
||||
for (XPCRootSetElem* e = mWrappedJSRoots; e ; e = e->GetNextRoot()) {
|
||||
cb.NoteXPCOMRoot(ToSupports(static_cast<nsXPCWrappedJS*>(e)));
|
||||
cb.NoteXPCOMRoot(ToSupports(static_cast<nsXPCWrappedJS*>(e)),
|
||||
nsXPCWrappedJS::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,17 +22,16 @@
|
|||
#two { background-color: green; }
|
||||
</style>
|
||||
<style type="text/css">
|
||||
/* not a URI token; the unterminated string ends at end of line, so
|
||||
the brace never matches */
|
||||
#three { background-color: green; }
|
||||
/* not a URI token; bad-url token is consumed until the first closing ) */
|
||||
#foo { background: url(foo"bar) }
|
||||
#three { background-color: red; }
|
||||
#three { background-color: green; }
|
||||
</style>
|
||||
<style type="text/css">
|
||||
/* not a URI token; the unterminated string ends at end of line */
|
||||
/* not a URI token; bad-url token is consumed until the first closing ) */
|
||||
#four { background-color: green; }
|
||||
#foo { background: url(foo"bar) }
|
||||
) }
|
||||
#four { background-color: green; }
|
||||
#four { background-color: red; }
|
||||
</style>
|
||||
<style type="text/css">
|
||||
/* not a URI token; the unterminated string ends at end of line, so
|
||||
|
@ -68,18 +67,19 @@
|
|||
#eleven { background: url([) green; }
|
||||
</style>
|
||||
<style type="text/css">
|
||||
/* not a URI token; brace matching should work only after invalid URI token */
|
||||
#twelve { background: url(}{""{)}); background-color: green; }
|
||||
/* not a URI token; bad-url token is consumed until the first closing )
|
||||
so the brace immediately after it closes the declaration block */
|
||||
#twelve { background-color: green; }
|
||||
#twelve { background: url(}{""{)}); background-color: red; }
|
||||
</style>
|
||||
<style type="text/css">
|
||||
/* invalid URI token absorbs the [ */
|
||||
#thirteen { background: url([""); background-color: green; }
|
||||
</style>
|
||||
<style type="text/css">
|
||||
/* not a URI token; the opening ( is never matched */
|
||||
#fourteen { background-color: green; }
|
||||
/* not a URI token; bad-url token is consumed until the first closing ) */
|
||||
#foo { background: url(() }
|
||||
#fourteen { background-color: red; }
|
||||
#fourteen { background-color: green; }
|
||||
</style>
|
||||
<!-- The next three tests test that invalid URI tokens absorb [ and { -->
|
||||
<style type="text/css">
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
== at-rule-013.html at-rule-013-ref.html
|
||||
fails-if(styloVsGecko||stylo) == invalid-url-handling.xhtml invalid-url-handling-ref.xhtml # bug 1383075
|
||||
== invalid-url-handling.xhtml invalid-url-handling-ref.xhtml
|
||||
== pseudo-elements-1.html pseudo-elements-1-ref.html
|
||||
== invalid-attr-1.html invalid-attr-1-ref.html
|
||||
== at-rule-error-handling-import-1.html at-rule-error-handling-ref.html
|
||||
|
|
|
@ -2730,20 +2730,32 @@ Gecko_ReportUnexpectedCSSError(ErrorReporter* reporter,
|
|||
const char* message,
|
||||
const char* param,
|
||||
uint32_t paramLen,
|
||||
const char* prefix,
|
||||
const char* prefixParam,
|
||||
uint32_t prefixParamLen,
|
||||
const char* suffix,
|
||||
const char* source,
|
||||
uint32_t sourceLen,
|
||||
uint32_t lineNumber,
|
||||
uint32_t colNumber,
|
||||
nsIURI* uri,
|
||||
const char* followup)
|
||||
uint32_t colNumber)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (prefix) {
|
||||
if (prefixParam) {
|
||||
nsDependentCSubstring paramValue(prefixParam, prefixParamLen);
|
||||
nsAutoString wideParam = NS_ConvertUTF8toUTF16(paramValue);
|
||||
reporter->ReportUnexpectedUnescaped(prefix, wideParam);
|
||||
} else {
|
||||
reporter->ReportUnexpected(prefix);
|
||||
}
|
||||
}
|
||||
|
||||
nsDependentCSubstring paramValue(param, paramLen);
|
||||
nsAutoString wideParam = NS_ConvertUTF8toUTF16(paramValue);
|
||||
reporter->ReportUnexpectedUnescaped(message, wideParam);
|
||||
if (followup) {
|
||||
reporter->ReportUnexpected(followup);
|
||||
if (suffix) {
|
||||
reporter->ReportUnexpected(suffix);
|
||||
}
|
||||
nsDependentCSubstring sourceValue(source, sourceLen);
|
||||
reporter->OutputError(lineNumber, colNumber, sourceValue);
|
||||
|
|
|
@ -680,12 +680,14 @@ void Gecko_ReportUnexpectedCSSError(mozilla::css::ErrorReporter* reporter,
|
|||
const char* message,
|
||||
const char* param,
|
||||
uint32_t paramLen,
|
||||
const char* prefix,
|
||||
const char* prefixParam,
|
||||
uint32_t prefixParamLen,
|
||||
const char* suffix,
|
||||
const char* source,
|
||||
uint32_t sourceLen,
|
||||
uint32_t lineNumber,
|
||||
uint32_t colNumber,
|
||||
nsIURI* aURI,
|
||||
const char* followup);
|
||||
uint32_t colNumber);
|
||||
|
||||
} // extern "C"
|
||||
|
||||
|
|
|
@ -1165,6 +1165,7 @@ nsCSSScanner::NextURL(nsCSSToken& aToken)
|
|||
// aToken.mIdent may be "url" at this point; clear that out
|
||||
aToken.mIdent.Truncate();
|
||||
|
||||
bool hasString = false;
|
||||
int32_t ch = Peek();
|
||||
// Do we have a string?
|
||||
if (ch == '"' || ch == '\'') {
|
||||
|
@ -1174,7 +1175,7 @@ nsCSSScanner::NextURL(nsCSSToken& aToken)
|
|||
return;
|
||||
}
|
||||
MOZ_ASSERT(aToken.mType == eCSSToken_String, "unexpected token type");
|
||||
|
||||
hasString = true;
|
||||
} else {
|
||||
// Otherwise, this is the start of a non-quoted url (which may be empty).
|
||||
aToken.mSymbol = char16_t(0);
|
||||
|
@ -1194,6 +1195,25 @@ nsCSSScanner::NextURL(nsCSSToken& aToken)
|
|||
} else {
|
||||
mSeenBadToken = true;
|
||||
aToken.mType = eCSSToken_Bad_URL;
|
||||
if (!hasString) {
|
||||
// Consume until before the next right parenthesis, which follows
|
||||
// how <bad-url-token> is consumed in CSS Syntax 3 spec.
|
||||
// Note that, we only do this when "url(" is not followed by a
|
||||
// string, because in the spec, "url(" followed by a string is
|
||||
// handled as a url function rather than a <url-token>, so the
|
||||
// rest of content before ")" should be consumed in balance,
|
||||
// which will be done by the parser.
|
||||
// The closing ")" is not consumed here. It is left to the parser
|
||||
// so that the parser can handle both cases.
|
||||
do {
|
||||
if (IsVertSpace(ch)) {
|
||||
AdvanceLine();
|
||||
} else {
|
||||
Advance();
|
||||
}
|
||||
ch = Peek();
|
||||
} while (ch >= 0 && ch != ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -55,8 +55,7 @@ var LEX_TESTS = [
|
|||
["url:http://example.com"]],
|
||||
// In CSS Level 3, this is an ordinary URL, not a BAD_URL.
|
||||
["url(http://example.com", ["url:http://example.com"]],
|
||||
// See bug 1153981 to understand why this gets a SYMBOL token.
|
||||
["url(http://example.com @", ["bad_url:http://example.com", "symbol:@"]],
|
||||
["url(http://example.com @", ["bad_url:http://example.com"]],
|
||||
["quo\\ting", ["ident:quoting"]],
|
||||
["'bad string\n", ["bad_string:bad string", "whitespace"]],
|
||||
["~=", ["includes"]],
|
||||
|
|
|
@ -5414,7 +5414,7 @@ pref("browser.safebrowsing.provider.mozilla.nextupdatetime", "1");
|
|||
pref("browser.safebrowsing.provider.mozilla.lists.base.name", "mozstdName");
|
||||
pref("browser.safebrowsing.provider.mozilla.lists.base.description", "mozstdDesc");
|
||||
pref("browser.safebrowsing.provider.mozilla.lists.content.name", "mozfullName");
|
||||
pref("browser.safebrowsing.provider.mozilla.lists.content.description", "mozfullDesc");
|
||||
pref("browser.safebrowsing.provider.mozilla.lists.content.description", "mozfullDesc2");
|
||||
|
||||
pref("urlclassifier.flashAllowTable", "allow-flashallow-digest256");
|
||||
pref("urlclassifier.flashAllowExceptTable", "except-flashallow-digest256");
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
#include "nsSocketTransportService2.h"
|
||||
#include "nsStreamUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsCORSListenerProxy.h"
|
||||
|
||||
#ifdef MOZ_TASK_TRACER
|
||||
#include "GeckoTaskTracer.h"
|
||||
|
@ -3598,5 +3599,22 @@ HttpChannelChild::ActorDestroy(ActorDestroyReason aWhy)
|
|||
}
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
HttpChannelChild::RecvLogBlockedCORSRequest(const nsString& aMessage)
|
||||
{
|
||||
Unused << LogBlockedCORSRequest(aMessage);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpChannelChild::LogBlockedCORSRequest(const nsAString & aMessage)
|
||||
{
|
||||
if (mLoadInfo) {
|
||||
uint64_t innerWindowID = mLoadInfo->GetInnerWindowID();
|
||||
nsCORSListenerProxy::LogBlockedCORSRequest(innerWindowID, aMessage);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -179,6 +179,9 @@ protected:
|
|||
// Get event target for processing network events.
|
||||
already_AddRefed<nsIEventTarget> GetNeckoTarget() override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvLogBlockedCORSRequest(const nsString& aMessage) override;
|
||||
NS_IMETHOD LogBlockedCORSRequest(const nsAString & aMessage) override;
|
||||
|
||||
private:
|
||||
// this section is for main-thread-only object
|
||||
// all the references need to be proxy released on main thread.
|
||||
|
|
|
@ -232,6 +232,12 @@ HttpChannelParent::CleanupBackgroundChannel()
|
|||
return;
|
||||
}
|
||||
|
||||
// The nsHttpChannel may have a reference to this parent, release it
|
||||
// to avoid circular references.
|
||||
if (mChannel) {
|
||||
mChannel->SetWarningReporter(nullptr);
|
||||
}
|
||||
|
||||
if (!mPromise.IsEmpty()) {
|
||||
mRequest.DisconnectIfExists();
|
||||
mPromise.Reject(NS_ERROR_FAILURE, __func__);
|
||||
|
@ -805,6 +811,8 @@ HttpChannelParent::ConnectChannel(const uint32_t& registrarId, const bool& shoul
|
|||
return true;
|
||||
}
|
||||
|
||||
mChannel->SetWarningReporter(this);
|
||||
|
||||
nsCOMPtr<nsINetworkInterceptController> controller;
|
||||
NS_QueryNotificationCallbacks(channel, controller);
|
||||
RefPtr<HttpChannelParentListener> parentListener = do_QueryObject(controller);
|
||||
|
@ -1544,6 +1552,8 @@ HttpChannelParent::OnStopRequest(nsIRequest *aRequest,
|
|||
mChannel->GetCacheReadStart(&timing.cacheReadStart);
|
||||
mChannel->GetCacheReadEnd(&timing.cacheReadEnd);
|
||||
|
||||
mChannel->SetWarningReporter(nullptr);
|
||||
|
||||
// Either IPC channel is closed or background channel
|
||||
// is ready to send OnStopRequest.
|
||||
MOZ_ASSERT(mIPCClosed || mBgParent);
|
||||
|
@ -2227,5 +2237,15 @@ HttpChannelParent::DoSendSetPriority(int16_t aValue)
|
|||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
HttpChannelParent::LogBlockedCORSRequest(const nsAString& aMessage)
|
||||
{
|
||||
if (mIPCClosed ||
|
||||
NS_WARN_IF(!SendLogBlockedCORSRequest(nsString(aMessage)))) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -214,6 +214,7 @@ protected:
|
|||
MOZ_MUST_USE nsresult
|
||||
ReportSecurityMessage(const nsAString& aMessageTag,
|
||||
const nsAString& aMessageCategory) override;
|
||||
nsresult LogBlockedCORSRequest(const nsAString& aMessage) override;
|
||||
|
||||
// Calls SendDeleteSelf and sets mIPCClosed to true because we should not
|
||||
// send any more messages after that. Bug 1274886
|
||||
|
|
|
@ -854,6 +854,12 @@ NullHttpChannel::SetIsMainDocumentChannel(bool aValue)
|
|||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::LogBlockedCORSRequest(const nsAString& aMessage)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
#define IMPL_TIMING_ATTR(name) \
|
||||
NS_IMETHODIMP \
|
||||
NullHttpChannel::Get##name##Time(PRTime* _retval) { \
|
||||
|
|
|
@ -141,6 +141,11 @@ child:
|
|||
// Tell the child to issue a deprecation warning.
|
||||
async IssueDeprecationWarning(uint32_t warning, bool asError);
|
||||
|
||||
// When CORS blocks the request in the parent process, it doesn't have the
|
||||
// correct window ID, so send the message to the child for logging to the web
|
||||
// console.
|
||||
async LogBlockedCORSRequest(nsString message);
|
||||
|
||||
both:
|
||||
// After receiving this message, the parent also calls
|
||||
// SendFinishInterceptedRedirect, and makes sure not to send any more messages
|
||||
|
|
|
@ -42,8 +42,10 @@
|
|||
#include "NullPrincipal.h"
|
||||
#include "nsICorsPreflightCallback.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsHttpChannel.h"
|
||||
#include "mozilla/LoadInfo.h"
|
||||
#include "nsIHttpHeaderVisitor.h"
|
||||
#include "nsQueryObject.h"
|
||||
#include <algorithm>
|
||||
|
||||
using namespace mozilla;
|
||||
|
@ -56,24 +58,11 @@ static bool gDisableCORSPrivateData = false;
|
|||
static void
|
||||
LogBlockedRequest(nsIRequest* aRequest,
|
||||
const char* aProperty,
|
||||
const char16_t* aParam)
|
||||
const char16_t* aParam,
|
||||
nsIHttpChannel* aCreatingChannel)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
// Build the error object and log it to the console
|
||||
nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv));
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to log blocked cross-site request (no console)");
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIScriptError> scriptError =
|
||||
do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to log blocked cross-site request (no scriptError)");
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
|
||||
nsCOMPtr<nsIURI> aUri;
|
||||
channel->GetURI(getter_AddRefs(aUri));
|
||||
|
@ -98,34 +87,19 @@ LogBlockedRequest(nsIRequest* aRequest,
|
|||
|
||||
nsAutoString msg(blockedMessage.get());
|
||||
|
||||
// query innerWindowID and log to web console, otherwise log to
|
||||
// the error to the browser console.
|
||||
uint64_t innerWindowID = nsContentUtils::GetInnerWindowID(aRequest);
|
||||
if (XRE_IsParentProcess()) {
|
||||
if (aCreatingChannel) {
|
||||
rv = aCreatingChannel->LogBlockedCORSRequest(msg);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
NS_WARNING("Failed to log blocked cross-site request to web console from parent->child, falling back to browser console");
|
||||
}
|
||||
|
||||
if (innerWindowID > 0) {
|
||||
rv = scriptError->InitWithWindowID(msg,
|
||||
EmptyString(), // sourceName
|
||||
EmptyString(), // sourceLine
|
||||
0, // lineNumber
|
||||
0, // columnNumber
|
||||
nsIScriptError::warningFlag,
|
||||
"CORS",
|
||||
innerWindowID);
|
||||
}
|
||||
else {
|
||||
rv = scriptError->Init(msg,
|
||||
EmptyString(), // sourceName
|
||||
EmptyString(), // sourceLine
|
||||
0, // lineNumber
|
||||
0, // columnNumber
|
||||
nsIScriptError::warningFlag,
|
||||
"CORS");
|
||||
}
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to log blocked cross-site request (scriptError init failed)");
|
||||
return;
|
||||
}
|
||||
console->LogMessage(scriptError);
|
||||
// log message ourselves
|
||||
uint64_t innerWindowID = nsContentUtils::GetInnerWindowID(aRequest);
|
||||
nsCORSListenerProxy::LogBlockedCORSRequest(innerWindowID, msg);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
@ -451,6 +425,7 @@ nsCORSListenerProxy::Init(nsIChannel* aChannel, DataURIHandling aAllowDataURI)
|
|||
mRequestingPrincipal = nullptr;
|
||||
mOriginHeaderPrincipal = nullptr;
|
||||
mOuterNotificationCallbacks = nullptr;
|
||||
mHttpChannel = nullptr;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
mInited = true;
|
||||
|
@ -540,9 +515,11 @@ nsCORSListenerProxy::CheckRequestApproved(nsIRequest* aRequest)
|
|||
if (!mHasBeenCrossSite) {
|
||||
return NS_OK;
|
||||
}
|
||||
nsCOMPtr<nsIHttpChannel> topChannel;
|
||||
topChannel.swap(mHttpChannel);
|
||||
|
||||
if (gDisableCORS) {
|
||||
LogBlockedRequest(aRequest, "CORSDisabled", nullptr);
|
||||
LogBlockedRequest(aRequest, "CORSDisabled", nullptr, topChannel);
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
|
||||
|
@ -560,7 +537,7 @@ nsCORSListenerProxy::CheckRequestApproved(nsIRequest* aRequest)
|
|||
// Test that things worked on a HTTP level
|
||||
nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aRequest);
|
||||
if (!http) {
|
||||
LogBlockedRequest(aRequest, "CORSRequestNotHttp", nullptr);
|
||||
LogBlockedRequest(aRequest, "CORSRequestNotHttp", nullptr, topChannel);
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
|
||||
|
@ -582,14 +559,14 @@ nsCORSListenerProxy::CheckRequestApproved(nsIRequest* aRequest)
|
|||
// check for duplicate headers
|
||||
rv = http->VisitOriginalResponseHeaders(visitor);
|
||||
if (NS_FAILED(rv)) {
|
||||
LogBlockedRequest(aRequest, "CORSAllowOriginNotMatchingOrigin", nullptr);
|
||||
LogBlockedRequest(aRequest, "CORSAllowOriginNotMatchingOrigin", nullptr, topChannel);
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = http->GetResponseHeader(
|
||||
NS_LITERAL_CSTRING("Access-Control-Allow-Origin"), allowedOriginHeader);
|
||||
if (NS_FAILED(rv)) {
|
||||
LogBlockedRequest(aRequest, "CORSMissingAllowOrigin", nullptr);
|
||||
LogBlockedRequest(aRequest, "CORSMissingAllowOrigin", nullptr, topChannel);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -602,7 +579,7 @@ nsCORSListenerProxy::CheckRequestApproved(nsIRequest* aRequest)
|
|||
// below since "if (A && B)" is included in "if (A || !B)".
|
||||
//
|
||||
if (mWithCredentials && allowedOriginHeader.EqualsLiteral("*")) {
|
||||
LogBlockedRequest(aRequest, "CORSNotSupportingCredentials", nullptr);
|
||||
LogBlockedRequest(aRequest, "CORSNotSupportingCredentials", nullptr, topChannel);
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
|
||||
|
@ -613,7 +590,7 @@ nsCORSListenerProxy::CheckRequestApproved(nsIRequest* aRequest)
|
|||
|
||||
if (!allowedOriginHeader.Equals(origin)) {
|
||||
LogBlockedRequest(aRequest, "CORSAllowOriginNotMatchingOrigin",
|
||||
NS_ConvertUTF8toUTF16(allowedOriginHeader).get());
|
||||
NS_ConvertUTF8toUTF16(allowedOriginHeader).get(), topChannel);
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
}
|
||||
|
@ -625,7 +602,7 @@ nsCORSListenerProxy::CheckRequestApproved(nsIRequest* aRequest)
|
|||
NS_LITERAL_CSTRING("Access-Control-Allow-Credentials"), allowCredentialsHeader);
|
||||
|
||||
if (!allowCredentialsHeader.EqualsLiteral("true")) {
|
||||
LogBlockedRequest(aRequest, "CORSMissingAllowCredentials", nullptr);
|
||||
LogBlockedRequest(aRequest, "CORSMissingAllowCredentials", nullptr, topChannel);
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
}
|
||||
|
@ -642,6 +619,7 @@ nsCORSListenerProxy::OnStopRequest(nsIRequest* aRequest,
|
|||
nsresult rv = mOuterListener->OnStopRequest(aRequest, aContext, aStatusCode);
|
||||
mOuterListener = nullptr;
|
||||
mOuterNotificationCallbacks = nullptr;
|
||||
mHttpChannel = nullptr;
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -994,6 +972,8 @@ nsCORSListenerProxy::UpdateChannel(nsIChannel* aChannel,
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
mHttpChannel = http;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1310,11 +1290,12 @@ nsCORSPreflightListener::CheckPreflightRequestApproved(nsIRequest* aRequest)
|
|||
nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aRequest);
|
||||
nsCOMPtr<nsIHttpChannelInternal> internal = do_QueryInterface(aRequest);
|
||||
NS_ENSURE_STATE(internal);
|
||||
nsCOMPtr<nsIHttpChannel> parentHttpChannel = do_QueryInterface(mCallback);
|
||||
|
||||
bool succeedded;
|
||||
rv = http->GetRequestSucceeded(&succeedded);
|
||||
if (NS_FAILED(rv) || !succeedded) {
|
||||
LogBlockedRequest(aRequest, "CORSPreflightDidNotSucceed", nullptr);
|
||||
LogBlockedRequest(aRequest, "CORSPreflightDidNotSucceed", nullptr, parentHttpChannel);
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
|
||||
|
@ -1334,13 +1315,13 @@ nsCORSPreflightListener::CheckPreflightRequestApproved(nsIRequest* aRequest)
|
|||
}
|
||||
if (!NS_IsValidHTTPToken(method)) {
|
||||
LogBlockedRequest(aRequest, "CORSInvalidAllowMethod",
|
||||
NS_ConvertUTF8toUTF16(method).get());
|
||||
NS_ConvertUTF8toUTF16(method).get(), parentHttpChannel);
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
foundMethod |= mPreflightMethod.Equals(method);
|
||||
}
|
||||
if (!foundMethod) {
|
||||
LogBlockedRequest(aRequest, "CORSMethodNotFound", nullptr);
|
||||
LogBlockedRequest(aRequest, "CORSMethodNotFound", nullptr, parentHttpChannel);
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
|
||||
|
@ -1357,7 +1338,7 @@ nsCORSPreflightListener::CheckPreflightRequestApproved(nsIRequest* aRequest)
|
|||
}
|
||||
if (!NS_IsValidHTTPToken(header)) {
|
||||
LogBlockedRequest(aRequest, "CORSInvalidAllowHeader",
|
||||
NS_ConvertUTF8toUTF16(header).get());
|
||||
NS_ConvertUTF8toUTF16(header).get(), parentHttpChannel);
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
headers.AppendElement(header);
|
||||
|
@ -1366,7 +1347,7 @@ nsCORSPreflightListener::CheckPreflightRequestApproved(nsIRequest* aRequest)
|
|||
if (!headers.Contains(mPreflightHeaders[i],
|
||||
nsCaseInsensitiveCStringArrayComparator())) {
|
||||
LogBlockedRequest(aRequest, "CORSMissingAllowHeaderFromPreflight",
|
||||
NS_ConvertUTF8toUTF16(mPreflightHeaders[i]).get());
|
||||
NS_ConvertUTF8toUTF16(mPreflightHeaders[i]).get(), parentHttpChannel);
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
}
|
||||
|
@ -1396,6 +1377,7 @@ nsCORSListenerProxy::RemoveFromCorsPreflightCache(nsIURI* aURI,
|
|||
}
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult
|
||||
nsCORSListenerProxy::StartCORSPreflight(nsIChannel* aRequestChannel,
|
||||
nsICorsPreflightCallback* aCallback,
|
||||
|
@ -1405,7 +1387,8 @@ nsCORSListenerProxy::StartCORSPreflight(nsIChannel* aRequestChannel,
|
|||
*aPreflightChannel = nullptr;
|
||||
|
||||
if (gDisableCORS) {
|
||||
LogBlockedRequest(aRequestChannel, "CORSDisabled", nullptr);
|
||||
nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aRequestChannel);
|
||||
LogBlockedRequest(aRequestChannel, "CORSDisabled", nullptr, http);
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
|
||||
|
@ -1501,6 +1484,13 @@ nsCORSListenerProxy::StartCORSPreflight(nsIChannel* aRequestChannel,
|
|||
method, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Set the CORS preflight channel's warning reporter to be the same as the
|
||||
// requesting channel so that all log messages are able to be reported through
|
||||
// the warning reporter.
|
||||
RefPtr<nsHttpChannel> reqCh = do_QueryObject(aRequestChannel);
|
||||
RefPtr<nsHttpChannel> preCh = do_QueryObject(preHttp);
|
||||
preCh->SetWarningReporter(reqCh->GetWarningReporter());
|
||||
|
||||
nsTArray<nsCString> preflightHeaders;
|
||||
if (!aUnsafeHeaders.IsEmpty()) {
|
||||
for (uint32_t i = 0; i < aUnsafeHeaders.Length(); ++i) {
|
||||
|
@ -1538,3 +1528,52 @@ nsCORSListenerProxy::StartCORSPreflight(nsIChannel* aRequestChannel,
|
|||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
nsCORSListenerProxy::LogBlockedCORSRequest(uint64_t aInnerWindowID,
|
||||
const nsAString& aMessage)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
// Build the error object and log it to the console
|
||||
nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv));
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to log blocked cross-site request (no console)");
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIScriptError> scriptError =
|
||||
do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to log blocked cross-site request (no scriptError)");
|
||||
return;
|
||||
}
|
||||
|
||||
// query innerWindowID and log to web console, otherwise log to
|
||||
// the error to the browser console.
|
||||
if (aInnerWindowID > 0) {
|
||||
rv = scriptError->InitWithWindowID(aMessage,
|
||||
EmptyString(), // sourceName
|
||||
EmptyString(), // sourceLine
|
||||
0, // lineNumber
|
||||
0, // columnNumber
|
||||
nsIScriptError::warningFlag,
|
||||
"CORS",
|
||||
aInnerWindowID);
|
||||
}
|
||||
else {
|
||||
rv = scriptError->Init(aMessage,
|
||||
EmptyString(), // sourceName
|
||||
EmptyString(), // sourceLine
|
||||
0, // lineNumber
|
||||
0, // columnNumber
|
||||
nsIScriptError::warningFlag,
|
||||
"CORS");
|
||||
}
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to log blocked cross-site request (scriptError init failed)");
|
||||
return;
|
||||
}
|
||||
console->LogMessage(scriptError);
|
||||
}
|
||||
|
|
|
@ -70,6 +70,10 @@ public:
|
|||
|
||||
void SetInterceptController(nsINetworkInterceptController* aInterceptController);
|
||||
|
||||
// When CORS blocks a request, log the message to the web console, or the
|
||||
// browser console if no valid inner window ID is found.
|
||||
static void LogBlockedCORSRequest(uint64_t aInnerWindowID,
|
||||
const nsAString& aMessage);
|
||||
private:
|
||||
// Only HttpChannelParent can call RemoveFromCorsPreflightCache
|
||||
friend class mozilla::net::HttpChannelParent;
|
||||
|
@ -108,6 +112,10 @@ private:
|
|||
// an http: request to https: in nsHttpChannel::Connect() and hence
|
||||
// a request might not be marked as cross site request based on that promise.
|
||||
bool mHasBeenCrossSite;
|
||||
// Under e10s, logging happens in the child process. Keep a reference to the
|
||||
// creator nsIHttpChannel in order to find the way back to the child. Released
|
||||
// in OnStopRequest().
|
||||
nsCOMPtr<nsIHttpChannel> mHttpChannel;
|
||||
#ifdef DEBUG
|
||||
bool mInited;
|
||||
#endif
|
||||
|
|
|
@ -416,6 +416,15 @@ nsHttpChannel::AddSecurityMessage(const nsAString& aMessageTag,
|
|||
aMessageCategory);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHttpChannel::LogBlockedCORSRequest(const nsAString& aMessage)
|
||||
{
|
||||
if (mWarningReporter) {
|
||||
return mWarningReporter->LogBlockedCORSRequest(aMessage);
|
||||
}
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsHttpChannel <private>
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -858,6 +867,7 @@ nsHttpChannel::ReleaseListeners()
|
|||
{
|
||||
HttpBaseChannel::ReleaseListeners();
|
||||
mChannelClassifier = nullptr;
|
||||
mWarningReporter = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -5922,6 +5932,7 @@ nsHttpChannel::Cancel(nsresult status)
|
|||
LOG((" ignoring; already canceled\n"));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (mWaitingForRedirectCallback) {
|
||||
LOG(("channel canceled during wait for redirect callback"));
|
||||
}
|
||||
|
@ -9250,5 +9261,19 @@ nsHttpChannel::Notify(nsITimer *aTimer)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpChannel::SetWarningReporter(HttpChannelSecurityWarningReporter *aReporter)
|
||||
{
|
||||
LOG(("nsHttpChannel [this=%p] SetWarningReporter [%p]", this, aReporter));
|
||||
mWarningReporter = aReporter;
|
||||
}
|
||||
|
||||
HttpChannelSecurityWarningReporter*
|
||||
nsHttpChannel::GetWarningReporter()
|
||||
{
|
||||
LOG(("nsHttpChannel [this=%p] GetWarningReporter [%p]", this, mWarningReporter.get()));
|
||||
return mWarningReporter.get();
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -43,12 +43,13 @@ namespace mozilla { namespace net {
|
|||
class nsChannelClassifier;
|
||||
class Http2PushedStream;
|
||||
|
||||
class HttpChannelSecurityWarningReporter
|
||||
class HttpChannelSecurityWarningReporter : public nsISupports
|
||||
{
|
||||
public:
|
||||
virtual MOZ_MUST_USE nsresult
|
||||
ReportSecurityMessage(const nsAString& aMessageTag,
|
||||
const nsAString& aMessageCategory) = 0;
|
||||
virtual nsresult LogBlockedCORSRequest(const nsAString& aMessage) = 0;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -190,10 +191,10 @@ public:
|
|||
MOZ_MUST_USE nsresult
|
||||
AddSecurityMessage(const nsAString& aMessageTag,
|
||||
const nsAString& aMessageCategory) override;
|
||||
NS_IMETHOD LogBlockedCORSRequest(const nsAString& aMessage) override;
|
||||
|
||||
void SetWarningReporter(HttpChannelSecurityWarningReporter* aReporter)
|
||||
{ mWarningReporter = aReporter; }
|
||||
|
||||
void SetWarningReporter(HttpChannelSecurityWarningReporter *aReporter);
|
||||
HttpChannelSecurityWarningReporter* GetWarningReporter();
|
||||
public: /* internal necko use only */
|
||||
|
||||
using InitLocalBlockListCallback = std::function<void(bool)>;
|
||||
|
@ -672,7 +673,7 @@ private:
|
|||
nsCString mUsername;
|
||||
|
||||
// If non-null, warnings should be reported to this object.
|
||||
HttpChannelSecurityWarningReporter* mWarningReporter;
|
||||
RefPtr<HttpChannelSecurityWarningReporter> mWarningReporter;
|
||||
|
||||
RefPtr<ADivertableParentChannel> mParentChannel;
|
||||
|
||||
|
|
|
@ -478,4 +478,14 @@ interface nsIHttpChannel : nsIChannel
|
|||
* Don't alter it otherwise.
|
||||
*/
|
||||
[must_use] attribute uint64_t topLevelOuterContentWindowId;
|
||||
|
||||
/**
|
||||
* In e10s, the information that the CORS response blocks the load is in the
|
||||
* parent, which doesn't know the true window id of the request, so we may
|
||||
* need to proxy the request to the child.
|
||||
*
|
||||
* @param aMessage
|
||||
* The message to print in the console.
|
||||
*/
|
||||
void logBlockedCORSRequest(in AString aMessage);
|
||||
};
|
||||
|
|
|
@ -1044,3 +1044,13 @@ nsViewSourceChannel::SetCorsPreflightParameters(const nsTArray<nsCString>& aUnsa
|
|||
{
|
||||
mHttpChannelInternal->SetCorsPreflightParameters(aUnsafeHeaders);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsViewSourceChannel::LogBlockedCORSRequest(const nsAString& aMessage)
|
||||
{
|
||||
if (!mHttpChannel) {
|
||||
NS_WARNING("nsViewSourceChannel::LogBlockedCORSRequest mHttpChannel is null");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
return mHttpChannel->LogBlockedCORSRequest(aMessage);
|
||||
}
|
||||
|
|
|
@ -111,7 +111,10 @@ class BookmarkRepairRequestor extends CollectionRepairRequestor {
|
|||
// that tests have a slightly easier time, hence the `|| []` in each loop.
|
||||
|
||||
// Missing children records when the parent exists but a child doesn't.
|
||||
for (let { child } of validationInfo.problems.missingChildren || []) {
|
||||
for (let { parent, child } of validationInfo.problems.missingChildren || []) {
|
||||
// We can't be sure if the child is missing or our copy of the parent is
|
||||
// wrong, so request both
|
||||
ids.add(parent);
|
||||
ids.add(child);
|
||||
}
|
||||
if (ids.size > MAX_REQUESTED_IDS) {
|
||||
|
@ -130,18 +133,22 @@ class BookmarkRepairRequestor extends CollectionRepairRequestor {
|
|||
return ids; // might as well give up here - we aren't going to repair.
|
||||
}
|
||||
|
||||
// Entries where we have the parent but know for certain that the child was
|
||||
// deleted.
|
||||
for (let { parent } of validationInfo.problems.deletedChildren || []) {
|
||||
// Entries where we have the parent but we have a record from the server that
|
||||
// claims the child was deleted.
|
||||
for (let { parent, child } of validationInfo.problems.deletedChildren || []) {
|
||||
// Request both, since we don't know if it's a botched deletion or revival
|
||||
ids.add(parent);
|
||||
ids.add(child);
|
||||
}
|
||||
if (ids.size > MAX_REQUESTED_IDS) {
|
||||
return ids; // might as well give up here - we aren't going to repair.
|
||||
}
|
||||
|
||||
// Entries where the child references a parent that we don't have, but we
|
||||
// know why: the parent was deleted.
|
||||
for (let { child } of validationInfo.problems.deletedParents || []) {
|
||||
// have a record from the server that claims the parent was deleted.
|
||||
for (let { parent, child } of validationInfo.problems.deletedParents || []) {
|
||||
// Request both, since we don't know if it's a botched deletion or revival
|
||||
ids.add(parent);
|
||||
ids.add(child);
|
||||
}
|
||||
if (ids.size > MAX_REQUESTED_IDS) {
|
||||
|
@ -169,7 +176,6 @@ class BookmarkRepairRequestor extends CollectionRepairRequestor {
|
|||
ids.add(child);
|
||||
}
|
||||
|
||||
// XXX - any others we should consider?
|
||||
return ids;
|
||||
}
|
||||
|
||||
|
|
|
@ -163,7 +163,7 @@ add_task(async function test_bookmark_repair_integration() {
|
|||
value: undefined,
|
||||
extra: {
|
||||
flowID,
|
||||
numIDs: "1",
|
||||
numIDs: "2",
|
||||
},
|
||||
}, {
|
||||
object: "sendcommand",
|
||||
|
@ -180,7 +180,7 @@ add_task(async function test_bookmark_repair_integration() {
|
|||
extra: {
|
||||
deviceID: Service.identity.hashedDeviceID(remoteID),
|
||||
flowID,
|
||||
numIDs: "1",
|
||||
numIDs: "2",
|
||||
},
|
||||
}], "Should record telemetry events for repair request");
|
||||
|
||||
|
@ -225,7 +225,7 @@ add_task(async function test_bookmark_repair_integration() {
|
|||
value: undefined,
|
||||
extra: {
|
||||
flowID,
|
||||
numIDs: "1",
|
||||
numIDs: "2",
|
||||
},
|
||||
}, {
|
||||
object: "sendcommand",
|
||||
|
@ -241,7 +241,7 @@ add_task(async function test_bookmark_repair_integration() {
|
|||
value: undefined,
|
||||
extra: {
|
||||
flowID,
|
||||
numIDs: "1",
|
||||
numIDs: "2",
|
||||
}
|
||||
}], "Should record telemetry events for repair response");
|
||||
|
||||
|
@ -290,7 +290,7 @@ add_task(async function test_bookmark_repair_integration() {
|
|||
extra: {
|
||||
flowID,
|
||||
deviceID: Service.identity.hashedDeviceID(remoteID),
|
||||
numIDs: "1",
|
||||
numIDs: "2",
|
||||
},
|
||||
}, {
|
||||
object: "repair",
|
||||
|
|
|
@ -105,12 +105,12 @@ add_task(async function test_requestor_no_clients() {
|
|||
{ object: "repair",
|
||||
method: "started",
|
||||
value: undefined,
|
||||
extra: { flowID, numIDs: 3 },
|
||||
extra: { flowID, numIDs: 4 },
|
||||
},
|
||||
{ object: "repair",
|
||||
method: "finished",
|
||||
value: undefined,
|
||||
extra: { flowID, numIDs: 3 },
|
||||
extra: { flowID, numIDs: 4 },
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
@ -156,22 +156,22 @@ add_task(async function test_requestor_one_client_no_response() {
|
|||
{ object: "repair",
|
||||
method: "started",
|
||||
value: undefined,
|
||||
extra: { flowID, numIDs: 3 },
|
||||
extra: { flowID, numIDs: 4 },
|
||||
},
|
||||
{ object: "repair",
|
||||
method: "request",
|
||||
value: "upload",
|
||||
extra: { flowID, numIDs: 3, deviceID: "client-a" },
|
||||
extra: { flowID, numIDs: 4, deviceID: "client-a" },
|
||||
},
|
||||
{ object: "repair",
|
||||
method: "request",
|
||||
value: "upload",
|
||||
extra: { flowID, numIDs: 3, deviceID: "client-a" },
|
||||
extra: { flowID, numIDs: 4, deviceID: "client-a" },
|
||||
},
|
||||
{ object: "repair",
|
||||
method: "finished",
|
||||
value: undefined,
|
||||
extra: { flowID, numIDs: 3 },
|
||||
extra: { flowID, numIDs: 4 },
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
@ -208,12 +208,12 @@ add_task(async function test_requestor_one_client_no_sync() {
|
|||
{ object: "repair",
|
||||
method: "started",
|
||||
value: undefined,
|
||||
extra: { flowID, numIDs: 3 },
|
||||
extra: { flowID, numIDs: 4 },
|
||||
},
|
||||
{ object: "repair",
|
||||
method: "request",
|
||||
value: "upload",
|
||||
extra: { flowID, numIDs: 3, deviceID: "client-a" },
|
||||
extra: { flowID, numIDs: 4, deviceID: "client-a" },
|
||||
},
|
||||
{ object: "repair",
|
||||
method: "abandon",
|
||||
|
@ -223,7 +223,7 @@ add_task(async function test_requestor_one_client_no_sync() {
|
|||
{ object: "repair",
|
||||
method: "finished",
|
||||
value: undefined,
|
||||
extra: { flowID, numIDs: 3 },
|
||||
extra: { flowID, numIDs: 4 },
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
@ -288,7 +288,7 @@ add_task(async function test_requestor_client_vanishes() {
|
|||
request: "upload",
|
||||
flowID: requestor._flowID,
|
||||
clientID: "client-b",
|
||||
ids: ["a", "b", "c"],
|
||||
ids: ["a", "b", "c", "x"],
|
||||
}
|
||||
await requestor.continueRepairs(response);
|
||||
|
||||
|
@ -298,12 +298,12 @@ add_task(async function test_requestor_client_vanishes() {
|
|||
{ object: "repair",
|
||||
method: "started",
|
||||
value: undefined,
|
||||
extra: { flowID, numIDs: 3 },
|
||||
extra: { flowID, numIDs: 4 },
|
||||
},
|
||||
{ object: "repair",
|
||||
method: "request",
|
||||
value: "upload",
|
||||
extra: { flowID, numIDs: 3, deviceID: "client-a" },
|
||||
extra: { flowID, numIDs: 4, deviceID: "client-a" },
|
||||
},
|
||||
{ object: "repair",
|
||||
method: "abandon",
|
||||
|
@ -313,12 +313,12 @@ add_task(async function test_requestor_client_vanishes() {
|
|||
{ object: "repair",
|
||||
method: "request",
|
||||
value: "upload",
|
||||
extra: { flowID, numIDs: 3, deviceID: "client-b" },
|
||||
extra: { flowID, numIDs: 4, deviceID: "client-b" },
|
||||
},
|
||||
{ object: "repair",
|
||||
method: "response",
|
||||
value: "upload",
|
||||
extra: { flowID, deviceID: "client-b", numIDs: 3 },
|
||||
extra: { flowID, deviceID: "client-b", numIDs: 4 },
|
||||
},
|
||||
{ object: "repair",
|
||||
method: "finished",
|
||||
|
@ -371,7 +371,7 @@ add_task(async function test_requestor_success_responses() {
|
|||
request: "upload",
|
||||
clientID: "client-b",
|
||||
flowID: requestor._flowID,
|
||||
ids: ["c"],
|
||||
ids: ["c", "x"],
|
||||
}
|
||||
await requestor.continueRepairs(response);
|
||||
|
||||
|
@ -381,12 +381,12 @@ add_task(async function test_requestor_success_responses() {
|
|||
{ object: "repair",
|
||||
method: "started",
|
||||
value: undefined,
|
||||
extra: { flowID, numIDs: 3 },
|
||||
extra: { flowID, numIDs: 4 },
|
||||
},
|
||||
{ object: "repair",
|
||||
method: "request",
|
||||
value: "upload",
|
||||
extra: { flowID, numIDs: 3, deviceID: "client-a" },
|
||||
extra: { flowID, numIDs: 4, deviceID: "client-a" },
|
||||
},
|
||||
{ object: "repair",
|
||||
method: "response",
|
||||
|
@ -396,12 +396,12 @@ add_task(async function test_requestor_success_responses() {
|
|||
{ object: "repair",
|
||||
method: "request",
|
||||
value: "upload",
|
||||
extra: { flowID, numIDs: 1, deviceID: "client-b" },
|
||||
extra: { flowID, numIDs: 2, deviceID: "client-b" },
|
||||
},
|
||||
{ object: "repair",
|
||||
method: "response",
|
||||
value: "upload",
|
||||
extra: { flowID, deviceID: "client-b", numIDs: 1 },
|
||||
extra: { flowID, deviceID: "client-b", numIDs: 2 },
|
||||
},
|
||||
{ object: "repair",
|
||||
method: "finished",
|
||||
|
@ -496,17 +496,17 @@ add_task(async function test_requestor_already_repairing_continue() {
|
|||
{ method: "started",
|
||||
object: "repair",
|
||||
value: undefined,
|
||||
extra: { flowID, numIDs: "3" },
|
||||
extra: { flowID, numIDs: "4" },
|
||||
},
|
||||
{ method: "request",
|
||||
object: "repair",
|
||||
value: "upload",
|
||||
extra: { flowID, numIDs: "3", deviceID: "client-a" },
|
||||
extra: { flowID, numIDs: "4", deviceID: "client-a" },
|
||||
},
|
||||
{ method: "aborted",
|
||||
object: "repair",
|
||||
value: undefined,
|
||||
extra: { flowID, numIDs: "3", reason: "other clients repairing" },
|
||||
extra: { flowID, numIDs: "4", reason: "other clients repairing" },
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
@ -2881,10 +2881,15 @@ extern "C" {
|
|||
param:
|
||||
*const ::std::os::raw::c_char,
|
||||
paramLen: u32,
|
||||
prefix:
|
||||
*const ::std::os::raw::c_char,
|
||||
prefixParam:
|
||||
*const ::std::os::raw::c_char,
|
||||
prefixParamLen: u32,
|
||||
suffix:
|
||||
*const ::std::os::raw::c_char,
|
||||
source:
|
||||
*const ::std::os::raw::c_char,
|
||||
sourceLen: u32, lineNumber: u32,
|
||||
colNumber: u32, aURI: *mut nsIURI,
|
||||
followup:
|
||||
*const ::std::os::raw::c_char);
|
||||
colNumber: u32);
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ use selectors::parser::SelectorParseError;
|
|||
#[cfg(feature = "servo")] use servo_config::prefs::PREFS;
|
||||
use shared_lock::StylesheetGuards;
|
||||
use style_traits::{PARSING_MODE_DEFAULT, HasViewportPercentage, ToCss, ParseError};
|
||||
use style_traits::{PropertyDeclarationParseError, StyleParseError};
|
||||
use style_traits::{PropertyDeclarationParseError, StyleParseError, ValueParseError};
|
||||
use stylesheets::{CssRuleType, MallocSizeOf, MallocSizeOfFn, Origin, UrlExtraData};
|
||||
#[cfg(feature = "servo")] use values::Either;
|
||||
use values::generics::text::LineHeight;
|
||||
|
@ -1489,7 +1489,8 @@ impl PropertyDeclaration {
|
|||
Ok(keyword) => DeclaredValueOwned::CSSWideKeyword(keyword),
|
||||
Err(_) => match ::custom_properties::SpecifiedValue::parse(context, input) {
|
||||
Ok(value) => DeclaredValueOwned::Value(value),
|
||||
Err(_) => return Err(PropertyDeclarationParseError::InvalidValue(name.to_string().into())),
|
||||
Err(e) => return Err(PropertyDeclarationParseError::InvalidValue(name.to_string().into(),
|
||||
ValueParseError::from_parse_error(e))),
|
||||
}
|
||||
};
|
||||
declarations.push(PropertyDeclaration::Custom(name, value));
|
||||
|
@ -1502,13 +1503,14 @@ impl PropertyDeclaration {
|
|||
input.look_for_var_functions();
|
||||
let start = input.position();
|
||||
input.parse_entirely(|input| id.parse_value(context, input))
|
||||
.or_else(|_| {
|
||||
.or_else(|err| {
|
||||
while let Ok(_) = input.next() {} // Look for var() after the error.
|
||||
if input.seen_var_functions() {
|
||||
input.reset(start);
|
||||
let (first_token_type, css) =
|
||||
::custom_properties::parse_non_custom_with_var(input).map_err(|_| {
|
||||
PropertyDeclarationParseError::InvalidValue(id.name().into())
|
||||
::custom_properties::parse_non_custom_with_var(input).map_err(|e| {
|
||||
PropertyDeclarationParseError::InvalidValue(id.name().into(),
|
||||
ValueParseError::from_parse_error(e))
|
||||
})?;
|
||||
Ok(PropertyDeclaration::WithVariables(id, Arc::new(UnparsedValue {
|
||||
css: css.into_owned(),
|
||||
|
@ -1517,7 +1519,8 @@ impl PropertyDeclaration {
|
|||
from_shorthand: None,
|
||||
})))
|
||||
} else {
|
||||
Err(PropertyDeclarationParseError::InvalidValue(id.name().into()))
|
||||
Err(PropertyDeclarationParseError::InvalidValue(id.name().into(),
|
||||
ValueParseError::from_parse_error(err)))
|
||||
}
|
||||
})
|
||||
}).map(|declaration| {
|
||||
|
@ -1539,13 +1542,14 @@ impl PropertyDeclaration {
|
|||
let start = input.position();
|
||||
// Not using parse_entirely here: each ${shorthand.ident}::parse_into function
|
||||
// needs to do so *before* pushing to `declarations`.
|
||||
id.parse_into(declarations, context, input).or_else(|_| {
|
||||
id.parse_into(declarations, context, input).or_else(|err| {
|
||||
while let Ok(_) = input.next() {} // Look for var() after the error.
|
||||
if input.seen_var_functions() {
|
||||
input.reset(start);
|
||||
let (first_token_type, css) =
|
||||
::custom_properties::parse_non_custom_with_var(input).map_err(|_| {
|
||||
PropertyDeclarationParseError::InvalidValue(id.name().into())
|
||||
::custom_properties::parse_non_custom_with_var(input).map_err(|e| {
|
||||
PropertyDeclarationParseError::InvalidValue(id.name().into(),
|
||||
ValueParseError::from_parse_error(e))
|
||||
})?;
|
||||
let unparsed = Arc::new(UnparsedValue {
|
||||
css: css.into_owned(),
|
||||
|
@ -1564,7 +1568,8 @@ impl PropertyDeclaration {
|
|||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Err(PropertyDeclarationParseError::InvalidValue(id.name().into()))
|
||||
Err(PropertyDeclarationParseError::InvalidValue(id.name().into(),
|
||||
ValueParseError::from_parse_error(err)))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ use parser::{ParserContext, Parse};
|
|||
use properties::longhands::color::SystemColor;
|
||||
use std::fmt;
|
||||
use std::io::Write;
|
||||
use style_traits::{ToCss, ParseError, StyleParseError};
|
||||
use style_traits::{ToCss, ParseError, StyleParseError, ValueParseError};
|
||||
use super::AllowQuirks;
|
||||
use values::computed::{Color as ComputedColor, Context, ToComputedValue};
|
||||
|
||||
|
@ -76,24 +76,28 @@ impl Parse for Color {
|
|||
_ => None,
|
||||
};
|
||||
input.reset(start_position);
|
||||
if let Ok(value) = input.try(CSSParserColor::parse) {
|
||||
Ok(match value {
|
||||
CSSParserColor::CurrentColor => Color::CurrentColor,
|
||||
CSSParserColor::RGBA(rgba) => Color::Numeric {
|
||||
parsed: rgba,
|
||||
authored: authored,
|
||||
},
|
||||
})
|
||||
} else {
|
||||
#[cfg(feature = "gecko")] {
|
||||
if let Ok(system) = input.try(SystemColor::parse) {
|
||||
Ok(Color::System(system))
|
||||
} else {
|
||||
gecko::SpecialColorKeyword::parse(input).map(Color::Special)
|
||||
match input.try(CSSParserColor::parse) {
|
||||
Ok(value) =>
|
||||
Ok(match value {
|
||||
CSSParserColor::CurrentColor => Color::CurrentColor,
|
||||
CSSParserColor::RGBA(rgba) => Color::Numeric {
|
||||
parsed: rgba,
|
||||
authored: authored,
|
||||
},
|
||||
}),
|
||||
Err(e) => {
|
||||
#[cfg(feature = "gecko")] {
|
||||
if let Ok(system) = input.try(SystemColor::parse) {
|
||||
return Ok(Color::System(system));
|
||||
} else if let Ok(c) = gecko::SpecialColorKeyword::parse(input) {
|
||||
return Ok(Color::Special(c));
|
||||
}
|
||||
}
|
||||
match e {
|
||||
BasicParseError::UnexpectedToken(t) =>
|
||||
Err(StyleParseError::ValueError(ValueParseError::InvalidColor(t)).into()),
|
||||
e => Err(e.into())
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "gecko"))] {
|
||||
Err(StyleParseError::UnspecifiedError.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -161,11 +165,13 @@ impl Color {
|
|||
input: &mut Parser<'i, 't>,
|
||||
allow_quirks: AllowQuirks)
|
||||
-> Result<Self, ParseError<'i>> {
|
||||
input.try(|i| Self::parse(context, i)).or_else(|_| {
|
||||
input.try(|i| Self::parse(context, i)).or_else(|e| {
|
||||
if !allow_quirks.allowed(context.quirks_mode) {
|
||||
return Err(StyleParseError::UnspecifiedError.into());
|
||||
return Err(e);
|
||||
}
|
||||
Color::parse_quirky_color(input).map(|rgba| Color::rgba(rgba))
|
||||
Color::parse_quirky_color(input)
|
||||
.map(|rgba| Color::rgba(rgba))
|
||||
.map_err(|_| e)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -124,10 +124,31 @@ pub enum StyleParseError<'i> {
|
|||
UnspecifiedError,
|
||||
/// An unexpected token was found within a namespace rule.
|
||||
UnexpectedTokenWithinNamespace(Token<'i>),
|
||||
/// An error was encountered while parsing a property value.
|
||||
ValueError(ValueParseError<'i>),
|
||||
}
|
||||
|
||||
/// Specific errors that can be encountered while parsing property values.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ValueParseError<'i> {
|
||||
/// An invalid token was encountered while parsing a color value.
|
||||
InvalidColor(Token<'i>),
|
||||
}
|
||||
|
||||
impl<'i> ValueParseError<'i> {
|
||||
/// Attempt to extract a ValueParseError value from a ParseError.
|
||||
pub fn from_parse_error(this: ParseError<'i>) -> Option<ValueParseError<'i>> {
|
||||
match this {
|
||||
cssparser::ParseError::Custom(
|
||||
SelectorParseError::Custom(
|
||||
StyleParseError::ValueError(e))) => Some(e),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The result of parsing a property declaration.
|
||||
#[derive(Eq, PartialEq, Clone, Debug)]
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub enum PropertyDeclarationParseError<'i> {
|
||||
/// The property declaration was for an unknown property.
|
||||
UnknownProperty(CowRcStr<'i>),
|
||||
|
@ -136,7 +157,7 @@ pub enum PropertyDeclarationParseError<'i> {
|
|||
/// The property declaration was for a disabled experimental property.
|
||||
ExperimentalProperty,
|
||||
/// The property declaration contained an invalid value.
|
||||
InvalidValue(CowRcStr<'i>),
|
||||
InvalidValue(CowRcStr<'i>, Option<ValueParseError<'i>>),
|
||||
/// The declaration contained an animation property, and we were parsing
|
||||
/// this as a keyframe block (so that property should be ignored).
|
||||
///
|
||||
|
|
|
@ -18,7 +18,7 @@ use style::gecko_bindings::structs::ErrorReporter as GeckoErrorReporter;
|
|||
use style::gecko_bindings::structs::URLExtraData as RawUrlExtraData;
|
||||
use style::gecko_bindings::sugar::refptr::RefPtr;
|
||||
use style::stylesheets::UrlExtraData;
|
||||
use style_traits::{ParseError, StyleParseError, PropertyDeclarationParseError};
|
||||
use style_traits::{ParseError, StyleParseError, PropertyDeclarationParseError, ValueParseError};
|
||||
|
||||
/// Wrapper around an instance of Gecko's CSS error reporter.
|
||||
pub struct ErrorReporter(*mut GeckoErrorReporter);
|
||||
|
@ -194,8 +194,59 @@ enum Action {
|
|||
|
||||
trait ErrorHelpers<'a> {
|
||||
fn error_data(self) -> (CowRcStr<'a>, ParseError<'a>);
|
||||
fn error_param(self) -> ErrorString<'a>;
|
||||
fn to_gecko_message(&self) -> (&'static [u8], Action);
|
||||
fn error_params(self) -> (ErrorString<'a>, Option<ErrorString<'a>>);
|
||||
fn to_gecko_message(&self) -> (Option<&'static [u8]>, &'static [u8], Action);
|
||||
}
|
||||
|
||||
fn extract_error_param<'a>(err: ParseError<'a>) -> Option<ErrorString<'a>> {
|
||||
Some(match err {
|
||||
CssParseError::Basic(BasicParseError::UnexpectedToken(t)) =>
|
||||
ErrorString::UnexpectedToken(t),
|
||||
|
||||
CssParseError::Basic(BasicParseError::AtRuleInvalid(i)) =>
|
||||
ErrorString::Snippet(format!("@{}", escape_css_ident(&i)).into()),
|
||||
|
||||
CssParseError::Custom(SelectorParseError::Custom(
|
||||
StyleParseError::PropertyDeclaration(
|
||||
PropertyDeclarationParseError::InvalidValue(property, None)))) =>
|
||||
ErrorString::Snippet(property),
|
||||
|
||||
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)))) =>
|
||||
ErrorString::Ident(property),
|
||||
|
||||
CssParseError::Custom(SelectorParseError::Custom(
|
||||
StyleParseError::UnexpectedTokenWithinNamespace(token))) =>
|
||||
ErrorString::UnexpectedToken(token),
|
||||
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
fn extract_value_error_param<'a>(err: ValueParseError<'a>) -> ErrorString<'a> {
|
||||
match err {
|
||||
ValueParseError::InvalidColor(t) => ErrorString::UnexpectedToken(t),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
CssParseError::Custom(SelectorParseError::Custom(
|
||||
StyleParseError::PropertyDeclaration(
|
||||
PropertyDeclarationParseError::InvalidValue(property, Some(e))))) =>
|
||||
Some((ErrorString::Snippet(property.into()), Some(extract_value_error_param(e)))),
|
||||
|
||||
err => extract_error_param(err).map(|e| (e, None)),
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ErrorHelpers<'a> for ContextualParseError<'a> {
|
||||
|
@ -222,40 +273,13 @@ impl<'a> ErrorHelpers<'a> for ContextualParseError<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn error_param(self) -> ErrorString<'a> {
|
||||
match self.error_data() {
|
||||
(_, CssParseError::Basic(BasicParseError::UnexpectedToken(t))) =>
|
||||
ErrorString::UnexpectedToken(t),
|
||||
|
||||
(_, CssParseError::Basic(BasicParseError::AtRuleInvalid(i))) =>
|
||||
ErrorString::Snippet(format!("@{}", escape_css_ident(&i)).into()),
|
||||
|
||||
(_, CssParseError::Custom(SelectorParseError::Custom(
|
||||
StyleParseError::PropertyDeclaration(
|
||||
PropertyDeclarationParseError::InvalidValue(property))))) =>
|
||||
ErrorString::Snippet(property),
|
||||
|
||||
(_, 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))))) =>
|
||||
ErrorString::Ident(property),
|
||||
|
||||
(_, CssParseError::Custom(SelectorParseError::Custom(
|
||||
StyleParseError::UnexpectedTokenWithinNamespace(token)))) =>
|
||||
ErrorString::UnexpectedToken(token),
|
||||
|
||||
(s, _) => ErrorString::Snippet(s)
|
||||
}
|
||||
fn error_params(self) -> (ErrorString<'a>, Option<ErrorString<'a>>) {
|
||||
let (s, error) = self.error_data();
|
||||
extract_error_params(error).unwrap_or((ErrorString::Snippet(s), None))
|
||||
}
|
||||
|
||||
fn to_gecko_message(&self) -> (&'static [u8], Action) {
|
||||
match *self {
|
||||
fn to_gecko_message(&self) -> (Option<&'static [u8]>, &'static [u8], Action) {
|
||||
let (msg, action): (&[u8], Action) = match *self {
|
||||
ContextualParseError::UnsupportedPropertyDeclaration(
|
||||
_, CssParseError::Basic(BasicParseError::UnexpectedToken(_))) |
|
||||
ContextualParseError::UnsupportedPropertyDeclaration(
|
||||
|
@ -264,8 +288,13 @@ impl<'a> ErrorHelpers<'a> for ContextualParseError<'a> {
|
|||
ContextualParseError::UnsupportedPropertyDeclaration(
|
||||
_, CssParseError::Custom(SelectorParseError::Custom(
|
||||
StyleParseError::PropertyDeclaration(
|
||||
PropertyDeclarationParseError::InvalidValue(_))))) =>
|
||||
(b"PEValueParsingError\0", Action::Drop),
|
||||
PropertyDeclarationParseError::InvalidValue(_, ref err))))) => {
|
||||
let prefix = match *err {
|
||||
Some(ValueParseError::InvalidColor(_)) => Some(&b"PEColorNotColor\0"[..]),
|
||||
_ => None,
|
||||
};
|
||||
return (prefix, b"PEValueParsingError\0", Action::Drop);
|
||||
}
|
||||
ContextualParseError::UnsupportedPropertyDeclaration(..) =>
|
||||
(b"PEUnknownProperty\0", Action::Drop),
|
||||
ContextualParseError::UnsupportedFontFaceDescriptor(..) =>
|
||||
|
@ -295,7 +324,8 @@ impl<'a> ErrorHelpers<'a> for ContextualParseError<'a> {
|
|||
ContextualParseError::UnsupportedFontFeatureValuesDescriptor(..) |
|
||||
ContextualParseError::InvalidFontFeatureValuesRule(..) =>
|
||||
(b"PEUnknownAtRule\0", Action::Skip),
|
||||
}
|
||||
};
|
||||
(None, msg, action)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -304,18 +334,21 @@ impl ParseErrorReporter for ErrorReporter {
|
|||
input: &mut Parser,
|
||||
position: SourcePosition,
|
||||
error: ContextualParseError<'a>,
|
||||
url: &UrlExtraData,
|
||||
_url: &UrlExtraData,
|
||||
line_number_offset: u64) {
|
||||
let location = input.source_location(position);
|
||||
let line_number = location.line + line_number_offset as u32;
|
||||
|
||||
let (name, action) = error.to_gecko_message();
|
||||
let followup = match action {
|
||||
let (pre, name, action) = error.to_gecko_message();
|
||||
let suffix = match action {
|
||||
Action::Nothing => ptr::null(),
|
||||
Action::Skip => b"PEDeclSkipped\0".as_ptr(),
|
||||
Action::Drop => b"PEDeclDropped\0".as_ptr(),
|
||||
};
|
||||
let param = error.error_param().into_str();
|
||||
let (param, pre_param) = error.error_params();
|
||||
let param = param.into_str();
|
||||
let pre_param = pre_param.map(|p| p.into_str());
|
||||
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 {
|
||||
|
@ -323,12 +356,14 @@ impl ParseErrorReporter for ErrorReporter {
|
|||
name.as_ptr() as *const _,
|
||||
param.as_ptr() as *const _,
|
||||
param.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,
|
||||
suffix as *const _,
|
||||
source.as_ptr() as *const _,
|
||||
source.len() as u32,
|
||||
line_number as u32,
|
||||
location.column as u32,
|
||||
url.mBaseURI.raw::<nsIURI>(),
|
||||
followup as *const _);
|
||||
location.column as u32);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -378,7 +378,7 @@ fn test_report_error_stylesheet() {
|
|||
|
||||
let error = errors.pop().unwrap();
|
||||
assert_eq!("Unsupported property declaration: 'display: invalid;', \
|
||||
Custom(PropertyDeclaration(InvalidValue(\"display\")))", error.message);
|
||||
Custom(PropertyDeclaration(InvalidValue(\"display\", None)))", error.message);
|
||||
assert_eq!(8, error.line);
|
||||
assert_eq!(8, error.column);
|
||||
|
||||
|
|
|
@ -184,6 +184,9 @@ win32-devedition/opt:
|
|||
config:
|
||||
- builds/taskcluster_firefox_windows_32_opt.py
|
||||
run-on-projects: ['mozilla-beta', ]
|
||||
toolchains:
|
||||
- win32-clang-cl
|
||||
- win64-sccache
|
||||
|
||||
win64-devedition/opt:
|
||||
description: "Win64 Dev Edition Opt"
|
||||
|
@ -205,6 +208,9 @@ win64-devedition/opt:
|
|||
config:
|
||||
- builds/taskcluster_firefox_windows_64_opt.py
|
||||
run-on-projects: ['mozilla-beta', ]
|
||||
toolchains:
|
||||
- win64-clang-cl
|
||||
- win64-sccache
|
||||
|
||||
win32-nightly/opt:
|
||||
description: "Win32 Nightly"
|
||||
|
@ -430,6 +436,9 @@ win64-asan/opt:
|
|||
config:
|
||||
- builds/taskcluster_firefox_win64_asan_opt.py
|
||||
run-on-projects: []
|
||||
toolchains:
|
||||
- win64-clang-cl
|
||||
- win64-sccache
|
||||
|
||||
win32-devedition-nightly/opt:
|
||||
description: "Win32 Dev Edition Nightly"
|
||||
|
@ -457,6 +466,9 @@ win32-devedition-nightly/opt:
|
|||
- taskcluster_nightly.py
|
||||
custom-build-variant-cfg: devedition
|
||||
run-on-projects: [ 'mozilla-beta', ]
|
||||
toolchains:
|
||||
- win32-clang-cl
|
||||
- win64-sccache
|
||||
|
||||
win64-devedition-nightly/opt:
|
||||
description: "Win64 Dev Edition Nightly"
|
||||
|
|
|
@ -41,7 +41,7 @@ class TestSelect(SelectTestCase):
|
|||
options[1].click()
|
||||
self.assertSelected(options[1])
|
||||
|
||||
def test_deselect(self):
|
||||
def test_deselect_others(self):
|
||||
self.marionette.navigate(inline("""
|
||||
<select>
|
||||
<option>first
|
||||
|
@ -60,6 +60,22 @@ class TestSelect(SelectTestCase):
|
|||
options[0].click()
|
||||
self.assertSelected(options[0])
|
||||
|
||||
def test_select_self(self):
|
||||
self.marionette.navigate(inline("""
|
||||
<select>
|
||||
<option>first
|
||||
<option>second
|
||||
</select>"""))
|
||||
select = self.marionette.find_element(By.TAG_NAME, "select")
|
||||
options = self.marionette.find_elements(By.TAG_NAME, "option")
|
||||
self.assertSelected(options[0])
|
||||
self.assertNotSelected(options[1])
|
||||
|
||||
options[1].click()
|
||||
self.assertSelected(options[1])
|
||||
options[1].click()
|
||||
self.assertSelected(options[1])
|
||||
|
||||
def test_out_of_view(self):
|
||||
self.marionette.navigate(inline("""
|
||||
<select>
|
||||
|
|
|
@ -9,12 +9,13 @@ const {utils: Cu} = Components;
|
|||
Cu.import("chrome://marionette/content/accessibility.js");
|
||||
Cu.import("chrome://marionette/content/atom.js");
|
||||
const {
|
||||
ElementClickInterceptedError,
|
||||
ElementNotInteractableError,
|
||||
error,
|
||||
InvalidArgument,
|
||||
ElementNotInteractableError,
|
||||
ElementClickInterceptedError,
|
||||
InvalidElementStateError,
|
||||
InvalidArgumentError,
|
||||
InvalidElementStateError,
|
||||
pprint,
|
||||
} = Cu.import("chrome://marionette/content/error.js", {});
|
||||
Cu.import("chrome://marionette/content/element.js");
|
||||
Cu.import("chrome://marionette/content/event.js");
|
||||
|
@ -281,10 +282,10 @@ function* seleniumClickElement(el, a11y) {
|
|||
*/
|
||||
interaction.selectOption = function(el) {
|
||||
if (element.isXULElement(el)) {
|
||||
throw new Error("XUL dropdowns not supported");
|
||||
throw new TypeError("XUL dropdowns not supported");
|
||||
}
|
||||
if (el.localName != "option") {
|
||||
throw new TypeError("Invalid elements");
|
||||
throw new TypeError(pprint`Expected <option> element, got ${el}`);
|
||||
}
|
||||
|
||||
let containerEl = element.getContainer(el);
|
||||
|
@ -295,8 +296,14 @@ interaction.selectOption = function(el) {
|
|||
event.focus(containerEl);
|
||||
event.input(containerEl);
|
||||
|
||||
// toggle selectedness the way holding down control works
|
||||
el.selected = !el.selected;
|
||||
// Clicking <option> in <select> should not be deselected if selected.
|
||||
// However, clicking one in a <select multiple> should toggle
|
||||
// selectedness the way holding down Control works.
|
||||
if (containerEl.multiple) {
|
||||
el.selected = !el.selected;
|
||||
} else if (!el.selected) {
|
||||
el.selected = true;
|
||||
}
|
||||
|
||||
event.change(containerEl);
|
||||
event.mouseup(containerEl);
|
||||
|
|
|
@ -305535,6 +305535,11 @@
|
|||
{}
|
||||
]
|
||||
],
|
||||
"webdriver/tests/element_click/__init__.py": [
|
||||
[
|
||||
{}
|
||||
]
|
||||
],
|
||||
"webdriver/tests/support/__init__.py": [
|
||||
[
|
||||
{}
|
||||
|
@ -402871,6 +402876,12 @@
|
|||
{}
|
||||
]
|
||||
],
|
||||
"webdriver/tests/element_click/select.py": [
|
||||
[
|
||||
"/webdriver/tests/element_click/select.py",
|
||||
{}
|
||||
]
|
||||
],
|
||||
"webdriver/tests/navigation.py": [
|
||||
[
|
||||
"/webdriver/tests/navigation.py",
|
||||
|
@ -621666,6 +621677,14 @@
|
|||
"e31177e638269864031e44808945fa1e7c46031c",
|
||||
"wdspec"
|
||||
],
|
||||
"webdriver/tests/element_click/__init__.py": [
|
||||
"da39a3ee5e6b4b0d3255bfef95601890afd80709",
|
||||
"support"
|
||||
],
|
||||
"webdriver/tests/element_click/select.py": [
|
||||
"5ba51b660c7203bba3ada597c2f56fe094358e1f",
|
||||
"wdspec"
|
||||
],
|
||||
"webdriver/tests/navigation.py": [
|
||||
"cec2987258d9c807a247da9e0216b3af1f171484",
|
||||
"wdspec"
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
[uri-013.xht]
|
||||
type: reftest
|
||||
expected:
|
||||
if stylo: FAIL
|
||||
expected: FAIL
|
||||
|
|
|
@ -637,10 +637,17 @@ class Element(object):
|
|||
def rect(self):
|
||||
return self.send_element_command("GET", "rect")
|
||||
|
||||
@property
|
||||
@command
|
||||
def property(self, name):
|
||||
return self.send_element_command("GET", "property/%s" % name)
|
||||
def selected(self):
|
||||
return self.send_element_command("GET", "selected")
|
||||
|
||||
@command
|
||||
def attribute(self, name):
|
||||
return self.send_element_command("GET", "attribute/%s" % name)
|
||||
|
||||
# This MUST come last because otherwise @property decorators above
|
||||
# will be overridden by this.
|
||||
@command
|
||||
def property(self, name):
|
||||
return self.send_element_command("GET", "property/%s" % name)
|
||||
|
|
|
@ -0,0 +1,213 @@
|
|||
from tests.support.inline import inline
|
||||
|
||||
|
||||
def test_click_option(session):
|
||||
session.url = inline("""
|
||||
<select>
|
||||
<option>first
|
||||
<option>second
|
||||
</select>""")
|
||||
options = session.find.css("option")
|
||||
|
||||
assert options[0].selected
|
||||
assert not options[1].selected
|
||||
|
||||
options[1].click()
|
||||
assert options[1].selected
|
||||
assert not options[0].selected
|
||||
|
||||
|
||||
def test_click_multiple_option(session):
|
||||
session.url = inline("""
|
||||
<select multiple>
|
||||
<option>first
|
||||
<option>second
|
||||
</select>""")
|
||||
options = session.find.css("option")
|
||||
|
||||
assert not options[0].selected
|
||||
assert not options[1].selected
|
||||
|
||||
options[0].click()
|
||||
assert options[0].selected
|
||||
assert not options[1].selected
|
||||
|
||||
|
||||
def test_click_preselected_option(session):
|
||||
session.url = inline("""
|
||||
<select>
|
||||
<option>first
|
||||
<option selected>second
|
||||
</select>""")
|
||||
options = session.find.css("option")
|
||||
|
||||
assert not options[0].selected
|
||||
assert options[1].selected
|
||||
|
||||
options[1].click()
|
||||
assert options[1].selected
|
||||
assert not options[0].selected
|
||||
|
||||
options[0].click()
|
||||
assert options[0].selected
|
||||
assert not options[1].selected
|
||||
|
||||
|
||||
def test_click_preselected_multiple_option(session):
|
||||
session.url = inline("""
|
||||
<select multiple>
|
||||
<option>first
|
||||
<option selected>second
|
||||
</select>""")
|
||||
options = session.find.css("option")
|
||||
|
||||
assert not options[0].selected
|
||||
assert options[1].selected
|
||||
|
||||
options[1].click()
|
||||
assert not options[1].selected
|
||||
assert not options[0].selected
|
||||
|
||||
options[0].click()
|
||||
assert options[0].selected
|
||||
assert not options[1].selected
|
||||
|
||||
|
||||
def test_click_deselects_others(session):
|
||||
session.url = inline("""
|
||||
<select>
|
||||
<option>first
|
||||
<option>second
|
||||
<option>third
|
||||
</select>""")
|
||||
options = session.find.css("option")
|
||||
|
||||
options[0].click()
|
||||
assert options[0].selected
|
||||
options[1].click()
|
||||
assert options[1].selected
|
||||
options[2].click()
|
||||
assert options[2].selected
|
||||
options[0].click()
|
||||
assert options[0].selected
|
||||
|
||||
|
||||
def test_click_multiple_does_not_deselect_others(session):
|
||||
session.url = inline("""
|
||||
<select multiple>
|
||||
<option>first
|
||||
<option>second
|
||||
<option>third
|
||||
</select>""")
|
||||
options = session.find.css("option")
|
||||
|
||||
options[0].click()
|
||||
assert options[0].selected
|
||||
options[1].click()
|
||||
assert options[0].selected
|
||||
assert options[1].selected
|
||||
options[2].click()
|
||||
assert options[0].selected
|
||||
assert options[1].selected
|
||||
assert options[2].selected
|
||||
|
||||
|
||||
def test_click_selected_option(session):
|
||||
session.url = inline("""
|
||||
<select>
|
||||
<option>first
|
||||
<option>second
|
||||
</select>""")
|
||||
options = session.find.css("option")
|
||||
|
||||
# First <option> is selected in dropdown
|
||||
assert options[0].selected
|
||||
assert not options[1].selected
|
||||
|
||||
options[1].click()
|
||||
assert options[1].selected
|
||||
options[1].click()
|
||||
assert options[1].selected
|
||||
|
||||
|
||||
def test_click_selected_multiple_option(session):
|
||||
session.url = inline("""
|
||||
<select multiple>
|
||||
<option>first
|
||||
<option>second
|
||||
</select>""")
|
||||
options = session.find.css("option")
|
||||
|
||||
# No implicitly selected <option> in <select multiple>
|
||||
assert not options[0].selected
|
||||
assert not options[1].selected
|
||||
|
||||
options[0].click()
|
||||
assert options[0].selected
|
||||
assert not options[1].selected
|
||||
|
||||
# Second click in <select multiple> deselects
|
||||
options[0].click()
|
||||
assert not options[0].selected
|
||||
assert not options[1].selected
|
||||
|
||||
|
||||
def test_out_of_view_dropdown(session):
|
||||
session.url = inline("""
|
||||
<select>
|
||||
<option>1
|
||||
<option>2
|
||||
<option>3
|
||||
<option>4
|
||||
<option>5
|
||||
<option>6
|
||||
<option>7
|
||||
<option>8
|
||||
<option>9
|
||||
<option>10
|
||||
<option>11
|
||||
<option>12
|
||||
<option>13
|
||||
<option>14
|
||||
<option>15
|
||||
<option>16
|
||||
<option>17
|
||||
<option>18
|
||||
<option>19
|
||||
<option>20
|
||||
</select>""")
|
||||
options = session.find.css("option")
|
||||
|
||||
options[14].click()
|
||||
assert options[14].selected
|
||||
|
||||
|
||||
def test_out_of_view_multiple(session):
|
||||
session.url = inline("""
|
||||
<select multiple>
|
||||
<option>1
|
||||
<option>2
|
||||
<option>3
|
||||
<option>4
|
||||
<option>5
|
||||
<option>6
|
||||
<option>7
|
||||
<option>8
|
||||
<option>9
|
||||
<option>10
|
||||
<option>11
|
||||
<option>12
|
||||
<option>13
|
||||
<option>14
|
||||
<option>15
|
||||
<option>16
|
||||
<option>17
|
||||
<option>18
|
||||
<option>19
|
||||
<option>20
|
||||
</select>""")
|
||||
options = session.find.css("option")
|
||||
|
||||
last_option = options[-1]
|
||||
last_option.click()
|
||||
assert last_option.selected
|
|
@ -640,7 +640,15 @@ class ProxyAPIImplementation extends SchemaAPIInterface {
|
|||
this.childApiManager.callParentFunctionNoReturn(this.path, args);
|
||||
}
|
||||
|
||||
callAsyncFunction(args, callback) {
|
||||
callAsyncFunction(args, callback, requireUserInput) {
|
||||
if (requireUserInput) {
|
||||
let context = this.childApiManager.context;
|
||||
let winUtils = context.contentWindow.getInterface(Ci.nsIDOMWindowUtils);
|
||||
if (!winUtils.isHandlingUserInput) {
|
||||
let err = new context.cloneScope.Error(`${this.path} may only be called from a user input handler`);
|
||||
return context.wrapPromise(Promise.reject(err), callback);
|
||||
}
|
||||
}
|
||||
return this.childApiManager.callParentAsyncFunction(this.path, args, callback);
|
||||
}
|
||||
|
||||
|
|
|
@ -450,10 +450,12 @@ class SchemaAPIInterface {
|
|||
* @param {Array} args The parameters for the function.
|
||||
* @param {function(*)} [callback] The callback to be called when the function
|
||||
* completes.
|
||||
* @param {boolean} [requireUserInput=false] If true, the function should
|
||||
* fail if the browser is not currently handling user input.
|
||||
* @returns {Promise|undefined} Must be void if `callback` is set, and a
|
||||
* promise otherwise. The promise is resolved when the function completes.
|
||||
*/
|
||||
callAsyncFunction(args, callback) {
|
||||
callAsyncFunction(args, callback, requireUserInput = false) {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
|
@ -559,9 +561,16 @@ class LocalAPIImplementation extends SchemaAPIInterface {
|
|||
this.pathObj[this.name](...args);
|
||||
}
|
||||
|
||||
callAsyncFunction(args, callback) {
|
||||
callAsyncFunction(args, callback, requireUserInput) {
|
||||
let promise;
|
||||
try {
|
||||
if (requireUserInput) {
|
||||
let winUtils = this.context.contentWindow
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
if (!winUtils.isHandlingUserInput) {
|
||||
throw new ExtensionError(`${this.name} may only be called from a user input handler`);
|
||||
}
|
||||
}
|
||||
promise = this.pathObj[this.name](...args) || Promise.resolve();
|
||||
} catch (e) {
|
||||
promise = Promise.reject(e);
|
||||
|
|
|
@ -1836,7 +1836,8 @@ class ArrayType extends Type {
|
|||
|
||||
class FunctionType extends Type {
|
||||
static get EXTRA_PROPERTIES() {
|
||||
return ["parameters", "async", "returns", ...super.EXTRA_PROPERTIES];
|
||||
return ["parameters", "async", "returns", "requireUserInput",
|
||||
...super.EXTRA_PROPERTIES];
|
||||
}
|
||||
|
||||
static parseSchema(schema, path, extraProperties = []) {
|
||||
|
@ -1887,14 +1888,16 @@ class FunctionType extends Type {
|
|||
}
|
||||
|
||||
|
||||
return new this(schema, parameters, isAsync, hasAsyncCallback);
|
||||
return new this(schema, parameters, isAsync, hasAsyncCallback,
|
||||
!!schema.requireUserInput);
|
||||
}
|
||||
|
||||
constructor(schema, parameters, isAsync, hasAsyncCallback) {
|
||||
constructor(schema, parameters, isAsync, hasAsyncCallback, requireUserInput) {
|
||||
super(schema);
|
||||
this.parameters = parameters;
|
||||
this.isAsync = isAsync;
|
||||
this.hasAsyncCallback = hasAsyncCallback;
|
||||
this.requireUserInput = requireUserInput;
|
||||
}
|
||||
|
||||
normalize(value, context) {
|
||||
|
@ -2167,6 +2170,7 @@ FunctionEntry = class FunctionEntry extends CallEntry {
|
|||
|
||||
this.isAsync = type.isAsync;
|
||||
this.hasAsyncCallback = type.hasAsyncCallback;
|
||||
this.requireUserInput = type.requireUserInput;
|
||||
}
|
||||
|
||||
checkValue({type, optional, name}, value, context) {
|
||||
|
@ -2216,7 +2220,7 @@ FunctionEntry = class FunctionEntry extends CallEntry {
|
|||
original(...args);
|
||||
};
|
||||
}
|
||||
let result = apiImpl.callAsyncFunction(actuals, callback);
|
||||
let result = apiImpl.callAsyncFunction(actuals, callback, this.requireUserInput);
|
||||
if (DEBUG && this.hasAsyncCallback && !callback) {
|
||||
return result.then(result => {
|
||||
this.checkCallback([result], context);
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
var {
|
||||
ExtensionError,
|
||||
} = ExtensionUtils;
|
||||
|
||||
this.downloads = class extends ExtensionAPI {
|
||||
getAPI(context) {
|
||||
return {
|
||||
downloads: {
|
||||
open(downloadId) {
|
||||
let winUtils = context.contentWindow.getInterface(Ci.nsIDOMWindowUtils);
|
||||
if (!winUtils.isHandlingUserInput) {
|
||||
throw new ExtensionError("May only open downloads from a user input handler");
|
||||
}
|
||||
|
||||
return context.childManager.callParentAsyncFunction("downloads.open_parent", [downloadId]);
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
|
@ -1,22 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
var {
|
||||
ExtensionError,
|
||||
} = ExtensionUtils;
|
||||
|
||||
this.permissions = class extends ExtensionAPI {
|
||||
getAPI(context) {
|
||||
return {
|
||||
permissions: {
|
||||
async request(perms) {
|
||||
let winUtils = context.contentWindow.getInterface(Ci.nsIDOMWindowUtils);
|
||||
if (!winUtils.isHandlingUserInput) {
|
||||
throw new ExtensionError("May only request permissions from a user input handler");
|
||||
}
|
||||
|
||||
return context.childManager.callParentAsyncFunction("permissions.request_parent", [perms]);
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
|
@ -41,13 +41,6 @@ extensions.registerModules({
|
|||
["runtime", "getBackgroundPage"],
|
||||
],
|
||||
},
|
||||
downloads: {
|
||||
url: "chrome://extensions/content/ext-c-downloads.js",
|
||||
scopes: ["addon_child"],
|
||||
paths: [
|
||||
["downloads"],
|
||||
],
|
||||
},
|
||||
extension: {
|
||||
url: "chrome://extensions/content/ext-c-extension.js",
|
||||
scopes: ["addon_child", "content_child", "devtools_child", "proxy_script"],
|
||||
|
@ -62,13 +55,6 @@ extensions.registerModules({
|
|||
["i18n"],
|
||||
],
|
||||
},
|
||||
permissions: {
|
||||
url: "chrome://extensions/content/ext-c-permissions.js",
|
||||
scopes: ["addon_child", "content_child", "devtools_child", "proxy_script"],
|
||||
paths: [
|
||||
["permissions"],
|
||||
],
|
||||
},
|
||||
runtime: {
|
||||
url: "chrome://extensions/content/ext-c-runtime.js",
|
||||
scopes: ["addon_child", "content_child", "devtools_child", "proxy_script"],
|
||||
|
|
|
@ -645,7 +645,7 @@ this.downloads = class extends ExtensionAPI {
|
|||
});
|
||||
},
|
||||
|
||||
open_parent(downloadId) {
|
||||
open(downloadId) {
|
||||
return DownloadMap.lazyInit().then(() => {
|
||||
let download = DownloadMap.fromId(downloadId).download;
|
||||
if (download.succeeded) {
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче