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