summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrei Popescu <andreip@google.com>2009-07-21 13:10:06 +0100
committerAndrei Popescu <andreip@google.com>2009-07-21 13:31:30 +0100
commitc60802dd50f86c37e0596d41c3ef6fc2c8804da4 (patch)
treeef54137cbf064976e5f146c125691c40fb1136d3
parentce39e03a248f9bee3e746c15e7961b3e40a871ed (diff)
downloadexternal_webkit-c60802dd50f86c37e0596d41c3ef6fc2c8804da4.zip
external_webkit-c60802dd50f86c37e0596d41c3ef6fc2c8804da4.tar.gz
external_webkit-c60802dd50f86c37e0596d41c3ef6fc2c8804da4.tar.bz2
Implements a mechanism that limit the growth of the application cache
-rw-r--r--WebCore/WebCore.base.exp2
-rw-r--r--WebCore/loader/EmptyClients.h4
-rw-r--r--WebCore/loader/appcache/ApplicationCache.cpp9
-rw-r--r--WebCore/loader/appcache/ApplicationCache.h7
-rw-r--r--WebCore/loader/appcache/ApplicationCacheGroup.cpp112
-rw-r--r--WebCore/loader/appcache/ApplicationCacheGroup.h9
-rw-r--r--WebCore/loader/appcache/ApplicationCacheResource.cpp23
-rw-r--r--WebCore/loader/appcache/ApplicationCacheResource.h2
-rw-r--r--WebCore/loader/appcache/ApplicationCacheStorage.cpp240
-rw-r--r--WebCore/loader/appcache/ApplicationCacheStorage.h30
-rw-r--r--WebCore/page/ChromeClient.h9
-rw-r--r--WebCore/platform/sql/SQLiteDatabase.cpp12
-rw-r--r--WebCore/platform/sql/SQLiteDatabase.h3
-rw-r--r--WebKit/android/WebCoreSupport/ChromeClientAndroid.cpp7
-rw-r--r--WebKit/android/WebCoreSupport/ChromeClientAndroid.h3
-rw-r--r--WebKit/gtk/WebCoreSupport/ChromeClientGtk.cpp8
-rw-r--r--WebKit/gtk/WebCoreSupport/ChromeClientGtk.h3
-rw-r--r--WebKit/mac/WebCoreSupport/WebChromeClient.h3
-rw-r--r--WebKit/mac/WebCoreSupport/WebChromeClient.mm7
-rw-r--r--WebKit/mac/WebKit.exp1
-rw-r--r--WebKit/qt/WebCoreSupport/ChromeClientQt.cpp8
-rw-r--r--WebKit/qt/WebCoreSupport/ChromeClientQt.h3
-rw-r--r--WebKit/win/WebCoreSupport/WebChromeClient.cpp9
-rw-r--r--WebKit/win/WebCoreSupport/WebChromeClient.h4
-rw-r--r--WebKit/wx/WebKitSupport/ChromeClientWx.cpp7
-rw-r--r--WebKit/wx/WebKitSupport/ChromeClientWx.h5
-rw-r--r--WebKitTools/DumpRenderTree/LayoutTestController.cpp17
-rw-r--r--WebKitTools/DumpRenderTree/LayoutTestController.h1
-rw-r--r--WebKitTools/DumpRenderTree/gtk/LayoutTestControllerGtk.cpp5
-rw-r--r--WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm6
-rw-r--r--WebKitTools/DumpRenderTree/win/LayoutTestControllerWin.cpp5
31 files changed, 509 insertions, 55 deletions
diff --git a/WebCore/WebCore.base.exp b/WebCore/WebCore.base.exp
index 1ce477e..6bcc46e 100644
--- a/WebCore/WebCore.base.exp
+++ b/WebCore/WebCore.base.exp
@@ -437,6 +437,8 @@ __ZN7WebCore22externalRepresentationEPNS_12RenderObjectE
__ZN7WebCore23ApplicationCacheStorage16storeCopyOfCacheERKNS_6StringEPNS_16ApplicationCacheE
__ZN7WebCore23ApplicationCacheStorage17setCacheDirectoryERKNS_6StringE
__ZN7WebCore23ApplicationCacheStorage5emptyEv
+__ZN7WebCore23ApplicationCacheStorage14setMaximumSizeEx
+__ZN7WebCore23ApplicationCacheStorage18vacuumDatabaseFileEv
__ZN7WebCore23ReplaceSelectionCommandC1EPNS_8DocumentEN3WTF10PassRefPtrINS_16DocumentFragmentEEEbbbbbNS_10EditActionE
__ZN7WebCore23createFragmentFromNodesEPNS_8DocumentERKN3WTF6VectorIPNS_4NodeELm0EEE
__ZN7WebCore24BinaryPropertyListWriter17writePropertyListEv
diff --git a/WebCore/loader/EmptyClients.h b/WebCore/loader/EmptyClients.h
index f6916ef..f93a878 100644
--- a/WebCore/loader/EmptyClients.h
+++ b/WebCore/loader/EmptyClients.h
@@ -133,6 +133,10 @@ public:
virtual void exceededDatabaseQuota(Frame*, const String&) { }
#endif
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ virtual void reachedMaxAppCacheSize(int64_t) { }
+#endif
+
virtual void runOpenPanel(Frame*, PassRefPtr<FileChooser>) { }
virtual void formStateDidChange(const Node*) { }
diff --git a/WebCore/loader/appcache/ApplicationCache.cpp b/WebCore/loader/appcache/ApplicationCache.cpp
index 42f5b6a..3c29d68 100644
--- a/WebCore/loader/appcache/ApplicationCache.cpp
+++ b/WebCore/loader/appcache/ApplicationCache.cpp
@@ -39,6 +39,7 @@ namespace WebCore {
ApplicationCache::ApplicationCache()
: m_group(0)
, m_manifest(0)
+ , m_estimatedSizeInStorage(0)
, m_storageID(0)
{
}
@@ -86,7 +87,9 @@ void ApplicationCache::addResource(PassRefPtr<ApplicationCacheResource> resource
// Add the resource to the storage.
cacheStorage().store(resource.get(), this);
}
-
+
+ m_estimatedSizeInStorage += resource->estimatedSizeInStorage();
+
m_resources.set(url, resource);
}
@@ -100,7 +103,9 @@ unsigned ApplicationCache::removeResource(const String& url)
unsigned type = it->second->type();
m_resources.remove(it);
-
+
+ m_estimatedSizeInStorage -= it->second->estimatedSizeInStorage();
+
return type;
}
diff --git a/WebCore/loader/appcache/ApplicationCache.h b/WebCore/loader/appcache/ApplicationCache.h
index afdab27..4566471 100644
--- a/WebCore/loader/appcache/ApplicationCache.h
+++ b/WebCore/loader/appcache/ApplicationCache.h
@@ -93,6 +93,8 @@ public:
static bool requestIsHTTPOrHTTPSGet(const ResourceRequest&);
+ int64_t estimatedSizeInStorage() const { return m_estimatedSizeInStorage; }
+
private:
ApplicationCache();
@@ -106,6 +108,11 @@ private:
// While an update is in progress, changes in dynamic entries are queued for later execution.
Vector<std::pair<KURL, bool> > m_pendingDynamicEntryActions;
+ // The total size of the resources belonging to this Application Cache instance.
+ // This is an estimation of the size this Application Cache occupies in the
+ // database file.
+ int64_t m_estimatedSizeInStorage;
+
unsigned m_storageID;
};
diff --git a/WebCore/loader/appcache/ApplicationCacheGroup.cpp b/WebCore/loader/appcache/ApplicationCacheGroup.cpp
index 9f2f6a4..735e3a3 100644
--- a/WebCore/loader/appcache/ApplicationCacheGroup.cpp
+++ b/WebCore/loader/appcache/ApplicationCacheGroup.cpp
@@ -31,6 +31,7 @@
#include "ApplicationCache.h"
#include "ApplicationCacheResource.h"
#include "ApplicationCacheStorage.h"
+#include "ChromeClient.h"
#include "DocumentLoader.h"
#include "DOMApplicationCache.h"
#include "DOMWindow.h"
@@ -53,6 +54,7 @@ ApplicationCacheGroup::ApplicationCacheGroup(const KURL& manifestURL, bool isCop
, m_isObsolete(false)
, m_completionType(None)
, m_isCopy(isCopy)
+ , m_calledReachedMaxAppCacheSize(false)
{
}
@@ -650,6 +652,15 @@ void ApplicationCacheGroup::didFinishLoadingManifest()
startLoadingEntry();
}
+void ApplicationCacheGroup::didReachMaxAppCacheSize()
+{
+ ASSERT(m_frame);
+ ASSERT(m_cacheBeingUpdated);
+ m_frame->page()->chrome()->client()->reachedMaxAppCacheSize(cacheStorage().spaceNeeded(m_cacheBeingUpdated->estimatedSizeInStorage()));
+ m_calledReachedMaxAppCacheSize = true;
+ checkIfLoadIsComplete();
+}
+
void ApplicationCacheGroup::cacheUpdateFailed()
{
stopLoading();
@@ -728,7 +739,15 @@ void ApplicationCacheGroup::checkIfLoadIsComplete()
// FIXME: Fetch the resource from manifest URL again, and check whether it is identical to the one used for update (in case the application was upgraded server-side in the meanwhile). (<rdar://problem/6467625>)
ASSERT(m_cacheBeingUpdated);
- m_cacheBeingUpdated->setManifestResource(m_manifestResource.release());
+ if (m_manifestResource)
+ m_cacheBeingUpdated->setManifestResource(m_manifestResource.release());
+ else {
+ // We can get here as a result of retrying the Complete step, following
+ // a failure of the cache storage to save the newest cache due to hitting
+ // the maximum size. In such a case, m_manifestResource may be 0, as
+ // the manifest was already set on the newest cache object.
+ ASSERT(cacheStorage().isMaximumSizeReached() && m_calledReachedMaxAppCacheSize);
+ }
RefPtr<ApplicationCache> oldNewestCache = (m_newestCache == m_cacheBeingUpdated) ? 0 : m_newestCache;
@@ -739,29 +758,42 @@ void ApplicationCacheGroup::checkIfLoadIsComplete()
cacheStorage().remove(oldNewestCache.get());
// Fire the success events.
postListenerTask(isUpgradeAttempt ? &DOMApplicationCache::callUpdateReadyListener : &DOMApplicationCache::callCachedListener, m_associatedDocumentLoaders);
- } else {
- // Run the "cache failure steps"
- // Fire the error events to all pending master entries, as well any other cache hosts
- // currently associated with a cache in this group.
- postListenerTask(&DOMApplicationCache::callErrorListener, m_associatedDocumentLoaders);
- // Disassociate the pending master entries from the failed new cache. Note that
- // all other loaders in the m_associatedDocumentLoaders are still associated with
- // some other cache in this group. They are not associated with the failed new cache.
-
- // Need to copy loaders, because the cache group may be destroyed at the end of iteration.
- Vector<DocumentLoader*> loaders;
- copyToVector(m_pendingMasterResourceLoaders, loaders);
- size_t count = loaders.size();
- for (size_t i = 0; i != count; ++i)
- disassociateDocumentLoader(loaders[i]); // This can delete this group.
-
- // Reinstate the oldNewestCache, if there was one.
- if (oldNewestCache) {
- // This will discard the failed new cache.
- setNewestCache(oldNewestCache.release());
- } else {
- // We must have been deleted by the last call to disassociateDocumentLoader().
- return;
+ } else {
+ if (cacheStorage().isMaximumSizeReached() && !m_calledReachedMaxAppCacheSize) {
+ // We ran out of space. All the changes in the cache storage have
+ // been rolled back. We roll back to the previous state in here,
+ // as well, call the chrome client asynchronously and retry to
+ // save the new cache.
+ // Save a reference to the new cache.
+ m_cacheBeingUpdated = m_newestCache.release();
+ if (oldNewestCache) {
+ // Reinstate the oldNewestCache.
+ setNewestCache(oldNewestCache.release());
+ }
+ scheduleReachedMaxAppCacheSizeCallback();
+ return;
+ } else {
+ // Run the "cache failure steps"
+ // Fire the error events to all pending master entries, as well any other cache hosts
+ // currently associated with a cache in this group.
+ postListenerTask(&DOMApplicationCache::callErrorListener, m_associatedDocumentLoaders);
+ // Disassociate the pending master entries from the failed new cache. Note that
+ // all other loaders in the m_associatedDocumentLoaders are still associated with
+ // some other cache in this group. They are not associated with the failed new cache.
+ // Need to copy loaders, because the cache group may be destroyed at the end of iteration.
+ Vector<DocumentLoader*> loaders;
+ copyToVector(m_pendingMasterResourceLoaders, loaders);
+ size_t count = loaders.size();
+ for (size_t i = 0; i != count; ++i)
+ disassociateDocumentLoader(loaders[i]); // This can delete this group.
+ // Reinstate the oldNewestCache, if there was one.
+ if (oldNewestCache) {
+ // This will discard the failed new cache.
+ setNewestCache(oldNewestCache.release());
+ } else {
+ // We must have been deleted by the last call to disassociateDocumentLoader().
+ return;
+ }
}
}
break;
@@ -773,6 +805,7 @@ void ApplicationCacheGroup::checkIfLoadIsComplete()
m_completionType = None;
m_updateStatus = Idle;
m_frame = 0;
+ m_calledReachedMaxAppCacheSize = false;
}
void ApplicationCacheGroup::startLoadingEntry()
@@ -855,7 +888,34 @@ void ApplicationCacheGroup::associateDocumentLoaderWithCache(DocumentLoader* loa
ASSERT(!m_associatedDocumentLoaders.contains(loader));
m_associatedDocumentLoaders.add(loader);
}
-
+
+class ChromeClientCallbackTimer: public TimerBase {
+public:
+ ChromeClientCallbackTimer(ApplicationCacheGroup* cacheGroup)
+ : m_cacheGroup(cacheGroup)
+ {
+ }
+
+private:
+ virtual void fired()
+ {
+ m_cacheGroup->didReachMaxAppCacheSize();
+ delete this;
+ }
+ // Note that there is no need to use a RefPtr here. The ApplicationCacheGroup instance is guaranteed
+ // to be alive when the timer fires since invoking the ChromeClient callback is part of its normal
+ // update machinery and nothing can yet cause it to get deleted.
+ ApplicationCacheGroup* m_cacheGroup;
+};
+
+void ApplicationCacheGroup::scheduleReachedMaxAppCacheSizeCallback()
+{
+ ASSERT(isMainThread());
+ ChromeClientCallbackTimer* timer = new ChromeClientCallbackTimer(this);
+ timer->startOneShot(0);
+ // The timer will delete itself once it fires.
+}
+
class CallCacheListenerTask : public ScriptExecutionContext::Task {
typedef void (DOMApplicationCache::*ListenerFunction)();
public:
@@ -908,7 +968,7 @@ void ApplicationCacheGroup::clearStorageID()
for (HashSet<ApplicationCache*>::const_iterator it = m_caches.begin(); it != end; ++it)
(*it)->clearStorageID();
}
-
+
}
diff --git a/WebCore/loader/appcache/ApplicationCacheGroup.h b/WebCore/loader/appcache/ApplicationCacheGroup.h
index 063fb3b..281dadf 100644
--- a/WebCore/loader/appcache/ApplicationCacheGroup.h
+++ b/WebCore/loader/appcache/ApplicationCacheGroup.h
@@ -95,6 +95,7 @@ private:
static void postListenerTask(ListenerFunction, const HashSet<DocumentLoader*>&);
static void postListenerTask(ListenerFunction, const Vector<RefPtr<DocumentLoader> >& loaders);
static void postListenerTask(ListenerFunction, DocumentLoader*);
+ void scheduleReachedMaxAppCacheSizeCallback();
PassRefPtr<ResourceHandle> createResourceHandle(const KURL&, ApplicationCacheResource* newestCachedResource);
@@ -106,6 +107,7 @@ private:
void didReceiveManifestResponse(const ResourceResponse&);
void didReceiveManifestData(const char*, int);
void didFinishLoadingManifest();
+ void didReachMaxAppCacheSize();
void startLoadingEntry();
void deliverDelayedMainResources();
@@ -163,12 +165,19 @@ private:
// Whether this cache group is a copy that's only used for transferring the cache to another file.
bool m_isCopy;
+
+ // This flag is set immediately after the ChromeClient::reachedMaxAppCacheSize() callback is invoked as a result of the storage layer failing to save a cache
+ // due to reaching the maximum size of the application cache database file. This flag is used by ApplicationCacheGroup::checkIfLoadIsComplete() to decide
+ // the course of action in case of this failure (i.e. call the ChromeClient callback or run the failure steps).
+ bool m_calledReachedMaxAppCacheSize;
RefPtr<ResourceHandle> m_currentHandle;
RefPtr<ApplicationCacheResource> m_currentResource;
RefPtr<ApplicationCacheResource> m_manifestResource;
RefPtr<ResourceHandle> m_manifestHandle;
+
+ friend class ChromeClientCallbackTimer;
};
} // namespace WebCore
diff --git a/WebCore/loader/appcache/ApplicationCacheResource.cpp b/WebCore/loader/appcache/ApplicationCacheResource.cpp
index 7c1241b..90e65ad 100644
--- a/WebCore/loader/appcache/ApplicationCacheResource.cpp
+++ b/WebCore/loader/appcache/ApplicationCacheResource.cpp
@@ -35,6 +35,7 @@ ApplicationCacheResource::ApplicationCacheResource(const KURL& url, const Resour
: SubstituteResource(url, response, data)
, m_type(type)
, m_storageID(0)
+ , m_estimatedSizeInStorage(0)
{
}
@@ -44,6 +45,28 @@ void ApplicationCacheResource::addType(unsigned type)
m_type |= type;
}
+int64_t ApplicationCacheResource::estimatedSizeInStorage()
+{
+ if (m_estimatedSizeInStorage)
+ return m_estimatedSizeInStorage;
+
+ if (data())
+ m_estimatedSizeInStorage = data()->size();
+
+ HTTPHeaderMap::const_iterator end = response().httpHeaderFields().end();
+ for (HTTPHeaderMap::const_iterator it = response().httpHeaderFields().begin(); it != end; ++it)
+ m_estimatedSizeInStorage += (it->first.length() + it->second.length() + 2) * sizeof(UChar);
+
+ m_estimatedSizeInStorage += url().string().length() * sizeof(UChar);
+ m_estimatedSizeInStorage += sizeof(int); // response().m_httpStatusCode
+ m_estimatedSizeInStorage += response().url().string().length() * sizeof(UChar);
+ m_estimatedSizeInStorage += sizeof(unsigned); // dataId
+ m_estimatedSizeInStorage += response().mimeType().length() * sizeof(UChar);
+ m_estimatedSizeInStorage += response().textEncodingName().length() * sizeof(UChar);
+
+ return m_estimatedSizeInStorage;
+}
+
#ifndef NDEBUG
void ApplicationCacheResource::dumpType(unsigned type)
{
diff --git a/WebCore/loader/appcache/ApplicationCacheResource.h b/WebCore/loader/appcache/ApplicationCacheResource.h
index 28d8280..53fdd1d 100644
--- a/WebCore/loader/appcache/ApplicationCacheResource.h
+++ b/WebCore/loader/appcache/ApplicationCacheResource.h
@@ -54,6 +54,7 @@ public:
void setStorageID(unsigned storageID) { m_storageID = storageID; }
unsigned storageID() const { return m_storageID; }
void clearStorageID() { m_storageID = 0; }
+ int64_t estimatedSizeInStorage();
#ifndef NDEBUG
static void dumpType(unsigned type);
@@ -64,6 +65,7 @@ private:
unsigned m_type;
unsigned m_storageID;
+ int64_t m_estimatedSizeInStorage;
};
} // namespace WebCore
diff --git a/WebCore/loader/appcache/ApplicationCacheStorage.cpp b/WebCore/loader/appcache/ApplicationCacheStorage.cpp
index 1c59581..a1dad15 100644
--- a/WebCore/loader/appcache/ApplicationCacheStorage.cpp
+++ b/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;
};
@@ -223,6 +224,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)
@@ -353,6 +356,56 @@ 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;
+ int64_t currentSize = 0;
+ if (!getFileSize(m_cacheFile, currentSize))
+ return 0;
+
+ // 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 +419,7 @@ bool ApplicationCacheStorage::executeSQLCommand(const String& sql)
return result;
}
-static const int schemaVersion = 3;
+static const int schemaVersion = 4;
void ApplicationCacheStorage::verifySchemaVersion()
{
@@ -400,13 +453,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 +469,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 +509,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 +525,7 @@ bool ApplicationCacheStorage::store(ApplicationCacheGroup* group)
return false;
group->setStorageID(static_cast<unsigned>(m_database.lastInsertRowID()));
+ journal->add(group, 0);
return true;
}
@@ -480,11 +535,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 +637,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());
@@ -629,33 +688,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());
@@ -665,11 +747,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.
@@ -683,7 +767,8 @@ bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup* group)
if (!executeStatement(statement))
return false;
- storageIDJournal.commit();
+ groupStorageIDJournal.commit();
+ resourceStorageIDJournal.commit();
storeCacheTransaction.commit();
return true;
}
@@ -879,7 +964,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, ());
diff --git a/WebCore/loader/appcache/ApplicationCacheStorage.h b/WebCore/loader/appcache/ApplicationCacheStorage.h
index b13b596..c6d687e 100644
--- a/WebCore/loader/appcache/ApplicationCacheStorage.h
+++ b/WebCore/loader/appcache/ApplicationCacheStorage.h
@@ -40,13 +40,19 @@ class ApplicationCache;
class ApplicationCacheGroup;
class ApplicationCacheResource;
class KURL;
-class ResourceStorageIDJournal;
-
+template <class T>
+class StorageIDJournal;
+
class ApplicationCacheStorage {
public:
void setCacheDirectory(const String&);
const String& cacheDirectory() const;
+ void setMaximumSize(int64_t size);
+ int64_t maximumSize() const;
+ bool isMaximumSizeReached() const;
+ int64_t spaceNeeded(int64_t cacheToSave);
+
ApplicationCacheGroup* cacheGroupForURL(const KURL&); // Cache to load a main resource from.
ApplicationCacheGroup* fallbackCacheGroupForURL(const KURL&); // Cache that has a fallback entry to load a main resource from if normal loading fails.
@@ -55,7 +61,7 @@ public:
void cacheGroupMadeObsolete(ApplicationCacheGroup*);
bool storeNewestCache(ApplicationCacheGroup*); // Updates the cache group, but doesn't remove old cache.
- void store(ApplicationCacheResource*, ApplicationCache*);
+ bool store(ApplicationCacheResource*, ApplicationCache*);
bool storeUpdatedType(ApplicationCacheResource*, ApplicationCache*);
// Removes the group if the cache to be removed is the newest one (so, storeNewestCache() needs to be called beforehand when updating).
@@ -65,11 +71,19 @@ public:
static bool storeCopyOfCache(const String& cacheDirectory, ApplicationCache*);
+ bool manifestURLs(Vector<KURL>* urls);
+ bool cacheGroupSize(const String& manifestURL, int64_t* size);
+ bool deleteCacheGroup(const String& manifestURL);
+ void vacuumDatabaseFile();
private:
+ ApplicationCacheStorage();
PassRefPtr<ApplicationCache> loadCache(unsigned storageID);
ApplicationCacheGroup* loadCacheGroup(const KURL& manifestURL);
- bool store(ApplicationCacheGroup*);
+ typedef StorageIDJournal<ApplicationCacheResource> ResourceStorageIDJournal;
+ typedef StorageIDJournal<ApplicationCacheGroup> GroupStorageIDJournal;
+
+ bool store(ApplicationCacheGroup*, GroupStorageIDJournal*);
bool store(ApplicationCache*, ResourceStorageIDJournal*);
bool store(ApplicationCacheResource*, unsigned cacheStorageID);
@@ -81,8 +95,14 @@ private:
bool executeStatement(SQLiteStatement&);
bool executeSQLCommand(const String&);
+
+ void checkForMaxSizeReached();
String m_cacheDirectory;
+ String m_cacheFile;
+
+ int64_t m_maximumSize;
+ bool m_isMaximumSizeReached;
SQLiteDatabase m_database;
@@ -92,6 +112,8 @@ private:
typedef HashMap<String, ApplicationCacheGroup*> CacheGroupMap;
CacheGroupMap m_cachesInMemory; // Excludes obsolete cache groups.
+
+ friend ApplicationCacheStorage& cacheStorage();
};
ApplicationCacheStorage& cacheStorage();
diff --git a/WebCore/page/ChromeClient.h b/WebCore/page/ChromeClient.h
index e155754..46f37f3 100644
--- a/WebCore/page/ChromeClient.h
+++ b/WebCore/page/ChromeClient.h
@@ -139,6 +139,15 @@ namespace WebCore {
virtual void exceededDatabaseQuota(Frame*, const String& databaseName) = 0;
#endif
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ // Callback invoked when the application cache fails to save a cache object
+ // because storing it would grow the database file past its defined maximum
+ // size or past the amount of free space on the device.
+ // The chrome client would need to take some action such as evicting some
+ // old caches.
+ virtual void reachedMaxAppCacheSize(int64_t spaceNeeded) = 0;
+#endif
+
#if ENABLE(DASHBOARD_SUPPORT)
virtual void dashboardRegionsChanged();
#endif
diff --git a/WebCore/platform/sql/SQLiteDatabase.cpp b/WebCore/platform/sql/SQLiteDatabase.cpp
index 702cf02..e16b04e 100644
--- a/WebCore/platform/sql/SQLiteDatabase.cpp
+++ b/WebCore/platform/sql/SQLiteDatabase.cpp
@@ -151,6 +151,18 @@ int SQLiteDatabase::pageSize()
return m_pageSize;
}
+int64_t SQLiteDatabase::freeSpaceSize()
+{
+ MutexLocker locker(m_authorizerLock);
+ enableAuthorizer(false);
+ // Note: freelist_count was added in SQLite 3.4.1.
+ SQLiteStatement statement(*this, "PRAGMA freelist_count");
+ int64_t size = statement.getColumnInt64(0) * pageSize();
+
+ enableAuthorizer(true);
+ return size;
+}
+
void SQLiteDatabase::setSynchronous(SynchronousPragma sync)
{
executeCommand(String::format("PRAGMA synchronous = %i", sync));
diff --git a/WebCore/platform/sql/SQLiteDatabase.h b/WebCore/platform/sql/SQLiteDatabase.h
index 76700dc..d313435 100644
--- a/WebCore/platform/sql/SQLiteDatabase.h
+++ b/WebCore/platform/sql/SQLiteDatabase.h
@@ -83,6 +83,9 @@ public:
int64_t maximumSize();
void setMaximumSize(int64_t);
+ // Gets the number of unused bytes in the database file.
+ int64_t freeSpaceSize();
+
// The SQLite SYNCHRONOUS pragma can be either FULL, NORMAL, or OFF
// FULL - Any writing calls to the DB block until the data is actually on the disk surface
// NORMAL - SQLite pauses at some critical moments when writing, but much less than FULL
diff --git a/WebKit/android/WebCoreSupport/ChromeClientAndroid.cpp b/WebKit/android/WebCoreSupport/ChromeClientAndroid.cpp
index 39bc004..71d9f59 100644
--- a/WebKit/android/WebCoreSupport/ChromeClientAndroid.cpp
+++ b/WebKit/android/WebCoreSupport/ChromeClientAndroid.cpp
@@ -311,6 +311,13 @@ void ChromeClientAndroid::exceededDatabaseQuota(Frame* frame, const String& name
}
}
#endif
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+void ChromeClientAndroid::reachedMaxAppCacheSize(int64_t spaceNeeded)
+{
+ // FIXME: Free some space.
+ notImplemented();
+}
+#endif
void ChromeClientAndroid::requestGeolocationPermissionForFrame(Frame*, Geolocation*) { notImplemented(); }
void ChromeClientAndroid::runOpenPanel(Frame*, PassRefPtr<FileChooser>) { notImplemented(); }
diff --git a/WebKit/android/WebCoreSupport/ChromeClientAndroid.h b/WebKit/android/WebCoreSupport/ChromeClientAndroid.h
index 966d5c7..93426b8 100644
--- a/WebKit/android/WebCoreSupport/ChromeClientAndroid.h
+++ b/WebKit/android/WebCoreSupport/ChromeClientAndroid.h
@@ -111,6 +111,9 @@ namespace android {
#if ENABLE(DATABASE)
virtual void exceededDatabaseQuota(Frame*, const String&);
#endif
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ virtual void reachedMaxAppCacheSize(int64_t spaceNeeded);
+#endif
virtual void requestGeolocationPermissionForFrame(Frame*, Geolocation*);
virtual void runOpenPanel(Frame*, PassRefPtr<FileChooser>);
virtual bool setCursor(PlatformCursorHandle);
diff --git a/WebKit/gtk/WebCoreSupport/ChromeClientGtk.cpp b/WebKit/gtk/WebCoreSupport/ChromeClientGtk.cpp
index 892be74..7125305 100644
--- a/WebKit/gtk/WebCoreSupport/ChromeClientGtk.cpp
+++ b/WebKit/gtk/WebCoreSupport/ChromeClientGtk.cpp
@@ -418,6 +418,14 @@ void ChromeClient::exceededDatabaseQuota(Frame* frame, const String&)
}
#endif
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+void ChromeClient::reachedMaxAppCacheSize(int64_t spaceNeeded)
+{
+ // FIXME: Free some space.
+ notImplemented();
+}
+#endif
+
void ChromeClient::runOpenPanel(Frame*, PassRefPtr<FileChooser> prpFileChooser)
{
RefPtr<FileChooser> chooser = prpFileChooser;
diff --git a/WebKit/gtk/WebCoreSupport/ChromeClientGtk.h b/WebKit/gtk/WebCoreSupport/ChromeClientGtk.h
index fc0ea8a..bac9940 100644
--- a/WebKit/gtk/WebCoreSupport/ChromeClientGtk.h
+++ b/WebKit/gtk/WebCoreSupport/ChromeClientGtk.h
@@ -99,6 +99,9 @@ namespace WebKit {
#if ENABLE(DATABASE)
virtual void exceededDatabaseQuota(WebCore::Frame*, const WebCore::String&);
#endif
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ virtual void reachedMaxAppCacheSize(int64_t spaceNeeded);
+#endif
virtual void runOpenPanel(WebCore::Frame*, PassRefPtr<WebCore::FileChooser>);
virtual void formStateDidChange(const WebCore::Node*) { }
diff --git a/WebKit/mac/WebCoreSupport/WebChromeClient.h b/WebKit/mac/WebCoreSupport/WebChromeClient.h
index 6c3d71e..6974cb1 100644
--- a/WebKit/mac/WebCoreSupport/WebChromeClient.h
+++ b/WebKit/mac/WebCoreSupport/WebChromeClient.h
@@ -107,6 +107,9 @@ public:
#if ENABLE(DATABASE)
virtual void exceededDatabaseQuota(WebCore::Frame*, const WebCore::String& databaseName);
#endif
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ virtual void reachedMaxAppCacheSize(int64_t spaceNeeded);
+#endif
virtual void populateVisitedLinks();
#if ENABLE(DASHBOARD_SUPPORT)
diff --git a/WebKit/mac/WebCoreSupport/WebChromeClient.mm b/WebKit/mac/WebCoreSupport/WebChromeClient.mm
index 18c73e9..2ca86d1 100644
--- a/WebKit/mac/WebCoreSupport/WebChromeClient.mm
+++ b/WebKit/mac/WebCoreSupport/WebChromeClient.mm
@@ -517,6 +517,13 @@ void WebChromeClient::exceededDatabaseQuota(Frame* frame, const String& database
END_BLOCK_OBJC_EXCEPTIONS;
}
#endif
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+void WebChromeClient::reachedMaxAppCacheSize(int64_t spaceNeeded)
+{
+ // FIXME: Free some space.
+}
+#endif
void WebChromeClient::populateVisitedLinks()
{
diff --git a/WebKit/mac/WebKit.exp b/WebKit/mac/WebKit.exp
index d166894..cb26943 100644
--- a/WebKit/mac/WebKit.exp
+++ b/WebKit/mac/WebKit.exp
@@ -1,3 +1,4 @@
+.objc_class_name_WebApplicationCache
.objc_class_name_WebArchive
.objc_class_name_WebBackForwardList
.objc_class_name_WebCache
diff --git a/WebKit/qt/WebCoreSupport/ChromeClientQt.cpp b/WebKit/qt/WebCoreSupport/ChromeClientQt.cpp
index 5df554b..5199a5a 100644
--- a/WebKit/qt/WebCoreSupport/ChromeClientQt.cpp
+++ b/WebKit/qt/WebCoreSupport/ChromeClientQt.cpp
@@ -399,6 +399,14 @@ void ChromeClientQt::exceededDatabaseQuota(Frame* frame, const String& databaseN
}
#endif
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+void ChromeClientQt::reachedMaxAppCacheSize(int64_t spaceNeeded)
+{
+ // FIXME: Free some space.
+ notImplemented();
+}
+#endif
+
void ChromeClientQt::runOpenPanel(Frame* frame, PassRefPtr<FileChooser> prpFileChooser)
{
RefPtr<FileChooser> fileChooser = prpFileChooser;
diff --git a/WebKit/qt/WebCoreSupport/ChromeClientQt.h b/WebKit/qt/WebCoreSupport/ChromeClientQt.h
index 77c56fc..7922000 100644
--- a/WebKit/qt/WebCoreSupport/ChromeClientQt.h
+++ b/WebKit/qt/WebCoreSupport/ChromeClientQt.h
@@ -116,6 +116,9 @@ namespace WebCore {
#if ENABLE(DATABASE)
virtual void exceededDatabaseQuota(Frame*, const String&);
#endif
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ virtual void reachedMaxAppCacheSize(int64_t spaceNeeded);
+#endif
virtual void runOpenPanel(Frame*, PassRefPtr<FileChooser>);
virtual void formStateDidChange(const Node*) { }
diff --git a/WebKit/win/WebCoreSupport/WebChromeClient.cpp b/WebKit/win/WebCoreSupport/WebChromeClient.cpp
index 45b07cf..1f0c09c 100644
--- a/WebKit/win/WebCoreSupport/WebChromeClient.cpp
+++ b/WebKit/win/WebCoreSupport/WebChromeClient.cpp
@@ -543,6 +543,15 @@ void WebChromeClient::exceededDatabaseQuota(Frame* frame, const String& database
}
#endif
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+#include "ApplicationCacheStorage.h"
+void WebChromeClient::reachedMaxAppCacheSize(int64_t spaceNeeded)
+{
+ // FIXME: Free some space.
+ notImplemented();
+}
+#endif
+
void WebChromeClient::populateVisitedLinks()
{
WebHistory* history = WebHistory::sharedHistory();
diff --git a/WebKit/win/WebCoreSupport/WebChromeClient.h b/WebKit/win/WebCoreSupport/WebChromeClient.h
index 44c6107..4aa7422 100644
--- a/WebKit/win/WebCoreSupport/WebChromeClient.h
+++ b/WebKit/win/WebCoreSupport/WebChromeClient.h
@@ -109,6 +109,10 @@ public:
virtual void exceededDatabaseQuota(WebCore::Frame*, const WebCore::String&);
#endif
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ virtual void reachedMaxAppCacheSize(int64_t spaceNeeded);
+#endif
+
virtual void populateVisitedLinks();
virtual bool paintCustomScrollbar(WebCore::GraphicsContext*, const WebCore::FloatRect&, WebCore::ScrollbarControlSize,
diff --git a/WebKit/wx/WebKitSupport/ChromeClientWx.cpp b/WebKit/wx/WebKitSupport/ChromeClientWx.cpp
index 6dfe7a4..411f795 100644
--- a/WebKit/wx/WebKitSupport/ChromeClientWx.cpp
+++ b/WebKit/wx/WebKitSupport/ChromeClientWx.cpp
@@ -355,6 +355,13 @@ void ChromeClientWx::exceededDatabaseQuota(Frame*, const String&)
}
#endif
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+void ChromeClientWx::reachedMaxAppCacheSize(int64_t spaceNeeded)
+{
+ notImplemented();
+}
+#endif
+
void ChromeClientWx::scroll(const IntSize&, const IntRect&, const IntRect&)
{
notImplemented();
diff --git a/WebKit/wx/WebKitSupport/ChromeClientWx.h b/WebKit/wx/WebKitSupport/ChromeClientWx.h
index df1fdd8..d7f4152 100644
--- a/WebKit/wx/WebKitSupport/ChromeClientWx.h
+++ b/WebKit/wx/WebKitSupport/ChromeClientWx.h
@@ -113,6 +113,11 @@ public:
#if ENABLE(DATABASE)
virtual void exceededDatabaseQuota(Frame*, const String&);
#endif
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ virtual void reachedMaxAppCacheSize(int64_t spaceNeeded);
+#endif
+
virtual void runOpenPanel(Frame*, PassRefPtr<FileChooser>);
virtual void formStateDidChange(const Node*) { }
diff --git a/WebKitTools/DumpRenderTree/LayoutTestController.cpp b/WebKitTools/DumpRenderTree/LayoutTestController.cpp
index a78d115..2a06c4f 100644
--- a/WebKitTools/DumpRenderTree/LayoutTestController.cpp
+++ b/WebKitTools/DumpRenderTree/LayoutTestController.cpp
@@ -460,6 +460,22 @@ static JSValueRef setAcceptsEditingCallback(JSContextRef context, JSObjectRef fu
return JSValueMakeUndefined(context);
}
+static JSValueRef setAppCacheMaximumSizeCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ LayoutTestController* controller = static_cast<LayoutTestController*>(JSObjectGetPrivate(thisObject));
+
+ double size = JSValueToNumber(context, arguments[0], NULL);
+ if (!isnan(size))
+ controller->setAppCacheMaximumSize(static_cast<unsigned long long>(size));
+
+ return JSValueMakeUndefined(context);
+
+}
+
static JSValueRef setAuthorAndUserStylesEnabledCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
// Has mac & windows implementation
@@ -840,6 +856,7 @@ JSStaticFunction* LayoutTestController::staticFunctions()
{ "repaintSweepHorizontally", repaintSweepHorizontallyCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setAcceptsEditing", setAcceptsEditingCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setAuthorAndUserStylesEnabled", setAuthorAndUserStylesEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setAppCacheMaximumSize", setAppCacheMaximumSizeCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setCallCloseOnWebViews", setCallCloseOnWebViewsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setCanOpenWindows", setCanOpenWindowsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setCloseRemainingWindowsWhenComplete", setCloseRemainingWindowsWhenCompleteCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
diff --git a/WebKitTools/DumpRenderTree/LayoutTestController.h b/WebKitTools/DumpRenderTree/LayoutTestController.h
index 2de7f72..0f41923 100644
--- a/WebKitTools/DumpRenderTree/LayoutTestController.h
+++ b/WebKitTools/DumpRenderTree/LayoutTestController.h
@@ -58,6 +58,7 @@ public:
void queueReload();
void queueScript(JSStringRef url);
void setAcceptsEditing(bool acceptsEditing);
+ void setAppCacheMaximumSize(unsigned long long quota);
void setAuthorAndUserStylesEnabled(bool);
void setCustomPolicyDelegate(bool setDelegate, bool permissive);
void setDatabaseQuota(unsigned long long quota);
diff --git a/WebKitTools/DumpRenderTree/gtk/LayoutTestControllerGtk.cpp b/WebKitTools/DumpRenderTree/gtk/LayoutTestControllerGtk.cpp
index ca3bc36..5c2bae7 100644
--- a/WebKitTools/DumpRenderTree/gtk/LayoutTestControllerGtk.cpp
+++ b/WebKitTools/DumpRenderTree/gtk/LayoutTestControllerGtk.cpp
@@ -303,6 +303,11 @@ void LayoutTestController::setDatabaseQuota(unsigned long long quota)
// FIXME: implement
}
+void LayoutTestController::setAppCacheMaximumSize(unsigned long long size)
+{
+ // FIXME: implement
+}
+
bool LayoutTestController::pauseAnimationAtTimeOnElementWithId(JSStringRef animationName, double time, JSStringRef elementId)
{
gchar* name = JSStringCopyUTF8CString(animationName);
diff --git a/WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm b/WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm
index c80c78f..3aa1f2c 100644
--- a/WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm
+++ b/WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm
@@ -39,6 +39,7 @@
#import <JavaScriptCore/JSStringRefCF.h>
#import <WebKit/DOMDocument.h>
#import <WebKit/DOMElement.h>
+#import <WebKit/WebApplicationCache.h>
#import <WebKit/WebBackForwardList.h>
#import <WebKit/WebDatabaseManagerPrivate.h>
#import <WebKit/WebDataSource.h>
@@ -210,6 +211,11 @@ void LayoutTestController::setAcceptsEditing(bool newAcceptsEditing)
[(EditingDelegate *)[[mainFrame webView] editingDelegate] setAcceptsEditing:newAcceptsEditing];
}
+void LayoutTestController::setAppCacheMaximumSize(unsigned long long size)
+{
+ [WebApplicationCache setMaximumSize:size];
+}
+
void LayoutTestController::setAuthorAndUserStylesEnabled(bool flag)
{
[[[mainFrame webView] preferences] setAuthorAndUserStylesEnabled:flag];
diff --git a/WebKitTools/DumpRenderTree/win/LayoutTestControllerWin.cpp b/WebKitTools/DumpRenderTree/win/LayoutTestControllerWin.cpp
index ec5139b..56d0a80 100644
--- a/WebKitTools/DumpRenderTree/win/LayoutTestControllerWin.cpp
+++ b/WebKitTools/DumpRenderTree/win/LayoutTestControllerWin.cpp
@@ -681,6 +681,11 @@ void LayoutTestController::setDatabaseQuota(unsigned long long quota)
printf("ERROR: LayoutTestController::setDatabaseQuota() not implemented\n");
}
+void LayoutTestController::setAppCacheMaximumSize(unsigned long long size)
+{
+ printf("ERROR: LayoutTestController::setAppCacheMaximumSize() not implemented\n");
+}
+
bool LayoutTestController::pauseAnimationAtTimeOnElementWithId(JSStringRef animationName, double time, JSStringRef elementId)
{
COMPtr<IDOMDocument> document;