Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle negative ids in rrdom correctly + extra tests #927

Merged
merged 34 commits into from
Aug 6, 2022
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
42009f3
inline stylesheets when loaded
Juice10 May 31, 2022
b9ddfae
set empty link elements to loaded by default
Juice10 Jun 1, 2022
4c10804
Clean up stylesheet manager
Juice10 Jun 1, 2022
e9f8e06
Remove attribute mutation code
Juice10 Jun 2, 2022
c11e052
Update packages/rrweb/test/record.test.ts
Juice10 Jun 7, 2022
244a26a
Update packages/rrweb/test/record.test.ts
Juice10 Jun 7, 2022
1edb1ea
Update packages/rrweb/test/record.test.ts
Juice10 Jun 7, 2022
45bafbd
Merge branch 'master' of https://github.com/rrweb-io/rrweb into seria…
Juice10 Jun 7, 2022
401c503
Update packages/rrweb/scripts/repl.js
Juice10 Jun 7, 2022
2ec406b
Update packages/rrweb/test/record.test.ts
Juice10 Jun 7, 2022
2e8492e
Update packages/rrweb/src/record/index.ts
Juice10 Jun 7, 2022
217bd7c
Add todo
Juice10 Jun 7, 2022
c0371f0
Move require out of time sensitive assert
Juice10 Jun 7, 2022
fd38d43
Merge branch 'serialize-stylesheet-contents' of https://github.com/rr…
Juice10 Jun 7, 2022
dee14a6
Add waitForRAF, its more reliable than waitForTimeout
Juice10 Jun 7, 2022
1965152
Remove flaky tests
Juice10 Jun 7, 2022
8ff7bc6
Add recording stylesheets in iframes
Juice10 Jun 7, 2022
d5a83be
Remove variability from flaky test
Juice10 Jun 7, 2022
7e3a1a8
Make test more robust
Juice10 Jun 7, 2022
8c7a38f
Fix naming
Juice10 Jun 27, 2022
6878711
Merge branch 'master' of https://github.com/rrweb-io/rrweb into seria…
Juice10 Jun 27, 2022
d2cb411
Add test cases for inlineImages
Juice10 Jun 29, 2022
18c4475
Add test cases for inlineImages
Juice10 Jun 29, 2022
9efecfe
Merge branch 'inline-image-test-cases' of https://github.com/rrweb-io…
Juice10 Jun 30, 2022
da245bd
Record iframe mutations cross page
Juice10 Jun 30, 2022
be618d3
Test: should record images inside iframe with blob url after iframe w…
Juice10 Jun 30, 2022
9fc54b4
Merge branch 'master' of https://github.com/rrweb-io/rrweb into inlin…
Juice10 Jul 1, 2022
d63a529
Handle negative ids in rrdom correctly
Juice10 Jul 1, 2022
83102f0
Update packages/rrdom/src/diff.ts
Juice10 Jul 25, 2022
f872f76
Merge branch 'master' into rrdom-negative-ids
Juice10 Jul 25, 2022
3cabbf7
Start unserialized nodes at -2
Juice10 Jul 25, 2022
3a8ce08
Set unserialized id starting number at -2
Juice10 Jul 25, 2022
14179d6
Remove duplication
Juice10 Jul 25, 2022
452cabf
Merge branch 'master' into rrdom-negative-ids
YunFeng0817 Aug 4, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 28 additions & 5 deletions packages/rrdom/src/diff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,37 +259,57 @@ function diffChildren(
let oldIdToIndex: Record<number, number> | undefined = undefined,
indexInOld;
while (oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex) {
const oldStartId = replayer.mirror.getId(oldStartNode);
const oldEndId = replayer.mirror.getId(oldEndNode);
const newStartId = rrnodeMirror.getId(newStartNode);
const newEndId = rrnodeMirror.getId(newEndNode);

// rrdom contains elements with negative ids, we don't want to accidentally match those to a mirror mismatch (-1) id.
// Negative oldStartId happen when nodes are not in the mirror, but are in the DOM.
// eg.iframes come with a document, html, head and body nodes.
// thats why below we always check if an id is negative.

if (oldStartNode === undefined) {
oldStartNode = oldChildren[++oldStartIndex];
} else if (oldEndNode === undefined) {
oldEndNode = oldChildren[--oldEndIndex];
} else if (
replayer.mirror.getId(oldStartNode) === rrnodeMirror.getId(newStartNode)
oldStartId !== -1 &&
// same first element?
oldStartId === newStartId
) {
diff(oldStartNode, newStartNode, replayer, rrnodeMirror);
oldStartNode = oldChildren[++oldStartIndex];
newStartNode = newChildren[++newStartIndex];
} else if (
replayer.mirror.getId(oldEndNode) === rrnodeMirror.getId(newEndNode)
oldEndId !== -1 &&
// same last element?
oldEndId === newEndId
) {
diff(oldEndNode, newEndNode, replayer, rrnodeMirror);
oldEndNode = oldChildren[--oldEndIndex];
newEndNode = newChildren[--newEndIndex];
} else if (
replayer.mirror.getId(oldStartNode) === rrnodeMirror.getId(newEndNode)
oldStartId !== -1 &&
// is the first old element the same as the last new element?
oldStartId === newEndId
) {
parentNode.insertBefore(oldStartNode, oldEndNode.nextSibling);
diff(oldStartNode, newEndNode, replayer, rrnodeMirror);
oldStartNode = oldChildren[++oldStartIndex];
newEndNode = newChildren[--newEndIndex];
} else if (
replayer.mirror.getId(oldEndNode) === rrnodeMirror.getId(newStartNode)
oldEndId !== -1 &&
// is the last old element the same as the first new element?
oldEndId === newStartId
) {
parentNode.insertBefore(oldEndNode, oldStartNode);
diff(oldEndNode, newStartNode, replayer, rrnodeMirror);
oldEndNode = oldChildren[--oldEndIndex];
newStartNode = newChildren[++newStartIndex];
} else {
// none of the elements matched

if (!oldIdToIndex) {
oldIdToIndex = {};
for (let i = oldStartIndex; i <= oldEndIndex; i++) {
Expand Down Expand Up @@ -365,8 +385,11 @@ export function createOrGetNode(
domMirror: NodeMirror,
rrnodeMirror: Mirror,
): Node {
let node = domMirror.getNode(rrnodeMirror.getId(rrNode));
const nodeId = rrnodeMirror.getId(rrNode);
const sn = rrnodeMirror.getMeta(rrNode);
let node: Node | null = null;
// negative ids shouldn't be compared accross mirrors
if (nodeId > -1) node = domMirror.getNode(nodeId);
if (node !== null) return node;
switch (rrNode.RRNodeType) {
case RRNodeType.Document:
Expand Down
5 changes: 3 additions & 2 deletions packages/rrdom/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,11 @@ import {
} from './document';

export class RRDocument extends BaseRRDocumentImpl(RRNode) {
private UNSERIALIZED_STARTING_ID = -2;
// In the rrweb replayer, there are some unserialized nodes like the element that stores the injected style rules.
// These unserialized nodes may interfere the execution of the diff algorithm.
// The id of serialized node is larger than 0. So this value less than 0 is used as id for these unserialized nodes.
private _unserializedId = -1;
private _unserializedId = this.UNSERIALIZED_STARTING_ID;

/**
* Every time the id is used, it will minus 1 automatically to avoid collisions.
Expand Down Expand Up @@ -135,7 +136,7 @@ export class RRDocument extends BaseRRDocumentImpl(RRNode) {

open() {
super.open();
this._unserializedId = -1;
this._unserializedId = this.UNSERIALIZED_STARTING_ID;
}
}

Expand Down
260 changes: 130 additions & 130 deletions packages/rrdom/test/__snapshots__/virtual-dom.test.ts.snap
Original file line number Diff line number Diff line change
@@ -1,118 +1,118 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`RRDocument for browser environment create a RRDocument from a html document can build from a common html 1`] = `
"-1 RRDocument
-2 RRDocumentType
-3 HTML lang=\\"en\\"
-4 HEAD
-5 RRText text=\\"\\\\n \\"
-6 META charset=\\"UTF-8\\"
-7 RRText text=\\"\\\\n \\"
-8 META name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0\\"
-9 RRText text=\\"\\\\n \\"
-10 TITLE
-11 RRText text=\\"Main\\"
-12 RRText text=\\"\\\\n \\"
-13 LINK rel=\\"stylesheet\\" href=\\"somelink\\"
-14 RRText text=\\"\\\\n \\"
-15 STYLE
-16 RRText text=\\"\\\\n h1 {\\\\n color: 'black';\\\\n }\\\\n .blocks {\\\\n padding: 0;\\\\n }\\\\n .blocks1 {\\\\n margin: 0;\\\\n }\\\\n #block1 {\\\\n width: 100px;\\\\n height: 200px;\\\\n }\\\\n @import url('main.css');\\\\n \\"
-17 RRText text=\\"\\\\n \\"
-18 RRText text=\\"\\\\n \\"
-19 BODY
-20 RRText text=\\"\\\\n \\"
-21 H1
-22 RRText text=\\"This is a h1 heading\\"
-23 RRText text=\\"\\\\n \\"
-24 H1 style=\\"font-size: 16px\\"
-25 RRText text=\\"This is a h1 heading with styles\\"
-26 RRText text=\\"\\\\n \\"
-27 DIV id=\\"block1\\" class=\\"blocks blocks1\\"
-28 RRText text=\\"\\\\n \\"
-29 DIV id=\\"block2\\" class=\\"blocks blocks1 :hover\\"
-30 RRText text=\\"\\\\n Text 1\\\\n \\"
-31 DIV id=\\"block3\\"
-32 RRText text=\\"\\\\n \\"
-33 P
-34 RRText text=\\"This is a paragraph\\"
-35 RRText text=\\"\\\\n \\"
-36 BUTTON
-37 RRText text=\\"button1\\"
-38 RRText text=\\"\\\\n \\"
-39 RRText text=\\"\\\\n Text 2\\\\n \\"
-40 RRText text=\\"\\\\n \\"
-41 IMG src=\\"somelink\\" alt=\\"This is an image\\"
-42 RRText text=\\"\\\\n \\"
-43 RRComment text=\\" This is a line of comment \\"
-44 RRText text=\\"\\\\n \\"
-45 FORM
-46 RRText text=\\"\\\\n \\"
-47 INPUT type=\\"text\\" id=\\"input1\\"
-48 RRText text=\\"\\\\n \\"
-49 RRText text=\\"\\\\n \\"
-50 RRText text=\\"\\\\n \\\\n\\\\n\\"
"-2 RRDocument
-3 RRDocumentType
-4 HTML lang=\\"en\\"
-5 HEAD
-6 RRText text=\\"\\\\n \\"
-7 META charset=\\"UTF-8\\"
-8 RRText text=\\"\\\\n \\"
-9 META name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0\\"
-10 RRText text=\\"\\\\n \\"
-11 TITLE
-12 RRText text=\\"Main\\"
-13 RRText text=\\"\\\\n \\"
-14 LINK rel=\\"stylesheet\\" href=\\"somelink\\"
-15 RRText text=\\"\\\\n \\"
-16 STYLE
-17 RRText text=\\"\\\\n h1 {\\\\n color: 'black';\\\\n }\\\\n .blocks {\\\\n padding: 0;\\\\n }\\\\n .blocks1 {\\\\n margin: 0;\\\\n }\\\\n #block1 {\\\\n width: 100px;\\\\n height: 200px;\\\\n }\\\\n @import url('main.css');\\\\n \\"
-18 RRText text=\\"\\\\n \\"
-19 RRText text=\\"\\\\n \\"
-20 BODY
-21 RRText text=\\"\\\\n \\"
-22 H1
-23 RRText text=\\"This is a h1 heading\\"
-24 RRText text=\\"\\\\n \\"
-25 H1 style=\\"font-size: 16px\\"
-26 RRText text=\\"This is a h1 heading with styles\\"
-27 RRText text=\\"\\\\n \\"
-28 DIV id=\\"block1\\" class=\\"blocks blocks1\\"
-29 RRText text=\\"\\\\n \\"
-30 DIV id=\\"block2\\" class=\\"blocks blocks1 :hover\\"
-31 RRText text=\\"\\\\n Text 1\\\\n \\"
-32 DIV id=\\"block3\\"
-33 RRText text=\\"\\\\n \\"
-34 P
-35 RRText text=\\"This is a paragraph\\"
-36 RRText text=\\"\\\\n \\"
-37 BUTTON
-38 RRText text=\\"button1\\"
-39 RRText text=\\"\\\\n \\"
-40 RRText text=\\"\\\\n Text 2\\\\n \\"
-41 RRText text=\\"\\\\n \\"
-42 IMG src=\\"somelink\\" alt=\\"This is an image\\"
-43 RRText text=\\"\\\\n \\"
-44 RRComment text=\\" This is a line of comment \\"
-45 RRText text=\\"\\\\n \\"
-46 FORM
-47 RRText text=\\"\\\\n \\"
-48 INPUT type=\\"text\\" id=\\"input1\\"
-49 RRText text=\\"\\\\n \\"
-50 RRText text=\\"\\\\n \\"
-51 RRText text=\\"\\\\n \\\\n\\\\n\\"
"
`;

exports[`RRDocument for browser environment create a RRDocument from a html document can build from a html containing nested shadow doms 1`] = `
"-1 RRDocument
-2 RRDocumentType
-3 HTML lang=\\"en\\"
-4 HEAD
-5 RRText text=\\"\\\\n \\"
-6 META charset=\\"UTF-8\\"
-7 RRText text=\\"\\\\n \\"
-8 META name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0\\"
-9 RRText text=\\"\\\\n \\"
-10 TITLE
-11 RRText text=\\"shadow dom\\"
-12 RRText text=\\"\\\\n \\"
-13 RRText text=\\"\\\\n \\"
-14 BODY
-15 RRText text=\\"\\\\n \\"
-16 DIV
-17 SHADOWROOT
-18 RRText text=\\"\\\\n \\"
-19 SPAN
-20 RRText text=\\" shadow dom one \\"
-21 RRText text=\\"\\\\n \\"
-22 DIV
-23 SHADOWROOT
-24 RRText text=\\"\\\\n \\"
-25 SPAN
-26 RRText text=\\" shadow dom two \\"
-27 RRText text=\\"\\\\n \\"
-28 RRText text=\\"\\\\n \\\\n \\"
-29 RRText text=\\"\\\\n \\"
-30 RRText text=\\"\\\\n \\\\n \\"
-31 RRText text=\\"\\\\n \\\\n\\\\n\\"
"-2 RRDocument
-3 RRDocumentType
-4 HTML lang=\\"en\\"
-5 HEAD
-6 RRText text=\\"\\\\n \\"
-7 META charset=\\"UTF-8\\"
-8 RRText text=\\"\\\\n \\"
-9 META name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0\\"
-10 RRText text=\\"\\\\n \\"
-11 TITLE
-12 RRText text=\\"shadow dom\\"
-13 RRText text=\\"\\\\n \\"
-14 RRText text=\\"\\\\n \\"
-15 BODY
-16 RRText text=\\"\\\\n \\"
-17 DIV
-18 SHADOWROOT
-19 RRText text=\\"\\\\n \\"
-20 SPAN
-21 RRText text=\\" shadow dom one \\"
-22 RRText text=\\"\\\\n \\"
-23 DIV
-24 SHADOWROOT
-25 RRText text=\\"\\\\n \\"
-26 SPAN
-27 RRText text=\\" shadow dom two \\"
-28 RRText text=\\"\\\\n \\"
-29 RRText text=\\"\\\\n \\\\n \\"
-30 RRText text=\\"\\\\n \\"
-31 RRText text=\\"\\\\n \\\\n \\"
-32 RRText text=\\"\\\\n \\\\n\\\\n\\"
"
`;

exports[`RRDocument for browser environment create a RRDocument from a html document can build from a xml page 1`] = `
"-1 RRDocument
-2 XML
-3 RRCDATASection data=\\"Some <CDATA> data & then some\\"
"-2 RRDocument
-3 XML
-4 RRCDATASection data=\\"Some <CDATA> data & then some\\"
"
`;

exports[`RRDocument for browser environment create a RRDocument from a html document can build from an iframe html 1`] = `
"-1 RRDocument
-2 RRDocumentType
-3 HTML lang=\\"en\\"
-4 HEAD
-5 RRText text=\\"\\\\n \\"
-6 META charset=\\"UTF-8\\"
-7 RRText text=\\"\\\\n \\"
-8 META name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0\\"
-9 RRText text=\\"\\\\n \\"
-10 TITLE
-11 RRText text=\\"Iframe\\"
-12 RRText text=\\"\\\\n \\"
-13 RRText text=\\"\\\\n \\"
-14 BODY
-15 RRText text=\\"\\\\n \\"
-16 IFRAME id=\\"iframe1\\" srcdoc=\\"
"-2 RRDocument
-3 RRDocumentType
-4 HTML lang=\\"en\\"
-5 HEAD
-6 RRText text=\\"\\\\n \\"
-7 META charset=\\"UTF-8\\"
-8 RRText text=\\"\\\\n \\"
-9 META name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0\\"
-10 RRText text=\\"\\\\n \\"
-11 TITLE
-12 RRText text=\\"Iframe\\"
-13 RRText text=\\"\\\\n \\"
-14 RRText text=\\"\\\\n \\"
-15 BODY
-16 RRText text=\\"\\\\n \\"
-17 IFRAME id=\\"iframe1\\" srcdoc=\\"
<html>
<head>
<meta charset='UTF-8' />
Expand All @@ -126,35 +126,35 @@ exports[`RRDocument for browser environment create a RRDocument from a html docu
<iframe id='iframe3' srcdoc='<div>This is a block inside the iframe3.</div>'>
</body>
</html>\\"
-17 RRDocument
-18 HTML
-19 HEAD
-20 RRText text=\\"\\\\n \\"
-21 META charset=\\"UTF-8\\"
-22 RRText text=\\"\\\\n \\"
-23 META name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0\\"
-24 RRText text=\\"\\\\n \\"
-25 RRText text=\\"\\\\n \\"
-26 BODY
-27 RRText text=\\"\\\\n \\"
-28 DIV
-29 RRText text=\\"This is a block inside the iframe1.\\"
-30 RRText text=\\"\\\\n \\"
-31 IFRAME id=\\"iframe3\\" srcdoc=\\"<div>This is a block inside the iframe3.</div>\\"
-32 RRDocument
-33 HTML
-34 HEAD
-35 BODY
-36 DIV
-37 RRText text=\\"This is a block inside the iframe3.\\"
-38 RRText text=\\"\\\\n \\"
-39 IFRAME id=\\"iframe2\\" srcdoc=\\"<div>This is a block inside the iframe2.</div>\\"
-40 RRDocument
-41 HTML
-42 HEAD
-43 BODY
-44 DIV
-45 RRText text=\\"This is a block inside the iframe2.\\"
-46 RRText text=\\"\\\\n \\\\n\\\\n\\"
-18 RRDocument
-19 HTML
-20 HEAD
-21 RRText text=\\"\\\\n \\"
-22 META charset=\\"UTF-8\\"
-23 RRText text=\\"\\\\n \\"
-24 META name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0\\"
-25 RRText text=\\"\\\\n \\"
-26 RRText text=\\"\\\\n \\"
-27 BODY
-28 RRText text=\\"\\\\n \\"
-29 DIV
-30 RRText text=\\"This is a block inside the iframe1.\\"
-31 RRText text=\\"\\\\n \\"
-32 IFRAME id=\\"iframe3\\" srcdoc=\\"<div>This is a block inside the iframe3.</div>\\"
-33 RRDocument
-34 HTML
-35 HEAD
-36 BODY
-37 DIV
-38 RRText text=\\"This is a block inside the iframe3.\\"
-39 RRText text=\\"\\\\n \\"
-40 IFRAME id=\\"iframe2\\" srcdoc=\\"<div>This is a block inside the iframe2.</div>\\"
-41 RRDocument
-42 HTML
-43 HEAD
-44 BODY
-45 DIV
-46 RRText text=\\"This is a block inside the iframe2.\\"
-47 RRText text=\\"\\\\n \\\\n\\\\n\\"
"
`;
Loading