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 #include "content/browser/loader/resource_loader.h" 6 7 #include "base/command_line.h" 8 #include "base/message_loop/message_loop.h" 9 #include "base/metrics/histogram.h" 10 #include "base/time/time.h" 11 #include "content/browser/child_process_security_policy_impl.h" 12 #include "content/browser/loader/doomed_resource_handler.h" 13 #include "content/browser/loader/resource_loader_delegate.h" 14 #include "content/browser/loader/resource_request_info_impl.h" 15 #include "content/browser/ssl/ssl_client_auth_handler.h" 16 #include "content/browser/ssl/ssl_manager.h" 17 #include "content/common/ssl_status_serialization.h" 18 #include "content/public/browser/cert_store.h" 19 #include "content/public/browser/resource_dispatcher_host_login_delegate.h" 20 #include "content/public/browser/site_instance.h" 21 #include "content/public/common/content_client.h" 22 #include "content/public/common/content_switches.h" 23 #include "content/public/common/process_type.h" 24 #include "content/public/common/resource_response.h" 25 #include "content/public/common/url_constants.h" 26 #include "net/base/load_flags.h" 27 #include "net/http/http_response_headers.h" 28 #include "net/ssl/client_cert_store.h" 29 #include "net/ssl/client_cert_store_impl.h" 30 #include "webkit/browser/appcache/appcache_interceptor.h" 31 32 using base::TimeDelta; 33 using base::TimeTicks; 34 35 namespace content { 36 namespace { 37 38 void PopulateResourceResponse(net::URLRequest* request, 39 ResourceResponse* response) { 40 response->head.error_code = request->status().error(); 41 response->head.request_time = request->request_time(); 42 response->head.response_time = request->response_time(); 43 response->head.headers = request->response_headers(); 44 request->GetCharset(&response->head.charset); 45 response->head.content_length = request->GetExpectedContentSize(); 46 request->GetMimeType(&response->head.mime_type); 47 net::HttpResponseInfo response_info = request->response_info(); 48 response->head.was_fetched_via_spdy = response_info.was_fetched_via_spdy; 49 response->head.was_npn_negotiated = response_info.was_npn_negotiated; 50 response->head.npn_negotiated_protocol = 51 response_info.npn_negotiated_protocol; 52 response->head.connection_info = response_info.connection_info; 53 response->head.was_fetched_via_proxy = request->was_fetched_via_proxy(); 54 response->head.socket_address = request->GetSocketAddress(); 55 appcache::AppCacheInterceptor::GetExtraResponseInfo( 56 request, 57 &response->head.appcache_id, 58 &response->head.appcache_manifest_url); 59 // TODO(mmenke): Figure out if LOAD_ENABLE_LOAD_TIMING is safe to remove. 60 if (request->load_flags() & net::LOAD_ENABLE_LOAD_TIMING) 61 request->GetLoadTimingInfo(&response->head.load_timing); 62 } 63 64 } // namespace 65 66 ResourceLoader::ResourceLoader(scoped_ptr<net::URLRequest> request, 67 scoped_ptr<ResourceHandler> handler, 68 ResourceLoaderDelegate* delegate) 69 : weak_ptr_factory_(this) { 70 scoped_ptr<net::ClientCertStore> client_cert_store; 71 #if !defined(USE_OPENSSL) 72 client_cert_store.reset(new net::ClientCertStoreImpl()); 73 #endif 74 Init(request.Pass(), handler.Pass(), delegate, client_cert_store.Pass()); 75 } 76 77 ResourceLoader::~ResourceLoader() { 78 if (login_delegate_.get()) 79 login_delegate_->OnRequestCancelled(); 80 if (ssl_client_auth_handler_.get()) 81 ssl_client_auth_handler_->OnRequestCancelled(); 82 83 // Run ResourceHandler destructor before we tear-down the rest of our state 84 // as the ResourceHandler may want to inspect the URLRequest and other state. 85 handler_.reset(); 86 } 87 88 void ResourceLoader::StartRequest() { 89 if (delegate_->HandleExternalProtocol(this, request_->url())) { 90 CancelAndIgnore(); 91 return; 92 } 93 94 // Give the handler a chance to delay the URLRequest from being started. 95 bool defer_start = false; 96 if (!handler_->OnWillStart(GetRequestInfo()->GetRequestID(), request_->url(), 97 &defer_start)) { 98 Cancel(); 99 return; 100 } 101 102 if (defer_start) { 103 deferred_stage_ = DEFERRED_START; 104 } else { 105 StartRequestInternal(); 106 } 107 } 108 109 void ResourceLoader::CancelRequest(bool from_renderer) { 110 CancelRequestInternal(net::ERR_ABORTED, from_renderer); 111 } 112 113 void ResourceLoader::CancelAndIgnore() { 114 ResourceRequestInfoImpl* info = GetRequestInfo(); 115 info->set_was_ignored_by_handler(true); 116 CancelRequest(false); 117 } 118 119 void ResourceLoader::CancelWithError(int error_code) { 120 CancelRequestInternal(error_code, false); 121 } 122 123 void ResourceLoader::ReportUploadProgress() { 124 ResourceRequestInfoImpl* info = GetRequestInfo(); 125 126 if (waiting_for_upload_progress_ack_) 127 return; // Send one progress event at a time. 128 129 net::UploadProgress progress = request_->GetUploadProgress(); 130 if (!progress.size()) 131 return; // Nothing to upload. 132 133 if (progress.position() == last_upload_position_) 134 return; // No progress made since last time. 135 136 const uint64 kHalfPercentIncrements = 200; 137 const TimeDelta kOneSecond = TimeDelta::FromMilliseconds(1000); 138 139 uint64 amt_since_last = progress.position() - last_upload_position_; 140 TimeDelta time_since_last = TimeTicks::Now() - last_upload_ticks_; 141 142 bool is_finished = (progress.size() == progress.position()); 143 bool enough_new_progress = 144 (amt_since_last > (progress.size() / kHalfPercentIncrements)); 145 bool too_much_time_passed = time_since_last > kOneSecond; 146 147 if (is_finished || enough_new_progress || too_much_time_passed) { 148 if (request_->load_flags() & net::LOAD_ENABLE_UPLOAD_PROGRESS) { 149 handler_->OnUploadProgress( 150 info->GetRequestID(), progress.position(), progress.size()); 151 waiting_for_upload_progress_ack_ = true; 152 } 153 last_upload_ticks_ = TimeTicks::Now(); 154 last_upload_position_ = progress.position(); 155 } 156 } 157 158 void ResourceLoader::MarkAsTransferring(const GURL& target_url) { 159 CHECK_EQ(GetRequestInfo()->GetResourceType(), ResourceType::MAIN_FRAME) 160 << "Cannot transfer non-main frame navigations"; 161 is_transferring_ = true; 162 163 // When transferring a request to another process, the renderer doesn't get 164 // a chance to update the cookie policy URL. Do it here instead. 165 request()->set_first_party_for_cookies(target_url); 166 167 // When an URLRequest is transferred to a new RenderViewHost, its 168 // ResourceHandler should not receive any notifications because it may depend 169 // on the state of the old RVH. We set a ResourceHandler that only allows 170 // canceling requests, because on shutdown of the RDH all pending requests 171 // are canceled. The RVH of requests that are being transferred may be gone 172 // by that time. In CompleteTransfer, the ResoureHandlers are substituted 173 // again. 174 handler_.reset(new DoomedResourceHandler(handler_.Pass())); 175 } 176 177 void ResourceLoader::WillCompleteTransfer() { 178 handler_.reset(); 179 } 180 181 void ResourceLoader::CompleteTransfer(scoped_ptr<ResourceHandler> new_handler) { 182 DCHECK_EQ(DEFERRED_REDIRECT, deferred_stage_); 183 DCHECK(!handler_.get()); 184 185 handler_ = new_handler.Pass(); 186 handler_->SetController(this); 187 is_transferring_ = false; 188 189 Resume(); 190 } 191 192 ResourceRequestInfoImpl* ResourceLoader::GetRequestInfo() { 193 return ResourceRequestInfoImpl::ForRequest(request_.get()); 194 } 195 196 void ResourceLoader::ClearLoginDelegate() { 197 login_delegate_ = NULL; 198 } 199 200 void ResourceLoader::ClearSSLClientAuthHandler() { 201 ssl_client_auth_handler_ = NULL; 202 } 203 204 void ResourceLoader::OnUploadProgressACK() { 205 waiting_for_upload_progress_ack_ = false; 206 } 207 208 ResourceLoader::ResourceLoader( 209 scoped_ptr<net::URLRequest> request, 210 scoped_ptr<ResourceHandler> handler, 211 ResourceLoaderDelegate* delegate, 212 scoped_ptr<net::ClientCertStore> client_cert_store) 213 : weak_ptr_factory_(this) { 214 Init(request.Pass(), handler.Pass(), delegate, client_cert_store.Pass()); 215 } 216 217 void ResourceLoader::Init(scoped_ptr<net::URLRequest> request, 218 scoped_ptr<ResourceHandler> handler, 219 ResourceLoaderDelegate* delegate, 220 scoped_ptr<net::ClientCertStore> client_cert_store) { 221 deferred_stage_ = DEFERRED_NONE; 222 request_ = request.Pass(); 223 handler_ = handler.Pass(); 224 delegate_ = delegate; 225 last_upload_position_ = 0; 226 waiting_for_upload_progress_ack_ = false; 227 is_transferring_ = false; 228 client_cert_store_ = client_cert_store.Pass(); 229 230 request_->set_delegate(this); 231 handler_->SetController(this); 232 } 233 234 void ResourceLoader::OnReceivedRedirect(net::URLRequest* unused, 235 const GURL& new_url, 236 bool* defer) { 237 DCHECK_EQ(request_.get(), unused); 238 239 VLOG(1) << "OnReceivedRedirect: " << request_->url().spec(); 240 DCHECK(request_->status().is_success()); 241 242 ResourceRequestInfoImpl* info = GetRequestInfo(); 243 244 if (info->process_type() != PROCESS_TYPE_PLUGIN && 245 !ChildProcessSecurityPolicyImpl::GetInstance()-> 246 CanRequestURL(info->GetChildID(), new_url)) { 247 VLOG(1) << "Denied unauthorized request for " 248 << new_url.possibly_invalid_spec(); 249 250 // Tell the renderer that this request was disallowed. 251 Cancel(); 252 return; 253 } 254 255 delegate_->DidReceiveRedirect(this, new_url); 256 257 if (delegate_->HandleExternalProtocol(this, new_url)) { 258 // The request is complete so we can remove it. 259 CancelAndIgnore(); 260 return; 261 } 262 263 scoped_refptr<ResourceResponse> response(new ResourceResponse()); 264 PopulateResourceResponse(request_.get(), response.get()); 265 266 if (!handler_->OnRequestRedirected( 267 info->GetRequestID(), new_url, response.get(), defer)) { 268 Cancel(); 269 } else if (*defer) { 270 deferred_stage_ = DEFERRED_REDIRECT; // Follow redirect when resumed. 271 } 272 } 273 274 void ResourceLoader::OnAuthRequired(net::URLRequest* unused, 275 net::AuthChallengeInfo* auth_info) { 276 DCHECK_EQ(request_.get(), unused); 277 278 if (request_->load_flags() & net::LOAD_DO_NOT_PROMPT_FOR_LOGIN) { 279 request_->CancelAuth(); 280 return; 281 } 282 283 if (!delegate_->AcceptAuthRequest(this, auth_info)) { 284 request_->CancelAuth(); 285 return; 286 } 287 288 // Create a login dialog on the UI thread to get authentication data, or pull 289 // from cache and continue on the IO thread. 290 291 DCHECK(!login_delegate_.get()) 292 << "OnAuthRequired called with login_delegate pending"; 293 login_delegate_ = delegate_->CreateLoginDelegate(this, auth_info); 294 if (!login_delegate_.get()) 295 request_->CancelAuth(); 296 } 297 298 void ResourceLoader::OnCertificateRequested( 299 net::URLRequest* unused, 300 net::SSLCertRequestInfo* cert_info) { 301 DCHECK_EQ(request_.get(), unused); 302 303 if (!delegate_->AcceptSSLClientCertificateRequest(this, cert_info)) { 304 request_->Cancel(); 305 return; 306 } 307 308 #if !defined(USE_OPENSSL) 309 client_cert_store_->GetClientCerts(*cert_info, &cert_info->client_certs); 310 if (cert_info->client_certs.empty()) { 311 // No need to query the user if there are no certs to choose from. 312 request_->ContinueWithCertificate(NULL); 313 return; 314 } 315 #endif 316 317 DCHECK(!ssl_client_auth_handler_.get()) 318 << "OnCertificateRequested called with ssl_client_auth_handler pending"; 319 ssl_client_auth_handler_ = new SSLClientAuthHandler(request_.get(), 320 cert_info); 321 ssl_client_auth_handler_->SelectCertificate(); 322 } 323 324 void ResourceLoader::OnSSLCertificateError(net::URLRequest* request, 325 const net::SSLInfo& ssl_info, 326 bool fatal) { 327 ResourceRequestInfoImpl* info = GetRequestInfo(); 328 329 int render_process_id; 330 int render_view_id; 331 if (!info->GetAssociatedRenderView(&render_process_id, &render_view_id)) 332 NOTREACHED(); 333 334 SSLManager::OnSSLCertificateError( 335 weak_ptr_factory_.GetWeakPtr(), 336 info->GetGlobalRequestID(), 337 info->GetResourceType(), 338 request_->url(), 339 render_process_id, 340 render_view_id, 341 ssl_info, 342 fatal); 343 } 344 345 void ResourceLoader::OnResponseStarted(net::URLRequest* unused) { 346 DCHECK_EQ(request_.get(), unused); 347 348 VLOG(1) << "OnResponseStarted: " << request_->url().spec(); 349 350 // The CanLoadPage check should take place after any server redirects have 351 // finished, at the point in time that we know a page will commit in the 352 // renderer process. 353 ResourceRequestInfoImpl* info = GetRequestInfo(); 354 ChildProcessSecurityPolicyImpl* policy = 355 ChildProcessSecurityPolicyImpl::GetInstance(); 356 if (!policy->CanLoadPage(info->GetChildID(), 357 request_->url(), 358 info->GetResourceType())) { 359 Cancel(); 360 return; 361 } 362 363 if (!request_->status().is_success()) { 364 ResponseCompleted(); 365 return; 366 } 367 368 // We want to send a final upload progress message prior to sending the 369 // response complete message even if we're waiting for an ack to to a 370 // previous upload progress message. 371 waiting_for_upload_progress_ack_ = false; 372 ReportUploadProgress(); 373 374 CompleteResponseStarted(); 375 376 if (is_deferred()) 377 return; 378 379 if (request_->status().is_success()) { 380 StartReading(false); // Read the first chunk. 381 } else { 382 ResponseCompleted(); 383 } 384 } 385 386 void ResourceLoader::OnReadCompleted(net::URLRequest* unused, int bytes_read) { 387 DCHECK_EQ(request_.get(), unused); 388 VLOG(1) << "OnReadCompleted: \"" << request_->url().spec() << "\"" 389 << " bytes_read = " << bytes_read; 390 391 // bytes_read == -1 always implies an error. 392 if (bytes_read == -1 || !request_->status().is_success()) { 393 ResponseCompleted(); 394 return; 395 } 396 397 CompleteRead(bytes_read); 398 399 if (is_deferred()) 400 return; 401 402 if (request_->status().is_success() && bytes_read > 0) { 403 StartReading(true); // Read the next chunk. 404 } else { 405 ResponseCompleted(); 406 } 407 } 408 409 void ResourceLoader::CancelSSLRequest(const GlobalRequestID& id, 410 int error, 411 const net::SSLInfo* ssl_info) { 412 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 413 414 // The request can be NULL if it was cancelled by the renderer (as the 415 // request of the user navigating to a new page from the location bar). 416 if (!request_->is_pending()) 417 return; 418 DVLOG(1) << "CancelSSLRequest() url: " << request_->url().spec(); 419 420 if (ssl_info) { 421 request_->CancelWithSSLError(error, *ssl_info); 422 } else { 423 request_->CancelWithError(error); 424 } 425 } 426 427 void ResourceLoader::ContinueSSLRequest(const GlobalRequestID& id) { 428 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 429 430 DVLOG(1) << "ContinueSSLRequest() url: " << request_->url().spec(); 431 432 request_->ContinueDespiteLastError(); 433 } 434 435 void ResourceLoader::Resume() { 436 DCHECK(!is_transferring_); 437 438 DeferredStage stage = deferred_stage_; 439 deferred_stage_ = DEFERRED_NONE; 440 switch (stage) { 441 case DEFERRED_NONE: 442 NOTREACHED(); 443 break; 444 case DEFERRED_START: 445 StartRequestInternal(); 446 break; 447 case DEFERRED_REDIRECT: 448 request_->FollowDeferredRedirect(); 449 break; 450 case DEFERRED_READ: 451 base::MessageLoop::current()->PostTask( 452 FROM_HERE, 453 base::Bind(&ResourceLoader::ResumeReading, 454 weak_ptr_factory_.GetWeakPtr())); 455 break; 456 case DEFERRED_FINISH: 457 // Delay self-destruction since we don't know how we were reached. 458 base::MessageLoop::current()->PostTask( 459 FROM_HERE, 460 base::Bind(&ResourceLoader::CallDidFinishLoading, 461 weak_ptr_factory_.GetWeakPtr())); 462 break; 463 } 464 } 465 466 void ResourceLoader::Cancel() { 467 CancelRequest(false); 468 } 469 470 void ResourceLoader::StartRequestInternal() { 471 DCHECK(!request_->is_pending()); 472 request_->Start(); 473 474 delegate_->DidStartRequest(this); 475 } 476 477 void ResourceLoader::CancelRequestInternal(int error, bool from_renderer) { 478 VLOG(1) << "CancelRequestInternal: " << request_->url().spec(); 479 480 ResourceRequestInfoImpl* info = GetRequestInfo(); 481 482 // WebKit will send us a cancel for downloads since it no longer handles 483 // them. In this case, ignore the cancel since we handle downloads in the 484 // browser. 485 if (from_renderer && (info->is_download() || info->is_stream())) 486 return; 487 488 // TODO(darin): Perhaps we should really be looking to see if the status is 489 // IO_PENDING? 490 bool was_pending = request_->is_pending(); 491 492 if (login_delegate_.get()) { 493 login_delegate_->OnRequestCancelled(); 494 login_delegate_ = NULL; 495 } 496 if (ssl_client_auth_handler_.get()) { 497 ssl_client_auth_handler_->OnRequestCancelled(); 498 ssl_client_auth_handler_ = NULL; 499 } 500 501 request_->CancelWithError(error); 502 503 if (!was_pending) { 504 // If the request isn't in flight, then we won't get an asynchronous 505 // notification from the request, so we have to signal ourselves to finish 506 // this request. 507 base::MessageLoop::current()->PostTask( 508 FROM_HERE, 509 base::Bind(&ResourceLoader::ResponseCompleted, 510 weak_ptr_factory_.GetWeakPtr())); 511 } 512 } 513 514 void ResourceLoader::CompleteResponseStarted() { 515 ResourceRequestInfoImpl* info = GetRequestInfo(); 516 517 scoped_refptr<ResourceResponse> response(new ResourceResponse()); 518 PopulateResourceResponse(request_.get(), response.get()); 519 520 // The --site-per-process flag enables an out-of-process iframes 521 // prototype. It works by changing the MIME type of cross-site subframe 522 // responses to a Chrome specific one. This new type causes the subframe 523 // to be replaced by a <webview> tag with the same URL, which results in 524 // using a renderer in a different process. 525 // 526 // For prototyping purposes, we will use a small hack to ensure same site 527 // iframes are not changed. We can compare the URL for the subframe 528 // request with the referrer. If the two don't match, then it should be a 529 // cross-site iframe. 530 // Also, we don't do the MIME type change for chrome:// URLs, as those 531 // require different privileges and are not allowed in regular renderers. 532 // 533 // The usage of SiteInstance::IsSameWebSite is safe on the IO thread, 534 // if the browser_context parameter is NULL. This does not work for hosted 535 // apps, but should be fine for prototyping. 536 // TODO(nasko): Once the SiteInstance check is fixed, ensure we do the 537 // right thing here. http://crbug.com/160576 538 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 539 if (command_line.HasSwitch(switches::kSitePerProcess) && 540 GetRequestInfo()->GetResourceType() == ResourceType::SUB_FRAME && 541 response->head.mime_type == "text/html" && 542 !request_->url().SchemeIs(chrome::kChromeUIScheme) && 543 !SiteInstance::IsSameWebSite(NULL, request_->url(), 544 GURL(request_->referrer()))) { 545 response->head.mime_type = "application/browser-plugin"; 546 } 547 548 if (request_->ssl_info().cert.get()) { 549 int cert_id = CertStore::GetInstance()->StoreCert( 550 request_->ssl_info().cert.get(), info->GetChildID()); 551 response->head.security_info = SerializeSecurityInfo( 552 cert_id, 553 request_->ssl_info().cert_status, 554 request_->ssl_info().security_bits, 555 request_->ssl_info().connection_status); 556 } else { 557 // We should not have any SSL state. 558 DCHECK(!request_->ssl_info().cert_status && 559 request_->ssl_info().security_bits == -1 && 560 !request_->ssl_info().connection_status); 561 } 562 563 delegate_->DidReceiveResponse(this); 564 565 bool defer = false; 566 if (!handler_->OnResponseStarted( 567 info->GetRequestID(), response.get(), &defer)) { 568 Cancel(); 569 } else if (defer) { 570 read_deferral_start_time_ = base::TimeTicks::Now(); 571 deferred_stage_ = DEFERRED_READ; // Read first chunk when resumed. 572 } 573 } 574 575 void ResourceLoader::StartReading(bool is_continuation) { 576 int bytes_read = 0; 577 ReadMore(&bytes_read); 578 579 // If IO is pending, wait for the URLRequest to call OnReadCompleted. 580 if (request_->status().is_io_pending()) 581 return; 582 583 if (!is_continuation || bytes_read <= 0) { 584 OnReadCompleted(request_.get(), bytes_read); 585 } else { 586 // Else, trigger OnReadCompleted asynchronously to avoid starving the IO 587 // thread in case the URLRequest can provide data synchronously. 588 base::MessageLoop::current()->PostTask( 589 FROM_HERE, 590 base::Bind(&ResourceLoader::OnReadCompleted, 591 weak_ptr_factory_.GetWeakPtr(), 592 request_.get(), 593 bytes_read)); 594 } 595 } 596 597 void ResourceLoader::ResumeReading() { 598 DCHECK(!is_deferred()); 599 600 if (!read_deferral_start_time_.is_null()) { 601 UMA_HISTOGRAM_TIMES("Net.ResourceLoader.ReadDeferral", 602 base::TimeTicks::Now() - read_deferral_start_time_); 603 read_deferral_start_time_ = base::TimeTicks(); 604 } 605 if (request_->status().is_success()) { 606 StartReading(false); // Read the next chunk (OK to complete synchronously). 607 } else { 608 ResponseCompleted(); 609 } 610 } 611 612 void ResourceLoader::ReadMore(int* bytes_read) { 613 ResourceRequestInfoImpl* info = GetRequestInfo(); 614 DCHECK(!is_deferred()); 615 616 net::IOBuffer* buf; 617 int buf_size; 618 if (!handler_->OnWillRead(info->GetRequestID(), &buf, &buf_size, -1)) { 619 Cancel(); 620 return; 621 } 622 623 DCHECK(buf); 624 DCHECK(buf_size > 0); 625 626 request_->Read(buf, buf_size, bytes_read); 627 628 // No need to check the return value here as we'll detect errors by 629 // inspecting the URLRequest's status. 630 } 631 632 void ResourceLoader::CompleteRead(int bytes_read) { 633 DCHECK(bytes_read >= 0); 634 DCHECK(request_->status().is_success()); 635 636 ResourceRequestInfoImpl* info = GetRequestInfo(); 637 638 bool defer = false; 639 if (!handler_->OnReadCompleted(info->GetRequestID(), bytes_read, &defer)) { 640 Cancel(); 641 } else if (defer) { 642 deferred_stage_ = DEFERRED_READ; // Read next chunk when resumed. 643 } 644 } 645 646 void ResourceLoader::ResponseCompleted() { 647 VLOG(1) << "ResponseCompleted: " << request_->url().spec(); 648 ResourceRequestInfoImpl* info = GetRequestInfo(); 649 650 std::string security_info; 651 const net::SSLInfo& ssl_info = request_->ssl_info(); 652 if (ssl_info.cert.get() != NULL) { 653 int cert_id = CertStore::GetInstance()->StoreCert(ssl_info.cert.get(), 654 info->GetChildID()); 655 security_info = SerializeSecurityInfo( 656 cert_id, ssl_info.cert_status, ssl_info.security_bits, 657 ssl_info.connection_status); 658 } 659 660 if (handler_->OnResponseCompleted(info->GetRequestID(), request_->status(), 661 security_info)) { 662 // This will result in our destruction. 663 CallDidFinishLoading(); 664 } else { 665 // The handler is not ready to die yet. We will call DidFinishLoading when 666 // we resume. 667 deferred_stage_ = DEFERRED_FINISH; 668 } 669 } 670 671 void ResourceLoader::CallDidFinishLoading() { 672 delegate_->DidFinishLoading(this); 673 } 674 675 } // namespace content 676