Unreviewed, rolling out r103405.
[WebKit-https.git] / Source / WebCore / inspector / front-end / DebuggerModel.js
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     this._debuggerPausedDetails = null;
38     /**
39      * @type {Object.<string, WebInspector.Script>}
40      */
41     this._scripts = {};
42
43     this._canSetScriptSource = false;
44
45     InspectorBackend.registerDebuggerDispatcher(new WebInspector.DebuggerDispatcher(this));
46 }
47
48 /**
49  * @constructor
50  * @param {Array.<DebuggerAgent.CallFrame>} callFrames
51  * @param {string} reason
52  * @param {*} auxData
53  */
54 WebInspector.DebuggerPausedDetails = function(callFrames, reason, auxData)
55 {
56     this.callFrames = callFrames;
57     this.reason = reason;
58     this.auxData = auxData;
59 }
60
61 /**
62  * @constructor
63  */
64 WebInspector.DebuggerModel.Location = function()
65 {
66     this.scriptId = null;
67 }
68
69 WebInspector.DebuggerModel.Events = {
70     DebuggerWasEnabled: "debugger-was-enabled",
71     DebuggerWasDisabled: "debugger-was-disabled",
72     DebuggerPaused: "debugger-paused",
73     DebuggerResumed: "debugger-resumed",
74     ParsedScriptSource: "parsed-script-source",
75     FailedToParseScriptSource: "failed-to-parse-script-source",
76     BreakpointResolved: "breakpoint-resolved",
77     GlobalObjectCleared: "global-object-cleared"
78 }
79
80 WebInspector.DebuggerModel.BreakReason = {
81     DOM: "DOM",
82     EventListener: "EventListener",
83     XHR: "XHR",
84     Exception: "exception"
85 }
86
87 WebInspector.DebuggerModel.prototype = {
88     enableDebugger: function()
89     {
90         function callback(error, result)
91         {
92             this._canSetScriptSource = result;
93         }
94         DebuggerAgent.canSetScriptSource(callback.bind(this));
95         DebuggerAgent.enable(this._debuggerWasEnabled.bind(this));
96     },
97
98     disableDebugger: function()
99     {
100         DebuggerAgent.disable(this._debuggerWasDisabled.bind(this));
101     },
102
103     /**
104      * @return {boolean}
105      */
106     canSetScriptSource: function()
107     {
108         return this._canSetScriptSource;
109     },
110
111     _debuggerWasEnabled: function()
112     {
113         this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.DebuggerWasEnabled);
114     },
115
116     _debuggerWasDisabled: function()
117     {
118         this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.DebuggerWasDisabled);
119     },
120
121     /**
122      * @param {DebuggerAgent.Location} location
123      */
124     continueToLocation: function(location)
125     {
126         DebuggerAgent.continueToLocation(location);
127     },
128
129     /**
130      * @param {DebuggerAgent.Location} location
131      * @param {string} condition
132      * @param {function()} callback
133      */
134     setBreakpointByScriptLocation: function(location, condition, callback)
135     {
136         var script = this.scriptForSourceID(location.scriptId);
137         if (script.sourceURL)
138             this.setBreakpoint(script.sourceURL, location.lineNumber, location.columnNumber, condition, callback);
139         else
140             this.setBreakpointBySourceId(location, condition, callback);
141     },
142
143     /**
144      * @param {string} url
145      * @param {number} lineNumber
146      * @param {number=} columnNumber
147      * @param {string=} condition
148      * @param {function(?DebuggerAgent.BreakpointId, Array.<DebuggerAgent.Location>)=} callback
149      */
150     setBreakpoint: function(url, lineNumber, columnNumber, condition, callback)
151     {
152         // Adjust column if needed.
153         var minColumnNumber = 0;
154         for (var id in this._scripts) {
155             var script = this._scripts[id];
156             if (url === script.sourceURL && lineNumber === script.lineOffset)
157                 minColumnNumber = minColumnNumber ? Math.min(minColumnNumber, script.columnOffset) : script.columnOffset;
158         }
159         columnNumber = Math.max(columnNumber, minColumnNumber);
160
161         /**
162          * @this {WebInspector.DebuggerModel}
163          * @param {?Protocol.Error} error
164          * @param {DebuggerAgent.BreakpointId} breakpointId
165          * @param {Array.<DebuggerAgent.Location>} locations
166          */
167         function didSetBreakpoint(error, breakpointId, locations)
168         {
169             if (callback)
170                 callback(error ? null : breakpointId, locations);
171         }
172         DebuggerAgent.setBreakpointByUrl(lineNumber, url, undefined, columnNumber, condition, didSetBreakpoint.bind(this));
173         WebInspector.userMetrics.ScriptsBreakpointSet.record();
174     },
175
176     /**
177      * @param {DebuggerAgent.Location} location
178      * @param {string} condition
179      * @param {function(?DebuggerAgent.BreakpointId, Array.<DebuggerAgent.Location>)=} callback
180      */
181     setBreakpointBySourceId: function(location, condition, callback)
182     {
183         /**
184          * @this {WebInspector.DebuggerModel}
185          * @param {?Protocol.Error} error
186          * @param {DebuggerAgent.BreakpointId} breakpointId
187          * @param {DebuggerAgent.Location} actualLocation
188          */
189         function didSetBreakpoint(error, breakpointId, actualLocation)
190         {
191             if (callback)
192                 callback(error ? null : breakpointId, [actualLocation]);
193         }
194         DebuggerAgent.setBreakpoint(location, condition, didSetBreakpoint.bind(this));
195         WebInspector.userMetrics.ScriptsBreakpointSet.record();
196     },
197
198     /**
199      * @param {DebuggerAgent.BreakpointId} breakpointId
200      * @param {function(?Protocol.Error)=} callback
201      */
202     removeBreakpoint: function(breakpointId, callback)
203     {
204         DebuggerAgent.removeBreakpoint(breakpointId, callback);
205     },
206
207     /**
208      * @param {DebuggerAgent.BreakpointId} breakpointId
209      * @param {DebuggerAgent.Location} location
210      */
211     _breakpointResolved: function(breakpointId, location)
212     {
213         this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.BreakpointResolved, {breakpointId: breakpointId, location: location});
214     },
215
216     _globalObjectCleared: function()
217     {
218         this._debuggerPausedDetails = null;
219         this._scripts = {};
220         this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.GlobalObjectCleared);
221     },
222
223     /**
224      * @return {Object.<string, WebInspector.Script>}
225      */
226     get scripts()
227     {
228         return this._scripts;
229     },
230
231     /**
232      * @param {DebuggerAgent.ScriptId} scriptId
233      * @return {WebInspector.Script|undefined}
234      */
235     scriptForSourceID: function(scriptId)
236     {
237         return this._scripts[scriptId];
238     },
239
240     /**
241      * @param {string} url
242      */
243     scriptsForURL: function(url)
244     {
245         return this.queryScripts(function(s) { return s.sourceURL === url; });
246     },
247
248     /**
249      * @param {function(WebInspector.Script):boolean} filter
250      */
251     queryScripts: function(filter)
252     {
253         var scripts = [];
254         for (var scriptId in this._scripts) {
255             var script = this._scripts[scriptId];
256             if (filter(script))
257                 scripts.push(script);
258         }
259         return scripts;
260     },
261
262     /**
263      * @param {DebuggerAgent.ScriptId} scriptId
264      * @param {string} newSource
265      * @param {function(?Protocol.Error)} callback
266      */
267     setScriptSource: function(scriptId, newSource, callback)
268     {
269         this._scripts[scriptId].editSource(newSource, this._didEditScriptSource.bind(this, scriptId, newSource, callback));
270     },
271
272     /**
273      * @param {DebuggerAgent.ScriptId} scriptId
274      * @param {string} newSource
275      * @param {function(?Protocol.Error)} callback
276      * @param {?Protocol.Error} error
277      * @param {Array.<DebuggerAgent.CallFrame>} callFrames
278      */
279     _didEditScriptSource: function(scriptId, newSource, callback, error, callFrames)
280     {
281         if (!error && callFrames && callFrames.length)
282             this._debuggerPausedDetails.callFrames = callFrames;
283         callback(error);
284     },
285
286     /**
287      * @return {Array.<DebuggerAgent.CallFrame>} 
288      */
289     get callFrames()
290     {
291         return this._debuggerPausedDetails ? this._debuggerPausedDetails.callFrames : null;
292     },
293
294     /**
295      * @return {?WebInspector.DebuggerPausedDetails} 
296      */
297     get debuggerPausedDetails()
298     {
299         return this._debuggerPausedDetails;
300     },
301
302     /**
303      * @param {Array.<DebuggerAgent.CallFrame>} callFrames
304      * @param {string} reason
305      * @param {*} auxData
306      */
307     _pausedScript: function(callFrames, reason, auxData)
308     {
309         this._debuggerPausedDetails = new WebInspector.DebuggerPausedDetails(callFrames, reason, auxData);
310         this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.DebuggerPaused, this._debuggerPausedDetails);
311     },
312
313     _resumedScript: function()
314     {
315         this._debuggerPausedDetails = null;
316         this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.DebuggerResumed);
317     },
318
319     /**
320      * @param {DebuggerAgent.ScriptId} scriptId
321      * @param {string} sourceURL
322      * @param {number} startLine
323      * @param {number} startColumn
324      * @param {number} endLine
325      * @param {number} endColumn
326      * @param {boolean} isContentScript
327      */
328     _parsedScriptSource: function(scriptId, sourceURL, startLine, startColumn, endLine, endColumn, isContentScript)
329     {
330         var script = new WebInspector.Script(scriptId, sourceURL, startLine, startColumn, endLine, endColumn, isContentScript);
331         this._scripts[scriptId] = script;
332         this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.ParsedScriptSource, script);
333     },
334
335     /**
336      * @param {string} sourceURL
337      * @param {string} source
338      * @param {number} startingLine
339      * @param {number} errorLine
340      * @param {string} errorMessage
341      */
342     _failedToParseScriptSource: function(sourceURL, source, startingLine, errorLine, errorMessage)
343     {
344         var script = new WebInspector.Script(null, sourceURL, startingLine, 0, 0, 0, false);
345         this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.FailedToParseScriptSource, script);
346     }
347 }
348
349 WebInspector.DebuggerModel.prototype.__proto__ = WebInspector.Object.prototype;
350
351 WebInspector.DebuggerEventTypes = {
352     JavaScriptPause: 0,
353     JavaScriptBreakpoint: 1,
354     NativeBreakpoint: 2
355 };
356
357 /**
358  * @constructor
359  * @implements {DebuggerAgent.Dispatcher}
360  * @param {WebInspector.DebuggerModel} debuggerModel
361  */
362 WebInspector.DebuggerDispatcher = function(debuggerModel)
363 {
364     this._debuggerModel = debuggerModel;
365 }
366
367 WebInspector.DebuggerDispatcher.prototype = {
368     /**
369      * @param {Array.<DebuggerAgent.CallFrame>} callFrames
370      * @param {string} reason
371      * @param {*} auxData
372      */
373     paused: function(callFrames, reason, auxData)
374     {
375         this._debuggerModel._pausedScript(callFrames, reason, auxData);
376     },
377
378     resumed: function()
379     {
380         this._debuggerModel._resumedScript();
381     },
382
383     globalObjectCleared: function()
384     {
385         this._debuggerModel._globalObjectCleared();
386     },
387
388     /**
389      * @param {DebuggerAgent.ScriptId} scriptId
390      * @param {string} sourceURL
391      * @param {number} startLine
392      * @param {number} startColumn
393      * @param {number} endLine
394      * @param {number} endColumn
395      * @param {boolean=} isContentScript
396      */
397     scriptParsed: function(scriptId, sourceURL, startLine, startColumn, endLine, endColumn, isContentScript)
398     {
399         this._debuggerModel._parsedScriptSource(scriptId, sourceURL, startLine, startColumn, endLine, endColumn, !!isContentScript);
400     },
401
402     /**
403      * @param {string} sourceURL
404      * @param {string} source
405      * @param {number} startingLine
406      * @param {number} errorLine
407      * @param {string} errorMessage
408      */
409     scriptFailedToParse: function(sourceURL, source, startingLine, errorLine, errorMessage)
410     {
411         this._debuggerModel._failedToParseScriptSource(sourceURL, source, startingLine, errorLine, errorMessage);
412     },
413
414     /**
415     * @param {DebuggerAgent.BreakpointId} breakpointId
416     * @param {DebuggerAgent.Location} location
417      */
418     breakpointResolved: function(breakpointId, location)
419     {
420         this._debuggerModel._breakpointResolved(breakpointId, location);
421     }
422 }
423
424 /**
425  * @type {?WebInspector.DebuggerModel}
426  */
427 WebInspector.debuggerModel = null;