blob: 0905c7e0018ed48be7048e412010e647092ecd92 [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 <WebCore/DatabaseTracker.h>
#import <WebCore/OriginLock.h>
#import <WebCore/SQLiteDatabase.h>
#import <WebCore/SQLiteStatement.h>
#import <WebCore/SecurityOriginData.h>
#import <wtf/FileSystem.h>
using namespace WebCore;
namespace TestWebKitAPI {
#if PLATFORM(IOS_FAMILY)
TEST(DatabaseTracker, DeleteDatabaseFileIfEmpty)
{
FileSystem::PlatformFileHandle handle;
String databaseFilePath = FileSystem::openTemporaryFile("tempEmptyDatabase", handle);
FileSystem::closeFile(handle);
auto fileSize = FileSystem::fileSize(databaseFilePath).value_or(0);
EXPECT_EQ(0U, fileSize);
EXPECT_TRUE(DatabaseTracker::deleteDatabaseFileIfEmpty(databaseFilePath));
bool fileStillExists = FileSystem::fileExists(databaseFilePath);
EXPECT_FALSE(fileStillExists);
if (fileStillExists)
FileSystem::deleteFile(databaseFilePath);
}
#endif // PLATFORM(IOS_FAMILY)
#if PLATFORM(COCOA)
static void addToDatabasesTable(const String& databasePath, const SecurityOriginData& origin, const String& newDatabaseName, const String& newDatabasePath)
{
SQLiteDatabase database;
database.open(databasePath);
if (auto addDatabaseStatement = database.prepareStatement("INSERT INTO Databases (origin, name, path) VALUES (?, ?, ?);"_s)) {
addDatabaseStatement->bindText(1, origin.databaseIdentifier());
addDatabaseStatement->bindText(2, newDatabaseName);
addDatabaseStatement->bindText(3, newDatabasePath);
addDatabaseStatement->executeCommand();
}
database.close();
}
static void removeDirectoryAndAllContents(const String& directoryPath)
{
for (auto& fileName : FileSystem::listDirectory(directoryPath))
EXPECT_TRUE(FileSystem::deleteFile(FileSystem::pathByAppendingComponent(directoryPath, fileName)));
if (FileSystem::fileExists(directoryPath))
EXPECT_TRUE(FileSystem::deleteEmptyDirectory(directoryPath));
}
static void createFileAtPath(const String& path)
{
FileSystem::PlatformFileHandle fileHandle = FileSystem::openFile(path, FileSystem::FileOpenMode::Write);
EXPECT_NE(-1, fileHandle);
FileSystem::closeFile(fileHandle);
EXPECT_TRUE(FileSystem::fileExists(path));
}
TEST(DatabaseTracker, DeleteOrigin)
{
// Test the expected case. There is an entry in both the Origins and Databases
// tables, and an actual database on disk.
// In this case, we should remove the origin's information from both the Origins
// and Databases tables, and remove the database from disk.
NSString *webSQLDirectory = FileSystem::createTemporaryDirectory(@"WebSQL");
String databaseDirectoryPath(webSQLDirectory.UTF8String);
std::unique_ptr<DatabaseTracker> databaseTracker = DatabaseTracker::trackerWithDatabasePath(databaseDirectoryPath);
SecurityOriginData origin("https", "webkit.org", 443);
databaseTracker->setQuota(origin, 5242880);
EXPECT_EQ((unsigned)1, databaseTracker->origins().size());
String databasePath = FileSystem::pathByAppendingComponent(databaseDirectoryPath, "Databases.db");
EXPECT_TRUE(FileSystem::fileExists(databasePath));
String webDatabaseName = "database_name";
addToDatabasesTable(databasePath, origin, webDatabaseName, "database.db");
EXPECT_EQ((unsigned)1, databaseTracker->databaseNames(origin).size());
String originPath = FileSystem::pathByAppendingComponent(databaseDirectoryPath, origin.databaseIdentifier());
EXPECT_TRUE(FileSystem::makeAllDirectories(originPath));
EXPECT_TRUE(FileSystem::fileExists(originPath));
String fullWebDatabasePath = databaseTracker->fullPathForDatabase(origin, webDatabaseName, false);
createFileAtPath(fullWebDatabasePath);
EXPECT_TRUE(databaseTracker->deleteOrigin(origin));
EXPECT_TRUE(databaseTracker->origins().isEmpty());
EXPECT_TRUE(databaseTracker->databaseNames(origin).isEmpty());
#if PLATFORM(IOS_FAMILY)
EXPECT_TRUE(DatabaseTracker::deleteDatabaseFileIfEmpty(fullWebDatabasePath));
EXPECT_TRUE(FileSystem::deleteEmptyDirectory(originPath));
#else
EXPECT_FALSE(FileSystem::fileExists(fullWebDatabasePath));
EXPECT_FALSE(FileSystem::fileExists(originPath));
#endif
removeDirectoryAndAllContents(databaseDirectoryPath);
EXPECT_FALSE(FileSystem::fileExists(databaseDirectoryPath));
}
TEST(DatabaseTracker, DeleteOriginWhenDatabaseDoesNotExist)
{
// Test the case where there is an entry in both the Origins and Databases tables,
// but not an actual database on disk.
// The information should still be removed from the tables.
NSString *webSQLDirectory = FileSystem::createTemporaryDirectory(@"WebSQL");
String databaseDirectoryPath(webSQLDirectory.UTF8String);
std::unique_ptr<DatabaseTracker> databaseTracker = DatabaseTracker::trackerWithDatabasePath(databaseDirectoryPath);
SecurityOriginData origin("https", "webkit.org", 443);
databaseTracker->setQuota(origin, 5242880);
EXPECT_EQ((unsigned)1, databaseTracker->origins().size());
String databasePath = FileSystem::pathByAppendingComponent(databaseDirectoryPath, "Databases.db");
EXPECT_TRUE(FileSystem::fileExists(databasePath));
String webDatabaseName = "database_name";
addToDatabasesTable(databasePath, origin, webDatabaseName, "database.db");
EXPECT_EQ((unsigned)1, databaseTracker->databaseNames(origin).size());
String webDatabaseFullPath = databaseTracker->fullPathForDatabase(origin, webDatabaseName, false);
EXPECT_FALSE(FileSystem::fileExists(webDatabaseFullPath));
EXPECT_TRUE(databaseTracker->deleteOrigin(origin));
EXPECT_TRUE(databaseTracker->origins().isEmpty());
EXPECT_TRUE(databaseTracker->databaseNames(origin).isEmpty());
removeDirectoryAndAllContents(databaseDirectoryPath);
EXPECT_FALSE(FileSystem::fileExists(databaseDirectoryPath));
}
TEST(DatabaseTracker, DeleteOriginWhenDeletingADatabaseFails)
{
// Test the case where there are entries in both the Databases and Origins tables.
// There are also databases on disk.
// When we call deleteOrigin(), deleting one of these databases fails.
// In this case, we shouldn't remove the information from either the Databases or
// Origins tables.
NSString *webSQLDirectory = FileSystem::createTemporaryDirectory(@"WebSQL");
String databaseDirectoryPath(webSQLDirectory.UTF8String);
std::unique_ptr<DatabaseTracker> databaseTracker = DatabaseTracker::trackerWithDatabasePath(databaseDirectoryPath);
SecurityOriginData origin("https", "webkit.org", 443);
databaseTracker->setQuota(origin, 5242880);
EXPECT_EQ((unsigned)1, databaseTracker->origins().size());
String databasePath = FileSystem::pathByAppendingComponent(databaseDirectoryPath, "Databases.db");
EXPECT_TRUE(FileSystem::fileExists(databasePath));
String webDatabaseName = "database_name";
addToDatabasesTable(databasePath, origin, webDatabaseName, "database.db");
EXPECT_EQ((unsigned)1, databaseTracker->databaseNames(origin).size());
String originPath = FileSystem::pathByAppendingComponent(databaseDirectoryPath, origin.databaseIdentifier());
EXPECT_TRUE(FileSystem::makeAllDirectories(originPath));
EXPECT_TRUE(FileSystem::fileExists(originPath));
String fullWebDatabasePath = databaseTracker->fullPathForDatabase(origin, webDatabaseName, false);
createFileAtPath(fullWebDatabasePath);
chmod(fullWebDatabasePath.utf8().data(), 555);
#if !PLATFORM(IOS_FAMILY)
chflags(fullWebDatabasePath.utf8().data(), UF_IMMUTABLE);
#endif
EXPECT_FALSE(databaseTracker->deleteOrigin(origin));
EXPECT_TRUE(FileSystem::fileExists(fullWebDatabasePath));
EXPECT_TRUE(FileSystem::fileExists(originPath));
EXPECT_EQ((unsigned)1, databaseTracker->origins().size());
EXPECT_EQ((unsigned)1, databaseTracker->databaseNames(origin).size());
chmod(fullWebDatabasePath.utf8().data(), 666);
#if !PLATFORM(IOS_FAMILY)
chflags(fullWebDatabasePath.utf8().data(), 0);
#endif
EXPECT_TRUE(FileSystem::deleteFile(fullWebDatabasePath));
EXPECT_TRUE(FileSystem::deleteEmptyDirectory(originPath));
removeDirectoryAndAllContents(databaseDirectoryPath);
EXPECT_FALSE(FileSystem::fileExists(databaseDirectoryPath));
}
TEST(DatabaseTracker, DeleteOriginWithMissingEntryInDatabasesTable)
{
// Test the case where there is an entry in the Origins table but not
// the Databases table. There is an actual database on disk.
// The information should still be removed from the Origins table, and the
// database should be deleted from disk.
NSString *webSQLDirectory = FileSystem::createTemporaryDirectory(@"WebSQL");
String databaseDirectoryPath(webSQLDirectory.UTF8String);
std::unique_ptr<DatabaseTracker> databaseTracker = DatabaseTracker::trackerWithDatabasePath(databaseDirectoryPath);
SecurityOriginData origin("https", "webkit.org", 443);
databaseTracker->setQuota(origin, 5242880);
EXPECT_EQ((unsigned)1, databaseTracker->origins().size());
String databasePath = FileSystem::pathByAppendingComponent(databaseDirectoryPath, "Databases.db");
EXPECT_TRUE(FileSystem::fileExists(databasePath));
EXPECT_TRUE(databaseTracker->databaseNames(origin).isEmpty());
String originPath = FileSystem::pathByAppendingComponent(databaseDirectoryPath, origin.databaseIdentifier());
EXPECT_TRUE(FileSystem::makeAllDirectories(originPath));
EXPECT_TRUE(FileSystem::fileExists(originPath));
String webDatabasePath = FileSystem::pathByAppendingComponent(originPath, "database.db");
createFileAtPath(webDatabasePath);
EXPECT_TRUE(databaseTracker->deleteOrigin(origin));
EXPECT_FALSE(FileSystem::fileExists(webDatabasePath));
EXPECT_FALSE(FileSystem::fileExists(originPath));
EXPECT_TRUE(databaseTracker->origins().isEmpty());
EXPECT_TRUE(databaseTracker->databaseNames(origin).isEmpty());
removeDirectoryAndAllContents(databaseDirectoryPath);
EXPECT_FALSE(FileSystem::fileExists(databaseDirectoryPath));
}
TEST(DatabaseTracker, DeleteDatabase)
{
// Test the expected case. There is an entry in the Databases table
// and a database on disk. After the deletion, the database should be deleted
// from disk, and the information should be gone from the Databases table.
NSString *webSQLDirectory = FileSystem::createTemporaryDirectory(@"WebSQL");
String databaseDirectoryPath(webSQLDirectory.UTF8String);
std::unique_ptr<DatabaseTracker> databaseTracker = DatabaseTracker::trackerWithDatabasePath(databaseDirectoryPath);
SecurityOriginData origin("https", "webkit.org", 443);
databaseTracker->setQuota(origin, 5242880);
EXPECT_EQ((unsigned)1, databaseTracker->origins().size());
String databasePath = FileSystem::pathByAppendingComponent(databaseDirectoryPath, "Databases.db");
EXPECT_TRUE(FileSystem::fileExists(databasePath));
String webDatabaseName = "database_name";
addToDatabasesTable(databasePath, origin, webDatabaseName, "database.db");
EXPECT_EQ((unsigned)1, databaseTracker->databaseNames(origin).size());
String originPath = FileSystem::pathByAppendingComponent(databaseDirectoryPath, origin.databaseIdentifier());
EXPECT_TRUE(FileSystem::makeAllDirectories(originPath));
EXPECT_TRUE(FileSystem::fileExists(originPath));
String fullWebDatabasePath = databaseTracker->fullPathForDatabase(origin, webDatabaseName, false);
createFileAtPath(fullWebDatabasePath);
EXPECT_TRUE(databaseTracker->deleteDatabase(origin, webDatabaseName));
EXPECT_TRUE(databaseTracker->databaseNames(origin).isEmpty());
#if PLATFORM(IOS_FAMILY)
EXPECT_TRUE(DatabaseTracker::deleteDatabaseFileIfEmpty(fullWebDatabasePath));
#else
EXPECT_FALSE(FileSystem::fileExists(fullWebDatabasePath));
#endif
EXPECT_EQ((unsigned)1, databaseTracker->origins().size());
EXPECT_TRUE(FileSystem::deleteEmptyDirectory(originPath));
removeDirectoryAndAllContents(databaseDirectoryPath);
EXPECT_FALSE(FileSystem::fileExists(databaseDirectoryPath));
}
TEST(DatabaseTracker, DeleteDatabaseWhenDatabaseDoesNotExist)
{
// Test the case where we try to delete a database that doesn't exist on disk.
// We should still remove the database information from the Databases table.
NSString *webSQLDirectory = FileSystem::createTemporaryDirectory(@"WebSQL");
String databaseDirectoryPath(webSQLDirectory.UTF8String);
std::unique_ptr<DatabaseTracker> databaseTracker = DatabaseTracker::trackerWithDatabasePath(databaseDirectoryPath);
SecurityOriginData origin("https", "webkit.org", 443);
databaseTracker->setQuota(origin, 5242880);
EXPECT_EQ((unsigned)1, databaseTracker->origins().size());
String databasePath = FileSystem::pathByAppendingComponent(databaseDirectoryPath, "Databases.db");
EXPECT_TRUE(FileSystem::fileExists(databasePath));
String webDatabaseName = "database_name";
addToDatabasesTable(databasePath, origin, webDatabaseName, "database.db");
EXPECT_EQ((unsigned)1, databaseTracker->databaseNames(origin).size());
String webDatabaseFullPath = databaseTracker->fullPathForDatabase(origin, webDatabaseName, false);
EXPECT_FALSE(FileSystem::fileExists(webDatabaseFullPath));
EXPECT_TRUE(databaseTracker->deleteDatabase(origin, webDatabaseName));
EXPECT_TRUE(databaseTracker->databaseNames(origin).isEmpty());
removeDirectoryAndAllContents(databaseDirectoryPath);
EXPECT_FALSE(FileSystem::fileExists(databaseDirectoryPath));
}
#endif // PLATFORM(COCOA)
} // namespace TestWebKitAPI