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