DebuggerModel.js revision ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddb
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 31WebInspector.DebuggerModel = function() 32{ 33 this._paused = false; 34 this._callFrames = []; 35 this._breakpoints = {}; 36 this._sourceIDAndLineToBreakpointId = {}; 37 this._scripts = {}; 38 39 InspectorBackend.registerDomainDispatcher("Debugger", new WebInspector.DebuggerDispatcher(this)); 40} 41 42WebInspector.DebuggerModel.Events = { 43 DebuggerPaused: "debugger-paused", 44 DebuggerResumed: "debugger-resumed", 45 ParsedScriptSource: "parsed-script-source", 46 FailedToParseScriptSource: "failed-to-parse-script-source", 47 ScriptSourceChanged: "script-source-changed", 48 BreakpointAdded: "breakpoint-added", 49 BreakpointRemoved: "breakpoint-removed" 50} 51 52WebInspector.DebuggerModel.prototype = { 53 continueToLine: function(sourceID, lineNumber) 54 { 55 function didSetBreakpoint(breakpointId, actualLineNumber) 56 { 57 if (!breakpointId) 58 return; 59 if (this.findBreakpoint(sourceID, actualLineNumber)) { 60 InspectorBackend.removeBreakpoint(breakpointId); 61 return; 62 } 63 if ("_continueToLineBreakpointId" in this) 64 InspectorBackend.removeBreakpoint(this._continueToLineBreakpointId); 65 this._continueToLineBreakpointId = breakpointId; 66 } 67 InspectorBackend.setBreakpoint(sourceID, lineNumber, "", true, didSetBreakpoint.bind(this)); 68 if (this._paused) 69 InspectorBackend.resume(); 70 }, 71 72 setBreakpoint: function(sourceID, lineNumber, enabled, condition) 73 { 74 function didSetBreakpoint(breakpointId, actualLineNumber) 75 { 76 if (breakpointId) 77 this._breakpointSetOnBackend(breakpointId, sourceID, actualLineNumber, condition, enabled, lineNumber, false); 78 } 79 InspectorBackend.setBreakpoint(sourceID, lineNumber, condition, enabled, didSetBreakpoint.bind(this)); 80 }, 81 82 removeBreakpoint: function(breakpointId) 83 { 84 InspectorBackend.removeBreakpoint(breakpointId); 85 var breakpoint = this._breakpoints[breakpointId]; 86 delete this._breakpoints[breakpointId]; 87 delete this._sourceIDAndLineToBreakpointId[this._encodeSourceIDAndLine(breakpoint.sourceID, breakpoint.line)]; 88 this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.BreakpointRemoved, breakpointId); 89 breakpoint.dispatchEventToListeners("removed"); 90 }, 91 92 _breakpointSetOnBackend: function(breakpointId, sourceID, lineNumber, condition, enabled, originalLineNumber, restored) 93 { 94 var sourceIDAndLine = this._encodeSourceIDAndLine(sourceID, lineNumber); 95 if (sourceIDAndLine in this._sourceIDAndLineToBreakpointId) { 96 InspectorBackend.removeBreakpoint(breakpointId); 97 return; 98 } 99 100 var url = this._scripts[sourceID].sourceURL; 101 var breakpoint = new WebInspector.Breakpoint(this, breakpointId, sourceID, url, lineNumber, enabled, condition); 102 breakpoint.restored = restored; 103 breakpoint.originalLineNumber = originalLineNumber; 104 this._breakpoints[breakpointId] = breakpoint; 105 this._sourceIDAndLineToBreakpointId[sourceIDAndLine] = breakpointId; 106 this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.BreakpointAdded, breakpoint); 107 }, 108 109 breakpointForId: function(breakpointId) 110 { 111 return this._breakpoints[breakpointId]; 112 }, 113 114 queryBreakpoints: function(filter) 115 { 116 var breakpoints = []; 117 for (var id in this._breakpoints) { 118 var breakpoint = this._breakpoints[id]; 119 if (filter(breakpoint)) 120 breakpoints.push(breakpoint); 121 } 122 return breakpoints; 123 }, 124 125 findBreakpoint: function(sourceID, lineNumber) 126 { 127 var sourceIDAndLine = this._encodeSourceIDAndLine(sourceID, lineNumber); 128 var breakpointId = this._sourceIDAndLineToBreakpointId[sourceIDAndLine]; 129 return this._breakpoints[breakpointId]; 130 }, 131 132 _encodeSourceIDAndLine: function(sourceID, lineNumber) 133 { 134 return sourceID + ":" + lineNumber; 135 }, 136 137 reset: function() 138 { 139 this._paused = false; 140 this._callFrames = []; 141 this._breakpoints = {}; 142 delete this._oneTimeBreakpoint; 143 this._sourceIDAndLineToBreakpointId = {}; 144 this._scripts = {}; 145 }, 146 147 scriptForSourceID: function(sourceID) 148 { 149 return this._scripts[sourceID]; 150 }, 151 152 scriptsForURL: function(url) 153 { 154 return this.queryScripts(function(s) { return s.sourceURL === url; }); 155 }, 156 157 queryScripts: function(filter) 158 { 159 var scripts = []; 160 for (var sourceID in this._scripts) { 161 var script = this._scripts[sourceID]; 162 if (filter(script)) 163 scripts.push(script); 164 } 165 return scripts; 166 }, 167 168 editScriptSource: function(sourceID, scriptSource) 169 { 170 function didEditScriptSource(success, newBodyOrErrorMessage, callFrames) 171 { 172 if (success) { 173 if (callFrames && callFrames.length) 174 this._callFrames = callFrames; 175 this._updateScriptSource(sourceID, newBodyOrErrorMessage); 176 } else 177 WebInspector.log(newBodyOrErrorMessage, WebInspector.ConsoleMessage.MessageLevel.Warning); 178 } 179 InspectorBackend.editScriptSource(sourceID, scriptSource, didEditScriptSource.bind(this)); 180 }, 181 182 _updateScriptSource: function(sourceID, scriptSource) 183 { 184 var script = this._scripts[sourceID]; 185 var oldSource = script.source; 186 script.source = scriptSource; 187 188 // Clear and re-create breakpoints according to text diff. 189 var diff = Array.diff(oldSource.split("\n"), script.source.split("\n")); 190 for (var id in this._breakpoints) { 191 var breakpoint = this._breakpoints[id]; 192 if (breakpoint.sourceID !== sourceID) 193 continue; 194 breakpoint.remove(); 195 var lineNumber = breakpoint.line - 1; 196 var newLineNumber = diff.left[lineNumber].row; 197 if (newLineNumber === undefined) { 198 for (var i = lineNumber - 1; i >= 0; --i) { 199 if (diff.left[i].row === undefined) 200 continue; 201 var shiftedLineNumber = diff.left[i].row + lineNumber - i; 202 if (shiftedLineNumber < diff.right.length) { 203 var originalLineNumber = diff.right[shiftedLineNumber].row; 204 if (originalLineNumber === lineNumber || originalLineNumber === undefined) 205 newLineNumber = shiftedLineNumber; 206 } 207 break; 208 } 209 } 210 if (newLineNumber !== undefined) 211 this.setBreakpoint(sourceID, newLineNumber + 1, breakpoint.enabled, breakpoint.condition); 212 } 213 214 this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.ScriptSourceChanged, { sourceID: sourceID, oldSource: oldSource }); 215 }, 216 217 get callFrames() 218 { 219 return this._callFrames; 220 }, 221 222 _pausedScript: function(details) 223 { 224 this._paused = true; 225 this._callFrames = details.callFrames; 226 if ("_continueToLineBreakpointId" in this) { 227 InspectorBackend.removeBreakpoint(this._continueToLineBreakpointId); 228 delete this._continueToLineBreakpointId; 229 } 230 this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.DebuggerPaused, details); 231 }, 232 233 _resumedScript: function() 234 { 235 this._paused = false; 236 this._callFrames = []; 237 this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.DebuggerResumed); 238 }, 239 240 _parsedScriptSource: function(sourceID, sourceURL, lineOffset, columnOffset, length, scriptWorldType) 241 { 242 var script = new WebInspector.Script(sourceID, sourceURL, "", lineOffset, columnOffset, length, undefined, undefined, scriptWorldType); 243 this._scripts[sourceID] = script; 244 this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.ParsedScriptSource, sourceID); 245 }, 246 247 _failedToParseScriptSource: function(sourceURL, source, startingLine, errorLine, errorMessage) 248 { 249 var script = new WebInspector.Script(null, sourceURL, source, startingLine, errorLine, errorMessage, undefined); 250 this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.FailedToParseScriptSource, script); 251 } 252} 253 254WebInspector.DebuggerModel.prototype.__proto__ = WebInspector.Object.prototype; 255 256WebInspector.DebuggerEventTypes = { 257 JavaScriptPause: 0, 258 JavaScriptBreakpoint: 1, 259 NativeBreakpoint: 2 260}; 261 262WebInspector.DebuggerDispatcher = function(debuggerModel) 263{ 264 this._debuggerModel = debuggerModel; 265} 266 267WebInspector.DebuggerDispatcher.prototype = { 268 pausedScript: function(details) 269 { 270 this._debuggerModel._pausedScript(details); 271 }, 272 273 resumedScript: function() 274 { 275 this._debuggerModel._resumedScript(); 276 }, 277 278 debuggerWasEnabled: function() 279 { 280 WebInspector.panels.scripts.debuggerWasEnabled(); 281 }, 282 283 debuggerWasDisabled: function() 284 { 285 WebInspector.panels.scripts.debuggerWasDisabled(); 286 }, 287 288 parsedScriptSource: function(sourceID, sourceURL, lineOffset, columnOffset, length, scriptWorldType) 289 { 290 this._debuggerModel._parsedScriptSource(sourceID, sourceURL, lineOffset, columnOffset, length, scriptWorldType); 291 }, 292 293 failedToParseScriptSource: function(sourceURL, source, startingLine, errorLine, errorMessage) 294 { 295 this._debuggerModel._failedToParseScriptSource(sourceURL, source, startingLine, errorLine, errorMessage); 296 }, 297 298 breakpointResolved: function(breakpointId, sourceID, lineNumber, condition, enabled, originalLineNumber) 299 { 300 this._debuggerModel._breakpointSetOnBackend(breakpointId, sourceID, lineNumber, condition, enabled, originalLineNumber, true); 301 }, 302 303 didCreateWorker: function() 304 { 305 var workersPane = WebInspector.panels.scripts.sidebarPanes.workers; 306 workersPane.addWorker.apply(workersPane, arguments); 307 }, 308 309 didDestroyWorker: function() 310 { 311 var workersPane = WebInspector.panels.scripts.sidebarPanes.workers; 312 workersPane.removeWorker.apply(workersPane, arguments); 313 } 314} 315