• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "../../third_party/inspector_protocol/crdtp/json.h"
36 #include "include/v8-container.h"
37 #include "include/v8-context.h"
38 #include "include/v8-function.h"
39 #include "include/v8-inspector.h"
40 #include "include/v8-microtask-queue.h"
41 #include "src/debug/debug-interface.h"
42 #include "src/inspector/injected-script.h"
43 #include "src/inspector/inspected-context.h"
44 #include "src/inspector/protocol/Protocol.h"
45 #include "src/inspector/remote-object-id.h"
46 #include "src/inspector/v8-console-message.h"
47 #include "src/inspector/v8-debugger-agent-impl.h"
48 #include "src/inspector/v8-debugger.h"
49 #include "src/inspector/v8-inspector-impl.h"
50 #include "src/inspector/v8-inspector-session-impl.h"
51 #include "src/inspector/v8-stack-trace-impl.h"
52 #include "src/inspector/v8-value-utils.h"
53 #include "src/tracing/trace-event.h"
54 
55 namespace v8_inspector {
56 
57 namespace V8RuntimeAgentImplState {
58 static const char customObjectFormatterEnabled[] =
59     "customObjectFormatterEnabled";
60 static const char maxCallStackSizeToCapture[] = "maxCallStackSizeToCapture";
61 static const char runtimeEnabled[] = "runtimeEnabled";
62 static const char bindings[] = "bindings";
63 static const char globalBindingsKey[] = "";
64 }  // namespace V8RuntimeAgentImplState
65 
66 using protocol::Runtime::RemoteObject;
67 
68 namespace {
69 
70 template <typename ProtocolCallback>
71 class EvaluateCallbackWrapper : public EvaluateCallback {
72  public:
wrap(std::unique_ptr<ProtocolCallback> callback)73   static std::unique_ptr<EvaluateCallback> wrap(
74       std::unique_ptr<ProtocolCallback> callback) {
75     return std::unique_ptr<EvaluateCallback>(
76         new EvaluateCallbackWrapper(std::move(callback)));
77   }
sendSuccess(std::unique_ptr<protocol::Runtime::RemoteObject> result,protocol::Maybe<protocol::Runtime::ExceptionDetails> exceptionDetails)78   void sendSuccess(std::unique_ptr<protocol::Runtime::RemoteObject> result,
79                    protocol::Maybe<protocol::Runtime::ExceptionDetails>
80                        exceptionDetails) override {
81     return m_callback->sendSuccess(std::move(result),
82                                    std::move(exceptionDetails));
83   }
sendFailure(const protocol::DispatchResponse & response)84   void sendFailure(const protocol::DispatchResponse& response) override {
85     return m_callback->sendFailure(response);
86   }
87 
88  private:
EvaluateCallbackWrapper(std::unique_ptr<ProtocolCallback> callback)89   explicit EvaluateCallbackWrapper(std::unique_ptr<ProtocolCallback> callback)
90       : m_callback(std::move(callback)) {}
91 
92   std::unique_ptr<ProtocolCallback> m_callback;
93 };
94 
95 template <typename ProtocolCallback>
wrapEvaluateResultAsync(InjectedScript * injectedScript,v8::MaybeLocal<v8::Value> maybeResultValue,const v8::TryCatch & tryCatch,const String16 & objectGroup,WrapMode wrapMode,ProtocolCallback * callback)96 bool wrapEvaluateResultAsync(InjectedScript* injectedScript,
97                              v8::MaybeLocal<v8::Value> maybeResultValue,
98                              const v8::TryCatch& tryCatch,
99                              const String16& objectGroup, WrapMode wrapMode,
100                              ProtocolCallback* callback) {
101   std::unique_ptr<RemoteObject> result;
102   Maybe<protocol::Runtime::ExceptionDetails> exceptionDetails;
103 
104   Response response = injectedScript->wrapEvaluateResult(
105       maybeResultValue, tryCatch, objectGroup, wrapMode, &result,
106       &exceptionDetails);
107   if (response.IsSuccess()) {
108     callback->sendSuccess(std::move(result), std::move(exceptionDetails));
109     return true;
110   }
111   callback->sendFailure(response);
112   return false;
113 }
114 
innerCallFunctionOn(V8InspectorSessionImpl * session,InjectedScript::Scope & scope,v8::Local<v8::Value> recv,const String16 & expression,Maybe<protocol::Array<protocol::Runtime::CallArgument>> optionalArguments,bool silent,WrapMode wrapMode,bool userGesture,bool awaitPromise,const String16 & objectGroup,bool throw_on_side_effect,std::unique_ptr<V8RuntimeAgentImpl::CallFunctionOnCallback> callback)115 void innerCallFunctionOn(
116     V8InspectorSessionImpl* session, InjectedScript::Scope& scope,
117     v8::Local<v8::Value> recv, const String16& expression,
118     Maybe<protocol::Array<protocol::Runtime::CallArgument>> optionalArguments,
119     bool silent, WrapMode wrapMode, bool userGesture, bool awaitPromise,
120     const String16& objectGroup, bool throw_on_side_effect,
121     std::unique_ptr<V8RuntimeAgentImpl::CallFunctionOnCallback> callback) {
122   V8InspectorImpl* inspector = session->inspector();
123 
124   std::unique_ptr<v8::Local<v8::Value>[]> argv = nullptr;
125   int argc = 0;
126   if (optionalArguments.isJust()) {
127     protocol::Array<protocol::Runtime::CallArgument>* arguments =
128         optionalArguments.fromJust();
129     argc = static_cast<int>(arguments->size());
130     argv.reset(new v8::Local<v8::Value>[argc]);
131     for (int i = 0; i < argc; ++i) {
132       v8::Local<v8::Value> argumentValue;
133       Response response = scope.injectedScript()->resolveCallArgument(
134           (*arguments)[i].get(), &argumentValue);
135       if (!response.IsSuccess()) {
136         callback->sendFailure(response);
137         return;
138       }
139       argv[i] = argumentValue;
140     }
141   }
142 
143   if (silent) scope.ignoreExceptionsAndMuteConsole();
144   if (userGesture) scope.pretendUserGesture();
145 
146   // Temporarily enable allow evals for inspector.
147   scope.allowCodeGenerationFromStrings();
148 
149   v8::MaybeLocal<v8::Value> maybeFunctionValue;
150   v8::Local<v8::Script> functionScript;
151   if (inspector
152           ->compileScript(scope.context(), "(" + expression + ")", String16())
153           .ToLocal(&functionScript)) {
154     v8::MicrotasksScope microtasksScope(inspector->isolate(),
155                                         v8::MicrotasksScope::kRunMicrotasks);
156     maybeFunctionValue = functionScript->Run(scope.context());
157   }
158   // Re-initialize after running client's code, as it could have destroyed
159   // context or session.
160   Response response = scope.initialize();
161   if (!response.IsSuccess()) {
162     callback->sendFailure(response);
163     return;
164   }
165 
166   if (scope.tryCatch().HasCaught()) {
167     wrapEvaluateResultAsync(scope.injectedScript(), maybeFunctionValue,
168                             scope.tryCatch(), objectGroup, WrapMode::kNoPreview,
169                             callback.get());
170     return;
171   }
172 
173   v8::Local<v8::Value> functionValue;
174   if (!maybeFunctionValue.ToLocal(&functionValue) ||
175       !functionValue->IsFunction()) {
176     callback->sendFailure(Response::ServerError(
177         "Given expression does not evaluate to a function"));
178     return;
179   }
180 
181   v8::MaybeLocal<v8::Value> maybeResultValue;
182   {
183     v8::MicrotasksScope microtasksScope(inspector->isolate(),
184                                         v8::MicrotasksScope::kRunMicrotasks);
185     maybeResultValue = v8::debug::CallFunctionOn(
186         scope.context(), functionValue.As<v8::Function>(), recv, argc,
187         argv.get(), throw_on_side_effect);
188   }
189   // Re-initialize after running client's code, as it could have destroyed
190   // context or session.
191   response = scope.initialize();
192   if (!response.IsSuccess()) {
193     callback->sendFailure(response);
194     return;
195   }
196 
197   if (!awaitPromise || scope.tryCatch().HasCaught()) {
198     wrapEvaluateResultAsync(scope.injectedScript(), maybeResultValue,
199                             scope.tryCatch(), objectGroup, wrapMode,
200                             callback.get());
201     return;
202   }
203 
204   scope.injectedScript()->addPromiseCallback(
205       session, maybeResultValue, objectGroup, wrapMode, false /* replMode */,
206       EvaluateCallbackWrapper<V8RuntimeAgentImpl::CallFunctionOnCallback>::wrap(
207           std::move(callback)));
208 }
209 
ensureContext(V8InspectorImpl * inspector,int contextGroupId,Maybe<int> executionContextId,Maybe<String16> uniqueContextId,int * contextId)210 Response ensureContext(V8InspectorImpl* inspector, int contextGroupId,
211                        Maybe<int> executionContextId,
212                        Maybe<String16> uniqueContextId, int* contextId) {
213   if (executionContextId.isJust()) {
214     if (uniqueContextId.isJust()) {
215       return Response::InvalidParams(
216           "contextId and uniqueContextId are mutually exclusive");
217     }
218     *contextId = executionContextId.fromJust();
219   } else if (uniqueContextId.isJust()) {
220     internal::V8DebuggerId uniqueId(uniqueContextId.fromJust());
221     if (!uniqueId.isValid())
222       return Response::InvalidParams("invalid uniqueContextId");
223     int id = inspector->resolveUniqueContextId(uniqueId);
224     if (!id) return Response::InvalidParams("uniqueContextId not found");
225     *contextId = id;
226   } else {
227     v8::HandleScope handles(inspector->isolate());
228     v8::Local<v8::Context> defaultContext =
229         inspector->client()->ensureDefaultContextInGroup(contextGroupId);
230     if (defaultContext.IsEmpty())
231       return Response::ServerError("Cannot find default execution context");
232     *contextId = InspectedContext::contextId(defaultContext);
233   }
234 
235   return Response::Success();
236 }
237 
238 }  // namespace
239 
V8RuntimeAgentImpl(V8InspectorSessionImpl * session,protocol::FrontendChannel * FrontendChannel,protocol::DictionaryValue * state)240 V8RuntimeAgentImpl::V8RuntimeAgentImpl(
241     V8InspectorSessionImpl* session, protocol::FrontendChannel* FrontendChannel,
242     protocol::DictionaryValue* state)
243     : m_session(session),
244       m_state(state),
245       m_frontend(FrontendChannel),
246       m_inspector(session->inspector()),
247       m_enabled(false) {}
248 
249 V8RuntimeAgentImpl::~V8RuntimeAgentImpl() = default;
250 
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> maybeAwaitPromise,Maybe<bool> throwOnSideEffect,Maybe<double> timeout,Maybe<bool> disableBreaks,Maybe<bool> maybeReplMode,Maybe<bool> allowUnsafeEvalBlockedByCSP,Maybe<String16> uniqueContextId,Maybe<bool> generateWebDriverValue,std::unique_ptr<EvaluateCallback> callback)251 void V8RuntimeAgentImpl::evaluate(
252     const String16& expression, Maybe<String16> objectGroup,
253     Maybe<bool> includeCommandLineAPI, Maybe<bool> silent,
254     Maybe<int> executionContextId, Maybe<bool> returnByValue,
255     Maybe<bool> generatePreview, Maybe<bool> userGesture,
256     Maybe<bool> maybeAwaitPromise, Maybe<bool> throwOnSideEffect,
257     Maybe<double> timeout, Maybe<bool> disableBreaks, Maybe<bool> maybeReplMode,
258     Maybe<bool> allowUnsafeEvalBlockedByCSP, Maybe<String16> uniqueContextId,
259     Maybe<bool> generateWebDriverValue,
260     std::unique_ptr<EvaluateCallback> callback) {
261   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"),
262                "EvaluateScript");
263   int contextId = 0;
264   Response response = ensureContext(m_inspector, m_session->contextGroupId(),
265                                     std::move(executionContextId),
266                                     std::move(uniqueContextId), &contextId);
267   if (!response.IsSuccess()) {
268     callback->sendFailure(response);
269     return;
270   }
271 
272   InjectedScript::ContextScope scope(m_session, contextId);
273   response = scope.initialize();
274   if (!response.IsSuccess()) {
275     callback->sendFailure(response);
276     return;
277   }
278 
279   if (silent.fromMaybe(false)) scope.ignoreExceptionsAndMuteConsole();
280   if (userGesture.fromMaybe(false)) scope.pretendUserGesture();
281 
282   if (includeCommandLineAPI.fromMaybe(false)) scope.installCommandLineAPI();
283 
284   const bool replMode = maybeReplMode.fromMaybe(false);
285 
286   if (allowUnsafeEvalBlockedByCSP.fromMaybe(true)) {
287     // Temporarily enable allow evals for inspector.
288     scope.allowCodeGenerationFromStrings();
289   }
290   v8::MaybeLocal<v8::Value> maybeResultValue;
291   {
292     V8InspectorImpl::EvaluateScope evaluateScope(scope);
293     if (timeout.isJust()) {
294       response = evaluateScope.setTimeout(timeout.fromJust() / 1000.0);
295       if (!response.IsSuccess()) {
296         callback->sendFailure(response);
297         return;
298       }
299     }
300     v8::MicrotasksScope microtasksScope(m_inspector->isolate(),
301                                         v8::MicrotasksScope::kRunMicrotasks);
302     v8::debug::EvaluateGlobalMode mode =
303         v8::debug::EvaluateGlobalMode::kDefault;
304     if (throwOnSideEffect.fromMaybe(false)) {
305       mode = v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect;
306     } else if (disableBreaks.fromMaybe(false)) {
307       mode = v8::debug::EvaluateGlobalMode::kDisableBreaks;
308     }
309     const v8::Local<v8::String> source =
310         toV8String(m_inspector->isolate(), expression);
311     maybeResultValue = v8::debug::EvaluateGlobal(m_inspector->isolate(), source,
312                                                  mode, replMode);
313   }  // Run microtasks before returning result.
314 
315   // Re-initialize after running client's code, as it could have destroyed
316   // context or session.
317   response = scope.initialize();
318   if (!response.IsSuccess()) {
319     callback->sendFailure(response);
320     return;
321   }
322 
323   WrapMode wrap_mode = generatePreview.fromMaybe(false) ? WrapMode::kWithPreview
324                                                         : WrapMode::kNoPreview;
325   if (returnByValue.fromMaybe(false)) wrap_mode = WrapMode::kForceValue;
326   if (generateWebDriverValue.fromMaybe(false))
327     wrap_mode = WrapMode::kGenerateWebDriverValue;
328 
329   // REPL mode always returns a promise that must be awaited.
330   const bool await = replMode || maybeAwaitPromise.fromMaybe(false);
331   if (!await || scope.tryCatch().HasCaught()) {
332     wrapEvaluateResultAsync(scope.injectedScript(), maybeResultValue,
333                             scope.tryCatch(), objectGroup.fromMaybe(""),
334                             wrap_mode, callback.get());
335     return;
336   }
337   scope.injectedScript()->addPromiseCallback(
338       m_session, maybeResultValue, objectGroup.fromMaybe(""), wrap_mode,
339       replMode,
340       EvaluateCallbackWrapper<EvaluateCallback>::wrap(std::move(callback)));
341 }
342 
awaitPromise(const String16 & promiseObjectId,Maybe<bool> returnByValue,Maybe<bool> generatePreview,std::unique_ptr<AwaitPromiseCallback> callback)343 void V8RuntimeAgentImpl::awaitPromise(
344     const String16& promiseObjectId, Maybe<bool> returnByValue,
345     Maybe<bool> generatePreview,
346     std::unique_ptr<AwaitPromiseCallback> callback) {
347   InjectedScript::ObjectScope scope(m_session, promiseObjectId);
348   Response response = scope.initialize();
349   if (!response.IsSuccess()) {
350     callback->sendFailure(response);
351     return;
352   }
353   if (!scope.object()->IsPromise()) {
354     callback->sendFailure(
355         Response::ServerError("Could not find promise with given id"));
356     return;
357   }
358   WrapMode mode = generatePreview.fromMaybe(false) ? WrapMode::kWithPreview
359                                                    : WrapMode::kNoPreview;
360   if (returnByValue.fromMaybe(false)) mode = WrapMode::kForceValue;
361   scope.injectedScript()->addPromiseCallback(
362       m_session, scope.object(), scope.objectGroupName(), mode,
363       false /* replMode */,
364       EvaluateCallbackWrapper<AwaitPromiseCallback>::wrap(std::move(callback)));
365 }
366 
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,Maybe<bool> throwOnSideEffect,Maybe<bool> generateWebDriverValue,std::unique_ptr<CallFunctionOnCallback> callback)367 void V8RuntimeAgentImpl::callFunctionOn(
368     const String16& expression, Maybe<String16> objectId,
369     Maybe<protocol::Array<protocol::Runtime::CallArgument>> optionalArguments,
370     Maybe<bool> silent, Maybe<bool> returnByValue, Maybe<bool> generatePreview,
371     Maybe<bool> userGesture, Maybe<bool> awaitPromise,
372     Maybe<int> executionContextId, Maybe<String16> objectGroup,
373     Maybe<bool> throwOnSideEffect, Maybe<bool> generateWebDriverValue,
374     std::unique_ptr<CallFunctionOnCallback> callback) {
375   if (objectId.isJust() && executionContextId.isJust()) {
376     callback->sendFailure(Response::ServerError(
377         "ObjectId must not be specified together with executionContextId"));
378     return;
379   }
380   if (!objectId.isJust() && !executionContextId.isJust()) {
381     callback->sendFailure(Response::ServerError(
382         "Either ObjectId or executionContextId must be specified"));
383     return;
384   }
385   WrapMode wrap_mode = generatePreview.fromMaybe(false) ? WrapMode::kWithPreview
386                                                         : WrapMode::kNoPreview;
387   if (returnByValue.fromMaybe(false)) wrap_mode = WrapMode::kForceValue;
388   if (generateWebDriverValue.fromMaybe(false))
389     wrap_mode = WrapMode::kGenerateWebDriverValue;
390   if (objectId.isJust()) {
391     InjectedScript::ObjectScope scope(m_session, objectId.fromJust());
392     Response response = scope.initialize();
393     if (!response.IsSuccess()) {
394       callback->sendFailure(response);
395       return;
396     }
397     innerCallFunctionOn(
398         m_session, scope, scope.object(), expression,
399         std::move(optionalArguments), silent.fromMaybe(false), wrap_mode,
400         userGesture.fromMaybe(false), awaitPromise.fromMaybe(false),
401         objectGroup.isJust() ? objectGroup.fromMaybe(String16())
402                              : scope.objectGroupName(),
403         throwOnSideEffect.fromMaybe(false), std::move(callback));
404   } else {
405     int contextId = 0;
406     Response response = ensureContext(m_inspector, m_session->contextGroupId(),
407                                       std::move(executionContextId.fromJust()),
408                                       /* uniqueContextId */ {}, &contextId);
409     if (!response.IsSuccess()) {
410       callback->sendFailure(response);
411       return;
412     }
413     InjectedScript::ContextScope scope(m_session, contextId);
414     response = scope.initialize();
415     if (!response.IsSuccess()) {
416       callback->sendFailure(response);
417       return;
418     }
419     innerCallFunctionOn(
420         m_session, scope, scope.context()->Global(), expression,
421         std::move(optionalArguments), silent.fromMaybe(false), wrap_mode,
422         userGesture.fromMaybe(false), awaitPromise.fromMaybe(false),
423         objectGroup.fromMaybe(""), throwOnSideEffect.fromMaybe(false),
424         std::move(callback));
425   }
426 }
427 
getProperties(const String16 & objectId,Maybe<bool> ownProperties,Maybe<bool> accessorPropertiesOnly,Maybe<bool> generatePreview,Maybe<bool> nonIndexedPropertiesOnly,std::unique_ptr<protocol::Array<protocol::Runtime::PropertyDescriptor>> * result,Maybe<protocol::Array<protocol::Runtime::InternalPropertyDescriptor>> * internalProperties,Maybe<protocol::Array<protocol::Runtime::PrivatePropertyDescriptor>> * privateProperties,Maybe<protocol::Runtime::ExceptionDetails> * exceptionDetails)428 Response V8RuntimeAgentImpl::getProperties(
429     const String16& objectId, Maybe<bool> ownProperties,
430     Maybe<bool> accessorPropertiesOnly, Maybe<bool> generatePreview,
431     Maybe<bool> nonIndexedPropertiesOnly,
432     std::unique_ptr<protocol::Array<protocol::Runtime::PropertyDescriptor>>*
433         result,
434     Maybe<protocol::Array<protocol::Runtime::InternalPropertyDescriptor>>*
435         internalProperties,
436     Maybe<protocol::Array<protocol::Runtime::PrivatePropertyDescriptor>>*
437         privateProperties,
438     Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
439   using protocol::Runtime::InternalPropertyDescriptor;
440   using protocol::Runtime::PrivatePropertyDescriptor;
441 
442   InjectedScript::ObjectScope scope(m_session, objectId);
443   Response response = scope.initialize();
444   if (!response.IsSuccess()) return response;
445 
446   scope.ignoreExceptionsAndMuteConsole();
447   v8::MicrotasksScope microtasks_scope(m_inspector->isolate(),
448                                        v8::MicrotasksScope::kRunMicrotasks);
449   if (!scope.object()->IsObject())
450     return Response::ServerError("Value with given id is not an object");
451 
452   v8::Local<v8::Object> object = scope.object().As<v8::Object>();
453   response = scope.injectedScript()->getProperties(
454       object, scope.objectGroupName(), ownProperties.fromMaybe(false),
455       accessorPropertiesOnly.fromMaybe(false),
456       nonIndexedPropertiesOnly.fromMaybe(false),
457       generatePreview.fromMaybe(false) ? WrapMode::kWithPreview
458                                        : WrapMode::kNoPreview,
459       result, exceptionDetails);
460   if (!response.IsSuccess()) return response;
461   if (exceptionDetails->isJust()) return Response::Success();
462   std::unique_ptr<protocol::Array<InternalPropertyDescriptor>>
463       internalPropertiesProtocolArray;
464   std::unique_ptr<protocol::Array<PrivatePropertyDescriptor>>
465       privatePropertiesProtocolArray;
466   response = scope.injectedScript()->getInternalAndPrivateProperties(
467       object, scope.objectGroupName(), accessorPropertiesOnly.fromMaybe(false),
468       &internalPropertiesProtocolArray, &privatePropertiesProtocolArray);
469   if (!response.IsSuccess()) return response;
470   if (!internalPropertiesProtocolArray->empty())
471     *internalProperties = std::move(internalPropertiesProtocolArray);
472   if (!privatePropertiesProtocolArray->empty())
473     *privateProperties = std::move(privatePropertiesProtocolArray);
474   return Response::Success();
475 }
476 
releaseObject(const String16 & objectId)477 Response V8RuntimeAgentImpl::releaseObject(const String16& objectId) {
478   InjectedScript::ObjectScope scope(m_session, objectId);
479   Response response = scope.initialize();
480   if (!response.IsSuccess()) return response;
481   scope.injectedScript()->releaseObject(objectId);
482   return Response::Success();
483 }
484 
releaseObjectGroup(const String16 & objectGroup)485 Response V8RuntimeAgentImpl::releaseObjectGroup(const String16& objectGroup) {
486   m_session->releaseObjectGroup(objectGroup);
487   return Response::Success();
488 }
489 
runIfWaitingForDebugger()490 Response V8RuntimeAgentImpl::runIfWaitingForDebugger() {
491   m_inspector->client()->runIfWaitingForDebugger(m_session->contextGroupId());
492   return Response::Success();
493 }
494 
setCustomObjectFormatterEnabled(bool enabled)495 Response V8RuntimeAgentImpl::setCustomObjectFormatterEnabled(bool enabled) {
496   m_state->setBoolean(V8RuntimeAgentImplState::customObjectFormatterEnabled,
497                       enabled);
498   if (!m_enabled) return Response::ServerError("Runtime agent is not enabled");
499   m_session->setCustomObjectFormatterEnabled(enabled);
500   return Response::Success();
501 }
502 
setMaxCallStackSizeToCapture(int size)503 Response V8RuntimeAgentImpl::setMaxCallStackSizeToCapture(int size) {
504   if (size < 0) {
505     return Response::ServerError(
506         "maxCallStackSizeToCapture should be non-negative");
507   }
508   TRACE_EVENT_WITH_FLOW1(
509       TRACE_DISABLED_BY_DEFAULT("v8.inspector"),
510       "V8RuntimeAgentImpl::setMaxCallStackSizeToCapture", this,
511       TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "size", size);
512   if (!m_enabled) return Response::ServerError("Runtime agent is not enabled");
513   m_state->setInteger(V8RuntimeAgentImplState::maxCallStackSizeToCapture, size);
514   m_inspector->debugger()->setMaxCallStackSizeToCapture(this, size);
515   return Response::Success();
516 }
517 
discardConsoleEntries()518 Response V8RuntimeAgentImpl::discardConsoleEntries() {
519   V8ConsoleMessageStorage* storage =
520       m_inspector->ensureConsoleMessageStorage(m_session->contextGroupId());
521   storage->clear();
522   return Response::Success();
523 }
524 
compileScript(const String16 & expression,const String16 & sourceURL,bool persistScript,Maybe<int> executionContextId,Maybe<String16> * scriptId,Maybe<protocol::Runtime::ExceptionDetails> * exceptionDetails)525 Response V8RuntimeAgentImpl::compileScript(
526     const String16& expression, const String16& sourceURL, bool persistScript,
527     Maybe<int> executionContextId, Maybe<String16>* scriptId,
528     Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
529   if (!m_enabled) return Response::ServerError("Runtime agent is not enabled");
530 
531   int contextId = 0;
532   Response response = ensureContext(m_inspector, m_session->contextGroupId(),
533                                     std::move(executionContextId),
534                                     /*uniqueContextId*/ {}, &contextId);
535   if (!response.IsSuccess()) return response;
536   InjectedScript::ContextScope scope(m_session, contextId);
537   response = scope.initialize();
538   if (!response.IsSuccess()) return response;
539 
540   if (!persistScript) m_inspector->debugger()->muteScriptParsedEvents();
541   v8::Local<v8::Script> script;
542   bool isOk = m_inspector->compileScript(scope.context(), expression, sourceURL)
543                   .ToLocal(&script);
544   if (!persistScript) m_inspector->debugger()->unmuteScriptParsedEvents();
545   if (!isOk) {
546     if (scope.tryCatch().HasCaught()) {
547       response = scope.injectedScript()->createExceptionDetails(
548           scope.tryCatch(), String16(), exceptionDetails);
549       if (!response.IsSuccess()) return response;
550       return Response::Success();
551     } else {
552       return Response::ServerError("Script compilation failed");
553     }
554   }
555 
556   if (!persistScript) return Response::Success();
557 
558   String16 scriptValueId =
559       String16::fromInteger(script->GetUnboundScript()->GetId());
560   std::unique_ptr<v8::Global<v8::Script>> global(
561       new v8::Global<v8::Script>(m_inspector->isolate(), script));
562   m_compiledScripts[scriptValueId] = std::move(global);
563   *scriptId = scriptValueId;
564   return Response::Success();
565 }
566 
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)567 void V8RuntimeAgentImpl::runScript(
568     const String16& scriptId, Maybe<int> executionContextId,
569     Maybe<String16> objectGroup, Maybe<bool> silent,
570     Maybe<bool> includeCommandLineAPI, Maybe<bool> returnByValue,
571     Maybe<bool> generatePreview, Maybe<bool> awaitPromise,
572     std::unique_ptr<RunScriptCallback> callback) {
573   if (!m_enabled) {
574     callback->sendFailure(
575         Response::ServerError("Runtime agent is not enabled"));
576     return;
577   }
578 
579   auto it = m_compiledScripts.find(scriptId);
580   if (it == m_compiledScripts.end()) {
581     callback->sendFailure(Response::ServerError("No script with given id"));
582     return;
583   }
584 
585   int contextId = 0;
586   Response response = ensureContext(m_inspector, m_session->contextGroupId(),
587                                     std::move(executionContextId),
588                                     /*uniqueContextId*/ {}, &contextId);
589   if (!response.IsSuccess()) {
590     callback->sendFailure(response);
591     return;
592   }
593 
594   InjectedScript::ContextScope scope(m_session, contextId);
595   response = scope.initialize();
596   if (!response.IsSuccess()) {
597     callback->sendFailure(response);
598     return;
599   }
600 
601   if (silent.fromMaybe(false)) scope.ignoreExceptionsAndMuteConsole();
602 
603   std::unique_ptr<v8::Global<v8::Script>> scriptWrapper = std::move(it->second);
604   m_compiledScripts.erase(it);
605   v8::Local<v8::Script> script = scriptWrapper->Get(m_inspector->isolate());
606   if (script.IsEmpty()) {
607     callback->sendFailure(Response::ServerError("Script execution failed"));
608     return;
609   }
610 
611   if (includeCommandLineAPI.fromMaybe(false)) scope.installCommandLineAPI();
612 
613   v8::MaybeLocal<v8::Value> maybeResultValue;
614   {
615     v8::MicrotasksScope microtasksScope(m_inspector->isolate(),
616                                         v8::MicrotasksScope::kRunMicrotasks);
617     maybeResultValue = script->Run(scope.context());
618   }
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   WrapMode mode = generatePreview.fromMaybe(false) ? WrapMode::kWithPreview
629                                                    : WrapMode::kNoPreview;
630   if (returnByValue.fromMaybe(false)) mode = WrapMode::kForceValue;
631   if (!awaitPromise.fromMaybe(false) || scope.tryCatch().HasCaught()) {
632     wrapEvaluateResultAsync(scope.injectedScript(), maybeResultValue,
633                             scope.tryCatch(), objectGroup.fromMaybe(""), mode,
634                             callback.get());
635     return;
636   }
637   scope.injectedScript()->addPromiseCallback(
638       m_session, maybeResultValue.ToLocalChecked(), objectGroup.fromMaybe(""),
639       mode, false /* replMode */,
640       EvaluateCallbackWrapper<RunScriptCallback>::wrap(std::move(callback)));
641 }
642 
queryObjects(const String16 & prototypeObjectId,Maybe<String16> objectGroup,std::unique_ptr<protocol::Runtime::RemoteObject> * objects)643 Response V8RuntimeAgentImpl::queryObjects(
644     const String16& prototypeObjectId, Maybe<String16> objectGroup,
645     std::unique_ptr<protocol::Runtime::RemoteObject>* objects) {
646   InjectedScript::ObjectScope scope(m_session, prototypeObjectId);
647   Response response = scope.initialize();
648   if (!response.IsSuccess()) return response;
649   if (!scope.object()->IsObject()) {
650     return Response::ServerError("Prototype should be instance of Object");
651   }
652   v8::Local<v8::Array> resultArray = m_inspector->debugger()->queryObjects(
653       scope.context(), scope.object().As<v8::Object>());
654   return scope.injectedScript()->wrapObject(
655       resultArray, objectGroup.fromMaybe(scope.objectGroupName()),
656       WrapMode::kNoPreview, objects);
657 }
658 
globalLexicalScopeNames(Maybe<int> executionContextId,std::unique_ptr<protocol::Array<String16>> * outNames)659 Response V8RuntimeAgentImpl::globalLexicalScopeNames(
660     Maybe<int> executionContextId,
661     std::unique_ptr<protocol::Array<String16>>* outNames) {
662   int contextId = 0;
663   Response response = ensureContext(m_inspector, m_session->contextGroupId(),
664                                     std::move(executionContextId),
665                                     /*uniqueContextId*/ {}, &contextId);
666   if (!response.IsSuccess()) return response;
667 
668   InjectedScript::ContextScope scope(m_session, contextId);
669   response = scope.initialize();
670   if (!response.IsSuccess()) return response;
671 
672   v8::PersistentValueVector<v8::String> names(m_inspector->isolate());
673   v8::debug::GlobalLexicalScopeNames(scope.context(), &names);
674   *outNames = std::make_unique<protocol::Array<String16>>();
675   for (size_t i = 0; i < names.Size(); ++i) {
676     (*outNames)->emplace_back(
677         toProtocolString(m_inspector->isolate(), names.Get(i)));
678   }
679   return Response::Success();
680 }
681 
getIsolateId(String16 * outIsolateId)682 Response V8RuntimeAgentImpl::getIsolateId(String16* outIsolateId) {
683   char buf[40];
684   std::snprintf(buf, sizeof(buf), "%" PRIx64, m_inspector->isolateId());
685   *outIsolateId = buf;
686   return Response::Success();
687 }
688 
getHeapUsage(double * out_usedSize,double * out_totalSize)689 Response V8RuntimeAgentImpl::getHeapUsage(double* out_usedSize,
690                                           double* out_totalSize) {
691   v8::HeapStatistics stats;
692   m_inspector->isolate()->GetHeapStatistics(&stats);
693   *out_usedSize = stats.used_heap_size();
694   *out_totalSize = stats.total_heap_size();
695   return Response::Success();
696 }
697 
terminateExecution(std::unique_ptr<TerminateExecutionCallback> callback)698 void V8RuntimeAgentImpl::terminateExecution(
699     std::unique_ptr<TerminateExecutionCallback> callback) {
700   m_inspector->debugger()->terminateExecution(std::move(callback));
701 }
702 
703 namespace {
getOrCreateDictionary(protocol::DictionaryValue * dict,const String16 & key)704 protocol::DictionaryValue* getOrCreateDictionary(
705     protocol::DictionaryValue* dict, const String16& key) {
706   if (protocol::DictionaryValue* bindings = dict->getObject(key))
707     return bindings;
708   dict->setObject(key, protocol::DictionaryValue::create());
709   return dict->getObject(key);
710 }
711 }  // namespace
712 
addBinding(const String16 & name,Maybe<int> executionContextId,Maybe<String16> executionContextName)713 Response V8RuntimeAgentImpl::addBinding(const String16& name,
714                                         Maybe<int> executionContextId,
715                                         Maybe<String16> executionContextName) {
716   if (executionContextId.isJust()) {
717     if (executionContextName.isJust()) {
718       return Response::InvalidParams(
719           "executionContextName is mutually exclusive with executionContextId");
720     }
721     int contextId = executionContextId.fromJust();
722     InspectedContext* context =
723         m_inspector->getContext(m_session->contextGroupId(), contextId);
724     if (!context) {
725       return Response::InvalidParams(
726           "Cannot find execution context with given executionContextId");
727     }
728     addBinding(context, name);
729     return Response::Success();
730   }
731 
732   // If it's a globally exposed binding, i.e. no context name specified, use
733   // a special value for the context name.
734   String16 contextKey = V8RuntimeAgentImplState::globalBindingsKey;
735   if (executionContextName.isJust()) {
736     contextKey = executionContextName.fromJust();
737     if (contextKey == V8RuntimeAgentImplState::globalBindingsKey) {
738       return Response::InvalidParams("Invalid executionContextName");
739     }
740   }
741   // Only persist non context-specific bindings, as contextIds don't make
742   // any sense when state is restored in a different process.
743   protocol::DictionaryValue* bindings =
744       getOrCreateDictionary(m_state, V8RuntimeAgentImplState::bindings);
745   protocol::DictionaryValue* contextBindings =
746       getOrCreateDictionary(bindings, contextKey);
747   contextBindings->setBoolean(name, true);
748 
749   m_inspector->forEachContext(
750       m_session->contextGroupId(),
751       [&name, &executionContextName, this](InspectedContext* context) {
752         if (executionContextName.isJust() &&
753             executionContextName.fromJust() != context->humanReadableName())
754           return;
755         addBinding(context, name);
756       });
757   return Response::Success();
758 }
759 
bindingCallback(const v8::FunctionCallbackInfo<v8::Value> & info)760 void V8RuntimeAgentImpl::bindingCallback(
761     const v8::FunctionCallbackInfo<v8::Value>& info) {
762   v8::Isolate* isolate = info.GetIsolate();
763   if (info.Length() != 1 || !info[0]->IsString()) {
764     info.GetIsolate()->ThrowError(
765         "Invalid arguments: should be exactly one string.");
766     return;
767   }
768   V8InspectorImpl* inspector =
769       static_cast<V8InspectorImpl*>(v8::debug::GetInspector(isolate));
770   int contextId = InspectedContext::contextId(isolate->GetCurrentContext());
771   int contextGroupId = inspector->contextGroupId(contextId);
772 
773   String16 name = toProtocolString(isolate, info.Data().As<v8::String>());
774   String16 payload = toProtocolString(isolate, info[0].As<v8::String>());
775 
776   inspector->forEachSession(
777       contextGroupId,
778       [&name, &payload, &contextId](V8InspectorSessionImpl* session) {
779         session->runtimeAgent()->bindingCalled(name, payload, contextId);
780       });
781 }
782 
addBinding(InspectedContext * context,const String16 & name)783 void V8RuntimeAgentImpl::addBinding(InspectedContext* context,
784                                     const String16& name) {
785   auto it = m_activeBindings.find(name);
786   if (it != m_activeBindings.end() && it->second.count(context->contextId())) {
787     return;
788   }
789   v8::HandleScope handles(m_inspector->isolate());
790   v8::Local<v8::Context> localContext = context->context();
791   v8::Local<v8::Object> global = localContext->Global();
792   v8::Local<v8::String> v8Name = toV8String(m_inspector->isolate(), name);
793   v8::Local<v8::Value> functionValue;
794   v8::MicrotasksScope microtasks(m_inspector->isolate(),
795                                  v8::MicrotasksScope::kDoNotRunMicrotasks);
796   if (v8::Function::New(localContext, bindingCallback, v8Name)
797           .ToLocal(&functionValue)) {
798     v8::Maybe<bool> success = global->Set(localContext, v8Name, functionValue);
799     USE(success);
800     if (it == m_activeBindings.end()) {
801       m_activeBindings.emplace(name,
802                                std::unordered_set<int>(context->contextId()));
803     } else {
804       m_activeBindings.at(name).insert(context->contextId());
805     }
806   }
807 }
808 
removeBinding(const String16 & name)809 Response V8RuntimeAgentImpl::removeBinding(const String16& name) {
810   protocol::DictionaryValue* bindings =
811       m_state->getObject(V8RuntimeAgentImplState::bindings);
812   if (bindings) bindings->remove(name);
813   m_activeBindings.erase(name);
814   return Response::Success();
815 }
816 
getExceptionDetails(const String16 & errorObjectId,Maybe<protocol::Runtime::ExceptionDetails> * out_exceptionDetails)817 Response V8RuntimeAgentImpl::getExceptionDetails(
818     const String16& errorObjectId,
819     Maybe<protocol::Runtime::ExceptionDetails>* out_exceptionDetails) {
820   InjectedScript::ObjectScope scope(m_session, errorObjectId);
821   Response response = scope.initialize();
822   if (!response.IsSuccess()) return response;
823 
824   const v8::Local<v8::Value> error = scope.object();
825   if (!error->IsNativeError())
826     return Response::ServerError("errorObjectId is not a JS error object");
827 
828   const v8::Local<v8::Message> message =
829       v8::debug::CreateMessageFromException(m_inspector->isolate(), error);
830 
831   response = scope.injectedScript()->createExceptionDetails(
832       message, error, scope.objectGroupName(), out_exceptionDetails);
833   if (!response.IsSuccess()) return response;
834 
835   CHECK(out_exceptionDetails->isJust());
836 
837   // When an exception object is present, `createExceptionDetails` assumes
838   // the exception is uncaught and will overwrite the text field to "Uncaught".
839   // Lets use the normal message text instead.
840   out_exceptionDetails->fromJust()->setText(
841       toProtocolString(m_inspector->isolate(), message->Get()));
842 
843   // Check if the exception has any metadata on the inspector and also attach
844   // it.
845   std::unique_ptr<protocol::DictionaryValue> data =
846       m_inspector->getAssociatedExceptionDataForProtocol(error);
847   if (data)
848     out_exceptionDetails->fromJust()->setExceptionMetaData(std::move(data));
849   return Response::Success();
850 }
851 
bindingCalled(const String16 & name,const String16 & payload,int executionContextId)852 void V8RuntimeAgentImpl::bindingCalled(const String16& name,
853                                        const String16& payload,
854                                        int executionContextId) {
855   if (!m_activeBindings.count(name)) return;
856   m_frontend.bindingCalled(name, payload, executionContextId);
857   m_frontend.flush();
858 }
859 
addBindings(InspectedContext * context)860 void V8RuntimeAgentImpl::addBindings(InspectedContext* context) {
861   const String16 contextName = context->humanReadableName();
862   if (!m_enabled) return;
863   protocol::DictionaryValue* bindings =
864       m_state->getObject(V8RuntimeAgentImplState::bindings);
865   if (!bindings) return;
866   protocol::DictionaryValue* globalBindings =
867       bindings->getObject(V8RuntimeAgentImplState::globalBindingsKey);
868   if (globalBindings) {
869     for (size_t i = 0; i < globalBindings->size(); ++i)
870       addBinding(context, globalBindings->at(i).first);
871   }
872   protocol::DictionaryValue* contextBindings =
873       contextName.isEmpty() ? nullptr : bindings->getObject(contextName);
874   if (contextBindings) {
875     for (size_t i = 0; i < contextBindings->size(); ++i)
876       addBinding(context, contextBindings->at(i).first);
877   }
878 }
879 
restore()880 void V8RuntimeAgentImpl::restore() {
881   if (!m_state->booleanProperty(V8RuntimeAgentImplState::runtimeEnabled, false))
882     return;
883   m_frontend.executionContextsCleared();
884   enable();
885   if (m_state->booleanProperty(
886           V8RuntimeAgentImplState::customObjectFormatterEnabled, false))
887     m_session->setCustomObjectFormatterEnabled(true);
888 
889   int size;
890   if (m_state->getInteger(V8RuntimeAgentImplState::maxCallStackSizeToCapture,
891                           &size))
892     m_inspector->debugger()->setMaxCallStackSizeToCapture(this, size);
893 
894   m_inspector->forEachContext(
895       m_session->contextGroupId(),
896       [this](InspectedContext* context) { addBindings(context); });
897 }
898 
enable()899 Response V8RuntimeAgentImpl::enable() {
900   if (m_enabled) return Response::Success();
901   TRACE_EVENT_WITH_FLOW0(TRACE_DISABLED_BY_DEFAULT("v8.inspector"),
902                          "V8RuntimeAgentImpl::enable", this,
903                          TRACE_EVENT_FLAG_FLOW_OUT);
904   m_inspector->client()->beginEnsureAllContextsInGroup(
905       m_session->contextGroupId());
906   m_enabled = true;
907   m_state->setBoolean(V8RuntimeAgentImplState::runtimeEnabled, true);
908   m_inspector->debugger()->setMaxCallStackSizeToCapture(
909       this, V8StackTraceImpl::kDefaultMaxCallStackSizeToCapture);
910   m_session->reportAllContexts(this);
911   V8ConsoleMessageStorage* storage =
912       m_inspector->ensureConsoleMessageStorage(m_session->contextGroupId());
913   for (const auto& message : storage->messages()) {
914     if (!reportMessage(message.get(), false)) break;
915   }
916   return Response::Success();
917 }
918 
disable()919 Response V8RuntimeAgentImpl::disable() {
920   if (!m_enabled) return Response::Success();
921   TRACE_EVENT_WITH_FLOW0(TRACE_DISABLED_BY_DEFAULT("v8.inspector"),
922                          "V8RuntimeAgentImpl::disable", this,
923                          TRACE_EVENT_FLAG_FLOW_IN);
924   m_enabled = false;
925   m_state->setBoolean(V8RuntimeAgentImplState::runtimeEnabled, false);
926   m_state->remove(V8RuntimeAgentImplState::bindings);
927   m_inspector->debugger()->setMaxCallStackSizeToCapture(this, -1);
928   m_session->setCustomObjectFormatterEnabled(false);
929   reset();
930   m_inspector->client()->endEnsureAllContextsInGroup(
931       m_session->contextGroupId());
932   if (m_session->debuggerAgent() && !m_session->debuggerAgent()->enabled()) {
933     m_session->debuggerAgent()->setAsyncCallStackDepth(0);
934   }
935   return Response::Success();
936 }
937 
reset()938 void V8RuntimeAgentImpl::reset() {
939   m_compiledScripts.clear();
940   if (m_enabled) {
941     int sessionId = m_session->sessionId();
942     m_inspector->forEachContext(m_session->contextGroupId(),
943                                 [&sessionId](InspectedContext* context) {
944                                   context->setReported(sessionId, false);
945                                 });
946     m_frontend.executionContextsCleared();
947   }
948 }
949 
reportExecutionContextCreated(InspectedContext * context)950 void V8RuntimeAgentImpl::reportExecutionContextCreated(
951     InspectedContext* context) {
952   if (!m_enabled) return;
953   context->setReported(m_session->sessionId(), true);
954   std::unique_ptr<protocol::Runtime::ExecutionContextDescription> description =
955       protocol::Runtime::ExecutionContextDescription::create()
956           .setId(context->contextId())
957           .setName(context->humanReadableName())
958           .setOrigin(context->origin())
959           .setUniqueId(context->uniqueId().toString())
960           .build();
961   const String16& aux = context->auxData();
962   if (!aux.isEmpty()) {
963     std::vector<uint8_t> cbor;
964     v8_crdtp::json::ConvertJSONToCBOR(
965         v8_crdtp::span<uint16_t>(aux.characters16(), aux.length()), &cbor);
966     description->setAuxData(protocol::DictionaryValue::cast(
967         protocol::Value::parseBinary(cbor.data(), cbor.size())));
968   }
969   m_frontend.executionContextCreated(std::move(description));
970 }
971 
reportExecutionContextDestroyed(InspectedContext * context)972 void V8RuntimeAgentImpl::reportExecutionContextDestroyed(
973     InspectedContext* context) {
974   if (m_enabled && context->isReported(m_session->sessionId())) {
975     context->setReported(m_session->sessionId(), false);
976     m_frontend.executionContextDestroyed(context->contextId());
977   }
978 }
979 
inspect(std::unique_ptr<protocol::Runtime::RemoteObject> objectToInspect,std::unique_ptr<protocol::DictionaryValue> hints,int executionContextId)980 void V8RuntimeAgentImpl::inspect(
981     std::unique_ptr<protocol::Runtime::RemoteObject> objectToInspect,
982     std::unique_ptr<protocol::DictionaryValue> hints, int executionContextId) {
983   if (m_enabled)
984     m_frontend.inspectRequested(std::move(objectToInspect), std::move(hints),
985                                 executionContextId);
986 }
987 
messageAdded(V8ConsoleMessage * message)988 void V8RuntimeAgentImpl::messageAdded(V8ConsoleMessage* message) {
989   if (m_enabled) reportMessage(message, true);
990 }
991 
reportMessage(V8ConsoleMessage * message,bool generatePreview)992 bool V8RuntimeAgentImpl::reportMessage(V8ConsoleMessage* message,
993                                        bool generatePreview) {
994   message->reportToFrontend(&m_frontend, m_session, generatePreview);
995   m_frontend.flush();
996   return m_inspector->hasConsoleMessageStorage(m_session->contextGroupId());
997 }
998 }  // namespace v8_inspector
999