diff --git a/src/dispatchers/frameDispatcher.ts b/src/dispatchers/frameDispatcher.ts index 43adf40cdc..9d40310e4f 100644 --- a/src/dispatchers/frameDispatcher.ts +++ b/src/dispatchers/frameDispatcher.ts @@ -21,7 +21,7 @@ import { ElementHandleDispatcher, createHandle } from './elementHandlerDispatche import { parseArgument, serializeResult } from './jsHandleDispatcher'; import { ResponseDispatcher, RequestDispatcher } from './networkDispatchers'; import { ActionMetadata } from '../server/instrumentation'; -import { runAbortableTask } from '../server/progress'; +import { ProgressController, runAbortableTask } from '../server/progress'; export class FrameDispatcher extends Dispatcher implements channels.FrameChannel { private _frame: Frame; @@ -53,8 +53,11 @@ export class FrameDispatcher extends Dispatcher { - return { response: lookupNullableDispatcher(await this._frame.goto(params.url, params)) }; + async goto(params: channels.FrameGotoParams, metadata?: channels.Metadata): Promise { + const page = this._frame._page; + const actionMetadata: ActionMetadata = { ...metadata, type: 'goto', value: params.url, page }; + const controller = new ProgressController(page._timeoutSettings.navigationTimeout(params), actionMetadata); + return { response: lookupNullableDispatcher(await this._frame.goto(controller, params.url, params)) }; } async frameElement(): Promise { @@ -98,8 +101,11 @@ export class FrameDispatcher extends Dispatcher { - await this._frame.setContent(params.html, params); + async setContent(params: channels.FrameSetContentParams, metadata?: channels.Metadata): Promise { + const page = this._frame._page; + const actionMetadata: ActionMetadata = { ...metadata, type: 'setContent', value: params.html, page }; + const controller = new ProgressController(page._timeoutSettings.navigationTimeout(params), actionMetadata); + return await this._frame.setContent(controller, params.html, params); } async addScriptTag(params: channels.FrameAddScriptTagParams): Promise { diff --git a/src/dispatchers/pageDispatcher.ts b/src/dispatchers/pageDispatcher.ts index 8af9e67a2c..99b2e6469a 100644 --- a/src/dispatchers/pageDispatcher.ts +++ b/src/dispatchers/pageDispatcher.ts @@ -31,6 +31,8 @@ import { ElementHandleDispatcher, createHandle } from './elementHandlerDispatche import { FileChooser } from '../server/fileChooser'; import { CRCoverage } from '../server/chromium/crCoverage'; import { VideoDispatcher } from './videoDispatcher'; +import { ActionMetadata } from '../server/instrumentation'; +import { ProgressController } from '../server/progress'; export class PageDispatcher extends Dispatcher implements channels.PageChannel { private _page: Page; @@ -94,16 +96,22 @@ export class PageDispatcher extends Dispatcher i await this._page.setExtraHTTPHeaders(params.headers); } - async reload(params: channels.PageReloadParams): Promise { - return { response: lookupNullableDispatcher(await this._page.reload(params)) }; + async reload(params: channels.PageReloadParams, metadata?: channels.Metadata): Promise { + const actionMetadata: ActionMetadata = { ...metadata, type: 'reload', page: this._page }; + const controller = new ProgressController(this._page._timeoutSettings.navigationTimeout(params), actionMetadata); + return { response: lookupNullableDispatcher(await this._page.reload(controller, params)) }; } - async goBack(params: channels.PageGoBackParams): Promise { - return { response: lookupNullableDispatcher(await this._page.goBack(params)) }; + async goBack(params: channels.PageGoBackParams, metadata?: channels.Metadata): Promise { + const actionMetadata: ActionMetadata = { ...metadata, type: 'goBack', page: this._page }; + const controller = new ProgressController(this._page._timeoutSettings.navigationTimeout(params), actionMetadata); + return { response: lookupNullableDispatcher(await this._page.goBack(controller, params)) }; } - async goForward(params: channels.PageGoForwardParams): Promise { - return { response: lookupNullableDispatcher(await this._page.goForward(params)) }; + async goForward(params: channels.PageGoForwardParams, metadata?: channels.Metadata): Promise { + const actionMetadata: ActionMetadata = { ...metadata, type: 'goForward', page: this._page }; + const controller = new ProgressController(this._page._timeoutSettings.navigationTimeout(params), actionMetadata); + return { response: lookupNullableDispatcher(await this._page.goForward(controller, params)) }; } async emulateMedia(params: channels.PageEmulateMediaParams): Promise { diff --git a/src/server/frames.ts b/src/server/frames.ts index 87a0d5d94f..bcf59d76a6 100644 --- a/src/server/frames.ts +++ b/src/server/frames.ts @@ -430,8 +430,7 @@ export class Frame extends EventEmitter { this._detachedPromise.then(() => controller.abort(new Error('Navigating frame was detached!'))); } - async goto(url: string, options: types.GotoOptions = {}): Promise { - const controller = new ProgressController(this._page._timeoutSettings.navigationTimeout(options)); + async goto(controller: ProgressController, url: string, options: types.GotoOptions = {}): Promise { this.setupNavigationProgressController(controller); return controller.run(async progress => { const waitUntil = verifyLifecycle('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil); @@ -607,8 +606,7 @@ export class Frame extends EventEmitter { }); } - async setContent(html: string, options: types.NavigateOptions = {}): Promise { - const controller = new ProgressController(this._page._timeoutSettings.navigationTimeout(options)); + async setContent(controller: ProgressController, html: string, options: types.NavigateOptions = {}): Promise { this.setupNavigationProgressController(controller); return controller.run(async progress => { const waitUntil = options.waitUntil === undefined ? 'load' : options.waitUntil; diff --git a/src/server/instrumentation.ts b/src/server/instrumentation.ts index 1b99ea8872..fb61ac3f96 100644 --- a/src/server/instrumentation.ts +++ b/src/server/instrumentation.ts @@ -19,9 +19,9 @@ import type { ElementHandle } from './dom'; import type { Page } from './page'; export type ActionMetadata = { - type: 'click' | 'fill' | 'dblclick' | 'hover' | 'selectOption' | 'setInputFiles' | 'type' | 'press' | 'check' | 'uncheck', + type: 'click' | 'fill' | 'dblclick' | 'hover' | 'selectOption' | 'setInputFiles' | 'type' | 'press' | 'check' | 'uncheck' | 'goto' | 'setContent' | 'goBack' | 'goForward' | 'reload', page: Page, - target: ElementHandle | string, + target?: ElementHandle | string, value?: string, stack?: string, }; diff --git a/src/server/page.ts b/src/server/page.ts index 92a984e1fc..85b79aa394 100644 --- a/src/server/page.ts +++ b/src/server/page.ts @@ -262,8 +262,7 @@ export class Page extends EventEmitter { this.emit(Page.Events.Console, message); } - async reload(options: types.NavigateOptions = {}): Promise { - const controller = new ProgressController(this._timeoutSettings.navigationTimeout(options)); + async reload(controller: ProgressController, options: types.NavigateOptions): Promise { this.mainFrame().setupNavigationProgressController(controller); const response = await controller.run(async progress => { const waitPromise = this.mainFrame()._waitForNavigation(progress, options); @@ -274,8 +273,7 @@ export class Page extends EventEmitter { return response; } - async goBack(options: types.NavigateOptions = {}): Promise { - const controller = new ProgressController(this._timeoutSettings.navigationTimeout(options)); + async goBack(controller: ProgressController, options: types.NavigateOptions): Promise { this.mainFrame().setupNavigationProgressController(controller); const response = await controller.run(async progress => { const waitPromise = this.mainFrame()._waitForNavigation(progress, options); @@ -290,8 +288,7 @@ export class Page extends EventEmitter { return response; } - async goForward(options: types.NavigateOptions = {}): Promise { - const controller = new ProgressController(this._timeoutSettings.navigationTimeout(options)); + async goForward(controller: ProgressController, options: types.NavigateOptions): Promise { this.mainFrame().setupNavigationProgressController(controller); const response = await controller.run(async progress => { const waitPromise = this.mainFrame()._waitForNavigation(progress, options); diff --git a/src/trace/tracer.ts b/src/trace/tracer.ts index 7599c2dbf3..417313c54d 100644 --- a/src/trace/tracer.ts +++ b/src/trace/tracer.ts @@ -25,7 +25,7 @@ import { ActionResult, InstrumentingAgent, instrumentingAgents, ActionMetadata } import { Page } from '../server/page'; import { Snapshotter } from './snapshotter'; import * as types from '../server/types'; -import type { ElementHandle } from '../server/dom'; +import { ElementHandle } from '../server/dom'; import { helper, RegisteredListener } from '../server/helper'; import { DEFAULT_TIMEOUT } from '../utils/timeoutSettings'; @@ -144,6 +144,7 @@ class ContextTracer implements SnapshotterDelegate { type: 'action', contextId: this._contextId, action: 'snapshot', + pageId: this._pageToId.get(page), label: options.label || 'snapshot', snapshot, }; @@ -157,7 +158,7 @@ class ContextTracer implements SnapshotterDelegate { contextId: this._contextId, pageId: this._pageToId.get(metadata.page), action: metadata.type, - target: await this._targetToString(metadata.target), + target: metadata.target instanceof ElementHandle ? await metadata.target._previewPromise : metadata.target, value: metadata.value, snapshot, startTime: result.startTime, @@ -193,10 +194,6 @@ class ContextTracer implements SnapshotterDelegate { }); } - private async _targetToString(target: ElementHandle | string): Promise { - return typeof target === 'string' ? target : await target._previewPromise; - } - private async _takeSnapshot(page: Page, timeout: number = 0): Promise<{ sha1: string, duration: number } | undefined> { if (!timeout) { // Never use zero timeout to avoid stalling because of snapshot. diff --git a/test/page-event-crash.spec.ts b/test/page-event-crash.spec.ts index ca5b1c89c6..ebe42aeb3b 100644 --- a/test/page-event-crash.spec.ts +++ b/test/page-event-crash.spec.ts @@ -17,13 +17,13 @@ import { it, expect, describe, options } from './playwright.fixtures'; -function crash(pageImpl, browserName) { +function crash(page, toImpl, browserName) { if (browserName === 'chromium') - pageImpl.mainFrame().goto('chrome://crash').catch(e => {}); + page.goto('chrome://crash').catch(e => {}); else if (browserName === 'webkit') - pageImpl._delegate._session.send('Page.crash', {}).catch(e => {}); + toImpl(page)._delegate._session.send('Page.crash', {}).catch(e => {}); else if (browserName === 'firefox') - pageImpl._delegate._session.send('Page.crash', {}).catch(e => {}); + toImpl(page)._delegate._session.send('Page.crash', {}).catch(e => {}); } describe('', (suite, parameters) => { @@ -32,13 +32,13 @@ describe('', (suite, parameters) => { }, () => { it('should emit crash event when page crashes', async ({page, browserName, toImpl}) => { await page.setContent(`
This page should crash
`); - crash(toImpl(page), browserName); + crash(page, toImpl, browserName); await new Promise(f => page.on('crash', f)); }); it('should throw on any action after page crashes', async ({page, browserName, toImpl}) => { await page.setContent(`
This page should crash
`); - crash(toImpl(page), browserName); + crash(page, toImpl, browserName); await page.waitForEvent('crash'); const err = await page.evaluate(() => {}).then(() => null, e => e); expect(err).toBeTruthy(); @@ -48,7 +48,7 @@ describe('', (suite, parameters) => { it('should cancel waitForEvent when page crashes', async ({page, browserName, toImpl}) => { await page.setContent(`
This page should crash
`); const promise = page.waitForEvent('response').catch(e => e); - crash(toImpl(page), browserName); + crash(page, toImpl, browserName); const error = await promise; expect(error.message).toContain('Page crashed'); }); @@ -58,7 +58,7 @@ describe('', (suite, parameters) => { server.setRoute('/one-style.css', () => {}); const promise = page.goto(server.PREFIX + '/one-style.html').catch(e => e); await page.waitForNavigation({ waitUntil: 'domcontentloaded' }); - crash(toImpl(page), browserName); + crash(page, toImpl, browserName); const error = await promise; expect(error.message).toContain('Navigation failed because page crashed'); }); @@ -68,7 +68,7 @@ describe('', (suite, parameters) => { test.flaky(options.FIREFOX(parameters) && WIN); }, async ({page, browserName, toImpl}) => { await page.setContent(`
This page should crash
`); - crash(toImpl(page), browserName); + crash(page, toImpl, browserName); await page.waitForEvent('crash'); await page.context().close(); });