5ab0e2d0cfdb67ac15869095b908e632fe23288f
[WebKit-https.git] / 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 WebInspector.DebuggerModel = function()
32 {
33     InspectorBackend.registerDomainDispatcher("Debugger", this);
34
35     this._paused = false;
36     this._breakpoints = {};
37     this._sourceIDAndLineToBreakpointId = {};
38     this._scripts = {};
39 }
40
41 WebInspector.DebuggerModel.Events = {
42     DebuggerPaused: "debugger-paused",
43     DebuggerResumed: "debugger-resumed",
44     ParsedScriptSource: "parsed-script-source",
45     FailedToParseScriptSource: "failed-to-parse-script-source",
46     BreakpointAdded: "breakpoint-added",
47     BreakpointRemoved: "breakpoint-removed"
48 }
49
50 WebInspector.DebuggerModel.prototype = {
51     continueToLine: function(sourceID, lineNumber)
52     {
53         function didSetBreakpoint(breakpointId, actualLineNumber)
54         {
55             if (!breakpointId)
56                 return;
57             if (this.findBreakpoint(sourceID, actualLineNumber)) {
58                 InspectorBackend.removeBreakpoint(breakpointId);
59                 return;
60             }
61             if ("_continueToLineBreakpointId" in this)
62                 InspectorBackend.removeBreakpoint(this._continueToLineBreakpointId);
63             this._continueToLineBreakpointId = breakpointId;
64         }
65         InspectorBackend.setBreakpoint(sourceID, lineNumber, "", true, didSetBreakpoint.bind(this));
66         if (this._paused)
67             InspectorBackend.resume();
68     },
69
70     setBreakpoint: function(sourceID, lineNumber, enabled, condition)
71     {
72         function didSetBreakpoint(breakpointId, actualLineNumber)
73         {
74             if (breakpointId)
75                 this._breakpointSetOnBackend(breakpointId, sourceID, actualLineNumber, condition, enabled, lineNumber, false);
76         }
77         InspectorBackend.setBreakpoint(sourceID, lineNumber, condition, enabled, didSetBreakpoint.bind(this));
78     },
79
80     removeBreakpoint: function(breakpointId)
81     {
82         InspectorBackend.removeBreakpoint(breakpointId);
83         var breakpoint = this._breakpoints[breakpointId];
84         delete this._breakpoints[breakpointId];
85         delete this._sourceIDAndLineToBreakpointId[this._encodeSourceIDAndLine(breakpoint.sourceID, breakpoint.line)];
86         this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.BreakpointRemoved, breakpointId);
87     },
88
89     breakpointResolved: function(breakpointId, sourceID, lineNumber, condition, enabled, originalLineNumber)
90     {
91         this._breakpointSetOnBackend(breakpointId, sourceID, lineNumber, condition, enabled, originalLineNumber, true);
92     },
93
94     _breakpointSetOnBackend: function(breakpointId, sourceID, lineNumber, condition, enabled, originalLineNumber, restored)
95     {
96         var sourceIDAndLine = this._encodeSourceIDAndLine(sourceID, lineNumber);
97         if (sourceIDAndLine in this._sourceIDAndLineToBreakpointId) {
98             InspectorBackend.removeBreakpoint(breakpointId);
99             return;
100         }
101
102         var url = this._scripts[sourceID].sourceURL;
103         var breakpoint = new WebInspector.Breakpoint(this, breakpointId, sourceID, url, lineNumber, enabled, condition);
104         breakpoint.restored = restored;
105         breakpoint.originalLineNumber = originalLineNumber;
106         this._breakpoints[breakpointId] = breakpoint;
107         this._sourceIDAndLineToBreakpointId[sourceIDAndLine] = breakpointId;
108         this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.BreakpointAdded, breakpoint);
109     },
110
111     queryBreakpoints: function(filter)
112     {
113         var breakpoints = [];
114         for (var id in this._breakpoints) {
115            var breakpoint = this._breakpoints[id];
116            if (filter(breakpoint))
117                breakpoints.push(breakpoint);
118         }
119         return breakpoints;
120     },
121
122     findBreakpoint: function(sourceID, lineNumber)
123     {
124         var sourceIDAndLine = this._encodeSourceIDAndLine(sourceID, lineNumber);
125         var breakpointId = this._sourceIDAndLineToBreakpointId[sourceIDAndLine];
126         return this._breakpoints[breakpointId];
127     },
128
129     _encodeSourceIDAndLine: function(sourceID, lineNumber)
130     {
131         return sourceID + ":" + lineNumber;
132     },
133
134     reset: function()
135     {
136         this._paused = false;
137         this._breakpoints = {};
138         delete this._oneTimeBreakpoint;
139         this._sourceIDAndLineToBreakpointId = {};
140         this._scripts = {};
141     },
142
143     scriptForSourceID: function(sourceID)
144     {
145         return this._scripts[sourceID];
146     },
147
148     scriptsForURL: function(url)
149     {
150         return this.queryScripts(function(s) { return s.sourceURL === url; });
151     },
152
153     queryScripts: function(filter)
154     {
155         var scripts = [];
156         for (var sourceID in this._scripts) {
157             var script = this._scripts[sourceID];
158             if (filter(script))
159                 scripts.push(script);
160         }
161         return scripts;
162     },
163
164     // All the methods below are InspectorBackend notification handlers.
165
166     pausedScript: function(details)
167     {
168         this._paused = true;
169         if ("_continueToLineBreakpointId" in this) {
170             InspectorBackend.removeBreakpoint(this._continueToLineBreakpointId);
171             delete this._continueToLineBreakpointId;
172         }
173         this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.DebuggerPaused, details);
174
175         if (details.eventType === WebInspector.DebuggerEventTypes.JavaScriptPause || details.eventType === WebInspector.DebuggerEventTypes.NativeBreakpoint)
176             return;
177
178         var breakpoint = this.findBreakpoint(details.callFrames[0].sourceID, details.callFrames[0].line);
179         if (!breakpoint)
180             return;
181         breakpoint.hit = true;
182         this._lastHitBreakpoint = breakpoint;
183     },
184
185     resumedScript: function()
186     {
187         this._paused = false;
188         this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.DebuggerResumed);
189
190         if (!this._lastHitBreakpoint)
191             return;
192         this._lastHitBreakpoint.hit = false;
193         delete this._lastHitBreakpoint;
194     },
195
196     attachDebuggerWhenShown: function()
197     {
198         WebInspector.panels.scripts.attachDebuggerWhenShown();
199     },
200
201     debuggerWasEnabled: function()
202     {
203         WebInspector.panels.scripts.debuggerWasEnabled();
204     },
205
206     debuggerWasDisabled: function()
207     {
208         WebInspector.panels.scripts.debuggerWasDisabled();
209     },
210
211     parsedScriptSource: function(sourceID, sourceURL, source, startingLine, scriptWorldType)
212     {
213         var script = new WebInspector.Script(sourceID, sourceURL, source, startingLine, undefined, undefined, scriptWorldType);
214         this._scripts[sourceID] = script;
215         this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.ParsedScriptSource, sourceID);
216     },
217
218     failedToParseScriptSource: function(sourceURL, source, startingLine, errorLine, errorMessage)
219     {
220         var script = new WebInspector.Script(null, sourceURL, source, startingLine, errorLine, errorMessage, undefined);
221         this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.FailedToParseScriptSource, script);
222     },
223
224     didCreateWorker: function()
225     {
226         var workersPane = WebInspector.panels.scripts.sidebarPanes.workers;
227         workersPane.addWorker.apply(workersPane, arguments);
228     },
229
230     didDestroyWorker: function()
231     {
232         var workersPane = WebInspector.panels.scripts.sidebarPanes.workers;
233         workersPane.removeWorker.apply(workersPane, arguments);
234     }
235 }
236
237 WebInspector.DebuggerModel.prototype.__proto__ = WebInspector.Object.prototype;
238
239 WebInspector.DebuggerEventTypes = {
240     JavaScriptPause: 0,
241     JavaScriptBreakpoint: 1,
242     NativeBreakpoint: 2
243 };