Upstream version 6.35.121.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / profiles / profile_info_cache.cc
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/profile_info_cache.h"
6
7 #include "base/bind.h"
8 #include "base/file_util.h"
9 #include "base/format_macros.h"
10 #include "base/i18n/case_conversion.h"
11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/prefs/pref_registry_simple.h"
14 #include "base/prefs/pref_service.h"
15 #include "base/prefs/scoped_user_pref_update.h"
16 #include "base/rand_util.h"
17 #include "base/stl_util.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/string_piece.h"
20 #include "base/strings/stringprintf.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "base/values.h"
23 #include "chrome/browser/browser_process.h"
24 #include "chrome/browser/chrome_notification_types.h"
25 #include "chrome/common/pref_names.h"
26 #include "chrome/common/profile_management_switches.h"
27 #include "content/public/browser/browser_thread.h"
28 #include "content/public/browser/notification_service.h"
29 #include "grit/generated_resources.h"
30 #include "grit/theme_resources.h"
31 #include "third_party/skia/include/core/SkBitmap.h"
32 #include "ui/base/l10n/l10n_util.h"
33 #include "ui/base/resource/resource_bundle.h"
34 #include "ui/gfx/image/image.h"
35 #include "ui/gfx/image/image_util.h"
36
37 using content::BrowserThread;
38
39 namespace {
40
41 const char kNameKey[] = "name";
42 const char kShortcutNameKey[] = "shortcut_name";
43 const char kGAIANameKey[] = "gaia_name";
44 const char kGAIAGivenNameKey[] = "gaia_given_name";
45 const char kUserNameKey[] = "user_name";
46 const char kIsUsingDefaultName[] = "is_using_default_name";
47 const char kAvatarIconKey[] = "avatar_icon";
48 const char kAuthCredentialsKey[] = "local_auth_credentials";
49 const char kUseGAIAPictureKey[] = "use_gaia_picture";
50 const char kBackgroundAppsKey[] = "background_apps";
51 const char kGAIAPictureFileNameKey[] = "gaia_picture_file_name";
52 const char kIsManagedKey[] = "is_managed";
53 const char kIsOmittedFromProfileListKey[] = "is_omitted_from_profile_list";
54 const char kSigninRequiredKey[] = "signin_required";
55 const char kManagedUserId[] = "managed_user_id";
56 const char kProfileIsEphemeral[] = "is_ephemeral";
57 const char kActiveTimeKey[] = "active_time";
58
59 const char kDefaultUrlPrefix[] = "chrome://theme/IDR_PROFILE_AVATAR_";
60 const char kGAIAPictureFileName[] = "Google Profile Picture.png";
61
62 const int kDefaultAvatarIconResources[] = {
63   IDR_PROFILE_AVATAR_0,
64   IDR_PROFILE_AVATAR_1,
65   IDR_PROFILE_AVATAR_2,
66   IDR_PROFILE_AVATAR_3,
67   IDR_PROFILE_AVATAR_4,
68   IDR_PROFILE_AVATAR_5,
69   IDR_PROFILE_AVATAR_6,
70   IDR_PROFILE_AVATAR_7,
71   IDR_PROFILE_AVATAR_8,
72   IDR_PROFILE_AVATAR_9,
73   IDR_PROFILE_AVATAR_10,
74   IDR_PROFILE_AVATAR_11,
75   IDR_PROFILE_AVATAR_12,
76   IDR_PROFILE_AVATAR_13,
77   IDR_PROFILE_AVATAR_14,
78   IDR_PROFILE_AVATAR_15,
79   IDR_PROFILE_AVATAR_16,
80   IDR_PROFILE_AVATAR_17,
81   IDR_PROFILE_AVATAR_18,
82   IDR_PROFILE_AVATAR_19,
83   IDR_PROFILE_AVATAR_20,
84   IDR_PROFILE_AVATAR_21,
85   IDR_PROFILE_AVATAR_22,
86   IDR_PROFILE_AVATAR_23,
87   IDR_PROFILE_AVATAR_24,
88   IDR_PROFILE_AVATAR_25,
89 };
90
91 const size_t kDefaultAvatarIconsCount = arraysize(kDefaultAvatarIconResources);
92
93 // The first 8 icons are generic.
94 const size_t kGenericIconCount = 8;
95
96 // First eight are generic icons, which use IDS_NUMBERED_PROFILE_NAME.
97 const int kDefaultNames[] = {
98   IDS_DEFAULT_AVATAR_NAME_8,
99   IDS_DEFAULT_AVATAR_NAME_9,
100   IDS_DEFAULT_AVATAR_NAME_10,
101   IDS_DEFAULT_AVATAR_NAME_11,
102   IDS_DEFAULT_AVATAR_NAME_12,
103   IDS_DEFAULT_AVATAR_NAME_13,
104   IDS_DEFAULT_AVATAR_NAME_14,
105   IDS_DEFAULT_AVATAR_NAME_15,
106   IDS_DEFAULT_AVATAR_NAME_16,
107   IDS_DEFAULT_AVATAR_NAME_17,
108   IDS_DEFAULT_AVATAR_NAME_18,
109   IDS_DEFAULT_AVATAR_NAME_19,
110   IDS_DEFAULT_AVATAR_NAME_20,
111   IDS_DEFAULT_AVATAR_NAME_21,
112   IDS_DEFAULT_AVATAR_NAME_22,
113   IDS_DEFAULT_AVATAR_NAME_23,
114   IDS_DEFAULT_AVATAR_NAME_24,
115   IDS_DEFAULT_AVATAR_NAME_25
116 };
117
118 typedef std::vector<unsigned char> ImageData;
119
120 // Writes |data| to disk and takes ownership of the pointer. On completion
121 // |success| is set to true on success and false on failure.
122 void SaveBitmap(ImageData* data,
123                 const base::FilePath& image_path,
124                 bool* success) {
125   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
126   scoped_ptr<ImageData> data_owner(data);
127   *success = false;
128
129   // Make sure the destination directory exists.
130   base::FilePath dir = image_path.DirName();
131   if (!base::DirectoryExists(dir) && !base::CreateDirectory(dir)) {
132     LOG(ERROR) << "Failed to create parent directory.";
133     return;
134   }
135
136   if (base::WriteFile(image_path, reinterpret_cast<char*>(&(*data)[0]),
137                       data->size()) == -1) {
138     LOG(ERROR) << "Failed to save image to file.";
139     return;
140   }
141
142   *success = true;
143 }
144
145 // Reads a PNG from disk and decodes it. If the bitmap was successfully read
146 // from disk the then |out_image| will contain the bitmap image, otherwise it
147 // will be NULL.
148 void ReadBitmap(const base::FilePath& image_path,
149                 gfx::Image** out_image) {
150   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
151   *out_image = NULL;
152
153   std::string image_data;
154   if (!base::ReadFileToString(image_path, &image_data)) {
155     LOG(ERROR) << "Failed to read PNG file from disk.";
156     return;
157   }
158
159   gfx::Image image = gfx::Image::CreateFrom1xPNGBytes(
160       base::RefCountedString::TakeString(&image_data));
161   if (image.IsEmpty()) {
162     LOG(ERROR) << "Failed to decode PNG file.";
163     return;
164   }
165
166   *out_image = new gfx::Image(image);
167 }
168
169 void DeleteBitmap(const base::FilePath& image_path) {
170   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
171   base::DeleteFile(image_path, false);
172 }
173
174 bool IsDefaultName(const base::string16& name) {
175   // Check if it's a "First user" old-style name.
176   if (name == l10n_util::GetStringUTF16(IDS_DEFAULT_PROFILE_NAME))
177     return true;
178
179   // Check if it's one of the old-style profile names.
180   for (size_t i = 0; i < arraysize(kDefaultNames); ++i) {
181     if (name == l10n_util::GetStringUTF16(kDefaultNames[i]))
182       return true;
183   }
184
185   // Check whether it's one of the "Person %d" style names.
186   std::string default_name_format = l10n_util::GetStringFUTF8(
187       IDS_NEW_NUMBERED_PROFILE_NAME, base::string16()) + "%d";
188
189   int generic_profile_number;  // Unused. Just a placeholder for sscanf.
190   int assignments = sscanf(base::UTF16ToUTF8(name).c_str(),
191                            default_name_format.c_str(),
192                            &generic_profile_number);
193   // Unless it matched the format, this is a custom name.
194   return assignments == 1;
195 }
196
197 }  // namespace
198
199 ProfileInfoCache::ProfileInfoCache(PrefService* prefs,
200                                    const base::FilePath& user_data_dir)
201     : prefs_(prefs),
202       user_data_dir_(user_data_dir) {
203   // Populate the cache
204   DictionaryPrefUpdate update(prefs_, prefs::kProfileInfoCache);
205   base::DictionaryValue* cache = update.Get();
206   for (base::DictionaryValue::Iterator it(*cache);
207        !it.IsAtEnd(); it.Advance()) {
208     base::DictionaryValue* info = NULL;
209     cache->GetDictionaryWithoutPathExpansion(it.key(), &info);
210     base::string16 name;
211     info->GetString(kNameKey, &name);
212     sorted_keys_.insert(FindPositionForProfile(it.key(), name), it.key());
213     // TODO(ibraaaa): delete this when 97% of our users are using M31.
214     // http://crbug.com/276163
215     bool is_managed = false;
216     if (info->GetBoolean(kIsManagedKey, &is_managed)) {
217       info->Remove(kIsManagedKey, NULL);
218       info->SetString(kManagedUserId, is_managed ? "DUMMY_ID" : std::string());
219     }
220     info->SetBoolean(kIsUsingDefaultName, IsDefaultName(name));
221   }
222 }
223
224 ProfileInfoCache::~ProfileInfoCache() {
225   STLDeleteContainerPairSecondPointers(
226       gaia_pictures_.begin(), gaia_pictures_.end());
227 }
228
229 void ProfileInfoCache::AddProfileToCache(const base::FilePath& profile_path,
230                                          const base::string16& name,
231                                          const base::string16& username,
232                                          size_t icon_index,
233                                          const std::string& managed_user_id) {
234   std::string key = CacheKeyFromProfilePath(profile_path);
235   DictionaryPrefUpdate update(prefs_, prefs::kProfileInfoCache);
236   base::DictionaryValue* cache = update.Get();
237
238   scoped_ptr<base::DictionaryValue> info(new base::DictionaryValue);
239   info->SetString(kNameKey, name);
240   info->SetString(kUserNameKey, username);
241   info->SetString(kAvatarIconKey, GetDefaultAvatarIconUrl(icon_index));
242   // Default value for whether background apps are running is false.
243   info->SetBoolean(kBackgroundAppsKey, false);
244   info->SetString(kManagedUserId, managed_user_id);
245   info->SetBoolean(kIsOmittedFromProfileListKey, !managed_user_id.empty());
246   info->SetBoolean(kProfileIsEphemeral, false);
247   info->SetBoolean(kIsUsingDefaultName, IsDefaultName(name));
248   cache->SetWithoutPathExpansion(key, info.release());
249
250   sorted_keys_.insert(FindPositionForProfile(key, name), key);
251
252   FOR_EACH_OBSERVER(ProfileInfoCacheObserver,
253                     observer_list_,
254                     OnProfileAdded(profile_path));
255
256   content::NotificationService::current()->Notify(
257       chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED,
258       content::NotificationService::AllSources(),
259       content::NotificationService::NoDetails());
260 }
261
262 void ProfileInfoCache::AddObserver(ProfileInfoCacheObserver* obs) {
263   observer_list_.AddObserver(obs);
264 }
265
266 void ProfileInfoCache::RemoveObserver(ProfileInfoCacheObserver* obs) {
267   observer_list_.RemoveObserver(obs);
268 }
269
270 void ProfileInfoCache::DeleteProfileFromCache(
271     const base::FilePath& profile_path) {
272   size_t profile_index = GetIndexOfProfileWithPath(profile_path);
273   if (profile_index == std::string::npos) {
274     NOTREACHED();
275     return;
276   }
277   base::string16 name = GetNameOfProfileAtIndex(profile_index);
278
279   FOR_EACH_OBSERVER(ProfileInfoCacheObserver,
280                     observer_list_,
281                     OnProfileWillBeRemoved(profile_path));
282
283   DictionaryPrefUpdate update(prefs_, prefs::kProfileInfoCache);
284   base::DictionaryValue* cache = update.Get();
285   std::string key = CacheKeyFromProfilePath(profile_path);
286   cache->Remove(key, NULL);
287   sorted_keys_.erase(std::find(sorted_keys_.begin(), sorted_keys_.end(), key));
288
289   FOR_EACH_OBSERVER(ProfileInfoCacheObserver,
290                     observer_list_,
291                     OnProfileWasRemoved(profile_path, name));
292
293   content::NotificationService::current()->Notify(
294       chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED,
295       content::NotificationService::AllSources(),
296       content::NotificationService::NoDetails());
297 }
298
299 size_t ProfileInfoCache::GetNumberOfProfiles() const {
300   return sorted_keys_.size();
301 }
302
303 size_t ProfileInfoCache::GetIndexOfProfileWithPath(
304     const base::FilePath& profile_path) const {
305   if (profile_path.DirName() != user_data_dir_)
306     return std::string::npos;
307   std::string search_key = CacheKeyFromProfilePath(profile_path);
308   for (size_t i = 0; i < sorted_keys_.size(); ++i) {
309     if (sorted_keys_[i] == search_key)
310       return i;
311   }
312   return std::string::npos;
313 }
314
315 base::string16 ProfileInfoCache::GetNameOfProfileAtIndex(size_t index) const {
316   base::string16 name;
317   // Unless the user has customized the profile name, we should use the
318   // profile's Gaia given name, if it's available.
319   if (ProfileIsUsingDefaultNameAtIndex(index)) {
320     base::string16 given_name = GetGAIAGivenNameOfProfileAtIndex(index);
321     name = given_name.empty() ? GetGAIANameOfProfileAtIndex(index) : given_name;
322   }
323   if (name.empty())
324     GetInfoForProfileAtIndex(index)->GetString(kNameKey, &name);
325   return name;
326 }
327
328 base::string16 ProfileInfoCache::GetShortcutNameOfProfileAtIndex(size_t index)
329     const {
330   base::string16 shortcut_name;
331   GetInfoForProfileAtIndex(index)->GetString(
332       kShortcutNameKey, &shortcut_name);
333   return shortcut_name;
334 }
335
336 base::FilePath ProfileInfoCache::GetPathOfProfileAtIndex(size_t index) const {
337   return user_data_dir_.AppendASCII(sorted_keys_[index]);
338 }
339
340 base::Time ProfileInfoCache::GetProfileActiveTimeAtIndex(size_t index) const {
341   double dt;
342   if (GetInfoForProfileAtIndex(index)->GetDouble(kActiveTimeKey, &dt)) {
343     return base::Time::FromDoubleT(dt);
344   } else {
345     return base::Time();
346   }
347 }
348
349 base::string16 ProfileInfoCache::GetUserNameOfProfileAtIndex(
350     size_t index) const {
351   base::string16 user_name;
352   GetInfoForProfileAtIndex(index)->GetString(kUserNameKey, &user_name);
353   return user_name;
354 }
355
356 const gfx::Image& ProfileInfoCache::GetAvatarIconOfProfileAtIndex(
357     size_t index) const {
358   if (IsUsingGAIAPictureOfProfileAtIndex(index)) {
359     const gfx::Image* image = GetGAIAPictureOfProfileAtIndex(index);
360     if (image)
361       return *image;
362   }
363
364   int resource_id = GetDefaultAvatarIconResourceIDAtIndex(
365       GetAvatarIconIndexOfProfileAtIndex(index));
366   return ResourceBundle::GetSharedInstance().GetNativeImageNamed(resource_id);
367 }
368
369 std::string ProfileInfoCache::GetLocalAuthCredentialsOfProfileAtIndex(
370     size_t index) const {
371   std::string credentials;
372   GetInfoForProfileAtIndex(index)->GetString(kAuthCredentialsKey, &credentials);
373   return credentials;
374 }
375
376 bool ProfileInfoCache::GetBackgroundStatusOfProfileAtIndex(
377     size_t index) const {
378   bool background_app_status;
379   if (!GetInfoForProfileAtIndex(index)->GetBoolean(kBackgroundAppsKey,
380                                                    &background_app_status)) {
381     return false;
382   }
383   return background_app_status;
384 }
385
386 base::string16 ProfileInfoCache::GetGAIANameOfProfileAtIndex(
387     size_t index) const {
388   base::string16 name;
389   GetInfoForProfileAtIndex(index)->GetString(kGAIANameKey, &name);
390   return name;
391 }
392
393 base::string16 ProfileInfoCache::GetGAIAGivenNameOfProfileAtIndex(
394     size_t index) const {
395   base::string16 name;
396   GetInfoForProfileAtIndex(index)->GetString(kGAIAGivenNameKey, &name);
397   return name;
398 }
399
400 const gfx::Image* ProfileInfoCache::GetGAIAPictureOfProfileAtIndex(
401     size_t index) const {
402   base::FilePath path = GetPathOfProfileAtIndex(index);
403   std::string key = CacheKeyFromProfilePath(path);
404
405   // If the picture is already loaded then use it.
406   if (gaia_pictures_.count(key)) {
407     if (gaia_pictures_[key]->IsEmpty())
408       return NULL;
409     return gaia_pictures_[key];
410   }
411
412   std::string file_name;
413   GetInfoForProfileAtIndex(index)->GetString(
414       kGAIAPictureFileNameKey, &file_name);
415
416   // If the picture is not on disk or it is already being loaded then return
417   // NULL.
418   if (file_name.empty() || gaia_pictures_loading_[key])
419     return NULL;
420
421   gaia_pictures_loading_[key] = true;
422   base::FilePath image_path = path.AppendASCII(file_name);
423   gfx::Image** image = new gfx::Image*;
424   BrowserThread::PostTaskAndReply(BrowserThread::FILE, FROM_HERE,
425       base::Bind(&ReadBitmap, image_path, image),
426       base::Bind(&ProfileInfoCache::OnGAIAPictureLoaded,
427           const_cast<ProfileInfoCache*>(this)->AsWeakPtr(), path, image));
428
429   return NULL;
430 }
431
432 bool ProfileInfoCache::ProfileIsManagedAtIndex(size_t index) const {
433   return !GetManagedUserIdOfProfileAtIndex(index).empty();
434 }
435
436 bool ProfileInfoCache::IsOmittedProfileAtIndex(size_t index) const {
437   bool value = false;
438   GetInfoForProfileAtIndex(index)->GetBoolean(kIsOmittedFromProfileListKey,
439                                               &value);
440   return value;
441 }
442
443 bool ProfileInfoCache::ProfileIsSigninRequiredAtIndex(size_t index) const {
444   bool value = false;
445   GetInfoForProfileAtIndex(index)->GetBoolean(kSigninRequiredKey, &value);
446   return value;
447 }
448
449 std::string ProfileInfoCache::GetManagedUserIdOfProfileAtIndex(
450     size_t index) const {
451   std::string managed_user_id;
452   GetInfoForProfileAtIndex(index)->GetString(kManagedUserId, &managed_user_id);
453   return managed_user_id;
454 }
455
456 bool ProfileInfoCache::ProfileIsEphemeralAtIndex(size_t index) const {
457   bool value = false;
458   GetInfoForProfileAtIndex(index)->GetBoolean(kProfileIsEphemeral, &value);
459   return value;
460 }
461
462 bool ProfileInfoCache::ProfileIsUsingDefaultNameAtIndex(size_t index) const {
463   bool value = false;
464   GetInfoForProfileAtIndex(index)->GetBoolean(kIsUsingDefaultName, &value);
465   return value;
466 }
467
468 void ProfileInfoCache::OnGAIAPictureLoaded(const base::FilePath& path,
469                                            gfx::Image** image) const {
470   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
471
472   std::string key = CacheKeyFromProfilePath(path);
473   gaia_pictures_loading_[key] = false;
474
475   if (*image) {
476     delete gaia_pictures_[key];
477     gaia_pictures_[key] = *image;
478   } else {
479     // Place an empty image in the cache to avoid reloading it again.
480     gaia_pictures_[key] = new gfx::Image();
481   }
482   delete image;
483
484   content::NotificationService::current()->Notify(
485       chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED,
486       content::NotificationService::AllSources(),
487       content::NotificationService::NoDetails());
488 }
489
490 void ProfileInfoCache::OnGAIAPictureSaved(const base::FilePath& path,
491                                           bool* success) const  {
492   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
493
494   if (*success) {
495     content::NotificationService::current()->Notify(
496         chrome::NOTIFICATION_PROFILE_CACHE_PICTURE_SAVED,
497         content::NotificationService::AllSources(),
498         content::NotificationService::NoDetails());
499   }
500   delete success;
501 }
502
503 bool ProfileInfoCache::IsUsingGAIAPictureOfProfileAtIndex(
504     size_t index) const {
505   bool value = false;
506   GetInfoForProfileAtIndex(index)->GetBoolean(kUseGAIAPictureKey, &value);
507   return value;
508 }
509
510 size_t ProfileInfoCache::GetAvatarIconIndexOfProfileAtIndex(size_t index)
511     const {
512   std::string icon_url;
513   GetInfoForProfileAtIndex(index)->GetString(kAvatarIconKey, &icon_url);
514   size_t icon_index = 0;
515   if (!IsDefaultAvatarIconUrl(icon_url, &icon_index))
516     DLOG(WARNING) << "Unknown avatar icon: " << icon_url;
517
518   return icon_index;
519 }
520
521 void ProfileInfoCache::SetProfileActiveTimeAtIndex(size_t index) {
522   scoped_ptr<base::DictionaryValue> info(
523       GetInfoForProfileAtIndex(index)->DeepCopy());
524   info->SetDouble(kActiveTimeKey, base::Time::Now().ToDoubleT());
525   // This takes ownership of |info|.
526   SetInfoQuietlyForProfileAtIndex(index, info.release());
527 }
528
529 void ProfileInfoCache::SetNameOfProfileAtIndex(size_t index,
530                                                const base::string16& name) {
531   scoped_ptr<base::DictionaryValue> info(
532       GetInfoForProfileAtIndex(index)->DeepCopy());
533   base::string16 current_name;
534   info->GetString(kNameKey, &current_name);
535   if (name == current_name)
536     return;
537
538   base::string16 old_display_name = GetNameOfProfileAtIndex(index);
539   info->SetString(kNameKey, name);
540   info->SetBoolean(kIsUsingDefaultName, false);
541
542   // This takes ownership of |info|.
543   SetInfoForProfileAtIndex(index, info.release());
544   base::string16 new_display_name = GetNameOfProfileAtIndex(index);
545   base::FilePath profile_path = GetPathOfProfileAtIndex(index);
546   UpdateSortForProfileIndex(index);
547
548   if (old_display_name != new_display_name) {
549     FOR_EACH_OBSERVER(ProfileInfoCacheObserver,
550                       observer_list_,
551                       OnProfileNameChanged(profile_path, old_display_name));
552   }
553 }
554
555 void ProfileInfoCache::SetShortcutNameOfProfileAtIndex(
556     size_t index,
557     const base::string16& shortcut_name) {
558   if (shortcut_name == GetShortcutNameOfProfileAtIndex(index))
559     return;
560   scoped_ptr<base::DictionaryValue> info(
561       GetInfoForProfileAtIndex(index)->DeepCopy());
562   info->SetString(kShortcutNameKey, shortcut_name);
563   // This takes ownership of |info|.
564   SetInfoForProfileAtIndex(index, info.release());
565 }
566
567 void ProfileInfoCache::SetUserNameOfProfileAtIndex(
568     size_t index,
569     const base::string16& user_name) {
570   if (user_name == GetUserNameOfProfileAtIndex(index))
571     return;
572
573   scoped_ptr<base::DictionaryValue> info(
574       GetInfoForProfileAtIndex(index)->DeepCopy());
575   info->SetString(kUserNameKey, user_name);
576   // This takes ownership of |info|.
577   SetInfoForProfileAtIndex(index, info.release());
578 }
579
580 void ProfileInfoCache::SetAvatarIconOfProfileAtIndex(size_t index,
581                                                      size_t icon_index) {
582   scoped_ptr<base::DictionaryValue> info(
583       GetInfoForProfileAtIndex(index)->DeepCopy());
584   info->SetString(kAvatarIconKey, GetDefaultAvatarIconUrl(icon_index));
585   // This takes ownership of |info|.
586   SetInfoForProfileAtIndex(index, info.release());
587
588   base::FilePath profile_path = GetPathOfProfileAtIndex(index);
589   FOR_EACH_OBSERVER(ProfileInfoCacheObserver,
590                     observer_list_,
591                     OnProfileAvatarChanged(profile_path));
592 }
593
594 void ProfileInfoCache::SetIsOmittedProfileAtIndex(size_t index,
595                                                   bool is_omitted) {
596   if (IsOmittedProfileAtIndex(index) == is_omitted)
597     return;
598   scoped_ptr<base::DictionaryValue> info(
599       GetInfoForProfileAtIndex(index)->DeepCopy());
600   info->SetBoolean(kIsOmittedFromProfileListKey, is_omitted);
601   // This takes ownership of |info|.
602   SetInfoForProfileAtIndex(index, info.release());
603 }
604
605 void ProfileInfoCache::SetManagedUserIdOfProfileAtIndex(size_t index,
606                                                         const std::string& id) {
607   scoped_ptr<base::DictionaryValue> info(
608       GetInfoForProfileAtIndex(index)->DeepCopy());
609   info->SetString(kManagedUserId, id);
610   // This takes ownership of |info|.
611   SetInfoForProfileAtIndex(index, info.release());
612 }
613
614 void ProfileInfoCache::SetLocalAuthCredentialsOfProfileAtIndex(
615     size_t index,
616     const std::string& credentials) {
617   scoped_ptr<base::DictionaryValue> info(
618       GetInfoForProfileAtIndex(index)->DeepCopy());
619   info->SetString(kAuthCredentialsKey, credentials);
620   // This takes ownership of |info|.
621   SetInfoForProfileAtIndex(index, info.release());
622 }
623
624 void ProfileInfoCache::SetBackgroundStatusOfProfileAtIndex(
625     size_t index,
626     bool running_background_apps) {
627   if (GetBackgroundStatusOfProfileAtIndex(index) == running_background_apps)
628     return;
629   scoped_ptr<base::DictionaryValue> info(
630       GetInfoForProfileAtIndex(index)->DeepCopy());
631   info->SetBoolean(kBackgroundAppsKey, running_background_apps);
632   // This takes ownership of |info|.
633   SetInfoForProfileAtIndex(index, info.release());
634 }
635
636 void ProfileInfoCache::SetGAIANameOfProfileAtIndex(size_t index,
637                                                    const base::string16& name) {
638   if (name == GetGAIANameOfProfileAtIndex(index))
639     return;
640
641   base::string16 old_display_name = GetNameOfProfileAtIndex(index);
642   scoped_ptr<base::DictionaryValue> info(
643       GetInfoForProfileAtIndex(index)->DeepCopy());
644   info->SetString(kGAIANameKey, name);
645   // This takes ownership of |info|.
646   SetInfoForProfileAtIndex(index, info.release());
647   base::string16 new_display_name = GetNameOfProfileAtIndex(index);
648   base::FilePath profile_path = GetPathOfProfileAtIndex(index);
649   UpdateSortForProfileIndex(index);
650
651   if (old_display_name != new_display_name) {
652     FOR_EACH_OBSERVER(ProfileInfoCacheObserver,
653                       observer_list_,
654                       OnProfileNameChanged(profile_path, old_display_name));
655   }
656 }
657
658 void ProfileInfoCache::SetGAIAGivenNameOfProfileAtIndex(
659     size_t index,
660     const base::string16& name) {
661   if (name == GetGAIAGivenNameOfProfileAtIndex(index))
662     return;
663
664   scoped_ptr<base::DictionaryValue> info(
665       GetInfoForProfileAtIndex(index)->DeepCopy());
666   info->SetString(kGAIAGivenNameKey, name);
667   // This takes ownership of |info|.
668   SetInfoForProfileAtIndex(index, info.release());
669 }
670
671 void ProfileInfoCache::SetGAIAPictureOfProfileAtIndex(size_t index,
672                                                       const gfx::Image* image) {
673   base::FilePath path = GetPathOfProfileAtIndex(index);
674   std::string key = CacheKeyFromProfilePath(path);
675
676   // Delete the old bitmap from cache.
677   std::map<std::string, gfx::Image*>::iterator it = gaia_pictures_.find(key);
678   if (it != gaia_pictures_.end()) {
679     delete it->second;
680     gaia_pictures_.erase(it);
681   }
682
683   std::string old_file_name;
684   GetInfoForProfileAtIndex(index)->GetString(
685       kGAIAPictureFileNameKey, &old_file_name);
686   std::string new_file_name;
687
688   if (!image) {
689     // Delete the old bitmap from disk.
690     if (!old_file_name.empty()) {
691       base::FilePath image_path = path.AppendASCII(old_file_name);
692       BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
693                               base::Bind(&DeleteBitmap, image_path));
694     }
695   } else {
696     // Save the new bitmap to disk.
697     gaia_pictures_[key] = new gfx::Image(*image);
698     scoped_ptr<ImageData> data(new ImageData);
699     scoped_refptr<base::RefCountedMemory> png_data = image->As1xPNGBytes();
700     data->assign(png_data->front(), png_data->front() + png_data->size());
701     if (!data->size()) {
702       LOG(ERROR) << "Failed to PNG encode the image.";
703     } else {
704       new_file_name =
705           old_file_name.empty() ? kGAIAPictureFileName : old_file_name;
706       base::FilePath image_path = path.AppendASCII(new_file_name);
707       bool* success = new bool;
708       BrowserThread::PostTaskAndReply(BrowserThread::FILE, FROM_HERE,
709           base::Bind(&SaveBitmap, data.release(), image_path, success),
710           base::Bind(&ProfileInfoCache::OnGAIAPictureSaved, AsWeakPtr(),
711                      path, success));
712     }
713   }
714
715   scoped_ptr<base::DictionaryValue> info(
716       GetInfoForProfileAtIndex(index)->DeepCopy());
717   info->SetString(kGAIAPictureFileNameKey, new_file_name);
718   // This takes ownership of |info|.
719   SetInfoForProfileAtIndex(index, info.release());
720
721   FOR_EACH_OBSERVER(ProfileInfoCacheObserver,
722                     observer_list_,
723                     OnProfileAvatarChanged(path));
724 }
725
726 void ProfileInfoCache::SetIsUsingGAIAPictureOfProfileAtIndex(size_t index,
727                                                              bool value) {
728   scoped_ptr<base::DictionaryValue> info(
729       GetInfoForProfileAtIndex(index)->DeepCopy());
730   info->SetBoolean(kUseGAIAPictureKey, value);
731   // This takes ownership of |info|.
732   SetInfoForProfileAtIndex(index, info.release());
733
734   // Retrieve some info to update observers who care about avatar changes.
735   base::FilePath profile_path = GetPathOfProfileAtIndex(index);
736   FOR_EACH_OBSERVER(ProfileInfoCacheObserver,
737                     observer_list_,
738                     OnProfileAvatarChanged(profile_path));
739 }
740
741 void ProfileInfoCache::SetProfileSigninRequiredAtIndex(size_t index,
742                                                        bool value) {
743   if (value == ProfileIsSigninRequiredAtIndex(index))
744     return;
745
746   scoped_ptr<base::DictionaryValue> info(
747       GetInfoForProfileAtIndex(index)->DeepCopy());
748   info->SetBoolean(kSigninRequiredKey, value);
749   // This takes ownership of |info|.
750   SetInfoForProfileAtIndex(index, info.release());
751
752   base::FilePath profile_path = GetPathOfProfileAtIndex(index);
753   FOR_EACH_OBSERVER(ProfileInfoCacheObserver,
754                     observer_list_,
755                     OnProfileSigninRequiredChanged(profile_path));
756 }
757
758 void ProfileInfoCache::SetProfileIsEphemeralAtIndex(size_t index, bool value) {
759   if (value == ProfileIsEphemeralAtIndex(index))
760     return;
761
762   scoped_ptr<base::DictionaryValue> info(
763       GetInfoForProfileAtIndex(index)->DeepCopy());
764   info->SetBoolean(kProfileIsEphemeral, value);
765   // This takes ownership of |info|.
766   SetInfoForProfileAtIndex(index, info.release());
767 }
768
769 void ProfileInfoCache::SetProfileIsUsingDefaultNameAtIndex(
770     size_t index, bool value) {
771   if (value == ProfileIsUsingDefaultNameAtIndex(index))
772     return;
773
774   scoped_ptr<base::DictionaryValue> info(
775       GetInfoForProfileAtIndex(index)->DeepCopy());
776   info->SetBoolean(kIsUsingDefaultName, value);
777   // This takes ownership of |info|.
778   SetInfoForProfileAtIndex(index, info.release());
779 }
780
781 base::string16 ProfileInfoCache::ChooseNameForNewProfile(
782     size_t icon_index) const {
783   base::string16 name;
784   for (int name_index = 1; ; ++name_index) {
785     if (switches::IsNewProfileManagement()) {
786       name = l10n_util::GetStringFUTF16Int(IDS_NEW_NUMBERED_PROFILE_NAME,
787                                            name_index);
788     } else if (icon_index < kGenericIconCount) {
789       name = l10n_util::GetStringFUTF16Int(IDS_NUMBERED_PROFILE_NAME,
790                                            name_index);
791     } else {
792       name = l10n_util::GetStringUTF16(
793           kDefaultNames[icon_index - kGenericIconCount]);
794       if (name_index > 1)
795         name.append(base::UTF8ToUTF16(base::IntToString(name_index)));
796     }
797
798     // Loop through previously named profiles to ensure we're not duplicating.
799     bool name_found = false;
800     for (size_t i = 0; i < GetNumberOfProfiles(); ++i) {
801       if (GetNameOfProfileAtIndex(i) == name) {
802         name_found = true;
803         break;
804       }
805     }
806     if (!name_found)
807       return name;
808   }
809 }
810
811 bool ProfileInfoCache::IconIndexIsUnique(size_t icon_index) const {
812   for (size_t i = 0; i < GetNumberOfProfiles(); ++i) {
813     if (GetAvatarIconIndexOfProfileAtIndex(i) == icon_index)
814       return false;
815   }
816   return true;
817 }
818
819 bool ProfileInfoCache::ChooseAvatarIconIndexForNewProfile(
820     bool allow_generic_icon,
821     bool must_be_unique,
822     size_t* out_icon_index) const {
823   // Always allow all icons for new profiles if using the
824   // --new-profile-management flag.
825   if (switches::IsNewProfileManagement())
826     allow_generic_icon = true;
827   size_t start = allow_generic_icon ? 0 : kGenericIconCount;
828   size_t end = GetDefaultAvatarIconCount();
829   size_t count = end - start;
830
831   int rand = base::RandInt(0, count);
832   for (size_t i = 0; i < count; ++i) {
833     size_t icon_index = start + (rand + i) %  count;
834     if (!must_be_unique || IconIndexIsUnique(icon_index)) {
835       *out_icon_index = icon_index;
836       return true;
837     }
838   }
839
840   return false;
841 }
842
843 size_t ProfileInfoCache::ChooseAvatarIconIndexForNewProfile() const {
844   size_t icon_index = 0;
845   // Try to find a unique, non-generic icon.
846   if (ChooseAvatarIconIndexForNewProfile(false, true, &icon_index))
847     return icon_index;
848   // Try to find any unique icon.
849   if (ChooseAvatarIconIndexForNewProfile(true, true, &icon_index))
850     return icon_index;
851   // Settle for any random icon, even if it's not unique.
852   if (ChooseAvatarIconIndexForNewProfile(true, false, &icon_index))
853     return icon_index;
854
855   NOTREACHED();
856   return 0;
857 }
858
859 const base::FilePath& ProfileInfoCache::GetUserDataDir() const {
860   return user_data_dir_;
861 }
862
863 // static
864 size_t ProfileInfoCache::GetDefaultAvatarIconCount() {
865   return kDefaultAvatarIconsCount;
866 }
867
868 // static
869 int ProfileInfoCache::GetDefaultAvatarIconResourceIDAtIndex(size_t index) {
870   DCHECK(IsDefaultAvatarIconIndex(index));
871   return kDefaultAvatarIconResources[index];
872 }
873
874 // static
875 std::string ProfileInfoCache::GetDefaultAvatarIconUrl(size_t index) {
876   DCHECK(IsDefaultAvatarIconIndex(index));
877   return base::StringPrintf("%s%" PRIuS, kDefaultUrlPrefix, index);
878 }
879
880 // static
881 bool ProfileInfoCache::IsDefaultAvatarIconIndex(size_t index) {
882   return index < kDefaultAvatarIconsCount;
883 }
884
885 // static
886 bool ProfileInfoCache::IsDefaultAvatarIconUrl(const std::string& url,
887                                               size_t* icon_index) {
888   DCHECK(icon_index);
889   if (url.find(kDefaultUrlPrefix) != 0)
890     return false;
891
892   int int_value = -1;
893   if (base::StringToInt(base::StringPiece(url.begin() +
894                                           strlen(kDefaultUrlPrefix),
895                                           url.end()),
896                         &int_value)) {
897     if (int_value < 0 ||
898         int_value >= static_cast<int>(kDefaultAvatarIconsCount))
899       return false;
900     *icon_index = int_value;
901     return true;
902   }
903
904   return false;
905 }
906
907 const base::DictionaryValue* ProfileInfoCache::GetInfoForProfileAtIndex(
908     size_t index) const {
909   DCHECK_LT(index, GetNumberOfProfiles());
910   const base::DictionaryValue* cache =
911       prefs_->GetDictionary(prefs::kProfileInfoCache);
912   const base::DictionaryValue* info = NULL;
913   cache->GetDictionaryWithoutPathExpansion(sorted_keys_[index], &info);
914   return info;
915 }
916
917 void ProfileInfoCache::SetInfoQuietlyForProfileAtIndex(
918     size_t index, base::DictionaryValue* info) {
919   DictionaryPrefUpdate update(prefs_, prefs::kProfileInfoCache);
920   base::DictionaryValue* cache = update.Get();
921   cache->SetWithoutPathExpansion(sorted_keys_[index], info);
922 }
923
924 // TODO(noms): Switch to newer notification system.
925 void ProfileInfoCache::SetInfoForProfileAtIndex(size_t index,
926                                                 base::DictionaryValue* info) {
927   SetInfoQuietlyForProfileAtIndex(index, info);
928
929   content::NotificationService::current()->Notify(
930       chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED,
931       content::NotificationService::AllSources(),
932       content::NotificationService::NoDetails());
933 }
934
935 std::string ProfileInfoCache::CacheKeyFromProfilePath(
936     const base::FilePath& profile_path) const {
937   DCHECK(user_data_dir_ == profile_path.DirName());
938   base::FilePath base_name = profile_path.BaseName();
939   return base_name.MaybeAsASCII();
940 }
941
942 std::vector<std::string>::iterator ProfileInfoCache::FindPositionForProfile(
943     const std::string& search_key,
944     const base::string16& search_name) {
945   base::string16 search_name_l = base::i18n::ToLower(search_name);
946   for (size_t i = 0; i < GetNumberOfProfiles(); ++i) {
947     base::string16 name_l = base::i18n::ToLower(GetNameOfProfileAtIndex(i));
948     int name_compare = search_name_l.compare(name_l);
949     if (name_compare < 0)
950       return sorted_keys_.begin() + i;
951     if (name_compare == 0) {
952       int key_compare = search_key.compare(sorted_keys_[i]);
953       if (key_compare < 0)
954         return sorted_keys_.begin() + i;
955     }
956   }
957   return sorted_keys_.end();
958 }
959
960 void ProfileInfoCache::UpdateSortForProfileIndex(size_t index) {
961   base::string16 name = GetNameOfProfileAtIndex(index);
962
963   // Remove and reinsert key in |sorted_keys_| to alphasort.
964   std::string key = CacheKeyFromProfilePath(GetPathOfProfileAtIndex(index));
965   std::vector<std::string>::iterator key_it =
966       std::find(sorted_keys_.begin(), sorted_keys_.end(), key);
967   DCHECK(key_it != sorted_keys_.end());
968   sorted_keys_.erase(key_it);
969   sorted_keys_.insert(FindPositionForProfile(key, name), key);
970
971   content::NotificationService::current()->Notify(
972       chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED,
973       content::NotificationService::AllSources(),
974       content::NotificationService::NoDetails());
975 }
976
977 // static
978 std::vector<base::string16> ProfileInfoCache::GetProfileNames() {
979   std::vector<base::string16> names;
980   PrefService* local_state = g_browser_process->local_state();
981   const base::DictionaryValue* cache = local_state->GetDictionary(
982       prefs::kProfileInfoCache);
983   base::string16 name;
984   for (base::DictionaryValue::Iterator it(*cache); !it.IsAtEnd();
985        it.Advance()) {
986     const base::DictionaryValue* info = NULL;
987     it.value().GetAsDictionary(&info);
988     info->GetString(kNameKey, &name);
989     names.push_back(name);
990   }
991   return names;
992 }
993
994 // static
995 void ProfileInfoCache::RegisterPrefs(PrefRegistrySimple* registry) {
996   registry->RegisterDictionaryPref(prefs::kProfileInfoCache);
997 }