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