1/* 2 * Copyright (C) 2011 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 31WebInspector.DebuggerPresentationModel = function() 32{ 33 this._sourceFiles = {}; 34 this._messages = []; 35 this._breakpointsByDebuggerId = {}; 36 this._breakpointsWithoutSourceFile = {}; 37 38 this._presentationCallFrames = []; 39 this._selectedCallFrameIndex = 0; 40 41 WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerWasEnabled, this._debuggerWasEnabled, this); 42 WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.ParsedScriptSource, this._parsedScriptSource, this); 43 WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.FailedToParseScriptSource, this._failedToParseScriptSource, this); 44 WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.BreakpointResolved, this._breakpointResolved, this); 45 WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerPaused, this._debuggerPaused, this); 46 WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerResumed, this._debuggerResumed, this); 47 WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.Reset, this._debuggerReset, this); 48 49 new WebInspector.DebuggerPresentationModelResourceBinding(this); 50} 51 52WebInspector.DebuggerPresentationModel.Events = { 53 SourceFileAdded: "source-file-added", 54 SourceFileChanged: "source-file-changed", 55 ConsoleMessageAdded: "console-message-added", 56 BreakpointAdded: "breakpoint-added", 57 BreakpointRemoved: "breakpoint-removed", 58 DebuggerPaused: "debugger-paused", 59 DebuggerResumed: "debugger-resumed", 60 CallFrameSelected: "call-frame-selected" 61} 62 63WebInspector.DebuggerPresentationModel.prototype = { 64 _debuggerWasEnabled: function() 65 { 66 if (this._breakpointsRestored) 67 return; 68 this._restoreBreakpointsFromSettings(); 69 this._breakpointsRestored = true; 70 }, 71 72 sourceFile: function(sourceFileId) 73 { 74 return this._sourceFiles[sourceFileId]; 75 }, 76 77 sourceFileForScriptURL: function(scriptURL) 78 { 79 return this._sourceFiles[scriptURL]; 80 }, 81 82 requestSourceFileContent: function(sourceFileId, callback) 83 { 84 this._sourceFiles[sourceFileId].requestContent(callback); 85 }, 86 87 _parsedScriptSource: function(event) 88 { 89 this._addScript(event.data); 90 }, 91 92 _failedToParseScriptSource: function(event) 93 { 94 this._addScript(event.data); 95 }, 96 97 _addScript: function(script) 98 { 99 var sourceFileId = this._createSourceFileId(script.sourceURL, script.sourceID); 100 var sourceFile = this._sourceFiles[sourceFileId]; 101 if (sourceFile) { 102 sourceFile.addScript(script); 103 return; 104 } 105 106 function contentChanged(sourceFile) 107 { 108 this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.SourceFileChanged, this._sourceFiles[sourceFileId]); 109 } 110 if (!this._formatSourceFiles) 111 sourceFile = new WebInspector.SourceFile(sourceFileId, script, contentChanged.bind(this)); 112 else 113 sourceFile = new WebInspector.FormattedSourceFile(sourceFileId, script, contentChanged.bind(this), this._formatter()); 114 this._sourceFiles[sourceFileId] = sourceFile; 115 116 this._restoreBreakpoints(sourceFile); 117 118 this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.SourceFileAdded, sourceFile); 119 }, 120 121 _restoreBreakpoints: function(sourceFile) 122 { 123 var pendingBreakpoints = this._breakpointsWithoutSourceFile[sourceFile.id]; 124 for (var i = 0; pendingBreakpoints && i < pendingBreakpoints.length; ++i) { 125 var breakpointData = pendingBreakpoints[i]; 126 if ("debuggerId" in breakpointData) { 127 var breakpoint = new WebInspector.PresentationBreakpoint(sourceFile, breakpointData.lineNumber, breakpointData.condition, breakpointData.enabled); 128 this._bindDebuggerId(breakpoint, breakpointData.debuggerId); 129 this._breakpointAdded(breakpoint); 130 } else 131 this.setBreakpoint(sourceFile.id, breakpointData.lineNumber, breakpointData.condition, breakpointData.enabled, true); 132 } 133 delete this._breakpointsWithoutSourceFile[sourceFile.id]; 134 }, 135 136 canEditScriptSource: function(sourceFileId) 137 { 138 if (!Preferences.canEditScriptSource || this._formatSourceFiles) 139 return false; 140 var script = this._scriptForSourceFileId(sourceFileId); 141 return !script.lineOffset && !script.columnOffset; 142 }, 143 144 editScriptSource: function(sourceFileId, newSource, callback) 145 { 146 var script = this._scriptForSourceFileId(sourceFileId); 147 var sourceFile = this._sourceFiles[sourceFileId]; 148 149 function didEditScriptSource(oldSource, error) 150 { 151 if (!error) { 152 sourceFile.content = newSource; 153 154 var resource = WebInspector.resourceForURL(sourceFile.url); 155 if (resource) 156 resource.addRevision(newSource); 157 } 158 159 callback(error); 160 161 if (!error && WebInspector.debuggerModel.callFrames) 162 this._debuggerPaused(); 163 } 164 165 var oldSource = sourceFile.requestContent(didReceiveSource.bind(this)); 166 function didReceiveSource(oldSource) 167 { 168 WebInspector.debuggerModel.editScriptSource(script.sourceID, newSource, didEditScriptSource.bind(this, oldSource)); 169 } 170 }, 171 172 _updateBreakpointsAfterLiveEdit: function(sourceFileId, oldSource, newSource) 173 { 174 var sourceFile = this._sourceFiles[sourceFileId]; 175 176 // Clear and re-create breakpoints according to text diff. 177 var diff = Array.diff(oldSource.split("\n"), newSource.split("\n")); 178 for (var lineNumber in sourceFile.breakpoints) { 179 var breakpoint = sourceFile.breakpoints[lineNumber]; 180 181 var lineNumber = breakpoint.lineNumber; 182 this.removeBreakpoint(sourceFileId, lineNumber); 183 184 var newLineNumber = diff.left[lineNumber].row; 185 if (newLineNumber === undefined) { 186 for (var i = lineNumber - 1; i >= 0; --i) { 187 if (diff.left[i].row === undefined) 188 continue; 189 var shiftedLineNumber = diff.left[i].row + lineNumber - i; 190 if (shiftedLineNumber < diff.right.length) { 191 var originalLineNumber = diff.right[shiftedLineNumber].row; 192 if (originalLineNumber === lineNumber || originalLineNumber === undefined) 193 newLineNumber = shiftedLineNumber; 194 } 195 break; 196 } 197 } 198 if (newLineNumber !== undefined) 199 this.setBreakpoint(sourceFileId, newLineNumber, breakpoint.condition, breakpoint.enabled); 200 } 201 }, 202 203 toggleFormatSourceFiles: function() 204 { 205 this._formatSourceFiles = !this._formatSourceFiles; 206 207 for (var id in this._sourceFiles) { 208 var sourceFile = this._sourceFiles[id]; 209 for (var line in sourceFile.breakpoints) 210 this._removeBreakpointFromDebugger(sourceFile.breakpoints[line]); 211 } 212 213 var messages = this._messages; 214 this._reset(); 215 216 var scripts = WebInspector.debuggerModel.scripts; 217 for (var id in scripts) 218 this._addScript(scripts[id]); 219 220 for (var i = 0; i < messages.length; ++i) 221 this.addConsoleMessage(messages[i]); 222 223 if (WebInspector.debuggerModel.callFrames) 224 this._debuggerPaused(); 225 }, 226 227 formatSourceFilesToggled: function() 228 { 229 return this._formatSourceFiles; 230 }, 231 232 _formatter: function() 233 { 234 if (!this._scriptFormatter) 235 this._scriptFormatter = new WebInspector.ScriptFormatter(); 236 return this._scriptFormatter; 237 }, 238 239 addConsoleMessage: function(message) 240 { 241 this._messages.push(message); 242 243 var sourceFile = this._sourceFileForScript(message.url); 244 if (!sourceFile) 245 return; 246 247 function didRequestSourceMapping(mapping) 248 { 249 var presentationMessage = {}; 250 presentationMessage.sourceFileId = sourceFile.id; 251 presentationMessage.lineNumber = mapping.scriptLocationToSourceLine({lineNumber:message.line - 1, columnNumber:0}); 252 presentationMessage.originalMessage = message; 253 sourceFile.messages.push(presentationMessage); 254 this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.ConsoleMessageAdded, presentationMessage); 255 } 256 sourceFile.requestSourceMapping(didRequestSourceMapping.bind(this)); 257 }, 258 259 clearConsoleMessages: function() 260 { 261 this._messages = []; 262 for (var id in this._sourceFiles) 263 this._sourceFiles[id].messages = []; 264 }, 265 266 continueToLine: function(sourceFileId, lineNumber) 267 { 268 function didRequestSourceMapping(mapping) 269 { 270 var location = mapping.sourceLineToScriptLocation(lineNumber); 271 WebInspector.debuggerModel.continueToLocation(location); 272 } 273 this._sourceFiles[sourceFileId].requestSourceMapping(didRequestSourceMapping.bind(this)); 274 }, 275 276 breakpointsForSourceFileId: function(sourceFileId) 277 { 278 var sourceFile = this.sourceFile(sourceFileId); 279 if (!sourceFile) 280 return []; 281 var breakpoints = []; 282 for (var lineNumber in sourceFile.breakpoints) 283 breakpoints.push(sourceFile.breakpoints[lineNumber]); 284 return breakpoints; 285 }, 286 287 setBreakpoint: function(sourceFileId, lineNumber, condition, enabled, dontSaveBreakpoints) 288 { 289 var sourceFile = this._sourceFiles[sourceFileId]; 290 if (!sourceFile) 291 return; 292 293 var breakpoint = new WebInspector.PresentationBreakpoint(sourceFile, lineNumber, condition, enabled); 294 if (!enabled) { 295 this._breakpointAdded(breakpoint); 296 if (!dontSaveBreakpoints) 297 this._saveBreakpoints(); 298 return; 299 } 300 301 function callback() 302 { 303 this._breakpointAdded(breakpoint); 304 if (!dontSaveBreakpoints) 305 this._saveBreakpoints(); 306 } 307 this._setBreakpointInDebugger(breakpoint, callback.bind(this)); 308 }, 309 310 _setBreakpointInDebugger: function(breakpoint, callback) 311 { 312 function didSetBreakpoint(breakpointId, locations) 313 { 314 if (!breakpointId) 315 return; 316 317 this._bindDebuggerId(breakpoint, breakpointId); 318 breakpoint.location = locations[0]; 319 callback(); 320 } 321 322 function didRequestSourceMapping(mapping) 323 { 324 var location = mapping.sourceLineToScriptLocation(breakpoint.lineNumber); 325 var script = WebInspector.debuggerModel.scriptForSourceID(location.sourceID); 326 if (script.sourceURL) 327 WebInspector.debuggerModel.setBreakpoint(script.sourceURL, location.lineNumber, location.columnNumber, breakpoint.condition, didSetBreakpoint.bind(this)); 328 else { 329 location.sourceID = script.sourceID; 330 WebInspector.debuggerModel.setBreakpointBySourceId(location, breakpoint.condition, didSetBreakpoint.bind(this)); 331 } 332 } 333 breakpoint.sourceFile.requestSourceMapping(didRequestSourceMapping.bind(this)); 334 }, 335 336 _removeBreakpointFromDebugger: function(breakpoint, callback) 337 { 338 if (!("debuggerId" in breakpoint)) { 339 if (callback) 340 callback(); 341 return; 342 } 343 344 function didRemoveBreakpoint() 345 { 346 this._unbindDebuggerId(breakpoint); 347 if (callback) 348 callback(); 349 } 350 WebInspector.debuggerModel.removeBreakpoint(breakpoint.debuggerId, didRemoveBreakpoint.bind(this)); 351 }, 352 353 _bindDebuggerId: function(breakpoint, debuggerId) 354 { 355 breakpoint.debuggerId = debuggerId; 356 this._breakpointsByDebuggerId[debuggerId] = breakpoint; 357 }, 358 359 _unbindDebuggerId: function(breakpoint) 360 { 361 delete this._breakpointsByDebuggerId[breakpoint.debuggerId]; 362 delete breakpoint.debuggerId; 363 }, 364 365 setBreakpointEnabled: function(sourceFileId, lineNumber, enabled) 366 { 367 var breakpoint = this.findBreakpoint(sourceFileId, lineNumber); 368 if (!breakpoint) 369 return; 370 371 this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.BreakpointRemoved, breakpoint); 372 373 breakpoint.enabled = enabled; 374 375 function afterUpdate() 376 { 377 this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.BreakpointAdded, breakpoint); 378 this._saveBreakpoints(); 379 } 380 381 if (!enabled) 382 this._removeBreakpointFromDebugger(breakpoint, afterUpdate.call(this)); 383 else 384 this._setBreakpointInDebugger(breakpoint, afterUpdate.bind(this)); 385 }, 386 387 updateBreakpoint: function(sourceFileId, lineNumber, condition, enabled) 388 { 389 this.removeBreakpoint(sourceFileId, lineNumber); 390 this.setBreakpoint(sourceFileId, lineNumber, condition, enabled); 391 }, 392 393 removeBreakpoint: function(sourceFileId, lineNumber) 394 { 395 var breakpoint = this.findBreakpoint(sourceFileId, lineNumber); 396 if (!breakpoint) 397 return; 398 399 function callback() 400 { 401 this._breakpointRemoved(breakpoint); 402 this._saveBreakpoints(); 403 } 404 this._removeBreakpointFromDebugger(breakpoint, callback.bind(this)); 405 }, 406 407 findBreakpoint: function(sourceFileId, lineNumber) 408 { 409 var sourceFile = this.sourceFile(sourceFileId); 410 if (sourceFile) 411 return sourceFile.breakpoints[lineNumber]; 412 }, 413 414 _breakpointAdded: function(breakpoint) 415 { 416 var sourceFile = breakpoint.sourceFile; 417 if (!sourceFile) 418 return; 419 420 function didRequestSourceMapping(mapping) 421 { 422 // Refine line number based on resolved location. 423 if (breakpoint.location) 424 breakpoint.lineNumber = mapping.scriptLocationToSourceLine(breakpoint.location); 425 426 var existingBreakpoint = this.findBreakpoint(sourceFile.id, breakpoint.lineNumber); 427 if (existingBreakpoint) { 428 // We can't show more than one breakpoint on a single source file line. 429 this._removeBreakpointFromDebugger(breakpoint); 430 return; 431 } 432 sourceFile.breakpoints[breakpoint.lineNumber] = breakpoint; 433 this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.BreakpointAdded, breakpoint); 434 } 435 sourceFile.requestSourceMapping(didRequestSourceMapping.bind(this)); 436 }, 437 438 _breakpointRemoved: function(breakpoint) 439 { 440 var sourceFile = breakpoint.sourceFile; 441 if (sourceFile.breakpoints[breakpoint.lineNumber] === breakpoint) { 442 // There can already be a newer breakpoint; 443 delete sourceFile.breakpoints[breakpoint.lineNumber]; 444 this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.BreakpointRemoved, breakpoint); 445 } 446 }, 447 448 _breakpointResolved: function(event) 449 { 450 var debuggerId = event.data.breakpointId; 451 if (!(debuggerId in this._breakpointsByDebuggerId)) 452 return; 453 var breakpoint = this._breakpointsByDebuggerId[debuggerId]; 454 455 this._breakpointRemoved(breakpoint); 456 breakpoint.location = event.data.location; 457 this._breakpointAdded(breakpoint); 458 }, 459 460 _restoreBreakpointsFromSettings: function() 461 { 462 var breakpoints = WebInspector.settings.breakpoints; 463 for (var i = 0; i < breakpoints.length; ++i) { 464 var breakpointData = breakpoints[i]; 465 var sourceFileId = breakpointData.sourceFileId; 466 if (!sourceFileId) 467 continue; 468 var sourceFile = this._sourceFiles[sourceFileId]; 469 if (sourceFile) { 470 this.setBreakpoint(sourceFileId, breakpointData.lineNumber, breakpointData.condition, breakpointData.enabled); 471 continue; 472 } 473 474 // Add breakpoint once source file becomes available. 475 var pendingBreakpoints = this._breakpointsWithoutSourceFile[sourceFileId]; 476 if (!pendingBreakpoints) { 477 pendingBreakpoints = []; 478 this._breakpointsWithoutSourceFile[sourceFileId] = pendingBreakpoints; 479 } 480 pendingBreakpoints.push(breakpointData); 481 } 482 }, 483 484 _saveBreakpoints: function() 485 { 486 var serializedBreakpoints = []; 487 488 // Store added breakpoints. 489 for (var sourceFileId in this._sourceFiles) { 490 var sourceFile = this._sourceFiles[sourceFileId]; 491 if (!sourceFile.url) 492 continue; 493 494 for (var lineNumber in sourceFile.breakpoints) 495 serializedBreakpoints.push(sourceFile.breakpoints[lineNumber].serialize()); 496 } 497 498 // Store not added breakpoints. 499 for (var sourceFileId in this._breakpointsWithoutSourceFile) 500 serializedBreakpoints = serializedBreakpoints.concat(this._breakpointsWithoutSourceFile[sourceFileId]); 501 502 // Sanitize debugger ids. 503 for (var i = 0; i < serializedBreakpoints.length; ++i) { 504 var breakpoint = serializedBreakpoints[i]; 505 var breakpointCopy = {}; 506 for (var property in breakpoint) { 507 if (property !== "debuggerId") 508 breakpointCopy[property] = breakpoint[property]; 509 } 510 serializedBreakpoints[i] = breakpointCopy; 511 } 512 513 WebInspector.settings.breakpoints = serializedBreakpoints; 514 }, 515 516 _debuggerPaused: function() 517 { 518 var callFrames = WebInspector.debuggerModel.callFrames; 519 this._presentationCallFrames = []; 520 for (var i = 0; i < callFrames.length; ++i) { 521 var callFrame = callFrames[i]; 522 var sourceFile; 523 var script = WebInspector.debuggerModel.scriptForSourceID(callFrame.location.sourceID); 524 if (script) 525 sourceFile = this._sourceFileForScript(script.sourceURL, script.sourceID); 526 this._presentationCallFrames.push(new WebInspector.PresenationCallFrame(callFrame, i, sourceFile)); 527 } 528 var details = WebInspector.debuggerModel.debuggerPausedDetails; 529 this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.DebuggerPaused, { callFrames: this._presentationCallFrames, details: details }); 530 531 this.selectedCallFrame = this._presentationCallFrames[this._selectedCallFrameIndex]; 532 }, 533 534 _debuggerResumed: function() 535 { 536 this._presentationCallFrames = []; 537 this._selectedCallFrameIndex = 0; 538 this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.DebuggerResumed); 539 }, 540 541 set selectedCallFrame(callFrame) 542 { 543 this._selectedCallFrameIndex = callFrame.index; 544 callFrame.select(); 545 this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.CallFrameSelected, callFrame); 546 }, 547 548 get selectedCallFrame() 549 { 550 return this._presentationCallFrames[this._selectedCallFrameIndex]; 551 }, 552 553 _sourceFileForScript: function(sourceURL, sourceID) 554 { 555 return this._sourceFiles[this._createSourceFileId(sourceURL, sourceID)]; 556 }, 557 558 _scriptForSourceFileId: function(sourceFileId) 559 { 560 function filter(script) 561 { 562 return this._createSourceFileId(script.sourceURL, script.sourceID) === sourceFileId; 563 } 564 return WebInspector.debuggerModel.queryScripts(filter.bind(this))[0]; 565 }, 566 567 _createSourceFileId: function(sourceURL, sourceID) 568 { 569 var prefix = this._formatSourceFiles ? "deobfuscated:" : ""; 570 return prefix + (sourceURL || sourceID); 571 }, 572 573 _reset: function() 574 { 575 for (var id in this._sourceFiles) { 576 var sourceFile = this._sourceFiles[id]; 577 for (var line in sourceFile.breakpoints) { 578 var breakpoints = this._breakpointsWithoutSourceFile[sourceFile.id]; 579 if (!breakpoints) { 580 breakpoints = []; 581 this._breakpointsWithoutSourceFile[sourceFile.id] = breakpoints; 582 } 583 breakpoints.push(sourceFile.breakpoints[line].serialize()); 584 } 585 } 586 587 this._sourceFiles = {}; 588 this._messages = []; 589 this._breakpointsByDebuggerId = {}; 590 }, 591 592 _debuggerReset: function() 593 { 594 this._reset(); 595 this._presentationCallFrames = []; 596 this._selectedCallFrameIndex = 0; 597 } 598} 599 600WebInspector.DebuggerPresentationModel.prototype.__proto__ = WebInspector.Object.prototype; 601 602WebInspector.PresentationBreakpoint = function(sourceFile, lineNumber, condition, enabled) 603{ 604 this.sourceFile = sourceFile; 605 this.sourceFileId = sourceFile.id; 606 this.lineNumber = lineNumber; 607 this.condition = condition; 608 this.enabled = enabled; 609} 610 611WebInspector.PresentationBreakpoint.prototype = { 612 get url() 613 { 614 return this.sourceFile.url; 615 }, 616 617 get resolved() 618 { 619 return !!this.location; 620 }, 621 622 loadSnippet: function(callback) 623 { 624 function didRequestContent(mimeType, content) 625 { 626 var lineEndings = content.lineEndings(); 627 var snippet = ""; 628 if (this.lineNumber < lineEndings.length) 629 snippet = content.substring(lineEndings[this.lineNumber - 1], lineEndings[this.lineNumber]); 630 callback(snippet); 631 } 632 if (!this.sourceFile) { 633 callback(WebInspector.UIString("N/A")); 634 return; 635 } 636 this.sourceFile.requestContent(didRequestContent.bind(this)); 637 }, 638 639 serialize: function() 640 { 641 var serializedBreakpoint = {}; 642 serializedBreakpoint.sourceFileId = this.sourceFile.id; 643 serializedBreakpoint.lineNumber = this.lineNumber; 644 serializedBreakpoint.condition = this.condition; 645 serializedBreakpoint.enabled = this.enabled; 646 serializedBreakpoint.debuggerId = this.debuggerId; 647 return serializedBreakpoint; 648 } 649} 650 651WebInspector.PresenationCallFrame = function(callFrame, index, sourceFile) 652{ 653 this._callFrame = callFrame; 654 this._index = index; 655 this._sourceFile = sourceFile; 656 this._script = WebInspector.debuggerModel.scriptForSourceID(callFrame.location.sourceID); 657} 658 659WebInspector.PresenationCallFrame.prototype = { 660 get functionName() 661 { 662 return this._callFrame.functionName; 663 }, 664 665 get type() 666 { 667 return this._callFrame.type; 668 }, 669 670 get isInternalScript() 671 { 672 return !this._script; 673 }, 674 675 get url() 676 { 677 if (this._sourceFile) 678 return this._sourceFile.url; 679 }, 680 681 get scopeChain() 682 { 683 return this._callFrame.scopeChain; 684 }, 685 686 get index() 687 { 688 return this._index; 689 }, 690 691 select: function() 692 { 693 if (this._sourceFile) 694 this._sourceFile.forceLoadContent(this._script); 695 }, 696 697 evaluate: function(code, objectGroup, includeCommandLineAPI, callback) 698 { 699 function didEvaluateOnCallFrame(error, result) 700 { 701 callback(WebInspector.RemoteObject.fromPayload(result)); 702 } 703 DebuggerAgent.evaluateOnCallFrame(this._callFrame.id, code, objectGroup, includeCommandLineAPI, didEvaluateOnCallFrame.bind(this)); 704 }, 705 706 sourceLine: function(callback) 707 { 708 if (!this._sourceFile) { 709 callback(undefined, this._callFrame.location.lineNumber); 710 return; 711 } 712 713 function didRequestSourceMapping(mapping) 714 { 715 callback(this._sourceFile.id, mapping.scriptLocationToSourceLine(this._callFrame.location)); 716 } 717 this._sourceFile.requestSourceMapping(didRequestSourceMapping.bind(this)); 718 } 719} 720 721WebInspector.DebuggerPresentationModelResourceBinding = function(model) 722{ 723 this._presentationModel = model; 724 WebInspector.Resource.registerDomainModelBinding(WebInspector.Resource.Type.Script, this); 725} 726 727WebInspector.DebuggerPresentationModelResourceBinding.prototype = { 728 canSetContent: function(resource) 729 { 730 var sourceFile = this._presentationModel._sourceFileForScript(resource.url) 731 if (!sourceFile) 732 return false; 733 return this._presentationModel.canEditScriptSource(sourceFile.id); 734 }, 735 736 setContent: function(resource, content, majorChange, userCallback) 737 { 738 if (!majorChange) 739 return; 740 741 var sourceFile = this._presentationModel._sourceFileForScript(resource.url); 742 if (!sourceFile) { 743 userCallback("Resource is not editable"); 744 return; 745 } 746 747 resource.requestContent(this._setContentWithInitialContent.bind(this, sourceFile, content, userCallback)); 748 }, 749 750 _setContentWithInitialContent: function(sourceFile, content, userCallback, oldContent) 751 { 752 function callback(error) 753 { 754 if (userCallback) 755 userCallback(error); 756 if (!error) { 757 this._presentationModel._updateBreakpointsAfterLiveEdit(sourceFile.id, oldContent, content); 758 sourceFile.reload(); 759 } 760 } 761 this._presentationModel.editScriptSource(sourceFile.id, content, callback.bind(this)); 762 } 763} 764 765WebInspector.DebuggerPresentationModelResourceBinding.prototype.__proto__ = WebInspector.ResourceDomainModelBinding.prototype; 766