OLD | NEW |
| (Empty) |
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. | |
4 | |
5 // An implementation of WebURLLoader in terms of ResourceLoaderBridge. | |
6 | |
7 #include "content/child/web_url_loader_impl.h" | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/files/file_path.h" | |
11 #include "base/memory/scoped_ptr.h" | |
12 #include "base/message_loop/message_loop.h" | |
13 #include "base/strings/string_util.h" | |
14 #include "base/time/time.h" | |
15 #include "content/child/blink_platform_impl.h" | |
16 #include "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 "net/url_request/url_request.h" | |
24 #include "third_party/WebKit/public/platform/WebHTTPHeaderVisitor.h" | |
25 #include "third_party/WebKit/public/platform/WebHTTPLoadInfo.h" | |
26 #include "third_party/WebKit/public/platform/WebURL.h" | |
27 #include "third_party/WebKit/public/platform/WebURLError.h" | |
28 #include "third_party/WebKit/public/platform/WebURLLoadTiming.h" | |
29 #include "third_party/WebKit/public/platform/WebURLLoaderClient.h" | |
30 #include "third_party/WebKit/public/platform/WebURLRequest.h" | |
31 #include "third_party/WebKit/public/platform/WebURLResponse.h" | |
32 #include "third_party/WebKit/public/web/WebSecurityPolicy.h" | |
33 #include "webkit/child/ftp_directory_listing_response_delegate.h" | |
34 #include "webkit/child/multipart_response_delegate.h" | |
35 #include "webkit/child/resource_loader_bridge.h" | |
36 #include "webkit/child/weburlrequest_extradata_impl.h" | |
37 #include "webkit/child/weburlresponse_extradata_impl.h" | |
38 #include "webkit/common/resource_request_body.h" | |
39 | |
40 using base::Time; | |
41 using base::TimeTicks; | |
42 using blink::WebData; | |
43 using blink::WebHTTPBody; | |
44 using blink::WebHTTPHeaderVisitor; | |
45 using blink::WebHTTPLoadInfo; | |
46 using blink::WebReferrerPolicy; | |
47 using blink::WebSecurityPolicy; | |
48 using blink::WebString; | |
49 using blink::WebURL; | |
50 using blink::WebURLError; | |
51 using blink::WebURLLoadTiming; | |
52 using blink::WebURLLoader; | |
53 using blink::WebURLLoaderClient; | |
54 using blink::WebURLRequest; | |
55 using blink::WebURLResponse; | |
56 using webkit_glue::FtpDirectoryListingResponseDelegate; | |
57 using webkit_glue::MultipartResponseDelegate; | |
58 using webkit_glue::ResourceDevToolsInfo; | |
59 using webkit_glue::ResourceLoaderBridge; | |
60 using webkit_glue::ResourceRequestBody; | |
61 using webkit_glue::ResourceResponseInfo; | |
62 using webkit_glue::WebURLResponseExtraDataImpl; | |
63 | |
64 namespace content { | |
65 | |
66 // Utilities ------------------------------------------------------------------ | |
67 | |
68 namespace { | |
69 | |
70 const char kThrottledErrorDescription[] = | |
71 "Request throttled. Visit http://dev.chromium.org/throttling for more " | |
72 "information."; | |
73 | |
74 class HeaderFlattener : public WebHTTPHeaderVisitor { | |
75 public: | |
76 explicit HeaderFlattener(int load_flags) | |
77 : load_flags_(load_flags), | |
78 has_accept_header_(false) { | |
79 } | |
80 | |
81 virtual void visitHeader(const WebString& name, const WebString& value) { | |
82 // Headers are latin1. | |
83 const std::string& name_latin1 = name.latin1(); | |
84 const std::string& value_latin1 = value.latin1(); | |
85 | |
86 // Skip over referrer headers found in the header map because we already | |
87 // pulled it out as a separate parameter. | |
88 if (LowerCaseEqualsASCII(name_latin1, "referer")) | |
89 return; | |
90 | |
91 // Skip over "Cache-Control: max-age=0" header if the corresponding | |
92 // load flag is already specified. FrameLoader sets both the flag and | |
93 // the extra header -- the extra header is redundant since our network | |
94 // implementation will add the necessary headers based on load flags. | |
95 // See http://code.google.com/p/chromium/issues/detail?id=3434. | |
96 if ((load_flags_ & net::LOAD_VALIDATE_CACHE) && | |
97 LowerCaseEqualsASCII(name_latin1, "cache-control") && | |
98 LowerCaseEqualsASCII(value_latin1, "max-age=0")) | |
99 return; | |
100 | |
101 if (LowerCaseEqualsASCII(name_latin1, "accept")) | |
102 has_accept_header_ = true; | |
103 | |
104 if (!buffer_.empty()) | |
105 buffer_.append("\r\n"); | |
106 buffer_.append(name_latin1 + ": " + value_latin1); | |
107 } | |
108 | |
109 const std::string& GetBuffer() { | |
110 // In some cases, WebKit doesn't add an Accept header, but not having the | |
111 // header confuses some web servers. See bug 808613. | |
112 if (!has_accept_header_) { | |
113 if (!buffer_.empty()) | |
114 buffer_.append("\r\n"); | |
115 buffer_.append("Accept: */*"); | |
116 has_accept_header_ = true; | |
117 } | |
118 return buffer_; | |
119 } | |
120 | |
121 private: | |
122 int load_flags_; | |
123 std::string buffer_; | |
124 bool has_accept_header_; | |
125 }; | |
126 | |
127 // Extracts the information from a data: url. | |
128 bool GetInfoFromDataURL(const GURL& url, | |
129 ResourceResponseInfo* info, | |
130 std::string* data, | |
131 int* error_code) { | |
132 std::string mime_type; | |
133 std::string charset; | |
134 if (net::DataURL::Parse(url, &mime_type, &charset, data)) { | |
135 *error_code = net::OK; | |
136 // Assure same time for all time fields of data: URLs. | |
137 Time now = Time::Now(); | |
138 info->load_timing.request_start = TimeTicks::Now(); | |
139 info->load_timing.request_start_time = now; | |
140 info->request_time = now; | |
141 info->response_time = now; | |
142 info->headers = NULL; | |
143 info->mime_type.swap(mime_type); | |
144 info->charset.swap(charset); | |
145 info->security_info.clear(); | |
146 info->content_length = data->length(); | |
147 info->encoded_data_length = 0; | |
148 | |
149 return true; | |
150 } | |
151 | |
152 *error_code = net::ERR_INVALID_URL; | |
153 return false; | |
154 } | |
155 | |
156 typedef ResourceDevToolsInfo::HeadersVector HeadersVector; | |
157 | |
158 // Converts timing data from |load_timing| to the format used by WebKit. | |
159 void PopulateURLLoadTiming(const net::LoadTimingInfo& load_timing, | |
160 WebURLLoadTiming* url_timing) { | |
161 DCHECK(!load_timing.request_start.is_null()); | |
162 | |
163 const TimeTicks kNullTicks; | |
164 url_timing->initialize(); | |
165 url_timing->setRequestTime( | |
166 (load_timing.request_start - kNullTicks).InSecondsF()); | |
167 url_timing->setProxyStart( | |
168 (load_timing.proxy_resolve_start - kNullTicks).InSecondsF()); | |
169 url_timing->setProxyEnd( | |
170 (load_timing.proxy_resolve_end - kNullTicks).InSecondsF()); | |
171 url_timing->setDNSStart( | |
172 (load_timing.connect_timing.dns_start - kNullTicks).InSecondsF()); | |
173 url_timing->setDNSEnd( | |
174 (load_timing.connect_timing.dns_end - kNullTicks).InSecondsF()); | |
175 url_timing->setConnectStart( | |
176 (load_timing.connect_timing.connect_start - kNullTicks).InSecondsF()); | |
177 url_timing->setConnectEnd( | |
178 (load_timing.connect_timing.connect_end - kNullTicks).InSecondsF()); | |
179 url_timing->setSSLStart( | |
180 (load_timing.connect_timing.ssl_start - kNullTicks).InSecondsF()); | |
181 url_timing->setSSLEnd( | |
182 (load_timing.connect_timing.ssl_end - kNullTicks).InSecondsF()); | |
183 url_timing->setSendStart( | |
184 (load_timing.send_start - kNullTicks).InSecondsF()); | |
185 url_timing->setSendEnd( | |
186 (load_timing.send_end - kNullTicks).InSecondsF()); | |
187 url_timing->setReceiveHeadersEnd( | |
188 (load_timing.receive_headers_end - kNullTicks).InSecondsF()); | |
189 } | |
190 | |
191 net::RequestPriority ConvertWebKitPriorityToNetPriority( | |
192 const WebURLRequest::Priority& priority) { | |
193 switch (priority) { | |
194 case WebURLRequest::PriorityVeryHigh: | |
195 return net::HIGHEST; | |
196 | |
197 case WebURLRequest::PriorityHigh: | |
198 return net::MEDIUM; | |
199 | |
200 case WebURLRequest::PriorityMedium: | |
201 return net::LOW; | |
202 | |
203 case WebURLRequest::PriorityLow: | |
204 return net::LOWEST; | |
205 | |
206 case WebURLRequest::PriorityVeryLow: | |
207 return net::IDLE; | |
208 | |
209 case WebURLRequest::PriorityUnresolved: | |
210 default: | |
211 NOTREACHED(); | |
212 return net::LOW; | |
213 } | |
214 } | |
215 | |
216 } // namespace | |
217 | |
218 // WebURLLoaderImpl::Context -------------------------------------------------- | |
219 | |
220 // This inner class exists since the WebURLLoader may be deleted while inside a | |
221 // call to WebURLLoaderClient. The bridge requires its Peer to stay alive | |
222 // until it receives OnCompletedRequest. | |
223 class WebURLLoaderImpl::Context : public base::RefCounted<Context>, | |
224 public ResourceLoaderBridge::Peer { | |
225 public: | |
226 explicit Context(WebURLLoaderImpl* loader); | |
227 | |
228 WebURLLoaderClient* client() const { return client_; } | |
229 void set_client(WebURLLoaderClient* client) { client_ = client; } | |
230 | |
231 void Cancel(); | |
232 void SetDefersLoading(bool value); | |
233 void DidChangePriority(WebURLRequest::Priority new_priority); | |
234 void Start(const WebURLRequest& request, | |
235 ResourceLoaderBridge::SyncLoadResponse* sync_load_response, | |
236 BlinkPlatformImpl* platform); | |
237 | |
238 // ResourceLoaderBridge::Peer methods: | |
239 virtual void OnUploadProgress(uint64 position, uint64 size) OVERRIDE; | |
240 virtual bool OnReceivedRedirect( | |
241 const GURL& new_url, | |
242 const ResourceResponseInfo& info, | |
243 bool* has_new_first_party_for_cookies, | |
244 GURL* new_first_party_for_cookies) OVERRIDE; | |
245 virtual void OnReceivedResponse(const ResourceResponseInfo& info) OVERRIDE; | |
246 virtual void OnDownloadedData(int len, int encoded_data_length) OVERRIDE; | |
247 virtual void OnReceivedData(const char* data, | |
248 int data_length, | |
249 int encoded_data_length) OVERRIDE; | |
250 virtual void OnReceivedCachedMetadata(const char* data, int len) OVERRIDE; | |
251 virtual void OnCompletedRequest( | |
252 int error_code, | |
253 bool was_ignored_by_handler, | |
254 bool stale_copy_in_cache, | |
255 const std::string& security_info, | |
256 const base::TimeTicks& completion_time, | |
257 int64 total_transfer_size) OVERRIDE; | |
258 | |
259 private: | |
260 friend class base::RefCounted<Context>; | |
261 virtual ~Context() {} | |
262 | |
263 // We can optimize the handling of data URLs in most cases. | |
264 bool CanHandleDataURL(const GURL& url) const; | |
265 void HandleDataURL(); | |
266 | |
267 WebURLLoaderImpl* loader_; | |
268 WebURLRequest request_; | |
269 WebURLLoaderClient* client_; | |
270 WebReferrerPolicy referrer_policy_; | |
271 scoped_ptr<ResourceLoaderBridge> bridge_; | |
272 scoped_ptr<FtpDirectoryListingResponseDelegate> ftp_listing_delegate_; | |
273 scoped_ptr<MultipartResponseDelegate> multipart_delegate_; | |
274 scoped_ptr<ResourceLoaderBridge> completed_bridge_; | |
275 }; | |
276 | |
277 WebURLLoaderImpl::Context::Context(WebURLLoaderImpl* loader) | |
278 : loader_(loader), | |
279 client_(NULL), | |
280 referrer_policy_(blink::WebReferrerPolicyDefault) { | |
281 } | |
282 | |
283 void WebURLLoaderImpl::Context::Cancel() { | |
284 // The bridge will still send OnCompletedRequest, which will Release() us, so | |
285 // we don't do that here. | |
286 if (bridge_) | |
287 bridge_->Cancel(); | |
288 | |
289 // Ensure that we do not notify the multipart delegate anymore as it has | |
290 // its own pointer to the client. | |
291 if (multipart_delegate_) | |
292 multipart_delegate_->Cancel(); | |
293 | |
294 // Do not make any further calls to the client. | |
295 client_ = NULL; | |
296 loader_ = NULL; | |
297 } | |
298 | |
299 void WebURLLoaderImpl::Context::SetDefersLoading(bool value) { | |
300 if (bridge_) | |
301 bridge_->SetDefersLoading(value); | |
302 } | |
303 | |
304 void WebURLLoaderImpl::Context::DidChangePriority( | |
305 WebURLRequest::Priority new_priority) { | |
306 if (bridge_) | |
307 bridge_->DidChangePriority( | |
308 ConvertWebKitPriorityToNetPriority(new_priority)); | |
309 } | |
310 | |
311 void WebURLLoaderImpl::Context::Start( | |
312 const WebURLRequest& request, | |
313 ResourceLoaderBridge::SyncLoadResponse* sync_load_response, | |
314 BlinkPlatformImpl* platform) { | |
315 DCHECK(!bridge_.get()); | |
316 | |
317 request_ = request; // Save the request. | |
318 | |
319 GURL url = request.url(); | |
320 if (url.SchemeIs("data") && CanHandleDataURL(url)) { | |
321 if (sync_load_response) { | |
322 // This is a sync load. Do the work now. | |
323 sync_load_response->url = url; | |
324 std::string data; | |
325 GetInfoFromDataURL(sync_load_response->url, sync_load_response, | |
326 &sync_load_response->data, | |
327 &sync_load_response->error_code); | |
328 } else { | |
329 AddRef(); // Balanced in OnCompletedRequest | |
330 base::MessageLoop::current()->PostTask( | |
331 FROM_HERE, base::Bind(&Context::HandleDataURL, this)); | |
332 } | |
333 return; | |
334 } | |
335 | |
336 GURL referrer_url( | |
337 request.httpHeaderField(WebString::fromUTF8("Referer")).latin1()); | |
338 const std::string& method = request.httpMethod().latin1(); | |
339 | |
340 int load_flags = net::LOAD_NORMAL; | |
341 switch (request.cachePolicy()) { | |
342 case WebURLRequest::ReloadIgnoringCacheData: | |
343 // Required by LayoutTests/http/tests/misc/refresh-headers.php | |
344 load_flags |= net::LOAD_VALIDATE_CACHE; | |
345 break; | |
346 case WebURLRequest::ReturnCacheDataElseLoad: | |
347 load_flags |= net::LOAD_PREFERRING_CACHE; | |
348 break; | |
349 case WebURLRequest::ReturnCacheDataDontLoad: | |
350 load_flags |= net::LOAD_ONLY_FROM_CACHE; | |
351 break; | |
352 case WebURLRequest::UseProtocolCachePolicy: | |
353 break; | |
354 } | |
355 | |
356 if (request.reportUploadProgress()) | |
357 load_flags |= net::LOAD_ENABLE_UPLOAD_PROGRESS; | |
358 if (request.reportLoadTiming()) | |
359 load_flags |= net::LOAD_ENABLE_LOAD_TIMING; | |
360 if (request.reportRawHeaders()) | |
361 load_flags |= net::LOAD_REPORT_RAW_HEADERS; | |
362 | |
363 if (!request.allowCookies() || !request.allowStoredCredentials()) { | |
364 load_flags |= net::LOAD_DO_NOT_SAVE_COOKIES; | |
365 load_flags |= net::LOAD_DO_NOT_SEND_COOKIES; | |
366 } | |
367 | |
368 if (!request.allowStoredCredentials()) | |
369 load_flags |= net::LOAD_DO_NOT_SEND_AUTH_DATA; | |
370 | |
371 if (request.targetType() == WebURLRequest::TargetIsXHR && | |
372 (url.has_username() || url.has_password())) { | |
373 load_flags |= net::LOAD_DO_NOT_PROMPT_FOR_LOGIN; | |
374 } | |
375 | |
376 HeaderFlattener flattener(load_flags); | |
377 request.visitHTTPHeaderFields(&flattener); | |
378 | |
379 // TODO(brettw) this should take parameter encoding into account when | |
380 // creating the GURLs. | |
381 | |
382 ResourceLoaderBridge::RequestInfo request_info; | |
383 request_info.method = method; | |
384 request_info.url = url; | |
385 request_info.first_party_for_cookies = request.firstPartyForCookies(); | |
386 request_info.referrer = referrer_url; | |
387 request_info.headers = flattener.GetBuffer(); | |
388 request_info.load_flags = load_flags; | |
389 // requestor_pid only needs to be non-zero if the request originates outside | |
390 // the render process, so we can use requestorProcessID even for requests | |
391 // from in-process plugins. | |
392 request_info.requestor_pid = request.requestorProcessID(); | |
393 request_info.request_type = | |
394 ResourceType::FromTargetType(request.targetType()); | |
395 request_info.priority = | |
396 ConvertWebKitPriorityToNetPriority(request.priority()); | |
397 request_info.appcache_host_id = request.appCacheHostID(); | |
398 request_info.routing_id = request.requestorID(); | |
399 request_info.download_to_file = request.downloadToFile(); | |
400 request_info.has_user_gesture = request.hasUserGesture(); | |
401 request_info.extra_data = request.extraData(); | |
402 referrer_policy_ = request.referrerPolicy(); | |
403 request_info.referrer_policy = request.referrerPolicy(); | |
404 bridge_.reset(platform->CreateResourceLoader(request_info)); | |
405 | |
406 if (!request.httpBody().isNull()) { | |
407 // GET and HEAD requests shouldn't have http bodies. | |
408 DCHECK(method != "GET" && method != "HEAD"); | |
409 const WebHTTPBody& httpBody = request.httpBody(); | |
410 size_t i = 0; | |
411 WebHTTPBody::Element element; | |
412 scoped_refptr<ResourceRequestBody> request_body = new ResourceRequestBody; | |
413 while (httpBody.elementAt(i++, element)) { | |
414 switch (element.type) { | |
415 case WebHTTPBody::Element::TypeData: | |
416 if (!element.data.isEmpty()) { | |
417 // WebKit sometimes gives up empty data to append. These aren't | |
418 // necessary so we just optimize those out here. | |
419 request_body->AppendBytes( | |
420 element.data.data(), static_cast<int>(element.data.size())); | |
421 } | |
422 break; | |
423 case WebHTTPBody::Element::TypeFile: | |
424 if (element.fileLength == -1) { | |
425 request_body->AppendFileRange( | |
426 base::FilePath::FromUTF16Unsafe(element.filePath), | |
427 0, kuint64max, base::Time()); | |
428 } else { | |
429 request_body->AppendFileRange( | |
430 base::FilePath::FromUTF16Unsafe(element.filePath), | |
431 static_cast<uint64>(element.fileStart), | |
432 static_cast<uint64>(element.fileLength), | |
433 base::Time::FromDoubleT(element.modificationTime)); | |
434 } | |
435 break; | |
436 case WebHTTPBody::Element::TypeFileSystemURL: { | |
437 GURL file_system_url = element.fileSystemURL; | |
438 DCHECK(file_system_url.SchemeIsFileSystem()); | |
439 request_body->AppendFileSystemFileRange( | |
440 file_system_url, | |
441 static_cast<uint64>(element.fileStart), | |
442 static_cast<uint64>(element.fileLength), | |
443 base::Time::FromDoubleT(element.modificationTime)); | |
444 break; | |
445 } | |
446 case WebHTTPBody::Element::TypeBlob: | |
447 request_body->AppendBlob(element.blobUUID.utf8()); | |
448 break; | |
449 default: | |
450 NOTREACHED(); | |
451 } | |
452 } | |
453 request_body->set_identifier(request.httpBody().identifier()); | |
454 bridge_->SetRequestBody(request_body.get()); | |
455 } | |
456 | |
457 if (sync_load_response) { | |
458 bridge_->SyncLoad(sync_load_response); | |
459 return; | |
460 } | |
461 | |
462 if (bridge_->Start(this)) { | |
463 AddRef(); // Balanced in OnCompletedRequest | |
464 } else { | |
465 bridge_.reset(); | |
466 } | |
467 } | |
468 | |
469 void WebURLLoaderImpl::Context::OnUploadProgress(uint64 position, uint64 size) { | |
470 if (client_) | |
471 client_->didSendData(loader_, position, size); | |
472 } | |
473 | |
474 bool WebURLLoaderImpl::Context::OnReceivedRedirect( | |
475 const GURL& new_url, | |
476 const ResourceResponseInfo& info, | |
477 bool* has_new_first_party_for_cookies, | |
478 GURL* new_first_party_for_cookies) { | |
479 if (!client_) | |
480 return false; | |
481 | |
482 WebURLResponse response; | |
483 response.initialize(); | |
484 PopulateURLResponse(request_.url(), info, &response); | |
485 | |
486 // TODO(darin): We lack sufficient information to construct the actual | |
487 // request that resulted from the redirect. | |
488 WebURLRequest new_request(new_url); | |
489 new_request.setFirstPartyForCookies(request_.firstPartyForCookies()); | |
490 new_request.setDownloadToFile(request_.downloadToFile()); | |
491 | |
492 WebString referrer_string = WebString::fromUTF8("Referer"); | |
493 WebString referrer = WebSecurityPolicy::generateReferrerHeader( | |
494 referrer_policy_, | |
495 new_url, | |
496 request_.httpHeaderField(referrer_string)); | |
497 if (!referrer.isEmpty()) | |
498 new_request.setHTTPReferrer(referrer, referrer_policy_); | |
499 | |
500 std::string method = request_.httpMethod().utf8(); | |
501 std::string new_method = net::URLRequest::ComputeMethodForRedirect( | |
502 method, response.httpStatusCode()); | |
503 new_request.setHTTPMethod(WebString::fromUTF8(new_method)); | |
504 if (new_method == method) | |
505 new_request.setHTTPBody(request_.httpBody()); | |
506 | |
507 client_->willSendRequest(loader_, new_request, response); | |
508 request_ = new_request; | |
509 *has_new_first_party_for_cookies = true; | |
510 *new_first_party_for_cookies = request_.firstPartyForCookies(); | |
511 | |
512 // Only follow the redirect if WebKit left the URL unmodified. | |
513 if (new_url == GURL(new_request.url())) | |
514 return true; | |
515 | |
516 // We assume that WebKit only changes the URL to suppress a redirect, and we | |
517 // assume that it does so by setting it to be invalid. | |
518 DCHECK(!new_request.url().isValid()); | |
519 return false; | |
520 } | |
521 | |
522 void WebURLLoaderImpl::Context::OnReceivedResponse( | |
523 const ResourceResponseInfo& info) { | |
524 if (!client_) | |
525 return; | |
526 | |
527 WebURLResponse response; | |
528 response.initialize(); | |
529 PopulateURLResponse(request_.url(), info, &response); | |
530 | |
531 bool show_raw_listing = (GURL(request_.url()).query() == "raw"); | |
532 | |
533 if (info.mime_type == "text/vnd.chromium.ftp-dir") { | |
534 if (show_raw_listing) { | |
535 // Set the MIME type to plain text to prevent any active content. | |
536 response.setMIMEType("text/plain"); | |
537 } else { | |
538 // We're going to produce a parsed listing in HTML. | |
539 response.setMIMEType("text/html"); | |
540 } | |
541 } | |
542 | |
543 scoped_refptr<Context> protect(this); | |
544 client_->didReceiveResponse(loader_, response); | |
545 | |
546 // We may have been cancelled after didReceiveResponse, which would leave us | |
547 // without a client and therefore without much need to do further handling. | |
548 if (!client_) | |
549 return; | |
550 | |
551 DCHECK(!ftp_listing_delegate_.get()); | |
552 DCHECK(!multipart_delegate_.get()); | |
553 if (info.headers.get() && info.mime_type == "multipart/x-mixed-replace") { | |
554 std::string content_type; | |
555 info.headers->EnumerateHeader(NULL, "content-type", &content_type); | |
556 | |
557 std::string mime_type; | |
558 std::string charset; | |
559 bool had_charset = false; | |
560 std::string boundary; | |
561 net::HttpUtil::ParseContentType(content_type, &mime_type, &charset, | |
562 &had_charset, &boundary); | |
563 base::TrimString(boundary, " \"", &boundary); | |
564 | |
565 // If there's no boundary, just handle the request normally. In the gecko | |
566 // code, nsMultiMixedConv::OnStartRequest throws an exception. | |
567 if (!boundary.empty()) { | |
568 multipart_delegate_.reset( | |
569 new MultipartResponseDelegate(client_, loader_, response, boundary)); | |
570 } | |
571 } else if (info.mime_type == "text/vnd.chromium.ftp-dir" && | |
572 !show_raw_listing) { | |
573 ftp_listing_delegate_.reset( | |
574 new FtpDirectoryListingResponseDelegate(client_, loader_, response)); | |
575 } | |
576 } | |
577 | |
578 void WebURLLoaderImpl::Context::OnDownloadedData(int len, | |
579 int encoded_data_length) { | |
580 if (client_) | |
581 client_->didDownloadData(loader_, len, encoded_data_length); | |
582 } | |
583 | |
584 void WebURLLoaderImpl::Context::OnReceivedData(const char* data, | |
585 int data_length, | |
586 int encoded_data_length) { | |
587 if (!client_) | |
588 return; | |
589 | |
590 if (ftp_listing_delegate_) { | |
591 // The FTP listing delegate will make the appropriate calls to | |
592 // client_->didReceiveData and client_->didReceiveResponse. | |
593 ftp_listing_delegate_->OnReceivedData(data, data_length); | |
594 } else if (multipart_delegate_) { | |
595 // The multipart delegate will make the appropriate calls to | |
596 // client_->didReceiveData and client_->didReceiveResponse. | |
597 multipart_delegate_->OnReceivedData(data, data_length, encoded_data_length); | |
598 } else { | |
599 client_->didReceiveData(loader_, data, data_length, encoded_data_length); | |
600 } | |
601 } | |
602 | |
603 void WebURLLoaderImpl::Context::OnReceivedCachedMetadata( | |
604 const char* data, int len) { | |
605 if (client_) | |
606 client_->didReceiveCachedMetadata(loader_, data, len); | |
607 } | |
608 | |
609 void WebURLLoaderImpl::Context::OnCompletedRequest( | |
610 int error_code, | |
611 bool was_ignored_by_handler, | |
612 bool stale_copy_in_cache, | |
613 const std::string& security_info, | |
614 const base::TimeTicks& completion_time, | |
615 int64 total_transfer_size) { | |
616 if (ftp_listing_delegate_) { | |
617 ftp_listing_delegate_->OnCompletedRequest(); | |
618 ftp_listing_delegate_.reset(NULL); | |
619 } else if (multipart_delegate_) { | |
620 multipart_delegate_->OnCompletedRequest(); | |
621 multipart_delegate_.reset(NULL); | |
622 } | |
623 | |
624 // Prevent any further IPC to the browser now that we're complete, but | |
625 // don't delete it to keep any downloaded temp files alive. | |
626 DCHECK(!completed_bridge_.get()); | |
627 completed_bridge_.swap(bridge_); | |
628 | |
629 if (client_) { | |
630 if (error_code != net::OK) { | |
631 client_->didFail(loader_, CreateError(request_.url(), | |
632 stale_copy_in_cache, | |
633 error_code)); | |
634 } else { | |
635 client_->didFinishLoading( | |
636 loader_, (completion_time - TimeTicks()).InSecondsF(), | |
637 total_transfer_size); | |
638 } | |
639 } | |
640 | |
641 // We are done with the bridge now, and so we need to release the reference | |
642 // to ourselves that we took on behalf of the bridge. This may cause our | |
643 // destruction. | |
644 Release(); | |
645 } | |
646 | |
647 bool WebURLLoaderImpl::Context::CanHandleDataURL(const GURL& url) const { | |
648 DCHECK(url.SchemeIs("data")); | |
649 | |
650 // Optimize for the case where we can handle a data URL locally. We must | |
651 // skip this for data URLs targetted at frames since those could trigger a | |
652 // download. | |
653 // | |
654 // NOTE: We special case MIME types we can render both for performance | |
655 // reasons as well as to support unit tests, which do not have an underlying | |
656 // ResourceLoaderBridge implementation. | |
657 | |
658 #if defined(OS_ANDROID) | |
659 // For compatibility reasons on Android we need to expose top-level data:// | |
660 // to the browser. | |
661 if (request_.targetType() == WebURLRequest::TargetIsMainFrame) | |
662 return false; | |
663 #endif | |
664 | |
665 if (request_.targetType() != WebURLRequest::TargetIsMainFrame && | |
666 request_.targetType() != WebURLRequest::TargetIsSubframe) | |
667 return true; | |
668 | |
669 std::string mime_type, unused_charset; | |
670 if (net::DataURL::Parse(url, &mime_type, &unused_charset, NULL) && | |
671 net::IsSupportedMimeType(mime_type)) | |
672 return true; | |
673 | |
674 return false; | |
675 } | |
676 | |
677 void WebURLLoaderImpl::Context::HandleDataURL() { | |
678 ResourceResponseInfo info; | |
679 int error_code; | |
680 std::string data; | |
681 | |
682 if (GetInfoFromDataURL(request_.url(), &info, &data, &error_code)) { | |
683 OnReceivedResponse(info); | |
684 if (!data.empty()) | |
685 OnReceivedData(data.data(), data.size(), 0); | |
686 } | |
687 | |
688 OnCompletedRequest(error_code, false, false, info.security_info, | |
689 base::TimeTicks::Now(), 0); | |
690 } | |
691 | |
692 // WebURLLoaderImpl ----------------------------------------------------------- | |
693 | |
694 WebURLLoaderImpl::WebURLLoaderImpl(BlinkPlatformImpl* platform) | |
695 : context_(new Context(this)), | |
696 platform_(platform) { | |
697 } | |
698 | |
699 WebURLLoaderImpl::~WebURLLoaderImpl() { | |
700 cancel(); | |
701 } | |
702 | |
703 WebURLError WebURLLoaderImpl::CreateError(const WebURL& unreachable_url, | |
704 bool stale_copy_in_cache, | |
705 int reason) { | |
706 WebURLError error; | |
707 error.domain = WebString::fromUTF8(net::kErrorDomain); | |
708 error.reason = reason; | |
709 error.unreachableURL = unreachable_url; | |
710 error.staleCopyInCache = stale_copy_in_cache; | |
711 if (reason == net::ERR_ABORTED) { | |
712 error.isCancellation = true; | |
713 } else if (reason == net::ERR_TEMPORARILY_THROTTLED) { | |
714 error.localizedDescription = WebString::fromUTF8( | |
715 kThrottledErrorDescription); | |
716 } else { | |
717 error.localizedDescription = WebString::fromUTF8( | |
718 net::ErrorToString(reason)); | |
719 } | |
720 return error; | |
721 } | |
722 | |
723 void WebURLLoaderImpl::PopulateURLResponse(const GURL& url, | |
724 const ResourceResponseInfo& info, | |
725 WebURLResponse* response) { | |
726 response->setURL(url); | |
727 response->setResponseTime(info.response_time.ToDoubleT()); | |
728 response->setMIMEType(WebString::fromUTF8(info.mime_type)); | |
729 response->setTextEncodingName(WebString::fromUTF8(info.charset)); | |
730 response->setExpectedContentLength(info.content_length); | |
731 response->setSecurityInfo(info.security_info); | |
732 response->setAppCacheID(info.appcache_id); | |
733 response->setAppCacheManifestURL(info.appcache_manifest_url); | |
734 response->setWasCached(!info.load_timing.request_start_time.is_null() && | |
735 info.response_time < info.load_timing.request_start_time); | |
736 response->setRemoteIPAddress( | |
737 WebString::fromUTF8(info.socket_address.host())); | |
738 response->setRemotePort(info.socket_address.port()); | |
739 response->setConnectionID(info.load_timing.socket_log_id); | |
740 response->setConnectionReused(info.load_timing.socket_reused); | |
741 response->setDownloadFilePath(info.download_file_path.AsUTF16Unsafe()); | |
742 WebURLResponseExtraDataImpl* extra_data = | |
743 new WebURLResponseExtraDataImpl(info.npn_negotiated_protocol); | |
744 response->setExtraData(extra_data); | |
745 extra_data->set_was_fetched_via_spdy(info.was_fetched_via_spdy); | |
746 extra_data->set_was_npn_negotiated(info.was_npn_negotiated); | |
747 extra_data->set_was_alternate_protocol_available( | |
748 info.was_alternate_protocol_available); | |
749 extra_data->set_connection_info(info.connection_info); | |
750 extra_data->set_was_fetched_via_proxy(info.was_fetched_via_proxy); | |
751 | |
752 // If there's no received headers end time, don't set load timing. This is | |
753 // the case for non-HTTP requests, requests that don't go over the wire, and | |
754 // certain error cases. | |
755 if (!info.load_timing.receive_headers_end.is_null()) { | |
756 WebURLLoadTiming timing; | |
757 PopulateURLLoadTiming(info.load_timing, &timing); | |
758 response->setLoadTiming(timing); | |
759 } | |
760 | |
761 if (info.devtools_info.get()) { | |
762 WebHTTPLoadInfo load_info; | |
763 | |
764 load_info.setHTTPStatusCode(info.devtools_info->http_status_code); | |
765 load_info.setHTTPStatusText(WebString::fromLatin1( | |
766 info.devtools_info->http_status_text)); | |
767 load_info.setEncodedDataLength(info.encoded_data_length); | |
768 | |
769 load_info.setRequestHeadersText(WebString::fromLatin1( | |
770 info.devtools_info->request_headers_text)); | |
771 load_info.setResponseHeadersText(WebString::fromLatin1( | |
772 info.devtools_info->response_headers_text)); | |
773 const HeadersVector& request_headers = info.devtools_info->request_headers; | |
774 for (HeadersVector::const_iterator it = request_headers.begin(); | |
775 it != request_headers.end(); ++it) { | |
776 load_info.addRequestHeader(WebString::fromLatin1(it->first), | |
777 WebString::fromLatin1(it->second)); | |
778 } | |
779 const HeadersVector& response_headers = | |
780 info.devtools_info->response_headers; | |
781 for (HeadersVector::const_iterator it = response_headers.begin(); | |
782 it != response_headers.end(); ++it) { | |
783 load_info.addResponseHeader(WebString::fromLatin1(it->first), | |
784 WebString::fromLatin1(it->second)); | |
785 } | |
786 response->setHTTPLoadInfo(load_info); | |
787 } | |
788 | |
789 const net::HttpResponseHeaders* headers = info.headers.get(); | |
790 if (!headers) | |
791 return; | |
792 | |
793 WebURLResponse::HTTPVersion version = WebURLResponse::Unknown; | |
794 if (headers->GetHttpVersion() == net::HttpVersion(0, 9)) | |
795 version = WebURLResponse::HTTP_0_9; | |
796 else if (headers->GetHttpVersion() == net::HttpVersion(1, 0)) | |
797 version = WebURLResponse::HTTP_1_0; | |
798 else if (headers->GetHttpVersion() == net::HttpVersion(1, 1)) | |
799 version = WebURLResponse::HTTP_1_1; | |
800 response->setHTTPVersion(version); | |
801 response->setHTTPStatusCode(headers->response_code()); | |
802 response->setHTTPStatusText(WebString::fromLatin1(headers->GetStatusText())); | |
803 | |
804 // TODO(darin): We should leverage HttpResponseHeaders for this, and this | |
805 // should be using the same code as ResourceDispatcherHost. | |
806 // TODO(jungshik): Figure out the actual value of the referrer charset and | |
807 // pass it to GetSuggestedFilename. | |
808 std::string value; | |
809 headers->EnumerateHeader(NULL, "content-disposition", &value); | |
810 response->setSuggestedFileName( | |
811 net::GetSuggestedFilename(url, | |
812 value, | |
813 std::string(), // referrer_charset | |
814 std::string(), // suggested_name | |
815 std::string(), // mime_type | |
816 std::string())); // default_name | |
817 | |
818 Time time_val; | |
819 if (headers->GetLastModifiedValue(&time_val)) | |
820 response->setLastModifiedDate(time_val.ToDoubleT()); | |
821 | |
822 // Build up the header map. | |
823 void* iter = NULL; | |
824 std::string name; | |
825 while (headers->EnumerateHeaderLines(&iter, &name, &value)) { | |
826 response->addHTTPHeaderField(WebString::fromLatin1(name), | |
827 WebString::fromLatin1(value)); | |
828 } | |
829 } | |
830 | |
831 void WebURLLoaderImpl::loadSynchronously(const WebURLRequest& request, | |
832 WebURLResponse& response, | |
833 WebURLError& error, | |
834 WebData& data) { | |
835 ResourceLoaderBridge::SyncLoadResponse sync_load_response; | |
836 context_->Start(request, &sync_load_response, platform_); | |
837 | |
838 const GURL& final_url = sync_load_response.url; | |
839 | |
840 // TODO(tc): For file loads, we may want to include a more descriptive | |
841 // status code or status text. | |
842 int error_code = sync_load_response.error_code; | |
843 if (error_code != net::OK) { | |
844 response.setURL(final_url); | |
845 error.domain = WebString::fromUTF8(net::kErrorDomain); | |
846 error.reason = error_code; | |
847 error.unreachableURL = final_url; | |
848 return; | |
849 } | |
850 | |
851 PopulateURLResponse(final_url, sync_load_response, &response); | |
852 | |
853 data.assign(sync_load_response.data.data(), | |
854 sync_load_response.data.size()); | |
855 } | |
856 | |
857 void WebURLLoaderImpl::loadAsynchronously(const WebURLRequest& request, | |
858 WebURLLoaderClient* client) { | |
859 DCHECK(!context_->client()); | |
860 | |
861 context_->set_client(client); | |
862 context_->Start(request, NULL, platform_); | |
863 } | |
864 | |
865 void WebURLLoaderImpl::cancel() { | |
866 context_->Cancel(); | |
867 } | |
868 | |
869 void WebURLLoaderImpl::setDefersLoading(bool value) { | |
870 context_->SetDefersLoading(value); | |
871 } | |
872 | |
873 void WebURLLoaderImpl::didChangePriority(WebURLRequest::Priority new_priority) { | |
874 context_->DidChangePriority(new_priority); | |
875 } | |
876 | |
877 } // namespace content | |
OLD | NEW |