Bug 1252074 - test_form_autocomplete.html and test_form_autocomplete_with_list.html should pass on e10s. r=paolo
authorMarco Bonardo <mbonardo@mozilla.com>
Wed, 02 Mar 2016 14:34:38 +0100
changeset 325906 ee0cb75bcbf99f082f6c31ba4e54d88a8b2d9f7b
parent 325905 0d38e391b3a15ff78e4922071a067640990a44fc
child 325907 337116c1afacfebc51240777af7777ea1405d7e4
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspaolo
bugs1252074
milestone48.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 1252074 - test_form_autocomplete.html and test_form_autocomplete_with_list.html should pass on e10s. r=paolo MozReview-Commit-ID: CISKztr4p4N
toolkit/components/satchel/AutoCompleteE10S.jsm
toolkit/components/satchel/nsFormAutoComplete.js
toolkit/components/satchel/nsFormFillController.cpp
toolkit/components/satchel/nsIFormAutoComplete.idl
toolkit/components/satchel/test/mochitest.ini
toolkit/components/satchel/test/parent_utils.js
toolkit/components/satchel/test/test_form_autocomplete.html
toolkit/components/satchel/test/test_form_autocomplete_with_list.html
--- a/toolkit/components/satchel/AutoCompleteE10S.jsm
+++ b/toolkit/components/satchel/AutoCompleteE10S.jsm
@@ -81,16 +81,17 @@ var AutoCompleteE10SView = {
 this.AutoCompleteE10S = {
   init: function() {
     let messageManager = Cc["@mozilla.org/globalmessagemanager;1"].
                          getService(Ci.nsIMessageListenerManager);
     messageManager.addMessageListener("FormAutoComplete:SelectBy", this);
     messageManager.addMessageListener("FormAutoComplete:GetSelectedIndex", this);
     messageManager.addMessageListener("FormAutoComplete:MaybeOpenPopup", this);
     messageManager.addMessageListener("FormAutoComplete:ClosePopup", this);
+    messageManager.addMessageListener("FormAutoComplete:Disconnect", this);
     messageManager.addMessageListener("FormAutoComplete:RemoveEntry", this);
   },
 
   _initPopup: function(browserWindow, rect, direction) {
     this._popupCache = { browserWindow, rect, direction };
 
     this.browser = browserWindow.gBrowser.selectedBrowser;
     this.popup = this.browser.autoCompletePopup;
@@ -185,20 +186,27 @@ this.AutoCompleteE10S = {
                                    Ci.nsIAutoCompleteResult.RESULT_SUCCESS,
                                    0, "", message.data.datalistResult.values,
                                    message.data.datalistResult.labels,
                                    [], null);
     } else {
       message.data.datalistResult = null;
     }
 
+    let previousResult = null;
+    let previousSearchString = message.data.previousSearchString;
+    let searchString = message.data.untrimmedSearchString.toLowerCase();
+    if (previousSearchString && previousSearchString.length > 1 &&
+        searchString.includes(previousSearchString)) {
+      previousResult = this._resultCache;
+    }
     formAutoComplete.autoCompleteSearchAsync(message.data.inputName,
                                              message.data.untrimmedSearchString,
                                              message.data.mockField,
-                                             null,
+                                             previousResult,
                                              message.data.datalistResult,
                                              { onSearchCompletion:
                                                this.onSearchComplete.bind(this) });
   },
 
   // The second half of search, this fills in the popup and returns the
   // results to the child.
   onSearchComplete: function(results) {
@@ -234,16 +242,24 @@ this.AutoCompleteE10S = {
                                     this._popupCache.rect,
                                     this._resultCache);
         }
         break;
 
       case "FormAutoComplete:ClosePopup":
         this.popup.closePopup();
         break;
+
+      case "FormAutoComplete:Disconnect":
+        // The controller stopped controlling the current input, so clear
+        // any cached data.  This is necessary cause otherwise we'd clear data
+        // only when starting a new search, but the next input could not support
+        // autocomplete and it would end up inheriting the existing data.
+        AutoCompleteE10SView.clearResults();
+        break;
     }
   },
 
   handleEnter: function(aIsPopupSelection) {
     this.browser.messageManager.sendAsyncMessage(
       "FormAutoComplete:HandleEnter",
       { selectedIndex: this.popup.selectedIndex,
         isPopupSelection: aIsPopupSelection }
--- a/toolkit/components/satchel/nsFormAutoComplete.js
+++ b/toolkit/components/satchel/nsFormAutoComplete.js
@@ -497,16 +497,17 @@ FormAutoCompleteChild.prototype = {
       let mm = topLevelDocshell.QueryInterface(Ci.nsIInterfaceRequestor)
                                .getInterface(Ci.nsIContentFrameMessageManager);
 
       mm.sendAsyncMessage("FormHistory:AutoCompleteSearchAsync", {
         inputName: aInputName,
         untrimmedSearchString: aUntrimmedSearchString,
         mockField: mockField,
         datalistResult: datalistResult,
+        previousSearchString: aPreviousResult && aPreviousResult.searchString.trim().toLowerCase(),
         left: rect.left,
         top: rect.top,
         width: rect.width,
         height: rect.height,
         direction: direction,
       });
 
       let search = this._pendingSearch = {};
@@ -519,32 +520,43 @@ FormAutoCompleteChild.prototype = {
           return;
         }
         this._pendingSearch = null;
 
         let result = new FormAutoCompleteResult(
           null,
           Array.from(message.data.results, res => ({ text: res })),
           null,
-          null,
+          aUntrimmedSearchString,
           mm
         );
         if (aListener) {
           aListener.onSearchCompletion(result);
         }
       }
 
       mm.addMessageListener("FormAutoComplete:AutoCompleteSearchAsyncResult", searchFinished);
       this.log("autoCompleteSearchAsync message was sent");
     },
 
     stopAutoCompleteSearch : function () {
       this.log("stopAutoCompleteSearch");
       this._pendingSearch = null;
     },
+
+    stopControllingInput(aField) {
+      let window = aField.ownerDocument.defaultView;
+      let topLevelDocshell = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                                   .getInterface(Ci.nsIDocShell)
+                                   .sameTypeRootTreeItem
+                                   .QueryInterface(Ci.nsIDocShell);
+      let mm = topLevelDocshell.QueryInterface(Ci.nsIInterfaceRequestor)
+                               .getInterface(Ci.nsIContentFrameMessageManager);
+      mm.sendAsyncMessage("FormAutoComplete:Disconnect");
+    }
 }; // end of FormAutoCompleteChild implementation
 
 // nsIAutoCompleteResult implementation
 function FormAutoCompleteResult(formHistory,
                                 entries,
                                 fieldName,
                                 searchString,
                                 messageManager) {
@@ -571,17 +583,17 @@ FormAutoCompleteResult.prototype = {
 
     // Allow autoCompleteSearch to get at the JS object so it can
     // modify some readonly properties for internal use.
     get wrappedJSObject() {
         return this;
     },
 
     // Interfaces from idl...
-    searchString : null,
+    searchString : "",
     errorDescription : "",
     get defaultIndex() {
         if (this.entries.length == 0)
             return -1;
         else
             return 0;
     },
     get searchResult() {
--- a/toolkit/components/satchel/nsFormFillController.cpp
+++ b/toolkit/components/satchel/nsFormFillController.cpp
@@ -1186,16 +1186,24 @@ nsFormFillController::StopControllingInp
     nsCOMPtr<nsIAutoCompleteInput> input;
     mController->GetInput(getter_AddRefs(input));
     if (input == this)
       mController->SetInput(nullptr);
   }
 
   if (mFocusedInputNode) {
     MaybeRemoveMutationObserver(mFocusedInputNode);
+
+    nsresult rv;
+    nsCOMPtr <nsIFormAutoComplete> formAutoComplete =
+      do_GetService("@mozilla.org/satchel/form-autocomplete;1", &rv);
+    if (formAutoComplete) {
+      formAutoComplete->StopControllingInput(mFocusedInput);
+    }
+
     mFocusedInputNode = nullptr;
     mFocusedInput = nullptr;
   }
   mFocusedPopup = nullptr;
 }
 
 nsIDocShell *
 nsFormFillController::GetDocShellForInput(nsIDOMHTMLInputElement *aInput)
--- a/toolkit/components/satchel/nsIFormAutoComplete.idl
+++ b/toolkit/components/satchel/nsIFormAutoComplete.idl
@@ -21,16 +21,21 @@ interface nsIFormAutoComplete: nsISuppor
                                  in nsIAutoCompleteResult aDatalistResult,
                                  in nsIFormAutoCompleteObserver aListener);
 
     /**
      * If a search is in progress, stop it. Otherwise, do nothing. This is used
      * to cancel an existing search, for example, in preparation for a new search.
      */
     void stopAutoCompleteSearch();
