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