fix: evaluate in utility for screenshots (#6364)

We use `waitForFunctionValue` in the main world that may be corrupted.

References #6356.
This commit is contained in:
Dmitry Gozman 2021-04-29 14:53:53 -07:00 committed by GitHub
parent a4561310e8
commit 263a0fd2e1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 17 additions and 10 deletions

View file

@ -398,7 +398,7 @@ export class FFPage implements PageDelegate {
async takeScreenshot(progress: Progress, format: 'png' | 'jpeg', documentRect: types.Rect | undefined, viewportRect: types.Rect | undefined, quality: number | undefined): Promise<Buffer> {
if (!documentRect) {
const scrollOffset = await this._page.mainFrame().waitForFunctionValue(progress, () => ({ x: window.scrollX, y: window.scrollY }));
const scrollOffset = await this._page.mainFrame().waitForFunctionValueInUtility(progress, () => ({ x: window.scrollX, y: window.scrollY }));
documentRect = {
x: viewportRect!.x + scrollOffset.x,
y: viewportRect!.y + scrollOffset.y,

View file

@ -1092,7 +1092,7 @@ export class Frame extends SdkObject {
}, this._page._timeoutSettings.timeout(options));
}
async _waitForFunctionExpression<R>(metadata: CallMetadata, expression: string, isFunction: boolean | undefined, arg: any, options: types.WaitForFunctionOptions = {}): Promise<js.SmartHandle<R>> {
async _waitForFunctionExpression<R>(metadata: CallMetadata, expression: string, isFunction: boolean | undefined, arg: any, options: types.WaitForFunctionOptions, world: types.World = 'main'): Promise<js.SmartHandle<R>> {
const controller = new ProgressController(metadata, this);
if (typeof options.pollingInterval === 'number')
assert(options.pollingInterval > 0, 'Cannot poll with non-positive interval: ' + options.pollingInterval);
@ -1116,19 +1116,18 @@ export class Frame extends SdkObject {
return injectedScript.pollInterval(polling, (progress, continuePolling) => predicate(arg) || continuePolling);
}, { expression, isFunction, polling: options.pollingInterval, arg });
return controller.run(
progress => this._scheduleRerunnableHandleTask(progress, 'main', task),
progress => this._scheduleRerunnableHandleTask(progress, world, task),
this._page._timeoutSettings.timeout(options));
}
async waitForFunctionValue<R>(progress: Progress, pageFunction: js.Func1<any, R>) {
async waitForFunctionValueInUtility<R>(progress: Progress, pageFunction: js.Func1<any, R>) {
const expression = `() => {
const result = (${pageFunction})();
if (!result)
return result;
return JSON.stringify(result);
}`;
const handle = await this._page.mainFrame()._waitForFunctionExpression(internalCallMetadata(), expression, true, undefined, { timeout: progress.timeUntilDeadline() });
const handle = await this._waitForFunctionExpression(internalCallMetadata(), expression, true, undefined, { timeout: progress.timeUntilDeadline() }, 'utility');
return JSON.parse(handle.rawValue()) as R;
}

View file

@ -73,7 +73,7 @@ export class SelectorEvaluatorImpl implements SelectorEvaluator {
const parserNames = Array.from(customCSSNames).slice();
parserNames.sort();
if (allNames.join('|') !== parserNames.join('|'))
throw new Error(`Please keep customCSSNames in sync with evaluator engines`);
throw new Error(`Please keep customCSSNames in sync with evaluator engines: ${allNames.join('|')} vs ${parserNames.join('|')}`);
}
begin() {

View file

@ -35,12 +35,12 @@ export class Screenshotter {
const originalViewportSize = this._page.viewportSize();
let viewportSize = originalViewportSize;
if (!viewportSize)
viewportSize = await this._page.mainFrame().waitForFunctionValue(progress, () => ({ width: window.innerWidth, height: window.innerHeight }));
viewportSize = await this._page.mainFrame().waitForFunctionValueInUtility(progress, () => ({ width: window.innerWidth, height: window.innerHeight }));
return { viewportSize, originalViewportSize };
}
private async _fullPageSize(progress: Progress): Promise<types.Size> {
const fullPageSize = await this._page.mainFrame().waitForFunctionValue(progress, () => {
const fullPageSize = await this._page.mainFrame().waitForFunctionValueInUtility(progress, () => {
if (!document.body || !document.documentElement)
return null;
return {
@ -122,7 +122,7 @@ export class Screenshotter {
}
progress.throwIfAborted(); // Avoid extra work.
const scrollOffset = await this._page.mainFrame().waitForFunctionValue(progress, () => ({ x: window.scrollX, y: window.scrollY }));
const scrollOffset = await this._page.mainFrame().waitForFunctionValueInUtility(progress, () => ({ x: window.scrollX, y: window.scrollY }));
const documentRect = { ...boundingBox };
documentRect.x += scrollOffset.x;
documentRect.y += scrollOffset.y;

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

View file

@ -268,6 +268,14 @@ it.describe('page screenshot', () => {
await page.screenshot();
expect(resizeTriggered).toBeFalsy();
});
it('should work with Array deleted', async ({page, server}) => {
await page.setViewportSize({width: 500, height: 500});
await page.goto(server.PREFIX + '/grid.html');
await page.evaluate(() => delete window.Array);
const screenshot = await page.screenshot({ fullPage: true });
expect(screenshot).toMatchSnapshot('screenshot-grid-fullpage.png');
});
});
browserTest.describe('page screenshot', () => {