If we fail to delete any database file, don't remove its information from the tracker...
authorbfulgham@apple.com <bfulgham@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 16 Oct 2017 19:20:17 +0000 (19:20 +0000)
committerbfulgham@apple.com <bfulgham@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 16 Oct 2017 19:20:17 +0000 (19:20 +0000)
<rdar://problem/34576132> and https://bugs.webkit.org/show_bug.cgi?id=178251

Patch by Maureen Daum <mdaum@apple.com> on 2017-10-16
Reviewed by Brady Eidson.

Add tests that verify we correctly delete databases and remove their information from
the tracker database, even if the database doesn't exist. Verify that if we fail to
delete a database, we don't remove its information from the tracker database.

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
Move DatabaseTrackerTest.cpp to DatabaseTrackerTest.mm so that we can use the cocoa
method for creating a temporary directory in the tests.
* TestWebKitAPI/Tests/WebCore/DatabaseTrackerTest.cpp: Removed.
The existing test was copied to DatabaseTrackerTest.mm.
* TestWebKitAPI/Tests/WebCore/cocoa/DatabaseTrackerTest.mm: Added.
(TestWebKitAPI::TEST):
(TestWebKitAPI::addToDatabasesTable):
(TestWebKitAPI::removeDirectoryAndAllContents):
(TestWebKitAPI::createFileAtPath):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@223422 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Tools/ChangeLog
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/WebCore/DatabaseTrackerTest.cpp [deleted file]
Tools/TestWebKitAPI/Tests/WebCore/cocoa/DatabaseTrackerTest.mm [new file with mode: 0644]

index 7d7ce27..6bfee9a 100644 (file)
@@ -1,3 +1,25 @@
+2017-10-16  Maureen Daum  <mdaum@apple.com>
+
+        If we fail to delete any database file, don't remove its information from the tracker database
+        <rdar://problem/34576132> and https://bugs.webkit.org/show_bug.cgi?id=178251
+
+        Reviewed by Brady Eidson.
+
+        Add tests that verify we correctly delete databases and remove their information from
+        the tracker database, even if the database doesn't exist. Verify that if we fail to
+        delete a database, we don't remove its information from the tracker database.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        Move DatabaseTrackerTest.cpp to DatabaseTrackerTest.mm so that we can use the cocoa
+        method for creating a temporary directory in the tests.
+        * TestWebKitAPI/Tests/WebCore/DatabaseTrackerTest.cpp: Removed.
+        The existing test was copied to DatabaseTrackerTest.mm.
+        * TestWebKitAPI/Tests/WebCore/cocoa/DatabaseTrackerTest.mm: Added.
+        (TestWebKitAPI::TEST):
+        (TestWebKitAPI::addToDatabasesTable):
+        (TestWebKitAPI::removeDirectoryAndAllContents):
+        (TestWebKitAPI::createFileAtPath):
+
 2017-10-16  David Kilzer  <ddkilzer@apple.com>
 
         Add RELEASE_ASSERT_WITH_SECURITY_IMPLICATION() macro
index 7a97797..88b4d7c 100644 (file)
                6356FB221EC4E0BA0044BF18 /* VisibleContentRect.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6356FB211EC4E0BA0044BF18 /* VisibleContentRect.mm */; };
                636353A71E98665D0009F8AF /* GeolocationGetCurrentPositionResult.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 636353A61E9861940009F8AF /* GeolocationGetCurrentPositionResult.html */; };
                6BFD294C1D5E6C1D008EC968 /* HashCountedSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7A38D7E51C752D5F004F157D /* HashCountedSet.cpp */; };
+               751B05D61F8EAC410028A09E /* DatabaseTrackerTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 751B05D51F8EAC1A0028A09E /* DatabaseTrackerTest.mm */; };
                754CEC811F6722F200D0039A /* AutoFillAvailable.mm in Sources */ = {isa = PBXBuildFile; fileRef = 754CEC801F6722DC00D0039A /* AutoFillAvailable.mm */; };
