Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,8 @@ export const AddressMetaDataExtension = {
},
"data/JP": {
alpha_3_code: "JPN",
fmt: "〒%Z%n%S%C%n%A%n%O%n%N",
require: "ACSZ",
},
"data/JE": {
alpha_3_code: "JEY",
Expand Down Expand Up @@ -474,6 +476,7 @@ export const AddressMetaDataExtension = {
},
"data/NL": {
alpha_3_code: "NLD",
address_reversed: true,
},
"data/NC": {
alpha_3_code: "NCL",
Expand Down
36 changes: 36 additions & 0 deletions firefox-ios/Client/Assets/CC_Script/AddressParser.sys.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -313,4 +313,40 @@ export class AddressParser {
static mergeWhitespace(s) {
return s?.replace(/\s{2,}/g, " ");
}

// This is quite fragile, but handles a number of basic cases. This is intended
// to split the street number suffix from the street number. For example:
// 35B will be split into number=35,suffix=B
// Returns an array with [housenumber, suffix]
static parseHouseSuffix(address, structuredAddress) {
let streetNumber = structuredAddress?.street_number;
if (!streetNumber) {
return null;
}

let numberIndex = address.indexOf(streetNumber);
if (numberIndex < 0) {
return [streetNumber];
}

let match = streetNumber.match(/^(\d+)(\w?)/);
if (!match) {
return [streetNumber];
}

let suffix;
let result = [match[1]];
// If the house number is after the street, include the rest of the address
// as part of the suffix, otherwise just include the suffix on the number.
if (numberIndex > structuredAddress?.street_name.length) {
suffix = address.substring(numberIndex + match[1].length).trim();
} else {
suffix = match[2];
}
if (suffix) {
result.push(suffix.replace("\n", " "));
}

return result;
}
}
20 changes: 20 additions & 0 deletions firefox-ios/Client/Assets/CC_Script/AddressRecord.sys.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { FormAutofillNameUtils } from "resource://gre/modules/shared/FormAutofil
import { FormAutofillUtils } from "resource://gre/modules/shared/FormAutofillUtils.sys.mjs";
import { PhoneNumber } from "resource://gre/modules/shared/PhoneNumber.sys.mjs";
import { FormAutofill } from "resource://autofill/FormAutofill.sys.mjs";
import { AddressParser } from "resource://gre/modules/shared/AddressParser.sys.mjs";

/**
* The AddressRecord class serves to handle and normalize internal address records.
Expand All @@ -32,6 +33,7 @@ export class AddressRecord {
static computeFields(address) {
this.#computeNameFields(address);
this.#computeAddressLineFields(address);
this.#computeStreetAndHouseNumberFields(address);
this.#computeCountryFields(address);
this.#computeTelFields(address);
}
Expand Down Expand Up @@ -66,6 +68,24 @@ export class AddressRecord {
}
}

static #computeStreetAndHouseNumberFields(address) {
if (!("address-housenumber" in address) && "street-address" in address) {
let streetAddress = address["street-address"];
let parsedAddress = AddressParser.parseStreetAddress(streetAddress);
if (parsedAddress) {
address["address-housenumber"] = parsedAddress.street_number;

let splitNumber = AddressParser.parseHouseSuffix(
streetAddress,
parsedAddress
);
if (splitNumber?.length >= 2) {
address["address-extra-housesuffix"] = splitNumber[1];
}
}
}
}

static #computeCountryFields(address) {
// Compute country name
if (!("country-name" in address)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const AutofillFormFactory = {
findRootForField(element) {
let ignoreForm;
try {
const bc = element.ownerGlobal.browsingContext;
const bc = element.documentGlobal.browsingContext;
ignoreForm = bc != bc.top;
} catch {
ignoreForm = false;
Expand All @@ -32,11 +32,15 @@ export const AutofillFormFactory = {
createFromField(aField) {
let ignoreForm;
try {
const bc = aField.ownerGlobal.browsingContext;
const bc = aField.documentGlobal.browsingContext;
ignoreForm = bc != bc.top;
} catch {
ignoreForm = false;
}
return lazy.FormLikeFactory.createFromField(aField, { ignoreForm });
},

createFromDocumentRoot(aDocRoot) {
return lazy.FormLikeFactory.createFromDocumentRoot(aDocRoot);
},
};
89 changes: 42 additions & 47 deletions firefox-ios/Client/Assets/CC_Script/AutofillTelemetry.sys.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@ class AutofillTelemetryBase {
EVENT_CATEGORY = null;
EVENT_OBJECT_FORM_INTERACTION = null;

HISTOGRAM_NUM_USES = null;
HISTOGRAM_PROFILE_NUM_USES = null;
HISTOGRAM_PROFILE_NUM_USES_KEY = null;

#initFormEventExtra(value) {
let extra = {};
for (const field of Object.values(this.SUPPORTED_FIELDS)) {
Expand Down Expand Up @@ -183,17 +179,6 @@ class AutofillTelemetryBase {
throw new Error("Not implemented.");
}

recordNumberOfUse(records) {
let histogram = Services.telemetry.getKeyedHistogramById(
this.HISTOGRAM_PROFILE_NUM_USES
);
histogram.clear();

for (let record of records) {
histogram.add(this.HISTOGRAM_PROFILE_NUM_USES_KEY, record.timesUsed);
}
}

recordIframeLayoutDetection(flowId, fieldDetails) {
const fieldsInMainFrame = [];
const fieldsInIframe = [];
Expand Down Expand Up @@ -237,9 +222,7 @@ export class AddressTelemetry extends AutofillTelemetryBase {
EVENT_CATEGORY = "address";
EVENT_OBJECT_FORM_INTERACTION = "AddressForm";
EVENT_OBJECT_FORM_INTERACTION_EXT = "AddressFormExt";

HISTOGRAM_PROFILE_NUM_USES = "AUTOFILL_PROFILE_NUM_USES";
HISTOGRAM_PROFILE_NUM_USES_KEY = "address";
EVENT_OBJECT_FORM_INTERACTION_MLCOMPARE = "Mlcompare";

// Fields that are recorded in `address_form` and `address_form_ext` telemetry
SUPPORTED_FIELDS = {
Expand Down Expand Up @@ -310,16 +293,48 @@ export class AddressTelemetry extends AutofillTelemetryBase {
recordAutofillProfileCount(count) {
Glean.formautofillAddresses.autofillProfilesCount.set(count);
}

recordMLDetection(fieldDetails, hash, mlTime) {
let reason = [];
let re = [];
let ml = [];
let computed = [];

let reTime = 0;

fieldDetails.forEach(detail => {
// The data is formed as followed:
// detail.originalFieldName - if reason is "autocomplete", the autocomplete value,
// otherwise the value determined by heuristics.
// detail.mlFieldName = the reason detected by the model.
reason.push(detail.reason);
re.push(detail.extraInfo.reFieldName || "");
ml.push(detail.mlFieldName || "");
computed.push(detail.fieldName || "");
reTime += detail.extraInfo.reTime || 0;
});

let extra = {
value: hash,
mlversion: "1",
retime: reTime.toFixed(2),
mltime: mlTime.toFixed(2),
reason: reason.toString(),
re: re.toString(),
ml: ml.toString(),
computed: computed.toString(),
};

Glean.address[
"detected" + this.EVENT_OBJECT_FORM_INTERACTION_MLCOMPARE
]?.record(extra);
}
}

class CreditCardTelemetry extends AutofillTelemetryBase {
EVENT_CATEGORY = "creditcard";
EVENT_OBJECT_FORM_INTERACTION = "CcFormV2";

HISTOGRAM_NUM_USES = "CREDITCARD_NUM_USES";
HISTOGRAM_PROFILE_NUM_USES = "AUTOFILL_PROFILE_NUM_USES";
HISTOGRAM_PROFILE_NUM_USES_KEY = "credit_card";

// Mapping of field name used in formautofill code to the field name
// used in the telemetry.
SUPPORTED_FIELDS = {
Expand Down Expand Up @@ -369,23 +384,6 @@ class CreditCardTelemetry extends AutofillTelemetryBase {
}
}

recordNumberOfUse(records) {
super.recordNumberOfUse(records);

if (!this.HISTOGRAM_NUM_USES) {
return;
}

let histogram = Services.telemetry.getHistogramById(
this.HISTOGRAM_NUM_USES
);
histogram.clear();

for (let record of records) {
histogram.add(record.timesUsed);
}
}

recordAutofillProfileCount(count) {
Glean.formautofillCreditcards.autofillProfilesCount.set(count);
}
Expand Down Expand Up @@ -463,15 +461,12 @@ export class AutofillTelemetry {
telemetry.recordAutofillProfileCount(count);
}

/**
* Utility functions for address/credit card number of use
*/
static recordNumberOfUse(type, records) {
const telemetry = this.#getTelemetryByType(type);
telemetry.recordNumberOfUse(records);
}

static recordFormSubmissionHeuristicCount(label) {
Glean.formautofill.formSubmissionHeuristic[label].add(1);
}

static recordMLDetection(fieldDetails, hash, mlTime) {
const telemetry = this.#getTelemetryByType(AutofillTelemetry.ADDRESS);
telemetry.recordMLDetection(fieldDetails, hash, mlTime);
}
}
5 changes: 2 additions & 3 deletions firefox-ios/Client/Assets/CC_Script/Constants.ios.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
const IOS_DEFAULT_PREFERENCES = {
"extensions.formautofill.creditCards.heuristics.mode": 1,
"extensions.formautofill.creditCards.heuristics.fathom.confidenceThreshold": 0.5,
"extensions.formautofill.creditCards.heuristics.fathom.highConfidenceThreshold": 0.95,
"extensions.formautofill.creditCards.heuristics.fathom.testConfidence": 0,
"extensions.formautofill.creditCards.heuristics.fathom.types":
"cc-number,cc-name",
Expand All @@ -32,15 +31,15 @@ const IOS_DEFAULT_PREFERENCES = {
"extensions.formautofill.heuristics.captureOnPageNavigation": false,
"extensions.formautofill.heuristics.detectDynamicFormChanges": false,
"extensions.formautofill.heuristics.fillOnDynamicFormChanges": false,
"extensions.formautofill.heuristics.refillOnSiteClearingFields": false,
"extensions.formautofill.focusOnAutofill": false,
"extensions.formautofill.test.ignoreVisibilityCheck": false,
"extensions.formautofill.heuristics.autofillSameOriginWithTop": false,
"signon.generation.confidenceThreshold": 0.75,
"extensions.formautofill.ml.experiment.enabled": false,
};

// Used Mimic the behavior of .getAutocompleteInfo()
// List from: https://searchfox.org/mozilla-central/source/dom/base/AutocompleteFieldList.h#89-149
// List from: https://searchfox.org/firefox-main/source/dom/base/AutocompleteFieldList.h#89-149
// Also found here: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete
const VALID_AUTOCOMPLETE_FIELDS = [
"off",
Expand Down
24 changes: 11 additions & 13 deletions firefox-ios/Client/Assets/CC_Script/CreditCard.sys.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ export const NETWORK_NAMES = {
// Based on https://en.wikipedia.org/wiki/Payment_card_number
//
// Notice:
// - CarteBancaire (`4035`, `4360`) is now recognized as Visa.
// - UnionPay (`63--`) is now recognized as Discover.
// - CarteBancaire (`4035`, `4360`) must precede the generic Visa entry.
// This means that the order matters.
// First we'll try to match more specific card,
// and if that doesn't match we'll test against the more generic range.
Expand All @@ -45,17 +44,15 @@ const CREDIT_CARD_IIN = [
{ type: "diners", start: 36, end: 36, len: [14, 19] },
{ type: "diners", start: 38, end: 39, len: [14, 19] },
{ type: "discover", start: 6011, end: 6011, len: [16, 19] },
{ type: "discover", start: 622126, end: 622925, len: [16, 19] },
{ type: "discover", start: 624000, end: 626999, len: [16, 19] },
{ type: "discover", start: 628200, end: 628899, len: [16, 19] },
{ type: "discover", start: 64, end: 65, len: [16, 19] },
{ type: "discover", start: 644, end: 649, len: [16, 19] },
{ type: "discover", start: 65, end: 65, len: [16, 19] },
{ type: "jcb", start: 3528, end: 3589, len: [16, 19] },
{ type: "mastercard", start: 2221, end: 2720, len: 16 },
{ type: "mastercard", start: 51, end: 55, len: 16 },
{ type: "mir", start: 2200, end: 2204, len: 16 },
{ type: "mir", start: 2200, end: 2204, len: [16, 19] },
{ type: "unionpay", start: 62, end: 62, len: [16, 19] },
{ type: "unionpay", start: 81, end: 81, len: [16, 19] },
{ type: "visa", start: 4, end: 4, len: 16 },
{ type: "visa", start: 4, end: 4, len: [13, 19] },
].sort((a, b) => b.start - a.start);

export class CreditCard {
Expand Down Expand Up @@ -155,7 +152,7 @@ export class CreditCard {
if (value) {
let normalizedNumber = CreditCard.normalizeCardNumber(value);
// Based on the information on wiki[1], the shortest valid length should be
// 12 digits (Maestro).
// 12 digits (Diners Club Carte Blanche has 14, but some legacy networks go lower).
// [1] https://en.wikipedia.org/wiki/Payment_card_number
normalizedNumber = normalizedNumber.match(/^\d{12,}$/)
? normalizedNumber
Expand Down Expand Up @@ -217,6 +214,7 @@ export class CreditCard {

/**
* Normalizes a credit card number.
*
* @param {string} number
* @return {string | null}
* @memberof CreditCard
Expand Down Expand Up @@ -346,8 +344,8 @@ export class CreditCard {
}

/**
*
* Please use getLabelInfo above, as it allows for localization.
*
* @deprecated
*/
static getLabel({ number, name }) {
Expand Down Expand Up @@ -454,15 +452,15 @@ export class CreditCard {
}

static formatMaskedNumber(maskedNumber) {
return "*".repeat(4) + maskedNumber.substr(-4);
return "".repeat(4) + maskedNumber.substr(-4);
}

static getMaskedNumber(number) {
return "*".repeat(4) + " " + number.substr(-4);
return "".repeat(4) + " " + number.substr(-4);
}

static getLongMaskedNumber(number) {
return "*".repeat(number.length - 4) + number.substr(-4);
return "".repeat(number.length - 4) + number.substr(-4);
}

static getCreditCardLogo(network) {
Expand Down
Loading