diff --git a/toolkit/components/autocomplete/nsAutoCompleteController.cpp b/toolkit/components/autocomplete/nsAutoCompleteController.cpp index 894d6857643..c532eb18b67 100644 --- a/toolkit/components/autocomplete/nsAutoCompleteController.cpp +++ b/toolkit/components/autocomplete/nsAutoCompleteController.cpp @@ -1453,6 +1453,15 @@ nsAutoCompleteController::GetDefaultCompleteValue(PRInt32 aResultIndex, 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 && diff --git a/toolkit/components/autocomplete/tests/unit/test_badDefaultIndex.js b/toolkit/components/autocomplete/tests/unit/test_badDefaultIndex.js new file mode 100644 index 00000000000..0ccbae319f1 --- /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(); +} diff --git a/toolkit/components/autocomplete/tests/unit/xpcshell.ini b/toolkit/components/autocomplete/tests/unit/xpcshell.ini index befeddec6a4..af764e9ba46 100644 --- a/toolkit/components/autocomplete/tests/unit/xpcshell.ini +++ b/toolkit/components/autocomplete/tests/unit/xpcshell.ini @@ -9,6 +9,7 @@ tail = [test_463023.js] [test_660156.js] [test_autocomplete_multiple.js] +[test_badDefaultIndex.js] [test_hiddenResult.js] [test_previousResult.js] [test_stopSearch.js] diff --git a/toolkit/components/places/nsTaggingService.js b/toolkit/components/places/nsTaggingService.js index b51a0cd3159..77610dd7667 100644 --- a/toolkit/components/places/nsTaggingService.js +++ b/toolkit/components/places/nsTaggingService.js @@ -535,6 +535,8 @@ TagAutoCompleteResult.prototype = { return this._results.length; }, + get typeAheadResult() false, + /** * Get the value of the result at the given index */ @@ -673,8 +675,11 @@ TagAutoCompleteSearch.prototype = { */ } - 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; }