blob: f3e779ac29f511988da4ecabb83fb23aebd74f6e [file] [log] [blame]
// Copyright 2018 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/extensions/bookmark_app_navigation_browsertest.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/profiles/profile_io_data.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/web_application_info.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_navigation_observer.h"
#include "net/base/escape.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
namespace {
const char kLaunchingPageHost[] = "launching-page.com";
const char kLaunchingPagePath[] = "/index.html";
const char kAppUrlHost[] = "app.com";
const char kOtherAppUrlHost[] = "other-app.com";
const char kAppScopePath[] = "/in_scope/";
const char kAppUrlPath[] = "/in_scope/index.html";
const char kInScopeUrlPath[] = "/in_scope/other.html";
const char kOutOfScopeUrlPath[] = "/out_of_scope/index.html";
const char kAppName[] = "Test app";
bool HasOpenedWindowAndOpener(content::WebContents* opener_contents,
content::WebContents* opened_contents) {
bool has_opener;
CHECK(content::ExecuteScriptAndExtractBool(
opened_contents, "window.domAutomationController.send(!!window.opener);",
&has_opener));
bool has_openedWindow;
CHECK(content::ExecuteScriptAndExtractBool(
opener_contents,
"window.domAutomationController.send(!!window.openedWindow.window)",
&has_openedWindow));
return has_opener && has_openedWindow;
}
// Wrapper so that we can use base::BindOnce with NavigateToURL.
void NavigateToURLWrapper(NavigateParams* params) {
ui_test_utils::NavigateToURL(params);
}
} // anonymous namespace
namespace extensions {
namespace test {
// static
const char* BookmarkAppNavigationBrowserTest::GetLaunchingPageHost() {
return kLaunchingPageHost;
}
// static
const char* BookmarkAppNavigationBrowserTest::GetLaunchingPagePath() {
return kLaunchingPagePath;
}
// static
const char* BookmarkAppNavigationBrowserTest::GetAppUrlHost() {
return kAppUrlHost;
}
// static
const char* BookmarkAppNavigationBrowserTest::GetOtherAppUrlHost() {
return kOtherAppUrlHost;
}
// static
const char* BookmarkAppNavigationBrowserTest::GetAppScopePath() {
return kAppScopePath;
}
// static
const char* BookmarkAppNavigationBrowserTest::GetAppUrlPath() {
return kAppUrlPath;
}
// static
const char* BookmarkAppNavigationBrowserTest::GetInScopeUrlPath() {
return kInScopeUrlPath;
}
// static
const char* BookmarkAppNavigationBrowserTest::GetOutOfScopeUrlPath() {
return kOutOfScopeUrlPath;
}
// static
const char* BookmarkAppNavigationBrowserTest::GetAppName() {
return kAppName;
}
// static
std::string BookmarkAppNavigationBrowserTest::CreateServerRedirect(
const GURL& target_url) {
const char* const kServerRedirectBase = "/server-redirect?";
return kServerRedirectBase +
net::EscapeQueryParamValue(target_url.spec(), false);
}
// static
std::unique_ptr<content::TestNavigationObserver>
BookmarkAppNavigationBrowserTest::GetTestNavigationObserver(
const GURL& target_url) {
auto observer = std::make_unique<content::TestNavigationObserver>(target_url);
observer->WatchExistingWebContents();
observer->StartWatchingNewWebContents();
return observer;
}
// static
content::RenderFrameHost* BookmarkAppNavigationBrowserTest::GetIFrame(
content::WebContents* web_contents) {
const auto all_frames = web_contents->GetAllFrames();
const content::RenderFrameHost* main_frame = web_contents->GetMainFrame();
DCHECK_EQ(2u, all_frames.size());
auto it = std::find_if(all_frames.begin(), all_frames.end(),
[main_frame](content::RenderFrameHost* frame) {
return main_frame != frame;
});
DCHECK(it != all_frames.end());
return *it;
}
// static
void BookmarkAppNavigationBrowserTest::ClickLinkWithModifiersAndWaitForURL(
content::WebContents* web_contents,
const GURL& link_url,
const GURL& target_url,
BookmarkAppNavigationBrowserTest::LinkTarget target,
const std::string& rel,
int modifiers) {
auto observer = GetTestNavigationObserver(target_url);
std::string script = base::StringPrintf(
"(() => {"
"const link = document.createElement('a');"
"link.href = '%s';"
"link.target = '%s';"
"link.rel = '%s';"
// Make a click target that covers the whole viewport.
"const click_target = document.createElement('textarea');"
"click_target.position = 'absolute';"
"click_target.top = 0;"
"click_target.left = 0;"
"click_target.style.height = '100vh';"
"click_target.style.width = '100vw';"
"link.appendChild(click_target);"
"document.body.appendChild(link);"
"})();",
link_url.spec().c_str(), target == LinkTarget::SELF ? "_self" : "_blank",
rel.c_str());
ASSERT_TRUE(content::ExecuteScript(web_contents, script));
content::SimulateMouseClick(web_contents, modifiers,
blink::WebMouseEvent::Button::kLeft);
observer->Wait();
}
// static
void BookmarkAppNavigationBrowserTest::ClickLinkAndWaitForURL(
content::WebContents* web_contents,
const GURL& link_url,
const GURL& target_url,
BookmarkAppNavigationBrowserTest::LinkTarget target,
const std::string& rel) {
ClickLinkWithModifiersAndWaitForURL(
web_contents, link_url, target_url, target, rel,
blink::WebInputEvent::Modifiers::kNoModifiers);
}
// static
void BookmarkAppNavigationBrowserTest::ClickLinkAndWait(
content::WebContents* web_contents,
const GURL& link_url,
BookmarkAppNavigationBrowserTest::LinkTarget target,
const std::string& rel) {
ClickLinkAndWaitForURL(web_contents, link_url, link_url, target, rel);
}
// static
void BookmarkAppNavigationBrowserTest::ClickLinkWithModifiersAndWait(
content::WebContents* web_contents,
const GURL& link_url,
BookmarkAppNavigationBrowserTest::LinkTarget target,
const std::string& rel,
int modifiers) {
ClickLinkWithModifiersAndWaitForURL(web_contents, link_url, link_url, target,
rel, modifiers);
}
BookmarkAppNavigationBrowserTest::BookmarkAppNavigationBrowserTest()
: https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
BookmarkAppNavigationBrowserTest::~BookmarkAppNavigationBrowserTest() = default;
void BookmarkAppNavigationBrowserTest::SetUp() {
https_server_.AddDefaultHandlers(GetChromeTestDataDir());
// Register a request handler that will return empty pages. Tests are
// responsible for adding elements and firing events on these empty pages.
https_server_.RegisterRequestHandler(
base::BindRepeating([](const net::test_server::HttpRequest& request) {
// Let the default request handlers handle redirections.
if (request.GetURL().path() == "/server-redirect" ||
request.GetURL().path() == "/client-redirect") {
return std::unique_ptr<net::test_server::HttpResponse>();
}
auto response = std::make_unique<net::test_server::BasicHttpResponse>();
response->set_content_type("text/html");
response->AddCustomHeader("Access-Control-Allow-Origin", "*");
return static_cast<std::unique_ptr<net::test_server::HttpResponse>>(
std::move(response));
}));
ExtensionBrowserTest::SetUp();
}
void BookmarkAppNavigationBrowserTest::SetUpInProcessBrowserTestFixture() {
ExtensionBrowserTest::SetUpInProcessBrowserTestFixture();
cert_verifier_.SetUpInProcessBrowserTestFixture();
}
void BookmarkAppNavigationBrowserTest::TearDownInProcessBrowserTestFixture() {
ExtensionBrowserTest::TearDownInProcessBrowserTestFixture();
cert_verifier_.TearDownInProcessBrowserTestFixture();
}
void BookmarkAppNavigationBrowserTest::SetUpCommandLine(
base::CommandLine* command_line) {
cert_verifier_.SetUpCommandLine(command_line);
}
void BookmarkAppNavigationBrowserTest::SetUpOnMainThread() {
ExtensionBrowserTest::SetUpOnMainThread();
host_resolver()->AddRule("*", "127.0.0.1");
// By default, all SSL cert checks are valid. Can be overriden in tests.
cert_verifier_.mock_cert_verifier()->set_default_result(net::OK);
}
void BookmarkAppNavigationBrowserTest::InstallTestBookmarkApp() {
test_bookmark_app_ = InstallTestBookmarkApp(GetAppUrlHost());
}
void BookmarkAppNavigationBrowserTest::InstallOtherTestBookmarkApp() {
InstallTestBookmarkApp(GetOtherAppUrlHost());
}
const Extension* BookmarkAppNavigationBrowserTest::InstallTestBookmarkApp(
const std::string& app_host) {
if (!https_server_.Started()) {
CHECK(https_server_.Start());
}
WebApplicationInfo web_app_info;
web_app_info.app_url = https_server_.GetURL(app_host, GetAppUrlPath());
web_app_info.scope = https_server_.GetURL(app_host, GetAppScopePath());
web_app_info.title = base::UTF8ToUTF16(GetAppName());
web_app_info.description = base::UTF8ToUTF16("Test description");
web_app_info.open_as_window = true;
return InstallBookmarkApp(web_app_info);
}
const Extension*
BookmarkAppNavigationBrowserTest::InstallImmediateRedirectingApp(
const std::string& target_host,
const std::string& target_path) {
EXPECT_TRUE(https_server_.Start());
const GURL target_url = https_server_.GetURL(target_host, target_path);
WebApplicationInfo web_app_info;
web_app_info.app_url =
https_server_.GetURL(GetAppUrlHost(), CreateServerRedirect(target_url));
web_app_info.scope = https_server_.GetURL(GetAppUrlHost(), "/");
web_app_info.title = base::UTF8ToUTF16("Redirecting Test app");
web_app_info.description = base::UTF8ToUTF16("Test description");
web_app_info.open_as_window = true;
return InstallBookmarkApp(web_app_info);
}
Browser* BookmarkAppNavigationBrowserTest::OpenTestBookmarkApp() {
GURL app_url = https_server_.GetURL(GetAppUrlHost(), GetAppUrlPath());
auto observer = GetTestNavigationObserver(app_url);
Browser* app_browser = LaunchAppBrowser(test_bookmark_app_);
observer->Wait();
return app_browser;
}
void BookmarkAppNavigationBrowserTest::NavigateToLaunchingPage(
Browser* browser) {
ui_test_utils::NavigateToURL(browser, GetLaunchingPageURL());
}
void BookmarkAppNavigationBrowserTest::NavigateToLaunchingPage() {
NavigateToLaunchingPage(browser());
}
void BookmarkAppNavigationBrowserTest::NavigateToTestAppURL() {
const GURL app_url = https_server_.GetURL(GetAppUrlHost(), GetAppUrlPath());
NavigateParams params(browser(), app_url, ui::PAGE_TRANSITION_TYPED);
ASSERT_TRUE(TestTabActionDoesNotOpenAppWindow(
app_url, base::BindOnce(&NavigateToURLWrapper, &params)));
}
void BookmarkAppNavigationBrowserTest::
TestTabActionDoesNotNavigateOrOpenAppWindow(base::OnceClosure action) {
size_t num_browsers = chrome::GetBrowserCount(profile());
int num_tabs = browser()->tab_strip_model()->count();
content::WebContents* initial_tab =
browser()->tab_strip_model()->GetActiveWebContents();
GURL initial_url = initial_tab->GetLastCommittedURL();
std::move(action).Run();
EXPECT_EQ(num_browsers, chrome::GetBrowserCount(profile()));
EXPECT_EQ(browser(), chrome::FindLastActive());
EXPECT_EQ(num_tabs, browser()->tab_strip_model()->count());
EXPECT_EQ(initial_tab, browser()->tab_strip_model()->GetActiveWebContents());
EXPECT_EQ(initial_url, initial_tab->GetLastCommittedURL());
}
void BookmarkAppNavigationBrowserTest::TestTabActionOpensBackgroundTab(
const GURL& target_url,
base::OnceClosure action) {
size_t num_browsers = chrome::GetBrowserCount(profile());
int num_tabs = browser()->tab_strip_model()->count();
content::WebContents* initial_tab =
browser()->tab_strip_model()->GetActiveWebContents();
GURL initial_url = initial_tab->GetLastCommittedURL();
std::move(action).Run();
EXPECT_EQ(num_browsers, chrome::GetBrowserCount(profile()));
EXPECT_EQ(browser(), chrome::FindLastActive());
EXPECT_EQ(++num_tabs, browser()->tab_strip_model()->count());
EXPECT_EQ(initial_tab, browser()->tab_strip_model()->GetActiveWebContents());
EXPECT_EQ(initial_url, initial_tab->GetLastCommittedURL());
content::WebContents* new_tab =
browser()->tab_strip_model()->GetWebContentsAt(num_tabs - 1);
EXPECT_NE(new_tab, initial_tab);
EXPECT_EQ(target_url, new_tab->GetLastCommittedURL());
}
void BookmarkAppNavigationBrowserTest::TestTabActionOpensForegroundWindow(
const GURL& target_url,
base::OnceClosure action) {
size_t num_browsers = chrome::GetBrowserCount(profile());
int num_tabs = browser()->tab_strip_model()->count();
content::WebContents* initial_tab =
browser()->tab_strip_model()->GetActiveWebContents();
GURL initial_url = initial_tab->GetLastCommittedURL();
std::move(action).Run();
EXPECT_EQ(++num_browsers, chrome::GetBrowserCount(profile()));
Browser* new_window = chrome::FindLastActive();
EXPECT_NE(new_window, browser());
EXPECT_FALSE(new_window->is_app());
EXPECT_EQ(target_url, new_window->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL());
EXPECT_EQ(num_tabs, browser()->tab_strip_model()->count());
EXPECT_EQ(initial_url, initial_tab->GetLastCommittedURL());
}
void BookmarkAppNavigationBrowserTest::TestTabActionOpensAppWindow(
const GURL& target_url,
base::OnceClosure action) {
content::WebContents* initial_tab =
browser()->tab_strip_model()->GetActiveWebContents();
GURL initial_url = initial_tab->GetLastCommittedURL();
int num_tabs = browser()->tab_strip_model()->count();
size_t num_browsers = chrome::GetBrowserCount(profile());
std::move(action).Run();
EXPECT_EQ(num_tabs, browser()->tab_strip_model()->count());
EXPECT_EQ(++num_browsers, chrome::GetBrowserCount(profile()));
EXPECT_NE(browser(), chrome::FindLastActive());
EXPECT_EQ(initial_url, initial_tab->GetLastCommittedURL());
EXPECT_EQ(target_url, chrome::FindLastActive()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL());
}
void BookmarkAppNavigationBrowserTest::TestTabActionOpensAppWindowWithOpener(
const GURL& target_url,
base::OnceClosure action) {
TestTabActionOpensAppWindow(target_url, std::move(action));
content::WebContents* initial_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
content::WebContents* app_web_contents =
chrome::FindLastActive()->tab_strip_model()->GetActiveWebContents();
EXPECT_TRUE(HasOpenedWindowAndOpener(initial_web_contents, app_web_contents));
}
bool BookmarkAppNavigationBrowserTest::TestActionDoesNotOpenAppWindow(
Browser* browser,
const GURL& target_url,
base::OnceClosure action) {
content::WebContents* initial_tab =
browser->tab_strip_model()->GetActiveWebContents();
int num_tabs = browser->tab_strip_model()->count();
size_t num_browsers = chrome::GetBrowserCount(browser->profile());
std::move(action).Run();
EXPECT_EQ(num_tabs, browser->tab_strip_model()->count());
EXPECT_EQ(num_browsers, chrome::GetBrowserCount(browser->profile()));
EXPECT_EQ(browser, chrome::FindLastActive());
EXPECT_EQ(initial_tab, browser->tab_strip_model()->GetActiveWebContents());
EXPECT_EQ(target_url, initial_tab->GetLastCommittedURL());
return !HasFailure();
}
void BookmarkAppNavigationBrowserTest::TestAppActionOpensForegroundTab(
Browser* app_browser,
const GURL& target_url,
base::OnceClosure action) {
size_t num_browsers = chrome::GetBrowserCount(profile());
int num_tabs_browser = browser()->tab_strip_model()->count();
int num_tabs_app_browser = app_browser->tab_strip_model()->count();
content::WebContents* app_web_contents =
app_browser->tab_strip_model()->GetActiveWebContents();
content::WebContents* initial_tab =
browser()->tab_strip_model()->GetActiveWebContents();
GURL initial_app_url = app_web_contents->GetLastCommittedURL();
GURL initial_tab_url = initial_tab->GetLastCommittedURL();
std::move(action).Run();
EXPECT_EQ(num_browsers, chrome::GetBrowserCount(profile()));
EXPECT_EQ(browser(), chrome::FindLastActive());
EXPECT_EQ(++num_tabs_browser, browser()->tab_strip_model()->count());
EXPECT_EQ(num_tabs_app_browser, app_browser->tab_strip_model()->count());
EXPECT_EQ(initial_app_url, app_web_contents->GetLastCommittedURL());
content::WebContents* new_tab =
browser()->tab_strip_model()->GetActiveWebContents();
EXPECT_NE(initial_tab, new_tab);
EXPECT_EQ(target_url, new_tab->GetLastCommittedURL());
}
void BookmarkAppNavigationBrowserTest::TestAppActionOpensAppWindowWithOpener(
Browser* app_browser,
const GURL& target_url,
base::OnceClosure action) {
size_t num_browsers = chrome::GetBrowserCount(profile());
int num_tabs_browser = browser()->tab_strip_model()->count();
int num_tabs_app_browser = app_browser->tab_strip_model()->count();
content::WebContents* app_web_contents =
app_browser->tab_strip_model()->GetActiveWebContents();
content::WebContents* initial_tab =
browser()->tab_strip_model()->GetActiveWebContents();
GURL initial_app_url = app_web_contents->GetLastCommittedURL();
GURL initial_tab_url = initial_tab->GetLastCommittedURL();
std::move(action).Run();
EXPECT_EQ(++num_browsers, chrome::GetBrowserCount(profile()));
Browser* new_app_browser = chrome::FindLastActive();
EXPECT_NE(new_app_browser, browser());
EXPECT_NE(new_app_browser, app_browser);
EXPECT_TRUE(new_app_browser->is_app());
EXPECT_EQ(num_tabs_browser, browser()->tab_strip_model()->count());
EXPECT_EQ(num_tabs_app_browser, app_browser->tab_strip_model()->count());
EXPECT_EQ(initial_app_url, app_web_contents->GetLastCommittedURL());
content::WebContents* new_app_web_contents =
new_app_browser->tab_strip_model()->GetActiveWebContents();
EXPECT_EQ(target_url, new_app_web_contents->GetLastCommittedURL());
EXPECT_TRUE(HasOpenedWindowAndOpener(app_web_contents, new_app_web_contents));
}
bool BookmarkAppNavigationBrowserTest::TestTabActionDoesNotOpenAppWindow(
const GURL& target_url,
base::OnceClosure action) {
return TestActionDoesNotOpenAppWindow(browser(), target_url,
std::move(action));
}
bool BookmarkAppNavigationBrowserTest::TestIFrameActionDoesNotOpenAppWindow(
const GURL& target_url,
base::OnceClosure action) {
size_t num_browsers = chrome::GetBrowserCount(profile());
int num_tabs = browser()->tab_strip_model()->count();
content::WebContents* initial_tab =
browser()->tab_strip_model()->GetActiveWebContents();
std::move(action).Run();
EXPECT_EQ(num_browsers, chrome::GetBrowserCount(profile()));
EXPECT_EQ(browser(), chrome::FindLastActive());
EXPECT_EQ(num_tabs, browser()->tab_strip_model()->count());
// When Site Isolation is enabled, navigating the iframe to a different
// origin causes the original iframe's RenderFrameHost to be deleted.
// So we retrieve the iframe's RenderFrameHost again.
EXPECT_EQ(target_url, GetIFrame(initial_tab)->GetLastCommittedURL());
return !HasFailure();
}
GURL BookmarkAppNavigationBrowserTest::GetLaunchingPageURL() {
return https_server_.GetURL(GetLaunchingPageHost(), GetLaunchingPagePath());
}
} // namespace test
} // namespace extensions