Skip to content

Commit 4a28623

Browse files
mydeabillyvg
authored andcommitted
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 fc7e2bb commit 4a28623

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();
@@ -292,13 +310,16 @@ function record<T = eventWithTime>(
292310
adoptedStyleSheetCb: wrappedAdoptedStyleSheetEmit,
293311
});
294312

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

303324
/**
304325
* Exposes mirror to the plugins
@@ -315,49 +336,56 @@ function record<T = eventWithTime>(
315336

316337
const processedNodeManager = new ProcessedNodeManager();
317338

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

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

362390
takeFullSnapshot = (isCheckout = false) => {
363391
if (!recordDOM) {

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,
@@ -124,10 +133,10 @@ export type observerParam = {
124133
dataURLOptions: DataURLOptions;
125134
doc: Document;
126135
mirror: Mirror;
127-
iframeManager: IframeManager;
136+
iframeManager: IframeManagerInterface;
128137
stylesheetManager: StylesheetManager;
129-
shadowDomManager: ShadowDomManager;
130-
canvasManager: CanvasManager;
138+
shadowDomManager: ShadowDomManagerInterface;
139+
canvasManager: CanvasManagerInterface;
131140
processedNodeManager: ProcessedNodeManager;
132141
ignoreCSSAttributes: Set<string>;
133142
plugins: Array<{

0 commit comments

Comments
 (0)