Skip to content
Open
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
110 changes: 92 additions & 18 deletions packages/rrweb/scripts/repl.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,42 @@ function getCode() {
void (async () => {
const code = getCode();
let events = [];
let injectionInProgress = false;

async function injectRecording(frame) {
// 防止并发注入
if (injectionInProgress) {
return;
}

try {
injectionInProgress = true;

// 检查 frame 是否仍然有效
if (frame.isDetached()) {
console.log('Frame is detached, skipping injection');
return;
}

// 等待页面稳定
await new Promise(resolve => setTimeout(resolve, 1000));

// 再次检查 frame 状态
if (frame.isDetached()) {
console.log('Frame became detached while waiting, skipping injection');
return;
}

// 检查是否已经注入过
const alreadyInjected = await frame.evaluate(() => {
return window.__IS_RECORDING__ === true;
}).catch(() => false);

if (alreadyInjected) {
console.log('Recording script already injected');
return;
}

await frame.evaluate((rrwebCode) => {
const win = window;
if (win.__IS_RECORDING__) return;
Expand All @@ -38,27 +71,45 @@ void (async () => {
document.head.append(s);
} else {
requestAnimationFrame(() => {
document.head.append(s);
if (document.head) {
document.head.append(s);
}
});
}
}
loadScript(rrwebCode);

win.events = [];
rrweb.record({
emit: (event) => {
win.events.push(event);
win._replLog(event);
},
plugins: [],
recordCanvas: true,
recordCrossOriginIframes: true,
collectFonts: true,
});
// 添加全局错误处理
try {
rrweb.record({
emit: (event) => {
win.events.push(event);
if (win._replLog) {
win._replLog(event);
}
},
plugins: [],
recordCanvas: true,
recordCrossOriginIframes: true,
collectFonts: true,
});
console.log('rrweb recording started successfully');
} catch (e) {
console.error('Failed to start rrweb recording:', e);
}
})();
}, code);

console.log('Recording script injected successfully');
} catch (e) {
console.error('failed to inject recording script:', e);
// 只在非上下文销毁错误时输出错误信息
if (!e.message.includes('Execution context was destroyed') &&
!e.message.includes('detached frame')) {
console.error('Failed to inject recording script:', e.message);
}
} finally {
injectionInProgress = false;
}
}

Expand Down Expand Up @@ -153,6 +204,8 @@ void (async () => {
'--start-maximized',
'--ignore-certificate-errors',
'--no-sandbox',
'--disable-web-security',
'--disable-features=VizDisplayCompositor'
],
});
const page = await browser.newPage();
Expand All @@ -161,15 +214,36 @@ void (async () => {
events.push(event);
});

page.on('framenavigated', async (frame) => {
await injectRecording(frame);
});
// 使用去抖动的注入函数
let injectionTimeout;
const debouncedInject = (frame) => {
clearTimeout(injectionTimeout);
injectionTimeout = setTimeout(() => {
injectRecording(frame);
}, 500);
};

page.on('framenavigated', debouncedInject);

await page.goto(url, {
waitUntil: 'domcontentloaded',
timeout: 300000,
// 监听页面加载完成事件
page.on('load', () => {
setTimeout(() => {
injectRecording(page.mainFrame());
}, 1000);
});

try {
await page.goto(url, {
waitUntil: 'domcontentloaded',
timeout: 300000,
});

// 初始注入
await injectRecording(page.mainFrame());
} catch (e) {
console.error('Failed to navigate to URL:', e.message);
}

emitter.once('done', async (shouldReplay) => {
const pages = await browser.pages();
await Promise.all(pages.map((page) => page.close()));
Expand Down
Loading