diff --git a/packages/rrweb/src/plugins/console/record/index.ts b/packages/rrweb/src/plugins/console/record/index.ts index 7fc0040b..bd4e5749 100644 --- a/packages/rrweb/src/plugins/console/record/index.ts +++ b/packages/rrweb/src/plugins/console/record/index.ts @@ -1,4 +1,4 @@ -import { listenerHandler, RecordPlugin } from '../../../types'; +import { listenerHandler, RecordPlugin, IWindow } from '../../../types'; import { patch } from '../../../utils'; import { ErrorStackParser, StackFrame } from './error-stack-parser'; import { stringify } from './stringify'; @@ -22,7 +22,7 @@ type LogRecordOptions = { level?: LogLevel[]; lengthThreshold?: number; stringifyOptions?: StringifyOptions; - logger?: Logger | string; + logger?: Logger | 'console'; }; const defaultLogOptions: LogRecordOptions = { @@ -106,7 +106,7 @@ export type Logger = { function initLogObserver( cb: logCallback, - win: Window, // top window or in an iframe + win: IWindow, // top window or in an iframe logOptions: LogRecordOptions, ): listenerHandler { const loggerType = logOptions.logger; @@ -115,7 +115,7 @@ function initLogObserver( } let logger: Logger; if (typeof loggerType === 'string') { - logger = (win as any)[loggerType]; + logger = win[loggerType]; } else { logger = loggerType; } diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts index a9444183..26cd1ef7 100644 --- a/packages/rrweb/src/record/observer.ts +++ b/packages/rrweb/src/record/observer.ts @@ -44,15 +44,16 @@ import { fontParam, Mirror, styleDeclarationCallback, + IWindow, } from '../types'; import MutationBuffer from './mutation'; import { IframeManager } from './iframe-manager'; import { ShadowDomManager } from './shadow-dom-manager'; -type WindowWithStoredMutationObserver = Window & { +type WindowWithStoredMutationObserver = IWindow & { __rrMutationObserver?: MutationObserver; }; -type WindowWithAngularZone = Window & { +type WindowWithAngularZone = IWindow & { Zone?: { __symbol__?: (key: string) => string; }; @@ -519,11 +520,14 @@ function getNestedCSSRulePositions(rule: CSSRule): number[] { function initStyleSheetObserver( cb: styleSheetRuleCallback, - win: Window, + win: IWindow, mirror: Mirror, ): listenerHandler { - const insertRule = (win as any).CSSStyleSheet.prototype.insertRule; - (win as any).CSSStyleSheet.prototype.insertRule = function (rule: string, index?: number) { + const insertRule = win.CSSStyleSheet.prototype.insertRule; + win.CSSStyleSheet.prototype.insertRule = function ( + rule: string, + index?: number, + ) { const id = mirror.getId(this.ownerNode as INode); if (id !== -1) { cb({ @@ -534,8 +538,8 @@ function initStyleSheetObserver( return insertRule.apply(this, arguments); }; - const deleteRule = (win as any).CSSStyleSheet.prototype.deleteRule; - (win as any).CSSStyleSheet.prototype.deleteRule = function (index: number) { + const deleteRule = win.CSSStyleSheet.prototype.deleteRule; + win.CSSStyleSheet.prototype.deleteRule = function (index: number) { const id = mirror.getId(this.ownerNode as INode); if (id !== -1) { cb({ @@ -550,20 +554,20 @@ function initStyleSheetObserver( [key: string]: GroupingCSSRuleTypes; } = {}; if (isCSSGroupingRuleSupported) { - supportedNestedCSSRuleTypes['CSSGroupingRule'] = (win as any).CSSGroupingRule; + supportedNestedCSSRuleTypes.CSSGroupingRule = win.CSSGroupingRule; } else { // Some browsers (Safari) don't support CSSGroupingRule // https://caniuse.com/?search=cssgroupingrule // fall back to monkey patching classes that would have inherited from CSSGroupingRule if (isCSSMediaRuleSupported) { - supportedNestedCSSRuleTypes['CSSMediaRule'] = (win as any).CSSMediaRule; + supportedNestedCSSRuleTypes.CSSMediaRule = win.CSSMediaRule; } if (isCSSConditionRuleSupported) { - supportedNestedCSSRuleTypes['CSSConditionRule'] = (win as any).CSSConditionRule; + supportedNestedCSSRuleTypes.CSSConditionRule = win.CSSConditionRule; } if (isCSSSupportsRuleSupported) { - supportedNestedCSSRuleTypes['CSSSupportsRule'] = (win as any).CSSSupportsRule; + supportedNestedCSSRuleTypes.CSSSupportsRule = win.CSSSupportsRule; } } @@ -612,8 +616,8 @@ function initStyleSheetObserver( }); return () => { - (win as any).CSSStyleSheet.prototype.insertRule = insertRule; - (win as any).CSSStyleSheet.prototype.deleteRule = deleteRule; + win.CSSStyleSheet.prototype.insertRule = insertRule; + win.CSSStyleSheet.prototype.deleteRule = deleteRule; Object.entries(supportedNestedCSSRuleTypes).forEach(([typeKey, type]) => { type.prototype.insertRule = unmodifiedFunctions[typeKey].insertRule; type.prototype.deleteRule = unmodifiedFunctions[typeKey].deleteRule; @@ -623,11 +627,11 @@ function initStyleSheetObserver( function initStyleDeclarationObserver( cb: styleDeclarationCallback, - win: Window, + win: IWindow, mirror: Mirror, ): listenerHandler { - const setProperty = (win as any).CSSStyleDeclaration.prototype.setProperty; - (win as any).CSSStyleDeclaration.prototype.setProperty = function ( + const setProperty = win.CSSStyleDeclaration.prototype.setProperty; + win.CSSStyleDeclaration.prototype.setProperty = function ( this: CSSStyleDeclaration, property: string, value: string, @@ -650,8 +654,8 @@ function initStyleDeclarationObserver( return setProperty.apply(this, arguments); }; - const removeProperty = (win as any).CSSStyleDeclaration.prototype.removeProperty; - (win as any).CSSStyleDeclaration.prototype.removeProperty = function ( + const removeProperty = win.CSSStyleDeclaration.prototype.removeProperty; + win.CSSStyleDeclaration.prototype.removeProperty = function ( this: CSSStyleDeclaration, property: string, ) { @@ -671,8 +675,8 @@ function initStyleDeclarationObserver( }; return () => { - (win as any).CSSStyleDeclaration.prototype.setProperty = setProperty; - (win as any).CSSStyleDeclaration.prototype.removeProperty = removeProperty; + win.CSSStyleDeclaration.prototype.setProperty = setProperty; + win.CSSStyleDeclaration.prototype.removeProperty = removeProperty; }; } @@ -704,23 +708,25 @@ function initMediaInteractionObserver( function initCanvasMutationObserver( cb: canvasMutationCallback, - win: Window, + win: IWindow, blockClass: blockClass, mirror: Mirror, ): listenerHandler { - const props = Object.getOwnPropertyNames((win as any).CanvasRenderingContext2D.prototype); + const props = Object.getOwnPropertyNames( + win.CanvasRenderingContext2D.prototype, + ); const handlers: listenerHandler[] = []; for (const prop of props) { try { if ( - typeof (win as any).CanvasRenderingContext2D.prototype[ + typeof win.CanvasRenderingContext2D.prototype[ prop as keyof CanvasRenderingContext2D ] !== 'function' ) { continue; } const restoreHandler = patch( - (win as any).CanvasRenderingContext2D.prototype, + win.CanvasRenderingContext2D.prototype, prop, function (original) { return function ( @@ -761,7 +767,7 @@ function initCanvasMutationObserver( handlers.push(restoreHandler); } catch { const hookHandler = hookSetter( - (win as any).CanvasRenderingContext2D.prototype, + win.CanvasRenderingContext2D.prototype, prop, { set(v) { @@ -782,19 +788,18 @@ function initCanvasMutationObserver( }; } -function initFontObserver( - cb: fontCallback, - doc: Document, -): listenerHandler { - const win = doc.defaultView; +function initFontObserver(cb: fontCallback, doc: Document): listenerHandler { + const win = doc.defaultView as IWindow; + if (!win) { + return () => {}; + } const handlers: listenerHandler[] = []; const fontMap = new WeakMap(); - const originalFontFace = (win as any).FontFace; - // tslint:disable-next-line: no-any - (win as any).FontFace = function FontFace( + const originalFontFace = win.FontFace; + win.FontFace = (function FontFace( family: string, source: string | ArrayBufferView, descriptors?: FontFaceDescriptors, @@ -811,7 +816,7 @@ function initFontObserver( JSON.stringify(Array.from(new Uint8Array(source as any))), }); return fontFace; - }; + } as unknown) as typeof FontFace; const restoreHandler = patch(doc.fonts, 'add', function (original) { return function (this: FontFaceSet, fontFace: FontFace) { @@ -827,8 +832,7 @@ function initFontObserver( }); handlers.push(() => { - // tslint:disable-next-line: no-any - (win as any).FontFace = originalFontFace; + win.FontFace = originalFontFace; }); handlers.push(restoreHandler); @@ -923,6 +927,11 @@ export function initObservers( o: observerParam, hooks: hooksParam = {}, ): listenerHandler { + const currentWindow = o.doc.defaultView; // basically document.window + if (!currentWindow) { + return () => {}; + } + mergeHooks(o, hooks); const mutationObserver = initMutationObserver( o.mutationCb, @@ -980,8 +989,6 @@ export function initObservers( o.mirror, ); - const currentWindow = o.doc.defaultView as Window; // basically document.window - const styleSheetObserver = initStyleSheetObserver( o.styleSheetRuleCb, currentWindow, @@ -994,20 +1001,21 @@ export function initObservers( ); const canvasMutationObserver = o.recordCanvas ? initCanvasMutationObserver( - o.canvasMutationCb, - currentWindow, - o.blockClass, - o.mirror, - ) : () => {}; - const fontObserver = o.collectFonts ? initFontObserver(o.fontCb, o.doc) : () => {}; + o.canvasMutationCb, + currentWindow, + o.blockClass, + o.mirror, + ) + : () => {}; + const fontObserver = o.collectFonts + ? initFontObserver(o.fontCb, o.doc) + : () => {}; // plugins const pluginHandlers: listenerHandler[] = []; for (const plugin of o.plugins) { - pluginHandlers.push(plugin.observer( - plugin.callback, - currentWindow, - plugin.options, - )); + pluginHandlers.push( + plugin.observer(plugin.callback, currentWindow, plugin.options), + ); } return () => { diff --git a/packages/rrweb/src/replay/index.ts b/packages/rrweb/src/replay/index.ts index 40e47267..cd54819d 100644 --- a/packages/rrweb/src/replay/index.ts +++ b/packages/rrweb/src/replay/index.ts @@ -38,6 +38,7 @@ import { styleAttributeValue, styleValueWithPriority, mouseMovePos, + IWindow, } from '../types'; import { createMirror, @@ -449,7 +450,7 @@ export class Replayer { this.iframe.contentDocument, ); - polyfill(this.iframe.contentWindow as Window & typeof globalThis); + polyfill(this.iframe.contentWindow as IWindow); } } diff --git a/packages/rrweb/src/types.ts b/packages/rrweb/src/types.ts index 74424567..4f881ecb 100644 --- a/packages/rrweb/src/types.ts +++ b/packages/rrweb/src/types.ts @@ -201,7 +201,7 @@ export type SamplingStrategy = Partial<{ export type RecordPlugin = { name: string; - observer: (cb: Function, win: Window, options: TOptions) => listenerHandler; + observer: (cb: Function, win: IWindow, options: TOptions) => listenerHandler; options: TOptions; }; @@ -598,3 +598,11 @@ export type ElementState = { }; export type KeepIframeSrcFn = (src: string) => boolean; + +declare global { + interface Window { + FontFace: typeof FontFace; + } +} + +export type IWindow = Window & typeof globalThis; diff --git a/packages/rrweb/src/utils.ts b/packages/rrweb/src/utils.ts index c61f7afd..8ae1635f 100644 --- a/packages/rrweb/src/utils.ts +++ b/packages/rrweb/src/utils.ts @@ -4,8 +4,6 @@ import { listenerHandler, hookResetter, blockClass, - eventWithTime, - EventType, IncrementalSource, addedNodeMutation, removedNodeMutation, @@ -15,6 +13,7 @@ import { scrollData, inputData, DocumentDimension, + IWindow, } from './types'; import { INode, @@ -27,7 +26,7 @@ import { export function on( type: string, fn: EventListenerOrEventListenerObject, - target: Document | Window = document, + target: Document | IWindow = document, ): listenerHandler { const options = { capture: true, passive: true }; target.addEventListener(type, fn, options);