blob: ef34c2b893300065d328733b5bdc2a8ba0fa2f7c [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/profiles/profile_window.h"
#include <stddef.h>
#include <utility>
#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind.h"
#include "base/test/test_future.h"
#include "base/values.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/devtools/devtools_window.h"
#include "chrome/browser/devtools/devtools_window_testing.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/profiles/profile_attributes_storage.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/signin/signin_util.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/find_bar/find_bar_state.h"
#include "chrome/browser/ui/find_bar/find_bar_state_factory.h"
#include "chrome/browser/ui/profile_picker.h"
#include "chrome/browser/ui/toolbar/app_menu_model.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/search_test_utils.h"
#include "chrome/test/base/ui_test_utils.h"
#include "chrome/test/base/web_ui_browser_test.h"
#include "components/account_id/account_id.h"
#include "components/history/core/browser/history_db_task.h"
#include "components/history/core/browser/history_service.h"
#include "components/search_engines/template_url_service.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_utils.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "url/gurl.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
#error "This test verifies the Desktop implementation of Guest only."
#endif
namespace {
// Code related to history borrowed from:
// chrome/browser/history/history_browsertest.cc
// Note: WaitableEvent is not used for synchronization between the main thread
// and history backend thread because the history subsystem posts tasks back
// to the main thread. Had we tried to Signal an event in such a task
// and Wait for it on the main thread, the task would not run at all because
// the main thread would be blocked on the Wait call, resulting in a deadlock.
// A task to be scheduled on the history backend thread.
// Notifies the main thread after all history backend thread tasks have run.
class WaitForHistoryTask : public history::HistoryDBTask {
public:
WaitForHistoryTask() = default;
WaitForHistoryTask(const WaitForHistoryTask&) = delete;
WaitForHistoryTask& operator=(const WaitForHistoryTask&) = delete;
bool RunOnDBThread(history::HistoryBackend* backend,
history::HistoryDatabase* db) override {
return true;
}
void DoneRunOnMainThread() override {
base::RunLoop::QuitCurrentWhenIdleDeprecated();
}
private:
~WaitForHistoryTask() override = default;
};
void WaitForHistoryBackendToRun(Profile* profile) {
base::CancelableTaskTracker task_tracker;
std::unique_ptr<history::HistoryDBTask> task(new WaitForHistoryTask());
history::HistoryService* history = HistoryServiceFactory::GetForProfile(
profile, ServiceAccessType::EXPLICIT_ACCESS);
history->ScheduleDBTask(FROM_HERE, std::move(task), &task_tracker);
content::RunMessageLoop();
}
class EmptyAcceleratorHandler : public ui::AcceleratorProvider {
public:
// Don't handle accelerators.
bool GetAcceleratorForCommandId(int command_id,
ui::Accelerator* accelerator) const override {
return false;
}
};
class BrowserAddedObserver : public BrowserListObserver {
public:
explicit BrowserAddedObserver(base::OnceCallback<void(Browser*)> callback)
: callback_(std::move(callback)) {
CHECK(callback_);
BrowserList::AddObserver(this);
}
void OnBrowserAdded(Browser* browser) override {
BrowserList::RemoveObserver(this);
std::move(callback_).Run(browser);
}
private:
base::OnceCallback<void(Browser*)> callback_;
};
} // namespace
class ProfileWindowBrowserTest : public InProcessBrowserTest {
public:
ProfileWindowBrowserTest() = default;
ProfileWindowBrowserTest(const ProfileWindowBrowserTest&) = delete;
ProfileWindowBrowserTest& operator=(const ProfileWindowBrowserTest&) = delete;
~ProfileWindowBrowserTest() override = default;
};
IN_PROC_BROWSER_TEST_F(ProfileWindowBrowserTest, CountForNullBrowser) {
EXPECT_EQ(size_t{0}, chrome::GetBrowserCount(nullptr));
EXPECT_EQ(0, BrowserList::GetOffTheRecordBrowsersActiveForProfile(nullptr));
}
class ProfileWindowCountBrowserTest : public ProfileWindowBrowserTest,
public testing::WithParamInterface<bool> {
protected:
ProfileWindowCountBrowserTest() = default;
bool is_incognito() { return GetParam(); }
int GetWindowCount() {
return is_incognito()
? BrowserList::GetOffTheRecordBrowsersActiveForProfile(
browser()->profile())
: BrowserList::GetGuestBrowserCount();
}
Browser* CreateGuestOrIncognitoBrowser() {
Browser* new_browser;
// When |profile_| is null this means no browsers have been created,
// this is the first browser instance.
if (!profile_) {
new_browser = is_incognito()
? CreateIncognitoBrowser(browser()->profile())
: CreateGuestBrowser();
profile_ = new_browser->profile();
} else {
new_browser = CreateIncognitoBrowser(profile_);
}
return new_browser;
}
private:
raw_ptr<Profile, DanglingUntriaged> profile_ = nullptr;
};
IN_PROC_BROWSER_TEST_P(ProfileWindowCountBrowserTest, CountProfileWindows) {
EXPECT_EQ(0, GetWindowCount());
// Create a browser and check the count.
Browser* browser1 = CreateGuestOrIncognitoBrowser();
EXPECT_EQ(1, GetWindowCount());
// Create another browser and check the count.
Browser* browser2 = CreateGuestOrIncognitoBrowser();
EXPECT_EQ(2, GetWindowCount());
// Close one browser and count.
CloseBrowserSynchronously(browser2);
EXPECT_EQ(1, GetWindowCount());
// Close another browser and count.
CloseBrowserSynchronously(browser1);
EXPECT_EQ(0, GetWindowCount());
}
// |OpenDevToolsWindowSync| is slow on Linux Debug and can result in flacky test
// failure. See (crbug.com/1186994).
#if BUILDFLAG(IS_LINUX) && !defined(NDEBUG)
#define MAYBE_DevToolsWindowsNotCounted DISABLED_DevToolsWindowsNotCounted
#else
#define MAYBE_DevToolsWindowsNotCounted DevToolsWindowsNotCounted
#endif
IN_PROC_BROWSER_TEST_P(ProfileWindowCountBrowserTest,
MAYBE_DevToolsWindowsNotCounted) {
Browser* browser = CreateGuestOrIncognitoBrowser();
EXPECT_EQ(1, GetWindowCount());
DevToolsWindow* devtools_window =
DevToolsWindowTesting::OpenDevToolsWindowSync(browser,
/*is_docked=*/true);
EXPECT_EQ(1, GetWindowCount());
DevToolsWindowTesting::CloseDevToolsWindowSync(devtools_window);
devtools_window = DevToolsWindowTesting::OpenDevToolsWindowSync(
browser, /*is_docked=*/false);
EXPECT_EQ(1, GetWindowCount());
DevToolsWindowTesting::CloseDevToolsWindowSync(devtools_window);
EXPECT_EQ(1, GetWindowCount());
}
INSTANTIATE_TEST_SUITE_P(All, ProfileWindowCountBrowserTest, testing::Bool());
IN_PROC_BROWSER_TEST_F(ProfileWindowBrowserTest, OpenGuestBrowser) {
EXPECT_TRUE(CreateGuestBrowser());
}
IN_PROC_BROWSER_TEST_F(ProfileWindowBrowserTest, GuestIsOffTheRecord) {
EXPECT_TRUE(CreateGuestBrowser()->profile()->IsOffTheRecord());
}
IN_PROC_BROWSER_TEST_F(ProfileWindowBrowserTest, GuestIgnoresHistory) {
Browser* guest_browser = CreateGuestBrowser();
ui_test_utils::WaitForHistoryToLoad(HistoryServiceFactory::GetForProfile(
guest_browser->profile(), ServiceAccessType::EXPLICIT_ACCESS));
GURL test_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(FILE_PATH_LITERAL("title2.html")));
ASSERT_TRUE(ui_test_utils::NavigateToURL(guest_browser, test_url));
WaitForHistoryBackendToRun(guest_browser->profile());
std::vector<GURL> urls =
ui_test_utils::HistoryEnumerator(guest_browser->profile()).urls();
ASSERT_EQ(0u, urls.size());
}
IN_PROC_BROWSER_TEST_F(ProfileWindowBrowserTest, GuestClearsCookies) {
Browser* guest_browser = CreateGuestBrowser();
Profile* guest_profile = guest_browser->profile();
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL("/set-cookie?cookie1"));
// Before navigation there are no cookies for the URL.
std::string cookie = content::GetCookies(guest_profile, url);
ASSERT_EQ("", cookie);
// After navigation there is a cookie for the URL.
ASSERT_TRUE(ui_test_utils::NavigateToURL(guest_browser, url));
cookie = content::GetCookies(guest_profile, url);
EXPECT_EQ("cookie1", cookie);
CloseBrowserSynchronously(guest_browser);
// Closing the browser has removed the cookie.
cookie = content::GetCookies(guest_profile, url);
ASSERT_EQ("", cookie);
}
IN_PROC_BROWSER_TEST_F(ProfileWindowBrowserTest, GuestClearsFindInPageCache) {
Browser* guest_browser = CreateGuestBrowser();
Profile* guest_profile = guest_browser->profile();
std::u16string fip_text = u"first guest session search text";
FindBarStateFactory::GetForBrowserContext(guest_profile)
->SetLastSearchText(fip_text);
// Open a second guest window and close one. This should not affect the find
// in page cache as the guest session hasn't been ended.
profiles::FindOrCreateNewWindowForProfile(
guest_profile, chrome::startup::IsProcessStartup::kNo,
chrome::startup::IsFirstRun::kNo, true /*always_create*/);
CloseBrowserSynchronously(guest_browser);
EXPECT_EQ(fip_text, FindBarStateFactory::GetForBrowserContext(guest_profile)
->GetSearchPrepopulateText());
// Close the remaining guest browser window.
guest_browser = chrome::FindAnyBrowser(guest_profile, true);
EXPECT_TRUE(guest_browser);
CloseBrowserSynchronously(guest_browser);
content::RunAllTasksUntilIdle();
// Open a new guest browser window. Since this is a separate session, the find
// in page text should have been cleared (along with all other browsing data).
profiles::FindOrCreateNewWindowForProfile(
guest_profile, chrome::startup::IsProcessStartup::kNo,
chrome::startup::IsFirstRun::kNo, true /*always_create*/);
EXPECT_EQ(std::u16string(),
FindBarStateFactory::GetForBrowserContext(guest_profile)
->GetSearchPrepopulateText());
}
IN_PROC_BROWSER_TEST_F(ProfileWindowBrowserTest, GuestCannotSignin) {
Browser* guest_browser = CreateGuestBrowser();
signin::IdentityManager* identity_manager =
IdentityManagerFactory::GetForProfile(guest_browser->profile());
// Guest profiles can't sign in without a IdentityManager.
ASSERT_FALSE(identity_manager);
}
IN_PROC_BROWSER_TEST_F(ProfileWindowBrowserTest, GuestAppMenuLacksBookmarks) {
EmptyAcceleratorHandler accelerator_handler;
// Verify the normal browser has a bookmark menu.
AppMenuModel model_normal_profile(&accelerator_handler, browser());
model_normal_profile.Init();
EXPECT_TRUE(
model_normal_profile.GetIndexOfCommandId(IDC_BOOKMARKS_MENU).has_value());
// Guest browser has no bookmark menu.
Browser* guest_browser = CreateGuestBrowser();
AppMenuModel model_guest_profile(&accelerator_handler, guest_browser);
EXPECT_FALSE(
model_guest_profile.GetIndexOfCommandId(IDC_BOOKMARKS_MENU).has_value());
}
IN_PROC_BROWSER_TEST_F(ProfileWindowBrowserTest, OpenBrowserWindowForProfile) {
Profile* profile = browser()->profile();
size_t num_browsers = BrowserList::GetInstance()->size();
base::test::TestFuture<Browser*> future;
profiles::OpenBrowserWindowForProfile(future.GetCallback(), true, false,
false, profile);
ASSERT_TRUE(future.Get());
EXPECT_NE(browser(), future.Get());
EXPECT_EQ(profile, future.Get()->profile());
EXPECT_EQ(num_browsers + 1, BrowserList::GetInstance()->size());
EXPECT_FALSE(ProfilePicker::IsOpen());
}
// Regression test for https://crbug.com/1433283
IN_PROC_BROWSER_TEST_F(ProfileWindowBrowserTest,
OpenTwoBrowserWindowsForProfile) {
Profile* profile = browser()->profile();
size_t num_browsers = BrowserList::GetInstance()->size();
base::test::TestFuture<Browser*> future;
profiles::OpenBrowserWindowForProfile(future.GetCallback(), true, false,
false, profile);
CreateBrowser(profile);
EXPECT_EQ(profile, future.Get()->profile());
EXPECT_EQ(num_browsers + 2, BrowserList::GetInstance()->size());
EXPECT_FALSE(ProfilePicker::IsOpen());
}
IN_PROC_BROWSER_TEST_F(ProfileWindowBrowserTest,
OpenBrowserWindowForProfileBrowserDestroyed) {
Profile* profile = browser()->profile();
size_t num_browsers = BrowserList::GetInstance()->size();
BrowserAddedObserver browser_added_observer(base::BindLambdaForTesting(
[this](Browser* browser) { this->CloseBrowserAsynchronously(browser); }));
base::test::TestFuture<Browser*> future;
profiles::OpenBrowserWindowForProfile(future.GetCallback(), true, false,
false, profile);
EXPECT_EQ(nullptr, future.Get());
EXPECT_EQ(num_browsers, BrowserList::GetInstance()->size());
EXPECT_FALSE(ProfilePicker::IsOpen());
}
// TODO(crbug.com/935746): Test is flaky on Win and Linux.
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN)
#define MAYBE_OpenBrowserWindowForProfileWithSigninRequired \
DISABLED_OpenBrowserWindowForProfileWithSigninRequired
#else
#define MAYBE_OpenBrowserWindowForProfileWithSigninRequired \
OpenBrowserWindowForProfileWithSigninRequired
#endif
IN_PROC_BROWSER_TEST_F(ProfileWindowBrowserTest,
MAYBE_OpenBrowserWindowForProfileWithSigninRequired) {
signin_util::ScopedForceSigninSetterForTesting force_signin_setter(true);
Profile* profile = browser()->profile();
ProfileAttributesEntry* entry =
g_browser_process->profile_manager()
->GetProfileAttributesStorage()
.GetProfileAttributesWithPath(profile->GetPath());
ASSERT_NE(entry, nullptr);
entry->LockForceSigninProfile(true);
size_t num_browsers = BrowserList::GetInstance()->size();
base::RunLoop run_loop;
ProfilePicker::AddOnProfilePickerOpenedCallbackForTesting(
run_loop.QuitClosure());
profiles::OpenBrowserWindowForProfile(base::OnceCallback<void(Browser*)>(),
true, false, false, profile);
run_loop.Run();
EXPECT_EQ(num_browsers, BrowserList::GetInstance()->size());
EXPECT_TRUE(ProfilePicker::IsOpen());
}
class ProfileWindowWebUIBrowserTest : public WebUIBrowserTest {
public:
void OnSystemProfileCreated(std::string* url_to_test,
base::OnceClosure quit_loop,
Profile* profile,
const std::string& url) {
*url_to_test = url;
std::move(quit_loop).Run();
}
private:
void SetUpOnMainThread() override {
WebUIBrowserTest::SetUpOnMainThread();
AddLibrary(
base::FilePath(FILE_PATH_LITERAL("profile_window_browsertest.js")));
}
};