Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / cocoa / profiles / avatar_base_controller.mm
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 #import "chrome/browser/ui/cocoa/profiles/avatar_base_controller.h"
6
7 #include "base/mac/foundation_util.h"
8 #include "chrome/app/chrome_command_ids.h"
9 #include "chrome/browser/browser_process.h"
10 #include "chrome/browser/profiles/profile_info_cache_observer.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/signin/signin_header_helper.h"
15 #include "chrome/browser/profiles/profiles_state.h"
16 #include "chrome/browser/profiles/profile_window.h"
17 #include "chrome/browser/ui/browser.h"
18 #include "chrome/browser/ui/browser_commands.h"
19 #include "chrome/browser/ui/browser_window.h"
20 #import "chrome/browser/ui/cocoa/base_bubble_controller.h"
21 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
22 #import "chrome/browser/ui/cocoa/profiles/avatar_menu_bubble_controller.h"
23 #import "chrome/browser/ui/cocoa/profiles/profile_chooser_controller.h"
24 #include "components/signin/core/browser/signin_error_controller.h"
25 #include "components/signin/core/common/profile_management_switches.h"
26
27 // Space between the avatar icon and the avatar menu bubble.
28 const CGFloat kMenuYOffsetAdjust = 1.0;
29 // Offset needed to align the edge of the avatar bubble with the edge of the
30 // avatar button.
31 const CGFloat kMenuXOffsetAdjust = 2.0;
32
33 @interface AvatarBaseController (Private)
34 // Shows the avatar bubble.
35 - (IBAction)buttonClicked:(id)sender;
36
37 - (void)bubbleWillClose:(NSNotification*)notif;
38
39 // Updates the profile name displayed by the avatar button. If |layoutParent| is
40 // yes, then the BrowserWindowController is notified to relayout the subviews,
41 // as the button needs to be repositioned.
42 - (void)updateAvatarButtonAndLayoutParent:(BOOL)layoutParent;
43
44 // Displays an error icon if any accounts associated with this profile have an
45 // auth error.
46 - (void)updateErrorStatus:(BOOL)hasError;
47 @end
48
49 class ProfileInfoUpdateObserver : public ProfileInfoCacheObserver,
50                                   public SigninErrorController::Observer {
51  public:
52   ProfileInfoUpdateObserver(Profile* profile,
53                             AvatarBaseController* avatarController)
54       : profile_(profile),
55         avatarController_(avatarController) {
56     g_browser_process->profile_manager()->
57         GetProfileInfoCache().AddObserver(this);
58
59     // Subscribe to authentication error changes so that the avatar button
60     // can update itself.
61     SigninErrorController* errorController =
62         profiles::GetSigninErrorController(profile_);
63     if (errorController)
64       errorController->AddObserver(this);
65   }
66
67   ~ProfileInfoUpdateObserver() override {
68     g_browser_process->profile_manager()->
69         GetProfileInfoCache().RemoveObserver(this);
70     SigninErrorController* errorController =
71         profiles::GetSigninErrorController(profile_);
72     if (errorController)
73       errorController->RemoveObserver(this);
74   }
75
76   // ProfileInfoCacheObserver:
77   void OnProfileAdded(const base::FilePath& profile_path) override {
78     [avatarController_ updateAvatarButtonAndLayoutParent:YES];
79   }
80
81   void OnProfileWasRemoved(const base::FilePath& profile_path,
82                            const base::string16& profile_name) override {
83     // If deleting the active profile, don't bother updating the avatar
84     // button, as the browser window is being closed anyway.
85     if (profile_->GetPath() != profile_path)
86       [avatarController_ updateAvatarButtonAndLayoutParent:YES];
87   }
88
89   void OnProfileNameChanged(const base::FilePath& profile_path,
90                             const base::string16& old_profile_name) override {
91     if (profile_->GetPath() == profile_path)
92       [avatarController_ updateAvatarButtonAndLayoutParent:YES];
93   }
94
95   void OnProfileAvatarChanged(const base::FilePath& profile_path) override {
96     if (profile_->GetPath() == profile_path)
97       [avatarController_ updateAvatarButtonAndLayoutParent:YES];
98   }
99
100   void OnProfileSupervisedUserIdChanged(
101       const base::FilePath& profile_path) override {
102     if (profile_->GetPath() == profile_path)
103       [avatarController_ updateAvatarButtonAndLayoutParent:YES];
104   }
105
106   // SigninErrorController::Observer:
107   void OnErrorChanged() override {
108     SigninErrorController* errorController =
109         profiles::GetSigninErrorController(profile_);
110     if (errorController)
111       [avatarController_ updateErrorStatus:errorController->HasError()];
112   }
113
114  private:
115   Profile* profile_;
116   AvatarBaseController* avatarController_;  // Weak; owns this.
117
118   DISALLOW_COPY_AND_ASSIGN(ProfileInfoUpdateObserver);
119 };
120
121 @implementation AvatarBaseController
122
123 - (id)initWithBrowser:(Browser*)browser {
124   if ((self = [super init])) {
125     browser_ = browser;
126     profileInfoObserver_.reset(
127         new ProfileInfoUpdateObserver(browser_->profile(), self));
128   }
129   return self;
130 }
131
132 - (void)dealloc {
133   [[NSNotificationCenter defaultCenter]
134       removeObserver:self
135                 name:NSWindowWillCloseNotification
136               object:[menuController_ window]];
137   [super dealloc];
138 }
139
140 - (NSButton*)buttonView {
141   CHECK(button_.get());  // Subclasses must set this.
142   return button_.get();
143 }
144
145 - (void)showAvatarBubbleAnchoredAt:(NSView*)anchor
146                           withMode:(BrowserWindow::AvatarBubbleMode)mode
147                    withServiceType:(signin::GAIAServiceType)serviceType {
148   if (menuController_) {
149     if (switches::IsNewAvatarMenu()) {
150       profiles::BubbleViewMode viewMode;
151       profiles::TutorialMode tutorialMode;
152       profiles::BubbleViewModeFromAvatarBubbleMode(
153           mode, &viewMode, &tutorialMode);
154       if (tutorialMode != profiles::TUTORIAL_MODE_NONE) {
155         ProfileChooserController* profileChooserController =
156             base::mac::ObjCCastStrict<ProfileChooserController>(
157                 menuController_);
158         [profileChooserController setTutorialMode:tutorialMode];
159         [profileChooserController initMenuContentsWithView:viewMode];
160       }
161     }
162     return;
163   }
164
165   DCHECK(chrome::IsCommandEnabled(browser_, IDC_SHOW_AVATAR_MENU));
166
167   NSWindowController* wc =
168       [browser_->window()->GetNativeWindow() windowController];
169   if ([wc isKindOfClass:[BrowserWindowController class]]) {
170     [static_cast<BrowserWindowController*>(wc)
171         lockBarVisibilityForOwner:self withAnimation:NO delay:NO];
172   }
173
174   // The new avatar bubble does not have an arrow, and it should be anchored
175   // to the edge of the avatar button.
176   int anchorX = switches::IsNewAvatarMenu() ?
177       NSMaxX([anchor bounds]) - kMenuXOffsetAdjust :
178       NSMidX([anchor bounds]);
179   NSPoint point = NSMakePoint(anchorX,
180                               NSMaxY([anchor bounds]) - kMenuYOffsetAdjust);
181   point = [anchor convertPoint:point toView:nil];
182   point = [[anchor window] convertBaseToScreen:point];
183
184   // |menuController_| will automatically release itself on close.
185   if (switches::IsNewAvatarMenu()) {
186     profiles::BubbleViewMode viewMode;
187     profiles::TutorialMode tutorialMode;
188     profiles::BubbleViewModeFromAvatarBubbleMode(
189         mode, &viewMode, &tutorialMode);
190     menuController_ =
191         [[ProfileChooserController alloc] initWithBrowser:browser_
192                                                anchoredAt:point
193                                                  viewMode:viewMode
194                                              tutorialMode:tutorialMode
195                                               serviceType:serviceType];
196   } else {
197     menuController_ =
198       [[AvatarMenuBubbleController alloc] initWithBrowser:browser_
199                                                anchoredAt:point];
200   }
201
202   [[NSNotificationCenter defaultCenter]
203       addObserver:self
204          selector:@selector(bubbleWillClose:)
205              name:NSWindowWillCloseNotification
206            object:[menuController_ window]];
207   [menuController_ showWindow:self];
208
209   ProfileMetrics::LogProfileOpenMethod(ProfileMetrics::ICON_AVATAR_BUBBLE);
210 }
211
212 - (IBAction)buttonClicked:(id)sender {
213   [self showAvatarBubbleAnchoredAt:button_
214                           withMode:BrowserWindow::AVATAR_BUBBLE_MODE_DEFAULT
215                    withServiceType:signin::GAIA_SERVICE_TYPE_NONE];
216 }
217
218 - (void)bubbleWillClose:(NSNotification*)notif {
219   NSWindowController* wc =
220       [browser_->window()->GetNativeWindow() windowController];
221   if ([wc isKindOfClass:[BrowserWindowController class]]) {
222     [static_cast<BrowserWindowController*>(wc)
223         releaseBarVisibilityForOwner:self withAnimation:YES delay:NO];
224   }
225   menuController_ = nil;
226 }
227
228 - (void)updateAvatarButtonAndLayoutParent:(BOOL)layoutParent {
229   NOTREACHED();
230 }
231
232 - (void)updateErrorStatus:(BOOL)hasError {
233 }
234
235 - (BaseBubbleController*)menuController {
236   return menuController_;
237 }
238
239 @end