Bug 1886064, make usage of label and comment fields in autofill items more consistent, r=credential-management-reviewers,geckoview-reviewers,reusable-components-reviewers,owlish,sgalich,tgiles

Differential Revision: https://phabricator.services.mozilla.com/D207439
This commit is contained in:
Neil Deakin 2024-05-14 15:37:32 +00:00
parent e28bfa026f
commit 8d1980e1cc
32 changed files with 204 additions and 176 deletions

View file

@ -8,7 +8,7 @@ const URL = BASE_URL + "autocomplete_basic.html";
function checkPopup(autoCompletePopup) {
let first = autoCompletePopup.view.results[0];
const { primary, secondary } = JSON.parse(first.label);
const { primary, secondary } = JSON.parse(first.comment);
ok(
primary.startsWith(TEST_ADDRESS_1["street-address"].split("\n")[0]),
"Check primary label is street address"

View file

@ -8,7 +8,7 @@ const URL = BASE_URL + "autocomplete_basic.html";
function checkPopup(autoCompletePopup) {
let first = autoCompletePopup.view.results[0];
const { primary, secondary } = JSON.parse(first.label);
const { primary, secondary } = JSON.parse(first.comment);
ok(
primary.startsWith(TEST_ADDRESS_1["street-address"].split("\n")[0]),
"Check primary label is street address"

View file

@ -89,7 +89,7 @@ add_task(async function all_saved_fields_less_than_threshold() {
await setInput("#cc-name", "");
await expectPopup();
synthesizeKey("KEY_ArrowDown");
checkMenuEntries([reducedMockRecord].map(patchRecordCCNumber).map(({ cc, expected }) => JSON.stringify({
checkMenuEntriesComment([reducedMockRecord].map(patchRecordCCNumber).map(({ cc, expected }) => JSON.stringify({
primary: cc["cc-name"],
secondary: replaceStars(cc.ccNumberFmt),
ariaLabel: `Visa ${cc["cc-name"]} ${cc.ccNumberFmt}`,
@ -106,7 +106,7 @@ add_task(async function check_menu_when_both_existed() {
await setInput("#cc-number", "");
await expectPopup();
synthesizeKey("KEY_ArrowDown");
checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(({ cc, expected }) => JSON.stringify({
checkMenuEntriesComment(MOCK_STORAGE.map(patchRecordCCNumber).map(({ cc, expected }) => JSON.stringify({
primary: replaceStars(cc.ccNumberFmt),
secondary: cc["cc-name"].toString(),
ariaLabel: `${getCCTypeName(cc)} ${cc.ccNumberFmt.replaceAll("*", "")} ${cc["cc-name"]}`,
@ -116,7 +116,7 @@ add_task(async function check_menu_when_both_existed() {
await setInput("#cc-name", "");
await expectPopup();
synthesizeKey("KEY_ArrowDown");
checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(({ cc, expected }) => JSON.stringify({
checkMenuEntriesComment(MOCK_STORAGE.map(patchRecordCCNumber).map(({ cc, expected }) => JSON.stringify({
primary: cc["cc-name"].toString(),
secondary: replaceStars(cc.ccNumberFmt),
ariaLabel: `${getCCTypeName(cc)} ${cc["cc-name"]} ${cc.ccNumberFmt}`,
@ -126,7 +126,7 @@ add_task(async function check_menu_when_both_existed() {
await setInput("#cc-exp-year", "");
await expectPopup();
synthesizeKey("KEY_ArrowDown");
checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(({ cc, expected }) => JSON.stringify({
checkMenuEntriesComment(MOCK_STORAGE.map(patchRecordCCNumber).map(({ cc, expected }) => JSON.stringify({
primary: cc["cc-exp-year"].toString(),
secondary: replaceStars(cc.ccNumberFmt),
ariaLabel: `${getCCTypeName(cc)} ${cc["cc-exp-year"]} ${cc.ccNumberFmt}`,
@ -136,7 +136,7 @@ add_task(async function check_menu_when_both_existed() {
await setInput("#cc-exp-month", "");
await expectPopup();
synthesizeKey("KEY_ArrowDown");
checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(({ cc, expected }) => JSON.stringify({
checkMenuEntriesComment(MOCK_STORAGE.map(patchRecordCCNumber).map(({ cc, expected }) => JSON.stringify({
primary: cc["cc-exp-month"].toString(),
secondary: replaceStars(cc.ccNumberFmt),
ariaLabel: `${getCCTypeName(cc)} ${cc["cc-exp-month"]} ${cc.ccNumberFmt}`,
@ -189,7 +189,7 @@ add_task(async function check_fields_after_form_autofill() {
synthesizeKey("KEY_ArrowDown");
// The popup doesn't auto-show on focus because the field isn't empty
await expectPopup();
checkMenuEntries(MOCK_STORAGE.slice(1).map(patchRecordCCNumber).map(({ cc, expected }) => JSON.stringify({
checkMenuEntriesComment(MOCK_STORAGE.slice(1).map(patchRecordCCNumber).map(({ cc, expected }) => JSON.stringify({
primary: cc["cc-exp-year"].toString(),
secondary: replaceStars(cc.ccNumberFmt),
ariaLabel: `${getCCTypeName(cc)} ${cc["cc-exp-year"]} ${cc.ccNumberFmt}`,
@ -228,7 +228,7 @@ add_task(async function check_cc_popup_on_field_blank() {
await setInput("#cc-name", "", true);
await expectPopup();
checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(({ cc, expected }) => JSON.stringify({
checkMenuEntriesComment(MOCK_STORAGE.map(patchRecordCCNumber).map(({ cc, expected }) => JSON.stringify({
primary: cc["cc-name"],
secondary: replaceStars(cc.ccNumberFmt),
ariaLabel: `${getCCTypeName(cc)} ${cc["cc-name"]} ${cc.ccNumberFmt}`,
@ -248,7 +248,7 @@ add_task(async function check_form_autofill_resume() {
await setInput("#cc-name", "");
synthesizeKey("KEY_ArrowDown");
await expectPopup();
checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(({ cc, expected }) => JSON.stringify({
checkMenuEntriesComment(MOCK_STORAGE.map(patchRecordCCNumber).map(({ cc, expected }) => JSON.stringify({
primary: cc["cc-name"],
secondary: replaceStars(cc.ccNumberFmt),
ariaLabel: `${getCCTypeName(cc)} ${cc["cc-name"]} ${cc.ccNumberFmt}`,

View file

@ -128,7 +128,8 @@ add_task(async function simple_clear() {
// Ensure the correctness of the autocomplete popup after the form is cleared
synthesizeKey("KEY_ArrowDown");
await expectPopup();
is(4, getMenuEntries().length, `Checking length of expected menu`);
is(4, getMenuEntries().labels.length, `Checking length of expected menu`);
});
add_task(async function clear_adapted_record() {

View file

@ -77,7 +77,7 @@ add_task(async function check_menu_when_both_with_autocomplete_off() {
await setInput("#cc-number", "");
synthesizeKey("KEY_ArrowDown");
await expectPopup();
checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(({ cc, expected }) => JSON.stringify({
checkMenuEntriesComment(MOCK_STORAGE.map(patchRecordCCNumber).map(({ cc, expected }) => JSON.stringify({
primary: replaceStars(cc.ccNumberFmt),
secondary: cc["cc-name"],
ariaLabel: `${getCCTypeName(cc)} ${cc.ccNumberFmt.replaceAll("*", "")} ${cc["cc-name"]}`,
@ -87,7 +87,7 @@ add_task(async function check_menu_when_both_with_autocomplete_off() {
await setInput("#cc-name", "");
synthesizeKey("KEY_ArrowDown");
await expectPopup();
checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(({ cc, expected }) => JSON.stringify({
checkMenuEntriesComment(MOCK_STORAGE.map(patchRecordCCNumber).map(({ cc, expected }) => JSON.stringify({
primary: cc["cc-name"],
secondary: replaceStars(cc.ccNumberFmt),
ariaLabel: `${getCCTypeName(cc)} ${cc["cc-name"]} ${cc.ccNumberFmt}`,

View file

@ -275,7 +275,7 @@ async function onStorageChanged(type) {
});
}
function makeAddressLabel({ primary, secondary, status }) {
function makeAddressComment({ primary, secondary, status }) {
return JSON.stringify({
primary,
secondary,
@ -284,8 +284,9 @@ function makeAddressLabel({ primary, secondary, status }) {
});
}
// Compare the labels on the autocomplete menu items to the expected labels.
function checkMenuEntries(expectedValues, extraRows = 1) {
let actualValues = getMenuEntries();
let actualValues = getMenuEntries().labels;
let expectedLength = expectedValues.length + extraRows;
is(actualValues.length, expectedLength, " Checking length of expected menu");
@ -294,6 +295,25 @@ function checkMenuEntries(expectedValues, extraRows = 1) {
}
}
// Compare the comment on the autocomplete menu items to the expected comment.
// The profile field is not compared.
function checkMenuEntriesComment(expectedValues, extraRows = 1) {
let actualValues = getMenuEntries().comments;
let expectedLength = expectedValues.length + extraRows;
is(actualValues.length, expectedLength, " Checking length of expected menu");
for (let i = 0; i < expectedValues.length; i++) {
let val = actualValues[i];
if (val) {
val = JSON.parse(val);
delete val.profile;
val = JSON.stringify(val);
}
is(val, expectedValues[i], " Checking menu entry #" + i);
}
}
function invokeAsyncChromeTask(message, payload = {}) {
info(`expecting the chrome task finished: ${message}`);
return formFillChromeScript.sendQuery(message, payload);

View file

@ -46,9 +46,9 @@ add_task(async function check_switch_autofill_form_popup() {
await setInput("#tel", "");
synthesizeKey("KEY_ArrowDown");
await expectPopup();
checkMenuEntries(
checkMenuEntriesComment(
[
makeAddressLabel({
makeAddressComment({
primary: "+13453453456",
secondary: "123 Sesame Street.",
status: statusText
@ -77,9 +77,9 @@ add_task(async function check_switch_autofill_form_popup_back() {
await setInput("#tel", "");
synthesizeKey("KEY_ArrowDown");
await expectPopup();
checkMenuEntries(
checkMenuEntriesComment(
[
makeAddressLabel({
makeAddressComment({
primary: "+13453453456",
secondary: "123 Sesame Street.",
status: statusText

View file

@ -38,8 +38,8 @@ add_task(async function check_autocomplete_on_autofocus_field() {
await setupAddressStorage();
synthesizeKey("KEY_ArrowDown");
await expectPopup();
checkMenuEntries(MOCK_STORAGE.map(address =>
makeAddressLabel({
checkMenuEntriesComment(MOCK_STORAGE.map(address =>
makeAddressComment({
primary: address.organization,
secondary: address["street-address"],
status: "Also autofills address, phone"

View file

@ -80,8 +80,8 @@ add_task(async function check_menu_when_both_existed() {
await notExpectPopup();
synthesizeKey("KEY_ArrowDown");
await expectPopup();
checkMenuEntries(MOCK_STORAGE.map(address =>
makeAddressLabel({
checkMenuEntriesComment(MOCK_STORAGE.map(address =>
makeAddressComment({
primary: address.organization,
secondary: FormAutofillUtils.toOneLineAddress(address["street-address"]),
status: "Also autofills address, phone"
@ -92,8 +92,8 @@ add_task(async function check_menu_when_both_existed() {
await notExpectPopup();
synthesizeKey("KEY_ArrowDown");
await expectPopup();
checkMenuEntries(MOCK_STORAGE.map(address =>
makeAddressLabel({
checkMenuEntriesComment(MOCK_STORAGE.map(address =>
makeAddressComment({
primary: FormAutofillUtils.toOneLineAddress(address["street-address"]),
secondary: address.organization,
status: "Also autofills organization, phone"
@ -104,8 +104,8 @@ add_task(async function check_menu_when_both_existed() {
await notExpectPopup();
synthesizeKey("KEY_ArrowDown");
await expectPopup();
checkMenuEntries(MOCK_STORAGE.map(address =>
makeAddressLabel({
checkMenuEntriesComment(MOCK_STORAGE.map(address =>
makeAddressComment({
primary: address.tel,
secondary: FormAutofillUtils.toOneLineAddress(address["street-address"]),
status: "Also autofills address, organization"
@ -116,8 +116,8 @@ add_task(async function check_menu_when_both_existed() {
await notExpectPopup();
synthesizeKey("KEY_ArrowDown");
await expectPopup();
checkMenuEntries(MOCK_STORAGE.map(address =>
makeAddressLabel({
checkMenuEntriesComment(MOCK_STORAGE.map(address =>
makeAddressComment({
primary: FormAutofillUtils.toOneLineAddress(address["street-address"]),
secondary: address.organization,
status: "Also autofills organization, phone"
@ -155,8 +155,8 @@ add_task(async function check_fields_after_form_autofill() {
await notExpectPopup();
synthesizeKey("KEY_ArrowDown");
await expectPopup();
checkMenuEntries(MOCK_STORAGE.map(address =>
makeAddressLabel({
checkMenuEntriesComment(MOCK_STORAGE.map(address =>
makeAddressComment({
primary: address.organization,
secondary: FormAutofillUtils.toOneLineAddress(address["street-address"]),
status: "Also autofills address, phone"
@ -184,8 +184,8 @@ add_task(async function check_form_autofill_resume() {
document.querySelector("#form1").reset();
await setInput("#tel", "");
await triggerPopupAndHoverItem("#tel", 0);
checkMenuEntries(MOCK_STORAGE.map(address =>
makeAddressLabel({
checkMenuEntriesComment(MOCK_STORAGE.map(address =>
makeAddressComment({
primary: address.tel,
secondary: FormAutofillUtils.toOneLineAddress(address["street-address"]),
status: "Also autofills address, organization"

View file

@ -60,8 +60,8 @@ async function checkFormChangeHappened(formId) {
synthesizeKey("KEY_ArrowDown");
await expectPopup();
synthesizeKey("KEY_ArrowDown");
checkMenuEntries(MOCK_STORAGE.map(address =>
makeAddressLabel({
checkMenuEntriesComment(MOCK_STORAGE.map(address =>
makeAddressComment({
primary: address.tel,
secondary: address.name,
status: "Also autofills name, organization"
@ -91,8 +91,8 @@ async function checkFormChangeHappened(formId) {
await focusAndWaitForFieldsIdentified(`#${formId} input[name=address-level2]`, true);
synthesizeKey("KEY_ArrowDown");
await expectPopup();
checkMenuEntries(MOCK_STORAGE.map(address =>
makeAddressLabel({
checkMenuEntriesComment(MOCK_STORAGE.map(address =>
makeAddressComment({
primary: address["address-level2"],
secondary: address.name,
status: "Also autofills name, organization, phone"

View file

@ -51,12 +51,13 @@ let allFieldNames = [
"tel",
];
function makeAddressLabel({ primary, secondary, status }) {
function makeAddressComment({ primary, secondary, status, profile }) {
return JSON.stringify({
primary,
secondary,
status,
ariaLabel: primary + " " + secondary + " " + status,
profile,
});
}
@ -75,22 +76,24 @@ let addressTestCases = [
{
value: "",
style: "autofill",
comment: JSON.stringify(matchingProfiles[0]),
label: makeAddressLabel({
label: "Sesame Street",
comment: makeAddressComment({
primary: "Sesame Street",
secondary: "123 Sesame Street.",
status: "Also autofills address, name, phone",
profile: matchingProfiles[0],
}),
image: "",
},
{
value: "",
style: "autofill",
comment: JSON.stringify(matchingProfiles[1]),
label: makeAddressLabel({
label: "Mozilla",
comment: makeAddressComment({
primary: "Mozilla",
secondary: "331 E. Evelyn Avenue",
status: "Also autofills address, name, phone",
profile: matchingProfiles[1],
}),
image: "",
},
@ -111,33 +114,36 @@ let addressTestCases = [
{
value: "",
style: "autofill",
comment: JSON.stringify(matchingProfiles[0]),
label: makeAddressLabel({
label: "1-345-345-3456.",
comment: makeAddressComment({
primary: "1-345-345-3456.",
secondary: "123 Sesame Street.",
status: "Also autofills address, name, organization",
profile: matchingProfiles[0],
}),
image: "",
},
{
value: "",
style: "autofill",
comment: JSON.stringify(matchingProfiles[1]),
label: makeAddressLabel({
label: "1-650-903-0800",
comment: makeAddressComment({
primary: "1-650-903-0800",
secondary: "331 E. Evelyn Avenue",
status: "Also autofills address, name, organization",
profile: matchingProfiles[1],
}),
image: "",
},
{
value: "",
style: "autofill",
comment: JSON.stringify(matchingProfiles[2]),
label: makeAddressLabel({
label: "1-000-000-0000",
comment: makeAddressComment({
primary: "1-000-000-0000",
secondary: "321, No Name St. 2nd line 3rd line",
status: "Also autofills address",
profile: matchingProfiles[2],
}),
image: "",
},
@ -158,33 +164,36 @@ let addressTestCases = [
{
value: "",
style: "autofill",
comment: JSON.stringify(matchingProfiles[0]),
label: makeAddressLabel({
label: "123 Sesame Street.",
comment: makeAddressComment({
primary: "123 Sesame Street.",
secondary: "Timothy Berners-Lee",
status: "Also autofills name, organization, phone",
profile: matchingProfiles[0],
}),
image: "",
},
{
value: "",
style: "autofill",
comment: JSON.stringify(matchingProfiles[1]),
label: makeAddressLabel({
label: "331 E. Evelyn Avenue",
comment: makeAddressComment({
primary: "331 E. Evelyn Avenue",
secondary: "John Doe",
status: "Also autofills name, organization, phone",
profile: matchingProfiles[1],
}),
image: "",
},
{
value: "",
style: "autofill",
comment: JSON.stringify(matchingProfiles[2]),
label: makeAddressLabel({
label: "321, No Name St. 2nd line 3rd line",
comment: makeAddressComment({
primary: "321, No Name St. 2nd line 3rd line",
secondary: "1-000-000-0000",
status: "Also autofills phone",
profile: matchingProfiles[2],
}),
image: "",
},
@ -205,33 +214,36 @@ let addressTestCases = [
{
value: "",
style: "autofill",
comment: JSON.stringify(matchingProfiles[0]),
label: makeAddressLabel({
label: "123 Sesame Street.",
comment: makeAddressComment({
primary: "123 Sesame Street.",
secondary: "Timothy Berners-Lee",
status: "Also autofills name, organization, phone",
profile: matchingProfiles[0],
}),
image: "",
},
{
value: "",
style: "autofill",
comment: JSON.stringify(matchingProfiles[1]),
label: makeAddressLabel({
label: "331 E. Evelyn Avenue",
comment: makeAddressComment({
primary: "331 E. Evelyn Avenue",
secondary: "John Doe",
status: "Also autofills name, organization, phone",
profile: matchingProfiles[1],
}),
image: "",
},
{
value: "",
style: "autofill",
comment: JSON.stringify(matchingProfiles[2]),
label: makeAddressLabel({
label: "321, No Name St.",
comment: makeAddressComment({
primary: "321, No Name St.",
secondary: "1-000-000-0000",
status: "Also autofills phone",
profile: matchingProfiles[2],
}),
image: "",
},
@ -308,24 +320,26 @@ let creditCardTestCases = [
{
value: "",
style: "autofill",
comment: JSON.stringify(matchingProfiles[0]),
label: JSON.stringify({
label: "Timothy Berners-Lee",
comment: JSON.stringify({
primary: "Timothy Berners-Lee",
secondary: "••••6785",
ariaLabel: "Visa Timothy Berners-Lee ****6785",
image: "chrome://formautofill/content/third-party/cc-logo-visa.svg",
profile: matchingProfiles[0],
}),
image: "chrome://formautofill/content/third-party/cc-logo-visa.svg",
},
{
value: "",
style: "autofill",
comment: JSON.stringify(matchingProfiles[1]),
label: JSON.stringify({
label: "John Doe",
comment: JSON.stringify({
primary: "John Doe",
secondary: "••••1234",
ariaLabel: "American Express John Doe ****1234",
image: "chrome://formautofill/content/third-party/cc-logo-amex.png",
profile: matchingProfiles[1],
}),
image: "chrome://formautofill/content/third-party/cc-logo-amex.png",
},
@ -346,36 +360,39 @@ let creditCardTestCases = [
{
value: "",
style: "autofill",
comment: JSON.stringify(matchingProfiles[0]),
label: JSON.stringify({
label: "••••6785",
comment: JSON.stringify({
primary: "••••6785",
secondary: "Timothy Berners-Lee",
ariaLabel: "Visa 6785 Timothy Berners-Lee",
image: "chrome://formautofill/content/third-party/cc-logo-visa.svg",
profile: matchingProfiles[0],
}),
image: "chrome://formautofill/content/third-party/cc-logo-visa.svg",
},
{
value: "",
style: "autofill",
comment: JSON.stringify(matchingProfiles[1]),
label: JSON.stringify({
label: "••••1234",
comment: JSON.stringify({
primary: "••••1234",
secondary: "John Doe",
ariaLabel: "American Express 1234 John Doe",
image: "chrome://formautofill/content/third-party/cc-logo-amex.png",
profile: matchingProfiles[1],
}),
image: "chrome://formautofill/content/third-party/cc-logo-amex.png",
},
{
value: "",
style: "autofill",
comment: JSON.stringify(matchingProfiles[2]),
label: JSON.stringify({
label: "••••5678",
comment: JSON.stringify({
primary: "••••5678",
secondary: "",
ariaLabel: "5678",
image: "chrome://formautofill/content/icon-credit-card-generic.svg",
profile: matchingProfiles[2],
}),
image: "chrome://formautofill/content/icon-credit-card-generic.svg",
},

View file

@ -612,8 +612,8 @@ export const GeckoViewAutocomplete = {
case "autofill": {
const comment = JSON.parse(option.comment);
debug`delegateSelection ${comment}`;
const creditCard = CreditCard.fromGecko(comment);
const address = Address.fromGecko(comment);
const creditCard = CreditCard.fromGecko(comment.profile);
const address = Address.fromGecko(comment.profile);
if (creditCard.isValid()) {
selectionType = "creditCard";
selectOptions.push(

View file

@ -92,16 +92,11 @@ var AutoCompleteResultView = {
},
getLabelAt(index) {
// Backwardly-used by richlist autocomplete - see getCommentAt.
// The label is used for secondary information.
return this.results[index].comment;
return this.results[index].label;
},
getCommentAt(index) {
// The richlist autocomplete popup uses comment for its main
// display of an item, which is why we're returning the label
// here instead.
return this.results[index].label;
return this.results[index].comment;
},
getStyleAt(index) {

View file

@ -50,17 +50,21 @@ interface nsIAutoCompleteResult : nsISupports
readonly attribute unsigned long matchCount;
/**
* Get the value of the result at the given index
* Get the value of the result at the given index. This is the value that
* will be filled into the text field.
*/
AString getValueAt(in long index);
/**
* This returns the string that is displayed in the dropdown
* Get the label at the given index. This is the string that is displayed
* in the autocomplete dropdown row. If there is additional text to be
* displayed, it should be stored within a field in the comment.
*/
AString getLabelAt(in long index);
/**
* Get the comment of the result at the given index
* Get the comment of the result at the given index. This is a serialized
* JSON object containing additional properties related to the index.
*/
AString getCommentAt(in long index);

View file

@ -101,7 +101,7 @@ export const ProfileAutocomplete = {
? lastProfileAutoCompleteResult.getCommentAt(selectedIndex)
: null;
let profile = JSON.parse(comment);
let parsedComment = JSON.parse(comment);
if (
selectedIndex == -1 ||
lastProfileAutoCompleteResult?.getStyleAt(selectedIndex) != "autofill"
@ -110,7 +110,7 @@ export const ProfileAutocomplete = {
focusedInput &&
focusedInput == autocompleteController?.input.focusedInput
) {
if (profile?.fillMessageName == "FormAutofill:ClearForm") {
if (parsedComment?.fillMessageName == "FormAutofill:ClearForm") {
// The child can do this directly.
getActorFromWindow(focusedInput.ownerGlobal)?.clearForm();
} else {

View file

@ -706,9 +706,9 @@ export class FormAutofillParent extends JSWindowActorParent {
previewFields(result) {
try {
const profile =
const comment =
result.style == "autofill" ? JSON.parse(result.comment) : null;
this.sendAsyncMessage("FormAutofill:PreviewProfile", profile);
this.sendAsyncMessage("FormAutofill:PreviewProfile", comment?.profile);
} catch (e) {
lazy.log.debug("Fail to get preview profile: ", e.message);
}
@ -717,8 +717,8 @@ export class FormAutofillParent extends JSWindowActorParent {
autofillFields(result) {
if (result.style == "autofill") {
try {
const profile = JSON.parse(result.comment);
this.sendAsyncMessage("FormAutofill:FillForm", profile);
const comment = JSON.parse(result.comment);
this.sendAsyncMessage("FormAutofill:FillForm", comment.profile);
} catch (e) {
lazy.log.debug("Fail to get autofill profile.");
}

View file

@ -128,16 +128,7 @@ class ProfileAutoCompleteResult {
getLabelAt(index) {
const label = this.getAt(index);
if (typeof label == "string") {
return label;
}
let type = this.getTypeOfIndex(index);
if (type == "clear" || type == "manage") {
return label.primary;
}
return JSON.stringify(label);
return typeof label == "string" ? label : label.primary;
}
/**
@ -147,6 +138,11 @@ class ProfileAutoCompleteResult {
* @returns {string} The comment at the specified index
*/
getCommentAt(index) {
const item = this.getAt(index);
if (item.style == "status") {
return JSON.stringify(item);
}
let type = this.getTypeOfIndex(index);
switch (type) {
case "clear":
@ -157,8 +153,12 @@ class ProfileAutoCompleteResult {
return '{"noLearnMore": true }';
}
const item = this.getAt(index);
return item.comment ?? JSON.stringify(this._matchingProfiles[index]);
if (item.comment) {
return item.comment;
}
item.profile = this._matchingProfiles[index];
return JSON.stringify(item);
}
/**

View file

@ -339,8 +339,8 @@ export class LoginAutoCompleteResult {
item =>
new GenericAutocompleteItem(
item.image,
item.title,
item.subtitle,
item.label,
item.secondary,
item.fillMessageName,
item.fillMessageData
)

View file

@ -27,15 +27,15 @@ XPCOMUtils.defineLazyServiceGetter(
export class ParentAutocompleteOption {
image;
title;
subtitle;
label;
secondary;
fillMessageName;
fillMessageData;
constructor(image, title, subtitle, fillMessageName, fillMessageData) {
constructor(image, label, secondary, fillMessageName, fillMessageData) {
this.image = image;
this.title = title;
this.subtitle = subtitle;
this.label = label;
this.secondary = secondary;
this.fillMessageName = fillMessageName;
this.fillMessageData = fillMessageData;
}

View file

@ -707,14 +707,14 @@ This tests the login manager in a secure setting using https. A similar test fil
const promise1 = notifyMenuChanged(1);
sendString("z");
const results1 = await promise1;
checkAutoCompleteResults(results1, [], window.location.host,
checkAutoCompleteResults(results1.labels, [], window.location.host,
"Check popup does not have any login items");
// check that empty results are cached - bug 496466
const promise2 = notifyMenuChanged(1);
sendString("z");
const results2 = await promise2;
checkAutoCompleteResults(results2, [], window.location.host,
checkAutoCompleteResults(results2.labels, [], window.location.host,
"Check popup only has the footer when it opens");
});

View file

@ -735,7 +735,7 @@ This tests the login manager in a insecure setting using http. A similar test fi
const promise1 = notifyMenuChanged(2);
sendString("z");
const results1 = await promise1;
checkAutoCompleteResults(results1, [
checkAutoCompleteResults(results1.labels, [
"This connection is not secure. Logins entered here could be compromised. Learn More",
], window.location.host,
"Check popup does not have any login items");
@ -744,7 +744,7 @@ This tests the login manager in a insecure setting using http. A similar test fi
const promise2 = notifyMenuChanged(2);
sendString("z");
const results2 = await promise2;
checkAutoCompleteResults(results2, [
checkAutoCompleteResults(results2.labels, [
"This connection is not secure. Logins entered here could be compromised. Learn More",
], window.location.host,
"Check popup only has the footer when it opens");

View file

@ -145,7 +145,8 @@ add_task(async function test_delete_first_entry() {
let results = await notifyMenuChanged(3, "name1");
checkAutoCompleteResults(results, ["name1", "name2"], hostname, "two logins should remain after deleting the first");
checkAutoCompleteResults(results.labels, ["name1", "name2"], hostname, "two logins should remain after deleting the first");
let popupState = await getPopupState();
is(popupState.open, true, "Check popup stays open after deleting");
synthesizeKey("KEY_Escape");
@ -177,7 +178,7 @@ add_task(async function test_delete_duplicate_entry() {
// the HTTP would remain.
let results = await notifyMenuChanged(2, "name2");
checkAutoCompleteResults(results, ["name2"], hostname, "one login should remain after deleting the HTTPS name1");
checkAutoCompleteResults(results.labels, ["name2"], hostname, "one login should remain after deleting the HTTPS name1");
let popupState = await getPopupState();
is(popupState.open, true, "Check popup stays open after deleting");
synthesizeKey("KEY_Escape");

View file

@ -49,7 +49,7 @@ add_setup(async () => {
add_task(async function test_initial_focus() {
let results = await notifyMenuChanged(3, "name");
checkAutoCompleteResults(results, ["name", "name1"], hostname, "Two login results");
checkAutoCompleteResults(results.labels, ["name", "name1"], hostname, "Two login results");
synthesizeKey("KEY_ArrowDown");
synthesizeKey("KEY_Enter");
await promiseFormsProcessedInSameProcess();

View file

@ -2,7 +2,7 @@
* 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 item shows image, title & subtitle.
// This item shows image, label & secondary.
// Once selected it will send fillMessageName with fillMessageData
// to the parent actor and response will be used to fill into the field.
export class GenericAutocompleteItem {
@ -10,11 +10,11 @@ export class GenericAutocompleteItem {
style = "generic";
value = "";
constructor(image, title, subtitle, fillMessageName, fillMessageData) {
constructor(image, label, secondary, fillMessageName, fillMessageData) {
this.image = image;
this.label = label;
this.comment = JSON.stringify({
title,
subtitle,
secondary,
fillMessageName,
fillMessageData,
});

View file

@ -223,8 +223,8 @@ export class FormHistoryChild extends JSWindowActorChild {
entry =>
new lazy.GenericAutocompleteItem(
entry.image,
entry.title,
entry.subtitle,
entry.label,
entry.secondary,
entry.fillMessageName,
entry.fillMessageData
)

View file

@ -15,13 +15,18 @@ var gAutocompletePopup =
assert.ok(gAutocompletePopup, "Got autocomplete popup");
var ParentUtils = {
// Returns a object with two fields:
// labels - an array of the labels of the current dropdown
// comments - an array of the comments of the current dropdown
getMenuEntries() {
let entries = [];
let labels = [],
comments = [];
let numRows = gAutocompletePopup.view.matchCount;
for (let i = 0; i < numRows; i++) {
entries.push(gAutocompletePopup.view.getValueAt(i));
labels.push(gAutocompletePopup.view.getLabelAt(i));
comments.push(gAutocompletePopup.view.getCommentAt(i));
}
return entries;
return { labels, comments };
},
cleanUpFormHistory() {
@ -41,8 +46,8 @@ var ParentUtils = {
},
popupshownListener() {
let results = this.getMenuEntries();
sendAsyncMessage("onpopupshown", { results });
let entries = this.getMenuEntries();
sendAsyncMessage("onpopupshown", entries);
},
countEntries(name, value) {
@ -80,6 +85,7 @@ var ParentUtils = {
return false;
}
}, `Waiting for row count change to ${expectedCount}, first value: ${expectedFirstValue}.`);
return this.getMenuEntries();
},

View file

@ -179,9 +179,9 @@ function listenForUnexpectedPopupShown() {
async function popupBy(triggerFn) {
gPopupShownExpected = true;
const promise = new Promise(resolve => {
gPopupShownListener = ({ results }) => {
gPopupShownListener = results => {
gPopupShownExpected = false;
resolve(results);
resolve(results.labels);
};
});
if (triggerFn) {
@ -240,10 +240,10 @@ function promiseNextStorageEvent() {
function satchelCommonSetup() {
let chromeURL = SimpleTest.getTestFileURL("parent_utils.js");
gChromeScript = SpecialPowers.loadChromeScript(chromeURL);
gChromeScript.addMessageListener("onpopupshown", ({ results }) => {
gChromeScript.addMessageListener("onpopupshown", results => {
gLastAutoCompleteResults = results;
if (gPopupShownListener) {
gPopupShownListener({ results });
gPopupShownListener(results);
}
});
@ -292,7 +292,7 @@ function assertValueAfterKeys(input, keys, expectedValue) {
function assertAutocompleteItems(...expectedValues) {
const actualValues = getMenuEntries();
isDeeply(actualValues, expectedValues, "expected autocomplete list");
isDeeply(actualValues.labels, expectedValues, "expected autocomplete list");
}
function deleteSelectedAutocompleteItem() {

View file

@ -42,7 +42,7 @@ add_task(async function test_dropdown_shown_when_type_attribute_changed() {
await openPopupOn(input);
isDeeply(
getMenuEntries(),
getMenuEntries().labels,
["Mozilla", "Firefox", "Thunderbird"],
"Datalist shown after changing input type from button to text.");
input.removeEventListener("click", () => input.setAttribute("type", "text"));

View file

@ -54,7 +54,7 @@ add_task(async function() {
synthesizeKey("c");
is(input.value, "abc", "<input>'s value has to be abc for initial data");
await notifyMenuChanged(4);
let values = getMenuEntries();
let values = getMenuEntries().labels;
is(values.length, 4, "expected count of datalist popup");
for (let i = 0; i < values.length; i++) {
is(values[i], DATALIST_DATA[input.value][i], "expected data #" + i);
@ -66,7 +66,7 @@ add_task(async function() {
synthesizeKey("KEY_ArrowDown");
await promise;
values = getMenuEntries();
values = getMenuEntries().labels;
is(values.length, 5, "expected count of datalist popup");
for (let i = 0; i < values.length; i++) {
is(values[i], DATALIST_DATA[input.value][i], "expected data #" + i);

View file

@ -47,7 +47,7 @@ add_task(async function test_non_duplicate_entries_are_shown_once() {
async function triggerAutofillAndCheckEntriesValue(inputValue, expectedValues) {
await openPopupOn("#form1 > input", { inputValue });
isDeeply(getMenuEntries(), expectedValues, "Matching deduplicated autocomplete list entries with expected values.");
isDeeply(getMenuEntries().labels, expectedValues, "Matching deduplicated autocomplete list entries with expected values.");
}
</script>

View file

@ -81,7 +81,7 @@ add_task(async function test_secure_noFormHistoryOrWarning() {
add_task(async function test_insecure_focusWarning() {
// Form 2 has an insecure action so should show the warning even if password manager is disabled.
await openPopupOn("#form2 > input");
ok(getMenuEntries()[0].includes("Logins entered here could be compromised"),
ok(getMenuEntries().labels[0].includes("Logins entered here could be compromised"),
"Check warning is first");
});
</script>

View file

@ -398,7 +398,7 @@
_adjustAcItem() {
let originalUrl = this.getAttribute("ac-value");
let title = this.getAttribute("ac-comment");
let title = this.getAttribute("ac-label");
this.setAttribute("url", originalUrl);
this.setAttribute("image", this.getAttribute("ac-image"));
this.setAttribute("title", title);
@ -522,8 +522,8 @@
return;
}
let label = this.getAttribute("ac-label");
if (label && JSON.parse(label)?.noLearnMore) {
let comment = this.getAttribute("ac-comment");
if (comment && JSON.parse(comment)?.noLearnMore) {
return;
}
@ -659,9 +659,7 @@
static get inheritedAttributes() {
return {
// getLabelAt:
".line1-label": "text=ac-value",
// getCommentAt:
".line2-label": "text=ac-label",
".line1-label": "text=ac-label",
".ac-site-icon": "src=ac-image",
};
}
@ -681,7 +679,13 @@
`;
}
_adjustAcItem() {}
_adjustAcItem() {
let comment = JSON.parse(this.getAttribute("ac-comment"));
this.querySelector(".line2-label").textContent = comment?.secondary || "";
this.querySelector(".ac-site-icon").collapsed =
this.getAttribute("ac-image") == "";
}
_onOverflow() {}
@ -697,47 +701,28 @@
}
onSecondaryAction() {
const details = JSON.parse(this.getAttribute("ac-label"));
const comment = JSON.parse(this.getAttribute("ac-comment"));
LoginHelper.openPasswordManager(window, {
loginGuid: details?.guid,
loginGuid: comment?.guid,
});
}
static get inheritedAttributes() {
return {
// getLabelAt:
".line1-label": "text=ac-value",
// Don't inherit ac-label with getCommentAt since the label is JSON.
".line1-label": "text=ac-label",
".ac-site-icon": "src=ac-image",
};
}
_adjustAcItem() {
super._adjustAcItem();
let details = JSON.parse(this.getAttribute("ac-label"));
this.querySelector(".line2-label").textContent = details.comment;
}
}
// This type has an action that is triggered when activated. The comment
// for that result should contain a fillMessageName -- the message to send --
// and, optionally a secondary label, for example:
// { "fillMessageName": "Fill:Clear", secondary: "Second Label" }
// for that result should contain a fillMessageName which is the message to send.
class MozAutocompleteActionRichlistitem extends MozAutocompleteTwoLineRichlistitem {
constructor() {
super();
this.selectedByMouseOver = true;
}
_adjustAcItem() {
super._adjustAcItem();
let comment = JSON.parse(this.getAttribute("ac-label"));
this.querySelector(".line2-label").textContent = comment?.secondary || "";
this.querySelector(".ac-site-icon").collapsed =
this.getAttribute("ac-image") == "";
}
}
// A row that conveys status information assigned from the status field
@ -776,12 +761,12 @@
: item;
let comment = JSON.parse(target.getAttribute("ac-comment"));
let statusBox = this.querySelector(".ac-status");
statusBox.textContent = comment?.status || "";
}
_adjustAcItem() {
super._adjustAcItem();
this.#setStatus(this);
this.setAttribute("disabled", "true");
}
@ -794,15 +779,15 @@
}
_adjustAcItem() {
let { primary, secondary, ariaLabel } = JSON.parse(
this.getAttribute("ac-value")
let label = this.getAttribute("ac-label");
this.querySelector(".line1-label").textContent = label;
let { secondary, ariaLabel } = JSON.parse(
this.getAttribute("ac-comment")
);
let line1Label = this.querySelector(".line1-label");
line1Label.textContent = primary.toString();
let line2Label = this.querySelector(".line2-label");
line2Label.textContent = secondary.toString();
line2Label.textContent = secondary ?? "";
if (ariaLabel) {
this.setAttribute("aria-label", ariaLabel);
@ -872,7 +857,7 @@
_adjustAcItem() {
let { generatedPassword, willAutoSaveGeneratedPassword } = JSON.parse(
this.getAttribute("ac-label")
this.getAttribute("ac-comment")
);
let line2Label = this.querySelector(".line2-label");
line2Label.textContent = "";
@ -899,8 +884,7 @@
static get inheritedAttributes() {
return {
// getLabelAt:
".line1-label": "text=ac-value",
// Don't inherit ac-label with getCommentAt since the label is JSON.
".line1-label": "text=ac-label",
};
}
@ -924,7 +908,7 @@
this.querySelector(".labels-wrapper"),
`autocomplete-import-logins-${this.getAttribute("ac-value")}`,
{
host: JSON.parse(this.getAttribute("ac-label")).hostname.replace(
host: JSON.parse(this.getAttribute("ac-comment")).hostname.replace(
/^www\./,
""
),