Bug 1837812 - Merging InputListAutoComplete.sys.mjs into FormAutoComplete.sys.mjs r=issammani
Differential Revision: https://phabricator.services.mozilla.com/D180612
This commit is contained in:
@@ -3,6 +3,14 @@
|
||||
* 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/. */
|
||||
|
||||
// This is ugly: there are two FormAutoCompleteResult classes in the
|
||||
// tree, one in a module and one in this file. Datalist results need to
|
||||
// use the one defined in the module but the rest of this file assumes
|
||||
// that we use the one defined here. To get around that, we explicitly
|
||||
// import the module here, out of the way of the other uses of
|
||||
// FormAutoCompleteResult.
|
||||
import { FormAutoCompleteResult as DataListAutoCompleteResult } from "resource://gre/modules/nsFormAutoCompleteResult.sys.mjs";
|
||||
|
||||
function isAutocompleteDisabled(aField) {
|
||||
if (!aField) {
|
||||
return false;
|
||||
@@ -341,7 +349,7 @@ export class FormAutoComplete {
|
||||
* aUntrimmedSearchString -- current value of the input
|
||||
* aField -- HTMLInputElement being autocompleted (may be null if from chrome)
|
||||
* aPreviousResult -- previous search result, if any.
|
||||
* aDatalistResult -- results from list=datalist for aField.
|
||||
* aAddDataList -- add results from list=datalist for aField.
|
||||
* aListener -- nsIFormAutoCompleteObserver that listens for the nsIAutoCompleteResult
|
||||
* that may be returned asynchronously.
|
||||
*/
|
||||
@@ -350,7 +358,7 @@ export class FormAutoComplete {
|
||||
aUntrimmedSearchString,
|
||||
aField,
|
||||
aPreviousResult,
|
||||
aDatalistResult,
|
||||
aAddDataList,
|
||||
aListener
|
||||
) {
|
||||
// Guard against void DOM strings filtering into this code.
|
||||
@@ -370,9 +378,13 @@ export class FormAutoComplete {
|
||||
aListener?.onSearchCompletion(result);
|
||||
}
|
||||
|
||||
const dataListResult = aAddDataList
|
||||
? this.getDataListResult(aField, aUntrimmedSearchString)
|
||||
: null;
|
||||
|
||||
// If we have datalist results, they become our "empty" result.
|
||||
const emptyResult =
|
||||
aDatalistResult ||
|
||||
dataListResult ||
|
||||
new FormAutoCompleteResult(
|
||||
client,
|
||||
[],
|
||||
@@ -487,7 +499,7 @@ export class FormAutoComplete {
|
||||
this.log("Creating new autocomplete search result.");
|
||||
|
||||
// Start with an empty list.
|
||||
let result = aDatalistResult
|
||||
let result = dataListResult
|
||||
? new FormAutoCompleteResult(
|
||||
client,
|
||||
[],
|
||||
@@ -505,8 +517,8 @@ export class FormAutoComplete {
|
||||
result.entries = aEntries;
|
||||
}
|
||||
|
||||
if (aDatalistResult?.matchCount > 0) {
|
||||
result = this.mergeResults(result, aDatalistResult);
|
||||
if (dataListResult?.matchCount > 0) {
|
||||
result = this.mergeResults(result, dataListResult);
|
||||
}
|
||||
|
||||
maybeNotifyListener(result);
|
||||
@@ -521,6 +533,50 @@ export class FormAutoComplete {
|
||||
}
|
||||
}
|
||||
|
||||
getDataListResult(aField, aUntrimmedSearchString) {
|
||||
const items = this.getDataListSuggestions(aField);
|
||||
const searchResult = items.length
|
||||
? Ci.nsIAutoCompleteResult.RESULT_SUCCESS
|
||||
: Ci.nsIAutoCompleteResult.RESULT_NOMATCH;
|
||||
const defaultIndex = items.length ? 0 : -1;
|
||||
|
||||
return new DataListAutoCompleteResult(
|
||||
aUntrimmedSearchString,
|
||||
searchResult,
|
||||
defaultIndex,
|
||||
"",
|
||||
items,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
getDataListSuggestions(aField) {
|
||||
const items = [];
|
||||
|
||||
if (!aField?.list) {
|
||||
return items;
|
||||
}
|
||||
|
||||
const upperFieldValue = aField.value.toUpperCase();
|
||||
|
||||
for (const option of aField.list.options) {
|
||||
const label = option.label || option.text || option.value || "";
|
||||
|
||||
if (!label.toUpperCase().includes(upperFieldValue)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
items.push({
|
||||
label,
|
||||
value: option.value,
|
||||
comment: "",
|
||||
removable: false,
|
||||
});
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
mergeResults(historyResult, datalistResult) {
|
||||
const items = datalistResult.wrappedJSObject._items;
|
||||
|
||||
@@ -554,16 +610,7 @@ export class FormAutoComplete {
|
||||
entry => !isInArray(entry.text, items, "value")
|
||||
);
|
||||
|
||||
// This is ugly: there are two FormAutoCompleteResult classes in the
|
||||
// tree, one in a module and one in this file. Datalist results need to
|
||||
// use the one defined in the module but the rest of this file assumes
|
||||
// that we use the one defined here. To get around that, we explicitly
|
||||
// import the module here, out of the way of the other uses of
|
||||
// FormAutoCompleteResult.
|
||||
const { FormAutoCompleteResult } = ChromeUtils.importESModule(
|
||||
"resource://gre/modules/nsFormAutoCompleteResult.sys.mjs"
|
||||
);
|
||||
return new FormAutoCompleteResult(
|
||||
return new DataListAutoCompleteResult(
|
||||
datalistResult.searchString,
|
||||
Ci.nsIAutoCompleteResult.RESULT_SUCCESS,
|
||||
0,
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
/* 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/. */
|
||||
|
||||
import { FormAutoCompleteResult } from "resource://gre/modules/nsFormAutoCompleteResult.sys.mjs";
|
||||
|
||||
export class InputListAutoComplete {
|
||||
classID = Components.ID("{bf1e01d0-953e-11df-981c-0800200c9a66}");
|
||||
QueryInterface = ChromeUtils.generateQI(["nsIInputListAutoComplete"]);
|
||||
|
||||
autoCompleteSearch(aUntrimmedSearchString, aField) {
|
||||
const items = this.getListSuggestions(aField);
|
||||
const searchResult = items.length
|
||||
? Ci.nsIAutoCompleteResult.RESULT_SUCCESS
|
||||
: Ci.nsIAutoCompleteResult.RESULT_NOMATCH;
|
||||
const defaultIndex = items.length ? 0 : -1;
|
||||
|
||||
return new FormAutoCompleteResult(
|
||||
aUntrimmedSearchString,
|
||||
searchResult,
|
||||
defaultIndex,
|
||||
"",
|
||||
items,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
getListSuggestions(aField) {
|
||||
const items = [];
|
||||
|
||||
if (!aField?.list) {
|
||||
return items;
|
||||
}
|
||||
|
||||
const lowerFieldValue = aField.value.toLowerCase();
|
||||
|
||||
for (const option of aField.list.options) {
|
||||
const label = option.label || option.text || option.value || "";
|
||||
|
||||
if (!label.toLowerCase().includes(lowerFieldValue)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
items.push({
|
||||
label,
|
||||
value: option.value,
|
||||
comment: "",
|
||||
removable: false,
|
||||
});
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
}
|
||||
@@ -23,12 +23,6 @@ Classes = [
|
||||
'esModule': 'resource://gre/modules/FormAutoComplete.sys.mjs',
|
||||
'constructor': 'FormAutoComplete',
|
||||
},
|
||||
{
|
||||
'cid': '{bf1e01d0-953e-11df-981c-0800200c9a66}',
|
||||
'contract_ids': ['@mozilla.org/satchel/inputlist-autocomplete;1'],
|
||||
'esModule': 'resource://gre/modules/InputListAutoComplete.sys.mjs',
|
||||
'constructor': 'InputListAutoComplete',
|
||||
},
|
||||
{
|
||||
'cid': '{3a0012eb-007f-4bb8-aa81-a07385f77a25}',
|
||||
'contract_ids': ['@mozilla.org/satchel/form-history-startup;1'],
|
||||
|
||||
@@ -14,7 +14,6 @@ BROWSER_CHROME_MANIFESTS += ["test/browser/browser.ini"]
|
||||
XPIDL_SOURCES += [
|
||||
"nsIFormAutoComplete.idl",
|
||||
"nsIFormFillController.idl",
|
||||
"nsIInputListAutoComplete.idl",
|
||||
]
|
||||
|
||||
XPIDL_MODULE = "satchel"
|
||||
@@ -32,7 +31,6 @@ EXTRA_JS_MODULES += [
|
||||
"FormAutoComplete.sys.mjs",
|
||||
"FormHistory.sys.mjs",
|
||||
"FormHistoryStartup.sys.mjs",
|
||||
"InputListAutoComplete.sys.mjs",
|
||||
"nsFormAutoCompleteResult.sys.mjs",
|
||||
]
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
#include "mozilla/StaticPrefs_ui.h"
|
||||
#include "nsCRT.h"
|
||||
#include "nsIFormAutoComplete.h"
|
||||
#include "nsIInputListAutoComplete.h"
|
||||
#include "nsString.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsIAutoCompleteResult.h"
|
||||
@@ -732,11 +731,9 @@ nsFormFillController::StartSearch(const nsAString& aSearchString,
|
||||
MOZ_LOG(sLogger, LogLevel::Debug, ("StartSearch: non-login field"));
|
||||
mLastListener = aListener;
|
||||
|
||||
nsCOMPtr<nsIAutoCompleteResult> datalistResult;
|
||||
if (IsTextControl(mFocusedInput)) {
|
||||
rv = PerformInputListAutoComplete(aSearchString,
|
||||
getter_AddRefs(datalistResult));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
bool addDataList = IsTextControl(mFocusedInput);
|
||||
if (addDataList) {
|
||||
MaybeObserveDataListMutations();
|
||||
}
|
||||
|
||||
auto formAutoComplete = GetFormAutoComplete();
|
||||
@@ -744,27 +741,18 @@ nsFormFillController::StartSearch(const nsAString& aSearchString,
|
||||
|
||||
formAutoComplete->AutoCompleteSearchAsync(aSearchParam, aSearchString,
|
||||
mFocusedInput, aPreviousResult,
|
||||
datalistResult, this);
|
||||
addDataList, this);
|
||||
mLastFormAutoComplete = formAutoComplete;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult nsFormFillController::PerformInputListAutoComplete(
|
||||
const nsAString& aSearch, nsIAutoCompleteResult** aResult) {
|
||||
void nsFormFillController::MaybeObserveDataListMutations() {
|
||||
// If an <input> is focused, check if it has a list="<datalist>" which can
|
||||
// provide the list of suggestions.
|
||||
|
||||
MOZ_ASSERT(!mPwmgrInputs.Get(mFocusedInput));
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIInputListAutoComplete> inputListAutoComplete =
|
||||
do_GetService("@mozilla.org/satchel/inputlist-autocomplete;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = inputListAutoComplete->AutoCompleteSearch(aSearch, mFocusedInput,
|
||||
aResult);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (mFocusedInput) {
|
||||
Element* list = mFocusedInput->GetList();
|
||||
@@ -782,8 +770,6 @@ nsresult nsFormFillController::PerformInputListAutoComplete(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void nsFormFillController::RevalidateDataList() {
|
||||
|
||||
@@ -84,8 +84,7 @@ class nsFormFillController final : public nsIFormFillController,
|
||||
MOZ_CAN_RUN_SCRIPT
|
||||
void MaybeStartControllingInput(mozilla::dom::HTMLInputElement* aElement);
|
||||
|
||||
nsresult PerformInputListAutoComplete(const nsAString& aSearch,
|
||||
nsIAutoCompleteResult** aResult);
|
||||
void MaybeObserveDataListMutations();
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT void RevalidateDataList();
|
||||
bool RowMatch(nsFormHistory* aHistory, uint32_t aIndex,
|
||||
|
||||
@@ -20,7 +20,7 @@ interface nsIFormAutoComplete: nsISupports {
|
||||
in AString aSearchString,
|
||||
in HTMLInputElement aField,
|
||||
in nsIAutoCompleteResult aPreviousResult,
|
||||
in nsIAutoCompleteResult aDatalistResult,
|
||||
in bool aAddDatalist,
|
||||
in nsIFormAutoCompleteObserver aListener);
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
/* 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/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIAutoCompleteResult;
|
||||
|
||||
webidl HTMLInputElement;
|
||||
|
||||
[scriptable, uuid(0e33de3e-4faf-4a1a-b96e-24115b8bfd45)]
|
||||
interface nsIInputListAutoComplete: nsISupports {
|
||||
/**
|
||||
* Generate results for a form input autocomplete menu.
|
||||
*/
|
||||
nsIAutoCompleteResult autoCompleteSearch(in AString aSearchString,
|
||||
in HTMLInputElement aField);
|
||||
};
|
||||
@@ -101,7 +101,7 @@ add_test(function test1() {
|
||||
add_test(function test2() {
|
||||
do_log_info("Check search contains all entries");
|
||||
|
||||
fac.autoCompleteSearchAsync("field1", "", null, null, null, {
|
||||
fac.autoCompleteSearchAsync("field1", "", null, null, false, {
|
||||
onSearchCompletion(aResults) {
|
||||
Assert.equal(numRecords, aResults.matchCount);
|
||||
run_next_test();
|
||||
@@ -113,7 +113,7 @@ add_test(function test3() {
|
||||
do_log_info("Check search result ordering with empty search term");
|
||||
|
||||
let lastFound = numRecords;
|
||||
fac.autoCompleteSearchAsync("field1", "", null, null, null, {
|
||||
fac.autoCompleteSearchAsync("field1", "", null, null, false, {
|
||||
onSearchCompletion(aResults) {
|
||||
for (let i = 0; i < numRecords; i += 2) {
|
||||
Assert.equal(
|
||||
@@ -134,7 +134,7 @@ add_test(function test4() {
|
||||
do_log_info('Check search result ordering with "v"');
|
||||
|
||||
let lastFound = numRecords;
|
||||
fac.autoCompleteSearchAsync("field1", "v", null, null, null, {
|
||||
fac.autoCompleteSearchAsync("field1", "v", null, null, false, {
|
||||
onSearchCompletion(aResults) {
|
||||
for (let i = 0; i < numRecords; i += 2) {
|
||||
Assert.equal(
|
||||
@@ -176,7 +176,7 @@ add_test(function test6() {
|
||||
do_log_info("Check search result ordering with empty search term");
|
||||
|
||||
let lastFound = timesUsedSamples;
|
||||
fac.autoCompleteSearchAsync("field2", "", null, null, null, {
|
||||
fac.autoCompleteSearchAsync("field2", "", null, null, false, {
|
||||
onSearchCompletion(aResults) {
|
||||
for (let i = 0; i < timesUsedSamples; i++) {
|
||||
Assert.equal(
|
||||
@@ -193,7 +193,7 @@ add_test(function test7() {
|
||||
do_log_info('Check search result ordering with "v"');
|
||||
|
||||
let lastFound = timesUsedSamples;
|
||||
fac.autoCompleteSearchAsync("field2", "v", null, null, null, {
|
||||
fac.autoCompleteSearchAsync("field2", "v", null, null, false, {
|
||||
onSearchCompletion(aResults) {
|
||||
for (let i = 0; i < timesUsedSamples; i++) {
|
||||
Assert.equal(
|
||||
@@ -235,7 +235,7 @@ add_test(function test8() {
|
||||
});
|
||||
|
||||
add_test(function test9() {
|
||||
fac.autoCompleteSearchAsync("field3", "", null, null, null, {
|
||||
fac.autoCompleteSearchAsync("field3", "", null, null, false, {
|
||||
onSearchCompletion(aResults) {
|
||||
Assert.equal(aResults.getValueAt(0), "senior citizen");
|
||||
Assert.equal(aResults.getValueAt(1), "old but not senior");
|
||||
@@ -276,7 +276,7 @@ add_test(function test10() {
|
||||
});
|
||||
|
||||
add_test(function test11() {
|
||||
fac.autoCompleteSearchAsync("field4", "", null, null, null, {
|
||||
fac.autoCompleteSearchAsync("field4", "", null, null, false, {
|
||||
onSearchCompletion(aResults) {
|
||||
Assert.equal(aResults.matchCount, 3);
|
||||
run_next_test();
|
||||
@@ -310,7 +310,7 @@ add_test(function test_token_limit_DB() {
|
||||
"a b c d e f g h i j .",
|
||||
null,
|
||||
previousResult,
|
||||
null,
|
||||
false,
|
||||
{
|
||||
onSearchCompletion(aResults) {
|
||||
Assert.equal(
|
||||
@@ -346,7 +346,7 @@ add_test(function test_token_limit_DB() {
|
||||
"a b c d e f g h i j .",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
{
|
||||
onSearchCompletion(aResults) {
|
||||
Assert.equal(
|
||||
@@ -377,7 +377,7 @@ add_test(async function can_search_escape_marker() {
|
||||
"/* Further reading */ t",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
{
|
||||
onSearchCompletion(aResults) {
|
||||
Assert.equal(1, aResults.matchCount);
|
||||
|
||||
Reference in New Issue
Block a user