Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / content / browser / loader / resource_dispatcher_host_unittest.cc
index a2a34d8..c798b19 100644 (file)
@@ -4,9 +4,12 @@
 
 #include <vector>
 
+#include "base/basictypes.h"
 #include "base/bind.h"
+#include "base/file_util.h"
 #include "base/files/file_path.h"
 #include "base/memory/scoped_vector.h"
+#include "base/memory/shared_memory.h"
 #include "base/message_loop/message_loop.h"
 #include "base/pickle.h"
 #include "base/run_loop.h"
 #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/cross_site_resource_handler.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"
+#include "content/common/appcache_interfaces.h"
 #include "content/common/child_process_host_impl.h"
 #include "content/common/resource_messages.h"
 #include "content/common/view_messages.h"
@@ -29,6 +35,7 @@
 #include "content/public/common/process_type.h"
 #include "content/public/common/resource_response.h"
 #include "content/public/test/test_browser_context.h"
+#include "content/public/test/test_browser_thread_bundle.h"
 #include "content/test/test_content_browser_client.h"
 #include "net/base/net_errors.h"
 #include "net/base/request_priority.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_job.h"
+#include "net/url_request/url_request_job_factory.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"
+#include "webkit/common/blob/shareable_file_reference.h"
 
 // TODO(eroman): Write unit tests for SafeBrowsing that exercise
 //               SafeBrowsingResourceHandler.
 
