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.
5 #include "content/child/appcache/web_application_cache_host_impl.h"
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"
17 using blink::WebApplicationCacheHost;
18 using blink::WebApplicationCacheHostClient;
19 using blink::WebDataSource;
20 using blink::WebFrame;
21 using blink::WebURLRequest;
23 using blink::WebURLResponse;
24 using blink::WebVector;
25 using appcache::AppCacheBackend;
26 using appcache::AppCacheResourceInfo;
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"
39 typedef IDMap<WebApplicationCacheHostImpl> HostsMap;
41 HostsMap* all_hosts() {
42 static HostsMap* map = new HostsMap;
46 GURL ClearUrlRef(const GURL& url) {
49 GURL::Replacements replacements;
50 replacements.ClearRef();
51 return url.ReplaceComponents(replacements);
56 WebApplicationCacheHostImpl* WebApplicationCacheHostImpl::FromId(int id) {
57 return all_hosts()->Lookup(id);
60 WebApplicationCacheHostImpl* WebApplicationCacheHostImpl::FromFrame(
61 const WebFrame* frame) {
64 WebDataSource* data_source = frame->dataSource();
67 return static_cast<WebApplicationCacheHostImpl*>
68 (data_source->applicationCacheHost());
71 WebApplicationCacheHostImpl::WebApplicationCacheHostImpl(
72 WebApplicationCacheHostClient* client,
73 AppCacheBackend* 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));
84 backend_->RegisterHost(host_id_);
87 WebApplicationCacheHostImpl::~WebApplicationCacheHostImpl() {
88 backend_->UnregisterHost(host_id_);
89 all_hosts()->Remove(host_id_);
92 void WebApplicationCacheHostImpl::OnCacheSelected(
93 const appcache::AppCacheInfo& info) {
95 client_->didChangeCacheAssociation();
98 void WebApplicationCacheHostImpl::OnStatusChanged(appcache::Status status) {
99 // TODO(michaeln): delete me, not used
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.
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);
114 case appcache::CHECKING_EVENT:
115 status_ = appcache::CHECKING;
117 case appcache::DOWNLOADING_EVENT:
118 status_ = appcache::DOWNLOADING;
120 case appcache::UPDATE_READY_EVENT:
121 status_ = appcache::UPDATE_READY;
123 case appcache::CACHED_EVENT:
124 case appcache::NO_UPDATE_EVENT:
125 status_ = appcache::IDLE;
127 case appcache::OBSOLETE_EVENT:
128 status_ = appcache::OBSOLETE;
135 client_->notifyEventListener(static_cast<EventID>(event_id));
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);
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,
157 OnLogMessage(appcache::LOG_ERROR, full_message);
159 status_ = cache_info_.is_complete ? appcache::IDLE : appcache::UNCACHED;
160 client_->notifyEventListener(static_cast<EventID>(appcache::ERROR_EVENT));
163 void WebApplicationCacheHostImpl::willStartMainResourceRequest(
164 WebURLRequest& request, const WebFrame* frame) {
165 WebApplicationCacheHostImpl* spawning_host = NULL;
167 const WebFrame* spawning_frame = frame->parent();
169 spawning_frame = frame->opener();
171 spawning_frame = frame;
173 spawning_host = FromFrame(spawning_frame);
175 willStartMainResourceRequest(request, spawning_host);
178 void WebApplicationCacheHostImpl::willStartMainResourceRequest(
179 WebURLRequest& request, const WebApplicationCacheHost* spawning_host) {
180 request.setAppCacheHostID(host_id_);
182 original_main_resource_url_ = ClearUrlRef(request.url());
184 std::string method = request.httpMethod().utf8();
185 is_get_method_ = (method == appcache::kHttpGETMethod);
186 DCHECK(method == StringToUpperASCII(method));
188 const WebApplicationCacheHostImpl* spawning_host_impl =
189 static_cast<const WebApplicationCacheHostImpl*>(spawning_host);
190 if (spawning_host_impl && (spawning_host_impl != this) &&
191 (spawning_host_impl->status_ != appcache::UNCACHED)) {
192 backend_->SetSpawningHostId(host_id_, spawning_host_impl->host_id());
196 void WebApplicationCacheHostImpl::willStartSubResourceRequest(
197 WebURLRequest& request) {
198 request.setAppCacheHostID(host_id_);
201 void WebApplicationCacheHostImpl::selectCacheWithoutManifest() {
202 if (was_select_cache_called_)
204 was_select_cache_called_ = true;
206 status_ = (document_response_.appCacheID() == appcache::kNoCacheId) ?
207 appcache::UNCACHED : appcache::CHECKING;
208 is_new_master_entry_ = NO;
209 backend_->SelectCache(host_id_, document_url_,
210 document_response_.appCacheID(),
214 bool WebApplicationCacheHostImpl::selectCacheWithManifest(
215 const WebURL& manifest_url) {
216 if (was_select_cache_called_)
218 was_select_cache_called_ = true;
220 GURL manifest_gurl(ClearUrlRef(manifest_url));
222 // 6.9.6 The application cache selection algorithm
223 // Check for new 'master' entries.
224 if (document_response_.appCacheID() == appcache::kNoCacheId) {
225 if (is_scheme_supported_ && is_get_method_ &&
226 (manifest_gurl.GetOrigin() == document_url_.GetOrigin())) {
227 status_ = appcache::CHECKING;
228 is_new_master_entry_ = YES;
230 status_ = appcache::UNCACHED;
231 is_new_master_entry_ = NO;
232 manifest_gurl = GURL();
234 backend_->SelectCache(
235 host_id_, document_url_, appcache::kNoCacheId, manifest_gurl);
239 DCHECK_EQ(NO, is_new_master_entry_);
241 // 6.9.6 The application cache selection algorithm
242 // Check for 'foreign' entries.
243 GURL document_manifest_gurl(document_response_.appCacheManifestURL());
244 if (document_manifest_gurl != manifest_gurl) {
245 backend_->MarkAsForeignEntry(host_id_, document_url_,
246 document_response_.appCacheID());
247 status_ = appcache::UNCACHED;
248 return false; // the navigation will be restarted
251 status_ = appcache::CHECKING;
253 // Its a 'master' entry thats already in the cache.
254 backend_->SelectCache(host_id_, document_url_,
255 document_response_.appCacheID(),
260 void WebApplicationCacheHostImpl::didReceiveResponseForMainResource(
261 const WebURLResponse& response) {
262 document_response_ = response;
263 document_url_ = ClearUrlRef(document_response_.url());
264 if (document_url_ != original_main_resource_url_)
265 is_get_method_ = true; // A redirect was involved.
266 original_main_resource_url_ = GURL();
268 is_scheme_supported_ = appcache::IsSchemeSupported(document_url_);
269 if ((document_response_.appCacheID() != appcache::kNoCacheId) ||
270 !is_scheme_supported_ || !is_get_method_)
271 is_new_master_entry_ = NO;
274 void WebApplicationCacheHostImpl::didReceiveDataForMainResource(
275 const char* data, int len) {
276 if (is_new_master_entry_ == NO)
278 // TODO(michaeln): write me
281 void WebApplicationCacheHostImpl::didFinishLoadingMainResource(bool success) {
282 if (is_new_master_entry_ == NO)
284 // TODO(michaeln): write me
287 WebApplicationCacheHost::Status WebApplicationCacheHostImpl::status() {
288 return static_cast<WebApplicationCacheHost::Status>(status_);
291 bool WebApplicationCacheHostImpl::startUpdate() {
292 if (!backend_->StartUpdate(host_id_))
294 if (status_ == appcache::IDLE || status_ == appcache::UPDATE_READY)
295 status_ = appcache::CHECKING;
297 status_ = backend_->GetStatus(host_id_);
301 bool WebApplicationCacheHostImpl::swapCache() {
302 if (!backend_->SwapCache(host_id_))
304 status_ = backend_->GetStatus(host_id_);
308 void WebApplicationCacheHostImpl::getAssociatedCacheInfo(
309 WebApplicationCacheHost::CacheInfo* info) {
310 info->manifestURL = cache_info_.manifest_url;
311 if (!cache_info_.is_complete)
313 info->creationTime = cache_info_.creation_time.ToDoubleT();
314 info->updateTime = cache_info_.last_update_time.ToDoubleT();
315 info->totalSize = cache_info_.size;
318 void WebApplicationCacheHostImpl::getResourceList(
319 WebVector<ResourceInfo>* resources) {
320 if (!cache_info_.is_complete)
322 std::vector<AppCacheResourceInfo> resource_infos;
323 backend_->GetResourceList(host_id_, &resource_infos);
325 WebVector<ResourceInfo> web_resources(resource_infos.size());
326 for (size_t i = 0; i < resource_infos.size(); ++i) {
327 web_resources[i].size = resource_infos[i].size;
328 web_resources[i].isMaster = resource_infos[i].is_master;
329 web_resources[i].isExplicit = resource_infos[i].is_explicit;
330 web_resources[i].isManifest = resource_infos[i].is_manifest;
331 web_resources[i].isForeign = resource_infos[i].is_foreign;
332 web_resources[i].isFallback = resource_infos[i].is_fallback;
333 web_resources[i].url = resource_infos[i].url;
335 resources->swap(web_resources);
338 } // namespace content