Bug 1740001 - Add cc exp year transformer, send input and change events when resetting select element via clear form. r=dimi,sgalich

Differential Revision: https://phabricator.services.mozilla.com/D139026
This commit is contained in:
Tim Giles 2022-03-16 14:01:57 +00:00
Родитель 6ae637e5d7
Коммит 990afe241f
13 изменённых файлов: 723 добавлений и 28 удалений

Просмотреть файл

@ -590,7 +590,7 @@ add_task(function setup() {
OSKeyStoreTestUtils.setup();
});
registerCleanupFunction(removeAllRecords);
registerCleanupFunction(async () => {
await removeAllRecords();
await OSKeyStoreTestUtils.cleanup();
});

Просмотреть файл

@ -0,0 +1,90 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Direct.asda.com</title>
</head>
<body>
<form class="card-form">
<div class="form-group"><label class="label" for="cardNumber">Card number</label>
<div>
<div class="input-group"><input class="form-control error" name="cardNumber" type="tel" autocomplete="on"
data-id="input-payment-add-card-number" value=""></div><span data-id="input-payment-add-card-number"
class="error">Please enter a card number</span>
</div>
</div>
<div class="form-group"><label class="label" for="cardHolder">Name on card</label>
<div>
<div class="input-group"><input class="form-control" name="cardHolder" type="text" autocomplete="on"
data-id="input-payment-add-card-name-on-card" value=""></div>
</div>
</div>
<div class="grid__list card-grid">
<div class="form-group cvv-form-group"><label class="label" for="cvv">CVV number</label>
<div>
<div class="input-group flex-grid flex-grid--center"><input class="form-control" name="cvv" type="tel"
autocomplete="on" data-id="input-payment-add-card-cvv" value=""><span class="icon cvv-code-icon"><svg
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="36" height="24">
<defs>
<rect id="cvv-dep-a" width="36" height="24" rx="2.182"></rect>
</defs>
<g fill="none" fill-rule="evenodd">
<rect width="36" height="24" fill="#D8D8D8" fill-rule="nonzero" rx="2.182"></rect>
<mask id="cvv-dep-b" fill="#fff">
<use xlink:href="#cvv-dep-a"></use>
</mask>
<path fill="#191919" fill-rule="nonzero" d="M0 0h36v5.526H0z" mask="url(#cvv-dep-b)"></path>
<rect width="6.545" height="4.435" x="26.727" y="9.282" stroke="#D8365A" stroke-width="1.091"
rx=".545"></rect>
<path fill="#FFF" fill-rule="nonzero" d="M2.182 8.737H24v5.526H2.182z"></path>
</g>
</svg></span><span class="icon cvv-code-helper"><svg xmlns="http://www.w3.org/2000/svg" width="24"
height="24">
<g fill="none" fill-rule="evenodd">
<circle fill="#191919" cx="12" cy="12" r="12"></circle><text font-family="Arial-BoldMT, Arial"
font-size="15" font-weight="bold" fill="#FFF">
<tspan x="7" y="17.235">?</tspan>
</text>
</g>
</svg></span></div>
</div>
</div>
<div class="grid__list adjust-date"><label class="label">Expiry date</label>
<div>
<div class="flex-grid--inline">
<div class="flex-grid--inline">
<div class="form-group expiry-month">
<div>
<div class="input-group"><input class="form-control" name="month" type="tel" placeholder="MM"
data-id="input-payment-add-card-expiry-month" value=""></div>
</div>
</div><span class="date-separator">/</span>
<div class="form-group expiry-year">
<div class="input-group">
<div>
<div class="input-group"><input class="form-control" name="year" type="tel" placeholder="YY"
data-id="input-payment-add-card-expiry-year" value=""></div>
</div>
</div>
</div>
</div>
<div></div>
</div>
</div>
</div>
<div class="save-card-text">
<div>
<div class="checkbox-section size-normal"><input class="checkbox-field" name="save" id="save"
data-id="checkbox-payment-save-card" type="checkbox"><label for="save"
class="custom-checkbox"></label><label for="save" class="checkbox-label">Save card</label></div>
</div>
</div>
</div>
</form>
</body>
</html>

