Dismiss autofill popup on screen orientation change.
[chromium-blink-merge.git] / webkit / browser / appcache / appcache_url_request_job.cc
blob077e09d8b99df3fc55de5c3edbeda80ebdf3602d
1 // Copyright (c) 2012 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_url_request_job.h"
7 #include <vector>
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/command_line.h"
12 #include "base/compiler_specific.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "net/base/io_buffer.h"
17 #include "net/base/net_errors.h"
18 #include "net/base/net_log.h"
19 #include "net/http/http_request_headers.h"
20 #include "net/http/http_response_headers.h"
21 #include "net/http/http_util.h"
22 #include "net/url_request/url_request.h"
23 #include "net/url_request/url_request_status.h"
24 #include "webkit/browser/appcache/appcache.h"
25 #include "webkit/browser/appcache/appcache_group.h"
26 #include "webkit/browser/appcache/appcache_histograms.h"
27 #include "webkit/browser/appcache/appcache_host.h"
28 #include "webkit/browser/appcache/appcache_service.h"
30 namespace appcache {
32 AppCacheURLRequestJob::AppCacheURLRequestJob(
33 net::URLRequest* request,
34 net::NetworkDelegate* network_delegate,
35 AppCacheStorage* storage,
36 AppCacheHost* host)
37 : net::URLRequestJob(request, network_delegate),
38 host_(host),
39 storage_(storage),
40 has_been_started_(false), has_been_killed_(false),
41 delivery_type_(AWAITING_DELIVERY_ORDERS),
42 group_id_(0), cache_id_(kNoCacheId), is_fallback_(false),
43 cache_entry_not_found_(false),
44 weak_factory_(this) {
45 DCHECK(storage_);
48 void AppCacheURLRequestJob::DeliverAppCachedResponse(
49 const GURL& manifest_url, int64 group_id, int64 cache_id,
50 const AppCacheEntry& entry, bool is_fallback) {
51 DCHECK(!has_delivery_orders());
52 DCHECK(entry.has_response_id());
53 delivery_type_ = APPCACHED_DELIVERY;
54 manifest_url_ = manifest_url;
55 group_id_ = group_id;
56 cache_id_ = cache_id;
57 entry_ = entry;
58 is_fallback_ = is_fallback;
59 MaybeBeginDelivery();
62 void AppCacheURLRequestJob::DeliverNetworkResponse() {
63 DCHECK(!has_delivery_orders());
64 delivery_type_ = NETWORK_DELIVERY;
65 storage_ = NULL; // not needed
66 MaybeBeginDelivery();
69 void AppCacheURLRequestJob::DeliverErrorResponse() {
70 DCHECK(!has_delivery_orders());
71 delivery_type_ = ERROR_DELIVERY;
72 storage_ = NULL; // not needed
73 MaybeBeginDelivery();
76 void AppCacheURLRequestJob::MaybeBeginDelivery() {
77 if (has_been_started() && has_delivery_orders()) {
78 // Start asynchronously so that all error reporting and data
79 // callbacks happen as they would for network requests.
80 base::MessageLoop::current()->PostTask(
81 FROM_HERE,
82 base::Bind(&AppCacheURLRequestJob::BeginDelivery,
83 weak_factory_.GetWeakPtr()));
87 void AppCacheURLRequestJob::BeginDelivery() {
88 DCHECK(has_delivery_orders() && has_been_started());
90 if (has_been_killed())
91 return;
93 switch (delivery_type_) {
94 case NETWORK_DELIVERY:
95 AppCacheHistograms::AddNetworkJobStartDelaySample(
96 base::TimeTicks::Now() - start_time_tick_);
97 // To fallthru to the network, we restart the request which will
98 // cause a new job to be created to retrieve the resource from the
99 // network. Our caller is responsible for arranging to not re-intercept
100 // the same request.
101 NotifyRestartRequired();
102 break;
104 case ERROR_DELIVERY:
105 AppCacheHistograms::AddErrorJobStartDelaySample(
106 base::TimeTicks::Now() - start_time_tick_);
107 request()->net_log().AddEvent(
108 net::NetLog::TYPE_APPCACHE_DELIVERING_ERROR_RESPONSE);
109 NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED,
110 net::ERR_FAILED));
111 break;
113 case APPCACHED_DELIVERY:
114 if (entry_.IsExecutable()) {
115 BeginExecutableHandlerDelivery();
116 return;
118 AppCacheHistograms::AddAppCacheJobStartDelaySample(
119 base::TimeTicks::Now() - start_time_tick_);
120 request()->net_log().AddEvent(
121 is_fallback_ ?
122 net::NetLog::TYPE_APPCACHE_DELIVERING_FALLBACK_RESPONSE :
123 net::NetLog::TYPE_APPCACHE_DELIVERING_CACHED_RESPONSE);
124 storage_->LoadResponseInfo(
125 manifest_url_, group_id_, entry_.response_id(), this);
126 break;
128 default:
129 NOTREACHED();
130 break;
134 void AppCacheURLRequestJob::BeginExecutableHandlerDelivery() {
135 DCHECK(CommandLine::ForCurrentProcess()->
136 HasSwitch(kEnableExecutableHandlers));
137 if (!storage_->service()->handler_factory()) {
138 BeginErrorDelivery("missing handler factory");
139 return;
142 request()->net_log().AddEvent(
143 net::NetLog::TYPE_APPCACHE_DELIVERING_EXECUTABLE_RESPONSE);
145 // We defer job delivery until the executable handler is spun up and
146 // provides a response. The sequence goes like this...
148 // 1. First we load the cache.
149 // 2. Then if the handler is not spun up, we load the script resource which
150 // is needed to spin it up.
151 // 3. Then we ask then we ask the handler to compute a response.
152 // 4. Finally we deilver that response to the caller.
153 storage_->LoadCache(cache_id_, this);
156 void AppCacheURLRequestJob::OnCacheLoaded(AppCache* cache, int64 cache_id) {
157 DCHECK_EQ(cache_id_, cache_id);
158 DCHECK(!has_been_killed());
160 if (!cache) {
161 BeginErrorDelivery("cache load failed");
162 return;
165 // Keep references to ensure they don't go out of scope until job completion.
166 cache_ = cache;
167 group_ = cache->owning_group();
169 // If the handler is spun up, ask it to compute a response.
170 AppCacheExecutableHandler* handler =
171 cache->GetExecutableHandler(entry_.response_id());
172 if (handler) {
173 InvokeExecutableHandler(handler);
174 return;
177 // Handler is not spun up yet, load the script resource to do that.
178 // NOTE: This is not ideal since multiple jobs may be doing this,
179 // concurrently but close enough for now, the first to load the script
180 // will win.
182 // Read the script data, truncating if its too large.
183 // NOTE: we just issue one read and don't bother chaining if the resource
184 // is very (very) large, close enough for now.
185 const int64 kLimit = 500 * 1000;
186 handler_source_buffer_ = new net::GrowableIOBuffer();
187 handler_source_buffer_->SetCapacity(kLimit);
188 handler_source_reader_.reset(storage_->CreateResponseReader(
189 manifest_url_, group_id_, entry_.response_id()));
190 handler_source_reader_->ReadData(
191 handler_source_buffer_.get(),
192 kLimit,
193 base::Bind(&AppCacheURLRequestJob::OnExecutableSourceLoaded,
194 base::Unretained(this)));
197 void AppCacheURLRequestJob::OnExecutableSourceLoaded(int result) {
198 DCHECK(!has_been_killed());
199 handler_source_reader_.reset();
200 if (result < 0) {
201 BeginErrorDelivery("script source load failed");
202 return;
205 handler_source_buffer_->SetCapacity(result); // Free up some memory.
207 AppCacheExecutableHandler* handler = cache_->GetOrCreateExecutableHandler(
208 entry_.response_id(), handler_source_buffer_.get());
209 handler_source_buffer_ = NULL; // not needed anymore
210 if (handler) {
211 InvokeExecutableHandler(handler);
212 return;
215 BeginErrorDelivery("factory failed to produce a handler");
218 void AppCacheURLRequestJob::InvokeExecutableHandler(
219 AppCacheExecutableHandler* handler) {
220 handler->HandleRequest(
221 request(),
222 base::Bind(&AppCacheURLRequestJob::OnExecutableResponseCallback,
223 weak_factory_.GetWeakPtr()));
226 void AppCacheURLRequestJob::OnExecutableResponseCallback(
227 const AppCacheExecutableHandler::Response& response) {
228 DCHECK(!has_been_killed());
229 if (response.use_network) {
230 delivery_type_ = NETWORK_DELIVERY;
231 storage_ = NULL;
232 BeginDelivery();
233 return;
236 if (!response.cached_resource_url.is_empty()) {
237 AppCacheEntry* entry_ptr = cache_->GetEntry(response.cached_resource_url);
238 if (entry_ptr && !entry_ptr->IsExecutable()) {
239 entry_ = *entry_ptr;
240 BeginDelivery();
241 return;
245 if (!response.redirect_url.is_empty()) {
246 // TODO(michaeln): playback a redirect
247 // response_headers_(new HttpResponseHeaders(response_headers)),
248 // fallthru for now to deliver an error
251 // Otherwise, return an error.
252 BeginErrorDelivery("handler returned an invalid response");
255 void AppCacheURLRequestJob::BeginErrorDelivery(const char* message) {
256 if (host_)
257 host_->frontend()->OnLogMessage(host_->host_id(), LOG_ERROR, message);
258 delivery_type_ = ERROR_DELIVERY;
259 storage_ = NULL;
260 BeginDelivery();
263 AppCacheURLRequestJob::~AppCacheURLRequestJob() {
264 if (storage_)
265 storage_->CancelDelegateCallbacks(this);
268 void AppCacheURLRequestJob::OnResponseInfoLoaded(
269 AppCacheResponseInfo* response_info, int64 response_id) {
270 DCHECK(is_delivering_appcache_response());
271 scoped_refptr<AppCacheURLRequestJob> protect(this);
272 if (response_info) {
273 info_ = response_info;
274 reader_.reset(storage_->CreateResponseReader(
275 manifest_url_, group_id_, entry_.response_id()));
277 if (is_range_request())
278 SetupRangeResponse();
280 NotifyHeadersComplete();
281 } else {
282 // A resource that is expected to be in the appcache is missing.
283 // See http://code.google.com/p/chromium/issues/detail?id=50657
284 // Instead of failing the request, we restart the request. The retry
285 // attempt will fallthru to the network instead of trying to load
286 // from the appcache.
287 storage_->service()->CheckAppCacheResponse(manifest_url_, cache_id_,
288 entry_.response_id());
289 cache_entry_not_found_ = true;
290 NotifyRestartRequired();
294 const net::HttpResponseInfo* AppCacheURLRequestJob::http_info() const {
295 if (!info_.get())
296 return NULL;
297 if (range_response_info_)
298 return range_response_info_.get();
299 return info_->http_response_info();
302 void AppCacheURLRequestJob::SetupRangeResponse() {
303 DCHECK(is_range_request() && info_.get() && reader_.get() &&
304 is_delivering_appcache_response());
305 int resource_size = static_cast<int>(info_->response_data_size());
306 if (resource_size < 0 || !range_requested_.ComputeBounds(resource_size)) {
307 range_requested_ = net::HttpByteRange();
308 return;
311 DCHECK(range_requested_.HasFirstBytePosition() &&
312 range_requested_.HasLastBytePosition());
313 int offset = static_cast<int>(range_requested_.first_byte_position());
314 int length = static_cast<int>(range_requested_.last_byte_position() -
315 range_requested_.first_byte_position() + 1);
317 // Tell the reader about the range to read.
318 reader_->SetReadRange(offset, length);
320 // Make a copy of the full response headers and fix them up
321 // for the range we'll be returning.
322 const char kLengthHeader[] = "Content-Length";
323 const char kRangeHeader[] = "Content-Range";
324 const char kPartialStatusLine[] = "HTTP/1.1 206 Partial Content";
325 range_response_info_.reset(
326 new net::HttpResponseInfo(*info_->http_response_info()));
327 net::HttpResponseHeaders* headers = range_response_info_->headers.get();
328 headers->RemoveHeader(kLengthHeader);
329 headers->RemoveHeader(kRangeHeader);
330 headers->ReplaceStatusLine(kPartialStatusLine);
331 headers->AddHeader(
332 base::StringPrintf("%s: %d", kLengthHeader, length));
333 headers->AddHeader(
334 base::StringPrintf("%s: bytes %d-%d/%d",
335 kRangeHeader,
336 offset,
337 offset + length - 1,
338 resource_size));
341 void AppCacheURLRequestJob::OnReadComplete(int result) {
342 DCHECK(is_delivering_appcache_response());
343 if (result == 0) {
344 NotifyDone(net::URLRequestStatus());
345 } else if (result < 0) {
346 storage_->service()->CheckAppCacheResponse(manifest_url_, cache_id_,
347 entry_.response_id());
348 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, result));
349 } else {
350 SetStatus(net::URLRequestStatus()); // Clear the IO_PENDING status
352 NotifyReadComplete(result);
355 // net::URLRequestJob overrides ------------------------------------------------
357 void AppCacheURLRequestJob::Start() {
358 DCHECK(!has_been_started());
359 has_been_started_ = true;
360 start_time_tick_ = base::TimeTicks::Now();
361 MaybeBeginDelivery();
364 void AppCacheURLRequestJob::Kill() {
365 if (!has_been_killed_) {
366 has_been_killed_ = true;
367 reader_.reset();
368 handler_source_reader_.reset();
369 if (storage_) {
370 storage_->CancelDelegateCallbacks(this);
371 storage_ = NULL;
373 host_ = NULL;
374 net::URLRequestJob::Kill();
375 weak_factory_.InvalidateWeakPtrs();
379 net::LoadState AppCacheURLRequestJob::GetLoadState() const {
380 if (!has_been_started())
381 return net::LOAD_STATE_IDLE;
382 if (!has_delivery_orders())
383 return net::LOAD_STATE_WAITING_FOR_APPCACHE;
384 if (delivery_type_ != APPCACHED_DELIVERY)
385 return net::LOAD_STATE_IDLE;
386 if (!info_.get())
387 return net::LOAD_STATE_WAITING_FOR_APPCACHE;
388 if (reader_.get() && reader_->IsReadPending())
389 return net::LOAD_STATE_READING_RESPONSE;
390 return net::LOAD_STATE_IDLE;
393 bool AppCacheURLRequestJob::GetMimeType(std::string* mime_type) const {
394 if (!http_info())
395 return false;
396 return http_info()->headers->GetMimeType(mime_type);
399 bool AppCacheURLRequestJob::GetCharset(std::string* charset) {
400 if (!http_info())
401 return false;
402 return http_info()->headers->GetCharset(charset);
405 void AppCacheURLRequestJob::GetResponseInfo(net::HttpResponseInfo* info) {
406 if (!http_info())
407 return;
408 *info = *http_info();
411 int AppCacheURLRequestJob::GetResponseCode() const {
412 if (!http_info())
413 return -1;
414 return http_info()->headers->response_code();
417 bool AppCacheURLRequestJob::ReadRawData(net::IOBuffer* buf, int buf_size,
418 int *bytes_read) {
419 DCHECK(is_delivering_appcache_response());
420 DCHECK_NE(buf_size, 0);
421 DCHECK(bytes_read);
422 DCHECK(!reader_->IsReadPending());
423 reader_->ReadData(
424 buf, buf_size, base::Bind(&AppCacheURLRequestJob::OnReadComplete,
425 base::Unretained(this)));
426 SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
427 return false;
430 void AppCacheURLRequestJob::SetExtraRequestHeaders(
431 const net::HttpRequestHeaders& headers) {
432 std::string value;
433 std::vector<net::HttpByteRange> ranges;
434 if (!headers.GetHeader(net::HttpRequestHeaders::kRange, &value) ||
435 !net::HttpUtil::ParseRangeHeader(value, &ranges)) {
436 return;
439 // If multiple ranges are requested, we play dumb and
440 // return the entire response with 200 OK.
441 if (ranges.size() == 1U)
442 range_requested_ = ranges[0];
445 } // namespace appcache