Home | History | Annotate | Download | only in appcache
      1 /*
      2  * Copyright (C) 2008, 2009, 2010 Apple Inc. All Rights Reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
     17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include "config.h"
     27 #include "ApplicationCacheGroup.h"
     28 
     29 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
     30 
     31 #include "ApplicationCache.h"
     32 #include "ApplicationCacheHost.h"
     33 #include "ApplicationCacheResource.h"
     34 #include "ApplicationCacheStorage.h"
     35 #include "Chrome.h"
     36 #include "ChromeClient.h"
     37 #include "DocumentLoader.h"
     38 #include "DOMApplicationCache.h"
     39 #include "DOMWindow.h"
     40 #include "Frame.h"
     41 #include "FrameLoader.h"
     42 #include "FrameLoaderClient.h"
     43 #include "InspectorInstrumentation.h"
     44 #include "MainResourceLoader.h"
     45 #include "ManifestParser.h"
     46 #include "Page.h"
     47 #include "SecurityOrigin.h"
     48 #include "Settings.h"
     49 #include <wtf/HashMap.h>
     50 #include <wtf/UnusedParam.h>
     51 
     52 #if ENABLE(INSPECTOR)
     53 #include "ProgressTracker.h"
     54 #endif
     55 
     56 namespace WebCore {
     57 
     58 ApplicationCacheGroup::ApplicationCacheGroup(const KURL& manifestURL, bool isCopy)
     59     : m_manifestURL(manifestURL)
     60     , m_origin(SecurityOrigin::create(manifestURL))
     61     , m_updateStatus(Idle)
     62     , m_downloadingPendingMasterResourceLoadersCount(0)
     63     , m_progressTotal(0)
     64     , m_progressDone(0)
     65     , m_frame(0)
     66     , m_storageID(0)
     67     , m_isObsolete(false)
     68     , m_completionType(None)
     69     , m_isCopy(isCopy)
     70     , m_calledReachedMaxAppCacheSize(false)
     71     , m_loadedSize(0)
     72     , m_availableSpaceInQuota(ApplicationCacheStorage::unknownQuota())
     73     , m_originQuotaReached(false)
     74 {
     75 }
     76 
     77 ApplicationCacheGroup::~ApplicationCacheGroup()
     78 {
     79     if (m_isCopy) {
     80         ASSERT(m_newestCache);
     81         ASSERT(m_caches.size() == 1);
     82         ASSERT(m_caches.contains(m_newestCache.get()));
     83         ASSERT(!m_cacheBeingUpdated);
     84         ASSERT(m_associatedDocumentLoaders.isEmpty());
     85         ASSERT(m_pendingMasterResourceLoaders.isEmpty());
     86         ASSERT(m_newestCache->group() == this);
     87 
     88         return;
     89     }
     90 
     91     ASSERT(!m_newestCache);
     92     ASSERT(m_caches.isEmpty());
     93 
     94     stopLoading();
     95 
     96     cacheStorage().cacheGroupDestroyed(this);
     97 }
     98 
     99 ApplicationCache* ApplicationCacheGroup::cacheForMainRequest(const ResourceRequest& request, DocumentLoader*)
    100 {
    101     if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request))
    102         return 0;
    103 
    104     KURL url(request.url());
    105     if (url.hasFragmentIdentifier())
    106         url.removeFragmentIdentifier();
    107 
    108     if (ApplicationCacheGroup* group = cacheStorage().cacheGroupForURL(url)) {
    109         ASSERT(group->newestCache());
    110         ASSERT(!group->isObsolete());
    111 
    112         return group->newestCache();
    113     }
    114 
    115     return 0;
    116 }
    117 
    118 ApplicationCache* ApplicationCacheGroup::fallbackCacheForMainRequest(const ResourceRequest& request, DocumentLoader*)
    119 {
    120     if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request))
    121         return 0;
    122 
    123     KURL url(request.url());
    124     if (url.hasFragmentIdentifier())
    125         url.removeFragmentIdentifier();
    126 
    127     if (ApplicationCacheGroup* group = cacheStorage().fallbackCacheGroupForURL(url)) {
    128         ASSERT(group->newestCache());
    129         ASSERT(!group->isObsolete());
    130 
    131         return group->newestCache();
    132     }
    133 
    134     return 0;
    135 }
    136 
    137 void ApplicationCacheGroup::selectCache(Frame* frame, const KURL& passedManifestURL)
    138 {
    139     ASSERT(frame && frame->page());
    140 
    141     if (!frame->settings()->offlineWebApplicationCacheEnabled())
    142         return;
    143 
    144     DocumentLoader* documentLoader = frame->loader()->documentLoader();
    145     ASSERT(!documentLoader->applicationCacheHost()->applicationCache());
    146 
    147     if (passedManifestURL.isNull()) {
    148         selectCacheWithoutManifestURL(frame);
    149         return;
    150     }
    151 
    152     KURL manifestURL(passedManifestURL);
    153     if (manifestURL.hasFragmentIdentifier())
    154         manifestURL.removeFragmentIdentifier();
    155 
    156     ApplicationCache* mainResourceCache = documentLoader->applicationCacheHost()->mainResourceApplicationCache();
    157 
    158     if (mainResourceCache) {
    159         if (manifestURL == mainResourceCache->group()->m_manifestURL) {
    160             // The cache may have gotten obsoleted after we've loaded from it, but before we parsed the document and saw cache manifest.
    161             if (mainResourceCache->group()->isObsolete())
    162                 return;
    163             mainResourceCache->group()->associateDocumentLoaderWithCache(documentLoader, mainResourceCache);
    164             mainResourceCache->group()->update(frame, ApplicationCacheUpdateWithBrowsingContext);
    165         } else {
    166             // The main resource was loaded from cache, so the cache must have an entry for it. Mark it as foreign.
    167             KURL resourceURL(documentLoader->responseURL());
    168             if (resourceURL.hasFragmentIdentifier())
    169                 resourceURL.removeFragmentIdentifier();
    170             ApplicationCacheResource* resource = mainResourceCache->resourceForURL(resourceURL);
    171             bool inStorage = resource->storageID();
    172             resource->addType(ApplicationCacheResource::Foreign);
    173             if (inStorage)
    174                 cacheStorage().storeUpdatedType(resource, mainResourceCache);
    175 
    176             // Restart the current navigation from the top of the navigation algorithm, undoing any changes that were made
    177             // as part of the initial load.
    178             // The navigation will not result in the same resource being loaded, because "foreign" entries are never picked during navigation.
    179             frame->navigationScheduler()->scheduleLocationChange(frame->document()->securityOrigin(), documentLoader->url(), frame->loader()->referrer(), true);
    180         }
    181 
    182         return;
    183     }
    184 
    185     // The resource was loaded from the network, check if it is a HTTP/HTTPS GET.
    186     const ResourceRequest& request = frame->loader()->activeDocumentLoader()->request();
    187 
    188     if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request))
    189         return;
    190 
    191     // Check that the resource URL has the same scheme/host/port as the manifest URL.
    192     if (!protocolHostAndPortAreEqual(manifestURL, request.url()))
    193         return;
    194 
    195     // Don't change anything on disk if private browsing is enabled.
    196     if (!frame->settings() || frame->settings()->privateBrowsingEnabled()) {
    197         postListenerTask(ApplicationCacheHost::CHECKING_EVENT, documentLoader);
    198         postListenerTask(ApplicationCacheHost::ERROR_EVENT, documentLoader);
    199         return;
    200     }
    201 
    202     ApplicationCacheGroup* group = cacheStorage().findOrCreateCacheGroup(manifestURL);
    203 
    204     documentLoader->applicationCacheHost()->setCandidateApplicationCacheGroup(group);
    205     group->m_pendingMasterResourceLoaders.add(documentLoader);
    206     group->m_downloadingPendingMasterResourceLoadersCount++;
    207 
    208     ASSERT(!group->m_cacheBeingUpdated || group->m_updateStatus != Idle);
    209     group->update(frame, ApplicationCacheUpdateWithBrowsingContext);
    210 }
    211 
    212 void ApplicationCacheGroup::selectCacheWithoutManifestURL(Frame* frame)
    213 {
    214     if (!frame->settings()->offlineWebApplicationCacheEnabled())
    215         return;
    216 
    217     DocumentLoader* documentLoader = frame->loader()->documentLoader();
    218     ASSERT(!documentLoader->applicationCacheHost()->applicationCache());
    219 
    220     ApplicationCache* mainResourceCache = documentLoader->applicationCacheHost()->mainResourceApplicationCache();
    221 
    222     if (mainResourceCache) {
    223         mainResourceCache->group()->associateDocumentLoaderWithCache(documentLoader, mainResourceCache);
    224         mainResourceCache->group()->update(frame, ApplicationCacheUpdateWithBrowsingContext);
    225     }
    226 }
    227 
    228 void ApplicationCacheGroup::finishedLoadingMainResource(DocumentLoader* loader)
    229 {
    230     ASSERT(m_pendingMasterResourceLoaders.contains(loader));
    231     ASSERT(m_completionType == None || m_pendingEntries.isEmpty());
    232     KURL url = loader->url();
    233     if (url.hasFragmentIdentifier())
    234         url.removeFragmentIdentifier();
    235 
    236     switch (m_completionType) {
    237     case None:
    238         // The main resource finished loading before the manifest was ready. It will be handled via dispatchMainResources() later.
    239         return;
    240     case NoUpdate:
    241         ASSERT(!m_cacheBeingUpdated);
    242         associateDocumentLoaderWithCache(loader, m_newestCache.get());
    243 
    244         if (ApplicationCacheResource* resource = m_newestCache->resourceForURL(url)) {
    245             if (!(resource->type() & ApplicationCacheResource::Master)) {
    246                 resource->addType(ApplicationCacheResource::Master);
    247                 ASSERT(!resource->storageID());
    248             }
    249         } else
    250             m_newestCache->addResource(ApplicationCacheResource::create(url, loader->response(), ApplicationCacheResource::Master, loader->mainResourceData()));
    251 
    252         break;
    253     case Failure:
    254         // Cache update has been a failure, so there is no reason to keep the document associated with the incomplete cache
    255         // (its main resource was not cached yet, so it is likely that the application changed significantly server-side).
    256         ASSERT(!m_cacheBeingUpdated); // Already cleared out by stopLoading().
    257         loader->applicationCacheHost()->setApplicationCache(0); // Will unset candidate, too.
    258         m_associatedDocumentLoaders.remove(loader);
    259         postListenerTask(ApplicationCacheHost::ERROR_EVENT, loader);
    260         break;
    261     case Completed:
    262         ASSERT(m_associatedDocumentLoaders.contains(loader));
    263 
    264         if (ApplicationCacheResource* resource = m_cacheBeingUpdated->resourceForURL(url)) {
    265             if (!(resource->type() & ApplicationCacheResource::Master)) {
    266                 resource->addType(ApplicationCacheResource::Master);
    267                 ASSERT(!resource->storageID());
    268             }
    269         } else
    270             m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, loader->response(), ApplicationCacheResource::Master, loader->mainResourceData()));
    271         // The "cached" event will be posted to all associated documents once update is complete.
    272         break;
    273     }
    274 
    275     m_downloadingPendingMasterResourceLoadersCount--;
    276     checkIfLoadIsComplete();
    277 }
    278 
    279 void ApplicationCacheGroup::failedLoadingMainResource(DocumentLoader* loader)
    280 {
    281     ASSERT(m_pendingMasterResourceLoaders.contains(loader));
    282     ASSERT(m_completionType == None || m_pendingEntries.isEmpty());
    283 
    284     switch (m_completionType) {
    285     case None:
    286         // The main resource finished loading before the manifest was ready. It will be handled via dispatchMainResources() later.
    287         return;
    288     case NoUpdate:
    289         ASSERT(!m_cacheBeingUpdated);
    290 
    291         // The manifest didn't change, and we have a relevant cache - but the main resource download failed mid-way, so it cannot be stored to the cache,
    292         // and the loader does not get associated to it. If there are other main resources being downloaded for this cache group, they may still succeed.
    293         postListenerTask(ApplicationCacheHost::ERROR_EVENT, loader);
    294 
    295         break;
    296     case Failure:
    297         // Cache update failed, too.
    298         ASSERT(!m_cacheBeingUpdated); // Already cleared out by stopLoading().
    299         ASSERT(!loader->applicationCacheHost()->applicationCache() || loader->applicationCacheHost()->applicationCache() == m_cacheBeingUpdated);
    300 
    301         loader->applicationCacheHost()->setApplicationCache(0); // Will unset candidate, too.
    302         m_associatedDocumentLoaders.remove(loader);
    303         postListenerTask(ApplicationCacheHost::ERROR_EVENT, loader);
    304         break;
    305     case Completed:
    306         // The cache manifest didn't list this main resource, and all cache entries were already updated successfully - but the main resource failed to load,
    307         // so it cannot be stored to the cache. If there are other main resources being downloaded for this cache group, they may still succeed.
    308         ASSERT(m_associatedDocumentLoaders.contains(loader));
    309         ASSERT(loader->applicationCacheHost()->applicationCache() == m_cacheBeingUpdated);
    310         ASSERT(!loader->applicationCacheHost()->candidateApplicationCacheGroup());
    311         m_associatedDocumentLoaders.remove(loader);
    312         loader->applicationCacheHost()->setApplicationCache(0);
    313 
    314         postListenerTask(ApplicationCacheHost::ERROR_EVENT, loader);
    315 
    316         break;
    317     }
    318 
    319     m_downloadingPendingMasterResourceLoadersCount--;
    320     checkIfLoadIsComplete();
    321 }
    322 
    323 void ApplicationCacheGroup::stopLoading()
    324 {
    325     if (m_manifestHandle) {
    326         ASSERT(!m_currentHandle);
    327 
    328         m_manifestHandle->setClient(0);
    329         m_manifestHandle->cancel();
    330         m_manifestHandle = 0;
    331     }
    332 
    333     if (m_currentHandle) {
    334         ASSERT(!m_manifestHandle);
    335         ASSERT(m_cacheBeingUpdated);
    336 
    337         m_currentHandle->setClient(0);
    338         m_currentHandle->cancel();
    339         m_currentHandle = 0;
    340     }
    341 
    342     m_cacheBeingUpdated = 0;
    343     m_pendingEntries.clear();
    344 }
    345 
    346 void ApplicationCacheGroup::disassociateDocumentLoader(DocumentLoader* loader)
    347 {
    348     HashSet<DocumentLoader*>::iterator it = m_associatedDocumentLoaders.find(loader);
    349     if (it != m_associatedDocumentLoaders.end())
    350         m_associatedDocumentLoaders.remove(it);
    351 
    352     m_pendingMasterResourceLoaders.remove(loader);
    353 
    354     loader->applicationCacheHost()->setApplicationCache(0); // Will set candidate to 0, too.
    355 
    356     if (!m_associatedDocumentLoaders.isEmpty() || !m_pendingMasterResourceLoaders.isEmpty())
    357         return;
    358 
    359     if (m_caches.isEmpty()) {
    360         // There is an initial cache attempt in progress.
    361         ASSERT(!m_newestCache);
    362         // Delete ourselves, causing the cache attempt to be stopped.
    363         delete this;
    364         return;
    365     }
    366 
    367     ASSERT(m_caches.contains(m_newestCache.get()));
    368 
    369     // Release our reference to the newest cache. This could cause us to be deleted.
    370     // Any ongoing updates will be stopped from destructor.
    371     m_newestCache.release();
    372 }
    373 
    374 void ApplicationCacheGroup::cacheDestroyed(ApplicationCache* cache)
    375 {
    376     if (!m_caches.contains(cache))
    377         return;
    378 
    379     m_caches.remove(cache);
    380 
    381     if (m_caches.isEmpty()) {
    382         ASSERT(m_associatedDocumentLoaders.isEmpty());
    383         ASSERT(m_pendingMasterResourceLoaders.isEmpty());
    384         delete this;
    385     }
    386 }
    387 
    388 void ApplicationCacheGroup::stopLoadingInFrame(Frame* frame)
    389 {
    390     if (frame != m_frame)
    391         return;
    392 
    393     stopLoading();
    394 }
    395 
    396 void ApplicationCacheGroup::setNewestCache(PassRefPtr<ApplicationCache> newestCache)
    397 {
    398     m_newestCache = newestCache;
    399 
    400     m_caches.add(m_newestCache.get());
    401     m_newestCache->setGroup(this);
    402     InspectorInstrumentation::updateApplicationCacheStatus(m_frame);
    403 }
    404 
    405 void ApplicationCacheGroup::makeObsolete()
    406 {
    407     if (isObsolete())
    408         return;
    409 
    410     m_isObsolete = true;
    411     cacheStorage().cacheGroupMadeObsolete(this);
    412     ASSERT(!m_storageID);
    413     InspectorInstrumentation::updateApplicationCacheStatus(m_frame);
    414 }
    415 
    416 void ApplicationCacheGroup::update(Frame* frame, ApplicationCacheUpdateOption updateOption)
    417 {
    418     if (m_updateStatus == Checking || m_updateStatus == Downloading) {
    419         if (updateOption == ApplicationCacheUpdateWithBrowsingContext) {
    420             postListenerTask(ApplicationCacheHost::CHECKING_EVENT, frame->loader()->documentLoader());
    421             if (m_updateStatus == Downloading)
    422                 postListenerTask(ApplicationCacheHost::DOWNLOADING_EVENT, frame->loader()->documentLoader());
    423         }
    424         return;
    425     }
    426 
    427     // Don't change anything on disk if private browsing is enabled.
    428     if (!frame->settings() || frame->settings()->privateBrowsingEnabled()) {
    429         ASSERT(m_pendingMasterResourceLoaders.isEmpty());
    430         ASSERT(m_pendingEntries.isEmpty());
    431         ASSERT(!m_cacheBeingUpdated);
    432         postListenerTask(ApplicationCacheHost::CHECKING_EVENT, frame->loader()->documentLoader());
    433         postListenerTask(ApplicationCacheHost::NOUPDATE_EVENT, frame->loader()->documentLoader());
    434         return;
    435     }
    436 
    437     ASSERT(!m_frame);
    438     m_frame = frame;
    439 
    440     setUpdateStatus(Checking);
    441 
    442     postListenerTask(ApplicationCacheHost::CHECKING_EVENT, m_associatedDocumentLoaders);
    443     if (!m_newestCache) {
    444         ASSERT(updateOption == ApplicationCacheUpdateWithBrowsingContext);
    445         postListenerTask(ApplicationCacheHost::CHECKING_EVENT, frame->loader()->documentLoader());
    446     }
    447 
    448     ASSERT(!m_manifestHandle);
    449     ASSERT(!m_manifestResource);
    450     ASSERT(m_completionType == None);
    451 
    452     // FIXME: Handle defer loading
    453     m_manifestHandle = createResourceHandle(m_manifestURL, m_newestCache ? m_newestCache->manifestResource() : 0);
    454 }
    455 
    456 PassRefPtr<ResourceHandle> ApplicationCacheGroup::createResourceHandle(const KURL& url, ApplicationCacheResource* newestCachedResource)
    457 {
    458     ResourceRequest request(url);
    459     m_frame->loader()->applyUserAgent(request);
    460     request.setHTTPHeaderField("Cache-Control", "max-age=0");
    461 
    462     if (newestCachedResource) {
    463         const String& lastModified = newestCachedResource->response().httpHeaderField("Last-Modified");
    464         const String& eTag = newestCachedResource->response().httpHeaderField("ETag");
    465         if (!lastModified.isEmpty() || !eTag.isEmpty()) {
    466             if (!lastModified.isEmpty())
    467                 request.setHTTPHeaderField("If-Modified-Since", lastModified);
    468             if (!eTag.isEmpty())
    469                 request.setHTTPHeaderField("If-None-Match", eTag);
    470         }
    471     }
    472 
    473     RefPtr<ResourceHandle> handle = ResourceHandle::create(m_frame->loader()->networkingContext(), request, this, false, true);
    474 #if ENABLE(INSPECTOR)
    475     // Because willSendRequest only gets called during redirects, we initialize
    476     // the identifier and the first willSendRequest here.
    477     m_currentResourceIdentifier = m_frame->page()->progress()->createUniqueIdentifier();
    478     ResourceResponse redirectResponse = ResourceResponse();
    479     InspectorInstrumentation::willSendRequest(m_frame, m_currentResourceIdentifier, m_frame->loader()->documentLoader(), request, redirectResponse);
    480 #endif
    481     return handle;
    482 }
    483 
    484 void ApplicationCacheGroup::didReceiveResponse(ResourceHandle* handle, const ResourceResponse& response)
    485 {
    486 #if ENABLE(INSPECTOR)
    487     DocumentLoader* loader = (handle == m_manifestHandle) ? 0 : m_frame->loader()->documentLoader();
    488     InspectorInstrumentationCookie cookie = InspectorInstrumentation::willReceiveResourceResponse(m_frame, m_currentResourceIdentifier, response);
    489     InspectorInstrumentation::didReceiveResourceResponse(cookie, m_currentResourceIdentifier, loader, response);
    490 #endif
    491 
    492     if (handle == m_manifestHandle) {
    493         didReceiveManifestResponse(response);
    494         return;
    495     }
    496 
    497     ASSERT(handle == m_currentHandle);
    498 
    499     KURL url(handle->firstRequest().url());
    500     if (url.hasFragmentIdentifier())
    501         url.removeFragmentIdentifier();
    502 
    503     ASSERT(!m_currentResource);
    504     ASSERT(m_pendingEntries.contains(url));
    505 
    506     unsigned type = m_pendingEntries.get(url);
    507 
    508     // If this is an initial cache attempt, we should not get master resources delivered here.
    509     if (!m_newestCache)
    510         ASSERT(!(type & ApplicationCacheResource::Master));
    511 
    512     if (m_newestCache && response.httpStatusCode() == 304) { // Not modified.
    513         ApplicationCacheResource* newestCachedResource = m_newestCache->resourceForURL(url);
    514         if (newestCachedResource) {
    515             m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, newestCachedResource->response(), type, newestCachedResource->data(), newestCachedResource->path()));
    516             m_pendingEntries.remove(m_currentHandle->firstRequest().url());
    517             m_currentHandle->cancel();
    518             m_currentHandle = 0;
    519             // Load the next resource, if any.
    520             startLoadingEntry();
    521             return;
    522         }
    523         // The server could return 304 for an unconditional request - in this case, we handle the response as a normal error.
    524     }
    525 
    526     if (response.httpStatusCode() / 100 != 2 || response.url() != m_currentHandle->firstRequest().url()) {
    527         if ((type & ApplicationCacheResource::Explicit) || (type & ApplicationCacheResource::Fallback)) {
    528             // Note that cacheUpdateFailed() can cause the cache group to be deleted.
    529             cacheUpdateFailed();
    530         } else if (response.httpStatusCode() == 404 || response.httpStatusCode() == 410) {
    531             // Skip this resource. It is dropped from the cache.
    532             m_currentHandle->cancel();
    533             m_currentHandle = 0;
    534             m_pendingEntries.remove(url);
    535             // Load the next resource, if any.
    536             startLoadingEntry();
    537         } else {
    538             // Copy the resource and its metadata from the newest application cache in cache group whose completeness flag is complete, and act
    539             // as if that was the fetched resource, ignoring the resource obtained from the network.
    540             ASSERT(m_newestCache);
    541             ApplicationCacheResource* newestCachedResource = m_newestCache->resourceForURL(handle->firstRequest().url());
    542             ASSERT(newestCachedResource);
    543             m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, newestCachedResource->response(), type, newestCachedResource->data(), newestCachedResource->path()));
    544             m_pendingEntries.remove(m_currentHandle->firstRequest().url());
    545             m_currentHandle->cancel();
    546             m_currentHandle = 0;
    547             // Load the next resource, if any.
    548             startLoadingEntry();
    549         }
    550         return;
    551     }
    552 
    553     m_currentResource = ApplicationCacheResource::create(url, response, type);
    554 }
    555 
    556 void ApplicationCacheGroup::didReceiveData(ResourceHandle* handle, const char* data, int length, int encodedDataLength)
    557 {
    558     UNUSED_PARAM(encodedDataLength);
    559 
    560 #if ENABLE(INSPECTOR)
    561     InspectorInstrumentation::didReceiveContentLength(m_frame, m_currentResourceIdentifier, length, 0);
    562 #endif
    563 
    564     if (handle == m_manifestHandle) {
    565         didReceiveManifestData(data, length);
    566         return;
    567     }
    568 
    569     ASSERT(handle == m_currentHandle);
    570 
    571     ASSERT(m_currentResource);
    572     m_currentResource->data()->append(data, length);
    573 
    574     m_loadedSize += length;
    575 }
    576 
    577 void ApplicationCacheGroup::didFinishLoading(ResourceHandle* handle, double finishTime)
    578 {
    579 #if ENABLE(INSPECTOR)
    580     InspectorInstrumentation::didFinishLoading(m_frame, m_currentResourceIdentifier, finishTime);
    581 #endif
    582 
    583     if (handle == m_manifestHandle) {
    584         didFinishLoadingManifest();
    585         return;
    586     }
    587 
    588     // After finishing the loading of any resource, we check if it will
    589     // fit in our last known quota limit.
    590     if (m_availableSpaceInQuota == ApplicationCacheStorage::unknownQuota()) {
    591         // Failed to determine what is left in the quota. Fallback to allowing anything.
    592         if (!cacheStorage().remainingSizeForOriginExcludingCache(m_origin.get(), m_newestCache.get(), m_availableSpaceInQuota))
    593             m_availableSpaceInQuota = ApplicationCacheStorage::noQuota();
    594     }
    595 
    596     // Check each resource, as it loads, to see if it would fit in our
    597     // idea of the available quota space.
    598     if (m_availableSpaceInQuota < m_loadedSize) {
    599         m_currentResource = 0;
    600         cacheUpdateFailedDueToOriginQuota();
    601         return;
    602     }
    603 
    604     ASSERT(m_currentHandle == handle);
    605     ASSERT(m_pendingEntries.contains(handle->firstRequest().url()));
    606 
    607     m_pendingEntries.remove(handle->firstRequest().url());
    608 
    609     ASSERT(m_cacheBeingUpdated);
    610 
    611     m_cacheBeingUpdated->addResource(m_currentResource.release());
    612     m_currentHandle = 0;
    613 
    614     // Load the next resource, if any.
    615     startLoadingEntry();
    616 }
    617 
    618 void ApplicationCacheGroup::didFail(ResourceHandle* handle, const ResourceError& error)
    619 {
    620 #if ENABLE(INSPECTOR)
    621     InspectorInstrumentation::didFailLoading(m_frame, m_currentResourceIdentifier, error);
    622 #endif
    623 
    624     if (handle == m_manifestHandle) {
    625         cacheUpdateFailed();
    626         return;
    627     }
    628 
    629     unsigned type = m_currentResource ? m_currentResource->type() : m_pendingEntries.get(handle->firstRequest().url());
    630     KURL url(handle->firstRequest().url());
    631     if (url.hasFragmentIdentifier())
    632         url.removeFragmentIdentifier();
    633 
    634     ASSERT(!m_currentResource || !m_pendingEntries.contains(url));
    635     m_currentResource = 0;
    636     m_pendingEntries.remove(url);
    637 
    638     if ((type & ApplicationCacheResource::Explicit) || (type & ApplicationCacheResource::Fallback)) {
    639         // Note that cacheUpdateFailed() can cause the cache group to be deleted.
    640         cacheUpdateFailed();
    641     } else {
    642         // Copy the resource and its metadata from the newest application cache in cache group whose completeness flag is complete, and act
    643         // as if that was the fetched resource, ignoring the resource obtained from the network.
    644         ASSERT(m_newestCache);
    645         ApplicationCacheResource* newestCachedResource = m_newestCache->resourceForURL(url);
    646         ASSERT(newestCachedResource);
    647         m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, newestCachedResource->response(), type, newestCachedResource->data(), newestCachedResource->path()));
    648         // Load the next resource, if any.
    649         startLoadingEntry();
    650     }
    651 }
    652 
    653 void ApplicationCacheGroup::didReceiveManifestResponse(const ResourceResponse& response)
    654 {
    655     ASSERT(!m_manifestResource);
    656     ASSERT(m_manifestHandle);
    657 
    658     if (response.httpStatusCode() == 404 || response.httpStatusCode() == 410) {
    659         manifestNotFound();
    660         return;
    661     }
    662 
    663     if (response.httpStatusCode() == 304)
    664         return;
    665 
    666     if (response.httpStatusCode() / 100 != 2 || response.url() != m_manifestHandle->firstRequest().url() || !equalIgnoringCase(response.mimeType(), "text/cache-manifest")) {
    667         cacheUpdateFailed();
    668         return;
    669     }
    670 
    671     m_manifestResource = ApplicationCacheResource::create(m_manifestHandle->firstRequest().url(), response, ApplicationCacheResource::Manifest);
    672 }
    673 
    674 void ApplicationCacheGroup::didReceiveManifestData(const char* data, int length)
    675 {
    676     if (m_manifestResource)
    677         m_manifestResource->data()->append(data, length);
    678 }
    679 
    680 void ApplicationCacheGroup::didFinishLoadingManifest()
    681 {
    682     bool isUpgradeAttempt = m_newestCache;
    683 
    684     if (!isUpgradeAttempt && !m_manifestResource) {
    685         // The server returned 304 Not Modified even though we didn't send a conditional request.
    686         cacheUpdateFailed();
    687         return;
    688     }
    689 
    690     m_manifestHandle = 0;
    691 
    692     // Check if the manifest was not modified.
    693     if (isUpgradeAttempt) {
    694         ApplicationCacheResource* newestManifest = m_newestCache->manifestResource();
    695         ASSERT(newestManifest);
    696 
    697         if (!m_manifestResource || // The resource will be null if HTTP response was 304 Not Modified.
    698             (newestManifest->data()->size() == m_manifestResource->data()->size() && !memcmp(newestManifest->data()->data(), m_manifestResource->data()->data(), newestManifest->data()->size()))) {
    699 
    700             m_completionType = NoUpdate;
    701             m_manifestResource = 0;
    702             deliverDelayedMainResources();
    703 
    704             return;
    705         }
    706     }
    707 
    708     Manifest manifest;
    709     if (!parseManifest(m_manifestURL, m_manifestResource->data()->data(), m_manifestResource->data()->size(), manifest)) {
    710         cacheUpdateFailed();
    711         return;
    712     }
    713 
    714     ASSERT(!m_cacheBeingUpdated);
    715     m_cacheBeingUpdated = ApplicationCache::create();
    716     m_cacheBeingUpdated->setGroup(this);
    717 
    718     HashSet<DocumentLoader*>::const_iterator masterEnd = m_pendingMasterResourceLoaders.end();
    719     for (HashSet<DocumentLoader*>::const_iterator iter = m_pendingMasterResourceLoaders.begin(); iter != masterEnd; ++iter)
    720         associateDocumentLoaderWithCache(*iter, m_cacheBeingUpdated.get());
    721 
    722     // We have the manifest, now download the resources.
    723     setUpdateStatus(Downloading);
    724 
    725     postListenerTask(ApplicationCacheHost::DOWNLOADING_EVENT, m_associatedDocumentLoaders);
    726 
    727     ASSERT(m_pendingEntries.isEmpty());
    728 
    729     if (isUpgradeAttempt) {
    730         ApplicationCache::ResourceMap::const_iterator end = m_newestCache->end();
    731         for (ApplicationCache::ResourceMap::const_iterator it = m_newestCache->begin(); it != end; ++it) {
    732             unsigned type = it->second->type();
    733             if (type & ApplicationCacheResource::Master)
    734                 addEntry(it->first, type);
    735         }
    736     }
    737 
    738     HashSet<String>::const_iterator end = manifest.explicitURLs.end();
    739     for (HashSet<String>::const_iterator it = manifest.explicitURLs.begin(); it != end; ++it)
    740         addEntry(*it, ApplicationCacheResource::Explicit);
    741 
    742     size_t fallbackCount = manifest.fallbackURLs.size();
    743     for (size_t i = 0; i  < fallbackCount; ++i)
    744         addEntry(manifest.fallbackURLs[i].second, ApplicationCacheResource::Fallback);
    745 
    746     m_cacheBeingUpdated->setOnlineWhitelist(manifest.onlineWhitelistedURLs);
    747     m_cacheBeingUpdated->setFallbackURLs(manifest.fallbackURLs);
    748     m_cacheBeingUpdated->setAllowsAllNetworkRequests(manifest.allowAllNetworkRequests);
    749 
    750     m_progressTotal = m_pendingEntries.size();
    751     m_progressDone = 0;
    752 
    753     startLoadingEntry();
    754 }
    755 
    756 void ApplicationCacheGroup::didReachMaxAppCacheSize()
    757 {
    758     ASSERT(m_frame);
    759     ASSERT(m_cacheBeingUpdated);
    760     m_frame->page()->chrome()->client()->reachedMaxAppCacheSize(cacheStorage().spaceNeeded(m_cacheBeingUpdated->estimatedSizeInStorage()));
    761     m_calledReachedMaxAppCacheSize = true;
    762     checkIfLoadIsComplete();
    763 }
    764 
    765 void ApplicationCacheGroup::didReachOriginQuota(PassRefPtr<Frame> frame)
    766 {
    767     // Inform the client the origin quota has been reached,
    768     // they may decide to increase the quota.
    769     frame->page()->chrome()->client()->reachedApplicationCacheOriginQuota(m_origin.get());
    770 }
    771 
    772 void ApplicationCacheGroup::cacheUpdateFailed()
    773 {
    774     stopLoading();
    775     m_manifestResource = 0;
    776 
    777     // Wait for master resource loads to finish.
    778     m_completionType = Failure;
    779     deliverDelayedMainResources();
    780 }
    781 
    782 void ApplicationCacheGroup::cacheUpdateFailedDueToOriginQuota()
    783 {
    784     if (!m_originQuotaReached) {
    785         m_originQuotaReached = true;
    786         scheduleReachedOriginQuotaCallback();
    787     }
    788 
    789     cacheUpdateFailed();
    790 }
    791 
    792 void ApplicationCacheGroup::manifestNotFound()
    793 {
    794     makeObsolete();
    795 
    796     postListenerTask(ApplicationCacheHost::OBSOLETE_EVENT, m_associatedDocumentLoaders);
    797     postListenerTask(ApplicationCacheHost::ERROR_EVENT, m_pendingMasterResourceLoaders);
    798 
    799     stopLoading();
    800 
    801     ASSERT(m_pendingEntries.isEmpty());
    802     m_manifestResource = 0;
    803 
    804     while (!m_pendingMasterResourceLoaders.isEmpty()) {
    805         HashSet<DocumentLoader*>::iterator it = m_pendingMasterResourceLoaders.begin();
    806 
    807         ASSERT((*it)->applicationCacheHost()->candidateApplicationCacheGroup() == this);
    808         ASSERT(!(*it)->applicationCacheHost()->applicationCache());
    809         (*it)->applicationCacheHost()->setCandidateApplicationCacheGroup(0);
    810         m_pendingMasterResourceLoaders.remove(it);
    811     }
    812 
    813     m_downloadingPendingMasterResourceLoadersCount = 0;
    814     setUpdateStatus(Idle);
    815     m_frame = 0;
    816 
    817     if (m_caches.isEmpty()) {
    818         ASSERT(m_associatedDocumentLoaders.isEmpty());
    819         ASSERT(!m_cacheBeingUpdated);
    820         delete this;
    821     }
    822 }
    823 
    824 void ApplicationCacheGroup::checkIfLoadIsComplete()
    825 {
    826     if (m_manifestHandle || !m_pendingEntries.isEmpty() || m_downloadingPendingMasterResourceLoadersCount)
    827         return;
    828 
    829     // We're done, all resources have finished downloading (successfully or not).
    830 
    831     bool isUpgradeAttempt = m_newestCache;
    832 
    833     switch (m_completionType) {
    834     case None:
    835         ASSERT_NOT_REACHED();
    836         return;
    837     case NoUpdate:
    838         ASSERT(isUpgradeAttempt);
    839         ASSERT(!m_cacheBeingUpdated);
    840 
    841         // The storage could have been manually emptied by the user.
    842         if (!m_storageID)
    843             cacheStorage().storeNewestCache(this);
    844 
    845         postListenerTask(ApplicationCacheHost::NOUPDATE_EVENT, m_associatedDocumentLoaders);
    846         break;
    847     case Failure:
    848         ASSERT(!m_cacheBeingUpdated);
    849         postListenerTask(ApplicationCacheHost::ERROR_EVENT, m_associatedDocumentLoaders);
    850         if (m_caches.isEmpty()) {
    851             ASSERT(m_associatedDocumentLoaders.isEmpty());
    852             delete this;
    853             return;
    854         }
    855         break;
    856     case Completed: {
    857         // FIXME: Fetch the resource from manifest URL again, and check whether it is identical to the one used for update (in case the application was upgraded server-side in the meanwhile). (<rdar://problem/6467625>)
    858 
    859         ASSERT(m_cacheBeingUpdated);
    860         if (m_manifestResource)
    861             m_cacheBeingUpdated->setManifestResource(m_manifestResource.release());
    862         else {
    863             // We can get here as a result of retrying the Complete step, following
    864             // a failure of the cache storage to save the newest cache due to hitting
    865             // the maximum size. In such a case, m_manifestResource may be 0, as
    866             // the manifest was already set on the newest cache object.
    867             ASSERT(cacheStorage().isMaximumSizeReached() && m_calledReachedMaxAppCacheSize);
    868         }
    869 
    870         ApplicationCacheStorage::FailureReason failureReason;
    871         RefPtr<ApplicationCache> oldNewestCache = (m_newestCache == m_cacheBeingUpdated) ? RefPtr<ApplicationCache>() : m_newestCache;
    872         setNewestCache(m_cacheBeingUpdated.release());
    873         if (cacheStorage().storeNewestCache(this, oldNewestCache.get(), failureReason)) {
    874             // New cache stored, now remove the old cache.
    875             if (oldNewestCache)
    876                 cacheStorage().remove(oldNewestCache.get());
    877 
    878             // Fire the final progress event.
    879             ASSERT(m_progressDone == m_progressTotal);
    880             postListenerTask(ApplicationCacheHost::PROGRESS_EVENT, m_progressTotal, m_progressDone, m_associatedDocumentLoaders);
    881 
    882             // Fire the success event.
    883             postListenerTask(isUpgradeAttempt ? ApplicationCacheHost::UPDATEREADY_EVENT : ApplicationCacheHost::CACHED_EVENT, m_associatedDocumentLoaders);
    884             // It is clear that the origin quota was not reached, so clear the flag if it was set.
    885             m_originQuotaReached = false;
    886         } else {
    887             if (failureReason == ApplicationCacheStorage::OriginQuotaReached) {
    888                 // We ran out of space for this origin. Roll back to previous state.
    889                 if (oldNewestCache)
    890                     setNewestCache(oldNewestCache.release());
    891                 cacheUpdateFailedDueToOriginQuota();
    892                 return;
    893             }
    894 
    895             if (failureReason == ApplicationCacheStorage::TotalQuotaReached && !m_calledReachedMaxAppCacheSize) {
    896                 // We ran out of space. All the changes in the cache storage have
    897                 // been rolled back. We roll back to the previous state in here,
    898                 // as well, call the chrome client asynchronously and retry to
    899                 // save the new cache.
    900 
    901                 // Save a reference to the new cache.
    902                 m_cacheBeingUpdated = m_newestCache.release();
    903                 if (oldNewestCache) {
    904                     // Reinstate the oldNewestCache.
    905                     setNewestCache(oldNewestCache.release());
    906                 }
    907                 scheduleReachedMaxAppCacheSizeCallback();
    908                 return;
    909             }
    910 
    911             // Run the "cache failure steps"
    912             // Fire the error events to all pending master entries, as well any other cache hosts
    913             // currently associated with a cache in this group.
    914             postListenerTask(ApplicationCacheHost::ERROR_EVENT, m_associatedDocumentLoaders);
    915             // Disassociate the pending master entries from the failed new cache. Note that
    916             // all other loaders in the m_associatedDocumentLoaders are still associated with
    917             // some other cache in this group. They are not associated with the failed new cache.
    918 
    919             // Need to copy loaders, because the cache group may be destroyed at the end of iteration.
    920             Vector<DocumentLoader*> loaders;
    921             copyToVector(m_pendingMasterResourceLoaders, loaders);
    922             size_t count = loaders.size();
    923             for (size_t i = 0; i != count; ++i)
    924                 disassociateDocumentLoader(loaders[i]); // This can delete this group.
    925 
    926             // Reinstate the oldNewestCache, if there was one.
    927             if (oldNewestCache) {
    928                 // This will discard the failed new cache.
    929                 setNewestCache(oldNewestCache.release());
    930             } else {
    931                 // We must have been deleted by the last call to disassociateDocumentLoader().
    932                 return;
    933             }
    934         }
    935         break;
    936     }
    937     }
    938 
    939     // Empty cache group's list of pending master entries.
    940     m_pendingMasterResourceLoaders.clear();
    941     m_completionType = None;
    942     setUpdateStatus(Idle);
    943     m_frame = 0;
    944     m_loadedSize = 0;
    945     m_availableSpaceInQuota = ApplicationCacheStorage::unknownQuota();
    946     m_calledReachedMaxAppCacheSize = false;
    947 }
    948 
    949 void ApplicationCacheGroup::startLoadingEntry()
    950 {
    951     ASSERT(m_cacheBeingUpdated);
    952 
    953     if (m_pendingEntries.isEmpty()) {
    954         m_completionType = Completed;
    955         deliverDelayedMainResources();
    956         return;
    957     }
    958 
    959     EntryMap::const_iterator it = m_pendingEntries.begin();
    960 
    961     postListenerTask(ApplicationCacheHost::PROGRESS_EVENT, m_progressTotal, m_progressDone, m_associatedDocumentLoaders);
    962     m_progressDone++;
    963 
    964     ASSERT(!m_currentHandle);
    965 
    966     m_currentHandle = createResourceHandle(KURL(ParsedURLString, it->first), m_newestCache ? m_newestCache->resourceForURL(it->first) : 0);
    967 }
    968 
    969 void ApplicationCacheGroup::deliverDelayedMainResources()
    970 {
    971     // Need to copy loaders, because the cache group may be destroyed at the end of iteration.
    972     Vector<DocumentLoader*> loaders;
    973     copyToVector(m_pendingMasterResourceLoaders, loaders);
    974     size_t count = loaders.size();
    975     for (size_t i = 0; i != count; ++i) {
    976         DocumentLoader* loader = loaders[i];
    977         if (loader->isLoadingMainResource())
    978             continue;
    979 
    980         const ResourceError& error = loader->mainDocumentError();
    981         if (error.isNull())
    982             finishedLoadingMainResource(loader);
    983         else
    984             failedLoadingMainResource(loader);
    985     }
    986     if (!count)
    987         checkIfLoadIsComplete();
    988 }
    989 
    990 void ApplicationCacheGroup::addEntry(const String& url, unsigned type)
    991 {
    992     ASSERT(m_cacheBeingUpdated);
    993     ASSERT(!KURL(ParsedURLString, url).hasFragmentIdentifier());
    994 
    995     // Don't add the URL if we already have an master resource in the cache
    996     // (i.e., the main resource finished loading before the manifest).
    997     if (ApplicationCacheResource* resource = m_cacheBeingUpdated->resourceForURL(url)) {
    998         ASSERT(resource->type() & ApplicationCacheResource::Master);
    999         ASSERT(!m_frame->loader()->documentLoader()->isLoadingMainResource());
   1000 
   1001         resource->addType(type);
   1002         return;
   1003     }
   1004 
   1005     // Don't add the URL if it's the same as the manifest URL.
   1006     ASSERT(m_manifestResource);
   1007     if (m_manifestResource->url() == url) {
   1008         m_manifestResource->addType(type);
   1009         return;
   1010     }
   1011 
   1012     pair<EntryMap::iterator, bool> result = m_pendingEntries.add(url, type);
   1013 
   1014     if (!result.second)
   1015         result.first->second |= type;
   1016 }
   1017 
   1018 void ApplicationCacheGroup::associateDocumentLoaderWithCache(DocumentLoader* loader, ApplicationCache* cache)
   1019 {
   1020     // If teardown started already, revive the group.
   1021     if (!m_newestCache && !m_cacheBeingUpdated)
   1022         m_newestCache = cache;
   1023 
   1024     ASSERT(!m_isObsolete);
   1025 
   1026     loader->applicationCacheHost()->setApplicationCache(cache);
   1027 
   1028     ASSERT(!m_associatedDocumentLoaders.contains(loader));
   1029     m_associatedDocumentLoaders.add(loader);
   1030 }
   1031 
   1032 class ChromeClientCallbackTimer: public TimerBase {
   1033 public:
   1034     ChromeClientCallbackTimer(ApplicationCacheGroup* cacheGroup)
   1035         : m_cacheGroup(cacheGroup)
   1036     {
   1037     }
   1038 
   1039 private:
   1040     virtual void fired()
   1041     {
   1042         m_cacheGroup->didReachMaxAppCacheSize();
   1043         delete this;
   1044     }
   1045     // Note that there is no need to use a RefPtr here. The ApplicationCacheGroup instance is guaranteed
   1046     // to be alive when the timer fires since invoking the ChromeClient callback is part of its normal
   1047     // update machinery and nothing can yet cause it to get deleted.
   1048     ApplicationCacheGroup* m_cacheGroup;
   1049 };
   1050 
   1051 void ApplicationCacheGroup::scheduleReachedMaxAppCacheSizeCallback()
   1052 {
   1053     ASSERT(isMainThread());
   1054     ChromeClientCallbackTimer* timer = new ChromeClientCallbackTimer(this);
   1055     timer->startOneShot(0);
   1056     // The timer will delete itself once it fires.
   1057 }
   1058 
   1059 void ApplicationCacheGroup::scheduleReachedOriginQuotaCallback()
   1060 {
   1061     // FIXME: it might be nice to run this asynchronously, because there is no return value to wait for.
   1062     didReachOriginQuota(m_frame);
   1063 }
   1064 
   1065 class CallCacheListenerTask : public ScriptExecutionContext::Task {
   1066 public:
   1067     static PassOwnPtr<CallCacheListenerTask> create(PassRefPtr<DocumentLoader> loader, ApplicationCacheHost::EventID eventID, int progressTotal, int progressDone)
   1068     {
   1069         return adoptPtr(new CallCacheListenerTask(loader, eventID, progressTotal, progressDone));
   1070     }
   1071 
   1072     virtual void performTask(ScriptExecutionContext* context)
   1073     {
   1074 
   1075         ASSERT_UNUSED(context, context->isDocument());
   1076         Frame* frame = m_documentLoader->frame();
   1077         if (!frame)
   1078             return;
   1079 
   1080         ASSERT(frame->loader()->documentLoader() == m_documentLoader.get());
   1081 
   1082         m_documentLoader->applicationCacheHost()->notifyDOMApplicationCache(m_eventID, m_progressTotal, m_progressDone);
   1083     }
   1084 
   1085 private:
   1086     CallCacheListenerTask(PassRefPtr<DocumentLoader> loader, ApplicationCacheHost::EventID eventID, int progressTotal, int progressDone)
   1087         : m_documentLoader(loader)
   1088         , m_eventID(eventID)
   1089         , m_progressTotal(progressTotal)
   1090         , m_progressDone(progressDone)
   1091     {
   1092     }
   1093 
   1094     RefPtr<DocumentLoader> m_documentLoader;
   1095     ApplicationCacheHost::EventID m_eventID;
   1096     int m_progressTotal;
   1097     int m_progressDone;
   1098 };
   1099 
   1100 void ApplicationCacheGroup::postListenerTask(ApplicationCacheHost::EventID eventID, int progressTotal, int progressDone, const HashSet<DocumentLoader*>& loaderSet)
   1101 {
   1102     HashSet<DocumentLoader*>::const_iterator loaderSetEnd = loaderSet.end();
   1103     for (HashSet<DocumentLoader*>::const_iterator iter = loaderSet.begin(); iter != loaderSetEnd; ++iter)
   1104         postListenerTask(eventID, progressTotal, progressDone, *iter);
   1105 }
   1106 
   1107 void ApplicationCacheGroup::postListenerTask(ApplicationCacheHost::EventID eventID, int progressTotal, int progressDone, DocumentLoader* loader)
   1108 {
   1109     Frame* frame = loader->frame();
   1110     if (!frame)
   1111         return;
   1112 
   1113     ASSERT(frame->loader()->documentLoader() == loader);
   1114 
   1115     frame->document()->postTask(CallCacheListenerTask::create(loader, eventID, progressTotal, progressDone));
   1116 }
   1117 
   1118 void ApplicationCacheGroup::setUpdateStatus(UpdateStatus status)
   1119 {
   1120     m_updateStatus = status;
   1121     InspectorInstrumentation::updateApplicationCacheStatus(m_frame);
   1122 }
   1123 
   1124 void ApplicationCacheGroup::clearStorageID()
   1125 {
   1126     m_storageID = 0;
   1127 
   1128     HashSet<ApplicationCache*>::const_iterator end = m_caches.end();
   1129     for (HashSet<ApplicationCache*>::const_iterator it = m_caches.begin(); it != end; ++it)
   1130         (*it)->clearStorageID();
   1131 }
   1132 
   1133 
   1134 }
   1135 
   1136 #endif // ENABLE(OFFLINE_WEB_APPLICATIONS)
   1137