Bug 1330111 - Automatically open login autocomplete from username fields if the form isn't filled. r=johannh
☠☠ backed out by 5776c72e7359 ☠ ☠
authorMatthew Noorenberghe <mozilla@noorenberghe.ca>
Fri, 03 Feb 2017 12:41:51 -0800
changeset 341057 2dee1d713c1d562341174eadcd90c6be2a01ef8b
parent 341056 089d4531be56bbc06b2f97bc6cab2e0479b788b8
child 341058 9c3763aa4bed5036916ea0b9e6a4a0b331a7e24f
push id86621
push usermozilla@noorenberghe.ca
push dateTue, 07 Feb 2017 05:12:02 +0000
treeherdermozilla-inbound@d71bc4a09493 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjohannh
bugs1330111
milestone54.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 1330111 - Automatically open login autocomplete from username fields if the form isn't filled. r=johannh MozReview-Commit-ID: BWJcc0ZaA3K
toolkit/components/passwordmgr/LoginManagerContent.jsm
--- a/toolkit/components/passwordmgr/LoginManagerContent.jsm
+++ b/toolkit/components/passwordmgr/LoginManagerContent.jsm
@@ -39,16 +39,17 @@ XPCOMUtils.defineLazyGetter(this, "log",
 
 // These mirror signon.* prefs.
 var gEnabled, gAutofillForms, gStoreWhenAutocompleteOff;
 
 var observer = {
   QueryInterface : XPCOMUtils.generateQI([Ci.nsIObserver,
                                           Ci.nsIFormSubmitObserver,
                                           Ci.nsIWebProgressListener,
+                                          Ci.nsIDOMEventListener,
                                           Ci.nsISupportsWeakReference]),
 
   // nsIFormSubmitObserver
   notify(formElement, aWindow, actionURI) {
     log("observer notified for form submission.");
 
     // We're invoked before the content's |onsubmit| handlers, so we
     // can grab form data before it might be modified (see bug 257781).
@@ -103,16 +104,34 @@ var observer = {
     if (!(aWebProgress.loadType & Ci.nsIDocShell.LOAD_CMD_NORMAL)) {
       log("onStateChange: loadType isn't LOAD_CMD_NORMAL:", aWebProgress.loadType);
       return;
     }
 
     log("onStateChange handled:", channel);
     LoginManagerContent._onNavigation(aWebProgress.DOMWindow.document);
   },
+
+  handleEvent(aEvent) {
+    if (!aEvent.isTrusted) {
+      return;
+    }
+
+    if (!gEnabled) {
+      return;
+    }
+
+    switch (aEvent.type) {
+      // Only used for username fields.
+      case "focus": {
+        LoginManagerContent._onUsernameFocus(aEvent);
+        break;
+      }
+    }
+  },
 };
 
 Services.obs.addObserver(observer, "earlyformsubmit", false);
 var prefBranch = Services.prefs.getBranch("signon.");
 prefBranch.addObserver("", observer.onPrefChange, false);
 
 observer.onPrefChange(); // read initial values
 
@@ -521,19 +540,41 @@ var LoginManagerContent = {
 
   loginsFound({ form, loginsFound, recipes }) {
     let doc = form.ownerDocument;
     let autofillForm = gAutofillForms && !PrivateBrowsingUtils.isContentWindowPrivate(doc.defaultView);
 
     this._fillForm(form, autofillForm, false, false, false, loginsFound, recipes);
   },
 
-  /*
-   * onUsernameInput
-   *
+  /**
+   * Focus event handler for username fields to decide whether to show autocomplete.
+   * @param {FocusEvent} event
+   */
+  _onUsernameFocus(event) {
+    let focusedField = event.target;
+    if (!focusedField.mozIsTextField(true) || focusedField.readOnly) {
+      return;
+    }
+
+    if (this._isLoginAlreadyFilled(focusedField)) {
+      log("_onUsernameFocus: Already filled");
+      return;
+    }
+
+    let formFillFocused = this._formFillService.focusedInput;
+    if (formFillFocused == focusedField) {
+      log("_onUsernameFocus: Opening the autocomplete popup");
+      this._formFillService.showPopup();
+    } else {
+      log("_onUsernameFocus: FormFillController has a different focused input");
+    }
+  },
+
+  /**
    * Listens for DOMAutoComplete and blur events on an input field.
    */
   onUsernameInput(event) {
     if (!event.isTrusted)
       return;
 
     if (!gEnabled)
       return;
@@ -982,18 +1023,16 @@ var LoginManagerContent = {
 
       // Need a valid password field to do anything.
       if (passwordField == null) {
         log("not filling form, no password field found");
         autofillResult = AUTOFILL_RESULT.NO_PASSWORD_FIELD;
         return;
       }
 
-      this._formFillService.markAsLoginManagerField(passwordField);
-
       // If the password field is disabled or read-only, there's nothing to do.
       if (passwordField.disabled || passwordField.readOnly) {
         log("not filling form, password field disabled or read-only");
         autofillResult = AUTOFILL_RESULT.PASSWORD_DISABLED_READONLY;
         return;
       }
 
       // Attach autocomplete stuff to the username field, if we have
@@ -1169,16 +1208,29 @@ var LoginManagerContent = {
       if (autofillResult == -1) {
         // eslint-disable-next-line no-unsafe-finally
         throw new Error("_fillForm: autofillResult must be specified");
       }
 
       if (!userTriggered) {
         // Ignore fills as a result of user action for this probe.
         Services.telemetry.getHistogramById("PWMGR_FORM_AUTOFILL_RESULT").add(autofillResult);
+
+        if (usernameField) {
+          let focusedElement = this._formFillService.focusedInput;
+          if (usernameField == focusedElement &&
+              autofillResult !== AUTOFILL_RESULT.FILLED) {
+            log("_fillForm: Opening username autocomplete popup since the form wasn't autofilled");
+            this._formFillService.showPopup();
+          }
+        }
+      }
+
+      if (usernameField) {
+        usernameField.addEventListener("focus", observer);
       }
 
       Services.obs.notifyObservers(form.rootElement, "passwordmgr-processed-form", null);
     }
   },
 
   /**
    * Given a field, determine whether that field was last filled as a username