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