From 328093cc718c23f9e1ee3746fec04b39ba3df57a Mon Sep 17 00:00:00 2001 From: Matthew Noorenberghe Date: Wed, 29 May 2019 08:08:47 +0000 Subject: [PATCH 01/36] Bug 1166113 - Use the autocomplete result GUID to fill the correct password. r=sfoster Differential Revision: https://phabricator.services.mozilla.com/D32914 --HG-- extra : moz-landing-system : lando --- browser/base/content/content.js | 6 - mobile/android/components/BrowserCLH.js | 7 - .../passwordmgr/LoginAutoCompleteResult.jsm | 16 +- .../passwordmgr/LoginManagerContent.jsm | 66 +- .../passwordmgr/LoginManagerParent.jsm | 36 +- ...est_basic_form_autocomplete_subdomain.html | 16 +- .../unit/test_login_autocomplete_result.js | 1432 +++++++++-------- toolkit/content/widgets/autocomplete-popup.js | 6 +- .../widgets/autocomplete-richlistitem.js | 21 + toolkit/content/widgets/autocomplete.xml | 6 +- 10 files changed, 829 insertions(+), 783 deletions(-) diff --git a/browser/base/content/content.js b/browser/base/content/content.js index 2c16961a1635..a41e1bdd0b97 100644 --- a/browser/base/content/content.js +++ b/browser/base/content/content.js @@ -63,12 +63,6 @@ addEventListener("DOMInputPasswordAdded", function(event) { let formLike = LoginFormFactory.createFromField(event.originalTarget); InsecurePasswordUtils.reportInsecurePasswords(formLike); }); -addEventListener("DOMAutoComplete", function(event) { - if (shouldIgnoreLoginManagerEvent(event)) { - return; - } - LoginManagerContent.onDOMAutoComplete(event); -}); ContentMetaHandler.init(this); diff --git a/mobile/android/components/BrowserCLH.js b/mobile/android/components/BrowserCLH.js index 392ef3c5d980..066fdb235ec3 100644 --- a/mobile/android/components/BrowserCLH.js +++ b/mobile/android/components/BrowserCLH.js @@ -228,13 +228,6 @@ BrowserCLH.prototype = { this.LoginManagerContent.onDOMInputPasswordAdded(event, event.target.ownerGlobal.top); }, options); - aWindow.addEventListener("DOMAutoComplete", event => { - if (shouldIgnoreLoginManagerEvent(event)) { - return; - } - this.LoginManagerContent.onDOMAutoComplete(event); - }, options); - aWindow.addEventListener("pageshow", event => { // XXXbz what about non-HTML documents?? if (ChromeUtils.getClassName(event.target) == "HTMLDocument") { diff --git a/toolkit/components/passwordmgr/LoginAutoCompleteResult.jsm b/toolkit/components/passwordmgr/LoginAutoCompleteResult.jsm index 753fb0319eb0..8c164cd9f1cd 100644 --- a/toolkit/components/passwordmgr/LoginAutoCompleteResult.jsm +++ b/toolkit/components/passwordmgr/LoginAutoCompleteResult.jsm @@ -99,7 +99,7 @@ class InsecureLoginFormAutocompleteItem extends AutocompleteItem { class LoginAutocompleteItem extends AutocompleteItem { constructor(login, isPasswordField, dateAndTimeFormatter, duplicateUsernames, messageManager) { super(SHOULD_SHOW_ORIGIN ? "loginWithOrigin" : "login"); - this._login = login; + this._login = login.QueryInterface(Ci.nsILoginMetaInfo); this._messageManager = messageManager; XPCOMUtils.defineLazyGetter(this, "label", () => { @@ -109,8 +109,7 @@ class LoginAutocompleteItem extends AutocompleteItem { if (!username) { username = getLocalizedString("noUsername"); } - let meta = login.QueryInterface(Ci.nsILoginMetaInfo); - let time = dateAndTimeFormatter.format(new Date(meta.timePasswordChanged)); + let time = dateAndTimeFormatter.format(new Date(login.timePasswordChanged)); username = getLocalizedString("loginHostAge", [username, time]); } @@ -122,14 +121,19 @@ class LoginAutocompleteItem extends AutocompleteItem { }); XPCOMUtils.defineLazyGetter(this, "comment", () => { + let comment = login.hostname; try { let uri = Services.io.newURI(login.hostname); // Fallback to handle file: URIs - return uri.displayHostPort || login.hostname; + comment = uri.displayHostPort || login.hostname; } catch (ex) { - // Fallback to origin below + // Fallback to login.hostname set above. } - return login.hostname; + + return JSON.stringify({ + guid: login.guid, + comment, + }); }); } diff --git a/toolkit/components/passwordmgr/LoginManagerContent.jsm b/toolkit/components/passwordmgr/LoginManagerContent.jsm index d4336b6ab06e..53748902d913 100644 --- a/toolkit/components/passwordmgr/LoginManagerContent.jsm +++ b/toolkit/components/passwordmgr/LoginManagerContent.jsm @@ -103,6 +103,33 @@ const observer = { LoginManagerContent._onNavigation(aWebProgress.DOMWindow.document); }, + // nsIObserver + observe(subject, topic, data) { + switch (topic) { + case "autocomplete-did-enter-text": { + let input = subject.QueryInterface(Ci.nsIAutoCompleteInput); + let {selectedIndex} = input.popup; + if (selectedIndex < 0) { + break; + } + let style = input.controller.getStyleAt(selectedIndex); + if (style != "login" && style != "loginWithOrigin") { + break; + } + let {focusedInput} = LoginManagerContent._formFillService; + if (focusedInput.nodePrincipal.isNullPrincipal) { + // If we have a null principal then prevent any more password manager code from running and + // incorrectly using the document `location`. + return; + } + let details = JSON.parse(input.controller.getCommentAt(selectedIndex)); + LoginManagerContent.onFieldAutoComplete(focusedInput, details.guid); + break; + } + } + }, + + // nsIDOMEventListener handleEvent(aEvent) { if (!aEvent.isTrusted) { return; @@ -116,7 +143,7 @@ const observer = { case "keydown": { if (aEvent.keyCode == aEvent.DOM_VK_TAB || aEvent.keyCode == aEvent.DOM_VK_RETURN) { - LoginManagerContent.onUsernameInput(aEvent); + LoginManagerContent.onUsernameAutocompleted(aEvent.target); } break; } @@ -144,6 +171,8 @@ const observer = { }, }; +// Add this observer once for the process. +Services.obs.addObserver(observer, "autocomplete-did-enter-text"); // This object maps to the "child" process (even in the single-process case). this.LoginManagerContent = { @@ -323,6 +352,7 @@ this.LoginManagerContent = { * * @param {HTMLFormElement} form - form to get login data for * @param {Object} options + * @param {boolean} options.guid - guid of a login to retrieve * @param {boolean} options.showMasterPassword - whether to show a master password prompt */ _getLoginDataFromParent(form, options) { @@ -733,19 +763,13 @@ this.LoginManagerContent = { }, /** - * Listens for DOMAutoComplete event on login form. + * A username or password was autocompleted into a field. */ - onDOMAutoComplete(event) { - if (!event.isTrusted) { - return; - } - + onFieldAutoComplete(acInputField, loginGUID) { if (!LoginHelper.enabled) { return; } - let acInputField = event.target; - // This is probably a bit over-conservatative. if (ChromeUtils.getClassName(acInputField.ownerDocument) != "HTMLDocument") { return; @@ -756,26 +780,17 @@ this.LoginManagerContent = { } if (LoginHelper.isUsernameFieldType(acInputField)) { - this.onUsernameInput(event); + this.onUsernameAutocompleted(acInputField, loginGUID); } else if (acInputField.hasBeenTypePassword) { - this._highlightFilledField(event.target); + this._highlightFilledField(acInputField); } }, /** - * Calls fill form on the username field. + * A username field was filled so try fill in the associated password in the password field. */ - onUsernameInput(event) { - let acInputField = event.target; - - // If the username is blank, bail out now -- we don't want - // fillForm() to try filling in a login without a username - // to filter on (bug 471906). - if (!acInputField.value) { - return; - } - - log("onUsernameInput from", event.type); + onUsernameAutocompleted(acInputField, loginGUID = null) { + log("onUsernameAutocompleted:", acInputField); let acForm = LoginFormFactory.createFromField(acInputField); let doc = acForm.ownerDocument; @@ -787,7 +802,10 @@ this.LoginManagerContent = { let [usernameField, passwordField, ignored] = this._getFormFields(acForm, false, recipes); if (usernameField == acInputField && passwordField) { - this._getLoginDataFromParent(acForm, { showMasterPassword: false }) + this._getLoginDataFromParent(acForm, { + guid: loginGUID, + showMasterPassword: false, + }) .then(({ form, loginsFound, recipes }) => { this._fillForm(form, loginsFound, recipes, { autofillForm: true, diff --git a/toolkit/components/passwordmgr/LoginManagerParent.jsm b/toolkit/components/passwordmgr/LoginManagerParent.jsm index c556492a079b..d17ca284c63b 100644 --- a/toolkit/components/passwordmgr/LoginManagerParent.jsm +++ b/toolkit/components/passwordmgr/LoginManagerParent.jsm @@ -107,11 +107,11 @@ this.LoginManagerParent = { switch (msg.name) { case "PasswordManager:findLogins": { // TODO Verify msg.target's principals against the formOrigin? - this.sendLoginDataToChild(data.options.showMasterPassword, - data.formOrigin, + this.sendLoginDataToChild(data.formOrigin, data.actionOrigin, data.requestId, - msg.target.messageManager); + msg.target.messageManager, + data.options); break; } @@ -194,8 +194,10 @@ this.LoginManagerParent = { /** * Send relevant data (e.g. logins and recipes) to the child process (LoginManagerContent). */ - async sendLoginDataToChild(showMasterPassword, formOrigin, actionOrigin, - requestId, target) { + async sendLoginDataToChild(formOrigin, actionOrigin, requestId, target, { + guid, + showMasterPassword, + }) { let recipes = []; if (formOrigin) { let formHost; @@ -244,8 +246,9 @@ this.LoginManagerParent = { return; } - self.sendLoginDataToChild(showMasterPassword, formOrigin, actionOrigin, - requestId, target); + self.sendLoginDataToChild(formOrigin, actionOrigin, requestId, target, { + showMasterPassword, + }); }, }; @@ -260,12 +263,19 @@ this.LoginManagerParent = { } // Autocomplete results do not need to match actionOrigin or exact hostname. - let logins = this._searchAndDedupeLogins(formOrigin, - actionOrigin, - { - ignoreActionAndRealm: true, - acceptDifferentSubdomains: INCLUDE_OTHER_SUBDOMAINS_IN_LOOKUP, - }); + let logins = null; + if (guid) { + logins = LoginHelper.searchLoginsWithObject({ + guid, + }); + } else { + logins = this._searchAndDedupeLogins(formOrigin, + actionOrigin, + { + ignoreActionAndRealm: true, + acceptDifferentSubdomains: INCLUDE_OTHER_SUBDOMAINS_IN_LOOKUP, // TODO: for TAB case + }); + } log("sendLoginDataToChild:", logins.length, "deduped logins"); // Convert the array of nsILoginInfo to vanilla JS objects since nsILoginInfo diff --git a/toolkit/components/passwordmgr/test/mochitest/test_basic_form_autocomplete_subdomain.html b/toolkit/components/passwordmgr/test/mochitest/test_basic_form_autocomplete_subdomain.html index 6a0a29370a0f..fa9bf3bb3e5d 100644 --- a/toolkit/components/passwordmgr/test/mochitest/test_basic_form_autocomplete_subdomain.html +++ b/toolkit/components/passwordmgr/test/mochitest/test_basic_form_autocomplete_subdomain.html @@ -117,13 +117,8 @@ add_task(async function test_form1_menu_shows_two_logins_same_usernames_for_diff synthesizeKey("KEY_Enter"); await promiseFormsProcessed(); - todo_is(pword.value, "dsdp1", - `Bug 1166113: The password should match the login that was selected. - From bug 499649 when there are two logins with the same case we just - blindly select the last one always. We should instead remember which - login was selected from the dropdown and use that login's password - when possible`); - checkLoginForm(uname, "dsdu1", pword, "dsdp1prime"); + is(pword.value, "dsdp1", "password should match the login that was selected"); + checkLoginForm(uname, "dsdu1", pword, "dsdp1"); restoreForm(); @@ -137,12 +132,7 @@ add_task(async function test_form1_menu_shows_two_logins_same_usernames_for_diff synthesizeKey("KEY_Enter"); await promiseFormsProcessed(); - todo_is(pword.value, "dsdp1", - `Bug 1166113: The password should match the login that was selected. - From bug 499649 when there are two logins with the same case we just - blindly select the last one always. We should instead remember which - login was selected from the dropdown and use that login's password - when possible`); + is(pword.value, "dsdp1prime", "Password should match the login that was selected"); checkLoginForm(uname, "dsdu1", pword, "dsdp1prime"); }); diff --git a/toolkit/components/passwordmgr/test/unit/test_login_autocomplete_result.js b/toolkit/components/passwordmgr/test/unit/test_login_autocomplete_result.js index ad4a8a94506b..dd160d7ccd62 100644 --- a/toolkit/components/passwordmgr/test/unit/test_login_autocomplete_result.js +++ b/toolkit/components/passwordmgr/test/unit/test_login_autocomplete_result.js @@ -20,717 +20,723 @@ matchingLogins.push(new nsLoginInfo("http://mochi.test:8888", "http://autocomple matchingLogins.push(new nsLoginInfo("http://mochi.test:8888", "http://autocomplete:8888", null, "zzzuser4", "zzzpass4", "uname", "pword")); -let meta = matchingLogins[0].QueryInterface(Ci.nsILoginMetaInfo); -let dateAndTimeFormatter = new Services.intl.DateTimeFormat(undefined, - { dateStyle: "medium" }); -let time = dateAndTimeFormatter.format(new Date(meta.timePasswordChanged)); -const LABEL_NO_USERNAME = "No username (" + time + ")"; - -let expectedResults = [ - { - insecureFieldWarningEnabled: true, - isSecure: true, - isPasswordField: false, - matchingLogins, - items: [{ - value: "", - label: LABEL_NO_USERNAME, - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "tempuser1", - label: "tempuser1", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "testuser2", - label: "testuser2", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "testuser3", - label: "testuser3", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "zzzuser4", - label: "zzzuser4", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "", - label: "View Saved Logins", - style: "loginsFooter", - comment: "mochi.test", - }], - }, - { - insecureFieldWarningEnabled: true, - isSecure: false, - isPasswordField: false, - matchingLogins: [], - items: [{ - value: "", - label: "This connection is not secure. Logins entered here could be compromised. Learn More", - style: "insecureWarning", - comment: "", - }, { - value: "", - label: "View Saved Logins", - style: "loginsFooter", - comment: "mochi.test", - }], - }, - { - insecureFieldWarningEnabled: true, - isSecure: false, - isPasswordField: false, - matchingLogins, - items: [{ - value: "", - label: "This connection is not secure. Logins entered here could be compromised. Learn More", - style: "insecureWarning", - comment: "", - }, { - value: "", - label: LABEL_NO_USERNAME, - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "tempuser1", - label: "tempuser1", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "testuser2", - label: "testuser2", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "testuser3", - label: "testuser3", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "zzzuser4", - label: "zzzuser4", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "", - label: "View Saved Logins", - style: "loginsFooter", - comment: "mochi.test", - }], - }, - { - insecureFieldWarningEnabled: true, - isSecure: true, - isPasswordField: true, - matchingLogins, - items: [{ - value: "emptypass1", - label: LABEL_NO_USERNAME, - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "temppass1", - label: "tempuser1", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "testpass2", - label: "testuser2", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "testpass3", - label: "testuser3", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "zzzpass4", - label: "zzzuser4", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "", - label: "View Saved Logins", - style: "loginsFooter", - comment: "mochi.test", - }], - }, - { - insecureFieldWarningEnabled: true, - isSecure: false, - isPasswordField: true, - matchingLogins, - items: [{ - value: "", - label: "This connection is not secure. Logins entered here could be compromised. Learn More", - style: "insecureWarning", - comment: "", - }, { - value: "emptypass1", - label: LABEL_NO_USERNAME, - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "temppass1", - label: "tempuser1", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "testpass2", - label: "testuser2", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "testpass3", - label: "testuser3", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "zzzpass4", - label: "zzzuser4", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "", - label: "View Saved Logins", - style: "loginsFooter", - comment: "mochi.test", - }], - }, - { - insecureFieldWarningEnabled: false, - isSecure: true, - isPasswordField: false, - matchingLogins, - items: [{ - value: "", - label: LABEL_NO_USERNAME, - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "tempuser1", - label: "tempuser1", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "testuser2", - label: "testuser2", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "testuser3", - label: "testuser3", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "zzzuser4", - label: "zzzuser4", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "", - label: "View Saved Logins", - style: "loginsFooter", - comment: "mochi.test", - }], - }, - { - insecureFieldWarningEnabled: false, - isSecure: false, - isPasswordField: false, - matchingLogins, - items: [{ - value: "", - label: LABEL_NO_USERNAME, - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "tempuser1", - label: "tempuser1", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "testuser2", - label: "testuser2", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "testuser3", - label: "testuser3", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "zzzuser4", - label: "zzzuser4", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "", - label: "View Saved Logins", - style: "loginsFooter", - comment: "mochi.test", - }], - }, - { - insecureFieldWarningEnabled: false, - isSecure: true, - isPasswordField: true, - matchingLogins, - items: [{ - value: "emptypass1", - label: LABEL_NO_USERNAME, - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "temppass1", - label: "tempuser1", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "testpass2", - label: "testuser2", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "testpass3", - label: "testuser3", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "zzzpass4", - label: "zzzuser4", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "", - label: "View Saved Logins", - style: "loginsFooter", - comment: "mochi.test", - }], - }, - { - insecureFieldWarningEnabled: false, - isSecure: false, - isPasswordField: true, - matchingLogins, - items: [{ - value: "emptypass1", - label: LABEL_NO_USERNAME, - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "temppass1", - label: "tempuser1", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "testpass2", - label: "testuser2", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "testpass3", - label: "testuser3", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "zzzpass4", - label: "zzzuser4", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "", - label: "View Saved Logins", - style: "loginsFooter", - comment: "mochi.test", - }], - }, - { - insecureFieldWarningEnabled: true, - isSecure: true, - isPasswordField: false, - matchingLogins, - items: [{ - value: "", - label: LABEL_NO_USERNAME, - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "tempuser1", - label: "tempuser1", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "testuser2", - label: "testuser2", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "testuser3", - label: "testuser3", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "zzzuser4", - label: "zzzuser4", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "", - label: "View Saved Logins", - style: "loginsFooter", - comment: "mochi.test", - }], - }, - { - insecureFieldWarningEnabled: true, - isSecure: false, - isPasswordField: false, - matchingLogins, - items: [{ - value: "", - label: "This connection is not secure. Logins entered here could be compromised. Learn More", - style: "insecureWarning", - comment: "", - }, { - value: "", - label: LABEL_NO_USERNAME, - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "tempuser1", - label: "tempuser1", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "testuser2", - label: "testuser2", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "testuser3", - label: "testuser3", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "zzzuser4", - label: "zzzuser4", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "", - label: "View Saved Logins", - style: "loginsFooter", - comment: "mochi.test", - }], - }, - { - insecureFieldWarningEnabled: true, - isSecure: true, - isPasswordField: true, - matchingLogins, - items: [{ - value: "emptypass1", - label: LABEL_NO_USERNAME, - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "temppass1", - label: "tempuser1", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "testpass2", - label: "testuser2", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "testpass3", - label: "testuser3", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "zzzpass4", - label: "zzzuser4", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "", - label: "View Saved Logins", - style: "loginsFooter", - comment: "mochi.test", - }], - }, - { - insecureFieldWarningEnabled: true, - isSecure: false, - isPasswordField: true, - matchingLogins, - items: [{ - value: "", - label: "This connection is not secure. Logins entered here could be compromised. Learn More", - style: "insecureWarning", - comment: "", - }, { - value: "emptypass1", - label: LABEL_NO_USERNAME, - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "temppass1", - label: "tempuser1", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "testpass2", - label: "testuser2", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "testpass3", - label: "testuser3", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "zzzpass4", - label: "zzzuser4", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "", - label: "View Saved Logins", - style: "loginsFooter", - comment: "mochi.test", - }], - }, - { - insecureFieldWarningEnabled: false, - isSecure: true, - isPasswordField: false, - matchingLogins, - items: [{ - value: "", - label: LABEL_NO_USERNAME, - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "tempuser1", - label: "tempuser1", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "testuser2", - label: "testuser2", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "testuser3", - label: "testuser3", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "zzzuser4", - label: "zzzuser4", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "", - label: "View Saved Logins", - style: "loginsFooter", - comment: "mochi.test", - }], - }, - { - insecureFieldWarningEnabled: false, - isSecure: false, - isPasswordField: false, - matchingLogins: [], - items: [{ - value: "", - label: "View Saved Logins", - style: "loginsFooter", - comment: "mochi.test", - }], - }, - { - insecureFieldWarningEnabled: false, - isSecure: false, - isPasswordField: false, - matchingLogins: [], - searchString: "foo", - items: [{ - value: "", - label: "View Saved Logins", - style: "loginsFooter", - comment: "mochi.test", - }], - }, - { - insecureFieldWarningEnabled: false, - isSecure: false, - isPasswordField: false, - matchingLogins, - items: [{ - value: "", - label: LABEL_NO_USERNAME, - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "tempuser1", - label: "tempuser1", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "testuser2", - label: "testuser2", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "testuser3", - label: "testuser3", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "zzzuser4", - label: "zzzuser4", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "", - label: "View Saved Logins", - style: "loginsFooter", - comment: "mochi.test", - }], - }, - { - insecureFieldWarningEnabled: false, - isSecure: true, - isPasswordField: true, - matchingLogins, - items: [{ - value: "emptypass1", - label: LABEL_NO_USERNAME, - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "temppass1", - label: "tempuser1", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "testpass2", - label: "testuser2", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "testpass3", - label: "testuser3", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "zzzpass4", - label: "zzzuser4", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "", - label: "View Saved Logins", - style: "loginsFooter", - comment: "mochi.test", - }], - }, - { - insecureFieldWarningEnabled: false, - isSecure: false, - isPasswordField: true, - matchingLogins, - items: [{ - value: "emptypass1", - label: LABEL_NO_USERNAME, - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "temppass1", - label: "tempuser1", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "testpass2", - label: "testuser2", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "testpass3", - label: "testuser3", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "zzzpass4", - label: "zzzuser4", - style: "loginWithOrigin", - comment: "mochi.test:8888", - }, { - value: "", - label: "View Saved Logins", - style: "loginsFooter", - comment: "mochi.test", - }], - }, - { - insecureFieldWarningEnabled: true, - isSecure: true, - isPasswordField: true, - matchingLogins: [], - items: [{ - value: "", - label: "View Saved Logins", - style: "loginsFooter", - comment: "mochi.test", - }], - }, - { - insecureFieldWarningEnabled: true, - isSecure: true, - isPasswordField: true, - matchingLogins: [], - searchString: "foo", - items: [], - }, - { - generatedPassword: "9ljgfd4shyktb45", - insecureFieldWarningEnabled: true, - isSecure: true, - isPasswordField: true, - matchingLogins: [], - items: [{ - value: "9ljgfd4shyktb45", - label: "Use Generated Password", - style: "generatedPassword", - comment: "9ljgfd4shyktb45", - }, { - value: "", - label: "View Saved Logins", - style: "loginsFooter", - comment: "mochi.test", - }], - }, - { - generatedPassword: "9ljgfd4shyktb45", - insecureFieldWarningEnabled: true, - isSecure: true, - isPasswordField: true, - matchingLogins: [], - searchString: "9ljgfd4shyktb45", - items: [], - }, -]; +add_task(async function setup() { + // Get a profile so we have storage access and insert the logins to get unique GUIDs. + do_get_profile(); + matchingLogins = await Services.logins.addLogins(matchingLogins); +}); add_task(async function test_all_patterns() { + let meta = matchingLogins[0].QueryInterface(Ci.nsILoginMetaInfo); + let dateAndTimeFormatter = new Services.intl.DateTimeFormat(undefined, + { dateStyle: "medium" }); + let time = dateAndTimeFormatter.format(new Date(meta.timePasswordChanged)); + const LABEL_NO_USERNAME = "No username (" + time + ")"; + + let expectedResults = [ + { + insecureFieldWarningEnabled: true, + isSecure: true, + isPasswordField: false, + matchingLogins, + items: [{ + value: "", + label: LABEL_NO_USERNAME, + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "tempuser1", + label: "tempuser1", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "testuser2", + label: "testuser2", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "testuser3", + label: "testuser3", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "zzzuser4", + label: "zzzuser4", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "", + label: "View Saved Logins", + style: "loginsFooter", + comment: "mochi.test", + }], + }, + { + insecureFieldWarningEnabled: true, + isSecure: false, + isPasswordField: false, + matchingLogins: [], + items: [{ + value: "", + label: "This connection is not secure. Logins entered here could be compromised. Learn More", + style: "insecureWarning", + comment: "", + }, { + value: "", + label: "View Saved Logins", + style: "loginsFooter", + comment: "mochi.test", + }], + }, + { + insecureFieldWarningEnabled: true, + isSecure: false, + isPasswordField: false, + matchingLogins, + items: [{ + value: "", + label: "This connection is not secure. Logins entered here could be compromised. Learn More", + style: "insecureWarning", + comment: "", + }, { + value: "", + label: LABEL_NO_USERNAME, + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "tempuser1", + label: "tempuser1", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "testuser2", + label: "testuser2", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "testuser3", + label: "testuser3", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "zzzuser4", + label: "zzzuser4", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "", + label: "View Saved Logins", + style: "loginsFooter", + comment: "mochi.test", + }], + }, + { + insecureFieldWarningEnabled: true, + isSecure: true, + isPasswordField: true, + matchingLogins, + items: [{ + value: "emptypass1", + label: LABEL_NO_USERNAME, + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "temppass1", + label: "tempuser1", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "testpass2", + label: "testuser2", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "testpass3", + label: "testuser3", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "zzzpass4", + label: "zzzuser4", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "", + label: "View Saved Logins", + style: "loginsFooter", + comment: "mochi.test", + }], + }, + { + insecureFieldWarningEnabled: true, + isSecure: false, + isPasswordField: true, + matchingLogins, + items: [{ + value: "", + label: "This connection is not secure. Logins entered here could be compromised. Learn More", + style: "insecureWarning", + comment: "", + }, { + value: "emptypass1", + label: LABEL_NO_USERNAME, + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "temppass1", + label: "tempuser1", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "testpass2", + label: "testuser2", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "testpass3", + label: "testuser3", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "zzzpass4", + label: "zzzuser4", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "", + label: "View Saved Logins", + style: "loginsFooter", + comment: "mochi.test", + }], + }, + { + insecureFieldWarningEnabled: false, + isSecure: true, + isPasswordField: false, + matchingLogins, + items: [{ + value: "", + label: LABEL_NO_USERNAME, + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "tempuser1", + label: "tempuser1", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "testuser2", + label: "testuser2", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "testuser3", + label: "testuser3", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "zzzuser4", + label: "zzzuser4", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "", + label: "View Saved Logins", + style: "loginsFooter", + comment: "mochi.test", + }], + }, + { + insecureFieldWarningEnabled: false, + isSecure: false, + isPasswordField: false, + matchingLogins, + items: [{ + value: "", + label: LABEL_NO_USERNAME, + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "tempuser1", + label: "tempuser1", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "testuser2", + label: "testuser2", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "testuser3", + label: "testuser3", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "zzzuser4", + label: "zzzuser4", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "", + label: "View Saved Logins", + style: "loginsFooter", + comment: "mochi.test", + }], + }, + { + insecureFieldWarningEnabled: false, + isSecure: true, + isPasswordField: true, + matchingLogins, + items: [{ + value: "emptypass1", + label: LABEL_NO_USERNAME, + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "temppass1", + label: "tempuser1", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "testpass2", + label: "testuser2", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "testpass3", + label: "testuser3", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "zzzpass4", + label: "zzzuser4", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "", + label: "View Saved Logins", + style: "loginsFooter", + comment: "mochi.test", + }], + }, + { + insecureFieldWarningEnabled: false, + isSecure: false, + isPasswordField: true, + matchingLogins, + items: [{ + value: "emptypass1", + label: LABEL_NO_USERNAME, + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "temppass1", + label: "tempuser1", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "testpass2", + label: "testuser2", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "testpass3", + label: "testuser3", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "zzzpass4", + label: "zzzuser4", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "", + label: "View Saved Logins", + style: "loginsFooter", + comment: "mochi.test", + }], + }, + { + insecureFieldWarningEnabled: true, + isSecure: true, + isPasswordField: false, + matchingLogins, + items: [{ + value: "", + label: LABEL_NO_USERNAME, + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "tempuser1", + label: "tempuser1", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "testuser2", + label: "testuser2", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "testuser3", + label: "testuser3", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "zzzuser4", + label: "zzzuser4", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "", + label: "View Saved Logins", + style: "loginsFooter", + comment: "mochi.test", + }], + }, + { + insecureFieldWarningEnabled: true, + isSecure: false, + isPasswordField: false, + matchingLogins, + items: [{ + value: "", + label: "This connection is not secure. Logins entered here could be compromised. Learn More", + style: "insecureWarning", + comment: "", + }, { + value: "", + label: LABEL_NO_USERNAME, + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "tempuser1", + label: "tempuser1", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "testuser2", + label: "testuser2", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "testuser3", + label: "testuser3", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "zzzuser4", + label: "zzzuser4", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "", + label: "View Saved Logins", + style: "loginsFooter", + comment: "mochi.test", + }], + }, + { + insecureFieldWarningEnabled: true, + isSecure: true, + isPasswordField: true, + matchingLogins, + items: [{ + value: "emptypass1", + label: LABEL_NO_USERNAME, + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "temppass1", + label: "tempuser1", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "testpass2", + label: "testuser2", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "testpass3", + label: "testuser3", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "zzzpass4", + label: "zzzuser4", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "", + label: "View Saved Logins", + style: "loginsFooter", + comment: "mochi.test", + }], + }, + { + insecureFieldWarningEnabled: true, + isSecure: false, + isPasswordField: true, + matchingLogins, + items: [{ + value: "", + label: "This connection is not secure. Logins entered here could be compromised. Learn More", + style: "insecureWarning", + comment: "", + }, { + value: "emptypass1", + label: LABEL_NO_USERNAME, + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "temppass1", + label: "tempuser1", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "testpass2", + label: "testuser2", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "testpass3", + label: "testuser3", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "zzzpass4", + label: "zzzuser4", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "", + label: "View Saved Logins", + style: "loginsFooter", + comment: "mochi.test", + }], + }, + { + insecureFieldWarningEnabled: false, + isSecure: true, + isPasswordField: false, + matchingLogins, + items: [{ + value: "", + label: LABEL_NO_USERNAME, + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "tempuser1", + label: "tempuser1", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "testuser2", + label: "testuser2", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "testuser3", + label: "testuser3", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "zzzuser4", + label: "zzzuser4", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "", + label: "View Saved Logins", + style: "loginsFooter", + comment: "mochi.test", + }], + }, + { + insecureFieldWarningEnabled: false, + isSecure: false, + isPasswordField: false, + matchingLogins: [], + items: [{ + value: "", + label: "View Saved Logins", + style: "loginsFooter", + comment: "mochi.test", + }], + }, + { + insecureFieldWarningEnabled: false, + isSecure: false, + isPasswordField: false, + matchingLogins: [], + searchString: "foo", + items: [{ + value: "", + label: "View Saved Logins", + style: "loginsFooter", + comment: "mochi.test", + }], + }, + { + insecureFieldWarningEnabled: false, + isSecure: false, + isPasswordField: false, + matchingLogins, + items: [{ + value: "", + label: LABEL_NO_USERNAME, + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "tempuser1", + label: "tempuser1", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "testuser2", + label: "testuser2", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "testuser3", + label: "testuser3", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "zzzuser4", + label: "zzzuser4", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "", + label: "View Saved Logins", + style: "loginsFooter", + comment: "mochi.test", + }], + }, + { + insecureFieldWarningEnabled: false, + isSecure: true, + isPasswordField: true, + matchingLogins, + items: [{ + value: "emptypass1", + label: LABEL_NO_USERNAME, + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "temppass1", + label: "tempuser1", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "testpass2", + label: "testuser2", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "testpass3", + label: "testuser3", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "zzzpass4", + label: "zzzuser4", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "", + label: "View Saved Logins", + style: "loginsFooter", + comment: "mochi.test", + }], + }, + { + insecureFieldWarningEnabled: false, + isSecure: false, + isPasswordField: true, + matchingLogins, + items: [{ + value: "emptypass1", + label: LABEL_NO_USERNAME, + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "temppass1", + label: "tempuser1", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "testpass2", + label: "testuser2", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "testpass3", + label: "testuser3", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "zzzpass4", + label: "zzzuser4", + style: "loginWithOrigin", + comment: { comment: "mochi.test:8888" }, + }, { + value: "", + label: "View Saved Logins", + style: "loginsFooter", + comment: "mochi.test", + }], + }, + { + insecureFieldWarningEnabled: true, + isSecure: true, + isPasswordField: true, + matchingLogins: [], + items: [{ + value: "", + label: "View Saved Logins", + style: "loginsFooter", + comment: "mochi.test", + }], + }, + { + insecureFieldWarningEnabled: true, + isSecure: true, + isPasswordField: true, + matchingLogins: [], + searchString: "foo", + items: [], + }, + { + generatedPassword: "9ljgfd4shyktb45", + insecureFieldWarningEnabled: true, + isSecure: true, + isPasswordField: true, + matchingLogins: [], + items: [{ + value: "9ljgfd4shyktb45", + label: "Use Generated Password", + style: "generatedPassword", + comment: "9ljgfd4shyktb45", + }, { + value: "", + label: "View Saved Logins", + style: "loginsFooter", + comment: "mochi.test", + }], + }, + { + generatedPassword: "9ljgfd4shyktb45", + insecureFieldWarningEnabled: true, + isSecure: true, + isPasswordField: true, + matchingLogins: [], + searchString: "9ljgfd4shyktb45", + items: [], + }, + ]; + LoginHelper.createLogger("LoginAutoCompleteResult"); Services.prefs.setBoolPref("signon.showAutoCompleteFooter", true); Services.prefs.setBoolPref("signon.showAutoCompleteOrigins", true); @@ -750,7 +756,13 @@ add_task(async function test_all_patterns() { equal(actual.getValueAt(index), item.value, `Value ${index}`); equal(actual.getLabelAt(index), item.label, `Label ${index}`); equal(actual.getStyleAt(index), item.style, `Style ${index}`); - equal(actual.getCommentAt(index), item.comment, `Comment ${index}`); + let actualComment = actual.getCommentAt(index); + if (typeof(item.comment) == "object") { + let parsedComment = JSON.parse(actualComment); + equal(parsedComment.comment, item.comment.comment, `Comment.comment ${index}`); + } else { + equal(actualComment, item.comment, `Comment ${index}`); + } }); if (pattern.items.length != 0) { diff --git a/toolkit/content/widgets/autocomplete-popup.js b/toolkit/content/widgets/autocomplete-popup.js index f4ff338a55be..c1df31b6977b 100644 --- a/toolkit/content/widgets/autocomplete-popup.js +++ b/toolkit/content/widgets/autocomplete-popup.js @@ -404,15 +404,17 @@ MozElements.MozAutocompleteRichlistboxPopup = class MozAutocompleteRichlistboxPo case "autofill-insecureWarning": options = { is: "autocomplete-creditcard-insecure-field" }; break; + case "generatedPassword": + options = { is: "autocomplete-two-line-richlistitem" }; + break; case "insecureWarning": options = { is: "autocomplete-richlistitem-insecure-warning" }; break; case "loginsFooter": options = { is: "autocomplete-richlistitem-logins-footer" }; break; - case "generatedPassword": case "loginWithOrigin": - options = { is: "autocomplete-two-line-richlistitem" }; + options = { is: "autocomplete-login-richlistitem" }; break; default: options = { is: "autocomplete-richlistitem" }; diff --git a/toolkit/content/widgets/autocomplete-richlistitem.js b/toolkit/content/widgets/autocomplete-richlistitem.js index 34c6dbf10661..a15c8da21a31 100644 --- a/toolkit/content/widgets/autocomplete-richlistitem.js +++ b/toolkit/content/widgets/autocomplete-richlistitem.js @@ -1052,6 +1052,23 @@ class MozAutocompleteTwoLineRichlistitem extends MozElements.MozRichlistitem { handleOverUnderflow() {} } +class MozAutocompleteLoginRichlistitem extends MozAutocompleteTwoLineRichlistitem { + static get inheritedAttributes() { + return { + // getLabelAt: + ".line1-label": "text=ac-value", + // Don't inherit ac-label with getCommentAt since the label is JSON. + }; + } + + _adjustAcItem() { + super._adjustAcItem(); + + let details = JSON.parse(this.getAttribute("ac-label")); + this.querySelector(".line2-label").textContent = details.comment; + } +} + customElements.define("autocomplete-richlistitem", MozElements.MozAutocompleteRichlistitem, { extends: "richlistitem", }); @@ -1067,4 +1084,8 @@ customElements.define("autocomplete-richlistitem-logins-footer", MozAutocomplete customElements.define("autocomplete-two-line-richlistitem", MozAutocompleteTwoLineRichlistitem, { extends: "richlistitem", }); + +customElements.define("autocomplete-login-richlistitem", MozAutocompleteLoginRichlistitem, { + extends: "richlistitem", +}); } diff --git a/toolkit/content/widgets/autocomplete.xml b/toolkit/content/widgets/autocomplete.xml index 8f8094f57e14..84881c559837 100644 --- a/toolkit/content/widgets/autocomplete.xml +++ b/toolkit/content/widgets/autocomplete.xml @@ -1042,15 +1042,17 @@ case "autofill-insecureWarning": options = { is: "autocomplete-creditcard-insecure-field" }; break; + case "generatedPassword": + options = { is: "autocomplete-two-line-richlistitem" }; + break; case "insecureWarning": options = { is: "autocomplete-richlistitem-insecure-warning" }; break; case "loginsFooter": options = { is: "autocomplete-richlistitem-logins-footer" }; break; - case "generatedPassword": case "loginWithOrigin": - options = { is: "autocomplete-two-line-richlistitem" }; + options = { is: "autocomplete-login-richlistitem" }; break; default: options = { is: "autocomplete-richlistitem" }; From 4d696c5df39950d17b310c845f1449bfddc7d808 Mon Sep 17 00:00:00 2001 From: Kagami Sascha Rosylight Date: Wed, 29 May 2019 07:58:19 +0000 Subject: [PATCH 02/36] Bug 1554186: Fix Python 3 negative import level error r=glandium Negative value for import level is obsolete in Python 3, which was used on Py2 for implicit relative import. This change ensures the level value to be >=0 on Py3 to fix test failures. Differential Revision: https://phabricator.services.mozilla.com/D32497 --HG-- extra : moz-landing-system : lando --- build/mach_bootstrap.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build/mach_bootstrap.py b/build/mach_bootstrap.py index 6bf0d9963d55..ca19d7b7a884 100644 --- a/build/mach_bootstrap.py +++ b/build/mach_bootstrap.py @@ -388,6 +388,9 @@ class ImportHook(object): def __call__(self, name, globals=None, locals=None, fromlist=None, level=-1): + if sys.version_info[0] >= 3 and level < 0: + level = 0 + # name might be a relative import. Instead of figuring out what that # resolves to, which is complex, just rely on the real import. # Since we don't know the full module name, we can't check sys.modules, From 1745f1b4ebdb05f5ea802f33ecaa2dbebb6fe59c Mon Sep 17 00:00:00 2001 From: Patrick Brosset Date: Wed, 29 May 2019 08:39:43 +0000 Subject: [PATCH 03/36] Bug 1554723 - Correctly exclude all inline-level elements when warning for misuse of vertical-align r=miker Differential Revision: https://phabricator.services.mozilla.com/D32799 --HG-- extra : moz-landing-system : lando --- .../client/inspector/rules/test/browser.ini | 1 + .../test/browser_rules_inactive_css_inline.js | 76 +++++++++++++++++++ .../actors/utils/inactive-property-helper.js | 22 +++++- 3 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 devtools/client/inspector/rules/test/browser_rules_inactive_css_inline.js diff --git a/devtools/client/inspector/rules/test/browser.ini b/devtools/client/inspector/rules/test/browser.ini index e0fd5578d321..cfbe6e233dbb 100644 --- a/devtools/client/inspector/rules/test/browser.ini +++ b/devtools/client/inspector/rules/test/browser.ini @@ -199,6 +199,7 @@ skip-if = os == 'linux' # focusEditableField times out consistently on linux. [browser_rules_highlight-used-fonts.js] [browser_rules_inactive_css_flexbox.js] [browser_rules_inactive_css_grid.js] +[browser_rules_inactive_css_inline.js] [browser_rules_inherited-properties_01.js] [browser_rules_inherited-properties_02.js] [browser_rules_inherited-properties_03.js] diff --git a/devtools/client/inspector/rules/test/browser_rules_inactive_css_inline.js b/devtools/client/inspector/rules/test/browser_rules_inactive_css_inline.js new file mode 100644 index 000000000000..7abbea3816e5 --- /dev/null +++ b/devtools/client/inspector/rules/test/browser_rules_inactive_css_inline.js @@ -0,0 +1,76 @@ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test css properties that are inactive on block-level elements. + +const TEST_URI = ` + +

