UNPKG

@sentry-internal/replay

Version:
1,669 lines (1,666 loc) 264 kB
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); const core = require('@sentry/core'); const browserUtils = require('@sentry-internal/browser-utils'); const WINDOW = core.GLOBAL_OBJ; const REPLAY_SESSION_KEY = "sentryReplaySession"; const REPLAY_EVENT_NAME = "replay_event"; const UNABLE_TO_SEND_REPLAY = "Unable to send Replay"; const SESSION_IDLE_PAUSE_DURATION = 3e5; const SESSION_IDLE_EXPIRE_DURATION = 9e5; const DEFAULT_FLUSH_MIN_DELAY = 5e3; const DEFAULT_FLUSH_MAX_DELAY = 5500; const BUFFER_CHECKOUT_TIME = 6e4; const RETRY_BASE_INTERVAL = 5e3; const RETRY_MAX_COUNT = 3; const NETWORK_BODY_MAX_SIZE = 15e4; const CONSOLE_ARG_MAX_SIZE = 5e3; const SLOW_CLICK_THRESHOLD = 3e3; const SLOW_CLICK_SCROLL_TIMEOUT = 300; const REPLAY_MAX_EVENT_BUFFER_SIZE = 2e7; const MIN_REPLAY_DURATION = 4999; const MIN_REPLAY_DURATION_LIMIT = 5e4; const MAX_REPLAY_DURATION = 36e5; 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 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 || {}); function isElement$1(n) { return n.nodeType === n.ELEMENT_NODE; } function isShadowRoot(n) { const host = n?.host; return Boolean(host?.shadowRoot === n); } function isNativeShadowDom(shadowRoot) { return Object.prototype.toString.call(shadowRoot) === "[object ShadowRoot]"; } function fixBrowserCompatibilityIssuesInCSS(cssText) { if (cssText.includes(" background-clip: text;") && !cssText.includes(" -webkit-background-clip: text;")) { cssText = cssText.replace( /\sbackground-clip:\s*text;/g, " -webkit-background-clip: text; background-clip: text;" ); } return cssText; } function escapeImportStatement(rule) { const { cssText } = rule; if (cssText.split('"').length < 3) return cssText; const statement = ["@import", `url(${JSON.stringify(rule.href)})`]; if (rule.layerName === "") { statement.push(`layer`); } else if (rule.layerName) { statement.push(`layer(${rule.layerName})`); } if (rule.supportsText) { statement.push(`supports(${rule.supportsText})`); } if (rule.media.length) { statement.push(rule.media.mediaText); } return statement.join(" ") + ";"; } function stringifyStylesheet(s) { try { const rules = s.rules || s.cssRules; return rules ? fixBrowserCompatibilityIssuesInCSS( Array.from(rules, stringifyRule).join("") ) : null; } catch (error) { return null; } } function fixAllCssProperty(rule) { let styles = ""; for (let i = 0; i < rule.style.length; i++) { const styleDeclaration = rule.style; const attribute = styleDeclaration[i]; const isImportant = styleDeclaration.getPropertyPriority(attribute); styles += `${attribute}:${styleDeclaration.getPropertyValue(attribute)}${isImportant ? ` !important` : ""};`; } return `${rule.selectorText} { ${styles} }`; } function stringifyRule(rule) { let importStringified; if (isCSSImportRule(rule)) { try { importStringified = // for same-origin stylesheets, // we can access the imported stylesheet rules directly stringifyStylesheet(rule.styleSheet) || // work around browser issues with the raw string `@import url(...)` statement escapeImportStatement(rule); } catch (error) { } } else if (isCSSStyleRule(rule)) { let cssText = rule.cssText; const needsSafariColonFix = rule.selectorText.includes(":"); const needsAllFix = typeof rule.style["all"] === "string" && rule.style["all"]; if (needsAllFix) { cssText = fixAllCssProperty(rule); } if (needsSafariColonFix) { cssText = fixSafariColons(cssText); } if (needsSafariColonFix || needsAllFix) { return cssText; } } return importStringified || rule.cssText; } function fixSafariColons(cssStringified) { const regex = /(\[(?:[\w-]+)[^\\])(:(?:[\w-]+)\])/gm; return cssStringified.replace(regex, "$1\\$2"); } function isCSSImportRule(rule) { return "styleSheet" in rule; } function isCSSStyleRule(rule) { return "selectorText" in rule; } class Mirror { constructor() { __publicField(this, "idNodeMap", /* @__PURE__ */ new Map()); __publicField(this, "nodeMetaMap", /* @__PURE__ */ new WeakMap()); } getId(n) { if (!n) return -1; const id = this.getMeta(n)?.id; return id ?? -1; } getNode(id) { return this.idNodeMap.get(id) || null; } getIds() { return Array.from(this.idNodeMap.keys()); } getMeta(n) { return this.nodeMetaMap.get(n) || null; } // removes the node from idNodeMap // doesn't remove the node from nodeMetaMap removeNodeFromMap(n) { const id = this.getId(n); this.idNodeMap.delete(id); if (n.childNodes) { n.childNodes.forEach( (childNode) => this.removeNodeFromMap(childNode) ); } } has(id) { return this.idNodeMap.has(id); } hasNode(node) { return this.nodeMetaMap.has(node); } add(n, meta) { const id = meta.id; this.idNodeMap.set(id, n); this.nodeMetaMap.set(n, meta); } replace(id, n) { const oldNode = this.getNode(id); if (oldNode) { const meta = this.nodeMetaMap.get(oldNode); if (meta) this.nodeMetaMap.set(n, meta); } this.idNodeMap.set(id, n); } reset() { this.idNodeMap = /* @__PURE__ */ new Map(); this.nodeMetaMap = /* @__PURE__ */ new WeakMap(); } } function createMirror() { return new Mirror(); } function shouldMaskInput({ maskInputOptions, tagName, type }) { if (tagName === "OPTION") { tagName = "SELECT"; } return Boolean( maskInputOptions[tagName.toLowerCase()] || type && maskInputOptions[type] || type === "password" || // Default to "text" option for inputs without a "type" attribute defined tagName === "INPUT" && !type && maskInputOptions["text"] ); } function maskInputValue({ isMasked, element, value, maskInputFn }) { let text = value || ""; if (!isMasked) { return text; } if (maskInputFn) { text = maskInputFn(text, element); } return "*".repeat(text.length); } function toLowerCase(str) { return str.toLowerCase(); } function toUpperCase(str) { return str.toUpperCase(); } const ORIGINAL_ATTRIBUTE_NAME = "__rrweb_original__"; function is2DCanvasBlank(canvas) { const ctx = canvas.getContext("2d"); if (!ctx) return true; const chunkSize = 50; for (let x = 0; x < canvas.width; x += chunkSize) { for (let y = 0; y < canvas.height; y += chunkSize) { const getImageData = ctx.getImageData; const originalGetImageData = ORIGINAL_ATTRIBUTE_NAME in getImageData ? getImageData[ORIGINAL_ATTRIBUTE_NAME] : getImageData; const pixelBuffer = new Uint32Array( // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access originalGetImageData.call( ctx, x, y, Math.min(chunkSize, canvas.width - x), Math.min(chunkSize, canvas.height - y) ).data.buffer ); if (pixelBuffer.some((pixel) => pixel !== 0)) return false; } } return true; } function getInputType(element) { const type = element.type; return element.hasAttribute("data-rr-is-password") ? "password" : type ? ( // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion toLowerCase(type) ) : null; } function getInputValue(el, tagName, type) { if (tagName === "INPUT" && (type === "radio" || type === "checkbox")) { return el.getAttribute("value") || ""; } return el.value; } function extractFileExtension(path, baseURL) { let url; try { url = new URL(path, baseURL ?? window.location.href); } catch (err) { return null; } const regex = /\.([0-9a-z]+)(?:$)/i; const match = url.pathname.match(regex); return match?.[1] ?? null; } const cachedImplementations$1 = {}; function getImplementation$1(name) { const cached = cachedImplementations$1[name]; if (cached) { return cached; } const document2 = window.document; let impl = window[name]; if (document2 && typeof document2.createElement === "function") { try { const sandbox = document2.createElement("iframe"); sandbox.hidden = true; document2.head.appendChild(sandbox); const contentWindow = sandbox.contentWindow; if (contentWindow && contentWindow[name]) { impl = // eslint-disable-next-line @typescript-eslint/unbound-method contentWindow[name]; } document2.head.removeChild(sandbox); } catch (e) { } } return cachedImplementations$1[name] = impl.bind( window ); } function setTimeout$1(...rest) { return getImplementation$1("setTimeout")(...rest); } function clearTimeout$1(...rest) { return getImplementation$1("clearTimeout")(...rest); } function getIFrameContentDocument$1(iframe) { try { return iframe.contentDocument; } catch { } } function getIFrameContentWindow$1(iframe) { try { return iframe.contentWindow; } catch { } } let _id = 1; const tagNameRegex = new RegExp("[^a-z0-9-_:]"); const IGNORED_NODE = -2; function genId() { return _id++; } function getValidTagName(element) { if (element instanceof HTMLFormElement) { return "form"; } const processedTagName = toLowerCase(element.tagName); if (tagNameRegex.test(processedTagName)) { return "div"; } return processedTagName; } function extractOrigin(url) { let origin = ""; if (url.indexOf("//") > -1) { origin = url.split("/").slice(0, 3).join("/"); } else { origin = url.split("/")[0]; } origin = origin.split("?")[0]; return origin; } let canvasService; let canvasCtx; const URL_IN_CSS_REF = /url\((?:(')([^']*)'|(")(.*?)"|([^)]*))\)/gm; const URL_PROTOCOL_MATCH = /^(?:[a-z+]+:)?\/\//i; const URL_WWW_MATCH = /^www\..*/i; const DATA_URI = /^(data:)([^,]*),(.*)/i; function filterCSSPropertiesFromInlineStyle(cssText, ignoredProperties) { if (!cssText || ignoredProperties.size === 0) { return cssText; } try { const properties = cssText.split(";"); const filteredProperties = []; for (let property of properties) { property = property.trim(); if (!property) continue; const colonIndex = property.indexOf(":"); if (colonIndex === -1) { filteredProperties.push(property); continue; } const propertyName = property.slice(0, colonIndex).trim(); if (!ignoredProperties.has(propertyName)) { filteredProperties.push(property); } } return filteredProperties.join("; ") + (filteredProperties.length > 0 && cssText.endsWith(";") ? ";" : ""); } catch (error) { console.warn("Error filtering CSS properties:", error); return cssText; } } function absoluteToStylesheet(cssText, href) { return (cssText || "").replace( URL_IN_CSS_REF, (origin, quote1, path1, quote2, path2, path3) => { const filePath = path1 || path2 || path3; const maybeQuote = quote1 || quote2 || ""; if (!filePath) { return origin; } if (URL_PROTOCOL_MATCH.test(filePath) || URL_WWW_MATCH.test(filePath)) { return `url(${maybeQuote}${filePath}${maybeQuote})`; } if (DATA_URI.test(filePath)) { return `url(${maybeQuote}${filePath}${maybeQuote})`; } if (filePath[0] === "/") { return `url(${maybeQuote}${extractOrigin(href) + filePath}${maybeQuote})`; } const stack = href.split("/"); const parts = filePath.split("/"); stack.pop(); for (const part of parts) { if (part === ".") { continue; } else if (part === "..") { stack.pop(); } else { stack.push(part); } } return `url(${maybeQuote}${stack.join("/")}${maybeQuote})`; } ); } const SRCSET_NOT_SPACES = /^[^ \t\n\r\u000c]+/; const SRCSET_COMMAS_OR_SPACES = /^[, \t\n\r\u000c]+/; function getAbsoluteSrcsetString(doc, attributeValue) { if (attributeValue.trim() === "") { return attributeValue; } let pos = 0; function collectCharacters(regEx) { let chars2; const match = regEx.exec(attributeValue.substring(pos)); if (match) { chars2 = match[0]; pos += chars2.length; return chars2; } return ""; } const output = []; while (true) { collectCharacters(SRCSET_COMMAS_OR_SPACES); if (pos >= attributeValue.length) { break; } let url = collectCharacters(SRCSET_NOT_SPACES); if (url.slice(-1) === ",") { url = absoluteToDoc(doc, url.substring(0, url.length - 1)); output.push(url); } else { let descriptorsStr = ""; url = absoluteToDoc(doc, url); let inParens = false; while (true) { const c = attributeValue.charAt(pos); if (c === "") { output.push((url + descriptorsStr).trim()); break; } else if (!inParens) { if (c === ",") { pos += 1; output.push((url + descriptorsStr).trim()); break; } else if (c === "(") { inParens = true; } } else { if (c === ")") { inParens = false; } } descriptorsStr += c; pos += 1; } } } return output.join(", "); } const cachedDocument = /* @__PURE__ */ new WeakMap(); function absoluteToDoc(doc, attributeValue) { if (!attributeValue || attributeValue.trim() === "") { return attributeValue; } return getHref(doc, attributeValue); } function isSVGElement(el) { return Boolean(el.tagName === "svg" || el.ownerSVGElement); } function getHref(doc, customHref) { let a = cachedDocument.get(doc); if (!a) { a = doc.createElement("a"); cachedDocument.set(doc, a); } if (!customHref) { customHref = ""; } else if (customHref.startsWith("blob:") || customHref.startsWith("data:")) { return customHref; } a.setAttribute("href", customHref); return a.href; } function transformAttribute(doc, tagName, name, value, element, maskAttributeFn, ignoreCSSAttributes) { if (!value) { return value; } if (name === "src" || name === "href" && !(tagName === "use" && value[0] === "#")) { return absoluteToDoc(doc, value); } else if (name === "xlink:href" && value[0] !== "#") { return absoluteToDoc(doc, value); } else if (name === "background" && (tagName === "table" || tagName === "td" || tagName === "th")) { return absoluteToDoc(doc, value); } else if (name === "srcset") { return getAbsoluteSrcsetString(doc, value); } else if (name === "style") { let processedStyle = absoluteToStylesheet(value, getHref(doc)); if (ignoreCSSAttributes && ignoreCSSAttributes.size > 0) { processedStyle = filterCSSPropertiesFromInlineStyle( processedStyle, ignoreCSSAttributes ); } return processedStyle; } else if (tagName === "object" && name === "data") { return absoluteToDoc(doc, value); } if (typeof maskAttributeFn === "function") { return maskAttributeFn(name, value, element); } return value; } function ignoreAttribute(tagName, name, _value) { return (tagName === "video" || tagName === "audio") && name === "autoplay"; } function _isBlockedElement(element, blockClass, blockSelector, unblockSelector) { try { if (unblockSelector && element.matches(unblockSelector)) { return false; } if (typeof blockClass === "string") { if (element.classList.contains(blockClass)) { return true; } } else { for (let eIndex = element.classList.length; eIndex--; ) { const className = element.classList[eIndex]; if (blockClass.test(className)) { return true; } } } if (blockSelector) { return element.matches(blockSelector); } } catch (e) { } return false; } function elementClassMatchesRegex(el, regex) { for (let eIndex = el.classList.length; eIndex--; ) { const className = el.classList[eIndex]; if (regex.test(className)) { return true; } } return false; } function distanceToMatch(node, matchPredicate, limit = Infinity, distance = 0) { if (!node) return -1; if (node.nodeType !== node.ELEMENT_NODE) return -1; if (distance > limit) return -1; if (matchPredicate(node)) return distance; return distanceToMatch(node.parentNode, matchPredicate, limit, distance + 1); } function createMatchPredicate(className, selector) { return (node) => { const el = node; if (el === null) return false; try { if (className) { if (typeof className === "string") { if (el.matches(`.${className}`)) return true; } else if (elementClassMatchesRegex(el, className)) { return true; } } if (selector && el.matches(selector)) return true; return false; } catch { return false; } }; } function needMaskingText(node, maskTextClass, maskTextSelector, unmaskTextClass, unmaskTextSelector, maskAllText) { try { const el = node.nodeType === node.ELEMENT_NODE ? node : node.parentElement; if (el === null) return false; if (el.tagName === "INPUT") { const autocomplete = el.getAttribute("autocomplete"); const disallowedAutocompleteValues = [ "current-password", "new-password", "cc-number", "cc-exp", "cc-exp-month", "cc-exp-year", "cc-csc" ]; if (disallowedAutocompleteValues.includes(autocomplete)) { return true; } } let maskDistance = -1; let unmaskDistance = -1; if (maskAllText) { unmaskDistance = distanceToMatch( el, createMatchPredicate(unmaskTextClass, unmaskTextSelector) ); if (unmaskDistance < 0) { return true; } maskDistance = distanceToMatch( el, createMatchPredicate(maskTextClass, maskTextSelector), unmaskDistance >= 0 ? unmaskDistance : Infinity ); } else { maskDistance = distanceToMatch( el, createMatchPredicate(maskTextClass, maskTextSelector) ); if (maskDistance < 0) { return false; } unmaskDistance = distanceToMatch( el, createMatchPredicate(unmaskTextClass, unmaskTextSelector), maskDistance >= 0 ? maskDistance : Infinity ); } return maskDistance >= 0 ? unmaskDistance >= 0 ? maskDistance <= unmaskDistance : true : unmaskDistance >= 0 ? false : !!maskAllText; } catch (e) { } return !!maskAllText; } function onceIframeLoaded(iframeEl, listener, iframeLoadTimeout) { const win = getIFrameContentWindow$1(iframeEl); if (!win) { return; } let fired = false; let readyState; try { readyState = win.document.readyState; } catch (error) { return; } if (readyState !== "complete") { const timer = setTimeout$1(() => { if (!fired) { listener(); fired = true; } }, iframeLoadTimeout); iframeEl.addEventListener("load", () => { clearTimeout$1(timer); fired = true; listener(); }); return; } const blankUrl = "about:blank"; if (win.location.href !== blankUrl || iframeEl.src === blankUrl || iframeEl.src === "") { setTimeout$1(listener, 0); return iframeEl.addEventListener("load", listener); } iframeEl.addEventListener("load", listener); } function onceStylesheetLoaded(link, listener, styleSheetLoadTimeout) { let fired = false; let styleSheetLoaded; try { styleSheetLoaded = link.sheet; } catch (error) { styleSheetLoaded = null; } if (styleSheetLoaded) return; const timer = setTimeout$1(() => { if (!fired) { listener(); fired = true; } }, styleSheetLoadTimeout); link.addEventListener("load", () => { clearTimeout$1(timer); fired = true; listener(); }); } function serializeNode(n, options) { const { doc, mirror, blockClass, blockSelector, unblockSelector, maskAllText, maskAttributeFn, maskTextClass, unmaskTextClass, maskTextSelector, unmaskTextSelector, inlineStylesheet, maskInputOptions = {}, maskTextFn, maskInputFn, dataURLOptions = {}, inlineImages, recordCanvas, keepIframeSrcFn, newlyAddedElement = false, ignoreCSSAttributes } = options; const rootId = getRootId(doc, mirror); switch (n.nodeType) { case n.DOCUMENT_NODE: if (n.compatMode !== "CSS1Compat") { return { type: NodeType$1.Document, childNodes: [], compatMode: n.compatMode // probably "BackCompat" }; } else { return { type: NodeType$1.Document, childNodes: [] }; } case n.DOCUMENT_TYPE_NODE: return { type: NodeType$1.DocumentType, name: n.name, publicId: n.publicId, systemId: n.systemId, rootId }; case n.ELEMENT_NODE: return serializeElementNode(n, { doc, blockClass, blockSelector, unblockSelector, inlineStylesheet, maskAttributeFn, maskInputOptions, maskInputFn, dataURLOptions, inlineImages, recordCanvas, keepIframeSrcFn, newlyAddedElement, rootId, maskTextClass, unmaskTextClass, maskTextSelector, unmaskTextSelector, ignoreCSSAttributes }); case n.TEXT_NODE: return serializeTextNode(n, { doc, maskAllText, maskTextClass, unmaskTextClass, maskTextSelector, unmaskTextSelector, maskTextFn, maskInputOptions, maskInputFn, rootId }); case n.CDATA_SECTION_NODE: return { type: NodeType$1.CDATA, textContent: "", rootId }; case n.COMMENT_NODE: return { type: NodeType$1.Comment, textContent: n.textContent || "", rootId }; default: return false; } } function getRootId(doc, mirror) { if (!mirror.hasNode(doc)) return void 0; const docId = mirror.getId(doc); return docId === 1 ? void 0 : docId; } function serializeTextNode(n, options) { const { maskAllText, maskTextClass, unmaskTextClass, maskTextSelector, unmaskTextSelector, maskTextFn, maskInputOptions, maskInputFn, rootId } = options; const parentTagName = n.parentNode && n.parentNode.tagName; let textContent = n.textContent; const isStyle = parentTagName === "STYLE" ? true : void 0; const isScript = parentTagName === "SCRIPT" ? true : void 0; const isTextarea = parentTagName === "TEXTAREA" ? true : void 0; if (isStyle && textContent) { try { if (n.nextSibling || n.previousSibling) { } else if (n.parentNode.sheet?.cssRules) { textContent = stringifyStylesheet( n.parentNode.sheet ); } } catch (err) { console.warn( `Cannot get CSS styles from text's parentNode. Error: ${err}`, n ); } textContent = absoluteToStylesheet(textContent, getHref(options.doc)); } if (isScript) { textContent = "SCRIPT_PLACEHOLDER"; } const forceMask = needMaskingText( n, maskTextClass, maskTextSelector, unmaskTextClass, unmaskTextSelector, maskAllText ); if (!isStyle && !isScript && !isTextarea && textContent && forceMask) { textContent = maskTextFn ? maskTextFn(textContent, n.parentElement) : textContent.replace(/[\S]/g, "*"); } if (isTextarea && textContent && (maskInputOptions.textarea || forceMask)) { textContent = maskInputFn ? maskInputFn(textContent, n.parentNode) : textContent.replace(/[\S]/g, "*"); } if (parentTagName === "OPTION" && textContent) { const isInputMasked = shouldMaskInput({ type: null, tagName: parentTagName, maskInputOptions }); textContent = maskInputValue({ isMasked: needMaskingText( n, maskTextClass, maskTextSelector, unmaskTextClass, unmaskTextSelector, isInputMasked ), element: n, value: textContent, maskInputFn }); } return { type: NodeType$1.Text, textContent: textContent || "", isStyle, rootId }; } function serializeElementNode(n, options) { const { doc, blockClass, blockSelector, unblockSelector, inlineStylesheet, maskInputOptions = {}, maskAttributeFn, maskInputFn, dataURLOptions = {}, inlineImages, recordCanvas, keepIframeSrcFn, newlyAddedElement = false, rootId, maskTextClass, unmaskTextClass, maskTextSelector, unmaskTextSelector, ignoreCSSAttributes } = options; const needBlock = _isBlockedElement( n, blockClass, blockSelector, unblockSelector ); const tagName = getValidTagName(n); let attributes2 = {}; const len = n.attributes.length; for (let i = 0; i < len; i++) { const attr = n.attributes[i]; if (attr.name && !ignoreAttribute(tagName, attr.name, attr.value)) { attributes2[attr.name] = transformAttribute( doc, tagName, toLowerCase(attr.name), attr.value, n, maskAttributeFn, ignoreCSSAttributes ); } } if (tagName === "link" && inlineStylesheet) { const stylesheet = Array.from(doc.styleSheets).find((s) => { return s.href === n.href; }); let cssText = null; if (stylesheet) { cssText = stringifyStylesheet(stylesheet); } if (cssText) { attributes2.rel = null; attributes2.href = null; attributes2.crossorigin = null; attributes2._cssText = absoluteToStylesheet(cssText, stylesheet.href); } } if (tagName === "style" && n.sheet && // TODO: Currently we only try to get dynamic stylesheet when it is an empty style element !(n.innerText || n.textContent || "").trim().length) { const cssText = stringifyStylesheet( n.sheet ); if (cssText) { attributes2._cssText = absoluteToStylesheet(cssText, getHref(doc)); } } if (tagName === "input" || tagName === "textarea" || tagName === "select" || tagName === "option") { const el = n; const type = getInputType(el); const value = getInputValue(el, toUpperCase(tagName), type); const checked = el.checked; if (type !== "submit" && type !== "button" && value) { const forceMask = needMaskingText( el, maskTextClass, maskTextSelector, unmaskTextClass, unmaskTextSelector, shouldMaskInput({ type, tagName: toUpperCase(tagName), maskInputOptions }) ); attributes2.value = maskInputValue({ isMasked: forceMask, element: el, value, maskInputFn }); } if (checked) { attributes2.checked = checked; } } if (tagName === "option") { if (n.selected && !maskInputOptions["select"]) { attributes2.selected = true; } else { delete attributes2.selected; } } if (tagName === "canvas" && recordCanvas) { if (n.__context === "2d") { if (!is2DCanvasBlank(n)) { attributes2.rr_dataURL = n.toDataURL( dataURLOptions.type, dataURLOptions.quality ); } } else if (!("__context" in n)) { const canvasDataURL = n.toDataURL( dataURLOptions.type, dataURLOptions.quality ); const blankCanvas = doc.createElement("canvas"); blankCanvas.width = n.width; blankCanvas.height = n.height; const blankCanvasDataURL = blankCanvas.toDataURL( dataURLOptions.type, dataURLOptions.quality ); if (canvasDataURL !== blankCanvasDataURL) { attributes2.rr_dataURL = canvasDataURL; } } } if (tagName === "img" && inlineImages) { if (!canvasService) { canvasService = doc.createElement("canvas"); canvasCtx = canvasService.getContext("2d"); } const image = n; const imageSrc = image.currentSrc || image.getAttribute("src") || "<unknown-src>"; const priorCrossOrigin = image.crossOrigin; const recordInlineImage = () => { image.removeEventListener("load", recordInlineImage); try { canvasService.width = image.naturalWidth; canvasService.height = image.naturalHeight; canvasCtx.drawImage(image, 0, 0); attributes2.rr_dataURL = canvasService.toDataURL( dataURLOptions.type, dataURLOptions.quality ); } catch (err) { if (image.crossOrigin !== "anonymous") { image.crossOrigin = "anonymous"; if (image.complete && image.naturalWidth !== 0) recordInlineImage(); else image.addEventListener("load", recordInlineImage); return; } else { console.warn( `Cannot inline img src=${imageSrc}! Error: ${err}` ); } } if (image.crossOrigin === "anonymous") { priorCrossOrigin ? attributes2.crossOrigin = priorCrossOrigin : image.removeAttribute("crossorigin"); } }; if (image.complete && image.naturalWidth !== 0) recordInlineImage(); else image.addEventListener("load", recordInlineImage); } if (tagName === "audio" || tagName === "video") { attributes2.rr_mediaState = n.paused ? "paused" : "played"; attributes2.rr_mediaCurrentTime = n.currentTime; } if (!newlyAddedElement) { if (n.scrollLeft) { attributes2.rr_scrollLeft = n.scrollLeft; } if (n.scrollTop) { attributes2.rr_scrollTop = n.scrollTop; } } if (needBlock) { const { width, height } = n.getBoundingClientRect(); attributes2 = { class: attributes2.class, rr_width: `${width}px`, rr_height: `${height}px` }; } if (tagName === "iframe" && !keepIframeSrcFn(attributes2.src)) { if (!needBlock && !getIFrameContentDocument$1(n)) { attributes2.rr_src = attributes2.src; } delete attributes2.src; } let isCustomElement; try { if (customElements.get(tagName)) isCustomElement = true; } catch (e) { } return { type: NodeType$1.Element, tagName, attributes: attributes2, childNodes: [], isSVG: isSVGElement(n) || void 0, needBlock, rootId, isCustom: isCustomElement }; } function lowerIfExists(maybeAttr) { if (maybeAttr === void 0 || maybeAttr === null) { return ""; } else { return maybeAttr.toLowerCase(); } } function slimDOMExcluded(sn, slimDOMOptions) { if (slimDOMOptions.comment && sn.type === NodeType$1.Comment) { return true; } else if (sn.type === NodeType$1.Element) { if (slimDOMOptions.script && // script tag (sn.tagName === "script" || // (module)preload link sn.tagName === "link" && (sn.attributes.rel === "preload" || sn.attributes.rel === "modulepreload") || // prefetch link sn.tagName === "link" && sn.attributes.rel === "prefetch" && typeof sn.attributes.href === "string" && extractFileExtension(sn.attributes.href) === "js")) { return true; } else if (slimDOMOptions.headFavicon && (sn.tagName === "link" && sn.attributes.rel === "shortcut icon" || sn.tagName === "meta" && (lowerIfExists(sn.attributes.name).match( /^msapplication-tile(image|color)$/ ) || lowerIfExists(sn.attributes.name) === "application-name" || lowerIfExists(sn.attributes.rel) === "icon" || lowerIfExists(sn.attributes.rel) === "apple-touch-icon" || lowerIfExists(sn.attributes.rel) === "shortcut icon"))) { return true; } else if (sn.tagName === "meta") { if (slimDOMOptions.headMetaDescKeywords && lowerIfExists(sn.attributes.name).match(/^description|keywords$/)) { return true; } else if (slimDOMOptions.headMetaSocial && (lowerIfExists(sn.attributes.property).match(/^(og|twitter|fb):/) || // og = opengraph (facebook) lowerIfExists(sn.attributes.name).match(/^(og|twitter):/) || lowerIfExists(sn.attributes.name) === "pinterest")) { return true; } else if (slimDOMOptions.headMetaRobots && (lowerIfExists(sn.attributes.name) === "robots" || lowerIfExists(sn.attributes.name) === "googlebot" || lowerIfExists(sn.attributes.name) === "bingbot")) { return true; } else if (slimDOMOptions.headMetaHttpEquiv && sn.attributes["http-equiv"] !== void 0) { return true; } else if (slimDOMOptions.headMetaAuthorship && (lowerIfExists(sn.attributes.name) === "author" || lowerIfExists(sn.attributes.name) === "generator" || lowerIfExists(sn.attributes.name) === "framework" || lowerIfExists(sn.attributes.name) === "publisher" || lowerIfExists(sn.attributes.name) === "progid" || lowerIfExists(sn.attributes.property).match(/^article:/) || lowerIfExists(sn.attributes.property).match(/^product:/))) { return true; } else if (slimDOMOptions.headMetaVerification && (lowerIfExists(sn.attributes.name) === "google-site-verification" || lowerIfExists(sn.attributes.name) === "yandex-verification" || lowerIfExists(sn.attributes.name) === "csrf-token" || lowerIfExists(sn.attributes.name) === "p:domain_verify" || lowerIfExists(sn.attributes.name) === "verify-v1" || lowerIfExists(sn.attributes.name) === "verification" || lowerIfExists(sn.attributes.name) === "shopify-checkout-api-token")) { return true; } } } return false; } function serializeNodeWithId(n, options) { const { doc, mirror, blockClass, blockSelector, unblockSelector, maskAllText, maskTextClass, unmaskTextClass, maskTextSelector, unmaskTextSelector, skipChild = false, inlineStylesheet = true, maskInputOptions = {}, maskAttributeFn, maskTextFn, maskInputFn, slimDOMOptions, dataURLOptions = {}, inlineImages = false, recordCanvas = false, onSerialize, onIframeLoad, iframeLoadTimeout = 5e3, onBlockedImageLoad, onStylesheetLoad, stylesheetLoadTimeout = 5e3, keepIframeSrcFn = () => false, newlyAddedElement = false, ignoreCSSAttributes } = options; let { preserveWhiteSpace = true } = options; const _serializedNode = serializeNode(n, { doc, mirror, blockClass, blockSelector, maskAllText, unblockSelector, maskTextClass, unmaskTextClass, maskTextSelector, unmaskTextSelector, inlineStylesheet, maskInputOptions, maskAttributeFn, maskTextFn, maskInputFn, dataURLOptions, inlineImages, recordCanvas, keepIframeSrcFn, newlyAddedElement, ignoreCSSAttributes }); if (!_serializedNode) { console.warn(n, "not serialized"); return null; } let id; if (mirror.hasNode(n)) { id = mirror.getId(n); } else if (slimDOMExcluded(_serializedNode, slimDOMOptions) || !preserveWhiteSpace && _serializedNode.type === NodeType$1.Text && !_serializedNode.isStyle && !_serializedNode.textContent.trim().length) { id = IGNORED_NODE; } else { id = genId(); } const serializedNode2 = Object.assign(_serializedNode, { id }); mirror.add(n, serializedNode2); if (id === IGNORED_NODE) { return null; } if (onSerialize) { onSerialize(n); } let recordChild = !skipChild; if (serializedNode2.type === NodeType$1.Element) { recordChild = recordChild && !serializedNode2.needBlock; const shadowRoot = n.shadowRoot; if (shadowRoot && isNativeShadowDom(shadowRoot)) serializedNode2.isShadowHost = true; } if ((serializedNode2.type === NodeType$1.Document || serializedNode2.type === NodeType$1.Element) && recordChild) { if (slimDOMOptions.headWhitespace && serializedNode2.type === NodeType$1.Element && serializedNode2.tagName === "head") { preserveWhiteSpace = false; } const bypassOptions = { doc, mirror, blockClass, blockSelector, maskAllText, unblockSelector, maskTextClass, unmaskTextClass, maskTextSelector, unmaskTextSelector, skipChild, inlineStylesheet, maskInputOptions, maskAttributeFn, maskTextFn, maskInputFn, slimDOMOptions, dataURLOptions, inlineImages, recordCanvas, preserveWhiteSpace, onSerialize, onIframeLoad, iframeLoadTimeout, onBlockedImageLoad, onStylesheetLoad, stylesheetLoadTimeout, keepIframeSrcFn, ignoreCSSAttributes }; const childNodes = n.childNodes ? Array.from(n.childNodes) : []; for (const childN of childNodes) { const serializedChildNode = serializeNodeWithId(childN, bypassOptions); if (serializedChildNode) { serializedNode2.childNodes.push(serializedChildNode); } } if (isElement$1(n) && n.shadowRoot) { for (const childN of Array.from(n.shadowRoot.childNodes)) { const serializedChildNode = serializeNodeWithId(childN, bypassOptions); if (serializedChildNode) { isNativeShadowDom(n.shadowRoot) && (serializedChildNode.isShadow = true); serializedNode2.childNodes.push(serializedChildNode); } } } } if (n.parentNode && isShadowRoot(n.parentNode) && isNativeShadowDom(n.parentNode)) { serializedNode2.isShadow = true; } if (serializedNode2.type === NodeType$1.Element && serializedNode2.tagName === "iframe" && !serializedNode2.needBlock) { onceIframeLoaded( n, () => { const iframeDoc = getIFrameContentDocument$1(n); if (iframeDoc && onIframeLoad) { const serializedIframeNode = serializeNodeWithId(iframeDoc, { doc: iframeDoc, mirror, blockClass, blockSelector, unblockSelector, maskAllText, maskTextClass, unmaskTextClass, maskTextSelector, unmaskTextSelector, skipChild: false, inlineStylesheet, maskInputOptions, maskAttributeFn, maskTextFn, maskInputFn, slimDOMOptions, dataURLOptions, inlineImages, recordCanvas, preserveWhiteSpace, onSerialize, onIframeLoad, iframeLoadTimeout, onStylesheetLoad, stylesheetLoadTimeout, keepIframeSrcFn, ignoreCSSAttributes }); if (serializedIframeNode) { onIframeLoad( n, serializedIframeNode ); } } }, iframeLoadTimeout ); } if (serializedNode2.type === NodeType$1.Element && serializedNode2.tagName === "img" && !n.complete && serializedNode2.needBlock) { const image = n; const updateImageDimensions = () => { if (image.isConnected && !image.complete && onBlockedImageLoad) { try { const rect = image.getBoundingClientRect(); if (rect.width > 0 && rect.height > 0) { onBlockedImageLoad(image, serializedNode2, rect); } } catch (error) { } } image.removeEventListener("load", updateImageDimensions); }; if (image.isConnected) { image.addEventListener("load", updateImageDimensions); } } if (serializedNode2.type === NodeType$1.Element && serializedNode2.tagName === "link" && typeof serializedNode2.attributes.rel === "string" && (serializedNode2.attributes.rel === "stylesheet" || serializedNode2.attributes.rel === "preload" && typeof serializedNode2.attributes.href === "string" && extractFileExtension(serializedNode2.attributes.href) === "css")) { onceStylesheetLoaded( n, () => { if (onStylesheetLoad) { const serializedLinkNode = serializeNodeWithId(n, { doc, mirror, blockClass, blockSelector, unblockSelector, maskAllText, maskTextClass, unmaskTextClass, maskTextSelector, unmaskTextSelector, skipChild: false, inlineStylesheet, maskInputOptions, maskAttributeFn, maskTextFn, maskInputFn, slimDOMOptions, dataURLOptions, inlineImages, recordCanvas, preserveWhiteSpace, onSerialize, onIframeLoad, iframeLoadTimeout, onStylesheetLoad, stylesheetLoadTimeout, keepIframeSrcFn, ignoreCSSAttributes }); if (serializedLinkNode) { onStylesheetLoad( n, serializedLinkNode ); } } }, stylesheetLoadTimeout ); } if (serializedNode2.type === NodeType$1.Element) { delete serializedNode2.needBlock; } return serializedNode2; } function snapshot(n, options) { const { mirror = new Mirror(), blockClass = "rr-block", blockSelector = null, unblockSelector = null, maskAllText = false, maskTextClass = "rr-mask", unmaskTextClass = null, maskTextSelector = null, unmaskTextSelector = null, inlineStylesheet = true, inlineImages = false, recordCanvas = false, maskAllInputs = false, maskAttributeFn, maskTextFn, maskInputFn, slimDOM = false, dataURLOptions, preserveWhiteSpace, onSerialize, onIframeLoad, iframeLoadTimeout, onBlockedImageLoad, onStylesheetLoad, stylesheetLoadTimeout, keepIframeSrcFn = () => false, ignoreCSSAttributes = /* @__PURE__ */ new Set([]) } = options || {}; const maskInputOptions = maskAllInputs === true ? { color: true, date: true, "datetime-local": true, email: true, month: true, number: true, range: true, search: true, tel: true, text: true, time: true, url: true, week: true, textarea: true, select: true } : maskAllInputs === false ? {} : maskAllInputs; const slimDOMOptions = slimDOM === true || slimDOM === "all" ? ( // if true: set of sensible options that should not throw away any information { script: true, comment: true, headFavicon: true, headWhitespace: true, headMetaDescKeywords: slimDOM === "all", // destructive headMetaSocial: true, headMetaRobots: true, headMetaHttpEquiv: true, headMetaAuthorship: true, headMetaVerification: true } ) : slimDOM === false ? {} : slimDOM; return serializeNodeWithId(n, { doc: n, mirror, blockClass, blockSelector, unblockSelector, maskAllText, maskTextClass, unmaskTextClass, maskTextSelector, unmaskTextSelector, skipChild: false, inlineStylesheet, maskInputOptions, maskAttributeFn, maskTextFn, maskInputFn, slimDOMOptions, dataURLOptions, inlineImages, recordCanvas, preserveWhiteSpace, onSerialize, onIframeLoad, iframeLoadTimeout, onBlockedImageLoad, onStylesheetLoad, stylesheetLoadTimeout, keepIframeSrcFn, newlyAddedElement: false, ignoreCSSAttributes }); } function on(type, fn, target = document) { const options = { capture: true, passive: true }; target.addEventListener(type, fn, options); return () => target.removeEventListener(type, fn, options); } const DEPARTED_MIRROR_ACCESS_WARNING = "Please stop import mirror directly. Instead of that,\r\nnow you can use replayer.getMirror() to access the mirror instance of a replayer,\r\nor you can use record.mirror to access the mirror instance during recording."; let _mirror = { map: {}, getId() { console.error(DEPARTED_MIRROR_ACCESS_WARNING); return -1; }, getNode() { console.error(DEPARTED_MIRROR_ACCESS_WARNING); return null; }, removeNodeFromMap() { console.error(DEPARTED_MIRROR_ACCESS_WARNING); }, has() { console.error(DEPARTED_MIRROR_ACCESS_WARNING); return false; }, reset() { console.error(DEPARTED_MIRROR_ACCESS_WARNING); } }; if (typeof window !== "undefined" && window.Proxy && window.Reflect) { _mirror = new Proxy(_mirror, { get(target, prop, receiver) { if (prop === "map") { console.error(DEPARTED_MIRROR_ACCESS_WARNING); } return Reflect.get(target, prop, receiver); } }); } function throttle$1(func, wait, options = {}) { let timeout = null; let previous = 0; return function(...args) { const now = Date.now(); if (!previous && options.leading === false) { previous = now; } const remaining = wait - (now - previous); const context = this; if (remaining <= 0 || remaining > wait) { if (timeout) { clearTimeout$2(timeout); timeout = null; } previous = now; func.apply(context, args); } else if (!timeout && options.trailing !== false) { timeout = setTimeout$2(() => { previous = options.leading === false ? 0 : Date.now(); timeout = null; func.apply(context, args); }, remaining); } }; } function hookSetter(target, key, d, isRevoked, win = window) { const original = win.Object.getOwnPropertyDescriptor(target, key); win.Object.defineProperty( target, key, isRevoked ? d : { set(value) { setTimeout$2(() => { d.set.call(this, value); }, 0); if (original && original.set) { original.set.call(this, value); } } } ); return () => hookSetter(target, key, original || {}, true); } function patch(source, name, replacement) { try { if (!(name in source)) { return () => { }; } const original = source[name]; const wrapped = replacement(original); if (typeof wrapped === "function") { wrapped.prototype = wrapped.prototype || {}; Object.defineProperties(wrapped, { __rrweb_original__: { enumerable: false, value: original } }); } source[name] = wrapped; return () => { source[name] = original; }; } catch { return () => { }; } } let nowTimestamp = Date.now; if (!/* @__PURE__ */ /[1-9][0-9]{12}/.test(Date.now().toString())) { nowTimestamp = () => (/* @__PURE__ */ new Date()).getTime(); } function getWindowScroll(win) { const doc = win.document; return { left: doc.scrollingElement ? doc.scrollingElement.scrollLeft : win.pageXOffset !== void 0 ? win.pageXOffset : doc?.documentElement.scrollLeft || doc?.body?.parentElement?.scrollLeft || doc?.body?.scrollLeft || 0, top: doc.scrollingElement ? doc.scrollingElement.scrollTop : win.pageYOffset !== void 0 ? win.pageYOffset : doc?.documentElement.scrollTop || doc?.body?.parentElement?.scrollTop || doc?.body?.scrollTop || 0 }; } function getWindowHeight() { return window.innerHeight || document.documentElement && document.documentElement.clientHeight || document.body && document.body.clientHeight; } function getWindowWidth() { return window.innerWidth || document.documentElement && document.documentElement.clientWidth || document.body && document.body.clientWidth; } function closestElementOfNode$1(node) { if (!node) { return null; } try { const el = node.nodeType === node.ELEMENT_NODE ? node : node.parentElement; return el; } catch (error) { return null; } } function isBlocked(node, blockClass, blockSelector, unblockSelector, checkAncestors) { if (!node) { return false; } const el = closestElementOfNode$1(node); if (!el) { return false; } const blockedPredicate = createMatchPredicate(blockClass, blockSelector); if (!checkAncestors) { const isUnblocked = unblockSelector && el.matches(unblockSelector); return blockedPredicate(el) && !isUnblocked; } const blockDistance = distanceToMatch(el, blockedPredicate); let unblockDistance = -1; if (blockDistance < 0) { return false; } if (unblockSelector) { unblockDistance = distanceToMatch( el, createMatchPredicate(null, unblockSelector) ); } if (blockDistance > -1 && unblockDistance < 0) { return true; } return blockDistance < unblockDistance; } function isSerialized(n, mirror) { return mirror.getId(n) !== -1; } function isIgnored(n, mirror) { return mirror.getId(n) === IGNORED_NODE; } function isAncestorRemoved(target, mirror) { if (isShadowRoot(target)) { return false; } const id = mirror.getId(target); if (!mirror.has(id)) { return true; } if (target.parentNode && target.parentNode.nodeType === target.DOCUMENT_NODE) { return false; } if (!target.parentNode) { return true; } return isAncestorRemoved(target.parentNode, mirror); } function legacy_isTouchEvent(event) { return Boolean(event.changedTouches); } function polyfill(win = window) { if ("NodeList" in win && !win.NodeList.prototype.forEach) { win.NodeList.prototype.forEach = Array.prototype.forEach; } if ("DOMTokenList" in win && !win.DOMTokenList.prototype.forEach) { win.DOMTokenList.prototype.forEach = Array.prototype.forEach; } if (!Node.prototype.contains) { Node.prototype.contains = (...args) => { let node = args[0]; if (!(0 in args)) { throw new TypeError("1 argument is required"); } do { if (this === node) { return true; } } whil