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