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..23a5af8242 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..167b7f3c26 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;