browser_rules_inactive_css_inline.js

+
Block
+ + +
A table cell
+
Inline flex element
+`; + +const TEST_DATA = [ + { + selector: "h1", + inactiveDeclarations: [ + { + declaration: { "vertical-align": "text-bottom" }, + ruleIndex: 0, + }, + ], + }, + { + selector: "#block", + inactiveDeclarations: [ + { + declaration: { "vertical-align": "sub" }, + ruleIndex: 1, + }, + ], + }, + { + selector: "td", + activeDeclarations: [ + { + declarations: { "vertical-align": "super" }, + ruleIndex: 1, + }, + ], + }, + { + selector: "#flex", + activeDeclarations: [ + { + declarations: { "vertical-align": "text-bottom" }, + ruleIndex: 1, + }, + ], + }, +]; + +add_task(async function() { + await pushPref("devtools.inspector.inactive.css.enabled", true); + await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); + const { inspector, view } = await openRuleView(); + + await runInactiveCSSTests(view, inspector, TEST_DATA); +}); diff --git a/devtools/server/actors/utils/inactive-property-helper.js b/devtools/server/actors/utils/inactive-property-helper.js index 386feefe3ba1..86d7e7aa5764 100644 --- a/devtools/server/actors/utils/inactive-property-helper.js +++ b/devtools/server/actors/utils/inactive-property-helper.js @@ -143,9 +143,7 @@ class InactivePropertyHelper { const isFirstLetter = selectorText && selectorText.includes("::first-letter"); const isFirstLine = selectorText && selectorText.includes("::first-line"); - const isInlineLevel = this.checkStyle("display", ["inline", "table-cell"]); - - return !isInlineLevel && !isFirstLetter && !isFirstLine; + return !this.isInlineLevel() && !isFirstLetter && !isFirstLine; }, fixId: "inactive-css-not-inline-or-tablecell-fix", msgId: "inactive-css-not-inline-or-tablecell", @@ -299,6 +297,24 @@ class InactivePropertyHelper { return values.some(value => this.style[propName] === value); } + /** + * Check if the current node is an inline-level box. + */ + isInlineLevel() { + return this.checkStyle("display", [ + "inline", + "inline-block", + "inline-table", + "inline-flex", + "inline-grid", + "table-cell", + "table-row", + "table-row-group", + "table-header-group", + "table-footer-group", + ]); + } + /** * Check if the current node is a flex container i.e. a node that has a style * of `display:flex` or `display:inline-flex`. From cff252373975ef0a1d59db1fb6118cd234af39dc Mon Sep 17 00:00:00 2001 From: Johann Hofmann Date: Wed, 29 May 2019 09:46:22 +0000 Subject: [PATCH 04/36] Bug 1535935 - Increase threshold for browser.tabs.remote.logSwitchTiming in pref usage test. r=florian Differential Revision: https://phabricator.services.mozilla.com/D32971 --HG-- extra : moz-landing-system : lando --- .../base/content/test/performance/browser_preferences_usage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/base/content/test/performance/browser_preferences_usage.js b/browser/base/content/test/performance/browser_preferences_usage.js index 63ec9efc814b..b0cf45c2fcd7 100644 --- a/browser/base/content/test/performance/browser_preferences_usage.js +++ b/browser/base/content/test/performance/browser_preferences_usage.js @@ -134,7 +134,7 @@ add_task(async function open_10_tabs() { max: 20, }, "browser.tabs.remote.logSwitchTiming": { - max: 25, + max: 35, }, "network.loadinfo.skip_type_assertion": { // This is accessed in debug only. From 548ca382a53110e8772d32e5bcd21484311a73c5 Mon Sep 17 00:00:00 2001 From: Andrei Lazar Date: Wed, 29 May 2019 09:46:39 +0000 Subject: [PATCH 05/36] Bug 1371618 Pop-ups not displayed if never show checkbox left unmarked r=johannh,petru Added the missing data to the UpdateBlockedPopups event fired from PopupBlockingChild component. Differential Revision: https://phabricator.services.mozilla.com/D31228 --HG-- extra : moz-landing-system : lando --- mobile/android/chrome/content/browser.js | 39 +++++++++++++----------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 2a330d3b517c..86a648093d6c 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -5399,32 +5399,35 @@ var PopupBlockerObserver = { Services.perms.addFromPrincipal(principal, "popup", aAllow ? Ci.nsIPermissionManager.ALLOW_ACTION : Ci.nsIPermissionManager.DENY_ACTION); - dump("Allowing popups for: " + currentURI); }, showPopupsForSite: function showPopupsForSite() { let uri = BrowserApp.selectedBrowser.currentURI; - let {blockedPopups} = BrowserApp.selectedBrowser; - if (blockedPopups) { - for (let i = 0; i < blockedPopups.length; ++i) { - let popupURIspec = blockedPopups[i].popupWindowURIspec; + BrowserApp.selectedBrowser.retrieveListOfBlockedPopups().then(blockedPopups => { + if (blockedPopups) { + for (let i = 0; i < blockedPopups.length; ++i) { + let popupURIspec = blockedPopups[i].popupWindowURIspec; - // Sometimes the popup URI that we get back from blockedPopups - // isn't useful (for instance, netscape.com's popup URI ends up - // being "http://www.netscape.com", which isn't really the URI of - // the popup they're trying to show). This isn't going to be - // useful to the user, so we won't create a menu item for it. - if (popupURIspec == "" || popupURIspec == "about:blank" || popupURIspec == uri.spec) - continue; + // Sometimes the popup URI that we get back from blockedPopups + // isn't useful (for instance, netscape.com's popup URI ends up + // being "http://www.netscape.com", which isn't really the URI of + // the popup they're trying to show). This isn't going to be + // useful to the user, so we won't create a menu item for it. + if (popupURIspec == "" || popupURIspec == "about:blank" || popupURIspec == uri.spec) + continue; - let popupFeatures = blockedPopups[i].popupWindowFeatures; - let popupName = blockedPopups[i].popupWindowName; + let popupFeatures = blockedPopups[i].popupWindowFeatures; + let popupName = blockedPopups[i].popupWindowName; - let parent = BrowserApp.selectedTab; - let isPrivate = PrivateBrowsingUtils.isBrowserPrivate(parent.browser); - BrowserApp.addTab(popupURIspec, { parentId: parent.id, isPrivate: isPrivate }); + let parent = BrowserApp.selectedTab; + let isPrivate = PrivateBrowsingUtils.isBrowserPrivate(parent.browser); + BrowserApp.addTab(popupURIspec, { + parentId: parent.id, + isPrivate: isPrivate, + }); + } } - } + }); }, }; From 2e4ac28424c393966f41196e7c1a235dc807c742 Mon Sep 17 00:00:00 2001 From: "Nicolas B. Pierron" Date: Tue, 28 May 2019 14:32:19 +0000 Subject: [PATCH 06/36] Bug 1546446 - Carry the pool-free size to the finishPool function. r=sstangl Differential Revision: https://phabricator.services.mozilla.com/D28816 --HG-- extra : moz-landing-system : lando --- .../IonAssemblerBufferWithConstantPools.h | 47 ++++++++++++------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h b/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h index 1221426fbd4f..f21aaaf088f4 100644 --- a/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h +++ b/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h @@ -7,6 +7,7 @@ #ifndef jit_shared_IonAssemblerBufferWithConstantPools_h #define jit_shared_IonAssemblerBufferWithConstantPools_h +#include "mozilla/CheckedInt.h" #include "mozilla/MathAlgorithms.h" #include @@ -783,7 +784,7 @@ struct AssemblerBufferWithConstantPools id, sizeExcludingCurrentPool()); } - finishPool(); + finishPool(numInst * InstSize); if (this->oom()) { return OOM_FAIL; } @@ -813,7 +814,7 @@ struct AssemblerBufferWithConstantPools JitSpew(JitSpew_Pools, "[%d] nextInstrOffset @ %d caused a constant pool spill", id, this->nextOffset().getOffset()); - finishPool(); + finishPool(ShortRangeBranchHysteresis); } return this->nextOffset(); } @@ -944,27 +945,38 @@ struct AssemblerBufferWithConstantPools private: // Are any short-range branches about to expire? - bool hasExpirableShortRangeBranches() const { + bool hasExpirableShortRangeBranches(size_t reservedBytes) const { if (branchDeadlines_.empty()) { return false; } - // Include branches that would expire in the next N bytes. - // The hysteresis avoids the needless creation of many tiny constant - // pools. - return this->nextOffset().getOffset() + ShortRangeBranchHysteresis > - size_t(branchDeadlines_.earliestDeadline().getOffset()); + // Include branches that would expire in the next N bytes. The reservedBytes + // argument avoids the needless creation of many tiny constant pools. + // + // As the reservedBytes could be of any sizes such as SIZE_MAX, in the case + // of flushPool, we have to check for overflow when comparing the deadline + // with our expected reserved bytes. + size_t deadline = branchDeadlines_.earliestDeadline().getOffset(); + using CheckedSize = mozilla::CheckedInt; + CheckedSize current(this->nextOffset().getOffset()); + CheckedSize poolFreeSpace(reservedBytes); + auto future = current + poolFreeSpace; + return !future.isValid() || deadline < future.value(); } - bool isPoolEmpty() const { - return pool_.numEntries() == 0 && !hasExpirableShortRangeBranches(); + bool isPoolEmptyFor(size_t bytes) const { + return pool_.numEntries() == 0 && !hasExpirableShortRangeBranches(bytes); } - void finishPool() { + void finishPool(size_t reservedBytes) { JitSpew(JitSpew_Pools, "[%d] Attempting to finish pool %zu with %u entries.", id, poolInfo_.length(), pool_.numEntries()); - if (isPoolEmpty()) { + if (reservedBytes < ShortRangeBranchHysteresis) { + reservedBytes = ShortRangeBranchHysteresis; + } + + if (isPoolEmptyFor(reservedBytes)) { // If there is no data in the pool being dumped, don't dump anything. JitSpew(JitSpew_Pools, "[%d] Aborting because the pool is empty", id); return; @@ -984,7 +996,7 @@ struct AssemblerBufferWithConstantPools // Now generate branch veneers for any short-range branches that are // about to expire. - while (hasExpirableShortRangeBranches()) { + while (hasExpirableShortRangeBranches(reservedBytes)) { unsigned rangeIdx = branchDeadlines_.earliestDeadlineRange(); BufferOffset deadline = branchDeadlines_.earliestDeadline(); @@ -1052,7 +1064,7 @@ struct AssemblerBufferWithConstantPools return; } JitSpew(JitSpew_Pools, "[%d] Requesting a pool flush", id); - finishPool(); + finishPool(SIZE_MAX); } void enterNoPool(size_t maxInst) { @@ -1070,7 +1082,8 @@ struct AssemblerBufferWithConstantPools if (!hasSpaceForInsts(maxInst, 0)) { JitSpew(JitSpew_Pools, "[%d] No-Pool instruction(%zu) caused a spill.", id, sizeExcludingCurrentPool()); - finishPool(); + finishPool(maxInst * InstSize); + MOZ_ASSERT(hasSpaceForInsts(maxInst, 0)); } #ifdef DEBUG @@ -1105,7 +1118,7 @@ struct AssemblerBufferWithConstantPools inhibitNops_ = false; } void assertNoPoolAndNoNops() { - MOZ_ASSERT(inhibitNops_ && (isPoolEmpty() || canNotPlacePool_)); + MOZ_ASSERT(inhibitNops_ && (isPoolEmptyFor(InstSize) || canNotPlacePool_)); } void align(unsigned alignment) { align(alignment, alignFillInst_); } @@ -1130,7 +1143,7 @@ struct AssemblerBufferWithConstantPools // Alignment would cause a pool dump, so dump the pool now. JitSpew(JitSpew_Pools, "[%d] Alignment of %d at %zu caused a spill.", id, alignment, sizeExcludingCurrentPool()); - finishPool(); + finishPool(requiredFill); } bool prevInhibitNops = inhibitNops_; From 055131a66710e66539c27c9166f282687fe816ca Mon Sep 17 00:00:00 2001 From: Razvan Caliman Date: Tue, 28 May 2019 12:32:26 +0000 Subject: [PATCH 07/36] Bug 1554876 - Break logic to mark declarations as overridden into smaller chunks. r=pbro Depends on D32760 Differential Revision: https://phabricator.services.mozilla.com/D32811 --HG-- extra : moz-landing-system : lando --- .../inspector/rules/models/element-style.js | 117 ++++++++++-------- 1 file changed, 68 insertions(+), 49 deletions(-) diff --git a/devtools/client/inspector/rules/models/element-style.js b/devtools/client/inspector/rules/models/element-style.js index cc46102df3b6..3fec3e820d99 100644 --- a/devtools/client/inspector/rules/models/element-style.js +++ b/devtools/client/inspector/rules/models/element-style.js @@ -288,55 +288,10 @@ class ElementStyle { * Optional pseudo-element for which to restrict marking CSS declarations as * overridden. */ - /* eslint-disable complexity */ updateDeclarations(pseudo = "") { - // Gather all the text properties applied by these rules, ordered - // from more- to less-specific. Text properties from keyframes rule are - // excluded from being marked as overridden since a number of criteria such - // as time, and animation overlay are required to be check in order to - // determine if the property is overridden. - const textProps = []; - - for (const rule of this.rules) { - // Skip @keyframes rules - if (rule.keyframes) { - continue; - } - - // Style rules must be considered only when they have selectors that match the node. - // When renaming a selector, the unmatched rule lingers in the Rule view, but it no - // longer matches the node. This strict check avoids accidentally causing - // declarations to be overridden in the remaining matching rules. - const isStyleRule = rule.pseudoElement === "" && rule.matchedSelectors.length > 0; - - // Style rules for pseudo-elements must always be considered, regardless if their - // selector matches the node. As a convenience, declarations in rules for - // pseudo-elements show up in a separate Pseudo-elements accordion when selecting - // the host node (instead of the pseudo-element node directly, which is sometimes - // impossible, for example with ::selection or ::first-line). - // Loosening the strict check on matched selectors ensures these declarations - // participate in the algorithm below to mark them as overridden. - const isPseudoElementRule = rule.pseudoElement !== "" && - rule.pseudoElement === pseudo; - - const isElementStyle = rule.domRule.type === ELEMENT_STYLE; - - const filterCondition = pseudo === "" - ? (isStyleRule || isElementStyle) - : isPseudoElementRule; - - // First, gather all relevant CSS declarations (aka TextProperty instances). - if (filterCondition) { - for (const textProp of rule.textProps.slice(0).reverse()) { - if (textProp.enabled) { - textProps.push(textProp); - } - } - } - } - - // Gather all the computed properties applied by those text - // properties. + // Gather all text properties applicable to the selected element or pseudo-element. + const textProps = this._getDeclarations(pseudo); + // Gather all the computed properties applied by those text properties. let computedProps = []; for (const textProp of textProps) { computedProps = computedProps.concat(textProp.computed); @@ -418,7 +373,71 @@ class ElementStyle { } } } - /* eslint-enable complexity */ + + /** + * Helper for |this.updateDeclarations()| to mark CSS declarations as overridden. + * + * Returns an array of CSS declarations (aka TextProperty instances) from all rules + * applicable to the selected element ordered from more- to less-specific. + * + * If a pseudo-element type is given, restrict the result only to declarations + * applicable to that pseudo-element. + * + * NOTE: this method skips CSS declarations in @keyframes rules because a number of + * criteria such as time and animation delay need to be checked in order to determine + * if the property is overridden at runtime. + * + * @param {String} pseudo + * Optional pseudo-element for which to restrict marking CSS declarations as + * overridden. If omitted, only declarations for regular style rules are + * returned (no pseudo-element style rules). + * + * @return {Array} + * Array of TextProperty instances. + */ + _getDeclarations(pseudo = "") { + const textProps = []; + + for (const rule of this.rules) { + // Skip @keyframes rules + if (rule.keyframes) { + continue; + } + + // Style rules must be considered only when they have selectors that match the node. + // When renaming a selector, the unmatched rule lingers in the Rule view, but it no + // longer matches the node. This strict check avoids accidentally causing + // declarations to be overridden in the remaining matching rules. + const isStyleRule = rule.pseudoElement === "" && rule.matchedSelectors.length > 0; + + // Style rules for pseudo-elements must always be considered, regardless if their + // selector matches the node. As a convenience, declarations in rules for + // pseudo-elements show up in a separate Pseudo-elements accordion when selecting + // the host node (instead of the pseudo-element node directly, which is sometimes + // impossible, for example with ::selection or ::first-line). + // Loosening the strict check on matched selectors ensures these declarations + // participate in the algorithm below to mark them as overridden. + const isPseudoElementRule = rule.pseudoElement !== "" && + rule.pseudoElement === pseudo; + + const isElementStyle = rule.domRule.type === ELEMENT_STYLE; + + const filterCondition = pseudo === "" + ? (isStyleRule || isElementStyle) + : isPseudoElementRule; + + // Collect all relevant CSS declarations (aka TextProperty instances). + if (filterCondition) { + for (const textProp of rule.textProps.slice(0).reverse()) { + if (textProp.enabled) { + textProps.push(textProp); + } + } + } + } + + return textProps; + } /** * Adds a new declaration to the rule. From e78ef3c77012e77193eba06bab21423c2398b5bc Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Tue, 28 May 2019 16:00:33 +0000 Subject: [PATCH 08/36] Bug 1535459 - Make sure ContentChild is connected in middleman processes, r=mccr8. Differential Revision: https://phabricator.services.mozilla.com/D32630 --HG-- extra : moz-landing-system : lando --- dom/ipc/ContentChild.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index aa76d065f91c..9cd48297d766 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -672,6 +672,11 @@ bool ContentChild::Init(MessageLoop* aIOLoop, base::ProcessId aParentPid, // their own children. if (recordreplay::IsMiddleman()) { SetMiddlemanIPCChannel(recordreplay::parent::ChannelToUIProcess()); + + // Eagerly mark this child as connected, as using another IPC channel will + // cause that channel's protocol to be marked as connected instead and + // prevent this one from being able to send IPDL messages. + ActorConnected(); } if (!Open(aChannel, aParentPid, aIOLoop)) { From 17f13cc4f55265092a88cd8cce85bc5c0f4127b6 Mon Sep 17 00:00:00 2001 From: Tom Schuster Date: Wed, 29 May 2019 00:57:46 +0000 Subject: [PATCH 09/36] Bug 1551939 - Try adding three more Proxy checks. r=jwalden Differential Revision: https://phabricator.services.mozilla.com/D32374 --HG-- extra : moz-landing-system : lando --- js/src/js.msg | 2 ++ js/src/proxy/ScriptedProxyHandler.cpp | 35 +++++++++++++++++-- .../Proxy/define-writable-as-non-writable.js | 19 ++++++++++ .../non262/Proxy/delete-non-extensible.js | 18 ++++++++++ .../Proxy/report-writable-as-non-writable.js | 20 +++++++++++ 5 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 js/src/tests/non262/Proxy/define-writable-as-non-writable.js create mode 100644 js/src/tests/non262/Proxy/delete-non-extensible.js create mode 100644 js/src/tests/non262/Proxy/report-writable-as-non-writable.js diff --git a/js/src/js.msg b/js/src/js.msg index 089501621c20..11adcbaf5034 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -435,12 +435,14 @@ MSG_DEF(JSMSG_PROXY_DELETE_RETURNED_FALSE, 1, JSEXN_TYPEERR, "can't delete prope MSG_DEF(JSMSG_PROXY_PREVENTEXTENSIONS_RETURNED_FALSE, 0, JSEXN_TYPEERR, "proxy preventExtensions handler returned false") MSG_DEF(JSMSG_PROXY_SET_RETURNED_FALSE, 1, JSEXN_TYPEERR, "proxy set handler returned false for property '{0}'") MSG_DEF(JSMSG_CANT_REPORT_AS_NON_EXTENSIBLE, 0, JSEXN_TYPEERR, "proxy can't report an extensible object as non-extensible") +MSG_DEF(JSMSG_CANT_DELETE_NON_EXTENSIBLE, 1, JSEXN_TYPEERR, "proxy can't delete property '{0}' on a non-extensible object") MSG_DEF(JSMSG_CANT_REPORT_C_AS_NC, 1, JSEXN_TYPEERR, "proxy can't report existing configurable property '{0}' as non-configurable") MSG_DEF(JSMSG_CANT_REPORT_E_AS_NE, 1, JSEXN_TYPEERR, "proxy can't report an existing own property '{0}' as non-existent on a non-extensible object") MSG_DEF(JSMSG_CANT_REPORT_INVALID, 2, JSEXN_TYPEERR, "proxy can't report an incompatible property descriptor ('{0}', {1})") MSG_DEF(JSMSG_CANT_REPORT_NC_AS_NE, 1, JSEXN_TYPEERR, "proxy can't report a non-configurable own property '{0}' as non-existent") MSG_DEF(JSMSG_CANT_REPORT_NEW, 1, JSEXN_TYPEERR, "proxy can't report a new property '{0}' on a non-extensible object") MSG_DEF(JSMSG_CANT_REPORT_NE_AS_NC, 1, JSEXN_TYPEERR, "proxy can't report a non-existent property '{0}' as non-configurable") +MSG_DEF(JSMSG_CANT_REPORT_W_AS_NW, 1, JSEXN_TYPEERR, "proxy can't report existing writable property '{0}' as non-writable") MSG_DEF(JSMSG_CANT_SET_NW_NC, 1, JSEXN_TYPEERR, "proxy can't successfully set a non-writable, non-configurable property '{0}'") MSG_DEF(JSMSG_CANT_SET_WO_SETTER, 1, JSEXN_TYPEERR, "proxy can't succesfully set an accessor property '{0}' without a setter") MSG_DEF(JSMSG_CANT_SKIP_NC, 1, JSEXN_TYPEERR, "proxy can't skip a non-configurable property '{0}'") diff --git a/js/src/proxy/ScriptedProxyHandler.cpp b/js/src/proxy/ScriptedProxyHandler.cpp index 455b97f47353..c17431a17c08 100644 --- a/js/src/proxy/ScriptedProxyHandler.cpp +++ b/js/src/proxy/ScriptedProxyHandler.cpp @@ -620,6 +620,12 @@ bool ScriptedProxyHandler::getOwnPropertyDescriptor( if (targetDesc.configurable()) { return js::Throw(cx, id, JSMSG_CANT_REPORT_C_AS_NC); } + + if (resultDesc.hasWritable() && !resultDesc.writable()) { + if (targetDesc.writable()) { + return js::Throw(cx, id, JSMSG_CANT_REPORT_W_AS_NW); + } + } } // Step 18. @@ -733,6 +739,17 @@ bool ScriptedProxyHandler::defineProperty(JSContext* cx, HandleObject proxy, return js::Throw(cx, id, JSMSG_CANT_DEFINE_INVALID, DETAILS_CANT_REPORT_C_AS_NC); } + + if (targetDesc.isDataDescriptor() && !targetDesc.configurable() && + targetDesc.writable()) { + if (desc.hasWritable() && !desc.writable()) { + static const char DETAILS_CANT_DEFINE_NW[] = + "proxy can't define an existing non-configurable writable property " + "as non-writable"; + return js::Throw(cx, id, JSMSG_CANT_DEFINE_INVALID, + DETAILS_CANT_DEFINE_NW); + } + } } // Step 17. @@ -992,12 +1009,26 @@ bool ScriptedProxyHandler::delete_(JSContext* cx, HandleObject proxy, return false; } + // Step 11. + if (!desc.object()) { + return result.succeed(); + } + // Step 12. - if (desc.object() && !desc.configurable()) { + if (!desc.configurable()) { return Throw(cx, id, JSMSG_CANT_DELETE); } - // Steps 11,13. + bool extensible; + if (!IsExtensible(cx, target, &extensible)) { + return false; + } + + if (!extensible) { + return Throw(cx, id, JSMSG_CANT_DELETE_NON_EXTENSIBLE); + } + + // Step 13. return result.succeed(); } diff --git a/js/src/tests/non262/Proxy/define-writable-as-non-writable.js b/js/src/tests/non262/Proxy/define-writable-as-non-writable.js new file mode 100644 index 000000000000..f764d278f0bc --- /dev/null +++ b/js/src/tests/non262/Proxy/define-writable-as-non-writable.js @@ -0,0 +1,19 @@ +"use strict"; + +var target = {}; +Object.defineProperty(target, "test", {configurable: false, writable: true, value: 5}); + +var proxy = new Proxy(target, { + defineProperty(target, property) { + assertEq(property, "test"); + return true; + } +}); + +assertThrowsInstanceOf( + () => Object.defineProperty(proxy, "test", {writable: false}), TypeError); + +assertThrowsInstanceOf( + () => Reflect.defineProperty(proxy, "test", {writable: false}), TypeError); + +reportCompare(0, 0); diff --git a/js/src/tests/non262/Proxy/delete-non-extensible.js b/js/src/tests/non262/Proxy/delete-non-extensible.js new file mode 100644 index 000000000000..b31216bb659a --- /dev/null +++ b/js/src/tests/non262/Proxy/delete-non-extensible.js @@ -0,0 +1,18 @@ +"use strict"; + +var target = { test: true }; +Object.preventExtensions(target); + +var proxy = new Proxy(target, { + deleteProperty(target, property) { + return true; + } +}); + +assertEq(delete proxy.missing, true); +assertEq(Reflect.deleteProperty(proxy, "missing"), true); + +assertThrowsInstanceOf(() => { delete proxy.test; }, TypeError); +assertThrowsInstanceOf(() => Reflect.deleteProperty(proxy, "test"), TypeError); + +reportCompare(0, 0); diff --git a/js/src/tests/non262/Proxy/report-writable-as-non-writable.js b/js/src/tests/non262/Proxy/report-writable-as-non-writable.js new file mode 100644 index 000000000000..c0facd10574a --- /dev/null +++ b/js/src/tests/non262/Proxy/report-writable-as-non-writable.js @@ -0,0 +1,20 @@ +"use strict"; + +var target = {}; +Object.defineProperty(target, "test", + {configurable: false, writable: true, value: 1}); + +var proxy = new Proxy(target, { + getOwnPropertyDescriptor(target, property) { + assertEq(property, "test"); + return {configurable: false, writable: false, value: 1}; + } +}); + +assertThrowsInstanceOf(() => Object.getOwnPropertyDescriptor(proxy, "test"), + TypeError); + +assertThrowsInstanceOf(() => Reflect.getOwnPropertyDescriptor(proxy, "test"), + TypeError); + +reportCompare(0, 0); From 17d59d11c8a8abfc2c9ea38a1e835c9227d9d2cc Mon Sep 17 00:00:00 2001 From: Alastor Wu Date: Wed, 29 May 2019 10:19:20 +0000 Subject: [PATCH 10/36] Bug 1555090 - remove 'max-asserts' for wpt 'addCue.html' and 'constructor.html'. r=jgraham There is no need to add `max-asserts` for those two wpts, they are running correctly without hitting any assertion. Differential Revision: https://phabricator.services.mozilla.com/D32869 --HG-- extra : moz-landing-system : lando --- .../media-elements/interfaces/TextTrack/addCue.html.ini | 2 -- .../media-elements/interfaces/TextTrackCue/constructor.html.ini | 2 -- 2 files changed, 4 deletions(-) delete mode 100644 testing/web-platform/meta/html/semantics/embedded-content/media-elements/interfaces/TextTrack/addCue.html.ini delete mode 100644 testing/web-platform/meta/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/constructor.html.ini diff --git a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/interfaces/TextTrack/addCue.html.ini b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/interfaces/TextTrack/addCue.html.ini deleted file mode 100644 index 90a40b2213bd..000000000000 --- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/interfaces/TextTrack/addCue.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[addCue.html] - max-asserts: 1848 diff --git a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/constructor.html.ini b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/constructor.html.ini deleted file mode 100644 index ad961415462a..000000000000 --- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/constructor.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[constructor.html] - max-asserts: 103 From 4b1ff2f791fff4ae90e51045414e75b76dd7693b Mon Sep 17 00:00:00 2001 From: Edgar Chen Date: Wed, 29 May 2019 07:48:16 +0000 Subject: [PATCH 11/36] Bug 1543439 - Part 1: Move special handling for MouseDown/Up event out of AutoHandlingUserInputStatePusher; r=masayuki PresShell::EventHandler::HandleEventWithCurrentEventInfo is the only possible place will do such handling; other places pass either a nullptr or a non-MouseDown/Up event. Differential Revision: https://phabricator.services.mozilla.com/D32431 --HG-- extra : moz-landing-system : lando --- accessible/generic/Accessible.cpp | 3 +- accessible/generic/DocAccessible.cpp | 3 +- dom/events/EventStateManager.cpp | 40 +--------------- dom/events/EventStateManager.h | 15 +----- dom/html/HTMLFormElement.cpp | 2 +- dom/ipc/BrowserParent.cpp | 2 +- layout/base/PresShell.cpp | 65 +++++++++++++++++++++++++- layout/xul/nsXULPopupManager.cpp | 3 +- xpcom/base/CycleCollectedJSContext.cpp | 2 +- 9 files changed, 73 insertions(+), 62 deletions(-) diff --git a/accessible/generic/Accessible.cpp b/accessible/generic/Accessible.cpp index f0765af68c40..b4e4830cbf3d 100644 --- a/accessible/generic/Accessible.cpp +++ b/accessible/generic/Accessible.cpp @@ -742,8 +742,7 @@ void Accessible::TakeFocus() const { nsFocusManager* fm = nsFocusManager::GetFocusManager(); if (fm) { - AutoHandlingUserInputStatePusher inputStatePusher(true, nullptr, - focusContent->OwnerDoc()); + AutoHandlingUserInputStatePusher inputStatePusher(true); // XXXbz: Can we actually have a non-element content here? RefPtr element = focusContent->IsElement() ? focusContent->AsElement() : nullptr; diff --git a/accessible/generic/DocAccessible.cpp b/accessible/generic/DocAccessible.cpp index 991d11a549cd..4160eedd27ca 100644 --- a/accessible/generic/DocAccessible.cpp +++ b/accessible/generic/DocAccessible.cpp @@ -294,8 +294,7 @@ void DocAccessible::TakeFocus() const { // Focus the document. nsFocusManager* fm = nsFocusManager::GetFocusManager(); RefPtr newFocus; - AutoHandlingUserInputStatePusher inputStatePusher(true, nullptr, - mDocumentNode); + AutoHandlingUserInputStatePusher inputStatePusher(true); fm->MoveFocus(mDocumentNode->GetWindow(), nullptr, nsFocusManager::MOVEFOCUS_ROOT, 0, getter_AddRefs(newFocus)); } diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp index eb202fab8270..58aec8fe37c9 100644 --- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -1558,8 +1558,7 @@ void EventStateManager::FireContextClick() { } } - Document* doc = mGestureDownContent->GetComposedDoc(); - AutoHandlingUserInputStatePusher userInpStatePusher(true, &event, doc); + AutoHandlingUserInputStatePusher userInpStatePusher(true, &event); // dispatch to DOM EventDispatcher::Dispatch(mGestureDownContent, mPresContext, &event, @@ -6257,36 +6256,13 @@ void EventStateManager::Prefs::Init() { /******************************************************************/ AutoHandlingUserInputStatePusher::AutoHandlingUserInputStatePusher( - bool aIsHandlingUserInput, WidgetEvent* aEvent, Document* aDocument) + bool aIsHandlingUserInput, WidgetEvent* aEvent) : mMessage(aEvent ? aEvent->mMessage : eVoidEvent), mIsHandlingUserInput(aIsHandlingUserInput) { if (!aIsHandlingUserInput) { return; } EventStateManager::StartHandlingUserInput(mMessage); - if (mMessage == eMouseDown) { - PresShell::ReleaseCapturingContent(); - PresShell::AllowMouseCapture(true); - } - if (!aDocument || !aEvent || !aEvent->IsTrusted()) { - return; - } - if (NeedsToResetFocusManagerMouseButtonHandlingState()) { - nsFocusManager* fm = nsFocusManager::GetFocusManager(); - NS_ENSURE_TRUE_VOID(fm); - // If it's in modal state, mouse button event handling may be nested. - // E.g., a modal dialog is opened at mousedown or mouseup event handler - // and the dialog is clicked. Therefore, we should store current - // mouse button event handling document if nsFocusManager already has it. - mMouseButtonEventHandlingDocument = - fm->SetMouseButtonHandlingDocument(aDocument); - } - if (NeedsToUpdateCurrentMouseBtnState()) { - WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent(); - if (mouseEvent) { - EventStateManager::sCurrentMouseBtn = mouseEvent->mButton; - } - } } AutoHandlingUserInputStatePusher::~AutoHandlingUserInputStatePusher() { @@ -6294,18 +6270,6 @@ AutoHandlingUserInputStatePusher::~AutoHandlingUserInputStatePusher() { return; } EventStateManager::StopHandlingUserInput(mMessage); - if (mMessage == eMouseDown) { - PresShell::AllowMouseCapture(false); - } - if (NeedsToResetFocusManagerMouseButtonHandlingState()) { - nsFocusManager* fm = nsFocusManager::GetFocusManager(); - NS_ENSURE_TRUE_VOID(fm); - nsCOMPtr handlingDocument = - fm->SetMouseButtonHandlingDocument(mMouseButtonEventHandlingDocument); - } - if (NeedsToUpdateCurrentMouseBtnState()) { - EventStateManager::sCurrentMouseBtn = MouseButton::eNotPressed; - } } } // namespace mozilla diff --git a/dom/events/EventStateManager.h b/dom/events/EventStateManager.h index fe24441a16f8..ba28ac06ab80 100644 --- a/dom/events/EventStateManager.h +++ b/dom/events/EventStateManager.h @@ -1286,24 +1286,13 @@ class EventStateManager : public nsSupportsWeakReference, public nsIObserver { */ class MOZ_RAII AutoHandlingUserInputStatePusher final { public: - AutoHandlingUserInputStatePusher(bool aIsHandlingUserInput, - WidgetEvent* aEvent, - dom::Document* aDocument); + explicit AutoHandlingUserInputStatePusher(bool aIsHandlingUserInput, + WidgetEvent* aEvent = nullptr); ~AutoHandlingUserInputStatePusher(); protected: - RefPtr mMouseButtonEventHandlingDocument; EventMessage mMessage; bool mIsHandlingUserInput; - - bool NeedsToResetFocusManagerMouseButtonHandlingState() const { - return mMessage == eMouseDown || mMessage == eMouseUp; - } - - bool NeedsToUpdateCurrentMouseBtnState() const { - return mMessage == eMouseDown || mMessage == eMouseUp || - mMessage == ePointerDown || mMessage == ePointerUp; - } }; } // namespace mozilla diff --git a/dom/html/HTMLFormElement.cpp b/dom/html/HTMLFormElement.cpp index 8688cbc10985..1b9b8f214384 100644 --- a/dom/html/HTMLFormElement.cpp +++ b/dom/html/HTMLFormElement.cpp @@ -698,7 +698,7 @@ nsresult HTMLFormElement::SubmitSubmission( AutoPopupStatePusher popupStatePusher(mSubmitPopupState); AutoHandlingUserInputStatePusher userInpStatePusher( - aFormSubmission->IsInitiatedFromUserInput(), nullptr, doc); + aFormSubmission->IsInitiatedFromUserInput()); nsCOMPtr postDataStream; rv = aFormSubmission->GetEncodedSubmission( diff --git a/dom/ipc/BrowserParent.cpp b/dom/ipc/BrowserParent.cpp index 1b6c09a1c34c..52959bc1d13a 100644 --- a/dom/ipc/BrowserParent.cpp +++ b/dom/ipc/BrowserParent.cpp @@ -2285,7 +2285,7 @@ mozilla::ipc::IPCResult BrowserParent::RecvReplyKeyEvent( NS_ENSURE_TRUE(presContext, IPC_OK()); AutoHandlingUserInputStatePusher userInpStatePusher(localEvent.IsTrusted(), - &localEvent, doc); + &localEvent); nsEventStatus status = nsEventStatus_eIgnore; diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp index f76edc908449..b6d05b2ac314 100644 --- a/layout/base/PresShell.cpp +++ b/layout/base/PresShell.cpp @@ -7730,6 +7730,67 @@ nsresult PresShell::EventHandler::HandleEventWithTarget( return rv; } +namespace { + +class MOZ_RAII AutoEventHandler final { + public: + AutoEventHandler(WidgetEvent* aEvent, Document* aDocument) : mEvent(aEvent) { + MOZ_ASSERT(mEvent); + MOZ_ASSERT(mEvent->IsTrusted()); + + if (mEvent->mMessage == eMouseDown) { + PresShell::ReleaseCapturingContent(); + PresShell::AllowMouseCapture(true); + } + if (aDocument && NeedsToResetFocusManagerMouseButtonHandlingState()) { + nsFocusManager* fm = nsFocusManager::GetFocusManager(); + NS_ENSURE_TRUE_VOID(fm); + // If it's in modal state, mouse button event handling may be nested. + // E.g., a modal dialog is opened at mousedown or mouseup event handler + // and the dialog is clicked. Therefore, we should store current + // mouse button event handling document if nsFocusManager already has it. + mMouseButtonEventHandlingDocument = + fm->SetMouseButtonHandlingDocument(aDocument); + } + if (NeedsToUpdateCurrentMouseBtnState()) { + WidgetMouseEvent* mouseEvent = mEvent->AsMouseEvent(); + if (mouseEvent) { + EventStateManager::sCurrentMouseBtn = mouseEvent->mButton; + } + } + } + + ~AutoEventHandler() { + if (mEvent->mMessage == eMouseDown) { + PresShell::AllowMouseCapture(false); + } + if (NeedsToResetFocusManagerMouseButtonHandlingState()) { + nsFocusManager* fm = nsFocusManager::GetFocusManager(); + NS_ENSURE_TRUE_VOID(fm); + RefPtr document = + fm->SetMouseButtonHandlingDocument(mMouseButtonEventHandlingDocument); + } + if (NeedsToUpdateCurrentMouseBtnState()) { + EventStateManager::sCurrentMouseBtn = MouseButton::eNotPressed; + } + } + + protected: + bool NeedsToResetFocusManagerMouseButtonHandlingState() const { + return mEvent->mMessage == eMouseDown || mEvent->mMessage == eMouseUp; + } + + bool NeedsToUpdateCurrentMouseBtnState() const { + return mEvent->mMessage == eMouseDown || mEvent->mMessage == eMouseUp || + mEvent->mMessage == ePointerDown || mEvent->mMessage == ePointerUp; + } + + RefPtr mMouseButtonEventHandlingDocument; + WidgetEvent* mEvent; +}; + +} // anonymous namespace + nsresult PresShell::EventHandler::HandleEventWithCurrentEventInfo( WidgetEvent* aEvent, nsEventStatus* aEventStatus, bool aIsHandlingNativeEvent, nsIContent* aOverrideClickTarget) { @@ -7768,8 +7829,8 @@ nsresult PresShell::EventHandler::HandleEventWithCurrentEventInfo( RecordEventPreparationPerformance(aEvent); AutoHandlingUserInputStatePusher userInpStatePusher(isHandlingUserInput, - aEvent, GetDocument()); - + aEvent); + AutoEventHandler eventHandler(aEvent, GetDocument()); AutoPopupStatePusher popupStatePusher( PopupBlocker::GetEventPopupControlState(aEvent)); diff --git a/layout/xul/nsXULPopupManager.cpp b/layout/xul/nsXULPopupManager.cpp index 2cd563b779de..bc8e2a5fbf56 100644 --- a/layout/xul/nsXULPopupManager.cpp +++ b/layout/xul/nsXULPopupManager.cpp @@ -2667,8 +2667,7 @@ nsXULMenuCommandEvent::Run() { // Deselect ourselves. if (mCloseMenuMode != CloseMenuMode_None) menuFrame->SelectMenu(false); - AutoHandlingUserInputStatePusher userInpStatePusher( - mUserInput, nullptr, presShell->GetDocument()); + AutoHandlingUserInputStatePusher userInpStatePusher(mUserInput); RefPtr menu = mMenu; nsContentUtils::DispatchXULCommand(menu, mIsTrusted, nullptr, presShell, mControl, mAlt, mShift, mMeta); diff --git a/xpcom/base/CycleCollectedJSContext.cpp b/xpcom/base/CycleCollectedJSContext.cpp index e2d318e939a8..66c8b9494d4f 100644 --- a/xpcom/base/CycleCollectedJSContext.cpp +++ b/xpcom/base/CycleCollectedJSContext.cpp @@ -233,7 +233,7 @@ class PromiseJobRunnable final : public MicroTaskRunnable { doc = win->GetExtantDoc(); } AutoHandlingUserInputStatePusher userInpStatePusher( - mPropagateUserInputEventHandling, nullptr, doc); + mPropagateUserInputEventHandling); mCallback->Call("promise callback"); aAso.CheckForInterrupt(); From 33d303678cd1845f87c4ec400c2f7c617ffdef16 Mon Sep 17 00:00:00 2001 From: Edgar Chen Date: Mon, 27 May 2019 10:14:06 +0000 Subject: [PATCH 12/36] Bug 1543439 - Part 2: Move is-user-interaction checks out of PresShell::EventHandler::PrepareToDispatchEvent; r=masayuki Differential Revision: https://phabricator.services.mozilla.com/D32019 --HG-- extra : moz-landing-system : lando --- dom/base/PopupBlocker.cpp | 8 ++++---- dom/events/EventStateManager.cpp | 30 ++++++++++++++++++++++++++++++ dom/events/EventStateManager.h | 6 ++++++ layout/base/PresShell.cpp | 31 +++++-------------------------- layout/base/PresShell.h | 7 +------ layout/base/TouchManager.cpp | 6 +----- layout/base/TouchManager.h | 2 +- 7 files changed, 48 insertions(+), 42 deletions(-) diff --git a/dom/base/PopupBlocker.cpp b/dom/base/PopupBlocker.cpp index 562812a10fad..b8e2b90db2b0 100644 --- a/dom/base/PopupBlocker.cpp +++ b/dom/base/PopupBlocker.cpp @@ -185,7 +185,7 @@ PopupBlocker::PopupControlState PopupBlocker::GetEventPopupControlState( case eBasicEventClass: // For these following events only allow popups if they're // triggered while handling user input. See - // PresShell::EventHandler::PrepareToDispatchEvent() for details. + // EventStateManager::IsUserInteractionEvent() for details. if (EventStateManager::IsHandlingUserInput()) { abuse = PopupBlocker::openBlocked; switch (aEvent->mMessage) { @@ -207,7 +207,7 @@ PopupBlocker::PopupControlState PopupBlocker::GetEventPopupControlState( case eEditorInputEventClass: // For this following event only allow popups if it's triggered // while handling user input. See - // PresShell::EventHandler::PrepareToDispatchEvent() for details. + // EventStateManager::IsUserInteractionEvent() for details. if (EventStateManager::IsHandlingUserInput()) { abuse = PopupBlocker::openBlocked; switch (aEvent->mMessage) { @@ -224,7 +224,7 @@ PopupBlocker::PopupControlState PopupBlocker::GetEventPopupControlState( case eInputEventClass: // For this following event only allow popups if it's triggered // while handling user input. See - // PresShell::EventHandler::PrepareToDispatchEvent() for details. + // EventStateManager::IsUserInteractionEvent() for details. if (EventStateManager::IsHandlingUserInput()) { abuse = PopupBlocker::openBlocked; switch (aEvent->mMessage) { @@ -370,7 +370,7 @@ PopupBlocker::PopupControlState PopupBlocker::GetEventPopupControlState( case eFormEventClass: // For these following events only allow popups if they're // triggered while handling user input. See - // PresShell::EventHandler::PrepareToDispatchEvent() for details. + // EventStateManager::IsUserInteractionEvent() for details. if (EventStateManager::IsHandlingUserInput()) { abuse = PopupBlocker::openBlocked; switch (aEvent->mMessage) { diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp index 58aec8fe37c9..e741bf1150e7 100644 --- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -4041,6 +4041,36 @@ class MOZ_STACK_CLASS ESMEventCB : public EventDispatchingCallback { nsCOMPtr mTarget; }; +/*static*/ +bool EventStateManager::IsUserInteractionEvent(const WidgetEvent* aEvent) { + if (!aEvent->IsTrusted()) { + return false; + } + + switch (aEvent->mMessage) { + // eKeyboardEventClass + case eKeyPress: + case eKeyDown: + case eKeyUp: + // Not all keyboard events are treated as user input, so that popups + // can't be opened, fullscreen mode can't be started, etc at + // unexpected time. + return aEvent->AsKeyboardEvent()->CanTreatAsUserInput(); + // eMouseEventClass + case eMouseDown: + case eMouseUp: + // ePointerEventClass + case ePointerDown: + case ePointerUp: + // eTouchEventClass + case eTouchStart: + case eTouchEnd: + return true; + default: + return false; + } +} + /*static*/ bool EventStateManager::IsHandlingUserInput() { return sUserInputEventDepth > 0; diff --git a/dom/events/EventStateManager.h b/dom/events/EventStateManager.h index ba28ac06ab80..51bdd754eafe 100644 --- a/dom/events/EventStateManager.h +++ b/dom/events/EventStateManager.h @@ -243,6 +243,12 @@ class EventStateManager : public nsSupportsWeakReference, public nsIObserver { const Maybe& aHotspot, nsIWidget* aWidget, bool aLockCursor); + /** + * Returns true if the event is considered as user interaction event. I.e., + * enough obvious input to allow to open popup, etc. Otherwise, returns false. + */ + static bool IsUserInteractionEvent(const WidgetEvent* aEvent); + /** * StartHandlingUserInput() is called when we start to handle a user input. * StopHandlingUserInput() is called when we finish handling a user input. diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp index b6d05b2ac314..8833e817e44a 100644 --- a/layout/base/PresShell.cpp +++ b/layout/base/PresShell.cpp @@ -7817,10 +7817,8 @@ nsresult PresShell::EventHandler::HandleEventWithCurrentEventInfo( } } - bool isHandlingUserInput = false; bool touchIsNew = false; - if (!PrepareToDispatchEvent(aEvent, aEventStatus, &isHandlingUserInput, - &touchIsNew)) { + if (!PrepareToDispatchEvent(aEvent, aEventStatus, &touchIsNew)) { return NS_OK; } @@ -7828,8 +7826,8 @@ nsresult PresShell::EventHandler::HandleEventWithCurrentEventInfo( // performance. RecordEventPreparationPerformance(aEvent); - AutoHandlingUserInputStatePusher userInpStatePusher(isHandlingUserInput, - aEvent); + AutoHandlingUserInputStatePusher userInpStatePusher( + EventStateManager::IsUserInteractionEvent(aEvent), aEvent); AutoEventHandler eventHandler(aEvent, GetDocument()); AutoPopupStatePusher popupStatePusher( PopupBlocker::GetEventPopupControlState(aEvent)); @@ -7938,11 +7936,9 @@ nsresult PresShell::EventHandler::DispatchEvent( } bool PresShell::EventHandler::PrepareToDispatchEvent( - WidgetEvent* aEvent, nsEventStatus* aEventStatus, bool* aIsUserInteraction, - bool* aTouchIsNew) { + WidgetEvent* aEvent, nsEventStatus* aEventStatus, bool* aTouchIsNew) { MOZ_ASSERT(aEvent->IsTrusted()); MOZ_ASSERT(aEventStatus); - MOZ_ASSERT(aIsUserInteraction); MOZ_ASSERT(aTouchIsNew); *aTouchIsNew = false; @@ -7956,26 +7952,14 @@ bool PresShell::EventHandler::PrepareToDispatchEvent( case eKeyUp: { WidgetKeyboardEvent* keyboardEvent = aEvent->AsKeyboardEvent(); MaybeHandleKeyboardEventBeforeDispatch(keyboardEvent); - // Not all keyboard events are treated as user input, so that popups - // can't be opened, fullscreen mode can't be started, etc at unexpected - // time. - *aIsUserInteraction = keyboardEvent->CanTreatAsUserInput(); return true; } - case eMouseDown: - case eMouseUp: - case ePointerDown: - case ePointerUp: - *aIsUserInteraction = true; - return true; - case eMouseMove: { bool allowCapture = EventStateManager::GetActiveEventStateManager() && GetPresContext() && GetPresContext()->EventStateManager() == EventStateManager::GetActiveEventStateManager(); PresShell::AllowMouseCapture(allowCapture); - *aIsUserInteraction = false; return true; } case eDrop: { @@ -7987,12 +7971,9 @@ bool PresShell::EventHandler::PrepareToDispatchEvent( aEvent->mFlags.mOnlyChromeDispatch = true; } } - *aIsUserInteraction = false; return true; } case eContextMenu: { - *aIsUserInteraction = false; - // If we cannot open context menu even though eContextMenu is fired, we // should stop dispatching it into the DOM. WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent(); @@ -8017,10 +7998,8 @@ bool PresShell::EventHandler::PrepareToDispatchEvent( case eTouchCancel: case eTouchPointerCancel: return mPresShell->mTouchManager.PreHandleEvent( - aEvent, aEventStatus, *aTouchIsNew, *aIsUserInteraction, - mPresShell->mCurrentEventContent); + aEvent, aEventStatus, *aTouchIsNew, mPresShell->mCurrentEventContent); default: - *aIsUserInteraction = false; return true; } } diff --git a/layout/base/PresShell.h b/layout/base/PresShell.h index f972d827029a..aba7d6a02b06 100644 --- a/layout/base/PresShell.h +++ b/layout/base/PresShell.h @@ -2563,10 +2563,6 @@ class PresShell final : public nsStubDocumentObserver, * * @param aEvent The handling event. * @param aEventStatus [in/out] The status of aEvent. - * @param aIsUserInteraction [out] Set to true if the event is user - * interaction. I.e., enough obvious input - * to allow to open popup, etc. Otherwise, - * set to false. * @param aTouchIsNew [out] Set to true if the event is an * eTouchMove event and it represents new * touch. Otherwise, set to false. @@ -2575,8 +2571,7 @@ class PresShell final : public nsStubDocumentObserver, */ MOZ_CAN_RUN_SCRIPT bool PrepareToDispatchEvent(WidgetEvent* aEvent, - nsEventStatus* aEventStatus, - bool* aIsUserInteraction, bool* aTouchIsNew); + nsEventStatus* aEventStatus, bool* aTouchIsNew); /** * MaybeHandleKeyboardEventBeforeDispatch() may handle aKeyboardEvent diff --git a/layout/base/TouchManager.cpp b/layout/base/TouchManager.cpp index 2b79b9537848..4e8ddce764ed 100644 --- a/layout/base/TouchManager.cpp +++ b/layout/base/TouchManager.cpp @@ -220,7 +220,7 @@ nsIFrame* TouchManager::SuppressInvalidPointsAndGetTargetedFrame( } bool TouchManager::PreHandleEvent(WidgetEvent* aEvent, nsEventStatus* aStatus, - bool& aTouchIsNew, bool& aIsHandlingUserInput, + bool& aTouchIsNew, nsCOMPtr& aCurrentEventContent) { MOZ_DIAGNOSTIC_ASSERT(aEvent->IsTrusted()); @@ -228,7 +228,6 @@ bool TouchManager::PreHandleEvent(WidgetEvent* aEvent, nsEventStatus* aStatus, // cases in PresShell::EventHandler::PrepareToDispatchEvent(). switch (aEvent->mMessage) { case eTouchStart: { - aIsHandlingUserInput = true; WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent(); // if there is only one touch in this touchstart event, assume that it is // the start of a new touch session and evict any old touches in the @@ -335,9 +334,6 @@ bool TouchManager::PreHandleEvent(WidgetEvent* aEvent, nsEventStatus* aStatus, break; } case eTouchEnd: - aIsHandlingUserInput = true; - // Fall through to touchcancel code - MOZ_FALLTHROUGH; case eTouchCancel: { // Remove the changed touches // need to make sure we only remove touches that are ending here diff --git a/layout/base/TouchManager.h b/layout/base/TouchManager.h index cb4d19630927..42844a6b75db 100644 --- a/layout/base/TouchManager.h +++ b/layout/base/TouchManager.h @@ -49,7 +49,7 @@ class TouchManager { WidgetTouchEvent* aEvent); bool PreHandleEvent(mozilla::WidgetEvent* aEvent, nsEventStatus* aStatus, - bool& aTouchIsNew, bool& aIsHandlingUserInput, + bool& aTouchIsNew, nsCOMPtr& aCurrentEventContent); static already_AddRefed GetAnyCapturedTouchTarget(); From b11b3efd4449bdcf87145886f1a01da34079bee6 Mon Sep 17 00:00:00 2001 From: Edgar Chen Date: Mon, 27 May 2019 12:50:33 +0000 Subject: [PATCH 13/36] Bug 1543439 - Part 3: change and click event fired from browser UI should be considered as an user interaction event; r=masayuki In general case, we set `IsHandlingUserInput` flag in `PresShell::EventHandler::HandleEventWithCurrentEventInfo()` for user interaction event. However the interaction could happen on parent process, like e10s select and input date etc., whose dropdown/popup menu is implemented on parent process and it sends the interaction result to content process, then generates/dispatches corresponding event on content process directly. For such case, we need an addition hook before calling the event listener, just like what we do for popup blocker. It also makes more sense that we handle `popup blocker state` and `IsHandlingUserInput` flag at a consistent time. Differential Revision: https://phabricator.services.mozilla.com/D32020 --HG-- extra : moz-landing-system : lando --- dom/events/EventListenerManager.cpp | 3 +++ dom/events/EventStateManager.cpp | 3 +++ 2 files changed, 6 insertions(+) diff --git a/dom/events/EventListenerManager.cpp b/dom/events/EventListenerManager.cpp index 27c9aa1fd1a1..840556c6eb3b 100644 --- a/dom/events/EventListenerManager.cpp +++ b/dom/events/EventListenerManager.cpp @@ -1141,8 +1141,11 @@ void EventListenerManager::HandleEventInternal(nsPresContext* aPresContext, aEvent->PreventDefault(); } + Maybe userInputStatePusher; Maybe popupStatePusher; if (mIsMainThreadELM) { + userInputStatePusher.emplace( + EventStateManager::IsUserInteractionEvent(aEvent), aEvent); popupStatePusher.emplace( PopupBlocker::GetEventPopupControlState(aEvent, *aDOMEvent)); } diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp index e741bf1150e7..0248985a4091 100644 --- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -4056,7 +4056,10 @@ bool EventStateManager::IsUserInteractionEvent(const WidgetEvent* aEvent) { // can't be opened, fullscreen mode can't be started, etc at // unexpected time. return aEvent->AsKeyboardEvent()->CanTreatAsUserInput(); + // eBasicEventClass + case eFormChange: // eMouseEventClass + case eMouseClick: case eMouseDown: case eMouseUp: // ePointerEventClass From e2745056eba06ddbfa78c0b5f6030624b8139801 Mon Sep 17 00:00:00 2001 From: Edgar Chen Date: Tue, 28 May 2019 23:47:48 +0000 Subject: [PATCH 14/36] Bug 1543439 - Part 4: Add tests; r=mconley Differential Revision: https://phabricator.services.mozilla.com/D32600 --HG-- extra : moz-landing-system : lando --- .../content/test/forms/browser_selectpopup.js | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/browser/base/content/test/forms/browser_selectpopup.js b/browser/base/content/test/forms/browser_selectpopup.js index cd14ee10a01f..2267bda416e6 100644 --- a/browser/base/content/test/forms/browser_selectpopup.js +++ b/browser/base/content/test/forms/browser_selectpopup.js @@ -794,3 +794,47 @@ add_task(async function test_blur_hides_popup() { BrowserTestUtils.removeTab(tab); }); + +function getIsHandlingUserInput(browser, elementId, eventName) { + return ContentTask.spawn(browser, [elementId, eventName], + async function([contentElementId, contentEventName]) { + let element = content.document.getElementById(contentElementId); + let isHandlingUserInput = false; + await ContentTaskUtils.waitForEvent(element, contentEventName, false, e => { + isHandlingUserInput = content.window.windowUtils.isHandlingUserInput; + return true; + }); + + return isHandlingUserInput; + }); +} + +// This test checks if the change/click event is considered as user input event. +add_task(async function test_handling_user_input() { + const pageUrl = "data:text/html," + escape(PAGECONTENT_SMALL); + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl); + + let menulist = document.getElementById("ContentSelectDropdown"); + let selectPopup = menulist.menupopup; + + // Test onchange event when changing value via keyboard. + await openSelectPopup(selectPopup, "click", "#one"); + let getPromise = getIsHandlingUserInput(tab.linkedBrowser, "one", "change"); + EventUtils.synthesizeKey("KEY_ArrowDown"); + await hideSelectPopup(selectPopup); + is(await getPromise, true, "isHandlingUserInput should be true"); + + // Test onchange event when changing value via mouse click + await openSelectPopup(selectPopup, "click", "#two"); + getPromise = getIsHandlingUserInput(tab.linkedBrowser, "two", "change"); + EventUtils.synthesizeMouseAtCenter(selectPopup.lastElementChild, {}); + is(await getPromise, true, "isHandlingUserInput should be true"); + + // Test onclick event fired from clicking select popup. + await openSelectPopup(selectPopup, "click", "#three"); + getPromise = getIsHandlingUserInput(tab.linkedBrowser, "three", "click"); + EventUtils.synthesizeMouseAtCenter(selectPopup.firstElementChild, {}); + is(await getPromise, true, "isHandlingUserInput should be true"); + + BrowserTestUtils.removeTab(tab); +}); From 6d9fff01687ac9628bd84f370d494dab97af4d64 Mon Sep 17 00:00:00 2001 From: Masatoshi Kimura Date: Wed, 29 May 2019 10:13:28 +0000 Subject: [PATCH 15/36] Bug 1554380 - Fix some issues in mozilla::ReadAhead. r=aklotz * CreateFileW will return INVALID_HANDLE_VALUE (-1) on failure, not NULL (0). * MapViewOfFile will map the entire section if the size is 0. No explicit size is required. * If SEC_IMAGE is specified, the mapped image size may be different from the file size on the storage. Differential Revision: https://phabricator.services.mozilla.com/D32563 --HG-- extra : moz-landing-system : lando --- mozglue/misc/NativeNt.h | 10 ++++++++++ xpcom/base/nsAutoRef.h | 2 ++ xpcom/glue/FileUtils.cpp | 25 ++++++++++++------------- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/mozglue/misc/NativeNt.h b/mozglue/misc/NativeNt.h index 66255273c665..389d66d16b49 100644 --- a/mozglue/misc/NativeNt.h +++ b/mozglue/misc/NativeNt.h @@ -320,6 +320,16 @@ class MOZ_RAII PEHeaders final { return reinterpret_cast(absAddress); } + Maybe> GetBounds() const { + if (!mImageLimit) { + return Nothing(); + } + + auto base = reinterpret_cast(mMzHeader); + DWORD imageSize = mPeHeader->OptionalHeader.SizeOfImage; + return Some(MakeSpan(base, imageSize)); + } + PIMAGE_IMPORT_DESCRIPTOR GetImportDirectory() { return GetImageDirectoryEntry( IMAGE_DIRECTORY_ENTRY_IMPORT); diff --git a/xpcom/base/nsAutoRef.h b/xpcom/base/nsAutoRef.h index 5ee49183b9bc..33c949af0856 100644 --- a/xpcom/base/nsAutoRef.h +++ b/xpcom/base/nsAutoRef.h @@ -154,6 +154,8 @@ class nsAutoRef : public nsAutoRefBase { // like a raw reference. operator typename SimpleRef::RawRef() const { return this->get(); } + explicit operator bool() const { return this->HaveResource(); } + // Transfer ownership from another smart reference. void steal(ThisClass& aOtherRef) { BaseClass::steal(aOtherRef); } diff --git a/xpcom/glue/FileUtils.cpp b/xpcom/glue/FileUtils.cpp index 9ab37eaf4e28..e491f8b664a9 100644 --- a/xpcom/glue/FileUtils.cpp +++ b/xpcom/glue/FileUtils.cpp @@ -35,6 +35,7 @@ # include #elif defined(XP_WIN) # include +# include # include #endif @@ -368,27 +369,25 @@ void mozilla::ReadAheadLib(mozilla::pathstr_t aFilePath) { if (!fd) { return; } - LARGE_INTEGER fileSize = {}; - BOOL success = GetFileSizeEx(fd, &fileSize); - if (!success || !fileSize.QuadPart || - fileSize.QuadPart > std::numeric_limits::max()) { - return; - } nsAutoHandle mapping( CreateFileMapping(fd, nullptr, SEC_IMAGE | PAGE_READONLY, 0, 0, nullptr)); - if (!mapping) { return; } - PVOID data = - MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, (size_t)fileSize.QuadPart); - auto guard = MakeScopeExit([=]() { UnmapViewOfFile(data); }); - - if (data) { - PrefetchMemory((uint8_t*)data, (size_t)fileSize.QuadPart); + PVOID data = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0); + if (!data) { + return; } + auto guard = MakeScopeExit([=]() { UnmapViewOfFile(data); }); + mozilla::nt::PEHeaders headers(data); + Maybe> bounds = headers.GetBounds(); + if (!bounds) { + return; + } + + PrefetchMemory((uint8_t*)data, bounds->Length()); #elif defined(LINUX) && !defined(ANDROID) int fd = open(aFilePath, O_RDONLY); From 2ff0dff5c28ee52d5d0584e33e2faf4d228ebb5d Mon Sep 17 00:00:00 2001 From: Frederik Braun Date: Wed, 29 May 2019 10:06:15 +0000 Subject: [PATCH 16/36] Bug 1552477 - Disallow System Principal to load remote documents in nightly/early beta r=ckerschb Differential Revision: https://phabricator.services.mozilla.com/D31763 --HG-- extra : moz-landing-system : lando --- dom/security/nsContentSecurityManager.cpp | 6 +++--- dom/security/test/general/browser.ini | 2 +- modules/libpref/init/all.js | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dom/security/nsContentSecurityManager.cpp b/dom/security/nsContentSecurityManager.cpp index 55e3f331c433..579aeaa84ef7 100644 --- a/dom/security/nsContentSecurityManager.cpp +++ b/dom/security/nsContentSecurityManager.cpp @@ -800,7 +800,7 @@ static void DebugDoContentSecurityCheck(nsIChannel* aChannel, } } -#if defined(DEBUG) || defined(FUZZING) +#ifdef EARLY_BETA_OR_EARLIER // Assert that we never use the SystemPrincipal to load remote documents // i.e., HTTP, HTTPS, FTP URLs static void AssertSystemPrincipalMustNotLoadRemoteDocuments( @@ -860,7 +860,7 @@ static void AssertSystemPrincipalMustNotLoadRemoteDocuments( // but other mochitest are exempt from this return; } - MOZ_ASSERT(false, "SystemPrincipal must not load remote documents."); + MOZ_RELEASE_ASSERT(false, "SystemPrincipal must not load remote documents."); } #endif @@ -890,7 +890,7 @@ nsresult nsContentSecurityManager::doContentSecurityCheck( DebugDoContentSecurityCheck(aChannel, loadInfo); } -#if defined(DEBUG) || defined(FUZZING) +#ifdef EARLY_BETA_OR_EARLIER AssertSystemPrincipalMustNotLoadRemoteDocuments(aChannel); #endif diff --git a/dom/security/test/general/browser.ini b/dom/security/test/general/browser.ini index 619f5d7d99aa..05b1df20f946 100644 --- a/dom/security/test/general/browser.ini +++ b/dom/security/test/general/browser.ini @@ -17,7 +17,7 @@ support-files = support-files = file_FTP_console_warning.html [browser_test_assert_systemprincipal_documents.js] -skip-if = !debug && !fuzzing +skip-if !nightly_build support-files = file_assert_systemprincipal_documents.html file_assert_systemprincipal_documents_iframe.html diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 0a3d86cc010d..31542cb811c7 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -2681,7 +2681,7 @@ pref("security.allow_eval_with_system_principal", false); pref("security.uris_using_eval_with_system_principal", "autocomplete.xml,redux.js,react-redux.js,content-task.js,preferencesbindings.js,lodash.js,jszip.js,sinon-7.2.7.js,jsol.js"); #endif -#if defined(DEBUG) || defined(FUZZING) +#ifdef EARLY_BETA_OR_EARLIER // Disallow web documents loaded with the SystemPrincipal pref("security.disallow_non_local_systemprincipal_in_tests", false); #endif From d2dc3fe4d7f11fd1d49116fb9b8ac5b5b7d72572 Mon Sep 17 00:00:00 2001 From: Cosmin Sabou Date: Wed, 29 May 2019 14:04:23 +0300 Subject: [PATCH 17/36] Bug 1552477 - Add "=" to the skip-if syntax. r=bustage CLOSED TREE --HG-- extra : amend_source : 4a979521cb075b7a265883d23c5a6c65149937ee --- dom/security/test/general/browser.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom/security/test/general/browser.ini b/dom/security/test/general/browser.ini index 05b1df20f946..e063df2a9d6e 100644 --- a/dom/security/test/general/browser.ini +++ b/dom/security/test/general/browser.ini @@ -17,7 +17,7 @@ support-files = support-files = file_FTP_console_warning.html [browser_test_assert_systemprincipal_documents.js] -skip-if !nightly_build +skip-if = !nightly_build support-files = file_assert_systemprincipal_documents.html file_assert_systemprincipal_documents_iframe.html From 0ad6e198b09733975c169dfeb4f859ff869f7079 Mon Sep 17 00:00:00 2001 From: Mark Banner Date: Wed, 29 May 2019 10:50:37 +0000 Subject: [PATCH 18/36] Bug 1554169 - Enable some ESLint rules for more of netwerk/. r=dragana Differential Revision: https://phabricator.services.mozilla.com/D32495 --HG-- extra : moz-landing-system : lando --- .eslintignore | 4 ---- .eslintrc.js | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 4 deletions(-) diff --git a/.eslintignore b/.eslintignore index dd7b54f13ba5..8fde0188f071 100644 --- a/.eslintignore +++ b/.eslintignore @@ -15,10 +15,6 @@ obj*/** # If you are enabling a directory, please add directory specific exclusions # below. layout/** -netwerk/cookie/test/browser/** -netwerk/test/browser/** -netwerk/test/mochitests/** -netwerk/test/unit*/** # We currently have no js files in these directories, so we ignore them by # default to aid ESLint's performance. diff --git a/.eslintrc.js b/.eslintrc.js index 2da37d8ab09c..36ba19caf35c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -193,5 +193,71 @@ module.exports = { "space-before-function-paren": "off", "space-infix-ops": "off", } + }, { + "files": [ + "netwerk/cookie/test/browser/**", + "netwerk/test/browser/**", + "netwerk/test/mochitests/**", + "netwerk/test/unit*/**", + ], + "rules": { + "object-shorthand": "off", + "mozilla/consistent-if-bracing": "off", + "mozilla/reject-importGlobalProperties": "off", + "mozilla/no-arbitrary-setTimeout": "off", + "mozilla/no-define-cc-etc": "off", + "mozilla/no-useless-parameters": "off", + "mozilla/no-useless-run-test": "off", + "mozilla/use-chromeutils-generateqi": "off", + "mozilla/use-chromeutils-import": "off", + "mozilla/use-default-preference-values": "off", + "mozilla/use-services": "off", + "consistent-return": "off", + "no-array-constructor": "off", + "no-extra-boolean-cast": "off", + "no-eval": "off", + "no-else-return": "off", + "no-global-assign": "off", + "no-lonely-if": "off", + "no-nested-ternary": "off", + "no-new-wrappers": "off", + "no-redeclare": "off", + "no-return-await": "off", + "no-sequences": "off", + "no-shadow": "off", + "no-throw-literal": "off", + "no-undef": "off", + "no-unreachable": "off", + "no-unused-vars": "off", + "no-useless-return": "off", + + // Not enabling the rules below for now pending prettier roll-out. + "arrow-spacing": "off", + "block-spacing": "off", + "brace-style": "off", + "comma-dangle": "off", + "comma-spacing": "off", + "comma-style": "off", + "eol-last": "off", + "func-call-spacing": "off", + "generator-star-spacing": "off", + "key-spacing": "off", + "keyword-spacing": "off", + "no-extra-semi": "off", + "no-tabs": "off", + "no-mixed-spaces-and-tabs": "off", + "no-multi-spaces": "off", + "no-trailing-spaces": "off", + "no-whitespace-before-property": "off", + "padded-blocks": "off", + "quotes": "off", + "rest-spread-spacing": "off", + "semi": "off", + "space-before-blocks": "off", + "space-before-function-paren": "off", + "space-infix-ops": "off", + "space-unary-ops": "off", + "spaced-comment": "off", + } }] }; From c270f7d5510bc117db61855e7b6f45f2d55cc73d Mon Sep 17 00:00:00 2001 From: Mark Banner Date: Wed, 29 May 2019 10:50:49 +0000 Subject: [PATCH 19/36] Bug 1554224 - Enable some ESLint rules for more of layout/. r=dholbert Differential Revision: https://phabricator.services.mozilla.com/D32496 --HG-- extra : moz-landing-system : lando --- .eslintignore | 5 - .eslintrc.js | 78 +++++++++++ ...ansformed_scrolling_repaints_3_window.html | 1 - .../test/test_bug536567_perwindowpb.html | 8 +- layout/forms/test/test_bug903715.html | 2 +- layout/generic/test/plugin_clipping_lib.js | 6 +- layout/generic/test/test_bug1499961.html | 1 + layout/generic/test/test_bug507902.html | 10 +- layout/style/test/test_acid3_test46.html | 7 +- .../test_align_justify_computed_values.html | 129 +++++++++-------- .../test/test_animations_event_order.html | 2 + .../test/test_animations_omta_start.html | 2 +- layout/style/test/test_bug73586.html | 5 +- layout/style/test/test_bug98997.html | 10 +- layout/style/test/test_font_loading_api.html | 14 +- layout/style/test/test_inherit_storage.html | 4 +- layout/style/test/test_initial_storage.html | 4 +- layout/style/test/test_media_queries.html | 16 +-- layout/style/test/test_media_query_list.html | 12 +- layout/style/test/test_namespace_rule.html | 27 ++-- .../style/test/test_of_type_selectors.xhtml | 5 +- layout/style/test/test_parse_rule.html | 4 +- .../style/test/test_rules_out_of_sheets.html | 6 +- layout/style/test/test_selectors.html | 30 ++-- layout/style/test/test_supports_rules.html | 8 +- layout/style/test/test_transitions.html | 130 +++++++++--------- .../test/test_transitions_per_property.html | 22 +-- layout/style/test/test_value_cloning.html | 16 +-- layout/style/test/test_value_storage.html | 10 +- layout/xul/test/browser_bug706743.js | 5 +- 30 files changed, 321 insertions(+), 258 deletions(-) diff --git a/.eslintignore b/.eslintignore index 8fde0188f071..02e51ca7655a 100644 --- a/.eslintignore +++ b/.eslintignore @@ -11,11 +11,6 @@ # Exclude expected objdirs. obj*/** -# We ignore all these directories by default, until we get them enabled. -# If you are enabling a directory, please add directory specific exclusions -# below. -layout/** - # We currently have no js files in these directories, so we ignore them by # default to aid ESLint's performance. build/** diff --git a/.eslintrc.js b/.eslintrc.js index 36ba19caf35c..509e7d9b5125 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -259,5 +259,83 @@ module.exports = { "space-unary-ops": "off", "spaced-comment": "off", } + }, { + "files": [ + "layout/**", + ], + "rules": { + "object-shorthand": "off", + "mozilla/avoid-removeChild": "off", + "mozilla/consistent-if-bracing": "off", + "mozilla/reject-importGlobalProperties": "off", + "mozilla/no-arbitrary-setTimeout": "off", + "mozilla/no-define-cc-etc": "off", + "mozilla/no-useless-parameters": "off", + "mozilla/no-useless-run-test": "off", + "mozilla/use-chromeutils-generateqi": "off", + "mozilla/use-chromeutils-import": "off", + "mozilla/use-default-preference-values": "off", + "mozilla/use-includes-instead-of-indexOf": "off", + "mozilla/use-services": "off", + "mozilla/use-ownerGlobal": "off", + "complexity": "off", + "consistent-return": "off", + "dot-notation": "off", + "no-array-constructor": "off", + "no-caller": "off", + "no-cond-assign": "off", + "no-extra-boolean-cast": "off", + "no-eval": "off", + "no-else-return": "off", + "no-func-assign": "off", + "no-global-assign": "off", + "no-implied-eval": "off", + "no-lonely-if": "off", + "no-nested-ternary": "off", + "no-new-wrappers": "off", + "no-redeclare": "off", + "no-restricted-globals": "off", + "no-return-await": "off", + "no-sequences": "off", + "no-throw-literal": "off", + "no-useless-concat": "off", + "no-undef": "off", + "no-unreachable": "off", + "no-unsanitized/method": "off", + "no-unsanitized/property": "off", + "no-unsafe-negation": "off", + "no-unused-vars": "off", + "no-useless-return": "off", + + // Not enabling the rules below for now pending prettier roll-out. + "arrow-spacing": "off", + "block-spacing": "off", + "brace-style": "off", + "comma-dangle": "off", + "comma-spacing": "off", + "comma-style": "off", + "eol-last": "off", + "func-call-spacing": "off", + "generator-star-spacing": "off", + "linebreak-style": "off", + "key-spacing": "off", + "keyword-spacing": "off", + "no-extra-semi": "off", + "no-tabs": "off", + "no-mixed-spaces-and-tabs": "off", + "no-multi-spaces": "off", + "no-trailing-spaces": "off", + "no-unexpected-multiline": "off", + "no-whitespace-before-property": "off", + "padded-blocks": "off", + "quotes": "off", + "rest-spread-spacing": "off", + "semi": "off", + "space-before-blocks": "off", + "space-before-function-paren": "off", + "space-infix-ops": "off", + "space-unary-ops": "off", + "spaced-comment": "off", + } }] }; diff --git a/layout/base/tests/transformed_scrolling_repaints_3_window.html b/layout/base/tests/transformed_scrolling_repaints_3_window.html index e946a9f2b0a7..1b345a25d082 100644 --- a/layout/base/tests/transformed_scrolling_repaints_3_window.html +++ b/layout/base/tests/transformed_scrolling_repaints_3_window.html @@ -14,7 +14,6 @@ var SimpleTest = window.opener.SimpleTest; var SpecialPowers = window.opener.SpecialPowers; var is = window.opener.is; -var t, e, utils, iterations; var smoothScrollPref = "general.smoothScroll"; function startTest() { diff --git a/layout/forms/test/test_bug536567_perwindowpb.html b/layout/forms/test/test_bug536567_perwindowpb.html index c4ea329b8dd0..dc2088e20c29 100644 --- a/layout/forms/test/test_bug536567_perwindowpb.html +++ b/layout/forms/test/test_bug536567_perwindowpb.html @@ -30,12 +30,12 @@ function newDir() { var dir = tmpDir.clone(); dir.append("testdir" + Math.floor(Math.random() * 10000)); dir.QueryInterface(Ci.nsIFile); - dir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, 0700); + dir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, 0o700); return dir; } var dirs = []; -for(var i = 0; i < 6; i++) { +for(let i = 0; i < 6; i++) { dirs.push(newDir()); } dirs.push(homeDir); @@ -119,7 +119,7 @@ function runTest() { } function endTest() { - for(var i = 0; i < dirs.length - 1; i++) { + for(let i = 0; i < dirs.length - 1; i++) { dirs[i].remove(true); } @@ -168,7 +168,7 @@ function testOnWindow(aIsPrivate, aCallback) { MockFilePicker.showCallback = function(filepicker) { var test = tests[testIndex]; var returned = -1; - for (var i = 0; i < dirs.length; i++) { + for (let i = 0; i < dirs.length; i++) { var dir = MockFilePicker.displayDirectory ? MockFilePicker.displayDirectory : Services.dirsvc.get(MockFilePicker.displaySpecialDirectory, Ci.nsIFile); diff --git a/layout/forms/test/test_bug903715.html b/layout/forms/test/test_bug903715.html index 9b9db48b75cf..b887b2cd0144 100644 --- a/layout/forms/test/test_bug903715.html +++ b/layout/forms/test/test_bug903715.html @@ -59,7 +59,7 @@ function runTests() select.addEventListener("popupshowing", function (aEvent) { setTimeout(function () { synthesizeKey("KEY_ArrowDown"); - select.addEventListener("popuphiding", function (aEvent) { + select.addEventListener("popuphiding", function (aEventInner) { setTimeout(function () { // Enter key should cause closing the dropdown of the select element // and keypress event shouldn't be fired on the input element because diff --git a/layout/generic/test/plugin_clipping_lib.js b/layout/generic/test/plugin_clipping_lib.js index 287c1574eadc..ee6deedb9187 100644 --- a/layout/generic/test/plugin_clipping_lib.js +++ b/layout/generic/test/plugin_clipping_lib.js @@ -114,17 +114,17 @@ function checkClipRegionWithDoc(doc, offsetX, offsetY, id, rects, checkBounds) { "': expected " + dumpRegion(rects) + ", got " + dumpRegion(clipRects)); } -checkClipRegion = function checkClipRegion(id, rects) { +checkClipRegion = function(id, rects) { checkClipRegionWithDoc(document, 0, 0, id, rects, true); } -checkClipRegionForFrame = function checkClipRegionForFrame(fid, id, rects) { +checkClipRegionForFrame = function(fid, id, rects) { var f = document.getElementById(fid); var bounds = f.getBoundingClientRect(); checkClipRegionWithDoc(f.contentDocument, bounds.left, bounds.top, id, rects, true); } -checkClipRegionNoBounds = function checkClipRegionNoBounds(id, rects) { +checkClipRegionNoBounds = function(id, rects) { checkClipRegionWithDoc(document, 0, 0, id, rects, false); } diff --git a/layout/generic/test/test_bug1499961.html b/layout/generic/test/test_bug1499961.html index a2f4c0ba1f1c..12b217f39e1f 100644 --- a/layout/generic/test/test_bug1499961.html +++ b/layout/generic/test/test_bug1499961.html @@ -29,6 +29,7 @@ limitations under the License.

         
+  
   
 
 
@@ -60,7 +60,7 @@ var canvasNames = [ "brokenIconTest",  "brokenIconReference",
                     "loadingIconTest", "loadingIconReference",
                     "loadedTest",      "loadedReference" ];
 var windowElem = document.documentElement;
-for (var i in canvasNames) {
+for (let i in canvasNames) {
   var can = document.createElement("canvas");
   can.setAttribute("width", windowElem.getAttribute("width"));
   can.setAttribute("height", windowElem.getAttribute("height"));
@@ -81,7 +81,7 @@ for (var i in canvasNames) {
   // with the image, but not the bottom and right borders.
 
   if ((i > 1) && (i < 4)) {
-    var ctx = can.getContext("2d");
+    let ctx = can.getContext("2d");
     ctx.beginPath();
     ctx.rect(0,0, 30, 30);
     ctx.clip();
@@ -327,7 +327,7 @@ function resetImage() {
 // debugging.
 //
 function makeCanvasesVisible() {
-  for (var i = 0; i < canvasNames.length - 1; i += 2) {
+  for (let i = 0; i < canvasNames.length - 1; i += 2) {
     var title = document.createElement("h3");
     title.innerHTML = canvasNames[i] + ", " + canvasNames[i+1] + ":";
     document.body.appendChild(title);
@@ -359,7 +359,7 @@ function disableBorderAndPad() {
 
 function drawWindowToCanvas(canvasName) {
   var win = testFrameElem.contentWindow;
-  var ctx = canvases[canvasName].getContext("2d");
+  let ctx = canvases[canvasName].getContext("2d");
   // drawWindow always draws one canvas pixel for each CSS pixel in the source
   // window, so scale the drawing to show the zoom (making each canvas pixel be one
   // device pixel instead)
diff --git a/layout/style/test/test_acid3_test46.html b/layout/style/test/test_acid3_test46.html
index 8845feddf4f2..4ec50cfddcea 100644
--- a/layout/style/test/test_acid3_test46.html
+++ b/layout/style/test/test_acid3_test46.html
@@ -23,7 +23,7 @@ extracted from the test framework there and put into Mochitest.
 
 Mozilla Bug 156716
 
 
 

From 157ed604536250c64ff6ccd312d8bf1fc83e8660 Mon Sep 17 00:00:00 2001
From: Mihai Alexandru Michis 
Date: Wed, 29 May 2019 12:58:11 +0000
Subject: [PATCH 35/36] Bug 1548125 - Disable mediasource-correct-frames.html
 for frequent failures on Windows 10. r=jmaher

Differential Revision: https://phabricator.services.mozilla.com/D32994

--HG--
extra : moz-landing-system : lando
---
 .../meta/media-source/mediasource-correct-frames.html.ini        | 1 +
 1 file changed, 1 insertion(+)

diff --git a/testing/web-platform/meta/media-source/mediasource-correct-frames.html.ini b/testing/web-platform/meta/media-source/mediasource-correct-frames.html.ini
index 71bcd08018a1..58c721aa43e8 100644
--- a/testing/web-platform/meta/media-source/mediasource-correct-frames.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-correct-frames.html.ini
@@ -1,6 +1,7 @@
 [mediasource-correct-frames.html]
   disabled:
     if (os == "android") and e10s: bug 1550895 (frequently fails on geckoview)
+    if (os == "win") and (version == "10.0.15063"): https://bugzilla.mozilla.org/show_bug.cgi?id=1548125
   expected:
     if (os == "android") and not e10s: ERROR
   [Test the expected frames are played at the expected times]

From eb09749a72763685d7909c0c3c9da4e886434f13 Mon Sep 17 00:00:00 2001
From: Martin Stransky 
Date: Wed, 29 May 2019 12:50:18 +0000
Subject: [PATCH 36/36] Bug 1554525 - [Wayland] Implement
 WindowBackBufferDMABuf backend for Wayland renderer, r=jhorak

- Rename recent WindowBackBuffer class to WindowBackBufferShm to clearly state that it uses Shm memory to store the pixel buffer.
- Implement WindowBackBufferDMABuf which stores pixel data in GPU memory in WaylandDMABufSurface object.
- Use WaylandDMABufSurface as a wayland backend when DMABuf is available and gfx.wayland_dmabuf_backend.enabled is set.
- Implement WindowImageSurface which temporary stores front buffer pixel data. It's used when front buffer is used by compositor and we want to draw.
  Instead of the front/back buffer flip and read-back data from front buffer, don't flip, store the drawing and draw the pixels when compositor
  releases the front buffer.

Differential Revision: https://phabricator.services.mozilla.com/D32635

--HG--
extra : moz-landing-system : lando
---
 widget/gtk/WindowSurfaceWayland.cpp | 387 ++++++++++++++++++++++------
 widget/gtk/WindowSurfaceWayland.h   | 133 ++++++++--
 widget/gtk/moz.build                |   3 +-
 widget/gtk/mozcontainer.cpp         |   4 +-
 widget/gtk/nsWindow.cpp             |   3 -
 5 files changed, 430 insertions(+), 100 deletions(-)

diff --git a/widget/gtk/WindowSurfaceWayland.cpp b/widget/gtk/WindowSurfaceWayland.cpp
index a890bd46dafb..6067d65cd8e2 100644
--- a/widget/gtk/WindowSurfaceWayland.cpp
+++ b/widget/gtk/WindowSurfaceWayland.cpp
@@ -35,6 +35,9 @@ extern mozilla::LazyLogModule gWidgetWaylandLog;
 namespace mozilla {
 namespace widget {
 
+bool WindowSurfaceWayland::mUseDMABuf = false;
+bool WindowSurfaceWayland::mUseDMABufInitialized = false;
+
 /*
   Wayland multi-thread rendering scheme
 
@@ -59,7 +62,7 @@ namespace widget {
         |       | WindowSurfaceWayland          |<------>| nsWindow       |
         |       |                               |        ------------------
         |       |  -----------------------      |
-        |       |  | WindowBackBuffer    |      |
+        |       |  | WindowBackBufferShm |      |
         |       |  |                     |      |
         |       |  | ------------------- |      |
         |       |  | |  WaylandShmPool | |      |
@@ -67,7 +70,7 @@ namespace widget {
         |       |  -----------------------      |
         |       |                               |
         |       |  -----------------------      |
-        |       |  | WindowBackBuffer    |      |
+        |       |  | WindowBackBufferShm |      |
         |       |  |                     |      |
         |       |  | ------------------- |      |
         |       |  | |  WaylandShmPool | |      |
@@ -80,7 +83,7 @@ namespace widget {
   | WindowSurfaceWayland          |<------>| nsWindow       |
   |                               |        ------------------
   |  -----------------------      |
-  |  | WindowBackBuffer    |      |
+  |  | WindowBackBufferShm |      |
   |  |                     |      |
   |  | ------------------- |      |
   |  | |  WaylandShmPool | |      |
@@ -88,7 +91,7 @@ namespace widget {
   |  -----------------------      |
   |                               |
   |  -----------------------      |
-  |  | WindowBackBuffer    |      |
+  |  | WindowBackBufferShm |      |
   |  |                     |      |
   |  | ------------------- |      |
   |  | |  WaylandShmPool | |      |
@@ -96,6 +99,34 @@ namespace widget {
   |  -----------------------      |
   ---------------------------------
 
+----------------------------------------------------------------
+When WindowBackBufferDMABuf is used it's similar to
+WindowBackBufferShm scheme:
+
+    |
+    |
+    |
+  -----------------------------------         ------------------
+  | WindowSurfaceWayland             |<------>| nsWindow       |
+  |                                  |        ------------------
+  |  --------------------------      |
+  |  |WindowBackBufferDMABuf  |      |
+  |  |                        |      |
+  |  | ---------------------- |      |
+  |  | |WaylandDMABufSurface  |      |
+  |  | ---------------------- |      |
+  |  --------------------------      |
+  |                                  |
+  |  --------------------------      |
+  |  |WindowBackBufferDMABuf  |      |
+  |  |                        |      |
+  |  | ---------------------- |      |
+  |  | |WaylandDMABufSurface  |      |
+  |  | ---------------------- |      |
+  |  --------------------------      |
+  -----------------------------------
+
+
 nsWaylandDisplay
 
 Is our connection to Wayland display server,
@@ -120,12 +151,14 @@ wl_surface and two wl_buffer objects (owned by WindowBackBuffer)
 as we use double buffering. When nsWindow drawing is finished to wl_buffer,
 the wl_buffer is attached to wl_surface and it's sent to Wayland compositor.
 
+When there's no wl_buffer available for drawing (all wl_buffers are locked in
+compositor for instance) we store the drawing to WindowImageSurface object
+and draw later when wl_buffer becomes availabe or discard the
+WindowImageSurface cache when whole screen is invalidated.
 
 WindowBackBuffer
 
-Manages one wl_buffer. It owns wl_buffer object, owns WaylandShmPool
-(which provides shared memory) and ties them together.
-
+Is an abstraction class which provides a wl_buffer for drawing.
 Wl_buffer is a main Wayland object with actual graphics data.
 Wl_buffer basically represent one complete window screen.
 When double buffering is involved every window (GdkWindow for instance)
@@ -133,6 +166,12 @@ utilises two wl_buffers which are cycled. One is filed with data by application
 and one is rendered by compositor.
 
 
+WindowBackBufferShm
+
+It's WindowBackBuffer implementation by shared memory (shm).
+It owns wl_buffer object, owns WaylandShmPool
+(which provides the shared memory) and ties them together.
+
 WaylandShmPool
 
 WaylandShmPool acts as a manager of shared memory for WindowBackBuffer.
@@ -142,6 +181,16 @@ We allocate shared memory (shm) by mmap(..., MAP_SHARED,...) as an interface
 between us and wayland compositor. We draw our graphics data to the shm and
 handle to wayland compositor by WindowBackBuffer/WindowSurfaceWayland
 (wl_buffer/wl_surface).
+
+WindowBackBufferDMABuf
+
+It's WindowBackBuffer implementation based on DMA Buffer.
+It owns wl_buffer object, owns WaylandDMABufSurface
+(which provides the DMA Buffer) and ties them together.
+
+WindowBackBufferDMABuf backend is used only when WaylandDMABufSurface is
+available and gfx.wayland_dmabuf_backend.enabled preference is set.
+
 */
 
 #define EVENT_LOOP_DELAY (1000 / 240)
@@ -260,7 +309,7 @@ static void buffer_release(void* data, wl_buffer* buffer) {
 
 static const struct wl_buffer_listener buffer_listener = {buffer_release};
 
-void WindowBackBuffer::Create(int aWidth, int aHeight) {
+void WindowBackBufferShm::Create(int aWidth, int aHeight) {
   MOZ_ASSERT(!IsAttached(), "We can't resize attached buffers.");
 
   int newBufferSize = aWidth * aHeight * BUFFER_BPP;
@@ -270,7 +319,7 @@ void WindowBackBuffer::Create(int aWidth, int aHeight) {
       wl_shm_pool_create_buffer(mShmPool.GetShmPool(), 0, aWidth, aHeight,
                                 aWidth * BUFFER_BPP, WL_SHM_FORMAT_ARGB8888);
   wl_proxy_set_queue((struct wl_proxy*)mWaylandBuffer,
-                     mWaylandDisplay->GetEventQueue());
+                     GetWaylandDisplay()->GetEventQueue());
   wl_buffer_add_listener(mWaylandBuffer, &buffer_listener, this);
 
   mWidth = aWidth;
@@ -282,31 +331,31 @@ void WindowBackBuffer::Create(int aWidth, int aHeight) {
       mWaylandBuffer ? wl_proxy_get_id((struct wl_proxy*)mWaylandBuffer) : -1));
 }
 
-void WindowBackBuffer::Release() {
+void WindowBackBufferShm::Release() {
   LOGWAYLAND(("%s [%p]\n", __PRETTY_FUNCTION__, (void*)this));
 
   wl_buffer_destroy(mWaylandBuffer);
   mWidth = mHeight = 0;
 }
 
-void WindowBackBuffer::Clear() {
+void WindowBackBufferShm::Clear() {
   memset(mShmPool.GetImageData(), 0, mHeight * mWidth * BUFFER_BPP);
 }
 
-WindowBackBuffer::WindowBackBuffer(nsWaylandDisplay* aWaylandDisplay,
-                                   int aWidth, int aHeight)
-    : mShmPool(aWaylandDisplay, aWidth * aHeight * BUFFER_BPP),
+WindowBackBufferShm::WindowBackBufferShm(nsWaylandDisplay* aWaylandDisplay,
+                                         int aWidth, int aHeight)
+    : WindowBackBuffer(aWaylandDisplay),
+      mShmPool(aWaylandDisplay, aWidth * aHeight * BUFFER_BPP),
       mWaylandBuffer(nullptr),
       mWidth(aWidth),
       mHeight(aHeight),
-      mAttached(false),
-      mWaylandDisplay(aWaylandDisplay) {
+      mAttached(false) {
   Create(aWidth, aHeight);
 }
 
-WindowBackBuffer::~WindowBackBuffer() { Release(); }
+WindowBackBufferShm::~WindowBackBufferShm() { Release(); }
 
-bool WindowBackBuffer::Resize(int aWidth, int aHeight) {
+bool WindowBackBufferShm::Resize(int aWidth, int aHeight) {
   if (aWidth == mWidth && aHeight == mHeight) return true;
 
   LOGWAYLAND(
@@ -319,20 +368,20 @@ bool WindowBackBuffer::Resize(int aWidth, int aHeight) {
 }
 
 void WindowBackBuffer::Attach(wl_surface* aSurface) {
-  LOGWAYLAND((
-      "%s [%p] wl_surface %p ID %d wl_buffer %p ID %d\n", __PRETTY_FUNCTION__,
-      (void*)this, (void*)aSurface,
-      aSurface ? wl_proxy_get_id((struct wl_proxy*)aSurface) : -1,
-      (void*)mWaylandBuffer,
-      mWaylandBuffer ? wl_proxy_get_id((struct wl_proxy*)mWaylandBuffer) : -1));
+  LOGWAYLAND(
+      ("%s [%p] wl_surface %p ID %d wl_buffer %p ID %d\n", __PRETTY_FUNCTION__,
+       (void*)this, (void*)aSurface,
+       aSurface ? wl_proxy_get_id((struct wl_proxy*)aSurface) : -1,
+       (void*)GetWlBuffer(),
+       GetWlBuffer() ? wl_proxy_get_id((struct wl_proxy*)GetWlBuffer()) : -1));
 
-  wl_surface_attach(aSurface, mWaylandBuffer, 0, 0);
+  wl_surface_attach(aSurface, GetWlBuffer(), 0, 0);
   wl_surface_commit(aSurface);
-  wl_display_flush(mWaylandDisplay->GetDisplay());
-  mAttached = true;
+  wl_display_flush(GetWaylandDisplay()->GetDisplay());
+  SetAttached();
 }
 
-void WindowBackBuffer::Detach(wl_buffer* aBuffer) {
+void WindowBackBufferShm::Detach(wl_buffer* aBuffer) {
   LOGWAYLAND(("%s [%p] wl_buffer %p ID %d\n", __PRETTY_FUNCTION__, (void*)this,
               (void*)aBuffer,
               aBuffer ? wl_proxy_get_id((struct wl_proxy*)aBuffer) : -1));
@@ -340,19 +389,20 @@ void WindowBackBuffer::Detach(wl_buffer* aBuffer) {
   mAttached = false;
 }
 
-bool WindowBackBuffer::SetImageDataFromBuffer(
+bool WindowBackBufferShm::SetImageDataFromBuffer(
     class WindowBackBuffer* aSourceBuffer) {
-  if (!IsMatchingSize(aSourceBuffer)) {
-    Resize(aSourceBuffer->mWidth, aSourceBuffer->mHeight);
+  auto sourceBuffer = static_cast(aSourceBuffer);
+  if (!IsMatchingSize(sourceBuffer)) {
+    Resize(sourceBuffer->mWidth, sourceBuffer->mHeight);
   }
 
   mShmPool.SetImageDataFromPool(
-      &aSourceBuffer->mShmPool,
-      aSourceBuffer->mWidth * aSourceBuffer->mHeight * BUFFER_BPP);
+      &sourceBuffer->mShmPool,
+      sourceBuffer->mWidth * sourceBuffer->mHeight * BUFFER_BPP);
   return true;
 }
 
-already_AddRefed WindowBackBuffer::Lock() {
+already_AddRefed WindowBackBufferShm::Lock() {
   LOGWAYLAND((
       "%s [%p] [%d x %d] wl_buffer %p ID %d\n", __PRETTY_FUNCTION__,
       (void*)this, mWidth, mHeight, (void*)mWaylandBuffer,
@@ -361,9 +411,71 @@ already_AddRefed WindowBackBuffer::Lock() {
   gfx::IntSize lockSize(mWidth, mHeight);
   return gfxPlatform::CreateDrawTargetForData(
       static_cast(mShmPool.GetImageData()), lockSize,
-      BUFFER_BPP * mWidth, mFormat);
+      BUFFER_BPP * mWidth, GetSurfaceFormat());
 }
 
+#ifdef HAVE_LIBDRM
+WindowBackBufferDMABuf::WindowBackBufferDMABuf(
+    nsWaylandDisplay* aWaylandDisplay, int aWidth, int aHeight)
+    : WindowBackBuffer(aWaylandDisplay) {
+  mDMAbufSurface.Create(aWidth, aHeight);
+}
+
+WindowBackBufferDMABuf::~WindowBackBufferDMABuf() { mDMAbufSurface.Release(); }
+
+already_AddRefed WindowBackBufferDMABuf::Lock() {
+  LOGWAYLAND(
+      ("%s [%p] [%d x %d] wl_buffer %p ID %d\n", __PRETTY_FUNCTION__,
+       (void*)this, GetWidth(), GetHeight(), (void*)GetWlBuffer(),
+       GetWlBuffer() ? wl_proxy_get_id((struct wl_proxy*)GetWlBuffer()) : -1));
+
+  uint32_t stride;
+  void* pixels = mDMAbufSurface.Map(&stride);
+  gfx::IntSize lockSize(GetWidth(), GetHeight());
+  return gfxPlatform::CreateDrawTargetForData(
+      static_cast(pixels), lockSize, stride,
+      GetSurfaceFormat());
+}
+
+void WindowBackBufferDMABuf::Unlock() { mDMAbufSurface.Unmap(); }
+
+bool WindowBackBufferDMABuf::IsAttached() {
+  return mDMAbufSurface.WLBufferIsAttached();
+}
+
+void WindowBackBufferDMABuf::SetAttached() {
+  return mDMAbufSurface.WLBufferSetAttached();
+}
+
+int WindowBackBufferDMABuf::GetWidth() { return mDMAbufSurface.GetWidth(); }
+
+int WindowBackBufferDMABuf::GetHeight() { return mDMAbufSurface.GetHeight(); }
+
+wl_buffer* WindowBackBufferDMABuf::GetWlBuffer() {
+  return mDMAbufSurface.GetWLBuffer();
+}
+
+bool WindowBackBufferDMABuf::IsLocked() { return mDMAbufSurface.IsMapped(); }
+
+bool WindowBackBufferDMABuf::Resize(int aWidth, int aHeight) {
+  return mDMAbufSurface.Resize(aWidth, aHeight);
+}
+
+bool WindowBackBufferDMABuf::SetImageDataFromBuffer(
+    class WindowBackBuffer* aSourceBuffer) {
+  WindowBackBufferDMABuf* source =
+      static_cast(aSourceBuffer);
+  mDMAbufSurface.CopyFrom(&source->mDMAbufSurface);
+  return true;
+}
+
+void WindowBackBufferDMABuf::Detach(wl_buffer* aBuffer) {
+  mDMAbufSurface.WLBufferDetach();
+}
+
+void WindowBackBufferDMABuf::Clear() { mDMAbufSurface.Clear(); }
+#endif
+
 static void frame_callback_handler(void* data, struct wl_callback* callback,
                                    uint32_t time) {
   auto surface = reinterpret_cast(data);
@@ -417,13 +529,53 @@ WindowSurfaceWayland::~WindowSurfaceWayland() {
   }
 }
 
-WindowBackBuffer* WindowSurfaceWayland::GetWaylandBufferToDraw(int aWidth,
-                                                               int aHeight) {
+bool WindowSurfaceWayland::UseDMABufBackend() {
+  if (!mUseDMABufInitialized) {
+#ifdef HAVE_LIBDRM
+    if (WaylandDMABufSurface::IsAvailable()) {
+      mUseDMABuf =
+          Preferences::GetBool("gfx.wayland_dmabuf_backend.enabled", false);
+    }
+#endif
+    mUseDMABufInitialized = true;
+  }
+  return mUseDMABuf;
+}
+
+WindowBackBuffer* WindowSurfaceWayland::CreateWaylandBuffer(int aWidth,
+                                                            int aHeight) {
+#ifdef HAVE_LIBDRM
+  if (UseDMABufBackend()) {
+    static bool sDMABufBufferCreated = false;
+    WindowBackBuffer* buffer =
+        new WindowBackBufferDMABuf(mWaylandDisplay, aWidth, aHeight);
+    if (buffer) {
+      sDMABufBufferCreated = true;
+      return buffer;
+    }
+    // If this is the first failure and there's no dmabuf already active
+    // we can safely fallback to Shm. Otherwise we can't mix DMAbuf and
+    // SHM buffers so just fails now.
+    if (sDMABufBufferCreated) {
+      NS_WARNING("Failed to allocate DMABuf buffer!");
+      return nullptr;
+    } else {
+      NS_WARNING("Wayland DMABuf failed, switched back to Shm backend!");
+      mUseDMABuf = false;
+    }
+  }
+#endif
+
+  return new WindowBackBufferShm(mWaylandDisplay, aWidth, aHeight);
+}
+
+WindowBackBuffer* WindowSurfaceWayland::GetWaylandBufferToDraw(
+    int aWidth, int aHeight, bool aFullScreenUpdate, bool aNoBackBufferCopy) {
   if (!mWaylandBuffer) {
     LOGWAYLAND(("%s [%p] Create [%d x %d]\n", __PRETTY_FUNCTION__, (void*)this,
                 aWidth, aHeight));
 
-    mWaylandBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight);
+    mWaylandBuffer = CreateWaylandBuffer(aWidth, aHeight);
     mWaitToFullScreenUpdate = true;
     return mWaylandBuffer;
   }
@@ -449,8 +601,7 @@ WindowBackBuffer* WindowSurfaceWayland::GetWaylandBufferToDraw(int aWidth,
   for (availableBuffer = 0; availableBuffer < BACK_BUFFER_NUM;
        availableBuffer++) {
     if (!mBackupBuffer[availableBuffer]) {
-      mBackupBuffer[availableBuffer] =
-          new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight);
+      mBackupBuffer[availableBuffer] = CreateWaylandBuffer(aWidth, aHeight);
       break;
     }
 
@@ -466,17 +617,26 @@ WindowBackBuffer* WindowSurfaceWayland::GetWaylandBufferToDraw(int aWidth,
     return nullptr;
   }
 
+  bool bufferFlip = mWaylandBuffer->IsMatchingSize(aWidth, aHeight);
+  if (bufferFlip && aNoBackBufferCopy && !aFullScreenUpdate) {
+    LOGWAYLAND(("%s [%p] Delayed hard copy from old buffer [%d x %d]\n",
+                __PRETTY_FUNCTION__, (void*)this, aWidth, aHeight));
+    return nullptr;
+  }
+
   WindowBackBuffer* lastWaylandBuffer = mWaylandBuffer;
   mWaylandBuffer = mBackupBuffer[availableBuffer];
   mBackupBuffer[availableBuffer] = lastWaylandBuffer;
 
-  if (lastWaylandBuffer->IsMatchingSize(aWidth, aHeight)) {
-    LOGWAYLAND(("%s [%p] Copy from old buffer [%d x %d]\n", __PRETTY_FUNCTION__,
-                (void*)this, aWidth, aHeight));
+  if (bufferFlip) {
     // Former front buffer has the same size as a requested one.
     // Gecko may expect a content already drawn on screen so copy
-    // existing data to the new buffer.
-    mWaylandBuffer->SetImageDataFromBuffer(lastWaylandBuffer);
+    // existing data to the new buffer if we don't do fullscreen redraw.
+    if (!aFullScreenUpdate) {
+      LOGWAYLAND(("%s [%p] Copy from old buffer [%d x %d]\n",
+                  __PRETTY_FUNCTION__, (void*)this, aWidth, aHeight));
+      mWaylandBuffer->SetImageDataFromBuffer(lastWaylandBuffer);
+    }
     // When buffer switches we need to damage whole screen
     // (https://bugzilla.redhat.com/show_bug.cgi?id=1418260)
     mWaylandBufferFullScreenDamage = true;
@@ -493,11 +653,15 @@ WindowBackBuffer* WindowSurfaceWayland::GetWaylandBufferToDraw(int aWidth,
 }
 
 already_AddRefed WindowSurfaceWayland::LockWaylandBuffer(
-    int aWidth, int aHeight, bool aClearBuffer) {
-  WindowBackBuffer* buffer = GetWaylandBufferToDraw(aWidth, aHeight);
+    int aWidth, int aHeight, bool aClearBuffer, bool aFullScreenUpdate,
+    bool aNoBackBufferCopy) {
+  WindowBackBuffer* buffer = GetWaylandBufferToDraw(
+      aWidth, aHeight, aFullScreenUpdate, aNoBackBufferCopy);
   if (!buffer) {
-    NS_WARNING(
-        "WindowSurfaceWayland::LockWaylandBuffer(): No buffer available");
+    if (!aNoBackBufferCopy) {
+      NS_WARNING(
+          "WindowSurfaceWayland::LockWaylandBuffer(): No buffer available");
+    }
     return nullptr;
   }
 
@@ -508,6 +672,8 @@ already_AddRefed WindowSurfaceWayland::LockWaylandBuffer(
   return buffer->Lock();
 }
 
+void WindowSurfaceWayland::UnlockWaylandBuffer() { mWaylandBuffer->Unlock(); }
+
 already_AddRefed WindowSurfaceWayland::LockImageSurface(
     const gfx::IntSize& aLockSize) {
   if (!mImageSurface || mImageSurface->CairoStatus() ||
@@ -557,9 +723,14 @@ already_AddRefed WindowSurfaceWayland::Lock(
        lockSize.height == screenRect.height);
 
   if (mDrawToWaylandBufferDirectly) {
-    RefPtr dt =
-        LockWaylandBuffer(screenRect.width, screenRect.height,
-                          mWindow->WaylandSurfaceNeedsClear());
+    // If there's any pending image commit scratch them as we're going
+    // to redraw the whole sceen anyway.
+    mDelayedImageCommits.Clear();
+
+    RefPtr dt = LockWaylandBuffer(
+        screenRect.width, screenRect.height,
+        mWindow->WaylandSurfaceNeedsClear(),
+        /* aFullScreenUpdate */ true, /* aNoBackBufferCopy */ true);
     if (dt) {
       // When we have a request to update whole screen at once
       // (surface was created, resized or changed somehow)
@@ -579,8 +750,49 @@ already_AddRefed WindowSurfaceWayland::Lock(
   return LockImageSurface(lockSize);
 }
 
+void WindowImageSurface::Draw(gfx::SourceSurface* aSurface,
+                              gfx::DrawTarget* aDest,
+                              const LayoutDeviceIntRegion& aRegion) {
+  uint32_t numRects = aRegion.GetNumRects();
+  if (numRects != 1) {
+    AutoTArray rects;
+    rects.SetCapacity(numRects);
+    for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
+      rects.AppendElement(iter.Get().ToUnknownRect());
+    }
+    aDest->PushDeviceSpaceClipRects(rects.Elements(), rects.Length());
+  }
+
+  gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect();
+  gfx::Rect rect(bounds);
+  aDest->DrawSurface(aSurface, rect, rect);
+
+  if (numRects != 1) {
+    aDest->PopClip();
+  }
+}
+
+void WindowImageSurface::Draw(gfx::DrawTarget* aDest,
+                              LayoutDeviceIntRegion& aWaylandBufferDamage) {
+  Draw(mSurface.get(), aDest, mUpdateRegion);
+  aWaylandBufferDamage.OrWith(mUpdateRegion);
+}
+
+WindowImageSurface::WindowImageSurface(
+    gfx::SourceSurface* aSurface, const LayoutDeviceIntRegion& aUpdateRegion)
+    : mSurface(aSurface), mUpdateRegion(aUpdateRegion){};
+
+void WindowSurfaceWayland::DrawDelayedImageCommits(
+    gfx::DrawTarget* aDrawTarget, LayoutDeviceIntRegion& aWaylandBufferDamage) {
+  for (unsigned int i = 0; i < mDelayedImageCommits.Length(); i++) {
+    mDelayedImageCommits[i].Draw(aDrawTarget, aWaylandBufferDamage);
+  }
+  mDelayedImageCommits.Clear();
+}
+
 bool WindowSurfaceWayland::CommitImageSurfaceToWaylandBuffer(
-    const LayoutDeviceIntRegion& aRegion) {
+    const LayoutDeviceIntRegion& aRegion,
+    LayoutDeviceIntRegion& aWaylandBufferDamage) {
   MOZ_ASSERT(!mDrawToWaylandBufferDirectly);
 
   LayoutDeviceIntRect screenRect = mWindow->GetBounds();
@@ -591,30 +803,27 @@ bool WindowSurfaceWayland::CommitImageSurfaceToWaylandBuffer(
     return false;
   }
 
-  RefPtr dt = LockWaylandBuffer(
-      screenRect.width, screenRect.height, mWindow->WaylandSurfaceNeedsClear());
   RefPtr surf =
       gfx::Factory::CreateSourceSurfaceForCairoSurface(
           mImageSurface->CairoSurface(), mImageSurface->GetSize(),
           mImageSurface->Format());
-  if (!dt || !surf) {
+  if (!surf) {
     return false;
   }
 
-  uint32_t numRects = aRegion.GetNumRects();
-  if (numRects != 1) {
-    AutoTArray rects;
-    rects.SetCapacity(numRects);
-    for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
-      rects.AppendElement(iter.Get().ToUnknownRect());
-    }
-    dt->PushDeviceSpaceClipRects(rects.Elements(), rects.Length());
-  }
-
-  dt->DrawSurface(surf, rect, rect);
-
-  if (numRects != 1) {
-    dt->PopClip();
+  RefPtr dt = LockWaylandBuffer(
+      screenRect.width, screenRect.height, /* needs clear*/ false,
+      /* fullscreenDrawing */ false, /* aNoBackBufferCopy */ true);
+  if (dt) {
+    // Draw any delayed image commits first
+    DrawDelayedImageCommits(dt, aWaylandBufferDamage);
+    WindowImageSurface::Draw(surf, dt, aRegion);
+    // Submit all drawing to final Wayland buffer upload
+    aWaylandBufferDamage.OrWith(aRegion);
+    UnlockWaylandBuffer();
+  } else {
+    mDelayedImageCommits.AppendElement(WindowImageSurface(surf, aRegion));
+    return false;
   }
 
   return true;
@@ -648,6 +857,21 @@ void WindowSurfaceWayland::CommitWaylandBuffer() {
     return;
   }
 
+  if (!mDrawToWaylandBufferDirectly) {
+    // There's some cached drawings - try to flush them now.
+    LayoutDeviceIntRect screenRect = mWindow->GetBounds();
+    RefPtr dt =
+        LockWaylandBuffer(screenRect.width, screenRect.height,
+                          /* needsClear */ false,
+                          /* fullscreenInvalidate */ false,
+                          /* aNoBackBufferCopy */ true);
+    if (dt) {
+      DrawDelayedImageCommits(dt, mWaylandBufferDamage);
+      UnlockWaylandBuffer();
+      mDrawToWaylandBufferDirectly = true;
+    }
+  }
+
   wl_surface* waylandSurface = mWindow->GetWaylandSurface();
   if (!waylandSurface) {
     // Target window is not created yet - delay the commit. This can happen only
@@ -740,14 +964,21 @@ void WindowSurfaceWayland::Commit(const LayoutDeviceIntRegion& aInvalidRegion) {
   }
 #endif
 
-  // We have new content at mImageSurface - copy data to mWaylandBuffer first.
-  if (!mDrawToWaylandBufferDirectly) {
-    CommitImageSurfaceToWaylandBuffer(aInvalidRegion);
-  }
-
-  // If we're not at fullscreen damage add drawing area from aInvalidRegion
-  if (!mWaylandBufferFullScreenDamage) {
-    mWaylandBufferDamage.OrWith(aInvalidRegion);
+  if (mDrawToWaylandBufferDirectly) {
+    MOZ_ASSERT(mWaylandBuffer->IsLocked());
+    // If we're not at fullscreen damage add drawing area from aInvalidRegion
+    if (!mWaylandBufferFullScreenDamage) {
+      mWaylandBufferDamage.OrWith(aInvalidRegion);
+    }
+    UnlockWaylandBuffer();
+  } else {
+    MOZ_ASSERT(!mWaylandBuffer->IsLocked(),
+               "Drawing to already locked buffer?");
+    if (CommitImageSurfaceToWaylandBuffer(aInvalidRegion,
+                                          mWaylandBufferDamage)) {
+      // Our cached drawing is flushed, we can draw fullscreen again.
+      mDrawToWaylandBufferDirectly = true;
+    }
   }
 
   // We're ready to commit.
diff --git a/widget/gtk/WindowSurfaceWayland.h b/widget/gtk/WindowSurfaceWayland.h
index d5ab3eee3b79..e565dc0c1858 100644
--- a/widget/gtk/WindowSurfaceWayland.h
+++ b/widget/gtk/WindowSurfaceWayland.h
@@ -10,6 +10,9 @@
 #include 
 #include "mozilla/gfx/Types.h"
 #include "nsWaylandDisplay.h"
+#ifdef HAVE_LIBDRM
+#  include "mozilla/gfx/WaylandDMABufSurface.h"
+#endif
 
 #define BACK_BUFFER_NUM 2
 
@@ -40,12 +43,53 @@ class WaylandShmPool {
 // Holds actual graphics data for wl_surface
 class WindowBackBuffer {
  public:
-  WindowBackBuffer(nsWaylandDisplay* aDisplay, int aWidth, int aHeight);
-  ~WindowBackBuffer();
+  virtual already_AddRefed Lock() = 0;
+  virtual void Unlock(){};
+  virtual bool IsLocked() { return false; };
+
+  void Attach(wl_surface* aSurface);
+  virtual void Detach(wl_buffer* aBuffer) = 0;
+  virtual bool IsAttached() = 0;
+
+  virtual void Clear() = 0;
+  virtual bool Resize(int aWidth, int aHeight) = 0;
+
+  virtual int GetWidth() = 0;
+  virtual int GetHeight() = 0;
+  virtual wl_buffer* GetWlBuffer() = 0;
+  virtual void SetAttached() = 0;
+
+  virtual bool SetImageDataFromBuffer(
+      class WindowBackBuffer* aSourceBuffer) = 0;
+
+  bool IsMatchingSize(int aWidth, int aHeight) {
+    return aWidth == GetWidth() && aHeight == GetHeight();
+  }
+  bool IsMatchingSize(class WindowBackBuffer* aBuffer) {
+    return aBuffer->IsMatchingSize(GetWidth(), GetHeight());
+  }
+
+  static gfx::SurfaceFormat GetSurfaceFormat() { return mFormat; }
+
+  nsWaylandDisplay* GetWaylandDisplay() { return mWaylandDisplay; };
+
+  WindowBackBuffer(nsWaylandDisplay* aWaylandDisplay)
+      : mWaylandDisplay(aWaylandDisplay){};
+  virtual ~WindowBackBuffer(){};
+
+ private:
+  static gfx::SurfaceFormat mFormat;
+  nsWaylandDisplay* mWaylandDisplay;
+};
+
+class WindowBackBufferShm : public WindowBackBuffer {
+ public:
+  WindowBackBufferShm(nsWaylandDisplay* aWaylandDisplay, int aWidth,
+                      int aHeight);
+  ~WindowBackBufferShm();
 
   already_AddRefed Lock();
 
-  void Attach(wl_surface* aSurface);
   void Detach(wl_buffer* aBuffer);
   bool IsAttached() { return mAttached; }
 
@@ -53,14 +97,11 @@ class WindowBackBuffer {
   bool Resize(int aWidth, int aHeight);
   bool SetImageDataFromBuffer(class WindowBackBuffer* aSourceBuffer);
 
-  bool IsMatchingSize(int aWidth, int aHeight) {
-    return aWidth == mWidth && aHeight == mHeight;
-  }
-  bool IsMatchingSize(class WindowBackBuffer* aBuffer) {
-    return aBuffer->mWidth == mWidth && aBuffer->mHeight == mHeight;
-  }
+  int GetWidth() { return mWidth; };
+  int GetHeight() { return mHeight; };
 
-  static gfx::SurfaceFormat GetSurfaceFormat() { return mFormat; }
+  wl_buffer* GetWlBuffer() { return mWaylandBuffer; };
+  void SetAttached() { mAttached = true; };
 
  private:
   void Create(int aWidth, int aHeight);
@@ -75,8 +116,51 @@ class WindowBackBuffer {
   int mWidth;
   int mHeight;
   bool mAttached;
-  nsWaylandDisplay* mWaylandDisplay;
-  static gfx::SurfaceFormat mFormat;
+};
+
+#ifdef HAVE_LIBDRM
+class WindowBackBufferDMABuf : public WindowBackBuffer {
+ public:
+  WindowBackBufferDMABuf(nsWaylandDisplay* aWaylandDisplay, int aWidth,
+                         int aHeight);
+  ~WindowBackBufferDMABuf();
+
+  bool IsAttached();
+  void SetAttached();
+
+  int GetWidth();
+  int GetHeight();
+  wl_buffer* GetWlBuffer();
+
+  bool SetImageDataFromBuffer(class WindowBackBuffer* aSourceBuffer);
+
+  already_AddRefed Lock();
+  bool IsLocked();
+  void Unlock();
+
+  void Clear();
+  void Detach(wl_buffer* aBuffer);
+  bool Resize(int aWidth, int aHeight);
+
+ private:
+  WaylandDMABufSurface mDMAbufSurface;
+};
+#endif
+
+class WindowImageSurface {
+ public:
+  static void Draw(gfx::SourceSurface* aSurface, gfx::DrawTarget* aDest,
+                   const LayoutDeviceIntRegion& aRegion);
+
+  void Draw(gfx::DrawTarget* aDest,
+            LayoutDeviceIntRegion& aWaylandBufferDamage);
+
+  WindowImageSurface(gfx::SourceSurface* aSurface,
+                     const LayoutDeviceIntRegion& aUpdateRegion);
+
+ private:
+  RefPtr mSurface;
+  const LayoutDeviceIntRegion mUpdateRegion;
 };
 
 // WindowSurfaceWayland is an abstraction for wl_surface
@@ -93,33 +177,50 @@ class WindowSurfaceWayland : public WindowSurface {
   void DelayedCommitHandler();
 
  private:
-  WindowBackBuffer* GetWaylandBufferToDraw(int aWidth, int aHeight);
+  WindowBackBuffer* CreateWaylandBuffer(int aWidth, int aHeight);
+  WindowBackBuffer* GetWaylandBufferToDraw(int aWidth, int aHeight,
+                                           bool aFullScreenUpdate,
+                                           bool aNoBackBufferCopy);
 
   already_AddRefed LockWaylandBuffer(int aWidth, int aHeight,
-                                                      bool aClearBuffer);
+                                                      bool aClearBuffer,
+                                                      bool aFullScreenUpdate,
+                                                      bool aNoBackBufferCopy);
+  void UnlockWaylandBuffer();
+
   already_AddRefed LockImageSurface(
       const gfx::IntSize& aLockSize);
-  bool CommitImageSurfaceToWaylandBuffer(const LayoutDeviceIntRegion& aRegion);
+  bool CommitImageSurfaceToWaylandBuffer(
+      const LayoutDeviceIntRegion& aRegion,
+      LayoutDeviceIntRegion& aWaylandBufferDamage);
   void CommitWaylandBuffer();
   void CalcRectScale(LayoutDeviceIntRect& aRect, int scale);
 
+  void DrawDelayedImageCommits(gfx::DrawTarget* aDrawTarget,
+                               LayoutDeviceIntRegion& aWaylandBufferDamage);
+
   // TODO: Do we need to hold a reference to nsWindow object?
   nsWindow* mWindow;
   nsWaylandDisplay* mWaylandDisplay;
   WindowBackBuffer* mWaylandBuffer;
   LayoutDeviceIntRegion mWaylandBufferDamage;
   WindowBackBuffer* mBackupBuffer[BACK_BUFFER_NUM];
-  RefPtr mImageSurface;
   wl_callback* mFrameCallback;
   wl_surface* mLastCommittedSurface;
   MessageLoop* mDisplayThreadMessageLoop;
   WindowSurfaceWayland** mDelayedCommitHandle;
+  RefPtr mImageSurface;
+  AutoTArray mDelayedImageCommits;
   bool mDrawToWaylandBufferDirectly;
   bool mPendingCommit;
   bool mWaylandBufferFullScreenDamage;
   bool mIsMainThread;
   bool mNeedScaleFactorUpdate;
   bool mWaitToFullScreenUpdate;
+
+  static bool UseDMABufBackend();
+  static bool mUseDMABufInitialized;
+  static bool mUseDMABuf;
 };
 
 }  // namespace widget
diff --git a/widget/gtk/moz.build b/widget/gtk/moz.build
index 210a8a5fe78b..6616cc7a541f 100644
--- a/widget/gtk/moz.build
+++ b/widget/gtk/moz.build
@@ -102,7 +102,8 @@ if CONFIG['MOZ_WAYLAND']:
         'WindowSurfaceWayland.cpp',
     ]
     EXPORTS.mozilla.widget += [
-        'nsWaylandDisplayShutdown.h'
+        'nsWaylandDisplay.h',
+        'nsWaylandDisplayShutdown.h',
     ]
 
 if CONFIG['ACCESSIBILITY']:
diff --git a/widget/gtk/mozcontainer.cpp b/widget/gtk/mozcontainer.cpp
index 7bc7651699e3..2a6f071933ba 100644
--- a/widget/gtk/mozcontainer.cpp
+++ b/widget/gtk/mozcontainer.cpp
@@ -646,9 +646,9 @@ gboolean moz_container_has_wl_egl_window(MozContainer* container) {
 }
 
 gboolean moz_container_surface_needs_clear(MozContainer* container) {
-  gboolean state = container->surface_needs_clear;
+  int ret = container->surface_needs_clear;
   container->surface_needs_clear = false;
-  return state;
+  return ret;
 }
 #endif
 
diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp
index c98919a9b290..795a922eb004 100644
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -6866,9 +6866,6 @@ bool nsWindow::WaylandSurfaceNeedsClear() {
   if (mContainer) {
     return moz_container_surface_needs_clear(MOZ_CONTAINER(mContainer));
   }
-
-  NS_WARNING(
-      "nsWindow::WaylandSurfaceNeedsClear(): We don't have any mContainer!");
   return false;
 }
 #endif