Reviewed by Darin.
[WebKit-https.git] / WebKitTools / Drosera / DebuggerDocument.m
1 /*
2  * Copyright (C) 2006 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 "DebuggerDocument.h"
30 #import "DebuggerApplication.h"
31
32 static NSString *DebuggerConsoleToolbarItem = @"DebuggerConsoleToolbarItem";
33 static NSString *DebuggerContinueToolbarItem = @"DebuggerContinueToolbarItem";
34 static NSString *DebuggerPauseToolbarItem = @"DebuggerPauseToolbarItem";
35 static NSString *DebuggerStepIntoToolbarItem = @"DebuggerStepIntoToolbarItem";
36 static NSString *DebuggerStepOverToolbarItem = @"DebuggerStepOverToolbarItem";
37 static NSString *DebuggerStepOutToolbarItem = @"DebuggerStepOutToolbarItem";
38
39 @interface WebScriptObject (WebScriptObjectPrivate)
40 - (unsigned int)count;
41 @end
42
43 @implementation DebuggerDocument
44 + (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector
45 {
46     return NO;
47 }
48
49 + (BOOL)isKeyExcludedFromWebScript:(const char *)name
50 {
51     return NO;
52 }
53
54 #pragma mark -
55
56 - (id)initWithServerName:(NSString *)serverName
57 {
58     if ((self = [super init]))
59         [self switchToServerNamed:serverName];
60     return self;
61 }
62
63 - (void)dealloc
64 {
65     [server release];
66     [currentServerName release];
67     [super dealloc];
68 }
69
70 #pragma mark -
71 #pragma mark Stack & Variables
72
73 - (WebScriptCallFrame *)currentFrame
74 {
75     return currentFrame;
76 }
77
78 - (NSString *)currentFrameFunctionName
79 {
80     return [currentFrame functionName];
81 }
82
83 - (NSArray *)currentFunctionStack
84 {
85     NSMutableArray *result = [[NSMutableArray alloc] init];
86     WebScriptCallFrame *frame = currentFrame;
87     while (frame) {
88         if ([frame functionName])
89             [result addObject:[frame functionName]];
90         frame = [frame caller];
91     }
92     return [result autorelease];
93 }
94
95 - (id)evaluateScript:(NSString *)script inCallFrame:(int)frame
96 {
97     WebScriptCallFrame *cframe = currentFrame;
98     for (unsigned count = 0; count < frame; count++)
99         cframe = [cframe caller];
100     if (!cframe)
101         return nil;
102
103     id result = [cframe evaluateWebScript:script];
104     if ([result isKindOfClass:NSClassFromString(@"WebScriptObject")])
105         return [result callWebScriptMethod:@"toString" withArguments:nil];
106     return result;
107 }
108
109 - (NSArray *)webScriptAttributeKeysForScriptObject:(WebScriptObject *)object
110 {
111     WebScriptObject *func = [object evaluateWebScript:@"(function () { var result = new Array(); for (var x in this) { result.push(x); } return result; })"];
112     [object setValue:func forKey:@"__drosera_introspection"];
113
114     NSMutableArray *result = [[NSMutableArray alloc] init];
115     WebScriptObject *variables = [object callWebScriptMethod:@"__drosera_introspection" withArguments:nil];
116     unsigned length = [variables count];
117     for (unsigned i = 0; i < length; i++) {
118         NSString *key = [variables webScriptValueAtIndex:i];
119         if (![key isEqualToString:@"__drosera_introspection"])
120             [result addObject:key];
121     }
122
123     [object removeWebScriptKey:@"__drosera_introspection"];
124
125     [result sortUsingSelector:@selector(compare:)];
126     return [result autorelease];
127 }
128
129 - (NSArray *)localScopeVariableNamesForCallFrame:(int)frame
130 {
131     WebScriptCallFrame *cframe = currentFrame;
132     for (unsigned count = 0; count < frame; count++)
133         cframe = [cframe caller];
134
135     if (![[cframe scopeChain] count])
136         return nil;
137
138     WebScriptObject *scope = [[cframe scopeChain] objectAtIndex:0]; // local is always first
139     return [self webScriptAttributeKeysForScriptObject:scope];
140 }
141
142 - (NSString *)valueForScopeVariableNamed:(NSString *)key inCallFrame:(int)frame
143 {
144     WebScriptCallFrame *cframe = currentFrame;
145     for (unsigned count = 0; count < frame; count++)
146         cframe = [cframe caller];
147
148     if (![[cframe scopeChain] count])
149         return nil;
150
151     unsigned scopeCount = [[cframe scopeChain] count];
152     for (unsigned i = 0; i < scopeCount; i++) {
153         WebScriptObject *scope = [[cframe scopeChain] objectAtIndex:i];
154         id value = [scope valueForKey:key];
155         if ([value isKindOfClass:NSClassFromString(@"WebScriptObject")])
156             return [value callWebScriptMethod:@"toString" withArguments:nil];
157         if (value && ![value isKindOfClass:[NSString class]])
158             return [NSString stringWithFormat:@"%@", value];
159         if (value)
160             return value;
161     }
162
163     return nil;
164 }
165
166 #pragma mark -
167 #pragma mark Pause & Step
168
169 - (BOOL)isPaused
170 {
171     return paused;
172 }
173
174 - (void)pause
175 {
176     paused = YES;
177     if ([[(NSDistantObject *)server connectionForProxy] isValid])
178         [server pause];
179     [[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
180 }
181
182 - (void)resume
183 {
184     paused = NO;
185     if ([[(NSDistantObject *)server connectionForProxy] isValid])
186         [server resume];
187 }
188
189 - (void)stepInto
190 {
191     if ([[(NSDistantObject *)server connectionForProxy] isValid])
192         [server step];
193 }
194
195 - (void)log:(NSString *)msg
196 {
197     NSLog(@"%@", msg);
198 }
199
200 #pragma mark -
201 #pragma mark Interface Actions
202
203 - (IBAction)pause:(id)sender
204 {
205     [[webView windowScriptObject] callWebScriptMethod:@"pause" withArguments:nil];
206 }
207
208 - (IBAction)resume:(id)sender
209 {
210     [[webView windowScriptObject] callWebScriptMethod:@"resume" withArguments:nil];
211 }
212
213 - (IBAction)stepInto:(id)sender
214 {
215     [[webView windowScriptObject] callWebScriptMethod:@"stepInto" withArguments:nil];
216 }
217
218 - (IBAction)stepOver:(id)sender
219 {
220     [[webView windowScriptObject] callWebScriptMethod:@"stepOver" withArguments:nil];
221 }
222
223 - (IBAction)stepOut:(id)sender
224 {
225     [[webView windowScriptObject] callWebScriptMethod:@"stepOut" withArguments:nil];
226 }
227
228 - (IBAction)showConsole:(id)sender
229 {
230     [[webView windowScriptObject] callWebScriptMethod:@"showConsoleWindow" withArguments:nil];
231 }
232
233 #pragma mark -
234 #pragma mark Window Controller Overrides
235
236 - (NSString *)windowNibName
237 {
238     return @"Debugger";
239 }
240
241 - (void)windowDidLoad
242 {
243     [super windowDidLoad];
244
245     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationTerminating:) name:NSApplicationWillTerminateNotification object:nil];
246
247     NSString *path = [[NSBundle mainBundle] pathForResource:@"debugger" ofType:@"html" inDirectory:nil];
248     [[webView mainFrame] loadRequest:[[[NSURLRequest alloc] initWithURL:[NSURL fileURLWithPath:path]] autorelease]];
249
250     NSToolbar *toolbar = [[NSToolbar alloc] initWithIdentifier:@"debugger"];
251     [toolbar setDelegate:self];
252     [toolbar setAllowsUserCustomization:YES];
253     [toolbar setAutosavesConfiguration:YES];
254     [[self window] setToolbar:toolbar];
255     [toolbar release];
256 }
257
258 - (void)windowWillClose:(NSNotification *)notification
259 {
260     [[webView windowScriptObject] removeWebScriptKey:@"DebuggerDocument"];
261
262     [[NSNotificationCenter defaultCenter] removeObserver:self name:NSApplicationWillTerminateNotification object:nil];
263
264     [self switchToServerNamed:nil];
265
266     [self autorelease]; // DebuggerApplication expects us to release on close
267 }
268
269 #pragma mark -
270 #pragma mark Connection Handling
271
272 - (void)switchToServerNamed:(NSString *)name
273 {
274     if (server) {
275         [[NSNotificationCenter defaultCenter] removeObserver:self name:NSConnectionDidDieNotification object:[(NSDistantObject *)server connectionForProxy]];
276         if ([[(NSDistantObject *)server connectionForProxy] isValid]) {
277             [self resume];
278             [server removeListener:self];
279         }
280     }
281
282     id old = server;
283     server = ([name length] ? [[NSConnection rootProxyForConnectionWithRegisteredName:name host:nil] retain] : nil);
284     [old release];
285
286     old = currentServerName;
287     currentServerName = [name copy];
288     [old release];
289
290     if (server) {
291         @try {
292             [(NSDistantObject *)server setProtocolForProxy:@protocol(WebScriptDebugServer)];
293             [server addListener:self];
294         } @catch (NSException *exception) {
295             [currentServerName release];
296             currentServerName = nil;
297             [server release];
298             server = nil;
299         }
300
301         if (server)
302             [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(serverConnectionDidDie:) name:NSConnectionDidDieNotification object:[(NSDistantObject *)server connectionForProxy]];  
303     }
304 }
305
306 - (void)applicationTerminating:(NSNotification *)notifiction
307 {
308     if (server && [[(NSDistantObject *)server connectionForProxy] isValid]) {
309         [self switchToServerNamed:nil];
310         // call the runloop for a while to make sure our removeListener: is sent to the server
311         [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.25]];
312     }
313 }
314
315 - (void)serverConnectionDidDie:(NSNotification *)notifiction
316 {
317     [self switchToServerNamed:nil];
318 }
319
320 #pragma mark -
321 #pragma mark Toolbar Delegate
322
323 - (NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag
324 {
325     if ([itemIdentifier isEqualToString:DebuggerContinueToolbarItem]) {
326         NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
327
328         [item setLabel:@"Continue"];
329         [item setPaletteLabel:@"Continue"];
330
331         [item setToolTip:@"Continue script execution"];
332         [item setImage:[NSImage imageNamed:@"continue"]];
333
334         [item setTarget:self];
335         [item setAction:@selector(resume:)];
336
337         return [item autorelease];
338     } else if ([itemIdentifier isEqualToString:DebuggerConsoleToolbarItem]) {
339         NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
340
341         [item setLabel:@"Console"];
342         [item setPaletteLabel:@"Console"];
343
344         [item setToolTip:@"Console"];
345         [item setImage:[NSImage imageNamed:@"console"]];
346
347         [item setTarget:self];
348         [item setAction:@selector(showConsole:)];
349
350         return [item autorelease];
351     } else if ([itemIdentifier isEqualToString:DebuggerPauseToolbarItem]) {
352         NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
353
354         [item setLabel:@"Pause"];
355         [item setPaletteLabel:@"Pause"];
356
357         [item setToolTip:@"Pause script execution"];
358         [item setImage:[NSImage imageNamed:@"pause"]];
359
360         [item setTarget:self];
361         [item setAction:@selector(pause:)];
362
363         return [item autorelease];
364     } else if ([itemIdentifier isEqualToString:DebuggerStepIntoToolbarItem]) {
365         NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
366
367         [item setLabel:@"Step Into"];
368         [item setPaletteLabel:@"Step Into"];
369
370         [item setToolTip:@"Step into function call"];
371         [item setImage:[NSImage imageNamed:@"step"]];
372
373         [item setTarget:self];
374         [item setAction:@selector(stepInto:)];
375
376         return [item autorelease];
377     } else if ([itemIdentifier isEqualToString:DebuggerStepOverToolbarItem]) {
378         NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
379
380         [item setLabel:@"Step Over"];
381         [item setPaletteLabel:@"Step Over"];
382
383         [item setToolTip:@"Step over function call"];
384         [item setImage:[NSImage imageNamed:@"stepOver"]];
385
386         [item setTarget:self];
387         [item setAction:@selector(stepOver:)];
388
389         return [item autorelease];
390     } else if ([itemIdentifier isEqualToString:DebuggerStepOutToolbarItem]) {
391         NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
392
393         [item setLabel:@"Step Out"];
394         [item setPaletteLabel:@"Step Over"];
395
396         [item setToolTip:@"Step out of current function"];
397         [item setImage:[NSImage imageNamed:@"stepOut"]];
398
399         [item setTarget:self];
400         [item setAction:@selector(stepOut:)];
401
402         return [item autorelease];
403     }
404
405     return nil;
406 }
407
408 - (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar*)toolbar
409 {
410     return [NSArray arrayWithObjects:DebuggerContinueToolbarItem, DebuggerPauseToolbarItem,
411         NSToolbarSeparatorItemIdentifier, DebuggerStepIntoToolbarItem, DebuggerStepOutToolbarItem,
412         DebuggerStepOverToolbarItem, NSToolbarFlexibleSpaceItemIdentifier, DebuggerConsoleToolbarItem, nil];
413 }
414
415 - (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar*)toolbar
416 {
417     return [NSArray arrayWithObjects:DebuggerConsoleToolbarItem, DebuggerContinueToolbarItem, DebuggerPauseToolbarItem,
418         DebuggerStepIntoToolbarItem, DebuggerStepOutToolbarItem, DebuggerStepOverToolbarItem, NSToolbarCustomizeToolbarItemIdentifier,
419         NSToolbarFlexibleSpaceItemIdentifier, NSToolbarSpaceItemIdentifier, NSToolbarSeparatorItemIdentifier, nil];
420 }
421
422 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)interfaceItem
423 {
424     SEL action = [interfaceItem action];
425     if (action == @selector(pause:))
426         return ![self isPaused];
427     if (action == @selector(resume:) ||
428         action == @selector(stepOver:) ||
429         action == @selector(stepOut:) ||
430         action == @selector(stepInto:))
431         return [self isPaused];
432     return YES;
433 }
434
435 #pragma mark -
436 #pragma mark WebView UI Delegate
437
438 - (WebView *)webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request
439 {
440     WebView *newWebView = [[WebView alloc] initWithFrame:NSZeroRect frameName:nil groupName:nil];
441     [newWebView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
442     [newWebView setUIDelegate:self];
443     [newWebView setPolicyDelegate:self];
444     [newWebView setFrameLoadDelegate:self];
445     if (request)
446         [[newWebView mainFrame] loadRequest:request];
447
448     NSWindow *window = [[NSWindow alloc] initWithContentRect:NSZeroRect styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask | NSUnifiedTitleAndToolbarWindowMask) backing:NSBackingStoreBuffered defer:NO screen:[[webView window] screen]];
449     [window setReleasedWhenClosed:YES];
450     [newWebView setFrame:[[window contentView] frame]];
451     [[window contentView] addSubview:newWebView];
452     [newWebView release];
453
454     return newWebView;
455 }
456
457 - (void)webViewShow:(WebView *)sender
458 {
459     [[sender window] makeKeyAndOrderFront:sender];
460 }
461
462 - (BOOL)webViewAreToolbarsVisible:(WebView *)sender
463 {
464     return [[[sender window] toolbar] isVisible];
465 }
466
467 - (void)webView:(WebView *)sender setToolbarsVisible:(BOOL)visible
468 {
469     [[[sender window] toolbar] setVisible:visible];
470 }
471
472 - (void)webView:(WebView *)sender setResizable:(BOOL)resizable
473 {
474     [[sender window] setShowsResizeIndicator:resizable];
475     [[[sender window] standardWindowButton:NSWindowZoomButton] setEnabled:resizable];
476 }
477
478 - (void)webView:(WebView *)sender runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame
479 {
480     NSRange range = [message rangeOfString:@"\t"];
481     NSString *title = @"Alert";
482     if (range.location != NSNotFound) {
483         title = [message substringToIndex:range.location];
484         message = [message substringFromIndex:(range.location + range.length)];
485     }
486
487     NSBeginInformationalAlertSheet(title, nil, nil, nil, [sender window], nil, NULL, NULL, NULL, message);
488 }
489
490 - (void)scriptConfirmSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(int *)contextInfo
491 {
492     *contextInfo = returnCode;
493 }
494
495 - (BOOL)webView:(WebView *)sender runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame
496 {
497     NSRange range = [message rangeOfString:@"\t"];
498     NSString *title = @"Alert";
499     if (range.location != NSNotFound) {
500         title = [message substringToIndex:range.location];
501         message = [message substringFromIndex:(range.location + range.length)];
502     }
503
504     int result = NSNotFound;
505     NSBeginInformationalAlertSheet(title, nil, @"Cancel", nil, [sender window], self, @selector(scriptConfirmSheetDidEnd:returnCode:contextInfo:), NULL, &result, message);
506
507     while (result == NSNotFound) {
508         NSEvent *nextEvent = [[NSApplication sharedApplication] nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantFuture] inMode:NSDefaultRunLoopMode dequeue:YES];
509         [[NSApplication sharedApplication] sendEvent:nextEvent];
510     }
511
512     return result;
513 }
514
515 #pragma mark -
516 #pragma mark WebView Frame Load Delegate
517
518 - (void)webView:(WebView *)sender windowScriptObjectAvailable:(WebScriptObject *)windowScriptObject
519 {
520     // note: this is the Debuggers's own WebView, not the one being debugged
521     if ([[sender window] isEqual:[self window]])
522         [[sender windowScriptObject] setValue:self forKey:@"DebuggerDocument"];
523 }
524
525 - (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame
526 {
527     // note: this is the Debuggers's own WebView, not the one being debugged
528     if ([[sender window] isEqual:[self window]])
529         webViewLoaded = YES;
530 }
531
532 - (void)webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame
533 {
534     // note: this is the Debuggers's own WebViews, not the one being debugged
535     if ([frame isEqual:[sender mainFrame]]) {
536         NSDictionary *info = [[(DebuggerApplication *)[[NSApplication sharedApplication] delegate] knownServers] objectForKey:currentServerName];
537         NSString *processName = [info objectForKey:WebScriptDebugServerProcessNameKey];
538         if (info && [processName length]) {
539             NSMutableString *newTitle = [[NSMutableString alloc] initWithString:processName];
540             [newTitle appendString:@" - "];
541             [newTitle appendString:title];
542             [[sender window] setTitle:newTitle];
543             [newTitle release];
544         } else 
545             [[sender window] setTitle:title];
546     }
547 }
548
549 #pragma mark -
550 #pragma mark Debug Listener Callbacks
551
552 - (void)webView:(WebView *)view didLoadMainResourceForDataSource:(WebDataSource *)dataSource
553 {
554     NSString *documentSourceCopy = nil;
555     id <WebDocumentRepresentation> rep = [dataSource representation];
556     if ([rep canProvideDocumentSource])
557         documentSourceCopy = [[rep documentSource] copy];
558
559     if (!documentSourceCopy)
560         return;
561
562     NSString *urlCopy = [[[[dataSource response] URL] absoluteString] copy];
563     NSArray *args = [[NSArray alloc] initWithObjects:(documentSourceCopy ? documentSourceCopy : @""), (urlCopy ? urlCopy : @""), [NSNumber numberWithBool:NO], nil];
564     [[webView windowScriptObject] callWebScriptMethod:@"updateFileSource" withArguments:args];
565
566     [args release];
567     [documentSourceCopy release];
568     [urlCopy release];
569 }
570
571 - (void)webView:(WebView *)view didParseSource:(NSString *)source baseLineNumber:(unsigned)baseLine fromURL:(NSURL *)url sourceId:(int)sid forWebFrame:(WebFrame *)webFrame
572 {
573     if (!webViewLoaded)
574         return;
575
576     NSString *sourceCopy = [source copy];
577     if (!sourceCopy)
578         return;
579
580     NSString *documentSourceCopy = nil;
581     NSString *urlCopy = [[url absoluteString] copy];
582
583     WebDataSource *dataSource = [webFrame dataSource];
584     if (!url || [[[dataSource response] URL] isEqual:url]) {
585         id <WebDocumentRepresentation> rep = [dataSource representation];
586         if ([rep canProvideDocumentSource])
587             documentSourceCopy = [[rep documentSource] copy];
588         if (!urlCopy)
589             urlCopy = [[[[dataSource response] URL] absoluteString] copy];
590     }
591
592     NSArray *args = [[NSArray alloc] initWithObjects:sourceCopy, (documentSourceCopy ? documentSourceCopy : @""), (urlCopy ? urlCopy : @""), [NSNumber numberWithInt:sid], [NSNumber numberWithUnsignedInt:baseLine], nil];
593     [[webView windowScriptObject] callWebScriptMethod:@"didParseScript" withArguments:args];
594
595     [args release];
596     [sourceCopy release];
597     [documentSourceCopy release];
598     [urlCopy release];
599 }
600
601 - (void)webView:(WebView *)view failedToParseSource:(NSString *)source baseLineNumber:(unsigned)baseLine fromURL:(NSURL *)url withError:(NSError *)error forWebFrame:(WebFrame *)webFrame
602 {
603 }
604
605 - (void)webView:(WebView *)view didEnterCallFrame:(WebScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno forWebFrame:(WebFrame *)webFrame
606 {
607     if (!webViewLoaded)
608         return;
609
610     id old = currentFrame;
611     currentFrame = [frame retain];
612     [old release];
613
614     NSArray *args = [[NSArray alloc] initWithObjects:[NSNumber numberWithInt:sid], [NSNumber numberWithInt:lineno], nil];
615     [[webView windowScriptObject] callWebScriptMethod:@"didEnterCallFrame" withArguments:args];
616     [args release];
617 }
618
619 - (void)webView:(WebView *)view willExecuteStatement:(WebScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno forWebFrame:(WebFrame *)webFrame
620 {
621     if (!webViewLoaded)
622         return;
623
624     NSArray *args = [[NSArray alloc] initWithObjects:[NSNumber numberWithInt:sid], [NSNumber numberWithInt:lineno], nil];
625     [[webView windowScriptObject] callWebScriptMethod:@"willExecuteStatement" withArguments:args];
626     [args release];
627 }
628
629 - (void)webView:(WebView *)view willLeaveCallFrame:(WebScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno forWebFrame:(WebFrame *)webFrame
630 {
631     if (!webViewLoaded)
632         return;
633
634     NSArray *args = [[NSArray alloc] initWithObjects:[NSNumber numberWithInt:sid], [NSNumber numberWithInt:lineno], nil];
635     [[webView windowScriptObject] callWebScriptMethod:@"willLeaveCallFrame" withArguments:args];
636     [args release];
637
638     id old = currentFrame;
639     currentFrame = [[frame caller] retain];
640     [old release];
641 }
642 @end