Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / content / browser / appcache / appcache_request_handler.cc
blob7987a9906e8871400422e626d226a14efa762720
1 // Copyright (c) 2011 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/browser/appcache/appcache_request_handler.h"
7 #include "content/browser/appcache/appcache.h"
8 #include "content/browser/appcache/appcache_backend_impl.h"
9 #include "content/browser/appcache/appcache_policy.h"
10 #include "content/browser/appcache/appcache_url_request_job.h"
11 #include "net/url_request/url_request.h"
12 #include "net/url_request/url_request_job.h"
14 namespace content {
16 AppCacheRequestHandler::AppCacheRequestHandler(AppCacheHost* host,
17 ResourceType resource_type)
18 : host_(host),
19 resource_type_(resource_type),
20 is_waiting_for_cache_selection_(false),
21 found_group_id_(0),
22 found_cache_id_(0),
23 found_network_namespace_(false),
24 cache_entry_not_found_(false),
25 maybe_load_resource_executed_(false) {
26 DCHECK(host_);
27 host_->AddObserver(this);
30 AppCacheRequestHandler::~AppCacheRequestHandler() {
31 if (host_) {
32 storage()->CancelDelegateCallbacks(this);
33 host_->RemoveObserver(this);
37 AppCacheStorage* AppCacheRequestHandler::storage() const {
38 DCHECK(host_);
39 return host_->storage();
42 AppCacheURLRequestJob* AppCacheRequestHandler::MaybeLoadResource(
43 net::URLRequest* request, net::NetworkDelegate* network_delegate) {
44 maybe_load_resource_executed_ = true;
45 if (!host_ || !IsSchemeAndMethodSupportedForAppCache(request) ||
46 cache_entry_not_found_)
47 return NULL;
49 // This method can get called multiple times over the life
50 // of a request. The case we detect here is having scheduled
51 // delivery of a "network response" using a job setup on an
52 // earlier call thru this method. To send the request thru
53 // to the network involves restarting the request altogether,
54 // which will call thru to our interception layer again.
55 // This time thru, we return NULL so the request hits the wire.
56 if (job_.get()) {
57 DCHECK(job_->is_delivering_network_response() ||
58 job_->cache_entry_not_found());
59 if (job_->cache_entry_not_found())
60 cache_entry_not_found_ = true;
61 job_ = NULL;
62 storage()->CancelDelegateCallbacks(this);
63 return NULL;
66 // Clear out our 'found' fields since we're starting a request for a
67 // new resource, any values in those fields are no longer valid.
68 found_entry_ = AppCacheEntry();
69 found_fallback_entry_ = AppCacheEntry();
70 found_cache_id_ = kAppCacheNoCacheId;
71 found_manifest_url_ = GURL();
72 found_network_namespace_ = false;
74 if (is_main_resource())
75 MaybeLoadMainResource(request, network_delegate);
76 else
77 MaybeLoadSubResource(request, network_delegate);
79 // If its been setup to deliver a network response, we can just delete
80 // it now and return NULL instead to achieve that since it couldn't
81 // have been started yet.
82 if (job_.get() && job_->is_delivering_network_response()) {
83 DCHECK(!job_->has_been_started());
84 job_ = NULL;
87 return job_.get();
90 AppCacheURLRequestJob* AppCacheRequestHandler::MaybeLoadFallbackForRedirect(
91 net::URLRequest* request,
92 net::NetworkDelegate* network_delegate,
93 const GURL& location) {
94 if (!host_ || !IsSchemeAndMethodSupportedForAppCache(request) ||
95 cache_entry_not_found_)
96 return NULL;
97 if (is_main_resource())
98 return NULL;
99 // TODO(vabr) This is a temporary fix (see crbug/141114). We should get rid of
100 // it once a more general solution to crbug/121325 is in place.
101 if (!maybe_load_resource_executed_)
102 return NULL;
103 if (request->url().GetOrigin() == location.GetOrigin())
104 return NULL;
106 DCHECK(!job_.get()); // our jobs never generate redirects
108 if (found_fallback_entry_.has_response_id()) {
109 // 6.9.6, step 4: If this results in a redirect to another origin,
110 // get the resource of the fallback entry.
111 job_ = new AppCacheURLRequestJob(request, network_delegate,
112 storage(), host_, is_main_resource());
113 DeliverAppCachedResponse(
114 found_fallback_entry_, found_cache_id_, found_group_id_,
115 found_manifest_url_, true, found_namespace_entry_url_);
116 } else if (!found_network_namespace_) {
117 // 6.9.6, step 6: Fail the resource load.
118 job_ = new AppCacheURLRequestJob(request, network_delegate,
119 storage(), host_, is_main_resource());
120 DeliverErrorResponse();
121 } else {
122 // 6.9.6 step 3 and 5: Fetch the resource normally.
125 return job_.get();
128 AppCacheURLRequestJob* AppCacheRequestHandler::MaybeLoadFallbackForResponse(
129 net::URLRequest* request, net::NetworkDelegate* network_delegate) {
130 if (!host_ || !IsSchemeAndMethodSupportedForAppCache(request) ||
131 cache_entry_not_found_)
132 return NULL;
133 if (!found_fallback_entry_.has_response_id())
134 return NULL;
136 if (request->status().status() == net::URLRequestStatus::CANCELED) {
137 // 6.9.6, step 4: But not if the user canceled the download.
138 return NULL;
141 // We don't fallback for responses that we delivered.
142 if (job_.get()) {
143 DCHECK(!job_->is_delivering_network_response());
144 return NULL;
147 if (request->status().is_success()) {
148 int code_major = request->GetResponseCode() / 100;
149 if (code_major !=4 && code_major != 5)
150 return NULL;
152 // Servers can override the fallback behavior with a response header.
153 const std::string kFallbackOverrideHeader(
154 "x-chromium-appcache-fallback-override");
155 const std::string kFallbackOverrideValue(
156 "disallow-fallback");
157 std::string header_value;
158 request->GetResponseHeaderByName(kFallbackOverrideHeader, &header_value);
159 if (header_value == kFallbackOverrideValue)
160 return NULL;
163 // 6.9.6, step 4: If this results in a 4xx or 5xx status code
164 // or there were network errors, get the resource of the fallback entry.
165 job_ = new AppCacheURLRequestJob(request, network_delegate,
166 storage(), host_, is_main_resource());
167 DeliverAppCachedResponse(
168 found_fallback_entry_, found_cache_id_, found_group_id_,
169 found_manifest_url_, true, found_namespace_entry_url_);
170 return job_.get();
173 void AppCacheRequestHandler::GetExtraResponseInfo(
174 int64* cache_id, GURL* manifest_url) {
175 if (job_.get() && job_->is_delivering_appcache_response()) {
176 *cache_id = job_->cache_id();
177 *manifest_url = job_->manifest_url();
181 void AppCacheRequestHandler::PrepareForCrossSiteTransfer(int old_process_id) {
182 if (!host_)
183 return;
184 AppCacheBackendImpl* backend = host_->service()->GetBackend(old_process_id);
185 host_for_cross_site_transfer_ = backend->TransferHostOut(host_->host_id());
186 DCHECK_EQ(host_, host_for_cross_site_transfer_.get());
189 void AppCacheRequestHandler::CompleteCrossSiteTransfer(
190 int new_process_id, int new_host_id) {
191 if (!host_for_cross_site_transfer_.get())
192 return;
193 DCHECK_EQ(host_, host_for_cross_site_transfer_.get());
194 AppCacheBackendImpl* backend = host_->service()->GetBackend(new_process_id);
195 backend->TransferHostIn(new_host_id, host_for_cross_site_transfer_.Pass());
198 void AppCacheRequestHandler::OnDestructionImminent(AppCacheHost* host) {
199 storage()->CancelDelegateCallbacks(this);
200 host_ = NULL; // no need to RemoveObserver, the host is being deleted
202 // Since the host is being deleted, we don't have to complete any job
203 // that is current running. It's destined for the bit bucket anyway.
204 if (job_.get()) {
205 job_->Kill();
206 job_ = NULL;
210 void AppCacheRequestHandler::DeliverAppCachedResponse(
211 const AppCacheEntry& entry, int64 cache_id, int64 group_id,
212 const GURL& manifest_url, bool is_fallback,
213 const GURL& namespace_entry_url) {
214 DCHECK(host_ && job_.get() && job_->is_waiting());
215 DCHECK(entry.has_response_id());
217 if (IsResourceTypeFrame(resource_type_) && !namespace_entry_url.is_empty())
218 host_->NotifyMainResourceIsNamespaceEntry(namespace_entry_url);
220 job_->DeliverAppCachedResponse(manifest_url, group_id, cache_id,
221 entry, is_fallback);
224 void AppCacheRequestHandler::DeliverErrorResponse() {
225 DCHECK(job_.get() && job_->is_waiting());
226 job_->DeliverErrorResponse();
229 void AppCacheRequestHandler::DeliverNetworkResponse() {
230 DCHECK(job_.get() && job_->is_waiting());
231 job_->DeliverNetworkResponse();
234 // Main-resource handling ----------------------------------------------
236 void AppCacheRequestHandler::MaybeLoadMainResource(
237 net::URLRequest* request, net::NetworkDelegate* network_delegate) {
238 DCHECK(!job_.get());
239 DCHECK(host_);
241 const AppCacheHost* spawning_host =
242 (resource_type_ == RESOURCE_TYPE_SHARED_WORKER) ?
243 host_ : host_->GetSpawningHost();
244 GURL preferred_manifest_url = spawning_host ?
245 spawning_host->preferred_manifest_url() : GURL();
247 // We may have to wait for our storage query to complete, but
248 // this query can also complete syncrhonously.
249 job_ = new AppCacheURLRequestJob(request, network_delegate,
250 storage(), host_, is_main_resource());
251 storage()->FindResponseForMainRequest(
252 request->url(), preferred_manifest_url, this);
255 void AppCacheRequestHandler::OnMainResponseFound(
256 const GURL& url, const AppCacheEntry& entry,
257 const GURL& namespace_entry_url, const AppCacheEntry& fallback_entry,
258 int64 cache_id, int64 group_id, const GURL& manifest_url) {
259 DCHECK(job_.get());
260 DCHECK(host_);
261 DCHECK(is_main_resource());
262 DCHECK(!entry.IsForeign());
263 DCHECK(!fallback_entry.IsForeign());
264 DCHECK(!(entry.has_response_id() && fallback_entry.has_response_id()));
266 if (!job_.get())
267 return;
269 AppCachePolicy* policy = host_->service()->appcache_policy();
270 bool was_blocked_by_policy = !manifest_url.is_empty() && policy &&
271 !policy->CanLoadAppCache(manifest_url, host_->first_party_url());
273 if (was_blocked_by_policy) {
274 if (IsResourceTypeFrame(resource_type_)) {
275 host_->NotifyMainResourceBlocked(manifest_url);
276 } else {
277 DCHECK_EQ(resource_type_, RESOURCE_TYPE_SHARED_WORKER);
278 host_->frontend()->OnContentBlocked(host_->host_id(), manifest_url);
280 DeliverNetworkResponse();
281 return;
284 if (IsResourceTypeFrame(resource_type_) && cache_id != kAppCacheNoCacheId) {
285 // AppCacheHost loads and holds a reference to the main resource cache
286 // for two reasons, firstly to preload the cache into the working set
287 // in advance of subresource loads happening, secondly to prevent the
288 // AppCache from falling out of the working set on frame navigations.
289 host_->LoadMainResourceCache(cache_id);
290 host_->set_preferred_manifest_url(manifest_url);
293 // 6.11.1 Navigating across documents, steps 10 and 14.
295 found_entry_ = entry;
296 found_namespace_entry_url_ = namespace_entry_url;
297 found_fallback_entry_ = fallback_entry;
298 found_cache_id_ = cache_id;
299 found_group_id_ = group_id;
300 found_manifest_url_ = manifest_url;
301 found_network_namespace_ = false; // not applicable to main requests
303 if (found_entry_.has_response_id()) {
304 DCHECK(!found_fallback_entry_.has_response_id());
305 DeliverAppCachedResponse(
306 found_entry_, found_cache_id_, found_group_id_, found_manifest_url_,
307 false, found_namespace_entry_url_);
308 } else {
309 DeliverNetworkResponse();
313 // Sub-resource handling ----------------------------------------------
315 void AppCacheRequestHandler::MaybeLoadSubResource(
316 net::URLRequest* request, net::NetworkDelegate* network_delegate) {
317 DCHECK(!job_.get());
319 if (host_->is_selection_pending()) {
320 // We have to wait until cache selection is complete and the
321 // selected cache is loaded.
322 is_waiting_for_cache_selection_ = true;
323 job_ = new AppCacheURLRequestJob(request, network_delegate,
324 storage(), host_, is_main_resource());
325 return;
328 if (!host_->associated_cache() ||
329 !host_->associated_cache()->is_complete() ||
330 host_->associated_cache()->owning_group()->is_being_deleted()) {
331 return;
334 job_ = new AppCacheURLRequestJob(request, network_delegate,
335 storage(), host_, is_main_resource());
336 ContinueMaybeLoadSubResource();
339 void AppCacheRequestHandler::ContinueMaybeLoadSubResource() {
340 // 6.9.6 Changes to the networking model
341 // If the resource is not to be fetched using the HTTP GET mechanism or
342 // equivalent ... then fetch the resource normally.
343 DCHECK(job_.get());
344 DCHECK(host_->associated_cache() && host_->associated_cache()->is_complete());
346 const GURL& url = job_->request()->url();
347 AppCache* cache = host_->associated_cache();
348 storage()->FindResponseForSubRequest(
349 host_->associated_cache(), url,
350 &found_entry_, &found_fallback_entry_, &found_network_namespace_);
352 if (found_entry_.has_response_id()) {
353 // Step 2: If there's an entry, get it instead.
354 DCHECK(!found_network_namespace_ &&
355 !found_fallback_entry_.has_response_id());
356 found_cache_id_ = cache->cache_id();
357 found_group_id_ = cache->owning_group()->group_id();
358 found_manifest_url_ = cache->owning_group()->manifest_url();
359 DeliverAppCachedResponse(
360 found_entry_, found_cache_id_, found_group_id_, found_manifest_url_,
361 false, GURL());
362 return;
365 if (found_fallback_entry_.has_response_id()) {
366 // Step 4: Fetch the resource normally, if this results
367 // in certain conditions, then use the fallback.
368 DCHECK(!found_network_namespace_ &&
369 !found_entry_.has_response_id());
370 found_cache_id_ = cache->cache_id();
371 found_manifest_url_ = cache->owning_group()->manifest_url();
372 DeliverNetworkResponse();
373 return;
376 if (found_network_namespace_) {
377 // Step 3 and 5: Fetch the resource normally.
378 DCHECK(!found_entry_.has_response_id() &&
379 !found_fallback_entry_.has_response_id());
380 DeliverNetworkResponse();
381 return;
384 // Step 6: Fail the resource load.
385 DeliverErrorResponse();
388 void AppCacheRequestHandler::OnCacheSelectionComplete(AppCacheHost* host) {
389 DCHECK(host == host_);
390 if (is_main_resource())
391 return;
392 if (!is_waiting_for_cache_selection_)
393 return;
395 is_waiting_for_cache_selection_ = false;
397 if (!host_->associated_cache() ||
398 !host_->associated_cache()->is_complete()) {
399 DeliverNetworkResponse();
400 return;
403 ContinueMaybeLoadSubResource();
406 } // namespace content