1 // Copyright (c) 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/profiles/profile_window.h"
6
7 #include <stddef.h>
8 #include <utility>
9
10 #include "base/bind.h"
11 #include "base/command_line.h"
12 #include "base/run_loop.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/values.h"
15 #include "build/build_config.h"
16 #include "chrome/app/chrome_command_ids.h"
17 #include "chrome/browser/browser_process.h"
18 #include "chrome/browser/devtools/devtools_window.h"
19 #include "chrome/browser/devtools/devtools_window_testing.h"
20 #include "chrome/browser/history/history_service_factory.h"
21 #include "chrome/browser/profiles/profile_attributes_storage.h"
22 #include "chrome/browser/profiles/profile_manager.h"
23 #include "chrome/browser/search_engines/template_url_service_factory.h"
24 #include "chrome/browser/signin/identity_manager_factory.h"
25 #include "chrome/browser/ui/browser.h"
26 #include "chrome/browser/ui/browser_finder.h"
27 #include "chrome/browser/ui/browser_list.h"
28 #include "chrome/browser/ui/find_bar/find_bar_state.h"
29 #include "chrome/browser/ui/find_bar/find_bar_state_factory.h"
30 #include "chrome/browser/ui/toolbar/app_menu_model.h"
31 #include "chrome/browser/ui/user_manager.h"
32 #include "chrome/test/base/in_process_browser_test.h"
33 #include "chrome/test/base/search_test_utils.h"
34 #include "chrome/test/base/testing_profile.h"
35 #include "chrome/test/base/ui_test_utils.h"
36 #include "chrome/test/base/web_ui_browser_test.h"
37 #include "components/account_id/account_id.h"
38 #include "components/history/core/browser/history_db_task.h"
39 #include "components/history/core/browser/history_service.h"
40 #include "components/search_engines/template_url_service.h"
41 #include "content/public/test/browser_test.h"
42 #include "content/public/test/browser_test_utils.h"
43 #include "content/public/test/test_utils.h"
44 #include "net/test/embedded_test_server/embedded_test_server.h"
45 #include "url/gurl.h"
46
47 #if defined(OS_CHROMEOS)
48 #error "This test verifies the Desktop implementation of Guest only."
49 #endif
50
51 namespace {
52
53 enum ProfileWindowType { INCOGNITO, GUEST, EPHEMERAL_GUEST };
54
55 // Code related to history borrowed from:
56 // chrome/browser/history/history_browsertest.cc
57
58 // Note: WaitableEvent is not used for synchronization between the main thread
59 // and history backend thread because the history subsystem posts tasks back
60 // to the main thread. Had we tried to Signal an event in such a task
61 // and Wait for it on the main thread, the task would not run at all because
62 // the main thread would be blocked on the Wait call, resulting in a deadlock.
63
64 // A task to be scheduled on the history backend thread.
65 // Notifies the main thread after all history backend thread tasks have run.
66 class WaitForHistoryTask : public history::HistoryDBTask {
67 public:
68 WaitForHistoryTask() = default;
69 WaitForHistoryTask(const WaitForHistoryTask&) = delete;
70 WaitForHistoryTask& operator=(const WaitForHistoryTask&) = delete;
71
RunOnDBThread(history::HistoryBackend * backend,history::HistoryDatabase * db)72 bool RunOnDBThread(history::HistoryBackend* backend,
73 history::HistoryDatabase* db) override {
74 return true;
75 }
76
DoneRunOnMainThread()77 void DoneRunOnMainThread() override {
78 base::RunLoop::QuitCurrentWhenIdleDeprecated();
79 }
80
81 private:
82 ~WaitForHistoryTask() override = default;
83 };
84
WaitForHistoryBackendToRun(Profile * profile)85 void WaitForHistoryBackendToRun(Profile* profile) {
86 base::CancelableTaskTracker task_tracker;
87 std::unique_ptr<history::HistoryDBTask> task(new WaitForHistoryTask());
88 history::HistoryService* history = HistoryServiceFactory::GetForProfile(
89 profile, ServiceAccessType::EXPLICIT_ACCESS);
90 history->ScheduleDBTask(FROM_HERE, std::move(task), &task_tracker);
91 content::RunMessageLoop();
92 }
93
94 class EmptyAcceleratorHandler : public ui::AcceleratorProvider {
95 public:
96 // Don't handle accelerators.
GetAcceleratorForCommandId(int command_id,ui::Accelerator * accelerator) const97 bool GetAcceleratorForCommandId(int command_id,
98 ui::Accelerator* accelerator) const override {
99 return false;
100 }
101 };
102
CreateTestingProfile(const std::string & name,const std::string & relative_path)103 base::FilePath CreateTestingProfile(const std::string& name,
104 const std::string& relative_path) {
105 ProfileManager* manager = g_browser_process->profile_manager();
106 ProfileAttributesStorage& storage = manager->GetProfileAttributesStorage();
107 size_t starting_number_of_profiles = storage.GetNumberOfProfiles();
108
109 base::FilePath profile_path =
110 manager->user_data_dir().AppendASCII(relative_path);
111 storage.AddProfile(profile_path, base::ASCIIToUTF16(name), std::string(),
112 base::string16(), false, 0u, std::string(),
113 EmptyAccountId());
114
115 EXPECT_EQ(starting_number_of_profiles + 1u, storage.GetNumberOfProfiles());
116 return profile_path;
117 }
118
119 } // namespace
120
121 class ProfileWindowBrowserTest : public InProcessBrowserTest {
122 public:
123 ProfileWindowBrowserTest() = default;
124 ProfileWindowBrowserTest(const ProfileWindowBrowserTest&) = delete;
125 ProfileWindowBrowserTest& operator=(const ProfileWindowBrowserTest&) = delete;
126 ~ProfileWindowBrowserTest() override = default;
127 };
128
129 class ProfileWindowCountBrowserTest
130 : public ProfileWindowBrowserTest,
131 public testing::WithParamInterface<ProfileWindowType> {
132 protected:
ProfileWindowCountBrowserTest()133 ProfileWindowCountBrowserTest() {
134 ProfileWindowType profile_type = GetParam();
135 is_incognito_ = profile_type == ProfileWindowType::INCOGNITO;
136 if (!is_incognito_)
137 TestingProfile::SetScopedFeatureListForEphemeralGuestProfiles(
138 scoped_feature_list_,
139 profile_type == ProfileWindowType::EPHEMERAL_GUEST);
140 }
141
GetWindowCount()142 int GetWindowCount() {
143 return is_incognito_ ? BrowserList::GetOffTheRecordBrowsersActiveForProfile(
144 browser()->profile())
145 : BrowserList::GetGuestBrowserCount();
146 }
147
CreateGuestOrIncognitoBrowser()148 Browser* CreateGuestOrIncognitoBrowser() {
149 Browser* new_browser;
150 // When |profile_| is null this means no browsers have been created,
151 // this is the first browser instance.
152 // |is_incognito_| is used to determine which browser type to open.
153 if (!profile_) {
154 new_browser = is_incognito_ ? CreateIncognitoBrowser(browser()->profile())
155 : CreateGuestBrowser();
156 profile_ = new_browser->profile();
157 } else {
158 if (profile_->IsEphemeralGuestProfile())
159 new_browser = CreateBrowser(profile_);
160 else
161 new_browser = CreateIncognitoBrowser(profile_);
162 }
163
164 return new_browser;
165 }
166
167 private:
168 bool is_incognito_;
169 base::test::ScopedFeatureList scoped_feature_list_;
170 Profile* profile_ = nullptr;
171 };
172
IN_PROC_BROWSER_TEST_P(ProfileWindowCountBrowserTest,CountProfileWindows)173 IN_PROC_BROWSER_TEST_P(ProfileWindowCountBrowserTest, CountProfileWindows) {
174 DCHECK_EQ(0, GetWindowCount());
175
176 // Create a browser and check the count.
177 Browser* browser1 = CreateGuestOrIncognitoBrowser();
178 DCHECK_EQ(1, GetWindowCount());
179
180 // Create another browser and check the count.
181 Browser* browser2 = CreateGuestOrIncognitoBrowser();
182 DCHECK_EQ(2, GetWindowCount());
183
184 // Open a docked DevTool window and count.
185 DevToolsWindow* devtools_window =
186 DevToolsWindowTesting::OpenDevToolsWindowSync(browser1, true);
187 DCHECK_EQ(2, GetWindowCount());
188 DevToolsWindowTesting::CloseDevToolsWindowSync(devtools_window);
189
190 // Open a detached DevTool window and count.
191 devtools_window =
192 DevToolsWindowTesting::OpenDevToolsWindowSync(browser1, false);
193 DCHECK_EQ(2, GetWindowCount());
194 DevToolsWindowTesting::CloseDevToolsWindowSync(devtools_window);
195
196 // Close one browser and count.
197 CloseBrowserSynchronously(browser2);
198 DCHECK_EQ(1, GetWindowCount());
199
200 // Close another browser and count.
201 CloseBrowserSynchronously(browser1);
202 DCHECK_EQ(0, GetWindowCount());
203 }
204
205 INSTANTIATE_TEST_SUITE_P(All,
206 ProfileWindowCountBrowserTest,
207 testing::Values(ProfileWindowType::INCOGNITO,
208 ProfileWindowType::GUEST,
209 ProfileWindowType::EPHEMERAL_GUEST));
210
211 class GuestProfileWindowBrowserTest : public ProfileWindowBrowserTest,
212 public testing::WithParamInterface<bool> {
213 protected:
GuestProfileWindowBrowserTest()214 GuestProfileWindowBrowserTest() {
215 is_ephemeral_ = GetParam();
216
217 // Change the value if Ephemeral is not supported.
218 is_ephemeral_ &=
219 TestingProfile::SetScopedFeatureListForEphemeralGuestProfiles(
220 scoped_feature_list_, is_ephemeral_);
221 }
222
IsEphemeral()223 bool IsEphemeral() { return is_ephemeral_; }
224
225 private:
226 bool is_ephemeral_;
227 base::test::ScopedFeatureList scoped_feature_list_;
228 };
229
IN_PROC_BROWSER_TEST_P(GuestProfileWindowBrowserTest,OpenGuestBrowser)230 IN_PROC_BROWSER_TEST_P(GuestProfileWindowBrowserTest, OpenGuestBrowser) {
231 EXPECT_TRUE(CreateGuestBrowser());
232 }
233
IN_PROC_BROWSER_TEST_P(GuestProfileWindowBrowserTest,GuestIsOffTheRecord)234 IN_PROC_BROWSER_TEST_P(GuestProfileWindowBrowserTest, GuestIsOffTheRecord) {
235 Profile* guest_profile = CreateGuestBrowser()->profile();
236 if (IsEphemeral())
237 EXPECT_FALSE(guest_profile->IsOffTheRecord());
238 else
239 EXPECT_TRUE(guest_profile->IsOffTheRecord());
240 }
241
IN_PROC_BROWSER_TEST_P(GuestProfileWindowBrowserTest,GuestIgnoresHistory)242 IN_PROC_BROWSER_TEST_P(GuestProfileWindowBrowserTest, GuestIgnoresHistory) {
243 Browser* guest_browser = CreateGuestBrowser();
244
245 ui_test_utils::WaitForHistoryToLoad(HistoryServiceFactory::GetForProfile(
246 guest_browser->profile(), ServiceAccessType::EXPLICIT_ACCESS));
247
248 GURL test_url = ui_test_utils::GetTestUrl(
249 base::FilePath(base::FilePath::kCurrentDirectory),
250 base::FilePath(FILE_PATH_LITERAL("title2.html")));
251
252 ui_test_utils::NavigateToURL(guest_browser, test_url);
253 WaitForHistoryBackendToRun(guest_browser->profile());
254
255 std::vector<GURL> urls =
256 ui_test_utils::HistoryEnumerator(guest_browser->profile()).urls();
257
258 unsigned int expect_history =
259 guest_browser->profile()->IsEphemeralGuestProfile() ? 1 : 0;
260 ASSERT_EQ(expect_history, urls.size());
261 }
262
IN_PROC_BROWSER_TEST_P(GuestProfileWindowBrowserTest,GuestClearsCookies)263 IN_PROC_BROWSER_TEST_P(GuestProfileWindowBrowserTest, GuestClearsCookies) {
264 Browser* guest_browser = CreateGuestBrowser();
265 Profile* guest_profile = guest_browser->profile();
266
267 ASSERT_TRUE(embedded_test_server()->Start());
268 GURL url(embedded_test_server()->GetURL("/set-cookie?cookie1"));
269
270 // Before navigation there are no cookies for the URL.
271 std::string cookie = content::GetCookies(guest_profile, url);
272 ASSERT_EQ("", cookie);
273
274 // After navigation there is a cookie for the URL.
275 ui_test_utils::NavigateToURL(guest_browser, url);
276 cookie = content::GetCookies(guest_profile, url);
277 EXPECT_EQ("cookie1", cookie);
278
279 CloseBrowserSynchronously(guest_browser);
280
281 // Closing the browser has removed the cookie.
282 cookie = content::GetCookies(guest_profile, url);
283 ASSERT_EQ("", cookie);
284 }
285
IN_PROC_BROWSER_TEST_P(GuestProfileWindowBrowserTest,GuestClearsFindInPageCache)286 IN_PROC_BROWSER_TEST_P(GuestProfileWindowBrowserTest,
287 GuestClearsFindInPageCache) {
288 Browser* guest_browser = CreateGuestBrowser();
289 Profile* guest_profile = guest_browser->profile();
290
291 base::string16 fip_text =
292 base::ASCIIToUTF16("first guest session search text");
293 FindBarStateFactory::GetForBrowserContext(guest_profile)
294 ->SetLastSearchText(fip_text);
295
296 // Open a second guest window and close one. This should not affect the find
297 // in page cache as the guest session hasn't been ended.
298 profiles::FindOrCreateNewWindowForProfile(
299 guest_profile, chrome::startup::IS_NOT_PROCESS_STARTUP,
300 chrome::startup::IS_NOT_FIRST_RUN, true /*always_create*/);
301 CloseBrowserSynchronously(guest_browser);
302 EXPECT_EQ(fip_text, FindBarStateFactory::GetForBrowserContext(guest_profile)
303 ->GetSearchPrepopulateText());
304
305 // Close the remaining guest browser window.
306 guest_browser = chrome::FindAnyBrowser(guest_profile, true);
307 EXPECT_TRUE(guest_browser);
308 CloseBrowserSynchronously(guest_browser);
309
310 // Open a new guest browser window. Since this is a separate session, the find
311 // in page text should have been cleared (along with all other browsing data).
312 // For ephemeral Guest profiles, after closing the last Guest browser the
313 // Guest profile is scheduled for deletion and is not considered a Guest
314 // profile anymore. Therefore the next Guest window requires opening a new
315 // browser and refreshing the profile object.
316 if (IsEphemeral()) {
317 guest_profile = CreateGuestBrowser()->profile();
318 } else {
319 profiles::FindOrCreateNewWindowForProfile(
320 guest_profile, chrome::startup::IS_NOT_PROCESS_STARTUP,
321 chrome::startup::IS_NOT_FIRST_RUN, true /*always_create*/);
322 }
323 EXPECT_EQ(base::string16(),
324 FindBarStateFactory::GetForBrowserContext(guest_profile)
325 ->GetSearchPrepopulateText());
326 }
327
IN_PROC_BROWSER_TEST_P(GuestProfileWindowBrowserTest,GuestCannotSignin)328 IN_PROC_BROWSER_TEST_P(GuestProfileWindowBrowserTest, GuestCannotSignin) {
329 // TODO(https://crbug.com/1125474): Enable the test after identity manager is
330 // updated for ephemeral Guest profiles.
331 if (IsEphemeral())
332 return;
333
334 Browser* guest_browser = CreateGuestBrowser();
335
336 signin::IdentityManager* identity_manager =
337 IdentityManagerFactory::GetForProfile(guest_browser->profile());
338
339 // Guest profiles can't sign in without a IdentityManager.
340 ASSERT_FALSE(identity_manager);
341 }
342
IN_PROC_BROWSER_TEST_P(GuestProfileWindowBrowserTest,GuestAppMenuLacksBookmarks)343 IN_PROC_BROWSER_TEST_P(GuestProfileWindowBrowserTest,
344 GuestAppMenuLacksBookmarks) {
345 EmptyAcceleratorHandler accelerator_handler;
346 // Verify the normal browser has a bookmark menu.
347 AppMenuModel model_normal_profile(&accelerator_handler, browser());
348 model_normal_profile.Init();
349 EXPECT_NE(-1, model_normal_profile.GetIndexOfCommandId(IDC_BOOKMARKS_MENU));
350
351 // Guest browser has no bookmark menu.
352 Browser* guest_browser = CreateGuestBrowser();
353 AppMenuModel model_guest_profile(&accelerator_handler, guest_browser);
354 EXPECT_EQ(-1, model_guest_profile.GetIndexOfCommandId(IDC_BOOKMARKS_MENU));
355 }
356
357 INSTANTIATE_TEST_SUITE_P(GuestProfileWindowBrowserTest,
358 GuestProfileWindowBrowserTest,
359 /*is_ephemeral=*/testing::Bool());
360
IN_PROC_BROWSER_TEST_F(ProfileWindowBrowserTest,OpenBrowserWindowForProfile)361 IN_PROC_BROWSER_TEST_F(ProfileWindowBrowserTest, OpenBrowserWindowForProfile) {
362 Profile* profile = browser()->profile();
363 size_t num_browsers = BrowserList::GetInstance()->size();
364 profiles::OpenBrowserWindowForProfile(
365 ProfileManager::CreateCallback(), true, false, false, profile,
366 Profile::CreateStatus::CREATE_STATUS_INITIALIZED);
367 base::RunLoop().RunUntilIdle();
368 EXPECT_EQ(num_browsers + 1, BrowserList::GetInstance()->size());
369 EXPECT_FALSE(UserManager::IsShowing());
370 }
371
372 // TODO(crbug.com/935746): Test is flaky on Win and Linux.
373 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_WIN)
374 #define MAYBE_OpenBrowserWindowForProfileWithSigninRequired \
375 DISABLED_OpenBrowserWindowForProfileWithSigninRequired
376 #else
377 #define MAYBE_OpenBrowserWindowForProfileWithSigninRequired \
378 OpenBrowserWindowForProfileWithSigninRequired
379 #endif
IN_PROC_BROWSER_TEST_F(ProfileWindowBrowserTest,MAYBE_OpenBrowserWindowForProfileWithSigninRequired)380 IN_PROC_BROWSER_TEST_F(ProfileWindowBrowserTest,
381 MAYBE_OpenBrowserWindowForProfileWithSigninRequired) {
382 Profile* profile = browser()->profile();
383 ProfileAttributesEntry* entry;
384 ASSERT_TRUE(g_browser_process->profile_manager()
385 ->GetProfileAttributesStorage()
386 .GetProfileAttributesWithPath(profile->GetPath(), &entry));
387 entry->SetIsSigninRequired(true);
388 size_t num_browsers = BrowserList::GetInstance()->size();
389 base::RunLoop run_loop;
390 UserManager::AddOnUserManagerShownCallbackForTesting(run_loop.QuitClosure());
391 profiles::OpenBrowserWindowForProfile(
392 ProfileManager::CreateCallback(), true, false, false, profile,
393 Profile::CreateStatus::CREATE_STATUS_INITIALIZED);
394 run_loop.Run();
395 EXPECT_EQ(num_browsers, BrowserList::GetInstance()->size());
396 EXPECT_TRUE(UserManager::IsShowing());
397 }
398
399 class ProfileWindowWebUIBrowserTest : public WebUIBrowserTest {
400 public:
OnSystemProfileCreated(std::string * url_to_test,base::OnceClosure quit_loop,Profile * profile,const std::string & url)401 void OnSystemProfileCreated(std::string* url_to_test,
402 base::OnceClosure quit_loop,
403 Profile* profile,
404 const std::string& url) {
405 *url_to_test = url;
406 std::move(quit_loop).Run();
407 }
408
409 private:
SetUpOnMainThread()410 void SetUpOnMainThread() override {
411 WebUIBrowserTest::SetUpOnMainThread();
412 AddLibrary(base::FilePath(
413 FILE_PATH_LITERAL("profile_window_browsertest.js")));
414 }
415 };
416
IN_PROC_BROWSER_TEST_F(ProfileWindowWebUIBrowserTest,UserManagerFocusSingleProfile)417 IN_PROC_BROWSER_TEST_F(ProfileWindowWebUIBrowserTest,
418 UserManagerFocusSingleProfile) {
419 std::string url_to_test;
420 base::RunLoop run_loop;
421 profiles::CreateSystemProfileForUserManager(
422 browser()->profile()->GetPath(),
423 profiles::USER_MANAGER_SELECT_PROFILE_NO_ACTION,
424 base::BindRepeating(
425 &ProfileWindowWebUIBrowserTest::OnSystemProfileCreated,
426 base::Unretained(this), &url_to_test, run_loop.QuitClosure()));
427 run_loop.Run();
428
429 ui_test_utils::NavigateToURL(browser(), GURL(url_to_test));
430 EXPECT_TRUE(RunJavascriptTest("testNoPodFocused"));
431 }
432
433 // This test is flaky, see https://crbug.com/611619.
IN_PROC_BROWSER_TEST_F(ProfileWindowWebUIBrowserTest,DISABLED_UserManagerFocusMultipleProfiles)434 IN_PROC_BROWSER_TEST_F(ProfileWindowWebUIBrowserTest,
435 DISABLED_UserManagerFocusMultipleProfiles) {
436 // The profile names are meant to sort differently by ICU collation and by
437 // naive sorting. See crbug/596280.
438 base::FilePath expected_path = CreateTestingProfile("#abc", "Profile 1");
439 CreateTestingProfile("?abc", "Profile 2");
440
441 std::string url_to_test;
442 base::RunLoop run_loop;
443 profiles::CreateSystemProfileForUserManager(
444 expected_path, profiles::USER_MANAGER_SELECT_PROFILE_NO_ACTION,
445 base::BindRepeating(
446 &ProfileWindowWebUIBrowserTest::OnSystemProfileCreated,
447 base::Unretained(this), &url_to_test, run_loop.QuitClosure()));
448 run_loop.Run();
449
450 ui_test_utils::NavigateToURL(browser(), GURL(url_to_test));
451 EXPECT_TRUE(RunJavascriptTest("testPodFocused",
452 base::Value(expected_path.AsUTF8Unsafe())));
453 }
454