blob: 27998ab6b639ece6dc97a776efa405a1a29ef840 [file] [log] [blame]
// Copyright 2022 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/ui/views/profiles/profile_management_step_controller.h"
#include "base/functional/bind.h"
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/delete_profile_helper.h"
#include "chrome/browser/profiles/keep_alive/profile_keep_alive_types.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/profiles/profile_metrics.h"
#include "chrome/browser/profiles/profile_window.h"
#include "chrome/browser/profiles/profiles_state.h"
#include "chrome/browser/search_engine_choice/search_engine_choice_dialog_service.h"
#include "chrome/browser/search_engine_choice/search_engine_choice_dialog_service_factory.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/profiles/profile_customization_util.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/views/profiles/profile_management_types.h"
#include "chrome/browser/ui/views/profiles/profile_picker_post_sign_in_adapter.h"
#include "chrome/browser/ui/views/profiles/profile_picker_sign_in_provider.h"
#include "chrome/browser/ui/views/profiles/profile_picker_web_contents_host.h"
#include "chrome/browser/ui/webui/search_engine_choice/search_engine_choice_ui.h"
#include "chrome/browser/ui/webui/signin/signin_ui_error.h"
#include "components/search_engines/search_engine_choice/search_engine_choice_utils.h"
#include "google_apis/gaia/core_account_id.h"
namespace {
class ProfilePickerAppStepController : public ProfileManagementStepController {
public:
explicit ProfilePickerAppStepController(ProfilePickerWebContentsHost* host,
const GURL& initial_url)
: ProfileManagementStepController(host), initial_url_(initial_url) {}
~ProfilePickerAppStepController() override = default;
void Show(StepSwitchFinishedCallback step_shown_callback,
bool reset_state) override {
base::OnceClosure step_shown_success =
base::BindOnce(std::move(step_shown_callback.value()), true);
if (!loaded_ui_in_picker_contents_) {
loaded_ui_in_picker_contents_ = true;
host()->ShowScreenInPickerContents(initial_url_,
std::move(step_shown_success));
return;
}
if (reset_state) {
// Don't do a full reset, just go back to the beginning of the history:
host()->GetPickerContents()->GetController().GoToIndex(0);
}
host()->ShowScreenInPickerContents(GURL(), std::move(step_shown_success));
}
void OnNavigateBackRequested() override {
NavigateBackInternal(host()->GetPickerContents());
}
private:
// We want to load the WebUI page exactly once, and do more lightweight
// transitions if we need to switch back to this step later. So we track
// whether the UI has been loaded or not.
// Note that this relies on other steps not clearing the picker contents and
// using their own instead.
bool loaded_ui_in_picker_contents_ = false;
const GURL initial_url_;
};
class SignInStepController : public ProfileManagementStepController {
public:
explicit SignInStepController(
ProfilePickerWebContentsHost* host,
std::unique_ptr<ProfilePickerSignInProvider> sign_in_provider,
SignInStepFinishedCallback signed_in_callback,
SigninErrorCallback signin_error_callback)
: ProfileManagementStepController(host),
signed_in_callback_(std::move(signed_in_callback)),
signin_error_callback_(std::move(signin_error_callback)),
sign_in_provider_(std::move(sign_in_provider)) {
CHECK(signed_in_callback_);
CHECK(signin_error_callback_);
}
~SignInStepController() override = default;
void Show(StepSwitchFinishedCallback step_shown_callback,
bool reset_state) override {
CHECK(!step_shown_callback->is_null());
DCHECK(signed_in_callback_) << "Attempting to show the sign-in step again "
"while it was previously completed";
// Unretained ok because the provider is owned by `this`.
sign_in_provider_->SwitchToSignIn(
std::move(step_shown_callback),
base::BindOnce(&SignInStepController::OnStepFinished,
base::Unretained(this)));
}
void OnHidden() override {
host()->SetNativeToolbarVisible(false);
// We don't reset the provider when we navigate back as we want to keep this
// page and the ephemeral profile around for performance reasons.
// The caller should delete the step if clearing the provider is needed.
}
bool CanPopStep() const override {
return ProfileManagementStepController::CanPopStep() && sign_in_provider_ &&
sign_in_provider_->IsInitialized();
}
void OnReloadRequested() override {
// Sign-in may fail due to connectivity issues, allow reloading.
if (sign_in_provider_) {
sign_in_provider_->ReloadSignInPage();
}
}
void OnNavigateBackRequested() override {
if (sign_in_provider_) {
NavigateBackInternal(sign_in_provider_->contents());
}
}
private:
void OnStepFinished(Profile* profile,
const CoreAccountInfo& account_info,
std::unique_ptr<content::WebContents> contents,
const SigninUIError& error) {
if (!error.IsOk()) {
std::move(signin_error_callback_).Run(profile, contents.get(), error);
return;
}
std::move(signed_in_callback_)
.Run(profile, account_info, std::move(contents),
StepSwitchFinishedCallback());
// The step controller can be destroyed when `signed_in_callback_`
// or `signin_error_callback_` runs. Don't interact with members below.
}
SignInStepFinishedCallback signed_in_callback_;
SigninErrorCallback signin_error_callback_;
std::unique_ptr<ProfilePickerSignInProvider> sign_in_provider_;
};
class FinishSamlSignInStepController : public ProfileManagementStepController {
public:
explicit FinishSamlSignInStepController(
ProfilePickerWebContentsHost* host,
Profile* profile,
std::unique_ptr<content::WebContents> contents,
base::OnceCallback<void(PostHostClearedCallback)>
finish_picker_section_callback)
: ProfileManagementStepController(host),
profile_(profile),
contents_(std::move(contents)),
finish_picker_section_callback_(
std::move(finish_picker_section_callback)) {
DCHECK(finish_picker_section_callback_);
profile_keep_alive_ = std::make_unique<ScopedProfileKeepAlive>(
profile_, ProfileKeepAliveOrigin::kProfileCreationSamlFlow);
}
~FinishSamlSignInStepController() override {
if (finish_picker_section_callback_) {
finish_picker_section_callback_.Reset();
}
}
void Show(StepSwitchFinishedCallback step_shown_callback,
bool reset_state) override {
// First, stop showing `contents_` to free it up so it can be moved to a new
// browser window.
host()->ShowScreenInPickerContents(
GURL(url::kAboutBlankURL),
/*navigation_finished_closure=*/
base::BindOnce(&FinishSamlSignInStepController::OnSignInContentsFreedUp,
weak_ptr_factory_.GetWeakPtr(),
std::move(step_shown_callback)));
}
void OnNavigateBackRequested() override {
// Not supported here
}
private:
// Note: This will be executed after the profile management view closes, so
// the step instance will already be deleted.
static void ContinueSAMLSignin(std::unique_ptr<content::WebContents> contents,
Browser* browser) {
DCHECK(browser);
// Make a new tab with the desired contents and close the old tab.
browser->tab_strip_model()->AppendWebContents(std::move(contents),
/*foreground=*/true);
browser->tab_strip_model()->DetachAndDeleteWebContentsAt(/*index=*/0);
ProfileMetrics::LogProfileAddSignInFlowOutcome(
ProfileMetrics::ProfileSignedInFlowOutcome::kSAML);
}
void OnSignInContentsFreedUp(StepSwitchFinishedCallback step_shown_callback) {
DCHECK(finish_picker_section_callback_);
CHECK(!step_shown_callback->is_null());
std::move(step_shown_callback.value()).Run(true);
ProfileMetrics::LogProfileAddNewUser(
ProfileMetrics::ADD_NEW_PROFILE_PICKER_SIGNED_IN);
// Second, ensure the profile creation and set up is complete.
FinalizeNewProfileSetup(profile_,
profiles::GetDefaultNameForNewEnterpriseProfile(),
/*is_default_name=*/false);
// Finally, instruct the flow terminate in the picker and continue in a full
// browser window.
auto continue_callback = PostHostClearedCallback(
base::BindOnce(&FinishSamlSignInStepController::ContinueSAMLSignin,
std::move(contents_)));
std::move(finish_picker_section_callback_)
.Run(std::move(continue_callback));
}
std::unique_ptr<ScopedProfileKeepAlive> profile_keep_alive_;
raw_ptr<Profile> profile_;
std::unique_ptr<content::WebContents> contents_;
// Callback to be executed to close the flow host, when it is ready to
// continue the SAML sign-in in the full browser.
base::OnceCallback<void(PostHostClearedCallback)>
finish_picker_section_callback_;
base::WeakPtrFactory<FinishSamlSignInStepController> weak_ptr_factory_{this};
};
class PostSignInStepController : public ProfileManagementStepController {
public:
explicit PostSignInStepController(
ProfilePickerWebContentsHost* host,
std::unique_ptr<ProfilePickerPostSignInAdapter> signed_in_flow)
: ProfileManagementStepController(host),
signed_in_flow_(std::move(signed_in_flow)) {}
~PostSignInStepController() override = default;
void Show(StepSwitchFinishedCallback step_shown_callback,
bool reset_state) override {
signed_in_flow_->Init(std::move(step_shown_callback));
}
void OnHidden() override { signed_in_flow_->Cancel(); }
void OnNavigateBackRequested() override {
// Do nothing, navigating back is not allowed.
}
private:
std::unique_ptr<ProfilePickerPostSignInAdapter> signed_in_flow_;
base::WeakPtrFactory<PostSignInStepController> weak_ptr_factory_{this};
};
class FinishFlowAndRunInBrowserStepController
: public ProfileManagementStepController {
public:
FinishFlowAndRunInBrowserStepController(
ProfilePickerWebContentsHost* host,
base::OnceClosure finish_flow_and_run_in_browser_callback)
: ProfileManagementStepController(host),
finish_flow_and_run_in_browser_callback_(
std::move(finish_flow_and_run_in_browser_callback)) {
CHECK(finish_flow_and_run_in_browser_callback_);
}
void Show(StepSwitchFinishedCallback step_shown_callback,
bool reset_state) override {
CHECK(reset_state);
CHECK(!step_shown_callback->is_null());
std::move(step_shown_callback.value()).Run(true);
std::move(finish_flow_and_run_in_browser_callback_).Run();
}
void OnNavigateBackRequested() override {
// Do nothing, navigating back is not allowed.
NOTREACHED();
}
private:
base::OnceClosure finish_flow_and_run_in_browser_callback_;
};
class SearchEngineChoiceStepController
: public ProfileManagementStepController {
public:
SearchEngineChoiceStepController(
ProfilePickerWebContentsHost* host,
SearchEngineChoiceDialogService* search_engine_choice_dialog_service,
content::WebContents* web_contents,
SearchEngineChoiceDialogService::EntryPoint entry_point,
base::OnceClosure step_completed_callback)
: ProfileManagementStepController(host),
entry_point_(entry_point),
search_engine_choice_dialog_service_(
search_engine_choice_dialog_service),
step_completed_callback_(std::move(step_completed_callback)),
web_contents_(web_contents) {
CHECK(web_contents_);
}
void Show(StepSwitchFinishedCallback step_shown_callback,
bool reset_state) override {
CHECK(reset_state);
CHECK(!step_shown_callback->is_null());
if (!search_engine_choice_dialog_service_) {
// Mark that this step was skipped and proceed with the next one.
std::move(step_shown_callback.value()).Run(false);
std::move(step_completed_callback_).Run();
return;
}
base::OnceClosure navigation_finished_closure =
base::BindOnce(&SearchEngineChoiceStepController::OnLoadFinished,
base::Unretained(this));
// Notify the caller first.
navigation_finished_closure =
base::BindOnce(std::move(step_shown_callback.value()), true)
.Then(std::move(navigation_finished_closure));
search_engines::SearchEngineChoiceScreenEvents choice_screen_event =
search_engines::SearchEngineChoiceScreenEvents::
kProfileCreationChoiceScreenWasDisplayed;
if (entry_point_ ==
SearchEngineChoiceDialogService::EntryPoint::kFirstRunExperience) {
choice_screen_event = search_engines::SearchEngineChoiceScreenEvents::
kFreChoiceScreenWasDisplayed;
}
search_engine_choice_dialog_service_->RecordChoiceScreenEvent(
choice_screen_event);
host()->ShowScreen(web_contents_,
GURL(chrome::kChromeUISearchEngineChoiceURL),
std::move(navigation_finished_closure));
}
void OnNavigateBackRequested() override {
// Do nothing, navigating back is not allowed.
NOTREACHED();
}
private:
void OnLoadFinished() {
auto* search_engine_choice_ui = web_contents_->GetWebUI()
->GetController()
->GetAs<SearchEngineChoiceUI>();
CHECK(search_engine_choice_ui);
CHECK(step_completed_callback_);
search_engine_choice_ui->Initialize(
/*display_dialog_callback=*/base::OnceClosure(),
/*on_choice_made_callback=*/
std::move(step_completed_callback_), entry_point_);
}
// The entry point from which the search engine choice screen is displayed.
// This could be either the FRE or Profile Creation in this case.
SearchEngineChoiceDialogService::EntryPoint entry_point_;
// Can be nullptr.
raw_ptr<SearchEngineChoiceDialogService> search_engine_choice_dialog_service_;
// Callback to be executed when the step is completed.
base::OnceClosure step_completed_callback_;
// The web contents in which we want to display the screen.
raw_ptr<content::WebContents> web_contents_;
};
} // namespace
// static
std::unique_ptr<ProfileManagementStepController>
ProfileManagementStepController::CreateForProfilePickerApp(
ProfilePickerWebContentsHost* host,
const GURL& initial_url) {
return std::make_unique<ProfilePickerAppStepController>(host, initial_url);
}
// static
std::unique_ptr<ProfileManagementStepController>
ProfileManagementStepController::CreateForSignIn(
ProfilePickerWebContentsHost* host,
std::unique_ptr<ProfilePickerSignInProvider> sign_in_provider,
SignInStepFinishedCallback signed_in_callback,
SigninErrorCallback signin_error_callback) {
return std::make_unique<SignInStepController>(
host, std::move(sign_in_provider), std::move(signed_in_callback),
std::move(signin_error_callback));
}
// static
std::unique_ptr<ProfileManagementStepController>
ProfileManagementStepController::CreateForFinishSamlSignIn(
ProfilePickerWebContentsHost* host,
Profile* profile,
std::unique_ptr<content::WebContents> contents,
base::OnceCallback<void(PostHostClearedCallback)>
finish_picker_section_callback) {
return std::make_unique<FinishSamlSignInStepController>(
host, profile, std::move(contents),
std::move(finish_picker_section_callback));
}
// static
std::unique_ptr<ProfileManagementStepController>
ProfileManagementStepController::CreateForPostSignInFlow(
ProfilePickerWebContentsHost* host,
std::unique_ptr<ProfilePickerPostSignInAdapter> signed_in_flow) {
return std::make_unique<PostSignInStepController>(host,
std::move(signed_in_flow));
}
// static
std::unique_ptr<ProfileManagementStepController>
ProfileManagementStepController::CreateForSearchEngineChoice(
ProfilePickerWebContentsHost* host,
SearchEngineChoiceDialogService* search_engine_choice_dialog_service,
content::WebContents* web_contents,
SearchEngineChoiceDialogService::EntryPoint entry_point,
base::OnceClosure callback) {
return std::make_unique<SearchEngineChoiceStepController>(
host, search_engine_choice_dialog_service, web_contents, entry_point,
std::move(callback));
}
// static
std::unique_ptr<ProfileManagementStepController>
ProfileManagementStepController::CreateForFinishFlowAndRunInBrowser(
ProfilePickerWebContentsHost* host,
base::OnceClosure finish_flow_and_run_in_browser_callback) {
return std::make_unique<FinishFlowAndRunInBrowserStepController>(
host, std::move(finish_flow_and_run_in_browser_callback));
}
ProfileManagementStepController::ProfileManagementStepController(
ProfilePickerWebContentsHost* host)
: host_(host) {}
ProfileManagementStepController::~ProfileManagementStepController() = default;
void ProfileManagementStepController::OnReloadRequested() {}
void ProfileManagementStepController::NavigateBackInternal(
content::WebContents* contents) {
if (contents && contents->GetController().CanGoBack()) {
contents->GetController().GoBack();
return;
}
if (CanPopStep()) {
DCHECK(pop_step_callback_);
std::move(pop_step_callback_).Run();
}
}
bool ProfileManagementStepController::CanPopStep() const {
return !pop_step_callback_.is_null();
}