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.
5 #include "chrome/browser/ui/views/profiles/user_manager_view.h"
7 #include "base/memory/scoped_ptr.h"
8 #include "base/time/time.h"
9 #include "chrome/browser/browser_process.h"
10 #include "chrome/browser/lifetime/application_lifetime.h"
11 #include "chrome/browser/profiles/profile_avatar_icon_util.h"
12 #include "chrome/browser/profiles/profile_manager.h"
13 #include "chrome/browser/profiles/profile_metrics.h"
14 #include "chrome/browser/profiles/profile_window.h"
15 #include "chrome/browser/profiles/profiles_state.h"
16 #include "chrome/browser/signin/signin_promo.h"
17 #include "chrome/browser/ui/browser.h"
18 #include "chrome/browser/ui/browser_dialogs.h"
19 #include "chrome/browser/ui/browser_finder.h"
20 #include "chrome/browser/ui/browser_window.h"
21 #include "chrome/browser/ui/user_manager.h"
22 #include "chrome/browser/ui/views/auto_keep_alive.h"
23 #include "chrome/browser/ui/views/browser_dialogs.h"
24 #include "chrome/grit/chromium_strings.h"
25 #include "chrome/grit/generated_resources.h"
26 #include "components/guest_view/browser/guest_view_manager.h"
27 #include "content/public/browser/navigation_details.h"
28 #include "content/public/browser/render_widget_host_view.h"
29 #include "content/public/browser/web_contents.h"
30 #include "google_apis/gaia/gaia_urls.h"
31 #include "ui/base/l10n/l10n_util.h"
32 #include "ui/gfx/screen.h"
33 #include "ui/views/controls/webview/webview.h"
34 #include "ui/views/layout/fill_layout.h"
35 #include "ui/views/view.h"
36 #include "ui/views/widget/widget.h"
37 #include "ui/views/window/dialog_client_view.h"
38 #include "ui/views/window/dialog_delegate.h"
41 #include "chrome/browser/shell_integration.h"
42 #include "ui/base/win/shell.h"
43 #include "ui/views/win/hwnd_util.h"
47 #include "ash/shelf/shelf_util.h"
48 #include "ash/wm/window_util.h"
49 #include "grit/ash_resources.h"
54 // An open User Manager window. There can only be one open at a time. This
55 // is reset to NULL when the window is closed.
56 UserManagerView
* instance_
= NULL
;
57 bool instance_under_construction_
= false;
59 class ReauthDelegate
: public views::DialogDelegateView
,
60 public UserManager::ReauthDialogObserver
{
62 ReauthDelegate(views::WebView
* web_view
,
63 const std::string
& email_address
);
64 ~ReauthDelegate() override
{}
68 // views::DialogDelegate:
69 gfx::Size
GetPreferredSize() const override
;
70 bool CanResize() const override
;
71 bool CanMaximize() const override
;
72 bool CanMinimize() const override
;
73 bool UseNewStyleForThisDialog() const override
;
74 ui::ModalType
GetModalType() const override
;
75 void DeleteDelegate() override
;
76 base::string16
GetWindowTitle() const override
;
77 int GetDialogButtons() const override
;
78 views::View
* GetInitiallyFocusedView() override
;
80 // UserManager::ReauthObserver:
81 void CloseReauthDialog() override
;
83 views::WebView
* web_view_
;
84 const std::string email_address_
;
86 DISALLOW_COPY_AND_ASSIGN(ReauthDelegate
);
89 ReauthDelegate::ReauthDelegate(views::WebView
* web_view
,
90 const std::string
& email_address
)
91 : UserManager::ReauthDialogObserver(
92 web_view
->GetWebContents(), email_address
),
94 email_address_(email_address
) {
95 AddChildView(web_view_
);
96 SetLayoutManager(new views::FillLayout());
98 // Load the re-auth URL, prepopulated with the user's email address.
99 // Add the index of the profile to the URL so that the inline login page
100 // knows which profile to load and update the credentials.
101 GURL url
= signin::GetReauthURLWithEmail(email_address_
);
102 web_view_
->LoadInitialURL(url
);
105 gfx::Size
ReauthDelegate::GetPreferredSize() const {
106 return gfx::Size(UserManager::kReauthDialogWidth
,
107 UserManager::kReauthDialogHeight
);
110 bool ReauthDelegate::CanResize() const {
114 bool ReauthDelegate::CanMaximize() const {
118 bool ReauthDelegate::CanMinimize() const {
122 bool ReauthDelegate::UseNewStyleForThisDialog() const {
126 ui::ModalType
ReauthDelegate::GetModalType() const {
127 return ui::MODAL_TYPE_WINDOW
;
130 void ReauthDelegate::DeleteDelegate() {
134 base::string16
ReauthDelegate::GetWindowTitle() const {
135 return l10n_util::GetStringUTF16(IDS_PROFILES_GAIA_SIGNIN_TITLE
);
138 int ReauthDelegate::GetDialogButtons() const {
139 return ui::DIALOG_BUTTON_NONE
;
142 views::View
* ReauthDelegate::GetInitiallyFocusedView() {
143 return static_cast<views::View
*>(web_view_
);
146 void ReauthDelegate::CloseReauthDialog() {
147 GetWidget()->Close();
152 // UserManager -----------------------------------------------------------------
155 void UserManager::Show(
156 const base::FilePath
& profile_path_to_focus
,
157 profiles::UserManagerTutorialMode tutorial_mode
,
158 profiles::UserManagerProfileSelected profile_open_action
) {
159 DCHECK(profile_path_to_focus
!= ProfileManager::GetGuestProfilePath());
161 ProfileMetrics::LogProfileOpenMethod(ProfileMetrics::OPEN_USER_MANAGER
);
163 // If we are showing the User Manager after locking a profile, change the
164 // active profile to Guest.
165 profiles::SetActiveProfileToGuestIfLocked();
167 // Note the time we started opening the User Manager.
168 instance_
->set_user_manager_started_showing(base::Time::Now());
170 // If there's a user manager window open already, just activate it.
171 instance_
->GetWidget()->Activate();
175 // Under some startup conditions, we can try twice to create the User Manager.
176 // Because creating the System profile is asynchronous, it's possible for
177 // there to then be multiple pending operations and eventually multiple
179 if (instance_under_construction_
)
182 // Create the system profile, if necessary, and open the user manager
183 // from the system profile.
184 UserManagerView
* user_manager
= new UserManagerView();
185 user_manager
->set_user_manager_started_showing(base::Time::Now());
186 profiles::CreateSystemProfileForUserManager(
187 profile_path_to_focus
,
190 base::Bind(&UserManagerView::OnSystemProfileCreated
,
191 base::Passed(make_scoped_ptr(user_manager
)),
192 base::Owned(new base::AutoReset
<bool>(
193 &instance_under_construction_
, true))));
197 void UserManager::Hide() {
199 instance_
->GetWidget()->Close();
203 bool UserManager::IsShowing() {
204 return instance_
? instance_
->GetWidget()->IsActive() : false;
208 void UserManager::OnUserManagerShown() {
210 instance_
->LogTimeToOpen();
214 void UserManager::ShowReauthDialog(content::BrowserContext
* browser_context
,
215 const std::string
& email
) {
216 // This method should only be called if the user manager is already showing.
220 // The dialog delegate will be deleted when the dialog closes and the created
221 // WebView's lifetime is managed by the delegate.
222 views::DialogDelegate
* delegate
=
223 new ReauthDelegate(new views::WebView(browser_context
), email
);
224 gfx::NativeView parent
= instance_
->GetWidget()->GetNativeView();
225 views::DialogDelegate::CreateDialogWidget(delegate
, nullptr, parent
);
226 delegate
->GetWidget()->Show();
229 // UserManagerView -------------------------------------------------------------
231 UserManagerView::UserManagerView()
233 keep_alive_(new AutoKeepAlive(NULL
)),
234 user_manager_started_showing_(base::Time()) {
237 UserManagerView::~UserManagerView() {
241 void UserManagerView::OnSystemProfileCreated(
242 scoped_ptr
<UserManagerView
> instance
,
243 base::AutoReset
<bool>* pending
,
244 Profile
* system_profile
,
245 const std::string
& url
) {
246 // If we are showing the User Manager after locking a profile, change the
247 // active profile to Guest.
248 profiles::SetActiveProfileToGuestIfLocked();
251 instance_
= instance
.release(); // |instance_| takes over ownership.
252 instance_
->Init(system_profile
, GURL(url
));
255 void UserManagerView::Init(Profile
* system_profile
, const GURL
& url
) {
256 web_view_
= new views::WebView(system_profile
);
257 web_view_
->set_allow_accelerators(true);
258 AddChildView(web_view_
);
259 SetLayoutManager(new views::FillLayout
);
260 AddAccelerator(ui::Accelerator(ui::VKEY_W
, ui::EF_CONTROL_DOWN
));
261 AddAccelerator(ui::Accelerator(ui::VKEY_F4
, ui::EF_ALT_DOWN
));
263 // If the user manager is being displayed from an existing profile, use
264 // its last active browser to determine where the user manager should be
265 // placed. This is used so that we can center the dialog on the correct
266 // monitor in a multiple-monitor setup.
268 // If the last active profile is empty (for example, starting up chrome
269 // when all existing profiles are locked), not loaded (for example, if guest
270 // was set after locking the only open profile) or we can't find an active
271 // browser, bounds will remain empty and the user manager will be centered on
272 // the default monitor by default.
274 // Note the profile is accessed via GetProfileByPath(GetLastUsedProfileDir())
275 // instead of GetLastUsedProfile(). If the last active profile isn't loaded,
276 // the latter may try to synchronously load it, which can only be done on a
277 // thread where disk IO is allowed.
279 ProfileManager
* profile_manager
= g_browser_process
->profile_manager();
280 const base::FilePath
& last_used_profile_path
=
281 profile_manager
->GetLastUsedProfileDir(profile_manager
->user_data_dir());
282 Profile
* profile
= profile_manager
->GetProfileByPath(last_used_profile_path
);
284 Browser
* browser
= chrome::FindLastActiveWithProfile(profile
,
285 chrome::GetActiveDesktop());
287 gfx::NativeView native_view
=
288 views::Widget::GetWidgetForNativeWindow(
289 browser
->window()->GetNativeWindow())->GetNativeView();
290 bounds
= gfx::Screen::GetScreenFor(native_view
)->
291 GetDisplayNearestWindow(native_view
).work_area();
292 bounds
.ClampToCenteredSize(gfx::Size(UserManager::kWindowWidth
,
293 UserManager::kWindowHeight
));
297 DialogDelegate::CreateDialogWidgetWithBounds(this, NULL
, NULL
, bounds
);
299 // Since the User Manager can be the only top level window, we don't
300 // want to accidentally quit all of Chrome if the user is just trying to
301 // unfocus the selected pod in the WebView.
302 GetDialogClientView()->RemoveAccelerator(
303 ui::Accelerator(ui::VKEY_ESCAPE
, ui::EF_NONE
));
306 // Set the app id for the task manager to the app id of its parent
307 ui::win::SetAppIdForWindow(
308 ShellIntegration::GetChromiumModelIdForProfile(
309 system_profile
->GetPath()),
310 views::HWNDForWidget(GetWidget()));
314 gfx::NativeWindow native_window
= GetWidget()->GetNativeWindow();
315 ash::SetShelfItemDetailsForDialogWindow(
316 native_window
, IDR_ASH_SHELF_LIST_BROWSER
, native_window
->title());
319 web_view_
->LoadInitialURL(url
);
320 content::RenderWidgetHostView
* rwhv
=
321 web_view_
->GetWebContents()->GetRenderWidgetHostView();
323 rwhv
->SetBackgroundColor(profiles::kUserManagerBackgroundColor
);
326 web_view_
->RequestFocus();
329 void UserManagerView::LogTimeToOpen() {
330 if (user_manager_started_showing_
== base::Time())
333 ProfileMetrics::LogTimeToOpenUserManager(
334 base::Time::Now() - user_manager_started_showing_
);
335 user_manager_started_showing_
= base::Time();
338 bool UserManagerView::AcceleratorPressed(const ui::Accelerator
& accelerator
) {
339 int key
= accelerator
.key_code();
340 int modifier
= accelerator
.modifiers();
341 DCHECK((key
== ui::VKEY_W
&& modifier
== ui::EF_CONTROL_DOWN
) ||
342 (key
== ui::VKEY_F4
&& modifier
== ui::EF_ALT_DOWN
));
343 GetWidget()->Close();
347 gfx::Size
UserManagerView::GetPreferredSize() const {
348 return gfx::Size(UserManager::kWindowWidth
, UserManager::kWindowHeight
);
351 bool UserManagerView::CanResize() const {
355 bool UserManagerView::CanMaximize() const {
359 bool UserManagerView::CanMinimize() const {
363 base::string16
UserManagerView::GetWindowTitle() const {
364 return l10n_util::GetStringUTF16(IDS_PRODUCT_NAME
);
367 int UserManagerView::GetDialogButtons() const {
368 return ui::DIALOG_BUTTON_NONE
;
371 void UserManagerView::WindowClosing() {
372 // Now that the window is closed, we can allow a new one to be opened.
373 // (WindowClosing comes in asynchronously from the call to Close() and we
374 // may have already opened a new instance).
375 if (instance_
== this)
379 bool UserManagerView::UseNewStyleForThisDialog() const {