@appsurify-testmap/rrweb-utils
Version:
This package contains the shared utility functions used across rrweb packages. See the [guide](../../guide.md) for more info on rrweb.
8 lines (7 loc) • 15 kB
Source Map (JSON)
{
"version": 3,
"sources": ["../src/index.ts"],
"sourcesContent": ["type PrototypeOwner = Node | ShadowRoot | MutationObserver | Element;\ntype TypeofPrototypeOwner =\n | typeof Node\n | typeof ShadowRoot\n | typeof MutationObserver\n | typeof Element;\n\ntype BasePrototypeCache = {\n Node: typeof Node.prototype;\n ShadowRoot: typeof ShadowRoot.prototype;\n MutationObserver: typeof MutationObserver.prototype;\n Element: typeof Element.prototype;\n};\n\nconst testableAccessors = {\n Node: ['childNodes', 'parentNode', 'parentElement', 'textContent'] as const,\n ShadowRoot: ['host', 'styleSheets'] as const,\n Element: ['shadowRoot', 'querySelector', 'querySelectorAll'] as const,\n MutationObserver: [] as const,\n} as const;\n\nconst testableMethods = {\n Node: ['contains', 'getRootNode'] as const,\n ShadowRoot: ['getSelection'],\n Element: [],\n MutationObserver: ['constructor'],\n} as const;\n\nconst untaintedBasePrototype: Partial<BasePrototypeCache> = {};\n\n/*\n When angular patches things - particularly the MutationObserver -\n they pass the `isNativeFunction` check\n That then causes performance issues\n because Angular's change detection\n doesn't like sharing a mutation observer\n Checking for the presence of the Zone object\n on global is a good-enough proxy for Angular\n to cover most cases\n (you can configure zone.js to have a different name\n on the global object and should then manually run rrweb\n outside the Zone)\n */\nexport const isAngularZonePresent = (): boolean => {\n return !!(globalThis as { Zone?: unknown }).Zone;\n};\n\nexport function getUntaintedPrototype<T extends keyof BasePrototypeCache>(\n key: T,\n): BasePrototypeCache[T] {\n if (untaintedBasePrototype[key])\n return untaintedBasePrototype[key] as BasePrototypeCache[T];\n\n const defaultObj = globalThis[key] as TypeofPrototypeOwner;\n const defaultPrototype = defaultObj.prototype as BasePrototypeCache[T];\n\n // use list of testable accessors to check if the prototype is tainted\n const accessorNames =\n key in testableAccessors ? testableAccessors[key] : undefined;\n const isUntaintedAccessors = Boolean(\n accessorNames &&\n // @ts-expect-error 2345\n accessorNames.every((accessor: keyof typeof defaultPrototype) =>\n Boolean(\n Object.getOwnPropertyDescriptor(defaultPrototype, accessor)\n ?.get?.toString()\n .includes('[native code]'),\n ),\n ),\n );\n\n const methodNames = key in testableMethods ? testableMethods[key] : undefined;\n const isUntaintedMethods = Boolean(\n methodNames &&\n methodNames.every(\n // @ts-expect-error 2345\n (method: keyof typeof defaultPrototype) =>\n typeof defaultPrototype[method] === 'function' &&\n defaultPrototype[method]?.toString().includes('[native code]'),\n ),\n );\n\n if (isUntaintedAccessors && isUntaintedMethods && !isAngularZonePresent()) {\n untaintedBasePrototype[key] = defaultObj.prototype as BasePrototypeCache[T];\n return defaultObj.prototype as BasePrototypeCache[T];\n }\n\n try {\n const iframeEl = document.createElement('iframe');\n document.body.appendChild(iframeEl);\n const win = iframeEl.contentWindow;\n if (!win) return defaultObj.prototype as BasePrototypeCache[T];\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any\n const untaintedObject = (win as any)[key]\n .prototype as BasePrototypeCache[T];\n // cleanup\n document.body.removeChild(iframeEl);\n\n if (!untaintedObject) return defaultPrototype;\n\n return (untaintedBasePrototype[key] = untaintedObject);\n } catch {\n return defaultPrototype;\n }\n}\n\nconst untaintedAccessorCache: Record<\n string,\n (this: PrototypeOwner, ...args: unknown[]) => unknown\n> = {};\n\nexport function getUntaintedAccessor<\n K extends keyof BasePrototypeCache,\n T extends keyof BasePrototypeCache[K],\n>(\n key: K,\n instance: BasePrototypeCache[K],\n accessor: T,\n): BasePrototypeCache[K][T] {\n const cacheKey = `${key}.${String(accessor)}`;\n if (untaintedAccessorCache[cacheKey])\n return untaintedAccessorCache[cacheKey].call(\n instance,\n ) as BasePrototypeCache[K][T];\n\n const untaintedPrototype = getUntaintedPrototype(key);\n // eslint-disable-next-line @typescript-eslint/unbound-method\n const untaintedAccessor = Object.getOwnPropertyDescriptor(\n untaintedPrototype,\n accessor,\n )?.get;\n\n if (!untaintedAccessor) return instance[accessor];\n\n untaintedAccessorCache[cacheKey] = untaintedAccessor;\n\n return untaintedAccessor.call(instance) as BasePrototypeCache[K][T];\n}\n\ntype BaseMethod<K extends keyof BasePrototypeCache> = (\n this: BasePrototypeCache[K],\n ...args: unknown[]\n) => unknown;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst untaintedMethodCache: Record<string, BaseMethod<any>> = {};\nexport function getUntaintedMethod<\n K extends keyof BasePrototypeCache,\n T extends keyof BasePrototypeCache[K],\n>(\n key: K,\n instance: BasePrototypeCache[K],\n method: T,\n): BasePrototypeCache[K][T] {\n const cacheKey = `${key}.${String(method)}`;\n if (untaintedMethodCache[cacheKey])\n return untaintedMethodCache[cacheKey].bind(\n instance,\n ) as BasePrototypeCache[K][T];\n\n const untaintedPrototype = getUntaintedPrototype(key);\n const untaintedMethod = untaintedPrototype[method];\n\n if (typeof untaintedMethod !== 'function') return instance[method];\n\n untaintedMethodCache[cacheKey] = untaintedMethod as BaseMethod<K>;\n\n return untaintedMethod.bind(instance) as BasePrototypeCache[K][T];\n}\n\nexport function childNodes(n: Node): NodeListOf<Node> {\n return getUntaintedAccessor('Node', n, 'childNodes');\n}\n\nexport function parentNode(n: Node): ParentNode | null {\n return getUntaintedAccessor('Node', n, 'parentNode');\n}\n\nexport function parentElement(n: Node): HTMLElement | null {\n return getUntaintedAccessor('Node', n, 'parentElement');\n}\n\nexport function textContent(n: Node): string | null {\n return getUntaintedAccessor('Node', n, 'textContent');\n}\n\nexport function contains(n: Node, other: Node): boolean {\n return getUntaintedMethod('Node', n, 'contains')(other);\n}\n\nexport function getRootNode(n: Node): Node {\n return getUntaintedMethod('Node', n, 'getRootNode')();\n}\n\nexport function host(n: ShadowRoot): Element | null {\n if (!n || !('host' in n)) return null;\n return getUntaintedAccessor('ShadowRoot', n, 'host');\n}\n\nexport function styleSheets(n: ShadowRoot): StyleSheetList {\n return n.styleSheets;\n}\n\nexport function shadowRoot(n: Node): ShadowRoot | null {\n if (!n || !('shadowRoot' in n)) return null;\n return getUntaintedAccessor('Element', n as Element, 'shadowRoot');\n}\n\nexport function querySelector(n: Element, selectors: string): Element | null {\n return getUntaintedAccessor('Element', n, 'querySelector')(selectors);\n}\n\nexport function querySelectorAll(\n n: Element,\n selectors: string,\n): NodeListOf<Element> {\n return getUntaintedAccessor('Element', n, 'querySelectorAll')(selectors);\n}\n\nexport function mutationObserverCtor(): (typeof MutationObserver)['prototype']['constructor'] {\n return getUntaintedPrototype('MutationObserver').constructor;\n}\n\n// copy from https://github.com/getsentry/sentry-javascript/blob/b2109071975af8bf0316d3b5b38f519bdaf5dc15/packages/utils/src/object.ts\nexport function patch(\n source: { [key: string]: any },\n name: string,\n replacement: (...args: unknown[]) => unknown,\n): () => void {\n try {\n if (!(name in source)) {\n return () => {\n //\n };\n }\n\n const original = source[name] as () => unknown;\n const wrapped = replacement(original);\n\n // Make sure it's a function first, as we need to attach an empty prototype for `defineProperties` to work\n // otherwise it'll throw \"TypeError: Object.defineProperties called on non-object\"\n if (typeof wrapped === 'function') {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n wrapped.prototype = wrapped.prototype || {};\n Object.defineProperties(wrapped, {\n __rrweb_original__: {\n enumerable: false,\n value: original,\n },\n });\n }\n\n source[name] = wrapped;\n\n return () => {\n source[name] = original;\n };\n } catch {\n return () => {\n //\n };\n // This can throw if multiple fill happens on a global object like XMLHttpRequest\n // Fixes https://github.com/getsentry/sentry-javascript/issues/2043\n }\n}\n\nexport function describeNode(el: Element): string {\n const tag = el.tagName.toLowerCase();\n const id = el.id ? `#${el.id}` : '';\n const classes = el.classList.length\n ? '.' + Array.from(el.classList).join('.')\n : '';\n return `${tag}${id}${classes}`;\n}\n\nexport function getElementVisibility(el: Element): {\n isVisible: boolean;\n ratio: number;\n} {\n const win = el.ownerDocument?.defaultView ?? window;\n const rect = el.getBoundingClientRect();\n\n const viewportWidth =\n win.innerWidth || win.document.documentElement.clientWidth || 0;\n const viewportHeight =\n win.innerHeight || win.document.documentElement.clientHeight || 0;\n\n const isRectVisible =\n rect.width > 0 &&\n rect.height > 0 &&\n rect.bottom > 0 &&\n rect.right > 0 &&\n rect.top < viewportHeight &&\n rect.left < viewportWidth;\n\n const style = win.getComputedStyle?.(el);\n const isStyleVisible =\n !!style &&\n style.display !== 'none' &&\n style.visibility !== 'hidden' &&\n (parseFloat(style.opacity) || 0) > 0;\n\n const isVisible = isStyleVisible && isRectVisible;\n\n let ratio = 0;\n if (isVisible) {\n const xOverlap = Math.max(\n 0,\n Math.min(rect.right, viewportWidth) - Math.max(rect.left, 0),\n );\n const yOverlap = Math.max(\n 0,\n Math.min(rect.bottom, viewportHeight) - Math.max(rect.top, 0),\n );\n const intersectionArea = xOverlap * yOverlap;\n const elementArea = rect.width * rect.height;\n ratio = elementArea > 0 ? intersectionArea / elementArea : 0;\n }\n\n return {\n isVisible,\n ratio,\n };\n}\n\nexport default {\n childNodes,\n parentNode,\n parentElement,\n textContent,\n contains,\n getRootNode,\n host,\n styleSheets,\n shadowRoot,\n querySelector,\n querySelectorAll,\n mutationObserver: mutationObserverCtor,\n patch,\n describeNode,\n getElementVisibility,\n};\n"],
"mappings": "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",
"names": ["_a"]
}