Skip to content

Commit d051b3c

Browse files
authored
feat: Add build flags to allow noop iframe/canvas/shadow dom managers (#114)
This PR adds 3 new build flags to rrweb: * `__RRWEB_EXCLUDE_CANVAS__` * `__RRWEB_EXCLUDE_SHADOW_DOM__` * `__RRWEB_EXCLUDE_IFRAME__` If you set these to `true` at build time, it will replace the regular `ShadowDomManager` / `CanvasManager` / `IframeManager` with a noop variant of these managers. All of these together shave off about 8 KB gzipped from our replay bundles, if set to `true`. For now, we'll probably keep this enabled by default, but at least we have a path for users to shake this out if they don't need these features. Note: I played with some other approaches, e.g. instead of having the noop class make these e.g. `iframeManager: IframeManager | undefined`, but I think overall the code to guard against using this everywhere ends up being a similar amount of bytes, plus we need to spread this much more through the codebase, making rebasing on upstream master etc. potentially harder. This way, IMHO it should be the easiest way to keep this as contained as possible.
1 parent a087e56 commit d051b3c

File tree

5 files changed

+177
-62
lines changed

5 files changed

+177
-62
lines changed

packages/rrweb/src/record/iframe-manager.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,37 @@ import type {
1212
} from '@sentry-internal/rrweb-types';
1313
import type { StylesheetManager } from './stylesheet-manager';
1414

15-
export class IframeManager {
15+
export interface IframeManagerInterface {
16+
crossOriginIframeMirror: CrossOriginIframeMirror;
17+
crossOriginIframeStyleMirror: CrossOriginIframeMirror;
18+
crossOriginIframeRootIdMap: WeakMap<HTMLIFrameElement, number>;
19+
20+
addIframe(iframeEl: HTMLIFrameElement): void;
21+
addLoadListener(cb: (iframeEl: HTMLIFrameElement) => unknown): void;
22+
attachIframe(
23+
iframeEl: HTMLIFrameElement,
24+
childSn: serializedNodeWithId,
25+
): void;
26+
}
27+
28+
export class IframeManagerNoop implements IframeManagerInterface {
29+
public crossOriginIframeMirror = new CrossOriginIframeMirror(genId);
30+
public crossOriginIframeStyleMirror: CrossOriginIframeMirror;
31+
public crossOriginIframeRootIdMap: WeakMap<HTMLIFrameElement, number> =
32+
new WeakMap();
33+
34+
public addIframe() {
35+
// noop
36+
}
37+
public addLoadListener() {
38+
// noop
39+
}
40+
public attachIframe() {
41+
// noop
42+
}
43+
}
44+
45+
export class IframeManager implements IframeManagerInterface {
1646
private iframes: WeakMap<HTMLIFrameElement, true> = new WeakMap();
1747
private crossOriginIframeMap: WeakMap<MessageEventSource, HTMLIFrameElement> =
1848
new WeakMap();

packages/rrweb/src/record/index.ts

Lines changed: 81 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,21 @@ import {
2929
adoptedStyleSheetParam,
3030
} from '@sentry-internal/rrweb-types';
3131
import type { CrossOriginIframeMessageEventContent } from '../types';
32-
import { IframeManager } from './iframe-manager';
33-
import { ShadowDomManager } from './shadow-dom-manager';
34-
import { CanvasManager } from './observers/canvas/canvas-manager';
32+
import {
33+
IframeManager,
34+
IframeManagerInterface,
35+
IframeManagerNoop,
36+
} from './iframe-manager';
37+
import {
38+
ShadowDomManager,
39+
ShadowDomManagerInterface,
40+
ShadowDomManagerNoop,
41+
} from './shadow-dom-manager';
42+
import {
43+
CanvasManager,
44+
CanvasManagerInterface,
45+
CanvasManagerNoop,
46+
} from './observers/canvas/canvas-manager';
3547
import { StylesheetManager } from './stylesheet-manager';
3648
import ProcessedNodeManager from './processed-node-manager';
3749
import {
@@ -47,10 +59,16 @@ function wrapEvent(e: event): eventWithTime {
4759
};
4860
}
4961

62+
declare global {
63+
const __RRWEB_EXCLUDE_CANVAS__: boolean;
64+
const __RRWEB_EXCLUDE_SHADOW_DOM__: boolean;
65+
const __RRWEB_EXCLUDE_IFRAME__: boolean;
66+
}
67+
5068
let wrappedEmit!: (e: eventWithTime, isCheckout?: boolean) => void;
5169

5270
let takeFullSnapshot!: (isCheckout?: boolean) => void;
53-
let canvasManager!: CanvasManager;
71+
let canvasManager: CanvasManagerInterface;
5472
let recording = false;
5573

5674
const mirror = createMirror();
@@ -291,13 +309,16 @@ function record<T = eventWithTime>(
291309
adoptedStyleSheetCb: wrappedAdoptedStyleSheetEmit,
292310
});
293311

294-
const iframeManager = new IframeManager({
295-
mirror,
296-
mutationCb: wrappedMutationEmit,
297-
stylesheetManager: stylesheetManager,
298-
recordCrossOriginIframes,
299-
wrappedEmit,
300-
});
312+
const iframeManager: IframeManagerInterface =
313+
typeof __RRWEB_EXCLUDE_IFRAME__ === 'boolean' && __RRWEB_EXCLUDE_IFRAME__
314+
? new IframeManagerNoop()
315+
: new IframeManager({
316+
mirror,
317+
mutationCb: wrappedMutationEmit,
318+
stylesheetManager: stylesheetManager,
319+
recordCrossOriginIframes,
320+
wrappedEmit,
321+
});
301322

302323
/**
303324
* Exposes mirror to the plugins
@@ -314,49 +335,56 @@ function record<T = eventWithTime>(
314335

315336
const processedNodeManager = new ProcessedNodeManager();
316337

317-
canvasManager = new CanvasManager({
318-
recordCanvas,
319-
mutationCb: wrappedCanvasMutationEmit,
320-
win: window,
321-
blockClass,
322-
blockSelector,
323-
unblockSelector,
324-
mirror,
325-
sampling: sampling.canvas,
326-
dataURLOptions,
327-
});
338+
canvasManager =
339+
typeof __RRWEB_EXCLUDE_CANVAS__ === 'boolean' && __RRWEB_EXCLUDE_CANVAS__
340+
? new CanvasManagerNoop()
341+
: new CanvasManager({
342+
recordCanvas,
343+
mutationCb: wrappedCanvasMutationEmit,
344+
win: window,
345+
blockClass,
346+
blockSelector,
347+
unblockSelector,
348+
mirror,
349+
sampling: sampling.canvas,
350+
dataURLOptions,
351+
});
328352

329-
const shadowDomManager = new ShadowDomManager({
330-
mutationCb: wrappedMutationEmit,
331-
scrollCb: wrappedScrollEmit,
332-
bypassOptions: {
333-
onMutation,
334-
blockClass,
335-
blockSelector,
336-
unblockSelector,
337-
maskAllText,
338-
maskTextClass,
339-
unmaskTextClass,
340-
maskTextSelector,
341-
unmaskTextSelector,
342-
inlineStylesheet,
343-
maskInputOptions,
344-
dataURLOptions,
345-
maskAttributeFn,
346-
maskTextFn,
347-
maskInputFn,
348-
recordCanvas,
349-
inlineImages,
350-
sampling,
351-
slimDOMOptions,
352-
iframeManager,
353-
stylesheetManager,
354-
canvasManager,
355-
keepIframeSrcFn,
356-
processedNodeManager,
357-
},
358-
mirror,
359-
});
353+
const shadowDomManager: ShadowDomManagerInterface =
354+
typeof __RRWEB_EXCLUDE_SHADOW_DOM__ === 'boolean' &&
355+
__RRWEB_EXCLUDE_SHADOW_DOM__
356+
? new ShadowDomManagerNoop()
357+
: new ShadowDomManager({
358+
mutationCb: wrappedMutationEmit,
359+
scrollCb: wrappedScrollEmit,
360+
bypassOptions: {
361+
onMutation,
362+
blockClass,
363+
blockSelector,
364+
unblockSelector,
365+
maskAllText,
366+
maskTextClass,
367+
unmaskTextClass,
368+
maskTextSelector,
369+
unmaskTextSelector,
370+
inlineStylesheet,
371+
maskInputOptions,
372+
dataURLOptions,
373+
maskAttributeFn,
374+
maskTextFn,
375+
maskInputFn,
376+
recordCanvas,
377+
inlineImages,
378+
sampling,
379+
slimDOMOptions,
380+
iframeManager,
381+
stylesheetManager,
382+
canvasManager,
383+
keepIframeSrcFn,
384+
processedNodeManager,
385+
},
386+
mirror,
387+
});
360388

361389
takeFullSnapshot = (isCheckout = false) => {
362390
wrappedEmit(

packages/rrweb/src/record/observers/canvas/canvas-manager.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,33 @@ type pendingCanvasMutationsMap = Map<
2828
canvasMutationWithType[]
2929
>;
3030

31-
export class CanvasManager {
31+
export interface CanvasManagerInterface {
32+
reset(): void;
33+
freeze(): void;
34+
unfreeze(): void;
35+
lock(): void;
36+
unlock(): void;
37+
}
38+
39+
export class CanvasManagerNoop implements CanvasManagerInterface {
40+
public reset() {
41+
// noop
42+
}
43+
public freeze() {
44+
// noop
45+
}
46+
public unfreeze() {
47+
// noop
48+
}
49+
public lock() {
50+
// noop
51+
}
52+
public unlock() {
53+
// noop
54+
}
55+
}
56+
57+
export class CanvasManager implements CanvasManagerInterface {
3258
private pendingCanvasMutations: pendingCanvasMutationsMap = new Map();
3359
private rafStamps: RafStamps = { latestId: 0, invokeId: null };
3460
private mirror: Mirror;

packages/rrweb/src/record/shadow-dom-manager.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,29 @@ type BypassOptions = Omit<
2020
sampling: SamplingStrategy;
2121
};
2222

23-
export class ShadowDomManager {
23+
export interface ShadowDomManagerInterface {
24+
init(): void;
25+
addShadowRoot(shadowRoot: ShadowRoot, doc: Document): void;
26+
observeAttachShadow(iframeElement: HTMLIFrameElement): void;
27+
reset(): void;
28+
}
29+
30+
export class ShadowDomManagerNoop implements ShadowDomManagerInterface {
31+
public init() {
32+
// noop
33+
}
34+
public addShadowRoot() {
35+
// noop
36+
}
37+
public observeAttachShadow() {
38+
// noop
39+
}
40+
public reset() {
41+
// noop
42+
}
43+
}
44+
45+
export class ShadowDomManager implements ShadowDomManagerInterface {
2446
private shadowDoms = new WeakSet<ShadowRoot>();
2547
private mutationCb: mutationCallBack;
2648
private scrollCb: scrollCallback;

packages/rrweb/src/types.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,20 @@ import type {
88
MaskAttributeFn,
99
} from '@sentry-internal/rrweb-snapshot';
1010
import type { PackFn, UnpackFn } from './packer/base';
11-
import type { IframeManager } from './record/iframe-manager';
12-
import type { ShadowDomManager } from './record/shadow-dom-manager';
11+
import type {
12+
IframeManager,
13+
IframeManagerInterface,
14+
} from './record/iframe-manager';
15+
import type {
16+
ShadowDomManager,
17+
ShadowDomManagerInterface,
18+
} from './record/shadow-dom-manager';
1319
import type { Replayer } from './replay';
1420
import type { RRNode } from '@sentry-internal/rrdom';
15-
import type { CanvasManager } from './record/observers/canvas/canvas-manager';
21+
import type {
22+
CanvasManager,
23+
CanvasManagerInterface,
24+
} from './record/observers/canvas/canvas-manager';
1625
import type { StylesheetManager } from './record/stylesheet-manager';
1726
import type {
1827
addedNodeMutation,
@@ -122,10 +131,10 @@ export type observerParam = {
122131
dataURLOptions: DataURLOptions;
123132
doc: Document;
124133
mirror: Mirror;
125-
iframeManager: IframeManager;
134+
iframeManager: IframeManagerInterface;
126135
stylesheetManager: StylesheetManager;
127-
shadowDomManager: ShadowDomManager;
128-
canvasManager: CanvasManager;
136+
shadowDomManager: ShadowDomManagerInterface;
137+
canvasManager: CanvasManagerInterface;
129138
processedNodeManager: ProcessedNodeManager;
130139
ignoreCSSAttributes: Set<string>;
131140
plugins: Array<{

0 commit comments

Comments
 (0)