blob: 478ce107f3f38e2654029a446057538fe05faa0e [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/browser.h"
#include "base/memory/raw_ptr.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/prefs/incognito_mode_prefs.h"
#include "chrome/browser/ui/browser_command_controller.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "components/zoom/zoom_controller.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/site_instance.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/web_contents_tester.h"
#include "printing/buildflags/buildflags.h"
#include "third_party/skia/include/core/SkColor.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
#include "components/session_manager/core/session_manager.h"
#include "components/user_manager/fake_user_manager.h"
#include "components/user_manager/scoped_user_manager.h"
#include "components/user_manager/user_names.h"
#endif
using content::SiteInstance;
using content::WebContents;
using content::WebContentsTester;
#if BUILDFLAG(IS_CHROMEOS_ASH)
using session_manager::SessionState;
#endif
class BrowserUnitTest : public BrowserWithTestWindowTest {
public:
BrowserUnitTest() = default;
BrowserUnitTest(const BrowserUnitTest&) = delete;
BrowserUnitTest& operator=(const BrowserUnitTest&) = delete;
~BrowserUnitTest() override = default;
// Caller owns the memory.
std::unique_ptr<WebContents> CreateTestWebContents() {
return WebContentsTester::CreateTestWebContents(
profile(), SiteInstance::Create(profile()));
}
};
// Ensure crashed tabs are not reloaded when selected. crbug.com/232323
TEST_F(BrowserUnitTest, ReloadCrashedTab) {
TabStripModel* tab_strip_model = browser()->tab_strip_model();
// Start with a single foreground tab. |tab_strip_model| owns the memory.
std::unique_ptr<WebContents> contents1 = CreateTestWebContents();
content::WebContents* raw_contents1 = contents1.get();
tab_strip_model->AppendWebContents(std::move(contents1), true);
WebContentsTester::For(raw_contents1)->NavigateAndCommit(GURL("about:blank"));
WebContentsTester::For(raw_contents1)->TestSetIsLoading(false);
EXPECT_TRUE(tab_strip_model->IsTabSelected(0));
EXPECT_FALSE(raw_contents1->IsLoading());
// Add a second tab in the background.
std::unique_ptr<WebContents> contents2 = CreateTestWebContents();
content::WebContents* raw_contents2 = contents2.get();
tab_strip_model->AppendWebContents(std::move(contents2), false);
WebContentsTester::For(raw_contents2)->NavigateAndCommit(GURL("about:blank"));
WebContentsTester::For(raw_contents2)->TestSetIsLoading(false);
EXPECT_EQ(2, browser()->tab_strip_model()->count());
EXPECT_TRUE(tab_strip_model->IsTabSelected(0));
EXPECT_FALSE(raw_contents2->IsLoading());
// Simulate the second tab crashing.
WebContentsTester::For(raw_contents2)
->SetIsCrashed(base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
EXPECT_TRUE(raw_contents2->IsCrashed());
// Selecting the second tab does not cause a load or clear the crash.
tab_strip_model->ActivateTabAt(
1, TabStripUserGestureDetails(
TabStripUserGestureDetails::GestureType::kOther));
EXPECT_TRUE(tab_strip_model->IsTabSelected(1));
EXPECT_FALSE(raw_contents2->IsLoading());
EXPECT_TRUE(raw_contents2->IsCrashed());
}
// This tests a workaround which is not necessary on Mac.
// https://crbug.com/719230
#if BUILDFLAG(IS_MAC)
#define MAYBE_SetBackgroundColorForNewTab DISABLED_SetBackgroundColorForNewTab
#else
#define MAYBE_SetBackgroundColorForNewTab SetBackgroundColorForNewTab
#endif
TEST_F(BrowserUnitTest, MAYBE_SetBackgroundColorForNewTab) {
TabStripModel* tab_strip_model = browser()->tab_strip_model();
std::unique_ptr<WebContents> contents1 = CreateTestWebContents();
content::WebContents* raw_contents1 = contents1.get();
tab_strip_model->AppendWebContents(std::move(contents1), true);
WebContentsTester::For(raw_contents1)->NavigateAndCommit(GURL("about:blank"));
WebContentsTester::For(raw_contents1)->TestSetIsLoading(false);
raw_contents1->GetPrimaryMainFrame()->GetView()->SetBackgroundColor(
SK_ColorRED);
// Add a second tab in the background.
std::unique_ptr<WebContents> contents2 = CreateTestWebContents();
content::WebContents* raw_contents2 = contents2.get();
tab_strip_model->AppendWebContents(std::move(contents2), false);
WebContentsTester::For(raw_contents2)->NavigateAndCommit(GURL("about:blank"));
WebContentsTester::For(raw_contents2)->TestSetIsLoading(false);
tab_strip_model->ActivateTabAt(
1, TabStripUserGestureDetails(
TabStripUserGestureDetails::GestureType::kOther));
ASSERT_TRUE(
raw_contents2->GetPrimaryMainFrame()->GetView()->GetBackgroundColor());
EXPECT_EQ(
SK_ColorRED,
*raw_contents2->GetPrimaryMainFrame()->GetView()->GetBackgroundColor());
}
#if BUILDFLAG(ENABLE_PRINTING)
// Ensure the print command gets disabled when a tab crashes.
TEST_F(BrowserUnitTest, DisablePrintOnCrashedTab) {
TabStripModel* tab_strip_model = browser()->tab_strip_model();
std::unique_ptr<WebContents> contents = CreateTestWebContents();
content::WebContents* raw_contents = contents.get();
tab_strip_model->AppendWebContents(std::move(contents), true);
WebContentsTester::For(raw_contents)->NavigateAndCommit(GURL("about:blank"));
CommandUpdater* command_updater = browser()->command_controller();
EXPECT_FALSE(raw_contents->IsCrashed());
EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_PRINT));
EXPECT_TRUE(chrome::CanPrint(browser()));
WebContentsTester::For(raw_contents)
->SetIsCrashed(base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
EXPECT_TRUE(raw_contents->IsCrashed());
EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_PRINT));
EXPECT_FALSE(chrome::CanPrint(browser()));
}
#endif // BUILDFLAG(ENABLE_PRINTING)
// Ensure the zoom-in and zoom-out commands get disabled when a tab crashes.
TEST_F(BrowserUnitTest, DisableZoomOnCrashedTab) {
TabStripModel* tab_strip_model = browser()->tab_strip_model();
std::unique_ptr<WebContents> contents = CreateTestWebContents();
content::WebContents* raw_contents = contents.get();
tab_strip_model->AppendWebContents(std::move(contents), true);
WebContentsTester::For(raw_contents)->NavigateAndCommit(GURL("about:blank"));
zoom::ZoomController* zoom_controller =
zoom::ZoomController::FromWebContents(raw_contents);
EXPECT_TRUE(
zoom_controller->SetZoomLevel(zoom_controller->GetDefaultZoomLevel()));
CommandUpdater* command_updater = browser()->command_controller();
EXPECT_TRUE(zoom_controller->IsAtDefaultZoom());
EXPECT_FALSE(raw_contents->IsCrashed());
EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_ZOOM_PLUS));
EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_ZOOM_MINUS));
EXPECT_TRUE(chrome::CanZoomIn(raw_contents));
EXPECT_TRUE(chrome::CanZoomOut(raw_contents));
WebContentsTester::For(raw_contents)
->SetIsCrashed(base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
EXPECT_TRUE(raw_contents->IsCrashed());
EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_ZOOM_PLUS));
EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_ZOOM_MINUS));
EXPECT_FALSE(chrome::CanZoomIn(raw_contents));
EXPECT_FALSE(chrome::CanZoomOut(raw_contents));
}
TEST_F(BrowserUnitTest, CreateBrowserFailsIfProfileDisallowsBrowserWindows) {
TestingProfile::Builder profile_builder;
profile_builder.DisallowBrowserWindows();
std::unique_ptr<TestingProfile> test_profile = profile_builder.Build();
TestingProfile::Builder otr_profile_builder;
otr_profile_builder.DisallowBrowserWindows();
otr_profile_builder.BuildIncognito(test_profile.get());
// Verify creating browser fails in both original and OTR version of the
// profile.
EXPECT_EQ(Browser::CreationStatus::kErrorProfileUnsuitable,
Browser::GetCreationStatusForProfile(test_profile.get()));
EXPECT_EQ(Browser::CreationStatus::kErrorProfileUnsuitable,
Browser::GetCreationStatusForProfile(
test_profile->GetPrimaryOTRProfile(/*create_if_needed=*/true)));
}
// Tests BrowserCreate() when Incognito mode is disabled.
TEST_F(BrowserUnitTest, CreateBrowserWithIncognitoModeDisabled) {
IncognitoModePrefs::SetAvailability(
profile()->GetPrefs(), IncognitoModePrefs::Availability::kDisabled);
// Creating a browser window in OTR profile should fail if incognito is
// disabled.
EXPECT_EQ(Browser::CreationStatus::kErrorProfileUnsuitable,
Browser::GetCreationStatusForProfile(
profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true)));
// Verify creating a browser in the original profile succeeds.
Browser::CreateParams create_params(profile(), false);
std::unique_ptr<BrowserWindow> test_window(CreateBrowserWindow());
create_params.window = test_window.get();
std::unique_ptr<Browser> test_browser(Browser::Create(create_params));
EXPECT_TRUE(test_browser);
}
// Tests BrowserCreate() when Incognito mode is forced.
TEST_F(BrowserUnitTest, CreateBrowserWithIncognitoModeForced) {
IncognitoModePrefs::SetAvailability(
profile()->GetPrefs(), IncognitoModePrefs::Availability::kForced);
// Creating a browser window in the original profile should fail if incognito
// is forced.
EXPECT_EQ(Browser::CreationStatus::kErrorProfileUnsuitable,
Browser::GetCreationStatusForProfile(profile()));
// Creating a browser in OTR test profile should succeed.
Browser::CreateParams off_the_record_create_params(
profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true), false);
std::unique_ptr<BrowserWindow> test_window(CreateBrowserWindow());
off_the_record_create_params.window = test_window.get();
std::unique_ptr<Browser> otr_browser(
Browser::Create(off_the_record_create_params));
EXPECT_TRUE(otr_browser);
}
// Tests BrowserCreate() with not restrictions on incognito mode.
TEST_F(BrowserUnitTest, CreateBrowserWithIncognitoModeEnabled) {
ASSERT_EQ(IncognitoModePrefs::Availability::kEnabled,
IncognitoModePrefs::GetAvailability(profile()->GetPrefs()));
// Creating a browser in the original test profile should succeed.
Browser::CreateParams create_params(profile(), false);
std::unique_ptr<BrowserWindow> test_window(CreateBrowserWindow());
create_params.window = test_window.get();
std::unique_ptr<Browser> test_browser(Browser::Create(create_params));
EXPECT_TRUE(test_browser);
// Creating a browser in OTR test profile should succeed.
Browser::CreateParams off_the_record_create_params(
profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true), false);
std::unique_ptr<BrowserWindow> otr_test_window(CreateBrowserWindow());
off_the_record_create_params.window = otr_test_window.get();
std::unique_ptr<Browser> otr_browser(
Browser::Create(off_the_record_create_params));
EXPECT_TRUE(otr_browser);
}
#if BUILDFLAG(IS_CHROMEOS_ASH)
TEST_F(BrowserUnitTest, CreateBrowserDuringKioskSplashScreen) {
// Setting up user manager state to be in kiosk mode:
// Creating a new user manager.
auto* user_manager = new ash::FakeChromeUserManager();
user_manager::ScopedUserManager manager{
std::unique_ptr<user_manager::UserManager>(user_manager)};
const user_manager::User* user =
user_manager->AddKioskAppUser(AccountId::FromUserEmail("fake_user@test"));
user_manager->LoginUser(user->GetAccountId());
TestingProfile profile;
session_manager::SessionManager::Get()->SetSessionState(
SessionState::LOGIN_PRIMARY);
// Browser should not be created during login session state.
EXPECT_EQ(Browser::CreationStatus::kErrorLoadingKiosk,
Browser::GetCreationStatusForProfile(&profile));
Browser::CreateParams create_params = Browser::CreateParams(&profile, false);
std::unique_ptr<BrowserWindow> window = CreateBrowserWindow();
create_params.window = window.get();
session_manager::SessionManager::Get()->SetSessionState(SessionState::ACTIVE);
std::unique_ptr<Browser> test_browser(Browser::Create(create_params));
// Normal flow, creation succeeds.
EXPECT_TRUE(test_browser);
}
#endif
class BrowserBookmarkBarTest : public BrowserWithTestWindowTest {
public:
BrowserBookmarkBarTest() {}
BrowserBookmarkBarTest(const BrowserBookmarkBarTest&) = delete;
BrowserBookmarkBarTest& operator=(const BrowserBookmarkBarTest&) = delete;
~BrowserBookmarkBarTest() override {}
protected:
BookmarkBar::State window_bookmark_bar_state() const {
return static_cast<BookmarkBarStateTestBrowserWindow*>(browser()->window())
->bookmark_bar_state();
}
// BrowserWithTestWindowTest:
void SetUp() override {
BrowserWithTestWindowTest::SetUp();
static_cast<BookmarkBarStateTestBrowserWindow*>(browser()->window())
->set_browser(browser());
}
std::unique_ptr<BrowserWindow> CreateBrowserWindow() override {
return std::make_unique<BookmarkBarStateTestBrowserWindow>();
}
private:
class BookmarkBarStateTestBrowserWindow : public TestBrowserWindow {
public:
BookmarkBarStateTestBrowserWindow()
: browser_(nullptr), bookmark_bar_state_(BookmarkBar::HIDDEN) {}
BookmarkBarStateTestBrowserWindow(
const BookmarkBarStateTestBrowserWindow&) = delete;
BookmarkBarStateTestBrowserWindow& operator=(
const BookmarkBarStateTestBrowserWindow&) = delete;
~BookmarkBarStateTestBrowserWindow() override {}
void set_browser(Browser* browser) { browser_ = browser; }
BookmarkBar::State bookmark_bar_state() const {
return bookmark_bar_state_;
}
private:
// TestBrowserWindow:
void BookmarkBarStateChanged(
BookmarkBar::AnimateChangeType change_type) override {
bookmark_bar_state_ = browser_->bookmark_bar_state();
TestBrowserWindow::BookmarkBarStateChanged(change_type);
}
void OnActiveTabChanged(content::WebContents* old_contents,
content::WebContents* new_contents,
int index,
int reason) override {
bookmark_bar_state_ = browser_->bookmark_bar_state();
TestBrowserWindow::OnActiveTabChanged(old_contents, new_contents, index,
reason);
}
raw_ptr<Browser> browser_; // Weak ptr.
BookmarkBar::State bookmark_bar_state_;
};
};
// Ensure bookmark bar states in Browser and BrowserWindow are in sync after
// Browser::ActiveTabChanged() calls BrowserWindow::OnActiveTabChanged().
TEST_F(BrowserBookmarkBarTest, StateOnActiveTabChanged) {
ASSERT_EQ(BookmarkBar::HIDDEN, browser()->bookmark_bar_state());
ASSERT_EQ(BookmarkBar::HIDDEN, window_bookmark_bar_state());
GURL ntp_url("chrome://newtab");
GURL non_ntp_url("http://foo");
// Open a tab to NTP.
AddTab(browser(), ntp_url);
EXPECT_EQ(BookmarkBar::HIDDEN, browser()->bookmark_bar_state());
EXPECT_EQ(BookmarkBar::HIDDEN, window_bookmark_bar_state());
// Navigate 1st tab to a non-NTP URL.
NavigateAndCommitActiveTab(non_ntp_url);
EXPECT_EQ(BookmarkBar::HIDDEN, browser()->bookmark_bar_state());
EXPECT_EQ(BookmarkBar::HIDDEN, window_bookmark_bar_state());
// Open a tab to NTP at index 0.
AddTab(browser(), ntp_url);
EXPECT_EQ(BookmarkBar::HIDDEN, browser()->bookmark_bar_state());
EXPECT_EQ(BookmarkBar::HIDDEN, window_bookmark_bar_state());
// Activate the 2nd tab which is non-NTP.
browser()->tab_strip_model()->ActivateTabAt(
1, TabStripUserGestureDetails(
TabStripUserGestureDetails::GestureType::kOther));
EXPECT_EQ(BookmarkBar::HIDDEN, browser()->bookmark_bar_state());
EXPECT_EQ(BookmarkBar::HIDDEN, window_bookmark_bar_state());
// Toggle bookmark bar while 2nd tab (non-NTP) is active.
chrome::ToggleBookmarkBar(browser());
EXPECT_EQ(BookmarkBar::SHOW, browser()->bookmark_bar_state());
EXPECT_EQ(BookmarkBar::SHOW, window_bookmark_bar_state());
// Activate the 1st tab which is NTP.
browser()->tab_strip_model()->ActivateTabAt(
0, TabStripUserGestureDetails(
TabStripUserGestureDetails::GestureType::kOther));
EXPECT_EQ(BookmarkBar::SHOW, browser()->bookmark_bar_state());
EXPECT_EQ(BookmarkBar::SHOW, window_bookmark_bar_state());
// Activate the 2nd tab which is non-NTP.
browser()->tab_strip_model()->ActivateTabAt(
1, TabStripUserGestureDetails(
TabStripUserGestureDetails::GestureType::kOther));
EXPECT_EQ(BookmarkBar::SHOW, browser()->bookmark_bar_state());
EXPECT_EQ(BookmarkBar::SHOW, window_bookmark_bar_state());
}
// Tests that Browser::Create creates a guest session browser.
TEST_F(BrowserUnitTest, CreateGuestSessionBrowser) {
TestingProfile* test_profile = profile_manager()->CreateGuestProfile();
TestingProfile::Builder otr_profile_builder;
otr_profile_builder.SetGuestSession();
Profile* guest_profile = nullptr;
// Try creating a browser in original guest profile - it should fail.
EXPECT_EQ(Browser::CreationStatus::kErrorProfileUnsuitable,
Browser::GetCreationStatusForProfile(test_profile));
// Create OTR profile for the Guest profile.
EXPECT_TRUE(otr_profile_builder.BuildIncognito(test_profile));
guest_profile = test_profile->GetPrimaryOTRProfile(/*create_if_needed=*/true);
// Creating a browser should succeed.
Browser::CreateParams create_params =
Browser::CreateParams(guest_profile, false);
std::unique_ptr<BrowserWindow> test_window = CreateBrowserWindow();
create_params.window = test_window.get();
std::unique_ptr<Browser> browser =
std::unique_ptr<Browser>(Browser::Create(create_params));
EXPECT_TRUE(browser);
}