// 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 <set>
#include "base/bind.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/path_service.h"
#include "base/prefs/pref_service.h"
#include "base/run_loop.h"
#include "base/threading/thread_restrictions.h"
#include "chrome/browser/browser_process.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/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.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/common/pref_names.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/google/core/browser/google_util.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/public/test/test_navigation_observer.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_interceptor.h"
#include "net/url_request/url_request_job.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::URLRequestInterceptor;
using net::URLRequestJob;
using ui_test_utils::NavigateToURL;
using ui_test_utils::NavigateToURLBlockUntilNavigationsComplete;
namespace chrome_browser_net {
namespace {
// Postable function to run a Closure on the UI thread. Since
// BrowserThread::PostTask returns a bool, it can't directly be posted to
// another thread.
void RunClosureOnUIThread(const base::Closure& closure) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, closure);
}
// 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.json");
}
// A request that can be delayed until Resume() is called. Can also run a
// callback if destroyed without being resumed. Resume can be called either
// before or after a the request is started.
class DelayableRequest {
public:
// Called by a DelayableRequest if it was set to be delayed, and has been
// destroyed without Undelay being called.
typedef base::Callback<void(DelayableRequest* request)> DestructionCallback;
virtual void Resume() = 0;
protected:
virtual ~DelayableRequest() {}
};
class DelayableURLRequestFailedJob : public URLRequestFailedJob,
public DelayableRequest {
public:
// |destruction_callback| is only called if a delayed request is destroyed
// without being resumed.
DelayableURLRequestFailedJob(net::URLRequest* request,
net::NetworkDelegate* network_delegate,
int net_error,
bool should_delay,
const DestructionCallback& destruction_callback)
: URLRequestFailedJob(request, network_delegate, net_error),
should_delay_(should_delay),
start_delayed_(false),
destruction_callback_(destruction_callback) {}
virtual void Start() OVERRIDE {
if (should_delay_) {
DCHECK(!start_delayed_);
start_delayed_ = true;
return;
}
URLRequestFailedJob::Start();
}
virtual void Resume() OVERRIDE {
DCHECK(should_delay_);
should_delay_ = false;
if (start_delayed_) {
start_delayed_ = false;
Start();
}
}
private:
virtual ~DelayableURLRequestFailedJob() {
if (should_delay_)
destruction_callback_.Run(this);
}
bool should_delay_;
bool start_delayed_;
const DestructionCallback destruction_callback_;
};
class DelayableURLRequestMockHTTPJob : public URLRequestMockHTTPJob,
public DelayableRequest {
public:
DelayableURLRequestMockHTTPJob(
net::URLRequest* request,
net::NetworkDelegate* network_delegate,
const base::FilePath& file_path,
bool should_delay,
const DestructionCallback& destruction_callback)
: URLRequestMockHTTPJob(request, network_delegate, file_path),
should_delay_(should_delay),
start_delayed_(false),
destruction_callback_(destruction_callback) {}
virtual void Start() OVERRIDE {
if (should_delay_) {
DCHECK(!start_delayed_);
start_delayed_ = true;
return;
}
URLRequestMockHTTPJob::Start();
}
virtual void Resume() OVERRIDE {
DCHECK(should_delay_);
should_delay_ = false;
if (start_delayed_) {
start_delayed_ = false;
Start();
}
}
private:
virtual ~DelayableURLRequestMockHTTPJob() {
if (should_delay_)
destruction_callback_.Run(this);
}
bool should_delay_;
bool start_delayed_;
const DestructionCallback destruction_callback_;
};
// Interceptor for navigation correction requests. Can cause requests to
// fail with an error, and/or delay a request until a test allows to continue.
// Also can run a callback when a delayed request is cancelled.
class BreakableCorrectionInterceptor : public URLRequestInterceptor {
public:
explicit BreakableCorrectionInterceptor(
const FilePath& mock_corrections_file_path)
: mock_corrections_file_path_(mock_corrections_file_path),
net_error_(net::OK),
delay_requests_(false),
on_request_destroyed_callback_(
base::Bind(&BreakableCorrectionInterceptor::OnRequestDestroyed,
base::Unretained(this))) {
}
virtual ~BreakableCorrectionInterceptor() {
// All delayed requests should have been resumed or cancelled by this point.
EXPECT_TRUE(delayed_requests_.empty());
}
virtual URLRequestJob* MaybeInterceptRequest(
URLRequest* request,
NetworkDelegate* network_delegate) const OVERRIDE {
if (net_error_ != net::OK) {
DelayableURLRequestFailedJob* job =
new DelayableURLRequestFailedJob(
request, network_delegate, net_error_, delay_requests_,
on_request_destroyed_callback_);
if (delay_requests_)
delayed_requests_.insert(job);
return job;
} else {
DelayableURLRequestMockHTTPJob* job =
new DelayableURLRequestMockHTTPJob(
request, network_delegate, mock_corrections_file_path_,
delay_requests_, on_request_destroyed_callback_);
if (delay_requests_)
delayed_requests_.insert(job);
return job;
}
}
void set_net_error(int net_error) { net_error_ = net_error; }
void SetDelayRequests(bool delay_requests) {
delay_requests_ = delay_requests;
// Resume all delayed requests if no longer delaying requests.
if (!delay_requests) {
while (!delayed_requests_.empty()) {
DelayableRequest* request = *delayed_requests_.begin();
delayed_requests_.erase(request);
request->Resume();
}
}
}
// Runs |callback| once all delayed requests have been destroyed. Does not
// wait for delayed requests that have been resumed.
void SetRequestDestructionCallback(const base::Closure& callback) {
ASSERT_TRUE(delayed_request_destruction_callback_.is_null());
if (delayed_requests_.empty()) {
callback.Run();
return;
}
delayed_request_destruction_callback_ = callback;
}
void OnRequestDestroyed(DelayableRequest* request) {
ASSERT_EQ(1u, delayed_requests_.count(request));
delayed_requests_.erase(request);
if (delayed_requests_.empty() &&
!delayed_request_destruction_callback_.is_null()) {
delayed_request_destruction_callback_.Run();
delayed_request_destruction_callback_.Reset();
}
}
private:
const FilePath mock_corrections_file_path_;
int net_error_;
bool delay_requests_;
// Called when a request is destroyed. Memeber variable because
// MaybeCreateJob is "const", so calling base::Bind in that function does
// not work well.
const DelayableRequest::DestructionCallback on_request_destroyed_callback_;
// Mutable is needed because MaybeCreateJob is const.
mutable std::set<DelayableRequest*> delayed_requests_;
base::Closure delayed_request_destruction_callback_;
};
class DnsProbeBrowserTestIOThreadHelper {
public:
DnsProbeBrowserTestIOThreadHelper();
void SetUpOnIOThread(IOThread* io_thread);
void CleanUpOnIOThreadAndDeleteHelper();
void SetMockDnsClientRules(MockDnsClientRule::Result system_good_result,
MockDnsClientRule::Result public_good_result);
void SetCorrectionServiceNetError(int net_error);
void SetCorrectionServiceDelayRequests(bool delay_requests);
void SetRequestDestructionCallback(const base::Closure& callback);
void StartDelayedProbes(int expected_delayed_probe_count);
private:
IOThread* io_thread_;
DnsProbeService* original_dns_probe_service_;
DelayingDnsProbeService* delaying_dns_probe_service_;
BreakableCorrectionInterceptor* interceptor_;
FilePath mock_corrections_file_path_;
};
DnsProbeBrowserTestIOThreadHelper::DnsProbeBrowserTestIOThreadHelper()
: io_thread_(NULL),
original_dns_probe_service_(NULL),
delaying_dns_probe_service_(NULL),
interceptor_(NULL),
mock_corrections_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(!interceptor_);
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();
interceptor_ =
new BreakableCorrectionInterceptor(mock_corrections_file_path_);
URLRequestFilter::GetInstance()->AddUrlInterceptor(
LinkDoctorBaseURL(), scoped_ptr<URLRequestInterceptor>(interceptor_));
}
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::SetCorrectionServiceNetError(
int net_error) {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
interceptor_->set_net_error(net_error);
}
void DnsProbeBrowserTestIOThreadHelper::SetCorrectionServiceDelayRequests(
bool delay_requests) {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
interceptor_->SetDelayRequests(delay_requests);
}
void DnsProbeBrowserTestIOThreadHelper::SetRequestDestructionCallback(
const base::Closure& callback) {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
interceptor_->SetRequestDestructionCallback(callback);
}
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 ~DnsProbeBrowserTest();
virtual void SetUpOnMainThread() OVERRIDE;
virtual void CleanUpOnMainThread() OVERRIDE;
protected:
// Sets the browser object that other methods apply to, and that has the
// DnsProbeStatus messages of its currently active tab monitored.
void SetActiveBrowser(Browser* browser);
void SetCorrectionServiceBroken(bool broken);
void SetCorrectionServiceDelayRequests(bool delay_requests);
void WaitForDelayedRequestDestruction();
void SetMockDnsClientRules(MockDnsClientRule::Result system_result,
MockDnsClientRule::Result public_result);
// These functions are often used to wait for two navigations because two
// pages are loaded when navigation corrections are enabled: a blank page, so
// the user stops seeing the previous page, and then the error page, either
// with navigation corrections or without them (If the request failed).
void NavigateToDnsError(int num_navigations);
void NavigateToOtherError(int num_navigations);
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);
// Checks that the local error page is being displayed, without navigation
// corrections, and with the specified status text. The status text should be
// either a network error or DNS probe status.
void ExpectDisplayingLocalErrorPage(const std::string& status_text);
// Checks that an error page with mock navigation corrections is being
// displayed, with the specified status text. The status text should be either
// a network error or DNS probe status.
void ExpectDisplayingCorrections(const std::string& status_text);
private:
void OnDnsProbeStatusSent(DnsProbeStatus dns_probe_status);
DnsProbeBrowserTestIOThreadHelper* helper_;
// Browser that methods apply to.
Browser* active_browser_;
// Helper that current has its DnsProbeStatus messages monitored.
NetErrorTabHelper* monitored_tab_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()),
active_browser_(NULL),
monitored_tab_helper_(NULL),
awaiting_dns_probe_status_(false) {
}
DnsProbeBrowserTest::~DnsProbeBrowserTest() {
// No tests should have any unconsumed probe statuses.
EXPECT_EQ(0, pending_status_count());
}
void DnsProbeBrowserTest::SetUpOnMainThread() {
NetErrorTabHelper::set_state_for_testing(
NetErrorTabHelper::TESTING_DEFAULT);
browser()->profile()->GetPrefs()->SetBoolean(
prefs::kAlternateErrorPagesEnabled, true);
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
Bind(&DnsProbeBrowserTestIOThreadHelper::SetUpOnIOThread,
Unretained(helper_),
g_browser_process->io_thread()));
SetActiveBrowser(browser());
}
void DnsProbeBrowserTest::CleanUpOnMainThread() {
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
Bind(&DnsProbeBrowserTestIOThreadHelper::CleanUpOnIOThreadAndDeleteHelper,
Unretained(helper_)));
NetErrorTabHelper::set_state_for_testing(
NetErrorTabHelper::TESTING_DEFAULT);
}
void DnsProbeBrowserTest::SetActiveBrowser(Browser* browser) {
// If currently watching a NetErrorTabHelper, stop doing so before start
// watching another.
if (monitored_tab_helper_) {
monitored_tab_helper_->set_dns_probe_status_snoop_callback_for_testing(
NetErrorTabHelper::DnsProbeStatusSnoopCallback());
}
active_browser_ = browser;
monitored_tab_helper_ = NetErrorTabHelper::FromWebContents(
active_browser_->tab_strip_model()->GetActiveWebContents());
monitored_tab_helper_->set_dns_probe_status_snoop_callback_for_testing(
Bind(&DnsProbeBrowserTest::OnDnsProbeStatusSent, Unretained(this)));
}
void DnsProbeBrowserTest::SetCorrectionServiceBroken(bool broken) {
int net_error = broken ? net::ERR_NAME_NOT_RESOLVED : net::OK;
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
Bind(&DnsProbeBrowserTestIOThreadHelper::SetCorrectionServiceNetError,
Unretained(helper_),
net_error));
}
void DnsProbeBrowserTest::SetCorrectionServiceDelayRequests(
bool delay_requests) {
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
Bind(&DnsProbeBrowserTestIOThreadHelper::
SetCorrectionServiceDelayRequests,
Unretained(helper_),
delay_requests));
}
void DnsProbeBrowserTest::WaitForDelayedRequestDestruction() {
base::RunLoop run_loop;
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
Bind(&DnsProbeBrowserTestIOThreadHelper::SetRequestDestructionCallback,
Unretained(helper_),
base::Bind(&RunClosureOnUIThread,
run_loop.QuitClosure())));
run_loop.Run();
}
void DnsProbeBrowserTest::NavigateToDnsError(int num_navigations) {
NavigateToURLBlockUntilNavigationsComplete(
active_browser_,
URLRequestFailedJob::GetMockHttpUrl(net::ERR_NAME_NOT_RESOLVED),
num_navigations);
}
void DnsProbeBrowserTest::NavigateToOtherError(int num_navigations) {
NavigateToURLBlockUntilNavigationsComplete(
active_browser_,
URLRequestFailedJob::GetMockHttpUrl(net::ERR_CONNECTION_REFUSED),
num_navigations);
}
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 =
active_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(
active_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::ExpectDisplayingLocalErrorPage(
const std::string& status_text) {
EXPECT_FALSE(PageContains("http://correction1/"));
EXPECT_FALSE(PageContains("http://correction2/"));
EXPECT_TRUE(PageContains(status_text));
}
void DnsProbeBrowserTest::ExpectDisplayingCorrections(
const std::string& status_text) {
EXPECT_TRUE(PageContains("http://correction1/"));
EXPECT_TRUE(PageContains("http://correction2/"));
EXPECT_TRUE(PageContains(status_text));
}
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 corrections load.
IN_PROC_BROWSER_TEST_F(DnsProbeBrowserTest, OtherErrorWithCorrectionsSuccess) {
SetCorrectionServiceBroken(false);
NavigateToOtherError(2);
ExpectDisplayingCorrections("ERR_CONNECTION_REFUSED");
}
// Make sure probes don't break non-DNS error pages when corrections failed to
// load.
IN_PROC_BROWSER_TEST_F(DnsProbeBrowserTest, OtherErrorWithCorrectionsFailure) {
SetCorrectionServiceBroken(true);
NavigateToOtherError(2);
ExpectDisplayingLocalErrorPage("ERR_CONNECTION_REFUSED");
}
// Make sure probes don't break DNS error pages when corrections load.
IN_PROC_BROWSER_TEST_F(DnsProbeBrowserTest,
NxdomainProbeResultWithWorkingCorrections) {
SetCorrectionServiceBroken(false);
SetMockDnsClientRules(MockDnsClientRule::OK, MockDnsClientRule::OK);
NavigateToDnsError(2);
ExpectDisplayingCorrections("ERR_NAME_NOT_RESOLVED");
// One status for committing a blank page before the corrections, and one for
// when the error page with corrections is committed.
EXPECT_EQ(chrome_common_net::DNS_PROBE_STARTED, WaitForSentStatus());
EXPECT_EQ(chrome_common_net::DNS_PROBE_STARTED, WaitForSentStatus());
EXPECT_EQ(0, pending_status_count());
ExpectDisplayingCorrections("ERR_NAME_NOT_RESOLVED");
StartDelayedProbes(1);
EXPECT_EQ(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN,
WaitForSentStatus());
EXPECT_EQ(0, pending_status_count());
ExpectDisplayingCorrections("ERR_NAME_NOT_RESOLVED");
}
// Make sure probes don't break corrections when probes complete before the
// corrections load.
IN_PROC_BROWSER_TEST_F(DnsProbeBrowserTest,
NxdomainProbeResultWithWorkingSlowCorrections) {
SetCorrectionServiceBroken(false);
SetCorrectionServiceDelayRequests(true);
SetMockDnsClientRules(MockDnsClientRule::OK, MockDnsClientRule::OK);
NavigateToDnsError(1);
// A blank page should be displayed while the corrections are loaded.
EXPECT_EQ("", Title());
// A single probe should be triggered by the error page load, and it should
// be ignored.
EXPECT_EQ(chrome_common_net::DNS_PROBE_STARTED, WaitForSentStatus());
EXPECT_EQ(0, pending_status_count());
EXPECT_EQ("", Title());
StartDelayedProbes(1);
EXPECT_EQ(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN,
WaitForSentStatus());
EXPECT_EQ(0, pending_status_count());
EXPECT_EQ("", Title());
content::TestNavigationObserver observer(
browser()->tab_strip_model()->GetActiveWebContents(), 1);
// The corrections finish loading.
SetCorrectionServiceDelayRequests(false);
// Wait for it to commit.
observer.Wait();
ExpectDisplayingCorrections("ERR_NAME_NOT_RESOLVED");
// Committing the corections page should trigger sending the probe result
// again.
EXPECT_EQ(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN,
WaitForSentStatus());
ExpectDisplayingCorrections("ERR_NAME_NOT_RESOLVED");
}
// Make sure probes update DNS error page properly when they're supposed to.
IN_PROC_BROWSER_TEST_F(DnsProbeBrowserTest,
NoInternetProbeResultWithBrokenCorrections) {
SetCorrectionServiceBroken(true);
SetMockDnsClientRules(MockDnsClientRule::TIMEOUT,
MockDnsClientRule::TIMEOUT);
NavigateToDnsError(2);
EXPECT_EQ(chrome_common_net::DNS_PROBE_STARTED, WaitForSentStatus());
EXPECT_EQ(chrome_common_net::DNS_PROBE_STARTED, WaitForSentStatus());
// Checking the page runs the RunLoop, so make sure nothing hairy happens.
EXPECT_EQ(0, pending_status_count());
ExpectDisplayingLocalErrorPage("DNS_PROBE_STARTED");
EXPECT_EQ(0, pending_status_count());
StartDelayedProbes(1);
EXPECT_EQ(chrome_common_net::DNS_PROBE_FINISHED_NO_INTERNET,
WaitForSentStatus());
// Checking the page runs the RunLoop, so make sure nothing hairy happens.
EXPECT_EQ(0, pending_status_count());
ExpectDisplayingLocalErrorPage("DNS_PROBE_FINISHED_NO_INTERNET");
}
// Make sure probes don't break corrections when probes complete before the
// corrections request returns an error.
IN_PROC_BROWSER_TEST_F(DnsProbeBrowserTest,
NoInternetProbeResultWithSlowBrokenCorrections) {
SetCorrectionServiceBroken(true);
SetCorrectionServiceDelayRequests(true);
SetMockDnsClientRules(MockDnsClientRule::TIMEOUT,
MockDnsClientRule::TIMEOUT);
NavigateToDnsError(1);
// A blank page should be displayed while the corrections load.
EXPECT_EQ("", Title());
// A single probe should be triggered by the error page load, and it should
// be ignored.
EXPECT_EQ(chrome_common_net::DNS_PROBE_STARTED, WaitForSentStatus());
EXPECT_EQ(0, pending_status_count());
EXPECT_EQ("", Title());
StartDelayedProbes(1);
EXPECT_EQ(chrome_common_net::DNS_PROBE_FINISHED_NO_INTERNET,
WaitForSentStatus());
EXPECT_EQ("", Title());
EXPECT_EQ(0, pending_status_count());
content::TestNavigationObserver observer(
browser()->tab_strip_model()->GetActiveWebContents(), 1);
// The corrections request fails.
SetCorrectionServiceDelayRequests(false);
// Wait for the DNS error page to load instead.
observer.Wait();
// The page committing should result in sending the probe results again.
EXPECT_EQ(chrome_common_net::DNS_PROBE_FINISHED_NO_INTERNET,
WaitForSentStatus());
EXPECT_EQ(0, pending_status_count());
ExpectDisplayingLocalErrorPage("DNS_PROBE_FINISHED_NO_INTERNET");
}
// Double-check to make sure sync failures don't explode.
IN_PROC_BROWSER_TEST_F(DnsProbeBrowserTest, SyncFailureWithBrokenCorrections) {
SetCorrectionServiceBroken(true);
SetMockDnsClientRules(MockDnsClientRule::FAIL, MockDnsClientRule::FAIL);
NavigateToDnsError(2);
EXPECT_EQ(chrome_common_net::DNS_PROBE_STARTED, WaitForSentStatus());
EXPECT_EQ(chrome_common_net::DNS_PROBE_STARTED, WaitForSentStatus());
// Checking the page runs the RunLoop, so make sure nothing hairy happens.
EXPECT_EQ(0, pending_status_count());
ExpectDisplayingLocalErrorPage("DNS_PROBE_STARTED");
EXPECT_EQ(0, pending_status_count());
StartDelayedProbes(1);
EXPECT_EQ(chrome_common_net::DNS_PROBE_FINISHED_INCONCLUSIVE,
WaitForSentStatus());
// Checking the page runs the RunLoop, so make sure nothing hairy happens.
EXPECT_EQ(0, pending_status_count());
ExpectDisplayingLocalErrorPage("ERR_NAME_NOT_RESOLVED");
EXPECT_EQ(0, pending_status_count());
}
// Test that pressing the stop button cancels loading corrections.
// TODO(mmenke): Add a test for the cross process navigation case.
// TODO(mmenke): This test could flakily pass due to the timeout on downloading
// the corrections. Disable that timeout for browser tests.
IN_PROC_BROWSER_TEST_F(DnsProbeBrowserTest, CorrectionsLoadStopped) {
SetCorrectionServiceDelayRequests(true);
SetCorrectionServiceBroken(true);
SetMockDnsClientRules(MockDnsClientRule::TIMEOUT, MockDnsClientRule::TIMEOUT);
NavigateToDnsError(1);
EXPECT_EQ(chrome_common_net::DNS_PROBE_STARTED, WaitForSentStatus());
StartDelayedProbes(1);
EXPECT_EQ(chrome_common_net::DNS_PROBE_FINISHED_NO_INTERNET,
WaitForSentStatus());
EXPECT_EQ("", Title());
EXPECT_EQ(0, pending_status_count());
chrome::Stop(browser());
WaitForDelayedRequestDestruction();
// End up displaying a blank page.
EXPECT_EQ("", Title());
}
// Test that pressing the stop button cancels the load of corrections, and
// receiving a probe result afterwards does not swap in a DNS error page.
IN_PROC_BROWSER_TEST_F(DnsProbeBrowserTest, CorrectionsLoadStoppedSlowProbe) {
SetCorrectionServiceDelayRequests(true);
SetCorrectionServiceBroken(true);
SetMockDnsClientRules(MockDnsClientRule::TIMEOUT, MockDnsClientRule::TIMEOUT);
NavigateToDnsError(1);
EXPECT_EQ(chrome_common_net::DNS_PROBE_STARTED, WaitForSentStatus());
EXPECT_EQ("", Title());
EXPECT_EQ(0, pending_status_count());
chrome::Stop(browser());
WaitForDelayedRequestDestruction();
EXPECT_EQ("", Title());
EXPECT_EQ(0, pending_status_count());
StartDelayedProbes(1);
EXPECT_EQ(chrome_common_net::DNS_PROBE_FINISHED_NO_INTERNET,
WaitForSentStatus());
EXPECT_EQ("", Title());
}
// Make sure probes don't run for subframe DNS errors.
IN_PROC_BROWSER_TEST_F(DnsProbeBrowserTest, NoProbeInSubframe) {
SetCorrectionServiceBroken(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) {
// Disable probes (And corrections).
browser()->profile()->GetPrefs()->SetBoolean(
prefs::kAlternateErrorPagesEnabled, false);
SetCorrectionServiceBroken(true);
SetMockDnsClientRules(MockDnsClientRule::TIMEOUT, MockDnsClientRule::TIMEOUT);
NavigateToDnsError(1);
EXPECT_EQ(chrome_common_net::DNS_PROBE_NOT_RUN, WaitForSentStatus());
// Checking the page runs the RunLoop, so make sure nothing hairy happens.
EXPECT_EQ(0, pending_status_count());
ExpectDisplayingLocalErrorPage("ERR_NAME_NOT_RESOLVED");
}
// Test the case that corrections are disabled, but DNS probes are enabled.
// This is the case with Chromium builds.
IN_PROC_BROWSER_TEST_F(DnsProbeBrowserTest, CorrectionsDisabled) {
// Disable corrections.
browser()->profile()->GetPrefs()->SetBoolean(
prefs::kAlternateErrorPagesEnabled, false);
// Requests to the correction service should work if any are made, so the test
// fails if that happens unexpectedly.
SetCorrectionServiceBroken(false);
// Normally disabling corrections disables DNS probes, so force DNS probes
// to be enabled.
NetErrorTabHelper::set_state_for_testing(
NetErrorTabHelper::TESTING_FORCE_ENABLED);
SetMockDnsClientRules(MockDnsClientRule::FAIL, MockDnsClientRule::FAIL);
// Just one commit and one sent status, since corrections are disabled.
NavigateToDnsError(1);
EXPECT_EQ(chrome_common_net::DNS_PROBE_STARTED, WaitForSentStatus());
// Checking the page runs the RunLoop, so make sure nothing hairy happens.
EXPECT_EQ(0, pending_status_count());
ExpectDisplayingLocalErrorPage("DNS_PROBE_STARTED");
EXPECT_EQ(0, pending_status_count());
StartDelayedProbes(1);
EXPECT_EQ(chrome_common_net::DNS_PROBE_FINISHED_INCONCLUSIVE,
WaitForSentStatus());
EXPECT_EQ(0, pending_status_count());
ExpectDisplayingLocalErrorPage("ERR_NAME_NOT_RESOLVED");
}
// Test incognito mode. Corrections should be disabled, but DNS probes are
// still enabled.
IN_PROC_BROWSER_TEST_F(DnsProbeBrowserTest, Incognito) {
// Requests to the correction service should work if any are made, so the test
// will fail if one is requested unexpectedly.
SetCorrectionServiceBroken(false);
Browser* incognito = CreateIncognitoBrowser();
SetActiveBrowser(incognito);
SetMockDnsClientRules(MockDnsClientRule::FAIL, MockDnsClientRule::FAIL);
// Just one commit and one sent status, since the corrections are disabled.
NavigateToDnsError(1);
EXPECT_EQ(chrome_common_net::DNS_PROBE_STARTED, WaitForSentStatus());
// Checking the page runs the RunLoop, so make sure nothing hairy happens.
EXPECT_EQ(0, pending_status_count());
ExpectDisplayingLocalErrorPage("DNS_PROBE_STARTED");
EXPECT_EQ(0, pending_status_count());
StartDelayedProbes(1);
EXPECT_EQ(chrome_common_net::DNS_PROBE_FINISHED_INCONCLUSIVE,
WaitForSentStatus());
EXPECT_EQ(0, pending_status_count());
ExpectDisplayingLocalErrorPage("ERR_NAME_NOT_RESOLVED");
}
} // namespace
} // namespace chrome_browser_net