dc16e23186f3d1be592a3c1b3d43eca1ccde37e8
[WebKit.git] / WebCore / platform / network / mac / ResourceHandleMac.mm
1 /*
2  * Copyright (C) 2004, 2006 Apple 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 "ResourceHandleInternal.h"
28
29 #import "AuthenticationChallenge.h"
30 #import "AuthenticationMac.h"
31 #import "BlockExceptions.h"
32 #import "DocLoader.h"
33 #import "FormDataStreamMac.h"
34 #import "Frame.h"
35 #import "FrameLoader.h"
36 #import "Logging.h"
37 #import "MIMETypeRegistry.h"
38 #import "Page.h"
39 #import "ResourceError.h"
40 #import "ResourceResponse.h"
41 #import "SchedulePair.h"
42 #import "Settings.h"
43 #import "SharedBuffer.h"
44 #import "SubresourceLoader.h"
45 #import "WebCoreSystemInterface.h"
46 #import "WebCoreURLResponse.h"
47 #import <wtf/UnusedParam.h>
48
49 #ifdef BUILDING_ON_TIGER
50 typedef int NSInteger;
51 #endif
52
53 using namespace WebCore;
54
55 @interface WebCoreResourceHandleAsDelegate : NSObject <NSURLAuthenticationChallengeSender>
56 {
57     ResourceHandle* m_handle;
58 }
59 - (id)initWithHandle:(ResourceHandle*)handle;
60 - (void)detachHandle;
61 @end
62
63 @interface NSURLConnection (NSURLConnectionTigerPrivate)
64 - (NSData *)_bufferedData;
65 @end
66
67 @interface NSURLResponse (Details)
68 - (void)_setMIMEType:(NSString *)type;
69 @end
70
71 @interface NSURLRequest (Details)
72 - (id)_propertyForKey:(NSString *)key;
73 @end
74
75 #ifndef BUILDING_ON_TIGER
76
77 @interface WebCoreSynchronousLoader : NSObject {
78     NSURL *m_url;
79     NSString *m_user;
80     NSString *m_pass;
81     BOOL m_allowStoredCredentials;
82     NSURLResponse *m_response;
83     NSMutableData *m_data;
84     NSError *m_error;
85     BOOL m_isDone;
86 }
87 + (NSData *)loadRequest:(NSURLRequest *)request allowStoredCredentials:(BOOL)allowStoredCredentials returningResponse:(NSURLResponse **)response error:(NSError **)error;
88 @end
89
90 static NSString *WebCoreSynchronousLoaderRunLoopMode = @"WebCoreSynchronousLoaderRunLoopMode";
91
92 #endif
93
94 namespace WebCore {
95
96 #ifdef BUILDING_ON_TIGER
97 static unsigned inNSURLConnectionCallback;
98 #endif
99
100 #ifndef NDEBUG
101 static bool isInitializingConnection;
102 #endif
103     
104 class CallbackGuard {
105 public:
106     CallbackGuard()
107     {
108 #ifdef BUILDING_ON_TIGER
109         ++inNSURLConnectionCallback;
110 #endif
111     }
112     ~CallbackGuard()
113     {
114 #ifdef BUILDING_ON_TIGER
115         ASSERT(inNSURLConnectionCallback > 0);
116         --inNSURLConnectionCallback;
117 #endif
118     }
119 };
120
121 ResourceHandleInternal::~ResourceHandleInternal()
122 {
123 }
124
125 ResourceHandle::~ResourceHandle()
126 {
127     releaseDelegate();
128
129     LOG(Network, "Handle %p destroyed", this);
130 }
131
132 static const double MaxFoundationVersionWithoutdidSendBodyDataDelegate = 677.21;
133 bool ResourceHandle::didSendBodyDataDelegateExists()
134 {
135     return NSFoundationVersionNumber > MaxFoundationVersionWithoutdidSendBodyDataDelegate;
136 }
137
138 bool ResourceHandle::start(Frame* frame)
139 {
140     if (!frame)
141         return false;
142
143     BEGIN_BLOCK_OBJC_EXCEPTIONS;
144
145     // If we are no longer attached to a Page, this must be an attempted load from an
146     // onUnload handler, so let's just block it.
147     Page* page = frame->page();
148     if (!page)
149         return false;
150
151 #ifndef NDEBUG
152     isInitializingConnection = YES;
153 #endif
154
155     id delegate;
156     
157     if (d->m_mightDownloadFromHandle) {
158         ASSERT(!d->m_proxy);
159         d->m_proxy = wkCreateNSURLConnectionDelegateProxy();
160         [d->m_proxy.get() setDelegate:ResourceHandle::delegate()];
161         [d->m_proxy.get() release];
162         
163         delegate = d->m_proxy.get();
164     } else 
165         delegate = ResourceHandle::delegate();
166
167     if ((!d->m_user.isEmpty() || !d->m_pass.isEmpty()) && !d->m_request.url().protocolInHTTPFamily()) {
168         // Credentials for ftp can only be passed in URL, the connection:didReceiveAuthenticationChallenge: delegate call won't be made.
169         KURL urlWithCredentials(d->m_request.url());
170         urlWithCredentials.setUser(d->m_user);
171         urlWithCredentials.setPass(d->m_pass);
172         d->m_request.setURL(urlWithCredentials);
173     }
174
175     if (!ResourceHandle::didSendBodyDataDelegateExists())
176         associateStreamWithResourceHandle([d->m_request.nsURLRequest() HTTPBodyStream], this);
177
178 #ifdef BUILDING_ON_TIGER
179     // A conditional request sent by WebCore (e.g. to update appcache) can be for a resource that is not cacheable by NSURLConnection,
180     // which can get confused and fail to load it in this case.
181     if (d->m_request.isConditional())
182         d->m_request.setCachePolicy(ReloadIgnoringCacheData);
183 #endif
184
185     d->m_needsSiteSpecificQuirks = frame->settings() && frame->settings()->needsSiteSpecificQuirks();
186
187     NSURLConnection *connection;
188     
189     if (d->m_shouldContentSniff || frame->settings()->localFileContentSniffingEnabled()) 
190 #ifdef BUILDING_ON_TIGER
191         connection = [[NSURLConnection alloc] initWithRequest:d->m_request.nsURLRequest() delegate:delegate];
192 #else
193         connection = [[NSURLConnection alloc] initWithRequest:d->m_request.nsURLRequest() delegate:delegate startImmediately:NO];
194 #endif
195     else {
196         NSMutableURLRequest *request = [d->m_request.nsURLRequest() mutableCopy];
197         wkSetNSURLRequestShouldContentSniff(request, NO);
198 #ifdef BUILDING_ON_TIGER
199         connection = [[NSURLConnection alloc] initWithRequest:request delegate:delegate];
200 #else
201         connection = [[NSURLConnection alloc] initWithRequest:request delegate:delegate startImmediately:NO];
202 #endif
203         [request release];
204     }
205
206 #ifndef BUILDING_ON_TIGER
207     bool scheduled = false;
208     if (SchedulePairHashSet* scheduledPairs = page->scheduledRunLoopPairs()) {
209         SchedulePairHashSet::iterator end = scheduledPairs->end();
210         for (SchedulePairHashSet::iterator it = scheduledPairs->begin(); it != end; ++it) {
211             if (NSRunLoop *runLoop = (*it)->nsRunLoop()) {
212                 [connection scheduleInRunLoop:runLoop forMode:(NSString *)(*it)->mode()];
213                 scheduled = true;
214             }
215         }
216     }
217
218     // Start the connection if we did schedule with at least one runloop.
219     // We can't start the connection until we have one runloop scheduled.
220     if (scheduled)
221         [connection start];
222     else
223         d->m_startWhenScheduled = true;
224 #endif
225
226 #ifndef NDEBUG
227     isInitializingConnection = NO;
228 #endif
229
230     LOG(Network, "Handle %p starting connection %p for %@", this, connection, d->m_request.nsURLRequest());
231     
232     d->m_connection = connection;
233
234     if (d->m_connection) {
235         [connection release];
236
237         if (d->m_defersLoading)
238             wkSetNSURLConnectionDefersCallbacks(d->m_connection.get(), YES);
239
240         return true;
241     }
242
243     END_BLOCK_OBJC_EXCEPTIONS;
244
245     return false;
246 }
247
248 void ResourceHandle::cancel()
249 {
250     LOG(Network, "Handle %p cancel connection %p", this, d->m_connection.get());
251
252     if (!ResourceHandle::didSendBodyDataDelegateExists())
253         disassociateStreamWithResourceHandle([d->m_request.nsURLRequest() HTTPBodyStream]);
254     [d->m_connection.get() cancel];
255 }
256
257 void ResourceHandle::setDefersLoading(bool defers)
258 {
259     LOG(Network, "Handle %p setDefersLoading(%s)", this, defers ? "true" : "false");
260
261     d->m_defersLoading = defers;
262     if (d->m_connection)
263         wkSetNSURLConnectionDefersCallbacks(d->m_connection.get(), defers);
264 }
265
266 void ResourceHandle::schedule(SchedulePair* pair)
267 {
268 #ifndef BUILDING_ON_TIGER
269     NSRunLoop *runLoop = pair->nsRunLoop();
270     if (!runLoop)
271         return;
272     [d->m_connection.get() scheduleInRunLoop:runLoop forMode:(NSString *)pair->mode()];
273     if (d->m_startWhenScheduled) {
274         [d->m_connection.get() start];
275         d->m_startWhenScheduled = false;
276     }
277 #else
278     UNUSED_PARAM(pair);
279 #endif
280 }
281
282 void ResourceHandle::unschedule(SchedulePair* pair)
283 {
284 #ifndef BUILDING_ON_TIGER
285     if (NSRunLoop *runLoop = pair->nsRunLoop())
286         [d->m_connection.get() unscheduleFromRunLoop:runLoop forMode:(NSString *)pair->mode()];
287 #else
288     UNUSED_PARAM(pair);
289 #endif
290 }
291
292 WebCoreResourceHandleAsDelegate *ResourceHandle::delegate()
293 {
294     if (!d->m_delegate) {
295         WebCoreResourceHandleAsDelegate *delegate = [[WebCoreResourceHandleAsDelegate alloc] initWithHandle:this];
296         d->m_delegate = delegate;
297         [delegate release];
298     }
299     return d->m_delegate.get();
300 }
301
302 void ResourceHandle::releaseDelegate()
303 {
304     if (!d->m_delegate)
305         return;
306     if (d->m_proxy)
307         [d->m_proxy.get() setDelegate:nil];
308     [d->m_delegate.get() detachHandle];
309     d->m_delegate = nil;
310 }
311
312 bool ResourceHandle::supportsBufferedData()
313 {
314     static bool supportsBufferedData = [NSURLConnection instancesRespondToSelector:@selector(_bufferedData)];
315     return supportsBufferedData;
316 }
317
318 PassRefPtr<SharedBuffer> ResourceHandle::bufferedData()
319 {
320     if (ResourceHandle::supportsBufferedData())
321         return SharedBuffer::wrapNSData([d->m_connection.get() _bufferedData]);
322
323     return 0;
324 }
325
326 id ResourceHandle::releaseProxy()
327 {
328     id proxy = [[d->m_proxy.get() retain] autorelease];
329     d->m_proxy = nil;
330     [proxy setDelegate:nil];
331     return proxy;
332 }
333
334 NSURLConnection *ResourceHandle::connection() const
335 {
336     return d->m_connection.get();
337 }
338
339 bool ResourceHandle::loadsBlocked()
340 {
341 #ifndef BUILDING_ON_TIGER
342     return false;
343 #else
344     // On Tiger, if we're in an NSURLConnection callback, that blocks all other NSURLConnection callbacks.
345     // On Leopard and newer, it blocks only callbacks on that same NSURLConnection object, which is not
346     // a problem in practice.
347     return inNSURLConnectionCallback != 0;
348 #endif
349 }
350
351 bool ResourceHandle::willLoadFromCache(ResourceRequest& request, Frame*)
352 {
353 #ifndef BUILDING_ON_TIGER
354     request.setCachePolicy(ReturnCacheDataDontLoad);
355     NSURLResponse *nsURLResponse = nil;
356     BEGIN_BLOCK_OBJC_EXCEPTIONS;
357     
358    [NSURLConnection sendSynchronousRequest:request.nsURLRequest() returningResponse:&nsURLResponse error:nil];
359     
360     END_BLOCK_OBJC_EXCEPTIONS;
361     
362     return nsURLResponse;
363 #else
364     // <rdar://problem/6803217> - Re-enable after <rdar://problem/6786454> is resolved.
365     UNUSED_PARAM(request);
366     return false;
367 #endif
368 }
369
370 void ResourceHandle::loadResourceSynchronously(const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data, Frame*)
371 {
372     NSError *nsError = nil;
373     
374     NSURLResponse *nsURLResponse = nil;
375     NSData *result = nil;
376
377     ASSERT(!request.isEmpty());
378     
379     NSURLRequest *nsRequest;
380     if (!shouldContentSniffURL(request.url())) {
381         NSMutableURLRequest *mutableRequest = [[request.nsURLRequest() mutableCopy] autorelease];
382         wkSetNSURLRequestShouldContentSniff(mutableRequest, NO);
383         nsRequest = mutableRequest;
384     } else
385         nsRequest = request.nsURLRequest();
386             
387     BEGIN_BLOCK_OBJC_EXCEPTIONS;
388     
389 #ifndef BUILDING_ON_TIGER
390     result = [WebCoreSynchronousLoader loadRequest:nsRequest allowStoredCredentials:(storedCredentials == AllowStoredCredentials) returningResponse:&nsURLResponse error:&nsError];
391 #else
392     UNUSED_PARAM(storedCredentials);
393     result = [NSURLConnection sendSynchronousRequest:nsRequest returningResponse:&nsURLResponse error:&nsError];
394 #endif
395     END_BLOCK_OBJC_EXCEPTIONS;
396
397     if (nsError == nil)
398         response = nsURLResponse;
399     else {
400         response = ResourceResponse(request.url(), String(), 0, String(), String());
401         if ([nsError domain] == NSURLErrorDomain)
402             switch ([nsError code]) {
403                 case NSURLErrorUserCancelledAuthentication:
404                     // FIXME: we should really return the actual HTTP response, but sendSynchronousRequest doesn't provide us with one.
405                     response.setHTTPStatusCode(401);
406                     break;
407                 default:
408                     response.setHTTPStatusCode([nsError code]);
409             }
410         else
411             response.setHTTPStatusCode(404);
412     }
413     
414     data.resize([result length]);
415     memcpy(data.data(), [result bytes], [result length]);
416     
417     error = nsError;
418 }
419
420 void ResourceHandle::willSendRequest(ResourceRequest& request, const ResourceResponse& redirectResponse)
421 {
422     const KURL& url = request.url();
423     d->m_user = url.user();
424     d->m_pass = url.pass();
425     request.removeCredentials();
426
427     client()->willSendRequest(this, request, redirectResponse);
428 }
429
430 bool ResourceHandle::shouldUseCredentialStorage()
431 {
432     if (client())
433         return client()->shouldUseCredentialStorage(this);
434
435     return false;
436 }
437
438 void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge)
439 {
440     ASSERT(!d->m_currentMacChallenge);
441     ASSERT(d->m_currentWebChallenge.isNull());
442     // Since NSURLConnection networking relies on keeping a reference to the original NSURLAuthenticationChallenge,
443     // we make sure that is actually present
444     ASSERT(challenge.nsURLAuthenticationChallenge());
445
446     if (!d->m_user.isNull() && !d->m_pass.isNull()) {
447         NSURLCredential *credential = [[NSURLCredential alloc] initWithUser:d->m_user
448                                                                    password:d->m_pass
449                                                                 persistence:NSURLCredentialPersistenceNone];
450         d->m_currentMacChallenge = challenge.nsURLAuthenticationChallenge();
451         d->m_currentWebChallenge = challenge;
452         receivedCredential(challenge, core(credential));
453         [credential release];
454         // FIXME: Per the specification, the user shouldn't be asked for credentials if there were incorrect ones provided explicitly.
455         d->m_user = String();
456         d->m_pass = String();
457         return;
458     }
459
460     if (!challenge.previousFailureCount() && (!client() || client()->shouldUseCredentialStorage(this))) {
461         NSURLCredential *credential = WebCoreCredentialStorage::get([mac(challenge) protectionSpace]);
462         if (credential) {
463             [challenge.sender() useCredential:credential forAuthenticationChallenge:mac(challenge)];
464             return;
465         }
466     }
467
468     d->m_currentMacChallenge = challenge.nsURLAuthenticationChallenge();
469     NSURLAuthenticationChallenge *webChallenge = [[NSURLAuthenticationChallenge alloc] initWithAuthenticationChallenge:d->m_currentMacChallenge 
470                                                                                        sender:(id<NSURLAuthenticationChallengeSender>)delegate()];
471     d->m_currentWebChallenge = core(webChallenge);
472     [webChallenge release];
473
474     if (client())
475         client()->didReceiveAuthenticationChallenge(this, d->m_currentWebChallenge);
476 }
477
478 void ResourceHandle::didCancelAuthenticationChallenge(const AuthenticationChallenge& challenge)
479 {
480     ASSERT(d->m_currentMacChallenge);
481     ASSERT(!d->m_currentWebChallenge.isNull());
482     ASSERT(d->m_currentWebChallenge == challenge);
483
484     if (client())
485         client()->didCancelAuthenticationChallenge(this, challenge);
486 }
487
488 void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge, const Credential& credential)
489 {
490     ASSERT(!challenge.isNull());
491     if (challenge != d->m_currentWebChallenge)
492         return;
493
494 #ifdef BUILDING_ON_TIGER
495     if (credential.persistence() == CredentialPersistenceNone) {
496         // NSURLCredentialPersistenceNone doesn't work on Tiger, so we have to use session persistence.
497         Credential webCredential(credential.user(), credential.password(), CredentialPersistenceForSession);
498         WebCoreCredentialStorage::set(mac(webCredential), [d->m_currentMacChallenge protectionSpace]);
499         [[d->m_currentMacChallenge sender] useCredential:mac(webCredential) forAuthenticationChallenge:d->m_currentMacChallenge];
500     } else
501 #else
502     if (credential.persistence() == CredentialPersistenceForSession && (!d->m_needsSiteSpecificQuirks || ![[[mac(challenge) protectionSpace] host] isEqualToString:@"gallery.me.com"])) {
503         // Manage per-session credentials internally, because once NSURLCredentialPersistenceForSession is used, there is no way
504         // to ignore it for a particular request (short of removing it altogether).
505         // <rdar://problem/6867598> gallery.me.com is temporarily whitelisted, so that QuickTime plug-in could see the credentials.
506         Credential webCredential(credential.user(), credential.password(), CredentialPersistenceNone);
507         WebCoreCredentialStorage::set(mac(webCredential), [d->m_currentMacChallenge protectionSpace]);
508         [[d->m_currentMacChallenge sender] useCredential:mac(webCredential) forAuthenticationChallenge:d->m_currentMacChallenge];
509     } else
510 #endif
511         [[d->m_currentMacChallenge sender] useCredential:mac(credential) forAuthenticationChallenge:d->m_currentMacChallenge];
512
513     clearAuthentication();
514 }
515
516 void ResourceHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge& challenge)
517 {
518     ASSERT(!challenge.isNull());
519     if (challenge != d->m_currentWebChallenge)
520         return;
521
522     [[d->m_currentMacChallenge sender] continueWithoutCredentialForAuthenticationChallenge:d->m_currentMacChallenge];
523
524     clearAuthentication();
525 }
526
527 void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challenge)
528 {
529     if (challenge != d->m_currentWebChallenge)
530         return;
531
532     if (client())
533         client()->receivedCancellation(this, challenge);
534 }
535
536 } // namespace WebCore
537
538 @implementation WebCoreResourceHandleAsDelegate
539
540 - (id)initWithHandle:(ResourceHandle*)handle
541 {
542     self = [self init];
543     if (!self)
544         return nil;
545     m_handle = handle;
546     return self;
547 }
548
549 - (void)detachHandle
550 {
551     m_handle = 0;
552 }
553
554 - (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse
555 {
556     UNUSED_PARAM(connection);
557
558     // the willSendRequest call may cancel this load, in which case self could be deallocated
559     RetainPtr<WebCoreResourceHandleAsDelegate> protect(self);
560
561     if (!m_handle || !m_handle->client())
562         return nil;
563     
564     // See <rdar://problem/5380697> .  This is a workaround for a behavior change in CFNetwork where willSendRequest gets called more often.
565     if (!redirectResponse)
566         return newRequest;
567     
568     LOG(Network, "Handle %p delegate connection:%p willSendRequest:%@ redirectResponse:%p", m_handle, connection, [newRequest description], redirectResponse);
569
570     CallbackGuard guard;
571     ResourceRequest request = newRequest;
572     m_handle->willSendRequest(request, redirectResponse);
573
574     if (!ResourceHandle::didSendBodyDataDelegateExists()) {
575         // The client may change the request's body stream, in which case we have to re-associate
576         // the handle with the new stream so upload progress callbacks continue to work correctly.
577         NSInputStream* oldBodyStream = [newRequest HTTPBodyStream];
578         NSInputStream* newBodyStream = [request.nsURLRequest() HTTPBodyStream];
579         if (oldBodyStream != newBodyStream) {
580             disassociateStreamWithResourceHandle(oldBodyStream);
581             associateStreamWithResourceHandle(newBodyStream, m_handle);
582         }
583     }
584
585     return request.nsURLRequest();
586 }
587
588 - (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection
589 {
590     UNUSED_PARAM(connection);
591
592     LOG(Network, "Handle %p delegate connectionShouldUseCredentialStorage:%p", m_handle, connection);
593
594     if (!m_handle)
595         return NO;
596
597     CallbackGuard guard;
598     return m_handle->shouldUseCredentialStorage();
599 }
600
601 - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
602 {
603     UNUSED_PARAM(connection);
604
605     LOG(Network, "Handle %p delegate connection:%p didReceiveAuthenticationChallenge:%p", m_handle, connection, challenge);
606
607     if (!m_handle)
608         return;
609     CallbackGuard guard;
610     m_handle->didReceiveAuthenticationChallenge(core(challenge));
611 }
612
613 - (void)connection:(NSURLConnection *)connection didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
614 {
615     UNUSED_PARAM(connection);
616
617     LOG(Network, "Handle %p delegate connection:%p didCancelAuthenticationChallenge:%p", m_handle, connection, challenge);
618
619     if (!m_handle)
620         return;
621     CallbackGuard guard;
622     m_handle->didCancelAuthenticationChallenge(core(challenge));
623 }
624
625 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)r
626 {
627     UNUSED_PARAM(connection);
628
629     LOG(Network, "Handle %p delegate connection:%p didReceiveResponse:%p (HTTP status %d, MIMEType '%s', reported MIMEType '%s')", m_handle, connection, r, [r respondsToSelector:@selector(statusCode)] ? [(id)r statusCode] : 0, [[r MIMEType] UTF8String], [[r _webcore_reportedMIMEType] UTF8String]);
630
631     if (!m_handle || !m_handle->client())
632         return;
633     CallbackGuard guard;
634
635     swizzleMIMETypeMethodIfNecessary();
636
637     if ([m_handle->request().nsURLRequest() _propertyForKey:@"ForceHTMLMIMEType"])
638         [r _setMIMEType:@"text/html"];
639
640 #if ENABLE(WML)
641     const KURL& url = [r URL];
642     if (url.isLocalFile()) {
643         // FIXME: Workaround for <rdar://problem/6917571>: The WML file extension ".wml" is not mapped to
644         // the right MIME type, work around that CFNetwork problem, to unbreak WML support for local files.
645         const String& path = url.path();
646   
647         DEFINE_STATIC_LOCAL(const String, wmlExt, (".wml"));
648         if (path.endsWith(wmlExt, false)) {
649             static NSString* defaultMIMETypeString = [(NSString*) defaultMIMEType() retain];
650             if ([[r MIMEType] isEqualToString:defaultMIMETypeString])
651                 [r _setMIMEType:@"text/vnd.wap.wml"];
652         }
653     }
654 #endif
655
656     m_handle->client()->didReceiveResponse(m_handle, r);
657 }
658
659 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data lengthReceived:(long long)lengthReceived
660 {
661     UNUSED_PARAM(connection);
662
663     LOG(Network, "Handle %p delegate connection:%p didReceiveData:%p lengthReceived:%lld", m_handle, connection, data, lengthReceived);
664
665     if (!m_handle || !m_handle->client())
666         return;
667     // FIXME: If we get more than 2B bytes in a single chunk, this code won't do the right thing.
668     // However, with today's computers and networking speeds, this won't happen in practice.
669     // Could be an issue with a giant local file.
670     CallbackGuard guard;
671     m_handle->client()->didReceiveData(m_handle, (const char*)[data bytes], [data length], static_cast<int>(lengthReceived));
672 }
673
674 - (void)connection:(NSURLConnection *)connection willStopBufferingData:(NSData *)data
675 {
676     UNUSED_PARAM(connection);
677
678     LOG(Network, "Handle %p delegate connection:%p willStopBufferingData:%p", m_handle, connection, data);
679
680     if (!m_handle || !m_handle->client())
681         return;
682     // FIXME: If we get a resource with more than 2B bytes, this code won't do the right thing.
683     // However, with today's computers and networking speeds, this won't happen in practice.
684     // Could be an issue with a giant local file.
685     CallbackGuard guard;
686     m_handle->client()->willStopBufferingData(m_handle, (const char*)[data bytes], static_cast<int>([data length]));
687 }
688
689 - (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
690 {
691     UNUSED_PARAM(connection);
692     UNUSED_PARAM(bytesWritten);
693
694     LOG(Network, "Handle %p delegate connection:%p didSendBodyData:%d totalBytesWritten:%d totalBytesExpectedToWrite:%d", m_handle, connection, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
695
696     if (!m_handle || !m_handle->client())
697         return;
698     CallbackGuard guard;
699     m_handle->client()->didSendData(m_handle, totalBytesWritten, totalBytesExpectedToWrite);
700 }
701
702 - (void)connectionDidFinishLoading:(NSURLConnection *)connection
703 {
704     UNUSED_PARAM(connection);
705
706     LOG(Network, "Handle %p delegate connectionDidFinishLoading:%p", m_handle, connection);
707
708     if (!m_handle || !m_handle->client())
709         return;
710     CallbackGuard guard;
711
712     if (!ResourceHandle::didSendBodyDataDelegateExists())
713         disassociateStreamWithResourceHandle([m_handle->request().nsURLRequest() HTTPBodyStream]);
714
715     m_handle->client()->didFinishLoading(m_handle);
716 }
717
718 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
719 {
720     UNUSED_PARAM(connection);
721
722     LOG(Network, "Handle %p delegate connection:%p didFailWithError:%@", m_handle, connection, error);
723
724     if (!m_handle || !m_handle->client())
725         return;
726     CallbackGuard guard;
727
728     if (!ResourceHandle::didSendBodyDataDelegateExists())
729         disassociateStreamWithResourceHandle([m_handle->request().nsURLRequest() HTTPBodyStream]);
730
731     m_handle->client()->didFail(m_handle, error);
732 }
733
734 #ifdef BUILDING_ON_TIGER
735 - (void)_callConnectionWillCacheResponseWithInfo:(NSMutableDictionary *)info
736 {
737     NSURLConnection *connection = [info objectForKey:@"connection"];
738     NSCachedURLResponse *cachedResponse = [info objectForKey:@"cachedResponse"];
739     NSCachedURLResponse *result = [self connection:connection willCacheResponse:cachedResponse];
740     if (result)
741         [info setObject:result forKey:@"result"];
742 }
743 #endif
744
745 - (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse
746 {
747     LOG(Network, "Handle %p delegate connection:%p willCacheResponse:%p", m_handle, connection, cachedResponse);
748
749 #ifdef BUILDING_ON_TIGER
750     // On Tiger CFURLConnection can sometimes call the connection:willCacheResponse: delegate method on
751     // a secondary thread instead of the main thread. If this happens perform the work on the main thread.
752     if (!pthread_main_np()) {
753         NSMutableDictionary *info = [[NSMutableDictionary alloc] init];
754         if (connection)
755             [info setObject:connection forKey:@"connection"];
756         if (cachedResponse)
757             [info setObject:cachedResponse forKey:@"cachedResponse"];
758
759         // Include synchronous url connection's mode as an acceptable run loopmode
760         // <rdar://problem/5511842>
761         NSArray *modes = [[NSArray alloc] initWithObjects:(NSString *)kCFRunLoopCommonModes, @"NSSynchronousURLConnection_PrivateMode", nil];        
762         [self performSelectorOnMainThread:@selector(_callConnectionWillCacheResponseWithInfo:) withObject:info waitUntilDone:YES modes:modes];
763         [modes release];
764
765         NSCachedURLResponse *result = [[info valueForKey:@"result"] retain];
766         [info release];
767
768         return [result autorelease];
769     }
770 #else
771     UNUSED_PARAM(connection);
772 #endif
773
774 #ifndef NDEBUG
775     if (isInitializingConnection)
776         LOG_ERROR("connection:willCacheResponse: was called inside of [NSURLConnection initWithRequest:delegate:] (4067625)");
777 #endif
778
779     if (!m_handle || !m_handle->client())
780         return nil;
781
782     CallbackGuard guard;
783     
784     NSCachedURLResponse *newResponse = m_handle->client()->willCacheResponse(m_handle, cachedResponse);
785     if (newResponse != cachedResponse)
786         return newResponse;
787     
788     CacheStoragePolicy policy = static_cast<CacheStoragePolicy>([newResponse storagePolicy]);
789         
790     m_handle->client()->willCacheResponse(m_handle, policy);
791
792     if (static_cast<NSURLCacheStoragePolicy>(policy) != [newResponse storagePolicy])
793         newResponse = [[[NSCachedURLResponse alloc] initWithResponse:[newResponse response]
794                                                                 data:[newResponse data]
795                                                             userInfo:[newResponse userInfo]
796                                                        storagePolicy:static_cast<NSURLCacheStoragePolicy>(policy)] autorelease];
797
798     return newResponse;
799 }
800
801 - (void)useCredential:(NSURLCredential *)credential forAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
802 {
803     if (!m_handle)
804         return;
805     m_handle->receivedCredential(core(challenge), core(credential));
806 }
807
808 - (void)continueWithoutCredentialForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
809 {
810     if (!m_handle)
811         return;
812     m_handle->receivedRequestToContinueWithoutCredential(core(challenge));
813 }
814
815 - (void)cancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
816 {
817     if (!m_handle)
818         return;
819     m_handle->receivedCancellation(core(challenge));
820 }
821
822 @end
823
824 #ifndef BUILDING_ON_TIGER
825
826 @implementation WebCoreSynchronousLoader
827
828 - (BOOL)_isDone
829 {
830     return m_isDone;
831 }
832
833 - (void)dealloc
834 {
835     [m_url release];
836     [m_user release];
837     [m_pass release];
838     [m_response release];
839     [m_data release];
840     [m_error release];
841     
842     [super dealloc];
843 }
844
845 - (NSURLRequest *)connection:(NSURLConnection *)unusedConnection willSendRequest:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse
846 {
847     UNUSED_PARAM(unusedConnection);
848
849     // FIXME: This needs to be fixed to follow the redirect correctly even for cross-domain requests.
850     if (m_url && !protocolHostAndPortAreEqual(m_url, [newRequest URL])) {
851         m_error = [[NSError alloc] initWithDomain:NSURLErrorDomain code:NSURLErrorBadServerResponse userInfo:nil];
852         m_isDone = YES;
853         return nil;
854     }
855
856     NSURL *copy = [[newRequest URL] copy];
857     [m_url release];
858     m_url = copy;
859
860     if (redirectResponse) {
861         // Take user/pass out of the URL.
862         [m_user release];
863         [m_pass release];
864         m_user = [[m_url user] copy];
865         m_pass = [[m_url password] copy];
866         if (m_user || m_pass) {
867             ResourceRequest requestWithoutCredentials = newRequest;
868             requestWithoutCredentials.removeCredentials();
869             return requestWithoutCredentials.nsURLRequest();
870         }
871     }
872
873     return newRequest;
874 }
875
876 - (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)unusedConnection
877 {
878     UNUSED_PARAM(unusedConnection);
879
880     // FIXME: We should ask FrameLoaderClient whether using credential storage is globally forbidden.
881     return m_allowStoredCredentials;
882 }
883
884 - (void)connection:(NSURLConnection *)unusedConnection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
885 {
886     UNUSED_PARAM(unusedConnection);
887
888     if (m_user && m_pass) {
889         NSURLCredential *credential = [[NSURLCredential alloc] initWithUser:m_user
890                                                                    password:m_pass
891                                                                 persistence:NSURLCredentialPersistenceNone];
892         WebCoreCredentialStorage::set(credential, [challenge protectionSpace]);
893         [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
894         [credential release];
895         [m_user release];
896         [m_pass release];
897         m_user = 0;
898         m_pass = 0;
899         return;
900     }
901     if ([challenge previousFailureCount] == 0 && m_allowStoredCredentials) {
902         NSURLCredential *credential = WebCoreCredentialStorage::get([challenge protectionSpace]);
903         ASSERT([credential persistence] == NSURLCredentialPersistenceNone);
904         if (credential) {
905             [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
906             return;
907         }
908     }
909     // FIXME: The user should be asked for credentials, as in async case.
910     [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
911 }
912
913 - (void)connection:(NSURLConnection *)unusedConnection didReceiveResponse:(NSURLResponse *)response
914 {
915     UNUSED_PARAM(unusedConnection);
916
917     NSURLResponse *r = [response copy];
918     
919     [m_response release];
920     m_response = r;
921 }
922
923 - (void)connection:(NSURLConnection *)unusedConnection didReceiveData:(NSData *)data
924 {
925     UNUSED_PARAM(unusedConnection);
926
927     if (!m_data)
928         m_data = [[NSMutableData alloc] init];
929     
930     [m_data appendData:data];
931 }
932
933 - (void)connectionDidFinishLoading:(NSURLConnection *)unusedConnection
934 {
935     UNUSED_PARAM(unusedConnection);
936
937     m_isDone = YES;
938 }
939
940 - (void)connection:(NSURLConnection *)unusedConnection didFailWithError:(NSError *)error
941 {
942     UNUSED_PARAM(unusedConnection);
943
944     ASSERT(!m_error);
945     
946     m_error = [error retain];
947     m_isDone = YES;
948 }
949
950 - (NSData *)_data
951 {
952     return [[m_data retain] autorelease];
953 }
954
955 - (NSURLResponse *)_response
956 {
957     return [[m_response retain] autorelease];
958 }
959
960 - (NSError *)_error
961 {
962     return [[m_error retain] autorelease];
963 }
964
965 + (NSData *)loadRequest:(NSURLRequest *)request allowStoredCredentials:(BOOL)allowStoredCredentials returningResponse:(NSURLResponse **)response error:(NSError **)error
966 {
967     WebCoreSynchronousLoader *delegate = [[WebCoreSynchronousLoader alloc] init];
968
969     NSURL *url = [request URL];
970     delegate->m_user = [[url user] copy];
971     delegate->m_pass = [[url password] copy];
972     delegate->m_allowStoredCredentials = allowStoredCredentials;
973
974     NSURLConnection *connection;
975
976     // Take user/pass out of the URL.
977     // Credentials for ftp can only be passed in URL, the connection:didReceiveAuthenticationChallenge: delegate call won't be made.
978     if ((delegate->m_user || delegate->m_pass) && KURL(url).protocolInHTTPFamily()) {
979         ResourceRequest requestWithoutCredentials = request;
980         requestWithoutCredentials.removeCredentials();
981         connection = [[NSURLConnection alloc] initWithRequest:requestWithoutCredentials.nsURLRequest() delegate:delegate startImmediately:NO];
982     } else
983         connection = [[NSURLConnection alloc] initWithRequest:request delegate:delegate startImmediately:NO];
984
985     [connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:WebCoreSynchronousLoaderRunLoopMode];
986     [connection start];
987     
988     while (![delegate _isDone])
989         [[NSRunLoop currentRunLoop] runMode:WebCoreSynchronousLoaderRunLoopMode beforeDate:[NSDate distantFuture]];
990
991     NSData *data = [delegate _data];
992     *response = [delegate _response];
993     *error = [delegate _error];
994     
995     [connection cancel];
996     
997     [connection release];
998     [delegate release];
999     
1000     return data;
1001 }
1002
1003 @end
1004
1005 #endif