web_url_loader_impl.cc revision c5cede9ae108bb15f6b7a8aea21c7e1fefa2834c
1// Copyright 2014 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.
4
5// An implementation of WebURLLoader in terms of ResourceLoaderBridge.
6
7#include "content/child/web_url_loader_impl.h"
8
9#include "base/bind.h"
10#include "base/files/file_path.h"
11#include "base/memory/scoped_ptr.h"
12#include "base/message_loop/message_loop.h"
13#include "base/strings/string_util.h"
14#include "base/time/time.h"
15#include "content/child/child_thread.h"
16#include "content/child/ftp_directory_listing_response_delegate.h"
17#include "content/child/request_extra_data.h"
18#include "content/child/request_info.h"
19#include "content/child/sync_load_response.h"
20#include "content/common/resource_request_body.h"
21#include "content/public/child/request_peer.h"
22#include "net/base/data_url.h"
23#include "net/base/filename_util.h"
24#include "net/base/load_flags.h"
25#include "net/base/mime_util.h"
26#include "net/base/net_errors.h"
27#include "net/http/http_response_headers.h"
28#include "net/http/http_util.h"
29#include "net/url_request/url_request.h"
30#include "third_party/WebKit/public/platform/WebHTTPHeaderVisitor.h"
31#include "third_party/WebKit/public/platform/WebHTTPLoadInfo.h"
32#include "third_party/WebKit/public/platform/WebURL.h"
33#include "third_party/WebKit/public/platform/WebURLError.h"
34#include "third_party/WebKit/public/platform/WebURLLoadTiming.h"
35#include "third_party/WebKit/public/platform/WebURLLoaderClient.h"
36#include "third_party/WebKit/public/platform/WebURLRequest.h"
37#include "third_party/WebKit/public/platform/WebURLResponse.h"
38#include "third_party/WebKit/public/web/WebSecurityPolicy.h"
39#include "webkit/child/multipart_response_delegate.h"
40#include "webkit/child/resource_loader_bridge.h"
41#include "webkit/child/weburlresponse_extradata_impl.h"
42
43using base::Time;
44using base::TimeTicks;
45using blink::WebData;
46using blink::WebHTTPBody;
47using blink::WebHTTPHeaderVisitor;
48using blink::WebHTTPLoadInfo;
49using blink::WebReferrerPolicy;
50using blink::WebSecurityPolicy;
51using blink::WebString;
52using blink::WebURL;
53using blink::WebURLError;
54using blink::WebURLLoadTiming;
55using blink::WebURLLoader;
56using blink::WebURLLoaderClient;
57using blink::WebURLRequest;
58using blink::WebURLResponse;
59using webkit_glue::MultipartResponseDelegate;
60using webkit_glue::ResourceDevToolsInfo;
61using webkit_glue::ResourceLoaderBridge;
62using webkit_glue::ResourceResponseInfo;
63using webkit_glue::WebURLResponseExtraDataImpl;
64
65namespace content {
66
67// Utilities ------------------------------------------------------------------
68
69namespace {
70
71const char kThrottledErrorDescription[] =
72    "Request throttled. Visit http://dev.chromium.org/throttling for more "
73    "information.";
74
75class HeaderFlattener : public WebHTTPHeaderVisitor {
76 public:
77  explicit HeaderFlattener(int load_flags)
78      : load_flags_(load_flags),
79        has_accept_header_(false) {
80  }
81
82  virtual void visitHeader(const WebString& name, const WebString& value) {
83    // Headers are latin1.
84    const std::string& name_latin1 = name.latin1();
85    const std::string& value_latin1 = value.latin1();
86
87    // Skip over referrer headers found in the header map because we already
88    // pulled it out as a separate parameter.
89    if (LowerCaseEqualsASCII(name_latin1, "referer"))
90      return;
91
92    // Skip over "Cache-Control: max-age=0" header if the corresponding
93    // load flag is already specified. FrameLoader sets both the flag and
94    // the extra header -- the extra header is redundant since our network
95    // implementation will add the necessary headers based on load flags.
96    // See http://code.google.com/p/chromium/issues/detail?id=3434.
97    if ((load_flags_ & net::LOAD_VALIDATE_CACHE) &&
98        LowerCaseEqualsASCII(name_latin1, "cache-control") &&
99        LowerCaseEqualsASCII(value_latin1, "max-age=0"))
100      return;
101
102    if (LowerCaseEqualsASCII(name_latin1, "accept"))
103      has_accept_header_ = true;
104
105    if (!buffer_.empty())
106      buffer_.append("\r\n");
107    buffer_.append(name_latin1 + ": " + value_latin1);
108  }
109
110  const std::string& GetBuffer() {
111    // In some cases, WebKit doesn't add an Accept header, but not having the
112    // header confuses some web servers.  See bug 808613.
113    if (!has_accept_header_) {
114      if (!buffer_.empty())
115        buffer_.append("\r\n");
116      buffer_.append("Accept: */*");
117      has_accept_header_ = true;
118    }
119    return buffer_;
120  }
121
122 private:
123  int load_flags_;
124  std::string buffer_;
125  bool has_accept_header_;
126};
127
128// Extracts the information from a data: url.
129bool GetInfoFromDataURL(const GURL& url,
130                        ResourceResponseInfo* info,
131                        std::string* data,
132                        int* error_code) {
133  std::string mime_type;
134  std::string charset;
135  if (net::DataURL::Parse(url, &mime_type, &charset, data)) {
136    *error_code = net::OK;
137    // Assure same time for all time fields of data: URLs.
138    Time now = Time::Now();
139    info->load_timing.request_start = TimeTicks::Now();
140    info->load_timing.request_start_time = now;
141    info->request_time = now;
142    info->response_time = now;
143    info->headers = NULL;
144    info->mime_type.swap(mime_type);
145    info->charset.swap(charset);
146    info->security_info.clear();
147    info->content_length = data->length();
148    info->encoded_data_length = 0;
149
150    return true;
151  }
152
153  *error_code = net::ERR_INVALID_URL;
154  return false;
155}
156
157typedef ResourceDevToolsInfo::HeadersVector HeadersVector;
158
159// Converts timing data from |load_timing| to the format used by WebKit.
160void PopulateURLLoadTiming(const net::LoadTimingInfo& load_timing,
161                           WebURLLoadTiming* url_timing) {
162  DCHECK(!load_timing.request_start.is_null());
163
164  const TimeTicks kNullTicks;
165  url_timing->initialize();
166  url_timing->setRequestTime(
167      (load_timing.request_start - kNullTicks).InSecondsF());
168  url_timing->setProxyStart(
169      (load_timing.proxy_resolve_start - kNullTicks).InSecondsF());
170  url_timing->setProxyEnd(
171      (load_timing.proxy_resolve_end - kNullTicks).InSecondsF());
172  url_timing->setDNSStart(
173      (load_timing.connect_timing.dns_start - kNullTicks).InSecondsF());
174  url_timing->setDNSEnd(
175      (load_timing.connect_timing.dns_end - kNullTicks).InSecondsF());
176  url_timing->setConnectStart(
177      (load_timing.connect_timing.connect_start - kNullTicks).InSecondsF());
178  url_timing->setConnectEnd(
179      (load_timing.connect_timing.connect_end - kNullTicks).InSecondsF());
180  url_timing->setSSLStart(
181      (load_timing.connect_timing.ssl_start - kNullTicks).InSecondsF());
182  url_timing->setSSLEnd(
183      (load_timing.connect_timing.ssl_end - kNullTicks).InSecondsF());
184  url_timing->setSendStart(
185      (load_timing.send_start - kNullTicks).InSecondsF());
186  url_timing->setSendEnd(
187      (load_timing.send_end - kNullTicks).InSecondsF());
188  url_timing->setReceiveHeadersEnd(
189      (load_timing.receive_headers_end - kNullTicks).InSecondsF());
190}
191
192net::RequestPriority ConvertWebKitPriorityToNetPriority(
193    const WebURLRequest::Priority& priority) {
194  switch (priority) {
195    case WebURLRequest::PriorityVeryHigh:
196      return net::HIGHEST;
197
198    case WebURLRequest::PriorityHigh:
199      return net::MEDIUM;
200
201    case WebURLRequest::PriorityMedium:
202      return net::LOW;
203
204    case WebURLRequest::PriorityLow:
205      return net::LOWEST;
206
207    case WebURLRequest::PriorityVeryLow:
208      return net::IDLE;
209
210    case WebURLRequest::PriorityUnresolved:
211    default:
212      NOTREACHED();
213      return net::LOW;
214  }
215}
216
217}  // namespace
218
219// WebURLLoaderImpl::Context --------------------------------------------------
220
221// This inner class exists since the WebURLLoader may be deleted while inside a
222// call to WebURLLoaderClient.  The bridge requires its Peer to stay alive
223// until it receives OnCompletedRequest.
224class WebURLLoaderImpl::Context : public base::RefCounted<Context>,
225                                  public RequestPeer {
226 public:
227  explicit Context(WebURLLoaderImpl* loader);
228
229  WebURLLoaderClient* client() const { return client_; }
230  void set_client(WebURLLoaderClient* client) { client_ = client; }
231
232  void Cancel();
233  void SetDefersLoading(bool value);
234  void DidChangePriority(WebURLRequest::Priority new_priority,
235                         int intra_priority_value);
236  void Start(const WebURLRequest& request,
237             SyncLoadResponse* sync_load_response);
238
239  // RequestPeer methods:
240  virtual void OnUploadProgress(uint64 position, uint64 size) OVERRIDE;
241  virtual bool OnReceivedRedirect(
242      const GURL& new_url,
243      const ResourceResponseInfo& info,
244      bool* has_new_first_party_for_cookies,
245      GURL* new_first_party_for_cookies) OVERRIDE;
246  virtual void OnReceivedResponse(const ResourceResponseInfo& info) OVERRIDE;
247  virtual void OnDownloadedData(int len, int encoded_data_length) OVERRIDE;
248  virtual void OnReceivedData(const char* data,
249                              int data_length,
250                              int encoded_data_length) OVERRIDE;
251  virtual void OnReceivedCachedMetadata(const char* data, int len) OVERRIDE;
252  virtual void OnCompletedRequest(
253      int error_code,
254      bool was_ignored_by_handler,
255      bool stale_copy_in_cache,
256      const std::string& security_info,
257      const base::TimeTicks& completion_time,
258      int64 total_transfer_size) OVERRIDE;
259
260 private:
261  friend class base::RefCounted<Context>;
262  virtual ~Context() {}
263
264  // We can optimize the handling of data URLs in most cases.
265  bool CanHandleDataURL(const GURL& url) const;
266  void HandleDataURL();
267
268  WebURLLoaderImpl* loader_;
269  WebURLRequest request_;
270  WebURLLoaderClient* client_;
271  WebReferrerPolicy referrer_policy_;
272  scoped_ptr<ResourceLoaderBridge> bridge_;
273  scoped_ptr<FtpDirectoryListingResponseDelegate> ftp_listing_delegate_;
274  scoped_ptr<MultipartResponseDelegate> multipart_delegate_;
275  scoped_ptr<ResourceLoaderBridge> completed_bridge_;
276};
277
278WebURLLoaderImpl::Context::Context(WebURLLoaderImpl* loader)
279    : loader_(loader),
280      client_(NULL),
281      referrer_policy_(blink::WebReferrerPolicyDefault) {
282}
283
284void WebURLLoaderImpl::Context::Cancel() {
285  // The bridge will still send OnCompletedRequest, which will Release() us, so
286  // we don't do that here.
287  if (bridge_)
288    bridge_->Cancel();
289
290  // Ensure that we do not notify the multipart delegate anymore as it has
291  // its own pointer to the client.
292  if (multipart_delegate_)
293    multipart_delegate_->Cancel();
294
295  // Do not make any further calls to the client.
296  client_ = NULL;
297  loader_ = NULL;
298}
299
300void WebURLLoaderImpl::Context::SetDefersLoading(bool value) {
301  if (bridge_)
302    bridge_->SetDefersLoading(value);
303}
304
305void WebURLLoaderImpl::Context::DidChangePriority(
306    WebURLRequest::Priority new_priority, int intra_priority_value) {
307  if (bridge_)
308    bridge_->DidChangePriority(
309        ConvertWebKitPriorityToNetPriority(new_priority), intra_priority_value);
310}
311
312void WebURLLoaderImpl::Context::Start(const WebURLRequest& request,
313                                      SyncLoadResponse* sync_load_response) {
314  DCHECK(!bridge_.get());
315
316  request_ = request;  // Save the request.
317
318  GURL url = request.url();
319  if (url.SchemeIs("data") && CanHandleDataURL(url)) {
320    if (sync_load_response) {
321      // This is a sync load. Do the work now.
322      sync_load_response->url = url;
323      std::string data;
324      GetInfoFromDataURL(sync_load_response->url, sync_load_response,
325                         &sync_load_response->data,
326                         &sync_load_response->error_code);
327    } else {
328      AddRef();  // Balanced in OnCompletedRequest
329      base::MessageLoop::current()->PostTask(
330          FROM_HERE, base::Bind(&Context::HandleDataURL, this));
331    }
332    return;
333  }
334
335  GURL referrer_url(
336      request.httpHeaderField(WebString::fromUTF8("Referer")).latin1());
337  const std::string& method = request.httpMethod().latin1();
338
339  int load_flags = net::LOAD_NORMAL;
340  switch (request.cachePolicy()) {
341    case WebURLRequest::ReloadIgnoringCacheData:
342      // Required by LayoutTests/http/tests/misc/refresh-headers.php
343      load_flags |= net::LOAD_VALIDATE_CACHE;
344      break;
345    case WebURLRequest::ReturnCacheDataElseLoad:
346      load_flags |= net::LOAD_PREFERRING_CACHE;
347      break;
348    case WebURLRequest::ReturnCacheDataDontLoad:
349      load_flags |= net::LOAD_ONLY_FROM_CACHE;
350      break;
351    case WebURLRequest::UseProtocolCachePolicy:
352      break;
353  }
354
355  if (request.reportUploadProgress())
356    load_flags |= net::LOAD_ENABLE_UPLOAD_PROGRESS;
357  if (request.reportLoadTiming())
358    load_flags |= net::LOAD_ENABLE_LOAD_TIMING;
359  if (request.reportRawHeaders())
360    load_flags |= net::LOAD_REPORT_RAW_HEADERS;
361
362  if (!request.allowStoredCredentials()) {
363    load_flags |= net::LOAD_DO_NOT_SAVE_COOKIES;
364    load_flags |= net::LOAD_DO_NOT_SEND_COOKIES;
365  }
366
367  if (!request.allowStoredCredentials())
368    load_flags |= net::LOAD_DO_NOT_SEND_AUTH_DATA;
369
370  if (request.targetType() == WebURLRequest::TargetIsXHR &&
371      (url.has_username() || url.has_password())) {
372    load_flags |= net::LOAD_DO_NOT_PROMPT_FOR_LOGIN;
373  }
374
375  HeaderFlattener flattener(load_flags);
376  request.visitHTTPHeaderFields(&flattener);
377
378  // TODO(brettw) this should take parameter encoding into account when
379  // creating the GURLs.
380
381  RequestInfo request_info;
382  request_info.method = method;
383  request_info.url = url;
384  request_info.first_party_for_cookies = request.firstPartyForCookies();
385  request_info.referrer = referrer_url;
386  request_info.headers = flattener.GetBuffer();
387  request_info.load_flags = load_flags;
388  // requestor_pid only needs to be non-zero if the request originates outside
389  // the render process, so we can use requestorProcessID even for requests
390  // from in-process plugins.
391  request_info.requestor_pid = request.requestorProcessID();
392  request_info.request_type =
393      ResourceType::FromTargetType(request.targetType());
394  request_info.priority =
395      ConvertWebKitPriorityToNetPriority(request.priority());
396  request_info.appcache_host_id = request.appCacheHostID();
397  request_info.routing_id = request.requestorID();
398  request_info.download_to_file = request.downloadToFile();
399  request_info.has_user_gesture = request.hasUserGesture();
400  request_info.extra_data = request.extraData();
401  referrer_policy_ = request.referrerPolicy();
402  request_info.referrer_policy = request.referrerPolicy();
403  bridge_.reset(ChildThread::current()->CreateBridge(request_info));
404
405  if (!request.httpBody().isNull()) {
406    // GET and HEAD requests shouldn't have http bodies.
407    DCHECK(method != "GET" && method != "HEAD");
408    const WebHTTPBody& httpBody = request.httpBody();
409    size_t i = 0;
410    WebHTTPBody::Element element;
411    scoped_refptr<ResourceRequestBody> request_body = new ResourceRequestBody;
412    while (httpBody.elementAt(i++, element)) {
413      switch (element.type) {
414        case WebHTTPBody::Element::TypeData:
415          if (!element.data.isEmpty()) {
416            // WebKit sometimes gives up empty data to append. These aren't
417            // necessary so we just optimize those out here.
418            request_body->AppendBytes(
419                element.data.data(), static_cast<int>(element.data.size()));
420          }
421          break;
422        case WebHTTPBody::Element::TypeFile:
423          if (element.fileLength == -1) {
424            request_body->AppendFileRange(
425                base::FilePath::FromUTF16Unsafe(element.filePath),
426                0, kuint64max, base::Time());
427          } else {
428            request_body->AppendFileRange(
429                base::FilePath::FromUTF16Unsafe(element.filePath),
430                static_cast<uint64>(element.fileStart),
431                static_cast<uint64>(element.fileLength),
432                base::Time::FromDoubleT(element.modificationTime));
433          }
434          break;
435        case WebHTTPBody::Element::TypeFileSystemURL: {
436          GURL file_system_url = element.fileSystemURL;
437          DCHECK(file_system_url.SchemeIsFileSystem());
438          request_body->AppendFileSystemFileRange(
439              file_system_url,
440              static_cast<uint64>(element.fileStart),
441              static_cast<uint64>(element.fileLength),
442              base::Time::FromDoubleT(element.modificationTime));
443          break;
444        }
445        case WebHTTPBody::Element::TypeBlob:
446          request_body->AppendBlob(element.blobUUID.utf8());
447          break;
448        default:
449          NOTREACHED();
450      }
451    }
452    request_body->set_identifier(request.httpBody().identifier());
453    bridge_->SetRequestBody(request_body.get());
454  }
455
456  if (sync_load_response) {
457    bridge_->SyncLoad(sync_load_response);
458    return;
459  }
460
461  if (bridge_->Start(this)) {
462    AddRef();  // Balanced in OnCompletedRequest
463  } else {
464    bridge_.reset();
465  }
466}
467
468void WebURLLoaderImpl::Context::OnUploadProgress(uint64 position, uint64 size) {
469  if (client_)
470    client_->didSendData(loader_, position, size);
471}
472
473bool WebURLLoaderImpl::Context::OnReceivedRedirect(
474    const GURL& new_url,
475    const ResourceResponseInfo& info,
476    bool* has_new_first_party_for_cookies,
477    GURL* new_first_party_for_cookies) {
478  if (!client_)
479    return false;
480
481  WebURLResponse response;
482  response.initialize();
483  PopulateURLResponse(request_.url(), info, &response);
484
485  // TODO(darin): We lack sufficient information to construct the actual
486  // request that resulted from the redirect.
487  WebURLRequest new_request(new_url);
488  new_request.setFirstPartyForCookies(request_.firstPartyForCookies());
489  new_request.setDownloadToFile(request_.downloadToFile());
490
491  WebString referrer_string = WebString::fromUTF8("Referer");
492  WebString referrer = WebSecurityPolicy::generateReferrerHeader(
493      referrer_policy_,
494      new_url,
495      request_.httpHeaderField(referrer_string));
496  if (!referrer.isEmpty())
497    new_request.setHTTPReferrer(referrer, referrer_policy_);
498
499  std::string method = request_.httpMethod().utf8();
500  std::string new_method = net::URLRequest::ComputeMethodForRedirect(
501      method, response.httpStatusCode());
502  new_request.setHTTPMethod(WebString::fromUTF8(new_method));
503  if (new_method == method)
504    new_request.setHTTPBody(request_.httpBody());
505
506  client_->willSendRequest(loader_, new_request, response);
507  request_ = new_request;
508  *has_new_first_party_for_cookies = true;
509  *new_first_party_for_cookies = request_.firstPartyForCookies();
510
511  // Only follow the redirect if WebKit left the URL unmodified.
512  if (new_url == GURL(new_request.url()))
513    return true;
514
515  // We assume that WebKit only changes the URL to suppress a redirect, and we
516  // assume that it does so by setting it to be invalid.
517  DCHECK(!new_request.url().isValid());
518  return false;
519}
520
521void WebURLLoaderImpl::Context::OnReceivedResponse(
522    const ResourceResponseInfo& info) {
523  if (!client_)
524    return;
525
526  WebURLResponse response;
527  response.initialize();
528  PopulateURLResponse(request_.url(), info, &response);
529
530  bool show_raw_listing = (GURL(request_.url()).query() == "raw");
531
532  if (info.mime_type == "text/vnd.chromium.ftp-dir") {
533    if (show_raw_listing) {
534      // Set the MIME type to plain text to prevent any active content.
535      response.setMIMEType("text/plain");
536    } else {
537      // We're going to produce a parsed listing in HTML.
538      response.setMIMEType("text/html");
539    }
540  }
541
542  scoped_refptr<Context> protect(this);
543  client_->didReceiveResponse(loader_, response);
544
545  // We may have been cancelled after didReceiveResponse, which would leave us
546  // without a client and therefore without much need to do further handling.
547  if (!client_)
548    return;
549
550  DCHECK(!ftp_listing_delegate_.get());
551  DCHECK(!multipart_delegate_.get());
552  if (info.headers.get() && info.mime_type == "multipart/x-mixed-replace") {
553    std::string content_type;
554    info.headers->EnumerateHeader(NULL, "content-type", &content_type);
555
556    std::string mime_type;
557    std::string charset;
558    bool had_charset = false;
559    std::string boundary;
560    net::HttpUtil::ParseContentType(content_type, &mime_type, &charset,
561                                    &had_charset, &boundary);
562    base::TrimString(boundary, " \"", &boundary);
563
564    // If there's no boundary, just handle the request normally.  In the gecko
565    // code, nsMultiMixedConv::OnStartRequest throws an exception.
566    if (!boundary.empty()) {
567      multipart_delegate_.reset(
568          new MultipartResponseDelegate(client_, loader_, response, boundary));
569    }
570  } else if (info.mime_type == "text/vnd.chromium.ftp-dir" &&
571             !show_raw_listing) {
572    ftp_listing_delegate_.reset(
573        new FtpDirectoryListingResponseDelegate(client_, loader_, response));
574  }
575}
576
577void WebURLLoaderImpl::Context::OnDownloadedData(int len,
578                                                 int encoded_data_length) {
579  if (client_)
580    client_->didDownloadData(loader_, len, encoded_data_length);
581}
582
583void WebURLLoaderImpl::Context::OnReceivedData(const char* data,
584                                               int data_length,
585                                               int encoded_data_length) {
586  if (!client_)
587    return;
588
589  if (ftp_listing_delegate_) {
590    // The FTP listing delegate will make the appropriate calls to
591    // client_->didReceiveData and client_->didReceiveResponse.
592    ftp_listing_delegate_->OnReceivedData(data, data_length);
593  } else if (multipart_delegate_) {
594    // The multipart delegate will make the appropriate calls to
595    // client_->didReceiveData and client_->didReceiveResponse.
596    multipart_delegate_->OnReceivedData(data, data_length, encoded_data_length);
597  } else {
598    client_->didReceiveData(loader_, data, data_length, encoded_data_length);
599  }
600}
601
602void WebURLLoaderImpl::Context::OnReceivedCachedMetadata(
603    const char* data, int len) {
604  if (client_)
605    client_->didReceiveCachedMetadata(loader_, data, len);
606}
607
608void WebURLLoaderImpl::Context::OnCompletedRequest(
609    int error_code,
610    bool was_ignored_by_handler,
611    bool stale_copy_in_cache,
612    const std::string& security_info,
613    const base::TimeTicks& completion_time,
614    int64 total_transfer_size) {
615  if (ftp_listing_delegate_) {
616    ftp_listing_delegate_->OnCompletedRequest();
617    ftp_listing_delegate_.reset(NULL);
618  } else if (multipart_delegate_) {
619    multipart_delegate_->OnCompletedRequest();
620    multipart_delegate_.reset(NULL);
621  }
622
623  // Prevent any further IPC to the browser now that we're complete, but
624  // don't delete it to keep any downloaded temp files alive.
625  DCHECK(!completed_bridge_.get());
626  completed_bridge_.swap(bridge_);
627
628  if (client_) {
629    if (error_code != net::OK) {
630      client_->didFail(loader_, CreateError(request_.url(),
631                                            stale_copy_in_cache,
632                                            error_code));
633    } else {
634      client_->didFinishLoading(
635          loader_, (completion_time - TimeTicks()).InSecondsF(),
636          total_transfer_size);
637    }
638  }
639
640  // We are done with the bridge now, and so we need to release the reference
641  // to ourselves that we took on behalf of the bridge.  This may cause our
642  // destruction.
643  Release();
644}
645
646bool WebURLLoaderImpl::Context::CanHandleDataURL(const GURL& url) const {
647  DCHECK(url.SchemeIs("data"));
648
649  // Optimize for the case where we can handle a data URL locally.  We must
650  // skip this for data URLs targetted at frames since those could trigger a
651  // download.
652  //
653  // NOTE: We special case MIME types we can render both for performance
654  // reasons as well as to support unit tests, which do not have an underlying
655  // ResourceLoaderBridge implementation.
656
657#if defined(OS_ANDROID)
658  // For compatibility reasons on Android we need to expose top-level data://
659  // to the browser.
660  if (request_.targetType() == WebURLRequest::TargetIsMainFrame)
661    return false;
662#endif
663
664  if (request_.targetType() != WebURLRequest::TargetIsMainFrame &&
665      request_.targetType() != WebURLRequest::TargetIsSubframe)
666    return true;
667
668  std::string mime_type, unused_charset;
669  if (net::DataURL::Parse(url, &mime_type, &unused_charset, NULL) &&
670      net::IsSupportedMimeType(mime_type))
671    return true;
672
673  return false;
674}
675
676void WebURLLoaderImpl::Context::HandleDataURL() {
677  ResourceResponseInfo info;
678  int error_code;
679  std::string data;
680
681  if (GetInfoFromDataURL(request_.url(), &info, &data, &error_code)) {
682    OnReceivedResponse(info);
683    if (!data.empty())
684      OnReceivedData(data.data(), data.size(), 0);
685  }
686
687  OnCompletedRequest(error_code, false, false, info.security_info,
688                     base::TimeTicks::Now(), 0);
689}
690
691// WebURLLoaderImpl -----------------------------------------------------------
692
693WebURLLoaderImpl::WebURLLoaderImpl()
694    : context_(new Context(this)) {
695}
696
697WebURLLoaderImpl::~WebURLLoaderImpl() {
698  cancel();
699}
700
701WebURLError WebURLLoaderImpl::CreateError(const WebURL& unreachable_url,
702                                          bool stale_copy_in_cache,
703                                          int reason) {
704  WebURLError error;
705  error.domain = WebString::fromUTF8(net::kErrorDomain);
706  error.reason = reason;
707  error.unreachableURL = unreachable_url;
708  error.staleCopyInCache = stale_copy_in_cache;
709  if (reason == net::ERR_ABORTED) {
710    error.isCancellation = true;
711  } else if (reason == net::ERR_TEMPORARILY_THROTTLED) {
712    error.localizedDescription = WebString::fromUTF8(
713        kThrottledErrorDescription);
714  } else {
715    error.localizedDescription = WebString::fromUTF8(
716        net::ErrorToString(reason));
717  }
718  return error;
719}
720
721void WebURLLoaderImpl::PopulateURLResponse(const GURL& url,
722                                           const ResourceResponseInfo& info,
723                                           WebURLResponse* response) {
724  response->setURL(url);
725  response->setResponseTime(info.response_time.ToDoubleT());
726  response->setMIMEType(WebString::fromUTF8(info.mime_type));
727  response->setTextEncodingName(WebString::fromUTF8(info.charset));
728  response->setExpectedContentLength(info.content_length);
729  response->setSecurityInfo(info.security_info);
730  response->setAppCacheID(info.appcache_id);
731  response->setAppCacheManifestURL(info.appcache_manifest_url);
732  response->setWasCached(!info.load_timing.request_start_time.is_null() &&
733      info.response_time < info.load_timing.request_start_time);
734  response->setRemoteIPAddress(
735      WebString::fromUTF8(info.socket_address.host()));
736  response->setRemotePort(info.socket_address.port());
737  response->setConnectionID(info.load_timing.socket_log_id);
738  response->setConnectionReused(info.load_timing.socket_reused);
739  response->setDownloadFilePath(info.download_file_path.AsUTF16Unsafe());
740  WebURLResponseExtraDataImpl* extra_data =
741      new WebURLResponseExtraDataImpl(info.npn_negotiated_protocol);
742  response->setExtraData(extra_data);
743  extra_data->set_was_fetched_via_spdy(info.was_fetched_via_spdy);
744  extra_data->set_was_npn_negotiated(info.was_npn_negotiated);
745  extra_data->set_was_alternate_protocol_available(
746      info.was_alternate_protocol_available);
747  extra_data->set_connection_info(info.connection_info);
748  extra_data->set_was_fetched_via_proxy(info.was_fetched_via_proxy);
749
750  // If there's no received headers end time, don't set load timing.  This is
751  // the case for non-HTTP requests, requests that don't go over the wire, and
752  // certain error cases.
753  if (!info.load_timing.receive_headers_end.is_null()) {
754    WebURLLoadTiming timing;
755    PopulateURLLoadTiming(info.load_timing, &timing);
756    response->setLoadTiming(timing);
757  }
758
759  if (info.devtools_info.get()) {
760    WebHTTPLoadInfo load_info;
761
762    load_info.setHTTPStatusCode(info.devtools_info->http_status_code);
763    load_info.setHTTPStatusText(WebString::fromLatin1(
764        info.devtools_info->http_status_text));
765    load_info.setEncodedDataLength(info.encoded_data_length);
766
767    load_info.setRequestHeadersText(WebString::fromLatin1(
768        info.devtools_info->request_headers_text));
769    load_info.setResponseHeadersText(WebString::fromLatin1(
770        info.devtools_info->response_headers_text));
771    const HeadersVector& request_headers = info.devtools_info->request_headers;
772    for (HeadersVector::const_iterator it = request_headers.begin();
773         it != request_headers.end(); ++it) {
774      load_info.addRequestHeader(WebString::fromLatin1(it->first),
775          WebString::fromLatin1(it->second));
776    }
777    const HeadersVector& response_headers =
778        info.devtools_info->response_headers;
779    for (HeadersVector::const_iterator it = response_headers.begin();
780         it != response_headers.end(); ++it) {
781      load_info.addResponseHeader(WebString::fromLatin1(it->first),
782          WebString::fromLatin1(it->second));
783    }
784    response->setHTTPLoadInfo(load_info);
785  }
786
787  const net::HttpResponseHeaders* headers = info.headers.get();
788  if (!headers)
789    return;
790
791  WebURLResponse::HTTPVersion version = WebURLResponse::Unknown;
792  if (headers->GetHttpVersion() == net::HttpVersion(0, 9))
793    version = WebURLResponse::HTTP_0_9;
794  else if (headers->GetHttpVersion() == net::HttpVersion(1, 0))
795    version = WebURLResponse::HTTP_1_0;
796  else if (headers->GetHttpVersion() == net::HttpVersion(1, 1))
797    version = WebURLResponse::HTTP_1_1;
798  response->setHTTPVersion(version);
799  response->setHTTPStatusCode(headers->response_code());
800  response->setHTTPStatusText(WebString::fromLatin1(headers->GetStatusText()));
801
802  // TODO(darin): We should leverage HttpResponseHeaders for this, and this
803  // should be using the same code as ResourceDispatcherHost.
804  // TODO(jungshik): Figure out the actual value of the referrer charset and
805  // pass it to GetSuggestedFilename.
806  std::string value;
807  headers->EnumerateHeader(NULL, "content-disposition", &value);
808  response->setSuggestedFileName(
809      net::GetSuggestedFilename(url,
810                                value,
811                                std::string(),  // referrer_charset
812                                std::string(),  // suggested_name
813                                std::string(),  // mime_type
814                                std::string()));  // default_name
815
816  Time time_val;
817  if (headers->GetLastModifiedValue(&time_val))
818    response->setLastModifiedDate(time_val.ToDoubleT());
819
820  // Build up the header map.
821  void* iter = NULL;
822  std::string name;
823  while (headers->EnumerateHeaderLines(&iter, &name, &value)) {
824    response->addHTTPHeaderField(WebString::fromLatin1(name),
825                                 WebString::fromLatin1(value));
826  }
827}
828
829void WebURLLoaderImpl::loadSynchronously(const WebURLRequest& request,
830                                         WebURLResponse& response,
831                                         WebURLError& error,
832                                         WebData& data) {
833  SyncLoadResponse sync_load_response;
834  context_->Start(request, &sync_load_response);
835
836  const GURL& final_url = sync_load_response.url;
837
838  // TODO(tc): For file loads, we may want to include a more descriptive
839  // status code or status text.
840  int error_code = sync_load_response.error_code;
841  if (error_code != net::OK) {
842    response.setURL(final_url);
843    error.domain = WebString::fromUTF8(net::kErrorDomain);
844    error.reason = error_code;
845    error.unreachableURL = final_url;
846    return;
847  }
848
849  PopulateURLResponse(final_url, sync_load_response, &response);
850
851  data.assign(sync_load_response.data.data(),
852              sync_load_response.data.size());
853}
854
855void WebURLLoaderImpl::loadAsynchronously(const WebURLRequest& request,
856                                          WebURLLoaderClient* client) {
857  DCHECK(!context_->client());
858
859  context_->set_client(client);
860  context_->Start(request, NULL);
861}
862
863void WebURLLoaderImpl::cancel() {
864  context_->Cancel();
865}
866
867void WebURLLoaderImpl::setDefersLoading(bool value) {
868  context_->SetDefersLoading(value);
869}
870
871void WebURLLoaderImpl::didChangePriority(WebURLRequest::Priority new_priority,
872                                         int intra_priority_value) {
873  context_->DidChangePriority(new_priority, intra_priority_value);
874}
875
876}  // namespace content
877