UNPKG

@sentry-internal/rrweb

Version:
1,532 lines 221 kB
"use strict"; Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); const canvasManager = require("./canvas-manager-900X62Xw.cjs"); var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); var __defProp2 = Object.defineProperty; var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField2 = (obj, key, value) => __defNormalProp2(obj, typeof key !== "symbol" ? key + "" : key, value); var NodeType$1 = /* @__PURE__ */ ((NodeType2) => { NodeType2[NodeType2["Document"] = 0] = "Document"; NodeType2[NodeType2["DocumentType"] = 1] = "DocumentType"; NodeType2[NodeType2["Element"] = 2] = "Element"; NodeType2[NodeType2["Text"] = 3] = "Text"; NodeType2[NodeType2["CDATA"] = 4] = "CDATA"; NodeType2[NodeType2["Comment"] = 5] = "Comment"; return NodeType2; })(NodeType$1 || {}); let Mirror$1 = class Mirror { constructor() { __publicField2(this, "idNodeMap", /* @__PURE__ */ new Map()); __publicField2(this, "nodeMetaMap", /* @__PURE__ */ new WeakMap()); } getId(n2) { if (!n2) return -1; const id = this.getMeta(n2)?.id; return id ?? -1; } getNode(id) { return this.idNodeMap.get(id) || null; } getIds() { return Array.from(this.idNodeMap.keys()); } getMeta(n2) { return this.nodeMetaMap.get(n2) || null; } // removes the node from idNodeMap // doesn't remove the node from nodeMetaMap removeNodeFromMap(n2) { const id = this.getId(n2); this.idNodeMap.delete(id); if (n2.childNodes) { n2.childNodes.forEach( (childNode) => this.removeNodeFromMap(childNode) ); } } has(id) { return this.idNodeMap.has(id); } hasNode(node) { return this.nodeMetaMap.has(node); } add(n2, meta) { const id = meta.id; this.idNodeMap.set(id, n2); this.nodeMetaMap.set(n2, meta); } replace(id, n2) { const oldNode = this.getNode(id); if (oldNode) { const meta = this.nodeMetaMap.get(oldNode); if (meta) this.nodeMetaMap.set(n2, meta); } this.idNodeMap.set(id, n2); } reset() { this.idNodeMap = /* @__PURE__ */ new Map(); this.nodeMetaMap = /* @__PURE__ */ new WeakMap(); } }; function createMirror$1() { return new Mirror$1(); } function parseCSSText(cssText) { const res = {}; const listDelimiter = /;(?![^(]*\))/g; const propertyDelimiter = /:(.+)/; const comment = /\/\*.*?\*\//g; cssText.replace(comment, "").split(listDelimiter).forEach(function(item) { if (item) { const tmp = item.split(propertyDelimiter); tmp.length > 1 && (res[camelize(tmp[0].trim())] = tmp[1].trim()); } }); return res; } function toCSSText(style) { const properties = []; for (const name in style) { const value = style[name]; if (typeof value !== "string") continue; const normalizedName = hyphenate(name); properties.push(`${normalizedName}: ${value};`); } return properties.join(" "); } const camelizeRE = /-([a-z])/g; const CUSTOM_PROPERTY_REGEX = /^--[a-zA-Z0-9-]+$/; const camelize = (str) => { if (CUSTOM_PROPERTY_REGEX.test(str)) return str; return str.replace(camelizeRE, (_, c2) => c2 ? c2.toUpperCase() : ""); }; const hyphenateRE = /\B([A-Z])/g; const hyphenate = (str) => { return str.replace(hyphenateRE, "-$1").toLowerCase(); }; class BaseRRNode { // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any constructor(..._args) { __publicField(this, "parentElement", null); __publicField(this, "parentNode", null); __publicField(this, "ownerDocument"); __publicField(this, "firstChild", null); __publicField(this, "lastChild", null); __publicField(this, "previousSibling", null); __publicField(this, "nextSibling", null); __publicField(this, "ELEMENT_NODE", 1); __publicField(this, "TEXT_NODE", 3); __publicField(this, "nodeType"); __publicField(this, "nodeName"); __publicField(this, "RRNodeType"); } get childNodes() { const childNodes = []; let childIterator = this.firstChild; while (childIterator) { childNodes.push(childIterator); childIterator = childIterator.nextSibling; } return childNodes; } contains(node) { if (!(node instanceof BaseRRNode)) return false; else if (node.ownerDocument !== this.ownerDocument) return false; else if (node === this) return true; while (node.parentNode) { if (node.parentNode === this) return true; node = node.parentNode; } return false; } // eslint-disable-next-line @typescript-eslint/no-unused-vars appendChild(_newChild) { throw new Error( `RRDomException: Failed to execute 'appendChild' on 'RRNode': This RRNode type does not support this method.` ); } // eslint-disable-next-line @typescript-eslint/no-unused-vars insertBefore(_newChild, _refChild) { throw new Error( `RRDomException: Failed to execute 'insertBefore' on 'RRNode': This RRNode type does not support this method.` ); } // eslint-disable-next-line @typescript-eslint/no-unused-vars removeChild(_node) { throw new Error( `RRDomException: Failed to execute 'removeChild' on 'RRNode': This RRNode type does not support this method.` ); } toString() { return "RRNode"; } } class BaseRRDocument extends BaseRRNode { // eslint-disable-next-line @typescript-eslint/no-explicit-any constructor(...args) { super(args); __publicField(this, "nodeType", 9); __publicField(this, "nodeName", "#document"); __publicField(this, "compatMode", "CSS1Compat"); __publicField(this, "RRNodeType", NodeType$1.Document); __publicField(this, "textContent", null); this.ownerDocument = this; } get documentElement() { return this.childNodes.find( (node) => node.RRNodeType === NodeType$1.Element && node.tagName === "HTML" ) || null; } get body() { return this.documentElement?.childNodes.find( (node) => node.RRNodeType === NodeType$1.Element && node.tagName === "BODY" ) || null; } get head() { return this.documentElement?.childNodes.find( (node) => node.RRNodeType === NodeType$1.Element && node.tagName === "HEAD" ) || null; } get implementation() { return this; } get firstElementChild() { return this.documentElement; } appendChild(newChild) { const nodeType = newChild.RRNodeType; if (nodeType === NodeType$1.Element || nodeType === NodeType$1.DocumentType) { if (this.childNodes.some((s2) => s2.RRNodeType === nodeType)) { throw new Error( `RRDomException: Failed to execute 'appendChild' on 'RRNode': Only one ${nodeType === NodeType$1.Element ? "RRElement" : "RRDoctype"} on RRDocument allowed.` ); } } const child = appendChild(this, newChild); child.parentElement = null; return child; } insertBefore(newChild, refChild) { const nodeType = newChild.RRNodeType; if (nodeType === NodeType$1.Element || nodeType === NodeType$1.DocumentType) { if (this.childNodes.some((s2) => s2.RRNodeType === nodeType)) { throw new Error( `RRDomException: Failed to execute 'insertBefore' on 'RRNode': Only one ${nodeType === NodeType$1.Element ? "RRElement" : "RRDoctype"} on RRDocument allowed.` ); } } const child = insertBefore(this, newChild, refChild); child.parentElement = null; return child; } removeChild(node) { return removeChild(this, node); } open() { this.firstChild = null; this.lastChild = null; } close() { } /** * Adhoc implementation for setting xhtml namespace in rebuilt.ts (rrweb-snapshot). * There are two lines used this function: * 1. doc.write('\<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" ""\>') * 2. doc.write('\<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" ""\>') */ write(content) { let publicId; if (content === '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "">') publicId = "-//W3C//DTD XHTML 1.0 Transitional//EN"; else if (content === '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "">') publicId = "-//W3C//DTD HTML 4.0 Transitional//EN"; if (publicId) { const doctype = this.createDocumentType("html", publicId, ""); this.open(); this.appendChild(doctype); } } createDocument(_namespace, _qualifiedName, _doctype) { return new BaseRRDocument(); } createDocumentType(qualifiedName, publicId, systemId) { const doctype = new BaseRRDocumentType(qualifiedName, publicId, systemId); doctype.ownerDocument = this; return doctype; } createElement(tagName) { const element = new BaseRRElement(tagName); element.ownerDocument = this; return element; } createElementNS(_namespaceURI, qualifiedName) { return this.createElement(qualifiedName); } createTextNode(data) { const text = new BaseRRText(data); text.ownerDocument = this; return text; } createComment(data) { const comment = new BaseRRComment(data); comment.ownerDocument = this; return comment; } createCDATASection(data) { const CDATASection = new BaseRRCDATASection(data); CDATASection.ownerDocument = this; return CDATASection; } toString() { return "RRDocument"; } } class BaseRRDocumentType extends BaseRRNode { constructor(qualifiedName, publicId, systemId) { super(); __publicField(this, "nodeType", 10); __publicField(this, "RRNodeType", NodeType$1.DocumentType); __publicField(this, "name"); __publicField(this, "publicId"); __publicField(this, "systemId"); __publicField(this, "textContent", null); this.name = qualifiedName; this.publicId = publicId; this.systemId = systemId; this.nodeName = qualifiedName; } toString() { return "RRDocumentType"; } } class BaseRRElement extends BaseRRNode { constructor(tagName) { super(); __publicField(this, "nodeType", 1); __publicField(this, "RRNodeType", NodeType$1.Element); __publicField(this, "tagName"); __publicField(this, "attributes", {}); __publicField(this, "shadowRoot", null); __publicField(this, "scrollLeft"); __publicField(this, "scrollTop"); this.tagName = tagName.toUpperCase(); this.nodeName = tagName.toUpperCase(); } get textContent() { let result = ""; this.childNodes.forEach((node) => result += node.textContent); return result; } set textContent(textContent) { this.firstChild = null; this.lastChild = null; this.appendChild(this.ownerDocument.createTextNode(textContent)); } get classList() { return new ClassList( this.attributes.class, (newClassName) => { this.attributes.class = newClassName; } ); } get id() { return this.attributes.id || ""; } get className() { return this.attributes.class || ""; } get style() { const style = this.attributes.style ? parseCSSText(this.attributes.style) : {}; const hyphenateRE2 = /\B([A-Z])/g; style.setProperty = (name, value, priority) => { if (hyphenateRE2.test(name)) return; const normalizedName = camelize(name); if (!value) delete style[normalizedName]; else style[normalizedName] = value; if (priority === "important") style[normalizedName] += " !important"; this.attributes.style = toCSSText(style); }; style.removeProperty = (name) => { if (hyphenateRE2.test(name)) return ""; const normalizedName = camelize(name); const value = style[normalizedName] || ""; delete style[normalizedName]; this.attributes.style = toCSSText(style); return value; }; return style; } getAttribute(name) { return this.attributes[name] || null; } setAttribute(name, attribute) { this.attributes[name] = attribute; } setAttributeNS(_namespace, qualifiedName, value) { this.setAttribute(qualifiedName, value); } removeAttribute(name) { delete this.attributes[name]; } appendChild(newChild) { return appendChild(this, newChild); } insertBefore(newChild, refChild) { return insertBefore(this, newChild, refChild); } removeChild(node) { return removeChild(this, node); } // eslint-disable-next-line @typescript-eslint/no-unused-vars attachShadow(_init) { const shadowRoot = this.ownerDocument.createElement("SHADOWROOT"); this.shadowRoot = shadowRoot; return shadowRoot; } // eslint-disable-next-line @typescript-eslint/no-unused-vars dispatchEvent(_event) { return true; } toString() { let attributeString = ""; for (const attribute in this.attributes) { attributeString += `${attribute}="${this.attributes[attribute]}" `; } return `${this.tagName} ${attributeString}`; } } class BaseRRMediaElement extends BaseRRElement { constructor() { super(...arguments); __publicField(this, "currentTime"); __publicField(this, "volume"); __publicField(this, "paused"); __publicField(this, "muted"); __publicField(this, "playbackRate"); __publicField(this, "loop"); } // eslint-disable-next-line @typescript-eslint/no-unused-vars attachShadow(_init) { throw new Error( `RRDomException: Failed to execute 'attachShadow' on 'RRElement': This RRElement does not support attachShadow` ); } play() { this.paused = false; } pause() { this.paused = true; } } class BaseRRText extends BaseRRNode { constructor(data) { super(); __publicField(this, "nodeType", 3); __publicField(this, "nodeName", "#text"); __publicField(this, "RRNodeType", NodeType$1.Text); __publicField(this, "data"); this.data = data; } get textContent() { return this.data; } set textContent(textContent) { this.data = textContent; } toString() { return `RRText text=${JSON.stringify(this.data)}`; } } class BaseRRComment extends BaseRRNode { constructor(data) { super(); __publicField(this, "nodeType", 8); __publicField(this, "nodeName", "#comment"); __publicField(this, "RRNodeType", NodeType$1.Comment); __publicField(this, "data"); this.data = data; } get textContent() { return this.data; } set textContent(textContent) { this.data = textContent; } toString() { return `RRComment text=${JSON.stringify(this.data)}`; } } class BaseRRCDATASection extends BaseRRNode { constructor(data) { super(); __publicField(this, "nodeName", "#cdata-section"); __publicField(this, "nodeType", 4); __publicField(this, "RRNodeType", NodeType$1.CDATA); __publicField(this, "data"); this.data = data; } get textContent() { return this.data; } set textContent(textContent) { this.data = textContent; } toString() { return `RRCDATASection data=${JSON.stringify(this.data)}`; } } class ClassList { constructor(classText, onChange) { __publicField(this, "onChange"); __publicField(this, "classes", []); __publicField(this, "add", (...classNames) => { for (const item of classNames) { const className = String(item); if (this.classes.indexOf(className) >= 0) continue; this.classes.push(className); } this.onChange && this.onChange(this.classes.join(" ")); }); __publicField(this, "remove", (...classNames) => { this.classes = this.classes.filter( (item) => classNames.indexOf(item) === -1 ); this.onChange && this.onChange(this.classes.join(" ")); }); if (classText) { const classes = classText.trim().split(/\s+/); this.classes.push(...classes); } this.onChange = onChange; } } function appendChild(parent, newChild) { if (newChild.parentNode) newChild.parentNode.removeChild(newChild); if (parent.lastChild) { parent.lastChild.nextSibling = newChild; newChild.previousSibling = parent.lastChild; } else { parent.firstChild = newChild; newChild.previousSibling = null; } parent.lastChild = newChild; newChild.nextSibling = null; newChild.parentNode = parent; newChild.parentElement = parent; newChild.ownerDocument = parent.ownerDocument; return newChild; } function insertBefore(parent, newChild, refChild) { if (!refChild) return appendChild(parent, newChild); if (refChild.parentNode !== parent) throw new Error( "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; if (newChild.previousSibling) newChild.previousSibling.nextSibling = newChild; else parent.firstChild = newChild; newChild.parentElement = parent; newChild.parentNode = parent; newChild.ownerDocument = parent.ownerDocument; return newChild; } function removeChild(parent, child) { if (child.parentNode !== parent) throw new Error( "Failed to execute 'removeChild' on 'RRNode': The RRNode to be removed is not a child of this RRNode." ); if (child.previousSibling) child.previousSibling.nextSibling = child.nextSibling; else parent.firstChild = child.nextSibling; if (child.nextSibling) child.nextSibling.previousSibling = child.previousSibling; else parent.lastChild = child.previousSibling; child.previousSibling = null; child.nextSibling = null; child.parentElement = null; child.parentNode = null; return child; } var NodeType = /* @__PURE__ */ ((NodeType2) => { NodeType2[NodeType2["PLACEHOLDER"] = 0] = "PLACEHOLDER"; NodeType2[NodeType2["ELEMENT_NODE"] = 1] = "ELEMENT_NODE"; NodeType2[NodeType2["ATTRIBUTE_NODE"] = 2] = "ATTRIBUTE_NODE"; NodeType2[NodeType2["TEXT_NODE"] = 3] = "TEXT_NODE"; NodeType2[NodeType2["CDATA_SECTION_NODE"] = 4] = "CDATA_SECTION_NODE"; NodeType2[NodeType2["ENTITY_REFERENCE_NODE"] = 5] = "ENTITY_REFERENCE_NODE"; NodeType2[NodeType2["ENTITY_NODE"] = 6] = "ENTITY_NODE"; NodeType2[NodeType2["PROCESSING_INSTRUCTION_NODE"] = 7] = "PROCESSING_INSTRUCTION_NODE"; NodeType2[NodeType2["COMMENT_NODE"] = 8] = "COMMENT_NODE"; NodeType2[NodeType2["DOCUMENT_NODE"] = 9] = "DOCUMENT_NODE"; NodeType2[NodeType2["DOCUMENT_TYPE_NODE"] = 10] = "DOCUMENT_TYPE_NODE"; NodeType2[NodeType2["DOCUMENT_FRAGMENT_NODE"] = 11] = "DOCUMENT_FRAGMENT_NODE"; return NodeType2; })(NodeType || {}); function getIFrameContentDocument(iframe) { try { return iframe.contentDocument; } catch (e2) { } } function getIFrameContentWindow(iframe) { try { return iframe.contentWindow; } catch (e2) { } } const NAMESPACES = { svg: "http://www.w3.org/2000/svg", "xlink:href": "http://www.w3.org/1999/xlink", xmlns: "http://www.w3.org/2000/xmlns/" }; const SVGTagMap = { altglyph: "altGlyph", altglyphdef: "altGlyphDef", altglyphitem: "altGlyphItem", animatecolor: "animateColor", animatemotion: "animateMotion", animatetransform: "animateTransform", clippath: "clipPath", feblend: "feBlend", fecolormatrix: "feColorMatrix", fecomponenttransfer: "feComponentTransfer", fecomposite: "feComposite", feconvolvematrix: "feConvolveMatrix", fediffuselighting: "feDiffuseLighting", fedisplacementmap: "feDisplacementMap", fedistantlight: "feDistantLight", fedropshadow: "feDropShadow", feflood: "feFlood", fefunca: "feFuncA", fefuncb: "feFuncB", fefuncg: "feFuncG", fefuncr: "feFuncR", fegaussianblur: "feGaussianBlur", feimage: "feImage", femerge: "feMerge", femergenode: "feMergeNode", femorphology: "feMorphology", feoffset: "feOffset", fepointlight: "fePointLight", fespecularlighting: "feSpecularLighting", fespotlight: "feSpotLight", fetile: "feTile", feturbulence: "feTurbulence", foreignobject: "foreignObject", glyphref: "glyphRef", lineargradient: "linearGradient", radialgradient: "radialGradient" }; let createdNodeSet = null; function diff(oldTree, newTree, replayer, rrnodeMirror = newTree.mirror || newTree.ownerDocument.mirror) { oldTree = diffBeforeUpdatingChildren( oldTree, newTree, replayer, rrnodeMirror ); diffChildren(oldTree, newTree, replayer, rrnodeMirror); diffAfterUpdatingChildren(oldTree, newTree, replayer); } function diffBeforeUpdatingChildren(oldTree, newTree, replayer, rrnodeMirror) { if (replayer.afterAppend && !createdNodeSet) { createdNodeSet = /* @__PURE__ */ new WeakSet(); setTimeout(() => { createdNodeSet = null; }, 0); } if (!sameNodeType(oldTree, newTree)) { const calibratedOldTree = createOrGetNode( newTree, replayer.mirror, rrnodeMirror ); oldTree.parentNode?.replaceChild(calibratedOldTree, oldTree); oldTree = calibratedOldTree; } switch (newTree.RRNodeType) { case NodeType$1.Document: { if (!nodeMatching(oldTree, newTree, replayer.mirror, rrnodeMirror)) { const newMeta = rrnodeMirror.getMeta(newTree); if (newMeta) { replayer.mirror.removeNodeFromMap(oldTree); oldTree.close(); oldTree.open(); replayer.mirror.add(oldTree, newMeta); createdNodeSet?.add(oldTree); } } break; } case NodeType$1.Element: { const oldElement = oldTree; const newRRElement = newTree; switch (newRRElement.tagName) { case "IFRAME": { const oldContentDocument = getIFrameContentDocument( oldTree ); if (!oldContentDocument) break; diff( oldContentDocument, newTree.contentDocument, replayer, rrnodeMirror ); break; } } if (newRRElement.shadowRoot) { if (!oldElement.shadowRoot) oldElement.attachShadow({ mode: "open" }); diffChildren( // eslint-disable-next-line @typescript-eslint/no-non-null-assertion oldElement.shadowRoot, newRRElement.shadowRoot, replayer, rrnodeMirror ); } diffProps(oldElement, newRRElement, rrnodeMirror); break; } } return oldTree; } function diffAfterUpdatingChildren(oldTree, newTree, replayer) { switch (newTree.RRNodeType) { case NodeType$1.Document: { const scrollData = newTree.scrollData; scrollData && replayer.applyScroll(scrollData, true); break; } case NodeType$1.Element: { const oldElement = oldTree; const newRRElement = newTree; newRRElement.scrollData && replayer.applyScroll(newRRElement.scrollData, true); newRRElement.inputData && replayer.applyInput(newRRElement.inputData); switch (newRRElement.tagName) { case "AUDIO": case "VIDEO": { const oldMediaElement = oldTree; const newMediaRRElement = newRRElement; if (newMediaRRElement.paused !== void 0) { const maybePromise = newMediaRRElement.paused ? oldMediaElement.pause() : oldMediaElement.play(); if (typeof maybePromise?.catch === "function") { maybePromise.catch((e2) => { console.warn(e2); }); } } if (newMediaRRElement.muted !== void 0) oldMediaElement.muted = newMediaRRElement.muted; if (newMediaRRElement.volume !== void 0) oldMediaElement.volume = newMediaRRElement.volume; if (newMediaRRElement.currentTime !== void 0) oldMediaElement.currentTime = newMediaRRElement.currentTime; if (newMediaRRElement.playbackRate !== void 0) oldMediaElement.playbackRate = newMediaRRElement.playbackRate; break; } case "CANVAS": { const rrCanvasElement = newTree; if (rrCanvasElement.rr_dataURL !== null) { const image = document.createElement("img"); image.onload = () => { const ctx = oldElement.getContext("2d"); if (ctx) { ctx.drawImage(image, 0, 0, image.width, image.height); } }; image.src = rrCanvasElement.rr_dataURL; } rrCanvasElement.canvasMutations.forEach( (canvasMutation2) => replayer.applyCanvas( canvasMutation2.event, canvasMutation2.mutation, oldTree ) ); break; } // Props of style elements have to be updated after all children are updated. Otherwise the props can be overwritten by textContent. case "STYLE": { const styleSheet = oldElement.sheet; styleSheet && newTree.rules.forEach( (data) => replayer.applyStyleSheetMutation(data, styleSheet) ); break; } } break; } case NodeType$1.Text: case NodeType$1.Comment: case NodeType$1.CDATA: { if (oldTree.textContent !== newTree.data) oldTree.textContent = newTree.data; break; } } if (createdNodeSet?.has(oldTree)) { createdNodeSet.delete(oldTree); replayer.afterAppend?.(oldTree, replayer.mirror.getId(oldTree)); } } function diffProps(oldTree, newTree, rrnodeMirror) { const oldAttributes = oldTree.attributes; const newAttributes = newTree.attributes; for (const name in newAttributes) { const newValue = newAttributes[name]; const sn = rrnodeMirror.getMeta(newTree); if (sn?.isSVG && NAMESPACES[name]) oldTree.setAttributeNS(NAMESPACES[name], name, newValue); else if (newTree.tagName === "CANVAS" && name === "rr_dataURL") { const image = document.createElement("img"); image.src = newValue; image.onload = () => { const ctx = oldTree.getContext("2d"); if (ctx) { ctx.drawImage(image, 0, 0, image.width, image.height); } }; } else if (newTree.tagName === "IFRAME" && name === "srcdoc") continue; else { try { oldTree.setAttribute(name, newValue); } catch (err) { console.warn(err); } } } for (const { name } of Array.from(oldAttributes)) if (!(name in newAttributes)) oldTree.removeAttribute(name); newTree.scrollLeft && (oldTree.scrollLeft = newTree.scrollLeft); newTree.scrollTop && (oldTree.scrollTop = newTree.scrollTop); } function diffChildren(oldTree, newTree, replayer, rrnodeMirror) { const oldChildren = Array.from(oldTree.childNodes); const newChildren = newTree.childNodes; if (oldChildren.length === 0 && newChildren.length === 0) return; let oldStartIndex = 0, oldEndIndex = oldChildren.length - 1, newStartIndex = 0, newEndIndex = newChildren.length - 1; let oldStartNode = oldChildren[oldStartIndex], oldEndNode = oldChildren[oldEndIndex], newStartNode = newChildren[newStartIndex], newEndNode = newChildren[newEndIndex]; let oldIdToIndex = void 0, indexInOld = void 0; while (oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex) { if (oldStartNode === void 0) { oldStartNode = oldChildren[++oldStartIndex]; } else if (oldEndNode === void 0) { oldEndNode = oldChildren[--oldEndIndex]; } else if ( // same first node? nodeMatching(oldStartNode, newStartNode, replayer.mirror, rrnodeMirror) ) { oldStartNode = oldChildren[++oldStartIndex]; newStartNode = newChildren[++newStartIndex]; } else if ( // same last node? nodeMatching(oldEndNode, newEndNode, replayer.mirror, rrnodeMirror) ) { oldEndNode = oldChildren[--oldEndIndex]; newEndNode = newChildren[--newEndIndex]; } else if ( // is the first old node the same as the last new node? nodeMatching(oldStartNode, newEndNode, replayer.mirror, rrnodeMirror) ) { try { handleInsertBefore(oldTree, oldStartNode, oldEndNode.nextSibling); } catch (e2) { console.warn(e2); } oldStartNode = oldChildren[++oldStartIndex]; newEndNode = newChildren[--newEndIndex]; } else if ( // is the last old node the same as the first new node? nodeMatching(oldEndNode, newStartNode, replayer.mirror, rrnodeMirror) ) { try { handleInsertBefore(oldTree, oldEndNode, oldStartNode); } catch (e2) { console.warn(e2); } oldEndNode = oldChildren[--oldEndIndex]; newStartNode = newChildren[++newStartIndex]; } else { if (!oldIdToIndex) { oldIdToIndex = {}; for (let i2 = oldStartIndex; i2 <= oldEndIndex; i2++) { const oldChild2 = oldChildren[i2]; if (oldChild2 && replayer.mirror.hasNode(oldChild2)) oldIdToIndex[replayer.mirror.getId(oldChild2)] = i2; } } indexInOld = oldIdToIndex[rrnodeMirror.getId(newStartNode)]; const nodeToMove = oldChildren[indexInOld]; if (indexInOld !== void 0 && nodeToMove && nodeMatching(nodeToMove, newStartNode, replayer.mirror, rrnodeMirror)) { try { handleInsertBefore(oldTree, nodeToMove, oldStartNode); } catch (e2) { console.warn(e2); } oldChildren[indexInOld] = void 0; } else { const newNode = createOrGetNode( newStartNode, replayer.mirror, rrnodeMirror ); if (oldTree.nodeName === "#document" && oldStartNode && /** * Special case 1: one document isn't allowed to have two doctype nodes at the same time, so we need to remove the old one first before inserting the new one. * How this case happens: A parent document in the old tree already has a doctype node with an id e.g. #1. A new full snapshot rebuilds the replayer with a new doctype node with another id #2. According to the algorithm, the new doctype node will be inserted before the old one, which is not allowed by the Document standard. */ (newNode.nodeType === newNode.DOCUMENT_TYPE_NODE && oldStartNode.nodeType === oldStartNode.DOCUMENT_TYPE_NODE || /** * Special case 2: one document isn't allowed to have two HTMLElements at the same time, so we need to remove the old one first before inserting the new one. * How this case happens: A mounted iframe element has an automatically created HTML element. We should delete it before inserting a serialized one. Otherwise, an error 'Only one element on document allowed' will be thrown. */ newNode.nodeType === newNode.ELEMENT_NODE && oldStartNode.nodeType === oldStartNode.ELEMENT_NODE)) { oldTree.removeChild(oldStartNode); replayer.mirror.removeNodeFromMap(oldStartNode); oldStartNode = oldChildren[++oldStartIndex]; } try { handleInsertBefore(oldTree, newNode, oldStartNode || null); } catch (e2) { console.warn(e2); } } newStartNode = newChildren[++newStartIndex]; } } if (oldStartIndex > oldEndIndex) { const referenceRRNode = newChildren[newEndIndex + 1]; let referenceNode = null; if (referenceRRNode) referenceNode = replayer.mirror.getNode( rrnodeMirror.getId(referenceRRNode) ); for (; newStartIndex <= newEndIndex; ++newStartIndex) { const newNode = createOrGetNode( newChildren[newStartIndex], replayer.mirror, rrnodeMirror ); try { handleInsertBefore(oldTree, newNode, referenceNode); } catch (e2) { console.warn(e2); } } } else if (newStartIndex > newEndIndex) { for (; oldStartIndex <= oldEndIndex; oldStartIndex++) { const node = oldChildren[oldStartIndex]; if (!node || node.parentNode !== oldTree) continue; try { oldTree.removeChild(node); replayer.mirror.removeNodeFromMap(node); } catch (e2) { console.warn(e2); } } } let oldChild = oldTree.firstChild; let newChild = newTree.firstChild; while (oldChild !== null && newChild !== null) { diff(oldChild, newChild, replayer, rrnodeMirror); oldChild = oldChild.nextSibling; newChild = newChild.nextSibling; } } function createOrGetNode(rrNode, domMirror, rrnodeMirror) { const nodeId = rrnodeMirror.getId(rrNode); const sn = rrnodeMirror.getMeta(rrNode); let node = null; if (nodeId > -1) node = domMirror.getNode(nodeId); if (node !== null && sameNodeType(node, rrNode)) return node; switch (rrNode.RRNodeType) { case NodeType$1.Document: node = new Document(); break; case NodeType$1.DocumentType: node = document.implementation.createDocumentType( rrNode.name, rrNode.publicId, rrNode.systemId ); break; case NodeType$1.Element: { let tagName = rrNode.tagName.toLowerCase(); tagName = SVGTagMap[tagName] || tagName; if (sn && "isSVG" in sn && sn?.isSVG) { node = document.createElementNS(NAMESPACES["svg"], tagName); } else node = document.createElement(rrNode.tagName); break; } case NodeType$1.Text: node = document.createTextNode(rrNode.data); break; case NodeType$1.Comment: node = document.createComment(rrNode.data); break; case NodeType$1.CDATA: node = document.createCDATASection(rrNode.data); break; } if (sn) domMirror.add(node, { ...sn }); try { createdNodeSet?.add(node); } catch (e2) { } return node; } function sameNodeType(node1, node2) { if (node1.nodeType !== node2.nodeType) return false; return node1.nodeType !== node1.ELEMENT_NODE || node1.tagName.toUpperCase() === node2.tagName; } function nodeMatching(node1, node2, domMirror, rrdomMirror) { const node1Id = domMirror.getId(node1); const node2Id = rrdomMirror.getId(node2); if (node1Id === -1 || node1Id !== node2Id) return false; return sameNodeType(node1, node2); } function getInsertedStylesFromElement(styleElement) { const elementCssRules = styleElement.sheet?.cssRules; if (!elementCssRules || !elementCssRules.length) return; const tempStyleSheet = new CSSStyleSheet(); tempStyleSheet.replaceSync(styleElement.innerText); const innerTextStylesMap = {}; for (let i2 = 0; i2 < tempStyleSheet.cssRules.length; i2++) { innerTextStylesMap[tempStyleSheet.cssRules[i2].cssText] = tempStyleSheet.cssRules[i2]; } const insertedStylesStyleSheet = []; for (let i2 = 0; i2 < elementCssRules?.length; i2++) { const cssRuleText = elementCssRules[i2].cssText; if (!innerTextStylesMap[cssRuleText]) { insertedStylesStyleSheet.push({ index: i2, cssRuleText }); } } return insertedStylesStyleSheet; } function handleInsertBefore(oldTree, nodeToMove, insertBeforeNode) { let insertedStyles; if (nodeToMove.nodeName === "STYLE") { insertedStyles = getInsertedStylesFromElement( nodeToMove ); } oldTree.insertBefore(nodeToMove, insertBeforeNode); if (insertedStyles && insertedStyles.length) { insertedStyles.forEach(({ cssRuleText, index }) => { nodeToMove.sheet?.insertRule(cssRuleText, index); }); } } class RRDocument extends BaseRRDocument { constructor(mirror2) { super(); __publicField(this, "UNSERIALIZED_STARTING_ID", -2); __publicField(this, "_unserializedId", this.UNSERIALIZED_STARTING_ID); __publicField(this, "mirror", createMirror()); __publicField(this, "scrollData", null); if (mirror2) { this.mirror = mirror2; } } /** * Every time the id is used, it will minus 1 automatically to avoid collisions. */ get unserializedId() { return this._unserializedId--; } createDocument(_namespace, _qualifiedName, _doctype) { return new RRDocument(); } createDocumentType(qualifiedName, publicId, systemId) { const documentTypeNode = new RRDocumentType( qualifiedName, publicId, systemId ); documentTypeNode.ownerDocument = this; return documentTypeNode; } createElement(tagName) { const upperTagName = tagName.toUpperCase(); let element; switch (upperTagName) { case "AUDIO": case "VIDEO": element = new RRMediaElement(upperTagName); break; case "IFRAME": element = new RRIFrameElement(upperTagName, this.mirror); break; case "CANVAS": element = new RRCanvasElement(upperTagName); break; case "STYLE": element = new RRStyleElement(upperTagName); break; default: element = new RRElement(upperTagName); break; } element.ownerDocument = this; return element; } createComment(data) { const commentNode = new RRComment(data); commentNode.ownerDocument = this; return commentNode; } createCDATASection(data) { const sectionNode = new RRCDATASection(data); sectionNode.ownerDocument = this; return sectionNode; } createTextNode(data) { const textNode = new RRText(data); textNode.ownerDocument = this; return textNode; } destroyTree() { this.firstChild = null; this.lastChild = null; this.mirror.reset(); } open() { super.open(); this._unserializedId = this.UNSERIALIZED_STARTING_ID; } } const RRDocumentType = BaseRRDocumentType; class RRElement extends BaseRRElement { constructor() { super(...arguments); __publicField(this, "inputData", null); __publicField(this, "scrollData", null); } } class RRMediaElement extends BaseRRMediaElement { } class RRCanvasElement extends RRElement { constructor() { super(...arguments); __publicField(this, "rr_dataURL", null); __publicField(this, "canvasMutations", []); } /** * This is a dummy implementation to distinguish RRCanvasElement from real HTMLCanvasElement. */ getContext() { return null; } } class RRStyleElement extends RRElement { constructor() { super(...arguments); __publicField(this, "rules", []); } } class RRIFrameElement extends RRElement { constructor(upperTagName, mirror2) { super(upperTagName); __publicField(this, "contentDocument", new RRDocument()); this.contentDocument.mirror = mirror2; } } const RRText = BaseRRText; const RRComment = BaseRRComment; const RRCDATASection = BaseRRCDATASection; function getValidTagName(element) { if (element instanceof HTMLFormElement) { return "FORM"; } return element.tagName.toUpperCase(); } function buildFromNode(node, rrdom, domMirror, parentRRNode) { let rrNode; switch (node.nodeType) { case NodeType.DOCUMENT_NODE: if (parentRRNode && parentRRNode.nodeName === "IFRAME") rrNode = parentRRNode.contentDocument; else { rrNode = rrdom; rrNode.compatMode = node.compatMode; } break; case NodeType.DOCUMENT_TYPE_NODE: { const documentType = node; rrNode = rrdom.createDocumentType( documentType.name, documentType.publicId, documentType.systemId ); break; } case NodeType.ELEMENT_NODE: { const elementNode = node; const tagName = getValidTagName(elementNode); rrNode = rrdom.createElement(tagName); const rrElement = rrNode; for (const { name, value } of Array.from(elementNode.attributes)) { rrElement.attributes[name] = value; } elementNode.scrollLeft && (rrElement.scrollLeft = elementNode.scrollLeft); elementNode.scrollTop && (rrElement.scrollTop = elementNode.scrollTop); break; } case NodeType.TEXT_NODE: rrNode = rrdom.createTextNode(node.textContent || ""); break; case NodeType.CDATA_SECTION_NODE: rrNode = rrdom.createCDATASection(node.data); break; case NodeType.COMMENT_NODE: rrNode = rrdom.createComment(node.textContent || ""); break; // if node is a shadow root case NodeType.DOCUMENT_FRAGMENT_NODE: rrNode = parentRRNode.attachShadow({ mode: "open" }); break; default: return null; } let sn = domMirror.getMeta(node); if (rrdom instanceof RRDocument) { if (!sn) { sn = getDefaultSN(rrNode, rrdom.unserializedId); domMirror.add(node, sn); } rrdom.mirror.add(rrNode, { ...sn }); } return rrNode; } function buildFromDom(dom, domMirror = createMirror$1(), rrdom = new RRDocument()) { function walk2(node, parentRRNode) { const rrNode = buildFromNode(node, rrdom, domMirror, parentRRNode); if (rrNode === null) return; if ( // if the parentRRNode isn't a RRIFrameElement parentRRNode?.nodeName !== "IFRAME" && // if node isn't a shadow root node.nodeType !== NodeType.DOCUMENT_FRAGMENT_NODE ) { parentRRNode?.appendChild(rrNode); rrNode.parentNode = parentRRNode; rrNode.parentElement = parentRRNode; } if (node.nodeName === "IFRAME") { const iframeDoc = getIFrameContentDocument(node); iframeDoc && walk2(iframeDoc, rrNode); } else if (node.nodeType === NodeType.DOCUMENT_NODE || node.nodeType === NodeType.ELEMENT_NODE || node.nodeType === NodeType.DOCUMENT_FRAGMENT_NODE) { if (node.nodeType === NodeType.ELEMENT_NODE && node.shadowRoot) walk2(node.shadowRoot, rrNode); node.childNodes.forEach((childNode) => walk2(childNode, rrNode)); } } walk2(dom, null); return rrdom; } function createMirror() { return new Mirror2(); } class Mirror2 { constructor() { __publicField(this, "idNodeMap", /* @__PURE__ */ new Map()); __publicField(this, "nodeMetaMap", /* @__PURE__ */ new WeakMap()); } getId(n2) { if (!n2) return -1; const id = this.getMeta(n2)?.id; return id ?? -1; } getNode(id) { return this.idNodeMap.get(id) || null; } getIds() { return Array.from(this.idNodeMap.keys()); } getMeta(n2) { return this.nodeMetaMap.get(n2) || null; } // removes the node from idNodeMap // doesn't remove the node from nodeMetaMap removeNodeFromMap(n2) { const id = this.getId(n2); this.idNodeMap.delete(id); if (n2.childNodes) { n2.childNodes.forEach((childNode) => this.removeNodeFromMap(childNode)); } } has(id) { return this.idNodeMap.has(id); } hasNode(node) { return this.nodeMetaMap.has(node); } add(n2, meta) { const id = meta.id; this.idNodeMap.set(id, n2); this.nodeMetaMap.set(n2, meta); } replace(id, n2) { const oldNode = this.getNode(id); if (oldNode) { const meta = this.nodeMetaMap.get(oldNode); if (meta) this.nodeMetaMap.set(n2, meta); } this.idNodeMap.set(id, n2); } reset() { this.idNodeMap = /* @__PURE__ */ new Map(); this.nodeMetaMap = /* @__PURE__ */ new WeakMap(); } } function getDefaultSN(node, id) { switch (node.RRNodeType) { case NodeType$1.Document: return { id, type: node.RRNodeType, childNodes: [] }; case NodeType$1.DocumentType: { const doctype = node; return { id, type: node.RRNodeType, name: doctype.name, publicId: doctype.publicId, systemId: doctype.systemId }; } case NodeType$1.Element: return { id, type: node.RRNodeType, tagName: node.tagName.toLowerCase(), // In rrweb data, all tagNames are lowercase. attributes: {}, childNodes: [] }; case NodeType$1.Text: return { id, type: node.RRNodeType, textContent: node.textContent || "" }; case NodeType$1.Comment: return { id, type: node.RRNodeType, textContent: node.textContent || "" }; case NodeType$1.CDATA: return { id, type: node.RRNodeType, textContent: "" }; } } class StyleDeclarationParser { constructor(doc) { this.doc = doc; this.unattachedDoc = null; } parse(styleText) { return this.parseWithConstructableStylesheet(styleText) || this.parseWithDetachedElement(styleText); } parseWithConstructableStylesheet(styleText) { if (typeof CSSStyleSheet === "undefined" || typeof CSSStyleSheet.prototype.replaceSync !== "function") { return null; } try { const sheet = new CSSStyleSheet(); sheet.replaceSync(`x { ${styleText} }`); const rule = sheet.cssRules[0]; if (!rule || rule.type !== CSSRule.STYLE_RULE) { return null; } return rule.style; } catch { return null; } } parseWithDetachedElement(styleText) { const old = this.getUnattachedDoc().createElement("span"); old.setAttribute("style", styleText); return old.style; } getUnattachedDoc() { if (!this.unattachedDoc) { try { this.unattachedDoc = document.implementation.createHTMLDocument(); } catch { this.unattachedDoc = this.doc; } } return this.unattachedDoc; } } function isNodeInLinkedList(n2) { return "__ln" in n2; } class DoubleLinkedList { 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?.next || null; } return current; } addNode(n2) { const node = { value: n2, previous: null, next: null }; n2.__ln = node; if (n2.previousSibling && isNodeInLinkedList(n2.previousSibling)) { const current = n2.previousSibling.__ln.next; node.next = current; node.previous = n2.previousSibling.__ln; n2.previousSibling.__ln.next = node; if (current) { current.previous = node; } } else if (n2.nextSibling && isNodeInLinkedList(n2.nextSibling) && n2.nextSibling.__ln.previous) { const current = n2.nextSibling.__ln.previous; node.previous = current; node.next = n2.nextSibling.__ln; n2.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(n2) { const current = n2.__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; } } if (n2.__ln) { delete n2.__ln; } this.length--; } } const moveKey = (id, parentId) => `${id}@${parentId}`; class MutationBuffer { constructor() { this.frozen = false; this.locked = false; this.texts = []; this.attributes = []; this.attributeMap = /* @__PURE__ */ new WeakMap(); this.removes = []; this.mapRemoves = []; this.movedMap = {}; this.addedSet = /* @__PURE__ */ new Set(); this.movedSet = /* @__PURE__ */ new Set(); this.droppedSet = /* @__PURE__ */ new Set(); this.processMutations = (mutations) => { mutations.forEach(this.processMutation); this.emit(); }; this.emit = () => { if (this.frozen || this.locked) { return; } const adds = []; const addedIds = /* @__PURE__ */ new Set(); const addList = new DoubleLinkedList(); const getNextId = (n2) => { let ns = n2; let nextId = canvasManager.IGNORED_NODE; while (nextId === canvasManager.IGNORED_NODE) { ns = ns && ns.nextSibling; nextId = ns && this.mirror.getId(ns); } return nextId; }; const pushAdd = (n2) => { if (!n2.parentNode || !canvasManager.inDom(n2)) { return; } const parentId = canvasManager.isShadowRoot(n2.parentNode) ? this.mirror.getId(canvasManager.getShadowHost(n2)) : this.mirror.getId(n2.parentNode); const nextId = getNextId(n2); if (parentId === -1 || nextId === -1) { return addList.addNode(n2); } const sn = canvasManager.serializeNodeWithId(n2, { doc: this.doc, mirror: this.mirror, blockClass: this.blockClass, blockSelector: this.blockSelector, maskAllText: this.maskAllText, unblockSelector: this.unblockSelector, maskTextClass: this.maskTextClass, unmaskTextClass: this.unmaskTextClass, maskTextSelector: this.maskTextSelector, unmaskTextSelector: this.unmaskTextSelector, skipChild: true, newlyAddedElement: true, inlineStylesheet: this.inlineStylesheet, maskInputOptions: this.maskInputOptions, maskAttributeFn: this.maskAttributeFn, maskTextFn: this.maskTextFn, maskInputFn: this.maskInputFn, slimDOMOptions: this.slimDOMOptions, dataURLOptions: this.dataURLOptions, recordCanvas: this.recordCanvas, inlineImages: this.inlineImages, onSerialize: (currentN) => { if (canvasManager.isSerializedIframe(currentN, this.mirror) && !canvasManager.isBlocked( currentN, this.blockClass, this.blockSelector, this.unblockSelector, false )) { this.iframe