<rdar://problem/13194263> Crashes in NetworkProcess due to threading issues
[WebKit-https.git] / Source / WebCore / platform / network / mac / ResourceHandleMac.mm
1 /*
2  * Copyright (C) 2004, 2006, 2007, 2008, 2009, 2010, 2011, 2012 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 "BlockExceptions.h"
34 #import "CookieStorage.h"
35 #import "CredentialStorage.h"
36 #import "CachedResourceLoader.h"
37 #import "EmptyProtocolDefinitions.h"
38 #import "FormDataStreamMac.h"
39 #import "Frame.h"
40 #import "FrameLoader.h"
41 #import "Logging.h"
42 #import "MIMETypeRegistry.h"
43 #import "NetworkingContext.h"
44 #import "Page.h"
45 #import "ResourceError.h"
46 #import "ResourceResponse.h"
47 #import "Settings.h"
48 #import "SharedBuffer.h"
49 #import "SubresourceLoader.h"
50 #import "WebCoreResourceHandleAsDelegate.h"
51 #import "WebCoreResourceHandleAsOperationQueueDelegate.h"
52 #import "SynchronousLoaderClient.h"
53 #import "WebCoreSystemInterface.h"
54 #import "WebCoreURLResponse.h"
55 #import <wtf/SchedulePair.h>
56 #import <wtf/UnusedParam.h>
57 #import <wtf/text/Base64.h>
58 #import <wtf/text/CString.h>
59
60 using namespace WebCore;
61
62 // WebCoreNSURLConnectionDelegateProxy exists so that we can cast m_proxy to it in order
63 // to disambiguate the argument type in the -setDelegate: call.  This avoids a spurious
64 // warning that the compiler would otherwise emit.
65 @interface WebCoreNSURLConnectionDelegateProxy : NSObject <NSURLConnectionDelegate>
66 - (void)setDelegate:(id<NSURLConnectionDelegate>)delegate;
67 @end
68
69 @interface NSURLConnection (Details)
70 -(id)_initWithRequest:(NSURLRequest *)request delegate:(id)delegate usesCache:(BOOL)usesCacheFlag maxContentLength:(long long)maxContentLength startImmediately:(BOOL)startImmediately connectionProperties:(NSDictionary *)connectionProperties;
71 @end
72
73 namespace WebCore {
74
75 static void applyBasicAuthorizationHeader(ResourceRequest& request, const Credential& credential)
76 {
77     String authenticationHeader = "Basic " + base64Encode(String(credential.user() + ":" + credential.password()).utf8());
78     request.clearHTTPAuthorization(); // FIXME: Should addHTTPHeaderField be smart enough to not build comma-separated lists in headers like Authorization?
79     request.addHTTPHeaderField("Authorization", authenticationHeader);
80 }
81
82 static NSOperationQueue *operationQueueForAsyncClients()
83 {
84     static NSOperationQueue *queue;
85     if (!queue) {
86         queue = [[NSOperationQueue alloc] init];
87         // Default concurrent operation count depends on current system workload, but delegate methods are mostly idling in IPC, so we can run as many as needed.
88         [queue setMaxConcurrentOperationCount:NSIntegerMax];
89     }
90     return queue;
91 }
92
93 ResourceHandleInternal::~ResourceHandleInternal()
94 {
95 }
96
97 ResourceHandle::~ResourceHandle()
98 {
99     releaseDelegate();
100     d->m_currentWebChallenge.setAuthenticationClient(0);
101
102     LOG(Network, "Handle %p destroyed", this);
103 }
104
105 void ResourceHandle::createNSURLConnection(id delegate, bool shouldUseCredentialStorage, bool shouldContentSniff)
106 {
107     // Credentials for ftp can only be passed in URL, the connection:didReceiveAuthenticationChallenge: delegate call won't be made.
108     if ((!d->m_user.isEmpty() || !d->m_pass.isEmpty()) && !firstRequest().url().protocolIsInHTTPFamily()) {
109         KURL urlWithCredentials(firstRequest().url());
110         urlWithCredentials.setUser(d->m_user);
111         urlWithCredentials.setPass(d->m_pass);
112         firstRequest().setURL(urlWithCredentials);
113     }
114
115     if (shouldUseCredentialStorage && firstRequest().url().protocolIsInHTTPFamily()) {
116         if (d->m_user.isEmpty() && d->m_pass.isEmpty()) {
117             // <rdar://problem/7174050> - For URLs that match the paths of those previously challenged for HTTP Basic authentication, 
118             // try and reuse the credential preemptively, as allowed by RFC 2617.
119             d->m_initialCredential = CredentialStorage::get(firstRequest().url());
120         } else {
121             // If there is already a protection space known for the URL, update stored credentials before sending a request.
122             // This makes it possible to implement logout by sending an XMLHttpRequest with known incorrect credentials, and aborting it immediately
123             // (so that an authentication dialog doesn't pop up).
124             CredentialStorage::set(Credential(d->m_user, d->m_pass, CredentialPersistenceNone), firstRequest().url());
125         }
126     }
127         
128     if (!d->m_initialCredential.isEmpty()) {
129         // FIXME: Support Digest authentication, and Proxy-Authorization.
130         applyBasicAuthorizationHeader(firstRequest(), d->m_initialCredential);
131     }
132
133     NSURLRequest *nsRequest = firstRequest().nsURLRequest(UpdateHTTPBody);
134     if (!shouldContentSniff) {
135         NSMutableURLRequest *mutableRequest = [[nsRequest mutableCopy] autorelease];
136         wkSetNSURLRequestShouldContentSniff(mutableRequest, NO);
137         nsRequest = mutableRequest;
138     }
139
140     if (d->m_storageSession)
141         nsRequest = [wkCopyRequestWithStorageSession(d->m_storageSession.get(), nsRequest) autorelease];
142
143     ASSERT([NSURLConnection instancesRespondToSelector:@selector(_initWithRequest:delegate:usesCache:maxContentLength:startImmediately:connectionProperties:)]);
144
145     NSMutableDictionary *streamProperties = [NSMutableDictionary dictionary];
146
147     if (!shouldUseCredentialStorage)
148         [streamProperties setObject:@"WebKitPrivateSession" forKey:@"_kCFURLConnectionSessionID"];
149
150 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090
151     RetainPtr<CFDataRef> sourceApplicationAuditData = d->m_context->sourceApplicationAuditData();
152     if (sourceApplicationAuditData)
153         [streamProperties setObject:(NSData *)sourceApplicationAuditData.get() forKey:@"kCFStreamPropertySourceApplication"];
154 #endif
155
156     NSDictionary *propertyDictionary = [NSDictionary dictionaryWithObject:streamProperties forKey:@"kCFURLConnectionSocketStreamProperties"];
157     d->m_connection.adoptNS([[NSURLConnection alloc] _initWithRequest:nsRequest delegate:delegate usesCache:YES maxContentLength:0 startImmediately:NO connectionProperties:propertyDictionary]);
158 }
159
160 bool ResourceHandle::start()
161 {
162     if (!d->m_context)
163         return false;
164
165     BEGIN_BLOCK_OBJC_EXCEPTIONS;
166
167     // If NetworkingContext is invalid then we are no longer attached to a Page,
168     // this must be an attempted load from an unload event handler, so let's just block it.
169     if (!d->m_context->isValid())
170         return false;
171
172     d->m_storageSession = d->m_context->storageSession().platformSession();
173
174     ASSERT(!d->m_proxy);
175     d->m_proxy.adoptNS(wkCreateNSURLConnectionDelegateProxy());
176     [static_cast<WebCoreNSURLConnectionDelegateProxy*>(d->m_proxy.get()) setDelegate:ResourceHandle::delegate()];
177
178     bool shouldUseCredentialStorage = !client() || client()->shouldUseCredentialStorage(this);
179
180     d->m_needsSiteSpecificQuirks = d->m_context->needsSiteSpecificQuirks();
181
182     createNSURLConnection(
183         d->m_proxy.get(),
184         shouldUseCredentialStorage,
185         d->m_shouldContentSniff || d->m_context->localFileContentSniffingEnabled());
186
187     bool scheduled = false;
188     if (SchedulePairHashSet* scheduledPairs = d->m_context->scheduledRunLoopPairs()) {
189         SchedulePairHashSet::iterator end = scheduledPairs->end();
190         for (SchedulePairHashSet::iterator it = scheduledPairs->begin(); it != end; ++it) {
191             if (NSRunLoop *runLoop = (*it)->nsRunLoop()) {
192                 [connection() scheduleInRunLoop:runLoop forMode:(NSString *)(*it)->mode()];
193                 scheduled = true;
194             }
195         }
196     }
197
198     if (client() && client()->usesAsyncCallbacks()) {
199         ASSERT(!scheduled);
200         [connection() setDelegateQueue:operationQueueForAsyncClients()];
201         scheduled = true;
202     }
203
204     // Start the connection if we did schedule with at least one runloop.
205     // We can't start the connection until we have one runloop scheduled.
206     if (scheduled)
207         [connection() start];
208     else
209         d->m_startWhenScheduled = true;
210
211     LOG(Network, "Handle %p starting connection %p for %@", this, connection(), firstRequest().nsURLRequest(DoNotUpdateHTTPBody));
212     
213     if (d->m_connection) {
214         if (d->m_defersLoading)
215             wkSetNSURLConnectionDefersCallbacks(connection(), YES);
216
217         return true;
218     }
219
220     END_BLOCK_OBJC_EXCEPTIONS;
221
222     return false;
223 }
224
225 void ResourceHandle::cancel()
226 {
227     LOG(Network, "Handle %p cancel connection %p", this, d->m_connection.get());
228
229     // Leaks were seen on HTTP tests without this; can be removed once <rdar://problem/6886937> is fixed.
230     if (d->m_currentMacChallenge)
231         [[d->m_currentMacChallenge sender] cancelAuthenticationChallenge:d->m_currentMacChallenge];
232
233     [d->m_connection.get() cancel];
234 }
235
236 void ResourceHandle::platformSetDefersLoading(bool defers)
237 {
238     if (d->m_connection)
239         wkSetNSURLConnectionDefersCallbacks(d->m_connection.get(), defers);
240 }
241
242 void ResourceHandle::schedule(SchedulePair* pair)
243 {
244     NSRunLoop *runLoop = pair->nsRunLoop();
245     if (!runLoop)
246         return;
247     [d->m_connection.get() scheduleInRunLoop:runLoop forMode:(NSString *)pair->mode()];
248     if (d->m_startWhenScheduled) {
249         [d->m_connection.get() start];
250         d->m_startWhenScheduled = false;
251     }
252 }
253
254 void ResourceHandle::unschedule(SchedulePair* pair)
255 {
256     if (NSRunLoop *runLoop = pair->nsRunLoop())
257         [d->m_connection.get() unscheduleFromRunLoop:runLoop forMode:(NSString *)pair->mode()];
258 }
259
260 id ResourceHandle::delegate()
261 {
262     if (!d->m_delegate) {
263         id <NSURLConnectionDelegate> delegate = (client() && client()->usesAsyncCallbacks()) ?
264             [[WebCoreResourceHandleAsOperationQueueDelegate alloc] initWithHandle:this]
265           : [[WebCoreResourceHandleAsDelegate alloc] initWithHandle:this];
266         d->m_delegate = delegate;
267         [delegate release];
268     }
269     return d->m_delegate.get();
270 }
271
272 void ResourceHandle::releaseDelegate()
273 {
274     if (!d->m_delegate)
275         return;
276     if (d->m_proxy)
277         [d->m_proxy.get() setDelegate:nil];
278     [d->m_delegate.get() detachHandle];
279     d->m_delegate = nil;
280 }
281
282 id ResourceHandle::releaseProxy()
283 {
284     id proxy = [[d->m_proxy.get() retain] autorelease];
285     d->m_proxy = nil;
286     [proxy setDelegate:nil];
287     return proxy;
288 }
289
290 NSURLConnection *ResourceHandle::connection() const
291 {
292     return d->m_connection.get();
293 }
294
295 bool ResourceHandle::loadsBlocked()
296 {
297     return false;
298 }
299
300 CFStringRef ResourceHandle::synchronousLoadRunLoopMode()
301 {
302     return CFSTR("WebCoreSynchronousLoaderRunLoopMode");
303 }
304
305 void ResourceHandle::platformLoadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data)
306 {
307     LOG(Network, "ResourceHandle::platformLoadResourceSynchronously:%@ allowStoredCredentials:%u", request.nsURLRequest(DoNotUpdateHTTPBody), storedCredentials);
308
309     ASSERT(!request.isEmpty());
310     
311     OwnPtr<SynchronousLoaderClient> client = SynchronousLoaderClient::create();
312     client->setAllowStoredCredentials(storedCredentials == AllowStoredCredentials);
313
314     RefPtr<ResourceHandle> handle = adoptRef(new ResourceHandle(context, request, client.get(), false /*defersLoading*/, true /*shouldContentSniff*/));
315
316     handle->d->m_storageSession = context->storageSession().platformSession();
317
318     if (context && handle->d->m_scheduledFailureType != NoFailure) {
319         error = context->blockedError(request);
320         return;
321     }
322
323     handle->createNSURLConnection(
324         handle->delegate(), // A synchronous request cannot turn into a download, so there is no need to proxy the delegate.
325         storedCredentials == AllowStoredCredentials,
326         handle->shouldContentSniff() || context->localFileContentSniffingEnabled());
327
328     [handle->connection() scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:(NSString *)synchronousLoadRunLoopMode()];
329     [handle->connection() start];
330     
331     while (!client->isDone())
332         [[NSRunLoop currentRunLoop] runMode:(NSString *)synchronousLoadRunLoopMode() beforeDate:[NSDate distantFuture]];
333
334     error = client->error();
335     
336     [handle->connection() cancel];
337
338     if (error.isNull())
339         response = client->response();
340     else {
341         response = ResourceResponse(request.url(), String(), 0, String(), String());
342         if (error.domain() == String(NSURLErrorDomain))
343             switch (error.errorCode()) {
344             case NSURLErrorUserCancelledAuthentication:
345                 // FIXME: we should really return the actual HTTP response, but sendSynchronousRequest doesn't provide us with one.
346                 response.setHTTPStatusCode(401);
347                 break;
348             default:
349                 response.setHTTPStatusCode(error.errorCode());
350             }
351         else
352             response.setHTTPStatusCode(404);
353     }
354
355     data.swap(client->mutableData());
356 }
357
358 void ResourceHandle::willSendRequest(ResourceRequest& request, const ResourceResponse& redirectResponse)
359 {
360     ASSERT(!redirectResponse.isNull());
361
362     if (redirectResponse.httpStatusCode() == 307) {
363         String lastHTTPMethod = d->m_lastHTTPMethod;
364         if (!equalIgnoringCase(lastHTTPMethod, request.httpMethod())) {
365             request.setHTTPMethod(lastHTTPMethod);
366     
367             FormData* body = d->m_firstRequest.httpBody();
368             if (!equalIgnoringCase(lastHTTPMethod, "GET") && body && !body->isEmpty())
369                 request.setHTTPBody(body);
370
371             String originalContentType = d->m_firstRequest.httpContentType();
372             if (!originalContentType.isEmpty())
373                 request.setHTTPHeaderField("Content-Type", originalContentType);
374         }
375     }
376
377     // Should not set Referer after a redirect from a secure resource to non-secure one.
378     if (!request.url().protocolIs("https") && protocolIs(request.httpReferrer(), "https") && d->m_context->shouldClearReferrerOnHTTPSToHTTPRedirect())
379         request.clearHTTPReferrer();
380
381     const KURL& url = request.url();
382     d->m_user = url.user();
383     d->m_pass = url.pass();
384     d->m_lastHTTPMethod = request.httpMethod();
385     request.removeCredentials();
386
387     if (!protocolHostAndPortAreEqual(request.url(), redirectResponse.url())) {
388         // If the network layer carries over authentication headers from the original request
389         // in a cross-origin redirect, we want to clear those headers here.
390         // As of Lion, CFNetwork no longer does this.
391         request.clearHTTPAuthorization();
392     } else {
393         // Only consider applying authentication credentials if this is actually a redirect and the redirect
394         // URL didn't include credentials of its own.
395         if (d->m_user.isEmpty() && d->m_pass.isEmpty() && !redirectResponse.isNull()) {
396             Credential credential = CredentialStorage::get(request.url());
397             if (!credential.isEmpty()) {
398                 d->m_initialCredential = credential;
399                 
400                 // FIXME: Support Digest authentication, and Proxy-Authorization.
401                 applyBasicAuthorizationHeader(request, d->m_initialCredential);
402             }
403         }
404     }
405
406     if (client()->usesAsyncCallbacks()) {
407         client()->willSendRequestAsync(this, request, redirectResponse);
408     } else {
409         RefPtr<ResourceHandle> protect(this);
410         client()->willSendRequest(this, request, redirectResponse);
411
412         // Client call may not preserve the session, especially if the request is sent over IPC.
413         if (!request.isNull())
414             request.setStorageSession(d->m_storageSession.get());
415     }
416 }
417
418 void ResourceHandle::continueWillSendRequest(const ResourceRequest& request)
419 {
420     ASSERT(client() && client()->usesAsyncCallbacks());
421
422     // Client call may not preserve the session, especially if the request is sent over IPC.
423     ResourceRequest newRequest = request;
424     if (!newRequest.isNull())
425         newRequest.setStorageSession(d->m_storageSession.get());
426     [(id)delegate() continueWillSendRequest:newRequest.nsURLRequest(UpdateHTTPBody)];
427 }
428
429 bool ResourceHandle::shouldUseCredentialStorage()
430 {
431     if (client()->usesAsyncCallbacks()) {
432         if (client())
433             client()->shouldUseCredentialStorageAsync(this);
434         else
435             continueShouldUseCredentialStorage(false);
436         return false; // Ignored by caller.
437     } else
438         return client() && client()->shouldUseCredentialStorage(this);
439 }
440
441 void ResourceHandle::continueShouldUseCredentialStorage(bool useCredentialStorage)
442 {
443     ASSERT(client() && client()->usesAsyncCallbacks());
444
445     [(id)delegate() continueShouldUseCredentialStorage:useCredentialStorage];
446 }
447
448 void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge)
449 {
450     ASSERT(!d->m_currentMacChallenge);
451     ASSERT(d->m_currentWebChallenge.isNull());
452     // Since NSURLConnection networking relies on keeping a reference to the original NSURLAuthenticationChallenge,
453     // we make sure that is actually present
454     ASSERT(challenge.nsURLAuthenticationChallenge());
455
456     // Proxy authentication is handled by CFNetwork internally. We can get here if the user cancels
457     // CFNetwork authentication dialog, and we shouldn't ask the client to display another one in that case.
458     if (challenge.protectionSpace().isProxy()) {
459         // Cannot use receivedRequestToContinueWithoutCredential(), because current challenge is not yet set.
460         [challenge.sender() continueWithoutCredentialForAuthenticationChallenge:challenge.nsURLAuthenticationChallenge()];
461         return;
462     }
463
464     if (!d->m_user.isNull() && !d->m_pass.isNull()) {
465         NSURLCredential *credential = [[NSURLCredential alloc] initWithUser:d->m_user
466                                                                    password:d->m_pass
467                                                                 persistence:NSURLCredentialPersistenceForSession];
468         d->m_currentMacChallenge = challenge.nsURLAuthenticationChallenge();
469         d->m_currentWebChallenge = challenge;
470         receivedCredential(challenge, core(credential));
471         [credential release];
472         // FIXME: Per the specification, the user shouldn't be asked for credentials if there were incorrect ones provided explicitly.
473         d->m_user = String();
474         d->m_pass = String();
475         return;
476     }
477
478     if (!client() || client()->shouldUseCredentialStorage(this)) {
479         if (!d->m_initialCredential.isEmpty() || challenge.previousFailureCount()) {
480             // The stored credential wasn't accepted, stop using it.
481             // There is a race condition here, since a different credential might have already been stored by another ResourceHandle,
482             // but the observable effect should be very minor, if any.
483             CredentialStorage::remove(challenge.protectionSpace());
484         }
485
486         if (!challenge.previousFailureCount()) {
487             Credential credential = CredentialStorage::get(challenge.protectionSpace());
488             if (!credential.isEmpty() && credential != d->m_initialCredential) {
489                 ASSERT(credential.persistence() == CredentialPersistenceNone);
490                 if (challenge.failureResponse().httpStatusCode() == 401) {
491                     // Store the credential back, possibly adding it as a default for this directory.
492                     CredentialStorage::set(credential, challenge.protectionSpace(), challenge.failureResponse().url());
493                 }
494                 [challenge.sender() useCredential:mac(credential) forAuthenticationChallenge:mac(challenge)];
495                 return;
496             }
497         }
498     }
499
500     d->m_currentMacChallenge = challenge.nsURLAuthenticationChallenge();
501     d->m_currentWebChallenge = core(d->m_currentMacChallenge);
502     d->m_currentWebChallenge.setAuthenticationClient(this);
503
504     // FIXME: Several concurrent requests can return with the an authentication challenge for the same protection space.
505     // We should avoid making additional client calls for the same protection space when already waiting for the user,
506     // because typing the same credentials several times is annoying.
507     if (client())
508         client()->didReceiveAuthenticationChallenge(this, d->m_currentWebChallenge);
509 }
510
511 void ResourceHandle::didCancelAuthenticationChallenge(const AuthenticationChallenge& challenge)
512 {
513     ASSERT(d->m_currentMacChallenge);
514     ASSERT(d->m_currentMacChallenge == challenge.nsURLAuthenticationChallenge());
515     ASSERT(!d->m_currentWebChallenge.isNull());
516
517     if (client())
518         client()->didCancelAuthenticationChallenge(this, challenge);
519 }
520
521 #if USE(PROTECTION_SPACE_AUTH_CALLBACK)
522 bool ResourceHandle::canAuthenticateAgainstProtectionSpace(const ProtectionSpace& protectionSpace)
523 {
524     if (client()->usesAsyncCallbacks()) {
525         if (client())
526             client()->canAuthenticateAgainstProtectionSpaceAsync(this, protectionSpace);
527         else
528             continueCanAuthenticateAgainstProtectionSpace(false);
529         return false; // Ignored by caller.
530     } else
531         return client() && client()->canAuthenticateAgainstProtectionSpace(this, protectionSpace);
532 }
533
534 void ResourceHandle::continueCanAuthenticateAgainstProtectionSpace(bool result)
535 {
536     ASSERT(client() && client()->usesAsyncCallbacks());
537
538     [(id)delegate() continueCanAuthenticateAgainstProtectionSpace:result];
539 }
540 #endif
541
542 void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge, const Credential& credential)
543 {
544     LOG(Network, "Handle %p receivedCredential", this);
545
546     ASSERT(!challenge.isNull());
547     if (challenge != d->m_currentWebChallenge)
548         return;
549
550     // FIXME: Support empty credentials. Currently, an empty credential cannot be stored in WebCore credential storage, as that's empty value for its map.
551     if (credential.isEmpty()) {
552         receivedRequestToContinueWithoutCredential(challenge);
553         return;
554     }
555
556     if (credential.persistence() == CredentialPersistenceForSession && (!d->m_needsSiteSpecificQuirks || ![[[mac(challenge) protectionSpace] host] isEqualToString:@"gallery.me.com"])) {
557         // Manage per-session credentials internally, because once NSURLCredentialPersistenceForSession is used, there is no way
558         // to ignore it for a particular request (short of removing it altogether).
559         // <rdar://problem/6867598> gallery.me.com is temporarily whitelisted, so that QuickTime plug-in could see the credentials.
560         Credential webCredential(credential, CredentialPersistenceNone);
561         KURL urlToStore;
562         if (challenge.failureResponse().httpStatusCode() == 401)
563             urlToStore = challenge.failureResponse().url();
564         CredentialStorage::set(webCredential, core([d->m_currentMacChallenge protectionSpace]), urlToStore);
565         [[d->m_currentMacChallenge sender] useCredential:mac(webCredential) forAuthenticationChallenge:d->m_currentMacChallenge];
566     } else
567         [[d->m_currentMacChallenge sender] useCredential:mac(credential) forAuthenticationChallenge:d->m_currentMacChallenge];
568
569     clearAuthentication();
570 }
571
572 void ResourceHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge& challenge)
573 {
574     LOG(Network, "Handle %p receivedRequestToContinueWithoutCredential", this);
575
576     ASSERT(!challenge.isNull());
577     if (challenge != d->m_currentWebChallenge)
578         return;
579
580     [[d->m_currentMacChallenge sender] continueWithoutCredentialForAuthenticationChallenge:d->m_currentMacChallenge];
581
582     clearAuthentication();
583 }
584
585 void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challenge)
586 {
587     LOG(Network, "Handle %p receivedCancellation", this);
588
589     if (challenge != d->m_currentWebChallenge)
590         return;
591
592     if (client())
593         client()->receivedCancellation(this, challenge);
594 }
595
596 void ResourceHandle::continueWillCacheResponse(NSCachedURLResponse *response)
597 {
598     ASSERT(client() && client()->usesAsyncCallbacks());
599
600     [(id)delegate() continueWillCacheResponse:response];
601 }
602
603
604 } // namespace WebCore
605
606 #endif // !USE(CFNETWORK)