bfa83a3e1d1be168ebb23cc0b8cf22a2663b3ff5
[WebKit-https.git] / WebCore / platform / network / mac / ResourceHandleMac.mm
1 /*
2  * Copyright (C) 2004, 2006-2009 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 NSURLRequest (Details)
68 - (id)_propertyForKey:(NSString *)key;
69 @end
70
71 #ifndef BUILDING_ON_TIGER
72
73 @interface WebCoreSynchronousLoader : NSObject {
74     NSURL *m_url;
75     NSString *m_user;
76     NSString *m_pass;
77     BOOL m_allowStoredCredentials;
78     NSURLResponse *m_response;
79     NSMutableData *m_data;
80     NSError *m_error;
81     BOOL m_isDone;
82 }
83 + (NSData *)loadRequest:(NSURLRequest *)request allowStoredCredentials:(BOOL)allowStoredCredentials returningResponse:(NSURLResponse **)response error:(NSError **)error;
84 @end
85
86 static NSString *WebCoreSynchronousLoaderRunLoopMode = @"WebCoreSynchronousLoaderRunLoopMode";
87
88 #endif
89
90 namespace WebCore {
91
92 #ifdef BUILDING_ON_TIGER
93 static unsigned inNSURLConnectionCallback;
94 #endif
95
96 #ifndef NDEBUG
97 static bool isInitializingConnection;
98 #endif
99     
100 class CallbackGuard {
101 public:
102     CallbackGuard()
103     {
104 #ifdef BUILDING_ON_TIGER
105         ++inNSURLConnectionCallback;
106 #endif
107     }
108     ~CallbackGuard()
109     {
110 #ifdef BUILDING_ON_TIGER
111         ASSERT(inNSURLConnectionCallback > 0);
112         --inNSURLConnectionCallback;
113 #endif
114     }
115 };
116
117 ResourceHandleInternal::~ResourceHandleInternal()
118 {
119 }
120
121 ResourceHandle::~ResourceHandle()
122 {
123     releaseDelegate();
124
125     LOG(Network, "Handle %p destroyed", this);
126 }
127
128 static const double MaxFoundationVersionWithoutdidSendBodyDataDelegate = 677.21;
129 bool ResourceHandle::didSendBodyDataDelegateExists()
130 {
131     return NSFoundationVersionNumber > MaxFoundationVersionWithoutdidSendBodyDataDelegate;
132 }
133
134 bool ResourceHandle::start(Frame* frame)
135 {
136     if (!frame)
137         return false;
138
139     BEGIN_BLOCK_OBJC_EXCEPTIONS;
140
141     // If we are no longer attached to a Page, this must be an attempted load from an
142     // onUnload handler, so let's just block it.
143     Page* page = frame->page();
144     if (!page)
145         return false;
146
147 #ifndef NDEBUG
148     isInitializingConnection = YES;
149 #endif
150
151     id delegate;
152     
153     if (d->m_mightDownloadFromHandle) {
154         ASSERT(!d->m_proxy);
155         d->m_proxy = wkCreateNSURLConnectionDelegateProxy();
156         [d->m_proxy.get() setDelegate:ResourceHandle::delegate()];
157         [d->m_proxy.get() release];
158         
159         delegate = d->m_proxy.get();
160     } else 
161         delegate = ResourceHandle::delegate();
162
163     if ((!d->m_user.isEmpty() || !d->m_pass.isEmpty()) && !d->m_request.url().protocolInHTTPFamily()) {
164         // Credentials for ftp can only be passed in URL, the connection:didReceiveAuthenticationChallenge: delegate call won't be made.
165         KURL urlWithCredentials(d->m_request.url());
166         urlWithCredentials.setUser(d->m_user);
167         urlWithCredentials.setPass(d->m_pass);
168         d->m_request.setURL(urlWithCredentials);
169     }
170
171     if (!ResourceHandle::didSendBodyDataDelegateExists())
172         associateStreamWithResourceHandle([d->m_request.nsURLRequest() HTTPBodyStream], this);
173
174 #ifdef BUILDING_ON_TIGER
175     // A conditional request sent by WebCore (e.g. to update appcache) can be for a resource that is not cacheable by NSURLConnection,
176     // which can get confused and fail to load it in this case.
177     if (d->m_request.isConditional())
178         d->m_request.setCachePolicy(ReloadIgnoringCacheData);
179 #endif
180
181     d->m_needsSiteSpecificQuirks = frame->settings() && frame->settings()->needsSiteSpecificQuirks();
182
183     NSURLConnection *connection;
184     
185     if (d->m_shouldContentSniff || frame->settings()->localFileContentSniffingEnabled()) 
186 #ifdef BUILDING_ON_TIGER
187         connection = [[NSURLConnection alloc] initWithRequest:d->m_request.nsURLRequest() delegate:delegate];
188 #else
189         connection = [[NSURLConnection alloc] initWithRequest:d->m_request.nsURLRequest() delegate:delegate startImmediately:NO];
190 #endif
191     else {
192         NSMutableURLRequest *request = [d->m_request.nsURLRequest() mutableCopy];
193         wkSetNSURLRequestShouldContentSniff(request, NO);
194 #ifdef BUILDING_ON_TIGER
195         connection = [[NSURLConnection alloc] initWithRequest:request delegate:delegate];
196 #else
197         connection = [[NSURLConnection alloc] initWithRequest:request delegate:delegate startImmediately:NO];
198 #endif
199         [request release];
200     }
201
202 #ifndef BUILDING_ON_TIGER
203     bool scheduled = false;
204     if (SchedulePairHashSet* scheduledPairs = page->scheduledRunLoopPairs()) {
205         SchedulePairHashSet::iterator end = scheduledPairs->end();
206         for (SchedulePairHashSet::iterator it = scheduledPairs->begin(); it != end; ++it) {
207             if (NSRunLoop *runLoop = (*it)->nsRunLoop()) {
208                 [connection scheduleInRunLoop:runLoop forMode:(NSString *)(*it)->mode()];
209                 scheduled = true;
210             }
211         }
212     }
213
214     // Start the connection if we did schedule with at least one runloop.
215     // We can't start the connection until we have one runloop scheduled.
216     if (scheduled)
217         [connection start];
218     else
219         d->m_startWhenScheduled = true;
220 #endif
221
222 #ifndef NDEBUG
223     isInitializingConnection = NO;
224 #endif
225
226     LOG(Network, "Handle %p starting connection %p for %@", this, connection, d->m_request.nsURLRequest());
227     
228     d->m_connection = connection;
229
230     if (d->m_connection) {
231         [connection release];
232
233         if (d->m_defersLoading)
234             wkSetNSURLConnectionDefersCallbacks(d->m_connection.get(), YES);
235
236         return true;
237     }
238
239     END_BLOCK_OBJC_EXCEPTIONS;
240
241     return false;
242 }
243
244 void ResourceHandle::cancel()
245 {
246     LOG(Network, "Handle %p cancel connection %p", this, d->m_connection.get());
247
248     // Leaks were seen on HTTP tests without this; can be removed once <rdar://problem/6886937> is fixed.
249     if (d->m_currentMacChallenge)
250         [[d->m_currentMacChallenge sender] cancelAuthenticationChallenge:d->m_currentMacChallenge];
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     if (redirectResponse && [redirectResponse isKindOfClass:[NSHTTPURLResponse class]] && [(NSHTTPURLResponse *)redirectResponse statusCode] == 307) {
571         String originalMethod = m_handle->request().httpMethod();
572         if (!equalIgnoringCase(originalMethod, String([newRequest HTTPMethod]))) {
573             NSMutableURLRequest *mutableRequest = [newRequest mutableCopy];
574             [mutableRequest setHTTPMethod:originalMethod];
575             newRequest = [mutableRequest autorelease];
576         }
577     }
578
579     CallbackGuard guard;
580     ResourceRequest request = newRequest;
581     m_handle->willSendRequest(request, redirectResponse);
582
583     if (!ResourceHandle::didSendBodyDataDelegateExists()) {
584         // The client may change the request's body stream, in which case we have to re-associate
585         // the handle with the new stream so upload progress callbacks continue to work correctly.
586         NSInputStream* oldBodyStream = [newRequest HTTPBodyStream];
587         NSInputStream* newBodyStream = [request.nsURLRequest() HTTPBodyStream];
588         if (oldBodyStream != newBodyStream) {
589             disassociateStreamWithResourceHandle(oldBodyStream);
590             associateStreamWithResourceHandle(newBodyStream, m_handle);
591         }
592     }
593
594     return request.nsURLRequest();
595 }
596
597 - (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection
598 {
599     UNUSED_PARAM(connection);
600
601     LOG(Network, "Handle %p delegate connectionShouldUseCredentialStorage:%p", m_handle, connection);
602
603     if (!m_handle)
604         return NO;
605
606     CallbackGuard guard;
607     return m_handle->shouldUseCredentialStorage();
608 }
609
610 - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
611 {
612     UNUSED_PARAM(connection);
613
614     LOG(Network, "Handle %p delegate connection:%p didReceiveAuthenticationChallenge:%p", m_handle, connection, challenge);
615
616     if (!m_handle)
617         return;
618     CallbackGuard guard;
619     m_handle->didReceiveAuthenticationChallenge(core(challenge));
620 }
621
622 - (void)connection:(NSURLConnection *)connection didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
623 {
624     UNUSED_PARAM(connection);
625
626     LOG(Network, "Handle %p delegate connection:%p didCancelAuthenticationChallenge:%p", m_handle, connection, challenge);
627
628     if (!m_handle)
629         return;
630     CallbackGuard guard;
631     m_handle->didCancelAuthenticationChallenge(core(challenge));
632 }
633
634 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)r
635 {
636     UNUSED_PARAM(connection);
637
638     LOG(Network, "Handle %p delegate connection:%p didReceiveResponse:%p (HTTP status %d, reported MIMEType '%s')", m_handle, connection, r, [r respondsToSelector:@selector(statusCode)] ? [(id)r statusCode] : 0, [[r MIMEType] UTF8String]);
639
640     if (!m_handle || !m_handle->client())
641         return;
642     CallbackGuard guard;
643
644     [r adjustMIMETypeIfNecessary];
645
646     if ([m_handle->request().nsURLRequest() _propertyForKey:@"ForceHTMLMIMEType"])
647         [r _setMIMEType:@"text/html"];
648
649 #if ENABLE(WML)
650     const KURL& url = [r URL];
651     if (url.isLocalFile()) {
652         // FIXME: Workaround for <rdar://problem/6917571>: The WML file extension ".wml" is not mapped to
653         // the right MIME type, work around that CFNetwork problem, to unbreak WML support for local files.
654         const String& path = url.path();
655   
656         DEFINE_STATIC_LOCAL(const String, wmlExt, (".wml"));
657         if (path.endsWith(wmlExt, false)) {
658             static NSString* defaultMIMETypeString = [(NSString*) defaultMIMEType() retain];
659             if ([[r MIMEType] isEqualToString:defaultMIMETypeString])
660                 [r _setMIMEType:@"text/vnd.wap.wml"];
661         }
662     }
663 #endif
664
665     m_handle->client()->didReceiveResponse(m_handle, r);
666 }
667
668 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data lengthReceived:(long long)lengthReceived
669 {
670     UNUSED_PARAM(connection);
671
672     LOG(Network, "Handle %p delegate connection:%p didReceiveData:%p lengthReceived:%lld", m_handle, connection, data, lengthReceived);
673
674     if (!m_handle || !m_handle->client())
675         return;
676     // FIXME: If we get more than 2B bytes in a single chunk, this code won't do the right thing.
677     // However, with today's computers and networking speeds, this won't happen in practice.
678     // Could be an issue with a giant local file.
679     CallbackGuard guard;
680     m_handle->client()->didReceiveData(m_handle, (const char*)[data bytes], [data length], static_cast<int>(lengthReceived));
681 }
682
683 - (void)connection:(NSURLConnection *)connection willStopBufferingData:(NSData *)data
684 {
685     UNUSED_PARAM(connection);
686
687     LOG(Network, "Handle %p delegate connection:%p willStopBufferingData:%p", m_handle, connection, data);
688
689     if (!m_handle || !m_handle->client())
690         return;
691     // FIXME: If we get a resource with more than 2B bytes, this code won't do the right thing.
692     // However, with today's computers and networking speeds, this won't happen in practice.
693     // Could be an issue with a giant local file.
694     CallbackGuard guard;
695     m_handle->client()->willStopBufferingData(m_handle, (const char*)[data bytes], static_cast<int>([data length]));
696 }
697
698 - (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
699 {
700     UNUSED_PARAM(connection);
701     UNUSED_PARAM(bytesWritten);
702
703     LOG(Network, "Handle %p delegate connection:%p didSendBodyData:%d totalBytesWritten:%d totalBytesExpectedToWrite:%d", m_handle, connection, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
704
705     if (!m_handle || !m_handle->client())
706         return;
707     CallbackGuard guard;
708     m_handle->client()->didSendData(m_handle, totalBytesWritten, totalBytesExpectedToWrite);
709 }
710
711 - (void)connectionDidFinishLoading:(NSURLConnection *)connection
712 {
713     UNUSED_PARAM(connection);
714
715     LOG(Network, "Handle %p delegate connectionDidFinishLoading:%p", m_handle, connection);
716
717     if (!m_handle || !m_handle->client())
718         return;
719     CallbackGuard guard;
720
721     if (!ResourceHandle::didSendBodyDataDelegateExists())
722         disassociateStreamWithResourceHandle([m_handle->request().nsURLRequest() HTTPBodyStream]);
723
724     m_handle->client()->didFinishLoading(m_handle);
725 }
726
727 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
728 {
729     UNUSED_PARAM(connection);
730
731     LOG(Network, "Handle %p delegate connection:%p didFailWithError:%@", m_handle, connection, error);
732
733     if (!m_handle || !m_handle->client())
734         return;
735     CallbackGuard guard;
736
737     if (!ResourceHandle::didSendBodyDataDelegateExists())
738         disassociateStreamWithResourceHandle([m_handle->request().nsURLRequest() HTTPBodyStream]);
739
740     m_handle->client()->didFail(m_handle, error);
741 }
742
743 #ifdef BUILDING_ON_TIGER
744 - (void)_callConnectionWillCacheResponseWithInfo:(NSMutableDictionary *)info
745 {
746     NSURLConnection *connection = [info objectForKey:@"connection"];
747     NSCachedURLResponse *cachedResponse = [info objectForKey:@"cachedResponse"];
748     NSCachedURLResponse *result = [self connection:connection willCacheResponse:cachedResponse];
749     if (result)
750         [info setObject:result forKey:@"result"];
751 }
752 #endif
753
754 - (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse
755 {
756     LOG(Network, "Handle %p delegate connection:%p willCacheResponse:%p", m_handle, connection, cachedResponse);
757
758 #ifdef BUILDING_ON_TIGER
759     // On Tiger CFURLConnection can sometimes call the connection:willCacheResponse: delegate method on
760     // a secondary thread instead of the main thread. If this happens perform the work on the main thread.
761     if (!pthread_main_np()) {
762         NSMutableDictionary *info = [[NSMutableDictionary alloc] init];
763         if (connection)
764             [info setObject:connection forKey:@"connection"];
765         if (cachedResponse)
766             [info setObject:cachedResponse forKey:@"cachedResponse"];
767
768         // Include synchronous url connection's mode as an acceptable run loopmode
769         // <rdar://problem/5511842>
770         NSArray *modes = [[NSArray alloc] initWithObjects:(NSString *)kCFRunLoopCommonModes, @"NSSynchronousURLConnection_PrivateMode", nil];        
771         [self performSelectorOnMainThread:@selector(_callConnectionWillCacheResponseWithInfo:) withObject:info waitUntilDone:YES modes:modes];
772         [modes release];
773
774         NSCachedURLResponse *result = [[info valueForKey:@"result"] retain];
775         [info release];
776
777         return [result autorelease];
778     }
779 #else
780     UNUSED_PARAM(connection);
781 #endif
782
783 #ifndef NDEBUG
784     if (isInitializingConnection)
785         LOG_ERROR("connection:willCacheResponse: was called inside of [NSURLConnection initWithRequest:delegate:] (4067625)");
786 #endif
787
788     if (!m_handle || !m_handle->client())
789         return nil;
790
791     CallbackGuard guard;
792     
793     NSCachedURLResponse *newResponse = m_handle->client()->willCacheResponse(m_handle, cachedResponse);
794     if (newResponse != cachedResponse)
795         return newResponse;
796     
797     CacheStoragePolicy policy = static_cast<CacheStoragePolicy>([newResponse storagePolicy]);
798         
799     m_handle->client()->willCacheResponse(m_handle, policy);
800
801     if (static_cast<NSURLCacheStoragePolicy>(policy) != [newResponse storagePolicy])
802         newResponse = [[[NSCachedURLResponse alloc] initWithResponse:[newResponse response]
803                                                                 data:[newResponse data]
804                                                             userInfo:[newResponse userInfo]
805                                                        storagePolicy:static_cast<NSURLCacheStoragePolicy>(policy)] autorelease];
806
807     return newResponse;
808 }
809
810 - (void)useCredential:(NSURLCredential *)credential forAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
811 {
812     if (!m_handle)
813         return;
814     m_handle->receivedCredential(core(challenge), core(credential));
815 }
816
817 - (void)continueWithoutCredentialForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
818 {
819     if (!m_handle)
820         return;
821     m_handle->receivedRequestToContinueWithoutCredential(core(challenge));
822 }
823
824 - (void)cancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
825 {
826     if (!m_handle)
827         return;
828     m_handle->receivedCancellation(core(challenge));
829 }
830
831 @end
832
833 #ifndef BUILDING_ON_TIGER
834
835 @implementation WebCoreSynchronousLoader
836
837 - (BOOL)_isDone
838 {
839     return m_isDone;
840 }
841
842 - (void)dealloc
843 {
844     [m_url release];
845     [m_user release];
846     [m_pass release];
847     [m_response release];
848     [m_data release];
849     [m_error release];
850     
851     [super dealloc];
852 }
853
854 - (NSURLRequest *)connection:(NSURLConnection *)unusedConnection willSendRequest:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse
855 {
856     UNUSED_PARAM(unusedConnection);
857
858     // FIXME: This needs to be fixed to follow the redirect correctly even for cross-domain requests.
859     if (m_url && !protocolHostAndPortAreEqual(m_url, [newRequest URL])) {
860         m_error = [[NSError alloc] initWithDomain:NSURLErrorDomain code:NSURLErrorBadServerResponse userInfo:nil];
861         m_isDone = YES;
862         return nil;
863     }
864
865     NSURL *copy = [[newRequest URL] copy];
866     [m_url release];
867     m_url = copy;
868
869     if (redirectResponse) {
870         // Take user/pass out of the URL.
871         [m_user release];
872         [m_pass release];
873         m_user = [[m_url user] copy];
874         m_pass = [[m_url password] copy];
875         if (m_user || m_pass) {
876             ResourceRequest requestWithoutCredentials = newRequest;
877             requestWithoutCredentials.removeCredentials();
878             return requestWithoutCredentials.nsURLRequest();
879         }
880     }
881
882     return newRequest;
883 }
884
885 - (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)unusedConnection
886 {
887     UNUSED_PARAM(unusedConnection);
888
889     // FIXME: We should ask FrameLoaderClient whether using credential storage is globally forbidden.
890     return m_allowStoredCredentials;
891 }
892
893 - (void)connection:(NSURLConnection *)unusedConnection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
894 {
895     UNUSED_PARAM(unusedConnection);
896
897     if (m_user && m_pass) {
898         NSURLCredential *credential = [[NSURLCredential alloc] initWithUser:m_user
899                                                                    password:m_pass
900                                                                 persistence:NSURLCredentialPersistenceNone];
901         WebCoreCredentialStorage::set(credential, [challenge protectionSpace]);
902         [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
903         [credential release];
904         [m_user release];
905         [m_pass release];
906         m_user = 0;
907         m_pass = 0;
908         return;
909     }
910     if ([challenge previousFailureCount] == 0 && m_allowStoredCredentials) {
911         NSURLCredential *credential = WebCoreCredentialStorage::get([challenge protectionSpace]);
912         ASSERT([credential persistence] == NSURLCredentialPersistenceNone);
913         if (credential) {
914             [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
915             return;
916         }
917     }
918     // FIXME: The user should be asked for credentials, as in async case.
919     [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
920 }
921
922 - (void)connection:(NSURLConnection *)unusedConnection didReceiveResponse:(NSURLResponse *)response
923 {
924     UNUSED_PARAM(unusedConnection);
925
926     NSURLResponse *r = [response copy];
927     
928     [m_response release];
929     m_response = r;
930 }
931
932 - (void)connection:(NSURLConnection *)unusedConnection didReceiveData:(NSData *)data
933 {
934     UNUSED_PARAM(unusedConnection);
935
936     if (!m_data)
937         m_data = [[NSMutableData alloc] init];
938     
939     [m_data appendData:data];
940 }
941
942 - (void)connectionDidFinishLoading:(NSURLConnection *)unusedConnection
943 {
944     UNUSED_PARAM(unusedConnection);
945
946     m_isDone = YES;
947 }
948
949 - (void)connection:(NSURLConnection *)unusedConnection didFailWithError:(NSError *)error
950 {
951     UNUSED_PARAM(unusedConnection);
952
953     ASSERT(!m_error);
954     
955     m_error = [error retain];
956     m_isDone = YES;
957 }
958
959 - (NSData *)_data
960 {
961     return [[m_data retain] autorelease];
962 }
963
964 - (NSURLResponse *)_response
965 {
966     return [[m_response retain] autorelease];
967 }
968
969 - (NSError *)_error
970 {
971     return [[m_error retain] autorelease];
972 }
973
974 + (NSData *)loadRequest:(NSURLRequest *)request allowStoredCredentials:(BOOL)allowStoredCredentials returningResponse:(NSURLResponse **)response error:(NSError **)error
975 {
976     WebCoreSynchronousLoader *delegate = [[WebCoreSynchronousLoader alloc] init];
977
978     NSURL *url = [request URL];
979     delegate->m_user = [[url user] copy];
980     delegate->m_pass = [[url password] copy];
981     delegate->m_allowStoredCredentials = allowStoredCredentials;
982
983     NSURLConnection *connection;
984
985     // Take user/pass out of the URL.
986     // Credentials for ftp can only be passed in URL, the connection:didReceiveAuthenticationChallenge: delegate call won't be made.
987     if ((delegate->m_user || delegate->m_pass) && KURL(url).protocolInHTTPFamily()) {
988         ResourceRequest requestWithoutCredentials = request;
989         requestWithoutCredentials.removeCredentials();
990         connection = [[NSURLConnection alloc] initWithRequest:requestWithoutCredentials.nsURLRequest() delegate:delegate startImmediately:NO];
991     } else
992         connection = [[NSURLConnection alloc] initWithRequest:request delegate:delegate startImmediately:NO];
993
994     [connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:WebCoreSynchronousLoaderRunLoopMode];
995     [connection start];
996     
997     while (![delegate _isDone])
998         [[NSRunLoop currentRunLoop] runMode:WebCoreSynchronousLoaderRunLoopMode beforeDate:[NSDate distantFuture]];
999
1000     NSData *data = [delegate _data];
1001     *response = [delegate _response];
1002     *error = [delegate _error];
1003     
1004     [connection cancel];
1005     
1006     [connection release];
1007     [delegate release];
1008     
1009     return data;
1010 }
1011
1012 @end
1013
1014 #endif