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/blink_platform_impl.h"
16 #include "content/child/ftp_directory_listing_response_delegate.h"
17 #include "content/common/resource_request_body.h"
18 #include "net/base/data_url.h"
19 #include "net/base/load_flags.h"
20 #include "net/base/mime_util.h"
21 #include "net/base/net_errors.h"
22 #include "net/base/net_util.h"
23 #include "net/http/http_response_headers.h"
24 #include "net/http/http_util.h"
25 #include "net/url_request/url_request.h"
26 #include "third_party/WebKit/public/platform/WebHTTPHeaderVisitor.h"
27 #include "third_party/WebKit/public/platform/WebHTTPLoadInfo.h"
28 #include "third_party/WebKit/public/platform/WebURL.h"
29 #include "third_party/WebKit/public/platform/WebURLError.h"
30 #include "third_party/WebKit/public/platform/WebURLLoadTiming.h"
31 #include "third_party/WebKit/public/platform/WebURLLoaderClient.h"
32 #include "third_party/WebKit/public/platform/WebURLRequest.h"
33 #include "third_party/WebKit/public/platform/WebURLResponse.h"
34 #include "third_party/WebKit/public/web/WebSecurityPolicy.h"
35 #include "webkit/child/multipart_response_delegate.h"
36 #include "webkit/child/resource_loader_bridge.h"
37 #include "webkit/child/weburlrequest_extradata_impl.h"
38 #include "webkit/child/weburlresponse_extradata_impl.h"
41 using base::TimeTicks
;
43 using blink::WebHTTPBody
;
44 using blink::WebHTTPHeaderVisitor
;
45 using blink::WebHTTPLoadInfo
;
46 using blink::WebReferrerPolicy
;
47 using blink::WebSecurityPolicy
;
48 using blink::WebString
;
50 using blink::WebURLError
;
51 using blink::WebURLLoadTiming
;
52 using blink::WebURLLoader
;
53 using blink::WebURLLoaderClient
;
54 using blink::WebURLRequest
;
55 using blink::WebURLResponse
;
56 using webkit_glue::MultipartResponseDelegate
;
57 using webkit_glue::ResourceDevToolsInfo
;
58 using webkit_glue::ResourceLoaderBridge
;
59 using webkit_glue::ResourceResponseInfo
;
60 using webkit_glue::WebURLResponseExtraDataImpl
;
64 // Utilities ------------------------------------------------------------------
68 const char kThrottledErrorDescription
[] =
69 "Request throttled. Visit http://dev.chromium.org/throttling for more "
72 class HeaderFlattener
: public WebHTTPHeaderVisitor
{
74 explicit HeaderFlattener(int load_flags
)
75 : load_flags_(load_flags
),
76 has_accept_header_(false) {
79 virtual void visitHeader(const WebString
& name
, const WebString
& value
) {
80 // Headers are latin1.
81 const std::string
& name_latin1
= name
.latin1();
82 const std::string
& value_latin1
= value
.latin1();
84 // Skip over referrer headers found in the header map because we already
85 // pulled it out as a separate parameter.
86 if (LowerCaseEqualsASCII(name_latin1
, "referer"))
89 // Skip over "Cache-Control: max-age=0" header if the corresponding
90 // load flag is already specified. FrameLoader sets both the flag and
91 // the extra header -- the extra header is redundant since our network
92 // implementation will add the necessary headers based on load flags.
93 // See http://code.google.com/p/chromium/issues/detail?id=3434.
94 if ((load_flags_
& net::LOAD_VALIDATE_CACHE
) &&
95 LowerCaseEqualsASCII(name_latin1
, "cache-control") &&
96 LowerCaseEqualsASCII(value_latin1
, "max-age=0"))
99 if (LowerCaseEqualsASCII(name_latin1
, "accept"))
100 has_accept_header_
= true;
102 if (!buffer_
.empty())
103 buffer_
.append("\r\n");
104 buffer_
.append(name_latin1
+ ": " + value_latin1
);
107 const std::string
& GetBuffer() {
108 // In some cases, WebKit doesn't add an Accept header, but not having the
109 // header confuses some web servers. See bug 808613.
110 if (!has_accept_header_
) {
111 if (!buffer_
.empty())
112 buffer_
.append("\r\n");
113 buffer_
.append("Accept: */*");
114 has_accept_header_
= true;
122 bool has_accept_header_
;
125 // Extracts the information from a data: url.
126 bool GetInfoFromDataURL(const GURL
& url
,
127 ResourceResponseInfo
* info
,
130 std::string mime_type
;
132 if (net::DataURL::Parse(url
, &mime_type
, &charset
, data
)) {
133 *error_code
= net::OK
;
134 // Assure same time for all time fields of data: URLs.
135 Time now
= Time::Now();
136 info
->load_timing
.request_start
= TimeTicks::Now();
137 info
->load_timing
.request_start_time
= now
;
138 info
->request_time
= now
;
139 info
->response_time
= now
;
140 info
->headers
= NULL
;
141 info
->mime_type
.swap(mime_type
);
142 info
->charset
.swap(charset
);
143 info
->security_info
.clear();
144 info
->content_length
= data
->length();
145 info
->encoded_data_length
= 0;
150 *error_code
= net::ERR_INVALID_URL
;
154 typedef ResourceDevToolsInfo::HeadersVector HeadersVector
;
156 // Converts timing data from |load_timing| to the format used by WebKit.
157 void PopulateURLLoadTiming(const net::LoadTimingInfo
& load_timing
,
158 WebURLLoadTiming
* url_timing
) {
159 DCHECK(!load_timing
.request_start
.is_null());
161 const TimeTicks kNullTicks
;
162 url_timing
->initialize();
163 url_timing
->setRequestTime(
164 (load_timing
.request_start
- kNullTicks
).InSecondsF());
165 url_timing
->setProxyStart(
166 (load_timing
.proxy_resolve_start
- kNullTicks
).InSecondsF());
167 url_timing
->setProxyEnd(
168 (load_timing
.proxy_resolve_end
- kNullTicks
).InSecondsF());
169 url_timing
->setDNSStart(
170 (load_timing
.connect_timing
.dns_start
- kNullTicks
).InSecondsF());
171 url_timing
->setDNSEnd(
172 (load_timing
.connect_timing
.dns_end
- kNullTicks
).InSecondsF());
173 url_timing
->setConnectStart(
174 (load_timing
.connect_timing
.connect_start
- kNullTicks
).InSecondsF());
175 url_timing
->setConnectEnd(
176 (load_timing
.connect_timing
.connect_end
- kNullTicks
).InSecondsF());
177 url_timing
->setSSLStart(
178 (load_timing
.connect_timing
.ssl_start
- kNullTicks
).InSecondsF());
179 url_timing
->setSSLEnd(
180 (load_timing
.connect_timing
.ssl_end
- kNullTicks
).InSecondsF());
181 url_timing
->setSendStart(
182 (load_timing
.send_start
- kNullTicks
).InSecondsF());
183 url_timing
->setSendEnd(
184 (load_timing
.send_end
- kNullTicks
).InSecondsF());
185 url_timing
->setReceiveHeadersEnd(
186 (load_timing
.receive_headers_end
- kNullTicks
).InSecondsF());
189 net::RequestPriority
ConvertWebKitPriorityToNetPriority(
190 const WebURLRequest::Priority
& priority
) {
192 case WebURLRequest::PriorityVeryHigh
:
195 case WebURLRequest::PriorityHigh
:
198 case WebURLRequest::PriorityMedium
:
201 case WebURLRequest::PriorityLow
:
204 case WebURLRequest::PriorityVeryLow
:
207 case WebURLRequest::PriorityUnresolved
:
216 // WebURLLoaderImpl::Context --------------------------------------------------
218 // This inner class exists since the WebURLLoader may be deleted while inside a
219 // call to WebURLLoaderClient. The bridge requires its Peer to stay alive
220 // until it receives OnCompletedRequest.
221 class WebURLLoaderImpl::Context
: public base::RefCounted
<Context
>,
222 public ResourceLoaderBridge::Peer
{
224 explicit Context(WebURLLoaderImpl
* loader
);
226 WebURLLoaderClient
* client() const { return client_
; }
227 void set_client(WebURLLoaderClient
* client
) { client_
= client
; }
230 void SetDefersLoading(bool value
);
231 void DidChangePriority(WebURLRequest::Priority new_priority
);
232 void Start(const WebURLRequest
& request
,
233 ResourceLoaderBridge::SyncLoadResponse
* sync_load_response
,
234 BlinkPlatformImpl
* platform
);
236 // ResourceLoaderBridge::Peer methods:
237 virtual void OnUploadProgress(uint64 position
, uint64 size
) OVERRIDE
;
238 virtual bool OnReceivedRedirect(
240 const ResourceResponseInfo
& info
,
241 bool* has_new_first_party_for_cookies
,
242 GURL
* new_first_party_for_cookies
) OVERRIDE
;
243 virtual void OnReceivedResponse(const ResourceResponseInfo
& info
) OVERRIDE
;
244 virtual void OnDownloadedData(int len
, int encoded_data_length
) OVERRIDE
;
245 virtual void OnReceivedData(const char* data
,
247 int encoded_data_length
) OVERRIDE
;
248 virtual void OnReceivedCachedMetadata(const char* data
, int len
) OVERRIDE
;
249 virtual void OnCompletedRequest(
251 bool was_ignored_by_handler
,
252 bool stale_copy_in_cache
,
253 const std::string
& security_info
,
254 const base::TimeTicks
& completion_time
,
255 int64 total_transfer_size
) OVERRIDE
;
258 friend class base::RefCounted
<Context
>;
259 virtual ~Context() {}
261 // We can optimize the handling of data URLs in most cases.
262 bool CanHandleDataURL(const GURL
& url
) const;
263 void HandleDataURL();
265 WebURLLoaderImpl
* loader_
;
266 WebURLRequest request_
;
267 WebURLLoaderClient
* client_
;
268 WebReferrerPolicy referrer_policy_
;
269 scoped_ptr
<ResourceLoaderBridge
> bridge_
;
270 scoped_ptr
<FtpDirectoryListingResponseDelegate
> ftp_listing_delegate_
;
271 scoped_ptr
<MultipartResponseDelegate
> multipart_delegate_
;
272 scoped_ptr
<ResourceLoaderBridge
> completed_bridge_
;
275 WebURLLoaderImpl::Context::Context(WebURLLoaderImpl
* loader
)
278 referrer_policy_(blink::WebReferrerPolicyDefault
) {
281 void WebURLLoaderImpl::Context::Cancel() {
282 // The bridge will still send OnCompletedRequest, which will Release() us, so
283 // we don't do that here.
287 // Ensure that we do not notify the multipart delegate anymore as it has
288 // its own pointer to the client.
289 if (multipart_delegate_
)
290 multipart_delegate_
->Cancel();
292 // Do not make any further calls to the client.
297 void WebURLLoaderImpl::Context::SetDefersLoading(bool value
) {
299 bridge_
->SetDefersLoading(value
);
302 void WebURLLoaderImpl::Context::DidChangePriority(
303 WebURLRequest::Priority new_priority
) {
305 bridge_
->DidChangePriority(
306 ConvertWebKitPriorityToNetPriority(new_priority
));
309 void WebURLLoaderImpl::Context::Start(
310 const WebURLRequest
& request
,
311 ResourceLoaderBridge::SyncLoadResponse
* sync_load_response
,
312 BlinkPlatformImpl
* platform
) {
313 DCHECK(!bridge_
.get());
315 request_
= request
; // Save the request.
317 GURL url
= request
.url();
318 if (url
.SchemeIs("data") && CanHandleDataURL(url
)) {
319 if (sync_load_response
) {
320 // This is a sync load. Do the work now.
321 sync_load_response
->url
= url
;
323 GetInfoFromDataURL(sync_load_response
->url
, sync_load_response
,
324 &sync_load_response
->data
,
325 &sync_load_response
->error_code
);
327 AddRef(); // Balanced in OnCompletedRequest
328 base::MessageLoop::current()->PostTask(
329 FROM_HERE
, base::Bind(&Context::HandleDataURL
, this));
335 request
.httpHeaderField(WebString::fromUTF8("Referer")).latin1());
336 const std::string
& method
= request
.httpMethod().latin1();
338 int load_flags
= net::LOAD_NORMAL
;
339 switch (request
.cachePolicy()) {
340 case WebURLRequest::ReloadIgnoringCacheData
:
341 // Required by LayoutTests/http/tests/misc/refresh-headers.php
342 load_flags
|= net::LOAD_VALIDATE_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
:
354 if (request
.reportUploadProgress())
355 load_flags
|= net::LOAD_ENABLE_UPLOAD_PROGRESS
;
356 if (request
.reportLoadTiming())
357 load_flags
|= net::LOAD_ENABLE_LOAD_TIMING
;
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
.targetType() == WebURLRequest::TargetIsXHR
&&
370 (url
.has_username() || url
.has_password())) {
371 load_flags
|= net::LOAD_DO_NOT_PROMPT_FOR_LOGIN
;
374 HeaderFlattener
flattener(load_flags
);
375 request
.visitHTTPHeaderFields(&flattener
);
377 // TODO(brettw) this should take parameter encoding into account when
378 // creating the GURLs.
380 ResourceLoaderBridge::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 // requestor_pid only needs to be non-zero if the request originates outside
388 // the render process, so we can use requestorProcessID even for requests
389 // from in-process plugins.
390 request_info
.requestor_pid
= request
.requestorProcessID();
391 request_info
.request_type
=
392 ResourceType::FromTargetType(request
.targetType());
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
.extra_data
= request
.extraData();
400 referrer_policy_
= request
.referrerPolicy();
401 request_info
.referrer_policy
= request
.referrerPolicy();
402 bridge_
.reset(platform
->CreateResourceLoader(request_info
));
404 if (!request
.httpBody().isNull()) {
405 // GET and HEAD requests shouldn't have http bodies.
406 DCHECK(method
!= "GET" && method
!= "HEAD");
407 const WebHTTPBody
& httpBody
= request
.httpBody();
409 WebHTTPBody::Element element
;
410 scoped_refptr
<ResourceRequestBody
> request_body
= new ResourceRequestBody
;
411 while (httpBody
.elementAt(i
++, element
)) {
412 switch (element
.type
) {
413 case WebHTTPBody::Element::TypeData
:
414 if (!element
.data
.isEmpty()) {
415 // WebKit sometimes gives up empty data to append. These aren't
416 // necessary so we just optimize those out here.
417 request_body
->AppendBytes(
418 element
.data
.data(), static_cast<int>(element
.data
.size()));
421 case WebHTTPBody::Element::TypeFile
:
422 if (element
.fileLength
== -1) {
423 request_body
->AppendFileRange(
424 base::FilePath::FromUTF16Unsafe(element
.filePath
),
425 0, kuint64max
, base::Time());
427 request_body
->AppendFileRange(
428 base::FilePath::FromUTF16Unsafe(element
.filePath
),
429 static_cast<uint64
>(element
.fileStart
),
430 static_cast<uint64
>(element
.fileLength
),
431 base::Time::FromDoubleT(element
.modificationTime
));
434 case WebHTTPBody::Element::TypeFileSystemURL
: {
435 GURL file_system_url
= element
.fileSystemURL
;
436 DCHECK(file_system_url
.SchemeIsFileSystem());
437 request_body
->AppendFileSystemFileRange(
439 static_cast<uint64
>(element
.fileStart
),
440 static_cast<uint64
>(element
.fileLength
),
441 base::Time::FromDoubleT(element
.modificationTime
));
444 case WebHTTPBody::Element::TypeBlob
:
445 request_body
->AppendBlob(element
.blobUUID
.utf8());
451 request_body
->set_identifier(request
.httpBody().identifier());
452 bridge_
->SetRequestBody(request_body
.get());
455 if (sync_load_response
) {
456 bridge_
->SyncLoad(sync_load_response
);
460 if (bridge_
->Start(this)) {
461 AddRef(); // Balanced in OnCompletedRequest
467 void WebURLLoaderImpl::Context::OnUploadProgress(uint64 position
, uint64 size
) {
469 client_
->didSendData(loader_
, position
, size
);
472 bool WebURLLoaderImpl::Context::OnReceivedRedirect(
474 const ResourceResponseInfo
& info
,
475 bool* has_new_first_party_for_cookies
,
476 GURL
* new_first_party_for_cookies
) {
480 WebURLResponse response
;
481 response
.initialize();
482 PopulateURLResponse(request_
.url(), info
, &response
);
484 // TODO(darin): We lack sufficient information to construct the actual
485 // request that resulted from the redirect.
486 WebURLRequest
new_request(new_url
);
487 new_request
.setFirstPartyForCookies(request_
.firstPartyForCookies());
488 new_request
.setDownloadToFile(request_
.downloadToFile());
490 WebString referrer_string
= WebString::fromUTF8("Referer");
491 WebString referrer
= WebSecurityPolicy::generateReferrerHeader(
494 request_
.httpHeaderField(referrer_string
));
495 if (!referrer
.isEmpty())
496 new_request
.setHTTPReferrer(referrer
, referrer_policy_
);
498 std::string method
= request_
.httpMethod().utf8();
499 std::string new_method
= net::URLRequest::ComputeMethodForRedirect(
500 method
, response
.httpStatusCode());
501 new_request
.setHTTPMethod(WebString::fromUTF8(new_method
));
502 if (new_method
== method
)
503 new_request
.setHTTPBody(request_
.httpBody());
505 client_
->willSendRequest(loader_
, new_request
, response
);
506 request_
= new_request
;
507 *has_new_first_party_for_cookies
= true;
508 *new_first_party_for_cookies
= request_
.firstPartyForCookies();
510 // Only follow the redirect if WebKit left the URL unmodified.
511 if (new_url
== GURL(new_request
.url()))
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 PopulateURLResponse(request_
.url(), info
, &response
);
529 bool show_raw_listing
= (GURL(request_
.url()).query() == "raw");
531 if (info
.mime_type
== "text/vnd.chromium.ftp-dir") {
532 if (show_raw_listing
) {
533 // Set the MIME type to plain text to prevent any active content.
534 response
.setMIMEType("text/plain");
536 // We're going to produce a parsed listing in HTML.
537 response
.setMIMEType("text/html");
541 scoped_refptr
<Context
> protect(this);
542 client_
->didReceiveResponse(loader_
, response
);
544 // We may have been cancelled after didReceiveResponse, which would leave us
545 // without a client and therefore without much need to do further handling.
549 DCHECK(!ftp_listing_delegate_
.get());
550 DCHECK(!multipart_delegate_
.get());
551 if (info
.headers
.get() && info
.mime_type
== "multipart/x-mixed-replace") {
552 std::string content_type
;
553 info
.headers
->EnumerateHeader(NULL
, "content-type", &content_type
);
555 std::string mime_type
;
557 bool had_charset
= false;
558 std::string boundary
;
559 net::HttpUtil::ParseContentType(content_type
, &mime_type
, &charset
,
560 &had_charset
, &boundary
);
561 base::TrimString(boundary
, " \"", &boundary
);
563 // If there's no boundary, just handle the request normally. In the gecko
564 // code, nsMultiMixedConv::OnStartRequest throws an exception.
565 if (!boundary
.empty()) {
566 multipart_delegate_
.reset(
567 new MultipartResponseDelegate(client_
, loader_
, response
, boundary
));
569 } else if (info
.mime_type
== "text/vnd.chromium.ftp-dir" &&
571 ftp_listing_delegate_
.reset(
572 new FtpDirectoryListingResponseDelegate(client_
, loader_
, response
));
576 void WebURLLoaderImpl::Context::OnDownloadedData(int len
,
577 int encoded_data_length
) {
579 client_
->didDownloadData(loader_
, len
, encoded_data_length
);
582 void WebURLLoaderImpl::Context::OnReceivedData(const char* data
,
584 int encoded_data_length
) {
588 if (ftp_listing_delegate_
) {
589 // The FTP listing delegate will make the appropriate calls to
590 // client_->didReceiveData and client_->didReceiveResponse.
591 ftp_listing_delegate_
->OnReceivedData(data
, data_length
);
592 } else if (multipart_delegate_
) {
593 // The multipart delegate will make the appropriate calls to
594 // client_->didReceiveData and client_->didReceiveResponse.
595 multipart_delegate_
->OnReceivedData(data
, data_length
, encoded_data_length
);
597 client_
->didReceiveData(loader_
, data
, data_length
, encoded_data_length
);
601 void WebURLLoaderImpl::Context::OnReceivedCachedMetadata(
602 const char* data
, int len
) {
604 client_
->didReceiveCachedMetadata(loader_
, data
, len
);
607 void WebURLLoaderImpl::Context::OnCompletedRequest(
609 bool was_ignored_by_handler
,
610 bool stale_copy_in_cache
,
611 const std::string
& security_info
,
612 const base::TimeTicks
& completion_time
,
613 int64 total_transfer_size
) {
614 if (ftp_listing_delegate_
) {
615 ftp_listing_delegate_
->OnCompletedRequest();
616 ftp_listing_delegate_
.reset(NULL
);
617 } else if (multipart_delegate_
) {
618 multipart_delegate_
->OnCompletedRequest();
619 multipart_delegate_
.reset(NULL
);
622 // Prevent any further IPC to the browser now that we're complete, but
623 // don't delete it to keep any downloaded temp files alive.
624 DCHECK(!completed_bridge_
.get());
625 completed_bridge_
.swap(bridge_
);
628 if (error_code
!= net::OK
) {
629 client_
->didFail(loader_
, CreateError(request_
.url(),
633 client_
->didFinishLoading(
634 loader_
, (completion_time
- TimeTicks()).InSecondsF(),
635 total_transfer_size
);
639 // We are done with the bridge now, and so we need to release the reference
640 // to ourselves that we took on behalf of the bridge. This may cause our
645 bool WebURLLoaderImpl::Context::CanHandleDataURL(const GURL
& url
) const {
646 DCHECK(url
.SchemeIs("data"));
648 // Optimize for the case where we can handle a data URL locally. We must
649 // skip this for data URLs targetted at frames since those could trigger a
652 // NOTE: We special case MIME types we can render both for performance
653 // reasons as well as to support unit tests, which do not have an underlying
654 // ResourceLoaderBridge implementation.
656 #if defined(OS_ANDROID)
657 // For compatibility reasons on Android we need to expose top-level data://
659 if (request_
.targetType() == WebURLRequest::TargetIsMainFrame
)
663 if (request_
.targetType() != WebURLRequest::TargetIsMainFrame
&&
664 request_
.targetType() != WebURLRequest::TargetIsSubframe
)
667 std::string mime_type
, unused_charset
;
668 if (net::DataURL::Parse(url
, &mime_type
, &unused_charset
, NULL
) &&
669 net::IsSupportedMimeType(mime_type
))
675 void WebURLLoaderImpl::Context::HandleDataURL() {
676 ResourceResponseInfo info
;
680 if (GetInfoFromDataURL(request_
.url(), &info
, &data
, &error_code
)) {
681 OnReceivedResponse(info
);
683 OnReceivedData(data
.data(), data
.size(), 0);
686 OnCompletedRequest(error_code
, false, false, info
.security_info
,
687 base::TimeTicks::Now(), 0);
690 // WebURLLoaderImpl -----------------------------------------------------------
692 WebURLLoaderImpl::WebURLLoaderImpl(BlinkPlatformImpl
* platform
)
693 : context_(new Context(this)),
694 platform_(platform
) {
697 WebURLLoaderImpl::~WebURLLoaderImpl() {
701 WebURLError
WebURLLoaderImpl::CreateError(const WebURL
& unreachable_url
,
702 bool stale_copy_in_cache
,
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
);
715 error
.localizedDescription
= WebString::fromUTF8(
716 net::ErrorToString(reason
));
721 void 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
);
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
);
759 if (info
.devtools_info
.get()) {
760 WebHTTPLoadInfo load_info
;
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
);
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
));
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
));
784 response
->setHTTPLoadInfo(load_info
);
787 const net::HttpResponseHeaders
* headers
= info
.headers
.get();
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()));
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.
807 headers
->EnumerateHeader(NULL
, "content-disposition", &value
);
808 response
->setSuggestedFileName(
809 net::GetSuggestedFilename(url
,
811 std::string(), // referrer_charset
812 std::string(), // suggested_name
813 std::string(), // mime_type
814 std::string())); // default_name
817 if (headers
->GetLastModifiedValue(&time_val
))
818 response
->setLastModifiedDate(time_val
.ToDoubleT());
820 // Build up the header map.
823 while (headers
->EnumerateHeaderLines(&iter
, &name
, &value
)) {
824 response
->addHTTPHeaderField(WebString::fromLatin1(name
),
825 WebString::fromLatin1(value
));
829 void WebURLLoaderImpl::loadSynchronously(const WebURLRequest
& request
,
830 WebURLResponse
& response
,
833 ResourceLoaderBridge::SyncLoadResponse sync_load_response
;
834 context_
->Start(request
, &sync_load_response
, platform_
);
836 const GURL
& final_url
= sync_load_response
.url
;
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
;
849 PopulateURLResponse(final_url
, sync_load_response
, &response
);
851 data
.assign(sync_load_response
.data
.data(),
852 sync_load_response
.data
.size());
855 void WebURLLoaderImpl::loadAsynchronously(const WebURLRequest
& request
,
856 WebURLLoaderClient
* client
) {
857 DCHECK(!context_
->client());
859 context_
->set_client(client
);
860 context_
->Start(request
, NULL
, platform_
);
863 void WebURLLoaderImpl::cancel() {
867 void WebURLLoaderImpl::setDefersLoading(bool value
) {
868 context_
->SetDefersLoading(value
);
871 void WebURLLoaderImpl::didChangePriority(WebURLRequest::Priority new_priority
) {
872 context_
->DidChangePriority(new_priority
);
875 } // namespace content