improve type assertion, from #716

This commit is contained in:
Yanzhen Yu
2026-04-01 12:00:00 +08:00
parent 808823639f
commit f0f0106749
5 changed files with 74 additions and 58 deletions

View File

@@ -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;
}

View File

@@ -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<CanvasRenderingContext2D>(
(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<FontFace, fontParam>();
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 () => {

View File

@@ -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);
}
}

View File

@@ -201,7 +201,7 @@ export type SamplingStrategy = Partial<{
export type RecordPlugin<TOptions = unknown> = {
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;

View File

@@ -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);