summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/loader/appcache
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/loader/appcache')
-rw-r--r--Source/WebCore/loader/appcache/ApplicationCache.cpp25
-rw-r--r--Source/WebCore/loader/appcache/ApplicationCache.h4
-rw-r--r--Source/WebCore/loader/appcache/ApplicationCacheGroup.cpp15
-rw-r--r--Source/WebCore/loader/appcache/ApplicationCacheHost.h7
-rw-r--r--Source/WebCore/loader/appcache/ApplicationCacheResource.cpp3
-rw-r--r--Source/WebCore/loader/appcache/ApplicationCacheResource.h10
-rw-r--r--Source/WebCore/loader/appcache/ApplicationCacheStorage.cpp225
-rw-r--r--Source/WebCore/loader/appcache/ApplicationCacheStorage.h7
8 files changed, 248 insertions, 48 deletions
diff --git a/Source/WebCore/loader/appcache/ApplicationCache.cpp b/Source/WebCore/loader/appcache/ApplicationCache.cpp
index 450fa10..f3479b4 100644
--- a/Source/WebCore/loader/appcache/ApplicationCache.cpp
+++ b/Source/WebCore/loader/appcache/ApplicationCache.cpp
@@ -32,6 +32,7 @@
#include "ApplicationCacheResource.h"
#include "ApplicationCacheStorage.h"
#include "ResourceRequest.h"
+#include "SecurityOrigin.h"
#include <wtf/text/CString.h>
#include <stdio.h>
@@ -182,7 +183,29 @@ void ApplicationCache::clearStorageID()
ResourceMap::const_iterator end = m_resources.end();
for (ResourceMap::const_iterator it = m_resources.begin(); it != end; ++it)
it->second->clearStorageID();
-}
+}
+
+void ApplicationCache::deleteCacheForOrigin(SecurityOrigin* origin)
+{
+ Vector<KURL> urls;
+ if (!cacheStorage().manifestURLs(&urls)) {
+ LOG_ERROR("Failed to retrieve ApplicationCache manifest URLs");
+ return;
+ }
+
+ KURL originURL(KURL(), origin->toString());
+
+ size_t count = urls.size();
+ for (size_t i = 0; i < count; ++i) {
+ if (protocolHostAndPortAreEqual(urls[i], originURL)) {
+ ApplicationCacheGroup* group = cacheStorage().findInMemoryCacheGroup(urls[i]);
+ if (group)
+ group->makeObsolete();
+ else
+ cacheStorage().deleteCacheGroup(urls[i]);
+ }
+ }
+}
#ifndef NDEBUG
void ApplicationCache::dump()
diff --git a/Source/WebCore/loader/appcache/ApplicationCache.h b/Source/WebCore/loader/appcache/ApplicationCache.h
index f073499..9d20361 100644
--- a/Source/WebCore/loader/appcache/ApplicationCache.h
+++ b/Source/WebCore/loader/appcache/ApplicationCache.h
@@ -42,12 +42,16 @@ class ApplicationCacheResource;
class DocumentLoader;
class KURL;
class ResourceRequest;
+class SecurityOrigin;
typedef Vector<std::pair<KURL, KURL> > FallbackURLVector;
class ApplicationCache : public RefCounted<ApplicationCache> {
public:
static PassRefPtr<ApplicationCache> create() { return adoptRef(new ApplicationCache); }
+
+ static void deleteCacheForOrigin(SecurityOrigin*);
+
~ApplicationCache();
void addResource(PassRefPtr<ApplicationCacheResource> resource);
diff --git a/Source/WebCore/loader/appcache/ApplicationCacheGroup.cpp b/Source/WebCore/loader/appcache/ApplicationCacheGroup.cpp
index 514ef19..aab8927 100644
--- a/Source/WebCore/loader/appcache/ApplicationCacheGroup.cpp
+++ b/Source/WebCore/loader/appcache/ApplicationCacheGroup.cpp
@@ -474,9 +474,8 @@ PassRefPtr<ResourceHandle> ApplicationCacheGroup::createResourceHandle(const KUR
// Because willSendRequest only gets called during redirects, we initialize
// the identifier and the first willSendRequest here.
m_currentResourceIdentifier = m_frame->page()->progress()->createUniqueIdentifier();
- InspectorInstrumentation::identifierForInitialRequest(m_frame, m_currentResourceIdentifier, m_frame->loader()->documentLoader(), handle->firstRequest());
ResourceResponse redirectResponse = ResourceResponse();
- InspectorInstrumentation::willSendRequest(m_frame, m_currentResourceIdentifier, request, redirectResponse);
+ InspectorInstrumentation::willSendRequest(m_frame, m_currentResourceIdentifier, m_frame->loader()->documentLoader(), request, redirectResponse);
#endif
return handle;
}
@@ -512,7 +511,7 @@ void ApplicationCacheGroup::didReceiveResponse(ResourceHandle* handle, const Res
if (m_newestCache && response.httpStatusCode() == 304) { // Not modified.
ApplicationCacheResource* newestCachedResource = m_newestCache->resourceForURL(url);
if (newestCachedResource) {
- m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, newestCachedResource->response(), type, newestCachedResource->data()));
+ m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, newestCachedResource->response(), type, newestCachedResource->data(), newestCachedResource->path()));
m_pendingEntries.remove(m_currentHandle->firstRequest().url());
m_currentHandle->cancel();
m_currentHandle = 0;
@@ -540,7 +539,7 @@ void ApplicationCacheGroup::didReceiveResponse(ResourceHandle* handle, const Res
ASSERT(m_newestCache);
ApplicationCacheResource* newestCachedResource = m_newestCache->resourceForURL(handle->firstRequest().url());
ASSERT(newestCachedResource);
- m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, newestCachedResource->response(), type, newestCachedResource->data()));
+ m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, newestCachedResource->response(), type, newestCachedResource->data(), newestCachedResource->path()));
m_pendingEntries.remove(m_currentHandle->firstRequest().url());
m_currentHandle->cancel();
m_currentHandle = 0;
@@ -555,8 +554,10 @@ void ApplicationCacheGroup::didReceiveResponse(ResourceHandle* handle, const Res
void ApplicationCacheGroup::didReceiveData(ResourceHandle* handle, const char* data, int length, int lengthReceived)
{
+ UNUSED_PARAM(lengthReceived);
+
#if ENABLE(INSPECTOR)
- InspectorInstrumentation::didReceiveContentLength(m_frame, m_currentResourceIdentifier, lengthReceived);
+ InspectorInstrumentation::didReceiveContentLength(m_frame, m_currentResourceIdentifier, length, 0);
#endif
if (handle == m_manifestHandle) {
@@ -642,7 +643,7 @@ void ApplicationCacheGroup::didFail(ResourceHandle* handle, const ResourceError&
ASSERT(m_newestCache);
ApplicationCacheResource* newestCachedResource = m_newestCache->resourceForURL(url);
ASSERT(newestCachedResource);
- m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, newestCachedResource->response(), type, newestCachedResource->data()));
+ m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, newestCachedResource->response(), type, newestCachedResource->data(), newestCachedResource->path()));
// Load the next resource, if any.
startLoadingEntry();
}
@@ -866,7 +867,7 @@ void ApplicationCacheGroup::checkIfLoadIsComplete()
}
ApplicationCacheStorage::FailureReason failureReason;
- RefPtr<ApplicationCache> oldNewestCache = (m_newestCache == m_cacheBeingUpdated) ? 0 : m_newestCache;
+ RefPtr<ApplicationCache> oldNewestCache = (m_newestCache == m_cacheBeingUpdated) ? RefPtr<ApplicationCache>() : m_newestCache;
setNewestCache(m_cacheBeingUpdated.release());
if (cacheStorage().storeNewestCache(this, oldNewestCache.get(), failureReason)) {
// New cache stored, now remove the old cache.
diff --git a/Source/WebCore/loader/appcache/ApplicationCacheHost.h b/Source/WebCore/loader/appcache/ApplicationCacheHost.h
index a1f2841..56a5f57 100644
--- a/Source/WebCore/loader/appcache/ApplicationCacheHost.h
+++ b/Source/WebCore/loader/appcache/ApplicationCacheHost.h
@@ -156,6 +156,11 @@ namespace WebCore {
CacheInfo applicationCacheInfo();
#endif
+#if !PLATFORM(CHROMIUM)
+ bool shouldLoadResourceFromApplicationCache(const ResourceRequest&, ApplicationCacheResource*&);
+ bool getApplicationCacheFallbackResource(const ResourceRequest&, ApplicationCacheResource*&, ApplicationCache* = 0);
+#endif
+
private:
bool isApplicationCacheEnabled();
DocumentLoader* documentLoader() const { return m_documentLoader; }
@@ -182,8 +187,6 @@ namespace WebCore {
friend class ApplicationCacheStorage;
bool scheduleLoadFallbackResourceFromApplicationCache(ResourceLoader*, ApplicationCache* = 0);
- bool shouldLoadResourceFromApplicationCache(const ResourceRequest&, ApplicationCacheResource*&);
- bool getApplicationCacheFallbackResource(const ResourceRequest&, ApplicationCacheResource*&, ApplicationCache* = 0);
void setCandidateApplicationCacheGroup(ApplicationCacheGroup* group);
ApplicationCacheGroup* candidateApplicationCacheGroup() const { return m_candidateApplicationCacheGroup; }
void setApplicationCache(PassRefPtr<ApplicationCache> applicationCache);
diff --git a/Source/WebCore/loader/appcache/ApplicationCacheResource.cpp b/Source/WebCore/loader/appcache/ApplicationCacheResource.cpp
index 03c5c83..c0df573 100644
--- a/Source/WebCore/loader/appcache/ApplicationCacheResource.cpp
+++ b/Source/WebCore/loader/appcache/ApplicationCacheResource.cpp
@@ -31,11 +31,12 @@
namespace WebCore {
-ApplicationCacheResource::ApplicationCacheResource(const KURL& url, const ResourceResponse& response, unsigned type, PassRefPtr<SharedBuffer> data)
+ApplicationCacheResource::ApplicationCacheResource(const KURL& url, const ResourceResponse& response, unsigned type, PassRefPtr<SharedBuffer> data, const String& path)
: SubstituteResource(url, response, data)
, m_type(type)
, m_storageID(0)
, m_estimatedSizeInStorage(0)
+ , m_path(path)
{
}
diff --git a/Source/WebCore/loader/appcache/ApplicationCacheResource.h b/Source/WebCore/loader/appcache/ApplicationCacheResource.h
index 2ca7846..6633c6c 100644
--- a/Source/WebCore/loader/appcache/ApplicationCacheResource.h
+++ b/Source/WebCore/loader/appcache/ApplicationCacheResource.h
@@ -42,10 +42,10 @@ public:
Fallback = 1 << 4
};
- static PassRefPtr<ApplicationCacheResource> create(const KURL& url, const ResourceResponse& response, unsigned type, PassRefPtr<SharedBuffer> buffer = SharedBuffer::create())
+ static PassRefPtr<ApplicationCacheResource> create(const KURL& url, const ResourceResponse& response, unsigned type, PassRefPtr<SharedBuffer> buffer = SharedBuffer::create(), const String& path = String())
{
ASSERT(!url.hasFragmentIdentifier());
- return adoptRef(new ApplicationCacheResource(url, response, type, buffer));
+ return adoptRef(new ApplicationCacheResource(url, response, type, buffer, path));
}
unsigned type() const { return m_type; }
@@ -56,16 +56,20 @@ public:
void clearStorageID() { m_storageID = 0; }
int64_t estimatedSizeInStorage();
+ const String& path() const { return m_path; }
+ void setPath(const String& path) { m_path = path; }
+
#ifndef NDEBUG
static void dumpType(unsigned type);
#endif
private:
- ApplicationCacheResource(const KURL& url, const ResourceResponse& response, unsigned type, PassRefPtr<SharedBuffer> buffer);
+ ApplicationCacheResource(const KURL&, const ResourceResponse&, unsigned type, PassRefPtr<SharedBuffer>, const String& path);
unsigned m_type;
unsigned m_storageID;
int64_t m_estimatedSizeInStorage;
+ String m_path;
};
} // namespace WebCore
diff --git a/Source/WebCore/loader/appcache/ApplicationCacheStorage.cpp b/Source/WebCore/loader/appcache/ApplicationCacheStorage.cpp
index 5c6937e..a126f8a 100644
--- a/Source/WebCore/loader/appcache/ApplicationCacheStorage.cpp
+++ b/Source/WebCore/loader/appcache/ApplicationCacheStorage.cpp
@@ -38,6 +38,7 @@
#include "SQLiteStatement.h"
#include "SQLiteTransaction.h"
#include "SecurityOrigin.h"
+#include "UUID.h"
#include <wtf/text/CString.h>
#include <wtf/StdLibExtras.h>
#include <wtf/StringExtras.h>
@@ -46,6 +47,8 @@ using namespace std;
namespace WebCore {
+static const char flatFileSubdirectory[] = "ApplicationCache";
+
template <class T>
class StorageIDJournal {
public:
@@ -90,7 +93,7 @@ static unsigned urlHostHash(const KURL& url)
unsigned hostStart = url.hostStart();
unsigned hostEnd = url.hostEnd();
- return AlreadyHashed::avoidDeletedValue(WTF::StringHasher::createHash(url.string().characters() + hostStart, hostEnd - hostStart));
+ return AlreadyHashed::avoidDeletedValue(StringHasher::computeHash(url.string().characters() + hostStart, hostEnd - hostStart));
}
ApplicationCacheGroup* ApplicationCacheStorage::loadCacheGroup(const KURL& manifestURL)
@@ -153,6 +156,11 @@ ApplicationCacheGroup* ApplicationCacheStorage::findOrCreateCacheGroup(const KUR
return group;
}
+ApplicationCacheGroup* ApplicationCacheStorage::findInMemoryCacheGroup(const KURL& manifestURL) const
+{
+ return m_cachesInMemory.get(manifestURL);
+}
+
void ApplicationCacheStorage::loadManifestHostHashes()
{
static bool hasLoadedHashes = false;
@@ -389,7 +397,7 @@ int64_t ApplicationCacheStorage::spaceNeeded(int64_t cacheToSave)
if (!getFileSize(m_cacheFile, fileSize))
return 0;
- int64_t currentSize = fileSize;
+ int64_t currentSize = fileSize + flatFileAreaSize();
// Determine the amount of free space we have available.
int64_t totalAvailableSize = 0;
@@ -555,7 +563,7 @@ bool ApplicationCacheStorage::executeSQLCommand(const String& sql)
// Update the schemaVersion when the schema of any the Application Cache
// SQLite tables changes. This allows the database to be rebuilt when
// a new, incompatible change has been introduced to the database schema.
-static const int schemaVersion = 6;
+static const int schemaVersion = 7;
void ApplicationCacheStorage::verifySchemaVersion()
{
@@ -563,7 +571,7 @@ void ApplicationCacheStorage::verifySchemaVersion()
if (version == schemaVersion)
return;
- m_database.clearAllTables();
+ deleteTables();
// Update user version.
SQLiteTransaction setDatabaseVersion(m_database);
@@ -613,7 +621,8 @@ void ApplicationCacheStorage::openDatabase(bool createIfDoesNotExist)
executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheEntries (cache INTEGER NOT NULL ON CONFLICT FAIL, type INTEGER, resource INTEGER NOT NULL)");
executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheResources (id INTEGER PRIMARY KEY AUTOINCREMENT, url TEXT NOT NULL ON CONFLICT FAIL, "
"statusCode INTEGER NOT NULL, responseURL TEXT NOT NULL, mimeType TEXT, textEncodingName TEXT, headers TEXT, data INTEGER NOT NULL ON CONFLICT FAIL)");
- executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheResourceData (id INTEGER PRIMARY KEY AUTOINCREMENT, data BLOB)");
+ executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheResourceData (id INTEGER PRIMARY KEY AUTOINCREMENT, data BLOB, path TEXT)");
+ executeSQLCommand("CREATE TABLE IF NOT EXISTS DeletedCacheResources (id INTEGER PRIMARY KEY AUTOINCREMENT, path TEXT)");
executeSQLCommand("CREATE TABLE IF NOT EXISTS Origins (origin TEXT UNIQUE ON CONFLICT IGNORE, quota INTEGER NOT NULL ON CONFLICT FAIL)");
// When a cache is deleted, all its entries and its whitelist should be deleted.
@@ -636,6 +645,15 @@ void ApplicationCacheStorage::openDatabase(bool createIfDoesNotExist)
" FOR EACH ROW BEGIN"
" DELETE FROM CacheResourceData WHERE id = OLD.data;"
" END");
+
+ // When a cache resource is deleted, if it contains a non-empty path, that path should
+ // be added to the DeletedCacheResources table so the flat file at that path can
+ // be deleted at a later time.
+ executeSQLCommand("CREATE TRIGGER IF NOT EXISTS CacheResourceDataDeleted AFTER DELETE ON CacheResourceData"
+ " FOR EACH ROW"
+ " WHEN OLD.path NOT NULL BEGIN"
+ " INSERT INTO DeletedCacheResources (path) values (OLD.path);"
+ " END");
}
bool ApplicationCacheStorage::executeStatement(SQLiteStatement& statement)
@@ -767,15 +785,43 @@ bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, unsigned
return false;
// First, insert the data
- SQLiteStatement dataStatement(m_database, "INSERT INTO CacheResourceData (data) VALUES (?)");
+ SQLiteStatement dataStatement(m_database, "INSERT INTO CacheResourceData (data, path) VALUES (?, ?)");
if (dataStatement.prepare() != SQLResultOk)
return false;
- if (resource->data()->size())
- dataStatement.bindBlob(1, resource->data()->data(), resource->data()->size());
+
+ String fullPath;
+ if (!resource->path().isEmpty())
+ dataStatement.bindText(2, 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) {
+ m_isMaximumSizeReached = true;
+ return false;
+ }
+
+ String flatFileDirectory = pathByAppendingComponent(m_cacheDirectory, flatFileSubdirectory);
+ makeAllDirectories(flatFileDirectory);
+ String path;
+ if (!writeDataToUniqueFileInDirectory(resource->data(), flatFileDirectory, path))
+ return false;
+
+ fullPath = 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 (!dataStatement.executeCommand())
+ if (!dataStatement.executeCommand()) {
+ // Clean up the file which we may have written to:
+ if (!fullPath.isEmpty())
+ deleteFile(fullPath);
+
return false;
+ }
unsigned dataId = static_cast<unsigned>(m_database.lastInsertRowID());
@@ -826,6 +872,12 @@ bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, unsigned
if (!executeStatement(entryStatement))
return false;
+ // Did we successfully write the resource data to a file? If so,
+ // release the resource's data and free up a potentially large amount
+ // of memory:
+ if (!fullPath.isEmpty())
+ resource->data()->clear();
+
resource->setStorageID(resourceId);
return true;
}
@@ -856,7 +908,7 @@ bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, Applicat
return false;
m_isMaximumSizeReached = false;
- m_database.setMaximumSize(m_maximumSize);
+ m_database.setMaximumSize(m_maximumSize - flatFileAreaSize());
SQLiteTransaction storeResourceTransaction(m_database);
storeResourceTransaction.begin();
@@ -903,7 +955,7 @@ bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup* group, App
return false;
m_isMaximumSizeReached = false;
- m_database.setMaximumSize(m_maximumSize);
+ m_database.setMaximumSize(m_maximumSize - flatFileAreaSize());
SQLiteTransaction storeCacheTransaction(m_database);
@@ -1003,7 +1055,7 @@ static inline void parseHeaders(const String& headers, ResourceResponse& respons
PassRefPtr<ApplicationCache> ApplicationCacheStorage::loadCache(unsigned storageID)
{
SQLiteStatement cacheStatement(m_database,
- "SELECT url, type, mimeType, textEncodingName, headers, CacheResourceData.data FROM CacheEntries INNER JOIN CacheResources ON CacheEntries.resource=CacheResources.id "
+ "SELECT url, 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) {
LOG_ERROR("Could not prepare cache statement, error \"%s\"", m_database.lastErrorMsg());
@@ -1013,7 +1065,9 @@ PassRefPtr<ApplicationCache> ApplicationCacheStorage::loadCache(unsigned storage
cacheStatement.bindInt64(1, storageID);
RefPtr<ApplicationCache> cache = ApplicationCache::create();
-
+
+ String flatFileDirectory = pathByAppendingComponent(m_cacheDirectory, flatFileSubdirectory);
+
int result;
while ((result = cacheStatement.step()) == SQLResultRow) {
KURL url(ParsedURLString, cacheStatement.getColumnText(0));
@@ -1025,15 +1079,24 @@ PassRefPtr<ApplicationCache> ApplicationCacheStorage::loadCache(unsigned storage
RefPtr<SharedBuffer> data = SharedBuffer::adoptVector(blob);
+ String path = cacheStatement.getColumnText(6);
+ long long size = 0;
+ if (path.isEmpty())
+ size = data->size();
+ else {
+ path = pathByAppendingComponent(flatFileDirectory, path);
+ getFileSize(path, size);
+ }
+
String mimeType = cacheStatement.getColumnText(2);
String textEncodingName = cacheStatement.getColumnText(3);
- ResourceResponse response(url, mimeType, data->size(), textEncodingName, "");
+ ResourceResponse response(url, mimeType, size, textEncodingName, "");
String headers = cacheStatement.getColumnText(4);
parseHeaders(headers, response);
- RefPtr<ApplicationCacheResource> resource = ApplicationCacheResource::create(url, response, type, data.release());
+ RefPtr<ApplicationCacheResource> resource = ApplicationCacheResource::create(url, response, type, data.release(), path);
if (type & ApplicationCacheResource::Manifest)
cache->setManifestResource(resource.release());
@@ -1127,6 +1190,8 @@ void ApplicationCacheStorage::remove(ApplicationCache* cache)
cache->group()->clearStorageID();
}
+
+ checkForDeletedResources();
}
void ApplicationCacheStorage::empty()
@@ -1147,7 +1212,51 @@ void ApplicationCacheStorage::empty()
CacheGroupMap::const_iterator end = m_cachesInMemory.end();
for (CacheGroupMap::const_iterator it = m_cachesInMemory.begin(); it != end; ++it)
it->second->clearStorageID();
-}
+
+ checkForDeletedResources();
+}
+
+void ApplicationCacheStorage::deleteTables()
+{
+ empty();
+ m_database.clearAllTables();
+}
+
+bool ApplicationCacheStorage::shouldStoreResourceAsFlatFile(ApplicationCacheResource* resource)
+{
+ return resource->response().mimeType().startsWith("audio/", false)
+ || resource->response().mimeType().startsWith("video/", false);
+}
+
+bool ApplicationCacheStorage::writeDataToUniqueFileInDirectory(SharedBuffer* data, const String& directory, String& path)
+{
+ String fullPath;
+
+ do {
+ path = encodeForFileName(createCanonicalUUIDString());
+ // 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));
+
+ PlatformFileHandle handle = openFile(fullPath, OpenForWrite);
+ if (!handle)
+ return false;
+
+ int64_t writtenBytes = writeToFile(handle, data->data(), data->size());
+ closeFile(handle);
+
+ if (writtenBytes != static_cast<int64_t>(data->size())) {
+ deleteFile(fullPath);
+ return false;
+ }
+
+ return true;
+}
bool ApplicationCacheStorage::storeCopyOfCache(const String& cacheDirectory, ApplicationCacheHost* cacheHost)
{
@@ -1166,7 +1275,7 @@ bool ApplicationCacheStorage::storeCopyOfCache(const String& cacheDirectory, App
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());
+ RefPtr<ApplicationCacheResource> resourceCopy = ApplicationCacheResource::create(resource->url(), resource->response(), resource->type(), resource->data(), resource->path());
cacheCopy->addResource(resourceCopy.release());
}
@@ -1274,6 +1383,9 @@ bool ApplicationCacheStorage::deleteCacheGroup(const String& manifestURL)
}
deleteTransaction.commit();
+
+ checkForDeletedResources();
+
return true;
}
@@ -1291,25 +1403,73 @@ void ApplicationCacheStorage::checkForMaxSizeReached()
if (m_database.lastError() == SQLResultFull)
m_isMaximumSizeReached = true;
}
-
-void ApplicationCacheStorage::getOriginsWithCache(HashSet<RefPtr<SecurityOrigin>, SecurityOriginHash>& origins)
+
+void ApplicationCacheStorage::checkForDeletedResources()
{
- Vector<KURL> urls;
- if (!manifestURLs(&urls)) {
- LOG_ERROR("Failed to retrieve ApplicationCache manifest URLs");
+ openDatabase(false);
+ if (!m_database.isOpen())
return;
+
+ // Select only the paths in DeletedCacheResources that do not also appear in CacheResourceData:
+ SQLiteStatement selectPaths(m_database, "SELECT DeletedCacheResources.path "
+ "FROM DeletedCacheResources "
+ "LEFT JOIN CacheResourceData "
+ "ON DeletedCacheResources.path = CacheResourceData.path "
+ "WHERE (SELECT DeletedCacheResources.path == CacheResourceData.path) IS NULL");
+
+ if (selectPaths.prepare() != SQLResultOk)
+ return;
+
+ if (selectPaths.step() != SQLResultRow)
+ return;
+
+ do {
+ String path = selectPaths.getColumnText(0);
+ if (path.isEmpty())
+ continue;
+
+ String flatFileDirectory = pathByAppendingComponent(m_cacheDirectory, flatFileSubdirectory);
+ String fullPath = 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)
+ continue;
+
+ deleteFile(fullPath);
+ } while (selectPaths.step() == SQLResultRow);
+
+ executeSQLCommand("DELETE FROM DeletedCacheResources");
+}
+
+long long ApplicationCacheStorage::flatFileAreaSize()
+{
+ openDatabase(false);
+ if (!m_database.isOpen())
+ return 0;
+
+ SQLiteStatement selectPaths(m_database, "SELECT path FROM CacheResourceData WHERE path NOT NULL");
+
+ if (selectPaths.prepare() != SQLResultOk) {
+ LOG_ERROR("Could not load flat file cache resource data, error \"%s\"", m_database.lastErrorMsg());
+ return 0;
}
- // 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);
+ long long totalSize = 0;
+ String flatFileDirectory = pathByAppendingComponent(m_cacheDirectory, flatFileSubdirectory);
+ while (selectPaths.step() == SQLResultRow) {
+ String path = selectPaths.getColumnText(0);
+ String fullPath = pathByAppendingComponent(flatFileDirectory, path);
+ long long pathSize = 0;
+ if (!getFileSize(fullPath, pathSize))
+ continue;
+ totalSize += pathSize;
}
+
+ return totalSize;
}
-void ApplicationCacheStorage::deleteEntriesForOrigin(SecurityOrigin* origin)
+void ApplicationCacheStorage::getOriginsWithCache(HashSet<RefPtr<SecurityOrigin>, SecurityOriginHash>& origins)
{
Vector<KURL> urls;
if (!manifestURLs(&urls)) {
@@ -1318,12 +1478,11 @@ void ApplicationCacheStorage::deleteEntriesForOrigin(SecurityOrigin* origin)
}
// 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 deleting by origin.
+ // 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> manifestOrigin = SecurityOrigin::create(urls[i]);
- if (manifestOrigin->isSameSchemeHostPort(origin))
- deleteCacheGroup(urls[i]);
+ RefPtr<SecurityOrigin> origin = SecurityOrigin::create(urls[i]);
+ origins.add(origin);
}
}
diff --git a/Source/WebCore/loader/appcache/ApplicationCacheStorage.h b/Source/WebCore/loader/appcache/ApplicationCacheStorage.h
index f283670..3136b52 100644
--- a/Source/WebCore/loader/appcache/ApplicationCacheStorage.h
+++ b/Source/WebCore/loader/appcache/ApplicationCacheStorage.h
@@ -74,6 +74,7 @@ public:
ApplicationCacheGroup* fallbackCacheGroupForURL(const KURL&); // Cache that has a fallback entry to load a main resource from if normal loading fails.
ApplicationCacheGroup* findOrCreateCacheGroup(const KURL& manifestURL);
+ ApplicationCacheGroup* findInMemoryCacheGroup(const KURL& manifestURL) const;
void cacheGroupDestroyed(ApplicationCacheGroup*);
void cacheGroupMadeObsolete(ApplicationCacheGroup*);
@@ -95,7 +96,6 @@ public:
void vacuumDatabaseFile();
void getOriginsWithCache(HashSet<RefPtr<SecurityOrigin>, SecurityOriginHash>&);
- void deleteEntriesForOrigin(SecurityOrigin*);
void deleteAllEntries();
static int64_t unknownQuota() { return -1; }
@@ -113,6 +113,9 @@ private:
bool store(ApplicationCacheResource*, unsigned cacheStorageID);
bool ensureOriginRecord(const SecurityOrigin*);
+ bool shouldStoreResourceAsFlatFile(ApplicationCacheResource*);
+ void deleteTables();
+ bool writeDataToUniqueFileInDirectory(SharedBuffer*, const String& directory, String& outFilename);
void loadManifestHostHashes();
@@ -124,6 +127,8 @@ private:
bool executeSQLCommand(const String&);
void checkForMaxSizeReached();
+ void checkForDeletedResources();
+ long long flatFileAreaSize();
String m_cacheDirectory;
String m_cacheFile;