void ApplicationCacheHost::maybeLoadMainResource(ResourceRequest& request, SubstituteData& substituteData) { // Check if this request should be loaded from the application cache if (!substituteData.isValid() && isApplicationCacheEnabled() && !isApplicationCacheBlockedForRequest(request)) { ASSERT(!m_mainResourceApplicationCache); m_mainResourceApplicationCache = ApplicationCacheGroup::cacheForMainRequest(request, &m_documentLoader); if (m_mainResourceApplicationCache) { // Get the resource from the application cache. By definition, cacheForMainRequest() returns a cache that contains the resource. ApplicationCacheResource* resource = m_mainResourceApplicationCache->resourceForRequest(request); // ApplicationCache resources have fragment identifiers stripped off of their URLs, // but we'll need to restore that for the SubstituteData. ResourceResponse responseToUse = resource->response(); if (request.url().hasFragmentIdentifier()) { URL url = responseToUse.url(); url.setFragmentIdentifier(request.url().fragmentIdentifier()); responseToUse.setURL(url); } substituteData = SubstituteData(&resource->data(), URL(), responseToUse, SubstituteData::SessionHistoryVisibility::Visible); } } }
bool ApplicationCacheHost::maybeLoadSynchronously(ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data) { ApplicationCacheResource* resource; if (shouldLoadResourceFromApplicationCache(request, resource)) { if (resource) { response = resource->response(); data.append(resource->data()->data(), resource->data()->size()); } else { error = documentLoader()->frameLoader()->client().cannotShowURLError(request); } return true; } return false; }
void ApplicationCacheHost::maybeLoadFallbackSynchronously(const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data) { // If normal loading results in a redirect to a resource with another origin (indicative of a captive portal), or a 4xx or 5xx status code or equivalent, // or if there were network errors (but not if the user canceled the download), then instead get, from the cache, the resource of the fallback entry // corresponding to the matched namespace. if ((!error.isNull() && !error.isCancellation()) || response.httpStatusCode() / 100 == 4 || response.httpStatusCode() / 100 == 5 || !protocolHostAndPortAreEqual(request.url(), response.url())) { ApplicationCacheResource* resource; if (getApplicationCacheFallbackResource(request, resource)) { response = resource->response(); data.clear(); data.append(resource->data()->data(), resource->data()->size()); } } }
bool ApplicationCacheStorage::storeCopyOfCache(const String& cacheDirectory, ApplicationCacheHost* cacheHost) { ApplicationCache* cache = cacheHost->applicationCache(); if (!cache) return true; // Create a new cache. RefPtr<ApplicationCache> cacheCopy = ApplicationCache::create(); cacheCopy->setOnlineWhitelist(cache->onlineWhitelist()); cacheCopy->setFallbackURLs(cache->fallbackURLs()); // Traverse the cache and add copies of all resources. ApplicationCache::ResourceMap::const_iterator end = cache->end(); for (ApplicationCache::ResourceMap::const_iterator it = cache->begin(); it != end; ++it) { ApplicationCacheResource* resource = it->second.get(); RefPtr<ApplicationCacheResource> resourceCopy = ApplicationCacheResource::create(resource->url(), resource->response(), resource->type(), resource->data()); cacheCopy->addResource(resourceCopy.release()); } // Now create a new cache group. OwnPtr<ApplicationCacheGroup> groupCopy(new ApplicationCacheGroup(cache->group()->manifestURL(), true)); groupCopy->setNewestCache(cacheCopy); ApplicationCacheStorage copyStorage; copyStorage.setCacheDirectory(cacheDirectory); // Empty the cache in case something was there before. copyStorage.empty(); return copyStorage.storeNewestCache(groupCopy.get()); }
void ApplicationCacheHost::maybeLoadMainResource(ResourceRequest& request, SubstituteData& substituteData) { // Check if this request should be loaded from the application cache if (!substituteData.isValid() && isApplicationCacheEnabled()) { ASSERT(!m_mainResourceApplicationCache); m_mainResourceApplicationCache = ApplicationCacheGroup::cacheForMainRequest(request, m_documentLoader); if (m_mainResourceApplicationCache) { // Get the resource from the application cache. By definition, cacheForMainRequest() returns a cache that contains the resource. ApplicationCacheResource* resource = m_mainResourceApplicationCache->resourceForRequest(request); substituteData = SubstituteData(resource->data(), resource->response().mimeType(), resource->response().textEncodingName(), KURL()); } } }
void ApplicationCacheHost::maybeLoadFallbackSynchronously(const ResourceRequest& request, ResourceError& error, ResourceResponse& response, RefPtr<SharedBuffer>& data) { // If normal loading results in a redirect to a resource with another origin (indicative of a captive portal), or a 4xx or 5xx status code or equivalent, // or if there were network errors (but not if the user canceled the download), then instead get, from the cache, the resource of the fallback entry // corresponding to the matched namespace. if ((!error.isNull() && !error.isCancellation()) || response.httpStatusCode() / 100 == 4 || response.httpStatusCode() / 100 == 5 || !protocolHostAndPortAreEqual(request.url(), response.url())) { ApplicationCacheResource* resource; if (getApplicationCacheFallbackResource(request, resource)) { response = resource->response(); // FIXME: Clients proably do not need a copy of the SharedBuffer. // Remove the call to copy() once we ensure SharedBuffer will not be modified. data = resource->data().copy(); } } }
void ApplicationCacheHost::fillResourceList(ResourceInfoList* resources) { ApplicationCache* cache = applicationCache(); if (!cache || !cache->isComplete()) return; for (const auto& urlAndResource : cache->resources()) { ApplicationCacheResource* resource = urlAndResource.value.get(); unsigned type = resource->type(); bool isMaster = type & ApplicationCacheResource::Master; bool isManifest = type & ApplicationCacheResource::Manifest; bool isExplicit = type & ApplicationCacheResource::Explicit; bool isForeign = type & ApplicationCacheResource::Foreign; bool isFallback = type & ApplicationCacheResource::Fallback; resources->append(ResourceInfo(resource->url(), isMaster, isManifest, isFallback, isForeign, isExplicit, resource->estimatedSizeInStorage())); } }
bool MainResourceLoader::load(const ResourceRequest& r, const SubstituteData& substituteData) { ASSERT(!m_handle); m_substituteData = substituteData; #if ENABLE(OFFLINE_WEB_APPLICATIONS) // Check if this request should be loaded from the application cache if (!m_substituteData.isValid() && frameLoader()->frame()->settings() && frameLoader()->frame()->settings()->offlineWebApplicationCacheEnabled()) { ASSERT(!m_applicationCache); m_applicationCache = ApplicationCacheGroup::cacheForMainRequest(r, m_documentLoader.get()); if (m_applicationCache) { // Get the resource from the application cache. By definition, cacheForMainRequest() returns a cache that contains the resource. ApplicationCacheResource* resource = m_applicationCache->resourceForRequest(r); m_substituteData = SubstituteData(resource->data(), resource->response().mimeType(), resource->response().textEncodingName(), KURL()); } } #endif ResourceRequest request(r); bool defer = defersLoading(); if (defer) { bool shouldLoadEmpty = shouldLoadAsEmptyDocument(r.url()); if (shouldLoadEmpty) defer = false; } if (!defer) { if (loadNow(request)) { // Started as an empty document, but was redirected to something non-empty. ASSERT(defersLoading()); defer = true; } } if (defer) m_initialRequest = request; return true; }
bool ApplicationCacheHost::maybeLoadSynchronously(ResourceRequest& request, ResourceError& error, ResourceResponse& response, RefPtr<SharedBuffer>& data) { ApplicationCacheResource* resource; if (shouldLoadResourceFromApplicationCache(request, resource)) { if (resource) { // FIXME: Clients proably do not need a copy of the SharedBuffer. // Remove the call to copy() once we ensure SharedBuffer will not be modified. if (resource->path().isEmpty()) data = resource->data().copy(); else data = SharedBuffer::createWithContentsOfFile(resource->path()); } if (!data) error = m_documentLoader.frameLoader()->client().cannotShowURLError(request); else response = resource->response(); return true; } return false; }
void ApplicationCacheGroup::didFail(ResourceHandle* handle, const ResourceError& error) { #if ENABLE(INSPECTOR) InspectorInstrumentation::didFailLoading(m_frame, m_frame->loader().documentLoader(), m_currentResourceIdentifier, error); #else UNUSED_PARAM(error); #endif if (handle == m_manifestHandle) { // A network error is logged elsewhere, no need to log again. Also, it's normal for manifest fetching to fail when working offline. cacheUpdateFailed(); return; } ASSERT(handle == m_currentHandle); unsigned type = m_currentResource ? m_currentResource->type() : m_pendingEntries.get(handle->firstRequest().url()); URL url(handle->firstRequest().url()); if (url.hasFragmentIdentifier()) url.removeFragmentIdentifier(); ASSERT(!m_currentResource || !m_pendingEntries.contains(url)); m_currentResource = 0; m_pendingEntries.remove(url); if ((type & ApplicationCacheResource::Explicit) || (type & ApplicationCacheResource::Fallback)) { m_frame->document()->addConsoleMessage(AppCacheMessageSource, ErrorMessageLevel, "Application Cache update failed, because " + url.stringCenterEllipsizedToLength() + " could not be fetched."); // Note that cacheUpdateFailed() can cause the cache group to be deleted. cacheUpdateFailed(); } else { // Copy the resource and its metadata from the newest application cache in cache group whose completeness flag is complete, and act // as if that was the fetched resource, ignoring the resource obtained from the network. ASSERT(m_newestCache); ApplicationCacheResource* newestCachedResource = m_newestCache->resourceForURL(url); ASSERT(newestCachedResource); m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, newestCachedResource->response(), type, newestCachedResource->data(), newestCachedResource->path())); // Load the next resource, if any. startLoadingEntry(); } }
void MediaPlayerPrivateQuickTimeVisualContext::loadInternal(const String& url) { if (!QTMovie::initializeQuickTime()) { // FIXME: is this the right error to return? m_networkState = MediaPlayer::DecodeError; m_player->networkStateChanged(); return; } disableComponentsOnce(); // Initialize the task timer. MediaPlayerPrivateTaskTimer::initialize(); if (m_networkState != MediaPlayer::Loading) { m_networkState = MediaPlayer::Loading; m_player->networkStateChanged(); } if (m_readyState != MediaPlayer::HaveNothing) { m_readyState = MediaPlayer::HaveNothing; m_player->readyStateChanged(); } cancelSeek(); setUpCookiesForQuickTime(url); m_movie = adoptRef(new QTMovie(m_movieClient.get())); #if ENABLE(OFFLINE_WEB_APPLICATIONS) Frame* frame = m_player->frameView() ? m_player->frameView()->frame() : 0; ApplicationCacheHost* cacheHost = frame ? frame->loader()->documentLoader()->applicationCacheHost() : 0; ApplicationCacheResource* resource = 0; if (cacheHost && cacheHost->shouldLoadResourceFromApplicationCache(ResourceRequest(url), resource) && resource && !resource->path().isEmpty()) m_movie->load(resource->path().characters(), resource->path().length(), m_player->preservesPitch()); else #endif m_movie->load(url.characters(), url.length(), m_player->preservesPitch()); m_movie->setVolume(m_player->volume()); }
void ApplicationCacheGroup::didFail(ResourceHandle* handle, const ResourceError& error) { #if ENABLE(INSPECTOR) if (Page* page = m_frame->page()) page->inspectorController()->didFailLoading(m_currentResourceIdentifier, error); #else UNUSED_PARAM(error); #endif if (handle == m_manifestHandle) { cacheUpdateFailed(); return; } unsigned type = m_currentResource ? m_currentResource->type() : m_pendingEntries.get(handle->firstRequest().url()); KURL url(handle->firstRequest().url()); if (url.hasFragmentIdentifier()) url.removeFragmentIdentifier(); ASSERT(!m_currentResource || !m_pendingEntries.contains(url)); m_currentResource = 0; m_pendingEntries.remove(url); if ((type & ApplicationCacheResource::Explicit) || (type & ApplicationCacheResource::Fallback)) { // Note that cacheUpdateFailed() can cause the cache group to be deleted. cacheUpdateFailed(); } else { // Copy the resource and its metadata from the newest application cache in cache group whose completeness flag is complete, and act // as if that was the fetched resource, ignoring the resource obtained from the network. ASSERT(m_newestCache); ApplicationCacheResource* newestCachedResource = m_newestCache->resourceForURL(url); ASSERT(newestCachedResource); m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, newestCachedResource->response(), type, newestCachedResource->data())); // Load the next resource, if any. startLoadingEntry(); } }
void MediaPlayerPrivateAVFoundation::setPreload(MediaPlayer::Preload preload) { m_preload = preload; if (!m_assetURL.length()) return; setDelayCallbacks(true); if (m_preload >= MediaPlayer::MetaData && assetStatus() == MediaPlayerAVAssetStatusDoesNotExist) { #if ENABLE(OFFLINE_WEB_APPLICATIONS) Frame* frame = m_player->frameView() ? m_player->frameView()->frame() : 0; ApplicationCacheHost* cacheHost = frame ? frame->loader()->documentLoader()->applicationCacheHost() : 0; ApplicationCacheResource* resource; if (cacheHost && cacheHost->shouldLoadResourceFromApplicationCache(ResourceRequest(m_assetURL), resource) && resource) { // AVFoundation can't open arbitrary data pointers, so if this ApplicationCacheResource doesn't // have a valid local path, just open the resource's original URL. if (resource->path().isEmpty()) createAVAssetForURL(resource->url()); else createAVAssetForCacheResource(resource); } else #endif createAVAssetForURL(m_assetURL); createAVPlayer(); checkPlayability(); } // Don't force creation of the player item unless we already know that the asset is playable. If we aren't // there yet, or if we already know it is not playable, creating it now won't help. if (m_preload == MediaPlayer::Auto && m_assetIsPlayable) createAVPlayerItem(); setDelayCallbacks(false); }
void ApplicationCacheGroup::didFinishLoadingManifest() { if (!m_manifestResource) { didFailToLoadManifest(); return; } bool isUpgradeAttempt = m_newestCache; m_manifestHandle = 0; // Check if the manifest is byte-for-byte identical. if (isUpgradeAttempt) { ApplicationCacheResource* newestManifest = m_newestCache->manifestResource(); ASSERT(newestManifest); if (newestManifest->data()->size() == m_manifestResource->data()->size() && !memcmp(newestManifest->data()->data(), m_manifestResource->data()->data(), newestManifest->data()->size())) { callListenersOnAssociatedDocuments(&DOMApplicationCache::callNoUpdateListener); m_status = Idle; m_frame = 0; m_manifestResource = 0; return; } } Manifest manifest; if (!parseManifest(m_manifestURL, m_manifestResource->data()->data(), m_manifestResource->data()->size(), manifest)) { didFailToLoadManifest(); return; } // We have the manifest, now download the resources. m_status = Downloading; callListenersOnAssociatedDocuments(&DOMApplicationCache::callDownloadingListener); #ifndef NDEBUG // We should only have implicit entries. { EntryMap::const_iterator end = m_pendingEntries.end(); for (EntryMap::const_iterator it = m_pendingEntries.begin(); it != end; ++it) ASSERT(it->second & ApplicationCacheResource::Implicit); } #endif if (isUpgradeAttempt) { ASSERT(!m_cacheBeingUpdated); m_cacheBeingUpdated = ApplicationCache::create(); 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::Implicit | ApplicationCacheResource::Dynamic)) 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); startLoadingEntry(); }
void ApplicationCacheGroup::selectCache(Frame* frame, const KURL& manifestURL) { ASSERT(frame && frame->page()); if (!frame->settings()->offlineWebApplicationCacheEnabled()) return; DocumentLoader* documentLoader = frame->loader()->documentLoader(); ASSERT(!documentLoader->applicationCache()); if (manifestURL.isNull()) { selectCacheWithoutManifestURL(frame); return; } ApplicationCache* mainResourceCache = documentLoader->mainResourceApplicationCache(); if (mainResourceCache) { if (manifestURL == mainResourceCache->group()->m_manifestURL) { mainResourceCache->group()->associateDocumentLoaderWithCache(documentLoader, mainResourceCache); mainResourceCache->group()->update(frame); } else { // The main resource was loaded from cache, so the cache must have an entry for it. Mark it as foreign. ApplicationCacheResource* resource = mainResourceCache->resourceForURL(documentLoader->url()); bool inStorage = resource->storageID(); resource->addType(ApplicationCacheResource::Foreign); if (inStorage) cacheStorage().storeUpdatedType(resource, mainResourceCache); // Restart the current navigation from the top of the navigation algorithm, undoing any changes that were made // as part of the initial load. // The navigation will not result in the same resource being loaded, because "foreign" entries are never picked during navigation. frame->loader()->scheduleLocationChange(documentLoader->url(), frame->loader()->referrer(), true); } return; } // The resource was loaded from the network, check if it is a HTTP/HTTPS GET. const ResourceRequest& request = frame->loader()->activeDocumentLoader()->request(); if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request)) { selectCacheWithoutManifestURL(frame); return; } // Check that the resource URL has the same scheme/host/port as the manifest URL. if (!protocolHostAndPortAreEqual(manifestURL, request.url())) { selectCacheWithoutManifestURL(frame); return; } ApplicationCacheGroup* group = cacheStorage().findOrCreateCacheGroup(manifestURL); if (ApplicationCache* cache = group->newestCache()) { ASSERT(cache->manifestResource()); group->associateDocumentLoaderWithCache(frame->loader()->documentLoader(), cache); if (!frame->loader()->documentLoader()->isLoadingMainResource()) group->finishedLoadingMainResource(frame->loader()->documentLoader()); group->update(frame); } else { bool isUpdating = group->m_cacheBeingUpdated; if (!isUpdating) group->m_cacheBeingUpdated = ApplicationCache::create(); documentLoader->setCandidateApplicationCacheGroup(group); group->m_cacheCandidates.add(documentLoader); const KURL& url = frame->loader()->documentLoader()->originalURL(); unsigned type = 0; // If the resource has already been downloaded, remove it so that it will be replaced with the implicit resource if (isUpdating) type = group->m_cacheBeingUpdated->removeResource(url); // Add the main resource URL as an implicit entry. group->addEntry(url, type | ApplicationCacheResource::Implicit); if (!frame->loader()->documentLoader()->isLoadingMainResource()) group->finishedLoadingMainResource(frame->loader()->documentLoader()); if (!isUpdating) group->update(frame); } }
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(); }
ApplicationCacheGroup* ApplicationCacheStorage::cacheGroupForURL(const KURL& url) { ASSERT(!url.hasFragmentIdentifier()); loadManifestHostHashes(); // Hash the host name and see if there's a manifest with the same host. if (!m_cacheHostSet.contains(urlHostHash(url))) return 0; // Check if a cache already exists in memory. CacheGroupMap::const_iterator end = m_cachesInMemory.end(); for (CacheGroupMap::const_iterator it = m_cachesInMemory.begin(); it != end; ++it) { ApplicationCacheGroup* group = it->second; ASSERT(!group->isObsolete()); if (!protocolHostAndPortAreEqual(url, group->manifestURL())) continue; if (ApplicationCache* cache = group->newestCache()) { ApplicationCacheResource* resource = cache->resourceForURL(url); if (!resource) continue; if (resource->type() & ApplicationCacheResource::Foreign) continue; return group; } } if (!m_database.isOpen()) return 0; // Check the database. Look for all cache groups with a newest cache. SQLiteStatement statement(m_database, "SELECT id, manifestURL, newestCache FROM CacheGroups WHERE newestCache IS NOT NULL"); if (statement.prepare() != SQLResultOk) return 0; int result; while ((result = statement.step()) == SQLResultRow) { KURL manifestURL = KURL(ParsedURLString, statement.getColumnText(1)); if (m_cachesInMemory.contains(manifestURL)) continue; if (!protocolHostAndPortAreEqual(url, manifestURL)) continue; // We found a cache group that matches. Now check if the newest cache has a resource with // 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) continue; if (resource->type() & ApplicationCacheResource::Foreign) continue; ApplicationCacheGroup* group = new ApplicationCacheGroup(manifestURL); group->setStorageID(static_cast<unsigned>(statement.getColumnInt64(0))); group->setNewestCache(cache.release()); m_cachesInMemory.set(group->manifestURL(), group); return group; } if (result != SQLResultDone) LOG_ERROR("Could not load cache group, error \"%s\"", m_database.lastErrorMsg()); return 0; }
void ApplicationCacheGroup::didReceiveResponse(ResourceHandle* handle, const ResourceResponse& response) { #if ENABLE(INSPECTOR) if (Page* page = m_frame->page()) { if (handle == m_manifestHandle) { if (InspectorApplicationCacheAgent* applicationCacheAgent = page->inspectorController()->applicationCacheAgent()) applicationCacheAgent->didReceiveManifestResponse(m_currentResourceIdentifier, response); } else page->inspectorController()->didReceiveResponse(m_currentResourceIdentifier, response); } #endif if (handle == m_manifestHandle) { didReceiveManifestResponse(response); return; } ASSERT(handle == m_currentHandle); KURL url(handle->firstRequest().url()); if (url.hasFragmentIdentifier()) url.removeFragmentIdentifier(); ASSERT(!m_currentResource); ASSERT(m_pendingEntries.contains(url)); unsigned type = m_pendingEntries.get(url); // If this is an initial cache attempt, we should not get master resources delivered here. if (!m_newestCache) ASSERT(!(type & ApplicationCacheResource::Master)); if (m_newestCache && response.httpStatusCode() == 304) { // Not modified. ApplicationCacheResource* newestCachedResource = m_newestCache->resourceForURL(url); if (newestCachedResource) { m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, newestCachedResource->response(), type, newestCachedResource->data())); m_pendingEntries.remove(m_currentHandle->firstRequest().url()); m_currentHandle->cancel(); m_currentHandle = 0; // Load the next resource, if any. startLoadingEntry(); return; } // The server could return 304 for an unconditional request - in this case, we handle the response as a normal error. } if (response.httpStatusCode() / 100 != 2 || response.url() != m_currentHandle->firstRequest().url()) { if ((type & ApplicationCacheResource::Explicit) || (type & ApplicationCacheResource::Fallback)) { // Note that cacheUpdateFailed() can cause the cache group to be deleted. cacheUpdateFailed(); } else if (response.httpStatusCode() == 404 || response.httpStatusCode() == 410) { // Skip this resource. It is dropped from the cache. m_currentHandle->cancel(); m_currentHandle = 0; m_pendingEntries.remove(url); // Load the next resource, if any. startLoadingEntry(); } else { // Copy the resource and its metadata from the newest application cache in cache group whose completeness flag is complete, and act // as if that was the fetched resource, ignoring the resource obtained from the network. ASSERT(m_newestCache); ApplicationCacheResource* newestCachedResource = m_newestCache->resourceForURL(handle->firstRequest().url()); ASSERT(newestCachedResource); m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, newestCachedResource->response(), type, newestCachedResource->data())); m_pendingEntries.remove(m_currentHandle->firstRequest().url()); m_currentHandle->cancel(); m_currentHandle = 0; // Load the next resource, if any. startLoadingEntry(); } return; } m_currentResource = ApplicationCacheResource::create(url, response, type); }
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::didReceiveResponse(ResourceHandle* handle, const ResourceResponse& response) { #if ENABLE(INSPECTOR) DocumentLoader* loader = (handle == m_manifestHandle) ? 0 : m_frame->loader().documentLoader(); InspectorInstrumentationCookie cookie = InspectorInstrumentation::willReceiveResourceResponse(m_frame, m_currentResourceIdentifier, response); InspectorInstrumentation::didReceiveResourceResponse(cookie, m_currentResourceIdentifier, loader, response, 0); #endif if (handle == m_manifestHandle) { didReceiveManifestResponse(response); return; } ASSERT(handle == m_currentHandle); URL url(handle->firstRequest().url()); if (url.hasFragmentIdentifier()) url.removeFragmentIdentifier(); ASSERT(!m_currentResource); ASSERT(m_pendingEntries.contains(url)); unsigned type = m_pendingEntries.get(url); // If this is an initial cache attempt, we should not get master resources delivered here. if (!m_newestCache) ASSERT(!(type & ApplicationCacheResource::Master)); if (m_newestCache && response.httpStatusCode() == 304) { // Not modified. ApplicationCacheResource* newestCachedResource = m_newestCache->resourceForURL(url); if (newestCachedResource) { m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, newestCachedResource->response(), type, newestCachedResource->data(), newestCachedResource->path())); m_pendingEntries.remove(m_currentHandle->firstRequest().url()); m_currentHandle->cancel(); m_currentHandle = 0; // Load the next resource, if any. startLoadingEntry(); return; } // The server could return 304 for an unconditional request - in this case, we handle the response as a normal error. } if (response.httpStatusCode() / 100 != 2 || response.url() != m_currentHandle->firstRequest().url()) { if ((type & ApplicationCacheResource::Explicit) || (type & ApplicationCacheResource::Fallback)) { m_frame->document()->addConsoleMessage(AppCacheMessageSource, ErrorMessageLevel, "Application Cache update failed, because " + m_currentHandle->firstRequest().url().stringCenterEllipsizedToLength() + ((response.httpStatusCode() / 100 != 2) ? " could not be fetched." : " was redirected.")); // Note that cacheUpdateFailed() can cause the cache group to be deleted. cacheUpdateFailed(); } else if (response.httpStatusCode() == 404 || response.httpStatusCode() == 410) { // Skip this resource. It is dropped from the cache. m_currentHandle->cancel(); m_currentHandle = 0; m_pendingEntries.remove(url); // Load the next resource, if any. startLoadingEntry(); } else { // Copy the resource and its metadata from the newest application cache in cache group whose completeness flag is complete, and act // as if that was the fetched resource, ignoring the resource obtained from the network. ASSERT(m_newestCache); ApplicationCacheResource* newestCachedResource = m_newestCache->resourceForURL(handle->firstRequest().url()); ASSERT(newestCachedResource); m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, newestCachedResource->response(), type, newestCachedResource->data(), newestCachedResource->path())); m_pendingEntries.remove(m_currentHandle->firstRequest().url()); m_currentHandle->cancel(); m_currentHandle = 0; // Load the next resource, if any. startLoadingEntry(); } return; } m_currentResource = ApplicationCacheResource::create(url, response, type); }
void ApplicationCacheGroup::selectCache(Frame* frame, const URL& passedManifestURL) { ASSERT(frame && frame->page()); if (!frame->settings().offlineWebApplicationCacheEnabled()) return; DocumentLoader* documentLoader = frame->loader().documentLoader(); ASSERT(!documentLoader->applicationCacheHost()->applicationCache()); if (passedManifestURL.isNull()) { selectCacheWithoutManifestURL(frame); 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())) { postListenerTask(ApplicationCacheHost::CHECKING_EVENT, documentLoader); postListenerTask(ApplicationCacheHost::ERROR_EVENT, documentLoader); return; } URL manifestURL(passedManifestURL); if (manifestURL.hasFragmentIdentifier()) manifestURL.removeFragmentIdentifier(); ApplicationCache* mainResourceCache = documentLoader->applicationCacheHost()->mainResourceApplicationCache(); if (mainResourceCache) { if (manifestURL == mainResourceCache->group()->m_manifestURL) { // The cache may have gotten obsoleted after we've loaded from it, but before we parsed the document and saw cache manifest. if (mainResourceCache->group()->isObsolete()) return; mainResourceCache->group()->associateDocumentLoaderWithCache(documentLoader, mainResourceCache); mainResourceCache->group()->update(frame, ApplicationCacheUpdateWithBrowsingContext); } else { // The main resource was loaded from cache, so the cache must have an entry for it. Mark it as foreign. URL resourceURL(documentLoader->responseURL()); if (resourceURL.hasFragmentIdentifier()) resourceURL.removeFragmentIdentifier(); ApplicationCacheResource* resource = mainResourceCache->resourceForURL(resourceURL); bool inStorage = resource->storageID(); resource->addType(ApplicationCacheResource::Foreign); if (inStorage) frame->page()->applicationCacheStorage().storeUpdatedType(resource, mainResourceCache); // Restart the current navigation from the top of the navigation algorithm, undoing any changes that were made // as part of the initial load. // The navigation will not result in the same resource being loaded, because "foreign" entries are never picked during navigation. frame->navigationScheduler().scheduleLocationChange(frame->document(), frame->document()->securityOrigin(), documentLoader->url(), frame->loader().referrer()); } return; } // The resource was loaded from the network, check if it is a HTTP/HTTPS GET. const ResourceRequest& request = frame->loader().activeDocumentLoader()->request(); if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request)) return; // Check that the resource URL has the same scheme/host/port as the manifest URL. if (!protocolHostAndPortAreEqual(manifestURL, request.url())) return; ApplicationCacheGroup* group = frame->page()->applicationCacheStorage().findOrCreateCacheGroup(manifestURL); documentLoader->applicationCacheHost()->setCandidateApplicationCacheGroup(group); group->m_pendingMasterResourceLoaders.add(documentLoader); group->m_downloadingPendingMasterResourceLoadersCount++; ASSERT(!group->m_cacheBeingUpdated || group->m_updateStatus != Idle); group->update(frame, ApplicationCacheUpdateWithBrowsingContext); }
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(); }