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