Reviewed by Darin.
[WebKit-https.git] / WebKit / WebView.subproj / WebBaseResourceHandleDelegate.m
1 /*
2  * Copyright (C) 2005 Apple Computer, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer. 
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution. 
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission. 
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #import <WebKit/WebBaseResourceHandleDelegate.h>
30
31 #import <Foundation/NSURLAuthenticationChallenge.h>
32 #import <Foundation/NSURLConnection.h>
33 #import <Foundation/NSURLConnectionPrivate.h>
34 #import <Foundation/NSURLRequest.h>
35 #import <Foundation/NSURLResponse.h>
36
37 #import <WebKit/WebAssertions.h>
38 #import <WebKit/WebDataProtocol.h>
39 #import <WebKit/WebDataSourcePrivate.h>
40 #import <WebKit/WebDefaultResourceLoadDelegate.h>
41 #import <WebKit/WebKitErrors.h>
42 #import <WebKit/WebKitErrorsPrivate.h>
43 #import <WebKit/WebNSURLRequestExtras.h>
44 #import <WebKit/WebKitNSStringExtras.h>
45 #import <WebKit/WebPreferences.h>
46 #import <WebKit/WebPreferencesPrivate.h>
47 #import <WebKit/WebResourceLoadDelegate.h>
48 #import <WebKit/WebResourcePrivate.h>
49 #import <WebKit/WebViewPrivate.h>
50 #import <WebKitSystemInterface.h>
51
52 static unsigned inNSURLConnectionCallback;
53 static BOOL NSURLConnectionSupportsBufferedData;
54
55 @interface NSURLConnection (NSURLConnectionTigerPrivate)
56 - (NSData *)_bufferedData;
57 @end
58
59 @interface WebBaseResourceHandleDelegate (WebNSURLAuthenticationChallengeSender) <NSURLAuthenticationChallengeSender>
60 @end
61
62 @implementation WebBaseResourceHandleDelegate (WebNSURLAuthenticationChallengeSender) 
63
64 - (void)useCredential:(NSURLCredential *)credential forAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
65 {
66     if (challenge == nil || challenge != currentWebChallenge) {
67         return;
68     }
69
70     [[currentConnectionChallenge sender] useCredential:credential forAuthenticationChallenge:currentConnectionChallenge];
71
72     [currentConnectionChallenge release];
73     currentConnectionChallenge = nil;
74     
75     [currentWebChallenge release];
76     currentWebChallenge = nil;
77 }
78
79 - (void)continueWithoutCredentialForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
80 {
81     if (challenge == nil || challenge != currentWebChallenge) {
82         return;
83     }
84
85     [[currentConnectionChallenge sender] continueWithoutCredentialForAuthenticationChallenge:currentConnectionChallenge];
86
87     [currentConnectionChallenge release];
88     currentConnectionChallenge = nil;
89     
90     [currentWebChallenge release];
91     currentWebChallenge = nil;
92 }
93
94 - (void)cancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
95 {
96     if (challenge == nil || challenge != currentWebChallenge) {
97         return;
98     }
99
100     [self cancel];
101 }
102
103 @end
104
105 // This declaration is only needed to ease the transition to a new SPI.  It can be removed
106 // moving forward beyond Tiger 8A416.
107 @interface NSURLProtocol (WebFoundationSecret) 
108 + (void)_removePropertyForKey:(NSString *)key inRequest:(NSMutableURLRequest *)request;
109 @end
110
111 @implementation WebBaseResourceHandleDelegate
112
113 + (void)initialize
114 {
115     NSURLConnectionSupportsBufferedData = [NSURLConnection instancesRespondToSelector:@selector(_bufferedData)];
116 }
117
118 - (void)releaseResources
119 {
120     ASSERT(!reachedTerminalState);
121     
122     // It's possible that when we release the handle, it will be
123     // deallocated and release the last reference to this object.
124     // We need to retain to avoid accessing the object after it
125     // has been deallocated and also to avoid reentering this method.
126     
127     [self retain];
128     
129     [identifier release];
130     identifier = nil;
131
132     [connection release];
133     connection = nil;
134
135     [webView release];
136     webView = nil;
137     
138     [dataSource release];
139     dataSource = nil;
140     
141     [resourceLoadDelegate release];
142     resourceLoadDelegate = nil;
143
144     [downloadDelegate release];
145     downloadDelegate = nil;
146     
147     [resource release];
148     resource = nil;
149     
150     [resourceData release];
151     resourceData = nil;
152     
153     reachedTerminalState = YES;
154     
155     [self release];
156 }
157
158 - (void)dealloc
159 {
160     ASSERT(reachedTerminalState);
161     [request release];
162     [response release];
163     [originalURL release];
164     [super dealloc];
165 }
166
167 - (void)deliverResource
168 {
169     ASSERT(resource);
170     ASSERT(waitingToDeliverResource);
171     
172     if (!defersCallbacks) {
173         [self didReceiveResponse:[resource _response]];
174         NSData *data = [resource data];
175         [self didReceiveData:data lengthReceived:[data length]];
176         [self didFinishLoading];
177         deliveredResource = YES;
178         waitingToDeliverResource = NO;
179     }
180 }
181
182 - (void)deliverResourceAfterDelay
183 {
184     if (resource && !defersCallbacks && !waitingToDeliverResource && !deliveredResource) {
185         [self performSelector:@selector(deliverResource) withObject:nil afterDelay:0];
186         waitingToDeliverResource = YES;
187     }
188 }
189
190 // The following 2 methods are copied from [NSHTTPURLProtocol _cachedResponsePassesValidityChecks] and modified for our needs.
191 // FIXME: It would be nice to eventually to share this code somehow.
192 - (BOOL)_canUseResourceForRequest:(NSURLRequest *)theRequest
193 {
194     NSURLRequestCachePolicy policy = [theRequest cachePolicy];
195         
196     if (policy == NSURLRequestReturnCacheDataElseLoad) {
197         return YES;
198     } else if (policy == NSURLRequestReturnCacheDataDontLoad) {
199         return YES;
200     } else if (policy == NSURLRequestReloadIgnoringCacheData) {
201         return NO;
202     } else if ([theRequest valueForHTTPHeaderField:@"must-revalidate"] != nil) {
203         return NO;
204     } else if ([theRequest valueForHTTPHeaderField:@"proxy-revalidate"] != nil) {
205         return NO;
206     } else if ([theRequest valueForHTTPHeaderField:@"If-Modified-Since"] != nil) {
207         return NO;
208     } else if ([theRequest valueForHTTPHeaderField:@"Cache-Control"] != nil) {
209         return NO;
210     } else if ([[theRequest HTTPMethod] _webkit_isCaseInsensitiveEqualToString:@"POST"]) {
211         return NO;
212     } else {
213         return YES;
214     }
215 }
216
217 - (BOOL)_canUseResourceWithResponse:(NSURLResponse *)theResponse
218 {
219     if (WKGetNSURLResponseMustRevalidate(theResponse)) {
220         return NO;
221     } else if (WKGetNSURLResponseCalculatedExpiration(theResponse) - CFAbsoluteTimeGetCurrent() < 1) {
222         return NO;
223     } else {
224         return YES;
225     }
226 }
227
228 - (BOOL)loadWithRequest:(NSURLRequest *)r
229 {
230     ASSERT(connection == nil);
231     ASSERT(resource == nil);
232     
233     NSURL *URL = [[r URL] retain];
234     [originalURL release];
235     originalURL = URL;
236     
237     deliveredResource = NO;
238     waitingToDeliverResource = NO;
239
240     NSURLRequest *clientRequest = [self willSendRequest:r redirectResponse:nil];
241     if (clientRequest == nil) {
242         NSError *badURLError = [NSError _webKitErrorWithDomain:NSURLErrorDomain 
243                                                           code:NSURLErrorCancelled
244                                                            URL:[r URL]];
245         [self didFailWithError:badURLError];
246         return NO;
247     }
248     r = clientRequest;
249     
250     if ([[r URL] isEqual:originalURL] && [self _canUseResourceForRequest:r]) {
251         resource = [dataSource subresourceForURL:originalURL];
252         if (resource != nil) {
253             if ([self _canUseResourceWithResponse:[resource _response]]) {
254                 [resource retain];
255                 // Deliver the resource after a delay because callers don't expect to receive callbacks while calling this method.
256                 [self deliverResourceAfterDelay];
257                 return YES;
258             } else {
259                 resource = nil;
260             }
261         }
262     }
263     
264 #ifndef NDEBUG
265     isInitializingConnection = YES;
266 #endif
267     connection = [[NSURLConnection alloc] initWithRequest:r delegate:self];
268 #ifndef NDEBUG
269     isInitializingConnection = NO;
270 #endif
271     if (defersCallbacks) {
272         [connection setDefersCallbacks:YES];
273     }
274
275     return YES;
276 }
277
278 - (void)setDefersCallbacks:(BOOL)defers
279 {
280     defersCallbacks = defers;
281     [connection setDefersCallbacks:defers];
282     // Deliver the resource after a delay because callers don't expect to receive callbacks while calling this method.
283     [self deliverResourceAfterDelay];
284 }
285
286 - (BOOL)defersCallbacks
287 {
288     return defersCallbacks;
289 }
290
291 - (void)setDataSource:(WebDataSource *)d
292 {
293     ASSERT(d);
294     ASSERT([d _webView]);
295     
296     [d retain];
297     [dataSource release];
298     dataSource = d;
299
300     [webView release];
301     webView = [[dataSource _webView] retain];
302     
303     [resourceLoadDelegate release];
304     resourceLoadDelegate = [[webView resourceLoadDelegate] retain];
305     implementations = [webView _resourceLoadDelegateImplementations];
306
307     [downloadDelegate release];
308     downloadDelegate = [[webView downloadDelegate] retain];
309
310     [self setDefersCallbacks:[webView defersCallbacks]];
311 }
312
313 - (WebDataSource *)dataSource
314 {
315     return dataSource;
316 }
317
318 - resourceLoadDelegate
319 {
320     return resourceLoadDelegate;
321 }
322
323 - downloadDelegate
324 {
325     return downloadDelegate;
326 }
327
328 - (void)addData:(NSData *)data
329 {
330     // Don't buffer data if we're loading it from a WebResource.
331     if (resource == nil) {
332         if (NSURLConnectionSupportsBufferedData) {
333             // Buffer data only if the connection has handed us the data because is has stopped buffering it.
334             if (resourceData != nil) {
335                 [resourceData appendData:data];
336             }
337         } else {
338             if (resourceData == nil) {
339                 resourceData = [[NSMutableData alloc] init];
340             }
341             [resourceData appendData:data];
342         }
343     }
344 }
345
346 - (void)saveResource
347 {
348     // Don't save data as a WebResource if it was loaded from a WebResource.
349     if (resource == nil) {
350         NSData *data = [self resourceData];
351         if ([data length] > 0) {
352             ASSERT(originalURL);
353             ASSERT([response MIMEType]);
354             WebResource *newResource = [[WebResource alloc] _initWithData:data URL:originalURL response:response];
355             if (newResource != nil) {
356                 [dataSource addSubresource:newResource];
357                 [newResource release];
358             } else {
359                 ASSERT_NOT_REACHED();
360             }
361         }
362     }
363 }
364
365 - (NSData *)resourceData
366 {
367     if (resource != nil) {
368         return [resource data];
369     }
370     if (resourceData != nil) {
371         // Retain and autorelease resourceData since releaseResources (which releases resourceData) may be called 
372         // before the caller of this method has an opporuntity to retain the returned data (4070729).
373         return [[resourceData retain] autorelease];
374     }
375     if (NSURLConnectionSupportsBufferedData) {
376         return [connection _bufferedData];
377     }
378     return nil;
379 }
380
381 - (NSURLRequest *)willSendRequest:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse
382 {
383     ASSERT(!reachedTerminalState);
384     NSMutableURLRequest *mutableRequest = [newRequest mutableCopy];
385     NSURLRequest *clientRequest, *updatedRequest;
386     BOOL haveDataSchemeRequest = NO;
387     
388     // retain/release self in this delegate method since the additional processing can do
389     // anything including possibly releasing self; one example of this is 3266216
390     [self retain];
391
392     [mutableRequest _web_setHTTPUserAgent:[webView userAgentForURL:[newRequest URL]]];
393     newRequest = [mutableRequest autorelease];
394
395     clientRequest = [newRequest _webDataRequestExternalRequest];
396     if(!clientRequest)
397         clientRequest = newRequest;
398     else
399         haveDataSchemeRequest = YES;
400     
401     if (identifier == nil) {
402         // The identifier is released after the last callback, rather than in dealloc
403         // to avoid potential cycles.
404         if (implementations.delegateImplementsIdentifierForRequest)
405             identifier = [[resourceLoadDelegate webView: webView identifierForInitialRequest:clientRequest fromDataSource:dataSource] retain];
406         else
407             identifier = [[[WebDefaultResourceLoadDelegate sharedResourceLoadDelegate] webView:webView identifierForInitialRequest:clientRequest fromDataSource:dataSource] retain];
408     }
409
410     // If we have a special "applewebdata" scheme URL we send a fake request to the delegate.
411     if (implementations.delegateImplementsWillSendRequest)
412         updatedRequest = [resourceLoadDelegate webView:webView resource:identifier willSendRequest:clientRequest redirectResponse:redirectResponse fromDataSource:dataSource];
413     else
414         updatedRequest = [[WebDefaultResourceLoadDelegate sharedResourceLoadDelegate] webView:webView resource:identifier willSendRequest:clientRequest redirectResponse:redirectResponse fromDataSource:dataSource];
415         
416     if (!haveDataSchemeRequest)
417         newRequest = updatedRequest;
418     else {
419         // If the delegate modified the request use that instead of
420         // our applewebdata request, otherwise use the original
421         // applewebdata request.
422         if (![updatedRequest isEqual:clientRequest]) {
423             newRequest = updatedRequest;
424         
425             // The respondsToSelector: check is only necessary for people building/running prior to Tier 8A416.
426             if ([NSURLProtocol respondsToSelector:@selector(_removePropertyForKey:inRequest:)] &&
427                 [newRequest isKindOfClass:[NSMutableURLRequest class]]) {
428                 NSMutableURLRequest *mr = (NSMutableURLRequest *)newRequest;
429                 [NSURLProtocol _removePropertyForKey:[NSURLRequest _webDataRequestPropertyKey] inRequest:mr];
430             }
431
432         }
433     }
434
435     // Store a copy of the request.
436     [request autorelease];
437
438     // Client may return a nil request, indicating that the request should be aborted.
439     if (newRequest){
440         request = [newRequest copy];
441     }
442     else {
443         request = nil;
444     }
445
446     [self release];
447     return request;
448 }
449
450 - (void)didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
451 {
452     ASSERT(!reachedTerminalState);
453     ASSERT(!currentConnectionChallenge);
454     ASSERT(!currentWebChallenge);
455
456     // retain/release self in this delegate method since the additional processing can do
457     // anything including possibly releasing self; one example of this is 3266216
458     [self retain];
459     currentConnectionChallenge = [challenge retain];;
460     currentWebChallenge = [[NSURLAuthenticationChallenge alloc] initWithAuthenticationChallenge:challenge sender:self];
461
462     if (implementations.delegateImplementsDidReceiveAuthenticationChallenge) {
463         [resourceLoadDelegate webView:webView resource:identifier didReceiveAuthenticationChallenge:currentWebChallenge fromDataSource:dataSource];
464     } else {
465         [[WebDefaultResourceLoadDelegate sharedResourceLoadDelegate] webView:webView resource:identifier didReceiveAuthenticationChallenge:currentWebChallenge fromDataSource:dataSource];
466     }
467     [self release];
468 }
469
470 - (void)didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
471 {
472     ASSERT(!reachedTerminalState);
473     ASSERT(currentConnectionChallenge);
474     ASSERT(currentWebChallenge);
475     ASSERT(currentConnectionChallenge = challenge);
476
477     // retain/release self in this delegate method since the additional processing can do
478     // anything including possibly releasing self; one example of this is 3266216
479     [self retain];
480     if (implementations.delegateImplementsDidCancelAuthenticationChallenge) {
481         [resourceLoadDelegate webView:webView resource:identifier didCancelAuthenticationChallenge:currentWebChallenge fromDataSource:dataSource];
482     } else {
483         [[WebDefaultResourceLoadDelegate sharedResourceLoadDelegate] webView:webView resource:identifier didCancelAuthenticationChallenge:currentWebChallenge fromDataSource:dataSource];
484     }
485     [self release];
486 }
487
488 - (void)didReceiveResponse:(NSURLResponse *)r
489 {
490     ASSERT(!reachedTerminalState);
491
492     // retain/release self in this delegate method since the additional processing can do
493     // anything including possibly releasing self; one example of this is 3266216
494     [self retain]; 
495
496     // If the URL is one of our whacky applewebdata URLs then
497     // fake up a substitute URL to present to the delegate.
498     if([WebDataProtocol _webIsDataProtocolURL:[r URL]]) {
499         r = [[[NSURLResponse alloc] initWithURL:[request _webDataRequestExternalURL] MIMEType:[r MIMEType] expectedContentLength:[r expectedContentLength] textEncodingName:[r textEncodingName]] autorelease];
500     }
501
502     [r retain];
503     [response release];
504     response = r;
505
506     [dataSource _addResponse: r];
507
508     [webView _incrementProgressForConnectionDelegate:self response:r];
509         
510     if (implementations.delegateImplementsDidReceiveResponse)
511         [resourceLoadDelegate webView:webView resource:identifier didReceiveResponse:r fromDataSource:dataSource];
512     else
513         [[WebDefaultResourceLoadDelegate sharedResourceLoadDelegate] webView:webView resource:identifier didReceiveResponse:r fromDataSource:dataSource];
514     [self release];
515 }
516
517 - (void)didReceiveData:(NSData *)data lengthReceived:(long long)lengthReceived
518 {
519     // The following assertions are not quite valid here, since a subclass
520     // might override didReceiveData: in a way that invalidates them. This
521     // happens with the steps listed in 3266216
522     // ASSERT(con == connection);
523     // ASSERT(!reachedTerminalState);
524
525     // retain/release self in this delegate method since the additional processing can do
526     // anything including possibly releasing self; one example of this is 3266216
527     [self retain];
528     
529     [self addData:data];
530     
531     [webView _incrementProgressForConnectionDelegate:self data:data];
532
533     if (implementations.delegateImplementsDidReceiveContentLength)
534         [resourceLoadDelegate webView:webView resource:identifier didReceiveContentLength:lengthReceived fromDataSource:dataSource];
535     else
536         [[WebDefaultResourceLoadDelegate sharedResourceLoadDelegate] webView:webView resource:identifier didReceiveContentLength:lengthReceived fromDataSource:dataSource];
537     [self release];
538 }
539
540 - (void)willStopBufferingData:(NSData *)data
541 {
542     ASSERT(resourceData == nil);
543     resourceData = [data mutableCopy];
544 }
545
546 - (void)didFinishLoading
547 {
548     // If load has been cancelled after finishing (which could happen with a 
549     // javascript that changes the window location), do nothing.
550     if (cancelledFlag) {
551         return;
552     }
553     
554     ASSERT(!reachedTerminalState);
555
556     [self saveResource];
557     
558     [webView _completeProgressForConnectionDelegate:self];
559
560     if (implementations.delegateImplementsDidFinishLoadingFromDataSource)
561         [resourceLoadDelegate webView:webView resource:identifier didFinishLoadingFromDataSource:dataSource];
562     else
563         [[WebDefaultResourceLoadDelegate sharedResourceLoadDelegate] webView:webView resource:identifier didFinishLoadingFromDataSource:dataSource];
564
565     [self releaseResources];
566 }
567
568 - (void)didFailWithError:(NSError *)error
569 {
570     ASSERT(!reachedTerminalState);
571
572     // retain/release self in this delegate method since the additional processing can do
573     // anything including possibly releasing self; one example of this is 3266216
574     [self retain];
575     [webView _completeProgressForConnectionDelegate:self];
576
577     [[webView _resourceLoadDelegateForwarder] webView:webView resource:identifier didFailLoadingWithError:error fromDataSource:dataSource];
578
579     [self releaseResources];
580     [self release];
581 }
582
583 - (NSCachedURLResponse *)willCacheResponse:(NSCachedURLResponse *)cachedResponse
584 {
585     // When in private browsing mode, prevent caching to disk
586     if ([cachedResponse storagePolicy] == NSURLCacheStorageAllowed && [[webView preferences] privateBrowsingEnabled]) {
587         cachedResponse = [[[NSCachedURLResponse alloc] initWithResponse:[cachedResponse response]
588                                                                    data:[cachedResponse data]
589                                                                userInfo:[cachedResponse userInfo]
590                                                           storagePolicy:NSURLCacheStorageAllowedInMemoryOnly] autorelease];
591     }
592     return cachedResponse;
593 }
594
595 - (NSURLRequest *)connection:(NSURLConnection *)con willSendRequest:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse
596 {
597     ASSERT(con == connection);
598     ++inNSURLConnectionCallback;
599     NSURLRequest *result = [self willSendRequest:newRequest redirectResponse:redirectResponse];
600     --inNSURLConnectionCallback;
601     return result;
602 }
603
604 - (void)connection:(NSURLConnection *)con didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
605 {
606     ASSERT(con == connection);
607     ++inNSURLConnectionCallback;
608     [self didReceiveAuthenticationChallenge:challenge];
609     --inNSURLConnectionCallback;
610 }
611
612 - (void)connection:(NSURLConnection *)con didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
613 {
614     ASSERT(con == connection);
615     ++inNSURLConnectionCallback;
616     [self didCancelAuthenticationChallenge:challenge];
617     --inNSURLConnectionCallback;
618 }
619
620 - (void)connection:(NSURLConnection *)con didReceiveResponse:(NSURLResponse *)r
621 {
622     ASSERT(con == connection);
623     ++inNSURLConnectionCallback;
624     [self didReceiveResponse:r];
625     --inNSURLConnectionCallback;
626 }
627
628 - (void)connection:(NSURLConnection *)con didReceiveData:(NSData *)data lengthReceived:(long long)lengthReceived
629 {
630     ASSERT(con == connection);
631     ++inNSURLConnectionCallback;
632     [self didReceiveData:data lengthReceived:lengthReceived];
633     --inNSURLConnectionCallback;
634 }
635
636 - (void)connection:(NSURLConnection *)con willStopBufferingData:(NSData *)data
637 {
638     ASSERT(con == connection);
639     ++inNSURLConnectionCallback;
640     [self willStopBufferingData:data];
641     --inNSURLConnectionCallback;
642 }
643
644 - (void)connectionDidFinishLoading:(NSURLConnection *)con
645 {
646     // don't worry about checking connection consistency if this load
647     // got cancelled while finishing.
648     ASSERT(cancelledFlag || con == connection);
649     ++inNSURLConnectionCallback;
650     [self didFinishLoading];
651     --inNSURLConnectionCallback;
652 }
653
654 - (void)connection:(NSURLConnection *)con didFailWithError:(NSError *)error
655 {
656     ASSERT(con == connection);
657     ++inNSURLConnectionCallback;
658     [self didFailWithError:error];
659     --inNSURLConnectionCallback;
660 }
661
662 - (NSCachedURLResponse *)connection:(NSURLConnection *)con willCacheResponse:(NSCachedURLResponse *)cachedResponse
663 {
664 #ifndef NDEBUG
665     if (connection == nil && isInitializingConnection) {
666         ERROR("connection:willCacheResponse: was called inside of [NSURLConnection initWithRequest:delegate:] (40676250)");
667     }
668 #endif
669     ++inNSURLConnectionCallback;
670     NSCachedURLResponse *result = [self willCacheResponse:cachedResponse];
671     --inNSURLConnectionCallback;
672     return result;
673 }
674
675 - (void)cancelWithError:(NSError *)error
676 {
677     ASSERT(!reachedTerminalState);
678
679     // This flag prevents bad behvior when loads that finish cause the
680     // load itself to be cancelled (which could happen with a javascript that 
681     // changes the window location). This is used to prevent both the body
682     // of this method and the body of connectionDidFinishLoading: running
683     // for a single delegate. Cancelling wins.
684     cancelledFlag = YES;
685     
686     [currentConnectionChallenge release];
687     currentConnectionChallenge = nil;
688     
689     [currentWebChallenge release];
690     currentWebChallenge = nil;
691
692     [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(deliverResource) object:nil];
693     [connection cancel];
694
695     [webView _completeProgressForConnectionDelegate:self];
696
697     if (error) {
698         [[webView _resourceLoadDelegateForwarder] webView:webView resource:identifier didFailLoadingWithError:error fromDataSource:dataSource];
699     }
700
701     [self releaseResources];
702 }
703
704 - (void)cancel
705 {
706     if (!reachedTerminalState) {
707         [self cancelWithError:[self cancelledError]];
708     }
709 }
710
711 - (NSError *)cancelledError
712 {
713     return [NSError _webKitErrorWithDomain:NSURLErrorDomain
714                                       code:NSURLErrorCancelled
715                                        URL:[request URL]];
716 }
717
718 - (void)setIdentifier: ident
719 {
720     if (identifier != ident){
721         [identifier release];
722         identifier = [ident retain];
723     }
724 }
725
726 - (NSURLResponse *)response
727 {
728     return response;
729 }
730
731 + (BOOL)inConnectionCallback
732 {
733     return inNSURLConnectionCallback != 0;
734 }
735
736 @end