+
+    /**
+     * Since the controller is disconnecting, any related data must be cleared.
+     */
+    void stopControllingInput(in nsIDOMHTMLInputElement aField);
 };
 
 [scriptable, function, uuid(604419ab-55a0-4831-9eca-1b9e67cc4751)]
 interface nsIFormAutoCompleteObserver : nsISupports
 {
   /*
    * Called when a search is complete and the results are ready even if the
    * result set is empty. If the search is cancelled or a new search is
--- a/toolkit/components/satchel/test/mochitest.ini
+++ b/toolkit/components/satchel/test/mochitest.ini
@@ -7,15 +7,13 @@ support-files =
   parent_utils.js
 
 [test_bug_511615.html]
 skip-if = e10s # bug 1162338 (needs refactoring to talk to the autocomplete popup)
 [test_bug_787624.html]
 skip-if = e10s # bug 1162338 (needs refactoring to talk to the autocomplete popup)
 [test_datalist_with_caching.html]
 [test_form_autocomplete.html]
-skip-if = e10s # bug 1162329 or bug 1162338
 [test_form_autocomplete_with_list.html]
-skip-if = e10s # bug 1162329 (autocomplete impl. in e10s isn't complete)
 [test_form_submission.html]
 [test_form_submission_cap.html]
 [test_form_submission_cap2.html]
 [test_popup_enter_event.html]
--- a/toolkit/components/satchel/test/parent_utils.js
+++ b/toolkit/components/satchel/test/parent_utils.js
@@ -67,17 +67,17 @@ var ParentUtils = {
     FormHistory.count(obj, listener);
   },
 
   checkRowCount(expectedCount, expectedFirstValue = null) {
     ContentTaskUtils.waitForCondition(() => {
       return gAutocompletePopup.tree.view.rowCount === expectedCount &&
         (!expectedFirstValue ||
           expectedCount <= 1 ||
-          gAutocompletePopup.tree.view.getValueAt(0, gAutocompletePopup.tree.columns[0]) ===
+          gAutocompletePopup.tree.view.getCellText(0, gAutocompletePopup.tree.columns[0]) ===
           expectedFirstValue);
     }).then(() => {
       let results = this.getMenuEntries();
       sendAsyncMessage("gotMenuChange", { results });
     });
   },
 
   observe(subject, topic, data) {
--- a/toolkit/components/satchel/test/test_form_autocomplete.html
+++ b/toolkit/components/satchel/test/test_form_autocomplete.html
@@ -646,25 +646,27 @@ function runTest() {
         waitForMenuChange(2);
         break;
 
     case 205:
         checkMenuEntries(["az", "aaz"], testNum);
         input.focus();
         doKey("left");
         expectPopup();
-        sendChar("a");
+        // Check case-insensitivity.
+        sendChar("A");
         break;
 
     case 206:
         checkMenuEntries(["aaz"], testNum);
         addEntry("field3", "aazq");
         break;
 
     case 207:
+        // check that results were cached
         input.focus();
         doKey("right");
         sendChar("q");
         waitForMenuChange(0);
         break;
 
     case 208:
         // check that results were cached
@@ -966,17 +968,17 @@ function runTest() {
         checkForm("");
         restoreForm();
         doKey("down");
         waitForMenuChange(0);
         break;
 
     case 601:
         checkMenuEntries([], testNum);
-        SpecialPowers.removeAutoCompletePopupEventListener(window, "popupshown", popupShownListener);
+        input.blur();
         SimpleTest.finish();
         return;
 
     default:
         ok(false, "Unexpected invocation of test #" + testNum);
         SimpleTest.finish();
         return;
   }
--- a/toolkit/components/satchel/test/test_form_autocomplete_with_list.html
+++ b/toolkit/components/satchel/test/test_form_autocomplete_with_list.html
@@ -345,78 +345,75 @@ function runTest() {
 
     case 201:
       // Adding an attribute after the first one while on the first then going
       // down and push enter. Value should be the on from the new suggestion.
       doKey("down");
       datalist = document.getElementById('suggest');
       var added = new Option("Foo");
       datalist.insertBefore(added, datalist.children[1]);
-
-      SimpleTest.executeSoon(function() {
-        doKey("down");
-        doKey("down");
-        doKey("return");
-        checkForm("Foo");
-
-        // Remove the element.
-        datalist.removeChild(added);
-        expectPopup();
-        restoreForm();
-        doKey("down");
-      });
+      waitForMenuChange(4);
       break;
 
     case 202:
+      doKey("down");
+      doKey("down");
+      doKey("return");
+      checkForm("Foo");
+
+      // Remove the element.
+      datalist = document.getElementById('suggest');
+      datalist.removeChild(datalist.children[1]);
+      waitForMenuChange(0);
+      break;
+
+    case 203:
       // Change the first element value attribute.
-      doKey("down");
+      restoreForm();
       datalist = document.getElementById('suggest');
       prevValue = datalist.children[0].value;
       datalist.children[0].value = "foo";
       expectPopup();
       break;
 
-    case 203:
+    case 204:
       doKey("down");
       doKey("return");
       checkForm("foo");
 
       datalist = document.getElementById('suggest');
       datalist.children[0].value = prevValue;
-      expectPopup();
-      restoreForm();
-      doKey("down");
+      waitForMenuChange(0);
       break;
 
-    case 204:
+    case 205:
       // Change the textContent to update the value attribute.
-      doKey("down");
+      restoreForm();
       datalist = document.getElementById('suggest');
       prevValue = datalist.children[0].getAttribute('value');
       datalist.children[0].removeAttribute('value');
       datalist.children[0].textContent = "foobar";
       expectPopup();
       break;
 
-    case 205:
+    case 206:
       doKey("down");
       doKey("return");
       checkForm("foobar");
 
       datalist = document.getElementById('suggest');
       datalist.children[0].setAttribute('value', prevValue);
       testNum = 299;
-      expectPopup();
-      restoreForm();
-      doKey("down");
+      waitForMenuChange(0);
       break;
 
     // Tests for filtering (or not).
     case 300:
       // Filters with first letter of the word.
+      restoreForm();
       synthesizeKey("f", {});
       expectPopup();
       break;
 
     case 301:
       doKey("down");
       doKey("return");
       checkForm("final");
@@ -464,16 +461,17 @@ function runTest() {
     case 400:
       // Check that the input event is fired.
       input.addEventListener("input", function(event) {
         input.removeEventListener("input", arguments.callee, false);
         ok(true, "oninput should have been received");
         ok(event.bubbles, "input event should bubble");
         ok(event.cancelable, "input event should be cancelable");
         checkForm("Google");
+        input.blur();
         SimpleTest.finish();
       }, false);
 
       doKey("down");
       checkForm("");
       doKey("return");
       break;