From 75c0d195a6ed2293375e8a9c639e34a21725e400 Mon Sep 17 00:00:00 2001 From: dbajpeyi Date: Wed, 18 Dec 2024 11:03:51 +0100 Subject: [PATCH] refactor: add feature toggle to support apple-only --- dist/autofill-debug.js | 42 +++++++------------ dist/autofill.js | 42 +++++++------------ integration-test/helpers/mocks.webkit.js | 1 + .../tests/save-prompts.android.spec.js | 5 ++- .../tests/save-prompts.ios.spec.js | 2 +- .../tests/save-prompts.windows.spec.js | 27 ++++++++++++ src/DeviceInterface/InterfacePrototype.js | 24 +++++------ src/Form/Form.js | 2 +- src/Form/Form.test.js | 8 ++-- src/Form/formatters.js | 19 ++++----- src/Form/formatters.test.js | 4 +- src/Settings.js | 1 + src/Settings.test.js | 1 + .../Resources/assets/autofill-debug.js | 42 +++++++------------ swift-package/Resources/assets/autofill.js | 42 +++++++------------ 15 files changed, 124 insertions(+), 138 deletions(-) diff --git a/dist/autofill-debug.js b/dist/autofill-debug.js index 7cb05d454..3059aee77 100644 --- a/dist/autofill-debug.js +++ b/dist/autofill-debug.js @@ -9462,17 +9462,6 @@ class InterfacePrototype { } } - /** - * Checks if partial save can be triggered - * @param {DataStorageObject} values - * @returns {boolean} - */ - shouldTriggerPartialSave(values) { - // If credentials has only username field, and no password field, then trigger is a partialSave - const isUsernameOnly = Object.keys(values?.credentials || {}).length === 1 && Boolean(values?.credentials?.username); - return isUsernameOnly && Boolean(this.settings.featureToggles.partial_form_saves); - } - /** * `postSubmit` gives features a one-time-only opportunity to perform an * action directly after a form submission was observed. @@ -9487,14 +9476,14 @@ class InterfacePrototype { postSubmit(values, form) { if (!form.form) return; if (!form.hasValues(values)) return; - const shouldPartialSave = this.shouldTriggerPartialSave(values); - const checks = [form.shouldPromptToStoreData && !form.submitHandlerExecuted, this.passwordGenerator.generated, shouldPartialSave]; + const shouldTriggerPartialSave = Object.keys(values?.credentials || {}).length === 1 && Boolean(values?.credentials?.username) && Boolean(this.settings.featureToggles.partial_form_saves); + const checks = [form.shouldPromptToStoreData && !form.submitHandlerExecuted, this.passwordGenerator.generated, shouldTriggerPartialSave]; if (checks.some(Boolean)) { const formData = (0, _Credentials.appendGeneratedKey)(values, { password: this.passwordGenerator.password, username: this.emailProtection.lastGenerated }); - const trigger = shouldPartialSave ? 'partialSave' : 'formSubmission'; + const trigger = shouldTriggerPartialSave ? 'partialSave' : 'formSubmission'; this.storeFormData(formData, trigger); } } @@ -10275,7 +10264,7 @@ class Form { */ getValuesReadyForStorage() { const formValues = this.getRawValues(); - return (0, _formatters.prepareFormValuesForStorage)(formValues); + return (0, _formatters.prepareFormValuesForStorage)(formValues, this.device.settings.featureToggles.partial_form_saves); } /** @@ -12103,7 +12092,7 @@ const getMMAndYYYYFromString = expiration => { }; /** - * @param {InternalDataStorageObject} credentials + * @param {InternalDataStorageObject} data * @return {boolean} */ exports.getMMAndYYYYFromString = getMMAndYYYYFromString; @@ -12115,7 +12104,7 @@ const shouldStoreIdentities = _ref3 => { }; /** - * @param {InternalDataStorageObject} credentials + * @param {InternalDataStorageObject} data * @return {boolean} */ const shouldStoreCreditCards = _ref4 => { @@ -12144,7 +12133,8 @@ const formatPhoneNumber = phone => phone.replaceAll(/[^0-9|+]/g, ''); * @return {DataStorageObject} */ exports.formatPhoneNumber = formatPhoneNumber; -const prepareFormValuesForStorage = formValues => { +const prepareFormValuesForStorage = function (formValues) { + let canTriggerPartialSave = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; /** @type {Partial} */ let { credentials, @@ -12158,13 +12148,12 @@ const prepareFormValuesForStorage = formValues => { } /** Fixes for credentials */ - if (!credentials.username && (0, _autofillUtils.hasUsernameLikeIdentity)(identities)) { - // @ts-ignore - We know that username is not a useful value here - credentials.username = identities.emailAddress || identities.phone; - } - - // If we still don't have any credentials, we discard the object - if (Object.keys(credentials ?? {}).length === 0) { + if (canTriggerPartialSave || Boolean(formValues.credentials?.password)) { + if (credentials.password && !credentials.username && (0, _autofillUtils.hasUsernameLikeIdentity)(identities)) { + // @ts-ignore + credentials.username = identities.emailAddress || identities.phone; + } + } else { credentials = undefined; } @@ -15390,7 +15379,8 @@ class Settings { inputType_credentials: false, inputType_creditCards: false, inlineIcon_credentials: false, - unknown_username_categorization: false + unknown_username_categorization: false, + partial_form_saves: false }, /** @type {AvailableInputTypes} */ availableInputTypes: { diff --git a/dist/autofill.js b/dist/autofill.js index 01575b7f4..1b7d9af6d 100644 --- a/dist/autofill.js +++ b/dist/autofill.js @@ -5099,17 +5099,6 @@ class InterfacePrototype { } } - /** - * Checks if partial save can be triggered - * @param {DataStorageObject} values - * @returns {boolean} - */ - shouldTriggerPartialSave(values) { - // If credentials has only username field, and no password field, then trigger is a partialSave - const isUsernameOnly = Object.keys(values?.credentials || {}).length === 1 && Boolean(values?.credentials?.username); - return isUsernameOnly && Boolean(this.settings.featureToggles.partial_form_saves); - } - /** * `postSubmit` gives features a one-time-only opportunity to perform an * action directly after a form submission was observed. @@ -5124,14 +5113,14 @@ class InterfacePrototype { postSubmit(values, form) { if (!form.form) return; if (!form.hasValues(values)) return; - const shouldPartialSave = this.shouldTriggerPartialSave(values); - const checks = [form.shouldPromptToStoreData && !form.submitHandlerExecuted, this.passwordGenerator.generated, shouldPartialSave]; + const shouldTriggerPartialSave = Object.keys(values?.credentials || {}).length === 1 && Boolean(values?.credentials?.username) && Boolean(this.settings.featureToggles.partial_form_saves); + const checks = [form.shouldPromptToStoreData && !form.submitHandlerExecuted, this.passwordGenerator.generated, shouldTriggerPartialSave]; if (checks.some(Boolean)) { const formData = (0, _Credentials.appendGeneratedKey)(values, { password: this.passwordGenerator.password, username: this.emailProtection.lastGenerated }); - const trigger = shouldPartialSave ? 'partialSave' : 'formSubmission'; + const trigger = shouldTriggerPartialSave ? 'partialSave' : 'formSubmission'; this.storeFormData(formData, trigger); } } @@ -5912,7 +5901,7 @@ class Form { */ getValuesReadyForStorage() { const formValues = this.getRawValues(); - return (0, _formatters.prepareFormValuesForStorage)(formValues); + return (0, _formatters.prepareFormValuesForStorage)(formValues, this.device.settings.featureToggles.partial_form_saves); } /** @@ -7740,7 +7729,7 @@ const getMMAndYYYYFromString = expiration => { }; /** - * @param {InternalDataStorageObject} credentials + * @param {InternalDataStorageObject} data * @return {boolean} */ exports.getMMAndYYYYFromString = getMMAndYYYYFromString; @@ -7752,7 +7741,7 @@ const shouldStoreIdentities = _ref3 => { }; /** - * @param {InternalDataStorageObject} credentials + * @param {InternalDataStorageObject} data * @return {boolean} */ const shouldStoreCreditCards = _ref4 => { @@ -7781,7 +7770,8 @@ const formatPhoneNumber = phone => phone.replaceAll(/[^0-9|+]/g, ''); * @return {DataStorageObject} */ exports.formatPhoneNumber = formatPhoneNumber; -const prepareFormValuesForStorage = formValues => { +const prepareFormValuesForStorage = function (formValues) { + let canTriggerPartialSave = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; /** @type {Partial} */ let { credentials, @@ -7795,13 +7785,12 @@ const prepareFormValuesForStorage = formValues => { } /** Fixes for credentials */ - if (!credentials.username && (0, _autofillUtils.hasUsernameLikeIdentity)(identities)) { - // @ts-ignore - We know that username is not a useful value here - credentials.username = identities.emailAddress || identities.phone; - } - - // If we still don't have any credentials, we discard the object - if (Object.keys(credentials ?? {}).length === 0) { + if (canTriggerPartialSave || Boolean(formValues.credentials?.password)) { + if (credentials.password && !credentials.username && (0, _autofillUtils.hasUsernameLikeIdentity)(identities)) { + // @ts-ignore + credentials.username = identities.emailAddress || identities.phone; + } + } else { credentials = undefined; } @@ -11027,7 +11016,8 @@ class Settings { inputType_credentials: false, inputType_creditCards: false, inlineIcon_credentials: false, - unknown_username_categorization: false + unknown_username_categorization: false, + partial_form_saves: false }, /** @type {AvailableInputTypes} */ availableInputTypes: { diff --git a/integration-test/helpers/mocks.webkit.js b/integration-test/helpers/mocks.webkit.js index e0b692ede..3ac1f7217 100644 --- a/integration-test/helpers/mocks.webkit.js +++ b/integration-test/helpers/mocks.webkit.js @@ -244,6 +244,7 @@ export function createWebkitMocks(platform = 'macos') { password_generation: true, credentials_saving: true, inlineIcon_credentials: true, + partial_form_saves: true, email: true, }, }, diff --git a/integration-test/tests/save-prompts.android.spec.js b/integration-test/tests/save-prompts.android.spec.js index 38095b556..3f109cb9b 100644 --- a/integration-test/tests/save-prompts.android.spec.js +++ b/integration-test/tests/save-prompts.android.spec.js @@ -56,6 +56,7 @@ test.describe('Android Save prompts', () => { androidStringReplacements({ featureToggles: { credentials_saving: true, + partial_form_saves: false, }, availableInputTypes: { credentials: { username: false, password: false }, @@ -81,11 +82,11 @@ test.describe('Android Save prompts', () => { await login.submitPasswordOnlyForm(credentials); await login.assertWasPromptedToSave(credentials); }); - test('with username only (should prompt)', async ({ page }) => { + test('with username only (should NOT prompt)', async ({ page }) => { const { login } = await setup(page); const credentials = { username: '123456' }; await login.submitUsernameOnlyForm(credentials.username); - await login.shouldPromptToSave(); + await login.shouldNotPromptToSave(); }); }); }); diff --git a/integration-test/tests/save-prompts.ios.spec.js b/integration-test/tests/save-prompts.ios.spec.js index ff0801b35..f25b7a847 100644 --- a/integration-test/tests/save-prompts.ios.spec.js +++ b/integration-test/tests/save-prompts.ios.spec.js @@ -105,7 +105,7 @@ test.describe('iOS Save prompts', () => { await login.assertWasPromptedToSave(credentials); }); - test('username only (should prompt)', async ({ page }) => { + test('username only (should NOT prompt)', async ({ page }) => { const login = await setup(page); const credentials = { username: '123456' }; diff --git a/integration-test/tests/save-prompts.windows.spec.js b/integration-test/tests/save-prompts.windows.spec.js index 3caccd19a..fdc3dd6c8 100644 --- a/integration-test/tests/save-prompts.windows.spec.js +++ b/integration-test/tests/save-prompts.windows.spec.js @@ -58,4 +58,31 @@ test.describe('Save prompts on windows', () => { await signup.assertWasNotPromptedToSaveWindows(); }); }); + + test.describe('When partial form saves are disabled', () => { + test('I should not be prompted to save for username only', async ({ page }) => { + // enable in-terminal exceptions + await forwardConsoleMessages(page); + + const { personalAddress } = constants.fields.email; + + const credentials = { + username: personalAddress, + }; + + const signup = signupPage(page); + await signup.navigate(); + + await createWindowsMocks() + .withFeatureToggles({ + partial_form_saves: false, + }) + .applyTo(page); + + await createAutofillScript().platform('windows').applyTo(page); + + await signup.enterCredentials(credentials); + await signup.assertWasNotPromptedToSaveWindows(); + }); + }); }); diff --git a/src/DeviceInterface/InterfacePrototype.js b/src/DeviceInterface/InterfacePrototype.js index 8b495fd06..d66205d04 100644 --- a/src/DeviceInterface/InterfacePrototype.js +++ b/src/DeviceInterface/InterfacePrototype.js @@ -785,17 +785,6 @@ class InterfacePrototype { } } - /** - * Checks if partial save can be triggered - * @param {DataStorageObject} values - * @returns {boolean} - */ - shouldTriggerPartialSave(values) { - // If credentials has only username field, and no password field, then trigger is a partialSave - const isUsernameOnly = Object.keys(values?.credentials || {}).length === 1 && Boolean(values?.credentials?.username); - return isUsernameOnly && Boolean(this.settings.featureToggles.partial_form_saves); - } - /** * `postSubmit` gives features a one-time-only opportunity to perform an * action directly after a form submission was observed. @@ -810,15 +799,22 @@ class InterfacePrototype { postSubmit(values, form) { if (!form.form) return; if (!form.hasValues(values)) return; - const shouldPartialSave = this.shouldTriggerPartialSave(values); - const checks = [form.shouldPromptToStoreData && !form.submitHandlerExecuted, this.passwordGenerator.generated, shouldPartialSave]; + const shouldTriggerPartialSave = + Object.keys(values?.credentials || {}).length === 1 && + Boolean(values?.credentials?.username) && + Boolean(this.settings.featureToggles.partial_form_saves); + const checks = [ + form.shouldPromptToStoreData && !form.submitHandlerExecuted, + this.passwordGenerator.generated, + shouldTriggerPartialSave, + ]; if (checks.some(Boolean)) { const formData = appendGeneratedKey(values, { password: this.passwordGenerator.password, username: this.emailProtection.lastGenerated, }); - const trigger = shouldPartialSave ? 'partialSave' : 'formSubmission'; + const trigger = shouldTriggerPartialSave ? 'partialSave' : 'formSubmission'; this.storeFormData(formData, trigger); } } diff --git a/src/Form/Form.js b/src/Form/Form.js index e5fcb46ad..d0b993806 100644 --- a/src/Form/Form.js +++ b/src/Form/Form.js @@ -240,7 +240,7 @@ class Form { */ getValuesReadyForStorage() { const formValues = this.getRawValues(); - return prepareFormValuesForStorage(formValues); + return prepareFormValuesForStorage(formValues, this.device.settings.featureToggles.partial_form_saves); } /** diff --git a/src/Form/Form.test.js b/src/Form/Form.test.js index bf69ff3e9..86e916019 100644 --- a/src/Form/Form.test.js +++ b/src/Form/Form.test.js @@ -84,8 +84,8 @@ describe('Test the form class reading values correctly', () => { `, - expHasValues: true, - expValues: { credentials: { username: 'testUsername' } }, + expHasValues: false, + expValues: { credentials: undefined }, }, { testCase: 'form where the password is <=3 characters long', @@ -95,8 +95,8 @@ describe('Test the form class reading values correctly', () => { `, - expHasValues: true, - expValues: { credentials: { username: 'testUsername' } }, + expHasValues: false, + expValues: { credentials: undefined }, }, { testCase: 'form with hidden email field', diff --git a/src/Form/formatters.js b/src/Form/formatters.js index 976145926..807fbf911 100644 --- a/src/Form/formatters.js +++ b/src/Form/formatters.js @@ -161,14 +161,14 @@ const getMMAndYYYYFromString = (expiration) => { }; /** - * @param {InternalDataStorageObject} credentials + * @param {InternalDataStorageObject} data * @return {boolean} */ const shouldStoreIdentities = ({ identities }) => Boolean((identities.firstName || identities.fullName) && identities.addressStreet && identities.addressCity); /** - * @param {InternalDataStorageObject} credentials + * @param {InternalDataStorageObject} data * @return {boolean} */ const shouldStoreCreditCards = ({ creditCards }) => { @@ -193,7 +193,7 @@ const formatPhoneNumber = (phone) => phone.replaceAll(/[^0-9|+]/g, ''); * @param {InternalDataStorageObject} formValues * @return {DataStorageObject} */ -const prepareFormValuesForStorage = (formValues) => { +const prepareFormValuesForStorage = (formValues, canTriggerPartialSave = false) => { /** @type {Partial} */ let { credentials, identities, creditCards } = formValues; @@ -203,13 +203,12 @@ const prepareFormValuesForStorage = (formValues) => { } /** Fixes for credentials */ - if (!credentials.username && hasUsernameLikeIdentity(identities)) { - // @ts-ignore - We know that username is not a useful value here - credentials.username = identities.emailAddress || identities.phone; - } - - // If we still don't have any credentials, we discard the object - if (Object.keys(credentials ?? {}).length === 0) { + if (canTriggerPartialSave || Boolean(formValues.credentials?.password)) { + if (credentials.password && !credentials.username && hasUsernameLikeIdentity(identities)) { + // @ts-ignore + credentials.username = identities.emailAddress || identities.phone; + } + } else { credentials = undefined; } diff --git a/src/Form/formatters.test.js b/src/Form/formatters.test.js index 0e12b5333..df3b1ed4d 100644 --- a/src/Form/formatters.test.js +++ b/src/Form/formatters.test.js @@ -61,7 +61,7 @@ describe('Can strip phone formatting characters', () => { describe('prepareFormValuesForStorage()', () => { describe('handling credentials', () => { - it('accepts for username only', () => { + it('rejects for username only', () => { const values = prepareFormValuesForStorage({ credentials: { username: 'dax@example.com' }, // @ts-ignore @@ -69,7 +69,7 @@ describe('prepareFormValuesForStorage()', () => { // @ts-ignore identities: {}, }); - expect(values.credentials?.username).toBe('dax@example.com'); + expect(values.credentials).toBeUndefined(); }); it('accepts password only', () => { const values = prepareFormValuesForStorage({ diff --git a/src/Settings.js b/src/Settings.js index b0e38b6b2..39ec6aa6b 100644 --- a/src/Settings.js +++ b/src/Settings.js @@ -330,6 +330,7 @@ export class Settings { inputType_creditCards: false, inlineIcon_credentials: false, unknown_username_categorization: false, + partial_form_saves: false, }, /** @type {AvailableInputTypes} */ availableInputTypes: { diff --git a/src/Settings.test.js b/src/Settings.test.js index 700adbd5a..9c36b1ace 100644 --- a/src/Settings.test.js +++ b/src/Settings.test.js @@ -146,6 +146,7 @@ describe('Settings', () => { "inputType_credentials": false, "inputType_creditCards": false, "inputType_identities": false, + "partial_form_saves": false, "password_generation": false, "unknown_username_categorization": false, } diff --git a/swift-package/Resources/assets/autofill-debug.js b/swift-package/Resources/assets/autofill-debug.js index 7cb05d454..3059aee77 100644 --- a/swift-package/Resources/assets/autofill-debug.js +++ b/swift-package/Resources/assets/autofill-debug.js @@ -9462,17 +9462,6 @@ class InterfacePrototype { } } - /** - * Checks if partial save can be triggered - * @param {DataStorageObject} values - * @returns {boolean} - */ - shouldTriggerPartialSave(values) { - // If credentials has only username field, and no password field, then trigger is a partialSave - const isUsernameOnly = Object.keys(values?.credentials || {}).length === 1 && Boolean(values?.credentials?.username); - return isUsernameOnly && Boolean(this.settings.featureToggles.partial_form_saves); - } - /** * `postSubmit` gives features a one-time-only opportunity to perform an * action directly after a form submission was observed. @@ -9487,14 +9476,14 @@ class InterfacePrototype { postSubmit(values, form) { if (!form.form) return; if (!form.hasValues(values)) return; - const shouldPartialSave = this.shouldTriggerPartialSave(values); - const checks = [form.shouldPromptToStoreData && !form.submitHandlerExecuted, this.passwordGenerator.generated, shouldPartialSave]; + const shouldTriggerPartialSave = Object.keys(values?.credentials || {}).length === 1 && Boolean(values?.credentials?.username) && Boolean(this.settings.featureToggles.partial_form_saves); + const checks = [form.shouldPromptToStoreData && !form.submitHandlerExecuted, this.passwordGenerator.generated, shouldTriggerPartialSave]; if (checks.some(Boolean)) { const formData = (0, _Credentials.appendGeneratedKey)(values, { password: this.passwordGenerator.password, username: this.emailProtection.lastGenerated }); - const trigger = shouldPartialSave ? 'partialSave' : 'formSubmission'; + const trigger = shouldTriggerPartialSave ? 'partialSave' : 'formSubmission'; this.storeFormData(formData, trigger); } } @@ -10275,7 +10264,7 @@ class Form { */ getValuesReadyForStorage() { const formValues = this.getRawValues(); - return (0, _formatters.prepareFormValuesForStorage)(formValues); + return (0, _formatters.prepareFormValuesForStorage)(formValues, this.device.settings.featureToggles.partial_form_saves); } /** @@ -12103,7 +12092,7 @@ const getMMAndYYYYFromString = expiration => { }; /** - * @param {InternalDataStorageObject} credentials + * @param {InternalDataStorageObject} data * @return {boolean} */ exports.getMMAndYYYYFromString = getMMAndYYYYFromString; @@ -12115,7 +12104,7 @@ const shouldStoreIdentities = _ref3 => { }; /** - * @param {InternalDataStorageObject} credentials + * @param {InternalDataStorageObject} data * @return {boolean} */ const shouldStoreCreditCards = _ref4 => { @@ -12144,7 +12133,8 @@ const formatPhoneNumber = phone => phone.replaceAll(/[^0-9|+]/g, ''); * @return {DataStorageObject} */ exports.formatPhoneNumber = formatPhoneNumber; -const prepareFormValuesForStorage = formValues => { +const prepareFormValuesForStorage = function (formValues) { + let canTriggerPartialSave = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; /** @type {Partial} */ let { credentials, @@ -12158,13 +12148,12 @@ const prepareFormValuesForStorage = formValues => { } /** Fixes for credentials */ - if (!credentials.username && (0, _autofillUtils.hasUsernameLikeIdentity)(identities)) { - // @ts-ignore - We know that username is not a useful value here - credentials.username = identities.emailAddress || identities.phone; - } - - // If we still don't have any credentials, we discard the object - if (Object.keys(credentials ?? {}).length === 0) { + if (canTriggerPartialSave || Boolean(formValues.credentials?.password)) { + if (credentials.password && !credentials.username && (0, _autofillUtils.hasUsernameLikeIdentity)(identities)) { + // @ts-ignore + credentials.username = identities.emailAddress || identities.phone; + } + } else { credentials = undefined; } @@ -15390,7 +15379,8 @@ class Settings { inputType_credentials: false, inputType_creditCards: false, inlineIcon_credentials: false, - unknown_username_categorization: false + unknown_username_categorization: false, + partial_form_saves: false }, /** @type {AvailableInputTypes} */ availableInputTypes: { diff --git a/swift-package/Resources/assets/autofill.js b/swift-package/Resources/assets/autofill.js index 01575b7f4..1b7d9af6d 100644 --- a/swift-package/Resources/assets/autofill.js +++ b/swift-package/Resources/assets/autofill.js @@ -5099,17 +5099,6 @@ class InterfacePrototype { } } - /** - * Checks if partial save can be triggered - * @param {DataStorageObject} values - * @returns {boolean} - */ - shouldTriggerPartialSave(values) { - // If credentials has only username field, and no password field, then trigger is a partialSave - const isUsernameOnly = Object.keys(values?.credentials || {}).length === 1 && Boolean(values?.credentials?.username); - return isUsernameOnly && Boolean(this.settings.featureToggles.partial_form_saves); - } - /** * `postSubmit` gives features a one-time-only opportunity to perform an * action directly after a form submission was observed. @@ -5124,14 +5113,14 @@ class InterfacePrototype { postSubmit(values, form) { if (!form.form) return; if (!form.hasValues(values)) return; - const shouldPartialSave = this.shouldTriggerPartialSave(values); - const checks = [form.shouldPromptToStoreData && !form.submitHandlerExecuted, this.passwordGenerator.generated, shouldPartialSave]; + const shouldTriggerPartialSave = Object.keys(values?.credentials || {}).length === 1 && Boolean(values?.credentials?.username) && Boolean(this.settings.featureToggles.partial_form_saves); + const checks = [form.shouldPromptToStoreData && !form.submitHandlerExecuted, this.passwordGenerator.generated, shouldTriggerPartialSave]; if (checks.some(Boolean)) { const formData = (0, _Credentials.appendGeneratedKey)(values, { password: this.passwordGenerator.password, username: this.emailProtection.lastGenerated }); - const trigger = shouldPartialSave ? 'partialSave' : 'formSubmission'; + const trigger = shouldTriggerPartialSave ? 'partialSave' : 'formSubmission'; this.storeFormData(formData, trigger); } } @@ -5912,7 +5901,7 @@ class Form { */ getValuesReadyForStorage() { const formValues = this.getRawValues(); - return (0, _formatters.prepareFormValuesForStorage)(formValues); + return (0, _formatters.prepareFormValuesForStorage)(formValues, this.device.settings.featureToggles.partial_form_saves); } /** @@ -7740,7 +7729,7 @@ const getMMAndYYYYFromString = expiration => { }; /** - * @param {InternalDataStorageObject} credentials + * @param {InternalDataStorageObject} data * @return {boolean} */ exports.getMMAndYYYYFromString = getMMAndYYYYFromString; @@ -7752,7 +7741,7 @@ const shouldStoreIdentities = _ref3 => { }; /** - * @param {InternalDataStorageObject} credentials + * @param {InternalDataStorageObject} data * @return {boolean} */ const shouldStoreCreditCards = _ref4 => { @@ -7781,7 +7770,8 @@ const formatPhoneNumber = phone => phone.replaceAll(/[^0-9|+]/g, ''); * @return {DataStorageObject} */ exports.formatPhoneNumber = formatPhoneNumber; -const prepareFormValuesForStorage = formValues => { +const prepareFormValuesForStorage = function (formValues) { + let canTriggerPartialSave = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; /** @type {Partial} */ let { credentials, @@ -7795,13 +7785,12 @@ const prepareFormValuesForStorage = formValues => { } /** Fixes for credentials */ - if (!credentials.username && (0, _autofillUtils.hasUsernameLikeIdentity)(identities)) { - // @ts-ignore - We know that username is not a useful value here - credentials.username = identities.emailAddress || identities.phone; - } - - // If we still don't have any credentials, we discard the object - if (Object.keys(credentials ?? {}).length === 0) { + if (canTriggerPartialSave || Boolean(formValues.credentials?.password)) { + if (credentials.password && !credentials.username && (0, _autofillUtils.hasUsernameLikeIdentity)(identities)) { + // @ts-ignore + credentials.username = identities.emailAddress || identities.phone; + } + } else { credentials = undefined; } @@ -11027,7 +11016,8 @@ class Settings { inputType_credentials: false, inputType_creditCards: false, inlineIcon_credentials: false, - unknown_username_categorization: false + unknown_username_categorization: false, + partial_form_saves: false }, /** @type {AvailableInputTypes} */ availableInputTypes: {