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,
     47     V8Inspector::Channel* channel, const StringView& state) {
     48   return std::unique_ptr<V8InspectorSessionImpl>(
     49       new V8InspectorSessionImpl(inspector, contextGroupId, channel, state));
     50 }
     51 
     52 V8InspectorSessionImpl::V8InspectorSessionImpl(V8InspectorImpl* inspector,
     53                                                int contextGroupId,
     54                                                V8Inspector::Channel* channel,
     55                                                const StringView& savedState)
     56     : m_contextGroupId(contextGroupId),
     57       m_inspector(inspector),
     58       m_channel(channel),
     59       m_customObjectFormatterEnabled(false),
     60       m_dispatcher(this),
     61       m_state(nullptr),
     62       m_runtimeAgent(nullptr),
     63       m_debuggerAgent(nullptr),
     64       m_heapProfilerAgent(nullptr),
     65       m_profilerAgent(nullptr),
     66       m_consoleAgent(nullptr),
     67       m_schemaAgent(nullptr) {
     68   if (savedState.length()) {
     69     std::unique_ptr<protocol::Value> state =
     70         protocol::StringUtil::parseJSON(toString16(savedState));
     71     if (state) m_state = protocol::DictionaryValue::cast(std::move(state));
     72     if (!m_state) m_state = protocol::DictionaryValue::create();
     73   } else {
     74     m_state = protocol::DictionaryValue::create();
     75   }
     76 
     77   m_runtimeAgent.reset(new V8RuntimeAgentImpl(
     78       this, this, agentState(protocol::Runtime::Metainfo::domainName)));
     79   protocol::Runtime::Dispatcher::wire(&m_dispatcher, m_runtimeAgent.get());
     80 
     81   m_debuggerAgent.reset(new V8DebuggerAgentImpl(
     82       this, this, agentState(protocol::Debugger::Metainfo::domainName)));
     83   protocol::Debugger::Dispatcher::wire(&m_dispatcher, m_debuggerAgent.get());
     84 
     85   m_profilerAgent.reset(new V8ProfilerAgentImpl(
     86       this, this, agentState(protocol::Profiler::Metainfo::domainName)));
     87   protocol::Profiler::Dispatcher::wire(&m_dispatcher, m_profilerAgent.get());
     88 
     89   m_heapProfilerAgent.reset(new V8HeapProfilerAgentImpl(
     90       this, this, agentState(protocol::HeapProfiler::Metainfo::domainName)));
     91   protocol::HeapProfiler::Dispatcher::wire(&m_dispatcher,
     92                                            m_heapProfilerAgent.get());
     93 
     94   m_consoleAgent.reset(new V8ConsoleAgentImpl(
     95       this, this, agentState(protocol::Console::Metainfo::domainName)));
     96   protocol::Console::Dispatcher::wire(&m_dispatcher, m_consoleAgent.get());
     97 
     98   m_schemaAgent.reset(new V8SchemaAgentImpl(
     99       this, this, agentState(protocol::Schema::Metainfo::domainName)));
    100   protocol::Schema::Dispatcher::wire(&m_dispatcher, m_schemaAgent.get());
    101 
    102   if (savedState.length()) {
    103     m_runtimeAgent->restore();
    104     m_debuggerAgent->restore();
    105     m_heapProfilerAgent->restore();
    106     m_profilerAgent->restore();
    107     m_consoleAgent->restore();
    108   }
    109 }
    110 
    111 V8InspectorSessionImpl::~V8InspectorSessionImpl() {
    112   m_consoleAgent->disable();
    113   m_profilerAgent->disable();
    114   m_heapProfilerAgent->disable();
    115   m_debuggerAgent->disable();
    116   m_runtimeAgent->disable();
    117 
    118   discardInjectedScripts();
    119   m_inspector->disconnect(this);
    120 }
    121 
    122 protocol::DictionaryValue* V8InspectorSessionImpl::agentState(
    123     const String16& name) {
    124   protocol::DictionaryValue* state = m_state->getObject(name);
    125   if (!state) {
    126     std::unique_ptr<protocol::DictionaryValue> newState =
    127         protocol::DictionaryValue::create();
    128     state = newState.get();
    129     m_state->setObject(name, std::move(newState));
    130   }
    131   return state;
    132 }
    133 
    134 namespace {
    135 
    136 class MessageBuffer : public StringBuffer {
    137  public:
    138   static std::unique_ptr<MessageBuffer> create(
    139       std::unique_ptr<protocol::Serializable> message) {
    140     return std::unique_ptr<MessageBuffer>(
    141         new MessageBuffer(std::move(message)));
    142   }
    143 
    144   const StringView& string() override {
    145     if (!m_serialized) {
    146       m_serialized = StringBuffer::create(toStringView(m_message->serialize()));
    147       m_message.reset(nullptr);
    148     }
    149     return m_serialized->string();
    150   }
    151 
    152  private:
    153   explicit MessageBuffer(std::unique_ptr<protocol::Serializable> message)
    154       : m_message(std::move(message)) {}
    155 
    156   std::unique_ptr<protocol::Serializable> m_message;
    157   std::unique_ptr<StringBuffer> m_serialized;
    158 };
    159 
    160 }  // namespace
    161 
    162 void V8InspectorSessionImpl::sendProtocolResponse(
    163     int callId, std::unique_ptr<protocol::Serializable> message) {
    164   m_channel->sendResponse(callId, MessageBuffer::create(std::move(message)));
    165 }
    166 
    167 void V8InspectorSessionImpl::sendProtocolNotification(
    168     std::unique_ptr<protocol::Serializable> message) {
    169   m_channel->sendNotification(MessageBuffer::create(std::move(message)));
    170 }
    171 
    172 void V8InspectorSessionImpl::flushProtocolNotifications() {
    173   m_channel->flushProtocolNotifications();
    174 }
    175 
    176 void V8InspectorSessionImpl::reset() {
    177   m_debuggerAgent->reset();
    178   m_runtimeAgent->reset();
    179   discardInjectedScripts();
    180 }
    181 
    182 void V8InspectorSessionImpl::discardInjectedScripts() {
    183   m_inspectedObjects.clear();
    184   const V8InspectorImpl::ContextByIdMap* contexts =
    185       m_inspector->contextGroup(m_contextGroupId);
    186   if (!contexts) return;
    187 
    188   std::vector<int> keys;
    189   keys.reserve(contexts->size());
    190   for (auto& idContext : *contexts) keys.push_back(idContext.first);
    191   for (auto& key : keys) {
    192     contexts = m_inspector->contextGroup(m_contextGroupId);
    193     if (!contexts) continue;
    194     auto contextIt = contexts->find(key);
    195     if (contextIt != contexts->end())
    196       contextIt->second
    197           ->discardInjectedScript();  // This may destroy some contexts.
    198   }
    199 }
    200 
    201 Response V8InspectorSessionImpl::findInjectedScript(
    202     int contextId, InjectedScript*& injectedScript) {
    203   injectedScript = nullptr;
    204   if (!contextId)
    205     return Response::Error("Cannot find context with specified id");
    206 
    207   const V8InspectorImpl::ContextByIdMap* contexts =
    208       m_inspector->contextGroup(m_contextGroupId);
    209   if (!contexts)
    210     return Response::Error("Cannot find context with specified id");
    211 
    212   auto contextsIt = contexts->find(contextId);
    213   if (contextsIt == contexts->end())
    214     return Response::Error("Cannot find context with specified id");
    215 
    216   const std::unique_ptr<InspectedContext>& context = contextsIt->second;
    217   if (!context->getInjectedScript()) {
    218     if (!context->createInjectedScript())
    219       return Response::Error("Cannot access specified execution context");
    220     if (m_customObjectFormatterEnabled)
    221       context->getInjectedScript()->setCustomObjectFormatterEnabled(true);
    222   }
    223   injectedScript = context->getInjectedScript();
    224   return Response::OK();
    225 }
    226 
    227 Response V8InspectorSessionImpl::findInjectedScript(
    228     RemoteObjectIdBase* objectId, InjectedScript*& injectedScript) {
    229   return findInjectedScript(objectId->contextId(), injectedScript);
    230 }
    231 
    232 void V8InspectorSessionImpl::releaseObjectGroup(const StringView& objectGroup) {
    233   releaseObjectGroup(toString16(objectGroup));
    234 }
    235 
    236 void V8InspectorSessionImpl::releaseObjectGroup(const String16& objectGroup) {
    237   const V8InspectorImpl::ContextByIdMap* contexts =
    238       m_inspector->contextGroup(m_contextGroupId);
    239   if (!contexts) return;
    240 
    241   std::vector<int> keys;
    242   for (auto& idContext : *contexts) keys.push_back(idContext.first);
    243   for (auto& key : keys) {
    244     contexts = m_inspector->contextGroup(m_contextGroupId);
    245     if (!contexts) continue;
    246     auto contextsIt = contexts->find(key);
    247     if (contextsIt == contexts->end()) continue;
    248     InjectedScript* injectedScript = contextsIt->second->getInjectedScript();
    249     if (injectedScript)
    250       injectedScript->releaseObjectGroup(
    251           objectGroup);  // This may destroy some contexts.
    252   }
    253 }
    254 
    255 bool V8InspectorSessionImpl::unwrapObject(
    256     std::unique_ptr<StringBuffer>* error, const StringView& objectId,
    257     v8::Local<v8::Value>* object, v8::Local<v8::Context>* context,
    258     std::unique_ptr<StringBuffer>* objectGroup) {
    259   String16 objectGroupString;
    260   Response response = unwrapObject(toString16(objectId), object, context,
    261                                    objectGroup ? &objectGroupString : nullptr);
    262   if (!response.isSuccess()) {
    263     if (error) {
    264       String16 errorMessage = response.errorMessage();
    265       *error = StringBufferImpl::adopt(errorMessage);
    266     }
    267     return false;
    268   }
    269   if (objectGroup) *objectGroup = StringBufferImpl::adopt(objectGroupString);
    270   return true;
    271 }
    272 
    273 Response V8InspectorSessionImpl::unwrapObject(const String16& objectId,
    274                                               v8::Local<v8::Value>* object,
    275                                               v8::Local<v8::Context>* context,
    276                                               String16* objectGroup) {
    277   std::unique_ptr<RemoteObjectId> remoteId;
    278   Response response = RemoteObjectId::parse(objectId, &remoteId);
    279   if (!response.isSuccess()) return response;
    280   InjectedScript* injectedScript = nullptr;
    281   response = findInjectedScript(remoteId.get(), injectedScript);
    282   if (!response.isSuccess()) return response;
    283   response = injectedScript->findObject(*remoteId, object);
    284   if (!response.isSuccess()) return response;
    285   *context = injectedScript->context()->context();
    286   if (objectGroup) *objectGroup = injectedScript->objectGroupName(*remoteId);
    287   return Response::OK();
    288 }
    289 
    290 std::unique_ptr<protocol::Runtime::API::RemoteObject>
    291 V8InspectorSessionImpl::wrapObject(v8::Local<v8::Context> context,
    292                                    v8::Local<v8::Value> value,
    293                                    const StringView& groupName) {
    294   return wrapObject(context, value, toString16(groupName), false);
    295 }
    296 
    297 std::unique_ptr<protocol::Runtime::RemoteObject>
    298 V8InspectorSessionImpl::wrapObject(v8::Local<v8::Context> context,
    299                                    v8::Local<v8::Value> value,
    300                                    const String16& groupName,
    301                                    bool generatePreview) {
    302   InjectedScript* injectedScript = nullptr;
    303   findInjectedScript(InspectedContext::contextId(context), injectedScript);
    304   if (!injectedScript) return nullptr;
    305   std::unique_ptr<protocol::Runtime::RemoteObject> result;
    306   injectedScript->wrapObject(value, groupName, false, generatePreview, &result);
    307   return result;
    308 }
    309 
    310 std::unique_ptr<protocol::Runtime::RemoteObject>
    311 V8InspectorSessionImpl::wrapTable(v8::Local<v8::Context> context,
    312                                   v8::Local<v8::Value> table,
    313                                   v8::Local<v8::Value> columns) {
    314   InjectedScript* injectedScript = nullptr;
    315   findInjectedScript(InspectedContext::contextId(context), injectedScript);
    316   if (!injectedScript) return nullptr;
    317   return injectedScript->wrapTable(table, columns);
    318 }
    319 
    320 void V8InspectorSessionImpl::setCustomObjectFormatterEnabled(bool enabled) {
    321   m_customObjectFormatterEnabled = enabled;
    322   const V8InspectorImpl::ContextByIdMap* contexts =
    323       m_inspector->contextGroup(m_contextGroupId);
    324   if (!contexts) return;
    325   for (auto& idContext : *contexts) {
    326     InjectedScript* injectedScript = idContext.second->getInjectedScript();
    327     if (injectedScript)
    328       injectedScript->setCustomObjectFormatterEnabled(enabled);
    329   }
    330 }
    331 
    332 void V8InspectorSessionImpl::reportAllContexts(V8RuntimeAgentImpl* agent) {
    333   const V8InspectorImpl::ContextByIdMap* contexts =
    334       m_inspector->contextGroup(m_contextGroupId);
    335   if (!contexts) return;
    336   for (auto& idContext : *contexts)
    337     agent->reportExecutionContextCreated(idContext.second.get());
    338 }
    339 
    340 void V8InspectorSessionImpl::dispatchProtocolMessage(
    341     const StringView& message) {
    342   m_dispatcher.dispatch(protocol::StringUtil::parseJSON(message));
    343 }
    344 
    345 std::unique_ptr<StringBuffer> V8InspectorSessionImpl::stateJSON() {
    346   String16 json = m_state->serialize();
    347   return StringBufferImpl::adopt(json);
    348 }
    349 
    350 std::vector<std::unique_ptr<protocol::Schema::API::Domain>>
    351 V8InspectorSessionImpl::supportedDomains() {
    352   std::vector<std::unique_ptr<protocol::Schema::Domain>> domains =
    353       supportedDomainsImpl();
    354   std::vector<std::unique_ptr<protocol::Schema::API::Domain>> result;
    355   for (size_t i = 0; i < domains.size(); ++i)
    356     result.push_back(std::move(domains[i]));
    357   return result;
    358 }
    359 
    360 std::vector<std::unique_ptr<protocol::Schema::Domain>>
    361 V8InspectorSessionImpl::supportedDomainsImpl() {
    362   std::vector<std::unique_ptr<protocol::Schema::Domain>> result;
    363   result.push_back(protocol::Schema::Domain::create()
    364                        .setName(protocol::Runtime::Metainfo::domainName)
    365                        .setVersion(protocol::Runtime::Metainfo::version)
    366                        .build());
    367   result.push_back(protocol::Schema::Domain::create()
    368                        .setName(protocol::Debugger::Metainfo::domainName)
    369                        .setVersion(protocol::Debugger::Metainfo::version)
    370                        .build());
    371   result.push_back(protocol::Schema::Domain::create()
    372                        .setName(protocol::Profiler::Metainfo::domainName)
    373                        .setVersion(protocol::Profiler::Metainfo::version)
    374                        .build());
    375   result.push_back(protocol::Schema::Domain::create()
    376                        .setName(protocol::HeapProfiler::Metainfo::domainName)
    377                        .setVersion(protocol::HeapProfiler::Metainfo::version)
    378                        .build());
    379   result.push_back(protocol::Schema::Domain::create()
    380                        .setName(protocol::Schema::Metainfo::domainName)
    381                        .setVersion(protocol::Schema::Metainfo::version)
    382                        .build());
    383   return result;
    384 }
    385 
    386 void V8InspectorSessionImpl::addInspectedObject(
    387     std::unique_ptr<V8InspectorSession::Inspectable> inspectable) {
    388   m_inspectedObjects.insert(m_inspectedObjects.begin(), std::move(inspectable));
    389   if (m_inspectedObjects.size() > kInspectedObjectBufferSize)
    390     m_inspectedObjects.resize(kInspectedObjectBufferSize);
    391 }
    392 
    393 V8InspectorSession::Inspectable* V8InspectorSessionImpl::inspectedObject(
    394     unsigned num) {
    395   if (num >= m_inspectedObjects.size()) return nullptr;
    396   return m_inspectedObjects[num].get();
    397 }
    398 
    399 void V8InspectorSessionImpl::schedulePauseOnNextStatement(
    400     const StringView& breakReason, const StringView& breakDetails) {
    401   m_debuggerAgent->schedulePauseOnNextStatement(
    402       toString16(breakReason),
    403       protocol::DictionaryValue::cast(
    404           protocol::StringUtil::parseJSON(breakDetails)));
    405 }
    406 
    407 void V8InspectorSessionImpl::cancelPauseOnNextStatement() {
    408   m_debuggerAgent->cancelPauseOnNextStatement();
    409 }
    410 
    411 void V8InspectorSessionImpl::breakProgram(const StringView& breakReason,
    412                                           const StringView& breakDetails) {
    413   m_debuggerAgent->breakProgram(
    414       toString16(breakReason),
    415       protocol::DictionaryValue::cast(
    416           protocol::StringUtil::parseJSON(breakDetails)));
    417 }
    418 
    419 void V8InspectorSessionImpl::setSkipAllPauses(bool skip) {
    420   m_debuggerAgent->setSkipAllPauses(skip);
    421 }
    422 
    423 void V8InspectorSessionImpl::resume() { m_debuggerAgent->resume(); }
    424 
    425 void V8InspectorSessionImpl::stepOver() { m_debuggerAgent->stepOver(); }
    426 
    427 std::vector<std::unique_ptr<protocol::Debugger::API::SearchMatch>>
    428 V8InspectorSessionImpl::searchInTextByLines(const StringView& text,
    429                                             const StringView& query,
    430                                             bool caseSensitive, bool isRegex) {
    431   // TODO(dgozman): search may operate on StringView and avoid copying |text|.
    432   std::vector<std::unique_ptr<protocol::Debugger::SearchMatch>> matches =
    433       searchInTextByLinesImpl(this, toString16(text), toString16(query),
    434                               caseSensitive, isRegex);
    435   std::vector<std::unique_ptr<protocol::Debugger::API::SearchMatch>> result;
    436   for (size_t i = 0; i < matches.size(); ++i)
    437     result.push_back(std::move(matches[i]));
    438   return result;
    439 }
    440 
    441 }  // namespace v8_inspector
    442