diff --git a/browser/components/payments/content/paymentDialogWrapper.js b/browser/components/payments/content/paymentDialogWrapper.js index 6cd035a304e2..dd769944dad4 100644 --- a/browser/components/payments/content/paymentDialogWrapper.js +++ b/browser/components/payments/content/paymentDialogWrapper.js @@ -560,52 +560,69 @@ var paymentDialogWrapper = { window.close(); }, - async onUpdateAutofillRecord(collectionName, record, guid, messageID) { - let responseMessage = { - guid, - messageID, - stateChange: {}, - }; - try { - if (collectionName == "creditCards" && !guid && !record.isTemporary) { - // We need to be logged in so we can encrypt the credit card number and - // that's only supported when we're adding a new record. - // TODO: "MasterPassword.ensureLoggedIn" can be removed after the storage - // APIs are refactored to be async functions (bug 1399367). - if (!await MasterPassword.ensureLoggedIn()) { - throw new Error("User canceled master password entry"); - } + async onUpdateAutofillRecord(collectionName, record, guid, { + errorStateChange, + preserveOldProperties, + selectedStateKey, + successStateChange, + }) { + if (collectionName == "creditCards" && !guid && !record.isTemporary) { + // We need to be logged in so we can encrypt the credit card number and + // that's only supported when we're adding a new record. + // TODO: "MasterPassword.ensureLoggedIn" can be removed after the storage + // APIs are refactored to be async functions (bug 1399367). + if (!await MasterPassword.ensureLoggedIn()) { + Cu.reportError("User canceled master password entry"); + return; } + } + let isTemporary = record.isTemporary; + let collection = isTemporary ? this.temporaryStore[collectionName] : + formAutofillStorage[collectionName]; - let isTemporary = record.isTemporary; - let collection = isTemporary ? this.temporaryStore[collectionName] : - formAutofillStorage[collectionName]; - + try { if (guid) { - let preserveOldProperties = true; await collection.update(guid, record, preserveOldProperties); } else { - responseMessage.guid = await collection.add(record); + guid = await collection.add(record); } if (isTemporary && collectionName == "addresses") { // there will be no formautofill-storage-changed event to update state // so add updated collection here - Object.assign(responseMessage.stateChange, { + Object.assign(successStateChange, { tempAddresses: this.temporaryStore.addresses.getAll(), }); } if (isTemporary && collectionName == "creditCards") { // there will be no formautofill-storage-changed event to update state // so add updated collection here - Object.assign(responseMessage.stateChange, { + Object.assign(successStateChange, { tempBasicCards: this.temporaryStore.creditCards.getAll(), }); } + + // Select the new record + if (selectedStateKey) { + if (selectedStateKey.length == 1) { + Object.assign(successStateChange, { + [selectedStateKey[0]]: guid, + }); + } else if (selectedStateKey.length == 2) { + // Need to keep properties like preserveFieldValues from getting removed. + let subObj = Object.assign({}, successStateChange[selectedStateKey[0]]); + subObj[selectedStateKey[1]] = guid; + Object.assign(successStateChange, { + [selectedStateKey[0]]: subObj, + }); + } else { + throw new Error(`selectedStateKey not supported: '${selectedStateKey}'`); + } + } + + this.sendMessageToContent("updateState", successStateChange); } catch (ex) { - responseMessage.error = true; - } finally { - this.sendMessageToContent("updateAutofillRecord:Response", responseMessage); + this.sendMessageToContent("updateState", errorStateChange); } }, @@ -678,7 +695,12 @@ var paymentDialogWrapper = { break; } case "updateAutofillRecord": { - this.onUpdateAutofillRecord(data.collectionName, data.record, data.guid, data.messageID); + this.onUpdateAutofillRecord(data.collectionName, data.record, data.guid, { + errorStateChange: data.errorStateChange, + preserveOldProperties: data.preserveOldProperties, + selectedStateKey: data.selectedStateKey, + successStateChange: data.successStateChange, + }); break; } } diff --git a/browser/components/payments/res/components/basic-card-option.css b/browser/components/payments/res/components/basic-card-option.css index 754cfd7a45d6..18ad5648815e 100644 --- a/browser/components/payments/res/components/basic-card-option.css +++ b/browser/components/payments/res/components/basic-card-option.css @@ -3,9 +3,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ basic-card-option { - grid-column-gap: 1ch; - grid-template-areas: "type cc-number cc-exp cc-name"; - justify-content: start; + grid-row-gap: 5px; + grid-column-gap: 10px; + grid-template-areas: "type cc-number cc-name cc-exp"; } basic-card-option > .cc-number { diff --git a/browser/components/payments/res/containers/address-form.js b/browser/components/payments/res/containers/address-form.js index 2d1c633d711e..903931aa1fb7 100644 --- a/browser/components/payments/res/containers/address-form.js +++ b/browser/components/payments/res/containers/address-form.js @@ -243,7 +243,7 @@ export default class AddressForm extends PaymentStateSubscriberMixin(PaymentRequ } } - async saveRecord() { + saveRecord() { let record = this.formHandler.buildFormObject(); let currentState = this.requestStore.getState(); let { @@ -258,15 +258,22 @@ export default class AddressForm extends PaymentStateSubscriberMixin(PaymentRequ record.isTemporary = true; } - let successStateChange; + let state = { + errorStateChange: { + page: { + id: "address-page", + onboardingWizard: page.onboardingWizard, + error: this.dataset.errorGenericSave, + }, + "address-page": addressPage, + }, + preserveOldProperties: true, + selectedStateKey: page.selectedStateKey, + }; + const previousId = page.previousId; if (page.onboardingWizard && !Object.keys(savedBasicCards).length) { - successStateChange = { - "basic-card-page": { - // Preserve field values as the user may have already edited the card - // page and went back to the address page to make a correction. - preserveFieldValues: true, - }, + state.successStateChange = { page: { id: "basic-card-page", previousId: "address-page", @@ -274,7 +281,7 @@ export default class AddressForm extends PaymentStateSubscriberMixin(PaymentRequ }, }; } else { - successStateChange = { + state.successStateChange = { page: { id: previousId || "payment-summary", onboardingWizard: page.onboardingWizard, @@ -283,40 +290,11 @@ export default class AddressForm extends PaymentStateSubscriberMixin(PaymentRequ } if (previousId) { - successStateChange[previousId] = Object.assign({}, currentState[previousId]); - successStateChange[previousId].preserveFieldValues = true; + state.successStateChange[previousId] = Object.assign({}, currentState[previousId]); + state.successStateChange[previousId].preserveFieldValues = true; } - try { - let {guid} = await paymentRequest.updateAutofillRecord("addresses", record, addressPage.guid); - let selectedStateKey = addressPage.selectedStateKey; - - if (selectedStateKey.length == 1) { - Object.assign(successStateChange, { - [selectedStateKey[0]]: guid, - }); - } else if (selectedStateKey.length == 2) { - // Need to keep properties like preserveFieldValues from getting removed. - let subObj = Object.assign({}, successStateChange[selectedStateKey[0]]); - subObj[selectedStateKey[1]] = guid; - Object.assign(successStateChange, { - [selectedStateKey[0]]: subObj, - }); - } else { - throw new Error(`selectedStateKey not supported: '${selectedStateKey}'`); - } - - this.requestStore.setState(successStateChange); - } catch (ex) { - log.warn("saveRecord: error:", ex); - this.requestStore.setState({ - page: { - id: "address-page", - onboardingWizard: page.onboardingWizard, - error: this.dataset.errorGenericSave, - }, - }); - } + paymentRequest.updateAutofillRecord("addresses", record, addressPage.guid, state); } } diff --git a/browser/components/payments/res/containers/address-picker.js b/browser/components/payments/res/containers/address-picker.js index c0fdc8f9e381..fead1239c2de 100644 --- a/browser/components/payments/res/containers/address-picker.js +++ b/browser/components/payments/res/containers/address-picker.js @@ -144,10 +144,10 @@ export default class AddressPicker extends RichPicker { let nextState = { page: { id: "address-page", + selectedStateKey: [this.selectedStateKey], }, "address-page": { addressFields: this.getAttribute("address-fields"), - selectedStateKey: [this.selectedStateKey], }, }; diff --git a/browser/components/payments/res/containers/basic-card-form.js b/browser/components/payments/res/containers/basic-card-form.js index c55f5a9dccbe..1acbaecb22f9 100644 --- a/browser/components/payments/res/containers/basic-card-form.js +++ b/browser/components/payments/res/containers/basic-card-form.js @@ -22,8 +22,6 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe super(); this.genericErrorText = document.createElement("div"); - this.genericErrorText.setAttribute("aria-live", "polite"); - this.genericErrorText.classList.add("page-error"); this.addressAddLink = document.createElement("a"); this.addressAddLink.className = "add-link"; @@ -56,8 +54,6 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe let url = "formautofill/editCreditCard.xhtml"; this.promiseReady = this._fetchMarkup(url).then(doc => { this.form = doc.getElementById("form"); - this.form.addEventListener("input", this); - this.form.addEventListener("invalid", this); return this.form; }); } @@ -167,8 +163,6 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe } else if (!editing) { billingAddressSelect.value = Object.keys(addresses)[0]; } - - this.updateSaveButtonState(); } handleEvent(event) { @@ -177,14 +171,6 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe this.onClick(event); break; } - case "input": { - this.onInput(event); - break; - } - case "invalid": { - this.onInvalid(event); - break; - } } } @@ -203,10 +189,10 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe page: { id: "address-page", previousId: "basic-card-page", + selectedStateKey: ["basic-card-page", "billingAddressGUID"], }, "address-page": { guid: null, - selectedStateKey: ["basic-card-page", "billingAddressGUID"], title: this.dataset.billingAddressTitleAdd, }, "basic-card-page": { @@ -261,9 +247,7 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe break; } case this.saveButton: { - if (this.form.checkValidity()) { - this.saveRecord(); - } + this.saveRecord(); break; } default: { @@ -272,22 +256,11 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe } } - onInput(event) { - this.updateSaveButtonState(); - } - - onInvalid(event) { - this.saveButton.disabled = true; - } - - updateSaveButtonState() { - this.saveButton.disabled = !this.form.checkValidity(); - } - - async saveRecord() { + saveRecord() { let record = this.formHandler.buildFormObject(); let currentState = this.requestStore.getState(); let { + page, tempBasicCards, "basic-card-page": basicCardPage, } = currentState; @@ -307,24 +280,28 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe record["cc-number"] = record["cc-number"] || ""; } - try { - let {guid} = await paymentRequest.updateAutofillRecord("creditCards", record, - basicCardPage.guid); - this.requestStore.setState({ - page: { - id: "payment-summary", - }, - selectedPaymentCard: guid, - }); - } catch (ex) { - log.warn("saveRecord: error:", ex); - this.requestStore.setState({ + let state = { + errorStateChange: { page: { id: "basic-card-page", error: this.dataset.errorGenericSave, }, - }); + }, + preserveOldProperties: true, + selectedStateKey: ["selectedPaymentCard"], + successStateChange: { + page: { + id: "payment-summary", + }, + }, + }; + + const previousId = page.previousId; + if (previousId) { + state.successStateChange[previousId] = Object.assign({}, currentState[previousId]); } + + paymentRequest.updateAutofillRecord("creditCards", record, basicCardPage.guid, state); } } diff --git a/browser/components/payments/res/containers/payment-dialog.js b/browser/components/payments/res/containers/payment-dialog.js index 35a367689b9e..07d0e4324da0 100644 --- a/browser/components/payments/res/containers/payment-dialog.js +++ b/browser/components/payments/res/containers/payment-dialog.js @@ -53,7 +53,7 @@ export default class PaymentDialog extends PaymentStateSubscriberMixin(HTMLEleme this._header = contents.querySelector("header"); - this._errorText = contents.querySelector("header > .page-error"); + this._errorText = contents.querySelector("#error-text"); this._disabledOverlay = contents.getElementById("disabled-overlay"); diff --git a/browser/components/payments/res/debugging.html b/browser/components/payments/res/debugging.html index 3b33b97b9765..345214a4e554 100644 --- a/browser/components/payments/res/debugging.html +++ b/browser/components/payments/res/debugging.html @@ -59,7 +59,6 @@
User Data Errors -
diff --git a/browser/components/payments/res/debugging.js b/browser/components/payments/res/debugging.js index ec1affded8c6..071f757469fd 100644 --- a/browser/components/payments/res/debugging.js +++ b/browser/components/payments/res/debugging.js @@ -333,11 +333,6 @@ let buttonActions = { requestStore.setState({}); }, - saveVisibleForm() { - // Bypasses field validation which is useful to test error handling. - paymentDialog.querySelector("#main-container > .page:not([hidden])").saveRecord(); - }, - setAddresses1() { paymentDialog.setStateFromParent({savedAddresses: ADDRESSES_1}); }, diff --git a/browser/components/payments/res/mixins/PaymentStateSubscriberMixin.js b/browser/components/payments/res/mixins/PaymentStateSubscriberMixin.js index 4ded6a1c72ff..0f79f69063cf 100644 --- a/browser/components/payments/res/mixins/PaymentStateSubscriberMixin.js +++ b/browser/components/payments/res/mixins/PaymentStateSubscriberMixin.js @@ -20,7 +20,6 @@ export let requestStore = new PaymentsStore({ }, "address-page": { guid: null, - selectedStateKey: null, title: "", }, "payment-summary": { @@ -30,6 +29,7 @@ export let requestStore = new PaymentsStore({ previousId: null, // onboardingWizard: true, // error: "", + // selectedStateKey: "", }, request: { completeStatus: "", diff --git a/browser/components/payments/res/paymentRequest.css b/browser/components/payments/res/paymentRequest.css index ebdad85aa7bf..6b398a11375e 100644 --- a/browser/components/payments/res/paymentRequest.css +++ b/browser/components/payments/res/paymentRequest.css @@ -45,25 +45,11 @@ payment-dialog > header, payment-dialog > header { border-bottom: 1px solid rgba(0,0,0,0.1); display: flex; - /* Wrap so that the error text appears full-width above the rest of the contents */ - flex-wrap: wrap; /* from visual spec: */ padding-bottom: 19px; padding-top: 19px; } -payment-dialog > header > .page-error:empty { - display: none; -} - -payment-dialog > header > .page-error { - background: #D70022; - border-radius: 3px; - color: white; - padding: 6px; - width: 100%; -} - #main-container { display: flex; grid-area: main; @@ -92,10 +78,6 @@ payment-dialog > header > .page-error { display: none; } -.page-error { - color: #D70022; -} - .page > footer { align-items: center; background-color: #eaeaee; @@ -105,6 +87,10 @@ payment-dialog > header > .page-error { padding-bottom: 18px; } +#error-text { + text-align: center; +} + #order-details-overlay { background-color: var(--in-content-page-background); overflow: auto; diff --git a/browser/components/payments/res/paymentRequest.js b/browser/components/payments/res/paymentRequest.js index 75f60a89f79b..9ce42d8be163 100644 --- a/browser/components/payments/res/paymentRequest.js +++ b/browser/components/payments/res/paymentRequest.js @@ -11,7 +11,6 @@ /* import-globals-from unprivileged-fallbacks.js */ var paymentRequest = { - _nextMessageID: 1, domReadyPromise: null, init() { @@ -55,23 +54,15 @@ var paymentRequest = { } }, - /** - * @param {string} messageType - * @param {[object]} detail - * @returns {number} message ID to be able to identify a reply (where applicable). - */ sendMessageToChrome(messageType, detail = {}) { - let messageID = this._nextMessageID++; - log.debug("sendMessageToChrome:", messageType, messageID, detail); + log.debug("sendMessageToChrome:", messageType, detail); let event = new CustomEvent("paymentContentToChrome", { bubbles: true, detail: Object.assign({ messageType, - messageID, }, detail), }); document.dispatchEvent(event); - return messageID; }, toggleDebuggingConsole() { @@ -152,14 +143,14 @@ var paymentRequest = { if (shippingRequested) { Object.assign(state["address-page"], { - selectedStateKey: ["selectedShippingAddress"], title: paymentDialog.dataset.shippingAddressTitleAdd, }); + state.page.selectedStateKey = ["selectedShippingAddress"]; } else { Object.assign(state["address-page"], { - selectedStateKey: ["basic-card-page", "billingAddressGUID"], title: paymentDialog.dataset.billingAddressTitleAdd, }); + state.page.selectedStateKey = ["basic-card-page", "billingAddressGUID"]; } } else if (!hasSavedCards) { state.page = { @@ -198,30 +189,21 @@ var paymentRequest = { * @param {string} collectionName The autofill collection that record belongs to. * @param {object} record The autofill record to add/update * @param {string} [guid] The guid of the autofill record to update - * @returns {Promise} when the update response is received */ - updateAutofillRecord(collectionName, record, guid) { - return new Promise((resolve, reject) => { - let messageID = this.sendMessageToChrome("updateAutofillRecord", { - collectionName, - guid, - record, - }); - - window.addEventListener("paymentChromeToContent", function onMsg({detail}) { - if (detail.messageType != "updateAutofillRecord:Response" - || detail.messageID != messageID) { - return; - } - log.debug("updateAutofillRecord: response:", detail); - window.removeEventListener("paymentChromeToContent", onMsg); - document.querySelector("payment-dialog").setStateFromParent(detail.stateChange); - if (detail.error) { - reject(detail); - } else { - resolve(detail); - } - }); + updateAutofillRecord(collectionName, record, guid, { + errorStateChange, + preserveOldProperties, + selectedStateKey, + successStateChange, + }) { + this.sendMessageToChrome("updateAutofillRecord", { + collectionName, + guid, + record, + errorStateChange, + preserveOldProperties, + selectedStateKey, + successStateChange, }); }, diff --git a/browser/components/payments/res/paymentRequest.xhtml b/browser/components/payments/res/paymentRequest.xhtml index 178cd5c41dc5..e32fdaa2ed2d 100644 --- a/browser/components/payments/res/paymentRequest.xhtml +++ b/browser/components/payments/res/paymentRequest.xhtml @@ -94,7 +94,6 @@