123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- const {helper, assert} = require('./helper');
- const {createJSHandle, JSHandle} = require('./JSHandle');
- const EVALUATION_SCRIPT_URL = '__puppeteer_evaluation_script__';
- const SOURCE_URL_REGEX = /^[\040\t]*\/\/[@#] sourceURL=\s*(\S*?)\s*$/m;
- class ExecutionContext {
-
- constructor(client, contextPayload, world) {
- this._client = client;
- this._world = world;
- this._contextId = contextPayload.id;
- }
-
- frame() {
- return this._world ? this._world.frame() : null;
- }
-
- async evaluate(pageFunction, ...args) {
- return await this._evaluateInternal(true , pageFunction, ...args);
- }
-
- async evaluateHandle(pageFunction, ...args) {
- return this._evaluateInternal(false , pageFunction, ...args);
- }
-
- async _evaluateInternal(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 (e1) {
-
-
- if (functionText.startsWith('async '))
- functionText = 'async function ' + functionText.substring('async '.length);
- else
- functionText = 'function ' + functionText;
- try {
- new Function('(' + functionText + ')');
- } catch (e2) {
-
- 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 (err) {
- if (err instanceof TypeError && err.message.startsWith('Converting circular structure to JSON'))
- err.message += ' Are you passing a nested JSHandle?';
- throw err;
- }
- 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);
-
- function convertArgument(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;
- }
- }
-
- 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);
- }
-
- async _adoptBackendNodeId(backendNodeId) {
- const {object} = await this._client.send('DOM.resolveNode', {
- backendNodeId: backendNodeId,
- executionContextId: this._contextId,
- });
- return (createJSHandle(this, object));
- }
-
- 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);
- }
- }
- module.exports = {ExecutionContext, EVALUATION_SCRIPT_URL};
|