• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 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-debugger-agent-impl.h"
6 
7 #include <algorithm>
8 
9 #include "src/debug/debug-interface.h"
10 #include "src/inspector/injected-script.h"
11 #include "src/inspector/inspected-context.h"
12 #include "src/inspector/java-script-call-frame.h"
13 #include "src/inspector/protocol/Protocol.h"
14 #include "src/inspector/remote-object-id.h"
15 #include "src/inspector/script-breakpoint.h"
16 #include "src/inspector/search-util.h"
17 #include "src/inspector/string-util.h"
18 #include "src/inspector/v8-debugger-script.h"
19 #include "src/inspector/v8-debugger.h"
20 #include "src/inspector/v8-inspector-impl.h"
21 #include "src/inspector/v8-inspector-session-impl.h"
22 #include "src/inspector/v8-regex.h"
23 #include "src/inspector/v8-runtime-agent-impl.h"
24 #include "src/inspector/v8-stack-trace-impl.h"
25 #include "src/inspector/v8-value-copier.h"
26 
27 #include "include/v8-inspector.h"
28 
29 namespace v8_inspector {
30 
31 using protocol::Array;
32 using protocol::Maybe;
33 using protocol::Debugger::BreakpointId;
34 using protocol::Debugger::CallFrame;
35 using protocol::Runtime::ExceptionDetails;
36 using protocol::Runtime::ScriptId;
37 using protocol::Runtime::StackTrace;
38 using protocol::Runtime::RemoteObject;
39 
40 namespace DebuggerAgentState {
41 static const char javaScriptBreakpoints[] = "javaScriptBreakopints";
42 static const char pauseOnExceptionsState[] = "pauseOnExceptionsState";
43 static const char asyncCallStackDepth[] = "asyncCallStackDepth";
44 static const char blackboxPattern[] = "blackboxPattern";
45 static const char debuggerEnabled[] = "debuggerEnabled";
46 
47 // Breakpoint properties.
48 static const char url[] = "url";
49 static const char isRegex[] = "isRegex";
50 static const char lineNumber[] = "lineNumber";
51 static const char columnNumber[] = "columnNumber";
52 static const char condition[] = "condition";
53 static const char skipAllPauses[] = "skipAllPauses";
54 
55 }  // namespace DebuggerAgentState
56 
57 static const char kBacktraceObjectGroup[] = "backtrace";
58 static const char kDebuggerNotEnabled[] = "Debugger agent is not enabled";
59 static const char kDebuggerNotPaused[] =
60     "Can only perform operation while paused.";
61 
62 namespace {
63 
TranslateWasmStackTraceLocations(Array<CallFrame> * stackTrace,WasmTranslation * wasmTranslation)64 void TranslateWasmStackTraceLocations(Array<CallFrame>* stackTrace,
65                                       WasmTranslation* wasmTranslation) {
66   for (size_t i = 0, e = stackTrace->length(); i != e; ++i) {
67     protocol::Debugger::Location* location = stackTrace->get(i)->getLocation();
68     String16 scriptId = location->getScriptId();
69     int lineNumber = location->getLineNumber();
70     int columnNumber = location->getColumnNumber(-1);
71 
72     if (!wasmTranslation->TranslateWasmScriptLocationToProtocolLocation(
73             &scriptId, &lineNumber, &columnNumber)) {
74       continue;
75     }
76 
77     location->setScriptId(std::move(scriptId));
78     location->setLineNumber(lineNumber);
79     location->setColumnNumber(columnNumber);
80   }
81 }
82 
breakpointIdSuffix(V8DebuggerAgentImpl::BreakpointSource source)83 String16 breakpointIdSuffix(V8DebuggerAgentImpl::BreakpointSource source) {
84   switch (source) {
85     case V8DebuggerAgentImpl::UserBreakpointSource:
86       break;
87     case V8DebuggerAgentImpl::DebugCommandBreakpointSource:
88       return ":debug";
89     case V8DebuggerAgentImpl::MonitorCommandBreakpointSource:
90       return ":monitor";
91   }
92   return String16();
93 }
94 
generateBreakpointId(const ScriptBreakpoint & breakpoint,V8DebuggerAgentImpl::BreakpointSource source)95 String16 generateBreakpointId(const ScriptBreakpoint& breakpoint,
96                               V8DebuggerAgentImpl::BreakpointSource source) {
97   String16Builder builder;
98   builder.append(breakpoint.script_id);
99   builder.append(':');
100   builder.appendNumber(breakpoint.line_number);
101   builder.append(':');
102   builder.appendNumber(breakpoint.column_number);
103   builder.append(breakpointIdSuffix(source));
104   return builder.toString();
105 }
106 
positionComparator(const std::pair<int,int> & a,const std::pair<int,int> & b)107 bool positionComparator(const std::pair<int, int>& a,
108                         const std::pair<int, int>& b) {
109   if (a.first != b.first) return a.first < b.first;
110   return a.second < b.second;
111 }
112 
buildProtocolLocation(const String16 & scriptId,int lineNumber,int columnNumber)113 std::unique_ptr<protocol::Debugger::Location> buildProtocolLocation(
114     const String16& scriptId, int lineNumber, int columnNumber) {
115   return protocol::Debugger::Location::create()
116       .setScriptId(scriptId)
117       .setLineNumber(lineNumber)
118       .setColumnNumber(columnNumber)
119       .build();
120 }
121 
122 }  // namespace
123 
V8DebuggerAgentImpl(V8InspectorSessionImpl * session,protocol::FrontendChannel * frontendChannel,protocol::DictionaryValue * state)124 V8DebuggerAgentImpl::V8DebuggerAgentImpl(
125     V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel,
126     protocol::DictionaryValue* state)
127     : m_inspector(session->inspector()),
128       m_debugger(m_inspector->debugger()),
129       m_session(session),
130       m_enabled(false),
131       m_state(state),
132       m_frontend(frontendChannel),
133       m_isolate(m_inspector->isolate()),
134       m_scheduledDebuggerStep(NoStep),
135       m_javaScriptPauseScheduled(false),
136       m_recursionLevelForStepOut(0) {
137 }
138 
~V8DebuggerAgentImpl()139 V8DebuggerAgentImpl::~V8DebuggerAgentImpl() {}
140 
enableImpl()141 void V8DebuggerAgentImpl::enableImpl() {
142   m_enabled = true;
143   m_state->setBoolean(DebuggerAgentState::debuggerEnabled, true);
144   m_debugger->enable();
145 
146   std::vector<std::unique_ptr<V8DebuggerScript>> compiledScripts;
147   m_debugger->getCompiledScripts(m_session->contextGroupId(), compiledScripts);
148   for (size_t i = 0; i < compiledScripts.size(); i++)
149     didParseSource(std::move(compiledScripts[i]), true);
150 
151   // FIXME(WK44513): breakpoints activated flag should be synchronized between
152   // all front-ends
153   m_debugger->setBreakpointsActivated(true);
154 }
155 
enabled()156 bool V8DebuggerAgentImpl::enabled() { return m_enabled; }
157 
enable()158 Response V8DebuggerAgentImpl::enable() {
159   if (enabled()) return Response::OK();
160 
161   if (!m_inspector->client()->canExecuteScripts(m_session->contextGroupId()))
162     return Response::Error("Script execution is prohibited");
163 
164   enableImpl();
165   return Response::OK();
166 }
167 
disable()168 Response V8DebuggerAgentImpl::disable() {
169   if (!enabled()) return Response::OK();
170 
171   m_state->setObject(DebuggerAgentState::javaScriptBreakpoints,
172                      protocol::DictionaryValue::create());
173   m_state->setInteger(DebuggerAgentState::pauseOnExceptionsState,
174                       v8::debug::NoBreakOnException);
175   m_state->setInteger(DebuggerAgentState::asyncCallStackDepth, 0);
176 
177   if (isPaused()) m_debugger->continueProgram();
178   m_debugger->disable();
179   JavaScriptCallFrames emptyCallFrames;
180   m_pausedCallFrames.swap(emptyCallFrames);
181   m_blackboxedPositions.clear();
182   m_blackboxPattern.reset();
183   resetBlackboxedStateCache();
184   m_scripts.clear();
185   m_breakpointIdToDebuggerBreakpointIds.clear();
186   m_debugger->setAsyncCallStackDepth(this, 0);
187   m_continueToLocationBreakpointId = String16();
188   clearBreakDetails();
189   m_scheduledDebuggerStep = NoStep;
190   m_javaScriptPauseScheduled = false;
191   m_skipAllPauses = false;
192   m_state->setBoolean(DebuggerAgentState::skipAllPauses, false);
193   m_state->remove(DebuggerAgentState::blackboxPattern);
194   m_enabled = false;
195   m_state->setBoolean(DebuggerAgentState::debuggerEnabled, false);
196   return Response::OK();
197 }
198 
restore()199 void V8DebuggerAgentImpl::restore() {
200   DCHECK(!m_enabled);
201   if (!m_state->booleanProperty(DebuggerAgentState::debuggerEnabled, false))
202     return;
203   if (!m_inspector->client()->canExecuteScripts(m_session->contextGroupId()))
204     return;
205 
206   enableImpl();
207 
208   int pauseState = v8::debug::NoBreakOnException;
209   m_state->getInteger(DebuggerAgentState::pauseOnExceptionsState, &pauseState);
210   setPauseOnExceptionsImpl(pauseState);
211 
212   m_skipAllPauses =
213       m_state->booleanProperty(DebuggerAgentState::skipAllPauses, false);
214 
215   int asyncCallStackDepth = 0;
216   m_state->getInteger(DebuggerAgentState::asyncCallStackDepth,
217                       &asyncCallStackDepth);
218   m_debugger->setAsyncCallStackDepth(this, asyncCallStackDepth);
219 
220   String16 blackboxPattern;
221   if (m_state->getString(DebuggerAgentState::blackboxPattern,
222                          &blackboxPattern)) {
223     setBlackboxPattern(blackboxPattern);
224   }
225 }
226 
setBreakpointsActive(bool active)227 Response V8DebuggerAgentImpl::setBreakpointsActive(bool active) {
228   if (!enabled()) return Response::Error(kDebuggerNotEnabled);
229   m_debugger->setBreakpointsActivated(active);
230   return Response::OK();
231 }
232 
setSkipAllPauses(bool skip)233 Response V8DebuggerAgentImpl::setSkipAllPauses(bool skip) {
234   m_state->setBoolean(DebuggerAgentState::skipAllPauses, skip);
235   m_skipAllPauses = skip;
236   return Response::OK();
237 }
238 
239 static std::unique_ptr<protocol::DictionaryValue>
buildObjectForBreakpointCookie(const String16 & url,int lineNumber,int columnNumber,const String16 & condition,bool isRegex)240 buildObjectForBreakpointCookie(const String16& url, int lineNumber,
241                                int columnNumber, const String16& condition,
242                                bool isRegex) {
243   std::unique_ptr<protocol::DictionaryValue> breakpointObject =
244       protocol::DictionaryValue::create();
245   breakpointObject->setString(DebuggerAgentState::url, url);
246   breakpointObject->setInteger(DebuggerAgentState::lineNumber, lineNumber);
247   breakpointObject->setInteger(DebuggerAgentState::columnNumber, columnNumber);
248   breakpointObject->setString(DebuggerAgentState::condition, condition);
249   breakpointObject->setBoolean(DebuggerAgentState::isRegex, isRegex);
250   return breakpointObject;
251 }
252 
matches(V8InspectorImpl * inspector,const String16 & url,const String16 & pattern,bool isRegex)253 static bool matches(V8InspectorImpl* inspector, const String16& url,
254                     const String16& pattern, bool isRegex) {
255   if (isRegex) {
256     V8Regex regex(inspector, pattern, true);
257     return regex.match(url) != -1;
258   }
259   return url == pattern;
260 }
261 
setBreakpointByUrl(int lineNumber,Maybe<String16> optionalURL,Maybe<String16> optionalURLRegex,Maybe<int> optionalColumnNumber,Maybe<String16> optionalCondition,String16 * outBreakpointId,std::unique_ptr<protocol::Array<protocol::Debugger::Location>> * locations)262 Response V8DebuggerAgentImpl::setBreakpointByUrl(
263     int lineNumber, Maybe<String16> optionalURL,
264     Maybe<String16> optionalURLRegex, Maybe<int> optionalColumnNumber,
265     Maybe<String16> optionalCondition, String16* outBreakpointId,
266     std::unique_ptr<protocol::Array<protocol::Debugger::Location>>* locations) {
267   *locations = Array<protocol::Debugger::Location>::create();
268   if (optionalURL.isJust() == optionalURLRegex.isJust())
269     return Response::Error("Either url or urlRegex must be specified.");
270 
271   String16 url = optionalURL.isJust() ? optionalURL.fromJust()
272                                       : optionalURLRegex.fromJust();
273   int columnNumber = 0;
274   if (optionalColumnNumber.isJust()) {
275     columnNumber = optionalColumnNumber.fromJust();
276     if (columnNumber < 0) return Response::Error("Incorrect column number");
277   }
278   String16 condition = optionalCondition.fromMaybe("");
279   bool isRegex = optionalURLRegex.isJust();
280 
281   String16 breakpointId = (isRegex ? "/" + url + "/" : url) + ":" +
282                           String16::fromInteger(lineNumber) + ":" +
283                           String16::fromInteger(columnNumber);
284   protocol::DictionaryValue* breakpointsCookie =
285       m_state->getObject(DebuggerAgentState::javaScriptBreakpoints);
286   if (!breakpointsCookie) {
287     std::unique_ptr<protocol::DictionaryValue> newValue =
288         protocol::DictionaryValue::create();
289     breakpointsCookie = newValue.get();
290     m_state->setObject(DebuggerAgentState::javaScriptBreakpoints,
291                        std::move(newValue));
292   }
293   if (breakpointsCookie->get(breakpointId))
294     return Response::Error("Breakpoint at specified location already exists.");
295 
296   breakpointsCookie->setObject(
297       breakpointId, buildObjectForBreakpointCookie(
298                         url, lineNumber, columnNumber, condition, isRegex));
299 
300   ScriptBreakpoint breakpoint(String16(), lineNumber, columnNumber, condition);
301   for (const auto& script : m_scripts) {
302     if (!matches(m_inspector, script.second->sourceURL(), url, isRegex))
303       continue;
304     breakpoint.script_id = script.first;
305     std::unique_ptr<protocol::Debugger::Location> location =
306         resolveBreakpoint(breakpointId, breakpoint, UserBreakpointSource);
307     if (location) (*locations)->addItem(std::move(location));
308   }
309 
310   *outBreakpointId = breakpointId;
311   return Response::OK();
312 }
313 
setBreakpoint(std::unique_ptr<protocol::Debugger::Location> location,Maybe<String16> optionalCondition,String16 * outBreakpointId,std::unique_ptr<protocol::Debugger::Location> * actualLocation)314 Response V8DebuggerAgentImpl::setBreakpoint(
315     std::unique_ptr<protocol::Debugger::Location> location,
316     Maybe<String16> optionalCondition, String16* outBreakpointId,
317     std::unique_ptr<protocol::Debugger::Location>* actualLocation) {
318   ScriptBreakpoint breakpoint(
319       location->getScriptId(), location->getLineNumber(),
320       location->getColumnNumber(0), optionalCondition.fromMaybe(String16()));
321 
322   String16 breakpointId =
323       generateBreakpointId(breakpoint, UserBreakpointSource);
324   if (m_breakpointIdToDebuggerBreakpointIds.find(breakpointId) !=
325       m_breakpointIdToDebuggerBreakpointIds.end()) {
326     return Response::Error("Breakpoint at specified location already exists.");
327   }
328   *actualLocation =
329       resolveBreakpoint(breakpointId, breakpoint, UserBreakpointSource);
330   if (!*actualLocation) return Response::Error("Could not resolve breakpoint");
331   *outBreakpointId = breakpointId;
332   return Response::OK();
333 }
334 
removeBreakpoint(const String16 & breakpointId)335 Response V8DebuggerAgentImpl::removeBreakpoint(const String16& breakpointId) {
336   if (!enabled()) return Response::Error(kDebuggerNotEnabled);
337   protocol::DictionaryValue* breakpointsCookie =
338       m_state->getObject(DebuggerAgentState::javaScriptBreakpoints);
339   if (breakpointsCookie) breakpointsCookie->remove(breakpointId);
340   removeBreakpointImpl(breakpointId);
341   return Response::OK();
342 }
343 
removeBreakpointImpl(const String16 & breakpointId)344 void V8DebuggerAgentImpl::removeBreakpointImpl(const String16& breakpointId) {
345   DCHECK(enabled());
346   BreakpointIdToDebuggerBreakpointIdsMap::iterator
347       debuggerBreakpointIdsIterator =
348           m_breakpointIdToDebuggerBreakpointIds.find(breakpointId);
349   if (debuggerBreakpointIdsIterator ==
350       m_breakpointIdToDebuggerBreakpointIds.end())
351     return;
352   const std::vector<String16>& ids = debuggerBreakpointIdsIterator->second;
353   for (size_t i = 0; i < ids.size(); ++i) {
354     const String16& debuggerBreakpointId = ids[i];
355 
356     m_debugger->removeBreakpoint(debuggerBreakpointId);
357     m_serverBreakpoints.erase(debuggerBreakpointId);
358   }
359   m_breakpointIdToDebuggerBreakpointIds.erase(breakpointId);
360 }
361 
getPossibleBreakpoints(std::unique_ptr<protocol::Debugger::Location> start,Maybe<protocol::Debugger::Location> end,std::unique_ptr<protocol::Array<protocol::Debugger::Location>> * locations)362 Response V8DebuggerAgentImpl::getPossibleBreakpoints(
363     std::unique_ptr<protocol::Debugger::Location> start,
364     Maybe<protocol::Debugger::Location> end,
365     std::unique_ptr<protocol::Array<protocol::Debugger::Location>>* locations) {
366   String16 scriptId = start->getScriptId();
367 
368   if (start->getLineNumber() < 0 || start->getColumnNumber(0) < 0)
369     return Response::Error(
370         "start.lineNumber and start.columnNumber should be >= 0");
371 
372   v8::debug::Location v8Start(start->getLineNumber(),
373                               start->getColumnNumber(0));
374   v8::debug::Location v8End;
375   if (end.isJust()) {
376     if (end.fromJust()->getScriptId() != scriptId)
377       return Response::Error("Locations should contain the same scriptId");
378     int line = end.fromJust()->getLineNumber();
379     int column = end.fromJust()->getColumnNumber(0);
380     if (line < 0 || column < 0)
381       return Response::Error(
382           "end.lineNumber and end.columnNumber should be >= 0");
383     v8End = v8::debug::Location(line, column);
384   }
385   auto it = m_scripts.find(scriptId);
386   if (it == m_scripts.end()) return Response::Error("Script not found");
387 
388   std::vector<v8::debug::Location> v8Locations;
389   if (!it->second->getPossibleBreakpoints(v8Start, v8End, &v8Locations))
390     return Response::InternalError();
391 
392   *locations = protocol::Array<protocol::Debugger::Location>::create();
393   for (size_t i = 0; i < v8Locations.size(); ++i) {
394     (*locations)
395         ->addItem(protocol::Debugger::Location::create()
396                       .setScriptId(scriptId)
397                       .setLineNumber(v8Locations[i].GetLineNumber())
398                       .setColumnNumber(v8Locations[i].GetColumnNumber())
399                       .build());
400   }
401   return Response::OK();
402 }
403 
continueToLocation(std::unique_ptr<protocol::Debugger::Location> location)404 Response V8DebuggerAgentImpl::continueToLocation(
405     std::unique_ptr<protocol::Debugger::Location> location) {
406   if (!enabled()) return Response::Error(kDebuggerNotEnabled);
407   if (!m_continueToLocationBreakpointId.isEmpty()) {
408     m_debugger->removeBreakpoint(m_continueToLocationBreakpointId);
409     m_continueToLocationBreakpointId = "";
410   }
411 
412   ScriptBreakpoint breakpoint(location->getScriptId(),
413                               location->getLineNumber(),
414                               location->getColumnNumber(0), String16());
415 
416   m_continueToLocationBreakpointId = m_debugger->setBreakpoint(
417       breakpoint, &breakpoint.line_number, &breakpoint.column_number);
418   // TODO(kozyatinskiy): Return actual line and column number.
419   return resume();
420 }
421 
isFunctionBlackboxed(const String16 & scriptId,const v8::debug::Location & start,const v8::debug::Location & end)422 bool V8DebuggerAgentImpl::isFunctionBlackboxed(const String16& scriptId,
423                                                const v8::debug::Location& start,
424                                                const v8::debug::Location& end) {
425   ScriptsMap::iterator it = m_scripts.find(scriptId);
426   if (it == m_scripts.end()) {
427     // Unknown scripts are blackboxed.
428     return true;
429   }
430   if (m_blackboxPattern) {
431     const String16& scriptSourceURL = it->second->sourceURL();
432     if (!scriptSourceURL.isEmpty() &&
433         m_blackboxPattern->match(scriptSourceURL) != -1)
434       return true;
435   }
436   auto itBlackboxedPositions = m_blackboxedPositions.find(scriptId);
437   if (itBlackboxedPositions == m_blackboxedPositions.end()) return false;
438 
439   const std::vector<std::pair<int, int>>& ranges =
440       itBlackboxedPositions->second;
441   auto itStartRange = std::lower_bound(
442       ranges.begin(), ranges.end(),
443       std::make_pair(start.GetLineNumber(), start.GetColumnNumber()),
444       positionComparator);
445   auto itEndRange = std::lower_bound(
446       itStartRange, ranges.end(),
447       std::make_pair(end.GetLineNumber(), end.GetColumnNumber()),
448       positionComparator);
449   // Ranges array contains positions in script where blackbox state is changed.
450   // [(0,0) ... ranges[0]) isn't blackboxed, [ranges[0] ... ranges[1]) is
451   // blackboxed...
452   return itStartRange == itEndRange &&
453          std::distance(ranges.begin(), itStartRange) % 2;
454 }
455 
456 std::unique_ptr<protocol::Debugger::Location>
resolveBreakpoint(const String16 & breakpointId,const ScriptBreakpoint & breakpoint,BreakpointSource source)457 V8DebuggerAgentImpl::resolveBreakpoint(const String16& breakpointId,
458                                        const ScriptBreakpoint& breakpoint,
459                                        BreakpointSource source) {
460   v8::HandleScope handles(m_isolate);
461   DCHECK(enabled());
462   // FIXME: remove these checks once crbug.com/520702 is resolved.
463   CHECK(!breakpointId.isEmpty());
464   CHECK(!breakpoint.script_id.isEmpty());
465   ScriptsMap::iterator scriptIterator = m_scripts.find(breakpoint.script_id);
466   if (scriptIterator == m_scripts.end()) return nullptr;
467   if (breakpoint.line_number < scriptIterator->second->startLine() ||
468       scriptIterator->second->endLine() < breakpoint.line_number)
469     return nullptr;
470 
471   // Translate from protocol location to v8 location for the debugger.
472   ScriptBreakpoint translatedBreakpoint = breakpoint;
473   m_debugger->wasmTranslation()->TranslateProtocolLocationToWasmScriptLocation(
474       &translatedBreakpoint.script_id, &translatedBreakpoint.line_number,
475       &translatedBreakpoint.column_number);
476 
477   int actualLineNumber;
478   int actualColumnNumber;
479   String16 debuggerBreakpointId = m_debugger->setBreakpoint(
480       translatedBreakpoint, &actualLineNumber, &actualColumnNumber);
481   if (debuggerBreakpointId.isEmpty()) return nullptr;
482 
483   // Translate back from v8 location to protocol location for the return value.
484   m_debugger->wasmTranslation()->TranslateWasmScriptLocationToProtocolLocation(
485       &translatedBreakpoint.script_id, &actualLineNumber, &actualColumnNumber);
486 
487   m_serverBreakpoints[debuggerBreakpointId] =
488       std::make_pair(breakpointId, source);
489   CHECK(!breakpointId.isEmpty());
490 
491   m_breakpointIdToDebuggerBreakpointIds[breakpointId].push_back(
492       debuggerBreakpointId);
493   return buildProtocolLocation(translatedBreakpoint.script_id, actualLineNumber,
494                                actualColumnNumber);
495 }
496 
searchInContent(const String16 & scriptId,const String16 & query,Maybe<bool> optionalCaseSensitive,Maybe<bool> optionalIsRegex,std::unique_ptr<Array<protocol::Debugger::SearchMatch>> * results)497 Response V8DebuggerAgentImpl::searchInContent(
498     const String16& scriptId, const String16& query,
499     Maybe<bool> optionalCaseSensitive, Maybe<bool> optionalIsRegex,
500     std::unique_ptr<Array<protocol::Debugger::SearchMatch>>* results) {
501   v8::HandleScope handles(m_isolate);
502   ScriptsMap::iterator it = m_scripts.find(scriptId);
503   if (it == m_scripts.end())
504     return Response::Error("No script for id: " + scriptId);
505 
506   std::vector<std::unique_ptr<protocol::Debugger::SearchMatch>> matches =
507       searchInTextByLinesImpl(m_session, it->second->source(m_isolate), query,
508                               optionalCaseSensitive.fromMaybe(false),
509                               optionalIsRegex.fromMaybe(false));
510   *results = protocol::Array<protocol::Debugger::SearchMatch>::create();
511   for (size_t i = 0; i < matches.size(); ++i)
512     (*results)->addItem(std::move(matches[i]));
513   return Response::OK();
514 }
515 
setScriptSource(const String16 & scriptId,const String16 & newContent,Maybe<bool> dryRun,Maybe<protocol::Array<protocol::Debugger::CallFrame>> * newCallFrames,Maybe<bool> * stackChanged,Maybe<StackTrace> * asyncStackTrace,Maybe<protocol::Runtime::ExceptionDetails> * optOutCompileError)516 Response V8DebuggerAgentImpl::setScriptSource(
517     const String16& scriptId, const String16& newContent, Maybe<bool> dryRun,
518     Maybe<protocol::Array<protocol::Debugger::CallFrame>>* newCallFrames,
519     Maybe<bool>* stackChanged, Maybe<StackTrace>* asyncStackTrace,
520     Maybe<protocol::Runtime::ExceptionDetails>* optOutCompileError) {
521   if (!enabled()) return Response::Error(kDebuggerNotEnabled);
522 
523   ScriptsMap::iterator it = m_scripts.find(scriptId);
524   if (it == m_scripts.end()) {
525     return Response::Error("No script with given id found");
526   }
527   if (it->second->isModule()) {
528     // TODO(kozyatinskiy): LiveEdit should support ES6 module
529     return Response::Error("Editing module's script is not supported.");
530   }
531 
532   v8::HandleScope handles(m_isolate);
533   v8::Local<v8::String> newSource = toV8String(m_isolate, newContent);
534   bool compileError = false;
535   Response response = m_debugger->setScriptSource(
536       scriptId, newSource, dryRun.fromMaybe(false), optOutCompileError,
537       &m_pausedCallFrames, stackChanged, &compileError);
538   if (!response.isSuccess() || compileError) return response;
539 
540   it->second->setSource(newSource);
541   std::unique_ptr<Array<CallFrame>> callFrames;
542   response = currentCallFrames(&callFrames);
543   if (!response.isSuccess()) return response;
544   *newCallFrames = std::move(callFrames);
545   *asyncStackTrace = currentAsyncStackTrace();
546   return Response::OK();
547 }
548 
restartFrame(const String16 & callFrameId,std::unique_ptr<Array<CallFrame>> * newCallFrames,Maybe<StackTrace> * asyncStackTrace)549 Response V8DebuggerAgentImpl::restartFrame(
550     const String16& callFrameId,
551     std::unique_ptr<Array<CallFrame>>* newCallFrames,
552     Maybe<StackTrace>* asyncStackTrace) {
553   if (!isPaused()) return Response::Error(kDebuggerNotPaused);
554   InjectedScript::CallFrameScope scope(m_inspector, m_session->contextGroupId(),
555                                        callFrameId);
556   Response response = scope.initialize();
557   if (!response.isSuccess()) return response;
558   if (scope.frameOrdinal() >= m_pausedCallFrames.size())
559     return Response::Error("Could not find call frame with given id");
560 
561   v8::Local<v8::Value> resultValue;
562   v8::Local<v8::Boolean> result;
563   if (!m_pausedCallFrames[scope.frameOrdinal()]->restart().ToLocal(
564           &resultValue) ||
565       scope.tryCatch().HasCaught() ||
566       !resultValue->ToBoolean(scope.context()).ToLocal(&result) ||
567       !result->Value()) {
568     return Response::InternalError();
569   }
570   JavaScriptCallFrames frames = m_debugger->currentCallFrames();
571   m_pausedCallFrames.swap(frames);
572 
573   response = currentCallFrames(newCallFrames);
574   if (!response.isSuccess()) return response;
575   *asyncStackTrace = currentAsyncStackTrace();
576   return Response::OK();
577 }
578 
getScriptSource(const String16 & scriptId,String16 * scriptSource)579 Response V8DebuggerAgentImpl::getScriptSource(const String16& scriptId,
580                                               String16* scriptSource) {
581   if (!enabled()) return Response::Error(kDebuggerNotEnabled);
582   ScriptsMap::iterator it = m_scripts.find(scriptId);
583   if (it == m_scripts.end())
584     return Response::Error("No script for id: " + scriptId);
585   v8::HandleScope handles(m_isolate);
586   *scriptSource = it->second->source(m_isolate);
587   return Response::OK();
588 }
589 
pushBreakDetails(const String16 & breakReason,std::unique_ptr<protocol::DictionaryValue> breakAuxData)590 void V8DebuggerAgentImpl::pushBreakDetails(
591     const String16& breakReason,
592     std::unique_ptr<protocol::DictionaryValue> breakAuxData) {
593   m_breakReason.push_back(std::make_pair(breakReason, std::move(breakAuxData)));
594 }
595 
popBreakDetails()596 void V8DebuggerAgentImpl::popBreakDetails() {
597   if (m_breakReason.empty()) return;
598   m_breakReason.pop_back();
599 }
600 
clearBreakDetails()601 void V8DebuggerAgentImpl::clearBreakDetails() {
602   std::vector<BreakReason> emptyBreakReason;
603   m_breakReason.swap(emptyBreakReason);
604 }
605 
schedulePauseOnNextStatement(const String16 & breakReason,std::unique_ptr<protocol::DictionaryValue> data)606 void V8DebuggerAgentImpl::schedulePauseOnNextStatement(
607     const String16& breakReason,
608     std::unique_ptr<protocol::DictionaryValue> data) {
609   if (!enabled() || m_scheduledDebuggerStep == StepInto ||
610       m_javaScriptPauseScheduled || isPaused() ||
611       !m_debugger->breakpointsActivated())
612     return;
613   if (m_breakReason.empty()) m_debugger->setPauseOnNextStatement(true);
614   pushBreakDetails(breakReason, std::move(data));
615 }
616 
schedulePauseOnNextStatementIfSteppingInto()617 void V8DebuggerAgentImpl::schedulePauseOnNextStatementIfSteppingInto() {
618   DCHECK(enabled());
619   if (m_scheduledDebuggerStep != StepInto || m_javaScriptPauseScheduled ||
620       isPaused())
621     return;
622   m_debugger->setPauseOnNextStatement(true);
623 }
624 
cancelPauseOnNextStatement()625 void V8DebuggerAgentImpl::cancelPauseOnNextStatement() {
626   if (m_javaScriptPauseScheduled || isPaused()) return;
627   popBreakDetails();
628   if (m_breakReason.empty()) m_debugger->setPauseOnNextStatement(false);
629 }
630 
pause()631 Response V8DebuggerAgentImpl::pause() {
632   if (!enabled()) return Response::Error(kDebuggerNotEnabled);
633   if (m_javaScriptPauseScheduled || isPaused()) return Response::OK();
634   clearBreakDetails();
635   m_javaScriptPauseScheduled = true;
636   m_scheduledDebuggerStep = NoStep;
637   m_debugger->setPauseOnNextStatement(true);
638   return Response::OK();
639 }
640 
resume()641 Response V8DebuggerAgentImpl::resume() {
642   if (!isPaused()) return Response::Error(kDebuggerNotPaused);
643   m_scheduledDebuggerStep = NoStep;
644   m_session->releaseObjectGroup(kBacktraceObjectGroup);
645   m_debugger->continueProgram();
646   return Response::OK();
647 }
648 
stepOver()649 Response V8DebuggerAgentImpl::stepOver() {
650   if (!isPaused()) return Response::Error(kDebuggerNotPaused);
651   // StepOver at function return point should fallback to StepInto.
652   JavaScriptCallFrame* frame =
653       !m_pausedCallFrames.empty() ? m_pausedCallFrames[0].get() : nullptr;
654   if (frame && frame->isAtReturn()) return stepInto();
655   m_scheduledDebuggerStep = StepOver;
656   m_session->releaseObjectGroup(kBacktraceObjectGroup);
657   m_debugger->stepOverStatement();
658   return Response::OK();
659 }
660 
stepInto()661 Response V8DebuggerAgentImpl::stepInto() {
662   if (!isPaused()) return Response::Error(kDebuggerNotPaused);
663   m_scheduledDebuggerStep = StepInto;
664   m_session->releaseObjectGroup(kBacktraceObjectGroup);
665   m_debugger->stepIntoStatement();
666   return Response::OK();
667 }
668 
stepOut()669 Response V8DebuggerAgentImpl::stepOut() {
670   if (!isPaused()) return Response::Error(kDebuggerNotPaused);
671   m_scheduledDebuggerStep = StepOut;
672   m_recursionLevelForStepOut = 1;
673   m_session->releaseObjectGroup(kBacktraceObjectGroup);
674   m_debugger->stepOutOfFunction();
675   return Response::OK();
676 }
677 
setPauseOnExceptions(const String16 & stringPauseState)678 Response V8DebuggerAgentImpl::setPauseOnExceptions(
679     const String16& stringPauseState) {
680   if (!enabled()) return Response::Error(kDebuggerNotEnabled);
681   v8::debug::ExceptionBreakState pauseState;
682   if (stringPauseState == "none") {
683     pauseState = v8::debug::NoBreakOnException;
684   } else if (stringPauseState == "all") {
685     pauseState = v8::debug::BreakOnAnyException;
686   } else if (stringPauseState == "uncaught") {
687     pauseState = v8::debug::BreakOnUncaughtException;
688   } else {
689     return Response::Error("Unknown pause on exceptions mode: " +
690                            stringPauseState);
691   }
692   setPauseOnExceptionsImpl(pauseState);
693   return Response::OK();
694 }
695 
setPauseOnExceptionsImpl(int pauseState)696 void V8DebuggerAgentImpl::setPauseOnExceptionsImpl(int pauseState) {
697   m_debugger->setPauseOnExceptionsState(
698       static_cast<v8::debug::ExceptionBreakState>(pauseState));
699   m_state->setInteger(DebuggerAgentState::pauseOnExceptionsState, pauseState);
700 }
701 
evaluateOnCallFrame(const String16 & callFrameId,const String16 & expression,Maybe<String16> objectGroup,Maybe<bool> includeCommandLineAPI,Maybe<bool> silent,Maybe<bool> returnByValue,Maybe<bool> generatePreview,Maybe<bool> throwOnSideEffect,std::unique_ptr<RemoteObject> * result,Maybe<protocol::Runtime::ExceptionDetails> * exceptionDetails)702 Response V8DebuggerAgentImpl::evaluateOnCallFrame(
703     const String16& callFrameId, const String16& expression,
704     Maybe<String16> objectGroup, Maybe<bool> includeCommandLineAPI,
705     Maybe<bool> silent, Maybe<bool> returnByValue, Maybe<bool> generatePreview,
706     Maybe<bool> throwOnSideEffect, std::unique_ptr<RemoteObject>* result,
707     Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
708   if (!isPaused()) return Response::Error(kDebuggerNotPaused);
709   InjectedScript::CallFrameScope scope(m_inspector, m_session->contextGroupId(),
710                                        callFrameId);
711   Response response = scope.initialize();
712   if (!response.isSuccess()) return response;
713   if (scope.frameOrdinal() >= m_pausedCallFrames.size())
714     return Response::Error("Could not find call frame with given id");
715 
716   if (includeCommandLineAPI.fromMaybe(false)) scope.installCommandLineAPI();
717   if (silent.fromMaybe(false)) scope.ignoreExceptionsAndMuteConsole();
718 
719   v8::MaybeLocal<v8::Value> maybeResultValue =
720       m_pausedCallFrames[scope.frameOrdinal()]->evaluate(
721           toV8String(m_isolate, expression),
722           throwOnSideEffect.fromMaybe(false));
723 
724   // Re-initialize after running client's code, as it could have destroyed
725   // context or session.
726   response = scope.initialize();
727   if (!response.isSuccess()) return response;
728   return scope.injectedScript()->wrapEvaluateResult(
729       maybeResultValue, scope.tryCatch(), objectGroup.fromMaybe(""),
730       returnByValue.fromMaybe(false), generatePreview.fromMaybe(false), result,
731       exceptionDetails);
732 }
733 
setVariableValue(int scopeNumber,const String16 & variableName,std::unique_ptr<protocol::Runtime::CallArgument> newValueArgument,const String16 & callFrameId)734 Response V8DebuggerAgentImpl::setVariableValue(
735     int scopeNumber, const String16& variableName,
736     std::unique_ptr<protocol::Runtime::CallArgument> newValueArgument,
737     const String16& callFrameId) {
738   if (!enabled()) return Response::Error(kDebuggerNotEnabled);
739   if (!isPaused()) return Response::Error(kDebuggerNotPaused);
740   InjectedScript::CallFrameScope scope(m_inspector, m_session->contextGroupId(),
741                                        callFrameId);
742   Response response = scope.initialize();
743   if (!response.isSuccess()) return response;
744   v8::Local<v8::Value> newValue;
745   response = scope.injectedScript()->resolveCallArgument(newValueArgument.get(),
746                                                          &newValue);
747   if (!response.isSuccess()) return response;
748 
749   if (scope.frameOrdinal() >= m_pausedCallFrames.size())
750     return Response::Error("Could not find call frame with given id");
751   v8::MaybeLocal<v8::Value> result =
752       m_pausedCallFrames[scope.frameOrdinal()]->setVariableValue(
753           scopeNumber, toV8String(m_isolate, variableName), newValue);
754   if (scope.tryCatch().HasCaught() || result.IsEmpty())
755     return Response::InternalError();
756   return Response::OK();
757 }
758 
setAsyncCallStackDepth(int depth)759 Response V8DebuggerAgentImpl::setAsyncCallStackDepth(int depth) {
760   if (!enabled()) return Response::Error(kDebuggerNotEnabled);
761   m_state->setInteger(DebuggerAgentState::asyncCallStackDepth, depth);
762   m_debugger->setAsyncCallStackDepth(this, depth);
763   return Response::OK();
764 }
765 
setBlackboxPatterns(std::unique_ptr<protocol::Array<String16>> patterns)766 Response V8DebuggerAgentImpl::setBlackboxPatterns(
767     std::unique_ptr<protocol::Array<String16>> patterns) {
768   if (!patterns->length()) {
769     m_blackboxPattern = nullptr;
770     resetBlackboxedStateCache();
771     m_state->remove(DebuggerAgentState::blackboxPattern);
772     return Response::OK();
773   }
774 
775   String16Builder patternBuilder;
776   patternBuilder.append('(');
777   for (size_t i = 0; i < patterns->length() - 1; ++i) {
778     patternBuilder.append(patterns->get(i));
779     patternBuilder.append("|");
780   }
781   patternBuilder.append(patterns->get(patterns->length() - 1));
782   patternBuilder.append(')');
783   String16 pattern = patternBuilder.toString();
784   Response response = setBlackboxPattern(pattern);
785   if (!response.isSuccess()) return response;
786   resetBlackboxedStateCache();
787   m_state->setString(DebuggerAgentState::blackboxPattern, pattern);
788   return Response::OK();
789 }
790 
setBlackboxPattern(const String16 & pattern)791 Response V8DebuggerAgentImpl::setBlackboxPattern(const String16& pattern) {
792   std::unique_ptr<V8Regex> regex(new V8Regex(
793       m_inspector, pattern, true /** caseSensitive */, false /** multiline */));
794   if (!regex->isValid())
795     return Response::Error("Pattern parser error: " + regex->errorMessage());
796   m_blackboxPattern = std::move(regex);
797   return Response::OK();
798 }
799 
resetBlackboxedStateCache()800 void V8DebuggerAgentImpl::resetBlackboxedStateCache() {
801   for (const auto& it : m_scripts) {
802     it.second->resetBlackboxedStateCache();
803   }
804 }
805 
setBlackboxedRanges(const String16 & scriptId,std::unique_ptr<protocol::Array<protocol::Debugger::ScriptPosition>> inPositions)806 Response V8DebuggerAgentImpl::setBlackboxedRanges(
807     const String16& scriptId,
808     std::unique_ptr<protocol::Array<protocol::Debugger::ScriptPosition>>
809         inPositions) {
810   auto it = m_scripts.find(scriptId);
811   if (it == m_scripts.end())
812     return Response::Error("No script with passed id.");
813 
814   if (!inPositions->length()) {
815     m_blackboxedPositions.erase(scriptId);
816     it->second->resetBlackboxedStateCache();
817     return Response::OK();
818   }
819 
820   std::vector<std::pair<int, int>> positions;
821   positions.reserve(inPositions->length());
822   for (size_t i = 0; i < inPositions->length(); ++i) {
823     protocol::Debugger::ScriptPosition* position = inPositions->get(i);
824     if (position->getLineNumber() < 0)
825       return Response::Error("Position missing 'line' or 'line' < 0.");
826     if (position->getColumnNumber() < 0)
827       return Response::Error("Position missing 'column' or 'column' < 0.");
828     positions.push_back(
829         std::make_pair(position->getLineNumber(), position->getColumnNumber()));
830   }
831 
832   for (size_t i = 1; i < positions.size(); ++i) {
833     if (positions[i - 1].first < positions[i].first) continue;
834     if (positions[i - 1].first == positions[i].first &&
835         positions[i - 1].second < positions[i].second)
836       continue;
837     return Response::Error(
838         "Input positions array is not sorted or contains duplicate values.");
839   }
840 
841   m_blackboxedPositions[scriptId] = positions;
842   it->second->resetBlackboxedStateCache();
843   return Response::OK();
844 }
845 
willExecuteScript(int scriptId)846 void V8DebuggerAgentImpl::willExecuteScript(int scriptId) {
847   changeJavaScriptRecursionLevel(+1);
848   if (m_scheduledDebuggerStep != StepInto) return;
849   schedulePauseOnNextStatementIfSteppingInto();
850 }
851 
didExecuteScript()852 void V8DebuggerAgentImpl::didExecuteScript() {
853   changeJavaScriptRecursionLevel(-1);
854 }
855 
changeJavaScriptRecursionLevel(int step)856 void V8DebuggerAgentImpl::changeJavaScriptRecursionLevel(int step) {
857   if (m_javaScriptPauseScheduled && !m_skipAllPauses && !isPaused()) {
858     // Do not ever loose user's pause request until we have actually paused.
859     m_debugger->setPauseOnNextStatement(true);
860   }
861   if (m_scheduledDebuggerStep == StepOut) {
862     m_recursionLevelForStepOut += step;
863     if (!m_recursionLevelForStepOut) {
864       // When StepOut crosses a task boundary (i.e. js -> c++) from where it was
865       // requested,
866       // switch stepping to step into a next JS task, as if we exited to a
867       // blackboxed framework.
868       m_scheduledDebuggerStep = StepInto;
869     }
870   }
871 }
872 
currentCallFrames(std::unique_ptr<Array<CallFrame>> * result)873 Response V8DebuggerAgentImpl::currentCallFrames(
874     std::unique_ptr<Array<CallFrame>>* result) {
875   if (!isPaused()) {
876     *result = Array<CallFrame>::create();
877     return Response::OK();
878   }
879   v8::HandleScope handles(m_isolate);
880   v8::Local<v8::Context> debuggerContext =
881       v8::debug::GetDebugContext(m_isolate);
882   v8::Context::Scope contextScope(debuggerContext);
883 
884   v8::Local<v8::Array> objects = v8::Array::New(m_isolate);
885 
886   for (size_t frameOrdinal = 0; frameOrdinal < m_pausedCallFrames.size();
887        ++frameOrdinal) {
888     const std::unique_ptr<JavaScriptCallFrame>& currentCallFrame =
889         m_pausedCallFrames[frameOrdinal];
890 
891     v8::Local<v8::Object> details;
892     if (!currentCallFrame->details().ToLocal(&details))
893       return Response::InternalError();
894 
895     int contextId = currentCallFrame->contextId();
896 
897     InjectedScript* injectedScript = nullptr;
898     if (contextId) m_session->findInjectedScript(contextId, injectedScript);
899 
900     String16 callFrameId =
901         RemoteCallFrameId::serialize(contextId, static_cast<int>(frameOrdinal));
902     if (!details
903              ->Set(debuggerContext,
904                    toV8StringInternalized(m_isolate, "callFrameId"),
905                    toV8String(m_isolate, callFrameId))
906              .FromMaybe(false)) {
907       return Response::InternalError();
908     }
909 
910     if (injectedScript) {
911       v8::Local<v8::Value> scopeChain;
912       if (!details
913                ->Get(debuggerContext,
914                      toV8StringInternalized(m_isolate, "scopeChain"))
915                .ToLocal(&scopeChain) ||
916           !scopeChain->IsArray()) {
917         return Response::InternalError();
918       }
919       v8::Local<v8::Array> scopeChainArray = scopeChain.As<v8::Array>();
920       Response response = injectedScript->wrapPropertyInArray(
921           scopeChainArray, toV8StringInternalized(m_isolate, "object"),
922           kBacktraceObjectGroup);
923       if (!response.isSuccess()) return response;
924       response = injectedScript->wrapObjectProperty(
925           details, toV8StringInternalized(m_isolate, "this"),
926           kBacktraceObjectGroup);
927       if (!response.isSuccess()) return response;
928       if (details
929               ->Has(debuggerContext,
930                     toV8StringInternalized(m_isolate, "returnValue"))
931               .FromMaybe(false)) {
932         response = injectedScript->wrapObjectProperty(
933             details, toV8StringInternalized(m_isolate, "returnValue"),
934             kBacktraceObjectGroup);
935         if (!response.isSuccess()) return response;
936       }
937     } else {
938       if (!details
939                ->Set(debuggerContext,
940                      toV8StringInternalized(m_isolate, "scopeChain"),
941                      v8::Array::New(m_isolate, 0))
942                .FromMaybe(false)) {
943         return Response::InternalError();
944       }
945       v8::Local<v8::Object> remoteObject = v8::Object::New(m_isolate);
946       if (!remoteObject
947                ->Set(debuggerContext, toV8StringInternalized(m_isolate, "type"),
948                      toV8StringInternalized(m_isolate, "undefined"))
949                .FromMaybe(false)) {
950         return Response::InternalError();
951       }
952       if (!details
953                ->Set(debuggerContext, toV8StringInternalized(m_isolate, "this"),
954                      remoteObject)
955                .FromMaybe(false)) {
956         return Response::InternalError();
957       }
958       if (!details
959                ->Delete(debuggerContext,
960                         toV8StringInternalized(m_isolate, "returnValue"))
961                .FromMaybe(false)) {
962         return Response::InternalError();
963       }
964     }
965 
966     if (!objects->Set(debuggerContext, static_cast<int>(frameOrdinal), details)
967              .FromMaybe(false)) {
968       return Response::InternalError();
969     }
970   }
971 
972   std::unique_ptr<protocol::Value> protocolValue;
973   Response response = toProtocolValue(debuggerContext, objects, &protocolValue);
974   if (!response.isSuccess()) return response;
975   protocol::ErrorSupport errorSupport;
976   *result = Array<CallFrame>::fromValue(protocolValue.get(), &errorSupport);
977   if (!*result) return Response::Error(errorSupport.errors());
978   TranslateWasmStackTraceLocations(result->get(),
979                                    m_debugger->wasmTranslation());
980   return Response::OK();
981 }
982 
currentAsyncStackTrace()983 std::unique_ptr<StackTrace> V8DebuggerAgentImpl::currentAsyncStackTrace() {
984   if (!isPaused()) return nullptr;
985   V8StackTraceImpl* stackTrace = m_debugger->currentAsyncCallChain();
986   return stackTrace ? stackTrace->buildInspectorObjectForTail(m_debugger)
987                     : nullptr;
988 }
989 
isPaused() const990 bool V8DebuggerAgentImpl::isPaused() const { return m_debugger->isPaused(); }
991 
didParseSource(std::unique_ptr<V8DebuggerScript> script,bool success)992 void V8DebuggerAgentImpl::didParseSource(
993     std::unique_ptr<V8DebuggerScript> script, bool success) {
994   v8::HandleScope handles(m_isolate);
995   String16 scriptSource = script->source(m_isolate);
996   if (!success) script->setSourceURL(findSourceURL(scriptSource, false));
997   if (!success)
998     script->setSourceMappingURL(findSourceMapURL(scriptSource, false));
999 
1000   int contextId = script->executionContextId();
1001   int contextGroupId = m_inspector->contextGroupId(contextId);
1002   InspectedContext* inspected =
1003       m_inspector->getContext(contextGroupId, contextId);
1004   std::unique_ptr<protocol::DictionaryValue> executionContextAuxData;
1005   if (inspected) {
1006     // Script reused between different groups/sessions can have a stale
1007     // execution context id.
1008     executionContextAuxData = protocol::DictionaryValue::cast(
1009         protocol::StringUtil::parseJSON(inspected->auxData()));
1010   }
1011   bool isLiveEdit = script->isLiveEdit();
1012   bool hasSourceURL = script->hasSourceURL();
1013   bool isModule = script->isModule();
1014   String16 scriptId = script->scriptId();
1015   String16 scriptURL = script->sourceURL();
1016 
1017   m_scripts[scriptId] = std::move(script);
1018 
1019   ScriptsMap::iterator scriptIterator = m_scripts.find(scriptId);
1020   DCHECK(scriptIterator != m_scripts.end());
1021   V8DebuggerScript* scriptRef = scriptIterator->second.get();
1022   // V8 could create functions for parsed scripts before reporting and asks
1023   // inspector about blackboxed state, we should reset state each time when we
1024   // make any change that change isFunctionBlackboxed output - adding parsed
1025   // script is changing.
1026   scriptRef->resetBlackboxedStateCache();
1027 
1028   Maybe<String16> sourceMapURLParam = scriptRef->sourceMappingURL();
1029   Maybe<protocol::DictionaryValue> executionContextAuxDataParam(
1030       std::move(executionContextAuxData));
1031   const bool* isLiveEditParam = isLiveEdit ? &isLiveEdit : nullptr;
1032   const bool* hasSourceURLParam = hasSourceURL ? &hasSourceURL : nullptr;
1033   const bool* isModuleParam = isModule ? &isModule : nullptr;
1034   if (success)
1035     m_frontend.scriptParsed(
1036         scriptId, scriptURL, scriptRef->startLine(), scriptRef->startColumn(),
1037         scriptRef->endLine(), scriptRef->endColumn(), contextId,
1038         scriptRef->hash(m_isolate), std::move(executionContextAuxDataParam),
1039         isLiveEditParam, std::move(sourceMapURLParam), hasSourceURLParam,
1040         isModuleParam);
1041   else
1042     m_frontend.scriptFailedToParse(
1043         scriptId, scriptURL, scriptRef->startLine(), scriptRef->startColumn(),
1044         scriptRef->endLine(), scriptRef->endColumn(), contextId,
1045         scriptRef->hash(m_isolate), std::move(executionContextAuxDataParam),
1046         std::move(sourceMapURLParam), hasSourceURLParam, isModuleParam);
1047 
1048   if (scriptURL.isEmpty() || !success) return;
1049 
1050   protocol::DictionaryValue* breakpointsCookie =
1051       m_state->getObject(DebuggerAgentState::javaScriptBreakpoints);
1052   if (!breakpointsCookie) return;
1053 
1054   for (size_t i = 0; i < breakpointsCookie->size(); ++i) {
1055     auto cookie = breakpointsCookie->at(i);
1056     protocol::DictionaryValue* breakpointObject =
1057         protocol::DictionaryValue::cast(cookie.second);
1058     bool isRegex;
1059     breakpointObject->getBoolean(DebuggerAgentState::isRegex, &isRegex);
1060     String16 url;
1061     breakpointObject->getString(DebuggerAgentState::url, &url);
1062     if (!matches(m_inspector, scriptURL, url, isRegex)) continue;
1063     ScriptBreakpoint breakpoint;
1064     breakpoint.script_id = scriptId;
1065     breakpointObject->getInteger(DebuggerAgentState::lineNumber,
1066                                  &breakpoint.line_number);
1067     breakpointObject->getInteger(DebuggerAgentState::columnNumber,
1068                                  &breakpoint.column_number);
1069     breakpointObject->getString(DebuggerAgentState::condition,
1070                                 &breakpoint.condition);
1071     std::unique_ptr<protocol::Debugger::Location> location =
1072         resolveBreakpoint(cookie.first, breakpoint, UserBreakpointSource);
1073     if (location)
1074       m_frontend.breakpointResolved(cookie.first, std::move(location));
1075   }
1076 }
1077 
didPause(int contextId,v8::Local<v8::Value> exception,const std::vector<String16> & hitBreakpoints,bool isPromiseRejection,bool isUncaught,bool isOOMBreak)1078 void V8DebuggerAgentImpl::didPause(int contextId,
1079                                    v8::Local<v8::Value> exception,
1080                                    const std::vector<String16>& hitBreakpoints,
1081                                    bool isPromiseRejection, bool isUncaught,
1082                                    bool isOOMBreak) {
1083   JavaScriptCallFrames frames = m_debugger->currentCallFrames();
1084   m_pausedCallFrames.swap(frames);
1085   v8::HandleScope handles(m_isolate);
1086 
1087   std::vector<BreakReason> hitReasons;
1088 
1089   if (isOOMBreak) {
1090     hitReasons.push_back(
1091         std::make_pair(protocol::Debugger::Paused::ReasonEnum::OOM, nullptr));
1092   } else if (!exception.IsEmpty()) {
1093     InjectedScript* injectedScript = nullptr;
1094     m_session->findInjectedScript(contextId, injectedScript);
1095     if (injectedScript) {
1096       String16 breakReason =
1097           isPromiseRejection
1098               ? protocol::Debugger::Paused::ReasonEnum::PromiseRejection
1099               : protocol::Debugger::Paused::ReasonEnum::Exception;
1100       std::unique_ptr<protocol::Runtime::RemoteObject> obj;
1101       injectedScript->wrapObject(exception, kBacktraceObjectGroup, false, false,
1102                                  &obj);
1103       std::unique_ptr<protocol::DictionaryValue> breakAuxData;
1104       if (obj) {
1105         breakAuxData = obj->toValue();
1106         breakAuxData->setBoolean("uncaught", isUncaught);
1107       } else {
1108         breakAuxData = nullptr;
1109       }
1110       hitReasons.push_back(
1111           std::make_pair(breakReason, std::move(breakAuxData)));
1112     }
1113   }
1114 
1115   std::unique_ptr<Array<String16>> hitBreakpointIds = Array<String16>::create();
1116 
1117   bool hasDebugCommandBreakpointReason = false;
1118   for (const auto& point : hitBreakpoints) {
1119     DebugServerBreakpointToBreakpointIdAndSourceMap::iterator
1120         breakpointIterator = m_serverBreakpoints.find(point);
1121     if (breakpointIterator != m_serverBreakpoints.end()) {
1122       const String16& localId = breakpointIterator->second.first;
1123       hitBreakpointIds->addItem(localId);
1124 
1125       BreakpointSource source = breakpointIterator->second.second;
1126       if (!hasDebugCommandBreakpointReason &&
1127           source == DebugCommandBreakpointSource) {
1128         hasDebugCommandBreakpointReason = true;
1129         hitReasons.push_back(std::make_pair(
1130             protocol::Debugger::Paused::ReasonEnum::DebugCommand, nullptr));
1131       }
1132     }
1133   }
1134 
1135   for (size_t i = 0; i < m_breakReason.size(); ++i) {
1136     hitReasons.push_back(std::move(m_breakReason[i]));
1137   }
1138   clearBreakDetails();
1139 
1140   String16 breakReason = protocol::Debugger::Paused::ReasonEnum::Other;
1141   std::unique_ptr<protocol::DictionaryValue> breakAuxData;
1142   if (hitReasons.size() == 1) {
1143     breakReason = hitReasons[0].first;
1144     breakAuxData = std::move(hitReasons[0].second);
1145   } else if (hitReasons.size() > 1) {
1146     breakReason = protocol::Debugger::Paused::ReasonEnum::Ambiguous;
1147     std::unique_ptr<protocol::ListValue> reasons =
1148         protocol::ListValue::create();
1149     for (size_t i = 0; i < hitReasons.size(); ++i) {
1150       std::unique_ptr<protocol::DictionaryValue> reason =
1151           protocol::DictionaryValue::create();
1152       reason->setString("reason", hitReasons[i].first);
1153       if (hitReasons[i].second)
1154         reason->setObject("auxData", std::move(hitReasons[i].second));
1155       reasons->pushValue(std::move(reason));
1156     }
1157     breakAuxData = protocol::DictionaryValue::create();
1158     breakAuxData->setArray("reasons", std::move(reasons));
1159   }
1160 
1161   std::unique_ptr<Array<CallFrame>> protocolCallFrames;
1162   Response response = currentCallFrames(&protocolCallFrames);
1163   if (!response.isSuccess()) protocolCallFrames = Array<CallFrame>::create();
1164   m_frontend.paused(std::move(protocolCallFrames), breakReason,
1165                     std::move(breakAuxData), std::move(hitBreakpointIds),
1166                     currentAsyncStackTrace());
1167   m_scheduledDebuggerStep = NoStep;
1168   m_javaScriptPauseScheduled = false;
1169 
1170   if (!m_continueToLocationBreakpointId.isEmpty()) {
1171     m_debugger->removeBreakpoint(m_continueToLocationBreakpointId);
1172     m_continueToLocationBreakpointId = "";
1173   }
1174 }
1175 
didContinue()1176 void V8DebuggerAgentImpl::didContinue() {
1177   JavaScriptCallFrames emptyCallFrames;
1178   m_pausedCallFrames.swap(emptyCallFrames);
1179   clearBreakDetails();
1180   m_frontend.resumed();
1181 }
1182 
breakProgram(const String16 & breakReason,std::unique_ptr<protocol::DictionaryValue> data)1183 void V8DebuggerAgentImpl::breakProgram(
1184     const String16& breakReason,
1185     std::unique_ptr<protocol::DictionaryValue> data) {
1186   if (!enabled() || !m_debugger->canBreakProgram() || m_skipAllPauses) return;
1187   std::vector<BreakReason> currentScheduledReason;
1188   currentScheduledReason.swap(m_breakReason);
1189   pushBreakDetails(breakReason, std::move(data));
1190   m_scheduledDebuggerStep = NoStep;
1191   m_debugger->breakProgram();
1192   popBreakDetails();
1193   m_breakReason.swap(currentScheduledReason);
1194 }
1195 
breakProgramOnException(const String16 & breakReason,std::unique_ptr<protocol::DictionaryValue> data)1196 void V8DebuggerAgentImpl::breakProgramOnException(
1197     const String16& breakReason,
1198     std::unique_ptr<protocol::DictionaryValue> data) {
1199   if (!enabled() ||
1200       m_debugger->getPauseOnExceptionsState() == v8::debug::NoBreakOnException)
1201     return;
1202   breakProgram(breakReason, std::move(data));
1203 }
1204 
setBreakpointAt(const String16 & scriptId,int lineNumber,int columnNumber,BreakpointSource source,const String16 & condition)1205 void V8DebuggerAgentImpl::setBreakpointAt(const String16& scriptId,
1206                                           int lineNumber, int columnNumber,
1207                                           BreakpointSource source,
1208                                           const String16& condition) {
1209   ScriptBreakpoint breakpoint(scriptId, lineNumber, columnNumber, condition);
1210   String16 breakpointId = generateBreakpointId(breakpoint, source);
1211   resolveBreakpoint(breakpointId, breakpoint, source);
1212 }
1213 
removeBreakpointAt(const String16 & scriptId,int lineNumber,int columnNumber,BreakpointSource source)1214 void V8DebuggerAgentImpl::removeBreakpointAt(const String16& scriptId,
1215                                              int lineNumber, int columnNumber,
1216                                              BreakpointSource source) {
1217   removeBreakpointImpl(generateBreakpointId(
1218       ScriptBreakpoint(scriptId, lineNumber, columnNumber, String16()),
1219       source));
1220 }
1221 
reset()1222 void V8DebuggerAgentImpl::reset() {
1223   if (!enabled()) return;
1224   m_scheduledDebuggerStep = NoStep;
1225   m_blackboxedPositions.clear();
1226   resetBlackboxedStateCache();
1227   m_scripts.clear();
1228   m_breakpointIdToDebuggerBreakpointIds.clear();
1229 }
1230 
1231 }  // namespace v8_inspector
1232