Fixed: <rdar://problem/3879870> Flash Player unable to stop data stream from continu...
authorcblu <cblu@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 1 Dec 2004 22:44:49 +0000 (22:44 +0000)
committercblu <cblu@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 1 Dec 2004 22:44:49 +0000 (22:44 +0000)
Also improved and cleaned-up the plug-in stream termination code.

        Reviewed by john.

        * Plugins.subproj/WebBaseNetscapePluginStream.h:
        * Plugins.subproj/WebBaseNetscapePluginStream.m:
        (+[WebBaseNetscapePluginStream reasonForError:]): return NPRES_DONE for a nil error
        (-[WebBaseNetscapePluginStream _pluginCancelledConnectionError]): new, factored out from other methods
        (-[WebBaseNetscapePluginStream errorForReason:]): new
        (-[WebBaseNetscapePluginStream dealloc]): release MIME type
        (-[WebBaseNetscapePluginStream setMIMEType:]): new
        (-[WebBaseNetscapePluginStream startStreamResponseURL:expectedContentLength:lastModifiedDate:MIMEType:]): call setMIMEType so we can use it in _pluginCancelledConnectionError, call renamed methods
        (-[WebBaseNetscapePluginStream _destroyStream]): prepended underscore, replaced some early returns with asserts as the callers are now smarter
        (-[WebBaseNetscapePluginStream _destroyStreamWithReason:]): prepended underscore, only call _destroyStream if there is an error or if the load is complete and there is no more data to be streamed
        (-[WebBaseNetscapePluginStream cancelLoadWithError:]): new, overridden by subclasses to cancel the actual NSURLConnection
        (-[WebBaseNetscapePluginStream destroyStreamWithError:]): new, calls _destroyStreamWithReason
        (-[WebBaseNetscapePluginStream finishedLoadingWithData:]): call renamed methods
        (-[WebBaseNetscapePluginStream _deliverData]): prepended underscore, call cancelLoadAndDestroyStreamWithError if NPP_Write returns a negative number
        * Plugins.subproj/WebBaseNetscapePluginView.m:
        (-[WebBaseNetscapePluginView destroyStream:reason:]): call cancelLoadAndDestroyStreamWithError
        * Plugins.subproj/WebNetscapePluginRepresentation.m:
        (-[WebNetscapePluginRepresentation receivedError:withDataSource:]): call destroyStreamWithError
        (-[WebNetscapePluginRepresentation cancelLoadWithError:]): new, override method, tell the data source to stop loading
        * Plugins.subproj/WebNetscapePluginStream.m:
        (-[WebNetscapePluginStream cancelLoadWithError:]): new, override method, tell the loader to stop
        (-[WebNetscapePluginStream stop]): call cancelLoadAndDestroyStreamWithError
        (-[WebNetscapePluginConnectionDelegate isDone]): new
        (-[WebNetscapePluginConnectionDelegate didReceiveResponse:]): call cancelLoadAndDestroyStreamWithError
        (-[WebNetscapePluginConnectionDelegate didFailWithError:]): call destroyStreamWithError

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@8099 268f45cc-cd09-0410-ab3c-d52691b4dbfc

WebKit/ChangeLog
WebKit/Plugins.subproj/WebBaseNetscapePluginStream.h
WebKit/Plugins.subproj/WebBaseNetscapePluginStream.m
WebKit/Plugins.subproj/WebBaseNetscapePluginView.m
WebKit/Plugins.subproj/WebNetscapePluginRepresentation.m
WebKit/Plugins.subproj/WebNetscapePluginStream.m

