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