improve type assertion, from #716

This commit is contained in:
Yanzhen Yu
2021-10-01 15:27:45 +08:00
parent 10375632ec
commit 53492c1ee4
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 { patch } from '../../../utils';
import { ErrorStackParser, StackFrame } from './error-stack-parser'; import { ErrorStackParser, StackFrame } from './error-stack-parser';
import { stringify } from './stringify'; import { stringify } from './stringify';
@@ -22,7 +22,7 @@ type LogRecordOptions = {
level?: LogLevel[]; level?: LogLevel[];
lengthThreshold?: number; lengthThreshold?: number;
stringifyOptions?: StringifyOptions; stringifyOptions?: StringifyOptions;
logger?: Logger | string; logger?: Logger | 'console';
}; };
const defaultLogOptions: LogRecordOptions = { const defaultLogOptions: LogRecordOptions = {
@@ -106,7 +106,7 @@ export type Logger = {
function initLogObserver( function initLogObserver(
cb: logCallback, cb: logCallback,
win: Window, // top window or in an iframe win: IWindow, // top window or in an iframe
logOptions: LogRecordOptions, logOptions: LogRecordOptions,
): listenerHandler { ): listenerHandler {
const loggerType = logOptions.logger; const loggerType = logOptions.logger;
@@ -115,7 +115,7 @@ function initLogObserver(
} }
let logger: Logger; let logger: Logger;
if (typeof loggerType === 'string') { if (typeof loggerType === 'string') {
logger = (win as any)[loggerType]; logger = win[loggerType];
} else { } else {
logger = loggerType; logger = loggerType;
} }

View File

@@ -44,15 +44,16 @@ import {
fontParam, fontParam,
Mirror, Mirror,
styleDeclarationCallback, styleDeclarationCallback,
IWindow,
} from '../types'; } from '../types';
import MutationBuffer from './mutation'; import MutationBuffer from './mutation';
import { IframeManager } from './iframe-manager'; import { IframeManager } from './iframe-manager';
import { ShadowDomManager } from './shadow-dom-manager'; import { ShadowDomManager } from './shadow-dom-manager';
type WindowWithStoredMutationObserver = Window & { type WindowWithStoredMutationObserver = IWindow & {
__rrMutationObserver?: MutationObserver; __rrMutationObserver?: MutationObserver;
}; };
type WindowWithAngularZone = Window & { type WindowWithAngularZone = IWindow & {
Zone?: { Zone?: {
__symbol__?: (key: string) => string; __symbol__?: (key: string) => string;
}; };
@@ -519,11 +520,14 @@ function getNestedCSSRulePositions(rule: CSSRule): number[] {
function initStyleSheetObserver( function initStyleSheetObserver(
cb: styleSheetRuleCallback, cb: styleSheetRuleCallback,
win: Window, win: IWindow,
mirror: Mirror, mirror: Mirror,
): listenerHandler { ): listenerHandler {
const insertRule = (win as any).CSSStyleSheet.prototype.insertRule; const insertRule = win.CSSStyleSheet.prototype.insertRule;
(win as any).CSSStyleSheet.prototype.insertRule = function (rule: string, index?: number) { win.CSSStyleSheet.prototype.insertRule = function (
rule: string,
index?: number,
) {
const id = mirror.getId(this.ownerNode as INode); const id = mirror.getId(this.ownerNode as INode);
if (id !== -1) { if (id !== -1) {
cb({ cb({
@@ -534,8 +538,8 @@ function initStyleSheetObserver(
return insertRule.apply(this, arguments); return insertRule.apply(this, arguments);
}; };
const deleteRule = (win as any).CSSStyleSheet.prototype.deleteRule; const deleteRule = win.CSSStyleSheet.prototype.deleteRule;
(win as any).CSSStyleSheet.prototype.deleteRule = function (index: number) { win.CSSStyleSheet.prototype.deleteRule = function (index: number) {
const id = mirror.getId(this.ownerNode as INode); const id = mirror.getId(this.ownerNode as INode);
if (id !== -1) { if (id !== -1) {
cb({ cb({
@@ -550,20 +554,20 @@ function initStyleSheetObserver(
[key: string]: GroupingCSSRuleTypes; [key: string]: GroupingCSSRuleTypes;
} = {}; } = {};
if (isCSSGroupingRuleSupported) { if (isCSSGroupingRuleSupported) {
supportedNestedCSSRuleTypes['CSSGroupingRule'] = (win as any).CSSGroupingRule; supportedNestedCSSRuleTypes.CSSGroupingRule = win.CSSGroupingRule;
} else { } else {
// Some browsers (Safari) don't support CSSGroupingRule // Some browsers (Safari) don't support CSSGroupingRule
// https://caniuse.com/?search=cssgroupingrule // https://caniuse.com/?search=cssgroupingrule
// fall back to monkey patching classes that would have inherited from CSSGroupingRule // fall back to monkey patching classes that would have inherited from CSSGroupingRule
if (isCSSMediaRuleSupported) { if (isCSSMediaRuleSupported) {
supportedNestedCSSRuleTypes['CSSMediaRule'] = (win as any).CSSMediaRule; supportedNestedCSSRuleTypes.CSSMediaRule = win.CSSMediaRule;
} }
if (isCSSConditionRuleSupported) { if (isCSSConditionRuleSupported) {
supportedNestedCSSRuleTypes['CSSConditionRule'] = (win as any).CSSConditionRule; supportedNestedCSSRuleTypes.CSSConditionRule = win.CSSConditionRule;
} }
if (isCSSSupportsRuleSupported) { if (isCSSSupportsRuleSupported) {
supportedNestedCSSRuleTypes['CSSSupportsRule'] = (win as any).CSSSupportsRule; supportedNestedCSSRuleTypes.CSSSupportsRule = win.CSSSupportsRule;
} }
} }
@@ -612,8 +616,8 @@ function initStyleSheetObserver(
}); });
return () => { return () => {
(win as any).CSSStyleSheet.prototype.insertRule = insertRule; win.CSSStyleSheet.prototype.insertRule = insertRule;
(win as any).CSSStyleSheet.prototype.deleteRule = deleteRule; win.CSSStyleSheet.prototype.deleteRule = deleteRule;
Object.entries(supportedNestedCSSRuleTypes).forEach(([typeKey, type]) => { Object.entries(supportedNestedCSSRuleTypes).forEach(([typeKey, type]) => {
type.prototype.insertRule = unmodifiedFunctions[typeKey].insertRule; type.prototype.insertRule = unmodifiedFunctions[typeKey].insertRule;
type.prototype.deleteRule = unmodifiedFunctions[typeKey].deleteRule; type.prototype.deleteRule = unmodifiedFunctions[typeKey].deleteRule;
@@ -623,11 +627,11 @@ function initStyleSheetObserver(
function initStyleDeclarationObserver( function initStyleDeclarationObserver(
cb: styleDeclarationCallback, cb: styleDeclarationCallback,
win: Window, win: IWindow,
mirror: Mirror, mirror: Mirror,
): listenerHandler { ): listenerHandler {
const setProperty = (win as any).CSSStyleDeclaration.prototype.setProperty; const setProperty = win.CSSStyleDeclaration.prototype.setProperty;
(win as any).CSSStyleDeclaration.prototype.setProperty = function ( win.CSSStyleDeclaration.prototype.setProperty = function (
this: CSSStyleDeclaration, this: CSSStyleDeclaration,
property: string, property: string,
value: string, value: string,
@@ -650,8 +654,8 @@ function initStyleDeclarationObserver(
return setProperty.apply(this, arguments); return setProperty.apply(this, arguments);
}; };
const removeProperty = (win as any).CSSStyleDeclaration.prototype.removeProperty; const removeProperty = win.CSSStyleDeclaration.prototype.removeProperty;
(win as any).CSSStyleDeclaration.prototype.removeProperty = function ( win.CSSStyleDeclaration.prototype.removeProperty = function (
this: CSSStyleDeclaration, this: CSSStyleDeclaration,
property: string, property: string,
) { ) {
@@ -671,8 +675,8 @@ function initStyleDeclarationObserver(
}; };
return () => { return () => {
(win as any).CSSStyleDeclaration.prototype.setProperty = setProperty; win.CSSStyleDeclaration.prototype.setProperty = setProperty;
(win as any).CSSStyleDeclaration.prototype.removeProperty = removeProperty; win.CSSStyleDeclaration.prototype.removeProperty = removeProperty;
}; };
} }
@@ -704,23 +708,25 @@ function initMediaInteractionObserver(
function initCanvasMutationObserver( function initCanvasMutationObserver(
cb: canvasMutationCallback, cb: canvasMutationCallback,
win: Window, win: IWindow,
blockClass: blockClass, blockClass: blockClass,
mirror: Mirror, mirror: Mirror,
): listenerHandler { ): listenerHandler {
const props = Object.getOwnPropertyNames((win as any).CanvasRenderingContext2D.prototype); const props = Object.getOwnPropertyNames(
win.CanvasRenderingContext2D.prototype,
);
const handlers: listenerHandler[] = []; const handlers: listenerHandler[] = [];
for (const prop of props) { for (const prop of props) {
try { try {
if ( if (
typeof (win as any).CanvasRenderingContext2D.prototype[ typeof win.CanvasRenderingContext2D.prototype[
prop as keyof CanvasRenderingContext2D prop as keyof CanvasRenderingContext2D
] !== 'function' ] !== 'function'
) { ) {
continue; continue;
} }
const restoreHandler = patch( const restoreHandler = patch(
(win as any).CanvasRenderingContext2D.prototype, win.CanvasRenderingContext2D.prototype,
prop, prop,
function (original) { function (original) {
return function ( return function (
@@ -761,7 +767,7 @@ function initCanvasMutationObserver(
handlers.push(restoreHandler); handlers.push(restoreHandler);
} catch { } catch {
const hookHandler = hookSetter<CanvasRenderingContext2D>( const hookHandler = hookSetter<CanvasRenderingContext2D>(
(win as any).CanvasRenderingContext2D.prototype, win.CanvasRenderingContext2D.prototype,
prop, prop,
{ {
set(v) { set(v) {
@@ -782,19 +788,18 @@ function initCanvasMutationObserver(
}; };
} }
function initFontObserver( function initFontObserver(cb: fontCallback, doc: Document): listenerHandler {
cb: fontCallback, const win = doc.defaultView as IWindow;
doc: Document, if (!win) {
): listenerHandler { return () => {};
const win = doc.defaultView; }
const handlers: listenerHandler[] = []; const handlers: listenerHandler[] = [];
const fontMap = new WeakMap<FontFace, fontParam>(); const fontMap = new WeakMap<FontFace, fontParam>();
const originalFontFace = (win as any).FontFace; const originalFontFace = win.FontFace;
// tslint:disable-next-line: no-any win.FontFace = (function FontFace(
(win as any).FontFace = function FontFace(
family: string, family: string,
source: string | ArrayBufferView, source: string | ArrayBufferView,
descriptors?: FontFaceDescriptors, descriptors?: FontFaceDescriptors,
@@ -811,7 +816,7 @@ function initFontObserver(
JSON.stringify(Array.from(new Uint8Array(source as any))), JSON.stringify(Array.from(new Uint8Array(source as any))),
}); });
return fontFace; return fontFace;
}; } as unknown) as typeof FontFace;
const restoreHandler = patch(doc.fonts, 'add', function (original) { const restoreHandler = patch(doc.fonts, 'add', function (original) {
return function (this: FontFaceSet, fontFace: FontFace) { return function (this: FontFaceSet, fontFace: FontFace) {
@@ -827,8 +832,7 @@ function initFontObserver(
}); });
handlers.push(() => { handlers.push(() => {
// tslint:disable-next-line: no-any win.FontFace = originalFontFace;
(win as any).FontFace = originalFontFace;
}); });
handlers.push(restoreHandler); handlers.push(restoreHandler);
@@ -923,6 +927,11 @@ export function initObservers(
o: observerParam, o: observerParam,
hooks: hooksParam = {}, hooks: hooksParam = {},
): listenerHandler { ): listenerHandler {
const currentWindow = o.doc.defaultView; // basically document.window
if (!currentWindow) {
return () => {};
}
mergeHooks(o, hooks); mergeHooks(o, hooks);
const mutationObserver = initMutationObserver( const mutationObserver = initMutationObserver(
o.mutationCb, o.mutationCb,
@@ -980,8 +989,6 @@ export function initObservers(
o.mirror, o.mirror,
); );
const currentWindow = o.doc.defaultView as Window; // basically document.window
const styleSheetObserver = initStyleSheetObserver( const styleSheetObserver = initStyleSheetObserver(
o.styleSheetRuleCb, o.styleSheetRuleCb,
currentWindow, currentWindow,
@@ -994,20 +1001,21 @@ export function initObservers(
); );
const canvasMutationObserver = o.recordCanvas const canvasMutationObserver = o.recordCanvas
? initCanvasMutationObserver( ? initCanvasMutationObserver(
o.canvasMutationCb, o.canvasMutationCb,
currentWindow, currentWindow,
o.blockClass, o.blockClass,
o.mirror, o.mirror,
) : () => {}; )
const fontObserver = o.collectFonts ? initFontObserver(o.fontCb, o.doc) : () => {}; : () => {};
const fontObserver = o.collectFonts
? initFontObserver(o.fontCb, o.doc)
: () => {};
// plugins // plugins
const pluginHandlers: listenerHandler[] = []; const pluginHandlers: listenerHandler[] = [];
for (const plugin of o.plugins) { for (const plugin of o.plugins) {
pluginHandlers.push(plugin.observer( pluginHandlers.push(
plugin.callback, plugin.observer(plugin.callback, currentWindow, plugin.options),
currentWindow, );
plugin.options,
));
} }
return () => { return () => {

View File

@@ -38,6 +38,7 @@ import {
styleAttributeValue, styleAttributeValue,
styleValueWithPriority, styleValueWithPriority,
mouseMovePos, mouseMovePos,
IWindow,
} from '../types'; } from '../types';
import { import {
createMirror, createMirror,
@@ -449,7 +450,7 @@ export class Replayer {
this.iframe.contentDocument, 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> = { export type RecordPlugin<TOptions = unknown> = {
name: string; name: string;
observer: (cb: Function, win: Window, options: TOptions) => listenerHandler; observer: (cb: Function, win: IWindow, options: TOptions) => listenerHandler;
options: TOptions; options: TOptions;
}; };
@@ -598,3 +598,11 @@ export type ElementState = {
}; };
export type KeepIframeSrcFn = (src: string) => boolean; 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, listenerHandler,
hookResetter, hookResetter,
blockClass, blockClass,
eventWithTime,
EventType,
IncrementalSource, IncrementalSource,
addedNodeMutation, addedNodeMutation,
removedNodeMutation, removedNodeMutation,
@@ -15,6 +13,7 @@ import {
scrollData, scrollData,
inputData, inputData,
DocumentDimension, DocumentDimension,
IWindow,
} from './types'; } from './types';
import { import {
INode, INode,
@@ -27,7 +26,7 @@ import {
export function on( export function on(
type: string, type: string,
fn: EventListenerOrEventListenerObject, fn: EventListenerOrEventListenerObject,
target: Document | Window = document, target: Document | IWindow = document,
): listenerHandler { ): listenerHandler {
const options = { capture: true, passive: true }; const options = { capture: true, passive: true };
target.addEventListener(type, fn, options); target.addEventListener(type, fn, options);