Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "snyk-api-and-web-record-sequence",
"version": "1.0.0",
"version": "1.1.0",
"description": "Snyk API & Web Record login/sequence",
"license": "MIT",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion src/manifest-firefox.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "Snyk API & Web Sequence Recorder",
"version": "1.0.0",
"version": "1.1.0",
"browser_specific_settings": {
"gecko": {
"id": "sequence-recorder@probely.com",
Expand Down
94 changes: 91 additions & 3 deletions src/pages/Content/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,21 @@ import getCustomSelector from './modules/getCustomSelector';
evMsg.data.source &&
evMsg.data.source === 'event-from-iframe'
) {
const obj = { ...evMsg.data.obj };
// Verify the message actually came from a child iframe
const aFrames = document.querySelectorAll('iframe, frame');
let isFromChildFrame = false;
for (const frame of aFrames) {
if (frame.contentWindow === evMsg.source) {
isFromChildFrame = true;
break;
}
}
if (!isFromChildFrame) {
return; // Message didn't come from any of our iframes
}

const obj = { ...evMsg.data.obj };
const framePath = evMsg.data.framePath || [];
for (const frame of aFrames) {
if (frame.contentWindow === evMsg.source) {
let frameSelector = null;
Expand Down Expand Up @@ -81,7 +94,9 @@ import getCustomSelector from './modules/getCustomSelector';
}
}
if (frameSelector && obj.event) {
obj.event.frame = frameSelector;
// Build the complete frame path: [outermost, ..., innermost]
const completeFramePath = [frameSelector, ...framePath];
obj.event.frame = completeFramePath.join(' >>> ');
chrome.runtime.sendMessage(obj);
}
break;
Expand All @@ -91,17 +106,90 @@ import getCustomSelector from './modules/getCustomSelector';
});
}

// intermediate frames: listen for messages from child iframes and forward to parent
if (window !== window.top) {
window.addEventListener('message', (evMsg) => {
if (
evMsg.data &&
evMsg.data.source &&
evMsg.data.source === 'event-from-iframe'
) {
// Verify the message actually came from a child iframe
const aFrames = document.querySelectorAll('iframe, frame');
let isFromChildFrame = false;
for (const frame of aFrames) {
if (frame.contentWindow === evMsg.source) {
isFromChildFrame = true;
break;
}
}
if (!isFromChildFrame) {
return; // Message didn't come from any of our iframes
}

// This is an intermediate frame - find the child iframe and add to path
const framePath = evMsg.data.framePath || [];

for (const frame of aFrames) {
if (frame.contentWindow === evMsg.source) {
let frameSelector = null;
try {
frameSelector = getCustomSelector(frame);
} catch (ex) {
// ignore
}
if (!frameSelector) {
try {
frameSelector = getNodeSelector(frame, {
root: window.document,
idName: (name) => {
return !/^[0-9]+.*/i.test(name);
},
className: (name) => {
return (
!name.includes('focus') &&
!name.includes('highlight') &&
!/^[0-9]+.*/i.test(name)
);
},
});
} catch (ex) {
// ignore
}
}

// Forward to parent with this frame's selector added to path
if (frameSelector) {
framePath.push(frameSelector);
}

window.parent.postMessage(
{
source: 'event-from-iframe',
obj: evMsg.data.obj,
framePath: framePath,
},
'*'
);
break;
}
}
}
});
}

function eventInterceptopMainHandler(ev) {
if (ev && ev.type === 'mouseover' && !mutationDetected) {
return;
}
interceptEvents(ev, window.document, null, (obj) => {
if (window !== window.top) {
// If the event is inside a frame, send it to the top
// If the event is inside a frame, send it to the parent
window.parent.postMessage(
{
source: 'event-from-iframe',
obj: { ...obj },
framePath: [], // Start with empty path, will be built as message bubbles up
},
'*'
);
Expand Down
51 changes: 50 additions & 1 deletion src/pages/Content/modules/collectEvents.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,15 +133,64 @@ export function interceptEvents(event, doc, ifrSelector, callback) {
return;
}
}
let typeStr = 'click';
if (nodeName === 'canvas') {
const rect = tgt.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
const width = rect.width;
const height = rect.height;

const clickData = { x, y, width, height };
oEventBase = { ...oEventBase, coords: clickData };

typeStr = 'bclick';
}
oEventToSend = {
...oEventBase,
type: 'click',
type: typeStr,
value: (tgt.value || tgt.textContent || '')
.trim()
.substr(0, 20)
.replace(/\n/gi, ''),
frame: ifrSelector,
};

// Add shadow host CSS selector for bclick events inside shadow DOM
if (typeStr === 'bclick' && shadowRootIdx > -1 && composedPath) {
const shadowRoot = composedPath[shadowRootIdx];
const shadowHost = shadowRoot.host;
if (shadowHost) {
let shadowHostSelector = null;
try {
shadowHostSelector = getCustomSelector(shadowHost, doc);
} catch (ex) {
// ignore
}
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)
);
},
});
} catch (ex) {
// ignore
}
}
if (shadowHostSelector) {
oEventToSend.shadow_host_css = shadowHostSelector;
}
}
}
} else if (type === 'dblclick') {
lastNodes.dblclick = tgt;
oEventToSend = {
Expand Down
4 changes: 4 additions & 0 deletions src/pages/Review/Review.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,10 @@ const Review = (props) => {
case 'goto':
newType = 'go to';
break;
case 'click':
case 'bclick':
newType = 'click';
break;
default:
newType = type;
break;
Expand Down
Loading