Home | History | Annotate | Download | only in loader
      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