Revision: 12287
          http://bibdesk.svn.sourceforge.net/bibdesk/?rev=12287&view=rev
Author:   amaxwell
Date:     2008-01-05 12:18:56 -0800 (Sat, 05 Jan 2008)

Log Message:
-----------
Use a pool of WebView instances, with the maximum number tunable via
NSUserDefaults.  This should help keep resource usage (network and memory)
at a sane level in case clients have a large number of web icons to load.
Revert to drawing the placeholder so there's some indication that an icon
is loading.

Modified Paths:
--------------
    trunk/bibdesk_vendorsrc/amaxwell/FileView/FVWebViewIcon.m

Modified: trunk/bibdesk_vendorsrc/amaxwell/FileView/FVWebViewIcon.m
===================================================================
--- trunk/bibdesk_vendorsrc/amaxwell/FileView/FVWebViewIcon.m   2008-01-05 
20:13:55 UTC (rev 12286)
+++ trunk/bibdesk_vendorsrc/amaxwell/FileView/FVWebViewIcon.m   2008-01-05 
20:18:56 UTC (rev 12287)
@@ -56,13 +56,52 @@
 
 static BOOL FVWebIconDisabled = NO;
 
+// webview pool variables to keep memory usage down; pool size is tunable
+static NSInteger __maxWebViews = 5;
+static NSMutableArray *__availableWebViews = nil;
+static NSInteger __numberOfWebViews = 0;
+static NSString * const FVWebIconWebViewAvailableNotificationName = 
@"FVWebIconWebViewAvailableNotificationName";
+
+// size of the view frame; large enough to fit a reasonably sized page
+static const NSSize __webViewSize = (NSSize){ 1000, 900 };
+
+// framework private; notifies the file view that a webkit-async load has 
finished
 NSString * const FVWebIconUpdatedNotificationName = 
@"FVWebIconUpdatedNotificationName";
 
 + (void)initialize
 {
     FVWebIconDisabled = [[NSUserDefaults standardUserDefaults] 
boolForKey:@"FVWebIconDisabled"];
+    NSInteger maxViews = [[NSUserDefaults standardUserDefaults] 
integerForKey:@"FVWebIconMaximumNumberOfWebViews"];
+    if (maxViews > 0)
+        __maxWebViews = maxViews;
+    
+    __availableWebViews = [[NSMutableArray alloc] 
initWithCapacity:__maxWebViews];
 }
 
