e5721bfe474855fef20546740bb77b0cf99d3751
[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 *DebuggerContinueToolbarItem = @"DebuggerContinueToolbarItem";
33 static NSString *DebuggerPauseToolbarItem = @"DebuggerPauseToolbarItem";
34 static NSString *DebuggerStepIntoToolbarItem = @"DebuggerStepIntoToolbarItem";
35 static NSString *DebuggerStepOverToolbarItem = @"DebuggerStepOverToolbarItem";
36 static NSString *DebuggerStepOutToolbarItem = @"DebuggerStepOutToolbarItem";
37
38 @implementation DebuggerDocument
39 + (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector
40 {
41     return NO;
42 }
43
44 + (BOOL)isKeyExcludedFromWebScript:(const char *)name
45 {
46     return NO;
47 }
48
49 #pragma mark -
50
51 - (id)initWithServerName:(NSString *)serverName
52 {
53     if ((self = [super init]))
54         [self switchToServerNamed:serverName];
55     return self;
56 }
57
58 - (void)dealloc
59 {
60     [server release];
61     [currentServerName release];
62     [super dealloc];
63 }
64
65 #pragma mark -
66 #pragma mark Stack & Variables
67
68 - (WebScriptCallFrame *)currentFrame
69 {
70     return currentFrame;
71 }
72
73 - (NSString *)currentFrameFunctionName
74 {
75     return [currentFrame functionName];
76 }
77
78 - (NSArray *)currentFunctionStack
79 {
80     NSMutableArray *result = [[NSMutableArray alloc] init];
81     WebScriptCallFrame *frame = currentFrame;
82     while (frame) {
83         if ([frame functionName])
84             [result addObject:[frame functionName]];
85         frame = [frame caller];
86     }
87     return [result autorelease];
88 }
89
90 #pragma mark -
91 #pragma mark Pause & Step
92
93 - (BOOL)isPaused
94 {
95     return paused;
96 }
97
98 - (void)pause
99 {
100     paused = YES;
101     if ([[(NSDistantObject *)server connectionForProxy] isValid])
102         [server pause];
103     [[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
104 }
105
106 - (void)resume
107 {
108     paused = NO;
109     if ([[(NSDistantObject *)server connectionForProxy] isValid])
110         [server resume];
111 }
112
113 - (void)stepInto
114 {
115     if ([[(NSDistantObject *)server connectionForProxy] isValid])
116         [server step];
117 }
118
119 - (void)log:(NSString *)msg
120 {
121     NSLog(@"%@", msg);
122 }
123
124 #pragma mark -
125 #pragma mark Interface Actions
126
127 - (IBAction)pause:(id)sender
128 {
129     [[webView windowScriptObject] callWebScriptMethod:@"pause" withArguments:nil];
130 }
131
132 - (IBAction)resume:(id)sender
133 {
134     [[webView windowScriptObject] callWebScriptMethod:@"resume" withArguments:nil];
135 }
136
137 - (IBAction)stepInto:(id)sender
138 {
139     [[webView windowScriptObject] callWebScriptMethod:@"stepInto" withArguments:nil];
140 }
141
142 - (IBAction)stepOver:(id)sender
143 {
144     [[webView windowScriptObject] callWebScriptMethod:@"stepOver" withArguments:nil];
145 }
146
147 - (IBAction)stepOut:(id)sender
148 {
149     [[webView windowScriptObject] callWebScriptMethod:@"stepOut" withArguments:nil];
150 }
151
152 #pragma mark -
153 #pragma mark Window Controller Overrides
154
155 - (NSString *)windowNibName
156 {
157     return @"Debugger";
158 }
159
160 - (void)windowDidLoad
161 {
162     [super windowDidLoad];
163
164     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationTerminating:) name:NSApplicationWillTerminateNotification object:nil];
165
166     NSString *path = [[NSBundle mainBundle] pathForResource:@"debugger" ofType:@"html" inDirectory:nil];
167     [[webView mainFrame] loadRequest:[[[NSURLRequest alloc] initWithURL:[NSURL fileURLWithPath:path]] autorelease]];
168
169     NSToolbar *toolbar = [[NSToolbar alloc] initWithIdentifier:@"debugger"];
170     [toolbar setDelegate:self];
171     [toolbar setAllowsUserCustomization:YES];
172     [toolbar setAutosavesConfiguration:YES];
173     [[self window] setToolbar:toolbar];
174     [toolbar release];
175 }
176
177 - (void)windowWillClose:(NSNotification *)notification
178 {
179     [[webView windowScriptObject] removeWebScriptKey:@"DebuggerDocument"];
180
181     [[NSNotificationCenter defaultCenter] removeObserver:self name:NSApplicationWillTerminateNotification object:nil];
182
183     [self switchToServerNamed:nil];
184
185     [self autorelease]; // DebuggerApplication expects us to release on close
186 }
187
188 #pragma mark -
189 #pragma mark Connection Handling
190
191 - (void)switchToServerNamed:(NSString *)name
192 {
193     if (server) {
194         [[NSNotificationCenter defaultCenter] removeObserver:self name:NSConnectionDidDieNotification object:[(NSDistantObject *)server connectionForProxy]];
195         if ([[(NSDistantObject *)server connectionForProxy] isValid]) {
196             [self resume];
197             [server removeListener:self];
198         }
199     }
200
201     id old = server;
202     server = ([name length] ? [[NSConnection rootProxyForConnectionWithRegisteredName:name host:nil] retain] : nil);
203     [old release];
204
205     old = currentServerName;
206     currentServerName = [name copy];
207     [old release];
208
209     if (server) {
210         @try {
211             [(NSDistantObject *)server setProtocolForProxy:@protocol(WebScriptDebugServer)];
212             [server addListener:self];
213         } @catch (NSException *exception) {
214             [currentServerName release];
215             currentServerName = nil;
216             [server release];
217             server = nil;
218         }
219
220         if (server)
221             [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(serverConnectionDidDie:) name:NSConnectionDidDieNotification object:[(NSDistantObject *)server connectionForProxy]];  
222     }
223 }
224
225 - (void)applicationTerminating:(NSNotification *)notifiction
226 {
227     if (server && [[(NSDistantObject *)server connectionForProxy] isValid]) {
228         [self switchToServerNamed:nil];
229         // call the runloop for a while to make sure our removeListener: is sent to the server
230         [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.25]];
231     }
232 }
233
234 - (void)serverConnectionDidDie:(NSNotification *)notifiction
235 {
236     [self switchToServerNamed:nil];
237 }
238
239 #pragma mark -
240 #pragma mark Toolbar Delegate
241
242 - (NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag
243 {
244     if ([itemIdentifier isEqualToString:DebuggerContinueToolbarItem]) {
245         NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
246
247         [item setLabel:@"Continue"];
248         [item setPaletteLabel:@"Continue"];
249
250         [item setToolTip:@"Continue script execution"];
251         [item setImage:[NSImage imageNamed:@"continue"]];
252
253         [item setTarget:self];
254         [item setAction:@selector(resume:)];
255
256         return [item autorelease];
257     } else if ([itemIdentifier isEqualToString:DebuggerPauseToolbarItem]) {
258         NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
259
260         [item setLabel:@"Pause"];
261         [item setPaletteLabel:@"Pause"];
262
263         [item setToolTip:@"Pause script execution"];
264         [item setImage:[NSImage imageNamed:@"pause"]];
265
266         [item setTarget:self];
267         [item setAction:@selector(pause:)];
268
269         return [item autorelease];
270     } else if ([itemIdentifier isEqualToString:DebuggerStepIntoToolbarItem]) {
271         NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
272
273         [item setLabel:@"Step Into"];
274         [item setPaletteLabel:@"Step Into"];
275
276         [item setToolTip:@"Step into function call"];
277         [item setImage:[NSImage imageNamed:@"step"]];
278
279         [item setTarget:self];
280         [item setAction:@selector(stepInto:)];
281
282         return [item autorelease];
283     } else if ([itemIdentifier isEqualToString:DebuggerStepOverToolbarItem]) {
284         NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
285
286         [item setLabel:@"Step Over"];
287         [item setPaletteLabel:@"Step Over"];
288
289         [item setToolTip:@"Step over function call"];
290         [item setImage:[NSImage imageNamed:@"stepOver"]];
291
292         [item setTarget:self];
293         [item setAction:@selector(stepOver:)];
294
295         return [item autorelease];
296     } else if ([itemIdentifier isEqualToString:DebuggerStepOutToolbarItem]) {
297         NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
298
299         [item setLabel:@"Step Out"];
300         [item setPaletteLabel:@"Step Over"];
301
302         [item setToolTip:@"Step out of current function"];
303         [item setImage:[NSImage imageNamed:@"stepOut"]];
304
305         [item setTarget:self];
306         [item setAction:@selector(stepOut:)];
307
308         return [item autorelease];
309     }
310
311     return nil;
312 }
313
314 - (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar*)toolbar
315 {
316     return [NSArray arrayWithObjects:DebuggerContinueToolbarItem, DebuggerPauseToolbarItem,
317         NSToolbarSeparatorItemIdentifier, DebuggerStepIntoToolbarItem, DebuggerStepOutToolbarItem,
318         DebuggerStepOverToolbarItem, nil];
319 }
320
321 - (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar*)toolbar
322 {
323     return [NSArray arrayWithObjects:DebuggerContinueToolbarItem, DebuggerPauseToolbarItem,
324         DebuggerStepIntoToolbarItem, DebuggerStepOutToolbarItem, DebuggerStepOverToolbarItem, NSToolbarCustomizeToolbarItemIdentifier,
325         NSToolbarFlexibleSpaceItemIdentifier, NSToolbarSpaceItemIdentifier, NSToolbarSeparatorItemIdentifier, nil];
326 }
327
328 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)interfaceItem
329 {
330     SEL action = [interfaceItem action];
331     if (action == @selector(pause:))
332         return ![self isPaused];
333     if (action == @selector(resume:) ||
334         action == @selector(stepOver:) ||
335         action == @selector(stepOut:) ||
336         action == @selector(stepInto:))
337         return [self isPaused];
338     return YES;
339 }
340
341 #pragma mark -
342 #pragma mark WebView Frame Load Delegate
343
344 - (void)webView:(WebView *)sender windowScriptObjectAvailable:(WebScriptObject *)windowScriptObject
345 {
346     // note: this is the Debuggers's own WebView, not the one being debugged
347     [[sender windowScriptObject] setValue:self forKey:@"DebuggerDocument"];
348 }
349
350 - (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame
351 {
352     // note: this is the Debuggers's own WebView, not the one being debugged
353     webViewLoaded = YES;
354 }
355
356 #pragma mark -
357 #pragma mark Debug Listener Callbacks
358
359 - (void)webView:(WebView *)view didLoadMainResourceForDataSource:(WebDataSource *)dataSource
360 {
361     NSString *documentSourceCopy = nil;
362     id <WebDocumentRepresentation> rep = [dataSource representation];
363     if ([rep canProvideDocumentSource])
364         documentSourceCopy = [[rep documentSource] copy];
365
366     if (!documentSourceCopy)
367         return;
368
369     NSString *urlCopy = [[[[dataSource response] URL] absoluteString] copy];
370     NSArray *args = [NSArray arrayWithObjects:(documentSourceCopy ? documentSourceCopy : @""), (urlCopy ? urlCopy : @""), [NSNumber numberWithBool:NO], nil];
371     [[webView windowScriptObject] callWebScriptMethod:@"updateFileSource" withArguments:args];
372
373     [documentSourceCopy release];
374     [urlCopy release];
375 }
376
377 - (void)webView:(WebView *)view didParseSource:(NSString *)source baseLineNumber:(unsigned)baseLine fromURL:(NSURL *)url sourceId:(int)sid forWebFrame:(WebFrame *)webFrame
378 {
379     if (!webViewLoaded)
380         return;
381
382     NSString *sourceCopy = [source copy];
383     if (!sourceCopy)
384         return;
385
386     NSString *documentSourceCopy = nil;
387     NSString *urlCopy = [[url absoluteString] copy];
388
389     WebDataSource *dataSource = [webFrame dataSource];
390     if (!url || [[[dataSource response] URL] isEqual:url]) {
391         id <WebDocumentRepresentation> rep = [dataSource representation];
392         if ([rep canProvideDocumentSource])
393             documentSourceCopy = [[rep documentSource] copy];
394         if (!urlCopy)
395             urlCopy = [[[[dataSource response] URL] absoluteString] copy];
396     }
397
398     NSArray *args = [NSArray arrayWithObjects:sourceCopy, (documentSourceCopy ? documentSourceCopy : @""), (urlCopy ? urlCopy : @""), [NSNumber numberWithInt:sid], [NSNumber numberWithUnsignedInt:baseLine], nil];
399     [[webView windowScriptObject] callWebScriptMethod:@"didParseScript" withArguments:args];
400
401     [sourceCopy release];
402     [documentSourceCopy release];
403     [urlCopy release];
404 }
405
406 - (void)webView:(WebView *)view failedToParseSource:(NSString *)source baseLineNumber:(unsigned)baseLine fromURL:(NSURL *)url withError:(NSError *)error forWebFrame:(WebFrame *)webFrame
407 {
408 }
409
410 - (void)webView:(WebView *)view didEnterCallFrame:(WebScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno forWebFrame:(WebFrame *)webFrame
411 {
412     if (!webViewLoaded)
413         return;
414
415     id old = currentFrame;
416     currentFrame = [frame retain];
417     [old release];
418
419     NSArray *args = [NSArray arrayWithObjects:[NSNumber numberWithInt:sid], [NSNumber numberWithInt:lineno], nil];
420     [[webView windowScriptObject] callWebScriptMethod:@"didEnterCallFrame" withArguments:args];
421 }
422
423 - (void)webView:(WebView *)view willExecuteStatement:(WebScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno forWebFrame:(WebFrame *)webFrame
424 {
425     if (!webViewLoaded)
426         return;
427
428     NSArray *args = [NSArray arrayWithObjects:[NSNumber numberWithInt:sid], [NSNumber numberWithInt:lineno], nil];
429     [[webView windowScriptObject] callWebScriptMethod:@"willExecuteStatement" withArguments:args];
430 }
431
432 - (void)webView:(WebView *)view willLeaveCallFrame:(WebScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno forWebFrame:(WebFrame *)webFrame
433 {
434     if (!webViewLoaded)
435         return;
436
437     NSArray *args = [NSArray arrayWithObjects:[NSNumber numberWithInt:sid], [NSNumber numberWithInt:lineno], nil];
438     [[webView windowScriptObject] callWebScriptMethod:@"willLeaveCallFrame" withArguments:args];
439
440     id old = currentFrame;
441     currentFrame = [[frame caller] retain];
442     [old release];
443 }
444 @end