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/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/Source/Platform/chromium/public/WebHTTPHeaderVisitor.h"
24 #include "third_party/WebKit/Source/Platform/chromium/public/WebHTTPLoadInfo.h"
25 #include "third_party/WebKit/Source/Platform/chromium/public/WebURL.h"
26 #include "third_party/WebKit/Source/Platform/chromium/public/WebURLError.h"
27 #include "third_party/WebKit/Source/Platform/chromium/public/WebURLLoaderClient.h"
28 #include "third_party/WebKit/Source/Platform/chromium/public/WebURLLoadTiming.h"
29 #include "third_party/WebKit/Source/Platform/chromium/public/WebURLRequest.h"
30 #include "third_party/WebKit/Source/Platform/chromium/public/WebURLResponse.h"
31 #include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityPolicy.h"
32 #include "webkit/base/file_path_string_conversions.h"
33 #include "webkit/glue/ftp_directory_listing_response_delegate.h"
34 #include "webkit/glue/multipart_response_delegate.h"
35 #include "webkit/glue/resource_loader_bridge.h"
36 #include "webkit/glue/resource_request_body.h"
37 #include "webkit/glue/webkitplatformsupport_impl.h"
38 #include "webkit/glue/weburlrequest_extradata_impl.h"
39 #include "webkit/glue/weburlresponse_extradata_impl.h"
42 using base::TimeTicks
;
43 using WebKit::WebData
;
44 using WebKit::WebHTTPBody
;
45 using WebKit::WebHTTPHeaderVisitor
;
46 using WebKit::WebHTTPLoadInfo
;
47 using WebKit::WebReferrerPolicy
;
48 using WebKit::WebSecurityPolicy
;
49 using WebKit::WebString
;
51 using WebKit::WebURLError
;
52 using WebKit::WebURLLoadTiming
;
53 using WebKit::WebURLLoader
;
54 using WebKit::WebURLLoaderClient
;
55 using WebKit::WebURLRequest
;
56 using WebKit::WebURLResponse
;
58 namespace webkit_glue
{
60 // Utilities ------------------------------------------------------------------
64 const char kThrottledErrorDescription
[] =
65 "Request throttled. Visit http://dev.chromium.org/throttling for more "
68 class HeaderFlattener
: public WebHTTPHeaderVisitor
{
70 explicit HeaderFlattener(int load_flags
)
71 : load_flags_(load_flags
),
72 has_accept_header_(false) {
75 virtual void visitHeader(const WebString
& name
, const WebString
& value
) {
76 // TODO(darin): is UTF-8 really correct here? It is if the strings are
77 // already ASCII (i.e., if they are already escaped properly).
78 const std::string
& name_utf8
= name
.utf8();
79 const std::string
& value_utf8
= value
.utf8();
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_utf8
, "referer"))
86 // Skip over "Cache-Control: max-age=0" header if the corresponding
87 // load flag is already specified. FrameLoader sets both the flag and
88 // the extra header -- the extra header is redundant since our network
89 // implementation will add the necessary headers based on load flags.
90 // See http://code.google.com/p/chromium/issues/detail?id=3434.
91 if ((load_flags_
& net::LOAD_VALIDATE_CACHE
) &&
92 LowerCaseEqualsASCII(name_utf8
, "cache-control") &&
93 LowerCaseEqualsASCII(value_utf8
, "max-age=0"))
96 if (LowerCaseEqualsASCII(name_utf8
, "accept"))
97 has_accept_header_
= true;
100 buffer_
.append("\r\n");
101 buffer_
.append(name_utf8
+ ": " + value_utf8
);
104 const std::string
& GetBuffer() {
105 // In some cases, WebKit doesn't add an Accept header, but not having the
106 // header confuses some web servers. See bug 808613.
107 if (!has_accept_header_
) {
108 if (!buffer_
.empty())
109 buffer_
.append("\r\n");
110 buffer_
.append("Accept: */*");
111 has_accept_header_
= true;
119 bool has_accept_header_
;
122 // Extracts the information from a data: url.
123 bool GetInfoFromDataURL(const GURL
& url
,
124 ResourceResponseInfo
* info
,
127 std::string mime_type
;
129 if (net::DataURL::Parse(url
, &mime_type
, &charset
, data
)) {
130 *error_code
= net::OK
;
131 // Assure same time for all time fields of data: URLs.
132 Time now
= Time::Now();
133 info
->load_timing
.request_start
= TimeTicks::Now();
134 info
->load_timing
.request_start_time
= now
;
135 info
->request_time
= now
;
136 info
->response_time
= now
;
137 info
->headers
= NULL
;
138 info
->mime_type
.swap(mime_type
);
139 info
->charset
.swap(charset
);
140 info
->security_info
.clear();
141 info
->content_length
= data
->length();
142 info
->encoded_data_length
= 0;
147 *error_code
= net::ERR_INVALID_URL
;
151 typedef ResourceDevToolsInfo::HeadersVector HeadersVector
;
153 // Given a base time and a second time, returns the time from the base time to
154 // the second time, in milliseconds. If the second time is null, returns -1.
155 // The base time must not be null.
156 int TimeTicksToOffset(base::TimeTicks base_time
, base::TimeTicks time
) {
159 DCHECK(!base_time
.is_null());
160 return static_cast<int>((time
- base_time
).InMillisecondsRoundedUp());
163 // Converts timing data from |load_timing| to the format used by WebKit.
164 void PopulateURLLoadTiming(const net::LoadTimingInfo
& load_timing
,
165 WebURLLoadTiming
* url_timing
) {
166 DCHECK(!load_timing
.request_start
.is_null());
168 url_timing
->initialize();
169 url_timing
->setRequestTime(
170 (load_timing
.request_start
- TimeTicks()).InSecondsF());
171 url_timing
->setProxyStart(TimeTicksToOffset(load_timing
.request_start
,
172 load_timing
.proxy_resolve_start
));
173 url_timing
->setProxyEnd(TimeTicksToOffset(load_timing
.request_start
,
174 load_timing
.proxy_resolve_end
));
175 url_timing
->setDNSStart(TimeTicksToOffset(
176 load_timing
.request_start
,
177 load_timing
.connect_timing
.dns_start
));
178 url_timing
->setDNSEnd(TimeTicksToOffset(load_timing
.request_start
,
179 load_timing
.connect_timing
.dns_end
));
180 url_timing
->setConnectStart(
181 TimeTicksToOffset(load_timing
.request_start
,
182 load_timing
.connect_timing
.connect_start
));
183 url_timing
->setConnectEnd(
184 TimeTicksToOffset(load_timing
.request_start
,
185 load_timing
.connect_timing
.connect_end
));
186 url_timing
->setSSLStart(
187 TimeTicksToOffset(load_timing
.request_start
,
188 load_timing
.connect_timing
.ssl_start
));
189 url_timing
->setSSLEnd(TimeTicksToOffset(load_timing
.request_start
,
190 load_timing
.connect_timing
.ssl_end
));
191 url_timing
->setSendStart(TimeTicksToOffset(load_timing
.request_start
,
192 load_timing
.send_start
));
193 url_timing
->setSendEnd(TimeTicksToOffset(load_timing
.request_start
,
194 load_timing
.send_end
));
195 url_timing
->setReceiveHeadersEnd(
196 TimeTicksToOffset(load_timing
.request_start
,
197 load_timing
.receive_headers_end
));
200 void PopulateURLResponse(
202 const ResourceResponseInfo
& info
,
203 WebURLResponse
* response
) {
204 response
->setURL(url
);
205 response
->setResponseTime(info
.response_time
.ToDoubleT());
206 response
->setMIMEType(WebString::fromUTF8(info
.mime_type
));
207 response
->setTextEncodingName(WebString::fromUTF8(info
.charset
));
208 response
->setExpectedContentLength(info
.content_length
);
209 response
->setSecurityInfo(info
.security_info
);
210 response
->setAppCacheID(info
.appcache_id
);
211 response
->setAppCacheManifestURL(info
.appcache_manifest_url
);
212 response
->setWasCached(!info
.load_timing
.request_start_time
.is_null() &&
213 info
.response_time
< info
.load_timing
.request_start_time
);
214 response
->setRemoteIPAddress(
215 WebString::fromUTF8(info
.socket_address
.host()));
216 response
->setRemotePort(info
.socket_address
.port());
217 response
->setConnectionID(info
.load_timing
.socket_log_id
);
218 response
->setConnectionReused(info
.load_timing
.socket_reused
);
219 response
->setDownloadFilePath(
220 webkit_base::FilePathToWebString(info
.download_file_path
));
221 WebURLResponseExtraDataImpl
* extra_data
=
222 new WebURLResponseExtraDataImpl(info
.npn_negotiated_protocol
);
223 response
->setExtraData(extra_data
);
224 extra_data
->set_was_fetched_via_spdy(info
.was_fetched_via_spdy
);
225 extra_data
->set_was_npn_negotiated(info
.was_npn_negotiated
);
226 extra_data
->set_was_alternate_protocol_available(
227 info
.was_alternate_protocol_available
);
228 extra_data
->set_was_fetched_via_proxy(info
.was_fetched_via_proxy
);
230 // If there's no received headers end time, don't set load timing. This is
231 // the case for non-HTTP requests, requests that don't go over the wire, and
232 // certain error cases.
233 if (!info
.load_timing
.receive_headers_end
.is_null()) {
234 WebURLLoadTiming timing
;
235 PopulateURLLoadTiming(info
.load_timing
, &timing
);
236 response
->setLoadTiming(timing
);
239 if (info
.devtools_info
.get()) {
240 WebHTTPLoadInfo load_info
;
242 load_info
.setHTTPStatusCode(info
.devtools_info
->http_status_code
);
243 load_info
.setHTTPStatusText(WebString::fromUTF8(
244 info
.devtools_info
->http_status_text
));
245 load_info
.setEncodedDataLength(info
.encoded_data_length
);
247 load_info
.setRequestHeadersText(WebString::fromUTF8(
248 info
.devtools_info
->request_headers_text
));
249 load_info
.setResponseHeadersText(WebString::fromUTF8(
250 info
.devtools_info
->response_headers_text
));
251 const HeadersVector
& request_headers
= info
.devtools_info
->request_headers
;
252 for (HeadersVector::const_iterator it
= request_headers
.begin();
253 it
!= request_headers
.end(); ++it
) {
254 load_info
.addRequestHeader(WebString::fromUTF8(it
->first
),
255 WebString::fromUTF8(it
->second
));
257 const HeadersVector
& response_headers
=
258 info
.devtools_info
->response_headers
;
259 for (HeadersVector::const_iterator it
= response_headers
.begin();
260 it
!= response_headers
.end(); ++it
) {
261 load_info
.addResponseHeader(WebString::fromUTF8(it
->first
),
262 WebString::fromUTF8(it
->second
));
264 response
->setHTTPLoadInfo(load_info
);
267 const net::HttpResponseHeaders
* headers
= info
.headers
;
271 WebURLResponse::HTTPVersion version
= WebURLResponse::Unknown
;
272 if (headers
->GetHttpVersion() == net::HttpVersion(0, 9))
273 version
= WebURLResponse::HTTP_0_9
;
274 else if (headers
->GetHttpVersion() == net::HttpVersion(1, 0))
275 version
= WebURLResponse::HTTP_1_0
;
276 else if (headers
->GetHttpVersion() == net::HttpVersion(1, 1))
277 version
= WebURLResponse::HTTP_1_1
;
278 response
->setHTTPVersion(version
);
279 response
->setHTTPStatusCode(headers
->response_code());
280 response
->setHTTPStatusText(WebString::fromUTF8(headers
->GetStatusText()));
282 // TODO(darin): We should leverage HttpResponseHeaders for this, and this
283 // should be using the same code as ResourceDispatcherHost.
284 // TODO(jungshik): Figure out the actual value of the referrer charset and
285 // pass it to GetSuggestedFilename.
287 headers
->EnumerateHeader(NULL
, "content-disposition", &value
);
288 response
->setSuggestedFileName(
289 net::GetSuggestedFilename(url
,
291 std::string(), // referrer_charset
292 std::string(), // suggested_name
293 std::string(), // mime_type
294 std::string())); // default_name
297 if (headers
->GetLastModifiedValue(&time_val
))
298 response
->setLastModifiedDate(time_val
.ToDoubleT());
300 // Build up the header map.
303 while (headers
->EnumerateHeaderLines(&iter
, &name
, &value
)) {
304 response
->addHTTPHeaderField(WebString::fromUTF8(name
),
305 WebString::fromUTF8(value
));
309 net::RequestPriority
ConvertWebKitPriorityToNetPriority(
310 const WebURLRequest::Priority
& priority
) {
312 case WebURLRequest::PriorityVeryHigh
:
315 case WebURLRequest::PriorityHigh
:
318 case WebURLRequest::PriorityMedium
:
321 case WebURLRequest::PriorityLow
:
324 case WebURLRequest::PriorityVeryLow
:
327 case WebURLRequest::PriorityUnresolved
:
336 // WebURLLoaderImpl::Context --------------------------------------------------
338 // This inner class exists since the WebURLLoader may be deleted while inside a
339 // call to WebURLLoaderClient. The bridge requires its Peer to stay alive
340 // until it receives OnCompletedRequest.
341 class WebURLLoaderImpl::Context
: public base::RefCounted
<Context
>,
342 public ResourceLoaderBridge::Peer
{
344 explicit Context(WebURLLoaderImpl
* loader
);
346 WebURLLoaderClient
* client() const { return client_
; }
347 void set_client(WebURLLoaderClient
* client
) { client_
= client
; }
350 void SetDefersLoading(bool value
);
351 void DidChangePriority(WebURLRequest::Priority new_priority
);
353 const WebURLRequest
& request
,
354 ResourceLoaderBridge::SyncLoadResponse
* sync_load_response
,
355 WebKitPlatformSupportImpl
* platform
);
357 // ResourceLoaderBridge::Peer methods:
358 virtual void OnUploadProgress(uint64 position
, uint64 size
) OVERRIDE
;
359 virtual bool OnReceivedRedirect(
361 const ResourceResponseInfo
& info
,
362 bool* has_new_first_party_for_cookies
,
363 GURL
* new_first_party_for_cookies
) OVERRIDE
;
364 virtual void OnReceivedResponse(const ResourceResponseInfo
& info
) OVERRIDE
;
365 virtual void OnDownloadedData(int len
) OVERRIDE
;
366 virtual void OnReceivedData(const char* data
,
368 int encoded_data_length
) OVERRIDE
;
369 virtual void OnReceivedCachedMetadata(const char* data
, int len
) OVERRIDE
;
370 virtual void OnCompletedRequest(
372 bool was_ignored_by_handler
,
373 const std::string
& security_info
,
374 const base::TimeTicks
& completion_time
) OVERRIDE
;
377 friend class base::RefCounted
<Context
>;
378 virtual ~Context() {}
380 // We can optimize the handling of data URLs in most cases.
381 bool CanHandleDataURL(const GURL
& url
) const;
382 void HandleDataURL();
384 WebURLLoaderImpl
* loader_
;
385 WebURLRequest request_
;
386 WebURLLoaderClient
* client_
;
387 WebReferrerPolicy referrer_policy_
;
388 scoped_ptr
<ResourceLoaderBridge
> bridge_
;
389 scoped_ptr
<FtpDirectoryListingResponseDelegate
> ftp_listing_delegate_
;
390 scoped_ptr
<MultipartResponseDelegate
> multipart_delegate_
;
391 scoped_ptr
<ResourceLoaderBridge
> completed_bridge_
;
394 WebURLLoaderImpl::Context::Context(WebURLLoaderImpl
* loader
)
397 referrer_policy_(WebKit::WebReferrerPolicyDefault
) {
400 void WebURLLoaderImpl::Context::Cancel() {
401 // The bridge will still send OnCompletedRequest, which will Release() us, so
402 // we don't do that here.
406 // Ensure that we do not notify the multipart delegate anymore as it has
407 // its own pointer to the client.
408 if (multipart_delegate_
.get())
409 multipart_delegate_
->Cancel();
411 // Do not make any further calls to the client.
416 void WebURLLoaderImpl::Context::SetDefersLoading(bool value
) {
418 bridge_
->SetDefersLoading(value
);
421 void WebURLLoaderImpl::Context::DidChangePriority(
422 WebURLRequest::Priority new_priority
) {
424 bridge_
->DidChangePriority(
425 ConvertWebKitPriorityToNetPriority(new_priority
));
428 void WebURLLoaderImpl::Context::Start(
429 const WebURLRequest
& request
,
430 ResourceLoaderBridge::SyncLoadResponse
* sync_load_response
,
431 WebKitPlatformSupportImpl
* platform
) {
432 DCHECK(!bridge_
.get());
434 request_
= request
; // Save the request.
436 GURL url
= request
.url();
437 if (url
.SchemeIs("data") && CanHandleDataURL(url
)) {
438 if (sync_load_response
) {
439 // This is a sync load. Do the work now.
440 sync_load_response
->url
= url
;
442 GetInfoFromDataURL(sync_load_response
->url
, sync_load_response
,
443 &sync_load_response
->data
,
444 &sync_load_response
->error_code
);
446 AddRef(); // Balanced in OnCompletedRequest
447 MessageLoop::current()->PostTask(FROM_HERE
,
448 base::Bind(&Context::HandleDataURL
, this));
454 request
.httpHeaderField(WebString::fromUTF8("Referer")).utf8());
455 const std::string
& method
= request
.httpMethod().utf8();
457 int load_flags
= net::LOAD_NORMAL
;
458 switch (request
.cachePolicy()) {
459 case WebURLRequest::ReloadIgnoringCacheData
:
460 // Required by LayoutTests/http/tests/misc/refresh-headers.php
461 load_flags
|= net::LOAD_VALIDATE_CACHE
;
463 case WebURLRequest::ReturnCacheDataElseLoad
:
464 load_flags
|= net::LOAD_PREFERRING_CACHE
;
466 case WebURLRequest::ReturnCacheDataDontLoad
:
467 load_flags
|= net::LOAD_ONLY_FROM_CACHE
;
469 case WebURLRequest::UseProtocolCachePolicy
:
473 if (request
.reportUploadProgress())
474 load_flags
|= net::LOAD_ENABLE_UPLOAD_PROGRESS
;
475 if (request
.reportLoadTiming())
476 load_flags
|= net::LOAD_ENABLE_LOAD_TIMING
;
477 if (request
.reportRawHeaders())
478 load_flags
|= net::LOAD_REPORT_RAW_HEADERS
;
480 if (!request
.allowCookies() || !request
.allowStoredCredentials()) {
481 load_flags
|= net::LOAD_DO_NOT_SAVE_COOKIES
;
482 load_flags
|= net::LOAD_DO_NOT_SEND_COOKIES
;
485 if (!request
.allowStoredCredentials())
486 load_flags
|= net::LOAD_DO_NOT_SEND_AUTH_DATA
;
488 HeaderFlattener
flattener(load_flags
);
489 request
.visitHTTPHeaderFields(&flattener
);
491 // TODO(brettw) this should take parameter encoding into account when
492 // creating the GURLs.
494 ResourceLoaderBridge::RequestInfo request_info
;
495 request_info
.method
= method
;
496 request_info
.url
= url
;
497 request_info
.first_party_for_cookies
= request
.firstPartyForCookies();
498 request_info
.referrer
= referrer_url
;
499 request_info
.headers
= flattener
.GetBuffer();
500 request_info
.load_flags
= load_flags
;
501 // requestor_pid only needs to be non-zero if the request originates outside
502 // the render process, so we can use requestorProcessID even for requests
503 // from in-process plugins.
504 request_info
.requestor_pid
= request
.requestorProcessID();
505 request_info
.request_type
=
506 ResourceType::FromTargetType(request
.targetType());
507 request_info
.priority
=
508 ConvertWebKitPriorityToNetPriority(request
.priority());
509 request_info
.appcache_host_id
= request
.appCacheHostID();
510 request_info
.routing_id
= request
.requestorID();
511 request_info
.download_to_file
= request
.downloadToFile();
512 request_info
.has_user_gesture
= request
.hasUserGesture();
513 request_info
.extra_data
= request
.extraData();
514 if (request
.extraData()) {
515 referrer_policy_
= static_cast<WebURLRequestExtraDataImpl
*>(
516 request
.extraData())->referrer_policy();
517 request_info
.referrer_policy
= referrer_policy_
;
519 bridge_
.reset(platform
->CreateResourceLoader(request_info
));
521 if (!request
.httpBody().isNull()) {
522 // GET and HEAD requests shouldn't have http bodies.
523 DCHECK(method
!= "GET" && method
!= "HEAD");
524 const WebHTTPBody
& httpBody
= request
.httpBody();
526 WebHTTPBody::Element element
;
527 scoped_refptr
<ResourceRequestBody
> request_body
= new ResourceRequestBody
;
528 while (httpBody
.elementAt(i
++, element
)) {
529 switch (element
.type
) {
530 case WebHTTPBody::Element::TypeData
:
531 if (!element
.data
.isEmpty()) {
532 // WebKit sometimes gives up empty data to append. These aren't
533 // necessary so we just optimize those out here.
534 request_body
->AppendBytes(
535 element
.data
.data(), static_cast<int>(element
.data
.size()));
538 case WebHTTPBody::Element::TypeFile
:
539 if (element
.fileLength
== -1) {
540 request_body
->AppendFileRange(
541 webkit_base::WebStringToFilePath(element
.filePath
),
542 0, kuint64max
, base::Time());
544 request_body
->AppendFileRange(
545 webkit_base::WebStringToFilePath(element
.filePath
),
546 static_cast<uint64
>(element
.fileStart
),
547 static_cast<uint64
>(element
.fileLength
),
548 base::Time::FromDoubleT(element
.modificationTime
));
551 case WebHTTPBody::Element::TypeURL
: {
552 GURL url
= GURL(element
.url
);
553 DCHECK(url
.SchemeIsFileSystem());
554 request_body
->AppendFileSystemFileRange(
556 static_cast<uint64
>(element
.fileStart
),
557 static_cast<uint64
>(element
.fileLength
),
558 base::Time::FromDoubleT(element
.modificationTime
));
561 case WebHTTPBody::Element::TypeBlob
:
562 request_body
->AppendBlob(GURL(element
.blobURL
));
568 request_body
->set_identifier(request
.httpBody().identifier());
569 bridge_
->SetRequestBody(request_body
);
572 if (sync_load_response
) {
573 bridge_
->SyncLoad(sync_load_response
);
577 if (bridge_
->Start(this)) {
578 AddRef(); // Balanced in OnCompletedRequest
584 void WebURLLoaderImpl::Context::OnUploadProgress(uint64 position
, uint64 size
) {
586 client_
->didSendData(loader_
, position
, size
);
589 bool WebURLLoaderImpl::Context::OnReceivedRedirect(
591 const ResourceResponseInfo
& info
,
592 bool* has_new_first_party_for_cookies
,
593 GURL
* new_first_party_for_cookies
) {
597 WebURLResponse response
;
598 response
.initialize();
599 PopulateURLResponse(request_
.url(), info
, &response
);
601 // TODO(darin): We lack sufficient information to construct the actual
602 // request that resulted from the redirect.
603 WebURLRequest
new_request(new_url
);
604 new_request
.setFirstPartyForCookies(request_
.firstPartyForCookies());
605 new_request
.setDownloadToFile(request_
.downloadToFile());
607 WebString referrer_string
= WebString::fromUTF8("Referer");
608 WebString referrer
= WebSecurityPolicy::generateReferrerHeader(
611 request_
.httpHeaderField(referrer_string
));
612 if (!referrer
.isEmpty())
613 new_request
.setHTTPHeaderField(referrer_string
, referrer
);
615 if (response
.httpStatusCode() == 307)
616 new_request
.setHTTPMethod(request_
.httpMethod());
618 client_
->willSendRequest(loader_
, new_request
, response
);
619 request_
= new_request
;
620 *has_new_first_party_for_cookies
= true;
621 *new_first_party_for_cookies
= request_
.firstPartyForCookies();
623 // Only follow the redirect if WebKit left the URL unmodified.
624 if (new_url
== GURL(new_request
.url()))
627 // We assume that WebKit only changes the URL to suppress a redirect, and we
628 // assume that it does so by setting it to be invalid.
629 DCHECK(!new_request
.url().isValid());
633 void WebURLLoaderImpl::Context::OnReceivedResponse(
634 const ResourceResponseInfo
& info
) {
638 WebURLResponse response
;
639 response
.initialize();
640 PopulateURLResponse(request_
.url(), info
, &response
);
642 bool show_raw_listing
= (GURL(request_
.url()).query() == "raw");
644 if (info
.mime_type
== "text/vnd.chromium.ftp-dir") {
645 if (show_raw_listing
) {
646 // Set the MIME type to plain text to prevent any active content.
647 response
.setMIMEType("text/plain");
649 // We're going to produce a parsed listing in HTML.
650 response
.setMIMEType("text/html");
654 client_
->didReceiveResponse(loader_
, response
);
656 // We may have been cancelled after didReceiveResponse, which would leave us
657 // without a client and therefore without much need to do further handling.
661 DCHECK(!ftp_listing_delegate_
.get());
662 DCHECK(!multipart_delegate_
.get());
663 if (info
.headers
&& info
.mime_type
== "multipart/x-mixed-replace") {
664 std::string content_type
;
665 info
.headers
->EnumerateHeader(NULL
, "content-type", &content_type
);
667 std::string mime_type
;
669 bool had_charset
= false;
670 std::string boundary
;
671 net::HttpUtil::ParseContentType(content_type
, &mime_type
, &charset
,
672 &had_charset
, &boundary
);
673 TrimString(boundary
, " \"", &boundary
);
675 // If there's no boundary, just handle the request normally. In the gecko
676 // code, nsMultiMixedConv::OnStartRequest throws an exception.
677 if (!boundary
.empty()) {
678 multipart_delegate_
.reset(
679 new MultipartResponseDelegate(client_
, loader_
, response
, boundary
));
681 } else if (info
.mime_type
== "text/vnd.chromium.ftp-dir" &&
683 ftp_listing_delegate_
.reset(
684 new FtpDirectoryListingResponseDelegate(client_
, loader_
, response
));
688 void WebURLLoaderImpl::Context::OnDownloadedData(int len
) {
690 client_
->didDownloadData(loader_
, len
);
693 void WebURLLoaderImpl::Context::OnReceivedData(const char* data
,
695 int encoded_data_length
) {
699 if (ftp_listing_delegate_
.get()) {
700 // The FTP listing delegate will make the appropriate calls to
701 // client_->didReceiveData and client_->didReceiveResponse.
702 ftp_listing_delegate_
->OnReceivedData(data
, data_length
);
703 } else if (multipart_delegate_
.get()) {
704 // The multipart delegate will make the appropriate calls to
705 // client_->didReceiveData and client_->didReceiveResponse.
706 multipart_delegate_
->OnReceivedData(data
, data_length
, encoded_data_length
);
708 client_
->didReceiveData(loader_
, data
, data_length
, encoded_data_length
);
712 void WebURLLoaderImpl::Context::OnReceivedCachedMetadata(
713 const char* data
, int len
) {
715 client_
->didReceiveCachedMetadata(loader_
, data
, len
);
718 void WebURLLoaderImpl::Context::OnCompletedRequest(
720 bool was_ignored_by_handler
,
721 const std::string
& security_info
,
722 const base::TimeTicks
& completion_time
) {
723 if (ftp_listing_delegate_
.get()) {
724 ftp_listing_delegate_
->OnCompletedRequest();
725 ftp_listing_delegate_
.reset(NULL
);
726 } else if (multipart_delegate_
.get()) {
727 multipart_delegate_
->OnCompletedRequest();
728 multipart_delegate_
.reset(NULL
);
731 // Prevent any further IPC to the browser now that we're complete, but
732 // don't delete it to keep any downloaded temp files alive.
733 DCHECK(!completed_bridge_
.get());
734 completed_bridge_
.swap(bridge_
);
737 if (error_code
!= net::OK
) {
739 if (error_code
== net::ERR_ABORTED
) {
740 error
.isCancellation
= true;
741 } else if (error_code
== net::ERR_TEMPORARILY_THROTTLED
) {
742 error
.localizedDescription
= WebString::fromUTF8(
743 kThrottledErrorDescription
);
745 error
.domain
= WebString::fromUTF8(net::kErrorDomain
);
746 error
.reason
= error_code
;
747 error
.unreachableURL
= request_
.url();
748 client_
->didFail(loader_
, error
);
750 client_
->didFinishLoading(
751 loader_
, (completion_time
- TimeTicks()).InSecondsF());
755 // We are done with the bridge now, and so we need to release the reference
756 // to ourselves that we took on behalf of the bridge. This may cause our
761 bool WebURLLoaderImpl::Context::CanHandleDataURL(const GURL
& url
) const {
762 DCHECK(url
.SchemeIs("data"));
764 // Optimize for the case where we can handle a data URL locally. We must
765 // skip this for data URLs targetted at frames since those could trigger a
768 // NOTE: We special case MIME types we can render both for performance
769 // reasons as well as to support unit tests, which do not have an underlying
770 // ResourceLoaderBridge implementation.
772 #if defined(OS_ANDROID)
773 // For compatibility reasons on Android we need to expose top-level data://
775 if (request_
.targetType() == WebURLRequest::TargetIsMainFrame
)
779 if (request_
.targetType() != WebURLRequest::TargetIsMainFrame
&&
780 request_
.targetType() != WebURLRequest::TargetIsSubframe
)
783 std::string mime_type
, unused_charset
;
784 if (net::DataURL::Parse(url
, &mime_type
, &unused_charset
, NULL
) &&
785 net::IsSupportedMimeType(mime_type
))
791 void WebURLLoaderImpl::Context::HandleDataURL() {
792 ResourceResponseInfo info
;
796 if (GetInfoFromDataURL(request_
.url(), &info
, &data
, &error_code
)) {
797 OnReceivedResponse(info
);
799 OnReceivedData(data
.data(), data
.size(), 0);
802 OnCompletedRequest(error_code
, false, info
.security_info
,
803 base::TimeTicks::Now());
806 // WebURLLoaderImpl -----------------------------------------------------------
808 WebURLLoaderImpl::WebURLLoaderImpl(WebKitPlatformSupportImpl
* platform
)
809 : ALLOW_THIS_IN_INITIALIZER_LIST(context_(new Context(this))),
810 platform_(platform
) {
813 WebURLLoaderImpl::~WebURLLoaderImpl() {
817 void WebURLLoaderImpl::loadSynchronously(const WebURLRequest
& request
,
818 WebURLResponse
& response
,
821 ResourceLoaderBridge::SyncLoadResponse sync_load_response
;
822 context_
->Start(request
, &sync_load_response
, platform_
);
824 const GURL
& final_url
= sync_load_response
.url
;
826 // TODO(tc): For file loads, we may want to include a more descriptive
827 // status code or status text.
828 int error_code
= sync_load_response
.error_code
;
829 if (error_code
!= net::OK
) {
830 response
.setURL(final_url
);
831 error
.domain
= WebString::fromUTF8(net::kErrorDomain
);
832 error
.reason
= error_code
;
833 error
.unreachableURL
= final_url
;
837 PopulateURLResponse(final_url
, sync_load_response
, &response
);
839 data
.assign(sync_load_response
.data
.data(),
840 sync_load_response
.data
.size());
843 void WebURLLoaderImpl::loadAsynchronously(const WebURLRequest
& request
,
844 WebURLLoaderClient
* client
) {
845 DCHECK(!context_
->client());
847 context_
->set_client(client
);
848 context_
->Start(request
, NULL
, platform_
);
851 void WebURLLoaderImpl::cancel() {
855 void WebURLLoaderImpl::setDefersLoading(bool value
) {
856 context_
->SetDefersLoading(value
);
859 void WebURLLoaderImpl::didChangePriority(WebURLRequest::Priority new_priority
) {
860 context_
->DidChangePriority(new_priority
);
863 } // namespace webkit_glue