diff options
Diffstat (limited to 'toolkit/components/autocomplete')
9 files changed, 262 insertions, 52 deletions
diff --git a/toolkit/components/autocomplete/nsAutoCompleteController.cpp b/toolkit/components/autocomplete/nsAutoCompleteController.cpp index 5d69ea1a3..9ca382fe5 100644 --- a/toolkit/components/autocomplete/nsAutoCompleteController.cpp +++ b/toolkit/components/autocomplete/nsAutoCompleteController.cpp @@ -1637,58 +1637,72 @@ nsAutoCompleteController::ProcessResult(int32_t aSearchIndex, nsIAutoCompleteRes MOZ_ASSERT(mResults.Count() >= aSearchIndex + 1, "aSearchIndex should always be valid for mResults"); - uint32_t oldRowCount = mRowCount; - // If the search failed, increase the match count to include the error - // description. - if (searchResult == nsIAutoCompleteResult::RESULT_FAILURE) { - nsAutoString error; - aResult->GetErrorDescription(error); - if (!error.IsEmpty()) { - ++mRowCount; - if (mTree) { - mTree->RowCountChanged(oldRowCount, 1); + bool isTypeAheadResult = false; + aResult->GetTypeAheadResult(&isTypeAheadResult); + + if (!isTypeAheadResult) { + uint32_t oldRowCount = mRowCount; + // If the search failed, increase the match count to include the error + // description. + if (searchResult == nsIAutoCompleteResult::RESULT_FAILURE) { + nsAutoString error; + aResult->GetErrorDescription(error); + if (!error.IsEmpty()) { + ++mRowCount; + if (mTree) { + mTree->RowCountChanged(oldRowCount, 1); + } } - } - } else if (searchResult == nsIAutoCompleteResult::RESULT_SUCCESS || - searchResult == nsIAutoCompleteResult::RESULT_SUCCESS_ONGOING) { - // Increase the match count for all matches in this result. - uint32_t totalMatchCount = 0; - for (uint32_t i = 0; i < mResults.Length(); i++) { - nsIAutoCompleteResult* result = mResults.SafeObjectAt(i); - if (result) { - uint32_t matchCount = 0; - result->GetMatchCount(&matchCount); - totalMatchCount += matchCount; + } else if (searchResult == nsIAutoCompleteResult::RESULT_SUCCESS || + searchResult == nsIAutoCompleteResult::RESULT_SUCCESS_ONGOING) { + // Increase the match count for all matches in this result. + uint32_t totalMatchCount = 0; + for (uint32_t i = 0; i < mResults.Length(); i++) { + nsIAutoCompleteResult* result = mResults.SafeObjectAt(i); + if (result) { + // not all results implement this, so it can likely fail. + bool typeAhead = false; + result->GetTypeAheadResult(&typeAhead); + if (!typeAhead) { + uint32_t matchCount = 0; + result->GetMatchCount(&matchCount); + totalMatchCount += matchCount; + } + } } - } - uint32_t delta = totalMatchCount - oldRowCount; + uint32_t delta = totalMatchCount - oldRowCount; - mRowCount += delta; - if (mTree) { - mTree->RowCountChanged(oldRowCount, delta); + mRowCount += delta; + if (mTree) { + mTree->RowCountChanged(oldRowCount, delta); + } } - } - // Try to autocomplete the default index for this search. - // Do this before invalidating so the binding knows about it. - CompleteDefaultIndex(aSearchIndex); + // Try to autocomplete the default index for this search. + // Do this before invalidating so the binding knows about it. + CompleteDefaultIndex(aSearchIndex); - // Refresh the popup view to display the new search results - nsCOMPtr<nsIAutoCompletePopup> popup; - input->GetPopup(getter_AddRefs(popup)); - NS_ENSURE_TRUE(popup != nullptr, NS_ERROR_FAILURE); - popup->Invalidate(nsIAutoCompletePopup::INVALIDATE_REASON_NEW_RESULT); + // Refresh the popup view to display the new search results + nsCOMPtr<nsIAutoCompletePopup> popup; + input->GetPopup(getter_AddRefs(popup)); + NS_ENSURE_TRUE(popup != nullptr, NS_ERROR_FAILURE); + popup->Invalidate(nsIAutoCompletePopup::INVALIDATE_REASON_NEW_RESULT); - uint32_t minResults; - input->GetMinResultsForPopup(&minResults); + uint32_t minResults; + input->GetMinResultsForPopup(&minResults); - // Make sure the popup is open, if necessary, since we now have at least one - // search result ready to display. Don't force the popup closed if we might - // get results in the future to avoid unnecessarily canceling searches. - if (mRowCount || !minResults) { - OpenPopup(); - } else if (mSearchesOngoing == 0) { - ClosePopup(); + // Make sure the popup is open, if necessary, since we now have at least one + // search result ready to display. Don't force the popup closed if we might + // get results in the future to avoid unnecessarily canceling searches. + if (mRowCount || !minResults) { + OpenPopup(); + } else if (mSearchesOngoing == 0) { + ClosePopup(); + } + } else if (searchResult == nsIAutoCompleteResult::RESULT_SUCCESS || + searchResult == nsIAutoCompleteResult::RESULT_SUCCESS_ONGOING) { + // Try to autocomplete the default index for this search. + CompleteDefaultIndex(aSearchIndex); } return NS_OK; @@ -2033,14 +2047,20 @@ nsAutoCompleteController::RowIndexToSearch(int32_t aRowIndex, int32_t *aSearchIn uint32_t rowCount = 0; - uint16_t searchResult; - result->GetSearchResult(&searchResult); + // Skip past the result completely if it is marked as hidden + bool isTypeAheadResult = false; + result->GetTypeAheadResult(&isTypeAheadResult); - // Find out how many results were provided by the - // current nsIAutoCompleteSearch. - if (searchResult == nsIAutoCompleteResult::RESULT_SUCCESS || - searchResult == nsIAutoCompleteResult::RESULT_SUCCESS_ONGOING) { - result->GetMatchCount(&rowCount); + if (!isTypeAheadResult) { + uint16_t searchResult; + result->GetSearchResult(&searchResult); + + // Find out how many results were provided by the + // current nsIAutoCompleteSearch. + if (searchResult == nsIAutoCompleteResult::RESULT_SUCCESS || + searchResult == nsIAutoCompleteResult::RESULT_SUCCESS_ONGOING) { + result->GetMatchCount(&rowCount); + } } // If the given row index is within the results range diff --git a/toolkit/components/autocomplete/nsAutoCompleteSimpleResult.cpp b/toolkit/components/autocomplete/nsAutoCompleteSimpleResult.cpp index 9fd2c0022..683ac462a 100644 --- a/toolkit/components/autocomplete/nsAutoCompleteSimpleResult.cpp +++ b/toolkit/components/autocomplete/nsAutoCompleteSimpleResult.cpp @@ -43,7 +43,8 @@ struct AutoCompleteSimpleResultMatch nsAutoCompleteSimpleResult::nsAutoCompleteSimpleResult() : mDefaultIndex(-1), - mSearchResult(RESULT_NOMATCH) + mSearchResult(RESULT_NOMATCH), + mTypeAheadResult(false) { } @@ -66,6 +67,12 @@ nsAutoCompleteSimpleResult::AppendResult(nsIAutoCompleteResult* aResult) mErrorDescription = errorDescription; } + bool typeAheadResult = false; + if (NS_SUCCEEDED(aResult->GetTypeAheadResult(&typeAheadResult)) && + typeAheadResult) { + mTypeAheadResult = typeAheadResult; + } + int32_t defaultIndex = -1; if (NS_SUCCEEDED(aResult->GetDefaultIndex(&defaultIndex)) && defaultIndex >= 0) { @@ -166,6 +173,20 @@ nsAutoCompleteSimpleResult::SetErrorDescription( return NS_OK; } +// typeAheadResult +NS_IMETHODIMP +nsAutoCompleteSimpleResult::GetTypeAheadResult(bool *aTypeAheadResult) +{ + *aTypeAheadResult = mTypeAheadResult; + return NS_OK; +} +NS_IMETHODIMP +nsAutoCompleteSimpleResult::SetTypeAheadResult(bool aTypeAheadResult) +{ + mTypeAheadResult = aTypeAheadResult; + return NS_OK; +} + NS_IMETHODIMP nsAutoCompleteSimpleResult::InsertMatchAt(int32_t aIndex, const nsAString& aValue, diff --git a/toolkit/components/autocomplete/nsAutoCompleteSimpleResult.h b/toolkit/components/autocomplete/nsAutoCompleteSimpleResult.h index 28968aa57..61ee542e4 100644 --- a/toolkit/components/autocomplete/nsAutoCompleteSimpleResult.h +++ b/toolkit/components/autocomplete/nsAutoCompleteSimpleResult.h @@ -38,6 +38,8 @@ protected: int32_t mDefaultIndex; uint32_t mSearchResult; + bool mTypeAheadResult; + nsCOMPtr<nsIAutoCompleteSimpleResultListener> mListener; }; diff --git a/toolkit/components/autocomplete/nsIAutoCompleteResult.idl b/toolkit/components/autocomplete/nsIAutoCompleteResult.idl index c719d9427..9ae22ade7 100644 --- a/toolkit/components/autocomplete/nsIAutoCompleteResult.idl +++ b/toolkit/components/autocomplete/nsIAutoCompleteResult.idl @@ -50,6 +50,13 @@ interface nsIAutoCompleteResult : nsISupports readonly attribute unsigned long matchCount; /** + * If true, the results will not be displayed in the popup. However, + * if a default index is specified, the default item will still be + * completed in the input. + */ + readonly attribute boolean typeAheadResult; + + /** * Get the value of the result at the given index */ AString getValueAt(in long index); diff --git a/toolkit/components/autocomplete/nsIAutoCompleteSimpleResult.idl b/toolkit/components/autocomplete/nsIAutoCompleteSimpleResult.idl index 5e92e037a..6a8827ab8 100644 --- a/toolkit/components/autocomplete/nsIAutoCompleteSimpleResult.idl +++ b/toolkit/components/autocomplete/nsIAutoCompleteSimpleResult.idl @@ -42,6 +42,12 @@ interface nsIAutoCompleteSimpleResult : nsIAutoCompleteResult void setSearchResult(in unsigned short aSearchResult); /** + * A writer for the readonly attribute 'typeAheadResult', typically set + * because a result is only intended for type-ahead completion. + */ + void setTypeAheadResult(in boolean aHidden); + + /** * Inserts a match consisting of the given value, comment, image, style and * the value to use for defaultIndex completion at a given position. * @param aIndex diff --git a/toolkit/components/autocomplete/tests/unit/head_autocomplete.js b/toolkit/components/autocomplete/tests/unit/head_autocomplete.js index 1443879f0..5a458bdf4 100644 --- a/toolkit/components/autocomplete/tests/unit/head_autocomplete.js +++ b/toolkit/components/autocomplete/tests/unit/head_autocomplete.js @@ -85,6 +85,11 @@ AutoCompleteResultBase.prototype = { defaultIndex: -1, + _typeAheadResult: false, + get typeAheadResult() { + return this._typeAheadResult; + }, + get matchCount() { return this._values.length; }, diff --git a/toolkit/components/autocomplete/tests/unit/test_hiddenResult.js b/toolkit/components/autocomplete/tests/unit/test_hiddenResult.js new file mode 100644 index 000000000..8e2485716 --- /dev/null +++ b/toolkit/components/autocomplete/tests/unit/test_hiddenResult.js @@ -0,0 +1,76 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +function AutoCompleteResult(aValues) { + this._values = aValues; + this.defaultIndex = -1; + this._typeAheadResult = false; +} +AutoCompleteResult.prototype = Object.create(AutoCompleteResultBase.prototype); + +function AutoCompleteTypeAheadResult(aValues) { + this._values = aValues; + this.defaultIndex = 0; + this._typeAheadResult = true; +} +AutoCompleteTypeAheadResult.prototype = Object.create(AutoCompleteResultBase.prototype); + + +/** + * Test AutoComplete with multiple AutoCompleteSearch sources, with one of them + * being hidden from the popup, but can still do typeahead completion. + */ +function run_test() { + do_test_pending(); + + var inputStr = "moz"; + + // Type ahead result + var searchTypeAhead = new AutoCompleteSearchBase("search1", + new AutoCompleteTypeAheadResult(["mozillaTest1"])); + // Regular result + var searchNormal = new AutoCompleteSearchBase("search2", + new AutoCompleteResult(["mozillaTest2"])); + + // Register searches so AutoCompleteController can find them + registerAutoCompleteSearch(searchNormal); + registerAutoCompleteSearch(searchTypeAhead); + + // Make an AutoCompleteInput that uses our searches + // and confirms results on search complete. + var input = new AutoCompleteInputBase([searchTypeAhead.name, searchNormal.name]); + input.completeDefaultIndex = true; + input.textValue = inputStr; + + // Caret must be at the end. Autofill doesn't happen unless you're typing + // characters at the end. + var strLen = inputStr.length; + input.selectTextRange(strLen, strLen); + do_check_eq(input.selectionStart, strLen); + do_check_eq(input.selectionEnd, strLen); + + var controller = Cc["@mozilla.org/autocomplete/controller;1"]. + getService(Ci.nsIAutoCompleteController); + + controller.input = input; + controller.startSearch(inputStr); + + input.onSearchComplete = function() { + // Hidden results should still be able to do inline autocomplete + do_check_eq(input.textValue, "mozillaTest1"); + + // Now, let's fill the textbox with the first result of the popup. + // The first search is marked as hidden, so we must always get the + // second search. + controller.handleEnter(true); + do_check_eq(input.textValue, "mozillaTest2"); + + // Only one item in the popup. + do_check_eq(controller.matchCount, 1); + + // Unregister searches + unregisterAutoCompleteSearch(searchNormal); + unregisterAutoCompleteSearch(searchTypeAhead); + do_test_finished(); + }; +} diff --git a/toolkit/components/autocomplete/tests/unit/test_popupSelectionVsDefaultCompleteValue.js b/toolkit/components/autocomplete/tests/unit/test_popupSelectionVsDefaultCompleteValue.js new file mode 100644 index 000000000..fb4153355 --- /dev/null +++ b/toolkit/components/autocomplete/tests/unit/test_popupSelectionVsDefaultCompleteValue.js @@ -0,0 +1,71 @@ +/* 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, aFinalCompleteValues) { + this._values = aValues; + this._finalCompleteValues = aFinalCompleteValues; + this.defaultIndex = 0; + this._typeAheadResult = true; +} +AutoCompleteTypeAheadResult.prototype = Object.create(AutoCompleteResultBase.prototype); + +function AutoCompleteResult(aValues) { + this._values = aValues; +} +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" ]) + ); + 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(); + }; +} diff --git a/toolkit/components/autocomplete/tests/unit/xpcshell.ini b/toolkit/components/autocomplete/tests/unit/xpcshell.ini index 4d193965c..daf89db17 100644 --- a/toolkit/components/autocomplete/tests/unit/xpcshell.ini +++ b/toolkit/components/autocomplete/tests/unit/xpcshell.ini @@ -18,7 +18,9 @@ tail = [test_finalCompleteValue_forceComplete.js] [test_finalCompleteValueSelectedIndex.js] [test_finalDefaultCompleteValue.js] +[test_hiddenResult.js] [test_immediate_search.js] [test_insertMatchAt.js] +[test_popupSelectionVsDefaultCompleteValue.js] [test_previousResult.js] [test_stopSearch.js] |