1 // Copyright 2014 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/ui/views/profiles/user_manager_view.h"
6 
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/memory/ptr_util.h"
10 #include "base/time/time.h"
11 #include "build/build_config.h"
12 #include "chrome/browser/browser_process.h"
13 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
14 #include "chrome/browser/platform_util.h"
15 #include "chrome/browser/profiles/profile_avatar_icon_util.h"
16 #include "chrome/browser/profiles/profile_manager.h"
17 #include "chrome/browser/profiles/profile_metrics.h"
18 #include "chrome/browser/profiles/profile_window.h"
19 #include "chrome/browser/profiles/profiles_state.h"
20 #include "chrome/browser/signin/signin_promo.h"
21 #include "chrome/browser/signin/signin_util.h"
22 #include "chrome/browser/task_manager/web_contents_tags.h"
23 #include "chrome/browser/ui/autofill/chrome_autofill_client.h"
24 #include "chrome/browser/ui/browser.h"
25 #include "chrome/browser/ui/browser_dialogs.h"
26 #include "chrome/browser/ui/browser_finder.h"
27 #include "chrome/browser/ui/browser_window.h"
28 #include "chrome/browser/ui/profile_picker.h"
29 #include "chrome/browser/ui/ui_features.h"
30 #include "chrome/browser/ui/user_manager.h"
31 #include "chrome/common/url_constants.h"
32 #include "chrome/grit/chromium_strings.h"
33 #include "chrome/grit/generated_resources.h"
34 #include "components/guest_view/browser/guest_view_manager.h"
35 #include "components/keep_alive_registry/keep_alive_types.h"
36 #include "components/keep_alive_registry/scoped_keep_alive.h"
37 #include "components/web_modal/web_contents_modal_dialog_manager.h"
38 #include "content/public/browser/navigation_details.h"
39 #include "content/public/browser/render_widget_host_view.h"
40 #include "content/public/browser/web_contents.h"
41 #include "content/public/browser/web_contents_user_data.h"
42 #include "google_apis/gaia/gaia_urls.h"
43 #include "ui/display/display.h"
44 #include "ui/display/screen.h"
45 #include "ui/views/controls/webview/webview.h"
46 #include "ui/views/layout/fill_layout.h"
47 #include "ui/views/view.h"
48 #include "ui/views/widget/widget.h"
49 
50 #if defined(OS_WIN)
51 #include "chrome/browser/shell_integration_win.h"
52 #include "ui/base/win/shell.h"
53 #include "ui/views/win/hwnd_util.h"
54 #endif
55 
56 #if defined(OS_MAC)
57 #include "chrome/browser/app_controller_mac.h"
58 #endif
59 
60 namespace {
61 
62 // An open User Manager window. There can only be one open at a time. This
63 // is reset to nullptr when the window is closed.
64 UserManagerView* g_user_manager_view = nullptr;
65 base::OnceClosure* g_user_manager_shown_callback_for_testing = nullptr;
66 bool g_is_user_manager_view_under_construction = false;
67 }  // namespace
68 
69 // Delegate---------------------------------------------------------------
70 
UserManagerProfileDialogDelegate(UserManagerView * parent,views::WebView * web_view,const std::string & email_address,const GURL & url)71 UserManagerProfileDialogDelegate::UserManagerProfileDialogDelegate(
72     UserManagerView* parent,
73     views::WebView* web_view,
74     const std::string& email_address,
75     const GURL& url)
76     : parent_(parent), web_view_(web_view), email_address_(email_address) {
77   SetHasWindowSizeControls(true);
78   SetTitle(IDS_PROFILES_GAIA_SIGNIN_TITLE);
79   SetButtons(ui::DIALOG_BUTTON_NONE);
80   set_use_custom_frame(false);
81 
82   AddChildView(web_view_);
83   SetLayoutManager(std::make_unique<views::FillLayout>());
84 
85   web_view_->GetWebContents()->SetDelegate(this);
86 
87   ChromePasswordManagerClient::CreateForWebContentsWithAutofillClient(
88       web_view_->GetWebContents(),
89       autofill::ChromeAutofillClient::FromWebContents(
90           web_view_->GetWebContents()));
91 
92   web_modal::WebContentsModalDialogManager::CreateForWebContents(
93       web_view_->GetWebContents());
94   web_modal::WebContentsModalDialogManager::FromWebContents(
95       web_view_->GetWebContents())
96       ->SetDelegate(this);
97 
98   web_view_->LoadInitialURL(url);
99 
100   chrome::RecordDialogCreation(chrome::DialogIdentifier::USER_MANAGER_PROFILE);
101 }
102 
~UserManagerProfileDialogDelegate()103 UserManagerProfileDialogDelegate::~UserManagerProfileDialogDelegate() {}
104 
CalculatePreferredSize() const105 gfx::Size UserManagerProfileDialogDelegate::CalculatePreferredSize() const {
106   return gfx::Size(UserManagerProfileDialog::kDialogWidth,
107                    UserManagerProfileDialog::kDialogHeight);
108 }
109 
DisplayErrorMessage()110 void UserManagerProfileDialogDelegate::DisplayErrorMessage() {
111   web_view_->LoadInitialURL(GURL(chrome::kChromeUISigninErrorURL));
112 }
113 
114 web_modal::WebContentsModalDialogHost*
GetWebContentsModalDialogHost()115 UserManagerProfileDialogDelegate::GetWebContentsModalDialogHost() {
116   return this;
117 }
118 
GetHostView() const119 gfx::NativeView UserManagerProfileDialogDelegate::GetHostView() const {
120   return GetWidget()->GetNativeView();
121 }
122 
GetDialogPosition(const gfx::Size & size)123 gfx::Point UserManagerProfileDialogDelegate::GetDialogPosition(
124     const gfx::Size& size) {
125   gfx::Size widget_size = GetWidget()->GetWindowBoundsInScreen().size();
126   return gfx::Point(std::max(0, (widget_size.width() - size.width()) / 2),
127                     std::max(0, (widget_size.height() - size.height()) / 2));
128 }
129 
GetMaximumDialogSize()130 gfx::Size UserManagerProfileDialogDelegate::GetMaximumDialogSize() {
131   return GetWidget()->GetWindowBoundsInScreen().size();
132 }
133 
AddObserver(web_modal::ModalDialogHostObserver * observer)134 void UserManagerProfileDialogDelegate::AddObserver(
135     web_modal::ModalDialogHostObserver* observer) {}
136 
RemoveObserver(web_modal::ModalDialogHostObserver * observer)137 void UserManagerProfileDialogDelegate::RemoveObserver(
138     web_modal::ModalDialogHostObserver* observer) {}
139 
GetModalType() const140 ui::ModalType UserManagerProfileDialogDelegate::GetModalType() const {
141   return ui::MODAL_TYPE_WINDOW;
142 }
143 
DeleteDelegate()144 void UserManagerProfileDialogDelegate::DeleteDelegate() {
145   OnDialogDestroyed();
146   delete this;
147 }
148 
GetInitiallyFocusedView()149 views::View* UserManagerProfileDialogDelegate::GetInitiallyFocusedView() {
150   return static_cast<views::View*>(web_view_);
151 }
152 
CloseDialog()153 void UserManagerProfileDialogDelegate::CloseDialog() {
154   OnDialogDestroyed();
155   GetWidget()->Close();
156 }
157 
OnDialogDestroyed()158 void UserManagerProfileDialogDelegate::OnDialogDestroyed() {
159   if (parent_) {
160     parent_->OnDialogDestroyed();
161     parent_ = nullptr;
162   }
163 }
164 
165 // UserManager -----------------------------------------------------------------
166 
167 // static
Show(const base::FilePath & profile_path_to_focus,profiles::UserManagerAction user_manager_action)168 void UserManager::Show(
169     const base::FilePath& profile_path_to_focus,
170     profiles::UserManagerAction user_manager_action) {
171   DCHECK(profile_path_to_focus != ProfileManager::GetGuestProfilePath());
172 
173   if (!signin_util::IsForceSigninEnabled() &&
174       (user_manager_action == profiles::USER_MANAGER_SELECT_PROFILE_NO_ACTION ||
175        user_manager_action == profiles::USER_MANAGER_OPEN_CREATE_USER_PAGE) &&
176       base::FeatureList::IsEnabled(features::kNewProfilePicker)) {
177     // Use the new profile picker instead.
178     ProfilePicker::Show(
179         user_manager_action == profiles::USER_MANAGER_OPEN_CREATE_USER_PAGE
180             ? ProfilePicker::EntryPoint::kProfileMenuAddNewProfile
181             : ProfilePicker::EntryPoint::kProfileMenuManageProfiles);
182     return;
183   }
184 
185   if (g_user_manager_view) {
186     // If we are showing the User Manager after locking a profile, change the
187     // active profile to Guest.
188     profiles::SetActiveProfileToGuestIfLocked();
189 
190 #if defined(OS_MAC)
191     app_controller_mac::CreateGuestProfileIfNeeded();
192 #endif
193 
194     // Note the time we started opening the User Manager.
195     g_user_manager_view->set_user_manager_started_showing(base::Time::Now());
196 
197     // If there's a user manager window open already, just activate it.
198     g_user_manager_view->GetWidget()->Activate();
199     return;
200   }
201 
202   // Under some startup conditions, we can try twice to create the User Manager.
203   // Because creating the System profile is asynchronous, it's possible for
204   // there to then be multiple pending operations and eventually multiple
205   // User Managers.
206   if (g_is_user_manager_view_under_construction)
207     return;
208 
209   // Create the system profile, if necessary, and open the user manager
210   // from the system profile.
211   UserManagerView* user_manager = new UserManagerView();
212   user_manager->set_user_manager_started_showing(base::Time::Now());
213   profiles::CreateSystemProfileForUserManager(
214       profile_path_to_focus, user_manager_action,
215       base::BindRepeating(
216           &UserManagerView::OnSystemProfileCreated,
217           base::Passed(base::WrapUnique(user_manager)),
218           base::Owned(new base::AutoReset<bool>(
219               &g_is_user_manager_view_under_construction, true))));
220 }
221 
222 // static
Hide()223 void UserManager::Hide() {
224   // Hide the profile picker, in case it was opened by UserManager::Show().
225   ProfilePicker::Hide();
226 
227   if (g_user_manager_view)
228     g_user_manager_view->GetWidget()->Close();
229 }
230 
231 // static
IsShowing()232 bool UserManager::IsShowing() {
233 #if defined(OS_MAC)
234   // Widget activation works differently on Mac: the UserManager is a child
235   // widget, so it is not active in the IsActive() sense even when showing
236   // and interactable. Test for IsVisible instead - this is what the Cocoa
237   // UserManager::IsShowing() does as well.
238   return g_user_manager_view ? g_user_manager_view->GetWidget()->IsVisible()
239                              : false;
240 #else
241   return g_user_manager_view ? g_user_manager_view->GetWidget()->IsActive()
242                              : false;
243 #endif
244 }
245 
246 // static
OnUserManagerShown()247 void UserManager::OnUserManagerShown() {
248   if (g_user_manager_view) {
249     g_user_manager_view->LogTimeToOpen();
250     if (g_user_manager_shown_callback_for_testing) {
251       if (!g_user_manager_shown_callback_for_testing->is_null())
252         std::move(*g_user_manager_shown_callback_for_testing).Run();
253 
254       delete g_user_manager_shown_callback_for_testing;
255       g_user_manager_shown_callback_for_testing = nullptr;
256     }
257   }
258 }
259 
260 // static
AddOnUserManagerShownCallbackForTesting(base::OnceClosure callback)261 void UserManager::AddOnUserManagerShownCallbackForTesting(
262     base::OnceClosure callback) {
263   DCHECK(!g_user_manager_shown_callback_for_testing);
264   g_user_manager_shown_callback_for_testing =
265       new base::OnceClosure(std::move(callback));
266 }
267 
268 // static
GetSigninProfilePath()269 base::FilePath UserManager::GetSigninProfilePath() {
270   if (!g_user_manager_view)
271     return base::FilePath();
272 
273   return g_user_manager_view->GetSigninProfilePath();
274 }
275 
276 // UserManagerProfileDialog
277 // -------------------------------------------------------------
278 
279 // static
ShowUnlockDialog(content::BrowserContext * browser_context,const std::string & email)280 void UserManagerProfileDialog::ShowUnlockDialog(
281     content::BrowserContext* browser_context,
282     const std::string& email) {
283   ShowUnlockDialogWithProfilePath(browser_context, email, base::FilePath());
284 }
285 
286 // static
ShowUnlockDialogWithProfilePath(content::BrowserContext * browser_context,const std::string & email,const base::FilePath & profile_path)287 void UserManagerProfileDialog::ShowUnlockDialogWithProfilePath(
288     content::BrowserContext* browser_context,
289     const std::string& email,
290     const base::FilePath& profile_path) {
291   // This method should only be called if the user manager is already showing.
292   if (!UserManager::IsShowing())
293     return;
294   // Load the re-auth URL, prepopulated with the user's email address.
295   // Add the index of the profile to the URL so that the inline login page
296   // knows which profile to load and update the credentials.
297   GURL url = signin::GetEmbeddedReauthURLWithEmail(
298       signin_metrics::AccessPoint::ACCESS_POINT_USER_MANAGER,
299       signin_metrics::Reason::REASON_UNLOCK, email);
300   g_user_manager_view->SetSigninProfilePath(profile_path);
301   g_user_manager_view->ShowDialog(browser_context, email, url);
302 }
303 
304 // static
ShowForceSigninDialog(content::BrowserContext * browser_context,const base::FilePath & profile_path)305 void UserManagerProfileDialog::ShowForceSigninDialog(
306     content::BrowserContext* browser_context,
307     const base::FilePath& profile_path) {
308   if (!UserManager::IsShowing())
309     return;
310   g_user_manager_view->SetSigninProfilePath(profile_path);
311   GURL url = signin::GetEmbeddedPromoURL(
312       signin_metrics::AccessPoint::ACCESS_POINT_USER_MANAGER,
313       signin_metrics::Reason::REASON_FORCED_SIGNIN_PRIMARY_ACCOUNT, true);
314   g_user_manager_view->ShowDialog(browser_context, std::string(), url);
315 }
316 
ShowDialogAndDisplayErrorMessage(content::BrowserContext * browser_context)317 void UserManagerProfileDialog::ShowDialogAndDisplayErrorMessage(
318     content::BrowserContext* browser_context) {
319   if (!UserManager::IsShowing())
320     return;
321   // The error occurred before sign in happened, reset |signin_profile_path_|
322   // so that the error page will show the error message that is assoicated with
323   // the system profile.
324   g_user_manager_view->SetSigninProfilePath(base::FilePath());
325   g_user_manager_view->ShowDialog(browser_context, std::string(),
326                                   GURL(chrome::kChromeUISigninErrorURL));
327 }
328 
329 // static
DisplayErrorMessage()330 void UserManagerProfileDialog::DisplayErrorMessage() {
331   // This method should only be called if the user manager is already showing.
332   DCHECK(g_user_manager_view);
333   g_user_manager_view->DisplayErrorMessage();
334 }
335 
336 // static
HideDialog()337 void UserManagerProfileDialog::HideDialog() {
338   if (g_user_manager_view && g_user_manager_view->GetWidget()->IsVisible())
339     g_user_manager_view->HideDialog();
340 }
341 
342 // UserManagerView -------------------------------------------------------------
343 
UserManagerView()344 UserManagerView::UserManagerView()
345     : web_view_(nullptr),
346       delegate_(nullptr),
347       user_manager_started_showing_(base::Time()) {
348   SetButtons(ui::DIALOG_BUTTON_NONE);
349   SetHasWindowSizeControls(true);
350   SetTitle(IDS_PRODUCT_NAME);
351   set_use_custom_frame(false);
352   keep_alive_ = std::make_unique<ScopedKeepAlive>(
353       KeepAliveOrigin::USER_MANAGER_VIEW, KeepAliveRestartOption::DISABLED);
354   chrome::RecordDialogCreation(chrome::DialogIdentifier::USER_MANAGER);
355 }
356 
~UserManagerView()357 UserManagerView::~UserManagerView() {
358   HideDialog();
359 }
360 
361 // static
OnSystemProfileCreated(std::unique_ptr<UserManagerView> instance,base::AutoReset<bool> * pending,Profile * system_profile,const std::string & url)362 void UserManagerView::OnSystemProfileCreated(
363     std::unique_ptr<UserManagerView> instance,
364     base::AutoReset<bool>* pending,
365     Profile* system_profile,
366     const std::string& url) {
367   // If we are showing the User Manager after locking a profile, change the
368   // active profile to Guest.
369   profiles::SetActiveProfileToGuestIfLocked();
370 
371 #if defined(OS_MAC)
372   app_controller_mac::CreateGuestProfileIfNeeded();
373 #endif
374 
375   DCHECK(!g_user_manager_view);
376   g_user_manager_view =
377       instance.release();  // |g_user_manager_view| takes over ownership.
378   g_user_manager_view->Init(system_profile, GURL(url));
379 }
380 
ShowDialog(content::BrowserContext * browser_context,const std::string & email,const GURL & url)381 void UserManagerView::ShowDialog(content::BrowserContext* browser_context,
382                                  const std::string& email,
383                                  const GURL& url) {
384   HideDialog();
385   // The dialog delegate will be deleted when the widget closes. The created
386   // WebView's lifetime is managed by the delegate.
387   delegate_ = new UserManagerProfileDialogDelegate(
388       this, new views::WebView(browser_context), email, url);
389   gfx::NativeView parent = g_user_manager_view->GetWidget()->GetNativeView();
390   views::DialogDelegate::CreateDialogWidget(delegate_, nullptr, parent);
391   delegate_->GetWidget()->Show();
392 }
393 
HideDialog()394 void UserManagerView::HideDialog() {
395   if (delegate_) {
396     delegate_->CloseDialog();
397     DCHECK(!delegate_);
398   }
399 }
400 
OnDialogDestroyed()401 void UserManagerView::OnDialogDestroyed() {
402   delegate_ = nullptr;
403 }
404 
Init(Profile * system_profile,const GURL & url)405 void UserManagerView::Init(Profile* system_profile, const GURL& url) {
406   web_view_ = new views::WebView(system_profile);
407   web_view_->set_allow_accelerators(true);
408   AddChildView(web_view_);
409   SetLayoutManager(std::make_unique<views::FillLayout>());
410   AddAccelerator(ui::Accelerator(ui::VKEY_W, ui::EF_CONTROL_DOWN));
411   AddAccelerator(ui::Accelerator(ui::VKEY_F4, ui::EF_ALT_DOWN));
412 
413   // Make the user manager WebContents show up in the task manager.
414   content::WebContents* web_contents = web_view_->GetWebContents();
415   task_manager::WebContentsTags::CreateForToolContents(
416       web_contents, IDS_PROFILES_MANAGE_USERS_BUTTON);
417 
418   // If the user manager is being displayed from an existing profile, use
419   // its last active browser to determine where the user manager should be
420   // placed.  This is used so that we can center the dialog on the correct
421   // monitor in a multiple-monitor setup.
422   //
423   // If the last active profile is empty (for example, starting up chrome
424   // when all existing profiles are locked), not loaded (for example, if guest
425   // was set after locking the only open profile) or we can't find an active
426   // browser, bounds will remain empty and the user manager will be centered on
427   // the default monitor by default.
428   //
429   // Note the profile is accessed via GetProfileByPath(GetLastUsedProfileDir())
430   // instead of GetLastUsedProfile().  If the last active profile isn't loaded,
431   // the latter may try to synchronously load it, which can only be done on a
432   // thread where disk IO is allowed.
433   gfx::Rect bounds;
434   ProfileManager* profile_manager = g_browser_process->profile_manager();
435   const base::FilePath& last_used_profile_path =
436       profile_manager->GetLastUsedProfileDir(profile_manager->user_data_dir());
437   Profile* profile = profile_manager->GetProfileByPath(last_used_profile_path);
438   if (profile) {
439     Browser* browser = chrome::FindLastActiveWithProfile(profile);
440     if (browser) {
441       gfx::NativeView native_view =
442           views::Widget::GetWidgetForNativeWindow(
443               browser->window()->GetNativeWindow())->GetNativeView();
444       bounds = display::Screen::GetScreen()
445                    ->GetDisplayNearestView(native_view)
446                    .work_area();
447       bounds.ClampToCenteredSize(gfx::Size(UserManager::kWindowWidth,
448                                            UserManager::kWindowHeight));
449     }
450   }
451 
452   views::Widget::InitParams params =
453       GetDialogWidgetInitParams(this, nullptr, nullptr, bounds);
454   (new views::Widget)->Init(std::move(params));
455 
456 #if defined(OS_WIN)
457   // Set the app id for the user manager to the app id of its parent.
458   ui::win::SetAppIdForWindow(
459       shell_integration::win::GetAppUserModelIdForBrowser(
460           system_profile->GetPath()),
461       views::HWNDForWidget(GetWidget()));
462 #endif
463 
464   web_view_->LoadInitialURL(url);
465   content::RenderWidgetHostView* rwhv = web_contents->GetRenderWidgetHostView();
466   if (rwhv)
467     rwhv->SetBackgroundColor(profiles::kUserManagerBackgroundColor);
468 
469   GetWidget()->Show();
470   web_view_->RequestFocus();
471 }
472 
LogTimeToOpen()473 void UserManagerView::LogTimeToOpen() {
474   if (user_manager_started_showing_ == base::Time())
475     return;
476 
477   ProfileMetrics::LogTimeToOpenUserManager(
478       base::Time::Now() - user_manager_started_showing_);
479   user_manager_started_showing_ = base::Time();
480 }
481 
AcceleratorPressed(const ui::Accelerator & accelerator)482 bool UserManagerView::AcceleratorPressed(const ui::Accelerator& accelerator) {
483   int key = accelerator.key_code();
484   int modifier = accelerator.modifiers();
485 
486   // Ignore presses of the Escape key. The user manager may be Chrome's only
487   // top-level window, in which case we don't want presses of Esc to maybe quit
488   // the entire browser. This has higher priority than the default dialog Esc
489   // accelerator (which would otherwise close the window).
490   if (key == ui::VKEY_ESCAPE && modifier == ui::EF_NONE)
491     return true;
492 
493   DCHECK((key == ui::VKEY_W && modifier == ui::EF_CONTROL_DOWN) ||
494          (key == ui::VKEY_F4 && modifier == ui::EF_ALT_DOWN));
495   GetWidget()->Close();
496   return true;
497 }
498 
CalculatePreferredSize() const499 gfx::Size UserManagerView::CalculatePreferredSize() const {
500   return gfx::Size(UserManager::kWindowWidth, UserManager::kWindowHeight);
501 }
502 
WindowClosing()503 void UserManagerView::WindowClosing() {
504   // Now that the window is closed, we can allow a new one to be opened.
505   // (WindowClosing comes in asynchronously from the call to Close() and we
506   // may have already opened a new instance).
507   if (g_user_manager_view == this)
508     g_user_manager_view = nullptr;
509 }
510 
DisplayErrorMessage()511 void UserManagerView::DisplayErrorMessage() {
512   if (delegate_)
513     delegate_->DisplayErrorMessage();
514 }
515 
SetSigninProfilePath(const base::FilePath & profile_path)516 void UserManagerView::SetSigninProfilePath(const base::FilePath& profile_path) {
517   signin_profile_path_ = profile_path;
518 }
519 
GetSigninProfilePath()520 base::FilePath UserManagerView::GetSigninProfilePath() {
521   return signin_profile_path_;
522 }
523