-               755A20AF1E6E38630093C69F /* DatabaseTrackerTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 755A20AE1E6E38630093C69F /* DatabaseTrackerTest.cpp */; };
                764322D71B61CCC30024F801 /* WordBoundaryTypingAttributes.mm in Sources */ = {isa = PBXBuildFile; fileRef = 764322D51B61CCA40024F801 /* WordBoundaryTypingAttributes.mm */; };
                7673499D1930C5BB00E44DF9 /* StopLoadingDuringDidFailProvisionalLoad_bundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7673499A1930182E00E44DF9 /* StopLoadingDuringDidFailProvisionalLoad_bundle.cpp */; };
                76E182DD1547569100F1FADD /* WillSendSubmitEvent_Bundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 76E182DC1547569100F1FADD /* WillSendSubmitEvent_Bundle.cpp */; };
                634910DF1E9D3FF300880309 /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = System/Library/Frameworks/CoreLocation.framework; sourceTree = SDKROOT; };
                6356FB211EC4E0BA0044BF18 /* VisibleContentRect.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = VisibleContentRect.mm; sourceTree = "<group>"; };
                636353A61E9861940009F8AF /* GeolocationGetCurrentPositionResult.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = GeolocationGetCurrentPositionResult.html; sourceTree = "<group>"; };
+               751B05D51F8EAC1A0028A09E /* DatabaseTrackerTest.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = DatabaseTrackerTest.mm; sourceTree = "<group>"; };
                754CEC801F6722DC00D0039A /* AutoFillAvailable.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AutoFillAvailable.mm; sourceTree = "<group>"; };
-               755A20AE1E6E38630093C69F /* DatabaseTrackerTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DatabaseTrackerTest.cpp; sourceTree = "<group>"; };
                7560917719259C59009EF06E /* MemoryCacheAddImageToCacheIOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MemoryCacheAddImageToCacheIOS.mm; sourceTree = "<group>"; };
                75F3133F18C171B70041CAEC /* EphemeralSessionPushStateNoHistoryCallback.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EphemeralSessionPushStateNoHistoryCallback.cpp; sourceTree = "<group>"; };
                764322D51B61CCA40024F801 /* WordBoundaryTypingAttributes.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WordBoundaryTypingAttributes.mm; sourceTree = "<group>"; };
                                1C9EB8401E380DA1005C6442 /* ComplexTextController.cpp */,
                                7CB184C41AA3F2100066EDFD /* ContentExtensions.cpp */,
                                CD5451E919E41F9D0016936F /* CSSParser.cpp */,
