Reland of 218616.  It was reverted in 219027 due to crashes in the case of
responses with no addresses.

> [net/dns] Perform A/AAAA queries for AF_UNSPEC resolutions in parallel. 
> 
> The second DnsTransaction is scheduled as a second job on the resolver's
> PrioritizedDispatcher, at the beginning of its priority queue. The two
> DnsTransactions run independently of each other, although if one of them
> finishes with an error, the other one will be scrapped immediately, and
> the Job will fall back to ProcTask. 
> 
> BUG=174992
> 
> Original Review URL: https://chromiumcodereview.appspot.com/19498003
Revert URL: https://codereview.chromium.org/23102009

BUG=174992,277625

Review URL: https://chromiumcodereview.appspot.com/22909037

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@221469 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/net/dns/host_resolver_impl_unittest.cc b/net/dns/host_resolver_impl_unittest.cc
index d604b64..24a00a9 100644
--- a/net/dns/host_resolver_impl_unittest.cc
+++ b/net/dns/host_resolver_impl_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_vector.h"
 #include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/synchronization/condition_variable.h"
@@ -420,6 +421,20 @@
 
   HostResolverImplTest() : proc_(new MockHostResolverProc()) {}
 
+  void CreateResolver() {
+    CreateResolverWithLimitsAndParams(DefaultLimits(),
+                                      DefaultParams(proc_.get()));
+  }
+
+  // This HostResolverImpl will only allow 1 outstanding resolve at a time and
+  // perform no retries.
+  void CreateSerialResolver() {
+    HostResolverImpl::ProcTaskParams params = DefaultParams(proc_.get());
+    params.max_retry_attempts = 0u;
+    PrioritizedDispatcher::Limits limits(NUM_PRIORITIES, 1);
+    CreateResolverWithLimitsAndParams(limits, params);
+  }
+
  protected:
   // A Request::Handler which is a proxy to the HostResolverImplTest fixture.
   struct Handler : public Request::Handler {
@@ -443,24 +458,22 @@
     HostResolverImplTest* test;
   };
 
