@amplitude/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) • 12 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\ntype WindowWithZone = typeof globalThis & {\n Zone?: {\n __symbol__?: (key: string) => string;\n };\n};\n\ntype WindowWithUnpatchedSymbols = typeof globalThis &\n Record<string, TypeofPrototypeOwner>;\n\n/*\nAngular zone patches many things and can pass the untainted checks below, causing performance issues\nAngular zone, puts the unpatched originals on the window, and the names for hose on the zone object.\nSo, we get the unpatched versions from the window object if they exist.\nYou can rename Zone, but this is a good enough proxy to avoid going to an iframe to get the untainted versions.\nsee: https://github.com/angular/angular/issues/26948\n*/\nfunction angularZoneUnpatchedAlternative(key: keyof BasePrototypeCache) {\n const angularUnpatchedVersionSymbol = (\n globalThis as WindowWithZone\n )?.Zone?.__symbol__?.(key);\n if (\n angularUnpatchedVersionSymbol &&\n (globalThis as WindowWithUnpatchedSymbols)[angularUnpatchedVersionSymbol]\n ) {\n return (globalThis as WindowWithUnpatchedSymbols)[\n angularUnpatchedVersionSymbol\n ];\n } else {\n return undefined;\n }\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 candidate =\n angularZoneUnpatchedAlternative(key) ||\n (globalThis[key] as TypeofPrototypeOwner);\n const defaultPrototype = candidate.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) {\n untaintedBasePrototype[key] = candidate.prototype as BasePrototypeCache[T];\n return candidate.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 candidate.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\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};\n"],
"mappings": "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",
"names": ["testableAccessors", "testableMethods", "untaintedBasePrototype", "angularZoneUnpatchedAlternative", "key", "angularUnpatchedVersionSymbol", "_b", "_a", "getUntaintedPrototype", "candidate", "defaultPrototype", "accessorNames", "isUntaintedAccessors", "accessor", "methodNames", "isUntaintedMethods", "method", "iframeEl", "win", "untaintedObject", "e", "untaintedAccessorCache", "getUntaintedAccessor", "instance", "cacheKey", "untaintedPrototype", "untaintedAccessor", "untaintedMethodCache", "getUntaintedMethod", "untaintedMethod", "childNodes", "n", "parentNode", "parentElement", "textContent", "contains", "other", "getRootNode", "host", "styleSheets", "shadowRoot", "querySelector", "selectors", "querySelectorAll", "mutationObserverCtor", "index"]
}