index c5feb0f946845535c1bbce99793449e1266e595a..976efefa18a08343619bc3a973c83a3f8475e3b6 100644 (file)
@@ -1,3 +1,36 @@
+2004-12-01  Chris Blumenberg  <cblu@apple.com>
+
+       Fixed: <rdar://problem/3879870> Flash Player unable to stop data stream from continuing to download by returning -1 from NPP_Write
+       Also improved and cleaned-up the plug-in stream termination code.
+
+        Reviewed by john.
+
+        * Plugins.subproj/WebBaseNetscapePluginStream.h:
+        * Plugins.subproj/WebBaseNetscapePluginStream.m:
+        (+[WebBaseNetscapePluginStream reasonForError:]): return NPRES_DONE for a nil error
+        (-[WebBaseNetscapePluginStream _pluginCancelledConnectionError]): new, factored out from other methods
+        (-[WebBaseNetscapePluginStream errorForReason:]): new
+        (-[WebBaseNetscapePluginStream dealloc]): release MIME type
+        (-[WebBaseNetscapePluginStream setMIMEType:]): new
+        (-[WebBaseNetscapePluginStream startStreamResponseURL:expectedContentLength:lastModifiedDate:MIMEType:]): call setMIMEType so we can use it in _pluginCancelledConnectionError, call renamed methods
+        (-[WebBaseNetscapePluginStream _destroyStream]): prepended underscore, replaced some early returns with asserts as the callers are now smarter
+        (-[WebBaseNetscapePluginStream _destroyStreamWithReason:]): prepended underscore, only call _destroyStream if there is an error or if the load is complete and there is no more data to be streamed
+        (-[WebBaseNetscapePluginStream cancelLoadWithError:]): new, overridden by subclasses to cancel the actual NSURLConnection
+        (-[WebBaseNetscapePluginStream destroyStreamWithError:]): new, calls _destroyStreamWithReason
+        (-[WebBaseNetscapePluginStream finishedLoadingWithData:]): call renamed methods
+        (-[WebBaseNetscapePluginStream _deliverData]): prepended underscore, call cancelLoadAndDestroyStreamWithError if NPP_Write returns a negative number
+        * Plugins.subproj/WebBaseNetscapePluginView.m:
+        (-[WebBaseNetscapePluginView destroyStream:reason:]): call cancelLoadAndDestroyStreamWithError
+        * Plugins.subproj/WebNetscapePluginRepresentation.m:
+        (-[WebNetscapePluginRepresentation receivedError:withDataSource:]): call destroyStreamWithError
+        (-[WebNetscapePluginRepresentation cancelLoadWithError:]): new, override method, tell the data source to stop loading
+        * Plugins.subproj/WebNetscapePluginStream.m:
+        (-[WebNetscapePluginStream cancelLoadWithError:]): new, override method, tell the loader to stop
+        (-[WebNetscapePluginStream stop]): call cancelLoadAndDestroyStreamWithError
+        (-[WebNetscapePluginConnectionDelegate isDone]): new
+        (-[WebNetscapePluginConnectionDelegate didReceiveResponse:]): call cancelLoadAndDestroyStreamWithError
+        (-[WebNetscapePluginConnectionDelegate didFailWithError:]): call destroyStreamWithError
+
 2004-12-01  Kevin Decker  <kdecker@apple.com>
 
         Reviewed by Harrison.
index 598e8ad783df8e7a8fe4317d845604145606e738..fb5fab940dba01ef7de137c9486ab8257d8609ab 100644 (file)
 @class WebNetscapePluginPackage;
 @class NSURLResponse;
 
-#define WEB_REASON_PLUGIN_CANCELLED -1
-
 @interface WebBaseNetscapePluginStream : NSObject
 {
     NSMutableData *deliveryData;
     NSURL *requestURL;
     NSURL *responseURL;
+    NSString *MIMEType;
+    
     NPP instance;
     uint16 transferMode;
     int32 offset;
@@ -37,6 +37,7 @@
 }
 
 + (NPReason)reasonForError:(NSError *)error;
+- (NSError *)errorForReason:(NPReason)theReason;
 
 - (id)initWithRequestURL:(NSURL *)theRequestURL
            pluginPointer:(NPP)thePluginPointer
 - (void)setResponseURL:(NSURL *)theResponseURL;
 - (void)setPluginPointer:(NPP)pluginPointer;
 
+- (uint16)transferMode;
+
 - (void)startStreamResponseURL:(NSURL *)theResponseURL
          expectedContentLength:(long long)expectedContentLength
               lastModifiedDate:(NSDate *)lastModifiedDate
                       MIMEType:(NSString *)MIMEType;
 - (void)startStreamWithResponse:(NSURLResponse *)r;
