From f1f9dc166bb01e0233ac49f600d1672d0c0fd76b Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Tue, 10 Dec 2019 11:15:14 -0800 Subject: [PATCH] chore: remove unneeded files, reuse events between browsers, no implicit any (#191) --- src/ConnectionTransport.ts | 6 -- src/DeviceDescriptors.ts | 10 ++- src/chromium/Browser.ts | 2 +- src/chromium/Connection.ts | 2 +- src/chromium/FrameManager.ts | 48 +++++------ src/chromium/Launcher.ts | 15 ++-- src/chromium/LifecycleWatcher.ts | 2 +- src/chromium/Multimap.ts | 93 --------------------- src/chromium/NetworkManager.ts | 6 +- src/chromium/PipeTransport.ts | 2 +- src/chromium/Playwright.ts | 11 ++- src/chromium/Target.ts | 8 +- src/chromium/WebSocketTransport.ts | 2 +- src/chromium/tsconfig.json | 6 ++ src/firefox/Browser.ts | 5 +- src/firefox/Connection.ts | 2 +- src/firefox/FrameManager.ts | 23 +++-- src/firefox/Playwright.ts | 10 ++- src/firefox/WebSocketTransport.ts | 2 +- src/firefox/events.ts | 20 ----- src/injected/injected.ts | 10 +-- src/injected/tsconfig.json | 6 ++ src/types.ts | 7 ++ src/webkit/Connection.ts | 2 +- src/webkit/FrameManager.ts | 31 ++++--- src/webkit/Multimap.ts | 93 --------------------- src/webkit/PipeTransport.ts | 2 +- src/webkit/events.ts | 16 ---- utils/doclint/check_public_api/JSBuilder.js | 2 +- utils/protocol-types-generator/index.js | 2 + 30 files changed, 125 insertions(+), 321 deletions(-) delete mode 100644 src/ConnectionTransport.ts delete mode 100644 src/chromium/Multimap.ts create mode 100644 src/chromium/tsconfig.json create mode 100644 src/injected/tsconfig.json delete mode 100644 src/webkit/Multimap.ts diff --git a/src/ConnectionTransport.ts b/src/ConnectionTransport.ts deleted file mode 100644 index 640e1633de..0000000000 --- a/src/ConnectionTransport.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface ConnectionTransport { - send(s: string): void; - close(): void; - onmessage?: (message: string) => void, - onclose?: () => void, -} \ No newline at end of file diff --git a/src/DeviceDescriptors.ts b/src/DeviceDescriptors.ts index 8773f7471c..e387a309ab 100644 --- a/src/DeviceDescriptors.ts +++ b/src/DeviceDescriptors.ts @@ -15,7 +15,15 @@ * limitations under the License. */ -export const DeviceDescriptors = [ +import * as types from './types'; + +export type DeviceDescriptor = { + name: string, + userAgent: string, + viewport: types.Viewport, +}; + +export const DeviceDescriptors: DeviceDescriptor[] = [ { 'name': 'Blackberry PlayBook', 'userAgent': 'Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.1.0; en-US) AppleWebKit/536.2+ (KHTML like Gecko) Version/7.2.1.0 Safari/536.2+', diff --git a/src/chromium/Browser.ts b/src/chromium/Browser.ts index cc616f4136..f4a8a1d013 100644 --- a/src/chromium/Browser.ts +++ b/src/chromium/Browser.ts @@ -171,7 +171,7 @@ export class Browser extends EventEmitter { const existingTarget = this._allTargets().find(predicate); if (existingTarget) return existingTarget; - let resolve; + let resolve: (target: Target) => void; const targetPromise = new Promise(x => resolve = x); this.chromium.on(Events.Chromium.TargetCreated, check); this.chromium.on(Events.Chromium.TargetChanged, check); diff --git a/src/chromium/Connection.ts b/src/chromium/Connection.ts index a9fbe3bb1b..77821ef175 100644 --- a/src/chromium/Connection.ts +++ b/src/chromium/Connection.ts @@ -17,7 +17,7 @@ import * as debug from 'debug'; import { EventEmitter } from 'events'; -import { ConnectionTransport } from '../ConnectionTransport'; +import { ConnectionTransport } from '../types'; import { assert } from '../helper'; import { Protocol } from './protocol'; diff --git a/src/chromium/FrameManager.ts b/src/chromium/FrameManager.ts index 9c0b796d2d..520408ce63 100644 --- a/src/chromium/FrameManager.ts +++ b/src/chromium/FrameManager.ts @@ -28,7 +28,7 @@ import { LifecycleWatcher } from './LifecycleWatcher'; import { NetworkManager, NetworkManagerEvents } from './NetworkManager'; import { Page } from '../page'; import { Protocol } from './protocol'; -import { Events as CommonEvents } from '../events'; +import { Events } from '../events'; import { toConsoleMessageLocation, exceptionToError, releaseObject } from './protocolHelper'; import * as dialog from '../dialog'; import { PageDelegate } from '../page'; @@ -85,18 +85,18 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, (this._page as any).accessibility = new Accessibility(client); (this._page as any).coverage = new Coverage(client); (this._page as any).pdf = new PDF(client); - (this._page as any).workers = new Workers(client, this._page._addConsoleMessage.bind(this._page), error => this._page.emit(CommonEvents.Page.PageError, error)); + (this._page as any).workers = new Workers(client, this._page._addConsoleMessage.bind(this._page), error => this._page.emit(Events.Page.PageError, error)); (this._page as any).overrides = new Overrides(client); (this._page as any).interception = new Interception(this._networkManager); - this._networkManager.on(NetworkManagerEvents.Request, event => this._page.emit(CommonEvents.Page.Request, event)); - this._networkManager.on(NetworkManagerEvents.Response, event => this._page.emit(CommonEvents.Page.Response, event)); - this._networkManager.on(NetworkManagerEvents.RequestFailed, event => this._page.emit(CommonEvents.Page.RequestFailed, event)); - this._networkManager.on(NetworkManagerEvents.RequestFinished, event => this._page.emit(CommonEvents.Page.RequestFinished, event)); + this._networkManager.on(NetworkManagerEvents.Request, event => this._page.emit(Events.Page.Request, event)); + this._networkManager.on(NetworkManagerEvents.Response, event => this._page.emit(Events.Page.Response, event)); + this._networkManager.on(NetworkManagerEvents.RequestFailed, event => this._page.emit(Events.Page.RequestFailed, event)); + this._networkManager.on(NetworkManagerEvents.RequestFinished, event => this._page.emit(Events.Page.RequestFinished, event)); this._client.on('Inspector.targetCrashed', event => this._onTargetCrashed()); this._client.on('Log.entryAdded', event => this._onLogEntryAdded(event)); - this._client.on('Page.domContentEventFired', event => this._page.emit(CommonEvents.Page.DOMContentLoaded)); + this._client.on('Page.domContentEventFired', event => this._page.emit(Events.Page.DOMContentLoaded)); this._client.on('Page.fileChooserOpened', event => this._onFileChooserOpened(event)); this._client.on('Page.frameAttached', event => this._onFrameAttached(event.frameId, event.parentFrameId)); this._client.on('Page.frameDetached', event => this._onFrameDetached(event.frameId)); @@ -104,7 +104,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, this._client.on('Page.frameStoppedLoading', event => this._onFrameStoppedLoading(event.frameId)); this._client.on('Page.javascriptDialogOpening', event => this._onDialog(event)); this._client.on('Page.lifecycleEvent', event => this._onLifecycleEvent(event)); - this._client.on('Page.loadEventFired', event => this._page.emit(CommonEvents.Page.Load)); + this._client.on('Page.loadEventFired', event => this._page.emit(Events.Page.Load)); this._client.on('Page.navigatedWithinDocument', event => this._onFrameNavigatedWithinDocument(event.frameId, event.url)); this._client.on('Runtime.bindingCalled', event => this._onBindingCalled(event)); this._client.on('Runtime.consoleAPICalled', event => this._onConsoleAPI(event)); @@ -280,10 +280,10 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, id: frameId, loaderId: '', }; - frame[frameDataSymbol] = data; + (frame as any)[frameDataSymbol] = data; this._frames.set(frameId, frame); this.emit(FrameManagerEvents.FrameAttached, frame); - this._page.emit(CommonEvents.Page.FrameAttached, frame); + this._page.emit(Events.Page.FrameAttached, frame); } _onFrameNavigated(framePayload: Protocol.Page.Frame) { @@ -311,7 +311,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, id: framePayload.id, loaderId: '', }; - frame[frameDataSymbol] = data; + (frame as any)[frameDataSymbol] = data; } this._frames.set(framePayload.id, frame); this._mainFrame = frame; @@ -321,7 +321,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, frame._navigated(framePayload.url, framePayload.name); this.emit(FrameManagerEvents.FrameNavigated, frame); - this._page.emit(CommonEvents.Page.FrameNavigated, frame); + this._page.emit(Events.Page.FrameNavigated, frame); } async _ensureIsolatedWorld(name: string) { @@ -346,7 +346,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, frame._navigated(url, frame.name()); this.emit(FrameManagerEvents.FrameNavigatedWithinDocument, frame); this.emit(FrameManagerEvents.FrameNavigated, frame); - this._page.emit(CommonEvents.Page.FrameNavigated, frame); + this._page.emit(Events.Page.FrameNavigated, frame); } _onFrameDetached(frameId: string) { @@ -355,16 +355,16 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, this._removeFramesRecursively(frame); } - _onExecutionContextCreated(contextPayload) { + _onExecutionContextCreated(contextPayload: Protocol.Runtime.ExecutionContextDescription) { const frameId = contextPayload.auxData ? contextPayload.auxData.frameId : null; const frame = this._frames.get(frameId) || null; - if (contextPayload.auxData && contextPayload.auxData['type'] === 'isolated') + if (contextPayload.auxData && contextPayload.auxData.type === 'isolated') this._isolatedWorlds.add(contextPayload.name); const context = new js.ExecutionContext(new ExecutionContextDelegate(this._client, contextPayload)); if (frame) context._domWorld = new dom.DOMWorld(context, new DOMWorldDelegate(this, frame)); if (frame) { - if (contextPayload.auxData && !!contextPayload.auxData['isDefault']) + if (contextPayload.auxData && !!contextPayload.auxData.isDefault) frame._contextCreated('main', context); else if (contextPayload.name === UTILITY_WORLD_NAME) frame._contextCreated('utility', context); @@ -398,7 +398,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, frame._detach(); this._frames.delete(this._frameData(frame).id); this.emit(FrameManagerEvents.FrameDetached, frame); - this._page.emit(CommonEvents.Page.FrameDetached, frame); + this._page.emit(Events.Page.FrameDetached, frame); } async _onConsoleAPI(event: Protocol.Runtime.consoleAPICalledPayload) { @@ -435,7 +435,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, } _onDialog(event : Protocol.Page.javascriptDialogOpeningPayload) { - this._page.emit(CommonEvents.Page.Dialog, new dialog.Dialog( + this._page.emit(Events.Page.Dialog, new dialog.Dialog( event.type as dialog.DialogType, event.message, async (accept: boolean, promptText?: string) => { @@ -445,7 +445,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, } _handleException(exceptionDetails: Protocol.Runtime.ExceptionDetails) { - this._page.emit(CommonEvents.Page.PageError, exceptionToError(exceptionDetails)); + this._page.emit(Events.Page.PageError, exceptionToError(exceptionDetails)); } _onTargetCrashed() { @@ -457,7 +457,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, if (args) args.map(arg => releaseObject(this._client, arg)); if (source !== 'worker') - this._page.emit(CommonEvents.Page.Console, new ConsoleMessage(level, text, [], {url, lineNumber})); + this._page.emit(Events.Page.Console, new ConsoleMessage(level, text, [], {url, lineNumber})); } async _onFileChooserOpened(event: Protocol.Page.fileChooserOpenedPayload) { @@ -550,8 +550,8 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, } } -function assertNoLegacyNavigationOptions(options) { - assert(options['networkIdleTimeout'] === undefined, 'ERROR: networkIdleTimeout option is no longer supported.'); - assert(options['networkIdleInflight'] === undefined, 'ERROR: networkIdleInflight option is no longer supported.'); - assert(options.waitUntil !== 'networkidle', 'ERROR: "networkidle" option is no longer supported. Use "networkidle2" instead'); +function assertNoLegacyNavigationOptions(options: frames.NavigateOptions) { + assert((options as any)['networkIdleTimeout'] === undefined, 'ERROR: networkIdleTimeout option is no longer supported.'); + assert((options as any)['networkIdleInflight'] === undefined, 'ERROR: networkIdleInflight option is no longer supported.'); + assert((options as any).waitUntil !== 'networkidle', 'ERROR: "networkidle" option is no longer supported. Use "networkidle2" instead'); } diff --git a/src/chromium/Launcher.ts b/src/chromium/Launcher.ts index 22eb323c92..c40f661ce0 100644 --- a/src/chromium/Launcher.ts +++ b/src/chromium/Launcher.ts @@ -31,7 +31,7 @@ import { assert, debugError, helper } from '../helper'; import * as types from '../types'; import { PipeTransport } from './PipeTransport'; import { WebSocketTransport } from './WebSocketTransport'; -import { ConnectionTransport } from '../ConnectionTransport'; +import { ConnectionTransport } from '../types'; import * as util from 'util'; const mkdtempAsync = helper.promisify(fs.mkdtemp); @@ -100,7 +100,7 @@ export class Launcher { else chromeArguments.push(...args); - let temporaryUserDataDir = null; + let temporaryUserDataDir: string | null = null; if (!chromeArguments.some(argument => argument.startsWith('--remote-debugging-'))) chromeArguments.push(pipe ? '--remote-debugging-pipe' : '--remote-debugging-port=0'); @@ -139,7 +139,7 @@ export class Launcher { ); if (!chromeProcess.pid) { - let reject; + let reject: (e: Error) => void; const result = new Promise((f, r) => reject = r); chromeProcess.once('error', error => { reject(new Error('Failed to launch browser: ' + error)); @@ -160,7 +160,7 @@ export class Launcher { if (temporaryUserDataDir) { removeFolderAsync(temporaryUserDataDir) .then(() => fulfill()) - .catch(err => console.error(err)); + .catch((err: Error) => console.error(err)); } else { fulfill(); } @@ -344,7 +344,8 @@ function waitForWSEndpoint(chromeProcess: childProcess.ChildProcess, timeout: nu } function getWSEndpoint(browserURL: string): Promise { - let resolve, reject; + let resolve: (url: string) => void; + let reject: (e: Error) => void; const promise = new Promise((res, rej) => { resolve = res; reject = rej; }); const endpointURL = URL.resolve(browserURL, '/json/version'); @@ -423,7 +424,7 @@ export function createBrowserFetcher(projectRoot: string, options: BrowserFetche ...defaultOptions, ...options, }; - assert(!!downloadURLs[options.platform], 'Unsupported platform: ' + options.platform); + assert(!!(downloadURLs as any)[options.platform], 'Unsupported platform: ' + options.platform); return new BrowserFetcher(options.path, options.platform, (platform: string, revision: string) => { let archiveName = ''; @@ -440,7 +441,7 @@ export function createBrowserFetcher(projectRoot: string, options: BrowserFetche executablePath = path.join(archiveName, 'chrome.exe'); } return { - downloadUrl: util.format(downloadURLs[platform], options.host, revision, archiveName), + downloadUrl: util.format((downloadURLs as any)[platform], options.host, revision, archiveName), executablePath }; }); diff --git a/src/chromium/LifecycleWatcher.ts b/src/chromium/LifecycleWatcher.ts index 026a0640b7..2f0c86b7ee 100644 --- a/src/chromium/LifecycleWatcher.ts +++ b/src/chromium/LifecycleWatcher.ts @@ -18,7 +18,7 @@ import { CDPSessionEvents } from './Connection'; import { TimeoutError } from '../Errors'; import { FrameManager, FrameManagerEvents } from './FrameManager'; -import { assert, helper, RegisteredListener } from '../helper'; +import { helper, RegisteredListener } from '../helper'; import { NetworkManagerEvents } from './NetworkManager'; import * as frames from '../frames'; import * as network from '../network'; diff --git a/src/chromium/Multimap.ts b/src/chromium/Multimap.ts deleted file mode 100644 index 835a28e592..0000000000 --- a/src/chromium/Multimap.ts +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Copyright 2017 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 class Multimap { - private _map: Map>; - - constructor() { - this._map = new Map(); - } - - set(key: T, value: V) { - let set = this._map.get(key); - if (!set) { - set = new Set(); - this._map.set(key, set); - } - set.add(value); - } - - get(key: T): Set { - let result = this._map.get(key); - if (!result) - result = new Set(); - return result; - } - - has(key: T): boolean { - return this._map.has(key); - } - - hasValue(key: T, value: V): boolean { - const set = this._map.get(key); - if (!set) - return false; - return set.has(value); - } - - get size(): number { - return this._map.size; - } - - delete(key: T, value: V): boolean { - const values = this.get(key); - const result = values.delete(value); - if (!values.size) - this._map.delete(key); - return result; - } - - deleteAll(key: T) { - this._map.delete(key); - } - - firstValue(key: T): V { - const set = this._map.get(key); - if (!set) - return null; - return set.values().next().value; - } - - firstKey(): T { - return this._map.keys().next().value; - } - - valuesArray(): V[] { - const result = []; - for (const key of this._map.keys()) - result.push(...Array.from(this._map.get(key).values())); - return result; - } - - keysArray(): T[] { - return Array.from(this._map.keys()); - } - - clear() { - this._map.clear(); - } -} diff --git a/src/chromium/NetworkManager.ts b/src/chromium/NetworkManager.ts index c5ef9c26e7..af16f35215 100644 --- a/src/chromium/NetworkManager.ts +++ b/src/chromium/NetworkManager.ts @@ -337,7 +337,7 @@ class InterceptableRequest { await this._client.send('Fetch.fulfillRequest', { requestId: this._interceptionId, responseCode: response.status || 200, - responsePhrase: STATUS_TEXTS[response.status || 200], + responsePhrase: STATUS_TEXTS[String(response.status || 200)], responseHeaders: headersArray(responseHeaders), body: responseBody ? responseBody.toString('base64') : undefined, }).catch(error => { @@ -367,7 +367,7 @@ class InterceptableRequest { } } -const errorReasons = { +const errorReasons: { [reason: string]: Protocol.Network.ErrorReason } = { 'aborted': 'Aborted', 'accessdenied': 'AccessDenied', 'addressunreachable': 'AddressUnreachable', @@ -401,7 +401,7 @@ function headersObject(headers: Protocol.Network.Headers): network.Headers { } // List taken from https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml with extra 306 and 418 codes. -const STATUS_TEXTS = { +const STATUS_TEXTS: { [status: string]: string } = { '100': 'Continue', '101': 'Switching Protocols', '102': 'Processing', diff --git a/src/chromium/PipeTransport.ts b/src/chromium/PipeTransport.ts index 665d135c97..74db2f8188 100644 --- a/src/chromium/PipeTransport.ts +++ b/src/chromium/PipeTransport.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { ConnectionTransport } from '../ConnectionTransport'; +import { ConnectionTransport } from '../types'; import { debugError, helper, RegisteredListener } from '../helper'; export class PipeTransport implements ConnectionTransport { diff --git a/src/chromium/Playwright.ts b/src/chromium/Playwright.ts index 588c257cfb..6cf5608f42 100644 --- a/src/chromium/Playwright.ts +++ b/src/chromium/Playwright.ts @@ -14,13 +14,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { Browser } from './Browser'; import { BrowserFetcher, BrowserFetcherOptions, BrowserFetcherRevisionInfo, OnProgressCallback } from '../browserFetcher'; -import { ConnectionTransport } from '../ConnectionTransport'; -import { DeviceDescriptors } from '../DeviceDescriptors'; +import { ConnectionTransport } from '../types'; +import { DeviceDescriptors, DeviceDescriptor } from '../DeviceDescriptors'; import * as Errors from '../Errors'; import { Launcher, LauncherBrowserOptions, LauncherChromeArgOptions, LauncherLaunchOptions, createBrowserFetcher } from './Launcher'; +type Devices = { [name: string]: DeviceDescriptor } & DeviceDescriptor[]; + export class Playwright { private _projectRoot: string; private _launcher: Launcher; @@ -54,8 +57,8 @@ export class Playwright { return this._launcher.executablePath(); } - get devices(): any { - const result = DeviceDescriptors.slice(); + get devices(): Devices { + const result = DeviceDescriptors.slice() as Devices; for (const device of DeviceDescriptors) result[device.name] = device; return result; diff --git a/src/chromium/Target.ts b/src/chromium/Target.ts index 12b5ab27b0..07af08c8de 100644 --- a/src/chromium/Target.ts +++ b/src/chromium/Target.ts @@ -19,7 +19,7 @@ import * as types from '../types'; import { Browser } from './Browser'; import { BrowserContext } from './BrowserContext'; import { CDPSession, CDPSessionEvents } from './Connection'; -import { Events as CommonEvents } from '../events'; +import { Events } from '../events'; import { Worker } from './features/workers'; import { Page } from '../page'; import { Protocol } from './protocol'; @@ -65,10 +65,10 @@ export class Target { if (!opener || !opener._pagePromise || this.type() !== 'page') return true; const openerPage = await opener._pagePromise; - if (!openerPage.listenerCount(CommonEvents.Page.Popup)) + if (!openerPage.listenerCount(Events.Page.Popup)) return true; const popupPage = await this.page(); - openerPage.emit(CommonEvents.Page.Popup, popupPage); + openerPage.emit(Events.Page.Popup, popupPage); return true; }); this._isInitialized = this._targetInfo.type !== 'page' || this._targetInfo.url !== ''; @@ -87,7 +87,7 @@ export class Target { const frameManager = new FrameManager(client, this._browserContext, this._ignoreHTTPSErrors); const page = frameManager.page(); this._page = page; - page[targetSymbol] = this; + (page as any)[targetSymbol] = this; client.once(CDPSessionEvents.Disconnected, () => page._didDisconnect()); client.on('Target.attachedToTarget', event => { if (event.targetInfo.type !== 'worker') { diff --git a/src/chromium/WebSocketTransport.ts b/src/chromium/WebSocketTransport.ts index 739d53db8f..57306ed475 100644 --- a/src/chromium/WebSocketTransport.ts +++ b/src/chromium/WebSocketTransport.ts @@ -16,7 +16,7 @@ */ import * as WebSocket from 'ws'; -import { ConnectionTransport } from '../ConnectionTransport'; +import { ConnectionTransport } from '../types'; export class WebSocketTransport implements ConnectionTransport { private _ws: WebSocket; diff --git a/src/chromium/tsconfig.json b/src/chromium/tsconfig.json new file mode 100644 index 0000000000..beccd788d8 --- /dev/null +++ b/src/chromium/tsconfig.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "noImplicitAny": true + }, + "extends": "../../tsconfig.json" +} diff --git a/src/firefox/Browser.ts b/src/firefox/Browser.ts index e2c8880d10..839370348c 100644 --- a/src/firefox/Browser.ts +++ b/src/firefox/Browser.ts @@ -20,6 +20,7 @@ import { assert, helper, RegisteredListener } from '../helper'; import { filterCookies, NetworkCookie, SetNetworkCookieParam, rewriteCookies } from '../network'; import { Connection, ConnectionEvents, JugglerSessionEvents } from './Connection'; import { Events } from './events'; +import { Events as CommonEvents } from '../events'; import { Permissions } from './features/permissions'; import { Page } from '../page'; import * as types from '../types'; @@ -164,9 +165,9 @@ export class Browser extends EventEmitter { this._targets.set(targetId, target); if (target.opener() && target.opener()._pagePromise) { const openerPage = await target.opener()._pagePromise; - if (openerPage.listenerCount(Events.Page.Popup)) { + if (openerPage.listenerCount(CommonEvents.Page.Popup)) { const popupPage = await target.page(); - openerPage.emit(Events.Page.Popup, popupPage); + openerPage.emit(CommonEvents.Page.Popup, popupPage); } } } diff --git a/src/firefox/Connection.ts b/src/firefox/Connection.ts index 6474713b90..5a69a08a5e 100644 --- a/src/firefox/Connection.ts +++ b/src/firefox/Connection.ts @@ -18,7 +18,7 @@ import {assert} from '../helper'; import {EventEmitter} from 'events'; import * as debug from 'debug'; -import { ConnectionTransport } from '../ConnectionTransport'; +import { ConnectionTransport } from '../types'; import { Protocol } from './protocol'; const debugProtocol = debug('playwright:protocol'); diff --git a/src/firefox/FrameManager.ts b/src/firefox/FrameManager.ts index 336ed1e6b2..6c12273dda 100644 --- a/src/firefox/FrameManager.ts +++ b/src/firefox/FrameManager.ts @@ -27,8 +27,7 @@ import { NavigationWatchdog, NextNavigationWatchdog } from './NavigationWatchdog import { Page, PageDelegate } from '../page'; import { NetworkManager, NetworkManagerEvents } from './NetworkManager'; import { DOMWorldDelegate } from './JSHandle'; -import { Events } from './events'; -import { Events as CommonEvents } from '../events'; +import { Events } from '../events'; import * as dialog from '../dialog'; import { Protocol } from './protocol'; import * as input from '../input'; @@ -89,10 +88,10 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, helper.addEventListener(this._session, 'Page.dialogOpened', this._onDialogOpened.bind(this)), helper.addEventListener(this._session, 'Page.bindingCalled', this._onBindingCalled.bind(this)), helper.addEventListener(this._session, 'Page.fileChooserOpened', this._onFileChooserOpened.bind(this)), - helper.addEventListener(this._networkManager, NetworkManagerEvents.Request, request => this._page.emit(CommonEvents.Page.Request, request)), - helper.addEventListener(this._networkManager, NetworkManagerEvents.Response, response => this._page.emit(CommonEvents.Page.Response, response)), - helper.addEventListener(this._networkManager, NetworkManagerEvents.RequestFinished, request => this._page.emit(CommonEvents.Page.RequestFinished, request)), - helper.addEventListener(this._networkManager, NetworkManagerEvents.RequestFailed, request => this._page.emit(CommonEvents.Page.RequestFailed, request)), + helper.addEventListener(this._networkManager, NetworkManagerEvents.Request, request => this._page.emit(Events.Page.Request, request)), + helper.addEventListener(this._networkManager, NetworkManagerEvents.Response, response => this._page.emit(Events.Page.Response, response)), + helper.addEventListener(this._networkManager, NetworkManagerEvents.RequestFinished, request => this._page.emit(Events.Page.RequestFinished, request)), + helper.addEventListener(this._networkManager, NetworkManagerEvents.RequestFailed, request => this._page.emit(Events.Page.RequestFailed, request)), ]; this._page = new Page(this, browserContext); (this._page as any).interception = new Interception(this._networkManager); @@ -164,14 +163,14 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, data.lastCommittedNavigationId = params.navigationId; frame._firedLifecycleEvents.clear(); this.emit(FrameManagerEvents.FrameNavigated, frame); - this._page.emit(CommonEvents.Page.FrameNavigated, frame); + this._page.emit(Events.Page.FrameNavigated, frame); } _onSameDocumentNavigation(params) { const frame = this._frames.get(params.frameId); frame._navigated(params.url, frame.name()); this.emit(FrameManagerEvents.FrameNavigated, frame); - this._page.emit(CommonEvents.Page.FrameNavigated, frame); + this._page.emit(Events.Page.FrameNavigated, frame); } _onFrameAttached(params) { @@ -188,7 +187,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, } this._frames.set(params.frameId, frame); this.emit(FrameManagerEvents.FrameAttached, frame); - this._page.emit(CommonEvents.Page.FrameAttached, frame); + this._page.emit(Events.Page.FrameAttached, frame); } _onFrameDetached(params) { @@ -196,7 +195,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, this._frames.delete(params.frameId); frame._detach(); this.emit(FrameManagerEvents.FrameDetached, frame); - this._page.emit(CommonEvents.Page.FrameDetached, frame); + this._page.emit(Events.Page.FrameDetached, frame); } _onEventFired({frameId, name}) { @@ -205,14 +204,14 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, frame._firedLifecycleEvents.add('load'); if (frame === this._mainFrame) { this.emit(FrameManagerEvents.Load); - this._page.emit(CommonEvents.Page.Load); + this._page.emit(Events.Page.Load); } } if (name === 'DOMContentLoaded') { frame._firedLifecycleEvents.add('domcontentloaded'); if (frame === this._mainFrame) { this.emit(FrameManagerEvents.DOMContentLoaded); - this._page.emit(CommonEvents.Page.DOMContentLoaded); + this._page.emit(Events.Page.DOMContentLoaded); } } } diff --git a/src/firefox/Playwright.ts b/src/firefox/Playwright.ts index 13e7542bf1..89af36b022 100644 --- a/src/firefox/Playwright.ts +++ b/src/firefox/Playwright.ts @@ -16,11 +16,13 @@ */ import { Browser } from './Browser'; import { BrowserFetcher, BrowserFetcherOptions, OnProgressCallback, BrowserFetcherRevisionInfo } from '../browserFetcher'; -import { ConnectionTransport } from '../ConnectionTransport'; -import { DeviceDescriptors } from '../DeviceDescriptors'; +import { ConnectionTransport } from '../types'; +import { DeviceDescriptors, DeviceDescriptor } from '../DeviceDescriptors'; import * as Errors from '../Errors'; import { Launcher, createBrowserFetcher } from './Launcher'; +type Devices = { [name: string]: DeviceDescriptor } & DeviceDescriptor[]; + export class Playwright { private _projectRoot: string; private _launcher: Launcher; @@ -54,8 +56,8 @@ export class Playwright { return this._launcher.executablePath(); } - get devices(): any { - const result = DeviceDescriptors.slice(); + get devices(): Devices { + const result = DeviceDescriptors.slice() as Devices; for (const device of DeviceDescriptors) result[device.name] = device; return result; diff --git a/src/firefox/WebSocketTransport.ts b/src/firefox/WebSocketTransport.ts index ed2b7a3515..93dcf6b631 100644 --- a/src/firefox/WebSocketTransport.ts +++ b/src/firefox/WebSocketTransport.ts @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { ConnectionTransport } from '../ConnectionTransport'; +import { ConnectionTransport } from '../types'; import * as WebSocket from 'ws'; export class WebSocketTransport implements ConnectionTransport { diff --git a/src/firefox/events.ts b/src/firefox/events.ts index d39116fd65..3faa0cbfeb 100644 --- a/src/firefox/events.ts +++ b/src/firefox/events.ts @@ -16,26 +16,6 @@ */ export const Events = { - Page: { - Close: 'close', - Console: 'console', - Dialog: 'dialog', - 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', - }, - Browser: { Disconnected: 'disconnected' }, diff --git a/src/injected/injected.ts b/src/injected/injected.ts index b3d6434de6..c5d94362af 100644 --- a/src/injected/injected.ts +++ b/src/injected/injected.ts @@ -19,7 +19,7 @@ class Injected { querySelector(selector: string, root: Node): Element | undefined { const parsed = this._parseSelector(selector); - if (!root['querySelector']) + if (!(root as any)['querySelector']) throw new Error('Node is not queryable.'); let element = root as SelectorRoot; for (const { engine, selector } of parsed) { @@ -33,7 +33,7 @@ class Injected { querySelectorAll(selector: string, root: Node): Element[] { const parsed = this._parseSelector(selector); - if (!root['querySelectorAll']) + if (!(root as any)['querySelectorAll']) throw new Error('Node is not queryable.'); let set = new Set([ root as SelectorRoot ]); for (const { engine, selector } of parsed) { @@ -105,7 +105,7 @@ class Injected { if (success) return Promise.resolve(success); - let fulfill; + let fulfill: (result?: any) => void; const result = new Promise(x => fulfill = x); const observer = new MutationObserver(mutations => { if (timedOut) { @@ -131,7 +131,7 @@ class Injected { if (timeout) setTimeout(() => timedOut = true, timeout); - let fulfill; + let fulfill: (result?: any) => void; const result = new Promise(x => fulfill = x); onRaf(); return result; @@ -154,7 +154,7 @@ class Injected { if (timeout) setTimeout(() => timedOut = true, timeout); - let fulfill; + let fulfill: (result?: any) => void; const result = new Promise(x => fulfill = x); onTimeout(); return result; diff --git a/src/injected/tsconfig.json b/src/injected/tsconfig.json new file mode 100644 index 0000000000..beccd788d8 --- /dev/null +++ b/src/injected/tsconfig.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "noImplicitAny": true + }, + "extends": "../../tsconfig.json" +} diff --git a/src/types.ts b/src/types.ts index dbdf2f7341..14774a345e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -62,4 +62,11 @@ export type Viewport = { isMobile?: boolean; isLandscape?: boolean; hasTouch?: boolean; +}; + +export interface ConnectionTransport { + send(s: string): void; + close(): void; + onmessage?: (message: string) => void, + onclose?: () => void, } diff --git a/src/webkit/Connection.ts b/src/webkit/Connection.ts index 45ddac9fa0..7727c253a3 100644 --- a/src/webkit/Connection.ts +++ b/src/webkit/Connection.ts @@ -18,7 +18,7 @@ import {assert} from '../helper'; import * as debug from 'debug'; import {EventEmitter} from 'events'; -import { ConnectionTransport } from '../ConnectionTransport'; +import { ConnectionTransport } from '../types'; import { Protocol } from './protocol'; const debugProtocol = debug('playwright:protocol'); diff --git a/src/webkit/FrameManager.ts b/src/webkit/FrameManager.ts index b1765ea678..df5c277454 100644 --- a/src/webkit/FrameManager.ts +++ b/src/webkit/FrameManager.ts @@ -23,8 +23,7 @@ import * as js from '../javascript'; import * as dom from '../dom'; import * as network from '../network'; import { TargetSession, TargetSessionEvents } from './Connection'; -import { Events } from './events'; -import { Events as CommonEvents } from '../events'; +import { Events } from '../events'; import { ExecutionContextDelegate, EVALUATION_SCRIPT_URL } from './ExecutionContext'; import { NetworkManager, NetworkManagerEvents } from './NetworkManager'; import { Page, PageDelegate } from '../page'; @@ -78,10 +77,10 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, this._contextIdToContext = new Map(); this._isolatedWorlds = new Set(); this._page = new Page(this, browserContext); - this._networkManager.on(NetworkManagerEvents.Request, event => this._page.emit(CommonEvents.Page.Request, event)); - this._networkManager.on(NetworkManagerEvents.Response, event => this._page.emit(CommonEvents.Page.Response, event)); - this._networkManager.on(NetworkManagerEvents.RequestFailed, event => this._page.emit(CommonEvents.Page.RequestFailed, event)); - this._networkManager.on(NetworkManagerEvents.RequestFinished, event => this._page.emit(CommonEvents.Page.RequestFinished, event)); + this._networkManager.on(NetworkManagerEvents.Request, event => this._page.emit(Events.Page.Request, event)); + this._networkManager.on(NetworkManagerEvents.Response, event => this._page.emit(Events.Page.Response, event)); + this._networkManager.on(NetworkManagerEvents.RequestFailed, event => this._page.emit(Events.Page.RequestFailed, event)); + this._networkManager.on(NetworkManagerEvents.RequestFinished, event => this._page.emit(Events.Page.RequestFinished, event)); } async initialize(session: TargetSession) { @@ -128,9 +127,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, helper.addEventListener(this._session, 'Page.loadEventFired', event => this._onLifecycleEvent(event.frameId, 'load')), helper.addEventListener(this._session, 'Page.domContentEventFired', event => this._onLifecycleEvent(event.frameId, 'domcontentloaded')), helper.addEventListener(this._session, 'Runtime.executionContextCreated', event => this._onExecutionContextCreated(event.context)), - helper.addEventListener(this._session, 'Page.loadEventFired', event => this._page.emit(Events.Page.Load)), helper.addEventListener(this._session, 'Console.messageAdded', event => this._onConsoleMessage(event)), - helper.addEventListener(this._session, 'Page.domContentEventFired', event => this._page.emit(Events.Page.DOMContentLoaded)), helper.addEventListener(this._session, 'Dialog.javascriptDialogOpening', event => this._onDialog(event)), helper.addEventListener(this._session, 'Page.fileChooserOpened', event => this._onFileChooserOpened(event)) ]; @@ -158,9 +155,9 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, frame._firedLifecycleEvents.add('load'); this.emit(FrameManagerEvents.LifecycleEvent, frame); if (frame === this.mainFrame() && !hasDOMContentLoaded) - this._page.emit(CommonEvents.Page.DOMContentLoaded); + this._page.emit(Events.Page.DOMContentLoaded); if (frame === this.mainFrame() && !hasLoad) - this._page.emit(CommonEvents.Page.Load); + this._page.emit(Events.Page.Load); } _onLifecycleEvent(frameId: string, event: frames.LifecycleEvent) { @@ -171,9 +168,9 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, this.emit(FrameManagerEvents.LifecycleEvent, frame); if (frame === this.mainFrame()) { if (event === 'load') - this._page.emit(CommonEvents.Page.Load); + this._page.emit(Events.Page.Load); if (event === 'domcontentloaded') - this._page.emit(CommonEvents.Page.DOMContentLoaded); + this._page.emit(Events.Page.DOMContentLoaded); } } @@ -221,7 +218,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, frame[frameDataSymbol] = data; this._frames.set(frameId, frame); this.emit(FrameManagerEvents.FrameAttached, frame); - this._page.emit(CommonEvents.Page.FrameAttached, frame); + this._page.emit(Events.Page.FrameAttached, frame); return frame; } @@ -273,7 +270,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, } this.emit(FrameManagerEvents.FrameNavigated, frame); - this._page.emit(CommonEvents.Page.FrameNavigated, frame); + this._page.emit(Events.Page.FrameNavigated, frame); } _onFrameNavigatedWithinDocument(frameId: string, url: string) { @@ -283,7 +280,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, frame._navigated(url, frame.name()); this.emit(FrameManagerEvents.FrameNavigatedWithinDocument, frame); this.emit(FrameManagerEvents.FrameNavigated, frame); - this._page.emit(CommonEvents.Page.FrameNavigated, frame); + this._page.emit(Events.Page.FrameNavigated, frame); } _onFrameDetached(frameId: string) { @@ -324,7 +321,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, frame._detach(); this._frames.delete(this._frameData(frame).id); this.emit(FrameManagerEvents.FrameDetached, frame); - this._page.emit(CommonEvents.Page.FrameDetached, frame); + this._page.emit(Events.Page.FrameDetached, frame); } async navigateFrame(frame: frames.Frame, url: string, options: frames.GotoOptions = {}): Promise { @@ -589,7 +586,7 @@ class NextNavigationWatchdog { _registerDisconnectedListener() { if (this._disconnectedListener) - helper.removeEventListeners([this._disconnectedListener]); + helper.removeEventListeners([this._disconnectedListener]); const session = this._frameManager._session; this._disconnectedListener = helper.addEventListener(this._frameManager._session, TargetSessionEvents.Disconnected, () => { // Session may change on swap out, check that it's current. diff --git a/src/webkit/Multimap.ts b/src/webkit/Multimap.ts deleted file mode 100644 index 835a28e592..0000000000 --- a/src/webkit/Multimap.ts +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Copyright 2017 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 class Multimap { - private _map: Map>; - - constructor() { - this._map = new Map(); - } - - set(key: T, value: V) { - let set = this._map.get(key); - if (!set) { - set = new Set(); - this._map.set(key, set); - } - set.add(value); - } - - get(key: T): Set { - let result = this._map.get(key); - if (!result) - result = new Set(); - return result; - } - - has(key: T): boolean { - return this._map.has(key); - } - - hasValue(key: T, value: V): boolean { - const set = this._map.get(key); - if (!set) - return false; - return set.has(value); - } - - get size(): number { - return this._map.size; - } - - delete(key: T, value: V): boolean { - const values = this.get(key); - const result = values.delete(value); - if (!values.size) - this._map.delete(key); - return result; - } - - deleteAll(key: T) { - this._map.delete(key); - } - - firstValue(key: T): V { - const set = this._map.get(key); - if (!set) - return null; - return set.values().next().value; - } - - firstKey(): T { - return this._map.keys().next().value; - } - - valuesArray(): V[] { - const result = []; - for (const key of this._map.keys()) - result.push(...Array.from(this._map.get(key).values())); - return result; - } - - keysArray(): T[] { - return Array.from(this._map.keys()); - } - - clear() { - this._map.clear(); - } -} diff --git a/src/webkit/PipeTransport.ts b/src/webkit/PipeTransport.ts index 665d135c97..74db2f8188 100644 --- a/src/webkit/PipeTransport.ts +++ b/src/webkit/PipeTransport.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { ConnectionTransport } from '../ConnectionTransport'; +import { ConnectionTransport } from '../types'; import { debugError, helper, RegisteredListener } from '../helper'; export class PipeTransport implements ConnectionTransport { diff --git a/src/webkit/events.ts b/src/webkit/events.ts index 325077002a..3faa0cbfeb 100644 --- a/src/webkit/events.ts +++ b/src/webkit/events.ts @@ -16,22 +16,6 @@ */ export const Events = { - Page: { - Close: 'close', - Console: 'console', - Dialog: 'dialog', - FileChooser: 'filechooser', - DOMContentLoaded: 'domcontentloaded', - Request: 'request', - Response: 'response', - RequestFailed: 'requestfailed', - RequestFinished: 'requestfinished', - FrameAttached: 'frameattached', - FrameDetached: 'framedetached', - FrameNavigated: 'framenavigated', - Load: 'load', - }, - Browser: { Disconnected: 'disconnected' }, diff --git a/utils/doclint/check_public_api/JSBuilder.js b/utils/doclint/check_public_api/JSBuilder.js index 0d66ce60c3..84e418db8a 100644 --- a/utils/doclint/check_public_api/JSBuilder.js +++ b/utils/doclint/check_public_api/JSBuilder.js @@ -117,7 +117,7 @@ function checkSources(sources) { function serializeSymbol(symbol, circular = []) { const type = checker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration); const name = symbol.getName(); - if (symbol.valueDeclaration.dotDotDotToken) { + if (symbol.valueDeclaration && symbol.valueDeclaration.dotDotDotToken) { const innerType = serializeType(type.typeArguments ? type.typeArguments[0] : type, circular); innerType.name = '...' + innerType.name; return Documentation.Member.createProperty('...' + name, innerType); diff --git a/utils/protocol-types-generator/index.js b/utils/protocol-types-generator/index.js index ab506d7b99..f8b47e7202 100644 --- a/utils/protocol-types-generator/index.js +++ b/utils/protocol-types-generator/index.js @@ -113,6 +113,8 @@ function typeOfProperty(property, domain) { return typeOfProperty(property.items, domain) + '[]'; case 'integer': return 'number'; + case 'object': + return '{ [key: string]: string }'; } return property.type; }