Home | History | Annotate | Download | only in inspector
      1 // Copyright 2016 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "src/inspector/v8-inspector-session-impl.h"
      6 
      7 #include "src/inspector/injected-script.h"
      8 #include "src/inspector/inspected-context.h"
      9 #include "src/inspector/protocol/Protocol.h"
     10 #include "src/inspector/remote-object-id.h"
     11 #include "src/inspector/search-util.h"
     12 #include "src/inspector/string-util.h"
     13 #include "src/inspector/v8-console-agent-impl.h"
     14 #include "src/inspector/v8-debugger-agent-impl.h"
     15 #include "src/inspector/v8-debugger.h"
     16 #include "src/inspector/v8-heap-profiler-agent-impl.h"
     17 #include "src/inspector/v8-inspector-impl.h"
     18 #include "src/inspector/v8-profiler-agent-impl.h"
     19 #include "src/inspector/v8-runtime-agent-impl.h"
     20 #include "src/inspector/v8-schema-agent-impl.h"
     21 
     22 namespace v8_inspector {
     23 
     24 // static
     25 bool V8InspectorSession::canDispatchMethod(const StringView& method) {
     26   return stringViewStartsWith(method,
     27                               protocol::Runtime::Metainfo::commandPrefix) ||
     28          stringViewStartsWith(method,
     29                               protocol::Debugger::Metainfo::commandPrefix) ||
     30          stringViewStartsWith(method,
     31                               protocol::Profiler::Metainfo::commandPrefix) ||
     32          stringViewStartsWith(
     33              method, protocol::HeapProfiler::Metainfo::commandPrefix) ||
     34          stringViewStartsWith(method,
     35                               protocol::Console::Metainfo::commandPrefix) ||
     36          stringViewStartsWith(method,
     37                               protocol::Schema::Metainfo::commandPrefix);
     38 }
     39 
     40 // static
     41 int V8ContextInfo::executionContextId(v8::Local<v8::Context> context) {
     42   return InspectedContext::contextId(context);
     43 }
     44 
     45 std::unique_ptr<V8InspectorSessionImpl> V8InspectorSessionImpl::create(
     46     V8InspectorImpl* inspector, int contextGroupId, int sessionId,
     47     V8Inspector::Channel* channel, const StringView& state) {
     48   return std::unique_ptr<V8InspectorSessionImpl>(new V8InspectorSessionImpl(
     49       inspector, contextGroupId, sessionId, channel, state));
     50 }
     51 
     52 V8InspectorSessionImpl::V8InspectorSessionImpl(V8InspectorImpl* inspector,
     53                                                int contextGroupId,
     54                                                int sessionId,
     55                                                V8Inspector::Channel* channel,
     56                                                const StringView& savedState)
     57     : m_contextGroupId(contextGroupId),
     58       m_sessionId(sessionId),
     59       m_inspector(inspector),
     60       m_channel(channel),
     61       m_customObjectFormatterEnabled(false),
     62       m_dispatcher(this),
     63       m_state(nullptr),
     64       m_runtimeAgent(nullptr),
     65       m_debuggerAgent(nullptr),
     66       m_heapProfilerAgent(nullptr),
     67       m_profilerAgent(nullptr),
     68       m_consoleAgent(nullptr),
     69       m_schemaAgent(nullptr) {
     70   if (savedState.length()) {
     71     std::unique_ptr<protocol::Value> state =
     72         protocol::StringUtil::parseJSON(toString16(savedState));
     73     if (state) m_state = protocol::DictionaryValue::cast(std::move(state));
     74     if (!m_state) m_state = protocol::DictionaryValue::create();
     75   } else {
     76     m_state = protocol::DictionaryValue::create();
     77   }
     78 
     79   m_runtimeAgent.reset(new V8RuntimeAgentImpl(
     80       this, this, agentState(protocol::Runtime::Metainfo::domainName)));
     81   protocol::Runtime::Dispatcher::wire(&m_dispatcher, m_runtimeAgent.get());
     82 
     83   m_debuggerAgent.reset(new V8DebuggerAgentImpl(
     84       this, this, agentState(protocol::Debugger::Metainfo::domainName)));
     85   protocol::Debugger::Dispatcher::wire(&m_dispatcher, m_debuggerAgent.get());
     86 
     87   m_profilerAgent.reset(new V8ProfilerAgentImpl(
     88       this, this, agentState(protocol::Profiler::Metainfo::domainName)));
     89   protocol::Profiler::Dispatcher::wire(&m_dispatcher, m_profilerAgent.get());
     90 
     91   m_heapProfilerAgent.reset(new V8HeapProfilerAgentImpl(
     92       this, this, agentState(protocol::HeapProfiler::Metainfo::domainName)));
     93   protocol::HeapProfiler::Dispatcher::wire(&m_dispatcher,
     94                                            m_heapProfilerAgent.get());
     95 
     96   m_consoleAgent.reset(new V8ConsoleAgentImpl(
     97       this, this, agentState(protocol::Console::Metainfo::domainName)));
     98   protocol::Console::Dispatcher::wire(&m_dispatcher, m_consoleAgent.get());
     99 
    100   m_schemaAgent.reset(new V8SchemaAgentImpl(
    101       this, this, agentState(protocol::Schema::Metainfo::domainName)));
    102   protocol::Schema::Dispatcher::wire(&m_dispatcher, m_schemaAgent.get());
    103 
    104   if (savedState.length()) {
    105     m_runtimeAgent->restore();
    106     m_debuggerAgent->restore();
    107     m_heapProfilerAgent->restore();
    108     m_profilerAgent->restore();
    109     m_consoleAgent->restore();
    110   }
    111 }
    112 
    113 V8InspectorSessionImpl::~V8InspectorSessionImpl() {
    114   discardInjectedScripts();
    115   m_consoleAgent->disable();
    116   m_profilerAgent->disable();
    117   m_heapProfilerAgent->disable();
    118   m_debuggerAgent->disable();
    119   m_runtimeAgent->disable();
    120   m_inspector->disconnect(this);
    121 }
    122 
    123 protocol::DictionaryValue* V8InspectorSessionImpl::agentState(
    124     const String16& name) {
    125   protocol::DictionaryValue* state = m_state->getObject(name);
    126   if (!state) {
    127     std::unique_ptr<protocol::DictionaryValue> newState =
    128         protocol::DictionaryValue::create();
    129     state = newState.get();
    130     m_state->setObject(name, std::move(newState));
    131   }
    132   return state;
    133 }
    134 
    135 namespace {
    136 
    137 class MessageBuffer : public StringBuffer {
    138  public:
    139   static std::unique_ptr<MessageBuffer> create(
    140       std::unique_ptr<protocol::Serializable> message) {
    141     return std::unique_ptr<MessageBuffer>(
    142         new MessageBuffer(std::move(message)));
    143   }
    144 
    145   const StringView& string() override {
    146     if (!m_serialized) {
    147       m_serialized = StringBuffer::create(toStringView(m_message->serialize()));
    148       m_message.reset(nullptr);
    149     }
    150     return m_serialized->string();
    151   }
    152 
    153  private:
    154   explicit MessageBuffer(std::unique_ptr<protocol::Serializable> message)
    155       : m_message(std::move(message)) {}
    156 
    157   std::unique_ptr<protocol::Serializable> m_message;
    158   std::unique_ptr<StringBuffer> m_serialized;
    159 };
    160 
    161 }  // namespace
    162 
    163 void V8InspectorSessionImpl::sendProtocolResponse(
    164     int callId, std::unique_ptr<protocol::Serializable> message) {
    165   m_channel->sendResponse(callId, MessageBuffer::create(std::move(message)));
    166 }
    167 
    168 void V8InspectorSessionImpl::sendProtocolNotification(
    169     std::unique_ptr<protocol::Serializable> message) {
    170   m_channel->sendNotification(MessageBuffer::create(std::move(message)));
    171 }
    172 
    173 void V8InspectorSessionImpl::fallThrough(int callId, const String16& method,
    174                                          const String16& message) {
    175   // There's no other layer to handle the command.
    176   UNREACHABLE();
    177 }
    178 
    179 void V8InspectorSessionImpl::flushProtocolNotifications() {
    180   m_channel->flushProtocolNotifications();
    181 }
    182 
    183 void V8InspectorSessionImpl::reset() {
    184   m_debuggerAgent->reset();
    185   m_runtimeAgent->reset();
    186   discardInjectedScripts();
    187 }
    188 
    189 void V8InspectorSessionImpl::discardInjectedScripts() {
    190   m_inspectedObjects.clear();
    191   int sessionId = m_sessionId;
    192   m_inspector->forEachContext(m_contextGroupId,
    193                               [&sessionId](InspectedContext* context) {
    194                                 context->discardInjectedScript(sessionId);
    195                               });
    196 }
    197 
    198 Response V8InspectorSessionImpl::findInjectedScript(
    199     int contextId, InjectedScript*& injectedScript) {
    200   injectedScript = nullptr;
    201   InspectedContext* context =
    202       m_inspector->getContext(m_contextGroupId, contextId);
    203   if (!context) return Response::Error("Cannot find context with specified id");
    204   injectedScript = context->getInjectedScript(m_sessionId);
    205   if (!injectedScript) {
    206     if (!context->createInjectedScript(m_sessionId)) {
    207       if (m_inspector->isolate()->IsExecutionTerminating())
    208         return Response::Error("Execution was terminated");
    209       return Response::Error("Cannot access specified execution context");
    210     }
    211     injectedScript = context->getInjectedScript(m_sessionId);
    212     if (m_customObjectFormatterEnabled)
    213       injectedScript->setCustomObjectFormatterEnabled(true);
    214   }
    215   return Response::OK();
    216 }
    217 
    218 Response V8InspectorSessionImpl::findInjectedScript(
    219     RemoteObjectIdBase* objectId, InjectedScript*& injectedScript) {
    220   return findInjectedScript(objectId->contextId(), injectedScript);
    221 }
    222 
    223 void V8InspectorSessionImpl::releaseObjectGroup(const StringView& objectGroup) {
    224   releaseObjectGroup(toString16(objectGroup));
    225 }
    226 
    227 void V8InspectorSessionImpl::releaseObjectGroup(const String16& objectGroup) {
    228   int sessionId = m_sessionId;
    229   m_inspector->forEachContext(
    230       m_contextGroupId, [&objectGroup, &sessionId](InspectedContext* context) {
    231         InjectedScript* injectedScript = context->getInjectedScript(sessionId);
    232         if (injectedScript) injectedScript->releaseObjectGroup(objectGroup);
    233       });
    234 }
    235 
    236 bool V8InspectorSessionImpl::unwrapObject(
    237     std::unique_ptr<StringBuffer>* error, const StringView& objectId,
    238     v8::Local<v8::Value>* object, v8::Local<v8::Context>* context,
    239     std::unique_ptr<StringBuffer>* objectGroup) {
    240   String16 objectGroupString;
    241   Response response = unwrapObject(toString16(objectId), object, context,
    242                                    objectGroup ? &objectGroupString : nullptr);
    243   if (!response.isSuccess()) {
    244     if (error) {
    245       String16 errorMessage = response.errorMessage();
    246       *error = StringBufferImpl::adopt(errorMessage);
    247     }
    248     return false;
    249   }
    250   if (objectGroup) *objectGroup = StringBufferImpl::adopt(objectGroupString);
    251   return true;
    252 }
    253 
    254 Response V8InspectorSessionImpl::unwrapObject(const String16& objectId,
    255                                               v8::Local<v8::Value>* object,
    256                                               v8::Local<v8::Context>* context,
    257                                               String16* objectGroup) {
    258   std::unique_ptr<RemoteObjectId> remoteId;
    259   Response response = RemoteObjectId::parse(objectId, &remoteId);
    260   if (!response.isSuccess()) return response;
    261   InjectedScript* injectedScript = nullptr;
    262   response = findInjectedScript(remoteId.get(), injectedScript);
    263   if (!response.isSuccess()) return response;
    264   response = injectedScript->findObject(*remoteId, object);
    265   if (!response.isSuccess()) return response;
    266   *context = injectedScript->context()->context();
    267   if (objectGroup) *objectGroup = injectedScript->objectGroupName(*remoteId);
    268   return Response::OK();
    269 }
    270 
    271 std::unique_ptr<protocol::Runtime::API::RemoteObject>
    272 V8InspectorSessionImpl::wrapObject(v8::Local<v8::Context> context,
    273                                    v8::Local<v8::Value> value,
    274                                    const StringView& groupName,
    275                                    bool generatePreview) {
    276   return wrapObject(context, value, toString16(groupName), generatePreview);
    277 }
    278 
    279 std::unique_ptr<protocol::Runtime::RemoteObject>
    280 V8InspectorSessionImpl::wrapObject(v8::Local<v8::Context> context,
    281                                    v8::Local<v8::Value> value,
    282                                    const String16& groupName,
    283                                    bool generatePreview) {
    284   InjectedScript* injectedScript = nullptr;
    285   findInjectedScript(InspectedContext::contextId(context), injectedScript);
    286   if (!injectedScript) return nullptr;
    287   std::unique_ptr<protocol::Runtime::RemoteObject> result;
    288   injectedScript->wrapObject(value, groupName, false, generatePreview, &result);
    289   return result;
    290 }
    291 
    292 std::unique_ptr<protocol::Runtime::RemoteObject>
    293 V8InspectorSessionImpl::wrapTable(v8::Local<v8::Context> context,
    294                                   v8::Local<v8::Value> table,
    295                                   v8::Local<v8::Value> columns) {
    296   InjectedScript* injectedScript = nullptr;
    297   findInjectedScript(InspectedContext::contextId(context), injectedScript);
    298   if (!injectedScript) return nullptr;
    299   return injectedScript->wrapTable(table, columns);
    300 }
    301 
    302 void V8InspectorSessionImpl::setCustomObjectFormatterEnabled(bool enabled) {
    303   m_customObjectFormatterEnabled = enabled;
    304   int sessionId = m_sessionId;
    305   m_inspector->forEachContext(
    306       m_contextGroupId, [&enabled, &sessionId](InspectedContext* context) {
    307         InjectedScript* injectedScript = context->getInjectedScript(sessionId);
    308         if (injectedScript)
    309           injectedScript->setCustomObjectFormatterEnabled(enabled);
    310       });
    311 }
    312 
    313 void V8InspectorSessionImpl::reportAllContexts(V8RuntimeAgentImpl* agent) {
    314   m_inspector->forEachContext(m_contextGroupId,
    315                               [&agent](InspectedContext* context) {
    316                                 agent->reportExecutionContextCreated(context);
    317                               });
    318 }
    319 
    320 void V8InspectorSessionImpl::dispatchProtocolMessage(
    321     const StringView& message) {
    322   int callId;
    323   String16 method;
    324   std::unique_ptr<protocol::Value> parsedMessage =
    325       protocol::StringUtil::parseJSON(message);
    326   if (m_dispatcher.parseCommand(parsedMessage.get(), &callId, &method)) {
    327     // Pass empty string instead of the actual message to save on a conversion.
    328     // We're allowed to do so because fall-through is not implemented.
    329     m_dispatcher.dispatch(callId, method, std::move(parsedMessage), "");
    330   }
    331 }
    332 
    333 std::unique_ptr<StringBuffer> V8InspectorSessionImpl::stateJSON() {
    334   String16 json = m_state->serialize();
    335   return StringBufferImpl::adopt(json);
    336 }
    337 
    338 std::vector<std::unique_ptr<protocol::Schema::API::Domain>>
    339 V8InspectorSessionImpl::supportedDomains() {
    340   std::vector<std::unique_ptr<protocol::Schema::Domain>> domains =
    341       supportedDomainsImpl();
    342   std::vector<std::unique_ptr<protocol::Schema::API::Domain>> result;
    343   for (size_t i = 0; i < domains.size(); ++i)
    344     result.push_back(std::move(domains[i]));
    345   return result;
    346 }
    347 
    348 std::vector<std::unique_ptr<protocol::Schema::Domain>>
    349 V8InspectorSessionImpl::supportedDomainsImpl() {
    350   std::vector<std::unique_ptr<protocol::Schema::Domain>> result;
    351   result.push_back(protocol::Schema::Domain::create()
    352                        .setName(protocol::Runtime::Metainfo::domainName)
    353                        .setVersion(protocol::Runtime::Metainfo::version)
    354                        .build());
    355   result.push_back(protocol::Schema::Domain::create()
    356                        .setName(protocol::Debugger::Metainfo::domainName)
    357                        .setVersion(protocol::Debugger::Metainfo::version)
    358                        .build());
    359   result.push_back(protocol::Schema::Domain::create()
    360                        .setName(protocol::Profiler::Metainfo::domainName)
    361                        .setVersion(protocol::Profiler::Metainfo::version)
    362                        .build());
    363   result.push_back(protocol::Schema::Domain::create()
    364                        .setName(protocol::HeapProfiler::Metainfo::domainName)
    365                        .setVersion(protocol::HeapProfiler::Metainfo::version)
    366                        .build());
    367   result.push_back(protocol::Schema::Domain::create()
    368                        .setName(protocol::Schema::Metainfo::domainName)
    369                        .setVersion(protocol::Schema::Metainfo::version)
    370                        .build());
    371   return result;
    372 }
    373 
    374 void V8InspectorSessionImpl::addInspectedObject(
    375     std::unique_ptr<V8InspectorSession::Inspectable> inspectable) {
    376   m_inspectedObjects.insert(m_inspectedObjects.begin(), std::move(inspectable));
    377   if (m_inspectedObjects.size() > kInspectedObjectBufferSize)
    378     m_inspectedObjects.resize(kInspectedObjectBufferSize);
    379 }
    380 
    381 V8InspectorSession::Inspectable* V8InspectorSessionImpl::inspectedObject(
    382     unsigned num) {
    383   if (num >= m_inspectedObjects.size()) return nullptr;
    384   return m_inspectedObjects[num].get();
    385 }
    386 
    387 void V8InspectorSessionImpl::schedulePauseOnNextStatement(
    388     const StringView& breakReason, const StringView& breakDetails) {
    389   m_debuggerAgent->schedulePauseOnNextStatement(
    390       toString16(breakReason),
    391       protocol::DictionaryValue::cast(
    392           protocol::StringUtil::parseJSON(breakDetails)));
    393 }
    394 
    395 void V8InspectorSessionImpl::cancelPauseOnNextStatement() {
    396   m_debuggerAgent->cancelPauseOnNextStatement();
    397 }
    398 
    399 void V8InspectorSessionImpl::breakProgram(const StringView& breakReason,
    400                                           const StringView& breakDetails) {
    401   m_debuggerAgent->breakProgram(
    402       toString16(breakReason),
    403       protocol::DictionaryValue::cast(
    404           protocol::StringUtil::parseJSON(breakDetails)));
    405 }
    406 
    407 void V8InspectorSessionImpl::setSkipAllPauses(bool skip) {
    408   m_debuggerAgent->setSkipAllPauses(skip);
    409 }
    410 
    411 void V8InspectorSessionImpl::resume() { m_debuggerAgent->resume(); }
    412 
    413 void V8InspectorSessionImpl::stepOver() { m_debuggerAgent->stepOver(); }
    414 
    415 std::vector<std::unique_ptr<protocol::Debugger::API::SearchMatch>>
    416 V8InspectorSessionImpl::searchInTextByLines(const StringView& text,
    417                                             const StringView& query,
    418                                             bool caseSensitive, bool isRegex) {
    419   // TODO(dgozman): search may operate on StringView and avoid copying |text|.
    420   std::vector<std::unique_ptr<protocol::Debugger::SearchMatch>> matches =
    421       searchInTextByLinesImpl(this, toString16(text), toString16(query),
    422                               caseSensitive, isRegex);
    423   std::vector<std::unique_ptr<protocol::Debugger::API::SearchMatch>> result;
    424   for (size_t i = 0; i < matches.size(); ++i)
    425     result.push_back(std::move(matches[i]));
    426   return result;
    427 }
    428 
    429 }  // namespace v8_inspector
    430