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