-  void CreateResolver() {
-    resolver_.reset(new HostResolverImpl(HostCache::CreateDefaultCache(),
-                                         DefaultLimits(),
-                                         DefaultParams(proc_.get()),
-                                         NULL));
+  // testing::Test implementation:
+  virtual void SetUp() OVERRIDE {
+    CreateResolver();
   }
 
-  // This HostResolverImpl will only allow 1 outstanding resolve at a time and
-  // perform no retries.
-  void CreateSerialResolver() {
-    HostResolverImpl::ProcTaskParams params = DefaultParams(proc_.get());
-    params.max_retry_attempts = 0u;
-    PrioritizedDispatcher::Limits limits(NUM_PRIORITIES, 1);
-    resolver_.reset(new HostResolverImpl(
-        HostCache::CreateDefaultCache(),
-        limits,
-        params,
-        NULL));
+  virtual void TearDown() OVERRIDE {
+    if (resolver_.get())
+      EXPECT_EQ(0u, resolver_->num_running_dispatcher_jobs_for_tests());
+    EXPECT_FALSE(proc_->HasBlockedRequests());
+  }
+
+  virtual void CreateResolverWithLimitsAndParams(
+      const PrioritizedDispatcher::Limits& limits,
+      const HostResolverImpl::ProcTaskParams& params) {
+    resolver_.reset(new HostResolverImpl(HostCache::CreateDefaultCache(),
+                                         limits, params, NULL));
   }
 
   // The Request will not be made until a call to |Resolve()|, and the Job will
@@ -496,25 +509,15 @@
     return CreateRequest(hostname, kDefaultPort);
   }
 
-  virtual void SetUp() OVERRIDE {
-    CreateResolver();
-  }
-
-  virtual void TearDown() OVERRIDE {
-    if (resolver_.get())
-      EXPECT_EQ(0u, resolver_->num_running_jobs_for_tests());
-    EXPECT_FALSE(proc_->HasBlockedRequests());
-  }
-
   void set_handler(Handler* handler) {
     handler_.reset(handler);
     handler_->test = this;
   }
 
   // Friendship is not inherited, so use proxies to access those.
-  size_t num_running_jobs() const {
+  size_t num_running_dispatcher_jobs() const {
     DCHECK(resolver_.get());
-    return resolver_->num_running_jobs_for_tests();
+    return resolver_->num_running_dispatcher_jobs_for_tests();
   }
 
   void set_fallback_to_proctask(bool fallback_to_proctask) {
@@ -522,6 +525,10 @@
     resolver_->fallback_to_proctask_ = fallback_to_proctask;
   }
 
+  static unsigned maximum_dns_failures() {
+    return HostResolverImpl::kMaximumDnsFailures;
+  }
+
   scoped_refptr<MockHostResolverProc> proc_;
   scoped_ptr<HostResolverImpl> resolver_;
   ScopedVector<Request> requests_;
@@ -898,7 +905,7 @@
 
   EXPECT_EQ(ERR_NETWORK_CHANGED, requests_[0]->WaitForResult());
 
-  EXPECT_EQ(1u, num_running_jobs());
+  EXPECT_EQ(1u, num_running_dispatcher_jobs());
 
   EXPECT_FALSE(requests_[1]->completed());
   EXPECT_FALSE(requests_[2]->completed());
@@ -1265,36 +1272,70 @@
 
 // Specialized fixture for tests of DnsTask.
 class HostResolverImplDnsTest : public HostResolverImplTest {
+ public:
+  HostResolverImplDnsTest() : dns_client_(NULL) {}
+
  protected:
+  // testing::Test implementation:
   virtual void SetUp() OVERRIDE {
-    AddDnsRule("nx", dns_protocol::kTypeA, MockDnsClientRule::FAIL);
-    AddDnsRule("nx", dns_protocol::kTypeAAAA, MockDnsClientRule::FAIL);
-    AddDnsRule("ok", dns_protocol::kTypeA, MockDnsClientRule::OK);
-    AddDnsRule("ok", dns_protocol::kTypeAAAA, MockDnsClientRule::OK);
-    AddDnsRule("4ok", dns_protocol::kTypeA, MockDnsClientRule::OK);
-    AddDnsRule("4ok", dns_protocol::kTypeAAAA, MockDnsClientRule::EMPTY);
-    AddDnsRule("6ok", dns_protocol::kTypeA, MockDnsClientRule::EMPTY);
-    AddDnsRule("6ok", dns_protocol::kTypeAAAA, MockDnsClientRule::OK);
-    AddDnsRule("4nx", dns_protocol::kTypeA, MockDnsClientRule::OK);
-    AddDnsRule("4nx", dns_protocol::kTypeAAAA, MockDnsClientRule::FAIL);
+    AddDnsRule("nx", dns_protocol::kTypeA, MockDnsClientRule::FAIL, false);
+    AddDnsRule("nx", dns_protocol::kTypeAAAA, MockDnsClientRule::FAIL, false);
+    AddDnsRule("ok", dns_protocol::kTypeA, MockDnsClientRule::OK, false);
+    AddDnsRule("ok", dns_protocol::kTypeAAAA, MockDnsClientRule::OK, false);
+    AddDnsRule("4ok", dns_protocol::kTypeA, MockDnsClientRule::OK, false);
+    AddDnsRule("4ok", dns_protocol::kTypeAAAA, MockDnsClientRule::EMPTY, false);
+    AddDnsRule("6ok", dns_protocol::kTypeA, MockDnsClientRule::EMPTY, false);
+    AddDnsRule("6ok", dns_protocol::kTypeAAAA, MockDnsClientRule::OK, false);
+    AddDnsRule("4nx", dns_protocol::kTypeA, MockDnsClientRule::OK, false);
+    AddDnsRule("4nx", dns_protocol::kTypeAAAA, MockDnsClientRule::FAIL, false);
+    AddDnsRule("empty", dns_protocol::kTypeA, MockDnsClientRule::EMPTY, false);
+    AddDnsRule("empty", dns_protocol::kTypeAAAA, MockDnsClientRule::EMPTY,
+               false);
+
+    AddDnsRule("slow_nx", dns_protocol::kTypeA, MockDnsClientRule::FAIL, true);
+    AddDnsRule("slow_nx", dns_protocol::kTypeAAAA, MockDnsClientRule::FAIL,
+               true);
+
+    AddDnsRule("4slow_ok", dns_protocol::kTypeA, MockDnsClientRule::OK, true);
+    AddDnsRule("4slow_ok", dns_protocol::kTypeAAAA, MockDnsClientRule::OK,
+               false);
+    AddDnsRule("6slow_ok", dns_protocol::kTypeA, MockDnsClientRule::OK, false);
+    AddDnsRule("6slow_ok", dns_protocol::kTypeAAAA, MockDnsClientRule::OK,
+               true);
+    AddDnsRule("4slow_4ok", dns_protocol::kTypeA, MockDnsClientRule::OK, true);
+    AddDnsRule("4slow_4ok", dns_protocol::kTypeAAAA, MockDnsClientRule::EMPTY,
+               false);
+    AddDnsRule("4slow_4timeout", dns_protocol::kTypeA,
+               MockDnsClientRule::TIMEOUT, true);
+    AddDnsRule("4slow_4timeout", dns_protocol::kTypeAAAA, MockDnsClientRule::OK,
+               false);
+    AddDnsRule("4slow_6timeout", dns_protocol::kTypeA,
+               MockDnsClientRule::OK, true);
+    AddDnsRule("4slow_6timeout", dns_protocol::kTypeAAAA,
+               MockDnsClientRule::TIMEOUT, false);
     CreateResolver();
   }
 
-  void CreateResolver() {
+  // HostResolverImplTest implementation:
+  virtual void CreateResolverWithLimitsAndParams(
+      const PrioritizedDispatcher::Limits& limits,
+      const HostResolverImpl::ProcTaskParams& params) OVERRIDE {
     resolver_.reset(new HostResolverImpl(HostCache::CreateDefaultCache(),
-                                         DefaultLimits(),
-                                         DefaultParams(proc_.get()),
+                                         limits,
+                                         params,
                                          NULL));
     // Disable IPv6 support probing.
     resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_UNSPECIFIED);
-    resolver_->SetDnsClient(CreateMockDnsClient(DnsConfig(), dns_rules_));
+    dns_client_ = new MockDnsClient(DnsConfig(), dns_rules_);
+    resolver_->SetDnsClient(scoped_ptr<DnsClient>(dns_client_));
   }
 
   // Adds a rule to |dns_rules_|. Must be followed by |CreateResolver| to apply.
   void AddDnsRule(const std::string& prefix,
                   uint16 qtype,
-                  MockDnsClientRule::Result result) {
-    dns_rules_.push_back(MockDnsClientRule(prefix, qtype, result));
+                  MockDnsClientRule::Result result,
+                  bool delay) {
+    dns_rules_.push_back(MockDnsClientRule(prefix, qtype, result, delay));
   }
 
   void ChangeDnsConfig(const DnsConfig& config) {
@@ -1304,6 +1345,8 @@
   }
 
   MockDnsClientRuleList dns_rules_;
+  // Owned by |resolver_|.
+  MockDnsClient* dns_client_;
 };
 
 // TODO(szym): Test AbortAllInProgressJobs due to DnsConfig change.
@@ -1371,7 +1414,8 @@
 
   // Simulate the case when the preference or policy has disabled the DNS client
   // causing AbortDnsTasks.
-  resolver_->SetDnsClient(CreateMockDnsClient(DnsConfig(), dns_rules_));
+  resolver_->SetDnsClient(
+      scoped_ptr<DnsClient>(new MockDnsClient(DnsConfig(), dns_rules_)));
   ChangeDnsConfig(CreateValidDnsConfig());
 
   // First request is resolved by MockDnsClient, others should fail due to
@@ -1530,7 +1574,7 @@
   EXPECT_EQ(ERR_IO_PENDING, req->Resolve());
   EXPECT_EQ(OK, req->WaitForResult());
 
-  for (unsigned i = 0; i < 20; ++i) {
+  for (unsigned i = 0; i < maximum_dns_failures(); ++i) {
     // Use custom names to require separate Jobs.
     std::string hostname = base::StringPrintf("nx_%u", i);
     // Ensure fallback to ProcTask succeeds.
@@ -1595,7 +1639,8 @@
                                        DefaultLimits(),
                                        DefaultParams(proc.get()),
                                        NULL));
-  resolver_->SetDnsClient(CreateMockDnsClient(DnsConfig(), dns_rules_));
+  resolver_->SetDnsClient(
+      scoped_ptr<DnsClient>(new MockDnsClient(DnsConfig(), dns_rules_)));
   resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_IPV4);
 
   // Get the expected output.
@@ -1648,4 +1693,346 @@
   EXPECT_EQ(saw_ipv6, req->HasAddress("::1", 80));
 }
 
