Rename AtomicString to AtomString
[WebKit-https.git] / Source / WebCore / loader / appcache / ApplicationCacheStorage.cpp
index 778e4e3..da62d66 100644 (file)
 #include "ApplicationCacheGroup.h"
 #include "ApplicationCacheHost.h"
 #include "ApplicationCacheResource.h"
-#include "FileSystem.h"
-#include "KURL.h"
-#include "NotImplemented.h"
+#include "SQLiteDatabaseTracker.h"
 #include "SQLiteStatement.h"
 #include "SQLiteTransaction.h"
 #include "SecurityOrigin.h"
-#include "UUID.h"
-#include <wtf/text/CString.h>
+#include "SecurityOriginData.h"
+#include <wtf/FileSystem.h>
 #include <wtf/StdLibExtras.h>
-#include <wtf/StringExtras.h>
+#include <wtf/URL.h>
+#include <wtf/UUID.h>
+#include <wtf/text/CString.h>
 #include <wtf/text/StringBuilder.h>
 
-using namespace std;
-
 namespace WebCore {
 
-static const char flatFileSubdirectory[] = "ApplicationCache";
-
 template <class T>
 class StorageIDJournal {
 public:  
     ~StorageIDJournal()
     {
-        size_t size = m_records.size();
-        for (size_t i = 0; i < size; ++i)
-            m_records[i].restore();
+        for (auto& record : m_records)
+            record.restore();
     }
 
     void add(T* resource, unsigned storageID)
@@ -71,7 +66,7 @@ public:
 private:
     class Record {
     public:
-        Record() : m_resource(0), m_storageID(0) { }
+        Record() : m_resource(nullptr), m_storageID(0) { }
         Record(T* resource, unsigned storageID) : m_resource(resource), m_storageID(storageID) { }
 
         void restore()
@@ -87,75 +82,73 @@ private:
     Vector<Record> m_records;
 };
 
-static unsigned urlHostHash(const KURL& url)
+static unsigned urlHostHash(const URL& url)
 {
-    unsigned hostStart = url.hostStart();
-    unsigned hostEnd = url.hostEnd();
-    
-    return AlreadyHashed::avoidDeletedValue(StringHasher::computeHashAndMaskTop8Bits(url.string().characters() + hostStart, hostEnd - hostStart));
+    StringView host = url.host();
+    if (host.is8Bit())
+        return AlreadyHashed::avoidDeletedValue(StringHasher::computeHashAndMaskTop8Bits(host.characters8(), host.length()));
+    return AlreadyHashed::avoidDeletedValue(StringHasher::computeHashAndMaskTop8Bits(host.characters16(), host.length()));
 }
 
-ApplicationCacheGroup* ApplicationCacheStorage::loadCacheGroup(const KURL& manifestURL)
+ApplicationCacheGroup* ApplicationCacheStorage::loadCacheGroup(const URL& manifestURL)
 {
+    SQLiteTransactionInProgressAutoCounter transactionCounter;
+
     openDatabase(false);
     if (!m_database.isOpen())
-        return 0;
+        return nullptr;
 
     SQLiteStatement statement(m_database, "SELECT id, manifestURL, newestCache FROM CacheGroups WHERE newestCache IS NOT NULL AND manifestURL=?");
-    if (statement.prepare() != SQLResultOk)
-        return 0;
+    if (statement.prepare() != SQLITE_OK)
+        return nullptr;
     
     statement.bindText(1, manifestURL);
    
     int result = statement.step();
-    if (result == SQLResultDone)
-        return 0;
+    if (result == SQLITE_DONE)
+        return nullptr;
     
-    if (result != SQLResultRow) {
+    if (result != SQLITE_ROW) {
         LOG_ERROR("Could not load cache group, error \"%s\"", m_database.lastErrorMsg());
-        return 0;
+        return nullptr;
     }
     
     unsigned newestCacheStorageID = static_cast<unsigned>(statement.getColumnInt64(2));
 
-    RefPtr<ApplicationCache> cache = loadCache(newestCacheStorageID);
+    auto cache = loadCache(newestCacheStorageID);
     if (!cache)
-        return 0;
+        return nullptr;
         
-    ApplicationCacheGroup* group = new ApplicationCacheGroup(manifestURL);
-      
-    group->setStorageID(static_cast<unsigned>(statement.getColumnInt64(0)));
-    group->setNewestCache(cache.release());
-
-    return group;
+    auto& group = *new ApplicationCacheGroup(*this, manifestURL);
+    group.setStorageID(static_cast<unsigned>(statement.getColumnInt64(0)));
+    group.setNewestCache(cache.releaseNonNull());
+    return &group;
 }    
 
-ApplicationCacheGroup* ApplicationCacheStorage::findOrCreateCacheGroup(const KURL& manifestURL)
+ApplicationCacheGroup* ApplicationCacheStorage::findOrCreateCacheGroup(const URL& manifestURL)
 {
     ASSERT(!manifestURL.hasFragmentIdentifier());
 
-    CacheGroupMap::AddResult result = m_cachesInMemory.add(manifestURL, 0);
-    
+    auto result = m_cachesInMemory.add(manifestURL, nullptr);
     if (!result.isNewEntry) {
-        ASSERT(result.iterator->second);
-        return result.iterator->second;
+        ASSERT(result.iterator->value);
+        return result.iterator->value;
     }
 
     // Look up the group in the database
-    ApplicationCacheGroup* group = loadCacheGroup(manifestURL);
+    auto* group = loadCacheGroup(manifestURL);
     
     // If the group was not found we need to create it
     if (!group) {
-        group = new ApplicationCacheGroup(manifestURL);
+        group = new ApplicationCacheGroup(*this, manifestURL);
         m_cacheHostSet.add(urlHostHash(manifestURL));
     }
-    
-    result.iterator->second = group;
-    
+
+    result.iterator->value = group;
     return group;
 }
 
-ApplicationCacheGroup* ApplicationCacheStorage::findInMemoryCacheGroup(const KURL& manifestURL) const
+ApplicationCacheGroup* ApplicationCacheStorage::findInMemoryCacheGroup(const URL& manifestURL) const
 {
     return m_cachesInMemory.get(manifestURL);
 }
@@ -171,20 +164,22 @@ void ApplicationCacheStorage::loadManifestHostHashes()
     // to avoid trying to open the database over and over if it doesn't exist.
     hasLoadedHashes = true;
     
+    SQLiteTransactionInProgressAutoCounter transactionCounter;
+
     openDatabase(false);
     if (!m_database.isOpen())
         return;
 
     // Fetch the host hashes.
     SQLiteStatement statement(m_database, "SELECT manifestHostHash FROM CacheGroups");    
-    if (statement.prepare() != SQLResultOk)
+    if (statement.prepare() != SQLITE_OK)
         return;
     
-    while (statement.step() == SQLResultRow)
+    while (statement.step() == SQLITE_ROW)
         m_cacheHostSet.add(static_cast<unsigned>(statement.getColumnInt64(0)));
 }    
 
-ApplicationCacheGroup* ApplicationCacheStorage::cacheGroupForURL(const KURL& url)
+ApplicationCacheGroup* ApplicationCacheStorage::cacheGroupForURL(const URL& url)
 {
     ASSERT(!url.hasFragmentIdentifier());
     
@@ -192,13 +187,10 @@ ApplicationCacheGroup* ApplicationCacheStorage::cacheGroupForURL(const KURL& url
     
     // Hash the host name and see if there's a manifest with the same host.
     if (!m_cacheHostSet.contains(urlHostHash(url)))
-        return 0;
+        return nullptr;
 
     // Check if a cache already exists in memory.
-    CacheGroupMap::const_iterator end = m_cachesInMemory.end();
-    for (CacheGroupMap::const_iterator it = m_cachesInMemory.begin(); it != end; ++it) {
-        ApplicationCacheGroup* group = it->second;
-
+    for (const auto& group : m_cachesInMemory.values()) {
         ASSERT(!group->isObsolete());
 
         if (!protocolHostAndPortAreEqual(url, group->manifestURL()))
@@ -215,16 +207,18 @@ ApplicationCacheGroup* ApplicationCacheStorage::cacheGroupForURL(const KURL& url
     }
     
     if (!m_database.isOpen())
-        return 0;
+        return nullptr;
         
+    SQLiteTransactionInProgressAutoCounter transactionCounter;
+
     // Check the database. Look for all cache groups with a newest cache.
     SQLiteStatement statement(m_database, "SELECT id, manifestURL, newestCache FROM CacheGroups WHERE newestCache IS NOT NULL");
-    if (statement.prepare() != SQLResultOk)
-        return 0;
+    if (statement.prepare() != SQLITE_OK)
+        return nullptr;
     
     int result;
-    while ((result = statement.step()) == SQLResultRow) {
-        KURL manifestURL = KURL(ParsedURLString, statement.getColumnText(1));
+    while ((result = statement.step()) == SQLITE_ROW) {
+        URL manifestURL = URL({ }, statement.getColumnText(1));
 
         if (m_cachesInMemory.contains(manifestURL))
             continue;
@@ -235,45 +229,42 @@ ApplicationCacheGroup* ApplicationCacheStorage::cacheGroupForURL(const KURL& url
         // We found a cache group that matches. Now check if the newest cache has a resource with
         // a matching URL.
         unsigned newestCacheID = static_cast<unsigned>(statement.getColumnInt64(2));
-        RefPtr<ApplicationCache> cache = loadCache(newestCacheID);
+        auto cache = loadCache(newestCacheID);
         if (!cache)
             continue;
 
-        ApplicationCacheResource* resource = cache->resourceForURL(url);
+        auto* resource = cache->resourceForURL(url);
         if (!resource)
             continue;
         if (resource->type() & ApplicationCacheResource::Foreign)
             continue;
 
-        ApplicationCacheGroup* group = new ApplicationCacheGroup(manifestURL);
-        
-        group->setStorageID(static_cast<unsigned>(statement.getColumnInt64(0)));
-        group->setNewestCache(cache.release());
-        
-        m_cachesInMemory.set(group->manifestURL(), group);
-        
-        return group;
+        auto& group = *new ApplicationCacheGroup(*this, manifestURL);
+        group.setStorageID(static_cast<unsigned>(statement.getColumnInt64(0)));
+        group.setNewestCache(cache.releaseNonNull());
+        m_cachesInMemory.set(group.manifestURL(), &group);
+
+        return &group;
     }
 
-    if (result != SQLResultDone)
+    if (result != SQLITE_DONE)
         LOG_ERROR("Could not load cache group, error \"%s\"", m_database.lastErrorMsg());
     
-    return 0;
+    return nullptr;
 }
 
-ApplicationCacheGroup* ApplicationCacheStorage::fallbackCacheGroupForURL(const KURL& url)
+ApplicationCacheGroup* ApplicationCacheStorage::fallbackCacheGroupForURL(const URL& url)
 {
+    SQLiteTransactionInProgressAutoCounter transactionCounter;
+
     ASSERT(!url.hasFragmentIdentifier());
 
     // Check if an appropriate cache already exists in memory.
-    CacheGroupMap::const_iterator end = m_cachesInMemory.end();
-    for (CacheGroupMap::const_iterator it = m_cachesInMemory.begin(); it != end; ++it) {
-        ApplicationCacheGroup* group = it->second;
-        
+    for (auto* group : m_cachesInMemory.values()) {
         ASSERT(!group->isObsolete());
 
         if (ApplicationCache* cache = group->newestCache()) {
-            KURL fallbackURL;
+            URL fallbackURL;
             if (cache->isURLInOnlineWhitelist(url))
                 continue;
             if (!cache->urlMatchesFallbackNamespace(url, &fallbackURL))
@@ -285,16 +276,16 @@ ApplicationCacheGroup* ApplicationCacheStorage::fallbackCacheGroupForURL(const K
     }
     
     if (!m_database.isOpen())
-        return 0;
+        return nullptr;
         
     // Check the database. Look for all cache groups with a newest cache.
     SQLiteStatement statement(m_database, "SELECT id, manifestURL, newestCache FROM CacheGroups WHERE newestCache IS NOT NULL");
-    if (statement.prepare() != SQLResultOk)
-        return 0;
+    if (statement.prepare() != SQLITE_OK)
+        return nullptr;
     
     int result;
-    while ((result = statement.step()) == SQLResultRow) {
-        KURL manifestURL = KURL(ParsedURLString, statement.getColumnText(1));
+    while ((result = statement.step()) == SQLITE_ROW) {
+        URL manifestURL = URL({ }, statement.getColumnText(1));
 
         if (m_cachesInMemory.contains(manifestURL))
             continue;
@@ -306,9 +297,9 @@ ApplicationCacheGroup* ApplicationCacheStorage::fallbackCacheGroupForURL(const K
         // We found a cache group that matches. Now check if the newest cache has a resource with
         // a matching fallback namespace.
         unsigned newestCacheID = static_cast<unsigned>(statement.getColumnInt64(2));
-        RefPtr<ApplicationCache> cache = loadCache(newestCacheID);
+        auto cache = loadCache(newestCacheID);
 
-        KURL fallbackURL;
+        URL fallbackURL;
         if (cache->isURLInOnlineWhitelist(url))
             continue;
         if (!cache->urlMatchesFallbackNamespace(url, &fallbackURL))
@@ -316,62 +307,48 @@ ApplicationCacheGroup* ApplicationCacheStorage::fallbackCacheGroupForURL(const K
         if (cache->resourceForURL(fallbackURL)->type() & ApplicationCacheResource::Foreign)
             continue;
 
-        ApplicationCacheGroup* group = new ApplicationCacheGroup(manifestURL);
-        
-        group->setStorageID(static_cast<unsigned>(statement.getColumnInt64(0)));
-        group->setNewestCache(cache.release());
-        
-        m_cachesInMemory.set(group->manifestURL(), group);
-        
-        return group;
+        auto& group = *new ApplicationCacheGroup(*this, manifestURL);
+        group.setStorageID(static_cast<unsigned>(statement.getColumnInt64(0)));
+        group.setNewestCache(cache.releaseNonNull());
+
+        m_cachesInMemory.set(group.manifestURL(), &group);
+
+        return &group;
     }
 
-    if (result != SQLResultDone)
+    if (result != SQLITE_DONE)
         LOG_ERROR("Could not load cache group, error \"%s\"", m_database.lastErrorMsg());
     
-    return 0;
+    return nullptr;
 }
 
-void ApplicationCacheStorage::cacheGroupDestroyed(ApplicationCacheGroup* group)
+void ApplicationCacheStorage::cacheGroupDestroyed(ApplicationCacheGroup& group)
 {
-    if (group->isObsolete()) {
-        ASSERT(!group->storageID());
-        ASSERT(m_cachesInMemory.get(group->manifestURL()) != group);
+    if (group.isObsolete()) {
+        ASSERT(!group.storageID());
+        ASSERT(m_cachesInMemory.get(group.manifestURL()) != &group);
         return;
     }
 
-    ASSERT(m_cachesInMemory.get(group->manifestURL()) == group);
+    ASSERT(m_cachesInMemory.get(group.manifestURL()) == &group);
 
-    m_cachesInMemory.remove(group->manifestURL());
+    m_cachesInMemory.remove(group.manifestURL());
     
     // If the cache group is half-created, we don't want it in the saved set (as it is not stored in database).
-    if (!group->storageID())
-        m_cacheHostSet.remove(urlHostHash(group->manifestURL()));
+    if (!group.storageID())
+        m_cacheHostSet.remove(urlHostHash(group.manifestURL()));
 }
 
-void ApplicationCacheStorage::cacheGroupMadeObsolete(ApplicationCacheGroup* group)
+void ApplicationCacheStorage::cacheGroupMadeObsolete(ApplicationCacheGroup& group)
 {
-    ASSERT(m_cachesInMemory.get(group->manifestURL()) == group);
-    ASSERT(m_cacheHostSet.contains(urlHostHash(group->manifestURL())));
+    ASSERT(m_cachesInMemory.get(group.manifestURL()) == &group);
+    ASSERT(m_cacheHostSet.contains(urlHostHash(group.manifestURL())));
 
-    if (ApplicationCache* newestCache = group->newestCache())
+    if (auto* newestCache = group.newestCache())
         remove(newestCache);
 
-    m_cachesInMemory.remove(group->manifestURL());
-    m_cacheHostSet.remove(urlHostHash(group->manifestURL()));
-}
-
-void ApplicationCacheStorage::setCacheDirectory(const String& cacheDirectory)
-{
-    ASSERT(m_cacheDirectory.isNull());
-    ASSERT(!cacheDirectory.isNull());
-    
-    m_cacheDirectory = cacheDirectory;
-}
-
-const String& ApplicationCacheStorage::cacheDirectory() const
-{
-    return m_cacheDirectory;
+    m_cachesInMemory.remove(group.manifestURL());
+    m_cacheHostSet.remove(urlHostHash(group.manifestURL()));
 }
 
 void ApplicationCacheStorage::setMaximumSize(int64_t size)
@@ -393,7 +370,7 @@ int64_t ApplicationCacheStorage::spaceNeeded(int64_t cacheToSave)
 {
     int64_t spaceNeeded = 0;
     long long fileSize = 0;
-    if (!getFileSize(m_cacheFile, fileSize))
+    if (!FileSystem::getFileSize(m_cacheFile, fileSize))
         return 0;
 
     int64_t currentSize = fileSize + flatFileAreaSize();
@@ -432,20 +409,22 @@ void ApplicationCacheStorage::setDefaultOriginQuota(int64_t quota)
     m_defaultOriginQuota = quota;
 }
 
-bool ApplicationCacheStorage::calculateQuotaForOrigin(const SecurityOrigin* origin, int64_t& quota)
+bool ApplicationCacheStorage::calculateQuotaForOrigin(const SecurityOrigin& origin, int64_t& quota)
 {
+    SQLiteTransactionInProgressAutoCounter transactionCounter;
+
     // If an Origin record doesn't exist, then the COUNT will be 0 and quota will be 0.
     // Using the count to determine if a record existed or not is a safe way to determine
     // if a quota of 0 is real, from the record, or from null.
     SQLiteStatement statement(m_database, "SELECT COUNT(quota), quota FROM Origins WHERE origin=?");
-    if (statement.prepare() != SQLResultOk)
+    if (statement.prepare() != SQLITE_OK)
         return false;
 
-    statement.bindText(1, origin->databaseIdentifier());
+    statement.bindText(1, origin.data().databaseIdentifier());
     int result = statement.step();
 
     // Return the quota, or if it was null the default.
-    if (result == SQLResultRow) {
+    if (result == SQLITE_ROW) {
         bool wasNoRecord = statement.getColumnInt64(0) == 0;
         quota = wasNoRecord ? m_defaultOriginQuota : statement.getColumnInt64(1);
         return true;
@@ -457,6 +436,8 @@ bool ApplicationCacheStorage::calculateQuotaForOrigin(const SecurityOrigin* orig
 
 bool ApplicationCacheStorage::calculateUsageForOrigin(const SecurityOrigin* origin, int64_t& usage)
 {
+    SQLiteTransactionInProgressAutoCounter transactionCounter;
+
     // If an Origins record doesn't exist, then the SUM will be null,
     // which will become 0, as expected, when converting to a number.
     SQLiteStatement statement(m_database, "SELECT SUM(Caches.size)"
@@ -464,13 +445,13 @@ bool ApplicationCacheStorage::calculateUsageForOrigin(const SecurityOrigin* orig
                                           " INNER JOIN Origins ON CacheGroups.origin = Origins.origin"
                                           " INNER JOIN Caches ON CacheGroups.id = Caches.cacheGroup"
                                           " WHERE Origins.origin=?");
-    if (statement.prepare() != SQLResultOk)
+    if (statement.prepare() != SQLITE_OK)
         return false;
 
-    statement.bindText(1, origin->databaseIdentifier());
+    statement.bindText(1, origin->data().databaseIdentifier());
     int result = statement.step();
 
-    if (result == SQLResultRow) {
+    if (result == SQLITE_ROW) {
         usage = statement.getColumnInt64(0);
         return true;
     }
@@ -479,8 +460,10 @@ bool ApplicationCacheStorage::calculateUsageForOrigin(const SecurityOrigin* orig
     return false;
 }
 
-bool ApplicationCacheStorage::calculateRemainingSizeForOriginExcludingCache(const SecurityOrigin* origin, ApplicationCache* cache, int64_t& remainingSize)
+bool ApplicationCacheStorage::calculateRemainingSizeForOriginExcludingCache(const SecurityOrigin& origin, ApplicationCache* cache, int64_t& remainingSize)
 {
+    SQLiteTransactionInProgressAutoCounter transactionCounter;
+
     openDatabase(false);
     if (!m_database.isOpen())
         return false;
@@ -505,17 +488,17 @@ bool ApplicationCacheStorage::calculateRemainingSizeForOriginExcludingCache(cons
     }
 
     SQLiteStatement statement(m_database, query);
-    if (statement.prepare() != SQLResultOk)
+    if (statement.prepare() != SQLITE_OK)
         return false;
 
-    statement.bindText(1, origin->databaseIdentifier());
+    statement.bindText(1, origin.data().databaseIdentifier());
     if (excludingCacheIdentifier != 0)
         statement.bindInt64(2, excludingCacheIdentifier);
     int result = statement.step();
 
     // If the count was 0 that then we have to query the origin table directly
     // for its quota. Otherwise we can use the calculated value.
-    if (result == SQLResultRow) {
+    if (result == SQLITE_ROW) {
         int64_t numberOfCaches = statement.getColumnInt64(0);
         if (numberOfCaches == 0)
             calculateQuotaForOrigin(origin, remainingSize);
@@ -530,6 +513,8 @@ bool ApplicationCacheStorage::calculateRemainingSizeForOriginExcludingCache(cons
 
 bool ApplicationCacheStorage::storeUpdatedQuotaForOrigin(const SecurityOrigin* origin, int64_t quota)
 {
+    SQLiteTransactionInProgressAutoCounter transactionCounter;
+
     openDatabase(true);
     if (!m_database.isOpen())
         return false;
@@ -538,17 +523,18 @@ bool ApplicationCacheStorage::storeUpdatedQuotaForOrigin(const SecurityOrigin* o
         return false;
 
     SQLiteStatement updateStatement(m_database, "UPDATE Origins SET quota=? WHERE origin=?");
-    if (updateStatement.prepare() != SQLResultOk)
+    if (updateStatement.prepare() != SQLITE_OK)
         return false;
 
     updateStatement.bindInt64(1, quota);
-    updateStatement.bindText(2, origin->databaseIdentifier());
+    updateStatement.bindText(2, origin->data().databaseIdentifier());
 
     return executeStatement(updateStatement);
 }
 
 bool ApplicationCacheStorage::executeSQLCommand(const String& sql)
 {
+    ASSERT(SQLiteDatabaseTracker::hasTransactionInProgress());
     ASSERT(m_database.isOpen());
     
     bool result = m_database.executeCommand(sql);
@@ -566,11 +552,15 @@ static const int schemaVersion = 7;
     
 void ApplicationCacheStorage::verifySchemaVersion()
 {
+    ASSERT(SQLiteDatabaseTracker::hasTransactionInProgress());
+
     int version = SQLiteStatement(m_database, "PRAGMA user_version").getColumnInt(0);
     if (version == schemaVersion)
         return;
 
-    deleteTables();
+    // Version will be 0 if we just created an empty file. Trying to delete tables would cause errors, because they don't exist yet.
+    if (version)
+        deleteTables();
 
     // Update user version.
     SQLiteTransaction setDatabaseVersion(m_database);
@@ -581,7 +571,7 @@ void ApplicationCacheStorage::verifySchemaVersion()
     ASSERT_UNUSED(unusedNumBytes, static_cast<int>(sizeof(userVersionSQL)) >= unusedNumBytes);
 
     SQLiteStatement statement(m_database, userVersionSQL);
-    if (statement.prepare() != SQLResultOk)
+    if (statement.prepare() != SQLITE_OK)
         return;
     
     executeStatement(statement);
@@ -590,6 +580,8 @@ void ApplicationCacheStorage::verifySchemaVersion()
     
 void ApplicationCacheStorage::openDatabase(bool createIfDoesNotExist)
 {
+    SQLiteTransactionInProgressAutoCounter transactionCounter;
+
     if (m_database.isOpen())
         return;
 
@@ -597,11 +589,11 @@ void ApplicationCacheStorage::openDatabase(bool createIfDoesNotExist)
     if (m_cacheDirectory.isNull())
         return;
 
-    m_cacheFile = pathByAppendingComponent(m_cacheDirectory, "ApplicationCache.db");
-    if (!createIfDoesNotExist && !fileExists(m_cacheFile))
+    m_cacheFile = FileSystem::pathByAppendingComponent(m_cacheDirectory, "ApplicationCache.db");
+    if (!createIfDoesNotExist && !FileSystem::fileExists(m_cacheFile))
         return;
 
-    makeAllDirectories(m_cacheDirectory);
+    FileSystem::makeAllDirectories(m_cacheDirectory);
     m_database.open(m_cacheFile);
     
     if (!m_database.isOpen())
@@ -657,6 +649,7 @@ void ApplicationCacheStorage::openDatabase(bool createIfDoesNotExist)
 
 bool ApplicationCacheStorage::executeStatement(SQLiteStatement& statement)
 {
+    ASSERT(SQLiteDatabaseTracker::hasTransactionInProgress());
     bool result = statement.executeCommand();
     if (!result)
         LOG_ERROR("Application Cache Storage: failed to execute statement \"%s\" error \"%s\"", 
@@ -667,23 +660,30 @@ bool ApplicationCacheStorage::executeStatement(SQLiteStatement& statement)
 
 bool ApplicationCacheStorage::store(ApplicationCacheGroup* group, GroupStorageIDJournal* journal)
 {
+    ASSERT(SQLiteDatabaseTracker::hasTransactionInProgress());
     ASSERT(group->storageID() == 0);
     ASSERT(journal);
 
+    // For some reason, an app cache may be partially written to disk. In particular, there may be
+    // a cache group with an identical manifest URL and associated cache entries. We want to remove
+    // this cache group and its associated cache entries so that we can create it again (below) as
+    // a way to repair it.
+    deleteCacheGroupRecord(group->manifestURL());
+
     SQLiteStatement statement(m_database, "INSERT INTO CacheGroups (manifestHostHash, manifestURL, origin) VALUES (?, ?, ?)");
-    if (statement.prepare() != SQLResultOk)
+    if (statement.prepare() != SQLITE_OK)
         return false;
 
     statement.bindInt64(1, urlHostHash(group->manifestURL()));
     statement.bindText(2, group->manifestURL());
-    statement.bindText(3, group->origin()->databaseIdentifier());
+    statement.bindText(3, group->origin().data().databaseIdentifier());
 
     if (!executeStatement(statement))
         return false;
 
     unsigned groupStorageID = static_cast<unsigned>(m_database.lastInsertRowID());
 
-    if (!ensureOriginRecord(group->origin()))
+    if (!ensureOriginRecord(&group->origin()))
         return false;
 
     group->setStorageID(groupStorageID);
@@ -693,12 +693,13 @@ bool ApplicationCacheStorage::store(ApplicationCacheGroup* group, GroupStorageID
 
 bool ApplicationCacheStorage::store(ApplicationCache* cache, ResourceStorageIDJournal* storageIDJournal)
 {
+    ASSERT(SQLiteDatabaseTracker::hasTransactionInProgress());
     ASSERT(cache->storageID() == 0);
     ASSERT(cache->group()->storageID() != 0);
     ASSERT(storageIDJournal);
     
     SQLiteStatement statement(m_database, "INSERT INTO Caches (cacheGroup, size) VALUES (?, ?)");
-    if (statement.prepare() != SQLResultOk)
+    if (statement.prepare() != SQLITE_OK)
         return false;
 
     statement.bindInt64(1, cache->group()->storageID());
@@ -710,28 +711,24 @@ bool ApplicationCacheStorage::store(ApplicationCache* cache, ResourceStorageIDJo
     unsigned cacheStorageID = static_cast<unsigned>(m_database.lastInsertRowID());
 
     // Store all resources
-    {
-        ApplicationCache::ResourceMap::const_iterator end = cache->end();
-        for (ApplicationCache::ResourceMap::const_iterator it = cache->begin(); it != end; ++it) {
-            unsigned oldStorageID = it->second->storageID();
-            if (!store(it->second.get(), cacheStorageID))
-                return false;
+    for (auto& resource : cache->resources().values()) {
+        unsigned oldStorageID = resource->storageID();
+        if (!store(resource.get(), cacheStorageID))
+            return false;
 
-            // Storing the resource succeeded. Log its old storageID in case
-            // it needs to be restored later.
-            storageIDJournal->add(it->second.get(), oldStorageID);
-        }
+        // Storing the resource succeeded. Log its old storageID in case
+        // it needs to be restored later.
+        storageIDJournal->add(resource.get(), oldStorageID);
     }
     
     // Store the online whitelist
-    const Vector<KURL>& onlineWhitelist = cache->onlineWhitelist();
+    const Vector<URL>& onlineWhitelist = cache->onlineWhitelist();
     {
-        size_t whitelistSize = onlineWhitelist.size();
-        for (size_t i = 0; i < whitelistSize; ++i) {
+        for (auto& whitelistURL : onlineWhitelist) {
             SQLiteStatement statement(m_database, "INSERT INTO CacheWhitelistURLs (url, cache) VALUES (?, ?)");
             statement.prepare();
 
-            statement.bindText(1, onlineWhitelist[i]);
+            statement.bindText(1, whitelistURL);
             statement.bindInt64(2, cacheStorageID);
 
             if (!executeStatement(statement))
@@ -754,13 +751,12 @@ bool ApplicationCacheStorage::store(ApplicationCache* cache, ResourceStorageIDJo
     // Store fallback URLs.
     const FallbackURLVector& fallbackURLs = cache->fallbackURLs();
     {
-        size_t fallbackCount = fallbackURLs.size();
-        for (size_t i = 0; i < fallbackCount; ++i) {
+        for (auto& fallbackURL : fallbackURLs) {
             SQLiteStatement statement(m_database, "INSERT INTO FallbackURLs (namespace, fallbackURL, cache) VALUES (?, ?, ?)");
             statement.prepare();
 
-            statement.bindText(1, fallbackURLs[i].first);
-            statement.bindText(2, fallbackURLs[i].second);
+            statement.bindText(1, fallbackURL.first);
+            statement.bindText(2, fallbackURL.second);
             statement.bindInt64(3, cacheStorageID);
 
             if (!executeStatement(statement))
@@ -774,6 +770,7 @@ bool ApplicationCacheStorage::store(ApplicationCache* cache, ResourceStorageIDJo
 
 bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, unsigned cacheStorageID)
 {
+    ASSERT(SQLiteDatabaseTracker::hasTransactionInProgress());
     ASSERT(cacheStorageID);
     ASSERT(!resource->storageID());
     
@@ -785,23 +782,23 @@ bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, unsigned
 
     // First, insert the data
     SQLiteStatement dataStatement(m_database, "INSERT INTO CacheResourceData (data, path) VALUES (?, ?)");
-    if (dataStatement.prepare() != SQLResultOk)
+    if (dataStatement.prepare() != SQLITE_OK)
         return false;
     
 
     String fullPath;
     if (!resource->path().isEmpty())
-        dataStatement.bindText(2, pathGetFileName(resource->path()));
+        dataStatement.bindText(2, FileSystem::pathGetFileName(resource->path()));
     else if (shouldStoreResourceAsFlatFile(resource)) {
         // First, check to see if creating the flat file would violate the maximum total quota. We don't need
         // to check the per-origin quota here, as it was already checked in storeNewestCache().
-        if (m_database.totalSize() + flatFileAreaSize() + resource->data()->size() > m_maximumSize) {
+        if (m_database.totalSize() + flatFileAreaSize() + static_cast<int64_t>(resource->data().size()) > m_maximumSize) {
             m_isMaximumSizeReached = true;
             return false;
         }
         
-        String flatFileDirectory = pathByAppendingComponent(m_cacheDirectory, flatFileSubdirectory);
-        makeAllDirectories(flatFileDirectory);
+        String flatFileDirectory = FileSystem::pathByAppendingComponent(m_cacheDirectory, m_flatFileSubdirectoryName);
+        FileSystem::makeAllDirectories(flatFileDirectory);
 
         String extension;
         
@@ -814,18 +811,18 @@ bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, unsigned
         if (!writeDataToUniqueFileInDirectory(resource->data(), flatFileDirectory, path, extension))
             return false;
         
-        fullPath = pathByAppendingComponent(flatFileDirectory, path);
+        fullPath = FileSystem::pathByAppendingComponent(flatFileDirectory, path);
         resource->setPath(fullPath);
         dataStatement.bindText(2, path);
     } else {
-        if (resource->data()->size())
-            dataStatement.bindBlob(1, resource->data()->data(), resource->data()->size());
+        if (resource->data().size())
+            dataStatement.bindBlob(1, resource->data().data(), resource->data().size());
     }
     
     if (!dataStatement.executeCommand()) {
         // Clean up the file which we may have written to:
         if (!fullPath.isEmpty())
-            deleteFile(fullPath);
+            FileSystem::deleteFile(fullPath);
 
         return false;
     }
@@ -837,18 +834,17 @@ bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, unsigned
     // Serialize the headers
     StringBuilder stringBuilder;
     
-    HTTPHeaderMap::const_iterator end = resource->response().httpHeaderFields().end();
-    for (HTTPHeaderMap::const_iterator it = resource->response().httpHeaderFields().begin(); it!= end; ++it) {
-        stringBuilder.append(it->first);
-        stringBuilder.append((UChar)':');
-        stringBuilder.append(it->second);
-        stringBuilder.append((UChar)'\n');
+    for (const auto& header : resource->response().httpHeaderFields()) {
+        stringBuilder.append(header.key);
+        stringBuilder.append(':');
+        stringBuilder.append(header.value);
+        stringBuilder.append('\n');
     }
     
     String headers = stringBuilder.toString();
     
     SQLiteStatement resourceStatement(m_database, "INSERT INTO CacheResources (url, statusCode, responseURL, headers, data, mimeType, textEncodingName) VALUES (?, ?, ?, ?, ?, ?, ?)");
-    if (resourceStatement.prepare() != SQLResultOk)
+    if (resourceStatement.prepare() != SQLITE_OK)
         return false;
     
     // The same ApplicationCacheResource are used in ApplicationCacheResource::size()
@@ -869,7 +865,7 @@ bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, unsigned
     
     // Finally, insert the cache entry
     SQLiteStatement entryStatement(m_database, "INSERT INTO CacheEntries (cache, type, resource) VALUES (?, ?, ?)");
-    if (entryStatement.prepare() != SQLResultOk)
+    if (entryStatement.prepare() != SQLITE_OK)
         return false;
     
     entryStatement.bindInt64(1, cacheStorageID);
@@ -883,7 +879,7 @@ bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, unsigned
     // release the resource's data and free up a potentially large amount
     // of memory:
     if (!fullPath.isEmpty())
-        resource->data()->clear();
+        resource->data().clear();
 
     resource->setStorageID(resourceId);
     return true;
@@ -891,12 +887,14 @@ bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, unsigned
 
 bool ApplicationCacheStorage::storeUpdatedType(ApplicationCacheResource* resource, ApplicationCache* cache)
 {
+    SQLiteTransactionInProgressAutoCounter transactionCounter;
+
     ASSERT_UNUSED(cache, cache->storageID());
     ASSERT(resource->storageID());
 
     // First, insert the data
     SQLiteStatement entryStatement(m_database, "UPDATE CacheEntries SET type=? WHERE resource=?");
-    if (entryStatement.prepare() != SQLResultOk)
+    if (entryStatement.prepare() != SQLITE_OK)
         return false;
 
     entryStatement.bindInt64(1, resource->type());
@@ -907,6 +905,8 @@ bool ApplicationCacheStorage::storeUpdatedType(ApplicationCacheResource* resourc
 
 bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, ApplicationCache* cache)
 {
+    SQLiteTransactionInProgressAutoCounter transactionCounter;
+
     ASSERT(cache->storageID());
     
     openDatabase(true);
@@ -927,7 +927,7 @@ bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, Applicat
 
     // A resource was added to the cache. Update the total data size for the cache.
     SQLiteStatement sizeUpdateStatement(m_database, "UPDATE Caches SET size=size+? WHERE id=?");
-    if (sizeUpdateStatement.prepare() != SQLResultOk)
+    if (sizeUpdateStatement.prepare() != SQLITE_OK)
         return false;
 
     sizeUpdateStatement.bindInt64(1, resource->estimatedSizeInStorage());
@@ -942,11 +942,12 @@ bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, Applicat
 
 bool ApplicationCacheStorage::ensureOriginRecord(const SecurityOrigin* origin)
 {
+    ASSERT(SQLiteDatabaseTracker::hasTransactionInProgress());
     SQLiteStatement insertOriginStatement(m_database, "INSERT INTO Origins (origin, quota) VALUES (?, ?)");
-    if (insertOriginStatement.prepare() != SQLResultOk)
+    if (insertOriginStatement.prepare() != SQLITE_OK)
         return false;
 
-    insertOriginStatement.bindText(1, origin->databaseIdentifier());
+    insertOriginStatement.bindText(1, origin->data().databaseIdentifier());
     insertOriginStatement.bindInt64(2, m_defaultOriginQuota);
     if (!executeStatement(insertOriginStatement))
         return false;
@@ -958,7 +959,7 @@ bool ApplicationCacheStorage::checkOriginQuota(ApplicationCacheGroup* group, App
 {
     // Check if the oldCache with the newCache would reach the per-origin quota.
     int64_t remainingSpaceInOrigin;
-    const SecurityOrigin* origin = group->origin();
+    auto& origin = group->origin();
     if (calculateRemainingSizeForOriginExcludingCache(origin, oldCache, remainingSpaceInOrigin)) {
         if (remainingSpaceInOrigin < newCache->estimatedSizeInStorage()) {
             int64_t quota;
@@ -976,7 +977,7 @@ bool ApplicationCacheStorage::checkOriginQuota(ApplicationCacheGroup* group, App
     return true;
 }
 
-bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup* group, ApplicationCache* oldCache, FailureReason& failureReason)
+bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup& group, ApplicationCache* oldCache, FailureReason& failureReason)
 {
     openDatabase(true);
 
@@ -992,24 +993,24 @@ bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup* group, App
 
     // Check if this would reach the per-origin quota.
     int64_t totalSpaceNeededIgnored;
-    if (!checkOriginQuota(group, oldCache, group->newestCache(), totalSpaceNeededIgnored)) {
+    if (!checkOriginQuota(&group, oldCache, group.newestCache(), totalSpaceNeededIgnored)) {
         failureReason = OriginQuotaReached;
         return false;
     }
 
     GroupStorageIDJournal groupStorageIDJournal;
-    if (!group->storageID()) {
+    if (!group.storageID()) {
         // Store the group
-        if (!store(group, &groupStorageIDJournal)) {
+        if (!store(&group, &groupStorageIDJournal)) {
             checkForMaxSizeReached();
             failureReason = isMaximumSizeReached() ? TotalQuotaReached : DiskOrOperationFailure;
             return false;
         }
     }
     
-    ASSERT(group->newestCache());
-    ASSERT(!group->isObsolete());
-    ASSERT(!group->newestCache()->storageID());
+    ASSERT(group.newestCache());
+    ASSERT(!group.isObsolete());
+    ASSERT(!group.newestCache()->storageID());
     
     // Log the storageID changes to the in-memory resource objects. The journal
     // object will roll them back automatically in case a database operation
@@ -1017,7 +1018,7 @@ bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup* group, App
     ResourceStorageIDJournal resourceStorageIDJournal;
 
     // Store the newest cache
-    if (!store(group->newestCache(), &resourceStorageIDJournal)) {
+    if (!store(group.newestCache(), &resourceStorageIDJournal)) {
         checkForMaxSizeReached();
         failureReason = isMaximumSizeReached() ? TotalQuotaReached : DiskOrOperationFailure;
         return false;
@@ -1026,13 +1027,13 @@ bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup* group, App
     // Update the newest cache in the group.
     
     SQLiteStatement statement(m_database, "UPDATE CacheGroups SET newestCache=? WHERE id=?");
-    if (statement.prepare() != SQLResultOk) {
+    if (statement.prepare() != SQLITE_OK) {
         failureReason = DiskOrOperationFailure;
         return false;
     }
     
-    statement.bindInt64(1, group->newestCache()->storageID());
-    statement.bindInt64(2, group->storageID());
+    statement.bindInt64(1, group.newestCache()->storageID());
+    statement.bindInt64(2, group.storageID());
     
     if (!executeStatement(statement)) {
         failureReason = DiskOrOperationFailure;
@@ -1045,21 +1046,24 @@ bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup* group, App
     return true;
 }
 
-bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup* group)
+bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup& group)
 {
     // Ignore the reason for failing, just attempt the store.
     FailureReason ignoredFailureReason;
-    return storeNewestCache(group, 0, ignoredFailureReason);
+    return storeNewestCache(group, nullptr, ignoredFailureReason);
 }
 
-static inline void parseHeader(const UChar* header, size_t headerLength, ResourceResponse& response)
+template<typename CharacterType>
+static inline void parseHeader(const CharacterType* header, unsigned headerLength, ResourceResponse& response)
 {
-    size_t pos = find(header, headerLength, ':');
-    ASSERT(pos != notFound);
-    
-    AtomicString headerName = AtomicString(header, pos);
-    String headerValue = String(header + pos + 1, headerLength - pos - 1);
-    
+    ASSERT(find(header, headerLength, ':') != notFound);
+    unsigned colonPosition = find(header, headerLength, ':');
+
+    // Save memory by putting the header names into atomic strings so each is stored only once,
+    // even though the setHTTPHeaderField function does not require an atomic string.
+    AtomString headerName { header, colonPosition };
+    String headerValue { header + colonPosition + 1, headerLength - colonPosition - 1 };
+
     response.setHTTPHeaderField(headerName, headerValue);
 }
 
@@ -1070,34 +1074,42 @@ static inline void parseHeaders(const String& headers, ResourceResponse& respons
     while ((endPos = headers.find('\n', startPos)) != notFound) {
         ASSERT(startPos != endPos);
 
-        parseHeader(headers.characters() + startPos, endPos - startPos, response);
+        if (headers.is8Bit())
+            parseHeader(headers.characters8() + startPos, endPos - startPos, response);
+        else
+            parseHeader(headers.characters16() + startPos, endPos - startPos, response);
         
         startPos = endPos + 1;
     }
     
-    if (startPos != headers.length())
-        parseHeader(headers.characters(), headers.length(), response);
+    if (startPos != headers.length()) {
+        if (headers.is8Bit())
+            parseHeader(headers.characters8(), headers.length(), response);
+        else
+            parseHeader(headers.characters16(), headers.length(), response);
+    }
 }
     
-PassRefPtr<ApplicationCache> ApplicationCacheStorage::loadCache(unsigned storageID)
+RefPtr<ApplicationCache> ApplicationCacheStorage::loadCache(unsigned storageID)
 {
-    SQLiteStatement cacheStatement(m_database, 
+    ASSERT(SQLiteDatabaseTracker::hasTransactionInProgress());
+    SQLiteStatement cacheStatement(m_database,
                                    "SELECT url, statusCode, type, mimeType, textEncodingName, headers, CacheResourceData.data, CacheResourceData.path FROM CacheEntries INNER JOIN CacheResources ON CacheEntries.resource=CacheResources.id "
                                    "INNER JOIN CacheResourceData ON CacheResourceData.id=CacheResources.data WHERE CacheEntries.cache=?");
-    if (cacheStatement.prepare() != SQLResultOk) {
+    if (cacheStatement.prepare() != SQLITE_OK) {
         LOG_ERROR("Could not prepare cache statement, error \"%s\"", m_database.lastErrorMsg());
-        return 0;
+        return nullptr;
     }
     
     cacheStatement.bindInt64(1, storageID);
 
-    RefPtr<ApplicationCache> cache = ApplicationCache::create();
+    auto cache = ApplicationCache::create();
 
-    String flatFileDirectory = pathByAppendingComponent(m_cacheDirectory, flatFileSubdirectory);
+    String flatFileDirectory = FileSystem::pathByAppendingComponent(m_cacheDirectory, m_flatFileSubdirectoryName);
 
     int result;
-    while ((result = cacheStatement.step()) == SQLResultRow) {
-        KURL url(ParsedURLString, cacheStatement.getColumnText(0));
+    while ((result = cacheStatement.step()) == SQLITE_ROW) {
+        URL url({ }, cacheStatement.getColumnText(0));
         
         int httpStatusCode = cacheStatement.getColumnInt(1);
 
@@ -1106,89 +1118,96 @@ PassRefPtr<ApplicationCache> ApplicationCacheStorage::loadCache(unsigned storage
         Vector<char> blob;
         cacheStatement.getColumnBlobAsVector(6, blob);
         
-        RefPtr<SharedBuffer> data = SharedBuffer::adoptVector(blob);
+        auto data = SharedBuffer::create(WTFMove(blob));
         
         String path = cacheStatement.getColumnText(7);
         long long size = 0;
         if (path.isEmpty())
             size = data->size();
         else {
-            path = pathByAppendingComponent(flatFileDirectory, path);
-            getFileSize(path, size);
+            path = FileSystem::pathByAppendingComponent(flatFileDirectory, path);
+            FileSystem::getFileSize(path, size);
         }
         
         String mimeType = cacheStatement.getColumnText(3);
         String textEncodingName = cacheStatement.getColumnText(4);
         
-        ResourceResponse response(url, mimeType, size, textEncodingName, "");
+        ResourceResponse response(url, mimeType, size, textEncodingName);
         response.setHTTPStatusCode(httpStatusCode);
 
         String headers = cacheStatement.getColumnText(5);
         parseHeaders(headers, response);
         
-        RefPtr<ApplicationCacheResource> resource = ApplicationCacheResource::create(url, response, type, data.release(), path);
+        auto resource = ApplicationCacheResource::create(url, response, type, WTFMove(data), path);
 
         if (type & ApplicationCacheResource::Manifest)
-            cache->setManifestResource(resource.release());
+            cache->setManifestResource(WTFMove(resource));
         else
-            cache->addResource(resource.release());
+            cache->addResource(WTFMove(resource));
     }
 
-    if (result != SQLResultDone)
+    if (result != SQLITE_DONE)
         LOG_ERROR("Could not load cache resources, error \"%s\"", m_database.lastErrorMsg());
-    
+
+    if (!cache->manifestResource()) {
+        LOG_ERROR("Could not load application cache because there was no manifest resource");
+        return nullptr;
+    }
+
     // Load the online whitelist
     SQLiteStatement whitelistStatement(m_database, "SELECT url FROM CacheWhitelistURLs WHERE cache=?");
-    if (whitelistStatement.prepare() != SQLResultOk)
-        return 0;
+    if (whitelistStatement.prepare() != SQLITE_OK)
+        return nullptr;
     whitelistStatement.bindInt64(1, storageID);
     
-    Vector<KURL> whitelist;
-    while ((result = whitelistStatement.step()) == SQLResultRow
-        whitelist.append(KURL(ParsedURLString, whitelistStatement.getColumnText(0)));
+    Vector<URL> whitelist;
+    while ((result = whitelistStatement.step()) == SQLITE_ROW
+        whitelist.append(URL({ }, whitelistStatement.getColumnText(0)));
 
-    if (result != SQLResultDone)
+    if (result != SQLITE_DONE)
         LOG_ERROR("Could not load cache online whitelist, error \"%s\"", m_database.lastErrorMsg());
 
     cache->setOnlineWhitelist(whitelist);
 
     // Load online whitelist wildcard flag.
     SQLiteStatement whitelistWildcardStatement(m_database, "SELECT wildcard FROM CacheAllowsAllNetworkRequests WHERE cache=?");
-    if (whitelistWildcardStatement.prepare() != SQLResultOk)
-        return 0;
+    if (whitelistWildcardStatement.prepare() != SQLITE_OK)
+        return nullptr;
     whitelistWildcardStatement.bindInt64(1, storageID);
     
     result = whitelistWildcardStatement.step();
-    if (result != SQLResultRow)
+    if (result != SQLITE_ROW)
         LOG_ERROR("Could not load cache online whitelist wildcard flag, error \"%s\"", m_database.lastErrorMsg());
 
     cache->setAllowsAllNetworkRequests(whitelistWildcardStatement.getColumnInt64(0));
 
-    if (whitelistWildcardStatement.step() != SQLResultDone)
+    if (whitelistWildcardStatement.step() != SQLITE_DONE)
         LOG_ERROR("Too many rows for online whitelist wildcard flag");
 
     // Load fallback URLs.
     SQLiteStatement fallbackStatement(m_database, "SELECT namespace, fallbackURL FROM FallbackURLs WHERE cache=?");
-    if (fallbackStatement.prepare() != SQLResultOk)
-        return 0;
+    if (fallbackStatement.prepare() != SQLITE_OK)
+        return nullptr;
     fallbackStatement.bindInt64(1, storageID);
     
     FallbackURLVector fallbackURLs;
-    while ((result = fallbackStatement.step()) == SQLResultRow
-        fallbackURLs.append(make_pair(KURL(ParsedURLString, fallbackStatement.getColumnText(0)), KURL(ParsedURLString, fallbackStatement.getColumnText(1))));
+    while ((result = fallbackStatement.step()) == SQLITE_ROW
+        fallbackURLs.append(std::make_pair(URL({ }, fallbackStatement.getColumnText(0)), URL({ }, fallbackStatement.getColumnText(1))));
 
-    if (result != SQLResultDone)
+    if (result != SQLITE_DONE)
         LOG_ERROR("Could not load fallback URLs, error \"%s\"", m_database.lastErrorMsg());
 
     cache->setFallbackURLs(fallbackURLs);
     
     cache->setStorageID(storageID);
 
-    return cache.release();
+    return cache;
 }    
     
 void ApplicationCacheStorage::remove(ApplicationCache* cache)
 {
+    SQLiteTransactionInProgressAutoCounter transactionCounter;
+
     if (!cache->storageID())
         return;
     
@@ -1201,7 +1220,7 @@ void ApplicationCacheStorage::remove(ApplicationCache* cache)
 
     // All associated data will be deleted by database triggers.
     SQLiteStatement statement(m_database, "DELETE FROM Caches WHERE id=?");
-    if (statement.prepare() != SQLResultOk)
+    if (statement.prepare() != SQLITE_OK)
         return;
     
     statement.bindInt64(1, cache->storageID());
@@ -1212,7 +1231,7 @@ void ApplicationCacheStorage::remove(ApplicationCache* cache)
     if (cache->group()->newestCache() == cache) {
         // Currently, there are no triggers on the cache group, which is why the cache had to be removed separately above.
         SQLiteStatement groupStatement(m_database, "DELETE FROM CacheGroups WHERE id=?");
-        if (groupStatement.prepare() != SQLResultOk)
+        if (groupStatement.prepare() != SQLITE_OK)
             return;
         
         groupStatement.bindInt64(1, cache->group()->storageID());
@@ -1226,6 +1245,8 @@ void ApplicationCacheStorage::remove(ApplicationCache* cache)
 
 void ApplicationCacheStorage::empty()
 {
+    SQLiteTransactionInProgressAutoCounter transactionCounter;
+
     openDatabase(false);
     
     if (!m_database.isOpen())
@@ -1239,10 +1260,9 @@ void ApplicationCacheStorage::empty()
     // Clear the storage IDs for the caches in memory.
     // The caches will still work, but cached resources will not be saved to disk 
     // until a cache update process has been initiated.
-    CacheGroupMap::const_iterator end = m_cachesInMemory.end();
-    for (CacheGroupMap::const_iterator it = m_cachesInMemory.begin(); it != end; ++it)
-        it->second->clearStorageID();
-    
+    for (auto* group : m_cachesInMemory.values())
+        group->clearStorageID();
+
     checkForDeletedResources();
 }
     
@@ -1254,173 +1274,121 @@ void ApplicationCacheStorage::deleteTables()
     
 bool ApplicationCacheStorage::shouldStoreResourceAsFlatFile(ApplicationCacheResource* resource)
 {
-    return resource->response().mimeType().startsWith("audio/", false) 
-        || resource->response().mimeType().startsWith("video/", false);
+    auto& type = resource->response().mimeType();
+    return startsWithLettersIgnoringASCIICase(type, "audio/") || startsWithLettersIgnoringASCIICase(type, "video/");
 }
     
-bool ApplicationCacheStorage::writeDataToUniqueFileInDirectory(SharedBuffer* data, const String& directory, String& path, const String& fileExtension)
+bool ApplicationCacheStorage::writeDataToUniqueFileInDirectory(SharedBuffer& data, const String& directory, String& path, const String& fileExtension)
 {
     String fullPath;
     
     do {
-        path = encodeForFileName(createCanonicalUUIDString()) + fileExtension;
+        path = FileSystem::encodeForFileName(createCanonicalUUIDString()) + fileExtension;
         // Guard against the above function being called on a platform which does not implement
         // createCanonicalUUIDString().
         ASSERT(!path.isEmpty());
         if (path.isEmpty())
             return false;
         
-        fullPath = pathByAppendingComponent(directory, path);
-    } while (directoryName(fullPath) != directory || fileExists(fullPath));
+        fullPath = FileSystem::pathByAppendingComponent(directory, path);
+    } while (FileSystem::directoryName(fullPath) != directory || FileSystem::fileExists(fullPath));
     
-    PlatformFileHandle handle = openFile(fullPath, OpenForWrite);
+    FileSystem::PlatformFileHandle handle = FileSystem::openFile(fullPath, FileSystem::FileOpenMode::Write);
     if (!handle)
         return false;
     
-    int64_t writtenBytes = writeToFile(handle, data->data(), data->size());
-    closeFile(handle);
+    int64_t writtenBytes = FileSystem::writeToFile(handle, data.data(), data.size());
+    FileSystem::closeFile(handle);
     
-    if (writtenBytes != static_cast<int64_t>(data->size())) {
-        deleteFile(fullPath);
+    if (writtenBytes != static_cast<int64_t>(data.size())) {
+        FileSystem::deleteFile(fullPath);
         return false;
     }
     
     return true;
 }
 
-bool ApplicationCacheStorage::storeCopyOfCache(const String& cacheDirectory, ApplicationCacheHost* cacheHost)
+Optional<Vector<URL>> ApplicationCacheStorage::manifestURLs()
 {
-    ApplicationCache* cache = cacheHost->applicationCache();
-    if (!cache)
-        return true;
-
-    // Create a new cache.
-    RefPtr<ApplicationCache> cacheCopy = ApplicationCache::create();
-
-    cacheCopy->setOnlineWhitelist(cache->onlineWhitelist());
-    cacheCopy->setFallbackURLs(cache->fallbackURLs());
-
-    // Traverse the cache and add copies of all resources.
-    ApplicationCache::ResourceMap::const_iterator end = cache->end();
-    for (ApplicationCache::ResourceMap::const_iterator it = cache->begin(); it != end; ++it) {
-        ApplicationCacheResource* resource = it->second.get();
-        
-        RefPtr<ApplicationCacheResource> resourceCopy = ApplicationCacheResource::create(resource->url(), resource->response(), resource->type(), resource->data(), resource->path());
-        
-        cacheCopy->addResource(resourceCopy.release());
-    }
-    
-    // Now create a new cache group.
-    OwnPtr<ApplicationCacheGroup> groupCopy(adoptPtr(new ApplicationCacheGroup(cache->group()->manifestURL(), true)));
-    
-    groupCopy->setNewestCache(cacheCopy);
-    
-    ApplicationCacheStorage copyStorage;
-    copyStorage.setCacheDirectory(cacheDirectory);
-    
-    // Empty the cache in case something was there before.
-    copyStorage.empty();
-    
-    return copyStorage.storeNewestCache(groupCopy.get());
-}
+    SQLiteTransactionInProgressAutoCounter transactionCounter;
 
-bool ApplicationCacheStorage::manifestURLs(Vector<KURL>* urls)
-{
-    ASSERT(urls);
     openDatabase(false);
     if (!m_database.isOpen())
-        return false;
+        return WTF::nullopt;
 
     SQLiteStatement selectURLs(m_database, "SELECT manifestURL FROM CacheGroups");
 
-    if (selectURLs.prepare() != SQLResultOk)
-        return false;
+    if (selectURLs.prepare() != SQLITE_OK)
+        return WTF::nullopt;
 
-    while (selectURLs.step() == SQLResultRow)
-        urls->append(KURL(ParsedURLString, selectURLs.getColumnText(0)));
+    Vector<URL> urls;
+    while (selectURLs.step() == SQLITE_ROW)
+        urls.append(URL({ }, selectURLs.getColumnText(0)));
 
-    return true;
+    return urls;
 }
 
-bool ApplicationCacheStorage::cacheGroupSize(const String& manifestURL, int64_t* size)
+bool ApplicationCacheStorage::deleteCacheGroupRecord(const String& manifestURL)
 {
-    ASSERT(size);
-    openDatabase(false);
-    if (!m_database.isOpen())
+    ASSERT(SQLiteDatabaseTracker::hasTransactionInProgress());
+    SQLiteStatement idStatement(m_database, "SELECT id FROM CacheGroups WHERE manifestURL=?");
+    if (idStatement.prepare() != SQLITE_OK)
         return false;
 
-    SQLiteStatement statement(m_database, "SELECT sum(Caches.size) FROM Caches INNER JOIN CacheGroups ON Caches.cacheGroup=CacheGroups.id WHERE CacheGroups.manifestURL=?");
-    if (statement.prepare() != SQLResultOk)
+    idStatement.bindText(1, manifestURL);
+
+    int result = idStatement.step();
+    if (result != SQLITE_ROW)
         return false;
 
-    statement.bindText(1, manifestURL);
+    int64_t groupId = idStatement.getColumnInt64(0);
 
-    int result = statement.step();
-    if (result == SQLResultDone)
+    SQLiteStatement cacheStatement(m_database, "DELETE FROM Caches WHERE cacheGroup=?");
+    if (cacheStatement.prepare() != SQLITE_OK)
         return false;
 
-    if (result != SQLResultRow) {
-        LOG_ERROR("Could not get the size of the cache group, error \"%s\"", m_database.lastErrorMsg());
+    SQLiteStatement groupStatement(m_database, "DELETE FROM CacheGroups WHERE id=?");
+    if (groupStatement.prepare() != SQLITE_OK)
         return false;
-    }
 
-    *size = statement.getColumnInt64(0);
+    cacheStatement.bindInt64(1, groupId);
+    executeStatement(cacheStatement);
+    groupStatement.bindInt64(1, groupId);
+    executeStatement(groupStatement);
     return true;
 }
 
 bool ApplicationCacheStorage::deleteCacheGroup(const String& manifestURL)
 {
+    SQLiteTransactionInProgressAutoCounter transactionCounter;
+
     SQLiteTransaction deleteTransaction(m_database);
+
     // Check to see if the group is in memory.
-    ApplicationCacheGroup* group = m_cachesInMemory.get(manifestURL);
-    if (group)
-        cacheGroupMadeObsolete(group);
+    if (auto* group = m_cachesInMemory.get(manifestURL))
+        cacheGroupMadeObsolete(*group);
     else {
         // The cache group is not in memory, so remove it from the disk.
         openDatabase(false);
         if (!m_database.isOpen())
             return false;
-
-        SQLiteStatement idStatement(m_database, "SELECT id FROM CacheGroups WHERE manifestURL=?");
-        if (idStatement.prepare() != SQLResultOk)
-            return false;
-
-        idStatement.bindText(1, manifestURL);
-
-        int result = idStatement.step();
-        if (result == SQLResultDone)
-            return false;
-
-        if (result != SQLResultRow) {
-            LOG_ERROR("Could not load cache group id, error \"%s\"", m_database.lastErrorMsg());
+        if (!deleteCacheGroupRecord(manifestURL)) {
+            LOG_ERROR("Could not delete cache group record, error \"%s\"", m_database.lastErrorMsg());
             return false;
         }
-
-        int64_t groupId = idStatement.getColumnInt64(0);
-
-        SQLiteStatement cacheStatement(m_database, "DELETE FROM Caches WHERE cacheGroup=?");
-        if (cacheStatement.prepare() != SQLResultOk)
-            return false;
-
-        SQLiteStatement groupStatement(m_database, "DELETE FROM CacheGroups WHERE id=?");
-        if (groupStatement.prepare() != SQLResultOk)
-            return false;
-
-        cacheStatement.bindInt64(1, groupId);
-        executeStatement(cacheStatement);
-        groupStatement.bindInt64(1, groupId);
-        executeStatement(groupStatement);
     }
 
     deleteTransaction.commit();
-    
+
     checkForDeletedResources();
-    
+
     return true;
 }
 
 void ApplicationCacheStorage::vacuumDatabaseFile()
 {
+    SQLiteTransactionInProgressAutoCounter transactionCounter;
+
     openDatabase(false);
     if (!m_database.isOpen())
         return;
@@ -1430,7 +1398,7 @@ void ApplicationCacheStorage::vacuumDatabaseFile()
 
 void ApplicationCacheStorage::checkForMaxSizeReached()
 {
-    if (m_database.lastError() == SQLResultFull)
+    if (m_database.lastError() == SQLITE_FULL)
         m_isMaximumSizeReached = true;
 }
     
@@ -1447,10 +1415,10 @@ void ApplicationCacheStorage::checkForDeletedResources()
         "ON DeletedCacheResources.path = CacheResourceData.path "
         "WHERE (SELECT DeletedCacheResources.path == CacheResourceData.path) IS NULL");
     
-    if (selectPaths.prepare() != SQLResultOk)
+    if (selectPaths.prepare() != SQLITE_OK)
         return;
     
-    if (selectPaths.step() != SQLResultRow)
+    if (selectPaths.step() != SQLITE_ROW)
         return;
     
     do {
@@ -1458,16 +1426,16 @@ void ApplicationCacheStorage::checkForDeletedResources()
         if (path.isEmpty())
             continue;
         
-        String flatFileDirectory = pathByAppendingComponent(m_cacheDirectory, flatFileSubdirectory);
-        String fullPath = pathByAppendingComponent(flatFileDirectory, path);
+        String flatFileDirectory = FileSystem::pathByAppendingComponent(m_cacheDirectory, m_flatFileSubdirectoryName);
+        String fullPath = FileSystem::pathByAppendingComponent(flatFileDirectory, path);
         
         // Don't exit the flatFileDirectory! This should only happen if the "path" entry contains a directory 
         // component, but protect against it regardless.
-        if (directoryName(fullPath) != flatFileDirectory)
+        if (FileSystem::directoryName(fullPath) != flatFileDirectory)
             continue;
         
-        deleteFile(fullPath);
-    } while (selectPaths.step() == SQLResultRow);
+        FileSystem::deleteFile(fullPath);
+    } while (selectPaths.step() == SQLITE_ROW);
     
     executeSQLCommand("DELETE FROM DeletedCacheResources");
 }
@@ -1480,18 +1448,18 @@ long long ApplicationCacheStorage::flatFileAreaSize()
     
     SQLiteStatement selectPaths(m_database, "SELECT path FROM CacheResourceData WHERE path NOT NULL");
 
-    if (selectPaths.prepare() != SQLResultOk) {
+    if (selectPaths.prepare() != SQLITE_OK) {
         LOG_ERROR("Could not load flat file cache resource data, error \"%s\"", m_database.lastErrorMsg());
         return 0;
     }
 
     long long totalSize = 0;
-    String flatFileDirectory = pathByAppendingComponent(m_cacheDirectory, flatFileSubdirectory);
-    while (selectPaths.step() == SQLResultRow) {
+    String flatFileDirectory = FileSystem::pathByAppendingComponent(m_cacheDirectory, m_flatFileSubdirectoryName);
+    while (selectPaths.step() == SQLITE_ROW) {
         String path = selectPaths.getColumnText(0);
-        String fullPath = pathByAppendingComponent(flatFileDirectory, path);
+        String fullPath = FileSystem::pathByAppendingComponent(flatFileDirectory, path);
         long long pathSize = 0;
-        if (!getFileSize(fullPath, pathSize))
+        if (!FileSystem::getFileSize(fullPath, pathSize))
             continue;
         totalSize += pathSize;
     }
@@ -1499,21 +1467,19 @@ long long ApplicationCacheStorage::flatFileAreaSize()
     return totalSize;
 }
 
-void ApplicationCacheStorage::getOriginsWithCache(HashSet<RefPtr<SecurityOrigin>, SecurityOriginHash>& origins)
+Vector<Ref<SecurityOrigin>> ApplicationCacheStorage::originsWithCache()
 {
-    Vector<KURL> urls;
-    if (!manifestURLs(&urls)) {
-        LOG_ERROR("Failed to retrieve ApplicationCache manifest URLs");
-        return;
-    }
+    auto urls = manifestURLs();
+    if (!urls)
+        return { };
 
     // Multiple manifest URLs might share the same SecurityOrigin, so we might be creating extra, wasted origins here.
     // The current schema doesn't allow for a more efficient way of building this list.
-    size_t count = urls.size();
-    for (size_t i = 0; i < count; ++i) {
-        RefPtr<SecurityOrigin> origin = SecurityOrigin::create(urls[i]);
-        origins.add(origin);
-    }
+    Vector<Ref<SecurityOrigin>> origins;
+    origins.reserveInitialCapacity(urls->size());
+    for (auto& url : *urls)
+        origins.uncheckedAppend(SecurityOrigin::create(url));
+    return origins;
 }
 
 void ApplicationCacheStorage::deleteAllEntries()
@@ -1522,18 +1488,47 @@ void ApplicationCacheStorage::deleteAllEntries()
     vacuumDatabaseFile();
 }
 
-ApplicationCacheStorage::ApplicationCacheStorage() 
-    : m_maximumSize(ApplicationCacheStorage::noQuota())
-    , m_isMaximumSizeReached(false)
-    , m_defaultOriginQuota(ApplicationCacheStorage::noQuota())
+void ApplicationCacheStorage::deleteAllCaches()
 {
+    auto origins = originsWithCache();
+    for (auto& origin : origins)
+        deleteCacheForOrigin(origin);
+
+    vacuumDatabaseFile();
 }
 
-ApplicationCacheStorage& cacheStorage()
+void ApplicationCacheStorage::deleteCacheForOrigin(const SecurityOrigin& securityOrigin)
+{
+    auto urls = manifestURLs();
+    if (!urls) {
+        LOG_ERROR("Failed to retrieve ApplicationCache manifest URLs");
+        return;
+    }
+
+    URL originURL(URL(), securityOrigin.toString());
+
+    for (const auto& url : *urls) {
+        if (!protocolHostAndPortAreEqual(url, originURL))
+            continue;
+
+        if (auto* group = findInMemoryCacheGroup(url))
+            group->makeObsolete();
+        else
+            deleteCacheGroup(url);
+    }
+}
+
+int64_t ApplicationCacheStorage::diskUsageForOrigin(const SecurityOrigin& securityOrigin)
+{
+    int64_t usage = 0;
+    calculateUsageForOrigin(&securityOrigin, usage);
+    return usage;
+}
+
+ApplicationCacheStorage::ApplicationCacheStorage(const String& cacheDirectory, const String& flatFileSubdirectoryName)
+    : m_cacheDirectory(cacheDirectory)
+    , m_flatFileSubdirectoryName(flatFileSubdirectoryName)
 {
-    DEFINE_STATIC_LOCAL(ApplicationCacheStorage, storage, ());
-    
-    return storage;
 }
 
 } // namespace WebCore