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 17using blink::WebApplicationCacheHost; 18using blink::WebApplicationCacheHostClient; 19using blink::WebDataSource; 20using blink::WebFrame; 21using blink::WebURLRequest; 22using blink::WebURL; 23using blink::WebURLResponse; 24using blink::WebVector; 25using appcache::AppCacheBackend; 26using appcache::AppCacheResourceInfo; 27 28namespace content { 29 30namespace { 31 32// Note: the order of the elements in this array must match those 33// of the EventID enum in appcache_interfaces.h. 34const char* kEventNames[] = { 35 "Checking", "Error", "NoUpdate", "Downloading", "Progress", 36 "UpdateReady", "Cached", "Obsolete" 37}; 38 39typedef IDMap<WebApplicationCacheHostImpl> HostsMap; 40 41HostsMap* all_hosts() { 42 static HostsMap* map = new HostsMap; 43 return map; 44} 45 46GURL 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 56WebApplicationCacheHostImpl* WebApplicationCacheHostImpl::FromId(int id) { 57 return all_hosts()->Lookup(id); 58} 59 60WebApplicationCacheHostImpl* 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 71WebApplicationCacheHostImpl::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 87WebApplicationCacheHostImpl::~WebApplicationCacheHostImpl() { 88 backend_->UnregisterHost(host_id_); 89 all_hosts()->Remove(host_id_); 90} 91 92void WebApplicationCacheHostImpl::OnCacheSelected( 93 const appcache::AppCacheInfo& info) { 94 cache_info_ = info; 95 client_->didChangeCacheAssociation(); 96} 97 98void WebApplicationCacheHostImpl::OnStatusChanged(appcache::Status status) { 99 // TODO(michaeln): delete me, not used 100} 101 102void 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 138void 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 150void 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 163void 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 188void WebApplicationCacheHostImpl::willStartSubResourceRequest( 189 WebURLRequest& request) { 190 request.setAppCacheHostID(host_id_); 191} 192 193void 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 206bool 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 252void 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 266void WebApplicationCacheHostImpl::didReceiveDataForMainResource( 267 const char* data, int len) { 268 if (is_new_master_entry_ == NO) 269 return; 270 // TODO(michaeln): write me 271} 272 273void WebApplicationCacheHostImpl::didFinishLoadingMainResource(bool success) { 274 if (is_new_master_entry_ == NO) 275 return; 276 // TODO(michaeln): write me 277} 278 279WebApplicationCacheHost::Status WebApplicationCacheHostImpl::status() { 280 return static_cast<WebApplicationCacheHost::Status>(status_); 281} 282 283bool 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 293bool WebApplicationCacheHostImpl::swapCache() { 294 if (!backend_->SwapCache(host_id_)) 295 return false; 296 status_ = backend_->GetStatus(host_id_); 297 return true; 298} 299 300void 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 310void 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