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.
5 // An implementation of WebURLLoader in terms of ResourceLoaderBridge.
7 #include "content/child/web_url_loader_impl.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/ftp_directory_listing_response_delegate.h"
16 #include "content/child/multipart_response_delegate.h"
17 #include "content/child/request_extra_data.h"
18 #include "content/child/request_info.h"
19 #include "content/child/resource_dispatcher.h"
20 #include "content/child/resource_loader_bridge.h"
21 #include "content/child/sync_load_response.h"
22 #include "content/child/web_url_request_util.h"
23 #include "content/child/weburlresponse_extradata_impl.h"
24 #include "content/common/resource_request_body.h"
25 #include "content/public/child/request_peer.h"
26 #include "net/base/data_url.h"
27 #include "net/base/filename_util.h"
28 #include "net/base/load_flags.h"
29 #include "net/base/mime_util.h"
30 #include "net/base/net_errors.h"
31 #include "net/http/http_response_headers.h"
32 #include "net/http/http_util.h"
33 #include "net/url_request/redirect_info.h"
34 #include "net/url_request/url_request_data_job.h"
35 #include "third_party/WebKit/public/platform/WebHTTPHeaderVisitor.h"
36 #include "third_party/WebKit/public/platform/WebHTTPLoadInfo.h"
37 #include "third_party/WebKit/public/platform/WebURL.h"
38 #include "third_party/WebKit/public/platform/WebURLError.h"
39 #include "third_party/WebKit/public/platform/WebURLLoadTiming.h"
40 #include "third_party/WebKit/public/platform/WebURLLoaderClient.h"
41 #include "third_party/WebKit/public/platform/WebURLRequest.h"
42 #include "third_party/WebKit/public/platform/WebURLResponse.h"
43 #include "third_party/WebKit/public/web/WebSecurityPolicy.h"
46 using base::TimeTicks
;
48 using blink::WebHTTPBody
;
49 using blink::WebHTTPHeaderVisitor
;
50 using blink::WebHTTPLoadInfo
;
51 using blink::WebReferrerPolicy
;
52 using blink::WebSecurityPolicy
;
53 using blink::WebString
;
55 using blink::WebURLError
;
56 using blink::WebURLLoadTiming
;
57 using blink::WebURLLoader
;
58 using blink::WebURLLoaderClient
;
59 using blink::WebURLRequest
;
60 using blink::WebURLResponse
;
64 // Utilities ------------------------------------------------------------------
68 const char kThrottledErrorDescription
[] =
69 "Request throttled. Visit http://dev.chromium.org/throttling for more "
72 class HeaderFlattener
: public WebHTTPHeaderVisitor
{
74 HeaderFlattener() : has_accept_header_(false) {}
76 virtual void visitHeader(const WebString
& name
, const WebString
& value
) {
77 // Headers are latin1.
78 const std::string
& name_latin1
= name
.latin1();
79 const std::string
& value_latin1
= value
.latin1();
81 // Skip over referrer headers found in the header map because we already
82 // pulled it out as a separate parameter.
83 if (LowerCaseEqualsASCII(name_latin1
, "referer"))
86 if (LowerCaseEqualsASCII(name_latin1
, "accept"))
87 has_accept_header_
= true;
90 buffer_
.append("\r\n");
91 buffer_
.append(name_latin1
+ ": " + value_latin1
);
94 const std::string
& GetBuffer() {
95 // In some cases, WebKit doesn't add an Accept header, but not having the
96 // header confuses some web servers. See bug 808613.
97 if (!has_accept_header_
) {
99 buffer_
.append("\r\n");
100 buffer_
.append("Accept: */*");
101 has_accept_header_
= true;
108 bool has_accept_header_
;
111 typedef ResourceDevToolsInfo::HeadersVector HeadersVector
;
113 // Converts timing data from |load_timing| to the format used by WebKit.
114 void PopulateURLLoadTiming(const net::LoadTimingInfo
& load_timing
,
115 WebURLLoadTiming
* url_timing
) {
116 DCHECK(!load_timing
.request_start
.is_null());
118 const TimeTicks kNullTicks
;
119 url_timing
->initialize();
120 url_timing
->setRequestTime(
121 (load_timing
.request_start
- kNullTicks
).InSecondsF());
122 url_timing
->setProxyStart(
123 (load_timing
.proxy_resolve_start
- kNullTicks
).InSecondsF());
124 url_timing
->setProxyEnd(
125 (load_timing
.proxy_resolve_end
- kNullTicks
).InSecondsF());
126 url_timing
->setDNSStart(
127 (load_timing
.connect_timing
.dns_start
- kNullTicks
).InSecondsF());
128 url_timing
->setDNSEnd(
129 (load_timing
.connect_timing
.dns_end
- kNullTicks
).InSecondsF());
130 url_timing
->setConnectStart(
131 (load_timing
.connect_timing
.connect_start
- kNullTicks
).InSecondsF());
132 url_timing
->setConnectEnd(
133 (load_timing
.connect_timing
.connect_end
- kNullTicks
).InSecondsF());
134 url_timing
->setSSLStart(
135 (load_timing
.connect_timing
.ssl_start
- kNullTicks
).InSecondsF());
136 url_timing
->setSSLEnd(
137 (load_timing
.connect_timing
.ssl_end
- kNullTicks
).InSecondsF());
138 url_timing
->setSendStart(
139 (load_timing
.send_start
- kNullTicks
).InSecondsF());
140 url_timing
->setSendEnd(
141 (load_timing
.send_end
- kNullTicks
).InSecondsF());
142 url_timing
->setReceiveHeadersEnd(
143 (load_timing
.receive_headers_end
- kNullTicks
).InSecondsF());
146 net::RequestPriority
ConvertWebKitPriorityToNetPriority(
147 const WebURLRequest::Priority
& priority
) {
149 case WebURLRequest::PriorityVeryHigh
:
152 case WebURLRequest::PriorityHigh
:
155 case WebURLRequest::PriorityMedium
:
158 case WebURLRequest::PriorityLow
:
161 case WebURLRequest::PriorityVeryLow
:
164 case WebURLRequest::PriorityUnresolved
:
171 // Extracts info from a data scheme URL into |info| and |data|. Returns net::OK
172 // if successful. Returns a net error code otherwise. Exported only for testing.
173 int GetInfoFromDataURL(const GURL
& url
,
174 ResourceResponseInfo
* info
,
176 // Assure same time for all time fields of data: URLs.
177 Time now
= Time::Now();
178 info
->load_timing
.request_start
= TimeTicks::Now();
179 info
->load_timing
.request_start_time
= now
;
180 info
->request_time
= now
;
181 info
->response_time
= now
;
183 std::string mime_type
;
185 scoped_refptr
<net::HttpResponseHeaders
> headers(
186 new net::HttpResponseHeaders(std::string()));
187 int result
= net::URLRequestDataJob::BuildResponse(
188 url
, &mime_type
, &charset
, data
, headers
.get());
189 if (result
!= net::OK
)
192 info
->headers
= headers
;
193 info
->mime_type
.swap(mime_type
);
194 info
->charset
.swap(charset
);
195 info
->security_info
.clear();
196 info
->content_length
= data
->length();
197 info
->encoded_data_length
= 0;
204 // WebURLLoaderImpl::Context --------------------------------------------------
206 // This inner class exists since the WebURLLoader may be deleted while inside a
207 // call to WebURLLoaderClient. Refcounting is to keep the context from being
208 // deleted if it may have work to do after calling into the client.
209 class WebURLLoaderImpl::Context
: public base::RefCounted
<Context
>,
212 Context(WebURLLoaderImpl
* loader
, ResourceDispatcher
* resource_dispatcher
);
214 WebURLLoaderClient
* client() const { return client_
; }
215 void set_client(WebURLLoaderClient
* client
) { client_
= client
; }
218 void SetDefersLoading(bool value
);
219 void DidChangePriority(WebURLRequest::Priority new_priority
,
220 int intra_priority_value
);
221 bool AttachThreadedDataReceiver(
222 blink::WebThreadedDataReceiver
* threaded_data_receiver
);
223 void Start(const WebURLRequest
& request
,
224 SyncLoadResponse
* sync_load_response
);
226 // RequestPeer methods:
227 virtual void OnUploadProgress(uint64 position
, uint64 size
) OVERRIDE
;
228 virtual bool OnReceivedRedirect(const net::RedirectInfo
& redirect_info
,
229 const ResourceResponseInfo
& info
) OVERRIDE
;
230 virtual void OnReceivedResponse(const ResourceResponseInfo
& info
) OVERRIDE
;
231 virtual void OnDownloadedData(int len
, int encoded_data_length
) OVERRIDE
;
232 virtual void OnReceivedData(const char* data
,
234 int encoded_data_length
) OVERRIDE
;
235 virtual void OnReceivedCachedMetadata(const char* data
, int len
) OVERRIDE
;
236 virtual void OnCompletedRequest(
238 bool was_ignored_by_handler
,
239 bool stale_copy_in_cache
,
240 const std::string
& security_info
,
241 const base::TimeTicks
& completion_time
,
242 int64 total_transfer_size
) OVERRIDE
;
245 friend class base::RefCounted
<Context
>;
246 virtual ~Context() {}
248 // We can optimize the handling of data URLs in most cases.
249 bool CanHandleDataURLRequestLocally() const;
250 void HandleDataURL();
252 WebURLLoaderImpl
* loader_
;
253 WebURLRequest request_
;
254 WebURLLoaderClient
* client_
;
255 ResourceDispatcher
* resource_dispatcher_
;
256 WebReferrerPolicy referrer_policy_
;
257 scoped_ptr
<ResourceLoaderBridge
> bridge_
;
258 scoped_ptr
<FtpDirectoryListingResponseDelegate
> ftp_listing_delegate_
;
259 scoped_ptr
<MultipartResponseDelegate
> multipart_delegate_
;
260 scoped_ptr
<ResourceLoaderBridge
> completed_bridge_
;
263 WebURLLoaderImpl::Context::Context(WebURLLoaderImpl
* loader
,
264 ResourceDispatcher
* resource_dispatcher
)
267 resource_dispatcher_(resource_dispatcher
),
268 referrer_policy_(blink::WebReferrerPolicyDefault
) {
271 void WebURLLoaderImpl::Context::Cancel() {
277 // Ensure that we do not notify the multipart delegate anymore as it has
278 // its own pointer to the client.
279 if (multipart_delegate_
)
280 multipart_delegate_
->Cancel();
281 // Ditto for the ftp delegate.
282 if (ftp_listing_delegate_
)
283 ftp_listing_delegate_
->Cancel();
285 // Do not make any further calls to the client.
290 void WebURLLoaderImpl::Context::SetDefersLoading(bool value
) {
292 bridge_
->SetDefersLoading(value
);
295 void WebURLLoaderImpl::Context::DidChangePriority(
296 WebURLRequest::Priority new_priority
, int intra_priority_value
) {
298 bridge_
->DidChangePriority(
299 ConvertWebKitPriorityToNetPriority(new_priority
), intra_priority_value
);
302 bool WebURLLoaderImpl::Context::AttachThreadedDataReceiver(
303 blink::WebThreadedDataReceiver
* threaded_data_receiver
) {
305 return bridge_
->AttachThreadedDataReceiver(threaded_data_receiver
);
310 void WebURLLoaderImpl::Context::Start(const WebURLRequest
& request
,
311 SyncLoadResponse
* sync_load_response
) {
312 DCHECK(!bridge_
.get());
314 request_
= request
; // Save the request.
316 GURL url
= request
.url();
317 if (CanHandleDataURLRequestLocally()) {
318 if (sync_load_response
) {
319 // This is a sync load. Do the work now.
320 sync_load_response
->url
= url
;
321 sync_load_response
->error_code
=
322 GetInfoFromDataURL(sync_load_response
->url
, sync_load_response
,
323 &sync_load_response
->data
);
325 base::MessageLoop::current()->PostTask(
326 FROM_HERE
, base::Bind(&Context::HandleDataURL
, this));
332 request
.httpHeaderField(WebString::fromUTF8("Referer")).latin1());
333 const std::string
& method
= request
.httpMethod().latin1();
335 int load_flags
= net::LOAD_NORMAL
;
336 switch (request
.cachePolicy()) {
337 case WebURLRequest::ReloadIgnoringCacheData
:
338 // Required by LayoutTests/http/tests/misc/refresh-headers.php
339 load_flags
|= net::LOAD_VALIDATE_CACHE
;
341 case WebURLRequest::ReloadBypassingCache
:
342 load_flags
|= net::LOAD_BYPASS_CACHE
;
344 case WebURLRequest::ReturnCacheDataElseLoad
:
345 load_flags
|= net::LOAD_PREFERRING_CACHE
;
347 case WebURLRequest::ReturnCacheDataDontLoad
:
348 load_flags
|= net::LOAD_ONLY_FROM_CACHE
;
350 case WebURLRequest::UseProtocolCachePolicy
:
356 if (request
.reportUploadProgress())
357 load_flags
|= net::LOAD_ENABLE_UPLOAD_PROGRESS
;
358 if (request
.reportRawHeaders())
359 load_flags
|= net::LOAD_REPORT_RAW_HEADERS
;
361 if (!request
.allowStoredCredentials()) {
362 load_flags
|= net::LOAD_DO_NOT_SAVE_COOKIES
;
363 load_flags
|= net::LOAD_DO_NOT_SEND_COOKIES
;
366 if (!request
.allowStoredCredentials())
367 load_flags
|= net::LOAD_DO_NOT_SEND_AUTH_DATA
;
369 if (request
.requestContext() == WebURLRequest::RequestContextXMLHttpRequest
&&
370 (url
.has_username() || url
.has_password())) {
371 load_flags
|= net::LOAD_DO_NOT_PROMPT_FOR_LOGIN
;
374 HeaderFlattener flattener
;
375 request
.visitHTTPHeaderFields(&flattener
);
377 // TODO(brettw) this should take parameter encoding into account when
378 // creating the GURLs.
380 RequestInfo request_info
;
381 request_info
.method
= method
;
382 request_info
.url
= url
;
383 request_info
.first_party_for_cookies
= request
.firstPartyForCookies();
384 request_info
.referrer
= referrer_url
;
385 request_info
.headers
= flattener
.GetBuffer();
386 request_info
.load_flags
= load_flags
;
387 request_info
.enable_load_timing
= true;
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
= WebURLRequestToResourceType(request
);
393 request_info
.priority
=
394 ConvertWebKitPriorityToNetPriority(request
.priority());
395 request_info
.appcache_host_id
= request
.appCacheHostID();
396 request_info
.routing_id
= request
.requestorID();
397 request_info
.download_to_file
= request
.downloadToFile();
398 request_info
.has_user_gesture
= request
.hasUserGesture();
399 request_info
.skip_service_worker
= request
.skipServiceWorker();
400 request_info
.extra_data
= request
.extraData();
401 referrer_policy_
= request
.referrerPolicy();
402 request_info
.referrer_policy
= request
.referrerPolicy();
403 bridge_
.reset(resource_dispatcher_
->CreateBridge(request_info
));
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();
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()));
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());
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
));
435 case WebHTTPBody::Element::TypeFileSystemURL
: {
436 GURL file_system_url
= element
.fileSystemURL
;
437 DCHECK(file_system_url
.SchemeIsFileSystem());
438 request_body
->AppendFileSystemFileRange(
440 static_cast<uint64
>(element
.fileStart
),
441 static_cast<uint64
>(element
.fileLength
),
442 base::Time::FromDoubleT(element
.modificationTime
));
445 case WebHTTPBody::Element::TypeBlob
:
446 request_body
->AppendBlob(element
.blobUUID
.utf8());
452 request_body
->set_identifier(request
.httpBody().identifier());
453 bridge_
->SetRequestBody(request_body
.get());
456 if (sync_load_response
) {
457 bridge_
->SyncLoad(sync_load_response
);
461 // TODO(mmenke): This case probably never happens, anyways. Probably should
462 // not handle this case at all. If it's worth handling, this code currently
463 // results in the request just hanging, which should be fixed.
464 if (!bridge_
->Start(this))
468 void WebURLLoaderImpl::Context::OnUploadProgress(uint64 position
, uint64 size
) {
470 client_
->didSendData(loader_
, position
, size
);
473 bool WebURLLoaderImpl::Context::OnReceivedRedirect(
474 const net::RedirectInfo
& redirect_info
,
475 const ResourceResponseInfo
& info
) {
479 WebURLResponse response
;
480 response
.initialize();
481 PopulateURLResponse(request_
.url(), info
, &response
);
483 // TODO(darin): We lack sufficient information to construct the actual
484 // request that resulted from the redirect.
485 WebURLRequest
new_request(redirect_info
.new_url
);
486 new_request
.setFirstPartyForCookies(
487 redirect_info
.new_first_party_for_cookies
);
488 new_request
.setDownloadToFile(request_
.downloadToFile());
490 new_request
.setHTTPReferrer(WebString::fromUTF8(redirect_info
.new_referrer
),
493 std::string old_method
= request_
.httpMethod().utf8();
494 new_request
.setHTTPMethod(WebString::fromUTF8(redirect_info
.new_method
));
495 if (redirect_info
.new_method
== old_method
)
496 new_request
.setHTTPBody(request_
.httpBody());
498 // Protect from deletion during call to willSendRequest.
499 scoped_refptr
<Context
> protect(this);
501 client_
->willSendRequest(loader_
, new_request
, response
);
502 request_
= new_request
;
504 // Only follow the redirect if WebKit left the URL unmodified.
505 if (redirect_info
.new_url
== GURL(new_request
.url())) {
506 // First-party cookie logic moved from DocumentLoader in Blink to
507 // net::URLRequest in the browser. Assert that Blink didn't try to change it
508 // to something else.
509 DCHECK_EQ(redirect_info
.new_first_party_for_cookies
.spec(),
510 request_
.firstPartyForCookies().string().utf8());
514 // We assume that WebKit only changes the URL to suppress a redirect, and we
515 // assume that it does so by setting it to be invalid.
516 DCHECK(!new_request
.url().isValid());
520 void WebURLLoaderImpl::Context::OnReceivedResponse(
521 const ResourceResponseInfo
& info
) {
525 WebURLResponse response
;
526 response
.initialize();
527 // Updates the request url if the response was fetched by a ServiceWorker,
528 // and it was not generated inside the ServiceWorker.
529 if (info
.was_fetched_via_service_worker
&&
530 !info
.original_url_via_service_worker
.is_empty()) {
531 request_
.setURL(info
.original_url_via_service_worker
);
533 PopulateURLResponse(request_
.url(), info
, &response
);
535 bool show_raw_listing
= (GURL(request_
.url()).query() == "raw");
537 if (info
.mime_type
== "text/vnd.chromium.ftp-dir") {
538 if (show_raw_listing
) {
539 // Set the MIME type to plain text to prevent any active content.
540 response
.setMIMEType("text/plain");
542 // We're going to produce a parsed listing in HTML.
543 response
.setMIMEType("text/html");
547 // Prevent |this| from being destroyed if the client destroys the loader,
548 // ether in didReceiveResponse, or when the multipart/ftp delegate calls into
550 scoped_refptr
<Context
> protect(this);
551 client_
->didReceiveResponse(loader_
, response
);
553 // We may have been cancelled after didReceiveResponse, which would leave us
554 // without a client and therefore without much need to do further handling.
558 DCHECK(!ftp_listing_delegate_
.get());
559 DCHECK(!multipart_delegate_
.get());
560 if (info
.headers
.get() && info
.mime_type
== "multipart/x-mixed-replace") {
561 std::string content_type
;
562 info
.headers
->EnumerateHeader(NULL
, "content-type", &content_type
);
564 std::string mime_type
;
566 bool had_charset
= false;
567 std::string boundary
;
568 net::HttpUtil::ParseContentType(content_type
, &mime_type
, &charset
,
569 &had_charset
, &boundary
);
570 base::TrimString(boundary
, " \"", &boundary
);
572 // If there's no boundary, just handle the request normally. In the gecko
573 // code, nsMultiMixedConv::OnStartRequest throws an exception.
574 if (!boundary
.empty()) {
575 multipart_delegate_
.reset(
576 new MultipartResponseDelegate(client_
, loader_
, response
, boundary
));
578 } else if (info
.mime_type
== "text/vnd.chromium.ftp-dir" &&
580 ftp_listing_delegate_
.reset(
581 new FtpDirectoryListingResponseDelegate(client_
, loader_
, response
));
585 void WebURLLoaderImpl::Context::OnDownloadedData(int len
,
586 int encoded_data_length
) {
588 client_
->didDownloadData(loader_
, len
, encoded_data_length
);
591 void WebURLLoaderImpl::Context::OnReceivedData(const char* data
,
593 int encoded_data_length
) {
597 if (ftp_listing_delegate_
) {
598 // The FTP listing delegate will make the appropriate calls to
599 // client_->didReceiveData and client_->didReceiveResponse. Since the
600 // delegate may want to do work after sending data to the delegate, keep
601 // |this| and the delegate alive until it's finished handling the data.
602 scoped_refptr
<Context
> protect(this);
603 ftp_listing_delegate_
->OnReceivedData(data
, data_length
);
604 } else if (multipart_delegate_
) {
605 // The multipart delegate will make the appropriate calls to
606 // client_->didReceiveData and client_->didReceiveResponse. Since the
607 // delegate may want to do work after sending data to the delegate, keep
608 // |this| and the delegate alive until it's finished handling the data.
609 scoped_refptr
<Context
> protect(this);
610 multipart_delegate_
->OnReceivedData(data
, data_length
, encoded_data_length
);
612 client_
->didReceiveData(loader_
, data
, data_length
, encoded_data_length
);
616 void WebURLLoaderImpl::Context::OnReceivedCachedMetadata(
617 const char* data
, int len
) {
619 client_
->didReceiveCachedMetadata(loader_
, data
, len
);
622 void WebURLLoaderImpl::Context::OnCompletedRequest(
624 bool was_ignored_by_handler
,
625 bool stale_copy_in_cache
,
626 const std::string
& security_info
,
627 const base::TimeTicks
& completion_time
,
628 int64 total_transfer_size
) {
629 // The WebURLLoaderImpl may be deleted in any of the calls to the client or
630 // the delegates below (As they also may call in to the client). Keep |this|
631 // alive in that case, to avoid a crash. If that happens, the request will be
632 // cancelled and |client_| will be set to NULL.
633 scoped_refptr
<Context
> protect(this);
635 if (ftp_listing_delegate_
) {
636 ftp_listing_delegate_
->OnCompletedRequest();
637 ftp_listing_delegate_
.reset(NULL
);
638 } else if (multipart_delegate_
) {
639 multipart_delegate_
->OnCompletedRequest();
640 multipart_delegate_
.reset(NULL
);
643 // Prevent any further IPC to the browser now that we're complete, but
644 // don't delete it to keep any downloaded temp files alive.
645 DCHECK(!completed_bridge_
.get());
646 completed_bridge_
.swap(bridge_
);
649 if (error_code
!= net::OK
) {
650 client_
->didFail(loader_
, CreateError(request_
.url(),
654 client_
->didFinishLoading(
655 loader_
, (completion_time
- TimeTicks()).InSecondsF(),
656 total_transfer_size
);
661 bool WebURLLoaderImpl::Context::CanHandleDataURLRequestLocally() const {
662 GURL url
= request_
.url();
663 if (!url
.SchemeIs("data"))
666 // The fast paths for data URL, Start() and HandleDataURL(), don't support
667 // the downloadToFile option.
668 if (request_
.downloadToFile())
671 // Optimize for the case where we can handle a data URL locally. We must
672 // skip this for data URLs targetted at frames since those could trigger a
675 // NOTE: We special case MIME types we can render both for performance
676 // reasons as well as to support unit tests, which do not have an underlying
677 // ResourceLoaderBridge implementation.
679 #if defined(OS_ANDROID)
680 // For compatibility reasons on Android we need to expose top-level data://
682 if (request_
.frameType() == WebURLRequest::FrameTypeTopLevel
)
686 if (request_
.frameType() != WebURLRequest::FrameTypeTopLevel
&&
687 request_
.frameType() != WebURLRequest::FrameTypeNested
)
690 std::string mime_type
, unused_charset
;
691 if (net::DataURL::Parse(request_
.url(), &mime_type
, &unused_charset
, NULL
) &&
692 net::IsSupportedMimeType(mime_type
))
698 void WebURLLoaderImpl::Context::HandleDataURL() {
699 ResourceResponseInfo info
;
702 int error_code
= GetInfoFromDataURL(request_
.url(), &info
, &data
);
704 if (error_code
== net::OK
) {
705 OnReceivedResponse(info
);
707 OnReceivedData(data
.data(), data
.size(), 0);
710 OnCompletedRequest(error_code
, false, false, info
.security_info
,
711 base::TimeTicks::Now(), 0);
714 // WebURLLoaderImpl -----------------------------------------------------------
716 WebURLLoaderImpl::WebURLLoaderImpl(ResourceDispatcher
* resource_dispatcher
)
717 : context_(new Context(this, resource_dispatcher
)) {
720 WebURLLoaderImpl::~WebURLLoaderImpl() {
724 WebURLError
WebURLLoaderImpl::CreateError(const WebURL
& unreachable_url
,
725 bool stale_copy_in_cache
,
728 error
.domain
= WebString::fromUTF8(net::kErrorDomain
);
729 error
.reason
= reason
;
730 error
.unreachableURL
= unreachable_url
;
731 error
.staleCopyInCache
= stale_copy_in_cache
;
732 if (reason
== net::ERR_ABORTED
) {
733 error
.isCancellation
= true;
734 } else if (reason
== net::ERR_TEMPORARILY_THROTTLED
) {
735 error
.localizedDescription
= WebString::fromUTF8(
736 kThrottledErrorDescription
);
738 error
.localizedDescription
= WebString::fromUTF8(
739 net::ErrorToString(reason
));
744 void WebURLLoaderImpl::PopulateURLResponse(const GURL
& url
,
745 const ResourceResponseInfo
& info
,
746 WebURLResponse
* response
) {
747 response
->setURL(url
);
748 response
->setResponseTime(info
.response_time
.ToDoubleT());
749 response
->setMIMEType(WebString::fromUTF8(info
.mime_type
));
750 response
->setTextEncodingName(WebString::fromUTF8(info
.charset
));
751 response
->setExpectedContentLength(info
.content_length
);
752 response
->setSecurityInfo(info
.security_info
);
753 response
->setAppCacheID(info
.appcache_id
);
754 response
->setAppCacheManifestURL(info
.appcache_manifest_url
);
755 response
->setWasCached(!info
.load_timing
.request_start_time
.is_null() &&
756 info
.response_time
< info
.load_timing
.request_start_time
);
757 response
->setRemoteIPAddress(
758 WebString::fromUTF8(info
.socket_address
.host()));
759 response
->setRemotePort(info
.socket_address
.port());
760 response
->setConnectionID(info
.load_timing
.socket_log_id
);
761 response
->setConnectionReused(info
.load_timing
.socket_reused
);
762 response
->setDownloadFilePath(info
.download_file_path
.AsUTF16Unsafe());
763 response
->setWasFetchedViaServiceWorker(info
.was_fetched_via_service_worker
);
764 WebURLResponseExtraDataImpl
* extra_data
=
765 new WebURLResponseExtraDataImpl(info
.npn_negotiated_protocol
);
766 response
->setExtraData(extra_data
);
767 extra_data
->set_was_fetched_via_spdy(info
.was_fetched_via_spdy
);
768 extra_data
->set_was_npn_negotiated(info
.was_npn_negotiated
);
769 extra_data
->set_was_alternate_protocol_available(
770 info
.was_alternate_protocol_available
);
771 extra_data
->set_connection_info(info
.connection_info
);
772 extra_data
->set_was_fetched_via_proxy(info
.was_fetched_via_proxy
);
774 // If there's no received headers end time, don't set load timing. This is
775 // the case for non-HTTP requests, requests that don't go over the wire, and
776 // certain error cases.
777 if (!info
.load_timing
.receive_headers_end
.is_null()) {
778 WebURLLoadTiming timing
;
779 PopulateURLLoadTiming(info
.load_timing
, &timing
);
780 const TimeTicks kNullTicks
;
781 timing
.setServiceWorkerFetchStart(
782 (info
.service_worker_fetch_start
- kNullTicks
).InSecondsF());
783 timing
.setServiceWorkerFetchReady(
784 (info
.service_worker_fetch_ready
- kNullTicks
).InSecondsF());
785 timing
.setServiceWorkerFetchEnd(
786 (info
.service_worker_fetch_end
- kNullTicks
).InSecondsF());
787 response
->setLoadTiming(timing
);
790 if (info
.devtools_info
.get()) {
791 WebHTTPLoadInfo load_info
;
793 load_info
.setHTTPStatusCode(info
.devtools_info
->http_status_code
);
794 load_info
.setHTTPStatusText(WebString::fromLatin1(
795 info
.devtools_info
->http_status_text
));
796 load_info
.setEncodedDataLength(info
.encoded_data_length
);
798 load_info
.setRequestHeadersText(WebString::fromLatin1(
799 info
.devtools_info
->request_headers_text
));
800 load_info
.setResponseHeadersText(WebString::fromLatin1(
801 info
.devtools_info
->response_headers_text
));
802 const HeadersVector
& request_headers
= info
.devtools_info
->request_headers
;
803 for (HeadersVector::const_iterator it
= request_headers
.begin();
804 it
!= request_headers
.end(); ++it
) {
805 load_info
.addRequestHeader(WebString::fromLatin1(it
->first
),
806 WebString::fromLatin1(it
->second
));
808 const HeadersVector
& response_headers
=
809 info
.devtools_info
->response_headers
;
810 for (HeadersVector::const_iterator it
= response_headers
.begin();
811 it
!= response_headers
.end(); ++it
) {
812 load_info
.addResponseHeader(WebString::fromLatin1(it
->first
),
813 WebString::fromLatin1(it
->second
));
815 response
->setHTTPLoadInfo(load_info
);
818 const net::HttpResponseHeaders
* headers
= info
.headers
.get();
822 WebURLResponse::HTTPVersion version
= WebURLResponse::Unknown
;
823 if (headers
->GetHttpVersion() == net::HttpVersion(0, 9))
824 version
= WebURLResponse::HTTP_0_9
;
825 else if (headers
->GetHttpVersion() == net::HttpVersion(1, 0))
826 version
= WebURLResponse::HTTP_1_0
;
827 else if (headers
->GetHttpVersion() == net::HttpVersion(1, 1))
828 version
= WebURLResponse::HTTP_1_1
;
829 response
->setHTTPVersion(version
);
830 response
->setHTTPStatusCode(headers
->response_code());
831 response
->setHTTPStatusText(WebString::fromLatin1(headers
->GetStatusText()));
833 // TODO(darin): We should leverage HttpResponseHeaders for this, and this
834 // should be using the same code as ResourceDispatcherHost.
835 // TODO(jungshik): Figure out the actual value of the referrer charset and
836 // pass it to GetSuggestedFilename.
838 headers
->EnumerateHeader(NULL
, "content-disposition", &value
);
839 response
->setSuggestedFileName(
840 net::GetSuggestedFilename(url
,
842 std::string(), // referrer_charset
843 std::string(), // suggested_name
844 std::string(), // mime_type
845 std::string())); // default_name
848 if (headers
->GetLastModifiedValue(&time_val
))
849 response
->setLastModifiedDate(time_val
.ToDoubleT());
851 // Build up the header map.
854 while (headers
->EnumerateHeaderLines(&iter
, &name
, &value
)) {
855 response
->addHTTPHeaderField(WebString::fromLatin1(name
),
856 WebString::fromLatin1(value
));
860 void WebURLLoaderImpl::loadSynchronously(const WebURLRequest
& request
,
861 WebURLResponse
& response
,
864 SyncLoadResponse sync_load_response
;
865 context_
->Start(request
, &sync_load_response
);
867 const GURL
& final_url
= sync_load_response
.url
;
869 // TODO(tc): For file loads, we may want to include a more descriptive
870 // status code or status text.
871 int error_code
= sync_load_response
.error_code
;
872 if (error_code
!= net::OK
) {
873 response
.setURL(final_url
);
874 error
.domain
= WebString::fromUTF8(net::kErrorDomain
);
875 error
.reason
= error_code
;
876 error
.unreachableURL
= final_url
;
880 PopulateURLResponse(final_url
, sync_load_response
, &response
);
882 data
.assign(sync_load_response
.data
.data(),
883 sync_load_response
.data
.size());
886 void WebURLLoaderImpl::loadAsynchronously(const WebURLRequest
& request
,
887 WebURLLoaderClient
* client
) {
888 DCHECK(!context_
->client());
890 context_
->set_client(client
);
891 context_
->Start(request
, NULL
);
894 void WebURLLoaderImpl::cancel() {
898 void WebURLLoaderImpl::setDefersLoading(bool value
) {
899 context_
->SetDefersLoading(value
);
902 void WebURLLoaderImpl::didChangePriority(WebURLRequest::Priority new_priority
,
903 int intra_priority_value
) {
904 context_
->DidChangePriority(new_priority
, intra_priority_value
);
907 bool WebURLLoaderImpl::attachThreadedDataReceiver(
908 blink::WebThreadedDataReceiver
* threaded_data_receiver
) {
909 return context_
->AttachThreadedDataReceiver(threaded_data_receiver
);
912 } // namespace content