2010-12-22 Sheriff Bot <webkit.review.bot@gmail.com>
[WebKit-https.git] / WebCore / bindings / v8 / DebuggerScript.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 (function () {
32
33 var DebuggerScript = {};
34 DebuggerScript._breakpoints = {};
35
36 DebuggerScript.PauseOnExceptionsState = {
37     DontPauseOnExceptions : 0,
38     PauseOnAllExceptions : 1,
39     PauseOnUncaughtExceptions: 2
40 };
41
42 DebuggerScript.ScriptWorldType = {
43     MainWorld : 0,
44     ExtensionsWorld : 1
45 };
46
47 DebuggerScript._pauseOnExceptionsState = DebuggerScript.PauseOnExceptionsState.DontPauseOnExceptions;
48 Debug.clearBreakOnException();
49 Debug.clearBreakOnUncaughtException();
50
51 DebuggerScript.getAfterCompileScript = function(eventData)
52 {
53     return DebuggerScript._formatScript(eventData.script_.script_);
54 }
55
56 DebuggerScript.getScripts = function(contextData)
57 {
58     var result = [];
59
60     if (!contextData)
61         return result;
62     var comma = contextData.indexOf(",");
63     if (comma === -1)
64         return result;
65     // Context data is a string in the following format:
66     // ("page"|"injected")","<page id>
67     var idSuffix = contextData.substring(comma); // including the comma
68
69     var scripts = Debug.scripts();
70     for (var i = 0; i < scripts.length; ++i) {
71         var script = scripts[i];
72         if (script.context_data && script.context_data.lastIndexOf(idSuffix) != -1)
73             result.push(DebuggerScript._formatScript(script));
74     }
75     return result;
76 }
77
78 DebuggerScript._formatScript = function(script)
79 {
80     var scriptWorldType = DebuggerScript.ScriptWorldType.MainWorld;
81     if (script.context_data && script.context_data.indexOf("injected") == 0)
82         scriptWorldType = DebuggerScript.ScriptWorldType.ExtensionsWorld;
83     return {
84         id: script.id,
85         name: script.nameOrSourceURL(),
86         source: script.source,
87         lineOffset: DebuggerScript._v8ToWebkitLineNumber(script.line_offset),
88         lineCount: script.lineCount(),
89         scriptWorldType: scriptWorldType
90     };
91 }
92
93 DebuggerScript.setBreakpoint = function(execState, args)
94 {
95     args.lineNumber = DebuggerScript._webkitToV8LineNumber(args.lineNumber);
96     var breakId = Debug.setScriptBreakPointById(args.scriptId, args.lineNumber, 0 /* column */, args.condition);
97     if (!args.enabled)
98         Debug.disableScriptBreakPoint(breakId);
99
100     var locations = Debug.findBreakPointActualLocations(breakId);
101     if (!locations.length)
102         return undefined;
103     var actualLineNumber = locations[0].line;
104
105     var key = args.scriptId + ":" + actualLineNumber;
106     if (key in DebuggerScript._breakpoints) {
107         // Remove old breakpoint.
108         Debug.findBreakPoint(DebuggerScript._breakpoints[key], true);
109     }
110     DebuggerScript._breakpoints[key] = breakId;
111     return DebuggerScript._v8ToWebkitLineNumber(actualLineNumber);
112 }
113
114 DebuggerScript.removeBreakpoint = function(execState, args)
115 {
116     args.lineNumber = DebuggerScript._webkitToV8LineNumber(args.lineNumber);
117     var key = args.scriptId + ":" + args.lineNumber;
118     var breakId = DebuggerScript._breakpoints[key];
119     if (breakId)
120         Debug.findBreakPoint(breakId, true);
121     delete DebuggerScript._breakpoints[key];
122 }
123
124 DebuggerScript.pauseOnExceptionsState = function()
125 {
126     return DebuggerScript._pauseOnExceptionsState;
127 }
128
129 DebuggerScript.setPauseOnExceptionsState = function(newState)
130 {
131     DebuggerScript._pauseOnExceptionsState = newState;
132
133     if (DebuggerScript.PauseOnExceptionsState.PauseOnAllExceptions === newState)
134         Debug.setBreakOnException();
135     else
136         Debug.clearBreakOnException();
137
138     if (DebuggerScript.PauseOnExceptionsState.PauseOnUncaughtExceptions === newState)
139         Debug.setBreakOnUncaughtException();
140     else
141         Debug.clearBreakOnUncaughtException();
142 }
143
144 DebuggerScript.currentCallFrame = function(execState, args)
145 {
146     var frameCount = execState.frameCount();
147     if (frameCount === 0)
148         return undefined;
149
150     var topFrame;
151     for (var i = frameCount - 1; i >= 0; i--) {
152         var frameMirror = execState.frame(i);
153         topFrame = DebuggerScript._frameMirrorToJSCallFrame(frameMirror, topFrame);
154     }
155     return topFrame;
156 }
157
158 DebuggerScript.stepIntoStatement = function(execState)
159 {
160     execState.prepareStep(Debug.StepAction.StepIn, 1);
161 }
162
163 DebuggerScript.stepOverStatement = function(execState)
164 {
165     execState.prepareStep(Debug.StepAction.StepNext, 1);
166 }
167
168 DebuggerScript.stepOutOfFunction = function(execState)
169 {
170     execState.prepareStep(Debug.StepAction.StepOut, 1);
171 }
172
173 DebuggerScript.editScriptSource = function(scriptId, newSource)
174 {
175     var scripts = Debug.scripts();
176     var scriptToEdit = null;
177     for (var i = 0; i < scripts.length; i++) {
178         if (scripts[i].id == scriptId) {
179             scriptToEdit = scripts[i];
180             break;
181         }
182     }
183     if (!scriptToEdit)
184         throw("Script not found");
185
186     var changeLog = [];
187     Debug.LiveEdit.SetScriptSource(scriptToEdit, newSource, false, changeLog);
188     return scriptToEdit.source;
189 }
190
191 DebuggerScript.clearBreakpoints = function(execState, args)
192 {
193     for (var key in DebuggerScript._breakpoints) {
194         var breakId = DebuggerScript._breakpoints[key];
195         Debug.findBreakPoint(breakId, true);
196     }
197     DebuggerScript._breakpoints = {};
198 }
199
200 DebuggerScript.setBreakpointsActivated = function(execState, args)
201 {
202     Debug.debuggerFlags().breakPointsActive.setValue(args.enabled);
203 }
204
205 DebuggerScript._frameMirrorToJSCallFrame = function(frameMirror, callerFrame)
206 {
207     // Get function name.
208     var func;
209     try {
210         func = frameMirror.func();
211     } catch(e) {
212     }
213     var functionName;
214     if (func)
215         functionName = func.name() || func.inferredName();
216
217     // Get script ID.
218     var script = func.script();
219     var sourceID = script && script.id();
220
221     // Get line number.
222     var line = DebuggerScript._v8ToWebkitLineNumber(frameMirror.sourceLine());
223
224     // Get this object.
225     var thisObject = frameMirror.details_.receiver();
226
227     // Get scope chain array in format: [<scope type>, <scope object>, <scope type>, <scope object>,...]
228     var scopeChain = [];
229     var scopeType = [];
230     for (var i = 0; i < frameMirror.scopeCount(); i++) {
231         var scopeMirror = frameMirror.scope(i);
232         var scopeObjectMirror = scopeMirror.scopeObject();
233
234         var scopeObject;
235         switch (scopeMirror.scopeType()) {
236         case ScopeType.Local:
237         case ScopeType.Closure:
238             // For transient objects we create a "persistent" copy that contains
239             // the same properties.
240             scopeObject = {};
241             // Reset scope object prototype to null so that the proto properties
242             // don't appear in the local scope section.
243             scopeObject.__proto__ = null;
244             var properties = scopeObjectMirror.properties();
245             for (var j = 0; j < properties.length; j++) {
246                 var name = properties[j].name();
247                 if (name.charAt(0) === ".")
248                     continue; // Skip internal variables like ".arguments"
249                 scopeObject[name] = properties[j].value_;
250             }
251             break;
252         case ScopeType.Global:
253         case ScopeType.With:
254         case ScopeType.Catch:
255             scopeObject = scopeMirror.details_.object();
256             break;
257         }
258
259         scopeType.push(scopeMirror.scopeType());
260         scopeChain.push(scopeObject);
261     }
262
263     function evaluate(expression) {
264         return frameMirror.evaluate(expression, false).value();
265     }
266
267     return {
268         "sourceID": sourceID,
269         "line": line,
270         "functionName": functionName,
271         "type": "function",
272         "thisObject": thisObject,
273         "scopeChain": scopeChain,
274         "scopeType": scopeType,
275         "evaluate": evaluate,
276         "caller": callerFrame
277     };
278 }
279
280 DebuggerScript._webkitToV8LineNumber = function(line)
281 {
282     return line - 1;
283 };
284
285 DebuggerScript._v8ToWebkitLineNumber = function(line)
286 {
287     return line + 1;
288 };
289
290 return DebuggerScript;
291
292 })();