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 "webkit/browser/appcache/appcache.h" 6 7#include <algorithm> 8 9#include "base/logging.h" 10#include "base/stl_util.h" 11#include "webkit/browser/appcache/appcache_executable_handler.h" 12#include "webkit/browser/appcache/appcache_group.h" 13#include "webkit/browser/appcache/appcache_host.h" 14#include "webkit/browser/appcache/appcache_storage.h" 15#include "webkit/common/appcache/appcache_interfaces.h" 16 17namespace appcache { 18 19AppCache::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 29AppCache::~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 41void AppCache::UnassociateHost(AppCacheHost* host) { 42 associated_hosts_.erase(host); 43} 44 45void 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 51bool 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 63void 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 70AppCacheEntry* AppCache::GetEntry(const GURL& url) { 71 EntryMap::iterator it = entries_.find(url); 72 return (it != entries_.end()) ? &(it->second) : NULL; 73} 74 75const 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 88AppCacheExecutableHandler* 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 95AppCacheExecutableHandler* 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 118GURL AppCache::GetNamespaceEntryUrl(const NamespaceVector& 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 129namespace { 130bool SortNamespacesByLength( 131 const Namespace& lhs, const Namespace& rhs) { 132 return lhs.namespace_url.spec().length() > rhs.namespace_url.spec().length(); 133} 134} 135 136void AppCache::InitializeWithManifest(Manifest* 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 151void 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 Namespace(APPCACHE_NETWORK_NAMESPACE, 185 record.namespace_url, 186 GURL(), 187 record.is_pattern)); 188 } 189} 190 191void 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 246bool 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 if ((*found_network_namespace = IsInNetworkNamespace(url_no_ref))) 269 return true; 270 271 const Namespace* intercept_namespace = FindInterceptNamespace(url_no_ref); 272 if (intercept_namespace) { 273 entry = GetEntry(intercept_namespace->target_url); 274 DCHECK(entry); 275 *found_entry = *entry; 276 *found_intercept_namespace = intercept_namespace->namespace_url; 277 return true; 278 } 279 280 const Namespace* fallback_namespace = FindFallbackNamespace(url_no_ref); 281 if (fallback_namespace) { 282 entry = GetEntry(fallback_namespace->target_url); 283 DCHECK(entry); 284 *found_fallback_entry = *entry; 285 *found_fallback_namespace = fallback_namespace->namespace_url; 286 return true; 287 } 288 289 *found_network_namespace = online_whitelist_all_; 290 return *found_network_namespace; 291} 292 293 294void AppCache::ToResourceInfoVector(AppCacheResourceInfoVector* infos) const { 295 DCHECK(infos && infos->empty()); 296 for (EntryMap::const_iterator iter = entries_.begin(); 297 iter != entries_.end(); ++iter) { 298 infos->push_back(AppCacheResourceInfo()); 299 AppCacheResourceInfo& info = infos->back(); 300 info.url = iter->first; 301 info.is_master = iter->second.IsMaster(); 302 info.is_manifest = iter->second.IsManifest(); 303 info.is_intercept = iter->second.IsIntercept(); 304 info.is_fallback = iter->second.IsFallback(); 305 info.is_foreign = iter->second.IsForeign(); 306 info.is_explicit = iter->second.IsExplicit(); 307 info.size = iter->second.response_size(); 308 info.response_id = iter->second.response_id(); 309 } 310} 311 312// static 313const Namespace* AppCache::FindNamespace( 314 const NamespaceVector& namespaces, 315 const GURL& url) { 316 size_t count = namespaces.size(); 317 for (size_t i = 0; i < count; ++i) { 318 if (namespaces[i].IsMatch(url)) 319 return &namespaces[i]; 320 } 321 return NULL; 322} 323 324} // namespace appcache 325