1/* 2 * Copyright (C) 2010 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31/** 32 * @constructor 33 * @extends {WebInspector.TargetAwareObject} 34 * @param {!WebInspector.Target} target 35 */ 36WebInspector.DebuggerModel = function(target) 37{ 38 WebInspector.TargetAwareObject.call(this, target); 39 40 target.registerDebuggerDispatcher(new WebInspector.DebuggerDispatcher(this)); 41 this._agent = target.debuggerAgent(); 42 43 /** @type {?WebInspector.DebuggerPausedDetails} */ 44 this._debuggerPausedDetails = null; 45 /** @type {!Object.<string, !WebInspector.Script>} */ 46 this._scripts = {}; 47 /** @type {!StringMap.<!Array.<!WebInspector.Script>>} */ 48 this._scriptsBySourceURL = new StringMap(); 49 50 this._breakpointsActive = true; 51 /** @type {!WebInspector.Object} */ 52 this._breakpointResolvedEventTarget = new WebInspector.Object(); 53 54 this._isPausing = false; 55 WebInspector.settings.pauseOnExceptionEnabled.addChangeListener(this._pauseOnExceptionStateChanged, this); 56 WebInspector.settings.pauseOnCaughtException.addChangeListener(this._pauseOnExceptionStateChanged, this); 57 58 WebInspector.settings.enableAsyncStackTraces.addChangeListener(this._asyncStackTracesStateChanged, this); 59 target.profilingLock.addEventListener(WebInspector.Lock.Events.StateChanged, this._profilingStateChanged, this); 60 61 this.enableDebugger(); 62 63 WebInspector.settings.skipStackFramesSwitch.addChangeListener(this._applySkipStackFrameSettings, this); 64 WebInspector.settings.skipStackFramesPattern.addChangeListener(this._applySkipStackFrameSettings, this); 65 this._applySkipStackFrameSettings(); 66} 67 68/** 69 * Keep these in sync with WebCore::ScriptDebugServer 70 * 71 * @enum {string} 72 */ 73WebInspector.DebuggerModel.PauseOnExceptionsState = { 74 DontPauseOnExceptions : "none", 75 PauseOnAllExceptions : "all", 76 PauseOnUncaughtExceptions: "uncaught" 77}; 78 79WebInspector.DebuggerModel.Events = { 80 DebuggerWasEnabled: "DebuggerWasEnabled", 81 DebuggerWasDisabled: "DebuggerWasDisabled", 82 DebuggerPaused: "DebuggerPaused", 83 DebuggerResumed: "DebuggerResumed", 84 ParsedScriptSource: "ParsedScriptSource", 85 FailedToParseScriptSource: "FailedToParseScriptSource", 86 GlobalObjectCleared: "GlobalObjectCleared", 87 CallFrameSelected: "CallFrameSelected", 88 ConsoleCommandEvaluatedInSelectedCallFrame: "ConsoleCommandEvaluatedInSelectedCallFrame", 89 BreakpointsActiveStateChanged: "BreakpointsActiveStateChanged" 90} 91 92WebInspector.DebuggerModel.BreakReason = { 93 DOM: "DOM", 94 EventListener: "EventListener", 95 XHR: "XHR", 96 Exception: "exception", 97 Assert: "assert", 98 CSPViolation: "CSPViolation", 99 DebugCommand: "debugCommand" 100} 101 102WebInspector.DebuggerModel.prototype = { 103 /** 104 * @return {boolean} 105 */ 106 debuggerEnabled: function() 107 { 108 return !!this._debuggerEnabled; 109 }, 110 111 enableDebugger: function() 112 { 113 if (this._debuggerEnabled) 114 return; 115 116 this._agent.enable(this._debuggerWasEnabled.bind(this)); 117 }, 118 119 disableDebugger: function() 120 { 121 if (!this._debuggerEnabled) 122 return; 123 124 this._agent.disable(this._debuggerWasDisabled.bind(this)); 125 }, 126 127 /** 128 * @param {boolean} skip 129 * @param {boolean=} untilReload 130 */ 131 skipAllPauses: function(skip, untilReload) 132 { 133 if (this._skipAllPausesTimeout) { 134 clearTimeout(this._skipAllPausesTimeout); 135 delete this._skipAllPausesTimeout; 136 } 137 this._agent.setSkipAllPauses(skip, untilReload); 138 }, 139 140 /** 141 * @param {number} timeout 142 */ 143 skipAllPausesUntilReloadOrTimeout: function(timeout) 144 { 145 if (this._skipAllPausesTimeout) 146 clearTimeout(this._skipAllPausesTimeout); 147 this._agent.setSkipAllPauses(true, true); 148 // If reload happens before the timeout, the flag will be already unset and the timeout callback won't change anything. 149 this._skipAllPausesTimeout = setTimeout(this.skipAllPauses.bind(this, false), timeout); 150 }, 151 152 _debuggerWasEnabled: function() 153 { 154 this._debuggerEnabled = true; 155 this._pauseOnExceptionStateChanged(); 156 this._asyncStackTracesStateChanged(); 157 this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.DebuggerWasEnabled); 158 }, 159 160 _pauseOnExceptionStateChanged: function() 161 { 162 var state; 163 if (!WebInspector.settings.pauseOnExceptionEnabled.get()) { 164 state = WebInspector.DebuggerModel.PauseOnExceptionsState.DontPauseOnExceptions; 165 } else if (WebInspector.settings.pauseOnCaughtException.get()) { 166 state = WebInspector.DebuggerModel.PauseOnExceptionsState.PauseOnAllExceptions; 167 } else { 168 state = WebInspector.DebuggerModel.PauseOnExceptionsState.PauseOnUncaughtExceptions; 169 } 170 this._agent.setPauseOnExceptions(state); 171 }, 172 173 _profilingStateChanged: function() 174 { 175 if (WebInspector.experimentsSettings.disableAgentsWhenProfile.isEnabled()) { 176 if (this.target().profilingLock.isAcquired()) 177 this.disableDebugger(); 178 else 179 this.enableDebugger(); 180 } 181 this._asyncStackTracesStateChanged(); 182 }, 183 184 _asyncStackTracesStateChanged: function() 185 { 186 const maxAsyncStackChainDepth = 4; 187 var enabled = WebInspector.settings.enableAsyncStackTraces.get() && !this.target().profilingLock.isAcquired(); 188 this._agent.setAsyncCallStackDepth(enabled ? maxAsyncStackChainDepth : 0); 189 }, 190 191 _debuggerWasDisabled: function() 192 { 193 this._debuggerEnabled = false; 194 this._isPausing = false; 195 this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.DebuggerWasDisabled); 196 }, 197 198 stepInto: function() 199 { 200 /** 201 * @this {WebInspector.DebuggerModel} 202 */ 203 function callback() 204 { 205 this._agent.stepInto(); 206 } 207 this._agent.setOverlayMessage(undefined, callback.bind(this)); 208 }, 209 210 stepOver: function() 211 { 212 /** 213 * @this {WebInspector.DebuggerModel} 214 */ 215 function callback() 216 { 217 this._agent.stepOver(); 218 } 219 this._agent.setOverlayMessage(undefined, callback.bind(this)); 220 }, 221 222 stepOut: function() 223 { 224 /** 225 * @this {WebInspector.DebuggerModel} 226 */ 227 function callback() 228 { 229 this._agent.stepOut(); 230 } 231 this._agent.setOverlayMessage(undefined, callback.bind(this)); 232 }, 233 234 resume: function() 235 { 236 /** 237 * @this {WebInspector.DebuggerModel} 238 */ 239 function callback() 240 { 241 this._agent.resume(); 242 } 243 this._agent.setOverlayMessage(undefined, callback.bind(this)); 244 this._isPausing = false; 245 }, 246 247 pause: function() 248 { 249 this._isPausing = true; 250 this.skipAllPauses(false); 251 this._agent.pause(); 252 }, 253 254 /** 255 * @param {!WebInspector.DebuggerModel.Location} rawLocation 256 * @param {string} condition 257 * @param {function(?DebuggerAgent.BreakpointId, !Array.<!WebInspector.DebuggerModel.Location>):void=} callback 258 */ 259 setBreakpointByScriptLocation: function(rawLocation, condition, callback) 260 { 261 var script = rawLocation.script(); 262 if (script.sourceURL) 263 this.setBreakpointByURL(script.sourceURL, rawLocation.lineNumber, rawLocation.columnNumber, condition, callback); 264 else 265 this.setBreakpointBySourceId(rawLocation, condition, callback); 266 }, 267 268 /** 269 * @param {string} url 270 * @param {number} lineNumber 271 * @param {number=} columnNumber 272 * @param {string=} condition 273 * @param {function(?DebuggerAgent.BreakpointId, !Array.<!WebInspector.DebuggerModel.Location>)=} callback 274 */ 275 setBreakpointByURL: function(url, lineNumber, columnNumber, condition, callback) 276 { 277 // Adjust column if needed. 278 var minColumnNumber = 0; 279 var scripts = this._scriptsBySourceURL.get(url) || []; 280 for (var i = 0, l = scripts.length; i < l; ++i) { 281 var script = scripts[i]; 282 if (lineNumber === script.lineOffset) 283 minColumnNumber = minColumnNumber ? Math.min(minColumnNumber, script.columnOffset) : script.columnOffset; 284 } 285 columnNumber = Math.max(columnNumber, minColumnNumber); 286 287 var target = this.target(); 288 /** 289 * @param {?Protocol.Error} error 290 * @param {!DebuggerAgent.BreakpointId} breakpointId 291 * @param {!Array.<!DebuggerAgent.Location>} locations 292 */ 293 function didSetBreakpoint(error, breakpointId, locations) 294 { 295 if (callback) { 296 var rawLocations = locations ? locations.map(WebInspector.DebuggerModel.Location.fromPayload.bind(WebInspector.DebuggerModel.Location, target)) : []; 297 callback(error ? null : breakpointId, rawLocations); 298 } 299 } 300 this._agent.setBreakpointByUrl(lineNumber, url, undefined, columnNumber, condition, undefined, didSetBreakpoint); 301 WebInspector.userMetrics.ScriptsBreakpointSet.record(); 302 }, 303 304 /** 305 * @param {!WebInspector.DebuggerModel.Location} rawLocation 306 * @param {string} condition 307 * @param {function(?DebuggerAgent.BreakpointId, !Array.<!WebInspector.DebuggerModel.Location>)=} callback 308 */ 309 setBreakpointBySourceId: function(rawLocation, condition, callback) 310 { 311 var target = this.target(); 312 313 /** 314 * @param {?Protocol.Error} error 315 * @param {!DebuggerAgent.BreakpointId} breakpointId 316 * @param {!DebuggerAgent.Location} actualLocation 317 */ 318 function didSetBreakpoint(error, breakpointId, actualLocation) 319 { 320 if (callback) { 321 var location = WebInspector.DebuggerModel.Location.fromPayload(target, actualLocation); 322 callback(error ? null : breakpointId, [location]); 323 } 324 } 325 this._agent.setBreakpoint(rawLocation.payload(), condition, didSetBreakpoint); 326 WebInspector.userMetrics.ScriptsBreakpointSet.record(); 327 }, 328 329 /** 330 * @param {!DebuggerAgent.BreakpointId} breakpointId 331 * @param {function()=} callback 332 */ 333 removeBreakpoint: function(breakpointId, callback) 334 { 335 this._agent.removeBreakpoint(breakpointId, innerCallback); 336 337 /** 338 * @param {?Protocol.Error} error 339 */ 340 function innerCallback(error) 341 { 342 if (error) 343 console.error("Failed to remove breakpoint: " + error); 344 if (callback) 345 callback(); 346 } 347 }, 348 349 /** 350 * @param {!DebuggerAgent.BreakpointId} breakpointId 351 * @param {!DebuggerAgent.Location} location 352 */ 353 _breakpointResolved: function(breakpointId, location) 354 { 355 this._breakpointResolvedEventTarget.dispatchEventToListeners(breakpointId, WebInspector.DebuggerModel.Location.fromPayload(this.target(), location)); 356 }, 357 358 _globalObjectCleared: function() 359 { 360 this._setDebuggerPausedDetails(null); 361 this._reset(); 362 this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.GlobalObjectCleared); 363 }, 364 365 _reset: function() 366 { 367 this._scripts = {}; 368 this._scriptsBySourceURL.clear(); 369 }, 370 371 /** 372 * @return {!Object.<string, !WebInspector.Script>} 373 */ 374 get scripts() 375 { 376 return this._scripts; 377 }, 378 379 /** 380 * @param {!DebuggerAgent.ScriptId} scriptId 381 * @return {!WebInspector.Script} 382 */ 383 scriptForId: function(scriptId) 384 { 385 return this._scripts[scriptId] || null; 386 }, 387 388 /** 389 * @return {!Array.<!WebInspector.Script>} 390 */ 391 scriptsForSourceURL: function(sourceURL) 392 { 393 if (!sourceURL) 394 return []; 395 return this._scriptsBySourceURL.get(sourceURL) || []; 396 }, 397 398 /** 399 * @param {!DebuggerAgent.ScriptId} scriptId 400 * @param {string} newSource 401 * @param {function(?Protocol.Error, !DebuggerAgent.SetScriptSourceError=)} callback 402 */ 403 setScriptSource: function(scriptId, newSource, callback) 404 { 405 this._scripts[scriptId].editSource(newSource, this._didEditScriptSource.bind(this, scriptId, newSource, callback)); 406 }, 407 408 /** 409 * @param {!DebuggerAgent.ScriptId} scriptId 410 * @param {string} newSource 411 * @param {function(?Protocol.Error, !DebuggerAgent.SetScriptSourceError=)} callback 412 * @param {?Protocol.Error} error 413 * @param {!DebuggerAgent.SetScriptSourceError=} errorData 414 * @param {!Array.<!DebuggerAgent.CallFrame>=} callFrames 415 * @param {!DebuggerAgent.StackTrace=} asyncStackTrace 416 * @param {boolean=} needsStepIn 417 */ 418 _didEditScriptSource: function(scriptId, newSource, callback, error, errorData, callFrames, asyncStackTrace, needsStepIn) 419 { 420 callback(error, errorData); 421 if (needsStepIn) 422 this.stepInto(); 423 else if (!error && callFrames && callFrames.length) 424 this._pausedScript(callFrames, this._debuggerPausedDetails.reason, this._debuggerPausedDetails.auxData, this._debuggerPausedDetails.breakpointIds, asyncStackTrace); 425 }, 426 427 /** 428 * @return {?Array.<!WebInspector.DebuggerModel.CallFrame>} 429 */ 430 get callFrames() 431 { 432 return this._debuggerPausedDetails ? this._debuggerPausedDetails.callFrames : null; 433 }, 434 435 /** 436 * @return {?WebInspector.DebuggerPausedDetails} 437 */ 438 debuggerPausedDetails: function() 439 { 440 return this._debuggerPausedDetails; 441 }, 442 443 /** 444 * @param {?WebInspector.DebuggerPausedDetails} debuggerPausedDetails 445 */ 446 _setDebuggerPausedDetails: function(debuggerPausedDetails) 447 { 448 this._isPausing = false; 449 if (this._debuggerPausedDetails) 450 this._debuggerPausedDetails.dispose(); 451 this._debuggerPausedDetails = debuggerPausedDetails; 452 if (this._debuggerPausedDetails) 453 this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.DebuggerPaused, this._debuggerPausedDetails); 454 if (debuggerPausedDetails) { 455 this.setSelectedCallFrame(debuggerPausedDetails.callFrames[0]); 456 this._agent.setOverlayMessage(WebInspector.UIString("Paused in debugger")); 457 } else { 458 this.setSelectedCallFrame(null); 459 this._agent.setOverlayMessage(); 460 } 461 }, 462 463 /** 464 * @param {!Array.<!DebuggerAgent.CallFrame>} callFrames 465 * @param {string} reason 466 * @param {!Object|undefined} auxData 467 * @param {!Array.<string>} breakpointIds 468 * @param {!DebuggerAgent.StackTrace=} asyncStackTrace 469 */ 470 _pausedScript: function(callFrames, reason, auxData, breakpointIds, asyncStackTrace) 471 { 472 this._setDebuggerPausedDetails(new WebInspector.DebuggerPausedDetails(this.target(), callFrames, reason, auxData, breakpointIds, asyncStackTrace)); 473 }, 474 475 _resumedScript: function() 476 { 477 this._setDebuggerPausedDetails(null); 478 this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.DebuggerResumed); 479 }, 480 481 /** 482 * @param {!DebuggerAgent.ScriptId} scriptId 483 * @param {string} sourceURL 484 * @param {number} startLine 485 * @param {number} startColumn 486 * @param {number} endLine 487 * @param {number} endColumn 488 * @param {boolean} isContentScript 489 * @param {string=} sourceMapURL 490 * @param {boolean=} hasSourceURL 491 */ 492 _parsedScriptSource: function(scriptId, sourceURL, startLine, startColumn, endLine, endColumn, isContentScript, sourceMapURL, hasSourceURL) 493 { 494 var script = new WebInspector.Script(this.target(), scriptId, sourceURL, startLine, startColumn, endLine, endColumn, isContentScript, sourceMapURL, hasSourceURL); 495 this._registerScript(script); 496 this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.ParsedScriptSource, script); 497 }, 498 499 /** 500 * @param {!WebInspector.Script} script 501 */ 502 _registerScript: function(script) 503 { 504 this._scripts[script.scriptId] = script; 505 if (script.isAnonymousScript()) 506 return; 507 508 var scripts = this._scriptsBySourceURL.get(script.sourceURL); 509 if (!scripts) { 510 scripts = []; 511 this._scriptsBySourceURL.put(script.sourceURL, scripts); 512 } 513 scripts.push(script); 514 }, 515 516 /** 517 * @param {!WebInspector.Script} script 518 * @param {number} lineNumber 519 * @param {number} columnNumber 520 * @return {?WebInspector.DebuggerModel.Location} 521 */ 522 createRawLocation: function(script, lineNumber, columnNumber) 523 { 524 if (script.sourceURL) 525 return this.createRawLocationByURL(script.sourceURL, lineNumber, columnNumber) 526 return new WebInspector.DebuggerModel.Location(this.target(), script.scriptId, lineNumber, columnNumber); 527 }, 528 529 /** 530 * @param {string} sourceURL 531 * @param {number} lineNumber 532 * @param {number} columnNumber 533 * @return {?WebInspector.DebuggerModel.Location} 534 */ 535 createRawLocationByURL: function(sourceURL, lineNumber, columnNumber) 536 { 537 var closestScript = null; 538 var scripts = this._scriptsBySourceURL.get(sourceURL) || []; 539 for (var i = 0, l = scripts.length; i < l; ++i) { 540 var script = scripts[i]; 541 if (!closestScript) 542 closestScript = script; 543 if (script.lineOffset > lineNumber || (script.lineOffset === lineNumber && script.columnOffset > columnNumber)) 544 continue; 545 if (script.endLine < lineNumber || (script.endLine === lineNumber && script.endColumn <= columnNumber)) 546 continue; 547 closestScript = script; 548 break; 549 } 550 return closestScript ? new WebInspector.DebuggerModel.Location(this.target(), closestScript.scriptId, lineNumber, columnNumber) : null; 551 }, 552 553 /** 554 * @return {boolean} 555 */ 556 isPaused: function() 557 { 558 return !!this.debuggerPausedDetails(); 559 }, 560 561 /** 562 * @return {boolean} 563 */ 564 isPausing: function() 565 { 566 return this._isPausing; 567 }, 568 569 /** 570 * @param {?WebInspector.DebuggerModel.CallFrame} callFrame 571 */ 572 setSelectedCallFrame: function(callFrame) 573 { 574 this._selectedCallFrame = callFrame; 575 if (!this._selectedCallFrame) 576 return; 577 578 this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.CallFrameSelected, callFrame); 579 }, 580 581 /** 582 * @return {?WebInspector.DebuggerModel.CallFrame} 583 */ 584 selectedCallFrame: function() 585 { 586 return this._selectedCallFrame; 587 }, 588 589 /** 590 * @param {string} code 591 * @param {string} objectGroup 592 * @param {boolean} includeCommandLineAPI 593 * @param {boolean} doNotPauseOnExceptionsAndMuteConsole 594 * @param {boolean} returnByValue 595 * @param {boolean} generatePreview 596 * @param {function(?WebInspector.RemoteObject, boolean, ?RuntimeAgent.RemoteObject=)} callback 597 */ 598 evaluateOnSelectedCallFrame: function(code, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, generatePreview, callback) 599 { 600 /** 601 * @param {?RuntimeAgent.RemoteObject} result 602 * @param {boolean=} wasThrown 603 * @this {WebInspector.DebuggerModel} 604 */ 605 function didEvaluate(result, wasThrown) 606 { 607 if (!result) 608 callback(null, false); 609 else if (returnByValue) 610 callback(null, !!wasThrown, wasThrown ? null : result); 611 else 612 callback(this.target().runtimeModel.createRemoteObject(result), !!wasThrown); 613 614 if (objectGroup === "console") 615 this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.ConsoleCommandEvaluatedInSelectedCallFrame); 616 } 617 618 this.selectedCallFrame().evaluate(code, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, generatePreview, didEvaluate.bind(this)); 619 }, 620 621 /** 622 * @param {function(!Object)} callback 623 */ 624 getSelectedCallFrameVariables: function(callback) 625 { 626 var result = { this: true }; 627 628 var selectedCallFrame = this._selectedCallFrame; 629 if (!selectedCallFrame) 630 callback(result); 631 632 var pendingRequests = 0; 633 634 function propertiesCollected(properties) 635 { 636 for (var i = 0; properties && i < properties.length; ++i) 637 result[properties[i].name] = true; 638 if (--pendingRequests == 0) 639 callback(result); 640 } 641 642 for (var i = 0; i < selectedCallFrame.scopeChain.length; ++i) { 643 var scope = selectedCallFrame.scopeChain[i]; 644 var object = this.target().runtimeModel.createRemoteObject(scope.object); 645 pendingRequests++; 646 object.getAllProperties(false, propertiesCollected); 647 } 648 }, 649 650 /** 651 * @param {boolean} active 652 */ 653 setBreakpointsActive: function(active) 654 { 655 if (this._breakpointsActive === active) 656 return; 657 this._breakpointsActive = active; 658 this._agent.setBreakpointsActive(active); 659 this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.BreakpointsActiveStateChanged, active); 660 }, 661 662 /** 663 * @return {boolean} 664 */ 665 breakpointsActive: function() 666 { 667 return this._breakpointsActive; 668 }, 669 670 /** 671 * @param {!WebInspector.DebuggerModel.Location} rawLocation 672 * @param {function(!WebInspector.UILocation):(boolean|undefined)} updateDelegate 673 * @return {!WebInspector.Script.Location} 674 */ 675 createLiveLocation: function(rawLocation, updateDelegate) 676 { 677 var script = this._scripts[rawLocation.scriptId]; 678 return script.createLiveLocation(rawLocation, updateDelegate); 679 }, 680 681 /** 682 * @param {!WebInspector.DebuggerModel.Location|!DebuggerAgent.Location} rawLocation 683 * @return {?WebInspector.UILocation} 684 */ 685 rawLocationToUILocation: function(rawLocation) 686 { 687 var script = this._scripts[rawLocation.scriptId]; 688 if (!script) 689 return null; 690 return script.rawLocationToUILocation(rawLocation.lineNumber, rawLocation.columnNumber); 691 }, 692 693 /** 694 * Handles notification from JavaScript VM about updated stack (liveedit or frame restart action). 695 * @param {!Array.<!DebuggerAgent.CallFrame>=} newCallFrames 696 * @param {!Object=} details 697 * @param {!DebuggerAgent.StackTrace=} asyncStackTrace 698 */ 699 callStackModified: function(newCallFrames, details, asyncStackTrace) 700 { 701 // FIXME: declare this property in protocol and in JavaScript. 702 if (details && details["stack_update_needs_step_in"]) 703 this.stepInto(); 704 else if (newCallFrames && newCallFrames.length) 705 this._pausedScript(newCallFrames, this._debuggerPausedDetails.reason, this._debuggerPausedDetails.auxData, this._debuggerPausedDetails.breakpointIds, asyncStackTrace); 706 }, 707 708 _applySkipStackFrameSettings: function() 709 { 710 if (!WebInspector.experimentsSettings.frameworksDebuggingSupport.isEnabled()) 711 return; 712 var settings = WebInspector.settings; 713 var patternParameter = settings.skipStackFramesSwitch.get() ? settings.skipStackFramesPattern.get() : undefined; 714 this._agent.skipStackFrames(patternParameter); 715 }, 716 717 /** 718 * @param {!WebInspector.RemoteObject} remoteObject 719 * @param {function(?DebuggerAgent.FunctionDetails)} callback 720 */ 721 functionDetails: function(remoteObject, callback) 722 { 723 this._agent.getFunctionDetails(remoteObject.objectId, didGetDetails); 724 725 /** 726 * @param {?Protocol.Error} error 727 * @param {!DebuggerAgent.FunctionDetails} response 728 */ 729 function didGetDetails(error, response) 730 { 731 if (error) { 732 console.error(error); 733 callback(null); 734 return; 735 } 736 callback(response); 737 } 738 }, 739 740 /** 741 * @param {!DebuggerAgent.BreakpointId} breakpointId 742 * @param {function(!WebInspector.Event)} listener 743 * @param {!Object=} thisObject 744 */ 745 addBreakpointListener: function(breakpointId, listener, thisObject) 746 { 747 this._breakpointResolvedEventTarget.addEventListener(breakpointId, listener, thisObject) 748 }, 749 750 /** 751 * @param {!DebuggerAgent.BreakpointId} breakpointId 752 * @param {function(!WebInspector.Event)} listener 753 * @param {!Object=} thisObject 754 */ 755 removeBreakpointListener: function(breakpointId, listener, thisObject) 756 { 757 this._breakpointResolvedEventTarget.removeEventListener(breakpointId, listener, thisObject); 758 }, 759 760 __proto__: WebInspector.TargetAwareObject.prototype 761} 762 763WebInspector.DebuggerEventTypes = { 764 JavaScriptPause: 0, 765 JavaScriptBreakpoint: 1, 766 NativeBreakpoint: 2 767}; 768 769/** 770 * @constructor 771 * @implements {DebuggerAgent.Dispatcher} 772 * @param {!WebInspector.DebuggerModel} debuggerModel 773 */ 774WebInspector.DebuggerDispatcher = function(debuggerModel) 775{ 776 this._debuggerModel = debuggerModel; 777} 778 779WebInspector.DebuggerDispatcher.prototype = { 780 /** 781 * @param {!Array.<!DebuggerAgent.CallFrame>} callFrames 782 * @param {string} reason 783 * @param {!Object=} auxData 784 * @param {!Array.<string>=} breakpointIds 785 * @param {!DebuggerAgent.StackTrace=} asyncStackTrace 786 */ 787 paused: function(callFrames, reason, auxData, breakpointIds, asyncStackTrace) 788 { 789 this._debuggerModel._pausedScript(callFrames, reason, auxData, breakpointIds || [], asyncStackTrace); 790 }, 791 792 /** 793 * @override 794 */ 795 resumed: function() 796 { 797 this._debuggerModel._resumedScript(); 798 }, 799 800 /** 801 * @override 802 */ 803 globalObjectCleared: function() 804 { 805 this._debuggerModel._globalObjectCleared(); 806 }, 807 808 /** 809 * @param {!DebuggerAgent.ScriptId} scriptId 810 * @param {string} sourceURL 811 * @param {number} startLine 812 * @param {number} startColumn 813 * @param {number} endLine 814 * @param {number} endColumn 815 * @param {boolean=} isContentScript 816 * @param {string=} sourceMapURL 817 * @param {boolean=} hasSourceURL 818 */ 819 scriptParsed: function(scriptId, sourceURL, startLine, startColumn, endLine, endColumn, isContentScript, sourceMapURL, hasSourceURL) 820 { 821 this._debuggerModel._parsedScriptSource(scriptId, sourceURL, startLine, startColumn, endLine, endColumn, !!isContentScript, sourceMapURL, hasSourceURL); 822 }, 823 824 /** 825 * @param {string} sourceURL 826 * @param {string} source 827 * @param {number} startingLine 828 * @param {number} errorLine 829 * @param {string} errorMessage 830 */ 831 scriptFailedToParse: function(sourceURL, source, startingLine, errorLine, errorMessage) 832 { 833 }, 834 835 /** 836 * @param {!DebuggerAgent.BreakpointId} breakpointId 837 * @param {!DebuggerAgent.Location} location 838 */ 839 breakpointResolved: function(breakpointId, location) 840 { 841 this._debuggerModel._breakpointResolved(breakpointId, location); 842 } 843} 844 845/** 846 * @constructor 847 * @implements {WebInspector.RawLocation} 848 * @extends {WebInspector.TargetAware} 849 * @param {!WebInspector.Target} target 850 * @param {string} scriptId 851 * @param {number} lineNumber 852 * @param {number=} columnNumber 853 */ 854WebInspector.DebuggerModel.Location = function(target, scriptId, lineNumber, columnNumber) 855{ 856 WebInspector.TargetAware.call(this, target); 857 this._debuggerModel = target.debuggerModel; 858 this.scriptId = scriptId; 859 this.lineNumber = lineNumber; 860 this.columnNumber = columnNumber; 861} 862 863/** 864 * @param {!WebInspector.Target} target 865 * @param {!DebuggerAgent.Location} payload 866 * @return {!WebInspector.DebuggerModel.Location} 867 */ 868WebInspector.DebuggerModel.Location.fromPayload = function(target, payload) 869{ 870 return new WebInspector.DebuggerModel.Location(target, payload.scriptId, payload.lineNumber, payload.columnNumber); 871} 872 873WebInspector.DebuggerModel.Location.prototype = { 874 /** 875 * @return {!DebuggerAgent.Location} 876 */ 877 payload: function() 878 { 879 return { scriptId: this.scriptId, lineNumber: this.lineNumber, columnNumber: this.columnNumber }; 880 }, 881 882 /** 883 * @return {!WebInspector.Script} 884 */ 885 script: function() 886 { 887 return this._debuggerModel.scriptForId(this.scriptId); 888 }, 889 890 /** 891 * @param {function(!WebInspector.UILocation):(boolean|undefined)} updateDelegate 892 * @return {!WebInspector.Script.Location} 893 */ 894 createLiveLocation: function(updateDelegate) 895 { 896 return this._debuggerModel.createLiveLocation(this, updateDelegate); 897 }, 898 899 /** 900 * @return {?WebInspector.UILocation} 901 */ 902 toUILocation: function() 903 { 904 return this._debuggerModel.rawLocationToUILocation(this); 905 }, 906 907 continueToLocation: function() 908 { 909 this._debuggerModel._agent.continueToLocation(this.payload()); 910 }, 911 912 /** 913 * @return {string} 914 */ 915 id: function() 916 { 917 return this.target().id() + ":" + this.scriptId + ":" + this.lineNumber + ":" + this.columnNumber 918 }, 919 920 __proto__: WebInspector.TargetAware.prototype 921} 922 923/** 924 * @constructor 925 * @extends {WebInspector.TargetAware} 926 * @param {!WebInspector.Target} target 927 * @param {!WebInspector.Script} script 928 * @param {!DebuggerAgent.CallFrame} payload 929 * @param {boolean=} isAsync 930 */ 931WebInspector.DebuggerModel.CallFrame = function(target, script, payload, isAsync) 932{ 933 WebInspector.TargetAware.call(this, target); 934 this._debuggerAgent = target.debuggerModel._agent; 935 this._script = script; 936 this._payload = payload; 937 /** @type {!Array.<!WebInspector.Script.Location>} */ 938 this._liveLocations = []; 939 this._isAsync = isAsync; 940 this._location = WebInspector.DebuggerModel.Location.fromPayload(target, payload.location); 941} 942 943/** 944 * @param {!WebInspector.Target} target 945 * @param {!Array.<!DebuggerAgent.CallFrame>} callFrames 946 * @param {boolean=} isAsync 947 * @return {!Array.<!WebInspector.DebuggerModel.CallFrame>} 948 */ 949WebInspector.DebuggerModel.CallFrame.fromPayloadArray = function(target, callFrames, isAsync) 950{ 951 var result = []; 952 for (var i = 0; i < callFrames.length; ++i) { 953 var callFrame = callFrames[i]; 954 var script = target.debuggerModel.scriptForId(callFrame.location.scriptId); 955 if (script) 956 result.push(new WebInspector.DebuggerModel.CallFrame(target, script, callFrame, isAsync)); 957 } 958 return result; 959} 960 961WebInspector.DebuggerModel.CallFrame.prototype = { 962 963 /** 964 * @return {!WebInspector.Script} 965 */ 966 get script() 967 { 968 return this._script; 969 }, 970 971 /** 972 * @return {string} 973 */ 974 get type() 975 { 976 return this._payload.type; 977 }, 978 979 /** 980 * @return {string} 981 */ 982 get id() 983 { 984 return this._payload.callFrameId; 985 }, 986 987 /** 988 * @return {!Array.<!DebuggerAgent.Scope>} 989 */ 990 get scopeChain() 991 { 992 return this._payload.scopeChain; 993 }, 994 995 /** 996 * @return {?WebInspector.RemoteObject} 997 */ 998 thisObject: function() 999 { 1000 return this._payload.this ? this.target().runtimeModel.createRemoteObject(this._payload.this) : null; 1001 }, 1002 1003 /** 1004 * @return {?WebInspector.RemoteObject} 1005 */ 1006 returnValue: function() 1007 { 1008 return this._payload.returnValue ? this.target().runtimeModel.createRemoteObject(this._payload.returnValue) : null 1009 }, 1010 1011 /** 1012 * @return {string} 1013 */ 1014 get functionName() 1015 { 1016 return this._payload.functionName; 1017 }, 1018 1019 /** 1020 * @return {!WebInspector.DebuggerModel.Location} 1021 */ 1022 location: function() 1023 { 1024 return this._location; 1025 }, 1026 1027 /** 1028 * @return {boolean} 1029 */ 1030 isAsync: function() 1031 { 1032 return !!this._isAsync; 1033 }, 1034 1035 /** 1036 * @param {string} code 1037 * @param {string} objectGroup 1038 * @param {boolean} includeCommandLineAPI 1039 * @param {boolean} doNotPauseOnExceptionsAndMuteConsole 1040 * @param {boolean} returnByValue 1041 * @param {boolean} generatePreview 1042 * @param {function(?RuntimeAgent.RemoteObject, boolean=)=} callback 1043 */ 1044 evaluate: function(code, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, generatePreview, callback) 1045 { 1046 /** 1047 * @param {?Protocol.Error} error 1048 * @param {!RuntimeAgent.RemoteObject} result 1049 * @param {boolean=} wasThrown 1050 */ 1051 function didEvaluateOnCallFrame(error, result, wasThrown) 1052 { 1053 if (error) { 1054 console.error(error); 1055 callback(null, false); 1056 return; 1057 } 1058 callback(result, wasThrown); 1059 } 1060 this._debuggerAgent.evaluateOnCallFrame(this._payload.callFrameId, code, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, generatePreview, didEvaluateOnCallFrame); 1061 }, 1062 1063 /** 1064 * @param {function(?Protocol.Error=)=} callback 1065 */ 1066 restart: function(callback) 1067 { 1068 /** 1069 * @param {?Protocol.Error} error 1070 * @param {!Array.<!DebuggerAgent.CallFrame>=} callFrames 1071 * @param {!Object=} details 1072 * @param {!DebuggerAgent.StackTrace=} asyncStackTrace 1073 * @this {WebInspector.DebuggerModel.CallFrame} 1074 */ 1075 function protocolCallback(error, callFrames, details, asyncStackTrace) 1076 { 1077 if (!error) 1078 this.target().debuggerModel.callStackModified(callFrames, details, asyncStackTrace); 1079 if (callback) 1080 callback(error); 1081 } 1082 this._debuggerAgent.restartFrame(this._payload.callFrameId, protocolCallback.bind(this)); 1083 }, 1084 1085 /** 1086 * @param {function(!WebInspector.UILocation):(boolean|undefined)} updateDelegate 1087 * @return {!WebInspector.LiveLocation} 1088 */ 1089 createLiveLocation: function(updateDelegate) 1090 { 1091 var liveLocation = this._location.createLiveLocation(updateDelegate); 1092 this._liveLocations.push(liveLocation); 1093 return liveLocation; 1094 }, 1095 1096 dispose: function() 1097 { 1098 for (var i = 0; i < this._liveLocations.length; ++i) 1099 this._liveLocations[i].dispose(); 1100 this._liveLocations = []; 1101 }, 1102 1103 __proto__: WebInspector.TargetAware.prototype 1104} 1105 1106/** 1107 * @constructor 1108 * @param {!Array.<!WebInspector.DebuggerModel.CallFrame>} callFrames 1109 * @param {?WebInspector.DebuggerModel.StackTrace} asyncStackTrace 1110 * @param {string=} description 1111 */ 1112WebInspector.DebuggerModel.StackTrace = function(callFrames, asyncStackTrace, description) 1113{ 1114 this.callFrames = callFrames; 1115 this.asyncStackTrace = asyncStackTrace; 1116 this.description = description; 1117} 1118 1119/** 1120 * @param {!WebInspector.Target} target 1121 * @param {!DebuggerAgent.StackTrace=} payload 1122 * @param {boolean=} isAsync 1123 * @return {?WebInspector.DebuggerModel.StackTrace} 1124 */ 1125WebInspector.DebuggerModel.StackTrace.fromPayload = function(target, payload, isAsync) 1126{ 1127 if (!payload) 1128 return null; 1129 var callFrames = WebInspector.DebuggerModel.CallFrame.fromPayloadArray(target, payload.callFrames, isAsync); 1130 if (!callFrames.length) 1131 return null; 1132 var asyncStackTrace = WebInspector.DebuggerModel.StackTrace.fromPayload(target, payload.asyncStackTrace, true); 1133 return new WebInspector.DebuggerModel.StackTrace(callFrames, asyncStackTrace, payload.description); 1134} 1135 1136WebInspector.DebuggerModel.StackTrace.prototype = { 1137 dispose: function() 1138 { 1139 for (var i = 0; i < this.callFrames.length; ++i) 1140 this.callFrames[i].dispose(); 1141 if (this.asyncStackTrace) 1142 this.asyncStackTrace.dispose(); 1143 } 1144} 1145 1146/** 1147 * @constructor 1148 * @extends {WebInspector.TargetAware} 1149 * @param {!WebInspector.Target} target 1150 * @param {!Array.<!DebuggerAgent.CallFrame>} callFrames 1151 * @param {string} reason 1152 * @param {!Object|undefined} auxData 1153 * @param {!Array.<string>} breakpointIds 1154 * @param {!DebuggerAgent.StackTrace=} asyncStackTrace 1155 */ 1156WebInspector.DebuggerPausedDetails = function(target, callFrames, reason, auxData, breakpointIds, asyncStackTrace) 1157{ 1158 WebInspector.TargetAware.call(this, target); 1159 this.callFrames = WebInspector.DebuggerModel.CallFrame.fromPayloadArray(target, callFrames); 1160 this.reason = reason; 1161 this.auxData = auxData; 1162 this.breakpointIds = breakpointIds; 1163 this.asyncStackTrace = WebInspector.DebuggerModel.StackTrace.fromPayload(target, asyncStackTrace, true); 1164} 1165 1166WebInspector.DebuggerPausedDetails.prototype = { 1167 /** 1168 * @return {?WebInspector.RemoteObject} 1169 */ 1170 exception: function() 1171 { 1172 if (this.reason !== WebInspector.DebuggerModel.BreakReason.Exception) 1173 return null; 1174 return this.target().runtimeModel.createRemoteObject(/** @type {!RuntimeAgent.RemoteObject} */(this.auxData)); 1175 }, 1176 1177 dispose: function() 1178 { 1179 for (var i = 0; i < this.callFrames.length; ++i) 1180 this.callFrames[i].dispose(); 1181 if (this.asyncStackTrace) 1182 this.asyncStackTrace.dispose(); 1183 }, 1184 1185 __proto__: WebInspector.TargetAware.prototype 1186} 1187 1188/** 1189 * @type {!WebInspector.DebuggerModel} 1190 */ 1191WebInspector.debuggerModel; 1192