зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to inbound a=merge on a CLOSED TREE
This commit is contained in:
Коммит
3507348c1a
|
@ -180,6 +180,11 @@ export default class AddressForm extends PaymentStateSubscriberMixin(PaymentRequ
|
|||
} else {
|
||||
this.form.dataset.addressFields = "mailing-address tel";
|
||||
}
|
||||
if (addressPage.selectedStateKey == "selectedPayerAddress") {
|
||||
this.form.dataset.extraRequiredFields = addressPage.addressFields;
|
||||
} else {
|
||||
this.form.dataset.extraRequiredFields = "";
|
||||
}
|
||||
this.formHandler.loadRecord(record);
|
||||
|
||||
// Add validation to some address fields
|
||||
|
|
|
@ -304,7 +304,7 @@ export default class PaymentDialog extends PaymentStateSubscriberMixin(HTMLEleme
|
|||
}
|
||||
|
||||
if (payerRequested) {
|
||||
let fieldNames = new Set(); // default: ["name", "tel", "email"]
|
||||
let fieldNames = new Set();
|
||||
if (paymentOptions.requestPayerName) {
|
||||
fieldNames.add("name");
|
||||
}
|
||||
|
|
|
@ -75,6 +75,7 @@ var PaymentDialogUtils = {
|
|||
{fieldId: "address-level2"},
|
||||
],
|
||||
postalCodePattern: "\\d{5}",
|
||||
countryRequiredFields: ["street-address", "address-level2", "postal-code"],
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -94,6 +95,9 @@ var PaymentDialogUtils = {
|
|||
// The following values come from addressReferences.js and should not be changed.
|
||||
/* eslint-disable-next-line max-len */
|
||||
postalCodePattern: country == "US" ? "(\\d{5})(?:[ \\-](\\d{4}))?" : "[ABCEGHJKLMNPRSTVXY]\\d[ABCEGHJ-NPRSTV-Z] ?\\d[ABCEGHJ-NPRSTV-Z]\\d",
|
||||
countryRequiredFields: country == "US" || country == "CA" ?
|
||||
["street-address", "address-level2", "address-level1", "postal-code"] :
|
||||
["street-address", "address-level2", "postal-code"],
|
||||
};
|
||||
},
|
||||
getDefaultPreferences() {
|
||||
|
|
|
@ -17,6 +17,7 @@ skip-if = debug && (os == 'mac' || os == 'linux') # bug 1465673
|
|||
[browser_host_name.js]
|
||||
[browser_onboarding_wizard.js]
|
||||
[browser_openPreferences.js]
|
||||
[browser_payerRequestedFields.js]
|
||||
[browser_payment_completion.js]
|
||||
[browser_profile_storage.js]
|
||||
[browser_request_serialization.js]
|
||||
|
|
|
@ -471,6 +471,10 @@ add_task(async function test_payer_address_picker() {
|
|||
* Test that we can correctly add an address from a private window
|
||||
*/
|
||||
add_task(async function test_private_persist_addresses() {
|
||||
if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) {
|
||||
todo(false, "Cannot test OS key store login on official builds.");
|
||||
return;
|
||||
}
|
||||
let prefilledGuids = await setup();
|
||||
|
||||
is((await formAutofillStorage.addresses.getAll()).length, 1,
|
||||
|
@ -569,7 +573,7 @@ add_task(async function test_private_persist_addresses() {
|
|||
});
|
||||
|
||||
info("clicking pay");
|
||||
spawnPaymentDialogTask(frame, PTU.DialogContentTasks.completePayment);
|
||||
await loginAndCompletePayment(frame);
|
||||
|
||||
// Add a handler to complete the payment above.
|
||||
info("acknowledging the completion from the merchant page");
|
||||
|
|
|
@ -12,6 +12,10 @@ async function setup(addresses = [], cards = []) {
|
|||
}
|
||||
|
||||
async function add_link(aOptions = {}) {
|
||||
if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) {
|
||||
todo(false, "Cannot test OS key store login on official builds.");
|
||||
return;
|
||||
}
|
||||
let tabOpenFn = aOptions.isPrivate ? withNewTabInPrivateWindow : BrowserTestUtils.withNewTab;
|
||||
await tabOpenFn({
|
||||
gBrowser,
|
||||
|
@ -257,7 +261,7 @@ async function add_link(aOptions = {}) {
|
|||
"The saved card should be associated with the billing address");
|
||||
}, aOptions);
|
||||
|
||||
spawnPaymentDialogTask(frame, PTU.DialogContentTasks.completePayment);
|
||||
await loginAndCompletePayment(frame);
|
||||
|
||||
// Add a handler to complete the payment above.
|
||||
info("acknowledging the completion from the merchant page");
|
||||
|
|
|
@ -11,6 +11,10 @@ async function setup() {
|
|||
}
|
||||
|
||||
add_task(async function test_change_shipping() {
|
||||
if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) {
|
||||
todo(false, "Cannot test OS key store login on official builds.");
|
||||
return;
|
||||
}
|
||||
await setup();
|
||||
await BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
|
@ -108,7 +112,7 @@ add_task(async function test_change_shipping() {
|
|||
});
|
||||
|
||||
info("clicking pay");
|
||||
spawnPaymentDialogTask(frame, PTU.DialogContentTasks.completePayment);
|
||||
await loginAndCompletePayment(frame);
|
||||
|
||||
// Add a handler to complete the payment above.
|
||||
info("acknowledging the completion from the merchant page");
|
||||
|
@ -249,6 +253,10 @@ add_task(async function test_default_shippingOptions_allSelected() {
|
|||
});
|
||||
|
||||
add_task(async function test_no_shippingchange_without_shipping() {
|
||||
if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) {
|
||||
todo(false, "Cannot test OS key store login on official builds.");
|
||||
return;
|
||||
}
|
||||
await setup();
|
||||
await BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
|
@ -272,7 +280,7 @@ add_task(async function test_no_shippingchange_without_shipping() {
|
|||
});
|
||||
|
||||
info("clicking pay");
|
||||
spawnPaymentDialogTask(frame, PTU.DialogContentTasks.completePayment);
|
||||
await loginAndCompletePayment(frame);
|
||||
|
||||
// Add a handler to complete the payment above.
|
||||
info("acknowledging the completion from the merchant page");
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
/* eslint-disable no-shadow */
|
||||
|
||||
"use strict";
|
||||
|
||||
async function setup() {
|
||||
await setupFormAutofillStorage();
|
||||
await cleanupFormAutofillStorage();
|
||||
// add an address and card to avoid the FTU sequence
|
||||
let prefilledGuids = await addSampleAddressesAndBasicCard(
|
||||
[PTU.Addresses.TimBL], [PTU.BasicCards.JohnDoe]);
|
||||
|
||||
info("associating the card with the billing address");
|
||||
await formAutofillStorage.creditCards.update(prefilledGuids.card1GUID, {
|
||||
billingAddressGUID: prefilledGuids.address1GUID,
|
||||
}, true);
|
||||
|
||||
return prefilledGuids;
|
||||
}
|
||||
|
||||
/*
|
||||
* Test that the payerRequested* fields are marked as required
|
||||
* on the payer address form but aren't marked as required on
|
||||
* the shipping address form.
|
||||
*/
|
||||
add_task(async function test_add_link() {
|
||||
await setup();
|
||||
|
||||
await BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: BLANK_PAGE_URL,
|
||||
}, async browser => {
|
||||
let {win, frame} =
|
||||
await setupPaymentDialog(browser, {
|
||||
methodData: [PTU.MethodData.basicCard],
|
||||
details: Object.assign({}, PTU.Details.twoShippingOptions, PTU.Details.total2USD),
|
||||
options: {...PTU.Options.requestShipping, ...PTU.Options.requestPayerNameEmailAndPhone},
|
||||
merchantTaskFn: PTU.ContentTasks.createAndShowRequest,
|
||||
}
|
||||
);
|
||||
|
||||
await navigateToAddAddressPage(frame, {
|
||||
addLinkSelector: "address-picker.payer-related .add-link",
|
||||
initialPageId: "payment-summary",
|
||||
expectPersist: true,
|
||||
});
|
||||
|
||||
await spawnPaymentDialogTask(frame, async () => {
|
||||
let {
|
||||
PaymentTestUtils,
|
||||
} = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
|
||||
|
||||
let title = content.document.querySelector("address-form h2");
|
||||
is(title.textContent, "Add Payer Contact", "Page title should be set");
|
||||
|
||||
let saveButton = content.document.querySelector("address-form .save-button");
|
||||
is(saveButton.textContent, "Next", "Save button has the correct label");
|
||||
|
||||
info("check that payer requested fields are marked as required");
|
||||
for (let selector of ["#given-name", "#family-name", "#email", "#tel"]) {
|
||||
let element = content.document.querySelector(selector);
|
||||
ok(element.required, selector + " should be required");
|
||||
}
|
||||
|
||||
let backButton = content.document.querySelector("address-form .back-button");
|
||||
ok(content.isVisible(backButton),
|
||||
"Back button is visible on the payer address page");
|
||||
backButton.click();
|
||||
|
||||
await PaymentTestUtils.DialogContentUtils.waitForState(content, (state) => {
|
||||
return state.page.id == "payment-summary";
|
||||
}, "Switched back to payment-summary from payer address form");
|
||||
});
|
||||
|
||||
await navigateToAddAddressPage(frame, {
|
||||
addLinkSelector: "address-picker .add-link",
|
||||
initialPageId: "payment-summary",
|
||||
expectPersist: true,
|
||||
});
|
||||
|
||||
await spawnPaymentDialogTask(frame, async () => {
|
||||
let {
|
||||
PaymentTestUtils,
|
||||
} = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
|
||||
|
||||
let title = content.document.querySelector("address-form h2");
|
||||
is(title.textContent, "Add Shipping Address", "Page title should be set");
|
||||
|
||||
let saveButton = content.document.querySelector("address-form .save-button");
|
||||
is(saveButton.textContent, "Next", "Save button has the correct label");
|
||||
|
||||
ok(!content.document.querySelector("#tel").required, "#tel should not be required");
|
||||
|
||||
let backButton = content.document.querySelector("address-form .back-button");
|
||||
ok(content.isVisible(backButton),
|
||||
"Back button is visible on the payer address page");
|
||||
backButton.click();
|
||||
|
||||
await PaymentTestUtils.DialogContentUtils.waitForState(content, (state) => {
|
||||
return state.page.id == "payment-summary";
|
||||
}, "Switched back to payment-summary from payer address form");
|
||||
});
|
||||
|
||||
spawnPaymentDialogTask(frame, PTU.DialogContentTasks.manuallyClickCancel);
|
||||
await BrowserTestUtils.waitForCondition(() => win.closed, "dialog should be closed");
|
||||
});
|
||||
await cleanupFormAutofillStorage();
|
||||
});
|
||||
|
|
@ -15,6 +15,10 @@ async function setup() {
|
|||
}
|
||||
|
||||
add_task(async function test_complete_success() {
|
||||
if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) {
|
||||
todo(false, "Cannot test OS key store login on official builds.");
|
||||
return;
|
||||
}
|
||||
await setup();
|
||||
await BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
|
@ -32,7 +36,7 @@ add_task(async function test_complete_success() {
|
|||
securityCode: "123",
|
||||
});
|
||||
|
||||
spawnPaymentDialogTask(frame, PTU.DialogContentTasks.completePayment);
|
||||
await loginAndCompletePayment(frame);
|
||||
|
||||
// Add a handler to complete the payment above.
|
||||
info("acknowledging the completion from the merchant page");
|
||||
|
@ -47,6 +51,10 @@ add_task(async function test_complete_success() {
|
|||
});
|
||||
|
||||
add_task(async function test_complete_fail() {
|
||||
if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) {
|
||||
todo(false, "Cannot test OS key store login on official builds.");
|
||||
return;
|
||||
}
|
||||
await setup();
|
||||
await BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
|
@ -65,7 +73,7 @@ add_task(async function test_complete_fail() {
|
|||
});
|
||||
|
||||
info("clicking pay");
|
||||
spawnPaymentDialogTask(frame, PTU.DialogContentTasks.completePayment);
|
||||
await loginAndCompletePayment(frame);
|
||||
|
||||
info("acknowledging the completion from the merchant page");
|
||||
let {completeException} = await ContentTask.spawn(browser,
|
||||
|
@ -81,6 +89,10 @@ add_task(async function test_complete_fail() {
|
|||
});
|
||||
|
||||
add_task(async function test_complete_timeout() {
|
||||
if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) {
|
||||
todo(false, "Cannot test OS key store login on official builds.");
|
||||
return;
|
||||
}
|
||||
await setup();
|
||||
await BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
|
@ -102,7 +114,7 @@ add_task(async function test_complete_timeout() {
|
|||
});
|
||||
|
||||
info("clicking pay");
|
||||
await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.completePayment);
|
||||
await loginAndCompletePayment(frame);
|
||||
|
||||
info("acknowledging the completion from the merchant page after a delay");
|
||||
let {completeException} = await ContentTask.spawn(browser,
|
||||
|
|
|
@ -14,6 +14,10 @@ async function setup() {
|
|||
}
|
||||
|
||||
add_task(async function test_retry_with_genericError() {
|
||||
if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) {
|
||||
todo(false, "Cannot test OS key store login on official builds.");
|
||||
return;
|
||||
}
|
||||
await setup();
|
||||
await BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
|
@ -30,7 +34,7 @@ add_task(async function test_retry_with_genericError() {
|
|||
});
|
||||
|
||||
info("clicking the button to try pay the 1st time");
|
||||
await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.completePayment);
|
||||
await loginAndCompletePayment(frame);
|
||||
|
||||
let retryUpdatePromise = spawnPaymentDialogTask(frame, async function checkDialog() {
|
||||
let {
|
||||
|
@ -71,8 +75,7 @@ add_task(async function test_retry_with_genericError() {
|
|||
PTU.ContentTasks.addRetryHandler);
|
||||
|
||||
await retryUpdatePromise;
|
||||
|
||||
spawnPaymentDialogTask(frame, PTU.DialogContentTasks.completePayment);
|
||||
await loginAndCompletePayment(frame);
|
||||
|
||||
// We can only check the retry response after the closing as it only resolves upon complete.
|
||||
let {retryException} = await retryPromise;
|
||||
|
|
|
@ -41,6 +41,10 @@ add_task(async function test_show_manualAbort_dialog() {
|
|||
});
|
||||
|
||||
add_task(async function test_show_completePayment() {
|
||||
if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) {
|
||||
todo(false, "Cannot test OS key store login on official builds.");
|
||||
return;
|
||||
}
|
||||
let {address1GUID, card1GUID} = await addSampleAddressesAndBasicCard();
|
||||
|
||||
let onChanged = TestUtils.topicObserved("formautofill-storage-changed",
|
||||
|
@ -72,7 +76,7 @@ add_task(async function test_show_completePayment() {
|
|||
securityCode: "999",
|
||||
});
|
||||
info("clicking pay");
|
||||
spawnPaymentDialogTask(frame, PTU.DialogContentTasks.completePayment);
|
||||
await loginAndCompletePayment(frame);
|
||||
|
||||
// Add a handler to complete the payment above.
|
||||
info("acknowledging the completion from the merchant page");
|
||||
|
@ -97,6 +101,11 @@ add_task(async function test_show_completePayment() {
|
|||
});
|
||||
|
||||
add_task(async function test_show_completePayment2() {
|
||||
if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) {
|
||||
todo(false, "Cannot test OS key store login on official builds.");
|
||||
return;
|
||||
}
|
||||
|
||||
await BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: BLANK_PAGE_URL,
|
||||
|
@ -131,7 +140,7 @@ add_task(async function test_show_completePayment2() {
|
|||
});
|
||||
|
||||
info("clicking pay");
|
||||
spawnPaymentDialogTask(frame, PTU.DialogContentTasks.completePayment);
|
||||
await loginAndCompletePayment(frame);
|
||||
|
||||
// Add a handler to complete the payment above.
|
||||
info("acknowledging the completion from the merchant page");
|
||||
|
|
|
@ -328,6 +328,12 @@ async function spawnInDialogForMerchantTask(merchantTaskFn, dialogTaskFn, taskAr
|
|||
});
|
||||
}
|
||||
|
||||
async function loginAndCompletePayment(frame) {
|
||||
let osKeyStoreLoginShown = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true);
|
||||
await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.completePayment);
|
||||
await osKeyStoreLoginShown;
|
||||
}
|
||||
|
||||
async function setupFormAutofillStorage() {
|
||||
await formAutofillStorage.initialize();
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ function checkAddressForm(customEl, expectedAddress) {
|
|||
function sendStringAndCheckValidity(element, string, isValid) {
|
||||
fillField(element, string);
|
||||
ok(element.checkValidity() == isValid,
|
||||
`${element.id} should be ${isValid ? "valid" : "invalid"} (${string})`);
|
||||
`${element.id} should be ${isValid ? "valid" : "invalid"} ("${string}")`);
|
||||
}
|
||||
|
||||
add_task(async function test_initialState() {
|
||||
|
@ -278,8 +278,9 @@ add_task(async function test_edit() {
|
|||
|
||||
add_task(async function test_restricted_address_fields() {
|
||||
let form = new AddressForm();
|
||||
form.dataset.nextButtonLabel = "Next";
|
||||
form.dataset.errorGenericSave = "Generic error";
|
||||
form.dataset.fieldRequiredSymbol = "*";
|
||||
form.dataset.nextButtonLabel = "Next";
|
||||
await form.promiseReady;
|
||||
display.appendChild(form);
|
||||
await form.requestStore.setState({
|
||||
|
@ -312,15 +313,23 @@ add_task(async function test_restricted_address_fields() {
|
|||
"country should be hidden");
|
||||
ok(!isHidden(form.form.querySelector("#email")),
|
||||
"email should be visible");
|
||||
ok(!isHidden(form.form.querySelector("#tel")),
|
||||
let telField = form.form.querySelector("#tel");
|
||||
ok(!isHidden(telField),
|
||||
"tel should be visible");
|
||||
let telContainer = telField.closest(`#${telField.id}-container`);
|
||||
ok(telContainer.hasAttribute("required"), "tel container should have required attribute");
|
||||
let telSpan = telContainer.querySelector("span");
|
||||
is(telSpan.getAttribute("fieldRequiredSymbol"), "*",
|
||||
"tel span should have asterisk as fieldRequiredSymbol");
|
||||
is(getComputedStyle(telSpan, "::after").content, "attr(fieldRequiredSymbol)",
|
||||
"Asterisk should be on tel");
|
||||
|
||||
fillField(form.form.querySelector("#given-name"), "John");
|
||||
fillField(form.form.querySelector("#family-name"), "Smith");
|
||||
ok(form.saveButton.disabled, "Save button should be disabled due to empty fields");
|
||||
fillField(form.form.querySelector("#email"), "john@example.com");
|
||||
todo(form.saveButton.disabled,
|
||||
"Save button should be disabled due to empty fields - Bug 1483412");
|
||||
ok(form.saveButton.disabled,
|
||||
"Save button should be disabled due to empty fields");
|
||||
fillField(form.form.querySelector("#tel"), "+15555555555");
|
||||
ok(!form.saveButton.disabled, "Save button should be enabled with all required fields filled");
|
||||
|
||||
|
|
|
@ -451,6 +451,19 @@ this.FormAutofillUtils = {
|
|||
return this._collators[country];
|
||||
},
|
||||
|
||||
// Based on the list of fields abbreviations in
|
||||
// https://github.com/googlei18n/libaddressinput/wiki/AddressValidationMetadata
|
||||
FIELDS_LOOKUP: {
|
||||
N: "name",
|
||||
O: "organization",
|
||||
A: "street-address",
|
||||
S: "address-level1",
|
||||
C: "address-level2",
|
||||
D: "address-level3",
|
||||
Z: "postal-code",
|
||||
n: "newLine",
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse a country address format string and outputs an array of fields.
|
||||
* Spaces, commas, and other literals are ignored in this implementation.
|
||||
|
@ -468,22 +481,10 @@ this.FormAutofillUtils = {
|
|||
if (!fmt) {
|
||||
throw new Error("fmt string is missing.");
|
||||
}
|
||||
// Based on the list of fields abbreviations in
|
||||
// https://github.com/googlei18n/libaddressinput/wiki/AddressValidationMetadata
|
||||
const fieldsLookup = {
|
||||
N: "name",
|
||||
O: "organization",
|
||||
A: "street-address",
|
||||
S: "address-level1",
|
||||
C: "address-level2",
|
||||
D: "address-level3",
|
||||
Z: "postal-code",
|
||||
n: "newLine",
|
||||
};
|
||||
|
||||
return fmt.match(/%[^%]/g).reduce((parsed, part) => {
|
||||
// Take the first letter of each segment and try to identify it
|
||||
let fieldId = fieldsLookup[part[1]];
|
||||
let fieldId = this.FIELDS_LOOKUP[part[1]];
|
||||
// Early return if cannot identify part.
|
||||
if (!fieldId) {
|
||||
return parsed;
|
||||
|
@ -500,6 +501,23 @@ this.FormAutofillUtils = {
|
|||
}, []);
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse a require string and outputs an array of fields.
|
||||
* Spaces, commas, and other literals are ignored in this implementation.
|
||||
* For example, a require string "ACS" should return:
|
||||
* ["street-address", "address-level2", "address-level1"]
|
||||
*
|
||||
* @param {string} requireString Country address require string
|
||||
* @returns {array<string>} List of fields
|
||||
*/
|
||||
parseRequireString(requireString) {
|
||||
if (!requireString) {
|
||||
throw new Error("requireString string is missing.");
|
||||
}
|
||||
|
||||
return requireString.split("").map(fieldId => this.FIELDS_LOOKUP[fieldId]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Use alternative country name list to identify a country code from a
|
||||
* specified country name.
|
||||
|
@ -808,16 +826,15 @@ this.FormAutofillUtils = {
|
|||
getFormFormat(country) {
|
||||
const dataset = this.getCountryAddressData(country);
|
||||
return {
|
||||
// Phillipines doesn't specify a sublocality_name_type but
|
||||
// has one referenced in their fmt value.
|
||||
// When particular values are missing for a country, the
|
||||
// data/ZZ value should be used instead.
|
||||
addressLevel3Label: dataset.sublocality_name_type || "suburb",
|
||||
// Many locales don't specify a locality_name_type but
|
||||
// have one referenced in their fmt value.
|
||||
addressLevel2Label: dataset.locality_name_type || "city",
|
||||
addressLevel1Label: dataset.state_name_type || "province",
|
||||
postalCodeLabel: dataset.zip_name_type || "postalCode",
|
||||
fieldsOrder: this.parseAddressFormat(dataset.fmt || "%N%n%O%n%A%n%C, %S %Z"),
|
||||
fieldsOrder: this.parseAddressFormat(dataset.fmt || "%N%n%O%n%A%n%C"),
|
||||
postalCodePattern: dataset.zip,
|
||||
countryRequiredFields: this.parseRequireString(dataset.require || "AC"),
|
||||
};
|
||||
},
|
||||
|
||||
|
|
|
@ -18,6 +18,8 @@ ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
|||
ChromeUtils.defineModuleGetter(this, "AppConstants", "resource://gre/modules/AppConstants.jsm");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "nativeOSKeyStore",
|
||||
"@mozilla.org/security/oskeystore;1", Ci.nsIOSKeyStore);
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "osReauthenticator",
|
||||
"@mozilla.org/security/osreauthenticator;1", Ci.nsIOSReauthenticator);
|
||||
|
||||
// Skip reauth during tests, only works in non-official builds.
|
||||
const TEST_ONLY_REAUTH = "extensions.formautofill.osKeyStore.unofficialBuildOnlyLogin";
|
||||
|
@ -57,30 +59,20 @@ var OSKeyStore = {
|
|||
},
|
||||
|
||||
/**
|
||||
* If the test pref exist and applicable,
|
||||
* this method will dispatch a observer message and return
|
||||
* to simulate successful reauth, or throw to simulate
|
||||
* failed reauth.
|
||||
* If the test pref exists, this method will dispatch a observer message and
|
||||
* resolves to simulate successful reauth, or rejects to simulate failed reauth.
|
||||
*
|
||||
* @returns {boolean} True when reauth should NOT be skipped,
|
||||
* false when reauth has been skipped.
|
||||
* @throws If it needs to simulate reauth login failure.
|
||||
* @returns {Promise<undefined>} Resolves when sucessful login, rejects when
|
||||
* login fails.
|
||||
*/
|
||||
_maybeSkipReauthForTest() {
|
||||
// Don't take test reauth pref in the following configurations.
|
||||
if (nativeOSKeyStore.isNSSKeyStore ||
|
||||
AppConstants.MOZILLA_OFFICIAL ||
|
||||
!this._testReauth) {
|
||||
return true;
|
||||
}
|
||||
|
||||
async _reauthInTests() {
|
||||
// Skip this reauth because there is no way to mock the
|
||||
// native dialog in the testing environment, for now.
|
||||
log.debug("_ensureReauth: _testReauth: ", this._testReauth);
|
||||
switch (this._testReauth) {
|
||||
case "pass":
|
||||
Services.obs.notifyObservers(null, "oskeystore-testonly-reauth", "pass");
|
||||
return false;
|
||||
break;
|
||||
case "cancel":
|
||||
Services.obs.notifyObservers(null, "oskeystore-testonly-reauth", "cancel");
|
||||
throw new Components.Exception("Simulating user cancelling login dialog", Cr.NS_ERROR_FAILURE);
|
||||
|
@ -99,11 +91,13 @@ var OSKeyStore = {
|
|||
* This is why there aren't an |await| in the method. The method is marked as
|
||||
* |async| to communicate that it's async.
|
||||
*
|
||||
* @param {boolean} reauth Prompt the login dialog no matter it's logged in
|
||||
* or not if it's set to true.
|
||||
* @returns {Promise<boolean>} True if it's logged in or no password is set
|
||||
* and false if it's still not logged in (prompt
|
||||
* canceled or other error).
|
||||
* @param {boolean|string} reauth If it's set to true or a string, prompt
|
||||
* the reauth login dialog.
|
||||
* The string will be shown on the native OS
|
||||
* login dialog.
|
||||
* @returns {Promise<boolean>} True if it's logged in or no password is set
|
||||
* and false if it's still not logged in (prompt
|
||||
* canceled or other error).
|
||||
*/
|
||||
async ensureLoggedIn(reauth = false) {
|
||||
if (this._pendingUnlockPromise) {
|
||||
|
@ -112,15 +106,30 @@ var OSKeyStore = {
|
|||
}
|
||||
log.debug("ensureLoggedIn: Creating new pending unlock promise. reauth: ", reauth);
|
||||
|
||||
// TODO: Implementing re-auth by passing this value to the native implementation
|
||||
// in some way. Set this to false for now to ignore the reauth request (bug 1429265).
|
||||
reauth = false;
|
||||
let unlockPromise;
|
||||
|
||||
let unlockPromise = Promise.resolve().then(async () => {
|
||||
if (reauth) {
|
||||
reauth = this._maybeSkipReauthForTest();
|
||||
}
|
||||
// Decides who should handle reauth
|
||||
if (typeof reauth == "boolean" && !reauth) {
|
||||
unlockPromise = Promise.resolve();
|
||||
} else if (!AppConstants.MOZILLA_OFFICIAL && this._testReauth) {
|
||||
unlockPromise = this._reauthInTests();
|
||||
} else if (AppConstants.platform == "win" ||
|
||||
AppConstants.platform == "macosx") {
|
||||
let reauthLabel = typeof reauth == "string" ? reauth : "";
|
||||
// On Windows, this promise rejects when the user cancels login dialog, see bug 1502121.
|
||||
// On macOS this resolves to false, so we would need to check it.
|
||||
unlockPromise = osReauthenticator.asyncReauthenticateUser(reauthLabel)
|
||||
.then(reauthResult => {
|
||||
if (typeof reauthResult == "boolean" && !reauthResult) {
|
||||
throw new Components.Exception("User canceled OS reauth entry", Cr.NS_ERROR_FAILURE);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
log.debug("ensureLoggedIn: Skipping reauth on unsupported platforms");
|
||||
unlockPromise = Promise.resolve();
|
||||
}
|
||||
|
||||
unlockPromise = unlockPromise.then(async () => {
|
||||
if (!await nativeOSKeyStore.asyncSecretAvailable(this.STORE_LABEL)) {
|
||||
log.debug("ensureLoggedIn: Secret unavailable, attempt to generate new secret.");
|
||||
let recoveryPhrase = await nativeOSKeyStore.asyncGenerateSecret(this.STORE_LABEL);
|
||||
|
@ -170,10 +179,12 @@ var OSKeyStore = {
|
|||
* don't show that dialog), apart from other errors (e.g., gracefully
|
||||
* recover from that and still shows the dialog.)
|
||||
*
|
||||
* @param {string} cipherText Encrypted string including the algorithm details.
|
||||
* @param {boolean} reauth True if we want to force the prompt to show up
|
||||
* even if the user is already logged in.
|
||||
* @returns {Promise<string>} resolves to the decrypted string, or rejects otherwise.
|
||||
* @param {string} cipherText Encrypted string including the algorithm details.
|
||||
* @param {boolean|string} reauth If it's set to true or a string, prompt
|
||||
* the reauth login dialog.
|
||||
* The string may be shown on the native OS
|
||||
* login dialog.
|
||||
* @returns {Promise<string>} resolves to the decrypted string, or rejects otherwise.
|
||||
*/
|
||||
async decrypt(cipherText, reauth = false) {
|
||||
if (!await this.ensureLoggedIn(reauth)) {
|
||||
|
@ -230,14 +241,6 @@ var OSKeyStore = {
|
|||
async cleanup() {
|
||||
return nativeOSKeyStore.asyncDeleteSecret(this.STORE_LABEL);
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if the implementation is using the NSS key store.
|
||||
* If so, tests will be able to handle the reauth dialog.
|
||||
*/
|
||||
get isNSSKeyStore() {
|
||||
return nativeOSKeyStore.isNSSKeyStore;
|
||||
},
|
||||
};
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "log", () => {
|
||||
|
|
|
@ -213,14 +213,22 @@ class EditAddress extends EditAutofillForm {
|
|||
postalCodeLabel,
|
||||
fieldsOrder: mailingFieldsOrder,
|
||||
postalCodePattern,
|
||||
countryRequiredFields,
|
||||
} = this.getFormFormat(country);
|
||||
this._elements.addressLevel3Label.dataset.localization = addressLevel3Label;
|
||||
this._elements.addressLevel2Label.dataset.localization = addressLevel2Label;
|
||||
this._elements.addressLevel1Label.dataset.localization = addressLevel1Label;
|
||||
this._elements.postalCodeLabel.dataset.localization = postalCodeLabel;
|
||||
let addressFields = this._elements.form.dataset.addressFields;
|
||||
let extraRequiredFields = this._elements.form.dataset.extraRequiredFields;
|
||||
let fieldClasses = EditAddress.computeVisibleFields(mailingFieldsOrder, addressFields);
|
||||
this.arrangeFields(fieldClasses);
|
||||
let requiredFields = new Set(countryRequiredFields);
|
||||
if (extraRequiredFields) {
|
||||
for (let extraRequiredField of extraRequiredFields.trim().split(/\s+/)) {
|
||||
requiredFields.add(extraRequiredField);
|
||||
}
|
||||
}
|
||||
this.arrangeFields(fieldClasses, requiredFields);
|
||||
this.updatePostalCodeValidation(postalCodePattern);
|
||||
}
|
||||
|
||||
|
@ -228,8 +236,9 @@ class EditAddress extends EditAutofillForm {
|
|||
* Update address field visibility and order based on libaddressinput data.
|
||||
*
|
||||
* @param {object[]} fieldsOrder array of objects with `fieldId` and optional `newLine` properties
|
||||
* @param {Set} requiredFields Set of `fieldId` strings that mark which fields are required
|
||||
*/
|
||||
arrangeFields(fieldsOrder) {
|
||||
arrangeFields(fieldsOrder, requiredFields) {
|
||||
let fields = [
|
||||
"name",
|
||||
"organization",
|
||||
|
@ -245,9 +254,18 @@ class EditAddress extends EditAutofillForm {
|
|||
let inputs = [];
|
||||
for (let i = 0; i < fieldsOrder.length; i++) {
|
||||
let {fieldId, newLine} = fieldsOrder[i];
|
||||
|
||||
let container = this._elements.form.querySelector(`#${fieldId}-container`);
|
||||
let containerInputs = [...container.querySelectorAll("input, textarea, select")];
|
||||
containerInputs.forEach(function(input) { input.disabled = false; });
|
||||
containerInputs.forEach(function(input) {
|
||||
input.disabled = false;
|
||||
// libaddressinput doesn't list 'country' or 'name' as required.
|
||||
// The additional-name field should never get marked as required.
|
||||
input.required = (fieldId == "country" ||
|
||||
fieldId == "name" ||
|
||||
requiredFields.has(fieldId)) &&
|
||||
input.id != "additional-name";
|
||||
});
|
||||
inputs.push(...containerInputs);
|
||||
container.style.display = "flex";
|
||||
container.style.order = i;
|
||||
|
|
|
@ -41,23 +41,23 @@
|
|||
<span data-localization="organization2" class="label-text"/>
|
||||
</label>
|
||||
<label id="street-address-container" class="container">
|
||||
<textarea id="street-address" rows="3" required="required"/>
|
||||
<textarea id="street-address" rows="3"/>
|
||||
<span data-localization="streetAddress" class="label-text"/>
|
||||
</label>
|
||||
<label id="address-level3-container" class="container">
|
||||
<input id="address-level3" type="text" required="required"/>
|
||||
<input id="address-level3" type="text"/>
|
||||
<span class="label-text"/>
|
||||
</label>
|
||||
<label id="address-level2-container" class="container">
|
||||
<input id="address-level2" type="text" required="required"/>
|
||||
<input id="address-level2" type="text"/>
|
||||
<span class="label-text"/>
|
||||
</label>
|
||||
<label id="address-level1-container" class="container">
|
||||
<input id="address-level1" type="text" required="required"/>
|
||||
<input id="address-level1" type="text"/>
|
||||
<span class="label-text"/>
|
||||
</label>
|
||||
<label id="postal-code-container" class="container">
|
||||
<input id="postal-code" type="text" required="required"/>
|
||||
<input id="postal-code" type="text"/>
|
||||
<span class="label-text"/>
|
||||
</label>
|
||||
<label id="country-container" class="container">
|
||||
|
|
|
@ -16,7 +16,6 @@ skip-if = (verify && (os == 'win' || os == 'mac'))
|
|||
[browser_creditCard_doorhanger.js]
|
||||
skip-if = (os == "linux") || (os == "mac" && debug) || (os == "win") # bug 1425884
|
||||
[browser_creditCard_fill_cancel_login.js]
|
||||
skip-if = true # Re-auth is not implemented, cannot cancel OS key store login (bug 1429265)
|
||||
[browser_dropdown_layout.js]
|
||||
[browser_editAddressDialog.js]
|
||||
[browser_editCreditCardDialog.js]
|
||||
|
|
|
@ -153,19 +153,15 @@ add_task(async function test_hasEditLoginPrompt() {
|
|||
let selRecords = win.document.querySelector(TEST_SELECTORS.selRecords);
|
||||
let btnRemove = win.document.querySelector(TEST_SELECTORS.btnRemove);
|
||||
let btnAdd = win.document.querySelector(TEST_SELECTORS.btnAdd);
|
||||
// let btnEdit = win.document.querySelector(TEST_SELECTORS.btnEdit);
|
||||
let btnEdit = win.document.querySelector(TEST_SELECTORS.btnEdit);
|
||||
|
||||
EventUtils.synthesizeMouseAtCenter(selRecords.children[0], {}, win);
|
||||
|
||||
// Login dialog should show when trying to edit a credit card record.
|
||||
// TODO: test disabled because re-auth is not implemented yet (bug 1429265).
|
||||
/*
|
||||
let osKeyStoreLoginShown = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(); // cancel
|
||||
EventUtils.synthesizeMouseAtCenter(btnEdit, {}, win);
|
||||
await osKeyStoreLoginShown;
|
||||
await new Promise(resolve => waitForFocus(resolve, win));
|
||||
await new Promise(resolve => executeSoon(resolve));
|
||||
*/
|
||||
|
||||
// Login is not required for removing credit cards.
|
||||
EventUtils.synthesizeMouseAtCenter(btnRemove, {}, win);
|
||||
|
|
|
@ -8,61 +8,35 @@ var EXPORTED_SYMBOLS = [
|
|||
];
|
||||
|
||||
ChromeUtils.import("resource://formautofill/OSKeyStore.jsm", this);
|
||||
// TODO: Consider AppConstants.MOZILLA_OFFICIAL to decide if we could test re-auth (bug 1429265).
|
||||
/*
|
||||
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
*/
|
||||
ChromeUtils.import("resource://testing-common/LoginTestUtils.jsm", this);
|
||||
ChromeUtils.import("resource://testing-common/TestUtils.jsm");
|
||||
|
||||
var OSKeyStoreTestUtils = {
|
||||
/*
|
||||
TEST_ONLY_REAUTH: "extensions.formautofill.osKeyStore.unofficialBuildOnlyLogin",
|
||||
*/
|
||||
|
||||
setup() {
|
||||
// TODO: run tests with master password enabled to ensure NSS-implemented
|
||||
// key store prompts on re-auth (bug 1429265)
|
||||
/*
|
||||
LoginTestUtils.masterPassword.enable();
|
||||
*/
|
||||
|
||||
this.ORIGINAL_STORE_LABEL = OSKeyStore.STORE_LABEL;
|
||||
OSKeyStore.STORE_LABEL = "test-" + Math.random().toString(36).substr(2);
|
||||
},
|
||||
|
||||
async cleanup() {
|
||||
// TODO: run tests with master password enabled to ensure NSS-implemented
|
||||
// key store prompts on re-auth (bug 1429265)
|
||||
/*
|
||||
LoginTestUtils.masterPassword.disable();
|
||||
*/
|
||||
|
||||
await OSKeyStore.cleanup();
|
||||
OSKeyStore.STORE_LABEL = this.ORIGINAL_STORE_LABEL;
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks whether or not the test can be run by bypassing
|
||||
* the OS login dialog. We do not want the user to be able to
|
||||
* do so with in official builds.
|
||||
* @returns {boolean} True if the test can be preformed.
|
||||
*/
|
||||
canTestOSKeyStoreLogin() {
|
||||
// TODO: return true based on whether or not we could test the prompt on
|
||||
// the platform (bug 1429265).
|
||||
/*
|
||||
return OSKeyStore.isNSSKeyStore || !AppConstants.MOZILLA_OFFICIAL;
|
||||
*/
|
||||
return true;
|
||||
return !AppConstants.MOZILLA_OFFICIAL;
|
||||
},
|
||||
|
||||
// Wait for the master password dialog to popup and enter the password to log in
|
||||
// if "login" is "true" or dismiss it directly if otherwise.
|
||||
// Wait for the observer message that simulates login success of failure.
|
||||
async waitForOSKeyStoreLogin(login = false) {
|
||||
// TODO: Always resolves for now, because we are skipping re-auth on all
|
||||
// platforms (bug 1429265).
|
||||
/*
|
||||
if (OSKeyStore.isNSSKeyStore) {
|
||||
await this.waitForMasterPasswordDialog(login);
|
||||
return;
|
||||
}
|
||||
|
||||
const str = login ? "pass" : "cancel";
|
||||
|
||||
Services.prefs.setStringPref(this.TEST_ONLY_REAUTH, str);
|
||||
|
@ -71,23 +45,5 @@ var OSKeyStoreTestUtils = {
|
|||
(subject, data) => data == str);
|
||||
|
||||
Services.prefs.setStringPref(this.TEST_ONLY_REAUTH, "");
|
||||
*/
|
||||
},
|
||||
|
||||
async waitForMasterPasswordDialog(login = false) {
|
||||
let [subject] = await TestUtils.topicObserved("common-dialog-loaded");
|
||||
|
||||
let dialog = subject.Dialog;
|
||||
if (dialog.args.title !== "Password Required") {
|
||||
throw new Error("Incorrect master password dialog title");
|
||||
}
|
||||
|
||||
if (login) {
|
||||
dialog.ui.password1Textbox.value = LoginTestUtils.masterPassword.masterPassword;
|
||||
dialog.ui.button0.click();
|
||||
} else {
|
||||
dialog.ui.button1.click();
|
||||
}
|
||||
await TestUtils.waitForTick();
|
||||
},
|
||||
};
|
||||
|
|
|
@ -4,10 +4,8 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
ChromeUtils.import("resource://formautofill/FormAutofillUtils.jsm");
|
||||
ChromeUtils.import("resource://formautofill/OSKeyStore.jsm");
|
||||
ChromeUtils.import("resource://testing-common/OSKeyStoreTestUtils.jsm");
|
||||
|
||||
let {formAutofillStorage} = ChromeUtils.import("resource://formautofill/FormAutofillStorage.jsm", {});
|
||||
|
@ -226,7 +224,7 @@ addMessageListener("FormAutofillTest:CleanUpCreditCards", (msg) => {
|
|||
|
||||
addMessageListener("FormAutofillTest:CanTestOSKeyStoreLogin", (msg) => {
|
||||
sendAsyncMessage("FormAutofillTest:CanTestOSKeyStoreLoginResult",
|
||||
{canTest: OSKeyStore.isNSSKeyStore || !AppConstants.MOZILLA_OFFICIAL});
|
||||
{canTest: OSKeyStoreTestUtils.canTestOSKeyStoreLogin()});
|
||||
});
|
||||
|
||||
addMessageListener("FormAutofillTest:OSKeyStoreLogin", async (msg) => {
|
||||
|
|
|
@ -4,9 +4,6 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
|
||||
ChromeUtils.import("resource://testing-common/MockRegistrar.jsm");
|
||||
|
||||
let OSKeyStore;
|
||||
add_task(async function setup() {
|
||||
({OSKeyStore} = ChromeUtils.import("resource://formautofill/OSKeyStore.jsm", {}));
|
||||
|
@ -15,57 +12,6 @@ add_task(async function setup() {
|
|||
// Ensure that the appropriate initialization has happened.
|
||||
do_get_profile();
|
||||
|
||||
// For NSS key store, mocking out the dialog and control it from here.
|
||||
let gMockPrompter = {
|
||||
passwordToTry: "hunter2",
|
||||
resolve: null,
|
||||
login: undefined,
|
||||
|
||||
// This intentionally does not use arrow function syntax to avoid an issue
|
||||
// where in the context of the arrow function, |this != gMockPrompter| due to
|
||||
// how objects get wrapped when going across xpcom boundaries.
|
||||
promptPassword(dialogTitle, text, password, checkMsg, checkValue) {
|
||||
equal(text,
|
||||
"Please enter your master password.",
|
||||
"password prompt text should be as expected");
|
||||
equal(checkMsg, null, "checkMsg should be null");
|
||||
if (this.login) {
|
||||
password.value = this.passwordToTry;
|
||||
}
|
||||
this.resolve();
|
||||
this.resolve = null;
|
||||
|
||||
return this.login;
|
||||
},
|
||||
|
||||
QueryInterface: ChromeUtils.generateQI([Ci.nsIPrompt]),
|
||||
};
|
||||
|
||||
// Mock nsIWindowWatcher. PSM calls getNewPrompter on this to get an nsIPrompt
|
||||
// to call promptPassword. We return the mock one, above.
|
||||
let gWindowWatcher = {
|
||||
getNewPrompter: () => gMockPrompter,
|
||||
QueryInterface: ChromeUtils.generateQI([Ci.nsIWindowWatcher]),
|
||||
};
|
||||
|
||||
let nssToken;
|
||||
|
||||
const TEST_ONLY_REAUTH = "extensions.formautofill.osKeyStore.unofficialBuildOnlyLogin";
|
||||
|
||||
async function waitForReauth(login = false) {
|
||||
if (OSKeyStore.isNSSKeyStore) {
|
||||
gMockPrompter.login = login;
|
||||
await new Promise(resolve => { gMockPrompter.resolve = resolve; });
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let value = login ? "pass" : "cancel";
|
||||
Services.prefs.setStringPref(TEST_ONLY_REAUTH, value);
|
||||
await TestUtils.topicObserved("oskeystore-testonly-reauth",
|
||||
(subject, data) => data == value);
|
||||
}
|
||||
|
||||
const testText = "test string";
|
||||
let cipherText;
|
||||
|
||||
|
@ -79,34 +25,15 @@ add_task(async function test_encrypt_decrypt() {
|
|||
Assert.equal(testText, plainText);
|
||||
});
|
||||
|
||||
// TODO: skipped because re-auth is not implemented (bug 1429265).
|
||||
add_task(async function test_reauth() {
|
||||
let canTest = OSKeyStore.isNSSKeyStore || !AppConstants.MOZILLA_OFFICIAL;
|
||||
let canTest = OSKeyStoreTestUtils.canTestOSKeyStoreLogin();
|
||||
if (!canTest) {
|
||||
todo_check_false(canTest,
|
||||
todo_check_true(canTest,
|
||||
"test_reauth: Cannot test OS key store login on official builds.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (OSKeyStore.isNSSKeyStore) {
|
||||
let windowWatcherCID;
|
||||
windowWatcherCID =
|
||||
MockRegistrar.register("@mozilla.org/embedcomp/window-watcher;1",
|
||||
gWindowWatcher);
|
||||
registerCleanupFunction(() => {
|
||||
MockRegistrar.unregister(windowWatcherCID);
|
||||
});
|
||||
|
||||
// If we use the NSS key store implementation test that everything works
|
||||
// when a master password is set.
|
||||
// Set an initial password.
|
||||
let tokenDB = Cc["@mozilla.org/security/pk11tokendb;1"]
|
||||
.getService(Ci.nsIPK11TokenDB);
|
||||
nssToken = tokenDB.getInternalKeyToken();
|
||||
nssToken.initPassword("hunter2");
|
||||
}
|
||||
|
||||
let reauthObserved = waitForReauth(false);
|
||||
let reauthObserved = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(false);
|
||||
await new Promise(resolve => TestUtils.executeSoon(resolve));
|
||||
try {
|
||||
await OSKeyStore.decrypt(cipherText, true);
|
||||
|
@ -117,22 +44,22 @@ add_task(async function test_reauth() {
|
|||
}
|
||||
await reauthObserved;
|
||||
|
||||
reauthObserved = waitForReauth(false);
|
||||
reauthObserved = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(false);
|
||||
await new Promise(resolve => TestUtils.executeSoon(resolve));
|
||||
Assert.equal(await OSKeyStore.ensureLoggedIn(true), false, "Reauth cancelled.");
|
||||
await reauthObserved;
|
||||
|
||||
reauthObserved = waitForReauth(true);
|
||||
reauthObserved = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true);
|
||||
await new Promise(resolve => TestUtils.executeSoon(resolve));
|
||||
let plainText2 = await OSKeyStore.decrypt(cipherText, true);
|
||||
await reauthObserved;
|
||||
Assert.equal(testText, plainText2);
|
||||
|
||||
reauthObserved = waitForReauth(true);
|
||||
reauthObserved = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true);
|
||||
await new Promise(resolve => TestUtils.executeSoon(resolve));
|
||||
Assert.equal(await OSKeyStore.ensureLoggedIn(true), true, "Reauth logged in.");
|
||||
await reauthObserved;
|
||||
}).skip();
|
||||
});
|
||||
|
||||
add_task(async function test_decryption_failure() {
|
||||
try {
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ComputedTimingFunction.h"
|
||||
#include "mozilla/ServoBindings.h"
|
||||
#include "nsAlgorithm.h" // For clamped()
|
||||
#include "nsStyleUtil.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
@ -178,24 +178,32 @@ ComputedTimingFunction::Compare(const ComputedTimingFunction& aRhs) const
|
|||
void
|
||||
ComputedTimingFunction::AppendToString(nsAString& aResult) const
|
||||
{
|
||||
nsTimingFunction timing;
|
||||
switch (mType) {
|
||||
case Type::CubicBezier:
|
||||
nsStyleUtil::AppendCubicBezierTimingFunction(mTimingFunction.X1(),
|
||||
mTimingFunction.Y1(),
|
||||
mTimingFunction.X2(),
|
||||
mTimingFunction.Y2(),
|
||||
aResult);
|
||||
timing.mTiming = StyleComputedTimingFunction::CubicBezier(
|
||||
mTimingFunction.X1(),
|
||||
mTimingFunction.Y1(),
|
||||
mTimingFunction.X2(),
|
||||
mTimingFunction.Y2());
|
||||
break;
|
||||
case Type::Step:
|
||||
nsStyleUtil::AppendStepsTimingFunction(mSteps.mSteps,
|
||||
mSteps.mPos,
|
||||
aResult);
|
||||
timing.mTiming = StyleComputedTimingFunction::Steps(
|
||||
mSteps.mSteps,
|
||||
mSteps.mPos);
|
||||
break;
|
||||
case Type::Linear:
|
||||
case Type::Ease:
|
||||
case Type::EaseIn:
|
||||
case Type::EaseOut:
|
||||
case Type::EaseInOut:
|
||||
timing.mTiming = StyleComputedTimingFunction::Keyword(
|
||||
static_cast<StyleTimingKeyword>(mType));
|
||||
break;
|
||||
default:
|
||||
nsStyleUtil::AppendCubicBezierKeywordTimingFunction(
|
||||
StyleTimingKeyword(uint8_t(mType)), aResult);
|
||||
break;
|
||||
MOZ_ASSERT_UNREACHABLE("Unsupported timing type");
|
||||
}
|
||||
Servo_SerializeEasing(&timing, &aResult);
|
||||
}
|
||||
|
||||
/* static */ int32_t
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
// We clamp +infinity or -inifinity value in floating point to
|
||||
// maximum floating point value or -maxinum floating point value.
|
||||
const max_float = 3.40282e+38;
|
||||
const max_float = '3.40282e38';
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
@ -37,12 +37,12 @@ test(function(t) {
|
|||
|
||||
anim.effect.updateTiming({ easing: 'cubic-bezier(0, -1e+39, 0, 0)' });
|
||||
assert_equals(anim.effect.getComputedTiming().easing,
|
||||
'cubic-bezier(0, ' + -max_float + ', 0, 0)',
|
||||
'cubic-bezier(0, ' + '-' + max_float + ', 0, 0)',
|
||||
'y1 control point for effect easing is out of lower boundary');
|
||||
|
||||
anim.effect.updateTiming({ easing: 'cubic-bezier(0, 0, 0, -1e+39)' });
|
||||
assert_equals(anim.effect.getComputedTiming().easing,
|
||||
'cubic-bezier(0, 0, 0, ' + -max_float + ')',
|
||||
'cubic-bezier(0, 0, 0, ' + '-' + max_float + ')',
|
||||
'y2 control point for effect easing is out of lower boundary');
|
||||
|
||||
}, 'Clamp y1 and y2 control point out of boundaries for effect easing' );
|
||||
|
@ -63,12 +63,12 @@ test(function(t) {
|
|||
|
||||
anim.effect.setKeyframes([ { easing: 'cubic-bezier(0, -1e+39, 0, 0)' }]);
|
||||
assert_equals(anim.effect.getKeyframes()[0].easing,
|
||||
'cubic-bezier(0, ' + -max_float + ', 0, 0)',
|
||||
'cubic-bezier(0, ' + '-' + max_float + ', 0, 0)',
|
||||
'y1 control point for keyframe easing is out of lower boundary');
|
||||
|
||||
anim.effect.setKeyframes([ { easing: 'cubic-bezier(0, 0, 0, -1e+39)' }]);
|
||||
assert_equals(anim.effect.getKeyframes()[0].easing,
|
||||
'cubic-bezier(0, 0, 0, ' + -max_float + ')',
|
||||
'cubic-bezier(0, 0, 0, ' + '-' + max_float + ')',
|
||||
'y2 control point for keyframe easing is out of lower boundary');
|
||||
|
||||
}, 'Clamp y1 and y2 control point out of boundaries for keyframe easing' );
|
||||
|
@ -89,12 +89,12 @@ test(function(t) {
|
|||
|
||||
div.style.animation = 'anim 100s cubic-bezier(0, -1e+39, 0, 0)';
|
||||
assert_equals(div.getAnimations()[0].effect.getKeyframes()[0].easing,
|
||||
'cubic-bezier(0, ' + -max_float + ', 0, 0)',
|
||||
'cubic-bezier(0, ' + '-' + max_float + ', 0, 0)',
|
||||
'y1 control point for CSS animation is out of lower boundary');
|
||||
|
||||
div.style.animation = 'anim 100s cubic-bezier(0, 0, 0, -1e+39)';
|
||||
assert_equals(div.getAnimations()[0].effect.getKeyframes()[0].easing,
|
||||
'cubic-bezier(0, 0, 0, ' + -max_float + ')',
|
||||
'cubic-bezier(0, 0, 0, ' + '-' + max_float + ')',
|
||||
'y2 control point for CSS animation is out of lower boundary');
|
||||
|
||||
}, 'Clamp y1 and y2 control point out of boundaries for CSS animation' );
|
||||
|
@ -124,7 +124,7 @@ test(function(t) {
|
|||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '0px';
|
||||
assert_equals(div.getAnimations()[0].effect.getKeyframes()[0].easing,
|
||||
'cubic-bezier(0, ' + -max_float + ', 0, 0)',
|
||||
'cubic-bezier(0, ' + '-' + max_float + ', 0, 0)',
|
||||
'y1 control point for CSS transition on lower boundary');
|
||||
div.style.transition = '';
|
||||
div.style.marginLeft = '';
|
||||
|
@ -133,7 +133,7 @@ test(function(t) {
|
|||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '0px';
|
||||
assert_equals(div.getAnimations()[0].effect.getKeyframes()[0].easing,
|
||||
'cubic-bezier(0, 0, 0, ' + -max_float + ')',
|
||||
'cubic-bezier(0, 0, 0, ' + '-' + max_float + ')',
|
||||
'y2 control point for CSS transition on lower boundary');
|
||||
|
||||
}, 'Clamp y1 and y2 control point out of boundaries for CSS transition' );
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width; initial-scale=1.0">
|
||||
<title>Dragging the mouse on a scrollbar for a scrollframe inside nested transforms</title>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
function* test(testDriver) {
|
||||
var scrollableDiv = document.getElementById('scrollable');
|
||||
scrollableDiv.addEventListener('scroll', () => setTimeout(testDriver, 0), {once: true});
|
||||
|
||||
// Scroll down a small amount (10px). The bug in this case is that the
|
||||
// scrollthumb "jumps" by an additional 40 pixels (height of the "gap" div)
|
||||
// and the scrollframe scrolls by a corresponding amount. So after doing this
|
||||
// drag we check the scroll position to make sure it hasn't scrolled by
|
||||
// too much.
|
||||
// Given the scrollable height of 2000px and scrollframe height of 400px,
|
||||
// the scrollthumb should be approximately 80px tall, and dragging it 10px
|
||||
// should scroll approximately 50 pixels. If the bug manifests, it will get
|
||||
// dragged 50px and scroll approximately 250px.
|
||||
var dragFinisher = yield* dragVerticalScrollbar(scrollableDiv, testDriver, 10, 10);
|
||||
if (!dragFinisher) {
|
||||
ok(true, "No scrollbar, can't do this test");
|
||||
return;
|
||||
}
|
||||
|
||||
// the events above might be stuck in APZ input queue for a bit until the
|
||||
// layer is activated, so we wait here until the scroll event listener is
|
||||
// triggered.
|
||||
yield;
|
||||
|
||||
yield* dragFinisher();
|
||||
|
||||
// Flush everything just to be safe
|
||||
yield flushApzRepaints(testDriver);
|
||||
|
||||
// In this case we just want to make sure the scroll position moved from 0
|
||||
// which indicates the thumb dragging worked properly.
|
||||
ok(scrollableDiv.scrollTop < 100, "Scrollbar drag resulted in a scroll position of " + scrollableDiv.scrollTop);
|
||||
}
|
||||
|
||||
waitUntilApzStable()
|
||||
.then(runContinuation(test))
|
||||
.then(subtestDone);
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="gap" style="min-height: 40px"></div>
|
||||
<div style="height: 400px; transform: translateZ(0)">
|
||||
<div style="height: 100%; overflow-x: auto; overflow-y: hidden; transform: translateZ(0)">
|
||||
<div id="scrollable" style="display: inline-block; height: 100%; overflow-y: auto; transform: translateZ(0)">
|
||||
<div style="min-height: 2000px">Yay text</div>
|
||||
</div>
|
||||
<div style="display: inline-block; width: 2000px; height: 100%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,62 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width; initial-scale=1.0">
|
||||
<title>Dragging the mouse on a scrollbar for a scrollframe inside nested transforms</title>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
function* test(testDriver) {
|
||||
var scrollableDiv = document.getElementById('scrollable');
|
||||
scrollableDiv.addEventListener('scroll', () => setTimeout(testDriver, 0), {once: true});
|
||||
|
||||
// Scroll down a small amount (10px). The bug in this case is that the
|
||||
// scrollthumb "jumps" by an additional 40 pixels (height of the "gap" div)
|
||||
// and the scrollframe scrolls by a corresponding amount. So after doing this
|
||||
// drag we check the scroll position to make sure it hasn't scrolled by
|
||||
// too much.
|
||||
// Given the scrollable height of 2000px and scrollframe height of 400px,
|
||||
// the scrollthumb should be approximately 80px tall, and dragging it 10px
|
||||
// should scroll approximately 50 pixels. If the bug manifests, it will get
|
||||
// dragged 50px and scroll approximately 250px.
|
||||
var dragFinisher = yield* dragVerticalScrollbar(scrollableDiv, testDriver, 10, 10);
|
||||
if (!dragFinisher) {
|
||||
ok(true, "No scrollbar, can't do this test");
|
||||
return;
|
||||
}
|
||||
|
||||
// the events above might be stuck in APZ input queue for a bit until the
|
||||
// layer is activated, so we wait here until the scroll event listener is
|
||||
// triggered.
|
||||
yield;
|
||||
|
||||
yield* dragFinisher();
|
||||
|
||||
// Flush everything just to be safe
|
||||
yield flushApzRepaints(testDriver);
|
||||
|
||||
// In this case we just want to make sure the scroll position moved from 0
|
||||
// which indicates the thumb dragging worked properly.
|
||||
ok(scrollableDiv.scrollTop < 100, "Scrollbar drag resulted in a scroll position of " + scrollableDiv.scrollTop);
|
||||
}
|
||||
|
||||
waitUntilApzStable()
|
||||
.then(runContinuation(test))
|
||||
.then(subtestDone);
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="gap" style="min-height: 40px"></div>
|
||||
<div style="height: 400px; transform: translateZ(0)">
|
||||
<div style="height: 100%; opacity: 0.9; will-change: opacity">
|
||||
<div id="scrollable" style="height: 100%; overflow-y: auto; transform: translateZ(0)">
|
||||
<div style="min-height: 2000px">Yay text</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -28,7 +28,10 @@ var subtests = [
|
|||
{'file': 'helper_bug1462961.html'},
|
||||
// Scrollbar dragging where we exercise the snapback behaviour by moving the
|
||||
// mouse away from the scrollbar during drag
|
||||
{'file': 'helper_scrollbar_snap_bug1501062.html'}
|
||||
{'file': 'helper_scrollbar_snap_bug1501062.html'},
|
||||
// Tests for scrollbar-dragging on scrollframes inside nested transforms
|
||||
{'file': 'helper_bug1490393.html'},
|
||||
{'file': 'helper_bug1490393-2.html'}
|
||||
];
|
||||
|
||||
if (isApzEnabled()) {
|
||||
|
|
|
@ -23,6 +23,7 @@ StackingContextHelper::StackingContextHelper()
|
|||
}
|
||||
|
||||
StackingContextHelper::StackingContextHelper(const StackingContextHelper& aParentSC,
|
||||
const ActiveScrolledRoot* aAsr,
|
||||
wr::DisplayListBuilder& aBuilder,
|
||||
const nsTArray<wr::WrFilterOp>& aFilters,
|
||||
const LayoutDeviceRect& aBounds,
|
||||
|
@ -80,6 +81,24 @@ StackingContextHelper::StackingContextHelper(const StackingContextHelper& aParen
|
|||
|
||||
mAffectsClipPositioning = mReferenceFrameId.isSome() ||
|
||||
(aBounds.TopLeft() != LayoutDevicePoint());
|
||||
|
||||
// If the parent stacking context has a deferred transform item, inherit it
|
||||
// into this stacking context, as long as the ASR hasn't changed. Refer to
|
||||
// the comments on StackingContextHelper::mDeferredTransformItem for an
|
||||
// explanation of what goes in these fields.
|
||||
if (aParentSC.mDeferredTransformItem &&
|
||||
aAsr == (*aParentSC.mDeferredTransformItem)->GetActiveScrolledRoot()) {
|
||||
if (mDeferredTransformItem) {
|
||||
// If we are deferring another transform, put the combined transform from
|
||||
// all the ancestor deferred items into mDeferredAncestorTransform
|
||||
mDeferredAncestorTransform = aParentSC.GetDeferredTransformMatrix();
|
||||
} else {
|
||||
// We are not deferring another transform, so we can just inherit the
|
||||
// parent stacking context's deferred data without any modification.
|
||||
mDeferredTransformItem = aParentSC.mDeferredTransformItem;
|
||||
mDeferredAncestorTransform = aParentSC.mDeferredAncestorTransform;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StackingContextHelper::~StackingContextHelper()
|
||||
|
@ -95,5 +114,24 @@ StackingContextHelper::GetDeferredTransformItem() const
|
|||
return mDeferredTransformItem;
|
||||
}
|
||||
|
||||
Maybe<gfx::Matrix4x4>
|
||||
StackingContextHelper::GetDeferredTransformMatrix() const
|
||||
{
|
||||
if (mDeferredTransformItem) {
|
||||
// See the comments on StackingContextHelper::mDeferredTransformItem for
|
||||
// an explanation of what's stored in mDeferredTransformItem and
|
||||
// mDeferredAncestorTransform. Here we need to return the combined transform
|
||||
// transform from all the deferred ancestors, including
|
||||
// mDeferredTransformItem.
|
||||
gfx::Matrix4x4 result = (*mDeferredTransformItem)->GetTransform().GetMatrix();
|
||||
if (mDeferredAncestorTransform) {
|
||||
result = *mDeferredAncestorTransform * result;
|
||||
}
|
||||
return Some(result);
|
||||
} else {
|
||||
return Nothing();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
class nsDisplayTransform;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
struct ActiveScrolledRoot;
|
||||
|
||||
namespace layers {
|
||||
|
||||
/**
|
||||
|
@ -26,6 +29,7 @@ class MOZ_RAII StackingContextHelper
|
|||
{
|
||||
public:
|
||||
StackingContextHelper(const StackingContextHelper& aParentSC,
|
||||
const ActiveScrolledRoot* aAsr,
|
||||
wr::DisplayListBuilder& aBuilder,
|
||||
const nsTArray<wr::WrFilterOp>& aFilters = nsTArray<wr::WrFilterOp>(),
|
||||
const LayoutDeviceRect& aBounds = LayoutDeviceRect(),
|
||||
|
@ -63,6 +67,7 @@ public:
|
|||
}
|
||||
|
||||
const Maybe<nsDisplayTransform*>& GetDeferredTransformItem() const;
|
||||
Maybe<gfx::Matrix4x4> GetDeferredTransformMatrix() const;
|
||||
|
||||
bool AffectsClipPositioning() const { return mAffectsClipPositioning; }
|
||||
Maybe<wr::WrClipId> ReferenceFrameId() const { return mReferenceFrameId; }
|
||||
|
@ -80,7 +85,47 @@ private:
|
|||
gfx::Matrix mSnappingSurfaceTransform;
|
||||
bool mAffectsClipPositioning;
|
||||
Maybe<wr::WrClipId> mReferenceFrameId;
|
||||
|
||||
// The deferred transform item is used when building the WebRenderScrollData
|
||||
// structure. The backstory is that APZ needs to know about transforms that
|
||||
// apply to the different APZC instances. Prior to bug 1423370, we would do
|
||||
// this by creating a new WebRenderLayerScrollData for each nsDisplayTransform
|
||||
// item we encountered. However, this was unnecessarily expensive because it
|
||||
// turned out a lot of nsDisplayTransform items didn't have new ASRs defined
|
||||
// as descendants, so we'd create the WebRenderLayerScrollData and send it
|
||||
// over to APZ even though the transform information was not needed in that
|
||||
// case.
|
||||
//
|
||||
// In bug 1423370 and friends, this was optimized by "deferring" a
|
||||
// nsDisplayTransform item when we encountered it during display list
|
||||
// traversal. If we found a descendant of that transform item that had a
|
||||
// new ASR or otherwise was "relevant to APZ", we would then pluck the
|
||||
// transform matrix off the deferred item and put it on the
|
||||
// WebRenderLayerScrollData instance created for that APZ-relevant descendant.
|
||||
//
|
||||
// One complication with this is if there are multiple nsDisplayTransform
|
||||
// items in the ancestor chain for the APZ-relevant item. As we traverse the
|
||||
// display list, we will defer the outermost nsDisplayTransform item, and when
|
||||
// we encounter the next one we will need to merge it with the already-
|
||||
// deferred one somehow. What we do in this case is have mDeferredTransformItem
|
||||
// always point to the "innermost" deferred transform item (i.e. the closest
|
||||
// ancestor nsDisplayTransform item of the item that created this
|
||||
// StackingContextHelper). And then we use mDeferredAncestorTransform to store
|
||||
// the product of all the other transforms that were deferred. As a result,
|
||||
// there is an invariant here that if mDeferredTransformItem is Nothing(),
|
||||
// mDeferredAncestorTransform will also be Nothing(). Note that we
|
||||
// can only do this if the nsDisplayTransform items share the same ASR. If
|
||||
// we are processing an nsDisplayTransform item with a different ASR than the
|
||||
// previously-deferred item, we assume that the previously-deferred transform
|
||||
// will get sent to APZ as part of a separate WebRenderLayerScrollData item,
|
||||
// and so we don't need to bother with any merging. (The merging probably
|
||||
// wouldn't even make sense because the coordinate spaces might be different
|
||||
// in the face of async scrolling). This behaviour of forcing a
|
||||
// WebRenderLayerScrollData item to be generated when the ASR changes is
|
||||
// implemented in WebRenderCommandBuilder::CreateWebRenderCommandsFromDisplayList.
|
||||
Maybe<nsDisplayTransform*> mDeferredTransformItem;
|
||||
Maybe<gfx::Matrix4x4> mDeferredAncestorTransform;
|
||||
|
||||
bool mIsPreserve3D;
|
||||
bool mRasterizeLocally;
|
||||
};
|
||||
|
|
|
@ -1313,7 +1313,7 @@ WebRenderCommandBuilder::BuildWebRenderCommands(wr::DisplayListBuilder& aBuilder
|
|||
mClipManager.BeginBuild(mManager, aBuilder);
|
||||
|
||||
{
|
||||
StackingContextHelper pageRootSc(sc, aBuilder, aFilters);
|
||||
StackingContextHelper pageRootSc(sc, nullptr, aBuilder, aFilters);
|
||||
if (ShouldDumpDisplayList(aDisplayListBuilder)) {
|
||||
mBuilderDumpIndex = aBuilder.Dump(mDumpIndent + 1, Some(mBuilderDumpIndex), Nothing());
|
||||
}
|
||||
|
@ -1423,6 +1423,20 @@ WebRenderCommandBuilder::CreateWebRenderCommandsFromDisplayList(nsDisplayList* a
|
|||
forceNewLayerData = true;
|
||||
}
|
||||
|
||||
// Refer to the comment on StackingContextHelper::mDeferredTransformItem
|
||||
// for an overview of what this is about. This bit of code applies to the
|
||||
// case where we are deferring a transform item, and we then need to defer
|
||||
// another transform with a different ASR. In such a case we cannot just
|
||||
// merge the deferred transforms, but need to force a new
|
||||
// WebRenderLayerScrollData item to flush the old deferred transform, so
|
||||
// that we can then start deferring the new one.
|
||||
if (!forceNewLayerData &&
|
||||
item->GetType() == DisplayItemType::TYPE_TRANSFORM &&
|
||||
aSc.GetDeferredTransformItem() &&
|
||||
(*aSc.GetDeferredTransformItem())->GetActiveScrolledRoot() != asr) {
|
||||
forceNewLayerData = true;
|
||||
}
|
||||
|
||||
// If we're going to create a new layer data for this item, stash the
|
||||
// ASR so that if we recurse into a sublist they will know where to stop
|
||||
// walking up their ASR chain when building scroll metadata.
|
||||
|
@ -1475,13 +1489,12 @@ WebRenderCommandBuilder::CreateWebRenderCommandsFromDisplayList(nsDisplayList* a
|
|||
|
||||
int32_t descendants = mLayerScrollData.size() - layerCountBeforeRecursing;
|
||||
|
||||
// A deferred transform item is a nsDisplayTransform for which we did
|
||||
// not create a dedicated WebRenderLayerScrollData item at the point
|
||||
// that we encountered the item. Instead, we "deferred" the transform
|
||||
// from that item to combine it into the WebRenderLayerScrollData produced
|
||||
// by child display items. However, in the case where we have a child
|
||||
// display item with a different ASR than the nsDisplayTransform item,
|
||||
// we cannot do this, because it will not conform to APZ's expectations
|
||||
// See the comments on StackingContextHelper::mDeferredTransformItem
|
||||
// for an overview of what deferred transforms are.
|
||||
// In the case where we deferred a transform, but have a child display
|
||||
// item with a different ASR than the deferred transform item, we cannot
|
||||
// put the transform on the WebRenderLayerScrollData item for the child.
|
||||
// We cannot do this because it will not conform to APZ's expectations
|
||||
// with respect to how the APZ tree ends up structured. In particular,
|
||||
// the GetTransformToThis() for the child APZ (which is created for the
|
||||
// child item's ASR) will not include the transform when we actually do
|
||||
|
@ -1506,20 +1519,20 @@ WebRenderCommandBuilder::CreateWebRenderCommandsFromDisplayList(nsDisplayList* a
|
|||
descendants++;
|
||||
|
||||
// This creates the WebRenderLayerScrollData for the deferred transform
|
||||
// item. This holds the transform matrix. and the remaining ASRs
|
||||
// item. This holds the transform matrix and the remaining ASRs
|
||||
// needed to complete the ASR chain (i.e. the ones from the stopAtAsr
|
||||
// down to the deferred transform item's ASR, which must be "between"
|
||||
// stopAtAsr and |item|'s ASR in the ASR tree.
|
||||
// stopAtAsr and |item|'s ASR in the ASR tree).
|
||||
mLayerScrollData.emplace_back();
|
||||
mLayerScrollData.back().Initialize(mManager->GetScrollData(), *deferred,
|
||||
descendants, stopAtAsr, Some((*deferred)->GetTransform().GetMatrix()));
|
||||
descendants, stopAtAsr, aSc.GetDeferredTransformMatrix());
|
||||
} else {
|
||||
// This is the "simple" case where we don't need to create two
|
||||
// WebRenderLayerScrollData items; we can just create one that also
|
||||
// holds the deferred transform matrix, if any.
|
||||
mLayerScrollData.emplace_back();
|
||||
mLayerScrollData.back().Initialize(mManager->GetScrollData(), item,
|
||||
descendants, stopAtAsr, deferred ? Some((*deferred)->GetTransform().GetMatrix()) : Nothing());
|
||||
descendants, stopAtAsr, aSc.GetDeferredTransformMatrix());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,10 +79,10 @@ WebRenderLayerScrollData::Initialize(WebRenderScrollData& aOwner,
|
|||
asr = asr->mParent;
|
||||
}
|
||||
|
||||
// aAncestorTransform, if present, is the transform from an ancestor
|
||||
// nsDisplayTransform that was stored on the stacking context in order to
|
||||
// propagate it downwards in the tree. (i.e. |aItem| is a strict descendant of
|
||||
// the nsDisplayTranform which produced aAncestorTransform). We store this
|
||||
// See the comments on StackingContextHelper::mDeferredTransformItem for an
|
||||
// overview of what deferred transforms are.
|
||||
// aAncestorTransform, if present, is the transform from a deferred transform
|
||||
// item that is an ancestor of |aItem|. We store this transform value
|
||||
// separately from mTransform because in the case where we have multiple
|
||||
// scroll metadata on this layer item, the mAncestorTransform is associated
|
||||
// with the "topmost" scroll metadata, and the mTransform is associated with
|
||||
|
|
|
@ -700,7 +700,6 @@ private:
|
|||
DECL_GFX_PREF(Live, "layout.display-list.flatten-transform", LayoutFlattenTransform, bool, true);
|
||||
|
||||
DECL_GFX_PREF(Once, "layout.frame_rate", LayoutFrameRate, int32_t, -1);
|
||||
DECL_GFX_PREF(Once, "layout.less-event-region-items", LessEventRegionItems, bool, true);
|
||||
DECL_GFX_PREF(Live, "layout.min-active-layer-size", LayoutMinActiveLayerSize, int, 64);
|
||||
DECL_GFX_PREF(Once, "layout.paint_rects_separately", LayoutPaintRectsSeparately, bool, true);
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace JS {
|
|||
// JSJITCOMPILER_ENABLE_TRACELOGGER to true.
|
||||
// This does nothing except return if the trace logger is already active.
|
||||
extern JS_PUBLIC_API(void)
|
||||
StartTraceLogger(JSContext *cx);
|
||||
StartTraceLogger(JSContext *cx, mozilla::TimeStamp profilerStart);
|
||||
|
||||
// Stop trace logging events. All textId's will be set to false, and the
|
||||
// global JSJITCOMPILER_ENABLE_TRACELOGGER will be set to false.
|
||||
|
@ -34,7 +34,7 @@ ResetTraceLogger(void);
|
|||
// Define empty inline functions for when trace logging compilation is not
|
||||
// enabled. TraceLogging.cpp will not be built in that case so we need to
|
||||
// provide something for any routines that reference these.
|
||||
inline void StartTraceLogger(JSContext *cx) {}
|
||||
inline void StartTraceLogger(JSContext *cx, mozilla::TimeStamp profilerStart) {}
|
||||
inline void StopTraceLogger(JSContext *cx) {}
|
||||
inline void ResetTraceLogger(void) {}
|
||||
#endif
|
||||
|
|
|
@ -30,59 +30,6 @@ using namespace js;
|
|||
|
||||
TraceLoggerThreadState* traceLoggerState = nullptr;
|
||||
|
||||
#if defined(MOZ_HAVE_RDTSC)
|
||||
|
||||
uint64_t inline rdtsc() {
|
||||
return ReadTimestampCounter();
|
||||
}
|
||||
|
||||
#elif defined(__powerpc__)
|
||||
static __inline__ uint64_t
|
||||
rdtsc(void)
|
||||
{
|
||||
uint64_t result=0;
|
||||
uint32_t upper, lower,tmp;
|
||||
__asm__ volatile(
|
||||
"0: \n"
|
||||
"\tmftbu %0 \n"
|
||||
"\tmftb %1 \n"
|
||||
"\tmftbu %2 \n"
|
||||
"\tcmpw %2,%0 \n"
|
||||
"\tbne 0b \n"
|
||||
: "=r"(upper),"=r"(lower),"=r"(tmp)
|
||||
);
|
||||
result = upper;
|
||||
result = result<<32;
|
||||
result = result|lower;
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
#elif defined(__arm__) || defined(__aarch64__)
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
static __inline__ uint64_t
|
||||
rdtsc(void)
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
uint64_t ret = tv.tv_sec;
|
||||
ret *= 1000000;
|
||||
ret += tv.tv_usec;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
uint64_t inline
|
||||
rdtsc(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // defined(MOZ_HAVE_RDTSC)
|
||||
|
||||
static bool
|
||||
EnsureTraceLoggerState()
|
||||
{
|
||||
|
@ -175,7 +122,8 @@ TraceLoggerThread::initGraph()
|
|||
|
||||
MOZ_ASSERT(traceLoggerState);
|
||||
bool graphFile = traceLoggerState->isGraphFileEnabled();
|
||||
uint64_t start = rdtsc() - traceLoggerState->startupTime;
|
||||
double delta = traceLoggerState->getTimeStampOffset(mozilla::TimeStamp::Now());
|
||||
uint64_t start = static_cast<uint64_t>(delta);
|
||||
if (!graph->init(start, graphFile)) {
|
||||
graph = nullptr;
|
||||
return;
|
||||
|
@ -303,7 +251,7 @@ TraceLoggerThread::~TraceLoggerThread()
|
|||
{
|
||||
if (graph.get()) {
|
||||
if (!failed) {
|
||||
graph->log(events);
|
||||
graph->log(events, traceLoggerState->startTime);
|
||||
}
|
||||
graph = nullptr;
|
||||
}
|
||||
|
@ -768,11 +716,11 @@ TraceLoggerThread::log(uint32_t id)
|
|||
// we record the time it took to make more space. To log this information
|
||||
// we need 2 extra free entries.
|
||||
if (!events.hasSpaceForAdd(3)) {
|
||||
uint64_t start = rdtsc() - traceLoggerState->startupTime;
|
||||
mozilla::TimeStamp start = mozilla::TimeStamp::Now();
|
||||
|
||||
if (!events.ensureSpaceBeforeAdd(3)) {
|
||||
if (graph.get()) {
|
||||
graph->log(events);
|
||||
graph->log(events, traceLoggerState->startTime);
|
||||
}
|
||||
|
||||
// The data structures are full, and the graph file is not enabled
|
||||
|
@ -798,13 +746,13 @@ TraceLoggerThread::log(uint32_t id)
|
|||
entryStart.textId = TraceLogger_Internal;
|
||||
|
||||
EventEntry& entryStop = events.pushUninitialized();
|
||||
entryStop.time = rdtsc() - traceLoggerState->startupTime;
|
||||
entryStop.time = mozilla::TimeStamp::Now();
|
||||
entryStop.textId = TraceLogger_Stop;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
uint64_t time = rdtsc() - traceLoggerState->startupTime;
|
||||
mozilla::TimeStamp time = mozilla::TimeStamp::Now();
|
||||
|
||||
EventEntry& entry = events.pushUninitialized();
|
||||
entry.time = time;
|
||||
|
@ -1018,7 +966,7 @@ TraceLoggerThreadState::init()
|
|||
spewErrors = false;
|
||||
}
|
||||
|
||||
startupTime = rdtsc();
|
||||
startTime = mozilla::TimeStamp::Now();
|
||||
|
||||
#ifdef DEBUG
|
||||
initialized = true;
|
||||
|
@ -1248,7 +1196,7 @@ JS::ResetTraceLogger(void)
|
|||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS::StartTraceLogger(JSContext *cx)
|
||||
JS::StartTraceLogger(JSContext *cx, mozilla::TimeStamp profilerStart)
|
||||
{
|
||||
if (jit::JitOptions.enableTraceLogger || !traceLoggerState) {
|
||||
return;
|
||||
|
@ -1259,7 +1207,7 @@ JS::StartTraceLogger(JSContext *cx)
|
|||
jit::JitOptions.enableTraceLogger = true;
|
||||
|
||||
// Reset the start time to profile start so it aligns with sampling.
|
||||
traceLoggerState->startupTime = rdtsc();
|
||||
traceLoggerState->startTime = profilerStart;
|
||||
|
||||
if (cx->traceLogger) {
|
||||
cx->traceLogger->enable();
|
||||
|
|
|
@ -418,7 +418,12 @@ class TraceLoggerThreadState
|
|||
uint32_t nextDictionaryId;
|
||||
|
||||
public:
|
||||
uint64_t startupTime;
|
||||
mozilla::TimeStamp startTime;
|
||||
|
||||
double getTimeStampOffset(mozilla::TimeStamp time) {
|
||||
mozilla::TimeDuration delta = time - startTime;
|
||||
return delta.ToMicroseconds();
|
||||
}
|
||||
|
||||
// Mutex to guard the data structures used to hold the payload data:
|
||||
// textIdPayloads, payloadDictionary & dictionaryData.
|
||||
|
@ -436,7 +441,6 @@ class TraceLoggerThreadState
|
|||
spewErrors(false),
|
||||
nextTextId(TraceLogger_Last),
|
||||
nextDictionaryId(0),
|
||||
startupTime(0),
|
||||
lock(js::mutexid::TraceLoggerThreadState)
|
||||
{ }
|
||||
|
||||
|
|
|
@ -681,15 +681,18 @@ TraceLoggerGraph::disable(uint64_t timestamp)
|
|||
}
|
||||
|
||||
void
|
||||
TraceLoggerGraph::log(ContinuousSpace<EventEntry>& events)
|
||||
TraceLoggerGraph::log(ContinuousSpace<EventEntry>& events, mozilla::TimeStamp startTime)
|
||||
{
|
||||
for (uint32_t i = 0; i < events.size(); i++) {
|
||||
mozilla::TimeDuration delta = events[i].time-startTime;
|
||||
uint64_t timeOffset = static_cast<uint64_t>(delta.ToMicroseconds());
|
||||
|
||||
if (events[i].textId == TraceLogger_Stop) {
|
||||
stopEvent(events[i].time);
|
||||
stopEvent(timeOffset);
|
||||
} else if (TLTextIdIsTreeEvent(events[i].textId)) {
|
||||
startEvent(events[i].textId, events[i].time);
|
||||
startEvent(events[i].textId, timeOffset);
|
||||
} else {
|
||||
logTimestamp(events[i].textId, events[i].time);
|
||||
logTimestamp(events[i].textId, timeOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -232,7 +232,8 @@ class TraceLoggerGraph
|
|||
mozilla::Maybe<uint32_t>& column);
|
||||
|
||||
// Create a tree out of all the given events.
|
||||
void log(ContinuousSpace<EventEntry>& events);
|
||||
void log(ContinuousSpace<EventEntry>& events,
|
||||
mozilla::TimeStamp startTime);
|
||||
|
||||
static size_t treeSizeFlushLimit() {
|
||||
// Allow tree size to grow to 100MB.
|
||||
|
|
|
@ -314,11 +314,11 @@ class ContinuousSpace {
|
|||
// The layout of the event log in memory and in the log file.
|
||||
// Readable by JS using TypedArrays.
|
||||
struct EventEntry {
|
||||
uint64_t time;
|
||||
mozilla::TimeStamp time;
|
||||
uint32_t textId;
|
||||
EventEntry(uint64_t time, uint32_t textId)
|
||||
: time(time), textId(textId)
|
||||
{ }
|
||||
EventEntry()
|
||||
: textId(0)
|
||||
{}
|
||||
};
|
||||
|
||||
#endif /* TraceLoggingTypes_h */
|
||||
|
|
|
@ -186,6 +186,17 @@ PrintDisplayItemTo(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem,
|
|||
aStream << ")";
|
||||
}
|
||||
|
||||
if (aItem->HasHitTestInfo()) {
|
||||
auto* hitTestInfoItem = static_cast<nsDisplayHitTestInfoItem*>(aItem);
|
||||
|
||||
aStream << nsPrintfCString(" hitTestInfo(0x%x)",
|
||||
hitTestInfoItem->HitTestFlags().serialize());
|
||||
|
||||
nsRect area = hitTestInfoItem->HitTestArea();
|
||||
aStream << nsPrintfCString(" hitTestArea(%d,%d,%d,%d)",
|
||||
area.x, area.y, area.width, area.height);
|
||||
}
|
||||
|
||||
// Display item specific debug info
|
||||
aItem->WriteDebugInfo(aStream);
|
||||
|
||||
|
|
|
@ -3509,6 +3509,62 @@ GetOrCreateRetainedDisplayListBuilder(nsIFrame* aFrame, bool aRetainingEnabled,
|
|||
return retainedBuilder;
|
||||
}
|
||||
|
||||
// #define PRINT_HITTESTINFO_STATS
|
||||
#ifdef PRINT_HITTESTINFO_STATS
|
||||
void
|
||||
PrintHitTestInfoStatsInternal(nsDisplayList& aList,
|
||||
int& aTotal,
|
||||
int& aHitTest,
|
||||
int& aVisible,
|
||||
int& aSpecial)
|
||||
{
|
||||
for (nsDisplayItem* i = aList.GetBottom(); i; i = i->GetAbove()) {
|
||||
aTotal++;
|
||||
|
||||
if (i->GetChildren()) {
|
||||
PrintHitTestInfoStatsInternal(
|
||||
*i->GetChildren(), aTotal, aHitTest, aVisible, aSpecial);
|
||||
}
|
||||
|
||||
if (i->GetType() == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) {
|
||||
aHitTest++;
|
||||
|
||||
const auto& hitTestInfo =
|
||||
static_cast<nsDisplayHitTestInfoItem*>(i)->HitTestFlags();
|
||||
|
||||
if (hitTestInfo.size() > 1) {
|
||||
aSpecial++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (hitTestInfo == CompositorHitTestVisibleToHit) {
|
||||
aVisible++;
|
||||
continue;
|
||||
}
|
||||
|
||||
aSpecial++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PrintHitTestInfoStats(nsDisplayList& aList)
|
||||
{
|
||||
int total = 0;
|
||||
int hitTest = 0;
|
||||
int visible = 0;
|
||||
int special = 0;
|
||||
|
||||
PrintHitTestInfoStatsInternal(
|
||||
aList, total, hitTest, visible, special);
|
||||
|
||||
double ratio = (double)hitTest / (double)total;
|
||||
|
||||
printf("List %p: total items: %d, hit test items: %d, ratio: %f, visible: %d, special: %d\n",
|
||||
&aList, total, hitTest, ratio, visible, special);
|
||||
}
|
||||
#endif
|
||||
|
||||
nsresult
|
||||
nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext, nsIFrame* aFrame,
|
||||
const nsRegion& aDirtyRegion, nscolor aBackstop,
|
||||
|
@ -3907,6 +3963,12 @@ nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext, nsIFrame* aFrame,
|
|||
flags |= nsDisplayList::PAINT_IDENTICAL_DISPLAY_LIST;
|
||||
}
|
||||
|
||||
#ifdef PRINT_HITTESTINFO_STATS
|
||||
if (XRE_IsContentProcess()) {
|
||||
PrintHitTestInfoStats(list);
|
||||
}
|
||||
#endif
|
||||
|
||||
TimeStamp paintStart = TimeStamp::Now();
|
||||
RefPtr<LayerManager> layerManager
|
||||
= list.PaintRoot(&builder, aRenderingContext, flags);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "mozilla/dom/ElementInlines.h"
|
||||
#include "mozilla/dom/Selection.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/gfx/gfxVars.h"
|
||||
#include "mozilla/gfx/PathHelpers.h"
|
||||
#include "mozilla/Sprintf.h"
|
||||
|
||||
|
@ -2679,16 +2680,27 @@ ItemParticipatesIn3DContext(nsIFrame* aAncestor, nsDisplayItem* aItem)
|
|||
}
|
||||
|
||||
static void
|
||||
WrapSeparatorTransform(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
|
||||
nsDisplayList* aSource, nsDisplayList* aTarget,
|
||||
int aIndex) {
|
||||
if (!aSource->IsEmpty()) {
|
||||
nsDisplayTransform *sepIdItem =
|
||||
MakeDisplayItem<nsDisplayTransform>(aBuilder, aFrame, aSource,
|
||||
aBuilder->GetVisibleRect(), Matrix4x4(), aIndex);
|
||||
sepIdItem->SetNoExtendContext();
|
||||
aTarget->AppendToTop(sepIdItem);
|
||||
WrapSeparatorTransform(nsDisplayListBuilder* aBuilder,
|
||||
nsIFrame* aFrame,
|
||||
nsDisplayList* aNonParticipants,
|
||||
nsDisplayList* aParticipants,
|
||||
int aIndex,
|
||||
nsDisplayItem** aSeparator)
|
||||
{
|
||||
if (aNonParticipants->IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsDisplayTransform* item =
|
||||
MakeDisplayItem<nsDisplayTransform>(aBuilder, aFrame, aNonParticipants,
|
||||
aBuilder->GetVisibleRect(), Matrix4x4(), aIndex);
|
||||
item->SetNoExtendContext();
|
||||
|
||||
if (*aSeparator == nullptr) {
|
||||
*aSeparator = item;
|
||||
}
|
||||
|
||||
aParticipants->AppendToTop(item);
|
||||
}
|
||||
|
||||
// Try to compute a clip rect to bound the contents of the mask item
|
||||
|
@ -2811,6 +2823,60 @@ struct AutoCheckBuilder {
|
|||
nsDisplayListBuilder* mBuilder;
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper class to track container creation. Stores the first tracked container.
|
||||
* Used to find the innermost container for hit test information, and to notify
|
||||
* callers whether a container item was created or not.
|
||||
*/
|
||||
struct ContainerTracker
|
||||
{
|
||||
void TrackContainer(nsDisplayItem* aContainer)
|
||||
{
|
||||
MOZ_ASSERT(aContainer);
|
||||
|
||||
if (!mContainer) {
|
||||
mContainer = aContainer;
|
||||
}
|
||||
|
||||
mCreatedContainer = true;
|
||||
}
|
||||
|
||||
void ResetCreatedContainer()
|
||||
{
|
||||
mCreatedContainer = false;
|
||||
}
|
||||
|
||||
nsDisplayItem* mContainer = nullptr;
|
||||
bool mCreatedContainer = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds hit test information |aHitTestInfo| on the container item |aContainer|,
|
||||
* or if the container item is null, creates a separate hit test item that is
|
||||
* added to the bottom of the display list |aList|.
|
||||
*/
|
||||
static void
|
||||
AddHitTestInfo(nsDisplayListBuilder* aBuilder,
|
||||
nsDisplayList* aList,
|
||||
nsDisplayItem* aContainer,
|
||||
nsIFrame* aFrame,
|
||||
mozilla::UniquePtr<HitTestInfo>&& aHitTestInfo)
|
||||
{
|
||||
nsDisplayHitTestInfoItem* hitTestItem;
|
||||
|
||||
if (aContainer) {
|
||||
MOZ_ASSERT(aContainer->IsHitTestItem());
|
||||
hitTestItem = static_cast<nsDisplayHitTestInfoItem*>(aContainer);
|
||||
hitTestItem->SetHitTestInfo(std::move(aHitTestInfo));
|
||||
} else {
|
||||
// No container item was created for this frame. Create a separate
|
||||
// nsDisplayCompositorHitTestInfo item instead.
|
||||
hitTestItem = MakeDisplayItem<nsDisplayCompositorHitTestInfo>(
|
||||
aBuilder, aFrame, std::move(aHitTestInfo));
|
||||
aList->AppendToBottom(hitTestItem);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
|
||||
nsDisplayList* aList,
|
||||
|
@ -3041,6 +3107,9 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
|
|||
clipForMask = ComputeClipForMaskItem(aBuilder, this);
|
||||
}
|
||||
|
||||
|
||||
mozilla::UniquePtr<HitTestInfo> hitTestInfo;
|
||||
|
||||
nsDisplayListCollection set(aBuilder);
|
||||
{
|
||||
DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder);
|
||||
|
@ -3075,8 +3144,23 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
|
|||
|
||||
aBuilder->AdjustWindowDraggingRegion(this);
|
||||
|
||||
aBuilder->BuildCompositorHitTestInfoIfNeeded(this, set.BorderBackground(),
|
||||
true);
|
||||
if (gfxVars::UseWebRender()) {
|
||||
aBuilder->BuildCompositorHitTestInfoIfNeeded(
|
||||
this, set.BorderBackground(), true);
|
||||
} else {
|
||||
CompositorHitTestInfo info = aBuilder->BuildCompositorHitTestInfo()
|
||||
? GetCompositorHitTestInfo(aBuilder)
|
||||
: CompositorHitTestInvisibleToHit;
|
||||
|
||||
if (info != CompositorHitTestInvisibleToHit) {
|
||||
// Frame has hit test flags set, initialize the hit test info structure.
|
||||
hitTestInfo = mozilla::MakeUnique<HitTestInfo>(aBuilder, this, info);
|
||||
|
||||
// Let child frames know the current hit test area and hit test flags.
|
||||
aBuilder->SetCompositorHitTestInfo(
|
||||
hitTestInfo->mArea, hitTestInfo->mFlags);
|
||||
}
|
||||
}
|
||||
|
||||
MarkAbsoluteFramesForDisplayList(aBuilder);
|
||||
aBuilder->Check();
|
||||
|
@ -3179,9 +3263,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
|
|||
// Get the ASR to use for the container items that we create here.
|
||||
const ActiveScrolledRoot* containerItemASR = contASRTracker.GetContainerASR();
|
||||
|
||||
if (aCreatedContainerItem) {
|
||||
*aCreatedContainerItem = false;
|
||||
}
|
||||
ContainerTracker ct;
|
||||
|
||||
/* If adding both a nsDisplayBlendContainer and a nsDisplayBlendMode to the
|
||||
* same list, the nsDisplayBlendContainer should be added first. This only
|
||||
|
@ -3195,9 +3277,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
|
|||
resultList.AppendToTop(
|
||||
nsDisplayBlendContainer::CreateForMixBlendMode(aBuilder, this, &resultList,
|
||||
containerItemASR));
|
||||
if (aCreatedContainerItem) {
|
||||
*aCreatedContainerItem = true;
|
||||
}
|
||||
ct.TrackContainer(resultList.GetTop());
|
||||
}
|
||||
|
||||
/* If there are any SVG effects, wrap the list up in an SVG effects item
|
||||
|
@ -3220,6 +3300,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
|
|||
/* List now emptied, so add the new list to the top. */
|
||||
resultList.AppendToTop(MakeDisplayItem<nsDisplayFilters>(
|
||||
aBuilder, this, &resultList));
|
||||
ct.TrackContainer(resultList.GetTop());
|
||||
}
|
||||
|
||||
if (usingMask) {
|
||||
|
@ -3239,15 +3320,17 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
|
|||
/* List now emptied, so add the new list to the top. */
|
||||
resultList.AppendToTop(MakeDisplayItem<nsDisplayMasksAndClipPaths>(
|
||||
aBuilder, this, &resultList, maskASR));
|
||||
ct.TrackContainer(resultList.GetTop());
|
||||
}
|
||||
|
||||
// TODO(miko): We could probably create a wraplist here and avoid creating
|
||||
// it later in |BuildDisplayListForChild()|.
|
||||
ct.ResetCreatedContainer();
|
||||
|
||||
// Also add the hoisted scroll info items. We need those for APZ scrolling
|
||||
// because nsDisplayMasksAndClipPaths items can't build active layers.
|
||||
aBuilder->ExitSVGEffectsContents();
|
||||
resultList.AppendToTop(&hoistedScrollInfoItemsStorage);
|
||||
if (aCreatedContainerItem) {
|
||||
*aCreatedContainerItem = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* If the list is non-empty and there is CSS group opacity without SVG
|
||||
|
@ -3266,9 +3349,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
|
|||
containerItemASR,
|
||||
opacityItemForEventsAndPluginsOnly,
|
||||
needsActiveOpacityLayer));
|
||||
if (aCreatedContainerItem) {
|
||||
*aCreatedContainerItem = true;
|
||||
}
|
||||
ct.TrackContainer(resultList.GetTop());
|
||||
}
|
||||
|
||||
/* If we're going to apply a transformation and don't have preserve-3d set, wrap
|
||||
|
@ -3289,10 +3370,15 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
|
|||
nsDisplayList participants;
|
||||
int index = 1;
|
||||
|
||||
nsDisplayItem* separator = nullptr;
|
||||
|
||||
while (nsDisplayItem* item = resultList.RemoveBottom()) {
|
||||
if (ItemParticipatesIn3DContext(this, item) && !item->GetClip().HasClip()) {
|
||||
if (ItemParticipatesIn3DContext(this, item) &&
|
||||
!item->GetClip().HasClip()) {
|
||||
// The frame of this item participates the same 3D context.
|
||||
WrapSeparatorTransform(aBuilder, this, &nonparticipants, &participants, index++);
|
||||
WrapSeparatorTransform(
|
||||
aBuilder, this, &nonparticipants, &participants, index++, &separator);
|
||||
|
||||
participants.AppendToTop(item);
|
||||
} else {
|
||||
// The frame of the item doesn't participate the current
|
||||
|
@ -3305,7 +3391,13 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
|
|||
nonparticipants.AppendToTop(item);
|
||||
}
|
||||
}
|
||||
WrapSeparatorTransform(aBuilder, this, &nonparticipants, &participants, index++);
|
||||
WrapSeparatorTransform(
|
||||
aBuilder, this, &nonparticipants, &participants, index++, &separator);
|
||||
|
||||
if (separator) {
|
||||
ct.TrackContainer(separator);
|
||||
}
|
||||
|
||||
resultList.AppendToTop(&participants);
|
||||
}
|
||||
|
||||
|
@ -3333,18 +3425,15 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
|
|||
&resultList, visibleRect, 0,
|
||||
allowAsyncAnimation);
|
||||
resultList.AppendToTop(transformItem);
|
||||
ct.TrackContainer(transformItem);
|
||||
|
||||
if (hasPerspective) {
|
||||
if (clipCapturedBy == ContainerItemType::ePerspective) {
|
||||
clipState.Restore();
|
||||
}
|
||||
resultList.AppendToTop(
|
||||
MakeDisplayItem<nsDisplayPerspective>(
|
||||
resultList.AppendToTop(MakeDisplayItem<nsDisplayPerspective>(
|
||||
aBuilder, this, &resultList));
|
||||
}
|
||||
|
||||
if (aCreatedContainerItem) {
|
||||
*aCreatedContainerItem = true;
|
||||
ct.TrackContainer(resultList.GetTop());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3355,9 +3444,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
|
|||
aBuilder->CurrentActiveScrolledRoot(),
|
||||
nsDisplayOwnLayerFlags::eNone,
|
||||
ScrollbarData{}, /* aForceActive = */ false));
|
||||
if (aCreatedContainerItem) {
|
||||
*aCreatedContainerItem = true;
|
||||
}
|
||||
ct.TrackContainer(resultList.GetTop());
|
||||
}
|
||||
|
||||
/* If we have sticky positioning, wrap it in a sticky position item.
|
||||
|
@ -3379,9 +3466,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
|
|||
resultList.AppendToTop(
|
||||
MakeDisplayItem<nsDisplayFixedPosition>(aBuilder, this, &resultList,
|
||||
fixedASR, containerItemASR));
|
||||
if (aCreatedContainerItem) {
|
||||
*aCreatedContainerItem = true;
|
||||
}
|
||||
ct.TrackContainer(resultList.GetTop());
|
||||
} else if (useStickyPosition) {
|
||||
// For position:sticky, the clip needs to be applied both to the sticky
|
||||
// container item and to the contents. The container item needs the clip
|
||||
|
@ -3401,9 +3486,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
|
|||
resultList.AppendToTop(
|
||||
MakeDisplayItem<nsDisplayStickyPosition>(aBuilder, this, &resultList,
|
||||
stickyASR, aBuilder->CurrentActiveScrolledRoot()));
|
||||
if (aCreatedContainerItem) {
|
||||
*aCreatedContainerItem = true;
|
||||
}
|
||||
ct.TrackContainer(resultList.GetTop());
|
||||
}
|
||||
|
||||
/* If there's blending, wrap up the list in a blend-mode item. Note
|
||||
|
@ -3417,12 +3500,25 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
|
|||
MakeDisplayItem<nsDisplayBlendMode>(aBuilder, this, &resultList,
|
||||
effects->mMixBlendMode,
|
||||
containerItemASR));
|
||||
if (aCreatedContainerItem) {
|
||||
*aCreatedContainerItem = true;
|
||||
}
|
||||
ct.TrackContainer(resultList.GetTop());
|
||||
}
|
||||
|
||||
CreateOwnLayerIfNeeded(aBuilder, &resultList, aCreatedContainerItem);
|
||||
bool createdOwnLayer = false;
|
||||
CreateOwnLayerIfNeeded(aBuilder, &resultList, &createdOwnLayer);
|
||||
if (createdOwnLayer) {
|
||||
ct.TrackContainer(resultList.GetTop());
|
||||
}
|
||||
|
||||
if (aCreatedContainerItem) {
|
||||
*aCreatedContainerItem = ct.mCreatedContainer;
|
||||
}
|
||||
|
||||
if (hitTestInfo) {
|
||||
// WebRender support is not yet implemented.
|
||||
MOZ_ASSERT(!gfxVars::UseWebRender());
|
||||
AddHitTestInfo(
|
||||
aBuilder, &resultList, ct.mContainer, this, std::move(hitTestInfo));
|
||||
}
|
||||
|
||||
aList->AppendToTop(&resultList);
|
||||
}
|
||||
|
@ -11266,6 +11362,31 @@ nsIFrame::AddSizeOfExcludingThisForTree(nsWindowSizes& aSizes) const
|
|||
}
|
||||
}
|
||||
|
||||
nsRect
|
||||
nsIFrame::GetCompositorHitTestArea(nsDisplayListBuilder* aBuilder)
|
||||
{
|
||||
nsRect area;
|
||||
|
||||
nsIScrollableFrame* scrollFrame =
|
||||
nsLayoutUtils::GetScrollableFrameFor(this);
|
||||
if (scrollFrame) {
|
||||
// If the frame is content of a scrollframe, then we need to pick up the
|
||||
// area corresponding to the overflow rect as well. Otherwise the parts of
|
||||
// the overflow that are not occupied by descendants get skipped and the
|
||||
// APZ code sends touch events to the content underneath instead.
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1127773#c15.
|
||||
area = GetScrollableOverflowRect();
|
||||
} else {
|
||||
area = nsRect(nsPoint(0, 0), GetSize());
|
||||
}
|
||||
|
||||
if (!area.IsEmpty()) {
|
||||
return area + aBuilder->ToReferenceFrame(this);
|
||||
}
|
||||
|
||||
return area;
|
||||
}
|
||||
|
||||
CompositorHitTestInfo
|
||||
nsIFrame::GetCompositorHitTestInfo(nsDisplayListBuilder* aBuilder)
|
||||
{
|
||||
|
@ -11320,10 +11441,8 @@ nsIFrame::GetCompositorHitTestInfo(nsDisplayListBuilder* aBuilder)
|
|||
// ancestor DOM elements. Refer to the documentation in TouchActionHelper.cpp
|
||||
// for details; this code is meant to be equivalent to that code, but woven
|
||||
// into the top-down recursive display list building process.
|
||||
CompositorHitTestInfo inheritedTouchAction = CompositorHitTestInvisibleToHit;
|
||||
if (nsDisplayCompositorHitTestInfo* parentInfo = aBuilder->GetCompositorHitTestInfo()) {
|
||||
inheritedTouchAction = parentInfo->HitTestInfo() & CompositorHitTestTouchActionMask;
|
||||
}
|
||||
CompositorHitTestInfo inheritedTouchAction =
|
||||
aBuilder->GetHitTestInfo() & CompositorHitTestTouchActionMask;
|
||||
|
||||
nsIFrame* touchActionFrame = this;
|
||||
if (nsIScrollableFrame* scrollFrame = nsLayoutUtils::GetScrollableFrameFor(this)) {
|
||||
|
|
|
@ -3686,19 +3686,22 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
|||
aBuilder->SetActiveScrolledRootForRootScrollframe(aBuilder->CurrentActiveScrolledRoot());
|
||||
}
|
||||
|
||||
if (mWillBuildScrollableLayer) {
|
||||
if (mWillBuildScrollableLayer && aBuilder->BuildCompositorHitTestInfo()) {
|
||||
// Create a hit test info item for the scrolled content that's not
|
||||
// clipped to the displayport. This ensures that within the bounds
|
||||
// of the scroll frame, the scrolled content is always hit, even
|
||||
// if we are checkerboarding.
|
||||
if (aBuilder->BuildCompositorHitTestInfo()) {
|
||||
CompositorHitTestInfo info = mScrolledFrame->GetCompositorHitTestInfo(aBuilder);
|
||||
if (info != CompositorHitTestInvisibleToHit) {
|
||||
nsDisplayCompositorHitTestInfo* hitInfo =
|
||||
MakeDisplayItem<nsDisplayCompositorHitTestInfo>(aBuilder, mScrolledFrame, info, 1);
|
||||
aBuilder->SetCompositorHitTestInfo(hitInfo);
|
||||
scrolledContent.BorderBackground()->AppendToTop(hitInfo);
|
||||
}
|
||||
CompositorHitTestInfo info =
|
||||
mScrolledFrame->GetCompositorHitTestInfo(aBuilder);
|
||||
|
||||
if (info != CompositorHitTestInvisibleToHit) {
|
||||
auto* hitInfo = MakeDisplayItem<nsDisplayCompositorHitTestInfo>(
|
||||
aBuilder, mScrolledFrame, info, 1);
|
||||
|
||||
aBuilder->SetCompositorHitTestInfo(
|
||||
hitInfo->HitTestArea(), hitInfo->HitTestFlags());
|
||||
|
||||
scrolledContent.BorderBackground()->AppendToTop(hitInfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4133,6 +4133,11 @@ public:
|
|||
bool MayHaveWillChangeBudget() { return mMayHaveWillChangeBudget; }
|
||||
void SetMayHaveWillChangeBudget(bool aHasBudget) { mMayHaveWillChangeBudget = aHasBudget; }
|
||||
|
||||
/**
|
||||
* Returns the hit test area of the frame.
|
||||
*/
|
||||
nsRect GetCompositorHitTestArea(nsDisplayListBuilder* aBuilder);
|
||||
|
||||
/**
|
||||
* Returns the set of flags indicating the properties of the frame that the
|
||||
* compositor might care about for hit-testing purposes. Note that this function
|
||||
|
|
|
@ -206,77 +206,6 @@ AddMarkerIfNeeded(nsDisplayItem* aItem, std::deque<DisplayItemEntry>& aMarkers)
|
|||
return true;
|
||||
}
|
||||
|
||||
class FLBDisplayItemIterator : protected FlattenedDisplayItemIterator
|
||||
{
|
||||
public:
|
||||
FLBDisplayItemIterator(nsDisplayListBuilder* aBuilder,
|
||||
nsDisplayList* aList,
|
||||
ContainerState* aState)
|
||||
: FlattenedDisplayItemIterator(aBuilder, aList, false)
|
||||
, mState(aState)
|
||||
, mStoreMarker(false)
|
||||
{
|
||||
MOZ_ASSERT(mState);
|
||||
ResolveFlattening();
|
||||
}
|
||||
|
||||
DisplayItemEntry GetNextEntry()
|
||||
{
|
||||
if (!mMarkers.empty()) {
|
||||
DisplayItemEntry entry = mMarkers.front();
|
||||
mMarkers.pop_front();
|
||||
return entry;
|
||||
}
|
||||
|
||||
nsDisplayItem* next = GetNext();
|
||||
return DisplayItemEntry{ next, DisplayItemEntryType::ITEM };
|
||||
}
|
||||
|
||||
nsDisplayItem* GetNext();
|
||||
|
||||
bool HasNext() const
|
||||
{
|
||||
return FlattenedDisplayItemIterator::HasNext() || !mMarkers.empty();
|
||||
}
|
||||
|
||||
nsDisplayItem* PeekNext() { return mNext; }
|
||||
|
||||
private:
|
||||
bool ShouldFlattenNextItem() override;
|
||||
|
||||
void StartNested(nsDisplayItem* aItem) override
|
||||
{
|
||||
if (!mStoreMarker) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (AddMarkerIfNeeded<MarkerType::StartMarker>(aItem, mMarkers)) {
|
||||
mActiveMarkers.AppendElement(aItem);
|
||||
}
|
||||
|
||||
mStoreMarker = false;
|
||||
}
|
||||
|
||||
void EndNested(nsDisplayItem* aItem) override
|
||||
{
|
||||
if (mActiveMarkers.IsEmpty() || mActiveMarkers.LastElement() != aItem) {
|
||||
// Do not emit an end marker if this item did not emit a start marker.
|
||||
return;
|
||||
}
|
||||
|
||||
if (AddMarkerIfNeeded<MarkerType::EndMarker>(aItem, mMarkers)) {
|
||||
mActiveMarkers.RemoveLastElement();
|
||||
}
|
||||
}
|
||||
|
||||
bool NextItemWantsInactiveLayer();
|
||||
|
||||
std::deque<DisplayItemEntry> mMarkers;
|
||||
AutoTArray<nsDisplayItem*, 4> mActiveMarkers;
|
||||
ContainerState* mState;
|
||||
bool mStoreMarker;
|
||||
};
|
||||
|
||||
DisplayItemData::DisplayItemData(LayerManagerData* aParent,
|
||||
uint32_t aKey,
|
||||
Layer* aLayer,
|
||||
|
@ -683,8 +612,8 @@ public:
|
|||
/**
|
||||
* Add the given hit test info to the hit regions for this PaintedLayer.
|
||||
*/
|
||||
void AccumulateHitTestInfo(ContainerState* aState,
|
||||
nsDisplayCompositorHitTestInfo* aItem,
|
||||
void AccumulateHitTestItem(ContainerState* aState,
|
||||
nsDisplayItem* aItem,
|
||||
TransformClipNode* aTransform);
|
||||
|
||||
/**
|
||||
|
@ -1298,6 +1227,7 @@ public:
|
|||
, mParameters(aParameters)
|
||||
, mPaintedLayerDataTree(*this, aBackgroundColor)
|
||||
, mLastDisplayPortAGR(nullptr)
|
||||
, mContainerItem(aContainerItem)
|
||||
{
|
||||
nsPresContext* presContext = aContainerFrame->PresContext();
|
||||
mAppUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
|
||||
|
@ -1726,6 +1656,8 @@ protected:
|
|||
AnimatedGeometryRoot* mLastDisplayPortAGR;
|
||||
nsRect mLastDisplayPortRect;
|
||||
|
||||
nsDisplayItem* mContainerItem;
|
||||
|
||||
// Cache ScrollMetadata so it doesn't need recomputed if the ASR and clip are
|
||||
// unchanged. If mASR == nullptr then mMetadata is not valid.
|
||||
struct CachedScrollMetadata
|
||||
|
@ -1743,95 +1675,175 @@ protected:
|
|||
CachedScrollMetadata mCachedScrollMetadata;
|
||||
};
|
||||
|
||||
nsDisplayItem*
|
||||
FLBDisplayItemIterator::GetNext()
|
||||
class FLBDisplayItemIterator : protected FlattenedDisplayItemIterator
|
||||
{
|
||||
// This function is only supposed to be called if there are no markers set.
|
||||
// Breaking this invariant can potentially break effect flattening and/or
|
||||
// display item merging.
|
||||
MOZ_ASSERT(mMarkers.empty());
|
||||
public:
|
||||
FLBDisplayItemIterator(nsDisplayListBuilder* aBuilder,
|
||||
nsDisplayList* aList,
|
||||
ContainerState* aState)
|
||||
: FlattenedDisplayItemIterator(aBuilder, aList, false)
|
||||
, mState(aState)
|
||||
, mAddingEffectMarker(false)
|
||||
{
|
||||
MOZ_ASSERT(mState);
|
||||
|
||||
nsDisplayItem* next = mNext;
|
||||
|
||||
// Advance mNext to the following item
|
||||
if (next) {
|
||||
nsDisplayItem* peek = next->GetAbove();
|
||||
|
||||
// Peek ahead to the next item and see if it can be merged with the
|
||||
// current item.
|
||||
if (peek && next->CanMerge(peek)) {
|
||||
// Create a list of consecutive items that can be merged together.
|
||||
AutoTArray<nsDisplayItem*, 2> mergedItems{ next, peek };
|
||||
while ((peek = peek->GetAbove())) {
|
||||
if (!next->CanMerge(peek)) {
|
||||
break;
|
||||
}
|
||||
|
||||
mergedItems.AppendElement(peek);
|
||||
}
|
||||
|
||||
// We have items that can be merged together.
|
||||
// Merge them into a temporary item and process that item immediately.
|
||||
MOZ_ASSERT(mergedItems.Length() > 1);
|
||||
next = mState->mBuilder->MergeItems(mergedItems);
|
||||
if (aState->mContainerItem) {
|
||||
// Add container item hit test information for processing, if needed.
|
||||
AddHitTestMarker(aState->mContainerItem);
|
||||
}
|
||||
|
||||
// |mNext| is either the first item that could not be merged with |next|,
|
||||
// or a nullptr.
|
||||
mNext = peek;
|
||||
|
||||
ResolveFlattening();
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
bool
|
||||
FLBDisplayItemIterator::NextItemWantsInactiveLayer()
|
||||
{
|
||||
LayerState layerState = mNext->GetLayerState(
|
||||
mState->mBuilder, mState->mManager, mState->mParameters);
|
||||
|
||||
return layerState == LayerState::LAYER_INACTIVE;
|
||||
}
|
||||
|
||||
bool
|
||||
FLBDisplayItemIterator::ShouldFlattenNextItem()
|
||||
{
|
||||
if (!mNext) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mNext->ShouldFlattenAway(mBuilder)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const DisplayItemType type = mNext->GetType();
|
||||
if (type != DisplayItemType::TYPE_OPACITY &&
|
||||
type != DisplayItemType::TYPE_TRANSFORM) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type == DisplayItemType::TYPE_OPACITY) {
|
||||
nsDisplayOpacity* opacity = static_cast<nsDisplayOpacity*>(mNext);
|
||||
|
||||
if (opacity->OpacityAppliedToChildren()) {
|
||||
// This is the previous opacity flattening path, where the opacity has
|
||||
// been applied to children.
|
||||
return true;
|
||||
void AddHitTestMarker(nsDisplayItem* aItem)
|
||||
{
|
||||
if (aItem->HasHitTestInfo()) {
|
||||
mMarkers.emplace_back(aItem, DisplayItemEntryType::HIT_TEST_INFO);
|
||||
}
|
||||
}
|
||||
|
||||
if (mState->IsInInactiveLayer() || !NextItemWantsInactiveLayer()) {
|
||||
// Do not flatten nested inactive display items, or display items that want
|
||||
// an active layer.
|
||||
return false;
|
||||
DisplayItemEntry GetNextEntry()
|
||||
{
|
||||
if (!mMarkers.empty()) {
|
||||
DisplayItemEntry entry = mMarkers.front();
|
||||
mMarkers.pop_front();
|
||||
return entry;
|
||||
}
|
||||
|
||||
nsDisplayItem* next = GetNext();
|
||||
return DisplayItemEntry{ next, DisplayItemEntryType::ITEM };
|
||||
}
|
||||
|
||||
// Flatten inactive nsDisplayOpacity and nsDisplayTransform.
|
||||
mStoreMarker = true;
|
||||
return true;
|
||||
}
|
||||
nsDisplayItem* GetNext()
|
||||
{
|
||||
// This function is only supposed to be called if there are no markers set.
|
||||
// Breaking this invariant can potentially break effect flattening and/or
|
||||
// display item merging.
|
||||
MOZ_ASSERT(mMarkers.empty());
|
||||
|
||||
nsDisplayItem* next = mNext;
|
||||
|
||||
// Advance mNext to the following item
|
||||
if (next) {
|
||||
nsDisplayItem* peek = next->GetAbove();
|
||||
|
||||
// Peek ahead to the next item and see if it can be merged with the
|
||||
// current item.
|
||||
if (peek && next->CanMerge(peek)) {
|
||||
// Create a list of consecutive items that can be merged together.
|
||||
AutoTArray<nsDisplayItem*, 2> mergedItems{ next, peek };
|
||||
while ((peek = peek->GetAbove())) {
|
||||
if (!next->CanMerge(peek)) {
|
||||
break;
|
||||
}
|
||||
|
||||
mergedItems.AppendElement(peek);
|
||||
}
|
||||
|
||||
// We have items that can be merged together.
|
||||
// Merge them into a temporary item and process that item immediately.
|
||||
MOZ_ASSERT(mergedItems.Length() > 1);
|
||||
next = mState->mBuilder->MergeItems(mergedItems);
|
||||
}
|
||||
|
||||
// |mNext| is either the first item that could not be merged with |next|,
|
||||
// or a nullptr.
|
||||
mNext = peek;
|
||||
|
||||
ResolveFlattening();
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
bool HasNext() const
|
||||
{
|
||||
return FlattenedDisplayItemIterator::HasNext() || !mMarkers.empty();
|
||||
}
|
||||
|
||||
nsDisplayItem* PeekNext() { return mNext; }
|
||||
|
||||
private:
|
||||
bool ShouldFlattenNextItem() override
|
||||
{
|
||||
if (!mNext) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mNext->ShouldFlattenAway(mBuilder)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const DisplayItemType type = mNext->GetType();
|
||||
if (type != DisplayItemType::TYPE_OPACITY &&
|
||||
type != DisplayItemType::TYPE_TRANSFORM) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type == DisplayItemType::TYPE_OPACITY) {
|
||||
nsDisplayOpacity* opacity = static_cast<nsDisplayOpacity*>(mNext);
|
||||
|
||||
if (opacity->OpacityAppliedToChildren()) {
|
||||
// This is the previous opacity flattening path, where the opacity has
|
||||
// been applied to children.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (mState->IsInInactiveLayer() || !NextItemWantsInactiveLayer()) {
|
||||
// Do not flatten nested inactive display items, or display items that
|
||||
// want an active layer.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Flatten inactive nsDisplayOpacity and nsDisplayTransform.
|
||||
mAddingEffectMarker = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void EnterChildList(nsDisplayItem* aItem) override
|
||||
{
|
||||
if (!mAddingEffectMarker) {
|
||||
// A container item will be flattened but no effect marker is needed.
|
||||
AddHitTestMarker(aItem);
|
||||
return;
|
||||
}
|
||||
|
||||
if (AddMarkerIfNeeded<MarkerType::StartMarker>(aItem, mMarkers)) {
|
||||
mActiveMarkers.AppendElement(aItem);
|
||||
}
|
||||
|
||||
// Place the hit test marker between the effect markers.
|
||||
AddHitTestMarker(aItem);
|
||||
|
||||
mAddingEffectMarker = false;
|
||||
}
|
||||
|
||||
void ExitChildList(nsDisplayItem* aItem) override
|
||||
{
|
||||
if (mActiveMarkers.IsEmpty() || mActiveMarkers.LastElement() != aItem) {
|
||||
// Do not emit an end marker if this item did not emit a start marker.
|
||||
return;
|
||||
}
|
||||
|
||||
if (AddMarkerIfNeeded<MarkerType::EndMarker>(aItem, mMarkers)) {
|
||||
mActiveMarkers.RemoveLastElement();
|
||||
}
|
||||
}
|
||||
|
||||
bool NextItemWantsInactiveLayer()
|
||||
{
|
||||
LayerState layerState = mNext->GetLayerState(
|
||||
mState->mBuilder, mState->mManager, mState->mParameters);
|
||||
|
||||
return layerState == LayerState::LAYER_INACTIVE;
|
||||
}
|
||||
|
||||
std::deque<DisplayItemEntry> mMarkers;
|
||||
AutoTArray<nsDisplayItem*, 4> mActiveMarkers;
|
||||
ContainerState* mState;
|
||||
bool mAddingEffectMarker;
|
||||
};
|
||||
|
||||
class PaintedDisplayItemLayerUserData : public LayerUserData
|
||||
{
|
||||
|
@ -4019,6 +4031,9 @@ PaintedLayerData::Accumulate(ContainerState* aState,
|
|||
nsTArray<size_t>& aOpacityIndices,
|
||||
const RefPtr<TransformClipNode>& aTransform)
|
||||
{
|
||||
MOZ_ASSERT(aType != DisplayItemEntryType::HIT_TEST_INFO,
|
||||
"Should have handled hit test items earlier!");
|
||||
|
||||
FLB_LOG_PAINTED_LAYER_DECISION(
|
||||
this,
|
||||
"Accumulating dp=%s(%p), f=%p against pld=%p\n",
|
||||
|
@ -4237,27 +4252,45 @@ PaintedLayerData::CombinedTouchActionRegion()
|
|||
}
|
||||
|
||||
void
|
||||
PaintedLayerData::AccumulateHitTestInfo(ContainerState* aState,
|
||||
nsDisplayCompositorHitTestInfo* aItem,
|
||||
PaintedLayerData::AccumulateHitTestItem(ContainerState* aState,
|
||||
nsDisplayItem* aItem,
|
||||
TransformClipNode* aTransform)
|
||||
{
|
||||
FLB_LOG_PAINTED_LAYER_DECISION(
|
||||
this, "Accumulating hit test info %p against pld=%p\n", aItem, this);
|
||||
MOZ_ASSERT(aItem->HasHitTestInfo());
|
||||
auto* item = static_cast<nsDisplayHitTestInfoItem*>(aItem);
|
||||
|
||||
const HitTestInfo& info = item->GetHitTestInfo();
|
||||
|
||||
nsRect area = info.mArea;
|
||||
const CompositorHitTestInfo& flags = info.mFlags;
|
||||
|
||||
FLB_LOG_PAINTED_LAYER_DECISION(
|
||||
this, "Accumulating hit test info %p against pld=%p, "
|
||||
"area: [%d, %d, %d, %d], flags: 0x%x]\n",
|
||||
item, this, area.x, area.y, area.width, area.height, flags.serialize());
|
||||
|
||||
const DisplayItemClip& clip = info.mClip
|
||||
? *info.mClip
|
||||
: DisplayItemClip::NoClip();
|
||||
|
||||
area = clip.ApplyNonRoundedIntersection(area);
|
||||
|
||||
const mozilla::DisplayItemClip& clip = aItem->GetClip();
|
||||
nsRect area = clip.ApplyNonRoundedIntersection(aItem->Area());
|
||||
if (aTransform) {
|
||||
area = aTransform->TransformRect(area, aState->mAppUnitsPerDevPixel);
|
||||
}
|
||||
const mozilla::gfx::CompositorHitTestInfo hitTestInfo = aItem->HitTestInfo();
|
||||
|
||||
if (area.IsEmpty()) {
|
||||
FLB_LOG_PAINTED_LAYER_DECISION(
|
||||
this, "Discarded empty hit test info %p for pld=%p\n", item, this);
|
||||
return;
|
||||
}
|
||||
|
||||
bool hasRoundedCorners = clip.GetRoundedRectCount() > 0;
|
||||
|
||||
// use the NS_FRAME_SIMPLE_EVENT_REGIONS to avoid calling the slightly
|
||||
// expensive HasNonZeroCorner function if we know from a previous run that
|
||||
// the frame has zero corners.
|
||||
nsIFrame* frame = aItem->Frame();
|
||||
|
||||
nsIFrame* frame = item->Frame();
|
||||
bool simpleRegions = frame->HasAnyStateBits(NS_FRAME_SIMPLE_EVENT_REGIONS);
|
||||
if (!simpleRegions) {
|
||||
if (nsLayoutUtils::HasNonZeroCorner(frame->StyleBorder()->mBorderRadius)) {
|
||||
|
@ -4273,15 +4306,15 @@ PaintedLayerData::AccumulateHitTestInfo(ContainerState* aState,
|
|||
mHitRegion.OrWith(area);
|
||||
}
|
||||
|
||||
if (aItem->HitTestInfo().contains(CompositorHitTestFlags::eDispatchToContent)) {
|
||||
if (flags.contains(CompositorHitTestFlags::eDispatchToContent)) {
|
||||
mDispatchToContentHitRegion.OrWith(area);
|
||||
|
||||
if (aItem->HitTestInfo().contains(CompositorHitTestFlags::eRequiresTargetConfirmation)) {
|
||||
if (flags.contains(CompositorHitTestFlags::eRequiresTargetConfirmation)) {
|
||||
mDTCRequiresTargetConfirmation = true;
|
||||
}
|
||||
}
|
||||
|
||||
const auto touchFlags = hitTestInfo & CompositorHitTestTouchActionMask;
|
||||
const auto touchFlags = flags & CompositorHitTestTouchActionMask;
|
||||
if (!touchFlags.isEmpty()) {
|
||||
// If there are multiple touch-action areas, there are multiple elements
|
||||
// with touch-action properties. We don't know what the relationship is
|
||||
|
@ -4312,11 +4345,11 @@ PaintedLayerData::AccumulateHitTestInfo(ContainerState* aState,
|
|||
// properly.
|
||||
if (touchFlags !=
|
||||
CompositorHitTestFlags::eTouchActionDoubleTapZoomDisabled) {
|
||||
if (!hitTestInfo.contains(CompositorHitTestFlags::eTouchActionPanXDisabled)) {
|
||||
if (!flags.contains(CompositorHitTestFlags::eTouchActionPanXDisabled)) {
|
||||
// pan-x is allowed
|
||||
mHorizontalPanRegion.OrWith(area);
|
||||
}
|
||||
if (!hitTestInfo.contains(CompositorHitTestFlags::eTouchActionPanYDisabled)) {
|
||||
if (!flags.contains(CompositorHitTestFlags::eTouchActionPanYDisabled)) {
|
||||
// pan-y is allowed
|
||||
mVerticalPanRegion.OrWith(area);
|
||||
}
|
||||
|
@ -4744,6 +4777,7 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList)
|
|||
PaintedLayerData* selectedLayer = nullptr;
|
||||
AutoTArray<size_t, 2> opacityIndices;
|
||||
|
||||
// AGR and ASR for the container item that was flattened.
|
||||
AnimatedGeometryRoot* containerAGR = nullptr;
|
||||
const ActiveScrolledRoot* containerASR = nullptr;
|
||||
RefPtr<TransformClipNode> transformNode = nullptr;
|
||||
|
@ -4762,26 +4796,19 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList)
|
|||
MOZ_ASSERT(item);
|
||||
DisplayItemType itemType = item->GetType();
|
||||
|
||||
const bool inEffect = InTransform() || InOpacity();
|
||||
|
||||
if (itemType == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) {
|
||||
nsDisplayCompositorHitTestInfo* hitTestInfo =
|
||||
static_cast<nsDisplayCompositorHitTestInfo*>(item);
|
||||
|
||||
if (hitTestInfo->Area().IsEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inEffect) {
|
||||
// If this item is inside a flattened effect, everything below is
|
||||
// unnecessary processing.
|
||||
MOZ_ASSERT(selectedLayer);
|
||||
selectedLayer->AccumulateHitTestInfo(this, hitTestInfo, transformNode);
|
||||
continue;
|
||||
}
|
||||
// Override the marker for nsDisplayCompositorHitTestInfo items.
|
||||
marker = DisplayItemEntryType::HIT_TEST_INFO;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(item->GetType() != DisplayItemType::TYPE_WRAP_LIST);
|
||||
const bool inEffect = InTransform() || InOpacity();
|
||||
|
||||
if (marker == DisplayItemEntryType::HIT_TEST_INFO && inEffect) {
|
||||
// Fast-path for hit test items inside flattened inactive layers.
|
||||
MOZ_ASSERT(selectedLayer);
|
||||
selectedLayer->AccumulateHitTestItem(this, item, transformNode);
|
||||
continue;
|
||||
}
|
||||
|
||||
NS_ASSERTION(mAppUnitsPerDevPixel == AppUnitsPerDevPixel(item),
|
||||
"items in a container layer should all have the same app "
|
||||
|
@ -4791,14 +4818,14 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList)
|
|||
aList->SetNeedsTransparentSurface();
|
||||
}
|
||||
|
||||
if (mParameters.mForEventsAndPluginsOnly && !item->GetChildren() &&
|
||||
(itemType != DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO &&
|
||||
if (mParameters.mForEventsAndPluginsOnly &&
|
||||
(marker != DisplayItemEntryType::HIT_TEST_INFO &&
|
||||
itemType != DisplayItemType::TYPE_PLUGIN)) {
|
||||
// Only process hit test info items or plugin items.
|
||||
continue;
|
||||
}
|
||||
|
||||
LayerState layerState = LAYER_NONE;
|
||||
|
||||
if (marker == DisplayItemEntryType::ITEM) {
|
||||
layerState = item->GetLayerState(mBuilder, mManager, mParameters);
|
||||
|
||||
|
@ -4807,43 +4834,26 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList)
|
|||
}
|
||||
}
|
||||
|
||||
bool forceInactive = false;
|
||||
AnimatedGeometryRoot* animatedGeometryRoot;
|
||||
const ActiveScrolledRoot* itemASR = nullptr;
|
||||
const DisplayItemClipChain* layerClipChain = nullptr;
|
||||
|
||||
if (mManager->IsWidgetLayerManager() && !inEffect) {
|
||||
animatedGeometryRoot = item->GetAnimatedGeometryRoot();
|
||||
itemASR = item->GetActiveScrolledRoot();
|
||||
const DisplayItemClipChain* itemClipChain = item->GetClipChain();
|
||||
if (itemClipChain && itemClipChain->mASR == itemASR &&
|
||||
itemType != DisplayItemType::TYPE_STICKY_POSITION) {
|
||||
layerClipChain = itemClipChain->mParent;
|
||||
} else {
|
||||
layerClipChain = itemClipChain;
|
||||
auto FuseItemClipChainIfNeeded = [&](const ActiveScrolledRoot* aASR) {
|
||||
if (marker == DisplayItemEntryType::ITEM || IsEffectStartMarker(marker)) {
|
||||
// No need to fuse clip chain for effect end markers, since it was
|
||||
// already done for effect start markers.
|
||||
item->FuseClipChainUpTo(mBuilder, aASR);
|
||||
}
|
||||
} else if (inEffect) {
|
||||
animatedGeometryRoot = containerAGR;
|
||||
itemASR = containerASR;
|
||||
|
||||
if (marker != DisplayItemEntryType::POP_TRANSFORM) {
|
||||
item->FuseClipChainUpTo(mBuilder, containerASR);
|
||||
}
|
||||
} else {
|
||||
animatedGeometryRoot = mContainerAnimatedGeometryRoot;
|
||||
itemASR = mContainerASR;
|
||||
item->FuseClipChainUpTo(mBuilder, mContainerASR);
|
||||
}
|
||||
|
||||
const DisplayItemClip& itemClip = item->GetClip();
|
||||
};
|
||||
|
||||
if (inEffect && marker == DisplayItemEntryType::ITEM) {
|
||||
// Fast-path for items inside flattened inactive layers. This works
|
||||
// because the layer state of the item cannot be active, otherwise the
|
||||
// parent item would not have been flattened.
|
||||
MOZ_ASSERT(selectedLayer);
|
||||
|
||||
FuseItemClipChainIfNeeded(containerASR);
|
||||
selectedLayer->Accumulate(this,
|
||||
item,
|
||||
nsIntRect(),
|
||||
nsRect(),
|
||||
itemClip,
|
||||
item->GetClip(),
|
||||
layerState,
|
||||
aList,
|
||||
marker,
|
||||
|
@ -4852,12 +4862,68 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (animatedGeometryRoot == lastAnimatedGeometryRoot) {
|
||||
// Items outside of flattened effects and non-item markers inside flattened
|
||||
// effects are processed here.
|
||||
MOZ_ASSERT(!inEffect || (marker != DisplayItemEntryType::ITEM));
|
||||
|
||||
AnimatedGeometryRoot* itemAGR = nullptr;
|
||||
const ActiveScrolledRoot* itemASR = nullptr;
|
||||
const DisplayItemClipChain* layerClipChain = nullptr;
|
||||
const DisplayItemClipChain* itemClipChain = nullptr;
|
||||
const DisplayItemClip* itemClipPtr = nullptr;
|
||||
|
||||
bool snap = false;
|
||||
nsRect itemContent;
|
||||
|
||||
if (marker == DisplayItemEntryType::HIT_TEST_INFO) {
|
||||
const auto& hitTestInfo =
|
||||
static_cast<nsDisplayHitTestInfoItem*>(item)->GetHitTestInfo();
|
||||
|
||||
// Override the layer selection hints for items that have hit test
|
||||
// information. This is needed because container items may have different
|
||||
// clipping, AGR, or ASR than the child items in them.
|
||||
itemAGR = hitTestInfo.mAGR;
|
||||
itemASR = hitTestInfo.mASR;
|
||||
itemClipChain = hitTestInfo.mClipChain;
|
||||
itemClipPtr = hitTestInfo.mClip;
|
||||
itemContent = hitTestInfo.mArea;
|
||||
} else {
|
||||
itemAGR = item->GetAnimatedGeometryRoot();
|
||||
itemASR = item->GetActiveScrolledRoot();
|
||||
itemClipChain = item->GetClipChain();
|
||||
itemClipPtr = &item->GetClip();
|
||||
itemContent = item->GetBounds(mBuilder, &snap);
|
||||
}
|
||||
|
||||
if (mManager->IsWidgetLayerManager() && !inEffect) {
|
||||
if (itemClipChain && itemClipChain->mASR == itemASR &&
|
||||
itemType != DisplayItemType::TYPE_STICKY_POSITION) {
|
||||
layerClipChain = itemClipChain->mParent;
|
||||
} else {
|
||||
layerClipChain = itemClipChain;
|
||||
}
|
||||
} else {
|
||||
// Inside a flattened effect or inactive layer, use container AGR and ASR.
|
||||
itemAGR = inEffect ? containerAGR : mContainerAnimatedGeometryRoot;
|
||||
itemASR = inEffect ? containerASR : mContainerASR;
|
||||
|
||||
FuseItemClipChainIfNeeded(itemASR);
|
||||
if (marker != DisplayItemEntryType::HIT_TEST_INFO) {
|
||||
// Because of the clip chain fusing, |itemClipPtr| needs to be updated.
|
||||
itemClipPtr = &item->GetClip();
|
||||
}
|
||||
}
|
||||
|
||||
const DisplayItemClip& itemClip = itemClipPtr
|
||||
? *itemClipPtr
|
||||
: DisplayItemClip::NoClip();
|
||||
|
||||
if (itemAGR == lastAnimatedGeometryRoot) {
|
||||
topLeft = lastTopLeft;
|
||||
} else {
|
||||
lastTopLeft = topLeft =
|
||||
(*animatedGeometryRoot)->GetOffsetToCrossDoc(mContainerReferenceFrame);
|
||||
lastAnimatedGeometryRoot = animatedGeometryRoot;
|
||||
(*itemAGR)->GetOffsetToCrossDoc(mContainerReferenceFrame);
|
||||
lastAnimatedGeometryRoot = itemAGR;
|
||||
}
|
||||
|
||||
const ActiveScrolledRoot* scrollMetadataASR =
|
||||
|
@ -4869,15 +4935,6 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList)
|
|||
itemType == DisplayItemType::TYPE_TRANSFORM &&
|
||||
static_cast<nsDisplayTransform*>(item)->MayBeAnimated(mBuilder);
|
||||
|
||||
bool snap;
|
||||
nsRect itemContent = item->GetBounds(mBuilder, &snap);
|
||||
|
||||
if (itemType == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) {
|
||||
nsDisplayCompositorHitTestInfo* hitInfo =
|
||||
static_cast<nsDisplayCompositorHitTestInfo*>(item);
|
||||
itemContent = hitInfo->Area();
|
||||
}
|
||||
|
||||
nsIntRect itemDrawRect = ScaleToOutsidePixels(itemContent, snap);
|
||||
ParentLayerIntRect clipRect;
|
||||
if (itemClip.HasClip()) {
|
||||
|
@ -4911,7 +4968,7 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList)
|
|||
#ifdef DEBUG
|
||||
nsRect bounds = itemContent;
|
||||
|
||||
if (itemType == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO || inEffect) {
|
||||
if (marker == DisplayItemEntryType::HIT_TEST_INFO || inEffect) {
|
||||
bounds.SetEmpty();
|
||||
}
|
||||
|
||||
|
@ -4942,9 +4999,7 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList)
|
|||
ScaleToOutsidePixels(itemBuildingRect, false));
|
||||
}
|
||||
|
||||
if (maxLayers != -1 && layerCount >= maxLayers) {
|
||||
forceInactive = true;
|
||||
}
|
||||
const bool forceInactive = maxLayers != -1 && layerCount >= maxLayers;
|
||||
|
||||
// Assign the item to a layer
|
||||
bool treatInactiveItemAsActive =
|
||||
|
@ -5028,8 +5083,7 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList)
|
|||
mPaintedLayerDataTree.AddingOwnLayer(
|
||||
clipAGR, &scrolledClipRect, uniformColorPtr);
|
||||
} else if (item->ShouldFixToViewport(mBuilder) && itemClip.HasClip() &&
|
||||
item->AnimatedGeometryRootForScrollMetadata() !=
|
||||
animatedGeometryRoot &&
|
||||
item->AnimatedGeometryRootForScrollMetadata() != itemAGR &&
|
||||
!nsLayoutUtils::UsesAsyncScrolling(item->Frame())) {
|
||||
// This is basically the same as the case above, but for the non-APZ
|
||||
// case. At the moment, when APZ is off, there is only the root ASR
|
||||
|
@ -5046,31 +5100,31 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList)
|
|||
// For scrollbar thumbs, the clip we care about is the clip added by the
|
||||
// slider frame.
|
||||
mPaintedLayerDataTree.AddingOwnLayer(
|
||||
animatedGeometryRoot->mParentAGR, clipPtr, uniformColorPtr);
|
||||
itemAGR->mParentAGR, clipPtr, uniformColorPtr);
|
||||
} else if (prerenderedTransform && mManager->IsWidgetLayerManager()) {
|
||||
if (animatedGeometryRoot->mParentAGR) {
|
||||
if (itemAGR->mParentAGR) {
|
||||
mPaintedLayerDataTree.AddingOwnLayer(
|
||||
animatedGeometryRoot->mParentAGR, clipPtr, uniformColorPtr);
|
||||
itemAGR->mParentAGR, clipPtr, uniformColorPtr);
|
||||
} else {
|
||||
mPaintedLayerDataTree.AddingOwnLayer(
|
||||
animatedGeometryRoot, nullptr, uniformColorPtr);
|
||||
itemAGR, nullptr, uniformColorPtr);
|
||||
}
|
||||
} else {
|
||||
// Using itemVisibleRect here isn't perfect. itemVisibleRect can be
|
||||
// larger or smaller than the potential bounds of item's contents in
|
||||
// animatedGeometryRoot: It's too large if there's a clipped display
|
||||
// itemAGR: It's too large if there's a clipped display
|
||||
// port somewhere among item's contents (see bug 1147673), and it can
|
||||
// be too small if the contents can move, because it only looks at the
|
||||
// contents' current bounds and doesn't anticipate any animations.
|
||||
// Time will tell whether this is good enough, or whether we need to do
|
||||
// something more sophisticated here.
|
||||
mPaintedLayerDataTree.AddingOwnLayer(
|
||||
animatedGeometryRoot, &itemVisibleRect, uniformColorPtr);
|
||||
itemAGR, &itemVisibleRect, uniformColorPtr);
|
||||
}
|
||||
|
||||
ContainerLayerParameters params = mParameters;
|
||||
params.mBackgroundColor = uniformColor;
|
||||
params.mLayerCreationHint = GetLayerCreationHint(animatedGeometryRoot);
|
||||
params.mLayerCreationHint = GetLayerCreationHint(itemAGR);
|
||||
params.mScrollMetadataASR = ActiveScrolledRoot::PickDescendant(
|
||||
mContainerScrollMetadataASR, scrollMetadataASR);
|
||||
params.mCompositorASR =
|
||||
|
@ -5236,7 +5290,7 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList)
|
|||
|
||||
NewLayerEntry* newLayerEntry = mNewChildLayers.AppendElement();
|
||||
newLayerEntry->mLayer = ownLayer;
|
||||
newLayerEntry->mAnimatedGeometryRoot = animatedGeometryRoot;
|
||||
newLayerEntry->mAnimatedGeometryRoot = itemAGR;
|
||||
newLayerEntry->mASR = itemASR;
|
||||
newLayerEntry->mScrollMetadataASR = scrollMetadataASR;
|
||||
newLayerEntry->mClipChain = layerClipChain;
|
||||
|
@ -5284,7 +5338,7 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList)
|
|||
}
|
||||
newLayerEntry->mOpaqueRegion = ComputeOpaqueRect(
|
||||
item,
|
||||
animatedGeometryRoot,
|
||||
itemAGR,
|
||||
itemASR,
|
||||
itemClip,
|
||||
aList,
|
||||
|
@ -5333,20 +5387,28 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList)
|
|||
}
|
||||
} else {
|
||||
const bool backfaceHidden = item->In3DContextAndBackfaceIsHidden();
|
||||
const nsIFrame* referenceFrame = item->ReferenceFrame();
|
||||
|
||||
// When container item hit test info is processed, we need to use the same
|
||||
// reference frame as the container children.
|
||||
const nsIFrame* referenceFrame = item == mContainerItem
|
||||
? mContainerReferenceFrame
|
||||
: item->ReferenceFrame();
|
||||
|
||||
MOZ_ASSERT(item != mContainerItem ||
|
||||
marker == DisplayItemEntryType::HIT_TEST_INFO);
|
||||
|
||||
PaintedLayerData* paintedLayerData = selectedLayer;
|
||||
|
||||
if (!paintedLayerData) {
|
||||
paintedLayerData = mPaintedLayerDataTree.FindPaintedLayerFor(
|
||||
animatedGeometryRoot,
|
||||
itemAGR,
|
||||
itemASR,
|
||||
layerClipChain,
|
||||
itemVisibleRect,
|
||||
backfaceHidden,
|
||||
[&](PaintedLayerData* aData) {
|
||||
NewPaintedLayerData(aData,
|
||||
animatedGeometryRoot,
|
||||
itemAGR,
|
||||
itemASR,
|
||||
layerClipChain,
|
||||
scrollMetadataASR,
|
||||
|
@ -5357,11 +5419,9 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList)
|
|||
}
|
||||
MOZ_ASSERT(paintedLayerData);
|
||||
|
||||
if (itemType == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) {
|
||||
nsDisplayCompositorHitTestInfo* hitTestInfo =
|
||||
static_cast<nsDisplayCompositorHitTestInfo*>(item);
|
||||
if (marker == DisplayItemEntryType::HIT_TEST_INFO) {
|
||||
MOZ_ASSERT(!transformNode);
|
||||
paintedLayerData->AccumulateHitTestInfo(this, hitTestInfo, nullptr);
|
||||
paintedLayerData->AccumulateHitTestItem(this, item, nullptr);
|
||||
} else {
|
||||
paintedLayerData->Accumulate(this,
|
||||
item,
|
||||
|
@ -5377,7 +5437,7 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList)
|
|||
if (!paintedLayerData->mLayer) {
|
||||
// Try to recycle the old layer of this display item.
|
||||
RefPtr<PaintedLayer> layer = AttemptToRecyclePaintedLayer(
|
||||
animatedGeometryRoot, item, topLeft, referenceFrame);
|
||||
itemAGR, item, topLeft, referenceFrame);
|
||||
if (layer) {
|
||||
paintedLayerData->mLayer = layer;
|
||||
|
||||
|
|
|
@ -53,7 +53,8 @@ enum class DisplayItemEntryType
|
|||
PUSH_OPACITY_WITH_BG,
|
||||
POP_OPACITY,
|
||||
PUSH_TRANSFORM,
|
||||
POP_TRANSFORM
|
||||
POP_TRANSFORM,
|
||||
HIT_TEST_INFO
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -1013,7 +1013,6 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
|
|||
bool aRetainingDisplayList)
|
||||
: mReferenceFrame(aReferenceFrame)
|
||||
, mIgnoreScrollFrame(nullptr)
|
||||
, mCompositorHitTestInfo(nullptr)
|
||||
, mCurrentTableItem(nullptr)
|
||||
, mCurrentActiveScrolledRoot(nullptr)
|
||||
, mCurrentContainerASR(nullptr)
|
||||
|
@ -1069,13 +1068,13 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
|
|||
, mDisablePartialUpdates(false)
|
||||
, mPartialBuildFailed(false)
|
||||
, mIsInActiveDocShell(false)
|
||||
, mHitTestArea()
|
||||
, mHitTestInfo(CompositorHitTestInvisibleToHit)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsDisplayListBuilder);
|
||||
|
||||
mBuildCompositorHitTestInfo = mAsyncPanZoomEnabled && IsForPainting();
|
||||
|
||||
mLessEventRegionItems = gfxPrefs::LessEventRegionItems();
|
||||
|
||||
nsPresContext* pc = aReferenceFrame->PresContext();
|
||||
nsIPresShell* shell = pc->PresShell();
|
||||
if (pc->IsRenderingOnlySelection()) {
|
||||
|
@ -1116,8 +1115,6 @@ nsDisplayListBuilder::EndFrame()
|
|||
FreeClipChains();
|
||||
FreeTemporaryItems();
|
||||
nsCSSRendering::EndFrameTreesLocked();
|
||||
|
||||
MOZ_ASSERT(!mCompositorHitTestInfo);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2312,31 +2309,6 @@ nsDisplayListBuilder::AppendNewScrollInfoItemForHoisting(
|
|||
mScrollInfoItemsForHoisting->AppendToTop(aScrollInfoItem);
|
||||
}
|
||||
|
||||
static nsRect
|
||||
GetFrameArea(const nsDisplayListBuilder* aBuilder, const nsIFrame* aFrame)
|
||||
{
|
||||
nsRect area;
|
||||
|
||||
nsIScrollableFrame* scrollFrame =
|
||||
nsLayoutUtils::GetScrollableFrameFor(aFrame);
|
||||
if (scrollFrame) {
|
||||
// If the frame is content of a scrollframe, then we need to pick up the
|
||||
// area corresponding to the overflow rect as well. Otherwise the parts of
|
||||
// the overflow that are not occupied by descendants get skipped and the
|
||||
// APZ code sends touch events to the content underneath instead.
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1127773#c15.
|
||||
area = aFrame->GetScrollableOverflowRect();
|
||||
} else {
|
||||
area = nsRect(nsPoint(0, 0), aFrame->GetSize());
|
||||
}
|
||||
|
||||
if (!area.IsEmpty()) {
|
||||
return area + aBuilder->ToReferenceFrame(aFrame);
|
||||
}
|
||||
|
||||
return area;
|
||||
}
|
||||
|
||||
void
|
||||
nsDisplayListBuilder::BuildCompositorHitTestInfoIfNeeded(nsIFrame* aFrame,
|
||||
nsDisplayList* aList,
|
||||
|
@ -2349,45 +2321,25 @@ nsDisplayListBuilder::BuildCompositorHitTestInfoIfNeeded(nsIFrame* aFrame,
|
|||
return;
|
||||
}
|
||||
|
||||
CompositorHitTestInfo info = aFrame->GetCompositorHitTestInfo(this);
|
||||
if (!ShouldBuildCompositorHitTestInfo(aFrame, info, aBuildNew)) {
|
||||
// Either the parent hit test info can be reused, or this frame has no hit
|
||||
// test flags set.
|
||||
const CompositorHitTestInfo info = aFrame->GetCompositorHitTestInfo(this);
|
||||
if (info == CompositorHitTestInvisibleToHit) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsDisplayCompositorHitTestInfo* item =
|
||||
MakeDisplayItem<nsDisplayCompositorHitTestInfo>(this, aFrame, info);
|
||||
const nsRect area = aFrame->GetCompositorHitTestArea(this);
|
||||
if (!aBuildNew &&
|
||||
GetHitTestInfo() == info &&
|
||||
GetHitTestArea().Contains(area)) {
|
||||
return;
|
||||
}
|
||||
|
||||
SetCompositorHitTestInfo(item);
|
||||
auto* item = MakeDisplayItem<nsDisplayCompositorHitTestInfo>(
|
||||
this, aFrame, info, 0, Some(area));
|
||||
|
||||
SetCompositorHitTestInfo(area, info);
|
||||
aList->AppendToTop(item);
|
||||
}
|
||||
|
||||
bool
|
||||
nsDisplayListBuilder::ShouldBuildCompositorHitTestInfo(
|
||||
const nsIFrame* aFrame,
|
||||
const CompositorHitTestInfo& aInfo,
|
||||
const bool aBuildNew) const
|
||||
{
|
||||
MOZ_ASSERT(mBuildCompositorHitTestInfo);
|
||||
|
||||
if (aInfo == CompositorHitTestInvisibleToHit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mCompositorHitTestInfo || !mLessEventRegionItems || aBuildNew) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mCompositorHitTestInfo->HitTestInfo() != aInfo) {
|
||||
// Hit test flags are different.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create a new item if the parent does not contain the child completely.
|
||||
return !mCompositorHitTestInfo->Area().Contains(GetFrameArea(this, aFrame));
|
||||
}
|
||||
|
||||
void
|
||||
nsDisplayListSet::MoveTo(const nsDisplayListSet& aDestination) const
|
||||
{
|
||||
|
@ -5335,11 +5287,10 @@ nsDisplayEventReceiver::CreateWebRenderCommands(
|
|||
nsDisplayCompositorHitTestInfo::nsDisplayCompositorHitTestInfo(
|
||||
nsDisplayListBuilder* aBuilder,
|
||||
nsIFrame* aFrame,
|
||||
mozilla::gfx::CompositorHitTestInfo aHitTestInfo,
|
||||
const mozilla::gfx::CompositorHitTestInfo& aHitTestFlags,
|
||||
uint32_t aIndex,
|
||||
const mozilla::Maybe<nsRect>& aArea)
|
||||
: nsDisplayEventReceiver(aBuilder, aFrame)
|
||||
, mHitTestInfo(aHitTestInfo)
|
||||
: nsDisplayHitTestInfoItem(aBuilder, aFrame)
|
||||
, mIndex(aIndex)
|
||||
, mAppUnitsPerDevPixel(mFrame->PresContext()->AppUnitsPerDevPixel())
|
||||
{
|
||||
|
@ -5348,20 +5299,39 @@ nsDisplayCompositorHitTestInfo::nsDisplayCompositorHitTestInfo(
|
|||
// compositor hit-test info or if the computed hit info indicated the
|
||||
// frame is invisible to hit-testing
|
||||
MOZ_ASSERT(aBuilder->BuildCompositorHitTestInfo());
|
||||
MOZ_ASSERT(mHitTestInfo != CompositorHitTestInvisibleToHit);
|
||||
MOZ_ASSERT(aHitTestFlags != CompositorHitTestInvisibleToHit);
|
||||
|
||||
const nsRect& area = aArea.isSome()
|
||||
? *aArea
|
||||
: aFrame->GetCompositorHitTestArea(aBuilder);
|
||||
|
||||
SetHitTestInfo(area, aHitTestFlags);
|
||||
InitializeScrollTarget(aBuilder);
|
||||
}
|
||||
|
||||
nsDisplayCompositorHitTestInfo::nsDisplayCompositorHitTestInfo(
|
||||
nsDisplayListBuilder* aBuilder,
|
||||
nsIFrame* aFrame,
|
||||
mozilla::UniquePtr<HitTestInfo>&& aHitTestInfo)
|
||||
: nsDisplayHitTestInfoItem(aBuilder, aFrame)
|
||||
, mIndex(0)
|
||||
, mAppUnitsPerDevPixel(mFrame->PresContext()->AppUnitsPerDevPixel())
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsDisplayCompositorHitTestInfo);
|
||||
SetHitTestInfo(std::move(aHitTestInfo));
|
||||
InitializeScrollTarget(aBuilder);
|
||||
}
|
||||
|
||||
void
|
||||
nsDisplayCompositorHitTestInfo::InitializeScrollTarget(
|
||||
nsDisplayListBuilder* aBuilder)
|
||||
{
|
||||
if (aBuilder->GetCurrentScrollbarDirection().isSome()) {
|
||||
// In the case of scrollbar frames, we use the scrollbar's target
|
||||
// scrollframe instead of the scrollframe with which the scrollbar actually
|
||||
// moves.
|
||||
MOZ_ASSERT(mHitTestInfo.contains(CompositorHitTestFlags::eScrollbar));
|
||||
mScrollTarget = Some(aBuilder->GetCurrentScrollbarTarget());
|
||||
}
|
||||
|
||||
if (aArea.isSome()) {
|
||||
mArea = *aArea;
|
||||
} else {
|
||||
mArea = GetFrameArea(aBuilder, aFrame);
|
||||
MOZ_ASSERT(HitTestFlags().contains(CompositorHitTestFlags::eScrollbar));
|
||||
mScrollTarget = mozilla::Some(aBuilder->GetCurrentScrollbarTarget());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5373,7 +5343,7 @@ nsDisplayCompositorHitTestInfo::CreateWebRenderCommands(
|
|||
mozilla::layers::WebRenderLayerManager* aManager,
|
||||
nsDisplayListBuilder* aDisplayListBuilder)
|
||||
{
|
||||
if (mArea.IsEmpty()) {
|
||||
if (HitTestArea().IsEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -5397,10 +5367,10 @@ nsDisplayCompositorHitTestInfo::CreateWebRenderCommands(
|
|||
});
|
||||
|
||||
// Insert a transparent rectangle with the hit-test info
|
||||
aBuilder.SetHitTestInfo(scrollId, mHitTestInfo);
|
||||
aBuilder.SetHitTestInfo(scrollId, HitTestFlags());
|
||||
|
||||
const LayoutDeviceRect devRect =
|
||||
LayoutDeviceRect::FromAppUnits(mArea, mAppUnitsPerDevPixel);
|
||||
LayoutDeviceRect::FromAppUnits(HitTestArea(), mAppUnitsPerDevPixel);
|
||||
|
||||
const wr::LayoutRect rect = wr::ToRoundedLayoutRect(devRect);
|
||||
|
||||
|
@ -5411,13 +5381,6 @@ nsDisplayCompositorHitTestInfo::CreateWebRenderCommands(
|
|||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
nsDisplayCompositorHitTestInfo::WriteDebugInfo(std::stringstream& aStream)
|
||||
{
|
||||
aStream << nsPrintfCString(" (hitTestInfo 0x%x)", mHitTestInfo.serialize()).get();
|
||||
AppendToString(aStream, mArea, " hitTestArea");
|
||||
}
|
||||
|
||||
uint32_t
|
||||
nsDisplayCompositorHitTestInfo::GetPerFrameKey() const
|
||||
{
|
||||
|
@ -6043,7 +6006,7 @@ nsDisplayWrapList::nsDisplayWrapList(
|
|||
bool aClearClipChain,
|
||||
uint32_t aIndex,
|
||||
bool aAnonymous)
|
||||
: nsDisplayItem(aBuilder, aFrame, aActiveScrolledRoot, aAnonymous)
|
||||
: nsDisplayHitTestInfoItem(aBuilder, aFrame, aActiveScrolledRoot, aAnonymous)
|
||||
, mFrameActiveScrolledRoot(aBuilder->CurrentActiveScrolledRoot())
|
||||
, mOverrideZIndex(0)
|
||||
, mIndex(aIndex)
|
||||
|
@ -6090,10 +6053,10 @@ nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
|
|||
nsIFrame* aFrame,
|
||||
nsDisplayItem* aItem,
|
||||
bool aAnonymous)
|
||||
: nsDisplayItem(aBuilder,
|
||||
aFrame,
|
||||
aBuilder->CurrentActiveScrolledRoot(),
|
||||
aAnonymous)
|
||||
: nsDisplayHitTestInfoItem(aBuilder,
|
||||
aFrame,
|
||||
aBuilder->CurrentActiveScrolledRoot(),
|
||||
aAnonymous)
|
||||
, mOverrideZIndex(0)
|
||||
, mIndex(0)
|
||||
, mHasZIndexOverride(false)
|
||||
|
@ -6750,6 +6713,7 @@ nsDisplayOpacity::CreateWebRenderCommands(
|
|||
|
||||
nsTArray<mozilla::wr::WrFilterOp> filters;
|
||||
StackingContextHelper sc(aSc,
|
||||
GetActiveScrolledRoot(),
|
||||
aBuilder,
|
||||
filters,
|
||||
LayoutDeviceRect(),
|
||||
|
@ -6803,6 +6767,7 @@ nsDisplayBlendMode::CreateWebRenderCommands(
|
|||
{
|
||||
nsTArray<mozilla::wr::WrFilterOp> filters;
|
||||
StackingContextHelper sc(aSc,
|
||||
GetActiveScrolledRoot(),
|
||||
aBuilder,
|
||||
filters,
|
||||
LayoutDeviceRect(),
|
||||
|
@ -6954,7 +6919,7 @@ nsDisplayBlendContainer::CreateWebRenderCommands(
|
|||
mozilla::layers::WebRenderLayerManager* aManager,
|
||||
nsDisplayListBuilder* aDisplayListBuilder)
|
||||
{
|
||||
StackingContextHelper sc(aSc, aBuilder);
|
||||
StackingContextHelper sc(aSc, GetActiveScrolledRoot(), aBuilder);
|
||||
|
||||
return nsDisplayWrapList::CreateWebRenderCommands(
|
||||
aBuilder, aResources, sc, aManager, aDisplayListBuilder);
|
||||
|
@ -7094,6 +7059,7 @@ nsDisplayOwnLayer::CreateWebRenderCommands(
|
|||
prop.effect_type = wr::WrAnimationType::Transform;
|
||||
|
||||
StackingContextHelper sc(aSc,
|
||||
GetActiveScrolledRoot(),
|
||||
aBuilder,
|
||||
nsTArray<wr::WrFilterOp>(),
|
||||
LayoutDeviceRect(),
|
||||
|
@ -7862,7 +7828,7 @@ nsDisplayStickyPosition::CreateWebRenderCommands(
|
|||
}
|
||||
|
||||
{
|
||||
StackingContextHelper sc(aSc, aBuilder);
|
||||
StackingContextHelper sc(aSc, GetActiveScrolledRoot(), aBuilder);
|
||||
nsDisplayWrapList::CreateWebRenderCommands(
|
||||
aBuilder, aResources, sc, aManager, aDisplayListBuilder);
|
||||
}
|
||||
|
@ -8071,7 +8037,7 @@ nsDisplayTransform::nsDisplayTransform(
|
|||
const nsRect& aChildrenBuildingRect,
|
||||
ComputeTransformFunction aTransformGetter,
|
||||
uint32_t aIndex)
|
||||
: nsDisplayItem(aBuilder, aFrame)
|
||||
: nsDisplayHitTestInfoItem(aBuilder, aFrame)
|
||||
, mStoredList(aBuilder, aFrame, aList)
|
||||
, mTransformGetter(aTransformGetter)
|
||||
, mAnimatedGeometryRootForChildren(mAnimatedGeometryRoot)
|
||||
|
@ -8140,7 +8106,7 @@ nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
|
|||
const nsRect& aChildrenBuildingRect,
|
||||
uint32_t aIndex,
|
||||
bool aAllowAsyncAnimation)
|
||||
: nsDisplayItem(aBuilder, aFrame)
|
||||
: nsDisplayHitTestInfoItem(aBuilder, aFrame)
|
||||
, mStoredList(aBuilder, aFrame, aList)
|
||||
, mTransformGetter(nullptr)
|
||||
, mAnimatedGeometryRootForChildren(mAnimatedGeometryRoot)
|
||||
|
@ -8165,7 +8131,7 @@ nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
|
|||
const nsRect& aChildrenBuildingRect,
|
||||
const Matrix4x4& aTransform,
|
||||
uint32_t aIndex)
|
||||
: nsDisplayItem(aBuilder, aFrame)
|
||||
: nsDisplayHitTestInfoItem(aBuilder, aFrame)
|
||||
, mStoredList(aBuilder, aFrame, aList)
|
||||
, mTransform(Some(aTransform))
|
||||
, mTransformGetter(nullptr)
|
||||
|
@ -8849,6 +8815,7 @@ nsDisplayTransform::CreateWebRenderCommands(
|
|||
ActiveLayerTracker::IsStyleMaybeAnimated(Frame(), eCSSProperty_transform);
|
||||
|
||||
StackingContextHelper sc(aSc,
|
||||
GetActiveScrolledRoot(),
|
||||
aBuilder,
|
||||
filters,
|
||||
LayoutDeviceRect(position, LayoutDeviceSize()),
|
||||
|
@ -9380,7 +9347,7 @@ nsDisplayTransform::WriteDebugInfo(std::stringstream& aStream)
|
|||
nsDisplayPerspective::nsDisplayPerspective(nsDisplayListBuilder* aBuilder,
|
||||
nsIFrame* aFrame,
|
||||
nsDisplayList* aList)
|
||||
: nsDisplayItem(aBuilder, aFrame)
|
||||
: nsDisplayHitTestInfoItem(aBuilder, aFrame)
|
||||
, mList(aBuilder, aFrame, aList, true)
|
||||
{
|
||||
MOZ_ASSERT(mList.GetChildren()->Count() == 1);
|
||||
|
@ -9498,6 +9465,7 @@ nsDisplayPerspective::CreateWebRenderCommands(
|
|||
|
||||
nsTArray<mozilla::wr::WrFilterOp> filters;
|
||||
StackingContextHelper sc(aSc,
|
||||
GetActiveScrolledRoot(),
|
||||
aBuilder,
|
||||
filters,
|
||||
LayoutDeviceRect(),
|
||||
|
@ -10184,6 +10152,7 @@ nsDisplayMasksAndClipPaths::CreateWebRenderCommands(
|
|||
: Nothing();
|
||||
|
||||
layer.emplace(aSc,
|
||||
GetActiveScrolledRoot(),
|
||||
aBuilder,
|
||||
/*aFilters: */ nsTArray<wr::WrFilterOp>(),
|
||||
/*aBounds: */ bounds,
|
||||
|
@ -10498,6 +10467,7 @@ nsDisplayFilters::CreateWebRenderCommands(
|
|||
|
||||
float opacity = mFrame->StyleEffects()->mOpacity;
|
||||
StackingContextHelper sc(aSc,
|
||||
GetActiveScrolledRoot(),
|
||||
aBuilder,
|
||||
wrFilters,
|
||||
LayoutDeviceRect(),
|
||||
|
|
|
@ -474,6 +474,7 @@ public:
|
|||
typedef mozilla::layers::Layer Layer;
|
||||
typedef mozilla::layers::FrameMetrics FrameMetrics;
|
||||
typedef mozilla::layers::FrameMetrics::ViewID ViewID;
|
||||
typedef mozilla::gfx::CompositorHitTestInfo CompositorHitTestInfo;
|
||||
typedef mozilla::gfx::Matrix4x4 Matrix4x4;
|
||||
typedef mozilla::Maybe<mozilla::layers::ScrollDirection> MaybeScrollDirection;
|
||||
|
||||
|
@ -795,19 +796,20 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets the current compositor hit test info to |aHitTestInfo|.
|
||||
* Sets the current compositor hit test area and info to |aHitTestArea| and
|
||||
* |aHitTestInfo|.
|
||||
* This is used during display list building to determine if the parent frame
|
||||
* hit test info contains the same information that child frame needs.
|
||||
*/
|
||||
void SetCompositorHitTestInfo(nsDisplayCompositorHitTestInfo* aHitTestInfo)
|
||||
void SetCompositorHitTestInfo(const nsRect& aHitTestArea,
|
||||
const CompositorHitTestInfo& aHitTestInfo)
|
||||
{
|
||||
mCompositorHitTestInfo = aHitTestInfo;
|
||||
mHitTestArea = aHitTestArea;
|
||||
mHitTestInfo = aHitTestInfo;
|
||||
}
|
||||
|
||||
nsDisplayCompositorHitTestInfo* GetCompositorHitTestInfo() const
|
||||
{
|
||||
return mCompositorHitTestInfo;
|
||||
}
|
||||
const nsRect& GetHitTestArea() const { return mHitTestArea; }
|
||||
const CompositorHitTestInfo& GetHitTestInfo() const { return mHitTestInfo; }
|
||||
|
||||
/**
|
||||
* Builds a new nsDisplayCompositorHitTestInfo for the frame |aFrame| if
|
||||
|
@ -1171,7 +1173,8 @@ public:
|
|||
: mBuilder(aBuilder)
|
||||
, mPrevFrame(aBuilder->mCurrentFrame)
|
||||
, mPrevReferenceFrame(aBuilder->mCurrentReferenceFrame)
|
||||
, mPrevCompositorHitTestInfo(aBuilder->mCompositorHitTestInfo)
|
||||
, mPrevHitTestArea(aBuilder->mHitTestArea)
|
||||
, mPrevHitTestInfo(aBuilder->mHitTestInfo)
|
||||
, mPrevOffset(aBuilder->mCurrentOffsetToReferenceFrame)
|
||||
, mPrevVisibleRect(aBuilder->mVisibleRect)
|
||||
, mPrevDirtyRect(aBuilder->mDirtyRect)
|
||||
|
@ -1237,7 +1240,8 @@ public:
|
|||
{
|
||||
mBuilder->mCurrentFrame = mPrevFrame;
|
||||
mBuilder->mCurrentReferenceFrame = mPrevReferenceFrame;
|
||||
mBuilder->mCompositorHitTestInfo = mPrevCompositorHitTestInfo;
|
||||
mBuilder->mHitTestArea = mPrevHitTestArea;
|
||||
mBuilder->mHitTestInfo = mPrevHitTestInfo;
|
||||
mBuilder->mCurrentOffsetToReferenceFrame = mPrevOffset;
|
||||
mBuilder->mVisibleRect = mPrevVisibleRect;
|
||||
mBuilder->mDirtyRect = mPrevDirtyRect;
|
||||
|
@ -1255,7 +1259,8 @@ public:
|
|||
AGRState mCurrentAGRState;
|
||||
const nsIFrame* mPrevFrame;
|
||||
const nsIFrame* mPrevReferenceFrame;
|
||||
nsDisplayCompositorHitTestInfo* mPrevCompositorHitTestInfo;
|
||||
nsRect mPrevHitTestArea;
|
||||
CompositorHitTestInfo mPrevHitTestInfo;
|
||||
nsPoint mPrevOffset;
|
||||
nsRect mPrevVisibleRect;
|
||||
nsRect mPrevDirtyRect;
|
||||
|
@ -1947,16 +1952,6 @@ private:
|
|||
*/
|
||||
nsIFrame* FindAnimatedGeometryRootFrameFor(nsIFrame* aFrame, bool& aIsAsync);
|
||||
|
||||
/**
|
||||
* Returns true if nsDisplayCompositorHitTestInfo item should be build for
|
||||
* |aFrame|. Otherwise returns false. If |aBuildNew| is true, reusing the
|
||||
* previous hit test info will not be considered.
|
||||
*/
|
||||
bool ShouldBuildCompositorHitTestInfo(
|
||||
const nsIFrame* aFrame,
|
||||
const mozilla::gfx::CompositorHitTestInfo& aInfo,
|
||||
const bool aBuildNew) const;
|
||||
|
||||
friend class nsDisplayCanvasBackgroundImage;
|
||||
friend class nsDisplayBackgroundImage;
|
||||
friend class nsDisplayFixedPosition;
|
||||
|
@ -1966,6 +1961,7 @@ private:
|
|||
friend class nsDisplayItem;
|
||||
friend class nsDisplayOwnLayer;
|
||||
friend struct RetainedDisplayListBuilder;
|
||||
friend struct HitTestInfo;
|
||||
AnimatedGeometryRoot* FindAnimatedGeometryRootFor(nsIFrame* aFrame);
|
||||
|
||||
AnimatedGeometryRoot* WrapAGRForFrame(
|
||||
|
@ -2038,8 +2034,6 @@ private:
|
|||
|
||||
nsIFrame* const mReferenceFrame;
|
||||
nsIFrame* mIgnoreScrollFrame;
|
||||
nsDisplayCompositorHitTestInfo* mCompositorHitTestInfo;
|
||||
|
||||
nsPresArena mPool;
|
||||
|
||||
RefPtr<mozilla::dom::Selection> mBoundingSelection;
|
||||
|
@ -2158,10 +2152,12 @@ private:
|
|||
bool mIsBuilding;
|
||||
bool mInInvalidSubtree;
|
||||
bool mBuildCompositorHitTestInfo;
|
||||
bool mLessEventRegionItems;
|
||||
bool mDisablePartialUpdates;
|
||||
bool mPartialBuildFailed;
|
||||
bool mIsInActiveDocShell;
|
||||
|
||||
nsRect mHitTestArea;
|
||||
CompositorHitTestInfo mHitTestInfo;
|
||||
};
|
||||
|
||||
class nsDisplayItem;
|
||||
|
@ -2261,6 +2257,7 @@ public:
|
|||
typedef mozilla::image::imgDrawingParams imgDrawingParams;
|
||||
typedef mozilla::image::ImgDrawResult ImgDrawResult;
|
||||
typedef class mozilla::gfx::DrawTarget DrawTarget;
|
||||
typedef mozilla::gfx::CompositorHitTestInfo CompositorHitTestInfo;
|
||||
|
||||
// This is never instantiated directly (it has pure virtual methods), so no
|
||||
// need to count constructors and destructors.
|
||||
|
@ -3142,6 +3139,12 @@ public:
|
|||
|
||||
const nsRect& GetPaintRect() const { return mPaintRect; }
|
||||
|
||||
virtual bool HasHitTestInfo() const { return false; }
|
||||
|
||||
#ifdef DEBUG
|
||||
virtual bool IsHitTestItem() const { return false; }
|
||||
#endif
|
||||
|
||||
protected:
|
||||
typedef bool (*PrefFunc)(void);
|
||||
bool ShouldUseAdvancedLayer(LayerManager* aManager, PrefFunc aFunc) const;
|
||||
|
@ -3812,7 +3815,7 @@ protected:
|
|||
if (AtEndOfNestedList()) {
|
||||
// Pop the last item off the stack.
|
||||
mNext = mStack.LastElement();
|
||||
EndNested(mNext);
|
||||
ExitChildList(mNext);
|
||||
mStack.RemoveElementAt(mStack.Length() - 1);
|
||||
// We stored the item that was flattened, so advance to the next.
|
||||
mNext = mNext->GetAbove();
|
||||
|
@ -3820,7 +3823,7 @@ protected:
|
|||
// This item wants to be flattened. Store the current item on the stack,
|
||||
// and use the first item in the child list instead.
|
||||
mStack.AppendElement(mNext);
|
||||
StartNested(mNext);
|
||||
EnterChildList(mNext);
|
||||
|
||||
nsDisplayList* childItems =
|
||||
mNext->GetType() != DisplayItemType::TYPE_TRANSFORM
|
||||
|
@ -3832,14 +3835,117 @@ protected:
|
|||
}
|
||||
}
|
||||
|
||||
virtual void EndNested(nsDisplayItem* aItem) {}
|
||||
virtual void StartNested(nsDisplayItem* aItem) {}
|
||||
virtual void ExitChildList(nsDisplayItem* aItem) {}
|
||||
virtual void EnterChildList(nsDisplayItem* aItem) {}
|
||||
|
||||
nsDisplayListBuilder* mBuilder;
|
||||
nsDisplayItem* mNext;
|
||||
AutoTArray<nsDisplayItem*, 10> mStack;
|
||||
};
|
||||
|
||||
struct HitTestInfo
|
||||
{
|
||||
HitTestInfo(nsDisplayListBuilder* aBuilder,
|
||||
nsIFrame* aFrame,
|
||||
const mozilla::gfx::CompositorHitTestInfo& aHitTestFlags)
|
||||
: mArea(aFrame->GetCompositorHitTestArea(aBuilder))
|
||||
, mFlags(aHitTestFlags)
|
||||
, mAGR(aBuilder->FindAnimatedGeometryRootFor(aFrame))
|
||||
, mASR(aBuilder->CurrentActiveScrolledRoot())
|
||||
, mClipChain(aBuilder->ClipState().GetCurrentCombinedClipChain(aBuilder))
|
||||
, mClip(mozilla::DisplayItemClipChain::ClipForASR(mClipChain, mASR))
|
||||
{
|
||||
}
|
||||
|
||||
HitTestInfo(const nsRect& aArea,
|
||||
const mozilla::gfx::CompositorHitTestInfo& aHitTestFlags)
|
||||
: mArea(aArea)
|
||||
, mFlags(aHitTestFlags)
|
||||
, mAGR(nullptr)
|
||||
, mASR(nullptr)
|
||||
, mClipChain(nullptr)
|
||||
, mClip(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
nsRect mArea;
|
||||
mozilla::gfx::CompositorHitTestInfo mFlags;
|
||||
|
||||
AnimatedGeometryRoot* mAGR;
|
||||
const mozilla::ActiveScrolledRoot* mASR;
|
||||
RefPtr<const mozilla::DisplayItemClipChain> mClipChain;
|
||||
const mozilla::DisplayItemClip* mClip;
|
||||
};
|
||||
|
||||
class nsDisplayHitTestInfoItem : public nsDisplayItem
|
||||
{
|
||||
public:
|
||||
nsDisplayHitTestInfoItem(nsDisplayListBuilder* aBuilder,
|
||||
nsIFrame* aFrame)
|
||||
: nsDisplayItem(aBuilder, aFrame)
|
||||
{
|
||||
}
|
||||
|
||||
nsDisplayHitTestInfoItem(nsDisplayListBuilder* aBuilder,
|
||||
nsIFrame* aFrame,
|
||||
const ActiveScrolledRoot* aActiveScrolledRoot,
|
||||
bool aAnonymous = false)
|
||||
: nsDisplayItem(aBuilder, aFrame, aActiveScrolledRoot, aAnonymous)
|
||||
{
|
||||
}
|
||||
|
||||
nsDisplayHitTestInfoItem(nsDisplayListBuilder* aBuilder,
|
||||
const nsDisplayHitTestInfoItem& aOther)
|
||||
: nsDisplayItem(aBuilder, aOther)
|
||||
{
|
||||
}
|
||||
|
||||
const HitTestInfo& GetHitTestInfo() const
|
||||
{
|
||||
return *mHitTestInfo;
|
||||
}
|
||||
|
||||
void SetHitTestInfo(mozilla::UniquePtr<HitTestInfo>&& aHitTestInfo)
|
||||
{
|
||||
MOZ_ASSERT(aHitTestInfo);
|
||||
MOZ_ASSERT(aHitTestInfo->mFlags !=
|
||||
mozilla::gfx::CompositorHitTestInvisibleToHit);
|
||||
|
||||
mHitTestInfo = std::move(aHitTestInfo);
|
||||
}
|
||||
|
||||
void SetHitTestInfo(const nsRect& aArea,
|
||||
const mozilla::gfx::CompositorHitTestInfo& aHitTestFlags)
|
||||
{
|
||||
MOZ_ASSERT(aHitTestFlags != mozilla::gfx::CompositorHitTestInvisibleToHit);
|
||||
|
||||
mHitTestInfo = mozilla::MakeUnique<HitTestInfo>(aArea, aHitTestFlags);
|
||||
mHitTestInfo->mAGR = mAnimatedGeometryRoot;
|
||||
mHitTestInfo->mASR = mActiveScrolledRoot;
|
||||
mHitTestInfo->mClipChain = mClipChain;
|
||||
mHitTestInfo->mClip = mClip;
|
||||
}
|
||||
|
||||
const nsRect& HitTestArea() const
|
||||
{
|
||||
return mHitTestInfo->mArea;
|
||||
}
|
||||
|
||||
const mozilla::gfx::CompositorHitTestInfo& HitTestFlags() const
|
||||
{
|
||||
return mHitTestInfo->mFlags;
|
||||
}
|
||||
|
||||
bool HasHitTestInfo() const override { return mHitTestInfo.get(); }
|
||||
|
||||
#ifdef DEBUG
|
||||
bool IsHitTestItem() const override { return true; }
|
||||
#endif
|
||||
|
||||
protected:
|
||||
mozilla::UniquePtr<HitTestInfo> mHitTestInfo;
|
||||
};
|
||||
|
||||
class nsDisplayImageContainer : public nsDisplayItem
|
||||
{
|
||||
public:
|
||||
|
@ -5262,16 +5368,21 @@ public:
|
|||
* compositor some hit-test info for a frame. This is effectively a dummy item
|
||||
* whose sole purpose is to carry the hit-test info to the compositor.
|
||||
*/
|
||||
class nsDisplayCompositorHitTestInfo : public nsDisplayEventReceiver
|
||||
class nsDisplayCompositorHitTestInfo : public nsDisplayHitTestInfoItem
|
||||
{
|
||||
public:
|
||||
nsDisplayCompositorHitTestInfo(
|
||||
nsDisplayListBuilder* aBuilder,
|
||||
nsIFrame* aFrame,
|
||||
mozilla::gfx::CompositorHitTestInfo aHitTestInfo,
|
||||
const mozilla::gfx::CompositorHitTestInfo& aHitTestFlags,
|
||||
uint32_t aIndex = 0,
|
||||
const mozilla::Maybe<nsRect>& aArea = mozilla::Nothing());
|
||||
|
||||
nsDisplayCompositorHitTestInfo(
|
||||
nsDisplayListBuilder* aBuilder,
|
||||
nsIFrame* aFrame,
|
||||
mozilla::UniquePtr<HitTestInfo>&& aHitTestInfo);
|
||||
|
||||
#ifdef NS_BUILD_REFCNT_LOGGING
|
||||
~nsDisplayCompositorHitTestInfo() override
|
||||
{
|
||||
|
@ -5281,10 +5392,7 @@ public:
|
|||
|
||||
NS_DISPLAY_DECL_NAME("CompositorHitTestInfo", TYPE_COMPOSITOR_HITTEST_INFO)
|
||||
|
||||
mozilla::gfx::CompositorHitTestInfo HitTestInfo() const
|
||||
{
|
||||
return mHitTestInfo;
|
||||
}
|
||||
void InitializeScrollTarget(nsDisplayListBuilder* aBuilder);
|
||||
|
||||
bool CreateWebRenderCommands(
|
||||
mozilla::wr::DisplayListBuilder& aBuilder,
|
||||
|
@ -5292,16 +5400,10 @@ public:
|
|||
const StackingContextHelper& aSc,
|
||||
mozilla::layers::WebRenderLayerManager* aManager,
|
||||
nsDisplayListBuilder* aDisplayListBuilder) override;
|
||||
void WriteDebugInfo(std::stringstream& aStream) override;
|
||||
uint32_t GetPerFrameKey() const override;
|
||||
int32_t ZIndex() const override;
|
||||
void SetOverrideZIndex(int32_t aZIndex);
|
||||
|
||||
/**
|
||||
* Returns the hit test area of this item.
|
||||
*/
|
||||
const nsRect& Area() const { return mArea; }
|
||||
|
||||
/**
|
||||
* ApplyOpacity() is overriden for opacity flattening.
|
||||
*/
|
||||
|
@ -5323,9 +5425,7 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
mozilla::gfx::CompositorHitTestInfo mHitTestInfo;
|
||||
mozilla::Maybe<mozilla::layers::FrameMetrics::ViewID> mScrollTarget;
|
||||
nsRect mArea;
|
||||
uint32_t mIndex;
|
||||
mozilla::Maybe<int32_t> mOverrideZIndex;
|
||||
int32_t mAppUnitsPerDevPixel;
|
||||
|
@ -5345,7 +5445,7 @@ private:
|
|||
* we allow the frame to be nullptr. Callers to GetUnderlyingFrame must
|
||||
* detect and handle this case.
|
||||
*/
|
||||
class nsDisplayWrapList : public nsDisplayItem
|
||||
class nsDisplayWrapList : public nsDisplayHitTestInfoItem
|
||||
{
|
||||
public:
|
||||
/**
|
||||
|
@ -5367,7 +5467,7 @@ public:
|
|||
nsDisplayItem* aItem,
|
||||
bool aAnonymous = false);
|
||||
nsDisplayWrapList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
|
||||
: nsDisplayItem(aBuilder, aFrame)
|
||||
: nsDisplayHitTestInfoItem(aBuilder, aFrame)
|
||||
, mFrameActiveScrolledRoot(aBuilder->CurrentActiveScrolledRoot())
|
||||
, mOverrideZIndex(0)
|
||||
, mIndex(0)
|
||||
|
@ -5387,7 +5487,7 @@ public:
|
|||
nsDisplayWrapList(const nsDisplayWrapList& aOther) = delete;
|
||||
nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
|
||||
const nsDisplayWrapList& aOther)
|
||||
: nsDisplayItem(aBuilder, aOther)
|
||||
: nsDisplayHitTestInfoItem(aBuilder, aOther)
|
||||
, mListPtr(&mList)
|
||||
, mFrameActiveScrolledRoot(aOther.mFrameActiveScrolledRoot)
|
||||
, mMergedFrames(aOther.mMergedFrames)
|
||||
|
@ -6928,7 +7028,7 @@ private:
|
|||
* function.
|
||||
* INVARIANT: The wrapped frame is non-null.
|
||||
*/
|
||||
class nsDisplayTransform : public nsDisplayItem
|
||||
class nsDisplayTransform : public nsDisplayHitTestInfoItem
|
||||
{
|
||||
typedef mozilla::gfx::Matrix4x4 Matrix4x4;
|
||||
typedef mozilla::gfx::Matrix4x4Flagged Matrix4x4Flagged;
|
||||
|
@ -7437,7 +7537,7 @@ private:
|
|||
* perspective-origin is relative to an ancestor of the transformed frame, and
|
||||
* APZ can scroll the child separately.
|
||||
*/
|
||||
class nsDisplayPerspective : public nsDisplayItem
|
||||
class nsDisplayPerspective : public nsDisplayHitTestInfoItem
|
||||
{
|
||||
typedef mozilla::gfx::Point3D Point3D;
|
||||
|
||||
|
|
|
@ -603,6 +603,10 @@ bool Servo_ParseEasing(
|
|||
RawGeckoURLExtraData* data,
|
||||
nsTimingFunctionBorrowedMut output);
|
||||
|
||||
void Servo_SerializeEasing(
|
||||
nsTimingFunctionBorrowed easing,
|
||||
nsAString* output);
|
||||
|
||||
void Servo_GetComputedKeyframeValues(
|
||||
RawGeckoKeyframeListBorrowed keyframes,
|
||||
RawGeckoElementBorrowed element,
|
||||
|
|
|
@ -103,6 +103,7 @@ SERIALIZED_PREDEFINED_TYPES = [
|
|||
"Opacity",
|
||||
"Resize",
|
||||
"TextAlign",
|
||||
"TimingFunction",
|
||||
"TransformStyle",
|
||||
"background::BackgroundSize",
|
||||
"basic_shape::ClippingShape",
|
||||
|
|
|
@ -105,10 +105,6 @@ CSS_KEY(double, double)
|
|||
CSS_KEY(double-circle, double_circle)
|
||||
CSS_KEY(drop-shadow, drop_shadow)
|
||||
CSS_KEY(e-resize, e_resize)
|
||||
CSS_KEY(ease, ease)
|
||||
CSS_KEY(ease-in, ease_in)
|
||||
CSS_KEY(ease-in-out, ease_in_out)
|
||||
CSS_KEY(ease-out, ease_out)
|
||||
CSS_KEY(ellipse, ellipse)
|
||||
CSS_KEY(ellipsis, ellipsis)
|
||||
CSS_KEY(end, end)
|
||||
|
@ -144,7 +140,6 @@ CSS_KEY(layout, layout)
|
|||
CSS_KEY(left, left)
|
||||
CSS_KEY(legacy, legacy)
|
||||
CSS_KEY(line-through, line_through)
|
||||
CSS_KEY(linear, linear)
|
||||
CSS_KEY(list-item, list_item)
|
||||
CSS_KEY(mandatory, mandatory)
|
||||
CSS_KEY(manipulation, manipulation)
|
||||
|
|
|
@ -618,15 +618,6 @@ const KTableEntry nsCSSProps::kTouchActionKTable[] = {
|
|||
{ eCSSKeyword_UNKNOWN, -1 }
|
||||
};
|
||||
|
||||
const KTableEntry nsCSSProps::kTransitionTimingFunctionKTable[] = {
|
||||
{ eCSSKeyword_linear, StyleTimingKeyword::Linear },
|
||||
{ eCSSKeyword_ease, StyleTimingKeyword::Ease },
|
||||
{ eCSSKeyword_ease_in, StyleTimingKeyword::EaseIn },
|
||||
{ eCSSKeyword_ease_out, StyleTimingKeyword::EaseOut },
|
||||
{ eCSSKeyword_ease_in_out, StyleTimingKeyword::EaseInOut },
|
||||
{ eCSSKeyword_UNKNOWN, -1 }
|
||||
};
|
||||
|
||||
const KTableEntry nsCSSProps::kVerticalAlignKTable[] = {
|
||||
{ eCSSKeyword_baseline, NS_STYLE_VERTICAL_ALIGN_BASELINE },
|
||||
{ eCSSKeyword_sub, NS_STYLE_VERTICAL_ALIGN_SUB },
|
||||
|
|
|
@ -351,7 +351,6 @@ public:
|
|||
static const KTableEntry kTextEmphasisStyleShapeKTable[];
|
||||
static const KTableEntry kTextOverflowKTable[];
|
||||
static const KTableEntry kTouchActionKTable[];
|
||||
static const KTableEntry kTransitionTimingFunctionKTable[];
|
||||
static const KTableEntry kVerticalAlignKTable[];
|
||||
static const KTableEntry kWidthKTable[]; // also min-width, max-width
|
||||
static const KTableEntry kFlexBasisKTable[];
|
||||
|
|
|
@ -4576,56 +4576,6 @@ nsComputedDOMStyle::DoGetTransitionProperty()
|
|||
return valueList.forget();
|
||||
}
|
||||
|
||||
void
|
||||
nsComputedDOMStyle::AppendTimingFunction(nsDOMCSSValueList *aValueList,
|
||||
const nsTimingFunction& aTimingFunction)
|
||||
{
|
||||
RefPtr<nsROCSSPrimitiveValue> timingFunction = new nsROCSSPrimitiveValue;
|
||||
|
||||
nsAutoString tmp;
|
||||
switch (aTimingFunction.mTiming.tag) {
|
||||
case StyleComputedTimingFunction::Tag::CubicBezier:
|
||||
nsStyleUtil::AppendCubicBezierTimingFunction(
|
||||
aTimingFunction.mTiming.cubic_bezier.x1,
|
||||
aTimingFunction.mTiming.cubic_bezier.y1,
|
||||
aTimingFunction.mTiming.cubic_bezier.x2,
|
||||
aTimingFunction.mTiming.cubic_bezier.y2,
|
||||
tmp);
|
||||
break;
|
||||
case StyleComputedTimingFunction::Tag::Steps:
|
||||
nsStyleUtil::AppendStepsTimingFunction(
|
||||
aTimingFunction.mTiming.steps._0,
|
||||
aTimingFunction.mTiming.steps._1,
|
||||
tmp);
|
||||
break;
|
||||
case StyleComputedTimingFunction::Tag::Keyword:
|
||||
nsStyleUtil::AppendCubicBezierKeywordTimingFunction(
|
||||
aTimingFunction.mTiming.keyword._0,
|
||||
tmp);
|
||||
break;
|
||||
}
|
||||
timingFunction->SetString(tmp);
|
||||
aValueList->AppendCSSValue(timingFunction.forget());
|
||||
}
|
||||
|
||||
already_AddRefed<CSSValue>
|
||||
nsComputedDOMStyle::DoGetTransitionTimingFunction()
|
||||
{
|
||||
const nsStyleDisplay* display = StyleDisplay();
|
||||
|
||||
RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(true);
|
||||
|
||||
MOZ_ASSERT(display->mTransitionTimingFunctionCount > 0,
|
||||
"first item must be explicit");
|
||||
uint32_t i = 0;
|
||||
do {
|
||||
AppendTimingFunction(valueList,
|
||||
display->mTransitions[i].GetTimingFunction());
|
||||
} while (++i < display->mTransitionTimingFunctionCount);
|
||||
|
||||
return valueList.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<CSSValue>
|
||||
nsComputedDOMStyle::DoGetAnimationName()
|
||||
{
|
||||
|
@ -4696,24 +4646,6 @@ nsComputedDOMStyle::DoGetAnimationDuration()
|
|||
return valueList.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<CSSValue>
|
||||
nsComputedDOMStyle::DoGetAnimationTimingFunction()
|
||||
{
|
||||
const nsStyleDisplay* display = StyleDisplay();
|
||||
|
||||
RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(true);
|
||||
|
||||
MOZ_ASSERT(display->mAnimationTimingFunctionCount > 0,
|
||||
"first item must be explicit");
|
||||
uint32_t i = 0;
|
||||
do {
|
||||
AppendTimingFunction(valueList,
|
||||
display->mAnimations[i].GetTimingFunction());
|
||||
} while (++i < display->mAnimationTimingFunctionCount);
|
||||
|
||||
return valueList.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<CSSValue>
|
||||
nsComputedDOMStyle::DoGetAnimationIterationCount()
|
||||
{
|
||||
|
|
|
@ -48,7 +48,6 @@ struct nsStyleFilter;
|
|||
class nsStyleGradient;
|
||||
struct nsStyleImage;
|
||||
class nsStyleSides;
|
||||
struct nsTimingFunction;
|
||||
|
||||
class nsComputedDOMStyle final : public nsDOMCSSDeclaration
|
||||
, public nsStubMutationObserver
|
||||
|
@ -231,8 +230,6 @@ private:
|
|||
const nsStyleSides& aCropRect,
|
||||
nsString& aString);
|
||||
already_AddRefed<CSSValue> GetScrollSnapPoints(const nsStyleCoord& aCoord);
|
||||
void AppendTimingFunction(nsDOMCSSValueList *aValueList,
|
||||
const nsTimingFunction& aTimingFunction);
|
||||
|
||||
bool ShouldHonorMinSizeAutoInAxis(mozilla::PhysicalAxis aAxis);
|
||||
|
||||
|
@ -402,13 +399,11 @@ private:
|
|||
already_AddRefed<CSSValue> DoGetTransitionProperty();
|
||||
already_AddRefed<CSSValue> DoGetTransitionDuration();
|
||||
already_AddRefed<CSSValue> DoGetTransitionDelay();
|
||||
already_AddRefed<CSSValue> DoGetTransitionTimingFunction();
|
||||
|
||||
/* CSS Animations */
|
||||
already_AddRefed<CSSValue> DoGetAnimationName();
|
||||
already_AddRefed<CSSValue> DoGetAnimationDuration();
|
||||
already_AddRefed<CSSValue> DoGetAnimationDelay();
|
||||
already_AddRefed<CSSValue> DoGetAnimationTimingFunction();
|
||||
already_AddRefed<CSSValue> DoGetAnimationIterationCount();
|
||||
|
||||
/* CSS Flexbox properties */
|
||||
|
|
|
@ -274,77 +274,6 @@ nsStyleUtil::AppendPaintOrderValue(uint8_t aValue,
|
|||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
nsStyleUtil::AppendStepsTimingFunction(uint32_t aStepNumber,
|
||||
mozilla::StyleStepPosition aStepPos,
|
||||
nsAString& aResult)
|
||||
{
|
||||
aResult.AppendLiteral("steps(");
|
||||
aResult.AppendInt(aStepNumber);
|
||||
switch (aStepPos) {
|
||||
case StyleStepPosition::JumpStart:
|
||||
aResult.AppendLiteral(", jump-start)");
|
||||
break;
|
||||
case StyleStepPosition::JumpNone:
|
||||
aResult.AppendLiteral(", jump-none)");
|
||||
break;
|
||||
case StyleStepPosition::JumpBoth:
|
||||
aResult.AppendLiteral(", jump-both)");
|
||||
break;
|
||||
case StyleStepPosition::Start:
|
||||
aResult.AppendLiteral(", start)");
|
||||
break;
|
||||
case StyleStepPosition::JumpEnd:
|
||||
case StyleStepPosition::End:
|
||||
aResult.AppendLiteral(")");
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Unsupported timing function");
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
nsStyleUtil::AppendCubicBezierTimingFunction(float aX1, float aY1,
|
||||
float aX2, float aY2,
|
||||
nsAString& aResult)
|
||||
{
|
||||
// set the value from the cubic-bezier control points
|
||||
// (We could try to regenerate the keywords if we want.)
|
||||
aResult.AppendLiteral("cubic-bezier(");
|
||||
aResult.AppendFloat(aX1);
|
||||
aResult.AppendLiteral(", ");
|
||||
aResult.AppendFloat(aY1);
|
||||
aResult.AppendLiteral(", ");
|
||||
aResult.AppendFloat(aX2);
|
||||
aResult.AppendLiteral(", ");
|
||||
aResult.AppendFloat(aY2);
|
||||
aResult.Append(')');
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
nsStyleUtil::AppendCubicBezierKeywordTimingFunction(
|
||||
StyleTimingKeyword aType,
|
||||
nsAString& aResult)
|
||||
{
|
||||
switch (aType) {
|
||||
case StyleTimingKeyword::Linear:
|
||||
case StyleTimingKeyword::Ease:
|
||||
case StyleTimingKeyword::EaseIn:
|
||||
case StyleTimingKeyword::EaseOut:
|
||||
case StyleTimingKeyword::EaseInOut: {
|
||||
nsCSSKeyword keyword = nsCSSProps::ValueToKeywordEnum(
|
||||
static_cast<int32_t>(aType),
|
||||
nsCSSProps::kTransitionTimingFunctionKTable);
|
||||
AppendASCIItoUTF16(nsCSSKeywords::GetStringValue(keyword),
|
||||
aResult);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("unexpected aType");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ float
|
||||
nsStyleUtil::ColorComponentToFloat(uint8_t aAlpha)
|
||||
{
|
||||
|
|
|
@ -76,16 +76,6 @@ public:
|
|||
aResult.AppendFloat(aNumber);
|
||||
}
|
||||
|
||||
static void AppendStepsTimingFunction(uint32_t aStepNumber,
|
||||
mozilla::StyleStepPosition aStepPos,
|
||||
nsAString& aResult);
|
||||
static void AppendCubicBezierTimingFunction(float aX1, float aY1,
|
||||
float aX2, float aY2,
|
||||
nsAString& aResult);
|
||||
static void AppendCubicBezierKeywordTimingFunction(
|
||||
mozilla::StyleTimingKeyword aType,
|
||||
nsAString& aResult);
|
||||
|
||||
/*
|
||||
* Convert an author-provided floating point number to an integer (0
|
||||
* ... 255) appropriate for use in the alpha component of a color.
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
<html>
|
||||
<head><title>Fullscreen</title></head>
|
||||
<body>
|
||||
<div id="fullscreen">Fullscreen Div</div>
|
||||
</body>
|
||||
</html>
|
|
@ -41,6 +41,7 @@ open class BaseSessionTest(noErrorCollector: Boolean = false) {
|
|||
const val TITLE_CHANGE_HTML_PATH = "/assets/www/titleChange.html"
|
||||
const val TRACKERS_PATH = "/assets/www/trackers.html"
|
||||
const val UNKNOWN_HOST_URI = "http://www.test.invalid/"
|
||||
const val FULLSCREEN_PATH = "/assets/www/fullscreen.html"
|
||||
}
|
||||
|
||||
@get:Rule val sessionRule = GeckoSessionTestRule()
|
||||
|
|
|
@ -445,4 +445,38 @@ class ContentDelegateTest : BaseSessionTest() {
|
|||
assertThat("Should not have focused field",
|
||||
countAutoFillNodes({ it.isFocused }), equalTo(0))
|
||||
}
|
||||
|
||||
private fun goFullscreen() {
|
||||
sessionRule.setPrefsUntilTestEnd(mapOf("full-screen-api.allow-trusted-requests-only" to false))
|
||||
mainSession.loadTestPath(FULLSCREEN_PATH)
|
||||
mainSession.waitForPageStop()
|
||||
mainSession.evaluateJS("$('#fullscreen').requestFullscreen()")
|
||||
sessionRule.waitUntilCalled(object : Callbacks.ContentDelegate {
|
||||
override fun onFullScreen(session: GeckoSession, fullScreen: Boolean) {
|
||||
assertThat("Div went fullscreen", fullScreen, equalTo(true))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun waitForFullscreenExit() {
|
||||
sessionRule.waitUntilCalled(object : Callbacks.ContentDelegate {
|
||||
override fun onFullScreen(session: GeckoSession, fullScreen: Boolean) {
|
||||
assertThat("Div went fullscreen", fullScreen, equalTo(false))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@WithDevToolsAPI
|
||||
@Test fun fullscreen() {
|
||||
goFullscreen()
|
||||
mainSession.evaluateJS("document.exitFullscreen()")
|
||||
waitForFullscreenExit()
|
||||
}
|
||||
|
||||
@WithDevToolsAPI
|
||||
@Test fun sessionExitFullscreen() {
|
||||
goFullscreen()
|
||||
mainSession.exitFullScreen()
|
||||
waitForFullscreenExit()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3467,6 +3467,14 @@ pub extern "C" fn Servo_ParseEasing(
|
|||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn Servo_SerializeEasing(
|
||||
easing: nsTimingFunctionBorrowed,
|
||||
output: *mut nsAString,
|
||||
) {
|
||||
easing.mTiming.to_css(&mut CssWriter::new(&mut *output)).unwrap();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn Servo_GetProperties_Overriding_Animation(
|
||||
element: RawGeckoElementBorrowed,
|
||||
|
|
|
@ -68,7 +68,7 @@ jobs:
|
|||
max-run-time: 10800 # Takes 2-3 hours
|
||||
env:
|
||||
PRODUCT: firefox
|
||||
REVIEWERS: "mtabara, jlund"
|
||||
REVIEWERS: "ryanvm"
|
||||
command:
|
||||
- /runme.sh
|
||||
taskcluster-proxy: true
|
||||
|
|
|
@ -110,6 +110,18 @@ class Raptor(TestingMixin, MercurialScript, CodeCoverageMixin, AndroidMixin):
|
|||
"type": "int",
|
||||
"help": "How many samples to take with the profiler"
|
||||
}],
|
||||
[["--page-cycles"], {
|
||||
"dest": "page_cycles",
|
||||
"type": "int",
|
||||
"help": "How many times to repeat loading the test page (for page load tests); "
|
||||
"for benchmark tests this is how many times the benchmark test will be run"
|
||||
}],
|
||||
[["--page-timeout"], {
|
||||
"dest": "page_timeout",
|
||||
"type": "int",
|
||||
"help": "How long to wait (ms) for one page_cycle to complete, before timing out"
|
||||
}],
|
||||
|
||||
] + testing_config_options + copy.deepcopy(code_coverage_config_options)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
|
|
|
@ -37,6 +37,11 @@ def create_parser(mach_interface=False):
|
|||
help="How many samples to take with the profiler")
|
||||
add_arg('--symbolsPath', dest='symbols_path',
|
||||
help="Path to the symbols for the build we are testing")
|
||||
add_arg('--page-cycles', dest="page_cycles", type=int,
|
||||
help="How many times to repeat loading the test page (for page load tests); "
|
||||
"for benchmark tests this is how many times the benchmark test will be run")
|
||||
add_arg('--page-timeout', dest="page_timeout", type=int,
|
||||
help="How long to wait (ms) for one page_cycle to complete, before timing out")
|
||||
if not mach_interface:
|
||||
add_arg('--branchName', dest="branch_name", default='',
|
||||
help="Name of the branch we are testing on")
|
||||
|
|
|
@ -66,6 +66,7 @@ def write_test_settings_json(test_details, oskey):
|
|||
# write test settings json file with test details that the control
|
||||
# server will provide for the web ext
|
||||
test_url = transform_platform(test_details['test_url'], oskey)
|
||||
|
||||
test_settings = {
|
||||
"raptor-options": {
|
||||
"type": test_details['type'],
|
||||
|
@ -171,6 +172,18 @@ def get_raptor_test_list(args, oskey):
|
|||
# subtest comes from matching test ini file name, so add it
|
||||
tests_to_run.append(next_test)
|
||||
|
||||
# if --page-cycles command line arg was provided, override the page_cycles value
|
||||
# that was in the manifest/test INI with the command line arg value instead
|
||||
if args.page_cycles is not None:
|
||||
LOG.info("setting page-cycles to %d as specified on the command line" % args.page_cycles)
|
||||
next_test['page_cycles'] = args.page_cycles
|
||||
|
||||
# if --page-timeout command line arg was provided, override the page_timeout value
|
||||
# that was in the manifest/test INI with the command line arg value instead
|
||||
if args.page_timeout is not None:
|
||||
LOG.info("setting page-timeout to %d as specified on the command line" % args.page_timeout)
|
||||
next_test['page_timeout'] = args.page_timeout
|
||||
|
||||
# if geckoProfile is enabled, turn it on in test settings and limit pagecycles to 2
|
||||
if args.gecko_profile is True:
|
||||
for next_test in tests_to_run:
|
||||
|
|
|
@ -446,6 +446,7 @@ def main(args=sys.argv[1:]):
|
|||
next_test['page_timeout'] = 120000
|
||||
if 'page_cycles' not in next_test.keys():
|
||||
next_test['page_cycles'] = 1
|
||||
|
||||
raptor.run_test(next_test, timeout=int(next_test['page_timeout']))
|
||||
|
||||
success = raptor.process_results()
|
||||
|
|
|
@ -10,7 +10,11 @@ from raptor.cmdline import verify_options
|
|||
|
||||
|
||||
def test_verify_options(filedir):
|
||||
args = Namespace(app='firefox', binary='invalid/path', gecko_profile='False')
|
||||
args = Namespace(app='firefox',
|
||||
binary='invalid/path',
|
||||
gecko_profile='False',
|
||||
page_cycles=1,
|
||||
page_timeout=60000)
|
||||
parser = ArgumentParser()
|
||||
|
||||
with pytest.raises(SystemExit):
|
||||
|
|
|
@ -12677,8 +12677,8 @@
|
|||
"SANDBOX_HAS_USER_NAMESPACES": {
|
||||
"record_in_processes": ["main"],
|
||||
"alert_emails": ["gcp@mozilla.com", "jld@mozilla.com"],
|
||||
"bug_numbers": [1098428, 1370578, 1461546],
|
||||
"expires_in_version": "65",
|
||||
"bug_numbers": [1098428, 1370578, 1461546, 1464220],
|
||||
"expires_in_version": "70",
|
||||
"releaseChannelCollection": "opt-out",
|
||||
"kind": "boolean",
|
||||
"operating_systems": ["linux"],
|
||||
|
|
|
@ -2079,9 +2079,8 @@ var AddonManagerInternal = {
|
|||
*
|
||||
* @param aAddon
|
||||
* An addon object, meaning:
|
||||
* An object with either an icons property that is a key-value
|
||||
* list of icon size and icon URL, or an object having an iconURL
|
||||
* and icon64URL property.
|
||||
* An object with either an icons property that is a key-value list
|
||||
* of icon size and icon URL, or an object having an iconURL property.
|
||||
* @param aSize
|
||||
* Ideal icon size in pixels
|
||||
* @param aWindow
|
||||
|
@ -2102,9 +2101,6 @@ var AddonManagerInternal = {
|
|||
icons[32] = aAddon.iconURL;
|
||||
icons[48] = aAddon.iconURL;
|
||||
}
|
||||
if (aAddon.icon64URL) {
|
||||
icons[64] = aAddon.icon64URL;
|
||||
}
|
||||
}
|
||||
|
||||
// quick return if the exact size was found
|
||||
|
|
|
@ -958,7 +958,7 @@ var AddonTestUtils = {
|
|||
data = Object.assign({}, defaults, data);
|
||||
|
||||
let props = ["id", "version", "type", "internalName", "updateURL",
|
||||
"optionsURL", "optionsType", "aboutURL", "iconURL", "icon64URL",
|
||||
"optionsURL", "optionsType", "aboutURL", "iconURL",
|
||||
"skinnable", "bootstrap", "strictCompatibility",
|
||||
"hasEmbeddedWebExtension"];
|
||||
rdf += this._writeProps(data, props);
|
||||
|
|
|
@ -91,7 +91,7 @@ class InstallRDF extends Manifest {
|
|||
let result = {};
|
||||
|
||||
let props = ["id", "version", "type", "updateURL", "optionsURL",
|
||||
"optionsType", "aboutURL", "iconURL", "icon64URL",
|
||||
"optionsType", "aboutURL", "iconURL",
|
||||
"bootstrap", "unpack", "strictCompatibility",
|
||||
"hasEmbeddedWebExtension"];
|
||||
this._readProps(root, result, props);
|
||||
|
|
|
@ -115,7 +115,7 @@ const PROP_JSON_FIELDS = ["id", "syncGUID", "version", "type",
|
|||
"strictCompatibility", "locales", "targetApplications",
|
||||
"targetPlatforms", "signedState",
|
||||
"seen", "dependencies", "hasEmbeddedWebExtension",
|
||||
"userPermissions", "icons", "iconURL", "icon64URL",
|
||||
"userPermissions", "icons", "iconURL",
|
||||
"blocklistState", "blocklistURL", "startupData",
|
||||
"previewImage", "hidden", "installTelemetryInfo"];
|
||||
|
||||
|
@ -807,10 +807,6 @@ AddonWrapper = class {
|
|||
return AddonManager.getPreferredIconURL(this, 48);
|
||||
}
|
||||
|
||||
get icon64URL() {
|
||||
return AddonManager.getPreferredIconURL(this, 64);
|
||||
}
|
||||
|
||||
get icons() {
|
||||
let addon = addonFor(this);
|
||||
let icons = {};
|
||||
|
@ -833,10 +829,6 @@ AddonWrapper = class {
|
|||
icons[48] = addon.iconURL;
|
||||
}
|
||||
|
||||
if (canUseIconURLs && addon.icon64URL) {
|
||||
icons[64] = addon.icon64URL;
|
||||
}
|
||||
|
||||
Object.freeze(icons);
|
||||
return icons;
|
||||
}
|
||||
|
|
|
@ -164,8 +164,7 @@ const DIR_TRASH = "trash";
|
|||
|
||||
// Properties that exist in the install manifest
|
||||
const PROP_METADATA = ["id", "version", "type", "internalName", "updateURL",
|
||||
"optionsURL", "optionsType", "aboutURL",
|
||||
"iconURL", "icon64URL"];
|
||||
"optionsURL", "optionsType", "aboutURL", "iconURL"];
|
||||
const PROP_LOCALE_SINGLE = ["name", "description", "creator", "homepageURL"];
|
||||
const PROP_LOCALE_MULTI = ["developers", "translators", "contributors"];
|
||||
|
||||
|
@ -486,7 +485,6 @@ async function loadManifestFromWebManifest(aUri, aPackage) {
|
|||
|
||||
// WebExtensions don't use iconURLs
|
||||
addon.iconURL = null;
|
||||
addon.icon64URL = null;
|
||||
addon.icons = manifest.icons || {};
|
||||
addon.userPermissions = extension.manifestPermissions;
|
||||
|
||||
|
|
|
@ -55,7 +55,6 @@ async function test() {
|
|||
fullDescription: "Longer description",
|
||||
type: "extension",
|
||||
iconURL: "chrome://foo/skin/icon.png",
|
||||
icon64URL: "chrome://foo/skin/icon64.png",
|
||||
contributionURL: "http://foo.com",
|
||||
contributionAmount: "$0.99",
|
||||
sourceURI: Services.io.newURI("http://example.com/foo"),
|
||||
|
@ -514,7 +513,6 @@ add_test(function() {
|
|||
fullDescription: "Longer description replacement",
|
||||
type: "extension",
|
||||
iconURL: "chrome://foo/skin/icon.png",
|
||||
icon64URL: "chrome://foo/skin/icon264.png",
|
||||
sourceURI: Services.io.newURI("http://example.com/foo"),
|
||||
averageRating: 2,
|
||||
optionsURL: "chrome://foo/content/options.xul",
|
||||
|
|
|
@ -134,7 +134,6 @@ add_task(async function test_1() {
|
|||
checkAddon("addon1@tests.mozilla.org", addon, {
|
||||
install,
|
||||
iconURL: `jar:${uri.spec}!/icon.png`,
|
||||
icon64URL: `jar:${uri.spec}!/icon64.png`,
|
||||
sourceURI: uri,
|
||||
});
|
||||
notEqual(addon.syncGUID, null);
|
||||
|
@ -205,7 +204,6 @@ add_task(async function test_1() {
|
|||
name: "Test 1",
|
||||
foreignInstall: false,
|
||||
iconURL: uri2 + "icon.png",
|
||||
icon64URL: uri2 + "icon64.png",
|
||||
sourceURI: Services.io.newFileURI(XPIS.test_install1),
|
||||
});
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@ const ADDONS = [
|
|||
bootstrap: true,
|
||||
aboutURL: "chrome://test/content/about.xul",
|
||||
iconURL: "chrome://test/skin/icon.png",
|
||||
icon64URL: "chrome://test/skin/icon64.png",
|
||||
targetApplications: [{
|
||||
id: "xpcshell@tests.mozilla.org",
|
||||
minVersion: "1",
|
||||
|
@ -45,8 +44,7 @@ const ADDONS = [
|
|||
optionsType: null,
|
||||
aboutURL: "chrome://test/content/about.xul",
|
||||
iconURL: "chrome://test/skin/icon.png",
|
||||
icon64URL: "chrome://test/skin/icon64.png",
|
||||
icons: {32: "chrome://test/skin/icon.png", 48: "chrome://test/skin/icon.png", 64: "chrome://test/skin/icon64.png"},
|
||||
icons: {32: "chrome://test/skin/icon.png", 48: "chrome://test/skin/icon.png"},
|
||||
name: "Test Addon 1",
|
||||
description: "Test Description",
|
||||
creator: "Test Creator",
|
||||
|
|
|
@ -95,7 +95,6 @@ add_task(async function test_1() {
|
|||
isWebExtension: true,
|
||||
signedState: AddonManager.SIGNEDSTATE_PRIVILEGED,
|
||||
iconURL: `${uri}icon48.png`,
|
||||
icon64URL: `${uri}icon64.png`,
|
||||
});
|
||||
|
||||
// Should persist through a restart
|
||||
|
@ -122,7 +121,6 @@ add_task(async function test_1() {
|
|||
type: "extension",
|
||||
signedState: AddonManager.SIGNEDSTATE_PRIVILEGED,
|
||||
iconURL: `${uri}icon48.png`,
|
||||
icon64URL: `${uri}icon64.png`,
|
||||
});
|
||||
|
||||
await addon.disable();
|
||||
|
|
|
@ -28,9 +28,8 @@ async function testSimpleIconsetParsing(manifest) {
|
|||
64: uri + "icon64.png",
|
||||
});
|
||||
|
||||
// iconURL should map to icons[48] and icons[64]
|
||||
// iconURL should map to icons[48]
|
||||
equal(addon.iconURL, uri + "icon48.png");
|
||||
equal(addon.icon64URL, uri + "icon64.png");
|
||||
|
||||
// AddonManager gets the correct icon sizes from addon.icons
|
||||
equal(AddonManager.getPreferredIconURL(addon, 1), uri + "icon16.png");
|
||||
|
@ -91,7 +90,6 @@ async function testNoIconsParsing(manifest) {
|
|||
deepEqual(addon.icons, {});
|
||||
|
||||
equal(addon.iconURL, null);
|
||||
equal(addon.icon64URL, null);
|
||||
|
||||
equal(AddonManager.getPreferredIconURL(addon, 128), null);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче