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/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"
16 using blink::WebApplicationCacheHost
;
17 using blink::WebApplicationCacheHostClient
;
18 using blink::WebString
;
19 using blink::WebURLRequest
;
21 using blink::WebURLResponse
;
22 using blink::WebVector
;
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_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
));
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 AppCacheInfo
& info
) {
80 client_
->didChangeCacheAssociation();
83 void WebApplicationCacheHostImpl::OnStatusChanged(
84 AppCacheStatus status
) {
85 // TODO(michaeln): delete me, not used
88 void WebApplicationCacheHostImpl::OnEventRaised(
89 AppCacheEventID event_id
) {
91 APPCACHE_PROGRESS_EVENT
); // See OnProgressEventRaised.
92 DCHECK(event_id
!= APPCACHE_ERROR_EVENT
); // See OnErrorEventRaised.
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
);
102 case APPCACHE_CHECKING_EVENT
:
103 status_
= APPCACHE_STATUS_CHECKING
;
105 case APPCACHE_DOWNLOADING_EVENT
:
106 status_
= APPCACHE_STATUS_DOWNLOADING
;
108 case APPCACHE_UPDATE_READY_EVENT
:
109 status_
= APPCACHE_STATUS_UPDATE_READY
;
111 case APPCACHE_CACHED_EVENT
:
112 case APPCACHE_NO_UPDATE_EVENT
:
113 status_
= APPCACHE_STATUS_IDLE
;
115 case APPCACHE_OBSOLETE_EVENT
:
116 status_
= APPCACHE_STATUS_OBSOLETE
;
123 client_
->notifyEventListener(static_cast<EventID
>(event_id
));
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
);
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
);
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());
155 client_
->notifyErrorEventListener(static_cast<ErrorReason
>(details
.reason
),
158 WebString::fromUTF8(details
.message
));
162 void WebApplicationCacheHostImpl::willStartMainResourceRequest(
163 WebURLRequest
& request
, const WebApplicationCacheHost
* spawning_host
) {
164 request
.setAppCacheHostID(host_id_
);
166 original_main_resource_url_
= ClearUrlRef(request
.url());
168 std::string method
= request
.httpMethod().utf8();
169 is_get_method_
= (method
== kHttpGETMethod
);
170 DCHECK(method
== StringToUpperASCII(method
));
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());
180 void WebApplicationCacheHostImpl::willStartSubResourceRequest(
181 WebURLRequest
& request
) {
182 request
.setAppCacheHostID(host_id_
);
185 void WebApplicationCacheHostImpl::selectCacheWithoutManifest() {
186 if (was_select_cache_called_
)
188 was_select_cache_called_
= true;
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(),
198 bool WebApplicationCacheHostImpl::selectCacheWithManifest(
199 const WebURL
& manifest_url
) {
200 if (was_select_cache_called_
)
202 was_select_cache_called_
= true;
204 GURL
manifest_gurl(ClearUrlRef(manifest_url
));
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
;
214 status_
= APPCACHE_STATUS_UNCACHED
;
215 is_new_master_entry_
= NO
;
216 manifest_gurl
= GURL();
218 backend_
->SelectCache(
219 host_id_
, document_url_
, kAppCacheNoCacheId
, manifest_gurl
);
223 DCHECK_EQ(NO
, is_new_master_entry_
);
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
235 status_
= APPCACHE_STATUS_CHECKING
;
237 // Its a 'master' entry thats already in the cache.
238 backend_
->SelectCache(host_id_
, document_url_
,
239 document_response_
.appCacheID(),
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();
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
;
258 void WebApplicationCacheHostImpl::didReceiveDataForMainResource(
259 const char* data
, unsigned len
) {
260 if (is_new_master_entry_
== NO
)
262 // TODO(michaeln): write me
265 void WebApplicationCacheHostImpl::didFinishLoadingMainResource(bool success
) {
266 if (is_new_master_entry_
== NO
)
268 // TODO(michaeln): write me
271 WebApplicationCacheHost::Status
WebApplicationCacheHostImpl::status() {
272 return static_cast<WebApplicationCacheHost::Status
>(status_
);
275 bool WebApplicationCacheHostImpl::startUpdate() {
276 if (!backend_
->StartUpdate(host_id_
))
278 if (status_
== APPCACHE_STATUS_IDLE
||
279 status_
== APPCACHE_STATUS_UPDATE_READY
)
280 status_
= APPCACHE_STATUS_CHECKING
;
282 status_
= backend_
->GetStatus(host_id_
);
286 bool WebApplicationCacheHostImpl::swapCache() {
287 if (!backend_
->SwapCache(host_id_
))
289 status_
= backend_
->GetStatus(host_id_
);
293 void WebApplicationCacheHostImpl::getAssociatedCacheInfo(
294 WebApplicationCacheHost::CacheInfo
* info
) {
295 info
->manifestURL
= cache_info_
.manifest_url
;
296 if (!cache_info_
.is_complete
)
298 info
->creationTime
= cache_info_
.creation_time
.ToDoubleT();
299 info
->updateTime
= cache_info_
.last_update_time
.ToDoubleT();
300 info
->totalSize
= cache_info_
.size
;
303 void WebApplicationCacheHostImpl::getResourceList(
304 WebVector
<ResourceInfo
>* resources
) {
305 if (!cache_info_
.is_complete
)
307 std::vector
<AppCacheResourceInfo
> resource_infos
;
308 backend_
->GetResourceList(host_id_
, &resource_infos
);
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
;
320 resources
->swap(web_resources
);
323 } // namespace content