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