Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / browser / appcache / appcache.cc
blobaa0b224bd7e8b060d402aa20b9465ee36b815275
1 // Copyright (c) 2012 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.h"
7 #include <algorithm>
9 #include "base/logging.h"
10 #include "base/stl_util.h"
11 #include "content/browser/appcache/appcache_executable_handler.h"
12 #include "content/browser/appcache/appcache_group.h"
13 #include "content/browser/appcache/appcache_host.h"
14 #include "content/browser/appcache/appcache_storage.h"
15 #include "content/common/appcache_interfaces.h"
17 namespace content {
19 AppCache::AppCache(AppCacheStorage* storage, int64 cache_id)
20 : cache_id_(cache_id),
21 owning_group_(NULL),
22 online_whitelist_all_(false),
23 is_complete_(false),
24 cache_size_(0),
25 storage_(storage) {
26 storage_->working_set()->AddCache(this);
29 AppCache::~AppCache() {
30 DCHECK(associated_hosts_.empty());
31 if (owning_group_.get()) {
32 DCHECK(is_complete_);
33 owning_group_->RemoveCache(this);
35 DCHECK(!owning_group_.get());
36 storage_->working_set()->RemoveCache(this);
37 STLDeleteContainerPairSecondPointers(
38 executable_handlers_.begin(), executable_handlers_.end());
41 void AppCache::UnassociateHost(AppCacheHost* host) {
42 associated_hosts_.erase(host);
45 void AppCache::AddEntry(const GURL& url, const AppCacheEntry& entry) {
46 DCHECK(entries_.find(url) == entries_.end());
47 entries_.insert(EntryMap::value_type(url, entry));
48 cache_size_ += entry.response_size();
51 bool AppCache::AddOrModifyEntry(const GURL& url, const AppCacheEntry& entry) {
52 std::pair<EntryMap::iterator, bool> ret =
53 entries_.insert(EntryMap::value_type(url, entry));
55 // Entry already exists. Merge the types of the new and existing entries.
56 if (!ret.second)
57 ret.first->second.add_types(entry.types());
58 else
59 cache_size_ += entry.response_size(); // New entry. Add to cache size.
60 return ret.second;
63 void AppCache::RemoveEntry(const GURL& url) {
64 EntryMap::iterator found = entries_.find(url);
65 DCHECK(found != entries_.end());
66 cache_size_ -= found->second.response_size();
67 entries_.erase(found);
70 AppCacheEntry* AppCache::GetEntry(const GURL& url) {
71 EntryMap::iterator it = entries_.find(url);
72 return (it != entries_.end()) ? &(it->second) : NULL;
75 const AppCacheEntry* AppCache::GetEntryAndUrlWithResponseId(
76 int64 response_id, GURL* optional_url_out) {
77 for (EntryMap::const_iterator iter = entries_.begin();
78 iter != entries_.end(); ++iter) {
79 if (iter->second.response_id() == response_id) {
80 if (optional_url_out)
81 *optional_url_out = iter->first;
82 return &iter->second;
85 return NULL;
88 AppCacheExecutableHandler* AppCache::GetExecutableHandler(int64 response_id) {
89 HandlerMap::const_iterator found = executable_handlers_.find(response_id);
90 if (found != executable_handlers_.end())
91 return found->second;
92 return NULL;
95 AppCacheExecutableHandler* AppCache::GetOrCreateExecutableHandler(
96 int64 response_id, net::IOBuffer* handler_source) {
97 AppCacheExecutableHandler* handler = GetExecutableHandler(response_id);
98 if (handler)
99 return handler;
101 GURL handler_url;
102 const AppCacheEntry* entry = GetEntryAndUrlWithResponseId(
103 response_id, &handler_url);
104 if (!entry || !entry->IsExecutable())
105 return NULL;
107 DCHECK(storage_->service()->handler_factory());
108 scoped_ptr<AppCacheExecutableHandler> own_ptr =
109 storage_->service()->handler_factory()->
110 CreateHandler(handler_url, handler_source);
111 handler = own_ptr.release();
112 if (!handler)
113 return NULL;
114 executable_handlers_[response_id] = handler;
115 return handler;
118 GURL AppCache::GetNamespaceEntryUrl(const AppCacheNamespaceVector& namespaces,
119 const GURL& namespace_url) const {
120 size_t count = namespaces.size();
121 for (size_t i = 0; i < count; ++i) {
122 if (namespaces[i].namespace_url == namespace_url)
123 return namespaces[i].target_url;
125 NOTREACHED();
126 return GURL();
129 namespace {
130 bool SortNamespacesByLength(
131 const AppCacheNamespace& lhs, const AppCacheNamespace& rhs) {
132 return lhs.namespace_url.spec().length() > rhs.namespace_url.spec().length();
136 void AppCache::InitializeWithManifest(AppCacheManifest* manifest) {
137 DCHECK(manifest);
138 intercept_namespaces_.swap(manifest->intercept_namespaces);
139 fallback_namespaces_.swap(manifest->fallback_namespaces);
140 online_whitelist_namespaces_.swap(manifest->online_whitelist_namespaces);
141 online_whitelist_all_ = manifest->online_whitelist_all;
143 // Sort the namespaces by url string length, longest to shortest,
144 // since longer matches trump when matching a url to a namespace.
145 std::sort(intercept_namespaces_.begin(), intercept_namespaces_.end(),
146 SortNamespacesByLength);
147 std::sort(fallback_namespaces_.begin(), fallback_namespaces_.end(),
148 SortNamespacesByLength);
151 void AppCache::InitializeWithDatabaseRecords(
152 const AppCacheDatabase::CacheRecord& cache_record,
153 const std::vector<AppCacheDatabase::EntryRecord>& entries,
154 const std::vector<AppCacheDatabase::NamespaceRecord>& intercepts,
155 const std::vector<AppCacheDatabase::NamespaceRecord>& fallbacks,
156 const std::vector<AppCacheDatabase::OnlineWhiteListRecord>& whitelists) {
157 DCHECK(cache_id_ == cache_record.cache_id);
158 online_whitelist_all_ = cache_record.online_wildcard;
159 update_time_ = cache_record.update_time;
161 for (size_t i = 0; i < entries.size(); ++i) {
162 const AppCacheDatabase::EntryRecord& entry = entries.at(i);
163 AddEntry(entry.url, AppCacheEntry(entry.flags, entry.response_id,
164 entry.response_size));
166 DCHECK(cache_size_ == cache_record.cache_size);
168 for (size_t i = 0; i < intercepts.size(); ++i)
169 intercept_namespaces_.push_back(intercepts.at(i).namespace_);
171 for (size_t i = 0; i < fallbacks.size(); ++i)
172 fallback_namespaces_.push_back(fallbacks.at(i).namespace_);
174 // Sort the fallback namespaces by url string length, longest to shortest,
175 // since longer matches trump when matching a url to a namespace.
176 std::sort(intercept_namespaces_.begin(), intercept_namespaces_.end(),
177 SortNamespacesByLength);
178 std::sort(fallback_namespaces_.begin(), fallback_namespaces_.end(),
179 SortNamespacesByLength);
181 for (size_t i = 0; i < whitelists.size(); ++i) {
182 const AppCacheDatabase::OnlineWhiteListRecord& record = whitelists.at(i);
183 online_whitelist_namespaces_.push_back(
184 AppCacheNamespace(APPCACHE_NETWORK_NAMESPACE,
185 record.namespace_url,
186 GURL(),
187 record.is_pattern));
191 void AppCache::ToDatabaseRecords(
192 const AppCacheGroup* group,
193 AppCacheDatabase::CacheRecord* cache_record,
194 std::vector<AppCacheDatabase::EntryRecord>* entries,
195 std::vector<AppCacheDatabase::NamespaceRecord>* intercepts,
196 std::vector<AppCacheDatabase::NamespaceRecord>* fallbacks,
197 std::vector<AppCacheDatabase::OnlineWhiteListRecord>* whitelists) {
198 DCHECK(group && cache_record && entries && fallbacks && whitelists);
199 DCHECK(entries->empty() && fallbacks->empty() && whitelists->empty());
201 cache_record->cache_id = cache_id_;
202 cache_record->group_id = group->group_id();
203 cache_record->online_wildcard = online_whitelist_all_;
204 cache_record->update_time = update_time_;
205 cache_record->cache_size = 0;
207 for (EntryMap::const_iterator iter = entries_.begin();
208 iter != entries_.end(); ++iter) {
209 entries->push_back(AppCacheDatabase::EntryRecord());
210 AppCacheDatabase::EntryRecord& record = entries->back();
211 record.url = iter->first;
212 record.cache_id = cache_id_;
213 record.flags = iter->second.types();
214 record.response_id = iter->second.response_id();
215 record.response_size = iter->second.response_size();
216 cache_record->cache_size += record.response_size;
219 GURL origin = group->manifest_url().GetOrigin();
221 for (size_t i = 0; i < intercept_namespaces_.size(); ++i) {
222 intercepts->push_back(AppCacheDatabase::NamespaceRecord());
223 AppCacheDatabase::NamespaceRecord& record = intercepts->back();
224 record.cache_id = cache_id_;
225 record.origin = origin;
226 record.namespace_ = intercept_namespaces_[i];
229 for (size_t i = 0; i < fallback_namespaces_.size(); ++i) {
230 fallbacks->push_back(AppCacheDatabase::NamespaceRecord());
231 AppCacheDatabase::NamespaceRecord& record = fallbacks->back();
232 record.cache_id = cache_id_;
233 record.origin = origin;
234 record.namespace_ = fallback_namespaces_[i];
237 for (size_t i = 0; i < online_whitelist_namespaces_.size(); ++i) {
238 whitelists->push_back(AppCacheDatabase::OnlineWhiteListRecord());
239 AppCacheDatabase::OnlineWhiteListRecord& record = whitelists->back();
240 record.cache_id = cache_id_;
241 record.namespace_url = online_whitelist_namespaces_[i].namespace_url;
242 record.is_pattern = online_whitelist_namespaces_[i].is_pattern;
246 bool AppCache::FindResponseForRequest(const GURL& url,
247 AppCacheEntry* found_entry, GURL* found_intercept_namespace,
248 AppCacheEntry* found_fallback_entry, GURL* found_fallback_namespace,
249 bool* found_network_namespace) {
250 // Ignore fragments when looking up URL in the cache.
251 GURL url_no_ref;
252 if (url.has_ref()) {
253 GURL::Replacements replacements;
254 replacements.ClearRef();
255 url_no_ref = url.ReplaceComponents(replacements);
256 } else {
257 url_no_ref = url;
260 // 6.6.6 Changes to the networking model
262 AppCacheEntry* entry = GetEntry(url_no_ref);
263 if (entry) {
264 *found_entry = *entry;
265 return true;
268 *found_network_namespace = IsInNetworkNamespace(url_no_ref);
269 if (*found_network_namespace)
270 return true;
272 const AppCacheNamespace* intercept_namespace =
273 FindInterceptNamespace(url_no_ref);
274 if (intercept_namespace) {
275 entry = GetEntry(intercept_namespace->target_url);
276 DCHECK(entry);
277 *found_entry = *entry;
278 *found_intercept_namespace = intercept_namespace->namespace_url;
279 return true;
282 const AppCacheNamespace* fallback_namespace =
283 FindFallbackNamespace(url_no_ref);
284 if (fallback_namespace) {
285 entry = GetEntry(fallback_namespace->target_url);
286 DCHECK(entry);
287 *found_fallback_entry = *entry;
288 *found_fallback_namespace = fallback_namespace->namespace_url;
289 return true;
292 *found_network_namespace = online_whitelist_all_;
293 return *found_network_namespace;
297 void AppCache::ToResourceInfoVector(AppCacheResourceInfoVector* infos) const {
298 DCHECK(infos && infos->empty());
299 for (EntryMap::const_iterator iter = entries_.begin();
300 iter != entries_.end(); ++iter) {
301 infos->push_back(AppCacheResourceInfo());
302 AppCacheResourceInfo& info = infos->back();
303 info.url = iter->first;
304 info.is_master = iter->second.IsMaster();
305 info.is_manifest = iter->second.IsManifest();
306 info.is_intercept = iter->second.IsIntercept();
307 info.is_fallback = iter->second.IsFallback();
308 info.is_foreign = iter->second.IsForeign();
309 info.is_explicit = iter->second.IsExplicit();
310 info.size = iter->second.response_size();
311 info.response_id = iter->second.response_id();
315 // static
316 const AppCacheNamespace* AppCache::FindNamespace(
317 const AppCacheNamespaceVector& namespaces,
318 const GURL& url) {
319 size_t count = namespaces.size();
320 for (size_t i = 0; i < count; ++i) {
321 if (namespaces[i].IsMatch(url))
322 return &namespaces[i];
324 return NULL;
327 } // namespace content