Bug 1415022 - Provide additional description message that help user to indicate the profile to be operated. r=steveck, seanlee
MozReview-Commit-ID: 1QNaIQJJZ18
--- a/browser/extensions/formautofill/FormAutofillDoorhanger.jsm
+++ b/browser/extensions/formautofill/FormAutofillDoorhanger.jsm
@@ -69,16 +69,18 @@ const CONTENT = {
},
},
hideClose: true,
},
},
updateAddress: {
notificationId: "autofill-address",
message: GetStringFromName("updateAddressMessage"),
+ descriptionLabel: GetStringFromName("updateAddressDescriptionLabel"),
+ descriptionIcon: false,
linkMessage: GetStringFromName(autofillOptsKey),
anchor: {
id: "autofill-address-notification-icon",
URL: "chrome://formautofill/content/formfill-anchor.svg",
tooltiptext: GetStringFromName("openAutofillMessagePanel"),
},
mainAction: {
label: GetStringFromName("updateAddressLabel"),
@@ -94,16 +96,18 @@ const CONTENT = {
persistWhileVisible: true,
popupIconURL: "chrome://formautofill/content/icon-address-update.svg",
hideClose: true,
},
},
addCreditCard: {
notificationId: "autofill-credit-card",
message: formatStringFromName("saveCreditCardMessage", [brandShortName], 1),
+ descriptionLabel: GetStringFromName("saveCreditCardDescriptionLabel"),
+ descriptionIcon: true,
linkMessage: GetStringFromName(autofillSecurityOptionsKey),
anchor: {
id: "autofill-credit-card-notification-icon",
URL: "chrome://formautofill/content/formfill-anchor.svg",
tooltiptext: GetStringFromName("openAutofillMessagePanel"),
},
mainAction: {
label: GetStringFromName("saveCreditCardLabel"),
@@ -147,16 +151,18 @@ const CONTENT = {
log.debug("Set creditCard sync to", checked);
},
},
},
},
updateCreditCard: {
notificationId: "autofill-credit-card",
message: GetStringFromName("updateCreditCardMessage"),
+ descriptionLabel: GetStringFromName("updateCreditCardDescriptionLabel"),
+ descriptionIcon: true,
linkMessage: GetStringFromName(autofillOptsKey),
anchor: {
id: "autofill-credit-card-notification-icon",
URL: "chrome://formautofill/content/formfill-anchor.svg",
tooltiptext: GetStringFromName("openAutofillMessagePanel"),
},
mainAction: {
label: GetStringFromName("updateCreditCardLabel"),
@@ -217,39 +223,67 @@ let FormAutofillDoorhanger = {
},
_getNotificationElm(browser, id) {
let notificationId = id + "-notification";
let chromeDoc = browser.ownerDocument;
return chromeDoc.getElementById(notificationId);
},
/**
* Append the link label element to the popupnotificationcontent.
- * @param {XULElement} browser
- * Target browser element for showing doorhanger.
- * @param {string} id
- * The ID of the doorhanger.
+ * @param {XULElement} content
+ * popupnotificationcontent
* @param {string} message
* The localized string for link title.
*/
- _appendPrivacyPanelLink(browser, id, message) {
- let notificationId = id + "-notification";
- let chromeDoc = browser.ownerDocument;
- let notification = chromeDoc.getElementById(notificationId);
+ _appendPrivacyPanelLink(content, message) {
+ let chromeDoc = content.ownerDocument;
+ let privacyLinkElement = chromeDoc.createElement("label");
+ privacyLinkElement.className = "text-link";
+ privacyLinkElement.setAttribute("useoriginprincipal", true);
+ privacyLinkElement.setAttribute("href", "about:preferences#privacy");
+ privacyLinkElement.setAttribute("value", message);
+ content.appendChild(privacyLinkElement);
+ },
+
+ /**
+ * Append the description section to the popupnotificationcontent.
+ * @param {XULElement} content
+ * popupnotificationcontent
+ * @param {string} descriptionLabel
+ * The label showing above description.
+ * @param {string} descriptionIcon
+ * The src of description icon.
+ */
+ _appendDescription(content, descriptionLabel, descriptionIcon) {
+ let chromeDoc = content.ownerDocument;
+ let docFragment = chromeDoc.createDocumentFragment();
- if (!notification.querySelector("popupnotificationcontent")) {
- let notificationcontent = chromeDoc.createElement("popupnotificationcontent");
- let privacyLinkElement = chromeDoc.createElement("label");
- privacyLinkElement.className = "text-link";
- privacyLinkElement.setAttribute("useoriginprincipal", true);
- privacyLinkElement.setAttribute("href", "about:preferences#privacy");
- privacyLinkElement.setAttribute("value", message);
- notificationcontent.appendChild(privacyLinkElement);
- notification.append(notificationcontent);
+ let descriptionLabelElement = chromeDoc.createElement("label");
+ descriptionLabelElement.setAttribute("value", descriptionLabel);
+ docFragment.appendChild(descriptionLabelElement);
+
+ let descriptionWrapper = chromeDoc.createElement("hbox");
+ descriptionWrapper.className = "desc-message-box";
+
+ if (descriptionIcon) {
+ let descriptionIconElement = chromeDoc.createElement("image");
+ descriptionWrapper.appendChild(descriptionIconElement);
}
+
+ let descriptionElement = chromeDoc.createElement("description");
+ descriptionWrapper.appendChild(descriptionElement);
+ docFragment.appendChild(descriptionWrapper);
+
+ content.appendChild(docFragment);
},
+
+ _updateDescription(content, description) {
+ content.querySelector("description").textContent = description;
+ },
+
/**
* Create an image element for notification anchor if it doesn't already exist.
* @param {XULElement} browser
* Target browser element for showing doorhanger.
* @param {Object} anchor
* Anchor options for setting the anchor element.
* @param {string} anchor.id
* ID of the anchor element.
@@ -296,33 +330,40 @@ let FormAutofillDoorhanger = {
}
},
/**
* Show different types of doorhanger by leveraging PopupNotifications.
* @param {XULElement} browser
* Target browser element for showing doorhanger.
* @param {string} type
* The type of the doorhanger. There will have first time use/update/credit card.
+ * @param {string} description
+ * The message that provides more information on doorhanger.
* @returns {Promise}
Resolved with action type when action callback is triggered.
*/
- async show(browser, type) {
+ async show(browser, type, description) {
log.debug("show doorhanger with type:", type);
return new Promise((resolve) => {
let {
notificationId,
message,
+ descriptionLabel,
+ descriptionIcon,
linkMessage,
anchor,
mainAction,
secondaryActions,
options,
} = CONTENT[type];
- let chromeWin = browser.ownerGlobal;
+ const {
+ ownerGlobal: chromeWin,
+ ownerDocument: chromeDoc,
+ } = browser;
options.eventCallback = (topic) => {
log.debug("eventCallback:", topic);
if (topic == "removed" || topic == "dismissed") {
this._removeCheckboxListener(browser, {notificationId, options});
return;
}
@@ -332,17 +373,27 @@ let FormAutofillDoorhanger = {
}
this._addCheckboxListener(browser, {notificationId, options});
// There's no preferences link or other customization in first time use doorhanger.
if (type == "firstTimeUse") {
return;
}
- this._appendPrivacyPanelLink(browser, notificationId, linkMessage);
+ const notificationElementId = notificationId + "-notification";
+ const notification = chromeDoc.getElementById(notificationElementId);
+ const notificationContent = notification.querySelector("popupnotificationcontent") ||
+ chromeDoc.createElement("popupnotificationcontent");
+ if (!notification.contains(notificationContent)) {
+ notificationContent.setAttribute("orient", "vertical");
+ this._appendDescription(notificationContent, descriptionLabel, descriptionIcon);
+ this._appendPrivacyPanelLink(notificationContent, linkMessage);
+ notification.append(notificationContent);
+ }
+ this._updateDescription(notificationContent, description);
};
this._setAnchor(browser, anchor);
chromeWin.PopupNotifications.show(
browser,
notificationId,
message,
anchor.id,
...this._createActions(mainAction, secondaryActions, resolve),
--- a/browser/extensions/formautofill/FormAutofillParent.jsm
+++ b/browser/extensions/formautofill/FormAutofillParent.jsm
@@ -392,17 +392,18 @@ FormAutofillParent.prototype = {
address.record[field] = originalAddress[field];
}
}
if (!this.profileStorage.addresses.mergeIfPossible(address.guid, address.record, true)) {
this._recordFormFillingTime("address", "autofill-update", timeStartedFillingMS);
showDoorhanger = async () => {
- const state = await FormAutofillDoorhanger.show(target, "updateAddress");
+ const description = FormAutofillUtils.getAddressLabel(address.record);
+ const state = await FormAutofillDoorhanger.show(target, "updateAddress", description);
let changedGUIDs = this.profileStorage.addresses.mergeToStorage(address.record, true);
switch (state) {
case "create":
if (!changedGUIDs.length) {
changedGUIDs.push(this.profileStorage.addresses.add(address.record));
}
break;
case "update":
@@ -431,17 +432,18 @@ FormAutofillParent.prototype = {
}
changedGUIDs.forEach(guid => this.profileStorage.addresses.notifyUsed(guid));
this._recordFormFillingTime("address", "manual", timeStartedFillingMS);
// Show first time use doorhanger
if (FormAutofillUtils.isAutofillAddressesFirstTimeUse) {
Services.prefs.setBoolPref(FormAutofillUtils.ADDRESSES_FIRST_TIME_USE_PREF, false);
showDoorhanger = async () => {
- const state = await FormAutofillDoorhanger.show(target, "firstTimeUse");
+ const description = FormAutofillUtils.getAddressLabel(address.record);
+ const state = await FormAutofillDoorhanger.show(target, "firstTimeUse", description);
if (state !== "open-pref") {
return;
}
target.ownerGlobal.openPreferences("panePrivacy",
{origin: "autofillDoorhanger"});
};
} else {
@@ -516,17 +518,20 @@ FormAutofillParent.prototype = {
setUsedStatus(2);
return async () => {
// Suppress the pending doorhanger from showing up if user disabled credit card in previous doorhanger.
if (!FormAutofillUtils.isAutofillCreditCardsEnabled) {
return;
}
- const state = await FormAutofillDoorhanger.show(target, creditCard.guid ? "updateCreditCard" : "addCreditCard");
+ const description = FormAutofillUtils.getCreditCardLabel(creditCard.record, false);
+ const state = await FormAutofillDoorhanger.show(target,
+ creditCard.guid ? "updateCreditCard" : "addCreditCard",
+ description);
if (state == "cancel") {
return;
}
if (state == "disable") {
Services.prefs.setBoolPref("extensions.formautofill.creditCards.enabled", false);
return;
}
--- a/browser/extensions/formautofill/FormAutofillUtils.jsm
+++ b/browser/extensions/formautofill/FormAutofillUtils.jsm
@@ -254,16 +254,94 @@ this.FormAutofillUtils = {
},
getAddressSeparator() {
// The separator should be based on the L10N address format, and using a
// white space is a temporary solution.
return " ";
},
+ /**
+ * Get credit card display label. It should display masked numbers and the
+ * cardholder's name, separated by a comma. If `showCreditCards` is set to
+ * true, decrypted credit card numbers are shown instead.
+ *
+ * @param {object} creditCard
+ * @param {boolean} showCreditCards [optional]
+ * @returns {string}
+ */
+ getCreditCardLabel(creditCard, showCreditCards = false) {
+ let parts = [];
+ let ccLabel;
+ let ccNumber = creditCard["cc-number"];
+ let decryptedCCNumber = creditCard["cc-number-decrypted"];
+
+ if (showCreditCards && decryptedCCNumber) {
+ ccLabel = decryptedCCNumber;
+ }
+ if (ccNumber && !ccLabel) {
+ if (this.isCCNumber(ccNumber)) {
+ ccLabel = "*".repeat(4) + " " + ccNumber.substr(-4);
+ } else {
+ let {affix, label} = this.fmtMaskedCreditCardLabel(ccNumber);
+ ccLabel = `${affix} ${label}`;
+ }
+ }
+
+ if (ccLabel) {
+ parts.push(ccLabel);
+ }
+ if (creditCard["cc-name"]) {
+ parts.push(creditCard["cc-name"]);
+ }
+ return parts.join(", ");
+ },
+
+ /**
+ * Get address display label. It should display up to two pieces of
+ * information, separated by a comma.
+ *
+ * @param {object} address
+ * @returns {string}
+ */
+ getAddressLabel(address) {
+ // TODO: Implement a smarter way for deciding what to display
+ // as option text. Possibly improve the algorithm in
+ // ProfileAutoCompleteResult.jsm and reuse it here.
+ const fieldOrder = [
+ "name",
+ "-moz-street-address-one-line", // Street address
+ "address-level2", // City/Town
+ "organization", // Company or organization name
+ "address-level1", // Province/State (Standardized code if possible)
+ "country-name", // Country name
+ "postal-code", // Postal code
+ "tel", // Phone number
+ "email", // Email address
+ ];
+
+ address = {...address};
+ let parts = [];
+ if (address["street-address"]) {
+ address["-moz-street-address-one-line"] = this.toOneLineAddress(
+ address["street-address"]
+ );
+ }
+ for (const fieldName of fieldOrder) {
+ let string = address[fieldName];
+ if (string) {
+ parts.push(string);
+ }
+ if (parts.length == 2) {
+ break;
+ }
+ }
+ return parts.join(", ");
+ },
+
toOneLineAddress(address, delimiter = "\n") {
let array = typeof address == "string" ? address.split(delimiter) : address;
if (!Array.isArray(array)) {
return "";
}
return array
.map(s => s ? s.trim() : "")
--- a/browser/extensions/formautofill/content/formautofill.css
+++ b/browser/extensions/formautofill/content/formautofill.css
@@ -39,8 +39,24 @@
#PopupAutoComplete[firstresultstyle="autofill-profile"] {
min-width: 150px !important;
}
#PopupAutoComplete[firstresultstyle="autofill-insecureWarning"] {
min-width: 200px !important;
}
+
+/* Form Autofill Doorhanger */
+#autofill-address-notification > popupnotificationcontent > .desc-message-box,
+#autofill-credit-card-notification > popupnotificationcontent > .desc-message-box {
+ margin-block-end: 12px;
+}
+#autofill-credit-card-notification > popupnotificationcontent > .desc-message-box > image {
+ margin-inline-start: 6px;
+ width: 16px;
+ height: 16px;
+ list-style-image: url(chrome://formautofill/content/icon-credit-card-generic.svg);
+}
+#autofill-address-notification > popupnotificationcontent > .desc-message-box > description,
+#autofill-credit-card-notification > popupnotificationcontent > .desc-message-box > description {
+ font-style: italic;
+}
--- a/browser/extensions/formautofill/content/manageDialog.js
+++ b/browser/extensions/formautofill/content/manageDialog.js
@@ -281,55 +281,18 @@ class ManageAddresses extends ManageReco
* Open the edit address dialog to create/edit an address.
*
* @param {object} address [optional]
*/
openEditDialog(address) {
this.prefWin.gSubDialog.open(EDIT_ADDRESS_URL, null, address);
}
- /**
- * Get address display label. It should display up to two pieces of
- * information, separated by a comma.
- *
- * @param {object} address
- * @returns {string}
- */
getLabel(address) {
- // TODO: Implement a smarter way for deciding what to display
- // as option text. Possibly improve the algorithm in
- // ProfileAutoCompleteResult.jsm and reuse it here.
- const fieldOrder = [
- "name",
- "-moz-street-address-one-line", // Street address
- "address-level2", // City/Town
- "organization", // Company or organization name
- "address-level1", // Province/State (Standardized code if possible)
- "country-name", // Country name
- "postal-code", // Postal code
- "tel", // Phone number
- "email", // Email address
- ];
-
- let parts = [];
- if (address["street-address"]) {
- address["-moz-street-address-one-line"] = FormAutofillUtils.toOneLineAddress(
- address["street-address"]
- );
- }
- for (const fieldName of fieldOrder) {
- let string = address[fieldName];
- if (string) {
- parts.push(string);
- }
- if (parts.length == 2) {
- break;
- }
- }
- return parts.join(", ");
+ return FormAutofillUtils.getAddressLabel(address);
}
}
class ManageCreditCards extends ManageRecords {
constructor(elements) {
super("creditCards", elements);
elements.add.setAttribute("searchkeywords", FormAutofillUtils.EDIT_CREDITCARD_KEYWORDS
.map(key => FormAutofillUtils.stringBundle.GetStringFromName(key))
@@ -359,31 +322,22 @@ class ManageCreditCards extends ManageRe
* cardholder's name, separated by a comma. If `showCreditCards` is set to
* true, decrypted credit card numbers are shown instead.
*
* @param {object} creditCard
* @param {boolean} showCreditCards [optional]
* @returns {string}
*/
async getLabel(creditCard, showCreditCards = false) {
- let parts = [];
- if (creditCard["cc-number"]) {
- let ccLabel;
- if (showCreditCards) {
- ccLabel = await MasterPassword.decrypt(creditCard["cc-number-encrypted"]);
- } else {
- let {affix, label} = FormAutofillUtils.fmtMaskedCreditCardLabel(creditCard["cc-number"]);
- ccLabel = `${affix} ${label}`;
- }
- parts.push(ccLabel);
+ let patchObj = {};
+ if (creditCard["cc-number"] && showCreditCards) {
+ patchObj["cc-number-decrypted"] = await MasterPassword.decrypt(creditCard["cc-number-encrypted"]);
}
- if (creditCard["cc-name"]) {
- parts.push(creditCard["cc-name"]);
- }
- return parts.join(", ");
+
+ return FormAutofillUtils.getCreditCardLabel({...creditCard, ...patchObj}, showCreditCards);
}
async toggleShowHideCards(options) {
this._isDecrypted = !this._isDecrypted;
this.updateShowHideButtonState();
await this.updateLabels(options, this._isDecrypted);
}
--- a/browser/extensions/formautofill/locales/en-US/formautofill.properties
+++ b/browser/extensions/formautofill/locales/en-US/formautofill.properties
@@ -19,36 +19,39 @@ changeAutofillOptions = Change Form Auto
changeAutofillOptionsOSX = Change Form Autofill Preferences
changeAutofillOptionsAccessKey = C
# LOCALIZATION NOTE (addressesSyncCheckbox): If Sync is enabled, this checkbox is displayed on the doorhanger
# shown when saving addresses.
addressesSyncCheckbox = Share addresses with synced devices
# LOCALIZATION NOTE (creditCardsSyncCheckbox): If Sync is enabled and credit card sync is available,
# this checkbox is displayed on the doorhanger shown when saving credit card.
creditCardsSyncCheckbox = Share credit cards with synced devices
-# LOCALIZATION NOTE (updateAddressMessage, createAddressLabel, updateAddressLabel): Used on the doorhanger
-# when an address change is detected.
+# LOCALIZATION NOTE (updateAddressMessage, updateAddressDescriptionLabel, createAddressLabel, updateAddressLabel):
+# Used on the doorhanger when an address change is detected.
updateAddressMessage = Would you like to update your address with this new information?
+updateAddressDescriptionLabel = Address to update:
createAddressLabel = Create New Address
createAddressAccessKey = C
updateAddressLabel = Update Address
updateAddressAccessKey = U
-# LOCALIZATION NOTE (saveCreditCardMessage, saveCreditCardLabel, cancelCreditCardLabel, neverSaveCreditCardLabel):
+# LOCALIZATION NOTE (saveCreditCardMessage, saveCreditCardDescriptionLabel, saveCreditCardLabel, cancelCreditCardLabel, neverSaveCreditCardLabel):
# Used on the doorhanger when users submit payment with credit card.
# LOCALIZATION NOTE (saveCreditCardMessage): %S is brandShortName.
saveCreditCardMessage = Would you like %S to save this credit card? (Security code will not be saved)
+saveCreditCardDescriptionLabel = Credit card to save:
saveCreditCardLabel = Save Credit Card
saveCreditCardAccessKey = S
cancelCreditCardLabel = Don’t Save
cancelCreditCardAccessKey = D
neverSaveCreditCardLabel = Never Save Credit Cards
neverSaveCreditCardAccessKey = N
-# LOCALIZATION NOTE (updateCreditCardMessage, createCreditCardLabel, updateCreditCardLabel): Used on the doorhanger
-# when an credit card change is detected.
+# LOCALIZATION NOTE (updateCreditCardMessage, updateCreditCardDescriptionLabel, createCreditCardLabel, updateCreditCardLabel):
+# Used on the doorhanger when an credit card change is detected.
updateCreditCardMessage = Would you like to update your credit card with this new information?
+updateCreditCardDescriptionLabel = Credit card to update:
createCreditCardLabel = Create New Credit Card
createCreditCardAccessKey = C
updateCreditCardLabel = Update Credit Card
updateCreditCardAccessKey = U
# LOCALIZATION NOTE (openAutofillMessagePanel): Tooltip label for Form Autofill doorhanger icon on address bar.
openAutofillMessagePanel = Open Form Autofill message panel
# LOCALIZATION NOTE ( (autocompleteFooterOptionShort, autocompleteFooterOptionOSXShort): Used as a label for the button,