From 8a5008e30be78dd24fb2abfdbb286f9bbc6c87c5 Mon Sep 17 00:00:00 2001
From: Jack Franklin <jacktfranklin@chromium.org>
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 ef978931..92143506 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<ElementHandle> = null;
   _contextPromise?: Promise<ExecutionContext> = null;
@@ -43,14 +44,14 @@ export class DOMWorld {
   _detached = false;
   _waitTasks = new Set<WaitTask>();
 
-  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 75a6e19e..8201cb2d 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 35ebbda0..5a09bb60 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<string, Frame>();
+  _contextIdToContext = new Map<number, ExecutionContext>();
+  _isolatedWorlds = new Set<string>();
+  _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<string, !Frame>} */
-    this._frames = new Map();
-    /** @type {!Map<number, !ExecutionContext>} */
-    this._contextIdToContext = new Map();
-    /** @type {!Set<string>} */
-    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<void> {
+    const result = await Promise.all<Protocol.Page.enableReturnValue, Protocol.Page.getFrameTreeReturnValue>([
       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<!Puppeteer.PuppeteerLifeCycleEvent>}=} options
-   * @return {!Promise<?Puppeteer.Response>}
-   */
-  async navigateFrame(frame, url, options = {}) {
+  async navigateFrame(frame: Frame, url: string, options: {referer?: string; timeout?: number; waitUntil?: PuppeteerLifeCycleEvent|PuppeteerLifeCycleEvent[]} = {}): Promise<Puppeteer.Response | null> {
     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<?Error>}
-     */
-    async function navigate(client, url, referrer, frameId) {
+    async function navigate(client: CDPSession, url: string, referrer: string, frameId: string): Promise<Error | null> {
       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<!Puppeteer.PuppeteerLifeCycleEvent>}=} options
-   * @return {!Promise<?Puppeteer.Response>}
-   */
-  async waitForFrameNavigation(frame, options = {}) {
+  async waitForFrameNavigation(frame: Frame, options: {timeout?: number; waitUntil?: PuppeteerLifeCycleEvent|PuppeteerLifeCycleEvent[]} = {}): Promise<Puppeteer.Response | null> {
     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<!Frame>}
-   */
-  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<void> {
     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<string>();
+  _mainWorld: DOMWorld;
+  _secondaryWorld: DOMWorld;
+  _childFrames: Set<Frame>;
+
+  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<string>} */
-    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<!Frame>} */
     this._childFrames = new Set();
     if (this._parentFrame)
       this._parentFrame._childFrames.add(this);
   }
 
-  /**
-   * @param {string} url
-   * @param {!{referer?: string, timeout?: number, waitUntil?: !Puppeteer.PuppeteerLifeCycleEvent|!Array<!Puppeteer.PuppeteerLifeCycleEvent>}=} options
-   * @return {!Promise<?Puppeteer.Response>}
-   */
-  async goto(url, options) {
+  async goto(url: string, options: {referer?: string; timeout?: number; waitUntil?: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[]}): Promise<Puppeteer.Response | null> {
     return await this._frameManager.navigateFrame(this, url, options);
   }
 
-  /**
-   * @param {!{timeout?: number, waitUntil?: !Puppeteer.PuppeteerLifeCycleEvent|!Array<!Puppeteer.PuppeteerLifeCycleEvent>}=} options
-   * @return {!Promise<?Puppeteer.Response>}
-   */
-  async waitForNavigation(options) {
+  async waitForNavigation(options: {timeout?: number; waitUntil?: PuppeteerLifeCycleEvent|PuppeteerLifeCycleEvent[]}): Promise<Puppeteer.Response | null> {
     return await this._frameManager.waitForFrameNavigation(this, options);
   }
 
-  /**
-   * @return {!Promise<!ExecutionContext>}
-   */
-  executionContext() {
+  executionContext(): Promise<ExecutionContext> {
     return this._mainWorld.executionContext();
   }
 
-  /**
-   * @param {Function|string} pageFunction
-   * @param {!Array<*>} args
-   * @return {!Promise<!JSHandle>}
-   */
-  async evaluateHandle(pageFunction, ...args) {
+  async evaluateHandle(pageFunction: Function|string, ...args: unknown[]): Promise<JSHandle> {
     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<ReturnType extends any>(pageFunction: Function|string, ...args: unknown[]): Promise<ReturnType> {
+    return this._mainWorld.evaluate<ReturnType>(pageFunction, ...args);
   }
 
-  /**
-   * @param {string} selector
-   * @return {!Promise<?ElementHandle>}
-   */
-  async $(selector) {
+  async $(selector: string): Promise<ElementHandle | null> {
     return this._mainWorld.$(selector);
   }
 
-  /**
-   * @param {string} expression
-   * @return {!Promise<!Array<!ElementHandle>>}
-   */
-  async $x(expression) {
+  async $x(expression: string): Promise<ElementHandle[]> {
     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<ReturnType extends any>(selector: string, pageFunction: Function | string, ...args: unknown[]): Promise<ReturnType> {
+    return this._mainWorld.$eval<ReturnType>(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<ReturnType extends any>(selector: string, pageFunction: Function | string, ...args: unknown[]): Promise<ReturnType> {
+    return this._mainWorld.$$eval<ReturnType>(selector, pageFunction, ...args);
   }
 
-  /**
-   * @param {string} selector
-   * @return {!Promise<!Array<!ElementHandle>>}
-   */
-  async $$(selector) {
+  async $$(selector: string): Promise<ElementHandle[]> {
     return this._mainWorld.$$(selector);
   }
 
-  /**
-   * @return {!Promise<String>}
-   */
-  async content() {
+  async content(): Promise<string> {
     return this._secondaryWorld.content();
   }
 
-  /**
-   * @param {string} html
-   * @param {!{timeout?: number, waitUntil?: !Puppeteer.PuppeteerLifeCycleEvent|!Array<!Puppeteer.PuppeteerLifeCycleEvent>}=} options
-   */
-  async setContent(html, options = {}) {
+  async setContent(html: string, options: {timeout?: number; waitUntil?: PuppeteerLifeCycleEvent|PuppeteerLifeCycleEvent[]} = {}): Promise<void> {
     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.<!Frame>}
-   */
-  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<!ElementHandle>}
-   */
-  async addScriptTag(options) {
+  async addScriptTag(options: {url?: string; path?: string; content?: string; type?: string}): Promise<ElementHandle> {
     return this._mainWorld.addScriptTag(options);
   }
 
-  /**
-   * @param {!{url?: string, path?: string, content?: string}} options
-   * @return {!Promise<!ElementHandle>}
-   */
-  async addStyleTag(options) {
+  async addStyleTag(options: {url?: string; path?: string; content?: string}): Promise<ElementHandle> {
     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<void> {
     return this._secondaryWorld.click(selector, options);
   }
 
-  /**
-   * @param {string} selector
-   */
-  async focus(selector) {
+  async focus(selector: string): Promise<void> {
     return this._secondaryWorld.focus(selector);
   }
 
-  /**
-   * @param {string} selector
-   */
-  async hover(selector) {
+  async hover(selector: string): Promise<void> {
     return this._secondaryWorld.hover(selector);
   }
 
-  /**
-  * @param {string} selector
-  * @param {!Array<string>} values
-  * @return {!Promise<!Array<string>>}
-  */
-  select(selector, ...values){
+  select(selector: string, ...values: string[]): Promise<string[]> {
     return this._secondaryWorld.select(selector, ...values);
   }
 
-  /**
-   * @param {string} selector
-   */
-  async tap(selector) {
+  async tap(selector: string): Promise<void> {
     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<void> {
     return this._mainWorld.type(selector, text, options);
   }
 
-  /**
-   * @param {(string|number|Function)} selectorOrFunctionOrTimeout
-   * @param {!Object=} options
-   * @param {!Array<*>} args
-   * @return {!Promise<?JSHandle>}
-   */
-  waitFor(selectorOrFunctionOrTimeout, options = {}, ...args) {
+  waitFor(selectorOrFunctionOrTimeout: string|number|Function, options: {} = {}, ...args: unknown[]): Promise<JSHandle | null> {
     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<?ElementHandle>}
-   */
-  async waitForSelector(selector, options) {
+  async waitForSelector(selector: string, options: WaitForSelectorOptions): Promise<ElementHandle | null> {
     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<?ElementHandle>}
-   */
-  async waitForXPath(xpath, options) {
+  async waitForXPath(xpath: string, options: WaitForSelectorOptions): Promise<ElementHandle | null> {
     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<!JSHandle>}
-   */
-  waitForFunction(pageFunction, options = {}, ...args) {
+  waitForFunction(pageFunction: Function|string, options: {polling?: string|number; timeout?: number} = {}, ...args: unknown[]): Promise<JSHandle> {
     return this._mainWorld.waitForFunction(pageFunction, options, ...args);
   }
 
-  /**
-   * @return {!Promise<string>}
-   */
-  async title() {
+  async title(): Promise<string> {
     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 f433eb46..c78eec3b 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<Puppeteer.Frame | null> {
+  async contentFrame(): Promise<Frame | null> {
     const nodeInfo = await this._client.send('DOM.describeNode', {
       objectId: this._remoteObject.objectId
     });
diff --git a/src/LifecycleWatcher.ts b/src/LifecycleWatcher.ts
index 13b59af8..9a7f071b 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<PuppeteerLifeCycleEvent, ProtocolLi
 
 export class LifecycleWatcher {
   _expectedLifecycle: ProtocolLifeCycleEvent[];
-  _frameManager: Puppeteer.FrameManager;
-  _frame: Puppeteer.Frame;
+  _frameManager: FrameManager;
+  _frame: Frame;
   _timeout: number;
   _navigationRequest?: Puppeteer.Request;
   _eventListeners: PuppeteerEventListener[];
@@ -56,7 +57,7 @@ export class LifecycleWatcher {
   _maximumTimer?: NodeJS.Timeout;
   _hasSameDocumentNavigation?: boolean;
 
-  constructor(frameManager: Puppeteer.FrameManager, frame: Puppeteer.Frame, waitUntil: PuppeteerLifeCycleEvent|PuppeteerLifeCycleEvent[], timeout: number) {
+  constructor(frameManager: FrameManager, frame: Frame, waitUntil: PuppeteerLifeCycleEvent|PuppeteerLifeCycleEvent[], timeout: number) {
     if (Array.isArray(waitUntil))
       waitUntil = waitUntil.slice();
     else if (typeof waitUntil === 'string')
@@ -106,7 +107,7 @@ export class LifecycleWatcher {
     this._navigationRequest = request;
   }
 
-  _onFrameDetached(frame: Puppeteer.Frame): void {
+  _onFrameDetached(frame: Frame): void {
     if (this._frame === frame) {
       this._terminationCallback.call(null, new Error('Navigating frame was detached'));
       return;
@@ -146,7 +147,7 @@ export class LifecycleWatcher {
         .then(() => 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<string>} 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 2766543a..fc062f8a 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 6b91be9e..02a852f4 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<Puppeteer.Frame>}
+   * @return {!Array<Frame>}
    */
   frames() {
     return this._frameManager.frames();
diff --git a/src/externs.d.ts b/src/externs.d.ts
index 390967ab..bad0876a 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 1a81455d..e678f123 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 958f9a59..1df37191 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<PuppeteerLifeCycleEvent>'