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