| // Copyright 2020 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/views/profiles/profile_picker_view.h" |
| |
| #include "base/callback_helpers.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/test/bind.h" |
| #include "base/test/mock_callback.h" |
| #include "base/time/time.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/policy/cloud/user_policy_signin_service.h" |
| #include "chrome/browser/policy/cloud/user_policy_signin_service_factory.h" |
| #include "chrome/browser/profiles/profile_attributes_entry.h" |
| #include "chrome/browser/profiles/profile_attributes_storage.h" |
| #include "chrome/browser/profiles/profile_manager.h" |
| #include "chrome/browser/signin/identity_manager_factory.h" |
| #include "chrome/browser/sync/profile_sync_service_factory.h" |
| #include "chrome/browser/themes/theme_service.h" |
| #include "chrome/browser/themes/theme_service_factory.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/sync/profile_signin_confirmation_helper.h" |
| #include "chrome/browser/ui/tab_dialogs.h" |
| #include "chrome/browser/ui/ui_features.h" |
| #include "chrome/browser/ui/webui/signin/login_ui_service.h" |
| #include "chrome/browser/ui/webui/signin/login_ui_service_factory.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "components/keyed_service/content/browser_context_dependency_manager.h" |
| #include "components/policy/core/common/management/management_service.h" |
| #include "components/policy/core/common/management/scoped_management_service_override_for_testing.h" |
| #include "components/signin/public/identity_manager/account_info.h" |
| #include "components/signin/public/identity_manager/identity_manager.h" |
| #include "components/signin/public/identity_manager/identity_test_utils.h" |
| #include "components/sync/base/sync_prefs.h" |
| #include "components/sync/driver/sync_driver_switches.h" |
| #include "components/sync/driver/sync_service.h" |
| #include "components/sync/driver/sync_user_settings.h" |
| #include "content/public/browser/web_contents_observer.h" |
| #include "content/public/test/browser_test.h" |
| #include "content/public/test/browser_test_utils.h" |
| #include "google_apis/gaia/gaia_urls.h" |
| #include "third_party/skia/include/core/SkColor.h" |
| #include "ui/views/view_observer.h" |
| |
| namespace { |
| |
| const SkColor kProfileColor = SK_ColorRED; |
| const char kWork[] = "Work"; |
| |
| AccountInfo FillAccountInfo( |
| const CoreAccountInfo& core_info, |
| const std::string& given_name, |
| const std::string& hosted_domain = kNoHostedDomainFound) { |
| AccountInfo account_info; |
| account_info.email = core_info.email; |
| account_info.gaia = core_info.gaia; |
| account_info.account_id = core_info.account_id; |
| account_info.is_under_advanced_protection = |
| core_info.is_under_advanced_protection; |
| account_info.full_name = "Test Full Name"; |
| account_info.given_name = given_name; |
| account_info.hosted_domain = hosted_domain; |
| account_info.locale = "en"; |
| account_info.picture_url = "https://get-avatar.com/foo"; |
| account_info.is_child_account = false; |
| return account_info; |
| } |
| |
| // Waits until a first non empty paint for given `url`. |
| class FirstVisuallyNonEmptyPaintObserver : public content::WebContentsObserver { |
| public: |
| explicit FirstVisuallyNonEmptyPaintObserver(content::WebContents* contents, |
| const GURL& url) |
| : content::WebContentsObserver(contents), url_(url) {} |
| |
| void DidFirstVisuallyNonEmptyPaint() override { |
| if (web_contents()->GetVisibleURL() == url_) |
| run_loop_.Quit(); |
| } |
| |
| void Wait() { |
| if (IsExitConditionSatisfied()) { |
| return; |
| } |
| run_loop_.Run(); |
| EXPECT_TRUE(IsExitConditionSatisfied()) |
| << web_contents()->GetVisibleURL() << " != " << url_; |
| } |
| |
| private: |
| bool IsExitConditionSatisfied() { |
| return (web_contents()->GetVisibleURL() == url_ && |
| web_contents()->CompletedFirstVisuallyNonEmptyPaint()); |
| } |
| |
| base::RunLoop run_loop_{base::RunLoop::Type::kNestableTasksAllowed}; |
| GURL url_; |
| }; |
| |
| class WebViewAddedWaiter : public views::ViewObserver { |
| public: |
| WebViewAddedWaiter( |
| views::View* top_view, |
| base::RepeatingCallback<views::WebView*()> current_web_view_getter) |
| : current_web_view_getter_(current_web_view_getter) { |
| observation_.Observe(top_view); |
| } |
| ~WebViewAddedWaiter() override = default; |
| |
| void Wait() { run_loop_.Run(); } |
| |
| private: |
| // ViewObserver: |
| void OnChildViewAdded(views::View* observed_view, |
| views::View* child) override { |
| if (child == current_web_view_getter_.Run()) { |
| ASSERT_TRUE(child); |
| run_loop_.Quit(); |
| } |
| } |
| |
| base::RunLoop run_loop_; |
| base::RepeatingCallback<views::WebView*()> current_web_view_getter_; |
| base::ScopedObservation<views::View, views::ViewObserver> observation_{this}; |
| }; |
| |
| class BrowserAddedWaiter : public BrowserListObserver { |
| public: |
| explicit BrowserAddedWaiter(size_t total_count) : total_count_(total_count) { |
| BrowserList::AddObserver(this); |
| } |
| ~BrowserAddedWaiter() override { BrowserList::RemoveObserver(this); } |
| |
| Browser* Wait() { |
| if (BrowserList::GetInstance()->size() == total_count_) |
| return BrowserList::GetInstance()->GetLastActive(); |
| run_loop_.Run(); |
| EXPECT_TRUE(browser_); |
| return browser_; |
| } |
| |
| private: |
| // BrowserListObserver implementation. |
| void OnBrowserAdded(Browser* browser) override { |
| if (BrowserList::GetInstance()->size() != total_count_) |
| return; |
| browser_ = browser; |
| run_loop_.Quit(); |
| } |
| |
| const size_t total_count_; |
| Browser* browser_ = nullptr; |
| base::RunLoop run_loop_; |
| |
| DISALLOW_COPY_AND_ASSIGN(BrowserAddedWaiter); |
| }; |
| |
| // Fake user policy signin service immediately invoking the callbacks. |
| class FakeUserPolicySigninService : public policy::UserPolicySigninService { |
| public: |
| static std::unique_ptr<KeyedService> Build(content::BrowserContext* context) { |
| Profile* profile = Profile::FromBrowserContext(context); |
| return std::make_unique<FakeUserPolicySigninService>( |
| profile, IdentityManagerFactory::GetForProfile(profile), std::string(), |
| std::string()); |
| } |
| |
| static std::unique_ptr<KeyedService> BuildForEnterprise( |
| content::BrowserContext* context) { |
| Profile* profile = Profile::FromBrowserContext(context); |
| // Non-empty dm token & client id means enterprise account. |
| return std::make_unique<FakeUserPolicySigninService>( |
| profile, IdentityManagerFactory::GetForProfile(profile), "foo", "bar"); |
| } |
| |
| FakeUserPolicySigninService(Profile* profile, |
| signin::IdentityManager* identity_manager, |
| const std::string& dm_token, |
| const std::string& client_id) |
| : UserPolicySigninService(profile, |
| nullptr, |
| nullptr, |
| nullptr, |
| identity_manager, |
| nullptr), |
| dm_token_(dm_token), |
| client_id_(client_id) {} |
| |
| // policy::UserPolicySigninService: |
| void RegisterForPolicyWithAccountId( |
| const std::string& username, |
| const CoreAccountId& account_id, |
| PolicyRegistrationCallback callback) override { |
| std::move(callback).Run(dm_token_, client_id_); |
| } |
| |
| // policy::UserPolicySigninServiceBase: |
| void FetchPolicyForSignedInUser( |
| const AccountId& account_id, |
| const std::string& dm_token, |
| const std::string& client_id, |
| scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory, |
| PolicyFetchCallback callback) override { |
| std::move(callback).Run(true); |
| } |
| |
| private: |
| std::string dm_token_; |
| std::string client_id_; |
| }; |
| |
| class TestTabDialogs : public TabDialogs { |
| public: |
| TestTabDialogs(content::WebContents* contents, base::RunLoop* run_loop) |
| : contents_(contents), run_loop_(run_loop) {} |
| ~TestTabDialogs() override = default; |
| |
| // Creates a platform specific instance, and attaches it to |contents|. |
| // If an instance is already attached, it overwrites it. |
| static void OverwriteForWebContents(content::WebContents* contents, |
| base::RunLoop* run_loop) { |
| DCHECK(contents); |
| contents->SetUserData(UserDataKey(), |
| std::make_unique<TestTabDialogs>(contents, run_loop)); |
| } |
| |
| gfx::NativeView GetDialogParentView() const override { |
| return contents_->GetNativeView(); |
| } |
| void ShowCollectedCookies() override {} |
| void ShowHungRendererDialog( |
| content::RenderWidgetHost* render_widget_host, |
| base::RepeatingClosure hang_monitor_restarter) override {} |
| void HideHungRendererDialog( |
| content::RenderWidgetHost* render_widget_host) override {} |
| bool IsShowingHungRendererDialog() override { return false; } |
| |
| void ShowProfileSigninConfirmation( |
| Browser* browser, |
| Profile* profile, |
| const std::string& username, |
| std::unique_ptr<ui::ProfileSigninConfirmationDelegate> delegate) |
| override { |
| delegate->OnContinueSignin(); |
| run_loop_->Quit(); |
| } |
| |
| void ShowManagePasswordsBubble(bool user_action) override {} |
| void HideManagePasswordsBubble() override {} |
| |
| private: |
| content::WebContents* contents_; |
| base::RunLoop* run_loop_; |
| }; |
| |
| class ProfilePickerCreationFlowBrowserTest : public InProcessBrowserTest { |
| public: |
| ProfilePickerCreationFlowBrowserTest() { |
| feature_list_.InitWithFeatures( |
| {features::kProfilesUIRevamp, features::kNewProfilePicker}, {}); |
| } |
| |
| void SetUpInProcessBrowserTestFixture() override { |
| InProcessBrowserTest::SetUpInProcessBrowserTestFixture(); |
| create_services_subscription_ = |
| BrowserContextDependencyManager::GetInstance() |
| ->RegisterCreateServicesCallbackForTesting( |
| base::Bind(&ProfilePickerCreationFlowBrowserTest:: |
| OnWillCreateBrowserContextServices, |
| base::Unretained(this))); |
| } |
| |
| virtual void OnWillCreateBrowserContextServices( |
| content::BrowserContext* context) { |
| policy::UserPolicySigninServiceFactory::GetInstance()->SetTestingFactory( |
| context, base::BindRepeating(&FakeUserPolicySigninService::Build)); |
| } |
| |
| views::View* view() { return ProfilePicker::GetViewForTesting(); } |
| |
| views::WebView* web_view() { return ProfilePicker::GetWebViewForTesting(); } |
| |
| void WaitForNewWebView() { |
| ASSERT_TRUE(view()); |
| WebViewAddedWaiter( |
| view(), |
| base::BindRepeating(&ProfilePickerCreationFlowBrowserTest::web_view, |
| base::Unretained(this))) |
| .Wait(); |
| EXPECT_TRUE(web_view()); |
| } |
| |
| content::WebContents* web_contents() { |
| if (!web_view()) |
| return nullptr; |
| return web_view()->GetWebContents(); |
| } |
| |
| private: |
| base::CallbackListSubscription create_services_subscription_; |
| base::test::ScopedFeatureList feature_list_; |
| |
| // The sync service and waits for policies to load before starting for |
| // enterprise users, managed devices and browsers. This means that services |
| // depending on it might have to wait too. By setting the management |
| // authorities to none by default, we assume that the default test is on an |
| // unmanaged device and browser thus we avoid unnecessarily waiting for |
| // policies to load. Tests expecting either an enterprise user, a managed |
| // device or browser should add the appropriate management authorities. |
| policy::ScopedManagementServiceOverrideForTesting browser_management_ = |
| policy::ScopedManagementServiceOverrideForTesting( |
| policy::ManagementTarget::BROWSER, |
| base::flat_set<policy::EnterpriseManagementAuthority>()); |
| policy::ScopedManagementServiceOverrideForTesting platform_management_ = |
| policy::ScopedManagementServiceOverrideForTesting( |
| policy::ManagementTarget::PLATFORM, |
| base::flat_set<policy::EnterpriseManagementAuthority>()); |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(ProfilePickerCreationFlowBrowserTest, ShowChoice) { |
| ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuAddNewProfile); |
| WaitForNewWebView(); |
| EXPECT_TRUE(ProfilePicker::IsOpen()); |
| FirstVisuallyNonEmptyPaintObserver( |
| web_contents(), GURL("chrome://profile-picker/new-profile")) |
| .Wait(); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ProfilePickerCreationFlowBrowserTest, |
| CreateSignedInProfile) { |
| ASSERT_EQ(1u, BrowserList::GetInstance()->size()); |
| |
| ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuAddNewProfile); |
| WaitForNewWebView(); |
| |
| // Simulate a click on the signin button. |
| base::MockCallback<base::OnceClosure> switch_failure_callback; |
| EXPECT_CALL(switch_failure_callback, Run()).Times(0); |
| ProfilePicker::SwitchToSignIn(kProfileColor, switch_failure_callback.Get()); |
| |
| // The DICE navigation happens in a new web view (for the profile being |
| // created), wait for it. |
| WaitForNewWebView(); |
| FirstVisuallyNonEmptyPaintObserver( |
| web_contents(), GaiaUrls::GetInstance()->signin_chrome_sync_dice()) |
| .Wait(); |
| |
| // Add an account - simulate a successful Gaia sign-in. |
| Profile* profile_being_created = |
| static_cast<Profile*>(web_view()->GetBrowserContext()); |
| syncer::SyncService* sync_service = |
| ProfileSyncServiceFactory::GetForProfile(profile_being_created); |
| signin::IdentityManager* identity_manager = |
| IdentityManagerFactory::GetForProfile(profile_being_created); |
| CoreAccountInfo core_account_info = |
| signin::MakeAccountAvailable(identity_manager, "joe.consumer@gmail.com"); |
| ASSERT_TRUE(identity_manager->HasAccountWithRefreshToken( |
| core_account_info.account_id)); |
| |
| AccountInfo account_info = FillAccountInfo(core_account_info, "Joe"); |
| signin::UpdateAccountInfoForAccount(identity_manager, account_info); |
| |
| // Wait for the sign-in to propagate to the flow, resulting in sync |
| // confirmation screen getting displayed. |
| FirstVisuallyNonEmptyPaintObserver(web_contents(), |
| GURL("chrome://sync-confirmation/")) |
| .Wait(); |
| |
| // Simulate closing the UI with "Yes, I'm in". |
| LoginUIServiceFactory::GetForProfile(profile_being_created) |
| ->SyncConfirmationUIClosed(LoginUIService::SYNC_WITH_DEFAULT_SETTINGS); |
| Browser* new_browser = BrowserAddedWaiter(2u).Wait(); |
| FirstVisuallyNonEmptyPaintObserver( |
| new_browser->tab_strip_model()->GetActiveWebContents(), |
| GURL("chrome://newtab/")) |
| .Wait(); |
| |
| // Check expectations when the profile creation flow is done. |
| EXPECT_FALSE(ProfilePicker::IsOpen()); |
| |
| ProfileAttributesEntry* entry = nullptr; |
| ASSERT_TRUE(g_browser_process->profile_manager() |
| ->GetProfileAttributesStorage() |
| .GetProfileAttributesWithPath( |
| profile_being_created->GetPath(), &entry)); |
| EXPECT_FALSE(entry->IsEphemeral()); |
| EXPECT_TRUE(entry->IsAuthenticated()); |
| EXPECT_EQ(entry->GetLocalProfileName(), base::UTF8ToUTF16("Joe")); |
| EXPECT_EQ(ThemeServiceFactory::GetForProfile(profile_being_created) |
| ->GetAutogeneratedThemeColor(), |
| kProfileColor); |
| EXPECT_TRUE(sync_service->GetUserSettings()->IsSyncRequested()); |
| EXPECT_TRUE(sync_service->GetUserSettings()->IsFirstSetupComplete()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ProfilePickerCreationFlowBrowserTest, |
| CreateSignedInProfileWithSyncDisabled) { |
| ASSERT_EQ(1u, BrowserList::GetInstance()->size()); |
| |
| ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuAddNewProfile); |
| WaitForNewWebView(); |
| |
| // Simulate a click on the signin button. |
| base::MockCallback<base::OnceClosure> switch_failure_callback; |
| EXPECT_CALL(switch_failure_callback, Run()).Times(0); |
| ProfilePicker::SwitchToSignIn(kProfileColor, switch_failure_callback.Get()); |
| |
| // The DICE navigation happens in a new web view (for the profile being |
| // created), wait for it. |
| WaitForNewWebView(); |
| FirstVisuallyNonEmptyPaintObserver( |
| web_contents(), GaiaUrls::GetInstance()->signin_chrome_sync_dice()) |
| .Wait(); |
| |
| // Disable sync by setting the device as managed in prefs. |
| Profile* profile_being_created = |
| static_cast<Profile*>(web_view()->GetBrowserContext()); |
| syncer::SyncPrefs prefs(profile_being_created->GetPrefs()); |
| prefs.SetManagedForTest(true); |
| syncer::SyncService* sync_service = |
| ProfileSyncServiceFactory::GetForProfile(profile_being_created); |
| |
| // Add an account - simulate a successful Gaia sign-in. |
| signin::IdentityManager* identity_manager = |
| IdentityManagerFactory::GetForProfile(profile_being_created); |
| CoreAccountInfo core_account_info = |
| signin::MakeAccountAvailable(identity_manager, "joe.consumer@gmail.com"); |
| ASSERT_TRUE(identity_manager->HasAccountWithRefreshToken( |
| core_account_info.account_id)); |
| |
| AccountInfo account_info = FillAccountInfo(core_account_info, "Joe"); |
| signin::UpdateAccountInfoForAccount(identity_manager, account_info); |
| |
| // Wait for the sign-in to propagate to the flow, resulting in new browser |
| // getting opened. |
| Browser* new_browser = BrowserAddedWaiter(2u).Wait(); |
| FirstVisuallyNonEmptyPaintObserver( |
| new_browser->tab_strip_model()->GetActiveWebContents(), |
| GURL("chrome://newtab/")) |
| .Wait(); |
| |
| EXPECT_FALSE(ProfilePicker::IsOpen()); |
| |
| // Now the sync consent screen is shown, simulate closing the UI with "Stay |
| // signed in" |
| LoginUIServiceFactory::GetForProfile(profile_being_created) |
| ->SyncConfirmationUIClosed(LoginUIService::SYNC_WITH_DEFAULT_SETTINGS); |
| |
| // Check expectations when the profile creation flow is done. |
| ProfileAttributesEntry* entry = nullptr; |
| ASSERT_TRUE(g_browser_process->profile_manager() |
| ->GetProfileAttributesStorage() |
| .GetProfileAttributesWithPath( |
| profile_being_created->GetPath(), &entry)); |
| EXPECT_FALSE(entry->IsEphemeral()); |
| EXPECT_EQ(entry->GetLocalProfileName(), base::UTF8ToUTF16("Joe")); |
| EXPECT_EQ(ThemeServiceFactory::GetForProfile(profile_being_created) |
| ->GetAutogeneratedThemeColor(), |
| kProfileColor); |
| EXPECT_FALSE(sync_service->GetUserSettings()->IsSyncRequested()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ProfilePickerCreationFlowBrowserTest, |
| CreateSignedInProfileSettings) { |
| ASSERT_EQ(1u, BrowserList::GetInstance()->size()); |
| |
| ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuAddNewProfile); |
| WaitForNewWebView(); |
| |
| // Simulate a click on the signin button. |
| base::MockCallback<base::OnceClosure> switch_failure_callback; |
| EXPECT_CALL(switch_failure_callback, Run()).Times(0); |
| ProfilePicker::SwitchToSignIn(kProfileColor, switch_failure_callback.Get()); |
| |
| // The DICE navigation happens in a new web view (for the profile being |
| // created), wait for it. |
| WaitForNewWebView(); |
| FirstVisuallyNonEmptyPaintObserver( |
| web_contents(), GaiaUrls::GetInstance()->signin_chrome_sync_dice()) |
| .Wait(); |
| |
| // Add an account - simulate a successful Gaia sign-in. |
| Profile* profile_being_created = |
| static_cast<Profile*>(web_view()->GetBrowserContext()); |
| signin::IdentityManager* identity_manager = |
| IdentityManagerFactory::GetForProfile(profile_being_created); |
| CoreAccountInfo core_account_info = |
| signin::MakeAccountAvailable(identity_manager, "joe.consumer@gmail.com"); |
| ASSERT_TRUE(identity_manager->HasAccountWithRefreshToken( |
| core_account_info.account_id)); |
| |
| AccountInfo account_info = FillAccountInfo(core_account_info, "Joe"); |
| signin::UpdateAccountInfoForAccount(identity_manager, account_info); |
| |
| // Wait for the sign-in to propagate to the flow, resulting in sync |
| // confirmation screen getting displayed. |
| FirstVisuallyNonEmptyPaintObserver(web_contents(), |
| GURL("chrome://sync-confirmation/")) |
| .Wait(); |
| |
| // Simulate closing the UI with "Yes, I'm in". |
| LoginUIServiceFactory::GetForProfile(profile_being_created) |
| ->SyncConfirmationUIClosed(LoginUIService::CONFIGURE_SYNC_FIRST); |
| Browser* new_browser = BrowserAddedWaiter(2u).Wait(); |
| FirstVisuallyNonEmptyPaintObserver( |
| new_browser->tab_strip_model()->GetActiveWebContents(), |
| GURL("chrome://settings/syncSetup")) |
| .Wait(); |
| |
| // Check expectations when the profile creation flow is done. |
| EXPECT_FALSE(ProfilePicker::IsOpen()); |
| |
| ProfileAttributesEntry* entry = nullptr; |
| ASSERT_TRUE(g_browser_process->profile_manager() |
| ->GetProfileAttributesStorage() |
| .GetProfileAttributesWithPath( |
| profile_being_created->GetPath(), &entry)); |
| EXPECT_FALSE(entry->IsEphemeral()); |
| // Sync is technically enabled for the profile. Without SyncService, the |
| // difference between SYNC_WITH_DEFAULT_SETTINGS and CONFIGURE_SYNC_FIRST |
| // cannot be told. |
| EXPECT_TRUE(entry->IsAuthenticated()); |
| EXPECT_EQ(entry->GetLocalProfileName(), base::UTF8ToUTF16("Joe")); |
| EXPECT_EQ(ThemeServiceFactory::GetForProfile(profile_being_created) |
| ->GetAutogeneratedThemeColor(), |
| kProfileColor); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ProfilePickerCreationFlowBrowserTest, |
| CreateSignedInProfileOpenLink) { |
| ASSERT_EQ(1u, BrowserList::GetInstance()->size()); |
| |
| ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuAddNewProfile); |
| WaitForNewWebView(); |
| |
| // Simulate a click on the signin button. |
| base::MockCallback<base::OnceClosure> switch_failure_callback; |
| EXPECT_CALL(switch_failure_callback, Run()).Times(0); |
| ProfilePicker::SwitchToSignIn(kProfileColor, switch_failure_callback.Get()); |
| |
| // The DICE navigation happens in a new web view (for the profile being |
| // created), wait for it. |
| WaitForNewWebView(); |
| FirstVisuallyNonEmptyPaintObserver( |
| web_contents(), GaiaUrls::GetInstance()->signin_chrome_sync_dice()) |
| .Wait(); |
| |
| // Simulate clicking on a link that opens in a new window. |
| const GURL kURL("https://foo.google.com"); |
| EXPECT_TRUE(ExecuteScript(web_contents(), |
| "var link = document.createElement('a');" |
| "link.href = '" + |
| kURL.spec() + |
| "';" |
| "link.target = '_blank';" |
| "document.body.appendChild(link);" |
| "link.click();")); |
| // A new pppup browser is displayed (with the specified URL). |
| Browser* new_browser = BrowserAddedWaiter(2u).Wait(); |
| EXPECT_EQ(new_browser->type(), Browser::TYPE_POPUP); |
| FirstVisuallyNonEmptyPaintObserver( |
| new_browser->tab_strip_model()->GetActiveWebContents(), kURL) |
| .Wait(); |
| } |
| |
| // TODO(crbug.com/1144065): Flaky on multiple platforms. |
| IN_PROC_BROWSER_TEST_F(ProfilePickerCreationFlowBrowserTest, |
| DISABLED_CreateSignedInProfileSigninAlreadyExists) { |
| ASSERT_EQ(1u, BrowserList::GetInstance()->size()); |
| |
| // Create a pre-existing profile syncing with the same account as the profile |
| // being created. |
| ProfileManager* profile_manager = g_browser_process->profile_manager(); |
| base::FilePath new_path = profile_manager->GenerateNextProfileDirectoryPath(); |
| base::RunLoop run_loop; |
| profile_manager->CreateProfileAsync( |
| new_path, |
| base::BindLambdaForTesting( |
| [&run_loop](Profile* profile, Profile::CreateStatus status) { |
| if (status == Profile::CREATE_STATUS_INITIALIZED) { |
| run_loop.Quit(); |
| } |
| }), |
| base::string16(), std::string()); |
| run_loop.Run(); |
| ProfileAttributesEntry* other_entry = nullptr; |
| ProfileAttributesStorage& storage = |
| profile_manager->GetProfileAttributesStorage(); |
| ASSERT_TRUE(storage.GetProfileAttributesWithPath(new_path, &other_entry)); |
| // Fake sync is enabled in this profile with Joe's account. |
| other_entry->SetAuthInfo(std::string(), |
| base::UTF8ToUTF16("joe.consumer@gmail.com"), |
| /*is_consented_primary_account=*/true); |
| |
| ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuAddNewProfile); |
| WaitForNewWebView(); |
| |
| // Simulate a click on the signin button. |
| base::MockCallback<base::OnceClosure> switch_failure_callback; |
| EXPECT_CALL(switch_failure_callback, Run()).Times(0); |
| ProfilePicker::SwitchToSignIn(kProfileColor, switch_failure_callback.Get()); |
| |
| // The DICE navigation happens in a new web view (for the profile being |
| // created), wait for it. |
| WaitForNewWebView(); |
| FirstVisuallyNonEmptyPaintObserver( |
| web_contents(), GaiaUrls::GetInstance()->signin_chrome_sync_dice()) |
| .Wait(); |
| |
| // Add an account - simulate a successful Gaia sign-in. |
| Profile* profile_being_created = |
| static_cast<Profile*>(web_view()->GetBrowserContext()); |
| signin::IdentityManager* identity_manager = |
| IdentityManagerFactory::GetForProfile(profile_being_created); |
| CoreAccountInfo core_account_info = |
| signin::MakeAccountAvailable(identity_manager, "joe.consumer@gmail.com"); |
| ASSERT_TRUE(identity_manager->HasAccountWithRefreshToken( |
| core_account_info.account_id)); |
| |
| AccountInfo account_info = FillAccountInfo(core_account_info, "Joe"); |
| signin::UpdateAccountInfoForAccount(identity_manager, account_info); |
| |
| // Instead of sync confirmation, a browser is displayed (with a login error). |
| Browser* new_browser = BrowserAddedWaiter(2u).Wait(); |
| FirstVisuallyNonEmptyPaintObserver( |
| new_browser->tab_strip_model()->GetActiveWebContents(), |
| GURL("chrome://newtab/")) |
| .Wait(); |
| |
| // Check expectations when the profile creation flow is done. |
| EXPECT_FALSE(ProfilePicker::IsOpen()); |
| |
| ProfileAttributesEntry* entry = nullptr; |
| ASSERT_TRUE(storage.GetProfileAttributesWithPath( |
| profile_being_created->GetPath(), &entry)); |
| EXPECT_FALSE(entry->IsEphemeral()); |
| EXPECT_FALSE(entry->IsAuthenticated()); |
| EXPECT_EQ(entry->GetLocalProfileName(), base::UTF8ToUTF16("Joe")); |
| EXPECT_EQ(ThemeServiceFactory::GetForProfile(profile_being_created) |
| ->GetAutogeneratedThemeColor(), |
| kProfileColor); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ProfilePickerCreationFlowBrowserTest, |
| CreateSignedInProfileExtendedInfoTimeout) { |
| ASSERT_EQ(1u, BrowserList::GetInstance()->size()); |
| |
| ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuAddNewProfile); |
| WaitForNewWebView(); |
| |
| // Simulate a click on the signin button. |
| base::MockCallback<base::OnceClosure> switch_failure_callback; |
| EXPECT_CALL(switch_failure_callback, Run()).Times(0); |
| ProfilePicker::SwitchToSignIn(kProfileColor, switch_failure_callback.Get()); |
| |
| // The DICE navigation happens in a new web view (for the profile being |
| // created), wait for it. |
| WaitForNewWebView(); |
| FirstVisuallyNonEmptyPaintObserver( |
| web_contents(), GaiaUrls::GetInstance()->signin_chrome_sync_dice()) |
| .Wait(); |
| |
| Profile* profile_being_created = |
| static_cast<Profile*>(web_view()->GetBrowserContext()); |
| signin::IdentityManager* identity_manager = |
| IdentityManagerFactory::GetForProfile(profile_being_created); |
| |
| // Make it work without waiting for a long delay. |
| ProfilePicker::SetExtendedAccountInfoTimeoutForTesting( |
| base::TimeDelta::FromMilliseconds(10)); |
| |
| // Add an account - simulate a successful Gaia sign-in. |
| CoreAccountInfo core_account_info = |
| signin::MakeAccountAvailable(identity_manager, "joe.consumer@gmail.com"); |
| ASSERT_TRUE(identity_manager->HasAccountWithRefreshToken( |
| core_account_info.account_id)); |
| |
| // Wait for the sign-in to propagate to the flow, resulting in sync |
| // confirmation screen getting displayed. |
| FirstVisuallyNonEmptyPaintObserver(web_contents(), |
| GURL("chrome://sync-confirmation/")) |
| .Wait(); |
| |
| // Simulate closing the UI with "Yes, I'm in". |
| LoginUIServiceFactory::GetForProfile(profile_being_created) |
| ->SyncConfirmationUIClosed(LoginUIService::SYNC_WITH_DEFAULT_SETTINGS); |
| Browser* new_browser = BrowserAddedWaiter(2u).Wait(); |
| FirstVisuallyNonEmptyPaintObserver( |
| new_browser->tab_strip_model()->GetActiveWebContents(), |
| GURL("chrome://newtab/")) |
| .Wait(); |
| |
| // Check expectations when the profile creation flow is done. |
| EXPECT_FALSE(ProfilePicker::IsOpen()); |
| |
| ProfileAttributesEntry* entry = nullptr; |
| ASSERT_TRUE(g_browser_process->profile_manager() |
| ->GetProfileAttributesStorage() |
| .GetProfileAttributesWithPath( |
| profile_being_created->GetPath(), &entry)); |
| EXPECT_FALSE(entry->IsEphemeral()); |
| EXPECT_TRUE(entry->IsAuthenticated()); |
| // Since the given name is not provided, the email address is used instead as |
| // a profile name. |
| EXPECT_EQ(entry->GetLocalProfileName(), |
| base::UTF8ToUTF16("joe.consumer@gmail.com")); |
| EXPECT_EQ(ThemeServiceFactory::GetForProfile(profile_being_created) |
| ->GetAutogeneratedThemeColor(), |
| kProfileColor); |
| } |
| |
| class ProfilePickerEnterpriseCreationFlowBrowserTest |
| : public ProfilePickerCreationFlowBrowserTest { |
| public: |
| ProfilePickerEnterpriseCreationFlowBrowserTest() = default; |
| |
| void OnWillCreateBrowserContextServices( |
| content::BrowserContext* context) override { |
| policy::UserPolicySigninServiceFactory::GetInstance()->SetTestingFactory( |
| context, |
| base::BindRepeating(&FakeUserPolicySigninService::BuildForEnterprise)); |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(ProfilePickerEnterpriseCreationFlowBrowserTest, |
| CreateSignedInProfile) { |
| ASSERT_EQ(1u, BrowserList::GetInstance()->size()); |
| |
| ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuAddNewProfile); |
| WaitForNewWebView(); |
| |
| // Simulate a click on the signin button. |
| base::MockCallback<base::OnceClosure> switch_failure_callback; |
| EXPECT_CALL(switch_failure_callback, Run()).Times(0); |
| ProfilePicker::SwitchToSignIn(kProfileColor, switch_failure_callback.Get()); |
| |
| // The DICE navigation happens in a new web view (for the profile being |
| // created), wait for it. |
| WaitForNewWebView(); |
| FirstVisuallyNonEmptyPaintObserver( |
| web_contents(), GaiaUrls::GetInstance()->signin_chrome_sync_dice()) |
| .Wait(); |
| |
| // Add an account - simulate a successful Gaia sign-in. |
| Profile* profile_being_created = |
| static_cast<Profile*>(web_view()->GetBrowserContext()); |
| signin::IdentityManager* identity_manager = |
| IdentityManagerFactory::GetForProfile(profile_being_created); |
| // Consumer-looking gmail address avoids code that forces the sync service to |
| // actually start which would add overhead in mocking further stuff. |
| CoreAccountInfo core_account_info = signin::MakeAccountAvailable( |
| identity_manager, "joe.enterprise@gmail.com"); |
| ASSERT_TRUE(identity_manager->HasAccountWithRefreshToken( |
| core_account_info.account_id)); |
| |
| // Enterprise domain needed for this profile being detected as Work. |
| AccountInfo account_info = |
| FillAccountInfo(core_account_info, "Joe", "enterprise.com"); |
| signin::UpdateAccountInfoForAccount(identity_manager, account_info); |
| |
| // Wait for the sign-in to propagate to the flow, resulting in new browser |
| // getting opened. |
| Browser* new_browser = BrowserAddedWaiter(2u).Wait(); |
| // Inject a fake tab helper that confirms the enterprise dialog right away. |
| base::RunLoop loop_until_sync_confirmed; |
| TestTabDialogs::OverwriteForWebContents( |
| new_browser->tab_strip_model()->GetActiveWebContents(), |
| &loop_until_sync_confirmed); |
| // Wait until the final sync confirmation screen in shown and confirmed. |
| loop_until_sync_confirmed.Run(); |
| |
| // The picker should be closed even before the enterprise confirmation but it |
| // is closed asynchronously after opening the browser so after the NTP |
| // renders, it is safe to check. |
| FirstVisuallyNonEmptyPaintObserver( |
| new_browser->tab_strip_model()->GetActiveWebContents(), |
| GURL("chrome://newtab/")) |
| .Wait(); |
| EXPECT_FALSE(ProfilePicker::IsOpen()); |
| |
| // Now the sync consent screen is shown, simulate closing the UI with "Yes, |
| // I'm in". |
| LoginUIServiceFactory::GetForProfile(profile_being_created) |
| ->SyncConfirmationUIClosed(LoginUIService::SYNC_WITH_DEFAULT_SETTINGS); |
| |
| // Check expectations when the profile creation flow is done. |
| ProfileAttributesEntry* entry = nullptr; |
| ASSERT_TRUE(g_browser_process->profile_manager() |
| ->GetProfileAttributesStorage() |
| .GetProfileAttributesWithPath( |
| profile_being_created->GetPath(), &entry)); |
| EXPECT_FALSE(entry->IsEphemeral()); |
| EXPECT_EQ(entry->GetLocalProfileName(), base::UTF8ToUTF16(kWork)); |
| EXPECT_EQ(ThemeServiceFactory::GetForProfile(profile_being_created) |
| ->GetAutogeneratedThemeColor(), |
| kProfileColor); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ProfilePickerEnterpriseCreationFlowBrowserTest, |
| CreateSignedInProfileSettings) { |
| ASSERT_EQ(1u, BrowserList::GetInstance()->size()); |
| |
| ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuAddNewProfile); |
| WaitForNewWebView(); |
| |
| // Simulate a click on the signin button. |
| base::MockCallback<base::OnceClosure> switch_failure_callback; |
| EXPECT_CALL(switch_failure_callback, Run()).Times(0); |
| ProfilePicker::SwitchToSignIn(kProfileColor, switch_failure_callback.Get()); |
| |
| // The DICE navigation happens in a new web view (for the profile being |
| // created), wait for it. |
| WaitForNewWebView(); |
| FirstVisuallyNonEmptyPaintObserver( |
| web_contents(), GaiaUrls::GetInstance()->signin_chrome_sync_dice()) |
| .Wait(); |
| |
| // Add an account - simulate a successful Gaia sign-in. |
| Profile* profile_being_created = |
| static_cast<Profile*>(web_view()->GetBrowserContext()); |
| signin::IdentityManager* identity_manager = |
| IdentityManagerFactory::GetForProfile(profile_being_created); |
| // Consumer-looking gmail address avoids code that forces the sync service to |
| // actually start which would add overhead in mocking further stuff. |
| CoreAccountInfo core_account_info = signin::MakeAccountAvailable( |
| identity_manager, "joe.enterprise@gmail.com"); |
| ASSERT_TRUE(identity_manager->HasAccountWithRefreshToken( |
| core_account_info.account_id)); |
| |
| // Enterprise domain needed for this profile being detected as Work. |
| AccountInfo account_info = |
| FillAccountInfo(core_account_info, "Joe", "enterprise.com"); |
| signin::UpdateAccountInfoForAccount(identity_manager, account_info); |
| |
| // Wait for the sign-in to propagate to the flow, resulting in new browser |
| // getting opened. |
| Browser* new_browser = BrowserAddedWaiter(2u).Wait(); |
| // Inject a fake tab helper that confirms the enterprise dialog right away. |
| base::RunLoop loop_until_sync_confirmed; |
| TestTabDialogs::OverwriteForWebContents( |
| new_browser->tab_strip_model()->GetActiveWebContents(), |
| &loop_until_sync_confirmed); |
| // Wait until the final sync confirmation screen in shown and confirmed. |
| loop_until_sync_confirmed.Run(); |
| // Now the sync consent screen is shown, simulate closing the UI with |
| // "Configure sync". |
| LoginUIServiceFactory::GetForProfile(profile_being_created) |
| ->SyncConfirmationUIClosed(LoginUIService::CONFIGURE_SYNC_FIRST); |
| FirstVisuallyNonEmptyPaintObserver( |
| new_browser->tab_strip_model()->GetActiveWebContents(), |
| GURL("chrome://settings/syncSetup")) |
| .Wait(); |
| |
| // Check expectations when the profile creation flow is done. |
| EXPECT_FALSE(ProfilePicker::IsOpen()); |
| |
| ProfileAttributesEntry* entry = nullptr; |
| ASSERT_TRUE(g_browser_process->profile_manager() |
| ->GetProfileAttributesStorage() |
| .GetProfileAttributesWithPath( |
| profile_being_created->GetPath(), &entry)); |
| EXPECT_FALSE(entry->IsEphemeral()); |
| EXPECT_EQ(entry->GetLocalProfileName(), base::UTF8ToUTF16(kWork)); |
| EXPECT_EQ(ThemeServiceFactory::GetForProfile(profile_being_created) |
| ->GetAutogeneratedThemeColor(), |
| kProfileColor); |
| } |
| |
| } // namespace |