dabd32f5c9abbe49e8e54fd02f563f7c7a1fe8d7
[WebKit-https.git] / WebCore / platform / network / mac / ResourceHandleMac.mm
1 /*
2  * Copyright (C) 2004, 2006 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  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #import "config.h"
27 #import "ResourceHandle.h"
28 #import "ResourceHandleInternal.h"
29
30 #import "AuthenticationMac.h"
31 #import "BlockExceptions.h"
32 #import "DocLoader.h"
33 #import "Frame.h"
34 #import "FrameLoader.h"
35 #import "ResourceError.h"
36 #import "ResourceResponse.h"
37 #import "SharedBuffer.h"
38 #import "SubresourceLoader.h"
39 #import "AuthenticationChallenge.h"
40 #import "WebCoreSystemInterface.h"
41
42 using namespace WebCore;
43
44 @interface WebCoreResourceHandleAsDelegate : NSObject <NSURLAuthenticationChallengeSender>
45 {
46     ResourceHandle* m_handle;
47 #ifndef BUILDING_ON_TIGER
48     NSURL *m_url;
49 #endif
50 }
51 - (id)initWithHandle:(ResourceHandle*)handle;
52 - (void)detachHandle;
53 @end
54
55 @interface NSURLConnection (NSURLConnectionTigerPrivate)
56 - (NSData *)_bufferedData;
57 @end
58
59 @interface NSURLProtocol (WebFoundationSecret) 
60 + (void)_removePropertyForKey:(NSString *)key inRequest:(NSMutableURLRequest *)request;
61 @end
62
63 #ifndef BUILDING_ON_TIGER
64 @interface WebCoreSynchronousLoader : NSObject {
65     NSURL *m_url;
66     NSURLResponse *m_response;
67     NSMutableData *m_data;
68     NSError *m_error;
69     BOOL m_isDone;
70 }
71 + (NSData *)loadRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error;
72 @end
73
74 static NSString *WebCoreSynchronousLoaderRunLoopMode = @"WebCoreSynchronousLoaderRunLoopMode";
75 #endif
76
77 namespace WebCore {
78    
79 static unsigned inNSURLConnectionCallback;
80 static bool NSURLConnectionSupportsBufferedData;
81     
82 #ifndef NDEBUG
83 static bool isInitializingConnection;
84 #endif
85     
86 ResourceHandleInternal::~ResourceHandleInternal()
87 {
88 }
89
90 ResourceHandle::~ResourceHandle()
91 {
92     releaseDelegate();
93 }
94
95 bool ResourceHandle::start(Frame* frame)
96 {
97     if (!frame)
98         return false;
99
100     BEGIN_BLOCK_OBJC_EXCEPTIONS;
101
102     // If we are no longer attached to a Page, this must be an attempted load from an
103     // onUnload handler, so let's just block it.
104     if (!frame->page())
105         return false;
106   
107 #ifndef NDEBUG
108     isInitializingConnection = YES;
109 #endif
110     id delegate;
111     
112     if (d->m_mightDownloadFromHandle) {
113         ASSERT(!d->m_proxy);
114         d->m_proxy = wkCreateNSURLConnectionDelegateProxy();
115         [d->m_proxy.get() setDelegate:ResourceHandle::delegate()];
116         [d->m_proxy.get() release];
117         
118         delegate = d->m_proxy.get();
119     } else 
120         delegate = ResourceHandle::delegate();
121     
122     NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:d->m_request.nsURLRequest() delegate:delegate];
123 #ifndef NDEBUG
124     isInitializingConnection = NO;
125 #endif
126     d->m_connection = connection;
127     [connection release];
128     if (d->m_defersLoading)
129         wkSetNSURLConnectionDefersCallbacks(d->m_connection.get(), YES);
130     
131     if (d->m_connection)
132         return true;
133
134     END_BLOCK_OBJC_EXCEPTIONS;
135
136     return false;
137 }
138
139 void ResourceHandle::cancel()
140 {
141     [d->m_connection.get() cancel];
142 }
143
144 void ResourceHandle::setDefersLoading(bool defers)
145 {
146     d->m_defersLoading = defers;
147     wkSetNSURLConnectionDefersCallbacks(d->m_connection.get(), defers);
148 }
149
150 WebCoreResourceHandleAsDelegate *ResourceHandle::delegate()
151 {
152     if (!d->m_delegate) {
153         WebCoreResourceHandleAsDelegate *delegate = [[WebCoreResourceHandleAsDelegate alloc] initWithHandle:this];
154         d->m_delegate = delegate;
155         [delegate release];
156     }
157     return d->m_delegate.get();
158 }
159
160 void ResourceHandle::releaseDelegate()
161 {
162     if (!d->m_delegate)
163         return;
164     if (d->m_proxy)
165         [d->m_proxy.get() setDelegate:nil];
166     [d->m_delegate.get() detachHandle];
167     d->m_delegate = nil;
168 }
169
170 bool ResourceHandle::supportsBufferedData()
171 {
172     static bool initialized = false;
173     if (!initialized) {
174         NSURLConnectionSupportsBufferedData = [NSURLConnection instancesRespondToSelector:@selector(_bufferedData)];
175         initialized = true;
176     }
177
178     return NSURLConnectionSupportsBufferedData;
179 }
180
181 PassRefPtr<SharedBuffer> ResourceHandle::bufferedData()
182 {
183     if (ResourceHandle::supportsBufferedData())
184         return SharedBuffer::wrapNSData([d->m_connection.get() _bufferedData]);
185
186     return 0;
187 }
188
189 id ResourceHandle::releaseProxy()
190 {
191     id proxy = [[d->m_proxy.get() retain] autorelease];
192     d->m_proxy = nil;
193     [proxy setDelegate:nil];
194     return proxy;
195 }
196
197 NSURLConnection *ResourceHandle::connection() const
198 {
199     return d->m_connection.get();
200 }
201
202 bool ResourceHandle::loadsBlocked()
203 {
204     return inNSURLConnectionCallback != 0;
205 }
206
207 bool ResourceHandle::willLoadFromCache(ResourceRequest& request)
208 {
209     request.setCachePolicy(ReturnCacheDataDontLoad);
210     NSURLResponse *nsURLResponse = nil;
211     BEGIN_BLOCK_OBJC_EXCEPTIONS;
212     
213    [NSURLConnection sendSynchronousRequest:request.nsURLRequest() returningResponse:&nsURLResponse error:nil];
214     
215     END_BLOCK_OBJC_EXCEPTIONS;
216     
217     return nsURLResponse;
218 }
219
220 void ResourceHandle::loadResourceSynchronously(const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data)
221 {
222     NSError *nsError = nil;
223     
224     NSURLResponse *nsURLResponse = nil;
225     NSData *result = nil;
226
227     ASSERT(!request.isEmpty());
228     
229     BEGIN_BLOCK_OBJC_EXCEPTIONS;
230     
231 #ifndef BUILDING_ON_TIGER
232     result = [WebCoreSynchronousLoader loadRequest:request.nsURLRequest() returningResponse:&nsURLResponse error:&nsError];
233 #else
234     result = [NSURLConnection sendSynchronousRequest:request.nsURLRequest() returningResponse:&nsURLResponse error:&nsError];
235 #endif
236     END_BLOCK_OBJC_EXCEPTIONS;
237
238     if (nsError == nil)
239         response = nsURLResponse;
240     else {
241         response = ResourceResponse(request.url(), String(), 0, String(), String());
242         if ([nsError domain] == NSURLErrorDomain)
243             switch ([nsError code]) {
244                 case NSURLErrorUserCancelledAuthentication:
245                     // FIXME: we should really return the actual HTTP response, but sendSynchronousRequest doesn't provide us with one.
246                     response.setHTTPStatusCode(401);
247                     break;
248                 default:
249                     response.setHTTPStatusCode([nsError code]);
250             }
251         else
252             response.setHTTPStatusCode(404);
253     }
254     
255     data.resize([result length]);
256     memcpy(data.data(), [result bytes], [result length]);
257     
258     error = nsError;
259 }
260
261 void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge)
262 {
263     ASSERT(!d->m_currentMacChallenge);
264     ASSERT(d->m_currentWebChallenge.isNull());
265     // Since NSURLConnection networking relies on keeping a reference to the original NSURLAuthenticationChallenge,
266     // we make sure that is actually present
267     ASSERT(challenge.nsURLAuthenticationChallenge());
268         
269     d->m_currentMacChallenge = challenge.nsURLAuthenticationChallenge();
270     NSURLAuthenticationChallenge *webChallenge = [[NSURLAuthenticationChallenge alloc] initWithAuthenticationChallenge:d->m_currentMacChallenge 
271                                                                                        sender:(id<NSURLAuthenticationChallengeSender>)delegate()];
272     d->m_currentWebChallenge = core(webChallenge);
273     [webChallenge release];
274
275     if (client())
276         client()->didReceiveAuthenticationChallenge(this, d->m_currentWebChallenge);
277 }
278
279 void ResourceHandle::didCancelAuthenticationChallenge(const AuthenticationChallenge& challenge)
280 {
281     ASSERT(d->m_currentMacChallenge);
282     ASSERT(!d->m_currentWebChallenge.isNull());
283     ASSERT(d->m_currentWebChallenge == challenge);
284
285     if (client())
286         client()->didCancelAuthenticationChallenge(this, d->m_currentWebChallenge);
287 }
288
289 void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge, const Credential& credential)
290 {
291     ASSERT(!challenge.isNull());
292     if (challenge != d->m_currentWebChallenge)
293         return;
294
295     [[d->m_currentMacChallenge sender] useCredential:mac(credential) forAuthenticationChallenge:d->m_currentMacChallenge];
296
297     clearAuthentication();
298 }
299
300 void ResourceHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge& challenge)
301 {
302     ASSERT(!challenge.isNull());
303     if (challenge != d->m_currentWebChallenge)
304         return;
305
306     [[d->m_currentMacChallenge sender] continueWithoutCredentialForAuthenticationChallenge:d->m_currentMacChallenge];
307
308     clearAuthentication();
309 }
310
311 void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challenge)
312 {
313     if (challenge != d->m_currentWebChallenge)
314         return;
315
316     if (client())
317         client()->receivedCancellation(this, challenge);
318 }
319
320 } // namespace WebCore
321
322 @implementation WebCoreResourceHandleAsDelegate
323
324 - (id)initWithHandle:(ResourceHandle*)handle
325 {
326     self = [self init];
327     if (!self)
328         return nil;
329     m_handle = handle;
330     return self;
331 }
332
333 #ifndef BUILDING_ON_TIGER
334 - (void)dealloc
335 {
336     [m_url release];
337     [super dealloc];
338 }
339 #endif
340
341 - (void)detachHandle
342 {
343     m_handle = 0;
344 }
345
346 - (NSURLRequest *)connection:(NSURLConnection *)con willSendRequest:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse
347 {
348     if (!m_handle || !m_handle->client())
349         return nil;
350     ++inNSURLConnectionCallback;
351     ResourceRequest request = newRequest;
352     m_handle->client()->willSendRequest(m_handle, request, redirectResponse);
353     --inNSURLConnectionCallback;
354 #ifndef BUILDING_ON_TIGER
355     [m_url release];
356     m_url = [[request.nsURLRequest() URL] copy];    
357 #endif
358     
359     return request.nsURLRequest();
360 }
361
362 - (void)connection:(NSURLConnection *)con didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
363 {
364 #ifndef BUILDING_ON_TIGER
365     if ([challenge previousFailureCount] == 0) {
366         NSString *user = [m_url user];
367         NSString *password = [m_url password];
368
369         if (user && password) {
370             NSURLCredential *credential = [NSURLCredential credentialWithUser:user
371                                                                      password:password
372                                                                   persistence:NSURLCredentialPersistenceForSession];
373             [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
374             return;
375         }
376     }
377 #endif
378     
379     if (!m_handle)
380         return;
381     ++inNSURLConnectionCallback;
382     m_handle->didReceiveAuthenticationChallenge(core(challenge));
383     --inNSURLConnectionCallback;
384 }
385
386 - (void)connection:(NSURLConnection *)con didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
387 {
388     if (!m_handle)
389         return;
390     ++inNSURLConnectionCallback;
391     m_handle->didCancelAuthenticationChallenge(core(challenge));
392     --inNSURLConnectionCallback;
393 }
394
395 - (void)connection:(NSURLConnection *)con didReceiveResponse:(NSURLResponse *)r
396 {
397     if (!m_handle || !m_handle->client())
398         return;
399     ++inNSURLConnectionCallback;
400     m_handle->client()->didReceiveResponse(m_handle, r);
401     --inNSURLConnectionCallback;
402 }
403
404 - (void)connection:(NSURLConnection *)con didReceiveData:(NSData *)data lengthReceived:(long long)lengthReceived
405 {
406     if (!m_handle || !m_handle->client())
407         return;
408     // FIXME: If we get more than 2B bytes in a single chunk, this code won't do the right thing.
409     // However, with today's computers and networking speeds, this won't happen in practice.
410     // Could be an issue with a giant local file.
411     ++inNSURLConnectionCallback;
412     m_handle->client()->didReceiveData(m_handle, (const char*)[data bytes], [data length], static_cast<int>(lengthReceived));
413     --inNSURLConnectionCallback;
414 }
415
416 - (void)connection:(NSURLConnection *)con willStopBufferingData:(NSData *)data
417 {
418     if (!m_handle || !m_handle->client())
419         return;
420     // FIXME: If we get a resource with more than 2B bytes, this code won't do the right thing.
421     // However, with today's computers and networking speeds, this won't happen in practice.
422     // Could be an issue with a giant local file.
423     ++inNSURLConnectionCallback;
424     m_handle->client()->willStopBufferingData(m_handle, (const char*)[data bytes], static_cast<int>([data length]));
425     --inNSURLConnectionCallback;
426 }
427
428 - (void)connectionDidFinishLoading:(NSURLConnection *)con
429 {
430     if (!m_handle || !m_handle->client())
431         return;
432     ++inNSURLConnectionCallback;
433     m_handle->client()->didFinishLoading(m_handle);
434     --inNSURLConnectionCallback;
435 }
436
437 - (void)connection:(NSURLConnection *)con didFailWithError:(NSError *)error
438 {
439     if (!m_handle || !m_handle->client())
440         return;
441     ++inNSURLConnectionCallback;
442     m_handle->client()->didFail(m_handle, error);
443     --inNSURLConnectionCallback;
444 }
445
446 - (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse
447 {
448 #ifndef NDEBUG
449     if (isInitializingConnection)
450         LOG_ERROR("connection:willCacheResponse: was called inside of [NSURLConnection initWithRequest:delegate:] (4067625)");
451 #endif
452     if (!m_handle || !m_handle->client())
453         return nil;
454     ++inNSURLConnectionCallback;
455     
456     NSCachedURLResponse * newResponse = m_handle->client()->willCacheResponse(m_handle, cachedResponse);
457     if (newResponse != cachedResponse)
458         return newResponse;
459     
460     CacheStoragePolicy policy = static_cast<CacheStoragePolicy>([newResponse storagePolicy]);
461         
462     m_handle->client()->willCacheResponse(m_handle, policy);
463
464     if (static_cast<NSURLCacheStoragePolicy>(policy) != [newResponse storagePolicy])
465         newResponse = [[[NSCachedURLResponse alloc] initWithResponse:[newResponse response]
466                                                                    data:[newResponse data]
467                                                                userInfo:[newResponse userInfo]
468                                                           storagePolicy:static_cast<NSURLCacheStoragePolicy>(policy)] autorelease];
469
470     --inNSURLConnectionCallback;
471     return newResponse;
472 }
473
474 - (void)useCredential:(NSURLCredential *)credential forAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
475 {
476     if (!m_handle)
477         return;
478     m_handle->receivedCredential(core(challenge), core(credential));
479 }
480
481 - (void)continueWithoutCredentialForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
482 {
483     if (!m_handle)
484         return;
485     m_handle->receivedRequestToContinueWithoutCredential(core(challenge));
486 }
487
488 - (void)cancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
489 {
490     if (!m_handle)
491         return;
492     m_handle->receivedCancellation(core(challenge));
493 }
494
495 @end
496
497 #ifndef BUILDING_ON_TIGER
498 @implementation WebCoreSynchronousLoader
499
500 - (BOOL)_isDone
501 {
502     return m_isDone;
503 }
504
505 - (void)dealloc
506 {
507     [m_url release];
508     [m_response release];
509     [m_data release];
510     [m_error release];
511     
512     [super dealloc];
513 }
514
515 - (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse
516 {
517     [m_url release];
518     m_url = [[newRequest URL] copy];
519
520     return newRequest;
521 }
522
523 - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
524 {
525     if ([challenge previousFailureCount] == 0) {
526         NSString *user = [m_url user];
527         NSString *password = [m_url password];
528         
529         if (user && password) {
530             NSURLCredential *credential = [NSURLCredential credentialWithUser:user
531                                                                      password:password
532                                                                   persistence:NSURLCredentialPersistenceForSession];
533             [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
534             return;
535         }
536     }
537     
538     [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
539 }
540
541 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
542 {
543     NSURLResponse *r = [response copy];
544     
545     [m_response release];
546     m_response = r;
547 }
548
549 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
550 {
551     if (!m_data)
552         m_data = [[NSMutableData alloc] init];
553     
554     [m_data appendData:data];
555 }
556
557 - (void)connectionDidFinishLoading:(NSURLConnection *)connection
558 {
559     m_isDone = YES;
560 }
561
562 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
563 {
564     ASSERT(!m_error);
565     
566     m_error = [error retain];
567     m_isDone = YES;
568 }
569
570 - (NSData *)_data
571 {
572     return [[m_data retain] autorelease];
573 }
574
575 - (NSURLResponse *)_response
576 {
577     return [[m_response retain] autorelease];
578 }
579
580 - (NSError *)_error
581 {
582     return [[m_error retain] autorelease];
583 }
584
585 + (NSData *)loadRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error
586 {
587     WebCoreSynchronousLoader *delegate = [[WebCoreSynchronousLoader alloc] init];
588     
589     NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:delegate startImmediately:NO];
590     [connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:WebCoreSynchronousLoaderRunLoopMode];
591     [connection start];
592     
593     while (![delegate _isDone])
594         [[NSRunLoop currentRunLoop] runMode:WebCoreSynchronousLoaderRunLoopMode beforeDate:[NSDate distantFuture]];
595
596     NSData *data = [delegate _data];
597     *response = [delegate _response];
598     *error = [delegate _error];
599     
600     [connection cancel];
601     
602     [connection release];
603     [delegate release];
604     
605     return data;
606 }
607
608 @end
609 #endif