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