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 #import "chrome/browser/ui/cocoa/profiles/avatar_menu_bubble_controller.h"
7 #include "base/mac/bundle_locations.h"
8 #include "base/mac/mac_util.h"
9 #include "base/strings/sys_string_conversions.h"
10 #include "chrome/browser/browser_process.h"
11 #include "chrome/browser/profiles/avatar_menu.h"
12 #include "chrome/browser/profiles/profile_info_cache.h"
13 #include "chrome/browser/profiles/profile_manager.h"
14 #include "chrome/browser/profiles/profile_metrics.h"
15 #include "chrome/browser/ui/browser.h"
16 #include "chrome/browser/ui/browser_window.h"
17 #import "chrome/browser/ui/cocoa/info_bubble_view.h"
18 #import "chrome/browser/ui/cocoa/info_bubble_window.h"
19 #include "grit/generated_resources.h"
20 #include "grit/theme_resources.h"
21 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h"
22 #import "ui/base/cocoa/controls/hyperlink_button_cell.h"
23 #import "ui/base/cocoa/cocoa_base_utils.h"
24 #include "ui/base/l10n/l10n_util_mac.h"
25 #include "ui/base/resource/resource_bundle.h"
26 #include "ui/gfx/image/image.h"
28 @interface AvatarMenuBubbleController (Private)
30 - (NSView*)configureManagedUserInformation:(CGFloat)width;
31 - (NSButton*)configureNewUserButton:(CGFloat)yOffset
32 updateWidthAdjust:(CGFloat*)widthAdjust;
33 - (NSButton*)configureSwitchUserButton:(CGFloat)yOffset
34 updateWidthAdjust:(CGFloat*)widthAdjust;
35 - (AvatarMenuItemController*)initAvatarItem:(int)itemIndex
36 updateWidthAdjust:(CGFloat*)widthAdjust
37 setYOffset:(CGFloat)yOffset;
38 - (void)setWindowFrame:(CGFloat)yOffset widthAdjust:(CGFloat)width;
39 - (void)initMenuContents;
40 - (void)initManagedUserContents;
41 - (void)keyDown:(NSEvent*)theEvent;
42 - (void)moveDown:(id)sender;
43 - (void)moveUp:(id)sender;
44 - (void)insertNewline:(id)sender;
45 - (void)highlightNextItemByDelta:(NSInteger)delta;
46 - (void)highlightItem:(AvatarMenuItemController*)newItem;
51 // Constants taken from the Windows/Views implementation at:
52 // chrome/browser/ui/views/avatar_menu_bubble_view.cc
53 const CGFloat kBubbleMinWidth = 175;
54 const CGFloat kBubbleMaxWidth = 800;
55 const CGFloat kMaxItemTextWidth = 200;
57 // Values derived from the XIB.
58 const CGFloat kVerticalSpacing = 10.0;
59 const CGFloat kLinkSpacing = 15.0;
60 const CGFloat kLabelInset = 49.0;
62 // The offset of the managed user information label and the "switch user" link.
63 const CGFloat kManagedUserSpacing = 26.0;
67 @implementation AvatarMenuBubbleController
69 - (id)initWithBrowser:(Browser*)parentBrowser
70 anchoredAt:(NSPoint)point {
72 // Pass in a NULL observer. Rebuilding while the bubble is open will cause it
73 // to be positioned incorrectly. Since the bubble will be dismissed on losing
74 // key status, it's impossible for the user to edit the information in a
75 // meaningful way such that it would need to be redrawn.
76 AvatarMenu* menu = new AvatarMenu(
77 &g_browser_process->profile_manager()->GetProfileInfoCache(),
81 if ((self = [self initWithMenu:menu
82 parentWindow:parentBrowser->window()->GetNativeWindow()
88 - (IBAction)newProfile:(id)sender {
89 menu_->AddNewProfile(ProfileMetrics::ADD_NEW_USER_ICON);
92 - (IBAction)switchToProfile:(id)sender {
93 // Check the event flags to see if a new window should be crated.
94 bool always_create = ui::WindowOpenDispositionFromNSEvent(
95 [NSApp currentEvent]) == NEW_WINDOW;
96 menu_->SwitchToProfile([sender menuIndex], always_create,
97 ProfileMetrics::SWITCH_PROFILE_ICON);
100 - (IBAction)editProfile:(id)sender {
101 menu_->EditProfile([sender menuIndex]);
104 - (IBAction)switchProfile:(id)sender {
106 [self performLayout];
109 // Private /////////////////////////////////////////////////////////////////////
111 - (id)initWithMenu:(AvatarMenu*)menu
112 parentWindow:(NSWindow*)parent
113 anchoredAt:(NSPoint)point {
114 // Use an arbitrary height because it will reflect the size of the content.
115 NSRect contentRect = NSMakeRect(0, 0, kBubbleMinWidth, 150);
116 // Create an empty window into which content is placed.
117 base::scoped_nsobject<InfoBubbleWindow> window(
118 [[InfoBubbleWindow alloc] initWithContentRect:contentRect
119 styleMask:NSBorderlessWindowMask
120 backing:NSBackingStoreBuffered
122 if ((self = [super initWithWindow:window
124 anchoredAt:point])) {
127 [window accessibilitySetOverrideValue:
128 l10n_util::GetNSString(IDS_PROFILES_BUBBLE_ACCESSIBLE_NAME)
129 forAttribute:NSAccessibilityTitleAttribute];
130 [window accessibilitySetOverrideValue:
131 l10n_util::GetNSString(IDS_PROFILES_BUBBLE_ACCESSIBLE_DESCRIPTION)
132 forAttribute:NSAccessibilityHelpAttribute];
134 [[self bubble] setArrowLocation:info_bubble::kTopRight];
135 [self performLayout];
140 - (AvatarMenuItemController*)initAvatarItem:(int)itemIndex
141 updateWidthAdjust:(CGFloat*)widthAdjust
142 setYOffset:(CGFloat)yOffset {
143 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
144 const AvatarMenu::Item& item = menu_->GetItemAt(itemIndex);
145 // Create the item view controller. Autorelease it because it will be owned
146 // by the |items_| array.
147 AvatarMenuItemController* itemView =
148 [[[AvatarMenuItemController alloc] initWithMenuIndex:itemIndex
149 menuController:self] autorelease];
150 itemView.iconView.image = item.icon.ToNSImage();
152 // Adjust the name field to fit the string. If it overflows, record by how
153 // much the window needs to grow to accomodate the new size of the field.
154 NSTextField* nameField = itemView.nameField;
155 nameField.stringValue = base::SysUTF16ToNSString(item.name);
156 NSSize delta = [GTMUILocalizerAndLayoutTweaker sizeToFitView:nameField];
157 if (NSWidth([nameField frame]) > kMaxItemTextWidth) {
158 delta.width -= (NSWidth([nameField frame]) - kMaxItemTextWidth);
159 NSRect frame = [nameField frame];
160 frame.size.width = kMaxItemTextWidth;
161 [nameField setFrame:frame];
163 *widthAdjust = std::max(*widthAdjust, delta.width);
165 // Repeat for the sync state/email.
166 NSTextField* emailField = itemView.emailField;
167 emailField.stringValue = base::SysUTF16ToNSString(item.sync_state);
168 delta = [GTMUILocalizerAndLayoutTweaker sizeToFitView:emailField];
169 if (NSWidth([emailField frame]) > kMaxItemTextWidth) {
170 delta.width -= (NSWidth([emailField frame]) - kMaxItemTextWidth);
171 NSRect frame = [emailField frame];
172 frame.size.width = kMaxItemTextWidth;
173 [emailField setFrame:frame];
175 *widthAdjust = std::max(*widthAdjust, delta.width);
178 // In the inactive case, hide additional UI.
179 [itemView.activeView setHidden:YES];
180 [itemView.editButton setHidden:YES];
182 // Otherwise, set up the edit button and its three interaction states.
183 itemView.activeView.image =
184 rb.GetImageNamed(IDR_PROFILE_SELECTED).ToNSImage();
187 // Add the item to the content view.
188 [[itemView view] setFrameOrigin:NSMakePoint(0, yOffset)];
190 // Keep track of the view controller.
191 [items_ addObject:itemView];
195 - (void)setWindowFrame:(CGFloat)yOffset widthAdjust:(CGFloat)width {
196 // Set the window frame, clamping the width at a sensible max.
197 NSRect frame = [[self window] frame];
198 // Adjust the origin after we have switched from the managed user menu to the
200 CGFloat newWidth = std::min(kBubbleMinWidth + width, kBubbleMaxWidth);
202 frame.origin.x += frame.size.width - newWidth;
203 frame.origin.y += frame.size.height - yOffset;
205 frame.size.height = yOffset;
206 frame.size.width = newWidth;
207 [[self window] setFrame:frame display:YES];
210 - (void)initMenuContents {
211 NSView* contentView = [[self window] contentView];
213 // |yOffset| is the next position at which to draw in contentView coordinates.
214 // Use a little more vertical spacing because the items have padding built-
215 // into the xib, and this gives a little more space to visually match.
216 CGFloat yOffset = kLinkSpacing;
217 CGFloat widthAdjust = 0;
219 if (menu_->ShouldShowAddNewProfileLink()) {
220 // Since drawing happens bottom-up, start with the "New User" link.
221 NSButton* newButton =
222 [self configureNewUserButton:yOffset updateWidthAdjust:&widthAdjust];
223 [contentView addSubview:newButton];
224 yOffset += NSHeight([newButton frame]) + kVerticalSpacing;
226 NSBox* separator = [self separatorWithFrame:
227 NSMakeRect(10, yOffset, NSWidth([contentView frame]) - 20, 0)];
228 [separator setAutoresizingMask:NSViewWidthSizable];
229 [contentView addSubview:separator];
231 yOffset += NSHeight([separator frame]);
236 // Loop over the profiles in reverse, constructing the menu items.
237 for (int i = menu_->GetNumberOfItems() - 1; i >= 0; --i) {
238 AvatarMenuItemController* itemView = [self initAvatarItem:i
239 updateWidthAdjust:&widthAdjust
241 [contentView addSubview:[itemView view]];
242 yOffset += NSHeight([[itemView view] frame]);
245 yOffset += kVerticalSpacing * 1.5;
246 [self setWindowFrame:yOffset widthAdjust:widthAdjust];
249 - (void)initManagedUserContents {
250 NSView* contentView = [[self window] contentView];
252 // |yOffset| is the next position at which to draw in contentView coordinates.
253 // Use a little more vertical spacing because the items have padding built-
254 // into the xib, and this gives a little more space to visually match.
255 CGFloat yOffset = kLinkSpacing;
256 CGFloat widthAdjust = 0;
258 // Since drawing happens bottom-up, start with the "Switch User" link.
259 NSButton* newButton =
260 [self configureSwitchUserButton:yOffset updateWidthAdjust:&widthAdjust];
261 [contentView addSubview:newButton];
262 yOffset += NSHeight([newButton frame]) + kVerticalSpacing;
264 NSBox* separator = [self separatorWithFrame:
265 NSMakeRect(10, yOffset, NSWidth([contentView frame]) - 20, 0)];
266 [separator setAutoresizingMask:NSViewWidthSizable];
267 [contentView addSubview:separator];
269 yOffset += NSHeight([separator frame]) + kVerticalSpacing;
271 // First init the active profile in order to determine the required width. We
272 // will have to adjust its frame later after adding general information about
274 AvatarMenuItemController* itemView =
275 [self initAvatarItem:menu_->GetActiveProfileIndex()
276 updateWidthAdjust:&widthAdjust
279 // Don't increase the width too much (the total size should be at most
280 // |kBubbleMaxWidth|).
281 widthAdjust = std::min(widthAdjust, kBubbleMaxWidth - kBubbleMinWidth);
282 CGFloat newWidth = kBubbleMinWidth + widthAdjust;
284 // Add general information about managed users.
285 NSView* info = [self configureManagedUserInformation:newWidth];
286 [info setFrameOrigin:NSMakePoint(0, yOffset)];
287 [contentView addSubview:info];
288 yOffset += NSHeight([info frame]) + kVerticalSpacing;
290 separator = [self separatorWithFrame:
291 NSMakeRect(10, yOffset, NSWidth([contentView frame]) - 20, 0)];
292 [separator setAutoresizingMask:NSViewWidthSizable];
293 [contentView addSubview:separator];
295 yOffset += NSHeight([separator frame]);
297 // Now update the frame of the active profile and add it.
298 NSRect frame = [[itemView view] frame];
299 frame.origin.y = yOffset;
300 [[itemView view] setFrame:frame];
301 [contentView addSubview:[itemView view]];
303 yOffset += NSHeight(frame) + kVerticalSpacing * 1.5;
304 [self setWindowFrame:yOffset widthAdjust:widthAdjust];
307 - (void)performLayout {
308 NSView* contentView = [[self window] contentView];
310 // Reset the array of controllers and remove all the views.
311 items_.reset([[NSMutableArray alloc] init]);
312 [contentView setSubviews:[NSArray array]];
314 if (menu_->GetManagedUserInformation().empty() || expanded_)
315 [self initMenuContents];
317 [self initManagedUserContents];
320 - (NSView*)configureManagedUserInformation:(CGFloat)width {
321 base::scoped_nsobject<NSView> container(
322 [[NSView alloc] initWithFrame:NSZeroRect]);
324 // Add the limited user icon on the left side of the information TextView.
325 base::scoped_nsobject<NSImageView> iconView(
326 [[NSImageView alloc] initWithFrame:NSMakeRect(5, 0, 16, 16)]);
327 [iconView setImage:menu_->GetManagedUserIcon().ToNSImage()];
328 [container addSubview:iconView];
331 base::SysUTF16ToNSString(menu_->GetManagedUserInformation());
332 NSDictionary* attributes =
333 @{ NSFontAttributeName : [NSFont labelFontOfSize:12] };
334 base::scoped_nsobject<NSAttributedString> attrString(
335 [[NSAttributedString alloc] initWithString:info attributes:attributes]);
336 base::scoped_nsobject<NSTextView> label(
337 [[NSTextView alloc] initWithFrame:NSMakeRect(
338 kManagedUserSpacing, 0, width - kManagedUserSpacing - 5, 0)]);
339 [[label textStorage] setAttributedString:attrString];
340 [label setHorizontallyResizable:NO];
341 [label setEditable:NO];
343 [container addSubview:label];
344 [container setFrameSize:NSMakeSize(width, NSHeight([label frame]))];
346 // Reposition the limited user icon so that it is on top.
347 [iconView setFrameOrigin:NSMakePoint(5, NSHeight([label frame]) - 16)];
348 return container.autorelease();
351 - (NSButton*)configureNewUserButton:(CGFloat)yOffset
352 updateWidthAdjust:(CGFloat*)widthAdjust {
353 base::scoped_nsobject<NSButton> newButton([[NSButton alloc] initWithFrame:
354 NSMakeRect(kLabelInset, yOffset, kBubbleMinWidth - kLabelInset, 16)]);
355 base::scoped_nsobject<HyperlinkButtonCell> buttonCell(
356 [[HyperlinkButtonCell alloc] initTextCell:
357 l10n_util::GetNSString(IDS_PROFILES_CREATE_NEW_PROFILE_LINK)]);
358 [newButton setCell:buttonCell.get()];
359 [newButton setFont:[NSFont labelFontOfSize:12.0]];
360 [newButton setBezelStyle:NSRegularSquareBezelStyle];
361 [newButton setTarget:self];
362 [newButton setAction:@selector(newProfile:)];
363 NSSize delta = [GTMUILocalizerAndLayoutTweaker sizeToFitView:newButton];
365 *widthAdjust = std::max(*widthAdjust, delta.width);
366 return newButton.autorelease();
369 - (NSButton*)configureSwitchUserButton:(CGFloat)yOffset
370 updateWidthAdjust:(CGFloat*)widthAdjust {
371 base::scoped_nsobject<NSButton> newButton(
372 [[NSButton alloc] initWithFrame:NSMakeRect(
373 kManagedUserSpacing, yOffset, kBubbleMinWidth - kLabelInset, 16)]);
374 base::scoped_nsobject<HyperlinkButtonCell> buttonCell(
375 [[HyperlinkButtonCell alloc] initTextCell:
376 l10n_util::GetNSString(IDS_PROFILES_SWITCH_PROFILE_LINK)]);
377 [newButton setCell:buttonCell.get()];
378 [newButton setFont:[NSFont labelFontOfSize:12.0]];
379 [newButton setBezelStyle:NSRegularSquareBezelStyle];
380 [newButton setTarget:self];
381 [newButton setAction:@selector(switchProfile:)];
382 NSSize delta = [GTMUILocalizerAndLayoutTweaker sizeToFitView:newButton];
384 *widthAdjust = std::max(*widthAdjust, delta.width);
385 return newButton.autorelease();
388 - (NSMutableArray*)items {
392 - (void)keyDown:(NSEvent*)theEvent {
393 [self interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
396 - (void)moveDown:(id)sender {
397 [self highlightNextItemByDelta:-1];
400 - (void)moveUp:(id)sender {
401 [self highlightNextItemByDelta:1];
404 - (void)insertNewline:(id)sender {
405 for (AvatarMenuItemController* item in items_.get()) {
406 if ([item isHighlighted]) {
407 [self switchToProfile:item];
413 - (void)highlightNextItemByDelta:(NSInteger)delta {
414 NSUInteger count = [items_ count];
418 NSInteger old_index = -1;
419 for (NSUInteger i = 0; i < count; ++i) {
420 if ([[items_ objectAtIndex:i] isHighlighted]) {
427 // If nothing is selected then start at the top if we're going down and start
428 // at the bottom if we're going up.
430 new_index = delta < 0 ? (count - 1) : 0;
432 new_index = old_index + delta;
434 // Cap the index. We don't wrap around to match the behavior of Mac menus.
436 std::min(std::max(static_cast<NSInteger>(0), new_index),
437 static_cast<NSInteger>(count - 1));
439 [self highlightItem:[items_ objectAtIndex:new_index]];
442 - (void)highlightItem:(AvatarMenuItemController*)newItem {
443 AvatarMenuItemController* oldItem = nil;
444 for (AvatarMenuItemController* item in items_.get()) {
445 if ([item isHighlighted]) {
451 if (oldItem == newItem)
454 [oldItem setIsHighlighted:NO];
455 [newItem setIsHighlighted:YES];
461 // Menu Item Controller ////////////////////////////////////////////////////////
463 @interface AvatarMenuItemController (Private)
464 - (void)animateFromView:(NSView*)outView toView:(NSView*)inView;
467 @implementation AvatarMenuItemController
469 @synthesize menuIndex = menuIndex_;
470 @synthesize isHighlighted = isHighlighted_;
471 @synthesize iconView = iconView_;
472 @synthesize activeView = activeView_;
473 @synthesize nameField = nameField_;
474 @synthesize emailField = emailField_;
475 @synthesize editButton = editButton_;
477 - (id)initWithMenuIndex:(size_t)menuIndex
478 menuController:(AvatarMenuBubbleController*)controller {
479 if ((self = [super initWithNibName:@"AvatarMenuItem"
480 bundle:base::mac::FrameworkBundle()])) {
481 menuIndex_ = menuIndex;
482 controller_ = controller;
484 [nameField_ setAutoresizingMask:NSViewNotSizable];
485 [[nameField_ cell] setLineBreakMode:NSLineBreakByTruncatingTail];
486 [emailField_ setAutoresizingMask:NSViewNotSizable];
487 [[emailField_ cell] setLineBreakMode:NSLineBreakByTruncatingTail];
493 static_cast<AvatarMenuItemView*>(self.view).viewController = nil;
494 [linkAnimation_ stopAnimation];
495 [linkAnimation_ setDelegate:nil];
499 - (void)awakeFromNib {
500 [GTMUILocalizerAndLayoutTweaker sizeToFitView:self.editButton];
501 self.editButton.hidden = YES;
504 - (IBAction)switchToProfile:(id)sender {
505 [controller_ switchToProfile:self];
508 - (IBAction)editProfile:(id)sender {
509 [controller_ editProfile:self];
512 - (void)highlightForEventType:(NSEventType)type {
515 [controller_ highlightItem:self];
519 [controller_ highlightItem:nil];
527 - (void)setIsHighlighted:(BOOL)isHighlighted {
528 if (isHighlighted_ == isHighlighted)
531 isHighlighted_ = isHighlighted;
532 [[self view] setNeedsDisplay:YES];
534 // Cancel any running animation.
535 if (linkAnimation_.get()) {
536 [NSObject cancelPreviousPerformRequestsWithTarget:linkAnimation_
537 selector:@selector(startAnimation)
541 // Fade the edit link in or out only if this is the active view.
542 if (self.activeView.isHidden)
545 if (isHighlighted_) {
546 [self animateFromView:self.emailField toView:self.editButton];
548 // If the edit button is visible or the animation to make it so is
549 // running, stop the animation and fade it back to the email. If not, then
550 // don't run an animation to prevent flickering.
551 if (!self.editButton.isHidden || [linkAnimation_ isAnimating]) {
552 [linkAnimation_ stopAnimation];
553 linkAnimation_.reset();
554 [self animateFromView:self.editButton toView:self.emailField];
559 - (void)animateFromView:(NSView*)outView toView:(NSView*)inView {
560 const NSTimeInterval kAnimationDuration = 0.175;
562 NSDictionary* outDict = [NSDictionary dictionaryWithObjectsAndKeys:
563 outView, NSViewAnimationTargetKey,
564 NSViewAnimationFadeOutEffect, NSViewAnimationEffectKey,
567 NSDictionary* inDict = [NSDictionary dictionaryWithObjectsAndKeys:
568 inView, NSViewAnimationTargetKey,
569 NSViewAnimationFadeInEffect, NSViewAnimationEffectKey,
573 linkAnimation_.reset([[NSViewAnimation alloc] initWithViewAnimations:
574 [NSArray arrayWithObjects:outDict, inDict, nil]]);
575 [linkAnimation_ setDelegate:self];
576 [linkAnimation_ setDuration:kAnimationDuration];
578 [self willStartAnimation:linkAnimation_];
580 [linkAnimation_ performSelector:@selector(startAnimation)
585 - (void)willStartAnimation:(NSAnimation*)animation {
588 - (void)animationDidEnd:(NSAnimation*)animation {
589 if (animation == linkAnimation_.get())
590 linkAnimation_.reset();
593 - (void)animationDidStop:(NSAnimation*)animation {
594 if (animation == linkAnimation_.get())
595 linkAnimation_.reset();
600 // Profile Switch Button ///////////////////////////////////////////////////////
602 @implementation AvatarMenuItemView
604 @synthesize viewController = viewController_;
606 - (void)awakeFromNib {
607 [self updateTrackingAreas];
610 - (void)updateTrackingAreas {
611 if (trackingArea_.get())
612 [self removeTrackingArea:trackingArea_.get()];
615 [[CrTrackingArea alloc] initWithRect:[self bounds]
616 options:NSTrackingMouseEnteredAndExited |
617 NSTrackingActiveInKeyWindow
620 [self addTrackingArea:trackingArea_.get()];
622 [super updateTrackingAreas];
625 - (void)mouseEntered:(id)sender {
626 [viewController_ highlightForEventType:[[NSApp currentEvent] type]];
627 [self setNeedsDisplay:YES];
630 - (void)mouseExited:(id)sender {
631 [viewController_ highlightForEventType:[[NSApp currentEvent] type]];
632 [self setNeedsDisplay:YES];
635 - (void)mouseUp:(id)sender {
636 [viewController_ switchToProfile:self];
639 - (void)drawRect:(NSRect)dirtyRect {
640 NSColor* backgroundColor = nil;
641 if ([viewController_ isHighlighted]) {
642 backgroundColor = [NSColor colorWithCalibratedRed:223.0/255
647 backgroundColor = [NSColor clearColor];
650 [backgroundColor set];
651 [NSBezierPath fillRect:[self bounds]];
654 // Make sure the element is focusable for accessibility.
655 - (BOOL)canBecomeKeyView {
659 - (BOOL)accessibilityIsIgnored {
663 - (NSArray*)accessibilityAttributeNames {
664 NSMutableArray* attributes =
665 [[super accessibilityAttributeNames] mutableCopy];
666 [attributes addObject:NSAccessibilityTitleAttribute];
667 [attributes addObject:NSAccessibilityEnabledAttribute];
669 return [attributes autorelease];
672 - (NSArray*)accessibilityActionNames {
673 NSArray* parentActions = [super accessibilityActionNames];
674 return [parentActions arrayByAddingObject:NSAccessibilityPressAction];
677 - (id)accessibilityAttributeValue:(NSString*)attribute {
678 if ([attribute isEqual:NSAccessibilityRoleAttribute])
679 return NSAccessibilityButtonRole;
681 if ([attribute isEqual:NSAccessibilityRoleDescriptionAttribute])
682 return NSAccessibilityRoleDescription(NSAccessibilityButtonRole, nil);
684 if ([attribute isEqual:NSAccessibilityTitleAttribute]) {
685 return l10n_util::GetNSStringF(
686 IDS_PROFILES_SWITCH_TO_PROFILE_ACCESSIBLE_NAME,
687 base::SysNSStringToUTF16(self.viewController.nameField.stringValue));
690 if ([attribute isEqual:NSAccessibilityEnabledAttribute])
691 return [NSNumber numberWithBool:YES];
693 return [super accessibilityAttributeValue:attribute];
696 - (void)accessibilityPerformAction:(NSString*)action {
697 if ([action isEqual:NSAccessibilityPressAction]) {
698 [viewController_ switchToProfile:self];
702 [super accessibilityPerformAction:action];
707 ////////////////////////////////////////////////////////////////////////////////
709 @implementation AccessibilityIgnoredImageCell
710 - (BOOL)accessibilityIsIgnored {
715 @implementation AccessibilityIgnoredTextFieldCell
716 - (BOOL)accessibilityIsIgnored {