+using webkit_blob::ShareableFileReference;
+
 namespace content {
 
 namespace {
@@ -67,9 +78,31 @@ void GetResponseHead(const std::vector<IPC::Message>& messages,
 void GenerateIPCMessage(
     scoped_refptr<ResourceMessageFilter> filter,
     scoped_ptr<IPC::Message> message) {
-  bool msg_is_ok;
   ResourceDispatcherHostImpl::Get()->OnMessageReceived(
-      *message, filter.get(), &msg_is_ok);
+      *message, filter.get());
+}
+
+// On Windows, ResourceMsg_SetDataBuffer supplies a HANDLE which is not
+// automatically released.
+//
+// See ResourceDispatcher::ReleaseResourcesInDataMessage.
+//
+// TODO(davidben): It would be nice if the behavior for base::SharedMemoryHandle
+// were more like it is in POSIX where the received fds are tracked in a
+// ref-counted core that closes them if not extracted.
+void ReleaseHandlesInMessage(const IPC::Message& message) {
+  if (message.type() == ResourceMsg_SetDataBuffer::ID) {
+    PickleIterator iter(message);
+    int request_id;
+    CHECK(message.ReadInt(&iter, &request_id));
+    base::SharedMemoryHandle shm_handle;
+    if (IPC::ParamTraits<base::SharedMemoryHandle>::Read(&message,
+                                                         &iter,
+                                                         &shm_handle)) {
+      if (base::SharedMemory::IsHandleValid(shm_handle))
+        base::SharedMemory::CloseHandle(shm_handle);
+    }
+  }
 }
 
 }  // namespace
@@ -82,6 +115,7 @@ static int RequestIDForMessage(const IPC::Message& msg) {
     case ResourceMsg_ReceivedRedirect::ID:
     case ResourceMsg_SetDataBuffer::ID:
     case ResourceMsg_DataReceived::ID:
+    case ResourceMsg_DataDownloaded::ID:
     case ResourceMsg_RequestComplete::ID: {
       bool result = PickleIterator(msg).ReadInt(&request_id);
       DCHECK(result);
@@ -91,25 +125,23 @@ static int RequestIDForMessage(const IPC::Message& msg) {
   return request_id;
 }
 
-static ResourceHostMsg_Request CreateResourceRequest(
-    const char* method,
-    ResourceType::Type type,
-    const GURL& url) {
+static ResourceHostMsg_Request CreateResourceRequest(const char* method,
+                                                     ResourceType type,
+                                                     const GURL& url) {
   ResourceHostMsg_Request request;
   request.method = std::string(method);
   request.url = url;
   request.first_party_for_cookies = url;  // bypass third-party cookie blocking
-  request.referrer_policy = WebKit::WebReferrerPolicyDefault;
+  request.referrer_policy = blink::WebReferrerPolicyDefault;
   request.load_flags = 0;
   request.origin_pid = 0;
   request.resource_type = type;
   request.request_context = 0;
-  request.appcache_host_id = appcache::kNoHostId;
+  request.appcache_host_id = kAppCacheNoHostId;
   request.download_to_file = false;
   request.is_main_frame = true;
-  request.frame_id = 0;
   request.parent_is_main_frame = false;
-  request.parent_frame_id = -1;
+  request.parent_render_frame_id = -1;
   request.transition_type = PAGE_TRANSITION_LINK;
   request.allow_download = true;
   return request;
@@ -123,6 +155,13 @@ static void KickOffRequest() {
 // We may want to move this to a shared space if it is useful for something else
 class ResourceIPCAccumulator {
  public:
+  ~ResourceIPCAccumulator() {
+    for (size_t i = 0; i < messages_.size(); i++) {
+      ReleaseHandlesInMessage(messages_[i]);
+    }
+  }
+
+  // On Windows, takes ownership of SharedMemoryHandles in |msg|.
   void AddMessage(const IPC::Message& msg) {
     messages_.push_back(msg);
   }
@@ -130,7 +169,8 @@ 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_.
+  // Note that this clears messages_. The caller takes ownership of any
+  // SharedMemoryHandles in messages placed into |msgs|.
   typedef std::vector< std::vector<IPC::Message> > ClassifiedMessages;
   void GetClassifiedMessages(ClassifiedMessages* msgs);
 
@@ -163,35 +203,39 @@ void ResourceIPCAccumulator::GetClassifiedMessages(ClassifiedMessages* msgs) {
   }
 }
 
-// This class forwards the incoming messages to the ResourceDispatcherHostTest.
 // This is used to emulate different sub-processes, since this filter will
-// have a different ID than the original. For the test, we want all the incoming
-// messages to go to the same place, which is why this forwards.
-class ForwardingFilter : public ResourceMessageFilter {
+// have a different ID than the original.
+class TestFilter : public ResourceMessageFilter {
  public:
-  explicit ForwardingFilter(IPC::Sender* dest,
-                            ResourceContext* resource_context)
-    : ResourceMessageFilter(
-        ChildProcessHostImpl::GenerateChildProcessUniqueId(),
-        PROCESS_TYPE_RENDERER, NULL, NULL, NULL,
-        base::Bind(&ForwardingFilter::GetContexts,
-                   base::Unretained(this))),
-      dest_(dest),
-      resource_context_(resource_context) {
+  explicit TestFilter(ResourceContext* resource_context)
+      : ResourceMessageFilter(
+            ChildProcessHostImpl::GenerateChildProcessUniqueId(),
+            PROCESS_TYPE_RENDERER, NULL, NULL, NULL, NULL,
+            base::Bind(&TestFilter::GetContexts, base::Unretained(this))),
+        resource_context_(resource_context),
+        canceled_(false),
+        received_after_canceled_(0) {
+    ChildProcessSecurityPolicyImpl::GetInstance()->Add(child_id());
     set_peer_pid_for_testing(base::GetCurrentProcId());
   }
 
+  void set_canceled(bool canceled) { canceled_ = canceled; }
+  int received_after_canceled() const { return received_after_canceled_; }
+
   // ResourceMessageFilter override
   virtual bool Send(IPC::Message* msg) OVERRIDE {
-    if (!dest_)
-      return false;
-    return dest_->Send(msg);
+    // No messages should be received when the process has been canceled.
+    if (canceled_)
+      received_after_canceled_++;
+    ReleaseHandlesInMessage(*msg);
+    delete msg;
+    return true;
   }
 
   ResourceContext* resource_context() { return resource_context_; }
 
  protected:
-  virtual ~ForwardingFilter() {}
+  virtual ~TestFilter() {}
 
  private:
   void GetContexts(const ResourceHostMsg_Request& request,
@@ -201,12 +245,65 @@ class ForwardingFilter : public ResourceMessageFilter {
     *request_context = resource_context_->GetRequestContext();
   }
 
-  IPC::Sender* dest_;
   ResourceContext* resource_context_;
+  bool canceled_;
+  int received_after_canceled_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestFilter);
+};
+
+
+// This class forwards the incoming messages to the ResourceDispatcherHostTest.
+// For the test, we want all the incoming messages to go to the same place,
+// which is why this forwards.
+class ForwardingFilter : public TestFilter {
+ public:
+  explicit ForwardingFilter(IPC::Sender* dest,
+                            ResourceContext* resource_context)
+      : TestFilter(resource_context),
+        dest_(dest) {
+  }
+
+  // TestFilter override
+  virtual bool Send(IPC::Message* msg) OVERRIDE {
+    return dest_->Send(msg);
+  }
+
+ private:
+  virtual ~ForwardingFilter() {}
+
+  IPC::Sender* dest_;
 
   DISALLOW_COPY_AND_ASSIGN(ForwardingFilter);
 };
 
+// This class is a variation on URLRequestTestJob that will call
+// URLRequest::WillStartUsingNetwork before starting.
+class URLRequestTestDelayedNetworkJob : public net::URLRequestTestJob {
+ public:
+  URLRequestTestDelayedNetworkJob(net::URLRequest* request,
+                                  net::NetworkDelegate* network_delegate)
+      : net::URLRequestTestJob(request, network_delegate) {}
+
+  // Only start if not deferred for network start.
+  virtual void Start() OVERRIDE {
+    bool defer = false;
+    NotifyBeforeNetworkStart(&defer);
+    if (defer)
+      return;
+    net::URLRequestTestJob::Start();
+  }
+
+  virtual void ResumeNetworkStart() OVERRIDE {
+    net::URLRequestTestJob::StartAsync();
+  }
+
+ private:
+  virtual ~URLRequestTestDelayedNetworkJob() {}
+
+  DISALLOW_COPY_AND_ASSIGN(URLRequestTestDelayedNetworkJob);
+};
+
 // This class is a variation on URLRequestTestJob in that it does
 // not complete start upon entry, only when specifically told to.
 class URLRequestTestDelayedStartJob : public net::URLRequestTestJob {
@@ -362,6 +459,66 @@ class URLRequestBigJob : public net::URLRequestSimpleJob {
   }
 };
 
+class ResourceDispatcherHostTest;
+
+class TestURLRequestJobFactory : public net::URLRequestJobFactory {
+ public:
+  explicit TestURLRequestJobFactory(ResourceDispatcherHostTest* test_fixture)
+      : test_fixture_(test_fixture),
+        delay_start_(false),
+        delay_complete_(false),
+        network_start_notification_(false),
+        url_request_jobs_created_count_(0) {
+  }
+
+  void HandleScheme(const std::string& scheme) {
+    supported_schemes_.insert(scheme);
+  }
+
+  int url_request_jobs_created_count() const {
+    return url_request_jobs_created_count_;
+  }
+
+  void SetDelayedStartJobGeneration(bool delay_job_start) {
+    delay_start_ = delay_job_start;
+  }
+
+  void SetDelayedCompleteJobGeneration(bool delay_job_complete) {
+    delay_complete_ = delay_job_complete;
+  }
+
+  void SetNetworkStartNotificationJobGeneration(bool notification) {
+    network_start_notification_ = notification;
+  }
+
+  virtual net::URLRequestJob* MaybeCreateJobWithProtocolHandler(
+      const std::string& scheme,
+      net::URLRequest* request,
+      net::NetworkDelegate* network_delegate) const OVERRIDE;
+
+  virtual bool IsHandledProtocol(const std::string& scheme) const OVERRIDE {
+    return supported_schemes_.count(scheme) > 0;
+  }
+
+  virtual bool IsHandledURL(const GURL& url) const OVERRIDE {
+    return supported_schemes_.count(url.scheme()) > 0;
+  }
+
+  virtual bool IsSafeRedirectTarget(const GURL& location) const OVERRIDE {
+    return false;
+  }
+
+ private:
+  ResourceDispatcherHostTest* test_fixture_;
+  bool delay_start_;
+  bool delay_complete_;
+  bool network_start_notification_;
+  mutable int url_request_jobs_created_count_;
+  std::set<std::string> supported_schemes_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestURLRequestJobFactory);
+};
+
 // Associated with an URLRequest to determine if the URLRequest gets deleted.
 class TestUserData : public base::SupportsUserData::Data {
  public:
@@ -391,7 +548,8 @@ enum GenericResourceThrottleFlags {
   NONE                      = 0,
   DEFER_STARTING_REQUEST    = 1 << 0,
   DEFER_PROCESSING_RESPONSE = 1 << 1,
-  CANCEL_BEFORE_START       = 1 << 2
+  CANCEL_BEFORE_START       = 1 << 2,
+  DEFER_NETWORK_START       = 1 << 3
 };
 
 // Throttle that tracks the current throttle blocking a request.  Only one
@@ -438,6 +596,19 @@ class GenericResourceThrottle : public ResourceThrottle {
     }
   }
 
+  virtual void WillStartUsingNetwork(bool* defer) OVERRIDE {
+    ASSERT_EQ(NULL, active_throttle_);
+
+    if (flags_ & DEFER_NETWORK_START) {
+      active_throttle_ = this;
+      *defer = true;
+    }
+  }
+
+  virtual const char* GetNameForLogging() const OVERRIDE {
+    return "GenericResourceThrottle";
+  }
+
   void Resume() {
     ASSERT_TRUE(this == active_throttle_);
     active_throttle_ = NULL;
@@ -489,8 +660,8 @@ class TestResourceDispatcherHostDelegate
   virtual void RequestBeginning(
       net::URLRequest* request,
       ResourceContext* resource_context,
-      appcache::AppCacheService* appcache_service,
-      ResourceType::Type resource_type,
+      AppCacheService* appcache_service,
+      ResourceType resource_type,
       int child_id,
       int route_id,
       ScopedVector<ResourceThrottle>* throttles) OVERRIDE {
@@ -515,29 +686,48 @@ class TestResourceDispatcherHostDelegate
   scoped_ptr<base::SupportsUserData::Data> user_data_;
 };
 
+// Waits for a ShareableFileReference to be released.
+class ShareableFileReleaseWaiter {
+ public:
+  ShareableFileReleaseWaiter(const base::FilePath& path) {
+    scoped_refptr<ShareableFileReference> file =
+        ShareableFileReference::Get(path);
+    file->AddFinalReleaseCallback(
+        base::Bind(&ShareableFileReleaseWaiter::Released,
+                   base::Unretained(this)));
+  }
+
+  void Wait() {
+    loop_.Run();
+  }
+
+ private:
+  void Released(const base::FilePath& path) {
+    loop_.Quit();
+  }
+
+  base::RunLoop loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(ShareableFileReleaseWaiter);
+};
+
 class ResourceDispatcherHostTest : public testing::Test,
                                    public IPC::Sender {
  public:
   ResourceDispatcherHostTest()
-      : ui_thread_(BrowserThread::UI, &message_loop_),
-        file_thread_(BrowserThread::FILE_USER_BLOCKING, &message_loop_),
-        cache_thread_(BrowserThread::CACHE, &message_loop_),
-        io_thread_(BrowserThread::IO, &message_loop_),
+      : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
         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());
-  }
-
-  virtual ~ResourceDispatcherHostTest() {
-    for (std::set<int>::iterator it = child_ids_.begin();
-         it != child_ids_.end(); ++it) {
-      host_.CancelRequestsForProcess(*it);
-    }
+    base::RunLoop().RunUntilIdle();
+    filter_ = MakeForwardingFilter();
+    // TODO(cbentzel): Better way to get URLRequestContext?
+    net::URLRequestContext* request_context =
+        browser_context_->GetResourceContext()->GetRequestContext();
+    job_factory_.reset(new TestURLRequestJobFactory(this));
+    request_context->set_job_factory(job_factory_.get());
+    request_context->set_network_delegate(&network_delegate_);
   }
 
   // IPC::Sender implementation
@@ -549,36 +739,33 @@ class ResourceDispatcherHostTest : public testing::Test,
       GenerateDataReceivedACK(*msg);
     }
 
+    if (wait_for_request_complete_loop_ &&
+        msg->type() == ResourceMsg_RequestComplete::ID) {
+      wait_for_request_complete_loop_->Quit();
+    }
+
+    // Do not release handles in it yet; the accumulator owns them now.
     delete msg;
     return true;
   }
 
  protected:
+  friend class TestURLRequestJobFactory;
+
   // testing::Test
-  virtual void SetUp() {
-    DCHECK(!test_fixture_);
-    test_fixture_ = this;
+  virtual void SetUp() OVERRIDE {
     ChildProcessSecurityPolicyImpl::GetInstance()->Add(0);
-    net::URLRequest::Deprecated::RegisterProtocolFactory(
-        "test",
-        &ResourceDispatcherHostTest::Factory);
-    EnsureTestSchemeIsAllowed();
-    delay_start_ = false;
-    delay_complete_ = false;
-    url_request_jobs_created_count_ = 0;
+    HandleScheme("test");
   }
 
   virtual void TearDown() {
-    net::URLRequest::Deprecated::RegisterProtocolFactory("test", NULL);
-    if (!scheme_.empty())
-      net::URLRequest::Deprecated::RegisterProtocolFactory(
-          scheme_, old_factory_);
-
     EXPECT_TRUE(URLRequestTestDelayedStartJob::DelayedStartQueueEmpty());
     URLRequestTestDelayedStartJob::ClearQueue();
 
-    DCHECK(test_fixture_ == this);
-    test_fixture_ = NULL;
+    for (std::set<int>::iterator it = child_ids_.begin();
+         it != child_ids_.end(); ++it) {
+      host_.CancelRequestsForProcess(*it);
+    }
 
     host_.Shutdown();
 
@@ -589,29 +776,44 @@ class ResourceDispatcherHostTest : public testing::Test,
       ResourceDispatcherHostImpl::Get()->CancelRequestsForContext(
           browser_context_->GetResourceContext());
 
-    WorkerServiceImpl::GetInstance()->PerformTeardownForTesting();
-
     browser_context_.reset();
-    message_loop_.RunUntilIdle();
+    base::RunLoop().RunUntilIdle();
   }
 
-  // Creates a request using the current test object as the filter.
+  // Creates a new ForwardingFilter and registers it with |child_ids_| so as not
+  // to leak per-child state on test shutdown.
+  ForwardingFilter* MakeForwardingFilter() {
+    ForwardingFilter* filter =
+        new ForwardingFilter(this, browser_context_->GetResourceContext());
+    child_ids_.insert(filter->child_id());
+    return 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);
 
   void CancelRequest(int request_id);
+  void RendererCancelRequest(int request_id) {
+    ResourceMessageFilter* old_filter = SetFilter(filter_.get());
+    host_.OnCancelRequest(request_id);
+    SetFilter(old_filter);
+  }
 
   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();
@@ -619,10 +821,6 @@ class ResourceDispatcherHostTest : public testing::Test,
       policy->RegisterWebSafeScheme(scheme);
   }
 
-  void EnsureTestSchemeIsAllowed() {
-    EnsureSchemeIsAllowed("test");
-  }
-
   // Sets a particular response for any request from now on. To switch back to
   // the default bahavior, pass an empty |headers|. |headers| should be raw-
   // formatted (NULLs instead of EOLs).
@@ -635,69 +833,16 @@ 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;
   }
 
   // Intercepts requests for the given protocol.
   void HandleScheme(const std::string& scheme) {
-    DCHECK(scheme_.empty());
-    DCHECK(!old_factory_);
-    scheme_ = scheme;
-    old_factory_ = net::URLRequest::Deprecated::RegisterProtocolFactory(
-        scheme_, &ResourceDispatcherHostTest::Factory);
+    job_factory_->HandleScheme(scheme);
     EnsureSchemeIsAllowed(scheme);
   }
 
-  // Our own net::URLRequestJob factory.
-  static net::URLRequestJob* Factory(net::URLRequest* request,
-                                     net::NetworkDelegate* network_delegate,
-                                     const std::string& scheme) {
-    url_request_jobs_created_count_++;
-    if (test_fixture_->response_headers_.empty()) {
-      if (delay_start_) {
-        return new URLRequestTestDelayedStartJob(request, network_delegate);
-      } else if (delay_complete_) {
-        return new URLRequestTestDelayedCompletionJob(request,
-                                                      network_delegate);
-      } else if (scheme == "big-job") {
-        return new URLRequestBigJob(request, network_delegate);
-      } else {
-        return new net::URLRequestTestJob(request, network_delegate);
-      }
-    } else {
-      if (delay_start_) {
-        return new URLRequestTestDelayedStartJob(
-            request, network_delegate,
-            test_fixture_->response_headers_, test_fixture_->response_data_,
-            false);
-      } else if (delay_complete_) {
-        return new URLRequestTestDelayedCompletionJob(
-            request, network_delegate,
-            test_fixture_->response_headers_, test_fixture_->response_data_,
-            false);
-      } else {
-        return new net::URLRequestTestJob(
-            request, network_delegate,
-            test_fixture_->response_headers_, test_fixture_->response_data_,
-            false);
-      }
-    }
-  }
-
-  void SetDelayedStartJobGeneration(bool delay_job_start) {
-    delay_start_ = delay_job_start;
-  }
-
-  void SetDelayedCompleteJobGeneration(bool delay_job_complete) {
-    delay_complete_ = delay_job_complete;
-  }
-
   void GenerateDataReceivedACK(const IPC::Message& msg) {
     EXPECT_EQ(ResourceMsg_DataReceived::ID, msg.type());
 
@@ -712,57 +857,59 @@ class ResourceDispatcherHostTest : public testing::Test,
         base::Bind(&GenerateIPCMessage, filter_, base::Passed(&ack)));
   }
 
-  base::MessageLoopForIO message_loop_;
-  BrowserThreadImpl ui_thread_;
-  BrowserThreadImpl file_thread_;
-  BrowserThreadImpl cache_thread_;
-  BrowserThreadImpl io_thread_;
+  // Setting filters for testing renderer messages.
+  // Returns the previous filter.
+  ResourceMessageFilter* SetFilter(ResourceMessageFilter* new_filter) {
+    ResourceMessageFilter* old_filter = host_.filter_;
+    host_.filter_ = new_filter;
+    return old_filter;
+  }
+
+  void WaitForRequestComplete() {
+    DCHECK(!wait_for_request_complete_loop_);
+    wait_for_request_complete_loop_.reset(new base::RunLoop);
+    wait_for_request_complete_loop_->Run();
+    wait_for_request_complete_loop_.reset();
+  }
+
+  content::TestBrowserThreadBundle thread_bundle_;
   scoped_ptr<TestBrowserContext> browser_context_;
+  scoped_ptr<TestURLRequestJobFactory> job_factory_;
   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_;
-  static bool delay_start_;
-  static bool delay_complete_;
-  static int url_request_jobs_created_count_;
+  scoped_ptr<base::RunLoop> wait_for_request_complete_loop_;
 };
-// Static.
-ResourceDispatcherHostTest* ResourceDispatcherHostTest::test_fixture_ = NULL;
-bool ResourceDispatcherHostTest::delay_start_ = false;
-bool ResourceDispatcherHostTest::delay_complete_ = false;
-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, RESOURCE_TYPE_SUB_RESOURCE);
 }
 
