diff --git a/OUTPUT-EXAMPLE.md b/OUTPUT-EXAMPLE.md index 17ff76b..1cb3f2a 100644 --- a/OUTPUT-EXAMPLE.md +++ b/OUTPUT-EXAMPLE.md @@ -9,7 +9,8 @@ JSON example containing all possible step types produced by the sequence recorde "type": "goto", "url": "https://example.com/login", "windowWidth": 1280, - "windowHeight": 720 + "windowHeight": 720, + "allow_popups": true }, { "timestamp": 1718724000000, @@ -154,7 +155,7 @@ JSON example containing all possible step types produced by the sequence recorde | Type | Description | |------|-------------| -| `goto` | Navigate to a URL; first step in every recording | +| `goto` | Navigate to a URL; first step in every recording. The first step includes `allow_popups: true` when a popup (new window or tab opened by the page, e.g. `window.open()` or a `target="_blank"` link) was opened during the recording; the property is omitted otherwise | | `click` | Standard left click on an element | | `bclick` | Click on a canvas surface, includes `coords` (`x`, `y`, `width`, `height`). May include `shadow_host_css` when targeting a canvas inside a shadow DOM | | `dblclick` | Double click on an element | diff --git a/aux/popup_onload.html b/aux/popup_onload.html new file mode 100644 index 0000000..d350c88 --- /dev/null +++ b/aux/popup_onload.html @@ -0,0 +1,17 @@ + + + + +Popup on load test page + + +

Popup on load test page

+

A popup window with the second page opens automatically when this page loads.

+ +

Go back to the main page

+ + diff --git a/aux/testpage.html b/aux/testpage.html index 0a69cab..8e01871 100644 --- a/aux/testpage.html +++ b/aux/testpage.html @@ -116,6 +116,12 @@

Get elements test page

Go to second page

+
+

Popups

+

Open second page in a new tab

+ +

Go to a page that opens a popup on load

+

Elements that only change attribute

diff --git a/package.json b/package.json index f108339..7bd748c 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "snyk-api-and-web-record-sequence", - "version": "1.2.3", + "version": "1.2.5", "description": "Snyk API & Web Record login/sequence", "license": "MIT", "scripts": { diff --git a/src/manifest-chrome.json b/src/manifest-chrome.json index 3cef3b0..3f252a3 100755 --- a/src/manifest-chrome.json +++ b/src/manifest-chrome.json @@ -45,7 +45,8 @@ ], "permissions": [ "storage", - "activeTab" + "activeTab", + "webNavigation" ], "host_permissions": [ "http://*/*", diff --git a/src/manifest-firefox.json b/src/manifest-firefox.json index 772e6dd..ed65681 100755 --- a/src/manifest-firefox.json +++ b/src/manifest-firefox.json @@ -1,6 +1,6 @@ { "name": "Snyk API & Web Sequence Recorder", - "version": "1.2.3", + "version": "1.2.5", "browser_specific_settings": { "gecko": { "id": "sequence-recorder@probely.com", @@ -44,6 +44,7 @@ ], "permissions": [ "storage", + "webNavigation", "https://*/*", "http://*/*" ], diff --git a/src/manifest.json b/src/manifest.json index 3cef3b0..3f252a3 100755 --- a/src/manifest.json +++ b/src/manifest.json @@ -45,7 +45,8 @@ ], "permissions": [ "storage", - "activeTab" + "activeTab", + "webNavigation" ], "host_permissions": [ "http://*/*", diff --git a/src/pages/Background/index.js b/src/pages/Background/index.js index 24d7000..f06c857 100644 --- a/src/pages/Background/index.js +++ b/src/pages/Background/index.js @@ -4,23 +4,43 @@ import '../../assets/img/icon-128.png'; (function () { let startURLDimensionsUpdated = false; let aEvents = []; + // tabs that sent events during the current recording + let recordingTabIds = []; + // A page can open a popup before its tab's first event arrives (e.g. from + // window.onload on the start page), so keep the opener until it does. + let popupSourceTabIds = []; - chrome.storage.local.get(['recordingEvents', 'startURLDimensionsUpdated', 'recordingTimestamp'], (data) => { - const now = Date.now(); - const dataTTL = 10 * 60 * 1000; + const recordingStorageKeys = ['recordingEvents', 'startURLDimensionsUpdated', 'recordingTabIds', 'popupSourceTabIds', 'recordingTimestamp']; - if (data.recordingTimestamp && (now - data.recordingTimestamp) > dataTTL) { - chrome.storage.local.remove(['recordingEvents', 'startURLDimensionsUpdated', 'recordingTimestamp']); - aEvents = []; - startURLDimensionsUpdated = false; - } else { - if (data.recordingEvents && Array.isArray(data.recordingEvents)) { - aEvents = data.recordingEvents; - } - if (data.startURLDimensionsUpdated !== undefined) { - startURLDimensionsUpdated = data.startURLDimensionsUpdated; + // The background can be restarted mid-recording, so listeners must wait for + // the stored recording before changing (and re-saving) it. + const storageLoaded = new Promise((resolve) => { + chrome.storage.local.get(recordingStorageKeys, (data) => { + const now = Date.now(); + const dataTTL = 10 * 60 * 1000; + + if (data.recordingTimestamp && (now - data.recordingTimestamp) > dataTTL) { + chrome.storage.local.remove(recordingStorageKeys); + aEvents = []; + startURLDimensionsUpdated = false; + recordingTabIds = []; + popupSourceTabIds = []; + } else { + if (data.recordingEvents && Array.isArray(data.recordingEvents)) { + aEvents = data.recordingEvents; + } + if (data.startURLDimensionsUpdated !== undefined) { + startURLDimensionsUpdated = data.startURLDimensionsUpdated; + } + if (Array.isArray(data.recordingTabIds)) { + recordingTabIds = data.recordingTabIds; + } + if (Array.isArray(data.popupSourceTabIds)) { + popupSourceTabIds = data.popupSourceTabIds; + } } - } + resolve(); + }); }); chrome.storage.sync.get(['isRecording'], (data) => { @@ -45,12 +65,20 @@ import '../../assets/img/icon-128.png'; chrome.storage.local.set({ recordingEvents: aEvents, startURLDimensionsUpdated: startURLDimensionsUpdated, + recordingTabIds: recordingTabIds, + popupSourceTabIds: popupSourceTabIds, recordingTimestamp: Date.now() }); } - chrome.runtime.onMessage.addListener((data, sender, sendResponse) => { + function onMessage(data, sender) { if (data.messageType === 'events') { + if (sender.tab && recordingTabIds.indexOf(sender.tab.id) === -1) { + recordingTabIds.push(sender.tab.id); + if (popupSourceTabIds.indexOf(sender.tab.id) > -1 && aEvents.length) { + aEvents[0].allow_popups = true; + } + } if (!startURLDimensionsUpdated) { startURLDimensionsUpdated = true; if (aEvents.length === 1 && data.event.type === 'goto') { @@ -68,16 +96,44 @@ import '../../assets/img/icon-128.png'; } else if (data.messageType === 'start') { startURLDimensionsUpdated = false; aEvents = [data.event]; + recordingTabIds = []; + popupSourceTabIds = []; saveEventsToStorage(); } else if (data.messageType === 'clear') { startURLDimensionsUpdated = false; aEvents = []; - chrome.storage.local.remove(['recordingEvents', 'startURLDimensionsUpdated', 'recordingTimestamp']); + recordingTabIds = []; + popupSourceTabIds = []; + chrome.storage.local.remove(recordingStorageKeys); } else if (data.messageType === 'give_recording_data') { chrome.runtime.sendMessage({ messageType: 'recording_data', recordingData: [].concat(aEvents), }); } + } + + chrome.runtime.onMessage.addListener((data, sender, sendResponse) => { + storageLoaded.then(() => onMessage(data, sender)); + }); + + // Fired for window.open() and target="_blank" links/forms, including popups + // opened in a separate window (where tabs' openerTabId is not set). + chrome.webNavigation.onCreatedNavigationTarget.addListener((details) => { + storageLoaded.then(() => { + chrome.storage.sync.get(['isRecording'], (data) => { + if (!data.isRecording || !aEvents.length || aEvents[0].allow_popups) { + return; + } + if (recordingTabIds.indexOf(details.sourceTabId) > -1) { + aEvents[0].allow_popups = true; + } else if (popupSourceTabIds.indexOf(details.sourceTabId) === -1) { + popupSourceTabIds.push(details.sourceTabId); + } else { + return; + } + saveEventsToStorage(); + }); + }); }); })(); diff --git a/src/pages/Content/index.js b/src/pages/Content/index.js index 2e0327c..aac87f0 100644 --- a/src/pages/Content/index.js +++ b/src/pages/Content/index.js @@ -1,6 +1,9 @@ import { interceptEvents } from './modules/collectEvents'; import getNodeSelector from './modules/getNodeSelector'; -import getCustomSelector from './modules/getCustomSelector'; +import getCustomSelector, { + dataTestAttrs, + isStableClassName, +} from './modules/getCustomSelector'; (function () { // console.log('CONTENT SCRIPT HAS BEEN LOADED'); @@ -76,16 +79,11 @@ import getCustomSelector from './modules/getCustomSelector'; try { frameSelector = getNodeSelector(frame, { root: window.document, + attr: (name) => dataTestAttrs.includes(name), idName: (name) => { return !/^[0-9]+.*/i.test(name); }, - className: (name) => { - return ( - !name.includes('focus') && - !name.includes('highlight') && - !/^[0-9]+.*/i.test(name) - ); - }, + className: isStableClassName, // seedMinLength: 3, // optimizedMinLength: 3, }); @@ -142,16 +140,11 @@ import getCustomSelector from './modules/getCustomSelector'; try { frameSelector = getNodeSelector(frame, { root: window.document, + attr: (name) => dataTestAttrs.includes(name), idName: (name) => { return !/^[0-9]+.*/i.test(name); }, - className: (name) => { - return ( - !name.includes('focus') && - !name.includes('highlight') && - !/^[0-9]+.*/i.test(name) - ); - }, + className: isStableClassName, }); } catch (ex) { // ignore diff --git a/src/pages/Content/modules/collectEvents.js b/src/pages/Content/modules/collectEvents.js index 51b1f3a..d8eda2c 100644 --- a/src/pages/Content/modules/collectEvents.js +++ b/src/pages/Content/modules/collectEvents.js @@ -1,7 +1,12 @@ // import getNodeSelector from './getNodeSelector'; import getNodeSelector from './finder'; import getBestElement from './getBestElement'; -import getCustomSelector from './getCustomSelector'; +import getCustomSelector, { + dataTestAttrs, + getTestAncestor, + getTestAttrSelector, + isStableClassName, +} from './getCustomSelector'; import getXPath from './getXPath'; const oNodes = {}; @@ -24,6 +29,32 @@ let flushedInput = null; const keystrokeBuffers = new WeakMap(); let pendingCanvasInput = null; let canvasKeystrokeBuffer = ''; +let lastRecordedClick = null; +// A checkbox/radio "change" this soon after a recorded click on another +// element was caused by that click (e.g. custom checkboxes calling +// input.click()), so replaying both would toggle it twice. +const SIDE_EFFECT_CHANGE_MS = 100; +const watchedShadowRoots = new WeakSet(); + +// "change" is not a composed event, so it never reaches the document +// listeners from inside a shadow root; listen on each open root instead. +function watchShadowRoots(composedPath, doc, ifrSelector, callback) { + for (const item of composedPath) { + if (item instanceof ShadowRoot && !watchedShadowRoots.has(item)) { + watchedShadowRoots.add(item); + item.addEventListener( + 'change', + (ev) => { + // slotted (light DOM) targets also reach the outer listeners + if (ev.target && ev.target.getRootNode() === item) { + interceptEvents(ev, doc, ifrSelector, callback); + } + }, + true + ); + } + } +} function getEffectiveValue(element) { const domValue = element.value; @@ -55,11 +86,74 @@ function isCanvasSurface(element) { return false; } +const finderOptions = { + attr: (name) => dataTestAttrs.includes(name), + idName: (name) => { + return !/^[0-9]+.*/i.test(name); + }, + className: isStableClassName, +}; + +// The same test id can appear more than once (repeated widgets, other tab +// panes), so anchor it further up when it isn't unique on its own. +function getTestAncestorSelector(testAncestor, doc) { + const attrSelector = getTestAttrSelector(testAncestor); + try { + if (doc.querySelectorAll(attrSelector).length === 1) { + return attrSelector; + } + } catch (ex) { + // ignore + } + return getFallbackSelector(testAncestor, doc); +} + +// Prefer a selector anchored on the nearest QA/test-attribute ancestor; the +// plain finder stops at the first unique class and would skip it. +function getFallbackSelector(element, doc) { + const testAncestor = getTestAncestor(element); + if (testAncestor) { + try { + const inner = getNodeSelector(element, { + ...finderOptions, + root: testAncestor, + }); + const scoped = `${getTestAncestorSelector(testAncestor, doc)} ${inner}`; + const foundElms = doc.querySelectorAll(scoped); + if (foundElms.length === 1 && foundElms[0] === element) { + return scoped; + } + } catch (ex) { + // ignore + } + } + return getNodeSelector(element, { ...finderOptions, root: doc }); +} + +function getElementSelector(element, doc) { + let selector = null; + try { + selector = getCustomSelector(element, doc); + } catch (ex) { + // ignore + } + if (!selector) { + try { + selector = getFallbackSelector(element, doc); + } catch (ex) { + // ignore + } + } + return selector; +} + function getStableInputSelector(element, doc) { const dynamicPatterns = ['focus', 'highlight', 'editable', 'caret']; const classes = Array.from(element.classList || []).filter((cls) => { const lower = cls.toLowerCase(); - return !dynamicPatterns.some((p) => lower.includes(p)) && !/^[0-9]/.test(cls); + return ( + !dynamicPatterns.some((p) => lower.includes(p)) && isStableClassName(cls) + ); }); for (const cls of classes) { const sel = '.' + cls; @@ -90,9 +184,13 @@ export function interceptEvents(event, doc, ifrSelector, callback) { } if (shadowRootIdx > -1) { tgt = composedPath[0]; + watchShadowRoots(composedPath, doc, ifrSelector, callback); } else { tgt = event.target; } + const isInShadowRoot = + shadowRootIdx > -1 || + !!(tgt && tgt.getRootNode && tgt.getRootNode() instanceof ShadowRoot); const type = event.type; let nodeName = null; let nodeType = null; @@ -102,6 +200,8 @@ export function interceptEvents(event, doc, ifrSelector, callback) { } else { return; } + const isToggleInput = + nodeName === 'input' && (tgt.type === 'checkbox' || tgt.type === 'radio'); // console.log( // 'CAPTURED EVENT :: ', // type, @@ -112,35 +212,13 @@ export function interceptEvents(event, doc, ifrSelector, callback) { // ' :: event :: ', // event // ); - let selector = null; let bestElm = null; try { bestElm = getBestElement(tgt, doc); - selector = getCustomSelector(bestElm, doc); } catch (ex) { // ignore } - if (!selector) { - try { - selector = getNodeSelector(bestElm, { - root: doc, - idName: (name) => { - return !/^[0-9]+.*/i.test(name); - }, - className: (name) => { - return ( - !name.includes('focus') && - !name.includes('highlight') && - !/^[0-9]+.*/i.test(name) - ); - }, - // seedMinLength: 3, - // optimizedMinLength: 3, - }); - } catch (ex) { - // ignore - } - } + const selector = getElementSelector(bestElm, doc); if (selector && selector.toLowerCase() === 'html') { if (!(type === 'keydown' && pendingCanvasInput)) { return; @@ -152,7 +230,7 @@ export function interceptEvents(event, doc, ifrSelector, callback) { } catch (ex) { // ignore } - if (shadowRootIdx > -1) { + if (isInShadowRoot) { xpath = '/html/node/shadow'; } @@ -212,32 +290,50 @@ export function interceptEvents(event, doc, ifrSelector, callback) { // ignore this click, already submitted return; } + if (isToggleInput) { + return; + } + // the resulting "change" records the selection; replaying the click as + // well would just reopen the dropdown if ( - nodeName === 'input' && - (nodeType === 'checkbox' || nodeType === 'radio') + nodeName === 'select' || + ((nodeName === 'option' || nodeName === 'optgroup') && + tgt.closest('select')) ) { return; } + let clickedElm = tgt; const parentLabel = tgt.closest && tgt.closest('label'); if (parentLabel) { - // Only suppress when the click is actually forwarded to the labeled control. - // Clicks on interactive descendants (links, buttons, other fields) are NOT - // forwarded by the browser and must still be recorded. + // Clicks on interactive descendants (links, buttons, other fields) are + // not forwarded by the browser to the labeled control. const interactiveHit = tgt.closest('a, button, input, select, textarea'); - const forwardsToControl = !interactiveHit || !parentLabel.contains(interactiveHit); - if (forwardsToControl) { - const forAttr = parentLabel.getAttribute('for'); - if (forAttr) { - const root = tgt.getRootNode(); - const lookup = root && root.getElementById ? root : document; // shadow-DOM safe - const forEl = lookup.getElementById(forAttr); - if (forEl && forEl.nodeName.toLowerCase() === 'input' && - (forEl.type === 'checkbox' || forEl.type === 'radio')) { - return; + const forwardsToControl = + !interactiveHit || !parentLabel.contains(interactiveHit); + const control = parentLabel.control; + if ( + forwardsToControl && + control && + control.nodeName.toLowerCase() === 'input' && + (control.type === 'checkbox' || control.type === 'radio') + ) { + // Record the label click instead of the control's "change": setting + // .checked on replay bypasses framework handlers (e.g. React), and + // custom toggles often swap the inner icon when the state changes. + const labelSelector = getElementSelector(parentLabel, doc); + if (labelSelector) { + clickedElm = parentLabel; + let labelXPath = ''; + try { + labelXPath = getXPath(parentLabel, doc) || ''; + } catch (ex) { + // ignore } - } - if (parentLabel.querySelector('input[type="checkbox"], input[type="radio"]')) { - return; + oEventBase = { + ...oEventBase, + css: labelSelector, + xpath: isInShadowRoot ? oEventBase.xpath : labelXPath, + }; } } } @@ -257,12 +353,13 @@ export function interceptEvents(event, doc, ifrSelector, callback) { oEventToSend = { ...oEventBase, type: typeStr, - value: (tgt.value || tgt.textContent || '') + value: (clickedElm.value || clickedElm.textContent || '') .trim() .substr(0, 20) .replace(/\n/gi, ''), frame: ifrSelector, }; + lastRecordedClick = { element: clickedElm, timestamp: oEventBase.timestamp }; // Add shadow host CSS selector for bclick events inside shadow DOM if (typeStr === 'bclick' && shadowRootIdx > -1 && composedPath) { @@ -277,19 +374,7 @@ export function interceptEvents(event, doc, ifrSelector, callback) { } if (!shadowHostSelector) { try { - shadowHostSelector = getNodeSelector(shadowHost, { - root: doc, - idName: (name) => { - return !/^[0-9]+.*/i.test(name); - }, - className: (name) => { - return ( - !name.includes('focus') && - !name.includes('highlight') && - !/^[0-9]+.*/i.test(name) - ); - }, - }); + shadowHostSelector = getFallbackSelector(shadowHost, doc); } catch (ex) { // ignore } @@ -375,7 +460,7 @@ export function interceptEvents(event, doc, ifrSelector, callback) { // }; } else if (type === 'keydown') { lastNodes.keydown = tgt; - if (['input', 'textarea'].indexOf(nodeName) > -1) { + if (['input', 'textarea'].indexOf(nodeName) > -1 && !isToggleInput) { oNodes[tgt] = tgt.value; pendingInput = { element: tgt, oEventBase: { ...oEventBase }, frame: ifrSelector }; if (event.key && event.key.length === 1) { @@ -405,20 +490,22 @@ export function interceptEvents(event, doc, ifrSelector, callback) { hasKeyReturn = true; // lastSelectorWithReturn = selector; lastNodes.return = tgt; - const useBuffer = isBufferedValue(tgt); - oEventToSend = { - ...oEventBase, - type: useBuffer ? 'bfill_value' : 'fill_value', - value: getEffectiveValue(tgt), - frame: ifrSelector, - }; - if (useBuffer) { - const stableSel = getStableInputSelector(tgt, doc); - if (stableSel) { - oEventToSend.css = stableSel; + if (!isToggleInput) { + const useBuffer = isBufferedValue(tgt); + oEventToSend = { + ...oEventBase, + type: useBuffer ? 'bfill_value' : 'fill_value', + value: getEffectiveValue(tgt), + frame: ifrSelector, + }; + if (useBuffer) { + const stableSel = getStableInputSelector(tgt, doc); + if (stableSel) { + oEventToSend.css = stableSel; + } } + keystrokeBuffers.delete(tgt); } - keystrokeBuffers.delete(tgt); } else if (pendingCanvasInput && event.keyCode === 13) { if (canvasKeystrokeBuffer.length > 0 && callback) { const canvasFillEvent = { @@ -454,7 +541,7 @@ export function interceptEvents(event, doc, ifrSelector, callback) { if (pendingInput && pendingInput.element === tgt) { pendingInput = null; } - if (['input', 'textarea'].indexOf(nodeName) > -1) { + if (['input', 'textarea'].indexOf(nodeName) > -1 && !isToggleInput) { oNodes[tgt] = tgt.value; if (tgt === lastNodes.return) { lastNodes.return = null; @@ -485,6 +572,14 @@ export function interceptEvents(event, doc, ifrSelector, callback) { lastNodes.change = tgt; if (nodeName === 'input') { if (nodeType === 'checkbox' || nodeType === 'radio') { + if ( + lastRecordedClick && + lastRecordedClick.element !== tgt && + oEventBase.timestamp - lastRecordedClick.timestamp < + SIDE_EFFECT_CHANGE_MS + ) { + return; + } oEventToSend = { ...oEventBase, type: 'change', diff --git a/src/pages/Content/modules/getBestElement.js b/src/pages/Content/modules/getBestElement.js index 68e89d4..49d23d5 100644 --- a/src/pages/Content/modules/getBestElement.js +++ b/src/pages/Content/modules/getBestElement.js @@ -1,3 +1,5 @@ +import { dataTestAttrs } from './getCustomSelector'; + export default function getBestElement(node, doc) { let nodeName = null; try { @@ -33,12 +35,6 @@ export default function getBestElement(node, doc) { node = svgTag; } // check if node or parent has some "data-test" attribute - const dataTestAttrs = [ - 'data-test-id', - 'data-testid', - 'data-test', - 'data-cy', - ]; if ( node.matches(dataTestAttrs.map((dataAttr) => `[${dataAttr}]`).join(',')) ) { diff --git a/src/pages/Content/modules/getCustomSelector.js b/src/pages/Content/modules/getCustomSelector.js index 0466510..1fbd069 100644 --- a/src/pages/Content/modules/getCustomSelector.js +++ b/src/pages/Content/modules/getCustomSelector.js @@ -1,3 +1,107 @@ +export const dataTestAttrs = [ + 'data-test-id', + 'data-testid', + 'data-test', + 'data-cy', + 'data-qa', + 'data-automation-id', +]; +const MAX_TEST_ANCESTOR_DEPTH = 10; +// CSS-in-JS generated names (Emotion/react-select "css-1pahdxg-control", +// styled-components "sc-...", JSS "jss12") change with component state and +// between builds, so they can't be replayed reliably. +const generatedClassPattern = /^(css-[a-z0-9]+|sc-[a-z0-9]+|jss\d+)(-|$)/i; + +export function isStableClassName(name) { + return ( + !name.includes('focus') && + !name.includes('highlight') && + !/^[0-9]/.test(name) && + !generatedClassPattern.test(name) + ); +} + +export function getTestAttrSelector(node) { + for (const attr of dataTestAttrs) { + const value = node.getAttribute(attr); + if (value) { + return `[${attr}="${CSS.escape(value)}"]`; + } + } + return ''; +} + +export function getTestAncestor(node) { + let parent = node.parentElement; + for (let i = 0; parent && i < MAX_TEST_ANCESTOR_DEPTH; i++) { + if (getTestAttrSelector(parent)) { + return parent; + } + parent = parent.parentElement; + } + return null; +} + +function getUniqueScope(node) { + const rootNode = node.getRootNode(); + // documents have no + return ( + node.closest('body') || + (rootNode.nodeType === Node.DOCUMENT_NODE ? rootNode : null) + ); +} + +function getFrameSrcSelector(node, nodeName) { + const src = (node.getAttribute('src') || '').trim(); + if (!src || /^(about|javascript|data|blob):/i.test(src)) { + return null; + } + // query strings and hashes often carry session tokens or cache busters + const path = src.split(/[?#]/)[0]; + if (!path) { + return null; + } + const op = path === src ? '=' : '^='; + return `${nodeName}[src${op}"${CSS.escape(path)}"]`; +} + +// Tries one distinguishing attribute at a time, most stable first, and +// returns the first selector that matches only this frame. +function getFrameSelector(node, nodeName) { + const scope = getUniqueScope(node); + if (!scope) { + return ''; + } + const candidates = []; + const id = node.getAttribute('id'); + if (id) { + candidates.push(`${nodeName}#${CSS.escape(id)}`); + } + for (const attr of [...dataTestAttrs, 'name', 'title', 'aria-label']) { + const value = node.getAttribute(attr); + if (value) { + candidates.push(`${nodeName}[${attr}="${CSS.escape(value)}"]`); + } + } + const srcSelector = getFrameSrcSelector(node, nodeName); + if (srcSelector) { + candidates.push(srcSelector); + } + const testAncestor = getTestAncestor(node); + const prefix = testAncestor ? `${getTestAttrSelector(testAncestor)} ` : ''; + for (const candidate of candidates.map((c) => `${prefix}${c}`)) { + try { + const foundElms = scope.querySelectorAll(candidate); + if (foundElms.length === 1 && foundElms[0] === node) { + return candidate; + } + } catch (ex) { + // ignore + } + } + return ''; +} + export default function getCustomSelector(node, doc) { let selector = ''; let nodeName = null; @@ -10,28 +114,17 @@ export default function getCustomSelector(node, doc) { if (!nodeName) { return null; } + if (nodeName === 'frame' || nodeName === 'iframe') { + return getFrameSelector(node, nodeName); + } selector = `${nodeName}`; if (node.getAttribute('id')) { selector = `${selector}#${CSS.escape(node.getAttribute('id'))}`; ignoreAriaLabel = true; } - const dataTestAttrs = [ - 'data-test-id', - 'data-testid', - 'data-test', - 'data-cy', - ]; - if ( - node.matches(dataTestAttrs.map((dataAttr) => `[${dataAttr}]`).join(',')) - ) { - for (const testAttr of dataTestAttrs) { - const testAttrValue = node.getAttribute(testAttr); - if (testAttrValue) { - selector = `${selector}[${testAttr}="${CSS.escape(testAttrValue)}"]`; - break; - } - } - } + selector = `${selector}${getTestAttrSelector(node)}`; + let formClosest = null; + let formSelector = ''; if ( ['input', 'button', 'textarea', 'select', 'option'].includes(nodeName) ) { @@ -45,15 +138,33 @@ export default function getCustomSelector(node, doc) { node.getAttribute('name') )}"]`; } - const formClosest = node.closest('form'); + formClosest = node.closest('form'); if (formClosest) { - let form = 'form'; + formSelector = 'form'; if (formClosest.getAttribute('id')) { - form = `${form}#${CSS.escape(formClosest.getAttribute('id'))}`; + formSelector = `${formSelector}#${CSS.escape( + formClosest.getAttribute('id') + )}`; } - selector = `${form} ${selector}`; } } + let prefix = formSelector; + const testAncestor = getTestAncestor(node); + if (testAncestor) { + const ancestorSelector = getTestAttrSelector(testAncestor); + if (testAncestor === formClosest) { + prefix = `${formSelector}${ancestorSelector}`; + } else if (formClosest && formClosest.contains(testAncestor)) { + prefix = `${formSelector} ${ancestorSelector}`; + } else { + prefix = formSelector + ? `${ancestorSelector} ${formSelector}` + : ancestorSelector; + } + } + if (prefix) { + selector = `${prefix} ${selector}`; + } // check if there's a "aria-label" and is unique const ariaLabelValue = node.getAttribute('aria-label'); if (ariaLabelValue && !ignoreAriaLabel) { @@ -61,10 +172,10 @@ export default function getCustomSelector(node, doc) { } // check if selector is the only one in the page if (selector) { - const docBody = node.closest('body'); - if (docBody) { + const scope = getUniqueScope(node); + if (scope) { try { - const foundElms = docBody.querySelectorAll(selector); + const foundElms = scope.querySelectorAll(selector); if (foundElms.length !== 1) { selector = ''; } diff --git a/test-build-firefox/background.bundle.js b/test-build-firefox/background.bundle.js index 45ca07a..fc2c211 100644 --- a/test-build-firefox/background.bundle.js +++ b/test-build-firefox/background.bundle.js @@ -1 +1 @@ -(()=>{"use strict";var e={p:"/"};e.p,e.p;!function(){let e=!1,t=[];function r(){chrome.storage.local.set({recordingEvents:t,startURLDimensionsUpdated:e,recordingTimestamp:Date.now()})}chrome.storage.local.get(["recordingEvents","startURLDimensionsUpdated","recordingTimestamp"],(r=>{const n=Date.now();r.recordingTimestamp&&n-r.recordingTimestamp>6e5?(chrome.storage.local.remove(["recordingEvents","startURLDimensionsUpdated","recordingTimestamp"]),t=[],e=!1):(r.recordingEvents&&Array.isArray(r.recordingEvents)&&(t=r.recordingEvents),void 0!==r.startURLDimensionsUpdated&&(e=r.startURLDimensionsUpdated))})),chrome.storage.sync.get(["isRecording"],(e=>{e.isRecording?(chrome.action||chrome.browserAction).setBadgeText({text:"🔴"},(()=>{})):(chrome.action||chrome.browserAction).setBadgeText({text:""},(()=>{}))})),chrome.runtime.onMessage.addListener(((n,s,o)=>{if("events"===n.messageType){if(!e&&(e=!0,1===t.length&&"goto"===n.event.type&&(t[0].windowWidth=n.event.windowWidth,t[0].windowHeight=n.event.windowHeight,t[0].url===n.event.url)))return void r();t.push(n.event),r()}else"start"===n.messageType?(e=!1,t=[n.event],r()):"clear"===n.messageType?(e=!1,t=[],chrome.storage.local.remove(["recordingEvents","startURLDimensionsUpdated","recordingTimestamp"])):"give_recording_data"===n.messageType&&chrome.runtime.sendMessage({messageType:"recording_data",recordingData:[].concat(t)})}))}()})(); \ No newline at end of file +(()=>{"use strict";var e={p:"/"};e.p,e.p;!function(){let e=!1,r=[],o=[],t=[];const s=["recordingEvents","startURLDimensionsUpdated","recordingTabIds","popupSourceTabIds","recordingTimestamp"],n=new Promise((n=>{chrome.storage.local.get(s,(i=>{const a=Date.now();i.recordingTimestamp&&a-i.recordingTimestamp>6e5?(chrome.storage.local.remove(s),r=[],e=!1,o=[],t=[]):(i.recordingEvents&&Array.isArray(i.recordingEvents)&&(r=i.recordingEvents),void 0!==i.startURLDimensionsUpdated&&(e=i.startURLDimensionsUpdated),Array.isArray(i.recordingTabIds)&&(o=i.recordingTabIds),Array.isArray(i.popupSourceTabIds)&&(t=i.popupSourceTabIds)),n()}))}));function i(){chrome.storage.local.set({recordingEvents:r,startURLDimensionsUpdated:e,recordingTabIds:o,popupSourceTabIds:t,recordingTimestamp:Date.now()})}chrome.storage.sync.get(["isRecording"],(e=>{e.isRecording?(chrome.action||chrome.browserAction).setBadgeText({text:"🔴"},(()=>{})):(chrome.action||chrome.browserAction).setBadgeText({text:""},(()=>{}))})),chrome.runtime.onMessage.addListener(((a,d,c)=>{n.then((()=>function(n,a){if("events"===n.messageType){if(a.tab&&-1===o.indexOf(a.tab.id)&&(o.push(a.tab.id),t.indexOf(a.tab.id)>-1&&r.length&&(r[0].allow_popups=!0)),!e&&(e=!0,1===r.length&&"goto"===n.event.type&&(r[0].windowWidth=n.event.windowWidth,r[0].windowHeight=n.event.windowHeight,r[0].url===n.event.url)))return void i();r.push(n.event),i()}else"start"===n.messageType?(e=!1,r=[n.event],o=[],t=[],i()):"clear"===n.messageType?(e=!1,r=[],o=[],t=[],chrome.storage.local.remove(s)):"give_recording_data"===n.messageType&&chrome.runtime.sendMessage({messageType:"recording_data",recordingData:[].concat(r)})}(a,d)))})),chrome.webNavigation.onCreatedNavigationTarget.addListener((e=>{n.then((()=>{chrome.storage.sync.get(["isRecording"],(s=>{if(s.isRecording&&r.length&&!r[0].allow_popups){if(o.indexOf(e.sourceTabId)>-1)r[0].allow_popups=!0;else{if(-1!==t.indexOf(e.sourceTabId))return;t.push(e.sourceTabId)}i()}}))}))}))}()})(); \ No newline at end of file diff --git a/test-build-firefox/contentScript.bundle.js b/test-build-firefox/contentScript.bundle.js index 46eef33..3d55e3e 100644 --- a/test-build-firefox/contentScript.bundle.js +++ b/test-build-firefox/contentScript.bundle.js @@ -1 +1 @@ -(()=>{"use strict";let e,t;function n(n,r){if(n.nodeType!==Node.ELEMENT_NODE)throw new Error("Can't generate CSS selector for non-element node type.");if("html"===n.tagName.toLowerCase())return"html";const i={root:document.body,idName:e=>!0,className:e=>!0,tagName:e=>!0,attr:(e,t)=>!1,seedMinLength:1,optimizedMinLength:2,threshold:1e3,maxNumberOfTries:1e4};e={...i,...r},t=function(e,t){if(e.nodeType===Node.DOCUMENT_NODE)return e;if(e===t.root)return e.ownerDocument;return e}(e.root,i);let s=o(n,"all",(()=>o(n,"two",(()=>o(n,"one",(()=>o(n,"none")))))));if(s){const e=w(v(s,n));return e.length>0&&(s=e[0]),l(s)}throw new Error("Selector was not found.")}function o(t,n,o){let l=null,i=[],s=t,g=0;for(;s;){let t=p(a(s))||p(...c(s))||p(...u(s))||p(f(s))||[{name:"*",penalty:3}];const y=d(s);if("all"==n)y&&(t=t.concat(t.filter(h).map((e=>m(e,y)))));else if("two"==n)t=t.slice(0,1),y&&(t=t.concat(t.filter(h).map((e=>m(e,y)))));else if("one"==n){const[e]=t=t.slice(0,1);y&&h(e)&&(t=[m(e,y)])}else"none"==n&&(t=[{name:"*",penalty:3}],y&&(t=[m(t[0],y)]));for(let e of t)e.level=g;if(i.push(t),i.length>=e.seedMinLength&&(l=r(i,o),l))break;s=s.parentElement,g++}return l||(l=r(i,o)),!l&&o?o():l}function r(t,n){const o=w(y(t));if(o.length>e.threshold)return n?n():null;for(let e of o)if(s(e))return e;return null}function l(e){let t=e[0],n=t.name;for(let o=1;o ${n}`:`${e[o].name} ${n}`,t=e[o]}return n}function i(e){return e.map((e=>e.penalty)).reduce(((e,t)=>e+t),0)}function s(e){const n=l(e);switch(t.querySelectorAll(n).length){case 0:throw new Error(`Can't select any node with this selector: ${n}`);case 1:return!0;default:return!1}}function a(t){const n=t.getAttribute("id");return n&&e.idName(n)?{name:"#"+CSS.escape(n),penalty:0}:null}function c(t){const n=Array.from(t.attributes).filter((t=>e.attr(t.name,t.value)));return n.map((e=>({name:`[${CSS.escape(e.name)}="${CSS.escape(e.value)}"]`,penalty:.5})))}function u(t){return Array.from(t.classList).filter(e.className).map((e=>({name:"."+CSS.escape(e),penalty:1})))}function f(t){const n=t.tagName.toLowerCase();return e.tagName(n)?{name:n,penalty:2}:null}function d(e){const t=e.parentNode;if(!t)return null;let n=t.firstChild;if(!n)return null;let o=0;for(;n&&(n.nodeType===Node.ELEMENT_NODE&&o++,n!==e);)n=n.nextSibling;return o}function m(e,t){return{name:e.name+`:nth-child(${t})`,penalty:e.penalty+1}}function h(e){return"html"!==e.name&&!e.name.startsWith("#")}function p(...e){const t=e.filter(g);return t.length>0?t:null}function g(e){return null!=e}function*y(e,t=[]){if(e.length>0)for(let n of e[0])yield*y(e.slice(1,e.length),t.concat(n));else yield t}function w(e){return[...e].sort(((e,t)=>i(e)-i(t)))}function*v(t,n,o={counter:0,visited:new Map}){if(t.length>2&&t.length>e.optimizedMinLength)for(let r=1;re.maxNumberOfTries)return;o.counter+=1;const i=[...t];i.splice(r,1);const a=l(i);if(o.visited.has(a))return;s(i)&&b(i,n)&&(yield i,o.visited.set(a,!0),yield*v(i,n,o))}}function b(e,n){return t.querySelector(l(e))===n}function N(e,t){let n="",o=null,r=!1;try{if(o=e.nodeName&&"string"==typeof e.nodeName?e.nodeName.toLowerCase():null,!o)return null;n=`${o}`,e.getAttribute("id")&&(n=`${n}#${CSS.escape(e.getAttribute("id"))}`,r=!0);const t=["data-test-id","data-testid","data-test","data-cy"];if(e.matches(t.map((e=>`[${e}]`)).join(",")))for(const o of t){const t=e.getAttribute(o);if(t){n=`${n}[${o}="${CSS.escape(t)}"]`;break}}if(["input","button","textarea","select","option"].includes(o)){e.getAttribute("type")&&(n=`${n}[type="${CSS.escape(e.getAttribute("type"))}"]`),e.getAttribute("name")&&(n=`${n}[name="${CSS.escape(e.getAttribute("name"))}"]`);const t=e.closest("form");if(t){let e="form";t.getAttribute("id")&&(e=`${e}#${CSS.escape(t.getAttribute("id"))}`),n=`${e} ${n}`}}const l=e.getAttribute("aria-label");if(l&&!r&&(n=`${n}[aria-label="${CSS.escape(l)}"]`),n){const t=e.closest("body");if(t)try{1!==t.querySelectorAll(n).length&&(n="")}catch(e){n=""}}}catch(e){}return n}function E(e,t){let n,o=e,r=[];for(;o;){let e=0,n=o.previousElementSibling;for(;n;)n.nodeName===o.nodeName&&e++,n=n.previousElementSibling;let l=o.nodeName.toLowerCase();e>0&&(l=l+"["+(e+1)+"]"),o!==t&&r.unshift(l),o=o.parentNode}if(n="/"+r.join("/"),function(e,t){const n=t.evaluate(e,t,null,XPathResult.ANY_TYPE,null),o=n.iterateNext();return o}(n,t)===e)return n}const k={},S={keydown:null,return:null,blur:null,change:null,click:null,dblclick:null,contextmenu:null,focus:null,mouseover:null,mouseout:null};let C=!1,A=null,L=null;const T=new WeakMap;let $=null,_="";function x(e){const t=e.value;return t&&t.trim()?t:T.get(e)||t}function M(e){const t=e.value;return!(t&&t.trim())&&T.has(e)}function O(e,t){const n=["focus","highlight","editable","caret"],o=Array.from(e.classList||[]).filter((e=>{const t=e.toLowerCase();return!n.some((e=>t.includes(e)))&&!/^[0-9]/.test(e)}));for(const n of o){const o="."+n;try{const n=t.querySelectorAll(o);if(1===n.length&&n[0]===e)return o}catch(e){}}return null}function q(e,t){let n;var o;let r,l;function i(e,t,o){let l=null;const i=[];let a=e,c=0;for(;a&&a!==r.root.parentElement;){let e=w(f(a))||w(...d(a))||w(...m(a))||w(h(a))||[{name:"*",penalty:3}];const u=p(a);if(t===n.All)u&&(e=e.concat(e.filter(y).map((e=>g(e,u)))));else if(t===n.Two)e=e.slice(0,1),u&&(e=e.concat(e.filter(y).map((e=>g(e,u)))));else if(t===n.One){const[t]=e=e.slice(0,1);u&&y(t)&&(e=[g(t,u)])}for(const t of e)t.level=c;if(i.push(e),i.length>=r.seedMinLength&&(l=s(i,o),l))break;a=a.parentElement,c++}return l||(l=s(i,o)),l}function s(e,t){const n=N(b(e));if(n.length>r.threshold)return t?t():null;for(const e of n)if(u(e))return e;return null}function a(e){let t=e[0],n=t.name;for(let o=1;o ${n}`:`${e[o].name} ${n}`,t=e[o]}return n}function c(e){return e.map((e=>e.penalty)).reduce(((e,t)=>e+t),0)}function u(e){switch(l.querySelectorAll(a(e)).length){case 0:throw new Error(`Can't select any node with this selector: ${a(e)}`);case 1:return!0;default:return!1}}function f(e){const t=e.getAttribute("id");return t&&r.idName(t)?{name:`#${L(t,{isIdentifier:!0})}`,penalty:0}:null}function d(e){return Array.from(e.attributes).filter((e=>r.attr(e.name,e.value))).map((e=>({name:"["+L(e.name,{isIdentifier:!0})+'="'+L(e.value)+'"]',penalty:.5})))}function m(e){return Array.from(e.classList).filter(r.className).map((e=>({name:"."+L(e,{isIdentifier:!0}),penalty:1})))}function h(e){const t=e.tagName.toLowerCase();return r.tagName(t)?{name:t,penalty:2}:null}function p(e){const t=e.parentNode;if(!t)return null;let n=t.firstChild;if(!n)return null;let o=0;for(;n&&(n.nodeType===Node.ELEMENT_NODE&&o++,n!==e);)n=n.nextSibling;return o}function g(e,t){return{name:e.name+`:nth-child(${t})`,penalty:e.penalty+1}}function y(e){return"html"!==e.name&&!e.name.startsWith("#")}function w(...e){const t=e.filter(v);return 0c(e)-c(t)))}function E(e,t){return l.querySelector(a(e))===t}(o=n||(n={}))[o.All=0]="All",o[o.Two=1]="Two",o[o.One=2]="One";const k=/[ -,\.\/:-@\[-\^`\{-~]/,S=/[ -,\.\/:-@\[\]\^`\{-~]/,C=/(^|\\+)?(\\[A-F0-9]{1,6})\x20(?![a-fA-F0-9\x20])/g,A={escapeEverything:!1,isIdentifier:!1,quotes:"single",wrap:!1};function L(e,t={}){const n=Object.assign(Object.assign({},A),t);"single"!==n.quotes&&"double"!==n.quotes&&(n.quotes="single");const o="double"==n.quotes?'"':"'",r=n.isIdentifier,l=e.charAt(0);let i="",s=0;for(const t=e.length;sc||126=c&&s!0,className:()=>!0,tagName:()=>!0,attr:()=>!1,seedMinLength:1,optimizedMinLength:2,threshold:1e3,maxNumberOfTries:1e4};r=Object.assign(Object.assign({},T),t),l=function(e,t){return e.nodeType===Node.DOCUMENT_NODE?e:e===t.root?e.ownerDocument:e}(r.root,T);let $=i(e,n.All,(()=>i(e,n.Two,(()=>i(e,n.One)))));if($){const t=N(function*e(t,n,o={counter:0,visited:new Map}){if(2r.optimizedMinLength)for(let l=1;lr.maxNumberOfTries)return;o.counter+=1;const i=[...t];i.splice(l,1);const s=a(i);if(o.visited.has(s))return;u(i)&&E(i,n)&&(yield i,o.visited.set(s,!0),yield*e(i,n,o))}}($,e));return 0{if(!l.isRecording)return;let i=!1,s=null;function a(e){e&&"mouseover"===e.type&&!i||function(e,t,o,r){let l=!1,i=null,s=null;e&&e.composed&&e.composedPath&&(s=e.composedPath());let a=-1;s&&s.length>0&&(a=s.findIndex((e=>e instanceof ShadowRoot))),i=a>-1?s[0]:e.target;const c=e.type;let u=null,f=null;if(!i||!i.getAttribute)return;u=i.nodeName.toLowerCase(),f=i.getAttribute("type");let d=null,m=null;try{m=function(e){let t=null;try{if(t=e.nodeName&&"string"==typeof e.nodeName?e.nodeName.toLowerCase():null,!t)return e;if(["input","button","a","textarea","select","option","progress"].includes(t))return e;const n=e.closest("button,a");if(n)return n;const o=e.closest("svg");o&&(e=o);const r=["data-test-id","data-testid","data-test","data-cy"];if(e.matches(r.map((e=>`[${e}]`)).join(",")))return e;if(e.parentNode.matches(r.map((e=>`[${e}]`)).join(",")))return e.parentNode}catch(e){}return e}(i),d=N(m)}catch(e){}if(!d)try{d=n(m,{root:t,idName:e=>!/^[0-9]+.*/i.test(e),className:e=>!e.includes("focus")&&!e.includes("highlight")&&!/^[0-9]+.*/i.test(e)})}catch(e){}if(d&&"html"===d.toLowerCase()&&("keydown"!==c||!$))return;let h=null;try{h=E(m,t)}catch(e){}a>-1&&(h="/html/node/shadow");let p={timestamp:(new Date).getTime(),css:d||i,xpath:h||""},g={};if("click"===c){if(S.click=i,A&&A.element!==i&&r){const e=M(A.element),n={...A.oEventBase,type:e?"bfill_value":"fill_value",value:x(A.element),frame:A.frame};if(e){const e=O(A.element,t);e&&(n.css=e)}r({messageType:"events",event:{...n}}),T.delete(A.element),L=A.element,A=null}if($&&_.length>0&&r){const e={...$.oEventBase,type:"bfill_value",value:_,frame:$.frame};$.shadow_host_css&&(e.shadow_host_css=$.shadow_host_css),r({messageType:"events",event:{...e}})}if(_="",$=null,!(S.return!==S.change||i===S.return||"input"!==u&&"button"!==u||"submit"!==f&&"image"!==f||null===S.return))return;if("input"===u&&("checkbox"===f||"radio"===f))return;const l=i.closest&&i.closest("label");if(l){const e=i.closest("a, button, input, select, textarea");if(!e||!l.contains(e)){const e=l.getAttribute("for");if(e){const t=i.getRootNode(),n=(t&&t.getElementById?t:document).getElementById(e);if(n&&"input"===n.nodeName.toLowerCase()&&("checkbox"===n.type||"radio"===n.type))return}if(l.querySelector('input[type="checkbox"], input[type="radio"]'))return}}let c="click";if(function(e){if("canvas"===e.nodeName.toLowerCase())return!0;if(e.shadowRoot)try{if(e.shadowRoot.querySelector("canvas"))return!0}catch(e){}for(const t of e.children||[])if(t.shadowRoot)try{if(t.shadowRoot.querySelector("canvas"))return!0}catch(e){}return!1}(i)){const t=i.getBoundingClientRect(),n={x:e.clientX-t.left,y:e.clientY-t.top,width:t.width,height:t.height};p={...p,coords:n},c="bclick"}if(g={...p,type:c,value:(i.value||i.textContent||"").trim().substr(0,20).replace(/\n/gi,""),frame:o},"bclick"===c&&a>-1&&s){const e=s[a].host;if(e){let o=null;try{o=N(e)}catch(e){}if(!o)try{o=n(e,{root:t,idName:e=>!/^[0-9]+.*/i.test(e),className:e=>!e.includes("focus")&&!e.includes("highlight")&&!/^[0-9]+.*/i.test(e)})}catch(e){}o&&(g.shadow_host_css=o)}}"bclick"===c&&($={element:i,oEventBase:{...p},frame:o,shadow_host_css:g.shadow_host_css||null})}else if("dblclick"===c)S.dblclick=i,g={...p,type:"dblclick",value:(i.value||i.textContent||"").trim().substr(0,20).replace(/\n/gi,""),frame:o};else if("contextmenu"===c)S.contextmenu=i,g={...p,type:"contextmenu",value:(i.value||i.textContent||"").trim().substr(0,20).replace(/\n/gi,""),frame:o};else if("focus"===c);else if("mouseover"===c)C&&(clearTimeout(C),C=!1),C=setTimeout((()=>{C=!1,S.mouseover=i,g={...p,type:"mouseover",value:(i.value||i.textContent||"").trim().substr(0,20).replace(/\n/gi,""),frame:o},g&&g.type&&r&&r({messageType:"events",event:{...g}})}),500);else if("mouseout"===c);else if("keydown"===c){if(S.keydown=i,["input","textarea"].indexOf(u)>-1){if(k[i]=i.value,A={element:i,oEventBase:{...p},frame:o},e.key&&1===e.key.length){const t=T.get(i)||"";T.set(i,t+e.key)}else if("Backspace"===e.key){const e=T.get(i)||"";e.length>0&&T.set(i,e.slice(0,-1))}}else $&&(e.key&&1===e.key.length?_+=e.key:"Backspace"===e.key&&_.length>0&&(_=_.slice(0,-1)));if("input"===u&&13===e.keyCode){l=!0,S.return=i;const e=M(i);if(g={...p,type:e?"bfill_value":"fill_value",value:x(i),frame:o},e){const e=O(i,t);e&&(g.css=e)}T.delete(i)}else if($&&13===e.keyCode){if(_.length>0&&r){const e={...$.oEventBase,type:"bfill_value",value:_,frame:$.frame};$.shadow_host_css&&(e.shadow_host_css=$.shadow_host_css),r({messageType:"events",event:{...e}}),_=""}if(r){const e={...$.oEventBase,timestamp:(new Date).getTime(),type:"press_key",value:13,frame:$.frame};r({messageType:"events",event:{...e}})}$=null}}else if("blur"===c){if(S.blur=i,i===L)return void(L=null);if(A&&A.element===i&&(A=null),["input","textarea"].indexOf(u)>-1){if(k[i]=i.value,i===S.return)return void(S.return=null);if("input"===u&&("submit"===i.type||"button"===i.type||"image"===i.type))return;const e=M(i);if(g={...p,type:e?"bfill_value":"fill_value",value:x(i),frame:o},e){const e=O(i,t);e&&(g.css=e)}T.delete(i)}}else if("change"===c)if(S.change=i,"input"===u)"checkbox"!==f&&"radio"!==f||(g={...p,type:"change",subtype:"check",checked:i.checked,frame:o});else if("select"===u)if(i.multiple){const e=[];for(let t=0;t{window!==window.top?window.parent.postMessage({source:"event-from-iframe",obj:{...e},framePath:[]},"*"):chrome.runtime.sendMessage(e)}))}window===window.top&&window.addEventListener("message",(e=>{if(e.data&&e.data.source&&"event-from-iframe"===e.data.source){const t=document.querySelectorAll("iframe, frame");let n=!1;for(const o of t)if(o.contentWindow===e.source){n=!0;break}if(!n)return;const o={...e.data.obj},r=e.data.framePath||[];for(const n of t)if(n.contentWindow===e.source){let e=null;try{e=N(n)}catch(e){}if(!e)try{e=q(n,{root:window.document,idName:e=>!/^[0-9]+.*/i.test(e),className:e=>!e.includes("focus")&&!e.includes("highlight")&&!/^[0-9]+.*/i.test(e)})}catch(e){}if(e&&o.event){const t=[e,...r];o.event.frame=t.join(" >>> "),chrome.runtime.sendMessage(o)}break}}})),window!==window.top&&window.addEventListener("message",(e=>{if(e.data&&e.data.source&&"event-from-iframe"===e.data.source){const t=document.querySelectorAll("iframe, frame");let n=!1;for(const o of t)if(o.contentWindow===e.source){n=!0;break}if(!n)return;const o=e.data.framePath||[];for(const n of t)if(n.contentWindow===e.source){let t=null;try{t=N(n)}catch(e){}if(!t)try{t=q(n,{root:window.document,idName:e=>!/^[0-9]+.*/i.test(e),className:e=>!e.includes("focus")&&!e.includes("highlight")&&!/^[0-9]+.*/i.test(e)})}catch(e){}t&&o.push(t),window.parent.postMessage({source:"event-from-iframe",obj:e.data.obj,framePath:o},"*");break}}})),l.isRecording&&(t=!0,e=document.title,function(){if(!t)return document.title=`${e}`,r=!1,void(o&&(clearInterval(o),o=!1));o=setInterval((()=>{r?(document.title=`${e}`,r=!1):(document.title=`🔴 ${e}`,r=!0)}),1e3)}(),window===window.top&&chrome.runtime.sendMessage({messageType:"events",event:{type:"goto",timestamp:(new Date).getTime(),windowWidth:window.innerWidth,windowHeight:window.innerHeight,url:window.location.href}}),document.addEventListener("click",a,!0),document.addEventListener("mouseover",a,!0),document.addEventListener("dblclick",a,!0),document.addEventListener("contextmenu",a,!0),document.addEventListener("keydown",a,!0),document.addEventListener("blur",a,!0),document.addEventListener("change",a,!0));const c={attributes:!1,childList:!0,subtree:!0},u=new MutationObserver((async(e,t)=>{i=!0,s&&(clearTimeout(s),s=null),s=setTimeout((()=>{i=!1}),200)}));document.body&&u.observe(document.body,c)}))}()})(); \ No newline at end of file +(()=>{"use strict";let e,t;function n(n,r){if(n.nodeType!==Node.ELEMENT_NODE)throw new Error("Can't generate CSS selector for non-element node type.");if("html"===n.tagName.toLowerCase())return"html";const s={root:document.body,idName:e=>!0,className:e=>!0,tagName:e=>!0,attr:(e,t)=>!1,seedMinLength:1,optimizedMinLength:2,threshold:1e3,maxNumberOfTries:1e4};e={...s,...r},t=function(e,t){if(e.nodeType===Node.DOCUMENT_NODE)return e;if(e===t.root)return e.ownerDocument;return e}(e.root,s);let i=o(n,"all",(()=>o(n,"two",(()=>o(n,"one",(()=>o(n,"none")))))));if(i){const e=w(v(i,n));return e.length>0&&(i=e[0]),l(i)}throw new Error("Selector was not found.")}function o(t,n,o){let l=null,s=[],i=t,g=0;for(;i;){let t=p(a(i))||p(...c(i))||p(...u(i))||p(f(i))||[{name:"*",penalty:3}];const y=d(i);if("all"==n)y&&(t=t.concat(t.filter(h).map((e=>m(e,y)))));else if("two"==n)t=t.slice(0,1),y&&(t=t.concat(t.filter(h).map((e=>m(e,y)))));else if("one"==n){const[e]=t=t.slice(0,1);y&&h(e)&&(t=[m(e,y)])}else"none"==n&&(t=[{name:"*",penalty:3}],y&&(t=[m(t[0],y)]));for(let e of t)e.level=g;if(s.push(t),s.length>=e.seedMinLength&&(l=r(s,o),l))break;i=i.parentElement,g++}return l||(l=r(s,o)),!l&&o?o():l}function r(t,n){const o=w(y(t));if(o.length>e.threshold)return n?n():null;for(let e of o)if(i(e))return e;return null}function l(e){let t=e[0],n=t.name;for(let o=1;o ${n}`:`${e[o].name} ${n}`,t=e[o]}return n}function s(e){return e.map((e=>e.penalty)).reduce(((e,t)=>e+t),0)}function i(e){const n=l(e);switch(t.querySelectorAll(n).length){case 0:throw new Error(`Can't select any node with this selector: ${n}`);case 1:return!0;default:return!1}}function a(t){const n=t.getAttribute("id");return n&&e.idName(n)?{name:"#"+CSS.escape(n),penalty:0}:null}function c(t){const n=Array.from(t.attributes).filter((t=>e.attr(t.name,t.value)));return n.map((e=>({name:`[${CSS.escape(e.name)}="${CSS.escape(e.value)}"]`,penalty:.5})))}function u(t){return Array.from(t.classList).filter(e.className).map((e=>({name:"."+CSS.escape(e),penalty:1})))}function f(t){const n=t.tagName.toLowerCase();return e.tagName(n)?{name:n,penalty:2}:null}function d(e){const t=e.parentNode;if(!t)return null;let n=t.firstChild;if(!n)return null;let o=0;for(;n&&(n.nodeType===Node.ELEMENT_NODE&&o++,n!==e);)n=n.nextSibling;return o}function m(e,t){return{name:e.name+`:nth-child(${t})`,penalty:e.penalty+1}}function h(e){return"html"!==e.name&&!e.name.startsWith("#")}function p(...e){const t=e.filter(g);return t.length>0?t:null}function g(e){return null!=e}function*y(e,t=[]){if(e.length>0)for(let n of e[0])yield*y(e.slice(1,e.length),t.concat(n));else yield t}function w(e){return[...e].sort(((e,t)=>s(e)-s(t)))}function*v(t,n,o={counter:0,visited:new Map}){if(t.length>2&&t.length>e.optimizedMinLength)for(let r=1;re.maxNumberOfTries)return;o.counter+=1;const s=[...t];s.splice(r,1);const a=l(s);if(o.visited.has(a))return;i(s)&&b(s,n)&&(yield s,o.visited.set(a,!0),yield*v(s,n,o))}}function b(e,n){return t.querySelector(l(e))===n}const N=["data-test-id","data-testid","data-test","data-cy","data-qa","data-automation-id"],$=/^(css-[a-z0-9]+|sc-[a-z0-9]+|jss\d+)(-|$)/i;function S(e){return!(e.includes("focus")||e.includes("highlight")||/^[0-9]/.test(e)||$.test(e))}function E(e){for(const t of N){const n=e.getAttribute(t);if(n)return`[${t}="${CSS.escape(n)}"]`}return""}function C(e){let t=e.parentElement;for(let e=0;t&&e<10;e++){if(E(t))return t;t=t.parentElement}return null}function k(e){const t=e.getRootNode();return e.closest("body")||(t.nodeType===Node.DOCUMENT_NODE?t:null)}function A(e,t){const n=k(e);if(!n)return"";const o=[],r=e.getAttribute("id");r&&o.push(`${t}#${CSS.escape(r)}`);for(const n of[...N,"name","title","aria-label"]){const r=e.getAttribute(n);r&&o.push(`${t}[${n}="${CSS.escape(r)}"]`)}const l=function(e,t){const n=(e.getAttribute("src")||"").trim();if(!n||/^(about|javascript|data|blob):/i.test(n))return null;const o=n.split(/[?#]/)[0];return o?`${t}[src${o===n?"=":"^="}"${CSS.escape(o)}"]`:null}(e,t);l&&o.push(l);const s=C(e),i=s?`${E(s)} `:"";for(const t of o.map((e=>`${i}${e}`)))try{const o=n.querySelectorAll(t);if(1===o.length&&o[0]===e)return t}catch(e){}return""}function T(e,t){let n="",o=null,r=!1;try{if(o=e.nodeName&&"string"==typeof e.nodeName?e.nodeName.toLowerCase():null,!o)return null;if("frame"===o||"iframe"===o)return A(e,o);n=`${o}`,e.getAttribute("id")&&(n=`${n}#${CSS.escape(e.getAttribute("id"))}`,r=!0),n=`${n}${E(e)}`;let t=null,l="";["input","button","textarea","select","option"].includes(o)&&(e.getAttribute("type")&&(n=`${n}[type="${CSS.escape(e.getAttribute("type"))}"]`),e.getAttribute("name")&&(n=`${n}[name="${CSS.escape(e.getAttribute("name"))}"]`),t=e.closest("form"),t&&(l="form",t.getAttribute("id")&&(l=`${l}#${CSS.escape(t.getAttribute("id"))}`)));let s=l;const i=C(e);if(i){const e=E(i);s=i===t?`${l}${e}`:t&&t.contains(i)?`${l} ${e}`:l?`${e} ${l}`:e}s&&(n=`${s} ${n}`);const a=e.getAttribute("aria-label");if(a&&!r&&(n=`${n}[aria-label="${CSS.escape(a)}"]`),n){const t=k(e);if(t)try{1!==t.querySelectorAll(n).length&&(n="")}catch(e){n=""}}}catch(e){}return n}function L(e,t){let n,o=e,r=[];for(;o;){let e=0,n=o.previousElementSibling;for(;n;)n.nodeName===o.nodeName&&e++,n=n.previousElementSibling;let l=o.nodeName.toLowerCase();e>0&&(l=l+"["+(e+1)+"]"),o!==t&&r.unshift(l),o=o.parentNode}if(n="/"+r.join("/"),function(e,t){const n=t.evaluate(e,t,null,XPathResult.ANY_TYPE,null),o=n.iterateNext();return o}(n,t)===e)return n}const _={},x={keydown:null,return:null,blur:null,change:null,click:null,dblclick:null,contextmenu:null,focus:null,mouseover:null,mouseout:null};let O=!1,M=null,q=null;const D=new WeakMap;let R=null,j="",B=null;const W=new WeakSet;function I(e){const t=e.value;return t&&t.trim()?t:D.get(e)||t}function P(e){const t=e.value;return!(t&&t.trim())&&D.has(e)}const z={attr:e=>N.includes(e),idName:e=>!/^[0-9]+.*/i.test(e),className:S};function U(e,t){const o=C(e);if(o)try{const r=n(e,{...z,root:o}),l=`${function(e,t){const n=E(e);try{if(1===t.querySelectorAll(n).length)return n}catch(e){}return U(e,t)}(o,t)} ${r}`,s=t.querySelectorAll(l);if(1===s.length&&s[0]===e)return l}catch(e){}return n(e,{...z,root:t})}function Y(e,t){let n=null;try{n=T(e)}catch(e){}if(!n)try{n=U(e,t)}catch(e){}return n}function F(e,t){const n=["focus","highlight","editable","caret"],o=Array.from(e.classList||[]).filter((e=>{const t=e.toLowerCase();return!n.some((e=>t.includes(e)))&&S(e)}));for(const n of o){const o="."+n;try{const n=t.querySelectorAll(o);if(1===n.length&&n[0]===e)return o}catch(e){}}return null}function H(e,t,n,o){let r=!1,l=null,s=null;e&&e.composed&&e.composedPath&&(s=e.composedPath());let i=-1;s&&s.length>0&&(i=s.findIndex((e=>e instanceof ShadowRoot))),i>-1?(l=s[0],function(e,t,n,o){for(const r of e)r instanceof ShadowRoot&&!W.has(r)&&(W.add(r),r.addEventListener("change",(e=>{e.target&&e.target.getRootNode()===r&&H(e,t,n,o)}),!0))}(s,t,n,o)):l=e.target;const a=i>-1||!!(l&&l.getRootNode&&l.getRootNode()instanceof ShadowRoot),c=e.type;let u=null,f=null;if(!l||!l.getAttribute)return;u=l.nodeName.toLowerCase(),f=l.getAttribute("type");const d="input"===u&&("checkbox"===l.type||"radio"===l.type);let m=null;try{m=function(e){let t=null;try{if(t=e.nodeName&&"string"==typeof e.nodeName?e.nodeName.toLowerCase():null,!t)return e;if(["input","button","a","textarea","select","option","progress"].includes(t))return e;const n=e.closest("button,a");if(n)return n;const o=e.closest("svg");if(o&&(e=o),e.matches(N.map((e=>`[${e}]`)).join(",")))return e;if(e.parentNode.matches(N.map((e=>`[${e}]`)).join(",")))return e.parentNode}catch(e){}return e}(l)}catch(e){}const h=Y(m,t);if(h&&"html"===h.toLowerCase()&&("keydown"!==c||!R))return;let p=null;try{p=L(m,t)}catch(e){}a&&(p="/html/node/shadow");let g={timestamp:(new Date).getTime(),css:h||l,xpath:p||""},y={};if("click"===c){if(x.click=l,M&&M.element!==l&&o){const e=P(M.element),n={...M.oEventBase,type:e?"bfill_value":"fill_value",value:I(M.element),frame:M.frame};if(e){const e=F(M.element,t);e&&(n.css=e)}o({messageType:"events",event:{...n}}),D.delete(M.element),q=M.element,M=null}if(R&&j.length>0&&o){const e={...R.oEventBase,type:"bfill_value",value:j,frame:R.frame};R.shadow_host_css&&(e.shadow_host_css=R.shadow_host_css),o({messageType:"events",event:{...e}})}if(j="",R=null,!(x.return!==x.change||l===x.return||"input"!==u&&"button"!==u||"submit"!==f&&"image"!==f||null===x.return))return;if(d)return;if("select"===u||("option"===u||"optgroup"===u)&&l.closest("select"))return;let r=l;const c=l.closest&&l.closest("label");if(c){const e=l.closest("a, button, input, select, textarea"),n=!e||!c.contains(e),o=c.control;if(n&&o&&"input"===o.nodeName.toLowerCase()&&("checkbox"===o.type||"radio"===o.type)){const e=Y(c,t);if(e){r=c;let n="";try{n=L(c,t)||""}catch(e){}g={...g,css:e,xpath:a?g.xpath:n}}}}let m="click";if(function(e){if("canvas"===e.nodeName.toLowerCase())return!0;if(e.shadowRoot)try{if(e.shadowRoot.querySelector("canvas"))return!0}catch(e){}for(const t of e.children||[])if(t.shadowRoot)try{if(t.shadowRoot.querySelector("canvas"))return!0}catch(e){}return!1}(l)){const t=l.getBoundingClientRect(),n={x:e.clientX-t.left,y:e.clientY-t.top,width:t.width,height:t.height};g={...g,coords:n},m="bclick"}if(y={...g,type:m,value:(r.value||r.textContent||"").trim().substr(0,20).replace(/\n/gi,""),frame:n},B={element:r,timestamp:g.timestamp},"bclick"===m&&i>-1&&s){const e=s[i].host;if(e){let n=null;try{n=T(e)}catch(e){}if(!n)try{n=U(e,t)}catch(e){}n&&(y.shadow_host_css=n)}}"bclick"===m&&(R={element:l,oEventBase:{...g},frame:n,shadow_host_css:y.shadow_host_css||null})}else if("dblclick"===c)x.dblclick=l,y={...g,type:"dblclick",value:(l.value||l.textContent||"").trim().substr(0,20).replace(/\n/gi,""),frame:n};else if("contextmenu"===c)x.contextmenu=l,y={...g,type:"contextmenu",value:(l.value||l.textContent||"").trim().substr(0,20).replace(/\n/gi,""),frame:n};else if("focus"===c);else if("mouseover"===c)O&&(clearTimeout(O),O=!1),O=setTimeout((()=>{O=!1,x.mouseover=l,y={...g,type:"mouseover",value:(l.value||l.textContent||"").trim().substr(0,20).replace(/\n/gi,""),frame:n},y&&y.type&&o&&o({messageType:"events",event:{...y}})}),500);else if("mouseout"===c);else if("keydown"===c){if(x.keydown=l,["input","textarea"].indexOf(u)>-1&&!d){if(_[l]=l.value,M={element:l,oEventBase:{...g},frame:n},e.key&&1===e.key.length){const t=D.get(l)||"";D.set(l,t+e.key)}else if("Backspace"===e.key){const e=D.get(l)||"";e.length>0&&D.set(l,e.slice(0,-1))}}else R&&(e.key&&1===e.key.length?j+=e.key:"Backspace"===e.key&&j.length>0&&(j=j.slice(0,-1)));if("input"===u&&13===e.keyCode){if(r=!0,x.return=l,!d){const e=P(l);if(y={...g,type:e?"bfill_value":"fill_value",value:I(l),frame:n},e){const e=F(l,t);e&&(y.css=e)}D.delete(l)}}else if(R&&13===e.keyCode){if(j.length>0&&o){const e={...R.oEventBase,type:"bfill_value",value:j,frame:R.frame};R.shadow_host_css&&(e.shadow_host_css=R.shadow_host_css),o({messageType:"events",event:{...e}}),j=""}if(o){const e={...R.oEventBase,timestamp:(new Date).getTime(),type:"press_key",value:13,frame:R.frame};o({messageType:"events",event:{...e}})}R=null}}else if("blur"===c){if(x.blur=l,l===q)return void(q=null);if(M&&M.element===l&&(M=null),["input","textarea"].indexOf(u)>-1&&!d){if(_[l]=l.value,l===x.return)return void(x.return=null);if("input"===u&&("submit"===l.type||"button"===l.type||"image"===l.type))return;const e=P(l);if(y={...g,type:e?"bfill_value":"fill_value",value:I(l),frame:n},e){const e=F(l,t);e&&(y.css=e)}D.delete(l)}}else if("change"===c)if(x.change=l,"input"===u){if("checkbox"===f||"radio"===f){if(B&&B.element!==l&&g.timestamp-B.timestamp<100)return;y={...g,type:"change",subtype:"check",checked:l.checked,frame:n}}}else if("select"===u)if(l.multiple){const e=[];for(let t=0;tg(e,u)))));else if(t===n.Two)e=e.slice(0,1),u&&(e=e.concat(e.filter(y).map((e=>g(e,u)))));else if(t===n.One){const[t]=e=e.slice(0,1);u&&y(t)&&(e=[g(t,u)])}for(const t of e)t.level=c;if(s.push(e),s.length>=r.seedMinLength&&(l=i(s,o),l))break;a=a.parentElement,c++}return l||(l=i(s,o)),l}function i(e,t){const n=N(b(e));if(n.length>r.threshold)return t?t():null;for(const e of n)if(u(e))return e;return null}function a(e){let t=e[0],n=t.name;for(let o=1;o ${n}`:`${e[o].name} ${n}`,t=e[o]}return n}function c(e){return e.map((e=>e.penalty)).reduce(((e,t)=>e+t),0)}function u(e){switch(l.querySelectorAll(a(e)).length){case 0:throw new Error(`Can't select any node with this selector: ${a(e)}`);case 1:return!0;default:return!1}}function f(e){const t=e.getAttribute("id");return t&&r.idName(t)?{name:`#${A(t,{isIdentifier:!0})}`,penalty:0}:null}function d(e){return Array.from(e.attributes).filter((e=>r.attr(e.name,e.value))).map((e=>({name:"["+A(e.name,{isIdentifier:!0})+'="'+A(e.value)+'"]',penalty:.5})))}function m(e){return Array.from(e.classList).filter(r.className).map((e=>({name:"."+A(e,{isIdentifier:!0}),penalty:1})))}function h(e){const t=e.tagName.toLowerCase();return r.tagName(t)?{name:t,penalty:2}:null}function p(e){const t=e.parentNode;if(!t)return null;let n=t.firstChild;if(!n)return null;let o=0;for(;n&&(n.nodeType===Node.ELEMENT_NODE&&o++,n!==e);)n=n.nextSibling;return o}function g(e,t){return{name:e.name+`:nth-child(${t})`,penalty:e.penalty+1}}function y(e){return"html"!==e.name&&!e.name.startsWith("#")}function w(...e){const t=e.filter(v);return 0c(e)-c(t)))}function $(e,t){return l.querySelector(a(e))===t}(o=n||(n={}))[o.All=0]="All",o[o.Two=1]="Two",o[o.One=2]="One";const S=/[ -,\.\/:-@\[-\^`\{-~]/,E=/[ -,\.\/:-@\[\]\^`\{-~]/,C=/(^|\\+)?(\\[A-F0-9]{1,6})\x20(?![a-fA-F0-9\x20])/g,k={escapeEverything:!1,isIdentifier:!1,quotes:"single",wrap:!1};function A(e,t={}){const n=Object.assign(Object.assign({},k),t);"single"!==n.quotes&&"double"!==n.quotes&&(n.quotes="single");const o="double"==n.quotes?'"':"'",r=n.isIdentifier,l=e.charAt(0);let s="",i=0;for(const t=e.length;ic||126=c&&i!0,className:()=>!0,tagName:()=>!0,attr:()=>!1,seedMinLength:1,optimizedMinLength:2,threshold:1e3,maxNumberOfTries:1e4};r=Object.assign(Object.assign({},T),t),l=function(e,t){return e.nodeType===Node.DOCUMENT_NODE?e:e===t.root?e.ownerDocument:e}(r.root,T);let L=s(e,n.All,(()=>s(e,n.Two,(()=>s(e,n.One)))));if(L){const t=N(function*e(t,n,o={counter:0,visited:new Map}){if(2r.optimizedMinLength)for(let l=1;lr.maxNumberOfTries)return;o.counter+=1;const s=[...t];s.splice(l,1);const i=a(s);if(o.visited.has(i))return;u(s)&&$(s,n)&&(yield s,o.visited.set(i,!0),yield*e(s,n,o))}}(L,e));return 0{if(!r.isRecording)return;let l=!1,s=null;function i(e){e&&"mouseover"===e.type&&!l||H(e,window.document,null,(e=>{window!==window.top?window.parent.postMessage({source:"event-from-iframe",obj:{...e},framePath:[]},"*"):chrome.runtime.sendMessage(e)}))}window===window.top&&window.addEventListener("message",(e=>{if(e.data&&e.data.source&&"event-from-iframe"===e.data.source){const t=document.querySelectorAll("iframe, frame");let n=!1;for(const o of t)if(o.contentWindow===e.source){n=!0;break}if(!n)return;const o={...e.data.obj},r=e.data.framePath||[];for(const n of t)if(n.contentWindow===e.source){let e=null;try{e=T(n)}catch(e){}if(!e)try{e=X(n,{root:window.document,attr:e=>N.includes(e),idName:e=>!/^[0-9]+.*/i.test(e),className:S})}catch(e){}if(e&&o.event){const t=[e,...r];o.event.frame=t.join(" >>> "),chrome.runtime.sendMessage(o)}break}}})),window!==window.top&&window.addEventListener("message",(e=>{if(e.data&&e.data.source&&"event-from-iframe"===e.data.source){const t=document.querySelectorAll("iframe, frame");let n=!1;for(const o of t)if(o.contentWindow===e.source){n=!0;break}if(!n)return;const o=e.data.framePath||[];for(const n of t)if(n.contentWindow===e.source){let t=null;try{t=T(n)}catch(e){}if(!t)try{t=X(n,{root:window.document,attr:e=>N.includes(e),idName:e=>!/^[0-9]+.*/i.test(e),className:S})}catch(e){}t&&o.push(t),window.parent.postMessage({source:"event-from-iframe",obj:e.data.obj,framePath:o},"*");break}}})),r.isRecording&&(t=!0,e=document.title,function(){if(!t)return document.title=`${e}`,o=!1,void(n&&(clearInterval(n),n=!1));n=setInterval((()=>{o?(document.title=`${e}`,o=!1):(document.title=`🔴 ${e}`,o=!0)}),1e3)}(),window===window.top&&chrome.runtime.sendMessage({messageType:"events",event:{type:"goto",timestamp:(new Date).getTime(),windowWidth:window.innerWidth,windowHeight:window.innerHeight,url:window.location.href}}),document.addEventListener("click",i,!0),document.addEventListener("mouseover",i,!0),document.addEventListener("dblclick",i,!0),document.addEventListener("contextmenu",i,!0),document.addEventListener("keydown",i,!0),document.addEventListener("blur",i,!0),document.addEventListener("change",i,!0));const a={attributes:!1,childList:!0,subtree:!0},c=new MutationObserver((async(e,t)=>{l=!0,s&&(clearTimeout(s),s=null),s=setTimeout((()=>{l=!1}),200)}));document.body&&c.observe(document.body,a)}))}()})(); \ No newline at end of file diff --git a/test-build-firefox/manifest.json b/test-build-firefox/manifest.json index 772e6dd..ed65681 100644 --- a/test-build-firefox/manifest.json +++ b/test-build-firefox/manifest.json @@ -1,6 +1,6 @@ { "name": "Snyk API & Web Sequence Recorder", - "version": "1.2.3", + "version": "1.2.5", "browser_specific_settings": { "gecko": { "id": "sequence-recorder@probely.com", @@ -44,6 +44,7 @@ ], "permissions": [ "storage", + "webNavigation", "https://*/*", "http://*/*" ], diff --git a/test-build/background.bundle.js b/test-build/background.bundle.js index 45ca07a..fc2c211 100644 --- a/test-build/background.bundle.js +++ b/test-build/background.bundle.js @@ -1 +1 @@ -(()=>{"use strict";var e={p:"/"};e.p,e.p;!function(){let e=!1,t=[];function r(){chrome.storage.local.set({recordingEvents:t,startURLDimensionsUpdated:e,recordingTimestamp:Date.now()})}chrome.storage.local.get(["recordingEvents","startURLDimensionsUpdated","recordingTimestamp"],(r=>{const n=Date.now();r.recordingTimestamp&&n-r.recordingTimestamp>6e5?(chrome.storage.local.remove(["recordingEvents","startURLDimensionsUpdated","recordingTimestamp"]),t=[],e=!1):(r.recordingEvents&&Array.isArray(r.recordingEvents)&&(t=r.recordingEvents),void 0!==r.startURLDimensionsUpdated&&(e=r.startURLDimensionsUpdated))})),chrome.storage.sync.get(["isRecording"],(e=>{e.isRecording?(chrome.action||chrome.browserAction).setBadgeText({text:"🔴"},(()=>{})):(chrome.action||chrome.browserAction).setBadgeText({text:""},(()=>{}))})),chrome.runtime.onMessage.addListener(((n,s,o)=>{if("events"===n.messageType){if(!e&&(e=!0,1===t.length&&"goto"===n.event.type&&(t[0].windowWidth=n.event.windowWidth,t[0].windowHeight=n.event.windowHeight,t[0].url===n.event.url)))return void r();t.push(n.event),r()}else"start"===n.messageType?(e=!1,t=[n.event],r()):"clear"===n.messageType?(e=!1,t=[],chrome.storage.local.remove(["recordingEvents","startURLDimensionsUpdated","recordingTimestamp"])):"give_recording_data"===n.messageType&&chrome.runtime.sendMessage({messageType:"recording_data",recordingData:[].concat(t)})}))}()})(); \ No newline at end of file +(()=>{"use strict";var e={p:"/"};e.p,e.p;!function(){let e=!1,r=[],o=[],t=[];const s=["recordingEvents","startURLDimensionsUpdated","recordingTabIds","popupSourceTabIds","recordingTimestamp"],n=new Promise((n=>{chrome.storage.local.get(s,(i=>{const a=Date.now();i.recordingTimestamp&&a-i.recordingTimestamp>6e5?(chrome.storage.local.remove(s),r=[],e=!1,o=[],t=[]):(i.recordingEvents&&Array.isArray(i.recordingEvents)&&(r=i.recordingEvents),void 0!==i.startURLDimensionsUpdated&&(e=i.startURLDimensionsUpdated),Array.isArray(i.recordingTabIds)&&(o=i.recordingTabIds),Array.isArray(i.popupSourceTabIds)&&(t=i.popupSourceTabIds)),n()}))}));function i(){chrome.storage.local.set({recordingEvents:r,startURLDimensionsUpdated:e,recordingTabIds:o,popupSourceTabIds:t,recordingTimestamp:Date.now()})}chrome.storage.sync.get(["isRecording"],(e=>{e.isRecording?(chrome.action||chrome.browserAction).setBadgeText({text:"🔴"},(()=>{})):(chrome.action||chrome.browserAction).setBadgeText({text:""},(()=>{}))})),chrome.runtime.onMessage.addListener(((a,d,c)=>{n.then((()=>function(n,a){if("events"===n.messageType){if(a.tab&&-1===o.indexOf(a.tab.id)&&(o.push(a.tab.id),t.indexOf(a.tab.id)>-1&&r.length&&(r[0].allow_popups=!0)),!e&&(e=!0,1===r.length&&"goto"===n.event.type&&(r[0].windowWidth=n.event.windowWidth,r[0].windowHeight=n.event.windowHeight,r[0].url===n.event.url)))return void i();r.push(n.event),i()}else"start"===n.messageType?(e=!1,r=[n.event],o=[],t=[],i()):"clear"===n.messageType?(e=!1,r=[],o=[],t=[],chrome.storage.local.remove(s)):"give_recording_data"===n.messageType&&chrome.runtime.sendMessage({messageType:"recording_data",recordingData:[].concat(r)})}(a,d)))})),chrome.webNavigation.onCreatedNavigationTarget.addListener((e=>{n.then((()=>{chrome.storage.sync.get(["isRecording"],(s=>{if(s.isRecording&&r.length&&!r[0].allow_popups){if(o.indexOf(e.sourceTabId)>-1)r[0].allow_popups=!0;else{if(-1!==t.indexOf(e.sourceTabId))return;t.push(e.sourceTabId)}i()}}))}))}))}()})(); \ No newline at end of file diff --git a/test-build/contentScript.bundle.js b/test-build/contentScript.bundle.js index 46eef33..3d55e3e 100644 --- a/test-build/contentScript.bundle.js +++ b/test-build/contentScript.bundle.js @@ -1 +1 @@ -(()=>{"use strict";let e,t;function n(n,r){if(n.nodeType!==Node.ELEMENT_NODE)throw new Error("Can't generate CSS selector for non-element node type.");if("html"===n.tagName.toLowerCase())return"html";const i={root:document.body,idName:e=>!0,className:e=>!0,tagName:e=>!0,attr:(e,t)=>!1,seedMinLength:1,optimizedMinLength:2,threshold:1e3,maxNumberOfTries:1e4};e={...i,...r},t=function(e,t){if(e.nodeType===Node.DOCUMENT_NODE)return e;if(e===t.root)return e.ownerDocument;return e}(e.root,i);let s=o(n,"all",(()=>o(n,"two",(()=>o(n,"one",(()=>o(n,"none")))))));if(s){const e=w(v(s,n));return e.length>0&&(s=e[0]),l(s)}throw new Error("Selector was not found.")}function o(t,n,o){let l=null,i=[],s=t,g=0;for(;s;){let t=p(a(s))||p(...c(s))||p(...u(s))||p(f(s))||[{name:"*",penalty:3}];const y=d(s);if("all"==n)y&&(t=t.concat(t.filter(h).map((e=>m(e,y)))));else if("two"==n)t=t.slice(0,1),y&&(t=t.concat(t.filter(h).map((e=>m(e,y)))));else if("one"==n){const[e]=t=t.slice(0,1);y&&h(e)&&(t=[m(e,y)])}else"none"==n&&(t=[{name:"*",penalty:3}],y&&(t=[m(t[0],y)]));for(let e of t)e.level=g;if(i.push(t),i.length>=e.seedMinLength&&(l=r(i,o),l))break;s=s.parentElement,g++}return l||(l=r(i,o)),!l&&o?o():l}function r(t,n){const o=w(y(t));if(o.length>e.threshold)return n?n():null;for(let e of o)if(s(e))return e;return null}function l(e){let t=e[0],n=t.name;for(let o=1;o ${n}`:`${e[o].name} ${n}`,t=e[o]}return n}function i(e){return e.map((e=>e.penalty)).reduce(((e,t)=>e+t),0)}function s(e){const n=l(e);switch(t.querySelectorAll(n).length){case 0:throw new Error(`Can't select any node with this selector: ${n}`);case 1:return!0;default:return!1}}function a(t){const n=t.getAttribute("id");return n&&e.idName(n)?{name:"#"+CSS.escape(n),penalty:0}:null}function c(t){const n=Array.from(t.attributes).filter((t=>e.attr(t.name,t.value)));return n.map((e=>({name:`[${CSS.escape(e.name)}="${CSS.escape(e.value)}"]`,penalty:.5})))}function u(t){return Array.from(t.classList).filter(e.className).map((e=>({name:"."+CSS.escape(e),penalty:1})))}function f(t){const n=t.tagName.toLowerCase();return e.tagName(n)?{name:n,penalty:2}:null}function d(e){const t=e.parentNode;if(!t)return null;let n=t.firstChild;if(!n)return null;let o=0;for(;n&&(n.nodeType===Node.ELEMENT_NODE&&o++,n!==e);)n=n.nextSibling;return o}function m(e,t){return{name:e.name+`:nth-child(${t})`,penalty:e.penalty+1}}function h(e){return"html"!==e.name&&!e.name.startsWith("#")}function p(...e){const t=e.filter(g);return t.length>0?t:null}function g(e){return null!=e}function*y(e,t=[]){if(e.length>0)for(let n of e[0])yield*y(e.slice(1,e.length),t.concat(n));else yield t}function w(e){return[...e].sort(((e,t)=>i(e)-i(t)))}function*v(t,n,o={counter:0,visited:new Map}){if(t.length>2&&t.length>e.optimizedMinLength)for(let r=1;re.maxNumberOfTries)return;o.counter+=1;const i=[...t];i.splice(r,1);const a=l(i);if(o.visited.has(a))return;s(i)&&b(i,n)&&(yield i,o.visited.set(a,!0),yield*v(i,n,o))}}function b(e,n){return t.querySelector(l(e))===n}function N(e,t){let n="",o=null,r=!1;try{if(o=e.nodeName&&"string"==typeof e.nodeName?e.nodeName.toLowerCase():null,!o)return null;n=`${o}`,e.getAttribute("id")&&(n=`${n}#${CSS.escape(e.getAttribute("id"))}`,r=!0);const t=["data-test-id","data-testid","data-test","data-cy"];if(e.matches(t.map((e=>`[${e}]`)).join(",")))for(const o of t){const t=e.getAttribute(o);if(t){n=`${n}[${o}="${CSS.escape(t)}"]`;break}}if(["input","button","textarea","select","option"].includes(o)){e.getAttribute("type")&&(n=`${n}[type="${CSS.escape(e.getAttribute("type"))}"]`),e.getAttribute("name")&&(n=`${n}[name="${CSS.escape(e.getAttribute("name"))}"]`);const t=e.closest("form");if(t){let e="form";t.getAttribute("id")&&(e=`${e}#${CSS.escape(t.getAttribute("id"))}`),n=`${e} ${n}`}}const l=e.getAttribute("aria-label");if(l&&!r&&(n=`${n}[aria-label="${CSS.escape(l)}"]`),n){const t=e.closest("body");if(t)try{1!==t.querySelectorAll(n).length&&(n="")}catch(e){n=""}}}catch(e){}return n}function E(e,t){let n,o=e,r=[];for(;o;){let e=0,n=o.previousElementSibling;for(;n;)n.nodeName===o.nodeName&&e++,n=n.previousElementSibling;let l=o.nodeName.toLowerCase();e>0&&(l=l+"["+(e+1)+"]"),o!==t&&r.unshift(l),o=o.parentNode}if(n="/"+r.join("/"),function(e,t){const n=t.evaluate(e,t,null,XPathResult.ANY_TYPE,null),o=n.iterateNext();return o}(n,t)===e)return n}const k={},S={keydown:null,return:null,blur:null,change:null,click:null,dblclick:null,contextmenu:null,focus:null,mouseover:null,mouseout:null};let C=!1,A=null,L=null;const T=new WeakMap;let $=null,_="";function x(e){const t=e.value;return t&&t.trim()?t:T.get(e)||t}function M(e){const t=e.value;return!(t&&t.trim())&&T.has(e)}function O(e,t){const n=["focus","highlight","editable","caret"],o=Array.from(e.classList||[]).filter((e=>{const t=e.toLowerCase();return!n.some((e=>t.includes(e)))&&!/^[0-9]/.test(e)}));for(const n of o){const o="."+n;try{const n=t.querySelectorAll(o);if(1===n.length&&n[0]===e)return o}catch(e){}}return null}function q(e,t){let n;var o;let r,l;function i(e,t,o){let l=null;const i=[];let a=e,c=0;for(;a&&a!==r.root.parentElement;){let e=w(f(a))||w(...d(a))||w(...m(a))||w(h(a))||[{name:"*",penalty:3}];const u=p(a);if(t===n.All)u&&(e=e.concat(e.filter(y).map((e=>g(e,u)))));else if(t===n.Two)e=e.slice(0,1),u&&(e=e.concat(e.filter(y).map((e=>g(e,u)))));else if(t===n.One){const[t]=e=e.slice(0,1);u&&y(t)&&(e=[g(t,u)])}for(const t of e)t.level=c;if(i.push(e),i.length>=r.seedMinLength&&(l=s(i,o),l))break;a=a.parentElement,c++}return l||(l=s(i,o)),l}function s(e,t){const n=N(b(e));if(n.length>r.threshold)return t?t():null;for(const e of n)if(u(e))return e;return null}function a(e){let t=e[0],n=t.name;for(let o=1;o ${n}`:`${e[o].name} ${n}`,t=e[o]}return n}function c(e){return e.map((e=>e.penalty)).reduce(((e,t)=>e+t),0)}function u(e){switch(l.querySelectorAll(a(e)).length){case 0:throw new Error(`Can't select any node with this selector: ${a(e)}`);case 1:return!0;default:return!1}}function f(e){const t=e.getAttribute("id");return t&&r.idName(t)?{name:`#${L(t,{isIdentifier:!0})}`,penalty:0}:null}function d(e){return Array.from(e.attributes).filter((e=>r.attr(e.name,e.value))).map((e=>({name:"["+L(e.name,{isIdentifier:!0})+'="'+L(e.value)+'"]',penalty:.5})))}function m(e){return Array.from(e.classList).filter(r.className).map((e=>({name:"."+L(e,{isIdentifier:!0}),penalty:1})))}function h(e){const t=e.tagName.toLowerCase();return r.tagName(t)?{name:t,penalty:2}:null}function p(e){const t=e.parentNode;if(!t)return null;let n=t.firstChild;if(!n)return null;let o=0;for(;n&&(n.nodeType===Node.ELEMENT_NODE&&o++,n!==e);)n=n.nextSibling;return o}function g(e,t){return{name:e.name+`:nth-child(${t})`,penalty:e.penalty+1}}function y(e){return"html"!==e.name&&!e.name.startsWith("#")}function w(...e){const t=e.filter(v);return 0c(e)-c(t)))}function E(e,t){return l.querySelector(a(e))===t}(o=n||(n={}))[o.All=0]="All",o[o.Two=1]="Two",o[o.One=2]="One";const k=/[ -,\.\/:-@\[-\^`\{-~]/,S=/[ -,\.\/:-@\[\]\^`\{-~]/,C=/(^|\\+)?(\\[A-F0-9]{1,6})\x20(?![a-fA-F0-9\x20])/g,A={escapeEverything:!1,isIdentifier:!1,quotes:"single",wrap:!1};function L(e,t={}){const n=Object.assign(Object.assign({},A),t);"single"!==n.quotes&&"double"!==n.quotes&&(n.quotes="single");const o="double"==n.quotes?'"':"'",r=n.isIdentifier,l=e.charAt(0);let i="",s=0;for(const t=e.length;sc||126=c&&s!0,className:()=>!0,tagName:()=>!0,attr:()=>!1,seedMinLength:1,optimizedMinLength:2,threshold:1e3,maxNumberOfTries:1e4};r=Object.assign(Object.assign({},T),t),l=function(e,t){return e.nodeType===Node.DOCUMENT_NODE?e:e===t.root?e.ownerDocument:e}(r.root,T);let $=i(e,n.All,(()=>i(e,n.Two,(()=>i(e,n.One)))));if($){const t=N(function*e(t,n,o={counter:0,visited:new Map}){if(2r.optimizedMinLength)for(let l=1;lr.maxNumberOfTries)return;o.counter+=1;const i=[...t];i.splice(l,1);const s=a(i);if(o.visited.has(s))return;u(i)&&E(i,n)&&(yield i,o.visited.set(s,!0),yield*e(i,n,o))}}($,e));return 0{if(!l.isRecording)return;let i=!1,s=null;function a(e){e&&"mouseover"===e.type&&!i||function(e,t,o,r){let l=!1,i=null,s=null;e&&e.composed&&e.composedPath&&(s=e.composedPath());let a=-1;s&&s.length>0&&(a=s.findIndex((e=>e instanceof ShadowRoot))),i=a>-1?s[0]:e.target;const c=e.type;let u=null,f=null;if(!i||!i.getAttribute)return;u=i.nodeName.toLowerCase(),f=i.getAttribute("type");let d=null,m=null;try{m=function(e){let t=null;try{if(t=e.nodeName&&"string"==typeof e.nodeName?e.nodeName.toLowerCase():null,!t)return e;if(["input","button","a","textarea","select","option","progress"].includes(t))return e;const n=e.closest("button,a");if(n)return n;const o=e.closest("svg");o&&(e=o);const r=["data-test-id","data-testid","data-test","data-cy"];if(e.matches(r.map((e=>`[${e}]`)).join(",")))return e;if(e.parentNode.matches(r.map((e=>`[${e}]`)).join(",")))return e.parentNode}catch(e){}return e}(i),d=N(m)}catch(e){}if(!d)try{d=n(m,{root:t,idName:e=>!/^[0-9]+.*/i.test(e),className:e=>!e.includes("focus")&&!e.includes("highlight")&&!/^[0-9]+.*/i.test(e)})}catch(e){}if(d&&"html"===d.toLowerCase()&&("keydown"!==c||!$))return;let h=null;try{h=E(m,t)}catch(e){}a>-1&&(h="/html/node/shadow");let p={timestamp:(new Date).getTime(),css:d||i,xpath:h||""},g={};if("click"===c){if(S.click=i,A&&A.element!==i&&r){const e=M(A.element),n={...A.oEventBase,type:e?"bfill_value":"fill_value",value:x(A.element),frame:A.frame};if(e){const e=O(A.element,t);e&&(n.css=e)}r({messageType:"events",event:{...n}}),T.delete(A.element),L=A.element,A=null}if($&&_.length>0&&r){const e={...$.oEventBase,type:"bfill_value",value:_,frame:$.frame};$.shadow_host_css&&(e.shadow_host_css=$.shadow_host_css),r({messageType:"events",event:{...e}})}if(_="",$=null,!(S.return!==S.change||i===S.return||"input"!==u&&"button"!==u||"submit"!==f&&"image"!==f||null===S.return))return;if("input"===u&&("checkbox"===f||"radio"===f))return;const l=i.closest&&i.closest("label");if(l){const e=i.closest("a, button, input, select, textarea");if(!e||!l.contains(e)){const e=l.getAttribute("for");if(e){const t=i.getRootNode(),n=(t&&t.getElementById?t:document).getElementById(e);if(n&&"input"===n.nodeName.toLowerCase()&&("checkbox"===n.type||"radio"===n.type))return}if(l.querySelector('input[type="checkbox"], input[type="radio"]'))return}}let c="click";if(function(e){if("canvas"===e.nodeName.toLowerCase())return!0;if(e.shadowRoot)try{if(e.shadowRoot.querySelector("canvas"))return!0}catch(e){}for(const t of e.children||[])if(t.shadowRoot)try{if(t.shadowRoot.querySelector("canvas"))return!0}catch(e){}return!1}(i)){const t=i.getBoundingClientRect(),n={x:e.clientX-t.left,y:e.clientY-t.top,width:t.width,height:t.height};p={...p,coords:n},c="bclick"}if(g={...p,type:c,value:(i.value||i.textContent||"").trim().substr(0,20).replace(/\n/gi,""),frame:o},"bclick"===c&&a>-1&&s){const e=s[a].host;if(e){let o=null;try{o=N(e)}catch(e){}if(!o)try{o=n(e,{root:t,idName:e=>!/^[0-9]+.*/i.test(e),className:e=>!e.includes("focus")&&!e.includes("highlight")&&!/^[0-9]+.*/i.test(e)})}catch(e){}o&&(g.shadow_host_css=o)}}"bclick"===c&&($={element:i,oEventBase:{...p},frame:o,shadow_host_css:g.shadow_host_css||null})}else if("dblclick"===c)S.dblclick=i,g={...p,type:"dblclick",value:(i.value||i.textContent||"").trim().substr(0,20).replace(/\n/gi,""),frame:o};else if("contextmenu"===c)S.contextmenu=i,g={...p,type:"contextmenu",value:(i.value||i.textContent||"").trim().substr(0,20).replace(/\n/gi,""),frame:o};else if("focus"===c);else if("mouseover"===c)C&&(clearTimeout(C),C=!1),C=setTimeout((()=>{C=!1,S.mouseover=i,g={...p,type:"mouseover",value:(i.value||i.textContent||"").trim().substr(0,20).replace(/\n/gi,""),frame:o},g&&g.type&&r&&r({messageType:"events",event:{...g}})}),500);else if("mouseout"===c);else if("keydown"===c){if(S.keydown=i,["input","textarea"].indexOf(u)>-1){if(k[i]=i.value,A={element:i,oEventBase:{...p},frame:o},e.key&&1===e.key.length){const t=T.get(i)||"";T.set(i,t+e.key)}else if("Backspace"===e.key){const e=T.get(i)||"";e.length>0&&T.set(i,e.slice(0,-1))}}else $&&(e.key&&1===e.key.length?_+=e.key:"Backspace"===e.key&&_.length>0&&(_=_.slice(0,-1)));if("input"===u&&13===e.keyCode){l=!0,S.return=i;const e=M(i);if(g={...p,type:e?"bfill_value":"fill_value",value:x(i),frame:o},e){const e=O(i,t);e&&(g.css=e)}T.delete(i)}else if($&&13===e.keyCode){if(_.length>0&&r){const e={...$.oEventBase,type:"bfill_value",value:_,frame:$.frame};$.shadow_host_css&&(e.shadow_host_css=$.shadow_host_css),r({messageType:"events",event:{...e}}),_=""}if(r){const e={...$.oEventBase,timestamp:(new Date).getTime(),type:"press_key",value:13,frame:$.frame};r({messageType:"events",event:{...e}})}$=null}}else if("blur"===c){if(S.blur=i,i===L)return void(L=null);if(A&&A.element===i&&(A=null),["input","textarea"].indexOf(u)>-1){if(k[i]=i.value,i===S.return)return void(S.return=null);if("input"===u&&("submit"===i.type||"button"===i.type||"image"===i.type))return;const e=M(i);if(g={...p,type:e?"bfill_value":"fill_value",value:x(i),frame:o},e){const e=O(i,t);e&&(g.css=e)}T.delete(i)}}else if("change"===c)if(S.change=i,"input"===u)"checkbox"!==f&&"radio"!==f||(g={...p,type:"change",subtype:"check",checked:i.checked,frame:o});else if("select"===u)if(i.multiple){const e=[];for(let t=0;t{window!==window.top?window.parent.postMessage({source:"event-from-iframe",obj:{...e},framePath:[]},"*"):chrome.runtime.sendMessage(e)}))}window===window.top&&window.addEventListener("message",(e=>{if(e.data&&e.data.source&&"event-from-iframe"===e.data.source){const t=document.querySelectorAll("iframe, frame");let n=!1;for(const o of t)if(o.contentWindow===e.source){n=!0;break}if(!n)return;const o={...e.data.obj},r=e.data.framePath||[];for(const n of t)if(n.contentWindow===e.source){let e=null;try{e=N(n)}catch(e){}if(!e)try{e=q(n,{root:window.document,idName:e=>!/^[0-9]+.*/i.test(e),className:e=>!e.includes("focus")&&!e.includes("highlight")&&!/^[0-9]+.*/i.test(e)})}catch(e){}if(e&&o.event){const t=[e,...r];o.event.frame=t.join(" >>> "),chrome.runtime.sendMessage(o)}break}}})),window!==window.top&&window.addEventListener("message",(e=>{if(e.data&&e.data.source&&"event-from-iframe"===e.data.source){const t=document.querySelectorAll("iframe, frame");let n=!1;for(const o of t)if(o.contentWindow===e.source){n=!0;break}if(!n)return;const o=e.data.framePath||[];for(const n of t)if(n.contentWindow===e.source){let t=null;try{t=N(n)}catch(e){}if(!t)try{t=q(n,{root:window.document,idName:e=>!/^[0-9]+.*/i.test(e),className:e=>!e.includes("focus")&&!e.includes("highlight")&&!/^[0-9]+.*/i.test(e)})}catch(e){}t&&o.push(t),window.parent.postMessage({source:"event-from-iframe",obj:e.data.obj,framePath:o},"*");break}}})),l.isRecording&&(t=!0,e=document.title,function(){if(!t)return document.title=`${e}`,r=!1,void(o&&(clearInterval(o),o=!1));o=setInterval((()=>{r?(document.title=`${e}`,r=!1):(document.title=`🔴 ${e}`,r=!0)}),1e3)}(),window===window.top&&chrome.runtime.sendMessage({messageType:"events",event:{type:"goto",timestamp:(new Date).getTime(),windowWidth:window.innerWidth,windowHeight:window.innerHeight,url:window.location.href}}),document.addEventListener("click",a,!0),document.addEventListener("mouseover",a,!0),document.addEventListener("dblclick",a,!0),document.addEventListener("contextmenu",a,!0),document.addEventListener("keydown",a,!0),document.addEventListener("blur",a,!0),document.addEventListener("change",a,!0));const c={attributes:!1,childList:!0,subtree:!0},u=new MutationObserver((async(e,t)=>{i=!0,s&&(clearTimeout(s),s=null),s=setTimeout((()=>{i=!1}),200)}));document.body&&u.observe(document.body,c)}))}()})(); \ No newline at end of file +(()=>{"use strict";let e,t;function n(n,r){if(n.nodeType!==Node.ELEMENT_NODE)throw new Error("Can't generate CSS selector for non-element node type.");if("html"===n.tagName.toLowerCase())return"html";const s={root:document.body,idName:e=>!0,className:e=>!0,tagName:e=>!0,attr:(e,t)=>!1,seedMinLength:1,optimizedMinLength:2,threshold:1e3,maxNumberOfTries:1e4};e={...s,...r},t=function(e,t){if(e.nodeType===Node.DOCUMENT_NODE)return e;if(e===t.root)return e.ownerDocument;return e}(e.root,s);let i=o(n,"all",(()=>o(n,"two",(()=>o(n,"one",(()=>o(n,"none")))))));if(i){const e=w(v(i,n));return e.length>0&&(i=e[0]),l(i)}throw new Error("Selector was not found.")}function o(t,n,o){let l=null,s=[],i=t,g=0;for(;i;){let t=p(a(i))||p(...c(i))||p(...u(i))||p(f(i))||[{name:"*",penalty:3}];const y=d(i);if("all"==n)y&&(t=t.concat(t.filter(h).map((e=>m(e,y)))));else if("two"==n)t=t.slice(0,1),y&&(t=t.concat(t.filter(h).map((e=>m(e,y)))));else if("one"==n){const[e]=t=t.slice(0,1);y&&h(e)&&(t=[m(e,y)])}else"none"==n&&(t=[{name:"*",penalty:3}],y&&(t=[m(t[0],y)]));for(let e of t)e.level=g;if(s.push(t),s.length>=e.seedMinLength&&(l=r(s,o),l))break;i=i.parentElement,g++}return l||(l=r(s,o)),!l&&o?o():l}function r(t,n){const o=w(y(t));if(o.length>e.threshold)return n?n():null;for(let e of o)if(i(e))return e;return null}function l(e){let t=e[0],n=t.name;for(let o=1;o ${n}`:`${e[o].name} ${n}`,t=e[o]}return n}function s(e){return e.map((e=>e.penalty)).reduce(((e,t)=>e+t),0)}function i(e){const n=l(e);switch(t.querySelectorAll(n).length){case 0:throw new Error(`Can't select any node with this selector: ${n}`);case 1:return!0;default:return!1}}function a(t){const n=t.getAttribute("id");return n&&e.idName(n)?{name:"#"+CSS.escape(n),penalty:0}:null}function c(t){const n=Array.from(t.attributes).filter((t=>e.attr(t.name,t.value)));return n.map((e=>({name:`[${CSS.escape(e.name)}="${CSS.escape(e.value)}"]`,penalty:.5})))}function u(t){return Array.from(t.classList).filter(e.className).map((e=>({name:"."+CSS.escape(e),penalty:1})))}function f(t){const n=t.tagName.toLowerCase();return e.tagName(n)?{name:n,penalty:2}:null}function d(e){const t=e.parentNode;if(!t)return null;let n=t.firstChild;if(!n)return null;let o=0;for(;n&&(n.nodeType===Node.ELEMENT_NODE&&o++,n!==e);)n=n.nextSibling;return o}function m(e,t){return{name:e.name+`:nth-child(${t})`,penalty:e.penalty+1}}function h(e){return"html"!==e.name&&!e.name.startsWith("#")}function p(...e){const t=e.filter(g);return t.length>0?t:null}function g(e){return null!=e}function*y(e,t=[]){if(e.length>0)for(let n of e[0])yield*y(e.slice(1,e.length),t.concat(n));else yield t}function w(e){return[...e].sort(((e,t)=>s(e)-s(t)))}function*v(t,n,o={counter:0,visited:new Map}){if(t.length>2&&t.length>e.optimizedMinLength)for(let r=1;re.maxNumberOfTries)return;o.counter+=1;const s=[...t];s.splice(r,1);const a=l(s);if(o.visited.has(a))return;i(s)&&b(s,n)&&(yield s,o.visited.set(a,!0),yield*v(s,n,o))}}function b(e,n){return t.querySelector(l(e))===n}const N=["data-test-id","data-testid","data-test","data-cy","data-qa","data-automation-id"],$=/^(css-[a-z0-9]+|sc-[a-z0-9]+|jss\d+)(-|$)/i;function S(e){return!(e.includes("focus")||e.includes("highlight")||/^[0-9]/.test(e)||$.test(e))}function E(e){for(const t of N){const n=e.getAttribute(t);if(n)return`[${t}="${CSS.escape(n)}"]`}return""}function C(e){let t=e.parentElement;for(let e=0;t&&e<10;e++){if(E(t))return t;t=t.parentElement}return null}function k(e){const t=e.getRootNode();return e.closest("body")||(t.nodeType===Node.DOCUMENT_NODE?t:null)}function A(e,t){const n=k(e);if(!n)return"";const o=[],r=e.getAttribute("id");r&&o.push(`${t}#${CSS.escape(r)}`);for(const n of[...N,"name","title","aria-label"]){const r=e.getAttribute(n);r&&o.push(`${t}[${n}="${CSS.escape(r)}"]`)}const l=function(e,t){const n=(e.getAttribute("src")||"").trim();if(!n||/^(about|javascript|data|blob):/i.test(n))return null;const o=n.split(/[?#]/)[0];return o?`${t}[src${o===n?"=":"^="}"${CSS.escape(o)}"]`:null}(e,t);l&&o.push(l);const s=C(e),i=s?`${E(s)} `:"";for(const t of o.map((e=>`${i}${e}`)))try{const o=n.querySelectorAll(t);if(1===o.length&&o[0]===e)return t}catch(e){}return""}function T(e,t){let n="",o=null,r=!1;try{if(o=e.nodeName&&"string"==typeof e.nodeName?e.nodeName.toLowerCase():null,!o)return null;if("frame"===o||"iframe"===o)return A(e,o);n=`${o}`,e.getAttribute("id")&&(n=`${n}#${CSS.escape(e.getAttribute("id"))}`,r=!0),n=`${n}${E(e)}`;let t=null,l="";["input","button","textarea","select","option"].includes(o)&&(e.getAttribute("type")&&(n=`${n}[type="${CSS.escape(e.getAttribute("type"))}"]`),e.getAttribute("name")&&(n=`${n}[name="${CSS.escape(e.getAttribute("name"))}"]`),t=e.closest("form"),t&&(l="form",t.getAttribute("id")&&(l=`${l}#${CSS.escape(t.getAttribute("id"))}`)));let s=l;const i=C(e);if(i){const e=E(i);s=i===t?`${l}${e}`:t&&t.contains(i)?`${l} ${e}`:l?`${e} ${l}`:e}s&&(n=`${s} ${n}`);const a=e.getAttribute("aria-label");if(a&&!r&&(n=`${n}[aria-label="${CSS.escape(a)}"]`),n){const t=k(e);if(t)try{1!==t.querySelectorAll(n).length&&(n="")}catch(e){n=""}}}catch(e){}return n}function L(e,t){let n,o=e,r=[];for(;o;){let e=0,n=o.previousElementSibling;for(;n;)n.nodeName===o.nodeName&&e++,n=n.previousElementSibling;let l=o.nodeName.toLowerCase();e>0&&(l=l+"["+(e+1)+"]"),o!==t&&r.unshift(l),o=o.parentNode}if(n="/"+r.join("/"),function(e,t){const n=t.evaluate(e,t,null,XPathResult.ANY_TYPE,null),o=n.iterateNext();return o}(n,t)===e)return n}const _={},x={keydown:null,return:null,blur:null,change:null,click:null,dblclick:null,contextmenu:null,focus:null,mouseover:null,mouseout:null};let O=!1,M=null,q=null;const D=new WeakMap;let R=null,j="",B=null;const W=new WeakSet;function I(e){const t=e.value;return t&&t.trim()?t:D.get(e)||t}function P(e){const t=e.value;return!(t&&t.trim())&&D.has(e)}const z={attr:e=>N.includes(e),idName:e=>!/^[0-9]+.*/i.test(e),className:S};function U(e,t){const o=C(e);if(o)try{const r=n(e,{...z,root:o}),l=`${function(e,t){const n=E(e);try{if(1===t.querySelectorAll(n).length)return n}catch(e){}return U(e,t)}(o,t)} ${r}`,s=t.querySelectorAll(l);if(1===s.length&&s[0]===e)return l}catch(e){}return n(e,{...z,root:t})}function Y(e,t){let n=null;try{n=T(e)}catch(e){}if(!n)try{n=U(e,t)}catch(e){}return n}function F(e,t){const n=["focus","highlight","editable","caret"],o=Array.from(e.classList||[]).filter((e=>{const t=e.toLowerCase();return!n.some((e=>t.includes(e)))&&S(e)}));for(const n of o){const o="."+n;try{const n=t.querySelectorAll(o);if(1===n.length&&n[0]===e)return o}catch(e){}}return null}function H(e,t,n,o){let r=!1,l=null,s=null;e&&e.composed&&e.composedPath&&(s=e.composedPath());let i=-1;s&&s.length>0&&(i=s.findIndex((e=>e instanceof ShadowRoot))),i>-1?(l=s[0],function(e,t,n,o){for(const r of e)r instanceof ShadowRoot&&!W.has(r)&&(W.add(r),r.addEventListener("change",(e=>{e.target&&e.target.getRootNode()===r&&H(e,t,n,o)}),!0))}(s,t,n,o)):l=e.target;const a=i>-1||!!(l&&l.getRootNode&&l.getRootNode()instanceof ShadowRoot),c=e.type;let u=null,f=null;if(!l||!l.getAttribute)return;u=l.nodeName.toLowerCase(),f=l.getAttribute("type");const d="input"===u&&("checkbox"===l.type||"radio"===l.type);let m=null;try{m=function(e){let t=null;try{if(t=e.nodeName&&"string"==typeof e.nodeName?e.nodeName.toLowerCase():null,!t)return e;if(["input","button","a","textarea","select","option","progress"].includes(t))return e;const n=e.closest("button,a");if(n)return n;const o=e.closest("svg");if(o&&(e=o),e.matches(N.map((e=>`[${e}]`)).join(",")))return e;if(e.parentNode.matches(N.map((e=>`[${e}]`)).join(",")))return e.parentNode}catch(e){}return e}(l)}catch(e){}const h=Y(m,t);if(h&&"html"===h.toLowerCase()&&("keydown"!==c||!R))return;let p=null;try{p=L(m,t)}catch(e){}a&&(p="/html/node/shadow");let g={timestamp:(new Date).getTime(),css:h||l,xpath:p||""},y={};if("click"===c){if(x.click=l,M&&M.element!==l&&o){const e=P(M.element),n={...M.oEventBase,type:e?"bfill_value":"fill_value",value:I(M.element),frame:M.frame};if(e){const e=F(M.element,t);e&&(n.css=e)}o({messageType:"events",event:{...n}}),D.delete(M.element),q=M.element,M=null}if(R&&j.length>0&&o){const e={...R.oEventBase,type:"bfill_value",value:j,frame:R.frame};R.shadow_host_css&&(e.shadow_host_css=R.shadow_host_css),o({messageType:"events",event:{...e}})}if(j="",R=null,!(x.return!==x.change||l===x.return||"input"!==u&&"button"!==u||"submit"!==f&&"image"!==f||null===x.return))return;if(d)return;if("select"===u||("option"===u||"optgroup"===u)&&l.closest("select"))return;let r=l;const c=l.closest&&l.closest("label");if(c){const e=l.closest("a, button, input, select, textarea"),n=!e||!c.contains(e),o=c.control;if(n&&o&&"input"===o.nodeName.toLowerCase()&&("checkbox"===o.type||"radio"===o.type)){const e=Y(c,t);if(e){r=c;let n="";try{n=L(c,t)||""}catch(e){}g={...g,css:e,xpath:a?g.xpath:n}}}}let m="click";if(function(e){if("canvas"===e.nodeName.toLowerCase())return!0;if(e.shadowRoot)try{if(e.shadowRoot.querySelector("canvas"))return!0}catch(e){}for(const t of e.children||[])if(t.shadowRoot)try{if(t.shadowRoot.querySelector("canvas"))return!0}catch(e){}return!1}(l)){const t=l.getBoundingClientRect(),n={x:e.clientX-t.left,y:e.clientY-t.top,width:t.width,height:t.height};g={...g,coords:n},m="bclick"}if(y={...g,type:m,value:(r.value||r.textContent||"").trim().substr(0,20).replace(/\n/gi,""),frame:n},B={element:r,timestamp:g.timestamp},"bclick"===m&&i>-1&&s){const e=s[i].host;if(e){let n=null;try{n=T(e)}catch(e){}if(!n)try{n=U(e,t)}catch(e){}n&&(y.shadow_host_css=n)}}"bclick"===m&&(R={element:l,oEventBase:{...g},frame:n,shadow_host_css:y.shadow_host_css||null})}else if("dblclick"===c)x.dblclick=l,y={...g,type:"dblclick",value:(l.value||l.textContent||"").trim().substr(0,20).replace(/\n/gi,""),frame:n};else if("contextmenu"===c)x.contextmenu=l,y={...g,type:"contextmenu",value:(l.value||l.textContent||"").trim().substr(0,20).replace(/\n/gi,""),frame:n};else if("focus"===c);else if("mouseover"===c)O&&(clearTimeout(O),O=!1),O=setTimeout((()=>{O=!1,x.mouseover=l,y={...g,type:"mouseover",value:(l.value||l.textContent||"").trim().substr(0,20).replace(/\n/gi,""),frame:n},y&&y.type&&o&&o({messageType:"events",event:{...y}})}),500);else if("mouseout"===c);else if("keydown"===c){if(x.keydown=l,["input","textarea"].indexOf(u)>-1&&!d){if(_[l]=l.value,M={element:l,oEventBase:{...g},frame:n},e.key&&1===e.key.length){const t=D.get(l)||"";D.set(l,t+e.key)}else if("Backspace"===e.key){const e=D.get(l)||"";e.length>0&&D.set(l,e.slice(0,-1))}}else R&&(e.key&&1===e.key.length?j+=e.key:"Backspace"===e.key&&j.length>0&&(j=j.slice(0,-1)));if("input"===u&&13===e.keyCode){if(r=!0,x.return=l,!d){const e=P(l);if(y={...g,type:e?"bfill_value":"fill_value",value:I(l),frame:n},e){const e=F(l,t);e&&(y.css=e)}D.delete(l)}}else if(R&&13===e.keyCode){if(j.length>0&&o){const e={...R.oEventBase,type:"bfill_value",value:j,frame:R.frame};R.shadow_host_css&&(e.shadow_host_css=R.shadow_host_css),o({messageType:"events",event:{...e}}),j=""}if(o){const e={...R.oEventBase,timestamp:(new Date).getTime(),type:"press_key",value:13,frame:R.frame};o({messageType:"events",event:{...e}})}R=null}}else if("blur"===c){if(x.blur=l,l===q)return void(q=null);if(M&&M.element===l&&(M=null),["input","textarea"].indexOf(u)>-1&&!d){if(_[l]=l.value,l===x.return)return void(x.return=null);if("input"===u&&("submit"===l.type||"button"===l.type||"image"===l.type))return;const e=P(l);if(y={...g,type:e?"bfill_value":"fill_value",value:I(l),frame:n},e){const e=F(l,t);e&&(y.css=e)}D.delete(l)}}else if("change"===c)if(x.change=l,"input"===u){if("checkbox"===f||"radio"===f){if(B&&B.element!==l&&g.timestamp-B.timestamp<100)return;y={...g,type:"change",subtype:"check",checked:l.checked,frame:n}}}else if("select"===u)if(l.multiple){const e=[];for(let t=0;tg(e,u)))));else if(t===n.Two)e=e.slice(0,1),u&&(e=e.concat(e.filter(y).map((e=>g(e,u)))));else if(t===n.One){const[t]=e=e.slice(0,1);u&&y(t)&&(e=[g(t,u)])}for(const t of e)t.level=c;if(s.push(e),s.length>=r.seedMinLength&&(l=i(s,o),l))break;a=a.parentElement,c++}return l||(l=i(s,o)),l}function i(e,t){const n=N(b(e));if(n.length>r.threshold)return t?t():null;for(const e of n)if(u(e))return e;return null}function a(e){let t=e[0],n=t.name;for(let o=1;o ${n}`:`${e[o].name} ${n}`,t=e[o]}return n}function c(e){return e.map((e=>e.penalty)).reduce(((e,t)=>e+t),0)}function u(e){switch(l.querySelectorAll(a(e)).length){case 0:throw new Error(`Can't select any node with this selector: ${a(e)}`);case 1:return!0;default:return!1}}function f(e){const t=e.getAttribute("id");return t&&r.idName(t)?{name:`#${A(t,{isIdentifier:!0})}`,penalty:0}:null}function d(e){return Array.from(e.attributes).filter((e=>r.attr(e.name,e.value))).map((e=>({name:"["+A(e.name,{isIdentifier:!0})+'="'+A(e.value)+'"]',penalty:.5})))}function m(e){return Array.from(e.classList).filter(r.className).map((e=>({name:"."+A(e,{isIdentifier:!0}),penalty:1})))}function h(e){const t=e.tagName.toLowerCase();return r.tagName(t)?{name:t,penalty:2}:null}function p(e){const t=e.parentNode;if(!t)return null;let n=t.firstChild;if(!n)return null;let o=0;for(;n&&(n.nodeType===Node.ELEMENT_NODE&&o++,n!==e);)n=n.nextSibling;return o}function g(e,t){return{name:e.name+`:nth-child(${t})`,penalty:e.penalty+1}}function y(e){return"html"!==e.name&&!e.name.startsWith("#")}function w(...e){const t=e.filter(v);return 0c(e)-c(t)))}function $(e,t){return l.querySelector(a(e))===t}(o=n||(n={}))[o.All=0]="All",o[o.Two=1]="Two",o[o.One=2]="One";const S=/[ -,\.\/:-@\[-\^`\{-~]/,E=/[ -,\.\/:-@\[\]\^`\{-~]/,C=/(^|\\+)?(\\[A-F0-9]{1,6})\x20(?![a-fA-F0-9\x20])/g,k={escapeEverything:!1,isIdentifier:!1,quotes:"single",wrap:!1};function A(e,t={}){const n=Object.assign(Object.assign({},k),t);"single"!==n.quotes&&"double"!==n.quotes&&(n.quotes="single");const o="double"==n.quotes?'"':"'",r=n.isIdentifier,l=e.charAt(0);let s="",i=0;for(const t=e.length;ic||126=c&&i!0,className:()=>!0,tagName:()=>!0,attr:()=>!1,seedMinLength:1,optimizedMinLength:2,threshold:1e3,maxNumberOfTries:1e4};r=Object.assign(Object.assign({},T),t),l=function(e,t){return e.nodeType===Node.DOCUMENT_NODE?e:e===t.root?e.ownerDocument:e}(r.root,T);let L=s(e,n.All,(()=>s(e,n.Two,(()=>s(e,n.One)))));if(L){const t=N(function*e(t,n,o={counter:0,visited:new Map}){if(2r.optimizedMinLength)for(let l=1;lr.maxNumberOfTries)return;o.counter+=1;const s=[...t];s.splice(l,1);const i=a(s);if(o.visited.has(i))return;u(s)&&$(s,n)&&(yield s,o.visited.set(i,!0),yield*e(s,n,o))}}(L,e));return 0{if(!r.isRecording)return;let l=!1,s=null;function i(e){e&&"mouseover"===e.type&&!l||H(e,window.document,null,(e=>{window!==window.top?window.parent.postMessage({source:"event-from-iframe",obj:{...e},framePath:[]},"*"):chrome.runtime.sendMessage(e)}))}window===window.top&&window.addEventListener("message",(e=>{if(e.data&&e.data.source&&"event-from-iframe"===e.data.source){const t=document.querySelectorAll("iframe, frame");let n=!1;for(const o of t)if(o.contentWindow===e.source){n=!0;break}if(!n)return;const o={...e.data.obj},r=e.data.framePath||[];for(const n of t)if(n.contentWindow===e.source){let e=null;try{e=T(n)}catch(e){}if(!e)try{e=X(n,{root:window.document,attr:e=>N.includes(e),idName:e=>!/^[0-9]+.*/i.test(e),className:S})}catch(e){}if(e&&o.event){const t=[e,...r];o.event.frame=t.join(" >>> "),chrome.runtime.sendMessage(o)}break}}})),window!==window.top&&window.addEventListener("message",(e=>{if(e.data&&e.data.source&&"event-from-iframe"===e.data.source){const t=document.querySelectorAll("iframe, frame");let n=!1;for(const o of t)if(o.contentWindow===e.source){n=!0;break}if(!n)return;const o=e.data.framePath||[];for(const n of t)if(n.contentWindow===e.source){let t=null;try{t=T(n)}catch(e){}if(!t)try{t=X(n,{root:window.document,attr:e=>N.includes(e),idName:e=>!/^[0-9]+.*/i.test(e),className:S})}catch(e){}t&&o.push(t),window.parent.postMessage({source:"event-from-iframe",obj:e.data.obj,framePath:o},"*");break}}})),r.isRecording&&(t=!0,e=document.title,function(){if(!t)return document.title=`${e}`,o=!1,void(n&&(clearInterval(n),n=!1));n=setInterval((()=>{o?(document.title=`${e}`,o=!1):(document.title=`🔴 ${e}`,o=!0)}),1e3)}(),window===window.top&&chrome.runtime.sendMessage({messageType:"events",event:{type:"goto",timestamp:(new Date).getTime(),windowWidth:window.innerWidth,windowHeight:window.innerHeight,url:window.location.href}}),document.addEventListener("click",i,!0),document.addEventListener("mouseover",i,!0),document.addEventListener("dblclick",i,!0),document.addEventListener("contextmenu",i,!0),document.addEventListener("keydown",i,!0),document.addEventListener("blur",i,!0),document.addEventListener("change",i,!0));const a={attributes:!1,childList:!0,subtree:!0},c=new MutationObserver((async(e,t)=>{l=!0,s&&(clearTimeout(s),s=null),s=setTimeout((()=>{l=!1}),200)}));document.body&&c.observe(document.body,a)}))}()})(); \ No newline at end of file diff --git a/test-build/manifest.json b/test-build/manifest.json index e90753d..db678d6 100644 --- a/test-build/manifest.json +++ b/test-build/manifest.json @@ -1 +1 @@ -{"version":"1.2.3","manifest_version":3,"name":"Snyk API & Web Sequence Recorder","action":{"default_popup":"popup.html","default_icon":{"16":"icon-34.png","48":"icon-48.png"}},"icons":{"128":"icon-128.png"},"background":{"service_worker":"background.bundle.js"},"content_scripts":[{"matches":["http://*/*","https://*/*"],"js":["contentScript.bundle.js"],"css":["content.styles.css"],"run_at":"document_start","all_frames":true,"match_about_blank":true}],"web_accessible_resources":[{"resources":["content.styles.css","icon-128.png","icon-34.png"],"matches":[""]}],"permissions":["storage","activeTab"],"host_permissions":["http://*/*","https://*/*"]} \ No newline at end of file +{"version":"1.2.5","manifest_version":3,"name":"Snyk API & Web Sequence Recorder","action":{"default_popup":"popup.html","default_icon":{"16":"icon-34.png","48":"icon-48.png"}},"icons":{"128":"icon-128.png"},"background":{"service_worker":"background.bundle.js"},"content_scripts":[{"matches":["http://*/*","https://*/*"],"js":["contentScript.bundle.js"],"css":["content.styles.css"],"run_at":"document_start","all_frames":true,"match_about_blank":true}],"web_accessible_resources":[{"resources":["content.styles.css","icon-128.png","icon-34.png"],"matches":[""]}],"permissions":["storage","activeTab","webNavigation"],"host_permissions":["http://*/*","https://*/*"]} \ No newline at end of file