Move -_enterFrame and -_leaveFrame from WebCoreScriptDebugger to WebScriptDeb...
[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 "WebCoreScriptDebugger.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
45 using namespace KJS;
46 using namespace WebCore;
47
48 // FIXME: these error strings should be public for future use by WebScriptObject and in WebScriptObject.h
49 NSString * const WebScriptErrorDomain = @"WebScriptErrorDomain";
50 NSString * const WebScriptErrorDescriptionKey = @"WebScriptErrorDescription";
51 NSString * const WebScriptErrorLineNumberKey = @"WebScriptErrorLineNumber";
52
53 @interface WebScriptCallFrame (WebScriptDebugDelegateInternal)
54
55 - (WebScriptCallFrame *)_initWithGlobalObject:(WebScriptObject *)globalObj caller:(WebScriptCallFrame *)caller state:(ExecState *)state;
56 - (id)_convertValueToObjcValue:(JSValue *)value;
57
58 @end
59
60 @implementation WebScriptDebugger
61
62 - (WebScriptDebugger *)initWithWebFrame:(WebFrame *)webFrame
63 {
64     if ((self = [super init])) {
65         _webFrame = webFrame;
66         _debugger = [[WebCoreScriptDebugger alloc] initWithDelegate:self];
67     }
68     return self;
69 }
70
71 - (void)dealloc
72 {
73     [_debugger release];
74     [_current release];
75     [super dealloc];
76 }
77
78 - (WebScriptObject *)globalObject
79 {
80     return core(_webFrame)->windowScriptObject();
81 }
82
83 - (WebScriptCallFrame *)enterFrame:(ExecState*)state;
84 {
85     _current = [[WebScriptCallFrame alloc] _initWithGlobalObject:[self globalObject] caller:_current state:state];
86     return _current;
87 }
88
89 - (WebScriptCallFrame *)leaveFrame;
90 {
91     WebScriptCallFrame *caller = [[_current caller] retain];
92     [_current release];
93     return _current = caller;
94 }
95
96 - (void)parsedSource:(NSString *)source fromURL:(NSURL *)url sourceId:(int)sid startLine:(int)startLine errorLine:(int)errorLine errorMessage:(NSString *)errorMessage
97 {
98     WebView *webView = [_webFrame webView];
99     if (errorLine == -1) {
100         [[webView _scriptDebugDelegateForwarder] webView:webView didParseSource:source baseLineNumber:startLine fromURL:url sourceId:sid forWebFrame:_webFrame];
101         [[webView _scriptDebugDelegateForwarder] webView:webView didParseSource:source fromURL:[url absoluteString] sourceId:sid forWebFrame:_webFrame]; // deprecated delegate method
102         if ([WebScriptDebugServer listenerCount])
103             [[WebScriptDebugServer sharedScriptDebugServer] webView:webView didParseSource:source baseLineNumber:startLine fromURL:url sourceId:sid forWebFrame:_webFrame];
104     } else {
105         NSDictionary *info = [[NSDictionary alloc] initWithObjectsAndKeys:errorMessage, WebScriptErrorDescriptionKey, [NSNumber numberWithUnsignedInt:errorLine], WebScriptErrorLineNumberKey, nil];
106         NSError *error = [[NSError alloc] initWithDomain:WebScriptErrorDomain code:WebScriptGeneralErrorCode userInfo:info];
107         [[webView _scriptDebugDelegateForwarder] webView:webView failedToParseSource:source baseLineNumber:startLine fromURL:url withError:error forWebFrame:_webFrame];
108         if ([WebScriptDebugServer listenerCount])
109             [[WebScriptDebugServer sharedScriptDebugServer] webView:webView failedToParseSource:source baseLineNumber:startLine fromURL:url withError:error forWebFrame:_webFrame];
110         [error release];
111         [info release];
112     }
113 }
114
115 - (void)enteredFrame:(WebScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno
116 {
117     WebView *webView = [_webFrame webView];
118     [[webView _scriptDebugDelegateForwarder] webView:webView didEnterCallFrame:frame sourceId:sid line:lineno forWebFrame:_webFrame];
119     if ([WebScriptDebugServer listenerCount])
120         [[WebScriptDebugServer sharedScriptDebugServer] webView:webView didEnterCallFrame:frame sourceId:sid line:lineno forWebFrame:_webFrame];
121 }
122
123 - (void)hitStatement:(WebScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno
124 {
125     WebView *webView = [_webFrame webView];
126     [[webView _scriptDebugDelegateForwarder] webView:webView willExecuteStatement:frame sourceId:sid line:lineno forWebFrame:_webFrame];
127     if ([WebScriptDebugServer listenerCount])
128         [[WebScriptDebugServer sharedScriptDebugServer] webView:webView willExecuteStatement:frame sourceId:sid line:lineno forWebFrame:_webFrame];
129 }
130
131 - (void)leavingFrame:(WebScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno
132 {
133     WebView *webView = [_webFrame webView];
134     [[webView _scriptDebugDelegateForwarder] webView:webView willLeaveCallFrame:frame sourceId:sid line:lineno forWebFrame:_webFrame];
135     if ([WebScriptDebugServer listenerCount])
136         [[WebScriptDebugServer sharedScriptDebugServer] webView:webView willLeaveCallFrame:frame sourceId:sid line:lineno forWebFrame:_webFrame];
137 }
138
139 - (void)exceptionRaised:(WebScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno
140 {
141     WebView *webView = [_webFrame webView];
142     [[webView _scriptDebugDelegateForwarder] webView:webView exceptionWasRaised:frame sourceId:sid line:lineno forWebFrame:_webFrame];
143     if ([WebScriptDebugServer listenerCount])
144         [[WebScriptDebugServer sharedScriptDebugServer] webView:webView exceptionWasRaised:frame sourceId:sid line:lineno forWebFrame:_webFrame];
145 }
146
147 @end
148
149 @interface WebScriptCallFramePrivate : NSObject {
150 @public
151     WebScriptObject        *globalObject;   // the global object's proxy (not retained)
152     WebScriptCallFrame     *caller;         // previous stack frame
153     KJS::ExecState         *state;
154 }
155 @end
156
157 @implementation WebScriptCallFramePrivate
158 - (void)dealloc
159 {
160     [caller release];
161     [super dealloc];
162 }
163 @end
164
165 // WebScriptCallFrame
166 //
167 // One of these is created to represent each stack frame.  Additionally, there is a "global"
168 // frame to represent the outermost scope.  This global frame is always the last frame in
169 // the chain of callers.
170 //
171 // The delegate can assign a "wrapper" to each frame object so it can relay calls through its
172 // own exported interface.  This class is private to WebCore (and the delegate).
173
174 @implementation WebScriptCallFrame (WebScriptDebugDelegateInternal)
175
176 - (WebScriptCallFrame *)_initWithGlobalObject:(WebScriptObject *)globalObj caller:(WebScriptCallFrame *)caller state:(ExecState *)state
177 {
178     if ((self = [super init])) {
179         _private = [[WebScriptCallFramePrivate alloc] init];
180         _private->globalObject = globalObj;
181         _private->caller = caller; // (already retained)
182         _private->state = state;
183     }
184     return self;
185 }
186
187 - (id)_convertValueToObjcValue:(JSValue *)value
188 {
189     if (!value)
190         return nil;
191
192     WebScriptObject *globalObject = _private->globalObject;
193     if (value == [globalObject _imp])
194         return globalObject;
195
196     Bindings::RootObject* root1 = [globalObject _originRootObject];
197     if (!root1)
198         return nil;
199
200     Bindings::RootObject* root2 = [globalObject _rootObject];
201     if (!root2)
202         return nil;
203
204     return [WebScriptObject _convertValueToObjcValue:value originRootObject:root1 rootObject:root2];
205 }
206
207 @end
208
209
210
211 @implementation WebScriptCallFrame
212
213 - (void) dealloc
214 {
215     [_userInfo release];
216     [_private release];
217     [super dealloc];
218 }
219
220 - (void)setUserInfo:(id)userInfo
221 {
222     if (userInfo != _userInfo) {
223         [_userInfo release];
224         _userInfo = [userInfo retain];
225     }
226 }
227
228 - (id)userInfo
229 {
230     return _userInfo;
231 }
232
233 - (WebScriptCallFrame *)caller
234 {
235     return _private->caller;
236 }
237
238 // Returns an array of scope objects (most local first).
239 // The properties of each scope object are the variables for that scope.
240 // Note that the last entry in the array will _always_ be the global object (windowScriptObject),
241 // whose properties are the global variables.
242
243 - (NSArray *)scopeChain
244 {
245     ExecState* state = _private->state;
246     if (!state->scopeNode())  // global frame
247         return [NSArray arrayWithObject:_private->globalObject];
248
249     ScopeChain      chain  = state->scopeChain();
250     NSMutableArray *scopes = [[NSMutableArray alloc] init];
251
252     while (!chain.isEmpty()) {
253         [scopes addObject:[self _convertValueToObjcValue:chain.top()]];
254         chain.pop();
255     }
256
257     NSArray *result = [NSArray arrayWithArray:scopes];
258     [scopes release];
259     return result;
260 }
261
262 // Returns the name of the function for this frame, if available.
263 // Returns nil for anonymous functions and for the global frame.
264
265 - (NSString *)functionName
266 {
267     ExecState* state = _private->state;
268     if (!state->scopeNode())
269         return nil;
270
271     FunctionImp* func = state->function();
272     if (!func)
273         return nil;
274
275     Identifier fn = func->functionName();
276     return toNSString(fn.ustring());
277 }
278
279 // Returns the pending exception for this frame (nil if none).
280
281 - (id)exception
282 {
283     ExecState* state = _private->state;
284     if (!state->hadException())
285         return nil;
286     return [self _convertValueToObjcValue:state->exception()];
287 }
288
289 // Evaluate some JavaScript code in the context of this frame.
290 // The code is evaluated as if by "eval", and the result is returned.
291 // If there is an (uncaught) exception, it is returned as though _it_ were the result.
292 // Calling this method on the global frame is not quite the same as calling the WebScriptObject
293 // method of the same name, due to the treatment of exceptions.
294
295 // FIXME: If "script" contains var declarations, the machinery to handle local variables
296 // efficiently in JavaScriptCore will not work properly. This could lead to crashes or
297 // incorrect variable values. So this is not appropriate for evaluating arbitrary script.
298 - (id)evaluateWebScript:(NSString *)script
299 {
300     JSLock lock;
301
302     UString code = String(script);
303
304     ExecState* state = _private->state;
305     JSGlobalObject* globalObject = state->dynamicGlobalObject();
306
307     // find "eval"
308     JSObject* eval = NULL;
309     if (state->scopeNode()) {  // "eval" won't work without context (i.e. at global scope)
310         JSValue* v = globalObject->get(state, "eval");
311         if (v->isObject() && static_cast<JSObject*>(v)->implementsCall())
312             eval = static_cast<JSObject*>(v);
313         else
314             // no "eval" - fallback operates on global exec state
315             state = globalObject->globalExec();
316     }
317
318     JSValue* savedException = state->exception();
319     state->clearException();
320
321     // evaluate
322     JSValue* result;
323     if (eval) {
324         List args;
325         args.append(jsString(code));
326         result = eval->call(state, 0, args);
327     } else
328         // no "eval", or no context (i.e. global scope) - use global fallback
329         result = Interpreter::evaluate(state, UString(), 0, code.data(), code.size(), globalObject).value();
330
331     if (state->hadException())
332         result = state->exception();    // (may be redundant depending on which eval path was used)
333     state->setException(savedException);
334
335     return [self _convertValueToObjcValue:result];
336 }
337
338 @end