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,int sessionId,V8Inspector::Channel * channel,const StringView & state)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
V8InspectorSessionImpl(V8InspectorImpl * inspector,int contextGroupId,int sessionId,V8Inspector::Channel * channel,const StringView & savedState)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
~V8InspectorSessionImpl()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
agentState(const String16 & name)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:
create(std::unique_ptr<protocol::Serializable> message)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
string()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:
MessageBuffer(std::unique_ptr<protocol::Serializable> message)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
sendProtocolResponse(int callId,std::unique_ptr<protocol::Serializable> message)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
sendProtocolNotification(std::unique_ptr<protocol::Serializable> message)168 void V8InspectorSessionImpl::sendProtocolNotification(
169 std::unique_ptr<protocol::Serializable> message) {
170 m_channel->sendNotification(MessageBuffer::create(std::move(message)));
171 }
172
fallThrough(int callId,const String16 & method,const String16 & message)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
flushProtocolNotifications()179 void V8InspectorSessionImpl::flushProtocolNotifications() {
180 m_channel->flushProtocolNotifications();
181 }
182
reset()183 void V8InspectorSessionImpl::reset() {
184 m_debuggerAgent->reset();
185 m_runtimeAgent->reset();
186 discardInjectedScripts();
187 }
188
discardInjectedScripts()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
findInjectedScript(int contextId,InjectedScript * & injectedScript)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
findInjectedScript(RemoteObjectIdBase * objectId,InjectedScript * & injectedScript)218 Response V8InspectorSessionImpl::findInjectedScript(
219 RemoteObjectIdBase* objectId, InjectedScript*& injectedScript) {
220 return findInjectedScript(objectId->contextId(), injectedScript);
221 }
222
releaseObjectGroup(const StringView & objectGroup)223 void V8InspectorSessionImpl::releaseObjectGroup(const StringView& objectGroup) {
224 releaseObjectGroup(toString16(objectGroup));
225 }
226
releaseObjectGroup(const String16 & objectGroup)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
unwrapObject(std::unique_ptr<StringBuffer> * error,const StringView & objectId,v8::Local<v8::Value> * object,v8::Local<v8::Context> * context,std::unique_ptr<StringBuffer> * objectGroup)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
unwrapObject(const String16 & objectId,v8::Local<v8::Value> * object,v8::Local<v8::Context> * context,String16 * objectGroup)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>
wrapObject(v8::Local<v8::Context> context,v8::Local<v8::Value> value,const StringView & groupName,bool generatePreview)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>
wrapObject(v8::Local<v8::Context> context,v8::Local<v8::Value> value,const String16 & groupName,bool generatePreview)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>
wrapTable(v8::Local<v8::Context> context,v8::Local<v8::Value> table,v8::Local<v8::Value> columns)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
setCustomObjectFormatterEnabled(bool enabled)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
reportAllContexts(V8RuntimeAgentImpl * agent)313 void V8InspectorSessionImpl::reportAllContexts(V8RuntimeAgentImpl* agent) {
314 m_inspector->forEachContext(m_contextGroupId,
315 [&agent](InspectedContext* context) {
316 agent->reportExecutionContextCreated(context);
317 });
318 }
319
dispatchProtocolMessage(const StringView & message)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
stateJSON()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>>
supportedDomains()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>>
supportedDomainsImpl()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
addInspectedObject(std::unique_ptr<V8InspectorSession::Inspectable> inspectable)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
inspectedObject(unsigned num)381 V8InspectorSession::Inspectable* V8InspectorSessionImpl::inspectedObject(
382 unsigned num) {
383 if (num >= m_inspectedObjects.size()) return nullptr;
384 return m_inspectedObjects[num].get();
385 }
386
schedulePauseOnNextStatement(const StringView & breakReason,const StringView & breakDetails)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
cancelPauseOnNextStatement()395 void V8InspectorSessionImpl::cancelPauseOnNextStatement() {
396 m_debuggerAgent->cancelPauseOnNextStatement();
397 }
398
breakProgram(const StringView & breakReason,const StringView & breakDetails)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
setSkipAllPauses(bool skip)407 void V8InspectorSessionImpl::setSkipAllPauses(bool skip) {
408 m_debuggerAgent->setSkipAllPauses(skip);
409 }
410
resume()411 void V8InspectorSessionImpl::resume() { m_debuggerAgent->resume(); }
412
stepOver()413 void V8InspectorSessionImpl::stepOver() { m_debuggerAgent->stepOver(); }
414
415 std::vector<std::unique_ptr<protocol::Debugger::API::SearchMatch>>
searchInTextByLines(const StringView & text,const StringView & query,bool caseSensitive,bool isRegex)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