webengine/osswebengine/WebKit/DefaultDelegates/WebScriptDebugServer.m
changeset 0 dd21522fd290
equal deleted inserted replaced
-1:000000000000 0:dd21522fd290
       
     1 /*
       
     2  * Copyright (C) 2006, 2007 Apple 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 "WebScriptDebugServer.h"
       
    30 #import "WebScriptDebugServerPrivate.h"
       
    31 #import "WebViewInternal.h"
       
    32 
       
    33 #import <JavaScriptCore/Assertions.h>
       
    34 
       
    35 NSString *WebScriptDebugServerProcessNameKey = @"WebScriptDebugServerProcessNameKey";
       
    36 NSString *WebScriptDebugServerProcessBundleIdentifierKey = @"WebScriptDebugServerProcessBundleIdentifierKey";
       
    37 NSString *WebScriptDebugServerProcessIdentifierKey = @"WebScriptDebugServerProcessIdentifierKey";
       
    38 
       
    39 NSString *WebScriptDebugServerQueryNotification = @"WebScriptDebugServerQueryNotification";
       
    40 NSString *WebScriptDebugServerQueryReplyNotification = @"WebScriptDebugServerQueryReplyNotification";
       
    41 
       
    42 NSString *WebScriptDebugServerDidLoadNotification = @"WebScriptDebugServerDidLoadNotification";
       
    43 NSString *WebScriptDebugServerWillUnloadNotification = @"WebScriptDebugServerWillUnloadNotification";
       
    44 
       
    45 @implementation WebScriptDebugServer
       
    46 
       
    47 static WebScriptDebugServer *sharedServer = nil;
       
    48 static unsigned listenerCount = 0;
       
    49 
       
    50 + (WebScriptDebugServer *)sharedScriptDebugServer
       
    51 {
       
    52     if (!sharedServer)
       
    53         sharedServer = [[WebScriptDebugServer alloc] init];
       
    54     return sharedServer;
       
    55 }
       
    56 
       
    57 + (unsigned)listenerCount
       
    58 {
       
    59     return listenerCount;
       
    60 }
       
    61 
       
    62 - (id)init
       
    63 {
       
    64     self = [super init];
       
    65 
       
    66     NSProcessInfo *processInfo = [NSProcessInfo processInfo];
       
    67     serverName = [[NSString alloc] initWithFormat:@"WebScriptDebugServer-%@-%d", [processInfo processName], [processInfo processIdentifier]];
       
    68 
       
    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];
       
    77         [info release];
       
    78     } else {
       
    79         [serverConnection release];
       
    80         serverConnection = nil;
       
    81     }
       
    82 
       
    83     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationTerminating:) name:NSApplicationWillTerminateNotification object:nil];
       
    84     [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(serverQuery:) name:WebScriptDebugServerQueryNotification object:nil];
       
    85 
       
    86     listeners = [[NSMutableSet alloc] init];
       
    87 
       
    88     return self;
       
    89 }
       
    90 
       
    91 - (void)dealloc
       
    92 {
       
    93     // FIXME: Bad to do all this work in dealloc. What about under GC?
       
    94 
       
    95     ASSERT(listenerCount >= [listeners count]);
       
    96     listenerCount -= [listeners count];
       
    97     if (!listenerCount)
       
    98         [self detachScriptDebuggerFromAllWebViews];
       
    99     [[NSNotificationCenter defaultCenter] removeObserver:self name:NSApplicationWillTerminateNotification object:nil];
       
   100     [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:WebScriptDebugServerQueryNotification object:nil];
       
   101     [[NSDistributedNotificationCenter defaultCenter] postNotificationName:WebScriptDebugServerWillUnloadNotification object:serverName];
       
   102     [[NSNotificationCenter defaultCenter] removeObserver:self name:NSConnectionDidDieNotification object:nil];
       
   103     [serverConnection invalidate];
       
   104     [serverConnection release];
       
   105     [serverName release];
       
   106     [listeners release];
       
   107     [super dealloc];
       
   108 }
       
   109 
       
   110 - (void)applicationTerminating:(NSNotification *)notifiction
       
   111 {
       
   112     [[NSDistributedNotificationCenter defaultCenter] postNotificationName:WebScriptDebugServerWillUnloadNotification object:serverName];
       
   113 }
       
   114 
       
   115 - (void)attachScriptDebuggerToAllWebViews
       
   116 {
       
   117     [WebView _makeAllWebViewsPerformSelector:@selector(_attachScriptDebuggerToAllFrames)];
       
   118 }
       
   119 
       
   120 - (void)detachScriptDebuggerFromAllWebViews
       
   121 {
       
   122     [WebView _makeAllWebViewsPerformSelector:@selector(_detachScriptDebuggerFromAllFrames)];
       
   123 }
       
   124 
       
   125 - (void)serverQuery:(NSNotification *)notification
       
   126 {
       
   127     NSProcessInfo *processInfo = [NSProcessInfo processInfo];
       
   128     NSDictionary *info = [[NSDictionary alloc] initWithObjectsAndKeys:[processInfo processName], WebScriptDebugServerProcessNameKey,
       
   129         [[NSBundle mainBundle] bundleIdentifier], WebScriptDebugServerProcessBundleIdentifierKey,
       
   130         [NSNumber numberWithInt:[processInfo processIdentifier]], WebScriptDebugServerProcessIdentifierKey, nil];
       
   131     [[NSDistributedNotificationCenter defaultCenter] postNotificationName:WebScriptDebugServerQueryReplyNotification object:serverName userInfo:info];
       
   132     [info release];
       
   133 }
       
   134 
       
   135 - (void)listenerConnectionDidDie:(NSNotification *)notification
       
   136 {
       
   137     NSConnection *connection = [notification object];
       
   138     NSMutableSet *listenersToRemove = [[NSMutableSet alloc] initWithCapacity:[listeners count]];
       
   139     NSEnumerator *enumerator = [listeners objectEnumerator];
       
   140     NSDistantObject *listener = nil;
       
   141 
       
   142     [[NSNotificationCenter defaultCenter] removeObserver:self name:NSConnectionDidDieNotification object:connection];
       
   143 
       
   144     while ((listener = [enumerator nextObject]))
       
   145         if ([[listener connectionForProxy] isEqualTo:connection])
       
   146             [listenersToRemove addObject:listener];
       
   147 
       
   148     ASSERT(listenerCount >= [listenersToRemove count]);
       
   149     listenerCount -= [listenersToRemove count];
       
   150     [listeners minusSet:listenersToRemove];
       
   151     [listenersToRemove release];
       
   152 
       
   153     if (!listenerCount)
       
   154         [self detachScriptDebuggerFromAllWebViews];
       
   155 }
       
   156 
       
   157 - (oneway void)addListener:(id<WebScriptDebugListener>)listener
       
   158 {
       
   159     // can't use isKindOfClass: here because that will send over the wire and not check the proxy object
       
   160     if (!listener || [listener class] != [NSDistantObject class] || ![listener conformsToProtocol:@protocol(WebScriptDebugListener)])
       
   161         return;
       
   162     listenerCount++;
       
   163     [listeners addObject:listener];
       
   164     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(listenerConnectionDidDie:) name:NSConnectionDidDieNotification object:[(NSDistantObject *)listener connectionForProxy]];
       
   165     if (listenerCount == 1)
       
   166         [self attachScriptDebuggerToAllWebViews];
       
   167 }
       
   168 
       
   169 - (oneway void)removeListener:(id<WebScriptDebugListener>)listener
       
   170 {
       
   171     if (!listener || ![listeners containsObject:listener])
       
   172         return;
       
   173     ASSERT(listenerCount >= 1);
       
   174     listenerCount--;
       
   175     [[NSNotificationCenter defaultCenter] removeObserver:self name:NSConnectionDidDieNotification object:[(NSDistantObject *)listener connectionForProxy]];
       
   176     [listeners removeObject:listener];
       
   177     if (!listenerCount)
       
   178         [self detachScriptDebuggerFromAllWebViews];
       
   179 }
       
   180 
       
   181 - (oneway void)step
       
   182 {
       
   183     step = YES;
       
   184     paused = NO;
       
   185 }
       
   186 
       
   187 - (oneway void)pause
       
   188 {
       
   189     paused = YES;
       
   190     step = NO;
       
   191 }
       
   192 
       
   193 - (oneway void)resume
       
   194 {
       
   195     paused = NO;
       
   196     step = NO;
       
   197 }
       
   198 
       
   199 - (oneway BOOL)isPaused
       
   200 {
       
   201     return paused;
       
   202 }
       
   203 
       
   204 - (void)suspendProcessIfPaused
       
   205 {
       
   206     // this method will suspend this process when called during the dubugging callbacks
       
   207     // we need to do this to implement breakpoints and pausing of JavaScript
       
   208 
       
   209     while (paused)
       
   210         [[NSRunLoop currentRunLoop] runMode:NSConnectionReplyMode beforeDate:[NSDate distantFuture]];
       
   211 
       
   212     if (step) {
       
   213         step = NO;
       
   214         paused = YES;
       
   215     }
       
   216 }
       
   217 
       
   218 - (void)webView:(WebView *)webView didLoadMainResourceForDataSource:(WebDataSource *)dataSource
       
   219 {
       
   220     if (![listeners count] || inCallback)
       
   221         return;
       
   222 
       
   223     inCallback = YES;
       
   224 
       
   225     NSEnumerator *enumerator = [listeners objectEnumerator];
       
   226     NSDistantObject <WebScriptDebugListener> *listener = nil;
       
   227 
       
   228     while ((listener = [enumerator nextObject])) {
       
   229         if ([[listener connectionForProxy] isValid])
       
   230             [listener webView:webView didLoadMainResourceForDataSource:dataSource];
       
   231     }
       
   232 
       
   233     inCallback = NO;
       
   234 }
       
   235 
       
   236 - (void)webView:(WebView *)webView       didParseSource:(NSString *)source
       
   237                                          baseLineNumber:(NSUInteger)lineNumber
       
   238                                                 fromURL:(NSURL *)url
       
   239                                                sourceId:(int)sid
       
   240                                             forWebFrame:(WebFrame *)webFrame
       
   241 {
       
   242     if (![listeners count] || inCallback)
       
   243         return;
       
   244 
       
   245     inCallback = YES;
       
   246 
       
   247     NSEnumerator *enumerator = [listeners objectEnumerator];
       
   248     NSDistantObject <WebScriptDebugListener> *listener = nil;
       
   249 
       
   250     while ((listener = [enumerator nextObject])) {
       
   251         if ([[listener connectionForProxy] isValid])
       
   252             [listener webView:webView didParseSource:source baseLineNumber:lineNumber fromURL:url sourceId:sid forWebFrame:webFrame];
       
   253     }
       
   254 
       
   255     inCallback = NO;
       
   256 }
       
   257 
       
   258 - (void)webView:(WebView *)webView  failedToParseSource:(NSString *)source
       
   259                                          baseLineNumber:(NSUInteger)lineNumber
       
   260                                                 fromURL:(NSURL *)url
       
   261                                               withError:(NSError *)error
       
   262                                             forWebFrame:(WebFrame *)webFrame
       
   263 {
       
   264     if (![listeners count] || inCallback)
       
   265         return;
       
   266 
       
   267     inCallback = YES;
       
   268 
       
   269     NSEnumerator *enumerator = [listeners objectEnumerator];
       
   270     NSDistantObject <WebScriptDebugListener> *listener = nil;
       
   271 
       
   272     while ((listener = [enumerator nextObject])) {
       
   273         if ([[listener connectionForProxy] isValid])
       
   274             [listener webView:webView failedToParseSource:source baseLineNumber:lineNumber fromURL:url withError:error forWebFrame:webFrame];
       
   275     }
       
   276 
       
   277     inCallback = NO;
       
   278 }
       
   279 
       
   280 - (void)webView:(WebView *)webView    didEnterCallFrame:(WebScriptCallFrame *)frame
       
   281                                                sourceId:(int)sid
       
   282                                                    line:(int)lineno
       
   283                                             forWebFrame:(WebFrame *)webFrame
       
   284 {
       
   285     if (![listeners count] || inCallback)
       
   286         return;
       
   287 
       
   288     inCallback = YES;
       
   289 
       
   290     NSEnumerator *enumerator = [listeners objectEnumerator];
       
   291     NSDistantObject <WebScriptDebugListener> *listener = nil;
       
   292 
       
   293     while ((listener = [enumerator nextObject])) {
       
   294         if ([[listener connectionForProxy] isValid])
       
   295             [listener webView:webView didEnterCallFrame:frame sourceId:sid line:lineno forWebFrame:webFrame];
       
   296     }
       
   297 
       
   298     [self suspendProcessIfPaused];
       
   299 
       
   300     inCallback = NO;
       
   301 }
       
   302 
       
   303 - (void)webView:(WebView *)webView willExecuteStatement:(WebScriptCallFrame *)frame
       
   304                                                sourceId:(int)sid
       
   305                                                    line:(int)lineno
       
   306                                             forWebFrame:(WebFrame *)webFrame
       
   307 {
       
   308     if (![listeners count] || inCallback)
       
   309         return;
       
   310 
       
   311     inCallback = YES;
       
   312 
       
   313     NSEnumerator *enumerator = [listeners objectEnumerator];
       
   314     NSDistantObject <WebScriptDebugListener> *listener = nil;
       
   315 
       
   316     while ((listener = [enumerator nextObject])) {
       
   317         if ([[listener connectionForProxy] isValid])
       
   318             [listener webView:webView willExecuteStatement:frame sourceId:sid line:lineno forWebFrame:webFrame];
       
   319     }
       
   320 
       
   321     [self suspendProcessIfPaused];
       
   322 
       
   323     inCallback = NO;
       
   324 }
       
   325 
       
   326 - (void)webView:(WebView *)webView   willLeaveCallFrame:(WebScriptCallFrame *)frame
       
   327                                                sourceId:(int)sid
       
   328                                                    line:(int)lineno
       
   329                                             forWebFrame:(WebFrame *)webFrame
       
   330 {
       
   331     if (![listeners count] || inCallback)
       
   332         return;
       
   333 
       
   334     inCallback = YES;
       
   335 
       
   336     NSEnumerator *enumerator = [listeners objectEnumerator];
       
   337     NSDistantObject <WebScriptDebugListener> *listener = nil;
       
   338 
       
   339     while ((listener = [enumerator nextObject])) {
       
   340         if ([[listener connectionForProxy] isValid])
       
   341             [listener webView:webView willLeaveCallFrame:frame sourceId:sid line:lineno forWebFrame:webFrame];
       
   342     }
       
   343 
       
   344     [self suspendProcessIfPaused];
       
   345 
       
   346     inCallback = NO;
       
   347 }
       
   348 
       
   349 - (void)webView:(WebView *)webView   exceptionWasRaised:(WebScriptCallFrame *)frame
       
   350                                                sourceId:(int)sid
       
   351                                                    line:(int)lineno
       
   352                                             forWebFrame:(WebFrame *)webFrame
       
   353 {
       
   354     if (![listeners count] || inCallback)
       
   355         return;
       
   356 
       
   357     inCallback = YES;
       
   358 
       
   359     NSEnumerator *enumerator = [listeners objectEnumerator];
       
   360     NSDistantObject <WebScriptDebugListener> *listener = nil;
       
   361 
       
   362     while ((listener = [enumerator nextObject])) {
       
   363         if ([[listener connectionForProxy] isValid])
       
   364             [listener webView:webView exceptionWasRaised:frame sourceId:sid line:lineno forWebFrame:webFrame];
       
   365     }
       
   366 
       
   367     [self suspendProcessIfPaused];
       
   368 
       
   369     inCallback = NO;
       
   370 }
       
   371 
       
   372 @end