Bug 1497682 - Part 2. Don't set inputType to all <input type=text>. r=droeh
authorMakoto Kato <m_kato@ga2.so-net.ne.jp>
Tue, 18 Dec 2018 17:20:10 +0900
changeset 511109 78488dedd35ed0adcd9841f5f6a0db7eb365e58f
parent 511108 e5727b5630043971290ae6bac63e2b222b386328
child 511110 b68c8690cc9e3fc1b7092d0939af17290479975d
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdroeh
bugs1497682
milestone66.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 1497682 - Part 2. Don't set inputType to all <input type=text>. r=droeh Summary: LastPass will fill password to all input elements which InputType is TYPE_CALSS_TEXT and TYPE_TEXT_VARIATION_WEB_EDIT_TEXT and has no AutofillHint. And it will fill username when InputType and AutofillHint is nothing in <input type="text">. Actually, current implementation of GeckoView sets InputType only for <input type="text">, so LastPass fills password to all <input type="text"> So as workaround, we should set InputType and AutofillHint when input element presumes username fields. Depends on D12880 Reviewers: droeh Reviewed By: droeh Bug #: 1497682 Differential Revision: https://phabricator.services.mozilla.com/D12881
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/SessionTextInput.java
mobile/android/modules/geckoview/GeckoViewAutoFill.jsm
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/SessionTextInput.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/SessionTextInput.java
@@ -649,16 +649,17 @@ public final class SessionTextInput {
                     structure.setClassName("android.view.ViewGroup");
                 } else {
                     structure.setClassName("android.view.View");
                 }
                 break;
         }
 
         if (Build.VERSION.SDK_INT >= 26 && "INPUT".equals(tag)) {
+            // LastPass will fill password to the feild that setAutofillHints is unset and setInputType is set.
             switch (type) {
                 case "email":
                     structure.setAutofillHints(new String[] { View.AUTOFILL_HINT_EMAIL_ADDRESS });
                     structure.setInputType(InputType.TYPE_CLASS_TEXT |
                                               InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS);
                     break;
                 case "number":
                     structure.setInputType(InputType.TYPE_CLASS_NUMBER);
@@ -667,24 +668,28 @@ public final class SessionTextInput {
                     structure.setAutofillHints(new String[] { View.AUTOFILL_HINT_PASSWORD });
                     structure.setInputType(InputType.TYPE_CLASS_TEXT |
                                            InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD);
                     break;
                 case "tel":
                     structure.setAutofillHints(new String[] { View.AUTOFILL_HINT_PHONE });
                     structure.setInputType(InputType.TYPE_CLASS_PHONE);
                     break;
-                case "text":
-                    structure.setInputType(InputType.TYPE_CLASS_TEXT |
-                                           InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT);
-                    break;
                 case "url":
                     structure.setInputType(InputType.TYPE_CLASS_TEXT |
                                            InputType.TYPE_TEXT_VARIATION_URI);
                     break;
+                case "text":
+                    final String autofillhint = bundle.getString("autofillhint", "");
+                    if (autofillhint.equals("username")) {
+                        structure.setAutofillHints(new String[] { View.AUTOFILL_HINT_USERNAME });
+                        structure.setInputType(InputType.TYPE_CLASS_TEXT |
+                                               InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT);
+                    }
+                    break;
             }
         }
     }
 
     /* package */ void addAutoFill(@NonNull final GeckoBundle message,
                                    @NonNull final EventCallback callback) {
         if (Build.VERSION.SDK_INT < 23) {
             return;
--- a/mobile/android/modules/geckoview/GeckoViewAutoFill.jsm
+++ b/mobile/android/modules/geckoview/GeckoViewAutoFill.jsm
@@ -7,16 +7,17 @@
 var EXPORTED_SYMBOLS = ["GeckoViewAutoFill"];
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/GeckoViewUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetters(this, {
   DeferredTask: "resource://gre/modules/DeferredTask.jsm",
   FormLikeFactory: "resource://gre/modules/FormLikeFactory.jsm",
+  LoginManagerContent: "resource://gre/modules/LoginManagerContent.jsm",
 });
 
 GeckoViewUtils.initLogging("AutoFill", this);
 
 class GeckoViewAutoFill {
   constructor(aEventDispatcher) {
     this._eventDispatcher = aEventDispatcher;
     this._autoFillId = 0;
@@ -73,17 +74,17 @@ class GeckoViewAutoFill {
 
     if (!this._autoFillInfos) {
       this._autoFillInfos = new WeakMap();
       this._autoFillElements = new Map();
     }
 
     let sendFocusEvent = false;
     const window = aFormLike.rootElement.ownerGlobal;
-    const getInfo = (element, parent, root) => {
+    const getInfo = (element, parent, root, usernameField) => {
       let info = this._autoFillInfos.get(element);
       if (info) {
         return info;
       }
       info = {
         id: ++this._autoFillId,
         parent,
         root,
@@ -94,27 +95,38 @@ class GeckoViewAutoFill {
                    "number", "password", "range", "search", "tel", "text",
                    "time", "url", "week"].includes(element.type),
         disabled: element instanceof window.HTMLInputElement ? element.disabled
                                                              : null,
         attributes: Object.assign({}, ...Array.from(element.attributes)
             .filter(attr => attr.localName !== "value")
             .map(attr => ({[attr.localName]: attr.value}))),
         origin: element.ownerDocument.location.origin,
+        autofillhint: "",
       };
+
+      if (element === usernameField) {
+        info.autofillhint = "username"; // AUTOFILL_HINT_USERNAME
+      }
+
       this._autoFillInfos.set(element, info);
       this._autoFillElements.set(info.id, Cu.getWeakReference(element));
       sendFocusEvent |= (element === element.ownerDocument.activeElement);
       return info;
     };
 
-    const rootInfo = getInfo(aFormLike.rootElement, null, undefined);
+    let [usernameField] =
+      LoginManagerContent.getUserNameAndPasswordFields(aFormLike.elements[0]);
+
+    const rootInfo = getInfo(aFormLike.rootElement, null, undefined, null);
     rootInfo.root = rootInfo.id;
-    rootInfo.children = aFormLike.elements.map(
-        element => getInfo(element, rootInfo.id, rootInfo.id));
+    rootInfo.children = aFormLike.elements
+        .filter(element => (!usernameField || element.type != "text" ||
+                            element == usernameField))
+        .map(element => getInfo(element, rootInfo.id, rootInfo.id, usernameField));
 
     this._eventDispatcher.dispatch("GeckoView:AddAutoFill", rootInfo, {
       onSuccess: responses => {
         // `responses` is an object with IDs as keys.
         debug `Performing auto-fill ${Object.keys(responses)}`;
 
         const AUTOFILL_STATE = "-moz-autofill";
         const winUtils = window.windowUtils;