Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion packages/rrweb-snapshot/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"test": "jest",
"test:watch": "jest --watch",
"build": "rollup --config && ES_ONLY=true rollup --config",
"dev": "yarn bundle:es-only --watch",
"dev": "ES_ONLY=true rollup --config --watch",
"typegen": "tsc -d --declarationDir typings",
"prepublish": "npm run typegen && npm run build",
"lint": "yarn eslint src"
Expand Down
2 changes: 2 additions & 0 deletions packages/rrweb/src/record/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ function record<T = eventWithTime>(
enableStrictPrivacy = false,
ignoreCSSAttributes = new Set([]),
errorHandler,
logger,
} = options;

registerErrorHandler(errorHandler);
Expand Down Expand Up @@ -319,6 +320,7 @@ function record<T = eventWithTime>(
resizeQuality: sampling?.canvas?.resizeQuality,
resizeFactor: sampling?.canvas?.resizeFactor,
maxSnapshotDimension: sampling?.canvas?.maxSnapshotDimension,
logger: logger,
});

const shadowDomManager = new ShadowDomManager({
Expand Down
58 changes: 51 additions & 7 deletions packages/rrweb/src/record/observers/canvas/canvas-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ export class CanvasManager {
private pendingCanvasMutations: pendingCanvasMutationsMap = new Map();
private rafStamps: RafStamps = { latestId: 0, invokeId: null };
private mirror: Mirror;
private logger?: {
debug: (...args: Parameters<typeof console.debug>) => void;
warn: (...args: Parameters<typeof console.warn>) => void;
};

private mutationCb: canvasMutationCallback;
private resetObservers?: listenerHandler;
Expand Down Expand Up @@ -71,6 +75,10 @@ export class CanvasManager {
resizeQuality?: 'pixelated' | 'low' | 'medium' | 'high';
resizeFactor?: number;
maxSnapshotDimension?: number;
logger?: {
debug: (...args: Parameters<typeof console.debug>) => void;
warn: (...args: Parameters<typeof console.warn>) => void;
};
}) {
const {
sampling = 'all',
Expand All @@ -82,6 +90,7 @@ export class CanvasManager {
} = options;
this.mutationCb = options.mutationCb;
this.mirror = options.mirror;
this.logger = options.logger;

if (recordCanvas && sampling === 'all')
this.initCanvasMutationObserver(win, blockClass, blockSelector);
Expand All @@ -100,6 +109,18 @@ export class CanvasManager {
);
}

private debug(
canvas?: HTMLCanvasElement,
...args: Parameters<typeof console.log>
) {
if (!this.logger) return;
let prefix = '[highlight-canvas]';
if (canvas) {
prefix += ` [ctx:${(canvas as ICanvas).__context}]`;
}
this.logger.debug(prefix, canvas, ...args);
}

private processMutation: canvasManagerMutationCallback = (
target,
mutation,
Expand Down Expand Up @@ -183,6 +204,7 @@ export class CanvasManager {
const matchedCanvas: HTMLCanvasElement[] = [];
win.document.querySelectorAll('canvas').forEach((canvas) => {
if (!isBlocked(canvas, blockClass, blockSelector, true)) {
this.debug(canvas, 'discovered canvas');
matchedCanvas.push(canvas);
}
});
Expand All @@ -199,12 +221,15 @@ export class CanvasManager {
}
lastSnapshotTime = timestamp;

getCanvas()
// eslint-disable-next-line @typescript-eslint/no-misused-promises
.forEach(async (canvas: HTMLCanvasElement) => {
const id = this.mirror.getId(canvas);
if (snapshotInProgressMap.get(id)) return;
snapshotInProgressMap.set(id, true);
getCanvas().forEach(async (canvas: HTMLCanvasElement) => {
this.debug(canvas, 'starting snapshotting');
const id = this.mirror.getId(canvas);
if (snapshotInProgressMap.get(id)) {
this.debug(canvas, 'snapshotting already in progress for', id);
return;
}
snapshotInProgressMap.set(id, true);
try {
if (['webgl', 'webgl2'].includes((canvas as ICanvas).__context)) {
// if the canvas hasn't been modified recently,
// its contents won't be in memory and `createImageBitmap`
Expand All @@ -227,6 +252,10 @@ export class CanvasManager {
// canvas is not yet ready... this retry on the next sampling iteration.
// we don't want to crash the worker if the canvas is not yet rendered.
if (canvas.width === 0 || canvas.height === 0) {
this.debug(canvas, 'not yet ready', {
width: canvas.width,
height: canvas.height,
});
return;
}
let scale = resizeFactor || 1;
Expand All @@ -237,11 +266,18 @@ export class CanvasManager {
const width = canvas.width * scale;
const height = canvas.height * scale;

window.performance.mark(`canvas-${canvas.id}-snapshot`);
const bitmap = await createImageBitmap(canvas, {
resizeQuality: resizeQuality || 'low',
resizeWidth: width,
resizeHeight: height,
});
this.debug(
canvas,
'took a snapshot in',
window.performance.measure(`canvas-snapshot`),
);
window.performance.mark(`canvas-postMessage`);
worker.postMessage(
{
id,
Expand All @@ -254,7 +290,15 @@ export class CanvasManager {
},
[bitmap],
);
});
this.debug(
canvas,
'send message in',
window.performance.measure(`canvas-postMessage`),
);
} finally {
snapshotInProgressMap.set(id, false);
}
});
rafId = requestAnimationFrame(takeCanvasSnapshots);
};

Expand Down
4 changes: 4 additions & 0 deletions packages/rrweb/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ export type recordOptions<T> = {
* Text will be randomized. Instead of seeing "Hello World" in a recording, you will see "1fds1 j59a0".
*/
enableStrictPrivacy?: boolean;
logger?: {
debug: (...args: Parameters<typeof console.debug>) => void;
warn: (...args: Parameters<typeof console.warn>) => void;
};
};

export type observerParam = {
Expand Down