Apply _RELATIVE relocations ahead of others.
[chromium-blink-merge.git] / content / browser / appcache / appcache_request_handler.cc
bloba21337c111193c75d4e9c24ab5f2186f09f7b314
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 "content/browser/service_worker/service_worker_request_handler.h"
12 #include "net/url_request/url_request.h"
13 #include "net/url_request/url_request_job.h"
15 namespace content {
17 AppCacheRequestHandler::AppCacheRequestHandler(AppCacheHost* host,
18 ResourceType resource_type)
19 : host_(host),
20 resource_type_(resource_type),
21 is_waiting_for_cache_selection_(false),
22 found_group_id_(0),
23 found_cache_id_(0),
24 found_network_namespace_(false),
25 cache_entry_not_found_(false),
26 maybe_load_resource_executed_(false) {
27 DCHECK(host_);
28 host_->AddObserver(this);
31 AppCacheRequestHandler::~AppCacheRequestHandler() {
32 if (host_) {
33 storage()->CancelDelegateCallbacks(this);
34 host_->RemoveObserver(this);
38 AppCacheStorage* AppCacheRequestHandler::storage() const {
39 DCHECK(host_);
40 return host_->storage();
43 AppCacheURLRequestJob* AppCacheRequestHandler::MaybeLoadResource(
44 net::URLRequest* request, net::NetworkDelegate* network_delegate) {
45 maybe_load_resource_executed_ = true;
46 if (!host_ || !IsSchemeAndMethodSupportedForAppCache(request) ||
47 cache_entry_not_found_)
48 return NULL;
50 // This method can get called multiple times over the life
51 // of a request. The case we detect here is having scheduled
52 // delivery of a "network response" using a job setup on an
53 // earlier call thru this method. To send the request thru
54 // to the network involves restarting the request altogether,
55 // which will call thru to our interception layer again.
56 // This time thru, we return NULL so the request hits the wire.
57 if (job_.get()) {
58 DCHECK(job_->is_delivering_network_response() ||
59 job_->cache_entry_not_found());
60 if (job_->cache_entry_not_found())
61 cache_entry_not_found_ = true;
62 job_ = NULL;
63 storage()->CancelDelegateCallbacks(this);
64 return NULL;
67 // Clear out our 'found' fields since we're starting a request for a
68 // new resource, any values in those fields are no longer valid.
69 found_entry_ = AppCacheEntry();
70 found_fallback_entry_ = AppCacheEntry();
71 found_cache_id_ = kAppCacheNoCacheId;
72 found_manifest_url_ = GURL();
73 found_network_namespace_ = false;
75 if (is_main_resource())
76 MaybeLoadMainResource(request, network_delegate);
77 else
78 MaybeLoadSubResource(request, network_delegate);
80 // If its been setup to deliver a network response, we can just delete
81 // it now and return NULL instead to achieve that since it couldn't
82 // have been started yet.
83 if (job_.get() && job_->is_delivering_network_response()) {
84 DCHECK(!job_->has_been_started());
85 job_ = NULL;
88 return job_.get();
91 AppCacheURLRequestJob* AppCacheRequestHandler::MaybeLoadFallbackForRedirect(
92 net::URLRequest* request,
93 net::NetworkDelegate* network_delegate,
94 const GURL& location) {
95 if (!host_ || !IsSchemeAndMethodSupportedForAppCache(request) ||
96 cache_entry_not_found_)
97 return NULL;
98 if (is_main_resource())
99 return NULL;
100 // TODO(vabr) This is a temporary fix (see crbug/141114). We should get rid of
101 // it once a more general solution to crbug/121325 is in place.
102 if (!maybe_load_resource_executed_)
103 return NULL;
104 if (request->url().GetOrigin() == location.GetOrigin())
105 return NULL;
107 DCHECK(!job_.get()); // our jobs never generate redirects
109 if (found_fallback_entry_.has_response_id()) {
110 // 6.9.6, step 4: If this results in a redirect to another origin,
111 // get the resource of the fallback entry.
112 job_ = new AppCacheURLRequestJob(request, network_delegate,
113 storage(), host_, is_main_resource());
114 DeliverAppCachedResponse(
115 found_fallback_entry_, found_cache_id_, found_group_id_,
116 found_manifest_url_, true, found_namespace_entry_url_);
117 } else if (!found_network_namespace_) {
118 // 6.9.6, step 6: Fail the resource load.
119 job_ = new AppCacheURLRequestJob(request, network_delegate,
120 storage(), host_, is_main_resource());
121 DeliverErrorResponse();
122 } else {
123 // 6.9.6 step 3 and 5: Fetch the resource normally.
126 return job_.get();
129 AppCacheURLRequestJob* AppCacheRequestHandler::MaybeLoadFallbackForResponse(
130 net::URLRequest* request, net::NetworkDelegate* network_delegate) {
131 if (!host_ || !IsSchemeAndMethodSupportedForAppCache(request) ||
132 cache_entry_not_found_)
133 return NULL;
134 if (!found_fallback_entry_.has_response_id())
135 return NULL;
137 if (request->status().status() == net::URLRequestStatus::CANCELED) {
138 // 6.9.6, step 4: But not if the user canceled the download.
139 return NULL;
142 // We don't fallback for responses that we delivered.
143 if (job_.get()) {
144 DCHECK(!job_->is_delivering_network_response());
145 return NULL;
148 if (request->status().is_success()) {
149 int code_major = request->GetResponseCode() / 100;
150 if (code_major !=4 && code_major != 5)
151 return NULL;
153 // Servers can override the fallback behavior with a response header.
154 const std::string kFallbackOverrideHeader(
155 "x-chromium-appcache-fallback-override");
156 const std::string kFallbackOverrideValue(
157 "disallow-fallback");
158 std::string header_value;
159 request->GetResponseHeaderByName(kFallbackOverrideHeader, &header_value);
160 if (header_value == kFallbackOverrideValue)
161 return NULL;
164 // 6.9.6, step 4: If this results in a 4xx or 5xx status code
165 // or there were network errors, get the resource of the fallback entry.
166 job_ = new AppCacheURLRequestJob(request, network_delegate,
167 storage(), host_, is_main_resource());
168 DeliverAppCachedResponse(
169 found_fallback_entry_, found_cache_id_, found_group_id_,
170 found_manifest_url_, true, found_namespace_entry_url_);
171 return job_.get();
174 void AppCacheRequestHandler::GetExtraResponseInfo(
175 int64* cache_id, GURL* manifest_url) {
176 if (job_.get() && job_->is_delivering_appcache_response()) {
177 *cache_id = job_->cache_id();
178 *manifest_url = job_->manifest_url();
182 void AppCacheRequestHandler::PrepareForCrossSiteTransfer(int old_process_id) {
183 if (!host_)
184 return;
185 AppCacheBackendImpl* backend = host_->service()->GetBackend(old_process_id);
186 host_for_cross_site_transfer_ = backend->TransferHostOut(host_->host_id());
187 DCHECK_EQ(host_, host_for_cross_site_transfer_.get());
190 void AppCacheRequestHandler::CompleteCrossSiteTransfer(
191 int new_process_id, int new_host_id) {
192 if (!host_for_cross_site_transfer_.get())
193 return;
194 DCHECK_EQ(host_, host_for_cross_site_transfer_.get());
195 AppCacheBackendImpl* backend = host_->service()->GetBackend(new_process_id);
196 backend->TransferHostIn(new_host_id, host_for_cross_site_transfer_.Pass());
199 void AppCacheRequestHandler::OnDestructionImminent(AppCacheHost* host) {
200 storage()->CancelDelegateCallbacks(this);
201 host_ = NULL; // no need to RemoveObserver, the host is being deleted
203 // Since the host is being deleted, we don't have to complete any job
204 // that is current running. It's destined for the bit bucket anyway.
205 if (job_.get()) {
206 job_->Kill();
207 job_ = NULL;
211 void AppCacheRequestHandler::DeliverAppCachedResponse(
212 const AppCacheEntry& entry, int64 cache_id, int64 group_id,
213 const GURL& manifest_url, bool is_fallback,
214 const GURL& namespace_entry_url) {
215 DCHECK(host_ && job_.get() && job_->is_waiting());
216 DCHECK(entry.has_response_id());
218 if (IsResourceTypeFrame(resource_type_) && !namespace_entry_url.is_empty())
219 host_->NotifyMainResourceIsNamespaceEntry(namespace_entry_url);
221 job_->DeliverAppCachedResponse(manifest_url, group_id, cache_id,
222 entry, is_fallback);
225 void AppCacheRequestHandler::DeliverErrorResponse() {
226 DCHECK(job_.get() && job_->is_waiting());
227 job_->DeliverErrorResponse();
230 void AppCacheRequestHandler::DeliverNetworkResponse() {
231 DCHECK(job_.get() && job_->is_waiting());
232 job_->DeliverNetworkResponse();
235 // Main-resource handling ----------------------------------------------
237 void AppCacheRequestHandler::MaybeLoadMainResource(
238 net::URLRequest* request, net::NetworkDelegate* network_delegate) {
239 DCHECK(!job_.get());
240 DCHECK(host_);
242 // If a page falls into the scope of a ServiceWorker, any matching AppCaches
243 // should be ignored. This depends on the ServiceWorker handler being invoked
244 // prior to the AppCache handler.
245 if (ServiceWorkerRequestHandler::IsControlledByServiceWorker(request)) {
246 host_->enable_cache_selection(false);
247 return;
250 host_->enable_cache_selection(true);
252 const AppCacheHost* spawning_host =
253 (resource_type_ == RESOURCE_TYPE_SHARED_WORKER) ?
254 host_ : host_->GetSpawningHost();
255 GURL preferred_manifest_url = spawning_host ?
256 spawning_host->preferred_manifest_url() : GURL();
258 // We may have to wait for our storage query to complete, but
259 // this query can also complete syncrhonously.
260 job_ = new AppCacheURLRequestJob(request, network_delegate,
261 storage(), host_, is_main_resource());
262 storage()->FindResponseForMainRequest(
263 request->url(), preferred_manifest_url, this);
266 void AppCacheRequestHandler::OnMainResponseFound(
267 const GURL& url, const AppCacheEntry& entry,
268 const GURL& namespace_entry_url, const AppCacheEntry& fallback_entry,
269 int64 cache_id, int64 group_id, const GURL& manifest_url) {
270 DCHECK(job_.get());
271 DCHECK(host_);
272 DCHECK(is_main_resource());
273 DCHECK(!entry.IsForeign());
274 DCHECK(!fallback_entry.IsForeign());
275 DCHECK(!(entry.has_response_id() && fallback_entry.has_response_id()));
277 if (!job_.get())
278 return;
280 AppCachePolicy* policy = host_->service()->appcache_policy();
281 bool was_blocked_by_policy = !manifest_url.is_empty() && policy &&
282 !policy->CanLoadAppCache(manifest_url, host_->first_party_url());
284 if (was_blocked_by_policy) {
285 if (IsResourceTypeFrame(resource_type_)) {
286 host_->NotifyMainResourceBlocked(manifest_url);
287 } else {
288 DCHECK_EQ(resource_type_, RESOURCE_TYPE_SHARED_WORKER);
289 host_->frontend()->OnContentBlocked(host_->host_id(), manifest_url);
291 DeliverNetworkResponse();
292 return;
295 if (IsResourceTypeFrame(resource_type_) && cache_id != kAppCacheNoCacheId) {
296 // AppCacheHost loads and holds a reference to the main resource cache
297 // for two reasons, firstly to preload the cache into the working set
298 // in advance of subresource loads happening, secondly to prevent the
299 // AppCache from falling out of the working set on frame navigations.
300 host_->LoadMainResourceCache(cache_id);
301 host_->set_preferred_manifest_url(manifest_url);
304 // 6.11.1 Navigating across documents, steps 10 and 14.
306 found_entry_ = entry;
307 found_namespace_entry_url_ = namespace_entry_url;
308 found_fallback_entry_ = fallback_entry;
309 found_cache_id_ = cache_id;
310 found_group_id_ = group_id;
311 found_manifest_url_ = manifest_url;
312 found_network_namespace_ = false; // not applicable to main requests
314 if (found_entry_.has_response_id()) {
315 DCHECK(!found_fallback_entry_.has_response_id());
316 DeliverAppCachedResponse(
317 found_entry_, found_cache_id_, found_group_id_, found_manifest_url_,
318 false, found_namespace_entry_url_);
319 } else {
320 DeliverNetworkResponse();
324 // Sub-resource handling ----------------------------------------------
326 void AppCacheRequestHandler::MaybeLoadSubResource(
327 net::URLRequest* request, net::NetworkDelegate* network_delegate) {
328 DCHECK(!job_.get());
330 if (host_->is_selection_pending()) {
331 // We have to wait until cache selection is complete and the
332 // selected cache is loaded.
333 is_waiting_for_cache_selection_ = true;
334 job_ = new AppCacheURLRequestJob(request, network_delegate,
335 storage(), host_, is_main_resource());
336 return;
339 if (!host_->associated_cache() ||
340 !host_->associated_cache()->is_complete() ||
341 host_->associated_cache()->owning_group()->is_being_deleted()) {
342 return;
345 job_ = new AppCacheURLRequestJob(request, network_delegate,
346 storage(), host_, is_main_resource());
347 ContinueMaybeLoadSubResource();
350 void AppCacheRequestHandler::ContinueMaybeLoadSubResource() {
351 // 6.9.6 Changes to the networking model
352 // If the resource is not to be fetched using the HTTP GET mechanism or
353 // equivalent ... then fetch the resource normally.
354 DCHECK(job_.get());
355 DCHECK(host_->associated_cache() && host_->associated_cache()->is_complete());
357 const GURL& url = job_->request()->url();
358 AppCache* cache = host_->associated_cache();
359 storage()->FindResponseForSubRequest(
360 host_->associated_cache(), url,
361 &found_entry_, &found_fallback_entry_, &found_network_namespace_);
363 if (found_entry_.has_response_id()) {
364 // Step 2: If there's an entry, get it instead.
365 DCHECK(!found_network_namespace_ &&
366 !found_fallback_entry_.has_response_id());
367 found_cache_id_ = cache->cache_id();
368 found_group_id_ = cache->owning_group()->group_id();
369 found_manifest_url_ = cache->owning_group()->manifest_url();
370 DeliverAppCachedResponse(
371 found_entry_, found_cache_id_, found_group_id_, found_manifest_url_,
372 false, GURL());
373 return;
376 if (found_fallback_entry_.has_response_id()) {
377 // Step 4: Fetch the resource normally, if this results
378 // in certain conditions, then use the fallback.
379 DCHECK(!found_network_namespace_ &&
380 !found_entry_.has_response_id());
381 found_cache_id_ = cache->cache_id();
382 found_manifest_url_ = cache->owning_group()->manifest_url();
383 DeliverNetworkResponse();
384 return;
387 if (found_network_namespace_) {
388 // Step 3 and 5: Fetch the resource normally.
389 DCHECK(!found_entry_.has_response_id() &&
390 !found_fallback_entry_.has_response_id());
391 DeliverNetworkResponse();
392 return;
395 // Step 6: Fail the resource load.
396 DeliverErrorResponse();
399 void AppCacheRequestHandler::OnCacheSelectionComplete(AppCacheHost* host) {
400 DCHECK(host == host_);
401 if (is_main_resource())
402 return;
403 if (!is_waiting_for_cache_selection_)
404 return;
406 is_waiting_for_cache_selection_ = false;
408 if (!host_->associated_cache() ||
409 !host_->associated_cache()->is_complete()) {
410 DeliverNetworkResponse();
411 return;
414 ContinueMaybeLoadSubResource();
417 } // namespace content