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