Просмотреть файл

@ -0,0 +1,154 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Global.direct.asda.com</title>
</head>
<body>
<form action="/1/Payments/HandleCreditCardRequestV2?mode=13534" method="post" id="paymentFrm" novalidate="novalidate">
<div class="clearfix" id="secureContainer" data-culture="en-GB" data-direction="ltr">
<div class="form-horizontal">
<div class="form-group has-error has-feedback">
<label for="cardNum" class="col-sm-4 col-xs-12 control-label fcap paylabel">Card number<div data-show="true"
class="glyphicon glyphicon-star astrsk"></div></label>
<div class="col-sm-8 col-xs-12 fval" id="CreditCardCell" data-cc-valid="false">
<input aria-label="Card number" autocomplete="off" class="form-control input-validation-error"
data-type="unknown" data-type-id="1" data-val="true" data-val-luhn="Card number not valid"
data-val-luhn-allowempty="False" data-val-luhn-allowspaces="False" data-val-required="Card number"
id="cardNum" name="PaymentData.cardNum" pattern="[0-9]{13,16}" placeholder="Card number" type="tel"
value="" aria-required="true"><span class="glyphicon glyphicon-remove form-control-feedback"
aria-hidden="true"></span>
<div id="cardTypeInfo">
<div class="isvisa pm_visa pm_general"></div>
<div class="ismastercard pm_mastercard pm_general"></div>
<div class="isamex pm_amex pm_general"></div>
<div class="ismaestro pm_maestro pm_general"></div>
<div class="isjcb pm_jcb pm_general"></div>
<div class="isdiners pm_diners pm_general"></div>
<div class="isdiscover pm_discover pm_general"></div>
<div class="ismir pm_mir pm_general"></div>
<div class="isdefault pm_default pm_general"></div>
</div>
</div>
</div>
<div class="form-group">
<label for="cardExpiryMonth" class="col-sm-4 col-xs-12 control-label fcap paylabel">Expiry date<div
data-show="true" class="glyphicon glyphicon-star astrsk"></div></label>
<div class="col-sm-8 col-xs-12 fval">
<div class="row" id="expDateRow">
<div class="col-xs-6">
<div class="FSelect">
<div class="arrow"></div>
<div class="FCurValue">Month</div><select aria-label="Month" class="form-control"
data-tooltip-special-pos="top left;bottom left" data-val="true"
data-val-datemustbeequalorgreaterthancurrentdate="The credit card expiration date you provided has already expired."
data-val-required="Expiry date" data-widget="lightcombobox" id="cardExpiryMonth"
name="PaymentData.cardExpiryMonth" data-rendered="true" aria-required="true">
<option selected="selected" value="">Month</option>
<option value="1">01</option>
<option value="2">02</option>
<option value="3">03</option>
<option value="4">04</option>
<option value="5">05</option>
<option value="6">06</option>
<option value="7">07</option>
<option value="8">08</option>
<option value="9">09</option>
<option value="10">10</option>
<option value="11">11</option>
<option value="12">12</option>
</select>
</div>
</div>
<div class="col-xs-6">
<div class="FSelect">
<div class="arrow"></div>
<div class="FCurValue">Year</div><select aria-label="Year" class="form-control"
data-tooltip-special-pos="top right;bottom right" data-val="true"
data-val-datemustbeequalorgreaterthancurrentdate="The credit card expiration date you provided has already expired."
data-val-required="Expiry date" data-widget="lightcombobox" id="cardExpiryYear"
name="PaymentData.cardExpiryYear" data-rendered="true" aria-required="true">
<option selected="selected" value="">Year</option>
<option value="2022">2022</option>
<option value="2023">2023</option>
<option value="2024">2024</option>
<option value="2025">2025</option>
<option value="2026">2026</option>
<option value="2027">2027</option>
<option value="2028">2028</option>
<option value="2029">2029</option>
<option value="2030">2030</option>
<option value="2031">2031</option>
<option value="2032">2032</option>
<option value="2033">2033</option>
<option value="2034">2034</option>
<option value="2035">2035</option>
<option value="2036">2036</option>
<option value="2037">2037</option>
<option value="2038">2038</option>
<option value="2039">2039</option>
<option value="2040">2040</option>
<option value="2041">2041</option>
<option value="2042">2042</option>
</select>
</div>
</div>
</div>
</div>
</div>
<div id="cvvContainer" class="form-group">
<label for="cvdNumber" class="col-sm-4 col-xs-12 control-label fcap paylabel">Security code<div
data-show="true" class="glyphicon glyphicon-star astrsk"></div></label>
<div class="col-sm-8 col-xs-12 fval">
<input aria-label="Security code" autocomplete="off" class="form-control" data-tt-pos="top" data-val="true"
data-val-cvvval="Please enter a valid CVV" data-val-cvvval-otherpropertyname=""
data-val-required="Security code" id="cvdNumber" name="PaymentData.cvdNumber" pattern="[0-9]{3,4}"
placeholder="CVV" type="tel" value="" aria-required="true">
<span id="cvvDescriptionContainer" data-toggle="tooltip" data-placement="top" title=""
data-original-title="The&nbsp;Security Code&nbsp;on your credit card or debit card is a 3 digit number on the back of your VISA®, MasterCard® and Discover® branded credit and debit cards. On your American Express® branded credit or debit card it is a 4 digit numeric code on the front of the card.">
<label tabindex="0" class="control-label" id="cvvInfo"> What is this?</label>
<label class="control-label secureinfolabel"><span class="secureInfo lazy"></span></label>
</span>
</div>
</div>
</div>
</div>
<!--#region Hidden Payment Parameters-->
<input type="hidden" name="PaymentData.checkoutV2" value="true">
<input type="hidden" name="PaymentData.cartToken" id="cartToken" value="045078e1-dafa-4d42-8e1a-a8a8dc3c2234">
<input type="hidden" name="PaymentData.gatewayId" id="gatewayId" value="2">
<input type="hidden" name="PaymentData.paymentMethodId" id="paymentMethodId" value="1">
<input type="hidden" name="PaymentData.machineId" id="machineId">
<!--Needs to be updated by script-->
<input type="hidden" name="PaymentData.createTransaction" id="createTransaction" value="true">
<input type="hidden" name="PaymentData.checkoutCDNEnabled" id="checkoutURL" value="value">
<input type="hidden" name="PaymentData.recapchaToken" id="recapchaToken">
<!--Needs to be updated by script-->
<input type="hidden" name="PaymentData.recapchaTime" id="recapchaTime">
<!--Needs to be updated by script-->
<input type="hidden" name="PaymentData.customerScreenColorDepth" id="customerScreenColorDepth">
<!--Needs to be updated by script-->
<input type="hidden" name="PaymentData.customerScreenWidth" id="customerScreenWidth">
<!--Needs to be updated by script-->
<input type="hidden" name="PaymentData.customerScreenHeight" id="customerScreenHeight">
<!--Needs to be updated by script-->
<input type="hidden" name="PaymentData.customerTimeZoneOffset" id="customerTimeZoneOffset">
<!--Needs to be updated by script-->
<input type="hidden" name="PaymentData.customerLanguage" id="customerLanguage">
<!--Needs to be updated by script-->
<input type="hidden" name="PaymentData.UrlStructureTokenEncoded" id="UrlStructureTokenEncoded"
value="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJFbmNvZGVkTWVyY2hhbnRJZCI6IjhybzgiLCJDYXJ0VG9rZW4iOiIwNDUwNzhlMS1kYWZhLTRkNDItOGUxYS1hOGE4ZGMzYzIyMzQiLCJJc1JlcXVpcmVkVG9QYXlXaXRoRGVjb2RlZE1lcmNoYW50SWRBbmRUb2tlbkluVXJsIjpmYWxzZX0.NaxXTwl3w9OurCnPmosjy0P0kSvvs9JfY1OnRVI_w_4">
<!--#endregion-->
</form>
</body>
</html>

