Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8069055
Remove INode (`node.__sn`) and use Mirror as source of truth (#868)
Juice10 Apr 6, 2022
627d54f
Fix mutation edge case when blocked class gets unblocked (#867)
rahulrelicx Apr 15, 2022
1c851c9
Record canvas snapshots N times per second (#859)
Juice10 Apr 18, 2022
94e0007
Test: Stylesheet append text node (#886)
Juice10 May 5, 2022
b49f505
Fix for issue #890 (#891)
dkozlovskyi May 9, 2022
c3de806
Perf: Apply the latest text mutation only (#885)
Juice10 May 9, 2022
0e91e84
* rrdom: add a diff function for properties
Juice10 May 12, 2022
4711692
Introduce benchmark tests and improve snapshot attributes traversing …
Yuyz0112 May 14, 2022
dc64d49
Chore: Add issue/pr template and general housekeeping tools and docs …
Juice10 May 22, 2022
1d7fcbd
Fix #904 (#906)
Juice10 May 31, 2022
11cd5ca
inline stylesheets when loaded
Juice10 May 31, 2022
3514b4f
set empty link elements to loaded by default
Juice10 Jun 1, 2022
1dcd7ea
Clean up stylesheet manager
Juice10 Jun 1, 2022
a345326
Remove attribute mutation code
Juice10 Jun 2, 2022
e7e78fb
Bump minimist from 1.2.5 to 1.2.6 (#902)
dependabot[bot] Jun 5, 2022
e0215fe
Speed up snapshotting of many new dom nodes (#903)
Juice10 Jun 6, 2022
1e784f6
highlight fixes after merge
Vadman97 Jun 2, 2022
2637be9
cleanup script
Vadman97 Jun 9, 2022
2cc2b5f
bump versions
Vadman97 Jun 9, 2022
3df6929
update version dependencies
Vadman97 Jun 10, 2022
789073d
fix rebuild not restoring inlined images
Vadman97 Jun 10, 2022
5d0842f
inline stylesheets when loaded
Juice10 May 31, 2022
8d38f64
fixup! inline stylesheets when loaded
Vadman97 Jun 10, 2022
9c64602
avoid crash on rrdom diff issues
Vadman97 Jun 13, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
inline stylesheets when loaded
  • Loading branch information
Juice10 authored and Vadman97 committed Jun 2, 2022
commit 11cd5ca5a8a86ce1a6a9e8aff59708bf1c0d1b91
154 changes: 130 additions & 24 deletions packages/rrweb-snapshot/src/snapshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
MaskInputFn,
KeepIframeSrcFn,
ICanvas,
serializedElementNodeWithId,
} from './types';
import {
Mirror,
Expand Down Expand Up @@ -372,6 +373,44 @@ function onceIframeLoaded(
iframeEl.addEventListener('load', listener);
}

function isStylesheetLoaded(link: HTMLLinkElement) {
return link.sheet !== null;
}

function onceStylesheetLoaded(
link: HTMLLinkElement,
listener: () => unknown,
iframeLoadTimeout: number,
) {
let fired = false;
let styleSheetLoaded: StyleSheet | null;
try {
styleSheetLoaded = link.sheet;
} catch (error) {
return;
}

if (!styleSheetLoaded) {
const timer = setTimeout(() => {
if (!fired) {
listener();
fired = true;
}
}, iframeLoadTimeout);
link.addEventListener('load', () => {
clearTimeout(timer);
fired = true;
listener();
});
return;
}

// stylesheet was already loaded, make sure we wait to trigger the listener
// till _after_ the mutation that found this stylesheet has had time to process
setTimeout(listener, 0);
return;
}

function serializeNode(
n: Node,
options: {
Expand Down Expand Up @@ -466,7 +505,7 @@ function serializeNode(
});
let cssText: string | null = null;
if (stylesheet) {
cssText = getCssRulesString(stylesheet );
cssText = getCssRulesString(stylesheet);
}
if (cssText) {
delete attributes.rel;
Expand Down Expand Up @@ -845,9 +884,14 @@ export function serializeNodeWithId(
onSerialize?: (n: Node) => unknown;
onIframeLoad?: (
iframeNode: HTMLIFrameElement,
node: serializedNodeWithId,
node: serializedElementNodeWithId,
) => unknown;
iframeLoadTimeout?: number;
onStylesheetLoad?: (
linkNode: HTMLLinkElement,
node: serializedElementNodeWithId,
) => unknown;
stylesheetLoadTimeout?: number;
enableStrictPrivacy: boolean;
},
): serializedNodeWithId | null {
Expand All @@ -870,6 +914,8 @@ export function serializeNodeWithId(
onSerialize,
onIframeLoad,
iframeLoadTimeout = 5000,
onStylesheetLoad,
stylesheetLoadTimeout = 5000,
keepIframeSrcFn = () => false,
enableStrictPrivacy,
} = options;
Expand Down Expand Up @@ -973,6 +1019,8 @@ export function serializeNodeWithId(
onSerialize,
onIframeLoad,
iframeLoadTimeout,
onStylesheetLoad,
stylesheetLoadTimeout,
keepIframeSrcFn,
enableStrictPrivacy,
};
Expand Down Expand Up @@ -1032,14 +1080,64 @@ export function serializeNodeWithId(
});

if (serializedIframeNode) {
onIframeLoad(n as HTMLIFrameElement, serializedIframeNode);
onIframeLoad(
n as HTMLIFrameElement,
serializedIframeNode as serializedElementNodeWithId,
);
}
}
},
iframeLoadTimeout,
);
}

// <link rel=stylesheet href=...>
if (
serializedNode.type === NodeType.Element &&
serializedNode.tagName === 'link' &&
serializedNode.attributes.rel === 'stylesheet'
) {
onceStylesheetLoaded(
n as HTMLLinkElement,
() => {
if (onStylesheetLoad) {
const serializedLinkNode = serializeNodeWithId(n, {
doc,
mirror,
blockClass,
blockSelector,
maskTextClass,
maskTextSelector,
skipChild: false,
inlineStylesheet,
maskInputOptions,
maskTextFn,
maskInputFn,
slimDOMOptions,
dataURLOptions,
inlineImages,
recordCanvas,
preserveWhiteSpace,
onSerialize,
onIframeLoad,
onStylesheetLoad,
iframeLoadTimeout,
keepIframeSrcFn,
});

if (serializedLinkNode) {
onStylesheetLoad(
n as HTMLLinkElement,
serializedLinkNode as serializedElementNodeWithId,
);
}
}
},
stylesheetLoadTimeout,
);
if (isStylesheetLoaded(n as HTMLLinkElement) === false) return null; // add stylesheet in later mutation
}

return serializedNode;
}

Expand All @@ -1063,9 +1161,14 @@ function snapshot(
onSerialize?: (n: Node) => unknown;
onIframeLoad?: (
iframeNode: HTMLIFrameElement,
node: serializedNodeWithId,
node: serializedElementNodeWithId,
) => unknown;
iframeLoadTimeout?: number;
onStylesheetLoad?: (
linkNode: HTMLLinkElement,
node: serializedElementNodeWithId,
) => unknown;
stylesheetLoadTimeout?: number;
keepIframeSrcFn?: KeepIframeSrcFn;
enableStrictPrivacy: boolean;
},
Expand All @@ -1088,6 +1191,8 @@ function snapshot(
onSerialize,
onIframeLoad,
iframeLoadTimeout,
onStylesheetLoad,
stylesheetLoadTimeout,
keepIframeSrcFn = () => false,
enableStrictPrivacy = false,
} = options || {};
Expand Down Expand Up @@ -1137,26 +1242,27 @@ function snapshot(
return serializeNodeWithId(n, {
doc: n,
mirror,
blockClass,
blockSelector,
maskTextClass,
maskTextSelector,
skipChild: false,
inlineStylesheet,
maskInputOptions,
maskTextFn,
maskInputFn,
slimDOMOptions,
dataURLOptions,
inlineImages,
recordCanvas,
preserveWhiteSpace,
onSerialize,
onIframeLoad,
iframeLoadTimeout,
keepIframeSrcFn,
enableStrictPrivacy,
});
blockClass,
blockSelector,
maskTextClass,
maskTextSelector,
skipChild: false,
inlineStylesheet,
maskInputOptions,
maskTextFn,
maskInputFn,
slimDOMOptions,
dataURLOptions,
inlineImages,
recordCanvas,
preserveWhiteSpace,
onSerialize,
onIframeLoad,
iframeLoadTimeout,
onStylesheetLoad,
stylesheetLoadTimeout,keepIframeSrcFn,
enableStrictPrivacy,
});
}

export function visitSnapshot(
Expand Down
5 changes: 5 additions & 0 deletions packages/rrweb-snapshot/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ export type serializedNode = (

export type serializedNodeWithId = serializedNode & { id: number };

export type serializedElementNodeWithId = Extract<
serializedNodeWithId,
Record<'type', NodeType.Element>
>;

export type tagMap = {
[key: string]: string;
};
Expand Down
10 changes: 7 additions & 3 deletions packages/rrweb-snapshot/typings/snapshot.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { serializedNodeWithId, MaskInputOptions, SlimDOMOptions, DataURLOptions, MaskTextFn, MaskInputFn, KeepIframeSrcFn } from './types';
import { serializedNodeWithId, MaskInputOptions, SlimDOMOptions, DataURLOptions, MaskTextFn, MaskInputFn, KeepIframeSrcFn, serializedElementNodeWithId } from './types';
import { Mirror } from './utils';
export declare const IGNORED_NODE = -2;
export declare function absoluteToStylesheet(cssText: string | null, href: string): string;
Expand All @@ -25,8 +25,10 @@ export declare function serializeNodeWithId(n: Node, options: {
recordCanvas?: boolean;
preserveWhiteSpace?: boolean;
onSerialize?: (n: Node) => unknown;
onIframeLoad?: (iframeNode: HTMLIFrameElement, node: serializedNodeWithId) => unknown;
onIframeLoad?: (iframeNode: HTMLIFrameElement, node: serializedElementNodeWithId) => unknown;
iframeLoadTimeout?: number;
onStylesheetLoad?: (linkNode: HTMLLinkElement, node: serializedElementNodeWithId) => unknown;
stylesheetLoadTimeout?: number;
enableStrictPrivacy: boolean;
}): serializedNodeWithId | null;
declare function snapshot(n: Document, options?: {
Expand All @@ -45,8 +47,10 @@ declare function snapshot(n: Document, options?: {
recordCanvas?: boolean;
preserveWhiteSpace?: boolean;
onSerialize?: (n: Node) => unknown;
onIframeLoad?: (iframeNode: HTMLIFrameElement, node: serializedNodeWithId) => unknown;
onIframeLoad?: (iframeNode: HTMLIFrameElement, node: serializedElementNodeWithId) => unknown;
iframeLoadTimeout?: number;
onStylesheetLoad?: (linkNode: HTMLLinkElement, node: serializedElementNodeWithId) => unknown;
stylesheetLoadTimeout?: number;
keepIframeSrcFn?: KeepIframeSrcFn;
enableStrictPrivacy: boolean;
}): serializedNodeWithId | null;
Expand Down
1 change: 1 addition & 0 deletions packages/rrweb-snapshot/typings/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export declare type serializedNode = (documentNode | documentTypeNode | elementN
export declare type serializedNodeWithId = serializedNode & {
id: number;
};
export declare type serializedElementNodeWithId = Extract<serializedNodeWithId, Record<'type', NodeType.Element>>;
export declare type tagMap = {
[key: string]: string;
};
Expand Down
10 changes: 10 additions & 0 deletions packages/rrweb/src/record/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
import { IframeManager } from './iframe-manager';
import { ShadowDomManager } from './shadow-dom-manager';
import { CanvasManager } from './observers/canvas/canvas-manager';
import { StylesheetManager } from './stylesheet-manager';

function wrapEvent(e: event): eventWithTime {
return {
Expand Down Expand Up @@ -216,6 +217,10 @@ function record<T = eventWithTime>(
mutationCb: wrappedMutationEmit,
});

const stylesheetManager = new StylesheetManager({
mutationCb: wrappedMutationEmit,
});

const canvasManager = new CanvasManager({
recordCanvas,
mutationCb: wrappedCanvasMutationEmit,
Expand All @@ -242,6 +247,7 @@ function record<T = eventWithTime>(
sampling,
slimDOMOptions,
iframeManager,
stylesheetManager,
canvasManager,
enableStrictPrivacy
},
Expand Down Expand Up @@ -287,6 +293,9 @@ function record<T = eventWithTime>(
iframeManager.attachIframe(iframe, childSn, mirror);
shadowDomManager.observeAttachShadow(iframe);
},
onStylesheetLoad: (linkEl, childSn) => {
this.stylesheetManager.attachStylesheet(linkEl, childSn, this.mirror);
},
keepIframeSrcFn,
});

Expand Down Expand Up @@ -438,6 +447,7 @@ function record<T = eventWithTime>(
slimDOMOptions,
mirror,
iframeManager,
stylesheetManager,
shadowDomManager,
canvasManager,
enableStrictPrivacy,
Expand Down
5 changes: 5 additions & 0 deletions packages/rrweb/src/record/mutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ export default class MutationBuffer {
private doc: observerParam['doc'];
private mirror: observerParam['mirror'];
private iframeManager: observerParam['iframeManager'];
private stylesheetManager: observerParam['stylesheetManager'];
private shadowDomManager: observerParam['shadowDomManager'];
private canvasManager: observerParam['canvasManager'];
private enableStrictPrivacy: observerParam['enableStrictPrivacy'];
Expand All @@ -190,6 +191,7 @@ export default class MutationBuffer {
'doc',
'mirror',
'iframeManager',
'stylesheetManager',
'shadowDomManager',
'canvasManager',
'enableStrictPrivacy',
Expand Down Expand Up @@ -311,6 +313,9 @@ export default class MutationBuffer {
this.iframeManager.attachIframe(iframe, childSn, this.mirror);
this.shadowDomManager.observeAttachShadow(iframe);
},
onStylesheetLoad: (link, childSn) => {
this.stylesheetManager.attachStylesheet(link, childSn, this.mirror);
},
});
if (sn) {
adds.push({
Expand Down
30 changes: 30 additions & 0 deletions packages/rrweb/src/record/stylesheet-manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import type { Mirror, serializedNodeWithId } from 'rrweb-snapshot';
import type { mutationCallBack } from '../types';

export class StylesheetManager {
// private stylesheets: WeakMap<HTMLLinkElement, true> = new WeakMap();
private mutationCb: mutationCallBack;

constructor(options: { mutationCb: mutationCallBack }) {
this.mutationCb = options.mutationCb;
}

public attachStylesheet(
linkEl: HTMLLinkElement,
childSn: serializedNodeWithId,
mirror: Mirror,
) {
this.mutationCb({
adds: [
{
parentId: mirror.getId(linkEl),
nextId: null,
node: childSn,
},
],
removes: [],
texts: [],
attributes: [],
});
}
}
Loading