From 951e55ea7653e68836420dee99afb88bc8585200 Mon Sep 17 00:00:00 2001 From: Shane Osbourne Date: Sun, 21 Jul 2024 10:27:30 +0100 Subject: [PATCH 1/5] fix: re-enable ESM only scanner debugging. --- dist/autofill-debug.js | 16 ++-- dist/autofill.js | 16 ++-- src/DeviceInterface/InterfacePrototype.js | 6 +- src/Form/Form.js | 2 +- src/Scanner.debug.js | 86 +++++++++++++++++-- src/Scanner.js | 5 -- src/scanner-debug.html | 36 ++++++-- .../Resources/assets/autofill-debug.js | 16 ++-- swift-package/Resources/assets/autofill.js | 16 ++-- 9 files changed, 142 insertions(+), 57 deletions(-) diff --git a/dist/autofill-debug.js b/dist/autofill-debug.js index 426f640fa..1924b9695 100644 --- a/dist/autofill-debug.js +++ b/dist/autofill-debug.js @@ -8650,6 +8650,11 @@ class InterfacePrototype { } postInit() { const cleanup = this.scanner.init(); + if (this.globalConfig.isExtension) { + this.deviceApi.notify(new _deviceApiCalls.AddDebugFlagCall({ + flag: 'autofill' + })); + } this.addLogoutListener(() => { cleanup('Logged out'); if (this.globalConfig.isDDGDomain) { @@ -9754,7 +9759,7 @@ class Form { this.device = deviceInterface; this.hasShadowTree = hasShadowTree; - /** @type Record<'all' | SupportedMainTypes, Set> */ + /** @type {Record<'all' | SupportedMainTypes, Set>} */ this.inputs = { all: new Set(), credentials: new Set(), @@ -14190,7 +14195,6 @@ var _Form = require("./Form/Form.js"); var _constants = require("./constants.js"); var _matching = require("./Form/matching.js"); var _autofillUtils = require("./autofill-utils.js"); -var _deviceApiCalls = require("./deviceApiCalls/__generated__/deviceApiCalls.js"); const { MAX_INPUTS_PER_PAGE, MAX_FORMS_PER_PAGE, @@ -14289,12 +14293,6 @@ class DefaultScanner { */ init() { var _this = this; - if (this.device.globalConfig.isExtension) { - this.device.deviceApi.notify(new _deviceApiCalls.AddDebugFlagCall({ - flag: 'autofill' - })); - } - // Add the shadow DOM listener. Handlers in handleEvent window.addEventListener('pointerdown', this, true); // We don't listen for focus events on mobile, they can cause keyboard flashing @@ -14627,7 +14625,7 @@ function createScanner(device, scannerOptions) { }); } -},{"./Form/Form.js":33,"./Form/matching.js":43,"./autofill-utils.js":62,"./constants.js":65,"./deviceApiCalls/__generated__/deviceApiCalls.js":66}],50:[function(require,module,exports){ +},{"./Form/Form.js":33,"./Form/matching.js":43,"./autofill-utils.js":62,"./constants.js":65}],50:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { diff --git a/dist/autofill.js b/dist/autofill.js index 0604e2738..a3c13b871 100644 --- a/dist/autofill.js +++ b/dist/autofill.js @@ -4484,6 +4484,11 @@ class InterfacePrototype { } postInit() { const cleanup = this.scanner.init(); + if (this.globalConfig.isExtension) { + this.deviceApi.notify(new _deviceApiCalls.AddDebugFlagCall({ + flag: 'autofill' + })); + } this.addLogoutListener(() => { cleanup('Logged out'); if (this.globalConfig.isDDGDomain) { @@ -5588,7 +5593,7 @@ class Form { this.device = deviceInterface; this.hasShadowTree = hasShadowTree; - /** @type Record<'all' | SupportedMainTypes, Set> */ + /** @type {Record<'all' | SupportedMainTypes, Set>} */ this.inputs = { all: new Set(), credentials: new Set(), @@ -10024,7 +10029,6 @@ var _Form = require("./Form/Form.js"); var _constants = require("./constants.js"); var _matching = require("./Form/matching.js"); var _autofillUtils = require("./autofill-utils.js"); -var _deviceApiCalls = require("./deviceApiCalls/__generated__/deviceApiCalls.js"); const { MAX_INPUTS_PER_PAGE, MAX_FORMS_PER_PAGE, @@ -10123,12 +10127,6 @@ class DefaultScanner { */ init() { var _this = this; - if (this.device.globalConfig.isExtension) { - this.device.deviceApi.notify(new _deviceApiCalls.AddDebugFlagCall({ - flag: 'autofill' - })); - } - // Add the shadow DOM listener. Handlers in handleEvent window.addEventListener('pointerdown', this, true); // We don't listen for focus events on mobile, they can cause keyboard flashing @@ -10461,7 +10459,7 @@ function createScanner(device, scannerOptions) { }); } -},{"./Form/Form.js":23,"./Form/matching.js":33,"./autofill-utils.js":52,"./constants.js":55,"./deviceApiCalls/__generated__/deviceApiCalls.js":56}],40:[function(require,module,exports){ +},{"./Form/Form.js":23,"./Form/matching.js":33,"./autofill-utils.js":52,"./constants.js":55}],40:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { diff --git a/src/DeviceInterface/InterfacePrototype.js b/src/DeviceInterface/InterfacePrototype.js index 003940986..e40e80117 100644 --- a/src/DeviceInterface/InterfacePrototype.js +++ b/src/DeviceInterface/InterfacePrototype.js @@ -19,7 +19,8 @@ import {DeviceApi} from '../../packages/device-api/index.js' import { GetAutofillCredentialsCall, StoreFormDataCall, - SendJSPixelCall + SendJSPixelCall, + AddDebugFlagCall } from '../deviceApiCalls/__generated__/deviceApiCalls.js' import {initFormSubmissionsApi} from './initFormSubmissionsApi.js' import {EmailProtection} from '../EmailProtection.js' @@ -313,6 +314,9 @@ class InterfacePrototype { postInit () { const cleanup = this.scanner.init() + if (this.globalConfig.isExtension) { + this.deviceApi.notify(new AddDebugFlagCall({ flag: 'autofill' })) + } this.addLogoutListener(() => { cleanup('Logged out') if (this.globalConfig.isDDGDomain) { diff --git a/src/Form/Form.js b/src/Form/Form.js index 95f0c7516..962507543 100644 --- a/src/Form/Form.js +++ b/src/Form/Form.js @@ -61,7 +61,7 @@ class Form { this.device = deviceInterface this.hasShadowTree = hasShadowTree - /** @type Record<'all' | SupportedMainTypes, Set> */ + /** @type {Record<'all' | SupportedMainTypes, Set>} */ this.inputs = { all: new Set(), credentials: new Set(), diff --git a/src/Scanner.debug.js b/src/Scanner.debug.js index 1b861ab4f..04fef4459 100644 --- a/src/Scanner.debug.js +++ b/src/Scanner.debug.js @@ -1,7 +1,5 @@ import { createScanner } from './Scanner.js' -sessionStorage.setItem('ddg-autofill-debug', 'true') - /** * Scanner.debug.js * @@ -21,6 +19,12 @@ const mockInterface = { inputType_credentials: true, inputType_identities: true, inputType_creditCards: true + }, + populateDataIfNeeded: () => { + // console.log('populateDataIfNeeded'); + }, + canAutofillType: () => { + return true } }, globalConfig: { @@ -34,12 +38,80 @@ const mockInterface = { }, attachTooltip (...args) { console.log('device.attachTooltip', args) + }, + isTooltipActive: () => { + return false + }, + get scanner() { + return state.scanner } } -// @ts-ignore -const scanner = createScanner(mockInterface, { - initialDelay: 1 // allow debugging directly on macOS - if this was 0 then it would try to use requestIdleCallback, which is absent in WebKit -}) +let state = { + /** @type {import('./Scanner.js').Scanner | undefined} */ + scanner: undefined, + /** @type {HTMLSelectElement|null} */ + list: document.querySelector('select[name="html-list"]'), +} + +const url = new URL(window.location.href); +const initial = url.searchParams.get('form'); +const log = url.searchParams.has('log'); -scanner.init() +if (log) { + sessionStorage.setItem('ddg-autofill-debug', 'true') +} else { + sessionStorage.setItem('ddg-autofill-debug', 'false') +} + +loadList() + .then(() => { + if (initial) { + loadNewForm(initial).catch(console.error) + } + }) + + +async function loadList() { + const url = new URL(`/test-forms/index.json`, window.location.href); + fetch(url) + .then(response => response.json()) + .then(x => { + x.forEach(item => { + const option = document.createElement('option'); + option.value = item.html; + option.textContent = item.html; + state.list?.appendChild(option); + }); + }) +} +async function loadNewForm(filename) { + const url = new URL(`/test-forms/${filename}`, window.location.href); + fetch(url) + .then(response => response.text()) + .then(html => { + let mainElement = document.querySelector('main'); + if (mainElement) { + mainElement.innerHTML = html; + if (state.list) { + state.list.value = filename + } + state.scanner = createScanner(/** @type {any} */(mockInterface), { + initialDelay: 1 // allow debugging directly on macOS - if this was 0 then it would try to use requestIdleCallback, which is absent in WebKit + }) + state.scanner?.init(); + } else { + console.log("'main' element not found on the page."); + } + }) + .catch(error => { + console.error('Error:', error); + }); +} + +document.querySelector('select[name="html-list"]')?.addEventListener('change', (e) => { + const elem = /** @type {HTMLSelectElement} */(e.target); + const next = new URL(window.location.href); + next.searchParams.set('form', elem.value); + window.location.href = next.href; +}) diff --git a/src/Scanner.js b/src/Scanner.js index 5a1fcd541..1e1df5ba8 100644 --- a/src/Scanner.js +++ b/src/Scanner.js @@ -7,7 +7,6 @@ import { shouldLog, pierceShadowTree, findEnclosedElements } from './autofill-utils.js' -import { AddDebugFlagCall } from './deviceApiCalls/__generated__/deviceApiCalls.js' const { MAX_INPUTS_PER_PAGE, @@ -106,10 +105,6 @@ class DefaultScanner { * @returns {(reason: string, ...rest) => void} */ init () { - if (this.device.globalConfig.isExtension) { - this.device.deviceApi.notify(new AddDebugFlagCall({ flag: 'autofill' })) - } - // Add the shadow DOM listener. Handlers in handleEvent window.addEventListener('pointerdown', this, true) // We don't listen for focus events on mobile, they can cause keyboard flashing diff --git a/src/scanner-debug.html b/src/scanner-debug.html index 7d0e1f26c..105970b7a 100644 --- a/src/scanner-debug.html +++ b/src/scanner-debug.html @@ -6,15 +6,37 @@ content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> Scanner Test + -
-
- - - -
-
+ +
+ +
+
diff --git a/swift-package/Resources/assets/autofill-debug.js b/swift-package/Resources/assets/autofill-debug.js index 426f640fa..1924b9695 100644 --- a/swift-package/Resources/assets/autofill-debug.js +++ b/swift-package/Resources/assets/autofill-debug.js @@ -8650,6 +8650,11 @@ class InterfacePrototype { } postInit() { const cleanup = this.scanner.init(); + if (this.globalConfig.isExtension) { + this.deviceApi.notify(new _deviceApiCalls.AddDebugFlagCall({ + flag: 'autofill' + })); + } this.addLogoutListener(() => { cleanup('Logged out'); if (this.globalConfig.isDDGDomain) { @@ -9754,7 +9759,7 @@ class Form { this.device = deviceInterface; this.hasShadowTree = hasShadowTree; - /** @type Record<'all' | SupportedMainTypes, Set> */ + /** @type {Record<'all' | SupportedMainTypes, Set>} */ this.inputs = { all: new Set(), credentials: new Set(), @@ -14190,7 +14195,6 @@ var _Form = require("./Form/Form.js"); var _constants = require("./constants.js"); var _matching = require("./Form/matching.js"); var _autofillUtils = require("./autofill-utils.js"); -var _deviceApiCalls = require("./deviceApiCalls/__generated__/deviceApiCalls.js"); const { MAX_INPUTS_PER_PAGE, MAX_FORMS_PER_PAGE, @@ -14289,12 +14293,6 @@ class DefaultScanner { */ init() { var _this = this; - if (this.device.globalConfig.isExtension) { - this.device.deviceApi.notify(new _deviceApiCalls.AddDebugFlagCall({ - flag: 'autofill' - })); - } - // Add the shadow DOM listener. Handlers in handleEvent window.addEventListener('pointerdown', this, true); // We don't listen for focus events on mobile, they can cause keyboard flashing @@ -14627,7 +14625,7 @@ function createScanner(device, scannerOptions) { }); } -},{"./Form/Form.js":33,"./Form/matching.js":43,"./autofill-utils.js":62,"./constants.js":65,"./deviceApiCalls/__generated__/deviceApiCalls.js":66}],50:[function(require,module,exports){ +},{"./Form/Form.js":33,"./Form/matching.js":43,"./autofill-utils.js":62,"./constants.js":65}],50:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { diff --git a/swift-package/Resources/assets/autofill.js b/swift-package/Resources/assets/autofill.js index 0604e2738..a3c13b871 100644 --- a/swift-package/Resources/assets/autofill.js +++ b/swift-package/Resources/assets/autofill.js @@ -4484,6 +4484,11 @@ class InterfacePrototype { } postInit() { const cleanup = this.scanner.init(); + if (this.globalConfig.isExtension) { + this.deviceApi.notify(new _deviceApiCalls.AddDebugFlagCall({ + flag: 'autofill' + })); + } this.addLogoutListener(() => { cleanup('Logged out'); if (this.globalConfig.isDDGDomain) { @@ -5588,7 +5593,7 @@ class Form { this.device = deviceInterface; this.hasShadowTree = hasShadowTree; - /** @type Record<'all' | SupportedMainTypes, Set> */ + /** @type {Record<'all' | SupportedMainTypes, Set>} */ this.inputs = { all: new Set(), credentials: new Set(), @@ -10024,7 +10029,6 @@ var _Form = require("./Form/Form.js"); var _constants = require("./constants.js"); var _matching = require("./Form/matching.js"); var _autofillUtils = require("./autofill-utils.js"); -var _deviceApiCalls = require("./deviceApiCalls/__generated__/deviceApiCalls.js"); const { MAX_INPUTS_PER_PAGE, MAX_FORMS_PER_PAGE, @@ -10123,12 +10127,6 @@ class DefaultScanner { */ init() { var _this = this; - if (this.device.globalConfig.isExtension) { - this.device.deviceApi.notify(new _deviceApiCalls.AddDebugFlagCall({ - flag: 'autofill' - })); - } - // Add the shadow DOM listener. Handlers in handleEvent window.addEventListener('pointerdown', this, true); // We don't listen for focus events on mobile, they can cause keyboard flashing @@ -10461,7 +10459,7 @@ function createScanner(device, scannerOptions) { }); } -},{"./Form/Form.js":23,"./Form/matching.js":33,"./autofill-utils.js":52,"./constants.js":55,"./deviceApiCalls/__generated__/deviceApiCalls.js":56}],40:[function(require,module,exports){ +},{"./Form/Form.js":23,"./Form/matching.js":33,"./autofill-utils.js":52,"./constants.js":55}],40:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { From 7af3c466fed9cc783251d77095b0c66e24ce1ad6 Mon Sep 17 00:00:00 2001 From: Shane Osbourne Date: Sun, 21 Jul 2024 10:31:38 +0100 Subject: [PATCH 2/5] linting --- src/Scanner.debug.js | 49 ++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/src/Scanner.debug.js b/src/Scanner.debug.js index 04fef4459..16ad65a10 100644 --- a/src/Scanner.debug.js +++ b/src/Scanner.debug.js @@ -42,7 +42,7 @@ const mockInterface = { isTooltipActive: () => { return false }, - get scanner() { + get scanner () { return state.scanner } } @@ -51,12 +51,12 @@ let state = { /** @type {import('./Scanner.js').Scanner | undefined} */ scanner: undefined, /** @type {HTMLSelectElement|null} */ - list: document.querySelector('select[name="html-list"]'), + list: document.querySelector('select[name="html-list"]') } -const url = new URL(window.location.href); -const initial = url.searchParams.get('form'); -const log = url.searchParams.has('log'); +const url = new URL(window.location.href) +const initial = url.searchParams.get('form') +const log = url.searchParams.has('log') if (log) { sessionStorage.setItem('ddg-autofill-debug', 'true') @@ -71,47 +71,46 @@ loadList() } }) - -async function loadList() { - const url = new URL(`/test-forms/index.json`, window.location.href); +async function loadList () { + const url = new URL(`/test-forms/index.json`, window.location.href) fetch(url) .then(response => response.json()) .then(x => { x.forEach(item => { - const option = document.createElement('option'); - option.value = item.html; - option.textContent = item.html; - state.list?.appendChild(option); - }); + const option = document.createElement('option') + option.value = item.html + option.textContent = item.html + state.list?.appendChild(option) + }) }) } -async function loadNewForm(filename) { - const url = new URL(`/test-forms/${filename}`, window.location.href); +async function loadNewForm (filename) { + const url = new URL(`/test-forms/${filename}`, window.location.href) fetch(url) .then(response => response.text()) .then(html => { - let mainElement = document.querySelector('main'); + let mainElement = document.querySelector('main') if (mainElement) { - mainElement.innerHTML = html; + mainElement.innerHTML = html if (state.list) { state.list.value = filename } state.scanner = createScanner(/** @type {any} */(mockInterface), { initialDelay: 1 // allow debugging directly on macOS - if this was 0 then it would try to use requestIdleCallback, which is absent in WebKit }) - state.scanner?.init(); + state.scanner?.init() } else { - console.log("'main' element not found on the page."); + console.log("'main' element not found on the page.") } }) .catch(error => { - console.error('Error:', error); - }); + console.error('Error:', error) + }) } document.querySelector('select[name="html-list"]')?.addEventListener('change', (e) => { - const elem = /** @type {HTMLSelectElement} */(e.target); - const next = new URL(window.location.href); - next.searchParams.set('form', elem.value); - window.location.href = next.href; + const elem = /** @type {HTMLSelectElement} */(e.target) + const next = new URL(window.location.href) + next.searchParams.set('form', elem.value) + window.location.href = next.href }) From c56c01cb8f906b800181c5c2026ac451a3d934f0 Mon Sep 17 00:00:00 2001 From: Shane Osbourne Date: Sun, 21 Jul 2024 10:38:56 +0100 Subject: [PATCH 3/5] Update readme --- docs/scanner-debugging.md | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/docs/scanner-debugging.md b/docs/scanner-debugging.md index c254ce119..2e7816bc5 100644 --- a/docs/scanner-debugging.md +++ b/docs/scanner-debugging.md @@ -1,28 +1,27 @@ ## Debugging the Scanner -**On most occasions it's probably easier to debug using the steps outlined at [this page](https://app.asana.com/0/1200930669568058/1204279134793324/f). You can revert to this script for more complex needs.** +We have snapshots of various forms within [test-forms](..%2Ftest-forms), these can be loaded +into `src/scanner-debug.html` for easier debugger. -You can load our `Scanner.debug.js` script into any HTML file to inspect what's happening with the matching algorithms, form analysis etc. +### Step 1: start the server +```bash +npm run server +``` -The file `src/scanner-debug.html` is there as an example, inside there's this script tag: +### Step 2: open the page -```html - - - -``` +In any browser, load the following URL +- http://localhost:3210/src/scanner-debug.html -That will load the Scanner and initialize it on the current document. You then just need to run a local webserver -to view the code in the browser's debugging tools. +### Step 3: Start debugging -```bash -# run this to install `serve` globally. This is **not** required if you have other ways of running servers! -npm i -g serve +All JS files in this setup are loaded as native ESM modules, so all browser/IDE dev tools will +work as expected. -# now serve the `src` folder as the root - this is required for the ESM imports to work correctly -serve src -``` +Note: The URL will be updated to reflect your selection, so you can share links with your colleagues. + +### Step 4: Enable detailed logging -You should now be presented with a file-listing of everything in `src` -> just click on `scanner-debug.html` to start -debugging :) +When viewing any HTML form in this setup, you can append a `log` query parameter, this will +set `sessionStorage.setItem('ddg-autofill-debug', 'true')` and will output detailed information. From 93b22eced3f80689b380f0428f036d4effef16e0 Mon Sep 17 00:00:00 2001 From: Shane Osbourne Date: Sun, 21 Jul 2024 10:56:44 +0100 Subject: [PATCH 4/5] include perf logging --- src/Scanner.debug.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Scanner.debug.js b/src/Scanner.debug.js index 16ad65a10..fe582b10f 100644 --- a/src/Scanner.debug.js +++ b/src/Scanner.debug.js @@ -60,8 +60,10 @@ const log = url.searchParams.has('log') if (log) { sessionStorage.setItem('ddg-autofill-debug', 'true') + sessionStorage.setItem('ddg-autofill-perf', 'true') } else { sessionStorage.setItem('ddg-autofill-debug', 'false') + sessionStorage.setItem('ddg-autofill-perf', 'false') } loadList() From eade03d2ab55a3d0c064eac933f200e7cc591bfb Mon Sep 17 00:00:00 2001 From: Shane Osbourne Date: Mon, 22 Jul 2024 12:45:20 +0100 Subject: [PATCH 5/5] Update src/Scanner.debug.js Co-authored-by: Emanuele Feliziani --- src/Scanner.debug.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Scanner.debug.js b/src/Scanner.debug.js index fe582b10f..c85c634fd 100644 --- a/src/Scanner.debug.js +++ b/src/Scanner.debug.js @@ -77,8 +77,8 @@ async function loadList () { const url = new URL(`/test-forms/index.json`, window.location.href) fetch(url) .then(response => response.json()) - .then(x => { - x.forEach(item => { + .then(testForms => { + testForms.forEach(item => { const option = document.createElement('option') option.value = item.html option.textContent = item.html