Merge mozilla-central to inbound a=merge on a CLOSED TREE

This commit is contained in:
Coroiu Cristina 2018-11-02 07:18:47 +02:00
Родитель 3395e926b6 02eff2f0bf
Коммит 3507348c1a
77 изменённых файлов: 1493 добавлений и 992 удалений

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

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