Source/WebKit:
[WebKit-https.git] / Source / WebKit / NetworkProcess / cocoa / NetworkProcessCocoa.mm
1 /*
2  * Copyright (C) 2014-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 "NetworkProcess.h"
28
29 #import "CookieStorageUtilsCF.h"
30 #import "NetworkCache.h"
31 #import "NetworkProcessCreationParameters.h"
32 #import "NetworkResourceLoader.h"
33 #import "NetworkSessionCocoa.h"
34 #import "SandboxExtension.h"
35 #import "SessionTracker.h"
36 #import <WebCore/NetworkStorageSession.h>
37 #import <WebCore/PublicSuffix.h>
38 #import <WebCore/ResourceRequestCFNet.h>
39 #import <WebCore/RuntimeApplicationChecks.h>
40 #import <WebCore/SecurityOrigin.h>
41 #import <WebCore/SecurityOriginData.h>
42 #import <pal/spi/cf/CFNetworkSPI.h>
43 #import <wtf/BlockPtr.h>
44 #import <wtf/CallbackAggregator.h>
45 #import <wtf/ProcessPrivilege.h>
46
47 #if USE(APPLE_INTERNAL_SDK)
48 #import <WebKitAdditions/NetworkProcessCocoaAdditions.mm>
49 #endif
50
51 namespace WebKit {
52
53 static void initializeNetworkSettings()
54 {
55     static const unsigned preferredConnectionCount = 6;
56
57     _CFNetworkHTTPConnectionCacheSetLimit(kHTTPLoadWidth, preferredConnectionCount);
58
59     Boolean keyExistsAndHasValidFormat = false;
60     Boolean prefValue = CFPreferencesGetAppBooleanValue(CFSTR("WebKitEnableHTTPPipelining"), kCFPreferencesCurrentApplication, &keyExistsAndHasValidFormat);
61     if (keyExistsAndHasValidFormat)
62         WebCore::ResourceRequest::setHTTPPipeliningEnabled(prefValue);
63
64     if (WebCore::ResourceRequest::resourcePrioritiesEnabled()) {
65         const unsigned fastLaneConnectionCount = 1;
66
67         _CFNetworkHTTPConnectionCacheSetLimit(kHTTPPriorityNumLevels, toPlatformRequestPriority(WebCore::ResourceLoadPriority::Highest));
68         _CFNetworkHTTPConnectionCacheSetLimit(kHTTPMinimumFastLanePriority, toPlatformRequestPriority(WebCore::ResourceLoadPriority::Medium));
69         _CFNetworkHTTPConnectionCacheSetLimit(kHTTPNumFastLanes, fastLaneConnectionCount);
70     }
71 }
72
73 void NetworkProcess::platformInitializeNetworkProcessCocoa(const NetworkProcessCreationParameters& parameters)
74 {
75     WebCore::setApplicationBundleIdentifier(parameters.uiProcessBundleIdentifier);
76
77 #if PLATFORM(IOS)
78     SandboxExtension::consumePermanently(parameters.cookieStorageDirectoryExtensionHandle);
79     SandboxExtension::consumePermanently(parameters.containerCachesDirectoryExtensionHandle);
80     SandboxExtension::consumePermanently(parameters.parentBundleDirectoryExtensionHandle);
81 #endif
82     m_diskCacheDirectory = parameters.diskCacheDirectory;
83
84     _CFNetworkSetATSContext(parameters.networkATSContext.get());
85
86     SessionTracker::setIdentifierBase(parameters.uiProcessBundleIdentifier);
87
88     NetworkSessionCocoa::setSourceApplicationAuditTokenData(sourceApplicationAuditData());
89     NetworkSessionCocoa::setSourceApplicationBundleIdentifier(parameters.sourceApplicationBundleIdentifier);
90     NetworkSessionCocoa::setSourceApplicationSecondaryIdentifier(parameters.sourceApplicationSecondaryIdentifier);
91     NetworkSessionCocoa::setUsesNetworkCache(parameters.shouldEnableNetworkCache);
92 #if PLATFORM(IOS)
93     NetworkSessionCocoa::setCTDataConnectionServiceType(parameters.ctDataConnectionServiceType);
94 #endif
95
96     initializeNetworkSettings();
97
98 #if PLATFORM(COCOA)
99     setSharedHTTPCookieStorage(parameters.uiProcessCookieStorageIdentifier);
100 #endif
101
102     WebCore::NetworkStorageSession::setCookieStoragePartitioningEnabled(parameters.cookieStoragePartitioningEnabled);
103     WebCore::NetworkStorageSession::setStorageAccessAPIEnabled(parameters.storageAccessAPIEnabled);
104
105     // FIXME: Most of what this function does for cache size gets immediately overridden by setCacheModel().
106     // - memory cache size passed from UI process is always ignored;
107     // - disk cache size passed from UI process is effectively a minimum size.
108     // One non-obvious constraint is that we need to use -setSharedURLCache: even in testing mode, to prevent creating a default one on disk later, when some other code touches the cache.
109
110     ASSERT(!m_diskCacheIsDisabledForTesting || !parameters.nsURLCacheDiskCapacity);
111
112     if (!parameters.cacheStorageDirectory.isNull()) {
113         m_cacheStorageDirectory = parameters.cacheStorageDirectory;
114         m_cacheStoragePerOriginQuota = parameters.cacheStoragePerOriginQuota;
115         SandboxExtension::consumePermanently(parameters.cacheStorageDirectoryExtensionHandle);
116     }
117
118 #if ENABLE(WIFI_ASSERTIONS)
119     initializeWiFiAssertions(parameters);
120 #endif
121
122     if (!m_diskCacheDirectory.isNull()) {
123         SandboxExtension::consumePermanently(parameters.diskCacheDirectoryExtensionHandle);
124         if (parameters.shouldEnableNetworkCache) {
125             OptionSet<NetworkCache::Cache::Option> cacheOptions { NetworkCache::Cache::Option::RegisterNotify };
126             if (parameters.shouldEnableNetworkCacheEfficacyLogging)
127                 cacheOptions |= NetworkCache::Cache::Option::EfficacyLogging;
128             if (parameters.shouldUseTestingNetworkSession)
129                 cacheOptions |= NetworkCache::Cache::Option::TestingMode;
130 #if ENABLE(NETWORK_CACHE_SPECULATIVE_REVALIDATION)
131             if (parameters.shouldEnableNetworkCacheSpeculativeRevalidation)
132                 cacheOptions |= NetworkCache::Cache::Option::SpeculativeRevalidation;
133 #endif
134             m_cache = NetworkCache::Cache::open(m_diskCacheDirectory, cacheOptions);
135
136             if (m_cache) {
137                 // Disable NSURLCache.
138                 auto urlCache(adoptNS([[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:nil]));
139                 [NSURLCache setSharedURLCache:urlCache.get()];
140                 return;
141             }
142         }
143         String nsURLCacheDirectory = m_diskCacheDirectory;
144 #if PLATFORM(IOS)
145         // NSURLCache path is relative to network process cache directory.
146         // This puts cache files under <container>/Library/Caches/com.apple.WebKit.Networking/
147         nsURLCacheDirectory = ".";
148 #endif
149         [NSURLCache setSharedURLCache:adoptNS([[NSURLCache alloc]
150             initWithMemoryCapacity:parameters.nsURLCacheMemoryCapacity
151             diskCapacity:parameters.nsURLCacheDiskCapacity
152             diskPath:nsURLCacheDirectory]).get()];
153     }
154 }
155
156 void NetworkProcess::platformSetURLCacheSize(unsigned urlCacheMemoryCapacity, uint64_t urlCacheDiskCapacity)
157 {
158     NSURLCache *nsurlCache = [NSURLCache sharedURLCache];
159     [nsurlCache setMemoryCapacity:urlCacheMemoryCapacity];
160     if (!m_diskCacheIsDisabledForTesting)
161         [nsurlCache setDiskCapacity:std::max<uint64_t>(urlCacheDiskCapacity, [nsurlCache diskCapacity])]; // Don't shrink a big disk cache, since that would cause churn.
162 }
163
164 RetainPtr<CFDataRef> NetworkProcess::sourceApplicationAuditData() const
165 {
166 #if PLATFORM(IOS) && !ENABLE(MINIMAL_SIMULATOR)
167     audit_token_t auditToken;
168     ASSERT(parentProcessConnection());
169     if (!parentProcessConnection() || !parentProcessConnection()->getAuditToken(auditToken))
170         return nullptr;
171     return adoptCF(CFDataCreate(nullptr, (const UInt8*)&auditToken, sizeof(auditToken)));
172 #else
173     return nullptr;
174 #endif
175 }
176
177 void NetworkProcess::clearHSTSCache(WebCore::NetworkStorageSession& session, WallTime modifiedSince)
178 {
179     NSTimeInterval timeInterval = modifiedSince.secondsSinceEpoch().seconds();
180     NSDate *date = [NSDate dateWithTimeIntervalSince1970:timeInterval];
181
182     _CFNetworkResetHSTSHostsSinceDate(session.platformSession(), (__bridge CFDateRef)date);
183 }
184
185 static void clearNSURLCache(dispatch_group_t group, WallTime modifiedSince, Function<void ()>&& completionHandler)
186 {
187     dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), BlockPtr<void()>::fromCallable([modifiedSince, completionHandler = WTFMove(completionHandler)] () mutable {
188         NSURLCache *cache = [NSURLCache sharedURLCache];
189
190         NSTimeInterval timeInterval = modifiedSince.secondsSinceEpoch().seconds();
191         NSDate *date = [NSDate dateWithTimeIntervalSince1970:timeInterval];
192         [cache removeCachedResponsesSinceDate:date];
193
194         dispatch_async(dispatch_get_main_queue(), BlockPtr<void()>::fromCallable([completionHandler = WTFMove(completionHandler)] {
195             completionHandler();
196         }).get());
197     }).get());
198 }
199
200 void NetworkProcess::clearDiskCache(WallTime modifiedSince, Function<void ()>&& completionHandler)
201 {
202     if (!m_clearCacheDispatchGroup)
203         m_clearCacheDispatchGroup = dispatch_group_create();
204
205     if (auto* cache = NetworkProcess::singleton().cache()) {
206         auto group = m_clearCacheDispatchGroup;
207         dispatch_group_async(group, dispatch_get_main_queue(), BlockPtr<void()>::fromCallable([cache, group, modifiedSince, completionHandler = WTFMove(completionHandler)] () mutable {
208             cache->clear(modifiedSince, [group, modifiedSince, completionHandler = WTFMove(completionHandler)] () mutable {
209                 // FIXME: Probably not necessary.
210                 clearNSURLCache(group, modifiedSince, WTFMove(completionHandler));
211             });
212         }).get());
213         return;
214     }
215     clearNSURLCache(m_clearCacheDispatchGroup, modifiedSince, WTFMove(completionHandler));
216 }
217
218 #if PLATFORM(COCOA)
219 void NetworkProcess::setSharedHTTPCookieStorage(const Vector<uint8_t>& identifier)
220 {
221     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
222     [NSHTTPCookieStorage _setSharedHTTPCookieStorage:adoptNS([[NSHTTPCookieStorage alloc] _initWithCFHTTPCookieStorage:cookieStorageFromIdentifyingData(identifier).get()]).get()];
223 }
224 #endif
225
226 void NetworkProcess::setCookieStoragePartitioningEnabled(bool enabled)
227 {
228     WebCore::NetworkStorageSession::setCookieStoragePartitioningEnabled(enabled);
229 }
230
231 void NetworkProcess::setStorageAccessAPIEnabled(bool enabled)
232 {
233     WebCore::NetworkStorageSession::setStorageAccessAPIEnabled(enabled);
234 }
235
236 void NetworkProcess::syncAllCookies()
237 {
238     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
239 #pragma clang diagnostic push
240 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
241     
242 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101400) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 120000)
243     RefPtr<CallbackAggregator> callbackAggregator = CallbackAggregator::create([this] {
244         didSyncAllCookies();
245     });
246     WebCore::NetworkStorageSession::forEach([&] (auto& networkStorageSession) {
247         [networkStorageSession.nsCookieStorage() _saveCookies:[callbackAggregator] { }];
248     });
249 #else
250     _CFHTTPCookieStorageFlushCookieStores();
251     didSyncAllCookies();
252 #endif
253
254 #pragma clang diagnostic pop
255 }
256
257 void NetworkProcess::platformPrepareToSuspend()
258 {
259 #if ENABLE(WIFI_ASSERTIONS)
260     suspendWiFiAssertions();
261 #endif
262 }
263
264 void NetworkProcess::platformProcessDidResume()
265 {
266 #if ENABLE(WIFI_ASSERTIONS)
267     resumeWiFiAssertions();
268 #endif
269 }
270
271 }