-void ResourceDispatcherHostTest::MakeTestRequest(
+void ResourceDispatcherHostTest::MakeTestRequestWithResourceType(
     ResourceMessageFilter* filter,
     int render_view_id,
     int request_id,
-    const GURL& url) {
-  // If it's already there, this'll be dropped on the floor, which is fine.
-  child_ids_.insert(filter->child_id());
-
+    const GURL& url,
+    ResourceType type) {
   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);
+  host_.OnMessageReceived(msg, filter);
   KickOffRequest();
 }
 
 void ResourceDispatcherHostTest::CancelRequest(int request_id) {
-  host_.CancelRequest(filter_->child_id(), request_id, false);
+  host_.CancelRequest(filter_->child_id(), request_id);
 }
 
 void ResourceDispatcherHostTest::CompleteStartRequest(int request_id) {
@@ -779,16 +926,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 +986,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 +1000,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 +1028,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 +1037,15 @@ 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(),
+                                  RESOURCE_TYPE_PREFETCH);  // detachable type
+  MakeTestRequest(0, 5, net::URLRequestTestJob::test_url_redirect_to_url_2());
+
+  // Finish the redirection
+  ResourceHostMsg_FollowRedirect redirect_msg(5);
+  host_.OnMessageReceived(redirect_msg, filter_.get());
+  base::MessageLoop::current()->RunUntilIdle();
 
   // flush all the pending requests
   while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
