summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/interstitial_page.cc147
-rw-r--r--chrome/browser/interstitial_page.h11
-rw-r--r--chrome/browser/renderer_host/resource_dispatcher_host.cc103
-rw-r--r--chrome/browser/renderer_host/resource_dispatcher_host.h46
-rw-r--r--chrome/browser/renderer_host/resource_dispatcher_host_unittest.cc166
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());
+}