Skip to content

Commit

Permalink
support inlining remove video elements using the canvas manager
Browse files Browse the repository at this point in the history
  • Loading branch information
Vadman97 committed Feb 21, 2025
1 parent 2701b9d commit 51addd9
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 34 deletions.
71 changes: 49 additions & 22 deletions packages/rrweb-snapshot/src/snapshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,7 @@ function serializeNode(
maskInputFn: MaskInputFn | undefined;
dataURLOptions?: DataURLOptions;
inlineImages: boolean;
inlineVideos: boolean;
recordCanvas: boolean;
keepIframeSrcFn: KeepIframeSrcFn;
/**
Expand All @@ -426,6 +427,7 @@ function serializeNode(
maskInputFn,
dataURLOptions = {},
inlineImages,
inlineVideos,
recordCanvas,
keepIframeSrcFn,
newlyAddedElement = false,
Expand Down Expand Up @@ -466,6 +468,7 @@ function serializeNode(
maskTextClass,
dataURLOptions,
inlineImages,
inlineVideos,
recordCanvas,
keepIframeSrcFn,
newlyAddedElement,
Expand Down Expand Up @@ -596,6 +599,7 @@ function serializeElementNode(
maskTextClass: string | RegExp;
dataURLOptions?: DataURLOptions;
inlineImages: boolean;
inlineVideos: boolean;
recordCanvas: boolean;
keepIframeSrcFn: KeepIframeSrcFn;
/**
Expand All @@ -616,6 +620,7 @@ function serializeElementNode(
maskTextClass,
dataURLOptions = {},
inlineImages,
inlineVideos,
recordCanvas,
keepIframeSrcFn,
newlyAddedElement = false,
Expand Down Expand Up @@ -854,30 +859,43 @@ function serializeElementNode(
// In case old browsers don't support customElements
}

if (inlineImages && tagName === 'video') {
const video = n as HTMLVideoElement;
if (video.src === '' || video.src.indexOf('blob:') !== -1) {
const { width, height } = n.getBoundingClientRect();
attributes = {
width,
height,
rr_width: `${width}px`,
rr_height: `${height}px`,
rr_inlined_video: true,
class: attributes.class,
style: attributes.style,
};
tagName = 'canvas';
const saveVideoAsCanvas = (video: HTMLVideoElement) => {
const { width, height } = video.getBoundingClientRect();
attributes = {
width,
height,
rr_width: `${width}px`,
rr_height: `${height}px`,
rr_inlined_video: true,
class: attributes.class,
style: attributes.style,
};
tagName = 'canvas';

// create blank canvas of same dimensions
const blankCanvas = doc.createElement('canvas');
blankCanvas.width = (n as HTMLCanvasElement).width;
blankCanvas.height = (n as HTMLCanvasElement).height;
// create blank canvas of same dimensions
const blankCanvas = doc.createElement('canvas');
blankCanvas.width = (n as HTMLCanvasElement).width;
blankCanvas.height = (n as HTMLCanvasElement).height;

attributes.rr_dataURL = blankCanvas.toDataURL(
dataURLOptions.type,
dataURLOptions.quality,
);
attributes.rr_dataURL = blankCanvas.toDataURL(
dataURLOptions.type,
dataURLOptions.quality,
);
};

if (tagName === 'video') {
const video = n as HTMLVideoElement;
if (inlineImages) {
// local video - webcam (no src) or memory (blob)
if (video.src === '' || video.src.indexOf('blob:') !== -1) {
saveVideoAsCanvas(video);
}
}
if (inlineVideos) {
// remote video
if (video.src !== '' && video.src.indexOf('blob:') === -1) {
saveVideoAsCanvas(video);
}
}
}

Expand Down Expand Up @@ -1016,6 +1034,7 @@ export function serializeNodeWithId(
dataURLOptions?: DataURLOptions;
keepIframeSrcFn?: KeepIframeSrcFn;
inlineImages?: boolean;
inlineVideos?: boolean;
recordCanvas?: boolean;
preserveWhiteSpace?: boolean;
onSerialize?: (n: Node) => unknown;
Expand Down Expand Up @@ -1047,6 +1066,7 @@ export function serializeNodeWithId(
slimDOMOptions,
dataURLOptions = {},
inlineImages = false,
inlineVideos = false,
recordCanvas = false,
onSerialize,
onIframeLoad,
Expand Down Expand Up @@ -1084,6 +1104,7 @@ export function serializeNodeWithId(
maskInputFn,
dataURLOptions,
inlineImages,
inlineVideos,
recordCanvas,
keepIframeSrcFn,
newlyAddedElement,
Expand Down Expand Up @@ -1179,6 +1200,7 @@ export function serializeNodeWithId(
slimDOMOptions,
dataURLOptions,
inlineImages,
inlineVideos,
recordCanvas,
preserveWhiteSpace,
onSerialize,
Expand Down Expand Up @@ -1248,6 +1270,7 @@ export function serializeNodeWithId(
slimDOMOptions,
dataURLOptions,
inlineImages,
inlineVideos,
recordCanvas,
preserveWhiteSpace,
onSerialize,
Expand Down Expand Up @@ -1301,6 +1324,7 @@ export function serializeNodeWithId(
slimDOMOptions,
dataURLOptions,
inlineImages,
inlineVideos,
recordCanvas,
preserveWhiteSpace,
onSerialize,
Expand Down Expand Up @@ -1342,6 +1366,7 @@ function snapshot(
slimDOM?: 'all' | boolean | SlimDOMOptions;
dataURLOptions?: DataURLOptions;
inlineImages?: boolean;
inlineVideos?: boolean;
recordCanvas?: boolean;
preserveWhiteSpace?: boolean;
onSerialize?: (n: Node) => unknown;
Expand All @@ -1367,6 +1392,7 @@ function snapshot(
maskTextSelector = null,
inlineStylesheet = true,
inlineImages = false,
inlineVideos = false,
recordCanvas = false,
maskAllInputs = false,
maskTextFn,
Expand Down Expand Up @@ -1440,6 +1466,7 @@ function snapshot(
slimDOMOptions,
dataURLOptions,
inlineImages,
inlineVideos,
recordCanvas,
preserveWhiteSpace,
onSerialize,
Expand Down
7 changes: 6 additions & 1 deletion packages/rrweb/src/record/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ function record<T = eventWithTime>(
userTriggeredOnInput = false,
collectFonts = false,
inlineImages = false,
inlineVideos = false,
plugins,
keepIframeSrcFn = () => false,
privacySetting = 'default',
Expand Down Expand Up @@ -324,7 +325,8 @@ function record<T = eventWithTime>(

canvasManager = new CanvasManager({
recordCanvas,
recordVideos: inlineImages,
recordLocalVideos: inlineImages,
recordRemoteVideos: inlineVideos,
mutationCb: wrappedCanvasMutationEmit,
win: window,
blockClass,
Expand Down Expand Up @@ -355,6 +357,7 @@ function record<T = eventWithTime>(
maskInputFn,
recordCanvas,
inlineImages,
inlineVideos,
privacySetting,
sampling,
slimDOMOptions,
Expand Down Expand Up @@ -403,6 +406,7 @@ function record<T = eventWithTime>(
dataURLOptions,
recordCanvas,
inlineImages,
inlineVideos,
privacySetting,
onSerialize: (n) => {
if (isSerializedIframe(n, mirror)) {
Expand Down Expand Up @@ -552,6 +556,7 @@ function record<T = eventWithTime>(
recordDOM,
recordCanvas,
inlineImages,
inlineVideos,
userTriggeredOnInput,
collectFonts,
doc,
Expand Down
9 changes: 6 additions & 3 deletions packages/rrweb/src/record/mutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ export default class MutationBuffer {
private keepIframeSrcFn: observerParam['keepIframeSrcFn'];
private recordCanvas: observerParam['recordCanvas'];
private inlineImages: observerParam['inlineImages'];
private inlineVideos: observerParam['inlineVideos'];
private privacySetting: observerParam['privacySetting'];
private slimDOMOptions: observerParam['slimDOMOptions'];
private dataURLOptions: observerParam['dataURLOptions'];
Expand Down Expand Up @@ -212,6 +213,7 @@ export default class MutationBuffer {
'keepIframeSrcFn',
'recordCanvas',
'inlineImages',
'inlineVideos',
'privacySetting',
'slimDOMOptions',
'dataURLOptions',
Expand Down Expand Up @@ -319,6 +321,7 @@ export default class MutationBuffer {
dataURLOptions: this.dataURLOptions,
recordCanvas: this.recordCanvas,
inlineImages: this.inlineImages,
inlineVideos: this.inlineVideos,
privacySetting: this.privacySetting,
onSerialize: (currentN) => {
if (isSerializedIframe(currentN, this.mirror)) {
Expand Down Expand Up @@ -460,7 +463,7 @@ export default class MutationBuffer {
!highlightOverwriteRecord &&
value
) {
value = obfuscateText(value);
value = obfuscateText(value);
}
return {
id: this.mirror.getId(n),
Expand Down Expand Up @@ -640,8 +643,8 @@ export default class MutationBuffer {
if (tagName === 'INPUT') {
const node = m.target as HTMLInputElement;
if (node.type === 'password') {
item.attributes['value'] = '*'.repeat(node.value.length);
break;
item.attributes['value'] = '*'.repeat(node.value.length);
break;
}
}
/* End Highlight Code */
Expand Down
28 changes: 22 additions & 6 deletions packages/rrweb/src/record/observers/canvas/canvas-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ type pendingCanvasMutationsMap = Map<

interface Options {
recordCanvas: boolean;
recordVideos: boolean;
recordLocalVideos: boolean;
recordRemoteVideos: boolean;
mutationCb: canvasMutationCallback;
win: IWindow;
blockClass: blockClass;
Expand Down Expand Up @@ -91,7 +92,8 @@ export class CanvasManager {
blockClass,
blockSelector,
recordCanvas,
recordVideos,
recordLocalVideos,
recordRemoteVideos,
initialSnapshotDelay,
dataURLOptions,
} = options;
Expand All @@ -107,6 +109,7 @@ export class CanvasManager {

if (!('base64' in e.data)) {
this.debug(null, 'canvas worker received empty message', {
id,
data: e.data,
status: e.data.status,
});
Expand Down Expand Up @@ -155,7 +158,8 @@ export class CanvasManager {
} else if (recordCanvas && typeof sampling === 'number') {
this.debug(null, 'initializing canvas fps observer', { sampling });
this.initCanvasFPSObserver(
recordVideos,
recordLocalVideos,
recordRemoteVideos,
sampling,
win,
blockClass,
Expand Down Expand Up @@ -313,7 +317,8 @@ export class CanvasManager {
}

private initCanvasFPSObserver(
recordVideos: boolean,
recordLocalVideos: boolean,
recordRemoteVideos: boolean,
fps: number,
win: IWindow,
blockClass: blockClass,
Expand Down Expand Up @@ -372,9 +377,20 @@ export class CanvasManager {

const getVideos = (timestamp: DOMHighResTimeStamp): HTMLVideoElement[] => {
const matchedVideos: HTMLVideoElement[] = [];
if (recordVideos) {
if (recordLocalVideos || recordRemoteVideos) {
querySelectorAll(win.document, 'video').forEach((video) => {
if (video.src !== '' && video.src.indexOf('blob:') === -1) return;
if (!recordRemoteVideos) {
// remote video
if (video.src !== '' && video.src.indexOf('blob:') === -1) {
return;
}
}
if (!recordLocalVideos) {
// local video - webcam (no src) or memory (blob)
if (video.src === '' || video.src.indexOf('blob:') !== -1) {
return;
}
}
if (!isBlocked(video, blockClass, blockSelector, true)) {
matchedVideos.push(video);
const id = this.mirror.getId(video);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ worker.onmessage = async function (e) {
id,
base64,
});
return worker.postMessage({ id, status: 'unchanged' });
}
const msg = {
id,
Expand All @@ -104,7 +103,7 @@ worker.onmessage = async function (e) {
dy,
dw,
dh,
}
};
debug('[highlight-worker] canvas bitmap processed', msg);
worker.postMessage(msg);
lastBlobMap.set(id, base64);
Expand Down
3 changes: 3 additions & 0 deletions packages/rrweb/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export type recordOptions<T> = {
userTriggeredOnInput?: boolean;
collectFonts?: boolean;
inlineImages?: boolean;
inlineVideos?: boolean;
plugins?: RecordPlugin[];
// departed, please use sampling options
mousemoveWait?: number;
Expand Down Expand Up @@ -117,6 +118,7 @@ export type observerParam = {
recordDOM: boolean;
recordCanvas: boolean;
inlineImages: boolean;
inlineVideos: boolean;
privacySetting: PrivacySettingOption;
userTriggeredOnInput: boolean;
collectFonts: boolean;
Expand Down Expand Up @@ -155,6 +157,7 @@ export type MutationBufferParam = Pick<
| 'keepIframeSrcFn'
| 'recordCanvas'
| 'inlineImages'
| 'inlineVideos'
| 'privacySetting'
| 'slimDOMOptions'
| 'dataURLOptions'
Expand Down

0 comments on commit 51addd9

Please sign in to comment.