From 8a5008e30be78dd24fb2abfdbb286f9bbc6c87c5 Mon Sep 17 00:00:00 2001 From: Jack Franklin Date: Wed, 29 Apr 2020 12:28:16 +0100 Subject: [PATCH] chore: migrate src/FrameManager to TypeScript (#5773) --- src/DOMWorld.ts | 11 +- src/ExecutionContext.ts | 3 +- src/{FrameManager.js => FrameManager.ts} | 426 ++++++----------------- src/JSHandle.ts | 9 +- src/LifecycleWatcher.ts | 15 +- src/NetworkManager.js | 11 +- src/Page.js | 8 +- src/externs.d.ts | 3 - test/utils.js | 2 +- utils/doclint/check_public_api/index.js | 8 + 10 files changed, 149 insertions(+), 347 deletions(-) rename src/{FrameManager.js => FrameManager.ts} (57%) diff --git a/src/DOMWorld.ts b/src/DOMWorld.ts index ef978931ed225..92143506a4bd7 100644 --- a/src/DOMWorld.ts +++ b/src/DOMWorld.ts @@ -22,18 +22,19 @@ import {JSHandle, ElementHandle} from './JSHandle'; import {ExecutionContext} from './ExecutionContext'; import {TimeoutSettings} from './TimeoutSettings'; import {MouseButtonInput} from './Input'; +import {FrameManager, Frame} from './FrameManager'; const readFileAsync = helper.promisify(fs.readFile); -interface WaitForSelectorOptions { +export interface WaitForSelectorOptions { visible?: boolean; hidden?: boolean; timeout?: number; } export class DOMWorld { - _frameManager: Puppeteer.FrameManager; - _frame: Puppeteer.Frame; + _frameManager: FrameManager; + _frame: Frame; _timeoutSettings: TimeoutSettings; _documentPromise?: Promise = null; _contextPromise?: Promise = null; @@ -43,14 +44,14 @@ export class DOMWorld { _detached = false; _waitTasks = new Set(); - constructor(frameManager: Puppeteer.FrameManager, frame: Puppeteer.Frame, timeoutSettings: TimeoutSettings) { + constructor(frameManager: FrameManager, frame: Frame, timeoutSettings: TimeoutSettings) { this._frameManager = frameManager; this._frame = frame; this._timeoutSettings = timeoutSettings; this._setContext(null); } - frame(): Puppeteer.Frame { + frame(): Frame { return this._frame; } diff --git a/src/ExecutionContext.ts b/src/ExecutionContext.ts index 75a6e19e07c07..8201cb2d7ae08 100644 --- a/src/ExecutionContext.ts +++ b/src/ExecutionContext.ts @@ -18,6 +18,7 @@ import {helper, assert} from './helper'; import {createJSHandle, JSHandle, ElementHandle} from './JSHandle'; import {CDPSession} from './Connection'; import {DOMWorld} from './DOMWorld'; +import {Frame} from './FrameManager'; export const EVALUATION_SCRIPT_URL = '__puppeteer_evaluation_script__'; const SOURCE_URL_REGEX = /^[\040\t]*\/\/[@#] sourceURL=\s*(\S*?)\s*$/m; @@ -33,7 +34,7 @@ export class ExecutionContext { this._contextId = contextPayload.id; } - frame(): Puppeteer.Frame | null { + frame(): Frame | null { return this._world ? this._world.frame() : null; } diff --git a/src/FrameManager.js b/src/FrameManager.ts similarity index 57% rename from src/FrameManager.js rename to src/FrameManager.ts index 35ebbda0dead0..5a09bb60e36bb 100644 --- a/src/FrameManager.js +++ b/src/FrameManager.ts @@ -14,45 +14,36 @@ * limitations under the License. */ -const EventEmitter = require('events'); -const {helper, assert, debugError} = require('./helper'); -const {Events} = require('./Events'); -const {ExecutionContext, EVALUATION_SCRIPT_URL} = require('./ExecutionContext'); -const {LifecycleWatcher} = require('./LifecycleWatcher'); -const {DOMWorld} = require('./DOMWorld'); -const {NetworkManager} = require('./NetworkManager'); -// Used as a TypeDef -// eslint-disable-next-line no-unused-vars -const {TimeoutSettings} = require('./TimeoutSettings'); -// Used as a TypeDef -// eslint-disable-next-line no-unused-vars -const {CDPSession} = require('./Connection'); -// Used as a TypeDef -// eslint-disable-next-line no-unused-vars -const {JSHandle, ElementHandle} = require('./JSHandle'); +import * as EventEmitter from 'events'; +import {helper, assert, debugError} from './helper'; +import {Events} from './Events'; +import {ExecutionContext, EVALUATION_SCRIPT_URL} from './ExecutionContext'; +import {LifecycleWatcher, PuppeteerLifeCycleEvent} from './LifecycleWatcher'; +import {DOMWorld, WaitForSelectorOptions} from './DOMWorld'; +import {NetworkManager} from './NetworkManager'; +import {TimeoutSettings} from './TimeoutSettings'; +import {CDPSession} from './Connection'; +import {JSHandle, ElementHandle} from './JSHandle'; +import {MouseButtonInput} from './Input'; const UTILITY_WORLD_NAME = '__puppeteer_utility_world__'; -class FrameManager extends EventEmitter { - /** - * @param {!CDPSession} client - * @param {!Puppeteer.Page} page - * @param {boolean} ignoreHTTPSErrors - * @param {!TimeoutSettings} timeoutSettings - */ - constructor(client, page, ignoreHTTPSErrors, timeoutSettings) { +export class FrameManager extends EventEmitter { + _client: CDPSession; + _page: Puppeteer.Page; + _networkManager: Puppeteer.NetworkManager; + _timeoutSettings: TimeoutSettings; + _frames = new Map(); + _contextIdToContext = new Map(); + _isolatedWorlds = new Set(); + _mainFrame: Frame; + + constructor(client: CDPSession, page: Puppeteer.Page, ignoreHTTPSErrors: boolean, timeoutSettings: TimeoutSettings) { super(); this._client = client; this._page = page; this._networkManager = new NetworkManager(client, ignoreHTTPSErrors, this); this._timeoutSettings = timeoutSettings; - /** @type {!Map} */ - this._frames = new Map(); - /** @type {!Map} */ - this._contextIdToContext = new Map(); - /** @type {!Set} */ - this._isolatedWorlds = new Set(); - this._client.on('Page.frameAttached', event => this._onFrameAttached(event.frameId, event.parentFrameId)); this._client.on('Page.frameNavigated', event => this._onFrameNavigated(event.frame)); this._client.on('Page.navigatedWithinDocument', event => this._onFrameNavigatedWithinDocument(event.frameId, event.url)); @@ -60,17 +51,17 @@ class FrameManager extends EventEmitter { this._client.on('Page.frameStoppedLoading', event => this._onFrameStoppedLoading(event.frameId)); this._client.on('Runtime.executionContextCreated', event => this._onExecutionContextCreated(event.context)); this._client.on('Runtime.executionContextDestroyed', event => this._onExecutionContextDestroyed(event.executionContextId)); - this._client.on('Runtime.executionContextsCleared', event => this._onExecutionContextsCleared()); + this._client.on('Runtime.executionContextsCleared', () => this._onExecutionContextsCleared()); this._client.on('Page.lifecycleEvent', event => this._onLifecycleEvent(event)); } - async initialize() { - const result = await Promise.all([ + async initialize(): Promise { + const result = await Promise.all([ this._client.send('Page.enable'), this._client.send('Page.getFrameTree'), ]); - const {frameTree} = /** @type Protocol.Page.getFrameTreeReturnValue*/ (result[1]); + const {frameTree} = result[1]; this._handleFrameTree(frameTree); await Promise.all([ this._client.send('Page.setLifecycleEventsEnabled', {enabled: true}), @@ -79,20 +70,11 @@ class FrameManager extends EventEmitter { ]); } - /** - * @return {!NetworkManager} - */ - networkManager() { + networkManager(): Puppeteer.NetworkManager { return this._networkManager; } - /** - * @param {!Puppeteer.Frame} frame - * @param {string} url - * @param {!{referer?: string, timeout?: number, waitUntil?: !Puppeteer.PuppeteerLifeCycleEvent|!Array}=} options - * @return {!Promise} - */ - async navigateFrame(frame, url, options = {}) { + async navigateFrame(frame: Frame, url: string, options: {referer?: string; timeout?: number; waitUntil?: PuppeteerLifeCycleEvent|PuppeteerLifeCycleEvent[]} = {}): Promise { assertNoLegacyNavigationOptions(options); const { referer = this._networkManager.extraHTTPHeaders()['referer'], @@ -117,14 +99,7 @@ class FrameManager extends EventEmitter { throw error; return watcher.navigationResponse(); - /** - * @param {!CDPSession} client - * @param {string} url - * @param {string} referrer - * @param {string} frameId - * @return {!Promise} - */ - async function navigate(client, url, referrer, frameId) { + async function navigate(client: CDPSession, url: string, referrer: string, frameId: string): Promise { try { const response = await client.send('Page.navigate', {url, referrer, frameId}); ensureNewDocumentNavigation = !!response.loaderId; @@ -135,12 +110,7 @@ class FrameManager extends EventEmitter { } } - /** - * @param {!Puppeteer.Frame} frame - * @param {!{timeout?: number, waitUntil?: !Puppeteer.PuppeteerLifeCycleEvent|!Array}=} options - * @return {!Promise} - */ - async waitForFrameNavigation(frame, options = {}) { + async waitForFrameNavigation(frame: Frame, options: {timeout?: number; waitUntil?: PuppeteerLifeCycleEvent|PuppeteerLifeCycleEvent[]} = {}): Promise { assertNoLegacyNavigationOptions(options); const { waitUntil = ['load'], @@ -158,10 +128,7 @@ class FrameManager extends EventEmitter { return watcher.navigationResponse(); } - /** - * @param {!Protocol.Page.lifecycleEventPayload} event - */ - _onLifecycleEvent(event) { + _onLifecycleEvent(event: Protocol.Page.lifecycleEventPayload): void { const frame = this._frames.get(event.frameId); if (!frame) return; @@ -169,10 +136,7 @@ class FrameManager extends EventEmitter { this.emit(Events.FrameManager.LifecycleEvent, frame); } - /** - * @param {string} frameId - */ - _onFrameStoppedLoading(frameId) { + _onFrameStoppedLoading(frameId: string): void { const frame = this._frames.get(frameId); if (!frame) return; @@ -180,10 +144,7 @@ class FrameManager extends EventEmitter { this.emit(Events.FrameManager.LifecycleEvent, frame); } - /** - * @param {!Protocol.Page.FrameTree} frameTree - */ - _handleFrameTree(frameTree) { + _handleFrameTree(frameTree: Protocol.Page.FrameTree): void { if (frameTree.frame.parentId) this._onFrameAttached(frameTree.frame.id, frameTree.frame.parentId); this._onFrameNavigated(frameTree.frame); @@ -194,40 +155,23 @@ class FrameManager extends EventEmitter { this._handleFrameTree(child); } - /** - * @return {!Puppeteer.Page} - */ - page() { + page(): Puppeteer.Page { return this._page; } - /** - * @return {!Frame} - */ - mainFrame() { + mainFrame(): Frame { return this._mainFrame; } - /** - * @return {!Array} - */ - frames() { + frames(): Frame[] { return Array.from(this._frames.values()); } - /** - * @param {!string} frameId - * @return {?Frame} - */ - frame(frameId) { + frame(frameId: string): Frame | null { return this._frames.get(frameId) || null; } - /** - * @param {string} frameId - * @param {?string} parentFrameId - */ - _onFrameAttached(frameId, parentFrameId) { + _onFrameAttached(frameId: string, parentFrameId?: string): void { if (this._frames.has(frameId)) return; assert(parentFrameId); @@ -237,10 +181,7 @@ class FrameManager extends EventEmitter { this.emit(Events.FrameManager.FrameAttached, frame); } - /** - * @param {!Protocol.Page.Frame} framePayload - */ - _onFrameNavigated(framePayload) { + _onFrameNavigated(framePayload: Protocol.Page.Frame): void { const isMainFrame = !framePayload.parentId; let frame = isMainFrame ? this._mainFrame : this._frames.get(framePayload.id); assert(isMainFrame || frame, 'We either navigate top level or have old version of the navigated frame'); @@ -271,10 +212,7 @@ class FrameManager extends EventEmitter { this.emit(Events.FrameManager.FrameNavigated, frame); } - /** - * @param {string} name - */ - async _ensureIsolatedWorld(name) { + async _ensureIsolatedWorld(name: string): Promise { if (this._isolatedWorlds.has(name)) return; this._isolatedWorlds.add(name); @@ -289,11 +227,7 @@ class FrameManager extends EventEmitter { }).catch(debugError))); // frames might be removed before we send this } - /** - * @param {string} frameId - * @param {string} url - */ - _onFrameNavigatedWithinDocument(frameId, url) { + _onFrameNavigatedWithinDocument(frameId: string, url: string): void { const frame = this._frames.get(frameId); if (!frame) return; @@ -302,17 +236,15 @@ class FrameManager extends EventEmitter { this.emit(Events.FrameManager.FrameNavigated, frame); } - /** - * @param {string} frameId - */ - _onFrameDetached(frameId) { + _onFrameDetached(frameId: string): void { const frame = this._frames.get(frameId); if (frame) this._removeFramesRecursively(frame); } - _onExecutionContextCreated(contextPayload) { - const frameId = contextPayload.auxData ? contextPayload.auxData.frameId : null; + _onExecutionContextCreated(contextPayload: Protocol.Runtime.ExecutionContextDescription): void { + const auxData = contextPayload.auxData as { frameId?: string}; + const frameId = auxData ? auxData.frameId : null; const frame = this._frames.get(frameId) || null; let world = null; if (frame) { @@ -327,7 +259,6 @@ class FrameManager extends EventEmitter { } if (contextPayload.auxData && contextPayload.auxData['type'] === 'isolated') this._isolatedWorlds.add(contextPayload.name); - /** @type {!ExecutionContext} */ const context = new ExecutionContext(this._client, contextPayload, world); if (world) world._setContext(context); @@ -337,7 +268,7 @@ class FrameManager extends EventEmitter { /** * @param {number} executionContextId */ - _onExecutionContextDestroyed(executionContextId) { + _onExecutionContextDestroyed(executionContextId: number): void { const context = this._contextIdToContext.get(executionContextId); if (!context) return; @@ -346,7 +277,7 @@ class FrameManager extends EventEmitter { context._world._setContext(null); } - _onExecutionContextsCleared() { + _onExecutionContextsCleared(): void { for (const context of this._contextIdToContext.values()) { if (context._world) context._world._setContext(null); @@ -354,20 +285,13 @@ class FrameManager extends EventEmitter { this._contextIdToContext.clear(); } - /** - * @param {number} contextId - * @return {!ExecutionContext} - */ - executionContextById(contextId) { + executionContextById(contextId: number): ExecutionContext { const context = this._contextIdToContext.get(contextId); assert(context, 'INTERNAL ERROR: missing context with id = ' + contextId); return context; } - /** - * @param {!Frame} frame - */ - _removeFramesRecursively(frame) { + _removeFramesRecursively(frame: Frame): void { for (const child of frame.childFrames()) this._removeFramesRecursively(child); frame._detach(); @@ -376,17 +300,23 @@ class FrameManager extends EventEmitter { } } -/** - * @unrestricted - */ -class Frame { - /** - * @param {!FrameManager} frameManager - * @param {!CDPSession} client - * @param {?Frame} parentFrame - * @param {string} frameId - */ - constructor(frameManager, client, parentFrame, frameId) { +export class Frame { + _frameManager: FrameManager; + _client: CDPSession; + _parentFrame?: Frame; + _id: string; + + _url = ''; + _detached = false; + _loaderId = ''; + _name?: string; + + _lifecycleEvents = new Set(); + _mainWorld: DOMWorld; + _secondaryWorld: DOMWorld; + _childFrames: Set; + + constructor(frameManager: FrameManager, client: CDPSession, parentFrame: Frame | null, frameId: string) { this._frameManager = frameManager; this._client = client; this._parentFrame = parentFrame; @@ -395,246 +325,131 @@ class Frame { this._detached = false; this._loaderId = ''; - /** @type {!Set} */ - this._lifecycleEvents = new Set(); - /** @type {!DOMWorld} */ this._mainWorld = new DOMWorld(frameManager, this, frameManager._timeoutSettings); - /** @type {!DOMWorld} */ this._secondaryWorld = new DOMWorld(frameManager, this, frameManager._timeoutSettings); - /** @type {!Set} */ this._childFrames = new Set(); if (this._parentFrame) this._parentFrame._childFrames.add(this); } - /** - * @param {string} url - * @param {!{referer?: string, timeout?: number, waitUntil?: !Puppeteer.PuppeteerLifeCycleEvent|!Array}=} options - * @return {!Promise} - */ - async goto(url, options) { + async goto(url: string, options: {referer?: string; timeout?: number; waitUntil?: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[]}): Promise { return await this._frameManager.navigateFrame(this, url, options); } - /** - * @param {!{timeout?: number, waitUntil?: !Puppeteer.PuppeteerLifeCycleEvent|!Array}=} options - * @return {!Promise} - */ - async waitForNavigation(options) { + async waitForNavigation(options: {timeout?: number; waitUntil?: PuppeteerLifeCycleEvent|PuppeteerLifeCycleEvent[]}): Promise { return await this._frameManager.waitForFrameNavigation(this, options); } - /** - * @return {!Promise} - */ - executionContext() { + executionContext(): Promise { return this._mainWorld.executionContext(); } - /** - * @param {Function|string} pageFunction - * @param {!Array<*>} args - * @return {!Promise} - */ - async evaluateHandle(pageFunction, ...args) { + async evaluateHandle(pageFunction: Function|string, ...args: unknown[]): Promise { return this._mainWorld.evaluateHandle(pageFunction, ...args); } - /** - * @param {Function|string} pageFunction - * @param {!Array<*>} args - * @return {!Promise<*>} - */ - async evaluate(pageFunction, ...args) { - return this._mainWorld.evaluate(pageFunction, ...args); + async evaluate(pageFunction: Function|string, ...args: unknown[]): Promise { + return this._mainWorld.evaluate(pageFunction, ...args); } - /** - * @param {string} selector - * @return {!Promise} - */ - async $(selector) { + async $(selector: string): Promise { return this._mainWorld.$(selector); } - /** - * @param {string} expression - * @return {!Promise>} - */ - async $x(expression) { + async $x(expression: string): Promise { return this._mainWorld.$x(expression); } - /** - * @param {string} selector - * @param {Function|string} pageFunction - * @param {!Array<*>} args - * @return {!Promise<(!Object|undefined)>} - */ - async $eval(selector, pageFunction, ...args) { - return this._mainWorld.$eval(selector, pageFunction, ...args); + async $eval(selector: string, pageFunction: Function | string, ...args: unknown[]): Promise { + return this._mainWorld.$eval(selector, pageFunction, ...args); } - /** - * @param {string} selector - * @param {Function|string} pageFunction - * @param {!Array<*>} args - * @return {!Promise<(!Object|undefined)>} - */ - async $$eval(selector, pageFunction, ...args) { - return this._mainWorld.$$eval(selector, pageFunction, ...args); + async $$eval(selector: string, pageFunction: Function | string, ...args: unknown[]): Promise { + return this._mainWorld.$$eval(selector, pageFunction, ...args); } - /** - * @param {string} selector - * @return {!Promise>} - */ - async $$(selector) { + async $$(selector: string): Promise { return this._mainWorld.$$(selector); } - /** - * @return {!Promise} - */ - async content() { + async content(): Promise { return this._secondaryWorld.content(); } - /** - * @param {string} html - * @param {!{timeout?: number, waitUntil?: !Puppeteer.PuppeteerLifeCycleEvent|!Array}=} options - */ - async setContent(html, options = {}) { + async setContent(html: string, options: {timeout?: number; waitUntil?: PuppeteerLifeCycleEvent|PuppeteerLifeCycleEvent[]} = {}): Promise { return this._secondaryWorld.setContent(html, options); } - /** - * @return {string} - */ - name() { + name(): string { return this._name || ''; } - /** - * @return {string} - */ - url() { + url(): string { return this._url; } - /** - * @return {?Frame} - */ - parentFrame() { + parentFrame(): Frame | null { return this._parentFrame; } - /** - * @return {!Array.} - */ - childFrames() { + childFrames(): Frame[] { return Array.from(this._childFrames); } - /** - * @return {boolean} - */ - isDetached() { + isDetached(): boolean { return this._detached; } - /** - * @param {!{url?: string, path?: string, content?: string, type?: string}} options - * @return {!Promise} - */ - async addScriptTag(options) { + async addScriptTag(options: {url?: string; path?: string; content?: string; type?: string}): Promise { return this._mainWorld.addScriptTag(options); } - /** - * @param {!{url?: string, path?: string, content?: string}} options - * @return {!Promise} - */ - async addStyleTag(options) { + async addStyleTag(options: {url?: string; path?: string; content?: string}): Promise { return this._mainWorld.addStyleTag(options); } - /** - * @param {string} selector - * @param {!{delay?: number, button?: "left"|"right"|"middle", clickCount?: number}=} options - */ - async click(selector, options) { + async click(selector: string, options: {delay?: number; button?: MouseButtonInput; clickCount?: number}): Promise { return this._secondaryWorld.click(selector, options); } - /** - * @param {string} selector - */ - async focus(selector) { + async focus(selector: string): Promise { return this._secondaryWorld.focus(selector); } - /** - * @param {string} selector - */ - async hover(selector) { + async hover(selector: string): Promise { return this._secondaryWorld.hover(selector); } - /** - * @param {string} selector - * @param {!Array} values - * @return {!Promise>} - */ - select(selector, ...values){ + select(selector: string, ...values: string[]): Promise { return this._secondaryWorld.select(selector, ...values); } - /** - * @param {string} selector - */ - async tap(selector) { + async tap(selector: string): Promise { return this._secondaryWorld.tap(selector); } - /** - * @param {string} selector - * @param {string} text - * @param {{delay: (number|undefined)}=} options - */ - async type(selector, text, options) { + async type(selector: string, text: string, options?: {delay: number}): Promise { return this._mainWorld.type(selector, text, options); } - /** - * @param {(string|number|Function)} selectorOrFunctionOrTimeout - * @param {!Object=} options - * @param {!Array<*>} args - * @return {!Promise} - */ - waitFor(selectorOrFunctionOrTimeout, options = {}, ...args) { + waitFor(selectorOrFunctionOrTimeout: string|number|Function, options: {} = {}, ...args: unknown[]): Promise { const xPathPattern = '//'; if (helper.isString(selectorOrFunctionOrTimeout)) { - const string = /** @type {string} */ (selectorOrFunctionOrTimeout); + const string = selectorOrFunctionOrTimeout; if (string.startsWith(xPathPattern)) return this.waitForXPath(string, options); return this.waitForSelector(string, options); } if (helper.isNumber(selectorOrFunctionOrTimeout)) - return new Promise(fulfill => setTimeout(fulfill, /** @type {number} */ (selectorOrFunctionOrTimeout))); + return new Promise(fulfill => setTimeout(fulfill, selectorOrFunctionOrTimeout)); if (typeof selectorOrFunctionOrTimeout === 'function') return this.waitForFunction(selectorOrFunctionOrTimeout, options, ...args); return Promise.reject(new Error('Unsupported target type: ' + (typeof selectorOrFunctionOrTimeout))); } - /** - * @param {string} selector - * @param {!{visible?: boolean, hidden?: boolean, timeout?: number}=} options - * @return {!Promise} - */ - async waitForSelector(selector, options) { + async waitForSelector(selector: string, options: WaitForSelectorOptions): Promise { const handle = await this._secondaryWorld.waitForSelector(selector, options); if (!handle) return null; @@ -644,12 +459,7 @@ class Frame { return result; } - /** - * @param {string} xpath - * @param {!{visible?: boolean, hidden?: boolean, timeout?: number}=} options - * @return {!Promise} - */ - async waitForXPath(xpath, options) { + async waitForXPath(xpath: string, options: WaitForSelectorOptions): Promise { const handle = await this._secondaryWorld.waitForXPath(xpath, options); if (!handle) return null; @@ -659,44 +469,24 @@ class Frame { return result; } - /** - * @param {Function|string} pageFunction - * @param {!{polling?: string|number, timeout?: number}=} options - * @return {!Promise} - */ - waitForFunction(pageFunction, options = {}, ...args) { + waitForFunction(pageFunction: Function|string, options: {polling?: string|number; timeout?: number} = {}, ...args: unknown[]): Promise { return this._mainWorld.waitForFunction(pageFunction, options, ...args); } - /** - * @return {!Promise} - */ - async title() { + async title(): Promise { return this._secondaryWorld.title(); } - /** - * @param {!Protocol.Page.Frame} framePayload - */ - _navigated(framePayload) { + _navigated(framePayload: Protocol.Page.Frame): void { this._name = framePayload.name; - // TODO(lushnikov): remove this once requestInterception has loaderId exposed. - this._navigationURL = framePayload.url; this._url = framePayload.url; } - /** - * @param {string} url - */ - _navigatedWithinDocument(url) { + _navigatedWithinDocument(url: string): void { this._url = url; } - /** - * @param {string} loaderId - * @param {string} name - */ - _onLifecycleEvent(loaderId, name) { + _onLifecycleEvent(loaderId: string, name: string): void { if (name === 'init') { this._loaderId = loaderId; this._lifecycleEvents.clear(); @@ -704,12 +494,12 @@ class Frame { this._lifecycleEvents.add(name); } - _onLoadingStopped() { + _onLoadingStopped(): void { this._lifecycleEvents.add('DOMContentLoaded'); this._lifecycleEvents.add('load'); } - _detach() { + _detach(): void { this._detached = true; this._mainWorld._detach(); this._secondaryWorld._detach(); @@ -719,10 +509,8 @@ class Frame { } } -function assertNoLegacyNavigationOptions(options) { +function assertNoLegacyNavigationOptions(options: {[optionName: string]: unknown}): void { 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'); } - -module.exports = {FrameManager, Frame}; diff --git a/src/JSHandle.ts b/src/JSHandle.ts index f433eb46e5999..c78eec3bd14e8 100644 --- a/src/JSHandle.ts +++ b/src/JSHandle.ts @@ -18,6 +18,7 @@ import {helper, assert, debugError} from './helper'; import {ExecutionContext} from './ExecutionContext'; import {CDPSession} from './Connection'; import {KeyInput} from './USKeyboardLayout'; +import {FrameManager, Frame} from './FrameManager'; interface BoxModel { content: Array<{x: number; y: number}>; @@ -123,15 +124,15 @@ export class JSHandle { export class ElementHandle extends JSHandle { _page: Puppeteer.Page; - _frameManager: Puppeteer.FrameManager; + _frameManager: FrameManager; /** * @param {!ExecutionContext} context * @param {!CDPSession} client * @param {!Protocol.Runtime.RemoteObject} remoteObject * @param {!Puppeteer.Page} page - * @param {!Puppeteer.FrameManager} frameManager + * @param {!FrameManager} frameManager */ - constructor(context: ExecutionContext, client: CDPSession, remoteObject: Protocol.Runtime.RemoteObject, page: Puppeteer.Page, frameManager: Puppeteer.FrameManager) { + constructor(context: ExecutionContext, client: CDPSession, remoteObject: Protocol.Runtime.RemoteObject, page: Puppeteer.Page, frameManager: FrameManager) { super(context, client, remoteObject); this._client = client; this._remoteObject = remoteObject; @@ -143,7 +144,7 @@ export class ElementHandle extends JSHandle { return this; } - async contentFrame(): Promise { + async contentFrame(): Promise { const nodeInfo = await this._client.send('DOM.describeNode', { objectId: this._remoteObject.objectId }); diff --git a/src/LifecycleWatcher.ts b/src/LifecycleWatcher.ts index 13b59af869b52..9a7f071b0a7ac 100644 --- a/src/LifecycleWatcher.ts +++ b/src/LifecycleWatcher.ts @@ -17,6 +17,7 @@ import {helper, assert, PuppeteerEventListener} from './helper'; import {Events} from './Events'; import {TimeoutError} from './Errors'; +import {FrameManager, Frame} from './FrameManager'; export type PuppeteerLifeCycleEvent = 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2'; type ProtocolLifeCycleEvent = 'load' | 'DOMContentLoaded' | 'networkIdle' | 'networkAlmostIdle'; @@ -31,8 +32,8 @@ const puppeteerToProtocolLifecycle = new Map new TimeoutError(errorMessage)); } - _navigatedWithinDocument(frame: Puppeteer.Frame): void { + _navigatedWithinDocument(frame: Frame): void { if (frame !== this._frame) return; this._hasSameDocumentNavigation = true; @@ -166,11 +167,11 @@ export class LifecycleWatcher { this._newDocumentNavigationCompleteCallback(); /** - * @param {!Puppeteer.Frame} frame + * @param {!Frame} frame * @param {!Array} expectedLifecycle * @return {boolean} */ - function checkLifecycle(frame: Puppeteer.Frame, expectedLifecycle: ProtocolLifeCycleEvent[]): boolean { + function checkLifecycle(frame: Frame, expectedLifecycle: ProtocolLifeCycleEvent[]): boolean { for (const event of expectedLifecycle) { if (!frame._lifecycleEvents.has(event)) return false; diff --git a/src/NetworkManager.js b/src/NetworkManager.js index 2766543a9de55..fc062f8af7f28 100644 --- a/src/NetworkManager.js +++ b/src/NetworkManager.js @@ -19,11 +19,14 @@ const {Events} = require('./Events'); // CDPSession is used only as a typedef // eslint-disable-next-line no-unused-vars const {CDPSession} = require('./Connection'); +// used only as a typedef +// eslint-disable-next-line no-unused-vars +const {Frame, FrameManager} = require('./FrameManager'); class NetworkManager extends EventEmitter { /** * @param {!CDPSession} client - * @param {!Puppeteer.FrameManager} frameManager + * @param {!FrameManager} frameManager */ constructor(client, ignoreHTTPSErrors, frameManager) { super(); @@ -316,7 +319,7 @@ class NetworkManager extends EventEmitter { class Request { /** * @param {!CDPSession} client - * @param {?Puppeteer.Frame} frame + * @param {?Frame} frame * @param {string} interceptionId * @param {boolean} allowInterception * @param {!Protocol.Network.requestWillBeSentPayload} event @@ -388,7 +391,7 @@ class Request { } /** - * @return {?Puppeteer.Frame} + * @return {?Frame} */ frame() { return this._frame; @@ -659,7 +662,7 @@ class Response { } /** - * @return {?Puppeteer.Frame} + * @return {?Frame} */ frame() { return this._request.frame(); diff --git a/src/Page.js b/src/Page.js index 6b91be9e72eb7..02a852f4a2b1d 100644 --- a/src/Page.js +++ b/src/Page.js @@ -23,7 +23,9 @@ const {Events} = require('./Events'); const {Connection, CDPSession} = require('./Connection'); const {Dialog} = require('./Dialog'); const {EmulationManager} = require('./EmulationManager'); -const {FrameManager} = require('./FrameManager'); +// Import used as typedef +// eslint-disable-next-line no-unused-vars +const {Frame, FrameManager} = require('./FrameManager'); const {Keyboard, Mouse, Touchscreen} = require('./Input'); const {Tracing} = require('./Tracing'); const {helper, debugError, assert} = require('./helper'); @@ -236,7 +238,7 @@ class Page extends EventEmitter { } /** - * @return {!Puppeteer.Frame} + * @return {!Frame} */ mainFrame() { return this._frameManager.mainFrame(); @@ -278,7 +280,7 @@ class Page extends EventEmitter { } /** - * @return {!Array} + * @return {!Array} */ frames() { return this._frameManager.frames(); diff --git a/src/externs.d.ts b/src/externs.d.ts index 390967abec9df..bad0876aaab53 100644 --- a/src/externs.d.ts +++ b/src/externs.d.ts @@ -1,13 +1,10 @@ import {Target as RealTarget} from './Target.js'; import {Page as RealPage} from './Page.js'; -import {Frame as RealFrame, FrameManager as RealFrameManager} from './FrameManager.js'; import { NetworkManager as RealNetworkManager, Request as RealRequest, Response as RealResponse } from './NetworkManager.js'; import * as child_process from 'child_process'; declare global { module Puppeteer { export class Target extends RealTarget {} - export class Frame extends RealFrame {} - export class FrameManager extends RealFrameManager {} export class NetworkManager extends RealNetworkManager {} export class Page extends RealPage { } export class Response extends RealResponse { } diff --git a/test/utils.js b/test/utils.js index 1a81455d50710..e678f1231809e 100644 --- a/test/utils.js +++ b/test/utils.js @@ -45,7 +45,7 @@ const utils = module.exports = { * @param {!Page} page * @param {string} frameId * @param {string} url - * @return {!Puppeteer.Frame} + * @return {!Frame} */ attachFrame: async function(page, frameId, url) { const handle = await page.evaluateHandle(attachFrame, frameId, url); diff --git a/utils/doclint/check_public_api/index.js b/utils/doclint/check_public_api/index.js index 958f9a59c88ab..1df371919f4a4 100644 --- a/utils/doclint/check_public_api/index.js +++ b/utils/doclint/check_public_api/index.js @@ -299,6 +299,14 @@ function compareDocumentations(actual, expected) { actualName: 'Object', expectedName: 'TracingOptions' }], + ['Method Frame.waitForSelector() options', { + actualName: 'Object', + expectedName: 'WaitForSelectorOptions' + }], + ['Method Frame.waitForXPath() options', { + actualName: 'Object', + expectedName: 'WaitForSelectorOptions' + }], ['Method Frame.goto() options.waitUntil', { actualName: '"load"|"domcontentloaded"|"networkidle0"|"networkidle2"|Array', expectedName: '"load"|"domcontentloaded"|"networkidle0"|"networkidle2"|Array'