chore: remove apiName plumbing and some unused methods from server side (#3481)

We append apiName where needed on the client instead.
This commit is contained in:
Dmitry Gozman 2020-08-14 18:25:32 -07:00 committed by GitHub
parent 244c2f37b6
commit bc23324878
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 67 additions and 269 deletions

View file

@ -86,7 +86,7 @@ export abstract class BrowserContextBase extends EventEmitter implements Browser
async waitForEvent(event: string, optionsOrPredicate: types.WaitForEventOptions = {}): Promise<any> { async waitForEvent(event: string, optionsOrPredicate: types.WaitForEventOptions = {}): Promise<any> {
const options = typeof optionsOrPredicate === 'function' ? { predicate: optionsOrPredicate } : optionsOrPredicate; 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) if (event !== Events.BrowserContext.Close)
this._closePromise.then(error => progressController.abort(error)); this._closePromise.then(error => progressController.abort(error));
return progressController.run(progress => helper.waitForEvent(progress, this, event, options.predicate).promise); 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) if (!this.pages().length)
await this.waitForEvent('page'); await this.waitForEvent('page');
const pages = this.pages(); const pages = this.pages();
await pages[0].waitForLoadState(); await pages[0].mainFrame().waitForLoadState();
if (pages.length !== 1 || pages[0].url() !== 'about:blank') 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].url()})`); 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) { if (this._options.isMobile || this._options.locale) {
// Workaround for: // Workaround for:
// - chromium fails to change isMobile for existing page; // - chromium fails to change isMobile for existing page;

View file

@ -223,7 +223,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
async scrollIntoViewIfNeeded(options: types.TimeoutOptions = {}) { async scrollIntoViewIfNeeded(options: types.TimeoutOptions = {}) {
return this._page._runAbortableTask( return this._page._runAbortableTask(
progress => this._waitAndScrollIntoViewIfNeeded(progress), 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'> { private async _waitForVisible(progress: Progress): Promise<'error:notconnected' | 'done'> {
@ -376,7 +376,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
return this._page._runAbortableTask(async progress => { return this._page._runAbortableTask(async progress => {
const result = await this._hover(progress, options); const result = await this._hover(progress, options);
return assertDone(throwRetargetableDOMError(result)); 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'> { _hover(progress: Progress, options: types.PointerActionOptions & types.PointerActionWaitOptions): Promise<'error:notconnected' | 'done'> {
@ -387,7 +387,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
return this._page._runAbortableTask(async progress => { return this._page._runAbortableTask(async progress => {
const result = await this._click(progress, options); const result = await this._click(progress, options);
return assertDone(throwRetargetableDOMError(result)); 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'> { _click(progress: Progress, options: types.MouseClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> {
@ -398,7 +398,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
return this._page._runAbortableTask(async progress => { return this._page._runAbortableTask(async progress => {
const result = await this._dblclick(progress, options); const result = await this._dblclick(progress, options);
return assertDone(throwRetargetableDOMError(result)); 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'> { _dblclick(progress: Progress, options: types.MouseMultiClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> {
@ -409,7 +409,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
return this._page._runAbortableTask(async progress => { return this._page._runAbortableTask(async progress => {
const result = await this._selectOption(progress, values, options); const result = await this._selectOption(progress, values, options);
return throwRetargetableDOMError(result); 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<string[] | 'error:notconnected'> { async _selectOption(progress: Progress, values: string | ElementHandle | types.SelectOption | string[] | ElementHandle[] | types.SelectOption[] | null, options: types.NavigatingActionWaitOptions): Promise<string[] | 'error:notconnected'> {
@ -444,7 +444,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
return this._page._runAbortableTask(async progress => { return this._page._runAbortableTask(async progress => {
const result = await this._fill(progress, value, options); const result = await this._fill(progress, value, options);
assertDone(throwRetargetableDOMError(result)); 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'> { async _fill(progress: Progress, value: string, options: types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> {
@ -483,14 +483,14 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
const pollHandler = new InjectedScriptPollHandler(progress, poll); const pollHandler = new InjectedScriptPollHandler(progress, poll);
const result = throwFatalDOMError(await pollHandler.finish()); const result = throwFatalDOMError(await pollHandler.finish());
assertDone(throwRetargetableDOMError(result)); 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 = {}) { async setInputFiles(files: string | types.FilePayload | string[] | types.FilePayload[], options: types.NavigatingActionWaitOptions = {}) {
return this._page._runAbortableTask(async progress => { return this._page._runAbortableTask(async progress => {
const result = await this._setInputFiles(progress, files, options); const result = await this._setInputFiles(progress, files, options);
return assertDone(throwRetargetableDOMError(result)); 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'> { 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<T extends Node = Node> extends js.JSHandle<T> {
return this._page._runAbortableTask(async progress => { return this._page._runAbortableTask(async progress => {
const result = await this._focus(progress); const result = await this._focus(progress);
return assertDone(throwRetargetableDOMError(result)); return assertDone(throwRetargetableDOMError(result));
}, 0, 'elementHandle.focus'); }, 0);
} }
async _focus(progress: Progress, resetSelectionIfNotFocused?: boolean): Promise<'error:notconnected' | 'done'> { async _focus(progress: Progress, resetSelectionIfNotFocused?: boolean): Promise<'error:notconnected' | 'done'> {
@ -530,7 +530,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
return this._page._runAbortableTask(async progress => { return this._page._runAbortableTask(async progress => {
const result = await this._type(progress, text, options); const result = await this._type(progress, text, options);
return assertDone(throwRetargetableDOMError(result)); 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'> { async _type(progress: Progress, text: string, options: { delay?: number } & types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> {
@ -549,7 +549,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
return this._page._runAbortableTask(async progress => { return this._page._runAbortableTask(async progress => {
const result = await this._press(progress, key, options); const result = await this._press(progress, key, options);
return assertDone(throwRetargetableDOMError(result)); 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'> { async _press(progress: Progress, key: string, options: { delay?: number } & types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> {
@ -568,14 +568,14 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
return this._page._runAbortableTask(async progress => { return this._page._runAbortableTask(async progress => {
const result = await this._setChecked(progress, true, options); const result = await this._setChecked(progress, true, options);
return assertDone(throwRetargetableDOMError(result)); return assertDone(throwRetargetableDOMError(result));
}, this._page._timeoutSettings.timeout(options), 'elementHandle.check'); }, this._page._timeoutSettings.timeout(options));
} }
async uncheck(options: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions = {}) { async uncheck(options: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions = {}) {
return this._page._runAbortableTask(async progress => { return this._page._runAbortableTask(async progress => {
const result = await this._setChecked(progress, false, options); const result = await this._setChecked(progress, false, options);
return assertDone(throwRetargetableDOMError(result)); 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'> { async _setChecked(progress: Progress, state: boolean, options: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> {
@ -596,7 +596,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
async screenshot(options: types.ElementScreenshotOptions = {}): Promise<Buffer> { async screenshot(options: types.ElementScreenshotOptions = {}): Promise<Buffer> {
return this._page._runAbortableTask( return this._page._runAbortableTask(
progress => this._page._screenshotter.screenshotElement(progress, this, options), progress => this._page._screenshotter.screenshotElement(progress, this, options),
this._page._timeoutSettings.timeout(options), 'elementHandle.screenshot'); this._page._timeoutSettings.timeout(options));
} }
async $(selector: string): Promise<ElementHandle | null> { async $(selector: string): Promise<ElementHandle | null> {
@ -655,7 +655,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
} }
const handle = result.asElement() as ElementHandle<Element>; const handle = result.asElement() as ElementHandle<Element>;
return handle._adoptTo(await this._context.frame._mainContext()); 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<ElementHandle<T>> { async _adoptTo(context: FrameExecutionContext): Promise<ElementHandle<T>> {

View file

@ -378,11 +378,6 @@ export class Frame {
return this._page; return this._page;
} }
private _apiName(method: string) {
const subject = this._page._callingPageAPI ? 'page' : 'frame';
return `${subject}.${method}`;
}
_onLifecycleEvent(event: types.LifecycleEvent) { _onLifecycleEvent(event: types.LifecycleEvent) {
if (this._firedLifecycleEvents.has(event)) if (this._firedLifecycleEvents.has(event))
return; return;
@ -434,7 +429,7 @@ export class Frame {
} }
async goto(url: string, options: types.GotoOptions = {}): Promise<network.Response | null> { async goto(url: string, options: types.GotoOptions = {}): Promise<network.Response | null> {
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); const waitUntil = verifyLifecycle('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil);
progress.logger.info(`navigating to "${url}", waiting until "${waitUntil}"`); progress.logger.info(`navigating to "${url}", waiting until "${waitUntil}"`);
const headers = (this._page._state.extraHTTPHeaders || {}); const headers = (this._page._state.extraHTTPHeaders || {});
@ -478,7 +473,7 @@ export class Frame {
} }
async waitForNavigation(options: types.WaitForNavigationOptions = {}): Promise<network.Response | null> { async waitForNavigation(options: types.WaitForNavigationOptions = {}): Promise<network.Response | null> {
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 toUrl = typeof options.url === 'string' ? ` to "${options.url}"` : '';
const waitUntil = verifyLifecycle('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil); const waitUntil = verifyLifecycle('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil);
progress.logger.info(`waiting for navigation${toUrl} until "${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<void> { async waitForLoadState(state: types.LifecycleEvent = 'load', options: types.TimeoutOptions = {}): Promise<void> {
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<void> { async _waitForLoadState(progress: Progress, state: types.LifecycleEvent): Promise<void> {
@ -578,7 +573,7 @@ export class Frame {
} }
const handle = result.asElement() as dom.ElementHandle<Element>; const handle = result.asElement() as dom.ElementHandle<Element>;
return handle._adoptTo(await this._mainContext()); 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<void> { async dispatchEvent(selector: string, type: string, eventInit?: Object, options: types.TimeoutOptions = {}): Promise<void> {
@ -588,7 +583,7 @@ export class Frame {
progress.logger.info(`Dispatching "${type}" event on selector "${selector}"...`); progress.logger.info(`Dispatching "${type}" event on selector "${selector}"...`);
// Note: we always dispatch events in the main world. // Note: we always dispatch events in the main world.
await this._scheduleRerunnableTask(progress, 'main', task); await this._scheduleRerunnableTask(progress, 'main', task);
}, this._page._timeoutSettings.timeout(options), this._apiName('dispatchEvent')); }, this._page._timeoutSettings.timeout(options));
} }
async $eval<R, Arg>(selector: string, pageFunction: js.FuncOn<Element, Arg, R>, arg: Arg): Promise<R>; async $eval<R, Arg>(selector: string, pageFunction: js.FuncOn<Element, Arg, R>, arg: Arg): Promise<R>;
@ -638,7 +633,7 @@ export class Frame {
} }
async setContent(html: string, options: types.NavigateOptions = {}): Promise<void> { async setContent(html: string, options: types.NavigateOptions = {}): Promise<void> {
return runNavigationTask(this, options, this._apiName('setContent'), async progress => { return runNavigationTask(this, options, async progress => {
const waitUntil = options.waitUntil === undefined ? 'load' : options.waitUntil; const waitUntil = options.waitUntil === undefined ? 'load' : options.waitUntil;
progress.logger.info(`setting frame content, waiting until "${waitUntil}"`); progress.logger.info(`setting frame content, waiting until "${waitUntil}"`);
const tag = `--playwright--set--content--${this._id}--${++this._setContentCounter}--`; const tag = `--playwright--set--content--${this._id}--${++this._setContentCounter}--`;
@ -823,8 +818,7 @@ export class Frame {
private async _retryWithSelectorIfNotConnected<R>( private async _retryWithSelectorIfNotConnected<R>(
selector: string, options: types.TimeoutOptions, selector: string, options: types.TimeoutOptions,
action: (progress: Progress, handle: dom.ElementHandle<Element>) => Promise<R | 'error:notconnected'>, action: (progress: Progress, handle: dom.ElementHandle<Element>) => Promise<R | 'error:notconnected'>): Promise<R> {
apiName: string): Promise<R> {
const info = selectors._parseSelector(selector); const info = selectors._parseSelector(selector);
return this._page._runAbortableTask(async progress => { return this._page._runAbortableTask(async progress => {
while (progress.isRunning()) { while (progress.isRunning()) {
@ -846,23 +840,23 @@ export class Frame {
return result; return result;
} }
return undefined as any; 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 = {}) { 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 = {}) { 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 = {}) { 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 = {}) { 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<string | null> { async textContent(selector: string, options: types.TimeoutOptions = {}): Promise<string | null> {
@ -871,7 +865,7 @@ export class Frame {
return this._page._runAbortableTask(async progress => { return this._page._runAbortableTask(async progress => {
progress.logger.info(` retrieving textContent from "${selector}"`); progress.logger.info(` retrieving textContent from "${selector}"`);
return this._scheduleRerunnableTask(progress, info.world, task); 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<string> { async innerText(selector: string, options: types.TimeoutOptions = {}): Promise<string> {
@ -881,7 +875,7 @@ export class Frame {
progress.logger.info(` retrieving innerText from "${selector}"`); progress.logger.info(` retrieving innerText from "${selector}"`);
const result = dom.throwFatalDOMError(await this._scheduleRerunnableTask(progress, info.world, task)); const result = dom.throwFatalDOMError(await this._scheduleRerunnableTask(progress, info.world, task));
return result.innerText; return result.innerText;
}, this._page._timeoutSettings.timeout(options), this._apiName('innerText')); }, this._page._timeoutSettings.timeout(options));
} }
async innerHTML(selector: string, options: types.TimeoutOptions = {}): Promise<string> { async innerHTML(selector: string, options: types.TimeoutOptions = {}): Promise<string> {
@ -890,7 +884,7 @@ export class Frame {
return this._page._runAbortableTask(async progress => { return this._page._runAbortableTask(async progress => {
progress.logger.info(` retrieving innerHTML from "${selector}"`); progress.logger.info(` retrieving innerHTML from "${selector}"`);
return this._scheduleRerunnableTask(progress, info.world, task); 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<string | null> { async getAttribute(selector: string, name: string, options: types.TimeoutOptions = {}): Promise<string | null> {
@ -899,35 +893,35 @@ export class Frame {
return this._page._runAbortableTask(async progress => { return this._page._runAbortableTask(async progress => {
progress.logger.info(` retrieving attribute "${name}" from "${selector}"`); progress.logger.info(` retrieving attribute "${name}" from "${selector}"`);
return this._scheduleRerunnableTask(progress, info.world, task); 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 = {}) { 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<string[]> { async selectOption(selector: string, values: string | dom.ElementHandle | types.SelectOption | string[] | dom.ElementHandle[] | types.SelectOption[] | null, options: types.NavigatingActionWaitOptions = {}): Promise<string[]> {
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<void> { async setInputFiles(selector: string, files: string | types.FilePayload | string[] | types.FilePayload[], options: types.NavigatingActionWaitOptions = {}): Promise<void> {
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 = {}) { 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 = {}) { 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 = {}) { 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 = {}) { 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) { async waitForTimeout(timeout: number) {
@ -957,7 +951,7 @@ export class Frame {
}, { predicateBody, polling, arg }); }, { predicateBody, polling, arg });
return this._page._runAbortableTask( return this._page._runAbortableTask(
progress => this._scheduleRerunnableHandleTask(progress, 'main', task), progress => this._scheduleRerunnableHandleTask(progress, 'main', task),
this._page._timeoutSettings.timeout(options), this._apiName('waitForFunction')); this._page._timeoutSettings.timeout(options));
} }
async title(): Promise<string> { async title(): Promise<string> {
@ -1134,9 +1128,9 @@ class SignalBarrier {
} }
} }
async function runNavigationTask<T>(frame: Frame, options: types.TimeoutOptions, apiName: string, task: (progress: Progress) => Promise<T>): Promise<T> { async function runNavigationTask<T>(frame: Frame, options: types.TimeoutOptions, task: (progress: Progress) => Promise<T>): Promise<T> {
const page = frame._page; 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._disconnectedPromise.then(() => controller.abort(new Error('Navigation failed because page was closed!')));
page._crashedPromise.then(() => controller.abort(new Error('Navigation failed because page crashed!'))); page._crashedPromise.then(() => controller.abort(new Error('Navigation failed because page crashed!')));
frame._detachedPromise.then(() => controller.abort(new Error('Navigating frame was detached!'))); frame._detachedPromise.then(() => controller.abort(new Error('Navigating frame was detached!')));

View file

@ -117,7 +117,6 @@ export class Page extends EventEmitter {
readonly coverage: any; readonly coverage: any;
_routes: { url: types.URLMatch, handler: network.RouteHandler }[] = []; _routes: { url: types.URLMatch, handler: network.RouteHandler }[] = [];
_ownedContext: BrowserContext | undefined; _ownedContext: BrowserContext | undefined;
_callingPageAPI = false;
constructor(delegate: PageDelegate, browserContext: BrowserContextBase) { constructor(delegate: PageDelegate, browserContext: BrowserContextBase) {
super(); super();
@ -168,10 +167,10 @@ export class Page extends EventEmitter {
this._disconnectedCallback(new Error('Page closed')); this._disconnectedCallback(new Error('Page closed'));
} }
async _runAbortableTask<T>(task: (progress: Progress) => Promise<T>, timeout: number, apiName: string): Promise<T> { async _runAbortableTask<T>(task: (progress: Progress) => Promise<T>, timeout: number): Promise<T> {
return runAbortableTask(async progress => { return runAbortableTask(async progress => {
return task(progress); return task(progress);
}, this._logger, timeout, apiName); }, this._logger, timeout);
} }
async _onFileChooserOpened(handle: dom.ElementHandle) { async _onFileChooserOpened(handle: dom.ElementHandle) {
@ -219,63 +218,6 @@ export class Page extends EventEmitter {
this._timeoutSettings.setDefaultTimeout(timeout); this._timeoutSettings.setDefaultTimeout(timeout);
} }
async $(selector: string): Promise<dom.ElementHandle<Element> | null> {
return this._attributeToPage(() => this.mainFrame().$(selector));
}
async waitForSelector(selector: string, options?: types.WaitForElementOptions): Promise<dom.ElementHandle<Element> | null> {
return this._attributeToPage(() => this.mainFrame().waitForSelector(selector, options));
}
async dispatchEvent(selector: string, type: string, eventInit?: Object, options?: types.TimeoutOptions): Promise<void> {
return this._attributeToPage(() => this.mainFrame().dispatchEvent(selector, type, eventInit, options));
}
async evaluateHandle<R, Arg>(pageFunction: js.Func1<Arg, R>, arg: Arg): Promise<js.SmartHandle<R>>;
async evaluateHandle<R>(pageFunction: js.Func1<void, R>, arg?: any): Promise<js.SmartHandle<R>>;
async evaluateHandle<R, Arg>(pageFunction: js.Func1<Arg, R>, arg: Arg): Promise<js.SmartHandle<R>> {
assertMaxArguments(arguments.length, 2);
return this._attributeToPage(() => this.mainFrame().evaluateHandle(pageFunction, arg));
}
async _evaluateExpressionHandle(expression: string, isFunction: boolean, arg: any): Promise<any> {
return this._attributeToPage(() => this.mainFrame()._evaluateExpressionHandle(expression, isFunction, arg));
}
async $eval<R, Arg>(selector: string, pageFunction: js.FuncOn<Element, Arg, R>, arg: Arg): Promise<R>;
async $eval<R>(selector: string, pageFunction: js.FuncOn<Element, void, R>, arg?: any): Promise<R>;
async $eval<R, Arg>(selector: string, pageFunction: js.FuncOn<Element, Arg, R>, arg: Arg): Promise<R> {
assertMaxArguments(arguments.length, 3);
return this._attributeToPage(() => this.mainFrame().$eval(selector, pageFunction, arg));
}
async _$evalExpression(selector: string, expression: string, isFunction: boolean, arg: any): Promise<any> {
return this._attributeToPage(() => this.mainFrame()._$evalExpression(selector, expression, isFunction, arg));
}
async $$eval<R, Arg>(selector: string, pageFunction: js.FuncOn<Element[], Arg, R>, arg: Arg): Promise<R>;
async $$eval<R>(selector: string, pageFunction: js.FuncOn<Element[], void, R>, arg?: any): Promise<R>;
async $$eval<R, Arg>(selector: string, pageFunction: js.FuncOn<Element[], Arg, R>, arg: Arg): Promise<R> {
assertMaxArguments(arguments.length, 3);
return this._attributeToPage(() => this.mainFrame().$$eval(selector, pageFunction, arg));
}
async _$$evalExpression(selector: string, expression: string, isFunction: boolean, arg: any): Promise<any> {
return this._attributeToPage(() => this.mainFrame()._$$evalExpression(selector, expression, isFunction, arg));
}
async $$(selector: string): Promise<dom.ElementHandle<Element>[]> {
return this._attributeToPage(() => this.mainFrame().$$(selector));
}
async addScriptTag(options: { url?: string; path?: string; content?: string; type?: string; }): Promise<dom.ElementHandle> {
return this._attributeToPage(() => this.mainFrame().addScriptTag(options));
}
async addStyleTag(options: { url?: string; path?: string; content?: string; }): Promise<dom.ElementHandle> {
return this._attributeToPage(() => this.mainFrame().addStyleTag(options));
}
async exposeFunction(name: string, playwrightFunction: Function) { async exposeFunction(name: string, playwrightFunction: Function) {
await this.exposeBinding(name, (options, ...args: any) => playwrightFunction(...args)); await this.exposeBinding(name, (options, ...args: any) => playwrightFunction(...args));
} }
@ -310,36 +252,12 @@ export class Page extends EventEmitter {
this.emit(Events.Page.Console, message); this.emit(Events.Page.Console, message);
} }
url(): string {
return this._attributeToPage(() => this.mainFrame().url());
}
async content(): Promise<string> {
return this._attributeToPage(() => this.mainFrame().content());
}
async setContent(html: string, options?: types.NavigateOptions): Promise<void> {
return this._attributeToPage(() => this.mainFrame().setContent(html, options));
}
async goto(url: string, options?: types.GotoOptions): Promise<network.Response | null> {
return this._attributeToPage(() => this.mainFrame().goto(url, options));
}
async reload(options?: types.NavigateOptions): Promise<network.Response | null> { async reload(options?: types.NavigateOptions): Promise<network.Response | null> {
const waitPromise = this.waitForNavigation(options); const waitPromise = this.mainFrame().waitForNavigation(options);
await this._delegate.reload(); await this._delegate.reload();
return waitPromise; return waitPromise;
} }
async waitForLoadState(state?: types.LifecycleEvent, options?: types.TimeoutOptions): Promise<void> {
return this._attributeToPage(() => this.mainFrame().waitForLoadState(state, options));
}
async waitForNavigation(options?: types.WaitForNavigationOptions): Promise<network.Response | null> {
return this._attributeToPage(() => this.mainFrame().waitForNavigation(options));
}
async waitForRequest(urlOrPredicate: string | RegExp | ((r: network.Request) => boolean), options: types.TimeoutOptions = {}): Promise<network.Request> { async waitForRequest(urlOrPredicate: string | RegExp | ((r: network.Request) => boolean), options: types.TimeoutOptions = {}): Promise<network.Request> {
const predicate = (request: network.Request) => { const predicate = (request: network.Request) => {
if (helper.isString(urlOrPredicate) || helper.isRegExp(urlOrPredicate)) if (helper.isString(urlOrPredicate) || helper.isRegExp(urlOrPredicate))
@ -360,7 +278,7 @@ export class Page extends EventEmitter {
async waitForEvent(event: string, optionsOrPredicate: types.WaitForEventOptions = {}): Promise<any> { async waitForEvent(event: string, optionsOrPredicate: types.WaitForEventOptions = {}): Promise<any> {
const options = typeof optionsOrPredicate === 'function' ? { predicate: optionsOrPredicate } : optionsOrPredicate; 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)); this._disconnectedPromise.then(error => progressController.abort(error));
if (event !== Events.Page.Crash) if (event !== Events.Page.Crash)
this._crashedPromise.then(error => progressController.abort(error)); this._crashedPromise.then(error => progressController.abort(error));
@ -368,7 +286,7 @@ export class Page extends EventEmitter {
} }
async goBack(options?: types.NavigateOptions): Promise<network.Response | null> { async goBack(options?: types.NavigateOptions): Promise<network.Response | null> {
const waitPromise = this.waitForNavigation(options); const waitPromise = this.mainFrame().waitForNavigation(options);
const result = await this._delegate.goBack(); const result = await this._delegate.goBack();
if (!result) { if (!result) {
waitPromise.catch(() => {}); waitPromise.catch(() => {});
@ -378,7 +296,7 @@ export class Page extends EventEmitter {
} }
async goForward(options?: types.NavigateOptions): Promise<network.Response | null> { async goForward(options?: types.NavigateOptions): Promise<network.Response | null> {
const waitPromise = this.waitForNavigation(options); const waitPromise = this.mainFrame().waitForNavigation(options);
const result = await this._delegate.goForward(); const result = await this._delegate.goForward();
if (!result) { if (!result) {
waitPromise.catch(() => {}); waitPromise.catch(() => {});
@ -412,17 +330,6 @@ export class Page extends EventEmitter {
await this._delegate.bringToFront(); await this._delegate.bringToFront();
} }
async evaluate<R, Arg>(pageFunction: js.Func1<Arg, R>, arg: Arg): Promise<R>;
async evaluate<R>(pageFunction: js.Func1<void, R>, arg?: any): Promise<R>;
async evaluate<R, Arg>(pageFunction: js.Func1<Arg, R>, arg: Arg): Promise<R> {
assertMaxArguments(arguments.length, 2);
return this._attributeToPage(() => this.mainFrame().evaluate(pageFunction, arg));
}
async _evaluateExpression(expression: string, isFunction: boolean, arg: any): Promise<any> {
return this._attributeToPage(() => this.mainFrame()._evaluateExpression(expression, isFunction, arg));
}
async addInitScript(script: Function | string | { path?: string, content?: string }, arg?: any) { async addInitScript(script: Function | string | { path?: string, content?: string }, arg?: any) {
const source = await helper.evaluationScript(script, arg); const source = await helper.evaluationScript(script, arg);
await this._addInitScriptExpression(source); await this._addInitScriptExpression(source);
@ -482,11 +389,7 @@ export class Page extends EventEmitter {
async screenshot(options: types.ScreenshotOptions = {}): Promise<Buffer> { async screenshot(options: types.ScreenshotOptions = {}): Promise<Buffer> {
return this._runAbortableTask( return this._runAbortableTask(
progress => this._screenshotter.screenshotPage(progress, options), progress => this._screenshotter.screenshotPage(progress, options),
this._timeoutSettings.timeout(options), 'page.screenshot'); this._timeoutSettings.timeout(options));
}
async title(): Promise<string> {
return this._attributeToPage(() => this.mainFrame().title());
} }
async close(options?: { runBeforeUnload?: boolean }) { async close(options?: { runBeforeUnload?: boolean }) {
@ -513,89 +416,10 @@ export class Page extends EventEmitter {
return this._closedState === 'closed'; return this._closedState === 'closed';
} }
private _attributeToPage<T>(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<null|string> {
return this._attributeToPage(() => this.mainFrame().textContent(selector, options));
}
async innerText(selector: string, options?: types.TimeoutOptions): Promise<string> {
return this._attributeToPage(() => this.mainFrame().innerText(selector, options));
}
async innerHTML(selector: string, options?: types.TimeoutOptions): Promise<string> {
return this._attributeToPage(() => this.mainFrame().innerHTML(selector, options));
}
async getAttribute(selector: string, name: string, options?: types.TimeoutOptions): Promise<string | null> {
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<string[]> {
return this._attributeToPage(() => this.mainFrame().selectOption(selector, values, options));
}
async setInputFiles(selector: string, files: string | types.FilePayload | string[] | types.FilePayload[], options?: types.NavigatingActionWaitOptions): Promise<void> {
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) { async waitForTimeout(timeout: number) {
await this.mainFrame().waitForTimeout(timeout); await this.mainFrame().waitForTimeout(timeout);
} }
async waitForFunction<R, Arg>(pageFunction: js.Func1<Arg, R>, arg: Arg, options?: types.WaitForFunctionOptions): Promise<js.SmartHandle<R>>;
async waitForFunction<R>(pageFunction: js.Func1<void, R>, arg?: any, options?: types.WaitForFunctionOptions): Promise<js.SmartHandle<R>>;
async waitForFunction<R, Arg>(pageFunction: js.Func1<Arg, R>, arg: Arg, options?: types.WaitForFunctionOptions): Promise<js.SmartHandle<R>> {
return this._attributeToPage(() => this.mainFrame().waitForFunction(pageFunction, arg, options));
}
async _waitForFunctionExpression<R>(expression: string, isFunction: boolean, arg: any, options: types.WaitForFunctionOptions = {}): Promise<js.SmartHandle<R>> {
return this._attributeToPage(() => this.mainFrame()._waitForFunctionExpression(expression, isFunction, arg, options));
}
workers(): Worker[] { workers(): Worker[] {
return [...this._workers.values()]; return [...this._workers.values()];
} }

View file

@ -28,16 +28,11 @@ export interface Progress {
throwIfAborted(): void; throwIfAborted(): void;
} }
export async function runAbortableTask<T>(task: (progress: Progress) => Promise<T>, logger: Logger, timeout: number, apiName: string): Promise<T> { export async function runAbortableTask<T>(task: (progress: Progress) => Promise<T>, logger: Logger, timeout: number): Promise<T> {
const controller = new ProgressController(logger, timeout, apiName); const controller = new ProgressController(logger, timeout);
return controller.run(task); return controller.run(task);
} }
let useApiName = true;
export function setUseApiName(value: boolean) {
useApiName = value;
}
export class ProgressController { export class ProgressController {
// Promise and callback that forcefully abort the progress. // Promise and callback that forcefully abort the progress.
// This promise always rejects. // This promise always rejects.
@ -54,12 +49,10 @@ export class ProgressController {
private _logger: Logger; private _logger: Logger;
private _state: 'before' | 'running' | 'aborted' | 'finished' = 'before'; private _state: 'before' | 'running' | 'aborted' | 'finished' = 'before';
private _apiName: string;
private _deadline: number; private _deadline: number;
private _timeout: number; private _timeout: number;
constructor(logger: Logger, timeout: number, apiName: string) { constructor(logger: Logger, timeout: number) {
this._apiName = apiName;
this._logger = logger; this._logger = logger;
this._timeout = timeout; this._timeout = timeout;
@ -74,7 +67,7 @@ export class ProgressController {
assert(this._state === 'before'); assert(this._state === 'before');
this._state = 'running'; this._state = 'running';
const loggerScope = this._logger.createScope(useApiName ? this._apiName : undefined, true); const loggerScope = this._logger.createScope(undefined, true);
const progress: Progress = { const progress: Progress = {
aborted: this._abortedPromise, aborted: this._abortedPromise,
@ -105,7 +98,6 @@ export class ProgressController {
} catch (e) { } catch (e) {
this._aborted(); this._aborted();
rewriteErrorMessage(e, rewriteErrorMessage(e,
(useApiName ? `${this._apiName}: ` : '') +
e.message + e.message +
formatLogRecording(loggerScope.recording()) + formatLogRecording(loggerScope.recording()) +
kLoggingNote); kLoggingNote);

View file

@ -18,14 +18,11 @@ import { DispatcherConnection } from './server/dispatcher';
import type { Playwright as PlaywrightImpl } from '../server/playwright'; import type { Playwright as PlaywrightImpl } from '../server/playwright';
import type { Playwright as PlaywrightAPI } from './client/playwright'; import type { Playwright as PlaywrightAPI } from './client/playwright';
import { PlaywrightDispatcher } from './server/playwrightDispatcher'; import { PlaywrightDispatcher } from './server/playwrightDispatcher';
import { setUseApiName } from '../progress';
import { Connection } from './client/connection'; import { Connection } from './client/connection';
import { isUnderTest } from '../helper'; import { isUnderTest } from '../helper';
import { BrowserServerLauncherImpl } from './browserServerImpl'; import { BrowserServerLauncherImpl } from './browserServerImpl';
export function setupInProcess(playwright: PlaywrightImpl): PlaywrightAPI { export function setupInProcess(playwright: PlaywrightImpl): PlaywrightAPI {
setUseApiName(false);
const clientConnection = new Connection(); const clientConnection = new Connection();
const dispatcherConnection = new DispatcherConnection(); const dispatcherConnection = new DispatcherConnection();

View file

@ -19,11 +19,8 @@ import { DispatcherConnection } from './server/dispatcher';
import { Playwright } from '../server/playwright'; import { Playwright } from '../server/playwright';
import { PlaywrightDispatcher } from './server/playwrightDispatcher'; import { PlaywrightDispatcher } from './server/playwrightDispatcher';
import { Electron } from '../server/electron'; import { Electron } from '../server/electron';
import { setUseApiName } from '../progress';
import { gracefullyCloseAll } from '../server/processLauncher'; import { gracefullyCloseAll } from '../server/processLauncher';
setUseApiName(false);
const dispatcherConnection = new DispatcherConnection(); const dispatcherConnection = new DispatcherConnection();
const transport = new Transport(process.stdout, process.stdin); const transport = new Transport(process.stdout, process.stdin);
transport.onclose = async () => { transport.onclose = async () => {

View file

@ -149,10 +149,6 @@ export class PageDispatcher extends Dispatcher<Page, PageInitializer> implements
this._page.removeListener(Events.Page.FileChooser, this._onFileChooser); this._page.removeListener(Events.Page.FileChooser, this._onFileChooser);
} }
async title() {
return await this._page.title();
}
async keyboardDown(params: { key: string }): Promise<void> { async keyboardDown(params: { key: string }): Promise<void> {
await this._page.keyboard.down(params.key); await this._page.keyboard.down(params.key);
} }

View file

@ -85,8 +85,7 @@ export abstract class BrowserTypeBase implements BrowserType {
assert(!(options as any).port, 'Cannot specify a port without launching as a server.'); assert(!(options as any).port, 'Cannot specify a port without launching as a server.');
options = validateLaunchOptions(options); options = validateLaunchOptions(options);
const loggers = new Loggers(options.logger); 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)).catch(e => { throw this._rewriteStartupError(e); });
const browser = await runAbortableTask(progress => this._innerLaunch(progress, options, loggers, undefined), loggers.browser, TimeoutSettings.timeout(options), label).catch(e => { throw this._rewriteStartupError(e, label); });
return browser; return browser;
} }
@ -95,8 +94,7 @@ export abstract class BrowserTypeBase implements BrowserType {
options = validateLaunchOptions(options); options = validateLaunchOptions(options);
const persistent = validateBrowserContextOptions(options); const persistent = validateBrowserContextOptions(options);
const loggers = new Loggers(options.logger); 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)).catch(e => { throw this._rewriteStartupError(e); });
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); });
return browser._defaultContext!; return browser._defaultContext!;
} }
@ -224,7 +222,7 @@ export abstract class BrowserTypeBase implements BrowserType {
abstract _connectToTransport(transport: ConnectionTransport, options: BrowserOptions): Promise<BrowserBase>; abstract _connectToTransport(transport: ConnectionTransport, options: BrowserOptions): Promise<BrowserBase>;
abstract _amendEnvironment(env: Env, userDataDir: string, executable: string, browserArguments: string[]): Env; abstract _amendEnvironment(env: Env, userDataDir: string, executable: string, browserArguments: string[]): Env;
abstract _amendArguments(browserArguments: string[]): string[]; abstract _amendArguments(browserArguments: string[]): string[];
abstract _rewriteStartupError(error: Error, prefix: string): Error; abstract _rewriteStartupError(error: Error): Error;
abstract _attemptToGracefullyCloseBrowser(transport: ConnectionTransport): void; abstract _attemptToGracefullyCloseBrowser(transport: ConnectionTransport): void;
} }

View file

@ -61,13 +61,13 @@ export class Chromium extends BrowserTypeBase {
return CRBrowser.connect(transport, options, devtools); 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: // 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 // 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')) if (!error.message.includes('crbug.com/357670') && !error.message.includes('No usable sandbox!') && !error.message.includes('crbug.com/638180'))
return error; return error;
return rewriteErrorMessage(error, [ return rewriteErrorMessage(error, [
`${prefix}: Chromium sandboxing failed!`, `Chromium sandboxing failed!`,
`================================`, `================================`,
`To workaround sandboxing issues, do either of the following:`, `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`, ` - (preferred): Configure environment to support sandboxing: https://github.com/microsoft/playwright/blob/master/docs/troubleshooting.md`,

View file

@ -94,7 +94,7 @@ export class ElectronApplication extends EventEmitter {
this._windows.delete(page); this._windows.delete(page);
}); });
this._windows.add(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); this.emit(ElectronEvents.ElectronApplication.Window, page);
} }
@ -136,7 +136,7 @@ export class ElectronApplication extends EventEmitter {
async waitForEvent(event: string, optionsOrPredicate: types.WaitForEventOptions = {}): Promise<any> { async waitForEvent(event: string, optionsOrPredicate: types.WaitForEventOptions = {}): Promise<any> {
const options = typeof optionsOrPredicate === 'function' ? { predicate: optionsOrPredicate } : optionsOrPredicate; 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) if (event !== ElectronEvents.ElectronApplication.Close)
this._browserContext._closePromise.then(error => progressController.abort(error)); this._browserContext._closePromise.then(error => progressController.abort(error));
return progressController.run(progress => helper.waitForEvent(progress, this, event, options.predicate).promise); 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); app = new ElectronApplication(loggers, browser, nodeConnection);
await app._init(); await app._init();
return app; return app;
}, loggers.browser, TimeoutSettings.timeout(options), 'electron.launch'); }, loggers.browser, TimeoutSettings.timeout(options));
} }
} }

View file

@ -36,7 +36,7 @@ export class Firefox extends BrowserTypeBase {
return FFBrowser.connect(transport, options); return FFBrowser.connect(transport, options);
} }
_rewriteStartupError(error: Error, prefix: string): Error { _rewriteStartupError(error: Error): Error {
return error; return error;
} }

View file

@ -42,7 +42,7 @@ export class WebKit extends BrowserTypeBase {
return browserArguments; return browserArguments;
} }
_rewriteStartupError(error: Error, prefix: string): Error { _rewriteStartupError(error: Error): Error {
return error; return error;
} }

View file

@ -22,7 +22,7 @@ const CRASH_FAIL = (FFOX && WIN) || WIRE;
// Firefox Win: it just doesn't crash sometimes. // Firefox Win: it just doesn't crash sometimes.
function crash(pageImpl) { function crash(pageImpl) {
if (CHROMIUM) if (CHROMIUM)
pageImpl.goto('chrome://crash').catch(e => {}); pageImpl.mainFrame().goto('chrome://crash').catch(e => {});
else if (WEBKIT) else if (WEBKIT)
pageImpl._delegate._session.send('Page.crash', {}).catch(e => {}); pageImpl._delegate._session.send('Page.crash', {}).catch(e => {});
else if (FFOX) else if (FFOX)