Home | History | Annotate | Download | only in appcache
      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 
     17 namespace appcache {
     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 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 
    129 namespace {
    130 bool SortNamespacesByLength(
    131     const Namespace& lhs, const Namespace& rhs) {
    132   return lhs.namespace_url.spec().length() > rhs.namespace_url.spec().length();
    133 }
    134 }
    135 
    136 void 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 
    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         Namespace(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   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 
    294 void 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
    313 const 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