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