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