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