Bug 1667257 - Detect credit card type by examining IIN part of credit card number r=sgalich

Differential Revision: https://phabricator.services.mozilla.com/D164904
This commit is contained in:
Dimi
2023-01-03 08:11:54 +00:00
parent 72828a5ada
commit 20d1108620
17 changed files with 193 additions and 190 deletions

View File

@@ -514,7 +514,6 @@ class EditCreditCard extends EditAutofillForm {
), ),
month: this._elements.form.querySelector("#cc-exp-month"), month: this._elements.form.querySelector("#cc-exp-month"),
year: this._elements.form.querySelector("#cc-exp-year"), year: this._elements.form.querySelector("#cc-exp-year"),
ccType: this._elements.form.querySelector("#cc-type"),
billingAddress: this._elements.form.querySelector("#billingAddressGUID"), billingAddress: this._elements.form.querySelector("#billingAddressGUID"),
billingAddressRow: this._elements.form.querySelector( billingAddressRow: this._elements.form.querySelector(
".billingAddressRow" ".billingAddressRow"
@@ -531,8 +530,6 @@ class EditCreditCard extends EditAutofillForm {
this._addresses = addresses; this._addresses = addresses;
this.generateBillingAddressOptions(preserveFieldValues); this.generateBillingAddressOptions(preserveFieldValues);
if (!preserveFieldValues) { if (!preserveFieldValues) {
// Re-populating the networks will reset the selected option.
this.populateNetworks();
// Re-generating the months will reset the selected option. // Re-generating the months will reset the selected option.
this.generateMonths(); this.generateMonths();
// Re-generating the years will reset the selected option. // Re-generating the years will reset the selected option.
@@ -591,23 +588,6 @@ class EditCreditCard extends EditAutofillForm {
} }
} }
populateNetworks() {
// Clear the list
this._elements.ccType.textContent = "";
let frag = document.createDocumentFragment();
// include an empty first option
frag.appendChild(new Option("", ""));
let supportedNetworks = FormAutofillUtils.getCreditCardNetworks();
for (let id of supportedNetworks) {
const option = new Option(undefined, id);
// autofill-card-network-amex, ..., autofill-card-network-visa
option.dataset.l10nId = `autofill-card-network-${id}`;
frag.appendChild(option);
}
this._elements.ccType.appendChild(frag);
}
generateBillingAddressOptions(preserveFieldValues) { generateBillingAddressOptions(preserveFieldValues) {
let billingAddressGUID; let billingAddressGUID;
if (preserveFieldValues && this._elements.billingAddress.value) { if (preserveFieldValues && this._elements.billingAddress.value) {

View File

@@ -44,11 +44,6 @@
<input id="cc-name" type="text" required="required"/> <input id="cc-name" type="text" required="required"/>
<span data-l10n-id="autofill-card-name-on-card" class="label-text"/> <span data-l10n-id="autofill-card-name-on-card" class="label-text"/>
</label> </label>
<label id="cc-type-container" class="container">
<select id="cc-type" required="required">
</select>
<span data-l10n-id="autofill-card-network" class="label-text"/>
</label>
<label id="cc-csc-container" class="container" hidden="hidden"> <label id="cc-csc-container" class="container" hidden="hidden">
<!-- The CSC container will get filled in by forms that need a CSC (using csc-input.js) --> <!-- The CSC container will get filled in by forms that need a CSC (using csc-input.js) -->
</label> </label>

View File

@@ -6,7 +6,7 @@
display: grid; display: grid;
grid-template-areas: grid-template-areas:
"cc-number cc-exp-month cc-exp-year" "cc-number cc-exp-month cc-exp-year"
"cc-name cc-type cc-csc" "cc-name cc-csc ."
"billingAddressGUID billingAddressGUID billingAddressGUID"; "billingAddressGUID billingAddressGUID billingAddressGUID";
grid-template-columns: 4fr 2fr 2fr; grid-template-columns: 4fr 2fr 2fr;
grid-row-gap: var(--grid-column-row-gap); grid-row-gap: var(--grid-column-row-gap);
@@ -40,10 +40,6 @@
grid-area: cc-name; grid-area: cc-name;
} }
#cc-type-container {
grid-area: cc-type;
}
#cc-csc-container { #cc-csc-container {
grid-area: cc-csc; grid-area: cc-csc;
} }

View File

@@ -21,6 +21,8 @@ skip-if = ((os == "mac") || (os == 'linux') || (os == 'win'))
skip-if = ((!debug && os == "mac") || (os == 'linux') || (os == 'win')) skip-if = ((!debug && os == "mac") || (os == 'linux') || (os == 'win'))
[browser_creditCard_heuristics.js] [browser_creditCard_heuristics.js]
skip-if = apple_silicon && !debug # Bug 1714221 skip-if = apple_silicon && !debug # Bug 1714221
[browser_creditCard_submission_autodetect_type.js]
skip-if = apple_silicon && !debug
[browser_creditCard_submission_normalized.js] [browser_creditCard_submission_normalized.js]
skip-if = apple_silicon && !debug skip-if = apple_silicon && !debug
[browser_editCreditCardDialog.js] [browser_editCreditCardDialog.js]

View File

@@ -57,7 +57,7 @@ add_task(async function test_submit_creditCard_saved() {
focusSelector: "#cc-name", focusSelector: "#cc-name",
newValues: { newValues: {
"#cc-name": "User 1", "#cc-name": "User 1",
"#cc-number": "5038146897157463", "#cc-number": "5577000055770004",
"#cc-exp-month": "12", "#cc-exp-month": "12",
"#cc-exp-year": "2017", "#cc-exp-year": "2017",
"#cc-type": "mastercard", "#cc-type": "mastercard",
@@ -918,7 +918,6 @@ add_task(async function test_submit_third_party_creditCard_logo() {
add_task(async function test_update_third_party_creditCard_logo() { add_task(async function test_update_third_party_creditCard_logo() {
const amexCard = { const amexCard = {
"cc-number": "374542158116607", "cc-number": "374542158116607",
"cc-type": "amex",
"cc-name": "John Doe", "cc-name": "John Doe",
}; };
@@ -1070,7 +1069,6 @@ add_task(async function test_save_panel_spaces_in_cc_number_logo() {
add_task(async function test_update_panel_with_spaces_in_cc_number_logo() { add_task(async function test_update_panel_with_spaces_in_cc_number_logo() {
const amexCard = { const amexCard = {
"cc-number": "374 54215 8116607", "cc-number": "374 54215 8116607",
"cc-type": "amex",
"cc-name": "John Doe", "cc-name": "John Doe",
}; };

View File

@@ -0,0 +1,110 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
add_task(async function test_autodetect_credit_not_set() {
await SpecialPowers.pushPrefEnv({
set: [[CREDITCARDS_USED_STATUS_PREF, 0]],
});
const testCard = {
"cc-name": "John Doe",
"cc-number": "4012888888881881",
"cc-exp-month": "06",
"cc-exp-year": "2044",
};
const expectedData = {
...testCard,
...{ "cc-type": "visa" },
};
let onChanged = waitForStorageChangedEvents("add");
await BrowserTestUtils.withNewTab(
{ gBrowser, url: CREDITCARD_FORM_URL },
async function(browser) {
let promiseShown = waitForPopupShown();
await focusUpdateSubmitForm(browser, {
focusSelector: "#cc-name",
newValues: {
"#cc-name": testCard["cc-name"],
"#cc-number": testCard["cc-number"],
"#cc-exp-month": testCard["cc-exp-month"],
"#cc-exp-year": testCard["cc-exp-year"],
"#cc-type": testCard["cc-type"],
},
});
await promiseShown;
await clickDoorhangerButton(MAIN_BUTTON);
}
);
await onChanged;
let creditCards = await getCreditCards();
let savedCreditCard = creditCards[0];
let decryptedNumber = await OSKeyStore.decrypt(
savedCreditCard["cc-number-encrypted"]
);
savedCreditCard["cc-number"] = decryptedNumber;
for (let key in testCard) {
let expected = expectedData[key];
let actual = savedCreditCard[key];
Assert.equal(expected, actual, `${key} should match`);
}
await removeAllRecords();
});
add_task(async function test_autodetect_credit_overwrite() {
await SpecialPowers.pushPrefEnv({
set: [[CREDITCARDS_USED_STATUS_PREF, 0]],
});
const testCard = {
"cc-name": "John Doe",
"cc-number": "4012888888881881",
"cc-exp-month": "06",
"cc-exp-year": "2044",
"cc-type": "master", // Wrong credit card type
};
const expectedData = {
...testCard,
...{ "cc-type": "visa" },
};
let onChanged = waitForStorageChangedEvents("add");
await BrowserTestUtils.withNewTab(
{ gBrowser, url: CREDITCARD_FORM_URL },
async function(browser) {
let promiseShown = waitForPopupShown();
await focusUpdateSubmitForm(browser, {
focusSelector: "#cc-name",
newValues: {
"#cc-name": testCard["cc-name"],
"#cc-number": testCard["cc-number"],
"#cc-exp-month": testCard["cc-exp-month"],
"#cc-exp-year": testCard["cc-exp-year"],
"#cc-type": testCard["cc-type"],
},
});
await promiseShown;
await clickDoorhangerButton(MAIN_BUTTON);
}
);
await onChanged;
let creditCards = await getCreditCards();
let savedCreditCard = creditCards[0];
let decryptedNumber = await OSKeyStore.decrypt(
savedCreditCard["cc-number-encrypted"]
);
savedCreditCard["cc-number"] = decryptedNumber;
for (let key in testCard) {
let expected = expectedData[key];
let actual = savedCreditCard[key];
Assert.equal(expected, actual, `${key} should match`);
}
await removeAllRecords();
});

View File

@@ -663,8 +663,6 @@ add_task(async function test_saveCreditCard() {
EventUtils.synthesizeKey("VK_TAB", {}, win); EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey(TEST_CREDIT_CARD_1["cc-name"], {}, win); EventUtils.synthesizeKey(TEST_CREDIT_CARD_1["cc-name"], {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win); EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey(TEST_CREDIT_CARD_1["cc-type"], {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win); EventUtils.synthesizeKey("VK_TAB", {}, win);
info("saving credit card"); info("saving credit card");
EventUtils.synthesizeKey("VK_RETURN", {}, win); EventUtils.synthesizeKey("VK_RETURN", {}, win);
@@ -693,7 +691,6 @@ add_task(async function test_editCreditCard() {
EventUtils.synthesizeKey("VK_TAB", {}, win); EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win); EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win); EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey("VK_RIGHT", {}, win); EventUtils.synthesizeKey("VK_RIGHT", {}, win);
EventUtils.synthesizeKey("test", {}, win); EventUtils.synthesizeKey("test", {}, win);
win.document.querySelector("#save").click(); win.document.querySelector("#save").click();

View File

@@ -49,8 +49,6 @@ add_task(async function test_saveCreditCard() {
EventUtils.synthesizeKey("VK_TAB", {}, win); EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey(TEST_CREDIT_CARD_1["cc-name"], {}, win); EventUtils.synthesizeKey(TEST_CREDIT_CARD_1["cc-name"], {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win); EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey(TEST_CREDIT_CARD_1["cc-type"], {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win); EventUtils.synthesizeKey("VK_TAB", {}, win);
info("saving credit card"); info("saving credit card");
EventUtils.synthesizeKey("VK_RETURN", {}, win); EventUtils.synthesizeKey("VK_RETURN", {}, win);
@@ -87,9 +85,6 @@ add_task(async function test_saveCreditCardWithMaxYear() {
EventUtils.synthesizeKey("VK_TAB", {}, win); EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey(TEST_CREDIT_CARD_2["cc-name"], {}, win); EventUtils.synthesizeKey(TEST_CREDIT_CARD_2["cc-name"], {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win); EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey(TEST_CREDIT_CARD_1["cc-type"], {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win);
info("saving credit card"); info("saving credit card");
EventUtils.synthesizeKey("VK_RETURN", {}, win); EventUtils.synthesizeKey("VK_RETURN", {}, win);
}); });
@@ -133,8 +128,6 @@ add_task(async function test_saveCreditCardWithBillingAddress() {
EventUtils.synthesizeKey("VK_TAB", {}, win); EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey(TEST_CREDIT_CARD["cc-name"], {}, win); EventUtils.synthesizeKey(TEST_CREDIT_CARD["cc-name"], {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win); EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey(TEST_CREDIT_CARD["cc-type"], {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey(billingAddress["given-name"], {}, win); EventUtils.synthesizeKey(billingAddress["given-name"], {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win); EventUtils.synthesizeKey("VK_TAB", {}, win);
info("saving credit card"); info("saving credit card");
@@ -271,54 +264,6 @@ add_task(async function test_addInvalidCreditCard() {
is(creditCards.length, 0, "Credit card storage is empty"); is(creditCards.length, 0, "Credit card storage is empty");
}); });
add_task(async function test_editCardWithInvalidNetwork() {
const TEST_CREDIT_CARD = Object.assign({}, TEST_CREDIT_CARD_2, {
"cc-type": "asiv",
});
await setStorage(TEST_CREDIT_CARD);
let creditCards = await getCreditCards();
is(creditCards.length, 1, "one credit card in storage");
is(
creditCards[0]["cc-type"],
TEST_CREDIT_CARD["cc-type"],
"Check saved cc-type"
);
await testDialog(
EDIT_CREDIT_CARD_DIALOG_URL,
win => {
EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey("VK_RIGHT", {}, win);
EventUtils.synthesizeKey("test", {}, win);
win.document.querySelector("#save").click();
},
{
record: creditCards[0],
}
);
ok(true, "Edit credit card dialog is closed");
creditCards = await getCreditCards();
is(creditCards.length, 1, "only one credit card is in storage");
is(
creditCards[0]["cc-name"],
TEST_CREDIT_CARD["cc-name"] + "test",
"cc name changed"
);
is(
creditCards[0]["cc-type"],
"visa",
"unknown cc-type removed and next autodetected to visa upon manual save"
);
await removeCreditCards([creditCards[0].guid]);
creditCards = await getCreditCards();
is(creditCards.length, 0, "Credit card storage is empty");
});
add_task(async function test_editInvalidCreditCardNumber() { add_task(async function test_editInvalidCreditCardNumber() {
await setStorage(TEST_ADDRESS_4); await setStorage(TEST_ADDRESS_4);
let addresses = await getAddresses(); let addresses = await getAddresses();

View File

@@ -189,10 +189,7 @@ add_task(async function test_showCreditCardIcons() {
set: [["privacy.reduceTimerPrecision", false]], set: [["privacy.reduceTimerPrecision", false]],
}); });
await setStorage(TEST_CREDIT_CARD_1); await setStorage(TEST_CREDIT_CARD_1);
let unknownCard = Object.assign({}, TEST_CREDIT_CARD_3, { await setStorage(TEST_CREDIT_CARD_3);
"cc-type": "gringotts",
});
await setStorage(unknownCard);
let win = window.openDialog( let win = window.openDialog(
MANAGE_CREDIT_CARDS_DIALOG_URL, MANAGE_CREDIT_CARDS_DIALOG_URL,
@@ -216,7 +213,7 @@ add_task(async function test_showCreditCardIcons() {
is( is(
option0.getAttribute("cc-type"), option0.getAttribute("cc-type"),
"gringotts", "mastercard",
"Option has the expected cc-type" "Option has the expected cc-type"
); );
is( is(

View File

@@ -148,7 +148,6 @@ const TEST_CREDIT_CARD_1 = {
"cc-number": "4111111111111111", "cc-number": "4111111111111111",
"cc-exp-month": 4, "cc-exp-month": 4,
"cc-exp-year": new Date().getFullYear(), "cc-exp-year": new Date().getFullYear(),
"cc-type": "visa",
}; };
const TEST_CREDIT_CARD_2 = { const TEST_CREDIT_CARD_2 = {
@@ -156,25 +155,21 @@ const TEST_CREDIT_CARD_2 = {
"cc-number": "4929001587121045", "cc-number": "4929001587121045",
"cc-exp-month": 12, "cc-exp-month": 12,
"cc-exp-year": new Date().getFullYear() + 10, "cc-exp-year": new Date().getFullYear() + 10,
"cc-type": "visa",
}; };
const TEST_CREDIT_CARD_3 = { const TEST_CREDIT_CARD_3 = {
"cc-number": "5103059495477870", "cc-number": "5103059495477870",
"cc-exp-month": 1, "cc-exp-month": 1,
"cc-exp-year": 2000, "cc-exp-year": 2000,
"cc-type": "mastercard",
}; };
const TEST_CREDIT_CARD_4 = { const TEST_CREDIT_CARD_4 = {
"cc-number": "5105105105105100", "cc-number": "5105105105105100",
"cc-type": "mastercard",
}; };
const TEST_CREDIT_CARD_5 = { const TEST_CREDIT_CARD_5 = {
"cc-name": "Chris P. Bacon", "cc-name": "Chris P. Bacon",
"cc-number": "4012888888881881", "cc-number": "4012888888881881",
"cc-type": "visa",
}; };
const MAIN_BUTTON = "button"; const MAIN_BUTTON = "button";

View File

@@ -20,13 +20,11 @@ const MOCK_STORAGE = [{
"cc-number": "4929001587121045", "cc-number": "4929001587121045",
"cc-exp-month": 4, "cc-exp-month": 4,
"cc-exp-year": 2017, "cc-exp-year": 2017,
"cc-type": "visa",
}, { }, {
"cc-name": "Timothy Berners-Lee", "cc-name": "Timothy Berners-Lee",
"cc-number": "5103059495477870", "cc-number": "5103059495477870",
"cc-exp-month": 12, "cc-exp-month": 12,
"cc-exp-year": 2022, "cc-exp-year": 2022,
"cc-type": "mastercard",
}]; }];
const reducedMockRecord = { const reducedMockRecord = {

View File

@@ -12,10 +12,14 @@ const { CreditCard } = ChromeUtils.importESModule(
); );
let FormAutofillStorage; let FormAutofillStorage;
let CREDIT_CARD_SCHEMA_VERSION;
add_setup(async () => { add_setup(async () => {
({ FormAutofillStorage } = ChromeUtils.import( ({ FormAutofillStorage } = ChromeUtils.import(
"resource://autofill/FormAutofillStorage.jsm" "resource://autofill/FormAutofillStorage.jsm"
)); ));
({ CREDIT_CARD_SCHEMA_VERSION } = ChromeUtils.import(
"resource://autofill/FormAutofillStorageBase.jsm"
));
}); });
const TEST_STORE_FILE_NAME = "test-credit-card.json"; const TEST_STORE_FILE_NAME = "test-credit-card.json";
@@ -26,7 +30,6 @@ const TEST_CREDIT_CARD_1 = {
"cc-number": "4929001587121045", "cc-number": "4929001587121045",
"cc-exp-month": 4, "cc-exp-month": 4,
"cc-exp-year": 2017, "cc-exp-year": 2017,
"cc-type": "visa",
}; };
const TEST_CREDIT_CARD_2 = { const TEST_CREDIT_CARD_2 = {
@@ -34,20 +37,17 @@ const TEST_CREDIT_CARD_2 = {
"cc-number": "5103059495477870", "cc-number": "5103059495477870",
"cc-exp-month": 12, "cc-exp-month": 12,
"cc-exp-year": 2022, "cc-exp-year": 2022,
"cc-type": "mastercard",
}; };
const TEST_CREDIT_CARD_3 = { const TEST_CREDIT_CARD_3 = {
"cc-number": "3589993783099582", "cc-number": "3589993783099582",
"cc-exp-month": 1, "cc-exp-month": 1,
"cc-exp-year": 2000, "cc-exp-year": 2000,
"cc-type": "amex",
}; };
const TEST_CREDIT_CARD_4 = { const TEST_CREDIT_CARD_4 = {
"cc-name": "Foo Bar", "cc-name": "Foo Bar",
"cc-number": "3589993783099582", "cc-number": "3589993783099582",
"cc-type": "amex",
}; };
const TEST_CREDIT_CARD_WITH_BILLING_ADDRESS = { const TEST_CREDIT_CARD_WITH_BILLING_ADDRESS = {
@@ -61,7 +61,6 @@ const TEST_CREDIT_CARD_WITH_EMPTY_FIELD = {
"cc-name": "", "cc-name": "",
"cc-number": "344060747836806", "cc-number": "344060747836806",
"cc-exp-month": 1, "cc-exp-month": 1,
"cc-type": "",
}; };
const TEST_CREDIT_CARD_WITH_EMPTY_COMPUTED_FIELD = { const TEST_CREDIT_CARD_WITH_EMPTY_COMPUTED_FIELD = {
@@ -96,14 +95,6 @@ const TEST_CREDIT_CARD_WITH_SPACES_BETWEEN_DIGITS = {
"cc-number": "5103 0594 9547 7870", "cc-number": "5103 0594 9547 7870",
}; };
const TEST_CREDIT_CARD_WITH_INVALID_NETWORK = {
"cc-name": "John Doe",
"cc-number": "4929001587121045",
"cc-exp-month": 4,
"cc-exp-year": 2017,
"cc-type": "asiv",
};
const TEST_CREDIT_CARD_EMPTY_AFTER_NORMALIZE = { const TEST_CREDIT_CARD_EMPTY_AFTER_NORMALIZE = {
"cc-exp-month": 13, "cc-exp-month": 13,
}; };
@@ -332,7 +323,7 @@ add_task(async function test_add() {
do_check_credit_card_matches(creditCards[1], TEST_CREDIT_CARD_2); do_check_credit_card_matches(creditCards[1], TEST_CREDIT_CARD_2);
Assert.notEqual(creditCards[0].guid, undefined); Assert.notEqual(creditCards[0].guid, undefined);
Assert.equal(creditCards[0].version, 3); Assert.equal(creditCards[0].version, CREDIT_CARD_SCHEMA_VERSION);
Assert.notEqual(creditCards[0].timeCreated, undefined); Assert.notEqual(creditCards[0].timeCreated, undefined);
Assert.equal(creditCards[0].timeLastModified, creditCards[0].timeCreated); Assert.equal(creditCards[0].timeLastModified, creditCards[0].timeCreated);
Assert.equal(creditCards[0].timeLastUsed, 0); Assert.equal(creditCards[0].timeLastUsed, 0);
@@ -543,8 +534,6 @@ add_task(async function test_validate() {
await profileStorage.creditCards.add( await profileStorage.creditCards.add(
TEST_CREDIT_CARD_WITH_SPACES_BETWEEN_DIGITS TEST_CREDIT_CARD_WITH_SPACES_BETWEEN_DIGITS
); );
await profileStorage.creditCards.add(TEST_CREDIT_CARD_WITH_INVALID_NETWORK);
let creditCards = await profileStorage.creditCards.getAll(); let creditCards = await profileStorage.creditCards.getAll();
Assert.equal(creditCards[0]["cc-exp-month"], undefined); Assert.equal(creditCards[0]["cc-exp-month"], undefined);
@@ -562,10 +551,6 @@ add_task(async function test_validate() {
); );
Assert.equal(creditCards[2]["cc-number"].length, 16); Assert.equal(creditCards[2]["cc-number"].length, 16);
// dont enforce validity on the card network when storing a record,
// to avoid data loss when syncing records between different clients with different rules
Assert.equal(creditCards[3]["cc-type"], "asiv");
}); });
add_task(async function test_notifyUsed() { add_task(async function test_notifyUsed() {

View File

@@ -13,8 +13,12 @@ add_setup(async () => {
const TEST_STORE_FILE_NAME = "test-profile.json"; const TEST_STORE_FILE_NAME = "test-profile.json";
const ADDRESS_SCHEMA_VERSION = 1; const { ADDRESS_SCHEMA_VERSION } = ChromeUtils.import(
const CREDIT_CARD_SCHEMA_VERSION = 3; "resource://autofill/FormAutofillStorageBase.jsm"
);
const { CREDIT_CARD_SCHEMA_VERSION } = ChromeUtils.import(
"resource://autofill/FormAutofillStorageBase.jsm"
);
const ADDRESS_TESTCASES = [ const ADDRESS_TESTCASES = [
{ {

View File

@@ -1,7 +1,9 @@
"use strict"; "use strict";
const TEST_STORE_FILE_NAME = "test-profile.json"; const TEST_STORE_FILE_NAME = "test-profile.json";
const CURRENT_CC_VERSION = 3; const { CREDIT_CARD_SCHEMA_VERSION } = ChromeUtils.import(
"resource://autofill/FormAutofillStorageBase.jsm"
);
// NOTE: a guide to reading these test-cases: // NOTE: a guide to reading these test-cases:
// parent: What the local record looked like the last time we wrote the // parent: What the local record looked like the last time we wrote the
@@ -502,7 +504,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
parent: { parent: {
// So when we last wrote the record to the server, it had these values. // So when we last wrote the record to the server, it had these values.
guid: "2bbd2d8fbc6b", guid: "2bbd2d8fbc6b",
version: CURRENT_CC_VERSION, version: CREDIT_CARD_SCHEMA_VERSION,
"cc-name": "John Doe", "cc-name": "John Doe",
"cc-number": "4111111111111111", "cc-number": "4111111111111111",
}, },
@@ -519,7 +521,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
// we can deduce the record hasn't actually been changed remotely so we // we can deduce the record hasn't actually been changed remotely so we
// can safely ignore the incoming record and write our local changes. // can safely ignore the incoming record and write our local changes.
guid: "2bbd2d8fbc6b", guid: "2bbd2d8fbc6b",
version: CURRENT_CC_VERSION, version: CREDIT_CARD_SCHEMA_VERSION,
"cc-name": "John Doe", "cc-name": "John Doe",
"cc-number": "4111111111111111", "cc-number": "4111111111111111",
}, },
@@ -533,7 +535,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
description: "Remote change", description: "Remote change",
parent: { parent: {
guid: "e3680e9f890d", guid: "e3680e9f890d",
version: CURRENT_CC_VERSION, version: CREDIT_CARD_SCHEMA_VERSION,
"cc-name": "John Doe", "cc-name": "John Doe",
"cc-number": "4111111111111111", "cc-number": "4111111111111111",
}, },
@@ -545,7 +547,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
], ],
remote: { remote: {
guid: "e3680e9f890d", guid: "e3680e9f890d",
version: CURRENT_CC_VERSION, version: CREDIT_CARD_SCHEMA_VERSION,
"cc-name": "John Doe", "cc-name": "John Doe",
"cc-number": "4929001587121045", "cc-number": "4929001587121045",
}, },
@@ -560,7 +562,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
description: "New local field", description: "New local field",
parent: { parent: {
guid: "0cba738b1be0", guid: "0cba738b1be0",
version: CURRENT_CC_VERSION, version: CREDIT_CARD_SCHEMA_VERSION,
"cc-name": "John Doe", "cc-name": "John Doe",
"cc-number": "4111111111111111", "cc-number": "4111111111111111",
}, },
@@ -573,7 +575,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
], ],
remote: { remote: {
guid: "0cba738b1be0", guid: "0cba738b1be0",
version: CURRENT_CC_VERSION, version: CREDIT_CARD_SCHEMA_VERSION,
"cc-name": "John Doe", "cc-name": "John Doe",
"cc-number": "4111111111111111", "cc-number": "4111111111111111",
}, },
@@ -588,7 +590,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
description: "New remote field", description: "New remote field",
parent: { parent: {
guid: "be3ef97f8285", guid: "be3ef97f8285",
version: CURRENT_CC_VERSION, version: CREDIT_CARD_SCHEMA_VERSION,
"cc-name": "John Doe", "cc-name": "John Doe",
"cc-number": "4111111111111111", "cc-number": "4111111111111111",
}, },
@@ -600,7 +602,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
], ],
remote: { remote: {
guid: "be3ef97f8285", guid: "be3ef97f8285",
version: CURRENT_CC_VERSION, version: CREDIT_CARD_SCHEMA_VERSION,
"cc-name": "John Doe", "cc-name": "John Doe",
"cc-number": "4111111111111111", "cc-number": "4111111111111111",
"cc-exp-month": 12, "cc-exp-month": 12,
@@ -616,7 +618,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
description: "Deleted field locally", description: "Deleted field locally",
parent: { parent: {
guid: "9627322248ec", guid: "9627322248ec",
version: CURRENT_CC_VERSION, version: CREDIT_CARD_SCHEMA_VERSION,
"cc-name": "John Doe", "cc-name": "John Doe",
"cc-number": "4111111111111111", "cc-number": "4111111111111111",
"cc-exp-month": 12, "cc-exp-month": 12,
@@ -629,7 +631,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
], ],
remote: { remote: {
guid: "9627322248ec", guid: "9627322248ec",
version: CURRENT_CC_VERSION, version: CREDIT_CARD_SCHEMA_VERSION,
"cc-name": "John Doe", "cc-name": "John Doe",
"cc-number": "4111111111111111", "cc-number": "4111111111111111",
"cc-exp-month": 12, "cc-exp-month": 12,
@@ -644,7 +646,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
description: "Deleted field remotely", description: "Deleted field remotely",
parent: { parent: {
guid: "7d7509f3eeb2", guid: "7d7509f3eeb2",
version: CURRENT_CC_VERSION, version: CREDIT_CARD_SCHEMA_VERSION,
"cc-name": "John Doe", "cc-name": "John Doe",
"cc-number": "4111111111111111", "cc-number": "4111111111111111",
"cc-exp-month": 12, "cc-exp-month": 12,
@@ -658,7 +660,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
], ],
remote: { remote: {
guid: "7d7509f3eeb2", guid: "7d7509f3eeb2",
version: CURRENT_CC_VERSION, version: CREDIT_CARD_SCHEMA_VERSION,
"cc-name": "John Doe", "cc-name": "John Doe",
"cc-number": "4111111111111111", "cc-number": "4111111111111111",
}, },
@@ -673,7 +675,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
parent: { parent: {
// The last time we wrote this to the server, "cc-exp-month" was 12. // The last time we wrote this to the server, "cc-exp-month" was 12.
guid: "e087a06dfc57", guid: "e087a06dfc57",
version: CURRENT_CC_VERSION, version: CREDIT_CARD_SCHEMA_VERSION,
"cc-name": "John Doe", "cc-name": "John Doe",
"cc-number": "4111111111111111", "cc-number": "4111111111111111",
"cc-exp-month": 12, "cc-exp-month": 12,
@@ -689,7 +691,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
remote: { remote: {
// Remotely, we've changed "cc-exp-month" to 1. // Remotely, we've changed "cc-exp-month" to 1.
guid: "e087a06dfc57", guid: "e087a06dfc57",
version: CURRENT_CC_VERSION, version: CREDIT_CARD_SCHEMA_VERSION,
"cc-name": "John Doe", "cc-name": "John Doe",
"cc-number": "4111111111111111", "cc-number": "4111111111111111",
"cc-exp-month": 1, "cc-exp-month": 1,
@@ -705,7 +707,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
description: "Multiple local changes", description: "Multiple local changes",
parent: { parent: {
guid: "340a078c596f", guid: "340a078c596f",
version: CURRENT_CC_VERSION, version: CREDIT_CARD_SCHEMA_VERSION,
"cc-name": "John Doe", "cc-name": "John Doe",
"cc-number": "4111111111111111", "cc-number": "4111111111111111",
}, },
@@ -722,7 +724,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
], ],
remote: { remote: {
guid: "340a078c596f", guid: "340a078c596f",
version: CURRENT_CC_VERSION, version: CREDIT_CARD_SCHEMA_VERSION,
"cc-name": "John Doe", "cc-name": "John Doe",
"cc-number": "4111111111111111", "cc-number": "4111111111111111",
"cc-exp-year": 2000, "cc-exp-year": 2000,
@@ -741,7 +743,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
description: "Same change to local and remote", description: "Same change to local and remote",
parent: { parent: {
guid: "0b3a72a1bea2", guid: "0b3a72a1bea2",
version: CURRENT_CC_VERSION, version: CREDIT_CARD_SCHEMA_VERSION,
"cc-name": "John Doe", "cc-name": "John Doe",
"cc-number": "4111111111111111", "cc-number": "4111111111111111",
}, },
@@ -753,7 +755,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
], ],
remote: { remote: {
guid: "0b3a72a1bea2", guid: "0b3a72a1bea2",
version: CURRENT_CC_VERSION, version: CREDIT_CARD_SCHEMA_VERSION,
"cc-name": "John Doe", "cc-name": "John Doe",
"cc-number": "4929001587121045", "cc-number": "4929001587121045",
}, },
@@ -768,7 +770,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
parent: { parent: {
// This is what we last wrote to the sync server. // This is what we last wrote to the sync server.
guid: "62068784d089", guid: "62068784d089",
version: CURRENT_CC_VERSION, version: CREDIT_CARD_SCHEMA_VERSION,
"cc-name": "John Doe", "cc-name": "John Doe",
"cc-number": "4111111111111111", "cc-number": "4111111111111111",
}, },
@@ -782,7 +784,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
remote: { remote: {
// An incoming record has a different cc-number than any of the above! // An incoming record has a different cc-number than any of the above!
guid: "62068784d089", guid: "62068784d089",
version: CURRENT_CC_VERSION, version: CREDIT_CARD_SCHEMA_VERSION,
"cc-name": "John Doe", "cc-name": "John Doe",
"cc-number": "4929001587121045", "cc-number": "4929001587121045",
}, },
@@ -803,7 +805,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
description: "Conflicting changes to multiple fields", description: "Conflicting changes to multiple fields",
parent: { parent: {
guid: "244dbb692e94", guid: "244dbb692e94",
version: CURRENT_CC_VERSION, version: CREDIT_CARD_SCHEMA_VERSION,
"cc-name": "John Doe", "cc-name": "John Doe",
"cc-number": "4111111111111111", "cc-number": "4111111111111111",
"cc-exp-month": 12, "cc-exp-month": 12,
@@ -817,7 +819,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
], ],
remote: { remote: {
guid: "244dbb692e94", guid: "244dbb692e94",
version: CURRENT_CC_VERSION, version: CREDIT_CARD_SCHEMA_VERSION,
"cc-name": "John Doe", "cc-name": "John Doe",
"cc-number": "4929001587121045", "cc-number": "4929001587121045",
"cc-exp-month": 3, "cc-exp-month": 3,
@@ -838,7 +840,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
description: "Field deleted locally, changed remotely", description: "Field deleted locally, changed remotely",
parent: { parent: {
guid: "6fc45e03d19a", guid: "6fc45e03d19a",
version: CURRENT_CC_VERSION, version: CREDIT_CARD_SCHEMA_VERSION,
"cc-name": "John Doe", "cc-name": "John Doe",
"cc-number": "4111111111111111", "cc-number": "4111111111111111",
"cc-exp-month": 12, "cc-exp-month": 12,
@@ -851,7 +853,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
], ],
remote: { remote: {
guid: "6fc45e03d19a", guid: "6fc45e03d19a",
version: CURRENT_CC_VERSION, version: CREDIT_CARD_SCHEMA_VERSION,
"cc-name": "John Doe", "cc-name": "John Doe",
"cc-number": "4111111111111111", "cc-number": "4111111111111111",
"cc-exp-month": 3, "cc-exp-month": 3,
@@ -871,7 +873,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
description: "Field changed locally, deleted remotely", description: "Field changed locally, deleted remotely",
parent: { parent: {
guid: "fff9fa27fa18", guid: "fff9fa27fa18",
version: CURRENT_CC_VERSION, version: CREDIT_CARD_SCHEMA_VERSION,
"cc-name": "John Doe", "cc-name": "John Doe",
"cc-number": "4111111111111111", "cc-number": "4111111111111111",
"cc-exp-month": 12, "cc-exp-month": 12,
@@ -885,7 +887,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
], ],
remote: { remote: {
guid: "fff9fa27fa18", guid: "fff9fa27fa18",
version: CURRENT_CC_VERSION, version: CREDIT_CARD_SCHEMA_VERSION,
"cc-name": "John Doe", "cc-name": "John Doe",
"cc-number": "4111111111111111", "cc-number": "4111111111111111",
}, },
@@ -908,7 +910,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
"Created, last modified time reconciliation without local changes", "Created, last modified time reconciliation without local changes",
parent: { parent: {
guid: "5113f329c42f", guid: "5113f329c42f",
version: CURRENT_CC_VERSION, version: CREDIT_CARD_SCHEMA_VERSION,
"cc-name": "John Doe", "cc-name": "John Doe",
"cc-number": "4111111111111111", "cc-number": "4111111111111111",
timeCreated: 1234, timeCreated: 1234,
@@ -919,7 +921,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
local: [], local: [],
remote: { remote: {
guid: "5113f329c42f", guid: "5113f329c42f",
version: CURRENT_CC_VERSION, version: CREDIT_CARD_SCHEMA_VERSION,
"cc-name": "John Doe", "cc-name": "John Doe",
"cc-number": "4111111111111111", "cc-number": "4111111111111111",
timeCreated: 1200, timeCreated: 1200,
@@ -944,7 +946,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
"Created, last modified time reconciliation with local changes", "Created, last modified time reconciliation with local changes",
parent: { parent: {
guid: "791e5608b80a", guid: "791e5608b80a",
version: CURRENT_CC_VERSION, version: CREDIT_CARD_SCHEMA_VERSION,
"cc-name": "John Doe", "cc-name": "John Doe",
"cc-number": "4111111111111111", "cc-number": "4111111111111111",
timeCreated: 1234, timeCreated: 1234,
@@ -960,7 +962,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
], ],
remote: { remote: {
guid: "791e5608b80a", guid: "791e5608b80a",
version: CURRENT_CC_VERSION, version: CREDIT_CARD_SCHEMA_VERSION,
"cc-name": "John Doe", "cc-name": "John Doe",
"cc-number": "4111111111111111", "cc-number": "4111111111111111",
timeCreated: 1300, timeCreated: 1300,

View File

@@ -44,7 +44,6 @@ const { FormAutofillUtils } = ChromeUtils.import(
const lazy = {}; const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, { ChromeUtils.defineESModuleGetters(lazy, {
CreditCard: "resource://gre/modules/CreditCard.sys.mjs",
OSKeyStore: "resource://gre/modules/OSKeyStore.sys.mjs", OSKeyStore: "resource://gre/modules/OSKeyStore.sys.mjs",
}); });
@@ -615,15 +614,11 @@ class FormAutofillParent extends JSWindowActorParent {
} }
}; };
// Remove invalid cc-type values // Remove cc-type values
if (
creditCard.record["cc-type"] &&
!lazy.CreditCard.isValidNetwork(creditCard.record["cc-type"])
) {
// Let's reset the credit card to empty, and then network auto-detect will // Let's reset the credit card to empty, and then network auto-detect will
// pick it up. // pick it up.
creditCard.record["cc-type"] = ""; //creditCard.record["cc-type"] = "";
} delete creditCard.record["cc-type"];
// If `guid` is present, the form has been autofilled. // If `guid` is present, the form has been autofilled.
if (creditCard.guid) { if (creditCard.guid) {

View File

@@ -67,10 +67,10 @@
* cc-exp-month, * cc-exp-month,
* cc-exp-year, // 2-digit year will be converted to 4 digits * cc-exp-year, // 2-digit year will be converted to 4 digits
* // upon saving * // upon saving
* cc-type, // Optional card network id (instrument type)
* *
* // computed fields (These fields are computed based on the above fields * // computed fields (These fields are computed based on the above fields
* // and are not allowed to be modified directly.) * // and are not allowed to be modified directly.)
* cc-type, // Optional card network id (instrument type)
* cc-given-name, * cc-given-name,
* cc-additional-name, * cc-additional-name,
* cc-family-name, * cc-family-name,
@@ -128,6 +128,8 @@ const EXPORTED_SYMBOLS = [
"FormAutofillStorageBase", "FormAutofillStorageBase",
"CreditCardsBase", "CreditCardsBase",
"AddressesBase", "AddressesBase",
"ADDRESS_SCHEMA_VERSION",
"CREDIT_CARD_SCHEMA_VERSION",
]; ];
const { XPCOMUtils } = ChromeUtils.importESModule( const { XPCOMUtils } = ChromeUtils.importESModule(
@@ -158,7 +160,11 @@ const CryptoHash = Components.Constructor(
const STORAGE_SCHEMA_VERSION = 1; const STORAGE_SCHEMA_VERSION = 1;
const ADDRESS_SCHEMA_VERSION = 1; const ADDRESS_SCHEMA_VERSION = 1;
const CREDIT_CARD_SCHEMA_VERSION = 3;
// Version 2: Bug 1486954 - Encrypt `cc-number`
// Version 3: Bug 1639795 - Update keystore name
// Version 4: Bug 1667257 - Do not store `cc-type` field
const CREDIT_CARD_SCHEMA_VERSION = 4;
const VALID_ADDRESS_FIELDS = [ const VALID_ADDRESS_FIELDS = [
"given-name", "given-name",
@@ -201,10 +207,10 @@ const VALID_CREDIT_CARD_FIELDS = [
"cc-number", "cc-number",
"cc-exp-month", "cc-exp-month",
"cc-exp-year", "cc-exp-year",
"cc-type",
]; ];
const VALID_CREDIT_CARD_COMPUTED_FIELDS = [ const VALID_CREDIT_CARD_COMPUTED_FIELDS = [
"cc-type",
"cc-given-name", "cc-given-name",
"cc-additional-name", "cc-additional-name",
"cc-family-name", "cc-family-name",
@@ -1700,12 +1706,10 @@ class CreditCardsBase extends AutofillRecords {
return hasNewComputedFields; return hasNewComputedFields;
} }
if ("cc-number" in creditCard && !("cc-type" in creditCard)) {
let type = lazy.CreditCard.getType(creditCard["cc-number"]); let type = lazy.CreditCard.getType(creditCard["cc-number"]);
if (type) { if (type) {
creditCard["cc-type"] = type; creditCard["cc-type"] = type;
} }
}
// Compute split names // Compute split names
if (!("cc-given-name" in creditCard)) { if (!("cc-given-name" in creditCard)) {
@@ -1742,16 +1746,11 @@ class CreditCardsBase extends AutofillRecords {
} }
async _computeMigratedRecord(creditCard) { async _computeMigratedRecord(creditCard) {
if (creditCard.version <= 2) {
if (creditCard["cc-number-encrypted"]) { if (creditCard["cc-number-encrypted"]) {
switch (creditCard.version) {
case 1:
case 2: {
// We cannot decrypt the data, so silently remove the record for // We cannot decrypt the data, so silently remove the record for
// the user. // the user.
if (creditCard.deleted) { if (!creditCard.deleted) {
break;
}
this.log.warn( this.log.warn(
"Removing version", "Removing version",
creditCard.version, creditCard.version,
@@ -1772,15 +1771,16 @@ class CreditCardsBase extends AutofillRecords {
creditCard._sync = existingSync; creditCard._sync = existingSync;
existingSync.changeCounter++; existingSync.changeCounter++;
} }
break; }
}
} }
default: if (creditCard.version <= 3) {
throw new Error( if (creditCard["cc-type"]) {
"Unknown credit card version to migrate: " + creditCard.version delete creditCard["cc-type"];
);
} }
} }
return super._computeMigratedRecord(creditCard); return super._computeMigratedRecord(creditCard);
} }

View File

@@ -236,6 +236,10 @@ export class CreditCard {
* @returns {string|null} * @returns {string|null}
*/ */
static getType(ccNumber) { static getType(ccNumber) {
if (!ccNumber) {
return null;
}
for (let i = 0; i < CREDIT_CARD_IIN.length; i++) { for (let i = 0; i < CREDIT_CARD_IIN.length; i++) {
const range = CREDIT_CARD_IIN[i]; const range = CREDIT_CARD_IIN[i];
if (typeof range.len == "number") { if (typeof range.len == "number") {