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