45d60496a7ac076f22d9a47daf78b395343e33d9
[WebKit-https.git] / Source / WebKit / NetworkProcess / cocoa / NetworkDataTaskCocoa.mm
1 /*
2  * Copyright (C) 2016-2018 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 INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "NetworkDataTaskCocoa.h"
28
29 #import "AuthenticationManager.h"
30 #import "Download.h"
31 #import "DownloadProxyMessages.h"
32 #import "Logging.h"
33 #import "NetworkProcess.h"
34 #import "NetworkSessionCocoa.h"
35 #import "SessionTracker.h"
36 #import "WebCoreArgumentCoders.h"
37 #import <WebCore/AuthenticationChallenge.h>
38 #import <WebCore/FileSystem.h>
39 #import <WebCore/NetworkStorageSession.h>
40 #import <WebCore/NotImplemented.h>
41 #import <WebCore/ResourceRequest.h>
42 #import <pal/spi/cf/CFNetworkSPI.h>
43 #import <wtf/MainThread.h>
44 #import <wtf/ProcessPrivilege.h>
45 #import <wtf/text/Base64.h>
46
47 #if USE(APPLE_INTERNAL_SDK)
48 #import <WebKitAdditions/NetworkDataTaskCocoaAdditions.mm>
49 #endif
50
51 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101400) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 120000)
52 @interface NSURLSessionTask (Staging)
53 @property (nullable, readwrite, retain) NSURL *_siteForCookies;
54 @property (readwrite) BOOL _isTopLevelNavigation;
55 @end
56 #endif
57
58 #if HAVE(NW_ACTIVITY)
59 #import <CFNetwork/CFNSURLConnection.h>
60 #endif
61
62 namespace WebKit {
63
64 #if USE(CREDENTIAL_STORAGE_WITH_NETWORK_SESSION)
65 static void applyBasicAuthorizationHeader(WebCore::ResourceRequest& request, const WebCore::Credential& credential)
66 {
67     String authenticationHeader = "Basic " + base64Encode(String(credential.user() + ":" + credential.password()).utf8());
68     request.setHTTPHeaderField(WebCore::HTTPHeaderName::Authorization, authenticationHeader);
69 }
70 #endif
71
72 static float toNSURLSessionTaskPriority(WebCore::ResourceLoadPriority priority)
73 {
74     switch (priority) {
75     case WebCore::ResourceLoadPriority::VeryLow:
76         return 0;
77     case WebCore::ResourceLoadPriority::Low:
78         return 0.25;
79     case WebCore::ResourceLoadPriority::Medium:
80         return 0.5;
81     case WebCore::ResourceLoadPriority::High:
82         return 0.75;
83     case WebCore::ResourceLoadPriority::VeryHigh:
84         return 1;
85     }
86
87     ASSERT_NOT_REACHED();
88     return NSURLSessionTaskPriorityDefault;
89 }
90
91 void NetworkDataTaskCocoa::applySniffingPoliciesAndBindRequestToInferfaceIfNeeded(NSURLRequest*& nsRequest, bool shouldContentSniff, bool shouldContentEncodingSniff)
92 {
93 #if !PLATFORM(MAC)
94     UNUSED_PARAM(shouldContentEncodingSniff);
95 #elif __MAC_OS_X_VERSION_MIN_REQUIRED < 101302
96     shouldContentEncodingSniff = true;
97 #endif
98     auto& cocoaSession = static_cast<NetworkSessionCocoa&>(m_session.get());
99     if (shouldContentSniff
100         && shouldContentEncodingSniff
101         && cocoaSession.m_boundInterfaceIdentifier.isNull()
102         && !cocoaSession.m_proxyConfiguration)
103         return;
104
105     auto mutableRequest = adoptNS([nsRequest mutableCopy]);
106
107 #if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101302
108     if (!shouldContentEncodingSniff)
109         [mutableRequest _setProperty:@(YES) forKey:(NSString *)kCFURLRequestContentDecoderSkipURLCheck];
110 #endif
111
112     if (!shouldContentSniff)
113         [mutableRequest _setProperty:@(NO) forKey:(NSString *)_kCFURLConnectionPropertyShouldSniff];
114
115     if (!cocoaSession.m_boundInterfaceIdentifier.isNull())
116         [mutableRequest setBoundInterfaceIdentifier:cocoaSession.m_boundInterfaceIdentifier];
117
118     if (cocoaSession.m_proxyConfiguration)
119         CFURLRequestSetProxySettings([mutableRequest _CFURLRequest], cocoaSession.m_proxyConfiguration.get());
120
121     nsRequest = mutableRequest.autorelease();
122 }
123
124 #if HAVE(CFNETWORK_STORAGE_PARTITIONING)
125 NSHTTPCookieStorage *NetworkDataTaskCocoa::statelessCookieStorage()
126 {
127     static NeverDestroyed<RetainPtr<NSHTTPCookieStorage>> statelessCookieStorage;
128     if (!statelessCookieStorage.get()) {
129 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101300)
130 #pragma clang diagnostic push
131 #pragma clang diagnostic ignored "-Wnonnull"
132 #endif
133         statelessCookieStorage.get() = adoptNS([[NSHTTPCookieStorage alloc] _initWithIdentifier:nil private:YES]);
134 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101300)
135 #pragma clang diagnostic pop
136 #endif
137         statelessCookieStorage.get().get().cookieAcceptPolicy = NSHTTPCookieAcceptPolicyNever;
138     }
139     ASSERT(statelessCookieStorage.get().get().cookies.count == 0);
140     return statelessCookieStorage.get().get();
141 }
142
143 void NetworkDataTaskCocoa::applyCookieBlockingPolicy(bool shouldBlock)
144 {
145     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
146
147     if (shouldBlock == m_hasBeenSetToUseStatelessCookieStorage)
148         return;
149
150     NSHTTPCookieStorage *storage = shouldBlock ? statelessCookieStorage(): m_session->networkStorageSession().nsCookieStorage();
151     [m_task performSelector:NSSelectorFromString(@"_setExplicitCookieStorage:") withObject:(NSObject*)storage._cookieStorage];
152     m_hasBeenSetToUseStatelessCookieStorage = shouldBlock;
153 }
154
155 void NetworkDataTaskCocoa::applyCookiePartitioningPolicy(const String& requiredStoragePartition, const String& currentStoragePartition)
156 {
157     // The need for a partion change is according to the following:
158     //      currentStoragePartition:  null  ""    abc
159     // requiredStoragePartition: ""   false false true
160     //                           abc  true  true  false
161     //                           xyz  true  true  true
162     auto shouldChangePartition = !((requiredStoragePartition.isEmpty() && currentStoragePartition.isEmpty()) || currentStoragePartition == requiredStoragePartition);
163     if (shouldChangePartition)
164         m_task.get()._storagePartitionIdentifier = requiredStoragePartition;
165 }
166 #endif
167
168 bool NetworkDataTaskCocoa::isThirdPartyRequest(const WebCore::ResourceRequest& request)
169 {
170     return !WebCore::registrableDomainsAreEqual(request.url(), request.firstPartyForCookies());
171 }
172
173 static void updateTaskWithFirstPartyForSameSiteCookies(NSURLSessionDataTask* task, const WebCore::ResourceRequest& request)
174 {
175     if (request.isSameSiteUnspecified())
176         return;
177 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101400) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 120000)
178     static NSURL *emptyURL = [[NSURL alloc] initWithString:@""];
179     if ([task respondsToSelector:@selector(set_siteForCookies:)])
180         task._siteForCookies = request.isSameSite() ? task.currentRequest.URL : emptyURL;
181     if ([task respondsToSelector:@selector(set_isTopLevelNavigation:)])
182         task._isTopLevelNavigation = request.isTopSite();
183 #else
184     UNUSED_PARAM(task);
185 #endif
186 }
187
188 NetworkDataTaskCocoa::NetworkDataTaskCocoa(NetworkSession& session, NetworkDataTaskClient& client, const WebCore::ResourceRequest& requestWithCredentials, uint64_t frameID, uint64_t pageID, WebCore::StoredCredentialsPolicy storedCredentialsPolicy, WebCore::ContentSniffingPolicy shouldContentSniff, WebCore::ContentEncodingSniffingPolicy shouldContentEncodingSniff, bool shouldClearReferrerOnHTTPSToHTTPRedirect, PreconnectOnly shouldPreconnectOnly, bool dataTaskIsForMainFrameNavigation, std::optional<NetworkActivityTracker> networkActivityTracker)
189     : NetworkDataTask(session, client, requestWithCredentials, storedCredentialsPolicy, shouldClearReferrerOnHTTPSToHTTPRedirect, dataTaskIsForMainFrameNavigation)
190     , m_frameID(frameID)
191     , m_pageID(pageID)
192 {
193     if (m_scheduledFailureType != NoFailure)
194         return;
195
196     auto request = requestWithCredentials;
197     auto url = request.url();
198     if (storedCredentialsPolicy == WebCore::StoredCredentialsPolicy::Use && url.protocolIsInHTTPFamily()) {
199         m_user = url.user();
200         m_password = url.pass();
201         request.removeCredentials();
202         url = request.url();
203     
204 #if USE(CREDENTIAL_STORAGE_WITH_NETWORK_SESSION)
205         if (m_user.isEmpty() && m_password.isEmpty())
206             m_initialCredential = m_session->networkStorageSession().credentialStorage().get(m_partition, url);
207         else
208             m_session->networkStorageSession().credentialStorage().set(m_partition, WebCore::Credential(m_user, m_password, WebCore::CredentialPersistenceNone), url);
209 #endif
210     }
211
212 #if USE(CREDENTIAL_STORAGE_WITH_NETWORK_SESSION)
213     if (!m_initialCredential.isEmpty()) {
214         // FIXME: Support Digest authentication, and Proxy-Authorization.
215         applyBasicAuthorizationHeader(request, m_initialCredential);
216     }
217 #endif
218
219     bool shouldBlockCookies = false;
220 #if HAVE(CFNETWORK_STORAGE_PARTITIONING)
221     shouldBlockCookies = session.networkStorageSession().shouldBlockCookies(request);
222 #endif
223     if (shouldBlockCookies || (m_session->sessionID().isEphemeral() && isThirdPartyRequest(request)))
224         request.setExistingHTTPReferrerToOriginString();
225
226     NSURLRequest *nsRequest = request.nsURLRequest(WebCore::UpdateHTTPBody);
227     applySniffingPoliciesAndBindRequestToInferfaceIfNeeded(nsRequest, shouldContentSniff == WebCore::SniffContent && !url.isLocalFile(), shouldContentEncodingSniff == WebCore::ContentEncodingSniffingPolicy::Sniff);
228 #if ENABLE(WIFI_ASSERTIONS)
229     applyAdditionalProperties(request, *this, nsRequest);
230 #endif
231
232     auto& cocoaSession = static_cast<NetworkSessionCocoa&>(m_session.get());
233     if (storedCredentialsPolicy == WebCore::StoredCredentialsPolicy::Use) {
234         m_task = [cocoaSession.m_sessionWithCredentialStorage dataTaskWithRequest:nsRequest];
235         ASSERT(!cocoaSession.m_dataTaskMapWithCredentials.contains([m_task taskIdentifier]));
236         cocoaSession.m_dataTaskMapWithCredentials.add([m_task taskIdentifier], this);
237         LOG(NetworkSession, "%llu Creating stateless NetworkDataTask with URL %s", [m_task taskIdentifier], nsRequest.URL.absoluteString.UTF8String);
238     } else {
239         m_task = [cocoaSession.m_statelessSession dataTaskWithRequest:nsRequest];
240         ASSERT(!cocoaSession.m_dataTaskMapWithoutState.contains([m_task taskIdentifier]));
241         cocoaSession.m_dataTaskMapWithoutState.add([m_task taskIdentifier], this);
242         LOG(NetworkSession, "%llu Creating NetworkDataTask with URL %s", [m_task taskIdentifier], nsRequest.URL.absoluteString.UTF8String);
243     }
244
245     if (shouldPreconnectOnly == PreconnectOnly::Yes) {
246 #if ENABLE(SERVER_PRECONNECT)
247         m_task.get()._preconnect = true;
248 #else
249         ASSERT_NOT_REACHED();
250 #endif
251     }
252
253 #if HAVE(CFNETWORK_STORAGE_PARTITIONING)
254     if (shouldBlockCookies) {
255 #if HAVE(CFNETWORK_STORAGE_PARTITIONING) && !RELEASE_LOG_DISABLED
256         if (NetworkProcess::singleton().shouldLogCookieInformation())
257             RELEASE_LOG_IF(isAlwaysOnLoggingAllowed(), Network, "%p - NetworkDataTaskCocoa::logCookieInformation: pageID = %llu, frameID = %llu, taskID = %lu: Blocking cookies for URL %s", this, pageID, frameID, (unsigned long)[m_task taskIdentifier], nsRequest.URL.absoluteString.UTF8String);
258 #else
259         LOG(NetworkSession, "%llu Blocking cookies for URL %s", [m_task taskIdentifier], nsRequest.URL.absoluteString.UTF8String);
260 #endif
261         applyCookieBlockingPolicy(shouldBlockCookies);
262     } else {
263         auto storagePartition = session.networkStorageSession().cookieStoragePartition(request, m_frameID, m_pageID);
264         if (!storagePartition.isEmpty()) {
265 #if HAVE(CFNETWORK_STORAGE_PARTITIONING) && !RELEASE_LOG_DISABLED
266             if (NetworkProcess::singleton().shouldLogCookieInformation())
267                 RELEASE_LOG_IF(isAlwaysOnLoggingAllowed(), Network, "%p - NetworkDataTaskCocoa::logCookieInformation: pageID = %llu, frameID = %llu, taskID = %lu: Partitioning cookies for URL %s", this, pageID, frameID, (unsigned long)[m_task taskIdentifier], nsRequest.URL.absoluteString.UTF8String);
268 #else
269             LOG(NetworkSession, "%llu Partitioning cookies for URL %s", [m_task taskIdentifier], nsRequest.URL.absoluteString.UTF8String);
270 #endif
271             applyCookiePartitioningPolicy(storagePartition, emptyString());
272         }
273     }
274 #endif
275
276     if (WebCore::ResourceRequest::resourcePrioritiesEnabled())
277         m_task.get().priority = toNSURLSessionTaskPriority(request.priority());
278
279     updateTaskWithFirstPartyForSameSiteCookies(m_task.get(), request);
280
281 #if HAVE(NW_ACTIVITY)
282     if (networkActivityTracker)
283         m_task.get()._nw_activity = networkActivityTracker.value().getPlatformObject();
284 #endif
285 }
286
287 NetworkDataTaskCocoa::~NetworkDataTaskCocoa()
288 {
289     if (!m_task)
290         return;
291
292     auto& cocoaSession = static_cast<NetworkSessionCocoa&>(m_session.get());
293     if (m_storedCredentialsPolicy == WebCore::StoredCredentialsPolicy::Use) {
294         ASSERT(cocoaSession.m_dataTaskMapWithCredentials.get([m_task taskIdentifier]) == this);
295         cocoaSession.m_dataTaskMapWithCredentials.remove([m_task taskIdentifier]);
296     } else {
297         ASSERT(cocoaSession.m_dataTaskMapWithoutState.get([m_task taskIdentifier]) == this);
298         cocoaSession.m_dataTaskMapWithoutState.remove([m_task taskIdentifier]);
299     }
300 }
301
302 void NetworkDataTaskCocoa::didSendData(uint64_t totalBytesSent, uint64_t totalBytesExpectedToSend)
303 {
304     if (m_client)
305         m_client->didSendData(totalBytesSent, totalBytesExpectedToSend);
306 }
307
308 void NetworkDataTaskCocoa::didReceiveChallenge(const WebCore::AuthenticationChallenge& challenge, ChallengeCompletionHandler&& completionHandler)
309 {
310     if (tryPasswordBasedAuthentication(challenge, completionHandler))
311         return;
312
313     if (m_client)
314         m_client->didReceiveChallenge(challenge, WTFMove(completionHandler));
315     else {
316         ASSERT_NOT_REACHED();
317         completionHandler(AuthenticationChallengeDisposition::PerformDefaultHandling, { });
318     }
319 }
320
321 void NetworkDataTaskCocoa::didCompleteWithError(const WebCore::ResourceError& error, const WebCore::NetworkLoadMetrics& networkLoadMetrics)
322 {
323     if (m_client)
324         m_client->didCompleteWithError(error, networkLoadMetrics);
325 }
326
327 void NetworkDataTaskCocoa::didReceiveData(Ref<WebCore::SharedBuffer>&& data)
328 {
329     if (m_client)
330         m_client->didReceiveData(WTFMove(data));
331 }
332
333 void NetworkDataTaskCocoa::willPerformHTTPRedirection(WebCore::ResourceResponse&& redirectResponse, WebCore::ResourceRequest&& request, RedirectCompletionHandler&& completionHandler)
334 {
335     if (redirectResponse.httpStatusCode() == 307 || redirectResponse.httpStatusCode() == 308) {
336         ASSERT(m_lastHTTPMethod == request.httpMethod());
337         WebCore::FormData* body = m_firstRequest.httpBody();
338         if (body && !body->isEmpty() && !equalLettersIgnoringASCIICase(m_lastHTTPMethod, "get"))
339             request.setHTTPBody(body);
340         
341         String originalContentType = m_firstRequest.httpContentType();
342         if (!originalContentType.isEmpty())
343             request.setHTTPHeaderField(WebCore::HTTPHeaderName::ContentType, originalContentType);
344     }
345     
346     // Should not set Referer after a redirect from a secure resource to non-secure one.
347     if (m_shouldClearReferrerOnHTTPSToHTTPRedirect && !request.url().protocolIs("https") && WebCore::protocolIs(request.httpReferrer(), "https"))
348         request.clearHTTPReferrer();
349     
350     const auto& url = request.url();
351     m_user = url.user();
352     m_password = url.pass();
353     m_lastHTTPMethod = request.httpMethod();
354     request.removeCredentials();
355     
356     if (!protocolHostAndPortAreEqual(request.url(), redirectResponse.url())) {
357         // The network layer might carry over some headers from the original request that
358         // we want to strip here because the redirect is cross-origin.
359         request.clearHTTPAuthorization();
360         request.clearHTTPOrigin();
361 #if USE(CREDENTIAL_STORAGE_WITH_NETWORK_SESSION)
362     } else {
363         // Only consider applying authentication credentials if this is actually a redirect and the redirect
364         // URL didn't include credentials of its own.
365         if (m_user.isEmpty() && m_password.isEmpty() && !redirectResponse.isNull()) {
366             auto credential = m_session->networkStorageSession().credentialStorage().get(m_partition, request.url());
367             if (!credential.isEmpty()) {
368                 m_initialCredential = credential;
369
370                 // FIXME: Support Digest authentication, and Proxy-Authorization.
371                 applyBasicAuthorizationHeader(request, m_initialCredential);
372             }
373         }
374 #endif
375     }
376
377     if (isTopLevelNavigation())
378         request.setFirstPartyForCookies(request.url());
379
380     bool shouldBlockCookies = false;
381 #if HAVE(CFNETWORK_STORAGE_PARTITIONING)
382     shouldBlockCookies = m_session->networkStorageSession().shouldBlockCookies(request);
383 #if !RELEASE_LOG_DISABLED
384     if (NetworkProcess::singleton().shouldLogCookieInformation())
385         RELEASE_LOG_IF(isAlwaysOnLoggingAllowed(), Network, "%p - NetworkDataTaskCocoa::willPerformHTTPRedirection::logCookieInformation: pageID = %llu, frameID = %llu, taskID = %lu: %s cookies for redirect URL %s", this, m_pageID, m_frameID, (unsigned long)[m_task taskIdentifier], (shouldBlockCookies ? "Blocking" : "Not blocking"), request.url().string().utf8().data());
386 #else
387     LOG(NetworkSession, "%llu %s cookies for redirect URL %s", [m_task taskIdentifier], (shouldBlockCookies ? "Blocking" : "Not blocking"), request.url().string().utf8().data());
388 #endif
389 #endif
390
391     if (shouldBlockCookies || (m_session->sessionID().isEphemeral() && isThirdPartyRequest(request)))
392         request.setExistingHTTPReferrerToOriginString();
393
394 #if HAVE(CFNETWORK_STORAGE_PARTITIONING)
395     // Always apply the policy since blocking may need to be turned on or off in a redirect.
396     applyCookieBlockingPolicy(shouldBlockCookies);
397
398     if (!shouldBlockCookies) {
399         auto requiredStoragePartition = m_session->networkStorageSession().cookieStoragePartition(request, m_frameID, m_pageID);
400 #if !RELEASE_LOG_DISABLED
401         if (NetworkProcess::singleton().shouldLogCookieInformation())
402             RELEASE_LOG_IF(isAlwaysOnLoggingAllowed(), Network, "%p - NetworkDataTaskCocoa::willPerformHTTPRedirection::logCookieInformation: pageID = %llu, frameID = %llu, taskID = %lu: %s cookies for redirect URL %s", this, m_pageID, m_frameID, (unsigned long)[m_task taskIdentifier], (requiredStoragePartition.isEmpty() ? "Not partitioning" : "Partitioning"), request.url().string().utf8().data());
403 #else
404         LOG(NetworkSession, "%llu %s cookies for redirect URL %s", [m_task taskIdentifier], (requiredStoragePartition.isEmpty() ? "Not partitioning" : "Partitioning"), request.url().string().utf8().data());
405 #endif
406         applyCookiePartitioningPolicy(requiredStoragePartition, m_task.get()._storagePartitionIdentifier);
407     }
408 #endif
409
410     updateTaskWithFirstPartyForSameSiteCookies(m_task.get(), request);
411
412     if (m_client)
413         m_client->willPerformHTTPRedirection(WTFMove(redirectResponse), WTFMove(request), WTFMove(completionHandler));
414     else {
415         ASSERT_NOT_REACHED();
416         completionHandler({ });
417     }
418 }
419
420 void NetworkDataTaskCocoa::setPendingDownloadLocation(const WTF::String& filename, SandboxExtension::Handle&& sandboxExtensionHandle, bool allowOverwrite)
421 {
422     NetworkDataTask::setPendingDownloadLocation(filename, { }, allowOverwrite);
423
424     ASSERT(!m_sandboxExtension);
425     m_sandboxExtension = SandboxExtension::create(WTFMove(sandboxExtensionHandle));
426     if (m_sandboxExtension)
427         m_sandboxExtension->consume();
428
429     m_task.get()._pathToDownloadTaskFile = m_pendingDownloadLocation;
430
431     if (allowOverwrite && WebCore::FileSystem::fileExists(m_pendingDownloadLocation))
432         WebCore::FileSystem::deleteFile(filename);
433 }
434
435 bool NetworkDataTaskCocoa::tryPasswordBasedAuthentication(const WebCore::AuthenticationChallenge& challenge, ChallengeCompletionHandler& completionHandler)
436 {
437     if (!challenge.protectionSpace().isPasswordBased())
438         return false;
439     
440     if (!m_user.isNull() && !m_password.isNull()) {
441         auto persistence = m_storedCredentialsPolicy == WebCore::StoredCredentialsPolicy::Use ? WebCore::CredentialPersistenceForSession : WebCore::CredentialPersistenceNone;
442         completionHandler(AuthenticationChallengeDisposition::UseCredential, WebCore::Credential(m_user, m_password, persistence));
443         m_user = String();
444         m_password = String();
445         return true;
446     }
447
448 #if USE(CREDENTIAL_STORAGE_WITH_NETWORK_SESSION)
449     if (m_storedCredentialsPolicy == WebCore::StoredCredentialsPolicy::Use) {
450         if (!m_initialCredential.isEmpty() || challenge.previousFailureCount()) {
451             // The stored credential wasn't accepted, stop using it.
452             // There is a race condition here, since a different credential might have already been stored by another ResourceHandle,
453             // but the observable effect should be very minor, if any.
454             m_session->networkStorageSession().credentialStorage().remove(m_partition, challenge.protectionSpace());
455         }
456
457         if (!challenge.previousFailureCount()) {
458             auto credential = m_session->networkStorageSession().credentialStorage().get(m_partition, challenge.protectionSpace());
459             if (!credential.isEmpty() && credential != m_initialCredential) {
460                 ASSERT(credential.persistence() == WebCore::CredentialPersistenceNone);
461                 if (challenge.failureResponse().httpStatusCode() == 401) {
462                     // Store the credential back, possibly adding it as a default for this directory.
463                     m_session->networkStorageSession().credentialStorage().set(m_partition, credential, challenge.protectionSpace(), challenge.failureResponse().url());
464                 }
465                 completionHandler(AuthenticationChallengeDisposition::UseCredential, credential);
466                 return true;
467             }
468         }
469     }
470 #endif
471
472     if (!challenge.proposedCredential().isEmpty() && !challenge.previousFailureCount()) {
473         completionHandler(AuthenticationChallengeDisposition::UseCredential, challenge.proposedCredential());
474         return true;
475     }
476     
477     return false;
478 }
479
480 void NetworkDataTaskCocoa::transferSandboxExtensionToDownload(Download& download)
481 {
482     download.setSandboxExtension(WTFMove(m_sandboxExtension));
483 }
484
485 String NetworkDataTaskCocoa::suggestedFilename() const
486 {
487     if (!m_suggestedFilename.isEmpty())
488         return m_suggestedFilename;
489     return m_task.get().response.suggestedFilename;
490 }
491
492 void NetworkDataTaskCocoa::cancel()
493 {
494     [m_task cancel];
495 }
496
497 void NetworkDataTaskCocoa::resume()
498 {
499     if (m_scheduledFailureType != NoFailure)
500         m_failureTimer.startOneShot(0_s);
501     [m_task resume];
502 }
503
504 void NetworkDataTaskCocoa::suspend()
505 {
506     if (m_failureTimer.isActive())
507         m_failureTimer.stop();
508     [m_task suspend];
509 }
510
511 NetworkDataTask::State NetworkDataTaskCocoa::state() const
512 {
513     switch ([m_task state]) {
514     case NSURLSessionTaskStateRunning:
515         return State::Running;
516     case NSURLSessionTaskStateSuspended:
517         return State::Suspended;
518     case NSURLSessionTaskStateCanceling:
519         return State::Canceling;
520     case NSURLSessionTaskStateCompleted:
521         return State::Completed;
522     }
523
524     ASSERT_NOT_REACHED();
525     return State::Completed;
526 }
527
528 WebCore::Credential serverTrustCredential(const WebCore::AuthenticationChallenge& challenge)
529 {
530     return WebCore::Credential([NSURLCredential credentialForTrust:challenge.nsURLAuthenticationChallenge().protectionSpace.serverTrust]);
531 }
532
533 bool NetworkDataTaskCocoa::isAlwaysOnLoggingAllowed() const
534 {
535     if (NetworkProcess::singleton().sessionIsControlledByAutomation(m_session->sessionID()))
536         return true;
537
538     return m_session->sessionID().isAlwaysOnLoggingAllowed();
539 }
540
541 String NetworkDataTaskCocoa::description() const
542 {
543     return String([m_task description]);
544 }
545
546 }