commit d675f72925235c31495d00146b8d404aa2b8de2a
parent 224aca34f75894f977596fb4fb711d8ce9554727
Author: Tanvi Manku <tanvi.manku@gmail.com>
Date: Tue, 16 Dec 2025 02:05:29 +0000
Bug 2001254 - Create a subpage for managing addresses. r=dimi,fluent-reviewers,mkennedy,akulyk,desktop-theme-reviewers,flod,mtigley,hjones
Differential Revision: https://phabricator.services.mozilla.com/D273365
Diffstat:
6 files changed, 285 insertions(+), 23 deletions(-)
diff --git a/browser/components/preferences/main.js b/browser/components/preferences/main.js
@@ -1785,6 +1785,85 @@ Preferences.addSetting({
},
});
+Preferences.addSetting({
+ /** @type {{ _removeAddressDialogStrings: string[] } & SettingConfig} */
+ id: "address-item",
+ _removeAddressDialogStrings: [],
+ onUserClick(e) {
+ const action = e.target.getAttribute("action");
+ const guid = e.target.getAttribute("guid");
+ if (action === "remove") {
+ let [title, confirm, cancel] = this._removeAddressDialogStrings;
+ FormAutofillPreferences.prototype.openRemoveAddressDialog(
+ guid,
+ window.browsingContext.topChromeWindow.browsingContext,
+ title,
+ confirm,
+ cancel
+ );
+ } else if (action === "edit") {
+ FormAutofillPreferences.prototype.openEditAddressDialog(guid, window);
+ }
+ },
+ setup(emitChange) {
+ document.l10n
+ .formatValues([
+ { id: "addresses-delete-address-prompt-title" },
+ { id: "addresses-delete-address-prompt-confirm-button" },
+ { id: "addresses-delete-address-prompt-cancel-button" },
+ ])
+ .then(val => (this._removeAddressDialogStrings = val))
+ .then(emitChange);
+ },
+ disabled() {
+ return !!this._removeAddressDialogStrings.length;
+ },
+});
+
+Preferences.addSetting({
+ id: "add-address-button",
+ deps: ["saveAndFillAddresses"],
+ onUserClick: () => {
+ FormAutofillPreferences.prototype.openEditAddressDialog(undefined, window);
+ },
+ disabled: ({ saveAndFillAddresses }) => !saveAndFillAddresses.value,
+});
+
+Preferences.addSetting({
+ id: "addresses-list-header",
+});
+
+Preferences.addSetting(
+ class extends Preferences.AsyncSetting {
+ static id = "addresses-list";
+
+ async getAddresses() {
+ await FormAutofillPreferences.prototype.initializeAddressesStorage();
+ return FormAutofillPreferences.prototype.makeAddressesListItems();
+ }
+
+ async getControlConfig() {
+ return {
+ items: await this.getAddresses(),
+ };
+ }
+
+ setup() {
+ Services.obs.addObserver(this.emitChange, "formautofill-storage-changed");
+ return () =>
+ Services.obs.removeObserver(
+ this.emitChange,
+ "formautofill-storage-changed"
+ );
+ }
+
+ async visible() {
+ const items = await this.getAddresses();
+ return !!items.length;
+ }
+ }
+);
+
SettingGroupManager.registerGroups({
containers: {
// This section is marked as in progress for testing purposes
@@ -3109,9 +3188,7 @@ SettingGroupManager.registerGroups({
{
id: "payments-list",
control: "moz-box-group",
- l10nId: "payments-list-header",
controlAttrs: {
- hasHeader: true,
type: "list",
},
},
@@ -3323,6 +3400,22 @@ SettingGroupManager.registerGroups({
},
],
},
+ manageAddresses: {
+ items: [
+ {
+ id: "add-address-button",
+ control: "moz-button",
+ l10nId: "autofill-addresses-add-button",
+ },
+ {
+ id: "addresses-list",
+ control: "moz-box-group",
+ controlAttrs: {
+ type: "list",
+ },
+ },
+ ],
+ },
sync: {
inProgress: true,
l10nId: "sync-group-label",
diff --git a/browser/components/preferences/preferences.js b/browser/components/preferences/preferences.js
@@ -291,6 +291,11 @@ const CONFIG_PANES = Object.freeze({
l10nId: "preferences-etp-header",
groupIds: ["etpBanner", "etpAdvanced"],
},
+ manageAddresses: {
+ parent: "privacy",
+ l10nId: "autofill-addresses-manage-addresses-title",
+ groupIds: ["manageAddresses"],
+ },
});
var gLastCategory = { category: undefined, subcategory: undefined };
diff --git a/browser/extensions/formautofill/content/manageDialog.mjs b/browser/extensions/formautofill/content/manageDialog.mjs
@@ -2,8 +2,6 @@
* 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/. */
-const EDIT_ADDRESS_URL = "chrome://formautofill/content/editAddress.xhtml";
-
const { AppConstants } = ChromeUtils.importESModule(
"resource://gre/modules/AppConstants.sys.mjs"
);
@@ -336,13 +334,10 @@ export class ManageAddresses extends ManageRecords {
* @param {object} address [optional]
*/
openEditDialog(address) {
- this.prefWin.gSubDialog.open(EDIT_ADDRESS_URL, undefined, {
- record: address,
- // Don't validate in preferences since it's fine for fields to be missing
- // for autofill purposes. For PaymentRequest addresses get more validation.
- noValidate: true,
- l10nStrings: ManageAddresses.getAddressL10nStrings(),
- });
+ return lazy.FormAutofillPreferences.openEditAddressDialog(
+ address,
+ this.prefWin
+ );
}
getLabelInfo(address) {
diff --git a/browser/locales/en-US/browser/preferences/preferences.ftl b/browser/locales/en-US/browser/preferences/preferences.ftl
@@ -1341,10 +1341,19 @@ autofill-reauth-payment-methods-checkbox-2 =
autofill-payment-methods-add-button = Add new payment method
payments-list-header =
.label = Payment methods
-payments-list-item-label = <strong>Payment methods</strong>
payments-delete-payment-prompt-title = Delete this payment method?
payments-delete-payment-prompt-confirm-button = Delete
payments-delete-payment-prompt-cancel-button = Cancel
+
+# These values are displayed for each credit card record listed on the Manage Payment methods
+# settings page.
+# Variables:
+# $cardNumber (string) - The obscured credit card number (for example: 2423 *********)
+# $expDate (string) - The obscured expiry date of the credit card (for example: XX/2027)
+payment-moz-box-item =
+ .label = { $cardNumber }
+ .description = { $expDate }
+
autofill-addresses-title = Addresses and more
autofill-addresses-header =
.aria-label = Addresses and more
@@ -1354,15 +1363,26 @@ autofill-addresses-checkbox-message =
autofill-addresses-manage-addresses-button =
.label = Manage addresses and more
.accesskey = M
-
-# These values are displayed for each credit card record listed on the Manage Payment methods
-# settings page.
+addresses-list-header =
+ .label = Addresses
+addreses-delete-address-button-label =
+ .aria-label = Delete
+addreses-edit-address-button-label =
+ .aria-label = Edit
+addresses-delete-address-prompt-title = Delete this address?
+addresses-delete-address-prompt-confirm-button = Delete
+addresses-delete-address-prompt-cancel-button = Cancel
+autofill-addresses-add-button = Add new address
+autofill-addresses-manage-addresses-title =
+ .heading = Manage addresses and more
+
+# These values are displayed for each address record listed on the "Manage addresses and more" subpage.
# Variables:
-# $cardNumber (string) - The obscured credit card number (for example: 2423 *********)
-# $expDate (string) - The obscured expiry date of the credit card (for example: XX/2027)
-payment-moz-box-item =
- .label = { $cardNumber }
- .description = { $expDate }
+# $name (string) - The name associated with the address
+# $address (string) - The address
+address-moz-box-item =
+ .label = { $name }
+ .description = { $address }
## Privacy Section - History
diff --git a/browser/themes/shared/preferences/preferences.css b/browser/themes/shared/preferences/preferences.css
@@ -1529,3 +1529,8 @@ setting-group[groupid="home"] {
#etpLevelWarning {
--promo-image-position: top;
}
+
+#addresses-list-header,
+#payments-list-header {
+ --box-label-font-weight: var(--heading-font-weight);
+}
diff --git a/toolkit/components/formautofill/FormAutofillPreferences.sys.mjs b/toolkit/components/formautofill/FormAutofillPreferences.sys.mjs
@@ -8,6 +8,7 @@
const MANAGE_ADDRESSES_URL =
"chrome://formautofill/content/manageAddresses.xhtml";
+const EDIT_ADDRESS_URL = "chrome://formautofill/content/editAddress.xhtml";
const MANAGE_CREDITCARDS_URL =
"chrome://formautofill/content/manageCreditCards.xhtml";
const EDIT_CREDIT_CARD_URL =
@@ -20,6 +21,7 @@ const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
OSKeyStore: "resource://gre/modules/OSKeyStore.sys.mjs",
formAutofillStorage: "resource://autofill/FormAutofillStorage.sys.mjs",
+ ManageAddresses: "chrome://formautofill/content/manageDialog.mjs",
});
ChromeUtils.defineLazyGetter(
@@ -137,8 +139,13 @@ export class FormAutofillPreferences {
id: "savedAddressesButton",
pref: null,
visible: () => FormAutofill.isAutofillAddressesAvailable,
- onUserClick: ({ target }) => {
- target.ownerGlobal.gSubDialog.open(MANAGE_ADDRESSES_URL);
+ onUserClick: e => {
+ e.preventDefault();
+ if (Services.prefs.getBoolPref("browser.settings-redesign.enabled")) {
+ e.target.ownerGlobal.gotoPref("paneManageAddresses");
+ } else {
+ e.target.ownerGlobal.gSubDialog.open(MANAGE_ADDRESSES_URL);
+ }
},
});
@@ -197,6 +204,10 @@ export class FormAutofillPreferences {
await lazy.formAutofillStorage.initialize();
}
+ async initializeAddressesStorage() {
+ await lazy.formAutofillStorage.initialize();
+ }
+
async trySetOSAuthEnabled(win, checked) {
let messageText = await lazy.l10n.formatValueSync(
"autofill-creditcard-os-dialog-message"
@@ -280,7 +291,76 @@ export class FormAutofillPreferences {
{
id: "payments-list-header",
control: "moz-box-item",
- l10nId: "payments-list-item-label",
+ l10nId: "payments-list-header",
+ slot: "header",
+ },
+ ...items,
+ ];
+ }
+
+ async makeAddressesListItems() {
+ const addresses = await lazy.formAutofillStorage.addresses.getAll();
+ const records = addresses.slice().reverse();
+
+ if (!records.length) {
+ return [];
+ }
+
+ const items = records.map(record => {
+ const addressFormatted = [
+ record["street-address"],
+ record["address-level2"],
+ record["address-level1"],
+ record.country,
+ record["postal-code"],
+ ]
+ .filter(Boolean)
+ .join(", ");
+
+ const config = {
+ id: "address-item",
+ control: "moz-box-item",
+ l10nId: "address-moz-box-item",
+ iconSrc: "chrome://browser/skin/notification-icons/geo.svg",
+ l10nArgs: {
+ name: `${record.name}`,
+ address: addressFormatted,
+ },
+ options: [
+ {
+ control: "moz-button",
+ iconSrc: "chrome://global/skin/icons/delete.svg",
+ type: "icon",
+ l10nId: "addreses-delete-address-button-label",
+ controlAttrs: {
+ slot: "actions",
+ action: "remove",
+ guid: record.guid,
+ },
+ },
+ {
+ control: "moz-button",
+ iconSrc: "chrome://global/skin/icons/edit.svg",
+ type: "icon",
+ l10nId: "addreses-edit-address-button-label",
+ controlAttrs: {
+ slot: "actions",
+ action: "edit",
+ guid: record.guid,
+ },
+ },
+ ],
+ };
+
+ return config;
+ });
+
+ return [
+ {
+ id: "addresses-list-header",
+ control: "moz-box-item",
+ l10nId: "addresses-list-header",
+ slot: "header",
},
...items,
];
@@ -408,4 +488,68 @@ export class FormAutofillPreferences {
}
);
}
+ /**
+ * Open the browser window modal to prompt the user whether
+ * or they want to remove their address.
+ *
+ * @param {string} guid
+ * The guid of the address item we are prompting to remove.
+ * @param {object} browsingContext
+ * Browsing context to open the prompt in
+ * @param {string} title
+ * The title text displayed in the modal to prompt the user with
+ * @param {string} confirmBtn
+ * The text for confirming the removal of an address
+ * @param {string} cancelBtn
+ * The text for cancelling removal of an address
+ */
+ async openRemoveAddressDialog(
+ guid,
+ browsingContext,
+ title,
+ confirmBtn,
+ cancelBtn
+ ) {
+ const flags =
+ Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0 +
+ Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1;
+ const result = await Services.prompt.asyncConfirmEx(
+ browsingContext,
+ Services.prompt.MODAL_TYPE_INTERNAL_WINDOW,
+ title,
+ null,
+ flags,
+ confirmBtn,
+ cancelBtn,
+ null,
+ null,
+ false
+ );
+
+ const propBag = result.QueryInterface(Ci.nsIPropertyBag2);
+ // Confirmed
+ if (propBag.get("buttonNumClicked") === 0) {
+ lazy.formAutofillStorage.addresses.remove(guid);
+ }
+ }
+
+ async openEditAddressDialog(guid, window) {
+ const address = await lazy.formAutofillStorage.addresses.get(guid);
+ return FormAutofillPreferences.openEditAddressDialog(address, window);
+ }
+ /**
+ * Open the edit address dialog to create/edit an address.
+ *
+ * @param {object} address
+ * The address we want to edit.
+ */
+ static async openEditAddressDialog(address, window) {
+ window.gSubDialog.open(EDIT_ADDRESS_URL, undefined, {
+ record: address ?? undefined,
+ // Don't validate in preferences since it's fine for fields to be missing
+ // for autofill purposes. For PaymentRequest addresses get more validation.
+ noValidate: true,
+ l10nStrings: lazy.ManageAddresses.getAddressL10nStrings(),
+ });
+ }
}