OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2011 Google Inc. All rights reserved. | |
3 * | |
4 * Redistribution and use in source and binary forms, with or without | |
5 * modification, are permitted provided that the following conditions are | |
6 * met: | |
7 * | |
8 * * Redistributions of source code must retain the above copyright | |
9 * notice, this list of conditions and the following disclaimer. | |
10 * * Redistributions in binary form must reproduce the above | |
11 * copyright notice, this list of conditions and the following disclaimer | |
12 * in the documentation and/or other materials provided with the | |
13 * distribution. | |
14 * * Neither the name of Google Inc. nor the names of its | |
15 * contributors may be used to endorse or promote products derived from | |
16 * this software without specific prior written permission. | |
17 * | |
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 */ | |
30 | |
31 #include "src/inspector/V8RuntimeAgentImpl.h" | |
32 | |
33 #include "src/inspector/InjectedScript.h" | |
34 #include "src/inspector/InspectedContext.h" | |
35 #include "src/inspector/RemoteObjectId.h" | |
36 #include "src/inspector/StringUtil.h" | |
37 #include "src/inspector/V8ConsoleMessage.h" | |
38 #include "src/inspector/V8Debugger.h" | |
39 #include "src/inspector/V8DebuggerAgentImpl.h" | |
40 #include "src/inspector/V8InspectorImpl.h" | |
41 #include "src/inspector/V8InspectorSessionImpl.h" | |
42 #include "src/inspector/V8StackTraceImpl.h" | |
43 #include "src/inspector/protocol/Protocol.h" | |
44 | |
45 #include "include/v8-inspector.h" | |
46 | |
47 namespace v8_inspector { | |
48 | |
49 namespace V8RuntimeAgentImplState { | |
50 static const char customObjectFormatterEnabled[] = | |
51 "customObjectFormatterEnabled"; | |
52 static const char runtimeEnabled[] = "runtimeEnabled"; | |
53 }; | |
54 | |
55 using protocol::Runtime::RemoteObject; | |
56 | |
57 static bool hasInternalError(ErrorString* errorString, bool hasError) { | |
58 if (hasError) *errorString = "Internal error"; | |
59 return hasError; | |
60 } | |
61 | |
62 namespace { | |
63 | |
64 template <typename Callback> | |
65 class ProtocolPromiseHandler { | |
66 public: | |
67 static void add(V8InspectorImpl* inspector, v8::Local<v8::Context> context, | |
68 v8::MaybeLocal<v8::Value> value, | |
69 const String16& notPromiseError, int contextGroupId, | |
70 int executionContextId, const String16& objectGroup, | |
71 bool returnByValue, bool generatePreview, | |
72 std::unique_ptr<Callback> callback) { | |
73 if (value.IsEmpty()) { | |
74 callback->sendFailure("Internal error"); | |
75 return; | |
76 } | |
77 if (!value.ToLocalChecked()->IsPromise()) { | |
78 callback->sendFailure(notPromiseError); | |
79 return; | |
80 } | |
81 v8::Local<v8::Promise> promise = | |
82 v8::Local<v8::Promise>::Cast(value.ToLocalChecked()); | |
83 Callback* rawCallback = callback.get(); | |
84 ProtocolPromiseHandler<Callback>* handler = new ProtocolPromiseHandler( | |
85 inspector, contextGroupId, executionContextId, objectGroup, | |
86 returnByValue, generatePreview, std::move(callback)); | |
87 v8::Local<v8::Value> wrapper = handler->m_wrapper.Get(inspector->isolate()); | |
88 | |
89 v8::Local<v8::Function> thenCallbackFunction = | |
90 v8::Function::New(context, thenCallback, wrapper, 0, | |
91 v8::ConstructorBehavior::kThrow) | |
92 .ToLocalChecked(); | |
93 if (promise->Then(context, thenCallbackFunction).IsEmpty()) { | |
94 rawCallback->sendFailure("Internal error"); | |
95 return; | |
96 } | |
97 v8::Local<v8::Function> catchCallbackFunction = | |
98 v8::Function::New(context, catchCallback, wrapper, 0, | |
99 v8::ConstructorBehavior::kThrow) | |
100 .ToLocalChecked(); | |
101 if (promise->Catch(context, catchCallbackFunction).IsEmpty()) { | |
102 rawCallback->sendFailure("Internal error"); | |
103 return; | |
104 } | |
105 } | |
106 | |
107 private: | |
108 static void thenCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { | |
109 ProtocolPromiseHandler<Callback>* handler = | |
110 static_cast<ProtocolPromiseHandler<Callback>*>( | |
111 info.Data().As<v8::External>()->Value()); | |
112 DCHECK(handler); | |
113 v8::Local<v8::Value> value = | |
114 info.Length() > 0 | |
115 ? info[0] | |
116 : v8::Local<v8::Value>::Cast(v8::Undefined(info.GetIsolate())); | |
117 handler->m_callback->sendSuccess( | |
118 handler->wrapObject(value), | |
119 Maybe<protocol::Runtime::ExceptionDetails>()); | |
120 } | |
121 | |
122 static void catchCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { | |
123 ProtocolPromiseHandler<Callback>* handler = | |
124 static_cast<ProtocolPromiseHandler<Callback>*>( | |
125 info.Data().As<v8::External>()->Value()); | |
126 DCHECK(handler); | |
127 v8::Local<v8::Value> value = | |
128 info.Length() > 0 | |
129 ? info[0] | |
130 : v8::Local<v8::Value>::Cast(v8::Undefined(info.GetIsolate())); | |
131 | |
132 std::unique_ptr<V8StackTraceImpl> stack = | |
133 handler->m_inspector->debugger()->captureStackTrace(true); | |
134 std::unique_ptr<protocol::Runtime::ExceptionDetails> exceptionDetails = | |
135 protocol::Runtime::ExceptionDetails::create() | |
136 .setExceptionId(handler->m_inspector->nextExceptionId()) | |
137 .setText("Uncaught (in promise)") | |
138 .setLineNumber(stack && !stack->isEmpty() ? stack->topLineNumber() | |
139 : 0) | |
140 .setColumnNumber( | |
141 stack && !stack->isEmpty() ? stack->topColumnNumber() : 0) | |
142 .setException(handler->wrapObject(value)) | |
143 .build(); | |
144 if (stack) | |
145 exceptionDetails->setStackTrace(stack->buildInspectorObjectImpl()); | |
146 if (stack && !stack->isEmpty()) | |
147 exceptionDetails->setScriptId(toString16(stack->topScriptId())); | |
148 handler->m_callback->sendSuccess(handler->wrapObject(value), | |
149 std::move(exceptionDetails)); | |
150 } | |
151 | |
152 ProtocolPromiseHandler(V8InspectorImpl* inspector, int contextGroupId, | |
153 int executionContextId, const String16& objectGroup, | |
154 bool returnByValue, bool generatePreview, | |
155 std::unique_ptr<Callback> callback) | |
156 : m_inspector(inspector), | |
157 m_contextGroupId(contextGroupId), | |
158 m_executionContextId(executionContextId), | |
159 m_objectGroup(objectGroup), | |
160 m_returnByValue(returnByValue), | |
161 m_generatePreview(generatePreview), | |
162 m_callback(std::move(callback)), | |
163 m_wrapper(inspector->isolate(), | |
164 v8::External::New(inspector->isolate(), this)) { | |
165 m_wrapper.SetWeak(this, cleanup, v8::WeakCallbackType::kParameter); | |
166 } | |
167 | |
168 static void cleanup( | |
169 const v8::WeakCallbackInfo<ProtocolPromiseHandler<Callback>>& data) { | |
170 if (!data.GetParameter()->m_wrapper.IsEmpty()) { | |
171 data.GetParameter()->m_wrapper.Reset(); | |
172 data.SetSecondPassCallback(cleanup); | |
173 } else { | |
174 data.GetParameter()->m_callback->sendFailure("Promise was collected"); | |
175 delete data.GetParameter(); | |
176 } | |
177 } | |
178 | |
179 std::unique_ptr<protocol::Runtime::RemoteObject> wrapObject( | |
180 v8::Local<v8::Value> value) { | |
181 ErrorString errorString; | |
182 InjectedScript::ContextScope scope(&errorString, m_inspector, | |
183 m_contextGroupId, m_executionContextId); | |
184 if (!scope.initialize()) { | |
185 m_callback->sendFailure(errorString); | |
186 return nullptr; | |
187 } | |
188 std::unique_ptr<protocol::Runtime::RemoteObject> wrappedValue = | |
189 scope.injectedScript()->wrapObject(&errorString, value, m_objectGroup, | |
190 m_returnByValue, m_generatePreview); | |
191 if (!wrappedValue) { | |
192 m_callback->sendFailure(errorString); | |
193 return nullptr; | |
194 } | |
195 return wrappedValue; | |
196 } | |
197 | |
198 V8InspectorImpl* m_inspector; | |
199 int m_contextGroupId; | |
200 int m_executionContextId; | |
201 String16 m_objectGroup; | |
202 bool m_returnByValue; | |
203 bool m_generatePreview; | |
204 std::unique_ptr<Callback> m_callback; | |
205 v8::Global<v8::External> m_wrapper; | |
206 }; | |
207 | |
208 template <typename Callback> | |
209 bool wrapEvaluateResultAsync(InjectedScript* injectedScript, | |
210 v8::MaybeLocal<v8::Value> maybeResultValue, | |
211 const v8::TryCatch& tryCatch, | |
212 const String16& objectGroup, bool returnByValue, | |
213 bool generatePreview, Callback* callback) { | |
214 std::unique_ptr<RemoteObject> result; | |
215 Maybe<protocol::Runtime::ExceptionDetails> exceptionDetails; | |
216 | |
217 ErrorString errorString; | |
218 injectedScript->wrapEvaluateResult( | |
219 &errorString, maybeResultValue, tryCatch, objectGroup, returnByValue, | |
220 generatePreview, &result, &exceptionDetails); | |
221 if (errorString.isEmpty()) { | |
222 callback->sendSuccess(std::move(result), exceptionDetails); | |
223 return true; | |
224 } | |
225 callback->sendFailure(errorString); | |
226 return false; | |
227 } | |
228 | |
229 int ensureContext(ErrorString* errorString, V8InspectorImpl* inspector, | |
230 int contextGroupId, const Maybe<int>& executionContextId) { | |
231 int contextId; | |
232 if (executionContextId.isJust()) { | |
233 contextId = executionContextId.fromJust(); | |
234 } else { | |
235 v8::HandleScope handles(inspector->isolate()); | |
236 v8::Local<v8::Context> defaultContext = | |
237 inspector->client()->ensureDefaultContextInGroup(contextGroupId); | |
238 if (defaultContext.IsEmpty()) { | |
239 *errorString = "Cannot find default execution context"; | |
240 return 0; | |
241 } | |
242 contextId = V8Debugger::contextId(defaultContext); | |
243 } | |
244 return contextId; | |
245 } | |
246 | |
247 } // namespace | |
248 | |
249 V8RuntimeAgentImpl::V8RuntimeAgentImpl( | |
250 V8InspectorSessionImpl* session, protocol::FrontendChannel* FrontendChannel, | |
251 protocol::DictionaryValue* state) | |
252 : m_session(session), | |
253 m_state(state), | |
254 m_frontend(FrontendChannel), | |
255 m_inspector(session->inspector()), | |
256 m_enabled(false) {} | |
257 | |
258 V8RuntimeAgentImpl::~V8RuntimeAgentImpl() {} | |
259 | |
260 void V8RuntimeAgentImpl::evaluate( | |
261 const String16& expression, const Maybe<String16>& objectGroup, | |
262 const Maybe<bool>& includeCommandLineAPI, const Maybe<bool>& silent, | |
263 const Maybe<int>& executionContextId, const Maybe<bool>& returnByValue, | |
264 const Maybe<bool>& generatePreview, const Maybe<bool>& userGesture, | |
265 const Maybe<bool>& awaitPromise, | |
266 std::unique_ptr<EvaluateCallback> callback) { | |
267 ErrorString errorString; | |
268 int contextId = | |
269 ensureContext(&errorString, m_inspector, m_session->contextGroupId(), | |
270 executionContextId); | |
271 if (!errorString.isEmpty()) { | |
272 callback->sendFailure(errorString); | |
273 return; | |
274 } | |
275 | |
276 InjectedScript::ContextScope scope(&errorString, m_inspector, | |
277 m_session->contextGroupId(), contextId); | |
278 if (!scope.initialize()) { | |
279 callback->sendFailure(errorString); | |
280 return; | |
281 } | |
282 | |
283 if (silent.fromMaybe(false)) scope.ignoreExceptionsAndMuteConsole(); | |
284 if (userGesture.fromMaybe(false)) scope.pretendUserGesture(); | |
285 | |
286 if (includeCommandLineAPI.fromMaybe(false) && | |
287 !scope.installCommandLineAPI()) { | |
288 callback->sendFailure(errorString); | |
289 return; | |
290 } | |
291 | |
292 bool evalIsDisabled = !scope.context()->IsCodeGenerationFromStringsAllowed(); | |
293 // Temporarily enable allow evals for inspector. | |
294 if (evalIsDisabled) scope.context()->AllowCodeGenerationFromStrings(true); | |
295 | |
296 v8::MaybeLocal<v8::Value> maybeResultValue; | |
297 v8::Local<v8::Script> script = m_inspector->compileScript( | |
298 scope.context(), toV8String(m_inspector->isolate(), expression), | |
299 String16(), false); | |
300 if (!script.IsEmpty()) | |
301 maybeResultValue = m_inspector->runCompiledScript(scope.context(), script); | |
302 | |
303 if (evalIsDisabled) scope.context()->AllowCodeGenerationFromStrings(false); | |
304 | |
305 // Re-initialize after running client's code, as it could have destroyed | |
306 // context or session. | |
307 if (!scope.initialize()) { | |
308 callback->sendFailure(errorString); | |
309 return; | |
310 } | |
311 | |
312 if (!awaitPromise.fromMaybe(false) || scope.tryCatch().HasCaught()) { | |
313 wrapEvaluateResultAsync(scope.injectedScript(), maybeResultValue, | |
314 scope.tryCatch(), objectGroup.fromMaybe(""), | |
315 returnByValue.fromMaybe(false), | |
316 generatePreview.fromMaybe(false), callback.get()); | |
317 return; | |
318 } | |
319 ProtocolPromiseHandler<EvaluateCallback>::add( | |
320 m_inspector, scope.context(), maybeResultValue, | |
321 "Result of the evaluation is not a promise", m_session->contextGroupId(), | |
322 scope.injectedScript()->context()->contextId(), objectGroup.fromMaybe(""), | |
323 returnByValue.fromMaybe(false), generatePreview.fromMaybe(false), | |
324 std::move(callback)); | |
325 } | |
326 | |
327 void V8RuntimeAgentImpl::awaitPromise( | |
328 const String16& promiseObjectId, const Maybe<bool>& returnByValue, | |
329 const Maybe<bool>& generatePreview, | |
330 std::unique_ptr<AwaitPromiseCallback> callback) { | |
331 ErrorString errorString; | |
332 InjectedScript::ObjectScope scope( | |
333 &errorString, m_inspector, m_session->contextGroupId(), promiseObjectId); | |
334 if (!scope.initialize()) { | |
335 callback->sendFailure(errorString); | |
336 return; | |
337 } | |
338 ProtocolPromiseHandler<AwaitPromiseCallback>::add( | |
339 m_inspector, scope.context(), scope.object(), | |
340 "Could not find promise with given id", m_session->contextGroupId(), | |
341 scope.injectedScript()->context()->contextId(), scope.objectGroupName(), | |
342 returnByValue.fromMaybe(false), generatePreview.fromMaybe(false), | |
343 std::move(callback)); | |
344 } | |
345 | |
346 void V8RuntimeAgentImpl::callFunctionOn( | |
347 const String16& objectId, const String16& expression, | |
348 const Maybe<protocol::Array<protocol::Runtime::CallArgument>>& | |
349 optionalArguments, | |
350 const Maybe<bool>& silent, const Maybe<bool>& returnByValue, | |
351 const Maybe<bool>& generatePreview, const Maybe<bool>& userGesture, | |
352 const Maybe<bool>& awaitPromise, | |
353 std::unique_ptr<CallFunctionOnCallback> callback) { | |
354 ErrorString errorString; | |
355 InjectedScript::ObjectScope scope(&errorString, m_inspector, | |
356 m_session->contextGroupId(), objectId); | |
357 if (!scope.initialize()) { | |
358 callback->sendFailure(errorString); | |
359 return; | |
360 } | |
361 | |
362 std::unique_ptr<v8::Local<v8::Value>[]> argv = nullptr; | |
363 int argc = 0; | |
364 if (optionalArguments.isJust()) { | |
365 protocol::Array<protocol::Runtime::CallArgument>* arguments = | |
366 optionalArguments.fromJust(); | |
367 argc = arguments->length(); | |
368 argv.reset(new v8::Local<v8::Value>[argc]); | |
369 for (int i = 0; i < argc; ++i) { | |
370 v8::Local<v8::Value> argumentValue; | |
371 if (!scope.injectedScript() | |
372 ->resolveCallArgument(&errorString, arguments->get(i)) | |
373 .ToLocal(&argumentValue)) { | |
374 callback->sendFailure(errorString); | |
375 return; | |
376 } | |
377 argv[i] = argumentValue; | |
378 } | |
379 } | |
380 | |
381 if (silent.fromMaybe(false)) scope.ignoreExceptionsAndMuteConsole(); | |
382 if (userGesture.fromMaybe(false)) scope.pretendUserGesture(); | |
383 | |
384 v8::MaybeLocal<v8::Value> maybeFunctionValue = | |
385 m_inspector->compileAndRunInternalScript( | |
386 scope.context(), | |
387 toV8String(m_inspector->isolate(), "(" + expression + ")")); | |
388 // Re-initialize after running client's code, as it could have destroyed | |
389 // context or session. | |
390 if (!scope.initialize()) { | |
391 callback->sendFailure(errorString); | |
392 return; | |
393 } | |
394 | |
395 if (scope.tryCatch().HasCaught()) { | |
396 wrapEvaluateResultAsync(scope.injectedScript(), maybeFunctionValue, | |
397 scope.tryCatch(), scope.objectGroupName(), false, | |
398 false, callback.get()); | |
399 return; | |
400 } | |
401 | |
402 v8::Local<v8::Value> functionValue; | |
403 if (!maybeFunctionValue.ToLocal(&functionValue) || | |
404 !functionValue->IsFunction()) { | |
405 callback->sendFailure("Given expression does not evaluate to a function"); | |
406 return; | |
407 } | |
408 | |
409 v8::MaybeLocal<v8::Value> maybeResultValue = m_inspector->callFunction( | |
410 functionValue.As<v8::Function>(), scope.context(), scope.object(), argc, | |
411 argv.get()); | |
412 // Re-initialize after running client's code, as it could have destroyed | |
413 // context or session. | |
414 if (!scope.initialize()) { | |
415 callback->sendFailure(errorString); | |
416 return; | |
417 } | |
418 | |
419 if (!awaitPromise.fromMaybe(false) || scope.tryCatch().HasCaught()) { | |
420 wrapEvaluateResultAsync(scope.injectedScript(), maybeResultValue, | |
421 scope.tryCatch(), scope.objectGroupName(), | |
422 returnByValue.fromMaybe(false), | |
423 generatePreview.fromMaybe(false), callback.get()); | |
424 return; | |
425 } | |
426 | |
427 ProtocolPromiseHandler<CallFunctionOnCallback>::add( | |
428 m_inspector, scope.context(), maybeResultValue, | |
429 "Result of the function call is not a promise", | |
430 m_session->contextGroupId(), | |
431 scope.injectedScript()->context()->contextId(), scope.objectGroupName(), | |
432 returnByValue.fromMaybe(false), generatePreview.fromMaybe(false), | |
433 std::move(callback)); | |
434 } | |
435 | |
436 void V8RuntimeAgentImpl::getProperties( | |
437 ErrorString* errorString, const String16& objectId, | |
438 const Maybe<bool>& ownProperties, const Maybe<bool>& accessorPropertiesOnly, | |
439 const Maybe<bool>& generatePreview, | |
440 std::unique_ptr<protocol::Array<protocol::Runtime::PropertyDescriptor>>* | |
441 result, | |
442 Maybe<protocol::Array<protocol::Runtime::InternalPropertyDescriptor>>* | |
443 internalProperties, | |
444 Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) { | |
445 using protocol::Runtime::InternalPropertyDescriptor; | |
446 | |
447 InjectedScript::ObjectScope scope(errorString, m_inspector, | |
448 m_session->contextGroupId(), objectId); | |
449 if (!scope.initialize()) return; | |
450 | |
451 scope.ignoreExceptionsAndMuteConsole(); | |
452 if (!scope.object()->IsObject()) { | |
453 *errorString = "Value with given id is not an object"; | |
454 return; | |
455 } | |
456 | |
457 v8::Local<v8::Object> object = scope.object().As<v8::Object>(); | |
458 scope.injectedScript()->getProperties( | |
459 errorString, object, scope.objectGroupName(), | |
460 ownProperties.fromMaybe(false), accessorPropertiesOnly.fromMaybe(false), | |
461 generatePreview.fromMaybe(false), result, exceptionDetails); | |
462 if (!errorString->isEmpty() || exceptionDetails->isJust() || | |
463 accessorPropertiesOnly.fromMaybe(false)) | |
464 return; | |
465 v8::Local<v8::Array> propertiesArray; | |
466 if (hasInternalError(errorString, !m_inspector->debugger() | |
467 ->internalProperties(scope.context(), | |
468 scope.object()) | |
469 .ToLocal(&propertiesArray))) | |
470 return; | |
471 std::unique_ptr<protocol::Array<InternalPropertyDescriptor>> | |
472 propertiesProtocolArray = | |
473 protocol::Array<InternalPropertyDescriptor>::create(); | |
474 for (uint32_t i = 0; i < propertiesArray->Length(); i += 2) { | |
475 v8::Local<v8::Value> name; | |
476 if (hasInternalError( | |
477 errorString, | |
478 !propertiesArray->Get(scope.context(), i).ToLocal(&name)) || | |
479 !name->IsString()) | |
480 return; | |
481 v8::Local<v8::Value> value; | |
482 if (hasInternalError( | |
483 errorString, | |
484 !propertiesArray->Get(scope.context(), i + 1).ToLocal(&value))) | |
485 return; | |
486 std::unique_ptr<RemoteObject> wrappedValue = | |
487 scope.injectedScript()->wrapObject(errorString, value, | |
488 scope.objectGroupName()); | |
489 if (!wrappedValue) return; | |
490 propertiesProtocolArray->addItem( | |
491 InternalPropertyDescriptor::create() | |
492 .setName(toProtocolString(name.As<v8::String>())) | |
493 .setValue(std::move(wrappedValue)) | |
494 .build()); | |
495 } | |
496 if (!propertiesProtocolArray->length()) return; | |
497 *internalProperties = std::move(propertiesProtocolArray); | |
498 } | |
499 | |
500 void V8RuntimeAgentImpl::releaseObject(ErrorString* errorString, | |
501 const String16& objectId) { | |
502 InjectedScript::ObjectScope scope(errorString, m_inspector, | |
503 m_session->contextGroupId(), objectId); | |
504 if (!scope.initialize()) return; | |
505 scope.injectedScript()->releaseObject(objectId); | |
506 } | |
507 | |
508 void V8RuntimeAgentImpl::releaseObjectGroup(ErrorString*, | |
509 const String16& objectGroup) { | |
510 m_session->releaseObjectGroup(objectGroup); | |
511 } | |
512 | |
513 void V8RuntimeAgentImpl::runIfWaitingForDebugger(ErrorString* errorString) { | |
514 m_inspector->client()->runIfWaitingForDebugger(m_session->contextGroupId()); | |
515 } | |
516 | |
517 void V8RuntimeAgentImpl::setCustomObjectFormatterEnabled(ErrorString*, | |
518 bool enabled) { | |
519 m_state->setBoolean(V8RuntimeAgentImplState::customObjectFormatterEnabled, | |
520 enabled); | |
521 m_session->setCustomObjectFormatterEnabled(enabled); | |
522 } | |
523 | |
524 void V8RuntimeAgentImpl::discardConsoleEntries(ErrorString*) { | |
525 V8ConsoleMessageStorage* storage = | |
526 m_inspector->ensureConsoleMessageStorage(m_session->contextGroupId()); | |
527 storage->clear(); | |
528 } | |
529 | |
530 void V8RuntimeAgentImpl::compileScript( | |
531 ErrorString* errorString, const String16& expression, | |
532 const String16& sourceURL, bool persistScript, | |
533 const Maybe<int>& executionContextId, Maybe<String16>* scriptId, | |
534 Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) { | |
535 if (!m_enabled) { | |
536 *errorString = "Runtime agent is not enabled"; | |
537 return; | |
538 } | |
539 int contextId = | |
540 ensureContext(errorString, m_inspector, m_session->contextGroupId(), | |
541 executionContextId); | |
542 if (!errorString->isEmpty()) return; | |
543 InjectedScript::ContextScope scope(errorString, m_inspector, | |
544 m_session->contextGroupId(), contextId); | |
545 if (!scope.initialize()) return; | |
546 | |
547 if (!persistScript) m_inspector->debugger()->muteScriptParsedEvents(); | |
548 v8::Local<v8::Script> script = m_inspector->compileScript( | |
549 scope.context(), toV8String(m_inspector->isolate(), expression), | |
550 sourceURL, false); | |
551 if (!persistScript) m_inspector->debugger()->unmuteScriptParsedEvents(); | |
552 if (script.IsEmpty()) { | |
553 if (scope.tryCatch().HasCaught()) | |
554 *exceptionDetails = scope.injectedScript()->createExceptionDetails( | |
555 errorString, scope.tryCatch(), String16(), false); | |
556 else | |
557 *errorString = "Script compilation failed"; | |
558 return; | |
559 } | |
560 | |
561 if (!persistScript) return; | |
562 | |
563 String16 scriptValueId = | |
564 String16::fromInteger(script->GetUnboundScript()->GetId()); | |
565 std::unique_ptr<v8::Global<v8::Script>> global( | |
566 new v8::Global<v8::Script>(m_inspector->isolate(), script)); | |
567 m_compiledScripts[scriptValueId] = std::move(global); | |
568 *scriptId = scriptValueId; | |
569 } | |
570 | |
571 void V8RuntimeAgentImpl::runScript( | |
572 const String16& scriptId, const Maybe<int>& executionContextId, | |
573 const Maybe<String16>& objectGroup, const Maybe<bool>& silent, | |
574 const Maybe<bool>& includeCommandLineAPI, const Maybe<bool>& returnByValue, | |
575 const Maybe<bool>& generatePreview, const Maybe<bool>& awaitPromise, | |
576 std::unique_ptr<RunScriptCallback> callback) { | |
577 if (!m_enabled) { | |
578 callback->sendFailure("Runtime agent is not enabled"); | |
579 return; | |
580 } | |
581 | |
582 auto it = m_compiledScripts.find(scriptId); | |
583 if (it == m_compiledScripts.end()) { | |
584 callback->sendFailure("No script with given id"); | |
585 return; | |
586 } | |
587 | |
588 ErrorString errorString; | |
589 int contextId = | |
590 ensureContext(&errorString, m_inspector, m_session->contextGroupId(), | |
591 executionContextId); | |
592 if (!errorString.isEmpty()) { | |
593 callback->sendFailure(errorString); | |
594 return; | |
595 } | |
596 | |
597 InjectedScript::ContextScope scope(&errorString, m_inspector, | |
598 m_session->contextGroupId(), contextId); | |
599 if (!scope.initialize()) { | |
600 callback->sendFailure(errorString); | |
601 return; | |
602 } | |
603 | |
604 if (silent.fromMaybe(false)) scope.ignoreExceptionsAndMuteConsole(); | |
605 | |
606 std::unique_ptr<v8::Global<v8::Script>> scriptWrapper = std::move(it->second); | |
607 m_compiledScripts.erase(it); | |
608 v8::Local<v8::Script> script = scriptWrapper->Get(m_inspector->isolate()); | |
609 if (script.IsEmpty()) { | |
610 callback->sendFailure("Script execution failed"); | |
611 return; | |
612 } | |
613 | |
614 if (includeCommandLineAPI.fromMaybe(false) && !scope.installCommandLineAPI()) | |
615 return; | |
616 | |
617 v8::MaybeLocal<v8::Value> maybeResultValue = | |
618 m_inspector->runCompiledScript(scope.context(), script); | |
619 | |
620 // Re-initialize after running client's code, as it could have destroyed | |
621 // context or session. | |
622 if (!scope.initialize()) return; | |
623 | |
624 if (!awaitPromise.fromMaybe(false) || scope.tryCatch().HasCaught()) { | |
625 wrapEvaluateResultAsync(scope.injectedScript(), maybeResultValue, | |
626 scope.tryCatch(), objectGroup.fromMaybe(""), | |
627 returnByValue.fromMaybe(false), | |
628 generatePreview.fromMaybe(false), callback.get()); | |
629 return; | |
630 } | |
631 ProtocolPromiseHandler<RunScriptCallback>::add( | |
632 m_inspector, scope.context(), maybeResultValue.ToLocalChecked(), | |
633 "Result of the script execution is not a promise", | |
634 m_session->contextGroupId(), | |
635 scope.injectedScript()->context()->contextId(), objectGroup.fromMaybe(""), | |
636 returnByValue.fromMaybe(false), generatePreview.fromMaybe(false), | |
637 std::move(callback)); | |
638 } | |
639 | |
640 void V8RuntimeAgentImpl::restore() { | |
641 if (!m_state->booleanProperty(V8RuntimeAgentImplState::runtimeEnabled, false)) | |
642 return; | |
643 m_frontend.executionContextsCleared(); | |
644 ErrorString error; | |
645 enable(&error); | |
646 if (m_state->booleanProperty( | |
647 V8RuntimeAgentImplState::customObjectFormatterEnabled, false)) | |
648 m_session->setCustomObjectFormatterEnabled(true); | |
649 } | |
650 | |
651 void V8RuntimeAgentImpl::enable(ErrorString* errorString) { | |
652 if (m_enabled) return; | |
653 m_inspector->client()->beginEnsureAllContextsInGroup( | |
654 m_session->contextGroupId()); | |
655 m_enabled = true; | |
656 m_state->setBoolean(V8RuntimeAgentImplState::runtimeEnabled, true); | |
657 m_inspector->enableStackCapturingIfNeeded(); | |
658 m_session->reportAllContexts(this); | |
659 V8ConsoleMessageStorage* storage = | |
660 m_inspector->ensureConsoleMessageStorage(m_session->contextGroupId()); | |
661 for (const auto& message : storage->messages()) { | |
662 if (!reportMessage(message.get(), false)) return; | |
663 } | |
664 } | |
665 | |
666 void V8RuntimeAgentImpl::disable(ErrorString* errorString) { | |
667 if (!m_enabled) return; | |
668 m_enabled = false; | |
669 m_state->setBoolean(V8RuntimeAgentImplState::runtimeEnabled, false); | |
670 m_inspector->disableStackCapturingIfNeeded(); | |
671 m_session->discardInjectedScripts(); | |
672 reset(); | |
673 m_inspector->client()->endEnsureAllContextsInGroup( | |
674 m_session->contextGroupId()); | |
675 } | |
676 | |
677 void V8RuntimeAgentImpl::reset() { | |
678 m_compiledScripts.clear(); | |
679 if (m_enabled) { | |
680 if (const V8InspectorImpl::ContextByIdMap* contexts = | |
681 m_inspector->contextGroup(m_session->contextGroupId())) { | |
682 for (auto& idContext : *contexts) idContext.second->setReported(false); | |
683 } | |
684 m_frontend.executionContextsCleared(); | |
685 } | |
686 } | |
687 | |
688 void V8RuntimeAgentImpl::reportExecutionContextCreated( | |
689 InspectedContext* context) { | |
690 if (!m_enabled) return; | |
691 context->setReported(true); | |
692 std::unique_ptr<protocol::Runtime::ExecutionContextDescription> description = | |
693 protocol::Runtime::ExecutionContextDescription::create() | |
694 .setId(context->contextId()) | |
695 .setName(context->humanReadableName()) | |
696 .setOrigin(context->origin()) | |
697 .build(); | |
698 if (!context->auxData().isEmpty()) | |
699 description->setAuxData(protocol::DictionaryValue::cast( | |
700 protocol::parseJSON(context->auxData()))); | |
701 m_frontend.executionContextCreated(std::move(description)); | |
702 } | |
703 | |
704 void V8RuntimeAgentImpl::reportExecutionContextDestroyed( | |
705 InspectedContext* context) { | |
706 if (m_enabled && context->isReported()) { | |
707 context->setReported(false); | |
708 m_frontend.executionContextDestroyed(context->contextId()); | |
709 } | |
710 } | |
711 | |
712 void V8RuntimeAgentImpl::inspect( | |
713 std::unique_ptr<protocol::Runtime::RemoteObject> objectToInspect, | |
714 std::unique_ptr<protocol::DictionaryValue> hints) { | |
715 if (m_enabled) | |
716 m_frontend.inspectRequested(std::move(objectToInspect), std::move(hints)); | |
717 } | |
718 | |
719 void V8RuntimeAgentImpl::messageAdded(V8ConsoleMessage* message) { | |
720 if (m_enabled) reportMessage(message, true); | |
721 } | |
722 | |
723 bool V8RuntimeAgentImpl::reportMessage(V8ConsoleMessage* message, | |
724 bool generatePreview) { | |
725 message->reportToFrontend(&m_frontend, m_session, generatePreview); | |
726 m_frontend.flush(); | |
727 return m_inspector->hasConsoleMessageStorage(m_session->contextGroupId()); | |
728 } | |
729 | |
730 } // namespace v8_inspector | |
OLD | NEW |