Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(55)

Side by Side Diff: webkit/appcache/appcache_update_job.cc

Issue 201070: Implementation of application cache update algorithm.... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « webkit/appcache/appcache_update_job.h ('k') | webkit/appcache/appcache_update_job_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "webkit/appcache/appcache_update_job.h"
6
7 #include "base/compiler_specific.h"
8 #include "base/message_loop.h"
9 #include "net/base/io_buffer.h"
10 #include "net/base/load_flags.h"
11 #include "webkit/appcache/appcache_group.h"
12 #include "webkit/appcache/appcache_host.h"
13
14 namespace appcache {
15
16 static const int kBufferSize = 4096;
17 static const size_t kMaxConcurrentUrlFetches = 2;
18
19 // Extra info associated with requests for use during response processing.
20 // This info is deleted when the URLRequest is deleted.
21 struct UpdateJobInfo : public URLRequest::UserData {
22 enum RequestType {
23 MANIFEST_FETCH,
24 URL_FETCH,
25 MANIFEST_REFETCH,
26 };
27
28 explicit UpdateJobInfo(RequestType request_type)
29 : type(request_type),
30 buffer(new net::IOBuffer(kBufferSize)) {
31 }
32
33 RequestType type;
34 scoped_refptr<net::IOBuffer> buffer;
35 // TODO(jennb): need storage info to stream response data to storage
36 };
37
38 // Helper class for collecting hosts per frontend when sending notifications
39 // so that only one notification is sent for all hosts using the same frontend.
40 class HostNotifier {
41 public:
42 typedef std::vector<int> HostIds;
43 typedef std::map<AppCacheFrontend*, HostIds> NotifyHostMap;
44
45 // Caller is responsible for ensuring there will be no duplicate hosts.
46 void AddHost(AppCacheHost* host) {
47 std::pair<NotifyHostMap::iterator , bool> ret = hosts_to_notify.insert(
48 NotifyHostMap::value_type(host->frontend(), HostIds()));
49 ret.first->second.push_back(host->host_id());
50 }
51
52 void AddHosts(const std::set<AppCacheHost*>& hosts) {
53 for (std::set<AppCacheHost*>::const_iterator it = hosts.begin();
54 it != hosts.end(); ++it) {
55 AddHost(*it);
56 }
57 }
58
59 void SendNotifications(EventID event_id) {
60 for (NotifyHostMap::iterator it = hosts_to_notify.begin();
61 it != hosts_to_notify.end(); ++it) {
62 AppCacheFrontend* frontend = it->first;
63 frontend->OnEventRaised(it->second, event_id);
64 }
65 }
66
67 private:
68 NotifyHostMap hosts_to_notify;
69 };
70
71 AppCacheUpdateJob::AppCacheUpdateJob(AppCacheService* service,
72 AppCacheGroup* group)
73 : ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)),
74 service_(service),
75 group_(group),
76 update_type_(UNKNOWN_TYPE),
77 internal_state_(FETCH_MANIFEST),
78 master_entries_completed_(0),
79 url_fetches_completed_(0),
80 manifest_url_request_(NULL) {
81 DCHECK(group_);
82 manifest_url_ = group_->manifest_url();
83 }
84
85 AppCacheUpdateJob::~AppCacheUpdateJob() {
86 Cancel();
87
88 DCHECK(!manifest_url_request_);
89 DCHECK(pending_url_fetches_.empty());
90
91 group_->SetUpdateStatus(AppCacheGroup::IDLE);
92 }
93
94 void AppCacheUpdateJob::StartUpdate(AppCacheHost* host,
95 const GURL& new_master_resource) {
96 DCHECK(group_->update_job() == this);
97
98 if (!new_master_resource.is_empty()) {
99 /* TODO(jennb): uncomment when processing master entries is implemented
100 std::pair<PendingMasters::iterator, bool> ret =
101 pending_master_entries_.insert(
102 PendingMasters::value_type(new_master_resource, PendingHosts()));
103 ret.first->second.push_back(host);
104 */
105 }
106
107 // Notify host (if any) if already checking or downloading.
108 appcache::AppCacheGroup::UpdateStatus update_status = group_->update_status();
109 if (update_status == AppCacheGroup::CHECKING ||
110 update_status == AppCacheGroup::DOWNLOADING) {
111 if (host) {
112 NotifySingleHost(host, CHECKING_EVENT);
113 if (update_status == AppCacheGroup::DOWNLOADING)
114 NotifySingleHost(host, DOWNLOADING_EVENT);
115 }
116 return;
117 }
118
119 // Begin update process for the group.
120 group_->SetUpdateStatus(AppCacheGroup::CHECKING);
121 if (group_->HasCache()) {
122 update_type_ = UPGRADE_ATTEMPT;
123 NotifyAllAssociatedHosts(CHECKING_EVENT);
124 } else {
125 update_type_ = CACHE_ATTEMPT;
126 DCHECK(host);
127 NotifySingleHost(host, CHECKING_EVENT);
128 }
129
130 FetchManifest(true);
131 }
132
133 void AppCacheUpdateJob::FetchManifest(bool is_first_fetch) {
134 DCHECK(!manifest_url_request_);
135 manifest_url_request_ = new URLRequest(manifest_url_, this);
136 UpdateJobInfo::RequestType fetch_type = is_first_fetch ?
137 UpdateJobInfo::MANIFEST_FETCH : UpdateJobInfo::MANIFEST_REFETCH;
138 manifest_url_request_->SetUserData(this, new UpdateJobInfo(fetch_type));
139 manifest_url_request_->set_context(service_->request_context());
140 // TODO(jennb): add "If-Modified-Since" if have previous date
141 manifest_url_request_->set_load_flags(
142 manifest_url_request_->load_flags() | net::LOAD_DISABLE_INTERCEPT);
143 manifest_url_request_->Start();
144 }
145
146 void AppCacheUpdateJob::OnResponseStarted(URLRequest *request) {
147 if (request->status().is_success())
148 ReadResponseData(request);
149 else
150 OnResponseCompleted(request);
151 }
152
153 void AppCacheUpdateJob::ReadResponseData(URLRequest* request) {
154 if (internal_state_ == CACHE_FAILURE)
155 return;
156
157 int bytes_read = 0;
158 UpdateJobInfo* info =
159 static_cast<UpdateJobInfo*>(request->GetUserData(this));
160 request->Read(info->buffer, kBufferSize, &bytes_read);
161 OnReadCompleted(request, bytes_read);
162 }
163
164 void AppCacheUpdateJob::OnReadCompleted(URLRequest* request, int bytes_read) {
165 bool data_consumed = true;
166 if (request->status().is_success() && bytes_read > 0) {
167 UpdateJobInfo* info =
168 static_cast<UpdateJobInfo*>(request->GetUserData(this));
169
170 data_consumed = ConsumeResponseData(request, info, bytes_read);
171 if (data_consumed) {
172 bytes_read = 0;
173 while (request->Read(info->buffer, kBufferSize, &bytes_read)) {
174 if (bytes_read > 0) {
175 data_consumed = ConsumeResponseData(request, info, bytes_read);
176 if (!data_consumed)
177 break; // wait for async data processing, then read more
178 } else {
179 break;
180 }
181 }
182 }
183 }
184
185 if (data_consumed && !request->status().is_io_pending())
186 OnResponseCompleted(request);
187 }
188
189 bool AppCacheUpdateJob::ConsumeResponseData(URLRequest* request,
190 UpdateJobInfo* info,
191 int bytes_read) {
192 switch (info->type) {
193 case UpdateJobInfo::MANIFEST_FETCH:
194 manifest_data_.append(info->buffer->data(), bytes_read);
195 break;
196 case UpdateJobInfo::URL_FETCH:
197 // TODO(jennb): stream data to storage. will be async so need to wait
198 // for callback before reading next chunk.
199 // For now, schedule a task to continue reading to simulate async-ness.
200 MessageLoop::current()->PostTask(FROM_HERE,
201 method_factory_.NewRunnableMethod(
202 &AppCacheUpdateJob::ReadResponseData, request));
203 return false;
204 case UpdateJobInfo::MANIFEST_REFETCH:
205 manifest_refetch_data_.append(info->buffer->data(), bytes_read);
206 break;
207 default:
208 NOTREACHED();
209 }
210 return true;
211 }
212
213 void AppCacheUpdateJob::OnReceivedRedirect(URLRequest* request,
214 const GURL& new_url,
215 bool* defer_redirect) {
216 // Redirect is not allowed by the update process.
217 request->Cancel();
218 OnResponseCompleted(request);
219 }
220
221 void AppCacheUpdateJob::OnResponseCompleted(URLRequest* request) {
222 // TODO(jennb): think about retrying for 503s where retry-after is 0
223 UpdateJobInfo* info =
224 static_cast<UpdateJobInfo*>(request->GetUserData(this));
225 switch (info->type) {
226 case UpdateJobInfo::MANIFEST_FETCH:
227 HandleManifestFetchCompleted(request);
228 break;
229 case UpdateJobInfo::URL_FETCH:
230 HandleUrlFetchCompleted(request);
231 break;
232 case UpdateJobInfo::MANIFEST_REFETCH:
233 HandleManifestRefetchCompleted(request);
234 break;
235 default:
236 NOTREACHED();
237 }
238
239 delete request;
240 }
241
242 void AppCacheUpdateJob::HandleManifestFetchCompleted(URLRequest* request) {
243 DCHECK(internal_state_ == FETCH_MANIFEST);
244 manifest_url_request_ = NULL;
245
246 if (!request->status().is_success()) {
247 LOG(INFO) << "Request non-success, status: " << request->status().status()
248 << " os_error: " << request->status().os_error();
249 internal_state_ = CACHE_FAILURE;
250 MaybeCompleteUpdate(); // if not done, run async cache failure steps
251 return;
252 }
253
254 int response_code = request->GetResponseCode();
255 std::string mime_type;
256 request->GetMimeType(&mime_type);
257
258 if (response_code == 200 && mime_type == kManifestMimeType) {
259 if (update_type_ == UPGRADE_ATTEMPT)
260 CheckIfManifestChanged(); // continues asynchronously
261 else
262 ContinueHandleManifestFetchCompleted(true);
263 } else if (response_code == 304 && update_type_ == UPGRADE_ATTEMPT) {
264 ContinueHandleManifestFetchCompleted(false);
265 } else if (response_code == 404 || response_code == 410) {
266 group_->set_obsolete(true);
267 NotifyAllAssociatedHosts(OBSOLETE_EVENT);
268 NotifyAllPendingMasterHosts(ERROR_EVENT);
269 DeleteSoon();
270 } else {
271 LOG(INFO) << "Cache failure, response code: " << response_code;
272 internal_state_ = CACHE_FAILURE;
273 MaybeCompleteUpdate(); // if not done, run async cache failure steps
274 }
275 }
276
277 void AppCacheUpdateJob::ContinueHandleManifestFetchCompleted(bool changed) {
278 DCHECK(internal_state_ == FETCH_MANIFEST);
279
280 if (!changed) {
281 DCHECK(update_type_ == UPGRADE_ATTEMPT);
282 internal_state_ = NO_UPDATE;
283 MaybeCompleteUpdate(); // if not done, run async 6.9.4 step 7 substeps
284 return;
285 }
286
287 Manifest manifest;
288 if (!ParseManifest(manifest_url_, manifest_data_.data(),
289 manifest_data_.length(), manifest)) {
290 LOG(INFO) << "Failed to parse manifest: " << manifest_url_;
291 internal_state_ = CACHE_FAILURE;
292 MaybeCompleteUpdate(); // if not done, run async cache failure steps
293 return;
294 }
295
296 // Proceed with update process. Section 6.9.4 steps 8-20.
297 internal_state_ = DOWNLOADING;
298 inprogress_cache_ = new AppCache(service_, service_->NewCacheId());
299 inprogress_cache_->set_owning_group(group_);
300 BuildUrlFileList(manifest);
301 inprogress_cache_->InitializeWithManifest(&manifest);
302
303 // Associate all pending master hosts with the newly created cache.
304 for (PendingMasters::iterator it = pending_master_entries_.begin();
305 it != pending_master_entries_.end(); ++it) {
306 PendingHosts hosts = it->second;
307 for (PendingHosts::iterator host_it = hosts.begin();
308 host_it != hosts.end(); ++host_it) {
309 AppCacheHost* host = *host_it;
310 host->AssociateCache(inprogress_cache_);
311 }
312 }
313
314 group_->SetUpdateStatus(AppCacheGroup::DOWNLOADING);
315 NotifyAllAssociatedHosts(DOWNLOADING_EVENT);
316 FetchUrls();
317 MaybeCompleteUpdate(); // if not done, continues when async fetches complete
318 }
319
320 void AppCacheUpdateJob::HandleUrlFetchCompleted(URLRequest* request) {
321 DCHECK(internal_state_ == DOWNLOADING);
322
323 const GURL& url = request->original_url();
324 pending_url_fetches_.erase(url);
325 ++url_fetches_completed_;
326
327 int response_code = request->GetResponseCode();
328 AppCacheEntry& entry = url_file_list_.find(url)->second;
329
330 if (request->status().is_success() && response_code == 200) {
331 // TODO(jennb): associate storage with the new entry
332 inprogress_cache_->AddEntry(url, entry);
333
334 // Foreign entries will be detected during cache selection.
335 // Note: 6.9.4, step 17.9 possible optimization: if resource is HTML or XML
336 // file whose root element is an html element with a manifest attribute
337 // whose value doesn't match the manifest url of the application cache
338 // being processed, mark the entry as being foreign.
339 } else {
340 LOG(INFO) << "Request status: " << request->status().status()
341 << " os_error: " << request->status().os_error()
342 << " response code: " << response_code;
343
344 // TODO(jennb): discard any stored data for this entry
345 if (entry.IsExplicit() || entry.IsFallback()) {
346 internal_state_ = CACHE_FAILURE;
347
348 // Cancel any pending URL requests.
349 for (PendingUrlFetches::iterator it = pending_url_fetches_.begin();
350 it != pending_url_fetches_.end(); ++it) {
351 it->second->Cancel();
352 delete it->second;
353 }
354
355 url_fetches_completed_ +=
356 pending_url_fetches_.size() + urls_to_fetch_.size();
357 pending_url_fetches_.clear();
358 urls_to_fetch_.clear();
359 } else if (response_code == 404 || response_code == 410) {
360 // Entry is skipped. They are dropped from the cache.
361 } else if (update_type_ == UPGRADE_ATTEMPT) {
362 // Copy the resource and its metadata from the newest complete cache.
363 AppCache* cache = group_->newest_complete_cache();
364 AppCacheEntry* copy = cache->GetEntry(url);
365 if (copy)
366 CopyEntryToCache(url, *copy, &entry);
367 }
368 }
369
370 // Fetch another URL now that one request has completed.
371 if (internal_state_ != CACHE_FAILURE)
372 FetchUrls();
373
374 MaybeCompleteUpdate();
375 }
376
377 void AppCacheUpdateJob::HandleManifestRefetchCompleted(URLRequest* request) {
378 DCHECK(internal_state_ == REFETCH_MANIFEST);
379 manifest_url_request_ = NULL;
380
381 int response_code = request->GetResponseCode();
382 if (response_code == 304 || manifest_data_ == manifest_refetch_data_) {
383 AppCacheEntry entry(AppCacheEntry::MANIFEST);
384 // TODO(jennb): add manifest_data_ to storage and put storage key in entry
385 // Also store response headers from request for HTTP cache control.
386 inprogress_cache_->AddOrModifyEntry(manifest_url_, entry);
387 inprogress_cache_->set_update_time(base::TimeTicks::Now());
388
389 // TODO(jennb): start of part to make async (cache storage may fail;
390 // group storage may fail)
391 inprogress_cache_->set_complete(true);
392
393 // TODO(jennb): write new cache to storage here
394 group_->AddCache(inprogress_cache_);
395 // TODO(jennb): write new group to storage here
396 inprogress_cache_ = NULL;
397
398 if (update_type_ == CACHE_ATTEMPT) {
399 NotifyAllAssociatedHosts(CACHED_EVENT);
400 } else {
401 NotifyAllAssociatedHosts(UPDATE_READY_EVENT);
402 }
403 DeleteSoon();
404 // TODO(jennb): end of part that needs to be made async.
405 } else {
406 LOG(INFO) << "Request status: " << request->status().status()
407 << " os_error: " << request->status().os_error()
408 << " response code: " << response_code;
409 ScheduleUpdateRetry(kRerunDelayMs);
410 internal_state_ = CACHE_FAILURE;
411 HandleCacheFailure();
412 }
413 }
414
415 void AppCacheUpdateJob::NotifySingleHost(AppCacheHost* host,
416 EventID event_id) {
417 std::vector<int> ids(1, host->host_id());
418 host->frontend()->OnEventRaised(ids, event_id);
419 }
420
421 void AppCacheUpdateJob::NotifyAllPendingMasterHosts(EventID event_id) {
422 // Collect hosts so we only send one notification per frontend.
423 // A host can only be associated with a single pending master entry
424 // so no need to worry about duplicate hosts being added to the notifier.
425 HostNotifier host_notifier;
426 for (PendingMasters::iterator it = pending_master_entries_.begin();
427 it != pending_master_entries_.end(); ++it) {
428 PendingHosts hosts = it->second;
429 for (PendingHosts::iterator host_it = hosts.begin();
430 host_it != hosts.end(); ++host_it) {
431 AppCacheHost* host = *host_it;
432 host_notifier.AddHost(host);
433 }
434 }
435
436 host_notifier.SendNotifications(event_id);
437 }
438
439 void AppCacheUpdateJob::NotifyAllAssociatedHosts(EventID event_id) {
440 // Collect hosts so we only send one notification per frontend.
441 // A host can only be associated with a single cache so no need to worry
442 // about duplicate hosts being added to the notifier.
443 HostNotifier host_notifier;
444 if (inprogress_cache_) {
445 DCHECK(internal_state_ == DOWNLOADING || internal_state_ == CACHE_FAILURE);
446 host_notifier.AddHosts(inprogress_cache_->associated_hosts());
447 }
448
449 AppCacheGroup::Caches old_caches = group_->old_caches();
450 for (AppCacheGroup::Caches::const_iterator it = old_caches.begin();
451 it != old_caches.end(); ++it) {
452 host_notifier.AddHosts((*it)->associated_hosts());
453 }
454
455 AppCache* newest_cache = group_->newest_complete_cache();
456 if (newest_cache)
457 host_notifier.AddHosts(newest_cache->associated_hosts());
458
459 // TODO(jennb): if progress event, also pass params lengthComputable=true,
460 // total = url_file_list_.size(), loaded=url_fetches_completed_.
461 host_notifier.SendNotifications(event_id);
462 }
463
464 void AppCacheUpdateJob::CheckIfManifestChanged() {
465 DCHECK(update_type_ == UPGRADE_ATTEMPT);
466 /*
467 AppCacheEntry* entry =
468 group_->newest_complete_cache()->GetEntry(manifest_url_);
469 */
470 // TODO(jennb): load manifest data from entry (async), continues in callback
471 // callback invokes ContinueCheckIfManifestChanged
472 // For now, schedule a task to continue checking with fake loaded data
473 MessageLoop::current()->PostTask(FROM_HERE,
474 method_factory_.NewRunnableMethod(
475 &AppCacheUpdateJob::ContinueCheckIfManifestChanged,
476 simulate_manifest_changed_ ? "different" : manifest_data_));
477 }
478
479 void AppCacheUpdateJob::ContinueCheckIfManifestChanged(
480 const std::string& loaded_manifest) {
481 ContinueHandleManifestFetchCompleted(manifest_data_ != loaded_manifest);
482 }
483
484 void AppCacheUpdateJob::BuildUrlFileList(const Manifest& manifest) {
485 for (base::hash_set<std::string>::const_iterator it =
486 manifest.explicit_urls.begin();
487 it != manifest.explicit_urls.end(); ++it) {
488 AddUrlToFileList(GURL(*it), AppCacheEntry::EXPLICIT);
489 }
490
491 const std::vector<FallbackNamespace>& fallbacks =
492 manifest.fallback_namespaces;
493 for (std::vector<FallbackNamespace>::const_iterator it = fallbacks.begin();
494 it != fallbacks.end(); ++it) {
495 AddUrlToFileList(it->second, AppCacheEntry::FALLBACK);
496 }
497
498 // Add all master entries from newest complete cache.
499 if (update_type_ == UPGRADE_ATTEMPT) {
500 const AppCache::EntryMap& entries =
501 group_->newest_complete_cache()->entries();
502 for (AppCache::EntryMap::const_iterator it = entries.begin();
503 it != entries.end(); ++it) {
504 const AppCacheEntry& entry = it->second;
505 if (entry.IsMaster())
506 AddUrlToFileList(it->first, AppCacheEntry::MASTER);
507 }
508 }
509 }
510
511 void AppCacheUpdateJob::AddUrlToFileList(const GURL& url, int type) {
512 std::pair<AppCache::EntryMap::iterator, bool> ret = url_file_list_.insert(
513 AppCache::EntryMap::value_type(url, AppCacheEntry(type)));
514
515 if (ret.second)
516 urls_to_fetch_.push_back(UrlToFetch(url, false));
517 else
518 ret.first->second.add_types(type); // URL already exists. Merge types.
519 }
520
521 void AppCacheUpdateJob::FetchUrls() {
522 DCHECK(internal_state_ == DOWNLOADING);
523
524 // Fetch each URL in the list according to section 6.9.4 step 17.1-17.3.
525 // Fetch up to the concurrent limit. Other fetches will be triggered as each
526 // each fetch completes.
527 while (pending_url_fetches_.size() < kMaxConcurrentUrlFetches &&
528 !urls_to_fetch_.empty()) {
529 // Notify about progress first to ensure it starts from 0% in case any
530 // entries are skipped.
531 NotifyAllAssociatedHosts(PROGRESS_EVENT);
532
533 const GURL url = urls_to_fetch_.front().first;
534 bool storage_checked = urls_to_fetch_.front().second;
535 urls_to_fetch_.pop_front();
536
537 AppCache::EntryMap::iterator it = url_file_list_.find(url);
538 DCHECK(it != url_file_list_.end());
539 AppCacheEntry& entry = it->second;
540 if (ShouldSkipUrlFetch(entry)) {
541 ++url_fetches_completed_;
542 } else if (!storage_checked && MaybeLoadFromNewestCache(url, entry)) {
543 // Continues asynchronously after data is loaded from newest cache.
544 } else {
545 // Send URL request for the resource.
546 URLRequest* request = new URLRequest(url, this);
547 request->SetUserData(this, new UpdateJobInfo(UpdateJobInfo::URL_FETCH));
548 request->set_context(service_->request_context());
549 request->set_load_flags(
550 request->load_flags() | net::LOAD_DISABLE_INTERCEPT);
551 request->Start();
552 pending_url_fetches_.insert(PendingUrlFetches::value_type(url, request));
553 }
554 }
555 }
556
557 bool AppCacheUpdateJob::ShouldSkipUrlFetch(const AppCacheEntry& entry) {
558 if (entry.IsExplicit() || entry.IsFallback()) {
559 return false;
560 }
561
562 // TODO(jennb): decide if entry should be skipped to expire it from cache
563 return false;
564 }
565
566 bool AppCacheUpdateJob::MaybeLoadFromNewestCache(const GURL& url,
567 AppCacheEntry& entry) {
568 if (update_type_ != UPGRADE_ATTEMPT)
569 return false;
570
571 AppCache* newest = group_->newest_complete_cache();
572 AppCacheEntry* copy_me = newest->GetEntry(url);
573 if (!copy_me)
574 return false;
575
576 // TODO(jennb): load HTTP headers for copy_me and wait for callback
577 // In callback:
578 // if HTTP caching semantics for entry allows its use,
579 // CopyEntryData(url, copy_me, entry)
580 // ++urls_fetches_completed_;
581 // Else, add url back to front of urls_to_fetch and call FetchUrls().
582 // flag url somehow so that FetchUrls() doesn't end up here again.
583 // For now: post a message to pretend entry could not be copied
584 MessageLoop::current()->PostTask(FROM_HERE,
585 method_factory_.NewRunnableMethod(
586 &AppCacheUpdateJob::SimulateFailedLoadFromNewestCache, url));
587 return true;
588 }
589
590 // TODO(jennb): delete this after have real storage code
591 void AppCacheUpdateJob::SimulateFailedLoadFromNewestCache(const GURL& url) {
592 if (internal_state_ == CACHE_FAILURE)
593 return;
594
595 // Re-insert url at front of fetch list. Indicate storage has been checked.
596 urls_to_fetch_.push_front(AppCacheUpdateJob::UrlToFetch(url, true));
597 FetchUrls();
598 }
599
600 void AppCacheUpdateJob::CopyEntryToCache(const GURL& url,
601 const AppCacheEntry& src,
602 AppCacheEntry* dest) {
603 DCHECK(dest);
604 // TODO(jennb): copy storage key from src to dest
605 inprogress_cache_->AddEntry(url, *dest);
606 }
607
608 bool AppCacheUpdateJob::MaybeCompleteUpdate() {
609 if (master_entries_completed_ != pending_master_entries_.size() ||
610 url_fetches_completed_ != url_file_list_.size() ) {
611 return false;
612 }
613
614 switch (internal_state_) {
615 case NO_UPDATE:
616 // 6.9.4 steps 7.3-7.7.
617 NotifyAllAssociatedHosts(NO_UPDATE_EVENT);
618 pending_master_entries_.clear();
619 DeleteSoon();
620 break;
621 case DOWNLOADING:
622 internal_state_ = REFETCH_MANIFEST;
623 FetchManifest(false);
624 return false;
625 case CACHE_FAILURE:
626 HandleCacheFailure();
627 break;
628 default:
629 NOTREACHED();
630 }
631 return true;
632 }
633
634 void AppCacheUpdateJob::HandleCacheFailure() {
635 // 6.9.4 cache failure steps 2-8.
636 NotifyAllAssociatedHosts(ERROR_EVENT);
637 pending_master_entries_.clear();
638
639 // Discard the inprogress cache.
640 // TODO(jennb): cleanup possible storage for entries in the cache
641 if (inprogress_cache_) {
642 inprogress_cache_->set_owning_group(NULL);
643 inprogress_cache_ = NULL;
644 }
645
646 // For a CACHE_ATTEMPT, group will be discarded when this update
647 // job removes its reference to the group. Nothing more to do here.
648
649 DeleteSoon();
650 }
651
652 void AppCacheUpdateJob::ScheduleUpdateRetry(int delay_ms) {
653 // TODO(jennb): post a delayed task with the "same parameters" as this job
654 // to retry the update at a later time. Need group, URLs of pending master
655 // entries and their hosts.
656 }
657
658 void AppCacheUpdateJob::Cancel() {
659 if (manifest_url_request_) {
660 delete manifest_url_request_;
661 manifest_url_request_ = NULL;
662 }
663
664 // TODO(jennb): code other cancel cleanup (pending url requests, storage)
665 }
666
667 void AppCacheUpdateJob::DeleteSoon() {
668 // TODO(jennb): revisit if update should be deleting itself
669 MessageLoop::current()->DeleteSoon(FROM_HERE, this);
670 }
671 } // namespace appcache
OLDNEW
« no previous file with comments | « webkit/appcache/appcache_update_job.h ('k') | webkit/appcache/appcache_update_job_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698