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 .
* /
2017-06-22 20:38:10 +00:00
let fs = require ( 'fs' ) ;
let EventEmitter = require ( 'events' ) ;
let mime = require ( 'mime' ) ;
2017-06-29 06:09:28 +00:00
let NetworkManager = require ( './NetworkManager' ) ;
2017-06-22 20:38:10 +00:00
let Navigator = require ( './Navigator' ) ;
let Dialog = require ( './Dialog' ) ;
let FrameManager = require ( './FrameManager' ) ;
2017-06-27 19:40:46 +00:00
let helper = require ( './helper' ) ;
2017-05-11 07:06:41 +00:00
class Page extends EventEmitter {
2017-06-21 20:51:06 +00:00
/ * *
2017-06-21 20:58:49 +00:00
* @ param { ! Connection } client
* @ return { ! Promise < ! Page > }
* /
2017-06-21 20:51:06 +00:00
static async create ( client ) {
await Promise . all ( [
client . send ( 'Network.enable' , { } ) ,
client . send ( 'Page.enable' , { } ) ,
client . send ( 'Runtime.enable' , { } ) ,
client . send ( 'Security.enable' , { } ) ,
] ) ;
2017-06-29 06:09:28 +00:00
let screenDPIExpression = helper . evaluationString ( ( ) => window . devicePixelRatio ) ;
let { result : { value : screenDPI } } = await client . send ( 'Runtime.evaluate' , { expression : screenDPIExpression , returnByValue : true } ) ;
let userAgentExpression = helper . evaluationString ( ( ) => window . navigator . userAgent ) ;
let { result : { value : userAgent } } = await client . send ( 'Runtime.evaluate' , { expression : userAgentExpression , returnByValue : true } ) ;
2017-06-22 20:38:10 +00:00
let frameManager = await FrameManager . create ( client ) ;
2017-06-29 06:09:28 +00:00
let networkManager = new NetworkManager ( client , userAgent ) ;
let page = new Page ( client , frameManager , networkManager , screenDPI ) ;
2017-06-21 20:51:06 +00:00
// Initialize default page size.
await page . setViewportSize ( { width : 400 , height : 300 } ) ;
return page ;
}
/ * *
2017-06-21 20:58:49 +00:00
* @ param { ! Connection } client
* @ param { ! FrameManager } frameManager
2017-06-29 06:09:28 +00:00
* @ param { ! NetworkManager } networkManager
2017-06-21 20:58:49 +00:00
* @ param { number } screenDPI
* /
2017-06-29 06:09:28 +00:00
constructor ( client , frameManager , networkManager , screenDPI ) {
2017-06-21 20:51:06 +00:00
super ( ) ;
this . _client = client ;
this . _frameManager = frameManager ;
2017-06-29 06:09:28 +00:00
this . _networkManager = networkManager ;
2017-06-21 20:51:06 +00:00
this . _screenDPI = screenDPI ;
/** @type {!Map<string, function>} */
this . _inPageCallbacks = new Map ( ) ;
this . _screenshotTaskChain = Promise . resolve ( ) ;
this . _frameManager . on ( FrameManager . Events . FrameAttached , event => this . emit ( Page . Events . FrameAttached , event ) ) ;
this . _frameManager . on ( FrameManager . Events . FrameDetached , event => this . emit ( Page . Events . FrameDetached , event ) ) ;
this . _frameManager . on ( FrameManager . Events . FrameNavigated , event => this . emit ( Page . Events . FrameNavigated , event ) ) ;
2017-06-29 06:09:28 +00:00
this . _networkManager . on ( NetworkManager . Events . Request , event => this . emit ( Page . Events . Request , event ) ) ;
this . _networkManager . on ( NetworkManager . Events . Response , event => this . emit ( Page . Events . Response , event ) ) ;
this . _networkManager . on ( NetworkManager . Events . RequestFailed , event => this . emit ( Page . Events . RequestFailed , event ) ) ;
this . _networkManager . on ( NetworkManager . Events . RequestFinished , event => this . emit ( Page . Events . RequestFinished , event ) ) ;
2017-06-28 05:02:46 +00:00
client . on ( 'Page.loadEventFired' , event => this . emit ( Page . Events . Load ) ) ;
2017-06-21 20:51:06 +00:00
client . on ( 'Runtime.consoleAPICalled' , event => this . _onConsoleAPI ( event ) ) ;
client . on ( 'Page.javascriptDialogOpening' , event => this . _onDialog ( event ) ) ;
client . on ( 'Runtime.exceptionThrown' , exception => this . _handleException ( exception . exceptionDetails ) ) ;
}
/ * *
2017-06-21 20:58:49 +00:00
* @ return { ! Frame }
* /
2017-06-21 20:51:06 +00:00
mainFrame ( ) {
return this . _frameManager . mainFrame ( ) ;
}
2017-06-21 20:36:04 +00:00
2017-06-21 20:51:06 +00:00
/ * *
2017-06-21 20:58:49 +00:00
* @ return { ! Array < ! Frame > }
* /
2017-06-21 20:51:06 +00:00
frames ( ) {
return this . _frameManager . frames ( ) ;
}
2017-06-21 20:36:04 +00:00
2017-06-21 20:51:06 +00:00
/ * *
2017-06-28 06:31:38 +00:00
* @ param { ? function ( ! InterceptedRequest ) } interceptor
2017-06-21 20:58:49 +00:00
* /
2017-06-21 20:51:06 +00:00
async setRequestInterceptor ( interceptor ) {
2017-06-29 06:09:28 +00:00
return this . _networkManager . setRequestInterceptor ( interceptor ) ;
2017-06-21 20:51:06 +00:00
}
2017-06-15 07:20:37 +00:00
2017-06-21 20:51:06 +00:00
/ * *
2017-06-21 20:58:49 +00:00
* @ param { string } url
* @ return { ! Promise }
* /
2017-06-21 20:51:06 +00:00
async addScriptTag ( url ) {
return this . evaluate ( addScriptTag , url ) ;
2017-06-21 20:36:04 +00:00
2017-06-21 20:51:06 +00:00
/ * *
2017-06-21 20:58:49 +00:00
* @ param { string } url
* /
2017-06-21 20:51:06 +00:00
function addScriptTag ( url ) {
2017-06-22 20:38:10 +00:00
let script = document . createElement ( 'script' ) ;
2017-06-21 20:51:06 +00:00
script . src = url ;
2017-06-22 20:38:10 +00:00
let promise = new Promise ( x => script . onload = x ) ;
2017-06-21 20:51:06 +00:00
document . head . appendChild ( script ) ;
return promise ;
2017-05-12 17:01:22 +00:00
}
2017-06-21 20:51:06 +00:00
}
2017-06-21 20:36:04 +00:00
2017-06-21 20:51:06 +00:00
/ * *
2017-06-21 20:58:49 +00:00
* @ param { string } filePath
* @ return { ! Promise }
* /
2017-06-21 20:51:06 +00:00
async injectFile ( filePath ) {
2017-07-06 23:09:23 +00:00
const contents = await new Promise ( ( resolve , reject ) => {
fs . readFile ( filePath , 'utf8' , ( err , data ) => {
if ( err ) return reject ( err ) ;
resolve ( data ) ;
} ) ;
} ) ;
return this . _client . send ( 'Runtime.evaluate' , { expression : contents , returnByValue : true } ) ;
2017-06-21 20:51:06 +00:00
}
/ * *
2017-06-21 20:58:49 +00:00
* @ param { string } name
* @ param { function ( ? ) } callback
* /
2017-06-21 20:51:06 +00:00
async setInPageCallback ( name , callback ) {
if ( this . _inPageCallbacks [ name ] )
throw new Error ( ` Failed to set in-page callback with name ${ name } : window[' ${ name } '] already exists! ` ) ;
this . _inPageCallbacks [ name ] = callback ;
2017-06-27 19:40:46 +00:00
let expression = helper . evaluationString ( inPageCallback , name ) ;
2017-06-21 20:51:06 +00:00
await this . _client . send ( 'Page.addScriptToEvaluateOnLoad' , { scriptSource : expression } ) ;
await this . _client . send ( 'Runtime.evaluate' , { expression , returnByValue : true } ) ;
function inPageCallback ( callbackName ) {
window [ callbackName ] = async ( ... args ) => {
const me = window [ callbackName ] ;
let callbacks = me [ 'callbacks' ] ;
if ( ! callbacks ) {
callbacks = new Map ( ) ;
me [ 'callbacks' ] = callbacks ;
2017-06-20 21:54:53 +00:00
}
2017-06-21 20:51:06 +00:00
const seq = ( me [ 'lastSeq' ] || 0 ) + 1 ;
me [ 'lastSeq' ] = seq ;
const promise = new Promise ( fulfill => callbacks . set ( seq , fulfill ) ) ;
// eslint-disable-next-line no-console
console . debug ( 'driver:InPageCallback' , JSON . stringify ( { name : callbackName , seq , args } ) ) ;
return promise ;
} ;
2017-05-11 07:06:41 +00:00
}
2017-06-21 20:51:06 +00:00
}
2017-06-21 20:36:04 +00:00
2017-06-21 20:51:06 +00:00
/ * *
2017-06-21 20:58:49 +00:00
* @ param { ! Object } headers
* @ return { ! Promise }
* /
2017-06-29 06:09:28 +00:00
async setHTTPHeaders ( headers ) {
return this . _networkManager . setHTTPHeaders ( headers ) ;
2017-06-21 20:51:06 +00:00
}
/ * *
2017-06-21 20:58:49 +00:00
* @ return { ! Object }
* /
2017-06-29 06:09:28 +00:00
httpHeaders ( ) {
return this . _networkManager . httpHeaders ( ) ;
2017-06-21 20:51:06 +00:00
}
2017-06-21 20:36:04 +00:00
2017-06-21 20:51:06 +00:00
/ * *
2017-06-21 20:58:49 +00:00
* @ param { string } userAgent
* @ return { ! Promise }
* /
2017-06-29 06:09:28 +00:00
async setUserAgent ( userAgent ) {
return this . _networkManager . setUserAgent ( userAgent ) ;
2017-06-21 20:51:06 +00:00
}
2017-05-11 07:06:41 +00:00
2017-06-21 20:51:06 +00:00
/ * *
2017-06-21 20:58:49 +00:00
* @ return { string }
* /
2017-06-29 06:09:28 +00:00
userAgent ( ) {
return this . _networkManager . userAgent ( ) ;
2017-06-21 20:51:06 +00:00
}
2017-05-11 07:06:41 +00:00
2017-06-21 20:51:06 +00:00
/ * *
2017-06-21 20:58:49 +00:00
* @ param { ! Object } exceptionDetails
* /
2017-06-21 20:51:06 +00:00
async _handleException ( exceptionDetails ) {
2017-06-27 19:40:46 +00:00
let message = await helper . getExceptionMessage ( this . _client , exceptionDetails ) ;
2017-07-06 18:22:32 +00:00
this . emit ( Page . Events . PageError , new Error ( message ) ) ;
2017-06-21 20:51:06 +00:00
}
async _onConsoleAPI ( event ) {
if ( event . type === 'debug' && event . args . length && event . args [ 0 ] . value === 'driver:InPageCallback' ) {
2017-06-22 20:38:10 +00:00
let { name , seq , args } = JSON . parse ( event . args [ 1 ] . value ) ;
let result = await this . _inPageCallbacks [ name ] ( ... args ) ;
2017-06-27 19:40:46 +00:00
let expression = helper . evaluationString ( deliverResult , name , seq , result ) ;
2017-06-21 20:51:06 +00:00
this . _client . send ( 'Runtime.evaluate' , { expression } ) ;
function deliverResult ( name , seq , result ) {
window [ name ] [ 'callbacks' ] . get ( seq ) ( result ) ;
window [ name ] [ 'callbacks' ] . delete ( seq ) ;
}
return ;
}
2017-06-22 20:38:10 +00:00
let values = event . args . map ( arg => arg . value || arg . description || '' ) ;
2017-06-21 20:51:06 +00:00
this . emit ( Page . Events . ConsoleMessage , values . join ( ' ' ) ) ;
}
_onDialog ( event ) {
2017-06-22 20:38:10 +00:00
let dialogType = null ;
2017-06-21 20:51:06 +00:00
if ( event . type === 'alert' )
dialogType = Dialog . Type . Alert ;
else if ( event . type === 'confirm' )
dialogType = Dialog . Type . Confirm ;
else if ( event . type === 'prompt' )
dialogType = Dialog . Type . Prompt ;
else if ( event . type === 'beforeunload' )
dialogType = Dialog . Type . BeforeUnload ;
console . assert ( dialogType , 'Unknown javascript dialog type: ' + event . type ) ;
2017-06-22 20:38:10 +00:00
let dialog = new Dialog ( this . _client , dialogType , event . message ) ;
2017-06-21 20:51:06 +00:00
this . emit ( Page . Events . Dialog , dialog ) ;
}
/ * *
2017-06-21 20:58:49 +00:00
* @ return { ! Promise < string > }
* /
2017-06-21 20:51:06 +00:00
async url ( ) {
return this . evaluate ( ( ) => window . location . href ) ;
}
2017-06-21 20:36:04 +00:00
2017-06-21 20:51:06 +00:00
/ * *
2017-06-21 20:58:49 +00:00
* @ param { string } html
* @ return { ! Promise }
* /
2017-06-21 20:51:06 +00:00
async setContent ( html ) {
2017-07-06 02:07:33 +00:00
this . evaluate ( html => {
2017-06-21 20:51:06 +00:00
document . open ( ) ;
document . write ( html ) ;
document . close ( ) ;
} , html ) ;
}
/ * *
2017-06-21 20:58:49 +00:00
* @ param { string } html
* @ param { ! Object = } options
2017-07-10 22:09:52 +00:00
* @ return { ! Promise < ! Response > }
2017-06-21 20:58:49 +00:00
* /
2017-07-10 22:09:52 +00:00
async navigate ( url , options ) {
/** @type {!Map<string, !Response>} */
const responses = new Map ( ) ;
const onResponse = response => responses . set ( response . url , response ) ;
const navigator = new Navigator ( this . _client , url , this . _networkManager . httpHeaders ( ) . referer , options ) ;
try {
this . _networkManager . on ( NetworkManager . Events . Response , onResponse ) ;
await navigator . navigate ( ) ;
} catch ( e ) {
this . _networkManager . removeListener ( NetworkManager . Events . Response , onResponse ) ;
throw e ;
}
this . _networkManager . removeListener ( NetworkManager . Events . Response , onResponse ) ;
const response = responses . get ( this . mainFrame ( ) . url ( ) ) ;
console . assert ( response ) ;
return response ;
2017-06-21 20:51:06 +00:00
}
2017-06-21 20:36:04 +00:00
2017-06-21 20:51:06 +00:00
/ * *
2017-06-21 20:58:49 +00:00
* @ param { ! { width : number , height : number } } size
* @ return { ! Promise }
* /
2017-06-21 20:51:06 +00:00
async setViewportSize ( size ) {
this . _viewportSize = size ;
2017-06-22 20:38:10 +00:00
let width = size . width ;
let height = size . height ;
let zoom = this . _screenDPI ;
2017-06-21 20:51:06 +00:00
return Promise . all ( [
this . _client . send ( 'Emulation.setDeviceMetricsOverride' , {
width ,
height ,
deviceScaleFactor : 1 ,
scale : 1 / zoom ,
mobile : false ,
fitWindow : false
} ) ,
this . _client . send ( 'Emulation.setVisibleSize' , {
2017-07-10 17:09:59 +00:00
width : Math . floor ( width / zoom ) ,
height : Math . floor ( height / zoom ) ,
2017-06-21 20:51:06 +00:00
} )
] ) ;
}
/ * *
2017-06-21 20:58:49 +00:00
* @ return { ! { width : number , height : number } }
* /
2017-06-21 20:51:06 +00:00
viewportSize ( ) {
return this . _viewportSize ;
}
2017-06-21 20:36:04 +00:00
2017-06-21 20:51:06 +00:00
/ * *
2017-06-21 20:58:49 +00:00
* @ param { function ( ) } fun
* @ param { ! Array < * > } args
* @ return { ! Promise < ( ! Object | undefined ) > }
* /
2017-06-21 20:51:06 +00:00
async evaluate ( fun , ... args ) {
2017-06-27 19:40:46 +00:00
return this . _frameManager . mainFrame ( ) . evaluate ( fun , ... args ) ;
2017-06-21 20:51:06 +00:00
}
2017-06-21 20:36:04 +00:00
2017-06-21 20:51:06 +00:00
/ * *
2017-06-21 20:58:49 +00:00
* @ param { function ( ) } fun
* @ param { ! Array < * > } args
* @ return { ! Promise }
* /
2017-06-21 20:51:06 +00:00
async evaluateOnInitialized ( fun , ... args ) {
2017-06-27 19:40:46 +00:00
let scriptSource = helper . evaluationString ( fun , ... args ) ;
2017-06-21 20:51:06 +00:00
await this . _client . send ( 'Page.addScriptToEvaluateOnLoad' , { scriptSource } ) ;
}
2017-06-20 01:03:01 +00:00
2017-06-21 20:51:06 +00:00
/ * *
2017-06-21 20:58:49 +00:00
* @ param { ! Object = } options
* @ return { ! Promise < ! Buffer > }
* /
2017-06-21 20:51:06 +00:00
async screenshot ( options ) {
options = options || { } ;
2017-06-22 20:38:10 +00:00
let screenshotType = null ;
2017-06-21 20:51:06 +00:00
if ( options . path ) {
2017-06-22 20:38:10 +00:00
let mimeType = mime . lookup ( options . path ) ;
2017-06-21 20:51:06 +00:00
if ( mimeType === 'image/png' )
screenshotType = 'png' ;
else if ( mimeType === 'image/jpeg' )
screenshotType = 'jpeg' ;
console . assert ( screenshotType , 'Unsupported screenshot mime type: ' + mimeType ) ;
}
if ( options . type ) {
console . assert ( ! screenshotType || options . type === screenshotType , ` Passed screenshot type ' ${ options . type } ' does not match the type inferred from the file path: ' ${ screenshotType } ' ` ) ;
console . assert ( options . type === 'png' || options . type === 'jpeg' , 'Unknown options.type value: ' + options . type ) ;
screenshotType = options . type ;
}
if ( ! screenshotType )
screenshotType = 'png' ;
if ( options . quality ) {
console . assert ( screenshotType === 'jpeg' , 'options.quality is unsupported for the ' + screenshotType + ' screenshots' ) ;
console . assert ( typeof options . quality === 'number' , 'Expected options.quality to be a number but found ' + ( typeof options . quality ) ) ;
console . assert ( Number . isInteger ( options . quality ) , 'Expected options.quality to be an integer' ) ;
console . assert ( options . quality >= 0 && options . quality <= 100 , 'Expected options.quality to be between 0 and 100 (inclusive), got ' + options . quality ) ;
}
console . assert ( ! options . clip || ! options . fullPage , 'options.clip and options.fullPage are exclusive' ) ;
if ( options . clip ) {
console . assert ( typeof options . clip . x === 'number' , 'Expected options.clip.x to be a number but found ' + ( typeof options . clip . x ) ) ;
console . assert ( typeof options . clip . y === 'number' , 'Expected options.clip.y to be a number but found ' + ( typeof options . clip . y ) ) ;
console . assert ( typeof options . clip . width === 'number' , 'Expected options.clip.width to be a number but found ' + ( typeof options . clip . width ) ) ;
console . assert ( typeof options . clip . height === 'number' , 'Expected options.clip.height to be a number but found ' + ( typeof options . clip . height ) ) ;
}
this . _screenshotTaskChain = this . _screenshotTaskChain . then ( this . _screenshotTask . bind ( this , screenshotType , options ) ) ;
return this . _screenshotTaskChain ;
}
/ * *
2017-06-21 20:58:49 +00:00
* @ param { string } screenshotType
* @ param { ! Object = } options
* @ return { ! Promise < ! Buffer > }
* /
2017-06-21 20:51:06 +00:00
async _screenshotTask ( screenshotType , options ) {
if ( options . clip ) {
await Promise . all ( [
this . _client . send ( 'Emulation.setVisibleSize' , {
width : Math . ceil ( options . clip . width / this . _screenDPI ) ,
height : Math . ceil ( options . clip . height / this . _screenDPI ) ,
} ) ,
this . _client . send ( 'Emulation.forceViewport' , {
x : options . clip . x / this . _screenDPI ,
y : options . clip . y / this . _screenDPI ,
scale : 1 ,
} )
] ) ;
} else if ( options . fullPage ) {
2017-06-22 20:38:10 +00:00
let response = await this . _client . send ( 'Page.getLayoutMetrics' ) ;
2017-06-21 20:51:06 +00:00
await Promise . all ( [
this . _client . send ( 'Emulation.setVisibleSize' , {
width : Math . ceil ( response . contentSize . width / this . _screenDPI ) ,
height : Math . ceil ( response . contentSize . height / this . _screenDPI ) ,
} ) ,
this . _client . send ( 'Emulation.forceViewport' , {
x : 0 ,
y : 0 ,
scale : 1 ,
} )
] ) ;
}
2017-06-22 20:38:10 +00:00
let result = await this . _client . send ( 'Page.captureScreenshot' , {
2017-06-21 20:51:06 +00:00
fromSurface : true ,
format : screenshotType ,
quality : options . quality
} ) ;
if ( options . clip || options . fullPage ) {
await Promise . all ( [
this . setViewportSize ( this . viewportSize ( ) ) ,
this . _client . send ( 'Emulation.resetViewport' )
] ) ;
}
2017-06-22 20:38:10 +00:00
let buffer = new Buffer ( result . data , 'base64' ) ;
2017-06-21 20:51:06 +00:00
if ( options . path )
fs . writeFileSync ( options . path , buffer ) ;
return buffer ;
}
/ * *
2017-06-21 20:58:49 +00:00
* @ param { string } filePath
* @ param { ! Object = } options
* @ return { ! Promise }
* /
2017-06-21 20:51:06 +00:00
async printToPDF ( filePath , options ) {
options = options || { } ;
2017-06-22 20:38:10 +00:00
let scale = options . scale || 1 ;
let displayHeaderFooter = options . displayHeaderFooter || false ;
let printBackground = options . printBackground || true ;
let landscape = options . landscape || false ;
let pageRanges = options . pageRanges || '' ;
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 ) {
2017-06-22 20:38:10 +00:00
let format = Page . PaperFormats [ options . format ] ;
2017-06-21 20:51:06 +00:00
console . assert ( format , 'Unknown paper format: ' + options . format ) ;
paperWidth = format . width ;
paperHeight = format . height ;
} else {
paperWidth = convertPrintParameterToInches ( options . width ) || paperWidth ;
paperHeight = convertPrintParameterToInches ( options . height ) || paperHeight ;
}
2017-06-22 20:38:10 +00:00
let marginOptions = options . margin || { } ;
let marginTop = convertPrintParameterToInches ( marginOptions . top ) || 0 ;
let marginLeft = convertPrintParameterToInches ( marginOptions . left ) || 0 ;
let marginBottom = convertPrintParameterToInches ( marginOptions . bottom ) || 0 ;
let marginRight = convertPrintParameterToInches ( marginOptions . right ) || 0 ;
2017-06-21 20:51:06 +00:00
2017-06-22 20:38:10 +00:00
let result = await this . _client . send ( 'Page.printToPDF' , {
2017-06-21 20:51:06 +00:00
landscape : landscape ,
displayHeaderFooter : displayHeaderFooter ,
printBackground : printBackground ,
scale : scale ,
paperWidth : paperWidth ,
paperHeight : paperHeight ,
marginTop : marginTop ,
marginBottom : marginBottom ,
marginLeft : marginLeft ,
marginRight : marginRight ,
pageRanges : pageRanges
} ) ;
2017-06-22 20:38:10 +00:00
let buffer = new Buffer ( result . data , 'base64' ) ;
2017-06-21 20:51:06 +00:00
fs . writeFileSync ( filePath , buffer ) ;
}
/ * *
2017-06-21 20:58:49 +00:00
* @ return { ! Promise < string > }
* /
2017-06-21 20:51:06 +00:00
async plainText ( ) {
return this . evaluate ( ( ) => document . body . innerText ) ;
}
2017-06-21 20:36:04 +00:00
2017-06-21 20:51:06 +00:00
/ * *
2017-06-21 20:58:49 +00:00
* @ return { ! Promise < string > }
* /
2017-06-21 20:51:06 +00:00
async title ( ) {
return this . evaluate ( ( ) => document . title ) ;
}
2017-05-11 07:06:41 +00:00
2017-06-21 20:51:06 +00:00
/ * *
2017-06-21 20:58:49 +00:00
* @ return { ! Promise }
* /
2017-06-21 20:51:06 +00:00
async close ( ) {
await this . _client . dispose ( ) ;
}
2017-06-28 01:27:22 +00:00
/ * *
* @ param { string } selector
* @ param { ! Promise < number > }
* /
async _querySelector ( selector ) {
2017-07-11 23:25:25 +00:00
let { root } = await this . _client . send ( 'DOM.getDocument' , { depth : 1 } ) ;
2017-07-10 18:21:46 +00:00
let { nodeId } = await this . _client . send ( 'DOM.querySelector' , {
2017-07-11 23:25:25 +00:00
nodeId : root . nodeId ,
2017-06-28 01:27:22 +00:00
selector
2017-07-10 18:21:46 +00:00
} ) ;
if ( ! nodeId )
throw new Error ( 'No node found for selector: ' + selector ) ;
return nodeId ;
2017-06-28 01:27:22 +00:00
}
/ * *
* @ param { string } selector
* @ param { ! Promise }
* /
async click ( selector ) {
2017-07-11 23:25:25 +00:00
try {
const nodeId = await this . _querySelector ( selector ) ;
let boxModel = ( await this . _client . send ( 'DOM.getBoxModel' , { nodeId } ) ) . model . content ;
let x = Math . round ( ( boxModel [ 0 ] + boxModel [ 4 ] ) / 2 ) ;
let y = Math . round ( ( boxModel [ 1 ] + boxModel [ 5 ] ) / 2 ) ;
this . _client . send ( 'Input.dispatchMouseEvent' , {
type : 'mouseMoved' ,
x , y
} ) ;
this . _client . send ( 'Input.dispatchMouseEvent' , {
type : 'mousePressed' ,
button : 'left' ,
x , y ,
clickCount : 1
} ) ;
await this . _client . send ( 'Input.dispatchMouseEvent' , {
type : 'mouseReleased' ,
button : 'left' ,
x , y ,
clickCount : 1
} ) ;
} finally {
await this . _client . send ( 'DOM.disable' ) ;
}
2017-06-28 01:27:22 +00:00
}
/ * *
* @ param { string } selector
* @ param { ! Promise }
* /
async focus ( selector ) {
2017-07-11 23:25:25 +00:00
try {
const nodeId = await this . _querySelector ( selector ) ;
await this . _client . send ( 'DOM.focus' , { nodeId } ) ;
} finally {
await this . _client . send ( 'DOM.disable' ) ;
}
2017-06-28 01:27:22 +00:00
}
/ * *
* @ param { string } text
* @ param { ! Promise }
* /
async type ( text ) {
for ( let i = 0 ; i < text . length ; i ++ ) {
let char = text . charAt ( i ) ;
this . _client . send ( 'Input.dispatchKeyEvent' , {
type : 'keyDown' ,
key : char
} ) ;
this . _client . send ( 'Input.dispatchKeyEvent' , {
type : 'char' ,
text : char ,
key : char ,
unmodifiedText : char
} ) ;
await this . _client . send ( 'Input.dispatchKeyEvent' , {
type : 'keyUp' ,
key : char
} ) ;
}
}
2017-07-07 22:39:02 +00:00
/ * *
* @ param { string } selector
* @ return { ! Promise < undefined > }
* /
waitFor ( selector ) {
return this . mainFrame ( ) . waitFor ( selector ) ;
}
2017-07-10 18:21:46 +00:00
/ * *
* @ param { string } selector
2017-07-11 15:57:26 +00:00
* @ param { ! Array < string > } filePaths
2017-07-10 18:21:46 +00:00
* @ return { ! Promise }
* /
2017-07-11 15:57:26 +00:00
async uploadFile ( selector , ... filePaths ) {
2017-07-11 23:25:25 +00:00
try {
const nodeId = await this . _querySelector ( selector ) ;
2017-07-11 15:57:26 +00:00
await this . _client . send ( 'DOM.setFileInputFiles' , { nodeId , files : filePaths } ) ;
2017-07-11 23:25:25 +00:00
} finally {
await this . _client . send ( 'DOM.disable' ) ;
}
2017-07-10 18:21:46 +00:00
}
2017-05-11 07:06:41 +00:00
}
2017-05-14 18:29:42 +00:00
/** @enum {string} */
Page . PaperFormats = {
2017-06-21 20:51:06 +00:00
Letter : { width : 8.5 , height : 11 } ,
Legal : { width : 8.5 , height : 14 } ,
Tabloid : { width : 11 , height : 17 } ,
Ledger : { width : 17 , height : 11 } ,
A0 : { width : 33.1 , height : 46.8 } ,
A1 : { width : 23.4 , height : 33.1 } ,
A2 : { width : 16.5 , height : 23.4 } ,
A3 : { width : 11.7 , height : 16.5 } ,
A4 : { width : 8.27 , height : 11.7 } ,
A5 : { width : 5.83 , height : 8.27 } ,
2017-05-14 18:29:42 +00:00
} ;
2017-06-22 20:38:10 +00:00
let unitToPixels = {
2017-06-21 20:51:06 +00:00
'px' : 1 ,
'in' : 96 ,
'cm' : 37.8 ,
'mm' : 3.78
2017-05-14 18:29:42 +00:00
} ;
/ * *
* @ param { ( string | number | undefined ) } parameter
* @ return { ( number | undefined ) }
* /
function convertPrintParameterToInches ( parameter ) {
2017-06-21 20:51:06 +00:00
if ( typeof parameter === 'undefined' )
return undefined ;
2017-06-22 20:38:10 +00:00
let pixels ;
2017-06-21 20:51:06 +00:00
if ( typeof parameter === 'number' ) {
// Treat numbers as pixel values to be aligned with phantom's paperSize.
pixels = /** @type {number} */ ( parameter ) ;
} else if ( typeof parameter === 'string' ) {
2017-06-22 20:38:10 +00:00
let text = parameter ;
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-06-22 20:38:10 +00:00
let value = Number ( valueText ) ;
2017-06-21 20:51:06 +00:00
console . assert ( ! isNaN ( value ) , 'Failed to parse parameter value: ' + text ) ;
pixels = value * unitToPixels [ unit ] ;
} else {
throw new Error ( 'printToPDF Cannot handle parameter type: ' + ( typeof parameter ) ) ;
}
return pixels / 96 ;
2017-06-11 08:32:59 +00:00
}
2017-05-14 18:29:42 +00:00
2017-05-11 07:06:41 +00:00
Page . Events = {
2017-06-21 20:51:06 +00:00
ConsoleMessage : 'consolemessage' ,
Dialog : 'dialog' ,
2017-07-06 18:22:32 +00:00
// Can'e use just 'error' due to node.js special treatment of error events.
// @see https://nodejs.org/api/events.html#events_error_events
PageError : 'pageerror' ,
2017-06-28 08:10:23 +00:00
Request : 'request' ,
2017-06-29 06:09:28 +00:00
Response : 'response' ,
RequestFailed : 'requestfailed' ,
RequestFinished : 'requestfinished' ,
2017-06-21 20:51:06 +00:00
FrameAttached : 'frameattached' ,
FrameDetached : 'framedetached' ,
FrameNavigated : 'framenavigated' ,
2017-06-28 05:02:46 +00:00
Load : 'load' ,
2017-05-11 07:06:41 +00:00
} ;
module . exports = Page ;