Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1624)

Unified Diff: chrome/browser/ui/cocoa/browser/profile_chooser_controller.mm

Issue 114143002: [Mac] Account management view for the new avatar bubble. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: nits Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/ui/cocoa/browser/profile_chooser_controller.mm
diff --git a/chrome/browser/ui/cocoa/browser/profile_chooser_controller.mm b/chrome/browser/ui/cocoa/browser/profile_chooser_controller.mm
index b751ecb459f80b4a3e5b1e20c1cfc68e11bf82a6..739f3a69bf05e4bfa5dcafc70c719ef9e13c2e6d 100644
--- a/chrome/browser/ui/cocoa/browser/profile_chooser_controller.mm
+++ b/chrome/browser/ui/cocoa/browser/profile_chooser_controller.mm
@@ -7,13 +7,20 @@
#import "chrome/browser/ui/cocoa/browser/profile_chooser_controller.h"
#include "base/mac/bundle_locations.h"
+#include "base/stl_util.h"
#include "base/strings/sys_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/avatar_menu.h"
+#include "chrome/browser/profiles/avatar_menu_observer.h"
#include "chrome/browser/profiles/profile_info_cache.h"
#include "chrome/browser/profiles/profile_info_util.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/profiles/profile_window.h"
+#include "chrome/browser/signin/profile_oauth2_token_service.h"
+#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
+#include "chrome/browser/signin/signin_manager.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
#include "chrome/browser/signin/signin_promo.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_dialogs.h"
@@ -23,17 +30,22 @@
#import "chrome/browser/ui/cocoa/info_bubble_window.h"
#include "chrome/browser/ui/singleton_tabs.h"
#include "chrome/common/url_constants.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_view.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
+#include "google_apis/gaia/oauth2_token_service.h"
#include "skia/ext/skia_utils_mac.h"
#import "ui/base/cocoa/cocoa_event_utils.h"
+#import "ui/base/cocoa/controls/blue_label_button.h"
#import "ui/base/cocoa/controls/hyperlink_button_cell.h"
#include "ui/base/cocoa/window_size_constants.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/l10n/l10n_util_mac.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/image/image.h"
+#include "ui/gfx/text_elider.h"
namespace {
@@ -49,6 +61,10 @@ const CGFloat kHorizontalSpacing = 20.0;
const CGFloat kTitleFontSize = 15.0;
const CGFloat kTextFontSize = 12.0;
+// Minimum size for embedded sign in pages as defined in Gaia.
+const CGFloat kMinGaiaViewWidth = 320;
+const CGFloat kMinGaiaViewHeight = 440;
+
gfx::Image CreateProfileImage(const gfx::Image& icon, int imageSize) {
return profiles::GetSizedAvatarIconWithBorder(
icon, true /* image is a square */,
@@ -56,22 +72,82 @@ gfx::Image CreateProfileImage(const gfx::Image& icon, int imageSize) {
imageSize + profiles::kAvatarIconPadding);
}
-// Should only be called before the window is shown, as that sets the window
-// position.
+// Updates the window size and position.
void SetWindowSize(NSWindow* window, NSSize size) {
NSRect frame = [window frame];
+ frame.origin.x += frame.size.width - size.width;
+ frame.origin.y += frame.size.height - size.height;
frame.size = size;
[window setFrame:frame display:YES];
}
+NSString* ElideEmail(const std::string& email, CGFloat width) {
+ base::string16 elidedEmail = gfx::ElideEmail(
+ base::UTF8ToUTF16(email),
+ ui::ResourceBundle::GetSharedInstance().GetFontList(
+ ui::ResourceBundle::BaseFont),
+ width);
+ return base::SysUTF16ToNSString(elidedEmail);
+}
+
} // namespace
+// Class that listens to changes to the OAuth2Tokens for the active profile, or
+// changes to the avatar menu model.
+// TODO(noms): The avatar menu model changes can only be triggered by modifying
+// the name or avatar for the active profile, but this is not currently
+// implemented.
+class ActiveProfileObserverBridge : public AvatarMenuObserver,
+ public OAuth2TokenService::Observer {
+ public:
+ explicit ActiveProfileObserverBridge(ProfileChooserController* controller)
+ : controller_(controller) {
+ }
+
+ virtual ~ActiveProfileObserverBridge() {
+ }
+
+ // OAuth2TokenService::Observer:
+ virtual void OnRefreshTokenAvailable(const std::string& account_id) OVERRIDE {
+ // Tokens can only be added by adding an account through the inline flow,
+ // which is started from the account management view. Refresh it to show the
+ // update.
+ BubbleViewMode viewMode = [controller_ viewMode];
+ if (viewMode == ACCOUNT_MANAGEMENT_VIEW ||
+ viewMode == GAIA_SIGNIN_VIEW ||
+ viewMode == GAIA_ADD_ACCOUNT_VIEW) {
+ [controller_ initMenuContentsWithView:ACCOUNT_MANAGEMENT_VIEW];
+ }
+ }
+
+ virtual void OnRefreshTokenRevoked(const std::string& account_id) OVERRIDE {
+ // Tokens can only be removed from the account management view. Refresh it
+ // to show the update.
+ if ([controller_ viewMode] == ACCOUNT_MANAGEMENT_VIEW)
+ [controller_ initMenuContentsWithView:ACCOUNT_MANAGEMENT_VIEW];
+ }
+
+ // AvatarMenuObserver:
+ virtual void OnAvatarMenuChanged(AvatarMenu* avatar_menu) OVERRIDE {
+ // While the bubble is open, the avatar menu can only change from the
+ // profile chooser view by modifying the current profile's photo or name.
+ [controller_ initMenuContentsWithView:PROFILE_CHOOSER_VIEW];
+ }
+
+ private:
+ ProfileChooserController* controller_; // Weak; owns this.
+
+ DISALLOW_COPY_AND_ASSIGN(ActiveProfileObserverBridge);
+};
+
@interface ProfileChooserController (Private)
// Creates the main profile card for the profile |item| at the top of
-// the bubble. |isGuestView| is used to control which links/buttons are
-// displayed.
-- (NSView*)createCurrentProfileView:(const AvatarMenu::Item&)item
- isGuest:(BOOL)isGuestView;
+// the bubble.
+- (NSView*)createCurrentProfileView:(const AvatarMenu::Item&)item;
+
+// Creates the possible links for the main profile card with profile |item|.
+- (NSView*)createCurrentProfileLinksForItem:(const AvatarMenu::Item&)item
+ withXOffset:(CGFloat)xOffset;
// Creates a main profile card for the guest user.
- (NSView*)createGuestProfileView;
@@ -80,10 +156,17 @@ void SetWindowSize(NSWindow* window, NSSize size) {
// switcher in the middle of the bubble.
- (NSButton*)createOtherProfileView:(int)itemIndex;
-// Creates the Guest / Add person / View all persons buttons. |isGuestView| is
-// used to determine the text and functionality of the Guest button.
-- (NSView*)createOptionsViewWithRect:(NSRect)rect
- isGuestView:(BOOL)isGuestView;
+// Creates the Guest / Add person / View all persons buttons.
+- (NSView*)createOptionsViewWithRect:(NSRect)rect;
+
+// Creates the account management view for the active profile.
+- (NSView*)createCurrentProfileAccountsView:(NSRect)rect;
+
+// Creates the list of accounts for the active profile.
+- (NSView*)createAccountsListWithRect:(NSRect)rect;
+
+// Creates the Gaia sign-in/add account view.
+- (NSView*)createGaiaEmbeddedView;
// Creates a generic button with text given by |textResourceId|, an icon
// given by |imageResourceId| and with |action|.
@@ -98,11 +181,20 @@ void SetWindowSize(NSWindow* window, NSSize size) {
frameOrigin:(NSPoint)frameOrigin
action:(SEL)action;
-// Creates all the subviews of the avatar bubble.
-- (void)initMenuContents;
+// Creates an email account button with |title|. If |canBeDeleted| is YES, then
+// the button is clickable and has a remove icon.
+- (NSButton*)makeAccountButtonWithRect:(NSRect)rect
+ title:(const std::string&)title
+ canBeDeleted:(BOOL)canBeDeleted;
+
+- (void)dealloc;
+
@end
@implementation ProfileChooserController
+- (BubbleViewMode) viewMode {
+ return viewMode_;
+}
- (IBAction)addNewProfile:(id)sender {
profiles::CreateAndSwitchToNewProfile(
@@ -138,7 +230,7 @@ void SetWindowSize(NSWindow* window, NSSize size) {
}
- (IBAction)showAccountManagement:(id)sender {
- NOTIMPLEMENTED();
+ [self initMenuContentsWithView:ACCOUNT_MANAGEMENT_VIEW];
}
- (IBAction)lockProfile:(id)sender {
@@ -146,101 +238,155 @@ void SetWindowSize(NSWindow* window, NSSize size) {
}
- (IBAction)showSigninPage:(id)sender {
- GURL page = signin::GetPromoURL(signin::SOURCE_MENU, false);
- chrome::ShowSingletonTab(browser_, page);
+ [self initMenuContentsWithView:GAIA_SIGNIN_VIEW];
}
-- (id)initWithBrowser:(Browser*)browser anchoredAt:(NSPoint)point {
- browser_ = browser;
- // TODO(noms): Add an observer when profile name editing is implemented.
- avatarMenu_.reset(new AvatarMenu(
- &g_browser_process->profile_manager()->GetProfileInfoCache(),
- NULL,
- browser));
- avatarMenu_->RebuildMenu();
+- (IBAction)addAccount:(id)sender {
+ [self initMenuContentsWithView:GAIA_ADD_ACCOUNT_VIEW];
+}
+- (IBAction)removeAccount:(id)sender {
+ DCHECK(!isGuestSession_);
+ DCHECK_GE([sender tag], 0); // Should not be called for the primary account.
+ DCHECK(ContainsKey(currentProfileAccounts_, [sender tag]));
+ std::string account = currentProfileAccounts_[[sender tag]];
+ ProfileOAuth2TokenServiceFactory::GetForProfile(
+ browser_->profile())->RevokeCredentials(account);
+}
+
+- (id)initWithBrowser:(Browser*)browser anchoredAt:(NSPoint)point {
base::scoped_nsobject<InfoBubbleWindow> window([[InfoBubbleWindow alloc]
initWithContentRect:ui::kWindowSizeDeterminedLater
styleMask:NSBorderlessWindowMask
backing:NSBackingStoreBuffered
defer:NO]);
+
if ((self = [super initWithWindow:window
parentWindow:browser->window()->GetNativeWindow()
anchoredAt:point])) {
+ browser_ = browser;
+ viewMode_ = PROFILE_CHOOSER_VIEW;
+ observer_.reset(new ActiveProfileObserverBridge(self));
+
+ avatarMenu_.reset(new AvatarMenu(
+ &g_browser_process->profile_manager()->GetProfileInfoCache(),
+ observer_.get(),
+ browser_));
+ avatarMenu_->RebuildMenu();
+
+ // Guest profiles do not have a token service.
+ isGuestSession_ = browser_->profile()->IsGuestSession();
+ if (!isGuestSession_) {
+ ProfileOAuth2TokenService* oauth2TokenService =
+ ProfileOAuth2TokenServiceFactory::GetForProfile(browser_->profile());
+ DCHECK(oauth2TokenService);
+ oauth2TokenService->AddObserver(observer_.get());
+ }
+
[[self bubble] setArrowLocation:info_bubble::kTopRight];
- [self initMenuContents];
+ [self initMenuContentsWithView:viewMode_];
}
+
return self;
}
-- (void)initMenuContents {
+- (void)dealloc {
+ if (!isGuestSession_) {
+ ProfileOAuth2TokenService* oauth2TokenService =
+ ProfileOAuth2TokenServiceFactory::GetForProfile(browser_->profile());
+ DCHECK(oauth2TokenService);
+ oauth2TokenService->RemoveObserver(observer_.get());
+ }
+ [super dealloc];
+}
+
+- (void)initMenuContentsWithView:(BubbleViewMode)viewToDisplay {
+ viewMode_ = viewToDisplay;
NSView* contentView = [[self window] contentView];
+ [contentView setSubviews:[NSArray array]];
+
+ if (viewMode_ == GAIA_SIGNIN_VIEW || viewMode_ == GAIA_ADD_ACCOUNT_VIEW) {
+ [contentView addSubview:[self createGaiaEmbeddedView]];
+ SetWindowSize([self window],
+ NSMakeSize(kMinGaiaViewWidth, kMinGaiaViewHeight));
+ return;
+ }
- // Separate items into active and other profiles.
NSView* currentProfileView = nil;
base::scoped_nsobject<NSMutableArray> otherProfiles(
[[NSMutableArray alloc] init]);
- BOOL isGuestView = YES;
+ // Separate items into active and other profiles.
for (size_t i = 0; i < avatarMenu_->GetNumberOfItems(); ++i) {
const AvatarMenu::Item& item = avatarMenu_->GetItemAt(i);
if (item.active) {
- currentProfileView = [self createCurrentProfileView:item isGuest:NO];
- // The avatar menu only contains non-guest profiles, so an active profile
- // implies this is not a guest session browser.
- isGuestView = NO;
+ currentProfileView = [self createCurrentProfileView:item];
} else {
[otherProfiles addObject:[self createOtherProfileView:i]];
}
}
if (!currentProfileView) // Guest windows don't have an active profile.
currentProfileView = [self createGuestProfileView];
- CGFloat updatedMenuWidth =
+
+ CGFloat contentsWidth =
std::max(kMinMenuWidth, NSWidth([currentProfileView frame]));
+ CGFloat contentsWidthWithSpacing = contentsWidth + 2 * kHorizontalSpacing;
// |yOffset| is the next position at which to draw in |contentView|
// coordinates.
CGFloat yOffset = kVerticalSpacing;
+ NSRect viewRect = NSMakeRect(kHorizontalSpacing, yOffset, contentsWidth, 0);
// Guest / Add Person / View All Persons buttons.
- NSRect viewRect = NSMakeRect(kHorizontalSpacing, yOffset,
- updatedMenuWidth, 0);
- NSView* optionsView = [self createOptionsViewWithRect:viewRect
- isGuestView:isGuestView];
+ NSView* optionsView = [self createOptionsViewWithRect:viewRect];
[contentView addSubview:optionsView];
yOffset = NSMaxY([optionsView frame]) + kVerticalSpacing;
- NSBox* separator =
- [self separatorWithFrame:NSMakeRect(0, yOffset, updatedMenuWidth, 0)];
+ NSBox* separator = [self separatorWithFrame:NSMakeRect(
+ 0, yOffset, contentsWidthWithSpacing, 0)];
[contentView addSubview:separator];
yOffset = NSMaxY([separator frame]) + kVerticalSpacing;
- // Other profiles switcher.
- for (NSView *otherProfileView in otherProfiles.get()) {
- [otherProfileView setFrameOrigin:NSMakePoint(kHorizontalSpacing, yOffset)];
- [contentView addSubview:otherProfileView];
- yOffset = NSMaxY([otherProfileView frame]) + kSmallVerticalSpacing;
- }
+ if (viewToDisplay == PROFILE_CHOOSER_VIEW) {
+ // Other profiles switcher.
+ for (NSView *otherProfileView in otherProfiles.get()) {
+ [otherProfileView setFrameOrigin:NSMakePoint(kHorizontalSpacing,
+ yOffset)];
+ [contentView addSubview:otherProfileView];
+ yOffset = NSMaxY([otherProfileView frame]) + kSmallVerticalSpacing;
+ }
- // If we displayed other profiles, ensure the spacing between the last item
- // and the active profile card is the same as the spacing between the active
- // profile card and the bottom of the bubble.
- if ([otherProfiles.get() count] > 0)
- yOffset += kSmallVerticalSpacing;
+ // If we displayed other profiles, ensure the spacing between the last item
+ // and the active profile card is the same as the spacing between the active
+ // profile card and the bottom of the bubble.
+ if ([otherProfiles.get() count] > 0)
+ yOffset += kSmallVerticalSpacing;
+ } else if (viewToDisplay == ACCOUNT_MANAGEMENT_VIEW) {
+ // Reuse the same view area as for the option buttons.
+ viewRect.origin.y = yOffset;
+ NSView* currentProfileAccountsView =
+ [self createCurrentProfileAccountsView:viewRect];
+ [contentView addSubview:currentProfileAccountsView];
+ yOffset = NSMaxY([currentProfileAccountsView frame]) + kVerticalSpacing;
+
+ NSBox* accountsSeparator = [self separatorWithFrame:NSMakeRect(
+ 0, yOffset, contentsWidthWithSpacing, 0)];
+ [contentView addSubview:accountsSeparator];
+ yOffset = NSMaxY([accountsSeparator frame]) + kVerticalSpacing;
+ }
// Active profile card.
if (currentProfileView) {
- // Don't need to size this view, as it was done at its creation.
- [currentProfileView setFrameOrigin:NSMakePoint(0, yOffset)];
+ [currentProfileView setFrameOrigin:NSMakePoint(kHorizontalSpacing,
+ yOffset)];
[contentView addSubview:currentProfileView];
yOffset = NSMaxY([currentProfileView frame]) + kVerticalSpacing;
}
- SetWindowSize([self window], NSMakeSize(updatedMenuWidth, yOffset));
+ SetWindowSize([self window], NSMakeSize(contentsWidthWithSpacing, yOffset));
}
-- (NSView*)createCurrentProfileView:(const AvatarMenu::Item&)item
- isGuest:(BOOL)isGuestView {
+- (NSView*)createCurrentProfileView:(const AvatarMenu::Item&)item {
base::scoped_nsobject<NSView> container([[NSView alloc]
initWithFrame:NSZeroRect]);
@@ -249,21 +395,48 @@ void SetWindowSize(NSWindow* window, NSSize size) {
initWithFrame:NSMakeRect(0, 0, kLargeImageSide, kLargeImageSide)]);
[iconView setImage:CreateProfileImage(
item.icon, kLargeImageSide).ToNSImage()];
- // Position the image correctly so that the width of the container can be
- // used to correctly resize the bubble if needed.
- [iconView setFrameOrigin:NSMakePoint(kHorizontalSpacing, 0)];
+ [iconView setFrameOrigin:NSMakePoint(0, 0)];
[container addSubview:iconView];
CGFloat xOffset = NSMaxX([iconView frame]) + kHorizontalSpacing;
+ CGFloat yOffset = kVerticalSpacing;
+ CGFloat maxXLinksContainer = 0;
+ if (!isGuestSession_ && viewMode_ == PROFILE_CHOOSER_VIEW) {
+ NSView* linksContainer =
+ [self createCurrentProfileLinksForItem:item withXOffset:xOffset];
+ [container addSubview:linksContainer];
+ yOffset = NSMaxY([linksContainer frame]);
+ maxXLinksContainer = NSMaxX([linksContainer frame]);
+ }
+
+ // Profile name.
+ base::scoped_nsobject<NSTextField> profileName([[NSTextField alloc]
+ initWithFrame:NSZeroRect]);
+ [profileName setStringValue:base::SysUTF16ToNSString(item.name)];
+ [profileName setFont:[NSFont labelFontOfSize:kTitleFontSize]];
+ [profileName setEditable:NO];
+ [profileName setDrawsBackground:NO];
+ [profileName setBezeled:NO];
+ [profileName setFrameOrigin:NSMakePoint(xOffset, yOffset)];
+ [profileName sizeToFit];
+ [container addSubview:profileName];
+
+ CGFloat maxX = std::max(maxXLinksContainer,
+ NSMaxX([profileName frame]));
+
+ [container setFrameSize:NSMakeSize(maxX, NSHeight([iconView frame]))];
+ return container.autorelease();
+}
+
+- (NSView*)createCurrentProfileLinksForItem:(const AvatarMenu::Item&)item
+ withXOffset:(CGFloat)xOffset {
+ base::scoped_nsobject<NSView> container([[NSView alloc]
+ initWithFrame:NSZeroRect]);
+ CGFloat maxX = 0; // Ensure the container is wide enough for all the links.
CGFloat yOffset;
- // Since the bubble hasn't been sized yet, keep track of the widest
- // link (or profile name), to make sure everything fits.
- CGFloat maxXOfLinksColumn = 0;
// The available links depend on the type of profile that is active.
- if (isGuestView) {
- yOffset = kVerticalSpacing;
- } else if (item.signed_in) {
+ if (item.signed_in) {
yOffset = 0;
// We need to display 2 links instead of 1, so make the padding in between
// the links even smaller to fit.
@@ -282,7 +455,7 @@ void SetWindowSize(NSWindow* window, NSSize size) {
action:@selector(lockProfile:)];
yOffset = NSMaxY([signOutLink frame]) + kLinkSpacing;
- maxXOfLinksColumn = std::max(NSMaxX([manageAccountsLink frame]),
+ maxX = std::max(NSMaxX([manageAccountsLink frame]),
NSMaxX([signOutLink frame]));
[container addSubview:manageAccountsLink];
[container addSubview:signOutLink];
@@ -295,26 +468,12 @@ void SetWindowSize(NSWindow* window, NSSize size) {
frameOrigin:NSMakePoint(xOffset, yOffset)
action:@selector(showSigninPage:)];
yOffset = NSMaxY([signInLink frame]) + kSmallVerticalSpacing;
- maxXOfLinksColumn = NSMaxX([signInLink frame]);
+ maxX = NSMaxX([signInLink frame]);
+
[container addSubview:signInLink];
}
- // Profile name.
- base::scoped_nsobject<NSTextField> profileName([[NSTextField alloc]
- initWithFrame:NSZeroRect]);
- [profileName setStringValue:base::SysUTF16ToNSString(item.name)];
- [profileName setFont:[NSFont labelFontOfSize:kTitleFontSize]];
- [profileName setEditable:NO];
- [profileName setDrawsBackground:NO];
- [profileName setBezeled:NO];
- [profileName setFrameOrigin:NSMakePoint(xOffset, yOffset)];
- [profileName sizeToFit];
- maxXOfLinksColumn = std::max(maxXOfLinksColumn, NSMaxX([profileName frame]));
-
- [container addSubview:profileName];
-
- [container setFrameSize:NSMakeSize(maxXOfLinksColumn + kHorizontalSpacing,
- NSHeight([iconView frame]))];
+ [container setFrameSize:NSMakeSize(maxX, yOffset)];
return container.autorelease();
}
@@ -329,7 +488,7 @@ void SetWindowSize(NSWindow* window, NSSize size) {
guestItem.name = base::SysNSStringToUTF16(
l10n_util::GetNSString(IDS_PROFILES_GUEST_PROFILE_NAME));
- return [self createCurrentProfileView:guestItem isGuest:YES];
+ return [self createCurrentProfileView:guestItem];
}
- (NSButton*)createOtherProfileView:(int)itemIndex {
@@ -352,8 +511,7 @@ void SetWindowSize(NSWindow* window, NSSize size) {
return profileButton.autorelease();
}
-- (NSView*)createOptionsViewWithRect:(NSRect)rect
- isGuestView:(BOOL)isGuestView {
+- (NSView*)createOptionsViewWithRect:(NSRect)rect {
CGFloat yOffset = 0;
base::scoped_nsobject<NSView> container([[NSView alloc] initWithFrame:rect]);
@@ -371,10 +529,10 @@ void SetWindowSize(NSWindow* window, NSSize size) {
action:@selector(addNewProfile:)];
yOffset = NSMaxY([addUserButton frame]) + kSmallVerticalSpacing;
- int guestButtonText = isGuestView ? IDS_PROFILES_EXIT_GUEST_BUTTON :
- IDS_PROFILES_GUEST_BUTTON;
- SEL guestButtonAction = isGuestView ? @selector(exitGuestProfile:) :
- @selector(switchToGuestProfile:);
+ int guestButtonText = isGuestSession_ ? IDS_PROFILES_EXIT_GUEST_BUTTON :
+ IDS_PROFILES_GUEST_BUTTON;
+ SEL guestButtonAction = isGuestSession_ ? @selector(exitGuestProfile:) :
+ @selector(switchToGuestProfile:);
NSButton* guestButton =
[self buttonWithRect:NSMakeRect(0, yOffset, 0, 0)
textResourceId:guestButtonText
@@ -387,6 +545,100 @@ void SetWindowSize(NSWindow* window, NSSize size) {
return container.autorelease();
}
+- (NSView*)createCurrentProfileAccountsView:(NSRect)rect {
+ const CGFloat kBlueButtonHeight = 30;
+ const CGFloat kAccountButtonHeight = 15;
+
+ const AvatarMenu::Item& item =
+ avatarMenu_->GetItemAt(avatarMenu_->GetActiveProfileIndex());
+ DCHECK(item.signed_in);
+
+ base::scoped_nsobject<NSView> container([[NSView alloc] initWithFrame:rect]);
+
+ NSRect viewRect = NSMakeRect(0, 0, rect.size.width, kBlueButtonHeight);
+ base::scoped_nsobject<NSButton> addAccountsButton([[BlueLabelButton alloc]
+ initWithFrame:viewRect]);
+ [addAccountsButton setTitle:l10n_util::GetNSStringFWithFixup(
+ IDS_PROFILES_PROFILE_ADD_ACCOUNT_BUTTON, item.name)];
+ [addAccountsButton setTarget:self];
+ [addAccountsButton setAction:@selector(addAccount:)];
+ [container addSubview:addAccountsButton];
+
+ // Update the height of the email account buttons. This is needed so that the
+ // all the buttons span the entire width of the bubble.
+ viewRect.origin.y = NSMaxY([addAccountsButton frame]) + kVerticalSpacing;
+ viewRect.size.height = kAccountButtonHeight;
+
+ NSView* accountEmails = [self createAccountsListWithRect:viewRect];
+ [container addSubview:accountEmails];
+ [container setFrameSize:NSMakeSize(
+ NSWidth([container frame]), NSMaxY([accountEmails frame]))];
+ return container.autorelease();
+}
+
+- (NSView*)createAccountsListWithRect:(NSRect)rect {
+ base::scoped_nsobject<NSView> container([[NSView alloc] initWithFrame:rect]);
+ currentProfileAccounts_.clear();
+
+ // The primary account should always be listed first. However, the vector
+ // returned by ProfileOAuth2TokenService::GetAccounts() will contain the
+ // primary account too. Ignore it when it appears later.
+ // TODO(rogerta): we still need to further differentiate the primary account
+ // from the others, so more work is likely required here: crbug.com/311124.
+ Profile* profile = browser_->profile();
+
+ // TODO(noms): This code is duplicated by the views implementation. See
+ // crbug.com/331805.
+ std::string primaryAccount =
+ SigninManagerFactory::GetForProfile(profile)->GetAuthenticatedUsername();
+ DCHECK(!primaryAccount.empty());
+ std::vector<std::string> accounts =
+ ProfileOAuth2TokenServiceFactory::GetForProfile(profile)->GetAccounts();
+ DCHECK_EQ(1, std::count_if(accounts.begin(), accounts.end(),
+ std::bind1st(std::equal_to<std::string>(),
+ primaryAccount)));
+ rect.origin.y = 0;
+ for (size_t i = 0; i < accounts.size(); ++i) {
+ // Save the original email address, as the button text could be elided.
+ currentProfileAccounts_[i] = accounts[i];
+ if (primaryAccount != accounts[i]) {
+ NSButton* accountButton = [self makeAccountButtonWithRect:rect
+ title:accounts[i]
+ canBeDeleted:true];
+ [accountButton setTag:i];
+ [container addSubview:accountButton];
+ rect.origin.y = NSMaxY([accountButton frame]) + kSmallVerticalSpacing;
+ }
+ }
+
+ // Add the primary account button. It doesn't need a tag, as it cannot be
+ // removed.
+ NSButton* accountButton = [self makeAccountButtonWithRect:rect
+ title:primaryAccount
+ canBeDeleted:false];
+ [container addSubview:accountButton];
+ [container setFrameSize:NSMakeSize(NSWidth([container frame]),
+ NSMaxY([accountButton frame]))];
+ return container.autorelease();
+}
+
+- (NSView*) createGaiaEmbeddedView {
+ signin::Source source = (viewMode_ == GAIA_SIGNIN_VIEW) ?
+ signin::SOURCE_AVATAR_BUBBLE_SIGN_IN :
+ signin::SOURCE_AVATAR_BUBBLE_ADD_ACCOUNT;
+
+ webContents_.reset(content::WebContents::Create(
+ content::WebContents::CreateParams(browser_->profile())));
+ webContents_->GetController().LoadURL(
+ signin::GetPromoURL(source, false),
+ content::Referrer(),
+ content::PAGE_TRANSITION_AUTO_TOPLEVEL,
+ std::string());
+ NSView* webview = webContents_->GetView()->GetNativeView();
+ [webview setFrameSize:NSMakeSize(kMinGaiaViewWidth, kMinGaiaViewHeight)];
+ return webview;
+}
+
- (NSButton*)buttonWithRect:(NSRect)rect
textResourceId:(int)textResourceId
imageResourceId:(int)imageResourceId
@@ -426,5 +678,24 @@ void SetWindowSize(NSWindow* window, NSSize size) {
return link.autorelease();
}
+- (NSButton*)makeAccountButtonWithRect:(NSRect)rect
+ title:(const std::string&)title
+ canBeDeleted:(BOOL)canBeDeleted {
+ base::scoped_nsobject<NSButton> button([[NSButton alloc] initWithFrame:rect]);
+ [button setTitle:ElideEmail(title, rect.size.width)];
+ [button setAlignment:NSLeftTextAlignment];
+ [button setBordered:NO];
+
+ if (canBeDeleted) {
+ [button setImage:ui::ResourceBundle::GetSharedInstance().
+ GetNativeImageNamed(IDR_CLOSE_1).ToNSImage()];
+ [button setImagePosition:NSImageRight];
+ [button setTarget:self];
+ [button setAction:@selector(removeAccount:)];
+ }
+
+ return button.autorelease();
+}
+
@end

Powered by Google App Engine
This is Rietveld 408576698