Index: webkit/appcache/appcache_storage_impl.cc |
=================================================================== |
--- webkit/appcache/appcache_storage_impl.cc (revision 81647) |
+++ webkit/appcache/appcache_storage_impl.cc (working copy) |
@@ -1,4 +1,4 @@ |
-// Copyright (c) 2010 The Chromium Authors. All rights reserved. |
+// Copyright (c) 2011 The Chromium Authors. All rights reserved. |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
@@ -509,12 +509,13 @@ |
void AppCacheStorageImpl::StoreGroupAndCacheTask::RunCompleted() { |
if (success_) { |
- // TODO(kkanetkar): Add to creation time when that's enabled. |
storage_->origins_with_groups_.insert(group_->manifest_url().GetOrigin()); |
if (cache_ != group_->newest_complete_cache()) { |
cache_->set_complete(true); |
group_->AddCache(cache_); |
} |
+ if (group_->creation_time().is_null()) |
+ group_->set_creation_time(group_record_.creation_time); |
group_->AddNewlyDeletableResponseIds(&newly_deletable_response_ids_); |
} |
FOR_EACH_DELEGATE(delegates_, |
@@ -536,9 +537,13 @@ |
class AppCacheStorageImpl::FindMainResponseTask : public DatabaseTask { |
public: |
- FindMainResponseTask(AppCacheStorageImpl* storage, const GURL& url, |
+ FindMainResponseTask(AppCacheStorageImpl* storage, |
+ const GURL& url, |
+ const GURL& preferred_manifest_url, |
const AppCacheWorkingSet::GroupMap* groups_in_use) |
- : DatabaseTask(storage), url_(url), cache_id_(kNoCacheId) { |
+ : DatabaseTask(storage), url_(url), |
+ preferred_manifest_url_(preferred_manifest_url), |
+ cache_id_(kNoCacheId) { |
if (groups_in_use) { |
for (AppCacheWorkingSet::GroupMap::const_iterator it = |
groups_in_use->begin(); |
@@ -555,7 +560,15 @@ |
virtual void Run(); |
virtual void RunCompleted(); |
+ private: |
+ typedef std::vector<AppCacheDatabase::FallbackNameSpaceRecord*> |
+ FallbackNameSpaceVector; |
+ bool FindExactMatch(int64 preferred_id); |
+ bool FindFallback(int64 preferred_id); |
+ bool FindFirstValidFallback(const FallbackNameSpaceVector& fallbacks); |
+ |
GURL url_; |
+ GURL preferred_manifest_url_; |
std::set<int64> cache_ids_in_use_; |
AppCacheEntry entry_; |
AppCacheEntry fallback_entry_; |
@@ -566,6 +579,32 @@ |
// Helpers for FindMainResponseTask::Run() |
namespace { |
+class SortByCachePreference |
+ : public std::binary_function< |
+ AppCacheDatabase::EntryRecord, |
+ AppCacheDatabase::EntryRecord, |
+ bool> { |
+ public: |
+ SortByCachePreference(int64 preferred_id, const std::set<int64>& in_use_ids) |
+ : preferred_id_(preferred_id), in_use_ids_(in_use_ids) { |
+ } |
+ bool operator()( |
+ const AppCacheDatabase::EntryRecord& lhs, |
+ const AppCacheDatabase::EntryRecord& rhs) { |
+ return compute_value(lhs) > compute_value(rhs); |
+ } |
+ private: |
+ int compute_value(const AppCacheDatabase::EntryRecord& entry) { |
+ if (entry.cache_id == preferred_id_) |
+ return 100; |
+ else if (in_use_ids_.find(entry.cache_id) != in_use_ids_.end()) |
+ return 50; |
+ return 0; |
+ } |
+ int64 preferred_id_; |
+ const std::set<int64>& in_use_ids_; |
+}; |
+ |
bool SortByLength( |
const AppCacheDatabase::FallbackNameSpaceRecord& lhs, |
const AppCacheDatabase::FallbackNameSpaceRecord& rhs) { |
@@ -609,99 +648,150 @@ |
WhiteListMap namespaces_map_; |
AppCacheDatabase* database_; |
}; |
+ |
+bool FindManifestForEntry( |
+ const AppCacheDatabase::EntryRecord& entry_record, |
+ AppCacheDatabase* database, |
+ GURL* manifest_url_out) { |
+ AppCacheDatabase::GroupRecord group_record; |
+ if (!database->FindGroupForCache(entry_record.cache_id, &group_record)) { |
+ NOTREACHED() << "A cache without a group is not expected."; |
+ return false; |
+ } |
+ *manifest_url_out = group_record.manifest_url; |
+ return true; |
+} |
+ |
} // namespace |
void AppCacheStorageImpl::FindMainResponseTask::Run() { |
- // We have a bias for hits from caches that are in use. |
+ // NOTE: The heuristics around choosing amoungst multiple candidates |
+ // is underspecified, and just plain not fully understood. This needs |
+ // to be refined. |
- // TODO(michaeln): The heuristics around choosing amoungst |
- // multiple candidates is under specified, and just plain |
- // not fully understood. Refine these over time. In particular, |
- // * prefer candidates from newer caches |
- // * take into account the cache associated with the document |
- // that initiated the navigation |
- // * take into account the cache associated with the document |
- // currently residing in the frame being navigated |
+ // The 'preferred_manifest_url' is the url of the manifest associated |
+ // with the page that opened or embedded the page being loaded now. |
+ // We have a strong preference to use resources from that cache. |
+ // We also have a lesser bias to use resources from caches that are currently |
+ // being used by other unrelated pages. |
+ // TODO(michaeln): come up with a 'preferred_manifest_url' in more cases |
+ // - when navigating a frame whose current contents are from an appcache |
+ // - when clicking an href in a frame that is appcached |
+ int64 preferred_cache_id = kNoCacheId; |
+ if (!preferred_manifest_url_.is_empty()) { |
+ AppCacheDatabase::GroupRecord preferred_group; |
+ AppCacheDatabase::CacheRecord preferred_cache; |
+ if (database_->FindGroupForManifestUrl( |
+ preferred_manifest_url_, &preferred_group) && |
+ database_->FindCacheForGroup( |
+ preferred_group.group_id, &preferred_cache)) { |
+ preferred_cache_id = preferred_cache.cache_id; |
+ } |
+ } |
- // First look for an exact match. We don't worry about whether |
- // the containing cache is in-use in this loop because the |
- // storage class's FindResponseForMainRequest method does that |
- // as a pre-optimization. |
+ if (FindExactMatch(preferred_cache_id) || |
+ FindFallback(preferred_cache_id)) { |
+ // We found something. |
+ DCHECK(cache_id_ != kNoCacheId && !manifest_url_.is_empty()); |
+ return; |
+ } |
+ |
+ // We didn't find anything. |
+ DCHECK(cache_id_ == kNoCacheId && manifest_url_.is_empty()); |
+} |
+ |
+bool AppCacheStorageImpl:: |
+FindMainResponseTask::FindExactMatch(int64 preferred_cache_id) { |
std::vector<AppCacheDatabase::EntryRecord> entries; |
if (database_->FindEntriesForUrl(url_, &entries) && !entries.empty()) { |
+ // Sort them in order of preference, from the preferred_cache first, |
+ // followed by hits from caches that are 'in use', then the rest. |
+ std::sort(entries.begin(), entries.end(), |
+ SortByCachePreference(preferred_cache_id, cache_ids_in_use_)); |
+ |
+ // Take the first with a valid, non-foreign entry. |
std::vector<AppCacheDatabase::EntryRecord>::iterator iter; |
for (iter = entries.begin(); iter < entries.end(); ++iter) { |
- if (iter->flags & AppCacheEntry::FOREIGN) |
+ if ((iter->flags & AppCacheEntry::FOREIGN) || |
+ !FindManifestForEntry(*iter, database_, &manifest_url_)) { |
continue; |
- |
- AppCacheDatabase::GroupRecord group_record; |
- if (!database_->FindGroupForCache(iter->cache_id, &group_record)) { |
- NOTREACHED() << "A cache without a group is not expected."; |
- continue; |
} |
entry_ = AppCacheEntry(iter->flags, iter->response_id); |
cache_id_ = iter->cache_id; |
- manifest_url_ = group_record.manifest_url; |
- return; |
+ return true; // We found an exact match. |
} |
} |
+ return false; |
+} |
- // No exact matches, look at the fallback namespaces for this origin. |
- std::vector<AppCacheDatabase::FallbackNameSpaceRecord> fallbacks; |
- if (!database_->FindFallbackNameSpacesForOrigin(url_.GetOrigin(), &fallbacks) |
- || fallbacks.empty()) { |
- return; |
+bool AppCacheStorageImpl:: |
+FindMainResponseTask::FindFallback(int64 preferred_cache_id) { |
+ std::vector<AppCacheDatabase::FallbackNameSpaceRecord> all_fallbacks; |
+ if (!database_->FindFallbackNameSpacesForOrigin( |
+ url_.GetOrigin(), &all_fallbacks) |
+ || all_fallbacks.empty()) { |
+ return false; |
} |
- // Sort by namespace url string length, longest to shortest, |
- // since longer matches trump when matching a url to a namespace. |
- std::sort(fallbacks.begin(), fallbacks.end(), SortByLength); |
+ // Sort them by length, longer matches within the same cache/bucket take |
+ // precedence. |
+ std::sort(all_fallbacks.begin(), all_fallbacks.end(), SortByLength); |
- bool has_candidate = false; |
- GURL candidate_fallback_namespace; |
+ // Filter the list and bin them into buckets. |
+ NetworkNamespaceHelper network_namespace_helper(database_); |
+ FallbackNameSpaceVector preferred_fallbacks; |
+ FallbackNameSpaceVector inuse_fallbacks; |
+ FallbackNameSpaceVector other_fallbacks; |
std::vector<AppCacheDatabase::FallbackNameSpaceRecord>::iterator iter; |
- NetworkNamespaceHelper network_namespace_helper(database_); |
- for (iter = fallbacks.begin(); iter < fallbacks.end(); ++iter) { |
- // Skip this fallback namespace if the requested url falls into a network |
- // namespace of the containing appcache. |
+ for (iter = all_fallbacks.begin(); iter < all_fallbacks.end(); ++iter) { |
+ // Skip those that aren't a prefix match. |
+ if (!StartsWithASCII(url_.spec(), iter->namespace_url.spec(), true)) |
+ continue; |
+ |
+ // Skip fallback namespaces where the requested url falls into a network |
+ // namespace of its containing appcache. |
if (network_namespace_helper.IsInNetworkNamespace(url_, iter->cache_id)) |
continue; |
- if (has_candidate && |
- (candidate_fallback_namespace.spec().length() > |
- iter->namespace_url.spec().length())) { |
- break; // Stop iterating since longer namespace prefix matches win. |
- } |
+ // Bin them into one of our three buckets. |
+ if (iter->cache_id == preferred_cache_id) |
+ preferred_fallbacks.push_back(&(*iter)); |
+ else if (cache_ids_in_use_.find(iter->cache_id) != cache_ids_in_use_.end()) |
+ inuse_fallbacks.push_back(&(*iter)); |
+ else |
+ other_fallbacks.push_back(&(*iter)); |
+ } |
- if (StartsWithASCII(url_.spec(), iter->namespace_url.spec(), true)) { |
- bool is_cache_in_use = cache_ids_in_use_.find(iter->cache_id) != |
- cache_ids_in_use_.end(); |
+ if (FindFirstValidFallback(preferred_fallbacks) || |
+ FindFirstValidFallback(inuse_fallbacks) || |
+ FindFirstValidFallback(other_fallbacks)) |
+ return true; // We found one. |
- bool take_new_candidate = !has_candidate || is_cache_in_use; |
+ // We didn't find anything. |
+ return false; |
+} |
- AppCacheDatabase::EntryRecord entry_record; |
- if (take_new_candidate && |
- database_->FindEntry(iter->cache_id, iter->fallback_entry_url, |
- &entry_record)) { |
- if (entry_record.flags & AppCacheEntry::FOREIGN) |
- continue; |
- AppCacheDatabase::GroupRecord group_record; |
- if (!database_->FindGroupForCache(iter->cache_id, &group_record)) { |
- NOTREACHED() << "A cache without a group is not expected."; |
- continue; |
- } |
- cache_id_ = iter->cache_id; |
- fallback_url_ = iter->fallback_entry_url; |
- manifest_url_ = group_record.manifest_url; |
- fallback_entry_ = AppCacheEntry( |
- entry_record.flags, entry_record.response_id); |
- if (is_cache_in_use) |
- break; // Stop iterating since we favor hits from in-use caches. |
- candidate_fallback_namespace = iter->namespace_url; |
- has_candidate = true; |
+bool AppCacheStorageImpl:: |
+FindMainResponseTask::FindFirstValidFallback( |
+ const FallbackNameSpaceVector& fallbacks) { |
+ // Take the first with a valid, non-foreign entry. |
+ FallbackNameSpaceVector::const_iterator iter; |
+ for (iter = fallbacks.begin(); iter < fallbacks.end(); ++iter) { |
+ AppCacheDatabase::EntryRecord entry_record; |
+ if (database_->FindEntry((*iter)->cache_id, (*iter)->fallback_entry_url, |
+ &entry_record)) { |
+ if ((entry_record.flags & AppCacheEntry::FOREIGN) || |
+ !FindManifestForEntry(entry_record, database_, &manifest_url_)) { |
+ continue; |
} |
+ cache_id_ = (*iter)->cache_id; |
+ fallback_url_ = (*iter)->fallback_entry_url; |
+ fallback_entry_ = AppCacheEntry( |
+ entry_record.flags, entry_record.response_id); |
+ return true; // We found one. |
} |
} |
+ return false; // We didn't find a match. |
} |
void AppCacheStorageImpl::FindMainResponseTask::RunCompleted() { |
@@ -1038,7 +1128,8 @@ |
} |
void AppCacheStorageImpl::FindResponseForMainRequest( |
- const GURL& url, Delegate* delegate) { |
+ const GURL& url, const GURL& preferred_manifest_url, |
+ Delegate* delegate) { |
DCHECK(delegate); |
const GURL* url_ptr = &url; |
@@ -1057,22 +1148,23 @@ |
const AppCacheWorkingSet::GroupMap* groups_in_use = |
working_set()->GetGroupsInOrigin(origin); |
if (groups_in_use) { |
- for (AppCacheWorkingSet::GroupMap::const_iterator it = |
- groups_in_use->begin(); |
- it != groups_in_use->end(); ++it) { |
- AppCacheGroup* group = it->second; |
- AppCache* cache = group->newest_complete_cache(); |
- if (group->is_obsolete() || !cache) |
- continue; |
- |
- AppCacheEntry* entry = cache->GetEntry(*url_ptr); |
- if (entry && !entry->IsForeign()) { |
- ScheduleSimpleTask(method_factory_.NewRunnableMethod( |
- &AppCacheStorageImpl::DeliverShortCircuitedFindMainResponse, |
- url, *entry, make_scoped_refptr(group), make_scoped_refptr(cache), |
- make_scoped_refptr(GetOrCreateDelegateReference(delegate)))); |
- return; |
+ if (!preferred_manifest_url.is_empty()) { |
+ AppCacheWorkingSet::GroupMap::const_iterator found = |
+ groups_in_use->find(preferred_manifest_url); |
+ if (found != groups_in_use->end() && |
+ FindResponseForMainRequestInGroup( |
+ found->second, *url_ptr, delegate)) { |
+ return; |
} |
+ } else { |
+ for (AppCacheWorkingSet::GroupMap::const_iterator it = |
+ groups_in_use->begin(); |
+ it != groups_in_use->end(); ++it) { |
+ if (FindResponseForMainRequestInGroup( |
+ it->second, *url_ptr, delegate)) { |
+ return; |
+ } |
+ } |
} |
} |
@@ -1091,11 +1183,29 @@ |
// We have to query the database, schedule a database task to do so. |
scoped_refptr<FindMainResponseTask> task( |
- new FindMainResponseTask(this, *url_ptr, groups_in_use)); |
+ new FindMainResponseTask(this, *url_ptr, preferred_manifest_url, |
+ groups_in_use)); |
task->AddDelegate(GetOrCreateDelegateReference(delegate)); |
task->Schedule(); |
} |
+bool AppCacheStorageImpl::FindResponseForMainRequestInGroup( |
+ AppCacheGroup* group, const GURL& url, Delegate* delegate) { |
+ AppCache* cache = group->newest_complete_cache(); |
+ if (group->is_obsolete() || !cache) |
+ return false; |
+ |
+ AppCacheEntry* entry = cache->GetEntry(url); |
+ if (!entry || entry->IsForeign()) |
+ return false; |
+ |
+ ScheduleSimpleTask(method_factory_.NewRunnableMethod( |
+ &AppCacheStorageImpl::DeliverShortCircuitedFindMainResponse, |
+ url, *entry, make_scoped_refptr(group), make_scoped_refptr(cache), |
+ make_scoped_refptr(GetOrCreateDelegateReference(delegate)))); |
+ return true; |
+} |
+ |
void AppCacheStorageImpl::DeliverShortCircuitedFindMainResponse( |
const GURL& url, AppCacheEntry found_entry, |
scoped_refptr<AppCacheGroup> group, scoped_refptr<AppCache> cache, |