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 "WebScriptDebugServer.h"
30 #import "WebScriptDebugServerPrivate.h"
31 #import "WebViewInternal.h"
33 #import <JavaScriptCore/Assertions.h>
35 NSString *WebScriptDebugServerProcessNameKey = @"WebScriptDebugServerProcessNameKey";
36 NSString *WebScriptDebugServerProcessBundleIdentifierKey = @"WebScriptDebugServerProcessBundleIdentifierKey";
37 NSString *WebScriptDebugServerProcessIdentifierKey = @"WebScriptDebugServerProcessIdentifierKey";
39 NSString *WebScriptDebugServerQueryNotification = @"WebScriptDebugServerQueryNotification";
40 NSString *WebScriptDebugServerQueryReplyNotification = @"WebScriptDebugServerQueryReplyNotification";
42 NSString *WebScriptDebugServerDidLoadNotification = @"WebScriptDebugServerDidLoadNotification";
43 NSString *WebScriptDebugServerWillUnloadNotification = @"WebScriptDebugServerWillUnloadNotification";
45 @implementation WebScriptDebugServer
47 static WebScriptDebugServer *sharedServer = nil;
48 static unsigned listenerCount = 0;
50 + (WebScriptDebugServer *)sharedScriptDebugServer
53 sharedServer = [[WebScriptDebugServer alloc] init];
57 + (unsigned)listenerCount
66 NSProcessInfo *processInfo = [NSProcessInfo processInfo];
67 serverName = [[NSString alloc] initWithFormat:@"WebScriptDebugServer-%@-%d", [processInfo processName], [processInfo processIdentifier]];
69 serverConnection = [[NSConnection alloc] init];
70 if ([serverConnection registerName:serverName]) {
71 [serverConnection setRootObject:self];
72 NSProcessInfo *processInfo = [NSProcessInfo processInfo];
73 NSDictionary *info = [[NSDictionary alloc] initWithObjectsAndKeys:[processInfo processName], WebScriptDebugServerProcessNameKey,
74 [[NSBundle mainBundle] bundleIdentifier], WebScriptDebugServerProcessBundleIdentifierKey,
75 [NSNumber numberWithInt:[processInfo processIdentifier]], WebScriptDebugServerProcessIdentifierKey, nil];
76 [[NSDistributedNotificationCenter defaultCenter] postNotificationName:WebScriptDebugServerDidLoadNotification object:serverName userInfo:info];
79 [serverConnection release];
80 serverConnection = nil;
83 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationTerminating:) name:NSApplicationWillTerminateNotification object:nil];
84 [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(serverQuery:) name:WebScriptDebugServerQueryNotification object:nil];
86 listeners = [[NSMutableSet alloc] init];
93 ASSERT(listenerCount >= [listeners count]);
94 listenerCount -= [listeners count];
96 [self detachScriptDebuggerFromAllWebViews];
97 [[NSNotificationCenter defaultCenter] removeObserver:self name:NSApplicationWillTerminateNotification object:nil];
98 [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:WebScriptDebugServerQueryNotification object:nil];
99 [[NSDistributedNotificationCenter defaultCenter] postNotificationName:WebScriptDebugServerWillUnloadNotification object:serverName];
100 [[NSNotificationCenter defaultCenter] removeObserver:self name:NSConnectionDidDieNotification object:nil];
101 [serverConnection invalidate];
102 [serverConnection release];
103 [serverName release];
108 - (void)applicationTerminating:(NSNotification *)notifiction
110 [[NSDistributedNotificationCenter defaultCenter] postNotificationName:WebScriptDebugServerWillUnloadNotification object:serverName];
113 - (void)attachScriptDebuggerToAllWebViews
115 [WebView _makeAllWebViewsPerformSelector:@selector(_attachScriptDebuggerToAllFrames)];
118 - (void)detachScriptDebuggerFromAllWebViews
120 [WebView _makeAllWebViewsPerformSelector:@selector(_detachScriptDebuggerFromAllFrames)];
123 - (void)serverQuery:(NSNotification *)notification
125 NSProcessInfo *processInfo = [NSProcessInfo processInfo];
126 NSDictionary *info = [[NSDictionary alloc] initWithObjectsAndKeys:[processInfo processName], WebScriptDebugServerProcessNameKey,
127 [[NSBundle mainBundle] bundleIdentifier], WebScriptDebugServerProcessBundleIdentifierKey,
128 [NSNumber numberWithInt:[processInfo processIdentifier]], WebScriptDebugServerProcessIdentifierKey, nil];
129 [[NSDistributedNotificationCenter defaultCenter] postNotificationName:WebScriptDebugServerQueryReplyNotification object:serverName userInfo:info];
133 - (void)listenerConnectionDidDie:(NSNotification *)notification
135 NSConnection *connection = [notification object];
136 NSMutableSet *listenersToRemove = [[NSMutableSet alloc] initWithCapacity:[listeners count]];
137 NSEnumerator *enumerator = [listeners objectEnumerator];
138 NSDistantObject *listener = nil;
140 [[NSNotificationCenter defaultCenter] removeObserver:self name:NSConnectionDidDieNotification object:connection];
142 while ((listener = [enumerator nextObject]))
143 if ([[listener connectionForProxy] isEqualTo:connection])
144 [listenersToRemove addObject:listener];
146 ASSERT(listenerCount >= [listenersToRemove count]);
147 listenerCount -= [listenersToRemove count];
148 [listeners minusSet:listenersToRemove];
149 [listenersToRemove release];
152 [self detachScriptDebuggerFromAllWebViews];
155 - (oneway void)addListener:(id<WebScriptDebugListener>)listener
157 // can't use isKindOfClass: here because that will send over the wire and not check the proxy object
158 if (!listener || [listener class] != [NSDistantObject class] || ![listener conformsToProtocol:@protocol(WebScriptDebugListener)])
161 [listeners addObject:listener];
162 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(listenerConnectionDidDie:) name:NSConnectionDidDieNotification object:[(NSDistantObject *)listener connectionForProxy]];
163 if (listenerCount == 1)
164 [self attachScriptDebuggerToAllWebViews];
167 - (oneway void)removeListener:(id<WebScriptDebugListener>)listener
169 if (!listener || ![listeners containsObject:listener])
171 ASSERT(listenerCount >= 1);
173 [[NSNotificationCenter defaultCenter] removeObserver:self name:NSConnectionDidDieNotification object:[(NSDistantObject *)listener connectionForProxy]];
174 [listeners removeObject:listener];
176 [self detachScriptDebuggerFromAllWebViews];
191 - (oneway void)resume
197 - (oneway BOOL)isPaused
202 - (void)suspendProcessIfPaused
204 // this method will suspend this process when called during the dubugging callbacks
205 // we need to do this to implement breakpoints and pausing of JavaScript
208 [[NSRunLoop currentRunLoop] runMode:NSConnectionReplyMode beforeDate:[NSDate distantFuture]];
216 - (void)webView:(WebView *)webView didLoadMainResourceForDataSource:(WebDataSource *)dataSource
218 if (![listeners count] || inCallback)
223 NSEnumerator *enumerator = [listeners objectEnumerator];
224 NSDistantObject <WebScriptDebugListener> *listener = nil;
226 while ((listener = [enumerator nextObject])) {
227 if ([[listener connectionForProxy] isValid])
228 [listener webView:webView didLoadMainResourceForDataSource:dataSource];
234 - (void)webView:(WebView *)webView didParseSource:(NSString *)source
235 baseLineNumber:(unsigned)lineNumber
238 forWebFrame:(WebFrame *)webFrame
240 if (![listeners count] || inCallback)
245 NSEnumerator *enumerator = [listeners objectEnumerator];
246 NSDistantObject <WebScriptDebugListener> *listener = nil;
248 while ((listener = [enumerator nextObject])) {
249 if ([[listener connectionForProxy] isValid])
250 [listener webView:webView didParseSource:source baseLineNumber:lineNumber fromURL:url sourceId:sid forWebFrame:webFrame];
256 - (void)webView:(WebView *)webView failedToParseSource:(NSString *)source
257 baseLineNumber:(unsigned)lineNumber
259 withError:(NSError *)error
260 forWebFrame:(WebFrame *)webFrame
262 if (![listeners count] || inCallback)
267 NSEnumerator *enumerator = [listeners objectEnumerator];
268 NSDistantObject <WebScriptDebugListener> *listener = nil;
270 while ((listener = [enumerator nextObject])) {
271 if ([[listener connectionForProxy] isValid])
272 [listener webView:webView failedToParseSource:source baseLineNumber:lineNumber fromURL:url withError:error forWebFrame:webFrame];
278 - (void)webView:(WebView *)webView didEnterCallFrame:(WebScriptCallFrame *)frame
281 forWebFrame:(WebFrame *)webFrame
283 if (![listeners count] || inCallback)
288 NSEnumerator *enumerator = [listeners objectEnumerator];
289 NSDistantObject <WebScriptDebugListener> *listener = nil;
291 while ((listener = [enumerator nextObject])) {
292 if ([[listener connectionForProxy] isValid])
293 [listener webView:webView didEnterCallFrame:frame sourceId:sid line:lineno forWebFrame:webFrame];
296 [self suspendProcessIfPaused];
301 - (void)webView:(WebView *)webView willExecuteStatement:(WebScriptCallFrame *)frame
304 forWebFrame:(WebFrame *)webFrame
306 if (![listeners count] || inCallback)
311 NSEnumerator *enumerator = [listeners objectEnumerator];
312 NSDistantObject <WebScriptDebugListener> *listener = nil;
314 while ((listener = [enumerator nextObject])) {
315 if ([[listener connectionForProxy] isValid])
316 [listener webView:webView willExecuteStatement:frame sourceId:sid line:lineno forWebFrame:webFrame];
319 [self suspendProcessIfPaused];
324 - (void)webView:(WebView *)webView willLeaveCallFrame:(WebScriptCallFrame *)frame
327 forWebFrame:(WebFrame *)webFrame
329 if (![listeners count] || inCallback)
334 NSEnumerator *enumerator = [listeners objectEnumerator];
335 NSDistantObject <WebScriptDebugListener> *listener = nil;
337 while ((listener = [enumerator nextObject])) {
338 if ([[listener connectionForProxy] isValid])
339 [listener webView:webView willLeaveCallFrame:frame sourceId:sid line:lineno forWebFrame:webFrame];
342 [self suspendProcessIfPaused];
347 - (void)webView:(WebView *)webView exceptionWasRaised:(WebScriptCallFrame *)frame
350 forWebFrame:(WebFrame *)webFrame
352 if (![listeners count] || inCallback)
357 NSEnumerator *enumerator = [listeners objectEnumerator];
358 NSDistantObject <WebScriptDebugListener> *listener = nil;
360 while ((listener = [enumerator nextObject])) {
361 if ([[listener connectionForProxy] isValid])
362 [listener webView:webView exceptionWasRaised:frame sourceId:sid line:lineno forWebFrame:webFrame];
365 [self suspendProcessIfPaused];