Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / content / browser / appcache / appcache.cc
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.
4
5 #include "content/browser/appcache/appcache.h"
6
7 #include <algorithm>
8
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"
16
17 namespace content {
18
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);
27 }
28
29 AppCache::~AppCache() {
30   DCHECK(associated_hosts_.empty());
31   if (owning_group_.get()) {
32     DCHECK(is_complete_);
33     owning_group_->RemoveCache(this);
34   }
35   DCHECK(!owning_group_.get());
36   storage_->working_set()->RemoveCache(this);
37   STLDeleteContainerPairSecondPointers(
38       executable_handlers_.begin(), executable_handlers_.end());
39 }
40
41 void AppCache::UnassociateHost(AppCacheHost* host) {
42   associated_hosts_.erase(host);
43 }
44
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();
49 }
50
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));
54
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;
61 }
62
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);
68 }
69
70 AppCacheEntry* AppCache::GetEntry(const GURL& url) {
71   EntryMap::iterator it = entries_.find(url);
72   return (it != entries_.end()) ? &(it->second) : NULL;
73 }
74
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;
83     }
84   }
85   return NULL;
86 }
87
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;
93 }
94
95 AppCacheExecutableHandler* AppCache::GetOrCreateExecutableHandler(
96     int64 response_id, net::IOBuffer* handler_source) {
97   AppCacheExecutableHandler* handler = GetExecutableHandler(response_id);
98   if (handler)
99     return handler;
100
101   GURL handler_url;
102   const AppCacheEntry* entry = GetEntryAndUrlWithResponseId(
103       response_id, &handler_url);
104   if (!entry || !entry->IsExecutable())
105     return NULL;
106
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;
116 }
117
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;
124   }
125   NOTREACHED();
126   return GURL();
127 }
128
129 namespace {
130 bool SortNamespacesByLength(
131     const AppCacheNamespace& lhs, const AppCacheNamespace& rhs) {
132   return lhs.namespace_url.spec().length() > rhs.namespace_url.spec().length();
133 }
134 }
135
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;
142
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);
149 }
150
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;
160
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));
165   }
166   DCHECK(cache_size_ == cache_record.cache_size);
167
168   for (size_t i = 0; i < intercepts.size(); ++i)
169     intercept_namespaces_.push_back(intercepts.at(i).namespace_);
170
171   for (size_t i = 0; i < fallbacks.size(); ++i)
172     fallback_namespaces_.push_back(fallbacks.at(i).namespace_);
173
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);
180
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));
188   }
189 }
190
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());
200
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;
206
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;
217   }
218
219   GURL origin = group->manifest_url().GetOrigin();
220
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];
227   }
228
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];
235   }
236
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;
243   }
244 }
245
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;
258   }
259
260   // 6.6.6 Changes to the networking model
261
262   AppCacheEntry* entry = GetEntry(url_no_ref);
263   if (entry) {
264     *found_entry = *entry;
265     return true;
266   }
267
268   *found_network_namespace = IsInNetworkNamespace(url_no_ref);
269   if (*found_network_namespace)
270     return true;
271
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;
280   }
281
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;
290   }
291
292   *found_network_namespace = online_whitelist_all_;
293   return *found_network_namespace;
294 }
295
296
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();
312   }
313 }
314
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];
323   }
324   return NULL;
325 }
326
327 }  // namespace content