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