void ApplicationCacheGroup::checkIfLoadIsComplete() { if (m_manifestHandle || !m_pendingEntries.isEmpty() || m_downloadingPendingMasterResourceLoadersCount) return; // We're done, all resources have finished downloading (successfully or not). bool isUpgradeAttempt = m_newestCache; switch (m_completionType) { case None: ASSERT_NOT_REACHED(); return; case NoUpdate: ASSERT(isUpgradeAttempt); ASSERT(!m_cacheBeingUpdated); // The storage could have been manually emptied by the user. if (!m_storageID) cacheStorage().storeNewestCache(this); postListenerTask(ApplicationCacheHost::NOUPDATE_EVENT, m_associatedDocumentLoaders); break; case Failure: ASSERT(!m_cacheBeingUpdated); postListenerTask(ApplicationCacheHost::ERROR_EVENT, m_associatedDocumentLoaders); if (m_caches.isEmpty()) { ASSERT(m_associatedDocumentLoaders.isEmpty()); delete this; return; } break; case Completed: { // 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); 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) ? RefPtr<ApplicationCache>() : m_newestCache; // If we exceeded the origin quota while downloading we can request a quota // increase now, before we attempt to store the cache. int64_t totalSpaceNeeded; if (!cacheStorage().checkOriginQuota(this, oldNewestCache.get(), m_cacheBeingUpdated.get(), totalSpaceNeeded)) didReachOriginQuota(totalSpaceNeeded); ApplicationCacheStorage::FailureReason failureReason; setNewestCache(m_cacheBeingUpdated.release()); if (cacheStorage().storeNewestCache(this, oldNewestCache.get(), failureReason)) { // New cache stored, now remove the old cache. if (oldNewestCache) cacheStorage().remove(oldNewestCache.get()); // Fire the final progress event. ASSERT(m_progressDone == m_progressTotal); postListenerTask(ApplicationCacheHost::PROGRESS_EVENT, m_progressTotal, m_progressDone, m_associatedDocumentLoaders); // Fire the success event. postListenerTask(isUpgradeAttempt ? ApplicationCacheHost::UPDATEREADY_EVENT : ApplicationCacheHost::CACHED_EVENT, m_associatedDocumentLoaders); // It is clear that the origin quota was not reached, so clear the flag if it was set. m_originQuotaExceededPreviously = false; } else { if (failureReason == ApplicationCacheStorage::OriginQuotaReached) { // We ran out of space for this origin. Fall down to the normal error handling // after recording this state. m_originQuotaExceededPreviously = true; m_frame->document()->addConsoleMessage(OtherMessageSource, ErrorMessageLevel, "Application Cache update failed, because size quota was exceeded."); } if (failureReason == ApplicationCacheStorage::TotalQuotaReached && !m_calledReachedMaxAppCacheSize) { // FIXME: Should this be handled more like Origin Quotas? Does this fail properly? // 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; } // 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(ApplicationCacheHost::ERROR_EVENT, 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; } } // Empty cache group's list of pending master entries. m_pendingMasterResourceLoaders.clear(); m_completionType = None; setUpdateStatus(Idle); m_frame = 0; m_availableSpaceInQuota = ApplicationCacheStorage::unknownQuota(); m_calledReachedMaxAppCacheSize = false; }
void ApplicationCacheGroup::scheduleReachedOriginQuotaCallback() { // FIXME: it might be nice to run this asynchronously, because there is no return value to wait for. didReachOriginQuota(m_frame); }