forked from mirrors/gecko-dev
Bug 1869232 - Refactor credit card doorhanger to use the same architecture as the new address doorhanger r=credential-management-reviewers,fluent-reviewers,joschmidt,bolsson
Besides refactoring, this patch also updates strings for save and update credit card doorhanger. Differential Revision: https://phabricator.services.mozilla.com/D196054
This commit is contained in:
parent
130511fbed
commit
7b173948b3
7 changed files with 378 additions and 438 deletions
|
|
@ -251,6 +251,13 @@
|
||||||
</html:div>
|
</html:div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<popupnotification id="credit-card-save-update-notification" class="credit-card-capture-notification" hidden="true">
|
||||||
|
<popupnotificationcontent class="credit-card-save-update-notification-content" orient="vertical">
|
||||||
|
<html:div class="credit-card-capture-content">
|
||||||
|
</html:div>
|
||||||
|
</popupnotificationcontent>
|
||||||
|
</popupnotification>
|
||||||
|
|
||||||
<popupnotification id="address-save-update-notification" class="address-capture-notification" hidden="true">
|
<popupnotification id="address-save-update-notification" class="address-capture-notification" hidden="true">
|
||||||
<popupnotificationcontent class="address-save-update-notification-content" orient="vertical">
|
<popupnotificationcontent class="address-save-update-notification-content" orient="vertical">
|
||||||
<html:div class="address-capture-header">
|
<html:div class="address-capture-header">
|
||||||
|
|
|
||||||
|
|
@ -2,34 +2,6 @@
|
||||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
# 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/.
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
# LOCALIZATION NOTE (autofillOptionsLink, autofillOptionsLinkOSX): These strings are used in the doorhanger for
|
|
||||||
# updating addresses. The link leads users to Form Autofill browser preferences.
|
|
||||||
autofillOptionsLink = Form Autofill Options
|
|
||||||
autofillOptionsLinkOSX = Form Autofill Preferences
|
|
||||||
# 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 (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, 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 (autocompleteManageCreditCards):
|
# LOCALIZATION NOTE (autocompleteManageCreditCards):
|
||||||
# Used as a label for the button, displayed at the bottom of the dropdown suggestion, to open Form Autofill browser preferences.
|
# Used as a label for the button, displayed at the bottom of the dropdown suggestion, to open Form Autofill browser preferences.
|
||||||
autocompleteManageCreditCards = Manage credit cards
|
autocompleteManageCreditCards = Manage credit cards
|
||||||
|
|
|
||||||
26
python/l10n/fluent_migrations/bug_1867819_formautofill.py
Normal file
26
python/l10n/fluent_migrations/bug_1867819_formautofill.py
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
# Any copyright is dedicated to the Public Domain.
|
||||||
|
# http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
|
||||||
|
import fluent.syntax.ast as FTL
|
||||||
|
from fluent.migrate.transforms import COPY
|
||||||
|
|
||||||
|
|
||||||
|
def migrate(ctx):
|
||||||
|
"""Bug 1867819 - Convert formautofill.properties to Fluent, part {index}."""
|
||||||
|
|
||||||
|
source = "browser/extensions/formautofill/formautofill.properties"
|
||||||
|
target = "toolkit/toolkit/formautofill/formAutofill.ftl"
|
||||||
|
ctx.add_transforms(
|
||||||
|
target,
|
||||||
|
target,
|
||||||
|
[
|
||||||
|
FTL.Message(
|
||||||
|
id=FTL.Identifier("autofill-options-link"),
|
||||||
|
value=COPY(source, "autofillOptionsLink"),
|
||||||
|
),
|
||||||
|
FTL.Message(
|
||||||
|
id=FTL.Identifier("autofill-options-link-osx"),
|
||||||
|
value=COPY(source, "autofillOptionsLinkOSX"),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
@ -622,12 +622,16 @@ export class FormAutofillParent extends JSWindowActorParent {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Overwrite the guid if there is a duplicate
|
||||||
|
const duplicateRecord =
|
||||||
|
(await storage.getDuplicateRecords(creditCard.record).next()).value ?? {};
|
||||||
|
|
||||||
return async () => {
|
return async () => {
|
||||||
await lazy.FormAutofillPrompter.promptToSaveCreditCard(
|
await lazy.FormAutofillPrompter.promptToSaveCreditCard(
|
||||||
browser,
|
browser,
|
||||||
storage,
|
storage,
|
||||||
creditCard.record,
|
creditCard.flowId,
|
||||||
creditCard.flowId
|
{ oldRecord: duplicateRecord, newRecord: creditCard.record }
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,24 +33,28 @@ export let FormAutofillPrompter = {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
async promptToSaveAddress(browser, type, description) {
|
async promptToSaveAddress(
|
||||||
|
browser,
|
||||||
|
storage,
|
||||||
|
flowId,
|
||||||
|
{ oldRecord, newRecord }
|
||||||
|
) {
|
||||||
throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
|
throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
|
||||||
},
|
},
|
||||||
|
|
||||||
async promptToSaveCreditCard(browser, storage, record, flowId) {
|
async promptToSaveCreditCard(
|
||||||
const prompt = new lazy.GeckoViewPrompter(browser.ownerGlobal);
|
browser,
|
||||||
|
storage,
|
||||||
const duplicateRecord = (await storage.getDuplicateRecords(record).next())
|
flowId,
|
||||||
.value;
|
{ oldRecord, newRecord }
|
||||||
let newCreditCard;
|
) {
|
||||||
if (duplicateRecord) {
|
if (oldRecord) {
|
||||||
newCreditCard = { ...duplicateRecord, ...record };
|
newRecord = { ...oldRecord, ...newRecord };
|
||||||
} else {
|
|
||||||
newCreditCard = record;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const prompt = new lazy.GeckoViewPrompter(browser.ownerGlobal);
|
||||||
prompt.asyncShowPrompt(
|
prompt.asyncShowPrompt(
|
||||||
this._createMessage([lazy.CreditCard.fromGecko(newCreditCard)]),
|
this._createMessage([lazy.CreditCard.fromGecko(newRecord)]),
|
||||||
result => {
|
result => {
|
||||||
const selectedCreditCard = result?.selection?.value;
|
const selectedCreditCard = result?.selection?.value;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,23 +26,18 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
||||||
ChromeUtils.defineLazyGetter(lazy, "log", () =>
|
ChromeUtils.defineLazyGetter(lazy, "log", () =>
|
||||||
FormAutofill.defineLogGetter(lazy, "FormAutofillPrompter")
|
FormAutofill.defineLogGetter(lazy, "FormAutofillPrompter")
|
||||||
);
|
);
|
||||||
|
|
||||||
const l10n = new Localization(
|
const l10n = new Localization(
|
||||||
["browser/preferences/formAutofill.ftl", "branding/brand.ftl"],
|
[
|
||||||
|
"browser/preferences/formAutofill.ftl",
|
||||||
|
"toolkit/formautofill/formAutofill.ftl",
|
||||||
|
"branding/brand.ftl",
|
||||||
|
],
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
const { ENABLED_AUTOFILL_CREDITCARDS_PREF } = FormAutofill;
|
const { ENABLED_AUTOFILL_CREDITCARDS_PREF } = FormAutofill;
|
||||||
|
|
||||||
const GetStringFromName = FormAutofillUtils.stringBundle.GetStringFromName;
|
|
||||||
const formatStringFromName =
|
|
||||||
FormAutofillUtils.stringBundle.formatStringFromName;
|
|
||||||
const brandShortName =
|
|
||||||
FormAutofillUtils.brandBundle.GetStringFromName("brandShortName");
|
|
||||||
let autofillOptsKey = "autofillOptionsLink";
|
|
||||||
if (AppConstants.platform == "macosx") {
|
|
||||||
autofillOptsKey += "OSX";
|
|
||||||
}
|
|
||||||
|
|
||||||
let CONTENT = {};
|
let CONTENT = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -265,25 +260,16 @@ export class AutofillDoorhanger {
|
||||||
eventCallback: state => this.onEventCallback(state),
|
eventCallback: state => this.onEventCallback(state),
|
||||||
};
|
};
|
||||||
|
|
||||||
AutofillDoorhanger.setAnchor(this.doc, this.ui.anchor);
|
this.#setAnchor();
|
||||||
|
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
this.resolve = resolve;
|
this.resolve = resolve;
|
||||||
this.chromeWin.PopupNotifications.show(
|
this.chromeWin.PopupNotifications.show(
|
||||||
this.browser,
|
this.browser,
|
||||||
this.ui.id,
|
this.ui.id,
|
||||||
"",
|
this.getNotificationHeader?.() ?? "",
|
||||||
this.ui.anchor.id,
|
this.ui.anchor.id,
|
||||||
...AutofillDoorhanger.createActions(
|
...this.#createActions(),
|
||||||
this.ui.footer.mainAction,
|
|
||||||
this.ui.footer.secondaryActions,
|
|
||||||
resolve,
|
|
||||||
{
|
|
||||||
type: this.constructor.telemetryType,
|
|
||||||
object: this.constructor.telemetryObject,
|
|
||||||
flowId: this.flowId,
|
|
||||||
}
|
|
||||||
),
|
|
||||||
options
|
options
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
@ -307,57 +293,29 @@ export class AutofillDoorhanger {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an image element for notification anchor if it doesn't already exist.
|
* Create an image element for notification anchor if it doesn't already exist.
|
||||||
*
|
|
||||||
* @static
|
|
||||||
* @param {Document} doc - The document object where the anchor element should be created or modified.
|
|
||||||
* @param {object} anchor - An object containing the attributes for the anchor element.
|
|
||||||
* @param {string} anchor.id - The ID to assign to the anchor element.
|
|
||||||
* @param {string} anchor.URL - The image URL to set for the anchor element.
|
|
||||||
* @param {string} anchor.tooltiptext - The tooltip text to set for the anchor element.
|
|
||||||
*/
|
*/
|
||||||
// TODO: this is a static method so credit card doorhangers can also use this API.
|
#setAnchor() {
|
||||||
// we can change this to non-static after we impleemnt doorhanger with `class AutofillDoorhanger`
|
let anchor = this.doc.getElementById(this.ui.anchor.id);
|
||||||
static setAnchor(doc, anchor) {
|
if (!anchor) {
|
||||||
let anchorElement = doc.getElementById(anchor.id);
|
|
||||||
if (!anchorElement) {
|
|
||||||
const popupBox = doc.getElementById("notification-popup-box");
|
|
||||||
// Icon shown on URL bar
|
// Icon shown on URL bar
|
||||||
anchorElement = doc.createXULElement("image");
|
anchor = this.doc.createXULElement("image");
|
||||||
anchorElement.id = anchor.id;
|
anchor.id = this.ui.anchor.id;
|
||||||
anchorElement.setAttribute("src", anchor.URL);
|
anchor.setAttribute("src", this.ui.anchor.URL);
|
||||||
anchorElement.classList.add("notification-anchor-icon");
|
anchor.classList.add("notification-anchor-icon");
|
||||||
anchorElement.setAttribute("role", "button");
|
anchor.setAttribute("role", "button");
|
||||||
anchorElement.setAttribute("tooltiptext", anchor.tooltiptext);
|
anchor.setAttribute("tooltiptext", this.ui.anchor.tooltiptext);
|
||||||
popupBox.appendChild(anchorElement);
|
|
||||||
|
const popupBox = this.doc.getElementById("notification-popup-box");
|
||||||
|
popupBox.appendChild(anchor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate the main action and secondary actions from content parameters and
|
* Generate the main action and secondary actions from content parameters and
|
||||||
* promise resolve.
|
* promise resolve.
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @param {object} mainActionParams
|
|
||||||
* Parameters for main action.
|
|
||||||
* @param {Array<object>} secondaryActionParams
|
|
||||||
* Array of the parameters for secondary actions.
|
|
||||||
* @param {Function} onClick Should be called in action callback.
|
|
||||||
* @returns {Array<object>}
|
|
||||||
Return the mainAction and secondary actions in an array for showing doorhanger
|
|
||||||
*/
|
*/
|
||||||
// TODO: this is a static method so credit card doorhangers can also use this API.
|
#createActions() {
|
||||||
static createActions(
|
|
||||||
mainActionParams,
|
|
||||||
secondaryActionParams,
|
|
||||||
onClick,
|
|
||||||
telemetryOptions
|
|
||||||
) {
|
|
||||||
function getLabelAndAccessKey(param) {
|
function getLabelAndAccessKey(param) {
|
||||||
// This should be removed once we port credit card capture doorhanger to use fluent
|
|
||||||
if (!param.l10nId) {
|
|
||||||
return { label: param.label, accessKey: param.accessKey };
|
|
||||||
}
|
|
||||||
|
|
||||||
const msg = l10n.formatMessagesSync([{ id: param.l10nId }])[0];
|
const msg = l10n.formatMessagesSync([{ id: param.l10nId }])[0];
|
||||||
return {
|
return {
|
||||||
label: msg.attributes.find(x => x.name == "label").value,
|
label: msg.attributes.find(x => x.name == "label").value,
|
||||||
|
|
@ -366,15 +324,18 @@ export class AutofillDoorhanger {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mainActionParams = this.ui.footer.mainAction;
|
||||||
|
const secondaryActionParams = this.ui.footer.secondaryActions;
|
||||||
|
|
||||||
const callback = () => {
|
const callback = () => {
|
||||||
AutofillTelemetry.recordDoorhangerClicked(
|
AutofillTelemetry.recordDoorhangerClicked(
|
||||||
telemetryOptions.type,
|
this.constructor.telemetryType,
|
||||||
mainActionParams.callbackState,
|
mainActionParams.callbackState,
|
||||||
telemetryOptions.object,
|
this.constructor.telemetryObject,
|
||||||
telemetryOptions.flowId
|
this.flowId
|
||||||
);
|
);
|
||||||
|
|
||||||
onClick(mainActionParams.callbackState);
|
this.resolve(mainActionParams.callbackState);
|
||||||
};
|
};
|
||||||
|
|
||||||
const mainAction = {
|
const mainAction = {
|
||||||
|
|
@ -386,13 +347,13 @@ export class AutofillDoorhanger {
|
||||||
for (const params of secondaryActionParams) {
|
for (const params of secondaryActionParams) {
|
||||||
const callback = () => {
|
const callback = () => {
|
||||||
AutofillTelemetry.recordDoorhangerClicked(
|
AutofillTelemetry.recordDoorhangerClicked(
|
||||||
telemetryOptions.type,
|
this.constructor.telemetryType,
|
||||||
params.callbackState,
|
params.callbackState,
|
||||||
telemetryOptions.object,
|
this.constructor.telemetryObject,
|
||||||
telemetryOptions.flowId
|
this.flowId
|
||||||
);
|
);
|
||||||
|
|
||||||
onClick(params.callbackState);
|
this.resolve(params.callbackState);
|
||||||
};
|
};
|
||||||
|
|
||||||
secondaryActions.push({
|
secondaryActions.push({
|
||||||
|
|
@ -901,6 +862,168 @@ export class AddressEditDoorhanger extends AutofillDoorhanger {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class CreditCardSaveDoorhanger extends AutofillDoorhanger {
|
||||||
|
static contentClass = "credit-card-capture-content";
|
||||||
|
|
||||||
|
static telemetryType = AutofillTelemetry.CREDIT_CARD;
|
||||||
|
static telemetryObject = "capture_doorhanger";
|
||||||
|
|
||||||
|
static spotlightURL = "about:preferences#privacy-credit-card-autofill";
|
||||||
|
|
||||||
|
constructor(browser, oldRecord, newRecord, flowId) {
|
||||||
|
super(browser, oldRecord, newRecord, flowId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We have not yet sync address and credit card design. After syncing,
|
||||||
|
* we should be able to use the same "class"
|
||||||
|
*/
|
||||||
|
static content(panel) {
|
||||||
|
return panel.querySelector(`.${CreditCardSaveDoorhanger.contentClass}`);
|
||||||
|
}
|
||||||
|
get content() {
|
||||||
|
return CreditCardSaveDoorhanger.content(this.panel);
|
||||||
|
}
|
||||||
|
|
||||||
|
addCheckboxListener() {
|
||||||
|
if (!this.ui.options.checkbox) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { checkbox } = this.panel;
|
||||||
|
if (checkbox && !checkbox.hidden) {
|
||||||
|
checkbox.addEventListener("command", event => {
|
||||||
|
let { secondaryButton, menubutton } =
|
||||||
|
event.target.closest("popupnotification");
|
||||||
|
let checked = event.target.checked;
|
||||||
|
Services.prefs.setBoolPref("services.sync.engine.creditcards", checked);
|
||||||
|
secondaryButton.disabled = checked;
|
||||||
|
menubutton.disabled = checked;
|
||||||
|
lazy.log.debug("Set creditCard sync to", checked);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeCheckboxListener() {
|
||||||
|
if (!this.ui.options.checkbox) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { checkbox } = this.panel;
|
||||||
|
|
||||||
|
if (checkbox && !checkbox.hidden) {
|
||||||
|
checkbox.removeEventListener(
|
||||||
|
"command",
|
||||||
|
this.ui.options.checkbox.callback
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
appendDescription() {
|
||||||
|
const docFragment = this.doc.createDocumentFragment();
|
||||||
|
|
||||||
|
const label = this.doc.createXULElement("label");
|
||||||
|
this.doc.l10n.setAttributes(label, this.ui.description.l10nId);
|
||||||
|
docFragment.appendChild(label);
|
||||||
|
|
||||||
|
const descriptionWrapper = this.doc.createXULElement("hbox");
|
||||||
|
descriptionWrapper.className = "desc-message-box";
|
||||||
|
|
||||||
|
const number =
|
||||||
|
this.newRecord["cc-number"] || this.newRecord["cc-number-decrypted"];
|
||||||
|
const name = this.newRecord["cc-name"];
|
||||||
|
const network = lazy.CreditCard.getType(number);
|
||||||
|
|
||||||
|
const descriptionIcon = lazy.CreditCard.getCreditCardLogo(network);
|
||||||
|
if (descriptionIcon) {
|
||||||
|
const icon = this.doc.createXULElement("image");
|
||||||
|
if (
|
||||||
|
typeof descriptionIcon == "string" &&
|
||||||
|
(descriptionIcon.includes("cc-logo") ||
|
||||||
|
descriptionIcon.includes("icon-credit"))
|
||||||
|
) {
|
||||||
|
icon.setAttribute("src", descriptionIcon);
|
||||||
|
}
|
||||||
|
descriptionWrapper.appendChild(icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
const description = this.doc.createXULElement("description");
|
||||||
|
description.textContent =
|
||||||
|
`${lazy.CreditCard.getMaskedNumber(number)}` + (name ? `, ${name}` : ``);
|
||||||
|
|
||||||
|
descriptionWrapper.appendChild(description);
|
||||||
|
docFragment.appendChild(descriptionWrapper);
|
||||||
|
|
||||||
|
this.content.appendChild(docFragment);
|
||||||
|
}
|
||||||
|
|
||||||
|
appendPrivacyPanelLink() {
|
||||||
|
const privacyLinkElement = this.doc.createXULElement("label", {
|
||||||
|
is: "text-link",
|
||||||
|
});
|
||||||
|
privacyLinkElement.setAttribute("useoriginprincipal", true);
|
||||||
|
privacyLinkElement.setAttribute(
|
||||||
|
"href",
|
||||||
|
CreditCardSaveDoorhanger.spotlightURL ||
|
||||||
|
"about:preferences#privacy-form-autofill"
|
||||||
|
);
|
||||||
|
|
||||||
|
const linkId = `autofill-options-link${
|
||||||
|
AppConstants.platform == "macosx" ? "-osx" : ""
|
||||||
|
}`;
|
||||||
|
this.doc.l10n.setAttributes(privacyLinkElement, linkId);
|
||||||
|
|
||||||
|
this.content.appendChild(privacyLinkElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Currently, the header and description are unused. Align
|
||||||
|
// these with the address doorhanger's implementation during
|
||||||
|
// the credit card doorhanger redesign.
|
||||||
|
getNotificationHeader() {
|
||||||
|
return l10n.formatValueSync(this.ui.header.l10nId);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderHeader() {
|
||||||
|
// Not implement
|
||||||
|
}
|
||||||
|
|
||||||
|
renderDescription() {
|
||||||
|
// Not implement
|
||||||
|
}
|
||||||
|
|
||||||
|
renderContent() {
|
||||||
|
this.content.replaceChildren();
|
||||||
|
|
||||||
|
this.appendDescription();
|
||||||
|
|
||||||
|
this.appendPrivacyPanelLink();
|
||||||
|
}
|
||||||
|
|
||||||
|
onEventCallback(state) {
|
||||||
|
super.onEventCallback(state);
|
||||||
|
|
||||||
|
if (state == "removed" || state == "dismissed") {
|
||||||
|
this.removeCheckboxListener();
|
||||||
|
} else if (state == "shown") {
|
||||||
|
this.addCheckboxListener();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The record to be saved by this doorhanger
|
||||||
|
recordToSave() {
|
||||||
|
return this.newRecord;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CreditCardUpdateDoorhanger extends CreditCardSaveDoorhanger {
|
||||||
|
static telemetryType = AutofillTelemetry.CREDIT_CARD;
|
||||||
|
static telemetryObject = "update_doorhanger";
|
||||||
|
|
||||||
|
constructor(browser, oldRecord, newRecord, flowId) {
|
||||||
|
super(browser, oldRecord, newRecord, flowId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CONTENT = {
|
CONTENT = {
|
||||||
[AddressSaveDoorhanger.name]: {
|
[AddressSaveDoorhanger.name]: {
|
||||||
id: "address-save-update",
|
id: "address-save-update",
|
||||||
|
|
@ -1068,40 +1191,41 @@ CONTENT = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
addCreditCard: {
|
[CreditCardSaveDoorhanger.name]: {
|
||||||
notificationId: "autofill-credit-card-add",
|
id: "credit-card-save-update",
|
||||||
message: formatStringFromName("saveCreditCardMessage", [brandShortName]),
|
|
||||||
descriptionLabel: GetStringFromName("saveCreditCardDescriptionLabel"),
|
|
||||||
descriptionIcon: true,
|
|
||||||
linkMessage: GetStringFromName(autofillOptsKey),
|
|
||||||
spotlightURL: "about:preferences#privacy-credit-card-autofill",
|
|
||||||
anchor: {
|
anchor: {
|
||||||
id: "autofill-credit-card-notification-icon",
|
id: "autofill-credit-card-notification-icon",
|
||||||
URL: "chrome://formautofill/content/formfill-anchor.svg",
|
URL: "chrome://formautofill/content/formfill-anchor.svg",
|
||||||
tooltiptext: l10n.formatValueSync("autofill-message-tooltip"),
|
tooltiptext: l10n.formatValueSync("autofill-message-tooltip"),
|
||||||
},
|
},
|
||||||
|
header: {
|
||||||
|
l10nId: "credit-card-save-doorhanger-header",
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
l10nId: "credit-card-save-doorhanger-description",
|
||||||
|
},
|
||||||
|
content: {},
|
||||||
|
footer: {
|
||||||
mainAction: {
|
mainAction: {
|
||||||
label: GetStringFromName("saveCreditCardLabel"),
|
l10nId: "credit-card-capture-save-button",
|
||||||
accessKey: GetStringFromName("saveCreditCardAccessKey"),
|
callbackState: "create",
|
||||||
callbackState: "save",
|
|
||||||
confimationHintId: "confirmation-hint-credit-card-created",
|
|
||||||
},
|
},
|
||||||
secondaryActions: [
|
secondaryActions: [
|
||||||
{
|
{
|
||||||
label: GetStringFromName("cancelCreditCardLabel"),
|
l10nId: "credit-card-capture-cancel-button",
|
||||||
accessKey: GetStringFromName("cancelCreditCardAccessKey"),
|
|
||||||
callbackState: "cancel",
|
callbackState: "cancel",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: GetStringFromName("neverSaveCreditCardLabel"),
|
l10nId: "credit-card-capture-never-save-button",
|
||||||
accessKey: GetStringFromName("neverSaveCreditCardAccessKey"),
|
|
||||||
callbackState: "disable",
|
callbackState: "disable",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
},
|
||||||
options: {
|
options: {
|
||||||
persistWhileVisible: true,
|
persistWhileVisible: true,
|
||||||
popupIconURL: "chrome://formautofill/content/icon-credit-card.svg",
|
popupIconURL: "chrome://formautofill/content/icon-credit-card.svg",
|
||||||
hideClose: true,
|
hideClose: true,
|
||||||
|
|
||||||
checkbox: {
|
checkbox: {
|
||||||
get checked() {
|
get checked() {
|
||||||
return Services.prefs.getBoolPref("services.sync.engine.creditcards");
|
return Services.prefs.getBoolPref("services.sync.engine.creditcards");
|
||||||
|
|
@ -1117,50 +1241,41 @@ CONTENT = {
|
||||||
Services.prefs.getBoolPref(
|
Services.prefs.getBoolPref(
|
||||||
"services.sync.engine.creditcards.available"
|
"services.sync.engine.creditcards.available"
|
||||||
)
|
)
|
||||||
? GetStringFromName("creditCardsSyncCheckbox")
|
? l10n.formatValueSync(
|
||||||
|
"credit-card-doorhanger-credit-cards-sync-checkbox"
|
||||||
|
)
|
||||||
: null;
|
: null;
|
||||||
},
|
},
|
||||||
callback(event) {
|
|
||||||
let { secondaryButton, menubutton } =
|
|
||||||
event.target.closest("popupnotification");
|
|
||||||
let checked = event.target.checked;
|
|
||||||
Services.prefs.setBoolPref(
|
|
||||||
"services.sync.engine.creditcards",
|
|
||||||
checked
|
|
||||||
);
|
|
||||||
secondaryButton.disabled = checked;
|
|
||||||
menubutton.disabled = checked;
|
|
||||||
lazy.log.debug("Set creditCard sync to", checked);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
updateCreditCard: {
|
[CreditCardUpdateDoorhanger.name]: {
|
||||||
notificationId: "autofill-credit-card-update",
|
id: "credit-card-save-update",
|
||||||
message: GetStringFromName("updateCreditCardMessage"),
|
|
||||||
descriptionLabel: GetStringFromName("updateCreditCardDescriptionLabel"),
|
|
||||||
descriptionIcon: true,
|
|
||||||
linkMessage: GetStringFromName(autofillOptsKey),
|
|
||||||
spotlightURL: "about:preferences#privacy-credit-card-autofill",
|
|
||||||
anchor: {
|
anchor: {
|
||||||
id: "autofill-credit-card-notification-icon",
|
id: "autofill-credit-card-notification-icon",
|
||||||
URL: "chrome://formautofill/content/formfill-anchor.svg",
|
URL: "chrome://formautofill/content/formfill-anchor.svg",
|
||||||
tooltiptext: l10n.formatValueSync("autofill-message-tooltip"),
|
tooltiptext: l10n.formatValueSync("autofill-message-tooltip"),
|
||||||
},
|
},
|
||||||
|
header: {
|
||||||
|
l10nId: "credit-card-update-doorhanger-header",
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
l10nId: "credit-card-update-doorhanger-description",
|
||||||
|
},
|
||||||
|
content: {},
|
||||||
|
footer: {
|
||||||
mainAction: {
|
mainAction: {
|
||||||
label: GetStringFromName("updateCreditCardLabel"),
|
l10nId: "credit-card-capture-update-button",
|
||||||
accessKey: GetStringFromName("updateCreditCardAccessKey"),
|
|
||||||
callbackState: "update",
|
callbackState: "update",
|
||||||
confirmationHintId: "confirmation-hint-credit-card-updated",
|
|
||||||
},
|
},
|
||||||
secondaryActions: [
|
secondaryActions: [
|
||||||
{
|
{
|
||||||
label: GetStringFromName("createCreditCardLabel"),
|
l10nId: "credit-card-capture-save-new-button",
|
||||||
accessKey: GetStringFromName("createCreditCardAccessKey"),
|
|
||||||
callbackState: "create",
|
callbackState: "create",
|
||||||
confirmationHintId: "confirmation-hint-credit-card-created",
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
},
|
||||||
options: {
|
options: {
|
||||||
persistWhileVisible: true,
|
persistWhileVisible: true,
|
||||||
popupIconURL: "chrome://formautofill/content/icon-credit-card.svg",
|
popupIconURL: "chrome://formautofill/content/icon-credit-card.svg",
|
||||||
|
|
@ -1170,140 +1285,25 @@ CONTENT = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export let FormAutofillPrompter = {
|
export let FormAutofillPrompter = {
|
||||||
/**
|
async promptToSaveCreditCard(
|
||||||
* Append the link label element to the popupnotificationcontent.
|
|
||||||
*
|
|
||||||
* @param {XULElement} content
|
|
||||||
* popupnotificationcontent
|
|
||||||
* @param {string} message
|
|
||||||
* The localized string for link title.
|
|
||||||
* @param {string} link
|
|
||||||
* Makes it possible to open and highlight a section in preferences
|
|
||||||
*/
|
|
||||||
_appendPrivacyPanelLink(content, message, link) {
|
|
||||||
let chromeDoc = content.ownerDocument;
|
|
||||||
let privacyLinkElement = chromeDoc.createXULElement("label", {
|
|
||||||
is: "text-link",
|
|
||||||
});
|
|
||||||
privacyLinkElement.setAttribute("useoriginprincipal", true);
|
|
||||||
privacyLinkElement.setAttribute(
|
|
||||||
"href",
|
|
||||||
link || "about:preferences#privacy-form-autofill"
|
|
||||||
);
|
|
||||||
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.
|
|
||||||
* @param {string} descriptionId
|
|
||||||
* The id of description
|
|
||||||
*/
|
|
||||||
_appendDescription(
|
|
||||||
content,
|
|
||||||
descriptionLabel,
|
|
||||||
descriptionIcon,
|
|
||||||
descriptionId
|
|
||||||
) {
|
|
||||||
let chromeDoc = content.ownerDocument;
|
|
||||||
let docFragment = chromeDoc.createDocumentFragment();
|
|
||||||
|
|
||||||
let descriptionLabelElement = chromeDoc.createXULElement("label");
|
|
||||||
descriptionLabelElement.setAttribute("value", descriptionLabel);
|
|
||||||
docFragment.appendChild(descriptionLabelElement);
|
|
||||||
|
|
||||||
let descriptionWrapper = chromeDoc.createXULElement("hbox");
|
|
||||||
descriptionWrapper.className = "desc-message-box";
|
|
||||||
|
|
||||||
if (descriptionIcon) {
|
|
||||||
let descriptionIconElement = chromeDoc.createXULElement("image");
|
|
||||||
if (
|
|
||||||
typeof descriptionIcon == "string" &&
|
|
||||||
(descriptionIcon.includes("cc-logo") ||
|
|
||||||
descriptionIcon.includes("icon-credit"))
|
|
||||||
) {
|
|
||||||
descriptionIconElement.setAttribute("src", descriptionIcon);
|
|
||||||
}
|
|
||||||
descriptionWrapper.appendChild(descriptionIconElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
let descriptionElement = chromeDoc.createXULElement(descriptionId);
|
|
||||||
descriptionWrapper.appendChild(descriptionElement);
|
|
||||||
docFragment.appendChild(descriptionWrapper);
|
|
||||||
|
|
||||||
content.appendChild(docFragment);
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateDescription(content, descriptionId, description) {
|
|
||||||
let element = content.querySelector(descriptionId);
|
|
||||||
element.textContent = description;
|
|
||||||
},
|
|
||||||
|
|
||||||
_getNotificationElm(browser, id) {
|
|
||||||
let notificationId = id + "-notification";
|
|
||||||
let chromeDoc = browser.ownerDocument;
|
|
||||||
return chromeDoc.getElementById(notificationId);
|
|
||||||
},
|
|
||||||
|
|
||||||
_addCheckboxListener(browser, { notificationId, options }) {
|
|
||||||
if (!options.checkbox) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let { checkbox } = this._getNotificationElm(browser, notificationId);
|
|
||||||
|
|
||||||
if (checkbox && !checkbox.hidden) {
|
|
||||||
checkbox.addEventListener("command", options.checkbox.callback);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_removeCheckboxListener(browser, { notificationId, options }) {
|
|
||||||
if (!options.checkbox) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let { checkbox } = this._getNotificationElm(browser, notificationId);
|
|
||||||
|
|
||||||
if (checkbox && !checkbox.hidden) {
|
|
||||||
checkbox.removeEventListener("command", options.checkbox.callback);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async promptToSaveCreditCard(browser, storage, record, flowId) {
|
|
||||||
// Overwrite the guid if there is a duplicate
|
|
||||||
let doorhangerType;
|
|
||||||
const duplicateRecord = (await storage.getDuplicateRecords(record).next())
|
|
||||||
.value;
|
|
||||||
if (duplicateRecord) {
|
|
||||||
doorhangerType = "updateCreditCard";
|
|
||||||
} else {
|
|
||||||
doorhangerType = "addCreditCard";
|
|
||||||
}
|
|
||||||
|
|
||||||
lazy.log.debug(
|
|
||||||
`Show the ${duplicateRecord ? "update" : "sace"} credit card doorhanger`
|
|
||||||
);
|
|
||||||
|
|
||||||
const number = record["cc-number"] || record["cc-number-decrypted"];
|
|
||||||
const name = record["cc-name"];
|
|
||||||
const network = lazy.CreditCard.getType(number);
|
|
||||||
const maskedNumber = lazy.CreditCard.getMaskedNumber(number);
|
|
||||||
const description = `${maskedNumber}` + (name ? `, ${name}` : ``);
|
|
||||||
const descriptionIcon = lazy.CreditCard.getCreditCardLogo(network);
|
|
||||||
|
|
||||||
const action = await FormAutofillPrompter._showCreditCardCaptureDoorhanger(
|
|
||||||
browser,
|
browser,
|
||||||
doorhangerType,
|
storage,
|
||||||
description,
|
|
||||||
flowId,
|
flowId,
|
||||||
{ descriptionIcon }
|
{ oldRecord, newRecord }
|
||||||
|
) {
|
||||||
|
const showUpdateDoorhanger = !!Object.keys(oldRecord).length;
|
||||||
|
|
||||||
|
const { ownerGlobal: win } = browser;
|
||||||
|
win.MozXULElement.insertFTLIfNeeded(
|
||||||
|
"toolkit/formautofill/formAutofill.ftl"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let action;
|
||||||
|
const doorhanger = showUpdateDoorhanger
|
||||||
|
? new CreditCardUpdateDoorhanger(browser, oldRecord, newRecord, flowId)
|
||||||
|
: new CreditCardSaveDoorhanger(browser, oldRecord, newRecord, flowId);
|
||||||
|
action = await doorhanger.show();
|
||||||
|
|
||||||
lazy.log.debug(`Doorhanger action is ${action}`);
|
lazy.log.debug(`Doorhanger action is ${action}`);
|
||||||
|
|
||||||
if (action == "cancel") {
|
if (action == "cancel") {
|
||||||
|
|
@ -1322,145 +1322,11 @@ export let FormAutofillPrompter = {
|
||||||
browser,
|
browser,
|
||||||
storage,
|
storage,
|
||||||
"credit-card",
|
"credit-card",
|
||||||
action == "update" ? duplicateRecord : null,
|
action == "update" ? oldRecord : null,
|
||||||
record
|
doorhanger.recordToSave()
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
// TODO: Simplify the code after integrating credit card prompt to use AutofillDoorhanger
|
|
||||||
async _updateStorageAfterInteractWithPrompt(
|
|
||||||
browser,
|
|
||||||
storage,
|
|
||||||
type,
|
|
||||||
oldRecord,
|
|
||||||
newRecord
|
|
||||||
) {
|
|
||||||
let changedGUID = null;
|
|
||||||
if (oldRecord) {
|
|
||||||
changedGUID = oldRecord.guid;
|
|
||||||
await storage.update(changedGUID, newRecord, true);
|
|
||||||
} else {
|
|
||||||
changedGUID = await storage.add(newRecord);
|
|
||||||
}
|
|
||||||
storage.notifyUsed(changedGUID);
|
|
||||||
|
|
||||||
const hintId = `confirmation-hint-${type}-${
|
|
||||||
oldRecord ? "updated" : "created"
|
|
||||||
}`;
|
|
||||||
showConfirmation(browser, hintId);
|
|
||||||
},
|
|
||||||
|
|
||||||
_getUpdatedCCIcon(network) {
|
|
||||||
return FormAutofillUtils.getCreditCardLogo(network);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
* @param {string} flowId guid used to correlate events relating to the same form
|
|
||||||
* @param {object} [options = {}] a list of options for this method
|
|
||||||
* @param {string} options.descriptionIcon The icon for descriotion
|
|
||||||
* @returns {Promise} Resolved with action type when action callback is triggered.
|
|
||||||
*/
|
|
||||||
async _showCreditCardCaptureDoorhanger(
|
|
||||||
browser,
|
|
||||||
type,
|
|
||||||
description,
|
|
||||||
flowId,
|
|
||||||
{ descriptionIcon = null }
|
|
||||||
) {
|
|
||||||
const telemetryType = AutofillTelemetry.CREDIT_CARD;
|
|
||||||
const telemetryObject = type.startsWith("add")
|
|
||||||
? "capture_doorhanger"
|
|
||||||
: "update_doorhanger";
|
|
||||||
|
|
||||||
AutofillTelemetry.recordDoorhangerShown(
|
|
||||||
telemetryType,
|
|
||||||
telemetryObject,
|
|
||||||
flowId
|
|
||||||
);
|
|
||||||
|
|
||||||
return new Promise(resolve => {
|
|
||||||
let {
|
|
||||||
notificationId,
|
|
||||||
message,
|
|
||||||
descriptionLabel,
|
|
||||||
linkMessage,
|
|
||||||
spotlightURL,
|
|
||||||
anchor,
|
|
||||||
mainAction,
|
|
||||||
secondaryActions,
|
|
||||||
options,
|
|
||||||
} = CONTENT[type];
|
|
||||||
descriptionIcon = descriptionIcon ?? CONTENT[type].descriptionIcon;
|
|
||||||
const { ownerGlobal: chromeWin, ownerDocument: chromeDoc } = browser;
|
|
||||||
|
|
||||||
options.eventCallback = topic => {
|
|
||||||
lazy.log.debug("eventCallback:", topic);
|
|
||||||
|
|
||||||
if (topic == "removed" || topic == "dismissed") {
|
|
||||||
this._removeCheckboxListener(browser, { notificationId, options });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The doorhanger is customizable only when notification box is shown
|
|
||||||
if (topic != "shown") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._addCheckboxListener(browser, { notificationId, options });
|
|
||||||
|
|
||||||
const DESCRIPTION_ID = "description";
|
|
||||||
const NOTIFICATION_ID = notificationId + "-notification";
|
|
||||||
|
|
||||||
const notification = chromeDoc.getElementById(NOTIFICATION_ID);
|
|
||||||
const notificationContent =
|
|
||||||
notification.querySelector("popupnotificationcontent") ||
|
|
||||||
chromeDoc.createXULElement("popupnotificationcontent");
|
|
||||||
if (!notification.contains(notificationContent)) {
|
|
||||||
notificationContent.setAttribute("orient", "vertical");
|
|
||||||
|
|
||||||
this._appendDescription(
|
|
||||||
notificationContent,
|
|
||||||
descriptionLabel,
|
|
||||||
descriptionIcon,
|
|
||||||
DESCRIPTION_ID
|
|
||||||
);
|
|
||||||
|
|
||||||
this._appendPrivacyPanelLink(
|
|
||||||
notificationContent,
|
|
||||||
linkMessage,
|
|
||||||
spotlightURL
|
|
||||||
);
|
|
||||||
|
|
||||||
notification.appendNotificationContent(notificationContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._updateDescription(
|
|
||||||
notificationContent,
|
|
||||||
DESCRIPTION_ID,
|
|
||||||
description
|
|
||||||
);
|
|
||||||
};
|
|
||||||
AutofillDoorhanger.setAnchor(browser.ownerDocument, anchor);
|
|
||||||
chromeWin.PopupNotifications.show(
|
|
||||||
browser,
|
|
||||||
notificationId,
|
|
||||||
message,
|
|
||||||
anchor.id,
|
|
||||||
...AutofillDoorhanger.createActions(
|
|
||||||
mainAction,
|
|
||||||
secondaryActions,
|
|
||||||
resolve,
|
|
||||||
{ type: telemetryType, object: telemetryObject, flowId }
|
|
||||||
),
|
|
||||||
options
|
|
||||||
);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show save or update address doorhanger
|
* Show save or update address doorhanger
|
||||||
*
|
*
|
||||||
|
|
@ -1483,11 +1349,13 @@ export let FormAutofillPrompter = {
|
||||||
`Show the ${showUpdateDoorhanger ? "update" : "save"} address doorhanger`
|
`Show the ${showUpdateDoorhanger ? "update" : "save"} address doorhanger`
|
||||||
);
|
);
|
||||||
|
|
||||||
const { ownerGlobal: chromeWin } = browser;
|
const { ownerGlobal: win } = browser;
|
||||||
await chromeWin.ensureCustomElements("moz-support-link");
|
await win.ensureCustomElements("moz-support-link");
|
||||||
chromeWin.MozXULElement.insertFTLIfNeeded(
|
win.MozXULElement.insertFTLIfNeeded(
|
||||||
"browser/preferences/formAutofill.ftl"
|
"toolkit/formautofill/formAutofill.ftl"
|
||||||
);
|
);
|
||||||
|
// address-autofill-* are defined in browser/preferences now
|
||||||
|
win.MozXULElement.insertFTLIfNeeded("browser/preferences/formAutofill.ftl");
|
||||||
|
|
||||||
let doorhanger;
|
let doorhanger;
|
||||||
let action;
|
let action;
|
||||||
|
|
@ -1529,4 +1397,27 @@ export let FormAutofillPrompter = {
|
||||||
doorhanger.recordToSave()
|
doorhanger.recordToSave()
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// TODO: Simplify the code after integrating credit card prompt to use AutofillDoorhanger
|
||||||
|
async _updateStorageAfterInteractWithPrompt(
|
||||||
|
browser,
|
||||||
|
storage,
|
||||||
|
type,
|
||||||
|
oldRecord,
|
||||||
|
newRecord
|
||||||
|
) {
|
||||||
|
let changedGUID = null;
|
||||||
|
if (oldRecord) {
|
||||||
|
changedGUID = oldRecord.guid;
|
||||||
|
await storage.update(changedGUID, newRecord, true);
|
||||||
|
} else {
|
||||||
|
changedGUID = await storage.add(newRecord);
|
||||||
|
}
|
||||||
|
storage.notifyUsed(changedGUID);
|
||||||
|
|
||||||
|
const hintId = `confirmation-hint-${type}-${
|
||||||
|
oldRecord ? "updated" : "created"
|
||||||
|
}`;
|
||||||
|
showConfirmation(browser, hintId);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -16,3 +16,39 @@ autofill-use-payment-method-os-prompt-other = { -brand-short-name } is trying to
|
||||||
autofill-edit-payment-method-os-prompt-macos = show stored payment method information
|
autofill-edit-payment-method-os-prompt-macos = show stored payment method information
|
||||||
autofill-edit-payment-method-os-prompt-windows = { -brand-short-name } is trying to show stored payment method information. Confirm access to this Windows account below.
|
autofill-edit-payment-method-os-prompt-windows = { -brand-short-name } is trying to show stored payment method information. Confirm access to this Windows account below.
|
||||||
autofill-edit-payment-method-os-prompt-other = { -brand-short-name } is trying to show stored payment method information.
|
autofill-edit-payment-method-os-prompt-other = { -brand-short-name } is trying to show stored payment method information.
|
||||||
|
|
||||||
|
# The links lead users to Form Autofill browser preferences.
|
||||||
|
autofill-options-link = Form Autofill Options
|
||||||
|
autofill-options-link-osx = Form Autofill Preferences
|
||||||
|
|
||||||
|
## The credit card capture doorhanger
|
||||||
|
|
||||||
|
# If Sync is enabled and credit card sync is available,
|
||||||
|
# this checkbox is displayed on the doorhanger shown when saving credit card.
|
||||||
|
credit-card-doorhanger-credit-cards-sync-checkbox = Sync all saved cards across my devices
|
||||||
|
|
||||||
|
# Used on the doorhanger when users submit payment with credit card.
|
||||||
|
credit-card-save-doorhanger-header = Securely save this card?
|
||||||
|
credit-card-save-doorhanger-description = { -brand-short-name } encrypts your card number. Your security code won’t be saved.
|
||||||
|
|
||||||
|
credit-card-capture-save-button =
|
||||||
|
.label = Save
|
||||||
|
.accessKey = S
|
||||||
|
credit-card-capture-cancel-button =
|
||||||
|
.label = Not now
|
||||||
|
.accessKey = W
|
||||||
|
credit-card-capture-never-save-button =
|
||||||
|
.label = Never save cards
|
||||||
|
.accessKey = N
|
||||||
|
|
||||||
|
# Used on the doorhanger when an credit card change is detected.
|
||||||
|
|
||||||
|
credit-card-update-doorhanger-header = Update card?
|
||||||
|
credit-card-update-doorhanger-description = Card to update:
|
||||||
|
|
||||||
|
credit-card-capture-save-new-button =
|
||||||
|
.label = Save as new card
|
||||||
|
.accessKey = C
|
||||||
|
credit-card-capture-update-button =
|
||||||
|
.label = Update existing card
|
||||||
|
.accessKey = U
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue