Skip to content

Commit f022a0e

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 9cc865b commit f022a0e

5 files changed

Lines changed: 178 additions & 63 deletions

File tree

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

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

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

packages/rrweb/src/record/index.ts

Lines changed: 82 additions & 54 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 {
@@ -40,10 +52,16 @@ import {
4052
unregisterErrorHandler,
4153
} from './error-handler';
4254

43-
let wrappedEmit!: (e: eventWithoutTime, isCheckout?: boolean) => void;
55+
declare global {
56+
const __RRWEB_EXCLUDE_CANVAS__: boolean;
57+
const __RRWEB_EXCLUDE_SHADOW_DOM__: boolean;
58+
const __RRWEB_EXCLUDE_IFRAME__: boolean;
59+
}
60+
61+
let wrappedEmit!: (e: eventWithTime, isCheckout?: boolean) => void;
4462

4563
let takeFullSnapshot!: (isCheckout?: boolean) => void;
46-
let canvasManager!: CanvasManager;
64+
let canvasManager: CanvasManagerInterface;
4765
let recording = false;
4866

4967
const mirror = createMirror();
@@ -279,13 +297,16 @@ function record<T = eventWithTime>(
279297
adoptedStyleSheetCb: wrappedAdoptedStyleSheetEmit,
280298
});
281299

282-
const iframeManager = new IframeManager({
283-
mirror,
284-
mutationCb: wrappedMutationEmit,
285-
stylesheetManager: stylesheetManager,
286-
recordCrossOriginIframes,
287-
wrappedEmit,
288-
});
300+
const iframeManager: IframeManagerInterface =
301+
typeof __RRWEB_EXCLUDE_IFRAME__ === 'boolean' && __RRWEB_EXCLUDE_IFRAME__
302+
? new IframeManagerNoop()
303+
: new IframeManager({
304+
mirror,
305+
mutationCb: wrappedMutationEmit,
306+
stylesheetManager: stylesheetManager,
307+
recordCrossOriginIframes,
308+
wrappedEmit,
309+
});
289310

290311
/**
291312
* Exposes mirror to the plugins
@@ -302,49 +323,56 @@ function record<T = eventWithTime>(
302323

303324
const processedNodeManager = new ProcessedNodeManager();
304325

305-
canvasManager = new CanvasManager({
306-
recordCanvas,
307-
mutationCb: wrappedCanvasMutationEmit,
308-
win: window,
309-
blockClass,
310-
blockSelector,
311-
unblockSelector,
312-
mirror,
313-
sampling: sampling.canvas,
314-
dataURLOptions,
315-
});
326+
canvasManager =
327+
typeof __RRWEB_EXCLUDE_CANVAS__ === 'boolean' && __RRWEB_EXCLUDE_CANVAS__
328+
? new CanvasManagerNoop()
329+
: new CanvasManager({
330+
recordCanvas,
331+
mutationCb: wrappedCanvasMutationEmit,
332+
win: window,
333+
blockClass,
334+
blockSelector,
335+
unblockSelector,
336+
mirror,
337+
sampling: sampling.canvas,
338+
dataURLOptions,
339+
});
316340

317-
const shadowDomManager = new ShadowDomManager({
318-
mutationCb: wrappedMutationEmit,
319-
scrollCb: wrappedScrollEmit,
320-
bypassOptions: {
321-
onMutation,
322-
blockClass,
323-
blockSelector,
324-
unblockSelector,
325-
maskAllText,
326-
maskTextClass,
327-
unmaskTextClass,
328-
maskTextSelector,
329-
unmaskTextSelector,
330-
inlineStylesheet,
331-
maskInputOptions,
332-
dataURLOptions,
333-
maskAttributeFn,
334-
maskTextFn,
335-
maskInputFn,
336-
recordCanvas,
337-
inlineImages,
338-
sampling,
339-
slimDOMOptions,
340-
iframeManager,
341-
stylesheetManager,
342-
canvasManager,
343-
keepIframeSrcFn,
344-
processedNodeManager,
345-
},
346-
mirror,
347-
});
341+
const shadowDomManager: ShadowDomManagerInterface =
342+
typeof __RRWEB_EXCLUDE_SHADOW_DOM__ === 'boolean' &&
343+
__RRWEB_EXCLUDE_SHADOW_DOM__
344+
? new ShadowDomManagerNoop()
345+
: new ShadowDomManager({
346+
mutationCb: wrappedMutationEmit,
347+
scrollCb: wrappedScrollEmit,
348+
bypassOptions: {
349+
onMutation,
350+
blockClass,
351+
blockSelector,
352+
unblockSelector,
353+
maskAllText,
354+
maskTextClass,
355+
unmaskTextClass,
356+
maskTextSelector,
357+
unmaskTextSelector,
358+
inlineStylesheet,
359+
maskInputOptions,
360+
dataURLOptions,
361+
maskAttributeFn,
362+
maskTextFn,
363+
maskInputFn,
364+
recordCanvas,
365+
inlineImages,
366+
sampling,
367+
slimDOMOptions,
368+
iframeManager,
369+
stylesheetManager,
370+
canvasManager,
371+
keepIframeSrcFn,
372+
processedNodeManager,
373+
},
374+
mirror,
375+
});
348376

349377
takeFullSnapshot = (isCheckout = false) => {
350378
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)