diff --git a/package.json b/package.json index b5d940897..7b400b940 100644 --- a/package.json +++ b/package.json @@ -102,5 +102,10 @@ }, "browserslist": [ "> 0.5%, last 2 versions, Firefox ESR, not dead, IE 11" - ] + ], + "pnpm": { + "patchedDependencies": { + "rrweb@2.0.0-alpha.11": "patches/rrweb@2.0.0-alpha.11.patch" + } + } } diff --git a/patches/rrweb@2.0.0-alpha.11.patch b/patches/rrweb@2.0.0-alpha.11.patch new file mode 100644 index 000000000..a91212f72 --- /dev/null +++ b/patches/rrweb@2.0.0-alpha.11.patch @@ -0,0 +1,1134 @@ +diff --git a/es/rrweb/packages/rrweb/src/record/mutation.js b/es/rrweb/packages/rrweb/src/record/mutation.js +index 81ad4520d1435d088752a85f9a55a68ad6e0f514..39d96af4f65eb32ba13eb7d5a81076e996c9df26 100644 +--- a/es/rrweb/packages/rrweb/src/record/mutation.js ++++ b/es/rrweb/packages/rrweb/src/record/mutation.js +@@ -1,563 +1,607 @@ +-import { isShadowRoot, isNativeShadowDom, getInputType, maskInputValue, ignoreAttribute, transformAttribute, toLowerCase, needMaskingText, IGNORED_NODE, serializeNodeWithId } from '../../../rrweb-snapshot/es/rrweb-snapshot.js'; +-import { isIgnored, isBlocked, isSerialized, isAncestorRemoved, hasShadowRoot, inDom, getShadowHost, isSerializedIframe, isSerializedStylesheet } from '../utils.js'; ++import { ++ isShadowRoot, ++ isNativeShadowDom, ++ getInputType, ++ maskInputValue, ++ ignoreAttribute, ++ transformAttribute, ++ toLowerCase, ++ needMaskingText, ++ IGNORED_NODE, ++ serializeNodeWithId, ++} from "../../../rrweb-snapshot/es/rrweb-snapshot.js"; ++import { ++ isIgnored, ++ isBlocked, ++ isSerialized, ++ isAncestorRemoved, ++ hasShadowRoot, ++ inDom, ++ getShadowHost, ++ isSerializedIframe, ++ isSerializedStylesheet, ++} from "../utils.js"; + + function isNodeInLinkedList(n) { +- return '__ln' in n; ++ return "__ln" in n; + } + class DoubleLinkedList { +- constructor() { +- this.length = 0; +- this.head = null; ++ constructor() { ++ this.length = 0; ++ this.head = null; ++ this.tail = null; ++ } ++ get(position) { ++ if (position >= this.length) { ++ throw new Error("Position outside of list range"); ++ } ++ let current = this.head; ++ for (let index = 0; index < position; index++) { ++ current = ++ (current === null || current === void 0 ? void 0 : current.next) || ++ null; ++ } ++ return current; ++ } ++ addNode(n) { ++ const node = { ++ value: n, ++ previous: null, ++ next: null, ++ }; ++ n.__ln = node; ++ if (n.previousSibling && isNodeInLinkedList(n.previousSibling)) { ++ const current = n.previousSibling.__ln.next; ++ node.next = current; ++ node.previous = n.previousSibling.__ln; ++ n.previousSibling.__ln.next = node; ++ if (current) { ++ current.previous = node; ++ } ++ } else if ( ++ n.nextSibling && ++ isNodeInLinkedList(n.nextSibling) && ++ n.nextSibling.__ln.previous ++ ) { ++ const current = n.nextSibling.__ln.previous; ++ node.previous = current; ++ node.next = n.nextSibling.__ln; ++ n.nextSibling.__ln.previous = node; ++ if (current) { ++ current.next = node; ++ } ++ } else { ++ if (this.head) { ++ this.head.previous = node; ++ } ++ node.next = this.head; ++ this.head = node; ++ } ++ if (node.next === null) { ++ this.tail = node; ++ } ++ this.length++; ++ } ++ removeNode(n) { ++ const current = n.__ln; ++ if (!this.head) { ++ return; ++ } ++ if (!current.previous) { ++ this.head = current.next; ++ if (this.head) { ++ this.head.previous = null; ++ } else { + this.tail = null; ++ } ++ } else { ++ current.previous.next = current.next; ++ if (current.next) { ++ current.next.previous = current.previous; ++ } else { ++ this.tail = current.previous; ++ } + } +- get(position) { +- if (position >= this.length) { +- throw new Error('Position outside of list range'); ++ if (n.__ln) { ++ delete n.__ln; ++ } ++ this.length--; ++ } ++} ++const moveKey = (id, parentId) => `${id}@${parentId}`; ++class MutationBuffer { ++ constructor() { ++ this.frozen = false; ++ this.locked = false; ++ this.texts = []; ++ this.attributes = []; ++ this.removes = []; ++ this.mapRemoves = []; ++ this.movedMap = {}; ++ this.addedSet = new Set(); ++ this.movedSet = new Set(); ++ this.droppedSet = new Set(); ++ this.unattachedDoc = null; ++ this.processMutations = (mutations) => { ++ mutations.forEach(this.processMutation); ++ this.emit(); ++ }; ++ this.emit = () => { ++ if (this.frozen || this.locked) { ++ return; ++ } ++ const adds = []; ++ const addedIds = new Set(); ++ const addList = new DoubleLinkedList(); ++ const getNextId = (n) => { ++ let ns = n; ++ let nextId = IGNORED_NODE; ++ while (nextId === IGNORED_NODE) { ++ ns = ns && ns.nextSibling; ++ nextId = ns && this.mirror.getId(ns); + } +- let current = this.head; +- for (let index = 0; index < position; index++) { +- current = (current === null || current === void 0 ? void 0 : current.next) || null; ++ return nextId; ++ }; ++ const pushAdd = (n) => { ++ if (!n.parentNode || !inDom(n)) { ++ return; + } +- return current; +- } +- addNode(n) { +- const node = { +- value: n, +- previous: null, +- next: null, +- }; +- n.__ln = node; +- if (n.previousSibling && isNodeInLinkedList(n.previousSibling)) { +- const current = n.previousSibling.__ln.next; +- node.next = current; +- node.previous = n.previousSibling.__ln; +- n.previousSibling.__ln.next = node; +- if (current) { +- current.previous = node; +- } ++ 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); + } +- else if (n.nextSibling && +- isNodeInLinkedList(n.nextSibling) && +- n.nextSibling.__ln.previous) { +- const current = n.nextSibling.__ln.previous; +- node.previous = current; +- node.next = n.nextSibling.__ln; +- n.nextSibling.__ln.previous = node; +- if (current) { +- current.next = node; ++ 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); + } +- } +- else { +- if (this.head) { +- this.head.previous = node; ++ if (isSerializedStylesheet(currentN, this.mirror)) { ++ this.stylesheetManager.trackLinkElement(currentN); ++ } ++ if (hasShadowRoot(n)) { ++ this.shadowDomManager.addShadowRoot(n.shadowRoot, this.doc); + } +- node.next = this.head; +- this.head = node; ++ }, ++ 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); + } +- if (node.next === null) { +- this.tail = node; ++ }; ++ while (this.mapRemoves.length) { ++ this.mirror.removeNodeFromMap(this.mapRemoves.shift()); ++ } ++ for (const n of this.movedSet) { ++ if ( ++ isParentRemoved(this.removes, n, this.mirror) && ++ !this.movedSet.has(n.parentNode) ++ ) { ++ continue; + } +- this.length++; +- } +- removeNode(n) { +- const current = n.__ln; +- if (!this.head) { +- return; ++ pushAdd(n); ++ } ++ for (const n of this.addedSet) { ++ if ( ++ !isAncestorInSet(this.droppedSet, n) && ++ !isParentRemoved(this.removes, n, this.mirror) ++ ) { ++ pushAdd(n); ++ } else if (isAncestorInSet(this.movedSet, n)) { ++ pushAdd(n); ++ } else { ++ this.droppedSet.add(n); + } +- if (!current.previous) { +- this.head = current.next; +- if (this.head) { +- this.head.previous = null; +- } +- else { +- this.tail = null; +- } ++ } ++ let candidate = null; ++ while (addList.length) { ++ let node = null; ++ if (candidate) { ++ const parentId = this.mirror.getId(candidate.value.parentNode); ++ const nextId = getNextId(candidate.value); ++ if (parentId !== -1 && nextId !== -1) { ++ node = candidate; ++ } + } +- else { +- current.previous.next = current.next; +- if (current.next) { +- current.next.previous = current.previous; +- } +- else { +- this.tail = current.previous; ++ if (!node) { ++ let tailNode = addList.tail; ++ while (tailNode) { ++ const _node = tailNode; ++ tailNode = tailNode.previous; ++ if (_node) { ++ const parentId = this.mirror.getId(_node.value.parentNode); ++ const nextId = getNextId(_node.value); ++ if (nextId === -1) continue; ++ else if (parentId !== -1) { ++ node = _node; ++ break; ++ } else { ++ const unhandledNode = _node.value; ++ if ( ++ unhandledNode.parentNode && ++ unhandledNode.parentNode.nodeType === ++ Node.DOCUMENT_FRAGMENT_NODE ++ ) { ++ const shadowHost = unhandledNode.parentNode.host; ++ const parentId = this.mirror.getId(shadowHost); ++ if (parentId !== -1) { ++ node = _node; ++ break; ++ } ++ } ++ } + } ++ } + } +- if (n.__ln) { +- delete n.__ln; ++ if (!node) { ++ while (addList.head) { ++ addList.removeNode(addList.head.value); ++ } ++ break; + } +- this.length--; +- } +-} +-const moveKey = (id, parentId) => `${id}@${parentId}`; +-class MutationBuffer { +- constructor() { +- this.frozen = false; +- this.locked = false; +- this.texts = []; +- this.attributes = []; +- this.removes = []; +- this.mapRemoves = []; +- this.movedMap = {}; +- this.addedSet = new Set(); +- this.movedSet = new Set(); +- this.droppedSet = new Set(); +- this.processMutations = (mutations) => { +- mutations.forEach(this.processMutation); +- this.emit(); +- }; +- this.emit = () => { +- if (this.frozen || this.locked) { +- return; +- } +- const adds = []; +- const addedIds = new Set(); +- const addList = new DoubleLinkedList(); +- const getNextId = (n) => { +- let ns = n; +- let nextId = IGNORED_NODE; +- while (nextId === IGNORED_NODE) { +- ns = ns && ns.nextSibling; +- nextId = ns && this.mirror.getId(ns); +- } +- return nextId; +- }; +- const pushAdd = (n) => { +- if (!n.parentNode || !inDom(n)) { +- 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); +- } +- if (isSerializedStylesheet(currentN, this.mirror)) { +- this.stylesheetManager.trackLinkElement(currentN); +- } +- 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()); +- } +- for (const n of this.movedSet) { +- if (isParentRemoved(this.removes, n, this.mirror) && +- !this.movedSet.has(n.parentNode)) { +- continue; +- } +- pushAdd(n); +- } +- for (const n of this.addedSet) { +- if (!isAncestorInSet(this.droppedSet, n) && +- !isParentRemoved(this.removes, n, this.mirror)) { +- pushAdd(n); +- } +- else if (isAncestorInSet(this.movedSet, n)) { +- pushAdd(n); +- } +- else { +- this.droppedSet.add(n); +- } +- } +- let candidate = null; +- while (addList.length) { +- let node = null; +- if (candidate) { +- const parentId = this.mirror.getId(candidate.value.parentNode); +- const nextId = getNextId(candidate.value); +- if (parentId !== -1 && nextId !== -1) { +- node = candidate; +- } +- } +- if (!node) { +- let tailNode = addList.tail; +- while (tailNode) { +- const _node = tailNode; +- tailNode = tailNode.previous; +- if (_node) { +- const parentId = this.mirror.getId(_node.value.parentNode); +- const nextId = getNextId(_node.value); +- if (nextId === -1) +- continue; +- else if (parentId !== -1) { +- node = _node; +- break; +- } +- else { +- const unhandledNode = _node.value; +- if (unhandledNode.parentNode && +- unhandledNode.parentNode.nodeType === +- Node.DOCUMENT_FRAGMENT_NODE) { +- const shadowHost = unhandledNode.parentNode +- .host; +- const parentId = this.mirror.getId(shadowHost); +- if (parentId !== -1) { +- node = _node; +- break; +- } +- } +- } +- } +- } ++ candidate = node.previous; ++ addList.removeNode(node.value); ++ pushAdd(node.value); ++ } ++ const payload = { ++ texts: this.texts ++ .map((text) => ({ ++ id: this.mirror.getId(text.node), ++ value: text.value, ++ })) ++ .filter((text) => !addedIds.has(text.id)) ++ .filter((text) => this.mirror.has(text.id)), ++ attributes: this.attributes ++ .map((attribute) => { ++ const { attributes } = attribute; ++ if (typeof attributes.style === "string") { ++ const diffAsStr = JSON.stringify(attribute.styleDiff); ++ const unchangedAsStr = JSON.stringify(attribute._unchangedStyles); ++ if (diffAsStr.length < attributes.style.length) { ++ if ( ++ (diffAsStr + unchangedAsStr).split("var(").length === ++ attributes.style.split("var(").length ++ ) { ++ attributes.style = attribute.styleDiff; + } +- if (!node) { +- while (addList.head) { +- addList.removeNode(addList.head.value); +- } +- break; +- } +- candidate = node.previous; +- addList.removeNode(node.value); +- pushAdd(node.value); ++ } + } +- const payload = { +- texts: this.texts +- .map((text) => ({ +- id: this.mirror.getId(text.node), +- value: text.value, +- })) +- .filter((text) => !addedIds.has(text.id)) +- .filter((text) => this.mirror.has(text.id)), +- attributes: this.attributes +- .map((attribute) => { +- const { attributes } = attribute; +- if (typeof attributes.style === 'string') { +- const diffAsStr = JSON.stringify(attribute.styleDiff); +- const unchangedAsStr = JSON.stringify(attribute._unchangedStyles); +- if (diffAsStr.length < attributes.style.length) { +- if ((diffAsStr + unchangedAsStr).split('var(').length === +- attributes.style.split('var(').length) { +- attributes.style = attribute.styleDiff; +- } +- } +- } +- return { +- id: this.mirror.getId(attribute.node), +- attributes: attributes, +- }; +- }) +- .filter((attribute) => !addedIds.has(attribute.id)) +- .filter((attribute) => this.mirror.has(attribute.id)), +- removes: this.removes, +- adds, ++ return { ++ id: this.mirror.getId(attribute.node), ++ attributes: attributes, + }; +- if (!payload.texts.length && +- !payload.attributes.length && +- !payload.removes.length && +- !payload.adds.length) { +- return; +- } +- this.texts = []; +- this.attributes = []; +- this.removes = []; +- this.addedSet = new Set(); +- this.movedSet = new Set(); +- this.droppedSet = new Set(); +- this.movedMap = {}; +- this.mutationCb(payload); +- }; +- this.processMutation = (m) => { +- if (isIgnored(m.target, this.mirror)) { +- return; +- } +- let unattachedDoc; +- try { +- unattachedDoc = document.implementation.createHTMLDocument(); +- } +- catch (e) { +- unattachedDoc = this.doc; +- } +- switch (m.type) { +- case 'characterData': { +- const value = m.target.textContent; +- if (!isBlocked(m.target, this.blockClass, this.blockSelector, false) && +- value !== m.oldValue) { +- this.texts.push({ +- value: needMaskingText(m.target, this.maskTextClass, this.maskTextSelector) && value +- ? this.maskTextFn +- ? this.maskTextFn(value) +- : value.replace(/[\S]/g, '*') +- : value, +- node: m.target, +- }); +- } +- break; +- } +- case 'attributes': { +- const target = m.target; +- let attributeName = m.attributeName; +- let value = m.target.getAttribute(attributeName); +- if (attributeName === 'value') { +- const type = getInputType(target); +- value = maskInputValue({ +- element: target, +- maskInputOptions: this.maskInputOptions, +- tagName: target.tagName, +- type, +- value, +- maskInputFn: this.maskInputFn, +- }); +- } +- if (isBlocked(m.target, this.blockClass, this.blockSelector, false) || +- value === m.oldValue) { +- return; +- } +- let item = this.attributes.find((a) => a.node === m.target); +- if (target.tagName === 'IFRAME' && +- attributeName === 'src' && +- !this.keepIframeSrcFn(value)) { +- if (!target.contentDocument) { +- attributeName = 'rr_src'; +- } +- else { +- return; +- } +- } +- if (!item) { +- item = { +- node: m.target, +- attributes: {}, +- styleDiff: {}, +- _unchangedStyles: {}, +- }; +- this.attributes.push(item); +- } +- if (attributeName === 'type' && +- target.tagName === 'INPUT' && +- (m.oldValue || '').toLowerCase() === 'password') { +- target.setAttribute('data-rr-is-password', 'true'); +- } +- if (!ignoreAttribute(target.tagName, attributeName)) { +- item.attributes[attributeName] = transformAttribute(this.doc, toLowerCase(target.tagName), toLowerCase(attributeName), value); +- if (attributeName === 'style') { +- const old = unattachedDoc.createElement('span'); +- if (m.oldValue) { +- old.setAttribute('style', m.oldValue); +- } +- for (const pname of Array.from(target.style)) { +- const newValue = target.style.getPropertyValue(pname); +- const newPriority = target.style.getPropertyPriority(pname); +- if (newValue !== old.style.getPropertyValue(pname) || +- newPriority !== old.style.getPropertyPriority(pname)) { +- if (newPriority === '') { +- item.styleDiff[pname] = newValue; +- } +- else { +- item.styleDiff[pname] = [newValue, newPriority]; +- } +- } +- else { +- item._unchangedStyles[pname] = [newValue, newPriority]; +- } +- } +- for (const pname of Array.from(old.style)) { +- if (target.style.getPropertyValue(pname) === '') { +- item.styleDiff[pname] = false; +- } +- } +- } +- } +- break; +- } +- case 'childList': { +- if (isBlocked(m.target, this.blockClass, this.blockSelector, true)) +- return; +- m.addedNodes.forEach((n) => this.genAdds(n, m.target)); +- m.removedNodes.forEach((n) => { +- const nodeId = this.mirror.getId(n); +- const parentId = isShadowRoot(m.target) +- ? this.mirror.getId(m.target.host) +- : this.mirror.getId(m.target); +- if (isBlocked(m.target, this.blockClass, this.blockSelector, false) || +- isIgnored(n, this.mirror) || +- !isSerialized(n, this.mirror)) { +- return; +- } +- if (this.addedSet.has(n)) { +- deepDelete(this.addedSet, n); +- this.droppedSet.add(n); +- } +- else if (this.addedSet.has(m.target) && nodeId === -1) ; +- else if (isAncestorRemoved(m.target, this.mirror)) ; +- else if (this.movedSet.has(n) && +- this.movedMap[moveKey(nodeId, parentId)]) { +- deepDelete(this.movedSet, n); +- } +- else { +- this.removes.push({ +- parentId, +- id: nodeId, +- isShadow: isShadowRoot(m.target) && isNativeShadowDom(m.target) +- ? true +- : undefined, +- }); +- } +- this.mapRemoves.push(n); +- }); +- break; +- } ++ }) ++ .filter((attribute) => !addedIds.has(attribute.id)) ++ .filter((attribute) => this.mirror.has(attribute.id)), ++ removes: this.removes, ++ adds, ++ }; ++ if ( ++ !payload.texts.length && ++ !payload.attributes.length && ++ !payload.removes.length && ++ !payload.adds.length ++ ) { ++ return; ++ } ++ this.texts = []; ++ this.attributes = []; ++ this.removes = []; ++ this.addedSet = new Set(); ++ this.movedSet = new Set(); ++ this.droppedSet = new Set(); ++ this.movedMap = {}; ++ this.mutationCb(payload); ++ }; ++ this.processMutation = (m) => { ++ if (isIgnored(m.target, this.mirror)) { ++ return; ++ } ++ switch (m.type) { ++ case "characterData": { ++ const value = m.target.textContent; ++ if ( ++ !isBlocked(m.target, this.blockClass, this.blockSelector, false) && ++ value !== m.oldValue ++ ) { ++ this.texts.push({ ++ value: ++ needMaskingText( ++ m.target, ++ this.maskTextClass, ++ this.maskTextSelector ++ ) && value ++ ? this.maskTextFn ++ ? this.maskTextFn(value) ++ : value.replace(/[\S]/g, "*") ++ : value, ++ node: m.target, ++ }); ++ } ++ break; ++ } ++ case "attributes": { ++ const target = m.target; ++ let attributeName = m.attributeName; ++ let value = m.target.getAttribute(attributeName); ++ if (attributeName === "value") { ++ const type = getInputType(target); ++ value = maskInputValue({ ++ element: target, ++ maskInputOptions: this.maskInputOptions, ++ tagName: target.tagName, ++ type, ++ value, ++ maskInputFn: this.maskInputFn, ++ }); ++ } ++ if ( ++ isBlocked(m.target, this.blockClass, this.blockSelector, false) || ++ value === m.oldValue ++ ) { ++ return; ++ } ++ let item = this.attributes.find((a) => a.node === m.target); ++ if ( ++ target.tagName === "IFRAME" && ++ attributeName === "src" && ++ !this.keepIframeSrcFn(value) ++ ) { ++ if (!target.contentDocument) { ++ attributeName = "rr_src"; ++ } else { ++ return; + } +- }; +- this.genAdds = (n, target) => { +- if (this.processedNodeManager.inOtherBuffer(n, this)) +- return; +- if (this.addedSet.has(n) || this.movedSet.has(n)) +- return; +- if (this.mirror.hasNode(n)) { +- if (isIgnored(n, this.mirror)) { +- return; ++ } ++ if (!item) { ++ item = { ++ node: m.target, ++ attributes: {}, ++ styleDiff: {}, ++ _unchangedStyles: {}, ++ }; ++ this.attributes.push(item); ++ } ++ if ( ++ attributeName === "type" && ++ target.tagName === "INPUT" && ++ (m.oldValue || "").toLowerCase() === "password" ++ ) { ++ target.setAttribute("data-rr-is-password", "true"); ++ } ++ if (!ignoreAttribute(target.tagName, attributeName)) { ++ item.attributes[attributeName] = transformAttribute( ++ this.doc, ++ toLowerCase(target.tagName), ++ toLowerCase(attributeName), ++ value ++ ); ++ if (attributeName === "style") { ++ if (!this.unattachedDoc) { ++ try { ++ // avoid upsetting original document from a Content Security point of view ++ this.unattachedDoc = ++ document.implementation.createHTMLDocument(); ++ } catch (e) { ++ // fallback to more direct method ++ this.unattachedDoc = this.doc; + } +- this.movedSet.add(n); +- let targetId = null; +- if (target && this.mirror.hasNode(target)) { +- targetId = this.mirror.getId(target); ++ } ++ const old = this.unattachedDoc.createElement("span"); ++ if (m.oldValue) { ++ old.setAttribute("style", m.oldValue); ++ } ++ for (const pname of Array.from(target.style)) { ++ const newValue = target.style.getPropertyValue(pname); ++ const newPriority = target.style.getPropertyPriority(pname); ++ if ( ++ newValue !== old.style.getPropertyValue(pname) || ++ newPriority !== old.style.getPropertyPriority(pname) ++ ) { ++ if (newPriority === "") { ++ item.styleDiff[pname] = newValue; ++ } else { ++ item.styleDiff[pname] = [newValue, newPriority]; ++ } ++ } else { ++ item._unchangedStyles[pname] = [newValue, newPriority]; + } +- if (targetId && targetId !== -1) { +- this.movedMap[moveKey(this.mirror.getId(n), targetId)] = true; ++ } ++ for (const pname of Array.from(old.style)) { ++ if (target.style.getPropertyValue(pname) === "") { ++ item.styleDiff[pname] = false; + } ++ } + } +- else { +- this.addedSet.add(n); +- this.droppedSet.delete(n); ++ } ++ break; ++ } ++ case "childList": { ++ if (isBlocked(m.target, this.blockClass, this.blockSelector, true)) ++ return; ++ m.addedNodes.forEach((n) => this.genAdds(n, m.target)); ++ m.removedNodes.forEach((n) => { ++ const nodeId = this.mirror.getId(n); ++ const parentId = isShadowRoot(m.target) ++ ? this.mirror.getId(m.target.host) ++ : this.mirror.getId(m.target); ++ if ( ++ isBlocked(m.target, this.blockClass, this.blockSelector, false) || ++ isIgnored(n, this.mirror) || ++ !isSerialized(n, this.mirror) ++ ) { ++ return; + } +- if (!isBlocked(n, this.blockClass, this.blockSelector, false)) { +- n.childNodes.forEach((childN) => this.genAdds(childN)); +- if (hasShadowRoot(n)) { +- n.shadowRoot.childNodes.forEach((childN) => { +- this.processedNodeManager.add(childN, this); +- this.genAdds(childN, n); +- }); +- } ++ if (this.addedSet.has(n)) { ++ deepDelete(this.addedSet, n); ++ this.droppedSet.add(n); ++ } else if (this.addedSet.has(m.target) && nodeId === -1); ++ else if (isAncestorRemoved(m.target, this.mirror)); ++ else if ( ++ this.movedSet.has(n) && ++ this.movedMap[moveKey(nodeId, parentId)] ++ ) { ++ deepDelete(this.movedSet, n); ++ } else { ++ this.removes.push({ ++ parentId, ++ id: nodeId, ++ isShadow: ++ isShadowRoot(m.target) && isNativeShadowDom(m.target) ++ ? true ++ : undefined, ++ }); + } +- }; +- } +- init(options) { +- [ +- 'mutationCb', +- 'blockClass', +- 'blockSelector', +- 'maskTextClass', +- 'maskTextSelector', +- 'inlineStylesheet', +- 'maskInputOptions', +- 'maskTextFn', +- 'maskInputFn', +- 'keepIframeSrcFn', +- 'recordCanvas', +- 'inlineImages', +- 'slimDOMOptions', +- 'dataURLOptions', +- 'doc', +- 'mirror', +- 'iframeManager', +- 'stylesheetManager', +- 'shadowDomManager', +- 'canvasManager', +- 'processedNodeManager', +- ].forEach((key) => { +- this[key] = options[key]; +- }); +- } +- freeze() { +- this.frozen = true; +- this.canvasManager.freeze(); +- } +- unfreeze() { +- this.frozen = false; +- this.canvasManager.unfreeze(); +- this.emit(); +- } +- isFrozen() { +- return this.frozen; +- } +- lock() { +- this.locked = true; +- this.canvasManager.lock(); +- } +- unlock() { +- this.locked = false; +- this.canvasManager.unlock(); +- this.emit(); +- } +- reset() { +- this.shadowDomManager.reset(); +- this.canvasManager.reset(); +- } ++ this.mapRemoves.push(n); ++ }); ++ break; ++ } ++ } ++ }; ++ this.genAdds = (n, target) => { ++ if (this.processedNodeManager.inOtherBuffer(n, this)) return; ++ if (this.addedSet.has(n) || this.movedSet.has(n)) return; ++ if (this.mirror.hasNode(n)) { ++ if (isIgnored(n, this.mirror)) { ++ return; ++ } ++ this.movedSet.add(n); ++ let targetId = null; ++ if (target && this.mirror.hasNode(target)) { ++ targetId = this.mirror.getId(target); ++ } ++ if (targetId && targetId !== -1) { ++ this.movedMap[moveKey(this.mirror.getId(n), targetId)] = true; ++ } ++ } else { ++ this.addedSet.add(n); ++ this.droppedSet.delete(n); ++ } ++ if (!isBlocked(n, this.blockClass, this.blockSelector, false)) { ++ n.childNodes.forEach((childN) => this.genAdds(childN)); ++ if (hasShadowRoot(n)) { ++ n.shadowRoot.childNodes.forEach((childN) => { ++ this.processedNodeManager.add(childN, this); ++ this.genAdds(childN, n); ++ }); ++ } ++ } ++ }; ++ } ++ init(options) { ++ [ ++ "mutationCb", ++ "blockClass", ++ "blockSelector", ++ "maskTextClass", ++ "maskTextSelector", ++ "inlineStylesheet", ++ "maskInputOptions", ++ "maskTextFn", ++ "maskInputFn", ++ "keepIframeSrcFn", ++ "recordCanvas", ++ "inlineImages", ++ "slimDOMOptions", ++ "dataURLOptions", ++ "doc", ++ "mirror", ++ "iframeManager", ++ "stylesheetManager", ++ "shadowDomManager", ++ "canvasManager", ++ "processedNodeManager", ++ ].forEach((key) => { ++ this[key] = options[key]; ++ }); ++ } ++ freeze() { ++ this.frozen = true; ++ this.canvasManager.freeze(); ++ } ++ unfreeze() { ++ this.frozen = false; ++ this.canvasManager.unfreeze(); ++ this.emit(); ++ } ++ isFrozen() { ++ return this.frozen; ++ } ++ lock() { ++ this.locked = true; ++ this.canvasManager.lock(); ++ } ++ unlock() { ++ this.locked = false; ++ this.canvasManager.unlock(); ++ this.emit(); ++ } ++ reset() { ++ this.shadowDomManager.reset(); ++ this.canvasManager.reset(); ++ } + } + function deepDelete(addsSet, n) { +- addsSet.delete(n); +- n.childNodes.forEach((childN) => deepDelete(addsSet, childN)); ++ addsSet.delete(n); ++ n.childNodes.forEach((childN) => deepDelete(addsSet, childN)); + } + function isParentRemoved(removes, n, mirror) { +- if (removes.length === 0) +- return false; +- return _isParentRemoved(removes, n, mirror); ++ if (removes.length === 0) return false; ++ return _isParentRemoved(removes, n, mirror); + } + function _isParentRemoved(removes, n, mirror) { +- const { parentNode } = n; +- if (!parentNode) { +- return false; +- } +- const parentId = mirror.getId(parentNode); +- if (removes.some((r) => r.id === parentId)) { +- return true; +- } +- return _isParentRemoved(removes, parentNode, mirror); ++ const { parentNode } = n; ++ if (!parentNode) { ++ return false; ++ } ++ const parentId = mirror.getId(parentNode); ++ if (removes.some((r) => r.id === parentId)) { ++ return true; ++ } ++ return _isParentRemoved(removes, parentNode, mirror); + } + function isAncestorInSet(set, n) { +- if (set.size === 0) +- return false; +- return _isAncestorInSet(set, n); ++ if (set.size === 0) return false; ++ return _isAncestorInSet(set, n); + } + function _isAncestorInSet(set, n) { +- const { parentNode } = n; +- if (!parentNode) { +- return false; +- } +- if (set.has(parentNode)) { +- return true; +- } +- return _isAncestorInSet(set, parentNode); ++ const { parentNode } = n; ++ if (!parentNode) { ++ return false; ++ } ++ if (set.has(parentNode)) { ++ return true; ++ } ++ return _isAncestorInSet(set, parentNode); + } + + export { MutationBuffer as default }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b786c5e76..feb354a75 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,11 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +patchedDependencies: + rrweb@2.0.0-alpha.11: + hash: ehkxvj2dnc4d5fb4tq7odeskvu + path: patches/rrweb@2.0.0-alpha.11.patch + dependencies: fflate: specifier: ^0.4.8 @@ -159,7 +164,7 @@ devDependencies: version: 5.9.0(rollup@2.79.1) rrweb: specifier: 2.0.0-alpha.11 - version: 2.0.0-alpha.11 + version: 2.0.0-alpha.11(patch_hash=ehkxvj2dnc4d5fb4tq7odeskvu) rrweb-snapshot: specifier: 2.0.0-alpha.11 version: 2.0.0-alpha.11 @@ -9073,7 +9078,7 @@ packages: rrweb-snapshot: 1.1.14 dev: true - /rrweb@2.0.0-alpha.11: + /rrweb@2.0.0-alpha.11(patch_hash=ehkxvj2dnc4d5fb4tq7odeskvu): resolution: {integrity: sha512-vJ2gNvF+pUG9C2aaau7iSNqhWBSc4BwtUO4FpegOtDObuH4PIaxNJOlgHz82+WxKr9XPm93ER0LqmNpy0KYdKg==} dependencies: '@rrweb/types': 2.0.0-alpha.11 @@ -9085,6 +9090,7 @@ packages: rrdom: 2.0.0-alpha.11 rrweb-snapshot: 2.0.0-alpha.11 dev: true + patched: true /rsvp@4.8.5: resolution: {integrity: sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==}