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/v8-runtime-agent-impl.h"
32
33 #include <inttypes.h>
34
35 #include "src/debug/debug-interface.h"
36 #include "src/inspector/injected-script.h"
37 #include "src/inspector/inspected-context.h"
38 #include "src/inspector/protocol/Protocol.h"
39 #include "src/inspector/remote-object-id.h"
40 #include "src/inspector/v8-console-message.h"
41 #include "src/inspector/v8-debugger-agent-impl.h"
42 #include "src/inspector/v8-debugger.h"
43 #include "src/inspector/v8-inspector-impl.h"
44 #include "src/inspector/v8-inspector-session-impl.h"
45 #include "src/inspector/v8-stack-trace-impl.h"
46 #include "src/inspector/v8-value-utils.h"
47 #include "src/tracing/trace-event.h"
48
49 #include "include/v8-inspector.h"
50
51 namespace v8_inspector {
52
53 namespace V8RuntimeAgentImplState {
54 static const char customObjectFormatterEnabled[] =
55 "customObjectFormatterEnabled";
56 static const char runtimeEnabled[] = "runtimeEnabled";
57 };
58
59 using protocol::Runtime::RemoteObject;
60
61 namespace {
62
63 template <typename ProtocolCallback>
64 class EvaluateCallbackWrapper : public EvaluateCallback {
65 public:
wrap(std::unique_ptr<ProtocolCallback> callback)66 static std::unique_ptr<EvaluateCallback> wrap(
67 std::unique_ptr<ProtocolCallback> callback) {
68 return std::unique_ptr<EvaluateCallback>(
69 new EvaluateCallbackWrapper(std::move(callback)));
70 }
sendSuccess(std::unique_ptr<protocol::Runtime::RemoteObject> result,protocol::Maybe<protocol::Runtime::ExceptionDetails> exceptionDetails)71 void sendSuccess(std::unique_ptr<protocol::Runtime::RemoteObject> result,
72 protocol::Maybe<protocol::Runtime::ExceptionDetails>
73 exceptionDetails) override {
74 return m_callback->sendSuccess(std::move(result),
75 std::move(exceptionDetails));
76 }
sendFailure(const protocol::DispatchResponse & response)77 void sendFailure(const protocol::DispatchResponse& response) override {
78 return m_callback->sendFailure(response);
79 }
80
81 private:
EvaluateCallbackWrapper(std::unique_ptr<ProtocolCallback> callback)82 explicit EvaluateCallbackWrapper(std::unique_ptr<ProtocolCallback> callback)
83 : m_callback(std::move(callback)) {}
84
85 std::unique_ptr<ProtocolCallback> m_callback;
86 };
87
88 template <typename ProtocolCallback>
wrapEvaluateResultAsync(InjectedScript * injectedScript,v8::MaybeLocal<v8::Value> maybeResultValue,const v8::TryCatch & tryCatch,const String16 & objectGroup,bool returnByValue,bool generatePreview,ProtocolCallback * callback)89 bool wrapEvaluateResultAsync(InjectedScript* injectedScript,
90 v8::MaybeLocal<v8::Value> maybeResultValue,
91 const v8::TryCatch& tryCatch,
92 const String16& objectGroup, bool returnByValue,
93 bool generatePreview, ProtocolCallback* callback) {
94 std::unique_ptr<RemoteObject> result;
95 Maybe<protocol::Runtime::ExceptionDetails> exceptionDetails;
96
97 Response response = injectedScript->wrapEvaluateResult(
98 maybeResultValue, tryCatch, objectGroup, returnByValue, generatePreview,
99 &result, &exceptionDetails);
100 if (response.isSuccess()) {
101 callback->sendSuccess(std::move(result), std::move(exceptionDetails));
102 return true;
103 }
104 callback->sendFailure(response);
105 return false;
106 }
107
innerCallFunctionOn(V8InspectorSessionImpl * session,InjectedScript::Scope & scope,v8::Local<v8::Value> recv,const String16 & expression,Maybe<protocol::Array<protocol::Runtime::CallArgument>> optionalArguments,bool silent,bool returnByValue,bool generatePreview,bool userGesture,bool awaitPromise,const String16 & objectGroup,std::unique_ptr<V8RuntimeAgentImpl::CallFunctionOnCallback> callback)108 void innerCallFunctionOn(
109 V8InspectorSessionImpl* session, InjectedScript::Scope& scope,
110 v8::Local<v8::Value> recv, const String16& expression,
111 Maybe<protocol::Array<protocol::Runtime::CallArgument>> optionalArguments,
112 bool silent, bool returnByValue, bool generatePreview, bool userGesture,
113 bool awaitPromise, const String16& objectGroup,
114 std::unique_ptr<V8RuntimeAgentImpl::CallFunctionOnCallback> callback) {
115 V8InspectorImpl* inspector = session->inspector();
116
117 std::unique_ptr<v8::Local<v8::Value>[]> argv = nullptr;
118 int argc = 0;
119 if (optionalArguments.isJust()) {
120 protocol::Array<protocol::Runtime::CallArgument>* arguments =
121 optionalArguments.fromJust();
122 argc = static_cast<int>(arguments->length());
123 argv.reset(new v8::Local<v8::Value>[argc]);
124 for (int i = 0; i < argc; ++i) {
125 v8::Local<v8::Value> argumentValue;
126 Response response = scope.injectedScript()->resolveCallArgument(
127 arguments->get(i), &argumentValue);
128 if (!response.isSuccess()) {
129 callback->sendFailure(response);
130 return;
131 }
132 argv[i] = argumentValue;
133 }
134 }
135
136 if (silent) scope.ignoreExceptionsAndMuteConsole();
137 if (userGesture) scope.pretendUserGesture();
138
139 // Temporarily enable allow evals for inspector.
140 scope.allowCodeGenerationFromStrings();
141
142 v8::MaybeLocal<v8::Value> maybeFunctionValue;
143 v8::Local<v8::Script> functionScript;
144 if (inspector
145 ->compileScript(scope.context(), "(" + expression + ")", String16())
146 .ToLocal(&functionScript)) {
147 v8::MicrotasksScope microtasksScope(inspector->isolate(),
148 v8::MicrotasksScope::kRunMicrotasks);
149 maybeFunctionValue = functionScript->Run(scope.context());
150 }
151 // Re-initialize after running client's code, as it could have destroyed
152 // context or session.
153 Response response = scope.initialize();
154 if (!response.isSuccess()) {
155 callback->sendFailure(response);
156 return;
157 }
158
159 if (scope.tryCatch().HasCaught()) {
160 wrapEvaluateResultAsync(scope.injectedScript(), maybeFunctionValue,
161 scope.tryCatch(), objectGroup, false, false,
162 callback.get());
163 return;
164 }
165
166 v8::Local<v8::Value> functionValue;
167 if (!maybeFunctionValue.ToLocal(&functionValue) ||
168 !functionValue->IsFunction()) {
169 callback->sendFailure(
170 Response::Error("Given expression does not evaluate to a function"));
171 return;
172 }
173
174 v8::MaybeLocal<v8::Value> maybeResultValue;
175 {
176 v8::MicrotasksScope microtasksScope(inspector->isolate(),
177 v8::MicrotasksScope::kRunMicrotasks);
178 maybeResultValue = functionValue.As<v8::Function>()->Call(
179 scope.context(), recv, argc, argv.get());
180 }
181 // Re-initialize after running client's code, as it could have destroyed
182 // context or session.
183 response = scope.initialize();
184 if (!response.isSuccess()) {
185 callback->sendFailure(response);
186 return;
187 }
188
189 if (!awaitPromise || scope.tryCatch().HasCaught()) {
190 wrapEvaluateResultAsync(scope.injectedScript(), maybeResultValue,
191 scope.tryCatch(), objectGroup, returnByValue,
192 generatePreview, callback.get());
193 return;
194 }
195
196 scope.injectedScript()->addPromiseCallback(
197 session, maybeResultValue, objectGroup, returnByValue, generatePreview,
198 EvaluateCallbackWrapper<V8RuntimeAgentImpl::CallFunctionOnCallback>::wrap(
199 std::move(callback)));
200 }
201
ensureContext(V8InspectorImpl * inspector,int contextGroupId,Maybe<int> executionContextId,int * contextId)202 Response ensureContext(V8InspectorImpl* inspector, int contextGroupId,
203 Maybe<int> executionContextId, int* contextId) {
204 if (executionContextId.isJust()) {
205 *contextId = executionContextId.fromJust();
206 } else {
207 v8::HandleScope handles(inspector->isolate());
208 v8::Local<v8::Context> defaultContext =
209 inspector->client()->ensureDefaultContextInGroup(contextGroupId);
210 if (defaultContext.IsEmpty())
211 return Response::Error("Cannot find default execution context");
212 *contextId = InspectedContext::contextId(defaultContext);
213 }
214 return Response::OK();
215 }
216
217 } // namespace
218
V8RuntimeAgentImpl(V8InspectorSessionImpl * session,protocol::FrontendChannel * FrontendChannel,protocol::DictionaryValue * state)219 V8RuntimeAgentImpl::V8RuntimeAgentImpl(
220 V8InspectorSessionImpl* session, protocol::FrontendChannel* FrontendChannel,
221 protocol::DictionaryValue* state)
222 : m_session(session),
223 m_state(state),
224 m_frontend(FrontendChannel),
225 m_inspector(session->inspector()),
226 m_enabled(false) {}
227
~V8RuntimeAgentImpl()228 V8RuntimeAgentImpl::~V8RuntimeAgentImpl() {}
229
evaluate(const String16 & expression,Maybe<String16> objectGroup,Maybe<bool> includeCommandLineAPI,Maybe<bool> silent,Maybe<int> executionContextId,Maybe<bool> returnByValue,Maybe<bool> generatePreview,Maybe<bool> userGesture,Maybe<bool> awaitPromise,Maybe<bool> throwOnSideEffect,Maybe<double> timeout,std::unique_ptr<EvaluateCallback> callback)230 void V8RuntimeAgentImpl::evaluate(
231 const String16& expression, Maybe<String16> objectGroup,
232 Maybe<bool> includeCommandLineAPI, Maybe<bool> silent,
233 Maybe<int> executionContextId, Maybe<bool> returnByValue,
234 Maybe<bool> generatePreview, Maybe<bool> userGesture,
235 Maybe<bool> awaitPromise, Maybe<bool> throwOnSideEffect,
236 Maybe<double> timeout, std::unique_ptr<EvaluateCallback> callback) {
237 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"),
238 "EvaluateScript");
239 int contextId = 0;
240 Response response = ensureContext(m_inspector, m_session->contextGroupId(),
241 std::move(executionContextId), &contextId);
242 if (!response.isSuccess()) {
243 callback->sendFailure(response);
244 return;
245 }
246
247 InjectedScript::ContextScope scope(m_session, contextId);
248 response = scope.initialize();
249 if (!response.isSuccess()) {
250 callback->sendFailure(response);
251 return;
252 }
253
254 if (silent.fromMaybe(false)) scope.ignoreExceptionsAndMuteConsole();
255 if (userGesture.fromMaybe(false)) scope.pretendUserGesture();
256
257 if (includeCommandLineAPI.fromMaybe(false)) scope.installCommandLineAPI();
258
259 // Temporarily enable allow evals for inspector.
260 scope.allowCodeGenerationFromStrings();
261 v8::MaybeLocal<v8::Value> maybeResultValue;
262 {
263 V8InspectorImpl::EvaluateScope evaluateScope(m_inspector->isolate());
264 if (timeout.isJust()) {
265 response = evaluateScope.setTimeout(timeout.fromJust() / 1000.0);
266 if (!response.isSuccess()) {
267 callback->sendFailure(response);
268 return;
269 }
270 }
271 v8::MicrotasksScope microtasksScope(m_inspector->isolate(),
272 v8::MicrotasksScope::kRunMicrotasks);
273 maybeResultValue = v8::debug::EvaluateGlobal(
274 m_inspector->isolate(), toV8String(m_inspector->isolate(), expression),
275 throwOnSideEffect.fromMaybe(false));
276 } // Run microtasks before returning result.
277
278 // Re-initialize after running client's code, as it could have destroyed
279 // context or session.
280 response = scope.initialize();
281 if (!response.isSuccess()) {
282 callback->sendFailure(response);
283 return;
284 }
285
286 if (!awaitPromise.fromMaybe(false) || scope.tryCatch().HasCaught()) {
287 wrapEvaluateResultAsync(scope.injectedScript(), maybeResultValue,
288 scope.tryCatch(), objectGroup.fromMaybe(""),
289 returnByValue.fromMaybe(false),
290 generatePreview.fromMaybe(false), callback.get());
291 return;
292 }
293 scope.injectedScript()->addPromiseCallback(
294 m_session, maybeResultValue, objectGroup.fromMaybe(""),
295 returnByValue.fromMaybe(false), generatePreview.fromMaybe(false),
296 EvaluateCallbackWrapper<EvaluateCallback>::wrap(std::move(callback)));
297 }
298
awaitPromise(const String16 & promiseObjectId,Maybe<bool> returnByValue,Maybe<bool> generatePreview,std::unique_ptr<AwaitPromiseCallback> callback)299 void V8RuntimeAgentImpl::awaitPromise(
300 const String16& promiseObjectId, Maybe<bool> returnByValue,
301 Maybe<bool> generatePreview,
302 std::unique_ptr<AwaitPromiseCallback> callback) {
303 InjectedScript::ObjectScope scope(m_session, promiseObjectId);
304 Response response = scope.initialize();
305 if (!response.isSuccess()) {
306 callback->sendFailure(response);
307 return;
308 }
309 if (!scope.object()->IsPromise()) {
310 callback->sendFailure(
311 Response::Error("Could not find promise with given id"));
312 return;
313 }
314 scope.injectedScript()->addPromiseCallback(
315 m_session, scope.object(), scope.objectGroupName(),
316 returnByValue.fromMaybe(false), generatePreview.fromMaybe(false),
317 EvaluateCallbackWrapper<AwaitPromiseCallback>::wrap(std::move(callback)));
318 }
319
callFunctionOn(const String16 & expression,Maybe<String16> objectId,Maybe<protocol::Array<protocol::Runtime::CallArgument>> optionalArguments,Maybe<bool> silent,Maybe<bool> returnByValue,Maybe<bool> generatePreview,Maybe<bool> userGesture,Maybe<bool> awaitPromise,Maybe<int> executionContextId,Maybe<String16> objectGroup,std::unique_ptr<CallFunctionOnCallback> callback)320 void V8RuntimeAgentImpl::callFunctionOn(
321 const String16& expression, Maybe<String16> objectId,
322 Maybe<protocol::Array<protocol::Runtime::CallArgument>> optionalArguments,
323 Maybe<bool> silent, Maybe<bool> returnByValue, Maybe<bool> generatePreview,
324 Maybe<bool> userGesture, Maybe<bool> awaitPromise,
325 Maybe<int> executionContextId, Maybe<String16> objectGroup,
326 std::unique_ptr<CallFunctionOnCallback> callback) {
327 if (objectId.isJust() && executionContextId.isJust()) {
328 callback->sendFailure(Response::Error(
329 "ObjectId must not be specified together with executionContextId"));
330 return;
331 }
332 if (!objectId.isJust() && !executionContextId.isJust()) {
333 callback->sendFailure(Response::Error(
334 "Either ObjectId or executionContextId must be specified"));
335 return;
336 }
337 if (objectId.isJust()) {
338 InjectedScript::ObjectScope scope(m_session, objectId.fromJust());
339 Response response = scope.initialize();
340 if (!response.isSuccess()) {
341 callback->sendFailure(response);
342 return;
343 }
344 innerCallFunctionOn(
345 m_session, scope, scope.object(), expression,
346 std::move(optionalArguments), silent.fromMaybe(false),
347 returnByValue.fromMaybe(false), generatePreview.fromMaybe(false),
348 userGesture.fromMaybe(false), awaitPromise.fromMaybe(false),
349 objectGroup.isJust() ? objectGroup.fromMaybe(String16())
350 : scope.objectGroupName(),
351 std::move(callback));
352 } else {
353 int contextId = 0;
354 Response response =
355 ensureContext(m_inspector, m_session->contextGroupId(),
356 std::move(executionContextId.fromJust()), &contextId);
357 if (!response.isSuccess()) {
358 callback->sendFailure(response);
359 return;
360 }
361 InjectedScript::ContextScope scope(m_session, contextId);
362 response = scope.initialize();
363 if (!response.isSuccess()) {
364 callback->sendFailure(response);
365 return;
366 }
367 innerCallFunctionOn(
368 m_session, scope, scope.context()->Global(), expression,
369 std::move(optionalArguments), silent.fromMaybe(false),
370 returnByValue.fromMaybe(false), generatePreview.fromMaybe(false),
371 userGesture.fromMaybe(false), awaitPromise.fromMaybe(false),
372 objectGroup.fromMaybe(""), std::move(callback));
373 }
374 }
375
getProperties(const String16 & objectId,Maybe<bool> ownProperties,Maybe<bool> accessorPropertiesOnly,Maybe<bool> generatePreview,std::unique_ptr<protocol::Array<protocol::Runtime::PropertyDescriptor>> * result,Maybe<protocol::Array<protocol::Runtime::InternalPropertyDescriptor>> * internalProperties,Maybe<protocol::Runtime::ExceptionDetails> * exceptionDetails)376 Response V8RuntimeAgentImpl::getProperties(
377 const String16& objectId, Maybe<bool> ownProperties,
378 Maybe<bool> accessorPropertiesOnly, Maybe<bool> generatePreview,
379 std::unique_ptr<protocol::Array<protocol::Runtime::PropertyDescriptor>>*
380 result,
381 Maybe<protocol::Array<protocol::Runtime::InternalPropertyDescriptor>>*
382 internalProperties,
383 Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
384 using protocol::Runtime::InternalPropertyDescriptor;
385
386 InjectedScript::ObjectScope scope(m_session, objectId);
387 Response response = scope.initialize();
388 if (!response.isSuccess()) return response;
389
390 scope.ignoreExceptionsAndMuteConsole();
391 v8::MicrotasksScope microtasks_scope(m_inspector->isolate(),
392 v8::MicrotasksScope::kRunMicrotasks);
393 if (!scope.object()->IsObject())
394 return Response::Error("Value with given id is not an object");
395
396 v8::Local<v8::Object> object = scope.object().As<v8::Object>();
397 response = scope.injectedScript()->getProperties(
398 object, scope.objectGroupName(), ownProperties.fromMaybe(false),
399 accessorPropertiesOnly.fromMaybe(false), generatePreview.fromMaybe(false),
400 result, exceptionDetails);
401 if (!response.isSuccess()) return response;
402 if (exceptionDetails->isJust() || accessorPropertiesOnly.fromMaybe(false))
403 return Response::OK();
404 v8::Local<v8::Array> propertiesArray;
405 if (!m_inspector->debugger()
406 ->internalProperties(scope.context(), scope.object())
407 .ToLocal(&propertiesArray)) {
408 return Response::InternalError();
409 }
410 std::unique_ptr<protocol::Array<InternalPropertyDescriptor>>
411 propertiesProtocolArray =
412 protocol::Array<InternalPropertyDescriptor>::create();
413 for (uint32_t i = 0; i < propertiesArray->Length(); i += 2) {
414 v8::Local<v8::Value> name;
415 if (!propertiesArray->Get(scope.context(), i).ToLocal(&name) ||
416 !name->IsString()) {
417 return Response::InternalError();
418 }
419 v8::Local<v8::Value> value;
420 if (!propertiesArray->Get(scope.context(), i + 1).ToLocal(&value))
421 return Response::InternalError();
422 std::unique_ptr<RemoteObject> wrappedValue;
423 protocol::Response response = scope.injectedScript()->wrapObject(
424 value, scope.objectGroupName(), false, false, &wrappedValue);
425 if (!response.isSuccess()) return response;
426 propertiesProtocolArray->addItem(
427 InternalPropertyDescriptor::create()
428 .setName(toProtocolString(name.As<v8::String>()))
429 .setValue(std::move(wrappedValue))
430 .build());
431 }
432 if (propertiesProtocolArray->length())
433 *internalProperties = std::move(propertiesProtocolArray);
434 return Response::OK();
435 }
436
releaseObject(const String16 & objectId)437 Response V8RuntimeAgentImpl::releaseObject(const String16& objectId) {
438 InjectedScript::ObjectScope scope(m_session, objectId);
439 Response response = scope.initialize();
440 if (!response.isSuccess()) return response;
441 scope.injectedScript()->releaseObject(objectId);
442 return Response::OK();
443 }
444
releaseObjectGroup(const String16 & objectGroup)445 Response V8RuntimeAgentImpl::releaseObjectGroup(const String16& objectGroup) {
446 m_session->releaseObjectGroup(objectGroup);
447 return Response::OK();
448 }
449
runIfWaitingForDebugger()450 Response V8RuntimeAgentImpl::runIfWaitingForDebugger() {
451 m_inspector->client()->runIfWaitingForDebugger(m_session->contextGroupId());
452 return Response::OK();
453 }
454
setCustomObjectFormatterEnabled(bool enabled)455 Response V8RuntimeAgentImpl::setCustomObjectFormatterEnabled(bool enabled) {
456 m_state->setBoolean(V8RuntimeAgentImplState::customObjectFormatterEnabled,
457 enabled);
458 if (!m_enabled) return Response::Error("Runtime agent is not enabled");
459 m_session->setCustomObjectFormatterEnabled(enabled);
460 return Response::OK();
461 }
462
discardConsoleEntries()463 Response V8RuntimeAgentImpl::discardConsoleEntries() {
464 V8ConsoleMessageStorage* storage =
465 m_inspector->ensureConsoleMessageStorage(m_session->contextGroupId());
466 storage->clear();
467 return Response::OK();
468 }
469
compileScript(const String16 & expression,const String16 & sourceURL,bool persistScript,Maybe<int> executionContextId,Maybe<String16> * scriptId,Maybe<protocol::Runtime::ExceptionDetails> * exceptionDetails)470 Response V8RuntimeAgentImpl::compileScript(
471 const String16& expression, const String16& sourceURL, bool persistScript,
472 Maybe<int> executionContextId, Maybe<String16>* scriptId,
473 Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
474 if (!m_enabled) return Response::Error("Runtime agent is not enabled");
475
476 int contextId = 0;
477 Response response = ensureContext(m_inspector, m_session->contextGroupId(),
478 std::move(executionContextId), &contextId);
479 if (!response.isSuccess()) return response;
480 InjectedScript::ContextScope scope(m_session, contextId);
481 response = scope.initialize();
482 if (!response.isSuccess()) return response;
483
484 if (!persistScript) m_inspector->debugger()->muteScriptParsedEvents();
485 v8::Local<v8::Script> script;
486 bool isOk = m_inspector->compileScript(scope.context(), expression, sourceURL)
487 .ToLocal(&script);
488 if (!persistScript) m_inspector->debugger()->unmuteScriptParsedEvents();
489 if (!isOk) {
490 if (scope.tryCatch().HasCaught()) {
491 response = scope.injectedScript()->createExceptionDetails(
492 scope.tryCatch(), String16(), false, exceptionDetails);
493 if (!response.isSuccess()) return response;
494 return Response::OK();
495 } else {
496 return Response::Error("Script compilation failed");
497 }
498 }
499
500 if (!persistScript) return Response::OK();
501
502 String16 scriptValueId =
503 String16::fromInteger(script->GetUnboundScript()->GetId());
504 std::unique_ptr<v8::Global<v8::Script>> global(
505 new v8::Global<v8::Script>(m_inspector->isolate(), script));
506 m_compiledScripts[scriptValueId] = std::move(global);
507 *scriptId = scriptValueId;
508 return Response::OK();
509 }
510
runScript(const String16 & scriptId,Maybe<int> executionContextId,Maybe<String16> objectGroup,Maybe<bool> silent,Maybe<bool> includeCommandLineAPI,Maybe<bool> returnByValue,Maybe<bool> generatePreview,Maybe<bool> awaitPromise,std::unique_ptr<RunScriptCallback> callback)511 void V8RuntimeAgentImpl::runScript(
512 const String16& scriptId, Maybe<int> executionContextId,
513 Maybe<String16> objectGroup, Maybe<bool> silent,
514 Maybe<bool> includeCommandLineAPI, Maybe<bool> returnByValue,
515 Maybe<bool> generatePreview, Maybe<bool> awaitPromise,
516 std::unique_ptr<RunScriptCallback> callback) {
517 if (!m_enabled) {
518 callback->sendFailure(Response::Error("Runtime agent is not enabled"));
519 return;
520 }
521
522 auto it = m_compiledScripts.find(scriptId);
523 if (it == m_compiledScripts.end()) {
524 callback->sendFailure(Response::Error("No script with given id"));
525 return;
526 }
527
528 int contextId = 0;
529 Response response = ensureContext(m_inspector, m_session->contextGroupId(),
530 std::move(executionContextId), &contextId);
531 if (!response.isSuccess()) {
532 callback->sendFailure(response);
533 return;
534 }
535
536 InjectedScript::ContextScope scope(m_session, contextId);
537 response = scope.initialize();
538 if (!response.isSuccess()) {
539 callback->sendFailure(response);
540 return;
541 }
542
543 if (silent.fromMaybe(false)) scope.ignoreExceptionsAndMuteConsole();
544
545 std::unique_ptr<v8::Global<v8::Script>> scriptWrapper = std::move(it->second);
546 m_compiledScripts.erase(it);
547 v8::Local<v8::Script> script = scriptWrapper->Get(m_inspector->isolate());
548 if (script.IsEmpty()) {
549 callback->sendFailure(Response::Error("Script execution failed"));
550 return;
551 }
552
553 if (includeCommandLineAPI.fromMaybe(false)) scope.installCommandLineAPI();
554
555 v8::MaybeLocal<v8::Value> maybeResultValue;
556 {
557 v8::MicrotasksScope microtasksScope(m_inspector->isolate(),
558 v8::MicrotasksScope::kRunMicrotasks);
559 maybeResultValue = script->Run(scope.context());
560 }
561
562 // Re-initialize after running client's code, as it could have destroyed
563 // context or session.
564 response = scope.initialize();
565 if (!response.isSuccess()) {
566 callback->sendFailure(response);
567 return;
568 }
569
570 if (!awaitPromise.fromMaybe(false) || scope.tryCatch().HasCaught()) {
571 wrapEvaluateResultAsync(scope.injectedScript(), maybeResultValue,
572 scope.tryCatch(), objectGroup.fromMaybe(""),
573 returnByValue.fromMaybe(false),
574 generatePreview.fromMaybe(false), callback.get());
575 return;
576 }
577 scope.injectedScript()->addPromiseCallback(
578 m_session, maybeResultValue.ToLocalChecked(),
579 objectGroup.fromMaybe(""), returnByValue.fromMaybe(false),
580 generatePreview.fromMaybe(false),
581 EvaluateCallbackWrapper<RunScriptCallback>::wrap(std::move(callback)));
582 }
583
queryObjects(const String16 & prototypeObjectId,Maybe<String16> objectGroup,std::unique_ptr<protocol::Runtime::RemoteObject> * objects)584 Response V8RuntimeAgentImpl::queryObjects(
585 const String16& prototypeObjectId, Maybe<String16> objectGroup,
586 std::unique_ptr<protocol::Runtime::RemoteObject>* objects) {
587 InjectedScript::ObjectScope scope(m_session, prototypeObjectId);
588 Response response = scope.initialize();
589 if (!response.isSuccess()) return response;
590 if (!scope.object()->IsObject()) {
591 return Response::Error("Prototype should be instance of Object");
592 }
593 v8::Local<v8::Array> resultArray = m_inspector->debugger()->queryObjects(
594 scope.context(), v8::Local<v8::Object>::Cast(scope.object()));
595 return scope.injectedScript()->wrapObject(
596 resultArray, objectGroup.fromMaybe(scope.objectGroupName()), false, false,
597 objects);
598 }
599
globalLexicalScopeNames(Maybe<int> executionContextId,std::unique_ptr<protocol::Array<String16>> * outNames)600 Response V8RuntimeAgentImpl::globalLexicalScopeNames(
601 Maybe<int> executionContextId,
602 std::unique_ptr<protocol::Array<String16>>* outNames) {
603 int contextId = 0;
604 Response response = ensureContext(m_inspector, m_session->contextGroupId(),
605 std::move(executionContextId), &contextId);
606 if (!response.isSuccess()) return response;
607
608 InjectedScript::ContextScope scope(m_session, contextId);
609 response = scope.initialize();
610 if (!response.isSuccess()) return response;
611
612 v8::PersistentValueVector<v8::String> names(m_inspector->isolate());
613 v8::debug::GlobalLexicalScopeNames(scope.context(), &names);
614 *outNames = protocol::Array<String16>::create();
615 for (size_t i = 0; i < names.Size(); ++i) {
616 (*outNames)->addItem(toProtocolString(names.Get(i)));
617 }
618 return Response::OK();
619 }
620
getIsolateId(String16 * outIsolateId)621 Response V8RuntimeAgentImpl::getIsolateId(String16* outIsolateId) {
622 char buf[40];
623 std::snprintf(buf, sizeof(buf), "%" PRIx64, m_inspector->isolateId());
624 *outIsolateId = buf;
625 return Response::OK();
626 }
627
getHeapUsage(double * out_usedSize,double * out_totalSize)628 Response V8RuntimeAgentImpl::getHeapUsage(double* out_usedSize,
629 double* out_totalSize) {
630 v8::HeapStatistics stats;
631 m_inspector->isolate()->GetHeapStatistics(&stats);
632 *out_usedSize = stats.used_heap_size();
633 *out_totalSize = stats.total_heap_size();
634 return Response::OK();
635 }
636
terminateExecution(std::unique_ptr<TerminateExecutionCallback> callback)637 void V8RuntimeAgentImpl::terminateExecution(
638 std::unique_ptr<TerminateExecutionCallback> callback) {
639 m_inspector->debugger()->terminateExecution(std::move(callback));
640 }
641
restore()642 void V8RuntimeAgentImpl::restore() {
643 if (!m_state->booleanProperty(V8RuntimeAgentImplState::runtimeEnabled, false))
644 return;
645 m_frontend.executionContextsCleared();
646 enable();
647 if (m_state->booleanProperty(
648 V8RuntimeAgentImplState::customObjectFormatterEnabled, false))
649 m_session->setCustomObjectFormatterEnabled(true);
650 }
651
enable()652 Response V8RuntimeAgentImpl::enable() {
653 if (m_enabled) return Response::OK();
654 m_inspector->client()->beginEnsureAllContextsInGroup(
655 m_session->contextGroupId());
656 m_enabled = true;
657 m_state->setBoolean(V8RuntimeAgentImplState::runtimeEnabled, true);
658 m_inspector->enableStackCapturingIfNeeded();
659 m_session->reportAllContexts(this);
660 V8ConsoleMessageStorage* storage =
661 m_inspector->ensureConsoleMessageStorage(m_session->contextGroupId());
662 for (const auto& message : storage->messages()) {
663 if (!reportMessage(message.get(), false)) break;
664 }
665 return Response::OK();
666 }
667
disable()668 Response V8RuntimeAgentImpl::disable() {
669 if (!m_enabled) return Response::OK();
670 m_enabled = false;
671 m_state->setBoolean(V8RuntimeAgentImplState::runtimeEnabled, false);
672 m_inspector->disableStackCapturingIfNeeded();
673 m_session->setCustomObjectFormatterEnabled(false);
674 reset();
675 m_inspector->client()->endEnsureAllContextsInGroup(
676 m_session->contextGroupId());
677 return Response::OK();
678 }
679
reset()680 void V8RuntimeAgentImpl::reset() {
681 m_compiledScripts.clear();
682 if (m_enabled) {
683 int sessionId = m_session->sessionId();
684 m_inspector->forEachContext(m_session->contextGroupId(),
685 [&sessionId](InspectedContext* context) {
686 context->setReported(sessionId, false);
687 });
688 m_frontend.executionContextsCleared();
689 }
690 }
691
reportExecutionContextCreated(InspectedContext * context)692 void V8RuntimeAgentImpl::reportExecutionContextCreated(
693 InspectedContext* context) {
694 if (!m_enabled) return;
695 context->setReported(m_session->sessionId(), true);
696 std::unique_ptr<protocol::Runtime::ExecutionContextDescription> description =
697 protocol::Runtime::ExecutionContextDescription::create()
698 .setId(context->contextId())
699 .setName(context->humanReadableName())
700 .setOrigin(context->origin())
701 .build();
702 if (!context->auxData().isEmpty())
703 description->setAuxData(protocol::DictionaryValue::cast(
704 protocol::StringUtil::parseJSON(context->auxData())));
705 m_frontend.executionContextCreated(std::move(description));
706 }
707
reportExecutionContextDestroyed(InspectedContext * context)708 void V8RuntimeAgentImpl::reportExecutionContextDestroyed(
709 InspectedContext* context) {
710 if (m_enabled && context->isReported(m_session->sessionId())) {
711 context->setReported(m_session->sessionId(), false);
712 m_frontend.executionContextDestroyed(context->contextId());
713 }
714 }
715
inspect(std::unique_ptr<protocol::Runtime::RemoteObject> objectToInspect,std::unique_ptr<protocol::DictionaryValue> hints)716 void V8RuntimeAgentImpl::inspect(
717 std::unique_ptr<protocol::Runtime::RemoteObject> objectToInspect,
718 std::unique_ptr<protocol::DictionaryValue> hints) {
719 if (m_enabled)
720 m_frontend.inspectRequested(std::move(objectToInspect), std::move(hints));
721 }
722
messageAdded(V8ConsoleMessage * message)723 void V8RuntimeAgentImpl::messageAdded(V8ConsoleMessage* message) {
724 if (m_enabled) reportMessage(message, true);
725 }
726
reportMessage(V8ConsoleMessage * message,bool generatePreview)727 bool V8RuntimeAgentImpl::reportMessage(V8ConsoleMessage* message,
728 bool generatePreview) {
729 message->reportToFrontend(&m_frontend, m_session, generatePreview);
730 m_frontend.flush();
731 return m_inspector->hasConsoleMessageStorage(m_session->contextGroupId());
732 }
733
734 } // namespace v8_inspector
735