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