impl #309 observe font face set changes
This commit is contained in:
@@ -58,6 +58,7 @@
|
||||
"typescript": "^3.9.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/css-font-loading-module": "0.0.4",
|
||||
"@xstate/fsm": "^1.4.0",
|
||||
"mitt": "^1.1.3",
|
||||
"pako": "^1.0.11",
|
||||
|
||||
@@ -95,7 +95,8 @@ function getCode(): string {
|
||||
window.__IS_RECORDING__ = true
|
||||
rrweb.record({
|
||||
emit: event => window._replLog(event),
|
||||
recordCanvas: true
|
||||
recordCanvas: true,
|
||||
collectFonts: true
|
||||
});
|
||||
`);
|
||||
page.on('framenavigated', async () => {
|
||||
@@ -105,7 +106,8 @@ function getCode(): string {
|
||||
window.__IS_RECORDING__ = true
|
||||
rrweb.record({
|
||||
emit: event => window._replLog(event),
|
||||
recordCanvas: true
|
||||
recordCanvas: true,
|
||||
collectFonts: true
|
||||
});
|
||||
`);
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ function record<T = eventWithTime>(
|
||||
sampling = {},
|
||||
mousemoveWait,
|
||||
recordCanvas = false,
|
||||
collectFonts = false,
|
||||
} = options;
|
||||
// runtime checks for user options
|
||||
if (!emit) {
|
||||
@@ -256,12 +257,23 @@ function record<T = eventWithTime>(
|
||||
},
|
||||
}),
|
||||
),
|
||||
fontCb: (p) =>
|
||||
wrappedEmit(
|
||||
wrapEvent({
|
||||
type: EventType.IncrementalSnapshot,
|
||||
data: {
|
||||
source: IncrementalSource.Font,
|
||||
...p,
|
||||
},
|
||||
}),
|
||||
),
|
||||
blockClass,
|
||||
ignoreClass,
|
||||
maskInputOptions,
|
||||
inlineStylesheet,
|
||||
sampling,
|
||||
recordCanvas,
|
||||
collectFonts,
|
||||
},
|
||||
hooks,
|
||||
),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { INode, MaskInputOptions } from 'rrweb-snapshot';
|
||||
import { FontFaceDescriptors, FontFaceSet } from 'css-font-loading-module';
|
||||
import {
|
||||
mirror,
|
||||
throttle,
|
||||
@@ -32,6 +33,8 @@ import {
|
||||
MediaInteractions,
|
||||
SamplingStrategy,
|
||||
canvasMutationCallback,
|
||||
fontCallback,
|
||||
fontParam,
|
||||
} from '../types';
|
||||
import MutationBuffer from './mutation';
|
||||
|
||||
@@ -432,6 +435,56 @@ function initCanvasMutationObserver(
|
||||
};
|
||||
}
|
||||
|
||||
function initFontObserver(cb: fontCallback): listenerHandler {
|
||||
const handlers: listenerHandler[] = [];
|
||||
|
||||
const fontMap = new WeakMap<FontFace, fontParam>();
|
||||
|
||||
const originalFontFace = FontFace;
|
||||
// tslint:disable-next-line: no-any
|
||||
(window as any).FontFace = function FontFace(
|
||||
family: string,
|
||||
source: string | ArrayBufferView,
|
||||
descriptors?: FontFaceDescriptors,
|
||||
) {
|
||||
const fontFace = new originalFontFace(family, source, descriptors);
|
||||
fontMap.set(fontFace, {
|
||||
family,
|
||||
buffer: typeof source !== 'string',
|
||||
descriptors,
|
||||
fontSource:
|
||||
typeof source === 'string'
|
||||
? source
|
||||
: // tslint:disable-next-line: no-any
|
||||
JSON.stringify(Array.from(new Uint8Array(source as any))),
|
||||
});
|
||||
return fontFace;
|
||||
};
|
||||
|
||||
const restoreHandler = patch(document.fonts, 'add', function (original) {
|
||||
return function (this: FontFaceSet, fontFace: FontFace) {
|
||||
setTimeout(() => {
|
||||
const p = fontMap.get(fontFace);
|
||||
if (p) {
|
||||
cb(p);
|
||||
fontMap.delete(fontFace);
|
||||
}
|
||||
}, 0);
|
||||
return original.apply(this, [fontFace]);
|
||||
};
|
||||
});
|
||||
|
||||
handlers.push(() => {
|
||||
// tslint:disable-next-line: no-any
|
||||
(window as any).FonFace = originalFontFace;
|
||||
});
|
||||
handlers.push(restoreHandler);
|
||||
|
||||
return () => {
|
||||
handlers.forEach((h) => h());
|
||||
};
|
||||
}
|
||||
|
||||
function mergeHooks(o: observerParam, hooks: hooksParam) {
|
||||
const {
|
||||
mutationCb,
|
||||
@@ -443,6 +496,7 @@ function mergeHooks(o: observerParam, hooks: hooksParam) {
|
||||
mediaInteractionCb,
|
||||
styleSheetRuleCb,
|
||||
canvasMutationCb,
|
||||
fontCb,
|
||||
} = o;
|
||||
o.mutationCb = (...p: Arguments<mutationCallBack>) => {
|
||||
if (hooks.mutation) {
|
||||
@@ -498,6 +552,12 @@ function mergeHooks(o: observerParam, hooks: hooksParam) {
|
||||
}
|
||||
canvasMutationCb(...p);
|
||||
};
|
||||
o.fontCb = (...p: Arguments<fontCallback>) => {
|
||||
if (hooks.font) {
|
||||
hooks.font(...p);
|
||||
}
|
||||
fontCb(...p);
|
||||
};
|
||||
}
|
||||
|
||||
export default function initObservers(
|
||||
@@ -539,6 +599,7 @@ export default function initObservers(
|
||||
const canvasMutationObserver = o.recordCanvas
|
||||
? initCanvasMutationObserver(o.canvasMutationCb, o.blockClass)
|
||||
: () => {};
|
||||
const fontObserver = o.collectFonts ? initFontObserver(o.fontCb) : () => {};
|
||||
|
||||
return () => {
|
||||
mutationObserver.disconnect();
|
||||
@@ -550,5 +611,6 @@ export default function initObservers(
|
||||
mediaInteractionHandler();
|
||||
styleSheetObserver();
|
||||
canvasMutationObserver();
|
||||
fontObserver();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -813,6 +813,22 @@ export class Replayer {
|
||||
} catch (error) {
|
||||
this.warnCanvasMutationFailed(d, d.id, error);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IncrementalSource.Font: {
|
||||
try {
|
||||
const fontFace = new FontFace(
|
||||
d.family,
|
||||
d.buffer ? new Uint8Array(JSON.parse(d.fontSource)) : d.fontSource,
|
||||
d.descriptors,
|
||||
);
|
||||
this.iframe.contentDocument?.fonts.add(fontFace);
|
||||
} catch (error) {
|
||||
if (this.config.showWarning) {
|
||||
console.warn(error);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
}
|
||||
|
||||
22
src/types.ts
22
src/types.ts
@@ -5,6 +5,7 @@ import {
|
||||
MaskInputOptions,
|
||||
} from 'rrweb-snapshot';
|
||||
import { PackFn, UnpackFn } from './packer/base';
|
||||
import { FontFaceDescriptors } from 'css-font-loading-module';
|
||||
|
||||
export enum EventType {
|
||||
DomContentLoaded,
|
||||
@@ -71,6 +72,7 @@ export enum IncrementalSource {
|
||||
MediaInteraction,
|
||||
StyleSheetRule,
|
||||
CanvasMutation,
|
||||
Font,
|
||||
}
|
||||
|
||||
export type mutationData = {
|
||||
@@ -111,6 +113,10 @@ export type canvasMutationData = {
|
||||
source: IncrementalSource.CanvasMutation;
|
||||
} & canvasMutationParam;
|
||||
|
||||
export type fontData = {
|
||||
source: IncrementalSource.Font;
|
||||
} & fontParam;
|
||||
|
||||
export type incrementalData =
|
||||
| mutationData
|
||||
| mousemoveData
|
||||
@@ -120,7 +126,8 @@ export type incrementalData =
|
||||
| inputData
|
||||
| mediaInteractionData
|
||||
| styleSheetRuleData
|
||||
| canvasMutationData;
|
||||
| canvasMutationData
|
||||
| fontData;
|
||||
|
||||
export type event =
|
||||
| domContentLoadedEvent
|
||||
@@ -172,6 +179,7 @@ export type recordOptions<T> = {
|
||||
packFn?: PackFn;
|
||||
sampling?: SamplingStrategy;
|
||||
recordCanvas?: boolean;
|
||||
collectFonts?: boolean;
|
||||
// departed, please use sampling options
|
||||
mousemoveWait?: number;
|
||||
};
|
||||
@@ -190,8 +198,10 @@ export type observerParam = {
|
||||
inlineStylesheet: boolean;
|
||||
styleSheetRuleCb: styleSheetRuleCallback;
|
||||
canvasMutationCb: canvasMutationCallback;
|
||||
fontCb: fontCallback;
|
||||
sampling: SamplingStrategy;
|
||||
recordCanvas: boolean;
|
||||
collectFonts: boolean;
|
||||
};
|
||||
|
||||
export type hooksParam = {
|
||||
@@ -204,6 +214,7 @@ export type hooksParam = {
|
||||
mediaInteaction?: mediaInteractionCallback;
|
||||
styleSheetRule?: styleSheetRuleCallback;
|
||||
canvasMutation?: canvasMutationCallback;
|
||||
font?: fontCallback;
|
||||
};
|
||||
|
||||
// https://dom.spec.whatwg.org/#interface-mutationrecord
|
||||
@@ -328,6 +339,15 @@ export type canvasMutationParam = {
|
||||
setter?: true;
|
||||
};
|
||||
|
||||
export type fontParam = {
|
||||
family: string;
|
||||
fontSource: string;
|
||||
buffer: boolean;
|
||||
descriptors?: FontFaceDescriptors;
|
||||
};
|
||||
|
||||
export type fontCallback = (p: fontParam) => void;
|
||||
|
||||
export type viewportResizeDimention = {
|
||||
width: number;
|
||||
height: number;
|
||||
|
||||
@@ -14,5 +14,9 @@
|
||||
},
|
||||
"compileOnSave": true,
|
||||
"exclude": ["test"],
|
||||
"include": ["src", "test.d.ts"]
|
||||
"include": [
|
||||
"src",
|
||||
"test.d.ts",
|
||||
"node_modules/@types/css-font-loading-module/index.d.ts"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user