From 72aae99a05a4596dbd782afebcd00e00324872e1 Mon Sep 17 00:00:00 2001 From: JonasBa Date: Thu, 18 Jul 2024 21:44:56 -0400 Subject: [PATCH 1/2] ref(mutation) refactor pushadd to class member method --- .changeset/strange-papayas-pump.md | 5 + packages/rrweb/src/record/mutation.ts | 154 ++++++++++++++------------ 2 files changed, 89 insertions(+), 70 deletions(-) create mode 100644 .changeset/strange-papayas-pump.md diff --git a/.changeset/strange-papayas-pump.md b/.changeset/strange-papayas-pump.md new file mode 100644 index 0000000000..1456071498 --- /dev/null +++ b/.changeset/strange-papayas-pump.md @@ -0,0 +1,5 @@ +--- +"rrweb": minor +--- + +Refactor pushAdd to a class member method diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index a798441969..13548deeec 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -133,6 +133,16 @@ class DoubleLinkedList { const moveKey = (id: number, parentId: number) => `${id}@${parentId}`; +const getNextId = (n: Node, mirror: observerParam['mirror']): number | null => { + let ns: Node | null = n; + let nextId: number | null = IGNORED_NODE; // slimDOM: ignored + while (nextId === IGNORED_NODE) { + ns = ns && ns.nextSibling; + nextId = ns && mirror.getId(ns); + } + return nextId; +}; + /** * controls behaviour of a MutationObserver */ @@ -259,6 +269,74 @@ export default class MutationBuffer { this.emit(); // clears buffer if not locked/frozen }; + private pushAdd( + n: Node, + adds: addedNodeMutation[], + addList: DoubleLinkedList, + addedIds: Set, + ) { + if ( + !n.parentNode || + !inDom(n) || + (n.parentNode as Element).tagName === 'TEXTAREA' + ) { + return; + } + const parentId = isShadowRoot(n.parentNode) + ? this.mirror.getId(getShadowHost(n)) + : this.mirror.getId(n.parentNode); + const nextId = getNextId(n, this.mirror); + + if (parentId === -1 || nextId === -1) { + return addList.addNode(n); + } + + const sn = serializeNodeWithId(n, { + doc: this.doc, + mirror: this.mirror, + blockClass: this.blockClass, + blockSelector: this.blockSelector, + maskTextClass: this.maskTextClass, + maskTextSelector: this.maskTextSelector, + skipChild: true, + newlyAddedElement: true, + inlineStylesheet: this.inlineStylesheet, + maskInputOptions: this.maskInputOptions, + maskTextFn: this.maskTextFn, + maskInputFn: this.maskInputFn, + slimDOMOptions: this.slimDOMOptions, + dataURLOptions: this.dataURLOptions, + recordCanvas: this.recordCanvas, + inlineImages: this.inlineImages, + onSerialize: (currentN) => { + if (isSerializedIframe(currentN, this.mirror)) { + this.iframeManager.addIframe(currentN as HTMLIFrameElement); + } + if (isSerializedStylesheet(currentN, this.mirror)) { + this.stylesheetManager.trackLinkElement(currentN as HTMLLinkElement); + } + if (hasShadowRoot(n)) { + this.shadowDomManager.addShadowRoot(n.shadowRoot, this.doc); + } + }, + onIframeLoad: (iframe, childSn) => { + this.iframeManager.attachIframe(iframe, childSn); + this.shadowDomManager.observeAttachShadow(iframe); + }, + onStylesheetLoad: (link, childSn) => { + this.stylesheetManager.attachLinkElement(link, childSn); + }, + }); + if (sn) { + adds.push({ + parentId, + nextId, + node: sn, + }); + addedIds.add(sn.id); + } + } + public emit = () => { if (this.frozen || this.locked) { return; @@ -284,69 +362,6 @@ export default class MutationBuffer { } return nextId; }; - const pushAdd = (n: Node) => { - if ( - !n.parentNode || - !inDom(n) || - (n.parentNode as Element).tagName === 'TEXTAREA' - ) { - return; - } - const parentId = isShadowRoot(n.parentNode) - ? this.mirror.getId(getShadowHost(n)) - : this.mirror.getId(n.parentNode); - const nextId = getNextId(n); - if (parentId === -1 || nextId === -1) { - return addList.addNode(n); - } - const sn = serializeNodeWithId(n, { - doc: this.doc, - mirror: this.mirror, - blockClass: this.blockClass, - blockSelector: this.blockSelector, - maskTextClass: this.maskTextClass, - maskTextSelector: this.maskTextSelector, - skipChild: true, - newlyAddedElement: true, - inlineStylesheet: this.inlineStylesheet, - maskInputOptions: this.maskInputOptions, - maskTextFn: this.maskTextFn, - maskInputFn: this.maskInputFn, - slimDOMOptions: this.slimDOMOptions, - dataURLOptions: this.dataURLOptions, - recordCanvas: this.recordCanvas, - inlineImages: this.inlineImages, - onSerialize: (currentN) => { - if (isSerializedIframe(currentN, this.mirror)) { - this.iframeManager.addIframe(currentN as HTMLIFrameElement); - } - if (isSerializedStylesheet(currentN, this.mirror)) { - this.stylesheetManager.trackLinkElement( - currentN as HTMLLinkElement, - ); - } - if (hasShadowRoot(n)) { - this.shadowDomManager.addShadowRoot(n.shadowRoot, this.doc); - } - }, - onIframeLoad: (iframe, childSn) => { - this.iframeManager.attachIframe(iframe, childSn); - this.shadowDomManager.observeAttachShadow(iframe); - }, - onStylesheetLoad: (link, childSn) => { - this.stylesheetManager.attachLinkElement(link, childSn); - }, - }); - if (sn) { - adds.push({ - parentId, - nextId, - node: sn, - }); - addedIds.add(sn.id); - } - }; - while (this.mapRemoves.length) { this.mirror.removeNodeFromMap(this.mapRemoves.shift()!); } @@ -358,7 +373,7 @@ export default class MutationBuffer { ) { continue; } - pushAdd(n); + this.pushAdd(n, adds, addList, addedIds); } for (const n of this.addedSet) { @@ -366,9 +381,9 @@ export default class MutationBuffer { !isAncestorInSet(this.droppedSet, n) && !isParentRemoved(this.removes, n, this.mirror) ) { - pushAdd(n); + this.pushAdd(n, adds, addList, addedIds); } else if (isAncestorInSet(this.movedSet, n)) { - pushAdd(n); + this.pushAdd(n, adds, addList, addedIds); } else { this.droppedSet.add(n); } @@ -391,12 +406,11 @@ export default class MutationBuffer { tailNode = tailNode.previous; // ensure _node is defined before attempting to find value if (_node) { - const parentId = this.mirror.getId(_node.value.parentNode); const nextId = getNextId(_node.value); - if (nextId === -1) continue; // nextId !== -1 && parentId !== -1 - else if (parentId !== -1) { + const parentId = this.mirror.getId(_node.value.parentNode); + if (parentId !== -1) { node = _node; break; } @@ -434,7 +448,7 @@ export default class MutationBuffer { } candidate = node.previous; addList.removeNode(node.value); - pushAdd(node.value); + this.pushAdd(node.value, adds, addList, addedIds); } const payload = { From 99194c4babc81cba77337f54963a98d93f076d0e Mon Sep 17 00:00:00 2001 From: JonasBa Date: Thu, 19 Sep 2024 12:08:29 -0400 Subject: [PATCH 2/2] update pushAdd --- packages/rrweb/src/record/mutation.ts | 32 ++++++++++++++++++--------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index 13548deeec..6019919e17 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -32,6 +32,7 @@ import { getShadowHost, closestElementOfNode, } from '../utils'; +import dom from '@rrweb/utils'; type DoubleLinkedListNode = { previous: DoubleLinkedListNode | null; @@ -275,22 +276,31 @@ export default class MutationBuffer { addList: DoubleLinkedList, addedIds: Set, ) { - if ( - !n.parentNode || - !inDom(n) || - (n.parentNode as Element).tagName === 'TEXTAREA' - ) { + const parent = dom.parentNode(n); + if (!parent || !inDom(n)) { return; } - const parentId = isShadowRoot(n.parentNode) + let cssCaptured = false; + if (n.nodeType === Node.TEXT_NODE) { + const parentTag = (parent as Element).tagName; + if (parentTag === 'TEXTAREA') { + // genTextAreaValueMutation already called via parent + return; + } else if (parentTag === 'STYLE' && this.addedSet.has(parent)) { + // css content will be recorded via parent's _cssText attribute when + // mutation adds entire