diff options
-rw-r--r-- | chrome/browser/interstitial_page.cc | 147 | ||||
-rw-r--r-- | chrome/browser/interstitial_page.h | 11 | ||||
-rw-r--r-- | chrome/browser/renderer_host/resource_dispatcher_host.cc | 103 | ||||
-rw-r--r-- | chrome/browser/renderer_host/resource_dispatcher_host.h | 46 | ||||
-rw-r--r-- | chrome/browser/renderer_host/resource_dispatcher_host_unittest.cc | 166 |
5 files changed, 435 insertions, 38 deletions
diff --git a/chrome/browser/interstitial_page.cc b/chrome/browser/interstitial_page.cc index 514e822..fb69929 100644 --- a/chrome/browser/interstitial_page.cc +++ b/chrome/browser/interstitial_page.cc @@ -17,6 +17,55 @@ #include "chrome/views/window_delegate.h" #include "net/base/escape.h" +enum ResourceRequestAction { + BLOCK, + RESUME, + CANCEL +}; + +namespace { + +class ResourceRequestTask : public Task { + public: + ResourceRequestTask(RenderViewHost* render_view_host, + ResourceRequestAction action) + : action_(action), + process_id_(render_view_host->process()->host_id()), + render_view_host_id_(render_view_host->routing_id()), + resource_dispatcher_host_( + g_browser_process->resource_dispatcher_host()) { + } + + virtual void Run() { + switch (action_) { + case BLOCK: + resource_dispatcher_host_->BlockRequestsForRenderView( + process_id_, render_view_host_id_); + break; + case RESUME: + resource_dispatcher_host_->ResumeBlockedRequestsForRenderView( + process_id_, render_view_host_id_); + break; + case CANCEL: + resource_dispatcher_host_->CancelBlockedRequestsForRenderView( + process_id_, render_view_host_id_); + break; + default: + NOTREACHED(); + } + } + + private: + ResourceRequestAction action_; + int process_id_; + int render_view_host_id_; + ResourceDispatcherHost* resource_dispatcher_host_; + + DISALLOW_COPY_AND_ASSIGN(ResourceRequestTask); +}; + +} // namespace + // static InterstitialPage::InterstitialPageMap* InterstitialPage::tab_to_interstitial_page_ = NULL; @@ -30,7 +79,8 @@ InterstitialPage::InterstitialPage(WebContents* tab, enabled_(true), new_navigation_(new_navigation), render_view_host_(NULL), - should_revert_tab_title_(false) { + should_revert_tab_title_(false), + ui_loop_(MessageLoop::current()) { InitInterstitialPageMap(); // It would be inconsistent to create an interstitial with no new navigation // (which is the case when the interstitial was triggered by a sub-resource on @@ -51,6 +101,16 @@ void InterstitialPage::Show() { if (tab_->interstitial_page()) tab_->interstitial_page()->DontProceed(); + // Block the resource requests for the render view host while it is hidden. + TakeActionOnResourceDispatcher(BLOCK); + // We need to be notified when the RenderViewHost is destroyed so we can + // cancel the blocked requests. We cannot do that on + // NOTIFY_TAB_CONTENTS_DESTROYED as at that point the RenderViewHost has + // already been destroyed. + notification_registrar_.Add( + this, NOTIFY_RENDER_WIDGET_HOST_DESTROYED, + Source<RenderWidgetHost>(tab_->render_view_host())); + // Update the tab_to_interstitial_page_ map. InterstitialPageMap::const_iterator iter = tab_to_interstitial_page_->find(tab_); @@ -101,25 +161,38 @@ void InterstitialPage::Hide() { void InterstitialPage::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { - if (type == NOTIFY_NAV_ENTRY_PENDING) { - // We are navigating away from the interstitial. Make sure clicking on the - // interstitial will have no effect. - Disable(); - return; - } - DCHECK(type == NOTIFY_TAB_CONTENTS_DESTROYED || - type == NOTIFY_NAV_ENTRY_COMMITTED); - if (!action_taken_) { - // We are navigating away from the interstitial or closing a tab with an - // interstitial. Default to DontProceed(). We don't just call Hide as - // subclasses will almost certainly override DontProceed to do some work - // (ex: close pending connections). - DontProceed(); - } else { - // User decided to proceed and either the navigation was committed or the - // tab was closed before that. - Hide(); - // WARNING: we are now deleted! + switch (type) { + case NOTIFY_NAV_ENTRY_PENDING: + // We are navigating away from the interstitial. Make sure clicking on + // the interstitial will have no effect. + Disable(); + break; + case NOTIFY_RENDER_WIDGET_HOST_DESTROYED: + if (!action_taken_) { + // The RenderViewHost is being destroyed (as part of the tab being + // closed), make sure we clear the blocked requests. + DCHECK(Source<RenderViewHost>(source).ptr() == + tab_->render_view_host()); + TakeActionOnResourceDispatcher(CANCEL); + } + break; + case NOTIFY_TAB_CONTENTS_DESTROYED: + case NOTIFY_NAV_ENTRY_COMMITTED: + if (!action_taken_) { + // We are navigating away from the interstitial or closing a tab with an + // interstitial. Default to DontProceed(). We don't just call Hide as + // subclasses will almost certainly override DontProceed to do some work + // (ex: close pending connections). + DontProceed(); + } else { + // User decided to proceed and either the navigation was committed or + // the tab was closed before that. + Hide(); + // WARNING: we are now deleted! + } + break; + default: + NOTREACHED(); } } @@ -150,6 +223,15 @@ void InterstitialPage::Proceed() { // Resumes the throbber. tab_->SetIsLoading(true, NULL); + // If this is a new navigation, the old page is going away, so we cancel any + // blocked requests for it. If it is not a new navigation, then it means the + // interstitial was shown as a result of a resource loading in the page. + // Since the user wants to proceed, we'll let any blocked request go through. + if (new_navigation_) + TakeActionOnResourceDispatcher(CANCEL); + else + TakeActionOnResourceDispatcher(RESUME); + // No need to hide if we are a new navigation, we'll get hidden when the // navigation is committed. if (!new_navigation_) { @@ -163,6 +245,16 @@ void InterstitialPage::DontProceed() { Disable(); action_taken_ = true; + // If this is a new navigation, we are returning to the original page, so we + // resume blocked requests for it. If it is not a new navigation, then it + // means the interstitial was shown as a result of a resource loading in the + // page and we won't return to the original page, so we cancel blocked + // requests in that case. + if (new_navigation_) + TakeActionOnResourceDispatcher(RESUME); + else + TakeActionOnResourceDispatcher(CANCEL); + if (new_navigation_) { // Since no navigation happens we have to discard the transient entry // explicitely. Note that by calling DiscardNonCommittedEntries() we also @@ -236,6 +328,21 @@ void InterstitialPage::Disable() { enabled_ = false; } +void InterstitialPage::TakeActionOnResourceDispatcher( + ResourceRequestAction action) { + DCHECK(MessageLoop::current() == ui_loop_) << + "TakeActionOnResourceDispatcher should be called on the main thread."; + // The tab might not have a render_view_host if it was closed (in which case, + // we have taken care of the blocked requests when processing + // NOTIFY_RENDER_WIDGET_HOST_DESTROYED. + // Also we need to test there is an IO thread, as when unit-tests we don't + // have one. + if (tab_->render_view_host() && g_browser_process->io_thread()) { + g_browser_process->io_thread()->message_loop()->PostTask( + FROM_HERE, new ResourceRequestTask(tab_->render_view_host(), action)); + } +} + // static void InterstitialPage::InitInterstitialPageMap() { if (!tab_to_interstitial_page_) diff --git a/chrome/browser/interstitial_page.h b/chrome/browser/interstitial_page.h index 92674c1..6c2c76f5 100644 --- a/chrome/browser/interstitial_page.h +++ b/chrome/browser/interstitial_page.h @@ -26,6 +26,8 @@ class WebContents; // through a navigation, the WebContents closing them or the tab containing them // being closed. +enum ResourceRequestAction; + class InterstitialPage : public NotificationObserver, public RenderViewHostDelegate { public: @@ -34,7 +36,7 @@ class InterstitialPage : public NotificationObserver, // case a temporary navigation entry is created with the URL |url| and // added to the navigation controller (so the interstitial page appears as a // new navigation entry). |new_navigation| should be false when the - // interstitial was triggered by a loading a sub-resource in a page. + // interstitial was triggered by a loading a sub-resource in a page. InterstitialPage(WebContents* tab, bool new_navigation, const GURL& url); virtual ~InterstitialPage(); @@ -117,6 +119,11 @@ class InterstitialPage : public NotificationObserver, // - any command sent by the RenderViewHost will be ignored. void Disable(); + // Executes the passed action on the ResourceDispatcher (on the IO thread). + // Used to block/resume/cancel requests for the RenderViewHost hidden by this + // interstitial. + void TakeActionOnResourceDispatcher(ResourceRequestAction action); + // The tab in which we are displayed. WebContents* tab_; @@ -147,6 +154,8 @@ class InterstitialPage : public NotificationObserver, // interstitial is hidden. std::wstring original_tab_title_; + MessageLoop* ui_loop_; + // We keep a map of the various blocking pages shown as the UI tests need to // be able to retrieve them. typedef std::map<WebContents*,InterstitialPage*> InterstitialPageMap; diff --git a/chrome/browser/renderer_host/resource_dispatcher_host.cc b/chrome/browser/renderer_host/resource_dispatcher_host.cc index f746429..c591ee6 100644 --- a/chrome/browser/renderer_host/resource_dispatcher_host.cc +++ b/chrome/browser/renderer_host/resource_dispatcher_host.cc @@ -136,6 +136,23 @@ ResourceDispatcherHost::ResourceDispatcherHost(MessageLoop* io_loop) ResourceDispatcherHost::~ResourceDispatcherHost() { AsyncResourceHandler::GlobalCleanup(); STLDeleteValues(&pending_requests_); + + // Clear blocked requests if any left. + // Note that we have to do this in 2 passes as we cannot call + // CancelBlockedRequestsForRenderView while iterating over + // blocked_requests_map_, as it modifies it. + std::set<ProcessRendererIDs> ids; + for (BlockedRequestMap::const_iterator iter = blocked_requests_map_.begin(); + iter != blocked_requests_map_.end(); ++iter) { + std::pair<std::set<ProcessRendererIDs>::iterator, bool> result = + ids.insert(iter->first); + // We should not have duplicates. + DCHECK(result.second); + } + for (std::set<ProcessRendererIDs>::const_iterator iter = ids.begin(); + iter != ids.end(); ++iter) { + CancelBlockedRequestsForRenderView(iter->first, iter->second); + } } void ResourceDispatcherHost::Initialize() { @@ -611,6 +628,31 @@ void ResourceDispatcherHost::CancelRequestsForRenderView( if (iter != pending_requests_.end()) RemovePendingRequest(iter); } + + // Now deal with blocked requests if any. + if (render_view_id != -1) { + if (blocked_requests_map_.find(std::pair<int, int>(render_process_host_id, + render_view_id)) != + blocked_requests_map_.end()) { + CancelBlockedRequestsForRenderView(render_process_host_id, + render_view_id); + } + } else { + // We have to do all render views for the process |render_process_host_id|. + // Note that we have to do this in 2 passes as we cannot call + // CancelBlockedRequestsForRenderView while iterating over + // blocked_requests_map_, as it modifies it. + std::set<int> render_view_ids; + for (BlockedRequestMap::const_iterator iter = blocked_requests_map_.begin(); + iter != blocked_requests_map_.end(); ++iter) { + if (iter->first.first == render_process_host_id) + render_view_ids.insert(iter->first.second); + } + for (std::set<int>::const_iterator iter = render_view_ids.begin(); + iter != render_view_ids.end(); ++iter) { + CancelBlockedRequestsForRenderView(render_process_host_id, *iter); + } + } } // Cancels the request and removes it from the list. @@ -779,6 +821,16 @@ bool ResourceDispatcherHost::CompleteResponseStarted(URLRequest* request) { void ResourceDispatcherHost::BeginRequestInternal(URLRequest* request, bool mixed_content) { ExtraRequestInfo* info = ExtraInfoForRequest(request); + + std::pair<int, int> pair_id(info->render_process_host_id, + info->render_view_id); + BlockedRequestMap::const_iterator iter = blocked_requests_map_.find(pair_id); + if (iter != blocked_requests_map_.end()) { + // The request should be blocked. + iter->second->push_back(BlockedRequest(request, mixed_content)); + return; + } + GlobalRequestID global_id(info->render_process_host_id, info->request_id); pending_requests_[global_id] = request; if (mixed_content) { @@ -1215,3 +1267,54 @@ void ResourceDispatcherHost::MaybeUpdateUploadProgress(ExtraRequestInfo *info, info->last_upload_position = position; } } + +void ResourceDispatcherHost::BlockRequestsForRenderView( + int render_process_host_id, + int render_view_id) { + std::pair<int, int> key(render_process_host_id, render_view_id); + DCHECK(blocked_requests_map_.find(key) == blocked_requests_map_.end()) << + "BlockRequestsForRenderView called multiple time for the same RVH"; + blocked_requests_map_[key] = new BlockedRequestsList(); +} + +void ResourceDispatcherHost::ResumeBlockedRequestsForRenderView( + int render_process_host_id, + int render_view_id) { + ProcessBlockedRequestsForRenderView(render_process_host_id, + render_view_id, false); +} + +void ResourceDispatcherHost::CancelBlockedRequestsForRenderView( + int render_process_host_id, + int render_view_id) { + ProcessBlockedRequestsForRenderView(render_process_host_id, + render_view_id, true); +} + +void ResourceDispatcherHost::ProcessBlockedRequestsForRenderView( + int render_process_host_id, + int render_view_id, + bool cancel_requests) { + BlockedRequestMap::iterator iter = + blocked_requests_map_.find(std::pair<int, int>(render_process_host_id, + render_view_id)); + if (iter == blocked_requests_map_.end()) { + NOTREACHED(); + return; + } + + BlockedRequestsList* requests = iter->second; + + // Removing the vector from the map unblocks any subsequent requests. + blocked_requests_map_.erase(iter); + + for (BlockedRequestsList::iterator req_iter = requests->begin(); + req_iter != requests->end(); ++req_iter) { + if (cancel_requests) + delete req_iter->url_request; + else + BeginRequestInternal(req_iter->url_request, req_iter->mixed_content); + } + + delete requests; +} diff --git a/chrome/browser/renderer_host/resource_dispatcher_host.h b/chrome/browser/renderer_host/resource_dispatcher_host.h index c0e7e931..757de44 100644 --- a/chrome/browser/renderer_host/resource_dispatcher_host.h +++ b/chrome/browser/renderer_host/resource_dispatcher_host.h @@ -14,6 +14,7 @@ #include <map> #include <string> +#include <vector> #include "base/logging.h" #include "base/observer_list.h" @@ -202,7 +203,7 @@ class ResourceDispatcherHost : public URLRequest::Delegate { URLRequestContext* request_context, IPC::Message* sync_result); - // Initiate a download from the browser process (as opposed to a resource + // Initiates a download from the browser process (as opposed to a resource // request from the renderer). void BeginDownload(const GURL& url, const GURL& referrer, @@ -210,7 +211,7 @@ class ResourceDispatcherHost : public URLRequest::Delegate { int render_view_id, URLRequestContext* request_context); - // Initiate a save file from the browser process (as opposed to a resource + // Initiates a save file from the browser process (as opposed to a resource // request from the renderer). void BeginSaveFile(const GURL& url, const GURL& referrer, @@ -301,12 +302,12 @@ class ResourceDispatcherHost : public URLRequest::Delegate { return r; } - // Add an observer. The observer will be called on the IO thread. To + // Adds an observer. The observer will be called on the IO thread. To // observe resource events on the UI thread, subscribe to the // NOTIFY_RESOURCE_* notifications of the notification service. void AddObserver(Observer* obs); - // Remove an observer. + // Removes an observer. void RemoveObserver(Observer* obs); // Retrieves a URLRequest. Must be called from the IO thread. @@ -317,16 +318,41 @@ class ResourceDispatcherHost : public URLRequest::Delegate { bool ShouldDownload(const std::string& mime_type, const std::string& content_disposition); - // Notify our observers that a request has been cancelled. + // Notifies our observers that a request has been cancelled. void NotifyResponseCompleted(URLRequest* request, int render_process_host_id); void RemovePendingRequest(int render_process_host_id, int request_id); + // Causes all new requests for the render view identified by + // |render_process_host_id| and |render_view_id| to be blocked (not being + // started) until ResumeBlockedRequestsForRenderView or + // CancelBlockedRequestsForRenderView is called. + void BlockRequestsForRenderView(int render_process_host_id, + int render_view_id); + + // Resumes any blocked request for the specified RenderView. + void ResumeBlockedRequestsForRenderView(int render_process_host_id, + int render_view_id); + + // Cancels any blocked request for the specified RenderView. + void CancelBlockedRequestsForRenderView(int render_process_host_id, + int render_view_id); + private: + FRIEND_TEST(ResourceDispatcherHostTest, TestBlockedRequestsProcessDies); class ShutdownTask; friend class ShutdownTask; + struct BlockedRequest { + BlockedRequest(URLRequest* url_request, bool mixed_content) + : url_request(url_request), + mixed_content(mixed_content) { + } + URLRequest* url_request; + bool mixed_content; + }; + // A shutdown helper that runs on the IO thread. void OnShutdown(); @@ -396,6 +422,11 @@ class ResourceDispatcherHost : public URLRequest::Delegate { void MaybeUpdateUploadProgress(ExtraRequestInfo *info, URLRequest *request); + // Resumes or cancels (if |cancel_requests| is true) any blocked requests. + void ProcessBlockedRequestsForRenderView(int render_process_host_id, + int render_view_id, + bool cancel_requests); + PendingRequestList pending_requests_; // We cache the UI message loop so we can create new UI-related objects on it. @@ -440,6 +471,11 @@ class ResourceDispatcherHost : public URLRequest::Delegate { // True if the resource dispatcher host has been shut down. bool is_shutdown_; + typedef std::vector<BlockedRequest> BlockedRequestsList; + typedef std::pair<int, int> ProcessRendererIDs; + typedef std::map<ProcessRendererIDs, BlockedRequestsList*> BlockedRequestMap; + BlockedRequestMap blocked_requests_map_; + DISALLOW_COPY_AND_ASSIGN(ResourceDispatcherHost); }; diff --git a/chrome/browser/renderer_host/resource_dispatcher_host_unittest.cc b/chrome/browser/renderer_host/resource_dispatcher_host_unittest.cc index 8ae6e1a..d40c21b 100644 --- a/chrome/browser/renderer_host/resource_dispatcher_host_unittest.cc +++ b/chrome/browser/renderer_host/resource_dispatcher_host_unittest.cc @@ -51,6 +51,7 @@ class ResourceIPCAccumulator { // This groups the messages by their request ID. The groups will be in order // that the first message for each request ID was received, and the messages // within the groups will be in the order that they appeared. + // Note that this clears messages_. typedef std::vector< std::vector<IPC::Message> > ClassifiedMessages; void GetClassifiedMessages(ClassifiedMessages* msgs); @@ -71,7 +72,7 @@ void ResourceIPCAccumulator::GetClassifiedMessages(ClassifiedMessages* msgs) { if (id == cur_id) { cur_requests.push_back(messages_[i]); messages_.erase(messages_.begin() + i); - i --; + i--; } } messages_.erase(messages_.begin()); @@ -106,7 +107,10 @@ class ResourceDispatcherHostTest : public testing::Test, message_loop_.RunAllPending(); } - void MakeTestRequest(int request_id, const GURL& url); + void MakeTestRequest(int render_process_id, + int render_view_id, + int request_id, + const GURL& url); void MakeCancelRequest(int request_id); void EnsureTestSchemeIsAllowed() { @@ -128,12 +132,14 @@ static void KickOffRequest() { MessageLoop::current()->RunAllPending(); } -void ResourceDispatcherHostTest::MakeTestRequest(int request_id, +void ResourceDispatcherHostTest::MakeTestRequest(int render_process_id, + int render_view_id, + int request_id, const GURL& url) { ViewHostMsg_Resource_Request request = CreateResourceRequest("GET", url); - host_.BeginRequest(this, GetCurrentProcess(), 0, MSG_ROUTING_NONE, - request_id, request, NULL, NULL); + host_.BeginRequest(this, GetCurrentProcess(), render_process_id, + render_view_id, request_id, request, NULL, NULL); KickOffRequest(); } @@ -183,9 +189,9 @@ void CheckSuccessfulRequest(const std::vector<IPC::Message>& messages, // Tests whether many messages get dispatched properly. TEST_F(ResourceDispatcherHostTest, TestMany) { - MakeTestRequest(1, URLRequestTestJob::test_url_1()); - MakeTestRequest(2, URLRequestTestJob::test_url_2()); - MakeTestRequest(3, URLRequestTestJob::test_url_3()); + MakeTestRequest(0, 0, 1, URLRequestTestJob::test_url_1()); + MakeTestRequest(0, 0, 2, URLRequestTestJob::test_url_2()); + MakeTestRequest(0, 0, 3, URLRequestTestJob::test_url_3()); // flush all the pending requests while (URLRequestTestJob::ProcessOnePendingMessage()); @@ -207,9 +213,9 @@ TEST_F(ResourceDispatcherHostTest, TestMany) { TEST_F(ResourceDispatcherHostTest, Cancel) { ResourceDispatcherHost host(NULL); - MakeTestRequest(1, URLRequestTestJob::test_url_1()); - MakeTestRequest(2, URLRequestTestJob::test_url_2()); - MakeTestRequest(3, URLRequestTestJob::test_url_3()); + MakeTestRequest(0, 0, 1, URLRequestTestJob::test_url_1()); + MakeTestRequest(0, 0, 2, URLRequestTestJob::test_url_2()); + MakeTestRequest(0, 0, 3, URLRequestTestJob::test_url_3()); MakeCancelRequest(2); // flush all the pending requests @@ -277,7 +283,7 @@ TEST_F(ResourceDispatcherHostTest, TestProcessCancel) { KickOffRequest(); // request 2 goes to us - MakeTestRequest(2, URLRequestTestJob::test_url_2()); + MakeTestRequest(0, 0, 2, URLRequestTestJob::test_url_2()); // request 3 goes to the test delegate request.url = URLRequestTestJob::test_url_3(); @@ -314,3 +320,139 @@ TEST_F(ResourceDispatcherHostTest, TestProcessCancel) { CheckSuccessfulRequest(msgs[0], URLRequestTestJob::test_data_2()); } +// Tests blocking and resuming requests. +TEST_F(ResourceDispatcherHostTest, TestBlockingResumingRequests) { + host_.BlockRequestsForRenderView(0, 1); + host_.BlockRequestsForRenderView(0, 2); + host_.BlockRequestsForRenderView(0, 3); + + MakeTestRequest(0, 0, 1, URLRequestTestJob::test_url_1()); + MakeTestRequest(0, 1, 2, URLRequestTestJob::test_url_2()); + MakeTestRequest(0, 0, 3, URLRequestTestJob::test_url_3()); + MakeTestRequest(0, 1, 4, URLRequestTestJob::test_url_1()); + MakeTestRequest(0, 2, 5, URLRequestTestJob::test_url_2()); + MakeTestRequest(0, 3, 6, URLRequestTestJob::test_url_3()); + + // Flush all the pending requests + while (URLRequestTestJob::ProcessOnePendingMessage()); + + // Sort out all the messages we saw by request + ResourceIPCAccumulator::ClassifiedMessages msgs; + accum_.GetClassifiedMessages(&msgs); + + // All requests but the 2 for the RVH 0 should have been blocked. + ASSERT_EQ(2, msgs.size()); + + CheckSuccessfulRequest(msgs[0], URLRequestTestJob::test_data_1()); + CheckSuccessfulRequest(msgs[1], URLRequestTestJob::test_data_3()); + + // Resume requests for RVH 1 and flush pending requests. + host_.ResumeBlockedRequestsForRenderView(0, 1); + KickOffRequest(); + while (URLRequestTestJob::ProcessOnePendingMessage()); + + msgs.clear(); + accum_.GetClassifiedMessages(&msgs); + ASSERT_EQ(2, msgs.size()); + CheckSuccessfulRequest(msgs[0], URLRequestTestJob::test_data_2()); + CheckSuccessfulRequest(msgs[1], URLRequestTestJob::test_data_1()); + + // Test that new requests are not blocked for RVH 1. + MakeTestRequest(0, 1, 7, URLRequestTestJob::test_url_1()); + while (URLRequestTestJob::ProcessOnePendingMessage()); + msgs.clear(); + accum_.GetClassifiedMessages(&msgs); + ASSERT_EQ(1, msgs.size()); + CheckSuccessfulRequest(msgs[0], URLRequestTestJob::test_data_1()); + + // Now resumes requests for all RVH (2 and 3). + host_.ResumeBlockedRequestsForRenderView(0, 2); + host_.ResumeBlockedRequestsForRenderView(0, 3); + KickOffRequest(); + while (URLRequestTestJob::ProcessOnePendingMessage()); + + msgs.clear(); + accum_.GetClassifiedMessages(&msgs); + ASSERT_EQ(2, msgs.size()); + CheckSuccessfulRequest(msgs[0], URLRequestTestJob::test_data_2()); + CheckSuccessfulRequest(msgs[1], URLRequestTestJob::test_data_3()); +} + +// Tests blocking and canceling requests. +TEST_F(ResourceDispatcherHostTest, TestBlockingCancelingRequests) { + host_.BlockRequestsForRenderView(0, 1); + + MakeTestRequest(0, 0, 1, URLRequestTestJob::test_url_1()); + MakeTestRequest(0, 1, 2, URLRequestTestJob::test_url_2()); + MakeTestRequest(0, 0, 3, URLRequestTestJob::test_url_3()); + MakeTestRequest(0, 1, 4, URLRequestTestJob::test_url_1()); + + // Flush all the pending requests. + while (URLRequestTestJob::ProcessOnePendingMessage()); + + // Sort out all the messages we saw by request. + ResourceIPCAccumulator::ClassifiedMessages msgs; + accum_.GetClassifiedMessages(&msgs); + + // The 2 requests for the RVH 0 should have been processed. + ASSERT_EQ(2, msgs.size()); + + CheckSuccessfulRequest(msgs[0], URLRequestTestJob::test_data_1()); + CheckSuccessfulRequest(msgs[1], URLRequestTestJob::test_data_3()); + + // Cancel requests for RVH 1. + host_.CancelBlockedRequestsForRenderView(0, 1); + KickOffRequest(); + while (URLRequestTestJob::ProcessOnePendingMessage()); + msgs.clear(); + accum_.GetClassifiedMessages(&msgs); + ASSERT_EQ(0, msgs.size()); +} + +// Tests that blocked requests are canceled if their associated process dies. +TEST_F(ResourceDispatcherHostTest, TestBlockedRequestsProcessDies) { + host_.BlockRequestsForRenderView(1, 0); + + MakeTestRequest(0, 0, 1, URLRequestTestJob::test_url_1()); + MakeTestRequest(1, 0, 2, URLRequestTestJob::test_url_2()); + MakeTestRequest(0, 0, 3, URLRequestTestJob::test_url_3()); + MakeTestRequest(1, 0, 4, URLRequestTestJob::test_url_1()); + + // Simulate process death. + host_.CancelRequestsForProcess(1); + + // Flush all the pending requests. + while (URLRequestTestJob::ProcessOnePendingMessage()); + + // Sort out all the messages we saw by request. + ResourceIPCAccumulator::ClassifiedMessages msgs; + accum_.GetClassifiedMessages(&msgs); + + // The 2 requests for the RVH 0 should have been processed. + ASSERT_EQ(2, msgs.size()); + + CheckSuccessfulRequest(msgs[0], URLRequestTestJob::test_data_1()); + CheckSuccessfulRequest(msgs[1], URLRequestTestJob::test_data_3()); + + EXPECT_TRUE(host_.blocked_requests_map_.empty()); +} + +// Tests that blocked requests don't leak when the ResourceDispatcherHost goes +// away. Note that we rely on Purify for finding the leaks if any. +// If this test turns the Purify bot red, check the ResourceDispatcherHost +// destructor to make sure the blocked requests are deleted. +TEST_F(ResourceDispatcherHostTest, TestBlockedRequestsDontLeak) { + host_.BlockRequestsForRenderView(0, 1); + host_.BlockRequestsForRenderView(0, 2); + host_.BlockRequestsForRenderView(1, 1); + + MakeTestRequest(0, 0, 1, URLRequestTestJob::test_url_1()); + MakeTestRequest(0, 1, 2, URLRequestTestJob::test_url_2()); + MakeTestRequest(0, 0, 3, URLRequestTestJob::test_url_3()); + MakeTestRequest(1, 1, 4, URLRequestTestJob::test_url_1()); + MakeTestRequest(0, 2, 5, URLRequestTestJob::test_url_2()); + MakeTestRequest(0, 2, 6, URLRequestTestJob::test_url_3()); + + // Flush all the pending requests. + while (URLRequestTestJob::ProcessOnePendingMessage()); +} |