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