LCOV - code coverage report
Current view: top level - src/inspector - v8-debugger-agent-impl.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 563 625 90.1 %
Date: 2017-04-26 Functions: 57 63 90.5 %

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

Generated by: LCOV version 1.10