blob: 99f9d89a2cc87c2e0628a13ba585e73abe7c8b76 [file] [log] [blame]
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001// 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_model.h"
6
7#include "base/bind.h"
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +01008#include "base/command_line.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00009#include "base/metrics/field_trial.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000010#include "base/stl_util.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000011#include "base/strings/string_number_conversions.h"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010012#include "base/strings/utf_string_conversions.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000013#include "chrome/browser/browser_process.h"
Ben Murdoch7dbb3d52013-07-17 14:55:54 +010014#include "chrome/browser/chrome_notification_types.h"
Ben Murdochca12bfa2013-07-23 11:17:05 +010015#include "chrome/browser/prefs/incognito_mode_prefs.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000016#include "chrome/browser/profiles/avatar_menu_model_observer.h"
17#include "chrome/browser/profiles/profile.h"
18#include "chrome/browser/profiles/profile_info_cache.h"
19#include "chrome/browser/profiles/profile_info_util.h"
20#include "chrome/browser/profiles/profile_manager.h"
21#include "chrome/browser/profiles/profile_metrics.h"
Ben Murdoch9ab55632013-07-18 11:57:30 +010022#include "chrome/browser/profiles/profile_window.h"
23#include "chrome/browser/profiles/profiles_state.h"
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +010024#include "chrome/browser/signin/signin_promo.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000025#include "chrome/browser/ui/browser.h"
26#include "chrome/browser/ui/browser_list.h"
27#include "chrome/browser/ui/browser_window.h"
28#include "chrome/browser/ui/chrome_pages.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000029#include "chrome/browser/ui/host_desktop.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000030#include "chrome/browser/ui/startup/startup_browser_creator.h"
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010031#include "chrome/common/chrome_switches.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000032#include "chrome/common/url_constants.h"
33#include "content/public/browser/browser_thread.h"
34#include "content/public/browser/notification_service.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010035#include "content/public/browser/site_instance.h"
36#include "google_apis/gaia/gaia_urls.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000037#include "grit/generated_resources.h"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010038#include "grit/theme_resources.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000039#include "ui/base/l10n/l10n_util.h"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010040#include "ui/base/resource/resource_bundle.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000041
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000042#if defined(ENABLE_MANAGED_USERS)
43#include "chrome/browser/managed_mode/managed_user_service.h"
44#include "chrome/browser/managed_mode/managed_user_service_factory.h"
45#endif
46
Torne (Richard Coles)58218062012-11-14 11:43:16 +000047using content::BrowserThread;
48
49namespace {
50
51void OnProfileCreated(bool always_create,
52 chrome::HostDesktopType desktop_type,
53 Profile* profile,
54 Profile::CreateStatus status) {
55 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
56
57 if (status == Profile::CREATE_STATUS_INITIALIZED) {
Ben Murdoch9ab55632013-07-18 11:57:30 +010058 profiles::FindOrCreateNewWindowForProfile(
Torne (Richard Coles)58218062012-11-14 11:43:16 +000059 profile,
60 chrome::startup::IS_NOT_PROCESS_STARTUP,
61 chrome::startup::IS_NOT_FIRST_RUN,
62 desktop_type,
63 always_create);
64 }
65}
66
Ben Murdochca12bfa2013-07-23 11:17:05 +010067void OnGuestProfileCreated(bool always_create,
68 chrome::HostDesktopType desktop_type,
69 Profile* profile,
70 Profile::CreateStatus status) {
71 IncognitoModePrefs::SetAvailability(
72 profile->GetPrefs(),
73 IncognitoModePrefs::FORCED);
74 OnProfileCreated(always_create, desktop_type, profile, status);
75}
76
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000077// Constants for the show profile switcher experiment
78const char kShowProfileSwitcherFieldTrialName[] = "ShowProfileSwitcher";
79const char kAlwaysShowSwitcherGroupName[] = "AlwaysShow";
80
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010081
82class SignoutTracker : public content::WebContentsObserver {
83 public:
84 SignoutTracker(Profile* profile, const GURL& signout_landing_url,
85 content::WebContents* contents);
86
87 virtual void WebContentsDestroyed(content::WebContents* contents) OVERRIDE;
88 virtual void DidStopLoading(content::RenderViewHost* render_view_host)
89 OVERRIDE;
90
91 private:
92 scoped_ptr<content::WebContents> contents_;
93 GURL signout_landing_url_;
94 Profile* profile_;
95
96 DISALLOW_COPY_AND_ASSIGN(SignoutTracker);
97};
98
99
100SignoutTracker::SignoutTracker(Profile* profile,
101 const GURL& signout_landing_url,
102 content::WebContents* contents)
103 : WebContentsObserver(contents),
104 contents_(contents),
105 signout_landing_url_(signout_landing_url),
106 profile_(profile) {
107}
108
109void SignoutTracker::DidStopLoading(content::RenderViewHost* render_view_host) {
110 // Only close when we reach the final landing; ignore redirects until then.
111 if (web_contents()->GetURL() == signout_landing_url_) {
112 Observe(NULL);
113 BrowserList::CloseAllBrowsersWithProfile(profile_);
114 delete this; /* success */
115 }
116}
117
118void SignoutTracker::WebContentsDestroyed(content::WebContents* contents) {
119 delete this; /* failure */
120}
121
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000122} // namespace
123
124AvatarMenuModel::AvatarMenuModel(ProfileInfoInterface* profile_cache,
125 AvatarMenuModelObserver* observer,
126 Browser* browser)
127 : profile_info_(profile_cache),
128 observer_(observer),
129 browser_(browser) {
130 DCHECK(profile_info_);
131 // Don't DCHECK(browser_) so that unit tests can reuse this ctor.
132
133 // Register this as an observer of the info cache.
134 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED,
135 content::NotificationService::AllSources());
136
137 // Build the initial menu.
138 RebuildMenu();
139}
140
141AvatarMenuModel::~AvatarMenuModel() {
142 ClearMenu();
143}
144
145AvatarMenuModel::Item::Item(size_t model_index, const gfx::Image& icon)
146 : icon(icon),
147 active(false),
148 signed_in(false),
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100149 signin_required(false),
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000150 model_index(model_index) {
151}
152
153AvatarMenuModel::Item::~Item() {
154}
155
156void AvatarMenuModel::SwitchToProfile(size_t index, bool always_create) {
Ben Murdoch9ab55632013-07-18 11:57:30 +0100157 DCHECK(profiles::IsMultipleProfilesEnabled() ||
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000158 index == GetActiveProfileIndex());
159 const Item& item = GetItemAt(index);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000160 base::FilePath path =
161 profile_info_->GetPathOfProfileAtIndex(item.model_index);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000162
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000163 chrome::HostDesktopType desktop_type = chrome::GetActiveDesktop();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000164 if (browser_)
165 desktop_type = browser_->host_desktop_type();
166
Ben Murdochca12bfa2013-07-23 11:17:05 +0100167 profiles::SwitchToProfile(path, desktop_type, always_create);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000168 ProfileMetrics::LogProfileSwitchUser(ProfileMetrics::SWITCH_PROFILE_ICON);
169}
170
171void AvatarMenuModel::EditProfile(size_t index) {
172 Browser* browser = browser_;
173 if (!browser) {
174 Profile* profile = g_browser_process->profile_manager()->GetProfileByPath(
175 profile_info_->GetPathOfProfileAtIndex(GetItemAt(index).model_index));
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000176 browser = new Browser(Browser::CreateParams(profile,
177 chrome::GetActiveDesktop()));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000178 }
179 std::string page = chrome::kManageProfileSubPage;
180 page += "#";
181 page += base::IntToString(static_cast<int>(index));
182 chrome::ShowSettingsSubPage(browser, page);
183}
184
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000185void AvatarMenuModel::AddNewProfile(ProfileMetrics::ProfileAdd type) {
186 Browser* browser = browser_;
187 if (!browser) {
188 const Browser::CreateParams params(ProfileManager::GetLastUsedProfile(),
189 chrome::GetActiveDesktop());
190 browser = new Browser(params);
191 }
192 chrome::ShowSettingsSubPage(browser, chrome::kCreateProfileSubPage);
193 ProfileMetrics::LogProfileAddNewUser(type);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000194}
195
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100196base::FilePath AvatarMenuModel::GetProfilePath(size_t index) {
197 const Item& item = GetItemAt(index);
198 return profile_info_->GetPathOfProfileAtIndex(item.model_index);
199}
200
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100201// static
202void AvatarMenuModel::SwitchToGuestProfileWindow(Browser* browser) {
203 ProfileManager* profile_manager = g_browser_process->profile_manager();
204 profile_manager->CreateProfileAsync(ProfileManager::GetGuestProfilePath(),
Ben Murdochca12bfa2013-07-23 11:17:05 +0100205 base::Bind(&OnGuestProfileCreated,
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100206 false,
207 browser->host_desktop_type()),
208 string16(),
209 string16(),
Ben Murdoch2385ea32013-08-06 11:01:04 +0100210 std::string());
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100211}
212
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000213size_t AvatarMenuModel::GetNumberOfItems() {
214 return items_.size();
215}
216
217size_t AvatarMenuModel::GetActiveProfileIndex() {
218 // During singleton profile deletion, this function can be called with no
219 // profiles in the model - crbug.com/102278 .
220 if (items_.size() == 0)
221 return 0;
222
223 Profile* active_profile = NULL;
224 if (!browser_)
225 active_profile = ProfileManager::GetLastUsedProfile();
226 else
227 active_profile = browser_->profile();
228
229 size_t index =
230 profile_info_->GetIndexOfProfileWithPath(active_profile->GetPath());
231
232 DCHECK_LT(index, items_.size());
233 return index;
234}
235
236const AvatarMenuModel::Item& AvatarMenuModel::GetItemAt(size_t index) {
237 DCHECK_LT(index, items_.size());
238 return *items_[index];
239}
240
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000241bool AvatarMenuModel::ShouldShowAddNewProfileLink() const {
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100242 // |browser_| can be NULL in unit_tests.
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100243 return !browser_ || !browser_->profile()->IsManaged();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000244}
245
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100246base::string16 AvatarMenuModel::GetManagedUserInformation() const {
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100247 // |browser_| can be NULL in unit_tests.
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100248 if (browser_ && browser_->profile()->IsManaged()) {
249#if defined(ENABLE_MANAGED_USERS)
250 ManagedUserService* service = ManagedUserServiceFactory::GetForProfile(
251 browser_->profile());
Ben Murdochbb1529c2013-08-08 10:24:53 +0100252 base::string16 custodian = UTF8ToUTF16(service->GetCustodianEmailAddress());
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100253 return l10n_util::GetStringFUTF16(IDS_MANAGED_USER_INFO, custodian);
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100254#endif
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100255 }
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100256 return base::string16();
257}
258
259const gfx::Image& AvatarMenuModel::GetManagedUserIcon() const {
260 return ResourceBundle::GetSharedInstance().GetNativeImageNamed(
261 IDR_MANAGED_USER_ICON);
262}
263
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000264void AvatarMenuModel::Observe(int type,
265 const content::NotificationSource& source,
266 const content::NotificationDetails& details) {
267 DCHECK_EQ(chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED, type);
268 RebuildMenu();
269 if (observer_)
270 observer_->OnAvatarMenuModelChanged(this);
271}
272
273// static
274bool AvatarMenuModel::ShouldShowAvatarMenu() {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000275#if defined(OS_CHROMEOS)
276 // On Chrome OS we use different UI for multi-profiles.
277 return false;
278#endif
279
280 if (base::FieldTrialList::FindFullName(kShowProfileSwitcherFieldTrialName) ==
281 kAlwaysShowSwitcherGroupName) {
282 // We should only be in this group when multi-profiles is enabled.
Ben Murdoch9ab55632013-07-18 11:57:30 +0100283 DCHECK(profiles::IsMultipleProfilesEnabled());
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000284 return true;
285 }
Ben Murdoch9ab55632013-07-18 11:57:30 +0100286 if (profiles::IsMultipleProfilesEnabled()) {
287 return profiles::IsNewProfileManagementEnabled() ||
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100288 (g_browser_process->profile_manager() &&
289 g_browser_process->profile_manager()->GetNumberOfProfiles() > 1);
290 }
291 return false;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000292}
293
294void AvatarMenuModel::RebuildMenu() {
295 ClearMenu();
296
297 const size_t count = profile_info_->GetNumberOfProfiles();
298 for (size_t i = 0; i < count; ++i) {
299 bool is_gaia_picture =
300 profile_info_->IsUsingGAIAPictureOfProfileAtIndex(i) &&
301 profile_info_->GetGAIAPictureOfProfileAtIndex(i);
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100302
303 gfx::Image icon = profile_info_->GetAvatarIconOfProfileAtIndex(i);
304 if (!CommandLine::ForCurrentProcess()->HasSwitch(
305 switches::kNewProfileManagement)) {
306 // old avatar menu uses resized-small images
307 icon = profiles::GetAvatarIconForMenu(icon, is_gaia_picture);
308 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000309
310 Item* item = new Item(i, icon);
311 item->name = profile_info_->GetNameOfProfileAtIndex(i);
312 item->sync_state = profile_info_->GetUserNameOfProfileAtIndex(i);
313 item->signed_in = !item->sync_state.empty();
314 if (!item->signed_in) {
315 item->sync_state = l10n_util::GetStringUTF16(
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100316 profile_info_->ProfileIsManagedAtIndex(i) ?
317 IDS_MANAGED_USER_AVATAR_LABEL : IDS_PROFILES_LOCAL_PROFILE_STATE);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000318 }
319 if (browser_) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000320 base::FilePath path = profile_info_->GetPathOfProfileAtIndex(i);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000321 item->active = browser_->profile()->GetPath() == path;
322 }
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100323 item->signin_required = profile_info_->ProfileIsSigninRequiredAtIndex(i);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000324 items_.push_back(item);
325 }
326}
327
328void AvatarMenuModel::ClearMenu() {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000329 STLDeleteElements(&items_);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000330}
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100331
332
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100333content::WebContents* AvatarMenuModel::BeginSignOut() {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100334 ProfileManager* profile_manager = g_browser_process->profile_manager();
335 Profile* current_profile = browser_->profile();
336
337 ProfileInfoCache& cache = profile_manager->GetProfileInfoCache();
338 size_t index = cache.GetIndexOfProfileWithPath(current_profile->GetPath());
339 cache.SetProfileSigninRequiredAtIndex(index, true);
340
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100341 std::string landing_url = signin::GetLandingURL("close", 1).spec();
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100342 GURL logout_url(GaiaUrls::GetInstance()->service_logout_url() +
343 "?continue=" + landing_url);
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100344 if (!logout_override_.empty()) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100345 // We're testing...
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100346 landing_url = logout_override_;
347 logout_url = GURL(logout_override_);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100348 }
349
350 content::WebContents::CreateParams create_params(current_profile);
351 create_params.site_instance =
352 content::SiteInstance::CreateForURL(current_profile, logout_url);
353 content::WebContents* contents = content::WebContents::Create(create_params);
354 contents->GetController().LoadURL(
355 logout_url, content::Referrer(),
356 content::PAGE_TRANSITION_GENERATED, std::string());
357
358 // This object may be destructed when the menu closes but we need something
359 // around to finish the sign-out process and close the profile windows.
360 new SignoutTracker(current_profile, GURL(landing_url), contents);
361
362 return contents; // returned for testing purposes
363}