Просмотреть файл

@ -16,6 +16,8 @@ skip-if = xorigin
scheme=https
[test_clear_form.html]
scheme=https
[test_clear_form_expiry_select_elements.html]
scheme=https
[test_creditcard_autocomplete_off.html]
scheme=https
[test_preview_highlight_with_multiple_cc_number_fields.html]

Просмотреть файл

@ -0,0 +1,211 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Test form autofill - clear form button with select elements</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/EventUtils.js"></script>
<script type="text/javascript" src="../formautofill_common.js"></script>
<script type="text/javascript" src="../../../../../../toolkit/components/satchel/test/satchel_common.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
Form autofill test: clear form button with select elements.
<script>
"use strict";
const MOCK_ADDR_STORAGE = [{
organization: "Sesame Street",
"street-address": "2 Harrison St\nline2\nline3",
tel: "+13453453456",
}, {
organization: "Mozilla",
"street-address": "331 E. Evelyn Avenue",
}, {
organization: "Tel org",
tel: "+12223334444",
}];
const MOCK_CC_STORAGE = [{
"cc-name": "John Doe",
"cc-number": "4929001587121045",
"cc-exp-month": 4,
"cc-exp-year": 2017,
}, {
"cc-name": "Timothy Berners-Lee",
"cc-number": "5103059495477870",
"cc-exp-month": 12,
"cc-exp-year": 2022,
}];
initPopupListener();
add_task(async function setup_storage() {
await addAddress(MOCK_ADDR_STORAGE[0]);
await addCreditCard(MOCK_CC_STORAGE[0]);
await addCreditCard(MOCK_CC_STORAGE[1]);
});
async function checkIsFormCleared(patch = {}) {
const form = document.getElementById("form1");
for (const elem of form.elements) {
const expectedValue = patch[elem.id] || "";
checkFieldValue(elem, expectedValue);
await checkFieldHighlighted(elem, false);
await checkFieldPreview(elem, "");
}
}
async function confirmClear(selector) {
info("Await for clearing input");
let promise = new Promise(resolve => {
let beforeInputFired = false;
let element = document.querySelector(selector);
info(`Which element are we clearing? ${element.id}`);
element.addEventListener("beforeinput", (event) => {
beforeInputFired = true;
ok(event instanceof InputEvent,
'"beforeinput" event should be dispatched with InputEvent interface');
is(event.cancelable, SpecialPowers.getBoolPref("dom.input_event.allow_to_cancel_set_user_input"),
`"beforeinput" event should be cancelable unless it's disabled by the pref`);
is(event.bubbles, true,
'"beforeinput" event should always bubble');
is(event.inputType, "insertReplacementText",
'inputType value of "beforeinput" should be "insertReplacementText"');
is(event.data, "",
'data value of "beforeinput" should be empty string');
is(event.dataTransfer, null,
'dataTransfer value of "beforeinput" should be null');
is(event.getTargetRanges().length, 0,
'getTargetRanges() of "beforeinput" event should return empty array');
}, {once: true});
element.addEventListener("input", (event) => {
ok(beforeInputFired, `"beforeinput" event should've been fired before "input" on <${element.tagName} type="${element.type}">`);
ok(event instanceof InputEvent,
'"input" event should be dispatched with InputEvent interface');
is(event.cancelable, false,
'"input" event should be never cancelable');
is(event.bubbles, true,
'"input" event should always bubble');
is(event.inputType, "insertReplacementText",
'inputType value of "input" should be "insertReplacementText"');
is(event.data, "",
'data value of "input" should be empty string');
is(event.dataTransfer, null,
'dataTransfer value of "input" should be null');
is(event.getTargetRanges().length, 0,
'getTargetRanges() of "input" should return empty array');
resolve();
}, {once: true})
});
synthesizeKey("KEY_Enter");
await promise;
}
// tgiles: We need this task due to timing issues between focusAndWaitForFieldsIdentified and popupShownListener.
// There's a 300ms delay in focusAndWaitForFieldsIdentified that can cause triggerPopupAndHoverItem to get out of sync
// and cause the popup to appear before the test expects a popup to appear.
// Without this task we end up either getting a consistent timeout or getting the following exception:
// 0:20.55 GECKO(31108) JavaScript error: , line 0: uncaught exception: Checking selected index - timed out after 50 tries.
// This exception appears if you attempt to create the expectPopup promise earlier than it currently is in triggetPopupAndHoverItem
add_task(async function a_dummy_task() {
await triggerPopupAndHoverItem("#organization", 0);
await triggerAutofillAndCheckProfile(MOCK_ADDR_STORAGE[0]);
await triggerPopupAndHoverItem("#tel", 0);
await confirmClear("#tel");
await checkIsFormCleared({
"cc-exp-month": "MM",
"cc-exp-year": "YY"
});
});
add_task(async function clear_distinct_section() {
if (!(await canTestOSKeyStoreLogin())) {
todo(false, "Cannot test OS key store login on official builds.");
return;
}
let osKeyStoreLoginShown = waitForOSKeyStoreLogin(true);
await triggerPopupAndHoverItem("#cc-name", 0);
await triggerAutofillAndCheckProfile(MOCK_CC_STORAGE[0]);
await osKeyStoreLoginShown;
for (const [id, val] of Object.entries(MOCK_CC_STORAGE[0])) {
const element = document.getElementById(id);
if (!element) {
return;
}
checkFieldValue(element, val);
await checkFieldHighlighted(element, true);
}
await triggerPopupAndHoverItem("#cc-name", 0);
await confirmClear("#cc-name");
await checkIsFormCleared({
"cc-exp-month": "MM",
"cc-exp-year": "YY"
});
});
</script>
<p id="display"></p>
<div id="content">
<form id="form1">
<p>This is a basic form.</p>
<p><label>organization: <input id="organization" autocomplete="organization"></label></p>
<p><label>streetAddress: <input id="street-address" autocomplete="street-address"></label></p>
<p><label>tel: <input id="tel" autocomplete="tel"></label></p>
<p><label>country: <input id="country" autocomplete="country"></label></p>
<p><label>Name: <input id="cc-name" autocomplete="cc-name"></label></p>
<p><label>Card Number: <input id="cc-number" autocomplete="cc-number"></label></p>
<!-- NOTE: If you're going to write a test like this,
ensure that the selected option doesn't match the data that you're trying to autofill,
otherwise your test will wait forever for an event that will never fire.
I.e, if your saved cc-exp-month is 01, make sure your selected option ISN'T 01.
-->
<p><label>Expiration month: <select id="cc-exp-month" autocomplete="cc-exp-month">
<option value="MM" selected>MM</option>
<option value="1">01</option>
<option value="2">02</option>
<option value="3">03</option>
<option value="4">04</option>
<option value="5">05</option>
<option value="6">06</option>
<option value="7">07</option>
<option value="8">08</option>
<option value="9">09</option>
<option value="10">10</option>
<option value="11">11</option>
<option value="12">12</option>
</select>
</label></p>
<!-- NOTE: If you're going to write a test like this,
ensure that the selected option doesn't match the data that you're trying to autofill,
otherwise your test will wait forever for an event that will never fire.
I.e, if your saved cc-exp-year is 2017, make sure your selected option ISN'T 2017.
-->
<p><label>Expiration year: <select id="cc-exp-year" autocomplete="cc-exp-year">
<option value="YY" selected>YY</option>
<option value="2017">2017</option>
<option value="2018">2018</option>
<option value="2019">2019</option>
<option value="2020">2020</option>
<option value="2021">2021</option>
<option value="2022">2022</option>
</select>
</label></p>
<p><label>CSC: <input id="cc-csc" autocomplete="cc-csc"></label></p>
</form>
</div>
<pre id="test"></pre>
</body>
</html>

