diff options
author | ivankr@chromium.org <ivankr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-10-15 15:53:45 +0000 |
---|---|---|
committer | ivankr@chromium.org <ivankr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-10-15 15:53:45 +0000 |
commit | c4e418facea4f437f1a497f08c5adce76105725b (patch) | |
tree | 1f8e0c54dee8c46876286d328566cc06611beb53 | |
parent | 3acc325be40dff489fa048673a9da41fbf3d425c (diff) | |
download | chromium_src-c4e418facea4f437f1a497f08c5adce76105725b.zip chromium_src-c4e418facea4f437f1a497f08c5adce76105725b.tar.gz chromium_src-c4e418facea4f437f1a497f08c5adce76105725b.tar.bz2 |
[cros] Migrate user avatars from PNG to JPG format.
Also:
*) Decoupled image handling code from UserManager to UserImageManager (lots of refactoring).
*) Force Local State store on user login (and session start, for new users).
BUG=145613,154048,154370,154365,154366
Review URL: https://chromiumcodereview.appspot.com/11027064
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@161860 0039d316-1c4b-4281-b951-d872f2087c98
49 files changed, 1671 insertions, 1049 deletions
diff --git a/chrome/browser/automation/automation_provider_observers_chromeos.cc b/chrome/browser/automation/automation_provider_observers_chromeos.cc index 440091f..fb9c87a 100644 --- a/chrome/browser/automation/automation_provider_observers_chromeos.cc +++ b/chrome/browser/automation/automation_provider_observers_chromeos.cc @@ -12,6 +12,7 @@ #include "chrome/browser/chromeos/login/existing_user_controller.h" #include "chrome/browser/chromeos/login/screen_locker.h" #include "chrome/browser/chromeos/login/user_image.h" +#include "chrome/browser/chromeos/login/user_image_manager.h" #include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/chromeos/login/wizard_controller.h" #include "chrome/browser/chromeos/login/wizard_screen.h" @@ -496,8 +497,8 @@ void PhotoCaptureObserver::OnCapturingStopped( return; } - const chromeos::User& user = user_manager->GetLoggedInUser(); - if (user.email().empty()) { + const chromeos::User* user = user_manager->GetLoggedInUser(); + if (user->email().empty()) { if (automation_) { AutomationJSONReply( automation_, @@ -509,8 +510,8 @@ void PhotoCaptureObserver::OnCapturingStopped( // Set up an observer for UserManager (it will delete itself). user_manager->AddObserver(this); - user_manager->SaveUserImage( - user.email(), chromeos::UserImage::CreateAndEncode(photo)); + user_manager->GetUserImageManager()->SaveUserImage( + user->email(), chromeos::UserImage::CreateAndEncode(photo)); } void PhotoCaptureObserver::LocalStateChanged( diff --git a/chrome/browser/automation/testing_automation_provider_chromeos.cc b/chrome/browser/automation/testing_automation_provider_chromeos.cc index e1fcd7f..1b8eeca 100644 --- a/chrome/browser/automation/testing_automation_provider_chromeos.cc +++ b/chrome/browser/automation/testing_automation_provider_chromeos.cc @@ -313,11 +313,11 @@ void TestingAutomationProvider::GetLoginInfo(DictionaryValue* args, return_value->SetBoolean("is_logged_in", user_manager->IsUserLoggedIn()); return_value->SetBoolean("is_screen_locked", screen_locker); if (user_manager->IsUserLoggedIn()) { - const User& user = user_manager->GetLoggedInUser(); + const User* user = user_manager->GetLoggedInUser(); return_value->SetBoolean("is_guest", user_manager->IsLoggedInAsGuest()); - return_value->SetString("email", user.email()); - return_value->SetString("display_email", user.display_email()); - switch (user.image_index()) { + return_value->SetString("email", user->email()); + return_value->SetString("display_email", user->display_email()); + switch (user->image_index()) { case User::kExternalImageIndex: return_value->SetString("user_image", "file"); break; @@ -327,7 +327,7 @@ void TestingAutomationProvider::GetLoginInfo(DictionaryValue* args, break; default: - return_value->SetInteger("user_image", user.image_index()); + return_value->SetInteger("user_image", user->image_index()); break; } } diff --git a/chrome/browser/chromeos/cros/onc_network_parser.cc b/chrome/browser/chromeos/cros/onc_network_parser.cc index f0ac5db..213b716 100644 --- a/chrome/browser/chromeos/cros/onc_network_parser.cc +++ b/chrome/browser/chromeos/cros/onc_network_parser.cc @@ -687,13 +687,13 @@ std::string OncNetworkParser::GetUserExpandedValue( if (!UserManager::Get()->IsUserLoggedIn()) return string_value; - const User& logged_in_user(UserManager::Get()->GetLoggedInUser()); + const User* logged_in_user = UserManager::Get()->GetLoggedInUser(); ReplaceSubstringsAfterOffset(&string_value, 0, onc::substitutes::kLoginIDField, - logged_in_user.GetAccountName(false)); + logged_in_user->GetAccountName(false)); ReplaceSubstringsAfterOffset(&string_value, 0, onc::substitutes::kEmailField, - logged_in_user.email()); + logged_in_user->email()); return string_value; } diff --git a/chrome/browser/chromeos/extensions/wallpaper_private_api.cc b/chrome/browser/chromeos/extensions/wallpaper_private_api.cc index 7d44c40..0bdb21b 100644 --- a/chrome/browser/chromeos/extensions/wallpaper_private_api.cc +++ b/chrome/browser/chromeos/extensions/wallpaper_private_api.cc @@ -152,7 +152,7 @@ bool WallpaperSetWallpaperFunction::RunImpl() { return false; // Gets email address while at UI thread. - email_ = chromeos::UserManager::Get()->GetLoggedInUser().email(); + email_ = chromeos::UserManager::Get()->GetLoggedInUser()->email(); image_data_.assign(input->GetBuffer(), input->GetSize()); if (wallpaper_decoder_) @@ -252,7 +252,7 @@ bool WallpaperSetCustomWallpaperFunction::RunImpl() { layout_ = ash::GetLayoutEnum(layout_string); // Gets email address while at UI thread. - email_ = chromeos::UserManager::Get()->GetLoggedInUser().email(); + email_ = chromeos::UserManager::Get()->GetLoggedInUser()->email(); image_data_.assign(input->GetBuffer(), input->GetSize()); if (wallpaper_decoder_) diff --git a/chrome/browser/chromeos/login/login_utils.cc b/chrome/browser/chromeos/login/login_utils.cc index b177c05..8ee7f43 100644 --- a/chrome/browser/chromeos/login/login_utils.cc +++ b/chrome/browser/chromeos/login/login_utils.cc @@ -540,7 +540,7 @@ void LoginUtilsImpl::OnProfileCreated( google_services_username.Init(prefs::kGoogleServicesUsername, user_profile->GetPrefs(), NULL); google_services_username.SetValue( - UserManager::Get()->GetLoggedInUser().display_email()); + UserManager::Get()->GetLoggedInUser()->display_email()); // Make sure we flip every profile to not share proxies if the user hasn't // specified so explicitly. const PrefService::Preference* use_shared_proxies_pref = @@ -661,7 +661,7 @@ void LoginUtilsImpl::StartSignedInServices( // Make sure SigninManager is connected to our current user (this should // happen automatically because we set kGoogleServicesUsername in // OnProfileCreated()). - DCHECK_EQ(UserManager::Get()->GetLoggedInUser().display_email(), + DCHECK_EQ(UserManager::Get()->GetLoggedInUser()->display_email(), signin->GetAuthenticatedUsername()); static bool initialized = false; if (!initialized) { @@ -1030,7 +1030,7 @@ bool LoginUtilsImpl::ReadOAuth1AccessToken(Profile* user_profile, std::string* secret) { // Skip reading oauth token if user does not have a valid status. if (UserManager::Get()->IsUserLoggedIn() && - UserManager::Get()->GetLoggedInUser().oauth_token_status() != + UserManager::Get()->GetLoggedInUser()->oauth_token_status() != User::OAUTH_TOKEN_STATUS_VALID) { return false; } @@ -1062,6 +1062,7 @@ void LoginUtilsImpl::StoreOAuth1AccessToken(Profile* user_profile, std::string encrypted_secret = CrosLibrary::Get()->GetCertLibrary()->EncryptToken(secret); PrefService* pref_service = user_profile->GetPrefs(); + User* user = UserManager::Get()->GetLoggedInUser(); if (!encrypted_token.empty() && !encrypted_secret.empty()) { pref_service->SetString(prefs::kOAuth1Token, encrypted_token); pref_service->SetString(prefs::kOAuth1Secret, encrypted_secret); @@ -1069,15 +1070,13 @@ void LoginUtilsImpl::StoreOAuth1AccessToken(Profile* user_profile, // ...then record the presence of valid OAuth token for this account in // local state as well. UserManager::Get()->SaveUserOAuthStatus( - UserManager::Get()->GetLoggedInUser().email(), - User::OAUTH_TOKEN_STATUS_VALID); + user->email(), User::OAUTH_TOKEN_STATUS_VALID); } else { LOG(WARNING) << "Failed to get OAuth1 token/secret encrypted."; // Set the OAuth status invalid so that the user will go through full // GAIA login next time. UserManager::Get()->SaveUserOAuthStatus( - UserManager::Get()->GetLoggedInUser().email(), - User::OAUTH_TOKEN_STATUS_INVALID); + user->email(), User::OAUTH_TOKEN_STATUS_INVALID); } } @@ -1096,7 +1095,7 @@ void LoginUtilsImpl::FetchCredentials(Profile* user_profile, const std::string& secret) { oauth_login_verifier_.reset(new OAuthLoginVerifier( this, user_profile, token, secret, - UserManager::Get()->GetLoggedInUser().email())); + UserManager::Get()->GetLoggedInUser()->email())); oauth_login_verifier_->StartOAuthVerification(); } diff --git a/chrome/browser/chromeos/login/login_utils_browsertest.cc b/chrome/browser/chromeos/login/login_utils_browsertest.cc index f7f6ad7..b31daea 100644 --- a/chrome/browser/chromeos/login/login_utils_browsertest.cc +++ b/chrome/browser/chromeos/login/login_utils_browsertest.cc @@ -422,7 +422,7 @@ TEST_F(LoginUtilsTest, NormalLoginDoesntBlock) { EXPECT_TRUE(prepared_profile_); ASSERT_TRUE(user_manager->IsUserLoggedIn()); - EXPECT_EQ(kUsername, user_manager->GetLoggedInUser().email()); + EXPECT_EQ(kUsername, user_manager->GetLoggedInUser()->email()); } TEST_F(LoginUtilsTest, EnterpriseLoginDoesntBlockForNormalUser) { @@ -444,7 +444,7 @@ TEST_F(LoginUtilsTest, EnterpriseLoginDoesntBlockForNormalUser) { EXPECT_TRUE(prepared_profile_); ASSERT_TRUE(user_manager->IsUserLoggedIn()); - EXPECT_EQ(kUsernameOtherDomain, user_manager->GetLoggedInUser().email()); + EXPECT_EQ(kUsernameOtherDomain, user_manager->GetLoggedInUser()->email()); } TEST_F(LoginUtilsTest, OAuth1TokenFetchFailureUnblocksRefreshPolicies) { @@ -466,7 +466,7 @@ TEST_F(LoginUtilsTest, OAuth1TokenFetchFailureUnblocksRefreshPolicies) { profile_creation_observer.Wait(); EXPECT_TRUE(prepared_profile_); ASSERT_TRUE(user_manager->IsUserLoggedIn()); - EXPECT_EQ(kUsername, user_manager->GetLoggedInUser().email()); + EXPECT_EQ(kUsername, user_manager->GetLoggedInUser()->email()); // 2. Get the pending oauth1 access token fetcher. net::TestURLFetcher* fetcher = diff --git a/chrome/browser/chromeos/login/mock_user_image_manager.cc b/chrome/browser/chromeos/login/mock_user_image_manager.cc new file mode 100644 index 0000000..07482350 --- /dev/null +++ b/chrome/browser/chromeos/login/mock_user_image_manager.cc @@ -0,0 +1,15 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/chromeos/login/mock_user_image_manager.h" + +namespace chromeos { + +MockUserImageManager::MockUserImageManager() { +} + +MockUserImageManager::~MockUserImageManager() { +} + +} // namespace chromeos diff --git a/chrome/browser/chromeos/login/mock_user_image_manager.h b/chrome/browser/chromeos/login/mock_user_image_manager.h new file mode 100644 index 0000000..8bcd97b --- /dev/null +++ b/chrome/browser/chromeos/login/mock_user_image_manager.h @@ -0,0 +1,34 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_MOCK_USER_IMAGE_MANAGER_H_ +#define CHROME_BROWSER_CHROMEOS_LOGIN_MOCK_USER_IMAGE_MANAGER_H_ + +#include <string> + +#include "base/file_path.h" +#include "chrome/browser/chromeos/login/user_image.h" +#include "chrome/browser/chromeos/login/user_image_manager.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace chromeos { + +class MockUserImageManager : public UserImageManager { + public: + MockUserImageManager(); + virtual ~MockUserImageManager(); + + MOCK_METHOD2(SaveUserDefaultImageIndex, void(const std::string&, int)); + MOCK_METHOD2(SaveUserImage, void(const std::string&, const UserImage&)); + MOCK_METHOD2(SaveUserImageFromFile, void(const std::string&, + const FilePath&)); + MOCK_METHOD1(SaveUserImageFromProfileImage, void(const std::string&)); + MOCK_METHOD1(DownloadProfileImage, void(const std::string&)); + MOCK_CONST_METHOD0(DownloadedProfileImage, const gfx::ImageSkia& (void)); + +}; + +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_LOGIN_MOCK_USER_IMAGE_MANAGER_H_ diff --git a/chrome/browser/chromeos/login/mock_user_manager.cc b/chrome/browser/chromeos/login/mock_user_manager.cc index a1655c0..035d8f3 100644 --- a/chrome/browser/chromeos/login/mock_user_manager.cc +++ b/chrome/browser/chromeos/login/mock_user_manager.cc @@ -12,12 +12,16 @@ MockUserManager::~MockUserManager() { delete user_; } -const User& MockUserManager::GetLoggedInUser() const { - return *user_; +const User* MockUserManager::GetLoggedInUser() const { + return user_; } -User& MockUserManager::GetLoggedInUser() { - return *user_; +User* MockUserManager::GetLoggedInUser() { + return user_; +} + +UserImageManager* MockUserManager::GetUserImageManager() { + return user_image_manager_.get(); } // Creates a new User instance. diff --git a/chrome/browser/chromeos/login/mock_user_manager.h b/chrome/browser/chromeos/login/mock_user_manager.h index 8bc31b1..147dac1 100644 --- a/chrome/browser/chromeos/login/mock_user_manager.h +++ b/chrome/browser/chromeos/login/mock_user_manager.h @@ -9,6 +9,7 @@ #include "base/file_path.h" #include "base/memory/weak_ptr.h" +#include "chrome/browser/chromeos/login/mock_user_image_manager.h" #include "chrome/browser/chromeos/login/user_image.h" #include "chrome/browser/chromeos/login/user_manager.h" #include "testing/gmock/include/gmock/gmock.h" @@ -40,14 +41,8 @@ class MockUserManager : public UserManager { MOCK_METHOD2(SaveLoggedInUserWallpaperProperties, void(User::WallpaperType, int)); MOCK_CONST_METHOD1(GetUserDisplayEmail, std::string(const std::string&)); - MOCK_METHOD2(SaveUserDefaultImageIndex, void(const std::string&, int)); - MOCK_METHOD2(SaveUserImage, void(const std::string&, const UserImage&)); MOCK_METHOD1(SetLoggedInUserCustomWallpaperLayout,void( ash::WallpaperLayout)); - MOCK_METHOD2(SaveUserImageFromFile, void(const std::string&, - const FilePath&)); - MOCK_METHOD1(SaveUserImageFromProfileImage, void(const std::string&)); - MOCK_METHOD1(DownloadProfileImage, void(const std::string&)); MOCK_CONST_METHOD0(IsCurrentUserOwner, bool(void)); MOCK_CONST_METHOD0(IsCurrentUserNew, bool(void)); MOCK_CONST_METHOD0(IsCurrentUserEphemeral, bool(void)); @@ -60,20 +55,22 @@ class MockUserManager : public UserManager { MOCK_METHOD1(AddObserver, void(UserManager::Observer*)); MOCK_METHOD1(RemoveObserver, void(UserManager::Observer*)); MOCK_METHOD0(NotifyLocalStateChanged, void(void)); - MOCK_CONST_METHOD0(DownloadedProfileImage, const gfx::ImageSkia& (void)); // You can't mock this function easily because nobody can create User objects // but the UserManagerImpl and us. - virtual const User& GetLoggedInUser() const OVERRIDE; + virtual const User* GetLoggedInUser() const OVERRIDE; // You can't mock this function easily because nobody can create User objects // but the UserManagerImpl and us. - virtual User& GetLoggedInUser() OVERRIDE; + virtual User* GetLoggedInUser() OVERRIDE; + + virtual UserImageManager* GetUserImageManager() OVERRIDE; // Sets a new User instance. void SetLoggedInUser(const std::string& email); User* user_; + scoped_ptr<MockUserImageManager> user_image_manager_; }; // Class that provides easy life-cycle management for mocking the UserManager diff --git a/chrome/browser/chromeos/login/screen_locker.cc b/chrome/browser/chromeos/login/screen_locker.cc index 8a3bf91..f2be07b 100644 --- a/chrome/browser/chromeos/login/screen_locker.cc +++ b/chrome/browser/chromeos/login/screen_locker.cc @@ -284,7 +284,7 @@ void ScreenLocker::Show() { if (!screen_locker_) { ScreenLocker* locker = - new ScreenLocker(UserManager::Get()->GetLoggedInUser()); + new ScreenLocker(*UserManager::Get()->GetLoggedInUser()); VLOG(1) << "Created ScreenLocker " << locker; locker->Init(); } else { diff --git a/chrome/browser/chromeos/login/simple_jpeg_encoder.cc b/chrome/browser/chromeos/login/simple_jpeg_encoder.cc index c24ae46..d9dcc02 100644 --- a/chrome/browser/chromeos/login/simple_jpeg_encoder.cc +++ b/chrome/browser/chromeos/login/simple_jpeg_encoder.cc @@ -16,7 +16,7 @@ namespace chromeos { namespace { -const int kCustomWallpaperQuality = 90; +const int kDefaultEncodingQuality = 90; } SimpleJpegEncoder::SimpleJpegEncoder( @@ -29,12 +29,12 @@ SimpleJpegEncoder::SimpleJpegEncoder( void SimpleJpegEncoder::Run(EncoderCallback callback) { base::WorkerPool::PostTaskAndReply( FROM_HERE, - base::Bind(&SimpleJpegEncoder::EncodeWallpaper, this), + base::Bind(&SimpleJpegEncoder::EncodeBitmap, this), base::Bind(callback, data_), true /* task_is_slow */); } -void SimpleJpegEncoder::EncodeWallpaper() { +void SimpleJpegEncoder::EncodeBitmap() { if (cancel_flag_.IsSet()) return; SkAutoLockPixels lock_input(image_); @@ -44,7 +44,7 @@ void SimpleJpegEncoder::EncodeWallpaper() { image_.width(), image_.height(), image_.width() * image_.bytesPerPixel(), - kCustomWallpaperQuality, &data_->data()); + kDefaultEncodingQuality, &data_->data()); } void SimpleJpegEncoder::Cancel() { diff --git a/chrome/browser/chromeos/login/simple_jpeg_encoder.h b/chrome/browser/chromeos/login/simple_jpeg_encoder.h index 3c932f6..b193525 100644 --- a/chrome/browser/chromeos/login/simple_jpeg_encoder.h +++ b/chrome/browser/chromeos/login/simple_jpeg_encoder.h @@ -36,7 +36,7 @@ class SimpleJpegEncoder ~SimpleJpegEncoder(); // Encodes a SkBitmap with JPEG codec on worker pool. - void EncodeWallpaper(); + void EncodeBitmap(); base::CancellationFlag cancel_flag_; diff --git a/chrome/browser/chromeos/login/user.cc b/chrome/browser/chromeos/login/user.cc index 339dece..8df3ad9 100644 --- a/chrome/browser/chromeos/login/user.cc +++ b/chrome/browser/chromeos/login/user.cc @@ -36,7 +36,8 @@ User::User(const std::string& email) : email_(email), oauth_token_status_(OAUTH_TOKEN_STATUS_UNKNOWN), image_index_(kInvalidImageIndex), - image_is_stub_(false) { + image_is_stub_(false), + image_is_loading_(false) { // The email address of a demo user is for internal purposes only, // never meant for display. if (!is_demo_user()) @@ -49,6 +50,7 @@ void User::SetImage(const UserImage& user_image, int image_index) { user_image_ = user_image; image_index_ = image_index; image_is_stub_ = false; + image_is_loading_ = false; DCHECK(HasDefaultImage() || user_image.has_raw_image()); } @@ -56,12 +58,13 @@ void User::SetImageURL(const GURL& image_url) { user_image_.set_url(image_url); } -void User::SetStubImage(int image_index) { +void User::SetStubImage(int image_index, bool is_loading) { user_image_ = UserImage( *ResourceBundle::GetSharedInstance(). GetImageSkiaNamed(IDR_PROFILE_PICTURE_LOADING)); image_index_ = image_index; image_is_stub_ = true; + image_is_loading_ = is_loading; } std::string User::GetAccountName(bool use_display_email) const { diff --git a/chrome/browser/chromeos/login/user.h b/chrome/browser/chromeos/login/user.h index 63ec6c5..2989c64 100644 --- a/chrome/browser/chromeos/login/user.h +++ b/chrome/browser/chromeos/login/user.h @@ -83,6 +83,10 @@ class User { return user_image_.animated_image(); } + // Whether |raw_image| contains data in format that is considered safe to + // decode in sensitive environment (on Login screen). + bool image_is_safe_format() const { return user_image_.is_safe_format(); } + // Returns the URL of user image, if there is any. Currently only the profile // image has a URL, for other images empty URL is returned. GURL image_url() const { return user_image_.url(); } @@ -90,6 +94,9 @@ class User { // True if user image is a stub (while real image is being loaded from file). bool image_is_stub() const { return image_is_stub_; } + // True if image is being loaded from file. + bool image_is_loading() const { return image_is_loading_; } + // OAuth token status for this user. OAuthTokenStatus oauth_token_status() const { return oauth_token_status_; } @@ -104,6 +111,8 @@ class User { private: friend class UserManagerImpl; + friend class UserImageManagerImpl; + // For testing: friend class MockUserManager; friend class UserManagerTest; @@ -118,7 +127,8 @@ class User { // Sets a stub image until the next |SetImage| call. |image_index| may be // one of |kExternalImageIndex| or |kProfileImageIndex|. - void SetStubImage(int image_index); + // If |is_loading| is |true|, that means user image is being loaded from file. + void SetStubImage(int image_index, bool is_loading); // Set thumbnail of user custom wallpaper. void SetWallpaperThumbnail(const SkBitmap& wallpaper_thumbnail); @@ -135,6 +145,8 @@ class User { display_email_ = display_email; } + UserImage& user_image() { return user_image_; } + std::string email_; string16 display_name_; // The displayed user email, defaults to |email_|. @@ -149,6 +161,9 @@ class User { // True if current user image is a stub set by a |SetStubImage| call. bool image_is_stub_; + // True if current user image is being loaded from file. + bool image_is_loading_; + DISALLOW_COPY_AND_ASSIGN(User); }; diff --git a/chrome/browser/chromeos/login/user_image.cc b/chrome/browser/chromeos/login/user_image.cc index fe77a4d..347c1fb 100644 --- a/chrome/browser/chromeos/login/user_image.cc +++ b/chrome/browser/chromeos/login/user_image.cc @@ -4,12 +4,18 @@ #include "chrome/browser/chromeos/login/user_image.h" -#include "ui/gfx/codec/png_codec.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "ui/gfx/codec/jpeg_codec.h" + +#include "base/debug/trace_event.h" namespace chromeos { namespace { +// Default quality for encoding user images. +const int kDefaultEncodingQuality = 90; + bool IsAnimatedImage(const UserImage::RawImage& data) { const char kGIFStamp[] = "GIF"; const size_t kGIFStampLength = sizeof(kGIFStamp) - 1; @@ -21,13 +27,21 @@ bool IsAnimatedImage(const UserImage::RawImage& data) { return false; } -bool EncodeBGRAImageSkia(const gfx::ImageSkia& image, - bool discard_transparency, - std::vector<unsigned char>* output) { - if (image.isNull() || !image.bitmap()) +bool EncodeImageSkia(const gfx::ImageSkia& image, + std::vector<unsigned char>* output) { + TRACE_EVENT2("oobe", "EncodeImageSkia", + "width", image.width(), "height", image.height()); + if (image.isNull()) return false; - return gfx::PNGCodec::EncodeBGRASkBitmap(*image.bitmap(), - discard_transparency, output); + const SkBitmap& bitmap = *image.bitmap(); + SkAutoLockPixels lock_image(bitmap); + return gfx::JPEGCodec::Encode( + reinterpret_cast<unsigned char*>(bitmap.getAddr32(0, 0)), + gfx::JPEGCodec::FORMAT_SkBitmap, + bitmap.width(), + bitmap.height(), + bitmap.width() * bitmap.bytesPerPixel(), + kDefaultEncodingQuality, output); } } // namespace @@ -35,31 +49,40 @@ bool EncodeBGRAImageSkia(const gfx::ImageSkia& image, // static UserImage UserImage::CreateAndEncode(const gfx::ImageSkia& image) { RawImage raw_image; - return EncodeBGRAImageSkia(image, false, &raw_image) ? - UserImage(image, raw_image) : UserImage(image); + if (EncodeImageSkia(image, &raw_image)) { + UserImage result(image, raw_image); + result.MarkAsSafe(); + return result; + } + return UserImage(image); } UserImage::UserImage() : has_raw_image_(false), - has_animated_image_(false) { + has_animated_image_(false), + is_safe_format_(false) { } UserImage::UserImage(const gfx::ImageSkia& image) : image_(image), has_raw_image_(false), - has_animated_image_(false) { + has_animated_image_(false), + is_safe_format_(false) { } UserImage::UserImage(const gfx::ImageSkia& image, const RawImage& raw_image) : image_(image), has_raw_image_(false), - has_animated_image_(false) { + has_animated_image_(false), + is_safe_format_(false) { if (IsAnimatedImage(raw_image)) { has_animated_image_ = true; animated_image_ = raw_image; - if (EncodeBGRAImageSkia(image_, false, &raw_image_)) + if (EncodeImageSkia(image_, &raw_image_)) { has_raw_image_ = true; + MarkAsSafe(); + } } else { has_raw_image_ = true; raw_image_ = raw_image; @@ -72,4 +95,8 @@ void UserImage::DiscardRawImage() { RawImage().swap(raw_image_); // Clear |raw_image_|. } +void UserImage::MarkAsSafe() { + is_safe_format_ = true; +} + } // namespace chromeos diff --git a/chrome/browser/chromeos/login/user_image.h b/chrome/browser/chromeos/login/user_image.h index e91067b..1bae8d8 100644 --- a/chrome/browser/chromeos/login/user_image.h +++ b/chrome/browser/chromeos/login/user_image.h @@ -56,6 +56,11 @@ class UserImage { void set_url(const GURL& url) { url_ = url; } GURL url() const { return url_; } + // Whether |raw_image| contains data in format that is considered safe to + // decode in sensitive environment (on Login screen). + bool is_safe_format() const { return is_safe_format_; } + void MarkAsSafe(); + private: gfx::ImageSkia image_; bool has_raw_image_; @@ -63,6 +68,7 @@ class UserImage { bool has_animated_image_; RawImage animated_image_; GURL url_; + bool is_safe_format_; }; } // namespace chromeos diff --git a/chrome/browser/chromeos/login/user_image_loader.cc b/chrome/browser/chromeos/login/user_image_loader.cc index dbd4958..89fae74 100644 --- a/chrome/browser/chromeos/login/user_image_loader.cc +++ b/chrome/browser/chromeos/login/user_image_loader.cc @@ -95,10 +95,12 @@ void UserImageLoader::OnImageDecoded(const ImageDecoder* decoder, } gfx::ImageSkia final_image_skia(final_image); final_image_skia.MakeThreadSafe(); + UserImage user_image(final_image_skia, decoder->get_image_data()); + if (image_codec_ == ImageDecoder::ROBUST_JPEG_CODEC) + user_image.MarkAsSafe(); target_message_loop_->PostTask( FROM_HERE, - base::Bind(image_info.loaded_cb, - UserImage(final_image_skia, decoder->get_image_data()))); + base::Bind(image_info.loaded_cb, user_image)); image_info_map_.erase(info_it); } diff --git a/chrome/browser/chromeos/login/user_image_manager.cc b/chrome/browser/chromeos/login/user_image_manager.cc new file mode 100644 index 0000000..84699cc --- /dev/null +++ b/chrome/browser/chromeos/login/user_image_manager.cc @@ -0,0 +1,12 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/chromeos/login/user_image_manager.h" + +namespace chromeos { + +UserImageManager::~UserImageManager() { +} + +} // namespace chromeos diff --git a/chrome/browser/chromeos/login/user_image_manager.h b/chrome/browser/chromeos/login/user_image_manager.h new file mode 100644 index 0000000..ac6c1b07 --- /dev/null +++ b/chrome/browser/chromeos/login/user_image_manager.h @@ -0,0 +1,76 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_USER_IMAGE_MANAGER_H_ +#define CHROME_BROWSER_CHROMEOS_LOGIN_USER_IMAGE_MANAGER_H_ + +#include <string> + +#include "chrome/browser/chromeos/login/user.h" + +class FilePath; +class PrefService; + +namespace gfx { +class ImageSkia; +} + +namespace chromeos { + +class UserImage; + +// Base class that provides a mechanism for updating user images. +class UserImageManager { + public: + // Registers user image manager preferences. + static void RegisterPrefs(PrefService* local_state); + + virtual ~UserImageManager(); + + // Loads user image data from Local State. + virtual void LoadUserImages(const UserList& users) = 0; + + // Indicates that a user with the given email has just logged in. + virtual void UserLoggedIn(const std::string& email, bool user_is_new) = 0; + + // Sets user image to the default image with index |image_index|, sends + // LOGIN_USER_IMAGE_CHANGED notification and updates Local State. + virtual void SaveUserDefaultImageIndex(const std::string& username, + int image_index) = 0; + + // Saves image to file, sends LOGIN_USER_IMAGE_CHANGED notification and + // updates Local State. + virtual void SaveUserImage(const std::string& username, + const UserImage& user_image) = 0; + + // Tries to load user image from disk; if successful, sets it for the user, + // sends LOGIN_USER_IMAGE_CHANGED notification and updates Local State. + virtual void SaveUserImageFromFile(const std::string& username, + const FilePath& path) = 0; + + // Sets profile image as user image for |username|, sends + // LOGIN_USER_IMAGE_CHANGED notification and updates Local State. If the user + // is not logged-in or the last |DownloadProfileImage| call has failed, a + // default grey avatar will be used until the user logs in and profile image + // is downloaded successfully. + virtual void SaveUserImageFromProfileImage(const std::string& username) = 0; + + // Deletes user image and the corresponding image file. + virtual void DeleteUserImage(const std::string& username) = 0; + + // Starts downloading the profile image for the logged-in user. + // If user's image index is |kProfileImageIndex|, newly downloaded image + // is immediately set as user's current picture. + // |reason| is an arbitrary string (used to report UMA histograms with + // download times). + virtual void DownloadProfileImage(const std::string& reason) = 0; + + // Returns the result of the last successful profile image download, if any. + // Otherwise, returns an empty bitmap. + virtual const gfx::ImageSkia& DownloadedProfileImage() const = 0; +}; + +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_LOGIN_USER_IMAGE_MANAGER_H_ diff --git a/chrome/browser/chromeos/login/user_image_manager_browsertest.cc b/chrome/browser/chromeos/login/user_image_manager_browsertest.cc new file mode 100644 index 0000000..0f7b327 --- /dev/null +++ b/chrome/browser/chromeos/login/user_image_manager_browsertest.cc @@ -0,0 +1,262 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <string> + +#include "base/command_line.h" +#include "base/file_util.h" +#include "base/memory/ref_counted_memory.h" +#include "base/path_service.h" +#include "base/values.h" +#include "chrome/browser/chromeos/cros/cros_in_process_browser_test.h" +#include "chrome/browser/chromeos/login/default_user_images.h" +#include "chrome/browser/chromeos/login/mock_user_manager.h" +#include "chrome/browser/chromeos/login/user_image_manager_impl.h" +#include "chrome/browser/chromeos/login/user_manager.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/prefs/scoped_user_pref_update.h" +#include "chrome/common/chrome_notification_types.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/test/base/testing_browser_process.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" +#include "content/public/browser/notification_service.h" +#include "content/public/test/test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/layout.h" +#include "ui/base/resource/resource_bundle.h" + +namespace chromeos { + +const char kTestUser1[] = "test-user@example.com"; +const char kTestUser2[] = "test-user2@example.com"; + +class UserImageManagerTest : public CrosInProcessBrowserTest, + public content::NotificationObserver, + public UserManager::Observer { + protected: + UserImageManagerTest() { + } + + // CrosInProcessBrowserTest overrides: + virtual void SetUpOnMainThread() OVERRIDE { + UserManager::Get()->AddObserver(this); + user_image_manager_ = UserManager::Get()->GetUserImageManager(); + local_state_ = g_browser_process->local_state(); + // No migration delay for testing. + UserImageManagerImpl::user_image_migration_delay_ms = 0; + } + + virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { + command_line->AppendSwitch(switches::kLoginManager); + command_line->AppendSwitchASCII(switches::kLoginProfile, "user"); + } + + // content::NotificationObserver overrides: + virtual void Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) OVERRIDE { + DCHECK(type == chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED); + registrar_.Remove(this, chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED, + content::NotificationService::AllSources()); + MessageLoopForUI::current()->Quit(); + } + + // UserManager::Observer overrides: + virtual void LocalStateChanged(UserManager* user_manager) OVERRIDE { + MessageLoopForUI::current()->Quit(); + } + + // Adds given user to Local State, if not there. + void AddUser(const std::string& username) { + ListPrefUpdate users_pref(local_state_, "LoggedInUsers"); + users_pref->AppendIfNotPresent(base::Value::CreateStringValue(username)); + } + + // Logs in |username|. + void LogIn(const std::string& username) { + UserManager::Get()->UserLoggedIn(username, false); + } + + // Subscribes for image change notification. + void ExpectImageChange() { + registrar_.Add(this, chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED, + content::NotificationService::AllSources()); + } + + // Stores old (pre-migration) user image info. + void SetOldUserImageInfo(const std::string& username, + int image_index, + const FilePath& image_path) { + AddUser(username); + DictionaryPrefUpdate images_pref(local_state_, "UserImages"); + base::DictionaryValue* image_properties = new base::DictionaryValue(); + image_properties->Set( + "index", base::Value::CreateIntegerValue(image_index)); + image_properties->Set( + "path" , base::Value::CreateStringValue(image_path.value())); + images_pref->SetWithoutPathExpansion(username, image_properties); + } + + // Verifies user image info in |images_pref| dictionary. + void ExpectUserImageInfo(const base::DictionaryValue* images_pref, + const std::string& username, + int image_index, + const FilePath& image_path) { + ASSERT_TRUE(images_pref); + const base::DictionaryValue* image_properties = NULL; + images_pref->GetDictionaryWithoutPathExpansion(username, &image_properties); + ASSERT_TRUE(image_properties); + int actual_image_index; + std::string actual_image_path; + ASSERT_TRUE(image_properties->GetInteger("index", &actual_image_index) && + image_properties->GetString("path", &actual_image_path)); + EXPECT_EQ(image_index, actual_image_index); + EXPECT_EQ(image_path.value(), actual_image_path); + } + + // Verifies that there is no image info for |username| in dictionary + // |images_pref|. + void ExpectNoUserImageInfo(const base::DictionaryValue* images_pref, + const std::string& username) { + ASSERT_TRUE(images_pref); + const base::DictionaryValue* image_properties = NULL; + images_pref->GetDictionaryWithoutPathExpansion(username, &image_properties); + ASSERT_FALSE(image_properties); + } + + // Verifies that old user image info matches |image_index| and |image_path| + // and that new user image info does not exist. + void ExpectOldUserImageInfo(const std::string& username, + int image_index, + const FilePath& image_path) { + ExpectUserImageInfo(local_state_->GetDictionary("UserImages"), + username, image_index, image_path); + ExpectNoUserImageInfo(local_state_->GetDictionary("user_image_info"), + username); + } + + // Verifies that new user image info matches |image_index| and |image_path| + // and that old user image info does not exist. + void ExpectNewUserImageInfo(const std::string& username, + int image_index, + const FilePath& image_path) { + ExpectUserImageInfo(local_state_->GetDictionary("user_image_info"), + username, image_index, image_path); + ExpectNoUserImageInfo(local_state_->GetDictionary("UserImages"), + username); + } + + // Sets bitmap |resource_id| as image for |username| and saves it to disk. + void SaveUserImagePNG(const std::string& username, + int resource_id) { + FilePath image_path = GetUserImagePath(username, "png"); + scoped_refptr<base::RefCountedStaticMemory> image_data( + ResourceBundle::GetSharedInstance().LoadDataResourceBytes( + resource_id, ui::SCALE_FACTOR_100P)); + int written = file_util::WriteFile( + image_path, + reinterpret_cast<const char*>(image_data->front()), + image_data->size()); + EXPECT_EQ(static_cast<int>(image_data->size()), written); + SetOldUserImageInfo(username, User::kExternalImageIndex, image_path); + } + + // Returns the image path for user |username| with specified |extension|. + FilePath GetUserImagePath(const std::string& username, + const std::string& extension) { + FilePath user_data_dir; + PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); + return user_data_dir.Append(username).AddExtension(extension); + } + + UserImageManager* user_image_manager_; + PrefService* local_state_; + content::NotificationRegistrar registrar_; + + private: + DISALLOW_COPY_AND_ASSIGN(UserImageManagerTest); +}; + +IN_PROC_BROWSER_TEST_F(UserImageManagerTest, PRE_DefaultUserImagePreserved) { + // Setup an old default (stock) user image. + ScopedMockUserManagerEnabler mock_user_manager; + SetOldUserImageInfo(kTestUser1, kFirstDefaultImageIndex, FilePath()); +} + +IN_PROC_BROWSER_TEST_F(UserImageManagerTest, DefaultUserImagePreserved) { + UserManager::Get()->GetUsers(); // Load users. + // Old info preserved. + ExpectOldUserImageInfo(kTestUser1, kFirstDefaultImageIndex, FilePath()); + LogIn(kTestUser1); + // Wait for migration. + content::RunMessageLoop(); + // Image info is migrated now. + ExpectNewUserImageInfo(kTestUser1, kFirstDefaultImageIndex, FilePath()); +} + +IN_PROC_BROWSER_TEST_F(UserImageManagerTest, PRE_OtherUsersUnaffected) { + // Setup two users with stock images. + ScopedMockUserManagerEnabler mock_user_manager; + SetOldUserImageInfo(kTestUser1, kFirstDefaultImageIndex, FilePath()); + SetOldUserImageInfo(kTestUser2, kFirstDefaultImageIndex + 1, FilePath()); +} + +IN_PROC_BROWSER_TEST_F(UserImageManagerTest, OtherUsersUnaffected) { + UserManager::Get()->GetUsers(); // Load users. + // Old info preserved. + ExpectOldUserImageInfo(kTestUser1, kFirstDefaultImageIndex, FilePath()); + ExpectOldUserImageInfo(kTestUser2, kFirstDefaultImageIndex + 1, FilePath()); + LogIn(kTestUser1); + // Wait for migration. + content::RunMessageLoop(); + // Image info is migrated for the first user and unaffected for the rest. + ExpectNewUserImageInfo(kTestUser1, kFirstDefaultImageIndex, FilePath()); + ExpectOldUserImageInfo(kTestUser2, kFirstDefaultImageIndex + 1, FilePath()); +} + +IN_PROC_BROWSER_TEST_F(UserImageManagerTest, PRE_PRE_NonJPEGImageFromFile) { + // Setup a user with non-JPEG image. + ScopedMockUserManagerEnabler mock_user_manager; + SaveUserImagePNG( + kTestUser1, kDefaultImageResourceIDs[kFirstDefaultImageIndex]); +} + +IN_PROC_BROWSER_TEST_F(UserImageManagerTest, PRE_NonJPEGImageFromFile) { + UserManager::Get()->GetUsers(); // Load users. + // Old info preserved. + ExpectOldUserImageInfo(kTestUser1, User::kExternalImageIndex, + GetUserImagePath(kTestUser1, "png")); + LogIn(kTestUser1); + // Wait for migration. + content::RunMessageLoop(); + // Image info is migrated and the image is converted to JPG. + ExpectNewUserImageInfo(kTestUser1, User::kExternalImageIndex, + GetUserImagePath(kTestUser1, "jpg")); + const User* user = UserManager::Get()->GetLoggedInUser(); + ASSERT_TRUE(user); + EXPECT_FALSE(user->image_is_safe_format()); + // Check image dimensions. + const gfx::ImageSkia& saved_image = GetDefaultImage(kFirstDefaultImageIndex); + EXPECT_EQ(saved_image.width(), user->image().width()); + EXPECT_EQ(saved_image.height(), user->image().height()); +} + +IN_PROC_BROWSER_TEST_F(UserImageManagerTest, NonJPEGImageFromFile) { + ExpectImageChange(); + UserManager::Get()->GetUsers(); // Load users. + // Wait for image load. + content::RunMessageLoop(); + // Now the migrated image is used. + const User* user = UserManager::Get()->FindUser(kTestUser1); + ASSERT_TRUE(user); + EXPECT_TRUE(user->image_is_safe_format()); + // Check image dimensions. Images can't be compared since JPEG is lossy. + const gfx::ImageSkia& saved_image = GetDefaultImage(kFirstDefaultImageIndex); + EXPECT_EQ(saved_image.width(), user->image().width()); + EXPECT_EQ(saved_image.height(), user->image().height()); +} + +} // namespace chromeos diff --git a/chrome/browser/chromeos/login/user_image_manager_impl.cc b/chrome/browser/chromeos/login/user_image_manager_impl.cc new file mode 100644 index 0000000..5a7ca978 --- /dev/null +++ b/chrome/browser/chromeos/login/user_image_manager_impl.cc @@ -0,0 +1,722 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/chromeos/login/user_image_manager_impl.h" + +#include "base/bind.h" +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/metrics/histogram.h" +#include "base/path_service.h" +#include "base/rand_util.h" +#include "base/threading/worker_pool.h" +#include "base/time.h" +#include "base/values.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/chromeos/login/default_user_images.h" +#include "chrome/browser/chromeos/login/helper.h" +#include "chrome/browser/chromeos/login/user_image.h" +#include "chrome/browser/chromeos/login/user_manager.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/prefs/scoped_user_pref_update.h" +#include "chrome/browser/profiles/profile_downloader.h" +#include "chrome/browser/profiles/profile_manager.h" +#include "chrome/browser/ui/webui/web_ui_util.h" +#include "chrome/common/chrome_notification_types.h" +#include "chrome/common/chrome_paths.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/notification_service.h" +#include "content/public/common/url_constants.h" +#include "ui/gfx/image/image_skia.h" + +#include "base/debug/trace_event.h" + +using content::BrowserThread; + +namespace chromeos { + +namespace { + +// A dictionary that maps usernames to old user image data with images stored in +// PNG format. Deprecated. +// TODO(ivankr): remove this const char after migration is gone. +const char kUserImages[] = "UserImages"; + +// A dictionary that maps usernames to user image data with images stored in +// JPEG format. +const char kUserImageProperties[] = "user_image_info"; + +// Names of user image properties. +const char kImagePathNodeName[] = "path"; +const char kImageIndexNodeName[] = "index"; +const char kImageURLNodeName[] = "url"; + +// Delay betweeen user login and user image migration. +const long kUserImageMigrationDelayMs = 50000; + +// Delay betweeen user login and attempt to update user's profile data. +const long kProfileDataDownloadDelayMs = 10000; + +// Delay betweeen subsequent profile refresh attempts (24 hrs). +const long kProfileRefreshIntervalMs = 24L * 3600 * 1000; + +const char kSafeImagePathExtension[] = ".jpg"; + +// Enum for reporting histograms about profile picture download. +enum ProfileDownloadResult { + kDownloadSuccessChanged, + kDownloadSuccess, + kDownloadFailure, + kDownloadDefault, + kDownloadCached, + + // Must be the last, convenient count. + kDownloadResultsCount +}; + +// Time histogram prefix for a cached profile image download. +const char kProfileDownloadCachedTime[] = + "UserImage.ProfileDownloadTime.Cached"; +// Time histogram prefix for the default profile image download. +const char kProfileDownloadDefaultTime[] = + "UserImage.ProfileDownloadTime.Default"; +// Time histogram prefix for a failed profile image download. +const char kProfileDownloadFailureTime[] = + "UserImage.ProfileDownloadTime.Failure"; +// Time histogram prefix for a successful profile image download. +const char kProfileDownloadSuccessTime[] = + "UserImage.ProfileDownloadTime.Success"; +// Time histogram suffix for a profile image download after login. +const char kProfileDownloadReasonLoggedIn[] = "LoggedIn"; +// Time histogram suffix for a scheduled profile image download. +const char kProfileDownloadReasonScheduled[] = "Scheduled"; + +// Add a histogram showing the time it takes to download a profile image. +// Separate histograms are reported for each download |reason| and |result|. +void AddProfileImageTimeHistogram(ProfileDownloadResult result, + const std::string& download_reason, + const base::TimeDelta& time_delta) { + std::string histogram_name; + switch (result) { + case kDownloadFailure: + histogram_name = kProfileDownloadFailureTime; + break; + case kDownloadDefault: + histogram_name = kProfileDownloadDefaultTime; + break; + case kDownloadSuccess: + histogram_name = kProfileDownloadSuccessTime; + break; + case kDownloadCached: + histogram_name = kProfileDownloadCachedTime; + break; + default: + NOTREACHED(); + } + if (!download_reason.empty()) { + histogram_name += "."; + histogram_name += download_reason; + } + + static const base::TimeDelta min_time = base::TimeDelta::FromMilliseconds(1); + static const base::TimeDelta max_time = base::TimeDelta::FromSeconds(50); + const size_t bucket_count(50); + + base::Histogram* counter = base::Histogram::FactoryTimeGet( + histogram_name, min_time, max_time, bucket_count, + base::Histogram::kUmaTargetedHistogramFlag); + counter->AddTime(time_delta); + + DVLOG(1) << "Profile image download time: " << time_delta.InSecondsF(); +} + +// Deletes image file. +void DeleteImageFile(const std::string& image_path) { + if (image_path.empty()) + return; + FilePath fp(image_path); + BrowserThread::PostTask( + BrowserThread::FILE, + FROM_HERE, + base::Bind(base::IgnoreResult(&file_util::Delete), + fp, /* recursive= */ false)); +} + +// Converts |image_index| to UMA histogram value. +int ImageIndexToHistogramIndex(int image_index) { + switch (image_index) { + case User::kExternalImageIndex: + // TODO(ivankr): Distinguish this from selected from file. + return kHistogramImageFromCamera; + case User::kProfileImageIndex: + return kHistogramImageFromProfile; + default: + return image_index; + } +} + +} // namespace + +// static +long UserImageManagerImpl::user_image_migration_delay_ms = + kUserImageMigrationDelayMs; + +// static +void UserImageManager::RegisterPrefs(PrefService* local_state) { + local_state->RegisterDictionaryPref(kUserImages, + PrefService::UNSYNCABLE_PREF); + local_state->RegisterDictionaryPref(kUserImageProperties, + PrefService::UNSYNCABLE_PREF); +} + +UserImageManagerImpl::UserImageManagerImpl() + : image_loader_(new UserImageLoader(ImageDecoder::ROBUST_JPEG_CODEC)), + unsafe_image_loader_(new UserImageLoader(ImageDecoder::DEFAULT_CODEC)), + last_image_set_async_(false), + downloaded_profile_image_data_url_(chrome::kAboutBlankURL), + downloading_profile_image_(false), + migrate_current_user_on_load_(false) { +} + +UserImageManagerImpl::~UserImageManagerImpl() { +} + +void UserImageManagerImpl::LoadUserImages(const UserList& users) { + PrefService* local_state = g_browser_process->local_state(); + const DictionaryValue* prefs_images_unsafe = + local_state->GetDictionary(kUserImages); + const DictionaryValue* prefs_images = + local_state->GetDictionary(kUserImageProperties); + if (!prefs_images && !prefs_images_unsafe) + return; + + for (UserList::const_iterator it = users.begin(); it != users.end(); ++it) { + User* user = *it; + const base::DictionaryValue* image_properties = NULL; + bool needs_migration = false; // |true| if user has image in old format. + bool safe_source = false; // |true| if image loaded from safe source. + + if (prefs_images_unsafe) { + needs_migration = prefs_images_unsafe->GetDictionaryWithoutPathExpansion( + user->email(), &image_properties); + } + if (prefs_images) { + safe_source = prefs_images->GetDictionaryWithoutPathExpansion( + user->email(), &image_properties); + } + + if (needs_migration) + users_to_migrate_.insert(user->email()); + + if (!image_properties) { + SetInitialUserImage(user->email()); + } else { + int image_index = User::kInvalidImageIndex; + image_properties->GetInteger(kImageIndexNodeName, &image_index); + + if (image_index >= 0 && image_index < kDefaultImagesCount) { + user->SetImage(UserImage(GetDefaultImage(image_index)), + image_index); + } else if (image_index == User::kExternalImageIndex || + image_index == User::kProfileImageIndex) { + std::string image_path; + image_properties->GetString(kImagePathNodeName, &image_path); + // Path may be empty for profile images (meaning that the image + // hasn't been downloaded for the first time yet, in which case a + // download will be scheduled for |kProfileDataDownloadDelayMs| + // after user logs in). + DCHECK(!image_path.empty() || image_index == User::kProfileImageIndex); + std::string image_url; + image_properties->GetString(kImageURLNodeName, &image_url); + GURL image_gurl(image_url); + // Until image has been loaded, use the stub image (gray avatar). + user->SetStubImage(image_index, true); + user->SetImageURL(image_gurl); + if (!image_path.empty()) { + // Load user image asynchronously. + UserImageLoader* loader = + safe_source ? image_loader_.get() : unsafe_image_loader_.get(); + loader->Start( + image_path, 0 /* no resize */, + base::Bind(&UserImageManagerImpl::SetUserImage, + base::Unretained(this), + user->email(), image_index, image_gurl)); + } + } else { + NOTREACHED(); + } + } + } +} + +void UserImageManagerImpl::UserLoggedIn(const std::string& email, + bool user_is_new) { + if (user_is_new) { + SetInitialUserImage(email); + } else { + int image_index = UserManager::Get()->GetLoggedInUser()->image_index(); + // If current user image is profile image, it needs to be refreshed. + bool download_profile_image = image_index == User::kProfileImageIndex; + if (download_profile_image) + InitDownloadedProfileImage(); + + // Download user's profile data (full name and optionally image) to see if + // it has changed. + BrowserThread::PostDelayedTask( + BrowserThread::UI, + FROM_HERE, + base::Bind(&UserImageManagerImpl::DownloadProfileData, + base::Unretained(this), + kProfileDownloadReasonLoggedIn, + download_profile_image), + base::TimeDelta::FromMilliseconds(kProfileDataDownloadDelayMs)); + + UMA_HISTOGRAM_ENUMERATION("UserImage.LoggedIn", + ImageIndexToHistogramIndex(image_index), + kHistogramImagesCount); + + if (users_to_migrate_.count(email)) { + // User needs image format migration. + BrowserThread::PostDelayedTask( + BrowserThread::UI, + FROM_HERE, + base::Bind(&UserImageManagerImpl::MigrateUserImage, + base::Unretained(this)), + base::TimeDelta::FromMilliseconds(user_image_migration_delay_ms)); + } + } + + // Set up a repeating timer for refreshing the profile data. + profile_download_timer_.Start( + FROM_HERE, base::TimeDelta::FromMilliseconds(kProfileRefreshIntervalMs), + this, &UserImageManagerImpl::DownloadProfileDataScheduled); +} + +void UserImageManagerImpl::SaveUserDefaultImageIndex( + const std::string& username, + int image_index) { + DCHECK(image_index >= 0 && image_index < kDefaultImagesCount); + SetUserImage(username, image_index, GURL(), + UserImage(GetDefaultImage(image_index))); + SaveImageToLocalState(username, "", image_index, GURL(), false); +} + +void UserImageManagerImpl::SaveUserImage(const std::string& username, + const UserImage& user_image) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + SaveUserImageInternal(username, User::kExternalImageIndex, + GURL(), user_image); +} + +void UserImageManagerImpl::SaveUserImageFromFile(const std::string& username, + const FilePath& path) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + // Always use unsafe image loader because we resize the image when saving + // anyway. + unsafe_image_loader_->Start( + path.value(), login::kMaxUserImageSize, + base::Bind(&UserImageManagerImpl::SaveUserImage, + base::Unretained(this), username)); +} + +void UserImageManagerImpl::SaveUserImageFromProfileImage( + const std::string& username) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (!downloaded_profile_image_.isNull()) { + // Profile image has already been downloaded, so save it to file right now. + DCHECK(profile_image_url_.is_valid()); + SaveUserImageInternal( + username, + User::kProfileImageIndex, profile_image_url_, + UserImage::CreateAndEncode(downloaded_profile_image_)); + } else { + // No profile image - use the stub image (gray avatar). + SetUserImage(username, User::kProfileImageIndex, GURL(), UserImage()); + SaveImageToLocalState(username, "", User::kProfileImageIndex, + GURL(), false); + } +} + +void UserImageManagerImpl::DeleteUserImage(const std::string& username) { + // Delete from the old dictionary, if present. + DeleteOldUserImage(username); + + PrefService* prefs = g_browser_process->local_state(); + DictionaryPrefUpdate prefs_images_update(prefs, kUserImageProperties); + const base::DictionaryValue* image_properties; + if (prefs_images_update->GetDictionaryWithoutPathExpansion( + username, &image_properties)) { + std::string image_path; + image_properties->GetString(kImageURLNodeName, &image_path); + prefs_images_update->RemoveWithoutPathExpansion(username, NULL); + DeleteImageFile(image_path); + } +} + +void UserImageManagerImpl::DownloadProfileImage(const std::string& reason) { + DownloadProfileData(reason, true); +} + +const gfx::ImageSkia& UserImageManagerImpl::DownloadedProfileImage() const { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + return downloaded_profile_image_; +} + +FilePath UserImageManagerImpl::GetImagePathForUser( + const std::string& username) { + std::string filename = username + kSafeImagePathExtension; + FilePath user_data_dir; + PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); + return user_data_dir.AppendASCII(filename); +} + +void UserImageManagerImpl::SetInitialUserImage(const std::string& username) { + // Choose a random default image. + int image_id = + base::RandInt(kFirstDefaultImageIndex, kDefaultImagesCount - 1); + SaveUserDefaultImageIndex(username, image_id); +} + +void UserImageManagerImpl::SetUserImage(const std::string& username, + int image_index, + const GURL& image_url, + const UserImage& user_image) { + User* user = const_cast<User*>(UserManager::Get()->FindUser(username)); + // User may have been removed by now. + if (user) { + bool image_changed = user->image_index() != User::kInvalidImageIndex; + bool is_current_user = user == UserManager::Get()->GetLoggedInUser(); + if (!user_image.image().isNull()) + user->SetImage(user_image, image_index); + else + user->SetStubImage(image_index, false); + user->SetImageURL(image_url); + // For the logged-in user with a profile picture, initialize + // |downloaded_profile_picture_|. + if (is_current_user && image_index == User::kProfileImageIndex) { + InitDownloadedProfileImage(); + } + if (image_changed) { + // Unless this is first-time setting with |SetInitialUserImage|, + // send a notification about image change. + content::NotificationService::current()->Notify( + chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED, + content::Source<UserImageManager>(this), + content::Details<const User>(user)); + } + if (is_current_user && migrate_current_user_on_load_) + MigrateUserImage(); + } +} + +void UserImageManagerImpl::SaveUserImageInternal(const std::string& username, + int image_index, + const GURL& image_url, + const UserImage& user_image) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + SetUserImage(username, image_index, image_url, user_image); + + // Ignore for ephemeral users. + if (UserManager::Get()->IsEphemeralUser(username)) + return; + + FilePath image_path = GetImagePathForUser(username); + DVLOG(1) << "Saving user image to " << image_path.value(); + + last_image_set_async_ = true; + + base::WorkerPool::PostTask( + FROM_HERE, + base::Bind(&UserImageManagerImpl::SaveImageToFile, + base::Unretained(this), + username, user_image, image_path, image_index, image_url), + /* is_slow= */ false); +} + +void UserImageManagerImpl::SaveImageToFile(const std::string& username, + const UserImage& user_image, + const FilePath& image_path, + int image_index, + const GURL& image_url) { + if (!SaveBitmapToFile(user_image, image_path)) + return; + + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + base::Bind(&UserImageManagerImpl::SaveImageToLocalState, + base::Unretained(this), + username, image_path.value(), image_index, image_url, true)); +} + +void UserImageManagerImpl::SaveImageToLocalState(const std::string& username, + const std::string& image_path, + int image_index, + const GURL& image_url, + bool is_async) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + // Ignore for ephemeral users. + if (UserManager::Get()->IsEphemeralUser(username)) + return; + + // TODO(ivankr): use unique filenames for user images each time + // a new image is set so that only the last image update is saved + // to Local State and notified. + if (is_async && !last_image_set_async_) { + DVLOG(1) << "Ignoring saved image because it has changed"; + return; + } else if (!is_async) { + // Reset the async image save flag if called directly from the UI thread. + last_image_set_async_ = false; + } + + PrefService* local_state = g_browser_process->local_state(); + DictionaryPrefUpdate images_update(local_state, kUserImageProperties); + base::DictionaryValue* image_properties = new base::DictionaryValue(); + image_properties->Set(kImagePathNodeName, new StringValue(image_path)); + image_properties->Set(kImageIndexNodeName, + new base::FundamentalValue(image_index)); + if (!image_url.is_empty()) { + image_properties->Set(kImageURLNodeName, + new StringValue(image_url.spec())); + } else { + image_properties->Remove(kImageURLNodeName, NULL); + } + images_update->SetWithoutPathExpansion(username, image_properties); + DVLOG(1) << "Saving path to user image in Local State."; + + if (users_to_migrate_.count(username)) { + DeleteOldUserImage(username); + users_to_migrate_.erase(username); + } + + UserManager::Get()->NotifyLocalStateChanged(); +} + +bool UserImageManagerImpl::SaveBitmapToFile(const UserImage& user_image, + const FilePath& image_path) { + UserImage safe_image; + const UserImage::RawImage* encoded_image = NULL; + if (!user_image.is_safe_format()) { + safe_image = UserImage::CreateAndEncode(user_image.image()); + encoded_image = &safe_image.raw_image(); + UMA_HISTOGRAM_MEMORY_KB("UserImage.RecodedJpegSize", encoded_image->size()); + } else if (user_image.has_raw_image()) { + encoded_image = &user_image.raw_image(); + } else { + NOTREACHED() << "Raw image missing."; + return false; + } + + if (file_util::WriteFile(image_path, + reinterpret_cast<const char*>(&(*encoded_image)[0]), + encoded_image->size()) == -1) { + LOG(ERROR) << "Failed to save image to file."; + return false; + } + return true; +} + +void UserImageManagerImpl::InitDownloadedProfileImage() { + const User* logged_in_user = UserManager::Get()->GetLoggedInUser(); + DCHECK_EQ(logged_in_user->image_index(), User::kProfileImageIndex); + if (downloaded_profile_image_.isNull() && !logged_in_user->image_is_stub()) { + VLOG(1) << "Profile image initialized"; + downloaded_profile_image_ = logged_in_user->image(); + downloaded_profile_image_data_url_ = + web_ui_util::GetImageDataUrl(downloaded_profile_image_); + profile_image_url_ = logged_in_user->image_url(); + } +} + +void UserImageManagerImpl::DownloadProfileData(const std::string& reason, + bool download_image) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + // For guest login there's no profile image to download. + if (UserManager::Get()->IsLoggedInAsGuest()) + return; + + // Mark profile picture as needed. + downloading_profile_image_ |= download_image; + + // Another download is already in progress + if (profile_image_downloader_.get()) + return; + + profile_image_download_reason_ = reason; + profile_image_load_start_time_ = base::Time::Now(); + profile_image_downloader_.reset(new ProfileDownloader(this)); + profile_image_downloader_->Start(); +} + +void UserImageManagerImpl::DownloadProfileDataScheduled() { + const User* logged_in_user = UserManager::Get()->GetLoggedInUser(); + // If current user image is profile image, it needs to be refreshed. + bool download_profile_image = + logged_in_user->image_index() == User::kProfileImageIndex; + DownloadProfileData(kProfileDownloadReasonScheduled, download_profile_image); +} + +// ProfileDownloaderDelegate override. +bool UserImageManagerImpl::NeedsProfilePicture() const { + return downloading_profile_image_; +} + +// ProfileDownloaderDelegate override. +int UserImageManagerImpl::GetDesiredImageSideLength() const { + return GetCurrentUserImageSize(); +} + +// ProfileDownloaderDelegate override. +std::string UserImageManagerImpl::GetCachedPictureURL() const { + return profile_image_url_.spec(); +} + +Profile* UserImageManagerImpl::GetBrowserProfile() { + return ProfileManager::GetDefaultProfile(); +} + +void UserImageManagerImpl::OnProfileDownloadSuccess( + ProfileDownloader* downloader) { + // Make sure that |ProfileDownloader| gets deleted after return. + scoped_ptr<ProfileDownloader> profile_image_downloader( + profile_image_downloader_.release()); + DCHECK_EQ(downloader, profile_image_downloader.get()); + + UserManager* user_manager = UserManager::Get(); + const User* user = user_manager->GetLoggedInUser(); + + if (!downloader->GetProfileFullName().empty()) { + user_manager->SaveUserDisplayName( + user->email(), downloader->GetProfileFullName()); + } + + bool requested_image = downloading_profile_image_; + downloading_profile_image_ = false; + if (!requested_image) + return; + + ProfileDownloadResult result = kDownloadFailure; + switch (downloader->GetProfilePictureStatus()) { + case ProfileDownloader::PICTURE_SUCCESS: + result = kDownloadSuccess; + break; + case ProfileDownloader::PICTURE_CACHED: + result = kDownloadCached; + break; + case ProfileDownloader::PICTURE_DEFAULT: + result = kDownloadDefault; + break; + default: + NOTREACHED(); + } + + UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult", + result, kDownloadResultsCount); + + DCHECK(!profile_image_load_start_time_.is_null()); + base::TimeDelta delta = base::Time::Now() - profile_image_load_start_time_; + AddProfileImageTimeHistogram(result, profile_image_download_reason_, delta); + + if (result == kDownloadDefault) { + content::NotificationService::current()->Notify( + chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED, + content::Source<UserImageManager>(this), + content::NotificationService::NoDetails()); + } + + // Nothing to do if picture is cached or the default avatar. + if (result != kDownloadSuccess) + return; + + // Check if this image is not the same as already downloaded. + gfx::ImageSkia new_image(downloader->GetProfilePicture()); + std::string new_image_data_url = web_ui_util::GetImageDataUrl(new_image); + if (!downloaded_profile_image_data_url_.empty() && + new_image_data_url == downloaded_profile_image_data_url_) + return; + + downloaded_profile_image_data_url_ = new_image_data_url; + downloaded_profile_image_ = gfx::ImageSkia(downloader->GetProfilePicture()); + profile_image_url_ = GURL(downloader->GetProfilePictureURL()); + + if (user->image_index() == User::kProfileImageIndex) { + VLOG(1) << "Updating profile image for logged-in user"; + UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult", + kDownloadSuccessChanged, + kDownloadResultsCount); + // This will persist |downloaded_profile_image_| to file. + SaveUserImageFromProfileImage(user->email()); + } + + content::NotificationService::current()->Notify( + chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED, + content::Source<UserImageManager>(this), + content::Details<const gfx::ImageSkia>(&downloaded_profile_image_)); +} + +void UserImageManagerImpl::OnProfileDownloadFailure( + ProfileDownloader* downloader) { + DCHECK_EQ(downloader, profile_image_downloader_.get()); + profile_image_downloader_.reset(); + + downloading_profile_image_ = false; + + UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult", + kDownloadFailure, kDownloadResultsCount); + + DCHECK(!profile_image_load_start_time_.is_null()); + base::TimeDelta delta = base::Time::Now() - profile_image_load_start_time_; + AddProfileImageTimeHistogram(kDownloadFailure, profile_image_download_reason_, + delta); + + content::NotificationService::current()->Notify( + chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED, + content::Source<UserImageManager>(this), + content::NotificationService::NoDetails()); +} + +void UserImageManagerImpl::MigrateUserImage() { + User* user = UserManager::Get()->GetLoggedInUser(); + if (user->image_is_loading()) { + LOG(INFO) << "Waiting for user image to load before migration"; + migrate_current_user_on_load_ = true; + return; + } + migrate_current_user_on_load_ = false; + if (user->has_raw_image() && user->image_is_safe_format()) { + // Nothing to migrate already, make sure we delete old image. + DeleteOldUserImage(user->email()); + users_to_migrate_.erase(user->email()); + return; + } + if (user->HasDefaultImage()) { + SaveUserDefaultImageIndex(user->email(), user->image_index()); + } else { + SaveUserImageInternal(user->email(), user->image_index(), + user->image_url(), user->user_image()); + } + UMA_HISTOGRAM_ENUMERATION("UserImage.Migration", + ImageIndexToHistogramIndex(user->image_index()), + kHistogramImagesCount); +} + +void UserImageManagerImpl::DeleteOldUserImage(const std::string& username) { + PrefService* prefs = g_browser_process->local_state(); + DictionaryPrefUpdate prefs_images_update(prefs, kUserImages); + const base::DictionaryValue* image_properties; + if (prefs_images_update->GetDictionaryWithoutPathExpansion( + username, &image_properties)) { + std::string image_path; + image_properties->GetString(kImagePathNodeName, &image_path); + prefs_images_update->RemoveWithoutPathExpansion(username, NULL); + DeleteImageFile(image_path); + } +} + +} // namespace chromeos diff --git a/chrome/browser/chromeos/login/user_image_manager_impl.h b/chrome/browser/chromeos/login/user_image_manager_impl.h new file mode 100644 index 0000000..648139a --- /dev/null +++ b/chrome/browser/chromeos/login/user_image_manager_impl.h @@ -0,0 +1,173 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_USER_IMAGE_MANAGER_IMPL_H_ +#define CHROME_BROWSER_CHROMEOS_LOGIN_USER_IMAGE_MANAGER_IMPL_H_ + +#include <set> +#include <string> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/time.h" +#include "base/timer.h" +#include "chrome/browser/chromeos/login/user.h" +#include "chrome/browser/chromeos/login/user_image_loader.h" +#include "chrome/browser/chromeos/login/user_image_manager.h" +#include "chrome/browser/profiles/profile_downloader_delegate.h" +#include "ui/gfx/image/image_skia.h" + +class ProfileDownloader; +class UserImage; + +namespace chromeos { + +class UserImageManagerImpl : public UserImageManager, + public ProfileDownloaderDelegate { + public: + UserImageManagerImpl(); + + // UserImageManager implemenation: + virtual ~UserImageManagerImpl(); + virtual void LoadUserImages(const UserList& users) OVERRIDE; + virtual void UserLoggedIn(const std::string& email, + bool user_is_new) OVERRIDE; + virtual void SaveUserDefaultImageIndex(const std::string& username, + int image_index) OVERRIDE; + virtual void SaveUserImage(const std::string& username, + const UserImage& user_image) OVERRIDE; + virtual void SaveUserImageFromFile(const std::string& username, + const FilePath& path) OVERRIDE; + virtual void SaveUserImageFromProfileImage( + const std::string& username) OVERRIDE; + virtual void DeleteUserImage(const std::string& username) OVERRIDE; + virtual void DownloadProfileImage(const std::string& reason) OVERRIDE; + virtual const gfx::ImageSkia& DownloadedProfileImage() const OVERRIDE; + + private: + friend class UserImageManagerTest; + + // Non-const for testing purposes. + static long user_image_migration_delay_ms; + + // ProfileDownloaderDelegate implementation: + virtual bool NeedsProfilePicture() const OVERRIDE; + virtual int GetDesiredImageSideLength() const OVERRIDE; + virtual Profile* GetBrowserProfile() OVERRIDE; + virtual std::string GetCachedPictureURL() const OVERRIDE; + virtual void OnProfileDownloadSuccess(ProfileDownloader* downloader) OVERRIDE; + virtual void OnProfileDownloadFailure(ProfileDownloader* downloader) OVERRIDE; + + // Returns image filepath for the given user. + FilePath GetImagePathForUser(const std::string& username); + + // Sets one of the default images for the specified user and saves this + // setting in local state. + // Does not send LOGIN_USER_IMAGE_CHANGED notification. + void SetInitialUserImage(const std::string& username); + + // Sets image for user |username| and sends LOGIN_USER_IMAGE_CHANGED + // notification unless this is a new user and image is set for the first time. + // If |image| is empty, sets a stub image for the user. + void SetUserImage(const std::string& username, + int image_index, + const GURL& image_url, + const UserImage& user_image); + + // Saves image to file, updates local state preferences to given image index + // and sends LOGIN_USER_IMAGE_CHANGED notification. + void SaveUserImageInternal(const std::string& username, + int image_index, + const GURL& image_url, + const UserImage& user_image); + + // Saves image to file with specified path and sends LOGIN_USER_IMAGE_CHANGED + // notification. Runs on FILE thread. Posts task for saving image info to + // Local State on UI thread. + void SaveImageToFile(const std::string& username, + const UserImage& user_image, + const FilePath& image_path, + int image_index, + const GURL& image_url); + + // Stores path to the image and its index in local state. Runs on UI thread. + // If |is_async| is true, it has been posted from the FILE thread after + // saving the image. + void SaveImageToLocalState(const std::string& username, + const std::string& image_path, + int image_index, + const GURL& image_url, + bool is_async); + + // Saves |image| to the specified |image_path|. Runs on FILE thread. + bool SaveBitmapToFile(const UserImage& user_image, + const FilePath& image_path); + + // Initializes |downloaded_profile_image_| with the picture of the logged-in + // user. + void InitDownloadedProfileImage(); + + // Download user's profile data, including full name and picture, when + // |download_image| is true. + // |reason| is an arbitrary string (used to report UMA histograms with + // download times). + void DownloadProfileData(const std::string& reason, bool download_image); + + // Scheduled call for downloading profile data. + void DownloadProfileDataScheduled(); + + // Migrates image info for the current user and deletes the old image, if any. + void MigrateUserImage(); + + // Deletes old user image and dictionary entry. + void DeleteOldUserImage(const std::string& username); + + // Loader for JPEG user images. + scoped_refptr<UserImageLoader> image_loader_; + + // Unsafe loader instance for all user images formats. + scoped_refptr<UserImageLoader> unsafe_image_loader_; + + // Download user profile image on login to update it if it's changed. + scoped_ptr<ProfileDownloader> profile_image_downloader_; + + // Arbitrary string passed to the last |DownloadProfileImage| call. + std::string profile_image_download_reason_; + + // Time when the profile image download has started. + base::Time profile_image_load_start_time_; + + // True if the last user image required async save operation (which may not + // have been completed yet). This flag is used to avoid races when user image + // is first set with |SaveUserImage| and then with |SaveUserImagePath|. + bool last_image_set_async_; + + // Result of the last successful profile image download, if any. + gfx::ImageSkia downloaded_profile_image_; + + // Data URL for |downloaded_profile_image_|. + std::string downloaded_profile_image_data_url_; + + // Original URL of |downloaded_profile_image_|, from which it was downloaded. + GURL profile_image_url_; + + // True when |profile_image_downloader_| is fetching profile picture (not + // just full name). + bool downloading_profile_image_; + + // Timer triggering DownloadProfileDataScheduled for refreshing profile data. + base::RepeatingTimer<UserImageManagerImpl> profile_download_timer_; + + // Users that need image migration to JPEG. + std::set<std::string> users_to_migrate_; + + // If |true|, current user image should be migrated right after it is loaded. + bool migrate_current_user_on_load_; + + DISALLOW_COPY_AND_ASSIGN(UserImageManagerImpl); +}; + +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_LOGIN_USER_IMAGE_MANAGER_IMPL_H_ diff --git a/chrome/browser/chromeos/login/user_image_screen.cc b/chrome/browser/chromeos/login/user_image_screen.cc index a942789..034c7bc 100644 --- a/chrome/browser/chromeos/login/user_image_screen.cc +++ b/chrome/browser/chromeos/login/user_image_screen.cc @@ -11,6 +11,7 @@ #include "chrome/browser/chromeos/login/login_utils.h" #include "chrome/browser/chromeos/login/screen_observer.h" #include "chrome/browser/chromeos/login/user_image.h" +#include "chrome/browser/chromeos/login/user_image_manager.h" #include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/chromeos/login/wizard_controller.h" #include "chrome/common/chrome_notification_types.h" @@ -68,10 +69,11 @@ void UserImageScreen::Show() { return; actor_->Show(); - actor_->SelectImage(UserManager::Get()->GetLoggedInUser().image_index()); + actor_->SelectImage(UserManager::Get()->GetLoggedInUser()->image_index()); // Start fetching the profile image. - UserManager::Get()->DownloadProfileImage(kProfileDownloadReason); + UserManager::Get()->GetUserImageManager()-> + DownloadProfileImage(kProfileDownloadReason); accessibility::MaybeSpeak( l10n_util::GetStringUTF8(IDS_OPTIONS_CHANGE_PICTURE_DIALOG_TEXT)); @@ -118,8 +120,9 @@ void UserImageScreen::OnPhotoTaken(const gfx::ImageSkia& image) { UserManager* user_manager = UserManager::Get(); // TODO(ivankr): once old camera UI is gone, there's raw data in image // decoder, pass UserImage and user it instead. - user_manager->SaveUserImage(user_manager->GetLoggedInUser().email(), - UserImage::CreateAndEncode(image)); + user_manager->GetUserImageManager()->SaveUserImage( + user_manager->GetLoggedInUser()->email(), + UserImage::CreateAndEncode(image)); get_screen_observer()->OnExit(ScreenObserver::USER_IMAGE_SELECTED); @@ -130,8 +133,8 @@ void UserImageScreen::OnPhotoTaken(const gfx::ImageSkia& image) { void UserImageScreen::OnProfileImageSelected() { UserManager* user_manager = UserManager::Get(); - user_manager->SaveUserImageFromProfileImage( - user_manager->GetLoggedInUser().email()); + user_manager->GetUserImageManager()->SaveUserImageFromProfileImage( + user_manager->GetLoggedInUser()->email()); get_screen_observer()->OnExit(ScreenObserver::USER_IMAGE_SELECTED); @@ -142,8 +145,8 @@ void UserImageScreen::OnProfileImageSelected() { void UserImageScreen::OnDefaultImageSelected(int index) { UserManager* user_manager = UserManager::Get(); - user_manager->SaveUserDefaultImageIndex( - user_manager->GetLoggedInUser().email(), index); + user_manager->GetUserImageManager()->SaveUserDefaultImageIndex( + user_manager->GetLoggedInUser()->email(), index); get_screen_observer()->OnExit(ScreenObserver::USER_IMAGE_SELECTED); diff --git a/chrome/browser/chromeos/login/user_manager.cc b/chrome/browser/chromeos/login/user_manager.cc index 46e030d..4cc4474 100644 --- a/chrome/browser/chromeos/login/user_manager.cc +++ b/chrome/browser/chromeos/login/user_manager.cc @@ -4,23 +4,13 @@ #include "chrome/browser/chromeos/login/user_manager.h" -#include "base/synchronization/lock.h" #include "chrome/browser/chromeos/login/user_manager_impl.h" -#include "chrome/browser/prefs/pref_service.h" #include "content/public/browser/browser_thread.h" namespace chromeos { // static -const char UserManager::kLoggedInUsers[] = "LoggedInUsers"; const char UserManager::kStubUser[] = "stub-user@example.com"; -const char UserManager::kUserWallpapers[] = "UserWallpapers"; -const char UserManager::kUserWallpapersProperties[] = - "UserWallpapersProperties"; -const char UserManager::kUserImages[] = "UserImages"; -const char UserManager::kUserDisplayName[] = "UserDisplayName"; -const char UserManager::kUserDisplayEmail[] = "UserDisplayEmail"; -const char UserManager::kUserOAuthTokenStatus[] = "OAuthTokenStatus"; // Class that is holds pointer to UserManager instance. // One could set UserManager mock instance through it (see UserManager::Set). @@ -35,7 +25,6 @@ class UserManagerImplWrapper { } UserManager* get() { - base::AutoLock create(create_lock_); if (!ptr_.get()) reset(new UserManagerImpl); return ptr_.get(); @@ -57,7 +46,6 @@ class UserManagerImplWrapper { UserManagerImplWrapper() { } - base::Lock create_lock_; scoped_ptr<UserManager> ptr_; DISALLOW_COPY_AND_ASSIGN(UserManagerImplWrapper); @@ -75,23 +63,6 @@ UserManager* UserManager::Set(UserManager* mock) { return old_manager; } -// static -void UserManager::RegisterPrefs(PrefService* local_state) { - local_state->RegisterListPref(kLoggedInUsers, PrefService::UNSYNCABLE_PREF); - local_state->RegisterDictionaryPref(kUserWallpapers, - PrefService::UNSYNCABLE_PREF); - local_state->RegisterDictionaryPref(kUserWallpapersProperties, - PrefService::UNSYNCABLE_PREF); - local_state->RegisterDictionaryPref(kUserImages, - PrefService::UNSYNCABLE_PREF); - local_state->RegisterDictionaryPref(kUserOAuthTokenStatus, - PrefService::UNSYNCABLE_PREF); - local_state->RegisterDictionaryPref(kUserDisplayName, - PrefService::UNSYNCABLE_PREF); - local_state->RegisterDictionaryPref(kUserDisplayEmail, - PrefService::UNSYNCABLE_PREF); -} - UserManager::~UserManager() { } diff --git a/chrome/browser/chromeos/login/user_manager.h b/chrome/browser/chromeos/login/user_manager.h index 8396b53..b9e5865 100644 --- a/chrome/browser/chromeos/login/user_manager.h +++ b/chrome/browser/chromeos/login/user_manager.h @@ -12,17 +12,12 @@ #include "chrome/browser/chromeos/login/user.h" #include "chrome/browser/ui/webui/options/chromeos/set_wallpaper_options_handler.h" -class FilePath; class PrefService; -namespace gfx { -class ImageSkia; -} - namespace chromeos { class RemoveUserDelegate; -class UserImage; +class UserImageManager; // Base class for UserManagerImpl - provides a mechanism for discovering users // who have logged into this Chrome OS device before and updating that list. @@ -39,31 +34,9 @@ class UserManager { virtual ~Observer() {} }; - // A vector pref of the users who have logged into the device. - static const char kLoggedInUsers[]; - // Username for stub login when not running on ChromeOS. static const char kStubUser[]; - // A dictionary that maps usernames to file paths to their wallpapers. - // Deprecated. Will remove this const char after done migration. - static const char kUserWallpapers[]; - - // A dictionary that maps usernames to wallpaper properties. - static const char kUserWallpapersProperties[]; - - // A dictionary that maps usernames to file paths to their images. - static const char kUserImages[]; - - // A dictionary that maps usernames to the displayed name. - static const char kUserDisplayName[]; - - // A dictionary that maps usernames to the displayed (non-canonical) emails. - static const char kUserDisplayEmail[]; - - // A dictionary that maps usernames to OAuth token presence flag. - static const char kUserOAuthTokenStatus[]; - // Returns a shared instance of a UserManager. Not thread-safe, should only be // called from the main UI thread. static UserManager* Get(); @@ -93,6 +66,8 @@ class UserManager { virtual ~UserManager(); + virtual UserImageManager* GetUserImageManager() = 0; + // Returns a list of users who have logged into this device previously. This // is sorted by last login date with the most recent user at the beginning. virtual const UserList& GetUsers() const = 0; @@ -138,8 +113,8 @@ class UserManager { virtual const User* FindUser(const std::string& email) const = 0; // Returns the logged-in user. - virtual const User& GetLoggedInUser() const = 0; - virtual User& GetLoggedInUser() = 0; + virtual const User* GetLoggedInUser() const = 0; + virtual User* GetLoggedInUser() = 0; // Saves user's oauth token status in local state preferences. virtual void SaveUserOAuthStatus( @@ -172,40 +147,11 @@ class UserManager { virtual void SaveLoggedInUserWallpaperProperties(User::WallpaperType type, int index) = 0; - // Sets user image to the default image with index |image_index|, sends - // LOGIN_USER_IMAGE_CHANGED notification and updates Local State. - virtual void SaveUserDefaultImageIndex(const std::string& username, - int image_index) = 0; - - // Saves image to file, sends LOGIN_USER_IMAGE_CHANGED notification and - // updates Local State. - virtual void SaveUserImage(const std::string& username, - const UserImage& user_image) = 0; - // Updates custom wallpaper to selected layout and saves layout to Local // State. virtual void SetLoggedInUserCustomWallpaperLayout( ash::WallpaperLayout layout) = 0; - // Tries to load user image from disk; if successful, sets it for the user, - // sends LOGIN_USER_IMAGE_CHANGED notification and updates Local State. - virtual void SaveUserImageFromFile(const std::string& username, - const FilePath& path) = 0; - - // Sets profile image as user image for |username|, sends - // LOGIN_USER_IMAGE_CHANGED notification and updates Local State. If the user - // is not logged-in or the last |DownloadProfileImage| call has failed, a - // default grey avatar will be used until the user logs in and profile image - // is downloaded successfully. - virtual void SaveUserImageFromProfileImage(const std::string& username) = 0; - - // Starts downloading the profile image for the logged-in user. - // If user's image index is |kProfileImageIndex|, newly downloaded image - // is immediately set as user's current picture. - // |reason| is an arbitrary string (used to report UMA histograms with - // download times). - virtual void DownloadProfileImage(const std::string& reason) = 0; - // Returns true if current user is an owner. virtual bool IsCurrentUserOwner() const = 0; @@ -240,10 +186,6 @@ class UserManager { virtual void RemoveObserver(Observer* obs) = 0; virtual void NotifyLocalStateChanged() = 0; - - // Returns the result of the last successful profile image download, if any. - // Otherwise, returns an empty bitmap. - virtual const gfx::ImageSkia& DownloadedProfileImage() const = 0; }; } // namespace chromeos diff --git a/chrome/browser/chromeos/login/user_manager_impl.cc b/chrome/browser/chromeos/login/user_manager_impl.cc index 752085f..17ae134 100644 --- a/chrome/browser/chromeos/login/user_manager_impl.cc +++ b/chrome/browser/chromeos/login/user_manager_impl.cc @@ -4,8 +4,6 @@ #include "chrome/browser/chromeos/login/user_manager_impl.h" -#include <vector> - #include "ash/desktop_background/desktop_background_controller.h" #include "ash/shell.h" #include "base/bind.h" @@ -15,133 +13,49 @@ #include "base/file_path.h" #include "base/file_util.h" #include "base/logging.h" -#include "base/metrics/histogram.h" -#include "base/path_service.h" #include "base/rand_util.h" -#include "base/stl_util.h" -#include "base/stringprintf.h" -#include "base/time.h" #include "base/utf_string_conversions.h" #include "base/values.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chromeos/cros/cert_library.h" #include "chrome/browser/chromeos/cros/cros_library.h" #include "chrome/browser/chromeos/input_method/input_method_manager.h" -#include "chrome/browser/chromeos/login/default_user_images.h" -#include "chrome/browser/chromeos/login/helper.h" #include "chrome/browser/chromeos/login/login_display.h" #include "chrome/browser/chromeos/login/remove_user_delegate.h" -#include "chrome/browser/chromeos/login/user_image.h" +#include "chrome/browser/chromeos/login/user_image_manager_impl.h" #include "chrome/browser/chromeos/login/wizard_controller.h" #include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/browser/policy/browser_policy_connector.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/prefs/scoped_user_pref_update.h" -#include "chrome/browser/profiles/profile_downloader.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/sync/profile_sync_service.h" #include "chrome/browser/sync/profile_sync_service_factory.h" -#include "chrome/browser/ui/webui/web_ui_util.h" #include "chrome/common/chrome_notification_types.h" -#include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/pref_names.h" #include "chromeos/cryptohome/async_method_caller.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/notification_service.h" -#include "content/public/common/url_constants.h" #include "google_apis/gaia/google_service_auth_error.h" -#include "skia/ext/image_operations.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "ui/gfx/codec/png_codec.h" -#include "ui/gfx/image/image_skia.h" using content::BrowserThread; -typedef GoogleServiceAuthError AuthError; - namespace chromeos { namespace { -// Names of nodes with info about user image. -const char kImagePathNodeName[] = "path"; -const char kImageIndexNodeName[] = "index"; -const char kImageURLNodeName[] = "url"; - -// Delay betweeen user login and attempt to update user's profile data. -const long kProfileDataDownloadDelayMs = 10000; - -// Delay betweeen subsequent profile refresh attempts (24 hrs). -const long kProfileRefreshIntervalMs = 24L * 3600 * 1000; - -// Enum for reporting histograms about profile picture download. -enum ProfileDownloadResult { - kDownloadSuccessChanged, - kDownloadSuccess, - kDownloadFailure, - kDownloadDefault, - kDownloadCached, - - // Must be the last, convenient count. - kDownloadResultsCount -}; - -// Time histogram prefix for a cached profile image download. -const char kProfileDownloadCachedTime[] = - "UserImage.ProfileDownloadTime.Cached"; -// Time histogram prefix for the default profile image download. -const char kProfileDownloadDefaultTime[] = - "UserImage.ProfileDownloadTime.Default"; -// Time histogram prefix for a failed profile image download. -const char kProfileDownloadFailureTime[] = - "UserImage.ProfileDownloadTime.Failure"; -// Time histogram prefix for a successful profile image download. -const char kProfileDownloadSuccessTime[] = - "UserImage.ProfileDownloadTime.Success"; -// Time histogram suffix for a profile image download after login. -const char kProfileDownloadReasonLoggedIn[] = "LoggedIn"; -// Time histogram suffix for a scheduled profile image download. -const char kProfileDownloadReasonScheduled[] = "Scheduled"; - -// Add a histogram showing the time it takes to download a profile image. -// Separate histograms are reported for each download |reason| and |result|. -void AddProfileImageTimeHistogram(ProfileDownloadResult result, - const std::string& download_reason, - const base::TimeDelta& time_delta) { - std::string histogram_name; - switch (result) { - case kDownloadFailure: - histogram_name = kProfileDownloadFailureTime; - break; - case kDownloadDefault: - histogram_name = kProfileDownloadDefaultTime; - break; - case kDownloadSuccess: - histogram_name = kProfileDownloadSuccessTime; - break; - case kDownloadCached: - histogram_name = kProfileDownloadCachedTime; - break; - default: - NOTREACHED(); - } - if (!download_reason.empty()) { - histogram_name += "."; - histogram_name += download_reason; - } +// A vector pref of the users who have logged into the device. +const char kLoggedInUsers[] = "LoggedInUsers"; - static const base::TimeDelta min_time = base::TimeDelta::FromMilliseconds(1); - static const base::TimeDelta max_time = base::TimeDelta::FromSeconds(50); - const size_t bucket_count(50); +// A dictionary that maps usernames to the displayed name. +const char kUserDisplayName[] = "UserDisplayName"; - base::Histogram* counter = base::Histogram::FactoryTimeGet( - histogram_name, min_time, max_time, bucket_count, - base::Histogram::kUmaTargetedHistogramFlag); - counter->AddTime(time_delta); +// A dictionary that maps usernames to the displayed (non-canonical) emails. +const char kUserDisplayEmail[] = "UserDisplayEmail"; - DVLOG(1) << "Profile image download time: " << time_delta.InSecondsF(); -} +// A dictionary that maps usernames to OAuth token presence flag. +const char kUserOAuthTokenStatus[] = "OAuthTokenStatus"; // Callback that is called after user removal is complete. void OnRemoveUserComplete(const std::string& user_email, @@ -186,22 +100,29 @@ void RemoveUserInternal(const std::string& user_email, } // namespace +// static +void UserManager::RegisterPrefs(PrefService* local_state) { + LOG(ERROR) << "UserManager::RegisterPrefs"; + local_state->RegisterListPref(kLoggedInUsers, PrefService::UNSYNCABLE_PREF); + local_state->RegisterDictionaryPref(kUserOAuthTokenStatus, + PrefService::UNSYNCABLE_PREF); + local_state->RegisterDictionaryPref(kUserDisplayName, + PrefService::UNSYNCABLE_PREF); + local_state->RegisterDictionaryPref(kUserDisplayEmail, + PrefService::UNSYNCABLE_PREF); +} + UserManagerImpl::UserManagerImpl() - : image_loader_(new UserImageLoader(ImageDecoder::DEFAULT_CODEC)), - logged_in_user_(NULL), + : logged_in_user_(NULL), session_started_(false), is_current_user_owner_(false), is_current_user_new_(false), is_current_user_ephemeral_(false), ephemeral_users_enabled_(false), observed_sync_service_(NULL), - last_image_set_async_(false), - downloaded_profile_image_data_url_(chrome::kAboutBlankURL), - downloading_profile_image_(false) { + user_image_manager_(new UserImageManagerImpl) { // UserManager instance should be used only on UI thread. DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - MigrateWallpaperData(); registrar_.Add(this, chrome::NOTIFICATION_OWNERSHIP_STATUS_CHANGED, content::NotificationService::AllSources()); registrar_.Add(this, chrome::NOTIFICATION_PROFILE_ADDED, @@ -218,6 +139,10 @@ UserManagerImpl::~UserManagerImpl() { delete logged_in_user_; } +UserImageManager* UserManagerImpl::GetUserImageManager() { + return user_image_manager_.get(); +} + const UserList& UserManagerImpl::GetUsers() const { const_cast<UserManagerImpl*>(this)->EnsureUsersLoaded(); return users_; @@ -247,7 +172,7 @@ void UserManagerImpl::UserLoggedIn(const std::string& email, // Clear the prefs view of the users. PrefService* prefs = g_browser_process->local_state(); - ListPrefUpdate prefs_users_update(prefs, UserManager::kLoggedInUsers); + ListPrefUpdate prefs_users_update(prefs, kLoggedInUsers); prefs_users_update->Clear(); // Make sure this user is first. @@ -273,54 +198,21 @@ void UserManagerImpl::UserLoggedIn(const std::string& email, users_.insert(users_.begin(), logged_in_user_); if (is_current_user_new_) { - SaveUserDisplayName(GetLoggedInUser().email(), - UTF8ToUTF16(GetLoggedInUser().GetAccountName(true))); - SetInitialUserImage(email); + SaveUserDisplayName(logged_in_user_->email(), + UTF8ToUTF16(logged_in_user_->GetAccountName(true))); WallpaperManager::Get()->SetInitialUserWallpaper(email, true); - } else { - int image_index = logged_in_user_->image_index(); - // If current user image is profile image, it needs to be refreshed. - bool download_profile_image = image_index == User::kProfileImageIndex; - if (download_profile_image) - InitDownloadedProfileImage(); - - // Download user's profile data (full name and optionally image) to see if - // it has changed. - BrowserThread::PostDelayedTask( - BrowserThread::UI, - FROM_HERE, - base::Bind(&UserManagerImpl::DownloadProfileData, - base::Unretained(this), - kProfileDownloadReasonLoggedIn, - download_profile_image), - base::TimeDelta::FromMilliseconds(kProfileDataDownloadDelayMs)); - - int histogram_index = image_index; - switch (image_index) { - case User::kExternalImageIndex: - // TODO(avayvod): Distinguish this from selected from file. - histogram_index = kHistogramImageFromCamera; - break; - - case User::kProfileImageIndex: - histogram_index = kHistogramImageFromProfile; - break; - } - UMA_HISTOGRAM_ENUMERATION("UserImage.LoggedIn", - histogram_index, - kHistogramImagesCount); } - // Set up a repeating timer for refreshing the profile data. - profile_download_timer_.Start( - FROM_HERE, base::TimeDelta::FromMilliseconds(kProfileRefreshIntervalMs), - this, &UserManagerImpl::DownloadProfileDataScheduled); + user_image_manager_->UserLoggedIn(email, is_current_user_new_); if (!browser_restart) { // For GAIA login flow, logged in user wallpaper may not be loaded. WallpaperManager::Get()->EnsureLoggedInUserWallpaperLoaded(); } + // Make sure we persist new user data to Local State. + prefs->CommitPendingWrite(); + NotifyOnLogin(); } @@ -329,7 +221,7 @@ void UserManagerImpl::DemoUserLoggedIn() { is_current_user_new_ = true; is_current_user_ephemeral_ = true; logged_in_user_ = CreateUser(kDemoUser, /* is_ephemeral= */ true); - SetInitialUserImage(kDemoUser); + user_image_manager_->UserLoggedIn(kDemoUser, /* user_is_new= */ true); WallpaperManager::Get()->SetInitialUserWallpaper(kDemoUser, false); NotifyOnLogin(); } @@ -339,7 +231,7 @@ void UserManagerImpl::GuestUserLoggedIn() { is_current_user_ephemeral_ = true; WallpaperManager::Get()->SetInitialUserWallpaper(kGuestUser, false); logged_in_user_ = CreateUser(kGuestUser, /* is_ephemeral= */ true); - logged_in_user_->SetStubImage(User::kInvalidImageIndex); + logged_in_user_->SetStubImage(User::kInvalidImageIndex, false); NotifyOnLogin(); } @@ -348,7 +240,7 @@ void UserManagerImpl::EphemeralUserLoggedIn(const std::string& email) { is_current_user_new_ = true; is_current_user_ephemeral_ = true; logged_in_user_ = CreateUser(email, /* is_ephemeral= */ true); - SetInitialUserImage(email); + user_image_manager_->UserLoggedIn(email, /* user_is_new= */ true); WallpaperManager::Get()->SetInitialUserWallpaper(email, false); NotifyOnLogin(); } @@ -360,6 +252,10 @@ void UserManagerImpl::SessionStarted() { chrome::NOTIFICATION_SESSION_STARTED, content::NotificationService::AllSources(), content::NotificationService::NoDetails()); + if (is_current_user_new_) { + // Make sure we persist new user data to Local State. + g_browser_process->local_state()->CommitPendingWrite(); + } } void UserManagerImpl::RemoveUser(const std::string& email, @@ -401,14 +297,14 @@ const User* UserManagerImpl::FindUser(const std::string& email) const { return FindUserInList(email); } -const User& UserManagerImpl::GetLoggedInUser() const { +const User* UserManagerImpl::GetLoggedInUser() const { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - return *logged_in_user_; + return logged_in_user_; } -User& UserManagerImpl::GetLoggedInUser() { +User* UserManagerImpl::GetLoggedInUser() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - return *logged_in_user_; + return logged_in_user_; } void UserManagerImpl::SaveUserOAuthStatus( @@ -427,8 +323,7 @@ void UserManagerImpl::SaveUserOAuthStatus( PrefService* local_state = g_browser_process->local_state(); - DictionaryPrefUpdate oauth_status_update(local_state, - UserManager::kUserOAuthTokenStatus); + DictionaryPrefUpdate oauth_status_update(local_state, kUserOAuthTokenStatus); oauth_status_update->SetWithoutPathExpansion(username, new base::FundamentalValue(static_cast<int>(oauth_token_status))); } @@ -444,7 +339,7 @@ User::OAuthTokenStatus UserManagerImpl::LoadUserOAuthStatus( } else { PrefService* local_state = g_browser_process->local_state(); const DictionaryValue* prefs_oauth_status = - local_state->GetDictionary(UserManager::kUserOAuthTokenStatus); + local_state->GetDictionary(kUserOAuthTokenStatus); int oauth_token_status = User::OAUTH_TOKEN_STATUS_UNKNOWN; if (prefs_oauth_status && @@ -473,8 +368,7 @@ void UserManagerImpl::SaveUserDisplayName(const std::string& username, PrefService* local_state = g_browser_process->local_state(); - DictionaryPrefUpdate display_name_update(local_state, - UserManager::kUserDisplayName); + DictionaryPrefUpdate display_name_update(local_state, kUserDisplayName); display_name_update->SetWithoutPathExpansion( username, base::Value::CreateStringValue(display_name)); @@ -502,8 +396,7 @@ void UserManagerImpl::SaveUserDisplayEmail(const std::string& username, PrefService* local_state = g_browser_process->local_state(); - DictionaryPrefUpdate display_email_update(local_state, - UserManager::kUserDisplayEmail); + DictionaryPrefUpdate display_email_update(local_state, kUserDisplayEmail); display_email_update->SetWithoutPathExpansion( username, base::Value::CreateStringValue(display_email)); @@ -515,21 +408,6 @@ std::string UserManagerImpl::GetUserDisplayEmail( return user ? user->display_email() : username; } -void UserManagerImpl::SaveUserDefaultImageIndex(const std::string& username, - int image_index) { - DCHECK(image_index >= 0 && image_index < kDefaultImagesCount); - SetUserImage(username, image_index, GURL(), - UserImage(GetDefaultImage(image_index))); - SaveImageToLocalState(username, "", image_index, GURL(), false); -} - -void UserManagerImpl::SaveUserImage(const std::string& username, - const UserImage& user_image) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - SaveUserImageInternal(username, User::kExternalImageIndex, - GURL(), user_image); -} - void UserManagerImpl::SetLoggedInUserCustomWallpaperLayout( ash::WallpaperLayout layout) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); @@ -539,8 +417,7 @@ void UserManagerImpl::SetLoggedInUserCustomWallpaperLayout( // memory. Need a smarter way to solve this. if (IsCurrentUserEphemeral()) return; - const chromeos::User& user = GetLoggedInUser(); - std::string username = user.email(); + std::string username = logged_in_user_->email(); DCHECK(!username.empty()); std::string file_path = WallpaperManager::Get()-> @@ -550,37 +427,6 @@ void UserManagerImpl::SetLoggedInUserCustomWallpaperLayout( WallpaperManager::Get()->SetUserWallpaper(username); } -void UserManagerImpl::SaveUserImageFromFile(const std::string& username, - const FilePath& path) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - image_loader_->Start( - path.value(), login::kMaxUserImageSize, - base::Bind(&UserManagerImpl::SaveUserImage, - base::Unretained(this), username)); -} - -void UserManagerImpl::SaveUserImageFromProfileImage( - const std::string& username) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - if (!downloaded_profile_image_.isNull()) { - // Profile image has already been downloaded, so save it to file right now. - DCHECK(profile_image_url_.is_valid()); - SaveUserImageInternal( - username, - User::kProfileImageIndex, profile_image_url_, - UserImage::CreateAndEncode(downloaded_profile_image_)); - } else { - // No profile image - use the stub image (gray avatar). - SetUserImage(username, User::kProfileImageIndex, GURL(), UserImage()); - SaveImageToLocalState(username, "", User::kProfileImageIndex, - GURL(), false); - } -} - -void UserManagerImpl::DownloadProfileImage(const std::string& reason) { - DownloadProfileData(reason, true); -} - void UserManagerImpl::Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) { @@ -609,17 +455,18 @@ void UserManagerImpl::Observe(int type, void UserManagerImpl::OnStateChanged() { DCHECK(IsUserLoggedIn() && !IsLoggedInAsGuest()); - AuthError::State state = observed_sync_service_->GetAuthError().state(); - if (state != AuthError::NONE && - state != AuthError::CONNECTION_FAILED && - state != AuthError::SERVICE_UNAVAILABLE && - state != AuthError::REQUEST_CANCELED) { + GoogleServiceAuthError::State state = + observed_sync_service_->GetAuthError().state(); + if (state != GoogleServiceAuthError::NONE && + state != GoogleServiceAuthError::CONNECTION_FAILED && + state != GoogleServiceAuthError::SERVICE_UNAVAILABLE && + state != GoogleServiceAuthError::REQUEST_CANCELED) { // Invalidate OAuth token to force Gaia sign-in flow. This is needed // because sign-out/sign-in solution is suggested to the user. // TODO(altimofeev): this code isn't needed after crosbug.com/25978 is // implemented. DVLOG(1) << "Invalidate OAuth token because of a sync error."; - SaveUserOAuthStatus(GetLoggedInUser().email(), + SaveUserOAuthStatus(logged_in_user_->email(), User::OAUTH_TOKEN_STATUS_INVALID); } } @@ -704,24 +551,12 @@ void UserManagerImpl::RemoveObserver(UserManager::Observer* obs) { observer_list_.RemoveObserver(obs); } -const gfx::ImageSkia& UserManagerImpl::DownloadedProfileImage() const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - return downloaded_profile_image_; -} - void UserManagerImpl::NotifyLocalStateChanged() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); FOR_EACH_OBSERVER(UserManager::Observer, observer_list_, LocalStateChanged(this)); } -FilePath UserManagerImpl::GetImagePathForUser(const std::string& username) { - std::string filename = username + ".png"; - FilePath user_data_dir; - PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); - return user_data_dir.AppendASCII(filename); -} - void UserManagerImpl::EnsureUsersLoaded() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (!users_.empty()) @@ -731,96 +566,40 @@ void UserManagerImpl::EnsureUsersLoaded() { PrefService* local_state = g_browser_process->local_state(); const ListValue* prefs_users = - local_state->GetList(UserManager::kLoggedInUsers); - const DictionaryValue* prefs_images = - local_state->GetDictionary(UserManager::kUserImages); + local_state->GetList(kLoggedInUsers); const DictionaryValue* prefs_display_names = - local_state->GetDictionary(UserManager::kUserDisplayName); + local_state->GetDictionary(kUserDisplayName); const DictionaryValue* prefs_display_emails = - local_state->GetDictionary(UserManager::kUserDisplayEmail); - - if (prefs_users) { - for (ListValue::const_iterator it = prefs_users->begin(); - it != prefs_users->end(); ++it) { - std::string email; - if ((*it)->GetAsString(&email)) { - User* user = CreateUser(email, /* is_ephemeral= */ false); - users_.push_back(user); - - if (prefs_images) { - // Get account image path. - // TODO(avayvod): Reading image path as a string is here for - // backward compatibility. - std::string image_path; - const base::DictionaryValue* image_properties; - if (prefs_images->GetStringWithoutPathExpansion(email, &image_path)) { - int image_id = User::kInvalidImageIndex; - if (IsDefaultImagePath(image_path, &image_id)) { - user->SetImage(UserImage(GetDefaultImage(image_id)), image_id); - } else { - int image_index = User::kExternalImageIndex; - // Until image has been loaded, use the stub image. - user->SetStubImage(image_index); - DCHECK(!image_path.empty()); - // Load user image asynchronously. - image_loader_->Start( - image_path, 0 /* no resize */, - base::Bind(&UserManagerImpl::SetUserImage, - base::Unretained(this), - email, image_index, GURL())); - } - } else if (prefs_images->GetDictionaryWithoutPathExpansion( - email, &image_properties)) { - int image_index = User::kInvalidImageIndex; - image_properties->GetString(kImagePathNodeName, &image_path); - image_properties->GetInteger(kImageIndexNodeName, &image_index); - if (image_index >= 0 && image_index < kDefaultImagesCount) { - user->SetImage(UserImage(GetDefaultImage(image_index)), - image_index); - } else if (image_index == User::kExternalImageIndex || - image_index == User::kProfileImageIndex) { - // Path may be empty for profile images (meaning that the image - // hasn't been downloaded for the first time yet, in which case a - // download will be scheduled for |kProfileDataDownloadDelayMs| - // after user logs in). - DCHECK(!image_path.empty() || - image_index == User::kProfileImageIndex); - std::string image_url; - image_properties->GetString(kImageURLNodeName, &image_url); - GURL image_gurl(image_url); - // Until image has been loaded, use the stub image (gray avatar). - user->SetStubImage(image_index); - user->SetImageURL(image_gurl); - if (!image_path.empty()) { - // Load user image asynchronously. - image_loader_->Start( - image_path, 0 /* no resize */, - base::Bind(&UserManagerImpl::SetUserImage, - base::Unretained(this), - email, image_index, image_gurl)); - } - } else { - NOTREACHED(); - } - } - } + local_state->GetDictionary(kUserDisplayEmail); - string16 display_name; - if (prefs_display_names && - prefs_display_names->GetStringWithoutPathExpansion( - email, &display_name)) { - user->set_display_name(display_name); - } + if (!prefs_users) + return; - std::string display_email; - if (prefs_display_emails && - prefs_display_emails->GetStringWithoutPathExpansion( - email, &display_email)) { - user->set_display_email(display_email); - } + for (ListValue::const_iterator it = prefs_users->begin(); + it != prefs_users->end(); ++it) { + std::string email; + if ((*it)->GetAsString(&email)) { + User* user = CreateUser(email, /* is_ephemeral= */ false); + users_.push_back(user); + + string16 display_name; + if (prefs_display_names && + prefs_display_names->GetStringWithoutPathExpansion( + email, &display_name)) { + user->set_display_name(display_name); + } + + std::string display_email; + if (prefs_display_emails && + prefs_display_emails->GetStringWithoutPathExpansion( + email, &display_email)) { + user->set_display_email(display_email); } } } + + user_image_manager_->LoadUserImages(users_); + WallpaperManager::Get()->MigrateWallpaperData(users_); } void UserManagerImpl::RetrieveTrustedDevicePolicies() { @@ -859,7 +638,7 @@ void UserManagerImpl::RetrieveTrustedDevicePolicies() { if (changed) { content::NotificationService::current()->Notify( chrome::NOTIFICATION_POLICY_USER_LIST_CHANGED, - content::Source<UserManagerImpl>(this), + content::Source<UserManager>(this), content::NotificationService::NoDetails()); } } @@ -884,7 +663,7 @@ void UserManagerImpl::NotifyOnLogin() { CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); content::NotificationService::current()->Notify( chrome::NOTIFICATION_LOGIN_USER_CHANGED, - content::Source<UserManagerImpl>(this), + content::Source<UserManager>(this), content::Details<const User>(logged_in_user_)); CrosLibrary::Get()->GetCertLibrary()->LoadKeyStore(); @@ -894,177 +673,13 @@ void UserManagerImpl::NotifyOnLogin() { DeviceSettingsService::Get()->SetUsername(logged_in_user_->email()); } -void UserManagerImpl::SetInitialUserImage(const std::string& username) { - // Choose a random default image. - int image_id = - base::RandInt(kFirstDefaultImageIndex, kDefaultImagesCount - 1); - SaveUserDefaultImageIndex(username, image_id); -} - -void UserManagerImpl::MigrateWallpaperData() { - PrefService* local_state = g_browser_process->local_state(); - if (local_state) { - const DictionaryValue* user_wallpapers = - local_state->GetDictionary(kUserWallpapers); - int index; - const DictionaryValue* new_user_wallpapers = - local_state->GetDictionary(kUserWallpapersProperties); - if (new_user_wallpapers->empty()) { - const UserList& users = GetUsers(); - for (UserList::const_iterator it = users.begin(); - it != users.end(); - ++it) { - std::string username = (*it)->email(); - if (user_wallpapers->GetIntegerWithoutPathExpansion((username), - &index)) { - DictionaryPrefUpdate prefs_wallpapers_update(local_state, - kUserWallpapers); - prefs_wallpapers_update->RemoveWithoutPathExpansion(username, NULL); - WallpaperManager::Get()->SetUserWallpaperProperties(username, - User::DEFAULT, - index, - true); - } else { - // Before M20, wallpaper index is not saved into LocalState unless - // user specifically sets a wallpaper. After M20, the default - // wallpaper index is saved to LocalState as soon as a new user login. - // When migrating wallpaper index from M20 to M21, we only migrate - // data that is in LocalState. This cause a problem when users login - // on a M20 device and then update the device to M21. The default - // wallpaper index failed to migrate because it was not saved into - // LocalState. Then we assume that all users have index saved in - // LocalState in M21. This is not true and it results an empty - // wallpaper for those users as described in cr/130685. So here we use - // default wallpaper for users that exist in user list but does not - // have an index saved in LocalState. - WallpaperManager::Get()->SetUserWallpaperProperties(username, - User::DEFAULT, ash::GetDefaultWallpaperIndex(), true); - } - } - } - } -} - void UserManagerImpl::SaveLoggedInUserWallpaperProperties( User::WallpaperType type, int index) { // Ephemeral users can not save data to local state. // We just cache the index in memory for them. bool is_persistent = !IsCurrentUserEphemeral(); WallpaperManager::Get()->SetUserWallpaperProperties( - GetLoggedInUser().email(), type, index, is_persistent); -} - -void UserManagerImpl::SetUserImage(const std::string& username, - int image_index, - const GURL& image_url, - const UserImage& user_image) { - User* user = const_cast<User*>(FindUser(username)); - // User may have been removed by now. - if (user) { - // For existing users, a valid image index should have been set upon loading - // them from Local State. - DCHECK(user->image_index() != User::kInvalidImageIndex || - is_current_user_new_); - bool image_changed = user->image_index() != User::kInvalidImageIndex; - if (!user_image.image().isNull()) - user->SetImage(user_image, image_index); - else - user->SetStubImage(image_index); - user->SetImageURL(image_url); - // For the logged-in user with a profile picture, initialize - // |downloaded_profile_picture_|. - if (user == logged_in_user_ && image_index == User::kProfileImageIndex) - InitDownloadedProfileImage(); - if (image_changed) { - // Unless this is first-time setting with |SetInitialUserImage|, - // send a notification about image change. - content::NotificationService::current()->Notify( - chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED, - content::Source<UserManagerImpl>(this), - content::Details<const User>(user)); - } - } -} - -void UserManagerImpl::SaveUserImageInternal(const std::string& username, - int image_index, - const GURL& image_url, - const UserImage& user_image) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - SetUserImage(username, image_index, image_url, user_image); - - // Ignore for ephemeral users. - if (IsEphemeralUser(username)) - return; - - FilePath image_path = GetImagePathForUser(username); - DVLOG(1) << "Saving user image to " << image_path.value(); - - last_image_set_async_ = true; - - BrowserThread::PostTask( - BrowserThread::FILE, - FROM_HERE, - base::Bind(&UserManagerImpl::SaveImageToFile, - base::Unretained(this), - username, user_image, image_path, image_index, image_url)); -} - -void UserManagerImpl::SaveImageToFile(const std::string& username, - const UserImage& user_image, - const FilePath& image_path, - int image_index, - const GURL& image_url) { - if (!SaveBitmapToFile(user_image, image_path)) - return; - - BrowserThread::PostTask( - BrowserThread::UI, - FROM_HERE, - base::Bind(&UserManagerImpl::SaveImageToLocalState, - base::Unretained(this), - username, image_path.value(), image_index, image_url, true)); -} - -void UserManagerImpl::SaveImageToLocalState(const std::string& username, - const std::string& image_path, - int image_index, - const GURL& image_url, - bool is_async) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - // Ignore for ephemeral users. - if (IsEphemeralUser(username)) - return; - - // TODO(ivankr): use unique filenames for user images each time - // a new image is set so that only the last image update is saved - // to Local State and notified. - if (is_async && !last_image_set_async_) { - DVLOG(1) << "Ignoring saved image because it has changed"; - return; - } else if (!is_async) { - // Reset the async image save flag if called directly from the UI thread. - last_image_set_async_ = false; - } - - PrefService* local_state = g_browser_process->local_state(); - DictionaryPrefUpdate images_update(local_state, UserManager::kUserImages); - base::DictionaryValue* image_properties = new base::DictionaryValue(); - image_properties->Set(kImagePathNodeName, new StringValue(image_path)); - image_properties->Set(kImageIndexNodeName, - new base::FundamentalValue(image_index)); - if (!image_url.is_empty()) { - image_properties->Set(kImageURLNodeName, - new StringValue(image_url.spec())); - } else { - image_properties->Remove(kImageURLNodeName, NULL); - } - images_update->SetWithoutPathExpansion(username, image_properties); - DVLOG(1) << "Saving path to user image in Local State."; - - NotifyLocalStateChanged(); + logged_in_user_->email(), type, index, is_persistent); } void UserManagerImpl::SaveWallpaperToLocalState(const std::string& username, @@ -1076,78 +691,6 @@ void UserManagerImpl::SaveWallpaperToLocalState(const std::string& username, username, type, layout, true); } -bool UserManagerImpl::SaveBitmapToFile(const UserImage& user_image, - const FilePath& image_path) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - - const UserImage::RawImage* encoded_image = NULL; - if (user_image.has_animated_image()) { - encoded_image = &user_image.animated_image(); - } else if (user_image.has_raw_image()) { - encoded_image = &user_image.raw_image(); - } else { - LOG(ERROR) << "Failed to encode the image."; - return false; - } - DCHECK(encoded_image != NULL); - - if (file_util::WriteFile(image_path, - reinterpret_cast<const char*>(&(*encoded_image)[0]), - encoded_image->size()) == -1) { - LOG(ERROR) << "Failed to save image to file."; - return false; - } - return true; -} - -void UserManagerImpl::InitDownloadedProfileImage() { - DCHECK(logged_in_user_); - DCHECK_EQ(logged_in_user_->image_index(), User::kProfileImageIndex); - if (downloaded_profile_image_.isNull() && !logged_in_user_->image_is_stub()) { - VLOG(1) << "Profile image initialized"; - downloaded_profile_image_ = logged_in_user_->image(); - downloaded_profile_image_data_url_ = - web_ui_util::GetImageDataUrl(downloaded_profile_image_); - profile_image_url_ = logged_in_user_->image_url(); - } -} - -void UserManagerImpl::DownloadProfileData(const std::string& reason, - bool download_image) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - // For guest login there's no profile image to download. - if (IsLoggedInAsGuest()) - return; - - // Mark profile picture as needed. - downloading_profile_image_ |= download_image; - - // Another download is already in progress - if (profile_image_downloader_.get()) - return; - - profile_image_download_reason_ = reason; - profile_image_load_start_time_ = base::Time::Now(); - profile_image_downloader_.reset(new ProfileDownloader(this)); - profile_image_downloader_->Start(); -} - -void UserManagerImpl::DownloadProfileDataScheduled() { - // If current user image is profile image, it needs to be refreshed. - bool download_profile_image = - logged_in_user_->image_index() == User::kProfileImageIndex; - DownloadProfileData(kProfileDownloadReasonScheduled, download_profile_image); -} - -void UserManagerImpl::DeleteUserImage(const FilePath& image_path) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - if (!file_util::Delete(image_path, false)) { - LOG(ERROR) << "Failed to remove user image."; - return; - } -} - void UserManagerImpl::UpdateOwnership( DeviceSettingsService::OwnershipStatus status, bool is_owner) { @@ -1162,120 +705,6 @@ void UserManagerImpl::CheckOwnership() { base::Unretained(this))); } -// ProfileDownloaderDelegate override. -bool UserManagerImpl::NeedsProfilePicture() const { - return downloading_profile_image_; -} - -// ProfileDownloaderDelegate override. -int UserManagerImpl::GetDesiredImageSideLength() const { - return GetCurrentUserImageSize(); -} - -// ProfileDownloaderDelegate override. -std::string UserManagerImpl::GetCachedPictureURL() const { - return profile_image_url_.spec(); -} - -Profile* UserManagerImpl::GetBrowserProfile() { - return ProfileManager::GetDefaultProfile(); -} - -void UserManagerImpl::OnProfileDownloadSuccess(ProfileDownloader* downloader) { - // Make sure that |ProfileDownloader| gets deleted after return. - scoped_ptr<ProfileDownloader> profile_image_downloader( - profile_image_downloader_.release()); - DCHECK_EQ(downloader, profile_image_downloader.get()); - - if (!downloader->GetProfileFullName().empty()) { - SaveUserDisplayName(GetLoggedInUser().email(), - downloader->GetProfileFullName()); - } - - bool requested_image = downloading_profile_image_; - downloading_profile_image_ = false; - if (!requested_image) - return; - - ProfileDownloadResult result = kDownloadFailure; - switch (downloader->GetProfilePictureStatus()) { - case ProfileDownloader::PICTURE_SUCCESS: - result = kDownloadSuccess; - break; - case ProfileDownloader::PICTURE_CACHED: - result = kDownloadCached; - break; - case ProfileDownloader::PICTURE_DEFAULT: - result = kDownloadDefault; - break; - default: - NOTREACHED(); - } - - UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult", - result, kDownloadResultsCount); - - DCHECK(!profile_image_load_start_time_.is_null()); - base::TimeDelta delta = base::Time::Now() - profile_image_load_start_time_; - AddProfileImageTimeHistogram(result, profile_image_download_reason_, delta); - - if (result == kDownloadDefault) { - content::NotificationService::current()->Notify( - chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED, - content::Source<UserManagerImpl>(this), - content::NotificationService::NoDetails()); - } - - // Nothing to do if picture is cached or the default avatar. - if (result != kDownloadSuccess) - return; - - // Check if this image is not the same as already downloaded. - gfx::ImageSkia new_image(downloader->GetProfilePicture()); - std::string new_image_data_url = web_ui_util::GetImageDataUrl(new_image); - if (!downloaded_profile_image_data_url_.empty() && - new_image_data_url == downloaded_profile_image_data_url_) - return; - - downloaded_profile_image_data_url_ = new_image_data_url; - downloaded_profile_image_ = gfx::ImageSkia(downloader->GetProfilePicture()); - profile_image_url_ = GURL(downloader->GetProfilePictureURL()); - - if (GetLoggedInUser().image_index() == User::kProfileImageIndex) { - VLOG(1) << "Updating profile image for logged-in user"; - UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult", - kDownloadSuccessChanged, - kDownloadResultsCount); - // This will persist |downloaded_profile_image_| to file. - SaveUserImageFromProfileImage(GetLoggedInUser().email()); - } - - content::NotificationService::current()->Notify( - chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED, - content::Source<UserManagerImpl>(this), - content::Details<const gfx::ImageSkia>(&downloaded_profile_image_)); -} - -void UserManagerImpl::OnProfileDownloadFailure(ProfileDownloader* downloader) { - DCHECK_EQ(downloader, profile_image_downloader_.get()); - profile_image_downloader_.reset(); - - downloading_profile_image_ = false; - - UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult", - kDownloadFailure, kDownloadResultsCount); - - DCHECK(!profile_image_load_start_time_.is_null()); - base::TimeDelta delta = base::Time::Now() - profile_image_load_start_time_; - AddProfileImageTimeHistogram(kDownloadFailure, profile_image_download_reason_, - delta); - - content::NotificationService::current()->Notify( - chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED, - content::Source<UserManagerImpl>(this), - content::NotificationService::NoDetails()); -} - User* UserManagerImpl::CreateUser(const std::string& email, bool is_ephemeral) const { User* user = new User(email); @@ -1302,11 +731,6 @@ void UserManagerImpl::RemoveUserFromListInternal(const std::string& email) { WallpaperManager::Get()->RemoveUserWallpaperInfo(email); - DictionaryPrefUpdate prefs_images_update(prefs, kUserImages); - std::string image_path_string; - prefs_images_update->GetStringWithoutPathExpansion(email, &image_path_string); - prefs_images_update->RemoveWithoutPathExpansion(email, NULL); - DictionaryPrefUpdate prefs_oauth_update(prefs, kUserOAuthTokenStatus); int oauth_status; prefs_oauth_update->GetIntegerWithoutPathExpansion(email, &oauth_status); @@ -1322,18 +746,6 @@ void UserManagerImpl::RemoveUserFromListInternal(const std::string& email) { delete *user_to_remove; users_.erase(user_to_remove); } - - int default_image_id = User::kInvalidImageIndex; - if (!image_path_string.empty() && - !IsDefaultImagePath(image_path_string, &default_image_id)) { - FilePath image_path(image_path_string); - BrowserThread::PostTask( - BrowserThread::FILE, - FROM_HERE, - base::Bind(&UserManagerImpl::DeleteUserImage, - base::Unretained(this), - image_path)); - } } } // namespace chromeos diff --git a/chrome/browser/chromeos/login/user_manager_impl.h b/chrome/browser/chromeos/login/user_manager_impl.h index 10cf895..801ed8a 100644 --- a/chrome/browser/chromeos/login/user_manager_impl.h +++ b/chrome/browser/chromeos/login/user_manager_impl.h @@ -8,45 +8,36 @@ #include <string> #include "base/basictypes.h" -#include "base/hash_tables.h" #include "base/memory/scoped_ptr.h" #include "base/memory/singleton.h" -#include "base/memory/weak_ptr.h" #include "base/observer_list.h" #include "base/synchronization/lock.h" -#include "base/time.h" -#include "base/timer.h" #include "chrome/browser/api/sync/profile_sync_service_observer.h" #include "chrome/browser/chromeos/login/user.h" -#include "chrome/browser/chromeos/login/user_image_loader.h" +#include "chrome/browser/chromeos/login/user_image_manager_impl.h" #include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/chromeos/login/wallpaper_manager.h" #include "chrome/browser/chromeos/settings/device_settings_service.h" -#include "chrome/browser/profiles/profile_downloader_delegate.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" -#include "ui/gfx/image/image_skia.h" class FilePath; class PrefService; -class ProfileDownloader; class ProfileSyncService; -class SkBitmap; namespace chromeos { class RemoveUserDelegate; -class UserImage; // Implementation of the UserManager. class UserManagerImpl : public UserManager, - public ProfileDownloaderDelegate, public ProfileSyncServiceObserver, public content::NotificationObserver { public: - // UserManager implementation: virtual ~UserManagerImpl(); + // UserManager implementation: + virtual UserImageManager* GetUserImageManager() OVERRIDE; virtual const UserList& GetUsers() const OVERRIDE; virtual void UserLoggedIn(const std::string& email, bool browser_restart) OVERRIDE; @@ -59,8 +50,8 @@ class UserManagerImpl : public UserManager, virtual void RemoveUserFromList(const std::string& email) OVERRIDE; virtual bool IsKnownUser(const std::string& email) const OVERRIDE; virtual const User* FindUser(const std::string& email) const OVERRIDE; - virtual const User& GetLoggedInUser() const OVERRIDE; - virtual User& GetLoggedInUser() OVERRIDE; + virtual const User* GetLoggedInUser() const OVERRIDE; + virtual User* GetLoggedInUser() OVERRIDE; virtual void SaveUserOAuthStatus( const std::string& username, User::OAuthTokenStatus oauth_token_status) OVERRIDE; @@ -74,17 +65,8 @@ class UserManagerImpl : public UserManager, const std::string& username) const OVERRIDE; virtual void SaveLoggedInUserWallpaperProperties(User::WallpaperType type, int index) OVERRIDE; - virtual void SaveUserDefaultImageIndex(const std::string& username, - int image_index) OVERRIDE; - virtual void SaveUserImage(const std::string& username, - const UserImage& user_image) OVERRIDE; virtual void SetLoggedInUserCustomWallpaperLayout( ash::WallpaperLayout layout) OVERRIDE; - virtual void SaveUserImageFromFile(const std::string& username, - const FilePath& path) OVERRIDE; - virtual void SaveUserImageFromProfileImage( - const std::string& username) OVERRIDE; - virtual void DownloadProfileImage(const std::string& reason) OVERRIDE; virtual bool IsCurrentUserOwner() const OVERRIDE; virtual bool IsCurrentUserNew() const OVERRIDE; virtual bool IsCurrentUserEphemeral() const OVERRIDE; @@ -97,7 +79,6 @@ class UserManagerImpl : public UserManager, virtual void AddObserver(UserManager::Observer* obs) OVERRIDE; virtual void RemoveObserver(UserManager::Observer* obs) OVERRIDE; virtual void NotifyLocalStateChanged() OVERRIDE; - virtual const gfx::ImageSkia& DownloadedProfileImage() const OVERRIDE; // content::NotificationObserver implementation. virtual void Observe(int type, @@ -107,16 +88,12 @@ class UserManagerImpl : public UserManager, // ProfileSyncServiceObserver implementation. virtual void OnStateChanged() OVERRIDE; - protected: - UserManagerImpl(); - - // Returns image filepath for the given user. - FilePath GetImagePathForUser(const std::string& username); - private: friend class UserManagerImplWrapper; - friend class UserManagerTest; friend class WallpaperManager; + friend class UserManagerTest; + + UserManagerImpl(); // Loads |users_| from Local State if the list has not been loaded yet. // Subsequent calls have no effect. Must be called on the UI thread. @@ -143,76 +120,12 @@ class UserManagerImpl : public UserManager, void SetCurrentUserIsOwner(bool is_current_user_owner); - // Sets one of the default images for the specified user and saves this - // setting in local state. - // Does not send LOGIN_USER_IMAGE_CHANGED notification. - void SetInitialUserImage(const std::string& username); - - // Migrate the old wallpaper index to a new wallpaper structure. - // The new wallpaper structure is: - // { WallpaperType: DAILY|CUSTOMIZED|DEFAULT, - // index: index of the default wallpapers } - void MigrateWallpaperData(); - - // Sets image for user |username| and sends LOGIN_USER_IMAGE_CHANGED - // notification unless this is a new user and image is set for the first time. - // If |image| is empty, sets a stub image for the user. - void SetUserImage(const std::string& username, - int image_index, - const GURL& image_url, - const UserImage& user_image); - - // Saves image to file, updates local state preferences to given image index - // and sends LOGIN_USER_IMAGE_CHANGED notification. - void SaveUserImageInternal(const std::string& username, - int image_index, - const GURL& image_url, - const UserImage& user_image); - - // Saves image to file with specified path and sends LOGIN_USER_IMAGE_CHANGED - // notification. Runs on FILE thread. Posts task for saving image info to - // Local State on UI thread. - void SaveImageToFile(const std::string& username, - const UserImage& user_image, - const FilePath& image_path, - int image_index, - const GURL& image_url); - - // Stores path to the image and its index in local state. Runs on UI thread. - // If |is_async| is true, it has been posted from the FILE thread after - // saving the image. - void SaveImageToLocalState(const std::string& username, - const std::string& image_path, - int image_index, - const GURL& image_url, - bool is_async); - // Stores layout and type preference in local state. Runs on UI thread. void SaveWallpaperToLocalState(const std::string& username, const std::string& wallpaper_path, ash::WallpaperLayout layout, User::WallpaperType type); - // Saves |image| to the specified |image_path|. Runs on FILE thread. - bool SaveBitmapToFile(const UserImage& user_image, - const FilePath& image_path); - - // Initializes |downloaded_profile_image_| with the picture of the logged-in - // user. - void InitDownloadedProfileImage(); - - // Download user's profile data, including full name and picture, when - // |download_image| is true. - // |reason| is an arbitrary string (used to report UMA histograms with - // download times). - void DownloadProfileData(const std::string& reason, bool download_image); - - // Scheduled call for downloading profile data. - void DownloadProfileDataScheduled(); - - // Deletes user's image file. Runs on FILE thread. - void DeleteUserImage(const FilePath& image_path); - // Updates current user ownership on UI thread. void UpdateOwnership(DeviceSettingsService::OwnershipStatus status, bool is_owner); @@ -220,14 +133,6 @@ class UserManagerImpl : public UserManager, // Triggers an asynchronous ownership check. void CheckOwnership(); - // ProfileDownloaderDelegate implementation. - virtual bool NeedsProfilePicture() const OVERRIDE; - virtual int GetDesiredImageSideLength() const OVERRIDE; - virtual Profile* GetBrowserProfile() OVERRIDE; - virtual std::string GetCachedPictureURL() const OVERRIDE; - virtual void OnProfileDownloadSuccess(ProfileDownloader* downloader) OVERRIDE; - virtual void OnProfileDownloadFailure(ProfileDownloader* downloader) OVERRIDE; - // Creates a new User instance. User* CreateUser(const std::string& email, bool is_ephemeral) const; @@ -235,9 +140,6 @@ class UserManagerImpl : public UserManager, // picture. void RemoveUserFromListInternal(const std::string& email); - // Loads user image from its file. - scoped_refptr<UserImageLoader> image_loader_; - // List of all known users. User instances are owned by |this| and deleted // when users are removed by |RemoveUserFromListInternal|. mutable UserList users_; @@ -285,35 +187,7 @@ class UserManagerImpl : public UserManager, ObserverList<UserManager::Observer> observer_list_; - // Download user profile image on login to update it if it's changed. - scoped_ptr<ProfileDownloader> profile_image_downloader_; - - // Arbitrary string passed to the last |DownloadProfileImage| call. - std::string profile_image_download_reason_; - - // Time when the profile image download has started. - base::Time profile_image_load_start_time_; - - // True if the last user image required async save operation (which may not - // have been completed yet). This flag is used to avoid races when user image - // is first set with |SaveUserImage| and then with |SaveUserImagePath|. - bool last_image_set_async_; - - // Result of the last successful profile image download, if any. - gfx::ImageSkia downloaded_profile_image_; - - // Data URL for |downloaded_profile_image_|. - std::string downloaded_profile_image_data_url_; - - // Original URL of |downloaded_profile_image_|, from which it was downloaded. - GURL profile_image_url_; - - // True when |profile_image_downloader_| is fetching profile picture (not - // just full name). - bool downloading_profile_image_; - - // Timer triggering DownloadProfileDataScheduled for refreshing profile data. - base::RepeatingTimer<UserManagerImpl> profile_download_timer_; + scoped_ptr<UserImageManagerImpl> user_image_manager_; DISALLOW_COPY_AND_ASSIGN(UserManagerImpl); }; diff --git a/chrome/browser/chromeos/login/user_manager_unittest.cc b/chrome/browser/chromeos/login/user_manager_unittest.cc index c9b2df8..8d8f5e0 100644 --- a/chrome/browser/chromeos/login/user_manager_unittest.cc +++ b/chrome/browser/chromeos/login/user_manager_unittest.cc @@ -60,8 +60,9 @@ class UserManagerTest : public testing::Test { reinterpret_cast<TestingBrowserProcess*>(g_browser_process) ->SetLocalState(local_state_.get()); UserManager::RegisterPrefs(local_state_.get()); - // Wallpaper manager pref is also used by the unit test when new wallpaper - // picker ui is enabled. + // Wallpaper manager and user image managers prefs will be accessed by the + // unit-test as well. + UserImageManager::RegisterPrefs(local_state_.get()); WallpaperManager::RegisterPrefs(local_state_.get()); old_user_manager_ = UserManager::Get(); diff --git a/chrome/browser/chromeos/login/wallpaper_manager.cc b/chrome/browser/chromeos/login/wallpaper_manager.cc index 223a7a1..04f7c35 100644 --- a/chrome/browser/chromeos/login/wallpaper_manager.cc +++ b/chrome/browser/chromeos/login/wallpaper_manager.cc @@ -5,6 +5,7 @@ #include "chrome/browser/chromeos/login/wallpaper_manager.h" #include <vector> + #include "ash/desktop_background/desktop_background_controller.h" #include "ash/desktop_background/desktop_background_resources.h" #include "ash/shell.h" @@ -47,6 +48,11 @@ namespace { const int kWallpaperUpdateIntervalSec = 24 * 60 * 60; +// A dictionary pref that maps usernames to file paths to their wallpapers. +// Deprecated. Will remove this const char after done migration. +const char kUserWallpapers[] = "UserWallpapers"; + +// Names of nodes with info about wallpaper in |kUserWallpapers| dictionary. const char kWallpaperTypeNodeName[] = "type"; const char kWallpaperIndexNodeName[] = "index"; const char kWallpaperDateNodeName[] = "date"; @@ -66,7 +72,11 @@ const int kDefaultOOBEWallpaperIndex = 20; // IDR_AURA_WALLPAPERS_2_LANDSCAPE8 const int kDefaultOOBEWallpaperIndex = 0; // IDR_AURA_WALLPAPERS_ROMAINGUY_0 #endif -// Names of nodes with info about wallpaper. +// A dictionary pref that maps usernames to wallpaper properties. +const char kUserWallpapersProperties[] = "UserWallpapersProperties"; + +// Names of nodes with info about wallpaper in |kUserWallpapersProperties| +// dictionary. const char kNewWallpaperDateNodeName[] = "date"; const char kNewWallpaperLayoutNodeName[] = "layout"; const char kNewWallpaperFileNodeName[] = "file"; @@ -132,6 +142,10 @@ WallpaperManager::WallpaperManager() void WallpaperManager::RegisterPrefs(PrefService* local_state) { local_state->RegisterDictionaryPref(prefs::kUsersWallpaperInfo, PrefService::UNSYNCABLE_PREF); + local_state->RegisterDictionaryPref(kUserWallpapers, + PrefService::UNSYNCABLE_PREF); + local_state->RegisterDictionaryPref(kUserWallpapersProperties, + PrefService::UNSYNCABLE_PREF); } void WallpaperManager::AddObservers() { @@ -161,7 +175,7 @@ void WallpaperManager::EnsureLoggedInUserWallpaperLoaded() { return; } } - SetUserWallpaper(UserManager::Get()->GetLoggedInUser().email()); + SetUserWallpaper(UserManager::Get()->GetLoggedInUser()->email()); } void WallpaperManager::GetCustomWallpaper(const std::string& email, @@ -240,7 +254,7 @@ void WallpaperManager::GetLoggedInUserWallpaperProperties( return; } - GetUserWallpaperProperties(UserManager::Get()->GetLoggedInUser().email(), + GetUserWallpaperProperties(UserManager::Get()->GetLoggedInUser()->email(), type, index, last_modification_date); } @@ -254,7 +268,7 @@ bool WallpaperManager::GetLoggedInUserWallpaperInfo(WallpaperInfo* info) { return true; } - return GetUserWallpaperInfo(UserManager::Get()->GetLoggedInUser().email(), + return GetUserWallpaperInfo(UserManager::Get()->GetLoggedInUser()->email(), info); } @@ -309,7 +323,7 @@ void WallpaperManager::InitializeWallpaper() { } return; } - SetUserWallpaper(user_manager->GetLoggedInUser().email()); + SetUserWallpaper(user_manager->GetLoggedInUser()->email()); } void WallpaperManager::Observe(int type, @@ -355,7 +369,7 @@ void WallpaperManager::Observe(int type, void WallpaperManager::RemoveUserWallpaperInfo(const std::string& email) { PrefService* prefs = g_browser_process->local_state(); DictionaryPrefUpdate prefs_wallpapers_update(prefs, - UserManager::kUserWallpapersProperties); + kUserWallpapersProperties); prefs_wallpapers_update->RemoveWithoutPathExpansion(email, NULL); bool new_wallpaper_ui_disabled = CommandLine::ForCurrentProcess()-> @@ -412,7 +426,7 @@ void WallpaperManager::ResizeAndSaveWallpaper(const UserImage& wallpaper, gfx::Size(resized_width, resized_height)); scoped_refptr<base::RefCountedBytes> data = new base::RefCountedBytes(); - // Uses simple jpeg encoder to encode image on worker pool. So we do not block + // Uses simple JPG encoder to encode image on worker pool so we do not block // chrome shutdown on image encoding. SimpleJpegEncoder* jpeg_encoder = new SimpleJpegEncoder( data, *(resized_image.bitmap())); @@ -524,8 +538,7 @@ void WallpaperManager::SetUserWallpaperProperties(const std::string& email, return; PrefService* local_state = g_browser_process->local_state(); - DictionaryPrefUpdate wallpaper_update(local_state, - UserManager::kUserWallpapersProperties); + DictionaryPrefUpdate wallpaper_update(local_state, kUserWallpapersProperties); base::DictionaryValue* wallpaper_properties = new base::DictionaryValue(); wallpaper_properties->Set(kWallpaperTypeNodeName, @@ -711,7 +724,7 @@ void WallpaperManager::BatchUpdateWallpaper() { // TODO(bshe): Notify lock screen, wallpaper picker UI to update wallpaper // as well. if (user_manager->IsUserLoggedIn() && - email == user_manager->GetLoggedInUser().email()) { + email == user_manager->GetLoggedInUser()->email()) { SetUserWallpaper(email); } else if (show_users && email == last_selected_user_) { @@ -799,18 +812,15 @@ void WallpaperManager::DeleteWallpaperInList( void WallpaperManager::DeleteUserWallpapers(const std::string& email) { std::vector<FilePath> file_to_remove; // Remove small user wallpaper. - FilePath wallpaper_path = WallpaperManager::Get()-> - GetWallpaperPathForUser(email, true); + FilePath wallpaper_path = GetWallpaperPathForUser(email, true); file_to_remove.push_back(wallpaper_path); // Remove large user wallpaper. - wallpaper_path = WallpaperManager::Get()-> - GetWallpaperPathForUser(email, false); + wallpaper_path = GetWallpaperPathForUser(email, false); file_to_remove.push_back(wallpaper_path); // Remove original user wallpaper. - wallpaper_path = WallpaperManager::Get()-> - GetOriginalWallpaperPathForUser(email); + wallpaper_path = GetOriginalWallpaperPathForUser(email); file_to_remove.push_back(wallpaper_path); base::WorkerPool::PostTask( @@ -907,7 +917,7 @@ void WallpaperManager::GetUserWallpaperProperties(const std::string& email, // Override with values found in local store, if any. if (!email.empty()) { const DictionaryValue* user_wallpapers = g_browser_process->local_state()-> - GetDictionary(UserManager::kUserWallpapersProperties); + GetDictionary(kUserWallpapersProperties); const base::DictionaryValue* wallpaper_properties; if (user_wallpapers->GetDictionaryWithoutPathExpansion( email, @@ -985,12 +995,52 @@ void WallpaperManager::GetCustomWallpaperInternal(const std::string& email, base::Unretained(this), email, new_info, update_wallpaper)); } +void WallpaperManager::MigrateWallpaperData(const UserList& users) { + PrefService* local_state = g_browser_process->local_state(); + if (local_state) { + const DictionaryValue* user_wallpapers = + local_state->GetDictionary(kUserWallpapers); + int index; + const DictionaryValue* new_user_wallpapers = + local_state->GetDictionary(kUserWallpapersProperties); + if (new_user_wallpapers->empty()) { + for (UserList::const_iterator it = users.begin(); + it != users.end(); + ++it) { + std::string username = (*it)->email(); + if (user_wallpapers->GetIntegerWithoutPathExpansion( + username, &index)) { + DictionaryPrefUpdate prefs_wallpapers_update( + local_state, kUserWallpapers); + prefs_wallpapers_update->RemoveWithoutPathExpansion(username, NULL); + SetUserWallpaperProperties(username, User::DEFAULT, index, true); + } else { + // Before M20, wallpaper index is not saved into LocalState unless + // user specifically sets a wallpaper. After M20, the default + // wallpaper index is saved to LocalState as soon as a new user login. + // When migrating wallpaper index from M20 to M21, we only migrate + // data that is in LocalState. This cause a problem when users login + // on a M20 device and then update the device to M21. The default + // wallpaper index failed to migrate because it was not saved into + // LocalState. Then we assume that all users have index saved in + // LocalState in M21. This is not true and it results an empty + // wallpaper for those users as described in cr/130685. So here we use + // default wallpaper for users that exist in user list but does not + // have an index saved in LocalState. + SetUserWallpaperProperties( + username, User::DEFAULT, ash::GetDefaultWallpaperIndex(), true); + } + } + } + } +} + void WallpaperManager::MigrateBuiltInWallpaper(const std::string& email) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (!email.empty() && UserManager::Get()->IsKnownUser(email)) { PrefService* prefs = g_browser_process->local_state(); const DictionaryValue* user_wallpapers = prefs-> - GetDictionary(UserManager::kUserWallpapersProperties); + GetDictionary(kUserWallpapersProperties); const base::DictionaryValue* wallpaper_properties; User::WallpaperType type; int index; @@ -1167,7 +1217,7 @@ bool WallpaperManager::ShouldPersistDataForUser(const std::string& email) { // case. if (!user_manager->IsUserLoggedIn()) return true; - return !(email == user_manager->GetLoggedInUser().email() && + return !(email == user_manager->GetLoggedInUser()->email() && user_manager->IsCurrentUserEphemeral()); } diff --git a/chrome/browser/chromeos/login/wallpaper_manager.h b/chrome/browser/chromeos/login/wallpaper_manager.h index ae2b90d..378fdaa 100644 --- a/chrome/browser/chromeos/login/wallpaper_manager.h +++ b/chrome/browser/chromeos/login/wallpaper_manager.h @@ -62,6 +62,13 @@ class WallpaperManager: public system::TimezoneSettings::Observer, // added after PowerManagerClient initialized. void AddObservers(); + // Migrate the old wallpaper index to a new wallpaper structure for all users + // in |users|. + // The new wallpaper structure is: + // { WallpaperType: DAILY|CUSTOMIZED|DEFAULT, + // index: index of the default wallpapers } + void MigrateWallpaperData(const UserList& users); + // Loads wallpaper asynchronously if the current wallpaper is not the // wallpaper of logged in user. void EnsureLoggedInUserWallpaperLoaded(); diff --git a/chrome/browser/chromeos/login/webui_screen_locker.cc b/chrome/browser/chromeos/login/webui_screen_locker.cc index 0dd129f..7c03cf5 100644 --- a/chrome/browser/chromeos/login/webui_screen_locker.cc +++ b/chrome/browser/chromeos/login/webui_screen_locker.cc @@ -60,7 +60,7 @@ void WebUIScreenLocker::LockScreen(bool unlock_on_input) { lock_window->Grab(); // User list consisting of a single logged-in user. - UserList users(1, &chromeos::UserManager::Get()->GetLoggedInUser()); + UserList users(1, chromeos::UserManager::Get()->GetLoggedInUser()); login_display_.reset(new WebUILoginDisplay(this)); login_display_->set_background_bounds(bounds); login_display_->set_parent_window(GetNativeWindow()); diff --git a/chrome/browser/chromeos/power/power_button_observer.cc b/chrome/browser/chromeos/power/power_button_observer.cc index 173fbfc..9f00bd6 100644 --- a/chrome/browser/chromeos/power/power_button_observer.cc +++ b/chrome/browser/chromeos/power/power_button_observer.cc @@ -25,7 +25,7 @@ ash::user::LoginStatus GetCurrentLoginStatus() { if (!user_manager->IsUserLoggedIn()) return ash::user::LOGGED_IN_NONE; - if (user_manager->GetLoggedInUser().is_guest()) + if (user_manager->GetLoggedInUser()->is_guest()) return ash::user::LOGGED_IN_GUEST; return ash::user::LOGGED_IN_USER; diff --git a/chrome/browser/chromeos/system/ash_system_tray_delegate.cc b/chrome/browser/chromeos/system/ash_system_tray_delegate.cc index f8fed06..287ccca 100644 --- a/chrome/browser/chromeos/system/ash_system_tray_delegate.cc +++ b/chrome/browser/chromeos/system/ash_system_tray_delegate.cc @@ -252,15 +252,15 @@ class SystemTrayDelegate : public ash::SystemTrayDelegate, } virtual const string16 GetUserDisplayName() const OVERRIDE { - return UserManager::Get()->GetLoggedInUser().GetDisplayName(); + return UserManager::Get()->GetLoggedInUser()->GetDisplayName(); } virtual const std::string GetUserEmail() const OVERRIDE { - return UserManager::Get()->GetLoggedInUser().display_email(); + return UserManager::Get()->GetLoggedInUser()->display_email(); } virtual const gfx::ImageSkia& GetUserImage() const OVERRIDE { - return UserManager::Get()->GetLoggedInUser().image(); + return UserManager::Get()->GetLoggedInUser()->image(); } virtual ash::user::LoginStatus GetUserLoginStatus() const OVERRIDE { diff --git a/chrome/browser/chromeos/system/drm_settings.cc b/chrome/browser/chromeos/system/drm_settings.cc index adb7330..1b83806 100644 --- a/chrome/browser/chromeos/system/drm_settings.cc +++ b/chrome/browser/chromeos/system/drm_settings.cc @@ -111,10 +111,10 @@ void ToggleDrm(bool enable) { // The user email address is included in the hash to keep the identifier // from being the same across users. - std::string email = user_manager->GetLoggedInUser().email(); + std::string email = user_manager->GetLoggedInUser()->email(); // If there is no real user then there's nothing to do here. - if (email.length() == 0) { - LOG(WARNING) << "Unexpected 0 length user email."; + if (email.empty()) { + LOG(WARNING) << "Unexpected empty user email."; return; } diff --git a/chrome/browser/policy/browser_policy_connector.cc b/chrome/browser/policy/browser_policy_connector.cc index ca8b4d0..445ca3a 100644 --- a/chrome/browser/policy/browser_policy_connector.cc +++ b/chrome/browser/policy/browser_policy_connector.cc @@ -176,7 +176,7 @@ scoped_ptr<UserCloudPolicyManager> // Don't wait for a policy fetch if there's no logged in user. if (chromeos::UserManager::Get()->IsUserLoggedIn()) { std::string email = - chromeos::UserManager::Get()->GetLoggedInUser().email(); + chromeos::UserManager::Get()->GetLoggedInUser()->email(); wait_for_policy_fetch = GetUserAffiliation(email) == USER_AFFILIATION_MANAGED; } diff --git a/chrome/browser/policy/user_cloud_policy_store_chromeos.cc b/chrome/browser/policy/user_cloud_policy_store_chromeos.cc index f92b7da..e91259b 100644 --- a/chrome/browser/policy/user_cloud_policy_store_chromeos.cc +++ b/chrome/browser/policy/user_cloud_policy_store_chromeos.cc @@ -286,7 +286,7 @@ void UserCloudPolicyStoreChromeOS::Validate( scoped_ptr<UserCloudPolicyValidator> validator = CreateValidator(policy.Pass(), callback); validator->ValidateUsername( - chromeos::UserManager::Get()->GetLoggedInUser().email()); + chromeos::UserManager::Get()->GetLoggedInUser()->email()); // TODO(mnissler): Do a signature check here as well. The key is stored by // session_manager in the root-owned cryptohome area, which is currently diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc index 5b4153b..e1121b453 100644 --- a/chrome/browser/prefs/browser_prefs.cc +++ b/chrome/browser/prefs/browser_prefs.cc @@ -97,6 +97,7 @@ #include "chrome/browser/chromeos/audio/audio_handler.h" #include "chrome/browser/chromeos/customization_document.h" #include "chrome/browser/chromeos/display/display_preferences.h" +#include "chrome/browser/chromeos/login/user_image_manager.h" #include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/chromeos/login/wallpaper_manager.h" #include "chrome/browser/chromeos/login/wizard_controller.h" @@ -198,6 +199,7 @@ void RegisterLocalState(PrefService* local_state) { chromeos::ProxyConfigServiceImpl::RegisterPrefs(local_state); chromeos::RegisterDisplayLocalStatePrefs(local_state); chromeos::ServicesCustomizationDocument::RegisterPrefs(local_state); + chromeos::UserImageManager::RegisterPrefs(local_state); chromeos::UserManager::RegisterPrefs(local_state); chromeos::WallpaperManager::RegisterPrefs(local_state); chromeos::WizardController::RegisterPrefs(local_state); diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc index 8b1f198..6c71632 100644 --- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc +++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc @@ -728,8 +728,7 @@ void SigninScreenHandler::SendUserList(bool animated) { std::string owner; chromeos::CrosSettings::Get()->GetString(chromeos::kDeviceOwner, &owner); bool is_owner = (email == owner); - bool signed_in = UserManager::Get()->IsUserLoggedIn() && - email == UserManager::Get()->GetLoggedInUser().email(); + bool signed_in = *it == UserManager::Get()->GetLoggedInUser(); if (non_owner_count < max_non_owner_users || is_owner) { DictionaryValue* user_dict = new DictionaryValue(); diff --git a/chrome/browser/ui/webui/feedback_ui.cc b/chrome/browser/ui/webui/feedback_ui.cc index ff84057..7b1ec4b 100644 --- a/chrome/browser/ui/webui/feedback_ui.cc +++ b/chrome/browser/ui/webui/feedback_ui.cc @@ -102,7 +102,7 @@ std::string GetUserEmail() { if (!manager) return std::string(); else - return manager->GetLoggedInUser().display_email(); + return manager->GetLoggedInUser()->display_email(); } bool ScreenshotDriveTimestampComp(const drive::DriveEntryProto& entry1, diff --git a/chrome/browser/ui/webui/help/help_handler.cc b/chrome/browser/ui/webui/help/help_handler.cc index 374f2ee..0d24f59 100644 --- a/chrome/browser/ui/webui/help/help_handler.cc +++ b/chrome/browser/ui/webui/help/help_handler.cc @@ -87,7 +87,7 @@ bool CanChangeReleaseChannel() { return false; // Get the currently logged in user and strip the domain part only. std::string domain = ""; - std::string user = chromeos::UserManager::Get()->GetLoggedInUser().email(); + std::string user = chromeos::UserManager::Get()->GetLoggedInUser()->email(); size_t at_pos = user.find('@'); if (at_pos != std::string::npos && at_pos + 1 < user.length()) domain = user.substr(user.find('@') + 1); diff --git a/chrome/browser/ui/webui/options/browser_options_handler.cc b/chrome/browser/ui/webui/options/browser_options_handler.cc index 67e5116..e1ceea1 100644 --- a/chrome/browser/ui/webui/options/browser_options_handler.cc +++ b/chrome/browser/ui/webui/options/browser_options_handler.cc @@ -378,22 +378,20 @@ void BrowserOptionsHandler::GetLocalizedValues(DictionaryValue* values) { l10n_util::GetStringUTF16(IDS_PRODUCT_NAME))); #if defined(OS_CHROMEOS) - values->SetString("username", - chromeos::UserManager::Get()->IsUserLoggedIn() ? - chromeos::UserManager::Get()->GetLoggedInUser().email() : - std::string()); - - values->SetString( - "factoryResetWarning", - l10n_util::GetStringFUTF16( - IDS_OPTIONS_FACTORY_RESET_WARNING, - l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME))); - - values->SetString( - "factoryResetDescription", - l10n_util::GetStringFUTF16( - IDS_OPTIONS_FACTORY_RESET_DESCRIPTION, - l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME))); + const chromeos::User* user = chromeos::UserManager::Get()->GetLoggedInUser(); + values->SetString("username", user ? user->email() : std::string()); + + values->SetString( + "factoryResetWarning", + l10n_util::GetStringFUTF16( + IDS_OPTIONS_FACTORY_RESET_WARNING, + l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME))); + + values->SetString( + "factoryResetDescription", + l10n_util::GetStringFUTF16( + IDS_OPTIONS_FACTORY_RESET_DESCRIPTION, + l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME))); #endif // Pass along sync status early so it will be available during page init. @@ -1022,7 +1020,7 @@ void BrowserOptionsHandler::ThemesSetGTK(const ListValue* args) { #if defined(OS_CHROMEOS) void BrowserOptionsHandler::UpdateAccountPicture() { - std::string email = chromeos::UserManager::Get()->GetLoggedInUser().email(); + std::string email = chromeos::UserManager::Get()->GetLoggedInUser()->email(); if (!email.empty()) { web_ui()->CallJavascriptFunction("BrowserOptions.updateAccountPicture"); base::StringValue email_value(email); diff --git a/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.cc b/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.cc index 357d60f..06ebe11 100644 --- a/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.cc +++ b/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.cc @@ -15,6 +15,7 @@ #include "chrome/browser/chromeos/login/camera_detector.h" #include "chrome/browser/chromeos/login/default_user_images.h" #include "chrome/browser/chromeos/login/user_image.h" +#include "chrome/browser/chromeos/login/user_image_manager.h" #include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/chromeos/options/take_photo_dialog.h" #include "chrome/browser/ui/browser_finder.h" @@ -239,21 +240,21 @@ void ChangePictureOptionsHandler::HandlePageShown(const base::ListValue* args) { } void ChangePictureOptionsHandler::SendSelectedImage() { - const User& user = UserManager::Get()->GetLoggedInUser(); - DCHECK(!user.email().empty()); + const User* user = UserManager::Get()->GetLoggedInUser(); + DCHECK(!user->email().empty()); - previous_image_index_ = user.image_index(); + previous_image_index_ = user->image_index(); switch (previous_image_index_) { case User::kExternalImageIndex: { // User has image from camera/file, record it and add to the image list. - previous_image_ = user.image(); + previous_image_ = user->image(); previous_image_data_url_ = web_ui_util::GetImageDataUrl(previous_image_); web_ui()->CallJavascriptFunction("ChangePictureOptions.setOldImage"); break; } case User::kProfileImageIndex: { // User has his/her Profile image as the current image. - SendProfileImage(user.image(), true); + SendProfileImage(user->image(), true); break; } default: { @@ -282,15 +283,16 @@ void ChangePictureOptionsHandler::SendProfileImage(const gfx::ImageSkia& image, } void ChangePictureOptionsHandler::UpdateProfileImage() { - UserManager* user_manager = UserManager::Get(); + UserImageManager* user_image_manager = + UserManager::Get()->GetUserImageManager(); // If we have a downloaded profile image and haven't sent it in // |SendSelectedImage|, send it now (without selecting). if (previous_image_index_ != User::kProfileImageIndex && - !user_manager->DownloadedProfileImage().isNull()) - SendProfileImage(user_manager->DownloadedProfileImage(), false); + !user_image_manager->DownloadedProfileImage().isNull()) + SendProfileImage(user_image_manager->DownloadedProfileImage(), false); - user_manager->DownloadProfileImage(kProfileDownloadReason); + user_image_manager->DownloadProfileImage(kProfileDownloadReason); } void ChangePictureOptionsHandler::HandleSelectImage(const ListValue* args) { @@ -303,8 +305,9 @@ void ChangePictureOptionsHandler::HandleSelectImage(const ListValue* args) { } DCHECK(!image_url.empty()); - UserManager* user_manager = UserManager::Get(); - const User& user = user_manager->GetLoggedInUser(); + const User* user = UserManager::Get()->GetLoggedInUser(); + UserImageManager* user_image_manager = + UserManager::Get()->GetUserImageManager(); int image_index = User::kInvalidImageIndex; bool waiting_for_camera_photo = false; @@ -318,13 +321,13 @@ void ChangePictureOptionsHandler::HandleSelectImage(const ListValue* args) { if (previous_image_index_ == User::kExternalImageIndex) { DCHECK(!previous_image_.isNull()); - user_manager->SaveUserImage(user.email(), - UserImage::CreateAndEncode(previous_image_)); + user_image_manager->SaveUserImage( + user->email(), UserImage::CreateAndEncode(previous_image_)); } else { DCHECK(previous_image_index_ >= 0 && previous_image_index_ < kFirstDefaultImageIndex); - user_manager->SaveUserDefaultImageIndex(user.email(), - previous_image_index_); + user_image_manager->SaveUserDefaultImageIndex( + user->email(), previous_image_index_); } UMA_HISTOGRAM_ENUMERATION("UserImage.ChangeChoice", @@ -333,7 +336,7 @@ void ChangePictureOptionsHandler::HandleSelectImage(const ListValue* args) { VLOG(1) << "Selected old user image"; } else if (IsDefaultImageUrl(image_url, &image_index)) { // One of the default user images. - user_manager->SaveUserDefaultImageIndex(user.email(), image_index); + user_image_manager->SaveUserDefaultImageIndex(user->email(), image_index); UMA_HISTOGRAM_ENUMERATION("UserImage.ChangeChoice", GetDefaultImageHistogramValue(image_index), @@ -350,7 +353,7 @@ void ChangePictureOptionsHandler::HandleSelectImage(const ListValue* args) { } } else { // Profile image selected. Could be previous (old) user image. - user_manager->SaveUserImageFromProfileImage(user.email()); + user_image_manager->SaveUserImageFromProfileImage(user->email()); if (previous_image_index_ == User::kProfileImageIndex) { UMA_HISTOGRAM_ENUMERATION("UserImage.ChangeChoice", @@ -374,8 +377,8 @@ void ChangePictureOptionsHandler::FileSelected(const FilePath& path, int index, void* params) { UserManager* user_manager = UserManager::Get(); - user_manager->SaveUserImageFromFile(user_manager->GetLoggedInUser().email(), - path); + user_manager->GetUserImageManager()->SaveUserImageFromFile( + user_manager->GetLoggedInUser()->email(), path); UMA_HISTOGRAM_ENUMERATION("UserImage.ChangeChoice", kHistogramImageFromFile, kHistogramImagesCount); @@ -386,8 +389,9 @@ void ChangePictureOptionsHandler::OnPhotoAccepted(const gfx::ImageSkia& photo) { UserManager* user_manager = UserManager::Get(); // TODO(ivankr): once old camera UI is gone, there's always raw data in // |image_decoder_|, pass UserImage and user it instead. - user_manager->SaveUserImage(user_manager->GetLoggedInUser().email(), - UserImage::CreateAndEncode(photo)); + user_manager->GetUserImageManager()->SaveUserImage( + user_manager->GetLoggedInUser()->email(), + UserImage::CreateAndEncode(photo)); UMA_HISTOGRAM_ENUMERATION("UserImage.ChangeChoice", kHistogramImageFromCamera, kHistogramImagesCount); diff --git a/chrome/browser/ui/webui/options/chromeos/core_chromeos_options_handler.cc b/chrome/browser/ui/webui/options/chromeos/core_chromeos_options_handler.cc index b943376..6abee13 100644 --- a/chrome/browser/ui/webui/options/chromeos/core_chromeos_options_handler.cc +++ b/chrome/browser/ui/webui/options/chromeos/core_chromeos_options_handler.cc @@ -48,7 +48,7 @@ bool IsSettingOwnerOnly(const std::string& pref) { bool IsLoggedInOwner(const std::string& username) { UserManager* user_manager = UserManager::Get(); return user_manager->IsCurrentUserOwner() && - user_manager->GetLoggedInUser().email() == username; + user_manager->GetLoggedInUser()->email() == username; } // Creates a user info dictionary to be stored in the |ListValue| that is diff --git a/chrome/browser/ui/webui/options/chromeos/set_wallpaper_options_handler.cc b/chrome/browser/ui/webui/options/chromeos/set_wallpaper_options_handler.cc index 4d76f8f..64ed143 100644 --- a/chrome/browser/ui/webui/options/chromeos/set_wallpaper_options_handler.cc +++ b/chrome/browser/ui/webui/options/chromeos/set_wallpaper_options_handler.cc @@ -106,7 +106,7 @@ void SetWallpaperOptionsHandler::FileSelected(const FilePath& path, // Default wallpaper layout is CENTER_CROPPED. WallpaperManager::Get()->SetUserWallpaperFromFile( - user_manager->GetLoggedInUser().email(), path, ash::CENTER_CROPPED, + user_manager->GetLoggedInUser()->email(), path, ash::CENTER_CROPPED, weak_factory_.GetWeakPtr()); web_ui()->CallJavascriptFunction("SetWallpaperOptions.didSelectFile"); } diff --git a/chrome/browser/ui/webui/policy_ui.cc b/chrome/browser/ui/webui/policy_ui.cc index 9eebb59..9f96b37 100644 --- a/chrome/browser/ui/webui/policy_ui.cc +++ b/chrome/browser/ui/webui/policy_ui.cc @@ -322,9 +322,9 @@ base::DictionaryValue* PolicyUIHandler::GetStatusData() { results->SetString("userLastFetchTime", GetLastFetchTime(user_subsystem)); #if defined(OS_CHROMEOS) - const chromeos::User& user = + const chromeos::User* user = chromeos::UserManager::Get()->GetLoggedInUser(); - results->SetString("user", ASCIIToUTF16(user.email())); + results->SetString("user", ASCIIToUTF16(user->email())); #else results->SetString("user", string16()); #endif diff --git a/chrome/browser/ui/webui/web_ui_util.cc b/chrome/browser/ui/webui/web_ui_util.cc index 08614d3..2d43279 100644 --- a/chrome/browser/ui/webui/web_ui_util.cc +++ b/chrome/browser/ui/webui/web_ui_util.cc @@ -17,6 +17,8 @@ #include "ui/gfx/codec/png_codec.h" #include "ui/gfx/image/image_skia.h" +#include "base/debug/trace_event.h" + namespace { struct ScaleFactorMap { @@ -34,6 +36,8 @@ const ScaleFactorMap kScaleFactorMap[] = { namespace web_ui_util { std::string GetImageDataUrl(const gfx::ImageSkia& image) { + TRACE_EVENT2("oobe", "GetImageDataUrl", + "width", image.width(), "height", image.height()); std::vector<unsigned char> output; gfx::PNGCodec::EncodeBGRASkBitmap(*image.bitmap(), false, &output); std::string str_url; diff --git a/chrome/chrome_browser_chromeos.gypi b/chrome/chrome_browser_chromeos.gypi index 6a76868..ec40bef 100644 --- a/chrome/chrome_browser_chromeos.gypi +++ b/chrome/chrome_browser_chromeos.gypi @@ -460,6 +460,10 @@ 'browser/chromeos/login/user_image.h', 'browser/chromeos/login/user_image_loader.cc', 'browser/chromeos/login/user_image_loader.h', + 'browser/chromeos/login/user_image_manager.h', + 'browser/chromeos/login/user_image_manager.cc', + 'browser/chromeos/login/user_image_manager_impl.cc', + 'browser/chromeos/login/user_image_manager_impl.h', 'browser/chromeos/login/user_image_screen.cc', 'browser/chromeos/login/user_image_screen.h', 'browser/chromeos/login/user_image_screen_actor.h', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 59bb895..5f75c2f 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -109,6 +109,8 @@ 'browser/chromeos/login/mock_login_utils.h', 'browser/chromeos/login/mock_url_fetchers.cc', 'browser/chromeos/login/mock_url_fetchers.h', + 'browser/chromeos/login/mock_user_image_manager.cc', + 'browser/chromeos/login/mock_user_image_manager.h', 'browser/chromeos/login/mock_user_manager.cc', 'browser/chromeos/login/mock_user_manager.h', 'browser/chromeos/settings/device_settings_test_helper.cc', @@ -2827,6 +2829,7 @@ 'browser/chromeos/login/screen_locker_tester.cc', 'browser/chromeos/login/screen_locker_tester.h', 'browser/chromeos/login/update_screen_browsertest.cc', + 'browser/chromeos/login/user_image_manager_browsertest.cc', 'browser/chromeos/login/wizard_controller_browsertest.cc', 'browser/chromeos/login/wizard_in_process_browser_test.cc', 'browser/chromeos/login/wizard_in_process_browser_test.h', |