@@ -861,52 +1054,212 @@ 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(),
+                                  RESOURCE_TYPE_PREFETCH);  // detachable type
+
   CancelRequest(2);
 
+  // Cancel request must come from the renderer for a detachable resource to
+  // delay.
+  RendererCancelRequest(4);
+
+  // 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.
-  ASSERT_EQ(2U, msgs[1].size());
+  // Check that request 2 and 4 got canceled, as far as the renderer is
+  // concerned.  Request 2 will have been deleted.
+  ASSERT_EQ(1U, msgs[1].size());
   ASSERT_EQ(ResourceMsg_ReceivedResponse::ID, msgs[1][0].type());
-  CheckCancelledRequestCompleteMessage(msgs[1][1]);
+
+  ASSERT_EQ(2U, msgs[3].size());
+  ASSERT_EQ(ResourceMsg_ReceivedResponse::ID, msgs[3][0].type());
+  CheckRequestCompleteErrorCode(msgs[3][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(),
+                                  RESOURCE_TYPE_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();
+
+  RendererCancelRequest(1);
+
+  // 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) {
+  // test_url_1's data is available synchronously, so use 2 and 3.
+  ResourceHostMsg_Request request_prefetch = CreateResourceRequest(
+      "GET", RESOURCE_TYPE_PREFETCH, net::URLRequestTestJob::test_url_2());
+  ResourceHostMsg_Request request_ping = CreateResourceRequest(
+      "GET", RESOURCE_TYPE_PING, net::URLRequestTestJob::test_url_3());
+
+  ResourceHostMsg_RequestResource msg_prefetch(0, 1, request_prefetch);
+  host_.OnMessageReceived(msg_prefetch, filter_);
+  ResourceHostMsg_RequestResource msg_ping(0, 2, request_ping);
+  host_.OnMessageReceived(msg_ping, filter_);
+
+  // Remove the filter before processing the requests by simulating channel
+  // closure.
+  ResourceRequestInfoImpl* info_prefetch = ResourceRequestInfoImpl::ForRequest(
+      host_.GetURLRequest(GlobalRequestID(filter_->child_id(), 1)));
+  ResourceRequestInfoImpl* info_ping = ResourceRequestInfoImpl::ForRequest(
+      host_.GetURLRequest(GlobalRequestID(filter_->child_id(), 2)));
+  DCHECK_EQ(filter_.get(), info_prefetch->filter());
+  DCHECK_EQ(filter_.get(), info_ping->filter());
+  filter_->OnChannelClosing();
+  info_prefetch->filter_.reset();
+  info_ping->filter_.reset();
+
+  // From the renderer's perspective, the requests were cancelled.
+  ResourceIPCAccumulator::ClassifiedMessages msgs;
+  accum_.GetClassifiedMessages(&msgs);
+  ASSERT_EQ(2U, msgs.size());
+  CheckRequestCompleteErrorCode(msgs[0][0], net::ERR_ABORTED);
+  CheckRequestCompleteErrorCode(msgs[1][0], net::ERR_ABORTED);
+
+  // But it continues detached.
+  EXPECT_EQ(2, host_.pending_requests());
+  EXPECT_TRUE(info_prefetch->detachable_handler()->is_detached());
+  EXPECT_TRUE(info_ping->detachable_handler()->is_detached());
+
+  KickOffRequest();
+
+  // Make sure the requests weren't canceled early.
+  EXPECT_EQ(2, host_.pending_requests());
+
+  while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
+  base::MessageLoop::current()->RunUntilIdle();
+
+  EXPECT_EQ(0, host_.pending_requests());
+  EXPECT_EQ(2, 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", RESOURCE_TYPE_PREFETCH,
+      net::URLRequestTestJob::test_url_redirect_to_url_2());
+
+  ResourceHostMsg_RequestResource msg(0, 1, request);
+  host_.OnMessageReceived(msg, filter_);
+
+  // 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) {
@@ -919,18 +1272,58 @@ TEST_F(ResourceDispatcherHostTest, CancelWhileStartIsDeferred) {
   host_.SetDelegate(&delegate);
 
   MakeTestRequest(0, 1, net::URLRequestTestJob::test_url_1());
-  CancelRequest(1);
+  // We cancel from the renderer because all non-renderer cancels delete
+  // the request synchronously.
+  RendererCancelRequest(1);
 
   // Our TestResourceThrottle should not have been deleted yet.  This is to
   // ensure that destruction of the URLRequest happens asynchronously to
   // 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(),
+                                  RESOURCE_TYPE_PREFETCH);  // detachable type
+  // Cancel request must come from the renderer for a detachable resource to
+  // detach.
+  RendererCancelRequest(1);
+
+  // 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,10 +1344,10 @@ 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_);
+  EXPECT_EQ(0, job_factory_->url_request_jobs_created_count());
 }
 
 TEST_F(ResourceDispatcherHostTest, PausedStartError) {
@@ -963,7 +1356,7 @@ TEST_F(ResourceDispatcherHostTest, PausedStartError) {
   delegate.set_flags(DEFER_PROCESSING_RESPONSE);
   host_.SetDelegate(&delegate);
 
-  SetDelayedStartJobGeneration(true);
+  job_factory_->SetDelayedStartJobGeneration(true);
   MakeTestRequest(0, 1, net::URLRequestTestJob::test_url_error());
   CompleteStartRequest(1);
 
@@ -974,6 +1367,33 @@ TEST_F(ResourceDispatcherHostTest, PausedStartError) {
   EXPECT_EQ(0, host_.pending_requests());
 }
 
+// Test the WillStartUsingNetwork throttle.
+TEST_F(ResourceDispatcherHostTest, ThrottleNetworkStart) {
+  // Arrange to have requests deferred before processing response headers.
+  TestResourceDispatcherHostDelegate delegate;
+  delegate.set_flags(DEFER_NETWORK_START);
+  host_.SetDelegate(&delegate);
+
+  job_factory_->SetNetworkStartNotificationJobGeneration(true);
+  MakeTestRequest(0, 1, net::URLRequestTestJob::test_url_2());
+
+  // Should have deferred for network start.
+  GenericResourceThrottle* first_throttle =
+      GenericResourceThrottle::active_throttle();
+  ASSERT_TRUE(first_throttle);
+  EXPECT_EQ(0, network_delegate()->completed_requests());
+  EXPECT_EQ(1, host_.pending_requests());
+
+  first_throttle->Resume();
+
+  // Flush all the pending requests.
+  while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
+  base::MessageLoop::current()->RunUntilIdle();
+
+  EXPECT_EQ(1, network_delegate()->completed_requests());
+  EXPECT_EQ(0, host_.pending_requests());
+}
+
 TEST_F(ResourceDispatcherHostTest, ThrottleAndResumeTwice) {
   // Arrange to have requests deferred before starting.
   TestResourceDispatcherHostDelegate delegate;
@@ -1031,62 +1451,37 @@ 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
-// pending and some canceled.
-class TestFilter : public ForwardingFilter {
- public:
-  explicit TestFilter(ResourceContext* resource_context)
-      : ForwardingFilter(NULL, resource_context),
-        has_canceled_(false),
-        received_after_canceled_(0) {
-  }
-
-  // ForwardingFilter override
-  virtual bool Send(IPC::Message* msg) OVERRIDE {
-    // no messages should be received when the process has been canceled
-    if (has_canceled_)
-      received_after_canceled_++;
-    delete msg;
-    return true;
-  }
-
-  bool has_canceled_;
-  int received_after_canceled_;
-
- private:
-  virtual ~TestFilter() {}
-};
-
 // Tests CancelRequestsForProcess
 TEST_F(ResourceDispatcherHostTest, TestProcessCancel) {
   scoped_refptr<TestFilter> test_filter = new TestFilter(
       browser_context_->GetResourceContext());
+  child_ids_.insert(test_filter->child_id());
 
   // request 1 goes to the test delegate
   ResourceHostMsg_Request request = CreateResourceRequest(
-      "GET", ResourceType::SUB_RESOURCE, net::URLRequestTestJob::test_url_1());
+      "GET", RESOURCE_TYPE_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(),
+                                  RESOURCE_TYPE_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(),
+                                  RESOURCE_TYPE_SUB_RESOURCE);
+
+  // request 4 goes to us
+  MakeTestRequestWithResourceType(filter_.get(), 0, 4,
+                                  net::URLRequestTestJob::test_url_4(),
+                                  RESOURCE_TYPE_PREFETCH);  // detachable type
+
 
   // Make sure all requests have finished stage one. test_url_1 will have
   // finished.
@@ -1098,13 +1493,22 @@ 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());
 
   // Cancel the requests to the test process.
   host_.CancelRequestsForProcess(filter_->child_id());
-  test_filter->has_canceled_ = true;
+  test_filter->set_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()) {}
@@ -1112,13 +1516,67 @@ TEST_F(ResourceDispatcherHostTest, TestProcessCancel) {
   EXPECT_EQ(0, host_.pending_requests());
 
   // The test delegate should not have gotten any messages after being canceled.
-  ASSERT_EQ(0, test_filter->received_after_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(),
+                                  RESOURCE_TYPE_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 +1645,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(),
+                                  RESOURCE_TYPE_PREFETCH);  // detachable type
 
   // Flush all the pending requests.
   while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
@@ -1214,17 +1676,25 @@ TEST_F(ResourceDispatcherHostTest, TestBlockingCancelingRequests) {
 // Tests that blocked requests are canceled if their associated process dies.
 TEST_F(ResourceDispatcherHostTest, TestBlockedRequestsProcessDies) {
   // This second filter is used to emulate a second process.
-  scoped_refptr<ForwardingFilter> second_filter = new ForwardingFilter(
-      this, browser_context_->GetResourceContext());
+  scoped_refptr<ForwardingFilter> second_filter = MakeForwardingFilter();
 
   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(),
+                                  RESOURCE_TYPE_SUB_RESOURCE);
+  MakeTestRequestWithResourceType(second_filter.get(), 0, 2,
+                                  net::URLRequestTestJob::test_url_2(),
+                                  RESOURCE_TYPE_SUB_RESOURCE);
+  MakeTestRequestWithResourceType(filter_.get(), 0, 3,
+                                  net::URLRequestTestJob::test_url_3(),
+                                  RESOURCE_TYPE_SUB_RESOURCE);
+  MakeTestRequestWithResourceType(second_filter.get(), 0, 4,
+                                  net::URLRequestTestJob::test_url_1(),
+                                  RESOURCE_TYPE_SUB_RESOURCE);
+  MakeTestRequestWithResourceType(second_filter.get(), 0, 5,
+                                  net::URLRequestTestJob::test_url_4(),
+                                  RESOURCE_TYPE_PREFETCH);  // detachable type
 
   // Simulate process death.
   host_.CancelRequestsForProcess(second_filter->child_id());
@@ -1236,7 +1706,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());
@@ -1251,20 +1722,36 @@ TEST_F(ResourceDispatcherHostTest, TestBlockedRequestsProcessDies) {
 // destructor to make sure the blocked requests are deleted.
 TEST_F(ResourceDispatcherHostTest, TestBlockedRequestsDontLeak) {
   // This second filter is used to emulate a second process.
-  scoped_refptr<ForwardingFilter> second_filter = new ForwardingFilter(
-      this, browser_context_->GetResourceContext());
+  scoped_refptr<ForwardingFilter> second_filter = MakeForwardingFilter();
 
   host_.BlockRequestsForRoute(filter_->child_id(), 1);
   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(),
+                                  RESOURCE_TYPE_SUB_RESOURCE);
+  MakeTestRequestWithResourceType(filter_.get(), 1, 2,
+                                  net::URLRequestTestJob::test_url_2(),
+                                  RESOURCE_TYPE_SUB_RESOURCE);
+  MakeTestRequestWithResourceType(filter_.get(), 0, 3,
+                                  net::URLRequestTestJob::test_url_3(),
+                                  RESOURCE_TYPE_SUB_RESOURCE);
+  MakeTestRequestWithResourceType(second_filter.get(), 1, 4,
+                                  net::URLRequestTestJob::test_url_1(),
+                                  RESOURCE_TYPE_SUB_RESOURCE);
+  MakeTestRequestWithResourceType(filter_.get(), 2, 5,
+                                  net::URLRequestTestJob::test_url_2(),
+                                  RESOURCE_TYPE_SUB_RESOURCE);
+  MakeTestRequestWithResourceType(filter_.get(), 2, 6,
+                                  net::URLRequestTestJob::test_url_3(),
+                                  RESOURCE_TYPE_SUB_RESOURCE);
+  MakeTestRequestWithResourceType(filter_.get(), 0, 7,
+                                  net::URLRequestTestJob::test_url_4(),
+                                  RESOURCE_TYPE_PREFETCH);  // detachable type
+  MakeTestRequestWithResourceType(second_filter.get(), 1, 8,
+                                  net::URLRequestTestJob::test_url_4(),
+                                  RESOURCE_TYPE_PREFETCH);  // detachable type
 
   host_.CancelRequestsForProcess(filter_->child_id());
   host_.CancelRequestsForProcess(second_filter->child_id());
@@ -1319,27 +1806,31 @@ TEST_F(ResourceDispatcherHostTest, TooMuchOutstandingRequestsMemory) {
   size_t kMaxRequests = kMaxCostPerProcess / kMemoryCostOfTest2Req;
 
   // This second filter is used to emulate a second process.
-  scoped_refptr<ForwardingFilter> second_filter = new ForwardingFilter(
-      this, browser_context_->GetResourceContext());
+  scoped_refptr<ForwardingFilter> second_filter = MakeForwardingFilter();
 
   // 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(),
+                                    RESOURCE_TYPE_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(),
+                                  RESOURCE_TYPE_SUB_RESOURCE);
+  MakeTestRequestWithResourceType(filter_.get(), 0, kMaxRequests + 2,
+                                  net::URLRequestTestJob::test_url_2(),
+                                  RESOURCE_TYPE_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(),
+                                  RESOURCE_TYPE_SUB_RESOURCE);
+  MakeTestRequestWithResourceType(second_filter.get(), 0, kMaxRequests + 4,
+                                  net::URLRequestTestJob::test_url_2(),
+                                  RESOURCE_TYPE_SUB_RESOURCE);
 
   // Flush all the pending requests.
   while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
@@ -1383,30 +1874,32 @@ TEST_F(ResourceDispatcherHostTest, TooManyOutstandingRequests) {
   host_.set_max_num_in_flight_requests(kMaxRequests);
 
   // Needed to emulate additional processes.
-  scoped_refptr<ForwardingFilter> second_filter = new ForwardingFilter(
-      this, browser_context_->GetResourceContext());
-  scoped_refptr<ForwardingFilter> third_filter = new ForwardingFilter(
-      this, browser_context_->GetResourceContext());
+  scoped_refptr<ForwardingFilter> second_filter = MakeForwardingFilter();
+  scoped_refptr<ForwardingFilter> third_filter = MakeForwardingFilter();
 
   // 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(),
+                                    RESOURCE_TYPE_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(),
+                                  RESOURCE_TYPE_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(), RESOURCE_TYPE_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(), RESOURCE_TYPE_SUB_RESOURCE);
 
   // Flush all the pending requests.
   while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
@@ -1546,34 +2039,28 @@ 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"),
+                                  RESOURCE_TYPE_MAIN_FRAME);
 
   // Flush all pending requests.
   while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
+  base::MessageLoop::current()->RunUntilIdle();
 
   // Sorts out all the messages we saw by request.
   ResourceIPCAccumulator::ClassifiedMessages msgs;
   accum_.GetClassifiedMessages(&msgs);
 
   // We should have gotten one RequestComplete message.
+  ASSERT_EQ(1U, msgs.size());
   ASSERT_EQ(1U, msgs[0].size());
   EXPECT_EQ(ResourceMsg_RequestComplete::ID, msgs[0][0].type());
 
   // 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);
+  // ERR_INVALID_RESPONSE.
+  CheckRequestCompleteErrorCode(msgs[0][0], net::ERR_INVALID_RESPONSE);
 }
 
 // Test for http://crbug.com/76202 .  We don't want to destroy a
@@ -1597,19 +2084,19 @@ TEST_F(ResourceDispatcherHostTest, IgnoreCancelForDownloads) {
   response_data.resize(1025, ' ');
 
   SetResponse(raw_headers, response_data);
-  SetResourceType(ResourceType::MAIN_FRAME);
-  SetDelayedCompleteJobGeneration(true);
+  job_factory_->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"),
+                                  RESOURCE_TYPE_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());
 
   // And now simulate a cancellation coming from the renderer.
   ResourceHostMsg_CancelRequest msg(request_id);
-  bool msg_was_ok;
-  host_.OnMessageReceived(msg, filter_.get(), &msg_was_ok);
+  host_.OnMessageReceived(msg, filter_.get());
 
   // Since the request had already started processing as a download,
   // the cancellation above should have been ignored and the request
@@ -1632,19 +2119,19 @@ TEST_F(ResourceDispatcherHostTest, CancelRequestsForContext) {
   response_data.resize(1025, ' ');
 
   SetResponse(raw_headers, response_data);
-  SetResourceType(ResourceType::MAIN_FRAME);
-  SetDelayedCompleteJobGeneration(true);
+  job_factory_->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"),
+                                  RESOURCE_TYPE_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());
 
   // And now simulate a cancellation coming from the renderer.
   ResourceHostMsg_CancelRequest msg(request_id);
-  bool msg_was_ok;
-  host_.OnMessageReceived(msg, filter_.get(), &msg_was_ok);
+  host_.OnMessageReceived(msg, filter_.get());
 
   // Since the request had already started processing as a download,
   // the cancellation above should have been ignored and the request
@@ -1660,6 +2147,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(),
+                                  RESOURCE_TYPE_PREFETCH);  // detachable type
+
+  // Simulate a cancel coming from the renderer.
+  RendererCancelRequest(request_id);
+
+  // 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,19 +2187,19 @@ 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"),
+                                  RESOURCE_TYPE_MAIN_FRAME);
+
 
   GlobalRequestID global_request_id(filter_->child_id(), request_id);