+// return nil if __maxWebViews is exceeded
++ (WebView *)popWebView 
+{
+    NSAssert2(pthread_main_np() != 0, @"*** threading violation *** -[%@ 
[EMAIL PROTECTED] requires main thread", [self class], 
NSStringFromSelector(_cmd));
+    WebView *nextView = nil;
+    if ([__availableWebViews count]) {
+        nextView = [__availableWebViews lastObject];
+        [__availableWebViews removeLastObject];
+    }
+    else if (__numberOfWebViews <= __maxWebViews) {
+        nextView = [[WebView alloc] initWithFrame:NSMakeRect(0, 0, 
__webViewSize.width, __webViewSize.height)];
+        __numberOfWebViews++;
+    }
+    return nextView;
+}
+
++ (void)pushWebView:(WebView *)aView
+{
+    NSParameterAssert(nil != aView);
+    NSAssert2(pthread_main_np() != 0, @"*** threading violation *** -[%@ 
[EMAIL PROTECTED] requires main thread", [self class], 
NSStringFromSelector(_cmd));
+    [__availableWebViews insertObject:aView atIndex:0];
+    [[NSNotificationCenter defaultCenter] 
postNotificationName:FVWebIconWebViewAvailableNotificationName object:self];
+}
+
 - (id)initWithURL:(NSURL *)aURL;
 {    
     NSParameterAssert(nil != [aURL scheme] && NO == [aURL isFileURL]);
@@ -100,11 +139,13 @@
 - (void)_releaseWebView
 {
     NSAssert2(pthread_main_np() != 0, @"*** threading violation *** -[%@ 
[EMAIL PROTECTED] requires main thread", [self class], 
NSStringFromSelector(_cmd));
-    [_webView setPolicyDelegate:nil];
-    [_webView setFrameLoadDelegate:nil];
-    [_webView stopLoading:nil];
-    [_webView release];
-    _webView = nil;
+    if (nil != _webView) {
+        [_webView setPolicyDelegate:nil];
+        [_webView setFrameLoadDelegate:nil];
+        [_webView stopLoading:nil];
+        [[self class] pushWebView:_webView];
+        _webView = nil;
+    }
 }
 
 - (void)dealloc
@@ -149,8 +190,8 @@
 // size of the _fullImageRef
 - (NSSize)size { return (NSSize){ 500, 450 }; }
 
-// actual size of the webview; large enough to fit a reasonably sized page
-- (NSSize)_webviewSize { return (NSSize){ 1000, 900 }; }
+// actual size of the webview
+- (NSSize)_webviewSize { return __webViewSize; }
 
 // size of the _thumbnailRef (1/5 of webview size)
 - (NSSize)_thumbnailSize { return (NSSize){ 200, 180 }; }
@@ -208,6 +249,7 @@
 - (void)webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError 
*)error forFrame:(WebFrame *)frame;
 {
     NSAssert2(pthread_main_np() != 0, @"*** threading violation *** -[%@ 
[EMAIL PROTECTED] requires main thread", [self class], 
NSStringFromSelector(_cmd));
+    NSParameterAssert([sender isEqual:_webView]);
     pthread_mutex_lock(&_mutex);
     _webviewFailed = YES;
     [self _releaseWebView];
@@ -218,6 +260,7 @@
 - (void)webView:(WebView *)sender didFailLoadWithError:(NSError *)error 
forFrame:(WebFrame *)frame;
 {
     NSAssert2(pthread_main_np() != 0, @"*** threading violation *** -[%@ 
[EMAIL PROTECTED] requires main thread", [self class], 
NSStringFromSelector(_cmd));
+    NSParameterAssert([sender isEqual:_webView]);
     pthread_mutex_lock(&_mutex);
     _webviewFailed = YES;
     [self _releaseWebView];
@@ -289,6 +332,7 @@
 - (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame
 {
     NSAssert2(pthread_main_np() != 0, @"*** threading violation *** -[%@ 
[EMAIL PROTECTED] requires main thread", [self class], 
NSStringFromSelector(_cmd));
+    NSParameterAssert([sender isEqual:_webView]);
     // wait for the main frame
     if ([frame isEqual:[_webView mainFrame]])
         [self _mainFrameDidFinishLoading];
@@ -297,6 +341,7 @@
 - (void)webView:(WebView *)sender decidePolicyForMIMEType:(NSString *)type 
request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id < 
WebPolicyDecisionListener >)listener
 {
     NSAssert2(pthread_main_np() != 0, @"*** threading violation *** -[%@ 
[EMAIL PROTECTED] requires main thread", [self class], 
NSStringFromSelector(_cmd));
+    NSParameterAssert([sender isEqual:_webView]);
     
     // !!! Better to just load text/html and ignore everything else?  The 
point of implementing this method is to ignore PDF.  It doesn't show up in the 
thumbnail and it's slow to load, so there's no point in loading it.
     
@@ -340,39 +385,48 @@
     // Originally just checked nil here, but logging showed that a webview was 
instantiated twice and the page loaded both times.  A nil webview is not a 
sufficient condition, since there's a delay between needsRenderForSize: and 
renderOffscreen; remember that the webview isn't rendering synchronously as in 
the other subclasses, so it may finish in between those calls.
     
     NSAssert(nil == _webView, @"*** Render error *** 
renderOffscreenOnMainThread called when _webView already exists");
-
-    NSSize size = [self _webviewSize];
     
-    // always use the WebKit cache, and use a short timeout (default is 60 
seconds)
-    NSURLRequest *request = [NSURLRequest requestWithURL:_httpURL 
cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:10.0];
-    
-    // See also 
http://lists.apple.com/archives/quicklook-dev/2007/Nov/msg00047.html
-    // Note: changed from blocking to non-blocking; we now just keep state and 
rely on the
-    // delegate methods.
-    
-    _webView = [[WebView alloc] initWithFrame:NSMakeRect(0, 0, size.width, 
size.height)];
-    
-    Class cls = [self class];
-    NSString *prefIdentifier = [NSString stringWithFormat:@"[EMAIL 
PROTECTED]@", [[NSBundle bundleForClass:cls] bundleIdentifier], cls];
-    [_webView setPreferencesIdentifier:prefIdentifier];
-    
-    WebPreferences *prefs = [_webView preferences];
-    [prefs setPlugInsEnabled:NO];
-    [prefs setJavaEnabled:NO];
-    [prefs setJavaScriptCanOpenWindowsAutomatically:NO];
-    [prefs setJavaScriptEnabled:NO];
-    [prefs setAllowsAnimatedImages:NO];
-    
-    // most memory-efficient setting; remote resources are still cached to disk
-    if ([prefs respondsToSelector:@selector(setCacheModel:)])
-        [prefs setCacheModel:WebCacheModelDocumentViewer];
-    
-    [_webView setFrameLoadDelegate:self];
-    [_webView setPolicyDelegate:self];
-    [[_webView mainFrame] loadRequest:request];
+    _webView = [[self class] popWebView];
 
+    if (nil == _webView) {
+        [[NSNotificationCenter defaultCenter] addObserver:self 
selector:@selector(_handleWebViewAvailableNotification:) 
name:FVWebIconWebViewAvailableNotificationName object:[self class]];
+    }
+    else {
+        // always use the WebKit cache, and use a short timeout (default is 60 
seconds)
+        NSURLRequest *request = [NSURLRequest requestWithURL:_httpURL 
cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:10.0];
+        
+        // See also 
http://lists.apple.com/archives/quicklook-dev/2007/Nov/msg00047.html
+        // Note: changed from blocking to non-blocking; we now just keep state 
and rely on the
+        // delegate methods.
+        
+        Class cls = [self class];
+        NSString *prefIdentifier = [NSString stringWithFormat:@"[EMAIL 
PROTECTED]@", [[NSBundle bundleForClass:cls] bundleIdentifier], cls];
+        [_webView setPreferencesIdentifier:prefIdentifier];
+        
+        WebPreferences *prefs = [_webView preferences];
+        [prefs setPlugInsEnabled:NO];
+        [prefs setJavaEnabled:NO];
+        [prefs setJavaScriptCanOpenWindowsAutomatically:NO];
+        [prefs setJavaScriptEnabled:NO];
+        [prefs setAllowsAnimatedImages:NO];
+        
+        // most memory-efficient setting; remote resources are still cached to 
disk
+        if ([prefs respondsToSelector:@selector(setCacheModel:)])
+            [prefs setCacheModel:WebCacheModelDocumentViewer];
+        
+        [_webView setFrameLoadDelegate:self];
+        [_webView setPolicyDelegate:self];
+        [[_webView mainFrame] loadRequest:request];        
+    }
 }
 
+- (void)_handleWebViewAvailableNotification:(NSNotification *)aNotification
+{
+    NSAssert2(pthread_main_np() != 0, @"*** threading violation *** -[%@ 
[EMAIL PROTECTED] requires main thread", [self class], 
NSStringFromSelector(_cmd));
+    [[NSNotificationCenter defaultCenter] removeObserver:self 
name:FVWebIconWebViewAvailableNotificationName object:[self class]];
+    [self renderOffscreenOnMainThread];
+}
+
 - (void)renderOffscreen
 {
     // !!! early return here after a cache check
@@ -433,7 +487,9 @@
             CGContextDrawImage(context, drawRect, _fullImageRef);
         else if (_thumbnailRef)
             CGContextDrawImage(context, drawRect, _thumbnailRef);
-        else 
+        else if (NO == _webviewFailed)
+            [self _drawPlaceholderInRect:dstRect inCGContext:context];
+        else
             [_fallbackIcon drawInRect:dstRect inCGContext:context];
         
         pthread_mutex_unlock(&_mutex);


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.

-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2005.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
Bibdesk-commit mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/bibdesk-commit

Reply via email to