+// Cancel a request with a single DNS transaction active.
+TEST_F(HostResolverImplDnsTest, CancelWithOneTransactionActive) {
+  resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_IPV4);
+  ChangeDnsConfig(CreateValidDnsConfig());
+
+  EXPECT_EQ(ERR_IO_PENDING, CreateRequest("ok", 80)->Resolve());
+  EXPECT_EQ(1u, num_running_dispatcher_jobs());
+  requests_[0]->Cancel();
+
+  // Dispatcher state checked in TearDown.
+}
+
+// Cancel a request with a single DNS transaction active and another pending.
+TEST_F(HostResolverImplDnsTest, CancelWithOneTransactionActiveOnePending) {
+  CreateSerialResolver();
+  resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_UNSPECIFIED);
+  ChangeDnsConfig(CreateValidDnsConfig());
+
+  EXPECT_EQ(ERR_IO_PENDING, CreateRequest("ok", 80)->Resolve());
+  EXPECT_EQ(1u, num_running_dispatcher_jobs());
+  requests_[0]->Cancel();
+
+  // Dispatcher state checked in TearDown.
+}
+
+// Cancel a request with two DNS transactions active.
+TEST_F(HostResolverImplDnsTest, CancelWithTwoTransactionsActive) {
+  resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_UNSPECIFIED);
+  ChangeDnsConfig(CreateValidDnsConfig());
+
+  EXPECT_EQ(ERR_IO_PENDING, CreateRequest("ok", 80)->Resolve());
+  EXPECT_EQ(2u, num_running_dispatcher_jobs());
+  requests_[0]->Cancel();
+
+  // Dispatcher state checked in TearDown.
+}
+
+// Delete a resolver with some active requests and some queued requests.
+TEST_F(HostResolverImplDnsTest, DeleteWithActiveTransactions) {
+  // At most 10 Jobs active at once.
+  CreateResolverWithLimitsAndParams(
+      PrioritizedDispatcher::Limits(NUM_PRIORITIES, 10u),
+      DefaultParams(proc_.get()));
+
+  resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_UNSPECIFIED);
+  ChangeDnsConfig(CreateValidDnsConfig());
+
+  // First active job is an IPv4 request.
+  EXPECT_EQ(ERR_IO_PENDING, CreateRequest("ok", 80, MEDIUM,
+                                          ADDRESS_FAMILY_IPV4)->Resolve());
+
+  // Add 10 more DNS lookups for different hostnames.  First 4 should have two
+  // active jobs, next one has a single active job, and one pending.  Others
+  // should all be queued.
+  for (int i = 0; i < 10; ++i) {
+    EXPECT_EQ(ERR_IO_PENDING, CreateRequest(
+        base::StringPrintf("ok%i", i))->Resolve());
+  }
+  EXPECT_EQ(10u, num_running_dispatcher_jobs());
+
+  resolver_.reset();
+}
+
+// Cancel a request with only the IPv6 transaction active.
+TEST_F(HostResolverImplDnsTest, CancelWithIPv6TransactionActive) {
+  resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_UNSPECIFIED);
+  ChangeDnsConfig(CreateValidDnsConfig());
+
+  EXPECT_EQ(ERR_IO_PENDING, CreateRequest("6slow_ok", 80)->Resolve());
+  EXPECT_EQ(2u, num_running_dispatcher_jobs());
+
+  // The IPv4 request should complete, the IPv6 request is still pending.
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1u, num_running_dispatcher_jobs());
+  requests_[0]->Cancel();
+
+  // Dispatcher state checked in TearDown.
+}
+
+// Cancel a request with only the IPv4 transaction pending.
+TEST_F(HostResolverImplDnsTest, CancelWithIPv4TransactionPending) {
+  set_fallback_to_proctask(false);
+  resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_UNSPECIFIED);
+  ChangeDnsConfig(CreateValidDnsConfig());
+
+  EXPECT_EQ(ERR_IO_PENDING, CreateRequest("4slow_ok", 80)->Resolve());
+  EXPECT_EQ(2u, num_running_dispatcher_jobs());
+
+  // The IPv6 request should complete, the IPv4 request is still pending.
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1u, num_running_dispatcher_jobs());
+
+  requests_[0]->Cancel();
+}
+
+// Test cases where AAAA completes first.
+TEST_F(HostResolverImplDnsTest, AAAACompletesFirst) {
+  set_fallback_to_proctask(false);
+  resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_UNSPECIFIED);
+  ChangeDnsConfig(CreateValidDnsConfig());
+
+  EXPECT_EQ(ERR_IO_PENDING, CreateRequest("4slow_ok", 80)->Resolve());
+  EXPECT_EQ(ERR_IO_PENDING, CreateRequest("4slow_4ok", 80)->Resolve());
+  EXPECT_EQ(ERR_IO_PENDING, CreateRequest("4slow_4timeout", 80)->Resolve());
+  EXPECT_EQ(ERR_IO_PENDING, CreateRequest("4slow_6timeout", 80)->Resolve());
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(requests_[0]->completed());
+  EXPECT_FALSE(requests_[1]->completed());
+  EXPECT_FALSE(requests_[2]->completed());
+  // The IPv6 of the third request should have failed and resulted in cancelling
+  // the IPv4 request.
+  EXPECT_TRUE(requests_[3]->completed());
+  EXPECT_EQ(ERR_DNS_TIMED_OUT, requests_[3]->result());
+  EXPECT_EQ(3u, num_running_dispatcher_jobs());
+
+  dns_client_->CompleteDelayedTransactions();
+  EXPECT_TRUE(requests_[0]->completed());
+  EXPECT_EQ(OK, requests_[0]->result());
+  EXPECT_EQ(2u, requests_[0]->NumberOfAddresses());
+  EXPECT_TRUE(requests_[0]->HasAddress("127.0.0.1", 80));
+  EXPECT_TRUE(requests_[0]->HasAddress("::1", 80));
+
+  EXPECT_TRUE(requests_[1]->completed());
+  EXPECT_EQ(OK, requests_[1]->result());
+  EXPECT_EQ(1u, requests_[1]->NumberOfAddresses());
+  EXPECT_TRUE(requests_[1]->HasAddress("127.0.0.1", 80));
+
+  EXPECT_TRUE(requests_[2]->completed());
+  EXPECT_EQ(ERR_DNS_TIMED_OUT, requests_[2]->result());
+}
+
+// Test the case where only a single transaction slot is available.
+TEST_F(HostResolverImplDnsTest, SerialResolver) {
+  CreateSerialResolver();
+  set_fallback_to_proctask(false);
+  resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_UNSPECIFIED);
+  ChangeDnsConfig(CreateValidDnsConfig());
+
+  EXPECT_EQ(ERR_IO_PENDING, CreateRequest("ok", 80)->Resolve());
+  EXPECT_EQ(1u, num_running_dispatcher_jobs());
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(requests_[0]->completed());
+  EXPECT_EQ(OK, requests_[0]->result());
+  EXPECT_EQ(2u, requests_[0]->NumberOfAddresses());
+  EXPECT_TRUE(requests_[0]->HasAddress("127.0.0.1", 80));
+  EXPECT_TRUE(requests_[0]->HasAddress("::1", 80));
+}
+
+// Test the case where the AAAA query is started when another transaction
+// completes.
+TEST_F(HostResolverImplDnsTest, AAAAStartsAfterOtherJobFinishes) {
+  CreateResolverWithLimitsAndParams(
+      PrioritizedDispatcher::Limits(NUM_PRIORITIES, 2),
+      DefaultParams(proc_.get()));
+  set_fallback_to_proctask(false);
+  resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_UNSPECIFIED);
+  ChangeDnsConfig(CreateValidDnsConfig());
+
+  EXPECT_EQ(ERR_IO_PENDING, CreateRequest("ok", 80, MEDIUM,
+                                          ADDRESS_FAMILY_IPV4)->Resolve());
+  EXPECT_EQ(ERR_IO_PENDING,
+            CreateRequest("4slow_ok", 80, MEDIUM)->Resolve());
+  // An IPv4 request should have been started pending for each job.
+  EXPECT_EQ(2u, num_running_dispatcher_jobs());
+
+  // Request 0's IPv4 request should complete, starting Request 1's IPv6
+  // request, which should also complete.
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1u, num_running_dispatcher_jobs());
+  EXPECT_TRUE(requests_[0]->completed());
+  EXPECT_FALSE(requests_[1]->completed());
+
+  dns_client_->CompleteDelayedTransactions();
+  EXPECT_TRUE(requests_[1]->completed());
+  EXPECT_EQ(OK, requests_[1]->result());
+  EXPECT_EQ(2u, requests_[1]->NumberOfAddresses());
+  EXPECT_TRUE(requests_[1]->HasAddress("127.0.0.1", 80));
+  EXPECT_TRUE(requests_[1]->HasAddress("::1", 80));
+}
+
+// Tests the case that a Job with a single transaction receives an empty address
+// list, triggering fallback to ProcTask.
+TEST_F(HostResolverImplDnsTest, IPv4EmptyFallback) {
+  ChangeDnsConfig(CreateValidDnsConfig());
+  proc_->AddRuleForAllFamilies("empty_fallback", "192.168.0.1");
+  proc_->SignalMultiple(1u);
+  EXPECT_EQ(ERR_IO_PENDING,
+            CreateRequest("empty_fallback", 80, MEDIUM,
+                          ADDRESS_FAMILY_IPV4)->Resolve());
+  EXPECT_EQ(OK, requests_[0]->WaitForResult());
+  EXPECT_TRUE(requests_[0]->HasOneAddress("192.168.0.1", 80));
+}
+
+// Tests the case that a Job with two transactions receives two empty address
+// lists, triggering fallback to ProcTask.
+TEST_F(HostResolverImplDnsTest, UnspecEmptyFallback) {
+  ChangeDnsConfig(CreateValidDnsConfig());
+  proc_->AddRuleForAllFamilies("empty_fallback", "192.168.0.1");
+  proc_->SignalMultiple(1u);
+  EXPECT_EQ(ERR_IO_PENDING,
+            CreateRequest("empty_fallback", 80, MEDIUM,
+                          ADDRESS_FAMILY_UNSPECIFIED)->Resolve());
+  EXPECT_EQ(OK, requests_[0]->WaitForResult());
+  EXPECT_TRUE(requests_[0]->HasOneAddress("192.168.0.1", 80));
+}
+
+// Tests getting a new invalid DnsConfig while there are active DnsTasks.
+TEST_F(HostResolverImplDnsTest, InvalidDnsConfigWithPendingRequests) {
+  // At most 3 jobs active at once.  This number is important, since we want to
+  // make sure that aborting the first HostResolverImpl::Job does not trigger
+  // another DnsTransaction on the second Job when it releases its second
+  // prioritized dispatcher slot.
+  CreateResolverWithLimitsAndParams(
+      PrioritizedDispatcher::Limits(NUM_PRIORITIES, 3u),
+      DefaultParams(proc_.get()));
+
+  resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_UNSPECIFIED);
+  ChangeDnsConfig(CreateValidDnsConfig());
+
+  proc_->AddRuleForAllFamilies("slow_nx1", "192.168.0.1");
+  proc_->AddRuleForAllFamilies("slow_nx2", "192.168.0.2");
+  proc_->AddRuleForAllFamilies("ok", "192.168.0.3");
+
+  // First active job gets two slots.
+  EXPECT_EQ(ERR_IO_PENDING, CreateRequest("slow_nx1")->Resolve());
+  // Next job gets one slot, and waits on another.
+  EXPECT_EQ(ERR_IO_PENDING, CreateRequest("slow_nx2")->Resolve());
+  EXPECT_EQ(ERR_IO_PENDING, CreateRequest("ok")->Resolve());
+
+  EXPECT_EQ(3u, num_running_dispatcher_jobs());
+
+  // Clear DNS config.  Two in-progress jobs should be aborted, and the next one
+  // should use a ProcTask.
+  ChangeDnsConfig(DnsConfig());
+  EXPECT_EQ(ERR_NETWORK_CHANGED, requests_[0]->WaitForResult());
+  EXPECT_EQ(ERR_NETWORK_CHANGED, requests_[1]->WaitForResult());
+
+  // Finish up the third job.  Should bypass the DnsClient, and get its results
+  // from MockHostResolverProc.
+  EXPECT_FALSE(requests_[2]->completed());
+  proc_->SignalMultiple(1u);
+  EXPECT_EQ(OK, requests_[2]->WaitForResult());
+  EXPECT_TRUE(requests_[2]->HasOneAddress("192.168.0.3", 80));
+}
+
+// Tests the case that DnsClient is automatically disabled due to failures
+// while there are active DnsTasks.
+TEST_F(HostResolverImplDnsTest,
+       AutomaticallyDisableDnsClientWithPendingRequests) {
+  // Trying different limits is important for this test:  Different limits
+  // result in different behavior when aborting in-progress DnsTasks.  Having
+  // a DnsTask that has one job active and one in the queue when another job
+  // occupying two slots has its DnsTask aborted is the case most likely to run
+  // into problems.
+  for (size_t limit = 1u; limit < 6u; ++limit) {
+    CreateResolverWithLimitsAndParams(
+        PrioritizedDispatcher::Limits(NUM_PRIORITIES, limit),
+        DefaultParams(proc_.get()));
+
+    resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_UNSPECIFIED);
+    ChangeDnsConfig(CreateValidDnsConfig());
+
+    // Queue up enough failures to disable DnsTasks.  These will all fall back
+    // to ProcTasks, and succeed there.
+    for (unsigned i = 0u; i < maximum_dns_failures(); ++i) {
+      std::string host = base::StringPrintf("nx%u", i);
+      proc_->AddRuleForAllFamilies(host, "192.168.0.1");
+      EXPECT_EQ(ERR_IO_PENDING, CreateRequest(host)->Resolve());
+    }
+
+    // These requests should all bypass DnsTasks, due to the above failures,
+    // so should end up using ProcTasks.
+    proc_->AddRuleForAllFamilies("slow_ok1", "192.168.0.2");
+    EXPECT_EQ(ERR_IO_PENDING, CreateRequest("slow_ok1")->Resolve());
+    proc_->AddRuleForAllFamilies("slow_ok2", "192.168.0.3");
+    EXPECT_EQ(ERR_IO_PENDING, CreateRequest("slow_ok2")->Resolve());
+    proc_->AddRuleForAllFamilies("slow_ok3", "192.168.0.4");
+    EXPECT_EQ(ERR_IO_PENDING, CreateRequest("slow_ok3")->Resolve());
+    proc_->SignalMultiple(maximum_dns_failures() + 3);
+
+    for (size_t i = 0u; i < maximum_dns_failures(); ++i) {
+      EXPECT_EQ(OK, requests_[i]->WaitForResult());
+      EXPECT_TRUE(requests_[i]->HasOneAddress("192.168.0.1", 80));
+    }
+
+    EXPECT_EQ(OK, requests_[maximum_dns_failures()]->WaitForResult());
+    EXPECT_TRUE(requests_[maximum_dns_failures()]->HasOneAddress(
+                    "192.168.0.2", 80));
+    EXPECT_EQ(OK, requests_[maximum_dns_failures() + 1]->WaitForResult());
+    EXPECT_TRUE(requests_[maximum_dns_failures() + 1]->HasOneAddress(
+                    "192.168.0.3", 80));
+    EXPECT_EQ(OK, requests_[maximum_dns_failures() + 2]->WaitForResult());
+    EXPECT_TRUE(requests_[maximum_dns_failures() + 2]->HasOneAddress(
+                    "192.168.0.4", 80));
+    requests_.clear();
+  }
+}
+
+// Tests a call to SetDnsClient while there are active DnsTasks.
+TEST_F(HostResolverImplDnsTest, ManuallyDisableDnsClientWithPendingRequests) {
+  // At most 3 jobs active at once.  This number is important, since we want to
+  // make sure that aborting the first HostResolverImpl::Job does not trigger
+  // another DnsTransaction on the second Job when it releases its second
+  // prioritized dispatcher slot.
+  CreateResolverWithLimitsAndParams(
+      PrioritizedDispatcher::Limits(NUM_PRIORITIES, 3u),
+      DefaultParams(proc_.get()));
+
+  resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_UNSPECIFIED);
+  ChangeDnsConfig(CreateValidDnsConfig());
+
+  proc_->AddRuleForAllFamilies("slow_ok1", "192.168.0.1");
+  proc_->AddRuleForAllFamilies("slow_ok2", "192.168.0.2");
+  proc_->AddRuleForAllFamilies("ok", "192.168.0.3");
+
+  // First active job gets two slots.
+  EXPECT_EQ(ERR_IO_PENDING, CreateRequest("slow_ok1")->Resolve());
+  // Next job gets one slot, and waits on another.
+  EXPECT_EQ(ERR_IO_PENDING, CreateRequest("slow_ok2")->Resolve());
+  // Next one is queued.
+  EXPECT_EQ(ERR_IO_PENDING, CreateRequest("ok")->Resolve());
+
+  EXPECT_EQ(3u, num_running_dispatcher_jobs());
+
+  // Clear DnsClient.  The two in-progress jobs should fall back to a ProcTask,
+  // and the next one should be started with a ProcTask.
+  resolver_->SetDnsClient(scoped_ptr<DnsClient>());
+
+  // All three in-progress requests should now be running a ProcTask.
+  EXPECT_EQ(3u, num_running_dispatcher_jobs());
+  proc_->SignalMultiple(3u);
+
+  EXPECT_EQ(OK, requests_[0]->WaitForResult());
+  EXPECT_TRUE(requests_[0]->HasOneAddress("192.168.0.1", 80));
+  EXPECT_EQ(OK, requests_[1]->WaitForResult());
+  EXPECT_TRUE(requests_[1]->HasOneAddress("192.168.0.2", 80));
+  EXPECT_EQ(OK, requests_[2]->WaitForResult());
+  EXPECT_TRUE(requests_[2]->HasOneAddress("192.168.0.3", 80));
+}
+
 }  // namespace net