chore: refactor impl-side events to be per-class (#3569)
This commit is contained in:
parent
d4dac04212
commit
57e8617474
|
|
@ -19,7 +19,6 @@ import { BrowserContext } from './browserContext';
|
|||
import { Page } from './page';
|
||||
import { EventEmitter } from 'events';
|
||||
import { Download } from './download';
|
||||
import { Events } from './events';
|
||||
import { ProxySettings } from './types';
|
||||
import { ChildProcess } from 'child_process';
|
||||
|
||||
|
|
@ -40,6 +39,10 @@ export type BrowserOptions = types.UIOptions & {
|
|||
};
|
||||
|
||||
export abstract class Browser extends EventEmitter {
|
||||
static Events = {
|
||||
Disconnected: 'disconnected',
|
||||
};
|
||||
|
||||
readonly _options: BrowserOptions;
|
||||
private _downloads = new Map<string, Download>();
|
||||
_defaultContext: BrowserContext | null = null;
|
||||
|
|
@ -87,7 +90,7 @@ export abstract class Browser extends EventEmitter {
|
|||
context._browserClosed();
|
||||
if (this._defaultContext)
|
||||
this._defaultContext._browserClosed();
|
||||
this.emit(Events.Browser.Disconnected);
|
||||
this.emit(Browser.Events.Disconnected);
|
||||
}
|
||||
|
||||
async close() {
|
||||
|
|
@ -96,7 +99,7 @@ export abstract class Browser extends EventEmitter {
|
|||
await this._options.browserProcess.close();
|
||||
}
|
||||
if (this.isConnected())
|
||||
await new Promise(x => this.once(Events.Browser.Disconnected, x));
|
||||
await new Promise(x => this.once(Browser.Events.Disconnected, x));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ import { Page, PageBinding } from './page';
|
|||
import { TimeoutSettings } from './timeoutSettings';
|
||||
import * as frames from './frames';
|
||||
import * as types from './types';
|
||||
import { Events } from './events';
|
||||
import { Download } from './download';
|
||||
import { Browser } from './browser';
|
||||
import { EventEmitter } from 'events';
|
||||
|
|
@ -40,6 +39,13 @@ export class Screencast {
|
|||
}
|
||||
|
||||
export abstract class BrowserContext extends EventEmitter {
|
||||
static Events = {
|
||||
Close: 'close',
|
||||
Page: 'page',
|
||||
ScreencastStarted: 'screencaststarted',
|
||||
ScreencastStopped: 'screencaststopped',
|
||||
};
|
||||
|
||||
readonly _timeoutSettings = new TimeoutSettings();
|
||||
readonly _pageBindings = new Map<string, PageBinding>();
|
||||
readonly _options: types.BrowserContextOptions;
|
||||
|
|
@ -81,7 +87,7 @@ export abstract class BrowserContext extends EventEmitter {
|
|||
this._closedStatus = 'closed';
|
||||
this._downloads.clear();
|
||||
this._closePromiseFulfill!(new Error('Context closed'));
|
||||
this.emit(Events.BrowserContext.Close);
|
||||
this.emit(BrowserContext.Events.Close);
|
||||
}
|
||||
|
||||
// BrowserContext methods.
|
||||
|
|
@ -160,7 +166,7 @@ export abstract class BrowserContext extends EventEmitter {
|
|||
|
||||
async _loadDefaultContext(progress: Progress) {
|
||||
if (!this.pages().length) {
|
||||
const waitForEvent = helper.waitForEvent(progress, this, Events.BrowserContext.Page);
|
||||
const waitForEvent = helper.waitForEvent(progress, this, BrowserContext.Events.Page);
|
||||
progress.cleanupWhenAborted(() => waitForEvent.dispose);
|
||||
await waitForEvent.promise;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
import { Browser, BrowserOptions } from '../browser';
|
||||
import { assertBrowserContextIsNotOwned, BrowserContext, validateBrowserContextOptions, verifyGeolocation } from '../browserContext';
|
||||
import { Events as CommonEvents } from '../events';
|
||||
import { assert } from '../helper';
|
||||
import * as network from '../network';
|
||||
import { Page, PageBinding, Worker } from '../page';
|
||||
|
|
@ -26,7 +25,6 @@ import * as types from '../types';
|
|||
import { ConnectionEvents, CRConnection, CRSession } from './crConnection';
|
||||
import { CRPage } from './crPage';
|
||||
import { readProtocolStream } from './crProtocolHelper';
|
||||
import { Events } from './events';
|
||||
import { Protocol } from './protocol';
|
||||
import { CRExecutionContext } from './crExecutionContext';
|
||||
import { CRDevTools } from './crDevTools';
|
||||
|
|
@ -151,7 +149,7 @@ export class CRBrowser extends Browser {
|
|||
const backgroundPage = new CRPage(session, targetInfo.targetId, context, null, false);
|
||||
this._backgroundPages.set(targetInfo.targetId, backgroundPage);
|
||||
backgroundPage.pageOrError().then(() => {
|
||||
context!.emit(Events.ChromiumBrowserContext.BackgroundPage, backgroundPage._page);
|
||||
context!.emit(CRBrowserContext.CREvents.BackgroundPage, backgroundPage._page);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
|
@ -164,11 +162,11 @@ export class CRBrowser extends Browser {
|
|||
const page = crPage._page;
|
||||
if (pageOrError instanceof Error)
|
||||
page._setIsError();
|
||||
context!.emit(CommonEvents.BrowserContext.Page, page);
|
||||
context!.emit(BrowserContext.Events.Page, page);
|
||||
if (opener) {
|
||||
opener.pageOrError().then(openerPage => {
|
||||
if (openerPage instanceof Page && !openerPage.isClosed())
|
||||
openerPage.emit(CommonEvents.Page.Popup, page);
|
||||
openerPage.emit(Page.Events.Popup, page);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -178,7 +176,7 @@ export class CRBrowser extends Browser {
|
|||
if (targetInfo.type === 'service_worker') {
|
||||
const serviceWorker = new CRServiceWorker(context, session, targetInfo.url);
|
||||
this._serviceWorkers.set(targetInfo.targetId, serviceWorker);
|
||||
context.emit(Events.ChromiumBrowserContext.ServiceWorker, serviceWorker);
|
||||
context.emit(CRBrowserContext.CREvents.ServiceWorker, serviceWorker);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -202,7 +200,7 @@ export class CRBrowser extends Browser {
|
|||
const serviceWorker = this._serviceWorkers.get(targetId);
|
||||
if (serviceWorker) {
|
||||
this._serviceWorkers.delete(targetId);
|
||||
serviceWorker.emit(CommonEvents.Worker.Close);
|
||||
serviceWorker.emit(Worker.Events.Close);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -280,6 +278,11 @@ class CRServiceWorker extends Worker {
|
|||
}
|
||||
|
||||
export class CRBrowserContext extends BrowserContext {
|
||||
static CREvents = {
|
||||
BackgroundPage: 'backgroundpage',
|
||||
ServiceWorker: 'serviceworker',
|
||||
};
|
||||
|
||||
readonly _browser: CRBrowser;
|
||||
readonly _browserContextId: string | null;
|
||||
readonly _evaluateOnNewDocumentSources: string[];
|
||||
|
|
@ -432,7 +435,7 @@ export class CRBrowserContext extends BrowserContext {
|
|||
// asynchronously and we get detached from them later.
|
||||
// To avoid the wrong order of notifications, we manually fire
|
||||
// "close" event here and forget about the serivce worker.
|
||||
serviceWorker.emit(CommonEvents.Worker.Close);
|
||||
serviceWorker.emit(Worker.Events.Close);
|
||||
this._browser._serviceWorkers.delete(targetId);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ import { CRExecutionContext } from './crExecutionContext';
|
|||
import { CRNetworkManager } from './crNetworkManager';
|
||||
import { Page, Worker, PageBinding } from '../page';
|
||||
import { Protocol } from './protocol';
|
||||
import { Events } from '../events';
|
||||
import { toConsoleMessageLocation, exceptionToError, releaseObject } from './crProtocolHelper';
|
||||
import * as dialog from '../dialog';
|
||||
import { PageDelegate } from '../page';
|
||||
|
|
@ -590,7 +589,7 @@ class FrameSession {
|
|||
const args = event.args.map(o => worker._existingExecutionContext!.createHandle(o));
|
||||
this._page._addConsoleMessage(event.type, args, toConsoleMessageLocation(event.stackTrace));
|
||||
});
|
||||
session.on('Runtime.exceptionThrown', exception => this._page.emit(Events.Page.PageError, exceptionToError(exception.exceptionDetails)));
|
||||
session.on('Runtime.exceptionThrown', exception => this._page.emit(Page.Events.PageError, exceptionToError(exception.exceptionDetails)));
|
||||
// TODO: attribute workers to the right frame.
|
||||
this._networkManager.instrumentNetworkEvents(session, this._page._frameManager.frame(this._targetId)!);
|
||||
}
|
||||
|
|
@ -664,7 +663,7 @@ class FrameSession {
|
|||
}
|
||||
|
||||
_onDialog(event: Protocol.Page.javascriptDialogOpeningPayload) {
|
||||
this._page.emit(Events.Page.Dialog, new dialog.Dialog(
|
||||
this._page.emit(Page.Events.Dialog, new dialog.Dialog(
|
||||
event.type,
|
||||
event.message,
|
||||
async (accept: boolean, promptText?: string) => {
|
||||
|
|
@ -674,7 +673,7 @@ class FrameSession {
|
|||
}
|
||||
|
||||
_handleException(exceptionDetails: Protocol.Runtime.ExceptionDetails) {
|
||||
this._page.emit(Events.Page.PageError, exceptionToError(exceptionDetails));
|
||||
this._page.emit(Page.Events.PageError, exceptionToError(exceptionDetails));
|
||||
}
|
||||
|
||||
async _onTargetCrashed() {
|
||||
|
|
@ -692,7 +691,7 @@ class FrameSession {
|
|||
lineNumber: lineNumber || 0,
|
||||
columnNumber: 0,
|
||||
};
|
||||
this._page.emit(Events.Page.Console, new ConsoleMessage(level, text, [], location));
|
||||
this._page.emit(Page.Events.Console, new ConsoleMessage(level, text, [], location));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,23 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export const Events = {
|
||||
ChromiumBrowserContext: {
|
||||
BackgroundPage: 'backgroundpage',
|
||||
ServiceWorker: 'serviceworker',
|
||||
}
|
||||
};
|
||||
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
|
||||
import { BrowserContext } from '../browserContext';
|
||||
import { Events } from '../events';
|
||||
import * as frames from '../frames';
|
||||
import * as js from '../javascript';
|
||||
import { Page } from '../page';
|
||||
|
|
@ -23,10 +22,10 @@ import DebugScript from './injected/debugScript';
|
|||
|
||||
export class DebugController {
|
||||
constructor(context: BrowserContext) {
|
||||
context.on(Events.BrowserContext.Page, (page: Page) => {
|
||||
context.on(BrowserContext.Events.Page, (page: Page) => {
|
||||
for (const frame of page.frames())
|
||||
this.ensureInstalledInFrame(frame);
|
||||
page.on(Events.Page.FrameNavigated, frame => this.ensureInstalledInFrame(frame));
|
||||
page.on(Page.Events.FrameNavigated, frame => this.ensureInstalledInFrame(frame));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ import * as path from 'path';
|
|||
import * as fs from 'fs';
|
||||
import * as util from 'util';
|
||||
import { Page } from './page';
|
||||
import { Events } from './events';
|
||||
import { Readable } from 'stream';
|
||||
import { assert, mkdirIfNeeded } from './helper';
|
||||
|
||||
|
|
@ -47,13 +46,13 @@ export class Download {
|
|||
page._browserContext._downloads.add(this);
|
||||
this._acceptDownloads = !!this._page._browserContext._options.acceptDownloads;
|
||||
if (suggestedFilename !== undefined)
|
||||
this._page.emit(Events.Page.Download, this);
|
||||
this._page.emit(Page.Events.Download, this);
|
||||
}
|
||||
|
||||
_filenameSuggested(suggestedFilename: string) {
|
||||
assert(this._suggestedFilename === undefined);
|
||||
this._suggestedFilename = suggestedFilename;
|
||||
this._page.emit(Events.Page.Download, this);
|
||||
this._page.emit(Page.Events.Download, this);
|
||||
}
|
||||
|
||||
url(): string {
|
||||
|
|
|
|||
|
|
@ -1,60 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export const Events = {
|
||||
Browser: {
|
||||
Disconnected: 'disconnected'
|
||||
},
|
||||
|
||||
BrowserContext: {
|
||||
Close: 'close',
|
||||
Page: 'page',
|
||||
ScreencastStarted: 'screencaststarted',
|
||||
ScreencastStopped: 'screencaststopped',
|
||||
},
|
||||
|
||||
BrowserServer: {
|
||||
Close: 'close',
|
||||
},
|
||||
|
||||
Page: {
|
||||
Close: 'close',
|
||||
Crash: 'crash',
|
||||
Console: 'console',
|
||||
Dialog: 'dialog',
|
||||
Download: 'download',
|
||||
FileChooser: 'filechooser',
|
||||
DOMContentLoaded: 'domcontentloaded',
|
||||
// Can't use just 'error' due to node.js special treatment of error events.
|
||||
// @see https://nodejs.org/api/events.html#events_error_events
|
||||
PageError: 'pageerror',
|
||||
Request: 'request',
|
||||
Response: 'response',
|
||||
RequestFailed: 'requestfailed',
|
||||
RequestFinished: 'requestfinished',
|
||||
FrameAttached: 'frameattached',
|
||||
FrameDetached: 'framedetached',
|
||||
FrameNavigated: 'framenavigated',
|
||||
Load: 'load',
|
||||
Popup: 'popup',
|
||||
Worker: 'worker',
|
||||
},
|
||||
|
||||
Worker: {
|
||||
Close: 'close',
|
||||
},
|
||||
};
|
||||
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
import { Browser, BrowserOptions } from '../browser';
|
||||
import { assertBrowserContextIsNotOwned, BrowserContext, validateBrowserContextOptions, verifyGeolocation } from '../browserContext';
|
||||
import { Events } from '../events';
|
||||
import { assert, helper, RegisteredListener } from '../helper';
|
||||
import * as network from '../network';
|
||||
import { Page, PageBinding } from '../page';
|
||||
|
|
@ -129,12 +128,12 @@ export class FFBrowser extends Browser {
|
|||
const page = ffPage._page;
|
||||
if (pageOrError instanceof Error)
|
||||
page._setIsError();
|
||||
context.emit(Events.BrowserContext.Page, page);
|
||||
context.emit(BrowserContext.Events.Page, page);
|
||||
if (!opener)
|
||||
return;
|
||||
const openerPage = await opener.pageOrError();
|
||||
if (openerPage instanceof Page && !openerPage.isClosed())
|
||||
openerPage.emit(Events.Page.Popup, page);
|
||||
openerPage.emit(Page.Events.Popup, page);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
import * as dialog from '../dialog';
|
||||
import * as dom from '../dom';
|
||||
import { Events } from '../events';
|
||||
import * as frames from '../frames';
|
||||
import { assert, helper, RegisteredListener } from '../helper';
|
||||
import { Page, PageBinding, PageDelegate, Worker } from '../page';
|
||||
|
|
@ -32,7 +31,7 @@ import { FFNetworkManager } from './ffNetworkManager';
|
|||
import { Protocol } from './protocol';
|
||||
import { selectors } from '../selectors';
|
||||
import { rewriteErrorMessage } from '../utils/stackTrace';
|
||||
import { Screencast } from '../browserContext';
|
||||
import { Screencast, BrowserContext } from '../browserContext';
|
||||
|
||||
const UTILITY_WORLD_NAME = '__playwright_utility_world__';
|
||||
|
||||
|
|
@ -62,7 +61,7 @@ export class FFPage implements PageDelegate {
|
|||
this._browserContext = browserContext;
|
||||
this._page = new Page(this, browserContext);
|
||||
this._networkManager = new FFNetworkManager(session, this._page);
|
||||
this._page.on(Events.Page.FrameDetached, frame => this._removeContextsForFrame(frame));
|
||||
this._page.on(Page.Events.FrameDetached, frame => this._removeContextsForFrame(frame));
|
||||
// TODO: remove Page.willOpenNewWindowAsynchronously from the protocol.
|
||||
this._eventListeners = [
|
||||
helper.addEventListener(this._session, 'Page.eventFired', this._onEventFired.bind(this)),
|
||||
|
|
@ -179,7 +178,7 @@ export class FFPage implements PageDelegate {
|
|||
const message = params.message.startsWith('Error: ') ? params.message.substring(7) : params.message;
|
||||
const error = new Error(message);
|
||||
error.stack = params.stack;
|
||||
this._page.emit(Events.Page.PageError, error);
|
||||
this._page.emit(Page.Events.PageError, error);
|
||||
}
|
||||
|
||||
_onConsole(payload: Protocol.Runtime.consolePayload) {
|
||||
|
|
@ -189,7 +188,7 @@ export class FFPage implements PageDelegate {
|
|||
}
|
||||
|
||||
_onDialogOpened(params: Protocol.Page.dialogOpenedPayload) {
|
||||
this._page.emit(Events.Page.Dialog, new dialog.Dialog(
|
||||
this._page.emit(Page.Events.Dialog, new dialog.Dialog(
|
||||
params.type,
|
||||
params.message,
|
||||
async (accept: boolean, promptText?: string) => {
|
||||
|
|
@ -262,7 +261,7 @@ export class FFPage implements PageDelegate {
|
|||
_onScreencastStarted(event: Protocol.Page.screencastStartedPayload) {
|
||||
const screencast = new Screencast(event.file, this._page);
|
||||
this._idToScreencast.set(event.uid, screencast);
|
||||
this._browserContext.emit(Events.BrowserContext.ScreencastStarted, screencast);
|
||||
this._browserContext.emit(BrowserContext.Events.ScreencastStarted, screencast);
|
||||
}
|
||||
|
||||
_onScreencastStopped(event: Protocol.Page.screencastStoppedPayload) {
|
||||
|
|
@ -270,7 +269,7 @@ export class FFPage implements PageDelegate {
|
|||
if (!screencast)
|
||||
return;
|
||||
this._idToScreencast.delete(event.uid);
|
||||
this._browserContext.emit(Events.BrowserContext.ScreencastStopped, screencast);
|
||||
this._browserContext.emit(BrowserContext.Events.ScreencastStopped, screencast);
|
||||
}
|
||||
|
||||
async exposeBinding(binding: PageBinding) {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
import { ConsoleMessage } from './console';
|
||||
import * as dom from './dom';
|
||||
import { Events } from './events';
|
||||
import { assert, helper, RegisteredListener, debugLogger } from './helper';
|
||||
import * as js from './javascript';
|
||||
import * as network from './network';
|
||||
|
|
@ -50,7 +49,6 @@ type ConsoleTagHandler = () => void;
|
|||
|
||||
export type FunctionWithSource = (source: { context: BrowserContext, page: Page, frame: Frame}, ...args: any) => any;
|
||||
|
||||
export const kNavigationEvent = Symbol('navigation');
|
||||
export type NavigationEvent = {
|
||||
// New frame url after navigation.
|
||||
url: string,
|
||||
|
|
@ -63,8 +61,6 @@ export type NavigationEvent = {
|
|||
// the navigation did not commit.
|
||||
error?: Error,
|
||||
};
|
||||
export const kAddLifecycleEvent = Symbol('addLifecycle');
|
||||
export const kRemoveLifecycleEvent = Symbol('removeLifecycle');
|
||||
|
||||
export class FrameManager {
|
||||
private _page: Page;
|
||||
|
|
@ -120,7 +116,7 @@ export class FrameManager {
|
|||
assert(!this._frames.has(frameId));
|
||||
const frame = new Frame(this._page, frameId, parentFrame);
|
||||
this._frames.set(frameId, frame);
|
||||
this._page.emit(Events.Page.FrameAttached, frame);
|
||||
this._page.emit(Page.Events.FrameAttached, frame);
|
||||
return frame;
|
||||
}
|
||||
}
|
||||
|
|
@ -197,10 +193,10 @@ export class FrameManager {
|
|||
|
||||
frame._onClearLifecycle();
|
||||
const navigationEvent: NavigationEvent = { url, name, newDocument: frame._currentDocument };
|
||||
frame._eventEmitter.emit(kNavigationEvent, navigationEvent);
|
||||
frame.emit(Frame.Events.Navigation, navigationEvent);
|
||||
if (!initial) {
|
||||
debugLogger.log('api', ` navigated to "${url}"`);
|
||||
this._page.emit(Events.Page.FrameNavigated, frame);
|
||||
this._page.emit(Page.Events.FrameNavigated, frame);
|
||||
}
|
||||
// Restore pending if any - see comments above about keepPending.
|
||||
frame._pendingDocument = keepPending;
|
||||
|
|
@ -212,9 +208,9 @@ export class FrameManager {
|
|||
return;
|
||||
frame._url = url;
|
||||
const navigationEvent: NavigationEvent = { url, name: frame._name };
|
||||
frame._eventEmitter.emit(kNavigationEvent, navigationEvent);
|
||||
frame.emit(Frame.Events.Navigation, navigationEvent);
|
||||
debugLogger.log('api', ` navigated to "${url}"`);
|
||||
this._page.emit(Events.Page.FrameNavigated, frame);
|
||||
this._page.emit(Page.Events.FrameNavigated, frame);
|
||||
}
|
||||
|
||||
frameAbortedNavigation(frameId: string, errorText: string, documentId?: string) {
|
||||
|
|
@ -230,7 +226,7 @@ export class FrameManager {
|
|||
error: new Error(errorText),
|
||||
};
|
||||
frame._pendingDocument = undefined;
|
||||
frame._eventEmitter.emit(kNavigationEvent, navigationEvent);
|
||||
frame.emit(Frame.Events.Navigation, navigationEvent);
|
||||
}
|
||||
|
||||
frameDetached(frameId: string) {
|
||||
|
|
@ -266,13 +262,13 @@ export class FrameManager {
|
|||
|
||||
requestReceivedResponse(response: network.Response) {
|
||||
if (!response.request()._isFavicon)
|
||||
this._page.emit(Events.Page.Response, response);
|
||||
this._page.emit(Page.Events.Response, response);
|
||||
}
|
||||
|
||||
requestFinished(request: network.Request) {
|
||||
this._inflightRequestFinished(request);
|
||||
if (!request._isFavicon)
|
||||
this._page.emit(Events.Page.RequestFinished, request);
|
||||
this._page.emit(Page.Events.RequestFinished, request);
|
||||
}
|
||||
|
||||
requestFailed(request: network.Request, canceled: boolean) {
|
||||
|
|
@ -285,7 +281,7 @@ export class FrameManager {
|
|||
this.frameAbortedNavigation(frame._id, errorText, frame._pendingDocument.documentId);
|
||||
}
|
||||
if (!request._isFavicon)
|
||||
this._page.emit(Events.Page.RequestFailed, request);
|
||||
this._page.emit(Page.Events.RequestFailed, request);
|
||||
}
|
||||
|
||||
removeChildFramesRecursively(frame: Frame) {
|
||||
|
|
@ -297,7 +293,7 @@ export class FrameManager {
|
|||
this.removeChildFramesRecursively(frame);
|
||||
frame._onDetached();
|
||||
this._frames.delete(frame._id);
|
||||
this._page.emit(Events.Page.FrameDetached, frame);
|
||||
this._page.emit(Page.Events.FrameDetached, frame);
|
||||
}
|
||||
|
||||
private _inflightRequestFinished(request: network.Request) {
|
||||
|
|
@ -333,8 +329,13 @@ export class FrameManager {
|
|||
}
|
||||
}
|
||||
|
||||
export class Frame {
|
||||
readonly _eventEmitter: EventEmitter;
|
||||
export class Frame extends EventEmitter {
|
||||
static Events = {
|
||||
Navigation: 'navigation',
|
||||
AddLifecycle: 'addlifecycle',
|
||||
RemoveLifecycle: 'removelifecycle',
|
||||
};
|
||||
|
||||
_id: string;
|
||||
private _firedLifecycleEvents = new Set<types.LifecycleEvent>();
|
||||
_subtreeLifecycleEvents = new Set<types.LifecycleEvent>();
|
||||
|
|
@ -354,8 +355,8 @@ export class Frame {
|
|||
private _detachedCallback = () => {};
|
||||
|
||||
constructor(page: Page, id: string, parentFrame: Frame | null) {
|
||||
this._eventEmitter = new EventEmitter();
|
||||
this._eventEmitter.setMaxListeners(0);
|
||||
super();
|
||||
this.setMaxListeners(0);
|
||||
this._id = id;
|
||||
this._page = page;
|
||||
this._parentFrame = parentFrame;
|
||||
|
|
@ -406,18 +407,18 @@ export class Frame {
|
|||
for (const event of events) {
|
||||
// Checking whether we have already notified about this event.
|
||||
if (!this._subtreeLifecycleEvents.has(event)) {
|
||||
this._eventEmitter.emit(kAddLifecycleEvent, event);
|
||||
this.emit(Frame.Events.AddLifecycle, event);
|
||||
if (this === mainFrame && this._url !== 'about:blank')
|
||||
debugLogger.log('api', ` "${event}" event fired`);
|
||||
if (this === mainFrame && event === 'load')
|
||||
this._page.emit(Events.Page.Load);
|
||||
this._page.emit(Page.Events.Load);
|
||||
if (this === mainFrame && event === 'domcontentloaded')
|
||||
this._page.emit(Events.Page.DOMContentLoaded);
|
||||
this._page.emit(Page.Events.DOMContentLoaded);
|
||||
}
|
||||
}
|
||||
for (const event of this._subtreeLifecycleEvents) {
|
||||
if (!events.has(event))
|
||||
this._eventEmitter.emit(kRemoveLifecycleEvent, event);
|
||||
this.emit(Frame.Events.RemoveLifecycle, event);
|
||||
}
|
||||
this._subtreeLifecycleEvents = events;
|
||||
}
|
||||
|
|
@ -436,13 +437,13 @@ export class Frame {
|
|||
}
|
||||
url = helper.completeUserURL(url);
|
||||
|
||||
const sameDocument = helper.waitForEvent(progress, this._eventEmitter, kNavigationEvent, (e: NavigationEvent) => !e.newDocument);
|
||||
const sameDocument = helper.waitForEvent(progress, this, Frame.Events.Navigation, (e: NavigationEvent) => !e.newDocument);
|
||||
const navigateResult = await this._page._delegate.navigateFrame(this, url, referer);
|
||||
|
||||
let event: NavigationEvent;
|
||||
if (navigateResult.newDocumentId) {
|
||||
sameDocument.dispose();
|
||||
event = await helper.waitForEvent(progress, this._eventEmitter, kNavigationEvent, (event: NavigationEvent) => {
|
||||
event = await helper.waitForEvent(progress, this, Frame.Events.Navigation, (event: NavigationEvent) => {
|
||||
// We are interested either in this specific document, or any other document that
|
||||
// did commit and replaced the expected document.
|
||||
return event.newDocument && (event.newDocument.documentId === navigateResult.newDocumentId || !event.error);
|
||||
|
|
@ -460,7 +461,7 @@ export class Frame {
|
|||
}
|
||||
|
||||
if (!this._subtreeLifecycleEvents.has(waitUntil))
|
||||
await helper.waitForEvent(progress, this._eventEmitter, kAddLifecycleEvent, (e: types.LifecycleEvent) => e === waitUntil).promise;
|
||||
await helper.waitForEvent(progress, this, Frame.Events.AddLifecycle, (e: types.LifecycleEvent) => e === waitUntil).promise;
|
||||
|
||||
const request = event.newDocument ? event.newDocument.request : undefined;
|
||||
const response = request ? request._finalRequest().response() : null;
|
||||
|
|
@ -474,7 +475,7 @@ export class Frame {
|
|||
const waitUntil = verifyLifecycle('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil);
|
||||
progress.log(`waiting for navigation until "${waitUntil}"`);
|
||||
|
||||
const navigationEvent: NavigationEvent = await helper.waitForEvent(progress, this._eventEmitter, kNavigationEvent, (event: NavigationEvent) => {
|
||||
const navigationEvent: NavigationEvent = await helper.waitForEvent(progress, this, Frame.Events.Navigation, (event: NavigationEvent) => {
|
||||
// Any failed navigation results in a rejection.
|
||||
if (event.error)
|
||||
return true;
|
||||
|
|
@ -485,7 +486,7 @@ export class Frame {
|
|||
throw navigationEvent.error;
|
||||
|
||||
if (!this._subtreeLifecycleEvents.has(waitUntil))
|
||||
await helper.waitForEvent(progress, this._eventEmitter, kAddLifecycleEvent, (e: types.LifecycleEvent) => e === waitUntil).promise;
|
||||
await helper.waitForEvent(progress, this, Frame.Events.AddLifecycle, (e: types.LifecycleEvent) => e === waitUntil).promise;
|
||||
|
||||
const request = navigationEvent.newDocument ? navigationEvent.newDocument.request : undefined;
|
||||
return request ? request._finalRequest().response() : null;
|
||||
|
|
@ -499,7 +500,7 @@ export class Frame {
|
|||
async _waitForLoadState(progress: Progress, state: types.LifecycleEvent): Promise<void> {
|
||||
const waitUntil = verifyLifecycle('state', state);
|
||||
if (!this._subtreeLifecycleEvents.has(waitUntil))
|
||||
await helper.waitForEvent(progress, this._eventEmitter, kAddLifecycleEvent, (e: types.LifecycleEvent) => e === waitUntil).promise;
|
||||
await helper.waitForEvent(progress, this, Frame.Events.AddLifecycle, (e: types.LifecycleEvent) => e === waitUntil).promise;
|
||||
}
|
||||
|
||||
async frameElement(): Promise<dom.ElementHandle> {
|
||||
|
|
@ -752,7 +753,7 @@ export class Frame {
|
|||
resolve();
|
||||
});
|
||||
const errorPromise = new Promise(resolve => {
|
||||
listeners.push(helper.addEventListener(this._page, Events.Page.Console, (message: ConsoleMessage) => {
|
||||
listeners.push(helper.addEventListener(this._page, Page.Events.Console, (message: ConsoleMessage) => {
|
||||
if (message.type() === 'error' && message.text().includes('Content Security Policy')) {
|
||||
cspMessage = message;
|
||||
resolve();
|
||||
|
|
@ -1045,7 +1046,7 @@ class SignalBarrier {
|
|||
|
||||
async addFrameNavigation(frame: Frame) {
|
||||
this.retain();
|
||||
const waiter = helper.waitForEvent(null, frame._eventEmitter, kNavigationEvent, (e: NavigationEvent) => {
|
||||
const waiter = helper.waitForEvent(null, frame, Frame.Events.Navigation, (e: NavigationEvent) => {
|
||||
if (!e.error && this._progress)
|
||||
this._progress.log(` navigated to "${frame._url}"`);
|
||||
return true;
|
||||
|
|
|
|||
48
src/page.ts
48
src/page.ts
|
|
@ -24,7 +24,6 @@ import * as network from './network';
|
|||
import { Screenshotter } from './screenshotter';
|
||||
import { TimeoutSettings } from './timeoutSettings';
|
||||
import * as types from './types';
|
||||
import { Events } from './events';
|
||||
import { BrowserContext } from './browserContext';
|
||||
import { ConsoleMessage } from './console';
|
||||
import * as accessibility from './accessibility';
|
||||
|
|
@ -91,6 +90,29 @@ type PageState = {
|
|||
};
|
||||
|
||||
export class Page extends EventEmitter {
|
||||
static Events = {
|
||||
Close: 'close',
|
||||
Crash: 'crash',
|
||||
Console: 'console',
|
||||
Dialog: 'dialog',
|
||||
Download: 'download',
|
||||
FileChooser: 'filechooser',
|
||||
DOMContentLoaded: 'domcontentloaded',
|
||||
// Can't use just 'error' due to node.js special treatment of error events.
|
||||
// @see https://nodejs.org/api/events.html#events_error_events
|
||||
PageError: 'pageerror',
|
||||
Request: 'request',
|
||||
Response: 'response',
|
||||
RequestFailed: 'requestfailed',
|
||||
RequestFinished: 'requestfinished',
|
||||
FrameAttached: 'frameattached',
|
||||
FrameDetached: 'framedetached',
|
||||
FrameNavigated: 'framenavigated',
|
||||
Load: 'load',
|
||||
Popup: 'popup',
|
||||
Worker: 'worker',
|
||||
};
|
||||
|
||||
private _closedState: 'open' | 'closing' | 'closed' = 'open';
|
||||
private _closedCallback: () => void;
|
||||
private _closedPromise: Promise<void>;
|
||||
|
|
@ -154,13 +176,13 @@ export class Page extends EventEmitter {
|
|||
this._frameManager.dispose();
|
||||
assert(this._closedState !== 'closed', 'Page closed twice');
|
||||
this._closedState = 'closed';
|
||||
this.emit(Events.Page.Close);
|
||||
this.emit(Page.Events.Close);
|
||||
this._closedCallback();
|
||||
}
|
||||
|
||||
_didCrash() {
|
||||
this._frameManager.dispose();
|
||||
this.emit(Events.Page.Crash);
|
||||
this.emit(Page.Events.Crash);
|
||||
this._crashedCallback(new Error('Page crashed'));
|
||||
}
|
||||
|
||||
|
|
@ -179,12 +201,12 @@ export class Page extends EventEmitter {
|
|||
|
||||
async _onFileChooserOpened(handle: dom.ElementHandle) {
|
||||
const multiple = await handle.evaluate(element => !!(element as HTMLInputElement).multiple);
|
||||
if (!this.listenerCount(Events.Page.FileChooser)) {
|
||||
if (!this.listenerCount(Page.Events.FileChooser)) {
|
||||
handle.dispose();
|
||||
return;
|
||||
}
|
||||
const fileChooser = new FileChooser(this, handle, multiple);
|
||||
this.emit(Events.Page.FileChooser, fileChooser);
|
||||
this.emit(Page.Events.FileChooser, fileChooser);
|
||||
}
|
||||
|
||||
context(): BrowserContext {
|
||||
|
|
@ -235,10 +257,10 @@ export class Page extends EventEmitter {
|
|||
_addConsoleMessage(type: string, args: js.JSHandle[], location: types.ConsoleMessageLocation, text?: string) {
|
||||
const message = new ConsoleMessage(type, text, args, location);
|
||||
const intercepted = this._frameManager.interceptConsoleMessage(message);
|
||||
if (intercepted || !this.listenerCount(Events.Page.Console))
|
||||
if (intercepted || !this.listenerCount(Page.Events.Console))
|
||||
args.forEach(arg => arg.dispose());
|
||||
else
|
||||
this.emit(Events.Page.Console, message);
|
||||
this.emit(Page.Events.Console, message);
|
||||
}
|
||||
|
||||
async reload(options?: types.NavigateOptions): Promise<network.Response | null> {
|
||||
|
|
@ -315,7 +337,7 @@ export class Page extends EventEmitter {
|
|||
}
|
||||
|
||||
_requestStarted(request: network.Request) {
|
||||
this.emit(Events.Page.Request, request);
|
||||
this.emit(Page.Events.Request, request);
|
||||
const route = request._route();
|
||||
if (!route)
|
||||
return;
|
||||
|
|
@ -362,20 +384,20 @@ export class Page extends EventEmitter {
|
|||
|
||||
_addWorker(workerId: string, worker: Worker) {
|
||||
this._workers.set(workerId, worker);
|
||||
this.emit(Events.Page.Worker, worker);
|
||||
this.emit(Page.Events.Worker, worker);
|
||||
}
|
||||
|
||||
_removeWorker(workerId: string) {
|
||||
const worker = this._workers.get(workerId);
|
||||
if (!worker)
|
||||
return;
|
||||
worker.emit(Events.Worker.Close, worker);
|
||||
worker.emit(Worker.Events.Close, worker);
|
||||
this._workers.delete(workerId);
|
||||
}
|
||||
|
||||
_clearWorkers() {
|
||||
for (const [workerId, worker] of this._workers) {
|
||||
worker.emit(Events.Worker.Close, worker);
|
||||
worker.emit(Worker.Events.Close, worker);
|
||||
this._workers.delete(workerId);
|
||||
}
|
||||
}
|
||||
|
|
@ -386,6 +408,10 @@ export class Page extends EventEmitter {
|
|||
}
|
||||
|
||||
export class Worker extends EventEmitter {
|
||||
static Events = {
|
||||
Close: 'close',
|
||||
};
|
||||
|
||||
private _url: string;
|
||||
private _executionContextPromise: Promise<js.ExecutionContext>;
|
||||
private _executionContextCallback: (value?: js.ExecutionContext) => void;
|
||||
|
|
|
|||
|
|
@ -15,14 +15,12 @@
|
|||
*/
|
||||
|
||||
import { BrowserContext } from '../../browserContext';
|
||||
import { Events } from '../../events';
|
||||
import { Dispatcher, DispatcherScope, lookupDispatcher } from './dispatcher';
|
||||
import { PageDispatcher, BindingCallDispatcher, WorkerDispatcher } from './pageDispatcher';
|
||||
import * as channels from '../channels';
|
||||
import { RouteDispatcher, RequestDispatcher } from './networkDispatchers';
|
||||
import { CRBrowserContext } from '../../chromium/crBrowser';
|
||||
import { CDPSessionDispatcher } from './cdpSessionDispatcher';
|
||||
import { Events as ChromiumEvents } from '../../chromium/events';
|
||||
|
||||
export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channels.BrowserContextInitializer> implements channels.BrowserContextChannel {
|
||||
private _context: BrowserContext;
|
||||
|
|
@ -33,8 +31,8 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
|
|||
|
||||
for (const page of context.pages())
|
||||
this._dispatchEvent('page', { page: new PageDispatcher(this._scope, page) });
|
||||
context.on(Events.BrowserContext.Page, page => this._dispatchEvent('page', { page: new PageDispatcher(this._scope, page) }));
|
||||
context.on(Events.BrowserContext.Close, () => {
|
||||
context.on(BrowserContext.Events.Page, page => this._dispatchEvent('page', { page: new PageDispatcher(this._scope, page) }));
|
||||
context.on(BrowserContext.Events.Close, () => {
|
||||
this._dispatchEvent('close');
|
||||
this._dispose();
|
||||
});
|
||||
|
|
@ -42,10 +40,10 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
|
|||
if (context._browser._options.name === 'chromium') {
|
||||
for (const page of (context as CRBrowserContext).backgroundPages())
|
||||
this._dispatchEvent('crBackgroundPage', { page: new PageDispatcher(this._scope, page) });
|
||||
context.on(ChromiumEvents.ChromiumBrowserContext.BackgroundPage, page => this._dispatchEvent('crBackgroundPage', { page: new PageDispatcher(this._scope, page) }));
|
||||
context.on(CRBrowserContext.CREvents.BackgroundPage, page => this._dispatchEvent('crBackgroundPage', { page: new PageDispatcher(this._scope, page) }));
|
||||
for (const serviceWorker of (context as CRBrowserContext).serviceWorkers())
|
||||
this._dispatchEvent('crServiceWorker', new WorkerDispatcher(this._scope, serviceWorker));
|
||||
context.on(ChromiumEvents.ChromiumBrowserContext.ServiceWorker, serviceWorker => this._dispatchEvent('crServiceWorker', { worker: new WorkerDispatcher(this._scope, serviceWorker) }));
|
||||
context.on(CRBrowserContext.CREvents.ServiceWorker, serviceWorker => this._dispatchEvent('crServiceWorker', { worker: new WorkerDispatcher(this._scope, serviceWorker) }));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
|
||||
import { Browser } from '../../browser';
|
||||
import { Events } from '../../events';
|
||||
import * as channels from '../channels';
|
||||
import { BrowserContextDispatcher } from './browserContextDispatcher';
|
||||
import { CDPSessionDispatcher } from './cdpSessionDispatcher';
|
||||
|
|
@ -26,7 +25,7 @@ import { PageDispatcher } from './pageDispatcher';
|
|||
export class BrowserDispatcher extends Dispatcher<Browser, channels.BrowserInitializer> implements channels.BrowserChannel {
|
||||
constructor(scope: DispatcherScope, browser: Browser, guid?: string) {
|
||||
super(scope, browser, 'Browser', { version: browser.version() }, true, guid);
|
||||
browser.on(Events.Browser.Disconnected, () => this._didClose());
|
||||
browser.on(Browser.Events.Disconnected, () => this._didClose());
|
||||
}
|
||||
|
||||
_didClose() {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
|
||||
import { Dispatcher, DispatcherScope, lookupDispatcher } from './dispatcher';
|
||||
import { Electron, ElectronApplication, ElectronEvents, ElectronPage } from '../../server/electron';
|
||||
import { Electron, ElectronApplication, ElectronPage } from '../../server/electron';
|
||||
import * as channels from '../channels';
|
||||
import { BrowserContextDispatcher } from './browserContextDispatcher';
|
||||
import { PageDispatcher } from './pageDispatcher';
|
||||
|
|
@ -37,11 +37,11 @@ export class ElectronApplicationDispatcher extends Dispatcher<ElectronApplicatio
|
|||
constructor(scope: DispatcherScope, electronApplication: ElectronApplication) {
|
||||
super(scope, electronApplication, 'ElectronApplication', {}, true);
|
||||
this._dispatchEvent('context', { context: new BrowserContextDispatcher(this._scope, electronApplication.context()) });
|
||||
electronApplication.on(ElectronEvents.ElectronApplication.Close, () => {
|
||||
electronApplication.on(ElectronApplication.Events.Close, () => {
|
||||
this._dispatchEvent('close');
|
||||
this._dispose();
|
||||
});
|
||||
electronApplication.on(ElectronEvents.ElectronApplication.Window, (page: ElectronPage) => {
|
||||
electronApplication.on(ElectronApplication.Events.Window, (page: ElectronPage) => {
|
||||
this._dispatchEvent('window', {
|
||||
page: lookupDispatcher<PageDispatcher>(page),
|
||||
browserWindow: createHandle(this._scope, page.browserWindow),
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Frame, kAddLifecycleEvent, kRemoveLifecycleEvent, kNavigationEvent, NavigationEvent } from '../../frames';
|
||||
import { Frame, NavigationEvent } from '../../frames';
|
||||
import * as types from '../../types';
|
||||
import * as channels from '../channels';
|
||||
import { Dispatcher, DispatcherScope, lookupNullableDispatcher, existingDispatcher } from './dispatcher';
|
||||
|
|
@ -38,13 +38,13 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameInitializer
|
|||
loadStates: Array.from(frame._subtreeLifecycleEvents),
|
||||
});
|
||||
this._frame = frame;
|
||||
frame._eventEmitter.on(kAddLifecycleEvent, (event: types.LifecycleEvent) => {
|
||||
frame.on(Frame.Events.AddLifecycle, (event: types.LifecycleEvent) => {
|
||||
this._dispatchEvent('loadstate', { add: event });
|
||||
});
|
||||
frame._eventEmitter.on(kRemoveLifecycleEvent, (event: types.LifecycleEvent) => {
|
||||
frame.on(Frame.Events.RemoveLifecycle, (event: types.LifecycleEvent) => {
|
||||
this._dispatchEvent('loadstate', { remove: event });
|
||||
});
|
||||
frame._eventEmitter.on(kNavigationEvent, (event: NavigationEvent) => {
|
||||
frame.on(Frame.Events.Navigation, (event: NavigationEvent) => {
|
||||
const params = { url: event.url, name: event.name, error: event.error ? event.error.message : undefined };
|
||||
if (event.newDocument)
|
||||
(params as any).newDocument = { request: RequestDispatcher.fromNullable(this._scope, event.newDocument.request || null) };
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
|
||||
import { BrowserContext } from '../../browserContext';
|
||||
import { Events } from '../../events';
|
||||
import { Frame } from '../../frames';
|
||||
import { Request } from '../../network';
|
||||
import { Page, Worker } from '../../page';
|
||||
|
|
@ -44,29 +43,29 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageInitializer> i
|
|||
isClosed: page.isClosed()
|
||||
});
|
||||
this._page = page;
|
||||
page.on(Events.Page.Close, () => this._dispatchEvent('close'));
|
||||
page.on(Events.Page.Console, message => this._dispatchEvent('console', { message: new ConsoleMessageDispatcher(this._scope, message) }));
|
||||
page.on(Events.Page.Crash, () => this._dispatchEvent('crash'));
|
||||
page.on(Events.Page.DOMContentLoaded, () => this._dispatchEvent('domcontentloaded'));
|
||||
page.on(Events.Page.Dialog, dialog => this._dispatchEvent('dialog', { dialog: new DialogDispatcher(this._scope, dialog) }));
|
||||
page.on(Events.Page.Download, dialog => this._dispatchEvent('download', { download: new DownloadDispatcher(this._scope, dialog) }));
|
||||
this._page.on(Events.Page.FileChooser, (fileChooser: FileChooser) => this._dispatchEvent('fileChooser', {
|
||||
page.on(Page.Events.Close, () => this._dispatchEvent('close'));
|
||||
page.on(Page.Events.Console, message => this._dispatchEvent('console', { message: new ConsoleMessageDispatcher(this._scope, message) }));
|
||||
page.on(Page.Events.Crash, () => this._dispatchEvent('crash'));
|
||||
page.on(Page.Events.DOMContentLoaded, () => this._dispatchEvent('domcontentloaded'));
|
||||
page.on(Page.Events.Dialog, dialog => this._dispatchEvent('dialog', { dialog: new DialogDispatcher(this._scope, dialog) }));
|
||||
page.on(Page.Events.Download, dialog => this._dispatchEvent('download', { download: new DownloadDispatcher(this._scope, dialog) }));
|
||||
this._page.on(Page.Events.FileChooser, (fileChooser: FileChooser) => this._dispatchEvent('fileChooser', {
|
||||
element: new ElementHandleDispatcher(this._scope, fileChooser.element()),
|
||||
isMultiple: fileChooser.isMultiple()
|
||||
}));
|
||||
page.on(Events.Page.FrameAttached, frame => this._onFrameAttached(frame));
|
||||
page.on(Events.Page.FrameDetached, frame => this._onFrameDetached(frame));
|
||||
page.on(Events.Page.Load, () => this._dispatchEvent('load'));
|
||||
page.on(Events.Page.PageError, error => this._dispatchEvent('pageError', { error: serializeError(error) }));
|
||||
page.on(Events.Page.Popup, page => this._dispatchEvent('popup', { page: lookupDispatcher<PageDispatcher>(page) }));
|
||||
page.on(Events.Page.Request, request => this._dispatchEvent('request', { request: RequestDispatcher.from(this._scope, request) }));
|
||||
page.on(Events.Page.RequestFailed, (request: Request) => this._dispatchEvent('requestFailed', {
|
||||
page.on(Page.Events.FrameAttached, frame => this._onFrameAttached(frame));
|
||||
page.on(Page.Events.FrameDetached, frame => this._onFrameDetached(frame));
|
||||
page.on(Page.Events.Load, () => this._dispatchEvent('load'));
|
||||
page.on(Page.Events.PageError, error => this._dispatchEvent('pageError', { error: serializeError(error) }));
|
||||
page.on(Page.Events.Popup, page => this._dispatchEvent('popup', { page: lookupDispatcher<PageDispatcher>(page) }));
|
||||
page.on(Page.Events.Request, request => this._dispatchEvent('request', { request: RequestDispatcher.from(this._scope, request) }));
|
||||
page.on(Page.Events.RequestFailed, (request: Request) => this._dispatchEvent('requestFailed', {
|
||||
request: RequestDispatcher.from(this._scope, request),
|
||||
failureText: request._failureText
|
||||
}));
|
||||
page.on(Events.Page.RequestFinished, request => this._dispatchEvent('requestFinished', { request: RequestDispatcher.from(scope, request) }));
|
||||
page.on(Events.Page.Response, response => this._dispatchEvent('response', { response: new ResponseDispatcher(this._scope, response) }));
|
||||
page.on(Events.Page.Worker, worker => this._dispatchEvent('worker', { worker: new WorkerDispatcher(this._scope, worker) }));
|
||||
page.on(Page.Events.RequestFinished, request => this._dispatchEvent('requestFinished', { request: RequestDispatcher.from(scope, request) }));
|
||||
page.on(Page.Events.Response, response => this._dispatchEvent('response', { response: new ResponseDispatcher(this._scope, response) }));
|
||||
page.on(Page.Events.Worker, worker => this._dispatchEvent('worker', { worker: new WorkerDispatcher(this._scope, worker) }));
|
||||
}
|
||||
|
||||
async setDefaultNavigationTimeoutNoReply(params: channels.PageSetDefaultNavigationTimeoutNoReplyParams): Promise<void> {
|
||||
|
|
@ -232,7 +231,7 @@ export class WorkerDispatcher extends Dispatcher<Worker, channels.WorkerInitiali
|
|||
super(scope, worker, 'Worker', {
|
||||
url: worker.url()
|
||||
});
|
||||
worker.on(Events.Worker.Close, () => this._dispatchEvent('close'));
|
||||
worker.on(Worker.Events.Close, () => this._dispatchEvent('close'));
|
||||
}
|
||||
|
||||
async evaluateExpression(params: channels.WorkerEvaluateExpressionParams): Promise<channels.WorkerEvaluateExpressionResult> {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ import * as path from 'path';
|
|||
import { CRBrowser, CRBrowserContext } from '../chromium/crBrowser';
|
||||
import { CRConnection, CRSession } from '../chromium/crConnection';
|
||||
import { CRExecutionContext } from '../chromium/crExecutionContext';
|
||||
import { Events } from '../events';
|
||||
import * as js from '../javascript';
|
||||
import { Page } from '../page';
|
||||
import { TimeoutSettings } from '../timeoutSettings';
|
||||
|
|
@ -42,19 +41,17 @@ export type ElectronLaunchOptionsBase = {
|
|||
timeout?: number,
|
||||
};
|
||||
|
||||
export const ElectronEvents = {
|
||||
ElectronApplication: {
|
||||
Close: 'close',
|
||||
Window: 'window',
|
||||
}
|
||||
};
|
||||
|
||||
export interface ElectronPage extends Page {
|
||||
browserWindow: js.JSHandle<BrowserWindow>;
|
||||
_browserWindowId: number;
|
||||
}
|
||||
|
||||
export class ElectronApplication extends EventEmitter {
|
||||
static Events = {
|
||||
Close: 'close',
|
||||
Window: 'window',
|
||||
};
|
||||
|
||||
private _browserContext: CRBrowserContext;
|
||||
private _nodeConnection: CRConnection;
|
||||
private _nodeSession: CRSession;
|
||||
|
|
@ -67,11 +64,11 @@ export class ElectronApplication extends EventEmitter {
|
|||
constructor(browser: CRBrowser, nodeConnection: CRConnection) {
|
||||
super();
|
||||
this._browserContext = browser._defaultContext as CRBrowserContext;
|
||||
this._browserContext.on(Events.BrowserContext.Close, () => {
|
||||
this._browserContext.on(BrowserContext.Events.Close, () => {
|
||||
// Emit application closed after context closed.
|
||||
Promise.resolve().then(() => this.emit(ElectronEvents.ElectronApplication.Close));
|
||||
Promise.resolve().then(() => this.emit(ElectronApplication.Events.Close));
|
||||
});
|
||||
this._browserContext.on(Events.BrowserContext.Page, event => this._onPage(event));
|
||||
this._browserContext.on(BrowserContext.Events.Page, event => this._onPage(event));
|
||||
this._nodeConnection = nodeConnection;
|
||||
this._nodeSession = nodeConnection.rootSession;
|
||||
}
|
||||
|
|
@ -85,13 +82,13 @@ export class ElectronApplication extends EventEmitter {
|
|||
return;
|
||||
page.browserWindow = handle;
|
||||
page._browserWindowId = windowId;
|
||||
page.on(Events.Page.Close, () => {
|
||||
page.on(Page.Events.Close, () => {
|
||||
page.browserWindow.dispose();
|
||||
this._windows.delete(page);
|
||||
});
|
||||
this._windows.add(page);
|
||||
await page.mainFrame().waitForLoadState('domcontentloaded').catch(e => {}); // can happen after detach
|
||||
this.emit(ElectronEvents.ElectronApplication.Window, page);
|
||||
this.emit(ElectronApplication.Events.Window, page);
|
||||
}
|
||||
|
||||
async newBrowserWindow(options: any): Promise<Page> {
|
||||
|
|
@ -106,7 +103,7 @@ export class ElectronApplication extends EventEmitter {
|
|||
return page;
|
||||
}
|
||||
|
||||
return await this._waitForEvent(ElectronEvents.ElectronApplication.Window, (page: ElectronPage) => page._browserWindowId === windowId);
|
||||
return await this._waitForEvent(ElectronApplication.Events.Window, (page: ElectronPage) => page._browserWindowId === windowId);
|
||||
}
|
||||
|
||||
context(): BrowserContext {
|
||||
|
|
@ -114,7 +111,7 @@ export class ElectronApplication extends EventEmitter {
|
|||
}
|
||||
|
||||
async close() {
|
||||
const closed = this._waitForEvent(ElectronEvents.ElectronApplication.Close);
|
||||
const closed = this._waitForEvent(ElectronApplication.Events.Close);
|
||||
await this._nodeElectronHandle!.evaluate(({ app }) => app.quit());
|
||||
this._nodeConnection.close();
|
||||
await closed;
|
||||
|
|
@ -122,7 +119,7 @@ export class ElectronApplication extends EventEmitter {
|
|||
|
||||
private async _waitForEvent(event: string, predicate?: Function): Promise<any> {
|
||||
const progressController = new ProgressController(this._timeoutSettings.timeout({}));
|
||||
if (event !== ElectronEvents.ElectronApplication.Close)
|
||||
if (event !== ElectronApplication.Events.Close)
|
||||
this._browserContext._closePromise.then(error => progressController.abort(error));
|
||||
return progressController.run(progress => helper.waitForEvent(progress, this, event, predicate).promise);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
import { Browser, BrowserOptions } from '../browser';
|
||||
import { assertBrowserContextIsNotOwned, BrowserContext, validateBrowserContextOptions, verifyGeolocation } from '../browserContext';
|
||||
import { Events } from '../events';
|
||||
import { helper, RegisteredListener, assert } from '../helper';
|
||||
import * as network from '../network';
|
||||
import { Page, PageBinding } from '../page';
|
||||
|
|
@ -150,13 +149,13 @@ export class WKBrowser extends Browser {
|
|||
const page = wkPage._page;
|
||||
if (pageOrError instanceof Error)
|
||||
page._setIsError();
|
||||
context!.emit(Events.BrowserContext.Page, page);
|
||||
context!.emit(BrowserContext.Events.Page, page);
|
||||
if (!opener)
|
||||
return;
|
||||
await opener.pageOrError();
|
||||
const openerPage = opener._page;
|
||||
if (!openerPage.isClosed())
|
||||
openerPage.emit(Events.Page.Popup, page);
|
||||
openerPage.emit(Page.Events.Popup, page);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,13 +15,12 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Screencast } from '../browserContext';
|
||||
import { Screencast, BrowserContext } from '../browserContext';
|
||||
import * as frames from '../frames';
|
||||
import { helper, RegisteredListener, assert, debugAssert } from '../helper';
|
||||
import * as dom from '../dom';
|
||||
import * as network from '../network';
|
||||
import { WKSession } from './wkConnection';
|
||||
import { Events } from '../events';
|
||||
import { WKExecutionContext } from './wkExecutionContext';
|
||||
import { WKInterceptableRequest } from './wkInterceptableRequest';
|
||||
import { WKWorkers } from './wkWorkers';
|
||||
|
|
@ -81,7 +80,7 @@ export class WKPage implements PageDelegate {
|
|||
this._workers = new WKWorkers(this._page);
|
||||
this._session = undefined as any as WKSession;
|
||||
this._browserContext = browserContext;
|
||||
this._page.on(Events.Page.FrameDetached, (frame: frames.Frame) => this._removeContextsForFrame(frame, false));
|
||||
this._page.on(Page.Events.FrameDetached, (frame: frames.Frame) => this._removeContextsForFrame(frame, false));
|
||||
this._eventListeners = [
|
||||
helper.addEventListener(this._pageProxySession, 'Target.targetCreated', this._onTargetCreated.bind(this)),
|
||||
helper.addEventListener(this._pageProxySession, 'Target.targetDestroyed', this._onTargetDestroyed.bind(this)),
|
||||
|
|
@ -474,7 +473,7 @@ export class WKPage implements PageDelegate {
|
|||
} else {
|
||||
error.stack = '';
|
||||
}
|
||||
this._page.emit(Events.Page.PageError, error);
|
||||
this._page.emit(Page.Events.PageError, error);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -524,7 +523,7 @@ export class WKPage implements PageDelegate {
|
|||
}
|
||||
|
||||
_onDialog(event: Protocol.Dialog.javascriptDialogOpeningPayload) {
|
||||
this._page.emit(Events.Page.Dialog, new dialog.Dialog(
|
||||
this._page.emit(Page.Events.Dialog, new dialog.Dialog(
|
||||
event.type as dialog.DialogType,
|
||||
event.message,
|
||||
async (accept: boolean, promptText?: string) => {
|
||||
|
|
@ -718,7 +717,7 @@ export class WKPage implements PageDelegate {
|
|||
height: options.height,
|
||||
scale: options.scale,
|
||||
});
|
||||
this._browserContext.emit(Events.BrowserContext.ScreencastStarted, new Screencast(options.outputFile, this._initializedPage!));
|
||||
this._browserContext.emit(BrowserContext.Events.ScreencastStarted, new Screencast(options.outputFile, this._initializedPage!));
|
||||
} catch (e) {
|
||||
this._recordingVideoFile = null;
|
||||
throw e;
|
||||
|
|
@ -731,7 +730,7 @@ export class WKPage implements PageDelegate {
|
|||
const fileName = this._recordingVideoFile;
|
||||
this._recordingVideoFile = null;
|
||||
await this._pageProxySession.send('Screencast.stopVideoRecording');
|
||||
this._browserContext.emit(Events.BrowserContext.ScreencastStopped, new Screencast(fileName, this._initializedPage!));
|
||||
this._browserContext.emit(BrowserContext.Events.ScreencastStopped, new Screencast(fileName, this._initializedPage!));
|
||||
}
|
||||
|
||||
async takeScreenshot(format: string, documentRect: types.Rect | undefined, viewportRect: types.Rect | undefined, quality: number | undefined): Promise<Buffer> {
|
||||
|
|
|
|||
|
|
@ -61,32 +61,11 @@ function traceAPICoverage(apiCoverage, api, events) {
|
|||
* @param {string} browserName
|
||||
*/
|
||||
function apiForBrowser(browserName) {
|
||||
const BROWSER_CONFIGS = [
|
||||
{
|
||||
name: 'Firefox',
|
||||
events: require('../lib/events').Events,
|
||||
},
|
||||
{
|
||||
name: 'WebKit',
|
||||
events: require('../lib/events').Events,
|
||||
},
|
||||
{
|
||||
name: 'Chromium',
|
||||
events: {
|
||||
...require('../lib/events').Events,
|
||||
...require('../lib/chromium/events').Events,
|
||||
}
|
||||
},
|
||||
];
|
||||
const browserConfig = BROWSER_CONFIGS.find(config => config.name.toLowerCase() === browserName);
|
||||
const events = browserConfig.events;
|
||||
// TODO: we should rethink our api.ts approach to ensure coverage and async stacks.
|
||||
const api = {
|
||||
...require('../lib/rpc/client/api'),
|
||||
};
|
||||
|
||||
const events = require('../lib/rpc/client/events').Events;
|
||||
const api = require('../lib/rpc/client/api');
|
||||
const otherBrowsers = ['chromium', 'webkit', 'firefox'].filter(name => name.toLowerCase() !== browserName.toLowerCase());
|
||||
const filteredKeys = Object.keys(api).filter(apiName => {
|
||||
return !BROWSER_CONFIGS.some(config => apiName.startsWith(config.name)) || apiName.startsWith(browserConfig.name);
|
||||
return !otherBrowsers.some(otherName => apiName.toLowerCase().startsWith(otherName));
|
||||
});
|
||||
const filteredAPI = {};
|
||||
for (const key of filteredKeys)
|
||||
|
|
|
|||
Loading…
Reference in a new issue