2 * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
29 #import "DebuggerDocument.h"
30 #import "DebuggerApplication.h"
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";
38 @implementation DebuggerDocument
39 + (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector
44 + (BOOL)isKeyExcludedFromWebScript:(const char *)name
51 - (id)initWithServerName:(NSString *)serverName
53 if ((self = [super init]))
54 [self switchToServerNamed:serverName];
61 [currentServerName release];
66 #pragma mark Stack & Variables
68 - (WebScriptCallFrame *)currentFrame
73 - (NSString *)currentFrameFunctionName
75 return [currentFrame functionName];
78 - (NSArray *)currentFunctionStack
80 NSMutableArray *result = [[NSMutableArray alloc] init];
81 WebScriptCallFrame *frame = currentFrame;
83 if ([frame functionName])
84 [result addObject:[frame functionName]];
85 frame = [frame caller];
87 return [result autorelease];
91 #pragma mark Pause & Step
101 if ([[(NSDistantObject *)server connectionForProxy] isValid])
103 [[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
109 if ([[(NSDistantObject *)server connectionForProxy] isValid])
115 if ([[(NSDistantObject *)server connectionForProxy] isValid])
119 - (void)log:(NSString *)msg
125 #pragma mark Interface Actions
127 - (IBAction)pause:(id)sender
129 [[webView windowScriptObject] callWebScriptMethod:@"pause" withArguments:nil];
132 - (IBAction)resume:(id)sender
134 [[webView windowScriptObject] callWebScriptMethod:@"resume" withArguments:nil];
137 - (IBAction)stepInto:(id)sender
139 [[webView windowScriptObject] callWebScriptMethod:@"stepInto" withArguments:nil];
142 - (IBAction)stepOver:(id)sender
144 [[webView windowScriptObject] callWebScriptMethod:@"stepOver" withArguments:nil];
147 - (IBAction)stepOut:(id)sender
149 [[webView windowScriptObject] callWebScriptMethod:@"stepOut" withArguments:nil];
153 #pragma mark Window Controller Overrides
155 - (NSString *)windowNibName
160 - (void)windowDidLoad
162 [super windowDidLoad];
164 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationTerminating:) name:NSApplicationWillTerminateNotification object:nil];
166 NSString *path = [[NSBundle mainBundle] pathForResource:@"debugger" ofType:@"html" inDirectory:nil];
167 [[webView mainFrame] loadRequest:[[[NSURLRequest alloc] initWithURL:[NSURL fileURLWithPath:path]] autorelease]];
169 NSToolbar *toolbar = [[NSToolbar alloc] initWithIdentifier:@"debugger"];
170 [toolbar setDelegate:self];
171 [toolbar setAllowsUserCustomization:YES];
172 [toolbar setAutosavesConfiguration:YES];
173 [[self window] setToolbar:toolbar];
177 - (void)windowWillClose:(NSNotification *)notification
179 [[webView windowScriptObject] removeWebScriptKey:@"DebuggerDocument"];
181 [[NSNotificationCenter defaultCenter] removeObserver:self name:NSApplicationWillTerminateNotification object:nil];
183 [self switchToServerNamed:nil];
185 [self autorelease]; // DebuggerApplication expects us to release on close
189 #pragma mark Connection Handling
191 - (void)switchToServerNamed:(NSString *)name
194 [[NSNotificationCenter defaultCenter] removeObserver:self name:NSConnectionDidDieNotification object:[(NSDistantObject *)server connectionForProxy]];
195 if ([[(NSDistantObject *)server connectionForProxy] isValid]) {
197 [server removeListener:self];
202 server = ([name length] ? [[NSConnection rootProxyForConnectionWithRegisteredName:name host:nil] retain] : nil);
205 old = currentServerName;
206 currentServerName = [name copy];
211 [(NSDistantObject *)server setProtocolForProxy:@protocol(WebScriptDebugServer)];
212 [server addListener:self];
213 } @catch (NSException *exception) {
214 [currentServerName release];
215 currentServerName = nil;
221 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(serverConnectionDidDie:) name:NSConnectionDidDieNotification object:[(NSDistantObject *)server connectionForProxy]];
225 - (void)applicationTerminating:(NSNotification *)notifiction
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]];
234 - (void)serverConnectionDidDie:(NSNotification *)notifiction
236 [self switchToServerNamed:nil];
240 #pragma mark Toolbar Delegate
242 - (NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag
244 if ([itemIdentifier isEqualToString:DebuggerContinueToolbarItem]) {
245 NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
247 [item setLabel:@"Continue"];
248 [item setPaletteLabel:@"Continue"];
250 [item setToolTip:@"Continue script execution"];
251 [item setImage:[NSImage imageNamed:@"continue"]];
253 [item setTarget:self];
254 [item setAction:@selector(resume:)];
256 return [item autorelease];
257 } else if ([itemIdentifier isEqualToString:DebuggerPauseToolbarItem]) {
258 NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
260 [item setLabel:@"Pause"];
261 [item setPaletteLabel:@"Pause"];
263 [item setToolTip:@"Pause script execution"];
264 [item setImage:[NSImage imageNamed:@"pause"]];
266 [item setTarget:self];
267 [item setAction:@selector(pause:)];
269 return [item autorelease];
270 } else if ([itemIdentifier isEqualToString:DebuggerStepIntoToolbarItem]) {
271 NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
273 [item setLabel:@"Step Into"];
274 [item setPaletteLabel:@"Step Into"];
276 [item setToolTip:@"Step into function call"];
277 [item setImage:[NSImage imageNamed:@"step"]];
279 [item setTarget:self];
280 [item setAction:@selector(stepInto:)];
282 return [item autorelease];
283 } else if ([itemIdentifier isEqualToString:DebuggerStepOverToolbarItem]) {
284 NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
286 [item setLabel:@"Step Over"];
287 [item setPaletteLabel:@"Step Over"];
289 [item setToolTip:@"Step over function call"];
290 [item setImage:[NSImage imageNamed:@"stepOver"]];
292 [item setTarget:self];
293 [item setAction:@selector(stepOver:)];
295 return [item autorelease];
296 } else if ([itemIdentifier isEqualToString:DebuggerStepOutToolbarItem]) {
297 NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
299 [item setLabel:@"Step Out"];
300 [item setPaletteLabel:@"Step Over"];
302 [item setToolTip:@"Step out of current function"];
303 [item setImage:[NSImage imageNamed:@"stepOut"]];
305 [item setTarget:self];
306 [item setAction:@selector(stepOut:)];
308 return [item autorelease];
314 - (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar*)toolbar
316 return [NSArray arrayWithObjects:DebuggerContinueToolbarItem, DebuggerPauseToolbarItem,
317 NSToolbarSeparatorItemIdentifier, DebuggerStepIntoToolbarItem, DebuggerStepOutToolbarItem,
318 DebuggerStepOverToolbarItem, nil];
321 - (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar*)toolbar
323 return [NSArray arrayWithObjects:DebuggerContinueToolbarItem, DebuggerPauseToolbarItem,
324 DebuggerStepIntoToolbarItem, DebuggerStepOutToolbarItem, DebuggerStepOverToolbarItem, NSToolbarCustomizeToolbarItemIdentifier,
325 NSToolbarFlexibleSpaceItemIdentifier, NSToolbarSpaceItemIdentifier, NSToolbarSeparatorItemIdentifier, nil];
328 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)interfaceItem
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];
342 #pragma mark WebView Frame Load Delegate
344 - (void)webView:(WebView *)sender windowScriptObjectAvailable:(WebScriptObject *)windowScriptObject
346 // note: this is the Debuggers's own WebView, not the one being debugged
347 [[sender windowScriptObject] setValue:self forKey:@"DebuggerDocument"];
350 - (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame
352 // note: this is the Debuggers's own WebView, not the one being debugged
357 #pragma mark Debug Listener Callbacks
359 - (void)webView:(WebView *)view didLoadMainResourceForDataSource:(WebDataSource *)dataSource
361 NSString *documentSourceCopy = nil;
362 id <WebDocumentRepresentation> rep = [dataSource representation];
363 if ([rep canProvideDocumentSource])
364 documentSourceCopy = [[rep documentSource] copy];
366 if (!documentSourceCopy)
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];
373 [documentSourceCopy release];
377 - (void)webView:(WebView *)view didParseSource:(NSString *)source baseLineNumber:(unsigned)baseLine fromURL:(NSURL *)url sourceId:(int)sid forWebFrame:(WebFrame *)webFrame
382 NSString *sourceCopy = [source copy];
386 NSString *documentSourceCopy = nil;
387 NSString *urlCopy = [[url absoluteString] copy];
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];
395 urlCopy = [[[[dataSource response] URL] absoluteString] copy];
398 NSArray *args = [NSArray arrayWithObjects:sourceCopy, (documentSourceCopy ? documentSourceCopy : @""), (urlCopy ? urlCopy : @""), [NSNumber numberWithInt:sid], [NSNumber numberWithUnsignedInt:baseLine], nil];
399 [[webView windowScriptObject] callWebScriptMethod:@"didParseScript" withArguments:args];
401 [sourceCopy release];
402 [documentSourceCopy release];
406 - (void)webView:(WebView *)view failedToParseSource:(NSString *)source baseLineNumber:(unsigned)baseLine fromURL:(NSURL *)url withError:(NSError *)error forWebFrame:(WebFrame *)webFrame
410 - (void)webView:(WebView *)view didEnterCallFrame:(WebScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno forWebFrame:(WebFrame *)webFrame
415 id old = currentFrame;
416 currentFrame = [frame retain];
419 NSArray *args = [NSArray arrayWithObjects:[NSNumber numberWithInt:sid], [NSNumber numberWithInt:lineno], nil];
420 [[webView windowScriptObject] callWebScriptMethod:@"didEnterCallFrame" withArguments:args];
423 - (void)webView:(WebView *)view willExecuteStatement:(WebScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno forWebFrame:(WebFrame *)webFrame
428 NSArray *args = [NSArray arrayWithObjects:[NSNumber numberWithInt:sid], [NSNumber numberWithInt:lineno], nil];
429 [[webView windowScriptObject] callWebScriptMethod:@"willExecuteStatement" withArguments:args];
432 - (void)webView:(WebView *)view willLeaveCallFrame:(WebScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno forWebFrame:(WebFrame *)webFrame
437 NSArray *args = [NSArray arrayWithObjects:[NSNumber numberWithInt:sid], [NSNumber numberWithInt:lineno], nil];
438 [[webView windowScriptObject] callWebScriptMethod:@"willLeaveCallFrame" withArguments:args];
440 id old = currentFrame;
441 currentFrame = [[frame caller] retain];