+
+// cancelLoadWithError cancels the NSURLConnection and informs WebKit of the load error.
+// This method is overriden by subclasses.
+- (void)cancelLoadWithError:(NSError *)error;
+
+// destroyStreamWithError tells the plug-in that the load is completed (error == nil) or ended in error.
+- (void)destroyStreamWithError:(NSError *)error;
+
+// cancelLoadAndDestoryStreamWithError calls cancelLoadWithError: then destroyStreamWithError:.
+- (void)cancelLoadAndDestroyStreamWithError:(NSError *)error;
+
 - (void)receivedData:(NSData *)data;
 - (void)finishedLoadingWithData:(NSData *)data;
-- (void)receivedError:(NSError *)error;
-- (void)cancelWithReason:(NPReason)theReason;
-- (uint16)transferMode;
 
 @end
index 771587097a1f872552a82120fd92d5df1862c1f5..1939a76b43e83d48bc309c37bd10265e644b52f9 100644 (file)
@@ -6,6 +6,7 @@
 #import <WebKit/WebBaseNetscapePluginStream.h>
 
 #import <WebKit/WebBaseNetscapePluginView.h>
+#import <WebKit/WebKitErrorsPrivate.h>
 #import <WebKit/WebKitLogging.h>
 #import <WebKit/WebNetscapePluginPackage.h>
 #import <WebKit/WebNSObjectExtras.h>
 
 static const char *CarbonPathFromPOSIXPath(const char *posixPath);
 
+#define WEB_REASON_NONE -1
+
 @implementation WebBaseNetscapePluginStream
 
 + (NPReason)reasonForError:(NSError *)error
 {
+    if (error == nil) {
+        return NPRES_DONE;
+    }
     if ([[error domain] isEqualToString:NSURLErrorDomain] && [error code] == NSURLErrorCancelled) {
         return NPRES_USER_BREAK;
     }
     return NPRES_NETWORK_ERR;
 }
 
+- (NSError *)_pluginCancelledConnectionError
+{
+    return [[[NSError alloc] _initWithPluginErrorCode:WebKitErrorPlugInCancelledConnection
+                                           contentURL:responseURL != nil ? responseURL : requestURL
+                                        pluginPageURL:nil
+                                           pluginName:[plugin name]
+                                             MIMEType:MIMEType] autorelease];
+}
+
+- (NSError *)errorForReason:(NPReason)theReason
+{
+    if (theReason == NPRES_DONE) {
+        return nil;
+    }
+    if (theReason == NPRES_USER_BREAK) {
+        return [NSError _webKitErrorWithDomain:NSURLErrorDomain
+                                          code:NSURLErrorCancelled 
+                                           URL:responseURL != nil ? responseURL : requestURL];
+    }
+    return [self _pluginCancelledConnectionError];
+}
+
 - (id)initWithRequestURL:(NSURL *)theRequestURL
            pluginPointer:(NPP)thePluginPointer
               notifyData:(void *)theNotifyData
@@ -67,6 +95,7 @@ static const char *CarbonPathFromPOSIXPath(const char *posixPath);
 
     [requestURL release];
     [responseURL release];
+    [MIMEType release];
     [plugin release];
     [deliveryData release];
     
@@ -125,10 +154,17 @@ static const char *CarbonPathFromPOSIXPath(const char *posixPath);
     NPP_URLNotify =    [plugin NPP_URLNotify];
 }
 
+- (void)setMIMEType:(NSString *)theMIMEType
+{
+    [theMIMEType retain];
+    [MIMEType release];
+    MIMEType = theMIMEType;
+}
+
 - (void)startStreamResponseURL:(NSURL *)URL
          expectedContentLength:(long long)expectedContentLength
               lastModifiedDate:(NSDate *)lastModifiedDate
