1 // Copyright (c) 2012 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/profiles/avatar_menu.h" 6 7 #include "ash/ash_switches.h" 8 #include "base/bind.h" 9 #include "base/i18n/case_conversion.h" 10 #include "base/metrics/field_trial.h" 11 #include "base/strings/utf_string_conversions.h" 12 #include "chrome/browser/browser_process.h" 13 #include "chrome/browser/chrome_notification_types.h" 14 #include "chrome/browser/profiles/avatar_menu_actions.h" 15 #include "chrome/browser/profiles/avatar_menu_observer.h" 16 #include "chrome/browser/profiles/profile_list.h" 17 #include "chrome/browser/profiles/profile_manager.h" 18 #include "chrome/browser/profiles/profile_metrics.h" 19 #include "chrome/browser/profiles/profile_window.h" 20 #include "chrome/browser/profiles/profiles_state.h" 21 #include "chrome/browser/ui/ash/chrome_shell_delegate.h" 22 #include "chrome/browser/ui/browser.h" 23 #include "chrome/browser/ui/browser_dialogs.h" 24 #include "chrome/browser/ui/host_desktop.h" 25 #include "chrome/browser/ui/startup/startup_browser_creator.h" 26 #include "chrome/common/chrome_switches.h" 27 #include "components/signin/core/common/profile_management_switches.h" 28 #include "content/public/browser/browser_thread.h" 29 #include "content/public/browser/notification_service.h" 30 #include "grit/generated_resources.h" 31 #include "grit/theme_resources.h" 32 #include "ui/base/l10n/l10n_util.h" 33 #include "ui/base/resource/resource_bundle.h" 34 35 #if defined(ENABLE_MANAGED_USERS) 36 #include "chrome/browser/supervised_user/supervised_user_service.h" 37 #include "chrome/browser/supervised_user/supervised_user_service_factory.h" 38 #endif 39 40 using content::BrowserThread; 41 42 namespace { 43 44 // Constants for the show profile switcher experiment 45 const char kShowProfileSwitcherFieldTrialName[] = "ShowProfileSwitcher"; 46 const char kAlwaysShowSwitcherGroupName[] = "AlwaysShow"; 47 48 } // namespace 49 50 AvatarMenu::AvatarMenu(ProfileInfoInterface* profile_cache, 51 AvatarMenuObserver* observer, 52 Browser* browser) 53 : profile_list_(ProfileList::Create(profile_cache)), 54 menu_actions_(AvatarMenuActions::Create()), 55 profile_info_(profile_cache), 56 observer_(observer), 57 browser_(browser) { 58 DCHECK(profile_info_); 59 // Don't DCHECK(browser_) so that unit tests can reuse this ctor. 60 61 ActiveBrowserChanged(browser_); 62 63 // Register this as an observer of the info cache. 64 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED, 65 content::NotificationService::AllSources()); 66 } 67 68 AvatarMenu::~AvatarMenu() { 69 } 70 71 AvatarMenu::Item::Item(size_t menu_index, 72 size_t profile_index, 73 const gfx::Image& icon) 74 : icon(icon), 75 active(false), 76 signed_in(false), 77 signin_required(false), 78 menu_index(menu_index), 79 profile_index(profile_index) { 80 } 81 82 AvatarMenu::Item::~Item() { 83 } 84 85 // static 86 bool AvatarMenu::ShouldShowAvatarMenu() { 87 if (base::FieldTrialList::FindFullName(kShowProfileSwitcherFieldTrialName) == 88 kAlwaysShowSwitcherGroupName) { 89 // We should only be in this group when multi-profiles is enabled. 90 DCHECK(profiles::IsMultipleProfilesEnabled()); 91 return true; 92 } 93 94 // TODO: Eliminate this ifdef. Add a delegate interface for the menu which 95 // would also help remove the Browser dependency in AvatarMenuActions 96 // implementations. 97 if (profiles::IsMultipleProfilesEnabled()) { 98 #if defined(OS_CHROMEOS) 99 // On ChromeOS the menu will not be shown. 100 return false; 101 #else 102 return switches::IsNewAvatarMenu() || 103 (g_browser_process->profile_manager() && 104 g_browser_process->profile_manager()->GetNumberOfProfiles() > 1); 105 #endif 106 } 107 return false; 108 } 109 110 bool AvatarMenu::CompareItems(const Item* item1, const Item* item2) { 111 return base::i18n::ToLower(item1->name).compare( 112 base::i18n::ToLower(item2->name)) < 0; 113 } 114 115 void AvatarMenu::SwitchToProfile(size_t index, 116 bool always_create, 117 ProfileMetrics::ProfileOpen metric) { 118 DCHECK(profiles::IsMultipleProfilesEnabled() || 119 index == GetActiveProfileIndex()); 120 const Item& item = GetItemAt(index); 121 122 if (switches::IsNewProfileManagement()) { 123 // Don't open a browser window for signed-out profiles. 124 if (item.signin_required) { 125 chrome::ShowUserManager(item.profile_path); 126 return; 127 } 128 } 129 130 base::FilePath path = 131 profile_info_->GetPathOfProfileAtIndex(item.profile_index); 132 133 chrome::HostDesktopType desktop_type = chrome::GetActiveDesktop(); 134 if (browser_) 135 desktop_type = browser_->host_desktop_type(); 136 137 profiles::SwitchToProfile(path, desktop_type, always_create, 138 profiles::ProfileSwitchingDoneCallback(), 139 metric); 140 } 141 142 void AvatarMenu::AddNewProfile(ProfileMetrics::ProfileAdd type) { 143 menu_actions_->AddNewProfile(type); 144 } 145 146 void AvatarMenu::EditProfile(size_t index) { 147 // Get the index in the profile cache from the menu index. 148 size_t profile_index = profile_list_->GetItemAt(index).profile_index; 149 150 Profile* profile = g_browser_process->profile_manager()->GetProfileByPath( 151 profile_info_->GetPathOfProfileAtIndex(profile_index)); 152 153 menu_actions_->EditProfile(profile, profile_index); 154 } 155 156 void AvatarMenu::RebuildMenu() { 157 profile_list_->RebuildMenu(); 158 } 159 160 size_t AvatarMenu::GetNumberOfItems() const { 161 return profile_list_->GetNumberOfItems(); 162 } 163 164 const AvatarMenu::Item& AvatarMenu::GetItemAt(size_t index) const { 165 return profile_list_->GetItemAt(index); 166 } 167 size_t AvatarMenu::GetActiveProfileIndex() { 168 169 // During singleton profile deletion, this function can be called with no 170 // profiles in the model - crbug.com/102278 . 171 if (profile_list_->GetNumberOfItems() == 0) 172 return 0; 173 174 Profile* active_profile = NULL; 175 if (!browser_) 176 active_profile = ProfileManager::GetLastUsedProfile(); 177 else 178 active_profile = browser_->profile(); 179 180 size_t index = 181 profile_info_->GetIndexOfProfileWithPath(active_profile->GetPath()); 182 183 index = profile_list_->MenuIndexFromProfileIndex(index); 184 DCHECK_LT(index, profile_list_->GetNumberOfItems()); 185 return index; 186 } 187 188 base::string16 AvatarMenu::GetSupervisedUserInformation() const { 189 // |browser_| can be NULL in unit_tests. 190 if (browser_ && browser_->profile()->IsSupervised()) { 191 #if defined(ENABLE_MANAGED_USERS) 192 SupervisedUserService* service = 193 SupervisedUserServiceFactory::GetForProfile(browser_->profile()); 194 base::string16 custodian = 195 base::UTF8ToUTF16(service->GetCustodianEmailAddress()); 196 return l10n_util::GetStringFUTF16(IDS_MANAGED_USER_INFO, custodian); 197 #endif 198 } 199 return base::string16(); 200 } 201 202 const gfx::Image& AvatarMenu::GetSupervisedUserIcon() const { 203 return ResourceBundle::GetSharedInstance().GetNativeImageNamed( 204 IDR_MANAGED_USER_ICON); 205 } 206 207 void AvatarMenu::ActiveBrowserChanged(Browser* browser) { 208 browser_ = browser; 209 menu_actions_->ActiveBrowserChanged(browser); 210 211 // If browser is not NULL, get the path of its active profile. 212 base::FilePath path; 213 if (browser) 214 path = browser->profile()->GetPath(); 215 profile_list_->ActiveProfilePathChanged(path); 216 } 217 218 bool AvatarMenu::ShouldShowAddNewProfileLink() const { 219 return menu_actions_->ShouldShowAddNewProfileLink(); 220 } 221 222 bool AvatarMenu::ShouldShowEditProfileLink() const { 223 return menu_actions_->ShouldShowEditProfileLink(); 224 } 225 226 void AvatarMenu::Observe(int type, 227 const content::NotificationSource& source, 228 const content::NotificationDetails& details) { 229 DCHECK_EQ(chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED, type); 230 RebuildMenu(); 231 if (observer_) 232 observer_->OnAvatarMenuChanged(this); 233 } 234