mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-14 05:45:37 +00:00
Bug 1476204 - Handle autofill record update state changes in the unpriv. PR forms. r=jaws
This is easier to understand as we don't have to round-trip the whole success and error states to the privileged wrapper which could potentially lead to stale state changes. This is also much simpler for the basic-card-form as it doesn't need a lot of the complexity of the previous implementation. * Move selectedStateKey from page to address-page since it doesn't apply to basic-card-page MozReview-Commit-ID: B4kiZNWElGI --HG-- extra : rebase_source : bcca13bbdc506961834e2e3cc078dad7d6ee7ca7
This commit is contained in:
parent
97013686ad
commit
b0560084b3
@ -560,69 +560,52 @@ var paymentDialogWrapper = {
|
||||
window.close();
|
||||
},
|
||||
|
||||
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];
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
let isTemporary = record.isTemporary;
|
||||
let collection = isTemporary ? this.temporaryStore[collectionName] :
|
||||
formAutofillStorage[collectionName];
|
||||
|
||||
if (guid) {
|
||||
let preserveOldProperties = true;
|
||||
await collection.update(guid, record, preserveOldProperties);
|
||||
} else {
|
||||
guid = await collection.add(record);
|
||||
responseMessage.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(successStateChange, {
|
||||
Object.assign(responseMessage.stateChange, {
|
||||
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(successStateChange, {
|
||||
Object.assign(responseMessage.stateChange, {
|
||||
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) {
|
||||
this.sendMessageToContent("updateState", errorStateChange);
|
||||
responseMessage.error = true;
|
||||
} finally {
|
||||
this.sendMessageToContent("updateAutofillRecord:Response", responseMessage);
|
||||
}
|
||||
},
|
||||
|
||||
@ -695,12 +678,7 @@ var paymentDialogWrapper = {
|
||||
break;
|
||||
}
|
||||
case "updateAutofillRecord": {
|
||||
this.onUpdateAutofillRecord(data.collectionName, data.record, data.guid, {
|
||||
errorStateChange: data.errorStateChange,
|
||||
preserveOldProperties: data.preserveOldProperties,
|
||||
selectedStateKey: data.selectedStateKey,
|
||||
successStateChange: data.successStateChange,
|
||||
});
|
||||
this.onUpdateAutofillRecord(data.collectionName, data.record, data.guid, data.messageID);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -243,7 +243,7 @@ export default class AddressForm extends PaymentStateSubscriberMixin(PaymentRequ
|
||||
}
|
||||
}
|
||||
|
||||
saveRecord() {
|
||||
async saveRecord() {
|
||||
let record = this.formHandler.buildFormObject();
|
||||
let currentState = this.requestStore.getState();
|
||||
let {
|
||||
@ -258,22 +258,15 @@ export default class AddressForm extends PaymentStateSubscriberMixin(PaymentRequ
|
||||
record.isTemporary = true;
|
||||
}
|
||||
|
||||
let state = {
|
||||
errorStateChange: {
|
||||
page: {
|
||||
id: "address-page",
|
||||
onboardingWizard: page.onboardingWizard,
|
||||
error: this.dataset.errorGenericSave,
|
||||
},
|
||||
"address-page": addressPage,
|
||||
},
|
||||
preserveOldProperties: true,
|
||||
selectedStateKey: page.selectedStateKey,
|
||||
};
|
||||
|
||||
let successStateChange;
|
||||
const previousId = page.previousId;
|
||||
if (page.onboardingWizard && !Object.keys(savedBasicCards).length) {
|
||||
state.successStateChange = {
|
||||
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,
|
||||
},
|
||||
page: {
|
||||
id: "basic-card-page",
|
||||
previousId: "address-page",
|
||||
@ -281,7 +274,7 @@ export default class AddressForm extends PaymentStateSubscriberMixin(PaymentRequ
|
||||
},
|
||||
};
|
||||
} else {
|
||||
state.successStateChange = {
|
||||
successStateChange = {
|
||||
page: {
|
||||
id: previousId || "payment-summary",
|
||||
onboardingWizard: page.onboardingWizard,
|
||||
@ -290,11 +283,40 @@ export default class AddressForm extends PaymentStateSubscriberMixin(PaymentRequ
|
||||
}
|
||||
|
||||
if (previousId) {
|
||||
state.successStateChange[previousId] = Object.assign({}, currentState[previousId]);
|
||||
state.successStateChange[previousId].preserveFieldValues = true;
|
||||
successStateChange[previousId] = Object.assign({}, currentState[previousId]);
|
||||
successStateChange[previousId].preserveFieldValues = true;
|
||||
}
|
||||
|
||||
paymentRequest.updateAutofillRecord("addresses", record, addressPage.guid, state);
|
||||
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,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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],
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -207,10 +207,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": {
|
||||
@ -288,11 +288,10 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
|
||||
this.saveButton.disabled = !this.form.checkValidity();
|
||||
}
|
||||
|
||||
saveRecord() {
|
||||
async saveRecord() {
|
||||
let record = this.formHandler.buildFormObject();
|
||||
let currentState = this.requestStore.getState();
|
||||
let {
|
||||
page,
|
||||
tempBasicCards,
|
||||
"basic-card-page": basicCardPage,
|
||||
} = currentState;
|
||||
@ -312,28 +311,24 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
|
||||
record["cc-number"] = record["cc-number"] || "";
|
||||
}
|
||||
|
||||
let state = {
|
||||
errorStateChange: {
|
||||
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({
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,6 +59,7 @@
|
||||
<section class="group">
|
||||
<fieldset>
|
||||
<legend>User Data Errors</legend>
|
||||
<button id="saveVisibleForm" title="Bypasses field validation">Save Visible Form</button>
|
||||
<button id="setShippingError">Shipping Error</button>
|
||||
<button id="setAddressErrors">Address Errors</button>
|
||||
</fieldset>
|
||||
|
@ -333,6 +333,11 @@ 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});
|
||||
},
|
||||
|
@ -20,6 +20,7 @@ export let requestStore = new PaymentsStore({
|
||||
},
|
||||
"address-page": {
|
||||
guid: null,
|
||||
selectedStateKey: null,
|
||||
title: "",
|
||||
},
|
||||
"payment-summary": {
|
||||
@ -29,7 +30,6 @@ export let requestStore = new PaymentsStore({
|
||||
previousId: null,
|
||||
// onboardingWizard: true,
|
||||
// error: "",
|
||||
// selectedStateKey: "",
|
||||
},
|
||||
request: {
|
||||
completeStatus: "",
|
||||
|
@ -11,6 +11,7 @@
|
||||
/* import-globals-from unprivileged-fallbacks.js */
|
||||
|
||||
var paymentRequest = {
|
||||
_nextMessageID: 1,
|
||||
domReadyPromise: null,
|
||||
|
||||
init() {
|
||||
@ -54,15 +55,23 @@ var paymentRequest = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {string} messageType
|
||||
* @param {[object]} detail
|
||||
* @returns {number} message ID to be able to identify a reply (where applicable).
|
||||
*/
|
||||
sendMessageToChrome(messageType, detail = {}) {
|
||||
log.debug("sendMessageToChrome:", messageType, detail);
|
||||
let messageID = this._nextMessageID++;
|
||||
log.debug("sendMessageToChrome:", messageType, messageID, detail);
|
||||
let event = new CustomEvent("paymentContentToChrome", {
|
||||
bubbles: true,
|
||||
detail: Object.assign({
|
||||
messageType,
|
||||
messageID,
|
||||
}, detail),
|
||||
});
|
||||
document.dispatchEvent(event);
|
||||
return messageID;
|
||||
},
|
||||
|
||||
toggleDebuggingConsole() {
|
||||
@ -143,14 +152,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 = {
|
||||
@ -189,21 +198,30 @@ 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, {
|
||||
errorStateChange,
|
||||
preserveOldProperties,
|
||||
selectedStateKey,
|
||||
successStateChange,
|
||||
}) {
|
||||
this.sendMessageToChrome("updateAutofillRecord", {
|
||||
collectionName,
|
||||
guid,
|
||||
record,
|
||||
errorStateChange,
|
||||
preserveOldProperties,
|
||||
selectedStateKey,
|
||||
successStateChange,
|
||||
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);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -37,7 +37,7 @@ add_task(async function test_onboarding_wizard_without_saved_addresses_and_saved
|
||||
|
||||
await PTU.DialogContentUtils.waitForState(content, (state) => {
|
||||
return state.page.id == "address-page" &&
|
||||
state.page.selectedStateKey[0] == "selectedShippingAddress";
|
||||
state["address-page"].selectedStateKey[0] == "selectedShippingAddress";
|
||||
}, "Address page is shown first during on-boarding if there are no saved addresses");
|
||||
|
||||
info("Checking if the address page has been rendered");
|
||||
@ -319,8 +319,8 @@ add_task(async function test_onboarding_wizard_with_requestShipping_turned_off()
|
||||
|
||||
await PTU.DialogContentUtils.waitForState(content, (state) => {
|
||||
return state.page.id == "address-page" &&
|
||||
state.page.selectedStateKey[0] == "basic-card-page" &&
|
||||
state.page.selectedStateKey[1] == "billingAddressGUID";
|
||||
state["address-page"].selectedStateKey[0] == "basic-card-page" &&
|
||||
state["address-page"].selectedStateKey[1] == "billingAddressGUID";
|
||||
// eslint-disable-next-line max-len
|
||||
}, "Billing address page is shown first during on-boarding if requestShipping is turned off");
|
||||
|
||||
@ -438,8 +438,8 @@ add_task(async function test_back_button_on_basic_card_page_during_onboarding()
|
||||
|
||||
await PTU.DialogContentUtils.waitForState(content, (state) => {
|
||||
return state.page.id == "address-page";
|
||||
}, "Address page is shown first if there are saved addresses during on boarding");
|
||||
|
||||
}, "Billing address page is shown first if there are no saved addresses " +
|
||||
"and requestShipping is false during on boarding");
|
||||
info("Checking if the address page has been rendered");
|
||||
let addressSaveButton = content.document.querySelector("address-form .save-button");
|
||||
ok(content.isVisible(addressSaveButton), "Address save button is rendered");
|
||||
|
@ -140,22 +140,13 @@ add_task(async function test_saveButton() {
|
||||
synthesizeMouseAtCenter(form.saveButton, {});
|
||||
|
||||
let details = await messagePromise;
|
||||
ok(typeof(details.messageID) == "number" && details.messageID > 0, "Check messageID type");
|
||||
delete details.messageID;
|
||||
is(details.collectionName, "addresses", "Check collectionName");
|
||||
isDeeply(details, {
|
||||
collectionName: "addresses",
|
||||
errorStateChange: {
|
||||
page: {
|
||||
id: "address-page",
|
||||
error: "Generic error",
|
||||
onboardingWizard: undefined,
|
||||
},
|
||||
"address-page": {
|
||||
title: "Sample page title",
|
||||
},
|
||||
},
|
||||
guid: undefined,
|
||||
messageType: "updateAutofillRecord",
|
||||
preserveOldProperties: true,
|
||||
record: {
|
||||
"given-name": "Jaws",
|
||||
"family-name": "Swaj",
|
||||
@ -169,13 +160,6 @@ add_task(async function test_saveButton() {
|
||||
"email": "test@example.com",
|
||||
"tel": "+15555551212",
|
||||
},
|
||||
selectedStateKey: undefined,
|
||||
successStateChange: {
|
||||
page: {
|
||||
id: "payment-summary",
|
||||
onboardingWizard: undefined,
|
||||
},
|
||||
},
|
||||
}, "Check event details for the message to chrome");
|
||||
form.remove();
|
||||
});
|
||||
|
@ -117,18 +117,13 @@ add_task(async function test_saveButton() {
|
||||
synthesizeMouseAtCenter(form.saveButton, {});
|
||||
|
||||
let details = await messagePromise;
|
||||
ok(typeof(details.messageID) == "number" && details.messageID > 0, "Check messageID type");
|
||||
delete details.messageID;
|
||||
is(details.collectionName, "creditCards", "Check collectionName");
|
||||
isDeeply(details, {
|
||||
collectionName: "creditCards",
|
||||
errorStateChange: {
|
||||
page: {
|
||||
id: "basic-card-page",
|
||||
error: "Generic error",
|
||||
},
|
||||
},
|
||||
guid: undefined,
|
||||
messageType: "updateAutofillRecord",
|
||||
preserveOldProperties: true,
|
||||
record: {
|
||||
"cc-exp-month": "11",
|
||||
"cc-exp-year": year,
|
||||
@ -136,12 +131,6 @@ add_task(async function test_saveButton() {
|
||||
"cc-number": "4111 1111-1111 1111",
|
||||
"billingAddressGUID": "",
|
||||
},
|
||||
selectedStateKey: ["selectedPaymentCard"],
|
||||
successStateChange: {
|
||||
page: {
|
||||
id: "payment-summary",
|
||||
},
|
||||
},
|
||||
}, "Check event details for the message to chrome");
|
||||
form.remove();
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user