bool ApplicationCacheHost::shouldLoadResourceFromApplicationCache(const ResourceRequest& originalRequest, ApplicationCacheResource*& resource) { ApplicationCache* cache = applicationCache(); if (!cache || !cache->isComplete()) return false; ResourceRequest request(originalRequest); if (Frame* loaderFrame = m_documentLoader.frame()) { if (Document* document = loaderFrame->document()) document->contentSecurityPolicy()->upgradeInsecureRequestIfNeeded(request, ContentSecurityPolicy::InsecureRequestType::Load); } // If the resource is not to be fetched using the HTTP GET mechanism or equivalent, or if its URL has a different // <scheme> component than the application cache's manifest, then fetch the resource normally. if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request) || !equalIgnoringASCIICase(request.url().protocol(), cache->manifestResource()->url().protocol())) return false; // If the resource's URL is an master entry, the manifest, an explicit entry, or a fallback entry // in the application cache, then get the resource from the cache (instead of fetching it). resource = cache->resourceForURL(request.url()); // Resources that match fallback namespaces or online whitelist entries are fetched from the network, // unless they are also cached. if (!resource && (cache->allowsAllNetworkRequests() || cache->urlMatchesFallbackNamespace(request.url()) || cache->isURLInOnlineWhitelist(request.url()))) return false; // Resources that are not present in the manifest will always fail to load (at least, after the // cache has been primed the first time), making the testing of offline applications simpler. return true; }
unsigned DOMApplicationCache::length() const { ApplicationCache* cache = associatedCache(); if (!cache) return 0; return cache->numDynamicEntries(); }
bool ApplicationCacheHost::update() { ApplicationCache* cache = applicationCache(); if (!cache) return false; cache->group()->update(m_documentLoader->frame(), ApplicationCacheUpdateWithoutBrowsingContext); return true; }
ApplicationCacheHost::CacheInfo ApplicationCacheHost::applicationCacheInfo() { ApplicationCache* cache = applicationCache(); if (!cache || !cache->isComplete()) return CacheInfo(URL(), 0, 0, 0); // FIXME: Add "Creation Time" and "Update Time" to Application Caches. return CacheInfo(cache->manifestResource()->url(), 0, 0, cache->estimatedSizeInStorage()); }
void DOMApplicationCache::remove(const KURL& url, ExceptionCode& ec) { ApplicationCache* cache = associatedCache(); if (!cache) { ec = INVALID_STATE_ERR; return; } cache->removeDynamicEntry(url); }
void DOMApplicationCache::update(ExceptionCode& ec) { ApplicationCache* cache = associatedCache(); if (!cache) { ec = INVALID_STATE_ERR; return; } cache->group()->update(m_frame); }
void ApplicationCacheHost::abort() { ApplicationCacheGroup* cacheGroup = candidateApplicationCacheGroup(); if (cacheGroup) cacheGroup->abort(m_documentLoader->frame()); else { ApplicationCache* cache = applicationCache(); if (cache) cache->group()->abort(m_documentLoader->frame()); } }
String DOMApplicationCache::item(unsigned item, ExceptionCode& ec) { ApplicationCache* cache = associatedCache(); if (!cache) { ec = INVALID_STATE_ERR; return String(); } if (item >= length()) { ec = INDEX_SIZE_ERR; return String(); } return cache->dynamicEntry(item); }
void ApplicationCacheGroup::selectCacheWithoutManifestURL(Frame* frame) { if (!frame->settings()->offlineWebApplicationCacheEnabled()) return; DocumentLoader* documentLoader = frame->loader()->documentLoader(); ASSERT(!documentLoader->applicationCache()); ApplicationCache* mainResourceCache = documentLoader->mainResourceApplicationCache(); if (mainResourceCache) { mainResourceCache->group()->associateDocumentLoaderWithCache(documentLoader, mainResourceCache); mainResourceCache->group()->update(frame); } }
void ApplicationCacheHost::fillResourceList(ResourceInfoList* resources) { ApplicationCache* cache = applicationCache(); if (!cache || !cache->isComplete()) return; for (const auto& urlAndResource : *cache) { RefPtr<ApplicationCacheResource> resource = urlAndResource.value; 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())); } }
void ApplicationCacheHost::fillResourceList(ResourceInfoList* resources) { ApplicationCache* cache = applicationCache(); if (!cache || !cache->isComplete()) return; ApplicationCache::ResourceMap::const_iterator end = cache->end(); for (ApplicationCache::ResourceMap::const_iterator it = cache->begin(); it != end; ++it) { RefPtr<ApplicationCacheResource> resource = it->value; 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())); } }
void DOMApplicationCache::add(const KURL& url, ExceptionCode& ec) { ApplicationCache* cache = associatedCache(); if (!cache) { ec = INVALID_STATE_ERR; return; } if (!url.isValid()) { ec = SYNTAX_ERR; return; } if (!cache->addDynamicEntry(url)) { // This should use the (currently not specified) security exceptions in HTML5 4.3.4 ec = SECURITY_ERR; } }
void ApplicationCacheGroup::selectCacheWithoutManifestURL(Frame* frame) { if (!frame->settings().offlineWebApplicationCacheEnabled()) return; if (!frame->document()->securityOrigin()->canAccessApplicationCache(frame->tree().top().document()->securityOrigin())) return; DocumentLoader* documentLoader = frame->loader().documentLoader(); ASSERT(!documentLoader->applicationCacheHost()->applicationCache()); ApplicationCache* mainResourceCache = documentLoader->applicationCacheHost()->mainResourceApplicationCache(); if (mainResourceCache) { mainResourceCache->group()->associateDocumentLoaderWithCache(documentLoader, mainResourceCache); mainResourceCache->group()->update(frame, ApplicationCacheUpdateWithBrowsingContext); } }
bool DOMApplicationCache::swapCache() { if (!m_frame) return false; ApplicationCache* cache = m_frame->loader()->documentLoader()->applicationCache(); if (!cache) return false; // Check if we already have the newest cache ApplicationCache* newestCache = cache->group()->newestCache(); if (cache == newestCache) return false; ASSERT(cache->group() == newestCache->group()); m_frame->loader()->documentLoader()->setApplicationCache(newestCache); return true; }
bool DocumentLoader::shouldLoadResourceFromApplicationCache(const ResourceRequest& request, ApplicationCacheResource*& resource) { ApplicationCache* cache = topLevelApplicationCache(); if (!cache) return false; // If the resource is not a HTTP/HTTPS GET, then abort if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request)) return false; if (cache->isURLInOnlineWhitelist(request.url())) return false; resource = cache->resourceForURL(request.url()); // Don't load foreign resources. if (resource && (resource->type() & ApplicationCacheResource::Foreign)) resource = 0; return true; }
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 ApplicationCacheGroup::selectCacheWithoutManifestURL(Frame* frame) { if (!frame->settings().offlineWebApplicationCacheEnabled()) return; DocumentLoader* documentLoader = frame->loader().documentLoader(); ASSERT(!documentLoader->applicationCacheHost()->applicationCache()); // 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; } ApplicationCache* mainResourceCache = documentLoader->applicationCacheHost()->mainResourceApplicationCache(); if (mainResourceCache) { mainResourceCache->group()->associateDocumentLoaderWithCache(documentLoader, mainResourceCache); mainResourceCache->group()->update(frame, ApplicationCacheUpdateWithBrowsingContext); } }
ApplicationCacheHost::Status ApplicationCacheHost::status() const { ApplicationCache* cache = applicationCache(); if (!cache) return UNCACHED; switch (cache->group()->updateStatus()) { case ApplicationCacheGroup::Checking: return CHECKING; case ApplicationCacheGroup::Downloading: return DOWNLOADING; case ApplicationCacheGroup::Idle: { if (cache->group()->isObsolete()) return OBSOLETE; if (cache != cache->group()->newestCache()) return UPDATEREADY; return IDLE; } } ASSERT_NOT_REACHED(); return UNCACHED; }
bool DocumentLoader::shouldLoadResourceFromApplicationCache(const ResourceRequest& request, ApplicationCacheResource*& resource) { ApplicationCache* cache = applicationCache(); if (!cache) return false; // If the resource is not a HTTP/HTTPS GET, then abort if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request)) return false; // If the resource's URL is an master entry, the manifest, an explicit entry, a fallback entry, or a dynamic entry // in the application cache, then get the resource from the cache (instead of fetching it). resource = cache->resourceForURL(request.url()); // Resources that match fallback namespaces or online whitelist entries are fetched from the network, // unless they are also cached. if (!resource && (cache->urlMatchesFallbackNamespace(request.url()) || cache->isURLInOnlineWhitelist(request.url()))) return false; // Resources that are not present in the manifest will always fail to load (at least, after the // cache has been primed the first time), making the testing of offline applications simpler. return true; }
unsigned short DOMApplicationCache::status() const { ApplicationCache* cache = associatedCache(); if (!cache) return UNCACHED; switch (cache->group()->status()) { case ApplicationCacheGroup::Checking: return CHECKING; case ApplicationCacheGroup::Downloading: return DOWNLOADING; case ApplicationCacheGroup::Idle: { if (cache != cache->group()->newestCache()) return UPDATEREADY; return IDLE; } default: ASSERT_NOT_REACHED(); } return 0; }
bool ApplicationCacheHost::swapCache() { ApplicationCache* cache = applicationCache(); if (!cache) return false; // If the group of application caches to which cache belongs has the lifecycle status obsolete, unassociate document from cache. if (cache->group()->isObsolete()) { cache->group()->disassociateDocumentLoader(m_documentLoader); return true; } // If there is no newer cache, raise an INVALID_STATE_ERR exception. ApplicationCache* newestCache = cache->group()->newestCache(); if (cache == newestCache) return false; ASSERT(cache->group() == newestCache->group()); setApplicationCache(newestCache); InspectorInstrumentation::updateApplicationCacheStatus(m_documentLoader->frame()); return true; }
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::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); } }