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