From 34d8a056e4e3f9117e5102aab7d673870493bf23 Mon Sep 17 00:00:00 2001 From: Emanuele Feliziani Date: Tue, 10 Oct 2023 15:56:54 +0200 Subject: [PATCH 1/2] Revert mutation observer for forms Signed-off-by: Emanuele Feliziani --- dist/autofill-debug.js | 44 ++++--------------- dist/autofill.js | 44 ++++--------------- .../tests/mutating-form.macos.spec.js | 2 +- src/Form/Form.js | 34 -------------- src/Scanner.js | 9 +++- .../Resources/assets/autofill-debug.js | 44 ++++--------------- swift-package/Resources/assets/autofill.js | 44 ++++--------------- 7 files changed, 40 insertions(+), 181 deletions(-) diff --git a/dist/autofill-debug.js b/dist/autofill-debug.js index 21a055277..0ec178685 100644 --- a/dist/autofill-debug.js +++ b/dist/autofill-debug.js @@ -9411,7 +9411,6 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de const { ATTR_AUTOFILL, ATTR_INPUT_TYPE, - MAX_FORM_MUT_OBS_COUNT, MAX_INPUTS_PER_FORM } = _constants.constants; class Form { @@ -9460,29 +9459,6 @@ class Form { if (!entry.isIntersecting) this.removeTooltip(); } }); - this.mutObsCount = 0; - this.mutObsConfig = { - childList: true, - subtree: true - }; - this.mutObs = new MutationObserver(records => { - const anythingRemoved = records.some(record => record.removedNodes.length > 0); - if (anythingRemoved) { - // Must check for inputs because a parent may be removed and not show up in record.removedNodes - if ([...this.inputs.all].some(input => !input.isConnected)) { - // If any known input has been removed from the DOM, reanalyze the whole form - window.requestIdleCallback(() => { - this.formAnalyzer = new _FormAnalyzer.default(this.form, input, this.matching); - this.recategorizeAllInputs(); - }); - this.mutObsCount++; - // If the form mutates too much, disconnect to avoid performance issues - if (this.mutObsCount >= MAX_FORM_MUT_OBS_COUNT) { - this.mutObs.disconnect(); - } - } - } - }); // This ensures we fire the handler again if the form is changed this.addListener(form, 'input', () => { @@ -9492,7 +9468,6 @@ class Form { } }); this.categorizeInputs(); - this.mutObs.observe(this.form, this.mutObsConfig); this.logFormInfo(); if (shouldAutoprompt) { this.promptLoginIfNeeded(); @@ -9723,7 +9698,6 @@ class Form { this.removeAllDecorations(); this.removeTooltip(); this.forgetAllInputs(); - this.mutObs.disconnect(); this.matching.clear(); this.intObs = null; } @@ -9805,13 +9779,6 @@ class Form { return this; } - // When new inputs are added after the initial scan, reanalyze the whole form - if (this.initialScanComplete) { - this.formAnalyzer = new _FormAnalyzer.default(this.form, input, this.matching); - this.recategorizeAllInputs(); - return this; - } - // Nothing to do with 1-character fields if (input.maxLength === 1) return this; this.inputs.all.add(input); @@ -13868,9 +13835,14 @@ class DefaultScanner { addInput(input) { if (this.stopped) return; const parentForm = this.getParentForm(input); - if (parentForm instanceof HTMLFormElement && this.forms.has(parentForm)) { - // We've met the form, add the input - this.forms.get(parentForm)?.addInput(input); + if (parentForm && parentForm instanceof HTMLFormElement && this.forms.has(parentForm)) { + const foundForm = this.forms.get(parentForm); + // We've met the form, add the input provided it's below the max input limit + if (foundForm && foundForm.inputs.all.size < MAX_INPUTS_PER_FORM) { + foundForm.addInput(input); + } else { + this.stopScanner('The form has too many inputs, destroying.'); + } return; } diff --git a/dist/autofill.js b/dist/autofill.js index 6bc8e4009..81ff54670 100644 --- a/dist/autofill.js +++ b/dist/autofill.js @@ -5466,7 +5466,6 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de const { ATTR_AUTOFILL, ATTR_INPUT_TYPE, - MAX_FORM_MUT_OBS_COUNT, MAX_INPUTS_PER_FORM } = _constants.constants; class Form { @@ -5515,29 +5514,6 @@ class Form { if (!entry.isIntersecting) this.removeTooltip(); } }); - this.mutObsCount = 0; - this.mutObsConfig = { - childList: true, - subtree: true - }; - this.mutObs = new MutationObserver(records => { - const anythingRemoved = records.some(record => record.removedNodes.length > 0); - if (anythingRemoved) { - // Must check for inputs because a parent may be removed and not show up in record.removedNodes - if ([...this.inputs.all].some(input => !input.isConnected)) { - // If any known input has been removed from the DOM, reanalyze the whole form - window.requestIdleCallback(() => { - this.formAnalyzer = new _FormAnalyzer.default(this.form, input, this.matching); - this.recategorizeAllInputs(); - }); - this.mutObsCount++; - // If the form mutates too much, disconnect to avoid performance issues - if (this.mutObsCount >= MAX_FORM_MUT_OBS_COUNT) { - this.mutObs.disconnect(); - } - } - } - }); // This ensures we fire the handler again if the form is changed this.addListener(form, 'input', () => { @@ -5547,7 +5523,6 @@ class Form { } }); this.categorizeInputs(); - this.mutObs.observe(this.form, this.mutObsConfig); this.logFormInfo(); if (shouldAutoprompt) { this.promptLoginIfNeeded(); @@ -5778,7 +5753,6 @@ class Form { this.removeAllDecorations(); this.removeTooltip(); this.forgetAllInputs(); - this.mutObs.disconnect(); this.matching.clear(); this.intObs = null; } @@ -5860,13 +5834,6 @@ class Form { return this; } - // When new inputs are added after the initial scan, reanalyze the whole form - if (this.initialScanComplete) { - this.formAnalyzer = new _FormAnalyzer.default(this.form, input, this.matching); - this.recategorizeAllInputs(); - return this; - } - // Nothing to do with 1-character fields if (input.maxLength === 1) return this; this.inputs.all.add(input); @@ -9923,9 +9890,14 @@ class DefaultScanner { addInput(input) { if (this.stopped) return; const parentForm = this.getParentForm(input); - if (parentForm instanceof HTMLFormElement && this.forms.has(parentForm)) { - // We've met the form, add the input - this.forms.get(parentForm)?.addInput(input); + if (parentForm && parentForm instanceof HTMLFormElement && this.forms.has(parentForm)) { + const foundForm = this.forms.get(parentForm); + // We've met the form, add the input provided it's below the max input limit + if (foundForm && foundForm.inputs.all.size < MAX_INPUTS_PER_FORM) { + foundForm.addInput(input); + } else { + this.stopScanner('The form has too many inputs, destroying.'); + } return; } diff --git a/integration-test/tests/mutating-form.macos.spec.js b/integration-test/tests/mutating-form.macos.spec.js index 3451cdd37..6dc29ad84 100644 --- a/integration-test/tests/mutating-form.macos.spec.js +++ b/integration-test/tests/mutating-form.macos.spec.js @@ -8,7 +8,7 @@ import {test as base} from '@playwright/test' */ const test = base.extend({}) -test.describe('Mutating form page', () => { +test.describe.skip('Mutating form page', () => { async function applyScript (page) { await createAutofillScript() .replaceAll(macosContentScopeReplacements()) diff --git a/src/Form/Form.js b/src/Form/Form.js index 13a352cb7..7b22a877b 100644 --- a/src/Form/Form.js +++ b/src/Form/Form.js @@ -26,7 +26,6 @@ import {constants} from '../constants.js' const { ATTR_AUTOFILL, ATTR_INPUT_TYPE, - MAX_FORM_MUT_OBS_COUNT, MAX_INPUTS_PER_FORM } = constants @@ -77,30 +76,6 @@ class Form { } }) - this.mutObsCount = 0 - this.mutObsConfig = { childList: true, subtree: true } - this.mutObs = new MutationObserver( - (records) => { - const anythingRemoved = records.some(record => record.removedNodes.length > 0) - if (anythingRemoved) { - // Must check for inputs because a parent may be removed and not show up in record.removedNodes - if ([...this.inputs.all].some(input => !input.isConnected)) { - // If any known input has been removed from the DOM, reanalyze the whole form - window.requestIdleCallback(() => { - this.formAnalyzer = new FormAnalyzer(this.form, input, this.matching) - this.recategorizeAllInputs() - }) - - this.mutObsCount++ - // If the form mutates too much, disconnect to avoid performance issues - if (this.mutObsCount >= MAX_FORM_MUT_OBS_COUNT) { - this.mutObs.disconnect() - } - } - } - } - ) - // This ensures we fire the handler again if the form is changed this.addListener(form, 'input', () => { if (!this.isAutofilling) { @@ -110,7 +85,6 @@ class Form { }) this.categorizeInputs() - this.mutObs.observe(this.form, this.mutObsConfig) this.logFormInfo() @@ -356,7 +330,6 @@ class Form { this.removeAllDecorations() this.removeTooltip() this.forgetAllInputs() - this.mutObs.disconnect() this.matching.clear() this.intObs = null } @@ -444,13 +417,6 @@ class Form { return this } - // When new inputs are added after the initial scan, reanalyze the whole form - if (this.initialScanComplete) { - this.formAnalyzer = new FormAnalyzer(this.form, input, this.matching) - this.recategorizeAllInputs() - return this - } - // Nothing to do with 1-character fields if (input.maxLength === 1) return this diff --git a/src/Scanner.js b/src/Scanner.js index 989e14ebf..8457e0b55 100644 --- a/src/Scanner.js +++ b/src/Scanner.js @@ -226,8 +226,13 @@ class DefaultScanner { const parentForm = this.getParentForm(input) if (parentForm instanceof HTMLFormElement && this.forms.has(parentForm)) { - // We've met the form, add the input - this.forms.get(parentForm)?.addInput(input) + const foundForm = this.forms.get(parentForm) + // We've met the form, add the input provided it's below the max input limit + if (foundForm && foundForm.inputs.all.size < MAX_INPUTS_PER_FORM) { + foundForm.addInput(input) + } else { + this.stopScanner('The form has too many inputs, destroying.') + } return } diff --git a/swift-package/Resources/assets/autofill-debug.js b/swift-package/Resources/assets/autofill-debug.js index 21a055277..0ec178685 100644 --- a/swift-package/Resources/assets/autofill-debug.js +++ b/swift-package/Resources/assets/autofill-debug.js @@ -9411,7 +9411,6 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de const { ATTR_AUTOFILL, ATTR_INPUT_TYPE, - MAX_FORM_MUT_OBS_COUNT, MAX_INPUTS_PER_FORM } = _constants.constants; class Form { @@ -9460,29 +9459,6 @@ class Form { if (!entry.isIntersecting) this.removeTooltip(); } }); - this.mutObsCount = 0; - this.mutObsConfig = { - childList: true, - subtree: true - }; - this.mutObs = new MutationObserver(records => { - const anythingRemoved = records.some(record => record.removedNodes.length > 0); - if (anythingRemoved) { - // Must check for inputs because a parent may be removed and not show up in record.removedNodes - if ([...this.inputs.all].some(input => !input.isConnected)) { - // If any known input has been removed from the DOM, reanalyze the whole form - window.requestIdleCallback(() => { - this.formAnalyzer = new _FormAnalyzer.default(this.form, input, this.matching); - this.recategorizeAllInputs(); - }); - this.mutObsCount++; - // If the form mutates too much, disconnect to avoid performance issues - if (this.mutObsCount >= MAX_FORM_MUT_OBS_COUNT) { - this.mutObs.disconnect(); - } - } - } - }); // This ensures we fire the handler again if the form is changed this.addListener(form, 'input', () => { @@ -9492,7 +9468,6 @@ class Form { } }); this.categorizeInputs(); - this.mutObs.observe(this.form, this.mutObsConfig); this.logFormInfo(); if (shouldAutoprompt) { this.promptLoginIfNeeded(); @@ -9723,7 +9698,6 @@ class Form { this.removeAllDecorations(); this.removeTooltip(); this.forgetAllInputs(); - this.mutObs.disconnect(); this.matching.clear(); this.intObs = null; } @@ -9805,13 +9779,6 @@ class Form { return this; } - // When new inputs are added after the initial scan, reanalyze the whole form - if (this.initialScanComplete) { - this.formAnalyzer = new _FormAnalyzer.default(this.form, input, this.matching); - this.recategorizeAllInputs(); - return this; - } - // Nothing to do with 1-character fields if (input.maxLength === 1) return this; this.inputs.all.add(input); @@ -13868,9 +13835,14 @@ class DefaultScanner { addInput(input) { if (this.stopped) return; const parentForm = this.getParentForm(input); - if (parentForm instanceof HTMLFormElement && this.forms.has(parentForm)) { - // We've met the form, add the input - this.forms.get(parentForm)?.addInput(input); + if (parentForm && parentForm instanceof HTMLFormElement && this.forms.has(parentForm)) { + const foundForm = this.forms.get(parentForm); + // We've met the form, add the input provided it's below the max input limit + if (foundForm && foundForm.inputs.all.size < MAX_INPUTS_PER_FORM) { + foundForm.addInput(input); + } else { + this.stopScanner('The form has too many inputs, destroying.'); + } return; } diff --git a/swift-package/Resources/assets/autofill.js b/swift-package/Resources/assets/autofill.js index 6bc8e4009..81ff54670 100644 --- a/swift-package/Resources/assets/autofill.js +++ b/swift-package/Resources/assets/autofill.js @@ -5466,7 +5466,6 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de const { ATTR_AUTOFILL, ATTR_INPUT_TYPE, - MAX_FORM_MUT_OBS_COUNT, MAX_INPUTS_PER_FORM } = _constants.constants; class Form { @@ -5515,29 +5514,6 @@ class Form { if (!entry.isIntersecting) this.removeTooltip(); } }); - this.mutObsCount = 0; - this.mutObsConfig = { - childList: true, - subtree: true - }; - this.mutObs = new MutationObserver(records => { - const anythingRemoved = records.some(record => record.removedNodes.length > 0); - if (anythingRemoved) { - // Must check for inputs because a parent may be removed and not show up in record.removedNodes - if ([...this.inputs.all].some(input => !input.isConnected)) { - // If any known input has been removed from the DOM, reanalyze the whole form - window.requestIdleCallback(() => { - this.formAnalyzer = new _FormAnalyzer.default(this.form, input, this.matching); - this.recategorizeAllInputs(); - }); - this.mutObsCount++; - // If the form mutates too much, disconnect to avoid performance issues - if (this.mutObsCount >= MAX_FORM_MUT_OBS_COUNT) { - this.mutObs.disconnect(); - } - } - } - }); // This ensures we fire the handler again if the form is changed this.addListener(form, 'input', () => { @@ -5547,7 +5523,6 @@ class Form { } }); this.categorizeInputs(); - this.mutObs.observe(this.form, this.mutObsConfig); this.logFormInfo(); if (shouldAutoprompt) { this.promptLoginIfNeeded(); @@ -5778,7 +5753,6 @@ class Form { this.removeAllDecorations(); this.removeTooltip(); this.forgetAllInputs(); - this.mutObs.disconnect(); this.matching.clear(); this.intObs = null; } @@ -5860,13 +5834,6 @@ class Form { return this; } - // When new inputs are added after the initial scan, reanalyze the whole form - if (this.initialScanComplete) { - this.formAnalyzer = new _FormAnalyzer.default(this.form, input, this.matching); - this.recategorizeAllInputs(); - return this; - } - // Nothing to do with 1-character fields if (input.maxLength === 1) return this; this.inputs.all.add(input); @@ -9923,9 +9890,14 @@ class DefaultScanner { addInput(input) { if (this.stopped) return; const parentForm = this.getParentForm(input); - if (parentForm instanceof HTMLFormElement && this.forms.has(parentForm)) { - // We've met the form, add the input - this.forms.get(parentForm)?.addInput(input); + if (parentForm && parentForm instanceof HTMLFormElement && this.forms.has(parentForm)) { + const foundForm = this.forms.get(parentForm); + // We've met the form, add the input provided it's below the max input limit + if (foundForm && foundForm.inputs.all.size < MAX_INPUTS_PER_FORM) { + foundForm.addInput(input); + } else { + this.stopScanner('The form has too many inputs, destroying.'); + } return; } From 63c8a1b7a2b628d4c0e3864dd8e4b6e78ec1d1a6 Mon Sep 17 00:00:00 2001 From: Emanuele Feliziani Date: Tue, 10 Oct 2023 16:13:39 +0200 Subject: [PATCH 2/2] Commit compiled files Signed-off-by: Emanuele Feliziani --- dist/autofill-debug.js | 2 +- dist/autofill.js | 2 +- swift-package/Resources/assets/autofill-debug.js | 2 +- swift-package/Resources/assets/autofill.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dist/autofill-debug.js b/dist/autofill-debug.js index 0ec178685..5ff88f0a3 100644 --- a/dist/autofill-debug.js +++ b/dist/autofill-debug.js @@ -13835,7 +13835,7 @@ class DefaultScanner { addInput(input) { if (this.stopped) return; const parentForm = this.getParentForm(input); - if (parentForm && parentForm instanceof HTMLFormElement && this.forms.has(parentForm)) { + if (parentForm instanceof HTMLFormElement && this.forms.has(parentForm)) { const foundForm = this.forms.get(parentForm); // We've met the form, add the input provided it's below the max input limit if (foundForm && foundForm.inputs.all.size < MAX_INPUTS_PER_FORM) { diff --git a/dist/autofill.js b/dist/autofill.js index 81ff54670..e9bd2867c 100644 --- a/dist/autofill.js +++ b/dist/autofill.js @@ -9890,7 +9890,7 @@ class DefaultScanner { addInput(input) { if (this.stopped) return; const parentForm = this.getParentForm(input); - if (parentForm && parentForm instanceof HTMLFormElement && this.forms.has(parentForm)) { + if (parentForm instanceof HTMLFormElement && this.forms.has(parentForm)) { const foundForm = this.forms.get(parentForm); // We've met the form, add the input provided it's below the max input limit if (foundForm && foundForm.inputs.all.size < MAX_INPUTS_PER_FORM) { diff --git a/swift-package/Resources/assets/autofill-debug.js b/swift-package/Resources/assets/autofill-debug.js index 0ec178685..5ff88f0a3 100644 --- a/swift-package/Resources/assets/autofill-debug.js +++ b/swift-package/Resources/assets/autofill-debug.js @@ -13835,7 +13835,7 @@ class DefaultScanner { addInput(input) { if (this.stopped) return; const parentForm = this.getParentForm(input); - if (parentForm && parentForm instanceof HTMLFormElement && this.forms.has(parentForm)) { + if (parentForm instanceof HTMLFormElement && this.forms.has(parentForm)) { const foundForm = this.forms.get(parentForm); // We've met the form, add the input provided it's below the max input limit if (foundForm && foundForm.inputs.all.size < MAX_INPUTS_PER_FORM) { diff --git a/swift-package/Resources/assets/autofill.js b/swift-package/Resources/assets/autofill.js index 81ff54670..e9bd2867c 100644 --- a/swift-package/Resources/assets/autofill.js +++ b/swift-package/Resources/assets/autofill.js @@ -9890,7 +9890,7 @@ class DefaultScanner { addInput(input) { if (this.stopped) return; const parentForm = this.getParentForm(input); - if (parentForm && parentForm instanceof HTMLFormElement && this.forms.has(parentForm)) { + if (parentForm instanceof HTMLFormElement && this.forms.has(parentForm)) { const foundForm = this.forms.get(parentForm); // We've met the form, add the input provided it's below the max input limit if (foundForm && foundForm.inputs.all.size < MAX_INPUTS_PER_FORM) {