Bug 1398101 - Allow the FormAutofill fields with autocomplete="off" to proceed `startSearch`. r=MattN
MozReview-Commit-ID: 6pZUPY1Fray
--- a/browser/extensions/formautofill/FormAutofillContent.jsm
+++ b/browser/extensions/formautofill/FormAutofillContent.jsm
@@ -110,16 +110,22 @@ AutofillProfileAutoCompleteSearch.protot
// Fallback to form-history if ...
// - specified autofill feature is pref off.
// - no profile can fill the currently-focused input.
// - the current form has already been populated.
// - (address only) less than 3 inputs are covered by all saved fields in the storage.
if (!searchPermitted || !savedFieldNames.has(info.fieldName) || filledRecordGUID || (isAddressField &&
allFieldNames.filter(field => savedFieldNames.has(field)).length < FormAutofillUtils.AUTOFILL_FIELDS_THRESHOLD)) {
+ if (focusedInput.autocomplete == "off") {
+ // Create a dummy AddressResult as an empty search result.
+ let result = new AddressResult("", "", [], [], {});
+ listener.onSearchResult(this, result);
+ return;
+ }
let formHistory = Cc["@mozilla.org/autocomplete/search;1?name=form-history"]
.createInstance(Ci.nsIAutoCompleteSearch);
formHistory.startSearch(searchString, searchParam, previousResult, {
onSearchResult: (search, result) => {
listener.onSearchResult(this, result);
ProfileAutocomplete.setProfileAutoCompleteResult(result);
},
});
--- a/browser/extensions/formautofill/test/mochitest/formautofill_common.js
+++ b/browser/extensions/formautofill/test/mochitest/formautofill_common.js
@@ -2,16 +2,18 @@
/* import-globals-from ../../../../../toolkit/components/satchel/test/satchel_common.js */
/* eslint-disable no-unused-vars */
"use strict";
let formFillChromeScript;
let expectingPopup = null;
+const {FormAutofillUtils} = SpecialPowers.Cu.import("resource://formautofill/FormAutofillUtils.jsm");
+
async function sleep(ms = 500, reason = "Intentionally wait for UI ready") {
SimpleTest.requestFlakyTimeout(reason);
await new Promise(resolve => setTimeout(resolve, ms));
}
async function setInput(selector, value) {
let input = document.querySelector("input" + selector);
input.value = value;
@@ -107,26 +109,44 @@ async function cleanUpCreditCards() {
return invokeAsyncChromeTask("FormAutofillTest:CleanUpCreditCards", "FormAutofillTest:CreditCardsCleanedUp");
}
async function cleanUpStorage() {
await cleanUpAddresses();
await cleanUpCreditCards();
}
+function patchRecordCCNumber(record) {
+ const ccNumber = record["cc-number"];
+ const normalizedCCNumber = "*".repeat(ccNumber.length - 4) + ccNumber.substr(-4);
+ const ccNumberFmt = FormAutofillUtils.fmtMaskedCreditCardLabel(normalizedCCNumber);
+
+ return Object.assign({}, record, {ccNumberFmt});
+}
+
// Utils for registerPopupShownListener(in satchel_common.js) that handles dropdown popup
// Please call "initPopupListener()" in your test and "await expectPopup()"
// if you want to wait for dropdown menu displayed.
function expectPopup() {
info("expecting a popup");
return new Promise(resolve => {
expectingPopup = resolve;
});
}
+function notExpectPopup(ms = 500) {
+ info("not expecting a popup");
+ return new Promise((resolve, reject) => {
+ expectingPopup = reject.bind(this, "Unexpected Popup");
+ // TODO: We don't have an event to notify no popup showing, so wait for 500
+ // ms (in default) to predict any unexpected popup showing.
+ setTimeout(resolve, ms);
+ });
+}
+
function popupShownListener() {
info("popup shown for test ");
if (expectingPopup) {
expectingPopup();
expectingPopup = null;
}
}
--- a/browser/extensions/formautofill/test/mochitest/mochitest.ini
+++ b/browser/extensions/formautofill/test/mochitest/mochitest.ini
@@ -6,11 +6,13 @@ support-files =
../../../../../toolkit/components/satchel/test/parent_utils.js
formautofill_common.js
formautofill_parent_utils.js
[test_autofocus_form.html]
[test_basic_autocomplete_form.html]
[test_basic_creditcard_autocomplete_form.html]
scheme=https
+[test_creditcard_autocomplete_off.html]
+scheme=https
[test_formautofill_preview_highlight.html]
[test_multiple_forms.html]
[test_on_address_submission.html]
--- a/browser/extensions/formautofill/test/mochitest/test_basic_autocomplete_form.html
+++ b/browser/extensions/formautofill/test/mochitest/test_basic_autocomplete_form.html
@@ -14,18 +14,16 @@ Form autofill test: simple form address
<script>
/* import-globals-from ../../../../../testing/mochitest/tests/SimpleTest/SpawnTask.js */
/* import-globals-from ../../../../../toolkit/components/satchel/test/satchel_common.js */
/* import-globals-from formautofill_common.js */
"use strict";
-const {FormAutofillUtils} = SpecialPowers.Cu.import("resource://formautofill/FormAutofillUtils.jsm");
-
let MOCK_STORAGE = [{
organization: "Sesame Street",
"street-address": "123 Sesame Street.\n2-line\n3-line",
tel: "+13453453456",
country: "US",
"address-level1": "NY",
}, {
organization: "Mozilla",
--- a/browser/extensions/formautofill/test/mochitest/test_basic_creditcard_autocomplete_form.html
+++ b/browser/extensions/formautofill/test/mochitest/test_basic_creditcard_autocomplete_form.html
@@ -14,18 +14,16 @@ Form autofill test: simple form credit c
<script>
/* import-globals-from ../../../../../testing/mochitest/tests/SimpleTest/SpawnTask.js */
/* import-globals-from ../../../../../toolkit/components/satchel/test/satchel_common.js */
/* import-globals-from formautofill_common.js */
"use strict";
-const {FormAutofillUtils} = SpecialPowers.Cu.import("resource://formautofill/FormAutofillUtils.jsm");
-
const MOCK_STORAGE = [{
"cc-name": "John Doe",
"cc-number": "1234567812345678",
"cc-exp-month": 4,
"cc-exp-year": 2017,
}, {
"cc-name": "Timothy Berners-Lee",
"cc-number": "1111222233334444",
@@ -33,24 +31,16 @@ const MOCK_STORAGE = [{
"cc-exp-year": 2022,
}];
const reducedMockRecord = {
"cc-name": "John Doe",
"cc-number": "1234123456785678",
};
-function patchRecordCCNumber(record) {
- const ccNumber = record["cc-number"];
- const normalizedCCNumber = "*".repeat(ccNumber.length - 4) + ccNumber.substr(-4);
- const ccNumberFmt = FormAutofillUtils.fmtMaskedCreditCardLabel(normalizedCCNumber);
-
- return Object.assign({}, record, {ccNumberFmt});
-}
-
function checkElementFilled(element, expectedvalue) {
const focusedElem = document.activeElement;
const promises = [];
promises.push(new Promise(resolve => {
element.addEventListener("input", function onInput() {
ok(true, "Checking " + element.name + " field fires input event");
resolve();
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/test/mochitest/test_creditcard_autocomplete_off.html
@@ -0,0 +1,98 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test basic autofill</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+ <script type="text/javascript" src="formautofill_common.js"></script>
+ <script type="text/javascript" src="satchel_common.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+Form autofill test: simple form credit card autofill
+
+<script>
+/* import-globals-from ../../../../../testing/mochitest/tests/SimpleTest/SpawnTask.js */
+/* import-globals-from ../../../../../toolkit/components/satchel/test/satchel_common.js */
+/* import-globals-from formautofill_common.js */
+
+"use strict";
+
+const MOCK_STORAGE = [{
+ "cc-name": "John Doe",
+ "cc-number": "1234567812345678",
+ "cc-exp-month": 4,
+ "cc-exp-year": 2017,
+}, {
+ "cc-name": "Timothy Berners-Lee",
+ "cc-number": "1111222233334444",
+ "cc-exp-month": 12,
+ "cc-exp-year": 2022,
+}];
+
+async function setupCreditCardStorage() {
+ await addCreditCard(MOCK_STORAGE[0]);
+ await addCreditCard(MOCK_STORAGE[1]);
+}
+
+async function setupFormHistory() {
+ await updateFormHistory([
+ {op: "add", fieldname: "cc-name", value: "John Smith"},
+ {op: "add", fieldname: "cc-number", value: "1234000056780000"},
+ ]);
+}
+
+initPopupListener();
+
+// Show Form History popup for non-autocomplete="off" field only
+add_task(async function history_only_menu_checking() {
+ await setupFormHistory();
+
+ await setInput("#cc-number", "");
+ doKey("down");
+ await expectPopup();
+ checkMenuEntries(["1234000056780000"], false);
+
+ await setInput("#cc-name", "");
+ doKey("down");
+ await notExpectPopup();
+});
+
+// Show Form Autofill popup for the credit card fields.
+add_task(async function check_menu_when_both_with_autocomplete_off() {
+ await setupCreditCardStorage();
+
+ await setInput("#cc-number", "");
+ doKey("down");
+ await expectPopup();
+ checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(cc => JSON.stringify({
+ primaryAffix: cc.ccNumberFmt.affix,
+ primary: cc.ccNumberFmt.label,
+ secondary: cc["cc-name"],
+ })));
+
+ await setInput("#cc-name", "");
+ doKey("down");
+ await expectPopup();
+ checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(cc => JSON.stringify({
+ primary: cc["cc-name"],
+ secondary: cc.ccNumberFmt.affix + cc.ccNumberFmt.label,
+ })));
+});
+
+</script>
+
+<p id="display"></p>
+
+<div id="content">
+ <form id="form1">
+ <p>This is a Credit Card form with autocomplete="off" cc-name field.</p>
+ <p><label>Name: <input id="cc-name" autocomplete="off"></label></p>
+ <p><label>Card Number: <input id="cc-number" autocomplete="cc-number"></label></p>
+ </form>
+</div>
+
+<pre id="test"></pre>
+</body>
+</html>
--- a/toolkit/components/satchel/nsFormFillController.cpp
+++ b/toolkit/components/satchel/nsFormFillController.cpp
@@ -1062,17 +1062,22 @@ nsFormFillController::MaybeStartControll
bool isPwmgrInput = false;
nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(aInput);
MOZ_ASSERT(formControl, "If we have a text control, we have a form control!");
if (mPwmgrInputs.Get(inputNode) ||
formControl->ControlType() == NS_FORM_INPUT_PASSWORD) {
isPwmgrInput = true;
}
- if (isPwmgrInput || hasList || autocomplete) {
+ bool isAutofillInput = false;
+ if (mAutofillInputs.Get(inputNode)) {
+ isAutofillInput = true;
+ }
+
+ if (isAutofillInput || isPwmgrInput || hasList || autocomplete) {
StartControllingInput(aInput);
}
}
nsresult
nsFormFillController::Focus(nsIDOMEvent* aEvent)
{
nsCOMPtr<nsIDOMHTMLInputElement> input = do_QueryInterface(