/**
 * Copyright 2017 Google Inc. All rights reserved.
 *
 * 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.
 */ import { EventEmitter } from './EventEmitter.js' ; import { assert } from './assert.js' ; import { helper } from './helper.js' ; import { ExecutionContext , EVALUATION_SCRIPT_URL } from './ExecutionContext.js' ; import { LifecycleWatcher , PuppeteerLifeCycleEvent , } from './LifecycleWatcher.js' ; import { DOMWorld , WaitForSelectorOptions } from './DOMWorld.js' ; import { NetworkManager } from './NetworkManager.js' ; import { TimeoutSettings } from './TimeoutSettings.js' ; import { Connection , CDPSession } from './Connection.js' ; import { JSHandle , ElementHandle } from './JSHandle.js' ; import { MouseButton } from './Input.js' ; import { Page } from './Page.js' ; import { HTTPResponse } from './HTTPResponse.js' ; import { Protocol } from 'devtools-protocol' ; import { SerializableOrJSHandle , EvaluateHandleFn , WrapElementHandle , EvaluateFn , EvaluateFnReturnType , UnwrapPromiseLike , } from './EvalTypes.js' ; const UTILITY_WORLD_NAME = '__puppeteer_utility_world__' ; const xPathPattern = /^\(\/\/[^\)]+\)|^\/\// ; internal export const FrameManagerEmittedEvents = { FrameAttached : Symbol ( 'FrameManager.FrameAttached' ) , FrameNavigated : Symbol ( 'FrameManager.FrameNavigated' ) , FrameDetached : Symbol ( 'FrameManager.FrameDetached' ) , LifecycleEvent : Symbol ( 'FrameManager.LifecycleEvent' ) , FrameNavigatedWithinDocument : Symbol ( 'FrameManager.FrameNavigatedWithinDocument' ) , ExecutionContextCreated : Symbol ( 'FrameManager.ExecutionContextCreated' ) , ExecutionContextDestroyed : Symbol ( 'FrameManager.ExecutionContextDestroyed' ) , } ; internal export class FrameManager extends EventEmitter { _client : CDPSession ; private _page : Page ; private _networkManager : NetworkManager ; _timeoutSettings : TimeoutSettings ; private _frames = new Map < string , Frame > ( ) ; private _contextIdToContext = new Map < string , ExecutionContext > ( ) ; private _isolatedWorlds = new Set < string > ( ) ; private _mainFrame : Frame ; constructor ( client , page , ignoreHTTPSErrors , timeoutSettings ) { super ( ) ; this . _client = client ; this . _page = page ; this . _networkManager = new NetworkManager ( client , ignoreHTTPSErrors , this ) ; this . _timeoutSettings = timeoutSettings ; this . setupEventListeners ( this . _client ) ; } private setupEventListeners ( session ) { session . on ( 'Page.frameAttached' , ( event ) => { this . _onFrameAttached ( session , event . frameId , event . parentFrameId ) ; } ) ; session . on ( 'Page.frameNavigated' , ( event ) => { this . _onFrameNavigated ( event . frame ) ; } ) ; session . on ( 'Page.navigatedWithinDocument' , ( event ) => { this . _onFrameNavigatedWithinDocument ( event . frameId , event . url ) ; } ) ; session . on ( 'Page.frameDetached' , ( event ) => { this . _onFrameDetached ( event . frameId , event . reason as Protocol . Page . FrameDetachedEventReason ) ; } ) ; session . on ( 'Page.frameStoppedLoading' , ( event ) => { this . _onFrameStoppedLoading ( event . frameId ) ; } ) ; session . on ( 'Runtime.executionContextCreated' , ( event ) => { this . _onExecutionContextCreated ( event . context , session ) ; } ) ; session . on ( 'Runtime.executionContextDestroyed' , ( event ) => { this . _onExecutionContextDestroyed ( event . executionContextId , session ) ; } ) ; session . on ( 'Runtime.executionContextsCleared' , ( ) => { this . _onExecutionContextsCleared ( session ) ; } ) ; session . on ( 'Page.lifecycleEvent' , ( event ) => { this . _onLifecycleEvent ( event ) ; } ) ; session . on ( 'Target.attachedToTarget' , async ( event ) => { this . _onAttachedToTarget ( event ) ; } ) ; session . on ( 'Target.detachedFromTarget' , async ( event ) => { this . _onDetachedFromTarget ( event ) ; } ) ; } async initialize ( client = this . _client ) { try { const result = await Promise . all ( [ client . send ( 'Page.enable' ) , client . send ( 'Page.getFrameTree' ) , ] ) ; const { frameTree } = result [ 1 ] ; this . _handleFrameTree ( client , frameTree ) ; await Promise . all ( [ client . send ( 'Page.setLifecycleEventsEnabled' , { enabled : true } ) , client . send ( 'Runtime.enable' ) . then ( ( ) => this . _ensureIsolatedWorld ( client , UTILITY_WORLD_NAME ) ) , client === this . _client ? this . _networkManager . initialize ( ) : Promise . resolve ( ) , ] ) ; } catch ( error ) { if ( error . message . includes ( 'Target closed' ) || error . message . includes ( 'Session closed' ) ) { return ; } throw error ; } } networkManager ( ) { return this . _networkManager ; } async navigateFrame ( frame , url , options = { } ) { assertNoLegacyNavigationOptions ( options ) ; const { referer = this . _networkManager . extraHTTPHeaders ( ) [ 'referer' ] , waitUntil = [ 'load' ] , timeout = this . _timeoutSettings . navigationTimeout ( ) , } = options ; const watcher = new LifecycleWatcher ( this , frame , waitUntil , timeout ) ; let ensureNewDocumentNavigation = false ; let error = await Promise . race ( [ navigate ( this . _client , url , referer , frame . _id ) , watcher . timeoutOrTerminationPromise ( ) , ] ) ; if ( ! error ) { error = await Promise . race ( [ watcher . timeoutOrTerminationPromise ( ) , ensureNewDocumentNavigation ? watcher . newDocumentNavigationPromise ( ) : watcher . sameDocumentNavigationPromise ( ) , ] ) ; } watcher . dispose ( ) ; if ( error ) throw error ; return await watcher . navigationResponse ( ) ; async function navigate ( client , url , referrer , frameId ) { try { const response = await client . send ( 'Page.navigate' , { url , referrer , frameId , } ) ; ensureNewDocumentNavigation = ! ! response . loaderId ; return response . errorText ? new Error ( `${ response . errorText } at ${ url }` ) : null ; } catch ( error ) { return error ; } } } async waitForFrameNavigation ( frame , options = { } ) { assertNoLegacyNavigationOptions ( options ) ; const { waitUntil = [ 'load' ] , timeout = this . _timeoutSettings . navigationTimeout ( ) , } = options ; const watcher = new LifecycleWatcher ( this , frame , waitUntil , timeout ) ; const error = await Promise . race ( [ watcher . timeoutOrTerminationPromise ( ) , watcher . sameDocumentNavigationPromise ( ) , watcher . newDocumentNavigationPromise ( ) , ] ) ; watcher . dispose ( ) ; if ( error ) throw error ; return await watcher . navigationResponse ( ) ; } private async _onAttachedToTarget ( event ) { if ( event . targetInfo . type !== 'iframe' ) { return ; } const frame = this . _frames . get ( event . targetInfo . targetId ) ; const session = Connection . fromSession ( this . _client ) . session ( event . sessionId ) ; if ( frame ) frame . _updateClient ( session ) ; this . setupEventListeners ( session ) ; await this . initialize ( session ) ; } private async _onDetachedFromTarget ( event ) { const frame = this . _frames . get ( event . targetId ) ; if ( frame && frame . isOOPFrame ( ) ) { this . _removeFramesRecursively ( frame ) ; } } _onLifecycleEvent ( event ) { const frame = this . _frames . get ( event . frameId ) ; if ( ! frame ) return ; frame . _onLifecycleEvent ( event . loaderId , event . name ) ; this . emit ( FrameManagerEmittedEvents . LifecycleEvent , frame ) ; } _onFrameStoppedLoading ( frameId ) { const frame = this . _frames . get ( frameId ) ; if ( ! frame ) return ; frame . _onLoadingStopped ( ) ; this . emit ( FrameManagerEmittedEvents . LifecycleEvent , frame ) ; } _handleFrameTree ( session , frameTree ) { if ( frameTree . frame . parentId ) { this . _onFrameAttached ( session , frameTree . frame . id , frameTree . frame . parentId ) ; } this . _onFrameNavigated ( frameTree . frame ) ; if ( ! frameTree . childFrames ) return ; for ( const child of frameTree . childFrames ) { this . _handleFrameTree ( session , child ) ; } } page ( ) { return this . _page ; } mainFrame ( ) { return this . _mainFrame ; } frames ( ) { return Array . from ( this . _frames . values ( ) ) ; } frame ( frameId ) { return this . _frames . get ( frameId ) || null ; } _onFrameAttached ( session , frameId , parentFrameId ? ) { if ( this . _frames . has ( frameId ) ) { const frame = this . _frames . get ( frameId ) ; if ( session && frame . isOOPFrame ( ) ) { frame . _updateClient ( session ) ; } return ; } assert ( parentFrameId ) ; const parentFrame = this . _frames . get ( parentFrameId ) ; const frame = new Frame ( this , parentFrame , frameId , session ) ; this . _frames . set ( frame . _id , frame ) ; this . emit ( FrameManagerEmittedEvents . FrameAttached , frame ) ; } _onFrameNavigated ( framePayload ) { 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' ) ; if ( frame ) { for ( const child of frame . childFrames ( ) ) this . _removeFramesRecursively ( child ) ; } if ( isMainFrame ) { if ( frame ) { this . _frames . delete ( frame . _id ) ; frame . _id = framePayload . id ; } else { frame = new Frame ( this , null , framePayload . id , this . _client ) ; } this . _frames . set ( framePayload . id , frame ) ; this . _mainFrame = frame ; } frame . _navigated ( framePayload ) ; this . emit ( FrameManagerEmittedEvents . FrameNavigated , frame ) ; } async _ensureIsolatedWorld ( session , name ) { const key = `${ session . id ( ) }:${ name }` ; if ( this . _isolatedWorlds . has ( key ) ) return ; this . _isolatedWorlds . add ( key ) ; await session . send ( 'Page.addScriptToEvaluateOnNewDocument' , { source : `//# sourceURL=${ EVALUATION_SCRIPT_URL }` , worldName : name , } ) ; await Promise . all ( this . frames ( ) . filter ( ( frame ) => frame . _client === session ) . map ( ( frame ) => session . send ( 'Page.createIsolatedWorld' , { frameId : frame . _id , worldName : name , grantUniveralAccess : true , } ) ) ) ; } _onFrameNavigatedWithinDocument ( frameId , url ) { const frame = this . _frames . get ( frameId ) ; if ( ! frame ) return ; frame . _navigatedWithinDocument ( url ) ; this . emit ( FrameManagerEmittedEvents . FrameNavigatedWithinDocument , frame ) ; this . emit ( FrameManagerEmittedEvents . FrameNavigated , frame ) ; } _onFrameDetached ( frameId , reason ) { const frame = this . _frames . get ( frameId ) ; if ( reason === 'remove' ) { if ( frame ) this . _removeFramesRecursively ( frame ) ; } } _onExecutionContextCreated ( contextPayload , session ) { 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 ) { if ( frame . _client !== session ) return ; if ( contextPayload . auxData && ! ! contextPayload . auxData [ 'isDefault' ] ) { world = frame . _mainWorld ; } else if ( contextPayload . name === UTILITY_WORLD_NAME && ! frame . _secondaryWorld . _hasContext ( ) ) { world = frame . _secondaryWorld ; } } const context = new ExecutionContext ( frame ?. _client || this . _client , contextPayload , world ) ; if ( world ) world . _setContext ( context ) ; const key = `${ session . id ( ) }:${ contextPayload . id }` ; this . _contextIdToContext . set ( key , context ) ; } private _onExecutionContextDestroyed ( executionContextId , session ) { const key = `${ session . id ( ) }:${ executionContextId }` ; const context = this . _contextIdToContext . get ( key ) ; if ( ! context ) return ; this . _contextIdToContext . delete ( key ) ; if ( context . _world ) context . _world . _setContext ( null ) ; } private _onExecutionContextsCleared ( session ) { for ( const [ key , context ] of this . _contextIdToContext . entries ( ) ) { if ( context . _client !== session ) continue ; if ( context . _world ) context . _world . _setContext ( null ) ; this . _contextIdToContext . delete ( key ) ; } } executionContextById ( contextId , session = this . _client ) { const key = `${ session . id ( ) }:${ contextId }` ; const context = this . _contextIdToContext . get ( key ) ; assert ( context , 'INTERNAL ERROR: missing context with id = ' + contextId ) ; return context ; } private _removeFramesRecursively ( frame ) { for ( const child of frame . childFrames ( ) ) this . _removeFramesRecursively ( child ) ; frame . _detach ( ) ; this . _frames . delete ( frame . _id ) ; this . emit ( FrameManagerEmittedEvents . FrameDetached , frame ) ; } } public export interface FrameWaitForFunctionOptions { /**
     * An interval at which the `pageFunction` is executed, defaults to `raf`. If
     * `polling` is a number, then it is treated as an interval in milliseconds at
     * which the function would be executed. If `polling` is a string, then it can
     * be one of the following values:
     *
     * - `raf` - to constantly execute `pageFunction` in `requestAnimationFrame`
     *   callback. This is the tightest polling mode which is suitable to observe
     *   styling changes.
     *
     * - `mutation` - to execute `pageFunction` on every DOM mutation.
     */ polling ? : string | number ; /**
     * Maximum time to wait in milliseconds. Defaults to `30000` (30 seconds).
     * Pass `0` to disable the timeout. Puppeteer's default timeout can be changed
     * using  Page . setDefaultTimeout .
      timeout ? : number ; } public export interface FrameAddScriptTagOptions { /**
     * the URL of the script to be added.
     */ url ? : string ; remarks path ? : string ; /**
     * Raw JavaScript content to be injected into the frame.
     */ content ? : string ; /**
     * Set the script's `type`. Use `module` in order to load an ES2015 module.
     */ type ? : string ; } public export interface FrameAddStyleTagOptions { /**
     * the URL of the CSS file to be added.
     */ url ? : string ; remarks path ? : string ; /**
     * Raw CSS content to be injected into the frame.
     */ content ? : string ; } /**
 * At every point of time, page exposes its current frame tree via the
 *  Page . mainFrame and
 *  Frame . childFrames methods.
 *
 *  remarks `Frame` object lifecycles are controlled by three events that are all
 * dispatched on the page object:
 *
 * -  PageEmittedEvents . FrameAttached *
 * -  PageEmittedEvents . FrameNavigated *
 * -  PageEmittedEvents . FrameDetached Example Example public export class Frame { internal _frameManager : FrameManager ; private _parentFrame ? : Frame ; internal _id : string ; private _url = '' ; private _detached = false ; internal _loaderId = '' ; internal _name ? : string ; internal _lifecycleEvents = new Set < string > ( ) ; internal _mainWorld : DOMWorld ; internal _secondaryWorld : DOMWorld ; internal _childFrames : Set < Frame > ; internal _client : CDPSession ; internal constructor ( frameManager , parentFrame , frameId , client ) { this . _frameManager = frameManager ; this . _parentFrame = parentFrame ; this . _url = '' ; this . _id = frameId ; this . _detached = false ; this . _loaderId = '' ; this . _childFrames = new Set ( ) ; if ( this . _parentFrame ) this . _parentFrame . _childFrames . add ( this ) ; this . _updateClient ( client ) ; } internal _updateClient ( client ) { this . _client = client ; this . _mainWorld = new DOMWorld ( this . _client , this . _frameManager , this , this . _frameManager . _timeoutSettings ) ; this . _secondaryWorld = new DOMWorld ( this . _client , this . _frameManager , this , this . _frameManager . _timeoutSettings ) ; } isOOPFrame ( ) { return this . _client !== this . _frameManager . _client ; } remarks `frame.goto` will throw an error if:
     * - there's an SSL error (e.g. in case of self-signed certificates).
     *
     * - target URL is invalid.
     *
     * - the `timeout` is exceeded during navigation.
     *
     * - the remote server does not respond or is unreachable.
     *
     * - the main resource failed to load.
     *
     * `frame.goto` will not throw an error when any valid HTTP status code is
     * returned by the remote server, including 404 "Not Found" and 500 "Internal
     * Server Error".  The status code for such responses can be retrieved by
     * calling  HTTPResponse . status .
     *
     * NOTE: `frame.goto` either throws an error or returns a main resource
     * response. The only exceptions are navigation to `about:blank` or
     * navigation to the same URL with a different hash, which would succeed and
     * return `null`.
     *
     * NOTE: Headless mode doesn't support navigation to a PDF document. See
     * the  https * issue}.
     *
     *  param url param options - navigation options. `waitUntil` is useful to define when
     * the navigation should be considered successful - see the docs for
     *  PuppeteerLifeCycleEvent for more details.
     *
     *  returns async goto ( url , options = { } ) { return await this . _frameManager . navigateFrame ( this , url , options ) ; } remarks This resolves when the frame navigates to a new URL. It is useful for when
     * you run code which will indirectly cause the frame to navigate. Consider
     * this example:
     *
     * ```js
     * const [response] = await Promise.all([
     *   // The navigation promise resolves after navigation has finished
     *   frame.waitForNavigation(),
     *   // Clicking the link will indirectly cause a navigation
     *   frame.click('a.my-link'),
     * ]);
     * ```
     *
     * Usage of the  https to change the URL is considered a navigation.
     *
     *  param options returns async waitForNavigation ( options = { } ) { return await this . _frameManager . waitForFrameNavigation ( this , options ) ; } returns executionContext ( ) { return this . _mainWorld . executionContext ( ) ; } remarks The only difference between  Frame . evaluate and
     * `frame.evaluateHandle` is that `evaluateHandle` will return the value
     * wrapped in an in-page object.
     *
     * This method behaves identically to  Page . evaluateHandle except it's
     * run within the context of the `frame`, rather than the entire page.
     *
     *  param pageFunction param args async evaluateHandle < HandlerType extends JSHandle = JSHandle > ( pageFunction , ... args ) { return this . _mainWorld . evaluateHandle < HandlerType > ( pageFunction , ... args ) ; } remarks This method behaves identically to  Page . evaluate except it's run
     * within the context of the `frame`, rather than the entire page.
     *
     *  param pageFunction param args async evaluate < T extends EvaluateFn > ( pageFunction , ... args ) { return this . _mainWorld . evaluate < T > ( pageFunction , ... args ) ; } param selector returns async $ < T extends Element = Element > ( selector ) { return this . _mainWorld . $ < T > ( selector ) ; } param expression async $x ( expression ) { return this . _mainWorld . $x ( expression ) ; } remarks example param selector param pageFunction param args async $eval < ReturnType > ( selector , pageFunction , ... args ) { return this . _mainWorld . $eval < ReturnType > ( selector , pageFunction , ... args ) ; } remarks example param selector param pageFunction param args async $$eval < ReturnType > ( selector , pageFunction , ... args ) { return this . _mainWorld . $$eval < ReturnType > ( selector , pageFunction , ... args ) ; } param selector returns async $$ < T extends Element = Element > ( selector ) { return this . _mainWorld . $$ < T > ( selector ) ; } returns async content ( ) { return this . _secondaryWorld . content ( ) ; } param html param options async setContent ( html , options = { } ) { return this . _secondaryWorld . setContent ( html , options ) ; } remarks returns name ( ) { return this . _name || '' ; } returns url ( ) { return this . _url ; } returns parentFrame ( ) { return this . _parentFrame ; } returns childFrames ( ) { return Array . from ( this . _childFrames ) ; } returns isDetached ( ) { return this . _detached ; } param options returns async addScriptTag ( options ) { return this . _mainWorld . addScriptTag ( options ) ; } param options returns async addStyleTag ( options ) { return this . _mainWorld . addStyleTag ( options ) ; } remarks This method scrolls the element into view if needed, and then uses
     *  Page . mouse to click in the center of the element. If there's no
     * element matching `selector`, the method throws an error.
     *
     * Bear in mind that if `click()` triggers a navigation event and there's a
     * separate `page.waitForNavigation()` promise to be resolved, you may end up
     * with a race condition that yields unexpected results. The correct pattern
     * for click and wait for navigation is the following:
     *
     * ```javascript
     * const [response] = await Promise.all([
     *   page.waitForNavigation(waitOptions),
     *   frame.click(selector, clickOptions),
     * ]);
     * ```
     *  param selector async click ( selector , options = { } ) { return this . _secondaryWorld . click ( selector , options ) ; } remarks param selector async focus ( selector ) { return this . _secondaryWorld . focus ( selector ) ; } /**
     * This method fetches an element with `selector`, scrolls it into view if
     * needed, and then uses  Page . mouse to hover over the center of the
     * element.
     *
     *  remarks param selector async hover ( selector ) { return this . _secondaryWorld . hover ( selector ) ; } remarks example param selector param values returns select ( selector , ... values ) { return this . _secondaryWorld . select ( selector , ... values ) ; } /**
     * This method fetches an element with `selector`, scrolls it into view if
     * needed, and then uses  Page . touchscreen to tap in the center of the
     * element.
     *
     *  remarks param selector returns async tap ( selector ) { return this . _secondaryWorld . tap ( selector ) ; } remarks To press a special key, like `Control` or `ArrowDown`, use
     *  Keyboard . press .
     *
     *  example param selector param text param options returns async type ( selector , text , options ? ) { return this . _mainWorld . type ( selector , text , options ) ; } remarks This method behaves differently depending on the first parameter. If it's a
     * `string`, it will be treated as a `selector` or `xpath` (if the string
     * starts with `//`). This method then is a shortcut for
     *  Frame . waitForSelector or  Frame . waitForXPath .
     *
     * If the first argument is a function this method is a shortcut for
     *  Frame . waitForFunction .
     *
     * If the first argument is a `number`, it's treated as a timeout in
     * milliseconds and the method returns a promise which resolves after the
     * timeout.
     *
     *  param selectorOrFunctionOrTimeout param options param args deprecated Don't use this method directly. Instead use the more explicit
     * methods available:  Frame . waitForSelector ,
     *  Frame . waitForXPath ,  Frame . waitForFunction or
     *  Frame . waitForTimeout .
      waitFor ( selectorOrFunctionOrTimeout , options = { } , ... args ) { console . warn ( 'waitFor is deprecated and will be removed in a future release. See https://github.com/puppeteer/puppeteer/issues/6214 for details and how to migrate your code.' ) ; if ( helper . isString ( selectorOrFunctionOrTimeout ) ) { const string = selectorOrFunctionOrTimeout ; if ( xPathPattern . test ( string ) ) return this . waitForXPath ( string , options ) ; return this . waitForSelector ( string , options ) ; } if ( helper . isNumber ( 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 ) ) ; } remarks It's generally recommended to not wait for a number of seconds, but instead
     * use  Frame . waitForSelector ,  Frame . waitForXPath or
     *  Frame . waitForFunction to wait for exactly the conditions you want.
     *
     *  example param milliseconds waitForTimeout ( milliseconds ) { return new Promise ( ( resolve ) => { setTimeout ( resolve , milliseconds ) ; } ) ; } remarks example param selector param options returns async waitForSelector ( selector , options = { } ) { const handle = await this . _secondaryWorld . waitForSelector ( selector , options ) ; if ( ! handle ) return null ; const mainExecutionContext = await this . _mainWorld . executionContext ( ) ; const result = await mainExecutionContext . _adoptElementHandle ( handle ) ; await handle . dispose ( ) ; return result ; } remarks Wait for the `xpath` to appear in page. If at the moment of calling the
     * method the `xpath` already exists, the method will return immediately. If
     * the xpath doesn't appear after the `timeout` milliseconds of waiting, the
     * function will throw.
     *
     * For a code example, see the example for  Frame . waitForSelector . That
     * function behaves identically other than taking a CSS selector rather than
     * an XPath.
     *
     *  param xpath param options async waitForXPath ( xpath , options = { } ) { const handle = await this . _secondaryWorld . waitForXPath ( xpath , options ) ; if ( ! handle ) return null ; const mainExecutionContext = await this . _mainWorld . executionContext ( ) ; const result = await mainExecutionContext . _adoptElementHandle ( handle ) ; await handle . dispose ( ) ; return result ; } remarks example param pageFunction param options param args returns waitForFunction ( pageFunction , options = { } , ... args ) { return this . _mainWorld . waitForFunction ( pageFunction , options , ... args ) ; } returns async title ( ) { return this . _secondaryWorld . title ( ) ; } internal _navigated ( framePayload ) { this . _name = framePayload . name ; this . _url = `${ framePayload . url }${ framePayload . urlFragment || '' }` ; } internal _navigatedWithinDocument ( url ) { this . _url = url ; } internal _onLifecycleEvent ( loaderId , name ) { if ( name === 'init' ) { this . _loaderId = loaderId ; this . _lifecycleEvents . clear ( ) ; } this . _lifecycleEvents . add ( name ) ; } internal _onLoadingStopped ( ) { this . _lifecycleEvents . add ( 'DOMContentLoaded' ) ; this . _lifecycleEvents . add ( 'load' ) ; } internal _detach ( ) { this . _detached = true ; this . _mainWorld . _detach ( ) ; this . _secondaryWorld . _detach ( ) ; if ( this . _parentFrame ) this . _parentFrame . _childFrames . delete ( this ) ; this . _parentFrame = null ; } } 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' ) ; }