-                               755A20AE1E6E38630093C69F /* DatabaseTrackerTest.cpp */,
                                260BA5781B1D2E7B004FA07C /* DFACombiner.cpp */,
                                260BA57A1B1D2EE2004FA07C /* DFAHelpers.h */,
                                26F6E1EF1ADC749B00DE696B /* DFAMinimizer.cpp */,
                CD89D0371C4EDB1300040A04 /* cocoa */ = {
                        isa = PBXGroup;
                        children = (
+                               751B05D51F8EAC1A0028A09E /* DatabaseTrackerTest.mm */,
                                5769C50A1D9B0001000847FB /* SerializedCryptoKeyWrap.mm */,
                                A17991861E1C994E00A505ED /* SharedBuffer.mm */,
                                93A7EB3C18FA63A4009E7670 /* URLExtras.mm */,
                                7CCE7F291A411B1000447C4C /* CustomProtocolsInvalidScheme.mm in Sources */,
                                7CCE7F2A1A411B1000447C4C /* CustomProtocolsSyncXHRTest.mm in Sources */,
                                7CCE7F2B1A411B1000447C4C /* CustomProtocolsTest.mm in Sources */,
-                               755A20AF1E6E38630093C69F /* DatabaseTrackerTest.cpp in Sources */,
                                2DC4CF771D2D9DD800ECCC94 /* DataDetection.mm in Sources */,
                                F4D4F3B61E4E2BCB00BB2767 /* DataInteractionSimulator.mm in Sources */,
                                F4D4F3B91E4E36E400BB2767 /* DataInteractionTests.mm in Sources */,
                                0F3B94A71A77267400DE3272 /* WKWebViewEvaluateJavaScript.mm in Sources */,
                                D34E08761E4E42E1005FF14A /* WKWebViewGetContents.mm in Sources */,
                                F4FA91811E61849B007B8C1D /* WKWebViewMacEditingTests.mm in Sources */,
+                               751B05D61F8EAC410028A09E /* DatabaseTrackerTest.mm in Sources */,
                                93F56DA91E5F919D003EDE84 /* WKWebViewSnapshot.mm in Sources */,
                                9984FACC1CFFAF60008D198C /* WKWebViewTextInput.mm in Sources */,
                                764322D71B61CCC30024F801 /* WordBoundaryTypingAttributes.mm in Sources */,
diff --git a/Tools/TestWebKitAPI/Tests/WebCore/DatabaseTrackerTest.cpp b/Tools/TestWebKitAPI/Tests/WebCore/DatabaseTrackerTest.cpp
deleted file mode 100644 (file)
index ca07e88..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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.
- */
-
-#include "config.h"
-
-#if PLATFORM(IOS)
-
-#include <WebCore/DatabaseTracker.h>
-#include <WebCore/FileSystem.h>
-
-using namespace WebCore;
-
-namespace TestWebKitAPI {
-
-TEST(DatabaseTracker, DeleteDatabaseFileIfEmpty)
-{
-    PlatformFileHandle handle;
-    String databaseFilePath = openTemporaryFile("tempEmptyDatabase", handle);
-    closeFile(handle);
-
-    long long fileSize;
-    getFileSize(databaseFilePath, fileSize);
-    EXPECT_EQ(0, fileSize);
-
-    EXPECT_TRUE(DatabaseTracker::deleteDatabaseFileIfEmpty(databaseFilePath));
-
-    bool fileStillExists = fileExists(databaseFilePath);
-    EXPECT_FALSE(fileStillExists);
-
-    if (fileStillExists)
-        deleteFile(databaseFilePath);
-}
-
-} // namespace TestWebKitAPI
-
-#endif // PLATFORM(IOS)
diff --git a/Tools/TestWebKitAPI/Tests/WebCore/cocoa/DatabaseTrackerTest.mm b/Tools/TestWebKitAPI/Tests/WebCore/cocoa/DatabaseTrackerTest.mm
new file mode 100644 (file)
index 0000000..730a0ea
--- /dev/null
@@ -0,0 +1,307 @@
+/*
+ * 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.
+ */
+
+#include "config.h"
+
+#include <WebCore/DatabaseTracker.h>
+#include <WebCore/FileSystem.h>
+#include <WebCore/OriginLock.h>
+#include <WebCore/SQLiteDatabase.h>
+#include <WebCore/SQLiteStatement.h>
+#include <WebCore/SecurityOriginData.h>
+
+using namespace WebCore;
+
+namespace TestWebKitAPI {
+
+#if PLATFORM(IOS)
+
+TEST(DatabaseTracker, DeleteDatabaseFileIfEmpty)
+{
+    PlatformFileHandle handle;
+    String databaseFilePath = openTemporaryFile("tempEmptyDatabase", handle);
+    closeFile(handle);
+
+    long long fileSize;
+    getFileSize(databaseFilePath, fileSize);
+    EXPECT_EQ(0, fileSize);
+
+    EXPECT_TRUE(DatabaseTracker::deleteDatabaseFileIfEmpty(databaseFilePath));
+
+    bool fileStillExists = fileExists(databaseFilePath);
+    EXPECT_FALSE(fileStillExists);
+
+    if (fileStillExists)
+        deleteFile(databaseFilePath);
+}
+
+#endif // PLATFORM(IOS)
+
+#if PLATFORM(COCOA)
+
+static void addToDatabasesTable(const String& databasePath, const SecurityOriginData& origin, const String& newDatabaseName, const String& newDatabasePath)
+{
+    SQLiteDatabase database;
+    database.open(databasePath);
+
+    SQLiteStatement addDatabaseStatement(database, "INSERT INTO Databases (origin, name, path) VALUES (?, ?, ?);");
+    addDatabaseStatement.prepare();
+    addDatabaseStatement.bindText(1, origin.databaseIdentifier());
+    addDatabaseStatement.bindText(2, newDatabaseName);
+    addDatabaseStatement.bindText(3, newDatabasePath);
+    addDatabaseStatement.executeCommand();
+
+    database.close();
+}
+
+static void removeDirectoryAndAllContents(const String& directoryPath)
+{
+    for (const auto& file : listDirectory(directoryPath, "*"))
+        EXPECT_TRUE(deleteFile(file));
+
+    if (fileExists(directoryPath))
+        EXPECT_TRUE(deleteEmptyDirectory(directoryPath));
+}
+
+static void createFileAtPath(const String& path)
+{
+    PlatformFileHandle fileHandle = openFile(path, OpenForWrite);
+    EXPECT_NE(-1, fileHandle);
+    closeFile(fileHandle);
+    EXPECT_TRUE(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 = 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 = pathByAppendingComponent(databaseDirectoryPath, "Databases.db");
+    EXPECT_TRUE(fileExists(databasePath));
+
+    String webDatabaseName = "database_name";
+    addToDatabasesTable(databasePath, origin, webDatabaseName, "database.db");
+    EXPECT_EQ((unsigned)1, databaseTracker->databaseNames(origin).size());
+
+    String originPath = pathByAppendingComponent(databaseDirectoryPath, origin.databaseIdentifier());
+    EXPECT_TRUE(makeAllDirectories(originPath));
+    EXPECT_TRUE(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)
+    EXPECT_TRUE(DatabaseTracker::deleteDatabaseFileIfEmpty(fullWebDatabasePath));
+    EXPECT_TRUE(deleteEmptyDirectory(originPath));
+#else
+    EXPECT_FALSE(fileExists(fullWebDatabasePath));
+    EXPECT_FALSE(fileExists(originPath));
+#endif
+
+    removeDirectoryAndAllContents(databaseDirectoryPath);
+    EXPECT_FALSE(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 = 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 = pathByAppendingComponent(databaseDirectoryPath, "Databases.db");
+    EXPECT_TRUE(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(fileExists(webDatabaseFullPath));
+
+    EXPECT_TRUE(databaseTracker->deleteOrigin(origin));
+
+    EXPECT_TRUE(databaseTracker->origins().isEmpty());
+    EXPECT_TRUE(databaseTracker->databaseNames(origin).isEmpty());
+
+    removeDirectoryAndAllContents(databaseDirectoryPath);
+    EXPECT_FALSE(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 = 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 = pathByAppendingComponent(databaseDirectoryPath, "Databases.db");
+    EXPECT_TRUE(fileExists(databasePath));
+
+    String webDatabaseName = "database_name";
+    addToDatabasesTable(databasePath, origin, webDatabaseName, "database.db");
+    EXPECT_EQ((unsigned)1, databaseTracker->databaseNames(origin).size());
+
+    String originPath = pathByAppendingComponent(databaseDirectoryPath, origin.databaseIdentifier());
+    EXPECT_TRUE(makeAllDirectories(originPath));
+    EXPECT_TRUE(fileExists(originPath));
+
+    String fullWebDatabasePath = databaseTracker->fullPathForDatabase(origin, webDatabaseName, false);
+    createFileAtPath(fullWebDatabasePath);
+
+    chmod(fullWebDatabasePath.utf8().data(), 555);
+
+#if !PLATFORM(IOS)
+    chflags(fullWebDatabasePath.utf8().data(), UF_IMMUTABLE);
+#endif
+
+    EXPECT_FALSE(databaseTracker->deleteOrigin(origin));
+
+    EXPECT_TRUE(fileExists(fullWebDatabasePath));
+    EXPECT_TRUE(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)
+    chflags(fullWebDatabasePath.utf8().data(), 0);
+#endif
+
+    EXPECT_TRUE(deleteFile(fullWebDatabasePath));
+    EXPECT_TRUE(deleteEmptyDirectory(originPath));
+
+    removeDirectoryAndAllContents(databaseDirectoryPath);
+    EXPECT_FALSE(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 = 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 = pathByAppendingComponent(databaseDirectoryPath, "Databases.db");
+    EXPECT_TRUE(fileExists(databasePath));
+
+    String webDatabaseName = "database_name";
+    addToDatabasesTable(databasePath, origin, webDatabaseName, "database.db");
+    EXPECT_EQ((unsigned)1, databaseTracker->databaseNames(origin).size());
+
+    String originPath = pathByAppendingComponent(databaseDirectoryPath, origin.databaseIdentifier());
+    EXPECT_TRUE(makeAllDirectories(originPath));
+    EXPECT_TRUE(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)
+    EXPECT_TRUE(DatabaseTracker::deleteDatabaseFileIfEmpty(fullWebDatabasePath));
+#else
+    EXPECT_FALSE(fileExists(fullWebDatabasePath));
+#endif
+
+    EXPECT_EQ((unsigned)1, databaseTracker->origins().size());
+    EXPECT_TRUE(deleteEmptyDirectory(originPath));
+    removeDirectoryAndAllContents(databaseDirectoryPath);
+    EXPECT_FALSE(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 = 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 = pathByAppendingComponent(databaseDirectoryPath, "Databases.db");
+    EXPECT_TRUE(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(fileExists(webDatabaseFullPath));
+
+    EXPECT_TRUE(databaseTracker->deleteDatabase(origin, webDatabaseName));
+    EXPECT_TRUE(databaseTracker->databaseNames(origin).isEmpty());
+
+    removeDirectoryAndAllContents(databaseDirectoryPath);
+    EXPECT_FALSE(fileExists(databaseDirectoryPath));
+}
+
+#endif // PLATFORM(COCOA)
+
+} // namespace TestWebKitAPI
+