summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheStorage.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheStorage.cpp')
-rw-r--r--src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheStorage.cpp249
1 files changed, 225 insertions, 24 deletions
diff --git a/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheStorage.cpp b/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheStorage.cpp
index 7e611cd6e..18a0ed4d9 100644
--- a/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheStorage.cpp
+++ b/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheStorage.cpp
@@ -43,16 +43,17 @@ using namespace std;
namespace WebCore {
-class ResourceStorageIDJournal {
+template <class T>
+class StorageIDJournal {
public:
- ~ResourceStorageIDJournal()
+ ~StorageIDJournal()
{
size_t size = m_records.size();
for (size_t i = 0; i < size; ++i)
m_records[i].restore();
}
- void add(ApplicationCacheResource* resource, unsigned storageID)
+ void add(T* resource, unsigned storageID)
{
m_records.append(Record(resource, storageID));
}
@@ -66,7 +67,7 @@ private:
class Record {
public:
Record() : m_resource(0), m_storageID(0) { }
- Record(ApplicationCacheResource* resource, unsigned storageID) : m_resource(resource), m_storageID(storageID) { }
+ Record(T* resource, unsigned storageID) : m_resource(resource), m_storageID(storageID) { }
void restore()
{
@@ -74,7 +75,7 @@ private:
}
private:
- ApplicationCacheResource* m_resource;
+ T* m_resource;
unsigned m_storageID;
};
@@ -126,11 +127,12 @@ ApplicationCacheGroup* ApplicationCacheStorage::loadCacheGroup(const KURL& manif
ApplicationCacheGroup* ApplicationCacheStorage::findOrCreateCacheGroup(const KURL& manifestURL)
{
+ ASSERT(!manifestURL.hasRef());
+
std::pair<CacheGroupMap::iterator, bool> result = m_cachesInMemory.add(manifestURL, 0);
if (!result.second) {
ASSERT(result.first->second);
-
return result.first->second;
}
@@ -175,6 +177,8 @@ void ApplicationCacheStorage::loadManifestHostHashes()
ApplicationCacheGroup* ApplicationCacheStorage::cacheGroupForURL(const KURL& url)
{
+ ASSERT(!url.hasRef());
+
loadManifestHostHashes();
// Hash the host name and see if there's a manifest with the same host.
@@ -223,6 +227,8 @@ ApplicationCacheGroup* ApplicationCacheStorage::cacheGroupForURL(const KURL& url
// a matching URL.
unsigned newestCacheID = static_cast<unsigned>(statement.getColumnInt64(2));
RefPtr<ApplicationCache> cache = loadCache(newestCacheID);
+ if (!cache)
+ continue;
ApplicationCacheResource* resource = cache->resourceForURL(url);
if (!resource)
@@ -248,6 +254,8 @@ ApplicationCacheGroup* ApplicationCacheStorage::cacheGroupForURL(const KURL& url
ApplicationCacheGroup* ApplicationCacheStorage::fallbackCacheGroupForURL(const KURL& url)
{
+ ASSERT(!url.hasRef());
+
// 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) {
@@ -353,6 +361,58 @@ const String& ApplicationCacheStorage::cacheDirectory() const
return m_cacheDirectory;
}
+void ApplicationCacheStorage::setMaximumSize(int64_t size)
+{
+ m_maximumSize = size;
+}
+
+int64_t ApplicationCacheStorage::maximumSize() const
+{
+ return m_maximumSize;
+}
+
+bool ApplicationCacheStorage::isMaximumSizeReached() const
+{
+ return m_isMaximumSizeReached;
+}
+
+int64_t ApplicationCacheStorage::spaceNeeded(int64_t cacheToSave)
+{
+ int64_t spaceNeeded = 0;
+ long long fileSize = 0;
+ if (!getFileSize(m_cacheFile, fileSize))
+ return 0;
+
+ int64_t currentSize = fileSize;
+
+ // Determine the amount of free space we have available.
+ int64_t totalAvailableSize = 0;
+ if (m_maximumSize < currentSize) {
+ // The max size is smaller than the actual size of the app cache file.
+ // This can happen if the client previously imposed a larger max size
+ // value and the app cache file has already grown beyond the current
+ // max size value.
+ // The amount of free space is just the amount of free space inside
+ // the database file. Note that this is always 0 if SQLite is compiled
+ // with AUTO_VACUUM = 1.
+ totalAvailableSize = m_database.freeSpaceSize();
+ } else {
+ // The max size is the same or larger than the current size.
+ // The amount of free space available is the amount of free space
+ // inside the database file plus the amount we can grow until we hit
+ // the max size.
+ totalAvailableSize = (m_maximumSize - currentSize) + m_database.freeSpaceSize();
+ }
+
+ // The space needed to be freed in order to accomodate the failed cache is
+ // the size of the failed cache minus any already available free space.
+ spaceNeeded = cacheToSave - totalAvailableSize;
+ // The space needed value must be positive (or else the total already
+ // available free space would be larger than the size of the failed cache and
+ // saving of the cache should have never failed).
+ ASSERT(spaceNeeded);
+ return spaceNeeded;
+}
bool ApplicationCacheStorage::executeSQLCommand(const String& sql)
{
@@ -366,7 +426,7 @@ bool ApplicationCacheStorage::executeSQLCommand(const String& sql)
return result;
}
-static const int schemaVersion = 3;
+static const int schemaVersion = 4;
void ApplicationCacheStorage::verifySchemaVersion()
{
@@ -400,13 +460,13 @@ void ApplicationCacheStorage::openDatabase(bool createIfDoesNotExist)
// The cache directory should never be null, but if it for some weird reason is we bail out.
if (m_cacheDirectory.isNull())
return;
-
- String applicationCachePath = pathByAppendingComponent(m_cacheDirectory, "ApplicationCache.db");
- if (!createIfDoesNotExist && !fileExists(applicationCachePath))
+
+ m_cacheFile = pathByAppendingComponent(m_cacheDirectory, "ApplicationCache.db");
+ if (!createIfDoesNotExist && !fileExists(m_cacheFile))
return;
makeAllDirectories(m_cacheDirectory);
- m_database.open(applicationCachePath);
+ m_database.open(m_cacheFile);
if (!m_database.isOpen())
return;
@@ -416,7 +476,7 @@ void ApplicationCacheStorage::openDatabase(bool createIfDoesNotExist)
// Create tables
executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheGroups (id INTEGER PRIMARY KEY AUTOINCREMENT, "
"manifestHostHash INTEGER NOT NULL ON CONFLICT FAIL, manifestURL TEXT UNIQUE ON CONFLICT FAIL, newestCache INTEGER)");
- executeSQLCommand("CREATE TABLE IF NOT EXISTS Caches (id INTEGER PRIMARY KEY AUTOINCREMENT, cacheGroup INTEGER)");
+ executeSQLCommand("CREATE TABLE IF NOT EXISTS Caches (id INTEGER PRIMARY KEY AUTOINCREMENT, cacheGroup INTEGER, size INTEGER)");
executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheWhitelistURLs (url TEXT NOT NULL ON CONFLICT FAIL, cache INTEGER NOT NULL ON CONFLICT FAIL)");
executeSQLCommand("CREATE TABLE IF NOT EXISTS FallbackURLs (namespace TEXT NOT NULL ON CONFLICT FAIL, fallbackURL TEXT NOT NULL ON CONFLICT FAIL, "
"cache INTEGER NOT NULL ON CONFLICT FAIL)");
@@ -456,9 +516,10 @@ bool ApplicationCacheStorage::executeStatement(SQLiteStatement& statement)
return result;
}
-bool ApplicationCacheStorage::store(ApplicationCacheGroup* group)
+bool ApplicationCacheStorage::store(ApplicationCacheGroup* group, GroupStorageIDJournal* journal)
{
ASSERT(group->storageID() == 0);
+ ASSERT(journal);
SQLiteStatement statement(m_database, "INSERT INTO CacheGroups (manifestHostHash, manifestURL) VALUES (?, ?)");
if (statement.prepare() != SQLResultOk)
@@ -471,6 +532,7 @@ bool ApplicationCacheStorage::store(ApplicationCacheGroup* group)
return false;
group->setStorageID(static_cast<unsigned>(m_database.lastInsertRowID()));
+ journal->add(group, 0);
return true;
}
@@ -480,11 +542,12 @@ bool ApplicationCacheStorage::store(ApplicationCache* cache, ResourceStorageIDJo
ASSERT(cache->group()->storageID() != 0);
ASSERT(storageIDJournal);
- SQLiteStatement statement(m_database, "INSERT INTO Caches (cacheGroup) VALUES (?)");
+ SQLiteStatement statement(m_database, "INSERT INTO Caches (cacheGroup, size) VALUES (?, ?)");
if (statement.prepare() != SQLResultOk)
return false;
statement.bindInt64(1, cache->group()->storageID());
+ statement.bindInt64(2, cache->estimatedSizeInStorage());
if (!executeStatement(statement))
return false;
@@ -581,6 +644,9 @@ bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, unsigned
if (resourceStatement.prepare() != SQLResultOk)
return false;
+ // The same ApplicationCacheResource are used in ApplicationCacheResource::size()
+ // to calculate the approximate size of an ApplicationCacheResource object. If
+ // you change the code below, please also change ApplicationCacheResource::size().
resourceStatement.bindText(1, resource->url());
resourceStatement.bindInt64(2, resource->response().httpStatusCode());
resourceStatement.bindText(3, resource->response().url());
@@ -626,33 +692,56 @@ bool ApplicationCacheStorage::storeUpdatedType(ApplicationCacheResource* resourc
return executeStatement(entryStatement);
}
-void ApplicationCacheStorage::store(ApplicationCacheResource* resource, ApplicationCache* cache)
+bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, ApplicationCache* cache)
{
ASSERT(cache->storageID());
openDatabase(true);
+ m_isMaximumSizeReached = false;
+ m_database.setMaximumSize(m_maximumSize);
+
SQLiteTransaction storeResourceTransaction(m_database);
storeResourceTransaction.begin();
- if (!store(resource, cache->storageID()))
- return;
+ if (!store(resource, cache->storageID())) {
+ checkForMaxSizeReached();
+ return false;
+ }
+
+ // 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)
+ return false;
+
+ sizeUpdateStatement.bindInt64(1, resource->estimatedSizeInStorage());
+ sizeUpdateStatement.bindInt64(2, cache->storageID());
+
+ if (!executeStatement(sizeUpdateStatement))
+ return false;
storeResourceTransaction.commit();
+ return true;
}
bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup* group)
{
openDatabase(true);
-
+
+ m_isMaximumSizeReached = false;
+ m_database.setMaximumSize(m_maximumSize);
+
SQLiteTransaction storeCacheTransaction(m_database);
storeCacheTransaction.begin();
-
+
+ GroupStorageIDJournal groupStorageIDJournal;
if (!group->storageID()) {
// Store the group
- if (!store(group))
+ if (!store(group, &groupStorageIDJournal)) {
+ checkForMaxSizeReached();
return false;
+ }
}
ASSERT(group->newestCache());
@@ -662,11 +751,13 @@ bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup* group)
// Log the storageID changes to the in-memory resource objects. The journal
// object will roll them back automatically in case a database operation
// fails and this method returns early.
- ResourceStorageIDJournal storageIDJournal;
+ ResourceStorageIDJournal resourceStorageIDJournal;
// Store the newest cache
- if (!store(group->newestCache(), &storageIDJournal))
+ if (!store(group->newestCache(), &resourceStorageIDJournal)) {
+ checkForMaxSizeReached();
return false;
+ }
// Update the newest cache in the group.
@@ -680,7 +771,8 @@ bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup* group)
if (!executeStatement(statement))
return false;
- storageIDJournal.commit();
+ groupStorageIDJournal.commit();
+ resourceStorageIDJournal.commit();
storeCacheTransaction.commit();
return true;
}
@@ -876,7 +968,116 @@ bool ApplicationCacheStorage::storeCopyOfCache(const String& cacheDirectory, App
return copyStorage.storeNewestCache(groupCopy.get());
}
-
+
+bool ApplicationCacheStorage::manifestURLs(Vector<KURL>* urls)
+{
+ ASSERT(urls);
+ openDatabase(false);
+ if (!m_database.isOpen())
+ return false;
+
+ SQLiteStatement selectURLs(m_database, "SELECT manifestURL FROM CacheGroups");
+
+ if (selectURLs.prepare() != SQLResultOk)
+ return false;
+
+ while (selectURLs.step() == SQLResultRow)
+ urls->append(selectURLs.getColumnText(0));
+
+ return true;
+}
+
+bool ApplicationCacheStorage::cacheGroupSize(const String& manifestURL, int64_t* size)
+{
+ ASSERT(size);
+ openDatabase(false);
+ if (!m_database.isOpen())
+ 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)
+ return false;
+
+ statement.bindText(1, manifestURL);
+
+ int result = statement.step();
+ if (result == SQLResultDone)
+ return false;
+
+ if (result != SQLResultRow) {
+ LOG_ERROR("Could not get the size of the cache group, error \"%s\"", m_database.lastErrorMsg());
+ return false;
+ }
+
+ *size = statement.getColumnInt64(0);
+ return true;
+}
+
+bool ApplicationCacheStorage::deleteCacheGroup(const String& manifestURL)
+{
+ SQLiteTransaction deleteTransaction(m_database);
+ // Check to see if the group is in memory.
+ ApplicationCacheGroup* group = m_cachesInMemory.get(manifestURL);
+ if (group)
+ 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());
+ 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();
+ return true;
+}
+
+void ApplicationCacheStorage::vacuumDatabaseFile()
+{
+ m_database.runVacuumCommand();
+}
+
+void ApplicationCacheStorage::checkForMaxSizeReached()
+{
+ if (m_database.lastError() == SQLResultFull)
+ m_isMaximumSizeReached = true;
+}
+
+ApplicationCacheStorage::ApplicationCacheStorage()
+ : m_maximumSize(INT_MAX)
+ , m_isMaximumSizeReached(false)
+{
+}
+
ApplicationCacheStorage& cacheStorage()
{
DEFINE_STATIC_LOCAL(ApplicationCacheStorage, storage, ());