| // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "content/browser/loader/throttling_resource_handler.h" | 
 |  | 
 | #include "content/browser/loader/resource_request_info_impl.h" | 
 | #include "content/public/browser/resource_throttle.h" | 
 | #include "content/public/common/resource_response.h" | 
 | #include "net/url_request/url_request.h" | 
 |  | 
 | namespace content { | 
 |  | 
 | ThrottlingResourceHandler::ThrottlingResourceHandler( | 
 |     scoped_ptr<ResourceHandler> next_handler, | 
 |     net::URLRequest* request, | 
 |     ScopedVector<ResourceThrottle> throttles) | 
 |     : LayeredResourceHandler(request, next_handler.Pass()), | 
 |       deferred_stage_(DEFERRED_NONE), | 
 |       throttles_(throttles.Pass()), | 
 |       next_index_(0), | 
 |       cancelled_by_resource_throttle_(false) { | 
 |   for (size_t i = 0; i < throttles_.size(); ++i) { | 
 |     throttles_[i]->set_controller(this); | 
 |     // Throttles must have a name, as otherwise, bugs where a throttle fails | 
 |     // to resume a request can be very difficult to debug. | 
 |     DCHECK(throttles_[i]->GetNameForLogging()); | 
 |   } | 
 | } | 
 |  | 
 | ThrottlingResourceHandler::~ThrottlingResourceHandler() { | 
 | } | 
 |  | 
 | bool ThrottlingResourceHandler::OnRequestRedirected( | 
 |     const net::RedirectInfo& redirect_info, | 
 |     ResourceResponse* response, | 
 |     bool* defer) { | 
 |   DCHECK(!cancelled_by_resource_throttle_); | 
 |  | 
 |   *defer = false; | 
 |   while (next_index_ < throttles_.size()) { | 
 |     int index = next_index_; | 
 |     throttles_[index]->WillRedirectRequest(redirect_info.new_url, defer); | 
 |     next_index_++; | 
 |     if (cancelled_by_resource_throttle_) | 
 |       return false; | 
 |     if (*defer) { | 
 |       OnRequestDefered(index); | 
 |       deferred_stage_ = DEFERRED_REDIRECT; | 
 |       deferred_redirect_ = redirect_info; | 
 |       deferred_response_ = response; | 
 |       return true;  // Do not cancel. | 
 |     } | 
 |   } | 
 |  | 
 |   next_index_ = 0;  // Reset for next time. | 
 |  | 
 |   return next_handler_->OnRequestRedirected(redirect_info, response, defer); | 
 | } | 
 |  | 
 | bool ThrottlingResourceHandler::OnWillStart(const GURL& url, bool* defer) { | 
 |   DCHECK(!cancelled_by_resource_throttle_); | 
 |  | 
 |   *defer = false; | 
 |   while (next_index_ < throttles_.size()) { | 
 |     int index = next_index_; | 
 |     throttles_[index]->WillStartRequest(defer); | 
 |     next_index_++; | 
 |     if (cancelled_by_resource_throttle_) | 
 |       return false; | 
 |     if (*defer) { | 
 |       OnRequestDefered(index); | 
 |       deferred_stage_ = DEFERRED_START; | 
 |       deferred_url_ = url; | 
 |       return true;  // Do not cancel. | 
 |     } | 
 |   } | 
 |  | 
 |   next_index_ = 0;  // Reset for next time. | 
 |  | 
 |   return next_handler_->OnWillStart(url, defer); | 
 | } | 
 |  | 
 | bool ThrottlingResourceHandler::OnBeforeNetworkStart(const GURL& url, | 
 |                                                      bool* defer) { | 
 |   DCHECK(!cancelled_by_resource_throttle_); | 
 |  | 
 |   *defer = false; | 
 |   while (next_index_ < throttles_.size()) { | 
 |     int index = next_index_; | 
 |     throttles_[index]->WillStartUsingNetwork(defer); | 
 |     next_index_++; | 
 |     if (cancelled_by_resource_throttle_) | 
 |       return false; | 
 |     if (*defer) { | 
 |       OnRequestDefered(index); | 
 |       deferred_stage_ = DEFERRED_NETWORK_START; | 
 |       deferred_url_ = url; | 
 |       return true;  // Do not cancel. | 
 |     } | 
 |   } | 
 |  | 
 |   next_index_ = 0;  // Reset for next time. | 
 |  | 
 |   return next_handler_->OnBeforeNetworkStart(url, defer); | 
 | } | 
 |  | 
 | bool ThrottlingResourceHandler::OnResponseStarted(ResourceResponse* response, | 
 |                                                   bool* defer) { | 
 |   DCHECK(!cancelled_by_resource_throttle_); | 
 |  | 
 |   while (next_index_ < throttles_.size()) { | 
 |     int index = next_index_; | 
 |     throttles_[index]->WillProcessResponse(defer); | 
 |     next_index_++; | 
 |     if (cancelled_by_resource_throttle_) | 
 |       return false; | 
 |     if (*defer) { | 
 |       OnRequestDefered(index); | 
 |       deferred_stage_ = DEFERRED_RESPONSE; | 
 |       deferred_response_ = response; | 
 |       return true;  // Do not cancel. | 
 |     } | 
 |   } | 
 |  | 
 |   next_index_ = 0;  // Reset for next time. | 
 |  | 
 |   return next_handler_->OnResponseStarted(response, defer); | 
 | } | 
 |  | 
 | void ThrottlingResourceHandler::Cancel() { | 
 |   cancelled_by_resource_throttle_ = true; | 
 |   controller()->Cancel(); | 
 | } | 
 |  | 
 | void ThrottlingResourceHandler::CancelAndIgnore() { | 
 |   cancelled_by_resource_throttle_ = true; | 
 |   controller()->CancelAndIgnore(); | 
 | } | 
 |  | 
 | void ThrottlingResourceHandler::CancelWithError(int error_code) { | 
 |   cancelled_by_resource_throttle_ = true; | 
 |   controller()->CancelWithError(error_code); | 
 | } | 
 |  | 
 | void ThrottlingResourceHandler::Resume() { | 
 |   DCHECK(!cancelled_by_resource_throttle_); | 
 |  | 
 |   DeferredStage last_deferred_stage = deferred_stage_; | 
 |   deferred_stage_ = DEFERRED_NONE; | 
 |   // Clear information about the throttle that delayed the request. | 
 |   request()->LogUnblocked(); | 
 |   switch (last_deferred_stage) { | 
 |     case DEFERRED_NONE: | 
 |       NOTREACHED(); | 
 |       break; | 
 |     case DEFERRED_START: | 
 |       ResumeStart(); | 
 |       break; | 
 |     case DEFERRED_NETWORK_START: | 
 |       ResumeNetworkStart(); | 
 |       break; | 
 |     case DEFERRED_REDIRECT: | 
 |       ResumeRedirect(); | 
 |       break; | 
 |     case DEFERRED_RESPONSE: | 
 |       ResumeResponse(); | 
 |       break; | 
 |   } | 
 | } | 
 |  | 
 | void ThrottlingResourceHandler::ResumeStart() { | 
 |   DCHECK(!cancelled_by_resource_throttle_); | 
 |  | 
 |   GURL url = deferred_url_; | 
 |   deferred_url_ = GURL(); | 
 |  | 
 |   bool defer = false; | 
 |   if (!OnWillStart(url, &defer)) { | 
 |     controller()->Cancel(); | 
 |   } else if (!defer) { | 
 |     controller()->Resume(); | 
 |   } | 
 | } | 
 |  | 
 | void ThrottlingResourceHandler::ResumeNetworkStart() { | 
 |   DCHECK(!cancelled_by_resource_throttle_); | 
 |  | 
 |   GURL url = deferred_url_; | 
 |   deferred_url_ = GURL(); | 
 |  | 
 |   bool defer = false; | 
 |   if (!OnBeforeNetworkStart(url, &defer)) { | 
 |     controller()->Cancel(); | 
 |   } else if (!defer) { | 
 |     controller()->Resume(); | 
 |   } | 
 | } | 
 |  | 
 | void ThrottlingResourceHandler::ResumeRedirect() { | 
 |   DCHECK(!cancelled_by_resource_throttle_); | 
 |  | 
 |   net::RedirectInfo redirect_info = deferred_redirect_; | 
 |   deferred_redirect_ = net::RedirectInfo(); | 
 |   scoped_refptr<ResourceResponse> response; | 
 |   deferred_response_.swap(response); | 
 |  | 
 |   bool defer = false; | 
 |   if (!OnRequestRedirected(redirect_info, response.get(), &defer)) { | 
 |     controller()->Cancel(); | 
 |   } else if (!defer) { | 
 |     controller()->Resume(); | 
 |   } | 
 | } | 
 |  | 
 | void ThrottlingResourceHandler::ResumeResponse() { | 
 |   DCHECK(!cancelled_by_resource_throttle_); | 
 |  | 
 |   scoped_refptr<ResourceResponse> response; | 
 |   deferred_response_.swap(response); | 
 |  | 
 |   bool defer = false; | 
 |   if (!OnResponseStarted(response.get(), &defer)) { | 
 |     controller()->Cancel(); | 
 |   } else if (!defer) { | 
 |     controller()->Resume(); | 
 |   } | 
 | } | 
 |  | 
 | void ThrottlingResourceHandler::OnRequestDefered(int throttle_index) { | 
 |   request()->LogBlockedBy(throttles_[throttle_index]->GetNameForLogging()); | 
 | } | 
 |  | 
 | }  // namespace content |