Skip to content

Commit 88e0a3b

Browse files
authored
cherry-pick(#13222): fix(screenshot): do not stall on hideHiglight attempt 2 (#13228)
It turns out that "non stalling evaluate" can stall in Chromium in some weird conditions, like `document.open` after some weird `iframe.src` value. We now only hide highlight in those frames where we did install highlight in the first place.
1 parent 4eb6fca commit 88e0a3b

2 files changed

Lines changed: 27 additions & 8 deletions

File tree

packages/playwright-core/src/server/screenshotter.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -222,11 +222,18 @@ export class Screenshotter {
222222
}));
223223
}
224224

225-
async _maskElements(progress: Progress, options: ScreenshotOptions) {
225+
async _maskElements(progress: Progress, options: ScreenshotOptions): Promise<() => Promise<void>> {
226+
const framesToParsedSelectors: MultiMap<Frame, ParsedSelector> = new MultiMap();
227+
228+
const cleanup = async () => {
229+
await Promise.all([...framesToParsedSelectors.keys()].map(async frame => {
230+
await frame.hideHighlight();
231+
}));
232+
};
233+
226234
if (!options.mask || !options.mask.length)
227-
return false;
235+
return cleanup;
228236

229-
const framesToParsedSelectors: MultiMap<Frame, ParsedSelector> = new MultiMap();
230237
await Promise.all((options.mask || []).map(async ({ frame, selector }) => {
231238
const pair = await frame.resolveFrameForSelectorNoWait(selector);
232239
if (pair)
@@ -237,8 +244,8 @@ export class Screenshotter {
237244
await Promise.all([...framesToParsedSelectors.keys()].map(async frame => {
238245
await frame.maskSelectors(framesToParsedSelectors.get(frame));
239246
}));
240-
progress.cleanupWhenAborted(() => this._page.hideHighlight());
241-
return true;
247+
progress.cleanupWhenAborted(cleanup);
248+
return cleanup;
242249
}
243250

244251
private async _screenshot(progress: Progress, format: 'png' | 'jpeg', documentRect: types.Rect | undefined, viewportRect: types.Rect | undefined, fitsViewport: boolean | undefined, options: ScreenshotOptions): Promise<Buffer> {
@@ -252,14 +259,13 @@ export class Screenshotter {
252259
}
253260
progress.throwIfAborted(); // Avoid extra work.
254261

255-
const hasHighlight = await this._maskElements(progress, options);
262+
const cleanupHighlight = await this._maskElements(progress, options);
256263
progress.throwIfAborted(); // Avoid extra work.
257264

258265
const buffer = await this._page._delegate.takeScreenshot(progress, format, documentRect, viewportRect, options.quality, fitsViewport);
259266
progress.throwIfAborted(); // Avoid restoring after failure - should be done by cleanup.
260267

261-
if (hasHighlight)
262-
await this._page.hideHighlight();
268+
await cleanupHighlight();
263269
progress.throwIfAborted(); // Avoid restoring after failure - should be done by cleanup.
264270

265271
if (shouldSetDefaultBackground)

tests/page/page-screenshot.spec.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,19 @@ it.describe('page screenshot', () => {
444444
await route.fulfill({ body: '' });
445445
await done;
446446
});
447+
448+
it('should work when subframe used document.open after a weird url', async ({ page, server }) => {
449+
await page.goto(server.EMPTY_PAGE);
450+
await page.evaluate(() => {
451+
const iframe = document.createElement('iframe');
452+
iframe.src = 'javascript:hi';
453+
document.body.appendChild(iframe);
454+
iframe.contentDocument.open();
455+
iframe.contentDocument.write('Hello');
456+
iframe.contentDocument.close();
457+
});
458+
await page.screenshot({ mask: [ page.locator('non-existent') ] });
459+
});
447460
});
448461
});
449462

0 commit comments

Comments
 (0)