blob: e4cfe51d2c5e3c65e374f1712a4df0d509ac1c53 [file] [log] [blame]
// Copyright 2017 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/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/extensions/bookmark_app_helper.h"
#include "chrome/browser/extensions/bookmark_app_navigation_browsertest.h"
#include "chrome/browser/extensions/bookmark_app_navigation_throttle_utils.h"
#include "chrome/browser/extensions/launch_util.h"
#include "chrome/browser/prefs/session_startup_pref.h"
#include "chrome/browser/prerender/prerender_manager.h"
#include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/extensions/app_launch_params.h"
#include "chrome/browser/ui/extensions/application_launch.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/web_applications/web_app.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/common/context_menu_params.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_frame_navigation_observer.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/public/test/test_utils.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/notification_types.h"
#include "extensions/common/constants.h"
#include "net/base/escape.h"
#include "net/http/http_request_headers.h"
#include "net/url_request/url_fetcher.h"
#include "services/network/public/cpp/resource_request_body.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace extensions {
namespace test {
enum class StartIn {
BROWSER,
APP,
};
enum class WindowAccessResult {
CAN_ACCESS,
CANNOT_ACCESS,
// Used for when there was an unexpected issue when accessing the other window
// e.g. there is no other window, or the other window's URL does not match the
// expected URL.
OTHER,
};
namespace {
const char kTextPlainEncType[] = "text/plain";
const char kQueryParam[] = "test=";
const char kQueryParamName[] = "test";
// On macOS the Meta key is used as a modifier instead of Control.
#if defined(OS_MACOSX)
constexpr int kCtrlOrMeta = blink::WebInputEvent::kMetaKey;
#else
constexpr int kCtrlOrMeta = blink::WebInputEvent::kControlKey;
#endif
// Subclass of TestNavigationObserver that saves extra information about the
// navigation and watches all navigations to |target_url|.
class BookmarkAppNavigationObserver : public content::TestNavigationObserver {
public:
// Creates an observer that watches navigations to |target_url| on all
// existing and newly added WebContents.
explicit BookmarkAppNavigationObserver(const GURL& target_url)
: content::TestNavigationObserver(target_url),
last_navigation_is_post_(false) {
WatchExistingWebContents();
StartWatchingNewWebContents();
}
bool last_navigation_is_post() const { return last_navigation_is_post_; }
const net::HttpRequestHeaders& last_request_headers() const {
return last_request_headers_;
}
const scoped_refptr<network::ResourceRequestBody>&
last_resource_request_body() const {
return last_resource_request_body_;
}
private:
void OnDidFinishNavigation(
content::NavigationHandle* navigation_handle) override {
last_navigation_is_post_ = navigation_handle->IsPost();
last_request_headers_ = navigation_handle->GetRequestHeaders();
last_resource_request_body_ = navigation_handle->GetResourceRequestBody();
content::TestNavigationObserver::OnDidFinishNavigation(navigation_handle);
}
// True if the last navigation was a post.
bool last_navigation_is_post_;
// The request headers of the last navigation.
net::HttpRequestHeaders last_request_headers_;
// The request body of the last navigation if it was a post request.
scoped_refptr<network::ResourceRequestBody> last_resource_request_body_;
};
void ExpectNavigationResultHistogramEquals(
const base::HistogramTester& histogram_tester,
const std::vector<std::pair<BookmarkAppNavigationThrottleResult,
base::HistogramBase::Count>>& expected_counts) {
std::vector<base::Bucket> expected_bucket_counts;
for (const auto& pair : expected_counts) {
expected_bucket_counts.push_back(base::Bucket(
static_cast<base::HistogramBase::Sample>(pair.first), pair.second));
}
EXPECT_THAT(
histogram_tester.GetAllSamples("Extensions.BookmarkApp.NavigationResult"),
testing::UnorderedElementsAreArray(expected_bucket_counts));
}
// When an app is launched, whether it's in response to a navigation or click
// in a launch surface e.g. App Shelf, the first navigation in the app is
// an AUTO_BOOKMARK navigation.
std::pair<BookmarkAppNavigationThrottleResult, base::HistogramBase::Count>
GetAppLaunchedEntry() {
return {BookmarkAppNavigationThrottleResult::kProceedTransitionAutoBookmark,
1};
}
std::string CreateClientRedirect(const GURL& target_url) {
const char* const kClientRedirectBase = "/client-redirect?";
return kClientRedirectBase +
net::EscapeQueryParamValue(target_url.spec(), false);
}
// Inserts an iframe in the main frame of |web_contents|.
void InsertIFrame(content::WebContents* web_contents) {
ASSERT_TRUE(
content::ExecuteScript(web_contents,
"let iframe = document.createElement('iframe');"
"document.body.appendChild(iframe);"));
}
void ExecuteContextMenuLinkCommandAndWait(content::WebContents* web_contents,
const GURL& target_url,
int command_id) {
auto observer =
BookmarkAppNavigationBrowserTest::GetTestNavigationObserver(target_url);
content::ContextMenuParams params;
params.page_url = web_contents->GetLastCommittedURL();
params.link_url = target_url;
TestRenderViewContextMenu menu(web_contents->GetMainFrame(), params);
menu.Init();
menu.ExecuteCommand(command_id, 0 /* event_flags */);
observer->WaitForNavigationFinished();
}
void OpenPopupAndWait(content::WebContents* web_contents,
const GURL& target_url) {
auto observer =
BookmarkAppNavigationBrowserTest::GetTestNavigationObserver(target_url);
const std::string script = base::StringPrintf(
"(() => {"
" window.openedWindow = window.open('%s', '_blank', 'toolbar=no');"
"})();",
target_url.spec().c_str());
ASSERT_TRUE(content::ExecuteScript(web_contents, script));
observer->WaitForNavigationFinished();
}
// Calls window.open() with |target_url| on the main frame of |web_contents|.
// Returns once the new window has navigated to |target_url|.
void WindowOpenAndWait(content::WebContents* web_contents,
const GURL& target_url) {
auto observer =
BookmarkAppNavigationBrowserTest::GetTestNavigationObserver(target_url);
const std::string script = base::StringPrintf(
"(() => {"
" window.openedWindow = window.open('%s');"
"})();",
target_url.spec().c_str());
ASSERT_TRUE(content::ExecuteScript(web_contents, script));
observer->WaitForNavigationFinished();
}
// Calls window.open() with |target_url| on the main frame of |web_contents|.
// Returns true if the resulting child window is allowed to access members of
// its opener.
WindowAccessResult CanChildWindowAccessOpener(
content::WebContents* web_contents,
const GURL& target_url) {
WindowOpenAndWait(web_contents, target_url);
content::WebContents* new_contents =
chrome::FindLastActive()->tab_strip_model()->GetActiveWebContents();
const std::string script = base::StringPrintf(
"(() => {"
" const [CAN_ACCESS, CANNOT_ACCESS, OTHER] = [0, 1, 2];"
" let result = OTHER;"
" try {"
" if (window.opener.location.href === '%s')"
" result = CAN_ACCESS;"
" } catch (e) {"
" if (e.name === 'SecurityError')"
" result = CANNOT_ACCESS;"
" }"
" window.domAutomationController.send(result);"
"})();",
web_contents->GetLastCommittedURL().spec().c_str());
int access_result;
CHECK(content::ExecuteScriptAndExtractInt(new_contents, script,
&access_result));
switch (access_result) {
case 0:
return WindowAccessResult::CAN_ACCESS;
case 1:
return WindowAccessResult::CANNOT_ACCESS;
case 2:
return WindowAccessResult::OTHER;
default:
NOTREACHED();
return WindowAccessResult::OTHER;
}
}
// Adds a query parameter to |base_url|.
GURL AddTestQueryParam(const GURL& base_url) {
GURL::Replacements replacements;
std::string query(kQueryParam);
replacements.SetQuery(query.c_str(), url::Component(0, query.length()));
return base_url.ReplaceComponents(replacements);
}
// Creates a <form> element with a |target_url| action and |method| method. Adds
// the form to the DOM with a button and clicks the button. Returns once
// |target_url| has been loaded.
//
// If |method| is net::URLFetcher::RequestType::GET, |target_url| should contain
// an empty query string, since that URL will be loaded when submitting the form
// e.g. "https://www.example.com/?".
void SubmitFormAndWait(content::WebContents* web_contents,
const GURL& target_url,
net::URLFetcher::RequestType method) {
bool is_post = true;
if (method == net::URLFetcher::RequestType::GET) {
is_post = false;
ASSERT_TRUE(target_url.has_query());
ASSERT_EQ(kQueryParam, target_url.query());
}
BookmarkAppNavigationObserver observer(target_url);
std::string script = base::StringPrintf(
"(() => {"
"const form = document.createElement('form');"
"form.action = '%s';"
"form.method = '%s';"
"form.enctype = '%s';"
"const input = document.createElement('input');"
"input.name = '%s';"
"form.appendChild(input);"
"const button = document.createElement('input');"
"button.type = 'submit';"
"form.appendChild(button);"
"document.body.appendChild(form);"
"button.dispatchEvent(new MouseEvent('click', {'view': window}));"
"})();",
target_url.spec().c_str(),
method == net::URLFetcher::RequestType::POST ? "post" : "get",
kTextPlainEncType, kQueryParamName);
ASSERT_TRUE(content::ExecuteScript(web_contents, script));
observer.WaitForNavigationFinished();
EXPECT_EQ(is_post, observer.last_navigation_is_post());
if (is_post) {
const net::HttpRequestHeaders& headers = observer.last_request_headers();
std::string post_content_type;
headers.GetHeader(net::HttpRequestHeaders::kContentType,
&post_content_type);
EXPECT_EQ(kTextPlainEncType, post_content_type);
const std::vector<network::DataElement>* elements =
observer.last_resource_request_body()->elements();
EXPECT_EQ(1u, elements->size());
const auto& element = elements->front();
// The text/plain enconding algorithm appends "\r\n".
EXPECT_EQ(std::string(kQueryParam) + "\r\n",
std::string(element.bytes(), element.length()));
}
}
// Uses |params| to navigate to a URL. Blocks until the URL is loaded.
void NavigateToURLAndWait(NavigateParams* params) {
auto observer =
BookmarkAppNavigationBrowserTest::GetTestNavigationObserver(params->url);
ui_test_utils::NavigateToURL(params);
observer->WaitForNavigationFinished();
}
// Wrapper so that we can use base::BindOnce with NavigateToURL.
void NavigateToURLWrapper(NavigateParams* params) {
ui_test_utils::NavigateToURL(params);
}
} // namespace
// Tests for the behavior when the kDesktopPWAsLinkCapturing flag is on.
class BookmarkAppNavigationThrottleExperimentalBrowserTest
: public BookmarkAppNavigationBrowserTest {
public:
void SetUp() override {
scoped_feature_list_.InitWithFeatures(
{features::kDesktopPWAWindowing, features::kDesktopPWAsLinkCapturing},
{});
BookmarkAppNavigationBrowserTest::SetUp();
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
// Tests that the result is the same regardless of the 'rel' attribute of links.
class BookmarkAppNavigationThrottleExperimentalLinkBrowserTest
: public BookmarkAppNavigationThrottleExperimentalBrowserTest,
public ::testing::WithParamInterface<std::string> {};
// Tests that navigating to the Web App's app_url doesn't open a new window
// if features::kDesktopPWAsLinkCapturing is disabled before installing the app.
IN_PROC_BROWSER_TEST_P(BookmarkAppNavigationThrottleExperimentalLinkBrowserTest,
FeatureDisable_BeforeInstall) {
base::test::ScopedFeatureList feature_list;
feature_list.InitWithFeatures({}, {features::kDesktopPWAsLinkCapturing});
InstallTestBookmarkApp();
NavigateToLaunchingPage();
const GURL app_url = https_server().GetURL(GetAppUrlHost(), GetAppUrlPath());
TestTabActionDoesNotOpenAppWindow(
app_url,
base::BindOnce(&ClickLinkAndWait,
browser()->tab_strip_model()->GetActiveWebContents(),
app_url, LinkTarget::SELF, GetParam()));
ExpectNavigationResultHistogramEquals(global_histogram(), {});
}
// Tests that navigating to the Web App's app_url doesn't open a new window
// if features::kDesktopPWAWindowing is disabled after installing the app.
IN_PROC_BROWSER_TEST_P(BookmarkAppNavigationThrottleExperimentalLinkBrowserTest,
FeatureDisable_AfterInstall) {
InstallTestBookmarkApp();
base::test::ScopedFeatureList feature_list;
feature_list.InitWithFeatures({}, {features::kDesktopPWAsLinkCapturing});
NavigateToLaunchingPage();
const GURL app_url = https_server().GetURL(GetAppUrlHost(), GetAppUrlPath());
TestTabActionDoesNotOpenAppWindow(
app_url,
base::BindOnce(&ClickLinkAndWait,
browser()->tab_strip_model()->GetActiveWebContents(),
app_url, LinkTarget::SELF, GetParam()));
ExpectNavigationResultHistogramEquals(global_histogram(), {});
}
// Tests that most transition types for navigations to in-scope or
// out-of-scope URLs do not result in new app windows.
class BookmarkAppNavigationThrottleExperimentalTransitionBrowserTest
: public BookmarkAppNavigationThrottleExperimentalBrowserTest,
public ::testing::WithParamInterface<
std::tuple<std::string, ui::PageTransition>> {};
INSTANTIATE_TEST_CASE_P(
/* no prefix */,
BookmarkAppNavigationThrottleExperimentalTransitionBrowserTest,
testing::Combine(
testing::Values(
BookmarkAppNavigationBrowserTest::GetInScopeUrlPath(),
BookmarkAppNavigationBrowserTest::GetOutOfScopeUrlPath()),
testing::Range(ui::PAGE_TRANSITION_FIRST,
ui::PAGE_TRANSITION_LAST_CORE)));
#if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_MACOSX)
#define MAYBE_MainFrameNavigations DISABLED_MainFrameNavigations
#else
#define MAYBE_MainFrameNavigations MainFrameNavigations
#endif
IN_PROC_BROWSER_TEST_P(
BookmarkAppNavigationThrottleExperimentalTransitionBrowserTest,
MAYBE_MainFrameNavigations) {
InstallTestBookmarkApp();
GURL target_url =
https_server().GetURL(GetAppUrlHost(), std::get<0>(GetParam()));
ui::PageTransition transition = std::get<1>(GetParam());
NavigateParams params(browser(), target_url, transition);
if (!ui::PageTransitionIsMainFrame(transition)) {
// Subframe navigations require a different setup. See
// BookmarkAppNavigationThrottleExperimentalBrowserTest.SubframeNavigation.
return;
}
if (ui::PageTransitionCoreTypeIs(ui::PAGE_TRANSITION_LINK, transition) &&
target_url ==
https_server().GetURL(GetAppUrlHost(), GetInScopeUrlPath())) {
TestTabActionOpensAppWindow(target_url,
base::BindOnce(&NavigateToURLAndWait, &params));
} else {
TestTabActionDoesNotOpenAppWindow(
target_url, base::BindOnce(&NavigateToURLAndWait, &params));
}
}
// Tests that navigations in subframes don't open new app windows.
//
// The transition type for subframe navigations is not set until
// after NavigationThrottles run. Because of this, our
// BookmarkAppNavigationThrottle will not see the transition type as
// PAGE_TRANSITION_AUTO_SUBFRAME/PAGE_TRANSITION_MANUAL_SUBFRAME, even though,
// by the end of the navigation, the transition type is
// PAGE_TRANSITION_AUTO_SUBFRAME/PAGE_TRANSITON_MANUAL_SUBFRAME.
IN_PROC_BROWSER_TEST_F(BookmarkAppNavigationThrottleExperimentalBrowserTest,
AutoSubframeNavigation) {
InstallTestBookmarkApp();
NavigateToLaunchingPage();
content::WebContents* initial_tab =
browser()->tab_strip_model()->GetActiveWebContents();
InsertIFrame(initial_tab);
content::RenderFrameHost* iframe = GetIFrame(initial_tab);
const GURL app_url = https_server().GetURL(GetAppUrlHost(), GetAppUrlPath());
NavigateParams params(browser(), app_url, ui::PAGE_TRANSITION_LINK);
params.frame_tree_node_id = iframe->GetFrameTreeNodeId();
content::TestFrameNavigationObserver observer(iframe);
TestIFrameActionDoesNotOpenAppWindow(
app_url, base::BindOnce(&NavigateToURLWrapper, &params));
ASSERT_TRUE(ui::PageTransitionCoreTypeIs(observer.transition_type(),
ui::PAGE_TRANSITION_AUTO_SUBFRAME));
// Subframe navigations are completely ignored, so there should be no change
// in the histogram.
ExpectNavigationResultHistogramEquals(global_histogram(), {});
}
IN_PROC_BROWSER_TEST_F(BookmarkAppNavigationThrottleExperimentalBrowserTest,
ManualSubframeNavigation) {
InstallTestBookmarkApp();
NavigateToLaunchingPage();
content::WebContents* initial_tab =
browser()->tab_strip_model()->GetActiveWebContents();
InsertIFrame(initial_tab);
{
// Navigate the iframe once, so that the next navigation is a
// MANUAL_SUBFRAME navigation.
content::RenderFrameHost* iframe = GetIFrame(initial_tab);
NavigateParams params(browser(), GetLaunchingPageURL(),
ui::PAGE_TRANSITION_LINK);
params.frame_tree_node_id = iframe->GetFrameTreeNodeId();
ASSERT_TRUE(TestIFrameActionDoesNotOpenAppWindow(
GetLaunchingPageURL(), base::BindOnce(&NavigateToURLWrapper, &params)));
}
content::RenderFrameHost* iframe = GetIFrame(initial_tab);
const GURL app_url = https_server().GetURL(GetAppUrlHost(), GetAppUrlPath());
NavigateParams params(browser(), app_url, ui::PAGE_TRANSITION_LINK);
params.frame_tree_node_id = iframe->GetFrameTreeNodeId();
content::TestFrameNavigationObserver observer(iframe);
TestIFrameActionDoesNotOpenAppWindow(
app_url, base::BindOnce(&NavigateToURLWrapper, &params));
ASSERT_TRUE(ui::PageTransitionCoreTypeIs(
observer.transition_type(), ui::PAGE_TRANSITION_MANUAL_SUBFRAME));
// Subframe navigations are completely ignored, so there should be no change
// in the histogram.
ExpectNavigationResultHistogramEquals(global_histogram(), {});
}
// Tests that pasting an in-scope URL into the address bar and navigating to it,
// does not open an app window.
// https://crbug.com/782004
IN_PROC_BROWSER_TEST_F(BookmarkAppNavigationThrottleExperimentalBrowserTest,
LinkNavigationFromAddressBar) {
InstallTestBookmarkApp();
const GURL app_url = https_server().GetURL(GetAppUrlHost(), GetAppUrlPath());
// Fake a navigation with a TRANSITION_LINK core type and a
// TRANSITION_FROM_ADDRESS_BAR qualifier. This matches the transition that
// results from pasting a URL into the address and navigating to it.
NavigateParams params(
browser(), app_url,
ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK |
ui::PAGE_TRANSITION_FROM_ADDRESS_BAR));
ASSERT_TRUE(TestTabActionDoesNotOpenAppWindow(
app_url, base::BindOnce(&NavigateToURLWrapper, &params)));
ExpectNavigationResultHistogramEquals(
global_histogram(),
{{BookmarkAppNavigationThrottleResult::kProceedTransitionFromAddressBar,
1}});
}
// Tests that going back to an in-scope URL does not open a new app window.
IN_PROC_BROWSER_TEST_F(BookmarkAppNavigationThrottleExperimentalBrowserTest,
BackNavigation) {
InstallTestBookmarkApp();
NavigateToTestAppURL();
// Navigate to an in-scope URL to generate a link navigation that didn't
// get intercepted. The navigation won't get intercepted because the target
// URL is in-scope of the app for the current URL.
const GURL in_scope_url =
https_server().GetURL(GetAppUrlHost(), GetInScopeUrlPath());
ASSERT_TRUE(TestTabActionDoesNotOpenAppWindow(
in_scope_url,
base::BindOnce(&ClickLinkAndWait,
browser()->tab_strip_model()->GetActiveWebContents(),
in_scope_url, LinkTarget::SELF, "" /* rel */)));
// Navigate to an out-of-scope URL.
{
const GURL out_of_scope_url =
https_server().GetURL(GetAppUrlHost(), GetOutOfScopeUrlPath());
NavigateParams params(browser(), out_of_scope_url,
ui::PAGE_TRANSITION_TYPED);
ASSERT_TRUE(TestTabActionDoesNotOpenAppWindow(
out_of_scope_url, base::BindOnce(&NavigateToURLWrapper, &params)));
}
base::HistogramTester scoped_histogram;
TestTabActionDoesNotOpenAppWindow(
in_scope_url,
base::BindOnce(
[](content::WebContents* web_contents, const GURL& in_scope_url) {
auto observer = GetTestNavigationObserver(in_scope_url);
web_contents->GetController().GoBack();
observer->WaitForNavigationFinished();
},
browser()->tab_strip_model()->GetActiveWebContents(), in_scope_url));
ExpectNavigationResultHistogramEquals(
scoped_histogram,
{{BookmarkAppNavigationThrottleResult::kProceedTransitionForwardBack,
1}});
}
// Tests that clicking a link to an app that launches in a tab does not open a
// a new app window.
IN_PROC_BROWSER_TEST_P(BookmarkAppNavigationThrottleExperimentalLinkBrowserTest,
TabApp) {
InstallTestBookmarkApp();
// Set a pref indicating that the user wants to launch in a regular tab.
extensions::SetLaunchType(browser()->profile(), test_bookmark_app()->id(),
extensions::LAUNCH_TYPE_REGULAR);
NavigateToLaunchingPage();
const GURL app_url = https_server().GetURL(GetAppUrlHost(), GetAppUrlPath());
TestTabActionDoesNotOpenAppWindow(
app_url,
base::BindOnce(&ClickLinkAndWait,
browser()->tab_strip_model()->GetActiveWebContents(),
app_url, LinkTarget::SELF, GetParam()));
// Non-windowed apps are not considered when looking for a target app, so it's
// as if there was no app installed for the URL.
ExpectNavigationResultHistogramEquals(global_histogram(), {});
}
// Tests that clicking a link with target="_self" to the app's app_url opens the
// Bookmark App.
IN_PROC_BROWSER_TEST_P(BookmarkAppNavigationThrottleExperimentalLinkBrowserTest,
AppUrlSelf) {
InstallTestBookmarkApp();
NavigateToLaunchingPage();
const GURL app_url = https_server().GetURL(GetAppUrlHost(), GetAppUrlPath());
TestTabActionOpensAppWindow(
app_url,
base::BindOnce(&ClickLinkAndWait,
browser()->tab_strip_model()->GetActiveWebContents(),
app_url, LinkTarget::SELF, GetParam()));
ExpectNavigationResultHistogramEquals(
global_histogram(),
{{BookmarkAppNavigationThrottleResult::kCancelOpenedApp, 1},
GetAppLaunchedEntry()});
}
// Tests that clicking a link with target="_blank" to the app's app_url opens
// the Bookmark App.
IN_PROC_BROWSER_TEST_P(BookmarkAppNavigationThrottleExperimentalLinkBrowserTest,
AppUrlBlank) {
InstallTestBookmarkApp();
NavigateToLaunchingPage();
const GURL app_url = https_server().GetURL(GetAppUrlHost(), GetAppUrlPath());
TestTabActionOpensAppWindow(
app_url,
base::BindOnce(&ClickLinkAndWait,
browser()->tab_strip_model()->GetActiveWebContents(),
app_url, LinkTarget::BLANK, GetParam()));
ExpectNavigationResultHistogramEquals(
global_histogram(),
{{BookmarkAppNavigationThrottleResult::kDeferMovingContentsToNewAppWindow,
1}});
}
// Tests that Ctrl + Clicking a link to the app's app_url opens a new background
// tab and not the app.
IN_PROC_BROWSER_TEST_P(BookmarkAppNavigationThrottleExperimentalLinkBrowserTest,
AppUrlCtrlClick) {
InstallTestBookmarkApp();
NavigateToLaunchingPage();
const GURL app_url = https_server().GetURL(GetAppUrlPath());
TestTabActionOpensBackgroundTab(
app_url,
base::BindOnce(&ClickLinkWithModifiersAndWait,
browser()->tab_strip_model()->GetActiveWebContents(),
app_url, LinkTarget::SELF, GetParam(), kCtrlOrMeta));
ExpectNavigationResultHistogramEquals(
global_histogram(), {{BookmarkAppNavigationThrottleResult::
kProceedDispositionNewBackgroundTab,
1}});
}
// Tests that clicking a link with target="_self" and for which the server
// redirects to the app's app_url opens the Bookmark App.
IN_PROC_BROWSER_TEST_P(BookmarkAppNavigationThrottleExperimentalLinkBrowserTest,
ServerRedirectToAppUrlSelf) {
InstallTestBookmarkApp();
NavigateToLaunchingPage();
const GURL app_url = https_server().GetURL(GetAppUrlHost(), GetAppUrlPath());
const GURL redirecting_url = https_server().GetURL(
GetLaunchingPageHost(), CreateServerRedirect(app_url));
TestTabActionOpensAppWindow(
app_url,
base::BindOnce(&ClickLinkAndWaitForURL,
browser()->tab_strip_model()->GetActiveWebContents(),
redirecting_url, app_url, LinkTarget::SELF, GetParam()));
ExpectNavigationResultHistogramEquals(
global_histogram(),
{{BookmarkAppNavigationThrottleResult::kCancelOpenedApp, 1},
GetAppLaunchedEntry()});
}
// Tests that clicking a link with target="_blank" and for which the server
// redirects to the app's app_url opens the Bookmark App.
IN_PROC_BROWSER_TEST_P(BookmarkAppNavigationThrottleExperimentalLinkBrowserTest,
ServerRedirectToAppUrlBlank) {
InstallTestBookmarkApp();
NavigateToLaunchingPage();
const GURL app_url = https_server().GetURL(GetAppUrlHost(), GetAppUrlPath());
const GURL redirecting_url = https_server().GetURL(
GetLaunchingPageHost(), CreateServerRedirect(app_url));
TestTabActionOpensAppWindow(
app_url,
base::BindOnce(&ClickLinkAndWaitForURL,
browser()->tab_strip_model()->GetActiveWebContents(),
redirecting_url, app_url, LinkTarget::BLANK, GetParam()));
ExpectNavigationResultHistogramEquals(
global_histogram(),
{{BookmarkAppNavigationThrottleResult::kDeferMovingContentsToNewAppWindow,
1}});
}
// Tests that clicking a link with target="_self" and for which the client
// redirects to the app's app_url opens the Bookmark App. The initial tab will
// be left on the redirecting URL.
IN_PROC_BROWSER_TEST_P(BookmarkAppNavigationThrottleExperimentalLinkBrowserTest,
ClientRedirectToAppUrlSelf) {
InstallTestBookmarkApp();
NavigateToLaunchingPage();
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());
const GURL app_url = https_server().GetURL(GetAppUrlHost(), GetAppUrlPath());
const GURL redirecting_url = https_server().GetURL(
GetLaunchingPageHost(), CreateClientRedirect(app_url));
ClickLinkAndWaitForURL(browser()->tab_strip_model()->GetActiveWebContents(),
redirecting_url, app_url, LinkTarget::SELF,
GetParam());
EXPECT_EQ(num_tabs, browser()->tab_strip_model()->count());
EXPECT_EQ(++num_browsers, chrome::GetBrowserCount(profile()));
EXPECT_NE(browser(), chrome::FindLastActive());
EXPECT_EQ(redirecting_url, initial_tab->GetLastCommittedURL());
EXPECT_EQ(app_url, chrome::FindLastActive()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL());
ExpectNavigationResultHistogramEquals(
global_histogram(),
{{BookmarkAppNavigationThrottleResult::kCancelOpenedApp, 1},
GetAppLaunchedEntry()});
}
// Tests that clicking a link with target="_blank" and for which the client
// redirects to the app's app_url opens the Bookmark App. The new tab will be
// left on the redirecting URL.
IN_PROC_BROWSER_TEST_P(BookmarkAppNavigationThrottleExperimentalLinkBrowserTest,
ClientRedirectToAppUrlBlank) {
InstallTestBookmarkApp();
NavigateToLaunchingPage();
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());
const GURL app_url = https_server().GetURL(GetAppUrlHost(), GetAppUrlPath());
const GURL redirecting_url = https_server().GetURL(
GetLaunchingPageHost(), CreateClientRedirect(app_url));
ClickLinkAndWaitForURL(browser()->tab_strip_model()->GetActiveWebContents(),
redirecting_url, app_url, LinkTarget::BLANK,
GetParam());
EXPECT_EQ(++num_tabs, browser()->tab_strip_model()->count());
EXPECT_EQ(++num_browsers, chrome::GetBrowserCount(profile()));
EXPECT_NE(browser(), chrome::FindLastActive());
content::WebContents* new_tab =
browser()->tab_strip_model()->GetActiveWebContents();
EXPECT_NE(new_tab, initial_tab);
EXPECT_EQ(redirecting_url, new_tab->GetLastCommittedURL());
EXPECT_EQ(app_url, chrome::FindLastActive()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL());
ExpectNavigationResultHistogramEquals(
global_histogram(),
{{BookmarkAppNavigationThrottleResult::kCancelOpenedApp, 1},
GetAppLaunchedEntry()});
}
// Tests that clicking a link with target="_self" to a URL in the Web App's
// scope opens a new browser window.
IN_PROC_BROWSER_TEST_P(BookmarkAppNavigationThrottleExperimentalLinkBrowserTest,
InScopeUrlSelf) {
InstallTestBookmarkApp();
NavigateToLaunchingPage();
const GURL in_scope_url =
https_server().GetURL(GetAppUrlHost(), GetInScopeUrlPath());
TestTabActionOpensAppWindow(
in_scope_url,
base::BindOnce(&ClickLinkAndWait,
browser()->tab_strip_model()->GetActiveWebContents(),
in_scope_url, LinkTarget::SELF, GetParam()));
ExpectNavigationResultHistogramEquals(
global_histogram(),
{{BookmarkAppNavigationThrottleResult::kCancelOpenedApp, 1},
GetAppLaunchedEntry()});
}
// Tests that clicking a link with target="_self" to a URL out of the Web App's
// scope but with the same origin doesn't open a new browser window.
IN_PROC_BROWSER_TEST_P(BookmarkAppNavigationThrottleExperimentalLinkBrowserTest,
OutOfScopeUrlSelf) {
InstallTestBookmarkApp();
NavigateToLaunchingPage();
const GURL out_of_scope_url =
https_server().GetURL(GetAppUrlHost(), GetOutOfScopeUrlPath());
TestTabActionDoesNotOpenAppWindow(
out_of_scope_url,
base::BindOnce(&ClickLinkAndWait,
browser()->tab_strip_model()->GetActiveWebContents(),
out_of_scope_url, LinkTarget::SELF, GetParam()));
ExpectNavigationResultHistogramEquals(global_histogram(), {});
}
// Tests that prerender links don't open the app.
IN_PROC_BROWSER_TEST_F(BookmarkAppNavigationThrottleExperimentalBrowserTest,
PrerenderLinks) {
prerender::PrerenderManager::SetMode(
prerender::PrerenderManager::PRERENDER_MODE_ENABLED);
InstallTestBookmarkApp();
NavigateToLaunchingPage();
TestTabActionDoesNotNavigateOrOpenAppWindow(base::BindOnce(
[](content::WebContents* web_contents, const GURL& target_url) {
std::string script = base::StringPrintf(
"(() => {"
"const prerender_link = document.createElement('link');"
"prerender_link.rel = 'prerender';"
"prerender_link.href = '%s';"
"prerender_link.addEventListener('webkitprerenderstop',"
"() => window.domAutomationController.send(true));"
"document.body.appendChild(prerender_link);"
"})();",
target_url.spec().c_str());
bool result;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(web_contents, script,
&result));
ASSERT_TRUE(result);
},
browser()->tab_strip_model()->GetActiveWebContents(),
https_server().GetURL(GetAppUrlHost(), GetInScopeUrlPath())));
ExpectNavigationResultHistogramEquals(
global_histogram(),
{{BookmarkAppNavigationThrottleResult::kCancelPrerenderContents, 1}});
}
// Tests fetch calls don't open a new App window.
IN_PROC_BROWSER_TEST_F(BookmarkAppNavigationThrottleExperimentalBrowserTest,
Fetch) {
InstallTestBookmarkApp();
NavigateToLaunchingPage();
TestTabActionDoesNotNavigateOrOpenAppWindow(base::BindOnce(
[](content::WebContents* web_contents, const GURL& target_url) {
std::string script = base::StringPrintf(
"(() => {"
"fetch('%s').then(response => {"
" window.domAutomationController.send(response.ok);"
"});"
"})();",
target_url.spec().c_str());
bool result;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(web_contents, script,
&result));
ASSERT_TRUE(result);
},
browser()->tab_strip_model()->GetActiveWebContents(),
https_server().GetURL(GetAppUrlHost(), GetInScopeUrlPath())));
// Fetch requests don't go through our NavigationHandle.
ExpectNavigationResultHistogramEquals(global_histogram(), {});
}
// Tests that clicking "Open link in new tab" to an in-scope URL opens a new
// tab in the background.
IN_PROC_BROWSER_TEST_F(BookmarkAppNavigationThrottleExperimentalBrowserTest,
ContextMenuNewTab) {
InstallTestBookmarkApp();
NavigateToLaunchingPage();
const GURL app_url = https_server().GetURL(GetAppUrlHost(), GetAppUrlPath());
TestTabActionOpensBackgroundTab(
app_url,
base::BindOnce(&ExecuteContextMenuLinkCommandAndWait,
browser()->tab_strip_model()->GetActiveWebContents(),
app_url, IDC_CONTENT_CONTEXT_OPENLINKNEWTAB));
ExpectNavigationResultHistogramEquals(
global_histogram(),
{{BookmarkAppNavigationThrottleResult::kProceedStartedFromContextMenu,
1}});
}
// Tests that clicking "Open link in new window" to an in-scope URL opens a new
// window in the foreground.
IN_PROC_BROWSER_TEST_F(BookmarkAppNavigationThrottleExperimentalBrowserTest,
ContextMenuNewWindow) {
InstallTestBookmarkApp();
NavigateToLaunchingPage();
const GURL app_url = https_server().GetURL(GetAppUrlHost(), GetAppUrlPath());
TestTabActionOpensForegroundWindow(
app_url,
base::BindOnce(&ExecuteContextMenuLinkCommandAndWait,
browser()->tab_strip_model()->GetActiveWebContents(),
app_url, IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW));
ExpectNavigationResultHistogramEquals(
global_histogram(),
{{BookmarkAppNavigationThrottleResult::kProceedStartedFromContextMenu,
1}});
}
// Tests that clicking "Open link in new tab" in an app to an in-scope URL opens
// a new foreground tab in a regular browser window.
IN_PROC_BROWSER_TEST_F(BookmarkAppNavigationThrottleExperimentalBrowserTest,
InAppContextMenuNewTab) {
InstallTestBookmarkApp();
Browser* app_browser = OpenTestBookmarkApp();
base::HistogramTester scoped_histogram;
const GURL app_url = https_server().GetURL(GetAppUrlHost(), GetAppUrlPath());
TestAppActionOpensForegroundTab(
app_browser, app_url,
base::BindOnce(&ExecuteContextMenuLinkCommandAndWait,
app_browser->tab_strip_model()->GetActiveWebContents(),
app_url, IDC_CONTENT_CONTEXT_OPENLINKNEWTAB));
ExpectNavigationResultHistogramEquals(
scoped_histogram,
{{BookmarkAppNavigationThrottleResult::kProceedStartedFromContextMenu,
1}});
}
// Tests that clicking "Open link in incognito window" to an in-scope URL opens
// an incognito window and not an app window.
IN_PROC_BROWSER_TEST_F(BookmarkAppNavigationThrottleExperimentalBrowserTest,
OpenInIncognito) {
InstallTestBookmarkApp();
NavigateToLaunchingPage();
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();
const GURL in_scope_url =
https_server().GetURL(GetAppUrlHost(), GetInScopeUrlPath());
auto observer = GetTestNavigationObserver(in_scope_url);
content::ContextMenuParams params;
params.page_url = initial_url;
params.link_url = in_scope_url;
TestRenderViewContextMenu menu(initial_tab->GetMainFrame(), params);
menu.Init();
menu.ExecuteCommand(IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD,
0 /* event_flags */);
observer->WaitForNavigationFinished();
Browser* incognito_browser = chrome::FindLastActive();
EXPECT_EQ(incognito_browser->profile(), profile()->GetOffTheRecordProfile());
EXPECT_NE(browser(), incognito_browser);
EXPECT_EQ(in_scope_url, incognito_browser->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL());
EXPECT_EQ(num_browsers, chrome::GetBrowserCount(profile()));
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());
// Incognito navigations are completely ignored.
ExpectNavigationResultHistogramEquals(global_histogram(), {});
}
// Tests that clicking a link to an in-scope URL when in incognito does not open
// an App window.
IN_PROC_BROWSER_TEST_P(BookmarkAppNavigationThrottleExperimentalLinkBrowserTest,
InScopeUrlIncognito) {
InstallTestBookmarkApp();
Browser* incognito_browser = CreateIncognitoBrowser();
NavigateToLaunchingPage(incognito_browser);
const GURL in_scope_url =
https_server().GetURL(GetAppUrlHost(), GetInScopeUrlPath());
TestActionDoesNotOpenAppWindow(
incognito_browser, in_scope_url,
base::BindOnce(
&ClickLinkAndWait,
incognito_browser->tab_strip_model()->GetActiveWebContents(),
in_scope_url, LinkTarget::SELF, GetParam()));
// Incognito navigations are completely ignored.
ExpectNavigationResultHistogramEquals(global_histogram(), {});
}
// Tests that clicking a target=_self link from a URL out of the Web App's scope
// but with the same origin to an in-scope URL results in a new App window.
IN_PROC_BROWSER_TEST_P(BookmarkAppNavigationThrottleExperimentalLinkBrowserTest,
FromOutOfScopeUrlToInScopeUrlSelf) {
InstallTestBookmarkApp();
// Navigate to out-of-scope URL. Shouldn't open a new window.
const GURL out_of_scope_url =
https_server().GetURL(GetAppUrlHost(), GetOutOfScopeUrlPath());
NavigateParams params(browser(), out_of_scope_url, ui::PAGE_TRANSITION_TYPED);
ASSERT_TRUE(TestTabActionDoesNotOpenAppWindow(
out_of_scope_url, base::BindOnce(&NavigateToURLWrapper, &params)));
const GURL in_scope_url =
https_server().GetURL(GetAppUrlHost(), GetAppUrlPath());
TestTabActionOpensAppWindow(
in_scope_url,
base::BindOnce(&ClickLinkAndWait,
browser()->tab_strip_model()->GetActiveWebContents(),
in_scope_url, LinkTarget::SELF, GetParam()));
ExpectNavigationResultHistogramEquals(
global_histogram(),
{{BookmarkAppNavigationThrottleResult::kCancelOpenedApp, 1},
GetAppLaunchedEntry()});
}
// Tests that clicking a target=_blank link from a URL out of the Web App's
// scope but with the same origin to an in-scope URL results in a new App
// window.
// TODO(crbug.com/837277): Deflake and reenable.
IN_PROC_BROWSER_TEST_P(BookmarkAppNavigationThrottleExperimentalLinkBrowserTest,
DISABLED_FromOutOfScopeUrlToInScopeUrlBlank) {
InstallTestBookmarkApp();
// Navigate to out-of-scope URL. Shouldn't open a new window.
const GURL out_of_scope_url =
https_server().GetURL(GetAppUrlHost(), GetOutOfScopeUrlPath());
NavigateParams params(browser(), out_of_scope_url, ui::PAGE_TRANSITION_TYPED);
ASSERT_TRUE(TestTabActionDoesNotOpenAppWindow(
out_of_scope_url, base::BindOnce(&NavigateToURLWrapper, &params)));
const GURL in_scope_url =
https_server().GetURL(GetAppUrlHost(), GetAppUrlPath());
TestTabActionOpensAppWindow(
in_scope_url,
base::BindOnce(&ClickLinkAndWait,
browser()->tab_strip_model()->GetActiveWebContents(),
in_scope_url, LinkTarget::BLANK, GetParam()));
ExpectNavigationResultHistogramEquals(
global_histogram(),
{{BookmarkAppNavigationThrottleResult::kDeferMovingContentsToNewAppWindow,
1}});
}
// Tests that clicking links inside a website for an installed app doesn't open
// a new browser window.
IN_PROC_BROWSER_TEST_P(BookmarkAppNavigationThrottleExperimentalLinkBrowserTest,
InWebsiteNavigation) {
InstallTestBookmarkApp();
NavigateToTestAppURL();
base::HistogramTester scoped_histogram;
const GURL in_scope_url =
https_server().GetURL(GetAppUrlHost(), GetInScopeUrlPath());
TestTabActionDoesNotOpenAppWindow(
in_scope_url,
base::BindOnce(&ClickLinkAndWait,
browser()->tab_strip_model()->GetActiveWebContents(),
in_scope_url, LinkTarget::SELF, GetParam()));
ExpectNavigationResultHistogramEquals(
scoped_histogram,
{{BookmarkAppNavigationThrottleResult::kProceedInBrowserSameScope, 1}});
}
class BookmarkAppNavigationThrottleExperimentalWindowOpenBrowserTest
: public BookmarkAppNavigationThrottleExperimentalBrowserTest,
public ::testing::WithParamInterface<std::string> {};
// Tests that same-origin or cross-origin apps created with window.open() from
// a regular browser window have an opener.
IN_PROC_BROWSER_TEST_P(
BookmarkAppNavigationThrottleExperimentalWindowOpenBrowserTest,
WindowOpenInBrowser) {
InstallTestBookmarkApp();
InstallOtherTestBookmarkApp();
NavigateToTestAppURL();
// Call window.open() with |target_url|.
const GURL target_url = https_server().GetURL(GetParam(), GetAppUrlPath());
TestTabActionOpensAppWindowWithOpener(
target_url,
base::BindOnce(&WindowOpenAndWait,
browser()->tab_strip_model()->GetActiveWebContents(),
target_url));
}
// Tests that same-origin or cross-origin apps created with window.open() from
// another app window have an opener.
#if defined(OS_MACOSX)
#define MAYBE_WindowOpenInApp DISABLED_WindowOpenInApp
#else
#define MAYBE_WindowOpenInApp WindowOpenInApp
#endif
IN_PROC_BROWSER_TEST_P(
BookmarkAppNavigationThrottleExperimentalWindowOpenBrowserTest,
MAYBE_WindowOpenInApp) {
InstallTestBookmarkApp();
InstallOtherTestBookmarkApp();
// Open app window.
Browser* app_browser = OpenTestBookmarkApp();
content::WebContents* app_web_contents =
app_browser->tab_strip_model()->GetActiveWebContents();
// Call window.open() with |target_url|.
const GURL target_url = https_server().GetURL(GetParam(), GetAppUrlPath());
TestAppActionOpensAppWindowWithOpener(
app_browser, target_url,
base::BindOnce(&WindowOpenAndWait, app_web_contents, target_url));
}
INSTANTIATE_TEST_CASE_P(
/* no prefix */,
BookmarkAppNavigationThrottleExperimentalWindowOpenBrowserTest,
testing::Values(BookmarkAppNavigationBrowserTest::GetAppUrlHost(),
BookmarkAppNavigationBrowserTest::GetOtherAppUrlHost()));
// Tests that a child window can access its opener, when the opener is a regular
// browser tab.
IN_PROC_BROWSER_TEST_F(BookmarkAppNavigationThrottleExperimentalBrowserTest,
AccessOpenerBrowserWindowFromChildWindow) {
InstallTestBookmarkApp();
InstallOtherTestBookmarkApp();
NavigateToTestAppURL();
content::WebContents* current_tab =
browser()->tab_strip_model()->GetActiveWebContents();
const GURL app_url = https_server().GetURL(GetAppUrlHost(), GetAppUrlPath());
EXPECT_EQ(WindowAccessResult::CAN_ACCESS,
CanChildWindowAccessOpener(current_tab, app_url));
const GURL other_app_url =
https_server().GetURL(GetOtherAppUrlHost(), GetAppUrlPath());
EXPECT_EQ(WindowAccessResult::CANNOT_ACCESS,
CanChildWindowAccessOpener(current_tab, other_app_url));
}
// Tests that a child window can access its opener, when the opener is another
// app.
IN_PROC_BROWSER_TEST_F(BookmarkAppNavigationThrottleExperimentalBrowserTest,
AccessOpenerAppWindowFromChildWindow) {
InstallTestBookmarkApp();
InstallOtherTestBookmarkApp();
// Open app window.
Browser* app_browser = OpenTestBookmarkApp();
content::WebContents* app_web_contents =
app_browser->tab_strip_model()->GetActiveWebContents();
const GURL app_url = https_server().GetURL(GetAppUrlHost(), GetAppUrlPath());
EXPECT_EQ(WindowAccessResult::CAN_ACCESS,
CanChildWindowAccessOpener(app_web_contents, app_url));
const GURL other_app_url =
https_server().GetURL(GetOtherAppUrlHost(), GetAppUrlPath());
EXPECT_EQ(WindowAccessResult::CANNOT_ACCESS,
CanChildWindowAccessOpener(app_web_contents, other_app_url));
}
// Tests that in-browser navigations with all the following characteristics
// don't open a new app window or move the tab:
// 1. Are to in-scope URLs
// 2. Result in a AUTO_BOOKMARK transtion
// 3. Redirect to another in-scope URL.
//
// Clicking on sites in the New Tab Page is one of the ways to trigger this
// type of navigation.
IN_PROC_BROWSER_TEST_F(BookmarkAppNavigationThrottleExperimentalBrowserTest,
AutoBookmarkInScopeRedirect) {
const Extension* app =
InstallImmediateRedirectingApp(GetAppUrlHost(), GetInScopeUrlPath());
const GURL app_url = AppLaunchInfo::GetFullLaunchURL(app);
NavigateParams params(browser(), app_url, ui::PAGE_TRANSITION_AUTO_BOOKMARK);
TestTabActionDoesNotOpenAppWindow(
https_server().GetURL(GetAppUrlHost(), GetInScopeUrlPath()),
base::BindOnce(&NavigateToURLWrapper, &params));
// The first AUTO_BOOKMARK navigation is the one we triggered and the second
// one is the redirect.
ExpectNavigationResultHistogramEquals(
global_histogram(),
{{BookmarkAppNavigationThrottleResult::kProceedTransitionAutoBookmark,
2}});
}
// Tests that in-browser navigations with all the following characteristics
// don't open a new app window or move the tab:
// 1. Are to in-scope URLs
// 2. Result in a AUTO_BOOKMARK transtion
// 3. Redirect to an out-of-scope URL.
//
// Clicking on sites in the New Tab Page is one of the ways to trigger this
// type of navigation.
IN_PROC_BROWSER_TEST_F(BookmarkAppNavigationThrottleExperimentalBrowserTest,
AutoBookmarkOutOfScopeRedirect) {
const Extension* app = InstallImmediateRedirectingApp(GetLaunchingPageHost(),
GetLaunchingPagePath());
const GURL app_url = AppLaunchInfo::GetFullLaunchURL(app);
NavigateParams params(browser(), app_url, ui::PAGE_TRANSITION_AUTO_BOOKMARK);
TestTabActionDoesNotOpenAppWindow(
GetLaunchingPageURL(), base::BindOnce(&NavigateToURLWrapper, &params));
ExpectNavigationResultHistogramEquals(
global_histogram(),
{{BookmarkAppNavigationThrottleResult::kProceedTransitionAutoBookmark,
1}});
}
INSTANTIATE_TEST_CASE_P(
/* no prefix */,
BookmarkAppNavigationThrottleExperimentalLinkBrowserTest,
testing::Values("", "noopener", "noreferrer", "nofollow"));
// Base class for testing the behavior is the same regardless of the
// kDesktopPWAsLinkCapturing feature flag.
class BookmarkAppNavigationThrottleBaseCommonBrowserTest
: public BookmarkAppNavigationBrowserTest {
protected:
void EnableFeaturesForTest(bool should_enable_link_capturing) {
if (should_enable_link_capturing) {
scoped_feature_list_.InitWithFeatures(
{features::kDesktopPWAWindowing, features::kDesktopPWAsLinkCapturing},
{});
} else {
scoped_feature_list_.InitWithFeatures(
{features::kDesktopPWAWindowing},
{features::kDesktopPWAsLinkCapturing});
}
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
// Class for testing the behavior is the same regardless of the
// kDesktopPWAsLinkCapturing feature flag.
class BookmarkAppNavigationThrottleCommonBrowserTest
: public BookmarkAppNavigationThrottleBaseCommonBrowserTest,
public ::testing::WithParamInterface<bool> {
public:
void SetUp() override {
EnableFeaturesForTest(GetParam());
BookmarkAppNavigationThrottleBaseCommonBrowserTest::SetUp();
}
};
// Tests that an app that immediately redirects to an out-of-scope URL opens a
// new foreground tab.
IN_PROC_BROWSER_TEST_P(BookmarkAppNavigationThrottleCommonBrowserTest,
ImmediateOutOfScopeRedirect) {
size_t num_browsers = chrome::GetBrowserCount(profile());
int num_tabs_browser = browser()->tab_strip_model()->count();
content::WebContents* initial_tab =
browser()->tab_strip_model()->GetActiveWebContents();
GURL initial_url = initial_tab->GetLastCommittedURL();
const Extension* redirecting_app = InstallImmediateRedirectingApp(
GetLaunchingPageHost(), GetLaunchingPagePath());
auto observer = GetTestNavigationObserver(GetLaunchingPageURL());
LaunchAppBrowser(redirecting_app);
observer->WaitForNavigationFinished();
EXPECT_EQ(num_browsers, chrome::GetBrowserCount(profile()));
EXPECT_EQ(browser(), chrome::FindLastActive());
EXPECT_EQ(++num_tabs_browser, browser()->tab_strip_model()->count());
EXPECT_EQ(initial_url, initial_tab->GetLastCommittedURL());
content::WebContents* new_tab =
browser()->tab_strip_model()->GetActiveWebContents();
EXPECT_NE(initial_tab, new_tab);
EXPECT_EQ(GetLaunchingPageURL(), new_tab->GetLastCommittedURL());
// When kDesktopPWAsLinkCapturing is enabled, app launches get histogrammed,
// but when it's disabled, they don't.
if (GetParam()) {
ExpectNavigationResultHistogramEquals(
global_histogram(), {GetAppLaunchedEntry(),
{BookmarkAppNavigationThrottleResult::
kOpenInChromeProceedOutOfScopeLaunch,
1}});
} else {
ExpectNavigationResultHistogramEquals(
global_histogram(), {{BookmarkAppNavigationThrottleResult::
kOpenInChromeProceedOutOfScopeLaunch,
1}});
}
}
// Tests that popups to out-of-scope URLs are opened in regular popup windows
// and not in app windows.
// TODO(crbug.com/849163) Times out flakily on MacOS.
#if defined(OS_MACOSX)
#define MAYBE_OutOfScopePopup DISABLED_OutOfScopePopup
#else
#define MAYBE_OutOfScopePopup OutOfScopePopup
#endif
IN_PROC_BROWSER_TEST_P(BookmarkAppNavigationThrottleCommonBrowserTest,
MAYBE_OutOfScopePopup) {
InstallTestBookmarkApp();
Browser* app_browser = OpenTestBookmarkApp();
base::HistogramTester scoped_histogram;
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();
// Open a popup to an out-of-scope URL.
const GURL out_of_scope_url =
https_server().GetURL(GetAppUrlHost(), GetOutOfScopeUrlPath());
OpenPopupAndWait(app_web_contents, out_of_scope_url);
EXPECT_EQ(++num_browsers, chrome::GetBrowserCount(profile()));
Browser* new_popup_browser = chrome::FindLastActive();
EXPECT_NE(new_popup_browser, browser());
EXPECT_NE(new_popup_browser, app_browser);
EXPECT_TRUE(new_popup_browser->is_type_popup());
EXPECT_FALSE(new_popup_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_popup_web_contents =
new_popup_browser->tab_strip_model()->GetActiveWebContents();
EXPECT_EQ(out_of_scope_url, new_popup_web_contents->GetLastCommittedURL());
ExpectNavigationResultHistogramEquals(
scoped_histogram,
{{BookmarkAppNavigationThrottleResult::
kReparentIntoPopupProceedOutOfScopeInitialNavigation,
1}});
}
// Tests that popups to in-scope URLs are opened in App windows.
// TODO(crbug.com/849163) Times out flakily on MacOS.
#if defined(OS_MACOSX)
#define MAYBE_InScopePopup DISABLED_InScopePopup
#else
#define MAYBE_InScopePopup InScopePopup
#endif
IN_PROC_BROWSER_TEST_P(BookmarkAppNavigationThrottleCommonBrowserTest,
MAYBE_InScopePopup) {
InstallTestBookmarkApp();
Browser* app_browser = OpenTestBookmarkApp();
base::HistogramTester scoped_histogram;
// Open a popup to an out-of-scope URL.
const GURL in_scope_url =
https_server().GetURL(GetAppUrlHost(), GetInScopeUrlPath());
TestAppActionOpensAppWindowWithOpener(
app_browser, in_scope_url,
base::Bind(&OpenPopupAndWait,
app_browser->tab_strip_model()->GetActiveWebContents(),
in_scope_url));
ExpectNavigationResultHistogramEquals(
scoped_histogram,
{{BookmarkAppNavigationThrottleResult::kProceedInAppSameScope, 1}});
}
// Apps are only restored during startup on Chrome OS.
#if defined(OS_CHROMEOS)
IN_PROC_BROWSER_TEST_P(BookmarkAppNavigationThrottleCommonBrowserTest,
PRE_RestoreApp) {
InstallTestBookmarkApp();
OpenTestBookmarkApp();
SessionStartupPref::SetStartupPref(
profile(), SessionStartupPref(SessionStartupPref::LAST));
}
IN_PROC_BROWSER_TEST_P(BookmarkAppNavigationThrottleCommonBrowserTest,
RestoreApp) {
// There should be two windows. The app window and a regular browser window
// the test opens.
EXPECT_EQ(2u, chrome::GetBrowserCount(profile()));
Browser* app_browser = nullptr;
for (auto* browser : *BrowserList::GetInstance()) {
if (browser->is_app()) {
EXPECT_FALSE(app_browser) << "Found multiple app browsers.";
app_browser = browser;
}
}
ASSERT_TRUE(app_browser);
const Extension* app =
ExtensionRegistry::Get(profile())->enabled_extensions().GetByID(
web_app::GetExtensionIdFromApplicationName(app_browser->app_name()));
EXPECT_EQ(GetAppName(), app->name());
}
#endif // OS_CHROMEOS
INSTANTIATE_TEST_CASE_P(
/* no prefix */,
BookmarkAppNavigationThrottleCommonBrowserTest,
testing::Bool());
// Set of tests to make sure form submissions have the correct behavior
// regardless of the kDesktopPWAsLinkCapturing feature flag.
class BookmarkAppNavigationThrottleCommonFormSubmissionBrowserTest
: public BookmarkAppNavigationThrottleBaseCommonBrowserTest,
public ::testing::WithParamInterface<
std::tuple<bool,
StartIn,
std::string,
net::URLFetcher::RequestType,
std::string>> {
public:
void SetUp() override {
EnableFeaturesForTest(std::get<0>(GetParam()));
BookmarkAppNavigationThrottleBaseCommonBrowserTest::SetUp();
}
};
IN_PROC_BROWSER_TEST_P(
BookmarkAppNavigationThrottleCommonFormSubmissionBrowserTest,
FormSubmission) {
bool should_enable_link_capturing;
StartIn start_in;
std::string start_url_path;
net::URLFetcher::RequestType request_type;
std::string target_url_path;
std::tie(should_enable_link_capturing, start_in, start_url_path, request_type,
target_url_path) = GetParam();
InstallTestBookmarkApp();
// Pick where the test should start.
Browser* current_browser;
if (start_in == StartIn::APP)
current_browser = OpenTestBookmarkApp();
else
current_browser = browser();
// If in a regular browser window, navigate to the start URL.
if (start_in == StartIn::BROWSER) {
GURL start_url;
if (start_url_path == GetLaunchingPagePath())
start_url = GetLaunchingPageURL();
else
start_url = https_server().GetURL(GetAppUrlHost(), GetAppUrlPath());
NavigateParams params(current_browser, start_url,
ui::PAGE_TRANSITION_TYPED);
ASSERT_TRUE(TestTabActionDoesNotOpenAppWindow(
start_url, base::BindOnce(&NavigateToURLWrapper, &params)));
} else if (start_url_path == GetLaunchingPagePath()) {
// Return early here since the app window can't start with an out-of-scope
// URL.
return;
}
// If the submit method is GET then add an query params.
GURL target_url = https_server().GetURL(GetAppUrlHost(), target_url_path);
if (request_type == net::URLFetcher::RequestType::GET) {
target_url = AddTestQueryParam(target_url);
}
base::HistogramTester scoped_histogram;
// All form submissions that start in the browser should be kept in the
// browser.
if (start_in == StartIn::BROWSER) {
TestActionDoesNotOpenAppWindow(
current_browser, target_url,
base::BindOnce(
&SubmitFormAndWait,
current_browser->tab_strip_model()->GetActiveWebContents(),
target_url, request_type));
// Navigations to out-of-scope URLs are considered regular navigations and
// therefore not recorded. Nothing gets histogrammed when
// kDesktopPWAsLinkCapturing is disabled, because the navigation is not
// happening in an app window.
if (target_url_path == GetInScopeUrlPath() &&
should_enable_link_capturing) {
ExpectNavigationResultHistogramEquals(
scoped_histogram, {{BookmarkAppNavigationThrottleResult::
kProceedInBrowserFormSubmission,
1}});
}
return;
}
// When in an app, in-scope navigations should always be kept in the app
// window.
if (target_url_path == GetInScopeUrlPath()) {
TestActionDoesNotOpenAppWindow(
current_browser, target_url,
base::BindOnce(
&SubmitFormAndWait,
current_browser->tab_strip_model()->GetActiveWebContents(),
target_url, request_type));
ExpectNavigationResultHistogramEquals(
scoped_histogram,
{{BookmarkAppNavigationThrottleResult::kProceedInAppSameScope, 1}});
return;
}
// When in an app, out-of-scope navigations should always open a new
// foreground tab.
DCHECK_EQ(target_url_path, GetOutOfScopeUrlPath());
TestAppActionOpensForegroundTab(
current_browser, target_url,
base::BindOnce(&SubmitFormAndWait,
current_browser->tab_strip_model()->GetActiveWebContents(),
target_url, request_type));
ExpectNavigationResultHistogramEquals(
scoped_histogram,
{{BookmarkAppNavigationThrottleResult::kDeferOpenNewTabInAppOutOfScope,
1}});
}
INSTANTIATE_TEST_CASE_P(
/* no prefix */,
BookmarkAppNavigationThrottleCommonFormSubmissionBrowserTest,
testing::Combine(
testing::Bool(),
testing::Values(StartIn::BROWSER, StartIn::APP),
testing::Values(
BookmarkAppNavigationBrowserTest::GetLaunchingPagePath(),
BookmarkAppNavigationBrowserTest::GetAppUrlPath()),
testing::Values(net::URLFetcher::RequestType::GET,
net::URLFetcher::RequestType::POST),
testing::Values(
BookmarkAppNavigationBrowserTest::GetOutOfScopeUrlPath(),
BookmarkAppNavigationBrowserTest::GetInScopeUrlPath())));
// Tests that the result is the same regardless of the 'rel' attribute of links
// and of the kDesktopPWAsLinkCapturing feature flag.
class BookmarkAppNavigationThrottleCommonLinkBrowserTest
: public BookmarkAppNavigationThrottleBaseCommonBrowserTest,
public ::testing::WithParamInterface<std::tuple<bool, std::string>> {
public:
void SetUp() override {
EnableFeaturesForTest(std::get<0>(GetParam()));
BookmarkAppNavigationThrottleBaseCommonBrowserTest::SetUp();
}
};
// Tests that clicking links inside the app to in-scope URLs doesn't open new
// browser windows.
IN_PROC_BROWSER_TEST_P(BookmarkAppNavigationThrottleCommonLinkBrowserTest,
InAppInScopeNavigation) {
InstallTestBookmarkApp();
Browser* app_browser = OpenTestBookmarkApp();
content::WebContents* app_web_contents =
app_browser->tab_strip_model()->GetActiveWebContents();
int num_tabs_browser = browser()->tab_strip_model()->count();
int num_tabs_app_browser = app_browser->tab_strip_model()->count();
size_t num_browsers = chrome::GetBrowserCount(profile());
base::HistogramTester scoped_histogram;
const GURL in_scope_url =
https_server().GetURL(GetAppUrlHost(), GetInScopeUrlPath());
ClickLinkAndWait(app_web_contents, in_scope_url, LinkTarget::SELF,
std::get<1>(GetParam()));
EXPECT_EQ(num_tabs_browser, browser()->tab_strip_model()->count());
EXPECT_EQ(num_tabs_app_browser, app_browser->tab_strip_model()->count());
EXPECT_EQ(num_browsers, chrome::GetBrowserCount(profile()));
EXPECT_EQ(app_browser, chrome::FindLastActive());
EXPECT_EQ(in_scope_url, app_web_contents->GetLastCommittedURL());
ExpectNavigationResultHistogramEquals(
scoped_histogram,
{{BookmarkAppNavigationThrottleResult::kProceedInAppSameScope, 1}});
}
// Tests that clicking links inside the app to out-of-scope URLs opens a new
// tab in an existing browser window, instead of navigating the existing
// app window.
IN_PROC_BROWSER_TEST_P(BookmarkAppNavigationThrottleCommonLinkBrowserTest,
InAppOutOfScopeNavigation) {
InstallTestBookmarkApp();
Browser* app_browser = OpenTestBookmarkApp();
const base::HistogramTester scoped_histogram;
const GURL out_of_scope_url =
https_server().GetURL(GetAppUrlHost(), GetOutOfScopeUrlPath());
TestAppActionOpensForegroundTab(
app_browser, out_of_scope_url,
base::BindOnce(&ClickLinkAndWait,
app_browser->tab_strip_model()->GetActiveWebContents(),
out_of_scope_url, LinkTarget::SELF,
std::get<1>(GetParam())));
ExpectNavigationResultHistogramEquals(
scoped_histogram,
{{BookmarkAppNavigationThrottleResult::kDeferOpenNewTabInAppOutOfScope,
1}});
}
INSTANTIATE_TEST_CASE_P(
/* no prefix */,
BookmarkAppNavigationThrottleCommonLinkBrowserTest,
testing::Combine(
testing::Bool(),
testing::Values("", "noopener", "noreferrer", "nofollow")));
} // namespace test
} // namespace extensions