-  host_.MarkAsTransferredNavigation(global_request_id,
-                                    GURL("http://example.com/blah"));
+  host_.MarkAsTransferredNavigation(global_request_id);
 
   // And now simulate a cancellation coming from the renderer.
   ResourceHostMsg_CancelRequest msg(request_id);
-  bool msg_was_ok;
-  host_.OnMessageReceived(msg, filter_.get(), &msg_was_ok);
+  host_.OnMessageReceived(msg, filter_.get());
 
   // Since the request is marked as being transferred,
   // the cancellation above should have been ignored and the request
@@ -1704,6 +2218,10 @@ TEST_F(ResourceDispatcherHostTest, CancelRequestsForContextTransferred) {
 // Test transferred navigations with text/html, which doesn't trigger any
 // content sniffing.
 TEST_F(ResourceDispatcherHostTest, TransferNavigationHtml) {
+  // This test expects the cross site request to be leaked, so it can transfer
+  // the request directly.
+  CrossSiteResourceHandler::SetLeakRequestsForTesting(true);
+
   EXPECT_EQ(0, host_.pending_requests());
 
   int render_view_id = 0;
@@ -1713,7 +2231,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 +2238,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"),
+                                  RESOURCE_TYPE_MAIN_FRAME);
 
   // Now that we're blocked on the redirect, update the response and unblock by
   // telling the AsyncResourceHandler to follow the redirect.
@@ -1729,9 +2248,8 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationHtml) {
   SetResponse("HTTP/1.1 200 OK\n"
               "Content-Type: text/html\n\n",
               kResponseBody);
-  ResourceHostMsg_FollowRedirect redirect_msg(request_id, false, GURL());
-  bool msg_was_ok;
-  host_.OnMessageReceived(redirect_msg, filter_.get(), &msg_was_ok);
+  ResourceHostMsg_FollowRedirect redirect_msg(request_id);
+  host_.OnMessageReceived(redirect_msg, filter_.get());
   base::MessageLoop::current()->RunUntilIdle();
 
   // Flush all the pending requests to get the response through the
@@ -1742,24 +2260,20 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationHtml) {
   SetBrowserClientForTesting(old_client);
 
   // This second filter is used to emulate a second process.
-  scoped_refptr<ForwardingFilter> second_filter = new ForwardingFilter(
-      this, browser_context_->GetResourceContext());
+  scoped_refptr<ForwardingFilter> second_filter = MakeForwardingFilter();
 
   int new_render_view_id = 1;
   int new_request_id = 2;
 
   ResourceHostMsg_Request request =
-      CreateResourceRequest("GET", ResourceType::MAIN_FRAME,
+      CreateResourceRequest("GET", RESOURCE_TYPE_MAIN_FRAME,
                             GURL("http://other.com/blech"));
   request.transferred_request_child_id = filter_->child_id();
   request.transferred_request_request_id = request_id;
 
-  // For cleanup.
-  child_ids_.insert(second_filter->child_id());
   ResourceHostMsg_RequestResource transfer_request_msg(
       new_render_view_id, new_request_id, request);
-  host_.OnMessageReceived(
-      transfer_request_msg, second_filter.get(), &msg_was_ok);
+  host_.OnMessageReceived(transfer_request_msg, second_filter.get());
   base::MessageLoop::current()->RunUntilIdle();
 
   // Check generated messages.
@@ -1775,6 +2289,10 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationHtml) {
 // BufferedResourceHandler to buffer the response to sniff the content
 // before the transfer occurs.
 TEST_F(ResourceDispatcherHostTest, TransferNavigationText) {
+  // This test expects the cross site request to be leaked, so it can transfer
+  // the request directly.
+  CrossSiteResourceHandler::SetLeakRequestsForTesting(true);
+
   EXPECT_EQ(0, host_.pending_requests());
 
   int render_view_id = 0;
@@ -1784,7 +2302,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 +2309,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"),
+                                  RESOURCE_TYPE_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
@@ -1802,9 +2321,8 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationText) {
   SetResponse("HTTP/1.1 200 OK\n"
               "Content-Type: text/plain\n\n",
               kResponseBody);
-  ResourceHostMsg_FollowRedirect redirect_msg(request_id, false, GURL());
-  bool msg_was_ok;
-  host_.OnMessageReceived(redirect_msg, filter_.get(), &msg_was_ok);
+  ResourceHostMsg_FollowRedirect redirect_msg(request_id);
+  host_.OnMessageReceived(redirect_msg, filter_.get());
   base::MessageLoop::current()->RunUntilIdle();
 
   // Flush all the pending requests to get the response through the
@@ -1815,24 +2333,20 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationText) {
   SetBrowserClientForTesting(old_client);
 
   // This second filter is used to emulate a second process.
-  scoped_refptr<ForwardingFilter> second_filter = new ForwardingFilter(
-      this, browser_context_->GetResourceContext());
+  scoped_refptr<ForwardingFilter> second_filter = MakeForwardingFilter();
 
   int new_render_view_id = 1;
   int new_request_id = 2;
 
   ResourceHostMsg_Request request =
-      CreateResourceRequest("GET", ResourceType::MAIN_FRAME,
+      CreateResourceRequest("GET", RESOURCE_TYPE_MAIN_FRAME,
                             GURL("http://other.com/blech"));
   request.transferred_request_child_id = filter_->child_id();
   request.transferred_request_request_id = request_id;
 
-  // For cleanup.
-  child_ids_.insert(second_filter->child_id());
   ResourceHostMsg_RequestResource transfer_request_msg(
       new_render_view_id, new_request_id, request);
-  host_.OnMessageReceived(
-      transfer_request_msg, second_filter.get(), &msg_was_ok);
+  host_.OnMessageReceived(transfer_request_msg, second_filter.get());
   base::MessageLoop::current()->RunUntilIdle();
 
   // Check generated messages.
@@ -1845,6 +2359,10 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationText) {
 }
 
 TEST_F(ResourceDispatcherHostTest, TransferNavigationWithProcessCrash) {
+  // This test expects the cross site request to be leaked, so it can transfer
+  // the request directly.
+  CrossSiteResourceHandler::SetLeakRequestsForTesting(true);
+
   EXPECT_EQ(0, host_.pending_requests());
 
   int render_view_id = 0;
@@ -1856,7 +2374,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
@@ -1866,21 +2383,16 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationWithProcessCrash) {
 
   // Create a first filter that can be deleted before the second one starts.
   {
-    scoped_refptr<ForwardingFilter> first_filter = new ForwardingFilter(
-        this, browser_context_->GetResourceContext());
+    scoped_refptr<ForwardingFilter> first_filter = MakeForwardingFilter();
     first_child_id = first_filter->child_id();
 
     ResourceHostMsg_Request first_request =
-        CreateResourceRequest("GET", ResourceType::MAIN_FRAME,
+        CreateResourceRequest("GET", RESOURCE_TYPE_MAIN_FRAME,
                               GURL("http://example.com/blah"));
 
-    // For cleanup.
-    child_ids_.insert(first_child_id);
     ResourceHostMsg_RequestResource first_request_msg(
         render_view_id, request_id, first_request);
-    bool msg_was_ok;
-    host_.OnMessageReceived(
-        first_request_msg, first_filter.get(), &msg_was_ok);
+    host_.OnMessageReceived(first_request_msg, first_filter.get());
     base::MessageLoop::current()->RunUntilIdle();
 
     // Now that we're blocked on the redirect, update the response and unblock
@@ -1888,8 +2400,8 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationWithProcessCrash) {
     SetResponse("HTTP/1.1 200 OK\n"
                 "Content-Type: text/html\n\n",
                 kResponseBody);
-    ResourceHostMsg_FollowRedirect redirect_msg(request_id, false, GURL());
-    host_.OnMessageReceived(redirect_msg, first_filter.get(), &msg_was_ok);
+    ResourceHostMsg_FollowRedirect redirect_msg(request_id);
+    host_.OnMessageReceived(redirect_msg, first_filter.get());
     base::MessageLoop::current()->RunUntilIdle();
 
     // Flush all the pending requests to get the response through the
@@ -1905,14 +2417,13 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationWithProcessCrash) {
   GlobalRequestID first_global_request_id(first_child_id, request_id);
 
   // This second filter is used to emulate a second process.
-  scoped_refptr<ForwardingFilter> second_filter = new ForwardingFilter(
-      this, browser_context_->GetResourceContext());
+  scoped_refptr<ForwardingFilter> second_filter = MakeForwardingFilter();
 
   int new_render_view_id = 1;
   int new_request_id = 2;
 
   ResourceHostMsg_Request request =
-      CreateResourceRequest("GET", ResourceType::MAIN_FRAME,
+      CreateResourceRequest("GET", RESOURCE_TYPE_MAIN_FRAME,
                             GURL("http://other.com/blech"));
   request.transferred_request_child_id = first_child_id;
   request.transferred_request_request_id = request_id;
@@ -1921,9 +2432,7 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationWithProcessCrash) {
   child_ids_.insert(second_filter->child_id());
   ResourceHostMsg_RequestResource transfer_request_msg(
       new_render_view_id, new_request_id, request);
-  bool msg_was_ok;
-  host_.OnMessageReceived(
-      transfer_request_msg, second_filter.get(), &msg_was_ok);
+  host_.OnMessageReceived(transfer_request_msg, second_filter.get());
   base::MessageLoop::current()->RunUntilIdle();
 
   // Check generated messages.
@@ -1936,6 +2445,10 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationWithProcessCrash) {
 }
 
 TEST_F(ResourceDispatcherHostTest, TransferNavigationWithTwoRedirects) {
+  // This test expects the cross site request to be leaked, so it can transfer
+  // the request directly.
+  CrossSiteResourceHandler::SetLeakRequestsForTesting(true);
+
   EXPECT_EQ(0, host_.pending_requests());
 
   int render_view_id = 0;
@@ -1945,7 +2458,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,14 +2465,15 @@ 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"),
+                                  RESOURCE_TYPE_MAIN_FRAME);
 
   // Now that we're blocked on the redirect, simulate hitting another redirect.
   SetResponse("HTTP/1.1 302 Found\n"
               "Location: http://other.com/blerg\n\n");
-  ResourceHostMsg_FollowRedirect redirect_msg(request_id, false, GURL());
-  bool msg_was_ok;
-  host_.OnMessageReceived(redirect_msg, filter_.get(), &msg_was_ok);
+  ResourceHostMsg_FollowRedirect redirect_msg(request_id);
+  host_.OnMessageReceived(redirect_msg, filter_.get());
   base::MessageLoop::current()->RunUntilIdle();
 
   // Now that we're blocked on the second redirect, update the response and
@@ -1971,8 +2484,8 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationWithTwoRedirects) {
   SetResponse("HTTP/1.1 200 OK\n"
               "Content-Type: text/plain\n\n",
               kResponseBody);
-  ResourceHostMsg_FollowRedirect redirect_msg2(request_id, false, GURL());
-  host_.OnMessageReceived(redirect_msg2, filter_.get(), &msg_was_ok);
+  ResourceHostMsg_FollowRedirect redirect_msg2(request_id);
+  host_.OnMessageReceived(redirect_msg2, filter_.get());
   base::MessageLoop::current()->RunUntilIdle();
 
   // Flush all the pending requests to get the response through the
@@ -1983,14 +2496,13 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationWithTwoRedirects) {
   SetBrowserClientForTesting(old_client);
 
   // This second filter is used to emulate a second process.
-  scoped_refptr<ForwardingFilter> second_filter = new ForwardingFilter(
-      this, browser_context_->GetResourceContext());
+  scoped_refptr<ForwardingFilter> second_filter = MakeForwardingFilter();
 
   int new_render_view_id = 1;
   int new_request_id = 2;
 
   ResourceHostMsg_Request request =
-      CreateResourceRequest("GET", ResourceType::MAIN_FRAME,
+      CreateResourceRequest("GET", RESOURCE_TYPE_MAIN_FRAME,
                             GURL("http://other.com/blech"));
   request.transferred_request_child_id = filter_->child_id();
   request.transferred_request_request_id = request_id;
@@ -1999,8 +2511,7 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationWithTwoRedirects) {
   child_ids_.insert(second_filter->child_id());
   ResourceHostMsg_RequestResource transfer_request_msg(
       new_render_view_id, new_request_id, request);
-  host_.OnMessageReceived(
-      transfer_request_msg, second_filter.get(), &msg_was_ok);
+  host_.OnMessageReceived(transfer_request_msg, second_filter.get());
 
   // Verify that we update the ResourceRequestInfo.
   GlobalRequestID global_request_id(second_filter->child_id(), new_request_id);
@@ -2026,10 +2537,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"),
+                                  RESOURCE_TYPE_MAIN_FRAME);
 
   // Flush all pending requests.
   while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
@@ -2044,15 +2555,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 +2579,64 @@ 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);
+  job_factory_->SetDelayedCompleteJobGeneration(true);
+  HandleScheme("http");
+
+  MakeTestRequestWithResourceType(filter_.get(), render_view_id, request_id,
+                                  GURL("http://example.com/blah"),
+                                  RESOURCE_TYPE_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);
+  host_.OnMessageReceived(msg, filter_.get());
+
+  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());
 
@@ -2111,8 +2672,7 @@ TEST_F(ResourceDispatcherHostTest, DelayedDataReceivedACKs) {
       EXPECT_EQ(ResourceMsg_DataReceived::ID, msgs[0][i].type());
 
       ResourceHostMsg_DataReceived_ACK msg(1);
-      bool msg_was_ok;
-      host_.OnMessageReceived(msg, filter_.get(), &msg_was_ok);
+      host_.OnMessageReceived(msg, filter_.get());
     }
 
     base::MessageLoop::current()->RunUntilIdle();
@@ -2146,8 +2706,7 @@ TEST_F(ResourceDispatcherHostTest, DataReceivedUnexpectedACKs) {
   // Send some unexpected ACKs.
   for (size_t i = 0; i < 128; ++i) {
     ResourceHostMsg_DataReceived_ACK msg(1);
-    bool msg_was_ok;
-    host_.OnMessageReceived(msg, filter_.get(), &msg_was_ok);
+    host_.OnMessageReceived(msg, filter_.get());
   }
 
   msgs[0].erase(msgs[0].begin());
@@ -2165,8 +2724,7 @@ TEST_F(ResourceDispatcherHostTest, DataReceivedUnexpectedACKs) {
       EXPECT_EQ(ResourceMsg_DataReceived::ID, msgs[0][i].type());
 
       ResourceHostMsg_DataReceived_ACK msg(1);
-      bool msg_was_ok;
-      host_.OnMessageReceived(msg, filter_.get(), &msg_was_ok);
+      host_.OnMessageReceived(msg, filter_.get());
     }
 
     base::MessageLoop::current()->RunUntilIdle();
@@ -2176,4 +2734,194 @@ TEST_F(ResourceDispatcherHostTest, DataReceivedUnexpectedACKs) {
   }
 }
 
+// Tests the dispatcher host's temporary file management.
+TEST_F(ResourceDispatcherHostTest, RegisterDownloadedTempFile) {
+  const int kRequestID = 1;
+
+  // Create a temporary file.
+  base::FilePath file_path;
+  ASSERT_TRUE(base::CreateTemporaryFile(&file_path));
+  scoped_refptr<ShareableFileReference> deletable_file =
+      ShareableFileReference::GetOrCreate(
+          file_path,
+          ShareableFileReference::DELETE_ON_FINAL_RELEASE,
+          BrowserThread::GetMessageLoopProxyForThread(
+              BrowserThread::FILE).get());
+
+  // Not readable.
+  EXPECT_FALSE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile(
+      filter_->child_id(), file_path));
+
+  // Register it for a resource request.
+  host_.RegisterDownloadedTempFile(filter_->child_id(), kRequestID, file_path);
+
+  // Should be readable now.
+  EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile(
+      filter_->child_id(), file_path));
+
+  // The child releases from the request.
+  ResourceHostMsg_ReleaseDownloadedFile release_msg(kRequestID);
+  host_.OnMessageReceived(release_msg, filter_);
+
+  // Still readable because there is another reference to the file. (The child
+  // may take additional blob references.)
+  EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile(
+      filter_->child_id(), file_path));
+
+  // Release extra references and wait for the file to be deleted. (This relies
+  // on the delete happening on the FILE thread which is mapped to main thread
+  // in this test.)
+  deletable_file = NULL;
+  base::RunLoop().RunUntilIdle();
+
+  // The file is no longer readable to the child and has been deleted.
+  EXPECT_FALSE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile(
+      filter_->child_id(), file_path));
+  EXPECT_FALSE(base::PathExists(file_path));
+}
+
+// Tests that temporary files held on behalf of child processes are released
+// when the child process dies.
+TEST_F(ResourceDispatcherHostTest, ReleaseTemporiesOnProcessExit) {
+  const int kRequestID = 1;
+
+  // Create a temporary file.
+  base::FilePath file_path;
+  ASSERT_TRUE(base::CreateTemporaryFile(&file_path));
+  scoped_refptr<ShareableFileReference> deletable_file =
+      ShareableFileReference::GetOrCreate(
+          file_path,
+          ShareableFileReference::DELETE_ON_FINAL_RELEASE,
+          BrowserThread::GetMessageLoopProxyForThread(
+              BrowserThread::FILE).get());
+
+  // Register it for a resource request.
+  host_.RegisterDownloadedTempFile(filter_->child_id(), kRequestID, file_path);
+  deletable_file = NULL;
+
+  // Should be readable now.
+  EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile(
+      filter_->child_id(), file_path));
+
+  // Let the process die.
+  filter_->OnChannelClosing();
+  base::RunLoop().RunUntilIdle();
+
+  // The file is no longer readable to the child and has been deleted.
+  EXPECT_FALSE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile(
+      filter_->child_id(), file_path));
+  EXPECT_FALSE(base::PathExists(file_path));
+}
+
+TEST_F(ResourceDispatcherHostTest, DownloadToFile) {
+  // Make a request which downloads to file.
+  ResourceHostMsg_Request request = CreateResourceRequest(
+      "GET", RESOURCE_TYPE_SUB_RESOURCE, net::URLRequestTestJob::test_url_1());
+  request.download_to_file = true;
+  ResourceHostMsg_RequestResource request_msg(0, 1, request);
+  host_.OnMessageReceived(request_msg, filter_);
+
+  // Running the message loop until idle does not work because
+  // RedirectToFileResourceHandler posts things to base::WorkerPool. Instead,
+  // wait for the ResourceMsg_RequestComplete to go out. Then run the event loop
+  // until idle so the loader is gone.
+  WaitForRequestComplete();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(0, host_.pending_requests());
+
+  ResourceIPCAccumulator::ClassifiedMessages msgs;
+  accum_.GetClassifiedMessages(&msgs);
+
+  ASSERT_EQ(1U, msgs.size());
+  const std::vector<IPC::Message>& messages = msgs[0];
+
+  // The request should contain the following messages:
+  //     ReceivedResponse    (indicates headers received and filename)
+  //     DataDownloaded*     (bytes downloaded and total length)
+  //     RequestComplete     (request is done)
+
+  // ReceivedResponse
+  ResourceResponseHead response_head;
+  GetResponseHead(messages, &response_head);
+  ASSERT_FALSE(response_head.download_file_path.empty());
+
+  // DataDownloaded
+  size_t total_len = 0;
+  for (size_t i = 1; i < messages.size() - 1; i++) {
+    ASSERT_EQ(ResourceMsg_DataDownloaded::ID, messages[i].type());
+    PickleIterator iter(messages[i]);
+    int request_id, data_len;
+    ASSERT_TRUE(IPC::ReadParam(&messages[i], &iter, &request_id));
+    ASSERT_TRUE(IPC::ReadParam(&messages[i], &iter, &data_len));
+    total_len += data_len;
+  }
+  EXPECT_EQ(net::URLRequestTestJob::test_data_1().size(), total_len);
+
+  // RequestComplete
+  CheckRequestCompleteErrorCode(messages.back(), net::OK);
+
+  // Verify that the data ended up in the temporary file.
+  std::string contents;
+  ASSERT_TRUE(base::ReadFileToString(response_head.download_file_path,
+                                     &contents));
+  EXPECT_EQ(net::URLRequestTestJob::test_data_1(), contents);
+
+  // The file should be readable by the child.
+  EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile(
+      filter_->child_id(), response_head.download_file_path));
+
+  // When the renderer releases the file, it should be deleted. Again,
+  // RunUntilIdle doesn't work because base::WorkerPool is involved.
+  ShareableFileReleaseWaiter waiter(response_head.download_file_path);
+  ResourceHostMsg_ReleaseDownloadedFile release_msg(1);
+  host_.OnMessageReceived(release_msg, filter_);
+  waiter.Wait();
+  // The release callback runs before the delete is scheduled, so pump the
+  // message loop for the delete itself. (This relies on the delete happening on
+  // the FILE thread which is mapped to main thread in this test.)
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_FALSE(base::PathExists(response_head.download_file_path));
+  EXPECT_FALSE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile(
+      filter_->child_id(), response_head.download_file_path));
+}
+
+net::URLRequestJob* TestURLRequestJobFactory::MaybeCreateJobWithProtocolHandler(
+      const std::string& scheme,
+      net::URLRequest* request,
+      net::NetworkDelegate* network_delegate) const {
+  url_request_jobs_created_count_++;
+  if (test_fixture_->response_headers_.empty()) {
+    if (delay_start_) {
+      return new URLRequestTestDelayedStartJob(request, network_delegate);
+    } else if (delay_complete_) {
+      return new URLRequestTestDelayedCompletionJob(request,
+                                                    network_delegate);
+    } else if (network_start_notification_) {
+      return new URLRequestTestDelayedNetworkJob(request, network_delegate);
+    } else if (scheme == "big-job") {
+      return new URLRequestBigJob(request, network_delegate);
+    } else {
+      return new net::URLRequestTestJob(request, network_delegate);
+    }
+  } else {
+    if (delay_start_) {
+      return new URLRequestTestDelayedStartJob(
+          request, network_delegate,
+          test_fixture_->response_headers_, test_fixture_->response_data_,
+          false);
+    } else if (delay_complete_) {
+      return new URLRequestTestDelayedCompletionJob(
+          request, network_delegate,
+          test_fixture_->response_headers_, test_fixture_->response_data_,
+          false);
+    } else {
+      return new net::URLRequestTestJob(
+          request, network_delegate,
+          test_fixture_->response_headers_, test_fixture_->response_data_,
+          false);
+    }
+  }
+}
+
 }  // namespace content