blob: 0a5d1f354b44fcf72fbc997ede7bfa7409d8861c [file] [log] [blame]
// Copyright 2014 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 <stdint.h>
#include "base/command_line.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/common/site_isolation_policy.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/url_constants.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/shell/browser/shell.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/url_request/url_request_failed_job.h"
#include "url/gurl.h"
namespace content {
class BrowserSideNavigationBrowserTest : public ContentBrowserTest {
public:
BrowserSideNavigationBrowserTest() {}
protected:
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitch(switches::kEnableBrowserSideNavigation);
}
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
ASSERT_TRUE(embedded_test_server()->Start());
}
};
// Ensure that browser initiated basic navigations work with browser side
// navigation.
IN_PROC_BROWSER_TEST_F(BrowserSideNavigationBrowserTest,
BrowserInitiatedNavigations) {
// Perform a navigation with no live renderer.
{
TestNavigationObserver observer(shell()->web_contents());
GURL url(embedded_test_server()->GetURL("/title1.html"));
NavigateToURL(shell(), url);
EXPECT_EQ(url, observer.last_navigation_url());
EXPECT_TRUE(observer.last_navigation_succeeded());
}
RenderFrameHost* initial_rfh =
static_cast<WebContentsImpl*>(shell()->web_contents())
->GetFrameTree()->root()->current_frame_host();
// Perform a same site navigation.
{
TestNavigationObserver observer(shell()->web_contents());
GURL url(embedded_test_server()->GetURL("/title2.html"));
NavigateToURL(shell(), url);
EXPECT_EQ(url, observer.last_navigation_url());
EXPECT_TRUE(observer.last_navigation_succeeded());
}
// The RenderFrameHost should not have changed.
EXPECT_EQ(initial_rfh, static_cast<WebContentsImpl*>(shell()->web_contents())
->GetFrameTree()->root()->current_frame_host());
// Perform a cross-site navigation.
{
TestNavigationObserver observer(shell()->web_contents());
GURL url = embedded_test_server()->GetURL("foo.com", "/title3.html");
NavigateToURL(shell(), url);
EXPECT_EQ(url, observer.last_navigation_url());
EXPECT_TRUE(observer.last_navigation_succeeded());
}
// The RenderFrameHost should have changed.
EXPECT_NE(initial_rfh, static_cast<WebContentsImpl*>(shell()->web_contents())
->GetFrameTree()->root()->current_frame_host());
}
// Ensure that renderer initiated same-site navigations work with browser side
// navigation.
IN_PROC_BROWSER_TEST_F(BrowserSideNavigationBrowserTest,
RendererInitiatedSameSiteNavigation) {
// Perform a navigation with no live renderer.
{
TestNavigationObserver observer(shell()->web_contents());
GURL url(embedded_test_server()->GetURL("/simple_links.html"));
NavigateToURL(shell(), url);
EXPECT_EQ(url, observer.last_navigation_url());
EXPECT_TRUE(observer.last_navigation_succeeded());
}
RenderFrameHost* initial_rfh =
static_cast<WebContentsImpl*>(shell()->web_contents())
->GetFrameTree()->root()->current_frame_host();
// Simulate clicking on a same-site link.
{
TestNavigationObserver observer(shell()->web_contents());
GURL url(embedded_test_server()->GetURL("/title2.html"));
bool success = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
shell(), "window.domAutomationController.send(clickSameSiteLink());",
&success));
EXPECT_TRUE(success);
EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
EXPECT_EQ(url, observer.last_navigation_url());
EXPECT_TRUE(observer.last_navigation_succeeded());
}
// The RenderFrameHost should not have changed.
EXPECT_EQ(initial_rfh, static_cast<WebContentsImpl*>(shell()->web_contents())
->GetFrameTree()->root()->current_frame_host());
}
// Ensure that renderer initiated cross-site navigations work with browser side
// navigation.
IN_PROC_BROWSER_TEST_F(BrowserSideNavigationBrowserTest,
RendererInitiatedCrossSiteNavigation) {
// Perform a navigation with no live renderer.
{
TestNavigationObserver observer(shell()->web_contents());
GURL url(embedded_test_server()->GetURL("/simple_links.html"));
NavigateToURL(shell(), url);
EXPECT_EQ(url, observer.last_navigation_url());
EXPECT_TRUE(observer.last_navigation_succeeded());
}
RenderFrameHost* initial_rfh =
static_cast<WebContentsImpl*>(shell()->web_contents())
->GetFrameTree()->root()->current_frame_host();
// Simulate clicking on a cross-site link.
{
TestNavigationObserver observer(shell()->web_contents());
const char kReplacePortNumber[] =
"window.domAutomationController.send(setPortNumber(%d));";
uint16_t port_number = embedded_test_server()->port();
GURL url = embedded_test_server()->GetURL("foo.com", "/title2.html");
bool success = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
shell(), base::StringPrintf(kReplacePortNumber, port_number),
&success));
success = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
shell(), "window.domAutomationController.send(clickCrossSiteLink());",
&success));
EXPECT_TRUE(success);
EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
EXPECT_EQ(url, observer.last_navigation_url());
EXPECT_TRUE(observer.last_navigation_succeeded());
}
// The RenderFrameHost should not have changed unless site-per-process is
// enabled.
if (AreAllSitesIsolatedForTesting()) {
EXPECT_NE(initial_rfh,
static_cast<WebContentsImpl*>(shell()->web_contents())
->GetFrameTree()
->root()
->current_frame_host());
} else {
EXPECT_EQ(initial_rfh,
static_cast<WebContentsImpl*>(shell()->web_contents())
->GetFrameTree()
->root()
->current_frame_host());
}
}
// Ensure that browser side navigation handles navigation failures.
IN_PROC_BROWSER_TEST_F(BrowserSideNavigationBrowserTest, FailedNavigation) {
// Perform a navigation with no live renderer.
{
TestNavigationObserver observer(shell()->web_contents());
GURL url(embedded_test_server()->GetURL("/title1.html"));
NavigateToURL(shell(), url);
EXPECT_EQ(url, observer.last_navigation_url());
EXPECT_TRUE(observer.last_navigation_succeeded());
}
// Now navigate to an unreachable url.
{
TestNavigationObserver observer(shell()->web_contents());
GURL error_url(
net::URLRequestFailedJob::GetMockHttpUrl(net::ERR_CONNECTION_RESET));
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&net::URLRequestFailedJob::AddUrlHandler));
NavigateToURL(shell(), error_url);
EXPECT_EQ(error_url, observer.last_navigation_url());
NavigationEntry* entry =
shell()->web_contents()->GetController().GetLastCommittedEntry();
EXPECT_EQ(PAGE_TYPE_ERROR, entry->GetPageType());
}
}
// Ensure that browser side navigation handles POST navigations correctly.
IN_PROC_BROWSER_TEST_F(BrowserSideNavigationBrowserTest, POSTNavigation) {
GURL url(embedded_test_server()->GetURL("/session_history/form.html"));
GURL post_url = embedded_test_server()->GetURL("/echotitle");
// Navigate to a page with a form.
TestNavigationObserver observer(shell()->web_contents());
NavigateToURL(shell(), url);
EXPECT_EQ(url, observer.last_navigation_url());
EXPECT_TRUE(observer.last_navigation_succeeded());
// Submit the form.
GURL submit_url("javascript:submitForm('isubmit')");
NavigateToURL(shell(), submit_url);
// Check that a proper POST navigation was done.
EXPECT_EQ("text=&select=a",
base::UTF16ToASCII(shell()->web_contents()->GetTitle()));
EXPECT_EQ(post_url, shell()->web_contents()->GetLastCommittedURL());
EXPECT_TRUE(shell()
->web_contents()
->GetController()
.GetActiveEntry()
->GetHasPostData());
}
// Ensure that browser side navigation can load browser initiated navigations
// to view-source URLs.
IN_PROC_BROWSER_TEST_F(BrowserSideNavigationBrowserTest,
ViewSourceNavigation_BrowserInitiated) {
TestNavigationObserver observer(shell()->web_contents());
GURL url(embedded_test_server()->GetURL("/title1.html"));
GURL view_source_url(content::kViewSourceScheme + std::string(":") +
url.spec());
NavigateToURL(shell(), view_source_url);
EXPECT_EQ(url, observer.last_navigation_url());
EXPECT_TRUE(observer.last_navigation_succeeded());
}
// Ensure that browser side navigation blocks content initiated navigations to
// view-source URLs.
IN_PROC_BROWSER_TEST_F(BrowserSideNavigationBrowserTest,
ViewSourceNavigation_RendererInitiated) {
TestNavigationObserver observer(shell()->web_contents());
GURL kUrl(embedded_test_server()->GetURL("/simple_links.html"));
NavigateToURL(shell(), kUrl);
EXPECT_EQ(kUrl, observer.last_navigation_url());
EXPECT_TRUE(observer.last_navigation_succeeded());
std::unique_ptr<ConsoleObserverDelegate> console_delegate(
new ConsoleObserverDelegate(
shell()->web_contents(),
"Not allowed to load local resource: view-source:about:blank"));
shell()->web_contents()->SetDelegate(console_delegate.get());
bool success = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
shell()->web_contents(),
"window.domAutomationController.send(clickViewSourceLink());", &success));
EXPECT_TRUE(success);
console_delegate->Wait();
// Original page shouldn't navigate away.
EXPECT_EQ(kUrl, shell()->web_contents()->GetURL());
EXPECT_FALSE(shell()
->web_contents()
->GetController()
.GetLastCommittedEntry()
->IsViewSourceMode());
}
// Ensure that closing a page by running its beforeunload handler doesn't hang
// if there's an ongoing navigation.
IN_PROC_BROWSER_TEST_F(BrowserSideNavigationBrowserTest,
UnloadDuringNavigation) {
content::WindowedNotificationObserver close_observer(
content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
content::Source<content::WebContents>(shell()->web_contents()));
shell()->LoadURL(GURL("chrome://resources/css/tabs.css"));
shell()->web_contents()->DispatchBeforeUnload();
close_observer.Wait();
}
} // namespace content