Move WKProcessPool._registerURLSchemeServiceWorkersCanHandle to _WKWebsiteDataStoreCo...
[WebKit-https.git] / Source / WebKit / UIProcess / WebsiteData / Cocoa / WebsiteDataStoreCocoa.mm
1 /*
2  * Copyright (C) 2015-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 "WebsiteDataStore.h"
28
29 #import "CookieStorageUtilsCF.h"
30 #import "SandboxUtilities.h"
31 #import "StorageManager.h"
32 #import "WebResourceLoadStatisticsStore.h"
33 #import "WebsiteDataStoreParameters.h"
34 #import <WebCore/RegistrableDomain.h>
35 #import <WebCore/RuntimeApplicationChecks.h>
36 #import <WebCore/RuntimeEnabledFeatures.h>
37 #import <WebCore/SearchPopupMenuCocoa.h>
38 #import <pal/spi/cf/CFNetworkSPI.h>
39 #import <wtf/FileSystem.h>
40 #import <wtf/NeverDestroyed.h>
41 #import <wtf/ProcessPrivilege.h>
42 #import <wtf/URL.h>
43 #import <wtf/text/StringBuilder.h>
44
45 #if PLATFORM(IOS_FAMILY)
46 #import <UIKit/UIApplication.h>
47 #endif
48
49 namespace WebKit {
50
51 static id terminationObserver;
52
53 static HashSet<WebsiteDataStore*>& dataStores()
54 {
55     static NeverDestroyed<HashSet<WebsiteDataStore*>> dataStores;
56
57     return dataStores;
58 }
59
60 static NSString * const WebKitNetworkLoadThrottleLatencyMillisecondsDefaultsKey = @"WebKitNetworkLoadThrottleLatencyMilliseconds";
61
62 WebsiteDataStoreParameters WebsiteDataStore::parameters()
63 {
64     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
65
66     resolveDirectoriesIfNecessary();
67
68     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
69     bool shouldLogCookieInformation = false;
70     bool enableResourceLoadStatisticsDebugMode = false;
71     bool enableResourceLoadStatisticsNSURLSessionSwitching = WebCore::RuntimeEnabledFeatures::sharedFeatures().isITPSessionSwitchingEnabled();
72     WebCore::RegistrableDomain resourceLoadStatisticsManualPrevalentResource { };
73     bool enableLegacyTLS = [defaults boolForKey:@"WebKitEnableLegacyTLS"];
74 #if ENABLE(RESOURCE_LOAD_STATISTICS)
75     enableResourceLoadStatisticsDebugMode = [defaults boolForKey:@"ITPDebugMode"];
76     auto* manualPrevalentResource = [defaults stringForKey:@"ITPManualPrevalentResource"];
77     if (manualPrevalentResource) {
78         URL url { URL(), manualPrevalentResource };
79         if (!url.isValid()) {
80             StringBuilder builder;
81             builder.appendLiteral("http://");
82             builder.append(manualPrevalentResource);
83             url = { URL(), builder.toString() };
84         }
85         if (url.isValid())
86             resourceLoadStatisticsManualPrevalentResource = WebCore::RegistrableDomain { url };
87     }
88 #if !RELEASE_LOG_DISABLED
89     static NSString * const WebKitLogCookieInformationDefaultsKey = @"WebKitLogCookieInformation";
90     shouldLogCookieInformation = [defaults boolForKey:WebKitLogCookieInformationDefaultsKey];
91 #endif
92 #endif // ENABLE(RESOURCE_LOAD_STATISTICS)
93
94     URL httpProxy = m_configuration->httpProxy();
95     URL httpsProxy = m_configuration->httpsProxy();
96     
97     bool isSafari = false;
98 #if PLATFORM(IOS_FAMILY)
99     isSafari = WebCore::IOSApplication::isMobileSafari();
100 #elif PLATFORM(MAC)
101     isSafari = WebCore::MacApplication::isSafari();
102 #endif
103     // FIXME: Remove these once Safari adopts _WKWebsiteDataStoreConfiguration.httpProxy and .httpsProxy.
104     if (!httpProxy.isValid() && isSafari)
105         httpProxy = URL(URL(), [defaults stringForKey:(NSString *)WebKit2HTTPProxyDefaultsKey]);
106     if (!httpsProxy.isValid() && isSafari)
107         httpsProxy = URL(URL(), [defaults stringForKey:(NSString *)WebKit2HTTPSProxyDefaultsKey]);
108
109     auto resourceLoadStatisticsDirectory = m_configuration->resourceLoadStatisticsDirectory();
110     SandboxExtension::Handle resourceLoadStatisticsDirectoryHandle;
111     if (!resourceLoadStatisticsDirectory.isEmpty())
112         SandboxExtension::createHandleForReadWriteDirectory(resourceLoadStatisticsDirectory, resourceLoadStatisticsDirectoryHandle);
113
114     auto networkCacheDirectory = resolvedNetworkCacheDirectory();
115     SandboxExtension::Handle networkCacheDirectoryExtensionHandle;
116     if (!networkCacheDirectory.isEmpty())
117         SandboxExtension::createHandleForReadWriteDirectory(networkCacheDirectory, networkCacheDirectoryExtensionHandle);
118
119     bool shouldIncludeLocalhostInResourceLoadStatistics = isSafari;
120     WebsiteDataStoreParameters parameters;
121     parameters.networkSessionParameters = {
122         m_sessionID,
123         boundInterfaceIdentifier(),
124         allowsCellularAccess(),
125         proxyConfiguration(),
126         sourceApplicationBundleIdentifier(),
127         sourceApplicationSecondaryIdentifier(),
128         shouldLogCookieInformation,
129         Seconds { [defaults integerForKey:WebKitNetworkLoadThrottleLatencyMillisecondsDefaultsKey] / 1000. },
130         WTFMove(httpProxy),
131         WTFMove(httpsProxy),
132         enableLegacyTLS,
133         WTFMove(resourceLoadStatisticsDirectory),
134         WTFMove(resourceLoadStatisticsDirectoryHandle),
135         resourceLoadStatisticsEnabled(),
136         hasStatisticsTestingCallback(),
137         shouldIncludeLocalhostInResourceLoadStatistics,
138         enableResourceLoadStatisticsDebugMode,
139         enableResourceLoadStatisticsNSURLSessionSwitching,
140         m_configuration->deviceManagementRestrictionsEnabled(),
141         m_configuration->allLoadsBlockedByDeviceManagementRestrictionsForTesting(),
142         WTFMove(resourceLoadStatisticsManualPrevalentResource),
143         WTFMove(networkCacheDirectory),
144         WTFMove(networkCacheDirectoryExtensionHandle),
145         m_configuration->dataConnectionServiceType(),
146         m_configuration->fastServerTrustEvaluationEnabled(),
147         m_configuration->networkCacheSpeculativeValidationEnabled(),
148         m_configuration->testingSessionEnabled(),
149         m_configuration->testSpeedMultiplier(),
150         m_configuration->suppressesConnectionTerminationOnSystemChange(),
151     };
152     networkingHasBegun();
153
154     auto cookieFile = resolvedCookieStorageFile();
155
156     if (m_uiProcessCookieStorageIdentifier.isEmpty()) {
157         auto utf8File = cookieFile.utf8();
158         auto url = adoptCF(CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)utf8File.data(), (CFIndex)utf8File.length(), true));
159         m_cfCookieStorage = adoptCF(CFHTTPCookieStorageCreateFromFile(kCFAllocatorDefault, url.get(), nullptr));
160         m_uiProcessCookieStorageIdentifier = identifyingDataFromCookieStorage(m_cfCookieStorage.get());
161     }
162
163     parameters.uiProcessCookieStorageIdentifier = m_uiProcessCookieStorageIdentifier;
164     parameters.networkSessionParameters.sourceApplicationBundleIdentifier = sourceApplicationBundleIdentifier();
165     parameters.networkSessionParameters.sourceApplicationSecondaryIdentifier = sourceApplicationSecondaryIdentifier();
166
167     parameters.pendingCookies = copyToVector(m_pendingCookies);
168
169     if (!cookieFile.isEmpty())
170         SandboxExtension::createHandleForReadWriteDirectory(FileSystem::directoryName(cookieFile), parameters.cookieStoragePathExtensionHandle);
171
172 #if ENABLE(INDEXED_DATABASE)
173     parameters.indexedDatabaseDirectory = resolvedIndexedDatabaseDirectory();
174     if (!parameters.indexedDatabaseDirectory.isEmpty())
175         SandboxExtension::createHandleForReadWriteDirectory(parameters.indexedDatabaseDirectory, parameters.indexedDatabaseDirectoryExtensionHandle);
176 #endif
177
178 #if ENABLE(SERVICE_WORKER)
179     parameters.serviceWorkerRegistrationDirectory = resolvedServiceWorkerRegistrationDirectory();
180     if (!parameters.serviceWorkerRegistrationDirectory.isEmpty())
181         SandboxExtension::createHandleForReadWriteDirectory(parameters.serviceWorkerRegistrationDirectory, parameters.serviceWorkerRegistrationDirectoryExtensionHandle);
182     parameters.serviceWorkerProcessTerminationDelayEnabled = m_configuration->serviceWorkerProcessTerminationDelayEnabled();
183     parameters.serviceWorkerRegisteredSchemes = m_configuration->serviceWorkerRegisteredSchemes();
184 #endif
185
186     parameters.localStorageDirectory = resolvedLocalStorageDirectory();
187     if (!parameters.localStorageDirectory.isEmpty())
188         SandboxExtension::createHandleForReadWriteDirectory(parameters.localStorageDirectory, parameters.localStorageDirectoryExtensionHandle);
189
190     parameters.perOriginStorageQuota = perOriginStorageQuota();
191     parameters.perThirdPartyOriginStorageQuota = perThirdPartyOriginStorageQuota();
192
193     return parameters;
194 }
195
196 void WebsiteDataStore::platformInitialize()
197 {
198     if (!terminationObserver) {
199         ASSERT(dataStores().isEmpty());
200
201 #if PLATFORM(MAC)
202         NSString *notificationName = NSApplicationWillTerminateNotification;
203 #else
204         NSString *notificationName = UIApplicationWillTerminateNotification;
205 #endif
206         terminationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:notificationName object:nil queue:nil usingBlock:^(NSNotification *note) {
207             for (auto& dataStore : dataStores()) {
208 #if ENABLE(RESOURCE_LOAD_STATISTICS)
209                 if (dataStore->m_resourceLoadStatistics)
210                     dataStore->m_resourceLoadStatistics->applicationWillTerminate();
211 #endif
212             }
213         }];
214     }
215
216     ASSERT(!dataStores().contains(this));
217     dataStores().add(this);
218 }
219
220 void WebsiteDataStore::platformDestroy()
221 {
222 #if ENABLE(RESOURCE_LOAD_STATISTICS)
223     if (m_resourceLoadStatistics)
224         m_resourceLoadStatistics->applicationWillTerminate();
225 #endif
226
227     ASSERT(dataStores().contains(this));
228     dataStores().remove(this);
229
230     if (dataStores().isEmpty()) {
231         [[NSNotificationCenter defaultCenter] removeObserver:terminationObserver];
232         terminationObserver = nil;
233     }
234 }
235
236 void WebsiteDataStore::platformRemoveRecentSearches(WallTime oldestTimeToRemove)
237 {
238     WebCore::removeRecentlyModifiedRecentSearches(oldestTimeToRemove);
239 }
240
241 NSString *WebDatabaseDirectoryDefaultsKey = @"WebDatabaseDirectory";
242 NSString *WebStorageDirectoryDefaultsKey = @"WebKitLocalStorageDatabasePathPreferenceKey";
243 NSString *WebKitMediaCacheDirectoryDefaultsKey = @"WebKitMediaCacheDirectory";
244 NSString *WebKitMediaKeysStorageDirectoryDefaultsKey = @"WebKitMediaKeysStorageDirectory";
245
246 WTF::String WebsiteDataStore::defaultApplicationCacheDirectory()
247 {
248 #if PLATFORM(IOS_FAMILY)
249     // This quirk used to make these apps share application cache storage, but doesn't accomplish that any more.
250     // Preserving it avoids the need to migrate data when upgrading.
251     // FIXME: Ideally we should just have Safari, WebApp, and webbookmarksd create a data store with
252     // this application cache path, but that's not supported as of right now.
253     if (WebCore::IOSApplication::isMobileSafari() || WebCore::IOSApplication::isWebApp() || WebCore::IOSApplication::isWebBookmarksD()) {
254         NSString *cachePath = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Caches/com.apple.WebAppCache"];
255
256         return WebKit::stringByResolvingSymlinksInPath(cachePath.stringByStandardizingPath);
257     }
258 #endif
259
260     return cacheDirectoryFileSystemRepresentation("OfflineWebApplicationCache");
261 }
262
263 WTF::String WebsiteDataStore::defaultCacheStorageDirectory()
264 {
265     return cacheDirectoryFileSystemRepresentation("CacheStorage");
266 }
267
268 WTF::String WebsiteDataStore::defaultNetworkCacheDirectory()
269 {
270     return cacheDirectoryFileSystemRepresentation("NetworkCache");
271 }
272
273 WTF::String WebsiteDataStore::defaultMediaCacheDirectory()
274 {
275     return tempDirectoryFileSystemRepresentation("MediaCache");
276 }
277
278 WTF::String WebsiteDataStore::defaultIndexedDBDatabaseDirectory()
279 {
280     return websiteDataDirectoryFileSystemRepresentation("IndexedDB");
281 }
282
283 WTF::String WebsiteDataStore::defaultServiceWorkerRegistrationDirectory()
284 {
285     return cacheDirectoryFileSystemRepresentation("ServiceWorkers");
286 }
287
288 WTF::String WebsiteDataStore::defaultLocalStorageDirectory()
289 {
290     return websiteDataDirectoryFileSystemRepresentation("LocalStorage");
291 }
292
293 WTF::String WebsiteDataStore::defaultMediaKeysStorageDirectory()
294 {
295     return websiteDataDirectoryFileSystemRepresentation("MediaKeys");
296 }
297
298 WTF::String WebsiteDataStore::defaultWebSQLDatabaseDirectory()
299 {
300     return websiteDataDirectoryFileSystemRepresentation("WebSQL");
301 }
302
303 WTF::String WebsiteDataStore::defaultResourceLoadStatisticsDirectory()
304 {
305     return websiteDataDirectoryFileSystemRepresentation("ResourceLoadStatistics");
306 }
307
308 WTF::String WebsiteDataStore::defaultJavaScriptConfigurationDirectory()
309 {
310     return tempDirectoryFileSystemRepresentation("JavaScriptCoreDebug", ShouldCreateDirectory::No);
311 }
312
313 WTF::String WebsiteDataStore::tempDirectoryFileSystemRepresentation(const WTF::String& directoryName, ShouldCreateDirectory shouldCreateDirectory)
314 {
315     static dispatch_once_t onceToken;
316     static NSURL *tempURL;
317     
318     dispatch_once(&onceToken, ^{
319         NSURL *url = [NSURL fileURLWithPath:NSTemporaryDirectory() isDirectory:YES];
320         if (!url)
321             RELEASE_ASSERT_NOT_REACHED();
322         
323         if (!WebKit::processHasContainer()) {
324             NSString *bundleIdentifier = [NSBundle mainBundle].bundleIdentifier;
325             if (!bundleIdentifier)
326                 bundleIdentifier = [NSProcessInfo processInfo].processName;
327             url = [url URLByAppendingPathComponent:bundleIdentifier isDirectory:YES];
328         }
329         
330         tempURL = [[url URLByAppendingPathComponent:@"WebKit" isDirectory:YES] retain];
331     });
332     
333     NSURL *url = [tempURL URLByAppendingPathComponent:directoryName isDirectory:YES];
334
335     if (shouldCreateDirectory == ShouldCreateDirectory::Yes
336         && (![[NSFileManager defaultManager] createDirectoryAtURL:url withIntermediateDirectories:YES attributes:nil error:nullptr]))
337         LOG_ERROR("Failed to create directory %@", url);
338     
339     return url.absoluteURL.path.fileSystemRepresentation;
340 }
341
342 WTF::String WebsiteDataStore::cacheDirectoryFileSystemRepresentation(const WTF::String& directoryName)
343 {
344     static dispatch_once_t onceToken;
345     static NSURL *cacheURL;
346
347     dispatch_once(&onceToken, ^{
348         NSURL *url = [[NSFileManager defaultManager] URLForDirectory:NSCachesDirectory inDomain:NSUserDomainMask appropriateForURL:nullptr create:NO error:nullptr];
349         if (!url)
350             RELEASE_ASSERT_NOT_REACHED();
351
352         if (!WebKit::processHasContainer()) {
353             NSString *bundleIdentifier = [NSBundle mainBundle].bundleIdentifier;
354             if (!bundleIdentifier)
355                 bundleIdentifier = [NSProcessInfo processInfo].processName;
356             url = [url URLByAppendingPathComponent:bundleIdentifier isDirectory:YES];
357         }
358
359         cacheURL = [[url URLByAppendingPathComponent:@"WebKit" isDirectory:YES] retain];
360     });
361
362     NSURL *url = [cacheURL URLByAppendingPathComponent:directoryName isDirectory:YES];
363     if (![[NSFileManager defaultManager] createDirectoryAtURL:url withIntermediateDirectories:YES attributes:nil error:nullptr])
364         LOG_ERROR("Failed to create directory %@", url);
365
366     return url.absoluteURL.path.fileSystemRepresentation;
367 }
368
369 WTF::String WebsiteDataStore::websiteDataDirectoryFileSystemRepresentation(const WTF::String& directoryName)
370 {
371     static dispatch_once_t onceToken;
372     static NSURL *websiteDataURL;
373
374     dispatch_once(&onceToken, ^{
375         NSURL *url = [[NSFileManager defaultManager] URLForDirectory:NSLibraryDirectory inDomain:NSUserDomainMask appropriateForURL:nullptr create:NO error:nullptr];
376         if (!url)
377             RELEASE_ASSERT_NOT_REACHED();
378
379         url = [url URLByAppendingPathComponent:@"WebKit" isDirectory:YES];
380
381         if (!WebKit::processHasContainer()) {
382             NSString *bundleIdentifier = [NSBundle mainBundle].bundleIdentifier;
383             if (!bundleIdentifier)
384                 bundleIdentifier = [NSProcessInfo processInfo].processName;
385             url = [url URLByAppendingPathComponent:bundleIdentifier isDirectory:YES];
386         }
387
388         websiteDataURL = [[url URLByAppendingPathComponent:@"WebsiteData" isDirectory:YES] retain];
389     });
390
391     NSURL *url = [websiteDataURL URLByAppendingPathComponent:directoryName isDirectory:YES];
392     if (![[NSFileManager defaultManager] createDirectoryAtURL:url withIntermediateDirectories:YES attributes:nil error:nullptr])
393         LOG_ERROR("Failed to create directory %@", url);
394
395     return url.absoluteURL.path.fileSystemRepresentation;
396 }
397
398 }