Skip to content
Open
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
5 changes: 3 additions & 2 deletions OUTPUT-EXAMPLE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 |
Expand Down
17 changes: 17 additions & 0 deletions aux/popup_onload.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Popup on load test page</title>
</head>
<body>
<h1>Popup on load test page</h1>
<p>A popup window with the second page opens automatically when this page loads.</p>
<script type="text/javascript">
window.onload = function () {
window.open('./secondpage.html', 'popup_onload_window', 'width=600,height=500,resizable=yes,scrollbars=yes');
};
</script>
<p><a href="./testpage.html">Go back to the main page</a></p>
</body>
</html>
6 changes: 6 additions & 0 deletions aux/testpage.html
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,12 @@ <h1>Get elements test page</h1>
<div class="item_test_container">
<p><a href="./secondpage.html">Go to second page</a></p>
</div>
<div class="item_test_container">
<p>Popups</p>
<p><a href="./secondpage.html" target="_blank" id="popup_link">Open second page in a new tab</a></p>
<button type="button" id="popup_button" onclick="window.open('./secondpage.html', 'popup_window', 'width=600,height=400')">Open second page in a popup window</button>
<p><a href="./popup_onload.html" id="popup_onload_link">Go to a page that opens a popup on load</a></p>
</div>
<div class="item_test_container">
<p>Elements that only change attribute</p>
<div id="mouse_over_attributes">
Expand Down
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.2.3",
"version": "1.2.5",
"description": "Snyk API & Web Record login/sequence",
"license": "MIT",
"scripts": {
Expand Down
3 changes: 2 additions & 1 deletion src/manifest-chrome.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@
],
"permissions": [
"storage",
"activeTab"
"activeTab",
"webNavigation"
],
"host_permissions": [
"http://*/*",
Expand Down
3 changes: 2 additions & 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.2.3",
"version": "1.2.5",
"browser_specific_settings": {
"gecko": {
"id": "sequence-recorder@probely.com",
Expand Down Expand Up @@ -44,6 +44,7 @@
],
"permissions": [
"storage",
"webNavigation",
"https://*/*",
"http://*/*"
],
Expand Down
3 changes: 2 additions & 1 deletion src/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@
],
"permissions": [
"storage",
"activeTab"
"activeTab",
"webNavigation"
],
"host_permissions": [
"http://*/*",
Expand Down
88 changes: 72 additions & 16 deletions src/pages/Background/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand All @@ -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') {
Expand All @@ -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();
});
});
});
})();
23 changes: 8 additions & 15 deletions src/pages/Content/index.js
Original file line number Diff line number Diff line change
@@ -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');
Expand Down Expand Up @@ -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,
});
Expand Down Expand Up @@ -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
Expand Down
Loading
Loading