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 WebKit::WebApplicationCacheHost;
18 using WebKit::WebApplicationCacheHostClient;
19 using WebKit::WebDataSource;
20 using WebKit::WebFrame;
21 using WebKit::WebURLRequest;
23 using WebKit::WebURLResponse;
24 using WebKit::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 request.setAppCacheHostID(host_id_);
167 original_main_resource_url_ = ClearUrlRef(request.url());
169 std::string method = request.httpMethod().utf8();
170 is_get_method_ = (method == appcache::kHttpGETMethod);
171 DCHECK(method == StringToUpperASCII(method));
174 const WebFrame* spawning_frame = frame->parent();
176 spawning_frame = frame->opener();
178 spawning_frame = frame;
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());
188 void WebApplicationCacheHostImpl::willStartSubResourceRequest(
189 WebURLRequest& request) {
190 request.setAppCacheHostID(host_id_);
193 void WebApplicationCacheHostImpl::selectCacheWithoutManifest() {
194 if (was_select_cache_called_)
196 was_select_cache_called_ = true;
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(),
206 bool WebApplicationCacheHostImpl::selectCacheWithManifest(
207 const WebURL& manifest_url) {
208 if (was_select_cache_called_)
210 was_select_cache_called_ = true;
212 GURL manifest_gurl(ClearUrlRef(manifest_url));
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;
222 status_ = appcache::UNCACHED;
223 is_new_master_entry_ = NO;
224 manifest_gurl = GURL();
226 backend_->SelectCache(
227 host_id_, document_url_, appcache::kNoCacheId, manifest_gurl);
231 DCHECK_EQ(NO, is_new_master_entry_);
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
243 status_ = appcache::CHECKING;
245 // Its a 'master' entry thats already in the cache.
246 backend_->SelectCache(host_id_, document_url_,
247 document_response_.appCacheID(),
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();
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;
266 void WebApplicationCacheHostImpl::didReceiveDataForMainResource(
267 const char* data, int len) {
268 if (is_new_master_entry_ == NO)
270 // TODO(michaeln): write me
273 void WebApplicationCacheHostImpl::didFinishLoadingMainResource(bool success) {
274 if (is_new_master_entry_ == NO)
276 // TODO(michaeln): write me
279 WebApplicationCacheHost::Status WebApplicationCacheHostImpl::status() {
280 return static_cast<WebApplicationCacheHost::Status>(status_);
283 bool WebApplicationCacheHostImpl::startUpdate() {
284 if (!backend_->StartUpdate(host_id_))
286 if (status_ == appcache::IDLE || status_ == appcache::UPDATE_READY)
287 status_ = appcache::CHECKING;
289 status_ = backend_->GetStatus(host_id_);
293 bool WebApplicationCacheHostImpl::swapCache() {
294 if (!backend_->SwapCache(host_id_))
296 status_ = backend_->GetStatus(host_id_);
300 void WebApplicationCacheHostImpl::getAssociatedCacheInfo(
301 WebApplicationCacheHost::CacheInfo* info) {
302 info->manifestURL = cache_info_.manifest_url;
303 if (!cache_info_.is_complete)
305 info->creationTime = cache_info_.creation_time.ToDoubleT();
306 info->updateTime = cache_info_.last_update_time.ToDoubleT();
307 info->totalSize = cache_info_.size;
310 void WebApplicationCacheHostImpl::getResourceList(
311 WebVector<ResourceInfo>* resources) {
312 if (!cache_info_.is_complete)
314 std::vector<AppCacheResourceInfo> resource_infos;
315 backend_->GetResourceList(host_id_, &resource_infos);
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;
327 resources->swap(web_resources);
330 } // namespace content