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