v8-debugger-agent-impl.cc revision 62ed631aa0ff23db68a47fd423efa9c019ff2c9e
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
29namespace v8_inspector {
30
31using protocol::Array;
32using protocol::Maybe;
33using protocol::Debugger::BreakpointId;
34using protocol::Debugger::CallFrame;
35using protocol::Runtime::ExceptionDetails;
36using protocol::Runtime::ScriptId;
37using protocol::Runtime::StackTrace;
38using protocol::Runtime::RemoteObject;
39
40namespace DebuggerAgentState {
41static const char javaScriptBreakpoints[] = "javaScriptBreakopints";
42static const char pauseOnExceptionsState[] = "pauseOnExceptionsState";
43static const char asyncCallStackDepth[] = "asyncCallStackDepth";
44static const char blackboxPattern[] = "blackboxPattern";
45static const char debuggerEnabled[] = "debuggerEnabled";
46
47// Breakpoint properties.
48static const char url[] = "url";
49static const char isRegex[] = "isRegex";
50static const char lineNumber[] = "lineNumber";
51static const char columnNumber[] = "columnNumber";
52static const char condition[] = "condition";
53static const char skipAllPauses[] = "skipAllPauses";
54
55}  // namespace DebuggerAgentState
56
57static const char kBacktraceObjectGroup[] = "backtrace";
58static const char kDebuggerNotEnabled[] = "Debugger agent is not enabled";
59static const char kDebuggerNotPaused[] =
60    "Can only perform operation while paused.";
61
62namespace {
63
64void 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
83String16 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
95String16 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
107bool 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
113std::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
124V8DebuggerAgentImpl::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
139V8DebuggerAgentImpl::~V8DebuggerAgentImpl() {}
140
141void 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
156bool V8DebuggerAgentImpl::enabled() { return m_enabled; }
157
158Response 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
168Response 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
199void 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
227Response V8DebuggerAgentImpl::setBreakpointsActive(bool active) {
228  if (!enabled()) return Response::Error(kDebuggerNotEnabled);
229  m_debugger->setBreakpointsActivated(active);
230  return Response::OK();
231}
232
233Response V8DebuggerAgentImpl::setSkipAllPauses(bool skip) {
234  m_state->setBoolean(DebuggerAgentState::skipAllPauses, skip);
235  m_skipAllPauses = skip;
236  return Response::OK();
237}
238
239static std::unique_ptr<protocol::DictionaryValue>
240buildObjectForBreakpointCookie(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
253static 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
262Response 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
314Response 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
335Response 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
344void 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
362Response 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
404Response 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
422bool 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
456std::unique_ptr<protocol::Debugger::Location>
457V8DebuggerAgentImpl::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
497Response 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
516Response 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
549Response 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
579Response 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
590void 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
596void V8DebuggerAgentImpl::popBreakDetails() {
597  if (m_breakReason.empty()) return;
598  m_breakReason.pop_back();
599}
600
601void V8DebuggerAgentImpl::clearBreakDetails() {
602  std::vector<BreakReason> emptyBreakReason;
603  m_breakReason.swap(emptyBreakReason);
604}
605
606void 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
617void V8DebuggerAgentImpl::schedulePauseOnNextStatementIfSteppingInto() {
618  DCHECK(enabled());
619  if (m_scheduledDebuggerStep != StepInto || m_javaScriptPauseScheduled ||
620      isPaused())
621    return;
622  m_debugger->setPauseOnNextStatement(true);
623}
624
625void V8DebuggerAgentImpl::cancelPauseOnNextStatement() {
626  if (m_javaScriptPauseScheduled || isPaused()) return;
627  popBreakDetails();
628  if (m_breakReason.empty()) m_debugger->setPauseOnNextStatement(false);
629}
630
631Response 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
641Response 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
649Response 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
661Response 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
669Response 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
678Response 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
696void V8DebuggerAgentImpl::setPauseOnExceptionsImpl(int pauseState) {
697  m_debugger->setPauseOnExceptionsState(
698      static_cast<v8::debug::ExceptionBreakState>(pauseState));
699  m_state->setInteger(DebuggerAgentState::pauseOnExceptionsState, pauseState);
700}
701
702Response 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
734Response 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
759Response 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
766Response 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
791Response 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
800void V8DebuggerAgentImpl::resetBlackboxedStateCache() {
801  for (const auto& it : m_scripts) {
802    it.second->resetBlackboxedStateCache();
803  }
804}
805
806Response 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
846void V8DebuggerAgentImpl::willExecuteScript(int scriptId) {
847  changeJavaScriptRecursionLevel(+1);
848  if (m_scheduledDebuggerStep != StepInto) return;
849  schedulePauseOnNextStatementIfSteppingInto();
850}
851
852void V8DebuggerAgentImpl::didExecuteScript() {
853  changeJavaScriptRecursionLevel(-1);
854}
855
856void 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
873Response 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
983std::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
990bool V8DebuggerAgentImpl::isPaused() const { return m_debugger->isPaused(); }
991
992void 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
1078void 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
1176void V8DebuggerAgentImpl::didContinue() {
1177  JavaScriptCallFrames emptyCallFrames;
1178  m_pausedCallFrames.swap(emptyCallFrames);
1179  clearBreakDetails();
1180  m_frontend.resumed();
1181}
1182
1183void 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
1196void 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
1205void 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
1214void 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
1222void 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