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