ApplicationCacheGroup* ApplicationCacheStorage::loadCacheGroup(const KURL& manifestURL) { openDatabase(false); if (!m_database.isOpen()) return 0; SQLiteStatement statement(m_database, "SELECT id, manifestURL, newestCache FROM CacheGroups WHERE newestCache IS NOT NULL AND manifestURL=?"); if (statement.prepare() != SQLResultOk) return 0; statement.bindText(1, manifestURL); int result = statement.step(); if (result == SQLResultDone) return 0; if (result != SQLResultRow) { LOG_ERROR("Could not load cache group, error \"%s\"", m_database.lastErrorMsg()); return 0; } unsigned newestCacheStorageID = static_cast<unsigned>(statement.getColumnInt64(2)); RefPtr<ApplicationCache> cache = loadCache(newestCacheStorageID); if (!cache) return 0; ApplicationCacheGroup* group = new ApplicationCacheGroup(manifestURL); group->setStorageID(static_cast<unsigned>(statement.getColumnInt64(0))); group->setNewestCache(cache.release()); return group; }
void MainResourceLoader::didFinishLoading() { // There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred. // See <rdar://problem/6304600> for more details. #if !PLATFORM(CF) ASSERT(shouldLoadAsEmptyDocument(frameLoader()->activeDocumentLoader()->url()) || !defersLoading()); #endif // The additional processing can do anything including possibly removing the last // reference to this object. RefPtr<MainResourceLoader> protect(this); #if ENABLE(OFFLINE_WEB_APPLICATIONS) RefPtr<DocumentLoader> dl = documentLoader(); #endif frameLoader()->finishedLoading(); ResourceLoader::didFinishLoading(); #if ENABLE(OFFLINE_WEB_APPLICATIONS) ApplicationCacheGroup* group = dl->candidateApplicationCacheGroup(); if (!group && dl->applicationCache() && !dl->mainResourceApplicationCache()) group = dl->applicationCache()->group(); if (group) group->finishedLoadingMainResource(dl.get()); #endif }
void ApplicationCacheHost::finishedLoadingMainResource() { ApplicationCacheGroup* group = candidateApplicationCacheGroup(); if (!group && applicationCache() && !mainResourceApplicationCache()) group = applicationCache()->group(); if (group) group->finishedLoadingMainResource(m_documentLoader); }
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()); } }
void ApplicationCacheHost::failedLoadingMainResource() { ApplicationCacheGroup* group = m_candidateApplicationCacheGroup; if (!group && m_applicationCache) { ASSERT(!mainResourceApplicationCache()); // If the main resource were loaded from a cache, it wouldn't fail. group = m_applicationCache->group(); } if (group) group->failedLoadingMainResource(m_documentLoader); }
void ApplicationCacheHost::failedLoadingMainResource() { ApplicationCacheGroup* group = m_candidateApplicationCacheGroup; if (!group && m_applicationCache) { if (mainResourceApplicationCache()) { // Even when the main resource is being loaded from an application cache, loading can fail if aborted. return; } group = m_applicationCache->group(); } if (group) group->failedLoadingMainResource(m_documentLoader); }
void DocumentLoader::mainReceivedError(const ResourceError& error, bool isComplete) { #if ENABLE(OFFLINE_WEB_APPLICATIONS) ApplicationCacheGroup* group = m_candidateApplicationCacheGroup; if (!group && m_applicationCache && !mainResourceApplicationCache()) group = m_applicationCache->group(); if (group) group->failedLoadingMainResource(this); #endif if (!frameLoader()) return; setMainDocumentError(error); if (isComplete) frameLoader()->mainReceivedCompleteError(this, error); }
void ApplicationCache::deleteCacheForOrigin(SecurityOrigin* origin) { Vector<KURL> urls; if (!cacheStorage().manifestURLs(&urls)) { LOG_ERROR("Failed to retrieve ApplicationCache manifest URLs"); return; } KURL originURL(KURL(), origin->toString()); size_t count = urls.size(); for (size_t i = 0; i < count; ++i) { if (protocolHostAndPortAreEqual(urls[i], originURL)) { ApplicationCacheGroup* group = cacheStorage().findInMemoryCacheGroup(urls[i]); if (group) group->makeObsolete(); else cacheStorage().deleteCacheGroup(urls[i]); } } }
void DocumentLoader::mainReceivedError(const ResourceError& error, bool isComplete) { ASSERT(!error.isNull()); #if ENABLE(OFFLINE_WEB_APPLICATIONS) ApplicationCacheGroup* group = m_candidateApplicationCacheGroup; if (!group && m_applicationCache) { ASSERT(!mainResourceApplicationCache()); // If the main resource were loaded from a cache, it wouldn't fail. group = m_applicationCache->group(); } if (group) group->failedLoadingMainResource(this); #endif if (!frameLoader()) return; setMainDocumentError(error); if (isComplete) frameLoader()->mainReceivedCompleteError(this, error); }
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); } }
ApplicationCacheGroup* ApplicationCacheStorage::fallbackCacheGroupForURL(const KURL& url) { ASSERT(!url.hasFragmentIdentifier()); // Check if an appropriate 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 (ApplicationCache* cache = group->newestCache()) { KURL fallbackURL; if (!cache->urlMatchesFallbackNamespace(url, &fallbackURL)) continue; if (cache->resourceForURL(fallbackURL)->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; // Fallback namespaces always have the same origin as manifest URL, so we can avoid loading caches that cannot match. if (!protocolHostAndPortAreEqual(url, manifestURL)) continue; // We found a cache group that matches. Now check if the newest cache has a resource with // a matching fallback namespace. unsigned newestCacheID = static_cast<unsigned>(statement.getColumnInt64(2)); RefPtr<ApplicationCache> cache = loadCache(newestCacheID); KURL fallbackURL; if (!cache->urlMatchesFallbackNamespace(url, &fallbackURL)) continue; if (cache->resourceForURL(fallbackURL)->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; }
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; #if OS(OLYMPIA) ApplicationCacheGroup* group = new ApplicationCacheGroup(this, manifestURL); #else ApplicationCacheGroup* group = new ApplicationCacheGroup(manifestURL); #endif 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::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); }