d361c2d8821ebe79191afb983f16f369cbf38f17
[WebKit-https.git] / WebKit / mac / WebView / WebScriptDebugDelegate.mm
1 /*
2  * Copyright (C) 2005 Apple Computer, 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
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer. 
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution. 
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission. 
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #import "WebScriptDebugDelegatePrivate.h"
30
31 #import "WebCoreScriptDebuggerImp.h"
32 #import "WebDataSource.h"
33 #import "WebDataSourceInternal.h"
34 #import "WebFrameBridge.h"
35 #import "WebFrameInternal.h"
36 #import "WebScriptDebugServerPrivate.h"
37 #import "WebViewInternal.h"
38 #import <JavaScriptCore/ExecState.h>
39 #import <JavaScriptCore/JSGlobalObject.h>
40 #import <JavaScriptCore/function.h>
41 #import <JavaScriptCore/interpreter.h>
42 #import <WebCore/Frame.h>
43 #import <WebCore/WebScriptObjectPrivate.h>
44 #import <WebCore/runtime_root.h>
45
46 using namespace KJS;
47 using namespace WebCore;
48
49 // FIXME: these error strings should be public for future use by WebScriptObject and in WebScriptObject.h
50 NSString * const WebScriptErrorDomain = @"WebScriptErrorDomain";
51 NSString * const WebScriptErrorDescriptionKey = @"WebScriptErrorDescription";
52 NSString * const WebScriptErrorLineNumberKey = @"WebScriptErrorLineNumber";
53
54 @interface WebScriptCallFrame (WebScriptDebugDelegateInternal)
55
56 - (WebScriptCallFrame *)_initWithGlobalObject:(WebScriptObject *)globalObj caller:(WebScriptCallFrame *)caller state:(ExecState *)state;
57 - (id)_convertValueToObjcValue:(JSValue *)value;
58
59 @end
60
61 @implementation WebScriptDebugger
62
63 - (WebScriptDebugger *)initWithWebFrame:(WebFrame *)webFrame
64 {
65     if ((self = [super init])) {
66         _webFrame = webFrame;
67         _debugger = new WebCoreScriptDebuggerImp(self, [[self globalObject] _rootObject]->globalObject());
68     }
69     return self;
70 }
71
72 - (void)dealloc
73 {
74     delete _debugger;
75     [_current release];
76     [super dealloc];
77 }
78
79 - (WebFrame *)webFrame
80 {
81     return _webFrame;
82 }
83
84 - (WebScriptObject *)globalObject
85 {
86     return core(_webFrame)->windowScriptObject();
87 }
88
89 - (WebScriptCallFrame *)enterFrame:(ExecState*)state;
90 {
91     _current = [[WebScriptCallFrame alloc] _initWithGlobalObject:[self globalObject] caller:_current state:state];
92     return _current;
93 }
94
95 - (WebScriptCallFrame *)leaveFrame;
96 {
97     WebScriptCallFrame *caller = [[_current caller] retain];
98     [_current release];
99     return _current = caller;
100 }
101
102 @end
103
104 @interface WebScriptCallFramePrivate : NSObject {
105 @public
106     WebScriptObject        *globalObject;   // the global object's proxy (not retained)
107     WebScriptCallFrame     *caller;         // previous stack frame
108     KJS::ExecState         *state;
109 }
110 @end
111
112 @implementation WebScriptCallFramePrivate
113 - (void)dealloc
114 {
115     [caller release];
116     [super dealloc];
117 }
118 @end
119
120 // WebScriptCallFrame
121 //
122 // One of these is created to represent each stack frame.  Additionally, there is a "global"
123 // frame to represent the outermost scope.  This global frame is always the last frame in
124 // the chain of callers.
125 //
126 // The delegate can assign a "wrapper" to each frame object so it can relay calls through its
127 // own exported interface.  This class is private to WebCore (and the delegate).
128
129 @implementation WebScriptCallFrame (WebScriptDebugDelegateInternal)
130
131 - (WebScriptCallFrame *)_initWithGlobalObject:(WebScriptObject *)globalObj caller:(WebScriptCallFrame *)caller state:(ExecState *)state
132 {
133     if ((self = [super init])) {
134         _private = [[WebScriptCallFramePrivate alloc] init];
135         _private->globalObject = globalObj;
136         _private->caller = caller; // (already retained)
137         _private->state = state;
138     }
139     return self;
140 }
141
142 - (id)_convertValueToObjcValue:(JSValue *)value
143 {
144     if (!value)
145         return nil;
146
147     WebScriptObject *globalObject = _private->globalObject;
148     if (value == [globalObject _imp])
149         return globalObject;
150
151     Bindings::RootObject* root1 = [globalObject _originRootObject];
152     if (!root1)
153         return nil;
154
155     Bindings::RootObject* root2 = [globalObject _rootObject];
156     if (!root2)
157         return nil;
158
159     return [WebScriptObject _convertValueToObjcValue:value originRootObject:root1 rootObject:root2];
160 }
161
162 @end
163
164
165
166 @implementation WebScriptCallFrame
167
168 - (void) dealloc
169 {
170     [_userInfo release];
171     [_private release];
172     [super dealloc];
173 }
174
175 - (void)setUserInfo:(id)userInfo
176 {
177     if (userInfo != _userInfo) {
178         [_userInfo release];
179         _userInfo = [userInfo retain];
180     }
181 }
182
183 - (id)userInfo
184 {
185     return _userInfo;
186 }
187
188 - (WebScriptCallFrame *)caller
189 {
190     return _private->caller;
191 }
192
193 // Returns an array of scope objects (most local first).
194 // The properties of each scope object are the variables for that scope.
195 // Note that the last entry in the array will _always_ be the global object (windowScriptObject),
196 // whose properties are the global variables.
197
198 - (NSArray *)scopeChain
199 {
200     ExecState* state = _private->state;
201     if (!state->scopeNode())  // global frame
202         return [NSArray arrayWithObject:_private->globalObject];
203
204     ScopeChain      chain  = state->scopeChain();
205     NSMutableArray *scopes = [[NSMutableArray alloc] init];
206
207     while (!chain.isEmpty()) {
208         [scopes addObject:[self _convertValueToObjcValue:chain.top()]];
209         chain.pop();
210     }
211
212     NSArray *result = [NSArray arrayWithArray:scopes];
213     [scopes release];
214     return result;
215 }
216
217 // Returns the name of the function for this frame, if available.
218 // Returns nil for anonymous functions and for the global frame.
219
220 - (NSString *)functionName
221 {
222     ExecState* state = _private->state;
223     if (!state->scopeNode())
224         return nil;
225
226     FunctionImp* func = state->function();
227     if (!func)
228         return nil;
229
230     Identifier fn = func->functionName();
231     return toNSString(fn.ustring());
232 }
233
234 // Returns the pending exception for this frame (nil if none).
235
236 - (id)exception
237 {
238     ExecState* state = _private->state;
239     if (!state->hadException())
240         return nil;
241     return [self _convertValueToObjcValue:state->exception()];
242 }
243
244 // Evaluate some JavaScript code in the context of this frame.
245 // The code is evaluated as if by "eval", and the result is returned.
246 // If there is an (uncaught) exception, it is returned as though _it_ were the result.
247 // Calling this method on the global frame is not quite the same as calling the WebScriptObject
248 // method of the same name, due to the treatment of exceptions.
249
250 // FIXME: If "script" contains var declarations, the machinery to handle local variables
251 // efficiently in JavaScriptCore will not work properly. This could lead to crashes or
252 // incorrect variable values. So this is not appropriate for evaluating arbitrary script.
253 - (id)evaluateWebScript:(NSString *)script
254 {
255     JSLock lock;
256
257     UString code = String(script);
258
259     ExecState* state = _private->state;
260     JSGlobalObject* globalObject = state->dynamicGlobalObject();
261
262     // find "eval"
263     JSObject* eval = NULL;
264     if (state->scopeNode()) {  // "eval" won't work without context (i.e. at global scope)
265         JSValue* v = globalObject->get(state, "eval");
266         if (v->isObject() && static_cast<JSObject*>(v)->implementsCall())
267             eval = static_cast<JSObject*>(v);
268         else
269             // no "eval" - fallback operates on global exec state
270             state = globalObject->globalExec();
271     }
272
273     JSValue* savedException = state->exception();
274     state->clearException();
275
276     // evaluate
277     JSValue* result;
278     if (eval) {
279         List args;
280         args.append(jsString(code));
281         result = eval->call(state, 0, args);
282     } else
283         // no "eval", or no context (i.e. global scope) - use global fallback
284         result = Interpreter::evaluate(state, UString(), 0, code.data(), code.size(), globalObject).value();
285
286     if (state->hadException())
287         result = state->exception();    // (may be redundant depending on which eval path was used)
288     state->setException(savedException);
289
290     return [self _convertValueToObjcValue:result];
291 }
292
293 @end