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