Skip to content

Commit cbdd8b9

Browse files
committed
Fix infinite loop in replaceActions
We were seeing issues getting into an infinite loop calling `splice` to reset actions (I believe only with large action sets). Replacing the array completely seems to get around this issue.
1 parent 58f33cc commit cbdd8b9

3 files changed

Lines changed: 31 additions & 24 deletions

File tree

packages/rrweb/src/replay/index.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ import type {
5454
incrementalData,
5555
Handler,
5656
Emitter,
57-
metaEvent,
5857
mutationData,
5958
scrollData,
6059
inputData,
@@ -403,7 +402,7 @@ export class Replayer {
403402
(e) => e.type === EventType.FullSnapshot,
404403
);
405404
if (firstMeta) {
406-
const { width, height } = firstMeta.data as metaEvent['data'];
405+
const { width, height } = firstMeta.data ;
407406
setTimeout(() => {
408407
this.emitter.emit(ReplayerEvents.Resize, {
409408
width,
@@ -2019,7 +2018,7 @@ export class Replayer {
20192018
const svp = styleValues[s] as styleValueWithPriority;
20202019
targetEl.style.setProperty(s, svp[0], svp[1]);
20212020
} else {
2022-
const svs = styleValues[s] as string;
2021+
const svs = styleValues[s] ;
20232022
targetEl.style.setProperty(s, svs);
20242023
}
20252024
}
@@ -2252,7 +2251,7 @@ export class Replayer {
22522251
const adoptStyleSheets = (targetHost: Node, styleIds: number[]) => {
22532252
const stylesToAdopt = styleIds
22542253
.map((styleId) => this.styleMirror.getStyle(styleId))
2255-
.filter((style) => style !== null) as CSSStyleSheet[];
2254+
.filter((style) => style !== null);
22562255
if (hasShadowRoot(targetHost))
22572256
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
22582257
(targetHost as HTMLElement).shadowRoot!.adoptedStyleSheets =

packages/rrweb/src/replay/machine.ts

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -250,29 +250,31 @@ export function createPlayerService(
250250
}),
251251
/* Highlight Code Start */
252252
replaceEvents: assign((ctx, machineEvent) => {
253+
if (machineEvent.type !== 'REPLACE_EVENTS') return ctx;
254+
253255
const { events: curEvents, timer, baselineTime } = ctx;
254-
if (machineEvent.type === 'REPLACE_EVENTS') {
255-
const { events: newEvents } = machineEvent.payload;
256-
curEvents.length = 0;
257-
const actions: actionWithDelay[] = [];
258-
for (const event of newEvents) {
259-
addDelay(event, baselineTime);
260-
curEvents.push(event);
261-
if (event.timestamp >= timer.timeOffset + baselineTime) {
262-
const castFn = getCastFn(event, false);
263-
actions.push({
264-
doAction: () => {
265-
castFn();
266-
},
267-
delay: event.delay!,
268-
});
269-
}
270-
}
256+
const { events: newEvents } = machineEvent.payload;
257+
258+
if (newEvents.length === 0) return ctx;
259+
260+
curEvents.length = 0;
261+
const actions: actionWithDelay[] = [];
262+
const timeThreshold = timer.timeOffset + baselineTime;
271263

272-
if (timer.isActive()) {
273-
timer.replaceActions(actions);
264+
for (const event of newEvents) {
265+
addDelay(event, baselineTime);
266+
curEvents.push(event);
267+
if (event.timestamp >= timeThreshold) {
268+
actions.push({
269+
doAction: getCastFn(event, false),
270+
delay: event.delay!,
271+
});
274272
}
275273
}
274+
275+
if (timer.isActive()) {
276+
timer.replaceActions(actions);
277+
}
276278
return { ...ctx, events: curEvents };
277279
}),
278280
/* Highlight Code End */

packages/rrweb/src/replay/timer.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,14 @@ export class Timer {
5252
}
5353

5454
public replaceActions(actions: actionWithDelay[]) {
55+
const rafWasActive = this.raf === true;
56+
5557
this.actions.length = 0;
56-
this.actions.splice(0, 0, ...actions);
58+
this.actions = [...actions];
59+
60+
if (rafWasActive) {
61+
this.raf = requestAnimationFrame(this.rafCheck.bind(this));
62+
}
5763
}
5864
/* End Highlight Code */
5965

0 commit comments

Comments
 (0)