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/command_line.h"
11 #include "base/files/file_path.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/strings/string_util.h"
15 #include "base/time/time.h"
16 #include "content/child/ftp_directory_listing_response_delegate.h"
17 #include "content/child/multipart_response_delegate.h"
18 #include "content/child/request_extra_data.h"
19 #include "content/child/request_info.h"
20 #include "content/child/resource_dispatcher.h"
21 #include "content/child/resource_loader_bridge.h"
22 #include "content/child/sync_load_response.h"
23 #include "content/child/web_url_request_util.h"
24 #include "content/child/weburlresponse_extradata_impl.h"
25 #include "content/common/resource_request_body.h"
26 #include "content/common/service_worker/service_worker_types.h"
27 #include "content/public/child/request_peer.h"
28 #include "content/public/common/content_switches.h"
29 #include "net/base/data_url.h"
30 #include "net/base/filename_util.h"
31 #include "net/base/mime_util.h"
32 #include "net/base/net_errors.h"
33 #include "net/http/http_response_headers.h"
34 #include "net/http/http_util.h"
35 #include "net/url_request/redirect_info.h"
36 #include "net/url_request/url_request_data_job.h"
37 #include "third_party/WebKit/public/platform/WebHTTPLoadInfo.h"
38 #include "third_party/WebKit/public/platform/WebURL.h"
39 #include "third_party/WebKit/public/platform/WebURLError.h"
40 #include "third_party/WebKit/public/platform/WebURLLoadTiming.h"
41 #include "third_party/WebKit/public/platform/WebURLLoaderClient.h"
42 #include "third_party/WebKit/public/platform/WebURLRequest.h"
43 #include "third_party/WebKit/public/platform/WebURLResponse.h"
44 #include "third_party/WebKit/public/web/WebSecurityPolicy.h"
47 using base::TimeTicks
;
49 using blink::WebHTTPBody
;
50 using blink::WebHTTPHeaderVisitor
;
51 using blink::WebHTTPLoadInfo
;
52 using blink::WebReferrerPolicy
;
53 using blink::WebSecurityPolicy
;
54 using blink::WebString
;
56 using blink::WebURLError
;
57 using blink::WebURLLoadTiming
;
58 using blink::WebURLLoader
;
59 using blink::WebURLLoaderClient
;
60 using blink::WebURLRequest
;
61 using blink::WebURLResponse
;
65 // Utilities ------------------------------------------------------------------
69 const char kThrottledErrorDescription
[] =
70 "Request throttled. Visit http://dev.chromium.org/throttling for more "
73 typedef ResourceDevToolsInfo::HeadersVector HeadersVector
;
75 // Converts timing data from |load_timing| to the format used by WebKit.
76 void PopulateURLLoadTiming(const net::LoadTimingInfo
& load_timing
,
77 WebURLLoadTiming
* url_timing
) {
78 DCHECK(!load_timing
.request_start
.is_null());
80 const TimeTicks kNullTicks
;
81 url_timing
->initialize();
82 url_timing
->setRequestTime(
83 (load_timing
.request_start
- kNullTicks
).InSecondsF());
84 url_timing
->setProxyStart(
85 (load_timing
.proxy_resolve_start
- kNullTicks
).InSecondsF());
86 url_timing
->setProxyEnd(
87 (load_timing
.proxy_resolve_end
- kNullTicks
).InSecondsF());
88 url_timing
->setDNSStart(
89 (load_timing
.connect_timing
.dns_start
- kNullTicks
).InSecondsF());
90 url_timing
->setDNSEnd(
91 (load_timing
.connect_timing
.dns_end
- kNullTicks
).InSecondsF());
92 url_timing
->setConnectStart(
93 (load_timing
.connect_timing
.connect_start
- kNullTicks
).InSecondsF());
94 url_timing
->setConnectEnd(
95 (load_timing
.connect_timing
.connect_end
- kNullTicks
).InSecondsF());
96 url_timing
->setSSLStart(
97 (load_timing
.connect_timing
.ssl_start
- kNullTicks
).InSecondsF());
98 url_timing
->setSSLEnd(
99 (load_timing
.connect_timing
.ssl_end
- kNullTicks
).InSecondsF());
100 url_timing
->setSendStart(
101 (load_timing
.send_start
- kNullTicks
).InSecondsF());
102 url_timing
->setSendEnd(
103 (load_timing
.send_end
- kNullTicks
).InSecondsF());
104 url_timing
->setReceiveHeadersEnd(
105 (load_timing
.receive_headers_end
- kNullTicks
).InSecondsF());
108 net::RequestPriority
ConvertWebKitPriorityToNetPriority(
109 const WebURLRequest::Priority
& priority
) {
111 case WebURLRequest::PriorityVeryHigh
:
114 case WebURLRequest::PriorityHigh
:
117 case WebURLRequest::PriorityMedium
:
120 case WebURLRequest::PriorityLow
:
123 case WebURLRequest::PriorityVeryLow
:
126 case WebURLRequest::PriorityUnresolved
:
133 // Extracts info from a data scheme URL into |info| and |data|. Returns net::OK
134 // if successful. Returns a net error code otherwise. Exported only for testing.
135 int GetInfoFromDataURL(const GURL
& url
,
136 ResourceResponseInfo
* info
,
138 // Assure same time for all time fields of data: URLs.
139 Time now
= Time::Now();
140 info
->load_timing
.request_start
= TimeTicks::Now();
141 info
->load_timing
.request_start_time
= now
;
142 info
->request_time
= now
;
143 info
->response_time
= now
;
145 std::string mime_type
;
147 scoped_refptr
<net::HttpResponseHeaders
> headers(
148 new net::HttpResponseHeaders(std::string()));
149 int result
= net::URLRequestDataJob::BuildResponse(
150 url
, &mime_type
, &charset
, data
, headers
.get());
151 if (result
!= net::OK
)
154 info
->headers
= headers
;
155 info
->mime_type
.swap(mime_type
);
156 info
->charset
.swap(charset
);
157 info
->security_info
.clear();
158 info
->content_length
= data
->length();
159 info
->encoded_data_length
= 0;
164 #define COMPILE_ASSERT_MATCHING_ENUMS(content_name, blink_name) \
166 static_cast<int>(content_name) == static_cast<int>(blink_name), \
169 COMPILE_ASSERT_MATCHING_ENUMS(FETCH_REQUEST_MODE_SAME_ORIGIN
,
170 WebURLRequest::FetchRequestModeSameOrigin
);
171 COMPILE_ASSERT_MATCHING_ENUMS(FETCH_REQUEST_MODE_NO_CORS
,
172 WebURLRequest::FetchRequestModeNoCORS
);
173 COMPILE_ASSERT_MATCHING_ENUMS(FETCH_REQUEST_MODE_CORS
,
174 WebURLRequest::FetchRequestModeCORS
);
175 COMPILE_ASSERT_MATCHING_ENUMS(
176 FETCH_REQUEST_MODE_CORS_WITH_FORCED_PREFLIGHT
,
177 WebURLRequest::FetchRequestModeCORSWithForcedPreflight
);
179 FetchRequestMode
GetFetchRequestMode(const WebURLRequest
& request
) {
180 return static_cast<FetchRequestMode
>(request
.fetchRequestMode());
183 COMPILE_ASSERT_MATCHING_ENUMS(FETCH_CREDENTIALS_MODE_OMIT
,
184 WebURLRequest::FetchCredentialsModeOmit
);
185 COMPILE_ASSERT_MATCHING_ENUMS(FETCH_CREDENTIALS_MODE_SAME_ORIGIN
,
186 WebURLRequest::FetchCredentialsModeSameOrigin
);
187 COMPILE_ASSERT_MATCHING_ENUMS(FETCH_CREDENTIALS_MODE_INCLUDE
,
188 WebURLRequest::FetchCredentialsModeInclude
);
190 FetchCredentialsMode
GetFetchCredentialsMode(const WebURLRequest
& request
) {
191 return static_cast<FetchCredentialsMode
>(request
.fetchCredentialsMode());
194 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_FRAME_TYPE_AUXILIARY
,
195 WebURLRequest::FrameTypeAuxiliary
);
196 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_FRAME_TYPE_NESTED
,
197 WebURLRequest::FrameTypeNested
);
198 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_FRAME_TYPE_NONE
,
199 WebURLRequest::FrameTypeNone
);
200 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_FRAME_TYPE_TOP_LEVEL
,
201 WebURLRequest::FrameTypeTopLevel
);
203 RequestContextFrameType
GetRequestContextFrameType(
204 const WebURLRequest
& request
) {
205 return static_cast<RequestContextFrameType
>(request
.frameType());
208 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_TYPE_UNSPECIFIED
,
209 WebURLRequest::RequestContextUnspecified
);
210 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_TYPE_AUDIO
,
211 WebURLRequest::RequestContextAudio
);
212 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_TYPE_BEACON
,
213 WebURLRequest::RequestContextBeacon
);
214 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_TYPE_CSP_REPORT
,
215 WebURLRequest::RequestContextCSPReport
);
216 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_TYPE_DOWNLOAD
,
217 WebURLRequest::RequestContextDownload
);
218 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_TYPE_EMBED
,
219 WebURLRequest::RequestContextEmbed
);
220 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_TYPE_EVENT_SOURCE
,
221 WebURLRequest::RequestContextEventSource
);
222 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_TYPE_FAVICON
,
223 WebURLRequest::RequestContextFavicon
);
224 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_TYPE_FETCH
,
225 WebURLRequest::RequestContextFetch
);
226 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_TYPE_FONT
,
227 WebURLRequest::RequestContextFont
);
228 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_TYPE_FORM
,
229 WebURLRequest::RequestContextForm
);
230 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_TYPE_FRAME
,
231 WebURLRequest::RequestContextFrame
);
232 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_TYPE_HYPERLINK
,
233 WebURLRequest::RequestContextHyperlink
);
234 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_TYPE_IFRAME
,
235 WebURLRequest::RequestContextIframe
);
236 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_TYPE_IMAGE
,
237 WebURLRequest::RequestContextImage
);
238 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_TYPE_IMAGE_SET
,
239 WebURLRequest::RequestContextImageSet
);
240 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_TYPE_IMPORT
,
241 WebURLRequest::RequestContextImport
);
242 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_TYPE_INTERNAL
,
243 WebURLRequest::RequestContextInternal
);
244 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_TYPE_LOCATION
,
245 WebURLRequest::RequestContextLocation
);
246 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_TYPE_MANIFEST
,
247 WebURLRequest::RequestContextManifest
);
248 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_TYPE_OBJECT
,
249 WebURLRequest::RequestContextObject
);
250 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_TYPE_PING
,
251 WebURLRequest::RequestContextPing
);
252 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_TYPE_PLUGIN
,
253 WebURLRequest::RequestContextPlugin
);
254 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_TYPE_PREFETCH
,
255 WebURLRequest::RequestContextPrefetch
);
256 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_TYPE_SCRIPT
,
257 WebURLRequest::RequestContextScript
);
258 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_TYPE_SERVICE_WORKER
,
259 WebURLRequest::RequestContextServiceWorker
);
260 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_TYPE_SHARED_WORKER
,
261 WebURLRequest::RequestContextSharedWorker
);
262 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_TYPE_SUBRESOURCE
,
263 WebURLRequest::RequestContextSubresource
);
264 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_TYPE_STYLE
,
265 WebURLRequest::RequestContextStyle
);
266 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_TYPE_TRACK
,
267 WebURLRequest::RequestContextTrack
);
268 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_TYPE_VIDEO
,
269 WebURLRequest::RequestContextVideo
);
270 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_TYPE_WORKER
,
271 WebURLRequest::RequestContextWorker
);
272 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_TYPE_XML_HTTP_REQUEST
,
273 WebURLRequest::RequestContextXMLHttpRequest
);
274 COMPILE_ASSERT_MATCHING_ENUMS(REQUEST_CONTEXT_TYPE_XSLT
,
275 WebURLRequest::RequestContextXSLT
);
277 RequestContextType
GetRequestContextType(const WebURLRequest
& request
) {
278 return static_cast<RequestContextType
>(request
.requestContext());
283 // WebURLLoaderImpl::Context --------------------------------------------------
285 // This inner class exists since the WebURLLoader may be deleted while inside a
286 // call to WebURLLoaderClient. Refcounting is to keep the context from being
287 // deleted if it may have work to do after calling into the client.
288 class WebURLLoaderImpl::Context
: public base::RefCounted
<Context
>,
291 Context(WebURLLoaderImpl
* loader
, ResourceDispatcher
* resource_dispatcher
);
293 WebURLLoaderClient
* client() const { return client_
; }
294 void set_client(WebURLLoaderClient
* client
) { client_
= client
; }
297 void SetDefersLoading(bool value
);
298 void DidChangePriority(WebURLRequest::Priority new_priority
,
299 int intra_priority_value
);
300 bool AttachThreadedDataReceiver(
301 blink::WebThreadedDataReceiver
* threaded_data_receiver
);
302 void Start(const WebURLRequest
& request
,
303 SyncLoadResponse
* sync_load_response
);
305 // RequestPeer methods:
306 void OnUploadProgress(uint64 position
, uint64 size
) override
;
307 bool OnReceivedRedirect(const net::RedirectInfo
& redirect_info
,
308 const ResourceResponseInfo
& info
) override
;
309 void OnReceivedResponse(const ResourceResponseInfo
& info
) override
;
310 void OnDownloadedData(int len
, int encoded_data_length
) override
;
311 void OnReceivedData(const char* data
,
313 int encoded_data_length
) override
;
314 void OnReceivedCachedMetadata(const char* data
, int len
) override
;
315 void OnCompletedRequest(int error_code
,
316 bool was_ignored_by_handler
,
317 bool stale_copy_in_cache
,
318 const std::string
& security_info
,
319 const base::TimeTicks
& completion_time
,
320 int64 total_transfer_size
) override
;
323 friend class base::RefCounted
<Context
>;
324 ~Context() override
{}
326 // We can optimize the handling of data URLs in most cases.
327 bool CanHandleDataURLRequestLocally() const;
328 void HandleDataURL();
330 WebURLLoaderImpl
* loader_
;
331 WebURLRequest request_
;
332 WebURLLoaderClient
* client_
;
333 ResourceDispatcher
* resource_dispatcher_
;
334 WebReferrerPolicy referrer_policy_
;
335 scoped_ptr
<ResourceLoaderBridge
> bridge_
;
336 scoped_ptr
<FtpDirectoryListingResponseDelegate
> ftp_listing_delegate_
;
337 scoped_ptr
<MultipartResponseDelegate
> multipart_delegate_
;
338 scoped_ptr
<ResourceLoaderBridge
> completed_bridge_
;
339 scoped_ptr
<StreamOverrideParameters
> stream_override_
;
342 WebURLLoaderImpl::Context::Context(WebURLLoaderImpl
* loader
,
343 ResourceDispatcher
* resource_dispatcher
)
346 resource_dispatcher_(resource_dispatcher
),
347 referrer_policy_(blink::WebReferrerPolicyDefault
) {
350 void WebURLLoaderImpl::Context::Cancel() {
356 // Ensure that we do not notify the multipart delegate anymore as it has
357 // its own pointer to the client.
358 if (multipart_delegate_
)
359 multipart_delegate_
->Cancel();
360 // Ditto for the ftp delegate.
361 if (ftp_listing_delegate_
)
362 ftp_listing_delegate_
->Cancel();
364 // Do not make any further calls to the client.
369 void WebURLLoaderImpl::Context::SetDefersLoading(bool value
) {
371 bridge_
->SetDefersLoading(value
);
374 void WebURLLoaderImpl::Context::DidChangePriority(
375 WebURLRequest::Priority new_priority
, int intra_priority_value
) {
377 bridge_
->DidChangePriority(
378 ConvertWebKitPriorityToNetPriority(new_priority
), intra_priority_value
);
381 bool WebURLLoaderImpl::Context::AttachThreadedDataReceiver(
382 blink::WebThreadedDataReceiver
* threaded_data_receiver
) {
384 return bridge_
->AttachThreadedDataReceiver(threaded_data_receiver
);
389 void WebURLLoaderImpl::Context::Start(const WebURLRequest
& request
,
390 SyncLoadResponse
* sync_load_response
) {
391 DCHECK(!bridge_
.get());
393 request_
= request
; // Save the request.
394 if (request
.extraData()) {
395 RequestExtraData
* extra_data
=
396 static_cast<RequestExtraData
*>(request
.extraData());
397 stream_override_
= extra_data
->TakeStreamOverrideOwnership();
400 GURL url
= request
.url();
402 // PlzNavigate: during navigation, the renderer should request a stream which
403 // contains the body of the response. The request has already been made by the
405 if (stream_override_
.get()) {
406 CHECK(CommandLine::ForCurrentProcess()->HasSwitch(
407 switches::kEnableBrowserSideNavigation
));
408 DCHECK(!sync_load_response
);
409 DCHECK_NE(WebURLRequest::FrameTypeNone
, request
.frameType());
410 DCHECK_EQ("GET", request
.httpMethod().latin1());
411 url
= stream_override_
->stream_url
;
414 if (CanHandleDataURLRequestLocally()) {
415 if (sync_load_response
) {
416 // This is a sync load. Do the work now.
417 sync_load_response
->url
= url
;
418 sync_load_response
->error_code
=
419 GetInfoFromDataURL(sync_load_response
->url
, sync_load_response
,
420 &sync_load_response
->data
);
422 base::MessageLoop::current()->PostTask(
423 FROM_HERE
, base::Bind(&Context::HandleDataURL
, this));
429 request
.httpHeaderField(WebString::fromUTF8("Referer")).latin1());
430 const std::string
& method
= request
.httpMethod().latin1();
432 // TODO(brettw) this should take parameter encoding into account when
433 // creating the GURLs.
435 // TODO(horo): Check credentials flag is unset when credentials mode is omit.
436 // Check credentials flag is set when credentials mode is include.
438 RequestInfo request_info
;
439 request_info
.method
= method
;
440 request_info
.url
= url
;
441 request_info
.first_party_for_cookies
= request
.firstPartyForCookies();
442 request_info
.referrer
= referrer_url
;
443 request_info
.headers
= GetWebURLRequestHeaders(request
);
445 request_info
.load_flags
= GetLoadFlagsForWebURLRequest(request
);
446 request_info
.enable_load_timing
= true;
447 // requestor_pid only needs to be non-zero if the request originates outside
448 // the render process, so we can use requestorProcessID even for requests
449 // from in-process plugins.
450 request_info
.requestor_pid
= request
.requestorProcessID();
451 request_info
.request_type
= WebURLRequestToResourceType(request
);
452 request_info
.priority
=
453 ConvertWebKitPriorityToNetPriority(request
.priority());
454 request_info
.appcache_host_id
= request
.appCacheHostID();
455 request_info
.routing_id
= request
.requestorID();
456 request_info
.download_to_file
= request
.downloadToFile();
457 request_info
.has_user_gesture
= request
.hasUserGesture();
458 request_info
.skip_service_worker
= request
.skipServiceWorker();
459 request_info
.fetch_request_mode
= GetFetchRequestMode(request
);
460 request_info
.fetch_credentials_mode
= GetFetchCredentialsMode(request
);
461 request_info
.fetch_request_context_type
= GetRequestContextType(request
);
462 request_info
.fetch_frame_type
= GetRequestContextFrameType(request
);
463 request_info
.extra_data
= request
.extraData();
464 referrer_policy_
= request
.referrerPolicy();
465 request_info
.referrer_policy
= request
.referrerPolicy();
466 bridge_
.reset(resource_dispatcher_
->CreateBridge(request_info
));
468 if (!request
.httpBody().isNull()) {
469 // GET and HEAD requests shouldn't have http bodies.
470 DCHECK(method
!= "GET" && method
!= "HEAD");
471 const WebHTTPBody
& httpBody
= request
.httpBody();
473 WebHTTPBody::Element element
;
474 scoped_refptr
<ResourceRequestBody
> request_body
= new ResourceRequestBody
;
475 while (httpBody
.elementAt(i
++, element
)) {
476 switch (element
.type
) {
477 case WebHTTPBody::Element::TypeData
:
478 if (!element
.data
.isEmpty()) {
479 // WebKit sometimes gives up empty data to append. These aren't
480 // necessary so we just optimize those out here.
481 request_body
->AppendBytes(
482 element
.data
.data(), static_cast<int>(element
.data
.size()));
485 case WebHTTPBody::Element::TypeFile
:
486 if (element
.fileLength
== -1) {
487 request_body
->AppendFileRange(
488 base::FilePath::FromUTF16Unsafe(element
.filePath
),
489 0, kuint64max
, base::Time());
491 request_body
->AppendFileRange(
492 base::FilePath::FromUTF16Unsafe(element
.filePath
),
493 static_cast<uint64
>(element
.fileStart
),
494 static_cast<uint64
>(element
.fileLength
),
495 base::Time::FromDoubleT(element
.modificationTime
));
498 case WebHTTPBody::Element::TypeFileSystemURL
: {
499 GURL file_system_url
= element
.fileSystemURL
;
500 DCHECK(file_system_url
.SchemeIsFileSystem());
501 request_body
->AppendFileSystemFileRange(
503 static_cast<uint64
>(element
.fileStart
),
504 static_cast<uint64
>(element
.fileLength
),
505 base::Time::FromDoubleT(element
.modificationTime
));
508 case WebHTTPBody::Element::TypeBlob
:
509 request_body
->AppendBlob(element
.blobUUID
.utf8());
515 request_body
->set_identifier(request
.httpBody().identifier());
516 bridge_
->SetRequestBody(request_body
.get());
519 if (sync_load_response
) {
520 bridge_
->SyncLoad(sync_load_response
);
524 // TODO(mmenke): This case probably never happens, anyways. Probably should
525 // not handle this case at all. If it's worth handling, this code currently
526 // results in the request just hanging, which should be fixed.
527 if (!bridge_
->Start(this))
531 void WebURLLoaderImpl::Context::OnUploadProgress(uint64 position
, uint64 size
) {
533 client_
->didSendData(loader_
, position
, size
);
536 bool WebURLLoaderImpl::Context::OnReceivedRedirect(
537 const net::RedirectInfo
& redirect_info
,
538 const ResourceResponseInfo
& info
) {
542 WebURLResponse response
;
543 response
.initialize();
544 PopulateURLResponse(request_
.url(), info
, &response
);
546 // TODO(darin): We lack sufficient information to construct the actual
547 // request that resulted from the redirect.
548 WebURLRequest
new_request(redirect_info
.new_url
);
549 new_request
.setFirstPartyForCookies(
550 redirect_info
.new_first_party_for_cookies
);
551 new_request
.setDownloadToFile(request_
.downloadToFile());
552 new_request
.setRequestContext(request_
.requestContext());
553 new_request
.setFrameType(request_
.frameType());
555 new_request
.setHTTPReferrer(WebString::fromUTF8(redirect_info
.new_referrer
),
558 std::string old_method
= request_
.httpMethod().utf8();
559 new_request
.setHTTPMethod(WebString::fromUTF8(redirect_info
.new_method
));
560 if (redirect_info
.new_method
== old_method
)
561 new_request
.setHTTPBody(request_
.httpBody());
563 // Protect from deletion during call to willSendRequest.
564 scoped_refptr
<Context
> protect(this);
566 client_
->willSendRequest(loader_
, new_request
, response
);
567 request_
= new_request
;
569 // Only follow the redirect if WebKit left the URL unmodified.
570 if (redirect_info
.new_url
== GURL(new_request
.url())) {
571 // First-party cookie logic moved from DocumentLoader in Blink to
572 // net::URLRequest in the browser. Assert that Blink didn't try to change it
573 // to something else.
574 DCHECK_EQ(redirect_info
.new_first_party_for_cookies
.spec(),
575 request_
.firstPartyForCookies().string().utf8());
579 // We assume that WebKit only changes the URL to suppress a redirect, and we
580 // assume that it does so by setting it to be invalid.
581 DCHECK(!new_request
.url().isValid());
585 void WebURLLoaderImpl::Context::OnReceivedResponse(
586 const ResourceResponseInfo
& initial_info
) {
590 ResourceResponseInfo info
= initial_info
;
592 // PlzNavigate: during navigations, the ResourceResponse has already been
593 // received on the browser side, and has been passed down to the renderer.
594 if (stream_override_
.get()) {
595 CHECK(CommandLine::ForCurrentProcess()->HasSwitch(
596 switches::kEnableBrowserSideNavigation
));
597 info
= stream_override_
->response
;
600 WebURLResponse response
;
601 response
.initialize();
602 // Updates the request url if the response was fetched by a ServiceWorker,
603 // and it was not generated inside the ServiceWorker.
604 if (info
.was_fetched_via_service_worker
&&
605 !info
.original_url_via_service_worker
.is_empty()) {
606 request_
.setURL(info
.original_url_via_service_worker
);
608 PopulateURLResponse(request_
.url(), info
, &response
);
610 bool show_raw_listing
= (GURL(request_
.url()).query() == "raw");
612 if (info
.mime_type
== "text/vnd.chromium.ftp-dir") {
613 if (show_raw_listing
) {
614 // Set the MIME type to plain text to prevent any active content.
615 response
.setMIMEType("text/plain");
617 // We're going to produce a parsed listing in HTML.
618 response
.setMIMEType("text/html");
622 // Prevent |this| from being destroyed if the client destroys the loader,
623 // ether in didReceiveResponse, or when the multipart/ftp delegate calls into
625 scoped_refptr
<Context
> protect(this);
626 client_
->didReceiveResponse(loader_
, response
);
628 // We may have been cancelled after didReceiveResponse, which would leave us
629 // without a client and therefore without much need to do further handling.
633 DCHECK(!ftp_listing_delegate_
.get());
634 DCHECK(!multipart_delegate_
.get());
635 if (info
.headers
.get() && info
.mime_type
== "multipart/x-mixed-replace") {
636 std::string content_type
;
637 info
.headers
->EnumerateHeader(NULL
, "content-type", &content_type
);
639 std::string mime_type
;
641 bool had_charset
= false;
642 std::string boundary
;
643 net::HttpUtil::ParseContentType(content_type
, &mime_type
, &charset
,
644 &had_charset
, &boundary
);
645 base::TrimString(boundary
, " \"", &boundary
);
647 // If there's no boundary, just handle the request normally. In the gecko
648 // code, nsMultiMixedConv::OnStartRequest throws an exception.
649 if (!boundary
.empty()) {
650 multipart_delegate_
.reset(
651 new MultipartResponseDelegate(client_
, loader_
, response
, boundary
));
653 } else if (info
.mime_type
== "text/vnd.chromium.ftp-dir" &&
655 ftp_listing_delegate_
.reset(
656 new FtpDirectoryListingResponseDelegate(client_
, loader_
, response
));
660 void WebURLLoaderImpl::Context::OnDownloadedData(int len
,
661 int encoded_data_length
) {
663 client_
->didDownloadData(loader_
, len
, encoded_data_length
);
666 void WebURLLoaderImpl::Context::OnReceivedData(const char* data
,
668 int encoded_data_length
) {
672 if (ftp_listing_delegate_
) {
673 // The FTP listing delegate will make the appropriate calls to
674 // client_->didReceiveData and client_->didReceiveResponse. Since the
675 // delegate may want to do work after sending data to the delegate, keep
676 // |this| and the delegate alive until it's finished handling the data.
677 scoped_refptr
<Context
> protect(this);
678 ftp_listing_delegate_
->OnReceivedData(data
, data_length
);
679 } else if (multipart_delegate_
) {
680 // The multipart delegate will make the appropriate calls to
681 // client_->didReceiveData and client_->didReceiveResponse. Since the
682 // delegate may want to do work after sending data to the delegate, keep
683 // |this| and the delegate alive until it's finished handling the data.
684 scoped_refptr
<Context
> protect(this);
685 multipart_delegate_
->OnReceivedData(data
, data_length
, encoded_data_length
);
687 client_
->didReceiveData(loader_
, data
, data_length
, encoded_data_length
);
691 void WebURLLoaderImpl::Context::OnReceivedCachedMetadata(
692 const char* data
, int len
) {
694 client_
->didReceiveCachedMetadata(loader_
, data
, len
);
697 void WebURLLoaderImpl::Context::OnCompletedRequest(
699 bool was_ignored_by_handler
,
700 bool stale_copy_in_cache
,
701 const std::string
& security_info
,
702 const base::TimeTicks
& completion_time
,
703 int64 total_transfer_size
) {
704 // The WebURLLoaderImpl may be deleted in any of the calls to the client or
705 // the delegates below (As they also may call in to the client). Keep |this|
706 // alive in that case, to avoid a crash. If that happens, the request will be
707 // cancelled and |client_| will be set to NULL.
708 scoped_refptr
<Context
> protect(this);
710 if (ftp_listing_delegate_
) {
711 ftp_listing_delegate_
->OnCompletedRequest();
712 ftp_listing_delegate_
.reset(NULL
);
713 } else if (multipart_delegate_
) {
714 multipart_delegate_
->OnCompletedRequest();
715 multipart_delegate_
.reset(NULL
);
718 // Prevent any further IPC to the browser now that we're complete, but
719 // don't delete it to keep any downloaded temp files alive.
720 DCHECK(!completed_bridge_
.get());
721 completed_bridge_
.swap(bridge_
);
724 if (error_code
!= net::OK
) {
725 client_
->didFail(loader_
, CreateError(request_
.url(),
729 client_
->didFinishLoading(
730 loader_
, (completion_time
- TimeTicks()).InSecondsF(),
731 total_transfer_size
);
736 bool WebURLLoaderImpl::Context::CanHandleDataURLRequestLocally() const {
737 GURL url
= request_
.url();
738 if (!url
.SchemeIs(url::kDataScheme
))
741 // The fast paths for data URL, Start() and HandleDataURL(), don't support
742 // the downloadToFile option.
743 if (request_
.downloadToFile())
746 // Optimize for the case where we can handle a data URL locally. We must
747 // skip this for data URLs targetted at frames since those could trigger a
750 // NOTE: We special case MIME types we can render both for performance
751 // reasons as well as to support unit tests, which do not have an underlying
752 // ResourceLoaderBridge implementation.
754 #if defined(OS_ANDROID)
755 // For compatibility reasons on Android we need to expose top-level data://
757 if (request_
.frameType() == WebURLRequest::FrameTypeTopLevel
)
761 if (request_
.frameType() != WebURLRequest::FrameTypeTopLevel
&&
762 request_
.frameType() != WebURLRequest::FrameTypeNested
)
765 std::string mime_type
, unused_charset
;
766 if (net::DataURL::Parse(request_
.url(), &mime_type
, &unused_charset
, NULL
) &&
767 net::IsSupportedMimeType(mime_type
))
773 void WebURLLoaderImpl::Context::HandleDataURL() {
774 ResourceResponseInfo info
;
777 int error_code
= GetInfoFromDataURL(request_
.url(), &info
, &data
);
779 if (error_code
== net::OK
) {
780 OnReceivedResponse(info
);
782 OnReceivedData(data
.data(), data
.size(), 0);
785 OnCompletedRequest(error_code
, false, false, info
.security_info
,
786 base::TimeTicks::Now(), 0);
789 // WebURLLoaderImpl -----------------------------------------------------------
791 WebURLLoaderImpl::WebURLLoaderImpl(ResourceDispatcher
* resource_dispatcher
)
792 : context_(new Context(this, resource_dispatcher
)) {
795 WebURLLoaderImpl::~WebURLLoaderImpl() {
799 WebURLError
WebURLLoaderImpl::CreateError(const WebURL
& unreachable_url
,
800 bool stale_copy_in_cache
,
803 error
.domain
= WebString::fromUTF8(net::kErrorDomain
);
804 error
.reason
= reason
;
805 error
.unreachableURL
= unreachable_url
;
806 error
.staleCopyInCache
= stale_copy_in_cache
;
807 if (reason
== net::ERR_ABORTED
) {
808 error
.isCancellation
= true;
809 } else if (reason
== net::ERR_TEMPORARILY_THROTTLED
) {
810 error
.localizedDescription
= WebString::fromUTF8(
811 kThrottledErrorDescription
);
813 error
.localizedDescription
= WebString::fromUTF8(
814 net::ErrorToString(reason
));
819 void WebURLLoaderImpl::PopulateURLResponse(const GURL
& url
,
820 const ResourceResponseInfo
& info
,
821 WebURLResponse
* response
) {
822 response
->setURL(url
);
823 response
->setResponseTime(info
.response_time
.ToDoubleT());
824 response
->setMIMEType(WebString::fromUTF8(info
.mime_type
));
825 response
->setTextEncodingName(WebString::fromUTF8(info
.charset
));
826 response
->setExpectedContentLength(info
.content_length
);
827 response
->setSecurityInfo(info
.security_info
);
828 response
->setAppCacheID(info
.appcache_id
);
829 response
->setAppCacheManifestURL(info
.appcache_manifest_url
);
830 response
->setWasCached(!info
.load_timing
.request_start_time
.is_null() &&
831 info
.response_time
< info
.load_timing
.request_start_time
);
832 response
->setRemoteIPAddress(
833 WebString::fromUTF8(info
.socket_address
.host()));
834 response
->setRemotePort(info
.socket_address
.port());
835 response
->setConnectionID(info
.load_timing
.socket_log_id
);
836 response
->setConnectionReused(info
.load_timing
.socket_reused
);
837 response
->setDownloadFilePath(info
.download_file_path
.AsUTF16Unsafe());
838 response
->setWasFetchedViaServiceWorker(info
.was_fetched_via_service_worker
);
839 response
->setWasFallbackRequiredByServiceWorker(
840 info
.was_fallback_required_by_service_worker
);
841 response
->setServiceWorkerResponseType(info
.response_type_via_service_worker
);
842 WebURLResponseExtraDataImpl
* extra_data
=
843 new WebURLResponseExtraDataImpl(info
.npn_negotiated_protocol
);
844 response
->setExtraData(extra_data
);
845 extra_data
->set_was_fetched_via_spdy(info
.was_fetched_via_spdy
);
846 extra_data
->set_was_npn_negotiated(info
.was_npn_negotiated
);
847 extra_data
->set_was_alternate_protocol_available(
848 info
.was_alternate_protocol_available
);
849 extra_data
->set_connection_info(info
.connection_info
);
850 extra_data
->set_was_fetched_via_proxy(info
.was_fetched_via_proxy
);
852 // If there's no received headers end time, don't set load timing. This is
853 // the case for non-HTTP requests, requests that don't go over the wire, and
854 // certain error cases.
855 if (!info
.load_timing
.receive_headers_end
.is_null()) {
856 WebURLLoadTiming timing
;
857 PopulateURLLoadTiming(info
.load_timing
, &timing
);
858 const TimeTicks kNullTicks
;
859 timing
.setServiceWorkerFetchStart(
860 (info
.service_worker_fetch_start
- kNullTicks
).InSecondsF());
861 timing
.setServiceWorkerFetchReady(
862 (info
.service_worker_fetch_ready
- kNullTicks
).InSecondsF());
863 timing
.setServiceWorkerFetchEnd(
864 (info
.service_worker_fetch_end
- kNullTicks
).InSecondsF());
865 response
->setLoadTiming(timing
);
868 if (info
.devtools_info
.get()) {
869 WebHTTPLoadInfo load_info
;
871 load_info
.setHTTPStatusCode(info
.devtools_info
->http_status_code
);
872 load_info
.setHTTPStatusText(WebString::fromLatin1(
873 info
.devtools_info
->http_status_text
));
874 load_info
.setEncodedDataLength(info
.encoded_data_length
);
876 load_info
.setRequestHeadersText(WebString::fromLatin1(
877 info
.devtools_info
->request_headers_text
));
878 load_info
.setResponseHeadersText(WebString::fromLatin1(
879 info
.devtools_info
->response_headers_text
));
880 const HeadersVector
& request_headers
= info
.devtools_info
->request_headers
;
881 for (HeadersVector::const_iterator it
= request_headers
.begin();
882 it
!= request_headers
.end(); ++it
) {
883 load_info
.addRequestHeader(WebString::fromLatin1(it
->first
),
884 WebString::fromLatin1(it
->second
));
886 const HeadersVector
& response_headers
=
887 info
.devtools_info
->response_headers
;
888 for (HeadersVector::const_iterator it
= response_headers
.begin();
889 it
!= response_headers
.end(); ++it
) {
890 load_info
.addResponseHeader(WebString::fromLatin1(it
->first
),
891 WebString::fromLatin1(it
->second
));
893 response
->setHTTPLoadInfo(load_info
);
896 const net::HttpResponseHeaders
* headers
= info
.headers
.get();
900 WebURLResponse::HTTPVersion version
= WebURLResponse::Unknown
;
901 if (headers
->GetHttpVersion() == net::HttpVersion(0, 9))
902 version
= WebURLResponse::HTTP_0_9
;
903 else if (headers
->GetHttpVersion() == net::HttpVersion(1, 0))
904 version
= WebURLResponse::HTTP_1_0
;
905 else if (headers
->GetHttpVersion() == net::HttpVersion(1, 1))
906 version
= WebURLResponse::HTTP_1_1
;
907 response
->setHTTPVersion(version
);
908 response
->setHTTPStatusCode(headers
->response_code());
909 response
->setHTTPStatusText(WebString::fromLatin1(headers
->GetStatusText()));
911 // TODO(darin): We should leverage HttpResponseHeaders for this, and this
912 // should be using the same code as ResourceDispatcherHost.
913 // TODO(jungshik): Figure out the actual value of the referrer charset and
914 // pass it to GetSuggestedFilename.
916 headers
->EnumerateHeader(NULL
, "content-disposition", &value
);
917 response
->setSuggestedFileName(
918 net::GetSuggestedFilename(url
,
920 std::string(), // referrer_charset
921 std::string(), // suggested_name
922 std::string(), // mime_type
923 std::string())); // default_name
926 if (headers
->GetLastModifiedValue(&time_val
))
927 response
->setLastModifiedDate(time_val
.ToDoubleT());
929 // Build up the header map.
932 while (headers
->EnumerateHeaderLines(&iter
, &name
, &value
)) {
933 response
->addHTTPHeaderField(WebString::fromLatin1(name
),
934 WebString::fromLatin1(value
));
938 void WebURLLoaderImpl::loadSynchronously(const WebURLRequest
& request
,
939 WebURLResponse
& response
,
942 SyncLoadResponse sync_load_response
;
943 context_
->Start(request
, &sync_load_response
);
945 const GURL
& final_url
= sync_load_response
.url
;
947 // TODO(tc): For file loads, we may want to include a more descriptive
948 // status code or status text.
949 int error_code
= sync_load_response
.error_code
;
950 if (error_code
!= net::OK
) {
951 response
.setURL(final_url
);
952 error
.domain
= WebString::fromUTF8(net::kErrorDomain
);
953 error
.reason
= error_code
;
954 error
.unreachableURL
= final_url
;
958 PopulateURLResponse(final_url
, sync_load_response
, &response
);
960 data
.assign(sync_load_response
.data
.data(),
961 sync_load_response
.data
.size());
964 void WebURLLoaderImpl::loadAsynchronously(const WebURLRequest
& request
,
965 WebURLLoaderClient
* client
) {
966 DCHECK(!context_
->client());
968 context_
->set_client(client
);
969 context_
->Start(request
, NULL
);
972 void WebURLLoaderImpl::cancel() {
976 void WebURLLoaderImpl::setDefersLoading(bool value
) {
977 context_
->SetDefersLoading(value
);
980 void WebURLLoaderImpl::didChangePriority(WebURLRequest::Priority new_priority
,
981 int intra_priority_value
) {
982 context_
->DidChangePriority(new_priority
, intra_priority_value
);
985 bool WebURLLoaderImpl::attachThreadedDataReceiver(
986 blink::WebThreadedDataReceiver
* threaded_data_receiver
) {
987 return context_
->AttachThreadedDataReceiver(threaded_data_receiver
);
990 } // namespace content