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