Bug 1847888 - P3. Add testcases r=mtigley,joschmidt,credential-management-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D190583
This commit is contained in:
Dimi 2023-10-18 18:22:18 +00:00
Родитель 11da4086b3
Коммит b2897a17f3
14 изменённых файлов: 814 добавлений и 476 удалений

Просмотреть файл

@ -16,4 +16,10 @@ support-files = [
["browser_address_doorhanger_tel.js"]
["browser_address_doorhanger_ui.js"]
["browser_address_telemetry.js"]
["browser_edit_address_doorhanger_display.js"]
["browser_edit_address_doorhanger_save_edited_fields.js"]

Просмотреть файл

@ -12,7 +12,10 @@ async function expectSavedAddresses(expectedCount) {
add_setup(async function () {
await SpecialPowers.pushPrefEnv({
set: [["extensions.formautofill.addresses.capture.v2.enabled", true]],
set: [
["extensions.formautofill.addresses.capture.v2.enabled", true],
["extensions.formautofill.addresses.supported", "on"],
],
});
});
@ -137,11 +140,8 @@ add_task(async function test_doorhanger_not_shown_when_autofill_untouched() {
await BrowserTestUtils.withNewTab(
{ gBrowser, url: ADDRESS_FORM_URL },
async function (browser) {
await sleep(1000);
await openPopupOn(browser, "form #given-name");
await sleep(1000);
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
await sleep(1000);
await BrowserTestUtils.synthesizeKey("VK_RETURN", {}, browser);
await waitForAutofill(
browser,
@ -154,7 +154,6 @@ add_task(async function test_doorhanger_not_shown_when_autofill_untouched() {
form.querySelector("input[type=submit]").click();
});
await sleep(1000);
is(PopupNotifications.panel.state, "closed", "Doorhanger is hidden");
}
);
@ -183,7 +182,6 @@ add_task(async function test_doorhanger_not_shown_when_fill_duplicate() {
},
});
await sleep(1000);
is(PopupNotifications.panel.state, "closed", "Doorhanger is hidden");
}
);
@ -228,7 +226,6 @@ add_task(
},
});
await sleep(1000);
is(PopupNotifications.panel.state, "closed", "Doorhanger is hidden");
}
);

Просмотреть файл

@ -0,0 +1,293 @@
"use strict";
const { FormAutofill } = ChromeUtils.importESModule(
"resource://autofill/FormAutofill.sys.mjs"
);
async function expectSavedAddresses(expectedCount) {
const addresses = await getAddresses();
is(
addresses.length,
expectedCount,
`${addresses.length} address in the storage`
);
return addresses;
}
function verifyDoorhangerContent(saved, removed = {}) {
const rows = [
...getNotification().querySelectorAll(`.address-save-update-row-container`),
];
let texts = rows.reduce((acc, cur) => acc + cur.textContent, "");
for (const text of Object.values(saved)) {
ok(texts.includes(text), `Show ${text} in the doorhanger`);
texts = texts.replace(text, "");
}
for (const text of Object.values(removed)) {
ok(texts.includes(text), `Show ${text} in the doorhanger (removed)`);
texts = texts.replace(text, "");
}
is(texts.trim(), "", `Doorhanger shows all the submitted data`);
}
function checkVisibility(element) {
return element.checkVisibility({
checkOpacity: true,
checkVisibilityCSS: true,
});
}
function recordToFormSelector(record) {
let obj = {};
for (const [key, value] of Object.entries(record)) {
obj[`#${key}`] = value;
}
return obj;
}
add_setup(async function () {
await SpecialPowers.pushPrefEnv({
set: [
["extensions.formautofill.addresses.capture.v2.enabled", true],
["extensions.formautofill.addresses.supported", "on"],
],
});
});
async function showDoorhanger(browser, values = null) {
const defaultValues = {
"#given-name": "John",
"#family-name": "Doe",
"#organization": "Mozilla",
"#street-address": "123 Sesame Street",
};
const onPopupShown = waitForPopupShown();
const promise = BrowserTestUtils.browserLoaded(browser);
await focusUpdateSubmitForm(browser, {
focusSelector: "#given-name",
newValues: values ?? defaultValues,
});
await promise;
await onPopupShown;
}
// Save address doorhanger should show description when users has no saved address
add_task(async function test_save_doorhanger_show_description() {
await expectSavedAddresses(0);
await BrowserTestUtils.withNewTab(
{ gBrowser, url: ADDRESS_FORM_URL },
async function (browser) {
await showDoorhanger(browser);
const header = AutofillDoorhanger.header(getNotification());
is(checkVisibility(header), true, "Should always show header");
const description = AutofillDoorhanger.description(getNotification());
is(
checkVisibility(description),
true,
"Should show description when this is the first address saved"
);
}
);
});
// Save address doorhanger should not show description when users has at least one saved address
add_task(async function test_save_doorhanger_hide_description() {
await setStorage(TEST_ADDRESS_1);
await expectSavedAddresses(1);
await BrowserTestUtils.withNewTab(
{ gBrowser, url: ADDRESS_FORM_URL },
async function (browser) {
await showDoorhanger(browser);
const header = AutofillDoorhanger.header(getNotification());
is(checkVisibility(header), true, "Should always show header");
const description = AutofillDoorhanger.description(getNotification());
is(
checkVisibility(description),
false,
"Should not show description when there is at least one saved address"
);
}
);
await removeAllRecords();
});
// Test open edit address popup and then click "learn more" button
add_task(async function test_click_learn_more_button_in_edit_doorhanger() {
await expectSavedAddresses(0);
await BrowserTestUtils.withNewTab(
{ gBrowser, url: ADDRESS_FORM_URL },
async function (browser) {
await showDoorhanger(browser);
let tabOpenPromise = BrowserTestUtils.waitForNewTab(gBrowser, url =>
url.endsWith(AddressSaveDoorhanger.learnMoreURL)
);
await clickAddressDoorhangerButton(
ADDRESS_MENU_BUTTON,
ADDRESS_MENU_LEARN_MORE
);
const tab = await tabOpenPromise;
gBrowser.removeTab(tab);
}
);
});
add_task(async function test_click_address_setting_button_in_edit_doorhanger() {
await expectSavedAddresses(0);
await BrowserTestUtils.withNewTab(
{ gBrowser, url: ADDRESS_FORM_URL },
async function (browser) {
await showDoorhanger(browser);
let tabOpenPromise = BrowserTestUtils.waitForNewTab(
gBrowser,
`about:preferences#${AddressSaveDoorhanger.preferenceURL}`
);
await clickAddressDoorhangerButton(
ADDRESS_MENU_BUTTON,
ADDRESS_MENU_PREFENCE
);
const tab = await tabOpenPromise;
gBrowser.removeTab(tab);
}
);
});
add_task(async function test_address_display_in_save_doorhanger() {
await expectSavedAddresses(0);
const TESTS = [
{
description: "Test submit a form without email and tel fields",
form: {
"#family-name": "Doe",
"#organization": "Mozilla",
"#street-address": "123 Sesame Street",
},
expectedSectionCount: 1,
},
{
description: "Test submit a form with email field",
form: {
"#given-name": "John",
"#organization": "Mozilla",
"#street-address": "123 Sesame Street",
"#email": "test@mozilla.org",
},
expectedSectionCount: 2,
},
{
description: "Test submit a form with tel field",
form: {
"#given-name": "John",
"#family-name": "Doe",
"#organization": "Mozilla",
"#street-address": "123 Sesame Street",
"#tel": "+13453453456",
},
expectedSectionCount: 2,
},
];
for (const TEST of TESTS) {
await BrowserTestUtils.withNewTab(
{ gBrowser, url: ADDRESS_FORM_URL },
async function (browser) {
info(TEST.description);
await showDoorhanger(browser, TEST.form);
is(
getNotification().querySelectorAll(
`.address-save-update-row-container`
).length,
TEST.expectedSectionCount,
`Should have ${TEST.expectedSectionCount} address section`
);
// When the form has no country field, doorhanger shows the default region
verifyDoorhangerContent({
...TEST.form,
country: FormAutofill.DEFAULT_REGION,
});
await clickAddressDoorhangerButton(SECONDARY_BUTTON);
}
);
}
await removeAllRecords();
});
add_task(async function test_show_added_text_in_update_doorhanger() {
await setStorage(TEST_ADDRESS_2);
await expectSavedAddresses(1);
const form = {
...TEST_ADDRESS_2,
email: "test@mozilla.org", // Add email field
"given-name": TEST_ADDRESS_2["given-name"] + " Doe", // Append
"street-address": TEST_ADDRESS_2["street-address"] + " 4F", // Append
};
await BrowserTestUtils.withNewTab(
{ gBrowser, url: ADDRESS_FORM_URL },
async function (browser) {
await showDoorhanger(browser, recordToFormSelector(form));
// When the form has no country field, doorhanger shows the default region
verifyDoorhangerContent({
...form,
country: FormAutofill.DEFAULT_REGION,
});
await clickAddressDoorhangerButton(SECONDARY_BUTTON);
}
);
await removeAllRecords();
});
add_task(async function test_show_removed_text_in_update_doorhanger() {
const SAVED_ADDRESS = {
...TEST_ADDRESS_2,
organization: "Mozilla",
};
await setStorage(SAVED_ADDRESS);
await expectSavedAddresses(1);
// We will ask whether users would like to update "Mozilla" to "mozilla"
const form = {
...SAVED_ADDRESS,
organization: SAVED_ADDRESS.organization.toLowerCase(),
};
await BrowserTestUtils.withNewTab(
{ gBrowser, url: ADDRESS_FORM_URL },
async function (browser) {
await showDoorhanger(browser, recordToFormSelector(form));
// When the form has no country field, doorhanger shows the default region
verifyDoorhangerContent(
{ ...form, country: FormAutofill.DEFAULT_REGION },
{ organization: SAVED_ADDRESS.organization }
);
await clickAddressDoorhangerButton(SECONDARY_BUTTON);
}
);
await removeAllRecords();
});

