diff options
author | Andrei Popescu <andreip@google.com> | 2009-07-21 13:10:06 +0100 |
---|---|---|
committer | Andrei Popescu <andreip@google.com> | 2009-07-21 13:31:30 +0100 |
commit | c60802dd50f86c37e0596d41c3ef6fc2c8804da4 (patch) | |
tree | ef54137cbf064976e5f146c125691c40fb1136d3 | |
parent | ce39e03a248f9bee3e746c15e7961b3e40a871ed (diff) | |
download | external_webkit-c60802dd50f86c37e0596d41c3ef6fc2c8804da4.zip external_webkit-c60802dd50f86c37e0596d41c3ef6fc2c8804da4.tar.gz external_webkit-c60802dd50f86c37e0596d41c3ef6fc2c8804da4.tar.bz2 |
Implements a mechanism that limit the growth of the application cache
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; |