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
canDispatchMethod(const StringView & method)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
executionContextId(v8::Local<v8::Context> context)41 int V8ContextInfo::executionContextId(v8::Local<v8::Context> context) {
42 return InspectedContext::contextId(context);
43 }
44
create(V8InspectorImpl * inspector,int contextGroupId,V8Inspector::Channel * channel,const StringView & state)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
V8InspectorSessionImpl(V8InspectorImpl * inspector,int contextGroupId,V8Inspector::Channel * channel,const StringView & savedState)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
~V8InspectorSessionImpl()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
agentState(const String16 & name)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:
create(std::unique_ptr<protocol::Serializable> message)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
string()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:
MessageBuffer(std::unique_ptr<protocol::Serializable> message)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
sendProtocolResponse(int callId,std::unique_ptr<protocol::Serializable> message)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
sendProtocolNotification(std::unique_ptr<protocol::Serializable> message)167 void V8InspectorSessionImpl::sendProtocolNotification(
168 std::unique_ptr<protocol::Serializable> message) {
169 m_channel->sendNotification(MessageBuffer::create(std::move(message)));
170 }
171
flushProtocolNotifications()172 void V8InspectorSessionImpl::flushProtocolNotifications() {
173 m_channel->flushProtocolNotifications();
174 }
175
reset()176 void V8InspectorSessionImpl::reset() {
177 m_debuggerAgent->reset();
178 m_runtimeAgent->reset();
179 discardInjectedScripts();
180 }
181
discardInjectedScripts()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
findInjectedScript(int contextId,InjectedScript * & injectedScript)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
findInjectedScript(RemoteObjectIdBase * objectId,InjectedScript * & injectedScript)227 Response V8InspectorSessionImpl::findInjectedScript(
228 RemoteObjectIdBase* objectId, InjectedScript*& injectedScript) {
229 return findInjectedScript(objectId->contextId(), injectedScript);
230 }
231
releaseObjectGroup(const StringView & objectGroup)232 void V8InspectorSessionImpl::releaseObjectGroup(const StringView& objectGroup) {
233 releaseObjectGroup(toString16(objectGroup));
234 }
235
releaseObjectGroup(const String16 & objectGroup)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
unwrapObject(std::unique_ptr<StringBuffer> * error,const StringView & objectId,v8::Local<v8::Value> * object,v8::Local<v8::Context> * context,std::unique_ptr<StringBuffer> * objectGroup)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
unwrapObject(const String16 & objectId,v8::Local<v8::Value> * object,v8::Local<v8::Context> * context,String16 * objectGroup)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>
wrapObject(v8::Local<v8::Context> context,v8::Local<v8::Value> value,const StringView & groupName)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>
wrapObject(v8::Local<v8::Context> context,v8::Local<v8::Value> value,const String16 & groupName,bool generatePreview)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>
wrapTable(v8::Local<v8::Context> context,v8::Local<v8::Value> table,v8::Local<v8::Value> columns)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
setCustomObjectFormatterEnabled(bool enabled)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
reportAllContexts(V8RuntimeAgentImpl * agent)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
dispatchProtocolMessage(const StringView & message)340 void V8InspectorSessionImpl::dispatchProtocolMessage(
341 const StringView& message) {
342 m_dispatcher.dispatch(protocol::StringUtil::parseJSON(message));
343 }
344
stateJSON()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>>
supportedDomains()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>>
supportedDomainsImpl()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
addInspectedObject(std::unique_ptr<V8InspectorSession::Inspectable> inspectable)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
inspectedObject(unsigned num)393 V8InspectorSession::Inspectable* V8InspectorSessionImpl::inspectedObject(
394 unsigned num) {
395 if (num >= m_inspectedObjects.size()) return nullptr;
396 return m_inspectedObjects[num].get();
397 }
398
schedulePauseOnNextStatement(const StringView & breakReason,const StringView & breakDetails)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
cancelPauseOnNextStatement()407 void V8InspectorSessionImpl::cancelPauseOnNextStatement() {
408 m_debuggerAgent->cancelPauseOnNextStatement();
409 }
410
breakProgram(const StringView & breakReason,const StringView & breakDetails)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
setSkipAllPauses(bool skip)419 void V8InspectorSessionImpl::setSkipAllPauses(bool skip) {
420 m_debuggerAgent->setSkipAllPauses(skip);
421 }
422
resume()423 void V8InspectorSessionImpl::resume() { m_debuggerAgent->resume(); }
424
stepOver()425 void V8InspectorSessionImpl::stepOver() { m_debuggerAgent->stepOver(); }
426
427 std::vector<std::unique_ptr<protocol::Debugger::API::SearchMatch>>
searchInTextByLines(const StringView & text,const StringView & query,bool caseSensitive,bool isRegex)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