Skip to content

Commit 0e31ace

Browse files
committed
cherry-pick(#33575): fix(canvas snapshots): position mismatch in headless mode
1 parent b2a39ff commit 0e31ace

File tree

3 files changed

+30
-11
lines changed

3 files changed

+30
-11
lines changed

packages/playwright-core/src/server/trace/recorder/snapshotterInjected.ts

+13
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export function frameSnapshotStreamer(snapshotStreamer: string, removeNoScript:
4747
const kTargetAttribute = '__playwright_target__';
4848
const kCustomElementsAttribute = '__playwright_custom_elements__';
4949
const kCurrentSrcAttribute = '__playwright_current_src__';
50+
const kBoundingRectAttribute = '__playwright_bounding_rect__';
5051

5152
// Symbols for our own info on Nodes/StyleSheets.
5253
const kSnapshotFrameId = Symbol('__playwright_snapshot_frameid_');
@@ -436,6 +437,18 @@ export function frameSnapshotStreamer(snapshotStreamer: string, removeNoScript:
436437
expectValue(value);
437438
attrs[kSelectedAttribute] = value;
438439
}
440+
if (nodeName === 'CANVAS') {
441+
const boundingRect = (element as HTMLCanvasElement).getBoundingClientRect();
442+
const value = JSON.stringify({
443+
left: boundingRect.left / window.innerWidth,
444+
top: boundingRect.top / window.innerHeight,
445+
right: boundingRect.right / window.innerWidth,
446+
bottom: boundingRect.bottom / window.innerHeight
447+
});
448+
expectValue(kBoundingRectAttribute);
449+
expectValue(value);
450+
attrs[kBoundingRectAttribute] = value;
451+
}
439452
if (element.scrollTop) {
440453
expectValue(kScrollTopAttribute);
441454
expectValue(element.scrollTop);

packages/trace-viewer/src/sw/snapshotRenderer.ts

+16-10
Original file line numberDiff line numberDiff line change
@@ -427,25 +427,31 @@ function snapshotScript(...targetIds: (string | undefined)[]) {
427427
for (const canvas of canvasElements) {
428428
const context = canvas.getContext('2d')!;
429429

430-
const boundingRect = canvas.getBoundingClientRect();
431-
const xStart = boundingRect.left / window.innerWidth;
432-
const yStart = boundingRect.top / window.innerHeight;
433-
const xEnd = boundingRect.right / window.innerWidth;
434-
const yEnd = boundingRect.bottom / window.innerHeight;
435-
436-
const partiallyUncaptured = xEnd > 1 || yEnd > 1;
437-
const fullyUncaptured = xStart > 1 || yStart > 1;
430+
const boundingRectAttribute = canvas.getAttribute('__playwright_bounding_rect__');
431+
canvas.removeAttribute('__playwright_bounding_rect__');
432+
if (!boundingRectAttribute)
433+
continue;
434+
435+
let boundingRect: { left: number, top: number, right: number, bottom: number };
436+
try {
437+
boundingRect = JSON.parse(boundingRectAttribute);
438+
} catch (e) {
439+
continue;
440+
}
441+
442+
const partiallyUncaptured = boundingRect.right > 1 || boundingRect.bottom > 1;
443+
const fullyUncaptured = boundingRect.left > 1 || boundingRect.top > 1;
438444
if (fullyUncaptured) {
439445
canvas.title = `Playwright couldn't capture canvas contents because it's located outside the viewport.`;
440446
continue;
441447
}
442448

443449
drawCheckerboard(context, canvas);
444450

445-
context.drawImage(img, xStart * img.width, yStart * img.height, (xEnd - xStart) * img.width, (yEnd - yStart) * img.height, 0, 0, canvas.width, canvas.height);
451+
context.drawImage(img, boundingRect.left * img.width, boundingRect.top * img.height, (boundingRect.right - boundingRect.left) * img.width, (boundingRect.bottom - boundingRect.top) * img.height, 0, 0, canvas.width, canvas.height);
446452
if (isUnderTest)
447453
// eslint-disable-next-line no-console
448-
console.log(`canvas drawn:`, JSON.stringify([xStart, yStart, xEnd, yEnd].map(v => Math.floor(v * 100))));
454+
console.log(`canvas drawn:`, JSON.stringify([boundingRect.left, boundingRect.top, (boundingRect.right - boundingRect.left), (boundingRect.bottom - boundingRect.top)].map(v => Math.floor(v * 100))));
449455

450456
if (partiallyUncaptured)
451457
canvas.title = `Playwright couldn't capture full canvas contents because it's located partially outside the viewport.`;

tests/library/trace-viewer.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1510,7 +1510,7 @@ test('canvas clipping', async ({ runAndTrace, page, server }) => {
15101510
});
15111511

15121512
const msg = await traceViewer.page.waitForEvent('console', { predicate: msg => msg.text().startsWith('canvas drawn:') });
1513-
expect(msg.text()).toEqual('canvas drawn: [0,91,12,111]');
1513+
expect(msg.text()).toEqual('canvas drawn: [0,91,11,20]');
15141514

15151515
const snapshot = await traceViewer.snapshotFrame('page.goto');
15161516
await expect(snapshot.locator('canvas')).toHaveAttribute('title', `Playwright couldn't capture full canvas contents because it's located partially outside the viewport.`);

0 commit comments

Comments
 (0)