e4ee0206635ccbf707d8947cd5eefcca7db31d4e
[WebKit-https.git] / Tools / TestWebKitAPI / Tests / WebKitCocoa / WebsiteDataStoreCustomPaths.mm
1 /*
2  * Copyright (C) 2017 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
28 #import "PlatformUtilities.h"
29 #import "Test.h"
30 #import "TestNavigationDelegate.h"
31 #import "TestWKWebView.h"
32 #import <WebKit/WKPreferencesRef.h>
33 #import <WebKit/WKProcessPoolPrivate.h>
34 #import <WebKit/WKUserContentControllerPrivate.h>
35 #import <WebKit/WKWebViewConfigurationPrivate.h>
36 #import <WebKit/WKWebViewPrivate.h>
37 #import <WebKit/WKWebsiteDataStorePrivate.h>
38 #import <WebKit/WebKit.h>
39 #import <WebKit/_WKProcessPoolConfiguration.h>
40 #import <WebKit/_WKUserStyleSheet.h>
41 #import <WebKit/_WKWebsiteDataStoreConfiguration.h>
42 #import <wtf/Deque.h>
43 #import <wtf/RetainPtr.h>
44
45 static bool receivedScriptMessage;
46 static Deque<RetainPtr<WKScriptMessage>> scriptMessages;
47
48 @interface WebsiteDataStoreCustomPathsMessageHandler : NSObject <WKScriptMessageHandler, WKNavigationDelegate>
49 @end
50
51 @implementation WebsiteDataStoreCustomPathsMessageHandler
52
53 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
54 {
55     receivedScriptMessage = true;
56     scriptMessages.append(message);
57 }
58
59 - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView
60 {
61     // Overwrite the default policy which launches a new web process and reload page on crash.
62 }
63
64 @end
65
66 static WKScriptMessage *getNextMessage()
67 {
68     if (scriptMessages.isEmpty()) {
69         receivedScriptMessage = false;
70         TestWebKitAPI::Util::run(&receivedScriptMessage);
71     }
72
73     return [[scriptMessages.takeFirst() retain] autorelease];
74 }
75
76 enum class ShouldEnableProcessPrewarming { No, Yes };
77
78 static void runWebsiteDataStoreCustomPaths(ShouldEnableProcessPrewarming shouldEnableProcessPrewarming)
79 {
80     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
81     processPoolConfiguration.get().prewarmsProcessesAutomatically = shouldEnableProcessPrewarming == ShouldEnableProcessPrewarming::Yes ? YES : NO;
82     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
83
84     RetainPtr<WebsiteDataStoreCustomPathsMessageHandler> handler = adoptNS([[WebsiteDataStoreCustomPathsMessageHandler alloc] init]);
85     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
86     [[configuration userContentController] addScriptMessageHandler:handler.get() name:@"testHandler"];
87
88     NSURL *sqlPath = [NSURL fileURLWithPath:[@"~/Library/WebKit/TestWebKitAPI/CustomWebsiteData/WebSQL/" stringByExpandingTildeInPath] isDirectory:YES];
89     NSURL *idbPath = [NSURL fileURLWithPath:[@"~/Library/WebKit/TestWebKitAPI/CustomWebsiteData/IndexedDB/" stringByExpandingTildeInPath] isDirectory:YES];
90     NSURL *localStoragePath = [NSURL fileURLWithPath:[@"~/Library/WebKit/TestWebKitAPI/CustomWebsiteData/LocalStorage/" stringByExpandingTildeInPath] isDirectory:YES];
91     NSURL *cookieStorageFile = [NSURL fileURLWithPath:[@"~/Library/WebKit/TestWebKitAPI/CustomWebsiteData/CookieStorage/Cookie.File" stringByExpandingTildeInPath] isDirectory:NO];
92     NSURL *resourceLoadStatisticsPath = [NSURL fileURLWithPath:[@"~/Library/WebKit/TestWebKitAPI/CustomWebsiteData/ResourceLoadStatistics/" stringByExpandingTildeInPath] isDirectory:YES];
93
94     NSURL *defaultSQLPath = [NSURL fileURLWithPath:[@"~/Library/WebKit/TestWebKitAPI/WebsiteData/WebSQL/" stringByExpandingTildeInPath] isDirectory:YES];
95     NSURL *defaultIDBPath = [NSURL fileURLWithPath:[@"~/Library/WebKit/TestWebKitAPI/WebsiteData/IndexedDB/" stringByExpandingTildeInPath] isDirectory:YES];
96     NSURL *defaultLocalStoragePath = [NSURL fileURLWithPath:[@"~/Library/WebKit/TestWebKitAPI/WebsiteData/LocalStorage/" stringByExpandingTildeInPath] isDirectory:YES];
97     NSURL *defaultResourceLoadStatisticsPath = [NSURL fileURLWithPath:[@"~/Library/WebKit/TestWebKitAPI/WebsiteData/ResourceLoadStatistics/" stringByExpandingTildeInPath] isDirectory:YES];
98
99     [[NSFileManager defaultManager] removeItemAtURL:sqlPath error:nil];
100     [[NSFileManager defaultManager] removeItemAtURL:idbPath error:nil];
101     [[NSFileManager defaultManager] removeItemAtURL:localStoragePath error:nil];
102     [[NSFileManager defaultManager] removeItemAtURL:cookieStorageFile error:nil];
103     [[NSFileManager defaultManager] removeItemAtURL:resourceLoadStatisticsPath error:nil];
104     [[NSFileManager defaultManager] removeItemAtURL:defaultSQLPath error:nil];
105     [[NSFileManager defaultManager] removeItemAtURL:defaultIDBPath error:nil];
106     [[NSFileManager defaultManager] removeItemAtURL:defaultLocalStoragePath error:nil];
107     [[NSFileManager defaultManager] removeItemAtURL:defaultResourceLoadStatisticsPath error:nil];
108
109     EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:sqlPath.path]);
110     EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:idbPath.path]);
111     EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:localStoragePath.path]);
112     EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:cookieStorageFile.path]);
113     EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:resourceLoadStatisticsPath.path]);
114     EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:defaultSQLPath.path]);
115     EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:defaultIDBPath.path]);
116     EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:defaultLocalStoragePath.path]);
117     EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:defaultResourceLoadStatisticsPath.path]);
118
119     RetainPtr<_WKWebsiteDataStoreConfiguration> websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
120     websiteDataStoreConfiguration.get()._webSQLDatabaseDirectory = sqlPath;
121     websiteDataStoreConfiguration.get()._indexedDBDatabaseDirectory = idbPath;
122     websiteDataStoreConfiguration.get()._webStorageDirectory = localStoragePath;
123     websiteDataStoreConfiguration.get()._cookieStorageFile = cookieStorageFile;
124     websiteDataStoreConfiguration.get()._resourceLoadStatisticsDirectory = resourceLoadStatisticsPath;
125
126     configuration.get().websiteDataStore = [[[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()] autorelease];
127     configuration.get().processPool = processPool.get();
128
129     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
130     [webView setNavigationDelegate:handler.get()];
131
132     auto preferences = (__bridge WKPreferencesRef)[[webView configuration] preferences];
133     WKPreferencesSetWebSQLDisabled(preferences, false);
134
135     NSURLRequest *request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"WebsiteDataStoreCustomPaths" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
136     [webView loadRequest:request];
137
138     EXPECT_FALSE([WKWebsiteDataStore _defaultDataStoreExists]);
139
140     // We expect 4 messages, 1 each for WebSQL, IndexedDB, cookies, and localStorage.
141     EXPECT_STREQ([getNextMessage().body UTF8String], "localstorage written");
142     EXPECT_STREQ([getNextMessage().body UTF8String], "cookie written");
143     EXPECT_STREQ([getNextMessage().body UTF8String], "Exception: QuotaExceededError: The quota has been exceeded.");
144     EXPECT_STREQ([getNextMessage().body UTF8String], "Success opening indexed database");
145
146     [[[webView configuration] processPool] _syncNetworkProcessCookies];
147
148     // Forcibly shut down everything of WebKit that we can.
149     auto pid = [webView _webProcessIdentifier];
150     if (pid)
151         kill(pid, SIGKILL);
152
153     webView = nil;
154     handler = nil;
155     configuration = nil;
156
157     EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:sqlPath.path]);
158     EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:localStoragePath.path]);
159
160 #if PLATFORM(IOS_FAMILY) || (__MAC_OS_X_VERSION_MIN_REQUIRED < 101300)
161     int retryCount = 30;
162     while (retryCount--) {
163         if ([[NSFileManager defaultManager] fileExistsAtPath:cookieStorageFile.path])
164             break;
165         TestWebKitAPI::Util::sleep(0.1);
166     }
167     EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:cookieStorageFile.path]);
168
169     // Note: The format of the cookie file on disk is proprietary and opaque, so this is fragile to future changes.
170     // But right now, it is reliable to scan the file for the ASCII string of the cookie name we set.
171     // This helps verify that the cookie file was actually written to as we'd expect.
172     auto data = adoptNS([[NSData alloc] initWithContentsOfURL:cookieStorageFile]);
173     char bytes[] = "testkey";
174     auto cookieKeyData = adoptNS([[NSData alloc] initWithBytes:(void *)bytes length:strlen(bytes)]);
175     auto result = [data rangeOfData:cookieKeyData.get() options:0 range:NSMakeRange(0, data.get().length)];
176     EXPECT_NE((const long)result.location, NSNotFound);
177 #endif
178
179 #if PLATFORM(MAC)
180     // FIXME: The default SQL and LocalStorage paths are being used for something, but they shouldn't be. (theses should be false, not true)
181     EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:defaultSQLPath.path]);
182     EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:defaultLocalStoragePath.path]);
183 #endif
184
185     EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:idbPath.path]);
186     EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:defaultIDBPath.path]);
187     RetainPtr<NSURL> fileIDBPath = [[idbPath URLByAppendingPathComponent:@"v1"] URLByAppendingPathComponent:@"file__0"];
188     EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:fileIDBPath.get().path]);
189
190     RetainPtr<WKWebsiteDataStore> dataStore = [[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()];
191     RetainPtr<NSSet> types = adoptNS([[NSSet alloc] initWithObjects:WKWebsiteDataTypeIndexedDBDatabases, nil]);
192
193     // Subframe of different origins may also create IndexedDB files.
194     RetainPtr<NSURL> url1 = [[NSBundle mainBundle] URLForResource:@"IndexedDB" withExtension:@"sqlite3" subdirectory:@"TestWebKitAPI.resources"];
195     RetainPtr<NSURL> url2 = [[NSBundle mainBundle] URLForResource:@"IndexedDB" withExtension:@"sqlite3-shm" subdirectory:@"TestWebKitAPI.resources"];
196     RetainPtr<NSURL> url3 = [[NSBundle mainBundle] URLForResource:@"IndexedDB" withExtension:@"sqlite3-wal" subdirectory:@"TestWebKitAPI.resources"];
197
198     RetainPtr<NSURL> frameIDBPath = [[fileIDBPath URLByAppendingPathComponent:@"https_apple.com_0"] URLByAppendingPathComponent:@"WebsiteDataStoreCustomPaths"];
199     [[NSFileManager defaultManager] createDirectoryAtURL:frameIDBPath.get() withIntermediateDirectories:YES attributes:nil error:nil];
200
201     [[NSFileManager defaultManager] copyItemAtURL:url1.get() toURL:[frameIDBPath.get() URLByAppendingPathComponent:@"IndexedDB.sqlite3"] error:nil];
202     [[NSFileManager defaultManager] copyItemAtURL:url2.get() toURL:[frameIDBPath.get() URLByAppendingPathComponent:@"IndexedDB.sqlite3-shm"] error:nil];
203     [[NSFileManager defaultManager] copyItemAtURL:url3.get() toURL:[frameIDBPath.get() URLByAppendingPathComponent:@"IndexedDB.sqlite3-wal"] error:nil];
204
205     RetainPtr<NSURL> frameIDBPath2 = [[fileIDBPath URLByAppendingPathComponent:@"https_webkit.org_0"] URLByAppendingPathComponent:@"WebsiteDataStoreCustomPaths"];
206     [[NSFileManager defaultManager] createDirectoryAtURL:frameIDBPath2.get() withIntermediateDirectories:YES attributes:nil error:nil];
207
208     [[NSFileManager defaultManager] copyItemAtURL:url1.get() toURL:[frameIDBPath2.get() URLByAppendingPathComponent:@"IndexedDB.sqlite3"] error:nil];
209     [[NSFileManager defaultManager] copyItemAtURL:url2.get() toURL:[frameIDBPath2.get() URLByAppendingPathComponent:@"IndexedDB.sqlite3-shm"] error:nil];
210     [[NSFileManager defaultManager] copyItemAtURL:url3.get() toURL:[frameIDBPath2.get() URLByAppendingPathComponent:@"IndexedDB.sqlite3-wal"] error:nil];
211
212     [dataStore fetchDataRecordsOfTypes:types.get() completionHandler:^(NSArray<WKWebsiteDataRecord *> * records) {
213         EXPECT_EQ([records count], (unsigned long)3);
214         for (id record in records) {
215             if ([[record displayName] isEqual: @"apple.com"]) {
216                 [dataStore removeDataOfTypes:types.get() forDataRecords:[NSArray arrayWithObject:record] completionHandler:^() {
217                     receivedScriptMessage = true;
218                     EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:frameIDBPath.get().path]);
219                 }];
220             }
221         }
222     }];
223     receivedScriptMessage = false;
224     TestWebKitAPI::Util::run(&receivedScriptMessage);
225
226     [dataStore removeDataOfTypes:types.get() modifiedSince:[NSDate distantPast] completionHandler:[]() {
227         receivedScriptMessage = true;
228     }];
229     receivedScriptMessage = false;
230     TestWebKitAPI::Util::run(&receivedScriptMessage);
231
232     EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:fileIDBPath.get().path]);
233
234     // Now, with brand new WKWebsiteDataStores pointing at the same custom cookie storage location,
235     // in newly fired up NetworkProcesses, verify that the fetch and delete APIs work as expected.
236
237     [processPool _terminateNetworkProcess];
238     auto newCustomDataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
239
240     [newCustomDataStore fetchDataRecordsOfTypes:[NSSet setWithObjects:WKWebsiteDataTypeCookies, nil] completionHandler:^(NSArray<WKWebsiteDataRecord *> * records) {
241         EXPECT_GT([records count], (unsigned long)0);
242         receivedScriptMessage = true;
243     }];
244
245     receivedScriptMessage = false;
246     TestWebKitAPI::Util::run(&receivedScriptMessage);
247
248     [processPool _terminateNetworkProcess];
249     newCustomDataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
250
251     [newCustomDataStore removeDataOfTypes:[NSSet setWithObjects:WKWebsiteDataTypeCookies, nil] modifiedSince:[NSDate distantPast] completionHandler:^ {
252         receivedScriptMessage = true;
253     }];
254
255     receivedScriptMessage = false;
256     TestWebKitAPI::Util::run(&receivedScriptMessage);
257
258     // This time, reuse the same network process but still do a new websitedatastore, to make sure even an existing network process
259     // gets the new datastore.
260     newCustomDataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
261
262     [newCustomDataStore fetchDataRecordsOfTypes:[NSSet setWithObjects:WKWebsiteDataTypeCookies, nil] completionHandler:^(NSArray<WKWebsiteDataRecord *> * records) {
263         EXPECT_EQ([records count], (unsigned long)0);
264         receivedScriptMessage = true;
265     }];
266
267     receivedScriptMessage = false;
268     TestWebKitAPI::Util::run(&receivedScriptMessage);
269
270     EXPECT_FALSE([WKWebsiteDataStore _defaultDataStoreExists]);
271 }
272
273 TEST(WebKit, WebsiteDataStoreCustomPathsWithoutPrewarming)
274 {
275     runWebsiteDataStoreCustomPaths(ShouldEnableProcessPrewarming::No);
276 }
277
278 TEST(WebKit, WebsiteDataStoreCustomPathsWithPrewarming)
279 {
280     runWebsiteDataStoreCustomPaths(ShouldEnableProcessPrewarming::Yes);
281 }
282
283 TEST(WebKit, CustomDataStorePathsVersusCompletionHandlers)
284 {
285     // Copy the baked database files to the database directory
286     NSURL *url1 = [[NSBundle mainBundle] URLForResource:@"SimpleServiceWorkerRegistrations-4" withExtension:@"sqlite3" subdirectory:@"TestWebKitAPI.resources"];
287
288     NSURL *swPath = [NSURL fileURLWithPath:[@"~/Library/Caches/TestWebKitAPI/WebKit/ServiceWorkers/" stringByExpandingTildeInPath]];
289     [[NSFileManager defaultManager] removeItemAtURL:swPath error:nil];
290     EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:swPath.path]);
291
292     [[NSFileManager defaultManager] createDirectoryAtURL:swPath withIntermediateDirectories:YES attributes:nil error:nil];
293     [[NSFileManager defaultManager] copyItemAtURL:url1 toURL:[swPath URLByAppendingPathComponent:@"ServiceWorkerRegistrations-4.sqlite3"] error:nil];
294
295     auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
296     websiteDataStoreConfiguration.get()._serviceWorkerRegistrationDirectory = swPath;
297     auto dataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
298
299     // Fetch SW records
300     auto websiteDataTypes = adoptNS([[NSSet alloc] initWithArray:@[WKWebsiteDataTypeServiceWorkerRegistrations]]);
301     static bool readyToContinue;
302     [dataStore fetchDataRecordsOfTypes:websiteDataTypes.get() completionHandler:^(NSArray<WKWebsiteDataRecord *> *dataRecords) {
303         EXPECT_EQ(1U, dataRecords.count);
304         readyToContinue = true;
305     }];
306     TestWebKitAPI::Util::run(&readyToContinue);
307     readyToContinue = false;
308
309     // Fetch records again, this time releasing our reference to the data store while the request is in flight.
310     [dataStore fetchDataRecordsOfTypes:websiteDataTypes.get() completionHandler:^(NSArray<WKWebsiteDataRecord *> *dataRecords) {
311         EXPECT_EQ(1U, dataRecords.count);
312         readyToContinue = true;
313     }];
314     dataStore = nil;
315     TestWebKitAPI::Util::run(&readyToContinue);
316     readyToContinue = false;
317
318     // Delete all SW records, releasing our reference to the data store while the request is in flight.
319     dataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
320     [dataStore removeDataOfTypes:websiteDataTypes.get() modifiedSince:[NSDate distantPast] completionHandler:^() {
321         readyToContinue = true;
322     }];
323     dataStore = nil;
324     TestWebKitAPI::Util::run(&readyToContinue);
325     readyToContinue = false;
326
327     // The records should have been deleted, and the callback should have been made.
328     // Now refetch the records to verify they are gone.
329     dataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
330     [dataStore fetchDataRecordsOfTypes:websiteDataTypes.get() completionHandler:^(NSArray<WKWebsiteDataRecord *> *dataRecords) {
331         EXPECT_EQ(0U, dataRecords.count);
332         readyToContinue = true;
333     }];
334     TestWebKitAPI::Util::run(&readyToContinue);
335 }
336
337 TEST(WebKit, CustomDataStoreDestroyWhileFetchingNetworkProcessData)
338 {
339     NSURL *cookieStorageFile = [NSURL fileURLWithPath:[@"~/Library/WebKit/TestWebKitAPI/CustomWebsiteData/CookieStorage/Cookie.File" stringByExpandingTildeInPath] isDirectory:NO];
340     [[NSFileManager defaultManager] removeItemAtURL:cookieStorageFile error:nil];
341
342     auto websiteDataTypes = adoptNS([[NSSet alloc] initWithArray:@[WKWebsiteDataTypeCookies]]);
343     static bool readyToContinue;
344
345     auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
346     websiteDataStoreConfiguration.get()._cookieStorageFile = cookieStorageFile;
347
348     @autoreleasepool {
349         auto dataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
350
351         // Fetch records
352         [dataStore fetchDataRecordsOfTypes:websiteDataTypes.get() completionHandler:^(NSArray<WKWebsiteDataRecord *> *dataRecords) {
353             EXPECT_EQ((int)dataRecords.count, 0);
354             readyToContinue = true;
355         }];
356         TestWebKitAPI::Util::run(&readyToContinue);
357         readyToContinue = false;
358
359         // Fetch records again, this time releasing our reference to the data store while the request is in flight.
360         [dataStore fetchDataRecordsOfTypes:websiteDataTypes.get() completionHandler:^(NSArray<WKWebsiteDataRecord *> *dataRecords) {
361             EXPECT_EQ((int)dataRecords.count, 0);
362             readyToContinue = true;
363         }];
364         dataStore = nil;
365     }
366     TestWebKitAPI::Util::run(&readyToContinue);
367     readyToContinue = false;
368
369     @autoreleasepool {
370         auto dataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
371         [dataStore fetchDataRecordsOfTypes:websiteDataTypes.get() completionHandler:^(NSArray<WKWebsiteDataRecord *> *dataRecords) {
372             EXPECT_EQ((int)dataRecords.count, 0);
373             readyToContinue = true;
374         }];
375
376         // Terminate the network process while a query is pending.
377         auto* allProcessPools = [WKProcessPool _allProcessPoolsForTesting];
378         ASSERT_EQ(1U, [allProcessPools count]);
379         auto* processPool = allProcessPools[0];
380         while (![processPool _networkProcessIdentifier])
381             TestWebKitAPI::Util::sleep(0.01);
382         kill([processPool _networkProcessIdentifier], SIGKILL);
383         allProcessPools = nil;
384         dataStore = nil;
385     }
386
387     TestWebKitAPI::Util::run(&readyToContinue);
388     readyToContinue = false;
389 }
390
391 TEST(WebKit, WebsiteDataStoreEphemeral)
392 {
393     RetainPtr<WebsiteDataStoreCustomPathsMessageHandler> handler = adoptNS([[WebsiteDataStoreCustomPathsMessageHandler alloc] init]);
394     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
395     [[configuration userContentController] addScriptMessageHandler:handler.get() name:@"testHandler"];
396
397     NSURL *defaultResourceLoadStatisticsPath = [NSURL fileURLWithPath:[@"~/Library/WebKit/TestWebKitAPI/WebsiteData/ResourceLoadStatistics/" stringByExpandingTildeInPath] isDirectory:YES];
398
399     [[NSFileManager defaultManager] removeItemAtURL:defaultResourceLoadStatisticsPath error:nil];
400
401     EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:defaultResourceLoadStatisticsPath.path]);
402
403     configuration.get().websiteDataStore = [WKWebsiteDataStore nonPersistentDataStore];
404     [configuration.get().websiteDataStore _setResourceLoadStatisticsEnabled:YES];
405
406     // We expect the directory to be created by starting up the data store machinery, but not the data file.
407     EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:defaultResourceLoadStatisticsPath.path]);
408
409     NSURL *defaultResourceLoadStatisticsFilePath = [NSURL fileURLWithPath:[@"~/Library/WebKit/TestWebKitAPI/WebsiteData/ResourceLoadStatistics/full_browsing_session_resourceLog.plist" stringByExpandingTildeInPath] isDirectory:NO];
410     EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:defaultResourceLoadStatisticsFilePath.path]);
411
412     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
413
414     NSURLRequest *request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"WebsiteDataStoreCustomPaths" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
415     [webView loadRequest:request];
416
417     [[[webView configuration] processPool] _syncNetworkProcessCookies];
418
419     // Forcibly shut down everything of WebKit that we can.
420     auto pid = [webView _webProcessIdentifier];
421     if (pid)
422         kill(pid, SIGKILL);
423
424     webView = nil;
425     handler = nil;
426     configuration = nil;
427
428     EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:defaultResourceLoadStatisticsPath.path]);
429     EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:defaultResourceLoadStatisticsFilePath.path]);
430
431     [[NSFileManager defaultManager] removeItemAtURL:defaultResourceLoadStatisticsPath error:nil];
432 }
433
434 TEST(WebKit, WebsiteDataStoreEphemeralViaConfiguration)
435 {
436     RetainPtr<WebsiteDataStoreCustomPathsMessageHandler> handler = adoptNS([[WebsiteDataStoreCustomPathsMessageHandler alloc] init]);
437     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
438     [[configuration userContentController] addScriptMessageHandler:handler.get() name:@"testHandler"];
439
440     NSURL *defaultResourceLoadStatisticsPath = [NSURL fileURLWithPath:[@"~/Library/WebKit/TestWebKitAPI/WebsiteData/ResourceLoadStatistics/" stringByExpandingTildeInPath] isDirectory:YES];
441
442     [[NSFileManager defaultManager] removeItemAtURL:defaultResourceLoadStatisticsPath error:nil];
443
444     EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:defaultResourceLoadStatisticsPath.path]);
445
446     RetainPtr<_WKWebsiteDataStoreConfiguration> dataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] initNonPersistentConfiguration]);
447     configuration.get().websiteDataStore = [[[WKWebsiteDataStore alloc] _initWithConfiguration:dataStoreConfiguration.get()] autorelease];
448     [configuration.get().websiteDataStore _setResourceLoadStatisticsEnabled:YES];
449
450     // We expect the directory to be created by starting up the data store machinery, but not the data file.
451     EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:defaultResourceLoadStatisticsPath.path]);
452
453     NSURL *defaultResourceLoadStatisticsFilePath = [NSURL fileURLWithPath:[@"~/Library/WebKit/TestWebKitAPI/WebsiteData/ResourceLoadStatistics/full_browsing_session_resourceLog.plist" stringByExpandingTildeInPath] isDirectory:NO];
454     EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:defaultResourceLoadStatisticsFilePath.path]);
455
456     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
457
458     NSURLRequest *request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"WebsiteDataStoreCustomPaths" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
459     [webView loadRequest:request];
460
461     [[[webView configuration] processPool] _syncNetworkProcessCookies];
462
463     // Forcibly shut down everything of WebKit that we can.
464     auto pid = [webView _webProcessIdentifier];
465     if (pid)
466         kill(pid, SIGKILL);
467
468     webView = nil;
469     handler = nil;
470     configuration = nil;
471
472     EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:defaultResourceLoadStatisticsPath.path]);
473     EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:defaultResourceLoadStatisticsFilePath.path]);
474
475     [[NSFileManager defaultManager] removeItemAtURL:defaultResourceLoadStatisticsPath error:nil];
476 }
477
478 TEST(WebKit, DoLoadWithNonDefaultDataStoreAfterTerminatingNetworkProcess)
479 {
480     auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
481
482     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
483     webViewConfiguration.get().websiteDataStore = [[[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()] autorelease];
484
485     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
486
487     NSURLRequest *request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
488     [webView loadRequest:request];
489     [webView _test_waitForDidFinishNavigation];
490
491     TestWebKitAPI::Util::spinRunLoop(1);
492
493     [webViewConfiguration.get().processPool _terminateNetworkProcess];
494
495     request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
496     [webView loadRequest:request];
497     [webView _test_waitForDidFinishNavigation];
498 }
499
500 TEST(WebKit, ApplicationIdentifiers)
501 {
502     auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
503     [websiteDataStoreConfiguration setSourceApplicationBundleIdentifier:@"testidentifier"];
504
505     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
506     auto websiteDataStore = [[[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()] autorelease];
507     EXPECT_TRUE([websiteDataStore._sourceApplicationBundleIdentifier isEqualToString:@"testidentifier"]);
508     [websiteDataStore _setSourceApplicationBundleIdentifier:@"otheridentifier"];
509
510     [webViewConfiguration setWebsiteDataStore:websiteDataStore];
511     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
512     [webView synchronouslyLoadTestPageNamed:@"simple"];
513     
514     RetainPtr<NSException> exception;
515     @try {
516         [websiteDataStore _setSourceApplicationBundleIdentifier:@"settingShouldFailNow"];
517     } @catch(NSException *caught) {
518         exception = caught;
519     }
520     EXPECT_TRUE([[exception reason] isEqualToString:@"_setSourceApplicationBundleIdentifier cannot be called after networking has begun"]);
521     EXPECT_TRUE([websiteDataStore._sourceApplicationBundleIdentifier isEqualToString:@"otheridentifier"]);
522     EXPECT_TRUE([[websiteDataStoreConfiguration sourceApplicationBundleIdentifier] isEqualToString:@"testidentifier"]);
523 }