void ApplicationCacheGroup::manifestNotFound() { makeObsolete(); postListenerTask(ApplicationCacheHost::OBSOLETE_EVENT, m_associatedDocumentLoaders); postListenerTask(ApplicationCacheHost::ERROR_EVENT, m_pendingMasterResourceLoaders); stopLoading(); ASSERT(m_pendingEntries.isEmpty()); m_manifestResource = 0; while (!m_pendingMasterResourceLoaders.isEmpty()) { HashSet<DocumentLoader*>::iterator it = m_pendingMasterResourceLoaders.begin(); ASSERT((*it)->applicationCacheHost()->candidateApplicationCacheGroup() == this); ASSERT(!(*it)->applicationCacheHost()->applicationCache()); (*it)->applicationCacheHost()->setCandidateApplicationCacheGroup(0); m_pendingMasterResourceLoaders.remove(it); } m_downloadingPendingMasterResourceLoadersCount = 0; setUpdateStatus(Idle); m_frame = 0; if (m_caches.isEmpty()) { ASSERT(m_associatedDocumentLoaders.isEmpty()); ASSERT(!m_cacheBeingUpdated); delete this; } }
void ApplicationCacheGroup::update(Frame* frame, ApplicationCacheUpdateOption updateOption) { if (m_updateStatus == Checking || m_updateStatus == Downloading) { if (updateOption == ApplicationCacheUpdateWithBrowsingContext) { postListenerTask(ApplicationCacheHost::CHECKING_EVENT, frame->loader().documentLoader()); if (m_updateStatus == Downloading) postListenerTask(ApplicationCacheHost::DOWNLOADING_EVENT, frame->loader().documentLoader()); } return; } // Don't change anything on disk if private browsing is enabled. if (frame->settings().privateBrowsingEnabled()) { ASSERT(m_pendingMasterResourceLoaders.isEmpty()); ASSERT(m_pendingEntries.isEmpty()); ASSERT(!m_cacheBeingUpdated); postListenerTask(ApplicationCacheHost::CHECKING_EVENT, frame->loader().documentLoader()); postListenerTask(ApplicationCacheHost::NOUPDATE_EVENT, frame->loader().documentLoader()); return; } ASSERT(!m_frame); m_frame = frame; setUpdateStatus(Checking); postListenerTask(ApplicationCacheHost::CHECKING_EVENT, m_associatedDocumentLoaders); if (!m_newestCache) { ASSERT(updateOption == ApplicationCacheUpdateWithBrowsingContext); postListenerTask(ApplicationCacheHost::CHECKING_EVENT, frame->loader().documentLoader()); } ASSERT(!m_manifestHandle); ASSERT(!m_manifestResource); ASSERT(!m_currentHandle); ASSERT(!m_currentResource); ASSERT(m_completionType == None); // FIXME: Handle defer loading m_manifestHandle = createResourceHandle(m_manifestURL, m_newestCache ? m_newestCache->manifestResource() : 0); }
void ApplicationCacheGroup::update(Frame* frame, ApplicationCacheUpdateOption updateOption) { if (m_updateStatus == Checking || m_updateStatus == Downloading) { if (updateOption == ApplicationCacheUpdateWithBrowsingContext) { postListenerTask(ApplicationCacheHost::CHECKING_EVENT, frame->loader().documentLoader()); if (m_updateStatus == Downloading) postListenerTask(ApplicationCacheHost::DOWNLOADING_EVENT, frame->loader().documentLoader()); } return; } // Don't access anything on disk if private browsing is enabled. if (frame->page()->usesEphemeralSession() || !frame->document()->securityOrigin()->canAccessApplicationCache(frame->tree().top().document()->securityOrigin())) { ASSERT(m_pendingMasterResourceLoaders.isEmpty()); ASSERT(m_pendingEntries.isEmpty()); ASSERT(!m_cacheBeingUpdated); postListenerTask(ApplicationCacheHost::CHECKING_EVENT, frame->loader().documentLoader()); postListenerTask(ApplicationCacheHost::ERROR_EVENT, frame->loader().documentLoader()); return; } ASSERT(!m_frame); m_frame = frame; setUpdateStatus(Checking); postListenerTask(ApplicationCacheHost::CHECKING_EVENT, m_associatedDocumentLoaders); if (!m_newestCache) { ASSERT(updateOption == ApplicationCacheUpdateWithBrowsingContext); postListenerTask(ApplicationCacheHost::CHECKING_EVENT, frame->loader().documentLoader()); } ASSERT(!m_manifestHandle); ASSERT(!m_manifestResource); ASSERT(!m_currentHandle); ASSERT(!m_currentResource); ASSERT(m_completionType == None); // FIXME: Handle defer loading m_manifestHandle = createResourceHandle(m_manifestURL, m_newestCache ? m_newestCache->manifestResource() : 0); }
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::didFinishLoadingManifest() { bool isUpgradeAttempt = m_newestCache; if (!isUpgradeAttempt && !m_manifestResource) { // The server returned 304 Not Modified even though we didn't send a conditional request. m_frame->document()->addConsoleMessage(OtherMessageSource, ErrorMessageLevel, "Application Cache manifest could not be fetched because of an unexpected 304 Not Modified server response."); cacheUpdateFailed(); return; } m_manifestHandle = 0; // Check if the manifest was not modified. if (isUpgradeAttempt) { ApplicationCacheResource* newestManifest = m_newestCache->manifestResource(); ASSERT(newestManifest); if (!m_manifestResource || // The resource will be null if HTTP response was 304 Not Modified. (newestManifest->data()->size() == m_manifestResource->data()->size() && !memcmp(newestManifest->data()->data(), m_manifestResource->data()->data(), newestManifest->data()->size()))) { m_completionType = NoUpdate; m_manifestResource = 0; deliverDelayedMainResources(); return; } } Manifest manifest; if (!parseManifest(m_manifestURL, m_manifestResource->data()->data(), m_manifestResource->data()->size(), manifest)) { // At the time of this writing, lack of "CACHE MANIFEST" signature is the only reason for parseManifest to fail. m_frame->document()->addConsoleMessage(OtherMessageSource, ErrorMessageLevel, "Application Cache manifest could not be parsed. Does it start with CACHE MANIFEST?"); cacheUpdateFailed(); return; } ASSERT(!m_cacheBeingUpdated); m_cacheBeingUpdated = ApplicationCache::create(); m_cacheBeingUpdated->setGroup(this); HashSet<DocumentLoader*>::const_iterator masterEnd = m_pendingMasterResourceLoaders.end(); for (HashSet<DocumentLoader*>::const_iterator iter = m_pendingMasterResourceLoaders.begin(); iter != masterEnd; ++iter) associateDocumentLoaderWithCache(*iter, m_cacheBeingUpdated.get()); // We have the manifest, now download the resources. setUpdateStatus(Downloading); postListenerTask(ApplicationCacheHost::DOWNLOADING_EVENT, m_associatedDocumentLoaders); ASSERT(m_pendingEntries.isEmpty()); if (isUpgradeAttempt) { ApplicationCache::ResourceMap::const_iterator end = m_newestCache->end(); for (ApplicationCache::ResourceMap::const_iterator it = m_newestCache->begin(); it != end; ++it) { unsigned type = it->value->type(); if (type & ApplicationCacheResource::Master) addEntry(it->key, type); } } HashSet<String>::const_iterator end = manifest.explicitURLs.end(); for (HashSet<String>::const_iterator it = manifest.explicitURLs.begin(); it != end; ++it) addEntry(*it, ApplicationCacheResource::Explicit); size_t fallbackCount = manifest.fallbackURLs.size(); for (size_t i = 0; i < fallbackCount; ++i) addEntry(manifest.fallbackURLs[i].second, ApplicationCacheResource::Fallback); m_cacheBeingUpdated->setOnlineWhitelist(manifest.onlineWhitelistedURLs); m_cacheBeingUpdated->setFallbackURLs(manifest.fallbackURLs); m_cacheBeingUpdated->setAllowsAllNetworkRequests(manifest.allowAllNetworkRequests); m_progressTotal = m_pendingEntries.size(); m_progressDone = 0; recalculateAvailableSpaceInQuota(); startLoadingEntry(); }
void ApplicationCacheGroup::didFinishLoadingManifest() { bool isUpgradeAttempt = m_newestCache; if (!isUpgradeAttempt && !m_manifestResource) { // The server returned 304 Not Modified even though we didn't send a conditional request. cacheUpdateFailed(); return; } m_manifestHandle = 0; // Check if the manifest was not modified. if (isUpgradeAttempt) { ApplicationCacheResource* newestManifest = m_newestCache->manifestResource(); ASSERT(newestManifest); if (!m_manifestResource || // The resource will be null if HTTP response was 304 Not Modified. (newestManifest->data()->size() == m_manifestResource->data()->size() && !memcmp(newestManifest->data()->data(), m_manifestResource->data()->data(), newestManifest->data()->size()))) { m_completionType = NoUpdate; m_manifestResource = 0; deliverDelayedMainResources(); return; } } Manifest manifest; if (!parseManifest(m_manifestURL, m_manifestResource->data()->data(), m_manifestResource->data()->size(), manifest)) { cacheUpdateFailed(); return; } ASSERT(!m_cacheBeingUpdated); m_cacheBeingUpdated = ApplicationCache::create(); m_cacheBeingUpdated->setGroup(this); HashSet<DocumentLoader*>::const_iterator masterEnd = m_pendingMasterResourceLoaders.end(); for (HashSet<DocumentLoader*>::const_iterator iter = m_pendingMasterResourceLoaders.begin(); iter != masterEnd; ++iter) associateDocumentLoaderWithCache(*iter, m_cacheBeingUpdated.get()); // We have the manifest, now download the resources. setUpdateStatus(Downloading); postListenerTask(ApplicationCacheHost::DOWNLOADING_EVENT, m_associatedDocumentLoaders); ASSERT(m_pendingEntries.isEmpty()); if (isUpgradeAttempt) { ApplicationCache::ResourceMap::const_iterator end = m_newestCache->end(); for (ApplicationCache::ResourceMap::const_iterator it = m_newestCache->begin(); it != end; ++it) { unsigned type = it->second->type(); if (type & ApplicationCacheResource::Master) addEntry(it->first, type); } } HashSet<String>::const_iterator end = manifest.explicitURLs.end(); for (HashSet<String>::const_iterator it = manifest.explicitURLs.begin(); it != end; ++it) addEntry(*it, ApplicationCacheResource::Explicit); size_t fallbackCount = manifest.fallbackURLs.size(); for (size_t i = 0; i < fallbackCount; ++i) addEntry(manifest.fallbackURLs[i].second, ApplicationCacheResource::Fallback); m_cacheBeingUpdated->setOnlineWhitelist(manifest.onlineWhitelistedURLs); m_cacheBeingUpdated->setFallbackURLs(manifest.fallbackURLs); m_cacheBeingUpdated->setAllowsAllNetworkRequests(manifest.allowAllNetworkRequests); m_progressTotal = m_pendingEntries.size(); m_progressDone = 0; startLoadingEntry(); }
void ApplicationCacheGroup::didFinishLoadingManifest() { bool isUpgradeAttempt = m_newestCache; if (!isUpgradeAttempt && !m_manifestResource) { // The server returned 304 Not Modified even though we didn't send a conditional request. m_frame->document()->addConsoleMessage(MessageSource::AppCache, MessageLevel::Error, ASCIILiteral("Application Cache manifest could not be fetched because of an unexpected 304 Not Modified server response.")); cacheUpdateFailed(); return; } m_manifestHandle = nullptr; // Check if the manifest was not modified. if (isUpgradeAttempt) { ApplicationCacheResource* newestManifest = m_newestCache->manifestResource(); ASSERT(newestManifest); if (!m_manifestResource || // The resource will be null if HTTP response was 304 Not Modified. (newestManifest->data().size() == m_manifestResource->data().size() && !memcmp(newestManifest->data().data(), m_manifestResource->data().data(), newestManifest->data().size()))) { m_completionType = NoUpdate; m_manifestResource = nullptr; deliverDelayedMainResources(); return; } } Manifest manifest; if (!parseManifest(m_manifestURL, m_manifestResource->data().data(), m_manifestResource->data().size(), manifest)) { // At the time of this writing, lack of "CACHE MANIFEST" signature is the only reason for parseManifest to fail. m_frame->document()->addConsoleMessage(MessageSource::AppCache, MessageLevel::Error, ASCIILiteral("Application Cache manifest could not be parsed. Does it start with CACHE MANIFEST?")); cacheUpdateFailed(); return; } ASSERT(!m_cacheBeingUpdated); m_cacheBeingUpdated = ApplicationCache::create(); m_cacheBeingUpdated->setGroup(this); for (auto& loader : m_pendingMasterResourceLoaders) associateDocumentLoaderWithCache(loader, m_cacheBeingUpdated.get()); // We have the manifest, now download the resources. setUpdateStatus(Downloading); postListenerTask(ApplicationCacheHost::DOWNLOADING_EVENT, m_associatedDocumentLoaders); ASSERT(m_pendingEntries.isEmpty()); if (isUpgradeAttempt) { for (const auto& urlAndResource : m_newestCache->resources()) { unsigned type = urlAndResource.value->type(); if (type & ApplicationCacheResource::Master) addEntry(urlAndResource.key, type); } } for (const auto& explicitURL : manifest.explicitURLs) addEntry(explicitURL, ApplicationCacheResource::Explicit); for (auto& fallbackURL : manifest.fallbackURLs) addEntry(fallbackURL.second, ApplicationCacheResource::Fallback); m_cacheBeingUpdated->setOnlineWhitelist(manifest.onlineWhitelistedURLs); m_cacheBeingUpdated->setFallbackURLs(manifest.fallbackURLs); m_cacheBeingUpdated->setAllowsAllNetworkRequests(manifest.allowAllNetworkRequests); m_progressTotal = m_pendingEntries.size(); m_progressDone = 0; recalculateAvailableSpaceInQuota(); startLoadingEntry(); }