Просмотреть файл

@ -0,0 +1,42 @@
/* global runHeuristicsTest */
"use strict";
runHeuristicsTest(
[
{
fixturePath: "Payment.html",
expectedResult: [
[
[
{
section: "",
addressType: "",
contactType: "",
fieldName: "cc-number",
},
{
section: "",
addressType: "",
contactType: "",
fieldName: "cc-name"
},
{
section: "",
addressType: "",
contactType: "",
fieldName: "cc-exp-month"
},
{
section: "",
addressType: "",
contactType: "",
fieldName: "cc-exp-year"
},
],
],
],
},
],
"../../../fixtures/third_party/DirectAsda/"
)

Просмотреть файл

@ -0,0 +1,36 @@
/* global runHeuristicsTest */
"use strict";
runHeuristicsTest(
[
{
fixturePath: "Payment.html",
expectedResult: [
[
[
{
section: "",
addressType: "",
contactType: "",
fieldName: "cc-number",
},
{
section: "",
addressType: "",
contactType: "",
fieldName: "cc-exp-month"
},
{
section: "",
addressType: "",
contactType: "",
fieldName: "cc-exp-year"
},
],
],
],
},
],
"../../../fixtures/third_party/GlobalDirectAsda/"
)

Просмотреть файл

@ -12,9 +12,14 @@ skip-if = (os == "linux") && ccov # bug 1614100
skip-if = (os == "linux") && ccov # bug 1614100
[test_CostCo.js]
skip-if = (os == "linux") && ccov # bug 1614100
[test_DirectAsda.js]
skip-if = (os == "linux") && ccov # bug 1614100
[test_GlobalDirectAsda.js]
skip-if = (os == "linux") && ccov # bug 1614100
[test_HomeDepot.js]
skip-if = (os == "linux") && ccov # bug 1614100
[test_Lufthansa.js]
skip-if = (os == "linux") && ccov # bug 1614100
[test_Macys.js]
skip-if = (os == "linux") && ccov # bug 1614100
[test_NewEgg.js]

