void ApplicationCacheGroup::didReceiveManifestResponse(const ResourceResponse& response)
{
    ASSERT(!m_manifestResource);
    ASSERT(m_manifestHandle);

    if (response.httpStatusCode() == 404 || response.httpStatusCode() == 410) {
        manifestNotFound();
        return;
    }

    if (response.httpStatusCode() == 304)
        return;

    if (response.httpStatusCode() / 100 != 2) {
        m_frame->document()->addConsoleMessage(OtherMessageSource, ErrorMessageLevel, "Application Cache manifest could not be fetched.");
        cacheUpdateFailed();
        return;
    }

    if (response.url() != m_manifestHandle->firstRequest().url()) {
        m_frame->document()->addConsoleMessage(OtherMessageSource, ErrorMessageLevel, "Application Cache manifest could not be fetched, because a redirection was attempted.");
        cacheUpdateFailed();
        return;
    }

    m_manifestResource = ApplicationCacheResource::create(m_manifestHandle->firstRequest().url(), response, ApplicationCacheResource::Manifest);
}
Example #2
0
void ApplicationCacheGroup::didReceiveManifestResponse(const ResourceResponse& response)
{
    ASSERT(!m_manifestResource);
    ASSERT(m_manifestHandle);

    if (response.httpStatusCode() == 404 || response.httpStatusCode() == 410) {
        InspectorInstrumentation::didFailLoading(m_frame, m_frame->loader().documentLoader(), m_currentResourceIdentifier, m_frame->loader().cancelledError(m_manifestHandle->firstRequest()));
        m_frame->document()->addConsoleMessage(MessageSource::AppCache, MessageLevel::Error, makeString("Application Cache manifest could not be fetched, because the manifest had a ", String::number(response.httpStatusCode()), " response."));
        manifestNotFound();
        return;
    }

    if (response.httpStatusCode() == 304)
        return;

    if (response.httpStatusCode() / 100 != 2) {
        InspectorInstrumentation::didFailLoading(m_frame, m_frame->loader().documentLoader(), m_currentResourceIdentifier, m_frame->loader().cancelledError(m_manifestHandle->firstRequest()));
        m_frame->document()->addConsoleMessage(MessageSource::AppCache, MessageLevel::Error, makeString("Application Cache manifest could not be fetched, because the manifest had a ", String::number(response.httpStatusCode()), " response."));
        cacheUpdateFailed();
        return;
    }

    if (response.url() != m_manifestHandle->firstRequest().url()) {
        InspectorInstrumentation::didFailLoading(m_frame, m_frame->loader().documentLoader(), m_currentResourceIdentifier, m_frame->loader().cancelledError(m_manifestHandle->firstRequest()));
        m_frame->document()->addConsoleMessage(MessageSource::AppCache, MessageLevel::Error, ASCIILiteral("Application Cache manifest could not be fetched, because a redirection was attempted."));
        cacheUpdateFailed();
        return;
    }

    m_manifestResource = ApplicationCacheResource::create(m_manifestHandle->firstRequest().url(), response, ApplicationCacheResource::Manifest);
}
Example #3
0
void ApplicationCacheGroup::failedLoadingMainResource(DocumentLoader* loader)
{
    ASSERT(m_cacheCandidates.contains(loader) || m_associatedDocumentLoaders.contains(loader));

    // Note that cacheUpdateFailed() can cause the cache group to be deleted.
    cacheUpdateFailed();
}
Example #4
0
void ApplicationCacheGroup::didReceiveResponse(ResourceHandle* handle, const ResourceResponse& response)
{
    if (handle == m_manifestHandle) {
        didReceiveManifestResponse(response);
        return;
    }
    
    ASSERT(handle == m_currentHandle);
    
    int statusCode = response.httpStatusCode() / 100;
    if (statusCode == 4 || statusCode == 5) {
        // Note that cacheUpdateFailed() can cause the cache group to be deleted.
        cacheUpdateFailed();
        return;
    }
    
    const KURL& url = handle->request().url();
    
    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 implicit resources delivered here.
    if (!m_newestCache)
        ASSERT(!(type & ApplicationCacheResource::Implicit));
    
    m_currentResource = ApplicationCacheResource::create(url, response, type);
}
void ApplicationCacheGroup::stopLoadingInFrame(Frame* frame)
{
    if (frame != m_frame)
        return;

    cacheUpdateFailed();
}
Example #6
0
void ApplicationCacheGroup::didFinishLoading(ResourceHandle* handle, double finishTime)
{
    InspectorInstrumentation::didFinishLoading(m_frame, m_frame->loader().documentLoader(), m_currentResourceIdentifier, finishTime);

    if (handle == m_manifestHandle) {
        didFinishLoadingManifest();
        return;
    }

    ASSERT(m_currentHandle == handle);
    ASSERT(m_pendingEntries.contains(handle->firstRequest().url()));
    
    m_pendingEntries.remove(handle->firstRequest().url());
    
    ASSERT(m_cacheBeingUpdated);

    m_cacheBeingUpdated->addResource(m_currentResource.release());
    m_currentHandle = nullptr;

    // While downloading check to see if we have exceeded the available quota.
    // We can stop immediately if we have already previously failed
    // due to an earlier quota restriction. The client was already notified
    // of the quota being reached and decided not to increase it then.
    // FIXME: Should we break earlier and prevent redownloading on later page loads?
    if (m_originQuotaExceededPreviously && m_availableSpaceInQuota < m_cacheBeingUpdated->estimatedSizeInStorage()) {
        m_currentResource = nullptr;
        m_frame->document()->addConsoleMessage(MessageSource::AppCache, MessageLevel::Error, ASCIILiteral("Application Cache update failed, because size quota was exceeded."));
        cacheUpdateFailed();
        return;
    }
    
    // Load the next resource, if any.
    startLoadingEntry();
}
void ApplicationCacheGroup::cacheUpdateFailedDueToOriginQuota()
{
    if (!m_originQuotaReached) {
        m_originQuotaReached = true;
        scheduleReachedOriginQuotaCallback();
    }

    cacheUpdateFailed();
}
Example #8
0
void ApplicationCacheGroup::didFail(ResourceHandle* handle, const ResourceError&)
{
    if (handle == m_manifestHandle) {
        didFailToLoadManifest();
        return;
    }
    
    // Note that cacheUpdateFailed() can cause the cache group to be deleted.
    cacheUpdateFailed();
}
void ApplicationCacheGroup::abort(Frame* frame)
{
    if (m_updateStatus == Idle)
        return;
    ASSERT(m_updateStatus == Checking || (m_updateStatus == Downloading && m_cacheBeingUpdated));

    if (m_completionType != None)
        return;

    frame->document()->addConsoleMessage(NetworkMessageSource, DebugMessageLevel, "Application Cache download process was aborted.");
    cacheUpdateFailed();
}
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 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 ApplicationCacheGroup::didReceiveManifestResponse(const ResourceResponse& response)
{
    ASSERT(!m_manifestResource);
    ASSERT(m_manifestHandle);

    if (response.httpStatusCode() == 404 || response.httpStatusCode() == 410) {
        manifestNotFound();
        return;
    }

    if (response.httpStatusCode() == 304)
        return;

    if (response.httpStatusCode() / 100 != 2 || response.url() != m_manifestHandle->firstRequest().url() || !equalIgnoringCase(response.mimeType(), "text/cache-manifest")) {
        cacheUpdateFailed();
        return;
    }

    m_manifestResource = ApplicationCacheResource::create(m_manifestHandle->firstRequest().url(), response, ApplicationCacheResource::Manifest);
}
Example #13
0
void ApplicationCacheGroup::didFailToLoadManifest()
{
    // Note that cacheUpdateFailed() can cause the cache group to be deleted.
    cacheUpdateFailed();
}
Example #14
0
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();
}
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::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();
}