refactor: improved tab recording to improve stability (#1632)

* refactor: improved tab recording to improve stability

* feat: enable to import session

* improve stability

* feat: enable to edit session name

* prevent duplicate rrweb player in the dev mode
This commit is contained in:
Yun Feng
2025-01-20 13:07:04 -08:00
committed by GitHub
parent 79837ac8f2
commit 24f5fd99ba
12 changed files with 433 additions and 382 deletions

View File

@@ -1,16 +1,14 @@
import Browser from 'webextension-polyfill';
import { nanoid } from 'nanoid';
import type { eventWithTime } from '@rrweb/types';
import {
type LocalData,
LocalDataKey,
RecorderStatus,
ServiceName,
type Session,
type RecordStartedMessage,
type RecordStoppedMessage,
MessageName,
type EmitEventMessage,
EventName,
} from '~/types';
import Channel from '~/utils/channel';
import { isInCrossOriginIFrame } from '~/utils';
@@ -46,8 +44,6 @@ void (() => {
})();
async function initMainPage() {
let bufferedEvents: eventWithTime[] = [];
let newEvents: eventWithTime[] = [];
let startResponseCb: ((response: RecordStartedMessage) => void) | undefined =
undefined;
channel.provide(ServiceName.StartRecord, async () => {
@@ -58,24 +54,6 @@ async function initMainPage() {
};
});
});
channel.provide(ServiceName.ResumeRecord, async (params) => {
const { events, pausedTimestamp } = params as {
events: eventWithTime[];
pausedTimestamp: number;
};
bufferedEvents = events;
startRecord();
return new Promise((resolve) => {
startResponseCb = (response) => {
const pausedTime = response.startTimestamp - pausedTimestamp;
// Decrease the time spent in the pause state and make them look like a continuous recording.
bufferedEvents.forEach((event) => {
event.timestamp += pausedTime;
});
resolve(response);
};
});
});
let stopResponseCb: ((response: RecordStoppedMessage) => void) | undefined =
undefined;
channel.provide(ServiceName.StopRecord, () => {
@@ -83,29 +61,7 @@ async function initMainPage() {
return new Promise((resolve) => {
stopResponseCb = (response: RecordStoppedMessage) => {
stopResponseCb = undefined;
const newSession = generateSession();
response.session = newSession;
bufferedEvents = [];
newEvents = [];
resolve(response);
// clear cache
void Browser.storage.local.set({
[LocalDataKey.bufferedEvents]: [],
});
};
});
});
channel.provide(ServiceName.PauseRecord, () => {
window.postMessage({ message: MessageName.StopRecord });
return new Promise((resolve) => {
stopResponseCb = (response: RecordStoppedMessage) => {
stopResponseCb = undefined;
bufferedEvents = [];
newEvents = [];
resolve(response);
void Browser.storage.local.set({
[LocalDataKey.bufferedEvents]: response.events,
});
};
});
});
@@ -132,15 +88,14 @@ async function initMainPage() {
event.data.message === MessageName.RecordStopped &&
stopResponseCb
) {
const data = event.data as RecordStoppedMessage;
// On firefox, the event.data is immutable, so we need to clone it to avoid errors.
const newData = {
...data,
};
newData.events = bufferedEvents.concat(data.events);
stopResponseCb(newData);
const data = { ...(event.data as RecordStoppedMessage) };
stopResponseCb(data);
} else if (event.data.message === MessageName.EmitEvent)
newEvents.push((event.data as EmitEventMessage).event);
channel.emit(
EventName.ContentScriptEmitEvent,
(event.data as EmitEventMessage).event,
);
},
);
@@ -150,17 +105,7 @@ async function initMainPage() {
RecorderStatus.RECORDING
) {
startRecord();
bufferedEvents = localData[LocalDataKey.bufferedEvents] || [];
}
// Before unload pages, cache the new events in the local storage.
window.addEventListener('beforeunload', (event) => {
if (!newEvents.length) return;
event.preventDefault();
void Browser.storage.local.set({
[LocalDataKey.bufferedEvents]: bufferedEvents.concat(newEvents),
});
});
}
async function initCrossOriginIframe() {
@@ -193,15 +138,3 @@ function startRecord() {
document.documentElement.removeChild(scriptEl);
};
}
function generateSession() {
const newSession: Session = {
id: nanoid(),
name: document.title,
tags: [],
createTimestamp: Date.now(),
modifyTimestamp: Date.now(),
recorderVersion: Browser.runtime.getManifest().version_name || 'unknown',
};
return newSession;
}

View File

@@ -8,15 +8,12 @@ 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,
@@ -52,7 +49,6 @@ const messageHandler = (
}
postMessage({
message: MessageName.RecordStopped,
events,
endTimestamp: Date.now(),
});
window.removeEventListener('message', messageHandler);