Bug 1294194 - Don't prompt to re-save a filled login when used on a different origin. r=MattN
☠☠ backed out by d9bc0e494057 ☠ ☠
authorJared Wein <jwein@mozilla.com>
Wed, 06 Mar 2019 03:58:09 +0000
changeset 520423 bdf395eb0c640b4deea7a122ffdd5d1b5bc20c11
parent 520422 96c2336e72c01dfb89146026708b89060003a19d
child 520424 09dd6c2c111962afd8ef36a50184b4b3fa740c7e
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMattN
bugs1294194
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1294194 - Don't prompt to re-save a filled login when used on a different origin. r=MattN Differential Revision: https://phabricator.services.mozilla.com/D22143
toolkit/components/passwordmgr/LoginManagerContent.jsm
toolkit/components/passwordmgr/LoginManagerParent.jsm
toolkit/components/passwordmgr/test/browser/browser.ini
toolkit/components/passwordmgr/test/browser/browser_autofill_track_filled_logins.js
--- a/toolkit/components/passwordmgr/LoginManagerContent.jsm
+++ b/toolkit/components/passwordmgr/LoginManagerContent.jsm
@@ -1044,19 +1044,21 @@ var LoginManagerContent = {
                             null;
 
     // Make sure to pass the opener's top ID in case it was in a frame.
     let openerTopWindowID = null;
     if (win.opener) {
       openerTopWindowID = win.opener.top.windowUtils.outerWindowID;
     }
 
+    let autoFilledLogin = this.stateForDocument(doc).fillsByRootElement.get(form.rootElement);
     messageManager.sendAsyncMessage("PasswordManager:onFormSubmit",
                                     { hostname,
                                       formSubmitURL,
+                                      autoFilledLoginGuid: autoFilledLogin && autoFilledLogin.guid,
                                       usernameField: mockUsername,
                                       newPasswordField: mockPassword,
                                       oldPasswordField: mockOldPassword,
                                       openerTopWindowID,
                                     });
   },
 
   /** Remove login field highlight when its value is cleared or overwritten.
--- a/toolkit/components/passwordmgr/LoginManagerParent.jsm
+++ b/toolkit/components/passwordmgr/LoginManagerParent.jsm
@@ -84,23 +84,24 @@ var LoginManagerParent = {
 
       case "PasswordManager:findRecipes": {
         let formHost = (new URL(data.formOrigin)).host;
         return this._recipeManager.getRecipesForHost(formHost);
       }
 
       case "PasswordManager:onFormSubmit": {
         // TODO Verify msg.target's principals against the formOrigin?
-        this.onFormSubmit(data.hostname,
-                          data.formSubmitURL,
-                          data.usernameField,
-                          data.newPasswordField,
-                          data.oldPasswordField,
-                          data.openerTopWindowID,
-                          msg.target);
+        this.onFormSubmit({hostname: data.hostname,
+                           formSubmitURL: data.formSubmitURL,
+                           autoFilledLoginGuid: data.autoFilledLoginGuid,
+                           usernameField: data.usernameField,
+                           newPasswordField: data.newPasswordField,
+                           oldPasswordField: data.oldPasswordField,
+                           openerTopWindowID: data.openerTopWindowID,
+                           target: msg.target});
         break;
       }
 
       case "PasswordManager:insecureLoginFormPresent": {
         this.setHasInsecureLoginForms(msg.target, data.hasInsecureLoginForms);
         break;
       }
 
@@ -283,20 +284,20 @@ var LoginManagerParent = {
     // doesn't support structured cloning.
     var jsLogins = LoginHelper.loginsToVanillaObjects(matchingLogins);
     target.messageManager.sendAsyncMessage("PasswordManager:loginsAutoCompleted", {
       requestId,
       logins: jsLogins,
     });
   },
 
-  onFormSubmit(hostname, formSubmitURL,
-               usernameField, newPasswordField,
-               oldPasswordField, openerTopWindowID,
-               target) {
+  onFormSubmit({hostname, formSubmitURL, autoFilledLoginGuid,
+                usernameField, newPasswordField,
+                oldPasswordField, openerTopWindowID,
+                target}) {
     function getPrompter() {
       var prompterSvc = Cc["@mozilla.org/login-manager/prompter;1"].
                         createInstance(Ci.nsILoginManagerPrompter);
       prompterSvc.init(target.ownerGlobal);
       prompterSvc.browser = target;
 
       for (let win of Services.wm.getEnumerator(null)) {
         if (!win.gBrowser && !win.getBrowser) {
@@ -337,16 +338,29 @@ var LoginManagerParent = {
     var formLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].
                     createInstance(Ci.nsILoginInfo);
     formLogin.init(hostname, formSubmitURL, null,
                    (usernameField ? usernameField.value : ""),
                    newPasswordField.value,
                    (usernameField ? usernameField.name : ""),
                    newPasswordField.name);
 
+    if (autoFilledLoginGuid) {
+      let loginsForGuid = LoginHelper.searchLoginsWithObject({
+        guid: autoFilledLoginGuid,
+      });
+      if (loginsForGuid.length == 1 &&
+          loginsForGuid[0].password == formLogin.password &&
+          loginsForGuid[0].username == formLogin.username) {
+        log("The filled login matches the form submission. Nothing to change.");
+        recordLoginUse(loginsForGuid[0]);
+        return;
+      }
+    }
+
     // Below here we have one login per hostPort + action + username with the
     // matching scheme being preferred.
     let logins = this._searchAndDedupeLogins(hostname, formSubmitURL);
 
     // If we didn't find a username field, but seem to be changing a
     // password, allow the user to select from a list of applicable
     // logins to update the password for.
     if (!usernameField && oldPasswordField && logins.length > 0) {
--- a/toolkit/components/passwordmgr/test/browser/browser.ini
+++ b/toolkit/components/passwordmgr/test/browser/browser.ini
@@ -12,16 +12,17 @@ support-files =
   insecure_test.html
   insecure_test_subframe.html
   multiple_forms.html
   streamConverter_content.sjs
 
 [browser_autocomplete_footer.js]
 [browser_autocomplete_insecure_warning.js]
 skip-if = os == "linux" || os == "mac"  # Bug 1425879
+[browser_autofill_track_filled_logins.js]
 [browser_basicAuth_rateLimit.js]
 [browser_capture_doorhanger.js]
 skip-if = os == "linux" && debug # Bug 1334336
 support-files =
   subtst_notifications_1.html
   subtst_notifications_2.html
   subtst_notifications_2pw_0un.html
   subtst_notifications_2pw_1un_1text.html
new file mode 100644
--- /dev/null
+++ b/toolkit/components/passwordmgr/test/browser/browser_autofill_track_filled_logins.js
@@ -0,0 +1,58 @@
+"use strict";
+
+const TEST_HOSTNAME = "https://example.com";
+const BASIC_FORM_PAGE_PATH = DIRECTORY_PATH + "form_basic.html";
+
+function getSubmitMessage() {
+  info("getSubmitMessage");
+  return new Promise((resolve, reject) => {
+    Services.mm.addMessageListener("RemoteLogins:onFormSubmit", function onFormSubmit() {
+      Services.mm.removeMessageListener("RemoteLogins:onFormSubmit", onFormSubmit);
+      resolve();
+    });
+  });
+}
+
+add_task(async function test() {
+  let nsLoginInfo = new Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
+                                               Ci.nsILoginInfo, "init");
+  let login = new nsLoginInfo("https://example.com", "https://example.com", null,
+                              "bob", "mypassword", "form-basic-username", "form-basic-password");
+  login = Services.logins.addLogin(login);
+  is(login.timesUsed, 1, "The timesUsed should be 1 after creation");
+
+  let url = TEST_HOSTNAME + BASIC_FORM_PAGE_PATH;
+  let tab = await BrowserTestUtils.openNewForegroundTab({
+    gBrowser,
+    url,
+  });
+
+  ContentTask.spawn(tab.linkedBrowser, login, (addedLogin) => {
+    const { LoginManagerContent, LoginFormFactory } = ChromeUtils.import("resource://gre/modules/LoginManagerContent.jsm");
+
+    let inputElement = content.document.querySelector("input");
+    let formLike = LoginFormFactory.createFromField(inputElement);
+    info("Calling _fillForm with FormLike");
+    LoginManagerContent._fillForm(formLike, [addedLogin], null, {
+      inputElement,
+      autofillForm: true,
+      clobberUsername: true,
+      clobberPassword: true,
+      userTriggered: true,
+    });
+  });
+
+  let processedPromise = getSubmitMessage();
+  ContentTask.spawn(tab.linkedBrowser, null, () => {
+    content.document.getElementById("form-basic").submit();
+  });
+  await processedPromise;
+
+  let logins = Services.logins.getAllLogins();
+
+  is(logins.length, 1, "There should only be one login saved");
+  is(logins[0].guid, login.guid, "The saved login should match the one added and used above");
+  checkOnlyLoginWasUsedTwice({ justChanged: false });
+
+  BrowserTestUtils.removeTab(tab);
+});