зеркало из https://github.com/mozilla/gecko-dev.git
Merge autoland to mozilla-central a=merge
This commit is contained in:
Коммит
c748db6076
|
@ -1698,6 +1698,14 @@ pref("extensions.formautofill.creditCards.available", true);
|
|||
pref("extensions.formautofill.creditCards.available", false);
|
||||
#endif
|
||||
pref("extensions.formautofill.creditCards.enabled", true);
|
||||
// Pref for shield/heartbeat to recognize users who have used Credit Card
|
||||
// Autofill. The valid values can be:
|
||||
// 0: none
|
||||
// 1: submitted a manually-filled credit card form (but didn't see the doorhanger
|
||||
// because of a duplicate profile in the storage)
|
||||
// 2: saw the doorhanger
|
||||
// 3: submitted an autofill'ed credit card form
|
||||
pref("extensions.formautofill.creditCards.used", 0);
|
||||
pref("extensions.formautofill.firstTimeUse", true);
|
||||
pref("extensions.formautofill.heuristics.enabled", true);
|
||||
pref("extensions.formautofill.loglevel", "Warn");
|
||||
|
|
|
@ -105,7 +105,6 @@ static const mozilla::Module::ContractIDEntry kBrowserContracts[] = {
|
|||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "newtab", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "preferences", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "downloads", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "accounts", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
||||
#ifdef MOZ_SERVICES_HEALTHREPORT
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "healthreport", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
||||
#endif
|
||||
|
|
|
@ -71,7 +71,7 @@ const CONTENT = {
|
|||
hideClose: true,
|
||||
},
|
||||
},
|
||||
update: {
|
||||
updateAddress: {
|
||||
notificationId: "autofill-address",
|
||||
message: GetStringFromName("updateAddressMessage"),
|
||||
linkMessage: GetStringFromName(autofillOptsKey),
|
||||
|
@ -96,7 +96,7 @@ const CONTENT = {
|
|||
hideClose: true,
|
||||
},
|
||||
},
|
||||
creditCard: {
|
||||
addCreditCard: {
|
||||
notificationId: "autofill-credit-card",
|
||||
message: formatStringFromName("saveCreditCardMessage", [brandShortName], 1),
|
||||
linkMessage: GetStringFromName(autofillSecurityOptionsKey),
|
||||
|
@ -149,6 +149,31 @@ const CONTENT = {
|
|||
},
|
||||
},
|
||||
},
|
||||
updateCreditCard: {
|
||||
notificationId: "autofill-credit-card",
|
||||
message: GetStringFromName("updateCreditCardMessage"),
|
||||
linkMessage: GetStringFromName(autofillOptsKey),
|
||||
anchor: {
|
||||
id: "autofill-credit-card-notification-icon",
|
||||
URL: "chrome://formautofill/content/formfill-anchor.svg",
|
||||
tooltiptext: GetStringFromName("openAutofillMessagePanel"),
|
||||
},
|
||||
mainAction: {
|
||||
label: GetStringFromName("updateCreditCardLabel"),
|
||||
accessKey: "U",
|
||||
callbackState: "update",
|
||||
},
|
||||
secondaryActions: [{
|
||||
label: GetStringFromName("createCreditCardLabel"),
|
||||
accessKey: "C",
|
||||
callbackState: "create",
|
||||
}],
|
||||
options: {
|
||||
persistWhileVisible: true,
|
||||
popupIconURL: "chrome://formautofill/content/icon-credit-card.svg",
|
||||
hideClose: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
let FormAutofillDoorhanger = {
|
||||
|
|
|
@ -375,7 +375,7 @@ FormAutofillParent.prototype = {
|
|||
if (!this.profileStorage.addresses.mergeIfPossible(address.guid, address.record, true)) {
|
||||
this._recordFormFillingTime("address", "autofill-update", timeStartedFillingMS);
|
||||
|
||||
FormAutofillDoorhanger.show(target, "update").then((state) => {
|
||||
FormAutofillDoorhanger.show(target, "updateAddress").then((state) => {
|
||||
let changedGUIDs = this.profileStorage.addresses.mergeToStorage(address.record, true);
|
||||
switch (state) {
|
||||
case "create":
|
||||
|
@ -411,8 +411,8 @@ FormAutofillParent.prototype = {
|
|||
this._recordFormFillingTime("address", "manual", timeStartedFillingMS);
|
||||
|
||||
// Show first time use doorhanger
|
||||
if (Services.prefs.getBoolPref("extensions.formautofill.firstTimeUse")) {
|
||||
Services.prefs.setBoolPref("extensions.formautofill.firstTimeUse", false);
|
||||
if (FormAutofillUtils.isAutofillAddressesFirstTimeUse) {
|
||||
Services.prefs.setBoolPref(FormAutofillUtils.ADDRESSES_FIRST_TIME_USE_PREF, false);
|
||||
FormAutofillDoorhanger.show(target, "firstTimeUse").then((state) => {
|
||||
if (state !== "open-pref") {
|
||||
return;
|
||||
|
@ -429,10 +429,21 @@ FormAutofillParent.prototype = {
|
|||
},
|
||||
|
||||
async _onCreditCardSubmit(creditCard, target, timeStartedFillingMS) {
|
||||
// Updates the used status for shield/heartbeat to recognize users who have
|
||||
// used Credit Card Autofill.
|
||||
let setUsedStatus = status => {
|
||||
if (FormAutofillUtils.AutofillCreditCardsUsedStatus < status) {
|
||||
Services.prefs.setIntPref(FormAutofillUtils.CREDITCARDS_USED_STATUS_PREF, status);
|
||||
}
|
||||
};
|
||||
|
||||
// We'll show the credit card doorhanger if:
|
||||
// - User applys autofill and changed
|
||||
// - User fills form manually and the filling data is not duplicated to storage
|
||||
if (creditCard.guid) {
|
||||
// Indicate that the user has used Credit Card Autofill to fill in a form.
|
||||
setUsedStatus(3);
|
||||
|
||||
let originalCCData = this.profileStorage.creditCards.get(creditCard.guid);
|
||||
let unchanged = Object.keys(creditCard.record).every(field => {
|
||||
if (creditCard.record[field] === "" && !originalCCData[field]) {
|
||||
|
@ -457,6 +468,11 @@ FormAutofillParent.prototype = {
|
|||
Services.telemetry.scalarAdd("formautofill.creditCards.fill_type_autofill_modified", 1);
|
||||
this._recordFormFillingTime("creditCard", "autofill-update", timeStartedFillingMS);
|
||||
} else {
|
||||
// Indicate that the user neither sees the doorhanger nor uses Autofill
|
||||
// but somehow has a duplicate record in the storage. Will be reset to 2
|
||||
// if the doorhanger actually shows below.
|
||||
setUsedStatus(1);
|
||||
|
||||
// Add the probe to record credit card manual filling.
|
||||
Services.telemetry.scalarAdd("formautofill.creditCards.fill_type_manual", 1);
|
||||
this._recordFormFillingTime("creditCard", "manual", timeStartedFillingMS);
|
||||
|
@ -469,7 +485,10 @@ FormAutofillParent.prototype = {
|
|||
return;
|
||||
}
|
||||
|
||||
let state = await FormAutofillDoorhanger.show(target, "creditCard");
|
||||
// Indicate that the user has seen the doorhanger.
|
||||
setUsedStatus(2);
|
||||
|
||||
let state = await FormAutofillDoorhanger.show(target, creditCard.guid ? "updateCreditCard" : "addCreditCard");
|
||||
if (state == "cancel") {
|
||||
return;
|
||||
}
|
||||
|
@ -487,10 +506,13 @@ FormAutofillParent.prototype = {
|
|||
}
|
||||
|
||||
let changedGUIDs = [];
|
||||
// TODO: Autofill(with guid) case should show update doorhanger with update/create new.
|
||||
// It'll be implemented in bug 1403881 and only avoid mergering for now.
|
||||
if (creditCard.guid) {
|
||||
changedGUIDs.push(this.profileStorage.creditCards.add(creditCard.record));
|
||||
if (state == "update") {
|
||||
this.profileStorage.creditCards.update(creditCard.guid, creditCard.record, true);
|
||||
changedGUIDs.push(creditCard.guid);
|
||||
} else if ("create") {
|
||||
changedGUIDs.push(this.profileStorage.creditCards.add(creditCard.record));
|
||||
}
|
||||
} else {
|
||||
changedGUIDs.push(...this.profileStorage.creditCards.mergeToStorage(creditCard.record));
|
||||
if (!changedGUIDs.length) {
|
||||
|
|
|
@ -18,7 +18,9 @@ const ALTERNATIVE_COUNTRY_NAMES = {
|
|||
|
||||
const ADDRESSES_COLLECTION_NAME = "addresses";
|
||||
const CREDITCARDS_COLLECTION_NAME = "creditCards";
|
||||
const ADDRESSES_FIRST_TIME_USE_PREF = "extensions.formautofill.firstTimeUse";
|
||||
const ENABLED_AUTOFILL_ADDRESSES_PREF = "extensions.formautofill.addresses.enabled";
|
||||
const CREDITCARDS_USED_STATUS_PREF = "extensions.formautofill.creditCards.used";
|
||||
const AUTOFILL_CREDITCARDS_AVAILABLE_PREF = "extensions.formautofill.creditCards.available";
|
||||
const ENABLED_AUTOFILL_CREDITCARDS_PREF = "extensions.formautofill.creditCards.enabled";
|
||||
const MANAGE_ADDRESSES_KEYWORDS = ["manageAddressesTitle", "addNewAddressTitle"];
|
||||
|
@ -45,6 +47,8 @@ this.FormAutofillUtils = {
|
|||
CREDITCARDS_COLLECTION_NAME,
|
||||
ENABLED_AUTOFILL_ADDRESSES_PREF,
|
||||
ENABLED_AUTOFILL_CREDITCARDS_PREF,
|
||||
ADDRESSES_FIRST_TIME_USE_PREF,
|
||||
CREDITCARDS_USED_STATUS_PREF,
|
||||
MANAGE_ADDRESSES_KEYWORDS,
|
||||
EDIT_ADDRESS_KEYWORDS,
|
||||
MANAGE_CREDITCARDS_KEYWORDS,
|
||||
|
@ -608,3 +612,7 @@ XPCOMUtils.defineLazyPreferenceGetter(this.FormAutofillUtils,
|
|||
"isAutofillCreditCardsAvailable", AUTOFILL_CREDITCARDS_AVAILABLE_PREF);
|
||||
XPCOMUtils.defineLazyPreferenceGetter(this.FormAutofillUtils,
|
||||
"_isAutofillCreditCardsEnabled", ENABLED_AUTOFILL_CREDITCARDS_PREF);
|
||||
XPCOMUtils.defineLazyPreferenceGetter(this.FormAutofillUtils,
|
||||
"isAutofillAddressesFirstTimeUse", ADDRESSES_FIRST_TIME_USE_PREF);
|
||||
XPCOMUtils.defineLazyPreferenceGetter(this.FormAutofillUtils,
|
||||
"AutofillCreditCardsUsedStatus", CREDITCARDS_USED_STATUS_PREF);
|
||||
|
|
|
@ -35,6 +35,11 @@ saveCreditCardMessage = Would you like %S to save this credit card? (Security co
|
|||
saveCreditCardLabel = Save Credit Card
|
||||
cancelCreditCardLabel = Don’t Save
|
||||
neverSaveCreditCardLabel = Never Save Credit Cards
|
||||
# LOCALIZATION NOTE (updateCreditCardMessage, createCreditCardLabel, updateCreditCardLabel): Used on the doorhanger
|
||||
# when an credit card change is detected.
|
||||
updateCreditCardMessage = Would you like to update your credit card with this new information?
|
||||
createCreditCardLabel = Create New Credit Card
|
||||
updateCreditCardLabel = Update Credit Card
|
||||
# LOCALIZATION NOTE (openAutofillMessagePanel): Tooltip label for Form Autofill doorhanger icon on address bar.
|
||||
openAutofillMessagePanel = Open Form Autofill message panel
|
||||
|
||||
|
|
|
@ -20,27 +20,6 @@ add_task(async function setup_storage() {
|
|||
await saveAddress(TEST_ADDRESS_5);
|
||||
});
|
||||
|
||||
add_task(async function test_click_on_footer() {
|
||||
await BrowserTestUtils.withNewTab({gBrowser, url: URL}, async function(browser) {
|
||||
const {autoCompletePopup: {richlistbox: itemsBox}} = browser;
|
||||
|
||||
await openPopupOn(browser, "#organization");
|
||||
// Click on the footer
|
||||
const optionButton = itemsBox.querySelector(".autocomplete-richlistitem:last-child")._optionButton;
|
||||
const prefTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, PRIVACY_PREF_URL);
|
||||
// Wait for dropdown animation finished to continue mouse synthesizing.
|
||||
await sleep(1000);
|
||||
await EventUtils.synthesizeMouseAtCenter(optionButton, {});
|
||||
info(`expecting tab: about:preferences#privacy opened`);
|
||||
const prefTab = await prefTabPromise;
|
||||
info(`expecting tab: about:preferences#privacy removed`);
|
||||
await BrowserTestUtils.removeTab(prefTab);
|
||||
ok(true, "Tab: preferences#privacy was successfully opened by clicking on the footer");
|
||||
|
||||
await closePopup(browser);
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_press_enter_on_footer() {
|
||||
await BrowserTestUtils.withNewTab({gBrowser, url: URL}, async function(browser) {
|
||||
const {autoCompletePopup: {richlistbox: itemsBox}} = browser;
|
||||
|
@ -63,6 +42,27 @@ add_task(async function test_press_enter_on_footer() {
|
|||
});
|
||||
});
|
||||
|
||||
add_task(async function test_click_on_footer() {
|
||||
await BrowserTestUtils.withNewTab({gBrowser, url: URL}, async function(browser) {
|
||||
const {autoCompletePopup: {richlistbox: itemsBox}} = browser;
|
||||
|
||||
await openPopupOn(browser, "#organization");
|
||||
// Click on the footer
|
||||
const optionButton = itemsBox.querySelector(".autocomplete-richlistitem:last-child")._optionButton;
|
||||
const prefTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, PRIVACY_PREF_URL);
|
||||
// Wait for dropdown animation finished to continue mouse synthesizing.
|
||||
await sleep(3000);
|
||||
await EventUtils.synthesizeMouseAtCenter(optionButton, {});
|
||||
info(`expecting tab: about:preferences#privacy opened`);
|
||||
const prefTab = await prefTabPromise;
|
||||
info(`expecting tab: about:preferences#privacy removed`);
|
||||
await BrowserTestUtils.removeTab(prefTab);
|
||||
ok(true, "Tab: preferences#privacy was successfully opened by clicking on the footer");
|
||||
|
||||
await closePopup(browser);
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_phishing_warning_single_category() {
|
||||
await BrowserTestUtils.withNewTab({gBrowser, url: URL}, async function(browser) {
|
||||
const {autoCompletePopup: {richlistbox: itemsBox}} = browser;
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
"use strict";
|
||||
|
||||
add_task(async function test_submit_creditCard_cancel_saving() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
"set": [
|
||||
[CREDITCARDS_USED_STATUS_PREF, 0],
|
||||
],
|
||||
});
|
||||
await BrowserTestUtils.withNewTab({gBrowser, url: CREDITCARD_FORM_URL},
|
||||
async function(browser) {
|
||||
let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
|
||||
|
@ -32,9 +37,16 @@ add_task(async function test_submit_creditCard_cancel_saving() {
|
|||
await sleep(1000);
|
||||
let creditCards = await getCreditCards();
|
||||
is(creditCards.length, 0, "No credit card saved");
|
||||
is(SpecialPowers.getIntPref(CREDITCARDS_USED_STATUS_PREF), 2, "User has seen the doorhanger");
|
||||
SpecialPowers.clearUserPref(CREDITCARDS_USED_STATUS_PREF);
|
||||
});
|
||||
|
||||
add_task(async function test_submit_creditCard_saved() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
"set": [
|
||||
[CREDITCARDS_USED_STATUS_PREF, 0],
|
||||
],
|
||||
});
|
||||
await BrowserTestUtils.withNewTab({gBrowser, url: CREDITCARD_FORM_URL},
|
||||
async function(browser) {
|
||||
let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
|
||||
|
@ -62,10 +74,17 @@ add_task(async function test_submit_creditCard_saved() {
|
|||
let creditCards = await getCreditCards();
|
||||
is(creditCards.length, 1, "1 credit card in storage");
|
||||
is(creditCards[0]["cc-name"], "User 1", "Verify the name field");
|
||||
is(SpecialPowers.getIntPref(CREDITCARDS_USED_STATUS_PREF), 2, "User has seen the doorhanger");
|
||||
SpecialPowers.clearUserPref(CREDITCARDS_USED_STATUS_PREF);
|
||||
await removeAllRecords();
|
||||
});
|
||||
|
||||
add_task(async function test_submit_untouched_creditCard_form() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
"set": [
|
||||
[CREDITCARDS_USED_STATUS_PREF, 0],
|
||||
],
|
||||
});
|
||||
await saveCreditCard(TEST_CREDIT_CARD_1);
|
||||
let creditCards = await getCreditCards();
|
||||
is(creditCards.length, 1, "1 credit card in storage");
|
||||
|
@ -90,10 +109,17 @@ add_task(async function test_submit_untouched_creditCard_form() {
|
|||
creditCards = await getCreditCards();
|
||||
is(creditCards.length, 1, "Still 1 credit card");
|
||||
is(creditCards[0].timesUsed, 1, "timesUsed field set to 1");
|
||||
is(SpecialPowers.getIntPref(CREDITCARDS_USED_STATUS_PREF), 3, "User has used autofill");
|
||||
SpecialPowers.clearUserPref(CREDITCARDS_USED_STATUS_PREF);
|
||||
await removeAllRecords();
|
||||
});
|
||||
|
||||
add_task(async function test_submit_changed_subset_creditCard_form() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
"set": [
|
||||
[CREDITCARDS_USED_STATUS_PREF, 0],
|
||||
],
|
||||
});
|
||||
await saveCreditCard(TEST_CREDIT_CARD_1);
|
||||
let creditCards = await getCreditCards();
|
||||
is(creditCards.length, 1, "1 credit card in storage");
|
||||
|
@ -125,10 +151,17 @@ add_task(async function test_submit_changed_subset_creditCard_form() {
|
|||
creditCards = await getCreditCards();
|
||||
is(creditCards.length, 1, "Still 1 credit card in storage");
|
||||
is(creditCards[0]["cc-name"], TEST_CREDIT_CARD_1["cc-name"], "name field still exists");
|
||||
is(SpecialPowers.getIntPref(CREDITCARDS_USED_STATUS_PREF), 2, "User has seen the doorhanger");
|
||||
SpecialPowers.clearUserPref(CREDITCARDS_USED_STATUS_PREF);
|
||||
await removeAllRecords();
|
||||
});
|
||||
|
||||
add_task(async function test_submit_duplicate_creditCard_form() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
"set": [
|
||||
[CREDITCARDS_USED_STATUS_PREF, 0],
|
||||
],
|
||||
});
|
||||
await saveCreditCard(TEST_CREDIT_CARD_1);
|
||||
let creditCards = await getCreditCards();
|
||||
is(creditCards.length, 1, "1 credit card in storage");
|
||||
|
@ -158,6 +191,9 @@ add_task(async function test_submit_duplicate_creditCard_form() {
|
|||
is(creditCards.length, 1, "Still 1 credit card in storage");
|
||||
is(creditCards[0]["cc-name"], TEST_CREDIT_CARD_1["cc-name"], "Verify the name field");
|
||||
is(creditCards[0].timesUsed, 1, "timesUsed field set to 1");
|
||||
is(SpecialPowers.getIntPref(CREDITCARDS_USED_STATUS_PREF), 1,
|
||||
"User neither sees the doorhanger nor uses autofill but somehow has a record in the storage");
|
||||
SpecialPowers.clearUserPref(CREDITCARDS_USED_STATUS_PREF);
|
||||
await removeAllRecords();
|
||||
});
|
||||
|
||||
|
@ -195,6 +231,11 @@ add_task(async function test_submit_unnormailzed_creditCard_form() {
|
|||
});
|
||||
|
||||
add_task(async function test_submit_creditCard_never_save() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
"set": [
|
||||
[CREDITCARDS_USED_STATUS_PREF, 0],
|
||||
],
|
||||
});
|
||||
await BrowserTestUtils.withNewTab({gBrowser, url: CREDITCARD_FORM_URL},
|
||||
async function(browser) {
|
||||
let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
|
||||
|
@ -224,6 +265,8 @@ add_task(async function test_submit_creditCard_never_save() {
|
|||
let creditCardPref = SpecialPowers.getBoolPref(ENABLED_AUTOFILL_CREDITCARDS_PREF);
|
||||
is(creditCards.length, 0, "No credit card in storage");
|
||||
is(creditCardPref, false, "Credit card is disabled");
|
||||
is(SpecialPowers.getIntPref(CREDITCARDS_USED_STATUS_PREF), 2, "User has seen the doorhanger");
|
||||
SpecialPowers.clearUserPref(CREDITCARDS_USED_STATUS_PREF);
|
||||
SpecialPowers.clearUserPref(ENABLED_AUTOFILL_CREDITCARDS_PREF);
|
||||
});
|
||||
|
||||
|
@ -390,6 +433,11 @@ add_task(async function test_submit_creditCard_with_synced_already() {
|
|||
});
|
||||
|
||||
add_task(async function test_submit_manual_mergeable_creditCard_form() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
"set": [
|
||||
[CREDITCARDS_USED_STATUS_PREF, 0],
|
||||
],
|
||||
});
|
||||
await saveCreditCard(TEST_CREDIT_CARD_3);
|
||||
let creditCards = await getCreditCards();
|
||||
is(creditCards.length, 1, "1 credit card in storage");
|
||||
|
@ -419,5 +467,131 @@ add_task(async function test_submit_manual_mergeable_creditCard_form() {
|
|||
creditCards = await getCreditCards();
|
||||
is(creditCards.length, 1, "Still 1 credit card in storage");
|
||||
is(creditCards[0]["cc-name"], "User 3", "Verify the name field");
|
||||
is(SpecialPowers.getIntPref(CREDITCARDS_USED_STATUS_PREF), 2, "User has seen the doorhanger");
|
||||
SpecialPowers.clearUserPref(CREDITCARDS_USED_STATUS_PREF);
|
||||
await removeAllRecords();
|
||||
});
|
||||
|
||||
add_task(async function test_update_autofill_form() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
"set": [
|
||||
[CREDITCARDS_USED_STATUS_PREF, 0],
|
||||
],
|
||||
});
|
||||
await saveCreditCard(TEST_CREDIT_CARD_1);
|
||||
let creditCards = await getCreditCards();
|
||||
is(creditCards.length, 1, "1 credit card in storage");
|
||||
await BrowserTestUtils.withNewTab({gBrowser, url: CREDITCARD_FORM_URL},
|
||||
async function(browser) {
|
||||
let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
|
||||
"popupshown");
|
||||
await openPopupOn(browser, "form #cc-name");
|
||||
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
|
||||
await BrowserTestUtils.synthesizeKey("VK_RETURN", {}, browser);
|
||||
await ContentTask.spawn(browser, null, async function() {
|
||||
let form = content.document.getElementById("form");
|
||||
let name = form.querySelector("#cc-name");
|
||||
name.setUserInput("User 1");
|
||||
|
||||
// Wait 1000ms before submission to make sure the input value applied
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
form.querySelector("input[type=submit]").click();
|
||||
});
|
||||
|
||||
await promiseShown;
|
||||
await clickDoorhangerButton(MAIN_BUTTON);
|
||||
}
|
||||
);
|
||||
|
||||
creditCards = await getCreditCards();
|
||||
is(creditCards.length, 1, "Still 1 credit card");
|
||||
is(creditCards[0]["cc-name"], "User 1", "cc-name field is updated");
|
||||
is(SpecialPowers.getIntPref(CREDITCARDS_USED_STATUS_PREF), 3, "User has used autofill");
|
||||
SpecialPowers.clearUserPref(CREDITCARDS_USED_STATUS_PREF);
|
||||
await removeAllRecords();
|
||||
});
|
||||
|
||||
add_task(async function test_create_new_autofill_form() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
"set": [
|
||||
[CREDITCARDS_USED_STATUS_PREF, 0],
|
||||
],
|
||||
});
|
||||
await saveCreditCard(TEST_CREDIT_CARD_1);
|
||||
let creditCards = await getCreditCards();
|
||||
is(creditCards.length, 1, "1 credit card in storage");
|
||||
await BrowserTestUtils.withNewTab({gBrowser, url: CREDITCARD_FORM_URL},
|
||||
async function(browser) {
|
||||
let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
|
||||
"popupshown");
|
||||
await openPopupOn(browser, "form #cc-name");
|
||||
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
|
||||
await BrowserTestUtils.synthesizeKey("VK_RETURN", {}, browser);
|
||||
await ContentTask.spawn(browser, null, async function() {
|
||||
let form = content.document.getElementById("form");
|
||||
let name = form.querySelector("#cc-name");
|
||||
name.setUserInput("User 1");
|
||||
|
||||
// Wait 1000ms before submission to make sure the input value applied
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
form.querySelector("input[type=submit]").click();
|
||||
});
|
||||
|
||||
await promiseShown;
|
||||
await clickDoorhangerButton(SECONDARY_BUTTON);
|
||||
}
|
||||
);
|
||||
|
||||
creditCards = await getCreditCards();
|
||||
is(creditCards.length, 2, "2 credit cards in storage");
|
||||
is(creditCards[0]["cc-name"], TEST_CREDIT_CARD_1["cc-name"],
|
||||
"Original record's cc-name field is unchanged");
|
||||
is(creditCards[1]["cc-name"], "User 1", "cc-name field in the new record");
|
||||
is(SpecialPowers.getIntPref(CREDITCARDS_USED_STATUS_PREF), 3, "User has used autofill");
|
||||
SpecialPowers.clearUserPref(CREDITCARDS_USED_STATUS_PREF);
|
||||
await removeAllRecords();
|
||||
});
|
||||
|
||||
add_task(async function test_update_duplicate_autofill_form() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
"set": [
|
||||
[CREDITCARDS_USED_STATUS_PREF, 0],
|
||||
],
|
||||
});
|
||||
await saveCreditCard({
|
||||
"cc-number": "1234123412341234",
|
||||
});
|
||||
await saveCreditCard({
|
||||
"cc-number": "1111222233334444",
|
||||
});
|
||||
let creditCards = await getCreditCards();
|
||||
is(creditCards.length, 2, "2 credit card in storage");
|
||||
await BrowserTestUtils.withNewTab({gBrowser, url: CREDITCARD_FORM_URL},
|
||||
async function(browser) {
|
||||
await openPopupOn(browser, "form #cc-number");
|
||||
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
|
||||
await BrowserTestUtils.synthesizeKey("VK_RETURN", {}, browser);
|
||||
await ContentTask.spawn(browser, null, async function() {
|
||||
let form = content.document.getElementById("form");
|
||||
let number = form.querySelector("#cc-number");
|
||||
is(number.value, "1234123412341234", "Should be the first credit card number");
|
||||
// Change number to the second credit card number
|
||||
number.setUserInput("1111222233334444");
|
||||
|
||||
// Wait 1000ms before submission to make sure the input value applied
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
form.querySelector("input[type=submit]").click();
|
||||
});
|
||||
|
||||
await sleep(1000);
|
||||
is(PopupNotifications.panel.state, "closed", "Doorhanger is hidden");
|
||||
}
|
||||
);
|
||||
|
||||
creditCards = await getCreditCards();
|
||||
is(creditCards.length, 2, "Still 2 credit card");
|
||||
is(SpecialPowers.getIntPref(CREDITCARDS_USED_STATUS_PREF), 1,
|
||||
"User neither sees the doorhanger nor uses autofill but somehow has a record in the storage");
|
||||
SpecialPowers.clearUserPref(CREDITCARDS_USED_STATUS_PREF);
|
||||
await removeAllRecords();
|
||||
});
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
BASE_URL, TEST_ADDRESS_1, TEST_ADDRESS_2, TEST_ADDRESS_3, TEST_ADDRESS_4, TEST_ADDRESS_5,
|
||||
TEST_CREDIT_CARD_1, TEST_CREDIT_CARD_2, TEST_CREDIT_CARD_3, FORM_URL, CREDITCARD_FORM_URL,
|
||||
FTU_PREF, ENABLED_AUTOFILL_ADDRESSES_PREF, AUTOFILL_CREDITCARDS_AVAILABLE_PREF, ENABLED_AUTOFILL_CREDITCARDS_PREF,
|
||||
SYNC_USERNAME_PREF, SYNC_ADDRESSES_PREF, SYNC_CREDITCARDS_PREF, SYNC_CREDITCARDS_AVAILABLE_PREF,
|
||||
SYNC_USERNAME_PREF, SYNC_ADDRESSES_PREF, SYNC_CREDITCARDS_PREF, SYNC_CREDITCARDS_AVAILABLE_PREF, CREDITCARDS_USED_STATUS_PREF,
|
||||
sleep, expectPopupOpen, openPopupOn, expectPopupClose, closePopup, clickDoorhangerButton,
|
||||
getAddresses, saveAddress, removeAddresses, saveCreditCard,
|
||||
getDisplayedPopupItems, getDoorhangerCheckbox, waitForMasterPasswordDialog,
|
||||
|
@ -21,6 +21,7 @@ const FORM_URL = "http://mochi.test:8888/browser/browser/extensions/formautofill
|
|||
const CREDITCARD_FORM_URL =
|
||||
"https://example.org/browser/browser/extensions/formautofill/test/browser/autocomplete_creditcard_basic.html";
|
||||
const FTU_PREF = "extensions.formautofill.firstTimeUse";
|
||||
const CREDITCARDS_USED_STATUS_PREF = "extensions.formautofill.creditCards.used";
|
||||
const ENABLED_AUTOFILL_ADDRESSES_PREF = "extensions.formautofill.addresses.enabled";
|
||||
const AUTOFILL_CREDITCARDS_AVAILABLE_PREF = "extensions.formautofill.creditCards.available";
|
||||
const ENABLED_AUTOFILL_CREDITCARDS_PREF = "extensions.formautofill.creditCards.enabled";
|
||||
|
|
|
@ -170,8 +170,9 @@ class HeadersPanel extends Component {
|
|||
this.getProperties(uploadHeaders, REQUEST_HEADERS_FROM_UPLOAD),
|
||||
);
|
||||
|
||||
// not showing #hash in url
|
||||
let summaryUrl = urlDetails.unicodeUrl ?
|
||||
this.renderSummary(SUMMARY_URL, new URL(urlDetails.unicodeUrl).origin) : null;
|
||||
this.renderSummary(SUMMARY_URL, urlDetails.unicodeUrl.split("#")[0]) : null;
|
||||
|
||||
let summaryMethod = method ?
|
||||
this.renderSummary(SUMMARY_METHOD, method) : null;
|
||||
|
|
|
@ -30,6 +30,7 @@ add_task(function* () {
|
|||
// request #0
|
||||
method: "GET",
|
||||
uri: STATUS_CODES_SJS + "?sts=100",
|
||||
correctUri: STATUS_CODES_SJS + "?sts=100",
|
||||
details: {
|
||||
status: 101,
|
||||
statusText: "Switching Protocols",
|
||||
|
@ -42,7 +43,8 @@ add_task(function* () {
|
|||
{
|
||||
// request #1
|
||||
method: "GET",
|
||||
uri: STATUS_CODES_SJS + "?sts=200",
|
||||
uri: STATUS_CODES_SJS + "?sts=200#doh",
|
||||
correctUri: STATUS_CODES_SJS + "?sts=200",
|
||||
details: {
|
||||
status: 202,
|
||||
statusText: "Created",
|
||||
|
@ -56,6 +58,7 @@ add_task(function* () {
|
|||
// request #2
|
||||
method: "GET",
|
||||
uri: STATUS_CODES_SJS + "?sts=300",
|
||||
correctUri: STATUS_CODES_SJS + "?sts=300",
|
||||
details: {
|
||||
status: 303,
|
||||
statusText: "See Other",
|
||||
|
@ -69,6 +72,7 @@ add_task(function* () {
|
|||
// request #3
|
||||
method: "GET",
|
||||
uri: STATUS_CODES_SJS + "?sts=400",
|
||||
correctUri: STATUS_CODES_SJS + "?sts=400",
|
||||
details: {
|
||||
status: 404,
|
||||
statusText: "Not Found",
|
||||
|
@ -82,6 +86,7 @@ add_task(function* () {
|
|||
// request #4
|
||||
method: "GET",
|
||||
uri: STATUS_CODES_SJS + "?sts=500",
|
||||
correctUri: STATUS_CODES_SJS + "?sts=500",
|
||||
details: {
|
||||
status: 501,
|
||||
statusText: "Not Implemented",
|
||||
|
@ -169,9 +174,9 @@ add_task(function* () {
|
|||
|
||||
let panel = document.querySelector("#headers-panel");
|
||||
let summaryValues = panel.querySelectorAll(".tabpanel-summary-value.textbox-input");
|
||||
let { method, uri, details: { status, statusText } } = data;
|
||||
let { method, correctUri, details: { status, statusText } } = data;
|
||||
|
||||
is(summaryValues[0].value, new URL(uri).origin,
|
||||
is(summaryValues[0].value, correctUri,
|
||||
"The url summary value is incorrect.");
|
||||
is(summaryValues[1].value, method, "The method summary value is incorrect.");
|
||||
is(panel.querySelector(".requests-list-status-icon").dataset.code, status,
|
||||
|
@ -190,7 +195,8 @@ add_task(function* () {
|
|||
document.querySelector("#params-tab"));
|
||||
|
||||
let panel = document.querySelector("#params-panel");
|
||||
let statusParamValue = data.uri.split("=").pop();
|
||||
// Bug 1414981 - Request URL should not show #hash
|
||||
let statusParamValue = data.uri.split("=").pop().split("#")[0];
|
||||
let treeSections = panel.querySelectorAll(".tree-section");
|
||||
|
||||
is(treeSections.length, 1,
|
||||
|
|
|
@ -402,7 +402,8 @@ function verifyRequestItemTarget(document, requestList, requestItem, method,
|
|||
transferred, size, time, displayedStatus } = data;
|
||||
|
||||
let target = document.querySelectorAll(".request-list-item")[visibleIndex];
|
||||
let unicodeUrl = decodeUnicodeUrl(url);
|
||||
// Bug 1414981 - Request URL should not show #hash
|
||||
let unicodeUrl = decodeUnicodeUrl(url).split("#")[0];
|
||||
let name = getUrlBaseName(url);
|
||||
let query = getUrlQuery(url);
|
||||
let host = getUrlHost(url);
|
||||
|
@ -424,7 +425,7 @@ function verifyRequestItemTarget(document, requestList, requestItem, method,
|
|||
ok(requestItem.url.startsWith(url), "The attached url is correct.");
|
||||
} else {
|
||||
is(requestItem.method, method, "The attached method is correct.");
|
||||
is(requestItem.url, url, "The attached url is correct.");
|
||||
is(requestItem.url, url.split("#")[0], "The attached url is correct.");
|
||||
}
|
||||
|
||||
is(target.querySelector(".requests-list-method").textContent,
|
||||
|
|
|
@ -294,6 +294,7 @@ ChannelMediaResource::OnStartRequest(nsIRequest* aRequest,
|
|||
UpdatePrincipal();
|
||||
|
||||
mCacheStream.NotifyDataStarted(mLoadID, startOffset, seekable);
|
||||
mIsTransportSeekable = seekable;
|
||||
mChannelStatistics.Start();
|
||||
mReopenOnError = false;
|
||||
|
||||
|
@ -318,7 +319,8 @@ ChannelMediaResource::OnStartRequest(nsIRequest* aRequest,
|
|||
bool
|
||||
ChannelMediaResource::IsTransportSeekable()
|
||||
{
|
||||
return mCacheStream.IsTransportSeekable();
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mIsTransportSeekable;
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -387,7 +389,7 @@ ChannelMediaResource::OnStopRequest(nsIRequest* aRequest, nsresult aStatus)
|
|||
if (mReopenOnError && aStatus != NS_ERROR_PARSED_DATA_CACHED &&
|
||||
aStatus != NS_BINDING_ABORTED &&
|
||||
(GetOffset() == 0 || (GetLength() > 0 && GetOffset() != GetLength() &&
|
||||
mCacheStream.IsTransportSeekable()))) {
|
||||
mIsTransportSeekable))) {
|
||||
// If the stream did close normally, restart the channel if we're either
|
||||
// at the start of the resource, or if the server is seekable and we're
|
||||
// not at the end of stream. We don't restart the stream if we're at the
|
||||
|
@ -600,6 +602,8 @@ ChannelMediaResource::CloneData(MediaResourceCallback* aCallback)
|
|||
RefPtr<ChannelMediaResource> resource =
|
||||
new ChannelMediaResource(aCallback, nullptr, mURI, mChannelStatistics);
|
||||
|
||||
resource->mIsTransportSeekable = mIsTransportSeekable;
|
||||
|
||||
// Initially the clone is treated as suspended by the cache, because
|
||||
// we don't have a channel. If the cache needs to read data from the clone
|
||||
// it will call CacheClientResume (or CacheClientSeek with aResume true)
|
||||
|
@ -692,7 +696,7 @@ ChannelMediaResource::Suspend(bool aCloseImmediately)
|
|||
dom::HTMLMediaElement* element = owner->GetMediaElement();
|
||||
MOZ_DIAGNOSTIC_ASSERT(element);
|
||||
|
||||
if (mChannel && aCloseImmediately && mCacheStream.IsTransportSeekable()) {
|
||||
if (mChannel && aCloseImmediately && mIsTransportSeekable) {
|
||||
CloseChannel();
|
||||
element->DownloadSuspended();
|
||||
}
|
||||
|
|
|
@ -247,6 +247,8 @@ protected:
|
|||
// Main thread access only
|
||||
// True if Close() has been called.
|
||||
bool mClosed = false;
|
||||
// The last reported seekability state for the underlying channel
|
||||
bool mIsTransportSeekable = false;
|
||||
RefPtr<Listener> mListener;
|
||||
// A mono-increasing integer to uniquely identify the channel we are loading.
|
||||
uint32_t mLoadID = 0;
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "MemoryBlockCache.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/ErrorNames.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/ReentrantMonitor.h"
|
||||
|
@ -24,7 +25,6 @@
|
|||
#include "nsContentUtils.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsISeekableStream.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
@ -35,9 +35,11 @@ namespace mozilla {
|
|||
|
||||
#undef LOG
|
||||
#undef LOGI
|
||||
#undef LOGE
|
||||
LazyLogModule gMediaCacheLog("MediaCache");
|
||||
#define LOG(...) MOZ_LOG(gMediaCacheLog, LogLevel::Debug, (__VA_ARGS__))
|
||||
#define LOGI(...) MOZ_LOG(gMediaCacheLog, LogLevel::Info, (__VA_ARGS__))
|
||||
#define LOGE(...) NS_DebugBreak(NS_DEBUG_WARNING, nsPrintfCString(__VA_ARGS__).get(), nullptr, __FILE__, __LINE__)
|
||||
|
||||
// For HTTP seeking, if number of bytes needing to be
|
||||
// seeked forward is less than this value then a read is
|
||||
|
@ -2198,13 +2200,6 @@ MediaCacheStream::~MediaCacheStream()
|
|||
lengthKb);
|
||||
}
|
||||
|
||||
bool
|
||||
MediaCacheStream::IsTransportSeekable()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mMediaCache->GetReentrantMonitor());
|
||||
return mIsTransportSeekable;
|
||||
}
|
||||
|
||||
bool
|
||||
MediaCacheStream::AreAllStreamsForResourceSuspended()
|
||||
{
|
||||
|
@ -2411,41 +2406,22 @@ MediaCacheStream::SetPlaybackRate(uint32_t aBytesPerSecond)
|
|||
}
|
||||
|
||||
nsresult
|
||||
MediaCacheStream::Seek(int32_t aWhence, int64_t aOffset)
|
||||
MediaCacheStream::Seek(int64_t aOffset)
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
mMediaCache->GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
|
||||
ReentrantMonitorAutoEnter mon(mMediaCache->GetReentrantMonitor());
|
||||
if (mClosed)
|
||||
return NS_ERROR_FAILURE;
|
||||
if (!IsOffsetAllowed(aOffset)) {
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
if (mClosed) {
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
int64_t oldOffset = mStreamOffset;
|
||||
int64_t newOffset = mStreamOffset;
|
||||
switch (aWhence) {
|
||||
case PR_SEEK_END:
|
||||
if (mStreamLength < 0)
|
||||
return NS_ERROR_FAILURE;
|
||||
newOffset = mStreamLength + aOffset;
|
||||
break;
|
||||
case PR_SEEK_CUR:
|
||||
newOffset += aOffset;
|
||||
break;
|
||||
case PR_SEEK_SET:
|
||||
newOffset = aOffset;
|
||||
break;
|
||||
default:
|
||||
NS_ERROR("Unknown whence");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!IsOffsetAllowed(newOffset)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
mStreamOffset = newOffset;
|
||||
|
||||
mStreamOffset = aOffset;
|
||||
LOG("Stream %p Seek to %" PRId64, this, mStreamOffset);
|
||||
mMediaCache->NoteSeek(this, oldOffset);
|
||||
|
||||
mMediaCache->QueueUpdate();
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -2462,6 +2438,72 @@ MediaCacheStream::ThrottleReadahead(bool bThrottle)
|
|||
}
|
||||
}
|
||||
|
||||
uint32_t
|
||||
MediaCacheStream::ReadPartialBlock(int64_t aOffset, Span<char> aBuffer)
|
||||
{
|
||||
mMediaCache->GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
MOZ_ASSERT(IsOffsetAllowed(aOffset));
|
||||
|
||||
if (OffsetToBlockIndexUnchecked(mChannelOffset) !=
|
||||
OffsetToBlockIndexUnchecked(aOffset) ||
|
||||
aOffset >= mChannelOffset) {
|
||||
// Not in the partial block or no data to read.
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto source = MakeSpan<const uint8_t>(
|
||||
mPartialBlockBuffer.get() + OffsetInBlock(aOffset),
|
||||
OffsetInBlock(mChannelOffset) - OffsetInBlock(aOffset));
|
||||
// We have |source.Length() <= BLOCK_SIZE < INT32_MAX| to guarantee
|
||||
// that |bytesToRead| can fit into a uint32_t.
|
||||
uint32_t bytesToRead = std::min(aBuffer.Length(), source.Length());
|
||||
memcpy(aBuffer.Elements(), source.Elements(), bytesToRead);
|
||||
return bytesToRead;
|
||||
}
|
||||
|
||||
Result<uint32_t, nsresult>
|
||||
MediaCacheStream::ReadBlockFromCache(int64_t aOffset, Span<char> aBuffer)
|
||||
{
|
||||
mMediaCache->GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
MOZ_ASSERT(IsOffsetAllowed(aOffset));
|
||||
|
||||
// OffsetToBlockIndexUnchecked() is always non-negative.
|
||||
uint32_t index = OffsetToBlockIndexUnchecked(aOffset);
|
||||
int32_t cacheBlock = index < mBlocks.Length() ? mBlocks[index] : -1;
|
||||
if (cacheBlock < 0) {
|
||||
// Not in the cache.
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (aBuffer.Length() > size_t(BLOCK_SIZE)) {
|
||||
// Clamp the buffer to avoid overflow below since we will read at most
|
||||
// BLOCK_SIZE bytes.
|
||||
aBuffer = aBuffer.First(BLOCK_SIZE);
|
||||
}
|
||||
// |BLOCK_SIZE - OffsetInBlock(aOffset)| <= BLOCK_SIZE
|
||||
int32_t bytesToRead =
|
||||
std::min<int32_t>(BLOCK_SIZE - OffsetInBlock(aOffset), aBuffer.Length());
|
||||
int32_t bytesRead = 0;
|
||||
nsresult rv =
|
||||
mMediaCache->ReadCacheFile(cacheBlock * BLOCK_SIZE + OffsetInBlock(aOffset),
|
||||
aBuffer.Elements(),
|
||||
bytesToRead,
|
||||
&bytesRead);
|
||||
|
||||
// Ensure |cacheBlock * BLOCK_SIZE + OffsetInBlock(aOffset)| won't overflow.
|
||||
static_assert(INT64_MAX >= BLOCK_SIZE * (uint32_t(INT32_MAX) + 1),
|
||||
"BLOCK_SIZE too large!");
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
nsCString name;
|
||||
GetErrorName(rv, name);
|
||||
LOGE("Stream %p ReadCacheFile failed, rv=%s", this, name.Data());
|
||||
return mozilla::Err(rv);
|
||||
}
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
int64_t
|
||||
MediaCacheStream::Tell()
|
||||
{
|
||||
|
@ -2600,73 +2642,55 @@ MediaCacheStream::ReadAt(int64_t aOffset, char* aBuffer,
|
|||
NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
|
||||
|
||||
ReentrantMonitorAutoEnter mon(mMediaCache->GetReentrantMonitor());
|
||||
nsresult rv = Seek(nsISeekableStream::NS_SEEK_SET, aOffset);
|
||||
nsresult rv = Seek(aOffset);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
return Read(aBuffer, aCount, aBytes);
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaCacheStream::ReadFromCache(char* aBuffer, int64_t aOffset, int64_t aCount)
|
||||
MediaCacheStream::ReadFromCache(char* aBuffer, int64_t aOffset, uint32_t aCount)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mMediaCache->GetReentrantMonitor());
|
||||
|
||||
// The buffer we are about to fill.
|
||||
auto buffer = MakeSpan<char>(aBuffer, aCount);
|
||||
|
||||
// Read one block (or part of a block) at a time
|
||||
uint32_t count = 0;
|
||||
int64_t streamOffset = aOffset;
|
||||
while (count < aCount) {
|
||||
while (!buffer.IsEmpty()) {
|
||||
if (mClosed) {
|
||||
// We need to check |mClosed| in each iteration which might be changed
|
||||
// after calling |mMediaCache->ReadCacheFile|.
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
int32_t streamBlock = OffsetToBlockIndex(streamOffset);
|
||||
if (streamBlock < 0) {
|
||||
break;
|
||||
}
|
||||
uint32_t offsetInStreamBlock =
|
||||
uint32_t(streamOffset - streamBlock*BLOCK_SIZE);
|
||||
int64_t size = std::min<int64_t>(aCount - count, BLOCK_SIZE - offsetInStreamBlock);
|
||||
|
||||
if (mStreamLength >= 0) {
|
||||
// Don't try to read beyond the end of the stream
|
||||
int64_t bytesRemaining = mStreamLength - streamOffset;
|
||||
if (bytesRemaining <= 0) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
size = std::min(size, bytesRemaining);
|
||||
// Clamp size until 64-bit file size issues are fixed.
|
||||
size = std::min(size, int64_t(INT32_MAX));
|
||||
if (!IsOffsetAllowed(streamOffset)) {
|
||||
LOGE("Stream %p invalid offset=%" PRId64, this, streamOffset);
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
|
||||
int32_t bytes;
|
||||
int32_t channelBlock = OffsetToBlockIndexUnchecked(mChannelOffset);
|
||||
int32_t cacheBlock =
|
||||
size_t(streamBlock) < mBlocks.Length() ? mBlocks[streamBlock] : -1;
|
||||
if (channelBlock == streamBlock && streamOffset < mChannelOffset) {
|
||||
// We can just use the data in mPartialBlockBuffer. In fact we should
|
||||
// use it rather than waiting for the block to fill and land in
|
||||
// the cache.
|
||||
// Clamp bytes until 64-bit file size issues are fixed.
|
||||
int64_t toCopy = std::min<int64_t>(size, mChannelOffset - streamOffset);
|
||||
bytes = std::min(toCopy, int64_t(INT32_MAX));
|
||||
MOZ_ASSERT(bytes >= 0 && bytes <= toCopy, "Bytes out of range.");
|
||||
memcpy(aBuffer + count,
|
||||
mPartialBlockBuffer.get() + offsetInStreamBlock, bytes);
|
||||
} else {
|
||||
if (cacheBlock < 0) {
|
||||
// We expect all blocks to be cached! Fail!
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
int64_t offset = cacheBlock*BLOCK_SIZE + offsetInStreamBlock;
|
||||
MOZ_ASSERT(size >= 0 && size <= INT32_MAX, "Size out of range.");
|
||||
nsresult rv = mMediaCache->ReadCacheFile(
|
||||
offset, aBuffer + count, int32_t(size), &bytes);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
Result<uint32_t, nsresult> rv = ReadBlockFromCache(streamOffset, buffer);
|
||||
if (rv.isErr()) {
|
||||
return rv.unwrapErr();
|
||||
}
|
||||
streamOffset += bytes;
|
||||
count += bytes;
|
||||
|
||||
uint32_t bytes = rv.unwrap();
|
||||
if (bytes > 0) {
|
||||
// Read data from the cache successfully. Let's try next block.
|
||||
streamOffset += bytes;
|
||||
buffer = buffer.From(bytes);
|
||||
continue;
|
||||
}
|
||||
|
||||
// The partial block is our last chance to get data.
|
||||
bytes = ReadPartialBlock(streamOffset, buffer);
|
||||
if (bytes < buffer.Length()) {
|
||||
// Not enough data to read.
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Return for we've got all the requested bytes.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#define MediaCache_h_
|
||||
|
||||
#include "Intervals.h"
|
||||
#include "mozilla/Result.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsHashKeys.h"
|
||||
|
@ -314,9 +315,7 @@ public:
|
|||
// in the cache. Will not mark blocks as read. Can be called from the main
|
||||
// thread. It's the caller's responsibility to wrap the call in a pin/unpin,
|
||||
// and also to check that the range they want is cached before calling this.
|
||||
nsresult ReadFromCache(char* aBuffer,
|
||||
int64_t aOffset,
|
||||
int64_t aCount);
|
||||
nsresult ReadFromCache(char* aBuffer, int64_t aOffset, uint32_t aCount);
|
||||
|
||||
// IsDataCachedToEndOfStream returns true if all the data from
|
||||
// aOffset to the end of the stream (the server-reported end, if the
|
||||
|
@ -330,8 +329,6 @@ public:
|
|||
// because it doesn't know when the decoder was paused, buffering, etc.
|
||||
// Do not pass zero.
|
||||
void SetPlaybackRate(uint32_t aBytesPerSecond);
|
||||
// Returns the last set value of SetTransportSeekable.
|
||||
bool IsTransportSeekable();
|
||||
|
||||
// Returns true when all streams for this resource are suspended or their
|
||||
// channel has ended.
|
||||
|
@ -340,9 +337,6 @@ public:
|
|||
// These methods must be called on a different thread from the main
|
||||
// thread. They should always be called on the same thread for a given
|
||||
// stream.
|
||||
// This can fail when aWhence is NS_SEEK_END and no stream length
|
||||
// is known.
|
||||
nsresult Seek(int32_t aWhence, int64_t aOffset);
|
||||
int64_t Tell();
|
||||
// *aBytes gets the number of bytes that were actually read. This can
|
||||
// be less than aCount. If the first byte of data is not in the cache,
|
||||
|
@ -426,6 +420,19 @@ private:
|
|||
int32_t mCount;
|
||||
};
|
||||
|
||||
// Read data from the partial block and return the number of bytes read
|
||||
// successfully. 0 if aOffset is not an offset in the partial block or there
|
||||
// is nothing to read.
|
||||
uint32_t ReadPartialBlock(int64_t aOffset, Span<char> aBuffer);
|
||||
|
||||
// Read data from the cache block specified by aOffset. Return the number of
|
||||
// bytes read successfully or an error code if any failure.
|
||||
Result<uint32_t, nsresult> ReadBlockFromCache(int64_t aOffset,
|
||||
Span<char> aBuffer);
|
||||
|
||||
// Non-main thread only.
|
||||
nsresult Seek(int64_t aOffset);
|
||||
|
||||
// Returns the end of the bytes starting at the given offset
|
||||
// which are in cache.
|
||||
// This method assumes that the cache monitor is held and can be called on
|
||||
|
|
|
@ -560,6 +560,7 @@ ChromiumCDMChild::RecvLoadSession(const uint32_t& aPromiseId,
|
|||
const uint32_t& aSessionType,
|
||||
const nsCString& aSessionId)
|
||||
{
|
||||
MOZ_ASSERT(IsOnMessageLoopThread());
|
||||
GMP_LOG("ChromiumCDMChild::RecvLoadSession(pid=%u, type=%u, sessionId=%s)",
|
||||
aPromiseId,
|
||||
aSessionType,
|
||||
|
@ -737,6 +738,11 @@ ChromiumCDMChild::RecvInitializeVideoDecoder(
|
|||
{
|
||||
MOZ_ASSERT(IsOnMessageLoopThread());
|
||||
MOZ_ASSERT(!mDecoderInitialized);
|
||||
if (!mCDM) {
|
||||
GMP_LOG("ChromiumCDMChild::RecvInitializeVideoDecoder() no CDM");
|
||||
Unused << SendOnDecoderInitDone(cdm::kInitializationError);
|
||||
return IPC_OK();
|
||||
}
|
||||
cdm::VideoDecoderConfig config;
|
||||
config.codec =
|
||||
static_cast<cdm::VideoDecoderConfig::VideoCodec>(aConfig.mCodec());
|
||||
|
@ -761,10 +767,10 @@ ChromiumCDMChild::RecvDeinitializeVideoDecoder()
|
|||
MOZ_ASSERT(IsOnMessageLoopThread());
|
||||
GMP_LOG("ChromiumCDMChild::RecvDeinitializeVideoDecoder()");
|
||||
MOZ_ASSERT(mDecoderInitialized);
|
||||
if (mDecoderInitialized) {
|
||||
mDecoderInitialized = false;
|
||||
if (mDecoderInitialized && mCDM) {
|
||||
mCDM->DeinitializeDecoder(cdm::kStreamTypeVideo);
|
||||
}
|
||||
mDecoderInitialized = false;
|
||||
PurgeShmems();
|
||||
return IPC_OK();
|
||||
}
|
||||
|
@ -774,7 +780,7 @@ ChromiumCDMChild::RecvResetVideoDecoder()
|
|||
{
|
||||
MOZ_ASSERT(IsOnMessageLoopThread());
|
||||
GMP_LOG("ChromiumCDMChild::RecvResetVideoDecoder()");
|
||||
if (mDecoderInitialized) {
|
||||
if (mDecoderInitialized && mCDM) {
|
||||
mCDM->ResetDecoder(cdm::kStreamTypeVideo);
|
||||
}
|
||||
Unused << SendResetVideoDecoderComplete();
|
||||
|
@ -789,6 +795,12 @@ ChromiumCDMChild::RecvDecryptAndDecodeFrame(const CDMInputBuffer& aBuffer)
|
|||
aBuffer.mTimestamp());
|
||||
MOZ_ASSERT(mDecoderInitialized);
|
||||
|
||||
if (!mCDM) {
|
||||
GMP_LOG("ChromiumCDMChild::RecvDecryptAndDecodeFrame() no CDM");
|
||||
Unused << SendDecodeFailed(cdm::kDecodeError);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
RefPtr<ChromiumCDMChild> self = this;
|
||||
auto autoDeallocateShmem = MakeScopeExit([&, self] {
|
||||
self->DeallocShmem(aBuffer.mData());
|
||||
|
@ -882,6 +894,11 @@ mozilla::ipc::IPCResult
|
|||
ChromiumCDMChild::RecvDrain()
|
||||
{
|
||||
MOZ_ASSERT(IsOnMessageLoopThread());
|
||||
if (!mCDM) {
|
||||
GMP_LOG("ChromiumCDMChild::RecvDrain() no CDM");
|
||||
Unused << SendDrainComplete();
|
||||
return IPC_OK();
|
||||
}
|
||||
WidevineVideoFrame frame;
|
||||
cdm::InputBuffer sample;
|
||||
cdm::Status rv = mCDM->DecryptAndDecodeFrame(sample, &frame);
|
||||
|
|
|
@ -149,7 +149,7 @@ function UpdateSessionFunc(test, token, sessionType, resolve, reject) {
|
|||
function MaybeCrossOriginURI(test, uri)
|
||||
{
|
||||
if (test.crossOrigin) {
|
||||
return "http://test2.mochi.test:8888/tests/dom/media/test/allowed.sjs?" + uri;
|
||||
return "https://example.com:443/tests/dom/media/test/allowed.sjs?" + uri;
|
||||
} else {
|
||||
return uri;
|
||||
}
|
||||
|
|
|
@ -753,44 +753,63 @@ skip-if = android_version == '17' # android(bug 1232305)
|
|||
[test_duration_after_error.html]
|
||||
[test_eme_autoplay.html]
|
||||
skip-if = toolkit == 'android' # bug 1149374
|
||||
scheme=https
|
||||
[test_eme_pssh_in_moof.html]
|
||||
skip-if = toolkit == 'android' # bug 1149374
|
||||
scheme=https
|
||||
[test_eme_session_callable_value.html]
|
||||
scheme=https
|
||||
[test_eme_canvas_blocked.html]
|
||||
skip-if = toolkit == 'android' # bug 1149374
|
||||
scheme=https
|
||||
[test_eme_detach_media_keys.html]
|
||||
skip-if = toolkit == 'android' # bug 1149374
|
||||
scheme=https
|
||||
[test_eme_detach_reattach_same_mediakeys_during_playback.html]
|
||||
skip-if = toolkit == 'android' # bug 1149374
|
||||
scheme=https
|
||||
[test_eme_initDataTypes.html]
|
||||
skip-if = toolkit == 'android' # bug 1149374
|
||||
scheme=https
|
||||
[test_eme_missing_pssh.html]
|
||||
skip-if = toolkit == 'android' # bug 1149374
|
||||
scheme=https
|
||||
[test_eme_non_mse_fails.html]
|
||||
skip-if = toolkit == 'android' # bug 1149374
|
||||
scheme=https
|
||||
[test_eme_request_notifications.html]
|
||||
skip-if = toolkit == 'android' # bug 1149374
|
||||
scheme=https
|
||||
[test_eme_playback.html]
|
||||
skip-if = toolkit == 'android' # bug 1149374
|
||||
scheme=https
|
||||
[test_eme_requestKeySystemAccess.html]
|
||||
skip-if = toolkit == 'android' # bug 1149374
|
||||
scheme=https
|
||||
[test_eme_sample_groups_playback.html]
|
||||
skip-if = toolkit == 'android' # bug 1149374
|
||||
scheme=https
|
||||
[test_eme_setMediaKeys_before_attach_MediaSource.html]
|
||||
skip-if = toolkit == 'android' # bug 1149374
|
||||
scheme=https
|
||||
[test_eme_stream_capture_blocked_case1.html]
|
||||
tags=msg capturestream
|
||||
skip-if = toolkit == 'android' # bug 1149374
|
||||
scheme=https
|
||||
[test_eme_stream_capture_blocked_case2.html]
|
||||
tags=msg capturestream
|
||||
skip-if = toolkit == 'android' # bug 1149374
|
||||
scheme=https
|
||||
[test_eme_stream_capture_blocked_case3.html]
|
||||
tags=msg capturestream
|
||||
skip-if = toolkit == 'android' # bug 1149374
|
||||
scheme=https
|
||||
[test_eme_unsetMediaKeys_then_capture.html]
|
||||
skip-if = toolkit == 'android' # bug 1149374
|
||||
scheme=https
|
||||
[test_eme_waitingforkey.html]
|
||||
skip-if = toolkit == 'android' # bug 1149374
|
||||
scheme=https
|
||||
[test_empty_resource.html]
|
||||
[test_error_in_video_document.html]
|
||||
[test_error_on_404.html]
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<script type="text/javascript" src="manifest.js"></script>
|
||||
<script type="text/javascript" src="http://test1.mochi.test:8888/tests/dom/media/test/eme.js"></script>
|
||||
<script type="text/javascript" src="https://example.com:443/tests/dom/media/test/eme.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<script type="text/javascript" src="manifest.js"></script>
|
||||
<script type="text/javascript" src="http://test1.mochi.test:8888/tests/dom/media/test/eme.js"></script>
|
||||
<script type="text/javascript" src="https://example.com:443/tests/dom/media/test/eme.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<script type="text/javascript" src="manifest.js"></script>
|
||||
<script type="text/javascript" src="http://test1.mochi.test:8888/tests/dom/media/test/eme.js"></script>
|
||||
<script type="text/javascript" src="https://example.com:443/tests/dom/media/test/eme.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<script type="text/javascript" src="manifest.js"></script>
|
||||
<script type="text/javascript" src="http://test1.mochi.test:8888/tests/dom/media/test/eme.js"></script>
|
||||
<script type="text/javascript" src="https://example.com:443/tests/dom/media/test/eme.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<script type="text/javascript" src="manifest.js"></script>
|
||||
<script type="text/javascript" src="http://test1.mochi.test:8888/tests/dom/media/test/eme.js"></script>
|
||||
<script type="text/javascript" src="https://example.com:443/tests/dom/media/test/eme.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
|
|
|
@ -188,8 +188,8 @@ function testInitExpr(type, initialValue, nextValue, coercion, assertFunc = asse
|
|||
var module = wasmEvalText(`(module
|
||||
(import "globals" "a" (global ${type}))
|
||||
|
||||
(global (mut ${type}) (get_global 0))
|
||||
(global ${type} (get_global 0))
|
||||
(global $glob_mut (mut ${type}) (get_global 0))
|
||||
(global $glob_imm ${type} (get_global 0))
|
||||
|
||||
(func $get0 (result ${type}) (get_global 0))
|
||||
|
||||
|
@ -203,6 +203,7 @@ function testInitExpr(type, initialValue, nextValue, coercion, assertFunc = asse
|
|||
(export "get_cst" $get_cst)
|
||||
|
||||
(export "set1" $set1)
|
||||
(export "global_imm" (global $glob_imm))
|
||||
)`, {
|
||||
globals: {
|
||||
a: coercion(initialValue)
|
||||
|
@ -211,10 +212,12 @@ function testInitExpr(type, initialValue, nextValue, coercion, assertFunc = asse
|
|||
|
||||
assertFunc(module.get0(), coercion(initialValue));
|
||||
assertFunc(module.get1(), coercion(initialValue));
|
||||
assertFunc(module.global_imm, coercion(initialValue));
|
||||
|
||||
assertEq(module.set1(coercion(nextValue)), undefined);
|
||||
assertFunc(module.get1(), coercion(nextValue));
|
||||
assertFunc(module.get0(), coercion(initialValue));
|
||||
assertFunc(module.global_imm, coercion(initialValue));
|
||||
|
||||
assertFunc(module.get_cst(), coercion(initialValue));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
assertErrorMessage(() => {
|
||||
var desc = {
|
||||
element: "anyfunc",
|
||||
};
|
||||
var proxy = new Proxy({}, {
|
||||
has: true
|
||||
});
|
||||
Object.setPrototypeOf(desc, proxy);
|
||||
let table = new WebAssembly.Table(desc);
|
||||
}, TypeError, /proxy handler's has trap/);
|
|
@ -788,6 +788,8 @@ class AstModule : public AstNode
|
|||
AstElemSegmentVector elemSegments_;
|
||||
AstGlobalVector globals_;
|
||||
|
||||
size_t numGlobalImports_;
|
||||
|
||||
public:
|
||||
explicit AstModule(LifoAlloc& lifo)
|
||||
: lifo_(lifo),
|
||||
|
@ -801,7 +803,8 @@ class AstModule : public AstNode
|
|||
funcs_(lifo),
|
||||
dataSegments_(lifo),
|
||||
elemSegments_(lifo),
|
||||
globals_(lifo)
|
||||
globals_(lifo),
|
||||
numGlobalImports_(0)
|
||||
{}
|
||||
bool init() {
|
||||
return sigMap_.init();
|
||||
|
@ -891,9 +894,9 @@ class AstModule : public AstNode
|
|||
return false;
|
||||
break;
|
||||
case DefinitionKind::Global:
|
||||
numGlobalImports_++;
|
||||
break;
|
||||
}
|
||||
|
||||
return imports_.append(imp);
|
||||
}
|
||||
const ImportVector& imports() const {
|
||||
|
@ -917,6 +920,9 @@ class AstModule : public AstNode
|
|||
const AstGlobalVector& globals() const {
|
||||
return globals_;
|
||||
}
|
||||
size_t numGlobalImports() const {
|
||||
return numGlobalImports_;
|
||||
}
|
||||
};
|
||||
|
||||
class AstUnaryOperator final : public AstExpr
|
||||
|
|
|
@ -454,7 +454,10 @@ GetLimits(JSContext* cx, HandleObject obj, uint32_t maxInitial, uint32_t maxMaxi
|
|||
RootedId maximumId(cx, AtomToId(maximumAtom));
|
||||
|
||||
bool found;
|
||||
if (HasProperty(cx, obj, maximumId, &found) && found) {
|
||||
if (!HasProperty(cx, obj, maximumId, &found))
|
||||
return false;
|
||||
|
||||
if (found) {
|
||||
RootedValue maxVal(cx);
|
||||
if (!GetProperty(cx, obj, obj, maximumId, &maxVal))
|
||||
return false;
|
||||
|
|
|
@ -988,9 +988,29 @@ GetGlobalExport(JSContext* cx, const GlobalDescVector& globals, uint32_t globalI
|
|||
// Imports are located upfront in the globals array.
|
||||
Val val;
|
||||
switch (global.kind()) {
|
||||
case GlobalKind::Import: val = globalImports[globalIndex]; break;
|
||||
case GlobalKind::Variable: MOZ_CRASH("mutable variables can't be exported");
|
||||
case GlobalKind::Constant: val = global.constantValue(); break;
|
||||
case GlobalKind::Import: {
|
||||
val = globalImports[globalIndex];
|
||||
break;
|
||||
}
|
||||
case GlobalKind::Variable: {
|
||||
MOZ_ASSERT(!global.isMutable(), "mutable variables can't be exported");
|
||||
const InitExpr& init = global.initExpr();
|
||||
switch (init.kind()) {
|
||||
case InitExpr::Kind::Constant: {
|
||||
val = init.val();
|
||||
break;
|
||||
}
|
||||
case InitExpr::Kind::GetGlobal: {
|
||||
val = globalImports[init.globalIndex()];
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GlobalKind::Constant: {
|
||||
val = global.constantValue();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (global.type()) {
|
||||
|
|
|
@ -2660,7 +2660,7 @@ ParseFunc(WasmParseContext& c, AstModule* module)
|
|||
|
||||
if (c.ts.getIf(WasmToken::Export)) {
|
||||
AstRef ref = funcName.empty()
|
||||
? AstRef(module->funcImportNames().length() + module->funcs().length())
|
||||
? AstRef(module->numFuncImports() + module->funcs().length())
|
||||
: AstRef(funcName);
|
||||
if (!ParseInlineExport(c, DefinitionKind::Function, module, ref))
|
||||
return false;
|
||||
|
@ -3227,7 +3227,8 @@ ParseGlobal(WasmParseContext& c, AstModule* module)
|
|||
}
|
||||
|
||||
if (c.ts.getIf(WasmToken::Export)) {
|
||||
AstRef ref = name.empty() ? AstRef(module->globals().length()) : AstRef(name);
|
||||
size_t refIndex = module->numGlobalImports() + module->globals().length();
|
||||
AstRef ref = name.empty() ? AstRef(refIndex) : AstRef(name);
|
||||
if (!ParseInlineExport(c, DefinitionKind::Global, module, ref))
|
||||
return false;
|
||||
if (!c.ts.match(WasmToken::CloseParen, c.error))
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent()
|
||||
#include "mozilla/dom/HTMLAreaElement.h"
|
||||
#include "mozilla/gfx/PathHelpers.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "nsString.h"
|
||||
|
@ -30,10 +31,11 @@
|
|||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::gfx;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
class Area {
|
||||
public:
|
||||
explicit Area(nsIContent* aArea);
|
||||
explicit Area(HTMLAreaElement* aArea);
|
||||
virtual ~Area();
|
||||
|
||||
virtual void ParseCoords(const nsAString& aSpec);
|
||||
|
@ -46,17 +48,17 @@ public:
|
|||
|
||||
void HasFocus(bool aHasFocus);
|
||||
|
||||
nsCOMPtr<nsIContent> mArea;
|
||||
RefPtr<HTMLAreaElement> mArea;
|
||||
UniquePtr<nscoord[]> mCoords;
|
||||
int32_t mNumCoords;
|
||||
bool mHasFocus;
|
||||
};
|
||||
|
||||
Area::Area(nsIContent* aArea)
|
||||
Area::Area(HTMLAreaElement* aArea)
|
||||
: mArea(aArea)
|
||||
{
|
||||
MOZ_COUNT_CTOR(Area);
|
||||
NS_PRECONDITION(mArea, "How did that happen?");
|
||||
MOZ_ASSERT(mArea, "How did that happen?");
|
||||
mNumCoords = 0;
|
||||
mHasFocus = false;
|
||||
}
|
||||
|
@ -267,7 +269,7 @@ void Area::HasFocus(bool aHasFocus)
|
|||
|
||||
class DefaultArea : public Area {
|
||||
public:
|
||||
explicit DefaultArea(nsIContent* aArea);
|
||||
explicit DefaultArea(HTMLAreaElement* aArea);
|
||||
|
||||
virtual bool IsInside(nscoord x, nscoord y) const override;
|
||||
virtual void Draw(nsIFrame* aFrame, DrawTarget& aDrawTarget,
|
||||
|
@ -276,7 +278,7 @@ public:
|
|||
virtual void GetRect(nsIFrame* aFrame, nsRect& aRect) override;
|
||||
};
|
||||
|
||||
DefaultArea::DefaultArea(nsIContent* aArea)
|
||||
DefaultArea::DefaultArea(HTMLAreaElement* aArea)
|
||||
: Area(aArea)
|
||||
{
|
||||
}
|
||||
|
@ -311,7 +313,7 @@ void DefaultArea::GetRect(nsIFrame* aFrame, nsRect& aRect)
|
|||
|
||||
class RectArea : public Area {
|
||||
public:
|
||||
explicit RectArea(nsIContent* aArea);
|
||||
explicit RectArea(HTMLAreaElement* aArea);
|
||||
|
||||
virtual void ParseCoords(const nsAString& aSpec) override;
|
||||
virtual bool IsInside(nscoord x, nscoord y) const override;
|
||||
|
@ -321,7 +323,7 @@ public:
|
|||
virtual void GetRect(nsIFrame* aFrame, nsRect& aRect) override;
|
||||
};
|
||||
|
||||
RectArea::RectArea(nsIContent* aArea)
|
||||
RectArea::RectArea(HTMLAreaElement* aArea)
|
||||
: Area(aArea)
|
||||
{
|
||||
}
|
||||
|
@ -417,7 +419,7 @@ void RectArea::GetRect(nsIFrame* aFrame, nsRect& aRect)
|
|||
|
||||
class PolyArea : public Area {
|
||||
public:
|
||||
explicit PolyArea(nsIContent* aArea);
|
||||
explicit PolyArea(HTMLAreaElement* aArea);
|
||||
|
||||
virtual void ParseCoords(const nsAString& aSpec) override;
|
||||
virtual bool IsInside(nscoord x, nscoord y) const override;
|
||||
|
@ -427,7 +429,7 @@ public:
|
|||
virtual void GetRect(nsIFrame* aFrame, nsRect& aRect) override;
|
||||
};
|
||||
|
||||
PolyArea::PolyArea(nsIContent* aArea)
|
||||
PolyArea::PolyArea(HTMLAreaElement* aArea)
|
||||
: Area(aArea)
|
||||
{
|
||||
}
|
||||
|
@ -574,7 +576,7 @@ void PolyArea::GetRect(nsIFrame* aFrame, nsRect& aRect)
|
|||
|
||||
class CircleArea : public Area {
|
||||
public:
|
||||
explicit CircleArea(nsIContent* aArea);
|
||||
explicit CircleArea(HTMLAreaElement* aArea);
|
||||
|
||||
virtual void ParseCoords(const nsAString& aSpec) override;
|
||||
virtual bool IsInside(nscoord x, nscoord y) const override;
|
||||
|
@ -584,7 +586,7 @@ public:
|
|||
virtual void GetRect(nsIFrame* aFrame, nsRect& aRect) override;
|
||||
};
|
||||
|
||||
CircleArea::CircleArea(nsIContent* aArea)
|
||||
CircleArea::CircleArea(HTMLAreaElement* aArea)
|
||||
: Area(aArea)
|
||||
{
|
||||
}
|
||||
|
@ -677,9 +679,9 @@ void CircleArea::GetRect(nsIFrame* aFrame, nsRect& aRect)
|
|||
//----------------------------------------------------------------------
|
||||
|
||||
|
||||
nsImageMap::nsImageMap() :
|
||||
mImageFrame(nullptr),
|
||||
mContainsBlockContents(false)
|
||||
nsImageMap::nsImageMap()
|
||||
: mImageFrame(nullptr)
|
||||
, mConsiderWholeSubtree(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -747,38 +749,25 @@ nsImageMap::Init(nsImageFrame* aImageFrame, nsIContent* aMap)
|
|||
}
|
||||
|
||||
void
|
||||
nsImageMap::SearchForAreas(nsIContent* aParent, bool& aFoundArea,
|
||||
bool& aFoundAnchor)
|
||||
nsImageMap::SearchForAreas(nsIContent* aParent)
|
||||
{
|
||||
uint32_t i, n = aParent->GetChildCount();
|
||||
// Look for <area> elements.
|
||||
for (nsIContent* child = aParent->GetFirstChild();
|
||||
child;
|
||||
child = child->GetNextSibling()) {
|
||||
if (auto* area = HTMLAreaElement::FromContent(child)) {
|
||||
AddArea(area);
|
||||
|
||||
// Look for <area> or <a> elements. We'll use whichever type we find first.
|
||||
for (i = 0; i < n; i++) {
|
||||
nsIContent *child = aParent->GetChildAt(i);
|
||||
|
||||
// If we haven't determined that the map element contains an
|
||||
// <a> element yet, then look for <area>.
|
||||
if (!aFoundAnchor && child->IsHTMLElement(nsGkAtoms::area)) {
|
||||
aFoundArea = true;
|
||||
AddArea(child);
|
||||
|
||||
// Continue to next child. This stops mContainsBlockContents from
|
||||
// Continue to next child. This stops mConsiderWholeSubtree from
|
||||
// getting set. It also makes us ignore children of <area>s which
|
||||
// is consistent with how we react to dynamic insertion of such
|
||||
// children.
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we haven't determined that the map element contains an
|
||||
// <area> element yet, then look for <a>.
|
||||
if (!aFoundArea && child->IsHTMLElement(nsGkAtoms::a)) {
|
||||
aFoundAnchor = true;
|
||||
AddArea(child);
|
||||
}
|
||||
|
||||
if (child->IsElement()) {
|
||||
mContainsBlockContents = true;
|
||||
SearchForAreas(child, aFoundArea, aFoundAnchor);
|
||||
mConsiderWholeSubtree = true;
|
||||
SearchForAreas(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -789,11 +778,9 @@ nsImageMap::UpdateAreas()
|
|||
// Get rid of old area data
|
||||
FreeAreas();
|
||||
|
||||
bool foundArea = false;
|
||||
bool foundAnchor = false;
|
||||
mContainsBlockContents = false;
|
||||
mConsiderWholeSubtree = false;
|
||||
SearchForAreas(mMap);
|
||||
|
||||
SearchForAreas(mMap, foundArea, foundAnchor);
|
||||
#ifdef ACCESSIBILITY
|
||||
if (nsAccessibilityService* accService = GetAccService()) {
|
||||
accService->UpdateImageMap(mImageFrame);
|
||||
|
@ -802,7 +789,7 @@ nsImageMap::UpdateAreas()
|
|||
}
|
||||
|
||||
void
|
||||
nsImageMap::AddArea(nsIContent* aArea)
|
||||
nsImageMap::AddArea(HTMLAreaElement* aArea)
|
||||
{
|
||||
static nsIContent::AttrValuesArray strings[] =
|
||||
{&nsGkAtoms::rect, &nsGkAtoms::rectangle,
|
||||
|
@ -888,7 +875,7 @@ nsImageMap::Draw(nsIFrame* aFrame, DrawTarget& aDrawTarget,
|
|||
void
|
||||
nsImageMap::MaybeUpdateAreas(nsIContent *aContent)
|
||||
{
|
||||
if (aContent == mMap || mContainsBlockContents) {
|
||||
if (aContent == mMap || mConsiderWholeSubtree) {
|
||||
UpdateAreas();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,12 @@ class nsIFrame;
|
|||
class nsIContent;
|
||||
struct nsRect;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class HTMLAreaElement;
|
||||
}
|
||||
}
|
||||
|
||||
class nsImageMap final : public nsStubMutationObserver,
|
||||
public nsIDOMEventListener
|
||||
{
|
||||
|
@ -82,18 +88,21 @@ protected:
|
|||
void FreeAreas();
|
||||
|
||||
void UpdateAreas();
|
||||
void SearchForAreas(nsIContent* aParent,
|
||||
bool& aFoundArea,
|
||||
bool& aFoundAnchor);
|
||||
|
||||
void AddArea(nsIContent* aArea);
|
||||
void SearchForAreas(nsIContent* aParent);
|
||||
|
||||
void AddArea(mozilla::dom::HTMLAreaElement* aArea);
|
||||
|
||||
void MaybeUpdateAreas(nsIContent *aContent);
|
||||
|
||||
nsImageFrame* mImageFrame; // the frame that owns us
|
||||
nsCOMPtr<nsIContent> mMap;
|
||||
AutoTArray<Area*, 8> mAreas; // almost always has some entries
|
||||
bool mContainsBlockContents;
|
||||
|
||||
// This is set when we search for all area children and tells us whether we
|
||||
// should consider the whole subtree or just direct children when we get
|
||||
// content notifications about changes inside the map subtree.
|
||||
bool mConsiderWholeSubtree;
|
||||
};
|
||||
|
||||
#endif /* nsImageMap_h */
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#ifndef Utils_h
|
||||
#define Utils_h
|
||||
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/TemplateLib.h"
|
||||
|
||||
// Helper for log2 of powers of 2 at compile time.
|
||||
|
@ -41,4 +42,86 @@ constexpr unsigned long long int operator"" _MiB(unsigned long long int aNum)
|
|||
return aNum * 1024_KiB;
|
||||
}
|
||||
|
||||
constexpr long double operator""_percent(long double aPercent)
|
||||
{
|
||||
return aPercent / 100;
|
||||
}
|
||||
|
||||
// Helper for (fast) comparison of fractions without involving divisions or
|
||||
// floats.
|
||||
class Fraction
|
||||
{
|
||||
public:
|
||||
explicit constexpr Fraction(size_t aNumerator, size_t aDenominator)
|
||||
: mNumerator(aNumerator)
|
||||
, mDenominator(aDenominator)
|
||||
{
|
||||
}
|
||||
|
||||
MOZ_IMPLICIT constexpr Fraction(long double aValue)
|
||||
// We use an arbitrary power of two as denominator that provides enough
|
||||
// precision for our use case.
|
||||
: mNumerator(aValue * 4096)
|
||||
, mDenominator(4096)
|
||||
{
|
||||
}
|
||||
|
||||
inline bool operator<(const Fraction& aOther) const
|
||||
{
|
||||
#ifndef MOZ_DEBUG
|
||||
// We are comparing A / B < C / D, with all A, B, C and D being positive
|
||||
// numbers. Multiplying both sides with B * D, we have:
|
||||
// (A * B * D) / B < (C * B * D) / D, which can then be simplified as
|
||||
// A * D < C * B. When can thus compare our fractions without actually
|
||||
// doing any division.
|
||||
// This however assumes the multiplied quantities are small enough not
|
||||
// to overflow the multiplication. We use CheckedInt on debug builds
|
||||
// to enforce the assumption.
|
||||
return mNumerator * aOther.mDenominator < aOther.mNumerator * mDenominator;
|
||||
#else
|
||||
mozilla::CheckedInt<size_t> numerator(mNumerator);
|
||||
mozilla::CheckedInt<size_t> denominator(mDenominator);
|
||||
// value() asserts when the multiplication overflowed.
|
||||
size_t lhs = (numerator * aOther.mDenominator).value();
|
||||
size_t rhs = (aOther.mNumerator * denominator).value();
|
||||
return lhs < rhs;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool operator>(const Fraction& aOther) const { return aOther < *this; }
|
||||
|
||||
inline bool operator>=(const Fraction& aOther) const
|
||||
{
|
||||
return !(*this < aOther);
|
||||
}
|
||||
|
||||
inline bool operator<=(const Fraction& aOther) const
|
||||
{
|
||||
return !(*this > aOther);
|
||||
}
|
||||
|
||||
inline bool operator==(const Fraction& aOther) const
|
||||
{
|
||||
#ifndef MOZ_DEBUG
|
||||
// Same logic as operator<
|
||||
return mNumerator * aOther.mDenominator == aOther.mNumerator * mDenominator;
|
||||
#else
|
||||
mozilla::CheckedInt<size_t> numerator(mNumerator);
|
||||
mozilla::CheckedInt<size_t> denominator(mDenominator);
|
||||
size_t lhs = (numerator * aOther.mDenominator).value();
|
||||
size_t rhs = (aOther.mNumerator * denominator).value();
|
||||
return lhs == rhs;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool operator!=(const Fraction& aOther) const
|
||||
{
|
||||
return !(*this == aOther);
|
||||
}
|
||||
|
||||
private:
|
||||
size_t mNumerator;
|
||||
size_t mDenominator;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -514,25 +514,6 @@ static Atomic<size_t, ReleaseAcquire> gRecycledSize;
|
|||
|
||||
static size_t opt_dirty_max = DIRTY_MAX_DEFAULT;
|
||||
|
||||
// RUN_MAX_OVRHD indicates maximum desired run header overhead. Runs are sized
|
||||
// as small as possible such that this setting is still honored, without
|
||||
// violating other constraints. The goal is to make runs as small as possible
|
||||
// without exceeding a per run external fragmentation threshold.
|
||||
//
|
||||
// We use binary fixed point math for overhead computations, where the binary
|
||||
// point is implicitly RUN_BFP bits to the left.
|
||||
//
|
||||
// Note that it is possible to set RUN_MAX_OVRHD low enough that it cannot be
|
||||
// honored for some/all object sizes, since there is one bit of header overhead
|
||||
// per object (plus a constant). This constraint is relaxed (ignored) for runs
|
||||
// that are so small that the per-region overhead is greater than:
|
||||
//
|
||||
// (RUN_MAX_OVRHD / (reg_size << (3+RUN_BFP))
|
||||
#define RUN_BFP 12
|
||||
// \/ Implicit binary fixed point.
|
||||
#define RUN_MAX_OVRHD 0x0000003dU
|
||||
#define RUN_MAX_OVRHD_RELAX 0x00001800U
|
||||
|
||||
// Return the smallest chunk multiple that is >= s.
|
||||
#define CHUNK_CEILING(s) (((s) + kChunkSizeMask) & ~kChunkSizeMask)
|
||||
|
||||
|
@ -856,21 +837,35 @@ struct GetDoublyLinkedListElement<arena_chunk_t>
|
|||
struct arena_run_t
|
||||
{
|
||||
#if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
|
||||
uint32_t magic;
|
||||
uint32_t mMagic;
|
||||
#define ARENA_RUN_MAGIC 0x384adf93
|
||||
|
||||
// On 64-bit platforms, having the arena_bin_t pointer following
|
||||
// the mMagic field means there's padding between both fields, making
|
||||
// the run header larger than necessary.
|
||||
// But when MOZ_DIAGNOSTIC_ASSERT_ENABLED is not set, starting the
|
||||
// header with this field followed by the arena_bin_t pointer yields
|
||||
// the same padding. We do want the mMagic field to appear first, so
|
||||
// depending whether MOZ_DIAGNOSTIC_ASSERT_ENABLED is set or not, we
|
||||
// move some field to avoid padding.
|
||||
|
||||
// Number of free regions in run.
|
||||
unsigned mNumFree;
|
||||
#endif
|
||||
|
||||
// Bin this run is associated with.
|
||||
arena_bin_t* bin;
|
||||
arena_bin_t* mBin;
|
||||
|
||||
// Index of first element that might have a free region.
|
||||
unsigned regs_minelm;
|
||||
unsigned mRegionsMinElement;
|
||||
|
||||
#if !defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
|
||||
// Number of free regions in run.
|
||||
unsigned nfree;
|
||||
unsigned mNumFree;
|
||||
#endif
|
||||
|
||||
// Bitmask of in-use regions (0: in use, 1: free).
|
||||
unsigned regs_mask[1]; // Dynamically sized.
|
||||
unsigned mRegionsMask[1]; // Dynamically sized.
|
||||
};
|
||||
|
||||
struct arena_bin_t
|
||||
|
@ -895,7 +890,7 @@ struct arena_bin_t
|
|||
// Total number of regions in a run for this bin's size class.
|
||||
uint32_t mRunNumRegions;
|
||||
|
||||
// Number of elements in a run's regs_mask for this bin's size class.
|
||||
// Number of elements in a run's mRegionsMask for this bin's size class.
|
||||
uint32_t mRunNumRegionsMask;
|
||||
|
||||
// Offset of first region in a run for this bin's size class.
|
||||
|
@ -903,6 +898,25 @@ struct arena_bin_t
|
|||
|
||||
// Current number of runs in this bin, full or otherwise.
|
||||
unsigned long mNumRuns;
|
||||
|
||||
// Amount of overhead runs are allowed to have.
|
||||
static constexpr long double kRunOverhead = 1.6_percent;
|
||||
static constexpr long double kRunRelaxedOverhead = 2.4_percent;
|
||||
|
||||
// Initialize a bin for the given size class.
|
||||
// The generated run sizes, for a page size of 4 KiB, are:
|
||||
// size|run size|run size|run size|run
|
||||
// class|size class|size class|size class|size
|
||||
// 4 4 KiB 8 4 KiB 16 4 KiB 32 4 KiB
|
||||
// 48 4 KiB 64 4 KiB 80 4 KiB 96 4 KiB
|
||||
// 112 4 KiB 128 8 KiB 144 4 KiB 160 8 KiB
|
||||
// 176 4 KiB 192 4 KiB 208 8 KiB 224 4 KiB
|
||||
// 240 4 KiB 256 16 KiB 272 4 KiB 288 4 KiB
|
||||
// 304 12 KiB 320 12 KiB 336 4 KiB 352 8 KiB
|
||||
// 368 4 KiB 384 8 KiB 400 20 KiB 416 16 KiB
|
||||
// 432 12 KiB 448 4 KiB 464 16 KiB 480 8 KiB
|
||||
// 496 20 KiB 512 32 KiB 1024 64 KiB 2048 128 KiB
|
||||
inline void Init(SizeClass aSizeClass);
|
||||
};
|
||||
|
||||
struct arena_t
|
||||
|
@ -987,10 +1001,7 @@ private:
|
|||
|
||||
void DeallocChunk(arena_chunk_t* aChunk);
|
||||
|
||||
arena_run_t* AllocRun(arena_bin_t* aBin,
|
||||
size_t aSize,
|
||||
bool aLarge,
|
||||
bool aZero);
|
||||
arena_run_t* AllocRun(size_t aSize, bool aLarge, bool aZero);
|
||||
|
||||
void DallocRun(arena_run_t* aRun, bool aDirty);
|
||||
|
||||
|
@ -1010,10 +1021,6 @@ private:
|
|||
size_t aNewSize,
|
||||
bool dirty);
|
||||
|
||||
inline void* MallocBinEasy(arena_bin_t* aBin, arena_run_t* aRun);
|
||||
|
||||
void* MallocBinHard(arena_bin_t* aBin);
|
||||
|
||||
arena_run_t* GetNonFullBinRun(arena_bin_t* aBin);
|
||||
|
||||
inline void* MallocSmall(size_t aSize, bool aZero);
|
||||
|
@ -2264,14 +2271,14 @@ arena_run_reg_alloc(arena_run_t* run, arena_bin_t* bin)
|
|||
void* ret;
|
||||
unsigned i, mask, bit, regind;
|
||||
|
||||
MOZ_DIAGNOSTIC_ASSERT(run->magic == ARENA_RUN_MAGIC);
|
||||
MOZ_ASSERT(run->regs_minelm < bin->mRunNumRegionsMask);
|
||||
MOZ_DIAGNOSTIC_ASSERT(run->mMagic == ARENA_RUN_MAGIC);
|
||||
MOZ_ASSERT(run->mRegionsMinElement < bin->mRunNumRegionsMask);
|
||||
|
||||
// Move the first check outside the loop, so that run->regs_minelm can
|
||||
// Move the first check outside the loop, so that run->mRegionsMinElement can
|
||||
// be updated unconditionally, without the possibility of updating it
|
||||
// multiple times.
|
||||
i = run->regs_minelm;
|
||||
mask = run->regs_mask[i];
|
||||
i = run->mRegionsMinElement;
|
||||
mask = run->mRegionsMask[i];
|
||||
if (mask != 0) {
|
||||
// Usable allocation found.
|
||||
bit = CountTrailingZeroes32(mask);
|
||||
|
@ -2283,13 +2290,13 @@ arena_run_reg_alloc(arena_run_t* run, arena_bin_t* bin)
|
|||
|
||||
// Clear bit.
|
||||
mask ^= (1U << bit);
|
||||
run->regs_mask[i] = mask;
|
||||
run->mRegionsMask[i] = mask;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i++; i < bin->mRunNumRegionsMask; i++) {
|
||||
mask = run->regs_mask[i];
|
||||
mask = run->mRegionsMask[i];
|
||||
if (mask != 0) {
|
||||
// Usable allocation found.
|
||||
bit = CountTrailingZeroes32(mask);
|
||||
|
@ -2301,11 +2308,11 @@ arena_run_reg_alloc(arena_run_t* run, arena_bin_t* bin)
|
|||
|
||||
// Clear bit.
|
||||
mask ^= (1U << bit);
|
||||
run->regs_mask[i] = mask;
|
||||
run->mRegionsMask[i] = mask;
|
||||
|
||||
// Make a note that nothing before this element
|
||||
// contains a free region.
|
||||
run->regs_minelm = i; // Low payoff: + (mask == 0);
|
||||
run->mRegionsMinElement = i; // Low payoff: + (mask == 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -2343,7 +2350,7 @@ arena_run_reg_dalloc(arena_run_t* run, arena_bin_t* bin, void* ptr, size_t size)
|
|||
// clang-format on
|
||||
unsigned diff, regind, elm, bit;
|
||||
|
||||
MOZ_DIAGNOSTIC_ASSERT(run->magic == ARENA_RUN_MAGIC);
|
||||
MOZ_DIAGNOSTIC_ASSERT(run->mMagic == ARENA_RUN_MAGIC);
|
||||
static_assert(((sizeof(size_invs)) / sizeof(unsigned)) + 3 >=
|
||||
kNumQuantumClasses,
|
||||
"size_invs doesn't have enough values");
|
||||
|
@ -2352,33 +2359,8 @@ arena_run_reg_dalloc(arena_run_t* run, arena_bin_t* bin, void* ptr, size_t size)
|
|||
// actual division here can reduce allocator throughput by over 20%!
|
||||
diff =
|
||||
(unsigned)((uintptr_t)ptr - (uintptr_t)run - bin->mRunFirstRegionOffset);
|
||||
if ((size & (size - 1)) == 0) {
|
||||
// log2_table allows fast division of a power of two in the
|
||||
// [1..128] range.
|
||||
//
|
||||
// (x / divisor) becomes (x >> log2_table[divisor - 1]).
|
||||
// clang-format off
|
||||
static const unsigned char log2_table[] = {
|
||||
0, 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
if (size <= 128) {
|
||||
regind = (diff >> log2_table[size - 1]);
|
||||
} else if (size <= 32768) {
|
||||
regind = diff >> (8 + log2_table[(size >> 8) - 1]);
|
||||
} else {
|
||||
// The run size is too large for us to use the lookup
|
||||
// table. Use real division.
|
||||
regind = diff / size;
|
||||
}
|
||||
if (mozilla::IsPowerOfTwo(size)) {
|
||||
regind = diff >> FloorLog2(size);
|
||||
} else if (size <= ((sizeof(size_invs) / sizeof(unsigned)) * kQuantum) + 2) {
|
||||
regind = size_invs[(size / kQuantum) - 3] * diff;
|
||||
regind >>= SIZE_INV_SHIFT;
|
||||
|
@ -2393,12 +2375,12 @@ arena_run_reg_dalloc(arena_run_t* run, arena_bin_t* bin, void* ptr, size_t size)
|
|||
MOZ_DIAGNOSTIC_ASSERT(regind < bin->mRunNumRegions);
|
||||
|
||||
elm = regind >> (LOG2(sizeof(int)) + 3);
|
||||
if (elm < run->regs_minelm) {
|
||||
run->regs_minelm = elm;
|
||||
if (elm < run->mRegionsMinElement) {
|
||||
run->mRegionsMinElement = elm;
|
||||
}
|
||||
bit = regind - (elm << (LOG2(sizeof(int)) + 3));
|
||||
MOZ_DIAGNOSTIC_ASSERT((run->regs_mask[elm] & (1U << bit)) == 0);
|
||||
run->regs_mask[elm] |= (1U << bit);
|
||||
MOZ_DIAGNOSTIC_ASSERT((run->mRegionsMask[elm] & (1U << bit)) == 0);
|
||||
run->mRegionsMask[elm] |= (1U << bit);
|
||||
#undef SIZE_INV
|
||||
#undef SIZE_INV_SHIFT
|
||||
}
|
||||
|
@ -2595,7 +2577,7 @@ arena_t::DeallocChunk(arena_chunk_t* aChunk)
|
|||
}
|
||||
|
||||
arena_run_t*
|
||||
arena_t::AllocRun(arena_bin_t* aBin, size_t aSize, bool aLarge, bool aZero)
|
||||
arena_t::AllocRun(size_t aSize, bool aLarge, bool aZero)
|
||||
{
|
||||
arena_run_t* run;
|
||||
arena_chunk_map_t* mapelm;
|
||||
|
@ -2739,7 +2721,7 @@ arena_t::DallocRun(arena_run_t* aRun, bool aDirty)
|
|||
if ((chunk->map[run_ind].bits & CHUNK_MAP_LARGE) != 0) {
|
||||
size = chunk->map[run_ind].bits & ~gPageSizeMask;
|
||||
} else {
|
||||
size = aRun->bin->mRunSize;
|
||||
size = aRun->mBin->mRunSize;
|
||||
}
|
||||
run_pages = (size >> gPageSize2Pow);
|
||||
|
||||
|
@ -2886,7 +2868,7 @@ arena_t::GetNonFullBinRun(arena_bin_t* aBin)
|
|||
// No existing runs have any space available.
|
||||
|
||||
// Allocate a new run.
|
||||
run = AllocRun(aBin, aBin->mRunSize, false, false);
|
||||
run = AllocRun(aBin->mRunSize, false, false);
|
||||
if (!run) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -2897,135 +2879,110 @@ arena_t::GetNonFullBinRun(arena_bin_t* aBin)
|
|||
}
|
||||
|
||||
// Initialize run internals.
|
||||
run->bin = aBin;
|
||||
run->mBin = aBin;
|
||||
|
||||
for (i = 0; i < aBin->mRunNumRegionsMask - 1; i++) {
|
||||
run->regs_mask[i] = UINT_MAX;
|
||||
run->mRegionsMask[i] = UINT_MAX;
|
||||
}
|
||||
remainder = aBin->mRunNumRegions & ((1U << (LOG2(sizeof(int)) + 3)) - 1);
|
||||
if (remainder == 0) {
|
||||
run->regs_mask[i] = UINT_MAX;
|
||||
run->mRegionsMask[i] = UINT_MAX;
|
||||
} else {
|
||||
// The last element has spare bits that need to be unset.
|
||||
run->regs_mask[i] =
|
||||
run->mRegionsMask[i] =
|
||||
(UINT_MAX >> ((1U << (LOG2(sizeof(int)) + 3)) - remainder));
|
||||
}
|
||||
|
||||
run->regs_minelm = 0;
|
||||
run->mRegionsMinElement = 0;
|
||||
|
||||
run->nfree = aBin->mRunNumRegions;
|
||||
run->mNumFree = aBin->mRunNumRegions;
|
||||
#if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
|
||||
run->magic = ARENA_RUN_MAGIC;
|
||||
run->mMagic = ARENA_RUN_MAGIC;
|
||||
#endif
|
||||
|
||||
aBin->mNumRuns++;
|
||||
return run;
|
||||
}
|
||||
|
||||
// bin->mCurrentRun must have space available before this function is called.
|
||||
void*
|
||||
arena_t::MallocBinEasy(arena_bin_t* aBin, arena_run_t* aRun)
|
||||
void
|
||||
arena_bin_t::Init(SizeClass aSizeClass)
|
||||
{
|
||||
void* ret;
|
||||
|
||||
MOZ_DIAGNOSTIC_ASSERT(aRun->magic == ARENA_RUN_MAGIC);
|
||||
MOZ_DIAGNOSTIC_ASSERT(aRun->nfree > 0);
|
||||
|
||||
ret = arena_run_reg_alloc(aRun, aBin);
|
||||
MOZ_DIAGNOSTIC_ASSERT(ret);
|
||||
aRun->nfree--;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Re-fill aBin->mCurrentRun, then call arena_t::MallocBinEasy().
|
||||
void*
|
||||
arena_t::MallocBinHard(arena_bin_t* aBin)
|
||||
{
|
||||
aBin->mCurrentRun = GetNonFullBinRun(aBin);
|
||||
if (!aBin->mCurrentRun) {
|
||||
return nullptr;
|
||||
}
|
||||
MOZ_DIAGNOSTIC_ASSERT(aBin->mCurrentRun->magic == ARENA_RUN_MAGIC);
|
||||
MOZ_DIAGNOSTIC_ASSERT(aBin->mCurrentRun->nfree > 0);
|
||||
|
||||
return MallocBinEasy(aBin, aBin->mCurrentRun);
|
||||
}
|
||||
|
||||
// Calculate bin->mRunSize such that it meets the following constraints:
|
||||
//
|
||||
// *) bin->mRunSize >= min_run_size
|
||||
// *) bin->mRunSize <= gMaxLargeClass
|
||||
// *) bin->mRunSize <= gMaxBinClass
|
||||
// *) run header overhead <= RUN_MAX_OVRHD (or header overhead relaxed).
|
||||
//
|
||||
// bin->mRunNumRegions, bin->mRunNumRegionsMask, and bin->mRunFirstRegionOffset are
|
||||
// also calculated here, since these settings are all interdependent.
|
||||
static size_t
|
||||
arena_bin_run_size_calc(arena_bin_t* bin, size_t min_run_size)
|
||||
{
|
||||
size_t try_run_size, good_run_size;
|
||||
unsigned good_nregs, good_mask_nelms, good_reg0_offset;
|
||||
size_t try_run_size;
|
||||
unsigned try_nregs, try_mask_nelms, try_reg0_offset;
|
||||
// Size of the run header, excluding mRegionsMask.
|
||||
static const size_t kFixedHeaderSize = offsetof(arena_run_t, mRegionsMask);
|
||||
|
||||
MOZ_ASSERT(min_run_size >= gPageSize);
|
||||
MOZ_ASSERT(min_run_size <= gMaxLargeClass);
|
||||
MOZ_ASSERT(aSizeClass.Size() <= gMaxBinClass);
|
||||
|
||||
// Calculate known-valid settings before entering the mRunSize
|
||||
// expansion loop, so that the first part of the loop always copies
|
||||
// valid settings.
|
||||
//
|
||||
// The do..while loop iteratively reduces the number of regions until
|
||||
// the run header and the regions no longer overlap. A closed formula
|
||||
// would be quite messy, since there is an interdependency between the
|
||||
// header's mask length and the number of regions.
|
||||
try_run_size = min_run_size;
|
||||
try_nregs = ((try_run_size - sizeof(arena_run_t)) / bin->mSizeClass) +
|
||||
1; // Counter-act try_nregs-- in loop.
|
||||
do {
|
||||
try_nregs--;
|
||||
try_mask_nelms =
|
||||
(try_nregs >> (LOG2(sizeof(int)) + 3)) +
|
||||
((try_nregs & ((1U << (LOG2(sizeof(int)) + 3)) - 1)) ? 1 : 0);
|
||||
try_reg0_offset = try_run_size - (try_nregs * bin->mSizeClass);
|
||||
} while (sizeof(arena_run_t) + (sizeof(unsigned) * (try_mask_nelms - 1)) >
|
||||
try_reg0_offset);
|
||||
try_run_size = gPageSize;
|
||||
|
||||
mCurrentRun = nullptr;
|
||||
mNonFullRuns.Init();
|
||||
mSizeClass = aSizeClass.Size();
|
||||
mNumRuns = 0;
|
||||
|
||||
// mRunSize expansion loop.
|
||||
do {
|
||||
// Copy valid settings before trying more aggressive settings.
|
||||
good_run_size = try_run_size;
|
||||
good_nregs = try_nregs;
|
||||
good_mask_nelms = try_mask_nelms;
|
||||
good_reg0_offset = try_reg0_offset;
|
||||
|
||||
// Try more aggressive settings.
|
||||
try_run_size += gPageSize;
|
||||
try_nregs = ((try_run_size - sizeof(arena_run_t)) / bin->mSizeClass) +
|
||||
while (true) {
|
||||
try_nregs = ((try_run_size - kFixedHeaderSize) / mSizeClass) +
|
||||
1; // Counter-act try_nregs-- in loop.
|
||||
|
||||
// The do..while loop iteratively reduces the number of regions until
|
||||
// the run header and the regions no longer overlap. A closed formula
|
||||
// would be quite messy, since there is an interdependency between the
|
||||
// header's mask length and the number of regions.
|
||||
do {
|
||||
try_nregs--;
|
||||
try_mask_nelms =
|
||||
(try_nregs >> (LOG2(sizeof(int)) + 3)) +
|
||||
((try_nregs & ((1U << (LOG2(sizeof(int)) + 3)) - 1)) ? 1 : 0);
|
||||
try_reg0_offset = try_run_size - (try_nregs * bin->mSizeClass);
|
||||
} while (sizeof(arena_run_t) + (sizeof(unsigned) * (try_mask_nelms - 1)) >
|
||||
try_reg0_offset = try_run_size - (try_nregs * mSizeClass);
|
||||
} while (kFixedHeaderSize + (sizeof(unsigned) * try_mask_nelms) >
|
||||
try_reg0_offset);
|
||||
} while (try_run_size <= gMaxLargeClass &&
|
||||
RUN_MAX_OVRHD * (bin->mSizeClass << 3) > RUN_MAX_OVRHD_RELAX &&
|
||||
(try_reg0_offset << RUN_BFP) > RUN_MAX_OVRHD * try_run_size);
|
||||
|
||||
MOZ_ASSERT(sizeof(arena_run_t) + (sizeof(unsigned) * (good_mask_nelms - 1)) <=
|
||||
good_reg0_offset);
|
||||
MOZ_ASSERT((good_mask_nelms << (LOG2(sizeof(int)) + 3)) >= good_nregs);
|
||||
// Don't allow runs larger than the largest possible large size class.
|
||||
if (try_run_size > gMaxLargeClass) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Try to keep the run overhead below kRunOverhead.
|
||||
if (Fraction(try_reg0_offset, try_run_size) <= kRunOverhead) {
|
||||
break;
|
||||
}
|
||||
|
||||
// If the overhead is larger than the size class, it means the size class
|
||||
// is small and doesn't align very well with the header. It's desirable to
|
||||
// have smaller run sizes for them, so relax the overhead requirement.
|
||||
if (try_reg0_offset > mSizeClass) {
|
||||
if (Fraction(try_reg0_offset, try_run_size) <= kRunRelaxedOverhead) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The run header includes one bit per region of the given size. For sizes
|
||||
// small enough, the number of regions is large enough that growing the run
|
||||
// size barely moves the needle for the overhead because of all those bits.
|
||||
// For example, for a size of 8 bytes, adding 4KiB to the run size adds
|
||||
// close to 512 bits to the header, which is 64 bytes.
|
||||
// With such overhead, there is no way to get to the wanted overhead above,
|
||||
// so we give up if the required size for mRegionsMask more than doubles the
|
||||
// size of the run header.
|
||||
if (try_mask_nelms * sizeof(unsigned) >= kFixedHeaderSize) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Try more aggressive settings.
|
||||
try_run_size += gPageSize;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(kFixedHeaderSize + (sizeof(unsigned) * try_mask_nelms) <=
|
||||
try_reg0_offset);
|
||||
MOZ_ASSERT((try_mask_nelms << (LOG2(sizeof(int)) + 3)) >= try_nregs);
|
||||
|
||||
// Copy final settings.
|
||||
bin->mRunSize = good_run_size;
|
||||
bin->mRunNumRegions = good_nregs;
|
||||
bin->mRunNumRegionsMask = good_mask_nelms;
|
||||
bin->mRunFirstRegionOffset = good_reg0_offset;
|
||||
|
||||
return good_run_size;
|
||||
mRunSize = try_run_size;
|
||||
mRunNumRegions = try_nregs;
|
||||
mRunNumRegionsMask = try_mask_nelms;
|
||||
mRunFirstRegionOffset = try_reg0_offset;
|
||||
}
|
||||
|
||||
void*
|
||||
|
@ -3055,12 +3012,18 @@ arena_t::MallocSmall(size_t aSize, bool aZero)
|
|||
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
if ((run = bin->mCurrentRun) && run->nfree > 0) {
|
||||
ret = MallocBinEasy(bin, run);
|
||||
} else {
|
||||
ret = MallocBinHard(bin);
|
||||
run = bin->mCurrentRun;
|
||||
if (MOZ_UNLIKELY(!run || run->mNumFree == 0)) {
|
||||
run = bin->mCurrentRun = GetNonFullBinRun(bin);
|
||||
}
|
||||
|
||||
if (MOZ_UNLIKELY(!run)) {
|
||||
return nullptr;
|
||||
}
|
||||
MOZ_DIAGNOSTIC_ASSERT(run->mMagic == ARENA_RUN_MAGIC);
|
||||
MOZ_DIAGNOSTIC_ASSERT(run->mNumFree > 0);
|
||||
ret = arena_run_reg_alloc(run, bin);
|
||||
MOZ_DIAGNOSTIC_ASSERT(ret);
|
||||
run->mNumFree--;
|
||||
if (!ret) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -3091,7 +3054,7 @@ arena_t::MallocLarge(size_t aSize, bool aZero)
|
|||
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
ret = AllocRun(nullptr, aSize, true, aZero);
|
||||
ret = AllocRun(aSize, true, aZero);
|
||||
if (!ret) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -3145,7 +3108,7 @@ arena_t::Palloc(size_t aAlignment, size_t aSize, size_t aAllocSize)
|
|||
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
ret = AllocRun(nullptr, aAllocSize, true, false);
|
||||
ret = AllocRun(aAllocSize, true, false);
|
||||
if (!ret) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -3290,8 +3253,8 @@ arena_salloc(const void* ptr)
|
|||
MOZ_DIAGNOSTIC_ASSERT((mapbits & CHUNK_MAP_ALLOCATED) != 0);
|
||||
if ((mapbits & CHUNK_MAP_LARGE) == 0) {
|
||||
arena_run_t* run = (arena_run_t*)(mapbits & ~gPageSizeMask);
|
||||
MOZ_DIAGNOSTIC_ASSERT(run->magic == ARENA_RUN_MAGIC);
|
||||
ret = run->bin->mSizeClass;
|
||||
MOZ_DIAGNOSTIC_ASSERT(run->mMagic == ARENA_RUN_MAGIC);
|
||||
ret = run->mBin->mSizeClass;
|
||||
} else {
|
||||
ret = mapbits & ~gPageSizeMask;
|
||||
MOZ_DIAGNOSTIC_ASSERT(ret != 0);
|
||||
|
@ -3470,13 +3433,13 @@ MozJemalloc::jemalloc_ptr_info(const void* aPtr, jemalloc_ptr_info_t* aInfo)
|
|||
|
||||
// It must be a small allocation.
|
||||
auto run = (arena_run_t*)(mapbits & ~gPageSizeMask);
|
||||
MOZ_DIAGNOSTIC_ASSERT(run->magic == ARENA_RUN_MAGIC);
|
||||
MOZ_DIAGNOSTIC_ASSERT(run->mMagic == ARENA_RUN_MAGIC);
|
||||
|
||||
// The allocation size is stored in the run metadata.
|
||||
size_t size = run->bin->mSizeClass;
|
||||
size_t size = run->mBin->mSizeClass;
|
||||
|
||||
// Address of the first possible pointer in the run after its headers.
|
||||
uintptr_t reg0_addr = (uintptr_t)run + run->bin->mRunFirstRegionOffset;
|
||||
uintptr_t reg0_addr = (uintptr_t)run + run->mBin->mRunFirstRegionOffset;
|
||||
if (aPtr < (void*)reg0_addr) {
|
||||
// In the run header.
|
||||
*aInfo = { TagUnknown, nullptr, 0 };
|
||||
|
@ -3493,7 +3456,7 @@ MozJemalloc::jemalloc_ptr_info(const void* aPtr, jemalloc_ptr_info_t* aInfo)
|
|||
unsigned elm = regind >> (LOG2(sizeof(int)) + 3);
|
||||
unsigned bit = regind - (elm << (LOG2(sizeof(int)) + 3));
|
||||
PtrInfoTag tag =
|
||||
((run->regs_mask[elm] & (1U << bit))) ? TagFreedSmall : TagLiveSmall;
|
||||
((run->mRegionsMask[elm] & (1U << bit))) ? TagFreedSmall : TagLiveSmall;
|
||||
|
||||
*aInfo = { tag, addr, size };
|
||||
}
|
||||
|
@ -3519,8 +3482,8 @@ arena_t::DallocSmall(arena_chunk_t* aChunk,
|
|||
size_t size;
|
||||
|
||||
run = (arena_run_t*)(aMapElm->bits & ~gPageSizeMask);
|
||||
MOZ_DIAGNOSTIC_ASSERT(run->magic == ARENA_RUN_MAGIC);
|
||||
bin = run->bin;
|
||||
MOZ_DIAGNOSTIC_ASSERT(run->mMagic == ARENA_RUN_MAGIC);
|
||||
bin = run->mBin;
|
||||
size = bin->mSizeClass;
|
||||
MOZ_DIAGNOSTIC_ASSERT(uintptr_t(aPtr) >=
|
||||
uintptr_t(run) + bin->mRunFirstRegionOffset);
|
||||
|
@ -3531,9 +3494,9 @@ arena_t::DallocSmall(arena_chunk_t* aChunk,
|
|||
memset(aPtr, kAllocPoison, size);
|
||||
|
||||
arena_run_reg_dalloc(run, bin, aPtr, size);
|
||||
run->nfree++;
|
||||
run->mNumFree++;
|
||||
|
||||
if (run->nfree == bin->mRunNumRegions) {
|
||||
if (run->mNumFree == bin->mRunNumRegions) {
|
||||
// Deallocate run.
|
||||
if (run == bin->mCurrentRun) {
|
||||
bin->mCurrentRun = nullptr;
|
||||
|
@ -3549,18 +3512,18 @@ arena_t::DallocSmall(arena_chunk_t* aChunk,
|
|||
bin->mNonFullRuns.Remove(run_mapelm);
|
||||
}
|
||||
#if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
|
||||
run->magic = 0;
|
||||
run->mMagic = 0;
|
||||
#endif
|
||||
DallocRun(run, true);
|
||||
bin->mNumRuns--;
|
||||
} else if (run->nfree == 1 && run != bin->mCurrentRun) {
|
||||
} else if (run->mNumFree == 1 && run != bin->mCurrentRun) {
|
||||
// Make sure that bin->mCurrentRun always refers to the lowest
|
||||
// non-full run, if one exists.
|
||||
if (!bin->mCurrentRun) {
|
||||
bin->mCurrentRun = run;
|
||||
} else if (uintptr_t(run) < uintptr_t(bin->mCurrentRun)) {
|
||||
// Switch mCurrentRun.
|
||||
if (bin->mCurrentRun->nfree > 0) {
|
||||
if (bin->mCurrentRun->mNumFree > 0) {
|
||||
arena_chunk_t* runcur_chunk = GetChunkForPtr(bin->mCurrentRun);
|
||||
size_t runcur_pageind =
|
||||
(uintptr_t(bin->mCurrentRun) - uintptr_t(runcur_chunk)) >>
|
||||
|
@ -3792,8 +3755,6 @@ iralloc(void* aPtr, size_t aSize, arena_t* aArena)
|
|||
arena_t::arena_t()
|
||||
{
|
||||
unsigned i;
|
||||
arena_bin_t* bin;
|
||||
size_t prev_run_size;
|
||||
|
||||
MOZ_RELEASE_ASSERT(mLock.Init());
|
||||
|
||||
|
@ -3815,19 +3776,11 @@ arena_t::arena_t()
|
|||
mRunsAvail.Init();
|
||||
|
||||
// Initialize bins.
|
||||
prev_run_size = gPageSize;
|
||||
SizeClass sizeClass(1);
|
||||
|
||||
for (i = 0;; i++) {
|
||||
bin = &mBins[i];
|
||||
bin->mCurrentRun = nullptr;
|
||||
bin->mNonFullRuns.Init();
|
||||
|
||||
bin->mSizeClass = sizeClass.Size();
|
||||
|
||||
prev_run_size = arena_bin_run_size_calc(bin, prev_run_size);
|
||||
|
||||
bin->mNumRuns = 0;
|
||||
arena_bin_t& bin = mBins[i];
|
||||
bin.Init(sizeClass);
|
||||
|
||||
// SizeClass doesn't want sizes larger than gMaxSubPageClass for now.
|
||||
if (sizeClass.Size() == gMaxSubPageClass) {
|
||||
|
@ -4577,11 +4530,11 @@ MozJemalloc::jemalloc_stats(jemalloc_stats_t* aStats)
|
|||
|
||||
for (auto mapelm : bin->mNonFullRuns.iter()) {
|
||||
run = (arena_run_t*)(mapelm->bits & ~gPageSizeMask);
|
||||
bin_unused += run->nfree * bin->mSizeClass;
|
||||
bin_unused += run->mNumFree * bin->mSizeClass;
|
||||
}
|
||||
|
||||
if (bin->mCurrentRun) {
|
||||
bin_unused += bin->mCurrentRun->nfree * bin->mSizeClass;
|
||||
bin_unused += bin->mCurrentRun->mNumFree * bin->mSizeClass;
|
||||
}
|
||||
|
||||
arena_unused += bin_unused;
|
||||
|
|
|
@ -193,7 +193,7 @@ this.FxAccountsStorageManager.prototype = {
|
|||
result[fieldName] = this.cachedSecure[fieldName];
|
||||
}
|
||||
} else {
|
||||
throw new Error("unexpected field '" + name + "'");
|
||||
throw new Error("unexpected field '" + fieldName + "'");
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
|
|
@ -510,8 +510,7 @@ this.FxAccountsWebChannelHelpers.prototype = {
|
|||
ps.BUTTON_POS_1_DEFAULT;
|
||||
|
||||
// If running in context of the browser chrome, window does not exist.
|
||||
var targetWindow = typeof window === "undefined" ? null : window;
|
||||
let pressed = Services.prompt.confirmEx(targetWindow, title, body, buttonFlags,
|
||||
let pressed = Services.prompt.confirmEx(null, title, body, buttonFlags,
|
||||
continueLabel, null, null, null,
|
||||
{});
|
||||
return pressed === 0; // 0 is the "continue" button
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,4 +1,4 @@
|
|||
[clearkey-events-session-closed-event.html]
|
||||
[clearkey-events-session-closed-event.https.html]
|
||||
type: testharness
|
||||
[org.w3.clearkey test MediaKeySession closed event.]
|
||||
expected:
|
|
@ -0,0 +1,3 @@
|
|||
[clearkey-mp4-playback-destroy-persistent-license.https.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -0,0 +1,3 @@
|
|||
[clearkey-mp4-playback-persistent-license-events.https.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -0,0 +1,3 @@
|
|||
[clearkey-mp4-playback-persistent-license.https.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -0,0 +1,3 @@
|
|||
[clearkey-mp4-playback-persistent-usage-record-events.https.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -0,0 +1,3 @@
|
|||
[clearkey-mp4-playback-persistent-usage-record.https.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -0,0 +1,3 @@
|
|||
[clearkey-mp4-playback-retrieve-destroy-persistent-license.https.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -1,3 +0,0 @@
|
|||
[clearkey-mp4-playback-retrieve-persistent-license.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -0,0 +1,3 @@
|
|||
[clearkey-mp4-playback-retrieve-persistent-license.https.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -1,3 +0,0 @@
|
|||
[clearkey-mp4-playback-retrieve-persistent-usage-record.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -0,0 +1,3 @@
|
|||
[clearkey-mp4-playback-retrieve-persistent-usage-record.https.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -1,4 +1,4 @@
|
|||
[clearkey-mp4-playback-temporary-clear-encrypted.html]
|
||||
[clearkey-mp4-playback-temporary-clear-encrypted.https.html]
|
||||
type: testharness
|
||||
[org.w3.clearkey, temporary, mp4, playback, single key, clear then encrypted content]
|
||||
expected:
|
|
@ -1,4 +1,4 @@
|
|||
[clearkey-mp4-playback-temporary-encrypted-clear-sources.html]
|
||||
[clearkey-mp4-playback-temporary-encrypted-clear-sources.https.html]
|
||||
type: testharness
|
||||
[org.w3.clearkey, temporary, mp4, playback, encrypted and clear sources]
|
||||
expected:
|
|
@ -1,4 +1,4 @@
|
|||
[clearkey-mp4-playback-temporary-encrypted-clear.html]
|
||||
[clearkey-mp4-playback-temporary-encrypted-clear.https.html]
|
||||
type: testharness
|
||||
disabled:
|
||||
if os == "linux": https://bugzilla.mozilla.org/show_bug.cgi?id=1301418
|
|
@ -1,4 +1,4 @@
|
|||
[clearkey-mp4-playback-temporary-events.html]
|
||||
[clearkey-mp4-playback-temporary-events.https.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1312914
|
||||
[org.w3.clearkey, sucessful playback and events, temporary, mp4, set src before setMediaKeys]
|
|
@ -1,4 +1,4 @@
|
|||
[clearkey-mp4-playback-temporary-multikey-sequential-readyState.html]
|
||||
[clearkey-mp4-playback-temporary-multikey-sequential-readyState.https.html]
|
||||
type: testharness
|
||||
[org.w3.clearkey, successful playback, temporary, mp4, multiple keys, sequential, readyState]
|
||||
expected:
|
|
@ -1,4 +1,4 @@
|
|||
[clearkey-mp4-playback-temporary-multikey.html]
|
||||
[clearkey-mp4-playback-temporary-multikey.https.html]
|
||||
type: testharness
|
||||
[org.w3.clearkey, successful playback, temporary, mp4, multiple keys, single session, audio/video]
|
||||
expected:
|
|
@ -1,4 +1,4 @@
|
|||
[clearkey-mp4-playback-temporary-multisession.html]
|
||||
[clearkey-mp4-playback-temporary-multisession.https.html]
|
||||
type: testharness
|
||||
[org.w3.clearkey, temporary, mp4, playback with multiple sessions, multikey video]
|
||||
expected:
|
|
@ -1,4 +1,4 @@
|
|||
[clearkey-mp4-playback-temporary-setMediaKeys-after-src.html]
|
||||
[clearkey-mp4-playback-temporary-setMediaKeys-after-src.https.html]
|
||||
type: testharness
|
||||
[org.w3.clearkey, temporary, mp4, playback, setMediaKeys after setting video.src]
|
||||
expected:
|
|
@ -1,4 +1,4 @@
|
|||
[clearkey-mp4-playback-temporary-setMediaKeys-after-update.html]
|
||||
[clearkey-mp4-playback-temporary-setMediaKeys-after-update.https.html]
|
||||
type: testharness
|
||||
[org.w3.clearkey, temporary, mp4, playback, setMediaKeys after updating session]
|
||||
expected:
|
|
@ -1,4 +1,4 @@
|
|||
[clearkey-mp4-playback-temporary-setMediaKeys-immediately.html]
|
||||
[clearkey-mp4-playback-temporary-setMediaKeys-immediately.https.html]
|
||||
type: testharness
|
||||
[org.w3.clearkey, temporary, mp4, playback, setMediaKeys first]
|
||||
expected:
|
|
@ -1,4 +1,4 @@
|
|||
[clearkey-mp4-playback-temporary-setMediaKeys-onencrypted.html]
|
||||
[clearkey-mp4-playback-temporary-setMediaKeys-onencrypted.https.html]
|
||||
type: testharness
|
||||
[org.w3.clearkey, temporary, mp4, playback, setMediaKeys in encrypted event]
|
||||
expected:
|
|
@ -1,4 +1,4 @@
|
|||
[clearkey-mp4-playback-temporary-two-videos.html]
|
||||
[clearkey-mp4-playback-temporary-two-videos.https.html]
|
||||
type: testharness
|
||||
[org.w3.clearkey, sucessful playback, temporary, mp4, set src before setMediaKeys]
|
||||
expected:
|
|
@ -1,4 +1,4 @@
|
|||
[clearkey-mp4-playback-temporary-waitingforkey.html]
|
||||
[clearkey-mp4-playback-temporary-waitingforkey.https.html]
|
||||
type: testharness
|
||||
[org.w3.clearkey, successful playback, temporary, mp4, waitingforkey event, 1 key]
|
||||
expected:
|
|
@ -1,4 +1,4 @@
|
|||
[clearkey-mp4-playback-temporary.html]
|
||||
[clearkey-mp4-playback-temporary.https.html]
|
||||
type: testharness
|
||||
[org.w3.clearkey, sucessful playback, temporary, mp4, set src before setMediaKeys]
|
||||
expected:
|
|
@ -1,4 +1,4 @@
|
|||
[clearkey-mp4-requestmediakeysystemaccess.html]
|
||||
[clearkey-mp4-requestmediakeysystemaccess.https.html]
|
||||
type: testharness
|
||||
[Basic supported configuration]
|
||||
expected:
|
|
@ -1,4 +1,4 @@
|
|||
[clearkey-mp4-reset-src-after-setmediakeys.html]
|
||||
[clearkey-mp4-reset-src-after-setmediakeys.https.html]
|
||||
type: testharness
|
||||
[Reset src after setMediaKeys().]
|
||||
expected:
|
|
@ -1,4 +1,4 @@
|
|||
[clearkey-mp4-setmediakeys-again-after-resetting-src.html]
|
||||
[clearkey-mp4-setmediakeys-again-after-resetting-src.https.html]
|
||||
type: testharness
|
||||
[org.w3.clearkey, setmediakeys again after resetting src]
|
||||
expected:
|
|
@ -1,4 +1,4 @@
|
|||
[clearkey-mp4-setmediakeys-multiple-times-with-different-mediakeys.html]
|
||||
[clearkey-mp4-setmediakeys-multiple-times-with-different-mediakeys.https.html]
|
||||
type: testharness
|
||||
[org.w3.clearkey, setmediakeys multiple times with different mediakeys]
|
||||
expected:
|
|
@ -1,4 +1,4 @@
|
|||
[clearkey-mp4-setmediakeys-multiple-times-with-the-same-mediakeys.html]
|
||||
[clearkey-mp4-setmediakeys-multiple-times-with-the-same-mediakeys.https.html]
|
||||
type: testharness
|
||||
[org.w3.clearkey, setmediakeys multiple times with the same mediakeys]
|
||||
expected:
|
|
@ -1,4 +1,4 @@
|
|||
[clearkey-mp4-syntax-mediakeys.html]
|
||||
[clearkey-mp4-syntax-mediakeys.https.html]
|
||||
type: testharness
|
||||
[org.w3.clearkey test MediaKeys attribute syntax]
|
||||
expected:
|
|
@ -1,4 +1,4 @@
|
|||
[clearkey-mp4-syntax-mediakeysystemaccess.html]
|
||||
[clearkey-mp4-syntax-mediakeysystemaccess.https.html]
|
||||
type: testharness
|
||||
[org.w3.clearkey test MediaKeySystemAccess attribute syntax.]
|
||||
expected:
|
|
@ -1,4 +1,4 @@
|
|||
[clearkey-mp4-unique-origin.html]
|
||||
[clearkey-mp4-unique-origin.https.html]
|
||||
type: testharness
|
||||
[Unique origin is unable to create MediaKeys]
|
||||
expected: FAIL
|
|
@ -1,4 +1,4 @@
|
|||
[clearkey-mp4-waiting-for-a-key.html]
|
||||
[clearkey-mp4-waiting-for-a-key.https.html]
|
||||
type: testharness
|
||||
[Waiting for a key.]
|
||||
expected:
|
|
@ -1,4 +1,4 @@
|
|||
[clearkey-update-non-ascii-input.html]
|
||||
[clearkey-update-non-ascii-input.https.html]
|
||||
type: testharness
|
||||
[org.w3.clearkey test handling of non-ASCII responses for update()]
|
||||
expected:
|
|
@ -0,0 +1,3 @@
|
|||
[drm-check-initdata-type.https.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -0,0 +1,3 @@
|
|||
[drm-events-session-closed-event.https.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -1,3 +1,3 @@
|
|||
[drm-events.html]
|
||||
[drm-events.https.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -1,3 +1,3 @@
|
|||
[drm-keystatuses.html]
|
||||
[drm-expiration.https.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -1,3 +0,0 @@
|
|||
[drm-generate-request-disallowed-input.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -0,0 +1,3 @@
|
|||
[drm-generate-request-disallowed-input.https.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -1,3 +1,3 @@
|
|||
[drm-check-initdata-type.html]
|
||||
[drm-invalid-license.https.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -1,3 +0,0 @@
|
|||
[drm-keystatuses-multiple-sessions.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -0,0 +1,3 @@
|
|||
[drm-keystatuses-multiple-sessions.https.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -1,3 +1,3 @@
|
|||
[drm-invalid-license.html]
|
||||
[drm-keystatuses.https.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -1,3 +0,0 @@
|
|||
[drm-mp4-onencrypted.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -1,3 +1,3 @@
|
|||
[drm-expiration.html]
|
||||
[drm-mp4-onencrypted.https.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -1,3 +0,0 @@
|
|||
[drm-mp4-playback-destroy-persistent-license.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -0,0 +1,3 @@
|
|||
[drm-mp4-playback-destroy-persistent-license.https.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -1,3 +0,0 @@
|
|||
[drm-mp4-playback-persistent-license-events.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -1,3 +1,3 @@
|
|||
[clearkey-mp4-playback-destroy-persistent-license.html]
|
||||
[drm-mp4-playback-persistent-license-events.https.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -1,3 +0,0 @@
|
|||
[drm-mp4-playback-persistent-license.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -0,0 +1,3 @@
|
|||
[drm-mp4-playback-persistent-license.https.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -1,3 +0,0 @@
|
|||
[drm-mp4-playback-persistent-usage-record-events.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -0,0 +1,3 @@
|
|||
[drm-mp4-playback-persistent-usage-record-events.https.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -1,3 +0,0 @@
|
|||
[drm-mp4-playback-persistent-usage-record.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -0,0 +1,3 @@
|
|||
[drm-mp4-playback-persistent-usage-record.https.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -1,3 +0,0 @@
|
|||
[drm-mp4-playback-retrieve-destroy-persistent-license.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -0,0 +1,3 @@
|
|||
[drm-mp4-playback-retrieve-destroy-persistent-license.https.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -1,3 +0,0 @@
|
|||
[drm-mp4-playback-retrieve-persistent-license.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -0,0 +1,3 @@
|
|||
[drm-mp4-playback-retrieve-persistent-license.https.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -1,3 +0,0 @@
|
|||
[drm-mp4-playback-retrieve-persistent-usage-record.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
|
@ -0,0 +1,3 @@
|
|||
[drm-mp4-playback-retrieve-persistent-usage-record.https.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1313284
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче