2017-05-11 07:06:41 +00:00
/ * *
* 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 .
* /
2021-06-23 12:51:38 +00:00
import type { Readable } from 'stream' ;
2021-10-04 06:59:46 +00:00
import { EventEmitter , Handler } from './EventEmitter.js' ;
2020-07-13 09:22:26 +00:00
import {
Connection ,
CDPSession ,
CDPSessionEmittedEvents ,
} from './Connection.js' ;
import { Dialog } from './Dialog.js' ;
import { EmulationManager } from './EmulationManager.js' ;
import {
Frame ,
FrameManager ,
FrameManagerEmittedEvents ,
} from './FrameManager.js' ;
import { Keyboard , Mouse , Touchscreen , MouseButton } from './Input.js' ;
import { Tracing } from './Tracing.js' ;
2021-09-21 14:26:20 +00:00
import { assert , assertNever } from './assert.js' ;
2020-07-13 09:22:26 +00:00
import { helper , debugError } from './helper.js' ;
import { Coverage } from './Coverage.js' ;
import { WebWorker } from './WebWorker.js' ;
import { Browser , BrowserContext } from './Browser.js' ;
import { Target } from './Target.js' ;
import { createJSHandle , JSHandle , ElementHandle } from './JSHandle.js' ;
import { Viewport } from './PuppeteerViewport.js' ;
2021-01-21 09:00:57 +00:00
import {
Credentials ,
NetworkConditions ,
NetworkManagerEmittedEvents ,
} from './NetworkManager.js' ;
2020-07-13 09:22:26 +00:00
import { HTTPRequest } from './HTTPRequest.js' ;
import { HTTPResponse } from './HTTPResponse.js' ;
import { Accessibility } from './Accessibility.js' ;
import { TimeoutSettings } from './TimeoutSettings.js' ;
import { FileChooser } from './FileChooser.js' ;
import { ConsoleMessage , ConsoleMessageType } from './ConsoleMessage.js' ;
import { PuppeteerLifeCycleEvent } from './LifecycleWatcher.js' ;
2020-07-10 10:51:52 +00:00
import { Protocol } from 'devtools-protocol' ;
2020-07-01 11:44:08 +00:00
import {
SerializableOrJSHandle ,
EvaluateHandleFn ,
2020-07-02 09:09:34 +00:00
WrapElementHandle ,
2020-07-10 10:52:13 +00:00
EvaluateFn ,
EvaluateFnReturnType ,
UnwrapPromiseLike ,
2020-07-13 09:22:26 +00:00
} from './EvalTypes.js' ;
2020-07-17 12:58:56 +00:00
import { PDFOptions , paperFormats } from './PDFOptions.js' ;
2020-09-28 09:35:35 +00:00
import { isNode } from '../environment.js' ;
2021-09-23 12:37:35 +00:00
import { TaskQueue } from './TaskQueue.js' ;
2017-10-11 07:55:48 +00:00
2020-06-26 07:24:56 +00:00
/ * *
* @public
* /
2020-06-23 05:18:46 +00:00
export interface Metrics {
2020-05-07 10:54:55 +00:00
Timestamp? : number ;
Documents? : number ;
Frames? : number ;
JSEventListeners? : number ;
Nodes? : number ;
LayoutCount? : number ;
RecalcStyleCount? : number ;
LayoutDuration? : number ;
RecalcStyleDuration? : number ;
ScriptDuration? : number ;
TaskDuration? : number ;
JSHeapUsedSize? : number ;
JSHeapTotalSize? : number ;
2020-05-05 12:53:22 +00:00
}
2020-06-26 07:24:56 +00:00
/ * *
* @public
* /
export interface WaitTimeoutOptions {
/ * *
* Maximum wait time in milliseconds , defaults to 30 seconds , pass ` 0 ` to
* disable the timeout .
*
* @remarks
* The default value can be changed by using the
* { @link Page . setDefaultTimeout } method .
* /
timeout? : number ;
}
/ * *
* @public
* /
export interface WaitForOptions {
/ * *
* Maximum wait time in milliseconds , defaults to 30 seconds , pass ` 0 ` to
* disable the timeout .
*
* @remarks
* The default value can be changed by using the
* { @link Page . setDefaultTimeout } or { @link Page . setDefaultNavigationTimeout }
* methods .
* /
2020-05-05 12:53:22 +00:00
timeout? : number ;
2020-05-07 10:54:55 +00:00
waitUntil? : PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent [ ] ;
2020-05-05 12:53:22 +00:00
}
2020-06-26 07:24:56 +00:00
/ * *
* @public
* /
export interface GeolocationOptions {
/ * *
* Latitude between - 90 and 90 .
* /
longitude : number ;
/ * *
* Longitude between - 180 and 180 .
* /
latitude : number ;
/ * *
* Optional non - negative accuracy value .
* /
accuracy? : number ;
}
2021-04-12 13:57:05 +00:00
/ * *
* @public
* /
export interface MediaFeature {
2020-05-05 12:53:22 +00:00
name : string ;
value : string ;
}
2021-02-11 09:35:26 +00:00
/ * *
* @public
* /
export interface ScreenshotClip {
2020-05-05 12:53:22 +00:00
x : number ;
y : number ;
width : number ;
height : number ;
}
2021-02-11 09:35:26 +00:00
/ * *
* @public
* /
export interface ScreenshotOptions {
/ * *
2021-02-12 12:32:27 +00:00
* @defaultValue 'png'
2021-02-11 09:35:26 +00:00
* /
2021-09-21 14:26:20 +00:00
type ? : 'png' | 'jpeg' | 'webp' ;
2021-02-11 09:35:26 +00:00
/ * *
* The file path to save the image to . The screenshot type will be inferred
* from file extension . If path is a relative path , then it is resolved
* relative to current working directory . If no path is provided , the image
* won ' t be saved to the disk .
* /
2020-05-05 12:53:22 +00:00
path? : string ;
2021-02-11 09:35:26 +00:00
/ * *
* When true , takes a screenshot of the full page .
2021-02-12 12:32:27 +00:00
* @defaultValue false
2021-02-11 09:35:26 +00:00
* /
2020-05-05 12:53:22 +00:00
fullPage? : boolean ;
2021-02-11 09:35:26 +00:00
/ * *
* An object which specifies the clipping region of the page .
* /
2020-05-05 12:53:22 +00:00
clip? : ScreenshotClip ;
2021-02-11 09:35:26 +00:00
/ * *
* Quality of the image , between 0 - 100 . Not applicable to ` png ` images .
* /
2020-05-05 12:53:22 +00:00
quality? : number ;
2021-02-11 09:35:26 +00:00
/ * *
* Hides default white background and allows capturing screenshots with transparency .
2021-02-12 12:32:27 +00:00
* @defaultValue false
2021-02-11 09:35:26 +00:00
* /
2020-05-05 12:53:22 +00:00
omitBackground? : boolean ;
2021-02-11 09:35:26 +00:00
/ * *
* Encoding of the image .
2021-02-12 12:32:27 +00:00
* @defaultValue 'binary'
2021-02-11 09:35:26 +00:00
* /
encoding ? : 'base64' | 'binary' ;
2021-04-06 13:14:31 +00:00
/ * *
* If you need a screenshot bigger than the Viewport
* @defaultValue true
* /
captureBeyondViewport? : boolean ;
2020-05-05 12:53:22 +00:00
}
2020-06-12 10:10:12 +00:00
/ * *
* All the events that a page instance may emit .
2020-06-26 07:24:56 +00:00
*
* @public
2020-06-12 10:10:12 +00:00
* /
export const enum PageEmittedEvents {
2021-08-09 08:57:14 +00:00
/ * * E m i t t e d w h e n t h e p a g e c l o s e s .
* @eventProperty
* /
2020-07-06 10:34:55 +00:00
Close = 'close' ,
/ * *
* Emitted when JavaScript within the page calls one of console API methods ,
* e . g . ` console.log ` or ` console.dir ` . Also emitted if the page throws an
* error or a warning .
*
* @remarks
*
* A ` console ` event provides a { @link ConsoleMessage } representing the
* console message that was logged .
*
* @example
* An example of handling ` console ` event :
* ` ` ` js
* page . on ( 'console' , msg = > {
* for ( let i = 0 ; i < msg . args ( ) . length ; ++ i )
* console . log ( ` ${ i } : ${ msg . args ( ) [ i ] } ` ) ;
* } ) ;
* page . evaluate ( ( ) = > console . log ( 'hello' , 5 , { foo : 'bar' } ) ) ;
* ` ` `
* /
Console = 'console' ,
/ * *
* Emitted when a JavaScript dialog appears , such as ` alert ` , ` prompt ` ,
* ` confirm ` or ` beforeunload ` . Puppeteer can respond to the dialog via
* { @link Dialog . accept } or { @link Dialog . dismiss } .
* /
Dialog = 'dialog' ,
/ * *
* Emitted when the JavaScript
* { @link https : //developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded | DOMContentLoaded } event is dispatched.
* /
DOMContentLoaded = 'domcontentloaded' ,
/ * *
* Emitted when the page crashes . Will contain an ` Error ` .
* /
Error = 'error' ,
/** Emitted when a frame is attached. Will contain a {@link Frame}. */
FrameAttached = 'frameattached' ,
/** Emitted when a frame is detached. Will contain a {@link Frame}. */
FrameDetached = 'framedetached' ,
/** Emitted when a frame is navigated to a new URL. Will contain a {@link Frame}. */
FrameNavigated = 'framenavigated' ,
/ * *
* Emitted when the JavaScript
* { @link https : //developer.mozilla.org/en-US/docs/Web/Events/load | load}
* event is dispatched .
* /
Load = 'load' ,
/ * *
* Emitted when the JavaScript code makes a call to ` console.timeStamp ` . For
* the list of metrics see { @link Page . metrics | page . metrics } .
*
* @remarks
* Contains an object with two properties :
* - ` title ` : the title passed to ` console.timeStamp `
* - ` metrics ` : objec containing metrics as key / value pairs . The values will
* be ` number ` s .
* /
Metrics = 'metrics' ,
/ * *
* Emitted when an uncaught exception happens within the page .
* Contains an ` Error ` .
* /
PageError = 'pageerror' ,
/ * *
* Emitted when the page opens a new tab or window .
*
* Contains a { @link Page } corresponding to the popup window .
*
* @example
*
* ` ` ` js
* const [ popup ] = await Promise . all ( [
* new Promise ( resolve = > page . once ( 'popup' , resolve ) ) ,
* page . click ( 'a[target=_blank]' ) ,
* ] ) ;
* ` ` `
*
* ` ` ` js
* const [ popup ] = await Promise . all ( [
* new Promise ( resolve = > page . once ( 'popup' , resolve ) ) ,
* page . evaluate ( ( ) = > window . open ( 'https://example.com' ) ) ,
* ] ) ;
* ` ` `
* /
Popup = 'popup' ,
/ * *
* Emitted when a page issues a request and contains a { @link HTTPRequest } .
*
* @remarks
2021-04-12 13:57:05 +00:00
* The object is readonly . See { @link Page . setRequestInterception } for intercepting
2020-07-06 10:34:55 +00:00
* and mutating requests .
* /
Request = 'request' ,
2021-03-17 14:42:35 +00:00
/ * *
* Emitted when a request ended up loading from cache . Contains a { @link HTTPRequest } .
*
* @remarks
* For certain requests , might contain undefined .
2021-04-12 13:57:05 +00:00
* { @link https : //crbug.com/750469}
2021-03-17 14:42:35 +00:00
* /
RequestServedFromCache = 'requestservedfromcache' ,
2020-07-06 10:34:55 +00:00
/ * *
* Emitted when a request fails , for example by timing out .
*
* Contains a { @link HTTPRequest } .
*
* @remarks
*
* NOTE : HTTP Error responses , such as 404 or 503 , are still successful
* responses from HTTP standpoint , so request will complete with
* ` requestfinished ` event and not with ` requestfailed ` .
* /
RequestFailed = 'requestfailed' ,
/ * *
* Emitted when a request finishes successfully . Contains a { @link HTTPRequest } .
* /
RequestFinished = 'requestfinished' ,
/ * *
* Emitted when a response is received . Contains a { @link HTTPResponse } .
* /
Response = 'response' ,
2020-06-12 10:10:12 +00:00
/ * *
2020-06-19 14:39:03 +00:00
* Emitted when a dedicated
* { @link https : //developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API | WebWorker}
* is spawned by the page .
2020-06-12 10:10:12 +00:00
* /
WorkerCreated = 'workercreated' ,
2020-07-06 10:34:55 +00:00
/ * *
* Emitted when a dedicated
* { @link https : //developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API | WebWorker}
* is destroyed by the page .
* /
WorkerDestroyed = 'workerdestroyed' ,
2020-06-12 10:10:12 +00:00
}
2021-03-25 11:26:35 +00:00
/ * *
* Denotes the objects received by callback functions for page events .
*
* See { @link PageEmittedEvents } for more detail on the events and when they are
* emitted .
* @public
* /
export interface PageEventObject {
close : never ;
console : ConsoleMessage ;
dialog : Dialog ;
domcontentloaded : never ;
error : Error ;
frameattached : Frame ;
framedetached : Frame ;
framenavigated : Frame ;
load : never ;
metrics : { title : string ; metrics : Metrics } ;
pageerror : Error ;
popup : Page ;
request : HTTPRequest ;
response : HTTPResponse ;
requestfailed : HTTPRequest ;
requestfinished : HTTPRequest ;
requestservedfromcache : HTTPRequest ;
workercreated : WebWorker ;
workerdestroyed : WebWorker ;
}
2020-06-12 10:10:12 +00:00
/ * *
2020-07-06 10:34:55 +00:00
* Page provides methods to interact with a single tab or
* { @link https : //developer.chrome.com/extensions/background_pages | extension background page} in Chromium.
2020-06-12 10:10:12 +00:00
*
* @remarks
*
2020-07-06 10:34:55 +00:00
* One Browser instance might have multiple Page instances .
*
2020-06-12 10:10:12 +00:00
* @example
* This example creates a page , navigates it to a URL , and then * saves a screenshot :
* ` ` ` js
* const puppeteer = require ( 'puppeteer' ) ;
*
* ( async ( ) = > {
* const browser = await puppeteer . launch ( ) ;
* const page = await browser . newPage ( ) ;
* await page . goto ( 'https://example.com' ) ;
* await page . screenshot ( { path : 'screenshot.png' } ) ;
* await browser . close ( ) ;
* } ) ( ) ;
* ` ` `
*
2020-07-06 10:34:55 +00:00
* The Page class extends from Puppeteer ' s { @link EventEmitter } class and will
2020-06-19 14:39:03 +00:00
* emit various events which are documented in the { @link PageEmittedEvents } enum .
2020-06-12 10:10:12 +00:00
*
* @example
* This example logs a message for a single page ` load ` event :
* ` ` ` js
* page . once ( 'load' , ( ) = > console . log ( 'Page loaded!' ) ) ;
* ` ` `
*
* To unsubscribe from events use the ` off ` method :
*
* ` ` ` js
* function logRequest ( interceptedRequest ) {
* console . log ( 'A request was made:' , interceptedRequest . url ( ) ) ;
* }
* page . on ( 'request' , logRequest ) ;
* // Sometime later...
* page . off ( 'request' , logRequest ) ;
* ` ` `
* @public
* /
2020-05-05 12:53:22 +00:00
export class Page extends EventEmitter {
2020-06-12 10:10:12 +00:00
/ * *
* @internal
* /
2020-05-07 10:54:55 +00:00
static async create (
client : CDPSession ,
target : Target ,
ignoreHTTPSErrors : boolean ,
2021-09-23 12:37:35 +00:00
defaultViewport : Viewport | null ,
screenshotTaskQueue : TaskQueue
2020-05-07 10:54:55 +00:00
) : Promise < Page > {
2021-09-23 12:37:35 +00:00
const page = new Page (
client ,
target ,
ignoreHTTPSErrors ,
screenshotTaskQueue
) ;
2019-07-23 04:30:49 +00:00
await page . _initialize ( ) ;
2020-05-07 10:54:55 +00:00
if ( defaultViewport ) await page . setViewport ( defaultViewport ) ;
2017-06-21 20:51:06 +00:00
return page ;
}
2020-06-10 15:15:02 +00:00
private _closed = false ;
private _client : CDPSession ;
private _target : Target ;
private _keyboard : Keyboard ;
private _mouse : Mouse ;
private _timeoutSettings = new TimeoutSettings ( ) ;
private _touchscreen : Touchscreen ;
private _accessibility : Accessibility ;
private _frameManager : FrameManager ;
private _emulationManager : EmulationManager ;
private _tracing : Tracing ;
private _pageBindings = new Map < string , Function > ( ) ;
private _coverage : Coverage ;
private _javascriptEnabled = true ;
private _viewport : Viewport | null ;
2021-09-23 12:37:35 +00:00
private _screenshotTaskQueue : TaskQueue ;
2020-06-10 15:15:02 +00:00
private _workers = new Map < string , WebWorker > ( ) ;
2020-06-26 07:24:56 +00:00
// TODO: improve this typedef - it's a function that takes a file chooser or
// something?
2020-06-10 15:15:02 +00:00
private _fileChooserInterceptors = new Set < Function > ( ) ;
2020-05-05 12:53:22 +00:00
2020-06-10 15:15:02 +00:00
private _disconnectPromise? : Promise < Error > ;
2021-06-04 10:25:36 +00:00
private _userDragInterceptionEnabled = false ;
2021-10-04 06:59:46 +00:00
private _handlerMap = new WeakMap < Handler , Handler > ( ) ;
2020-05-05 12:53:22 +00:00
2020-06-12 10:10:12 +00:00
/ * *
* @internal
* /
2021-09-23 12:37:35 +00:00
constructor (
client : CDPSession ,
target : Target ,
ignoreHTTPSErrors : boolean ,
screenshotTaskQueue : TaskQueue
) {
2017-06-21 20:51:06 +00:00
super ( ) ;
this . _client = client ;
2018-01-11 03:33:22 +00:00
this . _target = target ;
2017-07-30 02:12:17 +00:00
this . _keyboard = new Keyboard ( client ) ;
this . _mouse = new Mouse ( client , this . _keyboard ) ;
2017-09-02 02:03:51 +00:00
this . _touchscreen = new Touchscreen ( client , this . _keyboard ) ;
2018-11-02 01:54:51 +00:00
this . _accessibility = new Accessibility ( client ) ;
2020-05-07 10:54:55 +00:00
this . _frameManager = new FrameManager (
client ,
this ,
ignoreHTTPSErrors ,
this . _timeoutSettings
) ;
2017-07-30 02:12:17 +00:00
this . _emulationManager = new EmulationManager ( client ) ;
2017-08-02 17:45:11 +00:00
this . _tracing = new Tracing ( client ) ;
2018-01-03 03:53:53 +00:00
this . _coverage = new Coverage ( client ) ;
2021-09-23 12:37:35 +00:00
this . _screenshotTaskQueue = screenshotTaskQueue ;
2020-05-05 12:53:22 +00:00
this . _viewport = null ;
2017-06-21 20:51:06 +00:00
2021-10-28 09:25:49 +00:00
client . on (
'Target.attachedToTarget' ,
( event : Protocol.Target.AttachedToTargetEvent ) = > {
if (
event . targetInfo . type !== 'worker' &&
event . targetInfo . type !== 'iframe'
) {
// If we don't detach from service workers, they will never die.
// We still want to attach to workers for emitting events.
// We still want to attach to iframes so sessions may interact with them.
// We detach from all other types out of an abundance of caution.
// See https://source.chromium.org/chromium/chromium/src/+/main:content/browser/devtools/devtools_agent_host_impl.cc?ss=chromium&q=f:devtools%20-f:out%20%22::kTypePage%5B%5D%22
// for the complete list of available types.
client
. send ( 'Target.detachFromTarget' , {
sessionId : event.sessionId ,
} )
. catch ( debugError ) ;
return ;
}
if ( event . targetInfo . type === 'worker' ) {
const session = Connection . fromSession ( client ) . session (
event . sessionId
) ;
const worker = new WebWorker (
session ,
event . targetInfo . url ,
this . _addConsoleMessage . bind ( this ) ,
this . _handleException . bind ( this )
) ;
this . _workers . set ( event . sessionId , worker ) ;
this . emit ( PageEmittedEvents . WorkerCreated , worker ) ;
}
2018-05-21 21:31:11 +00:00
}
2021-10-28 09:25:49 +00:00
) ;
2020-05-07 10:54:55 +00:00
client . on ( 'Target.detachedFromTarget' , ( event ) = > {
2018-05-21 21:31:11 +00:00
const worker = this . _workers . get ( event . sessionId ) ;
2020-05-07 10:54:55 +00:00
if ( ! worker ) return ;
2018-05-21 21:31:11 +00:00
this . _workers . delete ( event . sessionId ) ;
2021-04-12 07:34:06 +00:00
this . emit ( PageEmittedEvents . WorkerDestroyed , worker ) ;
2018-05-21 21:31:11 +00:00
} ) ;
2020-07-08 10:00:11 +00:00
this . _frameManager . on ( FrameManagerEmittedEvents . FrameAttached , ( event ) = >
2020-07-06 10:34:55 +00:00
this . emit ( PageEmittedEvents . FrameAttached , event )
2020-05-07 10:54:55 +00:00
) ;
2020-07-08 10:00:11 +00:00
this . _frameManager . on ( FrameManagerEmittedEvents . FrameDetached , ( event ) = >
2020-07-06 10:34:55 +00:00
this . emit ( PageEmittedEvents . FrameDetached , event )
2020-05-07 10:54:55 +00:00
) ;
2020-07-08 10:00:11 +00:00
this . _frameManager . on ( FrameManagerEmittedEvents . FrameNavigated , ( event ) = >
2020-07-06 10:34:55 +00:00
this . emit ( PageEmittedEvents . FrameNavigated , event )
2020-05-07 10:54:55 +00:00
) ;
2017-06-21 20:51:06 +00:00
2019-04-10 04:42:42 +00:00
const networkManager = this . _frameManager . networkManager ( ) ;
2020-07-07 15:43:55 +00:00
networkManager . on ( NetworkManagerEmittedEvents . Request , ( event ) = >
2020-07-06 10:34:55 +00:00
this . emit ( PageEmittedEvents . Request , event )
2020-05-07 10:54:55 +00:00
) ;
2021-03-17 14:42:35 +00:00
networkManager . on (
NetworkManagerEmittedEvents . RequestServedFromCache ,
( event ) = > this . emit ( PageEmittedEvents . RequestServedFromCache , event )
) ;
2020-07-07 15:43:55 +00:00
networkManager . on ( NetworkManagerEmittedEvents . Response , ( event ) = >
2020-07-06 10:34:55 +00:00
this . emit ( PageEmittedEvents . Response , event )
2020-05-07 10:54:55 +00:00
) ;
2020-07-07 15:43:55 +00:00
networkManager . on ( NetworkManagerEmittedEvents . RequestFailed , ( event ) = >
2020-07-06 10:34:55 +00:00
this . emit ( PageEmittedEvents . RequestFailed , event )
2020-05-07 10:54:55 +00:00
) ;
2020-07-07 15:43:55 +00:00
networkManager . on ( NetworkManagerEmittedEvents . RequestFinished , ( event ) = >
2020-07-06 10:34:55 +00:00
this . emit ( PageEmittedEvents . RequestFinished , event )
2020-05-07 10:54:55 +00:00
) ;
2019-07-23 04:30:49 +00:00
this . _fileChooserInterceptors = new Set ( ) ;
2017-06-29 06:09:28 +00:00
2020-05-07 10:54:55 +00:00
client . on ( 'Page.domContentEventFired' , ( ) = >
2020-07-06 10:34:55 +00:00
this . emit ( PageEmittedEvents . DOMContentLoaded )
2020-05-07 10:54:55 +00:00
) ;
2020-07-06 10:34:55 +00:00
client . on ( 'Page.loadEventFired' , ( ) = > this . emit ( PageEmittedEvents . Load ) ) ;
2020-05-07 10:54:55 +00:00
client . on ( 'Runtime.consoleAPICalled' , ( event ) = > this . _onConsoleAPI ( event ) ) ;
client . on ( 'Runtime.bindingCalled' , ( event ) = > this . _onBindingCalled ( event ) ) ;
client . on ( 'Page.javascriptDialogOpening' , ( event ) = > this . _onDialog ( event ) ) ;
client . on ( 'Runtime.exceptionThrown' , ( exception ) = >
this . _handleException ( exception . exceptionDetails )
) ;
2020-05-05 12:53:22 +00:00
client . on ( 'Inspector.targetCrashed' , ( ) = > this . _onTargetCrashed ( ) ) ;
2020-05-07 10:54:55 +00:00
client . on ( 'Performance.metrics' , ( event ) = > this . _emitMetrics ( event ) ) ;
client . on ( 'Log.entryAdded' , ( event ) = > this . _onLogEntryAdded ( event ) ) ;
client . on ( 'Page.fileChooserOpened' , ( event ) = > this . _onFileChooser ( event ) ) ;
2018-05-25 23:53:57 +00:00
this . _target . _isClosedPromise . then ( ( ) = > {
2020-07-06 10:34:55 +00:00
this . emit ( PageEmittedEvents . Close ) ;
2018-05-25 23:53:57 +00:00
this . _closed = true ;
} ) ;
2017-08-15 18:13:05 +00:00
}
2020-06-10 15:15:02 +00:00
private async _initialize ( ) : Promise < void > {
2019-07-23 04:30:49 +00:00
await Promise . all ( [
this . _frameManager . initialize ( ) ,
2020-05-07 10:54:55 +00:00
this . _client . send ( 'Target.setAutoAttach' , {
autoAttach : true ,
waitForDebuggerOnStart : false ,
flatten : true ,
} ) ,
2020-07-10 10:51:52 +00:00
this . _client . send ( 'Performance.enable' ) ,
this . _client . send ( 'Log.enable' ) ,
2019-07-23 04:30:49 +00:00
] ) ;
}
2020-06-10 15:15:02 +00:00
private async _onFileChooser (
2020-07-10 10:51:52 +00:00
event : Protocol.Page.FileChooserOpenedEvent
2020-05-07 10:54:55 +00:00
) : Promise < void > {
if ( ! this . _fileChooserInterceptors . size ) return ;
2020-01-27 13:44:53 +00:00
const frame = this . _frameManager . frame ( event . frameId ) ;
const context = await frame . executionContext ( ) ;
const element = await context . _adoptBackendNodeId ( event . backendNodeId ) ;
2019-07-23 04:30:49 +00:00
const interceptors = Array . from ( this . _fileChooserInterceptors ) ;
this . _fileChooserInterceptors . clear ( ) ;
2020-05-13 10:30:29 +00:00
const fileChooser = new FileChooser ( element , event ) ;
2020-05-07 10:54:55 +00:00
for ( const interceptor of interceptors ) interceptor . call ( null , fileChooser ) ;
2019-07-23 04:30:49 +00:00
}
2021-07-13 09:37:39 +00:00
/ * *
* @returns ` true ` if drag events are being intercepted , ` false ` otherwise .
* /
isDragInterceptionEnabled ( ) : boolean {
return this . _userDragInterceptionEnabled ;
}
2020-06-26 07:24:56 +00:00
/ * *
* @returns ` true ` if the page has JavaScript enabled , ` false ` otherwise .
* /
2020-06-10 15:15:02 +00:00
public isJavaScriptEnabled ( ) : boolean {
return this . _javascriptEnabled ;
}
2021-03-25 11:26:35 +00:00
/ * *
* Listen to page events .
* /
2021-07-02 17:58:32 +00:00
// Note: this method exists to define event typings and handle
// proper wireup of cooperative request interception. Actual event listening and
// dispatching is delegated to EventEmitter.
2021-03-25 11:26:35 +00:00
public on < K extends keyof PageEventObject > (
eventName : K ,
handler : ( event : PageEventObject [ K ] ) = > void
) : EventEmitter {
2021-07-02 17:58:32 +00:00
if ( eventName === 'request' ) {
2022-05-30 20:34:08 +00:00
const wrap =
this . _handlerMap . get ( handler ) ||
( ( event : HTTPRequest ) = > {
event . enqueueInterceptAction ( ( ) = >
handler ( event as PageEventObject [ K ] )
) ;
} ) ;
2021-10-04 06:59:46 +00:00
this . _handlerMap . set ( handler , wrap ) ;
return super . on ( eventName , wrap ) ;
2021-07-02 17:58:32 +00:00
}
2021-03-25 11:26:35 +00:00
return super . on ( eventName , handler ) ;
}
public once < K extends keyof PageEventObject > (
eventName : K ,
handler : ( event : PageEventObject [ K ] ) = > void
) : EventEmitter {
// Note: this method only exists to define the types; we delegate the impl
// to EventEmitter.
return super . once ( eventName , handler ) ;
}
2021-10-04 06:59:46 +00:00
off < K extends keyof PageEventObject > (
eventName : K ,
handler : ( event : PageEventObject [ K ] ) = > void
) : EventEmitter {
if ( eventName === 'request' ) {
handler = this . _handlerMap . get ( handler ) || handler ;
}
return super . off ( eventName , handler ) ;
}
2020-06-26 07:24:56 +00:00
/ * *
2021-06-25 12:40:53 +00:00
* This method is typically coupled with an action that triggers file
* choosing . The following example clicks a button that issues a file chooser
* and then responds with ` /tmp/myfile.pdf ` as if a user has selected this file .
*
* ` ` ` js
* const [ fileChooser ] = await Promise . all ( [
* page . waitForFileChooser ( ) ,
* page . click ( '#upload-file-button' ) ,
* // some button that triggers file selection
* ] ) ;
* await fileChooser . accept ( [ '/tmp/myfile.pdf' ] ) ;
* ` ` `
*
* NOTE : This must be called before the file chooser is launched . It will not
* return a currently active file chooser .
2020-06-26 07:24:56 +00:00
* @param options - Optional waiting parameters
* @returns Resolves after a page requests a file picker .
2021-06-25 12:40:53 +00:00
* @remarks
* NOTE : In non - headless Chromium , this method results in the native file picker
* dialog ` not showing up ` for the user .
2020-06-26 07:24:56 +00:00
* /
2020-05-07 10:54:55 +00:00
async waitForFileChooser (
2020-06-26 07:24:56 +00:00
options : WaitTimeoutOptions = { }
2020-05-07 10:54:55 +00:00
) : Promise < FileChooser > {
2020-01-31 07:43:34 +00:00
if ( ! this . _fileChooserInterceptors . size )
2020-05-07 10:54:55 +00:00
await this . _client . send ( 'Page.setInterceptFileChooserDialog' , {
enabled : true ,
} ) ;
2020-01-31 07:43:34 +00:00
2020-05-07 10:54:55 +00:00
const { timeout = this . _timeoutSettings . timeout ( ) } = options ;
2021-06-24 08:30:21 +00:00
let callback : ( value : FileChooser | PromiseLike < FileChooser > ) = > void ;
2020-05-07 10:54:55 +00:00
const promise = new Promise < FileChooser > ( ( x ) = > ( callback = x ) ) ;
2019-07-23 04:30:49 +00:00
this . _fileChooserInterceptors . add ( callback ) ;
2020-05-07 10:54:55 +00:00
return helper
. waitWithTimeout < FileChooser > (
promise ,
'waiting for file chooser' ,
timeout
)
. catch ( ( error ) = > {
this . _fileChooserInterceptors . delete ( callback ) ;
throw error ;
} ) ;
2019-07-23 04:30:49 +00:00
}
2020-06-26 07:24:56 +00:00
/ * *
* Sets the page ' s geolocation .
* @remarks
2021-06-23 08:23:36 +00:00
* NOTE : Consider using { @link BrowserContext . overridePermissions } to grant
2020-06-26 07:24:56 +00:00
* permissions for the page to read its geolocation .
* @example
* ` ` ` js
* await page . setGeolocation ( { latitude : 59.95 , longitude : 30.31667 } ) ;
* ` ` `
* /
async setGeolocation ( options : GeolocationOptions ) : Promise < void > {
2020-05-07 10:54:55 +00:00
const { longitude , latitude , accuracy = 0 } = options ;
2018-08-31 17:04:12 +00:00
if ( longitude < - 180 || longitude > 180 )
2020-05-07 10:54:55 +00:00
throw new Error (
` Invalid longitude " ${ longitude } ": precondition -180 <= LONGITUDE <= 180 failed. `
) ;
2018-08-31 17:04:12 +00:00
if ( latitude < - 90 || latitude > 90 )
2020-05-07 10:54:55 +00:00
throw new Error (
` Invalid latitude " ${ latitude } ": precondition -90 <= LATITUDE <= 90 failed. `
) ;
2018-08-31 17:04:12 +00:00
if ( accuracy < 0 )
2020-05-07 10:54:55 +00:00
throw new Error (
` Invalid accuracy " ${ accuracy } ": precondition 0 <= ACCURACY failed. `
) ;
await this . _client . send ( 'Emulation.setGeolocationOverride' , {
longitude ,
latitude ,
accuracy ,
} ) ;
2018-08-31 17:04:12 +00:00
}
2020-06-26 07:24:56 +00:00
/ * *
* @returns A target this page was created from .
* /
2020-05-05 12:53:22 +00:00
target ( ) : Target {
2018-01-11 03:33:22 +00:00
return this . _target ;
}
2021-09-21 07:39:47 +00:00
/ * *
* Get the CDP session client the page belongs to .
2021-09-21 13:20:02 +00:00
* @internal
2021-09-21 07:39:47 +00:00
* /
client ( ) : CDPSession {
return this . _client ;
}
2020-06-26 07:24:56 +00:00
/ * *
2021-06-02 12:47:20 +00:00
* Get the browser the page belongs to .
2020-06-26 07:24:56 +00:00
* /
2020-05-05 12:53:22 +00:00
browser ( ) : Browser {
2018-04-17 17:37:17 +00:00
return this . _target . browser ( ) ;
}
2020-06-26 07:24:56 +00:00
/ * *
2021-06-02 12:47:20 +00:00
* Get the browser context that the page belongs to .
2020-06-26 07:24:56 +00:00
* /
2020-05-05 12:53:22 +00:00
browserContext ( ) : BrowserContext {
2018-12-12 23:08:31 +00:00
return this . _target . browserContext ( ) ;
}
2020-06-12 10:10:12 +00:00
private _onTargetCrashed ( ) : void {
2017-08-15 18:13:05 +00:00
this . emit ( 'error' , new Error ( 'Page crashed!' ) ) ;
2017-06-21 20:51:06 +00:00
}
2020-07-10 10:51:52 +00:00
private _onLogEntryAdded ( event : Protocol.Log.EntryAddedEvent ) : void {
2020-05-07 10:54:55 +00:00
const { level , text , args , source , url , lineNumber } = event . entry ;
if ( args ) args . map ( ( arg ) = > helper . releaseObject ( this . _client , arg ) ) ;
2018-06-07 18:21:35 +00:00
if ( source !== 'worker' )
2020-05-07 10:54:55 +00:00
this . emit (
2020-07-06 10:34:55 +00:00
PageEmittedEvents . Console ,
2020-09-25 13:27:13 +00:00
new ConsoleMessage ( level , text , [ ] , [ { url , lineNumber } ] )
2020-05-07 10:54:55 +00:00
) ;
2018-04-28 04:40:09 +00:00
}
2020-06-26 07:24:56 +00:00
/ * *
* @returns The page ' s main frame .
2021-06-23 08:23:36 +00:00
* @remarks
* Page is guaranteed to have a main frame which persists during navigations .
2020-06-26 07:24:56 +00:00
* /
2020-05-05 12:53:22 +00:00
mainFrame ( ) : Frame {
2017-06-21 20:51:06 +00:00
return this . _frameManager . mainFrame ( ) ;
}
2017-06-21 20:36:04 +00:00
2020-05-05 12:53:22 +00:00
get keyboard ( ) : Keyboard {
2017-07-18 01:49:52 +00:00
return this . _keyboard ;
}
2020-05-05 12:53:22 +00:00
get touchscreen ( ) : Touchscreen {
2017-09-02 02:03:51 +00:00
return this . _touchscreen ;
}
2020-05-05 12:53:22 +00:00
get coverage ( ) : Coverage {
2018-01-03 03:53:53 +00:00
return this . _coverage ;
}
2020-05-05 12:53:22 +00:00
get tracing ( ) : Tracing {
2017-08-02 17:45:11 +00:00
return this . _tracing ;
}
2020-05-05 12:53:22 +00:00
get accessibility ( ) : Accessibility {
2018-11-02 01:54:51 +00:00
return this . _accessibility ;
}
2020-06-26 07:24:56 +00:00
/ * *
* @returns An array of all frames attached to the page .
* /
2020-05-05 12:53:22 +00:00
frames ( ) : Frame [ ] {
2017-06-21 20:51:06 +00:00
return this . _frameManager . frames ( ) ;
}
2017-06-21 20:36:04 +00:00
2020-06-26 07:24:56 +00:00
/ * *
* @returns all of the dedicated
2021-06-25 12:40:53 +00:00
* { @link https : //developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API |
* WebWorkers }
2020-06-26 07:24:56 +00:00
* associated with the page .
2021-06-25 12:40:53 +00:00
* @remarks
* NOTE : This does not contain ServiceWorkers
2020-06-26 07:24:56 +00:00
* /
2020-05-29 11:57:54 +00:00
workers ( ) : WebWorker [ ] {
2018-05-21 21:31:11 +00:00
return Array . from ( this . _workers . values ( ) ) ;
}
2020-06-26 07:24:56 +00:00
/ * *
* @param value - Whether to enable request interception .
*
* @remarks
* Activating request interception enables { @link HTTPRequest . abort } ,
* { @link HTTPRequest . continue } and { @link HTTPRequest . respond } methods . This
* provides the capability to modify network requests that are made by a page .
*
* Once request interception is enabled , every request will stall unless it ' s
2021-03-17 14:42:35 +00:00
* continued , responded or aborted ; or completed using the browser cache .
2020-06-26 07:24:56 +00:00
*
* @example
* An example of a naïve request interceptor that aborts all image requests :
* ` ` ` js
* const puppeteer = require ( 'puppeteer' ) ;
* ( async ( ) = > {
* const browser = await puppeteer . launch ( ) ;
* const page = await browser . newPage ( ) ;
* await page . setRequestInterception ( true ) ;
* page . on ( 'request' , interceptedRequest = > {
* if ( interceptedRequest . url ( ) . endsWith ( '.png' ) ||
* interceptedRequest . url ( ) . endsWith ( '.jpg' ) )
* interceptedRequest . abort ( ) ;
* else
* interceptedRequest . continue ( ) ;
* } ) ;
* await page . goto ( 'https://example.com' ) ;
* await browser . close ( ) ;
* } ) ( ) ;
* ` ` `
2021-06-23 08:23:36 +00:00
* NOTE : Enabling request interception disables page caching .
2020-06-26 07:24:56 +00:00
* /
2021-05-20 12:09:56 +00:00
async setRequestInterception ( value : boolean ) : Promise < void > {
return this . _frameManager . networkManager ( ) . setRequestInterception ( value ) ;
2017-06-21 20:51:06 +00:00
}
2017-06-15 07:20:37 +00:00
2021-06-04 10:25:36 +00:00
/ * *
* @param enabled - Whether to enable drag interception .
*
* @remarks
2021-07-09 08:12:01 +00:00
* Activating drag interception enables the ` Input.drag ` ,
2021-06-04 10:25:36 +00:00
* methods This provides the capability to capture drag events emitted
* on the page , which can then be used to simulate drag - and - drop .
* /
async setDragInterception ( enabled : boolean ) : Promise < void > {
this . _userDragInterceptionEnabled = enabled ;
return this . _client . send ( 'Input.setInterceptDrags' , { enabled } ) ;
}
2020-06-26 07:24:56 +00:00
/ * *
* @param enabled - When ` true ` , enables offline mode for the page .
2021-07-30 08:41:42 +00:00
* @remarks
* NOTE : while this method sets the network connection to offline , it does
* not change the parameters used in [ page . emulateNetworkConditions ( networkConditions ) ]
* ( # pageemulatenetworkconditionsnetworkconditions )
2020-06-26 07:24:56 +00:00
* /
2020-05-05 12:53:22 +00:00
setOfflineMode ( enabled : boolean ) : Promise < void > {
2019-04-10 04:42:42 +00:00
return this . _frameManager . networkManager ( ) . setOfflineMode ( enabled ) ;
2017-10-13 21:41:39 +00:00
}
2021-06-23 08:23:36 +00:00
/ * *
* @param networkConditions - Passing ` null ` disables network condition emulation .
* @example
* ` ` ` js
* const puppeteer = require ( 'puppeteer' ) ;
* const slow3G = puppeteer . networkConditions [ 'Slow 3G' ] ;
*
* ( async ( ) = > {
* const browser = await puppeteer . launch ( ) ;
* const page = await browser . newPage ( ) ;
* await page . emulateNetworkConditions ( slow3G ) ;
* await page . goto ( 'https://www.google.com' ) ;
* // other actions...
* await browser . close ( ) ;
* } ) ( ) ;
* ` ` `
* @remarks
* NOTE : This does not affect WebSockets and WebRTC PeerConnections ( see
2021-07-30 08:41:42 +00:00
* https : //crbug.com/563644). To set the page offline, you can use
* [ page . setOfflineMode ( enabled ) ] ( # pagesetofflinemodeenabled ) .
2021-06-23 08:23:36 +00:00
* /
2021-01-21 09:00:57 +00:00
emulateNetworkConditions (
networkConditions : NetworkConditions | null
) : Promise < void > {
return this . _frameManager
. networkManager ( )
. emulateNetworkConditions ( networkConditions ) ;
}
2020-06-26 07:24:56 +00:00
/ * *
2021-06-23 08:23:36 +00:00
* This setting will change the default maximum navigation time for the
* following methods and related shortcuts :
*
* - { @link Page . goBack | page . goBack ( options ) }
*
* - { @link Page . goForward | page . goForward ( options ) }
*
* - { @link Page . goto | page . goto ( url , options ) }
*
* - { @link Page . reload | page . reload ( options ) }
*
* - { @link Page . setContent | page . setContent ( html , options ) }
*
* - { @link Page . waitForNavigation | page . waitForNavigation ( options ) }
2020-06-26 07:24:56 +00:00
* @param timeout - Maximum navigation time in milliseconds .
* /
2020-05-05 12:53:22 +00:00
setDefaultNavigationTimeout ( timeout : number ) : void {
2019-01-29 01:16:12 +00:00
this . _timeoutSettings . setDefaultNavigationTimeout ( timeout ) ;
}
2020-06-26 07:24:56 +00:00
/ * *
* @param timeout - Maximum time in milliseconds .
* /
2020-05-05 12:53:22 +00:00
setDefaultTimeout ( timeout : number ) : void {
2019-01-29 01:16:12 +00:00
this . _timeoutSettings . setDefaultTimeout ( timeout ) ;
2018-01-10 21:04:01 +00:00
}
2020-06-26 07:24:56 +00:00
/ * *
* Runs ` document.querySelector ` within the page . If no element matches the
* selector , the return value resolves to ` null ` .
*
* @remarks
* Shortcut for { @link Frame . $ | Page . mainFrame ( ) . $ ( selector ) } .
*
2021-05-26 14:37:38 +00:00
* @param selector - A ` selector ` to query page for
2020-06-26 07:24:56 +00:00
* { @link https : //developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | selector}
* to query page for .
* /
2021-03-25 11:40:34 +00:00
async $ < T extends Element = Element > (
selector : string
) : Promise < ElementHandle < T > | null > {
return this . mainFrame ( ) . $ < T > ( selector ) ;
2017-08-15 21:54:02 +00:00
}
2017-10-06 22:35:02 +00:00
2020-07-01 11:44:08 +00:00
/ * *
* @remarks
*
* The only difference between { @link Page . evaluate | page . evaluate } and
* ` page.evaluateHandle ` is that ` evaluateHandle ` will return the value
* wrapped in an in - page object .
*
* If the function passed to ` page.evaluteHandle ` returns a Promise , the
* function will wait for the promise to resolve and return its value .
*
* You can pass a string instead of a function ( although functions are
* recommended as they are easier to debug and use with TypeScript ) :
*
* @example
* ` ` `
* const aHandle = await page . evaluateHandle ( 'document' )
* ` ` `
*
* @example
* { @link JSHandle } instances can be passed as arguments to the ` pageFunction ` :
* ` ` `
* const aHandle = await page . evaluateHandle ( ( ) = > document . body ) ;
* const resultHandle = await page . evaluateHandle ( body = > body . innerHTML , aHandle ) ;
* console . log ( await resultHandle . jsonValue ( ) ) ;
* await resultHandle . dispose ( ) ;
* ` ` `
*
* Most of the time this function returns a { @link JSHandle } ,
* but if ` pageFunction ` returns a reference to an element ,
* you instead get an { @link ElementHandle } back :
*
* @example
* ` ` `
* const button = await page . evaluateHandle ( ( ) = > document . querySelector ( 'button' ) ) ;
* // can call `click` because `button` is an `ElementHandle`
* await button . click ( ) ;
* ` ` `
*
* The TypeScript definitions assume that ` evaluateHandle ` returns
* a ` JSHandle ` , but if you know it ' s going to return an
* ` ElementHandle ` , pass it as the generic argument :
*
* ` ` `
* const button = await page . evaluateHandle < ElementHandle > ( . . . ) ;
* ` ` `
*
* @param pageFunction - a function that is run within the page
* @param args - arguments to be passed to the pageFunction
* /
async evaluateHandle < HandlerType extends JSHandle = JSHandle > (
pageFunction : EvaluateHandleFn ,
. . . args : SerializableOrJSHandle [ ]
) : Promise < HandlerType > {
2017-11-19 00:27:52 +00:00
const context = await this . mainFrame ( ) . executionContext ( ) ;
2020-07-01 11:44:08 +00:00
return context . evaluateHandle < HandlerType > ( pageFunction , . . . args ) ;
2017-10-06 22:35:02 +00:00
}
2017-08-15 21:54:02 +00:00
2020-07-17 12:58:56 +00:00
/ * *
* This method iterates the JavaScript heap and finds all objects with the
* given prototype .
*
* @remarks
2021-06-23 08:23:36 +00:00
* Shortcut for
* { @link ExecutionContext . queryObjects |
* page . mainFrame ( ) . executionContext ( ) . queryObjects ( prototypeHandle ) } .
2020-07-17 12:58:56 +00:00
*
* @example
*
* ` ` ` js
* // Create a Map object
* await page . evaluate ( ( ) = > window . map = new Map ( ) ) ;
* // Get a handle to the Map object prototype
* const mapPrototype = await page . evaluateHandle ( ( ) = > Map . prototype ) ;
* // Query all map instances into an array
* const mapInstances = await page . queryObjects ( mapPrototype ) ;
* // Count amount of map objects in heap
* const count = await page . evaluate ( maps = > maps . length , mapInstances ) ;
* await mapInstances . dispose ( ) ;
* await mapPrototype . dispose ( ) ;
* ` ` `
* @param prototypeHandle - a handle to the object prototype .
2021-06-23 08:23:36 +00:00
* @returns Promise which resolves to a handle to an array of objects with
* this prototype .
2020-07-17 12:58:56 +00:00
* /
2020-05-05 12:53:22 +00:00
async queryObjects ( prototypeHandle : JSHandle ) : Promise < JSHandle > {
2017-11-19 00:27:52 +00:00
const context = await this . mainFrame ( ) . executionContext ( ) ;
return context . queryObjects ( prototypeHandle ) ;
2017-10-11 21:41:20 +00:00
}
2020-07-02 09:09:34 +00:00
/ * *
* This method runs ` document.querySelector ` within the page and passes the
* result as the first argument to the ` pageFunction ` .
*
* @remarks
*
* If no element is found matching ` selector ` , the method will throw an error .
*
* If ` pageFunction ` returns a promise ` $ eval ` will wait for the promise to
* resolve and then return its value .
*
* @example
*
* ` ` `
* const searchValue = await page . $eval ( '#search' , el = > el . value ) ;
* const preloadHref = await page . $eval ( 'link[rel=preload]' , el = > el . href ) ;
* const html = await page . $eval ( '.main-container' , el = > el . outerHTML ) ;
* ` ` `
*
* If you are using TypeScript , you may have to provide an explicit type to the
* first argument of the ` pageFunction ` .
* By default it is typed as ` Element ` , but you may need to provide a more
* specific sub - type :
*
* @example
*
* ` ` `
* // if you don't provide HTMLInputElement here, TS will error
* // as `value` is not on `Element`
* const searchValue = await page . $eval ( '#search' , ( el : HTMLInputElement ) = > el . value ) ;
* ` ` `
*
* The compiler should be able to infer the return type
* from the ` pageFunction ` you provide . If it is unable to , you can use the generic
* type to tell the compiler what return type you expect from ` $ eval ` :
*
* @example
*
* ` ` `
* // The compiler can infer the return type in this case, but if it can't
* // or if you want to be more explicit, provide it as the generic type.
* const searchValue = await page . $eval < string > (
* '#search' , ( el : HTMLInputElement ) = > el . value
* ) ;
* ` ` `
*
2020-07-03 12:12:59 +00:00
* @param selector - the
2020-07-02 09:09:34 +00:00
* { @link https : //developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | selector}
* to query for
2020-07-03 12:12:59 +00:00
* @param pageFunction - the function to be evaluated in the page context .
* Will be passed the result of ` document.querySelector(selector) ` as its
* first argument .
* @param args - any additional arguments to pass through to ` pageFunction ` .
2020-07-02 09:09:34 +00:00
*
* @returns The result of calling ` pageFunction ` . If it returns an element it
* is wrapped in an { @link ElementHandle } , else the raw value itself is
* returned .
* /
async $eval < ReturnType > (
2020-05-07 10:54:55 +00:00
selector : string ,
2020-07-02 09:09:34 +00:00
pageFunction : (
element : Element ,
/ * U n f o r t u n a t e l y t h i s h a s t o b e u n k n o w n [ ] b e c a u s e i t ' s h a r d t o g e t
* TypeScript to understand that the arguments will be left alone unless
* they are an ElementHandle , in which case they will be unwrapped .
* The nice thing about unknown vs any is that unknown will force the user
* to type the item before using it to avoid errors .
*
* TODO ( @jackfranklin ) : We could fix this by using overloads like
* DefinitelyTyped does :
2021-09-14 15:02:39 +00:00
* https : //github.com/DefinitelyTyped/DefinitelyTyped/blob/HEAD/types/puppeteer/index.d.ts#L114
2020-07-02 09:09:34 +00:00
* /
. . . args : unknown [ ]
) = > ReturnType | Promise < ReturnType > ,
2020-06-25 12:38:01 +00:00
. . . args : SerializableOrJSHandle [ ]
2020-07-02 09:09:34 +00:00
) : Promise < WrapElementHandle < ReturnType > > {
2020-05-05 12:53:22 +00:00
return this . mainFrame ( ) . $eval < ReturnType > ( selector , pageFunction , . . . args ) ;
2017-08-31 22:38:01 +00:00
}
2020-07-03 12:12:59 +00:00
/ * *
* This method runs ` Array.from(document.querySelectorAll(selector)) ` within
* the page and passes the result as the first argument to the ` pageFunction ` .
*
* @remarks
*
* If ` pageFunction ` returns a promise ` $ $ eval ` will wait for the promise to
* resolve and then return its value .
*
* @example
2020-07-03 14:23:51 +00:00
*
* ` ` `
* // get the amount of divs on the page
2020-07-03 12:12:59 +00:00
* const divCount = await page . $ $eval ( 'div' , divs = > divs . length ) ;
2020-07-03 14:23:51 +00:00
*
* // get the text content of all the `.options` elements:
* const options = await page . $ $eval ( 'div > span.options' , options = > {
* return options . map ( option = > option . textContent )
* } ) ;
2020-07-03 12:12:59 +00:00
* ` ` `
*
2020-07-03 14:23:51 +00:00
* If you are using TypeScript , you may have to provide an explicit type to the
* first argument of the ` pageFunction ` .
* By default it is typed as ` Element[] ` , but you may need to provide a more
* specific sub - type :
*
2020-07-03 12:12:59 +00:00
* @example
2020-07-03 14:23:51 +00:00
*
* ` ` `
* // if you don't provide HTMLInputElement here, TS will error
* // as `value` is not on `Element`
* await page . $ $eval ( 'input' , ( elements : HTMLInputElement [ ] ) = > {
* return elements . map ( e = > e . value ) ;
* } ) ;
2020-07-03 12:12:59 +00:00
* ` ` `
*
2020-07-03 14:23:51 +00:00
* The compiler should be able to infer the return type
* from the ` pageFunction ` you provide . If it is unable to , you can use the generic
* type to tell the compiler what return type you expect from ` $ $ eval ` :
*
* @example
*
* ` ` `
* // The compiler can infer the return type in this case, but if it can't
* // or if you want to be more explicit, provide it as the generic type.
* const allInputValues = await page . $ $eval < string [ ] > (
* 'input' , ( elements : HTMLInputElement [ ] ) = > elements . map ( e = > e . textContent )
* ) ;
* ` ` `
*
2021-04-06 08:58:01 +00:00
* @param selector - the
2020-07-03 12:12:59 +00:00
* { @link https : //developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | selector}
* to query for
2021-04-06 08:58:01 +00:00
* @param pageFunction - the function to be evaluated in the page context . Will
2020-07-03 14:23:51 +00:00
* be passed the result of ` Array.from(document.querySelectorAll(selector)) `
* as its first argument .
2021-04-06 08:58:01 +00:00
* @param args - any additional arguments to pass through to ` pageFunction ` .
2020-07-03 12:12:59 +00:00
*
2020-07-03 14:23:51 +00:00
* @returns The result of calling ` pageFunction ` . If it returns an element it
* is wrapped in an { @link ElementHandle } , else the raw value itself is
* returned .
2020-07-03 12:12:59 +00:00
* /
2020-07-03 14:23:51 +00:00
async $ $eval < ReturnType > (
2020-05-07 10:54:55 +00:00
selector : string ,
2020-07-03 14:23:51 +00:00
pageFunction : (
elements : Element [ ] ,
/ * T h e s e h a v e t o b e t y p e d a s u n k n o w n [ ] f o r t h e s a m e r e a s o n a s t h e $ e v a l
* definition above , please see that comment for more details and the TODO
* that will improve things .
* /
. . . args : unknown [ ]
) = > ReturnType | Promise < ReturnType > ,
2020-06-25 12:38:01 +00:00
. . . args : SerializableOrJSHandle [ ]
2020-07-03 14:23:51 +00:00
) : Promise < WrapElementHandle < ReturnType > > {
2020-05-05 12:53:22 +00:00
return this . mainFrame ( ) . $ $eval < ReturnType > ( selector , pageFunction , . . . args ) ;
2017-10-11 06:23:14 +00:00
}
2021-06-02 12:47:20 +00:00
/ * *
* The method runs ` document.querySelectorAll ` within the page . If no elements
* match the selector , the return value resolves to ` [] ` .
* @remarks
* Shortcut for { @link Frame . $ $ | Page . mainFrame ( ) . $ $ ( selector ) } .
* @param selector - A ` selector ` to query page for
* /
2021-03-25 11:40:34 +00:00
async $ $ < T extends Element = Element > (
selector : string
) : Promise < Array < ElementHandle < T > >> {
return this . mainFrame ( ) . $ $ < T > ( selector ) ;
2017-08-23 05:56:55 +00:00
}
2021-06-02 12:47:20 +00:00
/ * *
* The method evaluates the XPath expression relative to the page document as
* its context node . If there are no such elements , the method resolves to an
* empty array .
* @remarks
* Shortcut for { @link Frame . $x | Page . mainFrame ( ) . $x ( expression ) } .
* @param expression - Expression to evaluate
* /
2020-05-05 12:53:22 +00:00
async $x ( expression : string ) : Promise < ElementHandle [ ] > {
2018-01-03 23:37:08 +00:00
return this . mainFrame ( ) . $x ( expression ) ;
2017-12-20 00:23:45 +00:00
}
2020-07-03 12:12:59 +00:00
/ * *
* If no URLs are specified , this method returns cookies for the current page
* URL . If URLs are specified , only cookies for those URLs are returned .
* /
2020-05-05 12:53:22 +00:00
async cookies ( . . . urls : string [ ] ) : Promise < Protocol.Network.Cookie [ ] > {
2020-05-07 10:54:55 +00:00
const originalCookies = (
await this . _client . send ( 'Network.getCookies' , {
urls : urls.length ? urls : [ this . url ( ) ] ,
} )
) . cookies ;
2020-04-16 07:20:27 +00:00
const unsupportedCookieAttributes = [ 'priority' ] ;
2020-05-07 10:54:55 +00:00
const filterUnsupportedAttributes = (
cookie : Protocol.Network.Cookie
) : Protocol . Network . Cookie = > {
for ( const attr of unsupportedCookieAttributes ) delete cookie [ attr ] ;
2020-04-16 07:20:27 +00:00
return cookie ;
} ;
return originalCookies . map ( filterUnsupportedAttributes ) ;
2017-08-24 19:21:46 +00:00
}
2020-05-07 10:54:55 +00:00
async deleteCookie (
2020-07-10 10:51:52 +00:00
. . . cookies : Protocol.Network.DeleteCookiesRequest [ ]
2020-05-07 10:54:55 +00:00
) : Promise < void > {
2017-08-24 19:21:46 +00:00
const pageURL = this . url ( ) ;
for ( const cookie of cookies ) {
const item = Object . assign ( { } , cookie ) ;
2020-05-07 10:54:55 +00:00
if ( ! cookie . url && pageURL . startsWith ( 'http' ) ) item . url = pageURL ;
2017-08-24 19:21:46 +00:00
await this . _client . send ( 'Network.deleteCookies' , item ) ;
}
}
2021-06-23 08:23:36 +00:00
/ * *
* @example
* ` ` ` js
* await page . setCookie ( cookieObject1 , cookieObject2 ) ;
* ` ` `
* /
2020-05-05 12:53:22 +00:00
async setCookie ( . . . cookies : Protocol.Network.CookieParam [ ] ) : Promise < void > {
2017-12-16 09:17:20 +00:00
const pageURL = this . url ( ) ;
const startsWithHTTP = pageURL . startsWith ( 'http' ) ;
2020-05-07 10:54:55 +00:00
const items = cookies . map ( ( cookie ) = > {
2017-08-24 19:21:46 +00:00
const item = Object . assign ( { } , cookie ) ;
2020-05-07 10:54:55 +00:00
if ( ! item . url && startsWithHTTP ) item . url = pageURL ;
assert (
item . url !== 'about:blank' ,
` Blank page can not have cookie " ${ item . name } " `
) ;
assert (
! String . prototype . startsWith . call ( item . url || '' , 'data:' ) ,
` Data URL page can not have cookie " ${ item . name } " `
) ;
2017-08-24 19:21:46 +00:00
return item ;
} ) ;
await this . deleteCookie ( . . . items ) ;
if ( items . length )
2020-05-07 10:54:55 +00:00
await this . _client . send ( 'Network.setCookies' , { cookies : items } ) ;
2017-08-24 19:21:46 +00:00
}
2021-06-02 12:47:20 +00:00
/ * *
* Adds a ` <script> ` tag into the page with the desired URL or content .
* @remarks
* Shortcut for { @link Frame . addScriptTag | page . mainFrame ( ) . addScriptTag ( options ) } .
* @returns Promise which resolves to the added tag when the script ' s onload fires or
* when the script content was injected into frame .
* /
2020-05-07 10:54:55 +00:00
async addScriptTag ( options : {
url? : string ;
path? : string ;
content? : string ;
type ? : string ;
2021-09-14 21:02:05 +00:00
id? : string ;
2020-05-07 10:54:55 +00:00
} ) : Promise < ElementHandle > {
2017-10-12 08:26:44 +00:00
return this . mainFrame ( ) . addScriptTag ( options ) ;
2017-06-21 20:51:06 +00:00
}
2017-06-21 20:36:04 +00:00
2021-06-02 12:47:20 +00:00
/ * *
* Adds a ` <link rel="stylesheet"> ` tag into the page with the desired URL or a
* ` <style type="text/css"> ` tag with the content .
* @returns Promise which resolves to the added tag when the stylesheet ' s
* onload fires or when the CSS content was injected into frame .
* /
2020-05-07 10:54:55 +00:00
async addStyleTag ( options : {
url? : string ;
path? : string ;
content? : string ;
} ) : Promise < ElementHandle > {
2017-10-12 08:26:44 +00:00
return this . mainFrame ( ) . addStyleTag ( options ) ;
2017-06-21 20:51:06 +00:00
}
2021-06-23 08:23:36 +00:00
/ * *
* The method adds a function called ` name ` on the page ' s ` window ` object . When
* called , the function executes ` puppeteerFunction ` in node . js and returns a
* ` Promise ` which resolves to the return value of ` puppeteerFunction ` .
*
* If the puppeteerFunction returns a ` Promise ` , it will be awaited .
*
* NOTE : Functions installed via ` page.exposeFunction ` survive navigations .
* @param name - Name of the function on the window object
* @param puppeteerFunction - Callback function which will be called in
* Puppeteer ' s context .
* @example
* An example of adding an ` md5 ` function into the page :
* ` ` ` js
* const puppeteer = require ( 'puppeteer' ) ;
* const crypto = require ( 'crypto' ) ;
*
* ( async ( ) = > {
* const browser = await puppeteer . launch ( ) ;
* const page = await browser . newPage ( ) ;
* page . on ( 'console' , ( msg ) = > console . log ( msg . text ( ) ) ) ;
* await page . exposeFunction ( 'md5' , ( text ) = >
* crypto . createHash ( 'md5' ) . update ( text ) . digest ( 'hex' )
* ) ;
* await page . evaluate ( async ( ) = > {
* // use window.md5 to compute hashes
* const myString = 'PUPPETEER' ;
* const myHash = await window . md5 ( myString ) ;
* console . log ( ` md5 of ${ myString } is ${ myHash } ` ) ;
* } ) ;
* await browser . close ( ) ;
* } ) ( ) ;
* ` ` `
* An example of adding a ` window.readfile ` function into the page :
* ` ` ` js
* const puppeteer = require ( 'puppeteer' ) ;
* const fs = require ( 'fs' ) ;
*
* ( async ( ) = > {
* const browser = await puppeteer . launch ( ) ;
* const page = await browser . newPage ( ) ;
* page . on ( 'console' , ( msg ) = > console . log ( msg . text ( ) ) ) ;
* await page . exposeFunction ( 'readfile' , async ( filePath ) = > {
* return new Promise ( ( resolve , reject ) = > {
* fs . readFile ( filePath , 'utf8' , ( err , text ) = > {
* if ( err ) reject ( err ) ;
* else resolve ( text ) ;
* } ) ;
* } ) ;
* } ) ;
* await page . evaluate ( async ( ) = > {
* // use window.readfile to read contents of a file
* const content = await window . readfile ( '/etc/hosts' ) ;
* console . log ( content ) ;
* } ) ;
* await browser . close ( ) ;
* } ) ( ) ;
* ` ` `
* /
2020-05-07 10:54:55 +00:00
async exposeFunction (
name : string ,
2021-09-29 16:32:49 +00:00
puppeteerFunction : Function | { default : Function }
2020-05-07 10:54:55 +00:00
) : Promise < void > {
2018-07-31 19:18:10 +00:00
if ( this . _pageBindings . has ( name ) )
2020-05-07 10:54:55 +00:00
throw new Error (
` Failed to add page binding with name ${ name } : window[' ${ name } '] already exists! `
) ;
2021-09-29 16:32:49 +00:00
let exposedFunction : Function ;
if ( typeof puppeteerFunction === 'function' ) {
exposedFunction = puppeteerFunction ;
} else if ( typeof puppeteerFunction . default === 'function' ) {
exposedFunction = puppeteerFunction . default ;
} else {
throw new Error (
` Failed to add page binding with name ${ name } : ${ puppeteerFunction } is not a function or a module with a default export. `
) ;
}
this . _pageBindings . set ( name , exposedFunction ) ;
2017-06-21 20:51:06 +00:00
2020-10-07 08:49:11 +00:00
const expression = helper . pageBindingInitString ( 'exposedFun' , name ) ;
2020-05-07 10:54:55 +00:00
await this . _client . send ( 'Runtime.addBinding' , { name : name } ) ;
await this . _client . send ( 'Page.addScriptToEvaluateOnNewDocument' , {
source : expression ,
} ) ;
await Promise . all (
this . frames ( ) . map ( ( frame ) = > frame . evaluate ( expression ) . catch ( debugError ) )
) ;
2017-06-21 20:51:06 +00:00
}
2017-06-21 20:36:04 +00:00
2021-06-02 12:47:20 +00:00
/ * *
* Provide credentials for ` HTTP authentication ` .
* @remarks To disable authentication , pass ` null ` .
* /
2020-05-05 12:53:22 +00:00
async authenticate ( credentials : Credentials ) : Promise < void > {
2019-04-10 04:42:42 +00:00
return this . _frameManager . networkManager ( ) . authenticate ( credentials ) ;
2017-09-11 23:32:13 +00:00
}
2021-06-23 08:23:36 +00:00
/ * *
* The extra HTTP headers will be sent with every request the page initiates .
* NOTE : All HTTP header names are lowercased . ( HTTP headers are
* case - insensitive , so this shouldn ’ t impact your server code . )
* NOTE : page.setExtraHTTPHeaders does not guarantee the order of headers in
* the outgoing requests .
* @param headers - An object containing additional HTTP headers to be sent
* with every request . All header values must be strings .
* @returns
* /
2020-05-05 12:53:22 +00:00
async setExtraHTTPHeaders ( headers : Record < string , string > ) : Promise < void > {
2019-04-10 04:42:42 +00:00
return this . _frameManager . networkManager ( ) . setExtraHTTPHeaders ( headers ) ;
2017-06-21 20:51:06 +00:00
}
2021-06-23 08:23:36 +00:00
/ * *
* @param userAgent - Specific user agent to use in this page
2021-06-29 16:29:55 +00:00
* @param userAgentData - Specific user agent client hint data to use in this
* page
2021-06-23 08:23:36 +00:00
* @returns Promise which resolves when the user agent is set .
* /
2021-06-29 16:29:55 +00:00
async setUserAgent (
userAgent : string ,
userAgentMetadata? : Protocol.Emulation.UserAgentMetadata
) : Promise < void > {
return this . _frameManager
. networkManager ( )
. setUserAgent ( userAgent , userAgentMetadata ) ;
2017-06-21 20:51:06 +00:00
}
2017-05-11 07:06:41 +00:00
2021-06-23 08:23:36 +00:00
/ * *
* @returns Object containing metrics as key / value pairs .
*
2021-07-09 08:12:01 +00:00
* - ` Timestamp ` : The timestamp when the metrics sample was taken .
2021-06-23 08:23:36 +00:00
*
2021-07-09 08:12:01 +00:00
* - ` Documents ` : Number of documents in the page .
2021-06-23 08:23:36 +00:00
*
2021-07-09 08:12:01 +00:00
* - ` Frames ` : Number of frames in the page .
2021-06-23 08:23:36 +00:00
*
2021-07-09 08:12:01 +00:00
* - ` JSEventListeners ` : Number of events in the page .
2021-06-23 08:23:36 +00:00
*
2021-07-09 08:12:01 +00:00
* - ` Nodes ` : Number of DOM nodes in the page .
2021-06-23 08:23:36 +00:00
*
2021-07-09 08:12:01 +00:00
* - ` LayoutCount ` : Total number of full or partial page layout .
2021-06-23 08:23:36 +00:00
*
2021-07-09 08:12:01 +00:00
* - ` RecalcStyleCount ` : Total number of page style recalculations .
2021-06-23 08:23:36 +00:00
*
2021-07-09 08:12:01 +00:00
* - ` LayoutDuration ` : Combined durations of all page layouts .
2021-06-23 08:23:36 +00:00
*
2021-07-09 08:12:01 +00:00
* - ` RecalcStyleDuration ` : Combined duration of all page style
2021-06-23 08:23:36 +00:00
* recalculations .
*
2021-07-09 08:12:01 +00:00
* - ` ScriptDuration ` : Combined duration of JavaScript execution .
2021-06-23 08:23:36 +00:00
*
2021-07-09 08:12:01 +00:00
* - ` TaskDuration ` : Combined duration of all tasks performed by the browser .
2021-06-23 08:23:36 +00:00
*
*
2021-07-09 08:12:01 +00:00
* - ` JSHeapUsedSize ` : Used JavaScript heap size .
*
* - ` JSHeapTotalSize ` : Total JavaScript heap size .
2021-06-23 08:23:36 +00:00
* @remarks
* NOTE : All timestamps are in monotonic time : monotonically increasing time
* in seconds since an arbitrary point in the past .
* /
2020-05-05 12:53:22 +00:00
async metrics ( ) : Promise < Metrics > {
2017-10-10 21:50:38 +00:00
const response = await this . _client . send ( 'Performance.getMetrics' ) ;
return this . _buildMetricsObject ( response . metrics ) ;
}
2020-07-10 10:51:52 +00:00
private _emitMetrics ( event : Protocol.Performance.MetricsEvent ) : void {
2020-07-06 10:34:55 +00:00
this . emit ( PageEmittedEvents . Metrics , {
2017-10-10 21:50:38 +00:00
title : event.title ,
2020-05-07 10:54:55 +00:00
metrics : this._buildMetricsObject ( event . metrics ) ,
2017-10-10 21:50:38 +00:00
} ) ;
}
2020-06-10 15:15:02 +00:00
private _buildMetricsObject (
metrics? : Protocol.Performance.Metric [ ]
) : Metrics {
2017-10-10 21:50:38 +00:00
const result = { } ;
for ( const metric of metrics || [ ] ) {
2020-05-07 10:54:55 +00:00
if ( supportedMetrics . has ( metric . name ) ) result [ metric . name ] = metric . value ;
2017-10-10 21:50:38 +00:00
}
return result ;
}
2020-06-10 15:15:02 +00:00
private _handleException (
exceptionDetails : Protocol.Runtime.ExceptionDetails
) : void {
2017-08-21 23:39:04 +00:00
const message = helper . getExceptionMessage ( exceptionDetails ) ;
2018-05-25 23:44:25 +00:00
const err = new Error ( message ) ;
err . stack = '' ; // Don't report clientside error with a node stack attached
2020-07-06 10:34:55 +00:00
this . emit ( PageEmittedEvents . PageError , err ) ;
2017-06-21 20:51:06 +00:00
}
2020-06-10 15:15:02 +00:00
private async _onConsoleAPI (
2020-07-10 10:51:52 +00:00
event : Protocol.Runtime.ConsoleAPICalledEvent
2020-05-07 10:54:55 +00:00
) : Promise < void > {
2019-01-31 00:19:02 +00:00
if ( event . executionContextId === 0 ) {
// DevTools protocol stores the last 1000 console messages. These
// messages are always reported even for removed execution contexts. In
// this case, they are marked with executionContextId = 0 and are
// reported upon enabling Runtime agent.
//
// Ignore these messages since:
// - there's no execution context we can use to operate with message
// arguments
// - these messages are reported before Puppeteer clients can subscribe
// to the 'console'
// page event.
//
2019-11-26 12:12:25 +00:00
// @see https://github.com/puppeteer/puppeteer/issues/3865
2019-01-31 00:19:02 +00:00
return ;
}
2020-05-07 10:54:55 +00:00
const context = this . _frameManager . executionContextById (
2021-10-28 09:25:49 +00:00
event . executionContextId ,
this . _client
2020-05-07 10:54:55 +00:00
) ;
const values = event . args . map ( ( arg ) = > createJSHandle ( context , arg ) ) ;
2019-01-11 02:05:28 +00:00
this . _addConsoleMessage ( event . type , values , event . stackTrace ) ;
2018-06-07 18:21:35 +00:00
}
2020-06-10 15:15:02 +00:00
private async _onBindingCalled (
2020-07-10 10:51:52 +00:00
event : Protocol.Runtime.BindingCalledEvent
2020-05-07 10:54:55 +00:00
) : Promise < void > {
2020-10-23 10:45:47 +00:00
let payload : { type : string ; name : string ; seq : number ; args : unknown [ ] } ;
try {
payload = JSON . parse ( event . payload ) ;
} catch {
// The binding was either called by something in the page or it was
// called before our wrapper was initialized.
return ;
}
const { type , name , seq , args } = payload ;
2020-10-07 08:49:11 +00:00
if ( type !== 'exposedFun' || ! this . _pageBindings . has ( name ) ) return ;
2018-11-15 22:51:34 +00:00
let expression = null ;
try {
const result = await this . _pageBindings . get ( name ) ( . . . args ) ;
2020-10-07 08:49:11 +00:00
expression = helper . pageBindingDeliverResultString ( name , seq , result ) ;
2018-11-15 22:51:34 +00:00
} catch ( error ) {
if ( error instanceof Error )
2020-10-07 08:49:11 +00:00
expression = helper . pageBindingDeliverErrorString (
2020-05-07 10:54:55 +00:00
name ,
seq ,
error . message ,
error . stack
) ;
2018-11-15 22:51:34 +00:00
else
2020-10-07 08:49:11 +00:00
expression = helper . pageBindingDeliverErrorValueString (
2020-05-07 10:54:55 +00:00
name ,
seq ,
error
) ;
2018-11-15 22:51:34 +00:00
}
2020-05-07 10:54:55 +00:00
this . _client
. send ( 'Runtime.evaluate' , {
expression ,
contextId : event.executionContextId ,
} )
. catch ( debugError ) ;
2018-06-18 20:41:03 +00:00
}
2020-06-10 15:15:02 +00:00
private _addConsoleMessage (
2020-06-22 12:52:39 +00:00
type : ConsoleMessageType ,
2020-05-07 10:54:55 +00:00
args : JSHandle [ ] ,
stackTrace? : Protocol.Runtime.StackTrace
) : void {
2020-07-06 10:34:55 +00:00
if ( ! this . listenerCount ( PageEmittedEvents . Console ) ) {
2020-05-07 10:54:55 +00:00
args . forEach ( ( arg ) = > arg . dispose ( ) ) ;
2017-07-25 04:43:54 +00:00
return ;
}
2017-10-10 17:54:20 +00:00
const textTokens = [ ] ;
2018-06-07 18:21:35 +00:00
for ( const arg of args ) {
const remoteObject = arg . _remoteObject ;
2020-05-07 10:54:55 +00:00
if ( remoteObject . objectId ) textTokens . push ( arg . toString ( ) ) ;
else textTokens . push ( helper . valueFromRemoteObject ( remoteObject ) ) ;
2017-10-10 17:54:20 +00:00
}
2020-09-25 13:27:13 +00:00
const stackTraceLocations = [ ] ;
if ( stackTrace ) {
for ( const callFrame of stackTrace . callFrames ) {
stackTraceLocations . push ( {
url : callFrame.url ,
lineNumber : callFrame.lineNumber ,
columnNumber : callFrame.columnNumber ,
} ) ;
}
}
2020-05-07 10:54:55 +00:00
const message = new ConsoleMessage (
type ,
textTokens . join ( ' ' ) ,
args ,
2020-09-25 13:27:13 +00:00
stackTraceLocations
2020-05-07 10:54:55 +00:00
) ;
2020-07-06 10:34:55 +00:00
this . emit ( PageEmittedEvents . Console , message ) ;
2017-06-21 20:51:06 +00:00
}
2020-07-10 10:51:52 +00:00
private _onDialog ( event : Protocol.Page.JavascriptDialogOpeningEvent ) : void {
2017-06-22 20:38:10 +00:00
let dialogType = null ;
2020-06-09 08:19:42 +00:00
const validDialogTypes = new Set < Protocol.Page.DialogType > ( [
'alert' ,
'confirm' ,
'prompt' ,
'beforeunload' ,
] ) ;
if ( validDialogTypes . has ( event . type ) ) {
dialogType = event . type as Protocol . Page . DialogType ;
}
2018-05-31 23:53:51 +00:00
assert ( dialogType , 'Unknown javascript dialog type: ' + event . type ) ;
2020-05-05 12:53:22 +00:00
2020-05-07 10:54:55 +00:00
const dialog = new Dialog (
this . _client ,
dialogType ,
event . message ,
event . defaultPrompt
) ;
2020-07-06 10:34:55 +00:00
this . emit ( PageEmittedEvents . Dialog , dialog ) ;
2017-06-21 20:51:06 +00:00
}
2021-03-18 19:57:32 +00:00
/ * *
* Resets default white background
* /
private async _resetDefaultBackgroundColor() {
await this . _client . send ( 'Emulation.setDefaultBackgroundColorOverride' ) ;
}
/ * *
* Hides default white background
* /
private async _setTransparentBackgroundColor ( ) : Promise < void > {
await this . _client . send ( 'Emulation.setDefaultBackgroundColorOverride' , {
color : { r : 0 , g : 0 , b : 0 , a : 0 } ,
} ) ;
}
2021-06-23 08:23:36 +00:00
/ * *
*
* @returns
* @remarks Shortcut for
* { @link Frame . url | page . mainFrame ( ) . url ( ) } .
* /
2020-05-05 12:53:22 +00:00
url ( ) : string {
2017-07-14 21:05:27 +00:00
return this . mainFrame ( ) . url ( ) ;
2017-06-21 20:51:06 +00:00
}
2017-06-21 20:36:04 +00:00
2020-05-05 12:53:22 +00:00
async content ( ) : Promise < string > {
2017-11-23 02:44:33 +00:00
return await this . _frameManager . mainFrame ( ) . content ( ) ;
2017-08-21 16:02:30 +00:00
}
2021-06-23 08:23:36 +00:00
/ * *
* @param html - HTML markup to assign to the page .
* @param options - Parameters that has some properties .
* @remarks
* The parameter ` options ` might have the following options .
*
* - ` timeout ` : Maximum time in milliseconds for resources to load , defaults
* to 30 seconds , pass ` 0 ` to disable timeout . The default value can be
* changed by using the
* { @link Page . setDefaultNavigationTimeout |
* page . setDefaultNavigationTimeout ( timeout ) }
* or { @link Page . setDefaultTimeout | page . setDefaultTimeout ( timeout ) }
* methods .
*
2021-07-12 09:42:44 +00:00
* - ` waitUntil ` : When to consider setting markup succeeded , defaults to ` load ` .
* Given an array of event strings , setting content is considered to be
* successful after all events have been fired . Events can be either : < br / >
2021-06-23 08:23:36 +00:00
* - ` load ` : consider setting content to be finished when the ` load ` event is
* fired . < br / >
* - ` domcontentloaded ` : consider setting content to be finished when the
* ` DOMContentLoaded ` event is fired . < br / >
* - ` networkidle0 ` : consider setting content to be finished when there are no
* more than 0 network connections for at least ` 500 ` ms . < br / >
* - ` networkidle2 ` : consider setting content to be finished when there are no
* more than 2 network connections for at least ` 500 ` ms .
* /
2020-06-23 05:18:46 +00:00
async setContent ( html : string , options : WaitForOptions = { } ) : Promise < void > {
2018-11-20 23:32:46 +00:00
await this . _frameManager . mainFrame ( ) . setContent ( html , options ) ;
2017-06-21 20:51:06 +00:00
}
2021-06-23 08:23:36 +00:00
/ * *
* @param url - URL to navigate page to . The URL should include scheme , e . g .
* ` https:// `
* @param options - Navigation Parameter
* @returns Promise which resolves to the main resource response . In case of
* multiple redirects , the navigation will resolve with the response of the
* last redirect .
* @remarks
* The argument ` options ` might have the following properties :
*
* - ` timeout ` : Maximum navigation time in milliseconds , defaults to 30
* seconds , pass 0 to disable timeout . The default value can be changed by
* using the
* { @link Page . setDefaultNavigationTimeout |
* page . setDefaultNavigationTimeout ( timeout ) }
* or { @link Page . setDefaultTimeout | page . setDefaultTimeout ( timeout ) }
* methods .
*
2021-07-12 09:42:44 +00:00
* - ` waitUntil ` : When to consider navigation succeeded , defaults to ` load ` .
* Given an array of event strings , navigation is considered to be successful
* after all events have been fired . Events can be either : < br / >
2021-06-23 08:23:36 +00:00
* - ` load ` : consider navigation to be finished when the load event is
* fired . < br / >
* - ` domcontentloaded ` : consider navigation to be finished when the
* DOMContentLoaded event is fired . < br / >
* - ` networkidle0 ` : consider navigation to be finished when there are no
* more than 0 network connections for at least ` 500 ` ms . < br / >
* - ` networkidle2 ` : consider navigation to be finished when there are no
* more than 2 network connections for at least ` 500 ` ms .
*
* - ` referer ` : Referer header value . If provided it will take preference
* over the referer header value set by
* { @link Page . setExtraHTTPHeaders | page . setExtraHTTPHeaders ( ) } .
*
* ` page.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 .
*
* ` page.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 response . status ( ) .
*
* NOTE : ` page.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
* { @link https : //bugs.chromium.org/p/chromium/issues/detail?id=761295
* | upstream issue } .
*
* Shortcut for { @link Frame . goto | page . mainFrame ( ) . goto ( url , options ) } .
* /
2020-05-07 10:54:55 +00:00
async goto (
url : string ,
2020-06-18 11:44:46 +00:00
options : WaitForOptions & { referer? : string } = { }
2020-05-29 10:49:30 +00:00
) : Promise < HTTPResponse > {
2018-09-20 18:31:19 +00:00
return await this . _frameManager . mainFrame ( ) . goto ( url , options ) ;
2017-07-18 01:13:04 +00:00
}
2021-06-23 08:23:36 +00:00
/ * *
* @param options - Navigation parameters which might have the following
* properties :
* @returns Promise which resolves to the main resource response . In case of
* multiple redirects , the navigation will resolve with the response of the
* last redirect .
* @remarks
* The argument ` options ` might have the following properties :
*
* - ` timeout ` : Maximum navigation time in milliseconds , defaults to 30
* seconds , pass 0 to disable timeout . The default value can be changed by
* using the
* { @link Page . setDefaultNavigationTimeout |
* page . setDefaultNavigationTimeout ( timeout ) }
* or { @link Page . setDefaultTimeout | page . setDefaultTimeout ( timeout ) }
* methods .
*
2021-07-12 09:42:44 +00:00
* - ` waitUntil ` : When to consider navigation succeeded , defaults to ` load ` .
* Given an array of event strings , navigation is considered to be
* successful after all events have been fired . Events can be either : < br / >
2021-06-23 08:23:36 +00:00
* - ` load ` : consider navigation to be finished when the load event is fired . < br / >
* - ` domcontentloaded ` : consider navigation to be finished when the
* DOMContentLoaded event is fired . < br / >
* - ` networkidle0 ` : consider navigation to be finished when there are no
* more than 0 network connections for at least ` 500 ` ms . < br / >
* - ` networkidle2 ` : consider navigation to be finished when there are no
* more than 2 network connections for at least ` 500 ` ms .
* /
2020-05-29 10:49:30 +00:00
async reload ( options? : WaitForOptions ) : Promise < HTTPResponse | null > {
2022-02-09 14:49:25 +00:00
const result = await Promise . all ( [
2020-07-10 10:51:52 +00:00
this . waitForNavigation ( options ) ,
this . _client . send ( 'Page.reload' ) ,
] ) ;
2020-03-31 08:48:09 +00:00
2020-05-05 12:53:22 +00:00
return result [ 0 ] ;
2017-07-19 01:54:24 +00:00
}
2017-07-18 01:13:04 +00:00
2021-06-25 12:40:53 +00:00
/ * *
* This resolves when the page navigates to a new URL or reloads . It is useful
* when you run code that will indirectly cause the page to navigate . Consider
* this example :
* ` ` ` js
* const [ response ] = await Promise . all ( [
* page . waitForNavigation ( ) , // The promise resolves after navigation has finished
* page . click ( 'a.my-link' ) , // Clicking the link will indirectly cause a navigation
* ] ) ;
* ` ` `
*
* @param options - Navigation parameters which might have the following properties :
* @returns Promise which resolves to the main resource response . In case of
* multiple redirects , the navigation will resolve with the response of the
* last redirect . In case of navigation to a different anchor or navigation
* due to History API usage , the navigation will resolve with ` null ` .
* @remarks
* NOTE : Usage of the
* { @link https : //developer.mozilla.org/en-US/docs/Web/API/History_API | History API}
* to change the URL is considered a navigation .
*
* Shortcut for
* { @link Frame . waitForNavigation | page . mainFrame ( ) . waitForNavigation ( options ) } .
* /
2020-05-07 10:54:55 +00:00
async waitForNavigation (
options : WaitForOptions = { }
2020-05-29 10:49:30 +00:00
) : Promise < HTTPResponse | null > {
2018-09-20 18:31:19 +00:00
return await this . _frameManager . mainFrame ( ) . waitForNavigation ( options ) ;
2017-06-21 20:51:06 +00:00
}
2017-06-21 20:36:04 +00:00
2020-06-10 15:15:02 +00:00
private _sessionClosePromise ( ) : Promise < Error > {
2019-08-21 17:26:48 +00:00
if ( ! this . _disconnectPromise )
2020-05-07 10:54:55 +00:00
this . _disconnectPromise = new Promise ( ( fulfill ) = >
2020-07-08 10:11:01 +00:00
this . _client . once ( CDPSessionEmittedEvents . Disconnected , ( ) = >
2020-05-07 10:54:55 +00:00
fulfill ( new Error ( 'Target closed' ) )
)
) ;
2019-08-21 17:26:48 +00:00
return this . _disconnectPromise ;
}
2021-06-25 12:40:53 +00:00
/ * *
* @param urlOrPredicate - A URL or predicate to wait for
* @param options - Optional waiting parameters
* @returns Promise which resolves to the matched response
* @example
* ` ` ` js
* const firstResponse = await page . waitForResponse (
* 'https://example.com/resource'
* ) ;
* const finalResponse = await page . waitForResponse (
* ( response ) = >
* response . url ( ) === 'https://example.com' && response . status ( ) === 200
* ) ;
* const finalResponse = await page . waitForResponse ( async ( response ) = > {
* return ( await response . text ( ) ) . includes ( '<html>' ) ;
* } ) ;
* return finalResponse . ok ( ) ;
* ` ` `
* @remarks
* Optional Waiting Parameters have :
*
* - ` timeout ` : Maximum wait time in milliseconds , defaults to ` 30 ` seconds , pass
* ` 0 ` to disable the timeout . The default value can be changed by using the
* { @link Page . setDefaultTimeout } method .
* /
2020-05-07 10:54:55 +00:00
async waitForRequest (
2021-04-06 19:39:12 +00:00
urlOrPredicate : string | ( ( req : HTTPRequest ) = > boolean | Promise < boolean > ) ,
2020-05-07 10:54:55 +00:00
options : { timeout? : number } = { }
2020-05-29 08:38:40 +00:00
) : Promise < HTTPRequest > {
2020-05-07 10:54:55 +00:00
const { timeout = this . _timeoutSettings . timeout ( ) } = options ;
return helper . waitForEvent (
this . _frameManager . networkManager ( ) ,
2020-07-07 15:43:55 +00:00
NetworkManagerEmittedEvents . Request ,
2020-05-07 10:54:55 +00:00
( request ) = > {
if ( helper . isString ( urlOrPredicate ) )
return urlOrPredicate === request . url ( ) ;
if ( typeof urlOrPredicate === 'function' )
return ! ! urlOrPredicate ( request ) ;
return false ;
} ,
timeout ,
this . _sessionClosePromise ( )
) ;
}
2021-06-25 12:40:53 +00:00
/ * *
* @param urlOrPredicate - A URL or predicate to wait for .
* @param options - Optional waiting parameters
* @returns Promise which resolves to the matched response .
* @example
* ` ` ` js
* const firstResponse = await page . waitForResponse (
* 'https://example.com/resource'
* ) ;
* const finalResponse = await page . waitForResponse (
* ( response ) = >
* response . url ( ) === 'https://example.com' && response . status ( ) === 200
* ) ;
* const finalResponse = await page . waitForResponse ( async ( response ) = > {
* return ( await response . text ( ) ) . includes ( '<html>' ) ;
* } ) ;
* return finalResponse . ok ( ) ;
* ` ` `
* @remarks
* Optional Parameter have :
*
* - ` timeout ` : Maximum wait time in milliseconds , defaults to ` 30 ` seconds ,
* pass ` 0 ` to disable the timeout . The default value can be changed by using
* the { @link Page . setDefaultTimeout } method .
* /
2020-05-07 10:54:55 +00:00
async waitForResponse (
2021-04-06 19:39:12 +00:00
urlOrPredicate :
| string
| ( ( res : HTTPResponse ) = > boolean | Promise < boolean > ) ,
2020-05-07 10:54:55 +00:00
options : { timeout? : number } = { }
2020-05-29 10:49:30 +00:00
) : Promise < HTTPResponse > {
2020-05-07 10:54:55 +00:00
const { timeout = this . _timeoutSettings . timeout ( ) } = options ;
return helper . waitForEvent (
this . _frameManager . networkManager ( ) ,
2020-07-07 15:43:55 +00:00
NetworkManagerEmittedEvents . Response ,
2020-11-25 10:35:47 +00:00
async ( response ) = > {
2020-05-07 10:54:55 +00:00
if ( helper . isString ( urlOrPredicate ) )
return urlOrPredicate === response . url ( ) ;
if ( typeof urlOrPredicate === 'function' )
2020-11-25 10:35:47 +00:00
return ! ! ( await urlOrPredicate ( response ) ) ;
2020-05-07 10:54:55 +00:00
return false ;
} ,
timeout ,
this . _sessionClosePromise ( )
) ;
2018-07-12 21:36:31 +00:00
}
2021-09-11 20:28:12 +00:00
/ * *
* @param options - Optional waiting parameters
* @returns Promise which resolves when network is idle
* /
async waitForNetworkIdle (
options : { idleTime? : number ; timeout? : number } = { }
) : Promise < void > {
const { idleTime = 500 , timeout = this . _timeoutSettings . timeout ( ) } =
options ;
const networkManager = this . _frameManager . networkManager ( ) ;
let idleResolveCallback ;
const idlePromise = new Promise ( ( resolve ) = > {
idleResolveCallback = resolve ;
} ) ;
let abortRejectCallback ;
const abortPromise = new Promise < Error > ( ( _ , reject ) = > {
abortRejectCallback = reject ;
} ) ;
let idleTimer ;
const onIdle = ( ) = > idleResolveCallback ( ) ;
const cleanup = ( ) = > {
idleTimer && clearTimeout ( idleTimer ) ;
abortRejectCallback ( new Error ( 'abort' ) ) ;
} ;
const evaluate = ( ) = > {
idleTimer && clearTimeout ( idleTimer ) ;
if ( networkManager . numRequestsInProgress ( ) === 0 )
idleTimer = setTimeout ( onIdle , idleTime ) ;
} ;
evaluate ( ) ;
const eventHandler = ( ) = > {
evaluate ( ) ;
return false ;
} ;
const listenToEvent = ( event ) = >
helper . waitForEvent (
networkManager ,
event ,
eventHandler ,
timeout ,
abortPromise
) ;
const eventPromises = [
listenToEvent ( NetworkManagerEmittedEvents . Request ) ,
listenToEvent ( NetworkManagerEmittedEvents . Response ) ,
] ;
await Promise . race ( [
idlePromise ,
. . . eventPromises ,
this . _sessionClosePromise ( ) ,
] ) . then (
( r ) = > {
cleanup ( ) ;
return r ;
} ,
( error ) = > {
cleanup ( ) ;
throw error ;
}
) ;
}
2021-10-28 09:25:49 +00:00
/ * *
* @param urlOrPredicate - A URL or predicate to wait for .
* @param options - Optional waiting parameters
* @returns Promise which resolves to the matched frame .
* @example
* ` ` ` js
* const frame = await page . waitForFrame ( async ( frame ) = > {
* return frame . name ( ) === 'Test' ;
* } ) ;
* ` ` `
* @remarks
* Optional Parameter have :
*
* - ` timeout ` : Maximum wait time in milliseconds , defaults to ` 30 ` seconds ,
* pass ` 0 ` to disable the timeout . The default value can be changed by using
* the { @link Page . setDefaultTimeout } method .
* /
async waitForFrame (
urlOrPredicate : string | ( ( frame : Frame ) = > boolean | Promise < boolean > ) ,
options : { timeout? : number } = { }
) : Promise < Frame > {
const { timeout = this . _timeoutSettings . timeout ( ) } = options ;
async function predicate ( frame : Frame ) {
if ( helper . isString ( urlOrPredicate ) )
return urlOrPredicate === frame . url ( ) ;
if ( typeof urlOrPredicate === 'function' )
return ! ! ( await urlOrPredicate ( frame ) ) ;
return false ;
}
2022-04-07 13:05:36 +00:00
const eventRace = Promise . race ( [
2021-10-28 09:25:49 +00:00
helper . waitForEvent (
this . _frameManager ,
FrameManagerEmittedEvents . FrameAttached ,
predicate ,
timeout ,
this . _sessionClosePromise ( )
) ,
helper . waitForEvent (
this . _frameManager ,
FrameManagerEmittedEvents . FrameNavigated ,
predicate ,
timeout ,
this . _sessionClosePromise ( )
) ,
] ) ;
2022-04-07 13:05:36 +00:00
return Promise . race ( [
eventRace ,
( async ( ) = > {
for ( const frame of this . frames ( ) ) {
if ( await predicate ( frame ) ) {
return frame ;
}
}
await eventRace ;
} ) ( ) ,
] ) ;
2021-10-28 09:25:49 +00:00
}
2021-06-23 08:23:36 +00:00
/ * *
* This method navigate to the previous page in history .
* @param options - Navigation parameters
* @returns Promise which resolves to the main resource response . In case of
* multiple redirects , the navigation will resolve with the response of the
* last redirect . If can not go back , resolves to ` null ` .
* @remarks
* The argument ` options ` might have the following properties :
*
* - ` timeout ` : Maximum navigation time in milliseconds , defaults to 30
* seconds , pass 0 to disable timeout . The default value can be changed by
* using the
* { @link Page . setDefaultNavigationTimeout
* | page . setDefaultNavigationTimeout ( timeout ) }
* or { @link Page . setDefaultTimeout | page . setDefaultTimeout ( timeout ) }
* methods .
*
2021-07-12 09:42:44 +00:00
* - ` waitUntil ` : When to consider navigation succeeded , defaults to ` load ` .
* Given an array of event strings , navigation is considered to be
* successful after all events have been fired . Events can be either : < br / >
2021-06-23 08:23:36 +00:00
* - ` load ` : consider navigation to be finished when the load event is fired . < br / >
* - ` domcontentloaded ` : consider navigation to be finished when the
* DOMContentLoaded event is fired . < br / >
* - ` networkidle0 ` : consider navigation to be finished when there are no
* more than 0 network connections for at least ` 500 ` ms . < br / >
* - ` networkidle2 ` : consider navigation to be finished when there are no
* more than 2 network connections for at least ` 500 ` ms .
* /
2020-06-23 05:18:46 +00:00
async goBack ( options : WaitForOptions = { } ) : Promise < HTTPResponse | null > {
2017-07-19 02:11:37 +00:00
return this . _go ( - 1 , options ) ;
}
2021-06-23 08:23:36 +00:00
/ * *
* This method navigate to the next page in history .
* @param options - Navigation Parameter
* @returns Promise which resolves to the main resource response . In case of
* multiple redirects , the navigation will resolve with the response of the
* last redirect . If can not go forward , resolves to ` null ` .
* @remarks
* The argument ` options ` might have the following properties :
*
* - ` timeout ` : Maximum navigation time in milliseconds , defaults to 30
* seconds , pass 0 to disable timeout . The default value can be changed by
* using the
* { @link Page . setDefaultNavigationTimeout
* | page . setDefaultNavigationTimeout ( timeout ) }
* or { @link Page . setDefaultTimeout | page . setDefaultTimeout ( timeout ) }
* methods .
*
2021-07-12 09:42:44 +00:00
* - ` waitUntil ` : When to consider navigation succeeded , defaults to ` load ` .
* Given an array of event strings , navigation is considered to be
* successful after all events have been fired . Events can be either : < br / >
2021-06-23 08:23:36 +00:00
* - ` load ` : consider navigation to be finished when the load event is fired . < br / >
* - ` domcontentloaded ` : consider navigation to be finished when the
* DOMContentLoaded event is fired . < br / >
* - ` networkidle0 ` : consider navigation to be finished when there are no
* more than 0 network connections for at least ` 500 ` ms . < br / >
* - ` networkidle2 ` : consider navigation to be finished when there are no
* more than 2 network connections for at least ` 500 ` ms .
* /
2020-06-23 05:18:46 +00:00
async goForward ( options : WaitForOptions = { } ) : Promise < HTTPResponse | null > {
2017-07-19 02:11:37 +00:00
return this . _go ( + 1 , options ) ;
}
2020-06-12 10:10:12 +00:00
private async _go (
2020-05-07 10:54:55 +00:00
delta : number ,
options : WaitForOptions
2020-05-29 10:49:30 +00:00
) : Promise < HTTPResponse | null > {
2017-07-19 02:11:37 +00:00
const history = await this . _client . send ( 'Page.getNavigationHistory' ) ;
const entry = history . entries [ history . currentIndex + delta ] ;
2020-05-07 10:54:55 +00:00
if ( ! entry ) return null ;
2020-07-10 10:51:52 +00:00
const result = await Promise . all ( [
2017-09-11 23:21:51 +00:00
this . waitForNavigation ( options ) ,
2020-05-07 10:54:55 +00:00
this . _client . send ( 'Page.navigateToHistoryEntry' , { entryId : entry.id } ) ,
2017-09-11 23:21:51 +00:00
] ) ;
2020-05-05 12:53:22 +00:00
return result [ 0 ] ;
2017-07-19 02:11:37 +00:00
}
2021-06-02 12:47:20 +00:00
/ * *
* Brings page to front ( activates tab ) .
* /
2020-05-05 12:53:22 +00:00
async bringToFront ( ) : Promise < void > {
2017-11-07 21:17:36 +00:00
await this . _client . send ( 'Page.bringToFront' ) ;
}
2021-06-03 07:34:22 +00:00
/ * *
* Emulates given device metrics and user agent . This method is a shortcut for
2021-06-23 08:23:36 +00:00
* calling two methods : { @link Page . setUserAgent } and { @link Page . setViewport }
2021-06-03 07:34:22 +00:00
* To aid emulation , Puppeteer provides a list of device descriptors that can
2021-06-23 08:23:36 +00:00
* be obtained via the { @link Puppeteer . devices } ` page.emulate ` will resize
2021-06-03 07:34:22 +00:00
* the page . A lot of websites don ' t expect phones to change size , so you
* should emulate before navigating to the page .
* @example
* ` ` ` js
* const puppeteer = require ( 'puppeteer' ) ;
* const iPhone = puppeteer . devices [ 'iPhone 6' ] ;
* ( async ( ) = > {
* const browser = await puppeteer . launch ( ) ;
* const page = await browser . newPage ( ) ;
* await page . emulate ( iPhone ) ;
* await page . goto ( 'https://www.google.com' ) ;
* // other actions...
* await browser . close ( ) ;
* } ) ( ) ;
* ` ` `
* @remarks List of all available devices is available in the source code :
* { @link https : //github.com/puppeteer/puppeteer/blob/main/src/common/DeviceDescriptors.ts | src/common/DeviceDescriptors.ts}.
* /
2020-05-07 10:54:55 +00:00
async emulate ( options : {
viewport : Viewport ;
userAgent : string ;
} ) : Promise < void > {
2018-11-08 06:48:43 +00:00
await Promise . all ( [
2017-08-11 01:42:30 +00:00
this . setViewport ( options . viewport ) ,
2020-05-07 10:54:55 +00:00
this . setUserAgent ( options . userAgent ) ,
2017-08-11 01:42:30 +00:00
] ) ;
}
2021-06-23 08:23:36 +00:00
/ * *
* @param enabled - Whether or not to enable JavaScript on the page .
* @returns
* @remarks
* NOTE : changing this value won ' t affect scripts that have already been run .
* It will take full effect on the next navigation .
* /
2020-05-05 12:53:22 +00:00
async setJavaScriptEnabled ( enabled : boolean ) : Promise < void > {
2020-05-07 10:54:55 +00:00
if ( this . _javascriptEnabled === enabled ) return ;
2018-07-19 01:51:18 +00:00
this . _javascriptEnabled = enabled ;
2020-05-07 10:54:55 +00:00
await this . _client . send ( 'Emulation.setScriptExecutionDisabled' , {
value : ! enabled ,
} ) ;
2017-08-23 21:08:56 +00:00
}
2021-06-23 08:23:36 +00:00
/ * *
* Toggles bypassing page ' s Content - Security - Policy .
* @param enabled - sets bypassing of page ' s Content - Security - Policy .
* @remarks
* NOTE : CSP bypassing happens at the moment of CSP initialization rather than
* evaluation . Usually , this means that ` page.setBypassCSP ` should be called
* before navigating to the domain .
* /
2020-05-05 12:53:22 +00:00
async setBypassCSP ( enabled : boolean ) : Promise < void > {
2020-05-07 10:54:55 +00:00
await this . _client . send ( 'Page.setBypassCSP' , { enabled } ) ;
2018-04-06 23:35:50 +00:00
}
2021-06-23 08:23:36 +00:00
/ * *
* @param type - Changes the CSS media type of the page . The only allowed
* values are ` screen ` , ` print ` and ` null ` . Passing ` null ` disables CSS media
* emulation .
* @example
* ` ` `
* await page . evaluate ( ( ) = > matchMedia ( 'screen' ) . matches ) ;
* // → true
* await page . evaluate ( ( ) = > matchMedia ( 'print' ) . matches ) ;
* // → false
*
* await page . emulateMediaType ( 'print' ) ;
* await page . evaluate ( ( ) = > matchMedia ( 'screen' ) . matches ) ;
* // → false
* await page . evaluate ( ( ) = > matchMedia ( 'print' ) . matches ) ;
* // → true
*
* await page . emulateMediaType ( null ) ;
* await page . evaluate ( ( ) = > matchMedia ( 'screen' ) . matches ) ;
* // → true
* await page . evaluate ( ( ) = > matchMedia ( 'print' ) . matches ) ;
* // → false
* ` ` `
* /
2020-05-05 12:53:22 +00:00
async emulateMediaType ( type ? : string ) : Promise < void > {
2020-05-07 10:54:55 +00:00
assert (
type === 'screen' || type === 'print' || type === null ,
'Unsupported media type: ' + type
) ;
await this . _client . send ( 'Emulation.setEmulatedMedia' , {
media : type || '' ,
} ) ;
2019-10-23 11:55:00 +00:00
}
2021-10-05 07:49:30 +00:00
/ * *
* Enables CPU throttling to emulate slow CPUs .
* @param factor - slowdown factor ( 1 is no throttle , 2 is 2 x slowdown , etc ) .
* /
2021-06-22 11:43:11 +00:00
async emulateCPUThrottling ( factor : number | null ) : Promise < void > {
assert (
factor === null || factor >= 1 ,
'Throttling rate should be greater or equal to 1'
) ;
await this . _client . send ( 'Emulation.setCPUThrottlingRate' , {
rate : factor !== null ? factor : 1 ,
} ) ;
}
2021-06-03 07:34:22 +00:00
/ * *
* @param features - ` <?Array<Object>> ` Given an array of media feature
* objects , emulates CSS media features on the page . Each media feature object
* must have the following properties :
* @example
* ` ` ` js
* await page . emulateMediaFeatures ( [
* { name : 'prefers-color-scheme' , value : 'dark' } ,
* ] ) ;
* await page . evaluate ( ( ) = > matchMedia ( '(prefers-color-scheme: dark)' ) . matches ) ;
* // → true
* await page . evaluate ( ( ) = > matchMedia ( '(prefers-color-scheme: light)' ) . matches ) ;
* // → false
*
* await page . emulateMediaFeatures ( [
* { name : 'prefers-reduced-motion' , value : 'reduce' } ,
* ] ) ;
* await page . evaluate (
* ( ) = > matchMedia ( '(prefers-reduced-motion: reduce)' ) . matches
* ) ;
* // → true
* await page . evaluate (
* ( ) = > matchMedia ( '(prefers-reduced-motion: no-preference)' ) . matches
* ) ;
* // → false
*
* await page . emulateMediaFeatures ( [
* { name : 'prefers-color-scheme' , value : 'dark' } ,
* { name : 'prefers-reduced-motion' , value : 'reduce' } ,
* ] ) ;
* await page . evaluate ( ( ) = > matchMedia ( '(prefers-color-scheme: dark)' ) . matches ) ;
* // → true
* await page . evaluate ( ( ) = > matchMedia ( '(prefers-color-scheme: light)' ) . matches ) ;
* // → false
* await page . evaluate (
* ( ) = > matchMedia ( '(prefers-reduced-motion: reduce)' ) . matches
* ) ;
* // → true
* await page . evaluate (
* ( ) = > matchMedia ( '(prefers-reduced-motion: no-preference)' ) . matches
* ) ;
* // → false
*
* await page . emulateMediaFeatures ( [ { name : 'color-gamut' , value : 'p3' } ] ) ;
* await page . evaluate ( ( ) = > matchMedia ( '(color-gamut: srgb)' ) . matches ) ;
* // → true
* await page . evaluate ( ( ) = > matchMedia ( '(color-gamut: p3)' ) . matches ) ;
* // → true
* await page . evaluate ( ( ) = > matchMedia ( '(color-gamut: rec2020)' ) . matches ) ;
* // → false
* ` ` `
* /
2020-05-05 12:53:22 +00:00
async emulateMediaFeatures ( features? : MediaFeature [ ] ) : Promise < void > {
2019-10-23 11:55:00 +00:00
if ( features === null )
2020-05-07 10:54:55 +00:00
await this . _client . send ( 'Emulation.setEmulatedMedia' , { features : null } ) ;
2019-10-23 11:55:00 +00:00
if ( Array . isArray ( features ) ) {
2020-05-07 10:54:55 +00:00
features . every ( ( mediaFeature ) = > {
2019-10-23 11:55:00 +00:00
const name = mediaFeature . name ;
2020-05-07 10:54:55 +00:00
assert (
2021-02-11 13:59:50 +00:00
/^(?:prefers-(?:color-scheme|reduced-motion)|color-gamut)$/ . test (
name
) ,
2020-05-07 10:54:55 +00:00
'Unsupported media feature: ' + name
) ;
2019-10-23 11:55:00 +00:00
return true ;
} ) ;
2020-05-07 10:54:55 +00:00
await this . _client . send ( 'Emulation.setEmulatedMedia' , {
features : features ,
} ) ;
2019-10-23 11:55:00 +00:00
}
2017-08-18 23:49:02 +00:00
}
2021-06-23 08:23:36 +00:00
/ * *
* @param timezoneId - Changes the timezone of the page . See
* { @link https : //source.chromium.org/chromium/chromium/deps/icu.git/+/faee8bc70570192d82d2978a71e2a615788597d1:source/data/misc/metaZones.txt | ICU’ s metaZones.txt}
* for a list of supported timezone IDs . Passing
* ` null ` disables timezone emulation .
* /
2020-05-05 12:53:22 +00:00
async emulateTimezone ( timezoneId? : string ) : Promise < void > {
2019-10-23 13:49:39 +00:00
try {
2020-05-07 10:54:55 +00:00
await this . _client . send ( 'Emulation.setTimezoneOverride' , {
timezoneId : timezoneId || '' ,
} ) ;
2020-04-28 13:16:28 +00:00
} catch ( error ) {
if ( error . message . includes ( 'Invalid timezone' ) )
2019-10-23 13:49:39 +00:00
throw new Error ( ` Invalid timezone ID: ${ timezoneId } ` ) ;
2020-04-28 13:16:28 +00:00
throw error ;
2019-10-23 13:49:39 +00:00
}
}
2020-09-14 09:31:23 +00:00
/ * *
* Emulates the idle state .
* If no arguments set , clears idle state emulation .
*
* @example
* ` ` ` js
* // set idle emulation
* await page . emulateIdleState ( { isUserActive : true , isScreenUnlocked : false } ) ;
*
* // do some checks here
* . . .
*
* // clear idle emulation
* await page . emulateIdleState ( ) ;
* ` ` `
*
2021-04-06 08:58:01 +00:00
* @param overrides - Mock idle state . If not set , clears idle overrides
2020-09-14 09:31:23 +00:00
* /
async emulateIdleState ( overrides ? : {
isUserActive : boolean ;
isScreenUnlocked : boolean ;
} ) : Promise < void > {
if ( overrides ) {
await this . _client . send ( 'Emulation.setIdleOverride' , {
isUserActive : overrides.isUserActive ,
isScreenUnlocked : overrides.isScreenUnlocked ,
} ) ;
} else {
await this . _client . send ( 'Emulation.clearIdleOverride' ) ;
}
}
2020-07-22 09:04:53 +00:00
/ * *
* Simulates the given vision deficiency on the page .
*
* @example
* ` ` ` js
* const puppeteer = require ( 'puppeteer' ) ;
*
* ( async ( ) = > {
* const browser = await puppeteer . launch ( ) ;
* const page = await browser . newPage ( ) ;
* await page . goto ( 'https://v8.dev/blog/10-years' ) ;
*
* await page . emulateVisionDeficiency ( 'achromatopsia' ) ;
* await page . screenshot ( { path : 'achromatopsia.png' } ) ;
*
* await page . emulateVisionDeficiency ( 'deuteranopia' ) ;
* await page . screenshot ( { path : 'deuteranopia.png' } ) ;
*
* await page . emulateVisionDeficiency ( 'blurredVision' ) ;
* await page . screenshot ( { path : 'blurred-vision.png' } ) ;
*
* await browser . close ( ) ;
* } ) ( ) ;
* ` ` `
*
* @param type - the type of deficiency to simulate , or ` 'none' ` to reset .
* /
async emulateVisionDeficiency (
type ? : Protocol . Emulation . SetEmulatedVisionDeficiencyRequest [ 'type' ]
) : Promise < void > {
const visionDeficiencies = new Set <
Protocol . Emulation . SetEmulatedVisionDeficiencyRequest [ 'type' ]
> ( [
2020-06-23 05:18:46 +00:00
'none' ,
'achromatopsia' ,
'blurredVision' ,
'deuteranopia' ,
'protanopia' ,
'tritanopia' ,
] ) ;
2020-05-26 15:14:20 +00:00
try {
assert (
! type || visionDeficiencies . has ( type ) ,
` Unsupported vision deficiency: ${ type } `
) ;
await this . _client . send ( 'Emulation.setEmulatedVisionDeficiency' , {
type : type || 'none' ,
} ) ;
} catch ( error ) {
throw error ;
}
}
2021-06-23 08:23:36 +00:00
/ * *
* ` page.setViewport ` will resize the page . A lot of websites don ' t expect
* phones to change size , so you should set the viewport before navigating to
* the page .
*
* In the case of multiple pages in a single browser , each page can have its
* own viewport size .
* @example
* ` ` ` js
* const page = await browser . newPage ( ) ;
* await page . setViewport ( {
* width : 640 ,
* height : 480 ,
* deviceScaleFactor : 1 ,
* } ) ;
* await page . goto ( 'https://example.com' ) ;
* ` ` `
*
2021-07-12 09:42:44 +00:00
* @param viewport -
2021-06-23 08:23:36 +00:00
* @remarks
* Argument viewport have following properties :
*
* - ` width ` : page width in pixels . required
*
* - ` height ` : page height in pixels . required
*
* - ` deviceScaleFactor ` : Specify device scale factor ( can be thought of as
* DPR ) . Defaults to ` 1 ` .
*
* - ` isMobile ` : Whether the meta viewport tag is taken into account . Defaults
* to ` false ` .
*
* - ` hasTouch ` : Specifies if viewport supports touch events . Defaults to ` false `
*
* - ` isLandScape ` : Specifies if viewport is in landscape mode . Defaults to false .
*
* NOTE : in certain cases , setting viewport will reload the page in order to
* set the isMobile or hasTouch properties .
* /
2020-05-06 13:23:07 +00:00
async setViewport ( viewport : Viewport ) : Promise < void > {
2018-02-05 20:51:43 +00:00
const needsReload = await this . _emulationManager . emulateViewport ( viewport ) ;
2017-07-18 01:13:04 +00:00
this . _viewport = viewport ;
2020-05-07 10:54:55 +00:00
if ( needsReload ) await this . reload ( ) ;
2017-06-21 20:51:06 +00:00
}
2021-06-23 08:23:36 +00:00
/ * *
* @returns
*
* - ` width ` : page ' s width in pixels
*
* - ` height ` : page ' s height in pixels
*
* - ` deviceScalarFactor ` : Specify device scale factor ( can be though of as
* dpr ) . Defaults to ` 1 ` .
*
* - ` isMobile ` : Whether the meta viewport tag is taken into account . Defaults
* to ` false ` .
*
* - ` hasTouch ` : Specifies if viewport supports touch events . Defaults to
* ` false ` .
*
* - ` isLandScape ` : Specifies if viewport is in landscape mode . Defaults to
* ` false ` .
* /
2020-05-06 13:23:07 +00:00
viewport ( ) : Viewport | null {
2017-07-18 01:13:04 +00:00
return this . _viewport ;
2017-06-21 20:51:06 +00:00
}
2017-06-21 20:36:04 +00:00
2020-07-09 13:22:58 +00:00
/ * *
* @remarks
*
* Evaluates a function in the page ' s context and returns the result .
*
* If the function passed to ` page.evaluteHandle ` returns a Promise , the
* function will wait for the promise to resolve and return its value .
*
* @example
*
* ` ` ` js
* const result = await frame . evaluate ( ( ) = > {
* return Promise . resolve ( 8 * 7 ) ;
* } ) ;
* console . log ( result ) ; // prints "56"
* ` ` `
*
* You can pass a string instead of a function ( although functions are
* recommended as they are easier to debug and use with TypeScript ) :
*
* @example
* ` ` `
* const aHandle = await page . evaluate ( '1 + 2' ) ;
* ` ` `
*
2020-07-10 10:52:13 +00:00
* To get the best TypeScript experience , you should pass in as the
* generic the type of ` pageFunction ` :
*
* ` ` `
* const aHandle = await page . evaluate < ( ) = > number > ( ( ) = > 2 ) ;
* ` ` `
*
2020-07-09 13:22:58 +00:00
* @example
*
* { @link ElementHandle } instances ( including { @link JSHandle } s ) can be passed
* as arguments to the ` pageFunction ` :
*
* ` ` `
* const bodyHandle = await page . $ ( 'body' ) ;
* const html = await page . evaluate ( body = > body . innerHTML , bodyHandle ) ;
* await bodyHandle . dispose ( ) ;
* ` ` `
*
* @param pageFunction - a function that is run within the page
* @param args - arguments to be passed to the pageFunction
*
* @returns the return value of ` pageFunction ` .
* /
2020-07-10 10:52:13 +00:00
async evaluate < T extends EvaluateFn > (
pageFunction : T ,
. . . args : SerializableOrJSHandle [ ]
) : Promise < UnwrapPromiseLike < EvaluateFnReturnType < T > >> {
return this . _frameManager . mainFrame ( ) . evaluate < T > ( pageFunction , . . . args ) ;
2017-06-21 20:51:06 +00:00
}
2017-06-21 20:36:04 +00:00
2021-06-23 08:23:36 +00:00
/ * *
* Adds a function which would be invoked in one of the following scenarios :
*
* - whenever the page is navigated
*
* - whenever the child frame is attached or navigated . In this case , the
* function is invoked in the context of the newly attached frame .
*
* The function is invoked after the document was created but before any of
* its scripts were run . This is useful to amend the JavaScript environment ,
* e . g . to seed ` Math.random ` .
* @param pageFunction - Function to be evaluated in browser context
* @param args - Arguments to pass to ` pageFunction `
* @example
* An example of overriding the navigator . languages property before the page loads :
* ` ` ` js
* // preload.js
*
* // overwrite the `languages` property to use a custom getter
* Object . defineProperty ( navigator , 'languages' , {
* get : function ( ) {
* return [ 'en-US' , 'en' , 'bn' ] ;
* } ,
* } ) ;
*
* // In your puppeteer script, assuming the preload.js file is
* in same folder of our script
* const preloadFile = fs . readFileSync ( './preload.js' , 'utf8' ) ;
* await page . evaluateOnNewDocument ( preloadFile ) ;
* ` ` `
* /
2020-05-07 10:54:55 +00:00
async evaluateOnNewDocument (
pageFunction : Function | string ,
. . . args : unknown [ ]
) : Promise < void > {
2017-08-21 23:39:04 +00:00
const source = helper . evaluationString ( pageFunction , . . . args ) ;
2020-05-07 10:54:55 +00:00
await this . _client . send ( 'Page.addScriptToEvaluateOnNewDocument' , {
source ,
} ) ;
2017-06-21 20:51:06 +00:00
}
2017-06-20 01:03:01 +00:00
2021-06-23 08:23:36 +00:00
/ * *
* Toggles ignoring cache for each request based on the enabled state . By
* default , caching is enabled .
* @param enabled - sets the ` enabled ` state of cache
* /
2020-05-05 12:53:22 +00:00
async setCacheEnabled ( enabled = true ) : Promise < void > {
2019-04-10 04:42:42 +00:00
await this . _frameManager . networkManager ( ) . setCacheEnabled ( enabled ) ;
2018-02-08 05:58:48 +00:00
}
2021-06-23 08:23:36 +00:00
/ * *
* @remarks
* Options object which might have the following properties :
*
2021-07-09 08:12:01 +00:00
* - ` path ` : The file path to save the image to . The screenshot type
2021-06-23 08:23:36 +00:00
* will be inferred from file extension . If ` path ` is a relative path , then
* it is resolved relative to
* { @link https : //nodejs.org/api/process.html#process_process_cwd
* | current working directory } .
* If no path is provided , the image won ' t be saved to the disk .
*
2021-07-09 08:12:01 +00:00
* - ` type ` : Specify screenshot type , can be either ` jpeg ` or ` png ` .
2021-06-23 08:23:36 +00:00
* Defaults to 'png' .
*
2021-07-09 08:12:01 +00:00
* - ` quality ` : The quality of the image , between 0 - 100 . Not
2021-06-23 08:23:36 +00:00
* applicable to ` png ` images .
*
2021-07-09 08:12:01 +00:00
* - ` fullPage ` : When true , takes a screenshot of the full
2021-06-23 08:23:36 +00:00
* scrollable page . Defaults to ` false `
*
2021-07-09 08:12:01 +00:00
* - ` clip ` : An object which specifies clipping region of the page .
2021-06-23 08:23:36 +00:00
* Should have the following fields : < br / >
2021-07-09 08:12:01 +00:00
* - ` x ` : x - coordinate of top - left corner of clip area . < br / >
* - ` y ` : y - coordinate of top - left corner of clip area . < br / >
* - ` width ` : width of clipping area . < br / >
* - ` height ` : height of clipping area .
2021-06-23 08:23:36 +00:00
*
2021-07-09 08:12:01 +00:00
* - ` omitBackground ` : Hides default white background and allows
2021-06-23 08:23:36 +00:00
* capturing screenshots with transparency . Defaults to ` false `
*
2021-07-09 08:12:01 +00:00
* - ` encoding ` : The encoding of the image , can be either base64 or
2021-06-23 08:23:36 +00:00
* binary . Defaults to ` binary ` .
*
*
* NOTE : Screenshots take at least 1 / 6 second on OS X . See
* { @link https : //crbug.com/741689} for discussion.
* @returns Promise which resolves to buffer or a base64 string ( depending on
* the value of ` encoding ` ) with captured screenshot .
* /
2021-09-23 12:37:35 +00:00
async screenshot ( options : ScreenshotOptions = { } ) : Promise < Buffer | string > {
2017-06-22 20:38:10 +00:00
let screenshotType = null ;
2017-12-04 22:04:36 +00:00
// options.type takes precedence over inferring the type from options.path
2020-06-19 14:39:03 +00:00
// because it may be a 0-length file with no extension created beforehand
// (i.e. as a temp file).
2017-12-04 22:04:36 +00:00
if ( options . type ) {
2021-09-21 14:26:20 +00:00
const type = options . type ;
if ( type !== 'png' && type !== 'jpeg' && type !== 'webp' ) {
assertNever ( type , 'Unknown options.type value: ' + type ) ;
}
2017-12-04 22:04:36 +00:00
screenshotType = options . type ;
} else if ( options . path ) {
2020-09-14 11:39:33 +00:00
const filePath = options . path ;
const extension = filePath
. slice ( filePath . lastIndexOf ( '.' ) + 1 )
. toLowerCase ( ) ;
if ( extension === 'png' ) screenshotType = 'png' ;
else if ( extension === 'jpg' || extension === 'jpeg' )
screenshotType = 'jpeg' ;
2021-09-21 14:26:20 +00:00
else if ( extension === 'webp' ) screenshotType = 'webp' ;
2020-09-14 11:39:33 +00:00
assert (
screenshotType ,
` Unsupported screenshot type for extension \` . ${ extension } \` `
) ;
2017-06-21 20:51:06 +00:00
}
2017-12-04 22:04:36 +00:00
2020-05-07 10:54:55 +00:00
if ( ! screenshotType ) screenshotType = 'png' ;
2017-06-21 20:51:06 +00:00
if ( options . quality ) {
2020-05-07 10:54:55 +00:00
assert (
2021-10-07 21:15:24 +00:00
screenshotType === 'jpeg' || screenshotType === 'webp' ,
2020-05-07 10:54:55 +00:00
'options.quality is unsupported for the ' +
screenshotType +
' screenshots'
) ;
assert (
typeof options . quality === 'number' ,
'Expected options.quality to be a number but found ' +
typeof options . quality
) ;
assert (
Number . isInteger ( options . quality ) ,
'Expected options.quality to be an integer'
) ;
assert (
options . quality >= 0 && options . quality <= 100 ,
'Expected options.quality to be between 0 and 100 (inclusive), got ' +
options . quality
) ;
2017-06-21 20:51:06 +00:00
}
2020-05-07 10:54:55 +00:00
assert (
! options . clip || ! options . fullPage ,
'options.clip and options.fullPage are exclusive'
) ;
2017-06-21 20:51:06 +00:00
if ( options . clip ) {
2020-05-07 10:54:55 +00:00
assert (
typeof options . clip . x === 'number' ,
'Expected options.clip.x to be a number but found ' +
typeof options . clip . x
) ;
assert (
typeof options . clip . y === 'number' ,
'Expected options.clip.y to be a number but found ' +
typeof options . clip . y
) ;
assert (
typeof options . clip . width === 'number' ,
'Expected options.clip.width to be a number but found ' +
typeof options . clip . width
) ;
assert (
typeof options . clip . height === 'number' ,
'Expected options.clip.height to be a number but found ' +
typeof options . clip . height
) ;
assert (
options . clip . width !== 0 ,
'Expected options.clip.width not to be 0.'
) ;
assert (
options . clip . height !== 0 ,
'Expected options.clip.height not to be 0.'
) ;
2017-06-21 20:51:06 +00:00
}
2020-05-07 13:49:42 +00:00
return this . _screenshotTaskQueue . postTask ( ( ) = >
this . _screenshotTask ( screenshotType , options )
2020-05-07 10:54:55 +00:00
) ;
2017-06-21 20:51:06 +00:00
}
2020-06-12 10:10:12 +00:00
private async _screenshotTask (
2021-09-21 14:26:20 +00:00
format : Protocol.Page.CaptureScreenshotRequestFormat ,
2020-05-07 10:54:55 +00:00
options? : ScreenshotOptions
) : Promise < Buffer | string > {
await this . _client . send ( 'Target.activateTarget' , {
targetId : this._target._targetId ,
} ) ;
2019-01-15 22:34:31 +00:00
let clip = options . clip ? processClip ( options . clip ) : undefined ;
2021-04-06 13:14:31 +00:00
let { captureBeyondViewport = true } = options ;
captureBeyondViewport =
typeof captureBeyondViewport === 'boolean' ? captureBeyondViewport : true ;
2017-07-26 22:28:44 +00:00
2017-07-17 19:15:06 +00:00
if ( options . fullPage ) {
2017-07-18 01:13:04 +00:00
const metrics = await this . _client . send ( 'Page.getLayoutMetrics' ) ;
2021-07-01 11:23:38 +00:00
// Fallback to `contentSize` in case of using Firefox.
const { width , height } = metrics . cssContentSize || metrics . contentSize ;
2017-08-11 01:42:30 +00:00
2021-02-03 13:30:46 +00:00
// Overwrite clip for full page.
2020-05-07 10:54:55 +00:00
clip = { x : 0 , y : 0 , width , height , scale : 1 } ;
2021-04-06 13:14:31 +00:00
if ( ! captureBeyondViewport ) {
2021-05-12 14:48:30 +00:00
const {
isMobile = false ,
deviceScaleFactor = 1 ,
isLandscape = false ,
} = this . _viewport || { } ;
const screenOrientation : Protocol.Emulation.ScreenOrientation =
isLandscape
? { angle : 90 , type : 'landscapePrimary' }
: { angle : 0 , type : 'portraitPrimary' } ;
2021-04-06 13:14:31 +00:00
await this . _client . send ( 'Emulation.setDeviceMetricsOverride' , {
mobile : isMobile ,
width ,
height ,
deviceScaleFactor ,
screenOrientation ,
} ) ;
}
2017-06-21 20:51:06 +00:00
}
2020-05-07 10:54:55 +00:00
const shouldSetDefaultBackground =
2021-09-21 14:26:20 +00:00
options . omitBackground && ( format === 'png' || format === 'webp' ) ;
2021-03-18 19:57:32 +00:00
if ( shouldSetDefaultBackground ) {
await this . _setTransparentBackgroundColor ( ) ;
}
2021-04-06 13:14:31 +00:00
2020-05-07 10:54:55 +00:00
const result = await this . _client . send ( 'Page.captureScreenshot' , {
format ,
quality : options.quality ,
clip ,
2021-04-06 13:14:31 +00:00
captureBeyondViewport ,
2020-05-07 10:54:55 +00:00
} ) ;
2021-03-18 19:57:32 +00:00
if ( shouldSetDefaultBackground ) {
await this . _resetDefaultBackgroundColor ( ) ;
}
2017-07-17 19:15:06 +00:00
2018-09-27 17:50:21 +00:00
if ( options . fullPage && this . _viewport )
2017-07-18 01:13:04 +00:00
await this . setViewport ( this . _viewport ) ;
2017-07-17 19:15:06 +00:00
2020-05-07 10:54:55 +00:00
const buffer =
options . encoding === 'base64'
? result . data
: Buffer . from ( result . data , 'base64' ) ;
2021-03-15 09:05:15 +00:00
if ( options . path ) {
if ( ! isNode ) {
throw new Error (
'Screenshots can only be written to a file path in a Node environment.'
) ;
}
const fs = await helper . importFSModule ( ) ;
await fs . promises . writeFile ( options . path , buffer ) ;
2020-09-28 09:35:35 +00:00
}
2017-06-21 20:51:06 +00:00
return buffer ;
2019-01-15 22:34:31 +00:00
2020-05-07 10:54:55 +00:00
function processClip (
clip : ScreenshotClip
) : ScreenshotClip & { scale : number } {
2019-01-15 22:34:31 +00:00
const x = Math . round ( clip . x ) ;
const y = Math . round ( clip . y ) ;
const width = Math . round ( clip . width + clip . x - x ) ;
const height = Math . round ( clip . height + clip . y - y ) ;
2020-05-07 10:54:55 +00:00
return { x , y , width , height , scale : 1 } ;
2019-01-15 22:34:31 +00:00
}
2017-06-21 20:51:06 +00:00
}
2020-07-17 12:58:56 +00:00
/ * *
2022-05-02 06:21:19 +00:00
* Generates a PDF of the page with the ` print ` CSS media type .
2020-07-17 12:58:56 +00:00
* @remarks
*
2021-06-23 08:23:36 +00:00
* NOTE : PDF generation is only supported in Chrome headless mode .
2020-07-17 12:58:56 +00:00
*
* To generate a PDF with the ` screen ` media type , call
* { @link Page . emulateMediaType | ` page.emulateMediaType('screen') ` } before
* calling ` page.pdf() ` .
*
* By default , ` page.pdf() ` generates a pdf with modified colors for printing .
* Use the
* { @link https : //developer.mozilla.org/en-US/docs/Web/CSS/-webkit-print-color-adjust | `-webkit-print-color-adjust`}
* property to force rendering of exact colors .
*
*
* @param options - options for generating the PDF .
* /
2021-06-23 12:51:38 +00:00
async createPDFStream ( options : PDFOptions = { } ) : Promise < Readable > {
2018-11-12 20:59:21 +00:00
const {
scale = 1 ,
displayHeaderFooter = false ,
headerTemplate = '' ,
footerTemplate = '' ,
printBackground = false ,
landscape = false ,
pageRanges = '' ,
preferCSSPageSize = false ,
margin = { } ,
2021-03-18 19:57:32 +00:00
omitBackground = false ,
2021-09-14 16:11:14 +00:00
timeout = 30000 ,
2018-11-12 20:59:21 +00:00
} = options ;
2017-06-21 20:51:06 +00:00
2017-06-22 20:38:10 +00:00
let paperWidth = 8.5 ;
let paperHeight = 11 ;
2017-06-21 20:51:06 +00:00
if ( options . format ) {
2020-05-05 12:53:22 +00:00
const format = paperFormats [ options . format . toLowerCase ( ) ] ;
2018-05-31 23:53:51 +00:00
assert ( format , 'Unknown paper format: ' + options . format ) ;
2017-06-21 20:51:06 +00:00
paperWidth = format . width ;
paperHeight = format . height ;
} else {
paperWidth = convertPrintParameterToInches ( options . width ) || paperWidth ;
2020-05-07 10:54:55 +00:00
paperHeight =
convertPrintParameterToInches ( options . height ) || paperHeight ;
2017-06-21 20:51:06 +00:00
}
2018-11-12 20:59:21 +00:00
const marginTop = convertPrintParameterToInches ( margin . top ) || 0 ;
const marginLeft = convertPrintParameterToInches ( margin . left ) || 0 ;
const marginBottom = convertPrintParameterToInches ( margin . bottom ) || 0 ;
const marginRight = convertPrintParameterToInches ( margin . right ) || 0 ;
2017-06-21 20:51:06 +00:00
2021-03-18 19:57:32 +00:00
if ( omitBackground ) {
await this . _setTransparentBackgroundColor ( ) ;
}
2021-09-14 16:11:14 +00:00
const printCommandPromise = this . _client . send ( 'Page.printToPDF' , {
2019-06-15 05:36:06 +00:00
transferMode : 'ReturnAsStream' ,
2018-11-12 20:59:21 +00:00
landscape ,
displayHeaderFooter ,
headerTemplate ,
footerTemplate ,
printBackground ,
scale ,
paperWidth ,
paperHeight ,
marginTop ,
marginBottom ,
marginLeft ,
marginRight ,
pageRanges ,
2020-05-07 10:54:55 +00:00
preferCSSPageSize ,
2017-06-21 20:51:06 +00:00
} ) ;
2021-03-18 19:57:32 +00:00
2021-09-14 16:11:14 +00:00
const result = await helper . waitWithTimeout (
printCommandPromise ,
'Page.printToPDF' ,
timeout
) ;
2021-03-18 19:57:32 +00:00
if ( omitBackground ) {
await this . _resetDefaultBackgroundColor ( ) ;
}
2021-06-23 12:51:38 +00:00
return helper . getReadableFromProtocolStream ( this . _client , result . stream ) ;
}
/ * *
2021-07-19 09:39:40 +00:00
* @param options -
* @returns
2021-06-23 12:51:38 +00:00
* /
async pdf ( options : PDFOptions = { } ) : Promise < Buffer > {
const { path = undefined } = options ;
const readable = await this . createPDFStream ( options ) ;
return await helper . getReadableAsBuffer ( readable , path ) ;
2017-06-21 20:51:06 +00:00
}
2021-06-23 08:23:36 +00:00
/ * *
* @returns The page ' s title
* @remarks
* Shortcut for { @link Frame . title | page . mainFrame ( ) . title ( ) } .
* /
2020-05-05 12:53:22 +00:00
async title ( ) : Promise < string > {
2017-07-25 18:37:46 +00:00
return this . mainFrame ( ) . title ( ) ;
2017-06-21 20:51:06 +00:00
}
2017-05-11 07:06:41 +00:00
2020-05-07 10:54:55 +00:00
async close (
options : { runBeforeUnload? : boolean } = { runBeforeUnload : undefined }
) : Promise < void > {
assert (
! ! this . _client . _connection ,
'Protocol error: Connection closed. Most likely the page has been closed.'
) ;
2018-05-02 22:51:45 +00:00
const runBeforeUnload = ! ! options . runBeforeUnload ;
if ( runBeforeUnload ) {
await this . _client . send ( 'Page.close' ) ;
} else {
2020-05-07 10:54:55 +00:00
await this . _client . _connection . send ( 'Target.closeTarget' , {
targetId : this._target._targetId ,
} ) ;
2018-05-02 22:51:45 +00:00
await this . _target . _isClosedPromise ;
}
2017-06-21 20:51:06 +00:00
}
2017-06-28 01:27:22 +00:00
2021-06-23 08:23:36 +00:00
/ * *
* Indicates that the page has been closed .
* @returns
* /
2020-05-05 12:53:22 +00:00
isClosed ( ) : boolean {
2018-05-25 23:53:57 +00:00
return this . _closed ;
}
2020-05-05 12:53:22 +00:00
get mouse ( ) : Mouse {
2017-07-22 03:29:31 +00:00
return this . _mouse ;
}
2021-06-02 12:47:20 +00:00
/ * *
* This method fetches an element with ` selector ` , scrolls it into view if
2021-06-23 08:23:36 +00:00
* needed , and then uses { @link Page . mouse } to click in the center of the
2021-06-02 12:47:20 +00:00
* element . If there ' s no element matching ` selector ` , the method throws an
* error .
* @remarks 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 :
* ` ` ` js
* const [ response ] = await Promise . all ( [
* page . waitForNavigation ( waitOptions ) ,
* page . click ( selector , clickOptions ) ,
* ] ) ;
* ` ` `
* Shortcut for { @link Frame . click | page . mainFrame ( ) . click ( selector [ , options ] ) } .
* @param selector - A ` selector ` to search for element to click . If there are
* multiple elements satisfying the ` selector ` , the first will be clicked
* @param options - ` Object `
* @returns Promise which resolves when the element matching ` selector ` is
* successfully clicked . The Promise will be rejected if there is no element
* matching ` selector ` .
* /
2020-05-07 10:54:55 +00:00
click (
selector : string ,
options : {
delay? : number ;
2020-07-02 11:15:39 +00:00
button? : MouseButton ;
2020-05-07 10:54:55 +00:00
clickCount? : number ;
} = { }
) : Promise < void > {
2018-02-05 22:58:03 +00:00
return this . mainFrame ( ) . click ( selector , options ) ;
2017-07-22 03:29:31 +00:00
}
2021-06-23 08:23:36 +00:00
/ * *
* This method fetches an element with ` selector ` and focuses it . If there ' s no
* element matching ` selector ` , the method throws an error .
* @param selector - A
* { @link https : //developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | selector }
* of an element to focus . If there are multiple elements satisfying the
* selector , the first will be focused .
* @returns Promise which resolves when the element matching selector is
* successfully focused . The promise will be rejected if there is no element
* matching selector .
* @remarks
* Shortcut for { @link Frame . focus | page . mainFrame ( ) . focus ( selector ) } .
* /
2020-05-05 12:53:22 +00:00
focus ( selector : string ) : Promise < void > {
2018-02-05 22:58:03 +00:00
return this . mainFrame ( ) . focus ( selector ) ;
2017-06-28 01:27:22 +00:00
}
2021-06-23 08:23:36 +00:00
/ * *
* This method fetches an element with ` selector ` , scrolls it into view if
* needed , and then uses { @link Page . mouse } to hover over the center of the element .
* If there ' s no element matching ` selector ` , the method throws an error .
* @param selector - A
* { @link https : //developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | selector}
* to search for element to hover . If there are multiple elements satisfying
* the selector , the first will be hovered .
* @returns Promise which resolves when the element matching ` selector ` is
* successfully hovered . Promise gets rejected if there ' s no element matching
* ` selector ` .
* @remarks
* Shortcut for { @link Page . hover | page . mainFrame ( ) . hover ( selector ) } .
* /
2020-05-05 12:53:22 +00:00
hover ( selector : string ) : Promise < void > {
2018-02-05 22:58:03 +00:00
return this . mainFrame ( ) . hover ( selector ) ;
2017-06-28 01:27:22 +00:00
}
2021-06-23 08:23:36 +00:00
/ * *
* Triggers a ` change ` and ` input ` event once all the provided options have been
* selected . If there ' s no ` <select> ` element matching ` selector ` , the method
* throws an error .
*
* @example
* ` ` ` js
* page . select ( 'select#colors' , 'blue' ) ; // single selection
* page . select ( 'select#colors' , 'red' , 'green' , 'blue' ) ; // multiple selections
* ` ` `
2021-07-12 09:42:44 +00:00
* @param selector - A
* { @link https : //developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | Selector}
* to query the page for
2021-06-23 08:23:36 +00:00
* @param values - Values of options to select . If the ` <select> ` has the
* ` multiple ` attribute , all values are considered , otherwise only the first one
* is taken into account .
* @returns
*
* @remarks
* Shortcut for { @link Frame . select | page . mainFrame ( ) . select ( ) }
* /
2020-05-05 12:53:22 +00:00
select ( selector : string , . . . values : string [ ] ) : Promise < string [ ] > {
2017-11-02 05:06:04 +00:00
return this . mainFrame ( ) . select ( selector , . . . values ) ;
2017-09-25 09:23:34 +00:00
}
2021-06-23 08:23:36 +00:00
/ * *
* This method fetches an element with ` selector ` , scrolls it into view if
* needed , and then uses { @link Page . touchscreen } to tap in the center of the element .
* If there ' s no element matching ` selector ` , the method throws an error .
* @param selector - A
* { @link https : //developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | Selector}
* to search for element to tap . If there are multiple elements satisfying the
* selector , the first will be tapped .
* @returns
* @remarks
* Shortcut for { @link Frame . tap | page . mainFrame ( ) . tap ( selector ) } .
* /
2020-05-05 12:53:22 +00:00
tap ( selector : string ) : Promise < void > {
2018-02-05 22:58:03 +00:00
return this . mainFrame ( ) . tap ( selector ) ;
}
2021-06-23 08:23:36 +00:00
/ * *
* Sends a ` keydown ` , ` keypress/input ` , and ` keyup ` event for each character
* in the text .
*
* To press a special key , like ` Control ` or ` ArrowDown ` , use { @link Keyboard . press } .
* @example
* ` ` `
* await page . type ( '#mytextarea' , 'Hello' ) ;
* // Types instantly
* await page . type ( '#mytextarea' , 'World' , { delay : 100 } ) ;
* // Types slower, like a user
* ` ` `
* @param selector - A
* { @link https : //developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | selector}
* of an element to type into . If there are multiple elements satisfying the
* selector , the first will be used .
* @param text - A text to type into a focused element .
* @param options - have property ` delay ` which is the Time to wait between
* key presses in milliseconds . Defaults to ` 0 ` .
* @returns
2021-07-12 09:42:44 +00:00
* @remarks
2021-06-23 08:23:36 +00:00
* /
2020-05-07 10:54:55 +00:00
type (
selector : string ,
text : string ,
options ? : { delay : number }
) : Promise < void > {
2018-02-05 22:58:03 +00:00
return this . mainFrame ( ) . type ( selector , text , options ) ;
2017-07-19 21:43:07 +00:00
}
2020-07-28 08:37:49 +00:00
/ * *
* @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
* { @link Page . waitForSelector } or { @link Page . waitForXPath } .
*
* If the first argument is a function this method is a shortcut for
* { @link Page . 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 - a selector , predicate or timeout to
* wait for .
* @param options - optional waiting parameters .
* @param args - arguments to pass to ` pageFunction ` .
*
* @deprecated Don ' t use this method directly . Instead use the more explicit
* methods available : { @link Page . waitForSelector } ,
* { @link Page . waitForXPath } , { @link Page . waitForFunction } or
* { @link Page . waitForTimeout } .
* /
2020-05-07 10:54:55 +00:00
waitFor (
selectorOrFunctionOrTimeout : string | number | Function ,
options : {
visible? : boolean ;
hidden? : boolean ;
timeout? : number ;
polling? : string | number ;
} = { } ,
2020-07-01 11:44:08 +00:00
. . . args : SerializableOrJSHandle [ ]
2020-05-07 10:54:55 +00:00
) : Promise < JSHandle > {
return this . mainFrame ( ) . waitFor (
selectorOrFunctionOrTimeout ,
options ,
. . . args
) ;
}
2020-07-28 08:37:49 +00:00
/ * *
* Causes your script to wait for the given number of milliseconds .
*
* @remarks
*
* It ' s generally recommended to not wait for a number of seconds , but instead
* use { @link Page . waitForSelector } , { @link Page . waitForXPath } or
* { @link Page . waitForFunction } to wait for exactly the conditions you want .
*
* @example
*
* Wait for 1 second :
*
* ` ` `
* await page . waitForTimeout ( 1000 ) ;
* ` ` `
*
* @param milliseconds - the number of milliseconds to wait .
* /
waitForTimeout ( milliseconds : number ) : Promise < void > {
return this . mainFrame ( ) . waitForTimeout ( milliseconds ) ;
}
2021-06-25 12:40:53 +00:00
/ * *
* Wait for the ` selector ` to appear in page . If at the moment of calling the
* method the ` selector ` already exists , the method will return immediately . If
* the ` selector ` doesn ' t appear after the ` timeout ` milliseconds of waiting , the
* function will throw .
*
* This method works across navigations :
* ` ` ` js
* const puppeteer = require ( 'puppeteer' ) ;
* ( async ( ) = > {
* const browser = await puppeteer . launch ( ) ;
* const page = await browser . newPage ( ) ;
* let currentURL ;
* page
* . waitForSelector ( 'img' )
* . then ( ( ) = > console . log ( 'First URL with image: ' + currentURL ) ) ;
* for ( currentURL of [
* 'https://example.com' ,
* 'https://google.com' ,
* 'https://bbc.com' ,
* ] ) {
* await page . goto ( currentURL ) ;
* }
* await browser . close ( ) ;
* } ) ( ) ;
* ` ` `
* @param selector - A
* { @link https : //developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | selector}
* of an element to wait for
* @param options - Optional waiting parameters
* @returns Promise which resolves when element specified by selector string
* is added to DOM . Resolves to ` null ` if waiting for hidden : ` true ` and
* selector is not found in DOM .
* @remarks
* The optional Parameter in Arguments ` options ` are :
*
* - ` Visible ` : A boolean wait for element to be present in DOM and to be
* visible , i . e . to not have ` display: none ` or ` visibility: hidden ` CSS
* properties . Defaults to ` false ` .
*
* - ` hidden ` : ait for element to not be found in the DOM or to be hidden ,
* i . e . have ` display: none ` or ` visibility: hidden ` CSS properties . Defaults to
* ` false ` .
*
* - ` timeout ` : maximum time to wait for in milliseconds . Defaults to ` 30000 `
* ( 30 seconds ) . Pass ` 0 ` to disable timeout . The default value can be changed
* by using the { @link Page . setDefaultTimeout } method .
* /
2020-05-07 10:54:55 +00:00
waitForSelector (
selector : string ,
options : {
visible? : boolean ;
hidden? : boolean ;
timeout? : number ;
} = { }
) : Promise < ElementHandle | null > {
2017-07-21 19:41:49 +00:00
return this . mainFrame ( ) . waitForSelector ( selector , options ) ;
2017-07-07 22:39:02 +00:00
}
2017-07-10 18:21:46 +00:00
2021-06-25 12:40:53 +00:00
/ * *
* 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 .
*
* This method works across navigation
* ` ` ` js
* const puppeteer = require ( 'puppeteer' ) ;
* ( async ( ) = > {
* const browser = await puppeteer . launch ( ) ;
* const page = await browser . newPage ( ) ;
* let currentURL ;
* page
* . waitForXPath ( '//img' )
* . then ( ( ) = > console . log ( 'First URL with image: ' + currentURL ) ) ;
* for ( currentURL of [
* 'https://example.com' ,
* 'https://google.com' ,
* 'https://bbc.com' ,
* ] ) {
* await page . goto ( currentURL ) ;
* }
* await browser . close ( ) ;
* } ) ( ) ;
* ` ` `
* @param xpath - A
* { @link https : //developer.mozilla.org/en-US/docs/Web/XPath | xpath} of an
* element to wait for
* @param options - Optional waiting parameters
* @returns Promise which resolves when element specified by xpath string is
* added to DOM . Resolves to ` null ` if waiting for ` hidden: true ` and xpath is
* not found in DOM .
* @remarks
* The optional Argument ` options ` have properties :
*
* - ` visible ` : A boolean to wait for element to be present in DOM and to be
* visible , i . e . to not have ` display: none ` or ` visibility: hidden ` CSS
* properties . Defaults to ` false ` .
*
* - ` hidden ` : A boolean wait for element to not be found in the DOM or to be
* hidden , i . e . have ` display: none ` or ` visibility: hidden ` CSS properties .
* Defaults to ` false ` .
*
* - ` timeout ` : A number which is maximum time to wait for in milliseconds .
* Defaults to ` 30000 ` ( 30 seconds ) . Pass ` 0 ` to disable timeout . The default
* value can be changed by using the { @link Page . setDefaultTimeout } method .
* /
2020-05-07 10:54:55 +00:00
waitForXPath (
xpath : string ,
options : {
visible? : boolean ;
hidden? : boolean ;
timeout? : number ;
} = { }
) : Promise < ElementHandle | null > {
2018-01-22 23:16:20 +00:00
return this . mainFrame ( ) . waitForXPath ( xpath , options ) ;
}
2021-06-25 12:40:53 +00:00
/ * *
* The ` waitForFunction ` can be used to observe viewport size change :
*
* ` ` `
* const puppeteer = require ( 'puppeteer' ) ;
* ( async ( ) = > {
* const browser = await puppeteer . launch ( ) ;
* const page = await browser . newPage ( ) ;
* const watchDog = page . waitForFunction ( 'window.innerWidth < 100' ) ;
* await page . setViewport ( { width : 50 , height : 50 } ) ;
* await watchDog ;
* await browser . close ( ) ;
* } ) ( ) ;
* ` ` `
* To pass arguments from node . js to the predicate of ` page.waitForFunction ` function :
* ` ` `
* const selector = '.foo' ;
* await page . waitForFunction (
* ( selector ) = > ! ! document . querySelector ( selector ) ,
* { } ,
* selector
* ) ;
* ` ` `
* The predicate of ` page.waitForFunction ` can be asynchronous too :
* ` ` `
* const username = 'github-username' ;
* await page . waitForFunction (
* async ( username ) = > {
* const githubResponse = await fetch (
* ` https://api.github.com/users/ ${ username } `
* ) ;
* const githubUser = await githubResponse . json ( ) ;
* // show the avatar
* const img = document . createElement ( 'img' ) ;
* img . src = githubUser . avatar_url ;
* // wait 3 seconds
* await new Promise ( ( resolve , reject ) = > setTimeout ( resolve , 3000 ) ) ;
* img . remove ( ) ;
* } ,
* { } ,
* username
* ) ;
* ` ` `
* @param pageFunction - Function to be evaluated in browser context
* @param options - Optional waiting parameters
* @param args - Arguments to pass to ` pageFunction `
* @returns Promise which resolves when the ` pageFunction ` returns a truthy
* value . It resolves to a JSHandle of the truthy value .
*
* The optional waiting parameter can be :
*
* - ` Polling ` : 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 : < br / >
* - ` raf ` : to constantly execute ` pageFunction ` in ` requestAnimationFrame `
* callback . This is the tightest polling mode which is suitable to
* observe styling changes . < br / >
* - ` mutation ` : to execute pageFunction on every DOM mutation .
*
* - ` timeout ` : maximum time to wait for in milliseconds . Defaults to ` 30000 `
* ( 30 seconds ) . Pass ` 0 ` to disable timeout . The default value can be changed
* by using the
* { @link Page . setDefaultTimeout | page . setDefaultTimeout ( timeout ) } method .
*
* /
2020-05-07 10:54:55 +00:00
waitForFunction (
2020-06-23 05:18:46 +00:00
pageFunction : Function | string ,
2020-05-07 10:54:55 +00:00
options : {
timeout? : number ;
polling? : string | number ;
} = { } ,
2020-07-01 11:44:08 +00:00
. . . args : SerializableOrJSHandle [ ]
2020-05-07 10:54:55 +00:00
) : Promise < JSHandle > {
2017-07-27 22:17:43 +00:00
return this . mainFrame ( ) . waitForFunction ( pageFunction , options , . . . args ) ;
}
2017-05-11 07:06:41 +00:00
}
2020-05-05 12:53:22 +00:00
const supportedMetrics = new Set < string > ( [
2017-10-10 21:50:38 +00:00
'Timestamp' ,
2017-10-12 08:17:06 +00:00
'Documents' ,
'Frames' ,
'JSEventListeners' ,
'Nodes' ,
2017-10-10 21:50:38 +00:00
'LayoutCount' ,
'RecalcStyleCount' ,
'LayoutDuration' ,
'RecalcStyleDuration' ,
'ScriptDuration' ,
'TaskDuration' ,
'JSHeapUsedSize' ,
'JSHeapTotalSize' ,
] ) ;
2017-08-21 23:39:04 +00:00
const unitToPixels = {
2020-05-07 10:54:55 +00:00
px : 1 ,
in : 96 ,
cm : 37.8 ,
mm : 3.78 ,
2017-05-14 18:29:42 +00:00
} ;
2020-05-07 10:54:55 +00:00
function convertPrintParameterToInches (
parameter? : string | number
) : number | undefined {
if ( typeof parameter === 'undefined' ) return undefined ;
2017-06-22 20:38:10 +00:00
let pixels ;
2017-07-27 22:17:43 +00:00
if ( helper . isNumber ( parameter ) ) {
2017-06-21 20:51:06 +00:00
// Treat numbers as pixel values to be aligned with phantom's paperSize.
2020-05-07 10:54:55 +00:00
pixels = /** @type {number} */ parameter ;
2017-07-27 22:17:43 +00:00
} else if ( helper . isString ( parameter ) ) {
2020-05-07 10:54:55 +00:00
const text = /** @type {string} */ parameter ;
2017-06-22 20:38:10 +00:00
let unit = text . substring ( text . length - 2 ) . toLowerCase ( ) ;
let valueText = '' ;
2017-06-21 20:51:06 +00:00
if ( unitToPixels . hasOwnProperty ( unit ) ) {
valueText = text . substring ( 0 , text . length - 2 ) ;
2017-05-14 18:29:42 +00:00
} else {
2017-06-21 20:51:06 +00:00
// In case of unknown unit try to parse the whole parameter as number of pixels.
// This is consistent with phantom's paperSize behavior.
unit = 'px' ;
valueText = text ;
}
2017-08-21 23:39:04 +00:00
const value = Number ( valueText ) ;
2018-05-31 23:53:51 +00:00
assert ( ! isNaN ( value ) , 'Failed to parse parameter value: ' + text ) ;
2017-06-21 20:51:06 +00:00
pixels = value * unitToPixels [ unit ] ;
} else {
2020-05-07 10:54:55 +00:00
throw new Error (
'page.pdf() Cannot handle parameter type: ' + typeof parameter
) ;
2017-06-21 20:51:06 +00:00
}
return pixels / 96 ;
2017-06-11 08:32:59 +00:00
}