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