Home | History | Annotate | Download | only in appcache
      1 // Copyright 2013 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/child/appcache/web_application_cache_host_impl.h"
      6 
      7 #include "base/compiler_specific.h"
      8 #include "base/id_map.h"
      9 #include "base/strings/string_util.h"
     10 #include "base/strings/stringprintf.h"
     11 #include "third_party/WebKit/public/platform/WebURL.h"
     12 #include "third_party/WebKit/public/platform/WebURLRequest.h"
     13 #include "third_party/WebKit/public/platform/WebURLResponse.h"
     14 #include "third_party/WebKit/public/web/WebDataSource.h"
     15 #include "third_party/WebKit/public/web/WebFrame.h"
     16 
     17 using WebKit::WebApplicationCacheHost;
     18 using WebKit::WebApplicationCacheHostClient;
     19 using WebKit::WebDataSource;
     20 using WebKit::WebFrame;
     21 using WebKit::WebURLRequest;
     22 using WebKit::WebURL;
     23 using WebKit::WebURLResponse;
     24 using WebKit::WebVector;
     25 using appcache::AppCacheBackend;
     26 using appcache::AppCacheResourceInfo;
     27 
     28 namespace content {
     29 
     30 namespace {
     31 
     32 // Note: the order of the elements in this array must match those
     33 // of the EventID enum in appcache_interfaces.h.
     34 const char* kEventNames[] = {
     35   "Checking", "Error", "NoUpdate", "Downloading", "Progress",
     36   "UpdateReady", "Cached", "Obsolete"
     37 };
     38 
     39 typedef IDMap<WebApplicationCacheHostImpl> HostsMap;
     40 
     41 HostsMap* all_hosts() {
     42   static HostsMap* map = new HostsMap;
     43   return map;
     44 }
     45 
     46 GURL ClearUrlRef(const GURL& url) {
     47   if (!url.has_ref())
     48     return url;
     49   GURL::Replacements replacements;
     50   replacements.ClearRef();
     51   return url.ReplaceComponents(replacements);
     52 }
     53 
     54 }  // anon namespace
     55 
     56 WebApplicationCacheHostImpl* WebApplicationCacheHostImpl::FromId(int id) {
     57   return all_hosts()->Lookup(id);
     58 }
     59 
     60 WebApplicationCacheHostImpl* WebApplicationCacheHostImpl::FromFrame(
     61     const WebFrame* frame) {
     62   if (!frame)
     63     return NULL;
     64   WebDataSource* data_source = frame->dataSource();
     65   if (!data_source)
     66     return NULL;
     67   return static_cast<WebApplicationCacheHostImpl*>
     68       (data_source->applicationCacheHost());
     69 }
     70 
     71 WebApplicationCacheHostImpl::WebApplicationCacheHostImpl(
     72     WebApplicationCacheHostClient* client,
     73     AppCacheBackend* backend)
     74     : client_(client),
     75       backend_(backend),
     76       host_id_(all_hosts()->Add(this)),
     77       status_(appcache::UNCACHED),
     78       is_scheme_supported_(false),
     79       is_get_method_(false),
     80       is_new_master_entry_(MAYBE),
     81       was_select_cache_called_(false) {
     82   DCHECK(client && backend && (host_id_ != appcache::kNoHostId));
     83 
     84   backend_->RegisterHost(host_id_);
     85 }
     86 
     87 WebApplicationCacheHostImpl::~WebApplicationCacheHostImpl() {
     88   backend_->UnregisterHost(host_id_);
     89   all_hosts()->Remove(host_id_);
     90 }
     91 
     92 void WebApplicationCacheHostImpl::OnCacheSelected(
     93     const appcache::AppCacheInfo& info) {
     94   cache_info_ = info;
     95   client_->didChangeCacheAssociation();
     96 }
     97 
     98 void WebApplicationCacheHostImpl::OnStatusChanged(appcache::Status status) {
     99   // TODO(michaeln): delete me, not used
    100 }
    101 
    102 void WebApplicationCacheHostImpl::OnEventRaised(appcache::EventID event_id) {
    103   DCHECK(event_id != appcache::PROGRESS_EVENT);  // See OnProgressEventRaised.
    104   DCHECK(event_id != appcache::ERROR_EVENT);  // See OnErrorEventRaised.
    105 
    106   // Emit logging output prior to calling out to script as we can get
    107   // deleted within the script event handler.
    108   const char* kFormatString = "Application Cache %s event";
    109   std::string message = base::StringPrintf(kFormatString,
    110                                            kEventNames[event_id]);
    111   OnLogMessage(appcache::LOG_INFO, message);
    112 
    113   switch (event_id) {
    114     case appcache::CHECKING_EVENT:
    115       status_ = appcache::CHECKING;
    116       break;
    117     case appcache::DOWNLOADING_EVENT:
    118       status_ = appcache::DOWNLOADING;
    119       break;
    120     case appcache::UPDATE_READY_EVENT:
    121       status_ = appcache::UPDATE_READY;
    122       break;
    123     case appcache::CACHED_EVENT:
    124     case appcache::NO_UPDATE_EVENT:
    125       status_ = appcache::IDLE;
    126       break;
    127     case appcache::OBSOLETE_EVENT:
    128       status_ = appcache::OBSOLETE;
    129       break;
    130     default:
    131       NOTREACHED();
    132       break;
    133   }
    134 
    135   client_->notifyEventListener(static_cast<EventID>(event_id));
    136 }
    137 
    138 void WebApplicationCacheHostImpl::OnProgressEventRaised(
    139     const GURL& url, int num_total, int num_complete) {
    140   // Emit logging output prior to calling out to script as we can get
    141   // deleted within the script event handler.
    142   const char* kFormatString = "Application Cache Progress event (%d of %d) %s";
    143   std::string message = base::StringPrintf(kFormatString, num_complete,
    144                                            num_total, url.spec().c_str());
    145   OnLogMessage(appcache::LOG_INFO, message);
    146   status_ = appcache::DOWNLOADING;
    147   client_->notifyProgressEventListener(url, num_total, num_complete);
    148 }
    149 
    150 void WebApplicationCacheHostImpl::OnErrorEventRaised(
    151     const std::string& message) {
    152   // Emit logging output prior to calling out to script as we can get
    153   // deleted within the script event handler.
    154   const char* kFormatString = "Application Cache Error event: %s";
    155   std::string full_message = base::StringPrintf(kFormatString,
    156                                                 message.c_str());
    157   OnLogMessage(appcache::LOG_ERROR, full_message);
    158 
    159   status_ = cache_info_.is_complete ? appcache::IDLE : appcache::UNCACHED;
    160   client_->notifyEventListener(static_cast<EventID>(appcache::ERROR_EVENT));
    161 }
    162 
    163 void WebApplicationCacheHostImpl::willStartMainResourceRequest(
    164     WebURLRequest& request, const WebFrame* frame) {
    165   request.setAppCacheHostID(host_id_);
    166 
    167   original_main_resource_url_ = ClearUrlRef(request.url());
    168 
    169   std::string method = request.httpMethod().utf8();
    170   is_get_method_ = (method == appcache::kHttpGETMethod);
    171   DCHECK(method == StringToUpperASCII(method));
    172 
    173   if (frame) {
    174     const WebFrame* spawning_frame = frame->parent();
    175     if (!spawning_frame)
    176       spawning_frame = frame->opener();
    177     if (!spawning_frame)
    178       spawning_frame = frame;
    179 
    180     WebApplicationCacheHostImpl* spawning_host = FromFrame(spawning_frame);
    181     if (spawning_host && (spawning_host != this) &&
    182         (spawning_host->status_ != appcache::UNCACHED)) {
    183       backend_->SetSpawningHostId(host_id_, spawning_host->host_id());
    184     }
    185   }
    186 }
    187 
    188 void WebApplicationCacheHostImpl::willStartSubResourceRequest(
    189     WebURLRequest& request) {
    190   request.setAppCacheHostID(host_id_);
    191 }
    192 
    193 void WebApplicationCacheHostImpl::selectCacheWithoutManifest() {
    194   if (was_select_cache_called_)
    195     return;
    196   was_select_cache_called_ = true;
    197 
    198   status_ = (document_response_.appCacheID() == appcache::kNoCacheId) ?
    199       appcache::UNCACHED : appcache::CHECKING;
    200   is_new_master_entry_ = NO;
    201   backend_->SelectCache(host_id_, document_url_,
    202                         document_response_.appCacheID(),
    203                         GURL());
    204 }
    205 
    206 bool WebApplicationCacheHostImpl::selectCacheWithManifest(
    207     const WebURL& manifest_url) {
    208   if (was_select_cache_called_)
    209     return true;
    210   was_select_cache_called_ = true;
    211 
    212   GURL manifest_gurl(ClearUrlRef(manifest_url));
    213 
    214   // 6.9.6 The application cache selection algorithm
    215   // Check for new 'master' entries.
    216   if (document_response_.appCacheID() == appcache::kNoCacheId) {
    217     if (is_scheme_supported_ && is_get_method_ &&
    218         (manifest_gurl.GetOrigin() == document_url_.GetOrigin())) {
    219       status_ = appcache::CHECKING;
    220       is_new_master_entry_ = YES;
    221     } else {
    222       status_ = appcache::UNCACHED;
    223       is_new_master_entry_ = NO;
    224       manifest_gurl = GURL();
    225     }
    226     backend_->SelectCache(
    227         host_id_, document_url_, appcache::kNoCacheId, manifest_gurl);
    228     return true;
    229   }
    230 
    231   DCHECK_EQ(NO, is_new_master_entry_);
    232 
    233   // 6.9.6 The application cache selection algorithm
    234   // Check for 'foreign' entries.
    235   GURL document_manifest_gurl(document_response_.appCacheManifestURL());
    236   if (document_manifest_gurl != manifest_gurl) {
    237     backend_->MarkAsForeignEntry(host_id_, document_url_,
    238                                  document_response_.appCacheID());
    239     status_ = appcache::UNCACHED;
    240     return false;  // the navigation will be restarted
    241   }
    242 
    243   status_ = appcache::CHECKING;
    244 
    245   // Its a 'master' entry thats already in the cache.
    246   backend_->SelectCache(host_id_, document_url_,
    247                         document_response_.appCacheID(),
    248                         manifest_gurl);
    249   return true;
    250 }
    251 
    252 void WebApplicationCacheHostImpl::didReceiveResponseForMainResource(
    253     const WebURLResponse& response) {
    254   document_response_ = response;
    255   document_url_ = ClearUrlRef(document_response_.url());
    256   if (document_url_ != original_main_resource_url_)
    257     is_get_method_ = true;  // A redirect was involved.
    258   original_main_resource_url_ = GURL();
    259 
    260   is_scheme_supported_ =  appcache::IsSchemeSupported(document_url_);
    261   if ((document_response_.appCacheID() != appcache::kNoCacheId) ||
    262       !is_scheme_supported_ || !is_get_method_)
    263     is_new_master_entry_ = NO;
    264 }
    265 
    266 void WebApplicationCacheHostImpl::didReceiveDataForMainResource(
    267     const char* data, int len) {
    268   if (is_new_master_entry_ == NO)
    269     return;
    270   // TODO(michaeln): write me
    271 }
    272 
    273 void WebApplicationCacheHostImpl::didFinishLoadingMainResource(bool success) {
    274   if (is_new_master_entry_ == NO)
    275     return;
    276   // TODO(michaeln): write me
    277 }
    278 
    279 WebApplicationCacheHost::Status WebApplicationCacheHostImpl::status() {
    280   return static_cast<WebApplicationCacheHost::Status>(status_);
    281 }
    282 
    283 bool WebApplicationCacheHostImpl::startUpdate() {
    284   if (!backend_->StartUpdate(host_id_))
    285     return false;
    286   if (status_ == appcache::IDLE || status_ == appcache::UPDATE_READY)
    287     status_ = appcache::CHECKING;
    288   else
    289     status_ = backend_->GetStatus(host_id_);
    290   return true;
    291 }
    292 
    293 bool WebApplicationCacheHostImpl::swapCache() {
    294   if (!backend_->SwapCache(host_id_))
    295     return false;
    296   status_ = backend_->GetStatus(host_id_);
    297   return true;
    298 }
    299 
    300 void WebApplicationCacheHostImpl::getAssociatedCacheInfo(
    301     WebApplicationCacheHost::CacheInfo* info) {
    302   info->manifestURL = cache_info_.manifest_url;
    303   if (!cache_info_.is_complete)
    304     return;
    305   info->creationTime = cache_info_.creation_time.ToDoubleT();
    306   info->updateTime = cache_info_.last_update_time.ToDoubleT();
    307   info->totalSize = cache_info_.size;
    308 }
    309 
    310 void WebApplicationCacheHostImpl::getResourceList(
    311     WebVector<ResourceInfo>* resources) {
    312   if (!cache_info_.is_complete)
    313     return;
    314   std::vector<AppCacheResourceInfo> resource_infos;
    315   backend_->GetResourceList(host_id_, &resource_infos);
    316 
    317   WebVector<ResourceInfo> web_resources(resource_infos.size());
    318   for (size_t i = 0; i < resource_infos.size(); ++i) {
    319     web_resources[i].size = resource_infos[i].size;
    320     web_resources[i].isMaster = resource_infos[i].is_master;
    321     web_resources[i].isExplicit = resource_infos[i].is_explicit;
    322     web_resources[i].isManifest = resource_infos[i].is_manifest;
    323     web_resources[i].isForeign = resource_infos[i].is_foreign;
    324     web_resources[i].isFallback = resource_infos[i].is_fallback;
    325     web_resources[i].url = resource_infos[i].url;
    326   }
    327   resources->swap(web_resources);
    328 }
    329 
    330 }  // namespace content
    331