/**
 * 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.
 */ import { assert } from './assert.js' ; import { helper } from './helper.js' ; import { createJSHandle , JSHandle , ElementHandle } from './JSHandle.js' ; import { CDPSession } from './Connection.js' ; import { DOMWorld } from './DOMWorld.js' ; import { Frame } from './FrameManager.js' ; import { Protocol } from 'devtools-protocol' ; import { EvaluateHandleFn , SerializableOrJSHandle } from './EvalTypes.js' ; public export const EVALUATION_SCRIPT_URL = '__puppeteer_evaluation_script__' ; const SOURCE_URL_REGEX = /^[\040\t]*\/\/[@#] sourceURL=\s*(\S*?)\s*$/m ; /**
 * This class represents a context for JavaScript execution. A [Page] might have
 * many execution contexts:
 * - each
 *    https *   frame } has "default" execution context that is always created after frame is
 *   attached to DOM. This context is returned by the
 *    Frame . executionContext method.
 * -  https 's content scripts
 *   create additional execution contexts.
 *
 * Besides pages, execution contexts can be found in
 *  https * workers }.
 *
 *  public export class ExecutionContext { internal _client : CDPSession ; internal _world : DOMWorld ; internal _contextId : number ; internal _contextName : string ; internal constructor ( client , contextPayload , world ) { this . _client = client ; this . _world = world ; this . _contextId = contextPayload . id ; this . _contextName = contextPayload . name ; } remarks returns frame ( ) { return this . _world ? this . _world . frame ( ) : null ; } remarks example example example JSHandle instances can be passed as arguments to the
     * `executionContext.* evaluate`:
     * ```js
     * const oneHandle = await executionContext.evaluateHandle(() => 1);
     * const twoHandle = await executionContext.evaluateHandle(() => 2);
     * const result = await executionContext.evaluate(
     *    (a, b) => a + b, oneHandle, * twoHandle
     * );
     * await oneHandle.dispose();
     * await twoHandle.dispose();
     * console.log(result); // prints '3'.
     * ```
     *  param pageFunction param args returns async evaluate < ReturnType extends any > ( pageFunction , ... args ) { return await this . _evaluateInternal < ReturnType > ( true , pageFunction , ... args ) ; } remarks The only difference between `executionContext.evaluate` and
     * `executionContext.evaluateHandle` is that `executionContext.evaluateHandle`
     * returns an in-page object (a  JSHandle ).
     * If the function passed to the `executionContext.evaluateHandle` returns a
     * Promise, then `executionContext.evaluateHandle` would wait for the
     * promise to resolve and return its value.
     *
     *  example example example param pageFunction param args returns A promise that resolves to the return value of the given function
     * as an in-page object (a  JSHandle ).
      async evaluateHandle < HandleType extends JSHandle | ElementHandle = JSHandle > ( pageFunction , ... args ) { return this . _evaluateInternal < HandleType > ( false , pageFunction , ... args ) ; } private async _evaluateInternal < ReturnType > ( returnByValue , pageFunction , ... args ) { const suffix = `//# sourceURL=${ EVALUATION_SCRIPT_URL }` ; if ( helper . isString ( pageFunction ) ) { const contextId = this . _contextId ; const expression = pageFunction ; const expressionWithSourceUrl = SOURCE_URL_REGEX . test ( expression ) ? expression : expression + '\n' + suffix ; const { exceptionDetails , result : remoteObject } = await this . _client . send ( 'Runtime.evaluate' , { expression : expressionWithSourceUrl , contextId , returnByValue , awaitPromise : true , userGesture : true , } ) . catch ( rewriteError ) ; if ( exceptionDetails ) throw new Error ( 'Evaluation failed: ' + helper . getExceptionMessage ( exceptionDetails ) ) ; return returnByValue ? helper . valueFromRemoteObject ( remoteObject ) : createJSHandle ( this , remoteObject ) ; } if ( typeof pageFunction !== 'function' ) throw new Error ( `Expected to get |string| or |function| as the first argument, but got "${ pageFunction }" instead.` ) ; let functionText = pageFunction . toString ( ) ; try { new Function ( '(' + functionText + ')' ) ; } catch ( error ) { if ( functionText . startsWith ( 'async ' ) ) functionText = 'async function ' + functionText . substring ( 'async ' . length ) ; else functionText = 'function ' + functionText ; try { new Function ( '(' + functionText + ')' ) ; } catch ( error ) { throw new Error ( 'Passed function is not well-serializable!' ) ; } } let callFunctionOnPromise ; try { callFunctionOnPromise = this . _client . send ( 'Runtime.callFunctionOn' , { functionDeclaration : functionText + '\n' + suffix + '\n' , executionContextId : this . _contextId , arguments : args . map ( convertArgument . bind ( this ) ) , returnByValue , awaitPromise : true , userGesture : true , } ) ; } catch ( error ) { if ( error instanceof TypeError && error . message . startsWith ( 'Converting circular structure to JSON' ) ) error . message += ' Are you passing a nested JSHandle?' ; throw error ; } const { exceptionDetails , result : remoteObject } = await callFunctionOnPromise . catch ( rewriteError ) ; if ( exceptionDetails ) throw new Error ( 'Evaluation failed: ' + helper . getExceptionMessage ( exceptionDetails ) ) ; return returnByValue ? helper . valueFromRemoteObject ( remoteObject ) : createJSHandle ( this , remoteObject ) ; param { * } arg returns { * } this { ExecutionContext } function convertArgument ( this , arg ) { if ( typeof arg === 'bigint' ) return { unserializableValue : `${ arg . toString ( ) }n` } ; if ( Object . is ( arg , - 0 ) ) return { unserializableValue : '-0' } ; if ( Object . is ( arg , Infinity ) ) return { unserializableValue : 'Infinity' } ; if ( Object . is ( arg , - Infinity ) ) return { unserializableValue : '-Infinity' } ; if ( Object . is ( arg , NaN ) ) return { unserializableValue : 'NaN' } ; const objectHandle = arg && arg instanceof JSHandle ? arg : null ; if ( objectHandle ) { if ( objectHandle . _context !== this ) throw new Error ( 'JSHandles can be evaluated only in the context they were created!' ) ; if ( objectHandle . _disposed ) throw new Error ( 'JSHandle is disposed!' ) ; if ( objectHandle . _remoteObject . unserializableValue ) return { unserializableValue : objectHandle . _remoteObject . unserializableValue , } ; if ( ! objectHandle . _remoteObject . objectId ) return { value : objectHandle . _remoteObject . value } ; return { objectId : objectHandle . _remoteObject . objectId } ; } return { value : arg } ; } function rewriteError ( error ) { 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' ) ) throw new Error ( 'Execution context was destroyed, most likely because of a navigation.' ) ; throw error ; } } remarks example param prototypeHandle returns async queryObjects ( prototypeHandle ) { assert ( ! prototypeHandle . _disposed , 'Prototype JSHandle is disposed!' ) ; assert ( prototypeHandle . _remoteObject . objectId , 'Prototype JSHandle must not be referencing primitive value' ) ; const response = await this . _client . send ( 'Runtime.queryObjects' , { prototypeObjectId : prototypeHandle . _remoteObject . objectId , } ) ; return createJSHandle ( this , response . objects ) ; } internal async _adoptBackendNodeId ( backendNodeId ) { const { object } = await this . _client . send ( 'DOM.resolveNode' , { backendNodeId : backendNodeId , executionContextId : this . _contextId , } ) ; return createJSHandle ( this , object ) as ElementHandle ; } internal async _adoptElementHandle ( elementHandle ) { assert ( elementHandle . executionContext ( ) !== this , 'Cannot adopt handle that already belongs to this execution context' ) ; assert ( this . _world , 'Cannot adopt handle without DOMWorld' ) ; const nodeInfo = await this . _client . send ( 'DOM.describeNode' , { objectId : elementHandle . _remoteObject . objectId , } ) ; return this . _adoptBackendNodeId ( nodeInfo . node . backendNodeId ) ; } }