diff --git a/dev-packages/browser-integration-tests/suites/replay/canvas/manualSnapshot/test.ts b/dev-packages/browser-integration-tests/suites/replay/canvas/manualSnapshot/test.ts index e90020780d73..60a0864b71ee 100644 --- a/dev-packages/browser-integration-tests/suites/replay/canvas/manualSnapshot/test.ts +++ b/dev-packages/browser-integration-tests/suites/replay/canvas/manualSnapshot/test.ts @@ -63,6 +63,8 @@ sentryTest('can manually snapshot canvas', async ({ getLocalTestUrl, page, brows }, 0, 0, + 150, + 150, ], property: 'drawImage', }, diff --git a/dev-packages/browser-integration-tests/suites/replay/canvas/records/test.ts b/dev-packages/browser-integration-tests/suites/replay/canvas/records/test.ts index 0f9580fccc52..e2beb0afc2df 100644 --- a/dev-packages/browser-integration-tests/suites/replay/canvas/records/test.ts +++ b/dev-packages/browser-integration-tests/suites/replay/canvas/records/test.ts @@ -65,6 +65,8 @@ sentryTest('can record canvas', async ({ getLocalTestUrl, page, browserName }) = }, 0, 0, + 150, + 150, ], property: 'drawImage', }, diff --git a/packages/replay-canvas/package.json b/packages/replay-canvas/package.json index 4b6ba1b8791b..42ef5c36ef79 100644 --- a/packages/replay-canvas/package.json +++ b/packages/replay-canvas/package.json @@ -69,7 +69,7 @@ "homepage": "https://docs.sentry.io/platforms/javascript/session-replay/", "devDependencies": { "@babel/core": "^7.17.5", - "@sentry-internal/rrweb": "2.12.0" + "@sentry-internal/rrweb": "2.13.0" }, "dependencies": { "@sentry-internal/replay": "8.0.0-beta.3", diff --git a/packages/replay-canvas/src/canvas.ts b/packages/replay-canvas/src/canvas.ts index b9dcf8f4b75c..6f7b1ebfc19e 100644 --- a/packages/replay-canvas/src/canvas.ts +++ b/packages/replay-canvas/src/canvas.ts @@ -5,12 +5,14 @@ import type { IntegrationFn } from '@sentry/types'; interface ReplayCanvasOptions { enableManualSnapshot?: boolean; + maxCanvasSize?: [width: number, height: number]; quality: 'low' | 'medium' | 'high'; } type GetCanvasManager = (options: CanvasManagerOptions) => CanvasManagerInterface; export interface ReplayCanvasIntegrationOptions { enableManualSnapshot?: boolean; + maxCanvasSize?: number; recordCanvas: true; getCanvasManager: GetCanvasManager; sampling: { @@ -53,12 +55,18 @@ const CANVAS_QUALITY = { }; const INTEGRATION_NAME = 'ReplayCanvas'; +const DEFAULT_MAX_CANVAS_SIZE = 1280; /** Exported only for type safe tests. */ export const _replayCanvasIntegration = ((options: Partial = {}) => { + const [maxCanvasWidth, maxCanvasHeight] = options.maxCanvasSize || []; const _canvasOptions = { quality: options.quality || 'medium', enableManualSnapshot: options.enableManualSnapshot, + maxCanvasSize: [ + maxCanvasWidth ? Math.min(maxCanvasWidth, DEFAULT_MAX_CANVAS_SIZE) : DEFAULT_MAX_CANVAS_SIZE, + maxCanvasHeight ? Math.min(maxCanvasHeight, DEFAULT_MAX_CANVAS_SIZE) : DEFAULT_MAX_CANVAS_SIZE, + ] as [number, number], }; let canvasManagerResolve: (value: CanvasManager) => void; @@ -67,15 +75,16 @@ export const _replayCanvasIntegration = ((options: Partial return { name: INTEGRATION_NAME, getOptions(): ReplayCanvasIntegrationOptions { - const { quality, enableManualSnapshot } = _canvasOptions; + const { quality, enableManualSnapshot, maxCanvasSize } = _canvasOptions; return { enableManualSnapshot, recordCanvas: true, - getCanvasManager: (options: CanvasManagerOptions) => { + getCanvasManager: (getCanvasManagerOptions: CanvasManagerOptions) => { const manager = new CanvasManager({ - ...options, + ...getCanvasManagerOptions, enableManualSnapshot, + maxCanvasSize, errorHandler: (err: unknown) => { try { if (typeof err === 'object') { diff --git a/packages/replay-canvas/test/canvas.test.ts b/packages/replay-canvas/test/canvas.test.ts index 534c28261498..42752684f5c5 100644 --- a/packages/replay-canvas/test/canvas.test.ts +++ b/packages/replay-canvas/test/canvas.test.ts @@ -1,9 +1,17 @@ +import { CanvasManager } from '@sentry-internal/rrweb'; import { _replayCanvasIntegration } from '../src/canvas'; +jest.mock('@sentry-internal/rrweb'); + +beforeEach(() => { + jest.clearAllMocks(); +}); + it('initializes with default options', () => { const rc = _replayCanvasIntegration(); + const options = rc.getOptions(); - expect(rc.getOptions()).toEqual({ + expect(options).toEqual({ recordCanvas: true, getCanvasManager: expect.any(Function), sampling: { @@ -14,12 +22,22 @@ it('initializes with default options', () => { quality: 0.4, }, }); + + // @ts-expect-error don't care about the normal options we need to call this with, just want to test maxCanvasSize + options.getCanvasManager({}); + + expect(CanvasManager).toHaveBeenCalledWith( + expect.objectContaining({ + maxCanvasSize: [1280, 1280], + }), + ); }); it('initializes with quality option and manual snapshot', () => { const rc = _replayCanvasIntegration({ enableManualSnapshot: true, quality: 'low' }); + const options = rc.getOptions(); - expect(rc.getOptions()).toEqual({ + expect(options).toEqual({ enableManualSnapshot: true, recordCanvas: true, getCanvasManager: expect.any(Function), @@ -31,4 +49,40 @@ it('initializes with quality option and manual snapshot', () => { quality: 0.25, }, }); + + // @ts-expect-error don't care about the normal options we need to call this with, just want to test maxCanvasSize + options.getCanvasManager({}); + + expect(CanvasManager).toHaveBeenCalledWith( + expect.objectContaining({ + maxCanvasSize: [1280, 1280], + }), + ); +}); + +it('enforces a max canvas size', () => { + const rc = _replayCanvasIntegration({ enableManualSnapshot: true, quality: 'low', maxCanvasSize: [2000, 2000] }); + const options = rc.getOptions(); + + expect(options).toEqual({ + enableManualSnapshot: true, + recordCanvas: true, + getCanvasManager: expect.any(Function), + sampling: { + canvas: 1, + }, + dataURLOptions: { + type: 'image/webp', + quality: 0.25, + }, + }); + + // @ts-expect-error don't care about the normal options we need to call this with, just want to test maxCanvasSize + options.getCanvasManager({}); + + expect(CanvasManager).toHaveBeenCalledWith( + expect.objectContaining({ + maxCanvasSize: [1280, 1280], + }), + ); }); diff --git a/packages/replay-internal/package.json b/packages/replay-internal/package.json index e60632752e5c..fb1dd7d24841 100644 --- a/packages/replay-internal/package.json +++ b/packages/replay-internal/package.json @@ -70,8 +70,8 @@ "devDependencies": { "@babel/core": "^7.17.5", "@sentry-internal/replay-worker": "8.0.0-beta.3", - "@sentry-internal/rrweb": "2.12.0", - "@sentry-internal/rrweb-snapshot": "2.12.0", + "@sentry-internal/rrweb": "2.13.0", + "@sentry-internal/rrweb-snapshot": "2.13.0", "fflate": "^0.8.1", "jsdom-worker": "^0.2.1" }, diff --git a/yarn.lock b/yarn.lock index 9df6c2615a7a..23a10317dd6e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5665,22 +5665,22 @@ dependencies: "@sentry-internal/rrweb-snapshot" "2.11.0" -"@sentry-internal/rrdom@2.12.0": - version "2.12.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/rrdom/-/rrdom-2.12.0.tgz#d3ca32b1e4b8c5d8cc9bdb44f933fe4b059573a0" - integrity sha512-EQ9vmhkTREdtzKp6SmD4GEkwr+RJcaEnbVcDZjbnQnxagskOpqvXjoPMONPf9hZhkULwnrnyFGGp0VpQOGBS0w== +"@sentry-internal/rrdom@2.13.0": + version "2.13.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/rrdom/-/rrdom-2.13.0.tgz#3bb77fd67e72f743d33699431d8f66efb193e951" + integrity sha512-Idm+phUohY74mu9KxFX+lhBqHAN7qTMB6TZGf1hKBn8CusGm91jdEoBe4xqwesnthe8a37svkf8D3CIsiblZPA== dependencies: - "@sentry-internal/rrweb-snapshot" "2.12.0" + "@sentry-internal/rrweb-snapshot" "2.13.0" "@sentry-internal/rrweb-snapshot@2.11.0": version "2.11.0" resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb-snapshot/-/rrweb-snapshot-2.11.0.tgz#1af79130604afea989d325465b209ac015b27c9a" integrity sha512-1nP22QlplMNooSNvTh+L30NSZ+E3UcfaJyxXSMLxUjQHTGPyM1VkndxZMmxlKhyR5X+rLbxi/+RvuAcpM43VoA== -"@sentry-internal/rrweb-snapshot@2.12.0": - version "2.12.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb-snapshot/-/rrweb-snapshot-2.12.0.tgz#2f1f6d4867a07ab757475fb4fa337d7f1aaa6b2d" - integrity sha512-AYo8CeDA7qDOKFG75E+bnxrS/qm7l5Ad0ftClA3VzoGV58bNNgv/aKiECtUPk0UPs4EqTQ8z8W/MZ9EYDF6vvA== +"@sentry-internal/rrweb-snapshot@2.13.0": + version "2.13.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb-snapshot/-/rrweb-snapshot-2.13.0.tgz#47fb017b2031075cdd4f1b54c098c0bd8eb75e16" + integrity sha512-ftSybKlmddX9QsLXq02gMiWfuXEfyjysSJe0tvKxGMP2r1y4rS5h2qjJeKx+GYPhcGi1s48KkjphLNwHehqf4g== "@sentry-internal/rrweb-types@2.11.0": version "2.11.0" @@ -5689,12 +5689,12 @@ dependencies: "@sentry-internal/rrweb-snapshot" "2.11.0" -"@sentry-internal/rrweb-types@2.12.0": - version "2.12.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb-types/-/rrweb-types-2.12.0.tgz#f7c57eda7610882c71860437657ffbbcb788184d" - integrity sha512-W0iLlTx3HeapBTGjg/uLoKQr1/DGPbkANqwjf4mW0IS4jHAVcxFX/e769aHHKEmd68Lm3+A8b08xdA9UDBXW5w== +"@sentry-internal/rrweb-types@2.13.0": + version "2.13.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb-types/-/rrweb-types-2.13.0.tgz#b68b9cf03c51626051bed9f8c41a40f1cf362991" + integrity sha512-noG66TvuN5xJuQAPfxHECW84XUsjYR95fytH6tSvPQQh7a0JUC+i5H76YSWdGhbu5eCMcrypzgATfw/GEN7bPA== dependencies: - "@sentry-internal/rrweb-snapshot" "2.12.0" + "@sentry-internal/rrweb-snapshot" "2.13.0" "@sentry-internal/rrweb@2.11.0": version "2.11.0" @@ -5710,14 +5710,14 @@ fflate "^0.4.4" mitt "^3.0.0" -"@sentry-internal/rrweb@2.12.0": - version "2.12.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb/-/rrweb-2.12.0.tgz#4becbedf7315f4b4e0ebc35319a848ec6f082dce" - integrity sha512-NosAF5f8dXdj6linXpI+e38/eKVtwy3R2rzmMohBCwdhPXgTkTV/Laj/9OsRxARNRyz81mIEGcn/Ivp/De7RaA== +"@sentry-internal/rrweb@2.13.0": + version "2.13.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb/-/rrweb-2.13.0.tgz#27f997a5922fa4af5990198b62a149bbf4c18833" + integrity sha512-Xi+Sg7T8+1UbIaW5l5zKNb+X7FlD7O0l8TZwZjMXVGQtfQcJTkPkrALspqZfUeaAtX+rQHQArcfpNhQWiDYezQ== dependencies: - "@sentry-internal/rrdom" "2.12.0" - "@sentry-internal/rrweb-snapshot" "2.12.0" - "@sentry-internal/rrweb-types" "2.12.0" + "@sentry-internal/rrdom" "2.13.0" + "@sentry-internal/rrweb-snapshot" "2.13.0" + "@sentry-internal/rrweb-types" "2.13.0" "@types/css-font-loading-module" "0.0.7" "@xstate/fsm" "^1.4.0" base64-arraybuffer "^1.0.1"