blob: f05bdff58cd044e55643a088b6ed65ef3a4fa1cc [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// 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/memory/raw_ptr.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/net/net_error_diagnostics_dialog.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/fenced_frame_test_util.h"
#include "content/public/test/prerender_test_util.h"
#include "content/public/test/url_loader_interceptor.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/url_request/url_request_failed_job.h"
namespace content {
namespace {
constexpr char kSearchingForDiagnosisScript[] = R"JS(
const kPattern = '<a href="javascript:diagnoseErrors()"';
function check() {
console.log(document.body.innerHTML);
if (document.body.innerHTML.indexOf(kPattern) === -1) {
return 'NOT FOUND';
} else {
return 'FOUND';
}
}
new Promise((resolve) => {
if (document.readyState === 'complete') {
resolve(check());
} else {
document.addEventListener('load', () => {
resolve(check());
});
}
})
)JS";
} // namespace
class NetErrorTabHelperTest : public InProcessBrowserTest {
public:
NetErrorTabHelperTest() = default;
~NetErrorTabHelperTest() override = default;
NetErrorTabHelperTest(const NetErrorTabHelperTest&) = delete;
NetErrorTabHelperTest& operator=(const NetErrorTabHelperTest&) = delete;
void SetUp() override { InProcessBrowserTest::SetUp(); }
void SetUpOnMainThread() override {
chrome_browser_net::NetErrorTabHelper::set_state_for_testing(
chrome_browser_net::NetErrorTabHelper::TESTING_DEFAULT);
host_resolver()->AddRule("mock.http", "127.0.0.1");
ASSERT_TRUE(embedded_test_server()->Start());
tab_helper_ = chrome_browser_net::NetErrorTabHelper::FromWebContents(
browser()->tab_strip_model()->GetActiveWebContents());
}
WebContents* GetWebContents() {
return browser()->tab_strip_model()->GetActiveWebContents();
}
chrome_browser_net::NetErrorTabHelper& tab_helper() const {
return *tab_helper_;
}
private:
raw_ptr<chrome_browser_net::NetErrorTabHelper, AcrossTasksDanglingUntriaged>
tab_helper_ = nullptr;
};
class NetErrorTabHelperWithPrerenderingTest : public NetErrorTabHelperTest {
public:
NetErrorTabHelperWithPrerenderingTest()
: prerender_helper_(base::BindRepeating(
&NetErrorTabHelperWithPrerenderingTest::GetWebContents,
base::Unretained(this))) {}
~NetErrorTabHelperWithPrerenderingTest() override = default;
NetErrorTabHelperWithPrerenderingTest(
const NetErrorTabHelperWithPrerenderingTest&) = delete;
NetErrorTabHelperWithPrerenderingTest& operator=(
const NetErrorTabHelperWithPrerenderingTest&) = delete;
void SetUp() override {
prerender_helper_.RegisterServerRequestMonitor(embedded_test_server());
NetErrorTabHelperTest::SetUp();
}
void SetUpOnMainThread() override {
NetErrorTabHelperTest::SetUpOnMainThread();
tab_helper().set_dns_probe_status_snoop_callback_for_testing(
base::BindRepeating(
&NetErrorTabHelperWithPrerenderingTest::OnDnsProbeStatusSent,
base::Unretained(this)));
url_loader_interceptor_ =
std::make_unique<URLLoaderInterceptor>(base::BindRepeating(
[](URLLoaderInterceptor::RequestParams* params) { return false; }));
}
void TearDownOnMainThread() override { url_loader_interceptor_.reset(); }
test::PrerenderTestHelper& prerender_helper() { return prerender_helper_; }
void OnDnsProbeStatusSent(error_page::DnsProbeStatus dns_probe_status) {
dns_probe_status_queue_.push_back(dns_probe_status);
}
void ClearProbeStatusQueue() { dns_probe_status_queue_.clear(); }
int pending_probe_status_count() const {
return dns_probe_status_queue_.size();
}
private:
test::PrerenderTestHelper prerender_helper_;
std::unique_ptr<URLLoaderInterceptor> url_loader_interceptor_;
std::list<error_page::DnsProbeStatus> dns_probe_status_queue_;
};
// TODO(crbug.com/40786063): Enable this test on macOS after the issue is fixed.
#if BUILDFLAG(IS_MAC)
#define MAYBE_ErrorPagesDoNotPrerenderOrTriggerDnsProbeStatuses \
DISABLED_ErrorPagesDoNotPrerenderOrTriggerDnsProbeStatuses
#else
#define MAYBE_ErrorPagesDoNotPrerenderOrTriggerDnsProbeStatuses \
ErrorPagesDoNotPrerenderOrTriggerDnsProbeStatuses
#endif
IN_PROC_BROWSER_TEST_F(
NetErrorTabHelperWithPrerenderingTest,
MAYBE_ErrorPagesDoNotPrerenderOrTriggerDnsProbeStatuses) {
GURL initial_url = embedded_test_server()->GetURL("/title2.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url));
EXPECT_FALSE(pending_probe_status_count());
test::PrerenderHostRegistryObserver registry_observer(*GetWebContents());
GURL error_page_url =
net::URLRequestFailedJob::GetMockHttpUrl(net::ERR_NAME_NOT_RESOLVED);
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), error_page_url));
EXPECT_TRUE(pending_probe_status_count());
ClearProbeStatusQueue();
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url));
EXPECT_FALSE(pending_probe_status_count());
GURL prerender_url = embedded_test_server()->GetURL("/title1.html");
// Adding two pages and making sure that only one of them gets pre-rendered.
prerender_helper().AddPrerenderAsync(error_page_url);
prerender_helper().AddPrerenderAsync(prerender_url);
registry_observer.WaitForTrigger(prerender_url);
EXPECT_FALSE(prerender_helper().GetHostForUrl(error_page_url));
EXPECT_TRUE(prerender_helper().GetHostForUrl(prerender_url));
EXPECT_FALSE(pending_probe_status_count());
}
IN_PROC_BROWSER_TEST_F(NetErrorTabHelperWithPrerenderingTest,
ShowErrorPagesInPrerender) {
GURL initial_url =
net::URLRequestFailedJob::GetMockHttpUrl(net::ERR_NAME_NOT_RESOLVED);
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url));
// Overrides the last committed origin to treat the network error as the same
// url with the non-opaque origins.
content::OverrideLastCommittedOrigin(GetWebContents()->GetPrimaryMainFrame(),
url::Origin::Create(initial_url));
GURL prerender_url =
net::URLRequestFailedJob::GetMockHttpUrl(net::ERR_NAME_NOT_RESOLVED);
test::PrerenderHostRegistryObserver registry_observer(*GetWebContents());
// Start prerendering `prerender_url`.
prerender_helper().AddPrerenderAsync(prerender_url);
registry_observer.WaitForTrigger(prerender_url);
content::FrameTreeNodeId host_id =
prerender_helper().GetHostForUrl(prerender_url);
EXPECT_TRUE(host_id);
test::PrerenderHostObserver host_observer(*GetWebContents(), host_id);
// PrerenderHost is destroyed by net::ERR_NAME_NOT_RESOLVED and it stops
// prerendering.
host_observer.WaitForDestroyed();
// The prerender host should be destroyed.
host_id = prerender_helper().GetHostForUrl(prerender_url);
EXPECT_FALSE(host_id);
}
class NetErrorTabHelperWithFencedFrameTest : public NetErrorTabHelperTest {
public:
NetErrorTabHelperWithFencedFrameTest() = default;
~NetErrorTabHelperWithFencedFrameTest() override = default;
NetErrorTabHelperWithFencedFrameTest(
const NetErrorTabHelperWithFencedFrameTest&) = delete;
NetErrorTabHelperWithFencedFrameTest& operator=(
const NetErrorTabHelperWithFencedFrameTest&) = delete;
test::FencedFrameTestHelper& fenced_frame_test_helper() {
return fenced_frame_test_helper_;
}
// Returns true if the platform has support for a diagnostics tool, and it
// can be launched from |render_frame_host|.
std::string WebContentsCanShowDiagnosticsTool(
RenderFrameHost* render_frame_host) {
auto* web_contents = WebContents::FromRenderFrameHost(render_frame_host);
return CanShowNetworkDiagnosticsDialog(web_contents) ? "FOUND"
: "NOT FOUND";
}
private:
test::FencedFrameTestHelper fenced_frame_test_helper_;
};
IN_PROC_BROWSER_TEST_F(NetErrorTabHelperWithFencedFrameTest,
CanRunDiagnosticsDialogOnMainFrame) {
GURL initial_url =
net::URLRequestFailedJob::GetMockHttpUrl(net::ERR_NAME_NOT_RESOLVED);
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url));
EvalJsResult result = EvalJs(GetWebContents()->GetPrimaryMainFrame(),
kSearchingForDiagnosisScript);
ASSERT_TRUE(result.is_ok());
EXPECT_EQ(WebContentsCanShowDiagnosticsTool(
GetWebContents()->GetPrimaryMainFrame()),
result.ExtractString());
}
IN_PROC_BROWSER_TEST_F(NetErrorTabHelperWithFencedFrameTest,
CanRunDiagnosticsDialogOnFencedFrame) {
GURL fenced_frame_url =
net::URLRequestFailedJob::GetMockHttpUrl(net::ERR_NAME_NOT_RESOLVED);
RenderFrameHost* inner_fenced_frame_rfh =
fenced_frame_test_helper().CreateFencedFrame(
GetWebContents()->GetPrimaryMainFrame(), fenced_frame_url,
net::ERR_NAME_NOT_RESOLVED);
EvalJsResult result =
EvalJs(inner_fenced_frame_rfh, kSearchingForDiagnosisScript);
ASSERT_TRUE(result.is_ok());
#if BUILDFLAG(IS_CHROMEOS)
// ChromeOS has its own diagnostics extension, which doesn't rely on a
// browser-initiated dialog.
EXPECT_EQ("FOUND", result.ExtractString());
#else
EXPECT_EQ("NOT FOUND", result.ExtractString());
#endif
}
} // namespace content