1 /* 2 * Copyright (C) 2010 Apple Inc. All rights reserved. 3 * Copyright (C) 2013 Google Inc. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 15 * its contributors may be used to endorse or promote products derived 16 * from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #include "config.h" 31 #include "core/inspector/InspectorDebuggerAgent.h" 32 #include "core/inspector/JavaScriptCallFrame.h" 33 34 #include "bindings/v8/ScriptDebugServer.h" 35 #include "bindings/v8/ScriptRegexp.h" 36 #include "bindings/v8/ScriptSourceCode.h" 37 #include "bindings/v8/ScriptValue.h" 38 #include "core/dom/Document.h" 39 #include "core/fetch/Resource.h" 40 #include "core/inspector/ContentSearchUtils.h" 41 #include "core/inspector/InjectedScriptManager.h" 42 #include "core/inspector/InspectorPageAgent.h" 43 #include "core/inspector/InspectorState.h" 44 #include "core/inspector/InstrumentingAgents.h" 45 #include "core/inspector/ScriptArguments.h" 46 #include "core/inspector/ScriptCallStack.h" 47 #include "platform/JSONValues.h" 48 #include "wtf/text/WTFString.h" 49 50 using WebCore::TypeBuilder::Array; 51 using WebCore::TypeBuilder::Debugger::BreakpointId; 52 using WebCore::TypeBuilder::Debugger::CallFrame; 53 using WebCore::TypeBuilder::Debugger::ExceptionDetails; 54 using WebCore::TypeBuilder::Debugger::FunctionDetails; 55 using WebCore::TypeBuilder::Debugger::ScriptId; 56 using WebCore::TypeBuilder::Debugger::StackTrace; 57 using WebCore::TypeBuilder::Runtime::RemoteObject; 58 59 namespace WebCore { 60 61 namespace DebuggerAgentState { 62 static const char debuggerEnabled[] = "debuggerEnabled"; 63 static const char javaScriptBreakpoints[] = "javaScriptBreakopints"; 64 static const char pauseOnExceptionsState[] = "pauseOnExceptionsState"; 65 static const char asyncCallStackDepth[] = "asyncCallStackDepth"; 66 67 // Breakpoint properties. 68 static const char url[] = "url"; 69 static const char isRegex[] = "isRegex"; 70 static const char lineNumber[] = "lineNumber"; 71 static const char columnNumber[] = "columnNumber"; 72 static const char condition[] = "condition"; 73 static const char isAnti[] = "isAnti"; 74 static const char skipStackPattern[] = "skipStackPattern"; 75 static const char skipAllPauses[] = "skipAllPauses"; 76 static const char skipAllPausesExpiresOnReload[] = "skipAllPausesExpiresOnReload"; 77 78 }; 79 80 static const int maxSkipStepInCount = 20; 81 82 const char InspectorDebuggerAgent::backtraceObjectGroup[] = "backtrace"; 83 84 static String breakpointIdSuffix(InspectorDebuggerAgent::BreakpointSource source) 85 { 86 switch (source) { 87 case InspectorDebuggerAgent::UserBreakpointSource: 88 break; 89 case InspectorDebuggerAgent::DebugCommandBreakpointSource: 90 return ":debug"; 91 case InspectorDebuggerAgent::MonitorCommandBreakpointSource: 92 return ":monitor"; 93 } 94 return String(); 95 } 96 97 static String generateBreakpointId(const String& scriptId, int lineNumber, int columnNumber, InspectorDebuggerAgent::BreakpointSource source) 98 { 99 return scriptId + ':' + String::number(lineNumber) + ':' + String::number(columnNumber) + breakpointIdSuffix(source); 100 } 101 102 InspectorDebuggerAgent::InspectorDebuggerAgent(InjectedScriptManager* injectedScriptManager) 103 : InspectorBaseAgent<InspectorDebuggerAgent>("Debugger") 104 , m_injectedScriptManager(injectedScriptManager) 105 , m_frontend(0) 106 , m_pausedScriptState(nullptr) 107 , m_javaScriptPauseScheduled(false) 108 , m_debuggerStepScheduled(false) 109 , m_pausingOnNativeEvent(false) 110 , m_listener(0) 111 , m_skippedStepInCount(0) 112 , m_skipAllPauses(false) 113 { 114 } 115 116 InspectorDebuggerAgent::~InspectorDebuggerAgent() 117 { 118 ASSERT(!m_instrumentingAgents->inspectorDebuggerAgent()); 119 } 120 121 void InspectorDebuggerAgent::init() 122 { 123 // FIXME: make breakReason optional so that there was no need to init it with "other". 124 clearBreakDetails(); 125 m_state->setLong(DebuggerAgentState::pauseOnExceptionsState, ScriptDebugServer::DontPauseOnExceptions); 126 } 127 128 void InspectorDebuggerAgent::enable() 129 { 130 m_instrumentingAgents->setInspectorDebuggerAgent(this); 131 132 startListeningScriptDebugServer(); 133 // FIXME(WK44513): breakpoints activated flag should be synchronized between all front-ends 134 scriptDebugServer().setBreakpointsActivated(true); 135 136 if (m_listener) 137 m_listener->debuggerWasEnabled(); 138 } 139 140 void InspectorDebuggerAgent::disable() 141 { 142 m_state->setObject(DebuggerAgentState::javaScriptBreakpoints, JSONObject::create()); 143 m_state->setLong(DebuggerAgentState::pauseOnExceptionsState, ScriptDebugServer::DontPauseOnExceptions); 144 m_state->setString(DebuggerAgentState::skipStackPattern, ""); 145 m_state->setLong(DebuggerAgentState::asyncCallStackDepth, 0); 146 m_instrumentingAgents->setInspectorDebuggerAgent(0); 147 148 scriptDebugServer().clearBreakpoints(); 149 scriptDebugServer().clearCompiledScripts(); 150 stopListeningScriptDebugServer(); 151 clear(); 152 153 if (m_listener) 154 m_listener->debuggerWasDisabled(); 155 156 m_skipAllPauses = false; 157 } 158 159 bool InspectorDebuggerAgent::enabled() 160 { 161 return m_state->getBoolean(DebuggerAgentState::debuggerEnabled); 162 } 163 164 void InspectorDebuggerAgent::enable(ErrorString*) 165 { 166 if (enabled()) 167 return; 168 169 enable(); 170 m_state->setBoolean(DebuggerAgentState::debuggerEnabled, true); 171 172 ASSERT(m_frontend); 173 } 174 175 void InspectorDebuggerAgent::disable(ErrorString*) 176 { 177 if (!enabled()) 178 return; 179 180 disable(); 181 m_state->setBoolean(DebuggerAgentState::debuggerEnabled, false); 182 } 183 184 static PassOwnPtr<ScriptRegexp> compileSkipCallFramePattern(String patternText) 185 { 186 if (patternText.isEmpty()) 187 return nullptr; 188 OwnPtr<ScriptRegexp> result = adoptPtr(new ScriptRegexp(patternText, TextCaseSensitive)); 189 if (!result->isValid()) 190 result.clear(); 191 return result.release(); 192 } 193 194 void InspectorDebuggerAgent::restore() 195 { 196 if (enabled()) { 197 m_frontend->globalObjectCleared(); 198 enable(); 199 long pauseState = m_state->getLong(DebuggerAgentState::pauseOnExceptionsState); 200 String error; 201 setPauseOnExceptionsImpl(&error, pauseState); 202 m_cachedSkipStackRegExp = compileSkipCallFramePattern(m_state->getString(DebuggerAgentState::skipStackPattern)); 203 m_skipAllPauses = m_state->getBoolean(DebuggerAgentState::skipAllPauses); 204 if (m_skipAllPauses && m_state->getBoolean(DebuggerAgentState::skipAllPausesExpiresOnReload)) { 205 m_skipAllPauses = false; 206 m_state->setBoolean(DebuggerAgentState::skipAllPauses, false); 207 } 208 m_asyncCallStackTracker.setAsyncCallStackDepth(m_state->getLong(DebuggerAgentState::asyncCallStackDepth)); 209 } 210 } 211 212 void InspectorDebuggerAgent::setFrontend(InspectorFrontend* frontend) 213 { 214 m_frontend = frontend->debugger(); 215 } 216 217 void InspectorDebuggerAgent::clearFrontend() 218 { 219 m_frontend = 0; 220 221 if (!enabled()) 222 return; 223 224 disable(); 225 226 // FIXME: due to m_state->mute() hack in InspectorController, debuggerEnabled is actually set to false only 227 // in InspectorState, but not in cookie. That's why after navigation debuggerEnabled will be true, 228 // but after front-end re-open it will still be false. 229 m_state->setBoolean(DebuggerAgentState::debuggerEnabled, false); 230 } 231 232 void InspectorDebuggerAgent::setBreakpointsActive(ErrorString*, bool active) 233 { 234 scriptDebugServer().setBreakpointsActivated(active); 235 } 236 237 void InspectorDebuggerAgent::setSkipAllPauses(ErrorString*, bool skipped, const bool* untilReload) 238 { 239 m_skipAllPauses = skipped; 240 bool untilReloadValue = untilReload && *untilReload; 241 m_state->setBoolean(DebuggerAgentState::skipAllPauses, m_skipAllPauses); 242 m_state->setBoolean(DebuggerAgentState::skipAllPausesExpiresOnReload, untilReloadValue); 243 } 244 245 void InspectorDebuggerAgent::pageDidCommitLoad() 246 { 247 if (m_state->getBoolean(DebuggerAgentState::skipAllPausesExpiresOnReload)) { 248 m_skipAllPauses = false; 249 m_state->setBoolean(DebuggerAgentState::skipAllPauses, m_skipAllPauses); 250 } 251 } 252 253 bool InspectorDebuggerAgent::isPaused() 254 { 255 return scriptDebugServer().isPaused(); 256 } 257 258 bool InspectorDebuggerAgent::runningNestedMessageLoop() 259 { 260 return scriptDebugServer().runningNestedMessageLoop(); 261 } 262 263 void InspectorDebuggerAgent::addMessageToConsole(MessageSource source, MessageType type) 264 { 265 if (source == ConsoleAPIMessageSource && type == AssertMessageType && scriptDebugServer().pauseOnExceptionsState() != ScriptDebugServer::DontPauseOnExceptions) 266 breakProgram(InspectorFrontend::Debugger::Reason::Assert, nullptr); 267 } 268 269 void InspectorDebuggerAgent::addMessageToConsole(MessageSource source, MessageType type, MessageLevel, const String&, PassRefPtrWillBeRawPtr<ScriptCallStack>, unsigned long) 270 { 271 addMessageToConsole(source, type); 272 } 273 274 void InspectorDebuggerAgent::addMessageToConsole(MessageSource source, MessageType type, MessageLevel, const String&, ScriptState*, PassRefPtrWillBeRawPtr<ScriptArguments>, unsigned long) 275 { 276 addMessageToConsole(source, type); 277 } 278 279 String InspectorDebuggerAgent::preprocessEventListener(LocalFrame* frame, const String& source, const String& url, const String& functionName) 280 { 281 return scriptDebugServer().preprocessEventListener(frame, source, url, functionName); 282 } 283 284 PassOwnPtr<ScriptSourceCode> InspectorDebuggerAgent::preprocess(LocalFrame* frame, const ScriptSourceCode& sourceCode) 285 { 286 return scriptDebugServer().preprocess(frame, sourceCode); 287 } 288 289 static PassRefPtr<JSONObject> buildObjectForBreakpointCookie(const String& url, int lineNumber, int columnNumber, const String& condition, bool isRegex, bool isAnti) 290 { 291 RefPtr<JSONObject> breakpointObject = JSONObject::create(); 292 breakpointObject->setString(DebuggerAgentState::url, url); 293 breakpointObject->setNumber(DebuggerAgentState::lineNumber, lineNumber); 294 breakpointObject->setNumber(DebuggerAgentState::columnNumber, columnNumber); 295 breakpointObject->setString(DebuggerAgentState::condition, condition); 296 breakpointObject->setBoolean(DebuggerAgentState::isRegex, isRegex); 297 breakpointObject->setBoolean(DebuggerAgentState::isAnti, isAnti); 298 return breakpointObject; 299 } 300 301 static bool matches(const String& url, const String& pattern, bool isRegex) 302 { 303 if (isRegex) { 304 ScriptRegexp regex(pattern, TextCaseSensitive); 305 return regex.match(url) != -1; 306 } 307 return url == pattern; 308 } 309 310 void InspectorDebuggerAgent::setBreakpointByUrl(ErrorString* errorString, int lineNumber, const String* const optionalURL, const String* const optionalURLRegex, const int* const optionalColumnNumber, const String* const optionalCondition, const bool* isAntiBreakpoint, BreakpointId* outBreakpointId, RefPtr<Array<TypeBuilder::Debugger::Location> >& locations) 311 { 312 locations = Array<TypeBuilder::Debugger::Location>::create(); 313 if (!optionalURL == !optionalURLRegex) { 314 *errorString = "Either url or urlRegex must be specified."; 315 return; 316 } 317 318 bool isAntiBreakpointValue = isAntiBreakpoint && *isAntiBreakpoint; 319 320 String url = optionalURL ? *optionalURL : *optionalURLRegex; 321 int columnNumber; 322 if (optionalColumnNumber) { 323 columnNumber = *optionalColumnNumber; 324 if (columnNumber < 0) { 325 *errorString = "Incorrect column number"; 326 return; 327 } 328 } else { 329 columnNumber = isAntiBreakpointValue ? -1 : 0; 330 } 331 String condition = optionalCondition ? *optionalCondition : ""; 332 bool isRegex = optionalURLRegex; 333 334 String breakpointId = (isRegex ? "/" + url + "/" : url) + ':' + String::number(lineNumber) + ':' + String::number(columnNumber); 335 RefPtr<JSONObject> breakpointsCookie = m_state->getObject(DebuggerAgentState::javaScriptBreakpoints); 336 if (breakpointsCookie->find(breakpointId) != breakpointsCookie->end()) { 337 *errorString = "Breakpoint at specified location already exists."; 338 return; 339 } 340 341 breakpointsCookie->setObject(breakpointId, buildObjectForBreakpointCookie(url, lineNumber, columnNumber, condition, isRegex, isAntiBreakpointValue)); 342 m_state->setObject(DebuggerAgentState::javaScriptBreakpoints, breakpointsCookie); 343 344 if (!isAntiBreakpointValue) { 345 ScriptBreakpoint breakpoint(lineNumber, columnNumber, condition); 346 for (ScriptsMap::iterator it = m_scripts.begin(); it != m_scripts.end(); ++it) { 347 if (!matches(it->value.url, url, isRegex)) 348 continue; 349 RefPtr<TypeBuilder::Debugger::Location> location = resolveBreakpoint(breakpointId, it->key, breakpoint, UserBreakpointSource); 350 if (location) 351 locations->addItem(location); 352 } 353 } 354 *outBreakpointId = breakpointId; 355 } 356 357 static bool parseLocation(ErrorString* errorString, PassRefPtr<JSONObject> location, String* scriptId, int* lineNumber, int* columnNumber) 358 { 359 if (!location->getString("scriptId", scriptId) || !location->getNumber("lineNumber", lineNumber)) { 360 // FIXME: replace with input validation. 361 *errorString = "scriptId and lineNumber are required."; 362 return false; 363 } 364 *columnNumber = 0; 365 location->getNumber("columnNumber", columnNumber); 366 return true; 367 } 368 369 void InspectorDebuggerAgent::setBreakpoint(ErrorString* errorString, const RefPtr<JSONObject>& location, const String* const optionalCondition, BreakpointId* outBreakpointId, RefPtr<TypeBuilder::Debugger::Location>& actualLocation) 370 { 371 String scriptId; 372 int lineNumber; 373 int columnNumber; 374 375 if (!parseLocation(errorString, location, &scriptId, &lineNumber, &columnNumber)) 376 return; 377 378 String condition = optionalCondition ? *optionalCondition : emptyString(); 379 380 String breakpointId = generateBreakpointId(scriptId, lineNumber, columnNumber, UserBreakpointSource); 381 if (m_breakpointIdToDebugServerBreakpointIds.find(breakpointId) != m_breakpointIdToDebugServerBreakpointIds.end()) { 382 *errorString = "Breakpoint at specified location already exists."; 383 return; 384 } 385 ScriptBreakpoint breakpoint(lineNumber, columnNumber, condition); 386 actualLocation = resolveBreakpoint(breakpointId, scriptId, breakpoint, UserBreakpointSource); 387 if (actualLocation) 388 *outBreakpointId = breakpointId; 389 else 390 *errorString = "Could not resolve breakpoint"; 391 } 392 393 void InspectorDebuggerAgent::removeBreakpoint(ErrorString*, const String& breakpointId) 394 { 395 RefPtr<JSONObject> breakpointsCookie = m_state->getObject(DebuggerAgentState::javaScriptBreakpoints); 396 JSONObject::iterator it = breakpointsCookie->find(breakpointId); 397 bool isAntibreakpoint = false; 398 if (it != breakpointsCookie->end()) { 399 RefPtr<JSONObject> breakpointObject = it->value->asObject(); 400 breakpointObject->getBoolean(DebuggerAgentState::isAnti, &isAntibreakpoint); 401 breakpointsCookie->remove(breakpointId); 402 m_state->setObject(DebuggerAgentState::javaScriptBreakpoints, breakpointsCookie); 403 } 404 405 if (!isAntibreakpoint) 406 removeBreakpoint(breakpointId); 407 } 408 409 void InspectorDebuggerAgent::removeBreakpoint(const String& breakpointId) 410 { 411 BreakpointIdToDebugServerBreakpointIdsMap::iterator debugServerBreakpointIdsIterator = m_breakpointIdToDebugServerBreakpointIds.find(breakpointId); 412 if (debugServerBreakpointIdsIterator == m_breakpointIdToDebugServerBreakpointIds.end()) 413 return; 414 for (size_t i = 0; i < debugServerBreakpointIdsIterator->value.size(); ++i) { 415 const String& debugServerBreakpointId = debugServerBreakpointIdsIterator->value[i]; 416 scriptDebugServer().removeBreakpoint(debugServerBreakpointId); 417 m_serverBreakpoints.remove(debugServerBreakpointId); 418 } 419 m_breakpointIdToDebugServerBreakpointIds.remove(debugServerBreakpointIdsIterator); 420 } 421 422 void InspectorDebuggerAgent::continueToLocation(ErrorString* errorString, const RefPtr<JSONObject>& location, const bool* interstateLocationOpt) 423 { 424 bool interstateLocation = interstateLocationOpt ? *interstateLocationOpt : false; 425 if (!m_continueToLocationBreakpointId.isEmpty()) { 426 scriptDebugServer().removeBreakpoint(m_continueToLocationBreakpointId); 427 m_continueToLocationBreakpointId = ""; 428 } 429 430 String scriptId; 431 int lineNumber; 432 int columnNumber; 433 434 if (!parseLocation(errorString, location, &scriptId, &lineNumber, &columnNumber)) 435 return; 436 437 ScriptBreakpoint breakpoint(lineNumber, columnNumber, ""); 438 m_continueToLocationBreakpointId = scriptDebugServer().setBreakpoint(scriptId, breakpoint, &lineNumber, &columnNumber, interstateLocation); 439 resume(errorString); 440 } 441 442 void InspectorDebuggerAgent::getStepInPositions(ErrorString* errorString, const String& callFrameId, RefPtr<Array<TypeBuilder::Debugger::Location> >& positions) 443 { 444 if (!isPaused() || m_currentCallStack.isEmpty()) { 445 *errorString = "Attempt to access callframe when debugger is not on pause"; 446 return; 447 } 448 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(callFrameId); 449 if (injectedScript.isEmpty()) { 450 *errorString = "Inspected frame has gone"; 451 return; 452 } 453 454 injectedScript.getStepInPositions(errorString, m_currentCallStack, callFrameId, positions); 455 } 456 457 void InspectorDebuggerAgent::getBacktrace(ErrorString* errorString, RefPtr<Array<CallFrame> >& callFrames, RefPtr<StackTrace>& asyncStackTrace) 458 { 459 if (!assertPaused(errorString)) 460 return; 461 m_currentCallStack = scriptDebugServer().currentCallFrames(); 462 callFrames = currentCallFrames(); 463 asyncStackTrace = currentAsyncStackTrace(); 464 } 465 466 String InspectorDebuggerAgent::scriptURL(JavaScriptCallFrame* frame) 467 { 468 String scriptIdString = String::number(frame->sourceID()); 469 ScriptsMap::iterator it = m_scripts.find(scriptIdString); 470 if (it == m_scripts.end()) 471 return String(); 472 return it->value.url; 473 } 474 475 ScriptDebugListener::SkipPauseRequest InspectorDebuggerAgent::shouldSkipExceptionPause() 476 { 477 // FIXME: Fast return: if (!m_cachedSkipStackRegExp && !has_any_anti_breakpoint) return ScriptDebugListener::NoSkip; 478 479 RefPtrWillBeRawPtr<JavaScriptCallFrame> topFrame = scriptDebugServer().topCallFrameNoScopes(); 480 if (!topFrame) 481 return ScriptDebugListener::NoSkip; 482 483 String topFrameScriptUrl = scriptURL(topFrame.get()); 484 if (m_cachedSkipStackRegExp && !topFrameScriptUrl.isEmpty() && m_cachedSkipStackRegExp->match(topFrameScriptUrl) != -1) 485 return ScriptDebugListener::Continue; 486 487 // Match against breakpoints. 488 if (topFrameScriptUrl.isEmpty()) 489 return ScriptDebugListener::NoSkip; 490 491 // Prepare top frame parameters. 492 int topFrameLineNumber = topFrame->line(); 493 int topFrameColumnNumber = topFrame->column(); 494 495 RefPtr<JSONObject> breakpointsCookie = m_state->getObject(DebuggerAgentState::javaScriptBreakpoints); 496 for (JSONObject::iterator it = breakpointsCookie->begin(); it != breakpointsCookie->end(); ++it) { 497 RefPtr<JSONObject> breakpointObject = it->value->asObject(); 498 bool isAntibreakpoint; 499 breakpointObject->getBoolean(DebuggerAgentState::isAnti, &isAntibreakpoint); 500 if (!isAntibreakpoint) 501 continue; 502 503 int breakLineNumber; 504 breakpointObject->getNumber(DebuggerAgentState::lineNumber, &breakLineNumber); 505 int breakColumnNumber; 506 breakpointObject->getNumber(DebuggerAgentState::columnNumber, &breakColumnNumber); 507 508 if (breakLineNumber != topFrameLineNumber) 509 continue; 510 511 if (breakColumnNumber != -1 && breakColumnNumber != topFrameColumnNumber) 512 continue; 513 514 bool isRegex; 515 breakpointObject->getBoolean(DebuggerAgentState::isRegex, &isRegex); 516 String url; 517 breakpointObject->getString(DebuggerAgentState::url, &url); 518 if (!matches(topFrameScriptUrl, url, isRegex)) 519 continue; 520 521 return ScriptDebugListener::Continue; 522 } 523 524 return ScriptDebugListener::NoSkip; 525 } 526 527 ScriptDebugListener::SkipPauseRequest InspectorDebuggerAgent::shouldSkipStepPause() 528 { 529 if (!m_cachedSkipStackRegExp) 530 return ScriptDebugListener::NoSkip; 531 532 RefPtrWillBeRawPtr<JavaScriptCallFrame> topFrame = scriptDebugServer().topCallFrameNoScopes(); 533 String scriptUrl = scriptURL(topFrame.get()); 534 if (scriptUrl.isEmpty() || m_cachedSkipStackRegExp->match(scriptUrl) == -1) 535 return ScriptDebugListener::NoSkip; 536 537 if (m_skippedStepInCount == 0) { 538 m_minFrameCountForSkip = scriptDebugServer().frameCount(); 539 m_skippedStepInCount = 1; 540 return ScriptDebugListener::StepInto; 541 } 542 543 if (m_skippedStepInCount < maxSkipStepInCount && topFrame->isAtReturn() && scriptDebugServer().frameCount() <= m_minFrameCountForSkip) 544 m_skippedStepInCount = maxSkipStepInCount; 545 546 if (m_skippedStepInCount >= maxSkipStepInCount) { 547 if (m_pausingOnNativeEvent) { 548 m_pausingOnNativeEvent = false; 549 m_skippedStepInCount = 0; 550 return ScriptDebugListener::Continue; 551 } 552 return ScriptDebugListener::StepOut; 553 } 554 555 ++m_skippedStepInCount; 556 return ScriptDebugListener::StepInto; 557 } 558 559 PassRefPtr<TypeBuilder::Debugger::Location> InspectorDebuggerAgent::resolveBreakpoint(const String& breakpointId, const String& scriptId, const ScriptBreakpoint& breakpoint, BreakpointSource source) 560 { 561 ScriptsMap::iterator scriptIterator = m_scripts.find(scriptId); 562 if (scriptIterator == m_scripts.end()) 563 return nullptr; 564 Script& script = scriptIterator->value; 565 if (breakpoint.lineNumber < script.startLine || script.endLine < breakpoint.lineNumber) 566 return nullptr; 567 568 int actualLineNumber; 569 int actualColumnNumber; 570 String debugServerBreakpointId = scriptDebugServer().setBreakpoint(scriptId, breakpoint, &actualLineNumber, &actualColumnNumber, false); 571 if (debugServerBreakpointId.isEmpty()) 572 return nullptr; 573 574 m_serverBreakpoints.set(debugServerBreakpointId, std::make_pair(breakpointId, source)); 575 576 BreakpointIdToDebugServerBreakpointIdsMap::iterator debugServerBreakpointIdsIterator = m_breakpointIdToDebugServerBreakpointIds.find(breakpointId); 577 if (debugServerBreakpointIdsIterator == m_breakpointIdToDebugServerBreakpointIds.end()) 578 m_breakpointIdToDebugServerBreakpointIds.set(breakpointId, Vector<String>()).storedValue->value.append(debugServerBreakpointId); 579 else 580 debugServerBreakpointIdsIterator->value.append(debugServerBreakpointId); 581 582 RefPtr<TypeBuilder::Debugger::Location> location = TypeBuilder::Debugger::Location::create() 583 .setScriptId(scriptId) 584 .setLineNumber(actualLineNumber); 585 location->setColumnNumber(actualColumnNumber); 586 return location; 587 } 588 589 void InspectorDebuggerAgent::searchInContent(ErrorString* error, const String& scriptId, const String& query, const bool* const optionalCaseSensitive, const bool* const optionalIsRegex, RefPtr<Array<WebCore::TypeBuilder::Page::SearchMatch> >& results) 590 { 591 bool isRegex = optionalIsRegex ? *optionalIsRegex : false; 592 bool caseSensitive = optionalCaseSensitive ? *optionalCaseSensitive : false; 593 594 ScriptsMap::iterator it = m_scripts.find(scriptId); 595 if (it != m_scripts.end()) 596 results = ContentSearchUtils::searchInTextByLines(it->value.source, query, caseSensitive, isRegex); 597 else 598 *error = "No script for id: " + scriptId; 599 } 600 601 void InspectorDebuggerAgent::setScriptSource(ErrorString* error, RefPtr<TypeBuilder::Debugger::SetScriptSourceError>& errorData, const String& scriptId, const String& newContent, const bool* const preview, RefPtr<Array<CallFrame> >& newCallFrames, RefPtr<JSONObject>& result, RefPtr<StackTrace>& asyncStackTrace) 602 { 603 bool previewOnly = preview && *preview; 604 if (!scriptDebugServer().setScriptSource(scriptId, newContent, previewOnly, error, errorData, &m_currentCallStack, &result)) 605 return; 606 newCallFrames = currentCallFrames(); 607 asyncStackTrace = currentAsyncStackTrace(); 608 } 609 610 void InspectorDebuggerAgent::restartFrame(ErrorString* errorString, const String& callFrameId, RefPtr<Array<CallFrame> >& newCallFrames, RefPtr<JSONObject>& result, RefPtr<StackTrace>& asyncStackTrace) 611 { 612 if (!isPaused() || m_currentCallStack.isEmpty()) { 613 *errorString = "Attempt to access callframe when debugger is not on pause"; 614 return; 615 } 616 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(callFrameId); 617 if (injectedScript.isEmpty()) { 618 *errorString = "Inspected frame has gone"; 619 return; 620 } 621 622 injectedScript.restartFrame(errorString, m_currentCallStack, callFrameId, &result); 623 m_currentCallStack = scriptDebugServer().currentCallFrames(); 624 newCallFrames = currentCallFrames(); 625 asyncStackTrace = currentAsyncStackTrace(); 626 } 627 628 void InspectorDebuggerAgent::getScriptSource(ErrorString* error, const String& scriptId, String* scriptSource) 629 { 630 ScriptsMap::iterator it = m_scripts.find(scriptId); 631 if (it != m_scripts.end()) 632 *scriptSource = it->value.source; 633 else 634 *error = "No script for id: " + scriptId; 635 } 636 637 void InspectorDebuggerAgent::getFunctionDetails(ErrorString* errorString, const String& functionId, RefPtr<FunctionDetails>& details) 638 { 639 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(functionId); 640 if (injectedScript.isEmpty()) { 641 *errorString = "Function object id is obsolete"; 642 return; 643 } 644 injectedScript.getFunctionDetails(errorString, functionId, &details); 645 } 646 647 void InspectorDebuggerAgent::schedulePauseOnNextStatement(InspectorFrontend::Debugger::Reason::Enum breakReason, PassRefPtr<JSONObject> data) 648 { 649 if (m_javaScriptPauseScheduled || isPaused()) 650 return; 651 m_breakReason = breakReason; 652 m_breakAuxData = data; 653 m_pausingOnNativeEvent = true; 654 scriptDebugServer().setPauseOnNextStatement(true); 655 } 656 657 void InspectorDebuggerAgent::cancelPauseOnNextStatement() 658 { 659 if (m_javaScriptPauseScheduled || isPaused()) 660 return; 661 clearBreakDetails(); 662 m_pausingOnNativeEvent = false; 663 scriptDebugServer().setPauseOnNextStatement(false); 664 } 665 666 void InspectorDebuggerAgent::didInstallTimer(ExecutionContext* context, int timerId, int timeout, bool singleShot) 667 { 668 if (m_asyncCallStackTracker.isEnabled()) 669 m_asyncCallStackTracker.didInstallTimer(context, timerId, singleShot, scriptDebugServer().currentCallFramesForAsyncStack()); 670 } 671 672 void InspectorDebuggerAgent::didRemoveTimer(ExecutionContext* context, int timerId) 673 { 674 if (m_asyncCallStackTracker.isEnabled()) 675 m_asyncCallStackTracker.didRemoveTimer(context, timerId); 676 } 677 678 bool InspectorDebuggerAgent::willFireTimer(ExecutionContext* context, int timerId) 679 { 680 if (m_asyncCallStackTracker.isEnabled()) 681 m_asyncCallStackTracker.willFireTimer(context, timerId); 682 return true; 683 } 684 685 void InspectorDebuggerAgent::didFireTimer() 686 { 687 if (m_asyncCallStackTracker.isEnabled()) 688 m_asyncCallStackTracker.didFireAsyncCall(); 689 cancelPauseOnNextStatement(); 690 } 691 692 void InspectorDebuggerAgent::didRequestAnimationFrame(Document* document, int callbackId) 693 { 694 if (m_asyncCallStackTracker.isEnabled()) 695 m_asyncCallStackTracker.didRequestAnimationFrame(document, callbackId, scriptDebugServer().currentCallFramesForAsyncStack()); 696 } 697 698 void InspectorDebuggerAgent::didCancelAnimationFrame(Document* document, int callbackId) 699 { 700 if (m_asyncCallStackTracker.isEnabled()) 701 m_asyncCallStackTracker.didCancelAnimationFrame(document, callbackId); 702 } 703 704 bool InspectorDebuggerAgent::willFireAnimationFrame(Document* document, int callbackId) 705 { 706 if (m_asyncCallStackTracker.isEnabled()) 707 m_asyncCallStackTracker.willFireAnimationFrame(document, callbackId); 708 return true; 709 } 710 711 void InspectorDebuggerAgent::didFireAnimationFrame() 712 { 713 if (m_asyncCallStackTracker.isEnabled()) 714 m_asyncCallStackTracker.didFireAsyncCall(); 715 } 716 717 void InspectorDebuggerAgent::didEnqueueEvent(EventTarget* eventTarget, Event* event) 718 { 719 if (m_asyncCallStackTracker.isEnabled()) 720 m_asyncCallStackTracker.didEnqueueEvent(eventTarget, event, scriptDebugServer().currentCallFramesForAsyncStack()); 721 } 722 723 void InspectorDebuggerAgent::didRemoveEvent(EventTarget* eventTarget, Event* event) 724 { 725 if (m_asyncCallStackTracker.isEnabled()) 726 m_asyncCallStackTracker.didRemoveEvent(eventTarget, event); 727 } 728 729 void InspectorDebuggerAgent::willHandleEvent(EventTarget* eventTarget, Event* event, EventListener* listener, bool useCapture) 730 { 731 if (m_asyncCallStackTracker.isEnabled()) 732 m_asyncCallStackTracker.willHandleEvent(eventTarget, event, listener, useCapture); 733 } 734 735 void InspectorDebuggerAgent::didHandleEvent() 736 { 737 if (m_asyncCallStackTracker.isEnabled()) 738 m_asyncCallStackTracker.didFireAsyncCall(); 739 cancelPauseOnNextStatement(); 740 } 741 742 void InspectorDebuggerAgent::willLoadXHR(XMLHttpRequest* xhr, ThreadableLoaderClient*, const AtomicString&, const KURL&, bool async, FormData*, const HTTPHeaderMap&, bool) 743 { 744 if (m_asyncCallStackTracker.isEnabled() && async) 745 m_asyncCallStackTracker.willLoadXHR(xhr, scriptDebugServer().currentCallFramesForAsyncStack()); 746 } 747 748 void InspectorDebuggerAgent::didEnqueueMutationRecord(ExecutionContext* context, MutationObserver* observer) 749 { 750 if (m_asyncCallStackTracker.isEnabled() && !m_asyncCallStackTracker.hasEnqueuedMutationRecord(context, observer)) 751 m_asyncCallStackTracker.didEnqueueMutationRecord(context, observer, scriptDebugServer().currentCallFramesForAsyncStack()); 752 } 753 754 void InspectorDebuggerAgent::didClearAllMutationRecords(ExecutionContext* context, MutationObserver* observer) 755 { 756 if (m_asyncCallStackTracker.isEnabled()) 757 m_asyncCallStackTracker.didClearAllMutationRecords(context, observer); 758 } 759 760 void InspectorDebuggerAgent::willDeliverMutationRecords(ExecutionContext* context, MutationObserver* observer) 761 { 762 if (m_asyncCallStackTracker.isEnabled()) 763 m_asyncCallStackTracker.willDeliverMutationRecords(context, observer); 764 } 765 766 void InspectorDebuggerAgent::didDeliverMutationRecords() 767 { 768 if (m_asyncCallStackTracker.isEnabled()) 769 m_asyncCallStackTracker.didFireAsyncCall(); 770 } 771 772 void InspectorDebuggerAgent::pause(ErrorString*) 773 { 774 if (m_javaScriptPauseScheduled || isPaused()) 775 return; 776 clearBreakDetails(); 777 m_javaScriptPauseScheduled = true; 778 scriptDebugServer().setPauseOnNextStatement(true); 779 } 780 781 void InspectorDebuggerAgent::resume(ErrorString* errorString) 782 { 783 if (!assertPaused(errorString)) 784 return; 785 m_debuggerStepScheduled = false; 786 m_injectedScriptManager->releaseObjectGroup(InspectorDebuggerAgent::backtraceObjectGroup); 787 scriptDebugServer().continueProgram(); 788 } 789 790 void InspectorDebuggerAgent::stepOver(ErrorString* errorString) 791 { 792 if (!assertPaused(errorString)) 793 return; 794 m_debuggerStepScheduled = true; 795 m_injectedScriptManager->releaseObjectGroup(InspectorDebuggerAgent::backtraceObjectGroup); 796 scriptDebugServer().stepOverStatement(); 797 } 798 799 void InspectorDebuggerAgent::stepInto(ErrorString* errorString) 800 { 801 if (!assertPaused(errorString)) 802 return; 803 m_debuggerStepScheduled = true; 804 m_injectedScriptManager->releaseObjectGroup(InspectorDebuggerAgent::backtraceObjectGroup); 805 scriptDebugServer().stepIntoStatement(); 806 if (m_listener) 807 m_listener->stepInto(); 808 } 809 810 void InspectorDebuggerAgent::stepOut(ErrorString* errorString) 811 { 812 if (!assertPaused(errorString)) 813 return; 814 m_debuggerStepScheduled = true; 815 m_injectedScriptManager->releaseObjectGroup(InspectorDebuggerAgent::backtraceObjectGroup); 816 scriptDebugServer().stepOutOfFunction(); 817 } 818 819 void InspectorDebuggerAgent::setPauseOnExceptions(ErrorString* errorString, const String& stringPauseState) 820 { 821 ScriptDebugServer::PauseOnExceptionsState pauseState; 822 if (stringPauseState == "none") 823 pauseState = ScriptDebugServer::DontPauseOnExceptions; 824 else if (stringPauseState == "all") 825 pauseState = ScriptDebugServer::PauseOnAllExceptions; 826 else if (stringPauseState == "uncaught") 827 pauseState = ScriptDebugServer::PauseOnUncaughtExceptions; 828 else { 829 *errorString = "Unknown pause on exceptions mode: " + stringPauseState; 830 return; 831 } 832 setPauseOnExceptionsImpl(errorString, pauseState); 833 } 834 835 void InspectorDebuggerAgent::setPauseOnExceptionsImpl(ErrorString* errorString, int pauseState) 836 { 837 scriptDebugServer().setPauseOnExceptionsState(static_cast<ScriptDebugServer::PauseOnExceptionsState>(pauseState)); 838 if (scriptDebugServer().pauseOnExceptionsState() != pauseState) 839 *errorString = "Internal error. Could not change pause on exceptions state"; 840 else 841 m_state->setLong(DebuggerAgentState::pauseOnExceptionsState, pauseState); 842 } 843 844 void InspectorDebuggerAgent::evaluateOnCallFrame(ErrorString* errorString, const String& callFrameId, const String& expression, const String* const objectGroup, const bool* const includeCommandLineAPI, const bool* const doNotPauseOnExceptionsAndMuteConsole, const bool* const returnByValue, const bool* generatePreview, RefPtr<RemoteObject>& result, TypeBuilder::OptOutput<bool>* wasThrown) 845 { 846 if (!isPaused() || m_currentCallStack.isEmpty()) { 847 *errorString = "Attempt to access callframe when debugger is not on pause"; 848 return; 849 } 850 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(callFrameId); 851 if (injectedScript.isEmpty()) { 852 *errorString = "Inspected frame has gone"; 853 return; 854 } 855 856 ScriptDebugServer::PauseOnExceptionsState previousPauseOnExceptionsState = scriptDebugServer().pauseOnExceptionsState(); 857 if (doNotPauseOnExceptionsAndMuteConsole ? *doNotPauseOnExceptionsAndMuteConsole : false) { 858 if (previousPauseOnExceptionsState != ScriptDebugServer::DontPauseOnExceptions) 859 scriptDebugServer().setPauseOnExceptionsState(ScriptDebugServer::DontPauseOnExceptions); 860 muteConsole(); 861 } 862 863 Vector<ScriptValue> asyncCallStacks; 864 const AsyncCallStackTracker::AsyncCallChain* asyncChain = m_asyncCallStackTracker.isEnabled() ? m_asyncCallStackTracker.currentAsyncCallChain() : 0; 865 if (asyncChain) { 866 const AsyncCallStackTracker::AsyncCallStackVector& callStacks = asyncChain->callStacks(); 867 asyncCallStacks.resize(callStacks.size()); 868 AsyncCallStackTracker::AsyncCallStackVector::const_iterator it = callStacks.begin(); 869 for (size_t i = 0; it != callStacks.end(); ++it, ++i) 870 asyncCallStacks[i] = (*it)->callFrames(); 871 } 872 873 injectedScript.evaluateOnCallFrame(errorString, m_currentCallStack, asyncCallStacks, callFrameId, expression, objectGroup ? *objectGroup : "", includeCommandLineAPI ? *includeCommandLineAPI : false, returnByValue ? *returnByValue : false, generatePreview ? *generatePreview : false, &result, wasThrown); 874 875 if (doNotPauseOnExceptionsAndMuteConsole ? *doNotPauseOnExceptionsAndMuteConsole : false) { 876 unmuteConsole(); 877 if (scriptDebugServer().pauseOnExceptionsState() != previousPauseOnExceptionsState) 878 scriptDebugServer().setPauseOnExceptionsState(previousPauseOnExceptionsState); 879 } 880 } 881 882 void InspectorDebuggerAgent::compileScript(ErrorString* errorString, const String& expression, const String& sourceURL, const int* executionContextId, TypeBuilder::OptOutput<ScriptId>* scriptId, RefPtr<ExceptionDetails>& exceptionDetails) 883 { 884 InjectedScript injectedScript = injectedScriptForEval(errorString, executionContextId); 885 if (injectedScript.isEmpty()) { 886 *errorString = "Inspected frame has gone"; 887 return; 888 } 889 890 String scriptIdValue; 891 String exceptionDetailsText; 892 int lineNumberValue = 0; 893 int columnNumberValue = 0; 894 RefPtrWillBeRawPtr<ScriptCallStack> stackTraceValue; 895 scriptDebugServer().compileScript(injectedScript.scriptState(), expression, sourceURL, &scriptIdValue, &exceptionDetailsText, &lineNumberValue, &columnNumberValue, &stackTraceValue); 896 if (!scriptIdValue && !exceptionDetailsText) { 897 *errorString = "Script compilation failed"; 898 return; 899 } 900 *scriptId = scriptIdValue; 901 if (!scriptIdValue.isEmpty()) 902 return; 903 904 exceptionDetails = ExceptionDetails::create().setText(exceptionDetailsText); 905 exceptionDetails->setLine(lineNumberValue); 906 exceptionDetails->setColumn(columnNumberValue); 907 if (stackTraceValue && stackTraceValue->size() > 0) 908 exceptionDetails->setStackTrace(stackTraceValue->buildInspectorArray()); 909 } 910 911 void InspectorDebuggerAgent::runScript(ErrorString* errorString, const ScriptId& scriptId, const int* executionContextId, const String* const objectGroup, const bool* const doNotPauseOnExceptionsAndMuteConsole, RefPtr<RemoteObject>& result, RefPtr<ExceptionDetails>& exceptionDetails) 912 { 913 InjectedScript injectedScript = injectedScriptForEval(errorString, executionContextId); 914 if (injectedScript.isEmpty()) { 915 *errorString = "Inspected frame has gone"; 916 return; 917 } 918 919 ScriptDebugServer::PauseOnExceptionsState previousPauseOnExceptionsState = scriptDebugServer().pauseOnExceptionsState(); 920 if (doNotPauseOnExceptionsAndMuteConsole && *doNotPauseOnExceptionsAndMuteConsole) { 921 if (previousPauseOnExceptionsState != ScriptDebugServer::DontPauseOnExceptions) 922 scriptDebugServer().setPauseOnExceptionsState(ScriptDebugServer::DontPauseOnExceptions); 923 muteConsole(); 924 } 925 926 ScriptValue value; 927 bool wasThrownValue; 928 String exceptionDetailsText; 929 int lineNumberValue = 0; 930 int columnNumberValue = 0; 931 RefPtrWillBeRawPtr<ScriptCallStack> stackTraceValue; 932 scriptDebugServer().runScript(injectedScript.scriptState(), scriptId, &value, &wasThrownValue, &exceptionDetailsText, &lineNumberValue, &columnNumberValue, &stackTraceValue); 933 if (value.isEmpty()) { 934 *errorString = "Script execution failed"; 935 return; 936 } 937 result = injectedScript.wrapObject(value, objectGroup ? *objectGroup : ""); 938 if (wasThrownValue) { 939 exceptionDetails = ExceptionDetails::create().setText(exceptionDetailsText); 940 exceptionDetails->setLine(lineNumberValue); 941 exceptionDetails->setColumn(columnNumberValue); 942 if (stackTraceValue && stackTraceValue->size() > 0) 943 exceptionDetails->setStackTrace(stackTraceValue->buildInspectorArray()); 944 } 945 946 if (doNotPauseOnExceptionsAndMuteConsole && *doNotPauseOnExceptionsAndMuteConsole) { 947 unmuteConsole(); 948 if (scriptDebugServer().pauseOnExceptionsState() != previousPauseOnExceptionsState) 949 scriptDebugServer().setPauseOnExceptionsState(previousPauseOnExceptionsState); 950 } 951 } 952 953 void InspectorDebuggerAgent::setOverlayMessage(ErrorString*, const String*) 954 { 955 } 956 957 void InspectorDebuggerAgent::setVariableValue(ErrorString* errorString, int scopeNumber, const String& variableName, const RefPtr<JSONObject>& newValue, const String* callFrameId, const String* functionObjectId) 958 { 959 InjectedScript injectedScript; 960 if (callFrameId) { 961 if (!isPaused() || m_currentCallStack.isEmpty()) { 962 *errorString = "Attempt to access callframe when debugger is not on pause"; 963 return; 964 } 965 injectedScript = m_injectedScriptManager->injectedScriptForObjectId(*callFrameId); 966 if (injectedScript.isEmpty()) { 967 *errorString = "Inspected frame has gone"; 968 return; 969 } 970 } else if (functionObjectId) { 971 injectedScript = m_injectedScriptManager->injectedScriptForObjectId(*functionObjectId); 972 if (injectedScript.isEmpty()) { 973 *errorString = "Function object id cannot be resolved"; 974 return; 975 } 976 } else { 977 *errorString = "Either call frame or function object must be specified"; 978 return; 979 } 980 String newValueString = newValue->toJSONString(); 981 982 injectedScript.setVariableValue(errorString, m_currentCallStack, callFrameId, functionObjectId, scopeNumber, variableName, newValueString); 983 } 984 985 void InspectorDebuggerAgent::skipStackFrames(ErrorString* errorString, const String* pattern) 986 { 987 OwnPtr<ScriptRegexp> compiled; 988 String patternValue = pattern ? *pattern : ""; 989 if (!patternValue.isEmpty()) { 990 compiled = compileSkipCallFramePattern(patternValue); 991 if (!compiled) { 992 *errorString = "Invalid regular expression"; 993 return; 994 } 995 } 996 m_state->setString(DebuggerAgentState::skipStackPattern, patternValue); 997 m_cachedSkipStackRegExp = compiled.release(); 998 } 999 1000 void InspectorDebuggerAgent::setAsyncCallStackDepth(ErrorString*, int depth) 1001 { 1002 m_state->setLong(DebuggerAgentState::asyncCallStackDepth, depth); 1003 m_asyncCallStackTracker.setAsyncCallStackDepth(depth); 1004 } 1005 1006 void InspectorDebuggerAgent::scriptExecutionBlockedByCSP(const String& directiveText) 1007 { 1008 if (scriptDebugServer().pauseOnExceptionsState() != ScriptDebugServer::DontPauseOnExceptions) { 1009 RefPtr<JSONObject> directive = JSONObject::create(); 1010 directive->setString("directiveText", directiveText); 1011 breakProgram(InspectorFrontend::Debugger::Reason::CSPViolation, directive.release()); 1012 } 1013 } 1014 1015 PassRefPtr<Array<CallFrame> > InspectorDebuggerAgent::currentCallFrames() 1016 { 1017 if (!m_pausedScriptState || m_currentCallStack.isEmpty()) 1018 return Array<CallFrame>::create(); 1019 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(m_pausedScriptState.get()); 1020 if (injectedScript.isEmpty()) { 1021 ASSERT_NOT_REACHED(); 1022 return Array<CallFrame>::create(); 1023 } 1024 return injectedScript.wrapCallFrames(m_currentCallStack, 0); 1025 } 1026 1027 PassRefPtr<StackTrace> InspectorDebuggerAgent::currentAsyncStackTrace() 1028 { 1029 if (!m_pausedScriptState || !m_asyncCallStackTracker.isEnabled()) 1030 return nullptr; 1031 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(m_pausedScriptState.get()); 1032 if (injectedScript.isEmpty()) { 1033 ASSERT_NOT_REACHED(); 1034 return nullptr; 1035 } 1036 const AsyncCallStackTracker::AsyncCallChain* chain = m_asyncCallStackTracker.currentAsyncCallChain(); 1037 if (!chain) 1038 return nullptr; 1039 const AsyncCallStackTracker::AsyncCallStackVector& callStacks = chain->callStacks(); 1040 if (callStacks.isEmpty()) 1041 return nullptr; 1042 RefPtr<StackTrace> result; 1043 int asyncOrdinal = callStacks.size(); 1044 for (AsyncCallStackTracker::AsyncCallStackVector::const_reverse_iterator it = callStacks.rbegin(); it != callStacks.rend(); ++it) { 1045 RefPtr<StackTrace> next = StackTrace::create() 1046 .setCallFrames(injectedScript.wrapCallFrames((*it)->callFrames(), asyncOrdinal--)) 1047 .release(); 1048 next->setDescription((*it)->description()); 1049 if (result) 1050 next->setAsyncStackTrace(result.release()); 1051 result.swap(next); 1052 } 1053 return result.release(); 1054 } 1055 1056 String InspectorDebuggerAgent::sourceMapURLForScript(const Script& script) 1057 { 1058 bool deprecated; 1059 String sourceMapURL = ContentSearchUtils::findSourceMapURL(script.source, ContentSearchUtils::JavaScriptMagicComment, &deprecated); 1060 if (!sourceMapURL.isEmpty()) { 1061 // FIXME: add deprecated console message here. 1062 return sourceMapURL; 1063 } 1064 1065 if (script.url.isEmpty()) 1066 return String(); 1067 1068 InspectorPageAgent* pageAgent = m_instrumentingAgents->inspectorPageAgent(); 1069 if (!pageAgent) 1070 return String(); 1071 return pageAgent->resourceSourceMapURL(script.url); 1072 } 1073 1074 // JavaScriptDebugListener functions 1075 1076 void InspectorDebuggerAgent::didParseSource(const String& scriptId, const Script& script) 1077 { 1078 // Don't send script content to the front end until it's really needed. 1079 const bool* isContentScript = script.isContentScript ? &script.isContentScript : 0; 1080 String sourceMapURL = sourceMapURLForScript(script); 1081 String* sourceMapURLParam = sourceMapURL.isNull() ? 0 : &sourceMapURL; 1082 String sourceURL; 1083 if (!script.startLine && !script.startColumn) { 1084 bool deprecated; 1085 sourceURL = ContentSearchUtils::findSourceURL(script.source, ContentSearchUtils::JavaScriptMagicComment, &deprecated); 1086 // FIXME: add deprecated console message here. 1087 } 1088 bool hasSourceURL = !sourceURL.isEmpty(); 1089 String scriptURL = hasSourceURL ? sourceURL : script.url; 1090 bool* hasSourceURLParam = hasSourceURL ? &hasSourceURL : 0; 1091 m_frontend->scriptParsed(scriptId, scriptURL, script.startLine, script.startColumn, script.endLine, script.endColumn, isContentScript, sourceMapURLParam, hasSourceURLParam); 1092 1093 m_scripts.set(scriptId, script); 1094 1095 if (scriptURL.isEmpty()) 1096 return; 1097 1098 RefPtr<JSONObject> breakpointsCookie = m_state->getObject(DebuggerAgentState::javaScriptBreakpoints); 1099 for (JSONObject::iterator it = breakpointsCookie->begin(); it != breakpointsCookie->end(); ++it) { 1100 RefPtr<JSONObject> breakpointObject = it->value->asObject(); 1101 bool isAntibreakpoint; 1102 breakpointObject->getBoolean(DebuggerAgentState::isAnti, &isAntibreakpoint); 1103 if (isAntibreakpoint) 1104 continue; 1105 bool isRegex; 1106 breakpointObject->getBoolean(DebuggerAgentState::isRegex, &isRegex); 1107 String url; 1108 breakpointObject->getString(DebuggerAgentState::url, &url); 1109 if (!matches(scriptURL, url, isRegex)) 1110 continue; 1111 ScriptBreakpoint breakpoint; 1112 breakpointObject->getNumber(DebuggerAgentState::lineNumber, &breakpoint.lineNumber); 1113 breakpointObject->getNumber(DebuggerAgentState::columnNumber, &breakpoint.columnNumber); 1114 breakpointObject->getString(DebuggerAgentState::condition, &breakpoint.condition); 1115 RefPtr<TypeBuilder::Debugger::Location> location = resolveBreakpoint(it->key, scriptId, breakpoint, UserBreakpointSource); 1116 if (location) 1117 m_frontend->breakpointResolved(it->key, location); 1118 } 1119 } 1120 1121 void InspectorDebuggerAgent::failedToParseSource(const String& url, const String& data, int firstLine, int errorLine, const String& errorMessage) 1122 { 1123 m_frontend->scriptFailedToParse(url, data, firstLine, errorLine, errorMessage); 1124 } 1125 1126 ScriptDebugListener::SkipPauseRequest InspectorDebuggerAgent::didPause(ScriptState* scriptState, const ScriptValue& callFrames, const ScriptValue& exception, const Vector<String>& hitBreakpoints) 1127 { 1128 ScriptDebugListener::SkipPauseRequest result; 1129 if (m_javaScriptPauseScheduled) 1130 result = ScriptDebugListener::NoSkip; // Don't skip explicit pause requests from front-end. 1131 else if (m_skipAllPauses) 1132 result = ScriptDebugListener::Continue; 1133 else if (!hitBreakpoints.isEmpty()) 1134 result = ScriptDebugListener::NoSkip; // Don't skip explicit breakpoints even if set in frameworks. 1135 else if (!exception.isEmpty()) 1136 result = shouldSkipExceptionPause(); 1137 else if (m_debuggerStepScheduled || m_pausingOnNativeEvent) 1138 result = shouldSkipStepPause(); 1139 else 1140 result = ScriptDebugListener::NoSkip; 1141 1142 if (result != ScriptDebugListener::NoSkip) 1143 return result; 1144 1145 ASSERT(scriptState && !m_pausedScriptState); 1146 m_pausedScriptState = scriptState; 1147 m_currentCallStack = callFrames; 1148 1149 if (!exception.isEmpty()) { 1150 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(scriptState); 1151 if (!injectedScript.isEmpty()) { 1152 m_breakReason = InspectorFrontend::Debugger::Reason::Exception; 1153 m_breakAuxData = injectedScript.wrapObject(exception, InspectorDebuggerAgent::backtraceObjectGroup)->openAccessors(); 1154 // m_breakAuxData might be null after this. 1155 } 1156 } 1157 1158 RefPtr<Array<String> > hitBreakpointIds = Array<String>::create(); 1159 1160 for (Vector<String>::const_iterator i = hitBreakpoints.begin(); i != hitBreakpoints.end(); ++i) { 1161 DebugServerBreakpointToBreakpointIdAndSourceMap::iterator breakpointIterator = m_serverBreakpoints.find(*i); 1162 if (breakpointIterator != m_serverBreakpoints.end()) { 1163 const String& localId = breakpointIterator->value.first; 1164 hitBreakpointIds->addItem(localId); 1165 1166 BreakpointSource source = breakpointIterator->value.second; 1167 if (m_breakReason == InspectorFrontend::Debugger::Reason::Other && source == DebugCommandBreakpointSource) 1168 m_breakReason = InspectorFrontend::Debugger::Reason::DebugCommand; 1169 } 1170 } 1171 1172 m_frontend->paused(currentCallFrames(), m_breakReason, m_breakAuxData, hitBreakpointIds, currentAsyncStackTrace()); 1173 m_javaScriptPauseScheduled = false; 1174 m_debuggerStepScheduled = false; 1175 m_pausingOnNativeEvent = false; 1176 m_skippedStepInCount = 0; 1177 1178 if (!m_continueToLocationBreakpointId.isEmpty()) { 1179 scriptDebugServer().removeBreakpoint(m_continueToLocationBreakpointId); 1180 m_continueToLocationBreakpointId = ""; 1181 } 1182 if (m_listener) 1183 m_listener->didPause(); 1184 return result; 1185 } 1186 1187 void InspectorDebuggerAgent::didContinue() 1188 { 1189 m_pausedScriptState = nullptr; 1190 m_currentCallStack = ScriptValue(); 1191 clearBreakDetails(); 1192 m_frontend->resumed(); 1193 } 1194 1195 bool InspectorDebuggerAgent::canBreakProgram() 1196 { 1197 return scriptDebugServer().canBreakProgram(); 1198 } 1199 1200 void InspectorDebuggerAgent::breakProgram(InspectorFrontend::Debugger::Reason::Enum breakReason, PassRefPtr<JSONObject> data) 1201 { 1202 if (m_skipAllPauses) 1203 return; 1204 m_breakReason = breakReason; 1205 m_breakAuxData = data; 1206 m_debuggerStepScheduled = false; 1207 m_pausingOnNativeEvent = false; 1208 scriptDebugServer().breakProgram(); 1209 } 1210 1211 void InspectorDebuggerAgent::clear() 1212 { 1213 m_pausedScriptState = nullptr; 1214 m_currentCallStack = ScriptValue(); 1215 m_scripts.clear(); 1216 m_breakpointIdToDebugServerBreakpointIds.clear(); 1217 m_asyncCallStackTracker.clear(); 1218 m_continueToLocationBreakpointId = String(); 1219 clearBreakDetails(); 1220 m_javaScriptPauseScheduled = false; 1221 m_debuggerStepScheduled = false; 1222 m_pausingOnNativeEvent = false; 1223 ErrorString error; 1224 setOverlayMessage(&error, 0); 1225 } 1226 1227 bool InspectorDebuggerAgent::assertPaused(ErrorString* errorString) 1228 { 1229 if (!m_pausedScriptState) { 1230 *errorString = "Can only perform operation while paused."; 1231 return false; 1232 } 1233 return true; 1234 } 1235 1236 void InspectorDebuggerAgent::clearBreakDetails() 1237 { 1238 m_breakReason = InspectorFrontend::Debugger::Reason::Other; 1239 m_breakAuxData = nullptr; 1240 } 1241 1242 void InspectorDebuggerAgent::setBreakpoint(const String& scriptId, int lineNumber, int columnNumber, BreakpointSource source, const String& condition) 1243 { 1244 String breakpointId = generateBreakpointId(scriptId, lineNumber, columnNumber, source); 1245 ScriptBreakpoint breakpoint(lineNumber, columnNumber, condition); 1246 resolveBreakpoint(breakpointId, scriptId, breakpoint, source); 1247 } 1248 1249 void InspectorDebuggerAgent::removeBreakpoint(const String& scriptId, int lineNumber, int columnNumber, BreakpointSource source) 1250 { 1251 removeBreakpoint(generateBreakpointId(scriptId, lineNumber, columnNumber, source)); 1252 } 1253 1254 void InspectorDebuggerAgent::reset() 1255 { 1256 m_scripts.clear(); 1257 m_breakpointIdToDebugServerBreakpointIds.clear(); 1258 m_asyncCallStackTracker.clear(); 1259 if (m_frontend) 1260 m_frontend->globalObjectCleared(); 1261 } 1262 1263 } // namespace WebCore 1264 1265