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