Skip to content

Commit 233bd63

Browse files
author
da.fei
committed
refactor(rrweb/scripts/repl.js): 增强录制脚本注入的鲁棒性与安全性
此次重构增强了录制脚本注入的鲁棒性和安全性,包括防止并发注入、检测并避免无效帧注入、添加全局错误处理以及优化注入时机。同时增加了对跨域iframe的安全设置。
1 parent 4db9782 commit 233bd63

File tree

1 file changed

+92
-18
lines changed

1 file changed

+92
-18
lines changed

packages/rrweb/scripts/repl.js

Lines changed: 92 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,42 @@ function getCode() {
2020
void (async () => {
2121
const code = getCode();
2222
let events = [];
23+
let injectionInProgress = false;
2324

2425
async function injectRecording(frame) {
26+
// 防止并发注入
27+
if (injectionInProgress) {
28+
return;
29+
}
30+
2531
try {
32+
injectionInProgress = true;
33+
34+
// 检查 frame 是否仍然有效
35+
if (frame.isDetached()) {
36+
console.log('Frame is detached, skipping injection');
37+
return;
38+
}
39+
40+
// 等待页面稳定
41+
await new Promise(resolve => setTimeout(resolve, 1000));
42+
43+
// 再次检查 frame 状态
44+
if (frame.isDetached()) {
45+
console.log('Frame became detached while waiting, skipping injection');
46+
return;
47+
}
48+
49+
// 检查是否已经注入过
50+
const alreadyInjected = await frame.evaluate(() => {
51+
return window.__IS_RECORDING__ === true;
52+
}).catch(() => false);
53+
54+
if (alreadyInjected) {
55+
console.log('Recording script already injected');
56+
return;
57+
}
58+
2659
await frame.evaluate((rrwebCode) => {
2760
const win = window;
2861
if (win.__IS_RECORDING__) return;
@@ -38,27 +71,45 @@ void (async () => {
3871
document.head.append(s);
3972
} else {
4073
requestAnimationFrame(() => {
41-
document.head.append(s);
74+
if (document.head) {
75+
document.head.append(s);
76+
}
4277
});
4378
}
4479
}
4580
loadScript(rrwebCode);
4681

4782
win.events = [];
48-
rrweb.record({
49-
emit: (event) => {
50-
win.events.push(event);
51-
win._replLog(event);
52-
},
53-
plugins: [],
54-
recordCanvas: true,
55-
recordCrossOriginIframes: true,
56-
collectFonts: true,
57-
});
83+
// 添加全局错误处理
84+
try {
85+
rrweb.record({
86+
emit: (event) => {
87+
win.events.push(event);
88+
if (win._replLog) {
89+
win._replLog(event);
90+
}
91+
},
92+
plugins: [],
93+
recordCanvas: true,
94+
recordCrossOriginIframes: true,
95+
collectFonts: true,
96+
});
97+
console.log('rrweb recording started successfully');
98+
} catch (e) {
99+
console.error('Failed to start rrweb recording:', e);
100+
}
58101
})();
59102
}, code);
103+
104+
console.log('Recording script injected successfully');
60105
} catch (e) {
61-
console.error('failed to inject recording script:', e);
106+
// 只在非上下文销毁错误时输出错误信息
107+
if (!e.message.includes('Execution context was destroyed') &&
108+
!e.message.includes('detached frame')) {
109+
console.error('Failed to inject recording script:', e.message);
110+
}
111+
} finally {
112+
injectionInProgress = false;
62113
}
63114
}
64115

@@ -153,6 +204,8 @@ void (async () => {
153204
'--start-maximized',
154205
'--ignore-certificate-errors',
155206
'--no-sandbox',
207+
'--disable-web-security',
208+
'--disable-features=VizDisplayCompositor'
156209
],
157210
});
158211
const page = await browser.newPage();
@@ -161,15 +214,36 @@ void (async () => {
161214
events.push(event);
162215
});
163216

164-
page.on('framenavigated', async (frame) => {
165-
await injectRecording(frame);
166-
});
217+
// 使用去抖动的注入函数
218+
let injectionTimeout;
219+
const debouncedInject = (frame) => {
220+
clearTimeout(injectionTimeout);
221+
injectionTimeout = setTimeout(() => {
222+
injectRecording(frame);
223+
}, 500);
224+
};
225+
226+
page.on('framenavigated', debouncedInject);
167227

168-
await page.goto(url, {
169-
waitUntil: 'domcontentloaded',
170-
timeout: 300000,
228+
// 监听页面加载完成事件
229+
page.on('load', () => {
230+
setTimeout(() => {
231+
injectRecording(page.mainFrame());
232+
}, 1000);
171233
});
172234

235+
try {
236+
await page.goto(url, {
237+
waitUntil: 'domcontentloaded',
238+
timeout: 300000,
239+
});
240+
241+
// 初始注入
242+
await injectRecording(page.mainFrame());
243+
} catch (e) {
244+
console.error('Failed to navigate to URL:', e.message);
245+
}
246+
173247
emitter.once('done', async (shouldReplay) => {
174248
const pages = await browser.pages();
175249
await Promise.all(pages.map((page) => page.close()));

0 commit comments

Comments
 (0)