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