Reviewed by darin.
* WebView.subproj/WebBaseResourceHandleDelegate.h:
* WebView.subproj/WebBaseResourceHandleDelegate.m:
(+[WebBaseResourceHandleDelegate initialize]): cache check to see if Foundation supports access to its buffered data
(-[WebBaseResourceHandleDelegate addData:]): don't buffer data if Foundation is buffering it for us
(-[WebBaseResourceHandleDelegate saveResource]): when creating a WebResource, pass NO for copyData since we know it won't be mutated
(-[WebBaseResourceHandleDelegate resourceData]): return the buffered data from the connection if it supports it
(-[WebBaseResourceHandleDelegate willStopBufferingData:]): make a mutable copy of the data from NSURLConnection so we can continue buffering
(-[WebBaseResourceHandleDelegate willCacheResponse:]): removed optimization that used the cached response data to save the resource since that is obsolete by this change
(-[WebBaseResourceHandleDelegate connection:willStopBufferingData:]): new callback from NSURLConnection, informs us that NSURLConnection has given up buffering
* WebView.subproj/WebDataSource.m:
(-[WebDataSource _receivedData:]): removed buffering code since that's done by NSURLConnection and the main client
(-[WebDataSource _setData:]): removed unnecessary cast since the resourceData ivar is now an NSData instead of NSMutableData
(-[WebDataSource data]): return resourceData ivar, else return the resourceData from the main client
* WebView.subproj/WebDataSourcePrivate.h:
* WebView.subproj/WebMainResourceClient.m:
(-[WebMainResourceClient releaseResources]): store resourceData on the data source so it can continue to have data after the main client has gone away
(-[WebMainResourceClient connection:didReceiveData:lengthReceived:]):don't call [dataSource data] just to get the length of data received since [dataSource data] can now cause data to be copied
(-[WebMainResourceClient connectionDidFinishLoading:]): ditto
* WebView.subproj/WebResource.m:
(-[WebResource initWithData:URL:MIMEType:textEncodingName:frameName:]): call following method with YES for copyData
(-[WebResource _initWithData:URL:MIMEType:textEncodingName:frameName:copyData:]): new initializer, allows caller to choose whether or not the data is copied
* WebView.subproj/WebResourcePrivate.h:
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@8131
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2004-12-06 Chris Blumenberg <cblu@apple.com>
+
+ Fixed: <rdar://problem/3907381> NSURLConnection and WebKit buffer 2 copies of incoming data
+
+ Reviewed by darin.
+
+ * WebView.subproj/WebBaseResourceHandleDelegate.h:
+ * WebView.subproj/WebBaseResourceHandleDelegate.m:
+ (+[WebBaseResourceHandleDelegate initialize]): cache check to see if Foundation supports access to its buffered data
+ (-[WebBaseResourceHandleDelegate addData:]): don't buffer data if Foundation is buffering it for us
+ (-[WebBaseResourceHandleDelegate saveResource]): when creating a WebResource, pass NO for copyData since we know it won't be mutated
+ (-[WebBaseResourceHandleDelegate resourceData]): return the buffered data from the connection if it supports it
+ (-[WebBaseResourceHandleDelegate willStopBufferingData:]): make a mutable copy of the data from NSURLConnection so we can continue buffering
+ (-[WebBaseResourceHandleDelegate willCacheResponse:]): removed optimization that used the cached response data to save the resource since that is obsolete by this change
+ (-[WebBaseResourceHandleDelegate connection:willStopBufferingData:]): new callback from NSURLConnection, informs us that NSURLConnection has given up buffering
+ * WebView.subproj/WebDataSource.m:
+ (-[WebDataSource _receivedData:]): removed buffering code since that's done by NSURLConnection and the main client
+ (-[WebDataSource _setData:]): removed unnecessary cast since the resourceData ivar is now an NSData instead of NSMutableData
+ (-[WebDataSource data]): return resourceData ivar, else return the resourceData from the main client
+ * WebView.subproj/WebDataSourcePrivate.h:
+ * WebView.subproj/WebMainResourceClient.m:
+ (-[WebMainResourceClient releaseResources]): store resourceData on the data source so it can continue to have data after the main client has gone away
+ (-[WebMainResourceClient connection:didReceiveData:lengthReceived:]):don't call [dataSource data] just to get the length of data received since [dataSource data] can now cause data to be copied
+ (-[WebMainResourceClient connectionDidFinishLoading:]): ditto
+ * WebView.subproj/WebResource.m:
+ (-[WebResource initWithData:URL:MIMEType:textEncodingName:frameName:]): call following method with YES for copyData
+ (-[WebResource _initWithData:URL:MIMEType:textEncodingName:frameName:copyData:]): new initializer, allows caller to choose whether or not the data is copied
+ * WebView.subproj/WebResourcePrivate.h:
+
2004-12-06 Richard Williamson <rjw@apple.com>
Fixed <rdar://problem/3903749> REGRESSION (8A321): WebKit gets incorrect glyph metrics due to change in how AppKit uses CGFont
- (void)didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
- (void)didReceiveResponse:(NSURLResponse *)r;
- (void)didReceiveData:(NSData *)data lengthReceived:(long long)lengthReceived;
+- (void)willStopBufferingData:(NSData *)data;
- (void)didFinishLoading;
- (void)didFailWithError:(NSError *)error;
+- (NSCachedURLResponse *)willCacheResponse:(NSCachedURLResponse *)cachedResponse;
@end
#import <WebKit/WebResourcePrivate.h>
#import <WebKit/WebViewPrivate.h>
+static BOOL NSURLConnectionSupportsBufferedData;
+
+@interface NSURLConnection (NSURLConnectionTigerPrivate)
+- (NSData *)_bufferedData;
+@end
+
@interface WebBaseResourceHandleDelegate (WebNSURLAuthenticationChallengeSender) <NSURLAuthenticationChallengeSender>
@end
@implementation WebBaseResourceHandleDelegate
++ (void)initialize
+{
+ NSURLConnectionSupportsBufferedData = [NSURLConnection instancesRespondToSelector:@selector(_bufferedData)];
+}
+
- (void)releaseResources
{
ASSERT(!reachedTerminalState);
- (void)addData:(NSData *)data
{
- if (!resource) {
- if (!resourceData) {
- resourceData = [[NSMutableData alloc] init];
+ // Don't buffer data if we're loading it from a WebResource.
+ if (resource == nil) {
+ if (NSURLConnectionSupportsBufferedData) {
+ // Buffer data only if the connection has handed us the data because is has stopped buffering it.
+ if (resourceData != nil) {
+ [resourceData appendData:data];
+ }
+ } else {
+ if (resourceData == nil) {
+ resourceData = [[NSMutableData alloc] init];
+ }
+ [resourceData appendData:data];
}
- [resourceData appendData:data];
}
}
- (void)saveResource
{
- if (!resource && [resourceData length] > 0) {
- WebResource *newResource = [[WebResource alloc] initWithData:resourceData
- URL:originalURL
- MIMEType:[response MIMEType]
- textEncodingName:[response textEncodingName]
- frameName:nil];
- [dataSource addSubresource:newResource];
- [newResource release];
- }
-}
-
-- (void)saveResourceWithCachedResponse:(NSCachedURLResponse *)cachedResponse
-{
- if (!resource) {
- // Overwrite the resource saved with saveResource with the cache version to save memory.
- WebResource *newResource = [[WebResource alloc] _initWithCachedResponse:cachedResponse originalURL:originalURL];
- [dataSource addSubresource:newResource];
- [newResource release];
+ // Don't save data as a WebResource if it was loaded from a WebResource.
+ if (resource == nil) {
+ NSData *data = [self resourceData];
+ if ([data length] > 0) {
+ // Don't have WebResource copy the data since the data is a NSMutableData that we know won't get modified.
+ WebResource *newResource = [[WebResource alloc] _initWithData:data
+ URL:originalURL
+ MIMEType:[response MIMEType]
+ textEncodingName:[response textEncodingName]
+ frameName:nil
+ copyData:NO];
+ [dataSource addSubresource:newResource];
+ [newResource release];
+ }
}
}
- (NSData *)resourceData
{
- return resource ? [resource data] : resourceData;
+ if (resource != nil) {
+ return [resource data];
+ }
+ if (resourceData != nil) {
+ return resourceData;
+ }
+ if (NSURLConnectionSupportsBufferedData) {
+ return [connection _bufferedData];
+ }
+ return nil;
}
- (NSURLRequest *)willSendRequest:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse
[self release];
}
+- (void)willStopBufferingData:(NSData *)data
+{
+ ASSERT(resourceData == nil);
+ resourceData = [data mutableCopy];
+}
+
- (void)didFinishLoading
{
// If load has been cancelled after finishing (which could happen with a
userInfo:[cachedResponse userInfo]
storagePolicy:NSURLCacheStorageAllowedInMemoryOnly] autorelease];
}
- [self saveResourceWithCachedResponse:cachedResponse];
return cachedResponse;
}
[self didReceiveData:data lengthReceived:lengthReceived];
}
+- (void)connection:(NSURLConnection *)con willStopBufferingData:(NSData *)data
+{
+ ASSERT(con == connection);
+ [self willStopBufferingData:data];
+}
+
- (void)connectionDidFinishLoading:(NSURLConnection *)con
{
// don't worry about checking connection consistency if this load
}
-(void)_receivedData:(NSData *)data
-{
- if (!_private->resourceData) {
- _private->resourceData = [[NSMutableData alloc] init];
- }
- ASSERT([_private->resourceData isKindOfClass:[NSMutableData class]]);
- [_private->resourceData appendData:data];
-
+{
_private->gotFirstByte = YES;
[self _commitIfReady];
- (void)_setData:(NSData *)data
{
+ ASSERT(_private->resourceData == nil);
[data retain];
[_private->resourceData release];
- _private->resourceData = (NSMutableData *)data;
+ _private->resourceData = data;
}
- (void)_finishedLoading
- (NSData *)data
{
- return _private->resourceData;
+ return _private->resourceData != nil ? _private->resourceData : [_private->mainClient resourceData];
}
- (id <WebDocumentRepresentation>) representation
@interface WebDataSourcePrivate : NSObject
{
@public
- NSMutableData *resourceData;
+ NSData *resourceData;
id <WebDocumentRepresentation> representation;
- (void)didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
- (void)didReceiveResponse:(NSURLResponse *)r;
- (void)didReceiveData:(NSData *)data lengthReceived:(long long)lengthReceived;
+- (void)willStopBufferingData:(NSData *)data;
- (void)didFinishLoading;
- (void)didFailWithError:(NSError *)error;
+- (NSCachedURLResponse *)willCacheResponse:(NSCachedURLResponse *)cachedResponse;
@end
#import <WebKit/WebResourcePrivate.h>
#import <WebKit/WebViewPrivate.h>
+static BOOL NSURLConnectionSupportsBufferedData;
+
+@interface NSURLConnection (NSURLConnectionTigerPrivate)
+- (NSData *)_bufferedData;
+@end
+
@interface WebBaseResourceHandleDelegate (WebNSURLAuthenticationChallengeSender) <NSURLAuthenticationChallengeSender>
@end
@implementation WebBaseResourceHandleDelegate
++ (void)initialize
+{
+ NSURLConnectionSupportsBufferedData = [NSURLConnection instancesRespondToSelector:@selector(_bufferedData)];
+}
+
- (void)releaseResources
{
ASSERT(!reachedTerminalState);
- (void)addData:(NSData *)data
{
- if (!resource) {
- if (!resourceData) {
- resourceData = [[NSMutableData alloc] init];
+ // Don't buffer data if we're loading it from a WebResource.
+ if (resource == nil) {
+ if (NSURLConnectionSupportsBufferedData) {
+ // Buffer data only if the connection has handed us the data because is has stopped buffering it.
+ if (resourceData != nil) {
+ [resourceData appendData:data];
+ }
+ } else {
+ if (resourceData == nil) {
+ resourceData = [[NSMutableData alloc] init];
+ }
+ [resourceData appendData:data];
}
- [resourceData appendData:data];
}
}
- (void)saveResource
{
- if (!resource && [resourceData length] > 0) {
- WebResource *newResource = [[WebResource alloc] initWithData:resourceData
- URL:originalURL
- MIMEType:[response MIMEType]
- textEncodingName:[response textEncodingName]
- frameName:nil];
- [dataSource addSubresource:newResource];
- [newResource release];
- }
-}
-
-- (void)saveResourceWithCachedResponse:(NSCachedURLResponse *)cachedResponse
-{
- if (!resource) {
- // Overwrite the resource saved with saveResource with the cache version to save memory.
- WebResource *newResource = [[WebResource alloc] _initWithCachedResponse:cachedResponse originalURL:originalURL];
- [dataSource addSubresource:newResource];
- [newResource release];
+ // Don't save data as a WebResource if it was loaded from a WebResource.
+ if (resource == nil) {
+ NSData *data = [self resourceData];
+ if ([data length] > 0) {
+ // Don't have WebResource copy the data since the data is a NSMutableData that we know won't get modified.
+ WebResource *newResource = [[WebResource alloc] _initWithData:data
+ URL:originalURL
+ MIMEType:[response MIMEType]
+ textEncodingName:[response textEncodingName]
+ frameName:nil
+ copyData:NO];
+ [dataSource addSubresource:newResource];
+ [newResource release];
+ }
}
}
- (NSData *)resourceData
{
- return resource ? [resource data] : resourceData;
+ if (resource != nil) {
+ return [resource data];
+ }
+ if (resourceData != nil) {
+ return resourceData;
+ }
+ if (NSURLConnectionSupportsBufferedData) {
+ return [connection _bufferedData];
+ }
+ return nil;
}
- (NSURLRequest *)willSendRequest:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse
[self release];
}
+- (void)willStopBufferingData:(NSData *)data
+{
+ ASSERT(resourceData == nil);
+ resourceData = [data mutableCopy];
+}
+
- (void)didFinishLoading
{
// If load has been cancelled after finishing (which could happen with a
userInfo:[cachedResponse userInfo]
storagePolicy:NSURLCacheStorageAllowedInMemoryOnly] autorelease];
}
- [self saveResourceWithCachedResponse:cachedResponse];
return cachedResponse;
}
[self didReceiveData:data lengthReceived:lengthReceived];
}
+- (void)connection:(NSURLConnection *)con willStopBufferingData:(NSData *)data
+{
+ ASSERT(con == connection);
+ [self willStopBufferingData:data];
+}
+
- (void)connectionDidFinishLoading:(NSURLConnection *)con
{
// don't worry about checking connection consistency if this load
[super finalize];
}
+- (void)releaseResources
+{
+ [dataSource _setData:[self resourceData]];
+ [super releaseResources];
+}
+
- (void)receivedError:(NSError *)error
{
// Calling _receivedMainResourceError will likely result in a call to release, so we must retain.
// Override. We don't want to save the main resource as a subresource of the data source.
}
-- (void)saveResourceWithCachedResponse:(NSCachedURLResponse *)cachedResponse
-{
- // Override. We don't want to save the main resource as a subresource of the data source.
- // Replace the data on the data source with the cache copy to save memory.
- [dataSource _setData:[cachedResponse data]];
-}
-
- (NSURLRequest *)connection:(NSURLConnection *)con willSendRequest:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse
{
// Note that there are no asserts here as there are for the other callbacks. This is due to the
// retain/release self in this delegate method since the additional processing can do
// anything including possibly releasing self; one example of this is 3266216
[self retain];
- [[dataSource _webView] _mainReceivedBytesSoFar:[[dataSource data] length]
+ [[dataSource _webView] _mainReceivedBytesSoFar:_bytesReceived
fromDataSource:dataSource
complete:NO];
-
+
[super connection:con didReceiveData:data lengthReceived:lengthReceived];
_bytesReceived += [data length];
[self retain];
[dataSource _finishedLoading];
- [[dataSource _webView] _mainReceivedBytesSoFar:[[dataSource data] length]
+ [[dataSource _webView] _mainReceivedBytesSoFar:_bytesReceived
fromDataSource:dataSource
complete:YES];
[super connectionDidFinishLoading:con];
[super finalize];
}
+- (void)releaseResources
+{
+ [dataSource _setData:[self resourceData]];
+ [super releaseResources];
+}
+
- (void)receivedError:(NSError *)error
{
// Calling _receivedMainResourceError will likely result in a call to release, so we must retain.
// Override. We don't want to save the main resource as a subresource of the data source.
}
-- (void)saveResourceWithCachedResponse:(NSCachedURLResponse *)cachedResponse
-{
- // Override. We don't want to save the main resource as a subresource of the data source.
- // Replace the data on the data source with the cache copy to save memory.
- [dataSource _setData:[cachedResponse data]];
-}
-
- (NSURLRequest *)connection:(NSURLConnection *)con willSendRequest:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse
{
// Note that there are no asserts here as there are for the other callbacks. This is due to the
// retain/release self in this delegate method since the additional processing can do
// anything including possibly releasing self; one example of this is 3266216
[self retain];
- [[dataSource _webView] _mainReceivedBytesSoFar:[[dataSource data] length]
+ [[dataSource _webView] _mainReceivedBytesSoFar:_bytesReceived
fromDataSource:dataSource
complete:NO];
-
+
[super connection:con didReceiveData:data lengthReceived:lengthReceived];
_bytesReceived += [data length];
[self retain];
[dataSource _finishedLoading];
- [[dataSource _webView] _mainReceivedBytesSoFar:[[dataSource data] length]
+ [[dataSource _webView] _mainReceivedBytesSoFar:_bytesReceived
fromDataSource:dataSource
complete:YES];
[super connectionDidFinishLoading:con];
- (id)initWithData:(NSData *)data URL:(NSURL *)URL MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)textEncodingName frameName:(NSString *)frameName
{
- [self init];
-
- if (!data) {
- [self release];
- return nil;
- }
- _private->data = [data copy];
-
- if (!URL) {
- [self release];
- return nil;
- }
- _private->URL = [URL copy];
-
- if (!MIMEType) {
- [self release];
- return nil;
- }
- _private->MIMEType = [MIMEType copy];
-
- _private->textEncodingName = [textEncodingName copy];
- _private->frameName = [frameName copy];
-
- return self;
+ return [self _initWithData:data URL:URL MIMEType:MIMEType textEncodingName:textEncodingName frameName:frameName copyData:YES];
}
- (id)initWithCoder:(NSCoder *)decoder
return propertyLists;
}
+- (id)_initWithData:(NSData *)data URL:(NSURL *)URL MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)textEncodingName frameName:(NSString *)frameName copyData:(BOOL)copyData
+{
+ [self init];
+
+ if (!data) {
+ [self release];
+ return nil;
+ }
+ _private->data = copyData ? [data copy] : [data retain];
+
+ if (!URL) {
+ [self release];
+ return nil;
+ }
+ _private->URL = [URL copy];
+
+ if (!MIMEType) {
+ [self release];
+ return nil;
+ }
+ _private->MIMEType = [MIMEType copy];
+
+ _private->textEncodingName = [textEncodingName copy];
+ _private->frameName = [frameName copy];
+
+ return self;
+}
+
- (id)_initWithPropertyList:(id)propertyList
{
if (![propertyList isKindOfClass:[NSDictionary class]]) {
frameName:[propertyList _web_stringForKey:WebResourceFrameNameKey]];
}
-- (id)_initWithCachedResponse:(NSCachedURLResponse *)cachedResponse originalURL:(NSURL *)originalURL
-{
- NSURLResponse *response = [cachedResponse response];
- return [self initWithData:[cachedResponse data]
- URL:originalURL
- MIMEType:[response MIMEType]
- textEncodingName:[response textEncodingName]
- frameName:nil];
-}
-
- (NSFileWrapper *)_fileWrapperRepresentation
{
NSFileWrapper *wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:_private->data] autorelease];
@interface WebResource (WebResourcePrivate)
+- (id)_initWithData:(NSData *)data URL:(NSURL *)URL MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)textEncodingName frameName:(NSString *)frameName copyData:(BOOL)copyData;
+
+ (NSArray *)_resourcesFromPropertyLists:(NSArray *)propertyLists;
+ (NSArray *)_propertyListsFromResources:(NSArray *)resources;
- (id)_initWithPropertyList:(id)propertyList;
-- (id)_initWithCachedResponse:(NSCachedURLResponse *)response originalURL:(NSURL *)originalURL;
- (NSFileWrapper *)_fileWrapperRepresentation;
- (id)_propertyListRepresentation;