blob: 13a3e98ebabbdfeeaa24dd886138cde731bf6b84 [file] [log] [blame]
/*
* 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 "HTTPServer.h"
#import "PlatformUtilities.h"
#import "TCPServer.h"
#import "Test.h"
#import "TestNavigationDelegate.h"
#import "TestWKWebView.h"
#import <JavaScriptCore/JSCConfig.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/WebKit.h>
#import <WebKit/_WKProcessPoolConfiguration.h>
#import <WebKit/_WKUserStyleSheet.h>
#import <WebKit/_WKWebsiteDataStoreConfiguration.h>
#import <wtf/Deque.h>
#import <wtf/RetainPtr.h>
#import <wtf/text/WTFString.h>
static bool receivedScriptMessage;
static Deque<RetainPtr<WKScriptMessage>> scriptMessages;
@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
static WKScriptMessage *getNextMessage()
{
if (scriptMessages.isEmpty()) {
receivedScriptMessage = false;
TestWebKitAPI::Util::run(&receivedScriptMessage);
}
return [[scriptMessages.takeFirst() retain] autorelease];
}
enum class ShouldEnableProcessPrewarming { No, Yes };
static void runWebsiteDataStoreCustomPaths(ShouldEnableProcessPrewarming shouldEnableProcessPrewarming)
{
auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
processPoolConfiguration.get().prewarmsProcessesAutomatically = shouldEnableProcessPrewarming == ShouldEnableProcessPrewarming::Yes ? YES : NO;
auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
RetainPtr<WebsiteDataStoreCustomPathsMessageHandler> handler = adoptNS([[WebsiteDataStoreCustomPathsMessageHandler alloc] init]);
RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
[[configuration userContentController] addScriptMessageHandler:handler.get() name:@"testHandler"];
NSURL *sqlPath = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/CustomWebsiteData/WebSQL/" stringByExpandingTildeInPath] isDirectory:YES];
NSURL *idbPath = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/CustomWebsiteData/IndexedDB/" stringByExpandingTildeInPath] isDirectory:YES];
NSURL *localStoragePath = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/CustomWebsiteData/LocalStorage/" stringByExpandingTildeInPath] isDirectory:YES];
NSURL *cookieStorageFile = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/CustomWebsiteData/CookieStorage/Cookie.File" stringByExpandingTildeInPath] isDirectory:NO];
NSURL *resourceLoadStatisticsPath = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/CustomWebsiteData/ResourceLoadStatistics/" stringByExpandingTildeInPath] isDirectory:YES];
NSURL *defaultSQLPath = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/WebsiteData/WebSQL/" stringByExpandingTildeInPath] isDirectory:YES];
NSURL *defaultIDBPath = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/WebsiteData/IndexedDB/" stringByExpandingTildeInPath] isDirectory:YES];
NSURL *defaultLocalStoragePath = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/WebsiteData/LocalStorage/" stringByExpandingTildeInPath] isDirectory:YES];
NSURL *defaultResourceLoadStatisticsPath = [NSURL fileURLWithPath:[@"~/Library/WebKit/com.apple.WebKit.TestWebKitAPI/WebsiteData/ResourceLoadStatistics/" stringByExpandingTildeInPath] isDirectory:YES];
[[NSFileManager defaultManager] removeItemAtURL:sqlPath error:nil];
[[NSFileManager defaultManager] removeItemAtURL:idbPath error:nil];
[[NSFileManager defaultManager] removeItemAtURL:localStoragePath error:nil];
[[NSFileManager defaultManager] removeItemAtURL:cookieStorageFile error:nil];
[[NSFileManager defaultManager] removeItemAtURL:resourceLoadStatisticsPath error:nil];
[[NSFileManager defaultManager] removeItemAtURL:defaultSQLPath error:nil];
[[NSFileManager defaultManager] removeItemAtURL:defaultIDBPath error:nil];
[[NSFileManager defaultManager] removeItemAtURL:defaultLocalStoragePath error:nil];
[[NSFileManager defaultManager] removeItemAtURL:defaultResourceLoadStatisticsPath error:nil];
EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:sqlPath.path]);
EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:idbPath.path]);
EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:localStoragePath.path]);
EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:cookieStorageFile.path]);
EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:resourceLoadStatisticsPath.path]);
EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:defaultSQLPath.path]);
EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:defaultIDBPath.path]);
EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:defaultLocalStoragePath.path]);
EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:defaultResourceLoadStatisticsPath.path]);
RetainPtr<_WKWebsiteDataStoreConfiguration> websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
websiteDataStoreConfiguration.get()._webSQLDatabaseDirectory = sqlPath;
websiteDataStoreConfiguration.get()._indexedDBDatabaseDirectory = idbPath;
websiteDataStoreConfiguration.get()._webStorageDirectory = localStoragePath;
websiteDataStoreConfiguration.get()._cookieStorageFile = cookieStorageFile;
websiteDataStoreConfiguration.get()._resourceLoadStatisticsDirectory = resourceLoadStatisticsPath;
configuration.get().websiteDataStore = [[[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()] autorelease];
configuration.get().processPool = processPool.get();
RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
[webView setNavigationDelegate:handler.get()];
auto preferences = (__bridge WKPreferencesRef)[[webView configuration] preferences];
WKPreferencesSetWebSQLDisabled(preferences, false);
NSURLRequest *request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"WebsiteDataStoreCustomPaths" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
[webView loadRequest:request];
EXPECT_FALSE([WKWebsiteDataStore _defaultDataStoreExists]);
// We expect 4 messages, 1 each for WebSQL, IndexedDB, cookies, and localStorage.
EXPECT_STREQ([getNextMessage().body UTF8String], "localstorage written");
EXPECT_STREQ([getNextMessage().body UTF8String], "cookie written");
EXPECT_STREQ([getNextMessage().body UTF8String], "Exception: QuotaExceededError: The quota has been exceeded.");
EXPECT_STREQ([getNextMessage().body UTF8String], "Success opening indexed database");
[[[webView configuration] processPool] _syncNetworkProcessCookies];
// 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:sqlPath.path]);
EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:localStoragePath.path]);
#if PLATFORM(IOS_FAMILY)
int retryCount = 30;
while (retryCount--) {
if ([[NSFileManager defaultManager] fileExistsAtPath:cookieStorageFile.path])
break;
TestWebKitAPI::Util::sleep(0.1);
}
EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:cookieStorageFile.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.
auto data = adoptNS([[NSData alloc] initWithContentsOfURL:cookieStorageFile]);
char bytes[] = "testkey";
auto 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 SQL and LocalStorage paths are being used for something, but they shouldn't be. (theses should be false, not true)
EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:defaultSQLPath.path]);
EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:defaultLocalStoragePath.path]);
#endif
EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:idbPath.path]);
EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:defaultIDBPath.path]);
RetainPtr<NSURL> fileIDBPath = [[idbPath URLByAppendingPathComponent:@"v1"] URLByAppendingPathComponent:@"file__0"];
EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:fileIDBPath.get().path]);
RetainPtr<WKWebsiteDataStore> dataStore = [[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()];
RetainPtr<NSSet> types = adoptNS([[NSSet alloc] initWithObjects:WKWebsiteDataTypeIndexedDBDatabases, nil]);
// Subframe of different origins may also create IndexedDB files.
RetainPtr<NSURL> url1 = [[NSBundle mainBundle] URLForResource:@"IndexedDB" withExtension:@"sqlite3" subdirectory:@"TestWebKitAPI.resources"];
RetainPtr<NSURL> url2 = [[NSBundle mainBundle] URLForResource:@"IndexedDB" withExtension:@"sqlite3-shm" subdirectory:@"TestWebKitAPI.resources"];
RetainPtr<NSURL> url3 = [[NSBundle mainBundle] URLForResource:@"IndexedDB" withExtension:@"sqlite3-wal" subdirectory:@"TestWebKitAPI.resources"];
RetainPtr<NSURL> frameIDBPath = [[fileIDBPath URLByAppendingPathComponent:@"https_apple.com_0"] URLByAppendingPathComponent:@"WebsiteDataStoreCustomPaths"];
[[NSFileManager defaultManager] createDirectoryAtURL:frameIDBPath.get() withIntermediateDirectories:YES attributes:nil error:nil];
[[NSFileManager defaultManager] copyItemAtURL:url1.get() toURL:[frameIDBPath.get() URLByAppendingPathComponent:@"IndexedDB.sqlite3"] error:nil];
[[NSFileManager defaultManager] copyItemAtURL:url2.get() toURL:[frameIDBPath.get() URLByAppendingPathComponent:@"IndexedDB.sqlite3-shm"] error:nil];
[[NSFileManager defaultManager] copyItemAtURL:url3.get() toURL:[frameIDBPath.get() URLByAppendingPathComponent:@"IndexedDB.sqlite3-wal"] error:nil];
RetainPtr<NSURL> frameIDBPath2 = [[fileIDBPath URLByAppendingPathComponent:@"https_webkit.org_0"] URLByAppendingPathComponent:@"WebsiteDataStoreCustomPaths"];
[[NSFileManager defaultManager] createDirectoryAtURL:frameIDBPath2.get() withIntermediateDirectories:YES attributes:nil error:nil];
[[NSFileManager defaultManager] copyItemAtURL:url1.get() toURL:[frameIDBPath2.get() URLByAppendingPathComponent:@"IndexedDB.sqlite3"] error:nil];
[[NSFileManager defaultManager] copyItemAtURL:url2.get() toURL:[frameIDBPath2.get() URLByAppendingPathComponent:@"IndexedDB.sqlite3-shm"] error:nil];
[[NSFileManager defaultManager] copyItemAtURL:url3.get() toURL:[frameIDBPath2.get() 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:[NSArray arrayWithObject:record] completionHandler:^() {
receivedScriptMessage = true;
EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:frameIDBPath.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:fileIDBPath.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.
[processPool _terminateNetworkProcess];
auto 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);
[processPool _terminateNetworkProcess];
newCustomDataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
[newCustomDataStore removeDataOfTypes:[NSSet setWithObjects:WKWebsiteDataTypeCookies, nil] modifiedSince:[NSDate distantPast] completionHandler:^ {
receivedScriptMessage = true;
}];
receivedScriptMessage = false;
TestWebKitAPI::Util::run(&receivedScriptMessage);
// This time, reuse the same network process but still do a new websitedatastore, to make sure even an existing network process
// gets the new datastore.
newCustomDataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
[newCustomDataStore fetchDataRecordsOfTypes:[NSSet setWithObjects:WKWebsiteDataTypeCookies, nil] completionHandler:^(NSArray<WKWebsiteDataRecord *> * records) {
EXPECT_EQ([records count], (unsigned long)0);
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, CustomDataStorePathsVersusCompletionHandlers)
{
// Copy the baked database files to the database directory
NSURL *url1 = [[NSBundle mainBundle] URLForResource:@"SimpleServiceWorkerRegistrations-4" withExtension:@"sqlite3" subdirectory:@"TestWebKitAPI.resources"];
NSURL *swPath = [NSURL fileURLWithPath:[@"~/Library/Caches/com.apple.WebKit.TestWebKitAPI/WebKit/ServiceWorkers/" stringByExpandingTildeInPath]];
[[NSFileManager defaultManager] removeItemAtURL:swPath error:nil];
EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:swPath.path]);
[[NSFileManager defaultManager] createDirectoryAtURL:swPath withIntermediateDirectories:YES attributes:nil error:nil];
[[NSFileManager defaultManager] copyItemAtURL:url1 toURL:[swPath URLByAppendingPathComponent:@"ServiceWorkerRegistrations-4.sqlite3"] error:nil];
auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
websiteDataStoreConfiguration.get()._serviceWorkerRegistrationDirectory = swPath;
auto dataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
// Fetch SW records
auto websiteDataTypes = adoptNS([[NSSet alloc] initWithArray:@[WKWebsiteDataTypeServiceWorkerRegistrations]]);
static bool readyToContinue;
[dataStore fetchDataRecordsOfTypes:websiteDataTypes.get() completionHandler:^(NSArray<WKWebsiteDataRecord *> *dataRecords) {
EXPECT_EQ(1U, dataRecords.count);
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(1U, dataRecords.count);
readyToContinue = true;
}];
dataStore = nil;
TestWebKitAPI::Util::run(&readyToContinue);
readyToContinue = false;
// Delete all SW records, releasing our reference to the data store while the request is in flight.
dataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
[dataStore removeDataOfTypes:websiteDataTypes.get() modifiedSince:[NSDate distantPast] completionHandler:^() {
readyToContinue = true;
}];
dataStore = nil;
TestWebKitAPI::Util::run(&readyToContinue);
readyToContinue = false;
// The records should have been deleted, and the callback should have been made.
// Now refetch the records to verify they are gone.
dataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
[dataStore fetchDataRecordsOfTypes:websiteDataTypes.get() completionHandler:^(NSArray<WKWebsiteDataRecord *> *dataRecords) {
EXPECT_EQ(0U, dataRecords.count);
readyToContinue = true;
}];
TestWebKitAPI::Util::run(&readyToContinue);
}
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(1U, [allProcessPools count]);
auto* processPool = allProcessPools[0];
while (![processPool _networkProcessIdentifier])
TestWebKitAPI::Util::sleep(0.01);
kill([processPool _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 mainBundle] URLForResource:@"WebsiteDataStoreCustomPaths" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
[webView loadRequest:request];
[[[webView configuration] processPool] _syncNetworkProcessCookies];
// 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, 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 = [[[WKWebsiteDataStore alloc] _initWithConfiguration:dataStoreConfiguration.get()] autorelease];
[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 mainBundle] URLForResource:@"WebsiteDataStoreCustomPaths" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
[webView loadRequest:request];
[[[webView configuration] processPool] _syncNetworkProcessCookies];
// 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 = [[[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()] autorelease];
auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
NSURLRequest *request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
[webView loadRequest:request];
[webView _test_waitForDidFinishNavigation];
TestWebKitAPI::Util::spinRunLoop(1);
[webViewConfiguration.get().processPool _terminateNetworkProcess];
request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
[webView loadRequest:request];
[webView _test_waitForDidFinishNavigation];
}
TEST(WebKit, WebsiteDataStoreConfigurationPathNull)
{
EXPECT_TRUE([[[[_WKWebsiteDataStoreConfiguration alloc] init] autorelease] _indexedDBDatabaseDirectory]);
EXPECT_FALSE([[[[_WKWebsiteDataStoreConfiguration alloc] initNonPersistentConfiguration] autorelease] _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, NetworkCacheDirectory)
{
using namespace TestWebKitAPI;
TCPServer server([] (int socket) {
TCPServer::read(socket);
const char* response =
"HTTP/1.1 200 OK\r\n"
"Cache-Control: max-age=1000000\r\n"
"Content-Length: 6\r\n\r\n"
"Hello!";
TCPServer::write(socket, response, strlen(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:[[[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()] autorelease]];
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);
}
#if HAVE(NETWORK_FRAMEWORK)
TEST(WebKit, ApplicationCacheDirectories)
{
TestWebKitAPI::HTTPServer server({
{ "/index.html", { "<html manifest='test.appcache'>" } },
{ "/test.appcache", { "CACHE MANIFEST\nindex.html\ntest.mp4\n" } },
{ "/test.mp4", { {{ "Content-Type", "video/test" }}, "test!" }},
});
NSURL *tempDir = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"CustomPathsTest"] isDirectory:YES];
NSString *path = tempDir.path;
NSString *subdirectoryPath = [path stringByAppendingPathComponent:@"testsubdirectory"];
NSFileManager *fileManager = [NSFileManager defaultManager];
EXPECT_FALSE([fileManager fileExistsAtPath:subdirectoryPath]);
auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
[websiteDataStoreConfiguration setApplicationCacheDirectory:tempDir];
[websiteDataStoreConfiguration setApplicationCacheFlatFileSubdirectoryName:@"testsubdirectory"];
auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
[webViewConfiguration setWebsiteDataStore:[[[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()] autorelease]];
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/index.html", server.port()]]]];
while (![fileManager fileExistsAtPath:subdirectoryPath])
TestWebKitAPI::Util::spinRunLoop();
NSError *error = nil;
[fileManager removeItemAtPath:path error:&error];
EXPECT_FALSE(error);
}
#if HAVE(CFNETWORK_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({
{ "/", { {{ "alt-svc", "h3-24=\":443\"; ma=3600; persist=1" }}, "<html>test content</html>" } }
}, 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];
_WKWebsiteDataStoreConfiguration *dataStoreConfiguration = [[[_WKWebsiteDataStoreConfiguration alloc] init] autorelease];
dataStoreConfiguration.alternativeServicesStorageDirectory = tempDir;
WKWebsiteDataStore *dataStore = [[[WKWebsiteDataStore alloc] _initWithConfiguration:dataStoreConfiguration] autorelease];
__block bool done = false;
[dataStore fetchDataRecordsOfTypes:[NSSet setWithObject:_WKWebsiteDataTypeAlternativeServices] completionHandler:^(NSArray<WKWebsiteDataRecord *> *records) {
EXPECT_EQ(records.count, 0u);
done = true;
}];
Util::run(&done);
WKWebViewConfiguration *webViewConfiguration = [[[WKWebViewConfiguration alloc] init] autorelease];
webViewConfiguration.websiteDataStore = dataStore;
WKWebView *webView = [[[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration] autorelease];
TestNavigationDelegate *delegate = [[[TestNavigationDelegate alloc] init] autorelease];
webView.navigationDelegate = delegate;
delegate.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, ^(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(CFNETWORK_ALTERNATIVE_SERVICE)
#endif // HAVE(NETWORK_FRAMEWORK)
// FIXME: investigate why this test times out on High Sierra
#if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101400) || PLATFORM(IOS_FAMILY)
TEST(WebKit, MediaCache)
{
JSC::Config::configureForTesting();
std::atomic<bool> done = false;
using namespace TestWebKitAPI;
RetainPtr<NSData> data = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"test" withExtension:@"mp4" subdirectory:@"TestWebKitAPI.resources"]];
uint64_t dataLength = [data length];
TCPServer server([&] (int socket) {
TCPServer::read(socket);
const char* 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>";
TCPServer::write(socket, firstResponse, strlen(firstResponse));
while (!done) {
auto bytes = TCPServer::read(socket);
if (done || bytes.isEmpty())
break;
StringView request(static_cast<const LChar*>(bytes.data()), bytes.size());
String 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 = *request.substring(begin + rangeBytes.length(), dash - begin - rangeBytes.length()).toUInt64Strict();
auto rangeEnd = *request.substring(dash + 1, end - dash - 1).toUInt64Strict();
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, dataLength, rangeEnd - rangeBegin];
NSData *responseHeader = [responseHeaderString dataUsingEncoding:NSUTF8StringEncoding];
NSData *responseBody = [data subdataWithRange:NSMakeRange(rangeBegin, rangeEnd - rangeBegin)];
NSMutableData *response = [NSMutableData dataWithCapacity:responseHeader.length + responseBody.length];
[response appendData:responseHeader];
[response appendData:responseBody];
TCPServer::write(socket, response.bytes, response.length);
}
});
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:[[[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()] autorelease]];
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);
done = true;
[[webView configuration].processPool _terminateNetworkProcess];
[fileManager removeItemAtPath:path error:&error];
EXPECT_FALSE(error);
}
#endif