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