-                      MIMEType:(NSString *)MIMEType
+                      MIMEType:(NSString *)theMIMEType
 {
     ASSERT(!isTerminated);
     
@@ -137,6 +173,7 @@ static const char *CarbonPathFromPOSIXPath(const char *posixPath);
     }
     
     [self setResponseURL:URL];
+    [self setMIMEType:theMIMEType];
     
     free((void *)stream.url);
     stream.url = strdup([responseURL _web_URLCString]);
@@ -148,7 +185,7 @@ static const char *CarbonPathFromPOSIXPath(const char *posixPath);
     
     transferMode = NP_NORMAL;
     offset = 0;
-    reason = WEB_REASON_PLUGIN_CANCELLED;
+    reason = WEB_REASON_NONE;
 
     // FIXME: Need a way to check if stream is seekable
 
@@ -157,8 +194,8 @@ static const char *CarbonPathFromPOSIXPath(const char *posixPath);
 
     if (npErr != NPERR_NO_ERROR) {
         ERROR("NPP_NewStream failed with error: %d responseURL: %@", npErr, responseURL);
-        // Calling cancelWithReason with WEB_REASON_PLUGIN_CANCELLED cancels the load, but doesn't call NPP_DestroyStream.
-        [self cancelWithReason:WEB_REASON_PLUGIN_CANCELLED];
+        // Calling cancelLoadWithError: cancels the load, but doesn't call NPP_DestroyStream.
+        [self cancelLoadWithError:[self _pluginCancelledConnectionError]];
         return;
     }
 
@@ -174,7 +211,7 @@ static const char *CarbonPathFromPOSIXPath(const char *posixPath);
             break;
         case NP_SEEK:
             ERROR("Stream type: NP_SEEK not yet supported");
-            [self cancelWithReason:NPRES_NETWORK_ERR];
+            [self cancelLoadAndDestroyStreamWithError:[self _pluginCancelledConnectionError]];
             break;
         default:
             ERROR("unknown stream type");
@@ -189,12 +226,15 @@ static const char *CarbonPathFromPOSIXPath(const char *posixPath);
                         MIMEType:[r MIMEType]];
 }
 
