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