Files
rrweb/packages/web-extension/src/content/inject.ts
Yun Feng b837600e80 rrweb extension implementation (#1044)
* feat: add rrweb web-extension package

* refactor: make the extension suitable for manifest v3

* update tsconfig.json

* use version_name rather than recorder_version in manifest.json

* update manifest.json

* enable to keep recording after changing tabs

* enable to record between tabs and urls

* fix CI error

* try to fix CI error

* feat: add pause and resume buttons

* feat: add a link to new session after recording

* improve session list

* refactor: migrate session storage from chrome local storage to indexedDB

* feat: add pagination to session list

* fix: multiple recorders are started after pausing and resuming process

* fix: can't stop recording on firefox browser

* update type import of 'eventWithTime'

* fix CI error

* doc: add readme

* Apply suggestions from Justin's code review

Co-authored-by: Justin Halsall <Juice10@users.noreply.github.com>

* refactor: make use of webNavigation API to implement recording consistent during page navigation

* fix firefox compatibility issue and add title to pages

* add mouseleave listener to enhance the recording liability

* fix firefox compatibility issue and improve the experience of recording resume after closing tabs

* update tsconfig

* upgrade vite-plugin-web-extension config to fix some bugs on facebook web page

* update import links

* refactor: cross tab recording mechanism

apply Justin's suggestion

* refactor: slipt util/index.ts into multiple files

* implement cross-origin iframe recording

* fix: regression of issue: ShadowHost can't be a string (issue 941)

* refactor shadow dom recording to make tests cover key code

* Apply formatting changes

* increase the node memory limitation to avoid CI failure

* Create lovely-pears-cross.md

* Apply formatting changes

* Update packages/web-extension/package.json

* Update .changeset/lovely-pears-cross.md

* update change logs

* delete duplicated property

---------

Co-authored-by: Justin Halsall <Juice10@users.noreply.github.com>
2026-04-01 12:00:00 +08:00

73 lines
1.8 KiB
TypeScript

import { record } from 'rrweb';
import type { recordOptions } from 'rrweb/typings/types';
import type { eventWithTime } from '@rrweb/types';
import { MessageName, RecordStartedMessage } from '~/types';
import { isInCrossOriginIFrame } from '~/utils';
/**
* This script is injected into both main page and cross-origin IFrames through <script> tags.
*/
const events: eventWithTime[] = [];
let stopFn: (() => void) | null = null;
function startRecord(config: recordOptions<eventWithTime>) {
events.length = 0;
stopFn =
record({
emit: (event) => {
events.push(event);
postMessage({
message: MessageName.EmitEvent,
event,
});
},
...config,
}) || null;
postMessage({
message: MessageName.RecordStarted,
startTimestamp: Date.now(),
} as RecordStartedMessage);
}
const messageHandler = (
event: MessageEvent<{
message: MessageName;
config?: recordOptions<eventWithTime>;
}>,
) => {
if (event.source !== window) return;
const data = event.data;
const eventHandler = {
[MessageName.StartRecord]: () => {
startRecord(data.config || {});
},
[MessageName.StopRecord]: () => {
if (stopFn) stopFn();
postMessage({
message: MessageName.RecordStopped,
events,
endTimestamp: Date.now(),
});
window.removeEventListener('message', messageHandler);
},
} as Record<MessageName, () => void>;
if (eventHandler[data.message]) eventHandler[data.message]();
};
/**
* Only post message in the main page.
*/
function postMessage(message: unknown) {
if (!isInCrossOriginIFrame()) window.postMessage(message, location.origin);
}
window.addEventListener('message', messageHandler);
window.postMessage(
{
message: MessageName.RecordScriptReady,
},
location.origin,
);