Restore typeAheadResult support in autocomplete

This commit is contained in:
NTD 2018-04-17 07:31:18 -04:00 committed by Roy Tam
commit 6106e49360
11 changed files with 273 additions and 52 deletions

View file

@ -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) {
// 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;
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);
// 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);
// 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) {
// 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;
}
}
uint32_t delta = totalMatchCount - oldRowCount;
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);
// 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);
// 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();
// 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

View file

@ -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,

View file

@ -38,6 +38,8 @@ protected:
int32_t mDefaultIndex;
uint32_t mSearchResult;
bool mTypeAheadResult;
nsCOMPtr<nsIAutoCompleteSimpleResultListener> mListener;
};

View file

@ -49,6 +49,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
*/

View file

@ -41,6 +41,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.

View file

@ -85,6 +85,11 @@ AutoCompleteResultBase.prototype = {
defaultIndex: -1,
_typeAheadResult: false,
get typeAheadResult() {
return this._typeAheadResult;
},
get matchCount() {
return this._values.length;
},

View file

@ -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();
};
}

View file

@ -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();
};
}

View file

@ -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]

View file

@ -133,6 +133,13 @@ NS_IMETHODIMP nsFileResult::GetMatchCount(uint32_t *aMatchCount)
return NS_OK;
}
NS_IMETHODIMP nsFileResult::GetTypeAheadResult(bool *aTypeAheadResult)
{
NS_ENSURE_ARG_POINTER(aTypeAheadResult);
*aTypeAheadResult = false;
return NS_OK;
}
NS_IMETHODIMP nsFileResult::GetValueAt(int32_t index, nsAString & aValue)
{
aValue = mValues[index];

View file

@ -528,6 +528,10 @@ TagAutoCompleteResult.prototype = {
return this._results.length;
},
get typeAheadResult() {
return false;
},
/**
* Get the value of the result at the given index
*/