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
41using content::BrowserThread;
42
43namespace {
44
45// Constants for the show profile switcher experiment
46const char kShowProfileSwitcherFieldTrialName[] = "ShowProfileSwitcher";
47const char kAlwaysShowSwitcherGroupName[] = "AlwaysShow";
48
49}  // namespace
50
51AvatarMenu::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
81AvatarMenu::~AvatarMenu() {
82}
83
84AvatarMenu::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
95AvatarMenu::Item::~Item() {
96}
97
98// static
99bool 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
123bool 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
128void 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
157void AvatarMenu::AddNewProfile(ProfileMetrics::ProfileAdd type) {
158  menu_actions_->AddNewProfile(type);
159}
160
161void 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
171void AvatarMenu::RebuildMenu() {
172  profile_list_->RebuildMenu();
173}
174
175size_t AvatarMenu::GetNumberOfItems() const {
176  return profile_list_->GetNumberOfItems();
177}
178
179const AvatarMenu::Item& AvatarMenu::GetItemAt(size_t index) const {
180  return profile_list_->GetItemAt(index);
181}
182size_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
203base::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
217const gfx::Image& AvatarMenu::GetSupervisedUserIcon() const {
218  return ResourceBundle::GetSharedInstance().GetNativeImageNamed(
219      IDR_SUPERVISED_USER_ICON);
220}
221
222void 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
233bool AvatarMenu::ShouldShowAddNewProfileLink() const {
234  return menu_actions_->ShouldShowAddNewProfileLink();
235}
236
237bool AvatarMenu::ShouldShowEditProfileLink() const {
238  return menu_actions_->ShouldShowEditProfileLink();
239}
240
241void 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)
251void AvatarMenu::OnCustodianInfoChanged() {
252  RebuildMenu();
253  if (observer_)
254    observer_->OnAvatarMenuChanged(this);
255}
256#endif
257