blob: 75901c56c590701935bf10c9f7929d22eb014599 [file]
/*
* Copyright (C) 2017 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "config.h"
#import "DeprecatedGlobalValues.h"
#import "HTTPServer.h"
#import "PlatformUtilities.h"
#import "Test.h"
#import "TestNavigationDelegate.h"
#import "TestWKWebView.h"
#import <JavaScriptCore/JSCConfig.h>
#import <WebCore/SQLiteFileSystem.h>
#import <WebCore/SecurityOriginData.h>
#import <WebKit/WKHTTPCookieStorePrivate.h>
#import <WebKit/WKPreferencesPrivate.h>
#import <WebKit/WKPreferencesRef.h>
#import <WebKit/WKProcessPoolPrivate.h>
#import <WebKit/WKUserContentControllerPrivate.h>
#import <WebKit/WKWebViewConfigurationPrivate.h>
#import <WebKit/WKWebViewPrivate.h>
#import <WebKit/WKWebsiteDataRecordPrivate.h>
#import <WebKit/WKWebsiteDataStorePrivate.h>
#import <WebKit/WKWebsiteDataStoreRef.h>
#import <WebKit/WebKit.h>
#import <WebKit/_WKProcessPoolConfiguration.h>
#import <WebKit/_WKUserStyleSheet.h>
#import <WebKit/_WKWebsiteDataStoreConfiguration.h>
#import <wtf/Deque.h>
#import <wtf/FileSystem.h>
#import <wtf/RetainPtr.h>
#import <wtf/cocoa/VectorCocoa.h>
#import <wtf/text/StringToIntegerConversion.h>
#import <wtf/text/WTFString.h>
@interface WebsiteDataStoreCustomPathsMessageHandler : NSObject <WKScriptMessageHandler, WKNavigationDelegate>
@end
@implementation WebsiteDataStoreCustomPathsMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
receivedScriptMessage = true;
scriptMessages.append(message);
}
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView
{
// Overwrite the default policy which launches a new web process and reload page on crash.
}
@end
enum class ShouldEnableProcessPrewarming : bool { No, Yes };
static void runWebsiteDataStoreCustomPaths(ShouldEnableProcessPrewarming shouldEnableProcessPrewarming)
{
RetainPtr processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
processPoolConfiguration.get().prewarmsProcessesAutomatically = shouldEnableProcessPrewarming == ShouldEnableProcessPrewarming::Yes ? YES : NO;
RetainPtr processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
RetainPtr handler = adoptNS([[WebsiteDataStoreCustomPathsMessageHandler alloc] init]);
RetainPtr configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
[[configuration userContentController] addScriptMessageHandler:handler.get() name:@"testHandler"];
RetainPtr idbPath = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/CustomWebsiteData/IndexedDB/" stringByExpandingTildeInPath] isDirectory:YES];
RetainPtr localStoragePath = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/CustomWebsiteData/LocalStorage/" stringByExpandingTildeInPath] isDirectory:YES];
RetainPtr cookieStorageFile = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/CustomWebsiteData/CookieStorage/Cookie.File" stringByExpandingTildeInPath] isDirectory:NO];
RetainPtr resourceLoadStatisticsPath = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/CustomWebsiteData/ResourceLoadStatistics/" stringByExpandingTildeInPath] isDirectory:YES];
RetainPtr defaultIDBPath = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/WebsiteData/IndexedDB/" stringByExpandingTildeInPath] isDirectory:YES];
RetainPtr defaultLocalStoragePath = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/WebsiteData/LocalStorage/" stringByExpandingTildeInPath] isDirectory:YES];
RetainPtr defaultResourceLoadStatisticsPath = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/WebsiteData/ResourceLoadStatistics/" stringByExpandingTildeInPath] isDirectory:YES];
[[NSFileManager defaultManager] removeItemAtURL:idbPath.get() error:nil];
[[NSFileManager defaultManager] removeItemAtURL:localStoragePath.get() error:nil];
[[NSFileManager defaultManager] removeItemAtURL:cookieStorageFile.get() error:nil];
[[NSFileManager defaultManager] removeItemAtURL:resourceLoadStatisticsPath.get() error:nil];
[[NSFileManager defaultManager] removeItemAtURL:defaultIDBPath.get() error:nil];
[[NSFileManager defaultManager] removeItemAtURL:defaultLocalStoragePath.get() error:nil];
[[NSFileManager defaultManager] removeItemAtURL:defaultResourceLoadStatisticsPath.get() error:nil];
EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:idbPath.get().path]);
EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:localStoragePath.get().path]);
EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:cookieStorageFile.get().path]);
EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:resourceLoadStatisticsPath.get().path]);
EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:defaultIDBPath.get().path]);
EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:defaultLocalStoragePath.get().path]);
EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:defaultResourceLoadStatisticsPath.get().path]);
RetainPtr websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
websiteDataStoreConfiguration.get().unifiedOriginStorageLevel = _WKUnifiedOriginStorageLevelNone;
websiteDataStoreConfiguration.get()._indexedDBDatabaseDirectory = idbPath.get();
websiteDataStoreConfiguration.get()._webStorageDirectory = localStoragePath.get();
websiteDataStoreConfiguration.get()._cookieStorageFile = cookieStorageFile.get();
websiteDataStoreConfiguration.get()._resourceLoadStatisticsDirectory = resourceLoadStatisticsPath.get();
configuration.get().websiteDataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]).get();
configuration.get().processPool = processPool.get();
RetainPtr webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
[webView setNavigationDelegate:handler.get()];
RetainPtr request = [NSURLRequest requestWithURL:[NSBundle.test_resourcesBundle URLForResource:@"WebsiteDataStoreCustomPaths" withExtension:@"html"]];
[webView loadRequest:request.get()];
EXPECT_FALSE([WKWebsiteDataStore _defaultDataStoreExists]);
// We expect 3 messages, 1 each for IndexedDB, cookies, and localStorage.
EXPECT_STREQ([getNextMessage().body UTF8String], "localstorage written");
EXPECT_STREQ([getNextMessage().body UTF8String], "cookie written");
EXPECT_STREQ([getNextMessage().body UTF8String], "Success opening indexed database");
__block bool flushed = false;
[configuration.get().websiteDataStore.httpCookieStore _flushCookiesToDiskWithCompletionHandler:^{
flushed = true;
}];
TestWebKitAPI::Util::run(&flushed);
// Get the IndexedDB database path in use.
RetainPtr fileURL = [NSURL URLWithString:@"file://"];
__block RetainPtr<NSString> fileIDBPathString;
done = false;
[configuration.get().websiteDataStore _originDirectoryForTesting:fileURL.get() topOrigin:fileURL.get() type:WKWebsiteDataTypeIndexedDBDatabases completionHandler:^(NSString *result) {
fileIDBPathString = result;
done = true;
}];
TestWebKitAPI::Util::run(&done);
RetainPtr fileIDBPath = [NSURL fileURLWithPath:fileIDBPathString.get() isDirectory:YES];
// Forcibly shut down everything of WebKit that we can.
auto pid = [webView _webProcessIdentifier];
if (pid)
kill(pid, SIGKILL);
webView = nil;
handler = nil;
configuration = nil;
EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:localStoragePath.get().path]);
#if PLATFORM(IOS_FAMILY)
int retryCount = 30;
while (retryCount--) {
if ([[NSFileManager defaultManager] fileExistsAtPath:cookieStorageFile.get().path])
break;
TestWebKitAPI::Util::runFor(0.1_s);
}
EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:cookieStorageFile.get().path]);
// Note: The format of the cookie file on disk is proprietary and opaque, so this is fragile to future changes.
// But right now, it is reliable to scan the file for the ASCII string of the cookie name we set.
// This helps verify that the cookie file was actually written to as we'd expect.
RetainPtr data = adoptNS([[NSData alloc] initWithContentsOfURL:cookieStorageFile.get()]);
char bytes[] = "testkey";
RetainPtr cookieKeyData = adoptNS([[NSData alloc] initWithBytes:(void *)bytes length:strlen(bytes)]);
auto result = [data rangeOfData:cookieKeyData.get() options:0 range:NSMakeRange(0, data.get().length)];
EXPECT_NE((const long)result.location, NSNotFound);
#endif
#if PLATFORM(MAC)
// FIXME: The default LocalStorage path is being used for something, but they shouldn't be. (theses should be false, not true)
EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:defaultLocalStoragePath.get().path]);
#endif
EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:idbPath.get().path]);
EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:defaultIDBPath.get().path]);
EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:fileIDBPath.get().path]);
@autoreleasepool {
RetainPtr dataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
RetainPtr types = adoptNS([[NSSet alloc] initWithObjects:WKWebsiteDataTypeIndexedDBDatabases, nil]);
RetainPtr appleURL = [NSURL URLWithString:@"https://apple.com"];
__block RetainPtr<NSString> idbPathString;
done = false;
[dataStore _originDirectoryForTesting:appleURL.get() topOrigin:fileURL.get() type:WKWebsiteDataTypeIndexedDBDatabases completionHandler:^(NSString *result) {
idbPathString = result;
done = true;
}];
TestWebKitAPI::Util::run(&done);
RetainPtr fileAppleIDBPath = [NSURL fileURLWithPath:idbPathString.get() isDirectory:YES];
RetainPtr webkitURL = [NSURL URLWithString:@"https://webkit.org"];
done = false;
[dataStore _originDirectoryForTesting:webkitURL.get() topOrigin:fileURL.get() type:WKWebsiteDataTypeIndexedDBDatabases completionHandler:^(NSString *result) {
idbPathString = result;
done = true;
}];
TestWebKitAPI::Util::run(&done);
RetainPtr fileWebKitIDBPath = [NSURL fileURLWithPath:idbPathString.get() isDirectory:YES];
// Subframe of different origins may also create IndexedDB files.
RetainPtr<NSURL> url1 = [NSBundle.test_resourcesBundle URLForResource:@"IndexedDB" withExtension:@"sqlite3"];
RetainPtr<NSURL> url2 = [NSBundle.test_resourcesBundle URLForResource:@"IndexedDB" withExtension:@"sqlite3-shm"];
RetainPtr<NSURL> url3 = [NSBundle.test_resourcesBundle URLForResource:@"IndexedDB" withExtension:@"sqlite3-wal"];
[[NSFileManager defaultManager] createDirectoryAtURL:fileAppleIDBPath.get() withIntermediateDirectories:YES attributes:nil error:nil];
[[NSFileManager defaultManager] copyItemAtURL:url1.get() toURL:[fileAppleIDBPath URLByAppendingPathComponent:@"IndexedDB.sqlite3"] error:nil];
[[NSFileManager defaultManager] copyItemAtURL:url2.get() toURL:[fileAppleIDBPath URLByAppendingPathComponent:@"IndexedDB.sqlite3-shm"] error:nil];
[[NSFileManager defaultManager] copyItemAtURL:url3.get() toURL:[fileAppleIDBPath URLByAppendingPathComponent:@"IndexedDB.sqlite3-wal"] error:nil];
[[NSFileManager defaultManager] createDirectoryAtURL:fileWebKitIDBPath.get() withIntermediateDirectories:YES attributes:nil error:nil];
[[NSFileManager defaultManager] copyItemAtURL:url1.get() toURL:[fileWebKitIDBPath URLByAppendingPathComponent:@"IndexedDB.sqlite3"] error:nil];
[[NSFileManager defaultManager] copyItemAtURL:url2.get() toURL:[fileWebKitIDBPath URLByAppendingPathComponent:@"IndexedDB.sqlite3-shm"] error:nil];
[[NSFileManager defaultManager] copyItemAtURL:url3.get() toURL:[fileWebKitIDBPath URLByAppendingPathComponent:@"IndexedDB.sqlite3-wal"] error:nil];
[dataStore fetchDataRecordsOfTypes:types.get() completionHandler:^(NSArray<WKWebsiteDataRecord *> * records) {
EXPECT_EQ([records count], (unsigned long)3);
for (id record in records) {
if ([[record displayName] isEqual: @"apple.com"]) {
[dataStore removeDataOfTypes:types.get() forDataRecords:@[record] completionHandler:^() {
receivedScriptMessage = true;
EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:fileAppleIDBPath.get().path]);
}];
}
}
}];
receivedScriptMessage = false;
TestWebKitAPI::Util::run(&receivedScriptMessage);
[dataStore removeDataOfTypes:types.get() modifiedSince:[NSDate distantPast] completionHandler:[]() {
receivedScriptMessage = true;
}];
receivedScriptMessage = false;
TestWebKitAPI::Util::run(&receivedScriptMessage);
EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:fileWebKitIDBPath.get().path]);
// Now, with brand new WKWebsiteDataStores pointing at the same custom cookie storage location,
// in newly fired up NetworkProcesses, verify that the fetch and delete APIs work as expected.
[dataStore _terminateNetworkProcess];
}
RetainPtr newCustomDataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
[newCustomDataStore fetchDataRecordsOfTypes:[NSSet setWithObjects:WKWebsiteDataTypeCookies, nil] completionHandler:^(NSArray<WKWebsiteDataRecord *> * records) {
EXPECT_GT([records count], (unsigned long)0);
receivedScriptMessage = true;
}];
receivedScriptMessage = false;
TestWebKitAPI::Util::run(&receivedScriptMessage);
[newCustomDataStore removeDataOfTypes:[NSSet setWithObjects:WKWebsiteDataTypeCookies, nil] modifiedSince:[NSDate distantPast] completionHandler:^ {
receivedScriptMessage = true;
}];
receivedScriptMessage = false;
TestWebKitAPI::Util::run(&receivedScriptMessage);
EXPECT_FALSE([WKWebsiteDataStore _defaultDataStoreExists]);
}
TEST(WebKit, WebsiteDataStoreCustomPathsWithoutPrewarming)
{
runWebsiteDataStoreCustomPaths(ShouldEnableProcessPrewarming::No);
}
TEST(WebKit, WebsiteDataStoreCustomPathsWithPrewarming)
{
runWebsiteDataStoreCustomPaths(ShouldEnableProcessPrewarming::Yes);
}
TEST(WebKit, CustomDataStoreDestroyWhileFetchingNetworkProcessData)
{
NSURL *cookieStorageFile = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/CustomWebsiteData/CookieStorage/Cookie.File" stringByExpandingTildeInPath] isDirectory:NO];
[[NSFileManager defaultManager] removeItemAtURL:cookieStorageFile error:nil];
auto websiteDataTypes = adoptNS([[NSSet alloc] initWithArray:@[WKWebsiteDataTypeCookies]]);
static bool readyToContinue;
auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
websiteDataStoreConfiguration.get()._cookieStorageFile = cookieStorageFile;
@autoreleasepool {
auto dataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
// Fetch records
[dataStore fetchDataRecordsOfTypes:websiteDataTypes.get() completionHandler:^(NSArray<WKWebsiteDataRecord *> *dataRecords) {
EXPECT_EQ((int)dataRecords.count, 0);
readyToContinue = true;
}];
TestWebKitAPI::Util::run(&readyToContinue);
readyToContinue = false;
// Fetch records again, this time releasing our reference to the data store while the request is in flight.
[dataStore fetchDataRecordsOfTypes:websiteDataTypes.get() completionHandler:^(NSArray<WKWebsiteDataRecord *> *dataRecords) {
EXPECT_EQ((int)dataRecords.count, 0);
readyToContinue = true;
}];
dataStore = nil;
}
TestWebKitAPI::Util::run(&readyToContinue);
readyToContinue = false;
@autoreleasepool {
auto dataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
[dataStore fetchDataRecordsOfTypes:websiteDataTypes.get() completionHandler:^(NSArray<WKWebsiteDataRecord *> *dataRecords) {
EXPECT_EQ((int)dataRecords.count, 0);
readyToContinue = true;
}];
// Terminate the network process while a query is pending.
auto* allProcessPools = [WKProcessPool _allProcessPoolsForTesting];
ASSERT_EQ(0U, [allProcessPools count]);
while (![dataStore _networkProcessIdentifier])
TestWebKitAPI::Util::runFor(0.01_s);
kill([dataStore _networkProcessIdentifier], SIGKILL);
allProcessPools = nil;
dataStore = nil;
}
TestWebKitAPI::Util::run(&readyToContinue);
readyToContinue = false;
}
TEST(WebKit, WebsiteDataStoreEphemeral)
{
RetainPtr<WebsiteDataStoreCustomPathsMessageHandler> handler = adoptNS([[WebsiteDataStoreCustomPathsMessageHandler alloc] init]);
RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
[[configuration userContentController] addScriptMessageHandler:handler.get() name:@"testHandler"];
NSURL *defaultResourceLoadStatisticsPath = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/WebsiteData/ResourceLoadStatistics/" stringByExpandingTildeInPath] isDirectory:YES];
[[NSFileManager defaultManager] removeItemAtURL:defaultResourceLoadStatisticsPath error:nil];
EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:defaultResourceLoadStatisticsPath.path]);
configuration.get().websiteDataStore = [WKWebsiteDataStore nonPersistentDataStore];
[configuration.get().websiteDataStore _setResourceLoadStatisticsEnabled:YES];
EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:defaultResourceLoadStatisticsPath.path]);
NSURL *defaultResourceLoadStatisticsFilePath = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/WebsiteData/ResourceLoadStatistics/full_browsing_session_resourceLog.plist" stringByExpandingTildeInPath] isDirectory:NO];
EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:defaultResourceLoadStatisticsFilePath.path]);
RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
NSURLRequest *request = [NSURLRequest requestWithURL:[NSBundle.test_resourcesBundle URLForResource:@"WebsiteDataStoreCustomPaths" withExtension:@"html"]];
[webView loadRequest:request];
__block bool flushed = false;
[configuration.get().websiteDataStore.httpCookieStore _flushCookiesToDiskWithCompletionHandler:^{
flushed = true;
}];
TestWebKitAPI::Util::run(&flushed);
// Forcibly shut down everything of WebKit that we can.
auto pid = [webView _webProcessIdentifier];
if (pid)
kill(pid, SIGKILL);
webView = nil;
handler = nil;
configuration = nil;
[WKWebsiteDataStore defaultDataStore];
EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:defaultResourceLoadStatisticsPath.path]);
EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:defaultResourceLoadStatisticsFilePath.path]);
[[NSFileManager defaultManager] removeItemAtURL:defaultResourceLoadStatisticsPath error:nil];
}
TEST(WebKit, AlternativeServicesDefaultDirectoryCreation)
{
NSURL *defaultDirectory = [NSURL fileURLWithPath:[@"~/Library/Caches/com.apple.WebKit.TestWebKitAPI/WebKit/AlternativeServices" stringByExpandingTildeInPath] isDirectory:YES];
[[NSFileManager defaultManager] removeItemAtURL:defaultDirectory error:nil];
EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:defaultDirectory.path]);
auto webView1 = adoptNS([[TestWKWebView alloc] init]);
[webView1 synchronouslyLoadHTMLString:@"start auxiliary processes" baseURL:nil];
#if HAVE(ALTERNATIVE_SERVICE)
// We always create the path, even if HTTP/3 is turned off.
EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:defaultDirectory.path]);
auto configuration = adoptNS([WKWebViewConfiguration new]);
[configuration setWebsiteDataStore:adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]).get()]).get()];
auto webView2 = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
[webView2 synchronouslyLoadHTMLString:@"start auxiliary processes" baseURL:nil];
EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:defaultDirectory.path]);
[[NSFileManager defaultManager] removeItemAtURL:defaultDirectory error:nil];
#endif // HAVE(ALTERNATIVE_SERVICE)
}
TEST(WebKit, WebsiteDataStoreEphemeralViaConfiguration)
{
RetainPtr<WebsiteDataStoreCustomPathsMessageHandler> handler = adoptNS([[WebsiteDataStoreCustomPathsMessageHandler alloc] init]);
RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
[[configuration userContentController] addScriptMessageHandler:handler.get() name:@"testHandler"];
NSURL *defaultResourceLoadStatisticsPath = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/WebsiteData/ResourceLoadStatistics/" stringByExpandingTildeInPath] isDirectory:YES];
[[NSFileManager defaultManager] removeItemAtURL:defaultResourceLoadStatisticsPath error:nil];
EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:defaultResourceLoadStatisticsPath.path]);
RetainPtr<_WKWebsiteDataStoreConfiguration> dataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] initNonPersistentConfiguration]);
configuration.get().websiteDataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:dataStoreConfiguration.get()]).get();
[configuration.get().websiteDataStore _setResourceLoadStatisticsEnabled:YES];
EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:defaultResourceLoadStatisticsPath.path]);
NSURL *defaultResourceLoadStatisticsFilePath = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/WebsiteData/ResourceLoadStatistics/full_browsing_session_resourceLog.plist" stringByExpandingTildeInPath] isDirectory:NO];
EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:defaultResourceLoadStatisticsFilePath.path]);
RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
NSURLRequest *request = [NSURLRequest requestWithURL:[NSBundle.test_resourcesBundle URLForResource:@"WebsiteDataStoreCustomPaths" withExtension:@"html"]];
[webView loadRequest:request];
__block bool flushed = false;
[configuration.get().websiteDataStore.httpCookieStore _flushCookiesToDiskWithCompletionHandler:^{
flushed = true;
}];
TestWebKitAPI::Util::run(&flushed);
// Forcibly shut down everything of WebKit that we can.
auto pid = [webView _webProcessIdentifier];
if (pid)
kill(pid, SIGKILL);
webView = nil;
handler = nil;
configuration = nil;
[WKWebsiteDataStore defaultDataStore];
EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:defaultResourceLoadStatisticsPath.path]);
EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:defaultResourceLoadStatisticsFilePath.path]);
[[NSFileManager defaultManager] removeItemAtURL:defaultResourceLoadStatisticsPath error:nil];
}
TEST(WebKit, DoLoadWithNonDefaultDataStoreAfterTerminatingNetworkProcess)
{
auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
webViewConfiguration.get().websiteDataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]).get();
auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
NSURLRequest *request = [NSURLRequest requestWithURL:[NSBundle.test_resourcesBundle URLForResource:@"simple" withExtension:@"html"]];
[webView loadRequest:request];
[webView _test_waitForDidFinishNavigation];
TestWebKitAPI::Util::spinRunLoop(1);
[webViewConfiguration.get().websiteDataStore _terminateNetworkProcess];
request = [NSURLRequest requestWithURL:[NSBundle.test_resourcesBundle URLForResource:@"simple2" withExtension:@"html"]];
[webView loadRequest:request];
[webView _test_waitForDidFinishNavigation];
}
TEST(WebKit, WebsiteDataStoreConfigurationPathNull)
{
EXPECT_TRUE([adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]) _indexedDBDatabaseDirectory]);
EXPECT_FALSE([adoptNS([[_WKWebsiteDataStoreConfiguration alloc] initNonPersistentConfiguration]) _indexedDBDatabaseDirectory]);
}
TEST(WebKit, WebsiteDataStoreIfExists)
{
auto webViewConfiguration = adoptNS([WKWebViewConfiguration new]);
EXPECT_FALSE([webViewConfiguration _websiteDataStoreIfExists]);
WKWebsiteDataStore *dataStore = [webViewConfiguration websiteDataStore];
EXPECT_TRUE([webViewConfiguration _websiteDataStoreIfExists]);
EXPECT_TRUE(dataStore._configuration.persistent);
}
TEST(WebKit, WebsiteDataStoreRenameOriginForIndexedDatabase)
{
// Reset defaultDataStore before test.
__block bool done = false;
[[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
done = true;
}];
TestWebKitAPI::Util::run(&done);
done = false;
auto handler = adoptNS([[WebsiteDataStoreCustomPathsMessageHandler alloc] init]);
RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
[[configuration userContentController] addScriptMessageHandler:handler.get() name:@"testHandler"];
auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
NSString *testString = @"<script> \
var request = window.indexedDB.open('testDB'); \
var db; \
request.onupgradeneeded = function(event) { \
db = event.target.result; \
window.webkit.messageHandlers.testHandler.postMessage('database is created'); \
db.onclose = function(event) { \
window.webkit.messageHandlers.testHandler.postMessage('database is closed'); \
} \
}; \
request.onsuccess = function(event) { \
if (!db) { \
db = event.target.result; \
window.webkit.messageHandlers.testHandler.postMessage('database exists'); \
} \
}; \
request.onerror = function(event) { \
if (!db) { \
window.webkit.messageHandlers.testHandler.postMessage('database error: ' + event.target.error.name + ' - ' + event.target.error.message); \
} \
}; \
</script>";
NSURL *exampleURL = [NSURL URLWithString:@"http://example.com/"];
NSURL *webKitURL = [NSURL URLWithString:@"https://webkit.org/"];
[webView loadHTMLString:testString baseURL:exampleURL];
EXPECT_WK_STREQ("database is created", getNextMessage().body);
[webView stringByEvaluatingJavaScript:@"localStorage.setItem('testkey', 'testvalue')"];
auto dataStore = webView.get().configuration.websiteDataStore;
auto indexedDBType = adoptNS([[NSSet alloc] initWithObjects:WKWebsiteDataTypeIndexedDBDatabases, nil]);
[dataStore _renameOrigin:exampleURL to:webKitURL forDataOfTypes:indexedDBType.get() completionHandler:^{
done = true;
}];
TestWebKitAPI::Util::run(&done);
done = false;
EXPECT_WK_STREQ("database is closed", getNextMessage().body);
[webView loadHTMLString:testString baseURL:webKitURL];
EXPECT_WK_STREQ("database exists", getNextMessage().body);
// Ensure LocalStorage data is not moved.
EXPECT_WK_STREQ("<null>", [webView stringByEvaluatingJavaScript:@"localStorage.getItem('testkey')"]);
[webView loadHTMLString:testString baseURL:exampleURL];
EXPECT_WK_STREQ("database is created", getNextMessage().body);
// Clean up defaultDataStore after test.
[[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
done = true;
}];
TestWebKitAPI::Util::run(&done);
}
TEST(WebKit, WebsiteDataStoreRenameOrigin)
{
auto webView = adoptNS([[TestWKWebView alloc] init]);
[webView synchronouslyLoadHTMLString:@"<script>localStorage.setItem('testkey', 'testvalue')</script>" baseURL:[NSURL URLWithString:@"https://example.com/"]];
__block bool done = false;
NSURL *exampleURL = [NSURL URLWithString:@"https://example.com/"];
NSURL *webKitURL = [NSURL URLWithString:@"https://webkit.org/"];
WKWebsiteDataStore *dataStore = [webView configuration].websiteDataStore;
NSSet *localStorageSet = [NSSet setWithObject:WKWebsiteDataTypeLocalStorage];
[dataStore _renameOrigin:exampleURL to:webKitURL forDataOfTypes:localStorageSet completionHandler:^{
done = true;
}];
TestWebKitAPI::Util::run(&done);
[webView synchronouslyLoadHTMLString:@"hello" baseURL:webKitURL];
EXPECT_WK_STREQ([webView objectByEvaluatingJavaScript:@"localStorage.getItem('testkey')"], "testvalue");
[webView synchronouslyLoadHTMLString:@"hello" baseURL:exampleURL];
EXPECT_TRUE([[webView objectByEvaluatingJavaScript:@"localStorage.getItem('testkey')"] isKindOfClass:[NSNull class]]);
done = false;
[dataStore fetchDataRecordsOfTypes:localStorageSet completionHandler:^(NSArray<WKWebsiteDataRecord *> *records) {
[dataStore removeDataOfTypes:localStorageSet forDataRecords:records completionHandler:^{
done = true;
}];
}];
TestWebKitAPI::Util::run(&done);
}
TEST(WebKit, NetworkCacheDirectory)
{
using namespace TestWebKitAPI;
HTTPServer server([] (Connection connection) {
connection.receiveHTTPRequest([=] (Vector<char>&&) {
constexpr auto response =
"HTTP/1.1 200 OK\r\n"
"Cache-Control: max-age=1000000\r\n"
"Content-Length: 6\r\n\r\n"
"Hello!"_s;
connection.send(response);
});
});
NSURL *tempDir = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"CustomPathsTest"] isDirectory:YES];
auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
[websiteDataStoreConfiguration setNetworkCacheDirectory:tempDir];
auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
[webViewConfiguration setWebsiteDataStore:adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]).get()];
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
[webView synchronouslyLoadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://127.0.0.1:%d/", server.port()]]]];
NSString *path = tempDir.path;
NSFileManager *fileManager = [NSFileManager defaultManager];
while (![fileManager fileExistsAtPath:path])
Util::spinRunLoop();
NSError *error = nil;
[fileManager removeItemAtPath:path error:&error];
EXPECT_FALSE(error);
}
TEST(WebKit, NetworkCacheExcludedFromBackup)
{
TestWebKitAPI::HTTPServer server([] (TestWebKitAPI::Connection connection) {
connection.receiveHTTPRequest([=] (Vector<char>&&) {
constexpr auto response =
"HTTP/1.1 200 OK\r\n"
"Cache-Control: max-age=1000000\r\n"
"Content-Length: 6\r\n\r\n"
"Hello!"_s;
connection.send(response);
});
});
NSURL *networkCacheDirectory = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/CustomWebsiteData/NetworkCache" stringByExpandingTildeInPath] isDirectory:YES];
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager removeItemAtURL:networkCacheDirectory error:nil];
[fileManager createDirectoryAtURL:networkCacheDirectory withIntermediateDirectories:YES attributes:nil error:nil];
auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
[websiteDataStoreConfiguration setNetworkCacheDirectory:networkCacheDirectory];
auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
[webViewConfiguration setWebsiteDataStore:adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]).get()];
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
[webView synchronouslyLoadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://127.0.0.1:%d/", server.port()]]]];
NSArray *networkCacheFiles = [fileManager contentsOfDirectoryAtPath:networkCacheDirectory.path error:nil];
NSNumber *isExcluded = nil;
for (NSString *fileName in networkCacheFiles) {
NSURL *networkCacheFile = [networkCacheDirectory URLByAppendingPathComponent:fileName];
EXPECT_TRUE([networkCacheFile getResourceValue:&isExcluded forKey:NSURLIsExcludedFromBackupKey error:nil]);
EXPECT_TRUE(isExcluded.boolValue);
}
}
#if HAVE(ALTERNATIVE_SERVICE)
static void checkUntilEntryFound(WKWebsiteDataStore *dataStore, void(^completionHandler)(NSArray<WKWebsiteDataRecord *> *))
{
[dataStore fetchDataRecordsOfTypes:[NSSet setWithObject:_WKWebsiteDataTypeAlternativeServices] completionHandler:^(NSArray<WKWebsiteDataRecord *> *records) {
if (records.count)
completionHandler(records);
else
checkUntilEntryFound(dataStore, completionHandler);
}];
}
// This test needs to remain disabled until rdar://problem/59644683 is resolved.
TEST(WebKit, DISABLED_AlternativeService)
{
using namespace TestWebKitAPI;
HTTPServer server({
{ "/"_s, { {{ "alt-svc"_s, "h3-24=\":443\"; ma=3600; persist=1"_s }}, "<html>test content</html>"_s } }
}, HTTPServer::Protocol::Https);
NSURL *tempDir = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"AlternativeServiceTest"] isDirectory:YES];
NSString *path = [tempDir URLByAppendingPathComponent:@"AlternativeService.sqlite"].path;
NSError *error = nil;
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager removeItemAtPath:path error:&error];
auto dataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
[dataStoreConfiguration setAlternativeServicesStorageDirectory:tempDir];
auto dataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:dataStoreConfiguration.get()]);
__block bool done = false;
[dataStore fetchDataRecordsOfTypes:[NSSet setWithObject:_WKWebsiteDataTypeAlternativeServices] completionHandler:^(NSArray<WKWebsiteDataRecord *> *records) {
EXPECT_EQ(records.count, 0u);
done = true;
}];
Util::run(&done);
auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
[webViewConfiguration setWebsiteDataStore:dataStore.get()];
auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
auto delegate = adoptNS([[TestNavigationDelegate alloc] init]);
[webView setNavigationDelegate:delegate.get()];
delegate.get().didReceiveAuthenticationChallenge = ^(WKWebView *, NSURLAuthenticationChallenge *challenge, void (^completionHandler)(NSURLSessionAuthChallengeDisposition, NSURLCredential *)) {
EXPECT_WK_STREQ(challenge.protectionSpace.authenticationMethod, NSURLAuthenticationMethodServerTrust);
completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
};
[webView loadRequest:server.request()];
done = false;
checkUntilEntryFound(dataStore.get(), ^(NSArray<WKWebsiteDataRecord *> *records) {
EXPECT_EQ(records.count, 1u);
[dataStore removeDataOfTypes:[NSSet setWithObject:_WKWebsiteDataTypeAlternativeServices] forDataRecords:records completionHandler:^{
[dataStore fetchDataRecordsOfTypes:[NSSet setWithObject:_WKWebsiteDataTypeAlternativeServices] completionHandler:^(NSArray<WKWebsiteDataRecord *> *records) {
EXPECT_EQ(records.count, 0u);
done = true;
}];
}];
});
Util::run(&done);
EXPECT_TRUE([fileManager fileExistsAtPath:path]);
// We can't unlink the sqlite file because it is guarded by the network process right now.
// We delete it before running this test the next time.
}
#endif // HAVE(ALTERNATIVE_SERVICE)
static void respondToRangeRequests(const TestWebKitAPI::Connection& connection, const RetainPtr<NSData>& data)
{
connection.receiveHTTPRequest([=] (Vector<char>&& bytes) {
StringView request(bytes.span());
auto rangeBytes = "Range: bytes="_s;
auto begin = request.find(StringView(rangeBytes), 0);
ASSERT(begin != notFound);
auto dash = request.find('-', begin);
ASSERT(dash != notFound);
auto end = request.find('\r', dash);
ASSERT(end != notFound);
auto rangeBegin = parseInteger<uint64_t>(request.substring(begin + rangeBytes.length(), dash - begin - rangeBytes.length())).value();
auto rangeEnd = parseInteger<uint64_t>(request.substring(dash + 1, end - dash - 1)).value();
NSString *responseHeaderString = [NSString stringWithFormat:
@"HTTP/1.1 206 Partial Content\r\n"
"Content-Range: bytes %llu-%llu/%llu\r\n"
"Content-Length: %llu\r\n\r\n",
rangeBegin, rangeEnd, static_cast<uint64_t>(data.get().length), rangeEnd - rangeBegin];
NSData *responseHeader = [responseHeaderString dataUsingEncoding:NSUTF8StringEncoding];
NSData *responseBody = [data subdataWithRange:NSMakeRange(rangeBegin, rangeEnd - rangeBegin)];
auto response = makeVector(responseHeader);
response.append(span(responseBody));
connection.send(WTF::move(response), [=] {
respondToRangeRequests(connection, data);
});
});
}
TEST(WebKit, MediaCache)
{
JSC::Config::configureForTesting();
using namespace TestWebKitAPI;
RetainPtr<NSData> data = [NSData dataWithContentsOfURL:[NSBundle.test_resourcesBundle URLForResource:@"test" withExtension:@"mp4"]];
HTTPServer server([&] (Connection connection) {
connection.receiveHTTPRequest([=] (Vector<char>&&) {
constexpr auto firstResponse =
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html\r\n"
"Content-Length: 55\r\n\r\n"
"<video><source src='test.mp4' type='video/mp4'></video>"_s;
connection.send(firstResponse, [=] {
respondToRangeRequests(connection, data);
});
});
});
NSURL *tempDir = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"CustomPathsTest"] isDirectory:YES];
NSString *path = tempDir.path;
NSFileManager *fileManager = [NSFileManager defaultManager];
EXPECT_FALSE([fileManager fileExistsAtPath:path]);
auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
[websiteDataStoreConfiguration setMediaCacheDirectory:tempDir];
auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
[webViewConfiguration setWebsiteDataStore:adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]).get()];
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
[webView synchronouslyLoadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://127.0.0.1:%d/", server.port()]]]];
NSError *error = nil;
while (![fileManager contentsOfDirectoryAtPath:path error:&error].count)
Util::spinRunLoop();
EXPECT_FALSE(error);
[[webView configuration].websiteDataStore _terminateNetworkProcess];
[fileManager removeItemAtPath:path error:&error];
EXPECT_FALSE(error);
}
TEST(WebKit, MigrateLocalStorageDataToGeneralStorageDirectory)
{
NSURL *localStorageDirectory = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/CustomWebsiteData/LocalStorage/" stringByExpandingTildeInPath] isDirectory:YES];
NSURL *localStorageFile = [localStorageDirectory URLByAppendingPathComponent:@"https_webkit.org_0.localstorage"];
NSURL *generalStorageDirectory = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/CustomWebsiteData/Default" stringByExpandingTildeInPath] isDirectory:YES];
NSURL *resourceSalt = [NSBundle.test_resourcesBundle URLForResource:@"general-storage-directory" withExtension:@"salt"];
NSURL *newLocalStorageFile = [generalStorageDirectory URLByAppendingPathComponent:@"YUn_wgR51VLVo9lc5xiivAzZ8TMmojoa0IbW323qibs/YUn_wgR51VLVo9lc5xiivAzZ8TMmojoa0IbW323qibs/LocalStorage/localstorage.sqlite3"];
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager removeItemAtURL:localStorageDirectory error:nil];
[fileManager removeItemAtURL:generalStorageDirectory error:nil];
[fileManager createDirectoryAtURL:generalStorageDirectory withIntermediateDirectories:YES attributes:nil error:nil];
[fileManager copyItemAtURL:resourceSalt toURL:[generalStorageDirectory URLByAppendingPathComponent:@"salt"] error:nil];
NSString *htmlString = @"<script> \
result = localStorage.getItem('testkey'); \
if (!result) \
result = '[null]'; \
window.webkit.messageHandlers.testHandler.postMessage(result); \
</script>";
auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
websiteDataStoreConfiguration.get()._webStorageDirectory = localStorageDirectory;
websiteDataStoreConfiguration.get().generalStorageDirectory = generalStorageDirectory;
websiteDataStoreConfiguration.get().unifiedOriginStorageLevel = _WKUnifiedOriginStorageLevelNone;
auto messageHandler = adoptNS([[WebsiteDataStoreCustomPathsMessageHandler alloc] init]);
@autoreleasepool {
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
[configuration setWebsiteDataStore:adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]).get()];
[[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"testHandler"];
auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
receivedScriptMessage = false;
[webView loadHTMLString:htmlString baseURL:[NSURL URLWithString:@"https://webkit.org/"]];
TestWebKitAPI::Util::run(&receivedScriptMessage);
EXPECT_WK_STREQ("[null]", getNextMessage().body);
[webView stringByEvaluatingJavaScript:@"localStorage.setItem('testkey', 'testvalue')"];
// Ensure item is stored by getting it in another WKWebView.
auto secondWebView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
receivedScriptMessage = false;
[secondWebView loadHTMLString:htmlString baseURL:[NSURL URLWithString:@"https://webkit.org/"]];
TestWebKitAPI::Util::run(&receivedScriptMessage);
EXPECT_WK_STREQ("testvalue", getNextMessage().body);
EXPECT_TRUE([fileManager fileExistsAtPath:localStorageFile.path]);
EXPECT_FALSE([fileManager fileExistsAtPath:newLocalStorageFile.path]);
}
// Create a new WebsiteDataStore that performs migration.
websiteDataStoreConfiguration.get().unifiedOriginStorageLevel = _WKUnifiedOriginStorageLevelBasic;
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
[configuration setWebsiteDataStore:adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]).get()];
[[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"testHandler"];
auto thirdWebView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
receivedScriptMessage = false;
[thirdWebView loadHTMLString:htmlString baseURL:[NSURL URLWithString:@"https://webkit.org/"]];
TestWebKitAPI::Util::run(&receivedScriptMessage);
EXPECT_WK_STREQ("testvalue", getNextMessage().body);
EXPECT_FALSE([fileManager fileExistsAtPath:localStorageFile.path]);
EXPECT_TRUE([fileManager fileExistsAtPath:newLocalStorageFile.path]);
}
TEST(WebKit, MigrateIndexedDBDataToGeneralStorageDirectory)
{
NSURL *indexedDBDirectory = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/CustomWebsiteData/IndexedDB" stringByExpandingTildeInPath] isDirectory:YES];
NSURL *indexedDBOriginDirectory = [indexedDBDirectory URLByAppendingPathComponent:@"v1/https_webkit.org_0"];
static constexpr auto indexedDBDatabaseName = "TestDatabase"_s;
RetainPtr hashedIndexedDBDatabaseName = WebCore::SQLiteFileSystem::computeHashForFileName(indexedDBDatabaseName).createNSString();
NSURL *indexedDBDatabaseDirectory = [indexedDBOriginDirectory URLByAppendingPathComponent:hashedIndexedDBDatabaseName.get()];
NSURL *indexedDBFile = [indexedDBDatabaseDirectory URLByAppendingPathComponent:@"IndexedDB.sqlite3"];
NSURL *generalStorageDirectory = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/CustomWebsiteData/Default" stringByExpandingTildeInPath] isDirectory:YES];
NSURL *resourceSalt = [NSBundle.test_resourcesBundle URLForResource:@"general-storage-directory" withExtension:@"salt"];
NSURL *newIndexedDBOriginDirectory = [generalStorageDirectory URLByAppendingPathComponent:@"YUn_wgR51VLVo9lc5xiivAzZ8TMmojoa0IbW323qibs/YUn_wgR51VLVo9lc5xiivAzZ8TMmojoa0IbW323qibs/IndexedDB/"];
NSURL *newIndexedDBDirectory = [newIndexedDBOriginDirectory URLByAppendingPathComponent:hashedIndexedDBDatabaseName.get()];
NSURL *newIndexedDBFile = [newIndexedDBDirectory URLByAppendingPathComponent:@"IndexedDB.sqlite3"];
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager removeItemAtURL:indexedDBDirectory error:nil];
[fileManager removeItemAtURL:generalStorageDirectory error:nil];
[fileManager createDirectoryAtURL:generalStorageDirectory withIntermediateDirectories:YES attributes:nil error:nil];
[fileManager copyItemAtURL:resourceSalt toURL:[generalStorageDirectory URLByAppendingPathComponent:@"salt"] error:nil];
NSString *htmlString = @"<script> \
var request = window.indexedDB.open('TestDatabase'); \
var upgradeneededReceived = false; \
request.onupgradeneeded = function(event) { \
var db = event.target.result; \
var os = db.createObjectStore('TestObjectStore'); \
os.put('value', 'key'); \
window.webkit.messageHandlers.testHandler.postMessage('database is created'); \
upgradeneededReceived = true; \
}; \
request.onsuccess = function(event) { \
if (upgradeneededReceived) \
return; \
db = event.target.result; \
db.transaction('TestObjectStore').objectStore('TestObjectStore').get('key').onsuccess = function(event) { \
var result = event.target.result ? event.target.result : '[null]'; \
window.webkit.messageHandlers.testHandler.postMessage(result); \
}; \
}; \
</script>";
auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
websiteDataStoreConfiguration.get()._indexedDBDatabaseDirectory = indexedDBDirectory;
websiteDataStoreConfiguration.get().generalStorageDirectory = generalStorageDirectory;
websiteDataStoreConfiguration.get().unifiedOriginStorageLevel = _WKUnifiedOriginStorageLevelNone;
auto messageHandler = adoptNS([[WebsiteDataStoreCustomPathsMessageHandler alloc] init]);
@autoreleasepool {
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
[configuration setWebsiteDataStore:adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]).get()];
[[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"testHandler"];
auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
receivedScriptMessage = false;
[webView loadHTMLString:htmlString baseURL:[NSURL URLWithString:@"https://webkit.org/"]];
TestWebKitAPI::Util::run(&receivedScriptMessage);
EXPECT_WK_STREQ("database is created", getNextMessage().body);
// Ensure item is stored by getting it in another WKWebView.
auto secondWebView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
receivedScriptMessage = false;
[secondWebView loadHTMLString:htmlString baseURL:[NSURL URLWithString:@"https://webkit.org/"]];
TestWebKitAPI::Util::run(&receivedScriptMessage);
EXPECT_WK_STREQ("value", getNextMessage().body);
EXPECT_TRUE([fileManager fileExistsAtPath:indexedDBFile.path]);
}
// Create a new WebsiteDataStore that performs migration.
websiteDataStoreConfiguration.get().unifiedOriginStorageLevel = _WKUnifiedOriginStorageLevelBasic;
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
[configuration setWebsiteDataStore:adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]).get()];
[[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"testHandler"];
auto thirdWebView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
receivedScriptMessage = false;
[thirdWebView loadHTMLString:htmlString baseURL:[NSURL URLWithString:@"https://webkit.org/"]];
TestWebKitAPI::Util::run(&receivedScriptMessage);
EXPECT_WK_STREQ("value", getNextMessage().body);
EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:indexedDBFile.path]);
EXPECT_TRUE([fileManager fileExistsAtPath:newIndexedDBFile.path]);
}
TEST(WebKit, FetchDataAfterEnablingGeneralStorageDirectory)
{
NSURL *resourceSalt = [NSBundle.test_resourcesBundle URLForResource:@"general-storage-directory" withExtension:@"salt"];
NSURL *resourceLocalStorageFile = [NSBundle.test_resourcesBundle URLForResource:@"general-storage-directory-localstorage" withExtension:@"sqlite3"];
NSURL *resourceIndexedDBFile = [NSBundle.test_resourcesBundle URLForResource:@"general-storage-directory-indexeddb" withExtension:@"sqlite3"];
NSURL *resourceOriginFile = [NSBundle.test_resourcesBundle URLForResource:@"general-storage-directory" withExtension:@"origin"];
NSURL *customLocalStorageDirectory = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/CustomWebsiteData/LocalStorage/" stringByExpandingTildeInPath] isDirectory:YES];
NSURL *appleCustomLocalStorageFile = [customLocalStorageDirectory URLByAppendingPathComponent:@"https_apple.com_0.localstorage"];
NSURL *customIndexedDBStorageDirectory = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/CustomWebsiteData/IndexedDB/" stringByExpandingTildeInPath] isDirectory:YES];
NSURL *appleCustomIndexedDBDatabaseDirectory = [customIndexedDBStorageDirectory URLByAppendingPathComponent:@"v1/https_apple.com_0/620AD548000B0A49C02D2E8D75C32E0F85697B54DF86146ECAE360DE104AB3F9"];
NSURL *appleCustomIndexedDBDatabaseFile = [appleCustomIndexedDBDatabaseDirectory URLByAppendingPathComponent:@"IndexedDB.sqlite3"];
NSURL *generalStorageDirectory = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/CustomWebsiteData/Default" stringByExpandingTildeInPath] isDirectory:YES];
NSURL *webkitGeneralStorageDirectory = [generalStorageDirectory URLByAppendingPathComponent:@"YUn_wgR51VLVo9lc5xiivAzZ8TMmojoa0IbW323qibs/YUn_wgR51VLVo9lc5xiivAzZ8TMmojoa0IbW323qibs"];
NSURL *webkitGeneralLocalStorageDirectory = [webkitGeneralStorageDirectory URLByAppendingPathComponent:@"LocalStorage"];
NSURL *webKitGeneralLocalStorageFile = [webkitGeneralLocalStorageDirectory URLByAppendingPathComponent:@"localstorage.sqlite3"];
NSURL *appleGeneralStorageDirectory = [generalStorageDirectory URLByAppendingPathComponent:@"xKK8XQeHebxhSzMyotOOtNKp2jWZ4l_CEN4WggYYXbI/xKK8XQeHebxhSzMyotOOtNKp2jWZ4l_CEN4WggYYXbI"];
NSURL *appleGeneralOriginFile = [appleGeneralStorageDirectory URLByAppendingPathComponent:@"origin"];
NSURL *appleGeneralLocalStorageFile = [appleGeneralStorageDirectory URLByAppendingPathComponent:@"LocalStorage/localstorage.sqlite3"];
NSURL *appleGeneralIndexedDBDatabaseFile = [appleGeneralStorageDirectory URLByAppendingPathComponent:@"IndexedDB/620AD548000B0A49C02D2E8D75C32E0F85697B54DF86146ECAE360DE104AB3F9/IndexedDB.sqlite3"];
// Copy apple.com files to custom path.
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager removeItemAtURL:customLocalStorageDirectory error:nil];
[fileManager createDirectoryAtURL:customLocalStorageDirectory withIntermediateDirectories:YES attributes:nil error:nil];
[fileManager copyItemAtURL:resourceLocalStorageFile toURL:appleCustomLocalStorageFile error:nil];
[fileManager createDirectoryAtURL:appleCustomIndexedDBDatabaseDirectory withIntermediateDirectories:YES attributes:nil error:nil];
[fileManager copyItemAtURL:resourceIndexedDBFile toURL:appleCustomIndexedDBDatabaseFile error:nil];
// Copy webkit.org files to new path.
[fileManager removeItemAtURL:generalStorageDirectory error:nil];
[fileManager createDirectoryAtURL:webkitGeneralLocalStorageDirectory withIntermediateDirectories:YES attributes:nil error:nil];
[fileManager copyItemAtURL:resourceSalt toURL:[generalStorageDirectory URLByAppendingPathComponent:@"salt"] error:nil];
[fileManager copyItemAtURL:resourceOriginFile toURL:[webkitGeneralStorageDirectory URLByAppendingPathComponent:@"origin"] error:nil];
[fileManager copyItemAtURL:resourceLocalStorageFile toURL:webKitGeneralLocalStorageFile error:nil];
auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
websiteDataStoreConfiguration.get()._webStorageDirectory = customLocalStorageDirectory;
websiteDataStoreConfiguration.get()._indexedDBDatabaseDirectory = customIndexedDBStorageDirectory;
websiteDataStoreConfiguration.get().generalStorageDirectory = generalStorageDirectory;
websiteDataStoreConfiguration.get().unifiedOriginStorageLevel = _WKUnifiedOriginStorageLevelBasic;
auto websiteDataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
// Ensure data is fetched from both custom path and new path.
auto dataTypes = [NSSet setWithObjects:WKWebsiteDataTypeLocalStorage, WKWebsiteDataTypeIndexedDBDatabases, nil];
auto sortFunction = ^(WKWebsiteDataRecord *record1, WKWebsiteDataRecord *record2) {
return [record1.displayName compare:record2.displayName];
};
done = false;
[websiteDataStore fetchDataRecordsOfTypes:dataTypes completionHandler:^(NSArray<WKWebsiteDataRecord *> *records) {
EXPECT_EQ(records.count, 2u);
auto sortedRecords = [records sortedArrayUsingComparator:sortFunction];
auto appleRecord = [sortedRecords objectAtIndex:0];
EXPECT_WK_STREQ(@"apple.com", [appleRecord displayName]);
EXPECT_TRUE([[appleRecord dataTypes] isEqualToSet:dataTypes]);
auto webkitRecord = [sortedRecords objectAtIndex:1];
EXPECT_WK_STREQ(@"webkit.org", [webkitRecord displayName]);
EXPECT_TRUE([[webkitRecord dataTypes] isEqualToSet:[NSSet setWithObject:WKWebsiteDataTypeLocalStorage]]);
done = true;
}];
TestWebKitAPI::Util::run(&done);
// Ensure apple.com data is moved to new path after fetch.
EXPECT_FALSE([fileManager fileExistsAtPath:appleCustomLocalStorageFile.path]);
EXPECT_FALSE([fileManager fileExistsAtPath:appleCustomIndexedDBDatabaseFile.path]);
EXPECT_TRUE([fileManager fileExistsAtPath:appleGeneralOriginFile.path]);
EXPECT_TRUE([fileManager fileExistsAtPath:appleGeneralLocalStorageFile.path]);
EXPECT_TRUE([fileManager fileExistsAtPath:appleGeneralIndexedDBDatabaseFile.path]);
done = false;
[websiteDataStore fetchDataRecordsOfTypes:dataTypes completionHandler:^(NSArray<WKWebsiteDataRecord *> *records) {
EXPECT_EQ(records.count, 2u);
auto sortedRecords = [records sortedArrayUsingComparator:sortFunction];
EXPECT_WK_STREQ(@"apple.com", [[sortedRecords objectAtIndex:0] displayName]);
done = true;
}];
TestWebKitAPI::Util::run(&done);
}
TEST(WebKit, DeleteDataAfterEnablingGeneralStorageDirectory)
{
NSURL *resourceSalt = [NSBundle.test_resourcesBundle URLForResource:@"general-storage-directory" withExtension:@"salt"];
NSURL *resourceLocalStorageFile = [NSBundle.test_resourcesBundle URLForResource:@"general-storage-directory-localstorage" withExtension:@"sqlite3"];
NSURL *resourceIndexedDBFile = [NSBundle.test_resourcesBundle URLForResource:@"general-storage-directory-indexeddb" withExtension:@"sqlite3"];
NSURL *resourceOriginFile = [NSBundle.test_resourcesBundle URLForResource:@"general-storage-directory" withExtension:@"origin"];
NSURL *customLocalStorageDirectory = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/CustomWebsiteData/LocalStorage/" stringByExpandingTildeInPath] isDirectory:YES];
NSURL *appleCustomLocalStorageFile = [customLocalStorageDirectory URLByAppendingPathComponent:@"https_apple.com_0.localstorage"];
NSURL *customIndexedDBStorageDirectory = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/CustomWebsiteData/IndexedDB/" stringByExpandingTildeInPath] isDirectory:YES];
NSURL *appleCustomIndexedDBDatabaseDirectory = [customIndexedDBStorageDirectory URLByAppendingPathComponent:@"v1/https_apple.com_0/620AD548000B0A49C02D2E8D75C32E0F85697B54DF86146ECAE360DE104AB3F9"];
NSURL *appleCustomIndexedDBDatabaseFile = [appleCustomIndexedDBDatabaseDirectory URLByAppendingPathComponent:@"IndexedDB.sqlite3"];
NSURL *generalStorageDirectory = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/CustomWebsiteData/Default" stringByExpandingTildeInPath] isDirectory:YES];
NSURL *webkitGeneralStorageDirectory = [generalStorageDirectory URLByAppendingPathComponent:@"YUn_wgR51VLVo9lc5xiivAzZ8TMmojoa0IbW323qibs/YUn_wgR51VLVo9lc5xiivAzZ8TMmojoa0IbW323qibs"];
NSURL *webkitGeneralLocalStorageDirectory = [webkitGeneralStorageDirectory URLByAppendingPathComponent:@"LocalStorage"];
NSURL *webKitGeneralLocalStorageFile = [webkitGeneralLocalStorageDirectory URLByAppendingPathComponent:@"localstorage.sqlite3"];
NSURL *appleGeneralLocalStorageFile = [generalStorageDirectory URLByAppendingPathComponent:@"xKK8XQeHebxhSzMyotOOtNKp2jWZ4l_CEN4WggYYXbI/xKK8XQeHebxhSzMyotOOtNKp2jWZ4l_CEN4WggYYXbI/LocalStorage/localstorage.sqlite3"];
NSURL *appleGeneralIndexedDBDatabaseFile = [generalStorageDirectory URLByAppendingPathComponent:@"xKK8XQeHebxhSzMyotOOtNKp2jWZ4l_CEN4WggYYXbI/xKK8XQeHebxhSzMyotOOtNKp2jWZ4l_CEN4WggYYXbI/IndexedDB/620AD548000B0A49C02D2E8D75C32E0F85697B54DF86146ECAE360DE104AB3F9/IndexedDB.sqlite3"];
// Copy apple.com files to custom path.
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager removeItemAtURL:customLocalStorageDirectory error:nil];
[fileManager createDirectoryAtURL:customLocalStorageDirectory withIntermediateDirectories:YES attributes:nil error:nil];
[fileManager copyItemAtURL:resourceLocalStorageFile toURL:appleCustomLocalStorageFile error:nil];
[fileManager createDirectoryAtURL:appleCustomIndexedDBDatabaseDirectory withIntermediateDirectories:YES attributes:nil error:nil];
[fileManager copyItemAtURL:resourceIndexedDBFile toURL:appleCustomIndexedDBDatabaseFile error:nil];
// Copy webkit.org files to new path.
[fileManager removeItemAtURL:generalStorageDirectory error:nil];
[fileManager createDirectoryAtURL:webkitGeneralLocalStorageDirectory withIntermediateDirectories:YES attributes:nil error:nil];
[fileManager copyItemAtURL:resourceSalt toURL:[generalStorageDirectory URLByAppendingPathComponent:@"salt"] error:nil];
[fileManager copyItemAtURL:resourceOriginFile toURL:[webkitGeneralStorageDirectory URLByAppendingPathComponent:@"origin"] error:nil];
[fileManager copyItemAtURL:resourceLocalStorageFile toURL:webKitGeneralLocalStorageFile error:nil];
auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
websiteDataStoreConfiguration.get()._webStorageDirectory = customLocalStorageDirectory;
websiteDataStoreConfiguration.get()._indexedDBDatabaseDirectory = customIndexedDBStorageDirectory;
websiteDataStoreConfiguration.get().generalStorageDirectory = generalStorageDirectory;
websiteDataStoreConfiguration.get().unifiedOriginStorageLevel = _WKUnifiedOriginStorageLevelBasic;
auto websiteDataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
auto dataTypes = [NSSet setWithObjects:WKWebsiteDataTypeLocalStorage, WKWebsiteDataTypeIndexedDBDatabases, nil];
done = false;
[websiteDataStore removeDataOfTypes:dataTypes modifiedSince:[NSDate distantPast] completionHandler:^ {
done = true;
}];
TestWebKitAPI::Util::run(&done);
// Ensure data is deleted from both custom path and new path.
EXPECT_FALSE([fileManager fileExistsAtPath:appleCustomLocalStorageFile.path]);
EXPECT_FALSE([fileManager fileExistsAtPath:appleCustomIndexedDBDatabaseFile.path]);
EXPECT_FALSE([fileManager fileExistsAtPath:webKitGeneralLocalStorageFile.path]);
// Ensure apple.com data is not just moved but actually deleted.
EXPECT_FALSE([fileManager fileExistsAtPath:appleGeneralLocalStorageFile.path]);
EXPECT_FALSE([fileManager fileExistsAtPath:appleGeneralIndexedDBDatabaseFile.path]);
// Ensure data records do not exist after deletion.
done = false;
[websiteDataStore fetchDataRecordsOfTypes:dataTypes completionHandler:^(NSArray<WKWebsiteDataRecord *> *records) {
EXPECT_EQ(records.count, 0u);
done = true;
}];
TestWebKitAPI::Util::run(&done);
}
TEST(WebKit, CreateOriginFileWhenNeeded)
{
NSURL *generalStorageDirectory = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/CustomWebsiteData/Default" stringByExpandingTildeInPath] isDirectory:YES];
NSURL *resourceSalt = [NSBundle.test_resourcesBundle URLForResource:@"general-storage-directory" withExtension:@"salt"];
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager removeItemAtURL:generalStorageDirectory error:nil];
[fileManager createDirectoryAtURL:generalStorageDirectory withIntermediateDirectories:YES attributes:nil error:nil];
[fileManager copyItemAtURL:resourceSalt toURL:[generalStorageDirectory URLByAppendingPathComponent:@"salt"] error:nil];
NSURL *originFile = [generalStorageDirectory URLByAppendingPathComponent:@"YUn_wgR51VLVo9lc5xiivAzZ8TMmojoa0IbW323qibs/YUn_wgR51VLVo9lc5xiivAzZ8TMmojoa0IbW323qibs/origin"];
auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
[fileManager removeItemAtURL:websiteDataStoreConfiguration.get()._webStorageDirectory error:nil];
websiteDataStoreConfiguration.get().unifiedOriginStorageLevel = _WKUnifiedOriginStorageLevelBasic;
websiteDataStoreConfiguration.get().generalStorageDirectory = generalStorageDirectory;
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
[configuration setWebsiteDataStore:adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]).get()];
auto handler = adoptNS([[WebsiteDataStoreCustomPathsMessageHandler alloc] init]);
[[configuration userContentController] addScriptMessageHandler:handler.get() name:@"testHandler"];
auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
NSString *htmlString = @"<script> \
localStorage.getItem('testkey'); \
window.webkit.messageHandlers.testHandler.postMessage('done'); \
</script>";
receivedScriptMessage = false;
[webView loadHTMLString:htmlString baseURL:[NSURL URLWithString:@"https://webkit.org/"]];
TestWebKitAPI::Util::run(&receivedScriptMessage);
// No origin file for read.
EXPECT_FALSE([fileManager fileExistsAtPath:originFile.path]);
[webView stringByEvaluatingJavaScript:@"localStorage.setItem('testkey', 'testvalue')"];
// Origin file is created for write.
while (![fileManager fileExistsAtPath:originFile.path])
TestWebKitAPI::Util::spinRunLoop();
}
TEST(WKWebsiteDataStoreConfiguration, SameCustomPathForDifferentTypes)
{
auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
auto dataTypes = [NSSet setWithObjects:WKWebsiteDataTypeLocalStorage, WKWebsiteDataTypeIndexedDBDatabases, WKWebsiteDataTypeCookies, nil];
NSURL *sharedDirectory = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/SamePathForDifferentTypes" stringByExpandingTildeInPath] isDirectory:YES];
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager removeItemAtURL:sharedDirectory error:nil];
websiteDataStoreConfiguration.get()._webSQLDatabaseDirectory = sharedDirectory;
websiteDataStoreConfiguration.get()._webStorageDirectory = sharedDirectory;
websiteDataStoreConfiguration.get()._indexedDBDatabaseDirectory = sharedDirectory;
websiteDataStoreConfiguration.get()._serviceWorkerRegistrationDirectory = sharedDirectory;
websiteDataStoreConfiguration.get()._cookieStorageFile = [sharedDirectory URLByAppendingPathComponent:@"Cookies.binarycookies" isDirectory:NO];
websiteDataStoreConfiguration.get().generalStorageDirectory = [sharedDirectory URLByAppendingPathComponent:@"Default" isDirectory:YES];
@autoreleasepool {
auto websiteDataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
[configuration setWebsiteDataStore:websiteDataStore.get()];
auto messageHandler = adoptNS([[WebsiteDataStoreCustomPathsMessageHandler alloc] init]);
[[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"testHandler"];
auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
NSURLRequest *request = [NSURLRequest requestWithURL:[NSBundle.test_resourcesBundle URLForResource:@"WebsiteDataStoreCustomPaths" withExtension:@"html"]];
[webView loadRequest:request];
EXPECT_STREQ([getNextMessage().body UTF8String], "localstorage written");
EXPECT_STREQ([getNextMessage().body UTF8String], "cookie written");
EXPECT_STREQ([getNextMessage().body UTF8String], "Success opening indexed database");
// Ensure cookies are written to file.
done = false;
[websiteDataStore.get().httpCookieStore _flushCookiesToDiskWithCompletionHandler:^{
done = true;
}];
TestWebKitAPI::Util::run(&done);
done = false;
[websiteDataStore fetchDataRecordsOfTypes:dataTypes completionHandler:^(NSArray<WKWebsiteDataRecord *> * records) {
EXPECT_EQ([records count], 1u);
EXPECT_TRUE([[[records firstObject] dataTypes] isEqualToSet:dataTypes]);
done = true;
}];
TestWebKitAPI::Util::run(&done);
}
auto newWebsiteDataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
done = false;
[newWebsiteDataStore fetchDataRecordsOfTypes:dataTypes completionHandler:^(NSArray<WKWebsiteDataRecord *> * records) {
EXPECT_EQ([records count], 1u);
EXPECT_TRUE([[[records firstObject] dataTypes] isEqualToSet:dataTypes]);
done = true;
}];
TestWebKitAPI::Util::run(&done);
}
TEST(WebKit, DeleteEmptyOriginDirectoryWhenFetchData)
{
NSURL *generalStorageDirectory = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/CustomWebsiteData/Default" stringByExpandingTildeInPath] isDirectory:YES];
NSURL *topOriginDirectory = [generalStorageDirectory URLByAppendingPathComponent:@"YUn_wgR51VLVo9lc5xiivAzZ8TMmojoa0IbW323qibs"];
NSURL *originDirectory = [topOriginDirectory URLByAppendingPathComponent:@"YUn_wgR51VLVo9lc5xiivAzZ8TMmojoa0IbW323qibs"];
NSURL *resourceSalt = [NSBundle.test_resourcesBundle URLForResource:@"general-storage-directory" withExtension:@"salt"];
NSURL *resourceOriginFile = [NSBundle.test_resourcesBundle URLForResource:@"general-storage-directory" withExtension:@"origin"];
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager removeItemAtURL:generalStorageDirectory error:nil];
[fileManager createDirectoryAtURL:generalStorageDirectory withIntermediateDirectories:YES attributes:nil error:nil];
[fileManager copyItemAtURL:resourceSalt toURL:[generalStorageDirectory URLByAppendingPathComponent:@"salt"] error:nil];
[fileManager createDirectoryAtURL:originDirectory withIntermediateDirectories:YES attributes:nil error:nil];
[fileManager copyItemAtURL:resourceOriginFile toURL:[originDirectory URLByAppendingPathComponent:@"origin"] error:nil];
auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
[fileManager removeItemAtURL:websiteDataStoreConfiguration.get()._webStorageDirectory error:nil];
websiteDataStoreConfiguration.get().unifiedOriginStorageLevel = _WKUnifiedOriginStorageLevelBasic;
websiteDataStoreConfiguration.get().generalStorageDirectory = generalStorageDirectory;
auto dataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
done = false;
[dataStore fetchDataRecordsOfTypes:[NSSet setWithObjects:WKWebsiteDataTypeLocalStorage, nil] completionHandler:^(NSArray<WKWebsiteDataRecord *> * records) {
done = true;
}];
TestWebKitAPI::Util::run(&done);
EXPECT_FALSE([fileManager fileExistsAtPath:originDirectory.path]);
EXPECT_FALSE([fileManager fileExistsAtPath:topOriginDirectory.path]);
}
TEST(WebKit, DeleteEmptyOriginDirectoryWithHiddenFile)
{
RetainPtr resourceSalt = [NSBundle.test_resourcesBundle URLForResource:@"general-storage-directory" withExtension:@"salt"];
RetainPtr resourceOriginFile = [NSBundle.test_resourcesBundle URLForResource:@"general-storage-directory" withExtension:@"origin"];
RetainPtr generalStorageDirectory = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/CustomWebsiteData/Default" stringByExpandingTildeInPath] isDirectory:YES];
RetainPtr topOriginDirectory = [generalStorageDirectory URLByAppendingPathComponent:@"YUn_wgR51VLVo9lc5xiivAzZ8TMmojoa0IbW323qibs"];
RetainPtr originDirectory = [topOriginDirectory URLByAppendingPathComponent:@"YUn_wgR51VLVo9lc5xiivAzZ8TMmojoa0IbW323qibs"];
RetainPtr hiddenFile = [originDirectory URLByAppendingPathComponent:@".DS_Store"];
RetainPtr fileManager = [NSFileManager defaultManager];
[fileManager removeItemAtURL:generalStorageDirectory.get() error:nil];
[fileManager createDirectoryAtURL:generalStorageDirectory.get() withIntermediateDirectories:YES attributes:nil error:nil];
[fileManager copyItemAtURL:resourceSalt.get() toURL:[generalStorageDirectory URLByAppendingPathComponent:@"salt"] error:nil];
[fileManager createDirectoryAtURL:originDirectory.get() withIntermediateDirectories:YES attributes:nil error:nil];
[fileManager copyItemAtURL:resourceOriginFile.get() toURL:[originDirectory URLByAppendingPathComponent:@"origin"] error:nil];
[fileManager createFileAtPath:hiddenFile.get().path contents:nil attributes:nil];
RetainPtr websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
websiteDataStoreConfiguration.get().generalStorageDirectory = generalStorageDirectory.get();
RetainPtr websiteDataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
done = false;
[websiteDataStore removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^{
done = true;
}];
TestWebKitAPI::Util::run(&done);
EXPECT_FALSE([fileManager fileExistsAtPath:originDirectory.get().path]);
EXPECT_FALSE([fileManager fileExistsAtPath:topOriginDirectory.get().path]);
}
TEST(WebKit, DeleteEmptyOriginDirectoryWhenOriginIsGone)
{
NSURL *generalStorageDirectory = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/CustomWebsiteData/Default" stringByExpandingTildeInPath] isDirectory:YES];
NSURL *topOriginDirectory = [generalStorageDirectory URLByAppendingPathComponent:@"YUn_wgR51VLVo9lc5xiivAzZ8TMmojoa0IbW323qibs"];
NSURL *originDirectory = [topOriginDirectory URLByAppendingPathComponent:@"YUn_wgR51VLVo9lc5xiivAzZ8TMmojoa0IbW323qibs"];
NSURL *resourceSalt = [NSBundle.test_resourcesBundle URLForResource:@"general-storage-directory" withExtension:@"salt"];
NSURL *resourceOriginFile = [NSBundle.test_resourcesBundle URLForResource:@"general-storage-directory" withExtension:@"origin"];
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager removeItemAtURL:generalStorageDirectory error:nil];
[fileManager createDirectoryAtURL:generalStorageDirectory withIntermediateDirectories:YES attributes:nil error:nil];
[fileManager copyItemAtURL:resourceSalt toURL:[generalStorageDirectory URLByAppendingPathComponent:@"salt"] error:nil];
[fileManager createDirectoryAtURL:originDirectory withIntermediateDirectories:YES attributes:nil error:nil];
// Baked origin file webkit.org.
[fileManager copyItemAtURL:resourceOriginFile toURL:[originDirectory URLByAppendingPathComponent:@"origin"] error:nil];
auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
[fileManager removeItemAtURL:websiteDataStoreConfiguration.get()._webStorageDirectory error:nil];
websiteDataStoreConfiguration.get().unifiedOriginStorageLevel = _WKUnifiedOriginStorageLevelBasic;
websiteDataStoreConfiguration.get().generalStorageDirectory = generalStorageDirectory;
auto dataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
[configuration setWebsiteDataStore:dataStore.get()];
auto handler = adoptNS([[WebsiteDataStoreCustomPathsMessageHandler alloc] init]);
[[configuration userContentController] addScriptMessageHandler:handler.get() name:@"testHandler"];
auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
NSString *htmlString = @"<script> \
localStorage.getItem('testkey'); \
window.webkit.messageHandlers.testHandler.postMessage('done'); \
</script>";
receivedScriptMessage = false;
[webView loadHTMLString:htmlString baseURL:[NSURL URLWithString:@"https://webkit.org"]];
TestWebKitAPI::Util::run(&receivedScriptMessage);
auto pid = [webView _webProcessIdentifier];
EXPECT_NE(pid, 0);
// Ensure webkit.org is gone.
receivedScriptMessage = false;
[webView loadHTMLString:htmlString baseURL:[NSURL URLWithString:@"https://apple.com"]];
TestWebKitAPI::Util::run(&receivedScriptMessage);
kill(pid, SIGKILL);
while ([fileManager fileExistsAtPath:topOriginDirectory.path])
TestWebKitAPI::Util::spinRunLoop();
EXPECT_FALSE([fileManager fileExistsAtPath:topOriginDirectory.path]);
}
#if PLATFORM(IOS_FAMILY)
TEST(WebKit, OriginDirectoryExcludedFromBackup)
{
NSURL *resourceSalt = [NSBundle.test_resourcesBundle URLForResource:@"general-storage-directory" withExtension:@"salt"];
NSURL *generalStorageDirectory = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/CustomWebsiteData/Default" stringByExpandingTildeInPath] isDirectory:YES];
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager removeItemAtURL:generalStorageDirectory error:nil];
[fileManager createDirectoryAtURL:generalStorageDirectory withIntermediateDirectories:YES attributes:nil error:nil];
[fileManager copyItemAtURL:resourceSalt toURL:[generalStorageDirectory URLByAppendingPathComponent:@"salt"] error:nil];
auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
websiteDataStoreConfiguration.get().generalStorageDirectory = generalStorageDirectory;
websiteDataStoreConfiguration.get().unifiedOriginStorageLevel = _WKUnifiedOriginStorageLevelBasic;
auto websiteDataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
NSString *htmlString = @"<script> \
localStorage.setItem('local', 'storage'); \
sessionStorage.setItem('session', 'storage'); \
window.webkit.messageHandlers.testHandler.postMessage('done'); \
</script>";
auto handler = adoptNS([[WebsiteDataStoreCustomPathsMessageHandler alloc] init]);
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
[[configuration userContentController] addScriptMessageHandler:handler.get() name:@"testHandler"];
[configuration setWebsiteDataStore:websiteDataStore.get()];
auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
receivedScriptMessage = false;
[webView loadHTMLString:htmlString baseURL:[NSURL URLWithString:@"https://webkit.org"]];
TestWebKitAPI::Util::run(&receivedScriptMessage);
NSURL *originDirectory = [generalStorageDirectory URLByAppendingPathComponent:@"YUn_wgR51VLVo9lc5xiivAzZ8TMmojoa0IbW323qibs/YUn_wgR51VLVo9lc5xiivAzZ8TMmojoa0IbW323qibs/"];
EXPECT_TRUE([fileManager fileExistsAtPath:originDirectory.path]);
NSNumber *isDirectoryExcluded = nil;
EXPECT_TRUE([originDirectory getResourceValue:&isDirectoryExcluded forKey:NSURLIsExcludedFromBackupKey error:nil]);
EXPECT_TRUE(isDirectoryExcluded.boolValue);
// Set exclusion period to 0 so excluded attribute is updated on next visit.
done = false;
[websiteDataStore _setBackupExclusionPeriodForTesting:0.0 completionHandler:^{
done = true;
}];
TestWebKitAPI::Util::run(&done);
auto webView2 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
receivedScriptMessage = false;
[webView2 loadHTMLString:htmlString baseURL:[NSURL URLWithString:@"https://webkit.org"]];
TestWebKitAPI::Util::run(&receivedScriptMessage);
// Create new url that has updated value.
originDirectory = [generalStorageDirectory URLByAppendingPathComponent:@"YUn_wgR51VLVo9lc5xiivAzZ8TMmojoa0IbW323qibs/YUn_wgR51VLVo9lc5xiivAzZ8TMmojoa0IbW323qibs/"];
EXPECT_TRUE([originDirectory getResourceValue:&isDirectoryExcluded forKey:NSURLIsExcludedFromBackupKey error:nil]);
EXPECT_FALSE(isDirectoryExcluded.boolValue);
}
#endif
TEST(WebKit, RemoveStaleWebSQLData)
{
NSURL *resourceWebSQLDatabaseTrackerFile = [NSBundle.test_resourcesBundle URLForResource:@"websql-database-tracker" withExtension:@"db"];
NSURL *resourceWebSQLDatabaseFile = [NSBundle.test_resourcesBundle URLForResource:@"websql-database" withExtension:@"db"];
NSURL *resourceSalt = [NSBundle.test_resourcesBundle URLForResource:@"general-storage-directory" withExtension:@"salt"];
NSURL *customWebSQLDirectory = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/CustomWebSQL" stringByExpandingTildeInPath] isDirectory:YES];
NSURL *databaseTrackerFile = [customWebSQLDirectory URLByAppendingPathComponent:@"Databases.db"];
NSURL *webkitSQLDirectory = [customWebSQLDirectory URLByAppendingPathComponent:@"http_webkit.org_0"];
NSURL *databaseFile = [webkitSQLDirectory URLByAppendingPathComponent:@"f3013c11-b9f1-4534-a9d6-f3426f777eb3.db"];
NSURL *salt = [customWebSQLDirectory URLByAppendingPathComponent:@"salt"];
/* Directory structure.
- CustomWebSQL
- salt
- Databases.db
- http_webkit.org_0
- f3013c11-b9f1-4534-a9d6-f3426f777eb3.db
*/
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager removeItemAtURL:customWebSQLDirectory error:nil];
[fileManager createDirectoryAtURL:customWebSQLDirectory withIntermediateDirectories:YES attributes:nil error:nil];
[fileManager copyItemAtURL:resourceWebSQLDatabaseTrackerFile toURL:databaseTrackerFile error:nil];
[fileManager createDirectoryAtURL:webkitSQLDirectory withIntermediateDirectories:YES attributes:nil error:nil];
[fileManager copyItemAtURL:resourceWebSQLDatabaseFile toURL:databaseFile error:nil];
[fileManager copyItemAtURL:resourceSalt toURL:salt error:nil];
// Ensure WebSQL files are deleted.
auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
websiteDataStoreConfiguration.get()._webSQLDatabaseDirectory = customWebSQLDirectory;
auto websiteDataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
done = false;
[websiteDataStore fetchDataRecordsOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] completionHandler:^(NSArray*) {
done = true;
}];
TestWebKitAPI::Util::run(&done);
EXPECT_FALSE([fileManager fileExistsAtPath:databaseTrackerFile.path]);
EXPECT_FALSE([fileManager fileExistsAtPath:databaseFile.path]);
EXPECT_FALSE([fileManager fileExistsAtPath:webkitSQLDirectory.path]);
EXPECT_TRUE([fileManager fileExistsAtPath:salt.path]);
// Ensure WebSQL directory is deleted.
[fileManager removeItemAtURL:salt error:nil];
websiteDataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
done = false;
[websiteDataStore fetchDataRecordsOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] completionHandler:^(NSArray*) {
done = true;
}];
TestWebKitAPI::Util::run(&done);
EXPECT_FALSE([fileManager fileExistsAtPath:customWebSQLDirectory.path]);
}
TEST(WKWebsiteDataStore, FetchDiskCacheDataFromDifferentStores)
{
TestWebKitAPI::HTTPServer server([] (TestWebKitAPI::Connection connection) {
connection.receiveHTTPRequest([=] (Vector<char>&&) {
constexpr auto response =
"HTTP/1.1 200 OK\r\n"
"Cache-Control: max-age=1000000\r\n"
"Content-Length: 6\r\n\r\n"
"Hello!"_s;
connection.send(response);
});
});
auto uuid = adoptNS([[NSUUID alloc] initWithUUIDString:@"68753a44-4d6f-1226-9c60-0050e4c00067"]);
auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] initWithIdentifier:uuid.get()]);
auto customWebsiteDataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
// Clear data before test.
auto types = [NSSet setWithObject:WKWebsiteDataTypeDiskCache];
done = false;
[[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:types modifiedSince:[NSDate distantPast] completionHandler:^ {
[[WKWebsiteDataStore defaultDataStore] fetchDataRecordsOfTypes:types completionHandler:^(NSArray<WKWebsiteDataRecord *> * records) {
EXPECT_EQ([records count], 0u);
done = true;
}];
}];
TestWebKitAPI::Util::run(&done);
done = false;
[customWebsiteDataStore.get() removeDataOfTypes:types modifiedSince:[NSDate distantPast] completionHandler:^ {
[customWebsiteDataStore.get() fetchDataRecordsOfTypes:types completionHandler:^(NSArray<WKWebsiteDataRecord *> * records) {
EXPECT_EQ([records count], 0u);
done = true;
}];
}];
TestWebKitAPI::Util::run(&done);
// Create view with default store.
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
[webView synchronouslyLoadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://127.0.0.1:%d/", server.port()]]]];
static bool cached = false;
while (!cached) {
done = false;
[[WKWebsiteDataStore defaultDataStore] fetchDataRecordsOfTypes:types completionHandler:^(NSArray<WKWebsiteDataRecord *> * records) {
if ([records count]) {
EXPECT_EQ([records count], 1u);
EXPECT_WK_STREQ([[records firstObject] displayName], @"127.0.0.1");
cached = true;
}
done = true;
}];
TestWebKitAPI::Util::run(&done);
}
done = false;
[customWebsiteDataStore.get() fetchDataRecordsOfTypes:types completionHandler:^(NSArray<WKWebsiteDataRecord *> * records) {
EXPECT_EQ([records count], 0u);
done = true;
}];
TestWebKitAPI::Util::run(&done);
done = false;
[[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:types modifiedSince:[NSDate distantPast] completionHandler:^() {
done = true;
}];
TestWebKitAPI::Util::run(&done);
// Create view with custom store.
[configuration setWebsiteDataStore:customWebsiteDataStore.get()];
auto webView2 = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
[webView2 synchronouslyLoadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://127.0.0.1:%d/", server.port()]]]];
cached = false;
while (!cached) {
done = false;
[customWebsiteDataStore.get() fetchDataRecordsOfTypes:types completionHandler:^(NSArray<WKWebsiteDataRecord *> * records) {
if ([records count]) {
EXPECT_EQ([records count], 1u);
EXPECT_WK_STREQ([[records firstObject] displayName], @"127.0.0.1");
cached = true;
}
done = true;
}];
TestWebKitAPI::Util::run(&done);
}
done = false;
[[WKWebsiteDataStore defaultDataStore] fetchDataRecordsOfTypes:types completionHandler:^(NSArray<WKWebsiteDataRecord *> * records) {
EXPECT_EQ([records count], 0u);
done = true;
}];
TestWebKitAPI::Util::run(&done);
}
TEST(WKWebsiteDataStoreConfiguration, InitWithIdentifier)
{
NSString *htmlString = @"<script> \
document.cookie = \"testkey=value; expires=Mon, 01 Jan 2035 00:00:00 GMT\"; \
</script>";
NSString *uuidString = @"68753a44-4d6f-1226-9c60-0050e4c00067";
auto uuid = adoptNS([[NSUUID alloc] initWithUUIDString:uuidString]);
auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] initWithIdentifier:uuid.get()]);
NSURL *cookieStorageFile = websiteDataStoreConfiguration.get()._cookieStorageFile;
EXPECT_TRUE([cookieStorageFile.path containsString:uuidString]);
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager removeItemAtURL:cookieStorageFile error:nil];
EXPECT_FALSE([fileManager fileExistsAtPath:cookieStorageFile.path]);
auto websiteDataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
[configuration setWebsiteDataStore:websiteDataStore.get()];
auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
[webView loadHTMLString:htmlString baseURL:[NSURL URLWithString:@"https://webkit.org/"]];
while (![fileManager fileExistsAtPath:cookieStorageFile.path])
TestWebKitAPI::Util::spinRunLoop();
auto types = [NSSet setWithObject:WKWebsiteDataTypeCookies];
[websiteDataStore.get() fetchDataRecordsOfTypes:types completionHandler:^(NSArray<WKWebsiteDataRecord *> * records) {
EXPECT_EQ([records count], 1u);
EXPECT_WK_STREQ([[records firstObject] displayName], @"webkit.org");
}];
}
TEST(WKWebsiteDataStoreConfiguration, InitWithInvalidIdentifier)
{
bool hasException = false;
@try {
auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] initWithIdentifier:[[NSUUID alloc] initWithUUIDString:@"1234"]]);
} @catch (NSException *exception) {
EXPECT_WK_STREQ(NSInvalidArgumentException, exception.name);
hasException = true;
}
EXPECT_TRUE(hasException);
}
TEST(WKWebsiteDataStoreConfiguration, InitWithEmptyIdentifier)
{
bool hasException = false;
@try {
auto data = [NSMutableData dataWithLength:16];
unsigned char* dataBytes = (unsigned char*) [data mutableBytes];
auto emptyUUID = [[NSUUID alloc] initWithUUIDBytes:dataBytes];
auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] initWithIdentifier:emptyUUID]);
} @catch (NSException *exception) {
EXPECT_WK_STREQ(NSInvalidArgumentException, exception.name);
hasException = true;
}
EXPECT_TRUE(hasException);
}
TEST(WKWebsiteDataStoreConfiguration, SetPathOnConfigurationWithIdentifier)
{
bool hasException = false;
@try {
auto uuid = adoptNS([[NSUUID alloc] initWithUUIDString:@"68753A44-4D6F-1226-9C60-0050E4C00067"]);
auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] initWithIdentifier:uuid.get()]);
NSURL *cookieStorageFile = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/CustomWebsiteData/CookieStorage/Cookies.binarycookies" stringByExpandingTildeInPath] isDirectory:NO];
websiteDataStoreConfiguration.get()._cookieStorageFile = cookieStorageFile;
} @catch (NSException *exception) {
EXPECT_WK_STREQ(NSGenericException, exception.name);
hasException = true;
}
EXPECT_TRUE(hasException);
}
TEST(WKWebsiteDataStore, MigrateCacheStorageDataToGeneralStorageDirectory)
{
RetainPtr<NSURL> cacheStorageOriginDirectory = nullptr;
NSURL *cacheStorageDirectory = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/CustomWebsiteData/CacheStorage" stringByExpandingTildeInPath] isDirectory:YES];
NSURL *generalStorageDirectory = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/CustomWebsiteData/Origins" stringByExpandingTildeInPath] isDirectory:YES];
NSURL *resourceSalt = [NSBundle.test_resourcesBundle URLForResource:@"general-storage-directory" withExtension:@"salt"];
NSURL *newCacheStorageOriginDirectory = [generalStorageDirectory URLByAppendingPathComponent:@"YUn_wgR51VLVo9lc5xiivAzZ8TMmojoa0IbW323qibs/YUn_wgR51VLVo9lc5xiivAzZ8TMmojoa0IbW323qibs/CacheStorage/"];
NSURL *newCacheStorageCachesListFile = [newCacheStorageOriginDirectory URLByAppendingPathComponent:@"cacheslist"];
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager removeItemAtURL:cacheStorageDirectory error:nil];
[fileManager removeItemAtURL:generalStorageDirectory error:nil];
[fileManager createDirectoryAtURL:generalStorageDirectory withIntermediateDirectories:YES attributes:nil error:nil];
[fileManager copyItemAtURL:resourceSalt toURL:[generalStorageDirectory URLByAppendingPathComponent:@"salt"] error:nil];
NSString *htmlString = @"<script> \
var put = false; \
window.caches.keys().then((keys) => { \
if (!keys.length) put = true; \
return window.caches.open('testCache'); \
}).then((cache) => { \
if (put) \
return cache.put('https://webkit.org/test', new Response('this is response')); \
return cache.match(new Request('https://webkit.org/test')); \
}).then((result) => { \
if (put) \
window.webkit.messageHandlers.testHandler.postMessage('put succeeded'); \
else if (!result) \
window.webkit.messageHandlers.testHandler.postMessage('match failed'); \
else { \
result.text().then((resultText) => { \
window.webkit.messageHandlers.testHandler.postMessage('matched: ' + resultText); \
}); \
} \
}).catch(() => { \
window.webkit.messageHandlers.testHandler.postMessage('failed'); \
}); \
</script>";
auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
websiteDataStoreConfiguration.get()._cacheStorageDirectory = cacheStorageDirectory;
websiteDataStoreConfiguration.get().generalStorageDirectory = generalStorageDirectory;
websiteDataStoreConfiguration.get().unifiedOriginStorageLevel = _WKUnifiedOriginStorageLevelNone;
auto messageHandler = adoptNS([[WebsiteDataStoreCustomPathsMessageHandler alloc] init]);
@autoreleasepool {
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
[configuration setWebsiteDataStore:adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]).get()];
[[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"testHandler"];
auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
receivedScriptMessage = false;
[webView loadHTMLString:htmlString baseURL:[NSURL URLWithString:@"https://webkit.org/"]];
TestWebKitAPI::Util::run(&receivedScriptMessage);
EXPECT_WK_STREQ("put succeeded", getNextMessage().body);
NSArray *children = [fileManager contentsOfDirectoryAtPath:cacheStorageDirectory.path error:nullptr];
EXPECT_EQ(2U, [children count]);
for (NSString *child in children) {
if ([child isEqualToString:@"salt"])
continue;
cacheStorageOriginDirectory = [cacheStorageDirectory URLByAppendingPathComponent:child];
}
}
// Create a new WebsiteDataStore that performs migration.
websiteDataStoreConfiguration.get().unifiedOriginStorageLevel = _WKUnifiedOriginStorageLevelStandard;
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
[configuration setWebsiteDataStore:adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]).get()];
[[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"testHandler"];
auto thirdWebView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
receivedScriptMessage = false;
[thirdWebView loadHTMLString:htmlString baseURL:[NSURL URLWithString:@"https://webkit.org/"]];
TestWebKitAPI::Util::run(&receivedScriptMessage);
EXPECT_WK_STREQ("matched: this is response", getNextMessage().body);
EXPECT_FALSE([fileManager fileExistsAtPath:cacheStorageOriginDirectory.get().path]);
EXPECT_TRUE([fileManager fileExistsAtPath:newCacheStorageOriginDirectory.path]);
EXPECT_TRUE([fileManager fileExistsAtPath:newCacheStorageCachesListFile.path]);
}
static constexpr auto mainBytes = R"SWRESOURCE(
<script>
function log(message)
{
window.webkit.messageHandlers.testHandler.postMessage(message);
}
function install()
{
navigator.serviceWorker.register('/migratetest/sw.js').then((registration) => {
const worker = registration.installing ? registration.installing : registration.active;
worker.postMessage('Hello');
}).catch((error) => {
log('register() failed with: ' + error);
});
}
navigator.serviceWorker.addEventListener('message', function(event) {
log('Message from ServiceWorker: ' + event.data);
});
navigator.serviceWorker.getRegistration('/migratetest/').then((registration) => {
if (!registration)
install();
else
log('Found registration');
}).catch((error) => {
log('getRegistration() failed with: ' + error);
});
</script>
)SWRESOURCE"_s;
static constexpr auto scriptBytes = R"SWRESOURCE(
self.addEventListener('message', (event) => {
event.source.postMessage(event.data);
});
)SWRESOURCE"_s;
TEST(WKWebsiteDataStore, MigrateServiceWorkerRegistrationToGeneralStorageDirectory)
{
NSString *serviceWorkerDatabaseName = @"ServiceWorkerRegistrations-8.sqlite3";
NSURL *serviceWorkerDirectory = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/CustomWebsiteData/ServiceWorkers" stringByExpandingTildeInPath] isDirectory:YES];
NSURL *serviceWorkerDatabaseFile = [serviceWorkerDirectory URLByAppendingPathComponent:serviceWorkerDatabaseName];
NSURL *generalStorageDirectory = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/CustomWebsiteData/Origins" stringByExpandingTildeInPath] isDirectory:YES];
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager removeItemAtURL:serviceWorkerDirectory error:nil];
[fileManager removeItemAtURL:generalStorageDirectory error:nil];
TestWebKitAPI::HTTPServer server({
{ "/"_s, { mainBytes } },
{ "/migratetest/sw.js"_s, { { { "Content-Type"_s, "application/javascript"_s } }, scriptBytes } },
});
[WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
websiteDataStoreConfiguration.get()._serviceWorkerRegistrationDirectory = serviceWorkerDirectory;
websiteDataStoreConfiguration.get().generalStorageDirectory = generalStorageDirectory;
websiteDataStoreConfiguration.get().unifiedOriginStorageLevel = _WKUnifiedOriginStorageLevelNone;
auto messageHandler = adoptNS([[WebsiteDataStoreCustomPathsMessageHandler alloc] init]);
pid_t networkProcessIdentifier;
@autoreleasepool {
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
[configuration setWebsiteDataStore:adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]).get()];
[[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"testHandler"];
auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
[webView loadRequest:server.request()];
EXPECT_WK_STREQ("Message from ServiceWorker: Hello", getNextMessage().body);
done = false;
[configuration.get().websiteDataStore _storeServiceWorkerRegistrations: ^{
done = true;
}];
TestWebKitAPI::Util::run(&done);
EXPECT_TRUE([fileManager fileExistsAtPath:serviceWorkerDatabaseFile.path]);
networkProcessIdentifier = [configuration.get().websiteDataStore _networkProcessIdentifier];
EXPECT_NE(networkProcessIdentifier, 0);
}
// Wait until network process exits so we are sure data is read from file.
while (!kill(networkProcessIdentifier, 0))
TestWebKitAPI::Util::spinRunLoop();
// Create a new WebsiteDataStore that performs migration.
websiteDataStoreConfiguration.get().unifiedOriginStorageLevel = _WKUnifiedOriginStorageLevelStandard;
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
[configuration setWebsiteDataStore:adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]).get()];
[[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"testHandler"];
auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
[webView loadRequest:server.request()];
EXPECT_WK_STREQ("Found registration", getNextMessage().body);
__block RetainPtr<NSString> originDirectoryString = nil;
auto url = [server.request() URL];
done = false;
[configuration.get().websiteDataStore _originDirectoryForTesting:url topOrigin:url type:WKWebsiteDataTypeServiceWorkerRegistrations completionHandler:^(NSString *result) {
originDirectoryString = result;
done = true;
}];
TestWebKitAPI::Util::run(&done);
NSString *newServiceWorkerDatabaseFilePath = [originDirectoryString stringByAppendingPathComponent:serviceWorkerDatabaseName];
EXPECT_TRUE([fileManager fileExistsAtPath:newServiceWorkerDatabaseFilePath]);
EXPECT_FALSE([fileManager fileExistsAtPath:serviceWorkerDatabaseFile.path]);
}
static RetainPtr<WKWebsiteDataStore> createCustomWebsiteDataStoreForServiceWorker(TestWebKitAPI::HTTPServer* server)
{
[WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
NSURL *generalStorageDirectory = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/CustomWebsiteData/Origins" stringByExpandingTildeInPath] isDirectory:YES];
websiteDataStoreConfiguration.get().generalStorageDirectory = generalStorageDirectory;
auto websiteDataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
done = false;
[websiteDataStore removeDataOfTypes:[WKWebsiteDataStore _allWebsiteDataTypesIncludingPrivate] modifiedSince:[NSDate distantPast] completionHandler:^() {
done = true;
}];
TestWebKitAPI::Util::run(&done);
// Ensure web view is closed, so ServiceWorker directory is not in use and can be removed.
@autoreleasepool {
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
[configuration setWebsiteDataStore:websiteDataStore.get()];
auto messageHandler = adoptNS([[WebsiteDataStoreCustomPathsMessageHandler alloc] init]);
[[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"testHandler"];
auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
[webView loadRequest:server->request()];
EXPECT_WK_STREQ("Message from ServiceWorker: Hello", getNextMessage().body);
// Ensure data is stored to disk.
done = false;
[websiteDataStore _storeServiceWorkerRegistrations: ^{
done = true;
}];
TestWebKitAPI::Util::run(&done);
}
return websiteDataStore;
}
TEST(WKWebsiteDataStore, RemoveServiceWorkerData)
{
TestWebKitAPI::HTTPServer server({
{ "/"_s, { mainBytes } },
{ "/migratetest/sw.js"_s, { { { "Content-Type"_s, "application/javascript"_s } }, scriptBytes } },
});
RetainPtr websiteDataStore = createCustomWebsiteDataStoreForServiceWorker(&server);
__block RetainPtr<NSString> serviceWorkerDirectoryString;
RetainPtr url = [server.request() URL];
done = false;
[websiteDataStore.get() _originDirectoryForTesting:url.get() topOrigin:url.get() type:WKWebsiteDataTypeServiceWorkerRegistrations completionHandler:^(NSString *result) {
serviceWorkerDirectoryString = result;
done = true;
}];
TestWebKitAPI::Util::run(&done);
EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:serviceWorkerDirectoryString.get()]);
RetainPtr dataTypes = [NSSet setWithObjects:WKWebsiteDataTypeServiceWorkerRegistrations, nil];
done = false;
[websiteDataStore removeDataOfTypes:dataTypes.get() modifiedSince:[NSDate distantPast] completionHandler:^() {
done = true;
}];
TestWebKitAPI::Util::run(&done);
EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:serviceWorkerDirectoryString.get()]);
done = false;
[websiteDataStore fetchDataRecordsOfTypes:dataTypes.get() completionHandler:^(NSArray<WKWebsiteDataRecord *> * records) {
EXPECT_EQ([records count], 0u);
done = true;
}];
TestWebKitAPI::Util::run(&done);
}
TEST(WKWebsiteDataStore, RemoveServiceWorkerDataByOrigin)
{
TestWebKitAPI::HTTPServer server({
{ "/"_s, { mainBytes } },
{ "/migratetest/sw.js"_s, { { { "Content-Type"_s, "application/javascript"_s } }, scriptBytes } },
});
auto websiteDataStore = createCustomWebsiteDataStoreForServiceWorker(&server);
__block RetainPtr<NSString> serviceWorkerDirectoryString;
RetainPtr url = [server.request() URL];
done = false;
[websiteDataStore.get() _originDirectoryForTesting:url.get() topOrigin:url.get() type:WKWebsiteDataTypeServiceWorkerRegistrations completionHandler:^(NSString *result) {
serviceWorkerDirectoryString = result;
done = true;
}];
TestWebKitAPI::Util::run(&done);
// Fetch origin record.
auto dataTypes = [NSSet setWithObjects:WKWebsiteDataTypeServiceWorkerRegistrations, nil];
__block RetainPtr<NSArray<WKWebsiteDataRecord *>> records;
done = false;
[websiteDataStore fetchDataRecordsOfTypes:dataTypes completionHandler:^(NSArray<WKWebsiteDataRecord *> * dataRecords) {
records = dataRecords;
done = true;
}];
TestWebKitAPI::Util::run(&done);
EXPECT_EQ([records count], 1u);
EXPECT_WK_STREQ([[records firstObject] displayName], @"127.0.0.1");
// Delete data by origin.
done = false;
[websiteDataStore removeDataOfTypes:dataTypes forDataRecords:records.get() completionHandler:^() {
done = true;
}];
TestWebKitAPI::Util::run(&done);
// Kill network process to ensure data is read from disk.
pid_t networkProcessIdentifier = [websiteDataStore _networkProcessIdentifier];
EXPECT_NE(networkProcessIdentifier, 0);
kill(networkProcessIdentifier, SIGKILL);
while (!kill(networkProcessIdentifier, 0))
TestWebKitAPI::Util::spinRunLoop();
// Verify record is deleted.
done = false;
[websiteDataStore fetchDataRecordsOfTypes:dataTypes completionHandler:^(NSArray<WKWebsiteDataRecord *> * dataRecords) {
records = dataRecords;
done = true;
}];
TestWebKitAPI::Util::run(&done);
EXPECT_EQ([records count], 0u);
// Verify directory is deleted.
EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:serviceWorkerDirectoryString.get()]);
}
TEST(WKWebsiteDataStore, DeleteEmptyServiceWorkerDirectory)
{
RetainPtr resourceSalt = [NSBundle.test_resourcesBundle URLForResource:@"general-storage-directory" withExtension:@"salt"];
RetainPtr resourceOriginFile = [NSBundle.test_resourcesBundle URLForResource:@"general-storage-directory" withExtension:@"origin"];
RetainPtr resourceServiceWorkerDatabase = [NSBundle.test_resourcesBundle URLForResource:@"empty-service-worker-registrations" withExtension:@"sqlite3"];
RetainPtr generalStorageDirectory = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/CustomWebsiteData/Default" stringByExpandingTildeInPath] isDirectory:YES];
RetainPtr topOriginDirectory = [generalStorageDirectory.get() URLByAppendingPathComponent:@"YUn_wgR51VLVo9lc5xiivAzZ8TMmojoa0IbW323qibs"];
RetainPtr originDirectory = [topOriginDirectory.get() URLByAppendingPathComponent:@"YUn_wgR51VLVo9lc5xiivAzZ8TMmojoa0IbW323qibs"];
RetainPtr serviceWorkerDirectory = [originDirectory.get() URLByAppendingPathComponent:@"ServiceWorkers"];
RetainPtr serviceWorkerDatabase = [serviceWorkerDirectory.get() URLByAppendingPathComponent:@"ServiceWorkerRegistrations-8.sqlite3"];
RetainPtr serviceWorkerScriptsDirectory = [serviceWorkerDirectory.get() URLByAppendingPathComponent:@"Scripts"];
RetainPtr fileManager = [NSFileManager defaultManager];
[fileManager removeItemAtURL:generalStorageDirectory.get() error:nil];
[fileManager createDirectoryAtURL:generalStorageDirectory.get() withIntermediateDirectories:YES attributes:nil error:nil];
[fileManager copyItemAtURL:resourceSalt.get() toURL:[generalStorageDirectory.get() URLByAppendingPathComponent:@"salt"] error:nil];
[fileManager createDirectoryAtURL:originDirectory.get() withIntermediateDirectories:YES attributes:nil error:nil];
[fileManager copyItemAtURL:resourceOriginFile.get() toURL:[originDirectory URLByAppendingPathComponent:@"origin"] error:nil];
[fileManager createDirectoryAtURL:serviceWorkerDirectory.get() withIntermediateDirectories:YES attributes:nil error:nil];
[fileManager copyItemAtURL:resourceServiceWorkerDatabase.get() toURL:serviceWorkerDatabase.get() error:nil];
[fileManager createDirectoryAtURL:serviceWorkerScriptsDirectory.get() withIntermediateDirectories:YES attributes:nil error:nil];
RetainPtr websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
websiteDataStoreConfiguration.get().generalStorageDirectory = generalStorageDirectory.get();
RetainPtr websiteDataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
// Verify record does not exist with empty database.
__block RetainPtr<NSArray<WKWebsiteDataRecord *>> records;
RetainPtr dataTypes = [NSSet setWithObjects:WKWebsiteDataTypeServiceWorkerRegistrations, nil];
done = false;
[websiteDataStore fetchDataRecordsOfTypes:dataTypes.get() completionHandler:^(NSArray<WKWebsiteDataRecord *> * dataRecords) {
records = dataRecords;
done = true;
}];
TestWebKitAPI::Util::run(&done);
EXPECT_EQ([records count], 0u);
// Verify directory is deleted.
EXPECT_FALSE([fileManager fileExistsAtPath:[serviceWorkerDirectory path]]);
}
TEST(WKWebsiteDataStore, FetchAndDeleteMediaKeysData)
{
NSURL *customMediaKeysStorageDirectory = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/CustomWebsiteData/MediaKeys" stringByExpandingTildeInPath] isDirectory:YES];
WebCore::SecurityOriginData origin("https"_s, "webkit.org"_s, 443);
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *customWebKitDirectory = [customMediaKeysStorageDirectory URLByAppendingPathComponent:origin.databaseIdentifier().createNSString().get()];
[fileManager createDirectoryAtURL:customWebKitDirectory withIntermediateDirectories:YES attributes:nil error:nil];
NSURL *customMediaKeysStorageFile = [customWebKitDirectory URLByAppendingPathComponent:@"SecureStop.plist"];
[fileManager createFileAtPath:customMediaKeysStorageFile.path contents:nil attributes:nil];
FileSystem::updateFileModificationTime(customMediaKeysStorageFile.path);
NSURL *customVersionDirectory = [customMediaKeysStorageDirectory URLByAppendingPathComponent:@"v1"];
[fileManager createDirectoryAtURL:customVersionDirectory withIntermediateDirectories:YES attributes:nil error:nil];
NSURL *resourceSalt = [NSBundle.test_resourcesBundle URLForResource:@"general-storage-directory" withExtension:@"salt"];
[fileManager copyItemAtURL:resourceSalt toURL:[customVersionDirectory URLByAppendingPathComponent:@"salt"] error:nil];
NSURL *newWebKitDirectory = [customVersionDirectory URLByAppendingPathComponent:@"XLb5EY_51d2Xcs65bSUthGKAVscdhhcHXCR6DndbBnc"];
auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
websiteDataStoreConfiguration.get().mediaKeysStorageDirectory = customMediaKeysStorageDirectory;
auto websiteDataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
auto mediaKeysType = adoptNS([[NSSet alloc] initWithObjects:WKWebsiteDataTypeMediaKeys, nil]);
done = false;
__block RetainPtr<NSArray<WKWebsiteDataRecord *>> dataRecords;
[websiteDataStore fetchDataRecordsOfTypes:mediaKeysType.get() completionHandler:^(NSArray<WKWebsiteDataRecord *> * records) {
dataRecords = records;
done = true;
}];
TestWebKitAPI::Util::run(&done);
EXPECT_EQ([dataRecords count], 1u);
EXPECT_TRUE([[[dataRecords firstObject] displayName] isEqual:@"webkit.org"]);
EXPECT_TRUE([fileManager fileExistsAtPath:newWebKitDirectory.path]);
EXPECT_FALSE([fileManager fileExistsAtPath:customMediaKeysStorageFile.path]);
done = false;
[websiteDataStore removeDataOfTypes:mediaKeysType.get() forDataRecords:dataRecords.get() completionHandler:^() {
done = true;
}];
TestWebKitAPI::Util::run(&done);
EXPECT_FALSE([fileManager fileExistsAtPath:newWebKitDirectory.path]);
}