-- (void)destroyStream
+- (void)_destroyStream
 {
-    if (isTerminated || ![plugin isLoaded] || [deliveryData length] > 0 || reason == WEB_REASON_PLUGIN_CANCELLED) {
+    if (isTerminated || ![plugin isLoaded]) {
         return;
     }
     
+    ASSERT(reason != WEB_REASON_NONE);
+    ASSERT([deliveryData length] == 0);
+    
     if (stream.ndata != NULL) {
         if (reason == NPRES_DONE && (transferMode == NP_ASFILE || transferMode == NP_ASFILEONLY)) {
             ASSERT(path != NULL);
@@ -220,29 +260,35 @@ static const char *CarbonPathFromPOSIXPath(const char *posixPath);
     isTerminated = YES;
 }
 
-- (void)destroyStreamWithReason:(NPReason)theReason
+- (void)_destroyStreamWithReason:(NPReason)theReason
 {
     reason = theReason;
-    [self destroyStream];
+    if (reason != NPRES_DONE) {
+        // Stop any pending data from being streamed.
+        [deliveryData setLength:0];
+    } else if ([deliveryData length] > 0) {
+        // There is more data to be streamed, don't destroy the stream now.
+        return;
+    }
+    [self _destroyStream];
+    ASSERT(stream.ndata == nil);
 }
 
-- (void)destroyStreamWithFailingReason:(NPReason)theReason
+- (void)cancelLoadWithError:(NSError *)error
 {
-    ASSERT(theReason != NPRES_DONE);
-    // Stop any pending data from being streamed.
-    [deliveryData setLength:0];
-    [self destroyStreamWithReason:theReason];
-    stream.ndata = nil;
+    // Overridden by subclasses.
+    ASSERT_NOT_REACHED();
 }
 
-- (void)receivedError:(NSError *)error
+- (void)destroyStreamWithError:(NSError *)error
 {
-    [self destroyStreamWithFailingReason:[[self class] reasonForError:error]];
+    [self _destroyStreamWithReason:[[self class] reasonForError:error]];
 }
 
-- (void)cancelWithReason:(NPReason)theReason
+- (void)cancelLoadAndDestroyStreamWithError:(NSError *)error
 {
-    [self destroyStreamWithFailingReason:theReason];
+    [self cancelLoadWithError:error];
+    [self destroyStreamWithError:error];
 }
 
 - (void)finishedLoadingWithData:(NSData *)data
@@ -258,7 +304,7 @@ static const char *CarbonPathFromPOSIXPath(const char *posixPath);
             // This should almost never happen.
             ERROR("can't make temporary file, almost certainly a problem with /tmp");
             // This is not a network error, but the only error codes are "network error" and "user break".
-            [self destroyStreamWithFailingReason:NPRES_NETWORK_ERR];
+            [self _destroyStreamWithReason:NPRES_NETWORK_ERR];
             free(path);
             path = NULL;
             return;
@@ -271,7 +317,7 @@ static const char *CarbonPathFromPOSIXPath(const char *posixPath);
                 ERROR("error writing to temporary file, errno %d", errno);
                 close(fd);
                 // This is not a network error, but the only error codes are "network error" and "user break".
-                [self destroyStreamWithFailingReason:NPRES_NETWORK_ERR];
+                [self _destroyStreamWithReason:NPRES_NETWORK_ERR];
                 free(path);
                 path = NULL;
                 return;
@@ -280,10 +326,10 @@ static const char *CarbonPathFromPOSIXPath(const char *posixPath);
         close(fd);
     }
 
-    [self destroyStreamWithReason:NPRES_DONE];
+    [self _destroyStreamWithReason:NPRES_DONE];
 }
 
-- (void)deliverData
+- (void)_deliverData
 {
     if (![plugin isLoaded] || !stream.ndata || [deliveryData length] == 0) {
         return;
@@ -298,12 +344,17 @@ static const char *CarbonPathFromPOSIXPath(const char *posixPath);
         
         if (deliveryBytes <= 0) {
             // Plug-in can't receive anymore data right now. Send it later.
-            [self performSelector:@selector(deliverData) withObject:nil afterDelay:0];
+            [self performSelector:@selector(_deliverData) withObject:nil afterDelay:0];
             break;
         } else {
             deliveryBytes = MIN(deliveryBytes, totalBytes - totalBytesDelivered);
             NSData *subdata = [deliveryData subdataWithRange:NSMakeRange(totalBytesDelivered, deliveryBytes)];
             deliveryBytes = NPP_Write(instance, &stream, offset, [subdata length], (void *)[subdata bytes]);
+            if (deliveryBytes < 0) {
+                // Netscape documentation says that a negative result from NPP_Write means cancel the load.
+                [self cancelLoadAndDestroyStreamWithError:[self _pluginCancelledConnectionError]];
+                return;
+            }
             deliveryBytes = MIN((unsigned)deliveryBytes, [subdata length]);
             offset += deliveryBytes;
             totalBytesDelivered += deliveryBytes;
@@ -319,7 +370,9 @@ static const char *CarbonPathFromPOSIXPath(const char *posixPath);
             deliveryData = newDeliveryData;
         } else {
             [deliveryData setLength:0];
-            [self destroyStream];
+            if (reason != WEB_REASON_NONE) {
+                [self _destroyStream];
+            }
         }
     }
 }
@@ -333,7 +386,7 @@ static const char *CarbonPathFromPOSIXPath(const char *posixPath);
             deliveryData = [[NSMutableData alloc] initWithCapacity:[data length]];
         }
         [deliveryData appendData:data];
-        [self deliverData];
+        [self _deliverData];
     }
 }
 
index 7b0516e79acb957fb2cea02ed9b40ccd7387fcd5..095260b5dfa91c764a78a70116e16c948a628625 100644 (file)
@@ -1647,7 +1647,8 @@ static OSStatus TSMEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEve
     if (!stream->ndata) {
         return NPERR_INVALID_INSTANCE_ERROR;
     }
