summaryrefslogtreecommitdiffstats
path: root/WebCore/loader/appcache/ApplicationCacheStorage.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/loader/appcache/ApplicationCacheStorage.cpp')
-rw-r--r--WebCore/loader/appcache/ApplicationCacheStorage.cpp271
1 files changed, 225 insertions, 46 deletions
diff --git a/WebCore/loader/appcache/ApplicationCacheStorage.cpp b/WebCore/loader/appcache/ApplicationCacheStorage.cpp
index 910d00c..791df22 100644
--- a/WebCore/loader/appcache/ApplicationCacheStorage.cpp
+++ b/WebCore/loader/appcache/ApplicationCacheStorage.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ * Copyright (C) 2008, 2009 Apple Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -31,11 +31,15 @@
#include "ApplicationCache.h"
#include "ApplicationCacheGroup.h"
#include "ApplicationCacheResource.h"
-#include "FileSystem.h"
#include "CString.h"
+#include "FileSystem.h"
#include "KURL.h"
#include "SQLiteStatement.h"
#include "SQLiteTransaction.h"
+#include <wtf/StdLibExtras.h>
+#include <wtf/StringExtras.h>
+
+using namespace std;
namespace WebCore {
@@ -68,7 +72,7 @@ ApplicationCacheGroup* ApplicationCacheStorage::loadCacheGroup(const KURL& manif
return 0;
}
- unsigned newestCacheStorageID = (unsigned)statement.getColumnInt64(2);
+ unsigned newestCacheStorageID = static_cast<unsigned>(statement.getColumnInt64(2));
RefPtr<ApplicationCache> cache = loadCache(newestCacheStorageID);
if (!cache)
@@ -76,7 +80,7 @@ ApplicationCacheGroup* ApplicationCacheStorage::loadCacheGroup(const KURL& manif
ApplicationCacheGroup* group = new ApplicationCacheGroup(manifestURL);
- group->setStorageID((unsigned)statement.getColumnInt64(0));
+ group->setStorageID(static_cast<unsigned>(statement.getColumnInt64(0)));
group->setNewestCache(cache.release());
return group;
@@ -128,7 +132,7 @@ void ApplicationCacheStorage::loadManifestHostHashes()
int result;
while ((result = statement.step()) == SQLResultRow)
- m_cacheHostSet.add((unsigned)statement.getColumnInt64(0));
+ m_cacheHostSet.add(static_cast<unsigned>(statement.getColumnInt64(0)));
}
ApplicationCacheGroup* ApplicationCacheStorage::cacheGroupForURL(const KURL& url)
@@ -143,13 +147,19 @@ ApplicationCacheGroup* ApplicationCacheStorage::cacheGroupForURL(const KURL& url
CacheGroupMap::const_iterator end = m_cachesInMemory.end();
for (CacheGroupMap::const_iterator it = m_cachesInMemory.begin(); it != end; ++it) {
ApplicationCacheGroup* group = it->second;
-
+
+ ASSERT(!group->isObsolete());
+
if (!protocolHostAndPortAreEqual(url, group->manifestURL()))
continue;
if (ApplicationCache* cache = group->newestCache()) {
- if (cache->resourceForURL(url))
- return group;
+ ApplicationCacheResource* resource = cache->resourceForURL(url);
+ if (!resource)
+ continue;
+ if (resource->type() & ApplicationCacheResource::Foreign)
+ continue;
+ return group;
}
}
@@ -165,23 +175,93 @@ ApplicationCacheGroup* ApplicationCacheStorage::cacheGroupForURL(const KURL& url
while ((result = statement.step()) == SQLResultRow) {
KURL manifestURL = KURL(statement.getColumnText(1));
+ if (m_cachesInMemory.contains(manifestURL))
+ continue;
+
if (!protocolHostAndPortAreEqual(url, manifestURL))
continue;
// We found a cache group that matches. Now check if the newest cache has a resource with
// a matching URL.
- unsigned newestCacheID = (unsigned)statement.getColumnInt64(2);
+ unsigned newestCacheID = static_cast<unsigned>(statement.getColumnInt64(2));
+ RefPtr<ApplicationCache> cache = loadCache(newestCacheID);
+
+ ApplicationCacheResource* 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;
+ }
+
+ if (result != SQLResultDone)
+ LOG_ERROR("Could not load cache group, error \"%s\"", m_database.lastErrorMsg());
+
+ return 0;
+}
+
+ApplicationCacheGroup* ApplicationCacheStorage::fallbackCacheGroupForURL(const KURL& url)
+{
+ // 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;
+
+ ASSERT(!group->isObsolete());
+
+ if (ApplicationCache* cache = group->newestCache()) {
+ KURL fallbackURL;
+ if (!cache->urlMatchesFallbackNamespace(url, &fallbackURL))
+ continue;
+ if (cache->resourceForURL(fallbackURL)->type() & ApplicationCacheResource::Foreign)
+ continue;
+ return group;
+ }
+ }
+
+ if (!m_database.isOpen())
+ return 0;
+
+ // 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;
+
+ int result;
+ while ((result = statement.step()) == SQLResultRow) {
+ KURL manifestURL = KURL(statement.getColumnText(1));
+
+ if (m_cachesInMemory.contains(manifestURL))
+ continue;
+
+ // Fallback namespaces always have the same origin as manifest URL, so we can avoid loading caches that cannot match.
+ if (!protocolHostAndPortAreEqual(url, manifestURL))
+ continue;
+
+ // 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);
- if (!cache->resourceForURL(url))
+ KURL fallbackURL;
+ if (!cache->urlMatchesFallbackNamespace(url, &fallbackURL))
+ continue;
+ if (cache->resourceForURL(fallbackURL)->type() & ApplicationCacheResource::Foreign)
continue;
ApplicationCacheGroup* group = new ApplicationCacheGroup(manifestURL);
- group->setStorageID((unsigned)statement.getColumnInt64(0));
+ group->setStorageID(static_cast<unsigned>(statement.getColumnInt64(0)));
group->setNewestCache(cache.release());
- ASSERT(!m_cachesInMemory.contains(manifestURL));
m_cachesInMemory.set(group->manifestURL(), group);
return group;
@@ -195,15 +275,33 @@ ApplicationCacheGroup* ApplicationCacheStorage::cacheGroupForURL(const KURL& url
void ApplicationCacheStorage::cacheGroupDestroyed(ApplicationCacheGroup* group)
{
+ if (group->isObsolete()) {
+ ASSERT(!group->storageID());
+ ASSERT(m_cachesInMemory.get(group->manifestURL()) != group);
+ return;
+ }
+
ASSERT(m_cachesInMemory.get(group->manifestURL()) == group);
m_cachesInMemory.remove(group->manifestURL());
- // If the cache is half-created, we don't want it in the saved set.
- if (!group->savedNewestCachePointer())
+ // 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()));
}
+void ApplicationCacheStorage::cacheGroupMadeObsolete(ApplicationCacheGroup* group)
+{
+ ASSERT(m_cachesInMemory.get(group->manifestURL()) == group);
+ ASSERT(m_cacheHostSet.contains(urlHostHash(group->manifestURL())));
+
+ if (ApplicationCache* 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());
@@ -212,6 +310,12 @@ void ApplicationCacheStorage::setCacheDirectory(const String& cacheDirectory)
m_cacheDirectory = cacheDirectory;
}
+const String& ApplicationCacheStorage::cacheDirectory() const
+{
+ return m_cacheDirectory;
+}
+
+
bool ApplicationCacheStorage::executeSQLCommand(const String& sql)
{
ASSERT(m_database.isOpen());
@@ -224,30 +328,30 @@ bool ApplicationCacheStorage::executeSQLCommand(const String& sql)
return result;
}
-static const int SchemaVersion = 2;
+static const int schemaVersion = 3;
void ApplicationCacheStorage::verifySchemaVersion()
{
- if (m_database.tableExists("SchemaVersion")) {
- int version = SQLiteStatement(m_database, "SELECT version from SchemaVersion").getColumnInt(0);
-
- if (version == SchemaVersion)
- return;
- }
-
+ int version = SQLiteStatement(m_database, "PRAGMA user_version").getColumnInt(0);
+ if (version == schemaVersion)
+ return;
+
m_database.clearAllTables();
- SQLiteTransaction createSchemaVersionTable(m_database);
- createSchemaVersionTable.begin();
+ // Update user version.
+ SQLiteTransaction setDatabaseVersion(m_database);
+ setDatabaseVersion.begin();
- executeSQLCommand("CREATE TABLE SchemaVersion (version INTEGER NOT NULL)");
- SQLiteStatement statement(m_database, "INSERT INTO SchemaVersion (version) VALUES (?)");
+ char userVersionSQL[32];
+ int unusedNumBytes = snprintf(userVersionSQL, sizeof(userVersionSQL), "PRAGMA user_version=%d", schemaVersion);
+ ASSERT_UNUSED(unusedNumBytes, static_cast<int>(sizeof(userVersionSQL)) >= unusedNumBytes);
+
+ SQLiteStatement statement(m_database, userVersionSQL);
if (statement.prepare() != SQLResultOk)
return;
- statement.bindInt64(1, SchemaVersion);
executeStatement(statement);
- createSchemaVersionTable.commit();
+ setDatabaseVersion.commit();
}
void ApplicationCacheStorage::openDatabase(bool createIfDoesNotExist)
@@ -290,7 +394,13 @@ void ApplicationCacheStorage::openDatabase(bool createIfDoesNotExist)
" DELETE FROM CacheWhitelistURLs WHERE cache = OLD.id;"
" DELETE FROM FallbackURLs WHERE cache = OLD.id;"
" END");
-
+
+ // When a cache entry is deleted, its resource should also be deleted.
+ executeSQLCommand("CREATE TRIGGER IF NOT EXISTS CacheEntryDeleted AFTER DELETE ON CacheEntries"
+ " FOR EACH ROW BEGIN"
+ " DELETE FROM CacheResources WHERE id = OLD.resource;"
+ " END");
+
// When a cache resource is deleted, its data blob should also be deleted.
executeSQLCommand("CREATE TRIGGER IF NOT EXISTS CacheResourceDeleted AFTER DELETE ON CacheResources"
" FOR EACH ROW BEGIN"
@@ -322,7 +432,7 @@ bool ApplicationCacheStorage::store(ApplicationCacheGroup* group)
if (!executeStatement(statement))
return false;
- group->setStorageID((unsigned)m_database.lastInsertRowID());
+ group->setStorageID(static_cast<unsigned>(m_database.lastInsertRowID()));
return true;
}
@@ -340,7 +450,7 @@ bool ApplicationCacheStorage::store(ApplicationCache* cache)
if (!executeStatement(statement))
return false;
- unsigned cacheStorageID = (unsigned)m_database.lastInsertRowID();
+ unsigned cacheStorageID = static_cast<unsigned>(m_database.lastInsertRowID());
// Store all resources
{
@@ -352,14 +462,14 @@ bool ApplicationCacheStorage::store(ApplicationCache* cache)
}
// Store the online whitelist
- const HashSet<String>& onlineWhitelist = cache->onlineWhitelist();
+ const Vector<KURL>& onlineWhitelist = cache->onlineWhitelist();
{
- HashSet<String>::const_iterator end = onlineWhitelist.end();
- for (HashSet<String>::const_iterator it = onlineWhitelist.begin(); it != end; ++it) {
+ size_t whitelistSize = onlineWhitelist.size();
+ for (size_t i = 0; i < whitelistSize; ++i) {
SQLiteStatement statement(m_database, "INSERT INTO CacheWhitelistURLs (url, cache) VALUES (?, ?)");
statement.prepare();
- statement.bindText(1, *it);
+ statement.bindText(1, onlineWhitelist[i]);
statement.bindInt64(2, cacheStorageID);
if (!executeStatement(statement))
@@ -367,6 +477,23 @@ bool ApplicationCacheStorage::store(ApplicationCache* cache)
}
}
+ // Store fallback URLs.
+ const FallbackURLVector& fallbackURLs = cache->fallbackURLs();
+ {
+ size_t fallbackCount = fallbackURLs.size();
+ for (size_t i = 0; i < fallbackCount; ++i) {
+ 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.bindInt64(3, cacheStorageID);
+
+ if (!executeStatement(statement))
+ return false;
+ }
+ }
+
cache->setStorageID(cacheStorageID);
return true;
}
@@ -389,7 +516,7 @@ bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, unsigned
if (!dataStatement.executeCommand())
return false;
- unsigned dataId = (unsigned)m_database.lastInsertRowID();
+ unsigned dataId = static_cast<unsigned>(m_database.lastInsertRowID());
// Then, insert the resource
@@ -421,7 +548,7 @@ bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, unsigned
if (!executeStatement(resourceStatement))
return false;
- unsigned resourceId = (unsigned)m_database.lastInsertRowID();
+ unsigned resourceId = static_cast<unsigned>(m_database.lastInsertRowID());
// Finally, insert the cache entry
SQLiteStatement entryStatement(m_database, "INSERT INTO CacheEntries (cache, type, resource) VALUES (?, ?, ?)");
@@ -439,6 +566,25 @@ bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, unsigned
return true;
}
+bool ApplicationCacheStorage::storeUpdatedType(ApplicationCacheResource* resource, ApplicationCache* cache)
+{
+ ASSERT_UNUSED(cache, cache->storageID());
+ ASSERT(resource->storageID());
+
+ // FIXME: If the resource gained a Dynamic bit, it should be re-inserted at the end for correct order.
+ ASSERT(!(resource->type() & ApplicationCacheResource::Dynamic));
+
+ // First, insert the data
+ SQLiteStatement entryStatement(m_database, "UPDATE CacheEntries SET type=? WHERE resource=?");
+ if (entryStatement.prepare() != SQLResultOk)
+ return false;
+
+ entryStatement.bindInt64(1, resource->type());
+ entryStatement.bindInt64(2, resource->storageID());
+
+ return executeStatement(entryStatement);
+}
+
void ApplicationCacheStorage::store(ApplicationCacheResource* resource, ApplicationCache* cache)
{
ASSERT(cache->storageID());
@@ -469,6 +615,7 @@ bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup* group)
}
ASSERT(group->newestCache());
+ ASSERT(!group->isObsolete());
ASSERT(!group->newestCache()->storageID());
// Store the newest cache
@@ -496,7 +643,7 @@ static inline void parseHeader(const UChar* header, size_t headerLength, Resourc
int pos = find(header, headerLength, ':');
ASSERT(pos != -1);
- String headerName = String(header, pos);
+ AtomicString headerName = AtomicString(header, pos);
String headerValue = String(header + pos + 1, headerLength - pos - 1);
response.setHTTPHeaderField(headerName, headerValue);
@@ -536,7 +683,7 @@ PassRefPtr<ApplicationCache> ApplicationCacheStorage::loadCache(unsigned storage
while ((result = cacheStatement.step()) == SQLResultRow) {
KURL url(cacheStatement.getColumnText(0));
- unsigned type = (unsigned)cacheStatement.getColumnInt64(1);
+ unsigned type = static_cast<unsigned>(cacheStatement.getColumnInt64(1));
Vector<char> blob;
cacheStatement.getColumnBlobAsVector(5, blob);
@@ -568,14 +715,29 @@ PassRefPtr<ApplicationCache> ApplicationCacheStorage::loadCache(unsigned storage
return 0;
whitelistStatement.bindInt64(1, storageID);
- HashSet<String> whitelist;
+ Vector<KURL> whitelist;
while ((result = whitelistStatement.step()) == SQLResultRow)
- whitelist.add(whitelistStatement.getColumnText(0));
+ whitelist.append(whitelistStatement.getColumnText(0));
if (result != SQLResultDone)
LOG_ERROR("Could not load cache online whitelist, error \"%s\"", m_database.lastErrorMsg());
cache->setOnlineWhitelist(whitelist);
+
+ // Load fallback URLs.
+ SQLiteStatement fallbackStatement(m_database, "SELECT namespace, fallbackURL FROM FallbackURLs WHERE cache=?");
+ if (fallbackStatement.prepare() != SQLResultOk)
+ return 0;
+ fallbackStatement.bindInt64(1, storageID);
+
+ FallbackURLVector fallbackURLs;
+ while ((result = fallbackStatement.step()) == SQLResultRow)
+ fallbackURLs.append(make_pair(fallbackStatement.getColumnText(0), fallbackStatement.getColumnText(1)));
+
+ if (result != SQLResultDone)
+ LOG_ERROR("Could not load fallback URLs, error \"%s\"", m_database.lastErrorMsg());
+
+ cache->setFallbackURLs(fallbackURLs);
cache->setStorageID(storageID);
@@ -591,12 +753,30 @@ void ApplicationCacheStorage::remove(ApplicationCache* cache)
if (!m_database.isOpen())
return;
+ ASSERT(cache->group());
+ ASSERT(cache->group()->storageID());
+
+ // All associated data will be deleted by database triggers.
SQLiteStatement statement(m_database, "DELETE FROM Caches WHERE id=?");
if (statement.prepare() != SQLResultOk)
return;
statement.bindInt64(1, cache->storageID());
executeStatement(statement);
+
+ cache->clearStorageID();
+
+ 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)
+ return;
+
+ groupStatement.bindInt64(1, cache->group()->storageID());
+ executeStatement(groupStatement);
+
+ cache->group()->clearStorageID();
+ }
}
void ApplicationCacheStorage::empty()
@@ -609,7 +789,6 @@ void ApplicationCacheStorage::empty()
// Clear cache groups, caches and cache resources.
executeSQLCommand("DELETE FROM CacheGroups");
executeSQLCommand("DELETE FROM Caches");
- executeSQLCommand("DELETE FROM CacheResources");
// Clear the storage IDs for the caches in memory.
// The caches will still work, but cached resources will not be saved to disk
@@ -623,10 +802,10 @@ bool ApplicationCacheStorage::storeCopyOfCache(const String& cacheDirectory, App
{
// Create a new cache.
RefPtr<ApplicationCache> cacheCopy = ApplicationCache::create();
-
- // Set the online whitelist
+
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) {
@@ -653,7 +832,7 @@ bool ApplicationCacheStorage::storeCopyOfCache(const String& cacheDirectory, App
ApplicationCacheStorage& cacheStorage()
{
- static ApplicationCacheStorage storage;
+ DEFINE_STATIC_LOCAL(ApplicationCacheStorage, storage, ());
return storage;
}