2019-12-19 16:53:24 -08:00
/ * *
* Copyright 2017 Google Inc . All rights reserved .
* Modifications copyright ( c ) Microsoft Corporation .
*
* Licensed under the Apache License , Version 2.0 ( the "License" ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an "AS IS" BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
* /
import { CRSession } from './crConnection' ;
import { helper } from '../helper' ;
2020-05-27 17:19:05 -07:00
import { getExceptionMessage , releaseObject } from './crProtocolHelper' ;
2019-12-19 16:53:24 -08:00
import { Protocol } from './protocol' ;
import * as js from '../javascript' ;
2020-05-26 14:08:32 -07:00
import * as debugSupport from '../debug/debugSupport' ;
2020-05-27 17:19:05 -07:00
import { RemoteObject , parseEvaluationResultValue } from '../remoteObject' ;
2019-12-19 16:53:24 -08:00
export class CRExecutionContext implements js . ExecutionContextDelegate {
_client : CRSession ;
_contextId : number ;
constructor ( client : CRSession , contextPayload : Protocol.Runtime.ExecutionContextDescription ) {
this . _client = client ;
this . _contextId = contextPayload . id ;
}
2020-05-27 17:19:05 -07:00
async rawEvaluate ( expression : string ) : Promise < RemoteObject > {
2020-05-20 15:55:33 -07:00
const { exceptionDetails , result : remoteObject } = await this . _client . send ( 'Runtime.evaluate' , {
2020-05-26 14:08:32 -07:00
expression : debugSupport.ensureSourceUrl ( expression ) ,
2020-05-20 15:55:33 -07:00
contextId : this._contextId ,
} ) . catch ( rewriteError ) ;
if ( exceptionDetails )
throw new Error ( 'Evaluation failed: ' + getExceptionMessage ( exceptionDetails ) ) ;
return remoteObject ;
}
2019-12-19 16:53:24 -08:00
async evaluate ( context : js.ExecutionContext , returnByValue : boolean , pageFunction : Function | string , . . . args : any [ ] ) : Promise < any > {
if ( helper . isString ( pageFunction ) ) {
2020-05-21 16:00:55 -07:00
return this . _callOnUtilityScript ( context ,
` evaluate ` , [
2020-05-26 14:08:32 -07:00
{ value : debugSupport.ensureSourceUrl ( pageFunction ) } ,
2020-05-21 16:00:55 -07:00
] , returnByValue , ( ) = > { } ) ;
2019-12-19 16:53:24 -08:00
}
if ( typeof pageFunction !== 'function' )
throw new Error ( ` Expected to get |string| or |function| as the first argument, but got " ${ pageFunction } " instead. ` ) ;
2020-05-27 17:19:05 -07:00
const { functionText , values , handles , dispose } = await js . prepareFunctionCall ( pageFunction , context , args ) ;
2020-05-21 16:00:55 -07:00
return this . _callOnUtilityScript ( context ,
'callFunction' , [
{ value : functionText } ,
. . . values . map ( value = > ( { value } ) ) ,
. . . handles ,
] , returnByValue , dispose ) ;
}
private async _callOnUtilityScript ( context : js.ExecutionContext , method : string , args : Protocol.Runtime.CallArgument [ ] , returnByValue : boolean , dispose : ( ) = > void ) {
2020-03-19 13:07:33 -07:00
try {
2020-05-20 15:55:33 -07:00
const utilityScript = await context . utilityScript ( ) ;
2020-03-19 13:07:33 -07:00
const { exceptionDetails , result : remoteObject } = await this . _client . send ( 'Runtime.callFunctionOn' , {
2020-05-26 14:08:32 -07:00
functionDeclaration : ` function (...args) { return this. ${ method } (...args) } ` + debugSupport . generateSourceUrl ( ) ,
2020-05-27 17:19:05 -07:00
objectId : utilityScript._objectId ,
2020-03-19 13:07:33 -07:00
arguments : [
2020-05-21 16:00:55 -07:00
{ value : returnByValue } ,
. . . args
2020-03-19 13:07:33 -07:00
] ,
returnByValue ,
awaitPromise : true ,
userGesture : true
} ) . catch ( rewriteError ) ;
if ( exceptionDetails )
throw new Error ( 'Evaluation failed: ' + getExceptionMessage ( exceptionDetails ) ) ;
2020-05-27 17:19:05 -07:00
return returnByValue ? parseEvaluationResultValue ( remoteObject . value ) : context . createHandle ( remoteObject ) ;
2020-03-19 13:07:33 -07:00
} finally {
dispose ( ) ;
}
2019-12-19 16:53:24 -08:00
}
async getProperties ( handle : js.JSHandle ) : Promise < Map < string , js.JSHandle > > {
2020-05-27 17:19:05 -07:00
const objectId = handle . _objectId ;
2020-01-13 13:33:25 -08:00
if ( ! objectId )
return new Map ( ) ;
2019-12-19 16:53:24 -08:00
const response = await this . _client . send ( 'Runtime.getProperties' , {
2020-01-13 13:33:25 -08:00
objectId ,
2019-12-19 16:53:24 -08:00
ownProperties : true
} ) ;
const result = new Map ( ) ;
for ( const property of response . result ) {
2020-05-27 17:19:05 -07:00
if ( ! property . enumerable || ! property . value )
2019-12-19 16:53:24 -08:00
continue ;
2020-05-15 15:21:49 -07:00
result . set ( property . name , handle . _context . createHandle ( property . value ) ) ;
2019-12-19 16:53:24 -08:00
}
return result ;
}
async releaseHandle ( handle : js.JSHandle ) : Promise < void > {
2020-05-27 17:19:05 -07:00
if ( ! handle . _objectId )
return ;
await releaseObject ( this . _client , handle . _objectId ) ;
2019-12-19 16:53:24 -08:00
}
async handleJSONValue < T > ( handle : js.JSHandle < T > ) : Promise < T > {
2020-05-27 17:19:05 -07:00
if ( handle . _objectId ) {
return this . _callOnUtilityScript ( handle . _context ,
` jsonValue ` , [
{ objectId : handle._objectId } ,
] , true , ( ) = > { } ) ;
2019-12-19 16:53:24 -08:00
}
2020-05-27 17:19:05 -07:00
return handle . _value ;
2019-12-19 16:53:24 -08:00
}
}
2020-05-20 15:55:33 -07:00
function rewriteError ( error : Error ) : Protocol . Runtime . evaluateReturnValue {
if ( error . message . includes ( 'Object reference chain is too long' ) )
return { result : { type : 'undefined' } } ;
if ( error . message . includes ( 'Object couldn\'t be returned by value' ) )
return { result : { type : 'undefined' } } ;
if ( error . message . endsWith ( 'Cannot find context with specified id' ) || error . message . endsWith ( 'Inspected target navigated or closed' ) || error . message . endsWith ( 'Execution context was destroyed.' ) )
throw new Error ( 'Execution context was destroyed, most likely because of a navigation.' ) ;
if ( error instanceof TypeError && error . message . startsWith ( 'Converting circular structure to JSON' ) )
error . message += ' Are you passing a nested JSHandle?' ;
throw error ;
}