Home | History | Annotate | Download | only in inspector
      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 
     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 
     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 
     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 
    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 
    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 
    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 
    139 V8DebuggerAgentImpl::~V8DebuggerAgentImpl() {}
    140 
    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 
    156 bool V8DebuggerAgentImpl::enabled() { return m_enabled; }
    157 
    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 
    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 
    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 
    227 Response V8DebuggerAgentImpl::setBreakpointsActive(bool active) {
    228   if (!enabled()) return Response::Error(kDebuggerNotEnabled);
    229   m_debugger->setBreakpointsActivated(active);
    230   return Response::OK();
    231 }
    232 
    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>
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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>
    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 
    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 
    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 
    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 
    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 
    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 
    596 void V8DebuggerAgentImpl::popBreakDetails() {
    597   if (m_breakReason.empty()) return;
    598   m_breakReason.pop_back();
    599 }
    600 
    601 void V8DebuggerAgentImpl::clearBreakDetails() {
    602   std::vector<BreakReason> emptyBreakReason;
    603   m_breakReason.swap(emptyBreakReason);
    604 }
    605 
    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 
    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 
    625 void V8DebuggerAgentImpl::cancelPauseOnNextStatement() {
    626   if (m_javaScriptPauseScheduled || isPaused()) return;
    627   popBreakDetails();
    628   if (m_breakReason.empty()) m_debugger->setPauseOnNextStatement(false);
    629 }
    630 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    800 void V8DebuggerAgentImpl::resetBlackboxedStateCache() {
    801   for (const auto& it : m_scripts) {
    802     it.second->resetBlackboxedStateCache();
    803   }
    804 }
    805 
    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 
    846 void V8DebuggerAgentImpl::willExecuteScript(int scriptId) {
    847   changeJavaScriptRecursionLevel(+1);
    848   if (m_scheduledDebuggerStep != StepInto) return;
    849   schedulePauseOnNextStatementIfSteppingInto();
    850 }
    851 
    852 void V8DebuggerAgentImpl::didExecuteScript() {
    853   changeJavaScriptRecursionLevel(-1);
    854 }
    855 
    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 
    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 
    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 
    990 bool V8DebuggerAgentImpl::isPaused() const { return m_debugger->isPaused(); }
    991 
    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 
   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 
   1176 void V8DebuggerAgentImpl::didContinue() {
   1177   JavaScriptCallFrames emptyCallFrames;
   1178   m_pausedCallFrames.swap(emptyCallFrames);
   1179   clearBreakDetails();
   1180   m_frontend.resumed();
   1181 }
   1182 
   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 
   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 
   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 
   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 
   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