diff options
author | phajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-12-10 20:04:35 +0000 |
---|---|---|
committer | phajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-12-10 20:04:35 +0000 |
commit | 92b24c11f42b31158cca43921eac16f33c98839c (patch) | |
tree | 81304558304a29fb589327af857db90294f17fc8 /chrome | |
parent | 8eadc12f4690e648e11b5f84733da84f2f8825a8 (diff) | |
download | chromium_src-92b24c11f42b31158cca43921eac16f33c98839c.zip chromium_src-92b24c11f42b31158cca43921eac16f33c98839c.tar.gz chromium_src-92b24c11f42b31158cca43921eac16f33c98839c.tar.bz2 |
Implement ResourceQueue, an object that makes it easy to delay starting
requests in ResourceDispatcherHost until specified conditions are met.
Make UserScriptListener use ResourceQueue.
This is the first step toward waiting for the privacy blacklists to load.
TEST=Covered by unit_tests.
BUG=21541
Review URL: http://codereview.chromium.org/460108
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@34271 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
19 files changed, 579 insertions, 89 deletions
diff --git a/chrome/browser/extensions/user_script_listener.cc b/chrome/browser/extensions/user_script_listener.cc index 76a4ff5..aec615c 100644 --- a/chrome/browser/extensions/user_script_listener.cc +++ b/chrome/browser/extensions/user_script_listener.cc @@ -7,15 +7,16 @@ #include "chrome/browser/chrome_thread.h" #include "chrome/browser/extensions/extensions_service.h" #include "chrome/browser/profile.h" +#include "chrome/browser/renderer_host/global_request_id.h" #include "chrome/browser/renderer_host/resource_dispatcher_host_request_info.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/notification_service.h" #include "net/url_request/url_request.h" -UserScriptListener::UserScriptListener(ResourceDispatcherHost* rdh) - : resource_dispatcher_host_(rdh), +UserScriptListener::UserScriptListener(ResourceQueue* resource_queue) + : resource_queue_(resource_queue), user_scripts_ready_(false) { - DCHECK(resource_dispatcher_host_); + DCHECK(resource_queue_); registrar_.Add(this, NotificationType::EXTENSION_LOADED, NotificationService::AllSources()); @@ -25,57 +26,49 @@ UserScriptListener::UserScriptListener(ResourceDispatcherHost* rdh) NotificationService::AllSources()); } -bool UserScriptListener::ShouldStartRequest(URLRequest* request) { +bool UserScriptListener::ShouldDelayRequest( + URLRequest* request, + const ResourceDispatcherHostRequestInfo& request_info, + const GlobalRequestID& request_id) { DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); // If it's a frame load, then we need to check the URL against the list of // user scripts to see if we need to wait. - ResourceDispatcherHostRequestInfo* info = - ResourceDispatcherHost::InfoForRequest(request); - DCHECK(info); - - if (info->resource_type() != ResourceType::MAIN_FRAME && - info->resource_type() != ResourceType::SUB_FRAME) { - return true; + if (request_info.resource_type() != ResourceType::MAIN_FRAME && + request_info.resource_type() != ResourceType::SUB_FRAME) { + return false; } if (user_scripts_ready_) - return true; + return false; - // User scripts aren't ready yet. If one of them wants to inject into this - // request, we'll need to wait for it before we can start this request. - bool found_match = false; for (URLPatterns::iterator it = url_patterns_.begin(); it != url_patterns_.end(); ++it) { if ((*it).MatchesUrl(request->url())) { - found_match = true; - break; + // One of the user scripts wants to inject into this request, but the + // script isn't ready yet. Delay the request. + delayed_request_ids_.push_front(request_id); + return true; } } - if (!found_match) - return true; - - // Queue this request up. - delayed_request_ids_.push_front(ResourceDispatcherHost::GlobalRequestID( - info->child_id(), info->request_id())); return false; } +void UserScriptListener::WillShutdownResourceQueue() { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); + resource_queue_ = NULL; +} + void UserScriptListener::StartDelayedRequests() { DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); user_scripts_ready_ = true; - if (resource_dispatcher_host_) { + if (resource_queue_) { for (DelayedRequests::iterator it = delayed_request_ids_.begin(); it != delayed_request_ids_.end(); ++it) { - URLRequest* request = resource_dispatcher_host_->GetURLRequest(*it); - if (request) { - // The request shouldn't have started (SUCCESS is the initial state). - DCHECK(request->status().status() == URLRequestStatus::SUCCESS); - request->Start(); - } + resource_queue_->StartDelayedRequest(this, *it); } } diff --git a/chrome/browser/extensions/user_script_listener.h b/chrome/browser/extensions/user_script_listener.h index 105af4d..f24bd05 100644 --- a/chrome/browser/extensions/user_script_listener.h +++ b/chrome/browser/extensions/user_script_listener.h @@ -8,12 +8,13 @@ #include <list> #include "base/ref_counted.h" -#include "chrome/browser/renderer_host/resource_dispatcher_host.h" +#include "chrome/browser/renderer_host/resource_queue.h" #include "chrome/common/extensions/url_pattern.h" #include "chrome/common/notification_registrar.h" class Extension; class URLRequest; +struct GlobalRequestID; // This class handles delaying of resource loads that depend on unloaded user // scripts. For each request that comes in, we check if it depends on a user @@ -24,16 +25,17 @@ class URLRequest; // updates to loaded extensions. class UserScriptListener : public base::RefCountedThreadSafe<UserScriptListener>, + public ResourceQueueDelegate, public NotificationObserver { public: - explicit UserScriptListener(ResourceDispatcherHost* rdh); + explicit UserScriptListener(ResourceQueue* resource_queue); - void OnResourceDispatcherHostGone() { resource_dispatcher_host_ = NULL; } - - // Returns true if we're ready to service the request. Otherwise, if the - // request URL depends on any user scripts that haven't been loaded yet, we - // will delay the request until we're ready. - bool ShouldStartRequest(URLRequest* request); + // ResourceQueueDelegate: + virtual bool ShouldDelayRequest( + URLRequest* request, + const ResourceDispatcherHostRequestInfo& request_info, + const GlobalRequestID& request_id); + virtual void WillShutdownResourceQueue(); private: friend class base::RefCountedThreadSafe<UserScriptListener>; @@ -53,11 +55,11 @@ class UserScriptListener // deleted, so user_scripts_ready_ remains unchanged. void ReplaceURLPatterns(const URLPatterns& patterns); - ResourceDispatcherHost* resource_dispatcher_host_; + ResourceQueue* resource_queue_; // A list of every request that we delayed. Will be flushed when user scripts // are ready. - typedef std::list<ResourceDispatcherHost::GlobalRequestID> DelayedRequests; + typedef std::list<GlobalRequestID> DelayedRequests; DelayedRequests delayed_request_ids_; // TODO(mpcomplete): the rest of this stuff should really be per-profile, but @@ -83,7 +85,7 @@ class UserScriptListener NotificationRegistrar registrar_; - DISALLOW_EVIL_CONSTRUCTORS(UserScriptListener); + DISALLOW_COPY_AND_ASSIGN(UserScriptListener); }; #endif // CHROME_BROWSER_EXTENSIONS_USER_SCRIPT_LISTENER_H_ diff --git a/chrome/browser/renderer_host/async_resource_handler.cc b/chrome/browser/renderer_host/async_resource_handler.cc index 91a2bb4..7b5e8c2 100644 --- a/chrome/browser/renderer_host/async_resource_handler.cc +++ b/chrome/browser/renderer_host/async_resource_handler.cc @@ -8,6 +8,7 @@ #include "base/process.h" #include "base/shared_memory.h" #include "chrome/browser/net/chrome_url_request_context.h" +#include "chrome/browser/renderer_host/global_request_id.h" #include "chrome/browser/renderer_host/resource_dispatcher_host_request_info.h" #include "chrome/common/render_messages.h" #include "net/base/io_buffer.h" @@ -105,7 +106,7 @@ bool AsyncResourceHandler::OnResponseStarted(int request_id, // avoiding the possibility of zooming the old content or of having to layout // the new content twice. URLRequest* request = rdh_->GetURLRequest( - ResourceDispatcherHost::GlobalRequestID(process_id_, request_id)); + GlobalRequestID(process_id_, request_id)); ResourceDispatcherHostRequestInfo* info = rdh_->InfoForRequest(request); if (info->resource_type() == ResourceType::MAIN_FRAME) { std::string host(request->url().host()); diff --git a/chrome/browser/renderer_host/cross_site_resource_handler.cc b/chrome/browser/renderer_host/cross_site_resource_handler.cc index 158a4f3..e178cf1 100644 --- a/chrome/browser/renderer_host/cross_site_resource_handler.cc +++ b/chrome/browser/renderer_host/cross_site_resource_handler.cc @@ -8,8 +8,10 @@ #include "base/logging.h" #include "chrome/browser/chrome_thread.h" +#include "chrome/browser/renderer_host/global_request_id.h" #include "chrome/browser/renderer_host/render_view_host.h" #include "chrome/browser/renderer_host/render_view_host_delegate.h" +#include "chrome/browser/renderer_host/resource_dispatcher_host.h" #include "chrome/browser/renderer_host/resource_dispatcher_host_request_info.h" #include "net/base/io_buffer.h" @@ -105,8 +107,7 @@ bool CrossSiteResourceHandler::OnResponseStarted(int request_id, has_started_response_ = true; // Look up the request and associated info. - ResourceDispatcherHost::GlobalRequestID global_id(render_process_host_id_, - request_id); + GlobalRequestID global_id(render_process_host_id_, request_id); URLRequest* request = rdh_->GetURLRequest(global_id); if (!request) { DLOG(WARNING) << "Request wasn't found"; @@ -172,8 +173,7 @@ bool CrossSiteResourceHandler::OnResponseCompleted( // so that the error message (e.g., 404) can be displayed to the user. // Also continue with the logic below to remember that we completed // during the cross-site transition. - ResourceDispatcherHost::GlobalRequestID global_id( - render_process_host_id_, request_id); + GlobalRequestID global_id(render_process_host_id_, request_id); StartCrossSiteTransition(request_id, NULL, global_id); } } @@ -197,8 +197,7 @@ void CrossSiteResourceHandler::ResumeResponse() { in_cross_site_transition_ = false; // Find the request for this response. - ResourceDispatcherHost::GlobalRequestID global_id(render_process_host_id_, - request_id_); + GlobalRequestID global_id(render_process_host_id_, request_id_); URLRequest* request = rdh_->GetURLRequest(global_id); if (!request) { DLOG(WARNING) << "Resuming a request that wasn't found"; @@ -238,7 +237,7 @@ void CrossSiteResourceHandler::ResumeResponse() { void CrossSiteResourceHandler::StartCrossSiteTransition( int request_id, ResourceResponse* response, - ResourceDispatcherHost::GlobalRequestID global_id) { + const GlobalRequestID& global_id) { in_cross_site_transition_ = true; request_id_ = request_id; response_ = response; diff --git a/chrome/browser/renderer_host/cross_site_resource_handler.h b/chrome/browser/renderer_host/cross_site_resource_handler.h index 7e916c1..fc6bef2 100644 --- a/chrome/browser/renderer_host/cross_site_resource_handler.h +++ b/chrome/browser/renderer_host/cross_site_resource_handler.h @@ -5,9 +5,11 @@ #ifndef CHROME_BROWSER_RENDERER_HOST_CROSS_SITE_RESOURCE_HANDLER_H_ #define CHROME_BROWSER_RENDERER_HOST_CROSS_SITE_RESOURCE_HANDLER_H_ -#include "chrome/browser/renderer_host/resource_dispatcher_host.h" #include "chrome/browser/renderer_host/resource_handler.h" +class ResourceDispatcherHost; +struct GlobalRequestID; + // Ensures that cross-site responses are delayed until the onunload handler of // the previous page is allowed to run. This handler wraps an // AsyncEventHandler, and it sits inside SafeBrowsing and Buffered event @@ -44,7 +46,7 @@ class CrossSiteResourceHandler : public ResourceHandler { void StartCrossSiteTransition( int request_id, ResourceResponse* response, - ResourceDispatcherHost::GlobalRequestID global_id); + const GlobalRequestID& global_id); scoped_refptr<ResourceHandler> next_handler_; int render_process_host_id_; diff --git a/chrome/browser/renderer_host/download_resource_handler.cc b/chrome/browser/renderer_host/download_resource_handler.cc index f232771..6c64396 100644 --- a/chrome/browser/renderer_host/download_resource_handler.cc +++ b/chrome/browser/renderer_host/download_resource_handler.cc @@ -8,6 +8,7 @@ #include "chrome/browser/chrome_thread.h" #include "chrome/browser/download/download_file.h" #include "chrome/browser/download/download_manager.h" +#include "chrome/browser/renderer_host/global_request_id.h" #include "chrome/browser/renderer_host/resource_dispatcher_host.h" #include "net/base/io_buffer.h" #include "net/url_request/url_request_context.h" @@ -21,8 +22,7 @@ DownloadResourceHandler::DownloadResourceHandler(ResourceDispatcherHost* rdh, URLRequest* request, bool save_as) : download_id_(-1), - global_id_(ResourceDispatcherHost::GlobalRequestID(render_process_host_id, - request_id)), + global_id_(render_process_host_id, request_id), render_view_id_(render_view_id), url_(url), content_length_(0), diff --git a/chrome/browser/renderer_host/download_resource_handler.h b/chrome/browser/renderer_host/download_resource_handler.h index 9237cc5..24251f6 100644 --- a/chrome/browser/renderer_host/download_resource_handler.h +++ b/chrome/browser/renderer_host/download_resource_handler.h @@ -7,9 +7,13 @@ #include <string> -#include "chrome/browser/renderer_host/resource_dispatcher_host.h" +#include "base/timer.h" +#include "chrome/browser/renderer_host/global_request_id.h" #include "chrome/browser/renderer_host/resource_handler.h" +class DownloadFileManager; +class ResourceDispatcherHost; +class URLRequest; struct DownloadBuffer; // Forwards data to the download thread. @@ -57,7 +61,7 @@ class DownloadResourceHandler : public ResourceHandler { void StartPauseTimer(); int download_id_; - ResourceDispatcherHost::GlobalRequestID global_id_; + GlobalRequestID global_id_; int render_view_id_; scoped_refptr<net::IOBuffer> read_buffer_; std::string content_disposition_; diff --git a/chrome/browser/renderer_host/download_throttling_resource_handler.cc b/chrome/browser/renderer_host/download_throttling_resource_handler.cc index b49bc87..8ca251f 100644 --- a/chrome/browser/renderer_host/download_throttling_resource_handler.cc +++ b/chrome/browser/renderer_host/download_throttling_resource_handler.cc @@ -6,6 +6,7 @@ #include "base/logging.h" #include "chrome/browser/renderer_host/download_resource_handler.h" +#include "chrome/browser/renderer_host/resource_dispatcher_host.h" #include "net/base/io_buffer.h" DownloadThrottlingResourceHandler::DownloadThrottlingResourceHandler( diff --git a/chrome/browser/renderer_host/global_request_id.h b/chrome/browser/renderer_host/global_request_id.h new file mode 100644 index 0000000..89f8db5 --- /dev/null +++ b/chrome/browser/renderer_host/global_request_id.h @@ -0,0 +1,31 @@ +// Copyright (c) 2009 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. + +#ifndef CHROME_BROWSER_RENDERER_HOST_GLOBAL_REQUEST_ID_H_ +#define CHROME_BROWSER_RENDERER_HOST_GLOBAL_REQUEST_ID_H_ + +// Uniquely identifies a URLRequest. +struct GlobalRequestID { + GlobalRequestID() : child_id(-1), request_id(-1) { + } + + GlobalRequestID(int child_id, int request_id) + : child_id(child_id), + request_id(request_id) { + } + + // The unique ID of the child process (different from OS's PID). + int child_id; + + // The request ID (unique for the child). + int request_id; + + bool operator<(const GlobalRequestID& other) const { + if (child_id == other.child_id) + return request_id < other.request_id; + return child_id < other.child_id; + } +}; + +#endif // CHROME_BROWSER_RENDERER_HOST_GLOBAL_REQUEST_ID_H_ diff --git a/chrome/browser/renderer_host/resource_dispatcher_host.cc b/chrome/browser/renderer_host/resource_dispatcher_host.cc index 4bce18a..ad999cb 100644 --- a/chrome/browser/renderer_host/resource_dispatcher_host.cc +++ b/chrome/browser/renderer_host/resource_dispatcher_host.cc @@ -34,9 +34,11 @@ #include "chrome/browser/renderer_host/buffered_resource_handler.h" #include "chrome/browser/renderer_host/cross_site_resource_handler.h" #include "chrome/browser/renderer_host/download_resource_handler.h" +#include "chrome/browser/renderer_host/global_request_id.h" #include "chrome/browser/renderer_host/render_view_host.h" #include "chrome/browser/renderer_host/render_view_host_delegate.h" #include "chrome/browser/renderer_host/resource_dispatcher_host_request_info.h" +#include "chrome/browser/renderer_host/resource_queue.h" #include "chrome/browser/renderer_host/resource_request_details.h" #include "chrome/browser/renderer_host/safe_browsing_resource_handler.h" #include "chrome/browser/renderer_host/save_file_resource_handler.h" @@ -247,8 +249,7 @@ ResourceDispatcherHost::ResourceDispatcherHost() download_request_manager_(new DownloadRequestManager()), ALLOW_THIS_IN_INITIALIZER_LIST( save_file_manager_(new SaveFileManager(this))), - ALLOW_THIS_IN_INITIALIZER_LIST(user_script_listener_( - new UserScriptListener(this))), + user_script_listener_(new UserScriptListener(&resource_queue_)), safe_browsing_(new SafeBrowsingService), socket_stream_dispatcher_host_(new SocketStreamDispatcherHost), webkit_thread_(new WebKitThread), @@ -258,6 +259,9 @@ ResourceDispatcherHost::ResourceDispatcherHost() max_outstanding_requests_cost_per_process_( kMaxOutstandingRequestsCostPerProcess), receiver_(NULL) { + ResourceQueue::DelegateSet resource_queue_delegates; + resource_queue_delegates.insert(user_script_listener_.get()); + resource_queue_.Initialize(resource_queue_delegates); } ResourceDispatcherHost::~ResourceDispatcherHost() { @@ -280,8 +284,6 @@ ResourceDispatcherHost::~ResourceDispatcherHost() { iter != ids.end(); ++iter) { CancelBlockedRequestsForRoute(iter->first, iter->second); } - - user_script_listener_->OnResourceDispatcherHostGone(); } void ResourceDispatcherHost::Initialize() { @@ -307,6 +309,7 @@ void ResourceDispatcherHost::SetRequestInfo( void ResourceDispatcherHost::OnShutdown() { DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); is_shutdown_ = true; + resource_queue_.Shutdown(); STLDeleteValues(&pending_requests_); // Make sure we shutdown the timer now, otherwise by the time our destructor // runs if the timer is still running the Task is deleted twice (once by @@ -1002,9 +1005,10 @@ void ResourceDispatcherHost::RemovePendingRequest( IncrementOutstandingRequestsMemoryCost(-1 * info->memory_cost(), info->child_id()); - // Notify the login handler that this request object is going away. + // Notify interested parties that the request object is going away. if (info && info->login_handler()) info->login_handler()->OnRequestCancelled(); + resource_queue_.RemoveRequest(iter->first); delete iter->second; pending_requests_.erase(iter); @@ -1307,12 +1311,7 @@ void ResourceDispatcherHost::BeginRequestInternal(URLRequest* request) { GlobalRequestID global_id(info->child_id(), info->request_id()); pending_requests_[global_id] = request; - if (!user_script_listener_->ShouldStartRequest(request)) { - // This request depends on some user scripts that haven't loaded yet. The - // UserScriptListener will resume the request when they're ready. - return; - } - request->Start(); + resource_queue_.AddRequest(request, *info); // Make sure we have the load state monitor running if (!update_load_states_timer_.IsRunning()) { @@ -1535,7 +1534,7 @@ void ResourceDispatcherHost::RemoveObserver(Observer* obs) { } URLRequest* ResourceDispatcherHost::GetURLRequest( - GlobalRequestID request_id) const { + const GlobalRequestID& request_id) const { // This should be running in the IO loop. DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); diff --git a/chrome/browser/renderer_host/resource_dispatcher_host.h b/chrome/browser/renderer_host/resource_dispatcher_host.h index a48cebe..3dba767 100644 --- a/chrome/browser/renderer_host/resource_dispatcher_host.h +++ b/chrome/browser/renderer_host/resource_dispatcher_host.h @@ -23,6 +23,7 @@ #include "base/timer.h" #include "chrome/common/child_process_info.h" #include "chrome/browser/privacy_blacklist/blocked_response.h" +#include "chrome/browser/renderer_host/resource_queue.h" #include "ipc/ipc_message.h" #include "net/url_request/url_request.h" #include "webkit/glue/resource_type.h" @@ -41,6 +42,7 @@ class SSLClientAuthHandler; class UserScriptListener; class URLRequestContext; class WebKitThread; +struct GlobalRequestID; struct ViewHostMsg_Resource_Request; struct ViewMsg_ClosePage_Params; @@ -86,25 +88,6 @@ class ResourceDispatcherHost : public URLRequest::Delegate { const GURL& new_url) = 0; }; - // Uniquely identifies a URLRequest. - struct GlobalRequestID { - GlobalRequestID() : child_id(-1), request_id(-1) { - } - GlobalRequestID(int child_id, int request_id) - : child_id(child_id), - request_id(request_id) { - } - - int child_id; - int request_id; - - bool operator<(const GlobalRequestID& other) const { - if (child_id == other.child_id) - return request_id < other.request_id; - return child_id < other.child_id; - } - }; - ResourceDispatcherHost(); ~ResourceDispatcherHost(); @@ -249,7 +232,7 @@ class ResourceDispatcherHost : public URLRequest::Delegate { void RemoveObserver(Observer* obs); // Retrieves a URLRequest. Must be called from the IO thread. - URLRequest* GetURLRequest(GlobalRequestID request_id) const; + URLRequest* GetURLRequest(const GlobalRequestID& request_id) const; // Notifies our observers that a request has been cancelled. void NotifyResponseCompleted(URLRequest* request, int process_unique_id); @@ -414,6 +397,9 @@ class ResourceDispatcherHost : public URLRequest::Delegate { // is not empty. base::RepeatingTimer<ResourceDispatcherHost> update_load_states_timer_; + // Handles the resource requests from the moment we want to start them. + ResourceQueue resource_queue_; + // We own the download file writing thread and manager scoped_refptr<DownloadFileManager> download_file_manager_; diff --git a/chrome/browser/renderer_host/resource_dispatcher_host_unittest.cc b/chrome/browser/renderer_host/resource_dispatcher_host_unittest.cc index 4feed71..7650d9c 100644 --- a/chrome/browser/renderer_host/resource_dispatcher_host_unittest.cc +++ b/chrome/browser/renderer_host/resource_dispatcher_host_unittest.cc @@ -148,6 +148,7 @@ class ResourceDispatcherHostTest : public testing::Test, public: ResourceDispatcherHostTest() : Receiver(ChildProcessInfo::RENDER_PROCESS, -1), + ui_thread_(ChromeThread::UI, &message_loop_), io_thread_(ChromeThread::IO, &message_loop_), old_factory_(NULL) { set_handle(base::GetCurrentProcessHandle()); @@ -175,6 +176,7 @@ class ResourceDispatcherHostTest : public testing::Test, &ResourceDispatcherHostTest::Factory); EnsureTestSchemeIsAllowed(); } + virtual void TearDown() { URLRequest::RegisterProtocolFactory("test", NULL); if (!scheme_.empty()) @@ -245,6 +247,7 @@ class ResourceDispatcherHostTest : public testing::Test, } MessageLoopForIO message_loop_; + ChromeThread ui_thread_; ChromeThread io_thread_; ResourceDispatcherHost host_; ResourceIPCAccumulator accum_; diff --git a/chrome/browser/renderer_host/resource_queue.cc b/chrome/browser/renderer_host/resource_queue.cc new file mode 100644 index 0000000..409f6b9 --- /dev/null +++ b/chrome/browser/renderer_host/resource_queue.cc @@ -0,0 +1,92 @@ +// Copyright (c) 2009 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 "chrome/browser/renderer_host/resource_queue.h" + +#include "base/stl_util-inl.h" +#include "chrome/browser/chrome_thread.h" +#include "chrome/browser/renderer_host/global_request_id.h" +#include "chrome/browser/renderer_host/resource_dispatcher_host_request_info.h" + +ResourceQueueDelegate::~ResourceQueueDelegate() { +} + +ResourceQueue::ResourceQueue() : shutdown_(false) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); +} + +ResourceQueue::~ResourceQueue() { + // TODO(phajdan.jr): Add DCHECK(shutdown_) here when unit tests stop abusing + // ResourceDispatcherHost by not shutting it down in tests. +} + +void ResourceQueue::Initialize(const DelegateSet& delegates) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + DCHECK(delegates_.empty()); + delegates_ = delegates; +} + +void ResourceQueue::Shutdown() { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); + + shutdown_ = true; + for (DelegateSet::iterator i = delegates_.begin(); + i != delegates_.end(); ++i) { + (*i)->WillShutdownResourceQueue(); + } +} + +void ResourceQueue::AddRequest( + URLRequest* request, + const ResourceDispatcherHostRequestInfo& request_info) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); + DCHECK(!shutdown_); + + GlobalRequestID request_id(request_info.child_id(), + request_info.request_id()); + + DCHECK(!ContainsKey(requests_, request_id)); + requests_[request_id] = request; + + DelegateSet interested_delegates; + + for (DelegateSet::iterator i = delegates_.begin(); + i != delegates_.end(); ++i) { + if ((*i)->ShouldDelayRequest(request, request_info, request_id)) + interested_delegates.insert(*i); + } + + if (interested_delegates.empty()) { + request->Start(); + return; + } + + DCHECK(!ContainsKey(interested_delegates_, request_id)); + interested_delegates_[request_id] = interested_delegates; +} + +void ResourceQueue::RemoveRequest(const GlobalRequestID& request_id) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); + requests_.erase(request_id); +} + +void ResourceQueue::StartDelayedRequest(ResourceQueueDelegate* delegate, + const GlobalRequestID& request_id) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); + DCHECK(!shutdown_); + + DCHECK(ContainsKey(interested_delegates_, request_id)); + DCHECK(ContainsKey(interested_delegates_[request_id], delegate)); + interested_delegates_[request_id].erase(delegate); + if (interested_delegates_[request_id].empty()) { + interested_delegates_.erase(request_id); + + if (ContainsKey(requests_, request_id)) { + URLRequest* request = requests_[request_id]; + // The request shouldn't have started (SUCCESS is the initial state). + DCHECK_EQ(URLRequestStatus::SUCCESS, request->status().status()); + request->Start(); + } + } +} diff --git a/chrome/browser/renderer_host/resource_queue.h b/chrome/browser/renderer_host/resource_queue.h new file mode 100644 index 0000000..a853d56 --- /dev/null +++ b/chrome/browser/renderer_host/resource_queue.h @@ -0,0 +1,96 @@ +// Copyright (c) 2009 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. + +#ifndef CHROME_BROWSER_RENDERER_HOST_RESOURCE_QUEUE_H_ +#define CHROME_BROWSER_RENDERER_HOST_RESOURCE_QUEUE_H_ + +#include <map> +#include <set> + +#include "base/basictypes.h" + +class ResourceDispatcherHostRequestInfo; +class URLRequest; +struct GlobalRequestID; + +// Makes decisions about delaying or not each URLRequest in the queue. +// All methods are called on the IO thread. +class ResourceQueueDelegate { + public: + // Should return true if it wants the |request| to not be started at this + // point. To start the delayed request, ResourceQueue::StartDelayedRequest + // should be used. + virtual bool ShouldDelayRequest( + URLRequest* request, + const ResourceDispatcherHostRequestInfo& request_info, + const GlobalRequestID& request_id) = 0; + + // Called just before ResourceQueue shutdown. After that, the delegate + // should not use the ResourceQueue. + virtual void WillShutdownResourceQueue() = 0; + + protected: + virtual ~ResourceQueueDelegate(); +}; + +// Makes it easy to delay starting URL requests until specified conditions are +// met. +class ResourceQueue { + public: + typedef std::set<ResourceQueueDelegate*> DelegateSet; + + // UI THREAD ONLY ------------------------------------------------------------ + + // Construct the queue. You must initialize it using Initialize. + ResourceQueue(); + ~ResourceQueue(); + + // Initialize the queue with set of delegates it should ask for each incoming + // request. + void Initialize(const DelegateSet& delegates); + + // IO THREAD ONLY ------------------------------------------------------------ + + // Must be called before destroying the queue. No other methods can be called + // after that. + void Shutdown(); + + // Takes care to start the |request| after all delegates allow that. If no + // delegate demands delaying the request it will be started immediately. + void AddRequest(URLRequest* request, + const ResourceDispatcherHostRequestInfo& request_info); + + // Tells the queue that the URLRequest object associated with |request_id| + // is no longer valid. + void RemoveRequest(const GlobalRequestID& request_id); + + // A delegate should call StartDelayedRequest when it wants to allow the + // request to start. If it was the last delegate that demanded the request + // to be delayed, the request will be started. + void StartDelayedRequest(ResourceQueueDelegate* delegate, + const GlobalRequestID& request_id); + + private: + typedef std::map<GlobalRequestID, URLRequest*> RequestMap; + typedef std::map<GlobalRequestID, DelegateSet> InterestedDelegatesMap; + + // The registered delegates. Will not change after the queue has been + // initialized. + DelegateSet delegates_; + + // Stores URLRequest objects associated with each GlobalRequestID. This helps + // decoupling the queue from ResourceDispatcherHost. + RequestMap requests_; + + // Maps a GlobalRequestID to the set of delegates that want to prevent the + // associated request from starting yet. + InterestedDelegatesMap interested_delegates_; + + // True when we are shutting down. + bool shutdown_; + + DISALLOW_COPY_AND_ASSIGN(ResourceQueue); +}; + +#endif // CHROME_BROWSER_RENDERER_HOST_RESOURCE_QUEUE_H_ diff --git a/chrome/browser/renderer_host/resource_queue_unittest.cc b/chrome/browser/renderer_host/resource_queue_unittest.cc new file mode 100644 index 0000000..4cc202c --- /dev/null +++ b/chrome/browser/renderer_host/resource_queue_unittest.cc @@ -0,0 +1,275 @@ +// Copyright (c) 2009 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 "base/message_loop.h" +#include "chrome/browser/chrome_thread.h" +#include "chrome/browser/renderer_host/global_request_id.h" +#include "chrome/browser/renderer_host/resource_dispatcher_host_request_info.h" +#include "chrome/browser/renderer_host/resource_handler.h" +#include "chrome/browser/renderer_host/resource_queue.h" +#include "googleurl/src/gurl.h" +#include "net/url_request/url_request.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +const char kTestUrl[] = "data:text/plain,Hello World!"; + +class DummyResourceHandler : public ResourceHandler { + public: + DummyResourceHandler() { + } + + virtual bool OnRequestRedirected(int request_id, const GURL& url, + ResourceResponse* response, + bool* defer) { + NOTREACHED(); + return true; + } + + virtual bool OnResponseStarted(int request_id, + ResourceResponse* response) { + NOTREACHED(); + return true; + } + + virtual bool OnWillRead(int request_id, + net::IOBuffer** buf, + int* buf_size, + int min_size) { + NOTREACHED(); + return true; + } + + virtual bool OnReadCompleted(int request_id, int* bytes_read) { + NOTREACHED(); + return true; + } + + virtual bool OnResponseCompleted(int request_id, + const URLRequestStatus& status, + const std::string& security_info) { + NOTREACHED(); + return true; + } + + private: + DISALLOW_COPY_AND_ASSIGN(DummyResourceHandler); +}; + +ResourceDispatcherHostRequestInfo* GetRequestInfo(int request_id) { + return new ResourceDispatcherHostRequestInfo( + new DummyResourceHandler(), ChildProcessInfo::RENDER_PROCESS, 0, 0, + request_id, "null", "null", ResourceType::MAIN_FRAME, + 0, false, false, -1, -1); +} + +void InitializeQueue(ResourceQueue* queue, ResourceQueueDelegate* delegate) { + ResourceQueue::DelegateSet delegate_set; + delegate_set.insert(delegate); + queue->Initialize(delegate_set); +} + +void InitializeQueue(ResourceQueue* queue, + ResourceQueueDelegate* delegate1, + ResourceQueueDelegate* delegate2) { + ResourceQueue::DelegateSet delegate_set; + delegate_set.insert(delegate1); + delegate_set.insert(delegate2); + queue->Initialize(delegate_set); +} + +class NeverDelayingDelegate : public ResourceQueueDelegate { + public: + NeverDelayingDelegate() { + } + + virtual bool ShouldDelayRequest( + URLRequest* request, + const ResourceDispatcherHostRequestInfo& request_info, + const GlobalRequestID& request_id) { + return false; + } + + virtual void WillShutdownResourceQueue() { + } + + private: + DISALLOW_COPY_AND_ASSIGN(NeverDelayingDelegate); +}; + +class AlwaysDelayingDelegate : public ResourceQueueDelegate { + public: + AlwaysDelayingDelegate(ResourceQueue* resource_queue) + : resource_queue_(resource_queue) { + } + + virtual bool ShouldDelayRequest( + URLRequest* request, + const ResourceDispatcherHostRequestInfo& request_info, + const GlobalRequestID& request_id) { + delayed_requests_.push_back(request_id); + return true; + } + + virtual void WillShutdownResourceQueue() { + resource_queue_ = NULL; + } + + void StartDelayedRequests() { + if (!resource_queue_) + return; + + for (RequestList::iterator i = delayed_requests_.begin(); + i != delayed_requests_.end(); ++i) { + resource_queue_->StartDelayedRequest(this, *i); + } + } + + private: + typedef std::vector<GlobalRequestID> RequestList; + + ResourceQueue* resource_queue_; + + RequestList delayed_requests_; + + DISALLOW_COPY_AND_ASSIGN(AlwaysDelayingDelegate); +}; + +class ResourceQueueTest : public testing::Test, public URLRequest::Delegate { + public: + ResourceQueueTest() + : response_started_count_(0), + message_loop_(MessageLoop::TYPE_IO), + ui_thread_(ChromeThread::UI, &message_loop_), + io_thread_(ChromeThread::IO, &message_loop_) { + } + + virtual void OnResponseStarted(URLRequest* request) { + response_started_count_++; + // We're not going to do anything more with the request. Cancel it now + // to avoid leaking URLRequestJob. + request->Cancel(); + } + + virtual void OnReadCompleted(URLRequest* request, int bytes_read) { + } + + protected: + int response_started_count_; + + private: + MessageLoop message_loop_; + ChromeThread ui_thread_; + ChromeThread io_thread_; +}; + +TEST_F(ResourceQueueTest, Basic) { + // Test the simplest lifycycle of ResourceQueue. + ResourceQueue queue; + queue.Initialize(ResourceQueue::DelegateSet()); + queue.Shutdown(); +} + +TEST_F(ResourceQueueTest, NeverDelayingDelegate) { + ResourceQueue queue; + + NeverDelayingDelegate delegate; + InitializeQueue(&queue, &delegate); + + URLRequest request(GURL(kTestUrl), this); + scoped_ptr<ResourceDispatcherHostRequestInfo> request_info(GetRequestInfo(0)); + EXPECT_EQ(0, response_started_count_); + queue.AddRequest(&request, *request_info.get()); + MessageLoop::current()->RunAllPending(); + EXPECT_EQ(1, response_started_count_); + + queue.Shutdown(); +} + +TEST_F(ResourceQueueTest, AlwaysDelayingDelegate) { + ResourceQueue queue; + + AlwaysDelayingDelegate delegate(&queue); + InitializeQueue(&queue, &delegate); + + URLRequest request(GURL(kTestUrl), this); + scoped_ptr<ResourceDispatcherHostRequestInfo> request_info(GetRequestInfo(0)); + EXPECT_EQ(0, response_started_count_); + queue.AddRequest(&request, *request_info.get()); + MessageLoop::current()->RunAllPending(); + EXPECT_EQ(0, response_started_count_); + delegate.StartDelayedRequests(); + MessageLoop::current()->RunAllPending(); + EXPECT_EQ(1, response_started_count_); + + queue.Shutdown(); +} + +TEST_F(ResourceQueueTest, AlwaysDelayingDelegateAfterShutdown) { + ResourceQueue queue; + + AlwaysDelayingDelegate delegate(&queue); + InitializeQueue(&queue, &delegate); + + URLRequest request(GURL(kTestUrl), this); + scoped_ptr<ResourceDispatcherHostRequestInfo> request_info(GetRequestInfo(0)); + EXPECT_EQ(0, response_started_count_); + queue.AddRequest(&request, *request_info.get()); + MessageLoop::current()->RunAllPending(); + EXPECT_EQ(0, response_started_count_); + + queue.Shutdown(); + + delegate.StartDelayedRequests(); + MessageLoop::current()->RunAllPending(); + EXPECT_EQ(0, response_started_count_); +} + +TEST_F(ResourceQueueTest, TwoDelegates) { + ResourceQueue queue; + + AlwaysDelayingDelegate always_delaying_delegate(&queue); + NeverDelayingDelegate never_delaying_delegate; + InitializeQueue(&queue, &always_delaying_delegate, &never_delaying_delegate); + + URLRequest request(GURL(kTestUrl), this); + scoped_ptr<ResourceDispatcherHostRequestInfo> request_info(GetRequestInfo(0)); + EXPECT_EQ(0, response_started_count_); + queue.AddRequest(&request, *request_info.get()); + MessageLoop::current()->RunAllPending(); + EXPECT_EQ(0, response_started_count_); + always_delaying_delegate.StartDelayedRequests(); + MessageLoop::current()->RunAllPending(); + EXPECT_EQ(1, response_started_count_); + + queue.Shutdown(); +} + +TEST_F(ResourceQueueTest, RemoveRequest) { + ResourceQueue queue; + + AlwaysDelayingDelegate delegate(&queue); + InitializeQueue(&queue, &delegate); + + URLRequest request(GURL(kTestUrl), this); + scoped_ptr<ResourceDispatcherHostRequestInfo> request_info(GetRequestInfo(0)); + GlobalRequestID request_id(request_info->child_id(), + request_info->request_id()); + EXPECT_EQ(0, response_started_count_); + queue.AddRequest(&request, *request_info.get()); + MessageLoop::current()->RunAllPending(); + EXPECT_EQ(0, response_started_count_); + queue.RemoveRequest(request_id); + delegate.StartDelayedRequests(); + MessageLoop::current()->RunAllPending(); + EXPECT_EQ(0, response_started_count_); + + queue.Shutdown(); + + MessageLoop::current()->RunAllPending(); + EXPECT_EQ(0, response_started_count_); +} + +} // namespace diff --git a/chrome/browser/ssl/ssl_cert_error_handler.cc b/chrome/browser/ssl/ssl_cert_error_handler.cc index 1bb0c4d..d7dddf1 100644 --- a/chrome/browser/ssl/ssl_cert_error_handler.cc +++ b/chrome/browser/ssl/ssl_cert_error_handler.cc @@ -4,6 +4,7 @@ #include "chrome/browser/ssl/ssl_cert_error_handler.h" +#include "chrome/browser/renderer_host/resource_dispatcher_host.h" #include "chrome/browser/ssl/ssl_manager.h" #include "chrome/browser/ssl/ssl_policy.h" diff --git a/chrome/browser/ssl/ssl_error_handler.h b/chrome/browser/ssl/ssl_error_handler.h index cc80234..2589f5d 100644 --- a/chrome/browser/ssl/ssl_error_handler.h +++ b/chrome/browser/ssl/ssl_error_handler.h @@ -9,12 +9,13 @@ #include "base/basictypes.h" #include "base/ref_counted.h" +#include "chrome/browser/renderer_host/global_request_id.h" #include "chrome/browser/ssl/ssl_manager.h" -#include "chrome/browser/renderer_host/resource_dispatcher_host.h" #include "chrome/common/filter_policy.h" #include "googleurl/src/gurl.h" #include "webkit/glue/resource_type.h" +class ResourceDispatcherHost; class SSLCertErrorHandler; class TabContents; class URLRequest; @@ -113,7 +114,7 @@ class SSLErrorHandler : public base::RefCountedThreadSafe<SSLErrorHandler> { // The id of the URLRequest associated with this object. // Should only be accessed from the IO thread. - ResourceDispatcherHost::GlobalRequestID request_id_; + GlobalRequestID request_id_; // The ResourceDispatcherHost we are associated with. ResourceDispatcherHost* resource_dispatcher_host_; diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 240574d..3252a3f 100755 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1280,6 +1280,7 @@ 'browser/renderer_host/download_resource_handler.h', 'browser/renderer_host/download_throttling_resource_handler.cc', 'browser/renderer_host/download_throttling_resource_handler.h', + 'browser/renderer_host/global_request_id.h', 'browser/renderer_host/gtk_im_context_wrapper.cc', 'browser/renderer_host/gtk_im_context_wrapper.h', 'browser/renderer_host/gtk_key_bindings_handler.cc', @@ -1315,6 +1316,8 @@ 'browser/renderer_host/resource_message_filter_gtk.cc', 'browser/renderer_host/resource_message_filter_mac.mm', 'browser/renderer_host/resource_message_filter_win.cc', + 'browser/renderer_host/resource_queue.cc', + 'browser/renderer_host/resource_queue.h', 'browser/renderer_host/resource_request_details.h', 'browser/renderer_host/safe_browsing_resource_handler.cc', 'browser/renderer_host/safe_browsing_resource_handler.h', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 882de59..777f441 100755 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -738,6 +738,7 @@ 'browser/renderer_host/audio_renderer_host_unittest.cc', 'browser/renderer_host/render_widget_host_unittest.cc', 'browser/renderer_host/resource_dispatcher_host_unittest.cc', + 'browser/renderer_host/resource_queue_unittest.cc', 'browser/renderer_host/test/render_view_host_unittest.cc', 'browser/renderer_host/test/site_instance_unittest.cc', 'browser/renderer_host/web_cache_manager_unittest.cc', |