Skip to content

Commit

Permalink
improve type assertion, from #716
Browse files Browse the repository at this point in the history
  • Loading branch information
Yuyz0112 committed Oct 1, 2021
1 parent 1037563 commit 53492c1
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 58 deletions.
8 changes: 4 additions & 4 deletions packages/rrweb/src/plugins/console/record/index.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -22,7 +22,7 @@ type LogRecordOptions = {
level?: LogLevel[];
lengthThreshold?: number;
stringifyOptions?: StringifyOptions;
logger?: Logger | string;
logger?: Logger | 'console';

This comment has been minimized.

Copy link
@eoghanmurray

eoghanmurray Oct 1, 2021

Contributor

I'm not sure what other Loggers were to be passed in here?
Like was there another logger in mind that needed monkey patching?
If that other logger is loaded/defined independently within each iframe then we'd need to use a string so we can do
each_window[logger_name].prototype

};

const defaultLogOptions: LogRecordOptions = {
Expand Down Expand Up @@ -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;
Expand All @@ -115,7 +115,7 @@ function initLogObserver(
}
let logger: Logger;
if (typeof loggerType === 'string') {
logger = (win as any)[loggerType];
logger = win[loggerType];
} else {
logger = loggerType;
}
Expand Down
106 changes: 57 additions & 49 deletions packages/rrweb/src/record/observer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down Expand Up @@ -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({
Expand All @@ -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({
Expand All @@ -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;
}
}

Expand Down Expand Up @@ -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;
Expand All @@ -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,
Expand All @@ -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,
) {
Expand All @@ -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;
};
}

Expand Down Expand Up @@ -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 (
Expand Down Expand Up @@ -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) {
Expand All @@ -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,
Expand All @@ -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) {
Expand All @@ -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);

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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 () => {
Expand Down
3 changes: 2 additions & 1 deletion packages/rrweb/src/replay/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
styleAttributeValue,
styleValueWithPriority,
mouseMovePos,
IWindow,
} from '../types';
import {
createMirror,
Expand Down Expand Up @@ -449,7 +450,7 @@ export class Replayer {
this.iframe.contentDocument,
);

polyfill(this.iframe.contentWindow as Window & typeof globalThis);
polyfill(this.iframe.contentWindow as IWindow);
}
}

Expand Down
10 changes: 9 additions & 1 deletion packages/rrweb/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};

Expand Down Expand Up @@ -598,3 +598,11 @@ export type ElementState = {
};

export type KeepIframeSrcFn = (src: string) => boolean;

declare global {
interface Window {
FontFace: typeof FontFace;

This comment has been minimized.

Copy link
@eoghanmurray

eoghanmurray Oct 1, 2021

Contributor

Wondering: why is FontFace needed here? Why not the other things we are accessing, like window.CSSStyleSheet?

This comment has been minimized.

Copy link
@Yuyz0112

Yuyz0112 Oct 1, 2021

Author Member

I think this is TS limitation, FontFace was defined on Global instead of Window

}
}

export type IWindow = Window & typeof globalThis;
5 changes: 2 additions & 3 deletions packages/rrweb/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import {
listenerHandler,
hookResetter,
blockClass,
eventWithTime,
EventType,
IncrementalSource,
addedNodeMutation,
removedNodeMutation,
Expand All @@ -15,6 +13,7 @@ import {
scrollData,
inputData,
DocumentDimension,
IWindow,
} from './types';
import {
INode,
Expand All @@ -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);
Expand Down

0 comments on commit 53492c1

Please sign in to comment.