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