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