5ad21344a90a6b115d12c325ba3a9f685e7d05bf
[WebKit.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 "DeviceManagementSPI.h"
32 #import "Download.h"
33 #import "DownloadProxyMessages.h"
34 #import "Logging.h"
35 #import "NetworkProcess.h"
36 #import "NetworkSessionCocoa.h"
37 #import "WebCoreArgumentCoders.h"
38 #import <WebCore/AuthenticationChallenge.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/BlockPtr.h>
44 #import <wtf/FileSystem.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 !USE(CFNETWORK_CONTENT_ENCODING_SNIFFING_OVERRIDE)
85     UNUSED_PARAM(shouldContentEncodingSniff);
86 #endif
87
88     auto& cocoaSession = static_cast<NetworkSessionCocoa&>(*m_session);
89     if (shouldContentSniff
90 #if USE(CFNETWORK_CONTENT_ENCODING_SNIFFING_OVERRIDE)
91         && shouldContentEncodingSniff
92 #endif
93         && cocoaSession.m_boundInterfaceIdentifier.isNull()
94         && !cocoaSession.m_proxyConfiguration)
95         return;
96
97     auto mutableRequest = adoptNS([nsRequest mutableCopy]);
98
99 #if USE(CFNETWORK_CONTENT_ENCODING_SNIFFING_OVERRIDE)
100     if (!shouldContentEncodingSniff)
101         [mutableRequest _setProperty:@YES forKey:(NSString *)kCFURLRequestContentDecoderSkipURLCheck];
102 #endif
103
104     if (!shouldContentSniff)
105         [mutableRequest _setProperty:@NO forKey:(NSString *)_kCFURLConnectionPropertyShouldSniff];
106
107     if (!cocoaSession.m_boundInterfaceIdentifier.isNull())
108         [mutableRequest setBoundInterfaceIdentifier:cocoaSession.m_boundInterfaceIdentifier];
109
110     if (cocoaSession.m_proxyConfiguration)
111         CFURLRequestSetProxySettings([mutableRequest _CFURLRequest], cocoaSession.m_proxyConfiguration.get());
112
113     nsRequest = mutableRequest.autorelease();
114 }
115
116 #if ENABLE(RESOURCE_LOAD_STATISTICS)
117 NSHTTPCookieStorage *NetworkDataTaskCocoa::statelessCookieStorage()
118 {
119     static NeverDestroyed<RetainPtr<NSHTTPCookieStorage>> statelessCookieStorage;
120     if (!statelessCookieStorage.get()) {
121 #if HAVE(NSHTTPCOOKIESTORAGE__INITWITHIDENTIFIER_WITH_INACCURATE_NULLABILITY)
122         IGNORE_NULL_CHECK_WARNINGS_BEGIN
123 #endif
124         statelessCookieStorage.get() = adoptNS([[NSHTTPCookieStorage alloc] _initWithIdentifier:nil private:YES]);
125 #if HAVE(NSHTTPCOOKIESTORAGE__INITWITHIDENTIFIER_WITH_INACCURATE_NULLABILITY)
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::blockCookies()
135 {
136     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
137
138     if (m_hasBeenSetToUseStatelessCookieStorage)
139         return;
140
141     [m_task _setExplicitCookieStorage:statelessCookieStorage()._cookieStorage];
142     m_hasBeenSetToUseStatelessCookieStorage = true;
143 }
144 #endif
145
146 bool NetworkDataTaskCocoa::isThirdPartyRequest(const WebCore::ResourceRequest& request)
147 {
148     return !WebCore::areRegistrableDomainsEqual(request.url(), request.firstPartyForCookies());
149 }
150
151 static void updateTaskWithFirstPartyForSameSiteCookies(NSURLSessionDataTask* task, const WebCore::ResourceRequest& request)
152 {
153     if (request.isSameSiteUnspecified())
154         return;
155 #if HAVE(FOUNDATION_WITH_SAME_SITE_COOKIE_SUPPORT)
156     static NSURL *emptyURL = [[NSURL alloc] initWithString:@""];
157     task._siteForCookies = request.isSameSite() ? task.currentRequest.URL : emptyURL;
158     task._isTopLevelNavigation = request.isTopSite();
159 #else
160     UNUSED_PARAM(task);
161 #endif
162 }
163
164 static inline bool computeIsAlwaysOnLoggingAllowed(NetworkSession& session)
165 {
166     if (session.networkProcess().sessionIsControlledByAutomation(session.sessionID()))
167         return true;
168
169     return session.sessionID().isAlwaysOnLoggingAllowed();
170 }
171
172 NetworkDataTaskCocoa::NetworkDataTaskCocoa(NetworkSession& session, NetworkDataTaskClient& client, const WebCore::ResourceRequest& requestWithCredentials, uint64_t frameID, WebCore::PageIdentifier pageID, WebCore::StoredCredentialsPolicy storedCredentialsPolicy, WebCore::ContentSniffingPolicy shouldContentSniff, WebCore::ContentEncodingSniffingPolicy shouldContentEncodingSniff, bool shouldClearReferrerOnHTTPSToHTTPRedirect, PreconnectOnly shouldPreconnectOnly, bool dataTaskIsForMainFrameNavigation, bool dataTaskIsForMainResourceNavigationForAnyFrame, Optional<NetworkActivityTracker> networkActivityTracker)
173     : NetworkDataTask(session, client, requestWithCredentials, storedCredentialsPolicy, shouldClearReferrerOnHTTPSToHTTPRedirect, dataTaskIsForMainFrameNavigation)
174     , m_frameID(frameID)
175     , m_pageID(pageID)
176     , m_isForMainResourceNavigationForAnyFrame(dataTaskIsForMainResourceNavigationForAnyFrame)
177     , m_isAlwaysOnLoggingAllowed(computeIsAlwaysOnLoggingAllowed(session))
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 (auto* storageSession = m_session->networkStorageSession()) {
192             if (m_user.isEmpty() && m_password.isEmpty())
193                 m_initialCredential = storageSession->credentialStorage().get(m_partition, url);
194             else
195                 storageSession->credentialStorage().set(m_partition, WebCore::Credential(m_user, m_password, WebCore::CredentialPersistenceNone), url);
196         }
197 #endif
198     }
199
200 #if USE(CREDENTIAL_STORAGE_WITH_NETWORK_SESSION)
201     if (!m_initialCredential.isEmpty()) {
202         // FIXME: Support Digest authentication, and Proxy-Authorization.
203         applyBasicAuthorizationHeader(request, m_initialCredential);
204     }
205 #endif
206
207     bool shouldBlockCookies = false;
208 #if ENABLE(RESOURCE_LOAD_STATISTICS)
209     shouldBlockCookies = storedCredentialsPolicy == WebCore::StoredCredentialsPolicy::EphemeralStatelessCookieless
210         || (session.networkStorageSession() && session.networkStorageSession()->shouldBlockCookies(request, frameID, pageID));
211 #endif
212     restrictRequestReferrerToOriginIfNeeded(request, shouldBlockCookies);
213
214     NSURLRequest *nsRequest = request.nsURLRequest(WebCore::HTTPBodyUpdatePolicy::UpdateHTTPBody);
215     applySniffingPoliciesAndBindRequestToInferfaceIfNeeded(nsRequest, shouldContentSniff == WebCore::ContentSniffingPolicy::SniffContent && !url.isLocalFile(), shouldContentEncodingSniff == WebCore::ContentEncodingSniffingPolicy::Sniff);
216
217     auto& cocoaSession = static_cast<NetworkSessionCocoa&>(*m_session);
218     switch (storedCredentialsPolicy) {
219     case 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 stateful NetworkDataTask with URL %s", [m_task taskIdentifier], nsRequest.URL.absoluteString.UTF8String);
224         break;
225     case WebCore::StoredCredentialsPolicy::DoNotUse:
226         m_task = [cocoaSession.m_statelessSession dataTaskWithRequest:nsRequest];
227         ASSERT(!cocoaSession.m_dataTaskMapWithoutState.contains([m_task taskIdentifier]));
228         cocoaSession.m_dataTaskMapWithoutState.add([m_task taskIdentifier], this);
229         LOG(NetworkSession, "%llu Creating stateless NetworkDataTask with URL %s", [m_task taskIdentifier], nsRequest.URL.absoluteString.UTF8String);
230         break;
231     case WebCore::StoredCredentialsPolicy::EphemeralStatelessCookieless:
232         if (!cocoaSession.m_ephemeralStatelessCookielessSession)
233             cocoaSession.initializeEphemeralStatelessCookielessSession();
234         m_task = [cocoaSession.m_ephemeralStatelessCookielessSession dataTaskWithRequest:nsRequest];
235         ASSERT(!cocoaSession.m_dataTaskMapEphemeralStatelessCookieless.contains([m_task taskIdentifier]));
236         cocoaSession.m_dataTaskMapEphemeralStatelessCookieless.add([m_task taskIdentifier], this);
237         LOG(NetworkSession, "%llu Creating ephemeral, stateless, cookieless NetworkDataTask with URL %s", [m_task taskIdentifier], nsRequest.URL.absoluteString.UTF8String);
238         break;
239     }
240
241     if (shouldPreconnectOnly == PreconnectOnly::Yes) {
242 #if ENABLE(SERVER_PRECONNECT)
243         m_task.get()._preconnect = true;
244 #else
245         ASSERT_NOT_REACHED();
246 #endif
247     }
248
249 #if ENABLE(RESOURCE_LOAD_STATISTICS)
250     if (shouldBlockCookies) {
251 #if !RELEASE_LOG_DISABLED
252         if (m_session->shouldLogCookieInformation())
253             RELEASE_LOG_IF(isAlwaysOnLoggingAllowed(), Network, "%p - NetworkDataTaskCocoa::logCookieInformation: pageID = %llu, frameID = %llu, taskID = %lu: Blocking cookies for URL %s", this, pageID.toUInt64(), frameID, (unsigned long)[m_task taskIdentifier], nsRequest.URL.absoluteString.UTF8String);
254 #else
255         LOG(NetworkSession, "%llu Blocking cookies for URL %s", [m_task taskIdentifier], nsRequest.URL.absoluteString.UTF8String);
256 #endif
257         blockCookies();
258     }
259 #endif
260
261     if (WebCore::ResourceRequest::resourcePrioritiesEnabled())
262         m_task.get().priority = toNSURLSessionTaskPriority(request.priority());
263
264     updateTaskWithFirstPartyForSameSiteCookies(m_task.get(), request);
265
266 #if HAVE(NW_ACTIVITY)
267     if (networkActivityTracker)
268         m_task.get()._nw_activity = networkActivityTracker.value().getPlatformObject();
269 #endif
270 }
271
272 NetworkDataTaskCocoa::~NetworkDataTaskCocoa()
273 {
274     if (!m_task || !m_session)
275         return;
276
277     auto& cocoaSession = static_cast<NetworkSessionCocoa&>(*m_session);
278     switch (m_storedCredentialsPolicy) {
279     case WebCore::StoredCredentialsPolicy::Use:
280         ASSERT(cocoaSession.m_dataTaskMapWithCredentials.get([m_task taskIdentifier]) == this);
281         cocoaSession.m_dataTaskMapWithCredentials.remove([m_task taskIdentifier]);
282         break;
283     case WebCore::StoredCredentialsPolicy::DoNotUse:
284         ASSERT(cocoaSession.m_dataTaskMapWithoutState.get([m_task taskIdentifier]) == this);
285         cocoaSession.m_dataTaskMapWithoutState.remove([m_task taskIdentifier]);
286         break;
287     case WebCore::StoredCredentialsPolicy::EphemeralStatelessCookieless:
288         ASSERT(cocoaSession.m_dataTaskMapEphemeralStatelessCookieless.get([m_task taskIdentifier]) == this);
289         cocoaSession.m_dataTaskMapEphemeralStatelessCookieless.remove([m_task taskIdentifier]);
290         break;
291     }
292 }
293
294 void NetworkDataTaskCocoa::restrictRequestReferrerToOriginIfNeeded(WebCore::ResourceRequest& request, bool shouldBlockCookies)
295 {
296     if (shouldBlockCookies || (m_session->sessionID().isEphemeral() && isThirdPartyRequest(request)))
297         request.setExistingHTTPReferrerToOriginString();
298 }
299
300 void NetworkDataTaskCocoa::didSendData(uint64_t totalBytesSent, uint64_t totalBytesExpectedToSend)
301 {
302     if (m_client)
303         m_client->didSendData(totalBytesSent, totalBytesExpectedToSend);
304 }
305
306 void NetworkDataTaskCocoa::didReceiveChallenge(WebCore::AuthenticationChallenge&& challenge, ChallengeCompletionHandler&& completionHandler)
307 {
308     if (tryPasswordBasedAuthentication(challenge, completionHandler))
309         return;
310
311     if (m_client)
312         m_client->didReceiveChallenge(WTFMove(challenge), WTFMove(completionHandler));
313     else {
314         ASSERT_NOT_REACHED();
315         completionHandler(AuthenticationChallengeDisposition::PerformDefaultHandling, { });
316     }
317 }
318
319 void NetworkDataTaskCocoa::didCompleteWithError(const WebCore::ResourceError& error, const WebCore::NetworkLoadMetrics& networkLoadMetrics)
320 {
321     if (m_client)
322         m_client->didCompleteWithError(error, networkLoadMetrics);
323 }
324
325 void NetworkDataTaskCocoa::didReceiveData(Ref<WebCore::SharedBuffer>&& data)
326 {
327     if (m_client)
328         m_client->didReceiveData(WTFMove(data));
329 }
330
331 void NetworkDataTaskCocoa::willPerformHTTPRedirection(WebCore::ResourceResponse&& redirectResponse, WebCore::ResourceRequest&& request, RedirectCompletionHandler&& completionHandler)
332 {
333     if (redirectResponse.httpStatusCode() == 307 || redirectResponse.httpStatusCode() == 308) {
334         ASSERT(m_lastHTTPMethod == request.httpMethod());
335         WebCore::FormData* body = m_firstRequest.httpBody();
336         if (body && !body->isEmpty() && !equalLettersIgnoringASCIICase(m_lastHTTPMethod, "get"))
337             request.setHTTPBody(body);
338         
339         String originalContentType = m_firstRequest.httpContentType();
340         if (!originalContentType.isEmpty())
341             request.setHTTPHeaderField(WebCore::HTTPHeaderName::ContentType, originalContentType);
342     } else if (redirectResponse.httpStatusCode() == 303 && equalLettersIgnoringASCIICase(m_firstRequest.httpMethod(), "head")) // FIXME: (rdar://problem/13706454).
343         request.setHTTPMethod("HEAD"_s);
344     
345     // Should not set Referer after a redirect from a secure resource to non-secure one.
346     if (m_shouldClearReferrerOnHTTPSToHTTPRedirect && !request.url().protocolIs("https") && WTF::protocolIs(request.httpReferrer(), "https"))
347         request.clearHTTPReferrer();
348     
349     const auto& url = request.url();
350     m_user = url.user();
351     m_password = url.pass();
352     m_lastHTTPMethod = request.httpMethod();
353     request.removeCredentials();
354     
355     if (!protocolHostAndPortAreEqual(request.url(), redirectResponse.url())) {
356         // The network layer might carry over some headers from the original request that
357         // we want to strip here because the redirect is cross-origin.
358         request.clearHTTPAuthorization();
359         request.clearHTTPOrigin();
360 #if USE(CREDENTIAL_STORAGE_WITH_NETWORK_SESSION)
361     } else {
362         // Only consider applying authentication credentials if this is actually a redirect and the redirect
363         // URL didn't include credentials of its own.
364         if (m_user.isEmpty() && m_password.isEmpty() && !redirectResponse.isNull()) {
365             auto credential = m_session->networkStorageSession() ? m_session->networkStorageSession()->credentialStorage().get(m_partition, request.url()) : WebCore::Credential();
366             if (!credential.isEmpty()) {
367                 m_initialCredential = credential;
368
369                 // FIXME: Support Digest authentication, and Proxy-Authorization.
370                 applyBasicAuthorizationHeader(request, m_initialCredential);
371             }
372         }
373 #endif
374     }
375
376     if (isTopLevelNavigation())
377         request.setFirstPartyForCookies(request.url());
378
379 #if ENABLE(RESOURCE_LOAD_STATISTICS)
380     if (!m_hasBeenSetToUseStatelessCookieStorage) {
381         if (m_storedCredentialsPolicy == WebCore::StoredCredentialsPolicy::EphemeralStatelessCookieless
382             || (m_session->networkStorageSession() && m_session->networkStorageSession()->shouldBlockCookies(request, m_frameID, m_pageID)))
383             blockCookies();
384     }
385 #if !RELEASE_LOG_DISABLED
386     if (m_session->shouldLogCookieInformation())
387         RELEASE_LOG_IF(isAlwaysOnLoggingAllowed(), Network, "%p - NetworkDataTaskCocoa::willPerformHTTPRedirection::logCookieInformation: pageID = %llu, frameID = %llu, taskID = %lu: %s cookies for redirect URL %s", this, m_pageID.toUInt64(), m_frameID, (unsigned long)[m_task taskIdentifier], (m_hasBeenSetToUseStatelessCookieStorage ? "Blocking" : "Not blocking"), request.url().string().utf8().data());
388 #else
389     LOG(NetworkSession, "%llu %s cookies for redirect URL %s", [m_task taskIdentifier], (m_hasBeenSetToUseStatelessCookieStorage ? "Blocking" : "Not blocking"), request.url().string().utf8().data());
390 #endif
391 #endif
392
393     updateTaskWithFirstPartyForSameSiteCookies(m_task.get(), request);
394
395     if (m_client)
396         m_client->willPerformHTTPRedirection(WTFMove(redirectResponse), WTFMove(request), [completionHandler = WTFMove(completionHandler), this, weakThis = makeWeakPtr(*this)] (auto&& request) mutable {
397             if (!weakThis)
398                 return completionHandler({ });
399             if (!request.isNull()) {
400 #if ENABLE(RESOURCE_LOAD_STATISTICS)
401                 bool shouldBlockCookies = m_session->networkStorageSession() && m_session->networkStorageSession()->shouldBlockCookies(request, m_frameID, m_pageID);
402 #else
403                 bool shouldBlockCookies = false;
404 #endif
405                 restrictRequestReferrerToOriginIfNeeded(request, shouldBlockCookies);
406             }
407             completionHandler(WTFMove(request));
408         });
409     else {
410         ASSERT_NOT_REACHED();
411         completionHandler({ });
412     }
413 }
414
415 void NetworkDataTaskCocoa::setPendingDownloadLocation(const WTF::String& filename, SandboxExtension::Handle&& sandboxExtensionHandle, bool allowOverwrite)
416 {
417     NetworkDataTask::setPendingDownloadLocation(filename, { }, allowOverwrite);
418
419     ASSERT(!m_sandboxExtension);
420     m_sandboxExtension = SandboxExtension::create(WTFMove(sandboxExtensionHandle));
421     if (m_sandboxExtension)
422         m_sandboxExtension->consume();
423
424     m_task.get()._pathToDownloadTaskFile = m_pendingDownloadLocation;
425
426     if (allowOverwrite && FileSystem::fileExists(m_pendingDownloadLocation))
427         FileSystem::deleteFile(filename);
428 }
429
430 bool NetworkDataTaskCocoa::tryPasswordBasedAuthentication(const WebCore::AuthenticationChallenge& challenge, ChallengeCompletionHandler& completionHandler)
431 {
432     if (!challenge.protectionSpace().isPasswordBased())
433         return false;
434     
435     if (!m_user.isNull() && !m_password.isNull()) {
436         auto persistence = m_storedCredentialsPolicy == WebCore::StoredCredentialsPolicy::Use ? WebCore::CredentialPersistenceForSession : WebCore::CredentialPersistenceNone;
437         completionHandler(AuthenticationChallengeDisposition::UseCredential, WebCore::Credential(m_user, m_password, persistence));
438         m_user = String();
439         m_password = String();
440         return true;
441     }
442
443 #if USE(CREDENTIAL_STORAGE_WITH_NETWORK_SESSION)
444     if (m_storedCredentialsPolicy == WebCore::StoredCredentialsPolicy::Use) {
445         if (!m_initialCredential.isEmpty() || challenge.previousFailureCount()) {
446             // The stored credential wasn't accepted, stop using it.
447             // There is a race condition here, since a different credential might have already been stored by another ResourceHandle,
448             // but the observable effect should be very minor, if any.
449             if (auto* storageSession = m_session->networkStorageSession())
450                 storageSession->credentialStorage().remove(m_partition, challenge.protectionSpace());
451         }
452
453         if (!challenge.previousFailureCount()) {
454             auto credential = m_session->networkStorageSession() ? m_session->networkStorageSession()->credentialStorage().get(m_partition, challenge.protectionSpace()) : WebCore::Credential();
455             if (!credential.isEmpty() && credential != m_initialCredential) {
456                 ASSERT(credential.persistence() == WebCore::CredentialPersistenceNone);
457                 if (challenge.failureResponse().httpStatusCode() == 401) {
458                     // Store the credential back, possibly adding it as a default for this directory.
459                     if (auto* storageSession = m_session->networkStorageSession())
460                         storageSession->credentialStorage().set(m_partition, credential, challenge.protectionSpace(), challenge.failureResponse().url());
461                 }
462                 completionHandler(AuthenticationChallengeDisposition::UseCredential, credential);
463                 return true;
464             }
465         }
466     }
467 #endif
468
469     if (!challenge.proposedCredential().isEmpty() && !challenge.previousFailureCount()) {
470         completionHandler(AuthenticationChallengeDisposition::UseCredential, challenge.proposedCredential());
471         return true;
472     }
473     
474     return false;
475 }
476
477 void NetworkDataTaskCocoa::transferSandboxExtensionToDownload(Download& download)
478 {
479     download.setSandboxExtension(WTFMove(m_sandboxExtension));
480 }
481
482 String NetworkDataTaskCocoa::suggestedFilename() const
483 {
484     if (!m_suggestedFilename.isEmpty())
485         return m_suggestedFilename;
486     return m_task.get().response.suggestedFilename;
487 }
488
489 void NetworkDataTaskCocoa::cancel()
490 {
491     [m_task cancel];
492 }
493
494 void NetworkDataTaskCocoa::resume()
495 {
496     if (m_scheduledFailureType != NoFailure)
497         m_failureTimer.startOneShot(0_s);
498
499     auto& cocoaSession = static_cast<NetworkSessionCocoa&>(*m_session);
500     if (cocoaSession.deviceManagementRestrictionsEnabled() && m_isForMainResourceNavigationForAnyFrame) {
501         auto didDetermineDeviceRestrictionPolicyForURL = makeBlockPtr([this, protectedThis = makeRef(*this)](BOOL isBlocked) {
502             callOnMainThread([this, protectedThis = makeRef(*this), isBlocked] {
503                 if (isBlocked) {
504                     scheduleFailure(RestrictedURLFailure);
505                     return;
506                 }
507
508                 [m_task resume];
509             });
510         });
511
512 #if HAVE(DEVICE_MANAGEMENT)
513         if (cocoaSession.allLoadsBlockedByDeviceManagementRestrictionsForTesting())
514             didDetermineDeviceRestrictionPolicyForURL(true);
515         else {
516             RetainPtr<NSURL> urlToCheck = [m_task currentRequest].URL;
517             [cocoaSession.deviceManagementPolicyMonitor() requestPoliciesForWebsites:@[ urlToCheck.get() ] completionHandler:makeBlockPtr([didDetermineDeviceRestrictionPolicyForURL, urlToCheck] (NSDictionary<NSURL *, NSNumber *> *policies, NSError *error) {
518                 bool isBlocked = error || policies[urlToCheck.get()].integerValue != DMFPolicyOK;
519                 didDetermineDeviceRestrictionPolicyForURL(isBlocked);
520             }).get()];
521         }
522 #else
523         didDetermineDeviceRestrictionPolicyForURL(cocoaSession.allLoadsBlockedByDeviceManagementRestrictionsForTesting());
524 #endif
525         return;
526     }
527
528     [m_task resume];
529 }
530
531 NetworkDataTask::State NetworkDataTaskCocoa::state() const
532 {
533     switch ([m_task state]) {
534     case NSURLSessionTaskStateRunning:
535         return State::Running;
536     case NSURLSessionTaskStateSuspended:
537         return State::Suspended;
538     case NSURLSessionTaskStateCanceling:
539         return State::Canceling;
540     case NSURLSessionTaskStateCompleted:
541         return State::Completed;
542     }
543
544     ASSERT_NOT_REACHED();
545     return State::Completed;
546 }
547
548 WebCore::Credential serverTrustCredential(const WebCore::AuthenticationChallenge& challenge)
549 {
550     return WebCore::Credential([NSURLCredential credentialForTrust:challenge.nsURLAuthenticationChallenge().protectionSpace.serverTrust]);
551 }
552
553 bool NetworkDataTaskCocoa::isAlwaysOnLoggingAllowed() const
554 {
555     return m_isAlwaysOnLoggingAllowed;
556 }
557
558 String NetworkDataTaskCocoa::description() const
559 {
560     return String([m_task description]);
561 }
562
563 }