summaryrefslogtreecommitdiffstats
path: root/content
diff options
context:
space:
mode:
Diffstat (limited to 'content')
-rw-r--r--content/browser/loader/detachable_resource_handler.cc209
-rw-r--r--content/browser/loader/detachable_resource_handler.h94
-rw-r--r--content/browser/loader/resource_dispatcher_host_impl.cc63
-rw-r--r--content/browser/loader/resource_dispatcher_host_impl.h4
-rw-r--r--content/browser/loader/resource_dispatcher_host_unittest.cc708
-rw-r--r--content/browser/loader/resource_loader.cc8
-rw-r--r--content/browser/loader/resource_request_info_impl.cc1
-rw-r--r--content/browser/loader/resource_request_info_impl.h15
-rw-r--r--content/content_browser.gypi2
9 files changed, 942 insertions, 162 deletions
diff --git a/content/browser/loader/detachable_resource_handler.cc b/content/browser/loader/detachable_resource_handler.cc
new file mode 100644
index 0000000..7cc7b09
--- /dev/null
+++ b/content/browser/loader/detachable_resource_handler.cc
@@ -0,0 +1,209 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/loader/detachable_resource_handler.h"
+
+#include "base/logging.h"
+#include "base/time/time.h"
+#include "content/browser/loader/resource_request_info_impl.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/url_request/url_request_status.h"
+
+namespace {
+// This matches the maximum allocation size of AsyncResourceHandler.
+const int kReadBufSize = 32 * 1024;
+}
+
+namespace content {
+
+DetachableResourceHandler::DetachableResourceHandler(
+ net::URLRequest* request,
+ base::TimeDelta cancel_delay,
+ scoped_ptr<ResourceHandler> next_handler)
+ : ResourceHandler(request),
+ next_handler_(next_handler.Pass()),
+ cancel_delay_(cancel_delay),
+ is_deferred_(false),
+ is_finished_(false) {
+ GetRequestInfo()->set_detachable_handler(this);
+}
+
+DetachableResourceHandler::~DetachableResourceHandler() {
+ // Cleanup back-pointer stored on the request info.
+ GetRequestInfo()->set_detachable_handler(NULL);
+}
+
+void DetachableResourceHandler::Detach() {
+ if (is_detached())
+ return;
+
+ if (!is_finished_) {
+ // Simulate a cancel on the next handler before destroying it.
+ net::URLRequestStatus status(net::URLRequestStatus::CANCELED,
+ net::ERR_ABORTED);
+ bool defer_ignored = false;
+ next_handler_->OnResponseCompleted(GetRequestID(), status, std::string(),
+ &defer_ignored);
+ DCHECK(!defer_ignored);
+ // If |next_handler_| were to defer its shutdown in OnResponseCompleted,
+ // this would destroy it anyway. Fortunately, AsyncResourceHandler never
+ // does this anyway, so DCHECK it. BufferedResourceHandler and RVH shutdown
+ // already ignore deferred ResourceHandler shutdown, but
+ // DetachableResourceHandler and the detach-on-renderer-cancel logic
+ // introduces a case where this occurs when the renderer cancels a resource.
+ }
+ // A OnWillRead / OnReadCompleted pair may still be in progress, but
+ // OnWillRead passes back a scoped_refptr, so downstream handler's buffer will
+ // survive long enough to complete that read. From there, future reads will
+ // drain into |read_buffer_|. (If |next_handler_| is an AsyncResourceHandler,
+ // the net::IOBuffer takes a reference to the ResourceBuffer which owns the
+ // shared memory.)
+ next_handler_.reset();
+
+ // Time the request out if it takes too long.
+ detached_timer_.reset(new base::OneShotTimer<DetachableResourceHandler>());
+ detached_timer_->Start(
+ FROM_HERE, cancel_delay_, this, &DetachableResourceHandler::Cancel);
+
+ // Resume if necessary. The request may have been deferred, say, waiting on a
+ // full buffer in AsyncResourceHandler. Now that it has been detached, resume
+ // and drain it.
+ if (is_deferred_)
+ Resume();
+}
+
+void DetachableResourceHandler::SetController(ResourceController* controller) {
+ ResourceHandler::SetController(controller);
+
+ // Intercept the ResourceController for downstream handlers to keep track of
+ // whether the request is deferred.
+ if (next_handler_)
+ next_handler_->SetController(this);
+}
+
+bool DetachableResourceHandler::OnUploadProgress(int request_id,
+ uint64 position,
+ uint64 size) {
+ if (!next_handler_)
+ return true;
+
+ return next_handler_->OnUploadProgress(request_id, position, size);
+}
+
+bool DetachableResourceHandler::OnRequestRedirected(int request_id,
+ const GURL& url,
+ ResourceResponse* response,
+ bool* defer) {
+ DCHECK(!is_deferred_);
+
+ if (!next_handler_)
+ return true;
+
+ bool ret = next_handler_->OnRequestRedirected(request_id, url, response,
+ &is_deferred_);
+ *defer = is_deferred_;
+ return ret;
+}
+
+bool DetachableResourceHandler::OnResponseStarted(int request_id,
+ ResourceResponse* response,
+ bool* defer) {
+ DCHECK(!is_deferred_);
+
+ if (!next_handler_)
+ return true;
+
+ bool ret =
+ next_handler_->OnResponseStarted(request_id, response, &is_deferred_);
+ *defer = is_deferred_;
+ return ret;
+}
+
+bool DetachableResourceHandler::OnWillStart(int request_id, const GURL& url,
+ bool* defer) {
+ DCHECK(!is_deferred_);
+
+ if (!next_handler_)
+ return true;
+
+ bool ret = next_handler_->OnWillStart(request_id, url, &is_deferred_);
+ *defer = is_deferred_;
+ return ret;
+}
+
+bool DetachableResourceHandler::OnWillRead(int request_id,
+ scoped_refptr<net::IOBuffer>* buf,
+ int* buf_size,
+ int min_size) {
+ if (!next_handler_) {
+ DCHECK_EQ(-1, min_size);
+ if (!read_buffer_)
+ read_buffer_ = new net::IOBuffer(kReadBufSize);
+ *buf = read_buffer_;
+ *buf_size = kReadBufSize;
+ return true;
+ }
+
+ return next_handler_->OnWillRead(request_id, buf, buf_size, min_size);
+}
+
+bool DetachableResourceHandler::OnReadCompleted(int request_id, int bytes_read,
+ bool* defer) {
+ DCHECK(!is_deferred_);
+
+ if (!next_handler_)
+ return true;
+
+ bool ret =
+ next_handler_->OnReadCompleted(request_id, bytes_read, &is_deferred_);
+ *defer = is_deferred_;
+ return ret;
+}
+
+void DetachableResourceHandler::OnResponseCompleted(
+ int request_id,
+ const net::URLRequestStatus& status,
+ const std::string& security_info,
+ bool* defer) {
+ // No DCHECK(!is_deferred_) as the request may have been cancelled while
+ // deferred.
+
+ if (!next_handler_)
+ return;
+
+ is_finished_ = true;
+
+ next_handler_->OnResponseCompleted(request_id, status, security_info,
+ &is_deferred_);
+ *defer = is_deferred_;
+}
+
+void DetachableResourceHandler::OnDataDownloaded(int request_id,
+ int bytes_downloaded) {
+ if (!next_handler_)
+ return;
+
+ next_handler_->OnDataDownloaded(request_id, bytes_downloaded);
+}
+
+void DetachableResourceHandler::Resume() {
+ DCHECK(is_deferred_);
+ is_deferred_ = false;
+ controller()->Resume();
+}
+
+void DetachableResourceHandler::Cancel() {
+ controller()->Cancel();
+}
+
+void DetachableResourceHandler::CancelAndIgnore() {
+ controller()->CancelAndIgnore();
+}
+
+void DetachableResourceHandler::CancelWithError(int error_code) {
+ controller()->CancelWithError(error_code);
+}
+
+} // namespace content
diff --git a/content/browser/loader/detachable_resource_handler.h b/content/browser/loader/detachable_resource_handler.h
new file mode 100644
index 0000000..990084d
--- /dev/null
+++ b/content/browser/loader/detachable_resource_handler.h
@@ -0,0 +1,94 @@
+// Copyright 2013 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 CONTENT_BROWSER_LOADER_DETACHABLE_RESOURCE_HANDLER_H_
+#define CONTENT_BROWSER_LOADER_DETACHABLE_RESOURCE_HANDLER_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "content/browser/loader/resource_handler.h"
+#include "content/public/browser/resource_controller.h"
+
+namespace net {
+class IOBuffer;
+class URLRequest;
+} // namespace net
+
+namespace content {
+
+// A ResourceHandler which delegates all calls to the next handler, unless
+// detached. Once detached, it drives the request to completion itself. This is
+// used for requests which outlive the owning renderer, such as <link
+// rel=prefetch> and <a ping>. Requests do not start out detached so, e.g.,
+// prefetches appear in DevTools and get placed in the renderer's local
+// cache. If the request does not complete after a timeout on detach, it is
+// cancelled.
+//
+// Note that, once detached, the request continues without the original next
+// handler, so any policy decisions in that handler are skipped.
+class DetachableResourceHandler : public ResourceHandler,
+ public ResourceController {
+ public:
+ DetachableResourceHandler(net::URLRequest* request,
+ base::TimeDelta cancel_delay,
+ scoped_ptr<ResourceHandler> next_handler);
+ virtual ~DetachableResourceHandler();
+
+ bool is_detached() const { return next_handler_ == NULL; }
+ void Detach();
+
+ void set_cancel_delay(base::TimeDelta cancel_delay) {
+ cancel_delay_ = cancel_delay;
+ }
+
+ // ResourceHandler implementation:
+ virtual void SetController(ResourceController* controller) OVERRIDE;
+ virtual bool OnUploadProgress(int request_id, uint64 position,
+ uint64 size) OVERRIDE;
+ virtual bool OnRequestRedirected(int request_id, const GURL& url,
+ ResourceResponse* response,
+ bool* defer) OVERRIDE;
+ virtual bool OnResponseStarted(int request_id,
+ ResourceResponse* response,
+ bool* defer) OVERRIDE;
+ virtual bool OnWillStart(int request_id, const GURL& url,
+ bool* defer) OVERRIDE;
+ virtual bool OnWillRead(int request_id,
+ scoped_refptr<net::IOBuffer>* buf,
+ int* buf_size,
+ int min_size) OVERRIDE;
+ virtual bool OnReadCompleted(int request_id, int bytes_read,
+ bool* defer) OVERRIDE;
+ virtual void OnResponseCompleted(int request_id,
+ const net::URLRequestStatus& status,
+ const std::string& security_info,
+ bool* defer) OVERRIDE;
+ virtual void OnDataDownloaded(int request_id, int bytes_downloaded) OVERRIDE;
+
+ // ResourceController implementation:
+ virtual void Resume() OVERRIDE;
+ virtual void Cancel() OVERRIDE;
+ virtual void CancelAndIgnore() OVERRIDE;
+ virtual void CancelWithError(int error_code) OVERRIDE;
+
+ private:
+ scoped_ptr<ResourceHandler> next_handler_;
+ scoped_refptr<net::IOBuffer> read_buffer_;
+
+ scoped_ptr<base::OneShotTimer<DetachableResourceHandler> > detached_timer_;
+ base::TimeDelta cancel_delay_;
+
+ bool is_deferred_;
+ bool is_finished_;
+
+ DISALLOW_COPY_AND_ASSIGN(DetachableResourceHandler);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_LOADER_DETACHABLE_RESOURCE_HANDLER_H_
diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc
index 6da6d49..5681255 100644
--- a/content/browser/loader/resource_dispatcher_host_impl.cc
+++ b/content/browser/loader/resource_dispatcher_host_impl.cc
@@ -33,6 +33,7 @@
#include "content/browser/loader/async_resource_handler.h"
#include "content/browser/loader/buffered_resource_handler.h"
#include "content/browser/loader/cross_site_resource_handler.h"
+#include "content/browser/loader/detachable_resource_handler.h"
#include "content/browser/loader/power_save_block_resource_throttle.h"
#include "content/browser/loader/redirect_to_file_resource_handler.h"
#include "content/browser/loader/resource_message_filter.h"
@@ -127,6 +128,22 @@ const int kUserGestureWindowMs = 3500;
// use. Arbitrarily chosen.
const double kMaxRequestsPerProcessRatio = 0.45;
+// TODO(jkarlin): The value is high to reduce the chance of the detachable
+// request timing out, forcing a blocked second request to open a new connection
+// and start over. Reduce this value once we have a better idea of what it
+// should be and once we stop blocking multiple simultaneous requests for the
+// same resource (see bugs 46104 and 31014).
+const int kDefaultDetachableCancelDelayMs = 30000;
+
+bool IsDetachableResourceType(ResourceType::Type type) {
+ switch (type) {
+ case ResourceType::PREFETCH:
+ return true;
+ default:
+ return false;
+ }
+}
+
// Aborts a request before an URLRequest has actually been created.
void AbortRequestBeforeItStarts(ResourceMessageFilter* filter,
IPC::Message* sync_result,
@@ -406,12 +423,15 @@ void ResourceDispatcherHostImpl::CancelRequestsForContext(
for (LoaderList::iterator i = loaders_to_cancel.begin();
i != loaders_to_cancel.end(); ++i) {
// There is no strict requirement that this be the case, but currently
- // downloads, streams and transferred requests are the only requests that
- // aren't cancelled when the associated processes go away. It may be OK for
- // this invariant to change in the future, but if this assertion fires
- // without the invariant changing, then it's indicative of a leak.
+ // downloads, streams, detachable requests, and transferred requests are the
+ // only requests that aren't cancelled when the associated processes go
+ // away. It may be OK for this invariant to change in the future, but if
+ // this assertion fires without the invariant changing, then it's indicative
+ // of a leak.
DCHECK((*i)->GetRequestInfo()->is_download() ||
(*i)->GetRequestInfo()->is_stream() ||
+ ((*i)->GetRequestInfo()->detachable_handler() &&
+ (*i)->GetRequestInfo()->detachable_handler()->is_detached()) ||
(*i)->is_transferring());
}
#endif
@@ -694,18 +714,20 @@ void ResourceDispatcherHostImpl::DidReceiveRedirect(ResourceLoader* loader,
void ResourceDispatcherHostImpl::DidReceiveResponse(ResourceLoader* loader) {
ResourceRequestInfoImpl* info = loader->GetRequestInfo();
+
// There should be an entry in the map created when we dispatched the
- // request.
+ // request unless it's been detached and the renderer has died.
OfflineMap::iterator policy_it(
offline_policy_map_.find(info->GetGlobalRoutingID()));
if (offline_policy_map_.end() != policy_it) {
policy_it->second->UpdateStateForSuccessfullyStartedRequest(
loader->request()->response_info());
} else {
- // We should always have an entry in offline_policy_map_ from when
- // this request traversed Begin{Download,SaveFile,Request}.
+ // Unless detached, we should have an entry in offline_policy_map_ from
+ // when this request traversed Begin{Download,SaveFile,Request}.
// TODO(rdsmith): This isn't currently true; see http://crbug.com/241176.
- NOTREACHED();
+ DCHECK(info->detachable_handler() &&
+ info->detachable_handler()->is_detached());
}
int render_process_id, render_view_id;
@@ -1114,6 +1136,12 @@ void ResourceDispatcherHostImpl::BeginRequest(
handler.reset(new SyncResourceHandler(request, sync_result, this));
} else {
handler.reset(new AsyncResourceHandler(request, this));
+ if (IsDetachableResourceType(request_data.resource_type)) {
+ handler.reset(new DetachableResourceHandler(
+ request,
+ base::TimeDelta::FromMilliseconds(kDefaultDetachableCancelDelayMs),
+ handler.Pass()));
+ }
}
// The RedirectToFileResourceHandler depends on being next in the chain.
@@ -1233,7 +1261,7 @@ ResourceRequestInfoImpl* ResourceDispatcherHostImpl::CreateRequestInfo(
PAGE_TRANSITION_LINK,
false, // should_replace_current_entry
download, // is_download
- false, // is_stream
+ false, // is_stream
download, // allow_download
false, // has_user_gesture
blink::WebReferrerPolicyDefault,
@@ -1329,8 +1357,8 @@ void ResourceDispatcherHostImpl::ResumeDeferredNavigation(
}
// The object died, so cancel and detach all requests associated with it except
-// for downloads, which belong to the browser process even if initiated via a
-// renderer.
+// for downloads and detachable resources, which belong to the browser process
+// even if initiated via a renderer.
void ResourceDispatcherHostImpl::CancelRequestsForProcess(int child_id) {
CancelRequestsForRoute(child_id, -1 /* cancel all */);
registered_temp_files_.erase(child_id);
@@ -1355,14 +1383,15 @@ void ResourceDispatcherHostImpl::CancelRequestsForRoute(int child_id,
GlobalRequestID id(child_id, i->first.request_id);
DCHECK(id == i->first);
-
- // Don't cancel navigations that are transferring to another process,
- // since they belong to another process now.
+ // Don't cancel navigations that are expected to live beyond this process.
if (IsTransferredNavigation(id))
any_requests_transferring = true;
- if (!info->is_download() && !info->is_stream() &&
- !IsTransferredNavigation(id) &&
- (route_id == -1 || route_id == info->GetRouteID())) {
+
+ if (info->detachable_handler()) {
+ info->detachable_handler()->Detach();
+ } else if (!info->is_download() && !info->is_stream() &&
+ !IsTransferredNavigation(id) &&
+ (route_id == -1 || route_id == info->GetRouteID())) {
matching_requests.push_back(id);
}
}
diff --git a/content/browser/loader/resource_dispatcher_host_impl.h b/content/browser/loader/resource_dispatcher_host_impl.h
index 851463e..ef0e4cf 100644
--- a/content/browser/loader/resource_dispatcher_host_impl.h
+++ b/content/browser/loader/resource_dispatcher_host_impl.h
@@ -238,6 +238,10 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl
TestBlockedRequestsProcessDies);
FRIEND_TEST_ALL_PREFIXES(ResourceDispatcherHostTest,
CalculateApproximateMemoryCost);
+ FRIEND_TEST_ALL_PREFIXES(ResourceDispatcherHostTest,
+ DetachableResourceTimesOut);
+ FRIEND_TEST_ALL_PREFIXES(ResourceDispatcherHostTest,
+ TestProcessCancelDetachableTimesOut);
class ShutdownTask;
diff --git a/content/browser/loader/resource_dispatcher_host_unittest.cc b/content/browser/loader/resource_dispatcher_host_unittest.cc
index 07062ef..81e0c88 100644
--- a/content/browser/loader/resource_dispatcher_host_unittest.cc
+++ b/content/browser/loader/resource_dispatcher_host_unittest.cc
@@ -14,7 +14,9 @@
#include "base/strings/string_split.h"
#include "content/browser/browser_thread_impl.h"
#include "content/browser/child_process_security_policy_impl.h"
+#include "content/browser/loader/detachable_resource_handler.h"
#include "content/browser/loader/resource_dispatcher_host_impl.h"
+#include "content/browser/loader/resource_loader.h"
#include "content/browser/loader/resource_message_filter.h"
#include "content/browser/loader/resource_request_info_impl.h"
#include "content/browser/worker_host/worker_service_impl.h"
@@ -40,6 +42,7 @@
#include "net/url_request/url_request_job.h"
#include "net/url_request/url_request_simple_job.h"
#include "net/url_request/url_request_test_job.h"
+#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "webkit/common/appcache/appcache_interfaces.h"
@@ -524,13 +527,14 @@ class ResourceDispatcherHostTest : public testing::Test,
cache_thread_(BrowserThread::CACHE, &message_loop_),
io_thread_(BrowserThread::IO, &message_loop_),
old_factory_(NULL),
- resource_type_(ResourceType::SUB_RESOURCE),
send_data_received_acks_(false) {
browser_context_.reset(new TestBrowserContext());
BrowserContext::EnsureResourceContextInitialized(browser_context_.get());
message_loop_.RunUntilIdle();
- filter_ = new ForwardingFilter(
- this, browser_context_->GetResourceContext());
+ ResourceContext* resource_context = browser_context_->GetResourceContext();
+ filter_ = new ForwardingFilter(this, resource_context);
+ resource_context->GetRequestContext()->set_network_delegate(
+ &network_delegate_);
}
virtual ~ResourceDispatcherHostTest() {
@@ -595,23 +599,25 @@ class ResourceDispatcherHostTest : public testing::Test,
message_loop_.RunUntilIdle();
}
- // Creates a request using the current test object as the filter.
+ // Creates a request using the current test object as the filter and
+ // SubResource as the resource type.
void MakeTestRequest(int render_view_id,
int request_id,
const GURL& url);
- // Generates a request using the given filter. This will probably be a
- // ForwardingFilter.
- void MakeTestRequest(ResourceMessageFilter* filter,
- int render_view_id,
- int request_id,
- const GURL& url);
+ // Generates a request using the given filter and resource type.
+ void MakeTestRequestWithResourceType(ResourceMessageFilter* filter,
+ int render_view_id, int request_id,
+ const GURL& url,
+ ResourceType::Type type);
void CancelRequest(int request_id);
void CompleteStartRequest(int request_id);
void CompleteStartRequest(ResourceMessageFilter* filter, int request_id);
+ net::TestNetworkDelegate* network_delegate() { return &network_delegate_; }
+
void EnsureSchemeIsAllowed(const std::string& scheme) {
ChildProcessSecurityPolicyImpl* policy =
ChildProcessSecurityPolicyImpl::GetInstance();
@@ -635,11 +641,6 @@ class ResourceDispatcherHostTest : public testing::Test,
SetResponse(headers, std::string());
}
- // Sets a particular resource type for any request from now on.
- void SetResourceType(ResourceType::Type type) {
- resource_type_ = type;
- }
-
void SendDataReceivedACKs(bool send_acks) {
send_data_received_acks_ = send_acks;
}
@@ -719,13 +720,13 @@ class ResourceDispatcherHostTest : public testing::Test,
BrowserThreadImpl io_thread_;
scoped_ptr<TestBrowserContext> browser_context_;
scoped_refptr<ForwardingFilter> filter_;
+ net::TestNetworkDelegate network_delegate_;
ResourceDispatcherHostImpl host_;
ResourceIPCAccumulator accum_;
std::string response_headers_;
std::string response_data_;
std::string scheme_;
net::URLRequest::ProtocolFactory* old_factory_;
- ResourceType::Type resource_type_;
bool send_data_received_acks_;
std::set<int> child_ids_;
static ResourceDispatcherHostTest* test_fixture_;
@@ -742,19 +743,21 @@ int ResourceDispatcherHostTest::url_request_jobs_created_count_ = 0;
void ResourceDispatcherHostTest::MakeTestRequest(int render_view_id,
int request_id,
const GURL& url) {
- MakeTestRequest(filter_.get(), render_view_id, request_id, url);
+ MakeTestRequestWithResourceType(filter_.get(), render_view_id, request_id,
+ url, ResourceType::SUB_RESOURCE);
}
-void ResourceDispatcherHostTest::MakeTestRequest(
+void ResourceDispatcherHostTest::MakeTestRequestWithResourceType(
ResourceMessageFilter* filter,
int render_view_id,
int request_id,
- const GURL& url) {
+ const GURL& url,
+ ResourceType::Type type) {
// If it's already there, this'll be dropped on the floor, which is fine.
child_ids_.insert(filter->child_id());
ResourceHostMsg_Request request =
- CreateResourceRequest("GET", resource_type_, url);
+ CreateResourceRequest("GET", type, url);
ResourceHostMsg_RequestResource msg(render_view_id, request_id, request);
bool msg_was_ok;
host_.OnMessageReceived(msg, filter, &msg_was_ok);
@@ -779,16 +782,47 @@ void ResourceDispatcherHostTest::CompleteStartRequest(
URLRequestTestDelayedStartJob::CompleteStart(req);
}
-void CheckSuccessfulRequest(const std::vector<IPC::Message>& messages,
- const std::string& reference_data) {
+void CheckRequestCompleteErrorCode(const IPC::Message& message,
+ int expected_error_code) {
+ // Verify the expected error code was received.
+ int request_id;
+ int error_code;
+
+ ASSERT_EQ(ResourceMsg_RequestComplete::ID, message.type());
+
+ PickleIterator iter(message);
+ ASSERT_TRUE(IPC::ReadParam(&message, &iter, &request_id));
+ ASSERT_TRUE(IPC::ReadParam(&message, &iter, &error_code));
+ ASSERT_EQ(expected_error_code, error_code);
+}
+
+testing::AssertionResult ExtractDataOffsetAndLength(const IPC::Message& message,
+ int* data_offset,
+ int* data_length) {
+ PickleIterator iter(message);
+ int request_id;
+ if (!IPC::ReadParam(&message, &iter, &request_id))
+ return testing::AssertionFailure() << "Could not read request_id";
+ if (!IPC::ReadParam(&message, &iter, data_offset))
+ return testing::AssertionFailure() << "Could not read data_offset";
+ if (!IPC::ReadParam(&message, &iter, data_length))
+ return testing::AssertionFailure() << "Could not read data_length";
+ return testing::AssertionSuccess();
+}
+
+void CheckSuccessfulRequestWithErrorCode(
+ const std::vector<IPC::Message>& messages,
+ const std::string& reference_data,
+ int expected_error) {
// A successful request will have received 4 messages:
// ReceivedResponse (indicates headers received)
// SetDataBuffer (contains shared memory handle)
// DataReceived (data offset and length into shared memory)
// RequestComplete (request is done)
//
- // This function verifies that we received 4 messages and that they
- // are appropriate.
+ // This function verifies that we received 4 messages and that they are
+ // appropriate. It allows for an error code other than net::OK if the request
+ // should successfully receive data and then abort, e.g., on cancel.
ASSERT_EQ(4U, messages.size());
// The first messages should be received response
@@ -808,12 +842,10 @@ void CheckSuccessfulRequest(const std::vector<IPC::Message>& messages,
// should probably test multiple chunks later
ASSERT_EQ(ResourceMsg_DataReceived::ID, messages[2].type());
- PickleIterator iter2(messages[2]);
- ASSERT_TRUE(IPC::ReadParam(&messages[2], &iter2, &request_id));
int data_offset;
- ASSERT_TRUE(IPC::ReadParam(&messages[2], &iter2, &data_offset));
int data_length;
- ASSERT_TRUE(IPC::ReadParam(&messages[2], &iter2, &data_length));
+ ASSERT_TRUE(
+ ExtractDataOffsetAndLength(messages[2], &data_offset, &data_length));
ASSERT_EQ(reference_data.size(), static_cast<size_t>(data_length));
ASSERT_GE(shm_size, data_length);
@@ -824,7 +856,22 @@ void CheckSuccessfulRequest(const std::vector<IPC::Message>& messages,
ASSERT_EQ(0, memcmp(reference_data.c_str(), data, data_length));
// The last message should be all data received.
- ASSERT_EQ(ResourceMsg_RequestComplete::ID, messages[3].type());
+ CheckRequestCompleteErrorCode(messages[3], expected_error);
+}
+
+void CheckSuccessfulRequest(const std::vector<IPC::Message>& messages,
+ const std::string& reference_data) {
+ CheckSuccessfulRequestWithErrorCode(messages, reference_data, net::OK);
+}
+
+void CheckSuccessfulRedirect(const std::vector<IPC::Message>& messages,
+ const std::string& reference_data) {
+ ASSERT_EQ(5U, messages.size());
+ ASSERT_EQ(ResourceMsg_ReceivedRedirect::ID, messages[0].type());
+
+ const std::vector<IPC::Message> second_req_msgs =
+ std::vector<IPC::Message>(messages.begin() + 1, messages.end());
+ CheckSuccessfulRequest(second_req_msgs, reference_data);
}
void CheckFailedRequest(const std::vector<IPC::Message>& messages,
@@ -837,15 +884,8 @@ void CheckFailedRequest(const std::vector<IPC::Message>& messages,
if (messages.size() == 2) {
EXPECT_EQ(ResourceMsg_ReceivedResponse::ID, messages[0].type());
}
- EXPECT_EQ(ResourceMsg_RequestComplete::ID, messages[failure_index].type());
- int request_id;
- int error_code;
-
- PickleIterator iter(messages[failure_index]);
- EXPECT_TRUE(IPC::ReadParam(&messages[failure_index], &iter, &request_id));
- EXPECT_TRUE(IPC::ReadParam(&messages[failure_index], &iter, &error_code));
- EXPECT_EQ(expected_error, error_code);
+ CheckRequestCompleteErrorCode(messages[failure_index], expected_error);
}
// Tests whether many messages get dispatched properly.
@@ -853,6 +893,16 @@ TEST_F(ResourceDispatcherHostTest, TestMany) {
MakeTestRequest(0, 1, net::URLRequestTestJob::test_url_1());
MakeTestRequest(0, 2, net::URLRequestTestJob::test_url_2());
MakeTestRequest(0, 3, net::URLRequestTestJob::test_url_3());
+ MakeTestRequestWithResourceType(filter_.get(), 0, 4,
+ net::URLRequestTestJob::test_url_4(),
+ ResourceType::PREFETCH); // detachable type
+ MakeTestRequest(0, 5, net::URLRequestTestJob::test_url_redirect_to_url_2());
+
+ // Finish the redirection
+ ResourceHostMsg_FollowRedirect redirect_msg(5, false, GURL());
+ bool msg_was_ok;
+ host_.OnMessageReceived(redirect_msg, filter_.get(), &msg_was_ok);
+ base::MessageLoop::current()->RunUntilIdle();
// flush all the pending requests
while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
@@ -861,52 +911,203 @@ TEST_F(ResourceDispatcherHostTest, TestMany) {
ResourceIPCAccumulator::ClassifiedMessages msgs;
accum_.GetClassifiedMessages(&msgs);
- // there are three requests, so we should have gotten them classified as such
- ASSERT_EQ(3U, msgs.size());
+ // there are five requests, so we should have gotten them classified as such
+ ASSERT_EQ(5U, msgs.size());
CheckSuccessfulRequest(msgs[0], net::URLRequestTestJob::test_data_1());
CheckSuccessfulRequest(msgs[1], net::URLRequestTestJob::test_data_2());
CheckSuccessfulRequest(msgs[2], net::URLRequestTestJob::test_data_3());
+ CheckSuccessfulRequest(msgs[3], net::URLRequestTestJob::test_data_4());
+ CheckSuccessfulRedirect(msgs[4], net::URLRequestTestJob::test_data_2());
}
-void CheckCancelledRequestCompleteMessage(const IPC::Message& message) {
- ASSERT_EQ(ResourceMsg_RequestComplete::ID, message.type());
-
- int request_id;
- int error_code;
-
- PickleIterator iter(message);
- ASSERT_TRUE(IPC::ReadParam(&message, &iter, &request_id));
- ASSERT_TRUE(IPC::ReadParam(&message, &iter, &error_code));
-
- EXPECT_EQ(net::ERR_ABORTED, error_code);
-}
-
-// Tests whether messages get canceled properly. We issue three requests,
-// cancel one of them, and make sure that each sent the proper notifications.
+// Tests whether messages get canceled properly. We issue four requests,
+// cancel two of them, and make sure that each sent the proper notifications.
TEST_F(ResourceDispatcherHostTest, Cancel) {
MakeTestRequest(0, 1, net::URLRequestTestJob::test_url_1());
MakeTestRequest(0, 2, net::URLRequestTestJob::test_url_2());
MakeTestRequest(0, 3, net::URLRequestTestJob::test_url_3());
+
+ MakeTestRequestWithResourceType(filter_.get(), 0, 4,
+ net::URLRequestTestJob::test_url_4(),
+ ResourceType::PREFETCH); // detachable type
+
CancelRequest(2);
+ // Cancel request must come from the renderer for a detachable resource to
+ // delay.
+ host_.CancelRequest(filter_->child_id(), 4, true);
+
+ // The handler should have been detached now.
+ GlobalRequestID global_request_id(filter_->child_id(), 4);
+ ResourceRequestInfoImpl* info = ResourceRequestInfoImpl::ForRequest(
+ host_.GetURLRequest(global_request_id));
+ ASSERT_TRUE(info->detachable_handler()->is_detached());
+
// flush all the pending requests
while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
base::MessageLoop::current()->RunUntilIdle();
+ // Everything should be out now.
+ EXPECT_EQ(0, host_.pending_requests());
+
ResourceIPCAccumulator::ClassifiedMessages msgs;
accum_.GetClassifiedMessages(&msgs);
- // there are three requests, so we should have gotten them classified as such
- ASSERT_EQ(3U, msgs.size());
+ // there are four requests, so we should have gotten them classified as such
+ ASSERT_EQ(4U, msgs.size());
CheckSuccessfulRequest(msgs[0], net::URLRequestTestJob::test_data_1());
CheckSuccessfulRequest(msgs[2], net::URLRequestTestJob::test_data_3());
- // Check that request 2 got canceled.
+ // Check that request 2 and 4 got canceled, as far as the renderer is
+ // concerned.
ASSERT_EQ(2U, msgs[1].size());
ASSERT_EQ(ResourceMsg_ReceivedResponse::ID, msgs[1][0].type());
- CheckCancelledRequestCompleteMessage(msgs[1][1]);
+ CheckRequestCompleteErrorCode(msgs[1][1], net::ERR_ABORTED);
+
+ ASSERT_EQ(2U, msgs[3].size());
+ ASSERT_EQ(ResourceMsg_ReceivedResponse::ID, msgs[3][0].type());
+ CheckRequestCompleteErrorCode(msgs[1][1], net::ERR_ABORTED);
+
+ // However, request 4 should have actually gone to completion. (Only request 2
+ // was canceled.)
+ EXPECT_EQ(4, network_delegate()->completed_requests());
+ EXPECT_EQ(1, network_delegate()->canceled_requests());
+ EXPECT_EQ(0, network_delegate()->error_count());
+}
+
+// Shows that detachable requests will timeout if the request takes too long to
+// complete.
+TEST_F(ResourceDispatcherHostTest, DetachedResourceTimesOut) {
+ MakeTestRequestWithResourceType(filter_.get(), 0, 1,
+ net::URLRequestTestJob::test_url_2(),
+ ResourceType::PREFETCH); // detachable type
+ GlobalRequestID global_request_id(filter_->child_id(), 1);
+ ResourceRequestInfoImpl* info = ResourceRequestInfoImpl::ForRequest(
+ host_.GetURLRequest(global_request_id));
+ ASSERT_TRUE(info->detachable_handler());
+ info->detachable_handler()->set_cancel_delay(
+ base::TimeDelta::FromMilliseconds(200));
+ base::MessageLoop::current()->RunUntilIdle();
+ host_.CancelRequest(filter_->child_id(), 1, true);
+
+ // From the renderer's perspective, the request was cancelled.
+ ResourceIPCAccumulator::ClassifiedMessages msgs;
+ accum_.GetClassifiedMessages(&msgs);
+ ASSERT_EQ(1U, msgs.size());
+ ASSERT_EQ(2U, msgs[0].size());
+ ASSERT_EQ(ResourceMsg_ReceivedResponse::ID, msgs[0][0].type());
+ CheckRequestCompleteErrorCode(msgs[0][1], net::ERR_ABORTED);
+
+ // But it continues detached.
+ EXPECT_EQ(1, host_.pending_requests());
+ EXPECT_TRUE(info->detachable_handler()->is_detached());
+
+ // Wait until after the delay timer times out before we start processing any
+ // messages.
+ base::OneShotTimer<base::MessageLoop> timer;
+ timer.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(210),
+ base::MessageLoop::current(), &base::MessageLoop::QuitWhenIdle);
+ base::MessageLoop::current()->Run();
+
+ // The prefetch should be cancelled by now.
+ EXPECT_EQ(0, host_.pending_requests());
+ EXPECT_EQ(1, network_delegate()->completed_requests());
+ EXPECT_EQ(1, network_delegate()->canceled_requests());
+ EXPECT_EQ(0, network_delegate()->error_count());
+}
+
+// If the filter has disappeared then detachable resources should continue to
+// load.
+TEST_F(ResourceDispatcherHostTest, DeletedFilterDetached) {
+ ResourceHostMsg_Request request = CreateResourceRequest(
+ "GET", ResourceType::PREFETCH, net::URLRequestTestJob::test_url_4());
+
+ ResourceHostMsg_RequestResource msg(0, 1, request);
+ bool msg_was_ok;
+ host_.OnMessageReceived(msg, filter_, &msg_was_ok);
+
+ // Remove the filter before processing the request by simulating channel
+ // closure.
+ GlobalRequestID global_request_id(filter_->child_id(), 1);
+ ResourceRequestInfoImpl* info = ResourceRequestInfoImpl::ForRequest(
+ host_.GetURLRequest(global_request_id));
+ info->filter_->OnChannelClosing();
+ info->filter_.reset();
+
+ // From the renderer's perspective, the request was cancelled.
+ ResourceIPCAccumulator::ClassifiedMessages msgs;
+ accum_.GetClassifiedMessages(&msgs);
+ ASSERT_EQ(1U, msgs.size());
+ CheckRequestCompleteErrorCode(msgs[0][0], net::ERR_ABORTED);
+
+ // But it continues detached.
+ EXPECT_EQ(1, host_.pending_requests());
+ EXPECT_TRUE(info->detachable_handler()->is_detached());
+
+ KickOffRequest();
+
+ // Make sure the request wasn't canceled early.
+ EXPECT_EQ(1, host_.pending_requests());
+
+ while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
+ base::MessageLoop::current()->RunUntilIdle();
+
+ EXPECT_EQ(0, host_.pending_requests());
+ EXPECT_EQ(1, network_delegate()->completed_requests());
+ EXPECT_EQ(0, network_delegate()->canceled_requests());
+ EXPECT_EQ(0, network_delegate()->error_count());
+}
+
+// If the filter has disappeared (original process dies) then detachable
+// resources should continue to load, even when redirected.
+TEST_F(ResourceDispatcherHostTest, DeletedFilterDetachedRedirect) {
+ ResourceHostMsg_Request request = CreateResourceRequest(
+ "GET", ResourceType::PREFETCH,
+ net::URLRequestTestJob::test_url_redirect_to_url_2());
+
+ ResourceHostMsg_RequestResource msg(0, 1, request);
+ bool msg_was_ok;
+ host_.OnMessageReceived(msg, filter_, &msg_was_ok);
+
+ // Remove the filter before processing the request by simulating channel
+ // closure.
+ GlobalRequestID global_request_id(filter_->child_id(), 1);
+ ResourceRequestInfoImpl* info = ResourceRequestInfoImpl::ForRequest(
+ host_.GetURLRequest(global_request_id));
+ info->filter_->OnChannelClosing();
+ info->filter_.reset();
+
+ // From the renderer's perspective, the request was cancelled.
+ ResourceIPCAccumulator::ClassifiedMessages msgs;
+ accum_.GetClassifiedMessages(&msgs);
+ ASSERT_EQ(1U, msgs.size());
+ CheckRequestCompleteErrorCode(msgs[0][0], net::ERR_ABORTED);
+
+ // But it continues detached.
+ EXPECT_EQ(1, host_.pending_requests());
+ EXPECT_TRUE(info->detachable_handler()->is_detached());
+
+ // Verify no redirects before resetting the filter.
+ net::URLRequest* url_request = host_.GetURLRequest(global_request_id);
+ EXPECT_EQ(1u, url_request->url_chain().size());
+ KickOffRequest();
+
+ // Verify that a redirect was followed.
+ EXPECT_EQ(2u, url_request->url_chain().size());
+
+ // Make sure the request wasn't canceled early.
+ EXPECT_EQ(1, host_.pending_requests());
+
+ // Finish up the request.
+ while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
+ base::MessageLoop::current()->RunUntilIdle();
+
+ EXPECT_EQ(0, host_.pending_requests());
+ EXPECT_EQ(1, network_delegate()->completed_requests());
+ EXPECT_EQ(0, network_delegate()->canceled_requests());
+ EXPECT_EQ(0, network_delegate()->error_count());
}
TEST_F(ResourceDispatcherHostTest, CancelWhileStartIsDeferred) {
@@ -926,11 +1127,49 @@ TEST_F(ResourceDispatcherHostTest, CancelWhileStartIsDeferred) {
// calling CancelRequest.
EXPECT_FALSE(was_deleted);
- // flush all the pending requests
+ base::MessageLoop::current()->RunUntilIdle();
+
+ EXPECT_TRUE(was_deleted);
+}
+
+TEST_F(ResourceDispatcherHostTest, DetachWhileStartIsDeferred) {
+ bool was_deleted = false;
+
+ // Arrange to have requests deferred before starting.
+ TestResourceDispatcherHostDelegate delegate;
+ delegate.set_flags(DEFER_STARTING_REQUEST);
+ delegate.set_url_request_user_data(new TestUserData(&was_deleted));
+ host_.SetDelegate(&delegate);
+
+ MakeTestRequestWithResourceType(filter_.get(), 0, 1,
+ net::URLRequestTestJob::test_url_1(),
+ ResourceType::PREFETCH); // detachable type
+ // Cancel request must come from the renderer for a detachable resource to
+ // detach.
+ host_.CancelRequest(filter_->child_id(), 1, true);
+
+ // Even after driving the event loop, the request has not been deleted.
+ EXPECT_FALSE(was_deleted);
+
+ // However, it is still throttled because the defer happened above the
+ // DetachableResourceHandler.
while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_FALSE(was_deleted);
+
+ // Resume the request.
+ GenericResourceThrottle* throttle =
+ GenericResourceThrottle::active_throttle();
+ ASSERT_TRUE(throttle);
+ throttle->Resume();
+ // Now, the request completes.
+ while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
+ base::MessageLoop::current()->RunUntilIdle();
EXPECT_TRUE(was_deleted);
+ EXPECT_EQ(1, network_delegate()->completed_requests());
+ EXPECT_EQ(0, network_delegate()->canceled_requests());
+ EXPECT_EQ(0, network_delegate()->error_count());
}
// Tests if cancel is called in ResourceThrottle::WillStartRequest, then the
@@ -951,7 +1190,7 @@ TEST_F(ResourceDispatcherHostTest, CancelInResourceThrottleWillStartRequest) {
// Check that request got canceled.
ASSERT_EQ(1U, msgs[0].size());
- CheckCancelledRequestCompleteMessage(msgs[0][0]);
+ CheckRequestCompleteErrorCode(msgs[0][0], net::ERR_ABORTED);
// Make sure URLRequest is never started.
EXPECT_EQ(0, url_request_jobs_created_count_);
@@ -1031,16 +1270,8 @@ TEST_F(ResourceDispatcherHostTest, CancelInDelegate) {
// Check the cancellation
ASSERT_EQ(1U, msgs.size());
ASSERT_EQ(1U, msgs[0].size());
- ASSERT_EQ(ResourceMsg_RequestComplete::ID, msgs[0][0].type());
-
- int request_id;
- int error_code;
- PickleIterator iter(msgs[0][0]);
- ASSERT_TRUE(IPC::ReadParam(&msgs[0][0], &iter, &request_id));
- ASSERT_TRUE(IPC::ReadParam(&msgs[0][0], &iter, &error_code));
-
- EXPECT_EQ(net::ERR_ACCESS_DENIED, error_code);
+ CheckRequestCompleteErrorCode(msgs[0][0], net::ERR_ACCESS_DENIED);
}
// The host delegate acts as a second one so we can have some requests
@@ -1078,15 +1309,23 @@ TEST_F(ResourceDispatcherHostTest, TestProcessCancel) {
ResourceHostMsg_Request request = CreateResourceRequest(
"GET", ResourceType::SUB_RESOURCE, net::URLRequestTestJob::test_url_1());
- MakeTestRequest(test_filter.get(), 0, 1,
- net::URLRequestTestJob::test_url_1());
+ MakeTestRequestWithResourceType(test_filter.get(), 0, 1,
+ net::URLRequestTestJob::test_url_1(),
+ ResourceType::SUB_RESOURCE);
// request 2 goes to us
MakeTestRequest(0, 2, net::URLRequestTestJob::test_url_2());
// request 3 goes to the test delegate
- MakeTestRequest(test_filter.get(), 0, 3,
- net::URLRequestTestJob::test_url_3());
+ MakeTestRequestWithResourceType(test_filter.get(), 0, 3,
+ net::URLRequestTestJob::test_url_3(),
+ ResourceType::SUB_RESOURCE);
+
+ // request 4 goes to us
+ MakeTestRequestWithResourceType(filter_.get(), 0, 4,
+ net::URLRequestTestJob::test_url_4(),
+ ResourceType::PREFETCH); // detachable type
+
// Make sure all requests have finished stage one. test_url_1 will have
// finished.
@@ -1098,7 +1337,9 @@ TEST_F(ResourceDispatcherHostTest, TestProcessCancel) {
// breaks the whole test.
//EXPECT_EQ(3, host_.pending_requests());
- // Process each request for one level so one callback is called.
+ // Process test_url_2 and test_url_3 for one level so one callback is called.
+ // We'll cancel test_url_4 (detachable) before processing it to verify that it
+ // delays the cancel.
for (int i = 0; i < 2; i++)
EXPECT_TRUE(net::URLRequestTestJob::ProcessOnePendingMessage());
@@ -1106,6 +1347,13 @@ TEST_F(ResourceDispatcherHostTest, TestProcessCancel) {
host_.CancelRequestsForProcess(filter_->child_id());
test_filter->has_canceled_ = true;
+ // The requests should all be cancelled, except request 4, which is detached.
+ EXPECT_EQ(1, host_.pending_requests());
+ GlobalRequestID global_request_id(filter_->child_id(), 4);
+ ResourceRequestInfoImpl* info = ResourceRequestInfoImpl::ForRequest(
+ host_.GetURLRequest(global_request_id));
+ ASSERT_TRUE(info->detachable_handler()->is_detached());
+
// Flush all the pending requests.
while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
@@ -1114,11 +1362,65 @@ TEST_F(ResourceDispatcherHostTest, TestProcessCancel) {
// The test delegate should not have gotten any messages after being canceled.
ASSERT_EQ(0, test_filter->received_after_canceled_);
- // We should have gotten exactly one result.
+ // There should be two results.
ResourceIPCAccumulator::ClassifiedMessages msgs;
accum_.GetClassifiedMessages(&msgs);
- ASSERT_EQ(1U, msgs.size());
+ ASSERT_EQ(2U, msgs.size());
CheckSuccessfulRequest(msgs[0], net::URLRequestTestJob::test_data_2());
+ // The detachable request was cancelled by the renderer before it
+ // finished. From the perspective of the renderer, it should have cancelled.
+ ASSERT_EQ(2U, msgs[1].size());
+ ASSERT_EQ(ResourceMsg_ReceivedResponse::ID, msgs[1][0].type());
+ CheckRequestCompleteErrorCode(msgs[1][1], net::ERR_ABORTED);
+ // But it completed anyway. For the network stack, no requests were canceled.
+ EXPECT_EQ(4, network_delegate()->completed_requests());
+ EXPECT_EQ(0, network_delegate()->canceled_requests());
+ EXPECT_EQ(0, network_delegate()->error_count());
+}
+
+TEST_F(ResourceDispatcherHostTest, TestProcessCancelDetachedTimesOut) {
+ MakeTestRequestWithResourceType(filter_.get(), 0, 1,
+ net::URLRequestTestJob::test_url_4(),
+ ResourceType::PREFETCH); // detachable type
+ GlobalRequestID global_request_id(filter_->child_id(), 1);
+ ResourceRequestInfoImpl* info = ResourceRequestInfoImpl::ForRequest(
+ host_.GetURLRequest(global_request_id));
+ ASSERT_TRUE(info->detachable_handler());
+ info->detachable_handler()->set_cancel_delay(
+ base::TimeDelta::FromMilliseconds(200));
+ base::MessageLoop::current()->RunUntilIdle();
+
+ // Cancel the requests to the test process.
+ host_.CancelRequestsForProcess(filter_->child_id());
+ EXPECT_EQ(1, host_.pending_requests());
+
+ // Wait until after the delay timer times out before we start processing any
+ // messages.
+ base::OneShotTimer<base::MessageLoop> timer;
+ timer.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(210),
+ base::MessageLoop::current(), &base::MessageLoop::QuitWhenIdle);
+ base::MessageLoop::current()->Run();
+
+ // The prefetch should be cancelled by now.
+ EXPECT_EQ(0, host_.pending_requests());
+
+ // In case any messages are still to be processed.
+ while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
+ base::MessageLoop::current()->RunUntilIdle();
+
+ ResourceIPCAccumulator::ClassifiedMessages msgs;
+ accum_.GetClassifiedMessages(&msgs);
+
+ ASSERT_EQ(1U, msgs.size());
+
+ // The request should have cancelled.
+ ASSERT_EQ(2U, msgs[0].size());
+ ASSERT_EQ(ResourceMsg_ReceivedResponse::ID, msgs[0][0].type());
+ CheckRequestCompleteErrorCode(msgs[0][1], net::ERR_ABORTED);
+ // And not run to completion.
+ EXPECT_EQ(1, network_delegate()->completed_requests());
+ EXPECT_EQ(1, network_delegate()->canceled_requests());
+ EXPECT_EQ(0, network_delegate()->error_count());
}
// Tests blocking and resuming requests.
@@ -1187,6 +1489,10 @@ TEST_F(ResourceDispatcherHostTest, TestBlockingCancelingRequests) {
MakeTestRequest(1, 2, net::URLRequestTestJob::test_url_2());
MakeTestRequest(0, 3, net::URLRequestTestJob::test_url_3());
MakeTestRequest(1, 4, net::URLRequestTestJob::test_url_1());
+ // Blocked detachable resources should not delay cancellation.
+ MakeTestRequestWithResourceType(filter_.get(), 1, 5,
+ net::URLRequestTestJob::test_url_4(),
+ ResourceType::PREFETCH); // detachable type
// Flush all the pending requests.
while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
@@ -1219,12 +1525,21 @@ TEST_F(ResourceDispatcherHostTest, TestBlockedRequestsProcessDies) {
host_.BlockRequestsForRoute(second_filter->child_id(), 0);
- MakeTestRequest(filter_.get(), 0, 1, net::URLRequestTestJob::test_url_1());
- MakeTestRequest(second_filter.get(), 0, 2,
- net::URLRequestTestJob::test_url_2());
- MakeTestRequest(filter_.get(), 0, 3, net::URLRequestTestJob::test_url_3());
- MakeTestRequest(second_filter.get(), 0, 4,
- net::URLRequestTestJob::test_url_1());
+ MakeTestRequestWithResourceType(filter_.get(), 0, 1,
+ net::URLRequestTestJob::test_url_1(),
+ ResourceType::SUB_RESOURCE);
+ MakeTestRequestWithResourceType(second_filter.get(), 0, 2,
+ net::URLRequestTestJob::test_url_2(),
+ ResourceType::SUB_RESOURCE);
+ MakeTestRequestWithResourceType(filter_.get(), 0, 3,
+ net::URLRequestTestJob::test_url_3(),
+ ResourceType::SUB_RESOURCE);
+ MakeTestRequestWithResourceType(second_filter.get(), 0, 4,
+ net::URLRequestTestJob::test_url_1(),
+ ResourceType::SUB_RESOURCE);
+ MakeTestRequestWithResourceType(second_filter.get(), 0, 5,
+ net::URLRequestTestJob::test_url_4(),
+ ResourceType::PREFETCH); // detachable type
// Simulate process death.
host_.CancelRequestsForProcess(second_filter->child_id());
@@ -1236,7 +1551,8 @@ TEST_F(ResourceDispatcherHostTest, TestBlockedRequestsProcessDies) {
ResourceIPCAccumulator::ClassifiedMessages msgs;
accum_.GetClassifiedMessages(&msgs);
- // The 2 requests for the RVH 0 should have been processed.
+ // The 2 requests for the RVH 0 should have been processed. Note that
+ // blocked detachable requests are canceled without delay.
ASSERT_EQ(2U, msgs.size());
CheckSuccessfulRequest(msgs[0], net::URLRequestTestJob::test_data_1());
@@ -1258,13 +1574,30 @@ TEST_F(ResourceDispatcherHostTest, TestBlockedRequestsDontLeak) {
host_.BlockRequestsForRoute(filter_->child_id(), 2);
host_.BlockRequestsForRoute(second_filter->child_id(), 1);
- MakeTestRequest(filter_.get(), 0, 1, net::URLRequestTestJob::test_url_1());
- MakeTestRequest(filter_.get(), 1, 2, net::URLRequestTestJob::test_url_2());
- MakeTestRequest(filter_.get(), 0, 3, net::URLRequestTestJob::test_url_3());
- MakeTestRequest(second_filter.get(), 1, 4,
- net::URLRequestTestJob::test_url_1());
- MakeTestRequest(filter_.get(), 2, 5, net::URLRequestTestJob::test_url_2());
- MakeTestRequest(filter_.get(), 2, 6, net::URLRequestTestJob::test_url_3());
+ MakeTestRequestWithResourceType(filter_.get(), 0, 1,
+ net::URLRequestTestJob::test_url_1(),
+ ResourceType::SUB_RESOURCE);
+ MakeTestRequestWithResourceType(filter_.get(), 1, 2,
+ net::URLRequestTestJob::test_url_2(),
+ ResourceType::SUB_RESOURCE);
+ MakeTestRequestWithResourceType(filter_.get(), 0, 3,
+ net::URLRequestTestJob::test_url_3(),
+ ResourceType::SUB_RESOURCE);
+ MakeTestRequestWithResourceType(second_filter.get(), 1, 4,
+ net::URLRequestTestJob::test_url_1(),
+ ResourceType::SUB_RESOURCE);
+ MakeTestRequestWithResourceType(filter_.get(), 2, 5,
+ net::URLRequestTestJob::test_url_2(),
+ ResourceType::SUB_RESOURCE);
+ MakeTestRequestWithResourceType(filter_.get(), 2, 6,
+ net::URLRequestTestJob::test_url_3(),
+ ResourceType::SUB_RESOURCE);
+ MakeTestRequestWithResourceType(filter_.get(), 0, 7,
+ net::URLRequestTestJob::test_url_4(),
+ ResourceType::PREFETCH); // detachable type
+ MakeTestRequestWithResourceType(second_filter.get(), 1, 8,
+ net::URLRequestTestJob::test_url_4(),
+ ResourceType::PREFETCH); // detachable type
host_.CancelRequestsForProcess(filter_->child_id());
host_.CancelRequestsForProcess(second_filter->child_id());
@@ -1324,22 +1657,27 @@ TEST_F(ResourceDispatcherHostTest, TooMuchOutstandingRequestsMemory) {
// Saturate the number of outstanding requests for our process.
for (size_t i = 0; i < kMaxRequests; ++i) {
- MakeTestRequest(filter_.get(), 0, i + 1,
- net::URLRequestTestJob::test_url_2());
+ MakeTestRequestWithResourceType(filter_.get(), 0, i + 1,
+ net::URLRequestTestJob::test_url_2(),
+ ResourceType::SUB_RESOURCE);
}
// Issue two more requests for our process -- these should fail immediately.
- MakeTestRequest(filter_.get(), 0, kMaxRequests + 1,
- net::URLRequestTestJob::test_url_2());
- MakeTestRequest(filter_.get(), 0, kMaxRequests + 2,
- net::URLRequestTestJob::test_url_2());
+ MakeTestRequestWithResourceType(filter_.get(), 0, kMaxRequests + 1,
+ net::URLRequestTestJob::test_url_2(),
+ ResourceType::SUB_RESOURCE);
+ MakeTestRequestWithResourceType(filter_.get(), 0, kMaxRequests + 2,
+ net::URLRequestTestJob::test_url_2(),
+ ResourceType::SUB_RESOURCE);
// Issue two requests for the second process -- these should succeed since
// it is just process 0 that is saturated.
- MakeTestRequest(second_filter.get(), 0, kMaxRequests + 3,
- net::URLRequestTestJob::test_url_2());
- MakeTestRequest(second_filter.get(), 0, kMaxRequests + 4,
- net::URLRequestTestJob::test_url_2());
+ MakeTestRequestWithResourceType(second_filter.get(), 0, kMaxRequests + 3,
+ net::URLRequestTestJob::test_url_2(),
+ ResourceType::SUB_RESOURCE);
+ MakeTestRequestWithResourceType(second_filter.get(), 0, kMaxRequests + 4,
+ net::URLRequestTestJob::test_url_2(),
+ ResourceType::SUB_RESOURCE);
// Flush all the pending requests.
while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
@@ -1390,23 +1728,27 @@ TEST_F(ResourceDispatcherHostTest, TooManyOutstandingRequests) {
// Saturate the number of outstanding requests for our process.
for (size_t i = 0; i < kMaxRequestsPerProcess; ++i) {
- MakeTestRequest(filter_.get(), 0, i + 1,
- net::URLRequestTestJob::test_url_2());
+ MakeTestRequestWithResourceType(filter_.get(), 0, i + 1,
+ net::URLRequestTestJob::test_url_2(),
+ ResourceType::SUB_RESOURCE);
}
// Issue another request for our process -- this should fail immediately.
- MakeTestRequest(filter_.get(), 0, kMaxRequestsPerProcess + 1,
- net::URLRequestTestJob::test_url_2());
+ MakeTestRequestWithResourceType(filter_.get(), 0, kMaxRequestsPerProcess + 1,
+ net::URLRequestTestJob::test_url_2(),
+ ResourceType::SUB_RESOURCE);
// Issue a request for the second process -- this should succeed, because it
// is just process 0 that is saturated.
- MakeTestRequest(second_filter.get(), 0, kMaxRequestsPerProcess + 2,
- net::URLRequestTestJob::test_url_2());
+ MakeTestRequestWithResourceType(
+ second_filter.get(), 0, kMaxRequestsPerProcess + 2,
+ net::URLRequestTestJob::test_url_2(), ResourceType::SUB_RESOURCE);
// Issue a request for the third process -- this should fail, because the
// global limit has been reached.
- MakeTestRequest(third_filter.get(), 0, kMaxRequestsPerProcess + 3,
- net::URLRequestTestJob::test_url_2());
+ MakeTestRequestWithResourceType(
+ third_filter.get(), 0, kMaxRequestsPerProcess + 3,
+ net::URLRequestTestJob::test_url_2(), ResourceType::SUB_RESOURCE);
// Flush all the pending requests.
while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
@@ -1546,11 +1888,11 @@ TEST_F(ResourceDispatcherHostTest, ForbiddenDownload) {
std::string response_data("<html><title>Test One</title></html>");
SetResponse(raw_headers, response_data);
- // Only MAIN_FRAMEs can trigger a download.
- SetResourceType(ResourceType::MAIN_FRAME);
-
HandleScheme("http");
- MakeTestRequest(0, 1, GURL("http:bla"));
+
+ // Only MAIN_FRAMEs can trigger a download.
+ MakeTestRequestWithResourceType(filter_.get(), 0, 1, GURL("http:bla"),
+ ResourceType::MAIN_FRAME);
// Flush all pending requests.
while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
@@ -1565,15 +1907,7 @@ TEST_F(ResourceDispatcherHostTest, ForbiddenDownload) {
// The RequestComplete message should have had the error code of
// ERR_FILE_NOT_FOUND.
- int request_id;
- int error_code;
-
- PickleIterator iter(msgs[0][0]);
- EXPECT_TRUE(IPC::ReadParam(&msgs[0][0], &iter, &request_id));
- EXPECT_TRUE(IPC::ReadParam(&msgs[0][0], &iter, &error_code));
-
- EXPECT_EQ(1, request_id);
- EXPECT_EQ(net::ERR_FILE_NOT_FOUND, error_code);
+ CheckRequestCompleteErrorCode(msgs[0][0], net::ERR_FILE_NOT_FOUND);
}
// Test for http://crbug.com/76202 . We don't want to destroy a
@@ -1597,11 +1931,12 @@ TEST_F(ResourceDispatcherHostTest, IgnoreCancelForDownloads) {
response_data.resize(1025, ' ');
SetResponse(raw_headers, response_data);
- SetResourceType(ResourceType::MAIN_FRAME);
SetDelayedCompleteJobGeneration(true);
HandleScheme("http");
- MakeTestRequest(render_view_id, request_id, GURL("http://example.com/blah"));
+ MakeTestRequestWithResourceType(filter_.get(), render_view_id, request_id,
+ GURL("http://example.com/blah"),
+ ResourceType::MAIN_FRAME);
// Return some data so that the request is identified as a download
// and the proper resource handlers are created.
EXPECT_TRUE(net::URLRequestTestJob::ProcessOnePendingMessage());
@@ -1632,11 +1967,12 @@ TEST_F(ResourceDispatcherHostTest, CancelRequestsForContext) {
response_data.resize(1025, ' ');
SetResponse(raw_headers, response_data);
- SetResourceType(ResourceType::MAIN_FRAME);
SetDelayedCompleteJobGeneration(true);
HandleScheme("http");
- MakeTestRequest(render_view_id, request_id, GURL("http://example.com/blah"));
+ MakeTestRequestWithResourceType(filter_.get(), render_view_id, request_id,
+ GURL("http://example.com/blah"),
+ ResourceType::MAIN_FRAME);
// Return some data so that the request is identified as a download
// and the proper resource handlers are created.
EXPECT_TRUE(net::URLRequestTestJob::ProcessOnePendingMessage());
@@ -1660,6 +1996,33 @@ TEST_F(ResourceDispatcherHostTest, CancelRequestsForContext) {
EXPECT_EQ(0, host_.pending_requests());
}
+TEST_F(ResourceDispatcherHostTest, CancelRequestsForContextDetached) {
+ EXPECT_EQ(0, host_.pending_requests());
+
+ int render_view_id = 0;
+ int request_id = 1;
+
+ MakeTestRequestWithResourceType(filter_.get(), render_view_id, request_id,
+ net::URLRequestTestJob::test_url_4(),
+ ResourceType::PREFETCH); // detachable type
+
+ // Simulate a cancel coming from the renderer.
+ host_.CancelRequest(filter_->child_id(), request_id, true);
+
+ // Since the request had already started processing as detachable,
+ // the cancellation above should have been ignored and the request
+ // should have been detached.
+ EXPECT_EQ(1, host_.pending_requests());
+
+ // Cancelling by other methods should also leave it detached.
+ host_.CancelRequestsForProcess(render_view_id);
+ EXPECT_EQ(1, host_.pending_requests());
+
+ // Cancelling by context should work.
+ host_.CancelRequestsForContext(filter_->resource_context());
+ EXPECT_EQ(0, host_.pending_requests());
+}
+
// Test the cancelling of requests that are being transferred to a new renderer
// due to a redirection.
TEST_F(ResourceDispatcherHostTest, CancelRequestsForContextTransferred) {
@@ -1673,10 +2036,12 @@ TEST_F(ResourceDispatcherHostTest, CancelRequestsForContextTransferred) {
std::string response_data("<html>foobar</html>");
SetResponse(raw_headers, response_data);
- SetResourceType(ResourceType::MAIN_FRAME);
HandleScheme("http");
- MakeTestRequest(render_view_id, request_id, GURL("http://example.com/blah"));
+ MakeTestRequestWithResourceType(filter_.get(), render_view_id, request_id,
+ GURL("http://example.com/blah"),
+ ResourceType::MAIN_FRAME);
+
GlobalRequestID global_request_id(filter_->child_id(), request_id);
host_.MarkAsTransferredNavigation(global_request_id,
@@ -1713,7 +2078,6 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationHtml) {
SetResponse("HTTP/1.1 302 Found\n"
"Location: http://other.com/blech\n\n");
- SetResourceType(ResourceType::MAIN_FRAME);
HandleScheme("http");
// Temporarily replace ContentBrowserClient with one that will trigger the
@@ -1721,7 +2085,9 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationHtml) {
TransfersAllNavigationsContentBrowserClient new_client;
ContentBrowserClient* old_client = SetBrowserClientForTesting(&new_client);
- MakeTestRequest(render_view_id, request_id, GURL("http://example.com/blah"));
+ MakeTestRequestWithResourceType(filter_.get(), render_view_id, request_id,
+ GURL("http://example.com/blah"),
+ ResourceType::MAIN_FRAME);
// Now that we're blocked on the redirect, update the response and unblock by
// telling the AsyncResourceHandler to follow the redirect.
@@ -1784,7 +2150,6 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationText) {
SetResponse("HTTP/1.1 302 Found\n"
"Location: http://other.com/blech\n\n");
- SetResourceType(ResourceType::MAIN_FRAME);
HandleScheme("http");
// Temporarily replace ContentBrowserClient with one that will trigger the
@@ -1792,7 +2157,9 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationText) {
TransfersAllNavigationsContentBrowserClient new_client;
ContentBrowserClient* old_client = SetBrowserClientForTesting(&new_client);
- MakeTestRequest(render_view_id, request_id, GURL("http://example.com/blah"));
+ MakeTestRequestWithResourceType(filter_.get(), render_view_id, request_id,
+ GURL("http://example.com/blah"),
+ ResourceType::MAIN_FRAME);
// Now that we're blocked on the redirect, update the response and unblock by
// telling the AsyncResourceHandler to follow the redirect. Use a text/plain
@@ -1856,7 +2223,6 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationWithProcessCrash) {
"Location: http://other.com/blech\n\n");
const std::string kResponseBody = "hello world";
- SetResourceType(ResourceType::MAIN_FRAME);
HandleScheme("http");
// Temporarily replace ContentBrowserClient with one that will trigger the
@@ -1945,7 +2311,6 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationWithTwoRedirects) {
SetResponse("HTTP/1.1 302 Found\n"
"Location: http://other.com/blech\n\n");
- SetResourceType(ResourceType::MAIN_FRAME);
HandleScheme("http");
// Temporarily replace ContentBrowserClient with one that will trigger the
@@ -1953,7 +2318,9 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationWithTwoRedirects) {
TransfersAllNavigationsContentBrowserClient new_client;
ContentBrowserClient* old_client = SetBrowserClientForTesting(&new_client);
- MakeTestRequest(render_view_id, request_id, GURL("http://example.com/blah"));
+ MakeTestRequestWithResourceType(filter_.get(), render_view_id, request_id,
+ GURL("http://example.com/blah"),
+ ResourceType::MAIN_FRAME);
// Now that we're blocked on the redirect, simulate hitting another redirect.
SetResponse("HTTP/1.1 302 Found\n"
@@ -2026,10 +2393,10 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationWithTwoRedirects) {
TEST_F(ResourceDispatcherHostTest, UnknownURLScheme) {
EXPECT_EQ(0, host_.pending_requests());
- SetResourceType(ResourceType::MAIN_FRAME);
HandleScheme("http");
- MakeTestRequest(0, 1, GURL("foo://bar"));
+ MakeTestRequestWithResourceType(filter_.get(), 0, 1, GURL("foo://bar"),
+ ResourceType::MAIN_FRAME);
// Flush all pending requests.
while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
@@ -2044,15 +2411,7 @@ TEST_F(ResourceDispatcherHostTest, UnknownURLScheme) {
// The RequestComplete message should have the error code of
// ERR_UNKNOWN_URL_SCHEME.
- int request_id;
- int error_code;
-
- PickleIterator iter(msgs[0][0]);
- EXPECT_TRUE(IPC::ReadParam(&msgs[0][0], &iter, &request_id));
- EXPECT_TRUE(IPC::ReadParam(&msgs[0][0], &iter, &error_code));
-
- EXPECT_EQ(1, request_id);
- EXPECT_EQ(net::ERR_UNKNOWN_URL_SCHEME, error_code);
+ CheckRequestCompleteErrorCode(msgs[0][0], net::ERR_UNKNOWN_URL_SCHEME);
}
TEST_F(ResourceDispatcherHostTest, DataReceivedACKs) {
@@ -2076,6 +2435,65 @@ TEST_F(ResourceDispatcherHostTest, DataReceivedACKs) {
EXPECT_EQ(ResourceMsg_RequestComplete::ID, msgs[0][size - 1].type());
}
+// Request a very large detachable resource and cancel part way. Some of the
+// data should have been sent to the renderer, but not all.
+TEST_F(ResourceDispatcherHostTest, DataSentBeforeDetach) {
+ EXPECT_EQ(0, host_.pending_requests());
+
+ int render_view_id = 0;
+ int request_id = 1;
+
+ std::string raw_headers("HTTP\n"
+ "Content-type: image/jpeg\n\n");
+ std::string response_data("01234567890123456789\x01foobar");
+
+ // Create a response larger than kMaxAllocationSize (currently 32K). Note
+ // that if this increase beyond 512K we'll need to make the response longer.
+ const int kAllocSize = 1024*512;
+ response_data.resize(kAllocSize, ' ');
+
+ SetResponse(raw_headers, response_data);
+ SetDelayedCompleteJobGeneration(true);
+ HandleScheme("http");
+
+ MakeTestRequestWithResourceType(filter_.get(), render_view_id, request_id,
+ GURL("http://example.com/blah"),
+ ResourceType::PREFETCH);
+
+ // Get a bit of data before cancelling.
+ EXPECT_TRUE(net::URLRequestTestJob::ProcessOnePendingMessage());
+
+ // Simulate a cancellation coming from the renderer.
+ ResourceHostMsg_CancelRequest msg(request_id);
+ bool msg_was_ok;
+ host_.OnMessageReceived(msg, filter_.get(), &msg_was_ok);
+
+ EXPECT_EQ(1, host_.pending_requests());
+
+ while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
+
+ // Sort all the messages we saw by request.
+ ResourceIPCAccumulator::ClassifiedMessages msgs;
+ accum_.GetClassifiedMessages(&msgs);
+
+ EXPECT_EQ(4U, msgs[0].size());
+
+ // Figure out how many bytes were received by the renderer.
+ int data_offset;
+ int data_length;
+ ASSERT_TRUE(
+ ExtractDataOffsetAndLength(msgs[0][2], &data_offset, &data_length));
+ EXPECT_LT(0, data_length);
+ EXPECT_GT(kAllocSize, data_length);
+
+ // Verify the data that was received before cancellation. The request should
+ // have appeared to cancel, however.
+ CheckSuccessfulRequestWithErrorCode(
+ msgs[0],
+ std::string(response_data.begin(), response_data.begin() + data_length),
+ net::ERR_ABORTED);
+}
+
TEST_F(ResourceDispatcherHostTest, DelayedDataReceivedACKs) {
EXPECT_EQ(0, host_.pending_requests());
diff --git a/content/browser/loader/resource_loader.cc b/content/browser/loader/resource_loader.cc
index 94561ba..4993c82 100644
--- a/content/browser/loader/resource_loader.cc
+++ b/content/browser/loader/resource_loader.cc
@@ -10,6 +10,7 @@
#include "base/time/time.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/loader/cross_site_resource_handler.h"
+#include "content/browser/loader/detachable_resource_handler.h"
#include "content/browser/loader/resource_loader_delegate.h"
#include "content/browser/loader/resource_request_info_impl.h"
#include "content/browser/ssl/ssl_client_auth_handler.h"
@@ -439,6 +440,13 @@ void ResourceLoader::CancelRequestInternal(int error, bool from_renderer) {
if (from_renderer && (info->is_download() || info->is_stream()))
return;
+ if (from_renderer && info->detachable_handler()) {
+ // TODO(davidben): Fix Blink handling of prefetches so they are not
+ // cancelled on navigate away and end up in the local cache.
+ info->detachable_handler()->Detach();
+ return;
+ }
+
// TODO(darin): Perhaps we should really be looking to see if the status is
// IO_PENDING?
bool was_pending = request_->is_pending();
diff --git a/content/browser/loader/resource_request_info_impl.cc b/content/browser/loader/resource_request_info_impl.cc
index c7eed3b..ddd4f5e 100644
--- a/content/browser/loader/resource_request_info_impl.cc
+++ b/content/browser/loader/resource_request_info_impl.cc
@@ -106,6 +106,7 @@ ResourceRequestInfoImpl::ResourceRequestInfoImpl(
base::WeakPtr<ResourceMessageFilter> filter,
bool is_async)
: cross_site_handler_(NULL),
+ detachable_handler_(NULL),
process_type_(process_type),
child_id_(child_id),
route_id_(route_id),
diff --git a/content/browser/loader/resource_request_info_impl.h b/content/browser/loader/resource_request_info_impl.h
index b6f8563..0db6259 100644
--- a/content/browser/loader/resource_request_info_impl.h
+++ b/content/browser/loader/resource_request_info_impl.h
@@ -8,6 +8,7 @@
#include <string>
#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
@@ -19,6 +20,7 @@
namespace content {
class CrossSiteResourceHandler;
+class DetachableResourceHandler;
class ResourceContext;
class ResourceMessageFilter;
struct GlobalRequestID;
@@ -117,6 +119,14 @@ class ResourceRequestInfoImpl : public ResourceRequestInfo,
return should_replace_current_entry_;
}
+ // DetachableResourceHandler for this request. May be NULL.
+ DetachableResourceHandler* detachable_handler() const {
+ return detachable_handler_;
+ }
+ void set_detachable_handler(DetachableResourceHandler* h) {
+ detachable_handler_ = h;
+ }
+
// Identifies the type of process (renderer, plugin, etc.) making the request.
int process_type() const { return process_type_; }
@@ -141,8 +151,13 @@ class ResourceRequestInfoImpl : public ResourceRequestInfo,
void set_memory_cost(int cost) { memory_cost_ = cost; }
private:
+ FRIEND_TEST_ALL_PREFIXES(ResourceDispatcherHostTest,
+ DeletedFilterDetached);
+ FRIEND_TEST_ALL_PREFIXES(ResourceDispatcherHostTest,
+ DeletedFilterDetachedRedirect);
// Non-owning, may be NULL.
CrossSiteResourceHandler* cross_site_handler_;
+ DetachableResourceHandler* detachable_handler_;
int process_type_;
int child_id_;
diff --git a/content/content_browser.gypi b/content/content_browser.gypi
index ff24f75..e303df1 100644
--- a/content/content_browser.gypi
+++ b/content/content_browser.gypi
@@ -661,6 +661,8 @@
'browser/loader/certificate_resource_handler.h',
'browser/loader/cross_site_resource_handler.cc',
'browser/loader/cross_site_resource_handler.h',
+ 'browser/loader/detachable_resource_handler.cc',
+ 'browser/loader/detachable_resource_handler.h',
'browser/loader/global_routing_id.h',
'browser/loader/layered_resource_handler.cc',
'browser/loader/layered_resource_handler.h',