Bug 1393745 - [Form Autofill] Wrap up masterpassword decrypt API in FormAutofillHandler. r=lchang
MozReview-Commit-ID: LZdwhmmJcFD
--- a/browser/extensions/formautofill/FormAutofillHandler.jsm
+++ b/browser/extensions/formautofill/FormAutofillHandler.jsm
@@ -7,24 +7,23 @@
*/
"use strict";
this.EXPORTED_SYMBOLS = ["FormAutofillHandler"];
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://formautofill/FormAutofillUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FormAutofillHeuristics",
"resource://formautofill/FormAutofillHeuristics.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "MasterPassword",
- "resource://formautofill/MasterPassword.jsm");
this.log = null;
FormAutofillUtils.defineLazyLogGetter(this, this.EXPORTED_SYMBOLS[0]);
/**
* Handles profile autofill for a DOM Form element.
* @param {FormLike} form Form that need to be auto filled
*/
@@ -279,25 +278,24 @@ FormAutofillHandler.prototype = {
);
let targetSet;
if (FormAutofillUtils.isCreditCardField(focusedDetail.fieldName)) {
// When Master Password is enabled by users, the decryption process
// should prompt Master Password dialog to get the decrypted credit
// card number. Otherwise, the number can be decrypted with the default
// password.
if (profile["cc-number-encrypted"]) {
- try {
- profile["cc-number"] = await MasterPassword.decrypt(profile["cc-number-encrypted"], true);
- } catch (e) {
- if (e.result == Cr.NS_ERROR_ABORT) {
- log.warn("User canceled master password entry");
- return;
- }
- throw e;
+ let decrypted = await this._decrypt(profile["cc-number-encrypted"], true);
+
+ if (!decrypted) {
+ // Early return if the decrypted is empty or undefined
+ return;
}
+
+ profile["cc-number"] = decrypted;
}
targetSet = this.creditCard;
} else if (FormAutofillUtils.isAddressField(focusedDetail.fieldName)) {
targetSet = this.address;
} else {
throw new Error("Unknown form fields");
}
@@ -567,9 +565,20 @@ FormAutofillHandler.prototype = {
if (data.creditCard && !data.creditCard.record["cc-number"]) {
log.debug("No credit card record saving since card number is empty");
delete data.creditCard;
}
return data;
},
+
+ async _decrypt(cipherText, reauth) {
+ return new Promise((resolve) => {
+ Services.cpmm.addMessageListener("FormAutofill:DecryptedString", function getResult(result) {
+ Services.cpmm.removeMessageListener("FormAutofill:DecryptedString", getResult);
+ resolve(result.data);
+ });
+
+ Services.cpmm.sendAsyncMessage("FormAutofill:GetDecryptedString", {cipherText, reauth});
+ });
+ },
};
--- a/browser/extensions/formautofill/FormAutofillParent.jsm
+++ b/browser/extensions/formautofill/FormAutofillParent.jsm
@@ -87,16 +87,17 @@ FormAutofillParent.prototype = {
Services.obs.addObserver(this, "advanced-pane-loaded");
Services.ppmm.addMessageListener("FormAutofill:InitStorage", this);
Services.ppmm.addMessageListener("FormAutofill:GetRecords", this);
Services.ppmm.addMessageListener("FormAutofill:SaveAddress", this);
Services.ppmm.addMessageListener("FormAutofill:SaveCreditCard", this);
Services.ppmm.addMessageListener("FormAutofill:RemoveAddresses", this);
Services.ppmm.addMessageListener("FormAutofill:RemoveCreditCards", this);
Services.ppmm.addMessageListener("FormAutofill:OpenPreferences", this);
+ Services.ppmm.addMessageListener("FormAutofill:GetDecryptedString", this);
Services.mm.addMessageListener("FormAutofill:OnFormSubmit", this);
// Observing the pref and storage changes
Services.prefs.addObserver(ENABLED_AUTOFILL_ADDRESSES_PREF, this);
Services.prefs.addObserver(ENABLED_AUTOFILL_CREDITCARDS_PREF, this);
Services.obs.addObserver(this, "formautofill-storage-changed");
},
@@ -218,16 +219,31 @@ FormAutofillParent.prototype = {
}
case "FormAutofill:OnFormSubmit": {
this._onFormSubmit(data, target);
break;
}
case "FormAutofill:OpenPreferences": {
const win = RecentWindow.getMostRecentBrowserWindow();
win.openPreferences("panePrivacy", {origin: "autofillFooter"});
+ break;
+ }
+ case "FormAutofill:GetDecryptedString": {
+ let {cipherText, reauth} = data;
+ let string;
+ try {
+ string = await MasterPassword.decrypt(cipherText, reauth);
+ } catch (e) {
+ if (e.result != Cr.NS_ERROR_ABORT) {
+ throw e;
+ }
+ log.warn("User canceled master password entry");
+ }
+ target.sendAsyncMessage("FormAutofill:DecryptedString", string);
+ break;
}
}
},
/**
* Uninitializes FormAutofillParent. This is for testing only.
*
* @private
--- a/browser/extensions/formautofill/test/unit/test_autofillFormFields.js
+++ b/browser/extensions/formautofill/test/unit/test_autofillFormFields.js
@@ -498,16 +498,29 @@ function do_test(testcases, testFn) {
}
let doc = MockDocument.createTestDocument("http://localhost:8080/test/",
testcase.document);
let form = doc.querySelector("form");
let formLike = FormLikeFactory.createFromForm(form);
let handler = new FormAutofillHandler(formLike);
let promises = [];
+ // Replace the interal decrypt method with MasterPassword API
+ handler._decrypt = async (cipherText, reauth) => {
+ let string;
+ try {
+ string = await MasterPassword.decrypt(cipherText, reauth);
+ } catch (e) {
+ if (e.result != Cr.NS_ERROR_ABORT) {
+ throw e;
+ }
+ do_print("User canceled master password entry");
+ }
+ return string;
+ };
handler.collectFormFields();
let handlerInfo = handler[testcase.expectedFillingForm];
handlerInfo.fieldDetails.forEach(field => {
let element = field.elementWeakRef.get();
if (!testcase.profileData[field.fieldName]) {
// Avoid waiting for `change` event of a input with a blank value to
// be filled.