user_manager_mac.mm revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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/cocoa/profiles/user_manager_mac.h"
6
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_manager.h"
12#include "chrome/browser/profiles/profiles_state.h"
13#include "chrome/browser/ui/browser_dialogs.h"
14#import "chrome/browser/ui/cocoa/browser_window_utils.h"
15#include "chrome/browser/ui/cocoa/chrome_event_processing_window.h"
16#include "chrome/browser/ui/user_manager.h"
17#include "chrome/grit/chromium_strings.h"
18#include "content/public/browser/native_web_keyboard_event.h"
19#include "content/public/browser/web_contents.h"
20#include "content/public/browser/web_contents_delegate.h"
21#include "ui/base/l10n/l10n_util_mac.h"
22#include "ui/events/keycodes/keyboard_codes.h"
23
24
25// An open User Manager window. There can only be one open at a time. This
26// is reset to NULL when the window is closed.
27UserManagerMac* instance_ = NULL;  // Weak.
28
29// Custom WebContentsDelegate that allows handling of hotkeys.
30class UserManagerWebContentsDelegate : public content::WebContentsDelegate {
31 public:
32  UserManagerWebContentsDelegate() {}
33
34  // WebContentsDelegate implementation. Forwards all unhandled keyboard events
35  // to the current window.
36  virtual void HandleKeyboardEvent(
37      content::WebContents* source,
38      const content::NativeWebKeyboardEvent& event) OVERRIDE {
39    if (![BrowserWindowUtils shouldHandleKeyboardEvent:event])
40      return;
41
42    // -getCommandId returns -1 if the event isn't a chrome accelerator.
43    int chromeCommandId = [BrowserWindowUtils getCommandId:event];
44
45    // Check for Cmd+A and Cmd+V events that could come from a password field.
46    bool isTextEditingCommand =
47        (event.modifiers & blink::WebInputEvent::MetaKey) &&
48        (event.windowsKeyCode == ui::VKEY_A ||
49         event.windowsKeyCode == ui::VKEY_V);
50
51    // Only handle close window Chrome accelerators and text editing ones.
52    if (chromeCommandId == IDC_CLOSE_WINDOW || chromeCommandId == IDC_EXIT ||
53        isTextEditingCommand) {
54      [[NSApp mainMenu] performKeyEquivalent:event.os_event];
55    }
56  }
57};
58
59// Window controller for the User Manager view.
60@interface UserManagerWindowController : NSWindowController <NSWindowDelegate> {
61 @private
62  scoped_ptr<content::WebContents> webContents_;
63  scoped_ptr<UserManagerWebContentsDelegate> webContentsDelegate_;
64  UserManagerMac* userManagerObserver_;  // Weak.
65}
66- (void)windowWillClose:(NSNotification*)notification;
67- (void)dealloc;
68- (id)initWithProfile:(Profile*)profile
69         withObserver:(UserManagerMac*)userManagerObserver;
70- (void)showURL:(const GURL&)url;
71- (void)show;
72- (void)close;
73- (BOOL)isVisible;
74@end
75
76@implementation UserManagerWindowController
77
78- (id)initWithProfile:(Profile*)profile
79         withObserver:(UserManagerMac*)userManagerObserver {
80
81  // Center the window on the screen that currently has focus.
82  NSScreen* mainScreen = [NSScreen mainScreen];
83  CGFloat screenHeight = [mainScreen frame].size.height;
84  CGFloat screenWidth = [mainScreen frame].size.width;
85
86  NSRect contentRect =
87      NSMakeRect((screenWidth - UserManager::kWindowWidth) / 2,
88                 (screenHeight - UserManager::kWindowHeight) / 2,
89                 UserManager::kWindowWidth, UserManager::kWindowHeight);
90  ChromeEventProcessingWindow* window = [[ChromeEventProcessingWindow alloc]
91      initWithContentRect:contentRect
92                styleMask:NSTitledWindowMask |
93                          NSClosableWindowMask |
94                          NSResizableWindowMask
95                  backing:NSBackingStoreBuffered
96                    defer:NO
97                   screen:mainScreen];
98  [window setTitle:l10n_util::GetNSString(IDS_PRODUCT_NAME)];
99  [window setMinSize:NSMakeSize(UserManager::kWindowWidth,
100                                UserManager::kWindowHeight)];
101
102  if ((self = [super initWithWindow:window])) {
103    userManagerObserver_ = userManagerObserver;
104
105    // Initialize the web view.
106    webContents_.reset(content::WebContents::Create(
107        content::WebContents::CreateParams(profile)));
108    window.contentView = webContents_->GetNativeView();
109    webContentsDelegate_.reset(new UserManagerWebContentsDelegate());
110    webContents_->SetDelegate(webContentsDelegate_.get());
111    DCHECK(window.contentView);
112
113    [[NSNotificationCenter defaultCenter]
114        addObserver:self
115           selector:@selector(windowWillClose:)
116               name:NSWindowWillCloseNotification
117             object:self.window];
118  }
119  return self;
120}
121
122- (void)dealloc {
123  [[NSNotificationCenter defaultCenter] removeObserver:self];
124  [super dealloc];
125}
126
127- (void)showURL:(const GURL&)url {
128  webContents_->GetController().LoadURL(url, content::Referrer(),
129                                        ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
130                                        std::string());
131  [self show];
132}
133
134- (void)show {
135  // Because the User Manager isn't a BrowserWindowController, activating it
136  // will not trigger a -windowChangedToProfile and update the menu bar.
137  // This is only important if the active profile is Guest, which may have
138  // happened after locking a profile.
139  Profile* guestProfile = profiles::SetActiveProfileToGuestIfLocked();
140  if (guestProfile && guestProfile->IsGuestSession()) {
141    AppController* controller =
142        base::mac::ObjCCast<AppController>([NSApp delegate]);
143    [controller windowChangedToProfile:guestProfile];
144  }
145  [[self window] makeKeyAndOrderFront:self];
146}
147
148- (void)close {
149  [[self window] close];
150}
151
152-(BOOL)isVisible {
153  return [[self window] isVisible];
154}
155
156- (void)windowWillClose:(NSNotification*)notification {
157  [[NSNotificationCenter defaultCenter] removeObserver:self];
158  DCHECK(userManagerObserver_);
159  userManagerObserver_->WindowWasClosed();
160}
161
162@end
163
164
165void UserManager::Show(
166    const base::FilePath& profile_path_to_focus,
167    profiles::UserManagerTutorialMode tutorial_mode,
168    profiles::UserManagerProfileSelected profile_open_action) {
169  ProfileMetrics::LogProfileSwitchUser(ProfileMetrics::OPEN_USER_MANAGER);
170  if (instance_) {
171    // If there's a user manager window open already, just activate it.
172    [instance_->window_controller() show];
173    return;
174  }
175
176  // Create the guest profile, if necessary, and open the User Manager
177  // from the guest profile.
178  profiles::CreateGuestProfileForUserManager(
179      profile_path_to_focus,
180      tutorial_mode,
181      profile_open_action,
182      base::Bind(&UserManagerMac::OnGuestProfileCreated));
183}
184
185void UserManager::Hide() {
186  if (instance_)
187    [instance_->window_controller() close];
188}
189
190bool UserManager::IsShowing() {
191  return instance_ ? [instance_->window_controller() isVisible]: false;
192}
193
194UserManagerMac::UserManagerMac(Profile* profile) {
195  window_controller_.reset([[UserManagerWindowController alloc]
196      initWithProfile:profile withObserver:this]);
197}
198
199UserManagerMac::~UserManagerMac() {
200}
201
202// static
203void UserManagerMac::OnGuestProfileCreated(Profile* guest_profile,
204                                           const std::string& url) {
205  DCHECK(!instance_);
206  instance_ = new UserManagerMac(guest_profile);
207  [instance_->window_controller() showURL:GURL(url)];
208}
209
210void UserManagerMac::WindowWasClosed() {
211  instance_ = NULL;
212  delete this;
213}
214