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 blink::WebApplicationCacheHost;
18 using blink::WebApplicationCacheHostClient;
19 using blink::WebDataSource;
20 using blink::WebFrame;
21 using blink::WebURLRequest;
22 using blink::WebURL;
23 using blink::WebURLResponse;
24 using blink::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
all_hosts()41 HostsMap* all_hosts() {
42 static HostsMap* map = new HostsMap;
43 return map;
44 }
45
ClearUrlRef(const GURL & url)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
FromId(int id)56 WebApplicationCacheHostImpl* WebApplicationCacheHostImpl::FromId(int id) {
57 return all_hosts()->Lookup(id);
58 }
59
FromFrame(const WebFrame * frame)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
WebApplicationCacheHostImpl(WebApplicationCacheHostClient * client,AppCacheBackend * backend)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
~WebApplicationCacheHostImpl()87 WebApplicationCacheHostImpl::~WebApplicationCacheHostImpl() {
88 backend_->UnregisterHost(host_id_);
89 all_hosts()->Remove(host_id_);
90 }
91
OnCacheSelected(const appcache::AppCacheInfo & info)92 void WebApplicationCacheHostImpl::OnCacheSelected(
93 const appcache::AppCacheInfo& info) {
94 cache_info_ = info;
95 client_->didChangeCacheAssociation();
96 }
97
OnStatusChanged(appcache::Status status)98 void WebApplicationCacheHostImpl::OnStatusChanged(appcache::Status status) {
99 // TODO(michaeln): delete me, not used
100 }
101
OnEventRaised(appcache::EventID event_id)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
OnProgressEventRaised(const GURL & url,int num_total,int num_complete)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
OnErrorEventRaised(const std::string & message)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
willStartMainResourceRequest(WebURLRequest & request,const WebFrame * frame)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
willStartSubResourceRequest(WebURLRequest & request)188 void WebApplicationCacheHostImpl::willStartSubResourceRequest(
189 WebURLRequest& request) {
190 request.setAppCacheHostID(host_id_);
191 }
192
selectCacheWithoutManifest()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
selectCacheWithManifest(const WebURL & manifest_url)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
didReceiveResponseForMainResource(const WebURLResponse & response)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
didReceiveDataForMainResource(const char * data,int len)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
didFinishLoadingMainResource(bool success)273 void WebApplicationCacheHostImpl::didFinishLoadingMainResource(bool success) {
274 if (is_new_master_entry_ == NO)
275 return;
276 // TODO(michaeln): write me
277 }
278
status()279 WebApplicationCacheHost::Status WebApplicationCacheHostImpl::status() {
280 return static_cast<WebApplicationCacheHost::Status>(status_);
281 }
282
startUpdate()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
swapCache()293 bool WebApplicationCacheHostImpl::swapCache() {
294 if (!backend_->SwapCache(host_id_))
295 return false;
296 status_ = backend_->GetStatus(host_id_);
297 return true;
298 }
299
getAssociatedCacheInfo(WebApplicationCacheHost::CacheInfo * info)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
getResourceList(WebVector<ResourceInfo> * resources)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