Dismiss autofill popup on screen orientation change.
[chromium-blink-merge.git] / webkit / browser / appcache / appcache_request_handler.cc
blobd2367ceb00d33842f1427b1de199f7653d95c145
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 "webkit/browser/appcache/appcache_request_handler.h"
7 #include "net/url_request/url_request.h"
8 #include "net/url_request/url_request_job.h"
9 #include "webkit/browser/appcache/appcache.h"
10 #include "webkit/browser/appcache/appcache_policy.h"
11 #include "webkit/browser/appcache/appcache_url_request_job.h"
13 namespace appcache {
15 AppCacheRequestHandler::AppCacheRequestHandler(
16 AppCacheHost* host, ResourceType::Type resource_type)
17 : host_(host), resource_type_(resource_type),
18 is_waiting_for_cache_selection_(false), found_group_id_(0),
19 found_cache_id_(0), found_network_namespace_(false),
20 cache_entry_not_found_(false), maybe_load_resource_executed_(false) {
21 DCHECK(host_);
22 host_->AddObserver(this);
25 AppCacheRequestHandler::~AppCacheRequestHandler() {
26 if (host_) {
27 storage()->CancelDelegateCallbacks(this);
28 host_->RemoveObserver(this);
32 AppCacheStorage* AppCacheRequestHandler::storage() const {
33 DCHECK(host_);
34 return host_->service()->storage();
37 void AppCacheRequestHandler::GetExtraResponseInfo(
38 int64* cache_id, GURL* manifest_url) {
39 if (job_.get() && job_->is_delivering_appcache_response()) {
40 *cache_id = job_->cache_id();
41 *manifest_url = job_->manifest_url();
45 AppCacheURLRequestJob* AppCacheRequestHandler::MaybeLoadResource(
46 net::URLRequest* request, net::NetworkDelegate* network_delegate) {
47 maybe_load_resource_executed_ = true;
48 if (!host_ || !IsSchemeAndMethodSupported(request) || cache_entry_not_found_)
49 return NULL;
51 // This method can get called multiple times over the life
52 // of a request. The case we detect here is having scheduled
53 // delivery of a "network response" using a job setup on an
54 // earlier call thru this method. To send the request thru
55 // to the network involves restarting the request altogether,
56 // which will call thru to our interception layer again.
57 // This time thru, we return NULL so the request hits the wire.
58 if (job_.get()) {
59 DCHECK(job_->is_delivering_network_response() ||
60 job_->cache_entry_not_found());
61 if (job_->cache_entry_not_found())
62 cache_entry_not_found_ = true;
63 job_ = NULL;
64 storage()->CancelDelegateCallbacks(this);
65 return NULL;
68 // Clear out our 'found' fields since we're starting a request for a
69 // new resource, any values in those fields are no longer valid.
70 found_entry_ = AppCacheEntry();
71 found_fallback_entry_ = AppCacheEntry();
72 found_cache_id_ = kNoCacheId;
73 found_manifest_url_ = GURL();
74 found_network_namespace_ = false;
76 if (is_main_resource())
77 MaybeLoadMainResource(request, network_delegate);
78 else
79 MaybeLoadSubResource(request, network_delegate);
81 // If its been setup to deliver a network response, we can just delete
82 // it now and return NULL instead to achieve that since it couldn't
83 // have been started yet.
84 if (job_.get() && job_->is_delivering_network_response()) {
85 DCHECK(!job_->has_been_started());
86 job_ = NULL;
89 return job_.get();
92 AppCacheURLRequestJob* AppCacheRequestHandler::MaybeLoadFallbackForRedirect(
93 net::URLRequest* request,
94 net::NetworkDelegate* network_delegate,
95 const GURL& location) {
96 if (!host_ || !IsSchemeAndMethodSupported(request) || 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_);
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_);
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_ || !IsSchemeAndMethodSupported(request) || 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_);
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::OnDestructionImminent(AppCacheHost* host) {
174 storage()->CancelDelegateCallbacks(this);
175 host_ = NULL; // no need to RemoveObserver, the host is being deleted
177 // Since the host is being deleted, we don't have to complete any job
178 // that is current running. It's destined for the bit bucket anyway.
179 if (job_.get()) {
180 job_->Kill();
181 job_ = NULL;
185 void AppCacheRequestHandler::DeliverAppCachedResponse(
186 const AppCacheEntry& entry, int64 cache_id, int64 group_id,
187 const GURL& manifest_url, bool is_fallback,
188 const GURL& namespace_entry_url) {
189 DCHECK(host_ && job_.get() && job_->is_waiting());
190 DCHECK(entry.has_response_id());
192 if (ResourceType::IsFrame(resource_type_) && !namespace_entry_url.is_empty())
193 host_->NotifyMainResourceIsNamespaceEntry(namespace_entry_url);
195 job_->DeliverAppCachedResponse(manifest_url, group_id, cache_id,
196 entry, is_fallback);
199 void AppCacheRequestHandler::DeliverErrorResponse() {
200 DCHECK(job_.get() && job_->is_waiting());
201 job_->DeliverErrorResponse();
204 void AppCacheRequestHandler::DeliverNetworkResponse() {
205 DCHECK(job_.get() && job_->is_waiting());
206 job_->DeliverNetworkResponse();
209 // Main-resource handling ----------------------------------------------
211 void AppCacheRequestHandler::MaybeLoadMainResource(
212 net::URLRequest* request, net::NetworkDelegate* network_delegate) {
213 DCHECK(!job_.get());
214 DCHECK(host_);
216 const AppCacheHost* spawning_host =
217 ResourceType::IsSharedWorker(resource_type_) ?
218 host_ : host_->GetSpawningHost();
219 GURL preferred_manifest_url = spawning_host ?
220 spawning_host->preferred_manifest_url() : GURL();
222 // We may have to wait for our storage query to complete, but
223 // this query can also complete syncrhonously.
224 job_ = new AppCacheURLRequestJob(request, network_delegate,
225 storage(), host_);
226 storage()->FindResponseForMainRequest(
227 request->url(), preferred_manifest_url, this);
230 void AppCacheRequestHandler::OnMainResponseFound(
231 const GURL& url, const AppCacheEntry& entry,
232 const GURL& namespace_entry_url, const AppCacheEntry& fallback_entry,
233 int64 cache_id, int64 group_id, const GURL& manifest_url) {
234 DCHECK(job_.get());
235 DCHECK(host_);
236 DCHECK(is_main_resource());
237 DCHECK(!entry.IsForeign());
238 DCHECK(!fallback_entry.IsForeign());
239 DCHECK(!(entry.has_response_id() && fallback_entry.has_response_id()));
241 if (!job_.get())
242 return;
244 AppCachePolicy* policy = host_->service()->appcache_policy();
245 bool was_blocked_by_policy = !manifest_url.is_empty() && policy &&
246 !policy->CanLoadAppCache(manifest_url, host_->first_party_url());
248 if (was_blocked_by_policy) {
249 if (ResourceType::IsFrame(resource_type_)) {
250 host_->NotifyMainResourceBlocked(manifest_url);
251 } else {
252 DCHECK(ResourceType::IsSharedWorker(resource_type_));
253 host_->frontend()->OnContentBlocked(host_->host_id(), manifest_url);
255 DeliverNetworkResponse();
256 return;
259 if (ResourceType::IsFrame(resource_type_) && cache_id != kNoCacheId) {
260 // AppCacheHost loads and holds a reference to the main resource cache
261 // for two reasons, firstly to preload the cache into the working set
262 // in advance of subresource loads happening, secondly to prevent the
263 // AppCache from falling out of the working set on frame navigations.
264 host_->LoadMainResourceCache(cache_id);
265 host_->set_preferred_manifest_url(manifest_url);
268 // 6.11.1 Navigating across documents, steps 10 and 14.
270 found_entry_ = entry;
271 found_namespace_entry_url_ = namespace_entry_url;
272 found_fallback_entry_ = fallback_entry;
273 found_cache_id_ = cache_id;
274 found_group_id_ = group_id;
275 found_manifest_url_ = manifest_url;
276 found_network_namespace_ = false; // not applicable to main requests
278 if (found_entry_.has_response_id()) {
279 DCHECK(!found_fallback_entry_.has_response_id());
280 DeliverAppCachedResponse(
281 found_entry_, found_cache_id_, found_group_id_, found_manifest_url_,
282 false, found_namespace_entry_url_);
283 } else {
284 DeliverNetworkResponse();
288 // Sub-resource handling ----------------------------------------------
290 void AppCacheRequestHandler::MaybeLoadSubResource(
291 net::URLRequest* request, net::NetworkDelegate* network_delegate) {
292 DCHECK(!job_.get());
294 if (host_->is_selection_pending()) {
295 // We have to wait until cache selection is complete and the
296 // selected cache is loaded.
297 is_waiting_for_cache_selection_ = true;
298 job_ = new AppCacheURLRequestJob(request, network_delegate,
299 storage(), host_);
300 return;
303 if (!host_->associated_cache() ||
304 !host_->associated_cache()->is_complete()) {
305 return;
308 job_ = new AppCacheURLRequestJob(request, network_delegate,
309 storage(), host_);
310 ContinueMaybeLoadSubResource();
313 void AppCacheRequestHandler::ContinueMaybeLoadSubResource() {
314 // 6.9.6 Changes to the networking model
315 // If the resource is not to be fetched using the HTTP GET mechanism or
316 // equivalent ... then fetch the resource normally.
317 DCHECK(job_.get());
318 DCHECK(host_->associated_cache() && host_->associated_cache()->is_complete());
320 const GURL& url = job_->request()->url();
321 AppCache* cache = host_->associated_cache();
322 storage()->FindResponseForSubRequest(
323 host_->associated_cache(), url,
324 &found_entry_, &found_fallback_entry_, &found_network_namespace_);
326 if (found_entry_.has_response_id()) {
327 // Step 2: If there's an entry, get it instead.
328 DCHECK(!found_network_namespace_ &&
329 !found_fallback_entry_.has_response_id());
330 found_cache_id_ = cache->cache_id();
331 found_group_id_ = cache->owning_group()->group_id();
332 found_manifest_url_ = cache->owning_group()->manifest_url();
333 DeliverAppCachedResponse(
334 found_entry_, found_cache_id_, found_group_id_, found_manifest_url_,
335 false, GURL());
336 return;
339 if (found_fallback_entry_.has_response_id()) {
340 // Step 4: Fetch the resource normally, if this results
341 // in certain conditions, then use the fallback.
342 DCHECK(!found_network_namespace_ &&
343 !found_entry_.has_response_id());
344 found_cache_id_ = cache->cache_id();
345 found_manifest_url_ = cache->owning_group()->manifest_url();
346 DeliverNetworkResponse();
347 return;
350 if (found_network_namespace_) {
351 // Step 3 and 5: Fetch the resource normally.
352 DCHECK(!found_entry_.has_response_id() &&
353 !found_fallback_entry_.has_response_id());
354 DeliverNetworkResponse();
355 return;
358 // Step 6: Fail the resource load.
359 DeliverErrorResponse();
362 void AppCacheRequestHandler::OnCacheSelectionComplete(AppCacheHost* host) {
363 DCHECK(host == host_);
364 if (is_main_resource())
365 return;
366 if (!is_waiting_for_cache_selection_)
367 return;
369 is_waiting_for_cache_selection_ = false;
371 if (!host_->associated_cache() ||
372 !host_->associated_cache()->is_complete()) {
373 DeliverNetworkResponse();
374 return;
377 ContinueMaybeLoadSubResource();
380 } // namespace appcache