summaryrefslogtreecommitdiffstats
path: root/chrome/browser/net
diff options
context:
space:
mode:
authorttuttle@chromium.org <ttuttle@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-17 05:23:13 +0000
committerttuttle@chromium.org <ttuttle@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-17 05:23:13 +0000
commit2ea1efe9660ee70c8eefb98dde76caac1c918005 (patch)
treeb5b7e0ea619ae3df960f25d3c4ce23cbba335e2c /chrome/browser/net
parent3793b5f264db5e0ce9950d0e28e8748e5b8c56ff (diff)
downloadchromium_src-2ea1efe9660ee70c8eefb98dde76caac1c918005.zip
chromium_src-2ea1efe9660ee70c8eefb98dde76caac1c918005.tar.gz
chromium_src-2ea1efe9660ee70c8eefb98dde76caac1c918005.tar.bz2
Display DNS probe results.
1. Modify the browser-side NetErrorTabHelper to send extra messages when it starts or declines to start a DNS probe. 2. Create a new error domain, "dnsprobe", with errors for "might run a DNS probe", "currently running a DNS probe", and all of the possible probe results. 3. Modify ChromeContentRendererClient to give the renderer-side NetErrorHelper a chance to choose the error strings before we call LocalizedError directly. 4. Catch DNS errors and provide the strings for the "might run a DNS probe" pseudo-error instead. 5. Add a function to neterror.html that lets us re-render the template with a new set of strings. 6. When we get a "probe started" message, replace the strings with those for the "currently running a DNS probe" pseudo-error. 7. When we get a "probe finished" message, replace the strings with those for the probe result. 8. When we get a "probe not run" message, replace the strings with those for the original error we received. BUG=156415 TEST=DnsProbeBrowserTest Review URL: https://chromiumcodereview.appspot.com/13270005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@211950 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/net')
-rw-r--r--chrome/browser/net/dns_probe_browsertest.cc535
-rw-r--r--chrome/browser/net/dns_probe_job.cc236
-rw-r--r--chrome/browser/net/dns_probe_job.h55
-rw-r--r--chrome/browser/net/dns_probe_job_unittest.cc127
-rw-r--r--chrome/browser/net/dns_probe_runner.cc143
-rw-r--r--chrome/browser/net/dns_probe_runner.h85
-rw-r--r--chrome/browser/net/dns_probe_runner_unittest.cc95
-rw-r--r--chrome/browser/net/dns_probe_service.cc392
-rw-r--r--chrome/browser/net/dns_probe_service.h78
-rw-r--r--chrome/browser/net/dns_probe_service_unittest.cc239
-rw-r--r--chrome/browser/net/dns_probe_test_util.cc37
-rw-r--r--chrome/browser/net/dns_probe_test_util.h21
-rw-r--r--chrome/browser/net/net_error_tab_helper.cc166
-rw-r--r--chrome/browser/net/net_error_tab_helper.h66
-rw-r--r--chrome/browser/net/net_error_tab_helper_unittest.cc392
15 files changed, 1696 insertions, 971 deletions
diff --git a/chrome/browser/net/dns_probe_browsertest.cc b/chrome/browser/net/dns_probe_browsertest.cc
new file mode 100644
index 0000000..76d494a
--- /dev/null
+++ b/chrome/browser/net/dns_probe_browsertest.cc
@@ -0,0 +1,535 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/path_service.h"
+#include "base/threading/thread_restrictions.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/google/google_util.h"
+#include "chrome/browser/io_thread.h"
+#include "chrome/browser/net/dns_probe_test_util.h"
+#include "chrome/browser/net/net_error_tab_helper.h"
+#include "chrome/browser/net/url_request_mock_util.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/net/net_error_info.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/test/net/url_request_failed_job.h"
+#include "content/test/net/url_request_mock_http_job.h"
+#include "net/base/net_errors.h"
+#include "net/dns/dns_test_util.h"
+#include "net/url_request/url_request_filter.h"
+#include "net/url_request/url_request_job.h"
+#include "net/url_request/url_request_job_factory.h"
+
+using base::Bind;
+using base::Callback;
+using base::Closure;
+using base::ConstRef;
+using base::FilePath;
+using base::MessageLoop;
+using base::Unretained;
+using chrome_common_net::DnsProbeStatus;
+using content::BrowserThread;
+using content::URLRequestFailedJob;
+using content::URLRequestMockHTTPJob;
+using content::WebContents;
+using google_util::LinkDoctorBaseURL;
+using net::MockDnsClientRule;
+using net::NetworkDelegate;
+using net::URLRequest;
+using net::URLRequestFilter;
+using net::URLRequestJob;
+using net::URLRequestJobFactory;
+using ui_test_utils::NavigateToURL;
+using ui_test_utils::NavigateToURLBlockUntilNavigationsComplete;
+
+namespace chrome_browser_net {
+
+namespace {
+
+// Wraps DnsProbeService and delays callbacks until someone calls
+// CallDelayedCallbacks. This allows the DnsProbeBrowserTest to enforce a
+// stricter ordering of events.
+class DelayingDnsProbeService : public DnsProbeService {
+ public:
+ DelayingDnsProbeService() {}
+
+ virtual ~DelayingDnsProbeService() {
+ EXPECT_TRUE(delayed_probes_.empty());
+ }
+
+ virtual void ProbeDns(const ProbeCallback& callback) OVERRIDE {
+ delayed_probes_.push_back(callback);
+ }
+
+ void StartDelayedProbes() {
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ std::vector<ProbeCallback> probes;
+ probes.swap(delayed_probes_);
+
+ for (std::vector<ProbeCallback>::const_iterator i = probes.begin();
+ i != probes.end(); ++i) {
+ DnsProbeService::ProbeDns(*i);
+ }
+ }
+
+ int delayed_probe_count() const {
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ return delayed_probes_.size();
+ }
+
+ private:
+ std::vector<ProbeCallback> delayed_probes_;
+};
+
+FilePath GetMockLinkDoctorFilePath() {
+ FilePath root_http;
+ PathService::Get(chrome::DIR_TEST_DATA, &root_http);
+ return root_http.AppendASCII("mock-link-doctor.html");
+}
+
+class BreakableLinkDoctorProtocolHandler
+ : public URLRequestJobFactory::ProtocolHandler {
+ public:
+ explicit BreakableLinkDoctorProtocolHandler(
+ const FilePath& mock_link_doctor_file_path)
+ : mock_link_doctor_file_path_(mock_link_doctor_file_path),
+ net_error_(net::OK) {}
+
+ virtual ~BreakableLinkDoctorProtocolHandler() {}
+
+ virtual URLRequestJob* MaybeCreateJob(
+ URLRequest* request, NetworkDelegate* network_delegate) const OVERRIDE {
+ if (net_error_ != net::OK) {
+ return new URLRequestFailedJob(request, network_delegate, net_error_);
+ } else {
+ return new URLRequestMockHTTPJob(
+ request, network_delegate, mock_link_doctor_file_path_);
+ }
+ }
+
+ void set_net_error(int net_error) { net_error_ = net_error; }
+
+ private:
+ const FilePath mock_link_doctor_file_path_;
+ int net_error_;
+};
+
+class DnsProbeBrowserTestIOThreadHelper {
+ public:
+ DnsProbeBrowserTestIOThreadHelper();
+
+ void SetUpOnIOThread(IOThread* io_thread);
+ void CleanUpOnIOThreadAndDeleteHelper();
+
+ void SetMockDnsClientRules(MockDnsClientRule::Result system_good_result,
+ MockDnsClientRule::Result public_good_result);
+ void SetLinkDoctorNetError(int link_doctor_net_error);
+ void StartDelayedProbes(int expected_delayed_probe_count);
+
+ private:
+ IOThread* io_thread_;
+ DnsProbeService* original_dns_probe_service_;
+ DelayingDnsProbeService* delaying_dns_probe_service_;
+ BreakableLinkDoctorProtocolHandler* protocol_handler_;
+ FilePath mock_link_doctor_file_path_;
+};
+
+DnsProbeBrowserTestIOThreadHelper::DnsProbeBrowserTestIOThreadHelper()
+ : io_thread_(NULL),
+ original_dns_probe_service_(NULL),
+ delaying_dns_probe_service_(NULL),
+ protocol_handler_(NULL),
+ mock_link_doctor_file_path_(GetMockLinkDoctorFilePath()) {}
+
+void DnsProbeBrowserTestIOThreadHelper::SetUpOnIOThread(IOThread* io_thread) {
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ CHECK(io_thread);
+ CHECK(!io_thread_);
+ CHECK(!original_dns_probe_service_);
+ CHECK(!delaying_dns_probe_service_);
+ CHECK(!protocol_handler_);
+
+ io_thread_ = io_thread;
+
+ delaying_dns_probe_service_ = new DelayingDnsProbeService();
+
+ IOThread::Globals* globals = io_thread_->globals();
+ original_dns_probe_service_ = globals->dns_probe_service.release();
+ globals->dns_probe_service.reset(delaying_dns_probe_service_);
+
+ URLRequestFailedJob::AddUrlHandler();
+
+ scoped_ptr<URLRequestJobFactory::ProtocolHandler> protocol_handler(
+ new BreakableLinkDoctorProtocolHandler(mock_link_doctor_file_path_));
+ protocol_handler_ =
+ static_cast<BreakableLinkDoctorProtocolHandler*>(protocol_handler.get());
+ const GURL link_doctor_base_url = LinkDoctorBaseURL();
+ const std::string link_doctor_host = link_doctor_base_url.host();
+ URLRequestFilter::GetInstance()->AddHostnameProtocolHandler(
+ "http", link_doctor_host, protocol_handler.Pass());
+}
+
+void DnsProbeBrowserTestIOThreadHelper::CleanUpOnIOThreadAndDeleteHelper() {
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ URLRequestFilter::GetInstance()->ClearHandlers();
+
+ IOThread::Globals* globals = io_thread_->globals();
+ scoped_ptr<DnsProbeService> delaying_dns_probe_service(
+ globals->dns_probe_service.release());
+ globals->dns_probe_service.reset(original_dns_probe_service_);
+
+ CHECK_EQ(delaying_dns_probe_service_, delaying_dns_probe_service.get());
+
+ delete this;
+}
+
+void DnsProbeBrowserTestIOThreadHelper::SetMockDnsClientRules(
+ MockDnsClientRule::Result system_result,
+ MockDnsClientRule::Result public_result) {
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ DnsProbeService* service = io_thread_->globals()->dns_probe_service.get();
+ service->SetSystemClientForTesting(
+ CreateMockDnsClientForProbes(system_result));
+ service->SetPublicClientForTesting(
+ CreateMockDnsClientForProbes(public_result));
+}
+
+void DnsProbeBrowserTestIOThreadHelper::SetLinkDoctorNetError(
+ int link_doctor_net_error) {
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ protocol_handler_->set_net_error(link_doctor_net_error);
+}
+
+void DnsProbeBrowserTestIOThreadHelper::StartDelayedProbes(
+ int expected_delayed_probe_count) {
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ CHECK(delaying_dns_probe_service_);
+
+ int actual_delayed_probe_count =
+ delaying_dns_probe_service_->delayed_probe_count();
+ EXPECT_EQ(expected_delayed_probe_count, actual_delayed_probe_count);
+
+ delaying_dns_probe_service_->StartDelayedProbes();
+}
+
+class DnsProbeBrowserTest : public InProcessBrowserTest {
+ public:
+ DnsProbeBrowserTest();
+
+ virtual void SetUpOnMainThread() OVERRIDE;
+ virtual void CleanUpOnMainThread() OVERRIDE;
+
+ protected:
+ void SetLinkDoctorBroken(bool broken);
+ void SetMockDnsClientRules(MockDnsClientRule::Result system_result,
+ MockDnsClientRule::Result public_result);
+ void NavigateToDnsError();
+ void NavigateToOtherError();
+
+ void StartDelayedProbes(int expected_delayed_probe_count);
+ DnsProbeStatus WaitForSentStatus();
+ int pending_status_count() const { return dns_probe_status_queue_.size(); }
+
+ std::string Title();
+ bool PageContains(const std::string& expected);
+
+ private:
+ void OnDnsProbeStatusSent(DnsProbeStatus dns_probe_status);
+
+ DnsProbeBrowserTestIOThreadHelper* helper_;
+
+ bool awaiting_dns_probe_status_;
+ // Queue of statuses received but not yet consumed by WaitForSentStatus().
+ std::list<DnsProbeStatus> dns_probe_status_queue_;
+};
+
+DnsProbeBrowserTest::DnsProbeBrowserTest()
+ : helper_(new DnsProbeBrowserTestIOThreadHelper()),
+ awaiting_dns_probe_status_(false) {
+}
+
+void DnsProbeBrowserTest::SetUpOnMainThread() {
+ NetErrorTabHelper::set_state_for_testing(
+ NetErrorTabHelper::TESTING_FORCE_ENABLED);
+
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ Bind(&DnsProbeBrowserTestIOThreadHelper::SetUpOnIOThread,
+ Unretained(helper_),
+ g_browser_process->io_thread()));
+
+ NetErrorTabHelper* tab_helper = NetErrorTabHelper::FromWebContents(
+ browser()->tab_strip_model()->GetActiveWebContents());
+ tab_helper->set_dns_probe_status_snoop_callback_for_testing(Bind(
+ &DnsProbeBrowserTest::OnDnsProbeStatusSent,
+ Unretained(this)));
+}
+
+void DnsProbeBrowserTest::CleanUpOnMainThread() {
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ Bind(&DnsProbeBrowserTestIOThreadHelper::CleanUpOnIOThreadAndDeleteHelper,
+ Unretained(helper_)));
+
+ NetErrorTabHelper::set_state_for_testing(
+ NetErrorTabHelper::TESTING_DEFAULT);
+}
+
+void DnsProbeBrowserTest::SetLinkDoctorBroken(bool broken) {
+ int net_error = broken ? net::ERR_NAME_NOT_RESOLVED : net::OK;
+
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ Bind(&DnsProbeBrowserTestIOThreadHelper::SetLinkDoctorNetError,
+ Unretained(helper_),
+ net_error));
+}
+
+// These two functions wait for two navigations because Link Doctor loads two
+// pages: a blank page, so the user stops seeing the previous page, and then
+// either the Link Doctor page or a regular error page. We want to wait for
+// the error page, so we wait for both loads to finish.
+
+void DnsProbeBrowserTest::NavigateToDnsError() {
+ NavigateToURLBlockUntilNavigationsComplete(
+ browser(),
+ URLRequestFailedJob::GetMockHttpUrl(net::ERR_NAME_NOT_RESOLVED),
+ 2);
+}
+
+void DnsProbeBrowserTest::NavigateToOtherError() {
+ NavigateToURLBlockUntilNavigationsComplete(
+ browser(),
+ URLRequestFailedJob::GetMockHttpUrl(net::ERR_CONNECTION_REFUSED),
+ 2);
+}
+
+void DnsProbeBrowserTest::SetMockDnsClientRules(
+ MockDnsClientRule::Result system_result,
+ MockDnsClientRule::Result public_result) {
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ Bind(&DnsProbeBrowserTestIOThreadHelper::SetMockDnsClientRules,
+ Unretained(helper_),
+ system_result,
+ public_result));
+}
+
+void DnsProbeBrowserTest::StartDelayedProbes(
+ int expected_delayed_probe_count) {
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ Bind(&DnsProbeBrowserTestIOThreadHelper::StartDelayedProbes,
+ Unretained(helper_),
+ expected_delayed_probe_count));
+}
+
+DnsProbeStatus DnsProbeBrowserTest::WaitForSentStatus() {
+ CHECK(!awaiting_dns_probe_status_);
+ while (dns_probe_status_queue_.empty()) {
+ awaiting_dns_probe_status_ = true;
+ MessageLoop::current()->Run();
+ awaiting_dns_probe_status_ = false;
+ }
+
+ CHECK(!dns_probe_status_queue_.empty());
+ DnsProbeStatus status = dns_probe_status_queue_.front();
+ dns_probe_status_queue_.pop_front();
+ return status;
+}
+
+// Check title by roundtripping to renderer, to make sure any probe results
+// sent before this have been applied.
+std::string DnsProbeBrowserTest::Title() {
+ std::string title;
+
+ WebContents* contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+
+ bool rv = content::ExecuteScriptAndExtractString(
+ contents,
+ "domAutomationController.send(document.title);",
+ &title);
+ if (!rv)
+ return "";
+
+ return title;
+}
+
+// Check text by roundtripping to renderer, to make sure any probe results
+// sent before this have been applied.
+bool DnsProbeBrowserTest::PageContains(const std::string& expected) {
+ std::string text_content;
+
+ bool rv = content::ExecuteScriptAndExtractString(
+ browser()->tab_strip_model()->GetActiveWebContents(),
+ "domAutomationController.send(document.body.textContent);",
+ &text_content);
+ if (!rv)
+ return false;
+
+ return text_content.find(expected) != std::string::npos;
+}
+
+void DnsProbeBrowserTest::OnDnsProbeStatusSent(
+ DnsProbeStatus dns_probe_status) {
+ dns_probe_status_queue_.push_back(dns_probe_status);
+ if (awaiting_dns_probe_status_)
+ MessageLoop::current()->Quit();
+}
+
+// Make sure probes don't break non-DNS error pages when Link Doctor loads.
+IN_PROC_BROWSER_TEST_F(DnsProbeBrowserTest, OtherErrorWithWorkingLinkDoctor) {
+ SetLinkDoctorBroken(false);
+
+ NavigateToOtherError();
+ EXPECT_EQ("Mock Link Doctor", Title());
+
+ EXPECT_EQ(0, pending_status_count());
+}
+
+// Make sure probes don't break non-DNS error pages when Link Doctor doesn't
+// load.
+IN_PROC_BROWSER_TEST_F(DnsProbeBrowserTest, OtherErrorWithBrokenLinkDoctor) {
+ SetLinkDoctorBroken(true);
+
+ NavigateToOtherError();
+ EXPECT_TRUE(PageContains("CONNECTION_REFUSED"));
+
+ EXPECT_EQ(0, pending_status_count());
+}
+
+// Make sure probes don't break DNS error pages when Link doctor loads.
+IN_PROC_BROWSER_TEST_F(DnsProbeBrowserTest,
+ NxdomainProbeResultWithWorkingLinkDoctor) {
+ SetLinkDoctorBroken(false);
+ SetMockDnsClientRules(MockDnsClientRule::OK, MockDnsClientRule::OK);
+
+ NavigateToDnsError();
+ EXPECT_EQ("Mock Link Doctor", Title());
+
+ EXPECT_EQ(chrome_common_net::DNS_PROBE_STARTED, WaitForSentStatus());
+ EXPECT_EQ(chrome_common_net::DNS_PROBE_STARTED, WaitForSentStatus());
+ EXPECT_EQ(0, pending_status_count());
+ EXPECT_EQ("Mock Link Doctor", Title());
+
+ StartDelayedProbes(1);
+
+ EXPECT_EQ(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN,
+ WaitForSentStatus());
+ EXPECT_EQ(0, pending_status_count());
+ EXPECT_EQ("Mock Link Doctor", Title());
+}
+
+// Make sure probes update DNS error page properly when they're supposed to.
+IN_PROC_BROWSER_TEST_F(DnsProbeBrowserTest,
+ NoInternetProbeResultWithBrokenLinkDoctor) {
+ SetLinkDoctorBroken(true);
+ SetMockDnsClientRules(MockDnsClientRule::TIMEOUT,
+ MockDnsClientRule::TIMEOUT);
+
+ NavigateToDnsError();
+
+ EXPECT_EQ(chrome_common_net::DNS_PROBE_STARTED, WaitForSentStatus());
+ EXPECT_EQ(chrome_common_net::DNS_PROBE_STARTED, WaitForSentStatus());
+
+ // PageContains runs the RunLoop, so make sure nothing hairy happens.
+ EXPECT_EQ(0, pending_status_count());
+ EXPECT_TRUE(PageContains("DNS_PROBE_STARTED"));
+ EXPECT_EQ(0, pending_status_count());
+
+ StartDelayedProbes(1);
+
+ EXPECT_EQ(chrome_common_net::DNS_PROBE_FINISHED_NO_INTERNET,
+ WaitForSentStatus());
+
+ // PageContains runs the RunLoop, so make sure nothing hairy happens.
+ EXPECT_EQ(0, pending_status_count());
+ EXPECT_TRUE(PageContains("DNS_PROBE_FINISHED_NO_INTERNET"));
+ EXPECT_EQ(0, pending_status_count());
+}
+
+// Double-check to make sure sync failures don't explode.
+IN_PROC_BROWSER_TEST_F(DnsProbeBrowserTest, SyncFailureWithBrokenLinkDoctor) {
+ SetLinkDoctorBroken(true);
+ SetMockDnsClientRules(MockDnsClientRule::FAIL_SYNC,
+ MockDnsClientRule::FAIL_SYNC);
+
+ NavigateToDnsError();
+
+ EXPECT_EQ(chrome_common_net::DNS_PROBE_STARTED, WaitForSentStatus());
+ EXPECT_EQ(chrome_common_net::DNS_PROBE_STARTED, WaitForSentStatus());
+
+ // PageContains runs the RunLoop, so make sure nothing hairy happens.
+ EXPECT_EQ(0, pending_status_count());
+ EXPECT_TRUE(PageContains("DNS_PROBE_STARTED"));
+ EXPECT_EQ(0, pending_status_count());
+
+ StartDelayedProbes(1);
+
+ EXPECT_EQ(chrome_common_net::DNS_PROBE_FINISHED_INCONCLUSIVE,
+ WaitForSentStatus());
+
+ // PageContains runs the RunLoop, so make sure nothing hairy happens.
+ EXPECT_EQ(0, pending_status_count());
+ EXPECT_TRUE(PageContains("NAME_NOT_RESOLVED"));
+ EXPECT_EQ(0, pending_status_count());
+}
+
+// Make sure probes don't run for subframe DNS errors.
+IN_PROC_BROWSER_TEST_F(DnsProbeBrowserTest, NoProbeInSubframe) {
+ SetLinkDoctorBroken(false);
+
+ const FilePath::CharType kIframeDnsErrorHtmlName[] =
+ FILE_PATH_LITERAL("iframe_dns_error.html");
+
+ NavigateToURL(
+ browser(),
+ URLRequestMockHTTPJob::GetMockUrl(FilePath(kIframeDnsErrorHtmlName)));
+
+ // By the time NavigateToURL returns, the browser will have seen the failed
+ // provisional load. If a probe was started (or considered but not run),
+ // then the NetErrorTabHelper would have sent a NetErrorInfo message. Thus,
+ // if one hasn't been sent by now, the NetErrorTabHelper has not (and won't)
+ // start a probe for this DNS error.
+ EXPECT_EQ(0, pending_status_count());
+}
+
+// Make sure browser sends NOT_RUN properly when probes are disabled.
+IN_PROC_BROWSER_TEST_F(DnsProbeBrowserTest, ProbesDisabled) {
+ NetErrorTabHelper::set_state_for_testing(
+ NetErrorTabHelper::TESTING_FORCE_DISABLED);
+
+ SetLinkDoctorBroken(true);
+ SetMockDnsClientRules(MockDnsClientRule::TIMEOUT,
+ MockDnsClientRule::TIMEOUT);
+
+ NavigateToDnsError();
+
+ EXPECT_EQ(chrome_common_net::DNS_PROBE_NOT_RUN, WaitForSentStatus());
+ EXPECT_EQ(chrome_common_net::DNS_PROBE_NOT_RUN, WaitForSentStatus());
+
+ // PageContains runs the RunLoop, so make sure nothing hairy happens.
+ EXPECT_EQ(0, pending_status_count());
+ EXPECT_TRUE(PageContains("NAME_NOT_RESOLVED"));
+ EXPECT_EQ(0, pending_status_count());
+}
+
+} // namespace
+
+} // namespace chrome_browser_net
diff --git a/chrome/browser/net/dns_probe_job.cc b/chrome/browser/net/dns_probe_job.cc
deleted file mode 100644
index f51f450..0000000
--- a/chrome/browser/net/dns_probe_job.cc
+++ /dev/null
@@ -1,236 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/net/dns_probe_job.h"
-
-#include "base/compiler_specific.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/weak_ptr.h"
-#include "base/message_loop.h"
-#include "base/time/time.h"
-#include "net/base/address_list.h"
-#include "net/base/net_errors.h"
-#include "net/base/net_log.h"
-#include "net/dns/dns_client.h"
-#include "net/dns/dns_protocol.h"
-#include "net/dns/dns_response.h"
-#include "net/dns/dns_transaction.h"
-
-using base::TimeDelta;
-using net::AddressList;
-using net::BoundNetLog;
-using net::DnsClient;
-using net::DnsResponse;
-using net::DnsTransaction;
-using net::NetLog;
-
-namespace chrome_browser_net {
-
-namespace {
-
-// Returns true if the given net_error indicates that we received a response
-// from the DNS server containing an error, or false if the given net_error
-// indicates that we never received a response.
-bool DidReceiveDnsResponse(int net_error) {
- switch (net_error) {
- case net::ERR_NAME_NOT_RESOLVED: // NXDOMAIN maps to this.
- case net::ERR_DNS_MALFORMED_RESPONSE:
- case net::ERR_DNS_SERVER_REQUIRES_TCP:
- case net::ERR_DNS_SERVER_FAILED:
- case net::ERR_DNS_SORT_ERROR: // Can only happen if the server responds.
- return true;
- default:
- return false;
- }
-}
-
-class DnsProbeJobImpl : public DnsProbeJob {
- public:
- DnsProbeJobImpl(scoped_ptr<DnsClient> dns_client,
- const DnsProbeJob::CallbackType& callback,
- NetLog* net_log);
- virtual ~DnsProbeJobImpl();
-
- private:
- enum QueryResult {
- QUERY_UNKNOWN,
- QUERY_CORRECT,
- QUERY_INCORRECT,
- QUERY_DNS_ERROR,
- QUERY_NET_ERROR,
- };
-
- void Start();
-
- scoped_ptr<DnsTransaction> CreateTransaction(
- const std::string& hostname);
- void StartTransaction(DnsTransaction* transaction);
- // Checks that |net_error| is OK, |response| parses, and has at least one
- // address.
- QueryResult EvaluateGoodResponse(int net_error, const DnsResponse* response);
- // Checks that |net_error| is OK, |response| parses but has no addresses.
- QueryResult EvaluateBadResponse(int net_error, const DnsResponse* response);
- DnsProbeJob::Result EvaluateQueryResults();
- void OnTransactionComplete(DnsTransaction* transaction,
- int net_error,
- const DnsResponse* response);
-
- BoundNetLog bound_net_log_;
- scoped_ptr<DnsClient> dns_client_;
- const DnsProbeJob::CallbackType callback_;
- scoped_ptr<DnsTransaction> good_transaction_;
- scoped_ptr<DnsTransaction> bad_transaction_;
- bool good_running_;
- bool bad_running_;
- QueryResult good_result_;
- QueryResult bad_result_;
- base::WeakPtrFactory<DnsProbeJobImpl> weak_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(DnsProbeJobImpl);
-};
-
-DnsProbeJobImpl::DnsProbeJobImpl(scoped_ptr<DnsClient> dns_client,
- const DnsProbeJob::CallbackType& callback,
- NetLog* net_log)
- : bound_net_log_(
- BoundNetLog::Make(net_log, NetLog::SOURCE_DNS_PROBER)),
- dns_client_(dns_client.Pass()),
- callback_(callback),
- good_running_(false),
- bad_running_(false),
- good_result_(QUERY_UNKNOWN),
- bad_result_(QUERY_UNKNOWN),
- weak_factory_(this) {
- DCHECK(dns_client_.get());
- DCHECK(dns_client_->GetConfig());
-
- base::MessageLoop::current()->PostTask(
- FROM_HERE,
- base::Bind(&DnsProbeJobImpl::Start,
- weak_factory_.GetWeakPtr()));
-}
-
-DnsProbeJobImpl::~DnsProbeJobImpl() {
-}
-
-void DnsProbeJobImpl::Start() {
- // TODO(ttuttle): Pick a good random hostname for the bad case.
- // Consider running transactions in series?
- good_transaction_ = CreateTransaction("google.com");
- bad_transaction_ = CreateTransaction("thishostname.doesnotresolve");
-
- // StartTransaction may call the callback synchrononously, so set these
- // before we call it.
- good_running_ = true;
- bad_running_ = true;
-
- // TODO(ttuttle): Log probe started.
-
- StartTransaction(good_transaction_.get());
- StartTransaction(bad_transaction_.get());
-}
-
-scoped_ptr<DnsTransaction> DnsProbeJobImpl::CreateTransaction(
- const std::string& hostname) {
- return dns_client_->GetTransactionFactory()->CreateTransaction(
- hostname,
- net::dns_protocol::kTypeA,
- base::Bind(&DnsProbeJobImpl::OnTransactionComplete,
- base::Unretained(this)),
- bound_net_log_);
-}
-
-void DnsProbeJobImpl::StartTransaction(DnsTransaction* transaction) {
- int rv = transaction->Start();
- if (rv != net::ERR_IO_PENDING) {
- // TODO(ttuttle): Make sure this counts as unreachable, not error.
- OnTransactionComplete(transaction, rv, NULL);
- }
-
- // TODO(ttuttle): Log transaction started.
-}
-
-DnsProbeJobImpl::QueryResult DnsProbeJobImpl::EvaluateGoodResponse(
- int net_error,
- const DnsResponse* response) {
- if (net_error != net::OK)
- return DidReceiveDnsResponse(net_error) ? QUERY_DNS_ERROR : QUERY_NET_ERROR;
-
- AddressList addr_list;
- TimeDelta ttl;
- DnsResponse::Result result = response->ParseToAddressList(&addr_list, &ttl);
-
- if (result != DnsResponse::DNS_PARSE_OK)
- return QUERY_DNS_ERROR;
-
- if (addr_list.empty())
- return QUERY_INCORRECT;
-
- return QUERY_CORRECT;
-}
-
-DnsProbeJobImpl::QueryResult DnsProbeJobImpl::EvaluateBadResponse(
- int net_error,
- const DnsResponse* response) {
- if (net_error == net::ERR_NAME_NOT_RESOLVED) // NXDOMAIN maps to this
- return QUERY_CORRECT;
-
- if (net_error != net::OK)
- return DidReceiveDnsResponse(net_error) ? QUERY_DNS_ERROR : QUERY_NET_ERROR;
-
- return QUERY_INCORRECT;
-}
-
-DnsProbeJob::Result DnsProbeJobImpl::EvaluateQueryResults() {
- if (good_result_ == QUERY_NET_ERROR || bad_result_ == QUERY_NET_ERROR)
- return SERVERS_UNREACHABLE;
-
- if (good_result_ == QUERY_DNS_ERROR || bad_result_ == QUERY_DNS_ERROR)
- return SERVERS_FAILING;
-
- // Ignore incorrect responses to known-bad query to avoid flagging domain
- // helpers.
- if (good_result_ == QUERY_INCORRECT)
- return SERVERS_INCORRECT;
-
- return SERVERS_CORRECT;
-}
-
-void DnsProbeJobImpl::OnTransactionComplete(DnsTransaction* transaction,
- int net_error,
- const DnsResponse* response) {
- if (transaction == good_transaction_.get()) {
- DCHECK(good_running_);
- DCHECK_EQ(QUERY_UNKNOWN, good_result_);
- good_result_ = EvaluateGoodResponse(net_error, response);
- good_running_ = false;
- } else if (transaction == bad_transaction_.get()) {
- DCHECK(bad_running_);
- DCHECK_EQ(QUERY_UNKNOWN, bad_result_);
- bad_result_ = EvaluateBadResponse(net_error, response);
- bad_running_ = false;
- } else {
- NOTREACHED();
- return;
- }
-
- if (good_running_ || bad_running_)
- return;
-
- callback_.Run(this, EvaluateQueryResults());
-
- // TODO(ttuttle): Log probe finished.
-}
-
-} // namespace
-
-scoped_ptr<DnsProbeJob> DnsProbeJob::CreateJob(
- scoped_ptr<DnsClient> dns_client,
- const CallbackType& callback,
- NetLog* net_log) {
- return scoped_ptr<DnsProbeJob>(
- new DnsProbeJobImpl(dns_client.Pass(), callback, net_log));
-}
-
-} // namespace chrome_browser_net
diff --git a/chrome/browser/net/dns_probe_job.h b/chrome/browser/net/dns_probe_job.h
deleted file mode 100644
index 6572de1..0000000
--- a/chrome/browser/net/dns_probe_job.h
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_NET_DNS_PROBE_JOB_H_
-#define CHROME_BROWSER_NET_DNS_PROBE_JOB_H_
-
-#include "base/basictypes.h"
-#include "base/bind.h"
-#include "base/memory/scoped_ptr.h"
-
-namespace net {
-class DnsClient;
-class NetLog;
-}
-
-namespace chrome_browser_net {
-
-// A job to probe the health of a DNS configuration.
-class DnsProbeJob {
- public:
- enum Result {
- SERVERS_UNKNOWN,
- SERVERS_CORRECT, // Server responds with correct answers.
- SERVERS_INCORRECT, // Server responds with success but incorrect answer.
- // TODO(ttuttle): Do we want an "unreliable" result, for e.g. servers that
- // lie about NXDOMAIN?
- SERVERS_FAILING, // Server responds with errors.
- SERVERS_UNREACHABLE, // Server doesn't respond (or never got our packets)
- MAX_RESULT
- };
- typedef base::Callback<void(DnsProbeJob* job, Result result)> CallbackType;
-
- virtual ~DnsProbeJob() { }
-
- // Creates and starts a probe job.
- //
- // |dns_client| should be a DnsClient with the DnsConfig already set.
- // |callback| will be called when the probe finishes, which may happen
- // before the constructor returns (for example, if we can't create the DNS
- // transactions).
- static scoped_ptr<DnsProbeJob> CreateJob(
- scoped_ptr<net::DnsClient> dns_client,
- const CallbackType& callback,
- net::NetLog* net_log);
-
- protected:
- DnsProbeJob() { }
-
- DISALLOW_COPY_AND_ASSIGN(DnsProbeJob);
-};
-
-} // namespace chrome_browser_net
-
-#endif // CHROME_BROWSER_NET_DNS_PROBE_JOB_H_
diff --git a/chrome/browser/net/dns_probe_job_unittest.cc b/chrome/browser/net/dns_probe_job_unittest.cc
deleted file mode 100644
index d1779b1..0000000
--- a/chrome/browser/net/dns_probe_job_unittest.cc
+++ /dev/null
@@ -1,127 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/net/dns_probe_job.h"
-
-#include "base/basictypes.h"
-#include "base/message_loop.h"
-#include "base/run_loop.h"
-#include "net/base/net_log.h"
-#include "net/dns/dns_client.h"
-#include "net/dns/dns_config_service.h"
-#include "net/dns/dns_protocol.h"
-#include "net/dns/dns_test_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using net::DnsClient;
-using net::DnsConfig;
-using net::IPAddressNumber;
-using net::IPEndPoint;
-using net::ParseIPLiteralToNumber;
-using net::MockDnsClientRule;
-using net::MockDnsClientRuleList;
-using net::NetLog;
-
-namespace chrome_browser_net {
-
-namespace {
-
-class DnsProbeJobTest : public testing::Test {
- public:
- void RunProbe(MockDnsClientRule::Result expected_good_result,
- MockDnsClientRule::Result expected_bad_result);
-
- protected:
- void OnProbeFinished(DnsProbeJob* job, DnsProbeJob::Result result);
-
- bool callback_called_;
- DnsProbeJob::Result callback_result_;
-};
-
-// Runs a probe and waits for the callback. |good_result| and |bad_result|
-// are the result of the good and bad transactions that the DnsProbeJob will
-// receive.
-void DnsProbeJobTest::RunProbe(MockDnsClientRule::Result good_result,
- MockDnsClientRule::Result bad_result) {
- DnsConfig config;
- config.nameservers.clear();
- IPAddressNumber dns_ip;
- ParseIPLiteralToNumber("192.168.1.1", &dns_ip);
- const uint16 kDnsPort = net::dns_protocol::kDefaultPort;
- config.nameservers.push_back(IPEndPoint(dns_ip, kDnsPort));
-
- const uint16 kTypeA = net::dns_protocol::kTypeA;
- MockDnsClientRuleList rules;
- rules.push_back(MockDnsClientRule("google.com", kTypeA, good_result));
- rules.push_back(MockDnsClientRule(std::string(), kTypeA, bad_result));
-
- scoped_ptr<DnsClient> dns_client = CreateMockDnsClient(config, rules);
- dns_client->SetConfig(config);
-
- NetLog* net_log = NULL;
- DnsProbeJob::CallbackType callback = base::Bind(
- &DnsProbeJobTest::OnProbeFinished,
- base::Unretained(this));
-
- // Need to set these before creating job, because it can call the callback
- // synchronously in the constructor if both transactions fail to start.
- callback_called_ = false;
- callback_result_ = DnsProbeJob::SERVERS_UNKNOWN;
-
- // DnsProbeJob needs somewhere to post the callback.
- scoped_ptr<base::MessageLoop> message_loop_(new base::MessageLoopForIO());
-
- scoped_ptr<DnsProbeJob> job(
- DnsProbeJob::CreateJob(dns_client.Pass(), callback, net_log));
-
- // Force callback to run.
- base::RunLoop run_loop;
- run_loop.RunUntilIdle();
-}
-
-void DnsProbeJobTest::OnProbeFinished(DnsProbeJob* job,
- DnsProbeJob::Result result) {
- EXPECT_FALSE(callback_called_);
-
- callback_called_ = true;
- callback_result_ = result;
-}
-
-struct TestCase {
- MockDnsClientRule::Result good_result;
- MockDnsClientRule::Result bad_result;
- DnsProbeJob::Result expected_probe_result;
-};
-
-TEST_F(DnsProbeJobTest, Test) {
- static const TestCase kTestCases[] = {
- { MockDnsClientRule::OK,
- MockDnsClientRule::EMPTY,
- DnsProbeJob::SERVERS_CORRECT },
- { MockDnsClientRule::EMPTY,
- MockDnsClientRule::EMPTY,
- DnsProbeJob::SERVERS_INCORRECT },
- // TODO(ttuttle): Test that triggers QUERY_DNS_ERROR.
- // (Need to add another mock behavior to MockDnsClient.)
- { MockDnsClientRule::FAIL_ASYNC,
- MockDnsClientRule::FAIL_ASYNC,
- DnsProbeJob::SERVERS_FAILING },
- { MockDnsClientRule::FAIL_SYNC,
- MockDnsClientRule::FAIL_SYNC,
- DnsProbeJob::SERVERS_FAILING },
- { MockDnsClientRule::TIMEOUT,
- MockDnsClientRule::TIMEOUT,
- DnsProbeJob::SERVERS_UNREACHABLE },
- };
- for (size_t i = 0; i < arraysize(kTestCases); i++) {
- const TestCase* test_case = &kTestCases[i];
- RunProbe(test_case->good_result, test_case->bad_result);
- EXPECT_TRUE(callback_called_);
- EXPECT_EQ(test_case->expected_probe_result, callback_result_);
- }
-}
-
-} // namespace
-
-} // namespace chrome_browser_net
diff --git a/chrome/browser/net/dns_probe_runner.cc b/chrome/browser/net/dns_probe_runner.cc
new file mode 100644
index 0000000..d2b3a5d7
--- /dev/null
+++ b/chrome/browser/net/dns_probe_runner.cc
@@ -0,0 +1,143 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/net/dns_probe_runner.h"
+
+#include "base/bind.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/base/address_list.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_log.h"
+#include "net/base/net_util.h"
+#include "net/base/network_change_notifier.h"
+#include "net/dns/dns_client.h"
+#include "net/dns/dns_protocol.h"
+#include "net/dns/dns_response.h"
+#include "net/dns/dns_transaction.h"
+
+using base::TimeDelta;
+using content::BrowserThread;
+using net::AddressList;
+using net::BoundNetLog;
+using net::DnsClient;
+using net::DnsResponse;
+using net::DnsTransaction;
+using net::IPAddressNumber;
+using net::IPEndPoint;
+using net::NetLog;
+using net::NetworkChangeNotifier;
+using net::ParseIPLiteralToNumber;
+
+namespace chrome_browser_net {
+
+const char* DnsProbeRunner::kKnownGoodHostname = "google.com";
+
+namespace {
+
+DnsProbeRunner::Result EvaluateResponse(
+ int net_error,
+ const DnsResponse* response) {
+ switch (net_error) {
+ case net::OK:
+ break;
+
+ // ERR_NAME_NOT_RESOLVED maps to NXDOMAIN, which means the server is working
+ // but gave us a wrong answer.
+ case net::ERR_NAME_NOT_RESOLVED:
+ return DnsProbeRunner::INCORRECT;
+
+ // These results mean we heard *something* from the DNS server, but it was
+ // unsuccessful (SERVFAIL) or malformed.
+ case net::ERR_DNS_MALFORMED_RESPONSE:
+ case net::ERR_DNS_SERVER_REQUIRES_TCP: // Shouldn't happen; DnsTransaction
+ // will retry with TCP.
+ case net::ERR_DNS_SERVER_FAILED:
+ case net::ERR_DNS_SORT_ERROR: // Can only happen if the server responds.
+ return DnsProbeRunner::FAILING;
+
+ // Any other error means we never reached the DNS server in the first place.
+ case net::ERR_DNS_TIMED_OUT:
+ default:
+ // Something else happened, probably at a network level.
+ return DnsProbeRunner::UNREACHABLE;
+ }
+
+ AddressList addr_list;
+ TimeDelta ttl;
+ DnsResponse::Result result = response->ParseToAddressList(&addr_list, &ttl);
+
+ if (result != DnsResponse::DNS_PARSE_OK) {
+ return DnsProbeRunner::FAILING;
+ }
+
+ if (addr_list.empty()) {
+ return DnsProbeRunner::INCORRECT;
+ }
+
+ return DnsProbeRunner::CORRECT;
+}
+
+} // namespace
+
+DnsProbeRunner::DnsProbeRunner() : weak_factory_(this), result_(UNKNOWN) {}
+
+DnsProbeRunner::~DnsProbeRunner() {}
+
+void DnsProbeRunner::SetClient(scoped_ptr<net::DnsClient> client) {
+ client_ = client.Pass();
+}
+
+void DnsProbeRunner::RunProbe(const base::Closure& callback) {
+ DCHECK(!callback.is_null());
+ DCHECK(client_.get());
+ DCHECK(callback_.is_null());
+ DCHECK(!transaction_.get());
+
+ callback_ = callback;
+ transaction_ = client_->GetTransactionFactory()->CreateTransaction(
+ kKnownGoodHostname,
+ net::dns_protocol::kTypeA,
+ base::Bind(&DnsProbeRunner::OnTransactionComplete,
+ weak_factory_.GetWeakPtr()),
+ BoundNetLog());
+
+ int rv = transaction_->Start();
+ if (rv != net::ERR_IO_PENDING)
+ OnTransactionComplete(transaction_.get(), rv, NULL);
+}
+
+bool DnsProbeRunner::IsRunning() const {
+ return !callback_.is_null();
+}
+
+void DnsProbeRunner::OnTransactionComplete(
+ DnsTransaction* transaction,
+ int net_error,
+ const DnsResponse* response) {
+ DCHECK(!callback_.is_null());
+ DCHECK(transaction_.get());
+ DCHECK_EQ(transaction_.get(), transaction);
+
+ result_ = EvaluateResponse(net_error, response);
+ transaction_.reset();
+
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&DnsProbeRunner::CallCallback,
+ weak_factory_.GetWeakPtr()));
+}
+
+void DnsProbeRunner::CallCallback() {
+ DCHECK(!callback_.is_null());
+ DCHECK(!transaction_.get());
+
+ // Clear callback in case it starts a new probe immediately.
+ const base::Closure callback = callback_;
+ callback_.Reset();
+ callback.Run();
+}
+
+} // namespace chrome_browser_net
diff --git a/chrome/browser/net/dns_probe_runner.h b/chrome/browser/net/dns_probe_runner.h
new file mode 100644
index 0000000..3f294c2
--- /dev/null
+++ b/chrome/browser/net/dns_probe_runner.h
@@ -0,0 +1,85 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_NET_DNS_PROBE_RUNNER_H_
+#define CHROME_BROWSER_NET_DNS_PROBE_RUNNER_H_
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+
+namespace net {
+class DnsClient;
+class DnsResponse;
+class DnsTransaction;
+}
+
+namespace chrome_browser_net {
+
+// Runs DNS probes using a single DnsClient and evaluates the responses.
+// (Currently requests A records for google.com and expects at least one IP
+// address in the response.)
+// Used by DnsProbeService to probe the system and public DNS configurations.
+class DnsProbeRunner {
+ public:
+ static const char* kKnownGoodHostname;
+
+ // Used in histograms; add new entries at the bottom, and don't remove any.
+ enum Result {
+ UNKNOWN,
+ CORRECT, // Response contains at least one A record.
+ INCORRECT, // Response claimed success but included no A records.
+ FAILING, // Response included an error or was malformed.
+ UNREACHABLE // No response received (timeout, network unreachable, etc.).
+ };
+
+ DnsProbeRunner();
+ ~DnsProbeRunner();
+
+ // Sets the DnsClient that will be used for DNS probes sent by this runner.
+ // Must be called before RunProbe; can be called repeatedly, including during
+ // a probe. It will not affect an in-flight probe, if one is running.
+ void SetClient(scoped_ptr<net::DnsClient> client);
+
+ // Starts a probe using the client specified with SetClient, which must have
+ // been called before RunProbe. |callback| will be called asynchronously
+ // when the result is ready, even if it is ready synchronously. Must not
+ // be called again until the callback is called, but may be called during the
+ // callback.
+ void RunProbe(const base::Closure& callback);
+
+ // Returns true if a probe is running. Guaranteed to return true after
+ // RunProbe returns, and false during and after the callback.
+ bool IsRunning() const;
+
+ // Returns the result of the last probe.
+ Result result() const { return result_; }
+
+ private:
+ void OnTransactionComplete(net::DnsTransaction* transaction,
+ int net_error,
+ const net::DnsResponse* response);
+ void CallCallback();
+
+ base::WeakPtrFactory<DnsProbeRunner> weak_factory_;
+
+ scoped_ptr<net::DnsClient> client_;
+
+ // The callback passed to |RunProbe|. Cleared right before calling the
+ // callback.
+ base::Closure callback_;
+
+ // The transaction started in |RunProbe| for the DNS probe. Reset once the
+ // results have been examined.
+ scoped_ptr<net::DnsTransaction> transaction_;
+
+ Result result_;
+
+ DISALLOW_COPY_AND_ASSIGN(DnsProbeRunner);
+};
+
+} // namespace chrome_browser_net
+
+#endif // CHROME_BROWSER_NET_DNS_PROBE_RUNNER_H_
diff --git a/chrome/browser/net/dns_probe_runner_unittest.cc b/chrome/browser/net/dns_probe_runner_unittest.cc
new file mode 100644
index 0000000..5519da2
--- /dev/null
+++ b/chrome/browser/net/dns_probe_runner_unittest.cc
@@ -0,0 +1,95 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop.h"
+#include "base/run_loop.h"
+#include "chrome/browser/net/dns_probe_runner.h"
+#include "chrome/browser/net/dns_probe_test_util.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "net/dns/dns_client.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::MessageLoopForIO;
+using base::RunLoop;
+using content::TestBrowserThreadBundle;
+using net::MockDnsClientRule;
+
+namespace chrome_browser_net {
+
+namespace {
+
+class TestDnsProbeRunnerCallback {
+ public:
+ TestDnsProbeRunnerCallback()
+ : callback_(base::Bind(&TestDnsProbeRunnerCallback::OnCalled,
+ base::Unretained(this))),
+ called_(false) {}
+
+ const base::Closure& callback() const { return callback_; }
+ bool called() const { return called_; }
+
+ private:
+ void OnCalled() {
+ EXPECT_FALSE(called_);
+ called_ = true;
+ }
+
+ base::Closure callback_;
+ bool called_;
+};
+
+class DnsProbeRunnerTest : public testing::Test {
+ protected:
+ void RunTest(MockDnsClientRule::Result query_result,
+ DnsProbeRunner::Result expected_probe_result);
+
+ TestBrowserThreadBundle bundle_;
+ DnsProbeRunner runner_;
+};
+
+void DnsProbeRunnerTest::RunTest(
+ MockDnsClientRule::Result query_result,
+ DnsProbeRunner::Result expected_probe_result) {
+ TestDnsProbeRunnerCallback callback;
+
+ runner_.SetClient(CreateMockDnsClientForProbes(query_result));
+ runner_.RunProbe(callback.callback());
+ EXPECT_TRUE(runner_.IsRunning());
+
+ RunLoop().RunUntilIdle();
+ EXPECT_FALSE(runner_.IsRunning());
+ EXPECT_TRUE(callback.called());
+ EXPECT_EQ(expected_probe_result, runner_.result());
+}
+
+TEST_F(DnsProbeRunnerTest, Probe_OK) {
+ RunTest(MockDnsClientRule::OK, DnsProbeRunner::CORRECT);
+}
+
+TEST_F(DnsProbeRunnerTest, Probe_EMPTY) {
+ RunTest(MockDnsClientRule::EMPTY, DnsProbeRunner::INCORRECT);
+}
+
+TEST_F(DnsProbeRunnerTest, Probe_TIMEOUT) {
+ RunTest(MockDnsClientRule::TIMEOUT, DnsProbeRunner::UNREACHABLE);
+}
+
+TEST_F(DnsProbeRunnerTest, Probe_FAIL_ASYNC) {
+ RunTest(MockDnsClientRule::FAIL_ASYNC, DnsProbeRunner::INCORRECT);
+}
+
+TEST_F(DnsProbeRunnerTest, Probe_FAIL_SYNC) {
+ RunTest(MockDnsClientRule::FAIL_SYNC, DnsProbeRunner::INCORRECT);
+}
+
+TEST_F(DnsProbeRunnerTest, TwoProbes) {
+ RunTest(MockDnsClientRule::OK, DnsProbeRunner::CORRECT);
+ RunTest(MockDnsClientRule::EMPTY, DnsProbeRunner::INCORRECT);
+}
+
+} // namespace
+
+} // namespace chrome_browser_net
diff --git a/chrome/browser/net/dns_probe_service.cc b/chrome/browser/net/dns_probe_service.cc
index 156313f..36e28af 100644
--- a/chrome/browser/net/dns_probe_service.cc
+++ b/chrome/browser/net/dns_probe_service.cc
@@ -7,8 +7,6 @@
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram.h"
#include "base/strings/string_number_conversions.h"
-#include "chrome/browser/net/dns_probe_job.h"
-#include "chrome/common/net/net_error_info.h"
#include "net/base/ip_endpoint.h"
#include "net/base/net_util.h"
#include "net/dns/dns_client.h"
@@ -17,7 +15,7 @@
using base::FieldTrialList;
using base::StringToInt;
-using chrome_common_net::DnsProbeResult;
+using chrome_common_net::DnsProbeStatus;
using net::DnsClient;
using net::DnsConfig;
using net::IPAddressNumber;
@@ -36,8 +34,8 @@ const int kMaxResultAgeMs = 5000;
// The public DNS servers used by the DnsProbeService to verify internet
// connectivity.
-const char kPublicDnsPrimary[] = "8.8.8.8";
-const char kPublicDnsSecondary[] = "8.8.4.4";
+const char kGooglePublicDns1[] = "8.8.8.8";
+const char kGooglePublicDns2[] = "8.8.4.4";
IPEndPoint MakeDnsEndPoint(const std::string& dns_ip_literal) {
IPAddressNumber dns_ip_number;
@@ -46,299 +44,217 @@ IPEndPoint MakeDnsEndPoint(const std::string& dns_ip_literal) {
return IPEndPoint(dns_ip_number, net::dns_protocol::kDefaultPort);
}
-const int kAttemptsUseDefault = -1;
-
-const char kAttemptsFieldTrialName[] = "DnsProbe-Attempts";
+DnsProbeStatus EvaluateResults(DnsProbeRunner::Result system_result,
+ DnsProbeRunner::Result public_result) {
+ // If the system DNS is working, assume the domain doesn't exist.
+ if (system_result == DnsProbeRunner::CORRECT)
+ return chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN;
-int GetAttemptsFromFieldTrial() {
- std::string group = FieldTrialList::FindFullName(kAttemptsFieldTrialName);
- if (group == "" || group == "default")
- return kAttemptsUseDefault;
+ // If the system DNS is not working but another public server is, assume the
+ // DNS config is bad (or perhaps the DNS servers are down or broken).
+ if (public_result == DnsProbeRunner::CORRECT)
+ return chrome_common_net::DNS_PROBE_FINISHED_BAD_CONFIG;
- int attempts;
- if (!StringToInt(group, &attempts))
- return kAttemptsUseDefault;
+ // If the system DNS is not working and another public server is unreachable,
+ // assume the internet connection is down (note that system DNS may be a
+ // router on the LAN, so it may be reachable but returning errors.)
+ if (public_result == DnsProbeRunner::UNREACHABLE)
+ return chrome_common_net::DNS_PROBE_FINISHED_NO_INTERNET;
- return attempts;
+ // Otherwise: the system DNS is not working and another public server is
+ // responding but with errors or incorrect results. This is an awkward case;
+ // an invasive captive portal or a restrictive firewall may be intercepting
+ // or rewriting DNS traffic, or the public server may itself be failing or
+ // down.
+ return chrome_common_net::DNS_PROBE_FINISHED_INCONCLUSIVE;
}
-bool IsLocalhost(const IPAddressNumber& ip) {
- return (ip.size() == net::kIPv4AddressSize)
- && (ip[0] == 127) && (ip[1] == 0) && (ip[2] == 0) && (ip[3] == 1);
-}
+void HistogramProbe(DnsProbeStatus status, base::TimeDelta elapsed) {
+ DCHECK(chrome_common_net::DnsProbeStatusIsFinished(status));
+
+ int result = status - chrome_common_net::DNS_PROBE_FINISHED_INCONCLUSIVE;
+
+ const int kMaxResult = chrome_common_net::DNS_PROBE_MAX -
+ chrome_common_net::DNS_PROBE_FINISHED_INCONCLUSIVE;
-// The maximum number of nameservers counted in histograms.
-const int kNameserverCountMax = 10;
+ UMA_HISTOGRAM_ENUMERATION("DnsProbe.Status", result, kMaxResult);
+ UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.Elapsed", elapsed);
+
+ if (NetworkChangeNotifier::IsOffline()) {
+ UMA_HISTOGRAM_ENUMERATION("DnsProbe.Status_NcnOffline",
+ result, kMaxResult);
+ UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.Elapsed_NcnOffline", elapsed);
+ } else {
+ UMA_HISTOGRAM_ENUMERATION("DnsProbe.Status_NcnOnline",
+ result, kMaxResult);
+ UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.Elapsed_NcnOnline", elapsed);
+ }
+
+ switch (status) {
+ case chrome_common_net::DNS_PROBE_FINISHED_INCONCLUSIVE:
+ UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.Elapsed_Unknown",
+ elapsed);
+ break;
+ case chrome_common_net::DNS_PROBE_FINISHED_NO_INTERNET:
+ UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.Elapsed_NoInternet",
+ elapsed);
+ break;
+ case chrome_common_net::DNS_PROBE_FINISHED_BAD_CONFIG:
+ UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.Elapsed_BadConfig",
+ elapsed);
+ break;
+ case chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN:
+ UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.Elapsed_Nxdomain",
+ elapsed);
+ break;
+
+ // These aren't actually results.
+ case chrome_common_net::DNS_PROBE_POSSIBLE:
+ case chrome_common_net::DNS_PROBE_NOT_RUN:
+ case chrome_common_net::DNS_PROBE_STARTED:
+ case chrome_common_net::DNS_PROBE_MAX:
+ NOTREACHED();
+ break;
+ }
+}
} // namespace
DnsProbeService::DnsProbeService()
- : system_result_(DnsProbeJob::SERVERS_UNKNOWN),
- public_result_(DnsProbeJob::SERVERS_UNKNOWN),
- state_(STATE_NO_RESULTS),
- result_(chrome_common_net::DNS_PROBE_UNKNOWN),
- dns_attempts_(GetAttemptsFromFieldTrial()) {
- NetworkChangeNotifier::AddIPAddressObserver(this);
+ : state_(STATE_NO_RESULT) {
+ NetworkChangeNotifier::AddDNSObserver(this);
+ SetSystemClientToCurrentConfig();
+ SetPublicClientToGooglePublicDns();
}
DnsProbeService::~DnsProbeService() {
- NetworkChangeNotifier::RemoveIPAddressObserver(this);
+ NetworkChangeNotifier::RemoveDNSObserver(this);
}
-void DnsProbeService::ProbeDns(const DnsProbeService::CallbackType& callback) {
- callbacks_.push_back(callback);
+void DnsProbeService::ProbeDns(const DnsProbeService::ProbeCallback& callback) {
+ pending_callbacks_.push_back(callback);
- if (state_ == STATE_RESULTS_CACHED && ResultsExpired())
- ExpireResults();
+ if (CachedResultIsExpired())
+ ClearCachedResult();
switch (state_) {
- case STATE_NO_RESULTS:
- StartProbes();
- break;
- case STATE_RESULTS_CACHED:
- CallCallbacks();
- break;
- case STATE_PROBE_RUNNING:
- // do nothing; probe is already running, and will call the callback
- break;
+ case STATE_NO_RESULT:
+ StartProbes();
+ break;
+ case STATE_RESULT_CACHED:
+ CallCallbacks();
+ break;
+ case STATE_PROBE_RUNNING:
+ // Do nothing; probe is already running, and will call the callback.
+ break;
}
}
-scoped_ptr<DnsProbeJob> DnsProbeService::CreateSystemProbeJob(
- const DnsProbeJob::CallbackType& job_callback) {
- DnsConfig system_config;
- GetSystemDnsConfig(&system_config);
- return CreateProbeJob(system_config, job_callback);
+void DnsProbeService::OnDNSChanged() {
+ ClearCachedResult();
+ SetSystemClientToCurrentConfig();
}
-scoped_ptr<DnsProbeJob> DnsProbeService::CreatePublicProbeJob(
- const DnsProbeJob::CallbackType& job_callback) {
- DnsConfig public_config;
- GetPublicDnsConfig(&public_config);
- return CreateProbeJob(public_config, job_callback);
+void DnsProbeService::SetSystemClientForTesting(
+ scoped_ptr<DnsClient> system_client) {
+ system_runner_.SetClient(system_client.Pass());
}
-void DnsProbeService::OnIPAddressChanged() {
- if (state_ == STATE_RESULTS_CACHED)
- ExpireResults();
+void DnsProbeService::SetPublicClientForTesting(
+ scoped_ptr<DnsClient> public_client) {
+ public_runner_.SetClient(public_client.Pass());
}
-void DnsProbeService::ExpireResults() {
- DCHECK_EQ(STATE_RESULTS_CACHED, state_);
-
- state_ = STATE_NO_RESULTS;
- result_ = chrome_common_net::DNS_PROBE_UNKNOWN;
+void DnsProbeService::ClearCachedResultForTesting() {
+ ClearCachedResult();
}
-void DnsProbeService::StartProbes() {
- DCHECK_NE(STATE_PROBE_RUNNING, state_);
- DCHECK(!system_job_.get());
- DCHECK(!public_job_.get());
-
- DnsProbeJob::CallbackType job_callback =
- base::Bind(&DnsProbeService::OnProbeJobComplete,
- base::Unretained(this));
-
- // TODO(ttuttle): Do we want to keep explicit flags for "job done"?
- // Or maybe DnsProbeJob should have a "finished" flag?
- system_result_ = DnsProbeJob::SERVERS_UNKNOWN;
- public_result_ = DnsProbeJob::SERVERS_UNKNOWN;
-
- system_job_ = CreateSystemProbeJob(job_callback);
- public_job_ = CreatePublicProbeJob(job_callback);
-
- // If we can't create one or both jobs, fail the probe immediately.
- if (!system_job_.get() || !public_job_.get()) {
- system_job_.reset();
- public_job_.reset();
- state_ = STATE_RESULTS_CACHED;
- // TODO(ttuttle): Should this be BAD_CONFIG? Currently I think it only
- // happens when the system DnsConfig has no servers.
- result_ = chrome_common_net::DNS_PROBE_UNKNOWN;
- CallCallbacks();
- return;
- }
+void DnsProbeService::SetSystemClientToCurrentConfig() {
+ DnsConfig system_config;
+ NetworkChangeNotifier::GetDnsConfig(&system_config);
+ system_config.search.clear();
+ system_config.attempts = 1;
+ system_config.randomize_ports = false;
- state_ = STATE_PROBE_RUNNING;
- probe_start_time_ = base::Time::Now();
-}
+ scoped_ptr<DnsClient> system_client(DnsClient::CreateClient(NULL));
+ system_client->SetConfig(system_config);
-void DnsProbeService::OnProbesComplete() {
- DCHECK_EQ(STATE_PROBE_RUNNING, state_);
+ system_runner_.SetClient(system_client.Pass());
+}
- state_ = STATE_RESULTS_CACHED;
- result_ = EvaluateResults();
+void DnsProbeService::SetPublicClientToGooglePublicDns() {
+ DnsConfig public_config;
+ public_config.nameservers.push_back(MakeDnsEndPoint(kGooglePublicDns1));
+ public_config.nameservers.push_back(MakeDnsEndPoint(kGooglePublicDns2));
+ public_config.attempts = 1;
+ public_config.randomize_ports = false;
- HistogramProbes();
+ scoped_ptr<DnsClient> public_client(DnsClient::CreateClient(NULL));
+ public_client->SetConfig(public_config);
- CallCallbacks();
+ public_runner_.SetClient(public_client.Pass());
}
-void DnsProbeService::HistogramProbes() const {
- const DnsProbeResult kMaxResult = chrome_common_net::DNS_PROBE_MAX;
-
- DCHECK_EQ(STATE_RESULTS_CACHED, state_);
- DCHECK_NE(kMaxResult, result_);
-
- base::TimeDelta elapsed = base::Time::Now() - probe_start_time_;
+void DnsProbeService::StartProbes() {
+ DCHECK_EQ(STATE_NO_RESULT, state_);
- UMA_HISTOGRAM_ENUMERATION("DnsProbe.Probe.Result", result_, kMaxResult);
- UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.Probe.Elapsed", elapsed);
+ DCHECK(!system_runner_.IsRunning());
+ DCHECK(!public_runner_.IsRunning());
- if (NetworkChangeNotifier::IsOffline()) {
- UMA_HISTOGRAM_ENUMERATION("DnsProbe.Probe.NcnOffline.Result",
- result_, kMaxResult);
- UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.Probe.NcnOffline.Elapsed", elapsed);
- } else {
- UMA_HISTOGRAM_ENUMERATION("DnsProbe.Probe.NcnOnline.Result",
- result_, kMaxResult);
- UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.Probe.NcnOnline.Elapsed", elapsed);
- }
+ const base::Closure callback = base::Bind(&DnsProbeService::OnProbeComplete,
+ base::Unretained(this));
+ system_runner_.RunProbe(callback);
+ public_runner_.RunProbe(callback);
+ probe_start_time_ = base::Time::Now();
+ state_ = STATE_PROBE_RUNNING;
- switch (result_) {
- case chrome_common_net::DNS_PROBE_UNKNOWN:
- UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.Probe.ResultUnknown.Elapsed",
- elapsed);
- break;
- case chrome_common_net::DNS_PROBE_NO_INTERNET:
- UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.Probe.ResultNoInternet.Elapsed",
- elapsed);
- break;
- case chrome_common_net::DNS_PROBE_BAD_CONFIG:
- UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.Probe.ResultBadConfig.Elapsed",
- elapsed);
-
- // Histogram some extra data to see why BAD_CONFIG is happening.
- UMA_HISTOGRAM_ENUMERATION(
- "DnsProbe.Probe.ResultBadConfig.SystemJobResult",
- system_result_,
- DnsProbeJob::MAX_RESULT);
- UMA_HISTOGRAM_CUSTOM_COUNTS(
- "DnsProbe.Probe.ResultBadConfig.SystemNameserverCount",
- system_nameserver_count_,
- 0, kNameserverCountMax, kNameserverCountMax + 1);
- UMA_HISTOGRAM_BOOLEAN(
- "DnsProbe.Probe.ResultBadConfig.SystemIsLocalhost",
- system_is_localhost_);
- break;
- case chrome_common_net::DNS_PROBE_NXDOMAIN:
- UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.Probe.ResultNxdomain.Elapsed",
- elapsed);
- break;
- case chrome_common_net::DNS_PROBE_MAX:
- NOTREACHED();
- break;
- }
+ DCHECK(system_runner_.IsRunning());
+ DCHECK(public_runner_.IsRunning());
}
-DnsProbeResult DnsProbeService::EvaluateResults() const {
- DCHECK_NE(DnsProbeJob::SERVERS_UNKNOWN, system_result_);
- DCHECK_NE(DnsProbeJob::SERVERS_UNKNOWN, public_result_);
+void DnsProbeService::OnProbeComplete() {
+ DCHECK_EQ(STATE_PROBE_RUNNING, state_);
- // If the system DNS is working, assume the domain doesn't exist.
- if (system_result_ == DnsProbeJob::SERVERS_CORRECT)
- return chrome_common_net::DNS_PROBE_NXDOMAIN;
+ if (system_runner_.IsRunning() || public_runner_.IsRunning())
+ return;
- // If the system DNS is not working but another public server is, assume the
- // DNS config is bad (or perhaps the DNS servers are down or broken).
- if (public_result_ == DnsProbeJob::SERVERS_CORRECT)
- return chrome_common_net::DNS_PROBE_BAD_CONFIG;
+ cached_result_ = EvaluateResults(system_runner_.result(),
+ public_runner_.result());
+ state_ = STATE_RESULT_CACHED;
- // If the system DNS is not working and another public server is unreachable,
- // assume the internet connection is down (note that system DNS may be a
- // router on the LAN, so it may be reachable but returning errors.)
- if (public_result_ == DnsProbeJob::SERVERS_UNREACHABLE)
- return chrome_common_net::DNS_PROBE_NO_INTERNET;
+ HistogramProbe(cached_result_, base::Time::Now() - probe_start_time_);
- // Otherwise: the system DNS is not working and another public server is
- // responding but with errors or incorrect results. This is an awkward case;
- // an invasive captive portal or a restrictive firewall may be intercepting
- // or rewriting DNS traffic, or the public server may itself be failing or
- // down.
- return chrome_common_net::DNS_PROBE_UNKNOWN;
+ CallCallbacks();
}
void DnsProbeService::CallCallbacks() {
- DCHECK_EQ(STATE_RESULTS_CACHED, state_);
- DCHECK(!callbacks_.empty());
+ DCHECK_EQ(STATE_RESULT_CACHED, state_);
+ DCHECK(chrome_common_net::DnsProbeStatusIsFinished(cached_result_));
+ DCHECK(!pending_callbacks_.empty());
- std::vector<CallbackType> callbacks = callbacks_;
- callbacks_.clear();
+ std::vector<ProbeCallback> callbacks;
+ callbacks.swap(pending_callbacks_);
- for (std::vector<CallbackType>::const_iterator i = callbacks.begin();
+ for (std::vector<ProbeCallback>::const_iterator i = callbacks.begin();
i != callbacks.end(); ++i) {
- i->Run(result_);
+ i->Run(cached_result_);
}
}
-scoped_ptr<DnsProbeJob> DnsProbeService::CreateProbeJob(
- const DnsConfig& dns_config,
- const DnsProbeJob::CallbackType& job_callback) {
- if (!dns_config.IsValid())
- return scoped_ptr<DnsProbeJob>();
-
- scoped_ptr<DnsClient> dns_client(DnsClient::CreateClient(NULL));
- dns_client->SetConfig(dns_config);
- return DnsProbeJob::CreateJob(dns_client.Pass(), job_callback, NULL);
-}
-
-void DnsProbeService::OnProbeJobComplete(DnsProbeJob* job,
- DnsProbeJob::Result result) {
- DCHECK_EQ(STATE_PROBE_RUNNING, state_);
-
- if (job == system_job_.get()) {
- system_result_ = result;
- system_job_.reset();
- } else if (job == public_job_.get()) {
- public_result_ = result;
- public_job_.reset();
- } else {
- NOTREACHED();
- return;
- }
-
- if (system_result_ != DnsProbeJob::SERVERS_UNKNOWN &&
- public_result_ != DnsProbeJob::SERVERS_UNKNOWN) {
- OnProbesComplete();
+void DnsProbeService::ClearCachedResult() {
+ if (state_ == STATE_RESULT_CACHED) {
+ state_ = STATE_NO_RESULT;
+ cached_result_ = chrome_common_net::DNS_PROBE_MAX;
}
}
-void DnsProbeService::GetSystemDnsConfig(DnsConfig* config) {
- NetworkChangeNotifier::GetDnsConfig(config);
-
- // DNS probes don't need or want the suffix search list populated
- config->search.clear();
-
- if (dns_attempts_ != kAttemptsUseDefault)
- config->attempts = dns_attempts_;
-
- // Take notes in case the config turns out to be bad, so we can histogram
- // some useful data.
- system_nameserver_count_ = config->nameservers.size();
- system_is_localhost_ = (system_nameserver_count_ == 1)
- && IsLocalhost(config->nameservers[0].address());
-
- // Disable port randomization.
- config->randomize_ports = false;
-}
-
-void DnsProbeService::GetPublicDnsConfig(DnsConfig* config) {
- *config = DnsConfig();
-
- config->nameservers.push_back(MakeDnsEndPoint(kPublicDnsPrimary));
- config->nameservers.push_back(MakeDnsEndPoint(kPublicDnsSecondary));
-
- if (dns_attempts_ != kAttemptsUseDefault)
- config->attempts = dns_attempts_;
-
- // Disable port randomization.
- config->randomize_ports = false;
-}
+bool DnsProbeService::CachedResultIsExpired() const {
+ if (state_ != STATE_RESULT_CACHED)
+ return false;
-bool DnsProbeService::ResultsExpired() {
const base::TimeDelta kMaxResultAge =
base::TimeDelta::FromMilliseconds(kMaxResultAgeMs);
return base::Time::Now() - probe_start_time_ > kMaxResultAge;
}
-} // namespace chrome_browser_net
+} // namespace chrome_browser_net
diff --git a/chrome/browser/net/dns_probe_service.h b/chrome/browser/net/dns_probe_service.h
index a906d75..d1df98e 100644
--- a/chrome/browser/net/dns_probe_service.h
+++ b/chrome/browser/net/dns_probe_service.h
@@ -8,78 +8,70 @@
#include <vector>
#include "base/basictypes.h"
+#include "base/bind.h"
#include "base/memory/scoped_ptr.h"
#include "base/time/time.h"
-#include "chrome/browser/net/dns_probe_job.h"
+#include "chrome/browser/net/dns_probe_runner.h"
#include "chrome/common/net/net_error_info.h"
#include "net/base/network_change_notifier.h"
namespace net {
+class DnsClient;
struct DnsConfig;
}
namespace chrome_browser_net {
-class DnsProbeService : public net::NetworkChangeNotifier::IPAddressObserver {
+// Probes the system and public DNS servers to determine the (probable) cause
+// of a recent DNS-related page load error. Coalesces multiple probe requests
+// (perhaps from multiple tabs) and caches the results.
+//
+// Uses a single DNS attempt per config, and doesn't randomize source ports.
+class DnsProbeService : public net::NetworkChangeNotifier::DNSObserver {
public:
- typedef base::Callback<void(chrome_common_net::DnsProbeResult result)>
- CallbackType;
+ typedef base::Callback<void(chrome_common_net::DnsProbeStatus result)>
+ ProbeCallback;
DnsProbeService();
virtual ~DnsProbeService();
- void ProbeDns(const CallbackType& callback);
+ virtual void ProbeDns(const ProbeCallback& callback);
- // NetworkChangeNotifier::IPAddressObserver implementation:
- virtual void OnIPAddressChanged() OVERRIDE;
+ // NetworkChangeNotifier::DNSObserver implementation:
+ virtual void OnDNSChanged() OVERRIDE;
- protected:
- // This can be called by tests to pretend the cached reuslt has expired.
- void ExpireResults();
+ void SetSystemClientForTesting(scoped_ptr<net::DnsClient> system_client);
+ void SetPublicClientForTesting(scoped_ptr<net::DnsClient> public_client);
+ void ClearCachedResultForTesting();
private:
enum State {
- STATE_NO_RESULTS,
+ STATE_NO_RESULT,
STATE_PROBE_RUNNING,
- STATE_RESULTS_CACHED,
+ STATE_RESULT_CACHED,
};
+ void SetSystemClientToCurrentConfig();
+ void SetPublicClientToGooglePublicDns();
+
+ // Starts a probe (runs system and public probes).
void StartProbes();
- void OnProbesComplete();
+ void OnProbeComplete();
+ // Calls all |pending_callbacks_| with the |cached_result_|.
void CallCallbacks();
+ // Clears a cached probe result.
+ void ClearCachedResult();
+
+ bool CachedResultIsExpired() const;
- void OnProbeJobComplete(DnsProbeJob* job, DnsProbeJob::Result result);
- chrome_common_net::DnsProbeResult EvaluateResults() const;
- void HistogramProbes() const;
-
- // These are expected to be overridden by tests to return mock jobs.
- virtual scoped_ptr<DnsProbeJob> CreateSystemProbeJob(
- const DnsProbeJob::CallbackType& job_callback);
- virtual scoped_ptr<DnsProbeJob> CreatePublicProbeJob(
- const DnsProbeJob::CallbackType& job_callback);
-
- scoped_ptr<DnsProbeJob> CreateProbeJob(
- const net::DnsConfig& dns_config,
- const DnsProbeJob::CallbackType& job_callback);
- void GetSystemDnsConfig(net::DnsConfig* config);
- void GetPublicDnsConfig(net::DnsConfig* config);
- bool ResultsExpired();
-
- scoped_ptr<DnsProbeJob> system_job_;
- scoped_ptr<DnsProbeJob> public_job_;
- DnsProbeJob::Result system_result_;
- DnsProbeJob::Result public_result_;
- std::vector<CallbackType> callbacks_;
State state_;
- chrome_common_net::DnsProbeResult result_;
+ std::vector<ProbeCallback> pending_callbacks_;
base::Time probe_start_time_;
- // How many DNS request attempts the probe jobs will make before giving up
- // (Overrides the attempts field in the system DnsConfig.)
- const int dns_attempts_;
- // How many nameservers the system config has.
- int system_nameserver_count_;
- // Whether the only system nameserver is 127.0.0.1.
- bool system_is_localhost_;
+ chrome_common_net::DnsProbeStatus cached_result_;
+
+ // DnsProbeRunners for the system DNS configuration and a public DNS server.
+ DnsProbeRunner system_runner_;
+ DnsProbeRunner public_runner_;
DISALLOW_COPY_AND_ASSIGN(DnsProbeService);
};
diff --git a/chrome/browser/net/dns_probe_service_unittest.cc b/chrome/browser/net/dns_probe_service_unittest.cc
index 15cbb44..d8b55eb 100644
--- a/chrome/browser/net/dns_probe_service_unittest.cc
+++ b/chrome/browser/net/dns_probe_service_unittest.cc
@@ -9,111 +9,28 @@
#include "base/memory/weak_ptr.h"
#include "base/message_loop.h"
#include "base/run_loop.h"
-#include "chrome/browser/net/dns_probe_job.h"
+#include "chrome/browser/net/dns_probe_runner.h"
+#include "chrome/browser/net/dns_probe_test_util.h"
#include "chrome/common/net/net_error_info.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "net/dns/dns_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
-using chrome_common_net::DnsProbeResult;
+using base::MessageLoopForIO;
+using base::RunLoop;
+using chrome_common_net::DnsProbeStatus;
+using content::TestBrowserThreadBundle;
+using net::MockDnsClientRule;
namespace chrome_browser_net {
namespace {
-class MockDnsProbeJob : public DnsProbeJob {
- public:
- MockDnsProbeJob(const CallbackType& callback,
- DnsProbeJob::Result result)
- : weak_factory_(this) {
- base::MessageLoop::current()->PostTask(
- FROM_HERE,
- base::Bind(&MockDnsProbeJob::CallCallback,
- weak_factory_.GetWeakPtr(),
- callback,
- result));
- }
-
- virtual ~MockDnsProbeJob() { }
-
- private:
- void CallCallback(const CallbackType& callback, Result result) {
- callback.Run(this, result);
- }
-
- base::WeakPtrFactory<MockDnsProbeJob> weak_factory_;
-};
-
-class TestDnsProbeService : public DnsProbeService {
- public:
- TestDnsProbeService()
- : DnsProbeService(),
- system_job_created_(false),
- public_job_created_(false),
- mock_system_result_(DnsProbeJob::SERVERS_UNKNOWN),
- mock_public_result_(DnsProbeJob::SERVERS_UNKNOWN),
- mock_system_fail_(false) {
- }
-
- virtual ~TestDnsProbeService() { }
-
- void set_mock_results(
- DnsProbeJob::Result mock_system_result,
- DnsProbeJob::Result mock_public_result) {
- mock_system_result_ = mock_system_result;
- mock_public_result_ = mock_public_result;
- }
-
- void set_mock_system_fail(bool mock_system_fail) {
- mock_system_fail_ = mock_system_fail;
- }
-
- bool jobs_created(void) {
- return system_job_created_ && public_job_created_;
- }
-
- void ResetJobsCreated() {
- system_job_created_ = false;
- public_job_created_ = false;
- }
-
- void MockExpireResults() {
- ExpireResults();
- }
-
- bool system_job_created_;
- bool public_job_created_;
-
- private:
- // Override methods in DnsProbeService to return mock jobs:
-
- virtual scoped_ptr<DnsProbeJob> CreateSystemProbeJob(
- const DnsProbeJob::CallbackType& job_callback) OVERRIDE {
- if (mock_system_fail_)
- return scoped_ptr<DnsProbeJob>();
-
- system_job_created_ = true;
- return scoped_ptr<DnsProbeJob>(
- new MockDnsProbeJob(job_callback,
- mock_system_result_));
- }
-
- virtual scoped_ptr<DnsProbeJob> CreatePublicProbeJob(
- const DnsProbeJob::CallbackType& job_callback) OVERRIDE {
- public_job_created_ = true;
- return scoped_ptr<DnsProbeJob>(
- new MockDnsProbeJob(job_callback,
- mock_public_result_));
- }
-
- DnsProbeJob::Result mock_system_result_;
- DnsProbeJob::Result mock_public_result_;
- bool mock_system_fail_;
-};
-
class DnsProbeServiceTest : public testing::Test {
public:
DnsProbeServiceTest()
: callback_called_(false),
- callback_result_(chrome_common_net::DNS_PROBE_UNKNOWN) {
+ callback_result_(chrome_common_net::DNS_PROBE_MAX) {
}
void Probe() {
@@ -121,98 +38,94 @@ class DnsProbeServiceTest : public testing::Test {
base::Unretained(this)));
}
- void RunUntilIdle() {
- base::RunLoop run_loop;
- run_loop.RunUntilIdle();
- }
-
void Reset() {
- service_.ResetJobsCreated();
callback_called_ = false;
}
- base::MessageLoopForIO message_loop_;
- TestDnsProbeService service_;
- bool callback_called_;
- DnsProbeResult callback_result_;
+ protected:
+ void SetRules(MockDnsClientRule::Result system_query_result,
+ MockDnsClientRule::Result public_query_result) {
+ service_.SetSystemClientForTesting(
+ CreateMockDnsClientForProbes(system_query_result));
+ service_.SetPublicClientForTesting(
+ CreateMockDnsClientForProbes(public_query_result));
+ }
+
+ void RunTest(MockDnsClientRule::Result system_query_result,
+ MockDnsClientRule::Result public_query_result,
+ DnsProbeStatus expected_result) {
+ Reset();
+ SetRules(system_query_result, public_query_result);
+
+ Probe();
+ RunLoop().RunUntilIdle();
+ EXPECT_TRUE(callback_called_);
+ EXPECT_EQ(expected_result, callback_result_);
+ }
+
+ void ClearCachedResult() {
+ service_.ClearCachedResultForTesting();
+ }
private:
- void ProbeCallback(DnsProbeResult result) {
+ void ProbeCallback(DnsProbeStatus result) {
+ EXPECT_FALSE(callback_called_);
callback_called_ = true;
callback_result_ = result;
}
+
+ DnsProbeService service_;
+ bool callback_called_;
+ DnsProbeStatus callback_result_;
+ TestBrowserThreadBundle bundle_;
};
-TEST_F(DnsProbeServiceTest, Null) {
+TEST_F(DnsProbeServiceTest, Probe_OK_OK) {
+ RunTest(MockDnsClientRule::OK, MockDnsClientRule::OK,
+ chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
}
-TEST_F(DnsProbeServiceTest, Probe) {
- service_.set_mock_results(DnsProbeJob::SERVERS_CORRECT,
- DnsProbeJob::SERVERS_CORRECT);
-
- Probe();
- EXPECT_TRUE(service_.jobs_created());
- EXPECT_FALSE(callback_called_);
-
- RunUntilIdle();
- EXPECT_TRUE(callback_called_);
- EXPECT_EQ(chrome_common_net::DNS_PROBE_NXDOMAIN, callback_result_);
+TEST_F(DnsProbeServiceTest, Probe_TIMEOUT_OK) {
+ RunTest(MockDnsClientRule::TIMEOUT, MockDnsClientRule::OK,
+ chrome_common_net::DNS_PROBE_FINISHED_BAD_CONFIG);
}
-TEST_F(DnsProbeServiceTest, Cache) {
- service_.set_mock_results(DnsProbeJob::SERVERS_CORRECT,
- DnsProbeJob::SERVERS_CORRECT);
-
- Probe();
- RunUntilIdle();
- Reset();
-
- // Cached NXDOMAIN result should persist.
-
- Probe();
- EXPECT_FALSE(service_.jobs_created());
-
- RunUntilIdle();
- EXPECT_TRUE(callback_called_);
- EXPECT_EQ(chrome_common_net::DNS_PROBE_NXDOMAIN, callback_result_);
+TEST_F(DnsProbeServiceTest, Probe_TIMEOUT_TIMEOUT) {
+ RunTest(MockDnsClientRule::TIMEOUT, MockDnsClientRule::TIMEOUT,
+ chrome_common_net::DNS_PROBE_FINISHED_NO_INTERNET);
}
-TEST_F(DnsProbeServiceTest, Expired) {
- service_.set_mock_results(DnsProbeJob::SERVERS_CORRECT,
- DnsProbeJob::SERVERS_CORRECT);
-
- Probe();
- EXPECT_TRUE(service_.jobs_created());
-
- RunUntilIdle();
- EXPECT_TRUE(callback_called_);
- EXPECT_EQ(chrome_common_net::DNS_PROBE_NXDOMAIN, callback_result_);
-
- Reset();
-
- service_.MockExpireResults();
-
- Probe();
- EXPECT_TRUE(service_.jobs_created());
-
- RunUntilIdle();
- EXPECT_TRUE(callback_called_);
- EXPECT_EQ(chrome_common_net::DNS_PROBE_NXDOMAIN, callback_result_);
+TEST_F(DnsProbeServiceTest, Probe_OK_FAIL_SYNC) {
+ RunTest(MockDnsClientRule::OK, MockDnsClientRule::FAIL_SYNC,
+ chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
}
-TEST_F(DnsProbeServiceTest, SystemFail) {
- service_.set_mock_results(DnsProbeJob::SERVERS_CORRECT,
- DnsProbeJob::SERVERS_CORRECT);
- service_.set_mock_system_fail(true);
+TEST_F(DnsProbeServiceTest, Probe_FAIL_SYNC_OK) {
+ RunTest(MockDnsClientRule::FAIL_SYNC, MockDnsClientRule::OK,
+ chrome_common_net::DNS_PROBE_FINISHED_BAD_CONFIG);
+}
- Probe();
- EXPECT_TRUE(callback_called_);
- EXPECT_EQ(chrome_common_net::DNS_PROBE_UNKNOWN, callback_result_);
+TEST_F(DnsProbeServiceTest, Probe_FAIL_SYNC_FAIL_SYNC) {
+ RunTest(MockDnsClientRule::FAIL_SYNC, MockDnsClientRule::FAIL_SYNC,
+ chrome_common_net::DNS_PROBE_FINISHED_INCONCLUSIVE);
+}
- Reset();
+TEST_F(DnsProbeServiceTest, Cache) {
+ RunTest(MockDnsClientRule::OK, MockDnsClientRule::OK,
+ chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+ // Cached NXDOMAIN result should persist, not the result from the new rules.
+ RunTest(MockDnsClientRule::TIMEOUT, MockDnsClientRule::TIMEOUT,
+ chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+}
- RunUntilIdle();
- EXPECT_FALSE(callback_called_);
+TEST_F(DnsProbeServiceTest, Expire) {
+ RunTest(MockDnsClientRule::OK, MockDnsClientRule::OK,
+ chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+ // Pretend cache expires.
+ ClearCachedResult();
+ // New rules should apply, since a new probe should be run.
+ RunTest(MockDnsClientRule::TIMEOUT, MockDnsClientRule::TIMEOUT,
+ chrome_common_net::DNS_PROBE_FINISHED_NO_INTERNET);
}
} // namespace
diff --git a/chrome/browser/net/dns_probe_test_util.cc b/chrome/browser/net/dns_probe_test_util.cc
new file mode 100644
index 0000000..88d481d
--- /dev/null
+++ b/chrome/browser/net/dns_probe_test_util.cc
@@ -0,0 +1,37 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/net/dns_probe_test_util.h"
+
+#include "chrome/browser/net/dns_probe_runner.h"
+#include "net/dns/dns_config_service.h"
+#include "net/dns/dns_protocol.h"
+
+using net::DnsClient;
+using net::DnsConfig;
+using net::IPAddressNumber;
+using net::IPEndPoint;
+using net::MockDnsClientRule;
+using net::MockDnsClientRuleList;
+using net::ParseIPLiteralToNumber;
+
+namespace chrome_browser_net {
+
+scoped_ptr<DnsClient> CreateMockDnsClientForProbes(
+ MockDnsClientRule::Result result) {
+ DnsConfig config;
+ IPAddressNumber dns_ip;
+ ParseIPLiteralToNumber("192.168.1.1", &dns_ip);
+ const uint16 kDnsPort = net::dns_protocol::kDefaultPort;
+ config.nameservers.push_back(IPEndPoint(dns_ip, kDnsPort));
+
+ const uint16 kTypeA = net::dns_protocol::kTypeA;
+ MockDnsClientRuleList rules;
+ rules.push_back(
+ MockDnsClientRule(DnsProbeRunner::kKnownGoodHostname, kTypeA, result));
+
+ return CreateMockDnsClient(config, rules).Pass();
+}
+
+} // namespace chrome_browser_net
diff --git a/chrome/browser/net/dns_probe_test_util.h b/chrome/browser/net/dns_probe_test_util.h
new file mode 100644
index 0000000..0f36174
--- /dev/null
+++ b/chrome/browser/net/dns_probe_test_util.h
@@ -0,0 +1,21 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_NET_DNS_PROBE_TEST_UTIL_H_
+#define CHROME_BROWSER_NET_DNS_PROBE_TEST_UTIL_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "net/dns/dns_client.h"
+#include "net/dns/dns_test_util.h"
+
+namespace chrome_browser_net {
+
+// Creates a mock DNS client with a single rule for the known-good query
+// (currently google.com) that returns |result|.
+scoped_ptr<net::DnsClient> CreateMockDnsClientForProbes(
+ net::MockDnsClientRule::Result result);
+
+} // namespace chrome_browser_net
+
+#endif // CHROME_BROWSER_NET_DNS_PROBE_TEST_UTIL_H_
diff --git a/chrome/browser/net/net_error_tab_helper.cc b/chrome/browser/net/net_error_tab_helper.cc
index c7189a9..6f20a8a 100644
--- a/chrome/browser/net/net_error_tab_helper.cc
+++ b/chrome/browser/net/net_error_tab_helper.cc
@@ -18,7 +18,8 @@
#include "net/base/net_errors.h"
using base::FieldTrialList;
-using chrome_common_net::DnsProbeResult;
+using chrome_common_net::DnsProbeStatus;
+using chrome_common_net::DnsProbeStatusToString;
using content::BrowserContext;
using content::BrowserThread;
using content::PageTransition;
@@ -32,9 +33,6 @@ namespace chrome_browser_net {
namespace {
-const char kDnsProbeFieldTrialName[] = "DnsProbe-Enable";
-const char kDnsProbeFieldTrialEnableGroupName[] = "enable";
-
static NetErrorTabHelper::TestingState testing_state_ =
NetErrorTabHelper::TESTING_DEFAULT;
@@ -45,48 +43,24 @@ bool IsDnsError(int net_error) {
net_error == net::ERR_NAME_RESOLUTION_FAILED;
}
-bool GetEnabledByTrial() {
- return (FieldTrialList::FindFullName(kDnsProbeFieldTrialName)
- == kDnsProbeFieldTrialEnableGroupName);
-}
-
-NetErrorTracker::FrameType GetFrameType(bool is_main_frame) {
- return is_main_frame ? NetErrorTracker::FRAME_MAIN
- : NetErrorTracker::FRAME_SUB;
-}
-
-NetErrorTracker::PageType GetPageType(bool is_error_page) {
- return is_error_page ? NetErrorTracker::PAGE_ERROR
- : NetErrorTracker::PAGE_NORMAL;
-}
-
-NetErrorTracker::ErrorType GetErrorType(int net_error) {
- return IsDnsError(net_error) ? NetErrorTracker::ERROR_DNS
- : NetErrorTracker::ERROR_OTHER;
-}
-
void OnDnsProbeFinishedOnIOThread(
- const base::Callback<void(DnsProbeResult)>& callback,
- DnsProbeResult result) {
+ const base::Callback<void(DnsProbeStatus)>& callback,
+ DnsProbeStatus result) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- DVLOG(1) << "DNS probe finished with result " << result;
-
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
base::Bind(callback, result));
}
-// We can only access g_browser_process->io_thread() from the browser thread,
-// so we have to pass it in to the callback instead of dereferencing it here.
+// Can only access g_browser_process->io_thread() from the browser thread,
+// so have to pass it in to the callback instead of dereferencing it here.
void StartDnsProbeOnIOThread(
- const base::Callback<void(DnsProbeResult)>& callback,
+ const base::Callback<void(DnsProbeStatus)>& callback,
IOThread* io_thread) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- DVLOG(1) << "Starting DNS probe";
-
DnsProbeService* probe_service =
io_thread->globals()->dns_probe_service.get();
@@ -113,8 +87,10 @@ void NetErrorTabHelper::DidStartProvisionalLoadForFrame(
RenderViewHost* render_view_host) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- tracker_.OnStartProvisionalLoad(GetFrameType(is_main_frame),
- GetPageType(is_error_page));
+ if (!is_main_frame)
+ return;
+
+ is_error_page_ = is_error_page;
}
void NetErrorTabHelper::DidCommitProvisionalLoadForFrame(
@@ -125,7 +101,21 @@ void NetErrorTabHelper::DidCommitProvisionalLoadForFrame(
RenderViewHost* render_view_host) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- tracker_.OnCommitProvisionalLoad(GetFrameType(is_main_frame));
+ if (!is_main_frame)
+ return;
+
+ // Resend status every time an error page commits; this is somewhat spammy,
+ // but ensures that the status will make it to the real error page, even if
+ // the link doctor loads a blank intermediate page or the tab switches
+ // renderer processes.
+ if (is_error_page_ && dns_error_active_) {
+ dns_error_page_committed_ = true;
+ DVLOG(1) << "Committed error page; resending status.";
+ SendInfo();
+ } else {
+ dns_error_active_ = false;
+ dns_error_page_committed_ = false;
+ }
}
void NetErrorTabHelper::DidFailProvisionalLoad(
@@ -137,75 +127,70 @@ void NetErrorTabHelper::DidFailProvisionalLoad(
RenderViewHost* render_view_host) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- tracker_.OnFailProvisionalLoad(GetFrameType(is_main_frame),
- GetErrorType(error_code));
-}
+ if (!is_main_frame)
+ return;
-void NetErrorTabHelper::DidFinishLoad(
- int64 frame_id,
- const GURL& validated_url,
- bool is_main_frame,
- RenderViewHost* render_view_host) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- tracker_.OnFinishLoad(GetFrameType(is_main_frame));
+ if (IsDnsError(error_code)) {
+ dns_error_active_ = true;
+ OnMainFrameDnsError();
+ }
}
NetErrorTabHelper::NetErrorTabHelper(WebContents* contents)
: WebContentsObserver(contents),
weak_factory_(this),
- tracker_(base::Bind(&NetErrorTabHelper::TrackerCallback,
- weak_factory_.GetWeakPtr())),
- dns_error_page_state_(NetErrorTracker::DNS_ERROR_PAGE_NONE),
- dns_probe_state_(DNS_PROBE_NONE),
- enabled_by_trial_(GetEnabledByTrial()) {
+ is_error_page_(false),
+ dns_error_active_(false),
+ dns_error_page_committed_(false),
+ dns_probe_status_(chrome_common_net::DNS_PROBE_POSSIBLE),
+ enabled_by_trial_(chrome_common_net::DnsProbesEnabledByFieldTrial()) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- InitializePref(contents);
+ // If this helper is under test, it won't have a WebContents.
+ if (contents)
+ InitializePref(contents);
}
-void NetErrorTabHelper::TrackerCallback(
- NetErrorTracker::DnsErrorPageState state) {
- dns_error_page_state_ = state;
-
- MaybePostStartDnsProbeTask();
- MaybeSendInfo();
+void NetErrorTabHelper::OnMainFrameDnsError() {
+ if (ProbesAllowed()) {
+ // Don't start more than one probe at a time.
+ if (dns_probe_status_ != chrome_common_net::DNS_PROBE_STARTED) {
+ StartDnsProbe();
+ dns_probe_status_ = chrome_common_net::DNS_PROBE_STARTED;
+ }
+ } else {
+ dns_probe_status_ = chrome_common_net::DNS_PROBE_NOT_RUN;
+ }
}
-void NetErrorTabHelper::MaybePostStartDnsProbeTask() {
+void NetErrorTabHelper::StartDnsProbe() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(dns_error_active_);
+ DCHECK_NE(chrome_common_net::DNS_PROBE_STARTED, dns_probe_status_);
- if (dns_error_page_state_ != NetErrorTracker::DNS_ERROR_PAGE_NONE &&
- dns_probe_state_ != DNS_PROBE_STARTED &&
- ProbesAllowed()) {
- BrowserThread::PostTask(
- BrowserThread::IO,
- FROM_HERE,
- base::Bind(&StartDnsProbeOnIOThread,
- base::Bind(&NetErrorTabHelper::OnDnsProbeFinished,
- weak_factory_.GetWeakPtr()),
- g_browser_process->io_thread()));
- dns_probe_state_ = DNS_PROBE_STARTED;
- }
+ DVLOG(1) << "Starting DNS probe.";
+
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&StartDnsProbeOnIOThread,
+ base::Bind(&NetErrorTabHelper::OnDnsProbeFinished,
+ weak_factory_.GetWeakPtr()),
+ g_browser_process->io_thread()));
}
-void NetErrorTabHelper::OnDnsProbeFinished(DnsProbeResult result) {
+void NetErrorTabHelper::OnDnsProbeFinished(DnsProbeStatus result) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DCHECK_EQ(DNS_PROBE_STARTED, dns_probe_state_);
+ DCHECK_EQ(chrome_common_net::DNS_PROBE_STARTED, dns_probe_status_);
+ DCHECK(chrome_common_net::DnsProbeStatusIsFinished(result));
- dns_probe_result_ = result;
- dns_probe_state_ = DNS_PROBE_FINISHED;
+ DVLOG(1) << "Finished DNS probe with result "
+ << DnsProbeStatusToString(result) << ".";
- MaybeSendInfo();
-}
+ dns_probe_status_ = result;
-void NetErrorTabHelper::MaybeSendInfo() {
- if (dns_error_page_state_ == NetErrorTracker::DNS_ERROR_PAGE_LOADED &&
- dns_probe_state_ == DNS_PROBE_FINISHED) {
- DVLOG(1) << "Sending result " << dns_probe_result_ << " to renderer";
- Send(new ChromeViewMsg_NetErrorInfo(routing_id(), dns_probe_result_));
- dns_probe_state_ = DNS_PROBE_NONE;
- }
+ if (dns_error_page_committed_)
+ SendInfo();
}
void NetErrorTabHelper::InitializePref(WebContents* contents) {
@@ -226,4 +211,15 @@ bool NetErrorTabHelper::ProbesAllowed() const {
return enabled_by_trial_ && *resolve_errors_with_web_service_;
}
+void NetErrorTabHelper::SendInfo() {
+ DCHECK_NE(chrome_common_net::DNS_PROBE_POSSIBLE, dns_probe_status_);
+ DCHECK(dns_error_page_committed_);
+
+ DVLOG(1) << "Sending status " << DnsProbeStatusToString(dns_probe_status_);
+ Send(new ChromeViewMsg_NetErrorInfo(routing_id(), dns_probe_status_));
+
+ if (!dns_probe_status_snoop_callback_.is_null())
+ dns_probe_status_snoop_callback_.Run(dns_probe_status_);
+}
+
} // namespace chrome_browser_net
diff --git a/chrome/browser/net/net_error_tab_helper.h b/chrome/browser/net/net_error_tab_helper.h
index e48fa9f..fd0553e 100644
--- a/chrome/browser/net/net_error_tab_helper.h
+++ b/chrome/browser/net/net_error_tab_helper.h
@@ -6,12 +6,12 @@
#define CHROME_BROWSER_NET_NET_ERROR_TAB_HELPER_H_
#include "base/basictypes.h"
+#include "base/bind.h"
#include "base/compiler_specific.h"
#include "base/memory/weak_ptr.h"
#include "base/prefs/pref_member.h"
#include "chrome/browser/net/dns_probe_service.h"
#include "chrome/common/net/net_error_info.h"
-#include "chrome/common/net/net_error_tracker.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"
@@ -30,10 +30,21 @@ class NetErrorTabHelper
TESTING_FORCE_ENABLED
};
+ typedef base::Callback<void(chrome_common_net::DnsProbeStatus)>
+ DnsProbeStatusSnoopCallback;
+
virtual ~NetErrorTabHelper();
static void set_state_for_testing(TestingState testing_state);
+ // Sets a callback that will be called immediately after the helper sends
+ // a NetErrorHelper IPC. (Used by the DNS probe browser test to know when to
+ // check the error page for updates, instead of polling.)
+ void set_dns_probe_status_snoop_callback_for_testing(
+ const DnsProbeStatusSnoopCallback& dns_probe_status_snoop_callback) {
+ dns_probe_status_snoop_callback_ = dns_probe_status_snoop_callback;
+ }
+
// content::WebContentsObserver implementation.
virtual void DidStartProvisionalLoadForFrame(
int64 frame_id,
@@ -59,43 +70,50 @@ class NetErrorTabHelper
const string16& error_description,
content::RenderViewHost* render_view_host) OVERRIDE;
- virtual void DidFinishLoad(
- int64 frame_id,
- const GURL& validated_url,
- bool is_main_frame,
- content::RenderViewHost* render_view_host) OVERRIDE;
-
- private:
- friend class content::WebContentsUserData<NetErrorTabHelper>;
-
- enum DnsProbeState {
- DNS_PROBE_NONE,
- DNS_PROBE_STARTED,
- DNS_PROBE_FINISHED
- };
-
+ protected:
// |contents| is the WebContents of the tab this NetErrorTabHelper is
// attached to.
explicit NetErrorTabHelper(content::WebContents* contents);
+ virtual void StartDnsProbe();
+ virtual void SendInfo();
+ void OnDnsProbeFinished(chrome_common_net::DnsProbeStatus result);
+
+ chrome_common_net::DnsProbeStatus dns_probe_status() const {
+ return dns_probe_status_;
+ }
+
+ private:
+ friend class content::WebContentsUserData<NetErrorTabHelper>;
- void TrackerCallback(NetErrorTracker::DnsErrorPageState state);
- void MaybePostStartDnsProbeTask();
- void OnDnsProbeFinished(chrome_common_net::DnsProbeResult result);
- void MaybeSendInfo();
+ void OnMainFrameDnsError();
void InitializePref(content::WebContents* contents);
bool ProbesAllowed() const;
base::WeakPtrFactory<NetErrorTabHelper> weak_factory_;
- NetErrorTracker tracker_;
- NetErrorTracker::DnsErrorPageState dns_error_page_state_;
+ // True if the last provisional load that started was for an error page.
+ bool is_error_page_;
+
+ // True if the helper has seen a main frame page load fail with a DNS error,
+ // but has not yet seen a new page commit successfully afterwards.
+ bool dns_error_active_;
+
+ // True if the helper has seen an error page commit while |dns_error_active_|
+ // is true. (This should never be true if |dns_error_active_| is false.)
+ bool dns_error_page_committed_;
- DnsProbeState dns_probe_state_;
- chrome_common_net::DnsProbeResult dns_probe_result_;
+ // The status of a DNS probe that may or may not have started or finished.
+ // Since the renderer can change out from under the helper (in cross-process
+ // navigations), it re-sends the status whenever an error page commits.
+ chrome_common_net::DnsProbeStatus dns_probe_status_;
// Whether we are enabled to run by the DnsProbe-Enable field trial.
const bool enabled_by_trial_;
+
+ // Optional callback for browser test to snoop on outgoing NetErrorInfo IPCs.
+ DnsProbeStatusSnoopCallback dns_probe_status_snoop_callback_;
+
// "Use a web service to resolve navigation errors" preference is required
// to allow probes.
BooleanPrefMember resolve_errors_with_web_service_;
diff --git a/chrome/browser/net/net_error_tab_helper_unittest.cc b/chrome/browser/net/net_error_tab_helper_unittest.cc
new file mode 100644
index 0000000..3a029bb
--- /dev/null
+++ b/chrome/browser/net/net_error_tab_helper_unittest.cc
@@ -0,0 +1,392 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/net/net_error_tab_helper.h"
+
+#include "base/message_loop.h"
+#include "chrome/common/net/net_error_info.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/common/page_transition_types.h"
+#include "content/public/test/test_browser_thread.h"
+#include "net/base/net_errors.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::MessageLoop;
+using chrome_browser_net::NetErrorTabHelper;
+using chrome_common_net::DnsProbeStatus;
+using content::BrowserThread;
+using content::TestBrowserThread;
+
+class TestNetErrorTabHelper : public NetErrorTabHelper {
+ public:
+ TestNetErrorTabHelper()
+ : NetErrorTabHelper(NULL),
+ mock_probe_running_(false),
+ last_status_sent_(chrome_common_net::DNS_PROBE_MAX),
+ mock_sent_count_(0) {}
+
+ void FinishProbe(DnsProbeStatus status) {
+ EXPECT_TRUE(mock_probe_running_);
+ OnDnsProbeFinished(status);
+ mock_probe_running_ = false;
+ }
+
+ bool mock_probe_running() const { return mock_probe_running_; }
+ DnsProbeStatus last_status_sent() const { return last_status_sent_; }
+ int mock_sent_count() const { return mock_sent_count_; }
+
+ private:
+ virtual void StartDnsProbe() OVERRIDE {
+ EXPECT_FALSE(mock_probe_running_);
+ mock_probe_running_ = true;
+ }
+
+ virtual void SendInfo() OVERRIDE {
+ last_status_sent_ = dns_probe_status();
+ mock_sent_count_++;
+ }
+
+ bool mock_probe_running_;
+ DnsProbeStatus last_status_sent_;
+ int mock_sent_count_;
+};
+
+class NetErrorTabHelperTest : public testing::Test {
+ protected:
+ enum MainFrame { SUB_FRAME, MAIN_FRAME };
+ enum ErrorPage { NORMAL_PAGE, ERROR_PAGE };
+ enum ErrorType { DNS_ERROR, OTHER_ERROR };
+
+ NetErrorTabHelperTest()
+ : fake_ui_thread_(BrowserThread::UI, &message_loop_) {
+ NetErrorTabHelper::set_state_for_testing(
+ NetErrorTabHelper::TESTING_FORCE_ENABLED);
+ }
+
+ void StartProvisionalLoad(MainFrame main_frame, ErrorPage error_page) {
+ tab_helper_.DidStartProvisionalLoadForFrame(
+ 1, // frame_id
+ 0, // parent_frame_id
+ (main_frame == MAIN_FRAME),
+ bogus_url_, // validated_url
+ (error_page == ERROR_PAGE),
+ false, // is_iframe_srcdoc
+ NULL); // render_view_host
+ }
+
+ void CommitProvisionalLoad(MainFrame main_frame) {
+ tab_helper_.DidCommitProvisionalLoadForFrame(
+ 1, // frame id
+ (main_frame == MAIN_FRAME),
+ bogus_url_, // url
+ content::PAGE_TRANSITION_TYPED,
+ NULL); // render_view_host
+ }
+
+ void FailProvisionalLoad(MainFrame main_frame, ErrorType error_type) {
+ int net_error;
+
+ if (error_type == DNS_ERROR)
+ net_error = net::ERR_NAME_NOT_RESOLVED;
+ else
+ net_error = net::ERR_TIMED_OUT;
+
+ tab_helper_.DidFailProvisionalLoad(
+ 1, // frame id
+ (main_frame == MAIN_FRAME),
+ bogus_url_, // validated_url
+ net_error,
+ string16(),
+ NULL); // render_view_host
+ }
+
+ void FinishProbe(DnsProbeStatus status) {
+ tab_helper_.FinishProbe(status);
+ }
+
+ bool probe_running() { return tab_helper_.mock_probe_running(); }
+ DnsProbeStatus last_status_sent() { return tab_helper_.last_status_sent(); }
+ int sent_count() { return tab_helper_.mock_sent_count(); }
+
+ private:
+ MessageLoop message_loop_;
+ TestBrowserThread fake_ui_thread_;
+ TestNetErrorTabHelper tab_helper_;
+ GURL bogus_url_;
+};
+
+TEST_F(NetErrorTabHelperTest, Null) {
+ EXPECT_FALSE(probe_running());
+}
+
+TEST_F(NetErrorTabHelperTest, MainFrameNonDnsError) {
+ StartProvisionalLoad(MAIN_FRAME, NORMAL_PAGE);
+ FailProvisionalLoad(MAIN_FRAME, OTHER_ERROR);
+ EXPECT_FALSE(probe_running());
+ EXPECT_EQ(0, sent_count());
+}
+
+TEST_F(NetErrorTabHelperTest, NonMainFrameDnsError) {
+ StartProvisionalLoad(SUB_FRAME, NORMAL_PAGE);
+ FailProvisionalLoad(SUB_FRAME, DNS_ERROR);
+ EXPECT_FALSE(probe_running());
+ EXPECT_EQ(0, sent_count());
+}
+
+// Test complete DNS error page loads. Note that the helper can see two error
+// page loads: Link Doctor loads an empty HTML page so the user knows something
+// is going on, then fails over to the normal error page if and when Link
+// Doctor fails to load or declines to provide a page.
+
+TEST_F(NetErrorTabHelperTest, ProbeResponseBeforeFirstCommit) {
+ StartProvisionalLoad(MAIN_FRAME, NORMAL_PAGE);
+ FailProvisionalLoad(MAIN_FRAME, DNS_ERROR);
+ EXPECT_TRUE(probe_running());
+ EXPECT_EQ(0, sent_count());
+
+ StartProvisionalLoad(MAIN_FRAME, ERROR_PAGE);
+ EXPECT_TRUE(probe_running());
+ EXPECT_EQ(0, sent_count());
+
+ FinishProbe(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+ EXPECT_FALSE(probe_running());
+ EXPECT_EQ(0, sent_count());
+
+ CommitProvisionalLoad(MAIN_FRAME);
+ EXPECT_FALSE(probe_running());
+ EXPECT_EQ(1, sent_count());
+ EXPECT_EQ(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN, last_status_sent());
+
+ StartProvisionalLoad(MAIN_FRAME, ERROR_PAGE);
+ EXPECT_FALSE(probe_running());
+ EXPECT_EQ(1, sent_count());
+
+ CommitProvisionalLoad(MAIN_FRAME);
+ EXPECT_FALSE(probe_running());
+ EXPECT_EQ(2, sent_count());
+ EXPECT_EQ(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN, last_status_sent());
+}
+
+TEST_F(NetErrorTabHelperTest, ProbeResponseBetweenFirstAndSecondCommit) {
+ StartProvisionalLoad(MAIN_FRAME, NORMAL_PAGE);
+ FailProvisionalLoad(MAIN_FRAME, DNS_ERROR);
+ EXPECT_TRUE(probe_running());
+ EXPECT_EQ(0, sent_count());
+
+ StartProvisionalLoad(MAIN_FRAME, ERROR_PAGE);
+ EXPECT_TRUE(probe_running());
+ EXPECT_EQ(0, sent_count());
+
+ CommitProvisionalLoad(MAIN_FRAME);
+ EXPECT_TRUE(probe_running());
+ EXPECT_EQ(1, sent_count());
+ EXPECT_EQ(chrome_common_net::DNS_PROBE_STARTED, last_status_sent());
+
+ FinishProbe(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+ EXPECT_FALSE(probe_running());
+ EXPECT_EQ(2, sent_count());
+ EXPECT_EQ(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN, last_status_sent());
+
+ StartProvisionalLoad(MAIN_FRAME, ERROR_PAGE);
+ EXPECT_FALSE(probe_running());
+ EXPECT_EQ(2, sent_count());
+
+ CommitProvisionalLoad(MAIN_FRAME);
+ EXPECT_FALSE(probe_running());
+ EXPECT_EQ(3, sent_count());
+ EXPECT_EQ(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN, last_status_sent());
+}
+
+TEST_F(NetErrorTabHelperTest, ProbeResponseAfterSecondCommit) {
+ StartProvisionalLoad(MAIN_FRAME, NORMAL_PAGE);
+ FailProvisionalLoad(MAIN_FRAME, DNS_ERROR);
+ EXPECT_TRUE(probe_running());
+ EXPECT_EQ(0, sent_count());
+
+ StartProvisionalLoad(MAIN_FRAME, ERROR_PAGE);
+ EXPECT_TRUE(probe_running());
+ EXPECT_EQ(0, sent_count());
+
+ CommitProvisionalLoad(MAIN_FRAME);
+ EXPECT_TRUE(probe_running());
+ EXPECT_EQ(1, sent_count());
+ EXPECT_EQ(chrome_common_net::DNS_PROBE_STARTED, last_status_sent());
+
+ StartProvisionalLoad(MAIN_FRAME, ERROR_PAGE);
+ EXPECT_TRUE(probe_running());
+ EXPECT_EQ(1, sent_count());
+
+ CommitProvisionalLoad(MAIN_FRAME);
+ EXPECT_TRUE(probe_running());
+ EXPECT_EQ(2, sent_count());
+ EXPECT_EQ(chrome_common_net::DNS_PROBE_STARTED, last_status_sent());
+
+ FinishProbe(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+ EXPECT_FALSE(probe_running());
+ EXPECT_EQ(3, sent_count());
+ EXPECT_EQ(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN, last_status_sent());
+}
+
+// Send result even if a new page load has started; the error page is still
+// visible, and the user might cancel the load.
+TEST_F(NetErrorTabHelperTest, ProbeResponseAfterNewStart) {
+ StartProvisionalLoad(MAIN_FRAME, NORMAL_PAGE);
+ FailProvisionalLoad(MAIN_FRAME, DNS_ERROR);
+ EXPECT_TRUE(probe_running());
+ EXPECT_EQ(0, sent_count());
+
+ StartProvisionalLoad(MAIN_FRAME, ERROR_PAGE);
+ EXPECT_TRUE(probe_running());
+ EXPECT_EQ(0, sent_count());
+
+ CommitProvisionalLoad(MAIN_FRAME);
+ EXPECT_TRUE(probe_running());
+ EXPECT_EQ(1, sent_count());
+ EXPECT_EQ(chrome_common_net::DNS_PROBE_STARTED, last_status_sent());
+
+ StartProvisionalLoad(MAIN_FRAME, ERROR_PAGE);
+ EXPECT_TRUE(probe_running());
+ EXPECT_EQ(1, sent_count());
+
+ CommitProvisionalLoad(MAIN_FRAME);
+ EXPECT_TRUE(probe_running());
+ EXPECT_EQ(2, sent_count());
+ EXPECT_EQ(chrome_common_net::DNS_PROBE_STARTED, last_status_sent());
+
+ StartProvisionalLoad(MAIN_FRAME, NORMAL_PAGE);
+ EXPECT_TRUE(probe_running());
+ EXPECT_EQ(2, sent_count());
+
+ FinishProbe(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+ EXPECT_FALSE(probe_running());
+ EXPECT_EQ(3, sent_count());
+ EXPECT_EQ(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN, last_status_sent());
+}
+
+// Don't send result if a new page has committed; the result would go to the
+// wrong page, and the error page is gone anyway.
+TEST_F(NetErrorTabHelperTest, ProbeResponseAfterNewCommit) {
+ StartProvisionalLoad(MAIN_FRAME, NORMAL_PAGE);
+ FailProvisionalLoad(MAIN_FRAME, DNS_ERROR);
+ EXPECT_TRUE(probe_running());
+ EXPECT_EQ(0, sent_count());
+
+ StartProvisionalLoad(MAIN_FRAME, ERROR_PAGE);
+ EXPECT_TRUE(probe_running());
+ EXPECT_EQ(0, sent_count());
+
+ CommitProvisionalLoad(MAIN_FRAME);
+ EXPECT_TRUE(probe_running());
+ EXPECT_EQ(1, sent_count());
+ EXPECT_EQ(chrome_common_net::DNS_PROBE_STARTED, last_status_sent());
+
+ StartProvisionalLoad(MAIN_FRAME, ERROR_PAGE);
+ EXPECT_TRUE(probe_running());
+ EXPECT_EQ(1, sent_count());
+
+ CommitProvisionalLoad(MAIN_FRAME);
+ EXPECT_TRUE(probe_running());
+ EXPECT_EQ(2, sent_count());
+ EXPECT_EQ(chrome_common_net::DNS_PROBE_STARTED, last_status_sent());
+
+ StartProvisionalLoad(MAIN_FRAME, NORMAL_PAGE);
+ EXPECT_TRUE(probe_running());
+ EXPECT_EQ(2, sent_count());
+
+ CommitProvisionalLoad(MAIN_FRAME);
+ EXPECT_TRUE(probe_running());
+ EXPECT_EQ(2, sent_count());
+
+ FinishProbe(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+ EXPECT_FALSE(probe_running());
+ EXPECT_EQ(2, sent_count());
+}
+
+TEST_F(NetErrorTabHelperTest, MultipleDnsErrorsWithProbesWithoutErrorPages) {
+ StartProvisionalLoad(MAIN_FRAME, NORMAL_PAGE);
+ FailProvisionalLoad(MAIN_FRAME, DNS_ERROR);
+ EXPECT_TRUE(probe_running());
+ EXPECT_EQ(0, sent_count());
+
+ FinishProbe(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+ EXPECT_FALSE(probe_running());
+ EXPECT_EQ(0, sent_count());
+
+ StartProvisionalLoad(MAIN_FRAME, NORMAL_PAGE);
+ FailProvisionalLoad(MAIN_FRAME, DNS_ERROR);
+ EXPECT_TRUE(probe_running());
+ EXPECT_EQ(0, sent_count());
+
+ FinishProbe(chrome_common_net::DNS_PROBE_FINISHED_NO_INTERNET);
+ EXPECT_FALSE(probe_running());
+ EXPECT_EQ(0, sent_count());
+}
+
+TEST_F(NetErrorTabHelperTest, MultipleDnsErrorsWithProbesAndErrorPages) {
+ StartProvisionalLoad(MAIN_FRAME, NORMAL_PAGE);
+ FailProvisionalLoad(MAIN_FRAME, DNS_ERROR);
+ EXPECT_TRUE(probe_running());
+ EXPECT_EQ(0, sent_count());
+
+ StartProvisionalLoad(MAIN_FRAME, ERROR_PAGE);
+ CommitProvisionalLoad(MAIN_FRAME);
+ EXPECT_TRUE(probe_running());
+ EXPECT_EQ(1, sent_count());
+ EXPECT_EQ(chrome_common_net::DNS_PROBE_STARTED, last_status_sent());
+
+ FinishProbe(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+ EXPECT_FALSE(probe_running());
+ EXPECT_EQ(2, sent_count());
+ EXPECT_EQ(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN, last_status_sent());
+
+ StartProvisionalLoad(MAIN_FRAME, NORMAL_PAGE);
+ FailProvisionalLoad(MAIN_FRAME, DNS_ERROR);
+ EXPECT_TRUE(probe_running());
+ EXPECT_EQ(2, sent_count());
+
+ StartProvisionalLoad(MAIN_FRAME, ERROR_PAGE);
+ CommitProvisionalLoad(MAIN_FRAME);
+ EXPECT_TRUE(probe_running());
+ EXPECT_EQ(3, sent_count());
+ EXPECT_EQ(chrome_common_net::DNS_PROBE_STARTED, last_status_sent());
+
+ FinishProbe(chrome_common_net::DNS_PROBE_FINISHED_NO_INTERNET);
+ EXPECT_FALSE(probe_running());
+ EXPECT_EQ(4, sent_count());
+ EXPECT_EQ(chrome_common_net::DNS_PROBE_FINISHED_NO_INTERNET,
+ last_status_sent());
+}
+
+// If multiple DNS errors occur in a row before a probe result, don't start
+// multiple probes.
+TEST_F(NetErrorTabHelperTest, CoalesceFailures) {
+ StartProvisionalLoad(MAIN_FRAME, NORMAL_PAGE);
+ FailProvisionalLoad(MAIN_FRAME, DNS_ERROR);
+ StartProvisionalLoad(MAIN_FRAME, ERROR_PAGE);
+ CommitProvisionalLoad(MAIN_FRAME);
+ EXPECT_TRUE(probe_running());
+ EXPECT_EQ(1, sent_count());
+ EXPECT_EQ(chrome_common_net::DNS_PROBE_STARTED, last_status_sent());
+
+ StartProvisionalLoad(MAIN_FRAME, NORMAL_PAGE);
+ FailProvisionalLoad(MAIN_FRAME, DNS_ERROR);
+ StartProvisionalLoad(MAIN_FRAME, ERROR_PAGE);
+ CommitProvisionalLoad(MAIN_FRAME);
+ EXPECT_TRUE(probe_running());
+ EXPECT_EQ(2, sent_count());
+ EXPECT_EQ(chrome_common_net::DNS_PROBE_STARTED, last_status_sent());
+
+ StartProvisionalLoad(MAIN_FRAME, NORMAL_PAGE);
+ FailProvisionalLoad(MAIN_FRAME, DNS_ERROR);
+ StartProvisionalLoad(MAIN_FRAME, ERROR_PAGE);
+ CommitProvisionalLoad(MAIN_FRAME);
+ EXPECT_TRUE(probe_running());
+ EXPECT_EQ(3, sent_count());
+ EXPECT_EQ(chrome_common_net::DNS_PROBE_STARTED, last_status_sent());
+
+ FinishProbe(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+ EXPECT_FALSE(probe_running());
+ EXPECT_EQ(4, sent_count());
+ EXPECT_EQ(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN, last_status_sent());
+}