Skip to content

Commit

Permalink
Fix: Switch to real dom before rebuilding fullsnapshot (#1139)
Browse files Browse the repository at this point in the history
  • Loading branch information
YunFeng0817 authored Mar 8, 2023
1 parent d82c5ed commit f27e545
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changeset/eight-terms-hunt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'rrweb': patch
---

Fix: Switch from virtual dom to real dom before rebuilding fullsnapshot
5 changes: 5 additions & 0 deletions .changeset/real-masks-explode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'rrdom': patch
---

Fix: If RRNode appends a single child twice, children of the node will become an infinite link list.
2 changes: 1 addition & 1 deletion packages/rrdom/src/diff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,7 @@ function diffChildren(
} else if (newStartIndex > newEndIndex) {
for (; oldStartIndex <= oldEndIndex; oldStartIndex++) {
const node = oldChildren[oldStartIndex];
if (!node || !parentNode.contains(node)) continue;
if (!node || node.parentNode !== parentNode) continue;
try {
parentNode.removeChild(node);
replayer.mirror.removeNodeFromMap(node);
Expand Down
5 changes: 5 additions & 0 deletions packages/rrdom/src/document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,8 @@ export type CSSStyleDeclaration = Record<string, string> & {
};

function appendChild(parent: IRRNode, newChild: IRRNode) {
if (newChild.parentNode) newChild.parentNode.removeChild(newChild);

if (parent.lastChild) {
parent.lastChild.nextSibling = newChild;
newChild.previousSibling = parent.lastChild;
Expand Down Expand Up @@ -740,6 +742,9 @@ function insertBefore(
"Failed to execute 'insertBefore' on 'RRNode': The RRNode before which the new node is to be inserted is not a child of this RRNode.",
);

if (newChild === refChild) return newChild;
if (newChild.parentNode) newChild.parentNode.removeChild(newChild);

newChild.previousSibling = refChild.previousSibling;
refChild.previousSibling = newChild;
newChild.nextSibling = refChild;
Expand Down
35 changes: 35 additions & 0 deletions packages/rrdom/test/document.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,22 @@ describe('Basic RRDocument implementation', () => {
expect(node.contains(child2)).toBeTruthy();
});

it('can append a child with parent node', () => {
const node = document.createElement('div');
const child = document.createElement('span');
expect(node.appendChild(child)).toBe(child);
expect(node.childNodes).toEqual([child]);
expect(node.appendChild(child)).toBe(child);
expect(node.childNodes).toEqual([child]);
expect(child.parentNode).toBe(node);

const node1 = document.createElement('div');
expect(node1.appendChild(child)).toBe(child);
expect(node1.childNodes).toEqual([child]);
expect(child.parentNode).toBe(node1);
expect(node.childNodes).toEqual([]);
});

it('can insert new child before an existing child', () => {
const node = document.createElement('div');
const child1 = document.createElement('h1');
Expand Down Expand Up @@ -820,6 +836,25 @@ describe('Basic RRDocument implementation', () => {
expect(node.contains(child1)).toBeTruthy();
});

it('can insert a child with parent node', () => {
const node = document.createElement('div');
const child1 = document.createElement('h1');
expect(node.insertBefore(child1, null)).toBe(child1);
expect(node.childNodes).toEqual([child1]);
expect(node.insertBefore(child1, child1)).toBe(child1);
expect(node.childNodes).toEqual([child1]);
expect(child1.parentNode).toEqual(node);

const node2 = document.createElement('div');
const child2 = document.createElement('h2');
expect(node2.insertBefore(child2, null)).toBe(child2);
expect(node2.childNodes).toEqual([child2]);
expect(node2.insertBefore(child1, child2)).toBe(child1);
expect(node2.childNodes).toEqual([child1, child2]);
expect(child1.parentNode).toEqual(node2);
expect(node.childNodes).toEqual([]);
});

it('can remove an existing child', () => {
const node = document.createElement('div');
const child1 = document.createElement('h1');
Expand Down
10 changes: 10 additions & 0 deletions packages/rrweb/src/replay/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,16 @@ export class Replayer {
}
};

/**
* Normally rebuilding full snapshot should not be under virtual dom environment.
* But if the order of data events has some issues, it might be possible.
* Adding this check to avoid any potential issues.
*/
if (this.usingVirtualDom) {
this.virtualDom.destroyTree();
this.usingVirtualDom = false;
}

this.mirror.reset();
rebuild(event.data.node, {
doc: this.iframe.contentDocument,
Expand Down

0 comments on commit f27e545

Please sign in to comment.