2dfe34cac2bb12e88651d328458a5670310dd5e6
[WebKit-https.git] / Source / WebCore / loader / appcache / ApplicationCacheGroup.cpp
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 #include "ApplicationCache.h"
30 #include "ApplicationCacheHost.h"
31 #include "ApplicationCacheResource.h"
32 #include "ApplicationCacheStorage.h"
33 #include "Chrome.h"
34 #include "ChromeClient.h"
35 #include "Console.h"
36 #include "DOMApplicationCache.h"
37 #include "DOMWindow.h"
38 #include "DocumentLoader.h"
39 #include "Frame.h"
40 #include "FrameLoader.h"
41 #include "FrameLoaderClient.h"
42 #include "InspectorInstrumentation.h"
43 #include "MainResourceLoader.h"
44 #include "ManifestParser.h"
45 #include "Page.h"
46 #include "ScriptProfile.h"
47 #include "SecurityOrigin.h"
48 #include "Settings.h"
49 #include <wtf/HashMap.h>
50 #include <wtf/MainThread.h>
51 #include <wtf/UnusedParam.h>
52
53 #if ENABLE(INSPECTOR)
54 #include "ProgressTracker.h"
55 #endif
56
57 namespace WebCore {
58
59 ApplicationCacheGroup::ApplicationCacheGroup(const KURL& manifestURL, bool isCopy)
60     : m_manifestURL(manifestURL)
61     , m_origin(SecurityOrigin::create(manifestURL))
62     , m_updateStatus(Idle)
63     , m_downloadingPendingMasterResourceLoadersCount(0)
64     , m_progressTotal(0)
65     , m_progressDone(0)
66     , m_frame(0)
67     , m_storageID(0)
68     , m_isObsolete(false)
69     , m_completionType(None)
70     , m_isCopy(isCopy)
71     , m_calledReachedMaxAppCacheSize(false)
72     , m_availableSpaceInQuota(ApplicationCacheStorage::unknownQuota())
73     , m_originQuotaExceededPreviously(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() || !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());
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()->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() || !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     ASSERT(m_downloadingPendingMasterResourceLoadersCount > 0);
276     m_downloadingPendingMasterResourceLoadersCount--;
277     checkIfLoadIsComplete();
278 }
279
280 void ApplicationCacheGroup::failedLoadingMainResource(DocumentLoader* loader)
281 {
282     ASSERT(m_pendingMasterResourceLoaders.contains(loader));
283     ASSERT(m_completionType == None || m_pendingEntries.isEmpty());
284
285     switch (m_completionType) {
286     case None:
287         // The main resource finished loading before the manifest was ready. It will be handled via dispatchMainResources() later.
288         return;
289     case NoUpdate:
290         ASSERT(!m_cacheBeingUpdated);
291
292         // 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,
293         // 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.
294         postListenerTask(ApplicationCacheHost::ERROR_EVENT, loader);
295
296         break;
297     case Failure:
298         // Cache update failed, too.
299         ASSERT(!m_cacheBeingUpdated); // Already cleared out by stopLoading().
300         ASSERT(!loader->applicationCacheHost()->applicationCache() || loader->applicationCacheHost()->applicationCache() == m_cacheBeingUpdated);
301
302         loader->applicationCacheHost()->setApplicationCache(0); // Will unset candidate, too.
303         m_associatedDocumentLoaders.remove(loader);
304         postListenerTask(ApplicationCacheHost::ERROR_EVENT, loader);
305         break;
306     case Completed:
307         // The cache manifest didn't list this main resource, and all cache entries were already updated successfully - but the main resource failed to load,
308         // so it cannot be stored to the cache. If there are other main resources being downloaded for this cache group, they may still succeed.
309         ASSERT(m_associatedDocumentLoaders.contains(loader));
310         ASSERT(loader->applicationCacheHost()->applicationCache() == m_cacheBeingUpdated);
311         ASSERT(!loader->applicationCacheHost()->candidateApplicationCacheGroup());
312         m_associatedDocumentLoaders.remove(loader);
313         loader->applicationCacheHost()->setApplicationCache(0);
314
315         postListenerTask(ApplicationCacheHost::ERROR_EVENT, loader);
316
317         break;
318     }
319
320     ASSERT(m_downloadingPendingMasterResourceLoadersCount > 0);
321     m_downloadingPendingMasterResourceLoadersCount--;
322     checkIfLoadIsComplete();
323 }
324
325 void ApplicationCacheGroup::stopLoading()
326 {
327     if (m_manifestHandle) {
328         ASSERT(!m_currentHandle);
329
330         ASSERT(m_manifestHandle->client() == this);
331         m_manifestHandle->setClient(0);
332
333         m_manifestHandle->cancel();
334         m_manifestHandle = 0;
335     }
336     
337     if (m_currentHandle) {
338         ASSERT(!m_manifestHandle);
339         ASSERT(m_cacheBeingUpdated);
340
341         ASSERT(m_currentHandle->client() == this);
342         m_currentHandle->setClient(0);
343
344         m_currentHandle->cancel();
345         m_currentHandle = 0;
346     }    
347
348     // FIXME: Resetting just a tiny part of the state in this function is confusing. Callers have to take care of a lot more.
349     m_cacheBeingUpdated = 0;
350     m_pendingEntries.clear();
351 }    
352
353 void ApplicationCacheGroup::disassociateDocumentLoader(DocumentLoader* loader)
354 {
355     HashSet<DocumentLoader*>::iterator it = m_associatedDocumentLoaders.find(loader);
356     if (it != m_associatedDocumentLoaders.end())
357         m_associatedDocumentLoaders.remove(it);
358
359     m_pendingMasterResourceLoaders.remove(loader);
360
361     loader->applicationCacheHost()->setApplicationCache(0); // Will set candidate to 0, too.
362
363     if (!m_associatedDocumentLoaders.isEmpty() || !m_pendingMasterResourceLoaders.isEmpty())
364         return;
365
366     if (m_caches.isEmpty()) {
367         // There is an initial cache attempt in progress.
368         ASSERT(!m_newestCache);
369         // Delete ourselves, causing the cache attempt to be stopped.
370         delete this;
371         return;
372     }
373
374     ASSERT(m_caches.contains(m_newestCache.get()));
375
376     // Release our reference to the newest cache. This could cause us to be deleted.
377     // Any ongoing updates will be stopped from destructor.
378     m_newestCache.release();
379 }
380
381 void ApplicationCacheGroup::cacheDestroyed(ApplicationCache* cache)
382 {
383     if (!m_caches.contains(cache))
384         return;
385     
386     m_caches.remove(cache);
387
388     if (m_caches.isEmpty()) {
389         ASSERT(m_associatedDocumentLoaders.isEmpty());
390         ASSERT(m_pendingMasterResourceLoaders.isEmpty());
391         delete this;
392     }
393 }
394
395 void ApplicationCacheGroup::stopLoadingInFrame(Frame* frame)
396 {
397     if (frame != m_frame)
398         return;
399
400     cacheUpdateFailed();
401 }
402
403 void ApplicationCacheGroup::setNewestCache(PassRefPtr<ApplicationCache> newestCache)
404 {
405     m_newestCache = newestCache;
406
407     m_caches.add(m_newestCache.get());
408     m_newestCache->setGroup(this);
409 }
410
411 void ApplicationCacheGroup::makeObsolete()
412 {
413     if (isObsolete())
414         return;
415
416     m_isObsolete = true;
417     cacheStorage().cacheGroupMadeObsolete(this);
418     ASSERT(!m_storageID);
419 }
420
421 void ApplicationCacheGroup::update(Frame* frame, ApplicationCacheUpdateOption updateOption)
422 {
423     if (m_updateStatus == Checking || m_updateStatus == Downloading) {
424         if (updateOption == ApplicationCacheUpdateWithBrowsingContext) {
425             postListenerTask(ApplicationCacheHost::CHECKING_EVENT, frame->loader()->documentLoader());
426             if (m_updateStatus == Downloading)
427                 postListenerTask(ApplicationCacheHost::DOWNLOADING_EVENT, frame->loader()->documentLoader());
428         }
429         return;
430     }
431
432     // Don't change anything on disk if private browsing is enabled.
433     if (!frame->settings() || frame->settings()->privateBrowsingEnabled()) {
434         ASSERT(m_pendingMasterResourceLoaders.isEmpty());
435         ASSERT(m_pendingEntries.isEmpty());
436         ASSERT(!m_cacheBeingUpdated);
437         postListenerTask(ApplicationCacheHost::CHECKING_EVENT, frame->loader()->documentLoader());
438         postListenerTask(ApplicationCacheHost::NOUPDATE_EVENT, frame->loader()->documentLoader());
439         return;
440     }
441
442     ASSERT(!m_frame);
443     m_frame = frame;
444
445     setUpdateStatus(Checking);
446
447     postListenerTask(ApplicationCacheHost::CHECKING_EVENT, m_associatedDocumentLoaders);
448     if (!m_newestCache) {
449         ASSERT(updateOption == ApplicationCacheUpdateWithBrowsingContext);
450         postListenerTask(ApplicationCacheHost::CHECKING_EVENT, frame->loader()->documentLoader());
451     }
452     
453     ASSERT(!m_manifestHandle);
454     ASSERT(!m_manifestResource);
455     ASSERT(!m_currentHandle);
456     ASSERT(!m_currentResource);
457     ASSERT(m_completionType == None);
458
459     // FIXME: Handle defer loading
460     m_manifestHandle = createResourceHandle(m_manifestURL, m_newestCache ? m_newestCache->manifestResource() : 0);
461 }
462
463 void ApplicationCacheGroup::abort(Frame* frame)
464 {
465     if (m_updateStatus == Idle)
466         return;
467     ASSERT(m_updateStatus == Checking || (m_updateStatus == Downloading && m_cacheBeingUpdated));
468
469     if (m_completionType != None)
470         return;
471
472     frame->domWindow()->console()->addMessage(OtherMessageSource, LogMessageType, TipMessageLevel, "Application Cache download process was aborted.");
473     cacheUpdateFailed();
474 }
475
476 PassRefPtr<ResourceHandle> ApplicationCacheGroup::createResourceHandle(const KURL& url, ApplicationCacheResource* newestCachedResource)
477 {
478     ResourceRequest request(url);
479     m_frame->loader()->applyUserAgent(request);
480     request.setHTTPHeaderField("Cache-Control", "max-age=0");
481
482     if (newestCachedResource) {
483         const String& lastModified = newestCachedResource->response().httpHeaderField("Last-Modified");
484         const String& eTag = newestCachedResource->response().httpHeaderField("ETag");
485         if (!lastModified.isEmpty() || !eTag.isEmpty()) {
486             if (!lastModified.isEmpty())
487                 request.setHTTPHeaderField("If-Modified-Since", lastModified);
488             if (!eTag.isEmpty())
489                 request.setHTTPHeaderField("If-None-Match", eTag);
490         }
491     }
492
493 #if PLATFORM(BLACKBERRY)
494     ResourceRequest::TargetType target = ResourceRequest::TargetIsUnspecified;
495     if (newestCachedResource) {
496         const String& mimeType = newestCachedResource->response().mimeType();
497         if (!mimeType.isEmpty())
498             target = ResourceRequest::targetTypeFromMimeType(mimeType);
499     }
500     if (target == ResourceRequest::TargetIsUnspecified) {
501         String mimeType;
502         if (url.protocolIsData())
503             mimeType = mimeTypeFromDataURL(url);
504         if (!mimeType.isEmpty())
505             target = ResourceRequest::targetTypeFromMimeType(mimeType);
506     }
507
508     request.setTargetType(target);
509 #endif
510
511     RefPtr<ResourceHandle> handle = ResourceHandle::create(m_frame->loader()->networkingContext(), request, this, false, true);
512 #if ENABLE(INSPECTOR)
513     // Because willSendRequest only gets called during redirects, we initialize
514     // the identifier and the first willSendRequest here.
515     m_currentResourceIdentifier = m_frame->page()->progress()->createUniqueIdentifier();
516     ResourceResponse redirectResponse = ResourceResponse();
517     InspectorInstrumentation::willSendRequest(m_frame, m_currentResourceIdentifier, m_frame->loader()->documentLoader(), request, redirectResponse);
518 #endif
519     return handle;
520 }
521
522 void ApplicationCacheGroup::didReceiveResponse(ResourceHandle* handle, const ResourceResponse& response)
523 {
524 #if ENABLE(INSPECTOR)
525     DocumentLoader* loader = (handle == m_manifestHandle) ? 0 : m_frame->loader()->documentLoader();
526     InspectorInstrumentationCookie cookie = InspectorInstrumentation::willReceiveResourceResponse(m_frame, m_currentResourceIdentifier, response);
527     InspectorInstrumentation::didReceiveResourceResponse(cookie, m_currentResourceIdentifier, loader, response);
528 #endif
529
530     if (handle == m_manifestHandle) {
531         didReceiveManifestResponse(response);
532         return;
533     }
534     
535     ASSERT(handle == m_currentHandle);
536
537     KURL url(handle->firstRequest().url());
538     if (url.hasFragmentIdentifier())
539         url.removeFragmentIdentifier();
540     
541     ASSERT(!m_currentResource);
542     ASSERT(m_pendingEntries.contains(url));
543     
544     unsigned type = m_pendingEntries.get(url);
545     
546     // If this is an initial cache attempt, we should not get master resources delivered here.
547     if (!m_newestCache)
548         ASSERT(!(type & ApplicationCacheResource::Master));
549
550     if (m_newestCache && response.httpStatusCode() == 304) { // Not modified.
551         ApplicationCacheResource* newestCachedResource = m_newestCache->resourceForURL(url);
552         if (newestCachedResource) {
553             m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, newestCachedResource->response(), type, newestCachedResource->data(), newestCachedResource->path()));
554             m_pendingEntries.remove(m_currentHandle->firstRequest().url());
555             m_currentHandle->cancel();
556             m_currentHandle = 0;
557             // Load the next resource, if any.
558             startLoadingEntry();
559             return;
560         }
561         // The server could return 304 for an unconditional request - in this case, we handle the response as a normal error.
562     }
563
564     if (response.httpStatusCode() / 100 != 2 || response.url() != m_currentHandle->firstRequest().url()) {
565         if ((type & ApplicationCacheResource::Explicit) || (type & ApplicationCacheResource::Fallback)) {
566             m_frame->domWindow()->console()->addMessage(OtherMessageSource, LogMessageType, ErrorMessageLevel, "Application Cache update failed, because " + m_currentHandle->firstRequest().url().string() + 
567                 ((response.httpStatusCode() / 100 != 2) ? " could not be fetched." : " was redirected."));
568             // Note that cacheUpdateFailed() can cause the cache group to be deleted.
569             cacheUpdateFailed();
570         } else if (response.httpStatusCode() == 404 || response.httpStatusCode() == 410) {
571             // Skip this resource. It is dropped from the cache.
572             m_currentHandle->cancel();
573             m_currentHandle = 0;
574             m_pendingEntries.remove(url);
575             // Load the next resource, if any.
576             startLoadingEntry();
577         } else {
578             // Copy the resource and its metadata from the newest application cache in cache group whose completeness flag is complete, and act
579             // as if that was the fetched resource, ignoring the resource obtained from the network.
580             ASSERT(m_newestCache);
581             ApplicationCacheResource* newestCachedResource = m_newestCache->resourceForURL(handle->firstRequest().url());
582             ASSERT(newestCachedResource);
583             m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, newestCachedResource->response(), type, newestCachedResource->data(), newestCachedResource->path()));
584             m_pendingEntries.remove(m_currentHandle->firstRequest().url());
585             m_currentHandle->cancel();
586             m_currentHandle = 0;
587             // Load the next resource, if any.
588             startLoadingEntry();
589         }
590         return;
591     }
592     
593     m_currentResource = ApplicationCacheResource::create(url, response, type);
594 }
595
596 void ApplicationCacheGroup::didReceiveData(ResourceHandle* handle, const char* data, int length, int encodedDataLength)
597 {
598     UNUSED_PARAM(encodedDataLength);
599
600 #if ENABLE(INSPECTOR)
601     InspectorInstrumentation::didReceiveData(m_frame, m_currentResourceIdentifier, 0, length, 0);
602 #endif
603
604     if (handle == m_manifestHandle) {
605         didReceiveManifestData(data, length);
606         return;
607     }
608     
609     ASSERT(handle == m_currentHandle);
610     
611     ASSERT(m_currentResource);
612     m_currentResource->data()->append(data, length);
613 }
614
615 void ApplicationCacheGroup::didFinishLoading(ResourceHandle* handle, double finishTime)
616 {
617 #if ENABLE(INSPECTOR)
618     InspectorInstrumentation::didFinishLoading(m_frame, m_frame->loader()->documentLoader(), m_currentResourceIdentifier, finishTime);
619 #endif
620
621     if (handle == m_manifestHandle) {
622         didFinishLoadingManifest();
623         return;
624     }
625
626     ASSERT(m_currentHandle == handle);
627     ASSERT(m_pendingEntries.contains(handle->firstRequest().url()));
628     
629     m_pendingEntries.remove(handle->firstRequest().url());
630     
631     ASSERT(m_cacheBeingUpdated);
632
633     m_cacheBeingUpdated->addResource(m_currentResource.release());
634     m_currentHandle = 0;
635
636     // While downloading check to see if we have exceeded the available quota.
637     // We can stop immediately if we have already previously failed
638     // due to an earlier quota restriction. The client was already notified
639     // of the quota being reached and decided not to increase it then.
640     // FIXME: Should we break earlier and prevent redownloading on later page loads?
641     if (m_originQuotaExceededPreviously && m_availableSpaceInQuota < m_cacheBeingUpdated->estimatedSizeInStorage()) {
642         m_currentResource = 0;
643         m_frame->domWindow()->console()->addMessage(OtherMessageSource, LogMessageType, ErrorMessageLevel, "Application Cache update failed, because size quota was exceeded.");
644         cacheUpdateFailed();
645         return;
646     }
647     
648     // Load the next resource, if any.
649     startLoadingEntry();
650 }
651
652 void ApplicationCacheGroup::didFail(ResourceHandle* handle, const ResourceError& error)
653 {
654 #if ENABLE(INSPECTOR)
655     InspectorInstrumentation::didFailLoading(m_frame, m_frame->loader()->documentLoader(), m_currentResourceIdentifier, error);
656 #endif
657
658     if (handle == m_manifestHandle) {
659         // A network error is logged elsewhere, no need to log again. Also, it's normal for manifest fetching to fail when working offline.
660         cacheUpdateFailed();
661         return;
662     }
663
664     ASSERT(handle == m_currentHandle);
665
666     unsigned type = m_currentResource ? m_currentResource->type() : m_pendingEntries.get(handle->firstRequest().url());
667     KURL url(handle->firstRequest().url());
668     if (url.hasFragmentIdentifier())
669         url.removeFragmentIdentifier();
670
671     ASSERT(!m_currentResource || !m_pendingEntries.contains(url));
672     m_currentResource = 0;
673     m_pendingEntries.remove(url);
674
675     if ((type & ApplicationCacheResource::Explicit) || (type & ApplicationCacheResource::Fallback)) {
676         m_frame->domWindow()->console()->addMessage(OtherMessageSource, LogMessageType, ErrorMessageLevel, "Application Cache update failed, because " + url.string() + " could not be fetched.");
677         // Note that cacheUpdateFailed() can cause the cache group to be deleted.
678         cacheUpdateFailed();
679     } else {
680         // Copy the resource and its metadata from the newest application cache in cache group whose completeness flag is complete, and act
681         // as if that was the fetched resource, ignoring the resource obtained from the network.
682         ASSERT(m_newestCache);
683         ApplicationCacheResource* newestCachedResource = m_newestCache->resourceForURL(url);
684         ASSERT(newestCachedResource);
685         m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, newestCachedResource->response(), type, newestCachedResource->data(), newestCachedResource->path()));
686         // Load the next resource, if any.
687         startLoadingEntry();
688     }
689 }
690
691 void ApplicationCacheGroup::didReceiveManifestResponse(const ResourceResponse& response)
692 {
693     ASSERT(!m_manifestResource);
694     ASSERT(m_manifestHandle);
695
696     if (response.httpStatusCode() == 404 || response.httpStatusCode() == 410) {
697         manifestNotFound();
698         return;
699     }
700
701     if (response.httpStatusCode() == 304)
702         return;
703
704     if (response.httpStatusCode() / 100 != 2) {
705         m_frame->domWindow()->console()->addMessage(OtherMessageSource, LogMessageType, ErrorMessageLevel, "Application Cache manifest could not be fetched.");
706         cacheUpdateFailed();
707         return;
708     }
709
710     if (response.url() != m_manifestHandle->firstRequest().url()) {
711         m_frame->domWindow()->console()->addMessage(OtherMessageSource, LogMessageType, ErrorMessageLevel, "Application Cache manifest could not be fetched, because a redirection was attempted.");
712         cacheUpdateFailed();
713         return;
714     }
715
716     m_manifestResource = ApplicationCacheResource::create(m_manifestHandle->firstRequest().url(), response, ApplicationCacheResource::Manifest);
717 }
718
719 void ApplicationCacheGroup::didReceiveManifestData(const char* data, int length)
720 {
721     if (m_manifestResource)
722         m_manifestResource->data()->append(data, length);
723 }
724
725 void ApplicationCacheGroup::didFinishLoadingManifest()
726 {
727     bool isUpgradeAttempt = m_newestCache;
728
729     if (!isUpgradeAttempt && !m_manifestResource) {
730         // The server returned 304 Not Modified even though we didn't send a conditional request.
731         m_frame->domWindow()->console()->addMessage(OtherMessageSource, LogMessageType, ErrorMessageLevel, "Application Cache manifest could not be fetched because of an unexpected 304 Not Modified server response.");
732         cacheUpdateFailed();
733         return;
734     }
735
736     m_manifestHandle = 0;
737
738     // Check if the manifest was not modified.
739     if (isUpgradeAttempt) {
740         ApplicationCacheResource* newestManifest = m_newestCache->manifestResource();
741         ASSERT(newestManifest);
742     
743         if (!m_manifestResource || // The resource will be null if HTTP response was 304 Not Modified.
744             (newestManifest->data()->size() == m_manifestResource->data()->size() && !memcmp(newestManifest->data()->data(), m_manifestResource->data()->data(), newestManifest->data()->size()))) {
745
746             m_completionType = NoUpdate;
747             m_manifestResource = 0;
748             deliverDelayedMainResources();
749
750             return;
751         }
752     }
753     
754     Manifest manifest;
755     if (!parseManifest(m_manifestURL, m_manifestResource->data()->data(), m_manifestResource->data()->size(), manifest)) {
756         // At the time of this writing, lack of "CACHE MANIFEST" signature is the only reason for parseManifest to fail.
757         m_frame->domWindow()->console()->addMessage(OtherMessageSource, LogMessageType, ErrorMessageLevel, "Application Cache manifest could not be parsed. Does it start with CACHE MANIFEST?");
758         cacheUpdateFailed();
759         return;
760     }
761
762     ASSERT(!m_cacheBeingUpdated);
763     m_cacheBeingUpdated = ApplicationCache::create();
764     m_cacheBeingUpdated->setGroup(this);
765
766     HashSet<DocumentLoader*>::const_iterator masterEnd = m_pendingMasterResourceLoaders.end();
767     for (HashSet<DocumentLoader*>::const_iterator iter = m_pendingMasterResourceLoaders.begin(); iter != masterEnd; ++iter)
768         associateDocumentLoaderWithCache(*iter, m_cacheBeingUpdated.get());
769
770     // We have the manifest, now download the resources.
771     setUpdateStatus(Downloading);
772     
773     postListenerTask(ApplicationCacheHost::DOWNLOADING_EVENT, m_associatedDocumentLoaders);
774
775     ASSERT(m_pendingEntries.isEmpty());
776
777     if (isUpgradeAttempt) {
778         ApplicationCache::ResourceMap::const_iterator end = m_newestCache->end();
779         for (ApplicationCache::ResourceMap::const_iterator it = m_newestCache->begin(); it != end; ++it) {
780             unsigned type = it->second->type();
781             if (type & ApplicationCacheResource::Master)
782                 addEntry(it->first, type);
783         }
784     }
785     
786     HashSet<String>::const_iterator end = manifest.explicitURLs.end();
787     for (HashSet<String>::const_iterator it = manifest.explicitURLs.begin(); it != end; ++it)
788         addEntry(*it, ApplicationCacheResource::Explicit);
789
790     size_t fallbackCount = manifest.fallbackURLs.size();
791     for (size_t i = 0; i  < fallbackCount; ++i)
792         addEntry(manifest.fallbackURLs[i].second, ApplicationCacheResource::Fallback);
793     
794     m_cacheBeingUpdated->setOnlineWhitelist(manifest.onlineWhitelistedURLs);
795     m_cacheBeingUpdated->setFallbackURLs(manifest.fallbackURLs);
796     m_cacheBeingUpdated->setAllowsAllNetworkRequests(manifest.allowAllNetworkRequests);
797
798     m_progressTotal = m_pendingEntries.size();
799     m_progressDone = 0;
800
801     recalculateAvailableSpaceInQuota();
802
803     startLoadingEntry();
804 }
805
806 void ApplicationCacheGroup::didReachMaxAppCacheSize()
807 {
808     ASSERT(m_frame);
809     ASSERT(m_cacheBeingUpdated);
810     m_frame->page()->chrome()->client()->reachedMaxAppCacheSize(cacheStorage().spaceNeeded(m_cacheBeingUpdated->estimatedSizeInStorage()));
811     m_calledReachedMaxAppCacheSize = true;
812     checkIfLoadIsComplete();
813 }
814
815 void ApplicationCacheGroup::didReachOriginQuota(int64_t totalSpaceNeeded)
816 {
817     // Inform the client the origin quota has been reached, they may decide to increase the quota.
818     // We expect quota to be increased synchronously while waiting for the call to return.
819     m_frame->page()->chrome()->client()->reachedApplicationCacheOriginQuota(m_origin.get(), totalSpaceNeeded);
820 }
821
822 void ApplicationCacheGroup::cacheUpdateFailed()
823 {
824     stopLoading();
825     m_manifestResource = 0;
826
827     // Wait for master resource loads to finish.
828     m_completionType = Failure;
829     deliverDelayedMainResources();
830 }
831
832 void ApplicationCacheGroup::recalculateAvailableSpaceInQuota()
833 {
834     if (!cacheStorage().calculateRemainingSizeForOriginExcludingCache(m_origin.get(), m_newestCache.get(), m_availableSpaceInQuota)) {
835         // Failed to determine what is left in the quota. Fallback to allowing anything.
836         m_availableSpaceInQuota = ApplicationCacheStorage::noQuota();
837     }
838 }
839     
840 void ApplicationCacheGroup::manifestNotFound()
841 {
842     makeObsolete();
843
844     postListenerTask(ApplicationCacheHost::OBSOLETE_EVENT, m_associatedDocumentLoaders);
845     postListenerTask(ApplicationCacheHost::ERROR_EVENT, m_pendingMasterResourceLoaders);
846
847     stopLoading();
848
849     ASSERT(m_pendingEntries.isEmpty());
850     m_manifestResource = 0;
851
852     while (!m_pendingMasterResourceLoaders.isEmpty()) {
853         HashSet<DocumentLoader*>::iterator it = m_pendingMasterResourceLoaders.begin();
854         
855         ASSERT((*it)->applicationCacheHost()->candidateApplicationCacheGroup() == this);
856         ASSERT(!(*it)->applicationCacheHost()->applicationCache());
857         (*it)->applicationCacheHost()->setCandidateApplicationCacheGroup(0);
858         m_pendingMasterResourceLoaders.remove(it);
859     }
860
861     m_downloadingPendingMasterResourceLoadersCount = 0;
862     setUpdateStatus(Idle);    
863     m_frame = 0;
864     
865     if (m_caches.isEmpty()) {
866         ASSERT(m_associatedDocumentLoaders.isEmpty());
867         ASSERT(!m_cacheBeingUpdated);
868         delete this;
869     }
870 }
871
872 void ApplicationCacheGroup::checkIfLoadIsComplete()
873 {
874     if (m_manifestHandle || !m_pendingEntries.isEmpty() || m_downloadingPendingMasterResourceLoadersCount)
875         return;
876     
877     // We're done, all resources have finished downloading (successfully or not).
878
879     bool isUpgradeAttempt = m_newestCache;
880
881     switch (m_completionType) {
882     case None:
883         ASSERT_NOT_REACHED();
884         return;
885     case NoUpdate:
886         ASSERT(isUpgradeAttempt);
887         ASSERT(!m_cacheBeingUpdated);
888
889         // The storage could have been manually emptied by the user.
890         if (!m_storageID)
891             cacheStorage().storeNewestCache(this);
892
893         postListenerTask(ApplicationCacheHost::NOUPDATE_EVENT, m_associatedDocumentLoaders);
894         break;
895     case Failure:
896         ASSERT(!m_cacheBeingUpdated);
897         postListenerTask(ApplicationCacheHost::ERROR_EVENT, m_associatedDocumentLoaders);
898         if (m_caches.isEmpty()) {
899             ASSERT(m_associatedDocumentLoaders.isEmpty());
900             delete this;
901             return;
902         }
903         break;
904     case Completed: {
905         // 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>)
906
907         ASSERT(m_cacheBeingUpdated);
908         if (m_manifestResource)
909             m_cacheBeingUpdated->setManifestResource(m_manifestResource.release());
910         else {
911             // We can get here as a result of retrying the Complete step, following
912             // a failure of the cache storage to save the newest cache due to hitting
913             // the maximum size. In such a case, m_manifestResource may be 0, as
914             // the manifest was already set on the newest cache object.
915             ASSERT(cacheStorage().isMaximumSizeReached() && m_calledReachedMaxAppCacheSize);
916         }
917
918         RefPtr<ApplicationCache> oldNewestCache = (m_newestCache == m_cacheBeingUpdated) ? RefPtr<ApplicationCache>() : m_newestCache;
919
920         // If we exceeded the origin quota while downloading we can request a quota
921         // increase now, before we attempt to store the cache.
922         int64_t totalSpaceNeeded;
923         if (!cacheStorage().checkOriginQuota(this, oldNewestCache.get(), m_cacheBeingUpdated.get(), totalSpaceNeeded))
924             didReachOriginQuota(totalSpaceNeeded);
925
926         ApplicationCacheStorage::FailureReason failureReason;
927         setNewestCache(m_cacheBeingUpdated.release());
928         if (cacheStorage().storeNewestCache(this, oldNewestCache.get(), failureReason)) {
929             // New cache stored, now remove the old cache.
930             if (oldNewestCache)
931                 cacheStorage().remove(oldNewestCache.get());
932
933             // Fire the final progress event.
934             ASSERT(m_progressDone == m_progressTotal);
935             postListenerTask(ApplicationCacheHost::PROGRESS_EVENT, m_progressTotal, m_progressDone, m_associatedDocumentLoaders);
936
937             // Fire the success event.
938             postListenerTask(isUpgradeAttempt ? ApplicationCacheHost::UPDATEREADY_EVENT : ApplicationCacheHost::CACHED_EVENT, m_associatedDocumentLoaders);
939             // It is clear that the origin quota was not reached, so clear the flag if it was set.
940             m_originQuotaExceededPreviously = false;
941         } else {
942             if (failureReason == ApplicationCacheStorage::OriginQuotaReached) {
943                 // We ran out of space for this origin. Fall down to the normal error handling
944                 // after recording this state.
945                 m_originQuotaExceededPreviously = true;
946                 m_frame->domWindow()->console()->addMessage(OtherMessageSource, LogMessageType, ErrorMessageLevel, "Application Cache update failed, because size quota was exceeded.");
947             }
948
949             if (failureReason == ApplicationCacheStorage::TotalQuotaReached && !m_calledReachedMaxAppCacheSize) {
950                 // FIXME: Should this be handled more like Origin Quotas? Does this fail properly?
951
952                 // We ran out of space. All the changes in the cache storage have
953                 // been rolled back. We roll back to the previous state in here,
954                 // as well, call the chrome client asynchronously and retry to
955                 // save the new cache.
956
957                 // Save a reference to the new cache.
958                 m_cacheBeingUpdated = m_newestCache.release();
959                 if (oldNewestCache) {
960                     // Reinstate the oldNewestCache.
961                     setNewestCache(oldNewestCache.release());
962                 }
963                 scheduleReachedMaxAppCacheSizeCallback();
964                 return;
965             }
966
967             // Run the "cache failure steps"
968             // Fire the error events to all pending master entries, as well any other cache hosts
969             // currently associated with a cache in this group.
970             postListenerTask(ApplicationCacheHost::ERROR_EVENT, m_associatedDocumentLoaders);
971             // Disassociate the pending master entries from the failed new cache. Note that
972             // all other loaders in the m_associatedDocumentLoaders are still associated with
973             // some other cache in this group. They are not associated with the failed new cache.
974
975             // Need to copy loaders, because the cache group may be destroyed at the end of iteration.
976             Vector<DocumentLoader*> loaders;
977             copyToVector(m_pendingMasterResourceLoaders, loaders);
978             size_t count = loaders.size();
979             for (size_t i = 0; i != count; ++i)
980                 disassociateDocumentLoader(loaders[i]); // This can delete this group.
981
982             // Reinstate the oldNewestCache, if there was one.
983             if (oldNewestCache) {
984                 // This will discard the failed new cache.
985                 setNewestCache(oldNewestCache.release());
986             } else {
987                 // We must have been deleted by the last call to disassociateDocumentLoader().
988                 return;
989             }
990         }
991         break;
992     }
993     }
994
995     // Empty cache group's list of pending master entries.
996     m_pendingMasterResourceLoaders.clear();
997     m_completionType = None;
998     setUpdateStatus(Idle);
999     m_frame = 0;
1000     m_availableSpaceInQuota = ApplicationCacheStorage::unknownQuota();
1001     m_calledReachedMaxAppCacheSize = false;
1002 }
1003
1004 void ApplicationCacheGroup::startLoadingEntry()
1005 {
1006     ASSERT(m_cacheBeingUpdated);
1007
1008     if (m_pendingEntries.isEmpty()) {
1009         m_completionType = Completed;
1010         deliverDelayedMainResources();
1011         return;
1012     }
1013     
1014     EntryMap::const_iterator it = m_pendingEntries.begin();
1015
1016     postListenerTask(ApplicationCacheHost::PROGRESS_EVENT, m_progressTotal, m_progressDone, m_associatedDocumentLoaders);
1017     m_progressDone++;
1018
1019     ASSERT(!m_currentHandle);
1020     
1021     m_currentHandle = createResourceHandle(KURL(ParsedURLString, it->first), m_newestCache ? m_newestCache->resourceForURL(it->first) : 0);
1022 }
1023
1024 void ApplicationCacheGroup::deliverDelayedMainResources()
1025 {
1026     // Need to copy loaders, because the cache group may be destroyed at the end of iteration.
1027     Vector<DocumentLoader*> loaders;
1028     copyToVector(m_pendingMasterResourceLoaders, loaders);
1029     size_t count = loaders.size();
1030     for (size_t i = 0; i != count; ++i) {
1031         DocumentLoader* loader = loaders[i];
1032         if (loader->isLoadingMainResource())
1033             continue;
1034
1035         const ResourceError& error = loader->mainDocumentError();
1036         if (error.isNull())
1037             finishedLoadingMainResource(loader);
1038         else
1039             failedLoadingMainResource(loader);
1040     }
1041     if (!count)
1042         checkIfLoadIsComplete();
1043 }
1044
1045 void ApplicationCacheGroup::addEntry(const String& url, unsigned type)
1046 {
1047     ASSERT(m_cacheBeingUpdated);
1048     ASSERT(!KURL(ParsedURLString, url).hasFragmentIdentifier());
1049     
1050     // Don't add the URL if we already have an master resource in the cache
1051     // (i.e., the main resource finished loading before the manifest).
1052     if (ApplicationCacheResource* resource = m_cacheBeingUpdated->resourceForURL(url)) {
1053         ASSERT(resource->type() & ApplicationCacheResource::Master);
1054         ASSERT(!m_frame->loader()->documentLoader()->isLoadingMainResource());
1055     
1056         resource->addType(type);
1057         return;
1058     }
1059
1060     // Don't add the URL if it's the same as the manifest URL.
1061     ASSERT(m_manifestResource);
1062     if (m_manifestResource->url() == url) {
1063         m_manifestResource->addType(type);
1064         return;
1065     }
1066     
1067     EntryMap::AddResult result = m_pendingEntries.add(url, type);
1068     
1069     if (!result.isNewEntry)
1070         result.iterator->second |= type;
1071 }
1072
1073 void ApplicationCacheGroup::associateDocumentLoaderWithCache(DocumentLoader* loader, ApplicationCache* cache)
1074 {
1075     // If teardown started already, revive the group.
1076     if (!m_newestCache && !m_cacheBeingUpdated)
1077         m_newestCache = cache;
1078
1079     ASSERT(!m_isObsolete);
1080
1081     loader->applicationCacheHost()->setApplicationCache(cache);
1082
1083     ASSERT(!m_associatedDocumentLoaders.contains(loader));
1084     m_associatedDocumentLoaders.add(loader);
1085 }
1086
1087 class ChromeClientCallbackTimer: public TimerBase {
1088 public:
1089     ChromeClientCallbackTimer(ApplicationCacheGroup* cacheGroup)
1090         : m_cacheGroup(cacheGroup)
1091     {
1092     }
1093
1094 private:
1095     virtual void fired()
1096     {
1097         m_cacheGroup->didReachMaxAppCacheSize();
1098         delete this;
1099     }
1100     // Note that there is no need to use a RefPtr here. The ApplicationCacheGroup instance is guaranteed
1101     // to be alive when the timer fires since invoking the ChromeClient callback is part of its normal
1102     // update machinery and nothing can yet cause it to get deleted.
1103     ApplicationCacheGroup* m_cacheGroup;
1104 };
1105
1106 void ApplicationCacheGroup::scheduleReachedMaxAppCacheSizeCallback()
1107 {
1108     ASSERT(isMainThread());
1109     ChromeClientCallbackTimer* timer = new ChromeClientCallbackTimer(this);
1110     timer->startOneShot(0);
1111     // The timer will delete itself once it fires.
1112 }
1113
1114 class CallCacheListenerTask : public ScriptExecutionContext::Task {
1115 public:
1116     static PassOwnPtr<CallCacheListenerTask> create(PassRefPtr<DocumentLoader> loader, ApplicationCacheHost::EventID eventID, int progressTotal, int progressDone)
1117     {
1118         return adoptPtr(new CallCacheListenerTask(loader, eventID, progressTotal, progressDone));
1119     }
1120
1121     virtual void performTask(ScriptExecutionContext* context)
1122     {
1123         
1124         ASSERT_UNUSED(context, context->isDocument());
1125         Frame* frame = m_documentLoader->frame();
1126         if (!frame)
1127             return;
1128     
1129         ASSERT(frame->loader()->documentLoader() == m_documentLoader.get());
1130
1131         m_documentLoader->applicationCacheHost()->notifyDOMApplicationCache(m_eventID, m_progressTotal, m_progressDone);
1132     }
1133
1134 private:
1135     CallCacheListenerTask(PassRefPtr<DocumentLoader> loader, ApplicationCacheHost::EventID eventID, int progressTotal, int progressDone)
1136         : m_documentLoader(loader)
1137         , m_eventID(eventID)
1138         , m_progressTotal(progressTotal)
1139         , m_progressDone(progressDone)
1140     {
1141     }
1142
1143     RefPtr<DocumentLoader> m_documentLoader;
1144     ApplicationCacheHost::EventID m_eventID;
1145     int m_progressTotal;
1146     int m_progressDone;
1147 };
1148
1149 void ApplicationCacheGroup::postListenerTask(ApplicationCacheHost::EventID eventID, int progressTotal, int progressDone, const HashSet<DocumentLoader*>& loaderSet)
1150 {
1151     HashSet<DocumentLoader*>::const_iterator loaderSetEnd = loaderSet.end();
1152     for (HashSet<DocumentLoader*>::const_iterator iter = loaderSet.begin(); iter != loaderSetEnd; ++iter)
1153         postListenerTask(eventID, progressTotal, progressDone, *iter);
1154 }
1155
1156 void ApplicationCacheGroup::postListenerTask(ApplicationCacheHost::EventID eventID, int progressTotal, int progressDone, DocumentLoader* loader)
1157 {
1158     Frame* frame = loader->frame();
1159     if (!frame)
1160         return;
1161     
1162     ASSERT(frame->loader()->documentLoader() == loader);
1163
1164     frame->document()->postTask(CallCacheListenerTask::create(loader, eventID, progressTotal, progressDone));
1165 }
1166
1167 void ApplicationCacheGroup::setUpdateStatus(UpdateStatus status)
1168 {
1169     m_updateStatus = status;
1170 }
1171
1172 void ApplicationCacheGroup::clearStorageID()
1173 {
1174     m_storageID = 0;
1175     
1176     HashSet<ApplicationCache*>::const_iterator end = m_caches.end();
1177     for (HashSet<ApplicationCache*>::const_iterator it = m_caches.begin(); it != end; ++it)
1178         (*it)->clearStorageID();
1179 }
1180
1181 }