Skip to content

Commit

Permalink
feat: hide blinking cursor when making screenshots (#11854)
Browse files Browse the repository at this point in the history
References #9938
  • Loading branch information
aslushnikov authored Feb 4, 2022
1 parent c9e99d5 commit 547a328
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/playwright-core/src/server/chromium/crDragDrop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export class DragManager {
const val = await didStartDrag;
window.removeEventListener('mousemove', mouseListener, { capture: true });
window.removeEventListener('dragstart', dragListener, { capture: true });
delete window.__cleanupDrag;
return val;
};
}).toString(), true, 'utility').catch(() => {});
Expand Down
34 changes: 34 additions & 0 deletions packages/playwright-core/src/server/screenshotter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ import * as types from './types';
import { Progress } from './progress';
import { assert } from '../utils/utils';

declare global {
interface Window {
__cleanupScreenshot?: () => void;
}
}

export class Screenshotter {
private _queue = new TaskQueue();
private _page: Page;
Expand Down Expand Up @@ -115,8 +121,36 @@ export class Screenshotter {
progress.cleanupWhenAborted(() => this._page._delegate.setBackgroundColor());
}
progress.throwIfAborted(); // Avoid extra work.

const restoreBlinkingCaret = async () => {
await Promise.all(this._page.frames().map(async frame => {
frame.nonStallingEvaluateInExistingContext('window.__cleanupScreenshot && window.__cleanupScreenshot()', false, 'utility').catch(() => {});
}));
};
await Promise.all(this._page.frames().map(async frame => {
await frame.nonStallingEvaluateInExistingContext((function() {
const styleTag = document.createElement('style');
styleTag.textContent = `
* { caret-color: transparent !important; }
* > * { caret-color: transparent !important; }
* > * > * { caret-color: transparent !important; }
* > * > * > * { caret-color: transparent !important; }
* > * > * > * > * { caret-color: transparent !important; }
`;
document.documentElement.append(styleTag);
window.__cleanupScreenshot = () => {
styleTag.remove();
delete window.__cleanupScreenshot;
};
}).toString(), true, 'utility').catch(() => {});
}));
progress.cleanupWhenAborted(() => restoreBlinkingCaret());
progress.throwIfAborted(); // Avoid extra work.

const buffer = await this._page._delegate.takeScreenshot(progress, format, documentRect, viewportRect, options.quality, fitsViewport);
progress.throwIfAborted(); // Avoid restoring after failure - should be done by cleanup.
await restoreBlinkingCaret();
progress.throwIfAborted(); // Avoid restoring after failure - should be done by cleanup.
if (shouldSetDefaultBackground)
await this._page._delegate.setBackgroundColor();
progress.throwIfAborted(); // Avoid side effects.
Expand Down
17 changes: 17 additions & 0 deletions tests/page/page-screenshot.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,23 @@ it.describe('page screenshot', () => {
expect(screenshot).toMatchSnapshot('screenshot-sanity.png');
});

it('should not capture blinking caret', async ({ page, server }) => {
await page.setContent(`
<div contenteditable="true"></div>
`);
const div = page.locator('div');
await div.type('foo bar');
const screenshot = await div.screenshot();
for (let i = 0; i < 10; ++i) {
// Caret blinking time is set to 500ms.
// Try to capture variety of screenshots to make
// sure we don't capture blinking caret.
await new Promise(x => setTimeout(x, 150));
const newScreenshot = await div.screenshot();
expect(newScreenshot.equals(screenshot)).toBe(true);
}
});

it('should clip rect', async ({ page, server }) => {
await page.setViewportSize({ width: 500, height: 500 });
await page.goto(server.PREFIX + '/grid.html');
Expand Down

0 comments on commit 547a328

Please sign in to comment.