Просмотреть файл

@ -304,7 +304,7 @@ const TESTCASES = [
},
{
description:
"Fill credit card fields in a form without a placeholder on expiration month input field",
"Fill credit card fields in a form without a placeholder on expiration month and expiration year input fields",
document: `<form>
<input id="cc-number" autocomplete="cc-number">
<input id="cc-name" autocomplete="cc-name">
@ -327,6 +327,31 @@ const TESTCASES = [
"cc-exp-year": "25",
},
},
{
description:
"Fill credit card fields in a form with a placeholder on expiration year input field",
document: `<form>
<input id="cc-number" autocomplete="cc-number">
<input id="cc-name" autocomplete="cc-name">
<input id="cc-exp-month" autocomplete="cc-exp-month">
<input id="cc-exp-year" autocomplete="cc-exp-year" placeholder="YY">
</form>
`,
focusedInputId: "cc-number",
profileData: {
guid: "123",
"cc-number": "4111111111111111",
"cc-name": "test name",
"cc-exp-month": 6,
"cc-exp-year": 2025,
},
expectedResult: {
"cc-number": "4111111111111111",
"cc-name": "test name",
"cc-exp-month": "6",
"cc-exp-year": "25",
},
},
{
description:
"Form with hidden input and visible input that share the same autocomplete attribute",
@ -462,6 +487,31 @@ const TESTCASES = [
"cc-exp-year2": "",
},
},
{
description:
"Fill credit card fields in a form with placeholders on month and year and these inputs are type=tel",
document: `<form>
<input id="cardHolder">
<input id="cardNumber">
<input id="month" type="tel" name="month" placeholder="MM">
<input id="year" type="tel" name="year" placeholder="YY">
</form>
`,
focusedInputId: "cardHolder",
profileData: {
guid: "123",
"cc-number": "4111111111111111",
"cc-name": "test name",
"cc-exp-month": 6,
"cc-exp-year": 2025,
},
expectedResult: {
cardHolder: "test name",
cardNumber: "4111111111111111",
month: "06",
year: "25",
},
},
];
const TESTCASES_INPUT_UNCHANGED = [
@ -753,11 +803,20 @@ function do_test(testcases, testFn) {
let decryptHelper = async (cipherText, reauth) => {
return OSKeyStore.decrypt(cipherText, false);
};
handler.collectFormFields();
let focusedInput = doc.getElementById(testcase.focusedInputId);
handler.focusedInput = focusedInput;
try {
handler.focusedInput = focusedInput;
} catch (e) {
if (e.message.includes("WeakMap key must be an object")) {
throw new Error(
`Couldn't find the focusedInputId in the current form! Make sure focusedInputId exists in your test form! testcase description:${testcase.description}`
);
} else {
throw e;
}
}
for (let section of handler.sections) {
section._decrypt = decryptHelper;

Просмотреть файл

@ -49,6 +49,10 @@ const getCCExpMonthFormatted = () => {
return DEFAULT_CREDITCARD_RECORD["cc-exp-month"].toString().padStart(2, "0");
};
const getCCExpYearFormatted = () => {
return DEFAULT_CREDITCARD_RECORD["cc-exp-year"].toString().substring(2);
};
const TESTCASES = [
{
description: "Address form with street-address",
@ -1076,6 +1080,7 @@ const TESTCASES = [
document: `<form>
<input autocomplete="cc-number">
<input autocomplete="cc-exp-month" placeholder="MM">
<input autocomplete="cc-exp-year">
</form>`,
profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
expectedResult: [
@ -1084,6 +1089,20 @@ const TESTCASES = [
}),
],
},
{
description: "Use placeholder to adjust cc-exp-year field [yy].",
document: `<form>
<input autocomplete="cc-number">
<input autocomplete="cc-exp-month">
<input autocomplete="cc-exp-year" placeholder="YY">
</form>`,
profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
expectedResult: [
Object.assign({}, DEFAULT_CREDITCARD_RECORD, {
"cc-exp-year-formatted": getCCExpYearFormatted(),
}),
],
},
{
description: "Test maxlength=2 on numeric fields.",
document: `<form>

Просмотреть файл

@ -22,6 +22,10 @@ const DEFAULT_CREDITCARD_RECORD = {
"cc-exp": "2025-01",
};
const getCCExpYearFormatted = () => {
return DEFAULT_CREDITCARD_RECORD["cc-exp-year"].toString().substring(2);
};
const FR_TESTCASES = [
{
description: "Use placeholder to adjust cc-exp format [mm/aa].",
@ -89,6 +93,20 @@ const FR_TESTCASES = [
}),
],
},
{
description: "Use placeholder to adjust cc-exp-year field [aa].",
document: `<form>
<input autocomplete="cc-number">
<input autocomplete="cc-exp-month">
<input autocomplete="cc-exp-year" placeholder="AA">
</form>`,
profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
expectedResult: [
Object.assign({}, DEFAULT_CREDITCARD_RECORD, {
"cc-exp-year-formatted": getCCExpYearFormatted(),
}),
],
},
];
const DE_TESTCASES = [
@ -191,6 +209,20 @@ const DE_TESTCASES = [
}),
],
},
{
description: "Use placeholder to adjust cc-exp-year field [jj].",
document: `<form>
<input autocomplete="cc-number">
<input autocomplete="cc-exp-month">
<input autocomplete="cc-exp-year" placeholder="JJ">
</form>`,
profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
expectedResult: [
Object.assign({}, DEFAULT_CREDITCARD_RECORD, {
"cc-exp-year-formatted": getCCExpYearFormatted(),
}),
],
},
];
const TESTCASES = [FR_TESTCASES, DE_TESTCASES];

Просмотреть файл

@ -422,7 +422,10 @@ class FormAutofillSection {
for (let fieldDetail of this.fieldDetails) {
let element = fieldDetail.elementWeakRef.get();
let value = profile[fieldDetail.fieldName] || "";
let value =
profile[`${fieldDetail.fieldName}-formatted`] ||
profile[fieldDetail.fieldName] ||
"";
// Skip the field that is null
if (!element) {
@ -732,6 +735,12 @@ class FormAutofillSection {
option.hasAttribute("selected")
);
element.value = selected ? selected.value : element.options[0].value;
element.dispatchEvent(
new element.ownerGlobal.Event("input", { bubbles: true })
);
element.dispatchEvent(
new element.ownerGlobal.Event("change", { bubbles: true })
);
}
}
@ -1071,7 +1080,14 @@ class FormAutofillCreditCardSection extends FormAutofillSection {
);
}
creditCardExpDateTransformer(profile) {
/**
* Handles credit card expiry date transformation when
* the expiry date exists in a cc-exp field.
*
* @param {object} profile
* @memberof FormAutofillCreditCardSection
*/
creditCardExpiryDateTransformer(profile) {
if (!profile["cc-exp"]) {
return;
}
@ -1145,23 +1161,31 @@ class FormAutofillCreditCardSection extends FormAutofillSection {
}
}
creditCardExpMonthTransformer(profile) {
if (!profile["cc-exp-month"]) {
return;
}
let detail = this.getFieldDetailByName("cc-exp-month");
if (!detail) {
return;
}
let element = detail.elementWeakRef.get();
/**
* Handles credit card expiry date transformation when the expiry date exists in
* the separate cc-exp-month and cc-exp-year fields
*
* @param {object} profile
* @memberof FormAutofillCreditCardSection
*/
creditCardExpMonthAndYearTransformer(profile) {
const getInputElementByField = (field, self) => {
if (!field) {
return null;
}
let detail = self.getFieldDetailByName(field);
if (!detail) {
return null;
}
let element = detail.elementWeakRef.get();
return element.tagName === "INPUT" ? element : null;
};
let month = getInputElementByField("cc-exp-month", this);
// If the expiration month element is an input,
// then we examine any placeholder to see if we should format the expiration month
// as a zero padded string in order to autofill correctly.
if (element.tagName === "INPUT") {
let placeholder = element.placeholder;
if (month) {
let placeholder = month.placeholder;
// Checks for 'MM' placeholder and converts the month to a two digit string.
let result = /(?<!.)mm(?!.)/i.test(placeholder);
@ -1171,6 +1195,22 @@ class FormAutofillCreditCardSection extends FormAutofillSection {
.padStart(2, "0");
}
}
let year = getInputElementByField("cc-exp-year", this);
// If the expiration year element is an input,
// then we examine any placeholder to see if we should format the expiration year
// as a zero padded string in order to autofill correctly.
if (year) {
let placeholder = year.placeholder;
// Checks for 'YY'|'AA'|'JJ' placeholder and converts the year to a two digit string using the last two digits.
let result = /(?<!.)(yy|aa|jj)(?!.)/i.test(placeholder);
if (result) {
profile["cc-exp-year-formatted"] = profile["cc-exp-year"]
.toString()
.substring(2);
}
}
}
async _decrypt(cipherText, reauth) {
@ -1206,8 +1246,8 @@ class FormAutofillCreditCardSection extends FormAutofillSection {
// This ensures that the expiry value that is cached in the matchSelectOptions
// matches the expiry value that is stored in the profile ensuring that autofill works
// correctly when dealing with option elements.
this.creditCardExpDateTransformer(profile);
this.creditCardExpMonthTransformer(profile);
this.creditCardExpiryDateTransformer(profile);
this.creditCardExpMonthAndYearTransformer(profile);
this.matchSelectOptions(profile);
this.adaptFieldMaxLength(profile);
}

Просмотреть файл

@ -1,3 +1,5 @@
/* eslint-disable prettier/prettier */
/* eslint-disable no-useless-concat */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
@ -48,14 +50,17 @@ var HeuristicsRegExp = {
"address-level1": "land", // de-DE
"additional-name": "apellido.?materno|lastlastname",
"cc-name":
// eslint-disable-next-line prettier/prettier
"accountholdername" +
"accountholdername" +
"|titulaire", // fr-FR
"cc-number": "(cc|kk)nr", // de-DE
"cc-exp-month": "(cc|kk)month", // de-DE
"cc-exp-year": "(cc|kk)year", // de-DE
// eslint-disable-next-line prettier/prettier
"cc-type": "type" +
"cc-exp-month":
"month" +
"|(cc|kk)month", // de-DE
"cc-exp-year":
"year" +
"|(cc|kk)year", // de-DE
"cc-type":
"type" +
"|kartenmarke", // de-DE
},