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