Bug 1037353 - Don't show autocomplete popup after selecting an autocomplete value. r=jchen
authorBrian Nicholson <bnicholson@mozilla.com>
Fri, 29 Aug 2014 11:09:15 -0700
changeset 202348 c8449f5148ed11c77791c00418ee25b8582658dd
parent 202347 8f49394f80690a8b605e9363b19b12450e11da47
child 202349 45f490ca414f0ee106fdc2724eb5970c24fdac4f
push id8438
push userbnicholson@mozilla.com
push dateFri, 29 Aug 2014 18:09:44 +0000
treeherderfx-team@c8449f5148ed [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjchen
bugs1037353
milestone34.0a1
Bug 1037353 - Don't show autocomplete popup after selecting an autocomplete value. r=jchen
mobile/android/chrome/content/browser.js
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -5507,45 +5507,53 @@ var ErrorPageEventHandler = {
 
 var FormAssistant = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIFormSubmitObserver]),
 
   // Used to keep track of the element that corresponds to the current
   // autocomplete suggestions
   _currentInputElement: null,
 
+  // The value of the currently focused input
+  _currentInputValue: null,
+
+  // Whether we're in the middle of an autocomplete
+  _doingAutocomplete: false,
+
   _isBlocklisted: false,
 
   // Keep track of whether or not an invalid form has been submitted
   _invalidSubmit: false,
 
   init: function() {
     Services.obs.addObserver(this, "FormAssist:AutoComplete", false);
     Services.obs.addObserver(this, "FormAssist:Blocklisted", false);
     Services.obs.addObserver(this, "FormAssist:Hidden", false);
     Services.obs.addObserver(this, "invalidformsubmit", false);
     Services.obs.addObserver(this, "PanZoom:StateChange", false);
 
     // We need to use a capturing listener for focus events
     BrowserApp.deck.addEventListener("focus", this, true);
+    BrowserApp.deck.addEventListener("blur", this, true);
     BrowserApp.deck.addEventListener("click", this, true);
     BrowserApp.deck.addEventListener("input", this, false);
     BrowserApp.deck.addEventListener("pageshow", this, false);
 
     LoginManagerParent.init();
   },
 
   uninit: function() {
     Services.obs.removeObserver(this, "FormAssist:AutoComplete");
     Services.obs.removeObserver(this, "FormAssist:Blocklisted");
     Services.obs.removeObserver(this, "FormAssist:Hidden");
     Services.obs.removeObserver(this, "invalidformsubmit");
     Services.obs.removeObserver(this, "PanZoom:StateChange");
 
     BrowserApp.deck.removeEventListener("focus", this);
+    BrowserApp.deck.removeEventListener("blur", this);
     BrowserApp.deck.removeEventListener("click", this);
     BrowserApp.deck.removeEventListener("input", this);
     BrowserApp.deck.removeEventListener("pageshow", this);
   },
 
   observe: function(aSubject, aTopic, aData) {
     switch (aTopic) {
       case "PanZoom:StateChange":
@@ -5567,29 +5575,35 @@ var FormAssistant = {
         }
         break;
       case "FormAssist:AutoComplete":
         if (!this._currentInputElement)
           break;
 
         let editableElement = this._currentInputElement.QueryInterface(Ci.nsIDOMNSEditableElement);
 
+        this._doingAutocomplete = true;
+
         // If we have an active composition string, commit it before sending
         // the autocomplete event with the text that will replace it.
         try {
           let imeEditor = editableElement.editor.QueryInterface(Ci.nsIEditorIMESupport);
           if (imeEditor.composing)
             imeEditor.forceCompositionEnd();
         } catch (e) {}
 
         editableElement.setUserInput(aData);
+        this._currentInputValue = aData;
 
         let event = this._currentInputElement.ownerDocument.createEvent("Events");
         event.initEvent("DOMAutoComplete", true, true);
         this._currentInputElement.dispatchEvent(event);
+
+        this._doingAutocomplete = false;
+
         break;
 
       case "FormAssist:Blocklisted":
         this._isBlocklisted = (aData == "true");
         break;
 
       case "FormAssist:Hidden":
         this._currentInputElement = null;
@@ -5610,72 +5624,93 @@ var FormAssistant = {
 
     // Our focus listener will show the element's validation message
     let currentElement = aInvalidElements.queryElementAt(0, Ci.nsISupports);
     currentElement.focus();
   },
 
   handleEvent: function(aEvent) {
     switch (aEvent.type) {
-      case "focus":
+      case "focus": {
         let currentElement = aEvent.target;
 
         // Only show a validation message on focus.
         this._showValidationMessage(currentElement);
         break;
-
-      case "click":
-        currentElement = aEvent.target;
+      }
+
+      case "blur": {
+        this._currentInputValue = null;
+        break;
+      }
+
+      case "click": {
+        let currentElement = aEvent.target;
 
         // Prioritize a form validation message over autocomplete suggestions
         // when the element is first focused (a form validation message will
         // only be available if an invalid form was submitted)
         if (this._showValidationMessage(currentElement))
           break;
 
         let checkResultsClick = hasResults => {
           if (!hasResults) {
             this._hideFormAssistPopup();
           }
         };
 
         this._showAutoCompleteSuggestions(currentElement, checkResultsClick);
         break;
-
-      case "input":
-        currentElement = aEvent.target;
+      }
+
+      case "input": {
+        let currentElement = aEvent.target;
+
+        // If this element isn't focused, we're already in middle of an
+        // autocomplete, or its value hasn't changed, don't show the
+        // autocomplete popup.
+        if (currentElement !== BrowserApp.getFocusedInput(BrowserApp.selectedBrowser) ||
+            this._doingAutocomplete ||
+            currentElement.value === this._currentInputValue) {
+          break;
+        }
+
+        this._currentInputValue = currentElement.value;
 
         // Since we can only show one popup at a time, prioritze autocomplete
         // suggestions over a form validation message
         let checkResultsInput = hasResults => {
           if (hasResults)
             return;
 
           if (this._showValidationMessage(currentElement))
             return;
 
           // If we're not showing autocomplete suggestions, hide the form assist popup
           this._hideFormAssistPopup();
         };
 
         this._showAutoCompleteSuggestions(currentElement, checkResultsInput);
         break;
+      }
 
       // Reset invalid submit state on each pageshow
-      case "pageshow":
+      case "pageshow": {
         if (!this._invalidSubmit)
           return;
 
         let selectedBrowser = BrowserApp.selectedBrowser;
         if (selectedBrowser) {
           let selectedDocument = selectedBrowser.contentDocument;
           let target = aEvent.originalTarget;
           if (target == selectedDocument || target.ownerDocument == selectedDocument)
             this._invalidSubmit = false;
         }
+        break;
+      }
     }
   },
 
   // We only want to show autocomplete suggestions for certain elements
   _isAutoComplete: function _isAutoComplete(aElement) {
     if (!(aElement instanceof HTMLInputElement) || aElement.readOnly ||
         (aElement.getAttribute("type") == "password") ||
         (aElement.hasAttribute("autocomplete") &&