Bug 720081 - Part 1: backportable solution for autocomplete controller to provide a different final defaultComplete value for typeAheadResults
authorMarco Bonardo <mbonardo@mozilla.com>
Mon, 04 Jun 2012 00:42:48 +0200
changeset 95947 1851b8c13061fe22eb15a90a9284ec5b19dec808
parent 95946 173d2921c023501bf1544115dbf12754f6df1403
child 95948 762d16222aba62fd45c00ca98262ae5856e39133
push id886
push userlsblakk@mozilla.com
push dateMon, 04 Jun 2012 19:57:52 +0000
treeherdermozilla-beta@bbd8d5efd6d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs720081
milestone14.0a2
Bug 720081 - Part 1: backportable solution for autocomplete controller to provide a different final defaultComplete value for typeAheadResults r+a=gavin
toolkit/components/autocomplete/nsAutoCompleteController.cpp
toolkit/components/autocomplete/nsAutoCompleteController.h
toolkit/components/autocomplete/tests/unit/test_finalDefaultCompleteValue.js
toolkit/components/autocomplete/tests/unit/test_popupSelectionVsDefaultCompleteValue.js
toolkit/components/autocomplete/tests/unit/xpcshell.ini
toolkit/components/places/tests/inline/test_casing.js
toolkit/components/places/tests/inline/test_trimming.js
toolkit/components/places/tests/inline/xpcshell.ini
--- a/toolkit/components/autocomplete/nsAutoCompleteController.cpp
+++ b/toolkit/components/autocomplete/nsAutoCompleteController.cpp
@@ -1201,35 +1201,35 @@ nsAutoCompleteController::EnterMatch(boo
   nsAutoString value;
   popup->GetOverrideValue(value);
   if (value.IsEmpty()) {
     bool shouldComplete;
     mInput->GetCompleteDefaultIndex(&shouldComplete);
     bool completeSelection;
     input->GetCompleteSelectedIndex(&completeSelection);
 
-    // If completeselectedindex is false or a row was selected from the popup,
-    // enter it into the textbox. If completeselectedindex is true, or
-    // EnterMatch was called via other means, for instance pressing Enter,
-    // don't fill in the value as it will have already been filled in as needed.
     PRInt32 selectedIndex;
     popup->GetSelectedIndex(&selectedIndex);
-    if (selectedIndex >= 0 && (!completeSelection || aIsPopupSelection))
-      GetResultValueAt(selectedIndex, true, value);
+    if (selectedIndex >= 0) {
+      // If completeselectedindex is false or a row was selected from the popup,
+      // enter it into the textbox. If completeselectedindex is true, or
+      // EnterMatch was called via other means, for instance pressing Enter,
+      // don't fill in the value as it will have already been filled in as
+      // needed.
+      if (!completeSelection || aIsPopupSelection)
+        GetResultValueAt(selectedIndex, true, value);
+    }
     else if (shouldComplete) {
       // We usually try to preserve the casing of what user has typed, but
       // if he wants to autocomplete, we will replace the value with the
       // actual autocomplete result.
       // The user wants explicitely to use that result, so this ensures
       // association of the result with the autocompleted text.
       nsAutoString defaultIndexValue;
-      nsAutoString inputValue;
-      input->GetTextValue(inputValue);
-      if (NS_SUCCEEDED(GetDefaultCompleteValue(-1, false, defaultIndexValue)) &&
-          defaultIndexValue.Equals(inputValue, nsCaseInsensitiveStringComparator()))
+      if (NS_SUCCEEDED(GetFinalDefaultCompleteValue(defaultIndexValue)))
         value = defaultIndexValue;
     }
 
     if (forceComplete && value.IsEmpty()) {
       // Since nothing was selected, and forceComplete is specified, that means
       // we have to find the first default match and enter it instead
       PRUint32 count = mResults.Count();
       for (PRUint32 i = 0; i < count; ++i) {
@@ -1476,57 +1476,71 @@ nsAutoCompleteController::CompleteDefaul
     CompleteValue(resultValue);
 
   mDefaultIndexCompleted = true;
 
   return NS_OK;
 }
 
 nsresult
-nsAutoCompleteController::GetDefaultCompleteValue(PRInt32 aResultIndex,
-                                                  bool aPreserveCasing,
-                                                  nsAString &_retval)
+nsAutoCompleteController::GetDefaultCompleteResult(PRInt32 aResultIndex,
+                                                   nsIAutoCompleteResult** _result,
+                                                   PRInt32* _defaultIndex)
 {
-  PRInt32 defaultIndex = -1;
-  PRInt32 index = aResultIndex;
-  if (index < 0) {
-    PRUint32 count = mResults.Count();
-    for (PRUint32 i = 0; i < count; ++i) {
-      nsIAutoCompleteResult *result = mResults[i];
-      if (result && NS_SUCCEEDED(result->GetDefaultIndex(&defaultIndex)) &&
-          defaultIndex >= 0) {
-        index = i;
-        break;
-      }
+  *_defaultIndex = -1;
+  PRInt32 resultIndex = aResultIndex;
+
+  // If a result index was not provided, find the first defaultIndex result.
+  for (PRInt32 i = 0; resultIndex < 0 && i < mResults.Count(); ++i) {
+    nsIAutoCompleteResult *result = mResults[i];
+    if (result &&
+        NS_SUCCEEDED(result->GetDefaultIndex(_defaultIndex)) &&
+        *_defaultIndex >= 0) {
+      resultIndex = i;
     }
   }
-  NS_ENSURE_TRUE(index >= 0, NS_ERROR_FAILURE);
+  NS_ENSURE_TRUE(resultIndex >= 0, NS_ERROR_FAILURE);
 
-  nsIAutoCompleteResult *result = mResults.SafeObjectAt(index);
-  NS_ENSURE_TRUE(result != nsnull, NS_ERROR_FAILURE);
+  *_result = mResults.SafeObjectAt(resultIndex);
+  NS_ENSURE_TRUE(*_result, NS_ERROR_FAILURE);
 
-  if (defaultIndex < 0) {
+  if (*_defaultIndex < 0) {
     // The search must explicitly provide a default index in order
     // for us to be able to complete.
-    result->GetDefaultIndex(&defaultIndex);
+    (*_result)->GetDefaultIndex(_defaultIndex);
   }
-  if (defaultIndex < 0) {
+
+  if (*_defaultIndex < 0) {
     // We were given a result index, but that result doesn't want to
     // be autocompleted.
     return NS_ERROR_FAILURE;
   }
 
   // If the result wrongly notifies a RESULT_SUCCESS with no matches, or
   // provides a defaultIndex greater than its matchCount, avoid trying to
   // complete to an empty value.
   PRUint32 matchCount = 0;
-  result->GetMatchCount(&matchCount);
+  (*_result)->GetMatchCount(&matchCount);
   // Here defaultIndex is surely non-negative, so can be cast to unsigned.
-  if ((PRUint32)defaultIndex >= matchCount)
+  if ((PRUint32)(*_defaultIndex) >= matchCount) {
     return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+nsAutoCompleteController::GetDefaultCompleteValue(PRInt32 aResultIndex,
+                                                  bool aPreserveCasing,
+                                                  nsAString &_retval)
+{
+  nsIAutoCompleteResult *result;
+  PRInt32 defaultIndex = -1;
+  nsresult rv = GetDefaultCompleteResult(aResultIndex, &result, &defaultIndex);
+  if (NS_FAILED(rv)) return rv;
 
   nsAutoString resultValue;
   result->GetValueAt(defaultIndex, resultValue);
   if (aPreserveCasing &&
       StringBeginsWith(resultValue, mSearchString,
                        nsCaseInsensitiveStringComparator())) {
     // We try to preserve user casing, otherwise we would end up changing
     // the case of what he typed, if we have a result with a different casing.
@@ -1545,16 +1559,48 @@ nsAutoCompleteController::GetDefaultComp
   }
   else
     _retval = resultValue;
 
   return NS_OK;
 }
 
 nsresult
+nsAutoCompleteController::GetFinalDefaultCompleteValue(nsAString &_retval)
+{
+  nsIAutoCompleteResult *result;
+  PRInt32 defaultIndex = -1;
+  nsresult rv = GetDefaultCompleteResult(-1, &result, &defaultIndex);
+  if (NS_FAILED(rv)) return rv;
+
+  result->GetValueAt(defaultIndex, _retval);
+  nsAutoString inputValue;
+  mInput->GetTextValue(inputValue);
+  if (!_retval.Equals(inputValue, nsCaseInsensitiveStringComparator())) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // Hack: For typeAheadResults allow the comment to be used as the final
+  // defaultComplete value if provided, otherwise fall back to the usual
+  // value.  This allows to provide a different complete text when the user
+  // confirms the match.  Don't rely on this for production code, since it's a
+  // temporary solution that needs a dedicated API (bug 754265).
+  bool isTypeAheadResult = false;
+  nsAutoString commentValue;
+  if (NS_SUCCEEDED(result->GetTypeAheadResult(&isTypeAheadResult)) &&
+      isTypeAheadResult &&
+      NS_SUCCEEDED(result->GetCommentAt(defaultIndex, commentValue)) &&
+      !commentValue.IsEmpty()) {
+    _retval = commentValue;
+  }
+
+  return NS_OK;
+}
+
+nsresult
 nsAutoCompleteController::CompleteValue(nsString &aValue)
 /* mInput contains mSearchString, which we want to autocomplete to aValue.  If
  * selectDifference is true, select the remaining portion of aValue not
  * contained in mSearchString. */
 {
   const PRInt32 mSearchStringLength = mSearchString.Length();
   PRInt32 endSelect = aValue.Length();  // By default, select all of aValue.
 
--- a/toolkit/components/autocomplete/nsAutoCompleteController.h
+++ b/toolkit/components/autocomplete/nsAutoCompleteController.h
@@ -95,18 +95,59 @@ protected:
   nsresult GetResultValueAt(PRInt32 aIndex, bool aValueOnly,
                             nsAString & _retval);
   nsresult GetResultLabelAt(PRInt32 aIndex, bool aValueOnly,
                             nsAString & _retval);
 private:
   nsresult GetResultValueLabelAt(PRInt32 aIndex, bool aValueOnly,
                                  bool aGetValue, nsAString & _retval);
 protected:
+
+  /**
+   * Gets and validates the defaultComplete result and the relative
+   * defaultIndex value.
+   *
+   * @param aResultIndex
+   *        Index of the defaultComplete result to be used.  Pass -1 to search
+   *        for the first result providing a valid defaultIndex.
+   * @param _result
+   *        The found result.
+   * @param _defaultIndex
+   *        The defaultIndex relative to _result.
+   */
+  nsresult GetDefaultCompleteResult(PRInt32 aResultIndex,
+                                    nsIAutoCompleteResult** _result,
+                                    PRInt32* _defaultIndex);
+
+  /**
+   * Gets the defaultComplete value to be suggested to the user.
+   *
+   * @param aResultIndex
+   *        Index of the defaultComplete result to be used.
+   * @param aPreserveCasing
+   *        Whether user casing should be preserved.
+   * @param _retval
+   *        The value to be completed.
+   */
   nsresult GetDefaultCompleteValue(PRInt32 aResultIndex, bool aPreserveCasing,
                                    nsAString &_retval);
+
+  /**
+   * Gets the defaultComplete value to be used when the user confirms the
+   * current match.
+   * The value is returned only if it case-insensitively matches the current
+   * input text, otherwise the method returns NS_ERROR_FAILURE.
+   * This happens because we don't want to replace text if the user backspaces
+   * just before Enter.
+   *
+   * @param _retval
+   *        The value to be completed.
+   */
+  nsresult GetFinalDefaultCompleteValue(nsAString &_retval);
+
   nsresult ClearResults();
   
   nsresult RowIndexToSearch(PRInt32 aRowIndex,
                             PRInt32 *aSearchIndex, PRInt32 *aItemIndex);
 
   // members //////////////////////////////////////////
   
   nsCOMPtr<nsIAutoCompleteInput> mInput;
new file mode 100644
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_finalDefaultCompleteValue.js
@@ -0,0 +1,67 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function AutoCompleteResult(aValues, aComments) {
+  this._values = aValues;
+  this._comments = aComments;
+  this.defaultIndex = 0;
+  this._typeAheadResult = true;
+}
+AutoCompleteResult.prototype = Object.create(AutoCompleteResultBase.prototype);
+
+function AutoCompleteInput(aSearches) {
+  this.searches = aSearches;
+  this.popup.selectedIndex = -1;
+  this.completeDefaultIndex = true;
+}
+AutoCompleteInput.prototype = Object.create(AutoCompleteInputBase.prototype);
+
+function run_test() {
+  run_next_test();
+}
+
+add_test(function test_keyNavigation() {
+  doSearch("moz", "mozilla.com", "http://www.mozilla.com", function(aController) {
+    do_check_eq(aController.input.textValue, "mozilla.com");
+    aController.handleKeyNavigation(Ci.nsIDOMKeyEvent.DOM_VK_RIGHT);
+    do_check_eq(aController.input.textValue, "mozilla.com");
+  });
+});
+
+add_test(function test_handleEnter() {
+  doSearch("moz", "mozilla.com", "http://www.mozilla.com", function(aController) {
+    do_check_eq(aController.input.textValue, "mozilla.com");
+    aController.handleEnter(false);
+    do_check_eq(aController.input.textValue, "http://www.mozilla.com");
+  });
+});
+
+function doSearch(aSearchString, aResultValue, aCommentValue, aOnCompleteCallback) {
+  let search = new AutoCompleteSearchBase(
+    "search",
+    new AutoCompleteResult([ aResultValue ], [ aCommentValue ], 0)
+  );
+  registerAutoCompleteSearch(search);
+
+  let controller = Cc["@mozilla.org/autocomplete/controller;1"].
+                   getService(Ci.nsIAutoCompleteController);  
+  
+  // Make an AutoCompleteInput that uses our searches and confirms results.
+  let input = new AutoCompleteInput([ search.name ]);
+  input.textValue = aSearchString;
+
+  // Caret must be at the end for autofill to happen.
+  let strLen = aSearchString.length;
+  input.selectTextRange(strLen, strLen);
+  controller.input = input;
+  controller.startSearch(aSearchString);
+
+  input.onSearchComplete = function onSearchComplete() {
+    aOnCompleteCallback(controller);
+
+    // Clean up.
+    unregisterAutoCompleteSearch(search);
+    run_next_test();
+  };
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_popupSelectionVsDefaultCompleteValue.js
@@ -0,0 +1,72 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function AutoCompleteTypeAheadResult(aValues, aComments) {
+  this._values = aValues;
+  this._comments = aComments;
+  this.defaultIndex = 0;
+  this._typeAheadResult = true;
+}
+AutoCompleteTypeAheadResult.prototype = Object.create(AutoCompleteResultBase.prototype);
+
+function AutoCompleteResult(aValues, aComments) {
+  this._values = aValues;
+  this._comments = aComments;
+}
+AutoCompleteResult.prototype = Object.create(AutoCompleteResultBase.prototype);
+
+function AutoCompleteInput(aSearches) {
+  this.searches = aSearches;
+  this.popupOpen = true;
+  this.completeDefaultIndex = true;
+  this.completeSelectedIndex = true;
+}
+AutoCompleteInput.prototype = Object.create(AutoCompleteInputBase.prototype);
+
+function run_test() {
+  run_next_test();
+}
+
+add_test(function test_handleEnter() {
+  doSearch("moz", function(aController) {
+    do_check_eq(aController.input.textValue, "mozilla.com");
+    aController.handleEnter(true);
+    do_check_eq(aController.input.textValue, "mozilla.org");
+  });
+});
+
+function doSearch(aSearchString, aOnCompleteCallback) {
+  let typeAheadSearch = new AutoCompleteSearchBase(
+    "typeAheadSearch",
+    new AutoCompleteTypeAheadResult([ "mozilla.com" ], [ "http://www.mozilla.com" ])
+  );
+  registerAutoCompleteSearch(typeAheadSearch);
+
+  let search = new AutoCompleteSearchBase(
+    "search",
+    new AutoCompleteResult([ "mozilla.org" ], [ "http://www.mozilla.org" ])
+  );
+  registerAutoCompleteSearch(search);
+
+  let controller = Cc["@mozilla.org/autocomplete/controller;1"].
+                   getService(Ci.nsIAutoCompleteController);
+
+  // Make an AutoCompleteInput that uses our searches and confirms results.
+  let input = new AutoCompleteInput([ typeAheadSearch.name, search.name ]);
+  input.textValue = aSearchString;
+
+  // Caret must be at the end for autofill to happen.
+  let strLen = aSearchString.length;
+  input.selectTextRange(strLen, strLen);
+  controller.input = input;
+  controller.startSearch(aSearchString);
+
+  input.onSearchComplete = function onSearchComplete() {
+    aOnCompleteCallback(controller);
+
+    // Clean up.
+    unregisterAutoCompleteSearch(search);
+    run_next_test();
+  };
+}
--- a/toolkit/components/autocomplete/tests/unit/xpcshell.ini
+++ b/toolkit/components/autocomplete/tests/unit/xpcshell.ini
@@ -6,12 +6,14 @@ tail =
 [test_378079.js]
 [test_393191.js]
 [test_440866.js]
 [test_463023.js]
 [test_660156.js]
 [test_autocomplete_multiple.js]
 [test_badDefaultIndex.js]
 [test_completeDefaultIndex_casing.js]
+[test_finalDefaultCompleteValue.js]
 [test_hiddenResult.js]
 [test_immediate_search.js]
+[test_popupSelectionVsDefaultCompleteValue.js]
 [test_previousResult.js]
 [test_stopSearch.js]
--- a/toolkit/components/places/tests/inline/test_casing.js
+++ b/toolkit/components/places/tests/inline/test_casing.js
@@ -18,17 +18,17 @@ add_autocomplete_test([
   function () {
     addBookmark({ url: "http://mozilla.org/test/" });
   }
 ]);
 
 add_autocomplete_test([
   "Searching for cased entry 3",
   "mozilla.org/T",
-  { autoFilled: "mozilla.org/Test/", completed: "mozilla.org/Test/" },
+  { autoFilled: "mozilla.org/Test/", completed: "http://mozilla.org/Test/" },
   function () {
     addBookmark({ url: "http://mozilla.org/Test/" });
   }
 ]);
 
 add_autocomplete_test([
   "Searching for cased entry 4",
   "mOzilla.org/t",
@@ -36,17 +36,17 @@ add_autocomplete_test([
   function () {
     addBookmark({ url: "http://mozilla.org/Test/" });
   },
 ]);
 
 add_autocomplete_test([
   "Searching for cased entry 5",
   "mOzilla.org/T",
-  { autoFilled: "mOzilla.org/Test/", completed: "mozilla.org/Test/" },
+  { autoFilled: "mOzilla.org/Test/", completed: "http://mozilla.org/Test/" },
   function () {
     addBookmark({ url: "http://mozilla.org/Test/" });
   },
 ]);
 
 add_autocomplete_test([
   "Searching for untrimmed cased entry",
   "http://mOz",
@@ -54,17 +54,17 @@ add_autocomplete_test([
   function () {
     addBookmark({ url: "http://mozilla.org/Test/" });
   },
 ]);
 
 add_autocomplete_test([
   "Searching for untrimmed cased entry with www",
   "http://www.mOz",
-  { autoFilled: "http://www.mOzilla.org/", completed: "http://www.mozilla.org/" },
+  { autoFilled: "http://www.mOzilla.org/", completed: "www.mozilla.org/" },
   function () {
     addBookmark({ url: "http://www.mozilla.org/Test/" });
   },
 ]);
 
 add_autocomplete_test([
   "Searching for untrimmed cased entry with path",
   "http://mOzilla.org/t",
new file mode 100644
--- /dev/null
+++ b/toolkit/components/places/tests/inline/test_trimming.js
@@ -0,0 +1,134 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+add_autocomplete_test([
+  "Searching for untrimmed https://www entry",
+  "mo",
+  { autoFilled: "mozilla.org/", completed: "https://www.mozilla.org/" },
+  function () {
+    addBookmark({ url: "https://www.mozilla.org/test/" });
+  },
+]);
+
+add_autocomplete_test([
+  "Searching for untrimmed https://www entry with path",
+  "mozilla.org/t",
+  { autoFilled: "mozilla.org/test/", completed: "https://www.mozilla.org/test/" },
+  function () {
+    addBookmark({ url: "https://www.mozilla.org/test/" });
+  },
+]);
+
+add_autocomplete_test([
+  "Searching for untrimmed https:// entry",
+  "mo",
+  { autoFilled: "mozilla.org/", completed: "https://mozilla.org/" },
+  function () {
+    addBookmark({ url: "https://mozilla.org/test/" });
+  },
+]);
+
+add_autocomplete_test([
+  "Searching for untrimmed https:// entry with path",
+  "mozilla.org/t",
+  { autoFilled: "mozilla.org/test/", completed: "https://mozilla.org/test/" },
+  function () {
+    addBookmark({ url: "https://mozilla.org/test/" });
+  },
+]);
+
+add_autocomplete_test([
+  "Searching for untrimmed http://www entry",
+  "mo",
+  { autoFilled: "mozilla.org/", completed: "www.mozilla.org/" },
+  function () {
+    addBookmark({ url: "http://www.mozilla.org/test/" });
+  },
+]);
+
+add_autocomplete_test([
+  "Searching for untrimmed http://www entry with path",
+  "mozilla.org/t",
+  { autoFilled: "mozilla.org/test/", completed: "http://www.mozilla.org/test/" },
+  function () {
+    addBookmark({ url: "http://www.mozilla.org/test/" });
+  },
+]);
+
+add_autocomplete_test([
+  "Searching for untrimmed ftp:// entry",
+  "mo",
+  { autoFilled: "mozilla.org/", completed: "ftp://mozilla.org/" },
+  function () {
+    addBookmark({ url: "ftp://mozilla.org/test/" });
+  },
+]);
+
+add_autocomplete_test([
+  "Searching for untrimmed ftp:// entry with path",
+  "mozilla.org/t",
+  { autoFilled: "mozilla.org/test/", completed: "ftp://mozilla.org/test/" },
+  function () {
+    addBookmark({ url: "ftp://mozilla.org/test/" });
+  },
+]);
+
+add_autocomplete_test([
+  "Ensuring correct priority 1",
+  "mo",
+  { autoFilled: "mozilla.org/", completed: "https://www.mozilla.org/" },
+  function () {
+    addBookmark({ url: "https://www.mozilla.org/test/" });
+    addBookmark({ url: "https://mozilla.org/test/" });
+    addBookmark({ url: "ftp://mozilla.org/test/" });
+    addBookmark({ url: "http://www.mozilla.org/test/" });
+    addBookmark({ url: "http://mozilla.org/test/" });
+  },
+]);
+
+add_autocomplete_test([
+  "Ensuring correct priority 2",
+  "mo",
+  { autoFilled: "mozilla.org/", completed: "https://mozilla.org/" },
+  function () {
+    addBookmark({ url: "https://mozilla.org/test/" });
+    addBookmark({ url: "ftp://mozilla.org/test/" });
+    addBookmark({ url: "http://www.mozilla.org/test/" });
+    addBookmark({ url: "http://mozilla.org/test/" });
+  },
+]);
+
+add_autocomplete_test([
+  "Ensuring correct priority 3",
+  "mo",
+  { autoFilled: "mozilla.org/", completed: "ftp://mozilla.org/" },
+  function () {
+    addBookmark({ url: "ftp://mozilla.org/test/" });
+    addBookmark({ url: "http://www.mozilla.org/test/" });
+    addBookmark({ url: "http://mozilla.org/test/" });
+  },
+]);
+
+add_autocomplete_test([
+  "Ensuring correct priority 4",
+  "mo",
+  { autoFilled: "mozilla.org/", completed: "www.mozilla.org/" },
+  function () {
+    addBookmark({ url: "http://www.mozilla.org/test/" });
+    addBookmark({ url: "http://mozilla.org/test/" });
+  },
+]);
+
+add_autocomplete_test([
+  "Ensuring longer domain can't match",
+  "mo",
+  { autoFilled: "mozilla.co/", completed: "mozilla.co/" },
+  function () {
+    // The .co should be preferred, but should not get the https from the .com.
+    // The .co domain must be added later to activate the trigger bug.
+    addBookmark({ url: "https://mozilla.com/" });
+    addBookmark({ url: "http://mozilla.co/" });
+    addBookmark({ url: "http://mozilla.co/" });
+  },
+]);
--- a/toolkit/components/places/tests/inline/xpcshell.ini
+++ b/toolkit/components/places/tests/inline/xpcshell.ini
@@ -1,10 +1,11 @@
 [DEFAULT]
 head = head_autocomplete.js
 tail = 
 
 [test_autocomplete_functional.js]
 [test_casing.js]
 [test_do_not_trim.js]
 [test_keywords.js]
+[test_trimming.js]
 [test_typed.js]
 [test_zero_frecency.js]