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