1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // An implementation of WebURLLoader in terms of ResourceLoaderBridge.
7 #include "webkit/glue/weburlloader_impl.h"
10 #include "base/files/file_path.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/message_loop.h"
13 #include "base/process_util.h"
14 #include "base/strings/string_util.h"
15 #include "base/time.h"
16 #include "net/base/data_url.h"
17 #include "net/base/load_flags.h"
18 #include "net/base/mime_util.h"
19 #include "net/base/net_errors.h"
20 #include "net/base/net_util.h"
21 #include "net/http/http_response_headers.h"
22 #include "net/http/http_util.h"
23 #include "third_party/WebKit/public/platform/WebHTTPHeaderVisitor.h"
24 #include "third_party/WebKit/public/platform/WebHTTPLoadInfo.h"
25 #include "third_party/WebKit/public/platform/WebURL.h"
26 #include "third_party/WebKit/public/platform/WebURLError.h"
27 #include "third_party/WebKit/public/platform/WebURLLoaderClient.h"
28 #include "third_party/WebKit/public/platform/WebURLLoadTiming.h"
29 #include "third_party/WebKit/public/platform/WebURLRequest.h"
30 #include "third_party/WebKit/public/platform/WebURLResponse.h"
31 #include "third_party/WebKit/public/web/WebSecurityPolicy.h"
32 #include "webkit/glue/ftp_directory_listing_response_delegate.h"
33 #include "webkit/glue/multipart_response_delegate.h"
34 #include "webkit/glue/resource_loader_bridge.h"
35 #include "webkit/glue/resource_request_body.h"
36 #include "webkit/glue/webkitplatformsupport_impl.h"
37 #include "webkit/glue/weburlrequest_extradata_impl.h"
38 #include "webkit/glue/weburlresponse_extradata_impl.h"
41 using base::TimeTicks
;
42 using WebKit::WebData
;
43 using WebKit::WebHTTPBody
;
44 using WebKit::WebHTTPHeaderVisitor
;
45 using WebKit::WebHTTPLoadInfo
;
46 using WebKit::WebReferrerPolicy
;
47 using WebKit::WebSecurityPolicy
;
48 using WebKit::WebString
;
50 using WebKit::WebURLError
;
51 using WebKit::WebURLLoadTiming
;
52 using WebKit::WebURLLoader
;
53 using WebKit::WebURLLoaderClient
;
54 using WebKit::WebURLRequest
;
55 using WebKit::WebURLResponse
;
57 namespace webkit_glue
{
59 // Utilities ------------------------------------------------------------------
63 const char kThrottledErrorDescription
[] =
64 "Request throttled. Visit http://dev.chromium.org/throttling for more "
67 class HeaderFlattener
: public WebHTTPHeaderVisitor
{
69 explicit HeaderFlattener(int load_flags
)
70 : load_flags_(load_flags
),
71 has_accept_header_(false) {
74 virtual void visitHeader(const WebString
& name
, const WebString
& value
) {
75 // TODO(darin): is UTF-8 really correct here? It is if the strings are
76 // already ASCII (i.e., if they are already escaped properly).
77 const std::string
& name_utf8
= name
.utf8();
78 const std::string
& value_utf8
= value
.utf8();
80 // Skip over referrer headers found in the header map because we already
81 // pulled it out as a separate parameter.
82 if (LowerCaseEqualsASCII(name_utf8
, "referer"))
85 // Skip over "Cache-Control: max-age=0" header if the corresponding
86 // load flag is already specified. FrameLoader sets both the flag and
87 // the extra header -- the extra header is redundant since our network
88 // implementation will add the necessary headers based on load flags.
89 // See http://code.google.com/p/chromium/issues/detail?id=3434.
90 if ((load_flags_
& net::LOAD_VALIDATE_CACHE
) &&
91 LowerCaseEqualsASCII(name_utf8
, "cache-control") &&
92 LowerCaseEqualsASCII(value_utf8
, "max-age=0"))
95 if (LowerCaseEqualsASCII(name_utf8
, "accept"))
96 has_accept_header_
= true;
99 buffer_
.append("\r\n");
100 buffer_
.append(name_utf8
+ ": " + value_utf8
);
103 const std::string
& GetBuffer() {
104 // In some cases, WebKit doesn't add an Accept header, but not having the
105 // header confuses some web servers. See bug 808613.
106 if (!has_accept_header_
) {
107 if (!buffer_
.empty())
108 buffer_
.append("\r\n");
109 buffer_
.append("Accept: */*");
110 has_accept_header_
= true;
118 bool has_accept_header_
;
121 // Extracts the information from a data: url.
122 bool GetInfoFromDataURL(const GURL
& url
,
123 ResourceResponseInfo
* info
,
126 std::string mime_type
;
128 if (net::DataURL::Parse(url
, &mime_type
, &charset
, data
)) {
129 *error_code
= net::OK
;
130 // Assure same time for all time fields of data: URLs.
131 Time now
= Time::Now();
132 info
->load_timing
.request_start
= TimeTicks::Now();
133 info
->load_timing
.request_start_time
= now
;
134 info
->request_time
= now
;
135 info
->response_time
= now
;
136 info
->headers
= NULL
;
137 info
->mime_type
.swap(mime_type
);
138 info
->charset
.swap(charset
);
139 info
->security_info
.clear();
140 info
->content_length
= data
->length();
141 info
->encoded_data_length
= 0;
146 *error_code
= net::ERR_INVALID_URL
;
150 typedef ResourceDevToolsInfo::HeadersVector HeadersVector
;
152 // Converts timing data from |load_timing| to the format used by WebKit.
153 void PopulateURLLoadTiming(const net::LoadTimingInfo
& load_timing
,
154 WebURLLoadTiming
* url_timing
) {
155 DCHECK(!load_timing
.request_start
.is_null());
157 const TimeTicks kNullTicks
;
158 url_timing
->initialize();
159 url_timing
->setRequestTime(
160 (load_timing
.request_start
- kNullTicks
).InSecondsF());
161 url_timing
->setProxyStart(
162 (load_timing
.proxy_resolve_start
- kNullTicks
).InSecondsF());
163 url_timing
->setProxyEnd(
164 (load_timing
.proxy_resolve_end
- kNullTicks
).InSecondsF());
165 url_timing
->setDNSStart(
166 (load_timing
.connect_timing
.dns_start
- kNullTicks
).InSecondsF());
167 url_timing
->setDNSEnd(
168 (load_timing
.connect_timing
.dns_end
- kNullTicks
).InSecondsF());
169 url_timing
->setConnectStart(
170 (load_timing
.connect_timing
.connect_start
- kNullTicks
).InSecondsF());
171 url_timing
->setConnectEnd(
172 (load_timing
.connect_timing
.connect_end
- kNullTicks
).InSecondsF());
173 url_timing
->setSSLStart(
174 (load_timing
.connect_timing
.ssl_start
- kNullTicks
).InSecondsF());
175 url_timing
->setSSLEnd(
176 (load_timing
.connect_timing
.ssl_end
- kNullTicks
).InSecondsF());
177 url_timing
->setSendStart(
178 (load_timing
.send_start
- kNullTicks
).InSecondsF());
179 url_timing
->setSendEnd(
180 (load_timing
.send_end
- kNullTicks
).InSecondsF());
181 url_timing
->setReceiveHeadersEnd(
182 (load_timing
.receive_headers_end
- kNullTicks
).InSecondsF());
185 void PopulateURLResponse(
187 const ResourceResponseInfo
& info
,
188 WebURLResponse
* response
) {
189 response
->setURL(url
);
190 response
->setResponseTime(info
.response_time
.ToDoubleT());
191 response
->setMIMEType(WebString::fromUTF8(info
.mime_type
));
192 response
->setTextEncodingName(WebString::fromUTF8(info
.charset
));
193 response
->setExpectedContentLength(info
.content_length
);
194 response
->setSecurityInfo(info
.security_info
);
195 response
->setAppCacheID(info
.appcache_id
);
196 response
->setAppCacheManifestURL(info
.appcache_manifest_url
);
197 response
->setWasCached(!info
.load_timing
.request_start_time
.is_null() &&
198 info
.response_time
< info
.load_timing
.request_start_time
);
199 response
->setRemoteIPAddress(
200 WebString::fromUTF8(info
.socket_address
.host()));
201 response
->setRemotePort(info
.socket_address
.port());
202 response
->setConnectionID(info
.load_timing
.socket_log_id
);
203 response
->setConnectionReused(info
.load_timing
.socket_reused
);
204 response
->setDownloadFilePath(info
.download_file_path
.AsUTF16Unsafe());
205 WebURLResponseExtraDataImpl
* extra_data
=
206 new WebURLResponseExtraDataImpl(info
.npn_negotiated_protocol
);
207 response
->setExtraData(extra_data
);
208 extra_data
->set_was_fetched_via_spdy(info
.was_fetched_via_spdy
);
209 extra_data
->set_was_npn_negotiated(info
.was_npn_negotiated
);
210 extra_data
->set_was_alternate_protocol_available(
211 info
.was_alternate_protocol_available
);
212 extra_data
->set_connection_info(info
.connection_info
);
213 extra_data
->set_was_fetched_via_proxy(info
.was_fetched_via_proxy
);
215 // If there's no received headers end time, don't set load timing. This is
216 // the case for non-HTTP requests, requests that don't go over the wire, and
217 // certain error cases.
218 if (!info
.load_timing
.receive_headers_end
.is_null()) {
219 WebURLLoadTiming timing
;
220 PopulateURLLoadTiming(info
.load_timing
, &timing
);
221 response
->setLoadTiming(timing
);
224 if (info
.devtools_info
.get()) {
225 WebHTTPLoadInfo load_info
;
227 load_info
.setHTTPStatusCode(info
.devtools_info
->http_status_code
);
228 load_info
.setHTTPStatusText(WebString::fromUTF8(
229 info
.devtools_info
->http_status_text
));
230 load_info
.setEncodedDataLength(info
.encoded_data_length
);
232 load_info
.setRequestHeadersText(WebString::fromUTF8(
233 info
.devtools_info
->request_headers_text
));
234 load_info
.setResponseHeadersText(WebString::fromUTF8(
235 info
.devtools_info
->response_headers_text
));
236 const HeadersVector
& request_headers
= info
.devtools_info
->request_headers
;
237 for (HeadersVector::const_iterator it
= request_headers
.begin();
238 it
!= request_headers
.end(); ++it
) {
239 load_info
.addRequestHeader(WebString::fromUTF8(it
->first
),
240 WebString::fromUTF8(it
->second
));
242 const HeadersVector
& response_headers
=
243 info
.devtools_info
->response_headers
;
244 for (HeadersVector::const_iterator it
= response_headers
.begin();
245 it
!= response_headers
.end(); ++it
) {
246 load_info
.addResponseHeader(WebString::fromUTF8(it
->first
),
247 WebString::fromUTF8(it
->second
));
249 response
->setHTTPLoadInfo(load_info
);
252 const net::HttpResponseHeaders
* headers
= info
.headers
.get();
256 WebURLResponse::HTTPVersion version
= WebURLResponse::Unknown
;
257 if (headers
->GetHttpVersion() == net::HttpVersion(0, 9))
258 version
= WebURLResponse::HTTP_0_9
;
259 else if (headers
->GetHttpVersion() == net::HttpVersion(1, 0))
260 version
= WebURLResponse::HTTP_1_0
;
261 else if (headers
->GetHttpVersion() == net::HttpVersion(1, 1))
262 version
= WebURLResponse::HTTP_1_1
;
263 response
->setHTTPVersion(version
);
264 response
->setHTTPStatusCode(headers
->response_code());
265 response
->setHTTPStatusText(WebString::fromUTF8(headers
->GetStatusText()));
267 // TODO(darin): We should leverage HttpResponseHeaders for this, and this
268 // should be using the same code as ResourceDispatcherHost.
269 // TODO(jungshik): Figure out the actual value of the referrer charset and
270 // pass it to GetSuggestedFilename.
272 headers
->EnumerateHeader(NULL
, "content-disposition", &value
);
273 response
->setSuggestedFileName(
274 net::GetSuggestedFilename(url
,
276 std::string(), // referrer_charset
277 std::string(), // suggested_name
278 std::string(), // mime_type
279 std::string())); // default_name
282 if (headers
->GetLastModifiedValue(&time_val
))
283 response
->setLastModifiedDate(time_val
.ToDoubleT());
285 // Build up the header map.
288 while (headers
->EnumerateHeaderLines(&iter
, &name
, &value
)) {
289 response
->addHTTPHeaderField(WebString::fromUTF8(name
),
290 WebString::fromUTF8(value
));
294 net::RequestPriority
ConvertWebKitPriorityToNetPriority(
295 const WebURLRequest::Priority
& priority
) {
297 case WebURLRequest::PriorityVeryHigh
:
300 case WebURLRequest::PriorityHigh
:
303 case WebURLRequest::PriorityMedium
:
306 case WebURLRequest::PriorityLow
:
309 case WebURLRequest::PriorityVeryLow
:
312 case WebURLRequest::PriorityUnresolved
:
321 // WebURLLoaderImpl::Context --------------------------------------------------
323 // This inner class exists since the WebURLLoader may be deleted while inside a
324 // call to WebURLLoaderClient. The bridge requires its Peer to stay alive
325 // until it receives OnCompletedRequest.
326 class WebURLLoaderImpl::Context
: public base::RefCounted
<Context
>,
327 public ResourceLoaderBridge::Peer
{
329 explicit Context(WebURLLoaderImpl
* loader
);
331 WebURLLoaderClient
* client() const { return client_
; }
332 void set_client(WebURLLoaderClient
* client
) { client_
= client
; }
335 void SetDefersLoading(bool value
);
336 void DidChangePriority(WebURLRequest::Priority new_priority
);
338 const WebURLRequest
& request
,
339 ResourceLoaderBridge::SyncLoadResponse
* sync_load_response
,
340 WebKitPlatformSupportImpl
* platform
);
342 // ResourceLoaderBridge::Peer methods:
343 virtual void OnUploadProgress(uint64 position
, uint64 size
) OVERRIDE
;
344 virtual bool OnReceivedRedirect(
346 const ResourceResponseInfo
& info
,
347 bool* has_new_first_party_for_cookies
,
348 GURL
* new_first_party_for_cookies
) OVERRIDE
;
349 virtual void OnReceivedResponse(const ResourceResponseInfo
& info
) OVERRIDE
;
350 virtual void OnDownloadedData(int len
) OVERRIDE
;
351 virtual void OnReceivedData(const char* data
,
353 int encoded_data_length
) OVERRIDE
;
354 virtual void OnReceivedCachedMetadata(const char* data
, int len
) OVERRIDE
;
355 virtual void OnCompletedRequest(
357 bool was_ignored_by_handler
,
358 const std::string
& security_info
,
359 const base::TimeTicks
& completion_time
) OVERRIDE
;
362 friend class base::RefCounted
<Context
>;
363 virtual ~Context() {}
365 // We can optimize the handling of data URLs in most cases.
366 bool CanHandleDataURL(const GURL
& url
) const;
367 void HandleDataURL();
369 WebURLLoaderImpl
* loader_
;
370 WebURLRequest request_
;
371 WebURLLoaderClient
* client_
;
372 WebReferrerPolicy referrer_policy_
;
373 scoped_ptr
<ResourceLoaderBridge
> bridge_
;
374 scoped_ptr
<FtpDirectoryListingResponseDelegate
> ftp_listing_delegate_
;
375 scoped_ptr
<MultipartResponseDelegate
> multipart_delegate_
;
376 scoped_ptr
<ResourceLoaderBridge
> completed_bridge_
;
379 WebURLLoaderImpl::Context::Context(WebURLLoaderImpl
* loader
)
382 referrer_policy_(WebKit::WebReferrerPolicyDefault
) {
385 void WebURLLoaderImpl::Context::Cancel() {
386 // The bridge will still send OnCompletedRequest, which will Release() us, so
387 // we don't do that here.
391 // Ensure that we do not notify the multipart delegate anymore as it has
392 // its own pointer to the client.
393 if (multipart_delegate_
)
394 multipart_delegate_
->Cancel();
396 // Do not make any further calls to the client.
401 void WebURLLoaderImpl::Context::SetDefersLoading(bool value
) {
403 bridge_
->SetDefersLoading(value
);
406 void WebURLLoaderImpl::Context::DidChangePriority(
407 WebURLRequest::Priority new_priority
) {
409 bridge_
->DidChangePriority(
410 ConvertWebKitPriorityToNetPriority(new_priority
));
413 void WebURLLoaderImpl::Context::Start(
414 const WebURLRequest
& request
,
415 ResourceLoaderBridge::SyncLoadResponse
* sync_load_response
,
416 WebKitPlatformSupportImpl
* platform
) {
417 DCHECK(!bridge_
.get());
419 request_
= request
; // Save the request.
421 GURL url
= request
.url();
422 if (url
.SchemeIs("data") && CanHandleDataURL(url
)) {
423 if (sync_load_response
) {
424 // This is a sync load. Do the work now.
425 sync_load_response
->url
= url
;
427 GetInfoFromDataURL(sync_load_response
->url
, sync_load_response
,
428 &sync_load_response
->data
,
429 &sync_load_response
->error_code
);
431 AddRef(); // Balanced in OnCompletedRequest
432 base::MessageLoop::current()->PostTask(
433 FROM_HERE
, base::Bind(&Context::HandleDataURL
, this));
439 request
.httpHeaderField(WebString::fromUTF8("Referer")).utf8());
440 const std::string
& method
= request
.httpMethod().utf8();
442 int load_flags
= net::LOAD_NORMAL
;
443 switch (request
.cachePolicy()) {
444 case WebURLRequest::ReloadIgnoringCacheData
:
445 // Required by LayoutTests/http/tests/misc/refresh-headers.php
446 load_flags
|= net::LOAD_VALIDATE_CACHE
;
448 case WebURLRequest::ReturnCacheDataElseLoad
:
449 load_flags
|= net::LOAD_PREFERRING_CACHE
;
451 case WebURLRequest::ReturnCacheDataDontLoad
:
452 load_flags
|= net::LOAD_ONLY_FROM_CACHE
;
454 case WebURLRequest::UseProtocolCachePolicy
:
458 if (request
.reportUploadProgress())
459 load_flags
|= net::LOAD_ENABLE_UPLOAD_PROGRESS
;
460 if (request
.reportLoadTiming())
461 load_flags
|= net::LOAD_ENABLE_LOAD_TIMING
;
462 if (request
.reportRawHeaders())
463 load_flags
|= net::LOAD_REPORT_RAW_HEADERS
;
465 if (!request
.allowCookies() || !request
.allowStoredCredentials()) {
466 load_flags
|= net::LOAD_DO_NOT_SAVE_COOKIES
;
467 load_flags
|= net::LOAD_DO_NOT_SEND_COOKIES
;
470 if (!request
.allowStoredCredentials())
471 load_flags
|= net::LOAD_DO_NOT_SEND_AUTH_DATA
;
473 HeaderFlattener
flattener(load_flags
);
474 request
.visitHTTPHeaderFields(&flattener
);
476 // TODO(brettw) this should take parameter encoding into account when
477 // creating the GURLs.
479 ResourceLoaderBridge::RequestInfo request_info
;
480 request_info
.method
= method
;
481 request_info
.url
= url
;
482 request_info
.first_party_for_cookies
= request
.firstPartyForCookies();
483 request_info
.referrer
= referrer_url
;
484 request_info
.headers
= flattener
.GetBuffer();
485 request_info
.load_flags
= load_flags
;
486 // requestor_pid only needs to be non-zero if the request originates outside
487 // the render process, so we can use requestorProcessID even for requests
488 // from in-process plugins.
489 request_info
.requestor_pid
= request
.requestorProcessID();
490 request_info
.request_type
=
491 ResourceType::FromTargetType(request
.targetType());
492 request_info
.priority
=
493 ConvertWebKitPriorityToNetPriority(request
.priority());
494 request_info
.appcache_host_id
= request
.appCacheHostID();
495 request_info
.routing_id
= request
.requestorID();
496 request_info
.download_to_file
= request
.downloadToFile();
497 request_info
.has_user_gesture
= request
.hasUserGesture();
498 request_info
.extra_data
= request
.extraData();
499 if (request
.extraData()) {
500 referrer_policy_
= static_cast<WebURLRequestExtraDataImpl
*>(
501 request
.extraData())->referrer_policy();
502 request_info
.referrer_policy
= referrer_policy_
;
504 bridge_
.reset(platform
->CreateResourceLoader(request_info
));
506 if (!request
.httpBody().isNull()) {
507 // GET and HEAD requests shouldn't have http bodies.
508 DCHECK(method
!= "GET" && method
!= "HEAD");
509 const WebHTTPBody
& httpBody
= request
.httpBody();
511 WebHTTPBody::Element element
;
512 scoped_refptr
<ResourceRequestBody
> request_body
= new ResourceRequestBody
;
513 while (httpBody
.elementAt(i
++, element
)) {
514 switch (element
.type
) {
515 case WebHTTPBody::Element::TypeData
:
516 if (!element
.data
.isEmpty()) {
517 // WebKit sometimes gives up empty data to append. These aren't
518 // necessary so we just optimize those out here.
519 request_body
->AppendBytes(
520 element
.data
.data(), static_cast<int>(element
.data
.size()));
523 case WebHTTPBody::Element::TypeFile
:
524 if (element
.fileLength
== -1) {
525 request_body
->AppendFileRange(
526 base::FilePath::FromUTF16Unsafe(element
.filePath
),
527 0, kuint64max
, base::Time());
529 request_body
->AppendFileRange(
530 base::FilePath::FromUTF16Unsafe(element
.filePath
),
531 static_cast<uint64
>(element
.fileStart
),
532 static_cast<uint64
>(element
.fileLength
),
533 base::Time::FromDoubleT(element
.modificationTime
));
536 case WebHTTPBody::Element::TypeURL
: {
537 GURL url
= GURL(element
.url
);
538 DCHECK(url
.SchemeIsFileSystem());
539 request_body
->AppendFileSystemFileRange(
541 static_cast<uint64
>(element
.fileStart
),
542 static_cast<uint64
>(element
.fileLength
),
543 base::Time::FromDoubleT(element
.modificationTime
));
546 case WebHTTPBody::Element::TypeBlob
:
547 request_body
->AppendBlob(GURL(element
.blobURL
));
553 request_body
->set_identifier(request
.httpBody().identifier());
554 bridge_
->SetRequestBody(request_body
.get());
557 if (sync_load_response
) {
558 bridge_
->SyncLoad(sync_load_response
);
562 if (bridge_
->Start(this)) {
563 AddRef(); // Balanced in OnCompletedRequest
569 void WebURLLoaderImpl::Context::OnUploadProgress(uint64 position
, uint64 size
) {
571 client_
->didSendData(loader_
, position
, size
);
574 bool WebURLLoaderImpl::Context::OnReceivedRedirect(
576 const ResourceResponseInfo
& info
,
577 bool* has_new_first_party_for_cookies
,
578 GURL
* new_first_party_for_cookies
) {
582 WebURLResponse response
;
583 response
.initialize();
584 PopulateURLResponse(request_
.url(), info
, &response
);
586 // TODO(darin): We lack sufficient information to construct the actual
587 // request that resulted from the redirect.
588 WebURLRequest
new_request(new_url
);
589 new_request
.setFirstPartyForCookies(request_
.firstPartyForCookies());
590 new_request
.setDownloadToFile(request_
.downloadToFile());
592 WebString referrer_string
= WebString::fromUTF8("Referer");
593 WebString referrer
= WebSecurityPolicy::generateReferrerHeader(
596 request_
.httpHeaderField(referrer_string
));
597 if (!referrer
.isEmpty())
598 new_request
.setHTTPHeaderField(referrer_string
, referrer
);
600 if (response
.httpStatusCode() == 307)
601 new_request
.setHTTPMethod(request_
.httpMethod());
603 client_
->willSendRequest(loader_
, new_request
, response
);
604 request_
= new_request
;
605 *has_new_first_party_for_cookies
= true;
606 *new_first_party_for_cookies
= request_
.firstPartyForCookies();
608 // Only follow the redirect if WebKit left the URL unmodified.
609 if (new_url
== GURL(new_request
.url()))
612 // We assume that WebKit only changes the URL to suppress a redirect, and we
613 // assume that it does so by setting it to be invalid.
614 DCHECK(!new_request
.url().isValid());
618 void WebURLLoaderImpl::Context::OnReceivedResponse(
619 const ResourceResponseInfo
& info
) {
623 WebURLResponse response
;
624 response
.initialize();
625 PopulateURLResponse(request_
.url(), info
, &response
);
627 bool show_raw_listing
= (GURL(request_
.url()).query() == "raw");
629 if (info
.mime_type
== "text/vnd.chromium.ftp-dir") {
630 if (show_raw_listing
) {
631 // Set the MIME type to plain text to prevent any active content.
632 response
.setMIMEType("text/plain");
634 // We're going to produce a parsed listing in HTML.
635 response
.setMIMEType("text/html");
639 scoped_refptr
<Context
> protect(this);
640 client_
->didReceiveResponse(loader_
, response
);
642 // We may have been cancelled after didReceiveResponse, which would leave us
643 // without a client and therefore without much need to do further handling.
647 DCHECK(!ftp_listing_delegate_
.get());
648 DCHECK(!multipart_delegate_
.get());
649 if (info
.headers
.get() && info
.mime_type
== "multipart/x-mixed-replace") {
650 std::string content_type
;
651 info
.headers
->EnumerateHeader(NULL
, "content-type", &content_type
);
653 std::string mime_type
;
655 bool had_charset
= false;
656 std::string boundary
;
657 net::HttpUtil::ParseContentType(content_type
, &mime_type
, &charset
,
658 &had_charset
, &boundary
);
659 TrimString(boundary
, " \"", &boundary
);
661 // If there's no boundary, just handle the request normally. In the gecko
662 // code, nsMultiMixedConv::OnStartRequest throws an exception.
663 if (!boundary
.empty()) {
664 multipart_delegate_
.reset(
665 new MultipartResponseDelegate(client_
, loader_
, response
, boundary
));
667 } else if (info
.mime_type
== "text/vnd.chromium.ftp-dir" &&
669 ftp_listing_delegate_
.reset(
670 new FtpDirectoryListingResponseDelegate(client_
, loader_
, response
));
674 void WebURLLoaderImpl::Context::OnDownloadedData(int len
) {
676 client_
->didDownloadData(loader_
, len
);
679 void WebURLLoaderImpl::Context::OnReceivedData(const char* data
,
681 int encoded_data_length
) {
685 if (ftp_listing_delegate_
) {
686 // The FTP listing delegate will make the appropriate calls to
687 // client_->didReceiveData and client_->didReceiveResponse.
688 ftp_listing_delegate_
->OnReceivedData(data
, data_length
);
689 } else if (multipart_delegate_
) {
690 // The multipart delegate will make the appropriate calls to
691 // client_->didReceiveData and client_->didReceiveResponse.
692 multipart_delegate_
->OnReceivedData(data
, data_length
, encoded_data_length
);
694 client_
->didReceiveData(loader_
, data
, data_length
, encoded_data_length
);
698 void WebURLLoaderImpl::Context::OnReceivedCachedMetadata(
699 const char* data
, int len
) {
701 client_
->didReceiveCachedMetadata(loader_
, data
, len
);
704 void WebURLLoaderImpl::Context::OnCompletedRequest(
706 bool was_ignored_by_handler
,
707 const std::string
& security_info
,
708 const base::TimeTicks
& completion_time
) {
709 if (ftp_listing_delegate_
) {
710 ftp_listing_delegate_
->OnCompletedRequest();
711 ftp_listing_delegate_
.reset(NULL
);
712 } else if (multipart_delegate_
) {
713 multipart_delegate_
->OnCompletedRequest();
714 multipart_delegate_
.reset(NULL
);
717 // Prevent any further IPC to the browser now that we're complete, but
718 // don't delete it to keep any downloaded temp files alive.
719 DCHECK(!completed_bridge_
.get());
720 completed_bridge_
.swap(bridge_
);
723 if (error_code
!= net::OK
) {
724 client_
->didFail(loader_
, CreateError(request_
.url(), error_code
));
726 client_
->didFinishLoading(
727 loader_
, (completion_time
- TimeTicks()).InSecondsF());
731 // We are done with the bridge now, and so we need to release the reference
732 // to ourselves that we took on behalf of the bridge. This may cause our
737 bool WebURLLoaderImpl::Context::CanHandleDataURL(const GURL
& url
) const {
738 DCHECK(url
.SchemeIs("data"));
740 // Optimize for the case where we can handle a data URL locally. We must
741 // skip this for data URLs targetted at frames since those could trigger a
744 // NOTE: We special case MIME types we can render both for performance
745 // reasons as well as to support unit tests, which do not have an underlying
746 // ResourceLoaderBridge implementation.
748 #if defined(OS_ANDROID)
749 // For compatibility reasons on Android we need to expose top-level data://
751 if (request_
.targetType() == WebURLRequest::TargetIsMainFrame
)
755 if (request_
.targetType() != WebURLRequest::TargetIsMainFrame
&&
756 request_
.targetType() != WebURLRequest::TargetIsSubframe
)
759 std::string mime_type
, unused_charset
;
760 if (net::DataURL::Parse(url
, &mime_type
, &unused_charset
, NULL
) &&
761 net::IsSupportedMimeType(mime_type
))
767 void WebURLLoaderImpl::Context::HandleDataURL() {
768 ResourceResponseInfo info
;
772 if (GetInfoFromDataURL(request_
.url(), &info
, &data
, &error_code
)) {
773 OnReceivedResponse(info
);
775 OnReceivedData(data
.data(), data
.size(), 0);
778 OnCompletedRequest(error_code
, false, info
.security_info
,
779 base::TimeTicks::Now());
782 // WebURLLoaderImpl -----------------------------------------------------------
784 WebURLLoaderImpl::WebURLLoaderImpl(WebKitPlatformSupportImpl
* platform
)
785 : context_(new Context(this)),
786 platform_(platform
) {
789 WebURLLoaderImpl::~WebURLLoaderImpl() {
793 WebURLError
WebURLLoaderImpl::CreateError(const WebURL
& unreachable_url
,
796 error
.domain
= WebString::fromUTF8(net::kErrorDomain
);
797 error
.reason
= reason
;
798 error
.unreachableURL
= unreachable_url
;
799 if (reason
== net::ERR_ABORTED
) {
800 error
.isCancellation
= true;
801 } else if (reason
== net::ERR_TEMPORARILY_THROTTLED
) {
802 error
.localizedDescription
= WebString::fromUTF8(
803 kThrottledErrorDescription
);
808 void WebURLLoaderImpl::loadSynchronously(const WebURLRequest
& request
,
809 WebURLResponse
& response
,
812 ResourceLoaderBridge::SyncLoadResponse sync_load_response
;
813 context_
->Start(request
, &sync_load_response
, platform_
);
815 const GURL
& final_url
= sync_load_response
.url
;
817 // TODO(tc): For file loads, we may want to include a more descriptive
818 // status code or status text.
819 int error_code
= sync_load_response
.error_code
;
820 if (error_code
!= net::OK
) {
821 response
.setURL(final_url
);
822 error
.domain
= WebString::fromUTF8(net::kErrorDomain
);
823 error
.reason
= error_code
;
824 error
.unreachableURL
= final_url
;
828 PopulateURLResponse(final_url
, sync_load_response
, &response
);
830 data
.assign(sync_load_response
.data
.data(),
831 sync_load_response
.data
.size());
834 void WebURLLoaderImpl::loadAsynchronously(const WebURLRequest
& request
,
835 WebURLLoaderClient
* client
) {
836 DCHECK(!context_
->client());
838 context_
->set_client(client
);
839 context_
->Start(request
, NULL
, platform_
);
842 void WebURLLoaderImpl::cancel() {
846 void WebURLLoaderImpl::setDefersLoading(bool value
) {
847 context_
->SetDefersLoading(value
);
850 void WebURLLoaderImpl::didChangePriority(WebURLRequest::Priority new_priority
) {
851 context_
->DidChangePriority(new_priority
);
854 } // namespace webkit_glue