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