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