diff options
author | sail@chromium.org <sail@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-23 20:18:04 +0000 |
---|---|---|
committer | sail@chromium.org <sail@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-23 20:18:04 +0000 |
commit | cb114f14b97f51eb35991e98f4483a944ef1c323 (patch) | |
tree | f138832b59c0a6459feb02c141cd1ab9a223663c /chrome | |
parent | 74e8dc38735eabd488c05f663a5f641c1103d249 (diff) | |
download | chromium_src-cb114f14b97f51eb35991e98f4483a944ef1c323.zip chromium_src-cb114f14b97f51eb35991e98f4483a944ef1c323.tar.gz chromium_src-cb114f14b97f51eb35991e98f4483a944ef1c323.tar.bz2 |
Add GAIA info to profile info cache
BUG=91241
TEST=
Committed: http://src.chromium.org/viewvc/chrome?view=rev&revision=111254
Review URL: http://codereview.chromium.org/8587023
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@111401 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/browser/profiles/profile_info_cache.cc | 273 | ||||
-rw-r--r-- | chrome/browser/profiles/profile_info_cache.h | 36 | ||||
-rw-r--r-- | chrome/browser/profiles/profile_info_cache_unittest.cc | 264 | ||||
-rw-r--r-- | chrome/browser/profiles/profile_info_interface.h | 11 | ||||
-rw-r--r-- | chrome/common/chrome_notification_types.h | 4 | ||||
-rw-r--r-- | chrome/test/base/testing_profile_manager.cc | 4 | ||||
-rw-r--r-- | chrome/test/base/testing_profile_manager.h | 4 |
7 files changed, 567 insertions, 29 deletions
diff --git a/chrome/browser/profiles/profile_info_cache.cc b/chrome/browser/profiles/profile_info_cache.cc index c3b20e0..8b71dbb 100644 --- a/chrome/browser/profiles/profile_info_cache.cc +++ b/chrome/browser/profiles/profile_info_cache.cc @@ -4,10 +4,14 @@ #include "chrome/browser/profiles/profile_info_cache.h" +#include "base/bind.h" +#include "base/file_util.h" #include "base/format_macros.h" +#include "base/i18n/case_conversion.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/rand_util.h" +#include "base/stl_util.h" #include "base/string_number_conversions.h" #include "base/stringprintf.h" #include "base/utf_string_conversions.h" @@ -17,19 +21,30 @@ #include "chrome/browser/prefs/scoped_user_pref_update.h" #include "chrome/common/chrome_notification_types.h" #include "chrome/common/pref_names.h" +#include "content/public/browser/browser_thread.h" #include "content/public/browser/notification_service.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" +#include "third_party/skia/include/core/SkBitmap.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/image/image.h" +#include "ui/gfx/image/image_util.h" + +using content::BrowserThread; namespace { const char kNameKey[] = "name"; +const char kGAIANameKey[] = "gaia_name"; +const char kUseGAIANameKey[] = "use_gaia_name"; const char kUserNameKey[] = "user_name"; const char kAvatarIconKey[] = "avatar_icon"; +const char kUseGAIAPictureKey[] = "use_gaia_picture"; const char kBackgroundAppsKey[] = "background_apps"; +const char kHasMigratedToGAIAInfoKey[] = "has_migrated_to_gaia_info"; const char kDefaultUrlPrefix[] = "chrome://theme/IDR_PROFILE_AVATAR_"; +const char kGAIAPictureFileName[] = "Google Profile Picture.png"; const int kDefaultAvatarIconResources[] = { IDR_PROFILE_AVATAR_0, @@ -87,6 +102,59 @@ const int kDefaultNames[] = { IDS_DEFAULT_AVATAR_NAME_25 }; +typedef std::vector<unsigned char> ImageData; + +// Writes |data| to disk and takes ownership of the pointer. On completion +// |success| is set to true on success and false on failure. +void SaveBitmap(ImageData* data, + FilePath image_path, + bool* success) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + scoped_ptr<ImageData> data_owner(data); + *success = false; + + // Make sure the destination directory exists. + FilePath dir = image_path.DirName(); + if (!file_util::DirectoryExists(dir) && !file_util::CreateDirectory(dir)) { + LOG(ERROR) << "Failed to create parent directory."; + return; + } + + if (file_util::WriteFile(image_path, + reinterpret_cast<char*>(&(*data)[0]), + data->size()) == -1) { + LOG(ERROR) << "Failed to save image to file."; + return; + } + + *success = true; +} + +// Reads a PNG from disk and decodes it. If the bitmap was successfully read +// from disk the then |out_image| will contain the bitmap image, otherwise it +// will be NULL. +void ReadBitmap(FilePath image_path, + gfx::Image** out_image) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + *out_image = NULL; + + std::string image_data; + if (!file_util::ReadFileToString(image_path, &image_data)) { + LOG(ERROR) << "Failed to read PNG file from disk."; + return; + } + + const unsigned char* data = + reinterpret_cast<const unsigned char*>(image_data.data()); + gfx::Image* image = gfx::ImageFromPNGEncodedData(data, image_data.length()); + if (image == NULL) { + LOG(ERROR) << "Failed to decode PNG file."; + return; + } + + *out_image = image; +} + } // namespace ProfileInfoCache::ProfileInfoCache(PrefService* prefs, @@ -108,6 +176,8 @@ ProfileInfoCache::ProfileInfoCache(PrefService* prefs, } ProfileInfoCache::~ProfileInfoCache() { + STLDeleteContainerPairSecondPointers( + gaia_pictures_.begin(), gaia_pictures_.end()); } void ProfileInfoCache::AddProfileToCache(const FilePath& profile_path, @@ -186,19 +256,16 @@ size_t ProfileInfoCache::GetIndexOfProfileWithPath( } string16 ProfileInfoCache::GetNameOfProfileAtIndex(size_t index) const { + if (IsUsingGAIANameOfProfileAtIndex(index)) + return GetGAIANameOfProfileAtIndex(index); + string16 name; GetInfoForProfileAtIndex(index)->GetString(kNameKey, &name); return name; } FilePath ProfileInfoCache::GetPathOfProfileAtIndex(size_t index) const { - FilePath::StringType base_name; -#if defined(OS_POSIX) - base_name = sorted_keys_[index]; -#elif defined(OS_WIN) - base_name = ASCIIToWide(sorted_keys_[index]); -#endif - return user_data_dir_.Append(base_name); + return user_data_dir_.AppendASCII(sorted_keys_[index]); } string16 ProfileInfoCache::GetUserNameOfProfileAtIndex(size_t index) const { @@ -209,6 +276,9 @@ string16 ProfileInfoCache::GetUserNameOfProfileAtIndex(size_t index) const { const gfx::Image& ProfileInfoCache::GetAvatarIconOfProfileAtIndex( size_t index) const { + if (IsUsingGAIAPictureOfProfileAtIndex(index)) + return GetGAIAPictureOfProfileAtIndex(index); + int resource_id = GetDefaultAvatarIconResourceIDAtIndex( GetAvatarIconIndexOfProfileAtIndex(index)); return ResourceBundle::GetSharedInstance().GetImageNamed(resource_id); @@ -222,6 +292,76 @@ bool ProfileInfoCache::GetBackgroundStatusOfProfileAtIndex( return background_app_status; } +string16 ProfileInfoCache::GetGAIANameOfProfileAtIndex(size_t index) const { + string16 name; + GetInfoForProfileAtIndex(index)->GetString(kGAIANameKey, &name); + return name; +} + +bool ProfileInfoCache::IsUsingGAIANameOfProfileAtIndex(size_t index) const { + bool value = false; + GetInfoForProfileAtIndex(index)->GetBoolean(kUseGAIANameKey, &value); + return value; +} + +const gfx::Image& ProfileInfoCache::GetGAIAPictureOfProfileAtIndex( + size_t index) const { + FilePath path = GetPathOfProfileAtIndex(index); + std::string key = CacheKeyFromProfilePath(path); + if (gaia_pictures_.count(key)) { + return *gaia_pictures_[key]; + } + + // The GAIA picture is not in the cache yet. Load it from disk and return + // a blank picture for now. + gaia_pictures_[key] = new gfx::Image(new SkBitmap()); + + FilePath image_path = path.AppendASCII(kGAIAPictureFileName); + gfx::Image** image = new gfx::Image*; + BrowserThread::PostTaskAndReply(BrowserThread::FILE, FROM_HERE, + base::Bind(&ReadBitmap, image_path, image), + base::Bind(&ProfileInfoCache::OnGAIAPictureLoaded, + const_cast<ProfileInfoCache*>(this)->AsWeakPtr(), path, image)); + + return *gaia_pictures_[key]; +} + +void ProfileInfoCache::OnGAIAPictureLoaded(FilePath path, + gfx::Image** image) const { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + if (*image) { + std::string key = CacheKeyFromProfilePath(path); + delete gaia_pictures_[key]; + gaia_pictures_[key] = *image; + } + delete image; + + content::NotificationService::current()->Notify( + chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED, + content::NotificationService::AllSources(), + content::NotificationService::NoDetails()); +} + +void ProfileInfoCache::OnGAIAPictureSaved(FilePath path, bool* success) const { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + if (*success) { + content::NotificationService::current()->Notify( + chrome::NOTIFICATION_PROFILE_CACHE_PICTURE_SAVED, + content::NotificationService::AllSources(), + content::NotificationService::NoDetails()); + } + delete success; +} + +bool ProfileInfoCache::IsUsingGAIAPictureOfProfileAtIndex( + size_t index) const { + bool value = false; + GetInfoForProfileAtIndex(index)->GetBoolean(kUseGAIAPictureKey, &value); + return value; +} + size_t ProfileInfoCache::GetAvatarIconIndexOfProfileAtIndex(size_t index) const { std::string icon_url; @@ -236,40 +376,28 @@ size_t ProfileInfoCache::GetAvatarIconIndexOfProfileAtIndex(size_t index) void ProfileInfoCache::SetNameOfProfileAtIndex(size_t index, const string16& name) { + if (name == GetNameOfProfileAtIndex(index)) + return; + scoped_ptr<DictionaryValue> info(GetInfoForProfileAtIndex(index)->DeepCopy()); string16 old_name; info->GetString(kNameKey, &old_name); info->SetString(kNameKey, name); // This takes ownership of |info|. SetInfoForProfileAtIndex(index, info.release()); - - // Remove and reinsert key in |sorted_keys_| to alphasort. - std::string key = CacheKeyFromProfilePath(GetPathOfProfileAtIndex(index)); - std::vector<std::string>::iterator key_it = - std::find(sorted_keys_.begin(), sorted_keys_.end(), key); - DCHECK(key_it != sorted_keys_.end()); - sorted_keys_.erase(key_it); - sorted_keys_.insert(FindPositionForProfile(key, name), key); + UpdateSortForProfileIndex(index); FOR_EACH_OBSERVER(ProfileInfoCacheObserver, observer_list_, OnProfileNameChanged(old_name, name)); - - content::NotificationService::current()->Notify( - chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED, - content::NotificationService::AllSources(), - content::NotificationService::NoDetails()); } void ProfileInfoCache::SetUserNameOfProfileAtIndex(size_t index, const string16& user_name) { - string16 old_user_name; - const base::DictionaryValue* old_info = GetInfoForProfileAtIndex(index); - old_info->GetString(kUserNameKey, &old_user_name); - if (old_user_name == user_name) + if (user_name == GetUserNameOfProfileAtIndex(index)) return; - scoped_ptr<DictionaryValue> info(old_info->DeepCopy()); + scoped_ptr<DictionaryValue> info(GetInfoForProfileAtIndex(index)->DeepCopy()); info->SetString(kUserNameKey, user_name); // This takes ownership of |info|. SetInfoForProfileAtIndex(index, info.release()); @@ -294,6 +422,64 @@ void ProfileInfoCache::SetBackgroundStatusOfProfileAtIndex( SetInfoForProfileAtIndex(index, info.release()); } +void ProfileInfoCache::SetGAIANameOfProfileAtIndex(size_t index, + const string16& name) { + if (name == GetGAIANameOfProfileAtIndex(index)) + return; + + scoped_ptr<DictionaryValue> info(GetInfoForProfileAtIndex(index)->DeepCopy()); + info->SetString(kGAIANameKey, name); + // This takes ownership of |info|. + SetInfoForProfileAtIndex(index, info.release()); + UpdateSortForProfileIndex(index); +} + +void ProfileInfoCache::SetIsUsingGAIANameOfProfileAtIndex(size_t index, + bool value) { + if (value == IsUsingGAIANameOfProfileAtIndex(index)) + return; + + scoped_ptr<DictionaryValue> info(GetInfoForProfileAtIndex(index)->DeepCopy()); + info->SetBoolean(kUseGAIANameKey, value); + // This takes ownership of |info|. + SetInfoForProfileAtIndex(index, info.release()); + UpdateSortForProfileIndex(index); +} + +void ProfileInfoCache::SetGAIAPictureOfProfileAtIndex(size_t index, + const gfx::Image& image) { + FilePath path = GetPathOfProfileAtIndex(index); + std::string key = CacheKeyFromProfilePath(path); + + delete gaia_pictures_[key]; + gaia_pictures_[key] = new gfx::Image(image); + + scoped_ptr<ImageData> data(new ImageData); + if (!gfx::PNGEncodedDataFromImage(image, data.get())) { + LOG(ERROR) << "Failed to PNG encode the image."; + } else { + FilePath image_path = path.AppendASCII(kGAIAPictureFileName); + bool* success = new bool; + BrowserThread::PostTaskAndReply(BrowserThread::FILE, FROM_HERE, + base::Bind(&SaveBitmap, data.release(), image_path, success), + base::Bind(&ProfileInfoCache::OnGAIAPictureSaved, AsWeakPtr(), + path, success)); + } + + content::NotificationService::current()->Notify( + chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED, + content::NotificationService::AllSources(), + content::NotificationService::NoDetails()); +} + +void ProfileInfoCache::SetIsUsingGAIAPictureOfProfileAtIndex(size_t index, + bool value) { + scoped_ptr<DictionaryValue> info(GetInfoForProfileAtIndex(index)->DeepCopy()); + info->SetBoolean(kUseGAIAPictureKey, value); + // This takes ownership of |info|. + SetInfoForProfileAtIndex(index, info.release()); +} + string16 ProfileInfoCache::ChooseNameForNewProfile(size_t icon_index) { string16 name; for (int name_index = 1; ; ++name_index) { @@ -320,6 +506,22 @@ string16 ProfileInfoCache::ChooseNameForNewProfile(size_t icon_index) { } } +bool ProfileInfoCache::GetHasMigratedToGAIAInfoOfProfileAtIndex( + size_t index) const { + bool value = false; + GetInfoForProfileAtIndex(index)->GetBoolean( + kHasMigratedToGAIAInfoKey, &value); + return value; +} + +void ProfileInfoCache::SetHasMigratedToGAIAInfoOfProfileAtIndex( + size_t index, bool value) { + scoped_ptr<DictionaryValue> info(GetInfoForProfileAtIndex(index)->DeepCopy()); + info->SetBoolean(kHasMigratedToGAIAInfoKey, value); + // This takes ownership of |info|. + SetInfoForProfileAtIndex(index, info.release()); +} + bool ProfileInfoCache::IconIndexIsUnique(size_t icon_index) const { for (size_t i = 0; i < GetNumberOfProfiles(); ++i) { if (GetAvatarIconIndexOfProfileAtIndex(i) == icon_index) @@ -438,8 +640,10 @@ std::string ProfileInfoCache::CacheKeyFromProfilePath( std::vector<std::string>::iterator ProfileInfoCache::FindPositionForProfile( std::string search_key, const string16& search_name) { + string16 search_name_l = base::i18n::ToLower(search_name); for (size_t i = 0; i < GetNumberOfProfiles(); ++i) { - int name_compare = search_name.compare(GetNameOfProfileAtIndex(i)); + string16 name_l = base::i18n::ToLower(GetNameOfProfileAtIndex(i)); + int name_compare = search_name_l.compare(name_l); if (name_compare < 0) return sorted_keys_.begin() + i; if (name_compare == 0) { @@ -451,6 +655,23 @@ std::vector<std::string>::iterator ProfileInfoCache::FindPositionForProfile( return sorted_keys_.end(); } +void ProfileInfoCache::UpdateSortForProfileIndex(size_t index) { + string16 name = GetNameOfProfileAtIndex(index); + + // Remove and reinsert key in |sorted_keys_| to alphasort. + std::string key = CacheKeyFromProfilePath(GetPathOfProfileAtIndex(index)); + std::vector<std::string>::iterator key_it = + std::find(sorted_keys_.begin(), sorted_keys_.end(), key); + DCHECK(key_it != sorted_keys_.end()); + sorted_keys_.erase(key_it); + sorted_keys_.insert(FindPositionForProfile(key, name), key); + + content::NotificationService::current()->Notify( + chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED, + content::NotificationService::AllSources(), + content::NotificationService::NoDetails()); +} + // static std::vector<string16> ProfileInfoCache::GetProfileNames() { std::vector<string16> names; diff --git a/chrome/browser/profiles/profile_info_cache.h b/chrome/browser/profiles/profile_info_cache.h index 63e08ff..9687910 100644 --- a/chrome/browser/profiles/profile_info_cache.h +++ b/chrome/browser/profiles/profile_info_cache.h @@ -6,12 +6,14 @@ #define CHROME_BROWSER_PROFILES_PROFILE_INFO_CACHE_H_ #pragma once +#include <map> #include <string> #include <vector> #include "base/basictypes.h" #include "base/compiler_specific.h" #include "base/file_path.h" +#include "base/memory/weak_ptr.h" #include "base/observer_list.h" #include "base/string16.h" #include "chrome/browser/profiles/profile_info_cache_observer.h" @@ -30,7 +32,8 @@ class PrefService; // This class saves various information about profiles to local preferences. // This cache can be used to display a list of profiles without having to // actually load the profiles from disk. -class ProfileInfoCache : public ProfileInfoInterface { +class ProfileInfoCache : public ProfileInfoInterface, + public base::SupportsWeakPtr<ProfileInfoCache> { public: ProfileInfoCache(PrefService* prefs, const FilePath& user_data_dir); virtual ~ProfileInfoCache(); @@ -54,6 +57,12 @@ class ProfileInfoCache : public ProfileInfoInterface { size_t index) const OVERRIDE; virtual bool GetBackgroundStatusOfProfileAtIndex( size_t index) const OVERRIDE; + virtual string16 GetGAIANameOfProfileAtIndex(size_t index) const OVERRIDE; + virtual bool IsUsingGAIANameOfProfileAtIndex(size_t index) const OVERRIDE; + virtual const gfx::Image& GetGAIAPictureOfProfileAtIndex( + size_t index) const OVERRIDE; + virtual bool IsUsingGAIAPictureOfProfileAtIndex( + size_t index) const OVERRIDE; size_t GetAvatarIconIndexOfProfileAtIndex(size_t index) const; @@ -62,10 +71,24 @@ class ProfileInfoCache : public ProfileInfoInterface { void SetAvatarIconOfProfileAtIndex(size_t index, size_t icon_index); void SetBackgroundStatusOfProfileAtIndex(size_t index, bool running_background_apps); + void SetGAIANameOfProfileAtIndex(size_t index, const string16& name); + void SetIsUsingGAIANameOfProfileAtIndex(size_t index, bool value); + void SetGAIAPictureOfProfileAtIndex(size_t index, const gfx::Image& image); + void SetIsUsingGAIAPictureOfProfileAtIndex(size_t index, bool value); // Returns unique name that can be assigned to a newly created profile. string16 ChooseNameForNewProfile(size_t icon_index); + // Checks if the given profile has switched to using GAIA information + // for the profile name and picture. This pref is used to switch over + // to GAIA info the first time it is available. Afterwards this pref is + // checked to prevent clobbering the user's custom settings. + bool GetHasMigratedToGAIAInfoOfProfileAtIndex(size_t index) const; + + // Marks the given profile as having switched to using GAIA information + // for the profile name and picture. + void SetHasMigratedToGAIAInfoOfProfileAtIndex(size_t index, bool value); + // Returns an avatar icon index that can be assigned to a newly created // profile. Note that the icon may not be unique since there are a limited // set of default icons. @@ -116,12 +139,23 @@ class ProfileInfoCache : public ProfileInfoInterface { bool must_be_unique, size_t* out_icon_index) const; + // Updates the position of the profile at the given index so that the list + // of profiles is still sorted. + void UpdateSortForProfileIndex(size_t index); + + void OnGAIAPictureLoaded(FilePath path, gfx::Image** image) const; + void OnGAIAPictureSaved(FilePath path, bool* success) const; + PrefService* prefs_; std::vector<std::string> sorted_keys_; FilePath user_data_dir_; ObserverList<ProfileInfoCacheObserver> observer_list_; + // A cache of gaia profile pictures. This cache is updated lazily so it needs + // to be mutable. + mutable std::map<std::string, gfx::Image*> gaia_pictures_; + DISALLOW_COPY_AND_ASSIGN(ProfileInfoCache); }; diff --git a/chrome/browser/profiles/profile_info_cache_unittest.cc b/chrome/browser/profiles/profile_info_cache_unittest.cc index be03de0..b6227e0 100644 --- a/chrome/browser/profiles/profile_info_cache_unittest.cc +++ b/chrome/browser/profiles/profile_info_cache_unittest.cc @@ -8,25 +8,78 @@ #include "base/utf_string_conversions.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/profiles/profile_manager.h" +#include "chrome/common/chrome_notification_types.h" #include "chrome/test/base/testing_browser_process.h" #include "chrome/test/base/testing_pref_service.h" #include "chrome/test/base/testing_profile_manager.h" +#include "chrome/test/base/ui_test_utils.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" +#include "content/test/test_browser_thread.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/image/image.h" +using content::BrowserThread; + namespace { +bool IsEqual(const gfx::Image& image1, + const gfx::Image& image2) { + const SkBitmap& bmp1 = *image1.ToSkBitmap(); + const SkBitmap& bmp2 = *image2.ToSkBitmap(); + + if (bmp1.width() != bmp2.width() || + bmp1.height() != bmp2.height() || + bmp1.config() != SkBitmap::kARGB_8888_Config || + bmp2.config() != SkBitmap::kARGB_8888_Config) { + return false; + } + + SkAutoLockPixels lock1(bmp1); + SkAutoLockPixels lock2(bmp2); + if (!bmp1.getPixels() || !bmp2.getPixels()) + return false; + + for (int y = 0; y < bmp1.height(); ++y) { + for (int x = 0; x < bmp1.width(); ++x) { + if (*bmp1.getAddr32(x,y) != *bmp2.getAddr32(x,y)) + return false; + } + } + + return true; +} + +gfx::Image CreateTestImage() { + SkBitmap bitmap; + bitmap.setConfig(SkBitmap::kARGB_8888_Config, 100, 50); + bitmap.allocPixels(); + bitmap.eraseRGB(0xff, 0, 0); + return gfx::Image(new SkBitmap(bitmap)); +} + class ProfileInfoCacheUnittests : public testing::Test { protected: ProfileInfoCacheUnittests() : testing_profile_manager_( - static_cast<TestingBrowserProcess*>(g_browser_process)) { + static_cast<TestingBrowserProcess*>(g_browser_process)), + ui_thread_(BrowserThread::UI, &ui_loop_), + file_thread_(BrowserThread::FILE) { } virtual void SetUp() OVERRIDE { ASSERT_TRUE(testing_profile_manager_.SetUp()); + file_thread_.Start(); + } + + virtual void TearDown() OVERRIDE { + // Process all tasks on the FILE thread. + file_thread_.Stop(); + // The FILE thread might post tasks back to the UI thread so drain the UI + // thread too. + ui_loop_.RunAllPending(); } ProfileInfoCache* GetCache() { @@ -45,8 +98,15 @@ class ProfileInfoCacheUnittests : public testing::Test { #endif } + void ResetCache() { + testing_profile_manager_.DeleteProfileInfoCache(); + } + private: + MessageLoopForUI ui_loop_; TestingProfileManager testing_profile_manager_; + content::TestBrowserThread ui_thread_; + content::TestBrowserThread file_thread_; }; TEST_F(ProfileInfoCacheUnittests, AddProfiles) { @@ -60,7 +120,10 @@ TEST_F(ProfileInfoCacheUnittests, AddProfiles) { const SkBitmap& icon = ResourceBundle::GetSharedInstance().GetImageNamed( ProfileInfoCache::GetDefaultAvatarIconResourceIDAtIndex(i)); - GetCache()->AddProfileToCache(profile_path, profile_name, string16(), 0); + GetCache()->AddProfileToCache(profile_path, profile_name, string16(), i); + GetCache()->SetBackgroundStatusOfProfileAtIndex(i, true); + string16 gaia_name = ASCIIToUTF16(StringPrintf("gaia_%ud", i)); + GetCache()->SetGAIANameOfProfileAtIndex(i, gaia_name); EXPECT_EQ(i + 1, GetCache()->GetNumberOfProfiles()); EXPECT_EQ(profile_name, GetCache()->GetNameOfProfileAtIndex(i)); @@ -69,6 +132,23 @@ TEST_F(ProfileInfoCacheUnittests, AddProfiles) { EXPECT_EQ(icon.width(), actual_icon.width()); EXPECT_EQ(icon.height(), actual_icon.height()); } + + // Reset the cache and test the it reloads correctly. + ResetCache(); + + EXPECT_EQ(4u, GetCache()->GetNumberOfProfiles()); + for (uint32 i = 0; i < 4; ++i) { + std::string base_name = StringPrintf("path_%ud", i); + FilePath profile_path = + GetUserDataDir().Append(StringToFilePath(base_name)); + EXPECT_EQ(i, GetCache()->GetIndexOfProfileWithPath(profile_path)); + string16 profile_name = ASCIIToUTF16(StringPrintf("name_%ud", i)); + EXPECT_EQ(profile_name, GetCache()->GetNameOfProfileAtIndex(i)); + EXPECT_EQ(i, GetCache()->GetAvatarIconIndexOfProfileAtIndex(i)); + EXPECT_EQ(true, GetCache()->GetBackgroundStatusOfProfileAtIndex(i)); + string16 gaia_name = ASCIIToUTF16(StringPrintf("gaia_%ud", i)); + EXPECT_EQ(gaia_name, GetCache()->GetGAIANameOfProfileAtIndex(i)); + } } TEST_F(ProfileInfoCacheUnittests, DeleteProfile) { @@ -114,6 +194,47 @@ TEST_F(ProfileInfoCacheUnittests, MutateProfile) { GetCache()->GetAvatarIconOfProfileAtIndex(1); } +TEST_F(ProfileInfoCacheUnittests, Sort) { + string16 name_a = ASCIIToUTF16("apple"); + GetCache()->AddProfileToCache(GetUserDataDir().Append( + StringToFilePath("path_a")), name_a, string16(), 0); + + string16 name_c = ASCIIToUTF16("cat"); + GetCache()->AddProfileToCache(GetUserDataDir().Append( + StringToFilePath("path_c")), name_c, string16(), 0); + + // Sanity check the initial order. + EXPECT_EQ(name_a, GetCache()->GetNameOfProfileAtIndex(0)); + EXPECT_EQ(name_c, GetCache()->GetNameOfProfileAtIndex(1)); + + // Add a new profile (start with a capital to test case insensitive sorting. + string16 name_b = ASCIIToUTF16("Banana"); + GetCache()->AddProfileToCache(GetUserDataDir().Append( + StringToFilePath("path_b")), name_b, string16(), 0); + + // Verify the new order. + EXPECT_EQ(name_a, GetCache()->GetNameOfProfileAtIndex(0)); + EXPECT_EQ(name_b, GetCache()->GetNameOfProfileAtIndex(1)); + EXPECT_EQ(name_c, GetCache()->GetNameOfProfileAtIndex(2)); + + // Change the name of an existing profile. + name_a = UTF8ToUTF16("dog"); + GetCache()->SetNameOfProfileAtIndex(0, name_a); + + // Verify the new order. + EXPECT_EQ(name_b, GetCache()->GetNameOfProfileAtIndex(0)); + EXPECT_EQ(name_c, GetCache()->GetNameOfProfileAtIndex(1)); + EXPECT_EQ(name_a, GetCache()->GetNameOfProfileAtIndex(2)); + + // Delete a profile. + GetCache()->DeleteProfileFromCache(GetUserDataDir().Append( + StringToFilePath("path_c"))); + + // Verify the new order. + EXPECT_EQ(name_b, GetCache()->GetNameOfProfileAtIndex(0)); + EXPECT_EQ(name_a, GetCache()->GetNameOfProfileAtIndex(1)); +} + TEST_F(ProfileInfoCacheUnittests, BackgroundModeStatus) { GetCache()->AddProfileToCache( GetUserDataDir().Append(StringToFilePath("path_1")), @@ -141,4 +262,143 @@ TEST_F(ProfileInfoCacheUnittests, BackgroundModeStatus) { EXPECT_FALSE(GetCache()->GetBackgroundStatusOfProfileAtIndex(1)); } +TEST_F(ProfileInfoCacheUnittests, HasMigrated) { + GetCache()->AddProfileToCache( + GetUserDataDir().Append(StringToFilePath("path_1")), + ASCIIToUTF16("name_1"), string16(), 0); + GetCache()->AddProfileToCache( + GetUserDataDir().Append(StringToFilePath("path_2")), + ASCIIToUTF16("name_2"), string16(), 0); + + // Sanity check. + EXPECT_FALSE(GetCache()->GetHasMigratedToGAIAInfoOfProfileAtIndex(0)); + EXPECT_FALSE(GetCache()->GetHasMigratedToGAIAInfoOfProfileAtIndex(1)); + + // Set migrated state for 2nd profile. + GetCache()->SetHasMigratedToGAIAInfoOfProfileAtIndex(1, true); + EXPECT_FALSE(GetCache()->GetHasMigratedToGAIAInfoOfProfileAtIndex(0)); + EXPECT_TRUE(GetCache()->GetHasMigratedToGAIAInfoOfProfileAtIndex(1)); + + // Set migrated state for 1st profile. + GetCache()->SetHasMigratedToGAIAInfoOfProfileAtIndex(0, true); + EXPECT_TRUE(GetCache()->GetHasMigratedToGAIAInfoOfProfileAtIndex(0)); + EXPECT_TRUE(GetCache()->GetHasMigratedToGAIAInfoOfProfileAtIndex(1)); + + // Unset migrated state for 2nd profile. + GetCache()->SetHasMigratedToGAIAInfoOfProfileAtIndex(1, false); + EXPECT_TRUE(GetCache()->GetHasMigratedToGAIAInfoOfProfileAtIndex(0)); + EXPECT_FALSE(GetCache()->GetHasMigratedToGAIAInfoOfProfileAtIndex(1)); +} + +TEST_F(ProfileInfoCacheUnittests, GAIAName) { + GetCache()->AddProfileToCache( + GetUserDataDir().Append(StringToFilePath("path_1")), + ASCIIToUTF16("name_1"), string16(), 0); + string16 profile_name(ASCIIToUTF16("profile name 2")); + GetCache()->AddProfileToCache( + GetUserDataDir().Append(StringToFilePath("path_2")), + profile_name, string16(), 0); + + // Sanity check. + EXPECT_TRUE(GetCache()->GetGAIANameOfProfileAtIndex(0).empty()); + EXPECT_TRUE(GetCache()->GetGAIANameOfProfileAtIndex(1).empty()); + EXPECT_FALSE(GetCache()->IsUsingGAIANameOfProfileAtIndex(0)); + EXPECT_FALSE(GetCache()->IsUsingGAIANameOfProfileAtIndex(1)); + + // Set GAIA name. + string16 gaia_name(ASCIIToUTF16("Pat Smith")); + GetCache()->SetGAIANameOfProfileAtIndex(1, gaia_name); + EXPECT_TRUE(GetCache()->GetGAIANameOfProfileAtIndex(0).empty()); + EXPECT_EQ(gaia_name, GetCache()->GetGAIANameOfProfileAtIndex(1)); + EXPECT_EQ(profile_name, GetCache()->GetNameOfProfileAtIndex(1)); + + // Use GAIA name as profile name. + GetCache()->SetIsUsingGAIANameOfProfileAtIndex(1, true); + + EXPECT_EQ(gaia_name, GetCache()->GetNameOfProfileAtIndex(1)); + EXPECT_EQ(gaia_name, GetCache()->GetGAIANameOfProfileAtIndex(1)); + + // Don't use GAIA name as profile name. + GetCache()->SetIsUsingGAIANameOfProfileAtIndex(1, false); + EXPECT_EQ(profile_name, GetCache()->GetNameOfProfileAtIndex(1)); + EXPECT_EQ(gaia_name, GetCache()->GetGAIANameOfProfileAtIndex(1)); +} + +TEST_F(ProfileInfoCacheUnittests, GAIAPicture) { + GetCache()->AddProfileToCache( + GetUserDataDir().Append(StringToFilePath("path_1")), + ASCIIToUTF16("name_1"), string16(), 0); + GetCache()->AddProfileToCache( + GetUserDataDir().Append(StringToFilePath("path_2")), + ASCIIToUTF16("name_2"), string16(), 0); + + // Sanity check. + EXPECT_TRUE( + GetCache()->GetGAIAPictureOfProfileAtIndex(0).ToSkBitmap()->isNull()); + EXPECT_TRUE( + GetCache()->GetGAIAPictureOfProfileAtIndex(1).ToSkBitmap()->isNull()); + EXPECT_FALSE(GetCache()->IsUsingGAIAPictureOfProfileAtIndex(0)); + EXPECT_FALSE(GetCache()->IsUsingGAIAPictureOfProfileAtIndex(1)); + + // The profile icon should be the default one. + int id = ProfileInfoCache::GetDefaultAvatarIconResourceIDAtIndex(0); + const gfx::Image& profile_image( + ResourceBundle::GetSharedInstance().GetImageNamed(id)); + EXPECT_TRUE(IsEqual( + profile_image, GetCache()->GetAvatarIconOfProfileAtIndex(1))); + + // Set GAIA picture. + gfx::Image gaia_image(CreateTestImage()); + GetCache()->SetGAIAPictureOfProfileAtIndex(1, gaia_image); + EXPECT_TRUE( + GetCache()->GetGAIAPictureOfProfileAtIndex(0).ToSkBitmap()->isNull()); + EXPECT_TRUE(IsEqual( + gaia_image, GetCache()->GetGAIAPictureOfProfileAtIndex(1))); + EXPECT_TRUE(IsEqual( + profile_image, GetCache()->GetAvatarIconOfProfileAtIndex(1))); + + // Use GAIA picture as profile picture. + GetCache()->SetIsUsingGAIAPictureOfProfileAtIndex(1, true); + EXPECT_TRUE(IsEqual( + gaia_image, GetCache()->GetGAIAPictureOfProfileAtIndex(1))); + EXPECT_TRUE(IsEqual( + gaia_image, GetCache()->GetAvatarIconOfProfileAtIndex(1))); + + // Don't use GAIA picture as profile picture. + GetCache()->SetIsUsingGAIAPictureOfProfileAtIndex(1, false); + EXPECT_TRUE(IsEqual( + gaia_image, GetCache()->GetGAIAPictureOfProfileAtIndex(1))); + EXPECT_TRUE(IsEqual( + profile_image, GetCache()->GetAvatarIconOfProfileAtIndex(1))); +} + +TEST_F(ProfileInfoCacheUnittests, PersistGAIAPicture) { + GetCache()->AddProfileToCache( + GetUserDataDir().Append(StringToFilePath("path_1")), + ASCIIToUTF16("name_1"), string16(), 0); + gfx::Image gaia_image(CreateTestImage()); + + ui_test_utils::WindowedNotificationObserver save_observer( + chrome::NOTIFICATION_PROFILE_CACHE_PICTURE_SAVED, + content::NotificationService::AllSources()); + GetCache()->SetGAIAPictureOfProfileAtIndex(0, gaia_image); + EXPECT_TRUE(IsEqual( + gaia_image, GetCache()->GetGAIAPictureOfProfileAtIndex(0))); + + // Wait for the file to be written to disk then reset the cache. + save_observer.Wait(); + ResetCache(); + + // Try to get the GAIA picture. This should return NULL until the read from + // disk is done. + ui_test_utils::WindowedNotificationObserver read_observer( + chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED, + content::NotificationService::AllSources()); + EXPECT_TRUE( + GetCache()->GetGAIAPictureOfProfileAtIndex(0).ToSkBitmap()->isNull()); + read_observer.Wait(); + EXPECT_TRUE(IsEqual( + gaia_image, GetCache()->GetGAIAPictureOfProfileAtIndex(0))); +} + } // namespace diff --git a/chrome/browser/profiles/profile_info_interface.h b/chrome/browser/profiles/profile_info_interface.h index 6a08f9a..a7318de 100644 --- a/chrome/browser/profiles/profile_info_interface.h +++ b/chrome/browser/profiles/profile_info_interface.h @@ -36,6 +36,17 @@ class ProfileInfoInterface { virtual bool GetBackgroundStatusOfProfileAtIndex( size_t index) const = 0; + virtual string16 GetGAIANameOfProfileAtIndex(size_t index) const = 0; + + // Checks if the GAIA name should be used as the profile's name. + virtual bool IsUsingGAIANameOfProfileAtIndex(size_t index) const = 0; + + virtual const gfx::Image& GetGAIAPictureOfProfileAtIndex( + size_t index) const = 0; + + // Checks if the GAIA picture should be used as the profile's avatar icon. + virtual bool IsUsingGAIAPictureOfProfileAtIndex(size_t index) const = 0; + protected: virtual ~ProfileInfoInterface() {} }; diff --git a/chrome/common/chrome_notification_types.h b/chrome/common/chrome_notification_types.h index e1ad04d..5e96ad8 100644 --- a/chrome/common/chrome_notification_types.h +++ b/chrome/common/chrome_notification_types.h @@ -929,6 +929,10 @@ enum NotificationType { // Sent when the cached profile info has changed. NOTIFICATION_PROFILE_CACHED_INFO_CHANGED, + // Sent when the cached profile has finished writing a profile picture to + // disk. + NOTIFICATION_PROFILE_CACHE_PICTURE_SAVED, + // Sent when the browser enters or exits fullscreen mode. NOTIFICATION_FULLSCREEN_CHANGED, diff --git a/chrome/test/base/testing_profile_manager.cc b/chrome/test/base/testing_profile_manager.cc index f8273ee..328617a 100644 --- a/chrome/test/base/testing_profile_manager.cc +++ b/chrome/test/base/testing_profile_manager.cc @@ -97,6 +97,10 @@ ProfileInfoCache* TestingProfileManager::profile_info_cache() { return &profile_manager_->GetProfileInfoCache(); } +void TestingProfileManager::DeleteProfileInfoCache() { + profile_manager_->profile_info_cache_.reset(NULL); +} + void TestingProfileManager::SetUpInternal() { ASSERT_FALSE(browser_process_->profile_manager()) << "ProfileManager already exists"; diff --git a/chrome/test/base/testing_profile_manager.h b/chrome/test/base/testing_profile_manager.h index ae6a0ed..c055f34 100644 --- a/chrome/test/base/testing_profile_manager.h +++ b/chrome/test/base/testing_profile_manager.h @@ -52,6 +52,10 @@ class TestingProfileManager { // Deletes a TestingProfile from the profile subsystem. void DeleteTestingProfile(const std::string& profile_name); + // Deletes the cache instance. This is useful for testing that the cache is + // properly persisting data. + void DeleteProfileInfoCache(); + // Helper accessors. ProfileManager* profile_manager(); ProfileInfoCache* profile_info_cache(); |