blob: 8fb73704be6537648f65f62307f07b7a3d3dc51d [file] [log] [blame]
// 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/bind.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/containers/contains.h"
#include "base/files/file_path.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/trace_event/trace_event.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/lifetime/application_lifetime.h"
#include "chrome/browser/profiles/profile_attributes_storage.h"
#include "chrome/browser/profiles/profile_avatar_icon_util.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/signin/signin_promo.h"
#include "chrome/browser/signin/signin_util.h"
#include "chrome/browser/ui/browser_navigator.h"
#include "chrome/browser/ui/browser_navigator_params.h"
#include "chrome/browser/ui/layout_constants.h"
#include "chrome/browser/ui/views/accelerator_table.h"
#include "chrome/browser/ui/views/profiles/profile_picker_signed_in_flow_controller.h"
#include "chrome/browser/ui/webui/signin/profile_picker_ui.h"
#include "chrome/browser/ui/webui/signin/signin_web_dialog_ui.h"
#include "chrome/browser/ui/webui/signin/sync_confirmation_ui.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/grit/chromium_strings.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/grit/google_chrome_strings.h"
#include "components/keep_alive_registry/keep_alive_types.h"
#include "components/prefs/pref_service.h"
#include "components/signin/public/base/signin_metrics.h"
#include "components/startup_metric_utils/browser/startup_metric_utils.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/context_menu_params.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
#include "net/base/url_util.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/theme_provider.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/controls/webview/webview.h"
#include "ui/views/layout/fill_layout.h"
#include "ui/views/layout/flex_layout.h"
#include "ui/views/view.h"
#include "ui/views/view_class_properties.h"
#include "ui/views/widget/widget.h"
#include "url/gurl.h"
#include "url/url_constants.h"
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
#include "chrome/browser/ui/views/profiles/profile_picker_dice_sign_in_provider.h"
#include "chrome/browser/ui/views/profiles/profile_picker_dice_sign_in_toolbar.h"
#endif
#if BUILDFLAG(IS_WIN)
#include "chrome/browser/shell_integration_win.h"
#include "ui/base/win/shell.h"
#include "ui/views/win/hwnd_util.h"
#endif
#if BUILDFLAG(IS_MAC)
#include "chrome/browser/global_keyboard_shortcuts_mac.h"
#endif
namespace {
ProfilePickerView* g_profile_picker_view = nullptr;
base::OnceClosure* g_profile_picker_opened_callback_for_testing = nullptr;
constexpr int kWindowWidth = 1024;
constexpr int kWindowHeight = 758;
constexpr float kMaxRatioOfWorkArea = 0.9;
constexpr base::TimeDelta kExtendedAccountInfoTimeout = base::Seconds(10);
constexpr int kSupportedAcceleratorCommands[] = {
IDC_CLOSE_TAB, IDC_CLOSE_WINDOW, IDC_EXIT,
IDC_FULLSCREEN, IDC_MINIMIZE_WINDOW, IDC_BACK,
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
IDC_RELOAD
#endif
};
GURL CreateURLForEntryPoint(ProfilePicker::EntryPoint entry_point) {
GURL base_url = GURL(chrome::kChromeUIProfilePickerUrl);
switch (entry_point) {
case ProfilePicker::EntryPoint::kOnStartup: {
GURL::Replacements replacements;
replacements.SetQueryStr(chrome::kChromeUIProfilePickerStartupQuery);
return base_url.ReplaceComponents(replacements);
}
case ProfilePicker::EntryPoint::kProfileMenuManageProfiles:
case ProfilePicker::EntryPoint::kOpenNewWindowAfterProfileDeletion:
case ProfilePicker::EntryPoint::kNewSessionOnExistingProcess:
case ProfilePicker::EntryPoint::kProfileLocked:
case ProfilePicker::EntryPoint::kUnableToCreateBrowser:
case ProfilePicker::EntryPoint::kBackgroundModeManager:
return base_url;
case ProfilePicker::EntryPoint::kProfileMenuAddNewProfile:
return base_url.Resolve("new-profile");
case ProfilePicker::EntryPoint::kLacrosSelectAvailableAccount:
return base_url.Resolve("account-selection-lacros");
}
}
class ProfilePickerWidget : public views::Widget {
public:
explicit ProfilePickerWidget(ProfilePickerView* profile_picker_view)
: profile_picker_view_(profile_picker_view) {
views::Widget::InitParams params;
params.delegate = profile_picker_view_;
Init(std::move(params));
}
~ProfilePickerWidget() override = default;
// views::Widget:
const ui::ThemeProvider* GetThemeProvider() const override {
return profile_picker_view_->GetThemeProviderForProfileBeingCreated();
}
ui::ColorProviderManager::InitializerSupplier* GetCustomTheme()
const override {
return profile_picker_view_->GetCustomThemeForProfileBeingCreated();
}
private:
const raw_ptr<ProfilePickerView> profile_picker_view_;
};
} // namespace
// static
void ProfilePicker::Show(EntryPoint entry_point,
const GURL& on_select_profile_target_url,
const base::FilePath& custom_profile_path) {
// Re-open with new params if necessary.
if (g_profile_picker_view &&
g_profile_picker_view->ShouldReopen(
entry_point, on_select_profile_target_url, custom_profile_path)) {
return;
}
if (!g_profile_picker_view) {
// TODO(crbug.com/1226076): Enforce this on the level of API (putting
// `custom_profile_path` inside a struct that checks this).
DCHECK(custom_profile_path.empty() ||
entry_point == EntryPoint::kLacrosSelectAvailableAccount);
g_profile_picker_view = new ProfilePickerView(custom_profile_path);
}
g_profile_picker_view->set_on_select_profile_target_url(
on_select_profile_target_url);
#if !BUILDFLAG(IS_CHROMEOS_LACROS)
DCHECK_NE(entry_point, EntryPoint::kLacrosSelectAvailableAccount);
#endif
g_profile_picker_view->Display(entry_point);
}
// static
GURL ProfilePicker::GetOnSelectProfileTargetUrl() {
if (g_profile_picker_view) {
return g_profile_picker_view->GetOnSelectProfileTargetUrl();
}
return GURL();
}
// static
base::FilePath ProfilePicker::GetSwitchProfilePath() {
if (g_profile_picker_view && g_profile_picker_view->signed_in_flow_) {
return g_profile_picker_view->signed_in_flow_->switch_profile_path();
}
return base::FilePath();
}
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
// static
void ProfilePicker::SwitchToDiceSignIn(
absl::optional<SkColor> profile_color,
base::OnceCallback<void(bool)> switch_finished_callback) {
if (g_profile_picker_view) {
g_profile_picker_view->SwitchToDiceSignIn(
profile_color, std::move(switch_finished_callback));
}
}
#endif
// static
void ProfilePicker::SwitchToSignedInFlow(absl::optional<SkColor> profile_color,
Profile* signed_in_profile) {
if (g_profile_picker_view) {
g_profile_picker_view->SwitchToSignedInFlow(
profile_color, signed_in_profile,
content::WebContents::Create(
content::WebContents::CreateParams(signed_in_profile)),
/*is_saml=*/false);
}
}
// static
void ProfilePicker::CancelSignedInFlow() {
if (g_profile_picker_view) {
g_profile_picker_view->CancelSignedInFlow();
}
}
// static
base::FilePath ProfilePicker::GetPickerProfilePath() {
return ProfileManager::GetSystemProfilePath();
}
// static
void ProfilePicker::ShowDialog(content::BrowserContext* browser_context,
const GURL& url,
const base::FilePath& profile_path) {
if (g_profile_picker_view) {
g_profile_picker_view->ShowDialog(browser_context, url, profile_path);
}
}
// static
void ProfilePicker::HideDialog() {
if (g_profile_picker_view) {
g_profile_picker_view->HideDialog();
}
}
// static
base::FilePath ProfilePicker::GetForceSigninProfilePath() {
if (g_profile_picker_view) {
return g_profile_picker_view->GetForceSigninProfilePath();
}
return base::FilePath();
}
// static
void ProfilePicker::Hide() {
if (g_profile_picker_view)
g_profile_picker_view->Clear();
}
// static
bool ProfilePicker::IsOpen() {
return g_profile_picker_view;
}
bool ProfilePicker::IsActive() {
if (!IsOpen())
return false;
#if BUILDFLAG(IS_MAC)
return g_profile_picker_view->GetWidget()->IsVisible();
#else
return g_profile_picker_view->GetWidget()->IsActive();
#endif
}
// static
views::WebView* ProfilePicker::GetWebViewForTesting() {
if (!g_profile_picker_view)
return nullptr;
return g_profile_picker_view->web_view_;
}
// static
views::View* ProfilePicker::GetViewForTesting() {
return g_profile_picker_view;
}
// static
void ProfilePicker::AddOnProfilePickerOpenedCallbackForTesting(
base::OnceClosure callback) {
DCHECK(!g_profile_picker_opened_callback_for_testing);
DCHECK(!callback.is_null());
g_profile_picker_opened_callback_for_testing =
new base::OnceClosure(std::move(callback));
}
// static
void ProfilePicker::SetExtendedAccountInfoTimeoutForTesting(
base::TimeDelta timeout) {
if (g_profile_picker_view) {
g_profile_picker_view->SetExtendedAccountInfoTimeoutForTesting( // IN-TEST
timeout);
}
}
// ProfilePickerForceSigninDialog
// -------------------------------------------------------------
// static
void ProfilePickerForceSigninDialog::ShowReauthDialog(
content::BrowserContext* browser_context,
const std::string& email,
const base::FilePath& profile_path) {
DCHECK(signin_util::IsForceSigninEnabled());
if (!ProfilePicker::IsActive())
return;
GURL url = signin::GetEmbeddedReauthURLWithEmail(
signin_metrics::AccessPoint::ACCESS_POINT_USER_MANAGER,
signin_metrics::Reason::kReauthentication, email);
ProfilePicker::ShowDialog(browser_context, url, profile_path);
}
// static
void ProfilePickerForceSigninDialog::ShowForceSigninDialog(
content::BrowserContext* browser_context,
const base::FilePath& profile_path) {
DCHECK(signin_util::IsForceSigninEnabled());
if (!ProfilePicker::IsActive())
return;
GURL url = signin::GetEmbeddedPromoURL(
signin_metrics::AccessPoint::ACCESS_POINT_USER_MANAGER,
signin_metrics::Reason::kForcedSigninPrimaryAccount, true);
ProfilePicker::ShowDialog(browser_context, url, profile_path);
}
void ProfilePickerForceSigninDialog::ShowDialogAndDisplayErrorMessage(
content::BrowserContext* browser_context) {
DCHECK(signin_util::IsForceSigninEnabled());
if (!ProfilePicker::IsActive())
return;
GURL url(chrome::kChromeUISigninErrorURL);
ProfilePicker::ShowDialog(browser_context, url, base::FilePath());
return;
}
// static
void ProfilePickerForceSigninDialog::DisplayErrorMessage() {
DCHECK(signin_util::IsForceSigninEnabled());
if (g_profile_picker_view) {
g_profile_picker_view->DisplayErrorMessage();
}
}
// static
void ProfilePickerForceSigninDialog::HideDialog() {
ProfilePicker::HideDialog();
}
// ProfilePickerView::NavigationFinishedObserver ------------------------------
ProfilePickerView::NavigationFinishedObserver::NavigationFinishedObserver(
const GURL& url,
base::OnceClosure closure,
content::WebContents* contents)
: content::WebContentsObserver(contents),
url_(url),
closure_(std::move(closure)) {}
ProfilePickerView::NavigationFinishedObserver::~NavigationFinishedObserver() =
default;
void ProfilePickerView::NavigationFinishedObserver::DidFinishNavigation(
content::NavigationHandle* navigation_handle) {
if (!closure_ || navigation_handle->GetURL() != url_ ||
!navigation_handle->HasCommitted()) {
return;
}
std::move(closure_).Run();
}
// ProfilePickerView ----------------------------------------------------------
const ui::ThemeProvider*
ProfilePickerView::GetThemeProviderForProfileBeingCreated() const {
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
// Theme provider is only needed for the dice flow.
if (dice_sign_in_provider_)
return dice_sign_in_provider_->GetThemeProvider();
#endif
return nullptr;
}
ui::ColorProviderManager::InitializerSupplier*
ProfilePickerView::GetCustomThemeForProfileBeingCreated() const {
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
// Custom theme is only needed for the dice flow.
if (dice_sign_in_provider_)
return dice_sign_in_provider_->GetCustomTheme();
#endif
return nullptr;
}
void ProfilePickerView::DisplayErrorMessage() {
dialog_host_.DisplayErrorMessage();
}
void ProfilePickerView::ShowScreen(
content::WebContents* contents,
const GURL& url,
base::OnceClosure navigation_finished_closure) {
if (url.is_empty()) {
DCHECK(!navigation_finished_closure);
ShowScreenFinished(contents);
return;
}
contents->GetController().LoadURL(url, content::Referrer(),
ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
std::string());
// Special-case the first ever screen to make sure the WebView has a contents
// assigned in the moment when it gets displayed. This avoids a black flash on
// Win (and potentially other GPU artifacts on other platforms). The rest of
// the work can still be done asynchronously in ShowScreenFinished().
if (web_view_->GetWebContents() == nullptr)
web_view_->SetWebContents(contents);
// Binding as Unretained as `this` outlives member
// `show_screen_finished_observer_`. If ShowScreen gets called twice in a
// short period of time, the first callback may never get called as the first
// observer gets destroyed here or later in ShowScreenFinished(). This is okay
// as all the previous values get replaced by the new values.
show_screen_finished_observer_ = std::make_unique<NavigationFinishedObserver>(
url,
base::BindOnce(&ProfilePickerView::ShowScreenFinished,
base::Unretained(this), contents,
std::move(navigation_finished_closure)),
contents);
}
void ProfilePickerView::ShowScreenInPickerContents(
const GURL& url,
base::OnceClosure navigation_finished_closure) {
ShowScreen(contents_.get(), url, std::move(navigation_finished_closure));
}
void ProfilePickerView::Clear() {
TRACE_EVENT1("browser,startup", "ProfilePickerView::Clear", "state", state_);
if (state_ == kClosing)
return;
if (state_ == kReady) {
GetWidget()->Close();
state_ = kClosing;
return;
}
WindowClosing();
DeleteDelegate();
}
bool ProfilePickerView::ShouldUseDarkColors() const {
return GetNativeTheme()->ShouldUseDarkColors();
}
bool ProfilePickerView::HandleKeyboardEvent(
content::WebContents* source,
const content::NativeWebKeyboardEvent& event) {
// Forward the keyboard event to AcceleratorPressed() through the
// FocusManager.
return unhandled_keyboard_event_handler_.HandleKeyboardEvent(
event, GetFocusManager());
}
bool ProfilePickerView::HandleContextMenu(
content::RenderFrameHost& render_frame_host,
const content::ContextMenuParams& params) {
// Ignores context menu.
return true;
}
gfx::NativeView ProfilePickerView::GetHostView() const {
return GetWidget()->GetNativeView();
}
gfx::Point ProfilePickerView::GetDialogPosition(const gfx::Size& size) {
gfx::Size widget_size = GetWidget()->GetWindowBoundsInScreen().size();
return gfx::Point(std::max(0, (widget_size.width() - size.width()) / 2), 0);
}
gfx::Size ProfilePickerView::GetMaximumDialogSize() {
return GetWidget()->GetWindowBoundsInScreen().size();
}
void ProfilePickerView::AddObserver(
web_modal::ModalDialogHostObserver* observer) {}
void ProfilePickerView::RemoveObserver(
web_modal::ModalDialogHostObserver* observer) {}
ProfilePickerView::ProfilePickerView(const base::FilePath& custom_profile_path)
: keep_alive_(KeepAliveOrigin::USER_MANAGER_VIEW,
KeepAliveRestartOption::DISABLED),
extended_account_info_timeout_(kExtendedAccountInfoTimeout),
custom_profile_path_(custom_profile_path) {
// Setup the WidgetDelegate.
SetHasWindowSizeControls(true);
SetTitle(IDS_PRODUCT_NAME);
ConfigureAccelerators();
// TODO(crbug.com/1063856): Add |RecordDialogCreation|.
}
ProfilePickerView::~ProfilePickerView() {
if (contents_)
contents_->SetDelegate(nullptr);
}
bool ProfilePickerView::ShouldReopen(
ProfilePicker::EntryPoint entry_point,
const GURL& on_select_profile_target_url,
const base::FilePath& custom_profile_path) {
// Need to reopen if already closing or if `custom_profile_path` differs
// from the current one (as we can't switch the profile during run-time).
if (state_ != kClosing && custom_profile_path_ == custom_profile_path)
return false;
restart_on_window_closing_ =
base::BindOnce(&ProfilePicker::Show, entry_point,
on_select_profile_target_url, custom_profile_path);
// No-op if already closing.
ProfilePicker::Hide();
return true;
}
void ProfilePickerView::Display(ProfilePicker::EntryPoint entry_point) {
DCHECK_NE(state_, kClosing);
TRACE_EVENT2("browser,startup", "ProfilePickerView::Display", "entry_point",
entry_point, "state", state_);
// Record creation metrics.
base::UmaHistogramEnumeration("ProfilePicker.Shown", entry_point);
if (entry_point == ProfilePicker::EntryPoint::kOnStartup) {
DCHECK(creation_time_on_startup_.is_null());
// Display() is called right after the creation of this object.
creation_time_on_startup_ = base::TimeTicks::Now();
base::UmaHistogramTimes("ProfilePicker.StartupTime.BeforeCreation",
creation_time_on_startup_ -
startup_metric_utils::MainEntryPointTicks());
}
if (state_ == kNotStarted) {
state_ = kInitializing;
entry_point_ = entry_point;
// Build the layout synchronously before creating the picker profile to
// simplify tests.
BuildLayout();
g_browser_process->profile_manager()->CreateProfileAsync(
custom_profile_path_.empty() ? ProfilePicker::GetPickerProfilePath()
: custom_profile_path_,
base::BindRepeating(&ProfilePickerView::OnPickerProfileCreated,
weak_ptr_factory_.GetWeakPtr()));
return;
}
if (state_ == kInitializing)
return;
GetWidget()->Activate();
}
void ProfilePickerView::OnPickerProfileCreated(Profile* picker_profile,
Profile::CreateStatus status) {
TRACE_EVENT2("browser,startup", "ProfilePickerView::OnPickerProfileCreated",
"profile_path",
(picker_profile ? picker_profile->GetPath().AsUTF8Unsafe() : ""),
"status", status);
DCHECK_NE(status, Profile::CREATE_STATUS_LOCAL_FAIL);
if (status != Profile::CREATE_STATUS_INITIALIZED)
return;
Init(picker_profile);
}
void ProfilePickerView::Init(Profile* picker_profile) {
DCHECK_EQ(state_, kInitializing);
TRACE_EVENT1(
"browser,startup", "ProfilePickerView::Init", "profile_path",
(picker_profile ? picker_profile->GetPath().AsUTF8Unsafe() : ""));
contents_ = content::WebContents::Create(
content::WebContents::CreateParams(picker_profile));
contents_->SetDelegate(this);
// The widget is owned by the native widget.
new ProfilePickerWidget(this);
#if BUILDFLAG(IS_WIN)
// Set the app id for the user manager to the app id of its parent.
ui::win::SetAppIdForWindow(
shell_integration::win::GetAppUserModelIdForBrowser(
picker_profile->GetPath()),
views::HWNDForWidget(GetWidget()));
#endif
ShowScreenInPickerContents(CreateURLForEntryPoint(entry_point_));
GetWidget()->Show();
state_ = kReady;
PrefService* prefs = g_browser_process->local_state();
prefs->SetBoolean(prefs::kBrowserProfilePickerShown, true);
if (entry_point_ == ProfilePicker::EntryPoint::kOnStartup) {
DCHECK(!creation_time_on_startup_.is_null());
base::UmaHistogramTimes("ProfilePicker.StartupTime.WebViewCreated",
base::TimeTicks::Now() - creation_time_on_startup_);
}
if (g_profile_picker_opened_callback_for_testing) {
std::move(*g_profile_picker_opened_callback_for_testing).Run();
delete g_profile_picker_opened_callback_for_testing;
g_profile_picker_opened_callback_for_testing = nullptr;
}
}
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
void ProfilePickerView::SwitchToDiceSignIn(
absl::optional<SkColor> profile_color,
base::OnceCallback<void(bool)> switch_finished_callback) {
// TODO(crbug.com/1227029): Consider having forced signin as another
// implementation of an abstract signin interface to move the code out of
// this class.
if (signin_util::IsForceSigninEnabled()) {
size_t icon_index = profiles::GetPlaceholderAvatarIndex();
ProfileManager::CreateMultiProfileAsync(
g_browser_process->profile_manager()
->GetProfileAttributesStorage()
.ChooseNameForNewProfile(icon_index),
icon_index, /*is_hidden=*/true,
base::BindRepeating(
&ProfilePickerView::OnProfileForDiceForcedSigninCreated,
weak_ptr_factory_.GetWeakPtr(),
base::OwnedRef(std::move(switch_finished_callback))));
return;
}
if (!dice_sign_in_provider_) {
dice_sign_in_provider_ =
std::make_unique<ProfilePickerDiceSignInProvider>(this, toolbar_);
}
dice_sign_in_provider_->SwitchToSignIn(
std::move(switch_finished_callback),
base::BindOnce(&ProfilePickerView::OnDiceSigninFinished,
weak_ptr_factory_.GetWeakPtr(), profile_color));
}
void ProfilePickerView::OnProfileForDiceForcedSigninCreated(
base::OnceCallback<void(bool)>& switch_finished_callback,
Profile* profile,
Profile::CreateStatus status) {
DCHECK(signin_util::IsForceSigninEnabled());
if (status == Profile::CREATE_STATUS_LOCAL_FAIL) {
std::move(switch_finished_callback).Run(false);
return;
} else if (status != Profile::CREATE_STATUS_INITIALIZED) {
return;
}
DCHECK(profile);
std::move(switch_finished_callback).Run(true);
ProfilePickerForceSigninDialog::ShowForceSigninDialog(
web_view_->GetWebContents()->GetBrowserContext(), profile->GetPath());
}
void ProfilePickerView::OnDiceSigninFinished(
absl::optional<SkColor> profile_color,
Profile* signed_in_profile,
std::unique_ptr<content::WebContents> contents,
bool is_saml) {
dice_sign_in_provider_.reset();
SwitchToSignedInFlow(profile_color, signed_in_profile, std::move(contents),
is_saml);
}
#endif
void ProfilePickerView::SwitchToSignedInFlow(
absl::optional<SkColor> profile_color,
Profile* signed_in_profile,
std::unique_ptr<content::WebContents> contents,
bool is_saml) {
DCHECK(!signed_in_flow_);
signed_in_flow_ = std::make_unique<ProfilePickerSignedInFlowController>(
this, signed_in_profile, std::move(contents), profile_color,
extended_account_info_timeout_);
signed_in_flow_->Init(is_saml);
}
void ProfilePickerView::CancelSignedInFlow() {
DCHECK(signed_in_flow_);
signed_in_flow_->Cancel();
switch (entry_point_) {
case ProfilePicker::EntryPoint::kOnStartup:
case ProfilePicker::EntryPoint::kProfileMenuManageProfiles:
case ProfilePicker::EntryPoint::kOpenNewWindowAfterProfileDeletion:
case ProfilePicker::EntryPoint::kNewSessionOnExistingProcess:
case ProfilePicker::EntryPoint::kProfileLocked:
case ProfilePicker::EntryPoint::kUnableToCreateBrowser:
case ProfilePicker::EntryPoint::kBackgroundModeManager: {
// Navigate to the very beginning which is guaranteed to be the profile
// picker.
contents_->GetController().GoToIndex(0);
ShowScreenInPickerContents(GURL());
// Reset the sign-in flow.
signed_in_flow_.reset();
return;
}
case ProfilePicker::EntryPoint::kProfileMenuAddNewProfile: {
// This results in destroying `this` incl. `sign_in_`.
Clear();
return;
}
case ProfilePicker::EntryPoint::kLacrosSelectAvailableAccount:
NOTREACHED() << "Signed in flow is not reachable from this entry point";
}
}
void ProfilePickerView::WindowClosing() {
views::WidgetDelegateView::WindowClosing();
// Now that the window is closed, we can allow a new one to be opened.
// (WindowClosing comes in asynchronously from the call to Close() and we
// may have already opened a new instance).
if (g_profile_picker_view == this)
g_profile_picker_view = nullptr;
// Show a new profile window if it has been requested while the current window
// was closing.
if (state_ == kClosing && restart_on_window_closing_)
std::move(restart_on_window_closing_).Run();
}
views::ClientView* ProfilePickerView::CreateClientView(views::Widget* widget) {
return new views::ClientView(widget, TransferOwnershipOfContentsView());
}
views::View* ProfilePickerView::GetContentsView() {
return this;
}
std::u16string ProfilePickerView::GetAccessibleWindowTitle() const {
if (!web_view_ || !web_view_->GetWebContents() ||
web_view_->GetWebContents()->GetTitle().empty()) {
#if BUILDFLAG(IS_CHROMEOS_LACROS)
return l10n_util::GetStringUTF16(IDS_PROFILE_PICKER_MAIN_VIEW_TITLE_LACROS);
#else
return l10n_util::GetStringUTF16(IDS_PROFILE_PICKER_MAIN_VIEW_TITLE);
#endif
}
return web_view_->GetWebContents()->GetTitle();
}
gfx::Size ProfilePickerView::CalculatePreferredSize() const {
gfx::Size preferred_size = gfx::Size(kWindowWidth, kWindowHeight);
gfx::Size work_area_size = GetWidget()->GetWorkAreaBoundsInScreen().size();
// Keep the window smaller then |work_area_size| so that it feels more like a
// dialog then like the actual Chrome window.
gfx::Size max_dialog_size = ScaleToFlooredSize(
work_area_size, kMaxRatioOfWorkArea, kMaxRatioOfWorkArea);
preferred_size.SetToMin(max_dialog_size);
return preferred_size;
}
gfx::Size ProfilePickerView::GetMinimumSize() const {
// On small screens, the preferred size may be smaller than the picker
// minimum size. In that case there will be scrollbars on the picker.
gfx::Size minimum_size = GetPreferredSize();
minimum_size.SetToMin(ProfilePickerUI::GetMinimumSize());
return minimum_size;
}
bool ProfilePickerView::AcceleratorPressed(const ui::Accelerator& accelerator) {
const auto& iter = accelerator_table_.find(accelerator);
DCHECK(iter != accelerator_table_.end());
int command_id = iter->second;
switch (command_id) {
case IDC_CLOSE_TAB:
case IDC_CLOSE_WINDOW:
// kEscKeyPressed is used although that shortcut is disabled (this is
// Ctrl-Shift-W instead).
GetWidget()->CloseWithReason(views::Widget::ClosedReason::kEscKeyPressed);
break;
case IDC_EXIT:
chrome::AttemptUserExit();
break;
case IDC_FULLSCREEN:
GetWidget()->SetFullscreen(!GetWidget()->IsFullscreen());
break;
case IDC_MINIMIZE_WINDOW:
GetWidget()->Minimize();
break;
case IDC_BACK: {
NavigateBack();
break;
}
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
// Always reload bypassing cache.
case IDC_RELOAD:
case IDC_RELOAD_BYPASSING_CACHE:
case IDC_RELOAD_CLEARING_CACHE: {
// Sign-in may fail due to connectivity issues, allow reloading.
if (GetDiceSigningIn()) {
dice_sign_in_provider_->ReloadSignInPage();
}
break;
}
#endif
default:
NOTREACHED() << "Unexpected command_id: " << command_id;
break;
}
return true;
}
void ProfilePickerView::BuildLayout() {
SetLayoutManager(std::make_unique<views::FlexLayout>())
->SetOrientation(views::LayoutOrientation::kVertical)
.SetMainAxisAlignment(views::LayoutAlignment::kStart)
.SetCrossAxisAlignment(views::LayoutAlignment::kStretch)
.SetDefault(
views::kFlexBehaviorKey,
views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToMinimum,
views::MaximumFlexSizeRule::kUnbounded));
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
auto toolbar = std::make_unique<ProfilePickerDiceSignInToolbar>();
toolbar_ = AddChildView(std::move(toolbar));
// Toolbar gets built and set visible once we it's needed for the Dice signin.
toolbar_->SetVisible(false);
#endif
auto web_view = std::make_unique<views::WebView>();
web_view->set_allow_accelerators(true);
web_view_ = AddChildView(std::move(web_view));
}
void ProfilePickerView::ShowScreenFinished(
content::WebContents* contents,
base::OnceClosure navigation_finished_closure) {
// Stop observing for this (or any previous) navigation.
if (show_screen_finished_observer_)
show_screen_finished_observer_.reset();
web_view_->SetWebContents(contents);
contents->Focus();
if (navigation_finished_closure)
std::move(navigation_finished_closure).Run();
}
void ProfilePickerView::BackButtonPressed(const ui::Event& event) {
NavigateBack();
}
void ProfilePickerView::NavigateBack() {
// Navigating back is not allowed when in the sync-setup phase of profile
// creation.
if (signed_in_flow_)
return;
// Go back in the picker WebContents if it's currently displayed.
if (contents_ && web_view_->GetWebContents() == contents_.get() &&
web_view_->GetWebContents()->GetController().CanGoBack()) {
web_view_->GetWebContents()->GetController().GoBack();
return;
}
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
if (GetDiceSigningIn())
dice_sign_in_provider_->NavigateBack();
#endif
}
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
bool ProfilePickerView::GetDiceSigningIn() const {
// We are on the sign-in screen if the sign-in provider exists.
return static_cast<bool>(dice_sign_in_provider_);
}
#endif
void ProfilePickerView::SetExtendedAccountInfoTimeoutForTesting(
base::TimeDelta timeout) {
extended_account_info_timeout_ = timeout;
}
void ProfilePickerView::ConfigureAccelerators() {
const std::vector<AcceleratorMapping> accelerator_list(GetAcceleratorList());
for (const auto& entry : accelerator_list) {
if (!base::Contains(kSupportedAcceleratorCommands, entry.command_id))
continue;
ui::Accelerator accelerator(entry.keycode, entry.modifiers);
accelerator_table_[accelerator] = entry.command_id;
AddAccelerator(accelerator);
}
#if BUILDFLAG(IS_MAC)
// Check Mac-specific accelerators. Note: Chrome does not support dynamic or
// user-configured accelerators on Mac. Default static accelerators are used
// instead.
for (int command_id : kSupportedAcceleratorCommands) {
ui::Accelerator accelerator;
bool mac_accelerator_found =
GetDefaultMacAcceleratorForCommandId(command_id, &accelerator);
if (mac_accelerator_found) {
accelerator_table_[accelerator] = command_id;
AddAccelerator(accelerator);
}
}
#endif // OS_MAC
}
void ProfilePickerView::ShowDialog(content::BrowserContext* browser_context,
const GURL& url,
const base::FilePath& profile_path) {
gfx::NativeView parent = GetWidget()->GetNativeView();
dialog_host_.ShowDialog(browser_context, url, profile_path, parent);
}
void ProfilePickerView::HideDialog() {
dialog_host_.HideDialog();
}
base::FilePath ProfilePickerView::GetForceSigninProfilePath() const {
return dialog_host_.GetForceSigninProfilePath();
}
GURL ProfilePickerView::GetOnSelectProfileTargetUrl() const {
return on_select_profile_target_url_;
}
BEGIN_METADATA(ProfilePickerView, views::WidgetDelegateView)
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
ADD_READONLY_PROPERTY_METADATA(bool, DiceSigningIn)
#endif
ADD_READONLY_PROPERTY_METADATA(base::FilePath, ForceSigninProfilePath)
END_METADATA