Update V8 to version 4.6.55.
[chromium-blink-merge.git] / content / browser / appcache / appcache_host.cc
blobdbdaf308fcd9e2bce39e8dd5f34fde2db899a865
1 // Copyright (c) 2011 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.
5 #include "content/browser/appcache/appcache_host.h"
7 #include "base/logging.h"
8 #include "base/strings/string_util.h"
9 #include "base/strings/stringprintf.h"
10 #include "content/browser/appcache/appcache.h"
11 #include "content/browser/appcache/appcache_backend_impl.h"
12 #include "content/browser/appcache/appcache_policy.h"
13 #include "content/browser/appcache/appcache_request_handler.h"
14 #include "net/url_request/url_request.h"
15 #include "storage/browser/quota/quota_manager_proxy.h"
17 namespace content {
19 namespace {
21 void FillCacheInfo(const AppCache* cache,
22 const GURL& manifest_url,
23 AppCacheStatus status, AppCacheInfo* info) {
24 info->manifest_url = manifest_url;
25 info->status = status;
27 if (!cache)
28 return;
30 info->cache_id = cache->cache_id();
32 if (!cache->is_complete())
33 return;
35 DCHECK(cache->owning_group());
36 info->is_complete = true;
37 info->group_id = cache->owning_group()->group_id();
38 info->last_update_time = cache->update_time();
39 info->creation_time = cache->owning_group()->creation_time();
40 info->size = cache->cache_size();
43 } // Anonymous namespace
45 AppCacheHost::AppCacheHost(int host_id, AppCacheFrontend* frontend,
46 AppCacheServiceImpl* service)
47 : host_id_(host_id),
48 spawning_host_id_(kAppCacheNoHostId), spawning_process_id_(0),
49 parent_host_id_(kAppCacheNoHostId), parent_process_id_(0),
50 pending_main_resource_cache_id_(kAppCacheNoCacheId),
51 pending_selected_cache_id_(kAppCacheNoCacheId),
52 was_select_cache_called_(false),
53 is_cache_selection_enabled_(true),
54 frontend_(frontend), service_(service),
55 storage_(service->storage()),
56 pending_callback_param_(NULL),
57 main_resource_was_namespace_entry_(false),
58 main_resource_blocked_(false),
59 associated_cache_info_pending_(false) {
60 service_->AddObserver(this);
63 AppCacheHost::~AppCacheHost() {
64 service_->RemoveObserver(this);
65 FOR_EACH_OBSERVER(Observer, observers_, OnDestructionImminent(this));
66 if (associated_cache_.get())
67 associated_cache_->UnassociateHost(this);
68 if (group_being_updated_.get())
69 group_being_updated_->RemoveUpdateObserver(this);
70 storage()->CancelDelegateCallbacks(this);
71 if (service()->quota_manager_proxy() && !origin_in_use_.is_empty())
72 service()->quota_manager_proxy()->NotifyOriginNoLongerInUse(origin_in_use_);
75 void AppCacheHost::AddObserver(Observer* observer) {
76 observers_.AddObserver(observer);
79 void AppCacheHost::RemoveObserver(Observer* observer) {
80 observers_.RemoveObserver(observer);
83 void AppCacheHost::SelectCache(const GURL& document_url,
84 const int64 cache_document_was_loaded_from,
85 const GURL& manifest_url) {
86 DCHECK(pending_start_update_callback_.is_null() &&
87 pending_swap_cache_callback_.is_null() &&
88 pending_get_status_callback_.is_null() &&
89 !is_selection_pending() && !was_select_cache_called_);
91 was_select_cache_called_ = true;
92 if (!is_cache_selection_enabled_) {
93 FinishCacheSelection(NULL, NULL);
94 return;
97 origin_in_use_ = document_url.GetOrigin();
98 if (service()->quota_manager_proxy() && !origin_in_use_.is_empty())
99 service()->quota_manager_proxy()->NotifyOriginInUse(origin_in_use_);
101 if (main_resource_blocked_)
102 frontend_->OnContentBlocked(host_id_,
103 blocked_manifest_url_);
105 // 6.9.6 The application cache selection algorithm.
106 // The algorithm is started here and continues in FinishCacheSelection,
107 // after cache or group loading is complete.
108 // Note: Foreign entries are detected on the client side and
109 // MarkAsForeignEntry is called in that case, so that detection
110 // step is skipped here. See WebApplicationCacheHostImpl.cc
112 if (cache_document_was_loaded_from != kAppCacheNoCacheId) {
113 LoadSelectedCache(cache_document_was_loaded_from);
114 return;
117 if (!manifest_url.is_empty() &&
118 (manifest_url.GetOrigin() == document_url.GetOrigin())) {
119 DCHECK(!first_party_url_.is_empty());
120 AppCachePolicy* policy = service()->appcache_policy();
121 if (policy &&
122 !policy->CanCreateAppCache(manifest_url, first_party_url_)) {
123 FinishCacheSelection(NULL, NULL);
124 std::vector<int> host_ids(1, host_id_);
125 frontend_->OnEventRaised(host_ids, APPCACHE_CHECKING_EVENT);
126 frontend_->OnErrorEventRaised(
127 host_ids,
128 AppCacheErrorDetails(
129 "Cache creation was blocked by the content policy",
130 APPCACHE_POLICY_ERROR,
131 GURL(),
133 false /*is_cross_origin*/));
134 frontend_->OnContentBlocked(host_id_, manifest_url);
135 return;
138 // Note: The client detects if the document was not loaded using HTTP GET
139 // and invokes SelectCache without a manifest url, so that detection step
140 // is also skipped here. See WebApplicationCacheHostImpl.cc
141 set_preferred_manifest_url(manifest_url);
142 new_master_entry_url_ = document_url;
143 LoadOrCreateGroup(manifest_url);
144 return;
147 // TODO(michaeln): If there was a manifest URL, the user agent may report
148 // to the user that it was ignored, to aid in application development.
149 FinishCacheSelection(NULL, NULL);
152 void AppCacheHost::SelectCacheForWorker(int parent_process_id,
153 int parent_host_id) {
154 DCHECK(pending_start_update_callback_.is_null() &&
155 pending_swap_cache_callback_.is_null() &&
156 pending_get_status_callback_.is_null() &&
157 !is_selection_pending() && !was_select_cache_called_);
159 was_select_cache_called_ = true;
160 parent_process_id_ = parent_process_id;
161 parent_host_id_ = parent_host_id;
162 FinishCacheSelection(NULL, NULL);
165 void AppCacheHost::SelectCacheForSharedWorker(int64 appcache_id) {
166 DCHECK(pending_start_update_callback_.is_null() &&
167 pending_swap_cache_callback_.is_null() &&
168 pending_get_status_callback_.is_null() &&
169 !is_selection_pending() && !was_select_cache_called_);
171 was_select_cache_called_ = true;
172 if (appcache_id != kAppCacheNoCacheId) {
173 LoadSelectedCache(appcache_id);
174 return;
176 FinishCacheSelection(NULL, NULL);
179 // TODO(michaeln): change method name to MarkEntryAsForeign for consistency
180 void AppCacheHost::MarkAsForeignEntry(const GURL& document_url,
181 int64 cache_document_was_loaded_from) {
182 // The document url is not the resource url in the fallback case.
183 storage()->MarkEntryAsForeign(
184 main_resource_was_namespace_entry_ ? namespace_entry_url_ : document_url,
185 cache_document_was_loaded_from);
186 SelectCache(document_url, kAppCacheNoCacheId, GURL());
189 void AppCacheHost::GetStatusWithCallback(const GetStatusCallback& callback,
190 void* callback_param) {
191 DCHECK(pending_start_update_callback_.is_null() &&
192 pending_swap_cache_callback_.is_null() &&
193 pending_get_status_callback_.is_null());
195 pending_get_status_callback_ = callback;
196 pending_callback_param_ = callback_param;
197 if (is_selection_pending())
198 return;
200 DoPendingGetStatus();
203 void AppCacheHost::DoPendingGetStatus() {
204 DCHECK_EQ(false, pending_get_status_callback_.is_null());
206 pending_get_status_callback_.Run(GetStatus(), pending_callback_param_);
207 pending_get_status_callback_.Reset();
208 pending_callback_param_ = NULL;
211 void AppCacheHost::StartUpdateWithCallback(const StartUpdateCallback& callback,
212 void* callback_param) {
213 DCHECK(pending_start_update_callback_.is_null() &&
214 pending_swap_cache_callback_.is_null() &&
215 pending_get_status_callback_.is_null());
217 pending_start_update_callback_ = callback;
218 pending_callback_param_ = callback_param;
219 if (is_selection_pending())
220 return;
222 DoPendingStartUpdate();
225 void AppCacheHost::DoPendingStartUpdate() {
226 DCHECK_EQ(false, pending_start_update_callback_.is_null());
228 // 6.9.8 Application cache API
229 bool success = false;
230 if (associated_cache_.get() && associated_cache_->owning_group()) {
231 AppCacheGroup* group = associated_cache_->owning_group();
232 if (!group->is_obsolete() && !group->is_being_deleted()) {
233 success = true;
234 group->StartUpdate();
238 pending_start_update_callback_.Run(success, pending_callback_param_);
239 pending_start_update_callback_.Reset();
240 pending_callback_param_ = NULL;
243 void AppCacheHost::SwapCacheWithCallback(const SwapCacheCallback& callback,
244 void* callback_param) {
245 DCHECK(pending_start_update_callback_.is_null() &&
246 pending_swap_cache_callback_.is_null() &&
247 pending_get_status_callback_.is_null());
249 pending_swap_cache_callback_ = callback;
250 pending_callback_param_ = callback_param;
251 if (is_selection_pending())
252 return;
254 DoPendingSwapCache();
257 void AppCacheHost::DoPendingSwapCache() {
258 DCHECK_EQ(false, pending_swap_cache_callback_.is_null());
260 // 6.9.8 Application cache API
261 bool success = false;
262 if (associated_cache_.get() && associated_cache_->owning_group()) {
263 if (associated_cache_->owning_group()->is_obsolete()) {
264 success = true;
265 AssociateNoCache(GURL());
266 } else if (swappable_cache_.get()) {
267 DCHECK(swappable_cache_.get() ==
268 swappable_cache_->owning_group()->newest_complete_cache());
269 success = true;
270 AssociateCompleteCache(swappable_cache_.get());
274 pending_swap_cache_callback_.Run(success, pending_callback_param_);
275 pending_swap_cache_callback_.Reset();
276 pending_callback_param_ = NULL;
279 void AppCacheHost::SetSpawningHostId(
280 int spawning_process_id, int spawning_host_id) {
281 spawning_process_id_ = spawning_process_id;
282 spawning_host_id_ = spawning_host_id;
285 const AppCacheHost* AppCacheHost::GetSpawningHost() const {
286 AppCacheBackendImpl* backend = service_->GetBackend(spawning_process_id_);
287 return backend ? backend->GetHost(spawning_host_id_) : NULL;
290 AppCacheHost* AppCacheHost::GetParentAppCacheHost() const {
291 DCHECK(is_for_dedicated_worker());
292 AppCacheBackendImpl* backend = service_->GetBackend(parent_process_id_);
293 return backend ? backend->GetHost(parent_host_id_) : NULL;
296 AppCacheRequestHandler* AppCacheHost::CreateRequestHandler(
297 net::URLRequest* request,
298 ResourceType resource_type,
299 bool should_reset_appcache) {
300 if (is_for_dedicated_worker()) {
301 AppCacheHost* parent_host = GetParentAppCacheHost();
302 if (parent_host)
303 return parent_host->CreateRequestHandler(
304 request, resource_type, should_reset_appcache);
305 return NULL;
308 if (AppCacheRequestHandler::IsMainResourceType(resource_type)) {
309 // Store the first party origin so that it can be used later in SelectCache
310 // for checking whether the creation of the appcache is allowed.
311 first_party_url_ = request->first_party_for_cookies();
312 return new AppCacheRequestHandler(
313 this, resource_type, should_reset_appcache);
316 if ((associated_cache() && associated_cache()->is_complete()) ||
317 is_selection_pending()) {
318 return new AppCacheRequestHandler(
319 this, resource_type, should_reset_appcache);
321 return NULL;
324 void AppCacheHost::GetResourceList(
325 AppCacheResourceInfoVector* resource_infos) {
326 if (associated_cache_.get() && associated_cache_->is_complete())
327 associated_cache_->ToResourceInfoVector(resource_infos);
330 AppCacheStatus AppCacheHost::GetStatus() {
331 // 6.9.8 Application cache API
332 AppCache* cache = associated_cache();
333 if (!cache)
334 return APPCACHE_STATUS_UNCACHED;
336 // A cache without an owning group represents the cache being constructed
337 // during the application cache update process.
338 if (!cache->owning_group())
339 return APPCACHE_STATUS_DOWNLOADING;
341 if (cache->owning_group()->is_obsolete())
342 return APPCACHE_STATUS_OBSOLETE;
343 if (cache->owning_group()->update_status() == AppCacheGroup::CHECKING)
344 return APPCACHE_STATUS_CHECKING;
345 if (cache->owning_group()->update_status() == AppCacheGroup::DOWNLOADING)
346 return APPCACHE_STATUS_DOWNLOADING;
347 if (swappable_cache_.get())
348 return APPCACHE_STATUS_UPDATE_READY;
349 return APPCACHE_STATUS_IDLE;
352 void AppCacheHost::LoadOrCreateGroup(const GURL& manifest_url) {
353 DCHECK(manifest_url.is_valid());
354 pending_selected_manifest_url_ = manifest_url;
355 storage()->LoadOrCreateGroup(manifest_url, this);
358 void AppCacheHost::OnGroupLoaded(AppCacheGroup* group,
359 const GURL& manifest_url) {
360 DCHECK(manifest_url == pending_selected_manifest_url_);
361 pending_selected_manifest_url_ = GURL();
362 FinishCacheSelection(NULL, group);
365 void AppCacheHost::LoadSelectedCache(int64 cache_id) {
366 DCHECK(cache_id != kAppCacheNoCacheId);
367 pending_selected_cache_id_ = cache_id;
368 storage()->LoadCache(cache_id, this);
371 void AppCacheHost::OnCacheLoaded(AppCache* cache, int64 cache_id) {
372 if (cache_id == pending_main_resource_cache_id_) {
373 pending_main_resource_cache_id_ = kAppCacheNoCacheId;
374 main_resource_cache_ = cache;
375 } else if (cache_id == pending_selected_cache_id_) {
376 pending_selected_cache_id_ = kAppCacheNoCacheId;
377 FinishCacheSelection(cache, NULL);
381 void AppCacheHost::FinishCacheSelection(
382 AppCache *cache, AppCacheGroup* group) {
383 DCHECK(!associated_cache());
385 // 6.9.6 The application cache selection algorithm
386 if (cache) {
387 // If document was loaded from an application cache, Associate document
388 // with the application cache from which it was loaded. Invoke the
389 // application cache update process for that cache and with the browsing
390 // context being navigated.
391 DCHECK(cache->owning_group());
392 DCHECK(new_master_entry_url_.is_empty());
393 DCHECK_EQ(cache->owning_group()->manifest_url(), preferred_manifest_url_);
394 AppCacheGroup* owing_group = cache->owning_group();
395 const char* kFormatString =
396 "Document was loaded from Application Cache with manifest %s";
397 frontend_->OnLogMessage(
398 host_id_, APPCACHE_LOG_INFO,
399 base::StringPrintf(
400 kFormatString, owing_group->manifest_url().spec().c_str()));
401 AssociateCompleteCache(cache);
402 if (!owing_group->is_obsolete() && !owing_group->is_being_deleted()) {
403 owing_group->StartUpdateWithHost(this);
404 ObserveGroupBeingUpdated(owing_group);
406 } else if (group && !group->is_being_deleted()) {
407 // If document was loaded using HTTP GET or equivalent, and, there is a
408 // manifest URL, and manifest URL has the same origin as document.
409 // Invoke the application cache update process for manifest URL, with
410 // the browsing context being navigated, and with document and the
411 // resource from which document was loaded as the new master resourse.
412 DCHECK(!group->is_obsolete());
413 DCHECK(new_master_entry_url_.is_valid());
414 DCHECK_EQ(group->manifest_url(), preferred_manifest_url_);
415 const char* kFormatString = group->HasCache() ?
416 "Adding master entry to Application Cache with manifest %s" :
417 "Creating Application Cache with manifest %s";
418 frontend_->OnLogMessage(
419 host_id_, APPCACHE_LOG_INFO,
420 base::StringPrintf(kFormatString,
421 group->manifest_url().spec().c_str()));
422 // The UpdateJob may produce one for us later.
423 AssociateNoCache(preferred_manifest_url_);
424 group->StartUpdateWithNewMasterEntry(this, new_master_entry_url_);
425 ObserveGroupBeingUpdated(group);
426 } else {
427 // Otherwise, the Document is not associated with any application cache.
428 new_master_entry_url_ = GURL();
429 AssociateNoCache(GURL());
432 // Respond to pending callbacks now that we have a selection.
433 if (!pending_get_status_callback_.is_null())
434 DoPendingGetStatus();
435 else if (!pending_start_update_callback_.is_null())
436 DoPendingStartUpdate();
437 else if (!pending_swap_cache_callback_.is_null())
438 DoPendingSwapCache();
440 FOR_EACH_OBSERVER(Observer, observers_, OnCacheSelectionComplete(this));
443 void AppCacheHost::OnServiceReinitialized(
444 AppCacheStorageReference* old_storage_ref) {
445 // We continue to use the disabled instance, but arrange for its
446 // deletion when its no longer needed.
447 if (old_storage_ref->storage() == storage())
448 disabled_storage_reference_ = old_storage_ref;
451 void AppCacheHost::ObserveGroupBeingUpdated(AppCacheGroup* group) {
452 DCHECK(!group_being_updated_.get());
453 group_being_updated_ = group;
454 newest_cache_of_group_being_updated_ = group->newest_complete_cache();
455 group->AddUpdateObserver(this);
458 void AppCacheHost::OnUpdateComplete(AppCacheGroup* group) {
459 DCHECK_EQ(group, group_being_updated_.get());
460 group->RemoveUpdateObserver(this);
462 // Add a reference to the newest complete cache.
463 SetSwappableCache(group);
465 group_being_updated_ = NULL;
466 newest_cache_of_group_being_updated_ = NULL;
468 if (associated_cache_info_pending_ && associated_cache_.get() &&
469 associated_cache_->is_complete()) {
470 AppCacheInfo info;
471 FillCacheInfo(
472 associated_cache_.get(), preferred_manifest_url_, GetStatus(), &info);
473 associated_cache_info_pending_ = false;
474 frontend_->OnCacheSelected(host_id_, info);
478 void AppCacheHost::SetSwappableCache(AppCacheGroup* group) {
479 if (!group) {
480 swappable_cache_ = NULL;
481 } else {
482 AppCache* new_cache = group->newest_complete_cache();
483 if (new_cache != associated_cache_.get())
484 swappable_cache_ = new_cache;
485 else
486 swappable_cache_ = NULL;
490 void AppCacheHost::LoadMainResourceCache(int64 cache_id) {
491 DCHECK(cache_id != kAppCacheNoCacheId);
492 if (pending_main_resource_cache_id_ == cache_id ||
493 (main_resource_cache_.get() &&
494 main_resource_cache_->cache_id() == cache_id)) {
495 return;
497 pending_main_resource_cache_id_ = cache_id;
498 storage()->LoadCache(cache_id, this);
501 void AppCacheHost::NotifyMainResourceIsNamespaceEntry(
502 const GURL& namespace_entry_url) {
503 main_resource_was_namespace_entry_ = true;
504 namespace_entry_url_ = namespace_entry_url;
507 void AppCacheHost::NotifyMainResourceBlocked(const GURL& manifest_url) {
508 main_resource_blocked_ = true;
509 blocked_manifest_url_ = manifest_url;
512 void AppCacheHost::PrepareForTransfer() {
513 // This can only happen prior to the document having been loaded.
514 DCHECK(!associated_cache());
515 DCHECK(!is_selection_pending());
516 DCHECK(!group_being_updated_.get());
517 host_id_ = kAppCacheNoHostId;
518 frontend_ = NULL;
521 void AppCacheHost::CompleteTransfer(int host_id, AppCacheFrontend* frontend) {
522 host_id_ = host_id;
523 frontend_ = frontend;
526 void AppCacheHost::AssociateNoCache(const GURL& manifest_url) {
527 // manifest url can be empty.
528 AssociateCacheHelper(NULL, manifest_url);
531 void AppCacheHost::AssociateIncompleteCache(AppCache* cache,
532 const GURL& manifest_url) {
533 DCHECK(cache && !cache->is_complete());
534 DCHECK(!manifest_url.is_empty());
535 AssociateCacheHelper(cache, manifest_url);
538 void AppCacheHost::AssociateCompleteCache(AppCache* cache) {
539 DCHECK(cache && cache->is_complete());
540 AssociateCacheHelper(cache, cache->owning_group()->manifest_url());
543 void AppCacheHost::AssociateCacheHelper(AppCache* cache,
544 const GURL& manifest_url) {
545 if (associated_cache_.get()) {
546 associated_cache_->UnassociateHost(this);
549 associated_cache_ = cache;
550 SetSwappableCache(cache ? cache->owning_group() : NULL);
551 associated_cache_info_pending_ = cache && !cache->is_complete();
552 AppCacheInfo info;
553 if (cache)
554 cache->AssociateHost(this);
556 FillCacheInfo(cache, manifest_url, GetStatus(), &info);
557 frontend_->OnCacheSelected(host_id_, info);
560 } // namespace content