UNPKG

6.87 kBJavaScriptView Raw
1/**
2 * Copyright 2017 Google Inc. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17const {helper} = require('./helper');
18
19class ExecutionContext {
20 /**
21 * @param {!Puppeteer.CDPSession} client
22 * @param {!Object} contextPayload
23 * @param {function(*):!JSHandle} objectHandleFactory
24 */
25 constructor(client, contextPayload, objectHandleFactory) {
26 this._client = client;
27 this._contextId = contextPayload.id;
28
29 const auxData = contextPayload.auxData || {isDefault: true};
30 this._frameId = auxData.frameId || null;
31 this._isDefault = !!auxData.isDefault;
32 this._objectHandleFactory = objectHandleFactory;
33 }
34
35 /**
36 * @param {Function|string} pageFunction
37 * @param {...*} args
38 * @return {!Promise<(!Object|undefined)>}
39 */
40 async evaluate(pageFunction, ...args) {
41 const handle = await this.evaluateHandle(pageFunction, ...args);
42 const result = await handle.jsonValue().catch(error => undefined);
43 await handle.dispose();
44 return result;
45 }
46
47 /**
48 * @param {Function|string} pageFunction
49 * @param {...*} args
50 * @return {!Promise<!JSHandle>}
51 */
52 async evaluateHandle(pageFunction, ...args) {
53 if (helper.isString(pageFunction)) {
54 const contextId = this._contextId;
55 const expression = pageFunction;
56 const { exceptionDetails, result: remoteObject } = await this._client.send('Runtime.evaluate', { expression, contextId, returnByValue: false, awaitPromise: true});
57 if (exceptionDetails)
58 throw new Error('Evaluation failed: ' + helper.getExceptionMessage(exceptionDetails));
59 return this._objectHandleFactory(remoteObject);
60 }
61
62 const { exceptionDetails, result: remoteObject } = await this._client.send('Runtime.callFunctionOn', {
63 functionDeclaration: pageFunction.toString(),
64 executionContextId: this._contextId,
65 arguments: args.map(convertArgument.bind(this)),
66 returnByValue: false,
67 awaitPromise: true
68 });
69 if (exceptionDetails)
70 throw new Error('Evaluation failed: ' + helper.getExceptionMessage(exceptionDetails));
71 return this._objectHandleFactory(remoteObject);
72
73 /**
74 * @param {*} arg
75 * @return {*}
76 * @this {Frame}
77 */
78 function convertArgument(arg) {
79 if (Object.is(arg, -0))
80 return { unserializableValue: '-0' };
81 if (Object.is(arg, Infinity))
82 return { unserializableValue: 'Infinity' };
83 if (Object.is(arg, -Infinity))
84 return { unserializableValue: '-Infinity' };
85 if (Object.is(arg, NaN))
86 return { unserializableValue: 'NaN' };
87 const objectHandle = arg && (arg instanceof JSHandle) ? arg : null;
88 if (objectHandle) {
89 if (objectHandle._context !== this)
90 throw new Error('JSHandles can be evaluated only in the context they were created!');
91 if (objectHandle._disposed)
92 throw new Error('JSHandle is disposed!');
93 if (objectHandle._remoteObject.unserializableValue)
94 return { unserializableValue: objectHandle._remoteObject.unserializableValue };
95 if (!objectHandle._remoteObject.objectId)
96 return { value: objectHandle._remoteObject.value };
97 return { objectId: objectHandle._remoteObject.objectId };
98 }
99 return { value: arg };
100 }
101 }
102
103 /**
104 * @param {!JSHandle} prototypeHandle
105 * @return {!Promise<!JSHandle>}
106 */
107 async queryObjects(prototypeHandle) {
108 console.assert(!prototypeHandle._disposed, 'Prototype JSHandle is disposed!');
109 console.assert(prototypeHandle._remoteObject.objectId, 'Prototype JSHandle must not be referencing primitive value');
110 const response = await this._client.send('Runtime.queryObjects', {
111 prototypeObjectId: prototypeHandle._remoteObject.objectId
112 });
113 return this._objectHandleFactory(response.objects);
114 }
115}
116
117class JSHandle {
118 /**
119 * @param {!ExecutionContext} context
120 * @param {!Puppeteer.CDPSession} client
121 * @param {!Object} remoteObject
122 */
123 constructor(context, client, remoteObject) {
124 this._context = context;
125 this._client = client;
126 this._remoteObject = remoteObject;
127 this._disposed = false;
128 }
129
130 /**
131 * @return {!ExecutionContext}
132 */
133 executionContext() {
134 return this._context;
135 }
136
137 /**
138 * @param {string} propertyName
139 * @return {!Promise<?JSHandle>}
140 */
141 async getProperty(propertyName) {
142 const objectHandle = await this._context.evaluateHandle((object, propertyName) => {
143 const result = {__proto__: null};
144 result[propertyName] = object[propertyName];
145 return result;
146 }, this, propertyName);
147 const properties = await objectHandle.getProperties();
148 const result = properties.get(propertyName) || null;
149 await objectHandle.dispose();
150 return result;
151 }
152
153 /**
154 * @return {!Promise<Map<string, !JSHandle>>}
155 */
156 async getProperties() {
157 const response = await this._client.send('Runtime.getProperties', {
158 objectId: this._remoteObject.objectId,
159 ownProperties: true
160 });
161 const result = new Map();
162 for (const property of response.result) {
163 if (!property.enumerable)
164 continue;
165 result.set(property.name, this._context._objectHandleFactory(property.value));
166 }
167 return result;
168 }
169
170 /**
171 * @return {!Promise<?Object>}
172 */
173 async jsonValue() {
174 if (this._remoteObject.objectId) {
175 const response = await this._client.send('Runtime.callFunctionOn', {
176 functionDeclaration: 'function() { return this; }',
177 objectId: this._remoteObject.objectId,
178 returnByValue: true,
179 awaitPromise: true,
180 });
181 return helper.valueFromRemoteObject(response.result);
182 }
183 return helper.valueFromRemoteObject(this._remoteObject);
184 }
185
186 /**
187 * @return {?Puppeteer.ElementHandle}
188 */
189 asElement() {
190 return null;
191 }
192
193 async dispose() {
194 if (this._disposed)
195 return;
196 this._disposed = true;
197 await helper.releaseObject(this._client, this._remoteObject);
198 }
199
200 /**
201 * @override
202 * @return {string}
203 */
204 toString() {
205 if (this._remoteObject.objectId) {
206 const type = this._remoteObject.subtype || this._remoteObject.type;
207 return 'JSHandle@' + type;
208 }
209 return 'JSHandle:' + helper.valueFromRemoteObject(this._remoteObject);
210 }
211}
212
213helper.tracePublicAPI(JSHandle);
214module.exports = {ExecutionContext, JSHandle};