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 .
* /
2020-07-13 09:22:26 +00:00
import { EventEmitter } from './EventEmitter.js' ;
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' ;
import { assert } from './assert.js' ;
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' ;
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
* /
2020-05-05 12:53:22 +00:00
type ? : 'png' | 'jpeg' ;
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 {
2020-07-06 10:34:55 +00:00
/** Emitted when the page closes. */
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-05-07 13:49:42 +00:00
class ScreenshotTaskQueue {
_chain : Promise < Buffer | string | void > ;
constructor ( ) {
this . _chain = Promise . resolve < Buffer | string | void > ( undefined ) ;
}
public postTask (
task : ( ) = > Promise < Buffer | string >
) : Promise < Buffer | string | void > {
const result = this . _chain . then ( task ) ;
this . _chain = result . catch ( ( ) = > { } ) ;
return result ;
}
}
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 ,
2020-05-07 13:49:42 +00:00
defaultViewport : Viewport | null
2020-05-07 10:54:55 +00:00
) : Promise < Page > {
2020-05-07 13:49:42 +00:00
const page = new Page ( client , target , ignoreHTTPSErrors ) ;
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 ;
private _screenshotTaskQueue : ScreenshotTaskQueue ;
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 > ;
2020-05-05 12:53:22 +00:00
2020-06-12 10:10:12 +00:00
/ * *
* @internal
* /
2020-05-07 13:49:42 +00:00
constructor ( client : CDPSession , target : Target , ignoreHTTPSErrors : boolean ) {
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 ) ;
2020-05-07 13:49:42 +00:00
this . _screenshotTaskQueue = new ScreenshotTaskQueue ( ) ;
2020-05-05 12:53:22 +00:00
this . _viewport = null ;
2017-06-21 20:51:06 +00:00
2020-05-07 10:54:55 +00:00
client . on ( 'Target.attachedToTarget' , ( event ) = > {
2021-05-07 08:31:39 +00:00
if (
event . targetInfo . type !== 'worker' &&
event . targetInfo . type !== 'iframe'
) {
2018-05-21 21:31:11 +00:00
// If we don't detach from service workers, they will never die.
2021-05-07 08:31:39 +00:00
// 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/+/master:content/browser/devtools/devtools_agent_host_impl.cc?q=f:devtools%20-f:out%20%22::kTypePage%5B%5D%22&ss=chromium
// for the complete list of available types.
2020-05-07 10:54:55 +00:00
client
. send ( 'Target.detachFromTarget' , {
sessionId : event.sessionId ,
} )
. catch ( debugError ) ;
2018-05-21 21:31:11 +00:00
return ;
}
2019-01-22 23:10:11 +00:00
const session = Connection . fromSession ( client ) . session ( event . sessionId ) ;
2020-05-29 11:57:54 +00:00
const worker = new WebWorker (
2020-05-07 10:54:55 +00:00
session ,
event . targetInfo . url ,
this . _addConsoleMessage . bind ( this ) ,
this . _handleException . bind ( this )
) ;
2018-05-21 21:31:11 +00:00
this . _workers . set ( event . sessionId , worker ) ;
2020-06-12 10:10:12 +00:00
this . emit ( PageEmittedEvents . WorkerCreated , worker ) ;
2018-05-21 21:31:11 +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
}
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 .
* /
public on < 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 . 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 ) ;
}
2020-06-26 07:24:56 +00:00
/ * *
* @param options - Optional waiting parameters
* @returns Resolves after a page requests a file picker .
* /
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 ;
2019-07-23 04:30:49 +00:00
let callback ;
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
* Consider using { @link BrowserContext . overridePermissions } to grant
* 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 ;
}
2020-06-26 07:24:56 +00:00
/ * *
* @returns The browser this page belongs to .
* /
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
/ * *
* @returns The browser context that the page belongs to
* /
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 .
* /
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
* { @link https : //developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API | WebWorkers}
* associated with the page .
* /
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 .
2021-03-17 14:42:35 +00:00
* @param cacheSafe - Whether to trust browser caching . If set to false ,
* enabling request interception disables page caching . Defaults to false .
2020-06-26 07:24:56 +00:00
*
* @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-03-17 14:42:35 +00:00
async setRequestInterception (
value : boolean ,
cacheSafe = false
) : Promise < void > {
return this . _frameManager
. networkManager ( )
. setRequestInterception ( value , cacheSafe ) ;
2017-06-21 20:51:06 +00:00
}
2017-06-15 07:20:37 +00:00
2020-06-26 07:24:56 +00:00
/ * *
* @param enabled - When ` true ` , enables offline mode for the page .
* /
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-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
/ * *
* @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 ) } .
*
* @param selector - A
* { @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
*
* @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 .
* /
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 :
* https : //github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/puppeteer/index.d.ts#L114
* /
. . . 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-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
}
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 ) ;
}
}
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
}
2020-05-07 10:54:55 +00:00
async addScriptTag ( options : {
url? : string ;
path? : string ;
content? : string ;
type ? : string ;
} ) : 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
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
}
2020-05-07 10:54:55 +00:00
async exposeFunction (
name : string ,
puppeteerFunction : Function
) : 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! `
) ;
2018-07-31 19:18:10 +00:00
this . _pageBindings . set ( name , puppeteerFunction ) ;
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
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
}
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
}
2020-05-05 12:53:22 +00:00
async setUserAgent ( userAgent : string ) : Promise < void > {
2019-04-10 04:42:42 +00:00
return this . _frameManager . networkManager ( ) . setUserAgent ( userAgent ) ;
2017-06-21 20:51:06 +00:00
}
2017-05-11 07:06:41 +00:00
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 (
event . executionContextId
) ;
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 } ,
} ) ;
}
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
}
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
}
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
}
2020-05-29 10:49:30 +00:00
async reload ( options? : WaitForOptions ) : Promise < HTTPResponse | null > {
2020-07-10 10:51:52 +00:00
const result = await Promise . all < HTTPResponse , void > ( [
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
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 ;
}
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 ( )
) ;
}
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
}
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 ) ;
}
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
}
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' ) ;
}
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
] ) ;
}
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
}
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
}
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
}
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
}
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
* @param isUserActive - Mock isUserActive
* @param isScreenUnlocked - Mock isScreenUnlocked
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 ;
}
}
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
}
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
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
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
}
2020-05-07 13:49:42 +00:00
async screenshot (
options : ScreenshotOptions = { }
) : Promise < Buffer | string | void > {
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 ) {
2020-05-07 10:54:55 +00:00
assert (
options . type === 'png' || options . type === 'jpeg' ,
'Unknown options.type value: ' + options . 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' ;
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 (
screenshotType === 'jpeg' ,
'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 (
2020-05-07 10:54:55 +00:00
format : 'png' | 'jpeg' ,
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' ) ;
2017-07-17 19:15:06 +00:00
const width = Math . ceil ( metrics . contentSize . width ) ;
const height = Math . ceil ( metrics . contentSize . height ) ;
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 ) {
const { isMobile = false , deviceScaleFactor = 1 , isLandscape = false } =
this . _viewport || { } ;
const screenOrientation : Protocol.Emulation.ScreenOrientation = isLandscape
? { angle : 90 , type : 'landscapePrimary' }
: { angle : 0 , type : 'portraitPrimary' } ;
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 =
options . omitBackground && format === 'png' ;
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
/ * *
* Generatees a PDF of the page with the ` print ` CSS media type .
* @remarks
*
* IMPORTANT : PDF generation is only supported in Chrome headless mode .
*
* 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 .
* /
2020-05-05 12:53:22 +00:00
async pdf ( options : PDFOptions = { } ) : Promise < Buffer > {
2018-11-12 20:59:21 +00:00
const {
scale = 1 ,
displayHeaderFooter = false ,
headerTemplate = '' ,
footerTemplate = '' ,
printBackground = false ,
landscape = false ,
pageRanges = '' ,
preferCSSPageSize = false ,
margin = { } ,
2020-05-07 10:54:55 +00:00
path = null ,
2021-03-18 19:57:32 +00:00
omitBackground = false ,
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 ( ) ;
}
2017-08-21 23:39:04 +00:00
const result = await 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
if ( omitBackground ) {
await this . _resetDefaultBackgroundColor ( ) ;
}
2019-06-15 05:36:06 +00:00
return await helper . readProtocolStream ( this . _client , result . stream , path ) ;
2017-06-21 20:51:06 +00:00
}
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
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 ;
}
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
}
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
}
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
}
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
}
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 ) ;
}
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 ) ;
}
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
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 ) ;
}
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
}