From: ddkilzer Date: Fri, 4 Aug 2006 11:23:37 +0000 (+0000) Subject: WebKit: X-Git-Url: https://git.webkit.org/?p=WebKit-https.git;a=commitdiff_plain;h=c25574801957dff61b61daf162e97ef120f962ee WebKit: Reviewed by NOBODY (build fix). * WebCoreSupport/WebSubresourceLoader.m: REALLY moved to Loader/ * WebView/WebFrameLoader.h: REALLY moved to Loader/ * WebView/WebFrameLoader.m: REALLY moved to Loader/ * WebView/WebLoader.h: REALLY moved to Loader/ * WebView/WebLoader.m: REALLY moved to Loader/ * WebView/WebMainResourceLoader.m: REALLY moved to Loader/ git-svn-id: https://svn.webkit.org/repository/webkit/trunk@15796 268f45cc-cd09-0410-ab3c-d52691b4dbfc --- diff --git a/WebKit/ChangeLog b/WebKit/ChangeLog index c2a1b8bd9994..27db8f2b523a 100644 --- a/WebKit/ChangeLog +++ b/WebKit/ChangeLog @@ -1,3 +1,14 @@ +2006-08-04 David Kilzer + + Reviewed by NOBODY (build fix). + + * WebCoreSupport/WebSubresourceLoader.m: REALLY moved to Loader/ + * WebView/WebFrameLoader.h: REALLY moved to Loader/ + * WebView/WebFrameLoader.m: REALLY moved to Loader/ + * WebView/WebLoader.h: REALLY moved to Loader/ + * WebView/WebLoader.m: REALLY moved to Loader/ + * WebView/WebMainResourceLoader.m: REALLY moved to Loader/ + 2006-08-03 Maciej Stachowiak Reviewed by Darin. diff --git a/WebKit/Loader/WebFrameLoader.h b/WebKit/Loader/WebFrameLoader.h new file mode 100644 index 000000000000..8ed40a13480f --- /dev/null +++ b/WebKit/Loader/WebFrameLoader.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import +#import +#import + +@class WebDataSource; +@class WebMainResourceLoader; +@class WebIconLoader; +@class WebLoader; +@class WebResource; + +@interface WebFrameLoader : NSObject +{ +@public + // Client for main resource. + WebMainResourceLoader *mainResourceLoader; + + // Clients for other resources. + NSMutableArray *subresourceLoaders; + NSMutableArray *plugInStreamLoaders; + WebIconLoader *iconLoader; + + WebFrame *webFrame; + WebDataSource *dataSource; + WebDataSource *provisionalDataSource; + WebFrameState state; +} + +- (id)initWithWebFrame:(WebFrame *)wf; +// FIXME: should really split isLoadingIcon from hasLoadedIcon, no? +- (BOOL)hasIconLoader; +- (void)loadIconWithRequest:(NSURLRequest *)request; +- (void)stopLoadingIcon; +- (void)addPlugInStreamLoader:(WebLoader *)loader; +- (void)removePlugInStreamLoader:(WebLoader *)loader; +- (void)setDefersCallbacks:(BOOL)defers; +- (void)stopLoadingPlugIns; +- (BOOL)isLoadingMainResource; +- (BOOL)isLoadingSubresources; +- (BOOL)isLoading; +- (void)stopLoadingSubresources; +- (void)addSubresourceLoader:(WebLoader *)loader; +- (void)removeSubresourceLoader:(WebLoader *)loader; +- (NSData *)mainResourceData; +- (void)releaseMainResourceLoader; +- (void)cancelMainResourceLoad; +- (BOOL)startLoadingMainResourceWithRequest:(NSMutableURLRequest *)request identifier:(id)identifier; +- (void)stopLoadingWithError:(NSError *)error; +- (void)clearProvisionalLoad; +- (void)stopLoading; +- (void)markLoadComplete; +- (void)commitProvisionalLoad; +- (void)startLoading; +- (void)startProvisionalLoad:(WebDataSource *)dataSource; +- (WebDataSource *)dataSource; +- (WebDataSource *)provisionalDataSource; +- (WebDataSource *)activeDataSource; +- (WebFrameState)state; +- (void)clearDataSource; +- (void)setupForReplace; ++ (CFAbsoluteTime)timeOfLastCompletedLoad; + +- (WebResource *)_archivedSubresourceForURL:(NSURL *)URL; +- (BOOL)_defersCallbacks; +- (id)_identifierForInitialRequest:(NSURLRequest *)clientRequest; +- (NSURLRequest *)_willSendRequest:(NSMutableURLRequest *)clientRequest forResource:(id)identifier redirectResponse:(NSURLResponse *)redirectResponse; +- (void)_didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)currentWebChallenge forResource:(id)identifier; +- (void)_didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)currentWebChallenge forResource:(id)identifier; +- (void)_didReceiveResponse:(NSURLResponse *)r forResource:(id)identifier; +- (void)_didReceiveData:(NSData *)data contentLength:(int)lengthReceived forResource:(id)identifier; +- (void)_didFinishLoadingForResource:(id)identifier; +- (void)_didFailLoadingWithError:(NSError *)error forResource:(id)identifier; +- (BOOL)_privateBrowsingEnabled; +- (void)_didFailLoadingWithError:(NSError *)error forResource:(id)identifier; +- (void)_addPlugInStreamLoader:(WebLoader *)loader; +- (void)_removePlugInStreamLoader:(WebLoader *)loader; +- (void)_finishedLoadingResource; +- (void)_receivedError:(NSError *)error; +- (void)_addSubresourceLoader:(WebLoader *)loader; +- (void)_removeSubresourceLoader:(WebLoader *)loader; +- (NSURLRequest *)_originalRequest; +- (WebFrame *)webFrame; +- (void)_receivedMainResourceError:(NSError *)error complete:(BOOL)isComplete; +- (NSURLRequest *)initialRequest; +- (void)_receivedData:(NSData *)data; +- (void)_setRequest:(NSURLRequest *)request; +- (void)_downloadWithLoadingConnection:(NSURLConnection *)connection request:(NSURLRequest *)request response:(NSURLResponse *)r proxy:(WKNSURLConnectionDelegateProxyPtr)proxy; +- (void)_handleFallbackContent; +- (BOOL)_isStopping; +- (void)_decidePolicyForMIMEType:(NSString *)MIMEType decisionListener:(WebPolicyDecisionListener *)listener; +- (void)_setupForReplaceByMIMEType:(NSString *)newMIMEType; +- (void)_setResponse:(NSURLResponse *)response; +- (void)_mainReceivedError:(NSError *)error complete:(BOOL)isComplete; +- (void)_finishedLoading; +- (void)_mainReceivedBytesSoFar:(unsigned)bytesSoFar complete:(BOOL)isComplete; +- (void)_iconLoaderReceivedPageIcon:(WebIconLoader *)iconLoader; +- (NSURL *)_URL; + +@end diff --git a/WebKit/Loader/WebFrameLoader.m b/WebKit/Loader/WebFrameLoader.m new file mode 100644 index 000000000000..66ac749d5ce7 --- /dev/null +++ b/WebKit/Loader/WebFrameLoader.m @@ -0,0 +1,525 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +#import +#import +#import +#import +#import +#import +#import + +@implementation WebFrameLoader + +- (id)initWithWebFrame:(WebFrame *)wf +{ + self = [super init]; + if (self) { + webFrame = wf; + state = WebFrameStateCommittedPage; + } + return self; +} + +- (void)dealloc +{ + // FIXME: should these even exist? + [mainResourceLoader release]; + [subresourceLoaders release]; + [plugInStreamLoaders release]; + [iconLoader release]; + [dataSource release]; + [provisionalDataSource release]; + + [super dealloc]; +} + +- (BOOL)hasIconLoader +{ + return iconLoader != nil; +} + +- (void)loadIconWithRequest:(NSURLRequest *)request +{ + ASSERT(!iconLoader); + iconLoader = [[WebIconLoader alloc] initWithRequest:request]; + [iconLoader setFrameLoader:self]; + [iconLoader loadWithRequest:request]; +} + +- (void)stopLoadingIcon +{ + [iconLoader stopLoading]; +} + +- (void)addPlugInStreamLoader:(WebLoader *)loader +{ + if (!plugInStreamLoaders) + plugInStreamLoaders = [[NSMutableArray alloc] init]; + [plugInStreamLoaders addObject:loader]; +} + +- (void)removePlugInStreamLoader:(WebLoader *)loader +{ + [plugInStreamLoaders removeObject:loader]; +} + +- (void)setDefersCallbacks:(BOOL)defers +{ + [mainResourceLoader setDefersCallbacks:defers]; + + NSEnumerator *e = [subresourceLoaders objectEnumerator]; + WebLoader *loader; + while ((loader = [e nextObject])) + [loader setDefersCallbacks:defers]; + + e = [plugInStreamLoaders objectEnumerator]; + while ((loader = [e nextObject])) + [loader setDefersCallbacks:defers]; +} + +- (void)stopLoadingPlugIns +{ + [plugInStreamLoaders makeObjectsPerformSelector:@selector(cancel)]; + [plugInStreamLoaders removeAllObjects]; +} + +- (BOOL)isLoadingMainResource +{ + return mainResourceLoader != nil; +} + +- (BOOL)isLoadingSubresources +{ + return [subresourceLoaders count]; +} + +- (BOOL)isLoadingPlugIns +{ + return [plugInStreamLoaders count]; +} + +- (BOOL)isLoading +{ + return [self isLoadingMainResource] || [self isLoadingSubresources] || [self isLoadingPlugIns]; +} + +- (void)stopLoadingSubresources +{ + NSArray *loaders = [subresourceLoaders copy]; + [loaders makeObjectsPerformSelector:@selector(cancel)]; + [loaders release]; + [subresourceLoaders removeAllObjects]; +} + +- (void)addSubresourceLoader:(WebLoader *)loader +{ + if (subresourceLoaders == nil) + subresourceLoaders = [[NSMutableArray alloc] init]; + [subresourceLoaders addObject:loader]; +} + +- (void)removeSubresourceLoader:(WebLoader *)loader +{ + [subresourceLoaders removeObject:loader]; +} + +- (NSData *)mainResourceData +{ + return [mainResourceLoader resourceData]; +} + +- (void)releaseMainResourceLoader +{ + [mainResourceLoader release]; + mainResourceLoader = nil; +} + +- (void)cancelMainResourceLoad +{ + [mainResourceLoader cancel]; +} + +- (BOOL)startLoadingMainResourceWithRequest:(NSMutableURLRequest *)request identifier:(id)identifier +{ + mainResourceLoader = [[WebMainResourceLoader alloc] initWithFrameLoader:self]; + + [mainResourceLoader setIdentifier:identifier]; + [[provisionalDataSource webFrame] _addExtraFieldsToRequest:request mainResource:YES alwaysFromRequest:NO]; + if (![mainResourceLoader loadWithRequest:request]) { + // FIXME: if this should really be caught, we should just ASSERT this doesn't happen; + // should it be caught by other parts of WebKit or other parts of the app? + LOG_ERROR("could not create WebResourceHandle for URL %@ -- should be caught by policy handler level", [request URL]); + [mainResourceLoader release]; + mainResourceLoader = nil; + return NO; + } + + return YES; +} + +- (void)stopLoadingWithError:(NSError *)error +{ + [mainResourceLoader cancelWithError:error]; +} + +- (WebDataSource *)dataSource +{ + return dataSource; +} + +- (void)_setDataSource:(WebDataSource *)ds +{ + if (ds == nil && dataSource == nil) + return; + + ASSERT(ds != dataSource); + + [webFrame _prepareForDataSourceReplacement]; + [dataSource _setWebFrame:nil]; + + [ds retain]; + [dataSource release]; + dataSource = ds; + + [ds _setWebFrame:webFrame]; +} + +- (void)clearDataSource +{ + [self _setDataSource:nil]; +} + +- (WebDataSource *)provisionalDataSource +{ + return provisionalDataSource; +} + +- (void)_setProvisionalDataSource: (WebDataSource *)d +{ + ASSERT(!d || !provisionalDataSource); + + if (provisionalDataSource != dataSource) + [provisionalDataSource _setWebFrame:nil]; + + [d retain]; + [provisionalDataSource release]; + provisionalDataSource = d; + + [d _setWebFrame:webFrame]; +} + +- (void)_clearProvisionalDataSource +{ + [self _setProvisionalDataSource:nil]; +} + +- (WebFrameState)state +{ + return state; +} + +#ifndef NDEBUG +static const char * const stateNames[] = { + "WebFrameStateProvisional", + "WebFrameStateCommittedPage", + "WebFrameStateComplete" +}; +#endif + +static CFAbsoluteTime _timeOfLastCompletedLoad; + ++ (CFAbsoluteTime)timeOfLastCompletedLoad +{ + return _timeOfLastCompletedLoad; +} + +- (void)_setState:(WebFrameState)newState +{ + LOG(Loading, "%@: transition from %s to %s", [webFrame name], stateNames[state], stateNames[newState]); + if ([webFrame webView]) + LOG(Timing, "%@: transition from %s to %s, %f seconds since start of document load", [webFrame name], stateNames[state], stateNames[newState], CFAbsoluteTimeGetCurrent() - [[[[webFrame webView] mainFrame] dataSource] _loadingStartedTime]); + + if (newState == WebFrameStateComplete && webFrame == [[webFrame webView] mainFrame]) + LOG(DocumentLoad, "completed %@ (%f seconds)", [[[self dataSource] request] URL], CFAbsoluteTimeGetCurrent() - [[self dataSource] _loadingStartedTime]); + + state = newState; + + if (state == WebFrameStateProvisional) + [webFrame _provisionalLoadStarted]; + else if (state == WebFrameStateComplete) { + [webFrame _frameLoadCompleted]; + _timeOfLastCompletedLoad = CFAbsoluteTimeGetCurrent(); + [dataSource _stopRecordingResponses]; + } +} + +- (void)clearProvisionalLoad +{ + [self _setProvisionalDataSource:nil]; + [[webFrame webView] _progressCompleted:webFrame]; + [self _setState:WebFrameStateComplete]; +} + +- (void)markLoadComplete +{ + [self _setState:WebFrameStateComplete]; +} + +- (void)clearIconLoader +{ + [iconLoader release]; + iconLoader = nil; +} + +- (void)commitProvisionalLoad +{ + [self stopLoadingSubresources]; + [self stopLoadingPlugIns]; + [self clearIconLoader]; + + [self _setDataSource:provisionalDataSource]; + [self _setProvisionalDataSource:nil]; + [self _setState:WebFrameStateCommittedPage]; +} + +- (void)stopLoading +{ + [provisionalDataSource _stopLoading]; + [dataSource _stopLoading]; + [self _clearProvisionalDataSource]; +} + +// FIXME: poor method name; also why is this not part of startProvisionalLoad:? +- (void)startLoading +{ + [provisionalDataSource _startLoading]; +} + +- (void)startProvisionalLoad:(WebDataSource *)ds +{ + [self _setProvisionalDataSource:ds]; + [self _setState:WebFrameStateProvisional]; +} + +- (void)setupForReplace +{ + [self _setState:WebFrameStateProvisional]; + WebDataSource *old = provisionalDataSource; + provisionalDataSource = dataSource; + dataSource = nil; + [old release]; + + [webFrame _detachChildren]; +} + +- (WebDataSource *)activeDataSource +{ + if (state == WebFrameStateProvisional) + return provisionalDataSource; + + return dataSource; +} + +- (WebResource *)_archivedSubresourceForURL:(NSURL *)URL +{ + return [[self activeDataSource] _archivedSubresourceForURL:URL]; +} + +- (BOOL)_defersCallbacks +{ + return [[self activeDataSource] _defersCallbacks]; +} + +- (id)_identifierForInitialRequest:(NSURLRequest *)clientRequest +{ + return [[self activeDataSource] _identifierForInitialRequest:clientRequest]; +} + +- (NSURLRequest *)_willSendRequest:(NSMutableURLRequest *)clientRequest forResource:(id)identifier redirectResponse:(NSURLResponse *)redirectResponse +{ + return [[self activeDataSource] _willSendRequest:clientRequest forResource:identifier redirectResponse:redirectResponse]; +} + +- (void)_didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)currentWebChallenge forResource:(id)identifier +{ + return [[self activeDataSource] _didReceiveAuthenticationChallenge:currentWebChallenge forResource:identifier]; +} + +- (void)_didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)currentWebChallenge forResource:(id)identifier +{ + return [[self activeDataSource] _didCancelAuthenticationChallenge:currentWebChallenge forResource:identifier]; +} + +- (void)_didReceiveResponse:(NSURLResponse *)r forResource:(id)identifier +{ + return [[self activeDataSource] _didReceiveResponse:r forResource:identifier]; +} + +- (void)_didReceiveData:(NSData *)data contentLength:(int)lengthReceived forResource:(id)identifier +{ + return [[self activeDataSource] _didReceiveData:data contentLength:lengthReceived forResource:identifier]; +} + +- (void)_didFinishLoadingForResource:(id)identifier +{ + return [[self activeDataSource] _didFinishLoadingForResource:identifier]; +} + +- (void)_didFailLoadingWithError:(NSError *)error forResource:(id)identifier +{ + return [[self activeDataSource] _didFailLoadingWithError:error forResource:identifier]; +} + +- (BOOL)_privateBrowsingEnabled +{ + return [[self activeDataSource] _privateBrowsingEnabled]; +} + +- (void)_addPlugInStreamLoader:(WebLoader *)loader +{ + return [[self activeDataSource] _addPlugInStreamLoader:loader]; +} + +- (void)_removePlugInStreamLoader:(WebLoader *)loader +{ + return [[self activeDataSource] _removePlugInStreamLoader:loader]; +} + +- (void)_finishedLoadingResource +{ + return [[self activeDataSource] _finishedLoadingResource]; +} + +- (void)_receivedError:(NSError *)error +{ + return [[self activeDataSource] _receivedError:error]; +} + +- (void)_addSubresourceLoader:(WebLoader *)loader +{ + return [[self activeDataSource] _addSubresourceLoader:loader]; +} + +- (void)_removeSubresourceLoader:(WebLoader *)loader +{ + return [[self activeDataSource] _removeSubresourceLoader:loader]; +} + +- (NSURLRequest *)_originalRequest +{ + return [[self activeDataSource] _originalRequest]; +} + +- (WebFrame *)webFrame +{ + return [[self activeDataSource] webFrame]; +} + +- (void)_receivedMainResourceError:(NSError *)error complete:(BOOL)isComplete +{ + WebDataSource *ds = [self activeDataSource]; + [ds retain]; + [ds _receivedMainResourceError:error complete:isComplete]; + [ds release]; +} + +- (NSURLRequest *)initialRequest +{ + return [[self activeDataSource] initialRequest]; +} + +- (void)_receivedData:(NSData *)data +{ + [[self activeDataSource] _receivedData:data]; +} + +- (void)_setRequest:(NSURLRequest *)request +{ + [[self activeDataSource] _setRequest:request]; +} + +- (void)_downloadWithLoadingConnection:(NSURLConnection *)connection request:(NSURLRequest *)request response:(NSURLResponse *)r proxy:(WKNSURLConnectionDelegateProxyPtr)proxy +{ + [[self activeDataSource] _downloadWithLoadingConnection:connection request:request response:r proxy:proxy]; +} + +- (void)_handleFallbackContent +{ + [[self activeDataSource] _handleFallbackContent]; +} + +- (BOOL)_isStopping +{ + return [[self activeDataSource] _isStopping]; +} + +- (void)_decidePolicyForMIMEType:(NSString *)MIMEType decisionListener:(WebPolicyDecisionListener *)listener +{ + [[self activeDataSource] _decidePolicyForMIMEType:MIMEType decisionListener:listener]; +} + +- (void)_setupForReplaceByMIMEType:(NSString *)newMIMEType +{ + [[self activeDataSource] _setupForReplaceByMIMEType:newMIMEType]; +} + +- (void)_setResponse:(NSURLResponse *)response +{ + [[self activeDataSource] _setResponse:response]; +} + +- (void)_mainReceivedError:(NSError *)error complete:(BOOL)isComplete +{ + [[self activeDataSource] _mainReceivedError:error complete:isComplete]; +} + +- (void)_finishedLoading +{ + [[self activeDataSource] _finishedLoading]; +} + +- (void)_mainReceivedBytesSoFar:(unsigned)bytesSoFar complete:(BOOL)isComplete +{ + [[self activeDataSource] _mainReceivedBytesSoFar:bytesSoFar complete:isComplete]; +} + +- (void)_iconLoaderReceivedPageIcon:(WebIconLoader *)iLoader +{ + ASSERT(iLoader == iconLoader); + [[self activeDataSource] _iconLoaderReceivedPageIcon:iLoader]; +} + +- (NSURL *)_URL +{ + return [[self activeDataSource] _URL]; +} + +@end diff --git a/WebKit/Loader/WebLoader.h b/WebKit/Loader/WebLoader.h new file mode 100644 index 000000000000..af8d1b56a4fa --- /dev/null +++ b/WebKit/Loader/WebLoader.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +@class NSError; +@class NSURLAuthenticationChallenge; +@class NSURLConnection; +@class NSURLConnectionAuthenticationChallenge; +@class NSURLCredential; +@class NSURLRequest; +@class NSURLResponse; +@class WebDataSource; +@class WebResource; +@class WebFrameLoader; + +@interface WebLoader : NSObject +{ +@protected + WebFrameLoader *frameLoader; + NSURLConnection *connection; + NSURLRequest *request; + BOOL reachedTerminalState; + BOOL loadingMultipartContent; + BOOL signalledFinish; + BOOL cancelledFlag; + id identifier; +@private + NSURLResponse *response; + NSURLAuthenticationChallenge *currentConnectionChallenge; + NSURLAuthenticationChallenge *currentWebChallenge; + BOOL defersCallbacks; + BOOL waitingToDeliverResource; + BOOL deliveredResource; + NSURL *originalURL; + NSMutableData *resourceData; + WebResource *resource; +#ifndef NDEBUG + BOOL isInitializingConnection; +#endif +} +- (void)signalFinish; + +- (BOOL)loadWithRequest:(NSURLRequest *)request; + +- (void)setFrameLoader:(WebFrameLoader *)fl; +- (WebFrameLoader *)frameLoader; + +- (void)cancel; +- (void)cancelWithError:(NSError *)error; + +- (void)setDefersCallbacks:(BOOL)defers; +- (BOOL)defersCallbacks; + +- (NSError *)cancelledError; + +- (void)setIdentifier:(id)ident; + +- (void)releaseResources; +- (NSURLResponse *)response; + +- (void)addData:(NSData *)data; +- (NSData *)resourceData; +- (void)clearResourceData; + +// Connection-less callbacks allow us to send callbacks using data attained from a WebResource instead of an NSURLConnection. +- (NSURLRequest *)willSendRequest:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse; +- (void)didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge; +- (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; + +// Used to work around the fact that you don't get any more NSURLConnection callbacks until you return from the first one. ++ (BOOL)inConnectionCallback; + +@end + +// Note: This interface can be removed once this method is declared +// in Foundation (probably will be in Foundation-485). +@interface NSObject (WebLoaderExtras) +- (void)connection:(NSURLConnection *)con didReceiveData:(NSData *)data lengthReceived:(long long)lengthReceived; +@end diff --git a/WebKit/Loader/WebLoader.m b/WebKit/Loader/WebLoader.m new file mode 100644 index 000000000000..a10544d52651 --- /dev/null +++ b/WebKit/Loader/WebLoader.m @@ -0,0 +1,656 @@ +/* + * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +#import +#import +#import +#import + +#import +#import +#import +#import +#import + +#import +#import +#import +#import + +static unsigned inNSURLConnectionCallback; +static BOOL NSURLConnectionSupportsBufferedData; + +@interface NSURLConnection (NSURLConnectionTigerPrivate) +- (NSData *)_bufferedData; +@end + +@interface WebLoader (WebNSURLAuthenticationChallengeSender) +@end + +@implementation WebLoader (WebNSURLAuthenticationChallengeSender) + +- (void)useCredential:(NSURLCredential *)credential forAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge +{ + if (challenge == nil || challenge != currentWebChallenge) { + return; + } + + [[currentConnectionChallenge sender] useCredential:credential forAuthenticationChallenge:currentConnectionChallenge]; + + [currentConnectionChallenge release]; + currentConnectionChallenge = nil; + + [currentWebChallenge release]; + currentWebChallenge = nil; +} + +- (void)continueWithoutCredentialForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge +{ + if (challenge == nil || challenge != currentWebChallenge) { + return; + } + + [[currentConnectionChallenge sender] continueWithoutCredentialForAuthenticationChallenge:currentConnectionChallenge]; + + [currentConnectionChallenge release]; + currentConnectionChallenge = nil; + + [currentWebChallenge release]; + currentWebChallenge = nil; +} + +- (void)cancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge +{ + if (challenge == nil || challenge != currentWebChallenge) { + return; + } + + [self cancel]; +} + +@end + +// This declaration is only needed to ease the transition to a new SPI. It can be removed +// moving forward beyond Tiger 8A416. +@interface NSURLProtocol (WebFoundationSecret) ++ (void)_removePropertyForKey:(NSString *)key inRequest:(NSMutableURLRequest *)request; +@end + +@implementation WebLoader + ++ (void)initialize +{ + NSURLConnectionSupportsBufferedData = [NSURLConnection instancesRespondToSelector:@selector(_bufferedData)]; +} + +- (void)releaseResources +{ + ASSERT(!reachedTerminalState); + + // It's possible that when we release the handle, it will be + // deallocated and release the last reference to this object. + // We need to retain to avoid accessing the object after it + // has been deallocated and also to avoid reentering this method. + + [self retain]; + + // We need to set reachedTerminalState to YES before we release + // the resources to prevent a double dealloc of WebView + + reachedTerminalState = YES; + + [identifier release]; + identifier = nil; + + [connection release]; + connection = nil; + + [frameLoader release]; + frameLoader = nil; + + [resource release]; + resource = nil; + + [resourceData release]; + resourceData = nil; + + [self release]; +} + +- (void)dealloc +{ + ASSERT(reachedTerminalState); + [request release]; + [response release]; + [originalURL release]; + [super dealloc]; +} + +- (void)deliverResource +{ + ASSERT(resource); + ASSERT(waitingToDeliverResource); + + if (!defersCallbacks) { + [self didReceiveResponse:[resource _response]]; + NSData *data = [resource data]; + [self didReceiveData:data lengthReceived:[data length]]; + [self didFinishLoading]; + deliveredResource = YES; + waitingToDeliverResource = NO; + } +} + +- (void)deliverResourceAfterDelay +{ + if (resource && !defersCallbacks && !waitingToDeliverResource && !deliveredResource) { + [self performSelector:@selector(deliverResource) withObject:nil afterDelay:0]; + waitingToDeliverResource = YES; + } +} + +// The following 2 methods are copied from [NSHTTPURLProtocol _cachedResponsePassesValidityChecks] and modified for our needs. +// FIXME: It would be nice to eventually to share this code somehow. +- (BOOL)_canUseResourceForRequest:(NSURLRequest *)theRequest +{ + NSURLRequestCachePolicy policy = [theRequest cachePolicy]; + + if (policy == NSURLRequestReturnCacheDataElseLoad) { + return YES; + } else if (policy == NSURLRequestReturnCacheDataDontLoad) { + return YES; + } else if (policy == NSURLRequestReloadIgnoringCacheData) { + return NO; + } else if ([theRequest valueForHTTPHeaderField:@"must-revalidate"] != nil) { + return NO; + } else if ([theRequest valueForHTTPHeaderField:@"proxy-revalidate"] != nil) { + return NO; + } else if ([theRequest valueForHTTPHeaderField:@"If-Modified-Since"] != nil) { + return NO; + } else if ([theRequest valueForHTTPHeaderField:@"Cache-Control"] != nil) { + return NO; + } else if ([[theRequest HTTPMethod] _webkit_isCaseInsensitiveEqualToString:@"POST"]) { + return NO; + } else { + return YES; + } +} + +- (BOOL)_canUseResourceWithResponse:(NSURLResponse *)theResponse +{ + if (WKGetNSURLResponseMustRevalidate(theResponse)) { + return NO; + } else if (WKGetNSURLResponseCalculatedExpiration(theResponse) - CFAbsoluteTimeGetCurrent() < 1) { + return NO; + } else { + return YES; + } +} + +- (BOOL)loadWithRequest:(NSURLRequest *)r +{ + ASSERT(connection == nil); + ASSERT(resource == nil); + + NSURL *URL = [[r URL] retain]; + [originalURL release]; + originalURL = URL; + + deliveredResource = NO; + waitingToDeliverResource = NO; + + NSURLRequest *clientRequest = [self willSendRequest:r redirectResponse:nil]; + if (clientRequest == nil) { + NSError *badURLError = [NSError _webKitErrorWithDomain:NSURLErrorDomain + code:NSURLErrorCancelled + URL:[r URL]]; + [self didFailWithError:badURLError]; + return NO; + } + r = clientRequest; + + if ([[r URL] isEqual:originalURL] && [self _canUseResourceForRequest:r]) { + resource = [frameLoader _archivedSubresourceForURL:originalURL]; + if (resource != nil) { + if ([self _canUseResourceWithResponse:[resource _response]]) { + [resource retain]; + // Deliver the resource after a delay because callers don't expect to receive callbacks while calling this method. + [self deliverResourceAfterDelay]; + return YES; + } else { + resource = nil; + } + } + } + +#ifndef NDEBUG + isInitializingConnection = YES; +#endif + connection = [[NSURLConnection alloc] initWithRequest:r delegate:self]; +#ifndef NDEBUG + isInitializingConnection = NO; +#endif + if (defersCallbacks) { + WKSetNSURLConnectionDefersCallbacks(connection, YES); + } + + return YES; +} + +- (void)setDefersCallbacks:(BOOL)defers +{ + defersCallbacks = defers; + WKSetNSURLConnectionDefersCallbacks(connection, defers); + // Deliver the resource after a delay because callers don't expect to receive callbacks while calling this method. + [self deliverResourceAfterDelay]; +} + +- (BOOL)defersCallbacks +{ + return defersCallbacks; +} + +- (void)setFrameLoader:(WebFrameLoader *)fl +{ + ASSERT(fl); + + [fl retain]; + [frameLoader release]; + frameLoader = fl; + + [self setDefersCallbacks:[frameLoader _defersCallbacks]]; +} + +- (WebFrameLoader *)frameLoader +{ + return frameLoader; +} + +- (void)addData:(NSData *)data +{ + // 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]; + } + } +} + +- (NSData *)resourceData +{ + if (resource != nil) { + return [resource data]; + } + if (resourceData != nil) { + // Retain and autorelease resourceData since releaseResources (which releases resourceData) may be called + // before the caller of this method has an opporuntity to retain the returned data (4070729). + return [[resourceData retain] autorelease]; + } + if (NSURLConnectionSupportsBufferedData) { + return [connection _bufferedData]; + } + return nil; +} + +- (void)clearResourceData +{ + [resourceData setLength:0]; +} + +- (NSURLRequest *)willSendRequest:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse +{ + ASSERT(!reachedTerminalState); + NSMutableURLRequest *mutableRequest = [[newRequest mutableCopy] autorelease]; + NSMutableURLRequest *clientRequest; + NSURLRequest *updatedRequest; + BOOL haveDataSchemeRequest = NO; + + // 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]; + + newRequest = mutableRequest; + + // If we have a special "applewebdata" scheme URL we send a fake request to the delegate. + clientRequest = [mutableRequest _webDataRequestExternalRequest]; + if (!clientRequest) + clientRequest = mutableRequest; + else + haveDataSchemeRequest = YES; + + if (identifier == nil) + identifier = [frameLoader _identifierForInitialRequest:clientRequest]; + + updatedRequest = [frameLoader _willSendRequest:clientRequest forResource:identifier redirectResponse:redirectResponse]; + + if (!haveDataSchemeRequest) + newRequest = updatedRequest; + else { + // If the delegate modified the request use that instead of + // our applewebdata request, otherwise use the original + // applewebdata request. + if (![updatedRequest isEqual:clientRequest]) { + newRequest = updatedRequest; + + // The respondsToSelector: check is only necessary for people building/running prior to Tier 8A416. + if ([NSURLProtocol respondsToSelector:@selector(_removePropertyForKey:inRequest:)] && + [newRequest isKindOfClass:[NSMutableURLRequest class]]) { + NSMutableURLRequest *mr = (NSMutableURLRequest *)newRequest; + [NSURLProtocol _removePropertyForKey:[NSURLRequest _webDataRequestPropertyKey] inRequest:mr]; + } + + } + } + + // Store a copy of the request. + [request autorelease]; + + request = [newRequest copy]; + + [self release]; + return request; +} + +- (void)didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge +{ + ASSERT(!reachedTerminalState); + ASSERT(!currentConnectionChallenge); + ASSERT(!currentWebChallenge); + + // 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]; + currentConnectionChallenge = [challenge retain];; + currentWebChallenge = [[NSURLAuthenticationChallenge alloc] initWithAuthenticationChallenge:challenge sender:self]; + + [frameLoader _didReceiveAuthenticationChallenge:currentWebChallenge forResource:identifier]; + + [self release]; +} + +- (void)didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge +{ + ASSERT(!reachedTerminalState); + ASSERT(currentConnectionChallenge); + ASSERT(currentWebChallenge); + ASSERT(currentConnectionChallenge = challenge); + + // 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]; + [frameLoader _didCancelAuthenticationChallenge:currentWebChallenge forResource:identifier]; + [self release]; +} + +- (void)didReceiveResponse:(NSURLResponse *)r +{ + ASSERT(!reachedTerminalState); + + // 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]; + + // If the URL is one of our whacky applewebdata URLs then + // fake up a substitute URL to present to the delegate. + if([WebDataProtocol _webIsDataProtocolURL:[r URL]]) { + r = [[[NSURLResponse alloc] initWithURL:[request _webDataRequestExternalURL] MIMEType:[r MIMEType] expectedContentLength:[r expectedContentLength] textEncodingName:[r textEncodingName]] autorelease]; + } + + [r retain]; + [response release]; + response = r; + + [frameLoader _didReceiveResponse:r forResource:identifier]; + + [self release]; +} + +- (void)didReceiveData:(NSData *)data lengthReceived:(long long)lengthReceived +{ + // The following assertions are not quite valid here, since a subclass + // might override didReceiveData: in a way that invalidates them. This + // happens with the steps listed in 3266216 + // ASSERT(con == connection); + // ASSERT(!reachedTerminalState); + + // 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]; + + [self addData:data]; + + [frameLoader _didReceiveData:data contentLength:lengthReceived forResource:identifier]; + + [self release]; +} + +- (void)willStopBufferingData:(NSData *)data +{ + ASSERT(resourceData == nil); + resourceData = [data mutableCopy]; +} + +- (void)signalFinish +{ + signalledFinish = YES; + [frameLoader _didFinishLoadingForResource:identifier]; +} + +- (void)didFinishLoading +{ + // If load has been cancelled after finishing (which could happen with a + // javascript that changes the window location), do nothing. + if (cancelledFlag) + return; + + ASSERT(!reachedTerminalState); + + if (!signalledFinish) + [self signalFinish]; + + [self releaseResources]; +} + +- (void)didFailWithError:(NSError *)error +{ + if (cancelledFlag) { + return; + } + + ASSERT(!reachedTerminalState); + + // 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]; + + [frameLoader _didFailLoadingWithError:error forResource:identifier]; + + [self releaseResources]; + [self release]; +} + +- (NSCachedURLResponse *)willCacheResponse:(NSCachedURLResponse *)cachedResponse +{ + // When in private browsing mode, prevent caching to disk + if ([cachedResponse storagePolicy] == NSURLCacheStorageAllowed && [frameLoader _privateBrowsingEnabled]) { + cachedResponse = [[[NSCachedURLResponse alloc] initWithResponse:[cachedResponse response] + data:[cachedResponse data] + userInfo:[cachedResponse userInfo] + storagePolicy:NSURLCacheStorageAllowedInMemoryOnly] autorelease]; + } + return cachedResponse; +} + +- (NSURLRequest *)connection:(NSURLConnection *)con willSendRequest:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse +{ + ASSERT(con == connection); + ++inNSURLConnectionCallback; + NSURLRequest *result = [self willSendRequest:newRequest redirectResponse:redirectResponse]; + --inNSURLConnectionCallback; + return result; +} + +- (void)connection:(NSURLConnection *)con didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge +{ + ASSERT(con == connection); + ++inNSURLConnectionCallback; + [self didReceiveAuthenticationChallenge:challenge]; + --inNSURLConnectionCallback; +} + +- (void)connection:(NSURLConnection *)con didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge +{ + ASSERT(con == connection); + ++inNSURLConnectionCallback; + [self didCancelAuthenticationChallenge:challenge]; + --inNSURLConnectionCallback; +} + +- (void)connection:(NSURLConnection *)con didReceiveResponse:(NSURLResponse *)r +{ + ASSERT(con == connection); + ++inNSURLConnectionCallback; + [self didReceiveResponse:r]; + --inNSURLConnectionCallback; +} + +- (void)connection:(NSURLConnection *)con didReceiveData:(NSData *)data lengthReceived:(long long)lengthReceived +{ + ASSERT(con == connection); + ++inNSURLConnectionCallback; + [self didReceiveData:data lengthReceived:lengthReceived]; + --inNSURLConnectionCallback; +} + +- (void)connection:(NSURLConnection *)con willStopBufferingData:(NSData *)data +{ + ASSERT(con == connection); + ++inNSURLConnectionCallback; + [self willStopBufferingData:data]; + --inNSURLConnectionCallback; +} + +- (void)connectionDidFinishLoading:(NSURLConnection *)con +{ + // don't worry about checking connection consistency if this load + // got cancelled while finishing. + ASSERT(cancelledFlag || con == connection); + ++inNSURLConnectionCallback; + [self didFinishLoading]; + --inNSURLConnectionCallback; +} + +- (void)connection:(NSURLConnection *)con didFailWithError:(NSError *)error +{ + ASSERT(con == connection); + ++inNSURLConnectionCallback; + [self didFailWithError:error]; + --inNSURLConnectionCallback; +} + +- (NSCachedURLResponse *)connection:(NSURLConnection *)con willCacheResponse:(NSCachedURLResponse *)cachedResponse +{ +#ifndef NDEBUG + if (connection == nil && isInitializingConnection) { + LOG_ERROR("connection:willCacheResponse: was called inside of [NSURLConnection initWithRequest:delegate:] (40676250)"); + } +#endif + ++inNSURLConnectionCallback; + NSCachedURLResponse *result = [self willCacheResponse:cachedResponse]; + --inNSURLConnectionCallback; + return result; +} + +- (void)cancelWithError:(NSError *)error +{ + ASSERT(!reachedTerminalState); + + // This flag prevents bad behvior when loads that finish cause the + // load itself to be cancelled (which could happen with a javascript that + // changes the window location). This is used to prevent both the body + // of this method and the body of connectionDidFinishLoading: running + // for a single delegate. Cancelling wins. + cancelledFlag = YES; + + [currentConnectionChallenge release]; + currentConnectionChallenge = nil; + + [currentWebChallenge release]; + currentWebChallenge = nil; + + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(deliverResource) object:nil]; + [connection cancel]; + + [frameLoader _didFailLoadingWithError:error forResource:identifier]; + + [self releaseResources]; +} + +- (void)cancel +{ + if (!reachedTerminalState) { + [self cancelWithError:[self cancelledError]]; + } +} + +- (NSError *)cancelledError +{ + return [NSError _webKitErrorWithDomain:NSURLErrorDomain + code:NSURLErrorCancelled + URL:[request URL]]; +} + +- (void)setIdentifier: ident +{ + if (identifier != ident){ + [identifier release]; + identifier = [ident retain]; + } +} + +- (NSURLResponse *)response +{ + return response; +} + ++ (BOOL)inConnectionCallback +{ + return inNSURLConnectionCallback != 0; +} + +@end diff --git a/WebKit/Loader/WebMainResourceLoader.m b/WebKit/Loader/WebMainResourceLoader.m new file mode 100644 index 000000000000..03653781d615 --- /dev/null +++ b/WebKit/Loader/WebMainResourceLoader.m @@ -0,0 +1,486 @@ +/* + * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +#import +#import +#import +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +// FIXME: More that is in common with WebSubresourceLoader should move up into WebLoader. + +@implementation WebMainResourceLoader + +- (id)initWithFrameLoader:(WebFrameLoader *)fl +{ + self = [super init]; + + if (self) { + [self setFrameLoader:fl]; + proxy = WKCreateNSURLConnectionDelegateProxy(); + [proxy setDelegate:self]; + } + + return self; +} + +- (void)dealloc +{ + [_initialRequest release]; + + [proxy setDelegate:nil]; + [proxy release]; + + [super dealloc]; +} + +- (void)finalize +{ + [proxy setDelegate:nil]; + [super finalize]; +} + +- (void)receivedError:(NSError *)error +{ + // Calling _receivedMainResourceError will likely result in a call to release, so we must retain. + [self retain]; + WebFrameLoader *fl = [frameLoader retain]; // super's didFailWithError will release the frameLoader + + if (!cancelledFlag) { + ASSERT(!reachedTerminalState); + [frameLoader _didFailLoadingWithError:error forResource:identifier]; + } + + [fl _receivedMainResourceError:error complete:YES]; + + if (!cancelledFlag) + [self releaseResources]; + + ASSERT(reachedTerminalState); + + [fl release]; + [self release]; +} + +- (void)cancelContentPolicy +{ + [listener _invalidate]; + [listener release]; + listener = nil; + [policyResponse release]; + policyResponse = nil; +} + +-(void)cancelWithError:(NSError *)error +{ + // Calling _receivedMainResourceError will likely result in a call to release, so we must retain. + [self retain]; + + [self cancelContentPolicy]; + [frameLoader retain]; + [frameLoader _receivedMainResourceError:error complete:YES]; + [frameLoader release]; + [super cancelWithError:error]; + + [self release]; +} + +- (NSError *)interruptForPolicyChangeError +{ + return [NSError _webKitErrorWithDomain:WebKitErrorDomain code:WebKitErrorFrameLoadInterruptedByPolicyChange URL:[request URL]]; +} + +-(void)stopLoadingForPolicyChange +{ + [self retain]; + [self cancelWithError:[self interruptForPolicyChangeError]]; + [self release]; +} + +-(void)continueAfterNavigationPolicy:(NSURLRequest *)_request formState:(WebFormState *)state +{ + if (!_request) { + [self stopLoadingForPolicyChange]; + } +} + +- (BOOL)_isPostOrRedirectAfterPost:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse +{ + BOOL result = NO; + + if ([[newRequest HTTPMethod] isEqualToString:@"POST"]) { + result = YES; + } + else if (redirectResponse && [redirectResponse isKindOfClass:[NSHTTPURLResponse class]]) { + int status = [(NSHTTPURLResponse *)redirectResponse statusCode]; + if (((status >= 301 && status <= 303) || status == 307) + && [[[frameLoader initialRequest] HTTPMethod] isEqualToString:@"POST"]) { + result = YES; + } + } + + return result; +} + +- (void)addData:(NSData *)data +{ + [super addData:data]; + [frameLoader _receivedData:data]; +} + +- (void)saveResource +{ + // Override. We don't want to save the main resource as a subresource of the data source. +} + +- (NSURLRequest *)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 + // fact that this "callback" is sent when starting every load, and the state of callback + // deferrals plays less of a part in this function in preventing the bad behavior deferring + // callbacks is meant to prevent. + ASSERT(newRequest != nil); + + // 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]; + + NSURL *URL = [newRequest URL]; + + LOG(Redirect, "URL = %@", URL); + + NSMutableURLRequest *mutableRequest = nil; + // Update cookie policy base URL as URL changes, except for subframes, which use the + // URL of the main frame which doesn't change when we redirect. + if ([[frameLoader webFrame] _isMainFrame]) { + mutableRequest = [newRequest mutableCopy]; + [mutableRequest setMainDocumentURL:URL]; + } + + // If we're fielding a redirect in response to a POST, force a load from origin, since + // this is a common site technique to return to a page viewing some data that the POST + // just modified. + // Also, POST requests always load from origin, but this does not affect subresources. + if ([newRequest cachePolicy] == NSURLRequestUseProtocolCachePolicy && + [self _isPostOrRedirectAfterPost:newRequest redirectResponse:redirectResponse]) { + if (!mutableRequest) { + mutableRequest = [newRequest mutableCopy]; + } + [mutableRequest setCachePolicy:NSURLRequestReloadIgnoringCacheData]; + } + if (mutableRequest) { + newRequest = [mutableRequest autorelease]; + } + + // Note super will make a copy for us, so reassigning newRequest is important. Since we are returning this value, but + // it's only guaranteed to be retained by self, and self might be dealloc'ed in this method, we have to autorelease. + // See 3777253 for an example. + newRequest = [[[super willSendRequest:newRequest redirectResponse:redirectResponse] retain] autorelease]; + + // Don't set this on the first request. It is set + // when the main load was started. + [frameLoader _setRequest:newRequest]; + + [[frameLoader webFrame] _checkNavigationPolicyForRequest:newRequest + dataSource:[frameLoader activeDataSource] + formState:nil + andCall:self + withSelector:@selector(continueAfterNavigationPolicy:formState:)]; + + [self release]; + return newRequest; +} + +-(void)continueAfterContentPolicy:(WebPolicyAction)contentPolicy response:(NSURLResponse *)r +{ + NSURL *URL = [request URL]; + NSString *MIMEType = [r MIMEType]; + + switch (contentPolicy) { + case WebPolicyUse: + { + // Prevent remote web archives from loading because they can claim to be from any domain and thus avoid cross-domain security checks (4120255). + BOOL isRemote = ![URL isFileURL] && ![WebDataProtocol _webIsDataProtocolURL:URL]; + BOOL isRemoteWebArchive = isRemote && [MIMEType _webkit_isCaseInsensitiveEqualToString:@"application/x-webarchive"]; + if (![WebDataSource _canShowMIMEType:MIMEType] || isRemoteWebArchive) { + [[frameLoader webFrame] _handleUnimplementablePolicyWithErrorCode:WebKitErrorCannotShowMIMEType forURL:URL]; + // Check reachedTerminalState since the load may have already been cancelled inside of _handleUnimplementablePolicyWithErrorCode::. + if (!reachedTerminalState) { + [self stopLoadingForPolicyChange]; + } + return; + } + break; + } + case WebPolicyDownload: + [proxy setDelegate:nil]; + [frameLoader _downloadWithLoadingConnection:connection request:request response:r proxy:proxy]; + [proxy release]; + proxy = nil; + + [self receivedError:[self interruptForPolicyChangeError]]; + return; + + case WebPolicyIgnore: + [self stopLoadingForPolicyChange]; + return; + + default: + ASSERT_NOT_REACHED(); + } + + [self retain]; + + if ([r isKindOfClass:[NSHTTPURLResponse class]]) { + int status = [(NSHTTPURLResponse *)r statusCode]; + if (status < 200 || status >= 300) { + // Handle fallback for error cases. + DOMHTMLElement *hostElement = [[frameLoader webFrame] frameElement]; + [frameLoader _handleFallbackContent]; + if (hostElement && [hostElement isKindOfClass:[DOMHTMLObjectElement class]]) + // object elements are no longer rendered after we fallback, so don't + // keep trying to process data from their load + [self cancel]; + } + } + + // we may have cancelled this load as part of switching to fallback content + if (!reachedTerminalState) { + [super didReceiveResponse:r]; + } + + if (![frameLoader _isStopping] && ([URL _webkit_shouldLoadAsEmptyDocument] || [WebView _representationExistsForURLScheme:[URL scheme]])) { + [self didFinishLoading]; + } + + [self release]; +} + +-(void)continueAfterContentPolicy:(WebPolicyAction)policy +{ + NSURLResponse *r = [policyResponse retain]; + BOOL isStopping = [frameLoader _isStopping]; + + [self cancelContentPolicy]; + if (!isStopping) + [self continueAfterContentPolicy:policy response:r]; + + [r release]; +} + +-(void)checkContentPolicyForResponse:(NSURLResponse *)r +{ + WebPolicyDecisionListener *l = [[WebPolicyDecisionListener alloc] + _initWithTarget:self action:@selector(continueAfterContentPolicy:)]; + listener = l; + policyResponse = [r retain]; + + [l retain]; + [frameLoader _decidePolicyForMIMEType:[r MIMEType] decisionListener:listener]; + [l release]; +} + + +- (void)didReceiveResponse:(NSURLResponse *)r +{ + ASSERT([[r URL] _webkit_shouldLoadAsEmptyDocument] || ![self defersCallbacks]); + ASSERT([[r URL] _webkit_shouldLoadAsEmptyDocument] || ![frameLoader _defersCallbacks]); + + LOG(Loading, "main content type: %@", [r MIMEType]); + + if (loadingMultipartContent) { + [frameLoader _setupForReplaceByMIMEType:[r MIMEType]]; + [self clearResourceData]; + } + + if ([[r MIMEType] isEqualToString:@"multipart/x-mixed-replace"]) + loadingMultipartContent = YES; + + // FIXME: This is a workaround to make web archive files work with Foundations that + // are too old to know about web archive files. We should remove this before we ship. + NSURL *URL = [r URL]; + if ([[[URL path] pathExtension] _webkit_isCaseInsensitiveEqualToString:@"webarchive"]) { + r = [[[NSURLResponse alloc] initWithURL:URL + MIMEType:@"application/x-webarchive" + expectedContentLength:[r expectedContentLength] + textEncodingName:[r textEncodingName]] autorelease]; + } + + // 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]; + [frameLoader _setResponse:r]; + _contentLength = [r expectedContentLength]; + + [self checkContentPolicyForResponse:r]; + [self release]; +} + +- (void)didReceiveData:(NSData *)data lengthReceived:(long long)lengthReceived +{ + ASSERT(data); + ASSERT([data length] != 0); + ASSERT(![self defersCallbacks]); + ASSERT(![frameLoader _defersCallbacks]); + + LOG(Loading, "URL = %@, data = %p, length %d", [frameLoader _URL], data, [data length]); + + // 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]; + [frameLoader _mainReceivedBytesSoFar:_bytesReceived complete:NO]; + + [super didReceiveData:data lengthReceived:lengthReceived]; + _bytesReceived += [data length]; + + LOG(Loading, "%d of %d", _bytesReceived, _contentLength); + [self release]; +} + +- (void)didFinishLoading +{ + ASSERT([[frameLoader _URL] _webkit_shouldLoadAsEmptyDocument] || ![self defersCallbacks]); + ASSERT([[frameLoader _URL] _webkit_shouldLoadAsEmptyDocument] || ![frameLoader _defersCallbacks]); + + LOG(Loading, "URL = %@", [frameLoader _URL]); + + // Calls in this method will most likely result in a call to release, so we must retain. + [self retain]; + + [frameLoader _finishedLoading]; + [frameLoader _mainReceivedBytesSoFar:_bytesReceived complete:YES]; + [super didFinishLoading]; + + [self release]; +} + +- (void)didFailWithError:(NSError *)error +{ + ASSERT(![self defersCallbacks]); + ASSERT(![frameLoader _defersCallbacks]); + + [self receivedError:error]; +} + +- (NSURLRequest *)loadWithRequestNow:(NSURLRequest *)r +{ + BOOL shouldLoadEmptyBeforeRedirect = [[r URL] _webkit_shouldLoadAsEmptyDocument]; + + ASSERT(connection == nil); + ASSERT(shouldLoadEmptyBeforeRedirect || ![self defersCallbacks]); + ASSERT(shouldLoadEmptyBeforeRedirect || ![frameLoader _defersCallbacks]); + + // Send this synthetic delegate callback since clients expect it, and + // we no longer send the callback from within NSURLConnection for + // initial requests. + r = [self willSendRequest:r redirectResponse:nil]; + NSURL *URL = [r URL]; + BOOL shouldLoadEmpty = [URL _webkit_shouldLoadAsEmptyDocument]; + + if (shouldLoadEmptyBeforeRedirect && !shouldLoadEmpty && [self defersCallbacks]) { + return r; + } + + if (shouldLoadEmpty || [WebDataSource _representationExistsForURLScheme:[URL scheme]]) { + NSString *MIMEType; + if (shouldLoadEmpty) { + MIMEType = @"text/html"; + } else { + MIMEType = [WebDataSource _generatedMIMETypeForURLScheme:[URL scheme]]; + } + + NSURLResponse *resp = [[NSURLResponse alloc] initWithURL:URL MIMEType:MIMEType + expectedContentLength:0 textEncodingName:nil]; + [self didReceiveResponse:resp]; + [resp release]; + } else { + connection = [[NSURLConnection alloc] initWithRequest:r delegate:proxy]; + } + + return nil; +} + +- (BOOL)loadWithRequest:(NSURLRequest *)r +{ + ASSERT(connection == nil); + + BOOL defer = [self defersCallbacks]; + if (defer) { + NSURL *URL = [r URL]; + BOOL shouldLoadEmpty = [URL _webkit_shouldLoadAsEmptyDocument]; + if (shouldLoadEmpty) { + defer = NO; + } + } + if (!defer) { + r = [self loadWithRequestNow:r]; + if (r != nil) { + // Started as an empty document, but was redirected to something non-empty. + ASSERT([self defersCallbacks]); + defer = YES; + } + } + if (defer) { + NSURLRequest *copy = [r copy]; + [_initialRequest release]; + _initialRequest = copy; + } + + return YES; +} + +- (void)setDefersCallbacks:(BOOL)defers +{ + [super setDefersCallbacks:defers]; + if (!defers) { + NSURLRequest *r = _initialRequest; + if (r != nil) { + _initialRequest = nil; + [self loadWithRequestNow:r]; + [r release]; + } + } +} + +@end diff --git a/WebKit/Loader/WebSubresourceLoader.m b/WebKit/Loader/WebSubresourceLoader.m new file mode 100644 index 000000000000..59541e57d890 --- /dev/null +++ b/WebKit/Loader/WebSubresourceLoader.m @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +#import +#import +#import +#import +#import +#import +#import + +#import + +#import +#import + +@implementation WebSubresourceLoader + +- initWithLoader:(id )l frameLoader:(WebFrameLoader *)fl +{ + [super init]; + + coreLoader = [l retain]; + + [self setFrameLoader:fl]; + + return self; +} + +- (void)dealloc +{ + [coreLoader release]; + [super dealloc]; +} + ++ (WebSubresourceLoader *)startLoadingResource:(id )rLoader + withRequest:(NSMutableURLRequest *)newRequest + customHeaders:(NSDictionary *)customHeaders + referrer:(NSString *)referrer + forFrameLoader:(WebFrameLoader *)fl +{ + WebSubresourceLoader *loader = [[[self alloc] initWithLoader:rLoader frameLoader:fl] autorelease]; + + [fl _addSubresourceLoader:loader]; + + NSEnumerator *e = [customHeaders keyEnumerator]; + NSString *key; + while ((key = [e nextObject])) + [newRequest addValue:[customHeaders objectForKey:key] forHTTPHeaderField:key]; + + // Use the original request's cache policy for two reasons: + // 1. For POST requests, we mutate the cache policy for the main resource, + // but we do not want this to apply to subresources + // 2. Delegates that modify the cache policy using willSendRequest: should + // not affect any other resources. Such changes need to be done + // per request. + if ([newRequest _web_isConditionalRequest]) + [newRequest setCachePolicy:NSURLRequestReloadIgnoringCacheData]; + else + [newRequest setCachePolicy:[[fl _originalRequest] cachePolicy]]; + [newRequest _web_setHTTPReferrer:referrer]; + + [[fl webFrame] _addExtraFieldsToRequest:newRequest mainResource:NO alwaysFromRequest:NO]; + + if (![loader loadWithRequest:newRequest]) + loader = nil; + + return loader; +} + ++ (WebSubresourceLoader *)startLoadingResource:(id )rLoader + withMethod:(NSString *)method + URL:(NSURL *)URL + customHeaders:(NSDictionary *)customHeaders + referrer:(NSString *)referrer + forFrameLoader:(WebFrameLoader *)fl +{ + NSMutableURLRequest *newRequest = [[NSMutableURLRequest alloc] initWithURL:URL]; + + // setHTTPMethod is not called for GET requests to work around . + if (![method isEqualToString:@"GET"]) + [newRequest setHTTPMethod:method]; + + WebSubresourceLoader *loader = [self startLoadingResource:rLoader withRequest:newRequest customHeaders:customHeaders referrer:referrer forFrameLoader:fl]; + [newRequest release]; + + return loader; +} + ++ (WebSubresourceLoader *)startLoadingResource:(id )rLoader + withMethod:(NSString *)method + URL:(NSURL *)URL + customHeaders:(NSDictionary *)customHeaders + postData:(NSArray *)postData + referrer:(NSString *)referrer + forFrameLoader:(WebFrameLoader *)fl +{ + NSMutableURLRequest *newRequest = [[NSMutableURLRequest alloc] initWithURL:URL]; + + // setHTTPMethod is not called for GET requests to work around . + if (![method isEqualToString:@"GET"]) + [newRequest setHTTPMethod:method]; + + webSetHTTPBody(newRequest, postData); + + WebSubresourceLoader *loader = [self startLoadingResource:rLoader withRequest:newRequest customHeaders:customHeaders referrer:referrer forFrameLoader:fl]; + [newRequest release]; + + return loader; + +} + +- (void)receivedError:(NSError *)error +{ + [frameLoader _receivedError:error]; +} + +- (NSURLRequest *)willSendRequest:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse; +{ + NSURL *oldURL = [request URL]; + NSURLRequest *clientRequest = [super willSendRequest:newRequest redirectResponse:redirectResponse]; + + if (clientRequest != nil && oldURL != [clientRequest URL] && ![oldURL isEqual:[clientRequest URL]]) + [coreLoader redirectedToURL:[clientRequest URL]]; + + return clientRequest; +} + +- (void)didReceiveResponse:(NSURLResponse *)r +{ + ASSERT(r); + + if ([[r MIMEType] isEqualToString:@"multipart/x-mixed-replace"]) + loadingMultipartContent = YES; + + // 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]; + [coreLoader receivedResponse:r]; + // The coreLoader can cancel a load if it receives a multipart response for a non-image + if (reachedTerminalState) { + [self release]; + return; + } + [super didReceiveResponse:r]; + [self release]; + + if (loadingMultipartContent && [[self resourceData] length]) { + // A subresource loader does not load multipart sections progressively, deliver the previously received data to the coreLoader all at once + [coreLoader addData:[self resourceData]]; + // Clears the data to make way for the next multipart section + [self clearResourceData]; + + // After the first multipart section is complete, signal to delegates that this load is "finished" + if (!signalledFinish) + [self signalFinish]; + } +} + +- (void)didReceiveData:(NSData *)data lengthReceived:(long long)lengthReceived +{ + // 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]; + // A subresource loader does not load multipart sections progressively, don't deliver any data to the coreLoader yet + if (!loadingMultipartContent) + [coreLoader addData:data]; + [super didReceiveData:data lengthReceived:lengthReceived]; + [self release]; +} + +- (void)signalFinish +{ + [frameLoader _removeSubresourceLoader:self]; + [frameLoader _finishedLoadingResource]; + [super signalFinish]; +} + +- (void)didFinishLoading +{ + // Calling _removeSubresourceLoader will likely result in a call to release, so we must retain. + [self retain]; + + [coreLoader finishWithData:[self resourceData]]; + + if (!signalledFinish) + [self signalFinish]; + + [super didFinishLoading]; + + [self release]; +} + +- (void)didFailWithError:(NSError *)error +{ + // Calling _removeSubresourceLoader will likely result in a call to release, so we must retain. + [self retain]; + + [coreLoader reportError]; + [frameLoader _removeSubresourceLoader:self]; + [self receivedError:error]; + [super didFailWithError:error]; + + [self release]; +} + +- (void)cancel +{ + // Calling _removeSubresourceLoader will likely result in a call to release, so we must retain. + [self retain]; + + [coreLoader cancel]; + [frameLoader _removeSubresourceLoader:self]; + [self receivedError:[self cancelledError]]; + [super cancel]; + + [self release]; +} + +@end