From 383d02d1d6c46fc27518be9b51583225f5b3230e Mon Sep 17 00:00:00 2001 From: Michael Agner Date: Mon, 27 Jan 2025 10:40:04 -0500 Subject: [PATCH 1/2] update record and playback to account for parent node missing when it will be added later --- .changeset/dirty-scissors-promise.md | 5 +++++ packages/rrweb/src/record/mutation.ts | 4 +++- packages/rrweb/src/replay/index.ts | 16 +++++++++++++++- packages/rrweb/src/utils.ts | 2 +- 4 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 .changeset/dirty-scissors-promise.md diff --git a/.changeset/dirty-scissors-promise.md b/.changeset/dirty-scissors-promise.md new file mode 100644 index 0000000000..4fdccd6957 --- /dev/null +++ b/.changeset/dirty-scissors-promise.md @@ -0,0 +1,5 @@ +--- +"rrweb": patch +--- + +updating record and playback side to account for mutations where a node is missing a parent but it gets added in a different iteration of the mutation diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index 42170b4940..38efc67dc9 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -367,9 +367,11 @@ export default class MutationBuffer { } for (const n of this.movedSet) { + const parentNode = dom.parentNode(n); if ( isParentRemoved(this.removesSubTreeCache, n, this.mirror) && - !this.movedSet.has(dom.parentNode(n)!) + !this.movedSet.has(parentNode!) && + (!this.addedSet.has(parentNode!)) ) { continue; } diff --git a/packages/rrweb/src/replay/index.ts b/packages/rrweb/src/replay/index.ts index 5f1101cc23..4f43650641 100644 --- a/packages/rrweb/src/replay/index.ts +++ b/packages/rrweb/src/replay/index.ts @@ -83,6 +83,7 @@ import { getPositionsAndIndex, uniqueTextMutations, StyleSheetMirror, + type ResolveTree } from '../utils'; import getInjectStyleRules from './styles/inject-style'; import './styles/style.css'; @@ -1707,6 +1708,18 @@ export class Replayer { appendNode(mutation); }); + const nodeIdsToBeAdded = (resolveTrees: Array) => { + const ids = new Set(); + for (const tree of resolveTrees) { + ids.add(tree.value.node.id); + if (tree.children && tree.children.length > 0) { + const res = nodeIdsToBeAdded(tree.children); + res.forEach(id => ids.add(id)); + } + } + return ids; + }; + const startTime = Date.now(); while (queue.length) { // transform queue to resolve tree @@ -1721,7 +1734,8 @@ export class Replayer { } for (const tree of resolveTrees) { const parent = mirror.getNode(tree.value.parentId); - if (!parent) { + const ids = nodeIdsToBeAdded(resolveTrees); + if (!parent && !ids.has(tree.value.parentId)) { this.debug( 'Drop resolve tree since there is no parent for the root node.', tree, diff --git a/packages/rrweb/src/utils.ts b/packages/rrweb/src/utils.ts index ecf72d05ea..67f57396dd 100644 --- a/packages/rrweb/src/utils.ts +++ b/packages/rrweb/src/utils.ts @@ -333,7 +333,7 @@ export function polyfill(win = window) { } } -type ResolveTree = { +export type ResolveTree = { value: addedNodeMutation; children: ResolveTree[]; parent: ResolveTree | null; From 64ce15e040e253ca2f27bf87866c358ec9f5c65f Mon Sep 17 00:00:00 2001 From: mikeyagner Date: Mon, 27 Jan 2025 16:10:59 +0000 Subject: [PATCH 2/2] Apply formatting changes --- packages/rrweb/src/record/mutation.ts | 2 +- packages/rrweb/src/replay/index.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index 38efc67dc9..23a5af8242 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -371,7 +371,7 @@ export default class MutationBuffer { if ( isParentRemoved(this.removesSubTreeCache, n, this.mirror) && !this.movedSet.has(parentNode!) && - (!this.addedSet.has(parentNode!)) + !this.addedSet.has(parentNode!) ) { continue; } diff --git a/packages/rrweb/src/replay/index.ts b/packages/rrweb/src/replay/index.ts index 4f43650641..167b7f3c26 100644 --- a/packages/rrweb/src/replay/index.ts +++ b/packages/rrweb/src/replay/index.ts @@ -83,7 +83,7 @@ import { getPositionsAndIndex, uniqueTextMutations, StyleSheetMirror, - type ResolveTree + type ResolveTree, } from '../utils'; import getInjectStyleRules from './styles/inject-style'; import './styles/style.css'; @@ -1714,7 +1714,7 @@ export class Replayer { ids.add(tree.value.node.id); if (tree.children && tree.children.length > 0) { const res = nodeIdsToBeAdded(tree.children); - res.forEach(id => ids.add(id)); + res.forEach((id) => ids.add(id)); } } return ids;