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