-    [(WebBaseNetscapePluginStream *)stream->ndata cancelWithReason:reason];
+    WebBaseNetscapePluginStream *browserStream = (WebBaseNetscapePluginStream *)stream->ndata;
+    [browserStream cancelLoadAndDestroyStreamWithError:[browserStream errorForReason:reason]];
     return NPERR_NO_ERROR;
 }
 
index 55ac20ed272853a90c35f3e310f3689f433236c0..72881365db99850fd3f7b93ea2738e42741bf29d 100644 (file)
@@ -9,7 +9,6 @@
 #import <WebKit/WebDataSourcePrivate.h>
 #import <WebKit/WebFrame.h>
 #import <WebKit/WebFrameView.h>
-#import <WebKit/WebKitErrorsPrivate.h>
 #import <WebKit/WebNetscapePluginDocumentView.h>
 #import <WebKit/WebNetscapePluginPackage.h>
 
         return;
     }
     
-    [self receivedError:error];
+    [self destroyStreamWithError:error];
 }
 
-- (void)cancelWithReason:(NPReason)theReason
+- (void)cancelLoadWithError:(NSError *)error
 {
-    if (theReason == WEB_REASON_PLUGIN_CANCELLED) {
-        NSError *error = [[NSError alloc] _initWithPluginErrorCode:WebKitErrorPlugInCancelledConnection
-                                                        contentURL:[[_dataSource request] URL]
-                                                     pluginPageURL:nil
-                                                        pluginName:[plugin name]
-                                                          MIMEType:[[_dataSource response] MIMEType]];
-        [_dataSource _stopLoadingWithError:error];
-        [error release];
-    } else {
-        [[_dataSource webFrame] stopLoading];
-    }
-    [super cancelWithReason:theReason];
+    [_dataSource _stopLoadingWithError:error];
 }
 
 - (void)finishedLoadingWithDataSource:(WebDataSource *)ds
index 9cd0f2c48030201ba1b9a4d9fbafc3c125745e89..e23dd4f837d3cf9ccca0b1e12394333eac19fa06 100644 (file)
@@ -24,6 +24,7 @@
     WebBaseNetscapePluginView *view;
 }
 - initWithStream:(WebNetscapePluginStream *)theStream view:(WebBaseNetscapePluginView *)theView;
+- (BOOL)isDone;
 @end
 
 @implementation WebNetscapePluginStream
     }
 }
 
-- (void)cancelWithReason:(NPReason)theReason
+- (void)cancelLoadWithError:(NSError *)error
 {
-    if (theReason == WEB_REASON_PLUGIN_CANCELLED) {
-        NSURLResponse *response = [_loader response];
-        NSError *error = [[NSError alloc] _initWithPluginErrorCode:WebKitErrorPlugInCancelledConnection
-                                                        contentURL:[response URL]
-                                                     pluginPageURL:nil
-                                                        pluginName:[plugin name]
-                                                          MIMEType:[response MIMEType]];
+    if (![_loader isDone]) {
         [_loader cancelWithError:error];
-        [error release];
-    } else {
-        [_loader cancel];
     }
-    [super cancelWithReason:theReason];
 }
 
 - (void)stop
 {
-    [self cancelWithReason:NPRES_USER_BREAK];
+    [self cancelLoadAndDestroyStreamWithError:[_loader cancelledError]];
 }
 
 @end
     return self;
 }
 
+- (BOOL)isDone
+{
+    return stream == nil;
+}
+
 - (void)releaseResources
 {
     [stream release];
                 NSError *error = [NSError _webKitErrorWithDomain:NSURLErrorDomain
                                                             code:NSURLErrorFileDoesNotExist
                                                             URL:[theResponse URL]];
-                [stream receivedError:error];
-                [self cancelWithError:error];
+                [stream cancelLoadAndDestroyStreamWithError:error];
             }
         }
     }
 
     [[self dataSource] _removePlugInStreamClient:self];
     [[view webView] _receivedError:error fromDataSource:[self dataSource]];
-    [stream receivedError:error];
+    [stream destroyStreamWithError:error];
     [super didFailWithError:error];
 
     [self release];