2017-06-20 23:41:27 +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 .
* /
2018-05-31 23:53:51 +00:00
const { helper , assert } = require ( './helper' ) ;
2017-11-10 23:33:14 +00:00
const { FrameManager } = require ( './FrameManager' ) ;
2018-09-17 23:15:50 +00:00
const { NetworkManager } = require ( './NetworkManager' ) ;
2018-08-09 23:51:12 +00:00
const { TimeoutError } = require ( './Errors' ) ;
2018-09-14 18:44:54 +00:00
const { Connection } = require ( './Connection' ) ;
2017-07-19 01:54:24 +00:00
class NavigatorWatcher {
2017-06-21 20:51:06 +00:00
/ * *
2018-09-14 18:44:54 +00:00
* @ param { ! Puppeteer . CDPSession } client
2017-11-10 23:33:14 +00:00
* @ param { ! FrameManager } frameManager
2018-09-17 23:15:50 +00:00
* @ param { ! NetworkManager } networkManager
2017-11-10 23:33:14 +00:00
* @ param { ! Puppeteer . Frame } frame
2018-01-10 21:04:01 +00:00
* @ param { number } timeout
2017-06-21 20:58:49 +00:00
* @ param { ! Object = } options
* /
2018-09-17 23:15:50 +00:00
constructor ( client , frameManager , networkManager , frame , timeout , options = { } ) {
2018-05-31 23:53:51 +00:00
assert ( options . networkIdleTimeout === undefined , 'ERROR: networkIdleTimeout option is no longer supported.' ) ;
assert ( options . networkIdleInflight === undefined , 'ERROR: networkIdleInflight option is no longer supported.' ) ;
assert ( options . waitUntil !== 'networkidle' , 'ERROR: "networkidle" option is no longer supported. Use "networkidle2" instead' ) ;
2017-10-24 17:05:15 +00:00
let waitUntil = [ 'load' ] ;
if ( Array . isArray ( options . waitUntil ) )
waitUntil = options . waitUntil . slice ( ) ;
else if ( typeof options . waitUntil === 'string' )
waitUntil = [ options . waitUntil ] ;
2017-11-10 23:33:14 +00:00
this . _expectedLifecycle = waitUntil . map ( value => {
const protocolEvent = puppeteerToProtocolLifecycle [ value ] ;
2018-05-31 23:53:51 +00:00
assert ( protocolEvent , 'Unknown value for options.waitUntil: ' + value ) ;
2017-11-10 23:33:14 +00:00
return protocolEvent ;
} ) ;
this . _frameManager = frameManager ;
2018-09-17 23:15:50 +00:00
this . _networkManager = networkManager ;
2017-11-10 23:33:14 +00:00
this . _frame = frame ;
this . _initialLoaderId = frame . _loaderId ;
2018-01-10 21:04:01 +00:00
this . _timeout = timeout ;
2018-09-17 23:15:50 +00:00
/** @type {?Puppeteer.Request} */
this . _navigationRequest = null ;
2018-04-10 06:38:20 +00:00
this . _hasSameDocumentNavigation = false ;
2017-11-10 23:33:14 +00:00
this . _eventListeners = [
2018-09-14 18:44:54 +00:00
helper . addEventListener ( Connection . fromSession ( client ) , Connection . Events . Disconnected , ( ) => this . _terminate ( new Error ( 'Navigation failed because browser has disconnected!' ) ) ) ,
2017-11-20 22:47:11 +00:00
helper . addEventListener ( this . _frameManager , FrameManager . Events . LifecycleEvent , this . _checkLifecycleComplete . bind ( this ) ) ,
2018-04-10 06:38:20 +00:00
helper . addEventListener ( this . _frameManager , FrameManager . Events . FrameNavigatedWithinDocument , this . _navigatedWithinDocument . bind ( this ) ) ,
2018-09-17 23:15:50 +00:00
helper . addEventListener ( this . _frameManager , FrameManager . Events . FrameDetached , this . _checkLifecycleComplete . bind ( this ) ) ,
helper . addEventListener ( this . _networkManager , NetworkManager . Events . Request , this . _onRequest . bind ( this ) ) ,
2017-11-10 23:33:14 +00:00
] ;
2018-09-05 21:59:29 +00:00
this . _sameDocumentNavigationPromise = new Promise ( fulfill => {
this . _sameDocumentNavigationCompleteCallback = fulfill ;
2017-11-10 23:33:14 +00:00
} ) ;
2018-09-05 21:59:29 +00:00
this . _newDocumentNavigationPromise = new Promise ( fulfill => {
this . _newDocumentNavigationCompleteCallback = fulfill ;
2017-11-10 23:33:14 +00:00
} ) ;
2018-09-05 21:59:29 +00:00
this . _timeoutPromise = this . _createTimeoutPromise ( ) ;
2018-09-14 18:44:54 +00:00
this . _terminationPromise = new Promise ( fulfill => {
this . _terminationCallback = fulfill ;
} ) ;
}
2018-09-17 23:15:50 +00:00
/ * *
* @ param { ! Puppeteer . Request } request
* /
_onRequest ( request ) {
if ( request . frame ( ) !== this . _frame || ! request . isNavigationRequest ( ) )
return ;
this . _navigationRequest = request ;
}
/ * *
* @ return { ? Puppeteer . Response }
* /
navigationResponse ( ) {
return this . _navigationRequest ? this . _navigationRequest . response ( ) : null ;
}
2018-09-14 18:44:54 +00:00
/ * *
* @ param { ! Error } error
* /
_terminate ( error ) {
this . _terminationCallback . call ( null , error ) ;
2018-09-05 21:59:29 +00:00
}
/ * *
* @ return { ! Promise < ? Error > }
* /
sameDocumentNavigationPromise ( ) {
return this . _sameDocumentNavigationPromise ;
}
/ * *
* @ return { ! Promise < ? Error > }
* /
newDocumentNavigationPromise ( ) {
return this . _newDocumentNavigationPromise ;
}
/ * *
* @ return { ! Promise < ? Error > }
* /
2018-09-14 18:44:54 +00:00
timeoutOrTerminationPromise ( ) {
return Promise . race ( [ this . _timeoutPromise , this . _terminationPromise ] ) ;
2017-06-21 20:51:06 +00:00
}
2017-06-20 23:41:27 +00:00
2017-06-21 20:51:06 +00:00
/ * *
2017-12-28 03:00:37 +00:00
* @ return { ! Promise < ? Error > }
2017-06-21 20:58:49 +00:00
* /
2017-11-10 23:33:14 +00:00
_createTimeoutPromise ( ) {
if ( ! this . _timeout )
2017-11-21 05:21:25 +00:00
return new Promise ( ( ) => { } ) ;
2017-11-10 23:33:14 +00:00
const errorMessage = 'Navigation Timeout Exceeded: ' + this . _timeout + 'ms exceeded' ;
return new Promise ( fulfill => this . _maximumTimer = setTimeout ( fulfill , this . _timeout ) )
2018-08-09 23:51:12 +00:00
. then ( ( ) => new TimeoutError ( errorMessage ) ) ;
2017-06-21 20:51:06 +00:00
}
2017-06-20 23:41:27 +00:00
2018-04-10 06:38:20 +00:00
/ * *
* @ param { ! Puppeteer . Frame } frame
* /
_navigatedWithinDocument ( frame ) {
if ( frame !== this . _frame )
return ;
this . _hasSameDocumentNavigation = true ;
this . _checkLifecycleComplete ( ) ;
}
2017-11-10 23:33:14 +00:00
_checkLifecycleComplete ( ) {
// We expect navigation to commit.
2018-04-10 06:38:20 +00:00
if ( this . _frame . _loaderId === this . _initialLoaderId && ! this . _hasSameDocumentNavigation )
2017-10-24 01:10:59 +00:00
return ;
2017-11-10 23:33:14 +00:00
if ( ! checkLifecycle ( this . _frame , this . _expectedLifecycle ) )
2017-10-24 01:10:59 +00:00
return ;
2018-09-05 21:59:29 +00:00
if ( this . _hasSameDocumentNavigation )
this . _sameDocumentNavigationCompleteCallback ( ) ;
if ( this . _frame . _loaderId !== this . _initialLoaderId )
this . _newDocumentNavigationCompleteCallback ( ) ;
2017-11-10 23:33:14 +00:00
/ * *
* @ param { ! Puppeteer . Frame } frame
* @ param { ! Array < string > } expectedLifecycle
* @ return { boolean }
* /
function checkLifecycle ( frame , expectedLifecycle ) {
for ( const event of expectedLifecycle ) {
if ( ! frame . _lifecycleEvents . has ( event ) )
return false ;
}
2017-11-10 23:44:14 +00:00
for ( const child of frame . childFrames ( ) ) {
if ( ! checkLifecycle ( child , expectedLifecycle ) )
return false ;
}
2017-11-10 23:33:14 +00:00
return true ;
}
2017-06-28 21:39:37 +00:00
}
2018-09-05 21:59:29 +00:00
dispose ( ) {
2017-07-23 00:03:58 +00:00
helper . removeEventListeners ( this . _eventListeners ) ;
2017-07-07 22:43:17 +00:00
clearTimeout ( this . _maximumTimer ) ;
2017-06-21 20:51:06 +00:00
}
2017-06-20 23:41:27 +00:00
}
2017-11-10 23:33:14 +00:00
const puppeteerToProtocolLifecycle = {
2017-10-24 01:10:59 +00:00
'load' : 'load' ,
2017-11-10 23:33:14 +00:00
'domcontentloaded' : 'DOMContentLoaded' ,
'networkidle0' : 'networkIdle' ,
'networkidle2' : 'networkAlmostIdle' ,
2017-10-24 01:10:59 +00:00
} ;
2018-08-06 18:31:33 +00:00
module . exports = { NavigatorWatcher } ;