Bug 1919571 - The password manager dropdown is being triggered for the Email field instead of the address autofill one. r=dimi

Differential Revision: https://phabricator.services.mozilla.com/D247546
This commit is contained in:
Helena
2025-05-22 11:38:50 +00:00
committed by neil@mozilla.com
parent 4df3004687
commit 4670e46142
4 changed files with 116 additions and 3 deletions

View File

@@ -5,6 +5,7 @@ support-files = [
"../fixtures/autocomplete_basic.html",
"../fixtures/autocomplete_iframe.html",
"../fixtures/autocomplete_iframe_sandboxed.html",
"../fixtures/autocomplete_multiple_emails_checkout.html",
"../fixtures/autocomplete_simple_basic.html",
"../fixtures/dynamic_forms.html",
"../fixtures/dynamic_formless_fields_updated_due_to_node_mutations.html",
@@ -83,6 +84,8 @@ skip-if = [
"os == 'linux'" , # Bug 1788900
]
["browser_email_dropdown.js"]
["browser_fathom_cc.js"]
["browser_fillclear_events.js"]

View File

@@ -0,0 +1,59 @@
"use strict";
const PAGE_URL =
"https://example.org/browser/browser/extensions/formautofill/test/fixtures/autocomplete_multiple_emails_checkout.html";
// This testcase is to ensure that if a field gets recoginised by both
// login manager and formautofill providers, that if an address is saved,
// that the formautofill popup gets priority over the login manager.
add_task(async function test_email_field_is_address_dropdown() {
await SpecialPowers.pushPrefEnv({
set: [["signon.rememberSignons", true]],
});
// If an address is saved, show the formautofill dropdown.
await setStorage(TEST_ADDRESS_1);
await BrowserTestUtils.withNewTab(
{ gBrowser, url: PAGE_URL },
async function (browser) {
const focusInput = "#email";
// We need to initialize and identify fields on a field that doesn't trigger
// a login autocomplete on focus, otherwise the popup could appear too early.
await focusAndWaitForFieldsIdentified(browser, "#given-name");
await openPopupOn(browser, focusInput);
const item = getDisplayedPopupItems(browser)[2];
is(
item.getAttribute("ac-value"),
"Manage addresses",
"Address popup should show a valid email suggestion"
);
await closePopup(browser);
}
);
});
add_task(
async function test_email_field_shows_login_dropdown_when_no_saved_address() {
// However, if no addresses are saved, show the login manager.
await removeAllRecords();
await BrowserTestUtils.withNewTab(
{ gBrowser, url: PAGE_URL },
async function (browser) {
const focusInput = "#email";
await focusAndWaitForFieldsIdentified(browser, "#given-name");
await openPopupOn(browser, focusInput);
const item = getDisplayedPopupItems(browser)[0];
is(
item.getAttribute("ac-value"),
"Manage Passwords",
"Login Manager should be shown"
);
await closePopup(browser);
}
);
}
);

View File

@@ -0,0 +1,36 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Form Autofill Address - Two Emails at checkout</title>
</head>
<body>
<h1>Form Autofill Address Demo Page</h1>
<form id="form">
<p><label>firstName: <input type="text" id="given-name" name="given-name" autocomplete="given-name" /></label></p>
<p><label>lastName: <input type="text" id="family-name" name="family-name" autocomplete="family-name" /></label></p>
<p><label>country: <input type="text" id="country" name="country" autocomplete="country" /></label></p>
<p><label>postalCode: <input type="text" id="postal-code" name="postal-code" autocomplete="postal-code" /></label></p>
<p><label>city (addressLevel2): <input type="text" id="address-level2" name="address-level2" autocomplete="address-level2" /></label></p>
<p><label>streetAddress: <input type="text" id="street-address" name="street-address" autocomplete="street-address" /></label></p>
<p><label>addressLine2: <input type="text" id="address-line2" name="address-line2" autocomplete="address-line2" /></label></p>
<p><label>stair: <input type="text" id="stair" name="stair" autocomplete="address-line3" /></label></p>
<p><label>floor: <input type="text" id="floor" name="floor" autocomplete="additional-name" /></label></p>
<p><label>door: <input type="text" id="door" name="door" /></label></p>
<p><label>tel: <input type="tel" id="tel" name="tel" autocomplete="tel" /></label></p>
<p><label>email: <input type="email" id="email" name="email" autocomplete="email" /></label></p>
<p><label>emailRepeat: <input type="email" id="email-repeat" name="email-repeat" autocomplete="email" /></label></p>
<p><label>newPassword: <input type="password" id="new-password" name="new-password" autocomplete="new-password" /></label></p>
<p><label>newPasswordRepeat: <input type="password" id="new-password-repeat" name="new-password-repeat" autocomplete="new-password" /></label></p>
<p>
<input type="submit" value="Submit" />
<button type="reset">Reset</button>
</p>
</form>
</body>
</html>

View File

@@ -280,11 +280,26 @@ export class AutoCompleteChild extends JSWindowActorChild {
}
}
for (const provider of providers) {
// Search result could be empty. However, an autocomplete provider might
// want to show an autocomplete popup when there is no search result. For example,
// If one provider has a non-null result, use the non-results. However, if
// no providers have a non-null result, use the empty results instead.
// This is because an autocomplete provider might want to show an
// autocomplete popup when there is no search result. For example,
// <datalist> for FormHistory, insecure warning for LoginManager.
let foundResults = [];
let emptyResults = [];
for (const provider of providers) {
const searchResult = result.find(r => r.actorName == provider.actorName);
if (searchResult) {
foundResults.push([provider, searchResult]);
} else {
emptyResults.push([provider, undefined]);
}
}
for (const [provider, searchResult] of foundResults.length
? foundResults
: emptyResults) {
const acResult = provider.searchResultToAutoCompleteResult(
searchString,
input,