Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / profiles / user_manager_mac.mm
blob9cd790f7b9d6de890243873c086ffe9bec6ebad5
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/cocoa/profiles/user_manager_mac.h"
7 #include "base/mac/foundation_util.h"
8 #include "chrome/app/chrome_command_ids.h"
9 #import "chrome/browser/app_controller_mac.h"
10 #include "chrome/browser/browser_process.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/profiles_state.h"
15 #include "chrome/browser/signin/signin_promo.h"
16 #include "chrome/browser/ui/browser_dialogs.h"
17 #import "chrome/browser/ui/cocoa/browser_window_utils.h"
18 #include "chrome/browser/ui/cocoa/chrome_event_processing_window.h"
19 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sheet.h"
20 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_window.h"
21 #include "chrome/browser/ui/cocoa/constrained_window/constrained_window_mac.h"
22 #include "chrome/browser/ui/user_manager.h"
23 #include "chrome/grit/chromium_strings.h"
24 #include "components/web_modal/web_contents_modal_dialog_host.h"
25 #include "components/web_modal/web_contents_modal_dialog_manager.h"
26 #include "components/web_modal/web_contents_modal_dialog_manager_delegate.h"
27 #include "content/public/browser/native_web_keyboard_event.h"
28 #include "content/public/browser/render_widget_host_view.h"
29 #include "content/public/browser/web_contents.h"
30 #include "content/public/browser/web_contents_delegate.h"
31 #include "ui/base/l10n/l10n_util_mac.h"
32 #include "ui/events/keycodes/keyboard_codes.h"
34 namespace {
36 // Update the App Controller with a new Profile. Used when a Profile is locked
37 // to set the Controller to the Guest profile so the old Profile's bookmarks,
38 // etc... cannot be accessed.
39 void ChangeAppControllerForProfile(Profile* profile,
40                                    Profile::CreateStatus status) {
41   if (status == Profile::CREATE_STATUS_INITIALIZED) {
42     AppController* controller =
43         base::mac::ObjCCast<AppController>([NSApp delegate]);
44     [controller windowChangedToProfile:profile];
45   }
48 }  // namespace
50 // An open User Manager window. There can only be one open at a time. This
51 // is reset to NULL when the window is closed.
52 UserManagerMac* instance_ = NULL;  // Weak.
53 BOOL instance_under_construction_ = NO;
55 void CloseInstanceReauthDialog() {
56   DCHECK(instance_);
57   instance_->CloseReauthDialog();
60 // The modal dialog host the User Manager uses to display the reauth dialog.
61 class UserManagerModalHost : public web_modal::WebContentsModalDialogHost {
62  public:
63   UserManagerModalHost(gfx::NativeView host_view)
64       : host_view_(host_view) {}
66   gfx::Size GetMaximumDialogSize() override {
67     return gfx::Size(
68         UserManager::kReauthDialogWidth, UserManager::kReauthDialogHeight);
69   }
71   ~UserManagerModalHost() override {}
73   gfx::NativeView GetHostView() const override {
74     return host_view_;
75   }
77   gfx::Point GetDialogPosition(const gfx::Size& size) override {
78     return gfx::Point(0, 0);
79   }
81   void AddObserver(web_modal::ModalDialogHostObserver* observer) override {}
82   void RemoveObserver(web_modal::ModalDialogHostObserver* observer) override {}
84  private:
85   gfx::NativeView host_view_;
88 // The modal manager delegate allowing the display of constrained windows for
89 // the reauth dialog.
90 class UserManagerModalManagerDelegate :
91     public web_modal::WebContentsModalDialogManagerDelegate {
92  public:
93   UserManagerModalManagerDelegate(gfx::NativeView host_view) {
94     modal_host_.reset(new UserManagerModalHost(host_view));
95   }
97   web_modal::WebContentsModalDialogHost* GetWebContentsModalDialogHost()
98       override {
99     return modal_host_.get();
100   }
102   bool IsWebContentsVisible(content::WebContents* web_contents) override {
103     return true;
104   }
106    ~UserManagerModalManagerDelegate() override {}
107  protected:
108   scoped_ptr<UserManagerModalHost> modal_host_;
111 // Custom WebContentsDelegate that allows handling of hotkeys.
112 class UserManagerWebContentsDelegate : public content::WebContentsDelegate {
113  public:
114   UserManagerWebContentsDelegate() {}
116   // WebContentsDelegate implementation. Forwards all unhandled keyboard events
117   // to the current window.
118   void HandleKeyboardEvent(
119       content::WebContents* source,
120       const content::NativeWebKeyboardEvent& event) override {
121     if (![BrowserWindowUtils shouldHandleKeyboardEvent:event])
122       return;
124     // -getCommandId returns -1 if the event isn't a chrome accelerator.
125     int chromeCommandId = [BrowserWindowUtils getCommandId:event];
127     // Check for Cmd+A and Cmd+V events that could come from a password field.
128     bool isTextEditingCommand =
129         (event.modifiers & blink::WebInputEvent::MetaKey) &&
130         (event.windowsKeyCode == ui::VKEY_A ||
131          event.windowsKeyCode == ui::VKEY_V);
133     // Only handle close window Chrome accelerators and text editing ones.
134     if (chromeCommandId == IDC_CLOSE_WINDOW || chromeCommandId == IDC_EXIT ||
135         isTextEditingCommand) {
136       [[NSApp mainMenu] performKeyEquivalent:event.os_event];
137     }
138   }
141 class ReauthDialogDelegate : public UserManager::ReauthDialogObserver,
142                              public UserManagerWebContentsDelegate,
143                              public ConstrainedWindowMacDelegate {
144  public:
145    ReauthDialogDelegate(content::WebContents* web_contents, std::string email)
146       : UserManager::ReauthDialogObserver(web_contents, email) {}
148   // UserManager::ReauthDialogObserver:
149   void CloseReauthDialog() override {
150     CloseInstanceReauthDialog();
151   }
153   // ConstrainedWindowMacDelegate:
154   void OnConstrainedWindowClosed(ConstrainedWindowMac* window) override {
155     CloseReauthDialog();
156   }
158   DISALLOW_COPY_AND_ASSIGN(ReauthDialogDelegate);
161 // WindowController for the reauth dialog.
162 @interface ReauthDialogWindowController
163     : NSWindowController <NSWindowDelegate> {
164  @private
165   std::string emailAddress_;
166   content::WebContents* webContents_;
167   scoped_ptr<ReauthDialogDelegate> webContentsDelegate_;
168   scoped_ptr<ConstrainedWindowMac> constrained_window_;
169   scoped_ptr<content::WebContents> reauthWebContents_;
171 - (id)initWithProfile:(Profile*)profile
172                 email:(std::string)email
173           webContents:(content::WebContents*)webContents;
174 - (void)close;
175 @end
177 @implementation ReauthDialogWindowController
179 - (id)initWithProfile:(Profile*)profile
180                 email:(std::string)email
181           webContents:(content::WebContents*)webContents {
182   webContents_ = webContents;
183   emailAddress_ = email;
185   NSRect frame = NSMakeRect(
186       0, 0, UserManager::kReauthDialogWidth, UserManager::kReauthDialogHeight);
187   base::scoped_nsobject<ConstrainedWindowCustomWindow> window(
188       [[ConstrainedWindowCustomWindow alloc]
189           initWithContentRect:frame
190                     styleMask:NSTitledWindowMask | NSClosableWindowMask]);
191   if ((self = [super initWithWindow:window])) {
192     webContents_ = webContents;
194     reauthWebContents_.reset(content::WebContents::Create(
195         content::WebContents::CreateParams(profile)));
196     window.get().contentView = reauthWebContents_->GetNativeView();
197     webContentsDelegate_.reset(
198        new ReauthDialogDelegate(reauthWebContents_.get(), emailAddress_));
199     reauthWebContents_->SetDelegate(webContentsDelegate_.get());
201     base::scoped_nsobject<CustomConstrainedWindowSheet> sheet(
202        [[CustomConstrainedWindowSheet alloc]
203            initWithCustomWindow:[self window]]);
204     constrained_window_.reset(
205        new ConstrainedWindowMac(
206           webContentsDelegate_.get(), webContents_, sheet));
208     // The close button needs to call CloseWebContentsModalDialog() on the
209     // constrained window isntead of just [window close] so grab a reference to
210     // it in the title bar and change its action.
211     auto closeButton = [window standardWindowButton:NSWindowCloseButton];
212     [closeButton setTarget:self];
213     [closeButton setAction:@selector(closeButtonClicked:)];
214     [self show];
215   }
217   return self;
220 - (void)show {
221   GURL url = signin::GetReauthURLWithEmail(emailAddress_);
222   reauthWebContents_->GetController().LoadURL(url, content::Referrer(),
223                                         ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
224                                         std::string());
227 - (void)closeButtonClicked:(NSButton*)button {
228   [self close];
231 - (void)close {
232   constrained_window_->CloseWebContentsModalDialog();
235 - (void)dealloc {
236   constrained_window_->CloseWebContentsModalDialog();
238   [super dealloc];
241 @end
243 // Window controller for the User Manager view.
244 @interface UserManagerWindowController : NSWindowController <NSWindowDelegate> {
245  @private
246   scoped_ptr<content::WebContents> webContents_;
247   scoped_ptr<UserManagerWebContentsDelegate> webContentsDelegate_;
248   UserManagerMac* userManagerObserver_;  // Weak.
249   scoped_ptr<UserManagerModalManagerDelegate> modal_manager_delegate_;
250   base::scoped_nsobject<ReauthDialogWindowController> reauth_window_controller_;
252 - (void)windowWillClose:(NSNotification*)notification;
253 - (void)dealloc;
254 - (id)initWithProfile:(Profile*)profile
255          withObserver:(UserManagerMac*)userManagerObserver;
256 - (void)showURL:(const GURL&)url;
257 - (void)show;
258 - (void)close;
259 - (BOOL)isVisible;
260 - (void)showReauthDialogWithProfile:(Profile*)profile email:(std::string)email;
261 - (void)closeReauthDialog;
262 @end
264 @implementation UserManagerWindowController
266 - (id)initWithProfile:(Profile*)profile
267          withObserver:(UserManagerMac*)userManagerObserver {
268   // Center the window on the screen that currently has focus.
269   NSScreen* mainScreen = [NSScreen mainScreen];
270   CGFloat screenHeight = [mainScreen frame].size.height;
271   CGFloat screenWidth = [mainScreen frame].size.width;
273   NSRect contentRect =
274       NSMakeRect((screenWidth - UserManager::kWindowWidth) / 2,
275                  (screenHeight - UserManager::kWindowHeight) / 2,
276                  UserManager::kWindowWidth, UserManager::kWindowHeight);
277   ChromeEventProcessingWindow* window = [[ChromeEventProcessingWindow alloc]
278       initWithContentRect:contentRect
279                 styleMask:NSTitledWindowMask |
280                           NSClosableWindowMask |
281                           NSResizableWindowMask
282                   backing:NSBackingStoreBuffered
283                     defer:NO
284                    screen:mainScreen];
285   [window setTitle:l10n_util::GetNSString(IDS_PRODUCT_NAME)];
286   [window setMinSize:NSMakeSize(UserManager::kWindowWidth,
287                                 UserManager::kWindowHeight)];
289   if ((self = [super initWithWindow:window])) {
290     userManagerObserver_ = userManagerObserver;
292     // Initialize the web view.
293     webContents_.reset(content::WebContents::Create(
294         content::WebContents::CreateParams(profile)));
295     window.contentView = webContents_->GetNativeView();
296     webContentsDelegate_.reset(new UserManagerWebContentsDelegate());
297     webContents_->SetDelegate(webContentsDelegate_.get());
299     web_modal::WebContentsModalDialogManager::CreateForWebContents(
300         webContents_.get());
301     modal_manager_delegate_.reset(
302         new UserManagerModalManagerDelegate([[self window] contentView]));
303     web_modal::WebContentsModalDialogManager::FromWebContents(
304         webContents_.get())->SetDelegate(modal_manager_delegate_.get());
306     [[NSNotificationCenter defaultCenter]
307         addObserver:self
308            selector:@selector(windowWillClose:)
309                name:NSWindowWillCloseNotification
310              object:self.window];
311   }
312   return self;
315 - (void)dealloc {
316   [[NSNotificationCenter defaultCenter] removeObserver:self];
317   // Remove the ModalDailogManager that's about to be destroyed.
318   auto manager = web_modal::WebContentsModalDialogManager::FromWebContents(
319       webContents_.get());
320   if (manager)
321     manager->SetDelegate(nullptr);
323   [super dealloc];
326 - (void)showURL:(const GURL&)url {
327   webContents_->GetController().LoadURL(url, content::Referrer(),
328                                         ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
329                                         std::string());
330   content::RenderWidgetHostView* rwhv = webContents_->GetRenderWidgetHostView();
331   if (rwhv)
332     rwhv->SetBackgroundColor(profiles::kUserManagerBackgroundColor);
333   [self show];
336 - (void)show {
337   // Because the User Manager isn't a BrowserWindowController, activating it
338   // will not trigger a -windowChangedToProfile and update the menu bar.
339   // This is only important if the active profile is Guest, which may have
340   // happened after locking a profile.
341   if (profiles::SetActiveProfileToGuestIfLocked()) {
342     g_browser_process->profile_manager()->CreateProfileAsync(
343         ProfileManager::GetGuestProfilePath(),
344         base::Bind(&ChangeAppControllerForProfile),
345         base::string16(),
346         base::string16(),
347         std::string());
348   }
349   [[self window] makeKeyAndOrderFront:self];
352 - (void)close {
353   [[self window] close];
356 -(BOOL)isVisible {
357   return [[self window] isVisible];
360 - (void)windowWillClose:(NSNotification*)notification {
361   [[NSNotificationCenter defaultCenter] removeObserver:self];
362   DCHECK(userManagerObserver_);
363   userManagerObserver_->WindowWasClosed();
366 - (void)showReauthDialogWithProfile:(Profile*)profile email:(std::string)email {
367   reauth_window_controller_.reset(
368       [[ReauthDialogWindowController alloc]
369           initWithProfile:profile
370                     email:email
371               webContents:webContents_.get()]);
374 - (void)closeReauthDialog {
375   [reauth_window_controller_ close];
378 @end
381 // static
382 void UserManager::Show(
383     const base::FilePath& profile_path_to_focus,
384     profiles::UserManagerTutorialMode tutorial_mode,
385     profiles::UserManagerProfileSelected profile_open_action) {
386   DCHECK(profile_path_to_focus != ProfileManager::GetGuestProfilePath());
388   ProfileMetrics::LogProfileOpenMethod(ProfileMetrics::OPEN_USER_MANAGER);
389   if (instance_) {
390     // If there's a user manager window open already, just activate it.
391     [instance_->window_controller() show];
392     instance_->set_user_manager_started_showing(base::Time::Now());
393     return;
394   }
396   // Under some startup conditions, we can try twice to create the User Manager.
397   // Because creating the System profile is asynchronous, it's possible for
398   // there to then be multiple pending operations and eventually multiple
399   // User Managers.
400   if (instance_under_construction_)
401       return;
402   instance_under_construction_ = YES;
404   // Create the guest profile, if necessary, and open the User Manager
405   // from the guest profile.
406   profiles::CreateSystemProfileForUserManager(
407       profile_path_to_focus,
408       tutorial_mode,
409       profile_open_action,
410       base::Bind(&UserManagerMac::OnSystemProfileCreated, base::Time::Now()));
413 // static
414 void UserManager::Hide() {
415   if (instance_)
416     [instance_->window_controller() close];
419 // static
420 bool UserManager::IsShowing() {
421   return instance_ ? [instance_->window_controller() isVisible]: false;
424 // static
425 void UserManager::OnUserManagerShown() {
426   if (instance_)
427     instance_->LogTimeToOpen();
430 // static
431 void UserManager::ShowReauthDialog(content::BrowserContext* browser_context,
432                                    const std::string& email) {
433   DCHECK(instance_);
434   instance_->ShowReauthDialog(browser_context, email);
437 void UserManagerMac::ShowReauthDialog(content::BrowserContext* browser_context,
438                                       const std::string& email) {
439   [window_controller_
440       showReauthDialogWithProfile:Profile::FromBrowserContext(browser_context)
441                             email:email];
444 void UserManagerMac::CloseReauthDialog() {
445   [window_controller_ closeReauthDialog];
448 UserManagerMac::UserManagerMac(Profile* profile) {
449   window_controller_.reset([[UserManagerWindowController alloc]
450       initWithProfile:profile withObserver:this]);
453 UserManagerMac::~UserManagerMac() {
456 // static
457 void UserManagerMac::OnSystemProfileCreated(const base::Time& start_time,
458                                             Profile* system_profile,
459                                             const std::string& url) {
460   DCHECK(!instance_);
461   instance_ = new UserManagerMac(system_profile);
462   instance_->set_user_manager_started_showing(start_time);
463   [instance_->window_controller() showURL:GURL(url)];
464   instance_under_construction_ = NO;
467 void UserManagerMac::LogTimeToOpen() {
468   if (user_manager_started_showing_ == base::Time())
469     return;
471   ProfileMetrics::LogTimeToOpenUserManager(
472       base::Time::Now() - user_manager_started_showing_);
473   user_manager_started_showing_ = base::Time();
476 void UserManagerMac::WindowWasClosed() {
477   CloseReauthDialog();
478   instance_ = NULL;
479   delete this;