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