Bug 720066 - Tagging broken, cannot type in the tag field.
authorMarco Bonardo <mbonardo@mozilla.com>
Tue, 24 Jan 2012 20:37:44 +0100
changeset 85244 360bd1a6e72bbc745b9d0ed270a4763b5edf57b3
parent 85243 b7f926cfa8c8950b8d44a3b0d90099f7fa1c81d9
child 85245 ddba5171787231891f5d600841921342b088ee68
push id5245
push usermak77@bonardo.net
push dateTue, 24 Jan 2012 19:38:12 +0000
treeherdermozilla-inbound@0a116f325333 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs720066
milestone12.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 720066 - Tagging broken, cannot type in the tag field. r=gavin
toolkit/components/autocomplete/nsAutoCompleteController.cpp
toolkit/components/autocomplete/tests/unit/test_badDefaultIndex.js
toolkit/components/autocomplete/tests/unit/xpcshell.ini
toolkit/components/places/nsTaggingService.js
--- a/toolkit/components/autocomplete/nsAutoCompleteController.cpp
+++ b/toolkit/components/autocomplete/nsAutoCompleteController.cpp
@@ -1448,16 +1448,25 @@ nsAutoCompleteController::GetDefaultComp
     result->GetDefaultIndex(&defaultIndex);
   }
   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);
+  // Here defaultIndex is surely non-negative, so can be cast to unsigned.
+  if ((PRUint32)defaultIndex >= matchCount)
+    return NS_ERROR_FAILURE;
+
   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.
     // For example if we have result "Test", and user starts writing "tuna",
new file mode 100644
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_badDefaultIndex.js
@@ -0,0 +1,96 @@
+/* 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/. */
+
+/**
+ * A results that wants to defaultComplete to 0, but it has no matches,
+ * though it notifies SUCCESS to the controller.
+ */
+function AutoCompleteNoMatchResult() {
+  this.defaultIndex = 0;
+}
+AutoCompleteNoMatchResult.prototype = Object.create(AutoCompleteResultBase.prototype);
+
+/**
+ * A results that wants to defaultComplete to an index greater than the number
+ * of matches.
+ */
+function AutoCompleteBadIndexResult(aValues, aDefaultIndex) {
+  do_check_true(aValues.length <= aDefaultIndex);
+  this._values = aValues;
+  this.defaultIndex = aDefaultIndex;
+}
+AutoCompleteBadIndexResult.prototype = Object.create(AutoCompleteResultBase.prototype);
+
+add_test(function autocomplete_noMatch_success() {
+  const INPUT_STR = "moz";
+
+  let searchNoMatch =
+    new AutoCompleteSearchBase("searchNoMatch",
+                               new AutoCompleteNoMatchResult());
+  registerAutoCompleteSearch(searchNoMatch);
+
+  // Make an AutoCompleteInput that uses our search and confirms results.
+  let input = new AutoCompleteInputBase([searchNoMatch.name]);
+  input.completeDefaultIndex = true;
+  input.textValue = INPUT_STR;
+
+  // Caret must be at the end for autoFill to happen.
+  let strLen = INPUT_STR.length;
+  input.selectTextRange(strLen, strLen);
+  do_check_eq(input.selectionStart, strLen);
+  do_check_eq(input.selectionEnd, strLen);
+
+  let controller = Cc["@mozilla.org/autocomplete/controller;1"].
+                   getService(Ci.nsIAutoCompleteController);  
+  controller.input = input;
+  controller.startSearch(INPUT_STR);
+
+  input.onSearchComplete = function () {
+    // Should not try to autoFill to an empty value.
+    do_check_eq(input.textValue, "moz");
+
+    // Clean up.
+    unregisterAutoCompleteSearch(searchNoMatch);
+    run_next_test();
+  };
+});
+
+add_test(function autocomplete_defaultIndex_exceeds_matchCount() {
+  const INPUT_STR = "moz";
+
+  // Result returning matches, but a bad defaultIndex.
+  let searchBadIndex =
+    new AutoCompleteSearchBase("searchBadIndex",
+                               new AutoCompleteBadIndexResult(["mozillaTest"], 1));
+  registerAutoCompleteSearch(searchBadIndex);
+
+  // Make an AutoCompleteInput that uses our search and confirms results.
+  let input = new AutoCompleteInputBase([searchBadIndex.name]);
+  input.completeDefaultIndex = true;
+  input.textValue = INPUT_STR;
+
+  // Caret must be at the end for autoFill to happen.
+  let strLen = INPUT_STR.length;
+  input.selectTextRange(strLen, strLen);
+  do_check_eq(input.selectionStart, strLen);
+  do_check_eq(input.selectionEnd, strLen);
+
+  let controller = Cc["@mozilla.org/autocomplete/controller;1"].
+                   getService(Ci.nsIAutoCompleteController);  
+  controller.input = input;
+  controller.startSearch(INPUT_STR);
+
+  input.onSearchComplete = function () {
+    // Should not try to autoFill to an empty value.
+    do_check_eq(input.textValue, "moz");
+
+    // Clean up.
+    unregisterAutoCompleteSearch(searchBadIndex);
+    run_next_test();
+  };
+});
+
+function run_test() {
+  run_next_test();
+}
--- a/toolkit/components/autocomplete/tests/unit/xpcshell.ini
+++ b/toolkit/components/autocomplete/tests/unit/xpcshell.ini
@@ -4,11 +4,12 @@ tail =
 
 [test_330578.js]
 [test_378079.js]
 [test_393191.js]
 [test_440866.js]
 [test_463023.js]
 [test_660156.js]
 [test_autocomplete_multiple.js]
+[test_badDefaultIndex.js]
 [test_hiddenResult.js]
 [test_previousResult.js]
 [test_stopSearch.js]
--- a/toolkit/components/places/nsTaggingService.js
+++ b/toolkit/components/places/nsTaggingService.js
@@ -530,16 +530,18 @@ TagAutoCompleteResult.prototype = {
 
   /**
    * The number of matches
    */
   get matchCount() {
     return this._results.length;
   },
 
+  get typeAheadResult() false,
+
   /**
    * Get the value of the result at the given index
    */
   getValueAt: function PTACR_getValueAt(index) {
     return this._results[index];
   },
 
   getLabelAt: function PTACR_getLabelAt(index) {
@@ -668,18 +670,21 @@ TagAutoCompleteSearch.prototype = {
           var newResult = new TagAutoCompleteResult(searchString,
             Ci.nsIAutoCompleteResult.RESULT_SUCCESS_ONGOING, 0, "", results, comments);
           listener.onSearchResult(self, newResult);
           yield true;
         }
         */
       }
 
-      var newResult = new TagAutoCompleteResult(searchString,
-        Ci.nsIAutoCompleteResult.RESULT_SUCCESS, 0, "", results, comments);
+      let searchResult = results.length() > 0 ?
+                           Ci.nsIAutoCompleteResult.RESULT_SUCCESS :
+                           Ci.nsIAutoCompleteResult.RESULT_NOMATCH;
+      var newResult = new TagAutoCompleteResult(searchString, searchResult, 0,
+                                                "", results, comments);
       listener.onSearchResult(self, newResult);
       yield false;
     }
     
     // chunk the search results via the generator
     var gen = doSearch();
     while (gen.next());
     gen.close();