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