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