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"
15 using blink::WebApplicationCacheHost
;
16 using blink::WebApplicationCacheHostClient
;
17 using blink::WebURLRequest
;
19 using blink::WebURLResponse
;
20 using blink::WebVector
;
21 using appcache::AppCacheBackend
;
22 using appcache::AppCacheResourceInfo
;
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"
35 typedef IDMap
<WebApplicationCacheHostImpl
> HostsMap
;
37 HostsMap
* all_hosts() {
38 static HostsMap
* map
= new HostsMap
;
42 GURL
ClearUrlRef(const GURL
& url
) {
45 GURL::Replacements replacements
;
46 replacements
.ClearRef();
47 return url
.ReplaceComponents(replacements
);
52 WebApplicationCacheHostImpl
* WebApplicationCacheHostImpl::FromId(int id
) {
53 return all_hosts()->Lookup(id
);
56 WebApplicationCacheHostImpl::WebApplicationCacheHostImpl(
57 WebApplicationCacheHostClient
* client
,
58 AppCacheBackend
* backend
)
61 host_id_(all_hosts()->Add(this)),
62 status_(appcache::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_
!= appcache::kNoHostId
));
69 backend_
->RegisterHost(host_id_
);
72 WebApplicationCacheHostImpl::~WebApplicationCacheHostImpl() {
73 backend_
->UnregisterHost(host_id_
);
74 all_hosts()->Remove(host_id_
);
77 void WebApplicationCacheHostImpl::OnCacheSelected(
78 const appcache::AppCacheInfo
& info
) {
80 client_
->didChangeCacheAssociation();
83 void WebApplicationCacheHostImpl::OnStatusChanged(appcache::Status status
) {
84 // TODO(michaeln): delete me, not used
87 void WebApplicationCacheHostImpl::OnEventRaised(appcache::EventID event_id
) {
88 DCHECK(event_id
!= appcache::PROGRESS_EVENT
); // See OnProgressEventRaised.
89 DCHECK(event_id
!= appcache::ERROR_EVENT
); // See OnErrorEventRaised.
91 // Emit logging output prior to calling out to script as we can get
92 // deleted within the script event handler.
93 const char* kFormatString
= "Application Cache %s event";
94 std::string message
= base::StringPrintf(kFormatString
,
95 kEventNames
[event_id
]);
96 OnLogMessage(appcache::LOG_INFO
, message
);
99 case appcache::CHECKING_EVENT
:
100 status_
= appcache::CHECKING
;
102 case appcache::DOWNLOADING_EVENT
:
103 status_
= appcache::DOWNLOADING
;
105 case appcache::UPDATE_READY_EVENT
:
106 status_
= appcache::UPDATE_READY
;
108 case appcache::CACHED_EVENT
:
109 case appcache::NO_UPDATE_EVENT
:
110 status_
= appcache::IDLE
;
112 case appcache::OBSOLETE_EVENT
:
113 status_
= appcache::OBSOLETE
;
120 client_
->notifyEventListener(static_cast<EventID
>(event_id
));
123 void WebApplicationCacheHostImpl::OnProgressEventRaised(
124 const GURL
& url
, int num_total
, int num_complete
) {
125 // Emit logging output prior to calling out to script as we can get
126 // deleted within the script event handler.
127 const char* kFormatString
= "Application Cache Progress event (%d of %d) %s";
128 std::string message
= base::StringPrintf(kFormatString
, num_complete
,
129 num_total
, url
.spec().c_str());
130 OnLogMessage(appcache::LOG_INFO
, message
);
131 status_
= appcache::DOWNLOADING
;
132 client_
->notifyProgressEventListener(url
, num_total
, num_complete
);
135 void WebApplicationCacheHostImpl::OnErrorEventRaised(
136 const std::string
& message
) {
137 // Emit logging output prior to calling out to script as we can get
138 // deleted within the script event handler.
139 const char* kFormatString
= "Application Cache Error event: %s";
140 std::string full_message
= base::StringPrintf(kFormatString
,
142 OnLogMessage(appcache::LOG_ERROR
, full_message
);
144 status_
= cache_info_
.is_complete
? appcache::IDLE
: appcache::UNCACHED
;
145 client_
->notifyEventListener(static_cast<EventID
>(appcache::ERROR_EVENT
));
148 void WebApplicationCacheHostImpl::willStartMainResourceRequest(
149 WebURLRequest
& request
, const WebApplicationCacheHost
* spawning_host
) {
150 request
.setAppCacheHostID(host_id_
);
152 original_main_resource_url_
= ClearUrlRef(request
.url());
154 std::string method
= request
.httpMethod().utf8();
155 is_get_method_
= (method
== appcache::kHttpGETMethod
);
156 DCHECK(method
== StringToUpperASCII(method
));
158 const WebApplicationCacheHostImpl
* spawning_host_impl
=
159 static_cast<const WebApplicationCacheHostImpl
*>(spawning_host
);
160 if (spawning_host_impl
&& (spawning_host_impl
!= this) &&
161 (spawning_host_impl
->status_
!= appcache::UNCACHED
)) {
162 backend_
->SetSpawningHostId(host_id_
, spawning_host_impl
->host_id());
166 void WebApplicationCacheHostImpl::willStartSubResourceRequest(
167 WebURLRequest
& request
) {
168 request
.setAppCacheHostID(host_id_
);
171 void WebApplicationCacheHostImpl::selectCacheWithoutManifest() {
172 if (was_select_cache_called_
)
174 was_select_cache_called_
= true;
176 status_
= (document_response_
.appCacheID() == appcache::kNoCacheId
) ?
177 appcache::UNCACHED
: appcache::CHECKING
;
178 is_new_master_entry_
= NO
;
179 backend_
->SelectCache(host_id_
, document_url_
,
180 document_response_
.appCacheID(),
184 bool WebApplicationCacheHostImpl::selectCacheWithManifest(
185 const WebURL
& manifest_url
) {
186 if (was_select_cache_called_
)
188 was_select_cache_called_
= true;
190 GURL
manifest_gurl(ClearUrlRef(manifest_url
));
192 // 6.9.6 The application cache selection algorithm
193 // Check for new 'master' entries.
194 if (document_response_
.appCacheID() == appcache::kNoCacheId
) {
195 if (is_scheme_supported_
&& is_get_method_
&&
196 (manifest_gurl
.GetOrigin() == document_url_
.GetOrigin())) {
197 status_
= appcache::CHECKING
;
198 is_new_master_entry_
= YES
;
200 status_
= appcache::UNCACHED
;
201 is_new_master_entry_
= NO
;
202 manifest_gurl
= GURL();
204 backend_
->SelectCache(
205 host_id_
, document_url_
, appcache::kNoCacheId
, manifest_gurl
);
209 DCHECK_EQ(NO
, is_new_master_entry_
);
211 // 6.9.6 The application cache selection algorithm
212 // Check for 'foreign' entries.
213 GURL
document_manifest_gurl(document_response_
.appCacheManifestURL());
214 if (document_manifest_gurl
!= manifest_gurl
) {
215 backend_
->MarkAsForeignEntry(host_id_
, document_url_
,
216 document_response_
.appCacheID());
217 status_
= appcache::UNCACHED
;
218 return false; // the navigation will be restarted
221 status_
= appcache::CHECKING
;
223 // Its a 'master' entry thats already in the cache.
224 backend_
->SelectCache(host_id_
, document_url_
,
225 document_response_
.appCacheID(),
230 void WebApplicationCacheHostImpl::didReceiveResponseForMainResource(
231 const WebURLResponse
& response
) {
232 document_response_
= response
;
233 document_url_
= ClearUrlRef(document_response_
.url());
234 if (document_url_
!= original_main_resource_url_
)
235 is_get_method_
= true; // A redirect was involved.
236 original_main_resource_url_
= GURL();
238 is_scheme_supported_
= appcache::IsSchemeSupported(document_url_
);
239 if ((document_response_
.appCacheID() != appcache::kNoCacheId
) ||
240 !is_scheme_supported_
|| !is_get_method_
)
241 is_new_master_entry_
= NO
;
244 void WebApplicationCacheHostImpl::didReceiveDataForMainResource(
245 const char* data
, int len
) {
246 if (is_new_master_entry_
== NO
)
248 // TODO(michaeln): write me
251 void WebApplicationCacheHostImpl::didFinishLoadingMainResource(bool success
) {
252 if (is_new_master_entry_
== NO
)
254 // TODO(michaeln): write me
257 WebApplicationCacheHost::Status
WebApplicationCacheHostImpl::status() {
258 return static_cast<WebApplicationCacheHost::Status
>(status_
);
261 bool WebApplicationCacheHostImpl::startUpdate() {
262 if (!backend_
->StartUpdate(host_id_
))
264 if (status_
== appcache::IDLE
|| status_
== appcache::UPDATE_READY
)
265 status_
= appcache::CHECKING
;
267 status_
= backend_
->GetStatus(host_id_
);
271 bool WebApplicationCacheHostImpl::swapCache() {
272 if (!backend_
->SwapCache(host_id_
))
274 status_
= backend_
->GetStatus(host_id_
);
278 void WebApplicationCacheHostImpl::getAssociatedCacheInfo(
279 WebApplicationCacheHost::CacheInfo
* info
) {
280 info
->manifestURL
= cache_info_
.manifest_url
;
281 if (!cache_info_
.is_complete
)
283 info
->creationTime
= cache_info_
.creation_time
.ToDoubleT();
284 info
->updateTime
= cache_info_
.last_update_time
.ToDoubleT();
285 info
->totalSize
= cache_info_
.size
;
288 void WebApplicationCacheHostImpl::getResourceList(
289 WebVector
<ResourceInfo
>* resources
) {
290 if (!cache_info_
.is_complete
)
292 std::vector
<AppCacheResourceInfo
> resource_infos
;
293 backend_
->GetResourceList(host_id_
, &resource_infos
);
295 WebVector
<ResourceInfo
> web_resources(resource_infos
.size());
296 for (size_t i
= 0; i
< resource_infos
.size(); ++i
) {
297 web_resources
[i
].size
= resource_infos
[i
].size
;
298 web_resources
[i
].isMaster
= resource_infos
[i
].is_master
;
299 web_resources
[i
].isExplicit
= resource_infos
[i
].is_explicit
;
300 web_resources
[i
].isManifest
= resource_infos
[i
].is_manifest
;
301 web_resources
[i
].isForeign
= resource_infos
[i
].is_foreign
;
302 web_resources
[i
].isFallback
= resource_infos
[i
].is_fallback
;
303 web_resources
[i
].url
= resource_infos
[i
].url
;
305 resources
->swap(web_resources
);
308 } // namespace content