summaryrefslogtreecommitdiffstats
path: root/toolkit/components/formautofill/android
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /toolkit/components/formautofill/android
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/formautofill/android')
-rw-r--r--toolkit/components/formautofill/android/FormAutofillPrompter.sys.mjs70
-rw-r--r--toolkit/components/formautofill/android/FormAutofillStorage.sys.mjs277
2 files changed, 347 insertions, 0 deletions
diff --git a/toolkit/components/formautofill/android/FormAutofillPrompter.sys.mjs b/toolkit/components/formautofill/android/FormAutofillPrompter.sys.mjs
new file mode 100644
index 0000000000..6ac3744dac
--- /dev/null
+++ b/toolkit/components/formautofill/android/FormAutofillPrompter.sys.mjs
@@ -0,0 +1,70 @@
+/* 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/. */
+
+/*
+ * Implements doorhanger singleton that wraps up the PopupNotifications and handles
+ * the doorhager UI for formautofill related features.
+ */
+
+import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
+
+const lazy = {};
+
+ChromeUtils.defineESModuleGetters(lazy, {
+ GeckoViewPrompter: "resource://gre/modules/GeckoViewPrompter.sys.mjs",
+});
+
+XPCOMUtils.defineLazyModuleGetters(lazy, {
+ CreditCard: "resource://gre/modules/GeckoViewAutocomplete.jsm",
+ GeckoViewAutocomplete: "resource://gre/modules/GeckoViewAutocomplete.jsm",
+});
+
+// Sync with Autocomplete.SaveOption.Hint in Autocomplete.java
+const CreditCardStorageHint = {
+ NONE: 0,
+ GENERATED: 1 << 0,
+ LOW_CONFIDENCE: 1 << 1,
+};
+
+export let FormAutofillPrompter = {
+ _createMessage(creditCards) {
+ let hint = CreditCardStorageHint.NONE;
+ return {
+ // Sync with PromptController
+ type: "Autocomplete:Save:CreditCard",
+ hint,
+ creditCards,
+ };
+ },
+
+ async promptToSaveAddress(browser, type, description) {
+ throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
+ },
+
+ async promptToSaveCreditCard(browser, storage, record, flowId) {
+ const prompt = new lazy.GeckoViewPrompter(browser.ownerGlobal);
+
+ const duplicateRecord = (await storage.getDuplicateRecords(record).next())
+ .value;
+ let newCreditCard;
+ if (duplicateRecord) {
+ newCreditCard = { ...duplicateRecord, ...record };
+ } else {
+ newCreditCard = record;
+ }
+
+ prompt.asyncShowPrompt(
+ this._createMessage([lazy.CreditCard.fromGecko(newCreditCard)]),
+ result => {
+ const selectedCreditCard = result?.selection?.value;
+
+ if (!selectedCreditCard) {
+ return;
+ }
+
+ lazy.GeckoViewAutocomplete.onCreditCardSave(selectedCreditCard);
+ }
+ );
+ },
+};
diff --git a/toolkit/components/formautofill/android/FormAutofillStorage.sys.mjs b/toolkit/components/formautofill/android/FormAutofillStorage.sys.mjs
new file mode 100644
index 0000000000..76d583536b
--- /dev/null
+++ b/toolkit/components/formautofill/android/FormAutofillStorage.sys.mjs
@@ -0,0 +1,277 @@
+/* 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/. */
+
+/*
+ * Implements an interface of the storage of Form Autofill for GeckoView.
+ */
+
+// We expose a singleton from this module. Some tests may import the
+// constructor via a backstage pass.
+import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
+
+import {
+ FormAutofillStorageBase,
+ CreditCardsBase,
+ AddressesBase,
+} from "resource://autofill/FormAutofillStorageBase.sys.mjs";
+import { JSONFile } from "resource://gre/modules/JSONFile.sys.mjs";
+
+const lazy = {};
+
+XPCOMUtils.defineLazyModuleGetters(lazy, {
+ Address: "resource://gre/modules/GeckoViewAutocomplete.jsm",
+ CreditCard: "resource://gre/modules/GeckoViewAutocomplete.jsm",
+ GeckoViewAutocomplete: "resource://gre/modules/GeckoViewAutocomplete.jsm",
+});
+
+class GeckoViewStorage extends JSONFile {
+ constructor() {
+ super({ path: null, sanitizedBasename: "GeckoViewStorage" });
+ }
+
+ async updateCreditCards() {
+ const creditCards =
+ await lazy.GeckoViewAutocomplete.fetchCreditCards().then(
+ results => results?.map(r => lazy.CreditCard.parse(r).toGecko()) ?? [],
+ _ => []
+ );
+ super.data.creditCards = creditCards;
+ }
+
+ async updateAddresses() {
+ const addresses = await lazy.GeckoViewAutocomplete.fetchAddresses().then(
+ results => results?.map(r => lazy.Address.parse(r).toGecko()) ?? [],
+ _ => []
+ );
+ super.data.addresses = addresses;
+ }
+
+ async load() {
+ super.data = { creditCards: {}, addresses: {} };
+ await this.updateCreditCards();
+ await this.updateAddresses();
+ }
+
+ ensureDataReady() {
+ if (this.dataReady) {
+ return;
+ }
+ throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
+ }
+
+ async _save() {
+ throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
+ }
+}
+
+class Addresses extends AddressesBase {
+ // Override AutofillRecords methods.
+
+ _initialize() {
+ this._initializePromise = Promise.resolve();
+ }
+
+ async _saveRecord(record, { sourceSync = false } = {}) {
+ lazy.GeckoViewAutocomplete.onAddressSave(lazy.Address.fromGecko(record));
+ }
+
+ /**
+ * Returns the record with the specified GUID.
+ *
+ * @param {string} guid
+ * Indicates which record to retrieve.
+ * @param {object} options
+ * @param {boolean} [options.rawData = false]
+ * Returns a raw record without modifications and the computed fields
+ * (this includes private fields)
+ * @returns {Promise<object>}
+ * A clone of the record.
+ */
+ async get(guid, { rawData = false } = {}) {
+ await this._store.updateAddresses();
+ return super.get(guid, { rawData });
+ }
+
+ /**
+ * Returns all records.
+ *
+ * @param {object} options
+ * @param {boolean} [options.rawData = false]
+ * Returns raw records without modifications and the computed fields.
+ * @param {boolean} [options.includeDeleted = false]
+ * Also return any tombstone records.
+ * @returns {Promise<Array.<object>>}
+ * An array containing clones of all records.
+ */
+ async getAll({ rawData = false, includeDeleted = false } = {}) {
+ await this._store.updateAddresses();
+ return super.getAll({ rawData, includeDeleted });
+ }
+
+ /**
+ * Return all saved field names in the collection.
+ *
+ * @returns {Set} Set containing saved field names.
+ */
+ async getSavedFieldNames() {
+ await this._store.updateAddresses();
+ return super.getSavedFieldNames();
+ }
+
+ async reconcile(remoteRecord) {
+ throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
+ }
+
+ async findDuplicateGUID(remoteRecord) {
+ throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
+ }
+
+ async mergeToStorage(targetRecord, strict = false) {
+ throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
+ }
+}
+
+class CreditCards extends CreditCardsBase {
+ async _encryptNumber(creditCard) {
+ // Don't encrypt or obfuscate for GV, since we don't store or show
+ // the number. The API has to always provide the original number.
+ }
+
+ // Override AutofillRecords methods.
+
+ _initialize() {
+ this._initializePromise = Promise.resolve();
+ }
+
+ async _saveRecord(record, { sourceSync = false } = {}) {
+ lazy.GeckoViewAutocomplete.onCreditCardSave(
+ lazy.CreditCard.fromGecko(record)
+ );
+ }
+
+ /**
+ * Returns the record with the specified GUID.
+ *
+ * @param {string} guid
+ * Indicates which record to retrieve.
+ * @param {object} options
+ * @param {boolean} [options.rawData = false]
+ * Returns a raw record without modifications and the computed fields
+ * (this includes private fields)
+ * @returns {Promise<object>}
+ * A clone of the record.
+ */
+ async get(guid, { rawData = false } = {}) {
+ await this._store.updateCreditCards();
+ return super.get(guid, { rawData });
+ }
+
+ /**
+ * Returns all records.
+ *
+ * @param {object} options
+ * @param {boolean} [options.rawData = false]
+ * Returns raw records without modifications and the computed fields.
+ * @param {boolean} [options.includeDeleted = false]
+ * Also return any tombstone records.
+ * @returns {Promise<Array.<object>>}
+ * An array containing clones of all records.
+ */
+ async getAll({ rawData = false, includeDeleted = false } = {}) {
+ await this._store.updateCreditCards();
+ return super.getAll({ rawData, includeDeleted });
+ }
+
+ /**
+ * Return all saved field names in the collection.
+ *
+ * @returns {Set} Set containing saved field names.
+ */
+ async getSavedFieldNames() {
+ await this._store.updateCreditCards();
+ return super.getSavedFieldNames();
+ }
+
+ /**
+ * Find a duplicate credit card record in the storage.
+ *
+ * A record is considered as a duplicate of another record when two records
+ * are the "same". This might be true even when some of their fields are
+ * different. For example, one record has the same credit card number but has
+ * different expiration date as the other record are still considered as
+ * "duplicate".
+ * This is different from `getMatchRecord`, which ensures all the fields with
+ * value in the the record is equal to the returned record.
+ *
+ * @param {object} record
+ * The credit card for duplication checking. please make sure the
+ * record is normalized.
+ * @returns {object}
+ * Return the first duplicated record found in storage, null otherwise.
+ */
+ async *getDuplicateRecords(record) {
+ if (!record["cc-number"]) {
+ return null;
+ }
+
+ await this._store.updateCreditCards();
+ for (const recordInStorage of this._data) {
+ if (recordInStorage.deleted) {
+ continue;
+ }
+
+ if (recordInStorage["cc-number"] == record["cc-number"]) {
+ yield recordInStorage;
+ }
+ }
+ return null;
+ }
+
+ async reconcile(remoteRecord) {
+ throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
+ }
+
+ async findDuplicateGUID(remoteRecord) {
+ throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
+ }
+
+ async mergeToStorage(targetRecord, strict = false) {
+ throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
+ }
+}
+
+export class FormAutofillStorage extends FormAutofillStorageBase {
+ constructor() {
+ super(null);
+ }
+
+ getAddresses() {
+ if (!this._addresses) {
+ this._store.ensureDataReady();
+ this._addresses = new Addresses(this._store);
+ }
+ return this._addresses;
+ }
+
+ getCreditCards() {
+ if (!this._creditCards) {
+ this._store.ensureDataReady();
+ this._creditCards = new CreditCards(this._store);
+ }
+ return this._creditCards;
+ }
+
+ /**
+ * Initializes the in-memory async store API.
+ *
+ * @returns {JSONFile}
+ * The JSONFile store.
+ */
+ _initializeStore() {
+ return new GeckoViewStorage();
+ }
+}
+
+// The singleton exposed by this module.
+export const formAutofillStorage = new FormAutofillStorage();