diff --git a/src/browserContext.ts b/src/browserContext.ts index 8ed4755da9..3568aa0c8d 100644 --- a/src/browserContext.ts +++ b/src/browserContext.ts @@ -86,7 +86,7 @@ export abstract class BrowserContextBase extends EventEmitter implements Browser async waitForEvent(event: string, optionsOrPredicate: types.WaitForEventOptions = {}): Promise { const options = typeof optionsOrPredicate === 'function' ? { predicate: optionsOrPredicate } : optionsOrPredicate; - const progressController = new ProgressController(this._apiLogger, this._timeoutSettings.timeout(options), 'browserContext.waitForEvent'); + const progressController = new ProgressController(this._apiLogger, this._timeoutSettings.timeout(options)); if (event !== Events.BrowserContext.Close) this._closePromise.then(error => progressController.abort(error)); return progressController.run(progress => helper.waitForEvent(progress, this, event, options.predicate).promise); @@ -191,9 +191,9 @@ export abstract class BrowserContextBase extends EventEmitter implements Browser if (!this.pages().length) await this.waitForEvent('page'); const pages = this.pages(); - await pages[0].waitForLoadState(); - if (pages.length !== 1 || pages[0].url() !== 'about:blank') - throw new Error(`Arguments can not specify page to be opened (first url is ${pages[0].url()})`); + await pages[0].mainFrame().waitForLoadState(); + if (pages.length !== 1 || pages[0].mainFrame().url() !== 'about:blank') + throw new Error(`Arguments can not specify page to be opened (first url is ${pages[0].mainFrame().url()})`); if (this._options.isMobile || this._options.locale) { // Workaround for: // - chromium fails to change isMobile for existing page; diff --git a/src/dom.ts b/src/dom.ts index 139733dbe0..e294d45fd6 100644 --- a/src/dom.ts +++ b/src/dom.ts @@ -223,7 +223,7 @@ export class ElementHandle extends js.JSHandle { async scrollIntoViewIfNeeded(options: types.TimeoutOptions = {}) { return this._page._runAbortableTask( progress => this._waitAndScrollIntoViewIfNeeded(progress), - this._page._timeoutSettings.timeout(options), 'elementHandle.scrollIntoViewIfNeeded'); + this._page._timeoutSettings.timeout(options)); } private async _waitForVisible(progress: Progress): Promise<'error:notconnected' | 'done'> { @@ -376,7 +376,7 @@ export class ElementHandle extends js.JSHandle { return this._page._runAbortableTask(async progress => { const result = await this._hover(progress, options); return assertDone(throwRetargetableDOMError(result)); - }, this._page._timeoutSettings.timeout(options), 'elementHandle.hover'); + }, this._page._timeoutSettings.timeout(options)); } _hover(progress: Progress, options: types.PointerActionOptions & types.PointerActionWaitOptions): Promise<'error:notconnected' | 'done'> { @@ -387,7 +387,7 @@ export class ElementHandle extends js.JSHandle { return this._page._runAbortableTask(async progress => { const result = await this._click(progress, options); return assertDone(throwRetargetableDOMError(result)); - }, this._page._timeoutSettings.timeout(options), 'elementHandle.click'); + }, this._page._timeoutSettings.timeout(options)); } _click(progress: Progress, options: types.MouseClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> { @@ -398,7 +398,7 @@ export class ElementHandle extends js.JSHandle { return this._page._runAbortableTask(async progress => { const result = await this._dblclick(progress, options); return assertDone(throwRetargetableDOMError(result)); - }, this._page._timeoutSettings.timeout(options), 'elementHandle.dblclick'); + }, this._page._timeoutSettings.timeout(options)); } _dblclick(progress: Progress, options: types.MouseMultiClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> { @@ -409,7 +409,7 @@ export class ElementHandle extends js.JSHandle { return this._page._runAbortableTask(async progress => { const result = await this._selectOption(progress, values, options); return throwRetargetableDOMError(result); - }, this._page._timeoutSettings.timeout(options), 'elementHandle.selectOption'); + }, this._page._timeoutSettings.timeout(options)); } async _selectOption(progress: Progress, values: string | ElementHandle | types.SelectOption | string[] | ElementHandle[] | types.SelectOption[] | null, options: types.NavigatingActionWaitOptions): Promise { @@ -444,7 +444,7 @@ export class ElementHandle extends js.JSHandle { return this._page._runAbortableTask(async progress => { const result = await this._fill(progress, value, options); assertDone(throwRetargetableDOMError(result)); - }, this._page._timeoutSettings.timeout(options), 'elementHandle.fill'); + }, this._page._timeoutSettings.timeout(options)); } async _fill(progress: Progress, value: string, options: types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> { @@ -483,14 +483,14 @@ export class ElementHandle extends js.JSHandle { const pollHandler = new InjectedScriptPollHandler(progress, poll); const result = throwFatalDOMError(await pollHandler.finish()); assertDone(throwRetargetableDOMError(result)); - }, this._page._timeoutSettings.timeout(options), 'elementHandle.selectText'); + }, this._page._timeoutSettings.timeout(options)); } async setInputFiles(files: string | types.FilePayload | string[] | types.FilePayload[], options: types.NavigatingActionWaitOptions = {}) { return this._page._runAbortableTask(async progress => { const result = await this._setInputFiles(progress, files, options); return assertDone(throwRetargetableDOMError(result)); - }, this._page._timeoutSettings.timeout(options), 'elementHandle.setInputFiles'); + }, this._page._timeoutSettings.timeout(options)); } async _setInputFiles(progress: Progress, files: string | types.FilePayload | string[] | types.FilePayload[], options: types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> { @@ -517,7 +517,7 @@ export class ElementHandle extends js.JSHandle { return this._page._runAbortableTask(async progress => { const result = await this._focus(progress); return assertDone(throwRetargetableDOMError(result)); - }, 0, 'elementHandle.focus'); + }, 0); } async _focus(progress: Progress, resetSelectionIfNotFocused?: boolean): Promise<'error:notconnected' | 'done'> { @@ -530,7 +530,7 @@ export class ElementHandle extends js.JSHandle { return this._page._runAbortableTask(async progress => { const result = await this._type(progress, text, options); return assertDone(throwRetargetableDOMError(result)); - }, this._page._timeoutSettings.timeout(options), 'elementHandle.type'); + }, this._page._timeoutSettings.timeout(options)); } async _type(progress: Progress, text: string, options: { delay?: number } & types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> { @@ -549,7 +549,7 @@ export class ElementHandle extends js.JSHandle { return this._page._runAbortableTask(async progress => { const result = await this._press(progress, key, options); return assertDone(throwRetargetableDOMError(result)); - }, this._page._timeoutSettings.timeout(options), 'elementHandle.press'); + }, this._page._timeoutSettings.timeout(options)); } async _press(progress: Progress, key: string, options: { delay?: number } & types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> { @@ -568,14 +568,14 @@ export class ElementHandle extends js.JSHandle { return this._page._runAbortableTask(async progress => { const result = await this._setChecked(progress, true, options); return assertDone(throwRetargetableDOMError(result)); - }, this._page._timeoutSettings.timeout(options), 'elementHandle.check'); + }, this._page._timeoutSettings.timeout(options)); } async uncheck(options: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions = {}) { return this._page._runAbortableTask(async progress => { const result = await this._setChecked(progress, false, options); return assertDone(throwRetargetableDOMError(result)); - }, this._page._timeoutSettings.timeout(options), 'elementHandle.uncheck'); + }, this._page._timeoutSettings.timeout(options)); } async _setChecked(progress: Progress, state: boolean, options: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> { @@ -596,7 +596,7 @@ export class ElementHandle extends js.JSHandle { async screenshot(options: types.ElementScreenshotOptions = {}): Promise { return this._page._runAbortableTask( progress => this._page._screenshotter.screenshotElement(progress, this, options), - this._page._timeoutSettings.timeout(options), 'elementHandle.screenshot'); + this._page._timeoutSettings.timeout(options)); } async $(selector: string): Promise { @@ -655,7 +655,7 @@ export class ElementHandle extends js.JSHandle { } const handle = result.asElement() as ElementHandle; return handle._adoptTo(await this._context.frame._mainContext()); - }, this._page._timeoutSettings.timeout(options), 'elementHandle.waitForSelector'); + }, this._page._timeoutSettings.timeout(options)); } async _adoptTo(context: FrameExecutionContext): Promise> { diff --git a/src/frames.ts b/src/frames.ts index 2c88073fbc..cfa5f484d3 100644 --- a/src/frames.ts +++ b/src/frames.ts @@ -378,11 +378,6 @@ export class Frame { return this._page; } - private _apiName(method: string) { - const subject = this._page._callingPageAPI ? 'page' : 'frame'; - return `${subject}.${method}`; - } - _onLifecycleEvent(event: types.LifecycleEvent) { if (this._firedLifecycleEvents.has(event)) return; @@ -434,7 +429,7 @@ export class Frame { } async goto(url: string, options: types.GotoOptions = {}): Promise { - return runNavigationTask(this, options, this._apiName('goto'), async progress => { + return runNavigationTask(this, options, async progress => { const waitUntil = verifyLifecycle('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil); progress.logger.info(`navigating to "${url}", waiting until "${waitUntil}"`); const headers = (this._page._state.extraHTTPHeaders || {}); @@ -478,7 +473,7 @@ export class Frame { } async waitForNavigation(options: types.WaitForNavigationOptions = {}): Promise { - return runNavigationTask(this, options, this._apiName('waitForNavigation'), async progress => { + return runNavigationTask(this, options, async progress => { const toUrl = typeof options.url === 'string' ? ` to "${options.url}"` : ''; const waitUntil = verifyLifecycle('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil); progress.logger.info(`waiting for navigation${toUrl} until "${waitUntil}"`); @@ -502,7 +497,7 @@ export class Frame { } async waitForLoadState(state: types.LifecycleEvent = 'load', options: types.TimeoutOptions = {}): Promise { - return runNavigationTask(this, options, this._apiName('waitForLoadState'), progress => this._waitForLoadState(progress, state)); + return runNavigationTask(this, options, progress => this._waitForLoadState(progress, state)); } async _waitForLoadState(progress: Progress, state: types.LifecycleEvent): Promise { @@ -578,7 +573,7 @@ export class Frame { } const handle = result.asElement() as dom.ElementHandle; return handle._adoptTo(await this._mainContext()); - }, this._page._timeoutSettings.timeout(options), this._apiName('waitForSelector')); + }, this._page._timeoutSettings.timeout(options)); } async dispatchEvent(selector: string, type: string, eventInit?: Object, options: types.TimeoutOptions = {}): Promise { @@ -588,7 +583,7 @@ export class Frame { progress.logger.info(`Dispatching "${type}" event on selector "${selector}"...`); // Note: we always dispatch events in the main world. await this._scheduleRerunnableTask(progress, 'main', task); - }, this._page._timeoutSettings.timeout(options), this._apiName('dispatchEvent')); + }, this._page._timeoutSettings.timeout(options)); } async $eval(selector: string, pageFunction: js.FuncOn, arg: Arg): Promise; @@ -638,7 +633,7 @@ export class Frame { } async setContent(html: string, options: types.NavigateOptions = {}): Promise { - return runNavigationTask(this, options, this._apiName('setContent'), async progress => { + return runNavigationTask(this, options, async progress => { const waitUntil = options.waitUntil === undefined ? 'load' : options.waitUntil; progress.logger.info(`setting frame content, waiting until "${waitUntil}"`); const tag = `--playwright--set--content--${this._id}--${++this._setContentCounter}--`; @@ -823,8 +818,7 @@ export class Frame { private async _retryWithSelectorIfNotConnected( selector: string, options: types.TimeoutOptions, - action: (progress: Progress, handle: dom.ElementHandle) => Promise, - apiName: string): Promise { + action: (progress: Progress, handle: dom.ElementHandle) => Promise): Promise { const info = selectors._parseSelector(selector); return this._page._runAbortableTask(async progress => { while (progress.isRunning()) { @@ -846,23 +840,23 @@ export class Frame { return result; } return undefined as any; - }, this._page._timeoutSettings.timeout(options), apiName); + }, this._page._timeoutSettings.timeout(options)); } async click(selector: string, options: types.MouseClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions = {}) { - await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._click(progress, options), this._apiName('click')); + await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._click(progress, options)); } async dblclick(selector: string, options: types.MouseMultiClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions = {}) { - await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._dblclick(progress, options), this._apiName('dblclick')); + await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._dblclick(progress, options)); } async fill(selector: string, value: string, options: types.NavigatingActionWaitOptions = {}) { - await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._fill(progress, value, options), this._apiName('fill')); + await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._fill(progress, value, options)); } async focus(selector: string, options: types.TimeoutOptions = {}) { - await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._focus(progress), this._apiName('focus')); + await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._focus(progress)); } async textContent(selector: string, options: types.TimeoutOptions = {}): Promise { @@ -871,7 +865,7 @@ export class Frame { return this._page._runAbortableTask(async progress => { progress.logger.info(` retrieving textContent from "${selector}"`); return this._scheduleRerunnableTask(progress, info.world, task); - }, this._page._timeoutSettings.timeout(options), this._apiName('textContent')); + }, this._page._timeoutSettings.timeout(options)); } async innerText(selector: string, options: types.TimeoutOptions = {}): Promise { @@ -881,7 +875,7 @@ export class Frame { progress.logger.info(` retrieving innerText from "${selector}"`); const result = dom.throwFatalDOMError(await this._scheduleRerunnableTask(progress, info.world, task)); return result.innerText; - }, this._page._timeoutSettings.timeout(options), this._apiName('innerText')); + }, this._page._timeoutSettings.timeout(options)); } async innerHTML(selector: string, options: types.TimeoutOptions = {}): Promise { @@ -890,7 +884,7 @@ export class Frame { return this._page._runAbortableTask(async progress => { progress.logger.info(` retrieving innerHTML from "${selector}"`); return this._scheduleRerunnableTask(progress, info.world, task); - }, this._page._timeoutSettings.timeout(options), this._apiName('innerHTML')); + }, this._page._timeoutSettings.timeout(options)); } async getAttribute(selector: string, name: string, options: types.TimeoutOptions = {}): Promise { @@ -899,35 +893,35 @@ export class Frame { return this._page._runAbortableTask(async progress => { progress.logger.info(` retrieving attribute "${name}" from "${selector}"`); return this._scheduleRerunnableTask(progress, info.world, task); - }, this._page._timeoutSettings.timeout(options), this._apiName('getAttribute')); + }, this._page._timeoutSettings.timeout(options)); } async hover(selector: string, options: types.PointerActionOptions & types.PointerActionWaitOptions = {}) { - await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._hover(progress, options), this._apiName('hover')); + await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._hover(progress, options)); } async selectOption(selector: string, values: string | dom.ElementHandle | types.SelectOption | string[] | dom.ElementHandle[] | types.SelectOption[] | null, options: types.NavigatingActionWaitOptions = {}): Promise { - return this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._selectOption(progress, values, options), this._apiName('selectOption')); + return this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._selectOption(progress, values, options)); } async setInputFiles(selector: string, files: string | types.FilePayload | string[] | types.FilePayload[], options: types.NavigatingActionWaitOptions = {}): Promise { - await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._setInputFiles(progress, files, options), this._apiName('setInputFiles')); + await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._setInputFiles(progress, files, options)); } async type(selector: string, text: string, options: { delay?: number } & types.NavigatingActionWaitOptions = {}) { - await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._type(progress, text, options), this._apiName('type')); + await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._type(progress, text, options)); } async press(selector: string, key: string, options: { delay?: number } & types.NavigatingActionWaitOptions = {}) { - await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._press(progress, key, options), this._apiName('press')); + await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._press(progress, key, options)); } async check(selector: string, options: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions = {}) { - await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._setChecked(progress, true, options), this._apiName('check')); + await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._setChecked(progress, true, options)); } async uncheck(selector: string, options: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions = {}) { - await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._setChecked(progress, false, options), this._apiName('uncheck')); + await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._setChecked(progress, false, options)); } async waitForTimeout(timeout: number) { @@ -957,7 +951,7 @@ export class Frame { }, { predicateBody, polling, arg }); return this._page._runAbortableTask( progress => this._scheduleRerunnableHandleTask(progress, 'main', task), - this._page._timeoutSettings.timeout(options), this._apiName('waitForFunction')); + this._page._timeoutSettings.timeout(options)); } async title(): Promise { @@ -1134,9 +1128,9 @@ class SignalBarrier { } } -async function runNavigationTask(frame: Frame, options: types.TimeoutOptions, apiName: string, task: (progress: Progress) => Promise): Promise { +async function runNavigationTask(frame: Frame, options: types.TimeoutOptions, task: (progress: Progress) => Promise): Promise { const page = frame._page; - const controller = new ProgressController(page._logger, page._timeoutSettings.navigationTimeout(options), apiName); + const controller = new ProgressController(page._logger, page._timeoutSettings.navigationTimeout(options)); page._disconnectedPromise.then(() => controller.abort(new Error('Navigation failed because page was closed!'))); page._crashedPromise.then(() => controller.abort(new Error('Navigation failed because page crashed!'))); frame._detachedPromise.then(() => controller.abort(new Error('Navigating frame was detached!'))); diff --git a/src/page.ts b/src/page.ts index 350a5eb897..95f4b1c6a9 100644 --- a/src/page.ts +++ b/src/page.ts @@ -117,7 +117,6 @@ export class Page extends EventEmitter { readonly coverage: any; _routes: { url: types.URLMatch, handler: network.RouteHandler }[] = []; _ownedContext: BrowserContext | undefined; - _callingPageAPI = false; constructor(delegate: PageDelegate, browserContext: BrowserContextBase) { super(); @@ -168,10 +167,10 @@ export class Page extends EventEmitter { this._disconnectedCallback(new Error('Page closed')); } - async _runAbortableTask(task: (progress: Progress) => Promise, timeout: number, apiName: string): Promise { + async _runAbortableTask(task: (progress: Progress) => Promise, timeout: number): Promise { return runAbortableTask(async progress => { return task(progress); - }, this._logger, timeout, apiName); + }, this._logger, timeout); } async _onFileChooserOpened(handle: dom.ElementHandle) { @@ -219,63 +218,6 @@ export class Page extends EventEmitter { this._timeoutSettings.setDefaultTimeout(timeout); } - async $(selector: string): Promise | null> { - return this._attributeToPage(() => this.mainFrame().$(selector)); - } - - async waitForSelector(selector: string, options?: types.WaitForElementOptions): Promise | null> { - return this._attributeToPage(() => this.mainFrame().waitForSelector(selector, options)); - } - - async dispatchEvent(selector: string, type: string, eventInit?: Object, options?: types.TimeoutOptions): Promise { - return this._attributeToPage(() => this.mainFrame().dispatchEvent(selector, type, eventInit, options)); - } - - async evaluateHandle(pageFunction: js.Func1, arg: Arg): Promise>; - async evaluateHandle(pageFunction: js.Func1, arg?: any): Promise>; - async evaluateHandle(pageFunction: js.Func1, arg: Arg): Promise> { - assertMaxArguments(arguments.length, 2); - return this._attributeToPage(() => this.mainFrame().evaluateHandle(pageFunction, arg)); - } - - async _evaluateExpressionHandle(expression: string, isFunction: boolean, arg: any): Promise { - return this._attributeToPage(() => this.mainFrame()._evaluateExpressionHandle(expression, isFunction, arg)); - } - - async $eval(selector: string, pageFunction: js.FuncOn, arg: Arg): Promise; - async $eval(selector: string, pageFunction: js.FuncOn, arg?: any): Promise; - async $eval(selector: string, pageFunction: js.FuncOn, arg: Arg): Promise { - assertMaxArguments(arguments.length, 3); - return this._attributeToPage(() => this.mainFrame().$eval(selector, pageFunction, arg)); - } - - async _$evalExpression(selector: string, expression: string, isFunction: boolean, arg: any): Promise { - return this._attributeToPage(() => this.mainFrame()._$evalExpression(selector, expression, isFunction, arg)); - } - - async $$eval(selector: string, pageFunction: js.FuncOn, arg: Arg): Promise; - async $$eval(selector: string, pageFunction: js.FuncOn, arg?: any): Promise; - async $$eval(selector: string, pageFunction: js.FuncOn, arg: Arg): Promise { - assertMaxArguments(arguments.length, 3); - return this._attributeToPage(() => this.mainFrame().$$eval(selector, pageFunction, arg)); - } - - async _$$evalExpression(selector: string, expression: string, isFunction: boolean, arg: any): Promise { - return this._attributeToPage(() => this.mainFrame()._$$evalExpression(selector, expression, isFunction, arg)); - } - - async $$(selector: string): Promise[]> { - return this._attributeToPage(() => this.mainFrame().$$(selector)); - } - - async addScriptTag(options: { url?: string; path?: string; content?: string; type?: string; }): Promise { - return this._attributeToPage(() => this.mainFrame().addScriptTag(options)); - } - - async addStyleTag(options: { url?: string; path?: string; content?: string; }): Promise { - return this._attributeToPage(() => this.mainFrame().addStyleTag(options)); - } - async exposeFunction(name: string, playwrightFunction: Function) { await this.exposeBinding(name, (options, ...args: any) => playwrightFunction(...args)); } @@ -310,36 +252,12 @@ export class Page extends EventEmitter { this.emit(Events.Page.Console, message); } - url(): string { - return this._attributeToPage(() => this.mainFrame().url()); - } - - async content(): Promise { - return this._attributeToPage(() => this.mainFrame().content()); - } - - async setContent(html: string, options?: types.NavigateOptions): Promise { - return this._attributeToPage(() => this.mainFrame().setContent(html, options)); - } - - async goto(url: string, options?: types.GotoOptions): Promise { - return this._attributeToPage(() => this.mainFrame().goto(url, options)); - } - async reload(options?: types.NavigateOptions): Promise { - const waitPromise = this.waitForNavigation(options); + const waitPromise = this.mainFrame().waitForNavigation(options); await this._delegate.reload(); return waitPromise; } - async waitForLoadState(state?: types.LifecycleEvent, options?: types.TimeoutOptions): Promise { - return this._attributeToPage(() => this.mainFrame().waitForLoadState(state, options)); - } - - async waitForNavigation(options?: types.WaitForNavigationOptions): Promise { - return this._attributeToPage(() => this.mainFrame().waitForNavigation(options)); - } - async waitForRequest(urlOrPredicate: string | RegExp | ((r: network.Request) => boolean), options: types.TimeoutOptions = {}): Promise { const predicate = (request: network.Request) => { if (helper.isString(urlOrPredicate) || helper.isRegExp(urlOrPredicate)) @@ -360,7 +278,7 @@ export class Page extends EventEmitter { async waitForEvent(event: string, optionsOrPredicate: types.WaitForEventOptions = {}): Promise { const options = typeof optionsOrPredicate === 'function' ? { predicate: optionsOrPredicate } : optionsOrPredicate; - const progressController = new ProgressController(this._logger, this._timeoutSettings.timeout(options), 'page.waitForEvent'); + const progressController = new ProgressController(this._logger, this._timeoutSettings.timeout(options)); this._disconnectedPromise.then(error => progressController.abort(error)); if (event !== Events.Page.Crash) this._crashedPromise.then(error => progressController.abort(error)); @@ -368,7 +286,7 @@ export class Page extends EventEmitter { } async goBack(options?: types.NavigateOptions): Promise { - const waitPromise = this.waitForNavigation(options); + const waitPromise = this.mainFrame().waitForNavigation(options); const result = await this._delegate.goBack(); if (!result) { waitPromise.catch(() => {}); @@ -378,7 +296,7 @@ export class Page extends EventEmitter { } async goForward(options?: types.NavigateOptions): Promise { - const waitPromise = this.waitForNavigation(options); + const waitPromise = this.mainFrame().waitForNavigation(options); const result = await this._delegate.goForward(); if (!result) { waitPromise.catch(() => {}); @@ -412,17 +330,6 @@ export class Page extends EventEmitter { await this._delegate.bringToFront(); } - async evaluate(pageFunction: js.Func1, arg: Arg): Promise; - async evaluate(pageFunction: js.Func1, arg?: any): Promise; - async evaluate(pageFunction: js.Func1, arg: Arg): Promise { - assertMaxArguments(arguments.length, 2); - return this._attributeToPage(() => this.mainFrame().evaluate(pageFunction, arg)); - } - - async _evaluateExpression(expression: string, isFunction: boolean, arg: any): Promise { - return this._attributeToPage(() => this.mainFrame()._evaluateExpression(expression, isFunction, arg)); - } - async addInitScript(script: Function | string | { path?: string, content?: string }, arg?: any) { const source = await helper.evaluationScript(script, arg); await this._addInitScriptExpression(source); @@ -482,11 +389,7 @@ export class Page extends EventEmitter { async screenshot(options: types.ScreenshotOptions = {}): Promise { return this._runAbortableTask( progress => this._screenshotter.screenshotPage(progress, options), - this._timeoutSettings.timeout(options), 'page.screenshot'); - } - - async title(): Promise { - return this._attributeToPage(() => this.mainFrame().title()); + this._timeoutSettings.timeout(options)); } async close(options?: { runBeforeUnload?: boolean }) { @@ -513,89 +416,10 @@ export class Page extends EventEmitter { return this._closedState === 'closed'; } - private _attributeToPage(func: () => T): T { - try { - this._callingPageAPI = true; - return func(); - } finally { - this._callingPageAPI = false; - } - } - - async click(selector: string, options?: types.MouseClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions) { - return this._attributeToPage(() => this.mainFrame().click(selector, options)); - } - - async dblclick(selector: string, options?: types.MouseMultiClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions) { - return this._attributeToPage(() => this.mainFrame().dblclick(selector, options)); - } - - async fill(selector: string, value: string, options?: types.NavigatingActionWaitOptions) { - return this._attributeToPage(() => this.mainFrame().fill(selector, value, options)); - } - - async focus(selector: string, options?: types.TimeoutOptions) { - return this._attributeToPage(() => this.mainFrame().focus(selector, options)); - } - - async textContent(selector: string, options?: types.TimeoutOptions): Promise { - return this._attributeToPage(() => this.mainFrame().textContent(selector, options)); - } - - async innerText(selector: string, options?: types.TimeoutOptions): Promise { - return this._attributeToPage(() => this.mainFrame().innerText(selector, options)); - } - - async innerHTML(selector: string, options?: types.TimeoutOptions): Promise { - return this._attributeToPage(() => this.mainFrame().innerHTML(selector, options)); - } - - async getAttribute(selector: string, name: string, options?: types.TimeoutOptions): Promise { - return this._attributeToPage(() => this.mainFrame().getAttribute(selector, name, options)); - } - - async hover(selector: string, options?: types.PointerActionOptions & types.PointerActionWaitOptions) { - return this._attributeToPage(() => this.mainFrame().hover(selector, options)); - } - - async selectOption(selector: string, values: string | dom.ElementHandle | types.SelectOption | string[] | dom.ElementHandle[] | types.SelectOption[] | null, options?: types.NavigatingActionWaitOptions): Promise { - return this._attributeToPage(() => this.mainFrame().selectOption(selector, values, options)); - } - - async setInputFiles(selector: string, files: string | types.FilePayload | string[] | types.FilePayload[], options?: types.NavigatingActionWaitOptions): Promise { - return this._attributeToPage(() => this.mainFrame().setInputFiles(selector, files, options)); - } - - async type(selector: string, text: string, options?: { delay?: number } & types.NavigatingActionWaitOptions) { - return this._attributeToPage(() => this.mainFrame().type(selector, text, options)); - } - - async press(selector: string, key: string, options?: { delay?: number } & types.NavigatingActionWaitOptions) { - return this._attributeToPage(() => this.mainFrame().press(selector, key, options)); - } - - async check(selector: string, options?: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions) { - return this._attributeToPage(() => this.mainFrame().check(selector, options)); - } - - async uncheck(selector: string, options?: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions) { - return this._attributeToPage(() => this.mainFrame().uncheck(selector, options)); - } - async waitForTimeout(timeout: number) { await this.mainFrame().waitForTimeout(timeout); } - async waitForFunction(pageFunction: js.Func1, arg: Arg, options?: types.WaitForFunctionOptions): Promise>; - async waitForFunction(pageFunction: js.Func1, arg?: any, options?: types.WaitForFunctionOptions): Promise>; - async waitForFunction(pageFunction: js.Func1, arg: Arg, options?: types.WaitForFunctionOptions): Promise> { - return this._attributeToPage(() => this.mainFrame().waitForFunction(pageFunction, arg, options)); - } - - async _waitForFunctionExpression(expression: string, isFunction: boolean, arg: any, options: types.WaitForFunctionOptions = {}): Promise> { - return this._attributeToPage(() => this.mainFrame()._waitForFunctionExpression(expression, isFunction, arg, options)); - } - workers(): Worker[] { return [...this._workers.values()]; } diff --git a/src/progress.ts b/src/progress.ts index dec9c02f31..73aac79613 100644 --- a/src/progress.ts +++ b/src/progress.ts @@ -28,16 +28,11 @@ export interface Progress { throwIfAborted(): void; } -export async function runAbortableTask(task: (progress: Progress) => Promise, logger: Logger, timeout: number, apiName: string): Promise { - const controller = new ProgressController(logger, timeout, apiName); +export async function runAbortableTask(task: (progress: Progress) => Promise, logger: Logger, timeout: number): Promise { + const controller = new ProgressController(logger, timeout); return controller.run(task); } -let useApiName = true; -export function setUseApiName(value: boolean) { - useApiName = value; -} - export class ProgressController { // Promise and callback that forcefully abort the progress. // This promise always rejects. @@ -54,12 +49,10 @@ export class ProgressController { private _logger: Logger; private _state: 'before' | 'running' | 'aborted' | 'finished' = 'before'; - private _apiName: string; private _deadline: number; private _timeout: number; - constructor(logger: Logger, timeout: number, apiName: string) { - this._apiName = apiName; + constructor(logger: Logger, timeout: number) { this._logger = logger; this._timeout = timeout; @@ -74,7 +67,7 @@ export class ProgressController { assert(this._state === 'before'); this._state = 'running'; - const loggerScope = this._logger.createScope(useApiName ? this._apiName : undefined, true); + const loggerScope = this._logger.createScope(undefined, true); const progress: Progress = { aborted: this._abortedPromise, @@ -105,7 +98,6 @@ export class ProgressController { } catch (e) { this._aborted(); rewriteErrorMessage(e, - (useApiName ? `${this._apiName}: ` : '') + e.message + formatLogRecording(loggerScope.recording()) + kLoggingNote); diff --git a/src/rpc/inprocess.ts b/src/rpc/inprocess.ts index 27ecd26eab..017b55a7bb 100644 --- a/src/rpc/inprocess.ts +++ b/src/rpc/inprocess.ts @@ -18,14 +18,11 @@ import { DispatcherConnection } from './server/dispatcher'; import type { Playwright as PlaywrightImpl } from '../server/playwright'; import type { Playwright as PlaywrightAPI } from './client/playwright'; import { PlaywrightDispatcher } from './server/playwrightDispatcher'; -import { setUseApiName } from '../progress'; import { Connection } from './client/connection'; import { isUnderTest } from '../helper'; import { BrowserServerLauncherImpl } from './browserServerImpl'; export function setupInProcess(playwright: PlaywrightImpl): PlaywrightAPI { - setUseApiName(false); - const clientConnection = new Connection(); const dispatcherConnection = new DispatcherConnection(); diff --git a/src/rpc/server.ts b/src/rpc/server.ts index a1480e1196..4a8a61e328 100644 --- a/src/rpc/server.ts +++ b/src/rpc/server.ts @@ -19,11 +19,8 @@ import { DispatcherConnection } from './server/dispatcher'; import { Playwright } from '../server/playwright'; import { PlaywrightDispatcher } from './server/playwrightDispatcher'; import { Electron } from '../server/electron'; -import { setUseApiName } from '../progress'; import { gracefullyCloseAll } from '../server/processLauncher'; -setUseApiName(false); - const dispatcherConnection = new DispatcherConnection(); const transport = new Transport(process.stdout, process.stdin); transport.onclose = async () => { diff --git a/src/rpc/server/pageDispatcher.ts b/src/rpc/server/pageDispatcher.ts index ca124bbde5..bd43139fb4 100644 --- a/src/rpc/server/pageDispatcher.ts +++ b/src/rpc/server/pageDispatcher.ts @@ -149,10 +149,6 @@ export class PageDispatcher extends Dispatcher implements this._page.removeListener(Events.Page.FileChooser, this._onFileChooser); } - async title() { - return await this._page.title(); - } - async keyboardDown(params: { key: string }): Promise { await this._page.keyboard.down(params.key); } diff --git a/src/server/browserType.ts b/src/server/browserType.ts index c6ef15896d..dce38a9662 100644 --- a/src/server/browserType.ts +++ b/src/server/browserType.ts @@ -85,8 +85,7 @@ export abstract class BrowserTypeBase implements BrowserType { assert(!(options as any).port, 'Cannot specify a port without launching as a server.'); options = validateLaunchOptions(options); const loggers = new Loggers(options.logger); - const label = 'browserType.launch'; - const browser = await runAbortableTask(progress => this._innerLaunch(progress, options, loggers, undefined), loggers.browser, TimeoutSettings.timeout(options), label).catch(e => { throw this._rewriteStartupError(e, label); }); + const browser = await runAbortableTask(progress => this._innerLaunch(progress, options, loggers, undefined), loggers.browser, TimeoutSettings.timeout(options)).catch(e => { throw this._rewriteStartupError(e); }); return browser; } @@ -95,8 +94,7 @@ export abstract class BrowserTypeBase implements BrowserType { options = validateLaunchOptions(options); const persistent = validateBrowserContextOptions(options); const loggers = new Loggers(options.logger); - const label = 'browserType.launchPersistentContext'; - const browser = await runAbortableTask(progress => this._innerLaunch(progress, options, loggers, persistent, userDataDir), loggers.browser, TimeoutSettings.timeout(options), label).catch(e => { throw this._rewriteStartupError(e, label); }); + const browser = await runAbortableTask(progress => this._innerLaunch(progress, options, loggers, persistent, userDataDir), loggers.browser, TimeoutSettings.timeout(options)).catch(e => { throw this._rewriteStartupError(e); }); return browser._defaultContext!; } @@ -224,7 +222,7 @@ export abstract class BrowserTypeBase implements BrowserType { abstract _connectToTransport(transport: ConnectionTransport, options: BrowserOptions): Promise; abstract _amendEnvironment(env: Env, userDataDir: string, executable: string, browserArguments: string[]): Env; abstract _amendArguments(browserArguments: string[]): string[]; - abstract _rewriteStartupError(error: Error, prefix: string): Error; + abstract _rewriteStartupError(error: Error): Error; abstract _attemptToGracefullyCloseBrowser(transport: ConnectionTransport): void; } diff --git a/src/server/chromium.ts b/src/server/chromium.ts index 060cb30a6e..164b5544c0 100644 --- a/src/server/chromium.ts +++ b/src/server/chromium.ts @@ -61,13 +61,13 @@ export class Chromium extends BrowserTypeBase { return CRBrowser.connect(transport, options, devtools); } - _rewriteStartupError(error: Error, prefix: string): Error { + _rewriteStartupError(error: Error): Error { // These error messages are taken from Chromium source code as of July, 2020: // https://github.com/chromium/chromium/blob/70565f67e79f79e17663ad1337dc6e63ee207ce9/content/browser/zygote_host/zygote_host_impl_linux.cc if (!error.message.includes('crbug.com/357670') && !error.message.includes('No usable sandbox!') && !error.message.includes('crbug.com/638180')) return error; return rewriteErrorMessage(error, [ - `${prefix}: Chromium sandboxing failed!`, + `Chromium sandboxing failed!`, `================================`, `To workaround sandboxing issues, do either of the following:`, ` - (preferred): Configure environment to support sandboxing: https://github.com/microsoft/playwright/blob/master/docs/troubleshooting.md`, diff --git a/src/server/electron.ts b/src/server/electron.ts index 53c66a4336..aecac18fa8 100644 --- a/src/server/electron.ts +++ b/src/server/electron.ts @@ -94,7 +94,7 @@ export class ElectronApplication extends EventEmitter { this._windows.delete(page); }); this._windows.add(page); - await page.waitForLoadState('domcontentloaded').catch(e => {}); // can happen after detach + await page.mainFrame().waitForLoadState('domcontentloaded').catch(e => {}); // can happen after detach this.emit(ElectronEvents.ElectronApplication.Window, page); } @@ -136,7 +136,7 @@ export class ElectronApplication extends EventEmitter { async waitForEvent(event: string, optionsOrPredicate: types.WaitForEventOptions = {}): Promise { const options = typeof optionsOrPredicate === 'function' ? { predicate: optionsOrPredicate } : optionsOrPredicate; - const progressController = new ProgressController(this._apiLogger, this._timeoutSettings.timeout(options), 'electron.waitForEvent'); + const progressController = new ProgressController(this._apiLogger, this._timeoutSettings.timeout(options)); if (event !== ElectronEvents.ElectronApplication.Close) this._browserContext._closePromise.then(error => progressController.abort(error)); return progressController.run(progress => helper.waitForEvent(progress, this, event, options.predicate).promise); @@ -212,6 +212,6 @@ export class Electron { app = new ElectronApplication(loggers, browser, nodeConnection); await app._init(); return app; - }, loggers.browser, TimeoutSettings.timeout(options), 'electron.launch'); + }, loggers.browser, TimeoutSettings.timeout(options)); } } diff --git a/src/server/firefox.ts b/src/server/firefox.ts index d41655a4f9..fb4c4eb030 100644 --- a/src/server/firefox.ts +++ b/src/server/firefox.ts @@ -36,7 +36,7 @@ export class Firefox extends BrowserTypeBase { return FFBrowser.connect(transport, options); } - _rewriteStartupError(error: Error, prefix: string): Error { + _rewriteStartupError(error: Error): Error { return error; } diff --git a/src/server/webkit.ts b/src/server/webkit.ts index 4001108e99..1f3d24de00 100644 --- a/src/server/webkit.ts +++ b/src/server/webkit.ts @@ -42,7 +42,7 @@ export class WebKit extends BrowserTypeBase { return browserArguments; } - _rewriteStartupError(error: Error, prefix: string): Error { + _rewriteStartupError(error: Error): Error { return error; } diff --git a/test/page-event-crash.spec.ts b/test/page-event-crash.spec.ts index 6021d0c328..daca21574b 100644 --- a/test/page-event-crash.spec.ts +++ b/test/page-event-crash.spec.ts @@ -22,7 +22,7 @@ const CRASH_FAIL = (FFOX && WIN) || WIRE; // Firefox Win: it just doesn't crash sometimes. function crash(pageImpl) { if (CHROMIUM) - pageImpl.goto('chrome://crash').catch(e => {}); + pageImpl.mainFrame().goto('chrome://crash').catch(e => {}); else if (WEBKIT) pageImpl._delegate._session.send('Page.crash', {}).catch(e => {}); else if (FFOX)