Home | History | Annotate | Download | only in inspector
      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:
     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   }
     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   }
     78   void sendFailure(const protocol::DispatchResponse& response) override {
     79     return m_callback->sendFailure(response);
     80   }
     81 
     82  private:
     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>
     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 
    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 
    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 
    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 
    229 V8RuntimeAgentImpl::~V8RuntimeAgentImpl() {}
    230 
    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 
    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 
    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 
    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 
    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 
    447 Response V8RuntimeAgentImpl::releaseObjectGroup(const String16& objectGroup) {
    448   m_session->releaseObjectGroup(objectGroup);
    449   return Response::OK();
    450 }
    451 
    452 Response V8RuntimeAgentImpl::runIfWaitingForDebugger() {
    453   m_inspector->client()->runIfWaitingForDebugger(m_session->contextGroupId());
    454   return Response::OK();
    455 }
    456 
    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 
    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 
    473 Response V8RuntimeAgentImpl::discardConsoleEntries() {
    474   V8ConsoleMessageStorage* storage =
    475       m_inspector->ensureConsoleMessageStorage(m_session->contextGroupId());
    476   storage->clear();
    477   return Response::OK();
    478 }
    479 
    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 
    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 
    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 
    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 
    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 
    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 
    648 void V8RuntimeAgentImpl::terminateExecution(
    649     std::unique_ptr<TerminateExecutionCallback> callback) {
    650   m_inspector->debugger()->terminateExecution(std::move(callback));
    651 }
    652 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    840 void V8RuntimeAgentImpl::messageAdded(V8ConsoleMessage* message) {
    841   if (m_enabled) reportMessage(message, true);
    842 }
    843 
    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