Просмотреть файл

@ -1,5 +1,7 @@
"use strict";
requestLongerTimeout(2);
const { TelemetryTestUtils } = ChromeUtils.importESModule(
"resource://testing-common/TelemetryTestUtils.sys.mjs"
);
@ -8,11 +10,6 @@ const { AddressTelemetry } = ChromeUtils.importESModule(
"resource://autofill/AutofillTelemetry.sys.mjs"
);
// Preference definitions
const ENABLED_PREF = ENABLED_AUTOFILL_ADDRESSES_PREF;
const AVAILABLE_PREF = AUTOFILL_ADDRESSES_AVAILABLE_PREF;
const CAPTURE_ENABLE_PREF = ENABLED_AUTOFILL_ADDRESSES_CAPTURE_PREF;
// Telemetry definitions
const EVENT_CATEGORY = "address";
@ -260,9 +257,9 @@ async function openTabAndUseAutofillProfile(
add_setup(async function () {
await SpecialPowers.pushPrefEnv({
set: [
[ENABLED_PREF, true],
[AVAILABLE_PREF, "on"],
[CAPTURE_ENABLE_PREF, true],
[ENABLED_AUTOFILL_ADDRESSES_PREF, true],
[AUTOFILL_ADDRESSES_AVAILABLE_PREF, "on"],
["extensions.formautofill.addresses.capture.v2.enabled", true],
],
});
@ -349,9 +346,6 @@ add_task(async function test_popup_opened_form_without_autocomplete() {
});
add_task(async function test_submit_autofill_profile_new() {
await SpecialPowers.pushPrefEnv({
set: [["extensions.formautofill.addresses.capture.v2.enabled", true]],
});
async function test_per_command(
command,
idx,
@ -405,16 +399,12 @@ add_task(async function test_submit_autofill_profile_new() {
...formArgs("submitted", {}, fields, "user_filled", "unavailable"),
];
// FTU
await test_per_command(MAIN_BUTTON, undefined, { 1: 1 }, 1);
await assertTelemetry(expected_content, [
[EVENT_CATEGORY, "show", "capture_doorhanger"],
[EVENT_CATEGORY, "pref", "capture_doorhanger"],
[EVENT_CATEGORY, "save", "capture_doorhanger"],
]);
// Need to close preference tab
BrowserTestUtils.removeTab(gBrowser.selectedTab);
TelemetryTestUtils.assertScalar(
TelemetryTestUtils.getProcessScalars("content"),
SCALAR_DETECTED_SECTION_COUNT,
@ -486,7 +476,7 @@ add_task(async function test_submit_autofill_profile_update() {
useCount
);
SpecialPowers.clearUserPref(ENABLED_PREF);
SpecialPowers.clearUserPref(ENABLED_AUTOFILL_ADDRESSES_PREF);
await removeAllRecords();
}
@ -529,10 +519,10 @@ add_task(async function test_submit_autofill_profile_update() {
[EVENT_CATEGORY, "update", "update_doorhanger"],
]);
await test_per_command(SECONDARY_BUTTON, undefined, { 0: 1, 1: 1 }, 2);
await test_per_command(SECONDARY_BUTTON, undefined, { 0: 1 });
await assertTelemetry(expected_content, [
[EVENT_CATEGORY, "show", "update_doorhanger"],
[EVENT_CATEGORY, "save", "update_doorhanger"],
[EVENT_CATEGORY, "cancel", "update_doorhanger"],
]);
await removeAllRecords();

Просмотреть файл

@ -0,0 +1,216 @@
"use strict";
async function expectSavedAddresses(expectedCount) {
const addresses = await getAddresses();
is(
addresses.length,
expectedCount,
`${addresses.length} address in the storage`
);
return addresses;
}
function recordToFormSelector(record) {
let obj = {};
for (const [key, value] of Object.entries(record)) {
obj[`#${key}`] = value;
}
return obj;
}
add_setup(async function () {
await SpecialPowers.pushPrefEnv({
set: [
["extensions.formautofill.addresses.capture.v2.enabled", true],
["extensions.formautofill.addresses.supported", "on"],
],
});
});
// Test different scenarios when we change something in the edit address dorhanger
add_task(async function test_save_edited_fields() {
await expectSavedAddresses(0);
const initRecord = {
"given-name": "John",
"family-name": "Doe",
organization: "Mozilla",
"street-address": "123 Sesame Street",
tel: "+13453453456",
};
const TESTS = [
{
description: "adding the email field",
editedFields: {
email: "test@mozilla.org",
},
},
{
description: "changing the given-name field",
editedFields: {
name: "Jane",
},
},
{
description: "appending the street-address field",
editedFields: {
"street-address": initRecord["street-address"] + " 4F",
},
},
{
description: "removing some fields",
editedFields: {
name: "",
tel: "",
},
},
{
description: "doing all kinds of stuff",
editedFields: {
organization: initRecord.organization.toLowerCase(),
"address-level1": "CA",
tel: "",
"street-address": initRecord["street-address"] + " Apt.6",
name: "Jane Doe",
},
},
];
for (const TEST of TESTS) {
await BrowserTestUtils.withNewTab(
{ gBrowser, url: ADDRESS_FORM_URL },
async function (browser) {
info(`Test ${TEST.description}`);
const onSavePopupShown = waitForPopupShown();
await focusUpdateSubmitForm(browser, {
focusSelector: "#given-name",
newValues: recordToFormSelector(initRecord),
});
await onSavePopupShown;
const onEditPopupShown = waitForPopupShown();
await clickAddressDoorhangerButton(EDIT_ADDRESS_BUTTON);
await onEditPopupShown;
fillEditDoorhanger(TEST.editedFields);
await clickAddressDoorhangerButton(MAIN_BUTTON);
}
);
const expectedRecord = normalizeAddressFields({
...initRecord,
...TEST.editedFields,
});
const addresses = await expectSavedAddresses(1);
for (const [key, value] of Object.entries(expectedRecord)) {
is(addresses[0][key] ?? "", value, `${key} field is saved`);
}
await removeAllRecords();
}
});
// This test tests edit doorhanger "save" & "cancel" buttons work correctly
// when the edit doorhanger is triggered in an save doorhanger
add_task(async function test_edit_doorhanger_triggered_by_save_doorhanger() {
for (const CLICKED_BUTTON of [MAIN_BUTTON, SECONDARY_BUTTON]) {
await expectSavedAddresses(0);
const initRecord = {
"given-name": "John",
"family-name": "Doe",
organization: "Mozilla",
"street-address": "123 Sesame Street",
tel: "+13453453456",
};
const editRecord = {
email: "test@mozilla.org",
};
await BrowserTestUtils.withNewTab(
{ gBrowser, url: ADDRESS_FORM_URL },
async browser => {
info(
`Test clicking ${CLICKED_BUTTON == MAIN_BUTTON ? "save" : "cancel"}`
);
const onSavePopupShown = waitForPopupShown();
await focusUpdateSubmitForm(browser, {
focusSelector: "#given-name",
newValues: recordToFormSelector(initRecord),
});
await onSavePopupShown;
const onEditPopupShown = waitForPopupShown();
await clickAddressDoorhangerButton(EDIT_ADDRESS_BUTTON);
await onEditPopupShown;
fillEditDoorhanger(editRecord);
await clickAddressDoorhangerButton(CLICKED_BUTTON);
}
);
await expectSavedAddresses(CLICKED_BUTTON == MAIN_BUTTON ? 1 : 0);
await removeAllRecords();
}
});
// This test tests edit doorhanger "save" & "cancel" buttons work correctly
// when the edit doorhanger is triggered in an update doorhnager
add_task(async function test_edit_doorhanger_triggered_by_update_doorhanger() {
for (const CLICKED_BUTTON of [MAIN_BUTTON, SECONDARY_BUTTON]) {
// TEST_ADDRESS_2 doesn't contain email field
await setStorage(TEST_ADDRESS_2);
await expectSavedAddresses(1);
const initRecord = {
"given-name": TEST_ADDRESS_2["given-name"],
"street-address": TEST_ADDRESS_2["street-address"],
country: TEST_ADDRESS_2.country,
email: "test@mozilla.org",
};
const editRecord = {
email: "test@mozilla.org",
};
await BrowserTestUtils.withNewTab(
{ gBrowser, url: ADDRESS_FORM_URL },
async browser => {
info(
`Test clicking ${CLICKED_BUTTON == MAIN_BUTTON ? "save" : "cancel"}`
);
const onUpdatePopupShown = waitForPopupShown();
await focusUpdateSubmitForm(browser, {
focusSelector: "#given-name",
newValues: recordToFormSelector(initRecord),
});
await onUpdatePopupShown;
const onEditPopupShown = waitForPopupShown();
await clickAddressDoorhangerButton(EDIT_ADDRESS_BUTTON);
await onEditPopupShown;
fillEditDoorhanger(editRecord);
await clickAddressDoorhangerButton(CLICKED_BUTTON);
}
);
const addresses = await expectSavedAddresses(1);
const expectedRecord =
CLICKED_BUTTON == MAIN_BUTTON
? { ...initRecord, ...editRecord }
: TEST_ADDRESS_2;
for (const [key, value] of Object.entries(expectedRecord)) {
is(addresses[0][key] ?? "", value, `${key} field is saved`);
}
await removeAllRecords();
}
});

Просмотреть файл

@ -0,0 +1,116 @@
"use strict";
const SUBMIT_RECORD = {
"given-name": "John",
"family-name": "Doe",
organization: "Mozilla",
"street-address": "123 Sesame Street",
tel: "+13453453456",
};
const TEST_CASE = [
{
description: "adding the email field",
editedFields: {
email: "test@mozilla.org",
},
},
{
description: "changing the given-name field",
editedFields: {
name: "Jane",
},
},
{
description: "appending the street-address field",
editedFields: {
"street-address": SUBMIT_RECORD["street-address"] + " 4F",
},
},
{
description: "removing some fields",
editedFields: {
name: "",
tel: "",
},
},
{
description: "doing all kinds of stuff",
editedFields: {
organization: SUBMIT_RECORD.organization.toLowerCase(),
"address-level1": "CA",
tel: "",
"street-address": SUBMIT_RECORD["street-address"] + " Apt.6",
name: "Jane Doe",
},
},
];
async function expectSavedAddresses(expectedCount) {
const addresses = await getAddresses();
is(
addresses.length,
expectedCount,
`${addresses.length} address in the storage`
);
return addresses;
}
function recordToFormSelector(record) {
let obj = {};
for (const [key, value] of Object.entries(record)) {
obj[`#${key}`] = value;
}
return obj;
}
add_setup(async function () {
await SpecialPowers.pushPrefEnv({
set: [
["extensions.formautofill.addresses.capture.v2.enabled", true],
["extensions.formautofill.addresses.supported", "on"],
],
});
});
// Test different scenarios when we change something in the edit address dorhanger
add_task(async function test_save_edited_fields() {
await expectSavedAddresses(0);
for (const TEST of TEST_CASE) {
await BrowserTestUtils.withNewTab(
{ gBrowser, url: ADDRESS_FORM_URL },
async function (browser) {
info(`Test ${TEST.description}`);
let onPopupShown = waitForPopupShown();
await focusUpdateSubmitForm(browser, {
focusSelector: "#given-name",
newValues: recordToFormSelector(SUBMIT_RECORD),
});
await onPopupShown;
onPopupShown = waitForPopupShown();
await clickAddressDoorhangerButton(EDIT_ADDRESS_BUTTON);
await onPopupShown;
fillEditDoorhanger(TEST.editedFields);
await clickAddressDoorhangerButton(MAIN_BUTTON);
}
);
const expectedRecord = normalizeAddressFields({
...SUBMIT_RECORD,
...TEST.editedFields,
});
const addresses = await expectSavedAddresses(1);
for (const [key, value] of Object.entries(expectedRecord)) {
is(addresses[0][key] ?? "", value, `${key} field is saved`);
}
await removeAllRecords();
}
});

Просмотреть файл

@ -48,6 +48,3 @@ skip-if = [
["browser_remoteiframe.js"]
["browser_submission_in_private_mode.js"]
["browser_update_doorhanger.js"]
skip-if = ["true"] # bug 1426981 # Bug 1445538

Просмотреть файл

@ -82,6 +82,8 @@ add_task(async function test_saveAddress() {
"VK_TAB",
TEST_ADDRESS_1["family-name"],
"VK_TAB",
TEST_ADDRESS_1.organization,
"VK_TAB",
TEST_ADDRESS_1["street-address"],
"VK_TAB",
TEST_ADDRESS_1["address-level2"],
@ -90,8 +92,6 @@ add_task(async function test_saveAddress() {
"VK_TAB",
TEST_ADDRESS_1["postal-code"],
"VK_TAB",
TEST_ADDRESS_1.organization,
"VK_TAB",
// TEST_ADDRESS_1.country, // Country is already US
"VK_TAB",
TEST_ADDRESS_1.tel,

Просмотреть файл

@ -1,189 +0,0 @@
"use strict";
/**
* Note that this testcase is built based on the assumption that subtests are
* run in order. So if you're going to add a new subtest, please add it in the end.
*/
add_setup(async function () {
await SpecialPowers.pushPrefEnv({
set: [["extensions.formautofill.addresses.capture.enabled", true]],
});
});
add_task(async function test_update_address() {
await setStorage(TEST_ADDRESS_1);
let addresses = await getAddresses();
is(addresses.length, 1, "1 address in storage");
await BrowserTestUtils.withNewTab(
{ gBrowser, url: FORM_URL },
async function (browser) {
// Autofill address fields
await openPopupOn(browser, "form #organization");
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
await BrowserTestUtils.synthesizeKey("VK_RETURN", {}, browser);
await waitForAutofill(browser, "#tel", addresses[0].tel);
// Update address fields and submit
let onPopupShown = waitForPopupShown();
await focusUpdateSubmitForm(browser, {
focusSelector: "#organization",
newValues: {
"#organization": "Mozilla",
},
});
await onPopupShown;
// Choose to update address by doorhanger
let onUpdated = waitForStorageChangedEvents("update");
await clickDoorhangerButton(MAIN_BUTTON);
await onUpdated;
}
);
addresses = await getAddresses();
is(addresses.length, 1, "Still 1 address in storage");
is(addresses[0].organization, "Mozilla", "Verify the organization field");
});
add_task(async function test_create_new_address() {
let addresses = await getAddresses();
is(addresses.length, 1, "1 address in storage");
await BrowserTestUtils.withNewTab(
{ gBrowser, url: FORM_URL },
async function (browser) {
// Autofill address fields
await openPopupOn(browser, "form #tel");
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
await BrowserTestUtils.synthesizeKey("VK_RETURN", {}, browser);
await waitForAutofill(browser, "#tel", addresses[0].tel);
let onPopupShown = waitForPopupShown();
await focusUpdateSubmitForm(browser, {
focusSelector: "#tel",
newValues: {
"#tel": "+1234567890",
},
});
await onPopupShown;
// Choose to add address by doorhanger
let onAdded = waitForStorageChangedEvents("add");
await clickDoorhangerButton(SECONDARY_BUTTON);
await onAdded;
}
);
addresses = await getAddresses();
is(addresses.length, 2, "2 addresses in storage");
is(addresses[1].tel, "+1234567890", "Verify the tel field");
});
add_task(async function test_create_new_address_merge() {
let addresses = await getAddresses();
is(addresses.length, 2, "2 addresses in storage");
await BrowserTestUtils.withNewTab(
{ gBrowser, url: FORM_URL },
async function (browser) {
await openPopupOn(browser, "form #tel");
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
await BrowserTestUtils.synthesizeKey("VK_RETURN", {}, browser);
await waitForAutofill(browser, "#tel", addresses[1].tel);
// Choose the latest address and revert to the original phone number
let onPopupShown = waitForPopupShown();
await focusUpdateSubmitForm(browser, {
focusSelector: "#tel",
newValues: {
"#tel": "+16172535702",
},
});
await onPopupShown;
await clickDoorhangerButton(SECONDARY_BUTTON);
}
);
addresses = await getAddresses();
is(addresses.length, 2, "Still 2 addresses in storage");
});
add_task(async function test_submit_untouched_fields() {
let addresses = await getAddresses();
is(addresses.length, 2, "2 addresses in storage");
await BrowserTestUtils.withNewTab(
{ gBrowser, url: FORM_URL },
async function (browser) {
await openPopupOn(browser, "form #organization");
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
await BrowserTestUtils.synthesizeKey("VK_RETURN", {}, browser);
await waitForAutofill(browser, "#tel", addresses[0].tel);
let onPopupShown = waitForPopupShown();
await SpecialPowers.spawn(browser, [], async function () {
let form = content.document.getElementById("form");
let tel = form.querySelector("#tel");
tel.value = "12345"; // ".value" won't change the highlight status.
});
await focusUpdateSubmitForm(browser, {
focusSelector: "#tel",
newValues: {
"#organization": "Organization",
},
});
await onPopupShown;
let onUpdated = waitForStorageChangedEvents("update");
await clickDoorhangerButton(MAIN_BUTTON);
await onUpdated;
}
);
addresses = await getAddresses();
is(addresses.length, 2, "Still 2 addresses in storage");
is(addresses[0].organization, "Organization", "organization should change");
is(addresses[0].tel, "+16172535702", "tel should remain unchanged");
});
add_task(async function test_submit_reduced_fields() {
let addresses = await getAddresses();
is(addresses.length, 2, "2 addresses in storage");
let url = BASE_URL + "autocomplete_simple_basic.html";
await BrowserTestUtils.withNewTab(
{ gBrowser, url },
async function (browser) {
await openPopupOn(browser, "form#simple input[name=tel]");
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
await BrowserTestUtils.synthesizeKey("VK_RETURN", {}, browser);
await waitForAutofill(browser, "form #simple_tel", "6172535702");
let onPopupShown = waitForPopupShown();
await focusUpdateSubmitForm(browser, {
formSelector: "form#simple",
focusSelector: "form #simple_tel",
newValues: {
"input[name=tel]": "123456789",
},
});
await onPopupShown;
let onUpdated = waitForStorageChangedEvents("update");
await clickDoorhangerButton(MAIN_BUTTON);
await onUpdated;
}
);
addresses = await getAddresses();
is(addresses.length, 2, "Still 2 addresses in storage");
is(addresses[0].tel, "123456789", "tel should should be changed");
is(addresses[0]["postal-code"], "02139", "postal code should be kept");
});

Просмотреть файл

@ -11,6 +11,15 @@ const { FormAutofillParent } = ChromeUtils.importESModule(
"resource://autofill/FormAutofillParent.sys.mjs"
);
const { AutofillDoorhanger, AddressEditDoorhanger, AddressSaveDoorhanger } =
ChromeUtils.importESModule(
"resource://autofill/FormAutofillPrompter.sys.mjs"
);
const { FormAutofillNameUtils } = ChromeUtils.importESModule(
"resource://gre/modules/shared/FormAutofillNameUtils.sys.mjs"
);
const MANAGE_ADDRESSES_DIALOG_URL =
"chrome://formautofill/content/manageAddresses.xhtml";
const MANAGE_CREDIT_CARDS_DIALOG_URL =
@ -185,6 +194,10 @@ const TEST_CREDIT_CARD_5 = {
const MAIN_BUTTON = "button";
const SECONDARY_BUTTON = "secondaryButton";
const MENU_BUTTON = "menubutton";
const EDIT_ADDRESS_BUTTON = "edit";
const ADDRESS_MENU_BUTTON = "addressMenuButton";
const ADDRESS_MENU_LEARN_MORE = "learnMore";
const ADDRESS_MENU_PREFENCE = "preference";
/**
* Collection of timeouts that are used to ensure something should not happen.
@ -715,39 +728,66 @@ function waitForPopupShown() {
/**
* Clicks the popup notification button and wait for popup hidden.
*
* @param {string} button The button type in popup notification.
* @param {string} buttonType The button type in popup notification.
* @param {number} index The action's index in menu list.
*/
async function clickDoorhangerButton(button, index) {
async function clickDoorhangerButton(buttonType, index = 0) {
let popuphidden = BrowserTestUtils.waitForEvent(
PopupNotifications.panel,
"popuphidden"
);
if (button == MAIN_BUTTON || button == SECONDARY_BUTTON) {
EventUtils.synthesizeMouseAtCenter(getNotification()[button], {});
} else if (button == MENU_BUTTON) {
let button;
if (buttonType == MAIN_BUTTON || buttonType == SECONDARY_BUTTON) {
button = getNotification()[buttonType];
} else if (buttonType == MENU_BUTTON) {
// Click the dropmarker arrow and wait for the menu to show up.
info("expecting notification menu button present");
await BrowserTestUtils.waitForCondition(() => getNotification().menubutton);
await sleep(2000); // menubutton needs extra time for binding
let notification = getNotification();
ok(notification.menubutton, "notification menupopup displayed");
let dropdownPromise = BrowserTestUtils.waitForEvent(
notification.menupopup,
"popupshown"
);
await EventUtils.synthesizeMouseAtCenter(notification.menubutton, {});
notification.menubutton.click();
info("expecting notification popup show up");
await dropdownPromise;
let actionMenuItem = notification.querySelectorAll("menuitem")[index];
await EventUtils.synthesizeMouseAtCenter(actionMenuItem, {});
button = notification.querySelectorAll("menuitem")[index];
}
button.click();
info("expecting notification popup hidden");
await popuphidden;
}
async function clickAddressDoorhangerButton(buttonType, subType) {
const notification = getNotification();
let button;
if (buttonType == EDIT_ADDRESS_BUTTON) {
button = AddressSaveDoorhanger.editButton(notification);
} else if (buttonType == ADDRESS_MENU_BUTTON) {
const menu = AutofillDoorhanger.menuButton(notification);
const promise = BrowserTestUtils.waitForEvent(menu.menupopup, "popupshown");
menu.click();
await promise;
if (subType == ADDRESS_MENU_PREFENCE) {
button = AutofillDoorhanger.preferenceButton(notification);
} else if (subType == ADDRESS_MENU_LEARN_MORE) {
button = AutofillDoorhanger.learnMoreButton(notification);
}
} else {
await clickDoorhangerButton(buttonType);
return;
}
EventUtils.synthesizeMouseAtCenter(button, {});
}
function getDoorhangerCheckbox() {
return getNotification().checkbox;
}
@ -1120,6 +1160,31 @@ async function add_autofill_heuristic_tests(patterns, fixturePathPrefix = "") {
add_heuristic_tests(patterns, fixturePathPrefix, { testAutofill: true });
}
function fillEditDoorhanger(record) {
const notification = getNotification();
for (const [key, value] of Object.entries(record)) {
const id = AddressEditDoorhanger.getInputId(key);
const element = notification.querySelector(`#${id}`);
element.value = value;
}
}
// TODO: This function should be removed. We should make normalizeFields in
// FormAutofillStorageBase.sys.mjs static and using it directly
function normalizeAddressFields(record) {
let normalized = { ...record };
if (normalized.name != undefined) {
let nameParts = FormAutofillNameUtils.splitName(normalized.name);
normalized["given-name"] = nameParts.given;
normalized["additional-name"] = nameParts.middle;
normalized["family-name"] = nameParts.family;
delete normalized.name;
}
return normalized;
}
add_setup(function () {
OSKeyStoreTestUtils.setup();
});

Просмотреть файл

@ -13,8 +13,6 @@ support-files = [
"formautofill_common.js",
]
["test_address_level_1_submission.html"]
["test_autofill_and_ordinal_forms.html"]
["test_autofocus_form.html"]
@ -30,5 +28,3 @@ skip-if = ["verify"]
["test_multi_locale_CA_address_form.html"]
["test_multiple_forms.html"]
["test_on_address_submission.html"]

Просмотреть файл

@ -1,99 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Test autofill submission for a country without address-level1</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/EventUtils.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: Test autofill submission for a country without address-level1
<script>
/* import-globals-from ../../../../../toolkit/components/satchel/test/satchel_common.js */
"use strict";
const TEST_ADDRESSES = [{
organization: "Mozilla",
"street-address": "123 Sesame Street",
country: "DE",
timesUsed: 1,
}];
add_task(async function test_DE_is_valid_testcase() {
await SpecialPowers.pushPrefEnv({
set: [
["extensions.formautofill.addresses.capture.enabled", true],
["extensions.formautofill.addresses.supportedCountries", "US,CA,DE"],
["extensions.formautofill.creditCards.supportedCountries", "US,CA,DE"],
],
});
let chromeScript = SpecialPowers.loadChromeScript(function test_country_data() {
/* eslint-env mozilla/chrome-script */
const {AddressDataLoader} = ChromeUtils.importESModule("resource://gre/modules/shared/FormAutofillUtils.sys.mjs");
let data = AddressDataLoader.getData("DE");
addMessageListener("CheckSubKeys", () => {
return !data.defaultLocale.sub_keys;
});
});
SimpleTest.registerCleanupFunction(() => {
chromeScript.destroy();
});
let result = await chromeScript.sendQuery("CheckSubKeys");
ok(result, "Check that there are no sub_keys for the test country");
});
add_task(async function test_form_will_submit_without_sub_keys() {
await SpecialPowers.pushPrefEnv({
set: [
// This needs to match the country in the previous test and must have no sub_keys.
["browser.search.region", "DE"],
["extensions.formautofill.addresses.capture.enabled", true],
["extensions.formautofill.addresses.supportedCountries", "US,CA,DE"],
["extensions.formautofill.addresses.supported", "detect"]
],
});
// Click a field to get the form handler created
await focusAndWaitForFieldsIdentified("input[autocomplete='organization']");
let loadPromise = new Promise(resolve => {
/* eslint-disable-next-line mozilla/balanced-listeners */
document.getElementById("submit_frame").addEventListener("load", resolve);
});
clickOnElement("input[type=submit]");
await onStorageChanged("add");
// Check if timesUsed is set correctly
let matching = await checkAddresses(TEST_ADDRESSES);
ok(matching, "Address saved as expected");
await loadPromise;
isnot(window.submit_frame.location.href, "about:blank", "Check form submitted");
});
</script>
<div>
<!-- Submit to the frame so that the test doesn't get replaced. We don't return
-- false in onsubmit since we're testing the submission succeeds. -->
<iframe id="submit_frame" name="submit_frame"></iframe>
<form action="/" target="submit_frame" method="POST">
<p><label>organization: <input autocomplete="organization" value="Mozilla"></label></p>
<p><label>streetAddress: <input autocomplete="street-address" value="123 Sesame Street"></label></p>
<p><label>address-level1: <select autocomplete="address-level1">
<option selected>AL</option>
<option>AK</option>
</select></label></p>
<p><label>country: <input autocomplete="country" value="DE"></label></p>
<p><input type="submit"></p>
</form>
</div>
</body>
</html>

Просмотреть файл

@ -1,118 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Test autofill submit</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/EventUtils.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: check if address is saved/updated correctly
<script>
/* import-globals-from ../../../../../toolkit/components/satchel/test/satchel_common.js */
"use strict";
let TEST_ADDRESSES = [{
organization: "Sesame Street",
country: "US",
"street-address": "123 Sesame Street.",
tel: "+13453453456",
}, {
organization: "Mozilla",
country: "US",
"street-address": "331 E. Evelyn Avenue",
tel: "+16509030800",
}];
add_task(async function setup_prefs() {
await SpecialPowers.pushPrefEnv({
set: [
["extensions.formautofill.addresses.enabled", true],
["extensions.formautofill.addresses.capture.enabled", true],
],
});
});
initPopupListener();
// Submit first address for saving.
add_task(async function check_storage_after_form_submitted() {
for (let key in TEST_ADDRESSES[0]) {
await setInput("#" + key, TEST_ADDRESSES[0][key]);
}
clickOnElement("input[type=submit]");
let expectedAddresses = TEST_ADDRESSES.slice(0, 1);
await onStorageChanged("add");
// Check if timesUsed is set correctly
expectedAddresses[0].timesUsed = 1;
let matching = await checkAddresses(expectedAddresses);
ok(matching, "Address saved as expected");
delete expectedAddresses[0].timesUsed;
});
// Submit another new address.
add_task(async function check_storage_after_another_address_submitted() {
await SpecialPowers.pushPrefEnv({"set": [["privacy.reduceTimerPrecision", false]]});
document.querySelector("form").reset();
for (let key in TEST_ADDRESSES[1]) {
await setInput("#" + key, TEST_ADDRESSES[1][key]);
}
clickOnElement("input[type=submit]");
// The 2nd test address should be on the top since it's the last used one.
let addressesInMenu = TEST_ADDRESSES.slice(1);
addressesInMenu.push(TEST_ADDRESSES[0]);
// let expectedAddresses = TEST_ADDRESSES.slice(0);
await onStorageChanged("add");
let matching = await checkAddresses(TEST_ADDRESSES);
ok(matching, "New address saved as expected");
await setInput("#organization", "");
synthesizeKey("KEY_ArrowDown");
await expectPopup();
checkMenuEntries(addressesInMenu.map(address =>
JSON.stringify({primary: address.organization, secondary: address["street-address"]})
));
});
// Submit another new address that is mergeable.
add_task(async function new_address_submitted_and_merged() {
// TODO: Bug Bug 1812294
});
// Submit an updated autofill address and merge.
add_task(async function check_storage_after_form_submitted() {
// TODO: Bug Bug 1812294
});
// Submit a subset address manually.
add_task(async function submit_subset_manually() {
// TODO: Bug Bug 1812294
});
</script>
<div>
<form onsubmit="return false">
<p>This is a basic form for submitting test.</p>
<p><label>organization: <input id="organization" name="organization" autocomplete="organization" type="text"></label></p>
<p><label>streetAddress: <input id="street-address" name="street-address" autocomplete="street-address" type="text"></label></p>
<p><label>tel: <input id="tel" name="tel" autocomplete="tel" type="text"></label></p>
<p><label>country: <input id="country" name="country" autocomplete="country" type="text"></label></p>
<p><input type="submit"></p>
</form>
</div>
</body>
</html>

Просмотреть файл

@ -55,7 +55,7 @@ let CONTENT = {};
* The UI data sourced from the `CONTENT` variable is used for rendering. Derived classes
* should override the `render()` method to customize the layout.
*/
class AutofillDoorhanger {
export class AutofillDoorhanger {
/**
* Constructs an instance of the `AutofillDoorhanger` class.
*
@ -63,6 +63,14 @@ class AutofillDoorhanger {
* @param {object} oldRecord The old record that can be merged with the new record
* @param {object} newRecord The new record submitted by users
*/
static headerClass = "address-capture-header";
static descriptionClass = "address-capture-description";
static contentClass = "address-capture-content";
static menuButtonId = "address-capture-menu-button";
static preferenceURL = null;
static learnMoreURL = null;
constructor(browser, oldRecord, newRecord) {
this.browser = browser;
this.oldRecord = oldRecord;
@ -98,27 +106,63 @@ class AutofillDoorhanger {
*/
// The container of the header part
static header(panel) {
return panel.querySelector(`.${AutofillDoorhanger.headerClass}`);
}
get header() {
return this.panel.querySelector(".address-capture-header");
return AutofillDoorhanger.header(this.panel);
}
// The container of the description part
static description(panel) {
return panel.querySelector(`.${AutofillDoorhanger.descriptionClass}`);
}
get description() {
return this.panel.querySelector(".address-capture-description");
return AutofillDoorhanger.description(this.panel);
}
// The container of the content part
static content(panel) {
return panel.querySelector(`.${AutofillDoorhanger.contentClass}`);
}
get content() {
return this.panel.querySelector(".address-capture-content");
return AutofillDoorhanger.content(this.panel);
}
static menuButton(panel) {
return panel.querySelector(`#${AutofillDoorhanger.menuButtonId}`);
}
get menuButton() {
return AutofillDoorhanger.menuButton(this.panel);
}
static preferenceButton(panel) {
const menu = AutofillDoorhanger.menuButton(panel);
return menu.menupopup.querySelector(
`[data-l10n-id=address-capture-manage-address-button]`
);
}
static learnMoreButton(panel) {
const menu = AutofillDoorhanger.menuButton(panel);
return menu.menupopup.querySelector(
`[data-l10n-id=address-capture-learn-more-button]`
);
}
get preferenceURL() {
return this.constructor.preferenceURL;
}
get learnMoreURL() {
return this.constructor.learnMoreURL;
}
onMenuItemClick(evt) {
if (evt == "open-pref") {
this.browser.ownerGlobal.openPreferences("privacy-address-autofill");
this.browser.ownerGlobal.openPreferences(this.preferenceURL);
} else if (evt == "learn-more") {
const url =
Services.urlFormatter.formatURLPref("app.support.baseURL") +
"automatically-fill-your-address-web-forms";
this.learnMoreURL;
this.browser.ownerGlobal.openWebLinkIn(url, "tab", {
relatedToCurrent: true,
});
@ -141,16 +185,12 @@ class AutofillDoorhanger {
this.doc.l10n.setAttributes(text, this.ui.header.l10nId);
// Render the menu button
const menuButtonId = "address-capture-menu-button";
if (
!this.ui.menu?.length ||
this.header.querySelector(`#${menuButtonId}`)
) {
if (!this.ui.menu?.length || AutofillDoorhanger.menuButton(this.panel)) {
return;
}
const button = this.doc.createXULElement("toolbarbutton");
button.setAttribute("id", menuButtonId);
button.setAttribute("id", AutofillDoorhanger.menuButtonId);
const menupopup = this.doc.createXULElement("menupopup");
menupopup.setAttribute("class", "toolbar-menupopup");
@ -273,6 +313,7 @@ class AutofillDoorhanger {
return {
label: msg.attributes.find(x => x.name == "label").value,
accessKey: msg.attributes.find(x => x.name == "accessKey").value,
dismiss: param.dismiss,
};
}
@ -302,7 +343,11 @@ class AutofillDoorhanger {
}
}
class AddressSaveDoorhanger extends AutofillDoorhanger {
export class AddressSaveDoorhanger extends AutofillDoorhanger {
static preferenceURL = "privacy-address-autofill";
static learnMoreURL = "automatically-fill-your-address-web-forms";
static editButtonId = "address-capture-edit-address-button";
#editAddressCb = null;
constructor(browser, oldRecord, newRecord, editAddressCb) {
@ -311,6 +356,13 @@ class AddressSaveDoorhanger extends AutofillDoorhanger {
this.#editAddressCb = editAddressCb;
}
static editButton(panel) {
return panel.querySelector(`#${AddressSaveDoorhanger.editButtonId}`);
}
get editButton() {
return AddressSaveDoorhanger.editButton(this.panel);
}
/**
* Formats a line by comparing the old and the new address field and returns an array of
* <span> elements that represents the formatted line.
@ -399,6 +451,7 @@ class AddressSaveDoorhanger extends AutofillDoorhanger {
field => [this.oldRecord[field], this.newRecord[field]]
);
break;
case "country":
case "tel":
case "email":
case "organization":
@ -458,10 +511,9 @@ class AddressSaveDoorhanger extends AutofillDoorhanger {
this.content.appendChild(section);
// Put the edit address button in the first section
const editButtonId = "address-capture-edit-address-button";
if (!this.doc.getElementById(editButtonId)) {
if (!AddressSaveDoorhanger.editButton(this.panel)) {
const button = this.doc.createXULElement("toolbarbutton");
button.setAttribute("id", editButtonId);
button.setAttribute("id", AddressSaveDoorhanger.editButtonId);
// The element will be removed after the popup is closed
/* eslint-disable mozilla/balanced-listeners */
@ -479,9 +531,9 @@ class AddressSaveDoorhanger extends AutofillDoorhanger {
* Address Update doorhanger and Address Save doorhanger have the same implementation.
* The only difference is UI.
*/
class AddressUpdateDoorhanger extends AddressSaveDoorhanger {}
export class AddressUpdateDoorhanger extends AddressSaveDoorhanger {}
class AddressEditDoorhanger extends AutofillDoorhanger {
export class AddressEditDoorhanger extends AutofillDoorhanger {
constructor(browser, record) {
// Address edit dialog doesn't have "old" record
super(browser, null, record);
@ -694,7 +746,7 @@ class AddressEditDoorhanger extends AutofillDoorhanger {
input = this.doc.createElement("input");
}
input.setAttribute("id", AddressEditDoorhanger.#getInputId(fieldName));
input.setAttribute("id", AddressEditDoorhanger.getInputId(fieldName));
input.value = this.#getFieldDisplayData(fieldName) ?? null;
div.appendChild(input);
@ -706,7 +758,7 @@ class AddressEditDoorhanger extends AutofillDoorhanger {
return regex;
}
static #getInputId(fieldName) {
static getInputId(fieldName) {
return `address-edit-${fieldName}-input`;
}
@ -753,7 +805,13 @@ CONTENT = {
sections: [
{
imgClass: "address-capture-img-address",
categories: ["name", "organization", "street-address", "address"],
categories: [
"name",
"organization",
"street-address",
"address",
"country",
],
},
{
imgClass: "address-capture-img-email",
@ -806,7 +864,13 @@ CONTENT = {
sections: [
{
imgClass: "address-capture-img-address",
categories: ["name", "organization", "street-address", "address"],
categories: [
"name",
"organization",
"street-address",
"address",
"country",
],
},
{
imgClass: "address-capture-img-email",
@ -862,13 +926,14 @@ CONTENT = {
footer: {
mainAction: {
l10nId: "address-capture-save-button",
callbackState: "create",
callbackState: "edit",
confirmationHintId: "confirmation-hint-address-created",
},
secondaryActions: [
{
l10nId: "address-capture-cancel-button",
callbackState: "cancel",
dismiss: true,
},
],
},
@ -880,7 +945,7 @@ CONTENT = {
},
addCreditCard: {
notificationId: "autofill-credit-card",
notificationId: "autofill-credit-card-add",
message: formatStringFromName("saveCreditCardMessage", [brandShortName]),
descriptionLabel: GetStringFromName("saveCreditCardDescriptionLabel"),
descriptionIcon: true,
@ -947,7 +1012,7 @@ CONTENT = {
},
},
updateCreditCard: {
notificationId: "autofill-credit-card",
notificationId: "autofill-credit-card-update",
message: GetStringFromName("updateCreditCardMessage"),
descriptionLabel: GetStringFromName("updateCreditCardDescriptionLabel"),
descriptionIcon: true,
@ -1344,13 +1409,16 @@ export let FormAutofillPrompter = {
newRecord
);
if (state == "create") {
if (state == "edit") {
// If users choose "save" in the edit address doorhanger, we don't need
// to show the save/update doorhanger after the edit address doorhanger
// is closed
recordToSave = editedRecord;
chromeWin.PopupNotifications.remove(this._addrSaveDoorhanger);
resolve({ state, confimationHintId: null });
resolve({
state: isSave ? "create" : "update",
confimationHintId: null,
});
}
};