1 // Copyright (c) 2011 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/glue/weburlloader_impl.h" 8 9 #include "base/file_path.h" 10 #include "base/memory/scoped_ptr.h" 11 #include "base/message_loop.h" 12 #include "base/process_util.h" 13 #include "base/string_util.h" 14 #include "base/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 "third_party/WebKit/Source/WebKit/chromium/public/WebHTTPHeaderVisitor.h" 22 #include "third_party/WebKit/Source/WebKit/chromium/public/WebHTTPLoadInfo.h" 23 #include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityPolicy.h" 24 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURL.h" 25 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLError.h" 26 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLLoadTiming.h" 27 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLLoaderClient.h" 28 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLRequest.h" 29 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLResponse.h" 30 #include "webkit/glue/ftp_directory_listing_response_delegate.h" 31 #include "webkit/glue/multipart_response_delegate.h" 32 #include "webkit/glue/resource_loader_bridge.h" 33 #include "webkit/glue/site_isolation_metrics.h" 34 #include "webkit/glue/webkit_glue.h" 35 36 using base::Time; 37 using base::TimeDelta; 38 using WebKit::WebData; 39 using WebKit::WebHTTPBody; 40 using WebKit::WebHTTPHeaderVisitor; 41 using WebKit::WebHTTPLoadInfo; 42 using WebKit::WebSecurityPolicy; 43 using WebKit::WebString; 44 using WebKit::WebURL; 45 using WebKit::WebURLError; 46 using WebKit::WebURLLoadTiming; 47 using WebKit::WebURLLoader; 48 using WebKit::WebURLLoaderClient; 49 using WebKit::WebURLRequest; 50 using WebKit::WebURLResponse; 51 52 namespace webkit_glue { 53 54 // Utilities ------------------------------------------------------------------ 55 56 namespace { 57 58 class HeaderFlattener : public WebHTTPHeaderVisitor { 59 public: 60 explicit HeaderFlattener(int load_flags) 61 : load_flags_(load_flags), 62 has_accept_header_(false) { 63 } 64 65 virtual void visitHeader(const WebString& name, const WebString& value) { 66 // TODO(darin): is UTF-8 really correct here? It is if the strings are 67 // already ASCII (i.e., if they are already escaped properly). 68 const std::string& name_utf8 = name.utf8(); 69 const std::string& value_utf8 = value.utf8(); 70 71 // Skip over referrer headers found in the header map because we already 72 // pulled it out as a separate parameter. 73 if (LowerCaseEqualsASCII(name_utf8, "referer")) 74 return; 75 76 // Skip over "Cache-Control: max-age=0" header if the corresponding 77 // load flag is already specified. FrameLoader sets both the flag and 78 // the extra header -- the extra header is redundant since our network 79 // implementation will add the necessary headers based on load flags. 80 // See http://code.google.com/p/chromium/issues/detail?id=3434. 81 if ((load_flags_ & net::LOAD_VALIDATE_CACHE) && 82 LowerCaseEqualsASCII(name_utf8, "cache-control") && 83 LowerCaseEqualsASCII(value_utf8, "max-age=0")) 84 return; 85 86 if (LowerCaseEqualsASCII(name_utf8, "accept")) 87 has_accept_header_ = true; 88 89 if (!buffer_.empty()) 90 buffer_.append("\r\n"); 91 buffer_.append(name_utf8 + ": " + value_utf8); 92 } 93 94 const std::string& GetBuffer() { 95 // In some cases, WebKit doesn't add an Accept header, but not having the 96 // header confuses some web servers. See bug 808613. 97 if (!has_accept_header_) { 98 if (!buffer_.empty()) 99 buffer_.append("\r\n"); 100 buffer_.append("Accept: */*"); 101 has_accept_header_ = true; 102 } 103 return buffer_; 104 } 105 106 private: 107 int load_flags_; 108 std::string buffer_; 109 bool has_accept_header_; 110 }; 111 112 ResourceType::Type FromTargetType(WebURLRequest::TargetType type) { 113 switch (type) { 114 case WebURLRequest::TargetIsMainFrame: 115 return ResourceType::MAIN_FRAME; 116 case WebURLRequest::TargetIsSubframe: 117 return ResourceType::SUB_FRAME; 118 case WebURLRequest::TargetIsSubresource: 119 return ResourceType::SUB_RESOURCE; 120 case WebURLRequest::TargetIsStyleSheet: 121 return ResourceType::STYLESHEET; 122 case WebURLRequest::TargetIsScript: 123 return ResourceType::SCRIPT; 124 case WebURLRequest::TargetIsFontResource: 125 return ResourceType::FONT_RESOURCE; 126 case WebURLRequest::TargetIsImage: 127 return ResourceType::IMAGE; 128 case WebURLRequest::TargetIsObject: 129 return ResourceType::OBJECT; 130 case WebURLRequest::TargetIsMedia: 131 return ResourceType::MEDIA; 132 case WebURLRequest::TargetIsWorker: 133 return ResourceType::WORKER; 134 case WebURLRequest::TargetIsSharedWorker: 135 return ResourceType::SHARED_WORKER; 136 case WebURLRequest::TargetIsPrefetch: 137 return ResourceType::PREFETCH; 138 case WebURLRequest::TargetIsFavicon: 139 return ResourceType::FAVICON; 140 default: 141 NOTREACHED(); 142 return ResourceType::SUB_RESOURCE; 143 } 144 } 145 146 // Extracts the information from a data: url. 147 bool GetInfoFromDataURL(const GURL& url, 148 ResourceResponseInfo* info, 149 std::string* data, 150 net::URLRequestStatus* status) { 151 std::string mime_type; 152 std::string charset; 153 if (net::DataURL::Parse(url, &mime_type, &charset, data)) { 154 *status = net::URLRequestStatus(net::URLRequestStatus::SUCCESS, 0); 155 info->request_time = Time::Now(); 156 info->response_time = Time::Now(); 157 info->headers = NULL; 158 info->mime_type.swap(mime_type); 159 info->charset.swap(charset); 160 info->security_info.clear(); 161 info->content_length = -1; 162 info->encoded_data_length = 0; 163 info->load_timing.base_time = Time::Now(); 164 165 return true; 166 } 167 168 *status = net::URLRequestStatus(net::URLRequestStatus::FAILED, 169 net::ERR_INVALID_URL); 170 return false; 171 } 172 173 typedef ResourceDevToolsInfo::HeadersVector HeadersVector; 174 175 void PopulateURLResponse( 176 const GURL& url, 177 const ResourceResponseInfo& info, 178 WebURLResponse* response) { 179 response->setURL(url); 180 response->setResponseTime(info.response_time.ToDoubleT()); 181 response->setMIMEType(WebString::fromUTF8(info.mime_type)); 182 response->setTextEncodingName(WebString::fromUTF8(info.charset)); 183 response->setExpectedContentLength(info.content_length); 184 response->setSecurityInfo(info.security_info); 185 response->setAppCacheID(info.appcache_id); 186 response->setAppCacheManifestURL(info.appcache_manifest_url); 187 response->setWasCached(!info.load_timing.base_time.is_null() && 188 info.response_time < info.load_timing.base_time); 189 response->setWasFetchedViaSPDY(info.was_fetched_via_spdy); 190 response->setWasNpnNegotiated(info.was_npn_negotiated); 191 response->setWasAlternateProtocolAvailable( 192 info.was_alternate_protocol_available); 193 response->setWasFetchedViaProxy(info.was_fetched_via_proxy); 194 response->setRemoteIPAddress( 195 WebString::fromUTF8(info.socket_address.host())); 196 response->setRemotePort(info.socket_address.port()); 197 response->setConnectionID(info.connection_id); 198 response->setConnectionReused(info.connection_reused); 199 response->setDownloadFilePath(FilePathToWebString(info.download_file_path)); 200 201 const ResourceLoadTimingInfo& timing_info = info.load_timing; 202 if (!timing_info.base_time.is_null()) { 203 WebURLLoadTiming timing; 204 timing.initialize(); 205 timing.setRequestTime(timing_info.base_time.ToDoubleT()); 206 timing.setProxyStart(timing_info.proxy_start); 207 timing.setProxyEnd(timing_info.proxy_end); 208 timing.setDNSStart(timing_info.dns_start); 209 timing.setDNSEnd(timing_info.dns_end); 210 timing.setConnectStart(timing_info.connect_start); 211 timing.setConnectEnd(timing_info.connect_end); 212 timing.setSSLStart(timing_info.ssl_start); 213 timing.setSSLEnd(timing_info.ssl_end); 214 timing.setSendStart(timing_info.send_start); 215 timing.setSendEnd(timing_info.send_end); 216 timing.setReceiveHeadersEnd(timing_info.receive_headers_end); 217 response->setLoadTiming(timing); 218 } 219 220 if (info.devtools_info.get()) { 221 WebHTTPLoadInfo load_info; 222 223 load_info.setHTTPStatusCode(info.devtools_info->http_status_code); 224 load_info.setHTTPStatusText(WebString::fromUTF8( 225 info.devtools_info->http_status_text)); 226 load_info.setEncodedDataLength(info.encoded_data_length); 227 228 const HeadersVector& request_headers = info.devtools_info->request_headers; 229 for (HeadersVector::const_iterator it = request_headers.begin(); 230 it != request_headers.end(); ++it) { 231 load_info.addRequestHeader(WebString::fromUTF8(it->first), 232 WebString::fromUTF8(it->second)); 233 } 234 const HeadersVector& response_headers = 235 info.devtools_info->response_headers; 236 for (HeadersVector::const_iterator it = response_headers.begin(); 237 it != response_headers.end(); ++it) { 238 load_info.addResponseHeader(WebString::fromUTF8(it->first), 239 WebString::fromUTF8(it->second)); 240 } 241 response->setHTTPLoadInfo(load_info); 242 } 243 244 const net::HttpResponseHeaders* headers = info.headers; 245 if (!headers) 246 return; 247 248 response->setHTTPStatusCode(headers->response_code()); 249 response->setHTTPStatusText(WebString::fromUTF8(headers->GetStatusText())); 250 251 // TODO(darin): We should leverage HttpResponseHeaders for this, and this 252 // should be using the same code as ResourceDispatcherHost. 253 // TODO(jungshik): Figure out the actual value of the referrer charset and 254 // pass it to GetSuggestedFilename. 255 std::string value; 256 if (headers->EnumerateHeader(NULL, "content-disposition", &value)) { 257 response->setSuggestedFileName( 258 net::GetSuggestedFilename(url, value, "", string16())); 259 } 260 261 Time time_val; 262 if (headers->GetLastModifiedValue(&time_val)) 263 response->setLastModifiedDate(time_val.ToDoubleT()); 264 265 // Build up the header map. 266 void* iter = NULL; 267 std::string name; 268 while (headers->EnumerateHeaderLines(&iter, &name, &value)) { 269 response->addHTTPHeaderField(WebString::fromUTF8(name), 270 WebString::fromUTF8(value)); 271 } 272 } 273 274 } // namespace 275 276 // WebURLLoaderImpl::Context -------------------------------------------------- 277 278 // This inner class exists since the WebURLLoader may be deleted while inside a 279 // call to WebURLLoaderClient. The bridge requires its Peer to stay alive 280 // until it receives OnCompletedRequest. 281 class WebURLLoaderImpl::Context : public base::RefCounted<Context>, 282 public ResourceLoaderBridge::Peer { 283 public: 284 explicit Context(WebURLLoaderImpl* loader); 285 286 WebURLLoaderClient* client() const { return client_; } 287 void set_client(WebURLLoaderClient* client) { client_ = client; } 288 289 void Cancel(); 290 void SetDefersLoading(bool value); 291 void Start( 292 const WebURLRequest& request, 293 ResourceLoaderBridge::SyncLoadResponse* sync_load_response); 294 295 // ResourceLoaderBridge::Peer methods: 296 virtual void OnUploadProgress(uint64 position, uint64 size); 297 virtual bool OnReceivedRedirect( 298 const GURL& new_url, 299 const ResourceResponseInfo& info, 300 bool* has_new_first_party_for_cookies, 301 GURL* new_first_party_for_cookies); 302 virtual void OnReceivedResponse(const ResourceResponseInfo& info); 303 virtual void OnDownloadedData(int len); 304 virtual void OnReceivedData(const char* data, 305 int data_length, 306 int encoded_data_length); 307 virtual void OnReceivedCachedMetadata(const char* data, int len); 308 virtual void OnCompletedRequest(const net::URLRequestStatus& status, 309 const std::string& security_info, 310 const base::Time& completion_time); 311 312 private: 313 friend class base::RefCounted<Context>; 314 ~Context() {} 315 316 // We can optimize the handling of data URLs in most cases. 317 bool CanHandleDataURL(const GURL& url) const; 318 void HandleDataURL(); 319 320 WebURLLoaderImpl* loader_; 321 WebURLRequest request_; 322 WebURLLoaderClient* client_; 323 scoped_ptr<ResourceLoaderBridge> bridge_; 324 scoped_ptr<FtpDirectoryListingResponseDelegate> ftp_listing_delegate_; 325 scoped_ptr<MultipartResponseDelegate> multipart_delegate_; 326 scoped_ptr<ResourceLoaderBridge> completed_bridge_; 327 328 // TODO(japhet): Storing this is a temporary hack for site isolation logging. 329 WebURL response_url_; 330 }; 331 332 WebURLLoaderImpl::Context::Context(WebURLLoaderImpl* loader) 333 : loader_(loader), 334 client_(NULL) { 335 } 336 337 void WebURLLoaderImpl::Context::Cancel() { 338 // The bridge will still send OnCompletedRequest, which will Release() us, so 339 // we don't do that here. 340 if (bridge_.get()) 341 bridge_->Cancel(); 342 343 // Ensure that we do not notify the multipart delegate anymore as it has 344 // its own pointer to the client. 345 if (multipart_delegate_.get()) 346 multipart_delegate_->Cancel(); 347 348 // Do not make any further calls to the client. 349 client_ = NULL; 350 loader_ = NULL; 351 } 352 353 void WebURLLoaderImpl::Context::SetDefersLoading(bool value) { 354 if (bridge_.get()) 355 bridge_->SetDefersLoading(value); 356 } 357 358 void WebURLLoaderImpl::Context::Start( 359 const WebURLRequest& request, 360 ResourceLoaderBridge::SyncLoadResponse* sync_load_response) { 361 DCHECK(!bridge_.get()); 362 363 request_ = request; // Save the request. 364 365 GURL url = request.url(); 366 if (url.SchemeIs("data") && CanHandleDataURL(url)) { 367 if (sync_load_response) { 368 // This is a sync load. Do the work now. 369 sync_load_response->url = url; 370 std::string data; 371 GetInfoFromDataURL(sync_load_response->url, sync_load_response, 372 &sync_load_response->data, 373 &sync_load_response->status); 374 } else { 375 AddRef(); // Balanced in OnCompletedRequest 376 MessageLoop::current()->PostTask(FROM_HERE, 377 NewRunnableMethod(this, &Context::HandleDataURL)); 378 } 379 return; 380 } 381 382 GURL referrer_url( 383 request.httpHeaderField(WebString::fromUTF8("Referer")).utf8()); 384 const std::string& method = request.httpMethod().utf8(); 385 386 int load_flags = net::LOAD_NORMAL; 387 switch (request.cachePolicy()) { 388 case WebURLRequest::ReloadIgnoringCacheData: 389 // Required by LayoutTests/http/tests/misc/refresh-headers.php 390 load_flags |= net::LOAD_VALIDATE_CACHE; 391 break; 392 case WebURLRequest::ReturnCacheDataElseLoad: 393 load_flags |= net::LOAD_PREFERRING_CACHE; 394 break; 395 case WebURLRequest::ReturnCacheDataDontLoad: 396 load_flags |= net::LOAD_ONLY_FROM_CACHE; 397 break; 398 case WebURLRequest::UseProtocolCachePolicy: 399 break; 400 } 401 402 if (request.reportUploadProgress()) 403 load_flags |= net::LOAD_ENABLE_UPLOAD_PROGRESS; 404 if (request.reportLoadTiming()) 405 load_flags |= net::LOAD_ENABLE_LOAD_TIMING; 406 if (request.reportRawHeaders()) 407 load_flags |= net::LOAD_REPORT_RAW_HEADERS; 408 409 if (!request.allowCookies() || !request.allowStoredCredentials()) { 410 load_flags |= net::LOAD_DO_NOT_SAVE_COOKIES; 411 load_flags |= net::LOAD_DO_NOT_SEND_COOKIES; 412 } 413 414 if (!request.allowStoredCredentials()) 415 load_flags |= net::LOAD_DO_NOT_SEND_AUTH_DATA; 416 417 HeaderFlattener flattener(load_flags); 418 request.visitHTTPHeaderFields(&flattener); 419 420 // TODO(abarth): These are wrong! I need to figure out how to get the right 421 // strings here. See: http://crbug.com/8706 422 std::string frame_origin = request.firstPartyForCookies().spec(); 423 std::string main_frame_origin = request.firstPartyForCookies().spec(); 424 425 // TODO(brettw) this should take parameter encoding into account when 426 // creating the GURLs. 427 428 ResourceLoaderBridge::RequestInfo request_info; 429 request_info.method = method; 430 request_info.url = url; 431 request_info.first_party_for_cookies = request.firstPartyForCookies(); 432 request_info.referrer = referrer_url; 433 request_info.frame_origin = frame_origin; 434 request_info.main_frame_origin = main_frame_origin; 435 request_info.headers = flattener.GetBuffer(); 436 request_info.load_flags = load_flags; 437 // requestor_pid only needs to be non-zero if the request originates outside 438 // the render process, so we can use requestorProcessID even for requests 439 // from in-process plugins. 440 request_info.requestor_pid = request.requestorProcessID(); 441 request_info.request_type = FromTargetType(request.targetType()); 442 request_info.appcache_host_id = request.appCacheHostID(); 443 request_info.routing_id = request.requestorID(); 444 request_info.download_to_file = request.downloadToFile(); 445 request_info.has_user_gesture = request.hasUserGesture(); 446 bridge_.reset(ResourceLoaderBridge::Create(request_info)); 447 448 if (!request.httpBody().isNull()) { 449 // GET and HEAD requests shouldn't have http bodies. 450 DCHECK(method != "GET" && method != "HEAD"); 451 const WebHTTPBody& httpBody = request.httpBody(); 452 size_t i = 0; 453 WebHTTPBody::Element element; 454 while (httpBody.elementAt(i++, element)) { 455 switch (element.type) { 456 case WebHTTPBody::Element::TypeData: 457 if (!element.data.isEmpty()) { 458 // WebKit sometimes gives up empty data to append. These aren't 459 // necessary so we just optimize those out here. 460 bridge_->AppendDataToUpload( 461 element.data.data(), static_cast<int>(element.data.size())); 462 } 463 break; 464 case WebHTTPBody::Element::TypeFile: 465 if (element.fileLength == -1) { 466 bridge_->AppendFileToUpload( 467 WebStringToFilePath(element.filePath)); 468 } else { 469 bridge_->AppendFileRangeToUpload( 470 WebStringToFilePath(element.filePath), 471 static_cast<uint64>(element.fileStart), 472 static_cast<uint64>(element.fileLength), 473 base::Time::FromDoubleT(element.modificationTime)); 474 } 475 break; 476 case WebHTTPBody::Element::TypeBlob: 477 bridge_->AppendBlobToUpload(GURL(element.blobURL)); 478 break; 479 default: 480 NOTREACHED(); 481 } 482 } 483 bridge_->SetUploadIdentifier(request.httpBody().identifier()); 484 } 485 486 if (sync_load_response) { 487 bridge_->SyncLoad(sync_load_response); 488 return; 489 } 490 491 if (bridge_->Start(this)) { 492 AddRef(); // Balanced in OnCompletedRequest 493 } else { 494 bridge_.reset(); 495 } 496 } 497 498 void WebURLLoaderImpl::Context::OnUploadProgress(uint64 position, uint64 size) { 499 if (client_) 500 client_->didSendData(loader_, position, size); 501 } 502 503 bool WebURLLoaderImpl::Context::OnReceivedRedirect( 504 const GURL& new_url, 505 const ResourceResponseInfo& info, 506 bool* has_new_first_party_for_cookies, 507 GURL* new_first_party_for_cookies) { 508 if (!client_) 509 return false; 510 511 WebURLResponse response; 512 response.initialize(); 513 PopulateURLResponse(request_.url(), info, &response); 514 515 // TODO(darin): We lack sufficient information to construct the actual 516 // request that resulted from the redirect. 517 WebURLRequest new_request(new_url); 518 new_request.setFirstPartyForCookies(request_.firstPartyForCookies()); 519 new_request.setDownloadToFile(request_.downloadToFile()); 520 521 WebString referrer_string = WebString::fromUTF8("Referer"); 522 WebString referrer = request_.httpHeaderField(referrer_string); 523 if (!WebSecurityPolicy::shouldHideReferrer(new_url, referrer)) 524 new_request.setHTTPHeaderField(referrer_string, referrer); 525 526 if (response.httpStatusCode() == 307) 527 new_request.setHTTPMethod(request_.httpMethod()); 528 529 client_->willSendRequest(loader_, new_request, response); 530 request_ = new_request; 531 *has_new_first_party_for_cookies = true; 532 *new_first_party_for_cookies = request_.firstPartyForCookies(); 533 534 // Only follow the redirect if WebKit left the URL unmodified. 535 if (new_url == GURL(new_request.url())) 536 return true; 537 538 // We assume that WebKit only changes the URL to suppress a redirect, and we 539 // assume that it does so by setting it to be invalid. 540 DCHECK(!new_request.url().isValid()); 541 return false; 542 } 543 544 void WebURLLoaderImpl::Context::OnReceivedResponse( 545 const ResourceResponseInfo& info) { 546 if (!client_) 547 return; 548 549 WebURLResponse response; 550 response.initialize(); 551 PopulateURLResponse(request_.url(), info, &response); 552 553 bool show_raw_listing = (GURL(request_.url()).query() == "raw"); 554 555 if (info.mime_type == "text/vnd.chromium.ftp-dir") { 556 if (show_raw_listing) { 557 // Set the MIME type to plain text to prevent any active content. 558 response.setMIMEType("text/plain"); 559 } else { 560 // We're going to produce a parsed listing in HTML. 561 response.setMIMEType("text/html"); 562 } 563 } 564 565 client_->didReceiveResponse(loader_, response); 566 567 // We may have been cancelled after didReceiveResponse, which would leave us 568 // without a client and therefore without much need to do further handling. 569 if (!client_) 570 return; 571 572 DCHECK(!ftp_listing_delegate_.get()); 573 DCHECK(!multipart_delegate_.get()); 574 if (info.headers && info.mime_type == "multipart/x-mixed-replace") { 575 std::string content_type; 576 info.headers->EnumerateHeader(NULL, "content-type", &content_type); 577 578 std::string boundary = net::GetHeaderParamValue( 579 content_type, "boundary", net::QuoteRule::REMOVE_OUTER_QUOTES); 580 TrimString(boundary, " \"", &boundary); 581 582 // If there's no boundary, just handle the request normally. In the gecko 583 // code, nsMultiMixedConv::OnStartRequest throws an exception. 584 if (!boundary.empty()) { 585 multipart_delegate_.reset( 586 new MultipartResponseDelegate(client_, loader_, response, boundary)); 587 } 588 } else if (info.mime_type == "text/vnd.chromium.ftp-dir" && 589 !show_raw_listing) { 590 ftp_listing_delegate_.reset( 591 new FtpDirectoryListingResponseDelegate(client_, loader_, response)); 592 } 593 594 response_url_ = response.url(); 595 } 596 597 void WebURLLoaderImpl::Context::OnDownloadedData(int len) { 598 if (client_) 599 client_->didDownloadData(loader_, len); 600 } 601 602 void WebURLLoaderImpl::Context::OnReceivedData(const char* data, 603 int data_length, 604 int encoded_data_length) { 605 if (!client_) 606 return; 607 608 // Temporary logging, see site_isolation_metrics.h/cc. 609 SiteIsolationMetrics::SniffCrossOriginHTML(response_url_, data, data_length); 610 611 if (ftp_listing_delegate_.get()) { 612 // The FTP listing delegate will make the appropriate calls to 613 // client_->didReceiveData and client_->didReceiveResponse. 614 ftp_listing_delegate_->OnReceivedData(data, data_length); 615 } else if (multipart_delegate_.get()) { 616 // The multipart delegate will make the appropriate calls to 617 // client_->didReceiveData and client_->didReceiveResponse. 618 multipart_delegate_->OnReceivedData(data, data_length, encoded_data_length); 619 } else { 620 client_->didReceiveData(loader_, data, data_length, encoded_data_length); 621 } 622 } 623 624 void WebURLLoaderImpl::Context::OnReceivedCachedMetadata( 625 const char* data, int len) { 626 if (client_) 627 client_->didReceiveCachedMetadata(loader_, data, len); 628 } 629 630 void WebURLLoaderImpl::Context::OnCompletedRequest( 631 const net::URLRequestStatus& status, 632 const std::string& security_info, 633 const base::Time& completion_time) { 634 if (ftp_listing_delegate_.get()) { 635 ftp_listing_delegate_->OnCompletedRequest(); 636 ftp_listing_delegate_.reset(NULL); 637 } else if (multipart_delegate_.get()) { 638 multipart_delegate_->OnCompletedRequest(); 639 multipart_delegate_.reset(NULL); 640 } 641 642 // Prevent any further IPC to the browser now that we're complete, but 643 // don't delete it to keep any downloaded temp files alive. 644 DCHECK(!completed_bridge_.get()); 645 completed_bridge_.swap(bridge_); 646 647 if (client_) { 648 if (status.status() != net::URLRequestStatus::SUCCESS) { 649 int error_code; 650 if (status.status() == net::URLRequestStatus::HANDLED_EXTERNALLY) { 651 // By marking this request as aborted we insure that we don't navigate 652 // to an error page. 653 error_code = net::ERR_ABORTED; 654 } else { 655 error_code = status.os_error(); 656 } 657 WebURLError error; 658 error.domain = WebString::fromUTF8(net::kErrorDomain); 659 error.reason = error_code; 660 error.unreachableURL = request_.url(); 661 client_->didFail(loader_, error); 662 } else { 663 client_->didFinishLoading(loader_, completion_time.ToDoubleT()); 664 } 665 } 666 667 // Temporary logging, see site_isolation_metrics.h/cc 668 SiteIsolationMetrics::RemoveCompletedResponse(response_url_); 669 670 // We are done with the bridge now, and so we need to release the reference 671 // to ourselves that we took on behalf of the bridge. This may cause our 672 // destruction. 673 Release(); 674 } 675 676 bool WebURLLoaderImpl::Context::CanHandleDataURL(const GURL& url) const { 677 DCHECK(url.SchemeIs("data")); 678 679 // Optimize for the case where we can handle a data URL locally. We must 680 // skip this for data URLs targetted at frames since those could trigger a 681 // download. 682 // 683 // NOTE: We special case MIME types we can render both for performance 684 // reasons as well as to support unit tests, which do not have an underlying 685 // ResourceLoaderBridge implementation. 686 687 if (request_.targetType() != WebURLRequest::TargetIsMainFrame && 688 request_.targetType() != WebURLRequest::TargetIsSubframe) 689 return true; 690 691 std::string mime_type, unused_charset; 692 if (net::DataURL::Parse(url, &mime_type, &unused_charset, NULL) && 693 net::IsSupportedMimeType(mime_type)) 694 return true; 695 696 return false; 697 } 698 699 void WebURLLoaderImpl::Context::HandleDataURL() { 700 ResourceResponseInfo info; 701 net::URLRequestStatus status; 702 std::string data; 703 704 if (GetInfoFromDataURL(request_.url(), &info, &data, &status)) { 705 OnReceivedResponse(info); 706 if (!data.empty()) 707 OnReceivedData(data.data(), data.size(), 0); 708 } 709 710 OnCompletedRequest(status, info.security_info, base::Time::Now()); 711 } 712 713 // WebURLLoaderImpl ----------------------------------------------------------- 714 715 WebURLLoaderImpl::WebURLLoaderImpl() 716 : ALLOW_THIS_IN_INITIALIZER_LIST(context_(new Context(this))) { 717 } 718 719 WebURLLoaderImpl::~WebURLLoaderImpl() { 720 cancel(); 721 } 722 723 void WebURLLoaderImpl::loadSynchronously(const WebURLRequest& request, 724 WebURLResponse& response, 725 WebURLError& error, 726 WebData& data) { 727 ResourceLoaderBridge::SyncLoadResponse sync_load_response; 728 context_->Start(request, &sync_load_response); 729 730 const GURL& final_url = sync_load_response.url; 731 732 // TODO(tc): For file loads, we may want to include a more descriptive 733 // status code or status text. 734 const net::URLRequestStatus::Status& status = 735 sync_load_response.status.status(); 736 if (status != net::URLRequestStatus::SUCCESS && 737 status != net::URLRequestStatus::HANDLED_EXTERNALLY) { 738 response.setURL(final_url); 739 error.domain = WebString::fromUTF8(net::kErrorDomain); 740 error.reason = sync_load_response.status.os_error(); 741 error.unreachableURL = final_url; 742 return; 743 } 744 745 PopulateURLResponse(final_url, sync_load_response, &response); 746 747 data.assign(sync_load_response.data.data(), 748 sync_load_response.data.size()); 749 } 750 751 void WebURLLoaderImpl::loadAsynchronously(const WebURLRequest& request, 752 WebURLLoaderClient* client) { 753 DCHECK(!context_->client()); 754 755 context_->set_client(client); 756 context_->Start(request, NULL); 757 } 758 759 void WebURLLoaderImpl::cancel() { 760 context_->Cancel(); 761 } 762 763 void WebURLLoaderImpl::setDefersLoading(bool value) { 764 context_->SetDefersLoading(value); 765 } 766 767 } // namespace webkit_glue 768