blob: 5198d0531f1fe11f7bddd62cca2ca49b1e024ccc [file] [log] [blame] [edit]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/390223051): Remove C-library calls to fix the errors.
#pragma allow_unsafe_libc_calls
#endif
#include "chrome/browser/ui/browser_navigator_browsertest.h"
#include <memory>
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/file_system_access/file_system_access_features.h"
#include "chrome/browser/prefs/incognito_mode_prefs.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/renderer_host/chrome_navigation_ui_data.h"
#include "chrome/browser/search/search.h"
#include "chrome/browser/ui/browser.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/browser_navigator.h"
#include "chrome/browser/ui/browser_navigator_params.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/browser/ui/location_bar/location_bar.h"
#include "chrome/browser/ui/page_info/chrome_page_info_delegate.h"
#include "chrome/browser/ui/search/ntp_test_utils.h"
#include "chrome/browser/ui/singleton_tabs.h"
#include "chrome/browser/ui/tabs/tab_enums.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/captive_portal/core/buildflags.h"
#include "components/content_settings/core/common/features.h"
#include "components/omnibox/browser/omnibox_edit_model.h"
#include "components/omnibox/browser/omnibox_view.h"
#include "components/omnibox/browser/tab_matcher.h"
#include "components/policy/core/common/policy_pref_names.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/bindings_policy.h"
#include "content/public/common/content_features.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/no_renderer_crashes_assertion.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 "net/base/features.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/request_handler_util.h"
#include "net/test/scoped_mutually_exclusive_feature_list.h"
#include "services/network/public/cpp/resource_request_body.h"
#include "third_party/blink/public/common/features.h"
#include "ui/display/screen_base.h"
#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
#include "components/captive_portal/content/captive_portal_tab_helper.h"
#endif
#if BUILDFLAG(IS_CHROMEOS)
#include "ash/shell.h"
#include "ui/display/test/display_manager_test_api.h"
#endif // BUILDFLAG(IS_CHROMEOS)
#if BUILDFLAG(IS_OZONE)
#include "ui/ozone/public/ozone_platform.h"
#endif
using content::WebContents;
namespace {
const char kExpectedTitle[] = "PASSED!";
const char16_t kExpectedTitle16[] = u"PASSED!";
const char kEchoTitleCommand[] = "/echotitle";
GURL GetGoogleURL() {
return GURL("http://www.google.com/");
}
GURL GetSettingsURL() {
return GURL(chrome::kChromeUISettingsURL);
}
GURL GetContentSettingsURL() {
return GetSettingsURL().Resolve(chrome::kContentSettingsSubPage);
}
GURL GetClearBrowsingDataURL() {
return GetSettingsURL().Resolve(chrome::kClearBrowserDataSubPage);
}
void ShowSettings(Browser* browser) {
// chrome::ShowSettings just calls ShowSettingsSubPageInTabbedBrowser on
// non chromeos, but we want to test tab navigation here so call
// ShowSettingsSubPageInTabbedBrowser directly.
chrome::ShowSettingsSubPageInTabbedBrowser(browser, std::string());
}
// Intercepts requests and makes them 302 to the 'redirect' query parameter
// if it's set. If the 'popin-policy' query parameter is set, it sets the
// 'Popin-Policy' response header to that value.
std::unique_ptr<net::test_server::HttpResponse> PopinRequestHandler(
const net::test_server::HttpRequest& request) {
auto response = std::make_unique<net::test_server::BasicHttpResponse>();
response->set_content_type("text/html");
response->set_content("<html></html");
net::test_server::RequestQuery query =
net::test_server::ParseQuery(request.GetURL());
if (query.find("redirect") != query.end()) {
response->AddCustomHeader("Location", query["redirect"][0]);
response->set_code(net::HTTP_FOUND);
} else {
response->set_code(net::HTTP_OK);
}
if (query.find("popin_policy") != query.end()) {
std::string proposed_policy = query["popin_policy"][0];
// We need to fixup the dynamic port so that the policies can match.
base::ReplaceSubstringsAfterOffset(&proposed_policy, 0, "a.test",
"a.test:" + request.GetURL().port());
response->AddCustomHeader("Popin-Policy", "partitioned=" + proposed_policy);
}
return response;
}
} // namespace
BrowserNavigatorTest::BrowserNavigatorTest() {
scoped_feature_list_.InitWithFeatures(
{
features::kFileSystemAccessPersistentPermissions,
blink::features::kPartitionedPopins,
content_settings::features::kTrackingProtection3pcd,
},
{});
}
void BrowserNavigatorTest::SetUpOnMainThread() {
host_resolver()->AddRule("*", "127.0.0.1");
}
NavigateParams BrowserNavigatorTest::MakeNavigateParams() const {
return MakeNavigateParams(browser());
}
NavigateParams BrowserNavigatorTest::MakeNavigateParams(
Browser* browser) const {
NavigateParams params(browser, GetGoogleURL(), ui::PAGE_TRANSITION_LINK);
params.window_action = NavigateParams::SHOW_WINDOW;
return params;
}
bool BrowserNavigatorTest::OpenPOSTURLInNewForegroundTabAndGetTitle(
const GURL& url,
const std::string& post_data,
bool is_browser_initiated,
std::u16string* title) {
NavigateParams param(MakeNavigateParams());
param.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
param.url = url;
param.initiator_origin = url::Origin();
param.is_renderer_initiated = !is_browser_initiated;
param.post_data = network::ResourceRequestBody::CreateFromCopyOfBytes(
base::as_byte_span(post_data));
ui_test_utils::NavigateToURL(&param);
if (!param.navigated_or_inserted_contents) {
return false;
}
// Navigate() should have opened the contents in new foreground tab in the
// current Browser.
EXPECT_EQ(browser(), param.browser);
EXPECT_EQ(browser()->tab_strip_model()->GetActiveWebContents(),
param.navigated_or_inserted_contents);
// We should have one window, with one tab.
EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
EXPECT_EQ(2, browser()->tab_strip_model()->count());
*title = param.navigated_or_inserted_contents->GetTitle();
return true;
}
Browser* BrowserNavigatorTest::CreateEmptyBrowserForType(Browser::Type type,
Profile* profile) {
Browser* browser =
Browser::Create(Browser::CreateParams(type, profile, true));
chrome::AddTabAt(browser, GURL(), -1, true);
return browser;
}
Browser* BrowserNavigatorTest::CreateEmptyBrowserForApp(Profile* profile) {
Browser* browser = Browser::Create(Browser::CreateParams::CreateForApp(
"Test", false /* trusted_source */, gfx::Rect(), profile, true));
chrome::AddTabAt(browser, GURL(), -1, true);
return browser;
}
std::unique_ptr<WebContents> BrowserNavigatorTest::CreateWebContents(
bool initialize_renderer) {
WebContents::CreateParams create_params(browser()->profile());
create_params.desired_renderer_state =
initialize_renderer
? WebContents::CreateParams::kInitializeAndWarmupRendererProcess
: WebContents::CreateParams::kOkayToHaveRendererProcess;
return WebContents::Create(create_params);
}
void BrowserNavigatorTest::RunSuppressTest(WindowOpenDisposition disposition) {
GURL old_url = browser()->tab_strip_model()->GetActiveWebContents()->GetURL();
NavigateParams params(MakeNavigateParams());
params.disposition = disposition;
Navigate(&params);
// Nothing should have happened as a result of Navigate();
EXPECT_EQ(1, browser()->tab_strip_model()->count());
EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
EXPECT_EQ(old_url,
browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
}
void BrowserNavigatorTest::RunUseNonIncognitoWindowTest(
const GURL& url,
const ui::PageTransition& page_transition) {
Browser* incognito_browser = CreateIncognitoBrowser();
EXPECT_EQ(2u, chrome::GetTotalBrowserCount());
EXPECT_EQ(1, browser()->tab_strip_model()->count());
EXPECT_EQ(1, incognito_browser->tab_strip_model()->count());
// Navigate to the page.
NavigateParams params(MakeNavigateParams(incognito_browser));
params.disposition = WindowOpenDisposition::SINGLETON_TAB;
params.url = url;
params.window_action = NavigateParams::SHOW_WINDOW;
params.transition = page_transition;
Navigate(&params);
// This page should be opened in browser() window.
EXPECT_NE(incognito_browser, params.browser);
EXPECT_EQ(browser(), params.browser);
EXPECT_EQ(2, browser()->tab_strip_model()->count());
EXPECT_EQ(url,
browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
}
void BrowserNavigatorTest::RunDoNothingIfIncognitoIsForcedTest(
const GURL& url) {
Browser* browser = CreateIncognitoBrowser();
// Set kIncognitoModeAvailability to FORCED.
PrefService* prefs1 = browser->profile()->GetPrefs();
prefs1->SetInteger(
policy::policy_prefs::kIncognitoModeAvailability,
static_cast<int>(policy::IncognitoModeAvailability::kForced));
PrefService* prefs2 = browser->profile()->GetOriginalProfile()->GetPrefs();
prefs2->SetInteger(
policy::policy_prefs::kIncognitoModeAvailability,
static_cast<int>(policy::IncognitoModeAvailability::kForced));
// Navigate to the page.
NavigateParams params(MakeNavigateParams(browser));
params.disposition = WindowOpenDisposition::OFF_THE_RECORD;
params.url = url;
params.window_action = NavigateParams::SHOW_WINDOW;
Navigate(&params);
// The page should not be opened.
EXPECT_EQ(browser, params.browser);
EXPECT_EQ(1, browser->tab_strip_model()->count());
EXPECT_EQ(GURL(url::kAboutBlankURL),
browser->tab_strip_model()->GetActiveWebContents()->GetURL());
}
// Subclass of TestNavigationObserver that saves ChromeNavigationUIData.
class TestNavigationUIDataObserver : public content::TestNavigationObserver {
public:
// Creates an observer that watches navigations to |target_url| on
// existing and newly added WebContents.
explicit TestNavigationUIDataObserver(const GURL& target_url)
: content::TestNavigationObserver(target_url) {
WatchExistingWebContents();
StartWatchingNewWebContents();
}
const ChromeNavigationUIData* last_navigation_ui_data() const {
return static_cast<ChromeNavigationUIData*>(last_navigation_ui_data_.get());
}
private:
void OnDidFinishNavigation(
content::NavigationHandle* navigation_handle) override {
last_navigation_ui_data_ =
navigation_handle->GetNavigationUIData()->Clone();
content::TestNavigationObserver::OnDidFinishNavigation(navigation_handle);
}
std::unique_ptr<content::NavigationUIData> last_navigation_ui_data_;
};
Browser* BrowserNavigatorTest::NavigateHelper(const GURL& url,
Browser* browser,
WindowOpenDisposition disposition,
bool wait_for_navigation,
WebContents* expected_contents) {
// If this should navigate the current tab, than assume that the WebContents
// will be the same one. This is a convenience for the common case.
if (disposition == WindowOpenDisposition::CURRENT_TAB) {
EXPECT_FALSE(expected_contents);
expected_contents = browser->tab_strip_model()->GetActiveWebContents();
}
std::optional<content::CreateAndLoadWebContentsObserver> new_tab_observer;
std::optional<content::LoadStopObserver> load_stop_observer;
if (wait_for_navigation) {
if (expected_contents) {
load_stop_observer.emplace(expected_contents);
} else {
new_tab_observer.emplace();
}
}
NavigateParams params(MakeNavigateParams(browser));
params.disposition = disposition;
params.url = url;
params.window_action = NavigateParams::SHOW_WINDOW;
Navigate(&params);
if (load_stop_observer) {
load_stop_observer->Wait();
}
if (new_tab_observer) {
new_tab_observer->Wait();
}
return params.browser;
}
namespace {
// This test verifies that when a navigation occurs within a tab, the tab count
// of the Browser remains the same and the current tab bears the loaded URL.
// Note that network URLs are not actually loaded in tests, so this also tests
// that error pages leave the intended URL in the address bar.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, Disposition_CurrentTab) {
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetGoogleURL()));
EXPECT_EQ(GetGoogleURL(),
browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
// We should have one window with one tab.
EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
EXPECT_EQ(1, browser()->tab_strip_model()->count());
}
// This test verifies that a singleton tab is refocused if one is already opened
// in another or an existing window, or added if it is not.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, Disposition_SingletonTabExisting) {
const GURL singleton_url1("http://maps.google.com/");
chrome::AddSelectedTabWithURL(browser(), singleton_url1,
ui::PAGE_TRANSITION_LINK);
chrome::AddSelectedTabWithURL(browser(), GetGoogleURL(),
ui::PAGE_TRANSITION_LINK);
// We should have one browser with 3 tabs, the 3rd selected.
EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
EXPECT_EQ(3, browser()->tab_strip_model()->count());
EXPECT_EQ(2, browser()->tab_strip_model()->active_index());
// Navigate to singleton_url1.
NavigateParams params(MakeNavigateParams());
params.disposition = WindowOpenDisposition::SINGLETON_TAB;
params.url = singleton_url1;
Navigate(&params);
// The middle tab should now be selected.
EXPECT_EQ(browser(), params.browser);
EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
// No tab contents should have been created
EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
EXPECT_EQ(3, browser()->tab_strip_model()->count());
}
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
Disposition_SingletonTabNoneExisting) {
const GURL singleton_url1("http://maps.google.com/");
// We should have one browser with 1 tab.
EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
// Navigate to singleton_url1.
NavigateParams params(MakeNavigateParams());
params.disposition = WindowOpenDisposition::SINGLETON_TAB;
params.url = singleton_url1;
Navigate(&params);
// We should now have 2 tabs, the 2nd one selected.
EXPECT_EQ(browser(), params.browser);
EXPECT_EQ(2, browser()->tab_strip_model()->count());
EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
}
// This test verifies that when a navigation results in a foreground tab, the
// tab count of the Browser increases and the selected tab shifts to the new
// foreground tab.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, Disposition_NewForegroundTab) {
WebContents* old_contents =
browser()->tab_strip_model()->GetActiveWebContents();
NavigateParams params(MakeNavigateParams());
params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
Navigate(&params);
EXPECT_NE(old_contents, browser()->tab_strip_model()->GetActiveWebContents());
EXPECT_EQ(browser()->tab_strip_model()->GetActiveWebContents(),
params.navigated_or_inserted_contents);
EXPECT_EQ(2, browser()->tab_strip_model()->count());
}
// This test verifies that when a navigation results in a background tab, the
// tab count of the Browser increases but the selected tab remains the same.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, Disposition_NewBackgroundTab) {
WebContents* old_contents =
browser()->tab_strip_model()->GetActiveWebContents();
NavigateParams params(MakeNavigateParams());
params.disposition = WindowOpenDisposition::NEW_BACKGROUND_TAB;
Navigate(&params);
WebContents* new_contents =
browser()->tab_strip_model()->GetActiveWebContents();
// The selected tab should have remained unchanged, since the new tab was
// opened in the background.
EXPECT_EQ(old_contents, new_contents);
EXPECT_EQ(2, browser()->tab_strip_model()->count());
}
// This test verifies that when a navigation requiring a new foreground tab
// occurs in a Browser that cannot host multiple tabs, the new foreground tab
// is created in an existing compatible Browser.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
Disposition_IncompatibleWindow_Existing) {
// Open a foreground tab in a window that cannot open popups when there is an
// existing compatible window somewhere else that they can be opened within.
Browser* popup =
CreateEmptyBrowserForType(Browser::TYPE_POPUP, browser()->profile());
NavigateParams params(MakeNavigateParams(popup));
params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
Navigate(&params);
// Navigate() should have opened the tab in a different browser since the
// one we supplied didn't support additional tabs.
EXPECT_NE(popup, params.browser);
// Since browser() is an existing compatible tabbed browser, it should have
// opened the tab there.
EXPECT_EQ(browser(), params.browser);
// We should be left with 2 windows, the popup with one tab and the browser()
// provided by the framework with two.
EXPECT_EQ(2u, chrome::GetTotalBrowserCount());
EXPECT_EQ(1, popup->tab_strip_model()->count());
EXPECT_EQ(2, browser()->tab_strip_model()->count());
}
// This test verifies that when a navigation requiring a new foreground tab
// occurs in a Browser that cannot host multiple tabs and no compatible Browser
// that can is open, a compatible Browser is created.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
Disposition_IncompatibleWindow_NoExisting) {
// We want to simulate not being able to find an existing window compatible
// with our non-tabbed browser window so Navigate() is forced to create a
// new compatible window. Because browser() supplied by the in-process
// browser testing framework is compatible with browser()->profile(), we
// need a different profile, and creating a popup window with an incognito
// profile is a quick and dirty way of achieving this.
Browser* popup = CreateEmptyBrowserForType(
Browser::TYPE_POPUP,
browser()->profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true));
NavigateParams params(MakeNavigateParams(popup));
params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
Navigate(&params);
// Navigate() should have opened the tab in a different browser since the
// one we supplied didn't support additional tabs.
EXPECT_NE(popup, params.browser);
// This time, browser() is _not_ compatible with popup since it is not an
// incognito window.
EXPECT_NE(browser(), params.browser);
// We should have three windows, each with one tab:
// 1. the browser() provided by the framework (unchanged in this test)
// 2. the incognito popup we created originally
// 3. the new incognito tabbed browser that was created by Navigate().
EXPECT_EQ(3u, chrome::GetTotalBrowserCount());
EXPECT_EQ(1, browser()->tab_strip_model()->count());
EXPECT_EQ(1, popup->tab_strip_model()->count());
EXPECT_EQ(1, params.browser->tab_strip_model()->count());
EXPECT_TRUE(params.browser->is_type_normal());
EXPECT_TRUE(params.browser->window()->IsToolbarVisible());
}
// This test verifies that navigating with WindowOpenDisposition = NEW_POPUP
// from a normal Browser results in a new Browser with TYPE_POPUP.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, Disposition_NewPopup) {
NavigateParams params(MakeNavigateParams());
params.disposition = WindowOpenDisposition::NEW_POPUP;
params.window_features.bounds = gfx::Rect(0, 0, 200, 200);
// Wait for new popup to to load and gain focus.
ui_test_utils::NavigateToURL(&params);
// Navigate() should have opened a new, focused popup window, with a toolbar.
EXPECT_NE(browser(), params.browser);
#if 0
// TODO(stevenjb): Enable this test. See: crbug.com/79493
EXPECT_TRUE(browser->window()->IsActive());
#endif
EXPECT_TRUE(params.browser->is_type_popup());
EXPECT_TRUE(params.browser->window()->IsToolbarVisible());
// We should have two windows, the browser() provided by the framework and the
// new popup window.
EXPECT_EQ(2u, chrome::GetTotalBrowserCount());
EXPECT_EQ(1, browser()->tab_strip_model()->count());
EXPECT_EQ(1, params.browser->tab_strip_model()->count());
}
// This test verifies that navigating with WindowOpenDisposition = NEW_POPUP
// from a (kind of app) Browser results in a new Browser with TYPE_APP_POPUP.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, Disposition_NewPopup_ExtensionId) {
NavigateParams params(MakeNavigateParams());
params.disposition = WindowOpenDisposition::NEW_POPUP;
params.app_id = "extensionappid";
params.window_features.bounds = gfx::Rect(0, 0, 200, 200);
// Wait for new popup to to load and gain focus.
ui_test_utils::NavigateToURL(&params);
// Navigate() should have opened a new, focused TYPE_APP_POPUP window with no
// toolbar.
EXPECT_NE(browser(), params.browser);
EXPECT_TRUE(params.browser->is_type_app_popup());
EXPECT_FALSE(params.browser->window()->IsToolbarVisible());
// We should have two windows, the browser() provided by the framework and the
// new popup window.
EXPECT_EQ(2u, chrome::GetTotalBrowserCount());
EXPECT_EQ(1, browser()->tab_strip_model()->count());
EXPECT_EQ(1, params.browser->tab_strip_model()->count());
}
// This test verifies that navigating with WindowOpenDisposition = NEW_POPUP
// from a normal popup results in a new Browser with TYPE_POPUP.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, Disposition_NewPopupFromPopup) {
// Open a popup.
NavigateParams params1(MakeNavigateParams());
params1.disposition = WindowOpenDisposition::NEW_POPUP;
params1.window_features.bounds = gfx::Rect(0, 0, 200, 200);
Navigate(&params1);
// Open another popup.
NavigateParams params2(MakeNavigateParams(params1.browser));
params2.disposition = WindowOpenDisposition::NEW_POPUP;
params2.window_features.bounds = gfx::Rect(0, 0, 200, 200);
Navigate(&params2);
// Navigate() should have opened a new normal popup window.
EXPECT_NE(params1.browser, params2.browser);
EXPECT_TRUE(params2.browser->is_type_popup());
EXPECT_TRUE(params2.browser->window()->IsToolbarVisible());
// We should have three windows, the browser() provided by the framework,
// the first popup window, and the second popup window.
EXPECT_EQ(3u, chrome::GetTotalBrowserCount());
EXPECT_EQ(1, browser()->tab_strip_model()->count());
EXPECT_EQ(1, params1.browser->tab_strip_model()->count());
EXPECT_EQ(1, params2.browser->tab_strip_model()->count());
}
// This test verifies that navigating with WindowOpenDisposition = NEW_POPUP
// from an app frame results in a new Browser with TYPE_APP_POPUP.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
Disposition_NewPopupFromAppWindow) {
Browser* app_browser = CreateEmptyBrowserForApp(browser()->profile());
NavigateParams params(MakeNavigateParams(app_browser));
params.disposition = WindowOpenDisposition::NEW_POPUP;
params.window_features.bounds = gfx::Rect(0, 0, 200, 200);
Navigate(&params);
// Navigate() should have opened a new TYPE_APP_POPUP window with no toolbar.
EXPECT_NE(app_browser, params.browser);
EXPECT_NE(browser(), params.browser);
EXPECT_TRUE(params.browser->is_type_app_popup());
EXPECT_FALSE(params.browser->window()->IsToolbarVisible());
// We should now have three windows, the app window, the app popup it created,
// and the original browser() provided by the framework.
EXPECT_EQ(3u, chrome::GetTotalBrowserCount());
EXPECT_EQ(1, browser()->tab_strip_model()->count());
EXPECT_EQ(1, app_browser->tab_strip_model()->count());
EXPECT_EQ(1, params.browser->tab_strip_model()->count());
}
// This test verifies that navigating with WindowOpenDisposition = NEW_POPUP
// from an app popup results in a new Browser also of TYPE_APP_POPUP.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, Disposition_NewPopupFromAppPopup) {
Browser* app_browser = CreateEmptyBrowserForApp(browser()->profile());
// Open an app popup.
NavigateParams params1(MakeNavigateParams(app_browser));
params1.disposition = WindowOpenDisposition::NEW_POPUP;
params1.window_features.bounds = gfx::Rect(0, 0, 200, 200);
Navigate(&params1);
// Now open another app popup.
NavigateParams params2(MakeNavigateParams(params1.browser));
params2.disposition = WindowOpenDisposition::NEW_POPUP;
params2.window_features.bounds = gfx::Rect(0, 0, 200, 200);
Navigate(&params2);
// Navigate() should have opened a new popup app window.
EXPECT_NE(browser(), params1.browser);
EXPECT_NE(params1.browser, params2.browser);
EXPECT_TRUE(params2.browser->is_type_app_popup());
EXPECT_FALSE(params2.browser->window()->IsToolbarVisible());
// We should now have four windows, the app window, the first app popup,
// the second app popup, and the original browser() provided by the framework.
EXPECT_EQ(4u, chrome::GetTotalBrowserCount());
EXPECT_EQ(1, browser()->tab_strip_model()->count());
EXPECT_EQ(1, app_browser->tab_strip_model()->count());
EXPECT_EQ(1, params1.browser->tab_strip_model()->count());
EXPECT_EQ(1, params2.browser->tab_strip_model()->count());
}
// This test verifies that navigating with WindowOpenDisposition = NEW_POPUP
// from an extension app tab results in a new Browser with TYPE_APP_POPUP.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
Disposition_NewPopupFromExtensionApp) {
// TODO(beng): TBD.
}
// This test verifies that navigating with window_action = SHOW_WINDOW_INACTIVE
// does not focus a new new popup window.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, Disposition_NewPopupUnfocused) {
NavigateParams params(MakeNavigateParams());
params.disposition = WindowOpenDisposition::NEW_POPUP;
params.window_features.bounds = gfx::Rect(0, 0, 200, 200);
params.window_action = NavigateParams::SHOW_WINDOW_INACTIVE;
// Wait for new popup to load (and gain focus if the test fails).
ui_test_utils::NavigateToURL(&params);
// Navigate() should have opened a new, unfocused, popup window.
EXPECT_NE(browser(), params.browser);
EXPECT_TRUE(params.browser->is_type_popup());
EXPECT_TRUE(params.browser->window()->IsToolbarVisible());
#if 0
// TODO(stevenjb): Enable this test. See: crbug.com/79493
EXPECT_FALSE(p.browser->window()->IsActive());
#endif
}
// This test verifies that navigating with WindowOpenDisposition = NEW_POPUP
// and trusted_source = true results in a new Browser where is_trusted_source()
// is true.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, Disposition_NewPopupTrusted) {
NavigateParams params(MakeNavigateParams());
params.disposition = WindowOpenDisposition::NEW_POPUP;
params.trusted_source = true;
params.window_features.bounds = gfx::Rect(0, 0, 200, 200);
// Wait for new popup to to load and gain focus.
ui_test_utils::NavigateToURL(&params);
// Navigate() should have opened a new popup window of TYPE_POPUP with no
// toolbar.
EXPECT_NE(browser(), params.browser);
EXPECT_TRUE(params.browser->is_type_popup());
EXPECT_TRUE(params.browser->is_trusted_source());
EXPECT_FALSE(params.browser->window()->IsToolbarVisible());
}
#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
// This test verifies that navigating with WindowOpenDisposition = NEW_POPUP
// and NavigateParams::CaptivePortalWindowType::kPopup results in a new
// WebContents where is_captive_portal_window() is true.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
Disposition_NewPopupCaptivePortal) {
NavigateParams params(MakeNavigateParams());
params.disposition = WindowOpenDisposition::NEW_POPUP;
params.captive_portal_window_type =
captive_portal::CaptivePortalWindowType::kPopup;
params.window_features.bounds = gfx::Rect(0, 0, 200, 200);
// Wait for new popup to to load and gain focus.
ui_test_utils::NavigateToURL(&params);
// Navigate() should have opened a new popup window of TYPE_POPUP with a
// toolbar.
EXPECT_NE(browser(), params.browser);
EXPECT_TRUE(params.browser->is_type_popup());
EXPECT_TRUE(params.browser->window()->IsToolbarVisible());
EXPECT_TRUE(captive_portal::CaptivePortalTabHelper::FromWebContents(
params.navigated_or_inserted_contents)
->is_captive_portal_window());
}
#endif
// This test verifies that navigating with WindowOpenDisposition = NEW_POPUP
// and is_tab_modal_popup_deprecated = true results in a new WebContents that is
// a popup and behaves like a tab modal.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, Disposition_NewPopupTabModal) {
NavigateParams params(MakeNavigateParams());
params.disposition = WindowOpenDisposition::NEW_POPUP;
params.is_tab_modal_popup_deprecated = true;
params.window_features.bounds = gfx::Rect(0, 0, 200, 200);
// Wait for new popup to load and gain focus.
ui_test_utils::NavigateToURL(&params);
// Add a new tab.
chrome::AddTabAt(browser(), GURL("about:blank"), -1, true);
// Switch to the new tab.
browser()->tab_strip_model()->ActivateTabAt(1);
// Verify the popup window is hidden.
EXPECT_FALSE(params.browser->window()->IsVisible());
// Switch back to the original tab.
browser()->tab_strip_model()->ActivateTabAt(0);
// Verify the popup window is visible again.
EXPECT_TRUE(params.browser->window()->IsVisible());
// Verify the popup window is set as tab model popup.
EXPECT_TRUE(params.browser->window()->IsTabModalPopupDeprecated());
}
// This test verifies that navigating with WindowOpenDisposition = NEW_WINDOW
// always opens a new window.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, Disposition_NewWindow) {
NavigateParams params(MakeNavigateParams());
params.disposition = WindowOpenDisposition::NEW_WINDOW;
Navigate(&params);
// Navigate() should have opened a new toplevel window.
EXPECT_NE(browser(), params.browser);
EXPECT_TRUE(params.browser->is_type_normal());
EXPECT_TRUE(params.browser->window()->IsToolbarVisible());
// We should now have two windows, the browser() provided by the framework and
// the new normal window.
EXPECT_EQ(2u, chrome::GetTotalBrowserCount());
EXPECT_EQ(1, browser()->tab_strip_model()->count());
EXPECT_EQ(1, params.browser->tab_strip_model()->count());
}
// This test verifies that a source tab to the left of the target tab can
// be switched away from and closed. It verifies that if we close the
// earlier tab, that we don't use a stale index, and select the wrong tab.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, OutOfOrderTabSwitchTest) {
const GURL singleton_url("http://maps.google.com/");
NavigateHelper(singleton_url, browser(),
WindowOpenDisposition::NEW_FOREGROUND_TAB, true);
WebContents* new_tab = browser()->tab_strip_model()->GetActiveWebContents();
browser()->tab_strip_model()->ActivateTabAt(
0, TabStripUserGestureDetails(
TabStripUserGestureDetails::GestureType::kOther));
NavigateHelper(singleton_url, browser(), WindowOpenDisposition::SWITCH_TO_TAB,
false, new_tab);
}
// This test verifies the two cases of attempting to switch to a tab that no
// longer exists: if NTP, load in current tab, otherwise load in new
// foreground tab.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, NavigateOnTabSwitchLostTest) {
const GURL singleton_url("chrome://dino");
WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
NavigateHelper(singleton_url, browser(), WindowOpenDisposition::SWITCH_TO_TAB,
true, tab);
EXPECT_EQ(1, browser()->tab_strip_model()->count());
NavigateHelper(GURL("chrome://about"), browser(),
WindowOpenDisposition::NEW_FOREGROUND_TAB, true);
int previous_tab_count = browser()->tab_strip_model()->count();
browser()->tab_strip_model()->CloseWebContentsAt(0,
TabCloseTypes::CLOSE_NONE);
EXPECT_EQ(previous_tab_count - 1, browser()->tab_strip_model()->count());
// This expects a new WebContents, since we just closed the tab.
NavigateHelper(singleton_url, browser(), WindowOpenDisposition::SWITCH_TO_TAB,
true, nullptr /* expected_contents */);
EXPECT_EQ(2, browser()->tab_strip_model()->count());
}
// This test verifies that SWITCH_TO_TAB will switch to a tab even if the scheme
// mismatches, as long as the rest of the URL does.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, SchemeMismatchTabSwitchTest) {
GURL navigate_url("https://www.chromium.org/");
GURL search_url("http://www.chromium.org/");
GURL dino_url("chrome://dino");
NavigateHelper(navigate_url, browser(), WindowOpenDisposition::CURRENT_TAB,
true);
NavigateHelper(dino_url, browser(), WindowOpenDisposition::NEW_FOREGROUND_TAB,
true);
// We must be on another tab than the target for it to be found and
// switched to. To meet that requirement, ensure the dino tab is currently
// active.
EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
NavigateHelper(search_url, browser(), WindowOpenDisposition::SWITCH_TO_TAB,
false);
EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
}
// Make sure that switching tabs preserves the post-focus state (of the
// content area) of the previous tab.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, SaveAfterFocusTabSwitchTest) {
GURL first_url("chrome://dino/");
GURL second_url("chrome://history/");
NavigateHelper(first_url, browser(), WindowOpenDisposition::CURRENT_TAB,
true);
// Generate history so the tab isn't closed.
NavigateHelper(second_url, browser(),
WindowOpenDisposition::NEW_FOREGROUND_TAB, true);
LocationBar* location_bar = browser()->window()->GetLocationBar();
location_bar->FocusLocation(true);
NavigateHelper(first_url, browser(), WindowOpenDisposition::SWITCH_TO_TAB,
false);
browser()->tab_strip_model()->ActivateTabAt(
1, TabStripUserGestureDetails(
TabStripUserGestureDetails::GestureType::kOther));
OmniboxView* omnibox_view = location_bar->GetOmniboxView();
EXPECT_EQ(omnibox_view->model()->focus_state(),
OmniboxFocusState::OMNIBOX_FOCUS_NONE);
}
// This test verifies that we're picking the correct browser and tab to
// switch to. It verifies that we don't recommend the active tab, and that,
// when switching, we don't mistakenly pick the current browser. Note that this
// test checks which window the new tab was created in, but does not check
// whether the target window was activated - that would require a much slower
// interactive UI test, since we'd have to wait for the async window activation
// to complete to avoid flakes.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, SwitchToTabCorrectWindow) {
const GURL url1("http://example1.chromium.org");
const GURL url2("http://example2.chromium.org");
// Make singleton tab.
Browser* browser1 =
NavigateHelper(url1, browser(), WindowOpenDisposition::CURRENT_TAB, true);
// Make a new window with different URL.
Browser* browser2 =
NavigateHelper(url2, browser1, WindowOpenDisposition::NEW_WINDOW, true);
EXPECT_NE(browser1, browser2);
EXPECT_EQ(browser1,
NavigateHelper(url1, browser2, WindowOpenDisposition::SWITCH_TO_TAB,
false));
EXPECT_EQ(browser2,
NavigateHelper(url2, browser1, WindowOpenDisposition::SWITCH_TO_TAB,
false));
}
// TODO(crbug.com/40806044): Reactivate the test.
// This test verifies that "switch to tab" prefers the latest used browser,
// if multiple exist.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, DISABLED_SwitchToTabLatestWindow) {
// Navigate to a site.
NavigateHelper(GURL("http://maps.google.com/"), browser(),
WindowOpenDisposition::CURRENT_TAB, true);
// Navigate to a new window.
Browser* browser1 = NavigateHelper(GURL("http://maps.google.com/"), browser(),
WindowOpenDisposition::NEW_WINDOW, true);
// Make yet another window.
Browser* browser2 = NavigateHelper(GURL("http://maps.google.com/"), browser(),
WindowOpenDisposition::NEW_WINDOW, true);
// Navigate to the latest copy of the URL, in spite of specifying
// the previous browser.
Browser* test_browser =
NavigateHelper(GURL("http://maps.google.com/"), browser1,
WindowOpenDisposition::SWITCH_TO_TAB, false);
EXPECT_EQ(browser2, test_browser);
}
// Tests that a disposition of SINGLETON_TAB cannot see outside its
// window.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, SingletonWindowLeak) {
// Navigate to a site.
NavigateHelper(GURL("chrome://dino"), browser(),
WindowOpenDisposition::CURRENT_TAB, true);
// Navigate to a new window.
Browser* browser2 = NavigateHelper(GURL("chrome://about"), browser(),
WindowOpenDisposition::NEW_WINDOW, true);
// Make sure we open non-special URL here.
Browser* test_browser =
NavigateHelper(GURL("chrome://dino"), browser2,
WindowOpenDisposition::NEW_FOREGROUND_TAB, true);
EXPECT_EQ(browser2, test_browser);
}
// Tests that a disposition of SINGLETON_TAB cannot see across anonymity,
// except for certain non-incognito affinity URLs (e.g. settings).
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, SingletonIncognitoLeak) {
Browser* orig_browser;
// Navigate to a site.
orig_browser = NavigateHelper(GURL(chrome::kChromeUIVersionURL), browser(),
WindowOpenDisposition::CURRENT_TAB, true);
// Open about for (not) finding later.
NavigateHelper(GURL(chrome::kChromeUIAboutURL), orig_browser,
WindowOpenDisposition::NEW_FOREGROUND_TAB, true);
// Also open settings for finding later.
NavigateHelper(GURL(chrome::kChromeUISettingsURL), orig_browser,
WindowOpenDisposition::NEW_FOREGROUND_TAB, false);
EXPECT_EQ(3, browser()->tab_strip_model()->count());
Browser* test_browser;
{
Browser* incognito_browser = CreateIncognitoBrowser();
test_browser =
NavigateHelper(GURL(chrome::kChromeUIDownloadsURL), incognito_browser,
WindowOpenDisposition::OFF_THE_RECORD, true);
// Sanity check where OTR tab landed.
EXPECT_EQ(incognito_browser, test_browser);
// Sanity check that browser() always returns original.
EXPECT_EQ(orig_browser, browser());
// Open about singleton. Should not find in regular browser and
// open locally.
test_browser =
NavigateHelper(GURL(chrome::kChromeUIAboutURL), incognito_browser,
WindowOpenDisposition::SINGLETON_TAB, true);
EXPECT_NE(orig_browser, test_browser);
// Open settings. Should switch to non-incognito profile to do so.
test_browser =
NavigateHelper(GURL(chrome::kChromeUISettingsURL), incognito_browser,
WindowOpenDisposition::SINGLETON_TAB, false);
EXPECT_EQ(orig_browser, test_browser);
}
// Open downloads singleton. Should not search OTR browser and
// should open in regular browser.
test_browser =
NavigateHelper(GURL(chrome::kChromeUIDownloadsURL), orig_browser,
WindowOpenDisposition::SINGLETON_TAB, true);
EXPECT_EQ(browser(), test_browser);
}
// Tests that a disposition of SWITCH_TAB cannot see across anonymity,
// except for certain non-incognito affinity URLs (e.g. settings).
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, SwitchToTabIncognitoLeak) {
Browser* orig_browser;
// Navigate to a site.
orig_browser = NavigateHelper(GURL(chrome::kChromeUIVersionURL), browser(),
WindowOpenDisposition::CURRENT_TAB, true);
// Also open settings for finding later.
NavigateHelper(GURL(chrome::kChromeUISettingsURL), orig_browser,
WindowOpenDisposition::NEW_FOREGROUND_TAB, false);
// Also open about for searching too.
NavigateHelper(GURL(chrome::kChromeUIAboutURL), orig_browser,
WindowOpenDisposition::NEW_FOREGROUND_TAB, true);
EXPECT_EQ(3, browser()->tab_strip_model()->count());
Browser* test_browser;
{
Browser* incognito_browser = CreateIncognitoBrowser();
test_browser =
NavigateHelper(GURL(chrome::kChromeUIDownloadsURL), incognito_browser,
WindowOpenDisposition::OFF_THE_RECORD, true);
// Sanity check where OTR tab landed.
EXPECT_EQ(incognito_browser, test_browser);
// Sanity check that browser() always returns original.
EXPECT_EQ(orig_browser, browser());
// Try to open the original chrome://about via switch-to-tab. Should not
// find copy in regular browser, and open new tab in incognito.
test_browser =
NavigateHelper(GURL(chrome::kChromeUIAboutURL), incognito_browser,
WindowOpenDisposition::SWITCH_TO_TAB, true);
EXPECT_EQ(incognito_browser, test_browser);
// Open settings. Should switch to non-incognito profile to do so.
test_browser =
NavigateHelper(GURL(chrome::kChromeUISettingsURL), incognito_browser,
WindowOpenDisposition::SWITCH_TO_TAB, false);
EXPECT_EQ(orig_browser, test_browser);
}
// Switch-to-tab shouldn't find the incognito tab, and open new one in
// current browser.
test_browser =
NavigateHelper(GURL(chrome::kChromeUIDownloadsURL), orig_browser,
WindowOpenDisposition::SWITCH_TO_TAB, true);
EXPECT_EQ(browser(), test_browser);
}
#if BUILDFLAG(IS_MAC) && defined(ADDRESS_SANITIZER)
// Flaky on ASAN on Mac. See https://crbug.com/674497.
#define MAYBE_Disposition_Incognito DISABLED_Disposition_Incognito
#else
#define MAYBE_Disposition_Incognito Disposition_Incognito
#endif
// This test verifies that navigating with WindowOpenDisposition = INCOGNITO
// opens a new incognito window if no existing incognito window is present.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, MAYBE_Disposition_Incognito) {
NavigateParams params(MakeNavigateParams());
params.disposition = WindowOpenDisposition::OFF_THE_RECORD;
Navigate(&params);
// Navigate() should have opened a new toplevel incognito window.
EXPECT_NE(browser(), params.browser);
EXPECT_EQ(
browser()->profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true),
params.browser->profile());
// |source_contents| should be set to NULL because the profile for the new
// page is different from the originating page.
EXPECT_FALSE(params.source_contents);
// We should now have two windows, the browser() provided by the framework and
// the new incognito window.
EXPECT_EQ(2u, chrome::GetTotalBrowserCount());
EXPECT_EQ(1, browser()->tab_strip_model()->count());
EXPECT_EQ(1, params.browser->tab_strip_model()->count());
}
// This test verifies that navigating with WindowOpenDisposition = INCOGNITO
// reuses an existing incognito window when possible.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, Disposition_IncognitoRefocus) {
Browser* incognito_browser = CreateEmptyBrowserForType(
Browser::TYPE_NORMAL,
browser()->profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true));
NavigateParams params(MakeNavigateParams());
params.disposition = WindowOpenDisposition::OFF_THE_RECORD;
Navigate(&params);
// Navigate() should have opened a new tab in the existing incognito window.
EXPECT_NE(browser(), params.browser);
EXPECT_EQ(params.browser, incognito_browser);
// We should now have two windows, the browser() provided by the framework and
// the incognito window we opened earlier.
EXPECT_EQ(2u, chrome::GetTotalBrowserCount());
EXPECT_EQ(1, browser()->tab_strip_model()->count());
EXPECT_EQ(2, incognito_browser->tab_strip_model()->count());
}
// This test verifies that no navigation action occurs when
// WindowOpenDisposition = SAVE_TO_DISK.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, Disposition_SaveToDisk) {
RunSuppressTest(WindowOpenDisposition::SAVE_TO_DISK);
}
// This test verifies that no navigation action occurs when
// WindowOpenDisposition = IGNORE_ACTION.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, Disposition_IgnoreAction) {
RunSuppressTest(WindowOpenDisposition::IGNORE_ACTION);
}
// This tests adding a foreground tab with a predefined WebContents.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, TargetContents_ForegroundTab) {
NavigateParams params(MakeNavigateParams());
params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
params.contents_to_insert = CreateWebContents(false);
Navigate(&params);
// Navigate() should have opened the contents in a new foreground tab in the
// current Browser.
EXPECT_EQ(browser(), params.browser);
EXPECT_EQ(browser()->tab_strip_model()->GetActiveWebContents(),
params.navigated_or_inserted_contents);
// We should have one window, with two tabs.
EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
EXPECT_EQ(2, browser()->tab_strip_model()->count());
}
#if BUILDFLAG(IS_WIN)
// This tests adding a popup with a predefined WebContents.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, DISABLED_TargetContents_Popup) {
NavigateParams params(MakeNavigateParams());
params.disposition = WindowOpenDisposition::NEW_POPUP;
params.contents_to_insert = CreateWebContents(false);
params.window_features.bounds = gfx::Rect(10, 10, 500, 500);
Navigate(&params);
// Navigate() should have opened a new popup window.
EXPECT_NE(browser(), params.browser);
EXPECT_TRUE(params.browser->is_type_popup());
EXPECT_TRUE(params.browser->window()->IsToolbarVisible());
// The web platform is weird. The window bounds specified in
// `params.window_features.bounds` are used as follows:
// - the origin is used to position the window
// - the size is used to size the WebContents of the window.
// As such the position of the resulting window will always match
// `params.window_features.bounds.origin()`, but its size will not. We need to
// match the size against the selected tab's view's container size.
// Only Windows positions the window according to
// `params.window_features.bounds.origin()` - on Mac the window is offset from
// the opener and on Linux it always opens at 0,0.
EXPECT_EQ(params.window_features.bounds.origin(),
params.browser->window()->GetRestoredBounds().origin());
// All platforms should respect size however provided width > 400 (Mac has a
// minimum window width of 400).
EXPECT_EQ(params.window_features.bounds.size(),
params.navigated_or_inserted_contents->GetContainerBounds().size());
// We should have two windows, the new popup and the browser() provided by the
// framework.
EXPECT_EQ(2u, chrome::GetTotalBrowserCount());
EXPECT_EQ(1, browser()->tab_strip_model()->count());
EXPECT_EQ(1, params.browser->tab_strip_model()->count());
}
#endif
// This test checks that we can create WebContents with renderer process and
// RenderFrame without navigating it.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
CreateWebContentsWithRendererProcess) {
NavigateParams params(MakeNavigateParams());
params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
params.contents_to_insert = CreateWebContents(true);
ASSERT_TRUE(params.contents_to_insert);
// There is no navigation (to about:blank or something like that).
EXPECT_FALSE(params.contents_to_insert->IsLoading());
ASSERT_TRUE(params.contents_to_insert->GetPrimaryMainFrame());
EXPECT_TRUE(
params.contents_to_insert->GetPrimaryMainFrame()->IsRenderFrameLive());
EXPECT_TRUE(
params.contents_to_insert->GetController().IsInitialBlankNavigation());
int renderer_id = params.contents_to_insert->GetPrimaryMainFrame()
->GetProcess()
->GetDeprecatedID();
// We should have one window, with one tab of WebContents differ from
// params.target_contents.
EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
EXPECT_EQ(1, browser()->tab_strip_model()->count());
EXPECT_NE(browser()->tab_strip_model()->GetActiveWebContents(),
params.contents_to_insert.get());
Navigate(&params);
// Navigate() should have opened the contents in a new foreground tab in the
// current Browser, without changing the renderer process of target_contents.
EXPECT_EQ(browser(), params.browser);
EXPECT_EQ(browser()->tab_strip_model()->GetActiveWebContents(),
params.navigated_or_inserted_contents);
EXPECT_EQ(renderer_id,
params.navigated_or_inserted_contents->GetPrimaryMainFrame()
->GetProcess()
->GetDeprecatedID());
// We should have one window, with two tabs.
EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
EXPECT_EQ(2, browser()->tab_strip_model()->count());
}
// This tests adding a tab at a specific index.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, Tabstrip_InsertAtIndex) {
// This is not meant to be a comprehensive test of whether or not the tab
// implementation of the browser observes the insertion index. That is
// covered by the unit tests for TabStripModel. This merely verifies that
// insertion index preference is reflected in common cases.
NavigateParams params(MakeNavigateParams());
params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
params.tabstrip_index = 0;
params.tabstrip_add_types = AddTabTypes::ADD_FORCE_INDEX;
Navigate(&params);
// Navigate() should have inserted a new tab at slot 0 in the tabstrip.
EXPECT_EQ(browser(), params.browser);
EXPECT_EQ(0, browser()->tab_strip_model()->GetIndexOfWebContents(
static_cast<const WebContents*>(
params.navigated_or_inserted_contents)));
// We should have one window - the browser() provided by the framework.
EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
EXPECT_EQ(2, browser()->tab_strip_model()->count());
}
// This test verifies that constructing params with disposition = SINGLETON_TAB
// and IGNORE_AND_NAVIGATE opens a new tab navigated to the specified URL if
// no previous tab with that URL (minus the path) exists.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
Disposition_SingletonTabNew_IgnorePath) {
chrome::AddSelectedTabWithURL(browser(), GetGoogleURL(),
ui::PAGE_TRANSITION_LINK);
// We should have one browser with 2 tabs, the 2nd selected.
EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
EXPECT_EQ(2, browser()->tab_strip_model()->count());
EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
// Navigate to a new singleton tab with a sub-page.
NavigateParams params(MakeNavigateParams());
params.disposition = WindowOpenDisposition::SINGLETON_TAB;
params.url = GetContentSettingsURL();
params.window_action = NavigateParams::SHOW_WINDOW;
params.path_behavior = NavigateParams::IGNORE_AND_NAVIGATE;
Navigate(&params);
// The last tab should now be selected and navigated to the sub-page of the
// URL.
EXPECT_EQ(browser(), params.browser);
EXPECT_EQ(3, browser()->tab_strip_model()->count());
EXPECT_EQ(2, browser()->tab_strip_model()->active_index());
EXPECT_EQ(GetContentSettingsURL(),
browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
}
// This test verifies that constructing params with disposition = SINGLETON_TAB
// and IGNORE_AND_NAVIGATE opens an existing tab with the matching URL (minus
// the path) which is navigated to the specified URL.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
Disposition_SingletonTabExisting_IgnorePath) {
const GURL singleton_url(GetSettingsURL());
chrome::AddSelectedTabWithURL(browser(), singleton_url,
ui::PAGE_TRANSITION_LINK);
chrome::AddSelectedTabWithURL(browser(), GetGoogleURL(),
ui::PAGE_TRANSITION_LINK);
// We should have one browser with 3 tabs, the 3rd selected.
EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
EXPECT_EQ(3, browser()->tab_strip_model()->count());
EXPECT_EQ(2, browser()->tab_strip_model()->active_index());
// Navigate to |singleton_url|.
NavigateParams params(MakeNavigateParams());
params.disposition = WindowOpenDisposition::SINGLETON_TAB;
params.url = GetContentSettingsURL();
params.window_action = NavigateParams::SHOW_WINDOW;
params.path_behavior = NavigateParams::IGNORE_AND_NAVIGATE;
Navigate(&params);
// The middle tab should now be selected and navigated to the sub-page of the
// URL.
EXPECT_EQ(browser(), params.browser);
EXPECT_EQ(3, browser()->tab_strip_model()->count());
EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
EXPECT_EQ(GetContentSettingsURL(),
browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
}
// This test verifies that constructing params with disposition = SINGLETON_TAB
// and IGNORE_AND_NAVIGATE opens an existing tab with the matching URL (minus
// the path) which is navigated to the specified URL.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
Disposition_SingletonTabExistingSubPath_IgnorePath) {
const GURL singleton_url(GetContentSettingsURL());
chrome::AddSelectedTabWithURL(browser(), singleton_url,
ui::PAGE_TRANSITION_LINK);
chrome::AddSelectedTabWithURL(browser(), GetGoogleURL(),
ui::PAGE_TRANSITION_LINK);
// We should have one browser with 3 tabs, the 3rd selected.
EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
EXPECT_EQ(3, browser()->tab_strip_model()->count());
EXPECT_EQ(2, browser()->tab_strip_model()->active_index());
// Navigate to |singleton_url|.
NavigateParams params(MakeNavigateParams());
params.disposition = WindowOpenDisposition::SINGLETON_TAB;
params.url = GetClearBrowsingDataURL();
params.window_action = NavigateParams::SHOW_WINDOW;
params.path_behavior = NavigateParams::IGNORE_AND_NAVIGATE;
Navigate(&params);
// The middle tab should now be selected and navigated to the sub-page of the
// URL.
EXPECT_EQ(browser(), params.browser);
EXPECT_EQ(3, browser()->tab_strip_model()->count());
EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
EXPECT_EQ(GetClearBrowsingDataURL(),
browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
}
// This test verifies that constructing params with disposition = SINGLETON_TAB
// and IGNORE_AND_NAVIGATE will update the current tab's URL if the currently
// selected tab is a match but has a different path.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
Disposition_SingletonTabFocused_IgnorePath) {
const GURL singleton_url_current(GetContentSettingsURL());
chrome::AddSelectedTabWithURL(browser(), singleton_url_current,
ui::PAGE_TRANSITION_LINK);
// We should have one browser with 2 tabs, the 2nd selected.
EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
EXPECT_EQ(2, browser()->tab_strip_model()->count());
EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
// Navigate to a different settings path.
const GURL singleton_url_target(GetClearBrowsingDataURL());
NavigateParams params(MakeNavigateParams());
params.disposition = WindowOpenDisposition::SINGLETON_TAB;
params.url = singleton_url_target;
params.window_action = NavigateParams::SHOW_WINDOW;
params.path_behavior = NavigateParams::IGNORE_AND_NAVIGATE;
Navigate(&params);
// The second tab should still be selected, but navigated to the new path.
EXPECT_EQ(browser(), params.browser);
EXPECT_EQ(2, browser()->tab_strip_model()->count());
EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
EXPECT_EQ(singleton_url_target,
browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
}
// This test verifies that constructing params with disposition = SINGLETON_TAB
// and IGNORE_AND_NAVIGATE will open an existing matching tab with a different
// query.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
Disposition_SingletonTabExisting_IgnoreQuery) {
int initial_tab_count = browser()->tab_strip_model()->count();
const GURL singleton_url_current(GetContentSettingsURL());
chrome::AddSelectedTabWithURL(browser(), singleton_url_current,
ui::PAGE_TRANSITION_LINK);
EXPECT_EQ(initial_tab_count + 1, browser()->tab_strip_model()->count());
EXPECT_EQ(initial_tab_count, browser()->tab_strip_model()->active_index());
// Navigate to a different settings path.
const GURL singleton_url_target(GetClearBrowsingDataURL());
NavigateParams params(MakeNavigateParams());
params.disposition = WindowOpenDisposition::SINGLETON_TAB;
params.url = singleton_url_target;
params.window_action = NavigateParams::SHOW_WINDOW;
params.path_behavior = NavigateParams::IGNORE_AND_NAVIGATE;
Navigate(&params);
// Last tab should still be selected.
EXPECT_EQ(browser(), params.browser);
EXPECT_EQ(initial_tab_count + 1, browser()->tab_strip_model()->count());
EXPECT_EQ(initial_tab_count, browser()->tab_strip_model()->active_index());
}
// This test verifies that the settings page isn't opened in the incognito
// window.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
Disposition_Settings_UseNonIncognitoWindow) {
RunUseNonIncognitoWindowTest(
GetSettingsURL(), ui::PageTransition::PAGE_TRANSITION_AUTO_BOOKMARK);
}
// This test verifies that the view-source settings page isn't opened in the
// incognito window.
IN_PROC_BROWSER_TEST_F(
BrowserNavigatorTest,
Disposition_ViewSource_Settings_DoNothingIfIncognitoForced) {
std::string view_source(content::kViewSourceScheme);
view_source.append(":");
view_source.append(chrome::kChromeUISettingsURL);
RunDoNothingIfIncognitoIsForcedTest(GURL(view_source));
}
// This test verifies that the view-source settings page isn't opened in the
// incognito window even if incognito mode is forced (does nothing in that
// case).
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
Disposition_ViewSource_Settings_UseNonIncognitoWindow) {
std::string view_source(content::kViewSourceScheme);
view_source.append(":");
view_source.append(chrome::kChromeUISettingsURL);
RunUseNonIncognitoWindowTest(
GURL(view_source), ui::PageTransition::PAGE_TRANSITION_AUTO_BOOKMARK);
}
// This test verifies that the settings page isn't opened in the incognito
// window from a non-incognito window (bookmark open-in-incognito trigger).
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
Disposition_Settings_UseNonIncognitoWindowForBookmark) {
NavigateParams params(browser(), GetSettingsURL(),
ui::PAGE_TRANSITION_AUTO_BOOKMARK);
params.disposition = WindowOpenDisposition::OFF_THE_RECORD;
{
content::CreateAndLoadWebContentsObserver observer;
Navigate(&params);
observer.Wait();
}
EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
EXPECT_EQ(GetSettingsURL(),
browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
}
// Settings page is expected to always open in normal mode regardless
// of whether the user is trying to open it in incognito mode or not.
// This test verifies that if incognito mode is forced (by policy), settings
// page doesn't open at all.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
Disposition_Settings_DoNothingIfIncognitoIsForced) {
RunDoNothingIfIncognitoIsForcedTest(GetSettingsURL());
}
// This test verifies that the bookmarks page can open in incognito windows.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
Disposition_Bookmarks_UseIncognitoWindow) {
Browser* const incognito_browser = CreateIncognitoBrowser();
TabStripModel* const incognito_tab_strip_model =
incognito_browser->tab_strip_model();
EXPECT_EQ(2u, chrome::GetTotalBrowserCount());
EXPECT_EQ(1, browser()->tab_strip_model()->count());
EXPECT_EQ(1, incognito_tab_strip_model->count());
// Navigate to the page.
const GURL bookmarks_page = GURL(chrome::kChromeUIBookmarksURL);
NavigateParams params(MakeNavigateParams(incognito_browser));
params.disposition = WindowOpenDisposition::SINGLETON_TAB;
params.url = bookmarks_page;
params.window_action = NavigateParams::SHOW_WINDOW;
params.transition = ui::PageTransition::PAGE_TRANSITION_AUTO_BOOKMARK;
Navigate(&params);
// This page should be opened in browser() window.
EXPECT_EQ(incognito_browser, params.browser);
EXPECT_NE(browser(), params.browser);
EXPECT_EQ(2, incognito_tab_strip_model->count());
EXPECT_EQ(bookmarks_page,
incognito_tab_strip_model->GetActiveWebContents()->GetURL());
}
// This test makes sure a crashed singleton tab reloads from a new navigation.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, NavigateToCrashedSingletonTab) {
const GURL singleton_url(GetContentSettingsURL());
WebContents* web_contents = chrome::AddSelectedTabWithURL(
browser(), singleton_url, ui::PAGE_TRANSITION_LINK);
// We should have one browser with 2 tabs, the 2nd selected.
EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
EXPECT_EQ(2, browser()->tab_strip_model()->count());
EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
// Kill the singleton tab.
{
content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
content::RenderFrameDeletedObserver crash_observer(
web_contents->GetPrimaryMainFrame());
web_contents->GetPrimaryMainFrame()->GetProcess()->Shutdown(1);
crash_observer.WaitUntilDeleted();
}
EXPECT_TRUE(web_contents->IsCrashed());
NavigateParams params(MakeNavigateParams());
params.disposition = WindowOpenDisposition::SINGLETON_TAB;
params.url = singleton_url;
params.window_action = NavigateParams::SHOW_WINDOW;
params.path_behavior = NavigateParams::IGNORE_AND_NAVIGATE;
ui_test_utils::NavigateToURL(&params);
// The tab should not be sad anymore.
EXPECT_FALSE(web_contents->IsCrashed());
}
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
NavigateFromDefaultToOptionsInSameTab) {
{
content::LoadStopObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
ShowSettings(browser());
observer.Wait();
}
EXPECT_EQ(1, browser()->tab_strip_model()->count());
EXPECT_EQ(GetSettingsURL(),
browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
}
// TODO(crbug.com/40107334): Timing out on linux-chromeos-dbg.
#if BUILDFLAG(IS_CHROMEOS)
#define MAYBE_NavigateFromBlankToOptionsInSameTab \
DISABLED_NavigateFromBlankToOptionsInSameTab
#else
#define MAYBE_NavigateFromBlankToOptionsInSameTab \
NavigateFromBlankToOptionsInSameTab
#endif
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
MAYBE_NavigateFromBlankToOptionsInSameTab) {
NavigateParams params(MakeNavigateParams());
params.url = GURL(url::kAboutBlankURL);
ui_test_utils::NavigateToURL(&params);
{
content::LoadStopObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
ShowSettings(browser());
observer.Wait();
}
EXPECT_EQ(1, browser()->tab_strip_model()->count());
EXPECT_EQ(GetSettingsURL(),
browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
}
// TODO(crbug.com/40107334): Timing out on linux-chromeos-dbg.
#if BUILDFLAG(IS_CHROMEOS)
#define MAYBE_NavigateFromNTPToOptionsInSameTab \
DISABLED_NavigateFromNTPToOptionsInSameTab
#else
#define MAYBE_NavigateFromNTPToOptionsInSameTab \
NavigateFromNTPToOptionsInSameTab
#endif
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
MAYBE_NavigateFromNTPToOptionsInSameTab) {
NavigateParams params(MakeNavigateParams());
params.url = GURL(chrome::kChromeUINewTabURL);
ui_test_utils::NavigateToURL(&params);
EXPECT_EQ(1, browser()->tab_strip_model()->count());
EXPECT_EQ(ntp_test_utils::GetFinalNtpUrl(browser()->profile()),
browser()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL());
{
content::LoadStopObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
ShowSettings(browser());
observer.Wait();
}
EXPECT_EQ(1, browser()->tab_strip_model()->count());
EXPECT_EQ(GetSettingsURL(),
browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
}
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
NavigateFromPageToOptionsInNewTab) {
NavigateParams params(MakeNavigateParams());
ui_test_utils::NavigateToURL(&params);
EXPECT_EQ(GetGoogleURL(),
browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
EXPECT_EQ(1, browser()->tab_strip_model()->count());
{
content::CreateAndLoadWebContentsObserver observer;
ShowSettings(browser());
observer.Wait();
}
EXPECT_EQ(2, browser()->tab_strip_model()->count());
EXPECT_EQ(GetSettingsURL(),
browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
}
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
NavigateFromNTPToOptionsSingleton) {
{
content::LoadStopObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
ShowSettings(browser());
observer.Wait();
}
EXPECT_EQ(1, browser()->tab_strip_model()->count());
chrome::NewTab(browser());
EXPECT_EQ(2, browser()->tab_strip_model()->count());
{
content::LoadStopObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
ShowSettings(browser());
observer.Wait();
}
EXPECT_EQ(2, browser()->tab_strip_model()->count());
EXPECT_EQ(GetSettingsURL(),
browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
}
// TODO(crbug.com/40166082): This is disabled for Mac OS due to flakiness.
// TODO(crbug.com/40107334): Timing out on linux-chromeos-dbg.
#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC)
#define MAYBE_NavigateFromNTPToOptionsPageInSameTab \
DISABLED_NavigateFromNTPToOptionsPageInSameTab
#else
#define MAYBE_NavigateFromNTPToOptionsPageInSameTab \
NavigateFromNTPToOptionsPageInSameTab
#endif
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
MAYBE_NavigateFromNTPToOptionsPageInSameTab) {
{
content::LoadStopObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
chrome::ShowSettingsSubPageInTabbedBrowser(
browser(), chrome::kClearBrowserDataSubPage);
observer.Wait();
}
EXPECT_EQ(1, browser()->tab_strip_model()->count());
EXPECT_EQ(GetClearBrowsingDataURL(),
browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
chrome::NewTab(browser());
EXPECT_EQ(2, browser()->tab_strip_model()->count());
{
content::LoadStopObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
chrome::ShowSettingsSubPageInTabbedBrowser(
browser(), chrome::kClearBrowserDataSubPage);
observer.Wait();
}
EXPECT_EQ(2, browser()->tab_strip_model()->count());
EXPECT_EQ(GetClearBrowsingDataURL(),
browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
}
#if !BUILDFLAG(IS_ANDROID)
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
NavigateFromPageInfoToSiteSettingsInNewTab) {
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ChromePageInfoDelegate delegate(web_contents);
delegate.ShowSiteSettings(web_contents->GetVisibleURL());
content::LoadStopObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
observer.Wait();
// Site settings opens in a new tab.
EXPECT_EQ(2, browser()->tab_strip_model()->count());
EXPECT_EQ(chrome::GetSettingsUrl(chrome::kContentSettingsSubPage),
browser()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL());
}
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
NavigateFromPageInfoToSiteSettingsFileSystemInNewTab) {
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ChromePageInfoDelegate delegate(web_contents);
delegate.OpenContentSettingsExceptions(
ContentSettingsType::FILE_SYSTEM_WRITE_GUARD);
content::LoadStopObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
observer.Wait();
// File system site settings opens in a new tab.
EXPECT_EQ(2, browser()->tab_strip_model()->count());
EXPECT_EQ(chrome::GetSettingsUrl(chrome::kFileSystemSettingsSubpage),
browser()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL());
}
#endif
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
NavigateFromOtherTabToSingletonOptions) {
{
content::LoadStopObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
ShowSettings(browser());
observer.Wait();
}
{
content::CreateAndLoadWebContentsObserver observer;
chrome::AddSelectedTabWithURL(browser(), GetGoogleURL(),
ui::PAGE_TRANSITION_LINK);
observer.Wait();
}
// This load should simply cause a tab switch.
ShowSettings(browser());
EXPECT_EQ(2, browser()->tab_strip_model()->count());
EXPECT_EQ(GetSettingsURL(),
browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
}
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
NavigateFromNoTabStripWindowToOptions) {
{
content::LoadStopObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
ShowSettings(browser());
observer.Wait();
}
{
content::CreateAndLoadWebContentsObserver observer;
chrome::AddSelectedTabWithURL(browser(), GetGoogleURL(),
ui::PAGE_TRANSITION_LINK);
observer.Wait();
}
Browser* app_browser = CreateBrowserForApp("TestApp", browser()->profile());
// This load should cause a window and tab switch.
ShowSingletonTab(app_browser, GetSettingsURL());
EXPECT_EQ(2, browser()->tab_strip_model()->count());
EXPECT_EQ(GetSettingsURL(),
browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
}
// TODO(crbug.com/40107334): Timing out on linux-chromeos-dbg.
#if BUILDFLAG(IS_CHROMEOS)
#define MAYBE_CloseSingletonTab DISABLED_CloseSingletonTab
#else
#define MAYBE_CloseSingletonTab CloseSingletonTab
#endif
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, MAYBE_CloseSingletonTab) {
for (int i = 0; i < 2; ++i) {
content::CreateAndLoadWebContentsObserver observer;
chrome::AddSelectedTabWithURL(browser(), GetGoogleURL(),
ui::PAGE_TRANSITION_TYPED);
observer.Wait();
}
browser()->tab_strip_model()->ActivateTabAt(
0, TabStripUserGestureDetails(
TabStripUserGestureDetails::GestureType::kOther));
{
content::LoadStopObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
ShowSettings(browser());
observer.Wait();
}
int previous_tab_count = browser()->tab_strip_model()->count();
browser()->tab_strip_model()->CloseWebContentsAt(
2, TabCloseTypes::CLOSE_USER_GESTURE);
EXPECT_EQ(previous_tab_count - 1, browser()->tab_strip_model()->count());
EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
}
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
NavigateFromDefaultToHistoryInSameTab) {
{
content::LoadStopObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
chrome::ShowHistory(browser());
observer.Wait();
}
EXPECT_EQ(1, browser()->tab_strip_model()->count());
EXPECT_EQ(GURL(chrome::kChromeUIHistoryURL),
browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
}
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
NavigateFromDefaultToBookmarksInSameTab) {
{
content::LoadStopObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
chrome::ShowBookmarkManager(browser());
observer.Wait();
}
EXPECT_EQ(1, browser()->tab_strip_model()->count());
EXPECT_TRUE(base::StartsWith(
browser()->tab_strip_model()->GetActiveWebContents()->GetURL().spec(),
chrome::kChromeUIBookmarksURL, base::CompareCase::SENSITIVE));
}
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
NavigateFromDefaultToDownloadsInSameTab) {
{
content::LoadStopObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
chrome::ShowDownloads(browser());
observer.Wait();
}
EXPECT_EQ(1, browser()->tab_strip_model()->count());
EXPECT_EQ(GURL(chrome::kChromeUIDownloadsURL),
browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
}
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, NavigateWithoutBrowser) {
// First navigate using the profile of the existing browser window, and
// check that the window is reused.
NavigateParams params(browser()->profile(), GetGoogleURL(),
ui::PAGE_TRANSITION_LINK);
ui_test_utils::NavigateToURL(&params);
EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
// Now navigate using the incognito profile and check that a new window
// is created.
NavigateParams params_incognito(
browser()->profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true),
GetGoogleURL(), ui::PAGE_TRANSITION_LINK);
ui_test_utils::NavigateToURL(&params_incognito);
EXPECT_EQ(2u, chrome::GetTotalBrowserCount());
}
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, ViewSourceIsntSingleton) {
const std::string viewsource_ntp_url =
std::string(content::kViewSourceScheme) + ":" +
chrome::kChromeUIVersionURL;
NavigateParams viewsource_params(browser(), GURL(viewsource_ntp_url),
ui::PAGE_TRANSITION_LINK);
ui_test_utils::NavigateToURL(&viewsource_params);
NavigateParams singleton_params(browser(), GURL(chrome::kChromeUIVersionURL),
ui::PAGE_TRANSITION_LINK);
singleton_params.disposition = WindowOpenDisposition::SINGLETON_TAB;
EXPECT_EQ(-1, GetIndexOfExistingTab(browser(), singleton_params));
}
// Ensure that an incognito window invoking |view-source:| on a url forbidden in
// incognito loads the correct url in the non-incognito window.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, ViewSourceUrlMatching) {
// Open chrome://settings in the main window.
NavigateParams settings_params(browser(), GURL(chrome::kChromeUISettingsURL),
ui::PAGE_TRANSITION_LINK);
ui_test_utils::NavigateToURL(&settings_params);
// Create a new incognito window.
Browser* incognito_browser = CreateIncognitoBrowser();
EXPECT_EQ(2u, chrome::GetTotalBrowserCount());
EXPECT_EQ(1, browser()->tab_strip_model()->count());
EXPECT_EQ(1, incognito_browser->tab_strip_model()->count());
// In the Incognito window, start a navigation to the view-source page.
const std::string viewsource_settings_url =
std::string(content::kViewSourceScheme) + ":" +
chrome::kChromeUISettingsURL;
NavigateParams params(MakeNavigateParams(incognito_browser));
params.disposition = WindowOpenDisposition::SINGLETON_TAB;
params.url = GURL(viewsource_settings_url);
params.window_action = NavigateParams::SHOW_WINDOW;
params.transition = ui::PAGE_TRANSITION_AUTO_BOOKMARK;
Navigate(&params);
// The view-source page should be opened as a new tab in the non-incognito
// browser window.
EXPECT_NE(incognito_browser, params.browser);
EXPECT_EQ(browser(), params.browser);
EXPECT_EQ(2, browser()->tab_strip_model()->count());
EXPECT_EQ(viewsource_settings_url,
browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
}
enum class SplitCacheTestCase {
kEnabledTripleKeyed,
kEnabledTriplePlusCrossSiteMainFrameNavBool,
};
const struct {
const SplitCacheTestCase test_case;
base::test::FeatureRef feature;
} kTestCaseToFeatureMapping[] = {
{SplitCacheTestCase::kEnabledTriplePlusCrossSiteMainFrameNavBool,
net::features::kSplitCacheByCrossSiteMainFrameNavigationBoolean}};
class BrowserNavigatorSplitHttpCacheTest
: public BrowserNavigatorTest,
public testing::WithParamInterface<SplitCacheTestCase> {
protected:
BrowserNavigatorSplitHttpCacheTest()
: split_cache_experiment_feature_list_(GetParam(),
kTestCaseToFeatureMapping) {
split_cache_always_enabled_feature_list_.InitAndEnableFeature(
net::features::kSplitCacheByNetworkIsolationKey);
}
private:
net::test::ScopedMutuallyExclusiveFeatureList
split_cache_experiment_feature_list_;
base::test::ScopedFeatureList split_cache_always_enabled_feature_list_;
};
INSTANTIATE_TEST_SUITE_P(
All,
BrowserNavigatorSplitHttpCacheTest,
testing::ValuesIn(
{SplitCacheTestCase::kEnabledTripleKeyed,
SplitCacheTestCase::kEnabledTriplePlusCrossSiteMainFrameNavBool}),
[](const testing::TestParamInfo<SplitCacheTestCase>& info) {
switch (info.param) {
case (SplitCacheTestCase::kEnabledTripleKeyed):
return "TripleKeyed";
case (SplitCacheTestCase::kEnabledTriplePlusCrossSiteMainFrameNavBool):
return "TriplePlusCrossSiteMainFrameNavigationBool";
}
});
// This test verifies that browser initiated navigations can send requests
// using POST.
IN_PROC_BROWSER_TEST_P(BrowserNavigatorSplitHttpCacheTest,
SendBrowserInitiatedRequestUsingPOST) {
// Uses a test sever to verify POST request.
ASSERT_TRUE(embedded_test_server()->Start());
// Open a browser initiated POST request in new foreground tab.
std::u16string expected_title(kExpectedTitle16);
std::string post_data = kExpectedTitle;
std::u16string title;
ASSERT_TRUE(OpenPOSTURLInNewForegroundTabAndGetTitle(
embedded_test_server()->GetURL(kEchoTitleCommand), post_data, true,
&title));
EXPECT_EQ(expected_title, title);
}
// This test verifies that renderer initiated navigations can also send requests
// using POST.
IN_PROC_BROWSER_TEST_P(BrowserNavigatorSplitHttpCacheTest,
SendRendererInitiatedRequestUsingPOST) {
// Uses a test sever to verify POST request.
ASSERT_TRUE(embedded_test_server()->Start());
// Open a renderer initiated POST request in new foreground tab.
std::u16string expected_title(kExpectedTitle16);
std::string post_data = kExpectedTitle;
std::u16string title;
ASSERT_TRUE(OpenPOSTURLInNewForegroundTabAndGetTitle(
embedded_test_server()->GetURL(kEchoTitleCommand), post_data, false,
&title));
EXPECT_EQ(expected_title, title);
}
// This test navigates to a data URL that contains BiDi control
// characters. For security reasons, BiDi control chars should always be
// escaped in the URL but they should be unescaped in the loaded HTML.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
NavigateToDataURLWithBiDiControlChars) {
// Text in Arabic.
std::string text = "\xD8\xA7\xD8\xAE\xD8\xAA\xD8\xA8\xD8\xA7\xD8\xB1";
// Page title starts with RTL mark.
std::string unescaped_title = "\xE2\x80\x8F" + text;
std::string data_url = "data:text/html;charset=utf-8,<html><title>" +
unescaped_title + "</title></html>";
// BiDi control chars in URLs are always escaped, so the expected URL should
// have the title with the escaped RTL mark.
std::string escaped_title = "%E2%80%8F" + text;
std::string expected_url = "data:text/html;charset=utf-8,<html><title>" +
escaped_title + "</title></html>";
// Navigate to the page.
NavigateParams params(MakeNavigateParams());
params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
params.url = GURL(data_url);
params.window_action = NavigateParams::SHOW_WINDOW;
ui_test_utils::NavigateToURL(&params);
std::u16string expected_title(base::UTF8ToUTF16(unescaped_title));
EXPECT_TRUE(params.navigated_or_inserted_contents);
EXPECT_EQ(expected_title, params.navigated_or_inserted_contents->GetTitle());
// GURL always keeps non-ASCII characters escaped, but check them anyways.
EXPECT_EQ(GURL(expected_url).spec(),
params.navigated_or_inserted_contents->GetURL().spec());
// Check the omnibox text. It should have escaped RTL with unescaped text.
LocationBar* location_bar = browser()->window()->GetLocationBar();
OmniboxView* omnibox_view = location_bar->GetOmniboxView();
EXPECT_EQ(base::UTF8ToUTF16(expected_url), omnibox_view->GetText());
}
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
Disposition_PictureInPicture_Open) {
// Create the params for the PiP request.
auto pip_options = blink::mojom::PictureInPictureWindowOptions::New();
// The WebContents holds the parameters from the PiP request.
WebContents::CreateParams web_contents_params(browser()->profile());
web_contents_params.picture_in_picture_options = *pip_options;
// Opening a picture in picture window should create a new browser.
NavigateParams params = MakeNavigateParams(browser());
params.disposition = WindowOpenDisposition::NEW_PICTURE_IN_PICTURE;
// Navigate to https:// page
// TODO: Extract the navigation logic to a helper function?
net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
ASSERT_TRUE(https_server.Start());
WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
const GURL url = https_server.GetURL("/simple.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
params.source_contents = tab;
params.contents_to_insert = WebContents::Create(web_contents_params);
Navigate(&params);
// Should not re-use the browser.
EXPECT_NE(browser(), params.browser);
EXPECT_TRUE(params.browser->is_type_picture_in_picture());
EXPECT_EQ(params.browser->app_name(), std::string());
// The window should have respected the initial aspect ratio.
const gfx::Rect override_bounds = params.browser->override_bounds();
const double aspect_ratio = static_cast<double>(override_bounds.width()) /
static_cast<double>(override_bounds.height());
EXPECT_DOUBLE_EQ(1.0, aspect_ratio);
}
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
Disposition_PictureInPicture_OpenWithWidthAndHeight) {
// Set width/height with equivalent aspect ratio of 1.0.
auto pip_options = blink::mojom::PictureInPictureWindowOptions::New();
pip_options->width = 500;
pip_options->height = 500;
WebContents::CreateParams web_contents_params(browser()->profile());
web_contents_params.picture_in_picture_options = *pip_options;
// Opening a picture in picture window should create a new browser.
NavigateParams params = MakeNavigateParams(browser());
params.disposition = WindowOpenDisposition::NEW_PICTURE_IN_PICTURE;
// Navigate to https:// page
net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
ASSERT_TRUE(https_server.Start());
WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
const GURL url = https_server.GetURL("/simple.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
params.source_contents = tab;
params.contents_to_insert = WebContents::Create(web_contents_params);
Navigate(&params);
// The bounds may have small adjustments for window decorations, since the
// requested size is the inner size. We can't get the inner size easily here,
// so just verify that the aspect ratio is closer to 1.0 than 0.5.
const gfx::Rect override_bounds = params.browser->override_bounds();
float expected_aspect_ratio =
static_cast<float>(override_bounds.width()) / override_bounds.height();
EXPECT_NEAR(expected_aspect_ratio, 1.0f, 0.2);
}
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
Disposition_PictureInPicture_CantFromAnotherPip) {
// Make sure that attempting to open a picture in picture window from a
// picture in picture window fails.
Browser* pip = CreateEmptyBrowserForType(Browser::TYPE_PICTURE_IN_PICTURE,
browser()->profile());
NavigateParams params = MakeNavigateParams(pip);
params.disposition = WindowOpenDisposition::NEW_PICTURE_IN_PICTURE;
// Navigate to https:// page
net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
ASSERT_TRUE(https_server.Start());
WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
const GURL url = https_server.GetURL("/simple.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
params.source_contents = tab;
Navigate(&params);
EXPECT_EQ(params.browser, nullptr);
}
IN_PROC_BROWSER_TEST_F(
BrowserNavigatorTest,
Disposition_PictureInPicture_CantWithoutASourceContents) {
// Opening a picture-in-picture window without a source contents should fail.
Browser* pip = CreateEmptyBrowserForType(Browser::TYPE_PICTURE_IN_PICTURE,
browser()->profile());
NavigateParams params = MakeNavigateParams(pip);
params.disposition = WindowOpenDisposition::NEW_PICTURE_IN_PICTURE;
params.source_contents = nullptr;
EXPECT_EQ(nullptr, Navigate(&params));
}
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
Disposition_PictureInPicture_CantFromAboutBlank) {
// Disallow document PiP windows from opening from a window with about:blank
// in the omnibox
Browser* pip = CreateEmptyBrowserForType(Browser::TYPE_PICTURE_IN_PICTURE,
browser()->profile());
NavigateParams params = MakeNavigateParams(pip);
params.disposition = WindowOpenDisposition::NEW_PICTURE_IN_PICTURE;
WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
EXPECT_TRUE(tab->GetLastCommittedURL().IsAboutBlank());
params.source_contents = tab;
EXPECT_EQ(nullptr, Navigate(&params));
}
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
Disposition_PictureInPicture_OpenFromWebApp) {
// Create the params for the PiP request that looks like it's from an app.
auto pip_options = blink::mojom::PictureInPictureWindowOptions::New();
// The WebContents holds the parameters from the PiP request.
WebContents::CreateParams web_contents_params(browser()->profile());
web_contents_params.picture_in_picture_options = *pip_options;
// Opening a picture in picture window should create a new browser.
NavigateParams params = MakeNavigateParams(browser());
params.disposition = WindowOpenDisposition::NEW_PICTURE_IN_PICTURE;
params.app_id = "extensionappid";
// Navigate to https:// page
net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
ASSERT_TRUE(https_server.Start());
WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
const GURL url = https_server.GetURL("/simple.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
params.source_contents = tab;
params.contents_to_insert = WebContents::Create(web_contents_params);
Navigate(&params);
// Should be PiP, with an app name.
EXPECT_TRUE(params.browser->is_type_picture_in_picture());
EXPECT_NE(params.browser->app_name(), std::string());
}
// Test typical popin UX flow.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, Popin) {
// Setup server.
embedded_https_test_server().SetSSLConfig(
net::EmbeddedTestServer::CERT_TEST_NAMES);
embedded_https_test_server().RegisterRequestHandler(
base::BindRepeating(&PopinRequestHandler));
ASSERT_TRUE(embedded_https_test_server().Start());
// Navigate to a.test.
const GURL url = embedded_https_test_server().GetURL("a.test", "/empty.html");
content::WebContents* tab_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
chrome::AddTabAt(browser(), GURL("about:blank"), -1, false);
// Open popin and verify it's visible.
content::WebContentsAddedObserver new_tab_observer;
EXPECT_TRUE(content::ExecJs(
tab_web_contents,
"window.open('" + url.spec() + "?popin_policy=*', '_blank', 'popin')"));
content::WebContents* popin_web_contents = new_tab_observer.GetWebContents();
BrowserWindow* popin_browser_window =
BrowserWindow::FindBrowserWindowWithWebContents(popin_web_contents);
EXPECT_NE(popin_browser_window, browser()->window());
EXPECT_TRUE(popin_browser_window->IsVisible());
// Focus new tab and verify popin is hidden.
browser()->tab_strip_model()->ActivateTabAt(1);
EXPECT_FALSE(popin_browser_window->IsVisible());
// Switch back to original tab and verify popin is visible.
browser()->tab_strip_model()->ActivateTabAt(0);
EXPECT_TRUE(popin_browser_window->IsVisible());
}
// Verify that a popin must not be insecure nor opened from an insecure context.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, PopinContext) {
// Setup servers.
embedded_https_test_server().SetSSLConfig(
net::EmbeddedTestServer::CERT_TEST_NAMES);
embedded_https_test_server().RegisterRequestHandler(
base::BindRepeating(&PopinRequestHandler));
ASSERT_TRUE(embedded_https_test_server().Start());
ASSERT_TRUE(embedded_test_server()->Start());
// Navigate to a.test.
const GURL secure_url =
embedded_https_test_server().GetURL("a.test", "/empty.html");
const GURL insecure_url =
embedded_test_server()->GetURL("a.test", "/empty.html");
content::WebContents* tab_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
// Insecure context and insecure popin fails.
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), insecure_url));
auto result = content::ExecJs(tab_web_contents,
"window.open('" + insecure_url.spec() +
"?popin_policy=*', '_blank', 'popin')");
EXPECT_TRUE(strstr(result.message(),
"Partitioned popins must be opened from https URLs."));
// Secure context and insecure popin fails.
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), secure_url));
result = content::ExecJs(tab_web_contents,
"window.open('" + insecure_url.spec() +
"?popin_policy=*', '_blank', 'popin')");
EXPECT_TRUE(
strstr(result.message(), "Partitioned popins can only open https URLs."));
// Insecure context and secure popin fails.
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), insecure_url));
result = content::ExecJs(tab_web_contents,
"window.open('" + secure_url.spec() +
"?popin_policy=*', '_blank', 'popin')");
EXPECT_TRUE(strstr(result.message(),
"Partitioned popins must be opened from https URLs."));
// Secure context and secure popin succeeds.
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), secure_url));
EXPECT_TRUE(content::ExecJs(tab_web_contents,
"window.open('" + secure_url.spec() +
"?popin_policy=*', '_blank', 'popin')"));
}
// Test that a popin cannot be opened from a popin
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, PopinRecursion) {
// Setup server.
embedded_https_test_server().SetSSLConfig(
net::EmbeddedTestServer::CERT_TEST_NAMES);
embedded_https_test_server().RegisterRequestHandler(
base::BindRepeating(&PopinRequestHandler));
ASSERT_TRUE(embedded_https_test_server().Start());
// Navigate to a.test.
const GURL url = embedded_https_test_server().GetURL("a.test", "/empty.html");
content::WebContents* tab_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
// Open popin and verify it's visible.
content::WebContentsAddedObserver new_tab_observer;
EXPECT_TRUE(content::ExecJs(
tab_web_contents,
"window.open('" + url.spec() + "?popin_policy=*', '_blank', 'popin')"));
content::WebContents* popin_web_contents = new_tab_observer.GetWebContents();
BrowserWindow* popin_browser_window =
BrowserWindow::FindBrowserWindowWithWebContents(popin_web_contents);
EXPECT_NE(popin_browser_window, browser()->window());
EXPECT_TRUE(popin_browser_window->IsVisible());
// Opening a popin from a popin fails.
const auto& result = content::ExecJs(
popin_web_contents,
"window.open('" + url.spec() + "?popin_policy=*', '_blank', 'popin')");
EXPECT_TRUE(strstr(result.message(),
"Partitioned popins cannot open their own popin."));
}
// Test only one popin can be opened by a given context.
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, PopinLimit) {
// Setup server.
embedded_https_test_server().SetSSLConfig(
net::EmbeddedTestServer::CERT_TEST_NAMES);
embedded_https_test_server().RegisterRequestHandler(
base::BindRepeating(&PopinRequestHandler));
ASSERT_TRUE(embedded_https_test_server().Start());
// Navigate to a.test.
const GURL url = embedded_https_test_server().GetURL("a.test", "/empty.html");
content::WebContents* tab_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
// Open popin and succeed.
content::WebContents* popin_web_contents;
{
content::WebContentsAddedObserver new_tab_observer;
EXPECT_TRUE(content::ExecJs(
tab_web_contents,
"window.open('" + url.spec() + "?popin_policy=*', '_blank', 'popin')"));
popin_web_contents = new_tab_observer.GetWebContents();
EXPECT_TRUE(popin_web_contents);
}
// Try to open second popin and fail.
content::WebContentsConsoleObserver console_observer(tab_web_contents);
console_observer.SetPattern(
"Only one partitioned popin can be active at a time.");
EXPECT_FALSE(
content::EvalJs(tab_web_contents,
"window.open('" + url.spec() +
"?popin_policy=*', '_blank', 'popin') != null")
.ExtractBool());
ASSERT_TRUE(console_observer.Wait());
// Close first popin and verify second can be opened.
BrowserWindow::FindBrowserWindowWithWebContents(popin_web_contents)->Close();
EXPECT_TRUE(
content::EvalJs(tab_web_contents,
"window.open('" + url.spec() +
"?popin_policy=*', '_blank', 'popin') != null")
.ExtractBool());
}
// Test that a popin cannot navigate to an HTTP page
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, PopinHttpNavigation) {
// Setup servers.
embedded_https_test_server().SetSSLConfig(
net::EmbeddedTestServer::CERT_TEST_NAMES);
embedded_https_test_server().RegisterRequestHandler(
base::BindRepeating(&PopinRequestHandler));
ASSERT_TRUE(embedded_https_test_server().Start());
embedded_test_server()->RegisterRequestHandler(
base::BindRepeating(&PopinRequestHandler));
ASSERT_TRUE(embedded_test_server()->Start());
// Navigate to a.test.
const GURL secure_url =
embedded_https_test_server().GetURL("a.test", "/empty.html");
const GURL insecure_url =
embedded_test_server()->GetURL("a.test", "/empty.html");
content::WebContents* tab_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), secure_url));
// Open popin and verify it's visible.
content::WebContentsAddedObserver new_tab_observer;
EXPECT_TRUE(content::ExecJs(tab_web_contents,
"window.open('" + secure_url.spec() +
"?popin_policy=*', '_blank', 'popin')"));
content::WebContents* popin_web_contents = new_tab_observer.GetWebContents();
BrowserWindow* popin_browser_window =
BrowserWindow::FindBrowserWindowWithWebContents(popin_web_contents);
EXPECT_NE(popin_browser_window, browser()->window());
EXPECT_TRUE(popin_browser_window->IsVisible());
// Navigating to HTTP page fails.
content::TestNavigationObserver nav_observer(popin_web_contents);
EXPECT_TRUE(content::ExecJs(
popin_web_contents,
"window.location = '" + insecure_url.spec() + "?popin_policy=*';"));
nav_observer.Wait();
EXPECT_EQ("chrome-error://chromewebdata/",
content::EvalJs(popin_web_contents, "window.location.href")
.ExtractString());
}
struct PopinPolicyTestParams {
std::string description;
std::string relative_url;
bool policy_allows;
};
class BrowserNavigatorPopinPolicyTest
: public BrowserNavigatorTest,
public testing::WithParamInterface<
std::tuple<PopinPolicyTestParams, PopinPolicyTestParams>> {
public:
PopinPolicyTestParams redirect_case() { return std::get<0>(GetParam()); }
PopinPolicyTestParams target_case() { return std::get<1>(GetParam()); }
};
// Test that the HTTP response header `Popin-Policy` is respected.
IN_PROC_BROWSER_TEST_P(BrowserNavigatorPopinPolicyTest, PopinPolicy) {
// Setup server.
embedded_https_test_server().SetSSLConfig(
net::EmbeddedTestServer::CERT_TEST_NAMES);
embedded_https_test_server().RegisterRequestHandler(
base::BindRepeating(&PopinRequestHandler));
ASSERT_TRUE(embedded_https_test_server().Start());
// Navigate to a.test.
const GURL url = embedded_https_test_server().GetURL("a.test", "/empty.html");
content::WebContents* tab_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
// Open the popin and ensure the target page only loads if both the redirect
// and the target policy permit it.
SCOPED_TRACE(redirect_case().description + " -> " +
target_case().description);
content::WebContentsAddedObserver new_tab_observer;
content::TestNavigationObserver nav_observer(nullptr);
nav_observer.StartWatchingNewWebContents();
EXPECT_TRUE(content::ExecJs(
tab_web_contents, "window.open('" +
embedded_https_test_server()
.GetURL("a.test", redirect_case().relative_url)
.spec() +
embedded_https_test_server()
.GetURL("a.test", target_case().relative_url)
.spec() +
"', '_blank', 'popin')"));
content::WebContents* popin_web_contents = new_tab_observer.GetWebContents();
EXPECT_TRUE(popin_web_contents);
nav_observer.Wait();
EXPECT_EQ((redirect_case().policy_allows && target_case().policy_allows)
? "a.test"
: "chromewebdata",
content::EvalJs(popin_web_contents, "window.location.hostname")
.ExtractString());
BrowserWindow::FindBrowserWindowWithWebContents(popin_web_contents)->Close();
}
// Test all policy combinations.
INSTANTIATE_TEST_SUITE_P(
/*no prefix*/,
BrowserNavigatorPopinPolicyTest,
testing::Combine(
testing::ValuesIn(std::vector<PopinPolicyTestParams>{
{
"unset policy",
"/empty.html?redirect=",
/*policy_allows*/ false,
},
{
"wildcard policy",
"/empty.html?popin_policy=*&redirect=",
/*policy_allows*/ true,
},
{
"none policy",
"/empty.html?popin_policy=()&redirect=",
/*policy_allows*/ false,
},
{
"origin a policy",
"/empty.html?popin_policy=(\"https://a.test/\")&redirect=",
/*policy_allows*/ true,
},
{
"origin b policy",
"/empty.html?popin_policy=(\"https://b.test/\")&redirect=",
/*policy_allows*/ false,
},
{
"origins policy",
"/empty.html?popin_policy=(\"https://a.test/\" "
"\"https://b.test/\")&redirect=",
/*policy_allows*/ true,
},
}),
testing::ValuesIn(std::vector<PopinPolicyTestParams>{
{
"unset policy",
"/empty.html",
/*policy_allows*/ false,
},
{
"wildcard policy",
"/empty.html?popin_policy=*",
/*policy_allows*/ true,
},
{
"none policy",
"/empty.html?popin_policy=()",
/*policy_allows*/ false,
},
{
"origin a policy",
"/empty.html?popin_policy=(\"https://a.test/\")",
/*policy_allows*/ true,
},
{
"origin b policy",
"/empty.html?popin_policy=(\"https://b.test/\")",
/*policy_allows*/ false,
},
{
"origins policy",
"/empty.html?popin_policy=(\"https://a.test/\" "
"\"https://b.test/\")",
/*policy_allows*/ true,
},
})));
class BrowserNavigatorPopinPolicyBypassTest
: public BrowserNavigatorTest,
public testing::WithParamInterface<bool> {
public:
BrowserNavigatorPopinPolicyBypassTest() {
scoped_feature_list_.InitWithFeatureStates({
{blink::features::kPartitionedPopins, true},
{features::kPartitionedPopinsHeaderPolicyBypass,
PartitionedPopinsHeaderPolicyBypass()},
{content_settings::features::kTrackingProtection3pcd, true},
});
}
bool PartitionedPopinsHeaderPolicyBypass() const { return GetParam(); }
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
// kPartitionedPopinsHeaderPolicyBypass allows `Popin-Policy` to be bypassed.
IN_PROC_BROWSER_TEST_P(BrowserNavigatorPopinPolicyBypassTest,
PopinPolicyBypass) {
// Setup server.
embedded_https_test_server().SetSSLConfig(
net::EmbeddedTestServer::CERT_TEST_NAMES);
ASSERT_TRUE(embedded_https_test_server().Start());
// Navigate to a.test.
const GURL url = embedded_https_test_server().GetURL("a.test", "/empty.html");
content::WebContents* tab_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
// Open the popin to a page without the policy and see it succeed if the
// feature was enabled.
content::WebContentsAddedObserver new_tab_observer;
content::TestNavigationObserver nav_observer(nullptr);
nav_observer.StartWatchingNewWebContents();
EXPECT_TRUE(content::ExecJs(tab_web_contents, "window.open('" + url.spec() +
"', '_blank', 'popin')"));
content::WebContents* popin_web_contents = new_tab_observer.GetWebContents();
EXPECT_TRUE(popin_web_contents);
nav_observer.Wait();
EXPECT_EQ(PartitionedPopinsHeaderPolicyBypass() ? "a.test" : "chromewebdata",
content::EvalJs(popin_web_contents, "window.location.hostname")
.ExtractString());
BrowserWindow::FindBrowserWindowWithWebContents(popin_web_contents)->Close();
}
// Test all policy combinations.
INSTANTIATE_TEST_SUITE_P(
/*no prefix*/,
BrowserNavigatorPopinPolicyBypassTest,
testing::Bool());
// Test that a popin cannot navigate to an HTTP page
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, PopinHttpRedirectNavigation) {
// Setup servers.
embedded_https_test_server().SetSSLConfig(
net::EmbeddedTestServer::CERT_TEST_NAMES);
embedded_https_test_server().RegisterRequestHandler(
base::BindRepeating(&PopinRequestHandler));
ASSERT_TRUE(embedded_https_test_server().Start());
ASSERT_TRUE(embedded_test_server()->Start());
// Navigate to a.test.
const GURL secure_url =
embedded_https_test_server().GetURL("a.test", "/empty.html");
const GURL insecure_url =
embedded_test_server()->GetURL("a.test", "/empty.html");
content::WebContents* tab_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), secure_url));
// Open popin and verify it's visible.
content::WebContentsAddedObserver new_tab_observer;
EXPECT_TRUE(content::ExecJs(tab_web_contents,
"window.open('" + secure_url.spec() +
"?popin_policy=*', '_blank', 'popin')"));
content::WebContents* popin_web_contents = new_tab_observer.GetWebContents();
BrowserWindow* popin_browser_window =
BrowserWindow::FindBrowserWindowWithWebContents(popin_web_contents);
EXPECT_NE(popin_browser_window, browser()->window());
EXPECT_TRUE(popin_browser_window->IsVisible());
// Navigating to HTTPS page that redirects to HTTP which fails.
content::TestNavigationObserver nav_observer(popin_web_contents);
EXPECT_TRUE(content::ExecJs(
popin_web_contents,
"window.location = '" + secure_url.spec() +
"?popin_policy=*&redirect=" + insecure_url.spec() + "';"));
nav_observer.Wait();
EXPECT_EQ("chrome-error://chromewebdata/",
content::EvalJs(popin_web_contents, "window.location.href")
.ExtractString());
}
// Verify that a popin cannot access third-party cookies when opened from a
// cross-site context. This scenario was crashing before crrev.com/c/5845330
IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
PopinFromCrossSiteContextAccessCookies) {
// Setup server.
embedded_https_test_server().SetSSLConfig(
net::EmbeddedTestServer::CERT_TEST_NAMES);
embedded_https_test_server().ServeFilesFromSourceDirectory(
"content/test/data");
ASSERT_TRUE(embedded_https_test_server().Start());
// Navigate to a.test.
const GURL url_a = embedded_https_test_server().GetURL(
"a.test", "/partitioned_popins/iframe_allow_popins.html");
content::WebContents* tab_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url_a));
// Set a cookie for a.test.
const GURL url_a_root = embedded_https_test_server().GetURL("a.test", "/");
ASSERT_TRUE(content::SetCookie(tab_web_contents->GetBrowserContext(),
url_a_root,
"site_a=cookie;SameSite=None;Secure"));
// Navigate the iframe to b.test and set a cookie.
const GURL url_b =
embedded_https_test_server().GetURL("b.test", "/empty.html");
ASSERT_TRUE(NavigateIframeToURL(tab_web_contents, "test", url_b));
const GURL url_b_root = embedded_https_test_server().GetURL("b.test", "/");
ASSERT_TRUE(
content::SetCookie(tab_web_contents->GetBrowserContext(), url_b_root,
"site_b=cookie;Partitioned;SameSite=None;Secure"));
// Open popin from the iframe and succeed.
content::RenderFrameHost* iframe =
ChildFrameAt(tab_web_contents->GetPrimaryMainFrame(), 0);
ASSERT_TRUE(iframe);
content::WebContentsAddedObserver new_tab_observer;
content::TestNavigationObserver nav_observer(nullptr);
nav_observer.StartWatchingNewWebContents();
EXPECT_TRUE(content::ExecJs(
iframe, "window.open('" + url_a.spec() + "', '_blank', 'popin')"));
content::WebContents* popin_web_contents = new_tab_observer.GetWebContents();
EXPECT_TRUE(popin_web_contents);
nav_observer.Wait();
// Read cookies from the popin. No cookies should be visible.
EXPECT_EQ(content::EvalJs(popin_web_contents, "document.cookie"), "");
}
#if !BUILDFLAG(IS_CHROMEOS)
// This class extends the basic logic in display::ScreenBase to allow us to mock
// the call to `GetDisplayNearestWindow`. This provides a way to ensure that the
// opener window is on a specific display, since the display::ScreenBase
// implementation only ever returns the primary display. This is not needed on
// Ash since Ash uses DisplayManagerTestApi.
class MockScreen : public display::ScreenBase {
public:
MockScreen() = default;
MockScreen(const MockScreen&) = delete;
const MockScreen& operator=(const MockScreen&) = delete;
~MockScreen() override { display::Screen::SetScreenInstance(nullptr); }
void Init() { display::Screen::SetScreenInstance(this); }
// display::ScreenBase:
display::Display GetDisplayNearestWindow(
gfx::NativeWindow window) const override {
return display_nearest_window_.value_or(
display::ScreenBase::GetDisplayNearestWindow(window));
}
void set_display_nearest_window(display::Display display) {
display_nearest_window_ = display;
}
private:
std::optional<display::Display> display_nearest_window_;
};
#endif // !BUILDFLAG(IS_CHROMEOS)
// Windows has assumptions that the screen is a ScreenWin, which causes a crash
// when we inject the MockScreen.
#if BUILDFLAG(IS_WIN)
#define MAYBE_BrowserNavigatorTestWithMockScreen \
DISABLED_BrowserNavigatorTestWithMockScreen
#else
#define MAYBE_BrowserNavigatorTestWithMockScreen \
BrowserNavigatorTestWithMockScreen
#endif
class MAYBE_BrowserNavigatorTestWithMockScreen : public BrowserNavigatorTest {
public:
void SetScreenInstance() override {
#if BUILDFLAG(IS_CHROMEOS)
// Use the default. See `SetUpOnMainThread`.
BrowserNavigatorTest::SetScreenInstance();
#else
mock_screen_.Init();
mock_screen_.display_list().AddDisplay({1, gfx::Rect(0, 0, 800, 700)},
display::DisplayList::Type::PRIMARY);
mock_screen_.display_list().AddDisplay(
{2, gfx::Rect(800, 0, 800, 700)},
display::DisplayList::Type::NOT_PRIMARY);
ASSERT_EQ(2, display::Screen::GetScreen()->GetNumDisplays());
#endif // BUILDFLAG(IS_CHROMEOS)
}
void SetUpOnMainThread() override {
#if BUILDFLAG(IS_CHROMEOS)
// This has to happen later than `SetScreenInstance` as the Ash shell does
// not exist yet.
display::test::DisplayManagerTestApi(ash::Shell::Get()->display_manager())
.UpdateDisplay("0+0-800x700,800+0-800x700");
ASSERT_EQ(2, display::Screen::GetScreen()->GetNumDisplays());
#endif // BUILDFLAG(IS_CHROMEOS)
}
#if !BUILDFLAG(IS_CHROMEOS)
protected:
MockScreen& mock_screen() { return mock_screen_; }
private:
MockScreen mock_screen_;
#endif // !BUILDFLAG(IS_CHROMEOS)
};
IN_PROC_BROWSER_TEST_F(MAYBE_BrowserNavigatorTestWithMockScreen,
Disposition_PictureInPicture_OpensInSameDisplay) {
// Create the params for the PiP request.
auto pip_options = blink::mojom::PictureInPictureWindowOptions::New();
pip_options->width = 500;
pip_options->height = 400;
WebContents::CreateParams web_contents_params(browser()->profile());
web_contents_params.picture_in_picture_options = *pip_options;
// Ensure we have the two displays.
ASSERT_EQ(2, display::Screen::GetScreen()->GetNumDisplays());
auto display1 = display::Screen::GetScreen()->GetAllDisplays()[0];
auto display2 = display::Screen::GetScreen()->GetAllDisplays()[1];
{
#if BUILDFLAG(IS_CHROMEOS)
// Put the opener on display 1.
browser()->window()->SetBounds(display1.work_area());
#else
// Make the MockScreen report the opener as being on display 1.
mock_screen().set_display_nearest_window(display1);
#endif // BUILDFLAG(IS_CHROMEOS)
// Ensure that the opener is on display 1.
const auto opener_display =
display::Screen::GetScreen()->GetDisplayNearestWindow(
browser()->window()->GetNativeWindow());
ASSERT_EQ(display1.id(), opener_display.id());
// Open the PiP window.
NavigateParams params = MakeNavigateParams(browser());
params.disposition = WindowOpenDisposition::NEW_PICTURE_IN_PICTURE;
// Navigate to https:// page
net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
ASSERT_TRUE(https_server.Start());
WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
const GURL url = https_server.GetURL("/simple.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
params.source_contents = tab;
params.contents_to_insert = WebContents::Create(web_contents_params);
Navigate(&params);
// The PiP window should also be on display 1.
EXPECT_TRUE(
display1.work_area().Contains(params.browser->window()->GetBounds()));
}
{
#if BUILDFLAG(IS_CHROMEOS)
// Put the opener on display 2.
browser()->window()->SetBounds(display2.work_area());
#else
// Make the MockScreen report the opener as being on display 2.
mock_screen().set_display_nearest_window(display2);
#endif // BUILDFLAG(IS_CHROMEOS)
// Ensure that the opener is on display 2.
const auto opener_display =
display::Screen::GetScreen()->GetDisplayNearestWindow(
browser()->window()->GetNativeWindow());
ASSERT_EQ(display2.id(), opener_display.id());
// Open the PiP window.
NavigateParams params = MakeNavigateParams(browser());
params.disposition = WindowOpenDisposition::NEW_PICTURE_IN_PICTURE;
// Navigate to https:// page
net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
ASSERT_TRUE(https_server.Start());
WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
const GURL url = https_server.GetURL("/simple.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
params.source_contents = tab;
params.contents_to_insert = WebContents::Create(web_contents_params);
Navigate(&params);
// The PiP window should also be on display 2.
#if BUILDFLAG(IS_OZONE)
if (!ui::OzonePlatform::GetInstance()
->GetPlatformProperties()
.supports_global_screen_coordinates) {
// Since we cannot get the global coordinates of the window, check
// if the window is in the correct display without relying on bounds.
const auto pip_window_display =
display::Screen::GetScreen()->GetDisplayNearestWindow(
params.browser->window()->GetNativeWindow());
ASSERT_EQ(display2.id(), pip_window_display.id());
return;
}
#endif
EXPECT_TRUE(
display2.work_area().Contains(params.browser->window()->GetBounds()));
}
}
} // namespace