// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #import "chrome/browser/ui/cocoa/passwords/account_chooser_view_controller.h" #include "base/mac/foundation_util.h" #include "base/mac/scoped_nsobject.h" #include "base/strings/sys_string_conversions.h" #include "base/strings/utf_string_conversions.h" #import "chrome/browser/ui/cocoa/passwords/account_avatar_fetcher_manager.h" #import "chrome/browser/ui/cocoa/passwords/credential_item_button.h" #import "chrome/browser/ui/cocoa/passwords/passwords_bubble_utils.h" #include "chrome/browser/ui/passwords/manage_passwords_view_utils.h" #include "chrome/browser/ui/passwords/password_dialog_controller.h" #include "chrome/browser/ui/passwords/password_dialog_prompts.h" #include "chrome/grit/generated_resources.h" #include "components/autofill/core/common/password_form.h" #include "components/password_manager/core/common/credential_manager_types.h" #include "grit/theme_resources.h" #include "skia/ext/skia_utils_mac.h" #include "ui/base/cocoa/controls/hyperlink_text_view.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" #include "ui/strings/grit/ui_strings.h" @interface AccountChooserViewController () { NSButton* cancelButton_; // Weak. NSTextView* titleView_; // Weak. base::scoped_nsobject credentialButtons_; base::scoped_nsobject avatarManager_; } - (void)onCancelClicked:(id)sender; - (void)onCredentialClicked:(id)sender; - (void)loadCredentialItems; @end @implementation AccountChooserViewController @synthesize bridge = bridge_; - (instancetype)initWithBridge:(PasswordPromptBridgeInterface*)bridge { base::scoped_nsobject avatarManager( [[AccountAvatarFetcherManager alloc] initWithRequestContext:bridge->GetRequestContext()]); return [self initWithBridge:bridge avatarManager:avatarManager]; } - (void)loadView { base::scoped_nsobject view([[NSView alloc] initWithFrame:NSZeroRect]); // ------------------------------------ // | | // | Choose an account etc etc | // | | // | ---- | // | | | credential view | // | ---- | // | | | credential view | // | ---- | // | | // | [ Cancel ] | // ------------------------------------ // Create the views. // Title. std::pair title_text = bridge_->GetDialogController()->GetAccoutChooserTitle(); titleView_ = TitleDialogLabelWithLink(title_text.first, title_text.second, self); // Force the text to wrap to fit in the bubble size. [titleView_ setVerticallyResizable:YES]; const CGFloat width = kDesiredBubbleWidth - 2*kFramePadding; [titleView_ setFrameSize:NSMakeSize(width, MAXFLOAT)]; [titleView_ sizeToFit]; [[titleView_ textContainer] setLineFragmentPadding:0]; [view addSubview:titleView_]; // Credentials list. [self loadCredentialItems]; // "Cancel" button. cancelButton_ = DialogButton(l10n_util::GetNSString(IDS_APP_CANCEL)); [cancelButton_ setTarget:self]; [cancelButton_ setAction:@selector(onCancelClicked:)]; [view addSubview:cancelButton_]; // Lay out the views. [cancelButton_ setFrameOrigin:NSMakePoint( kFramePadding + width - NSWidth([cancelButton_ frame]), kFramePadding)]; CGFloat curY = NSMaxY([cancelButton_ frame]) + 3 * kRelatedControlVerticalSpacing; for (CredentialItemButton* button in credentialButtons_.get()) { [view addSubview:button]; [button setFrameOrigin:NSMakePoint(0, curY)]; curY = NSMaxY([button frame]); } curY += 2 * kRelatedControlVerticalSpacing; [titleView_ setFrameOrigin:NSMakePoint(kFramePadding, curY)]; const CGFloat frameHeight = NSMaxY([titleView_ frame]) + kFramePadding; [view setFrame:NSMakeRect(0, 0, kDesiredBubbleWidth, frameHeight)]; [self setView:view]; } - (BOOL)textView:(NSTextView*)textView clickedOnLink:(id)link atIndex:(NSUInteger)charIndex { if (bridge_ && bridge_->GetDialogController()) bridge_->GetDialogController()->OnSmartLockLinkClicked(); return YES; } - (void)onCancelClicked:(id)sender { if (bridge_) bridge_->PerformClose(); } - (void)onCredentialClicked:(id)sender { CredentialItemButton* button = base::mac::ObjCCastStrict(sender); if (bridge_ && bridge_->GetDialogController()) { bridge_->GetDialogController()->OnChooseCredentials(*button.passwordForm, button.credentialType); } } - (void)loadCredentialItems { base::scoped_nsobject items([[NSMutableArray alloc] init]); PasswordDialogController* controller = self.bridge->GetDialogController(); NSRect rect = NSMakeRect(0, 0, kDesiredBubbleWidth, kAvatarImageSize + 2 * kVerticalAvatarMargin); for (const auto& form : controller->GetLocalForms()) { base::scoped_nsobject item( [[CredentialItemButton alloc] initWithFrame:rect backgroundColor:[NSColor textBackgroundColor] hoverColor:skia::SkColorToSRGBNSColor(kButtonHoverColor)]); [item setPasswordForm:form.get()]; [item setCredentialType:password_manager::CredentialType:: CREDENTIAL_TYPE_PASSWORD]; std::pair labels = GetCredentialLabelsForAccountChooser(*form); if (labels.second.empty()) { [item setTitle:base::SysUTF16ToNSString(labels.first)]; } else { NSString* text = base::SysUTF16ToNSString( labels.first + base::ASCIIToUTF16("\n") + labels.second); NSFont* font = ResourceBundle::GetSharedInstance() .GetFontList(ResourceBundle::SmallFont) .GetPrimaryFont() .GetNativeFont(); NSDictionary* attrsDictionary = [NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName]; base::scoped_nsobject attributed_string( [[NSMutableAttributedString alloc] initWithString:text attributes:attrsDictionary]); [attributed_string beginEditing]; [attributed_string addAttribute:NSForegroundColorAttributeName value:skia::SkColorToSRGBNSColor(kAutoSigninTextColor) range:NSMakeRange(labels.first.size() + 1, labels.second.size())]; [attributed_string endEditing]; [item setAttributedTitle:attributed_string]; } [item setImage:[CredentialItemButton defaultAvatar]]; if (form->icon_url.is_valid()) [avatarManager_ fetchAvatar:form->icon_url forView:item]; [item setTarget:self]; [item setAction:@selector(onCredentialClicked:)]; [items addObject:item]; } credentialButtons_.reset(items.release()); } @end @implementation AccountChooserViewController(Testing) - (instancetype)initWithBridge:(PasswordPromptBridgeInterface*)bridge avatarManager:(AccountAvatarFetcherManager*)avatarManager { DCHECK(bridge); if (self = [super initWithNibName:nil bundle:nil]) { bridge_ = bridge; avatarManager_.reset([avatarManager retain]); } return self; } - (NSButton*)cancelButton { return cancelButton_; } - (NSArray*)credentialButtons { return credentialButtons_; } - (NSTextView*)titleView { return titleView_; } @end