diff options
Diffstat (limited to 'WebCore/loader/appcache/ApplicationCacheGroup.cpp')
-rw-r--r-- | WebCore/loader/appcache/ApplicationCacheGroup.cpp | 112 |
1 files changed, 86 insertions, 26 deletions
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(); } - + } |