Index: chrome/browser/chromeos/login/user_image_manager_impl.cc |
diff --git a/chrome/browser/chromeos/login/user_image_manager_impl.cc b/chrome/browser/chromeos/login/user_image_manager_impl.cc |
index 06cc80c05423f499f5ed923033cff6168b79bd4e..36a26437877dbba153382b4fea3eda59f9b0bc90 100644 |
--- a/chrome/browser/chromeos/login/user_image_manager_impl.cc |
+++ b/chrome/browser/chromeos/login/user_image_manager_impl.cc |
@@ -17,10 +17,9 @@ |
#include "base/prefs/scoped_user_pref_update.h" |
#include "base/rand_util.h" |
#include "base/sequenced_task_runner.h" |
+#include "base/task_runner_util.h" |
#include "base/threading/sequenced_worker_pool.h" |
-#include "base/threading/worker_pool.h" |
#include "base/time/time.h" |
-#include "base/values.h" |
#include "chrome/browser/browser_process.h" |
#include "chrome/browser/chrome_notification_types.h" |
#include "chrome/browser/chromeos/login/default_user_images.h" |
@@ -35,22 +34,19 @@ |
#include "chromeos/chromeos_switches.h" |
#include "content/public/browser/browser_thread.h" |
#include "content/public/browser/notification_service.h" |
-#include "content/public/common/url_constants.h" |
#include "ui/base/webui/web_ui_util.h" |
#include "ui/gfx/image/image_skia.h" |
-using content::BrowserThread; |
- |
namespace chromeos { |
namespace { |
-// A dictionary that maps usernames to old user image data with images stored in |
+// A dictionary that maps user_ids 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 |
+// A dictionary that maps user_ids to user image data with images stored in |
// JPEG format. |
const char kUserImageProperties[] = "user_image_info"; |
@@ -59,9 +55,6 @@ const char kImagePathNodeName[] = "path"; |
const char kImageIndexNodeName[] = "index"; |
const char kImageURLNodeName[] = "url"; |
-// Delay betweeen user login and user image migration. |
-const int kUserImageMigrationDelaySec = 50; |
bartfab (slow)
2013/11/13 23:12:09
I removed this because it was not working correctl
|
- |
// Delay betweeen user login and attempt to update user's profile data. |
const int kProfileDataDownloadDelaySec = 10; |
@@ -104,7 +97,7 @@ const char kProfileDownloadReasonScheduled[] = "Scheduled"; |
// Time histogram suffix for a profile image download retry. |
const char kProfileDownloadReasonRetry[] = "Retry"; |
-// Add a histogram showing the time it takes to download a profile image. |
+// Add a histogram showing the time it takes to download profile image. |
// Separate histograms are reported for each download |reason| and |result|. |
void AddProfileImageTimeHistogram(ProfileDownloadResult result, |
const std::string& download_reason, |
@@ -143,18 +136,6 @@ void AddProfileImageTimeHistogram(ProfileDownloadResult result, |
DVLOG(1) << "Profile image download time: " << time_delta.InSecondsF(); |
} |
-// Deletes image file. |
-void DeleteImageFile(const std::string& image_path) { |
- if (image_path.empty()) |
- return; |
- base::FilePath fp(image_path); |
- BrowserThread::PostTask( |
- BrowserThread::FILE, |
- FROM_HERE, |
- base::Bind(base::IgnoreResult(&base::DeleteFile), |
- fp, /* recursive= */ false)); |
-} |
- |
// Converts |image_index| to UMA histogram value. |
int ImageIndexToHistogramIndex(int image_index) { |
switch (image_index) { |
@@ -168,11 +149,31 @@ int ImageIndexToHistogramIndex(int image_index) { |
} |
} |
-} // namespace |
+bool SaveImage(const UserImage& user_image, const base::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; |
+ } |
-// static |
-int UserImageManagerImpl::user_image_migration_delay_sec = |
- kUserImageMigrationDelaySec; |
+ 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; |
+} |
+ |
+} // namespace |
// static |
void UserImageManager::RegisterPrefs(PrefRegistrySimple* registry) { |
@@ -180,22 +181,259 @@ void UserImageManager::RegisterPrefs(PrefRegistrySimple* registry) { |
registry->RegisterDictionaryPref(kUserImageProperties); |
} |
+// Every image load or update is encapsulated by a Job. The Job is allowed to |
+// perform tasks on background threads or in helper processes but: |
+// * Changes to User objects and local state as well as any calls to the |
+// |parent_| must be performed on the thread that the Job is created on only. |
+// * File writes and deletions must be performed via the |parent_|'s |
+// |background_task_runner_| only. |
+// |
+// Only one of the Load*() and Set*() methods may be called per Job. |
+class UserImageManagerImpl::Job { |
+ public: |
+ // The |Job| will update the |user| object for |user_id|. |
+ Job(UserImageManagerImpl* parent, const std::string& user_id); |
+ ~Job(); |
+ |
+ // Loads the image at |image_path| or one of the default images, depending on |
+ // |image_index|, and updates the |user| object for |user_id_| with the new |
+ // image. |
+ void LoadImage(base::FilePath image_path, |
+ const int image_index, |
+ const GURL& image_url); |
+ |
+ // Sets the user image for |user_id_| in local state to the default image |
+ // indicated by |default_image_index|. Also updates the |user| object for |
+ // |user_id_| with the new image. |
+ void SetToDefaultImage(int default_image_index); |
+ |
+ // Saves the |user_image| to disk and sets the user image for |user_id_| in |
+ // local state to that image. Also updates the |user| object for |user_id_| |
+ // with the new image. |
+ void SetToImage(int image_index, |
+ const UserImage& user_image); |
+ |
+ // Loads the the image at |path|, transcodes it to JPEG format, saves the |
+ // image to disk and sets the user image for |user_id_| in local state to that |
+ // image. If |resize| is true, the image is cropped and resized before |
+ // transcoding. Also updates the |user| object for |user_id_| with the new |
+ // image. |
+ void SetToPath(const base::FilePath& path, |
+ int image_index, |
+ const GURL& image_url, |
+ bool resize); |
+ |
+ private: |
+ // Called back after an image has been loaded from disk. |
+ void OnLoadImageDone(bool save, const UserImage& user_image); |
+ |
+ // Updates the |user| object for |user_id_| with |user_image_|. |
+ void UpdateUser(); |
+ |
+ // Saves |user_image_| to disk in JPEG format. Local state will be updated |
+ // when a callback indicates that the save has been successful. |
+ void SaveImageAndUpdateLocalState(); |
+ |
+ // Called back after the |user_image_| has been saved to disk. If |success| is |
+ // true sets the user image for |user_id_| in local state to that image. |
+ void OnSaveImageDone(bool success); |
+ |
+ // Updates the user image for |user_id_| in local state, setting it to |
+ // one of the default images or the saved |user_image_|, depending on |
+ // |image_index_|. |
+ void UpdateLocalState(); |
+ |
+ // Notifies the |parent_| that the Job is done. |
+ void NotifyJobDone(); |
+ |
+ UserImageManagerImpl* parent_; |
+ const std::string user_id_; |
+ |
+ // Whether one of the Load*() or Set*() methods has been run already. |
+ bool run_; |
+ |
+ int image_index_; |
+ GURL image_url_; |
+ base::FilePath image_path_; |
+ |
+ UserImage user_image_; |
+ |
+ base::WeakPtrFactory<Job> weak_factory_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(Job); |
+}; |
+ |
+UserImageManagerImpl::Job::Job(UserImageManagerImpl* parent, |
+ const std::string& user_id) |
+ : parent_(parent), |
+ user_id_(user_id), |
+ run_(false), |
+ weak_factory_(this) { |
+} |
+ |
+UserImageManagerImpl::Job::~Job() { |
+} |
+ |
+void UserImageManagerImpl::Job::LoadImage(base::FilePath image_path, |
+ const int image_index, |
+ const GURL& image_url) { |
+ DCHECK(!run_); |
+ run_ = true; |
+ |
+ image_index_ = image_index; |
+ image_url_ = image_url; |
+ image_path_ = image_path; |
+ |
+ if (image_index_ >= 0 && image_index_ < kDefaultImagesCount) { |
+ // Load one of the default images. This happens synchronously. |
+ user_image_ = UserImage(GetDefaultImage(image_index_)); |
+ UpdateUser(); |
+ NotifyJobDone(); |
+ } else if (image_index_ == User::kExternalImageIndex || |
+ image_index_ == User::kProfileImageIndex) { |
+ // Load the user image from a file referenced by |image_path|. This happens |
+ // asynchronously. The JPEG image loader can be used here because |
+ // LoadImage() is called only for users whose user image has previously |
+ // been set by one of the Set*() methods, which transcode to JPEG format. |
+ parent_->image_loader_->Start(image_path_.value(), |
Nikita (slow)
2013/11/14 15:17:13
DCHECK that image_path is not empty.
bartfab (slow)
2013/11/14 15:55:10
There actually is a DCHECK for this in the code th
|
+ 0, |
+ base::Bind(&Job::OnLoadImageDone, |
+ weak_factory_.GetWeakPtr(), |
+ false)); |
+ } else { |
+ NOTREACHED(); |
+ NotifyJobDone(); |
+ } |
+} |
+ |
+void UserImageManagerImpl::Job::SetToDefaultImage(int default_image_index) { |
+ DCHECK(!run_); |
+ run_ = true; |
+ |
+ DCHECK_LE(0, default_image_index); |
+ DCHECK_GT(kDefaultImagesCount, default_image_index); |
+ |
+ image_index_ = default_image_index; |
+ user_image_ = UserImage(GetDefaultImage(image_index_)); |
+ |
+ UpdateUser(); |
+ UpdateLocalState(); |
+ NotifyJobDone(); |
+} |
+ |
+void UserImageManagerImpl::Job::SetToImage(int image_index, |
+ const UserImage& user_image) { |
+ DCHECK(!run_); |
+ run_ = true; |
+ |
+ DCHECK(image_index == User::kExternalImageIndex || |
+ image_index == User::kProfileImageIndex); |
+ |
+ image_index_ = image_index; |
+ user_image_ = user_image; |
+ |
+ UpdateUser(); |
+ SaveImageAndUpdateLocalState(); |
+} |
+ |
+void UserImageManagerImpl::Job::SetToPath(const base::FilePath& path, |
+ int image_index, |
+ const GURL& image_url, |
+ bool resize) { |
+ DCHECK(!run_); |
Nikita (slow)
2013/11/14 15:17:13
DCHECK path.
bartfab (slow)
2013/11/14 15:55:10
Done.
|
+ run_ = true; |
+ |
+ image_index_ = image_index; |
+ image_url_ = image_url; |
+ |
+ parent_->unsafe_image_loader_->Start(path.value(), |
+ resize ? login::kMaxUserImageSize : 0, |
+ base::Bind(&Job::OnLoadImageDone, |
+ weak_factory_.GetWeakPtr(), |
+ true)); |
+} |
+ |
+void UserImageManagerImpl::Job::OnLoadImageDone(bool save, |
+ const UserImage& user_image) { |
+ user_image_ = user_image; |
+ UpdateUser(); |
+ if (save) |
+ SaveImageAndUpdateLocalState(); |
+ else |
+ NotifyJobDone(); |
+} |
+ |
+void UserImageManagerImpl::Job::UpdateUser() { |
+ User* user = const_cast<User*>(UserManager::Get()->FindUser(user_id_)); |
Nikita (slow)
2013/11/14 15:17:13
How can const_cast be avoided?
Declare this class
bartfab (slow)
2013/11/14 15:55:10
I actually inherited this case from the original i
|
+ if (!user) |
+ return; |
+ |
+ if (!user_image_.image().isNull()) |
+ user->SetImage(user_image_, image_index_); |
+ else |
+ user->SetStubImage(image_index_, false); |
+ user->SetImageURL(image_url_); |
+ |
+ parent_->OnJobChangedUserImage(user); |
+} |
+ |
+void UserImageManagerImpl::Job::SaveImageAndUpdateLocalState() { |
+ base::FilePath user_data_dir; |
+ PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); |
+ image_path_ = user_data_dir.Append(user_id_ + kSafeImagePathExtension); |
+ |
+ base::PostTaskAndReplyWithResult( |
+ parent_->background_task_runner_, |
+ FROM_HERE, |
+ base::Bind(&SaveImage, user_image_, image_path_), |
+ base::Bind(&Job::OnSaveImageDone, weak_factory_.GetWeakPtr())); |
+} |
+ |
+void UserImageManagerImpl::Job::OnSaveImageDone(bool success) { |
+ if (success) |
+ UpdateLocalState(); |
+ NotifyJobDone(); |
+} |
+ |
+void UserImageManagerImpl::Job::UpdateLocalState() { |
+ // Ignore if data stored or cached outside the user's cryptohome is to be |
+ // treated as ephemeral. |
+ if (UserManager::Get()->IsUserNonCryptohomeDataEphemeral(user_id_)) |
+ return; |
+ |
+ scoped_ptr<base::DictionaryValue> entry(new base::DictionaryValue); |
+ entry->Set(kImagePathNodeName, new base::StringValue(image_path_.value())); |
+ entry->Set(kImageIndexNodeName, new base::FundamentalValue(image_index_)); |
+ if (!image_url_.is_empty()) |
+ entry->Set(kImageURLNodeName, new StringValue(image_url_.spec())); |
+ DictionaryPrefUpdate update(g_browser_process->local_state(), |
+ kUserImageProperties); |
+ update->SetWithoutPathExpansion(user_id_, entry.release()); |
+ |
+ UserManager::Get()->NotifyLocalStateChanged(); |
+} |
+ |
+void UserImageManagerImpl::Job::NotifyJobDone() { |
+ parent_->OnJobDone(user_id_); |
+ // |parent_| drops ownership of |this| in OnJobDone(), requiring the Job to |
+ // destroy itself. |
+ delete this; |
Nikita (slow)
2013/11/14 15:17:13
Any reasons why me might want to use DeleteSoon()
bartfab (slow)
2013/11/14 15:55:10
I was sure I had a good reason not to use DeleteSo
|
+} |
+ |
UserImageManagerImpl::UserImageManagerImpl() |
- : last_image_set_async_(false), |
- downloaded_profile_image_data_url_(content::kAboutBlankURL), |
- downloading_profile_image_(false), |
- migrate_current_user_on_load_(false) { |
- base::SequencedWorkerPool* blocking_pool = BrowserThread::GetBlockingPool(); |
- // Background task runner on which file I/O, image decoding and resizing are |
- // done. |
- scoped_refptr<base::SequencedTaskRunner> task_runner = |
+ : downloading_profile_image_(false), |
+ profile_image_requested_(false), |
+ weak_factory_(this) { |
+ base::SequencedWorkerPool* blocking_pool = |
+ content::BrowserThread::GetBlockingPool(); |
+ background_task_runner_ = |
blocking_pool->GetSequencedTaskRunnerWithShutdownBehavior( |
blocking_pool->GetSequenceToken(), |
base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); |
image_loader_ = new UserImageLoader(ImageDecoder::ROBUST_JPEG_CODEC, |
- task_runner); |
+ background_task_runner_); |
unsafe_image_loader_ = new UserImageLoader(ImageDecoder::DEFAULT_CODEC, |
- task_runner); |
+ background_task_runner_); |
} |
UserImageManagerImpl::~UserImageManagerImpl() { |
@@ -212,215 +450,186 @@ void UserImageManagerImpl::LoadUserImages(const UserList& users) { |
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. |
+ const std::string& user_id = user->email(); |
+ bool needs_migration = false; |
+ // If entries are found in both |prefs_images_unsafe| and |prefs_images|, |
+ // |prefs_images| is honored for now but |prefs_images_unsafe| will be |
+ // migrated, overwriting the |prefs_images| entry, when the user logs in. |
+ const base::DictionaryValue* image_properties = NULL; |
if (prefs_images_unsafe) { |
needs_migration = prefs_images_unsafe->GetDictionaryWithoutPathExpansion( |
- user->email(), &image_properties); |
+ user_id, &image_properties); |
+ if (needs_migration) |
+ users_to_migrate_.insert(user_id); |
} |
if (prefs_images) { |
- safe_source = prefs_images->GetDictionaryWithoutPathExpansion( |
- user->email(), &image_properties); |
+ prefs_images->GetDictionaryWithoutPathExpansion(user_id, |
Nikita (slow)
2013/11/14 15:17:13
Can you please describe how migration implementati
bartfab (slow)
2013/11/14 15:55:10
Added to CL description.
|
+ &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()) { |
- if (needs_migration) { |
- // Non-JPG image will be migrated once user logs in. |
- // Stub image will be used for now. Continue with other users. |
- continue; |
- } |
- DCHECK(safe_source); |
- if (!safe_source) |
- continue; |
- |
- // Load user image asynchronously - at this point we are able to use |
- // JPEG image loaded since image comes from safe pref source |
- // i.e. converted to JPEG. |
- image_loader_->Start( |
- image_path, 0 /* no resize */, |
- base::Bind(&UserImageManagerImpl::SetUserImage, |
- base::Unretained(this), |
- user->email(), image_index, image_gurl)); |
- } |
- } else { |
- NOTREACHED(); |
- } |
+ SetInitialUserImage(user_id); |
+ continue; |
} |
+ |
+ 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); |
+ continue; |
+ } |
+ |
+ if (image_index != User::kExternalImageIndex && |
+ image_index != User::kProfileImageIndex) { |
+ NOTREACHED(); |
+ continue; |
+ } |
+ |
+ std::string image_url_string; |
+ image_properties->GetString(kImageURLNodeName, &image_url_string); |
+ GURL image_url(image_url_string); |
+ std::string image_path; |
+ image_properties->GetString(kImagePathNodeName, &image_path); |
+ |
+ user->SetImageURL(image_url); |
+ DCHECK(!image_path.empty() || image_index == User::kProfileImageIndex); |
+ if (image_path.empty() || needs_migration) { |
+ // Use a stub image (gray avatar) if either of the following is true: |
+ // * The profile image is to be used but has not been downloaded yet. The |
+ // profile image will be downloaded after login. |
+ // * The image needs migration. Migration will be performed after login. |
+ user->SetStubImage(image_index, true); |
+ continue; |
+ } |
+ |
+ linked_ptr<Job>& job = jobs_[user_id]; |
+ job.reset(new Job(this, user_id)); |
+ job->LoadImage(base::FilePath(image_path), image_index, image_url); |
} |
} |
-void UserImageManagerImpl::UserLoggedIn(const std::string& email, |
+void UserImageManagerImpl::UserLoggedIn(const std::string& user_id, |
bool user_is_new, |
bool user_is_local) { |
User* user = UserManager::Get()->GetLoggedInUser(); |
if (user_is_new) { |
if (!user_is_local) |
- SetInitialUserImage(email); |
+ SetInitialUserImage(user_id); |
} else { |
- if (!user_is_local) { |
- // If current user image is profile image, it needs to be refreshed. |
- bool download_profile_image = |
- user->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::FromSeconds(kProfileDataDownloadDelaySec)); |
- } |
- |
UMA_HISTOGRAM_ENUMERATION("UserImage.LoggedIn", |
ImageIndexToHistogramIndex(user->image_index()), |
kHistogramImagesCount); |
- if (users_to_migrate_.count(email)) { |
+ if (users_to_migrate_.find(user_id) != users_to_migrate_.end()) { |
const DictionaryValue* prefs_images_unsafe = |
g_browser_process->local_state()->GetDictionary(kUserImages); |
const base::DictionaryValue* image_properties = NULL; |
if (prefs_images_unsafe->GetDictionaryWithoutPathExpansion( |
- user->email(), &image_properties)) { |
+ user_id, &image_properties)) { |
std::string image_path; |
image_properties->GetString(kImagePathNodeName, &image_path); |
+ linked_ptr<Job>& job = jobs_[user_id]; |
+ job.reset(new Job(this, user_id)); |
if (!image_path.empty()) { |
- // User needs image format migration but |
- // first we need to load and decode that image. |
- LOG(INFO) << "Waiting for user image to load before migration"; |
- migrate_current_user_on_load_ = true; |
- unsafe_image_loader_->Start( |
- image_path, 0 /* no resize */, |
- base::Bind(&UserImageManagerImpl::SetUserImage, |
- base::Unretained(this), |
- user->email(), |
- user->image_index(), |
- user->image_url())); |
+ LOG(INFO) << "Loading old user image, then migrating it."; |
+ job->SetToPath(base::FilePath(image_path), |
+ user->image_index(), |
+ user->image_url(), |
+ false); |
} else { |
- // Otherwise migrate user image properties right away. |
- BrowserThread::PostDelayedTask( |
- BrowserThread::UI, |
- FROM_HERE, |
- base::Bind(&UserImageManagerImpl::MigrateUserImage, |
- base::Unretained(this)), |
- base::TimeDelta::FromSeconds(user_image_migration_delay_sec)); |
+ job->SetToDefaultImage(user->image_index()); |
} |
} |
} |
} |
- if (!user_is_local) { |
- // Set up a repeating timer for refreshing the profile data. |
- profile_download_timer_.Start( |
- FROM_HERE, base::TimeDelta::FromSeconds(kProfileRefreshIntervalSec), |
- this, &UserImageManagerImpl::DownloadProfileDataScheduled); |
+ // Reset the downloaded profile image as a new user logged in. |
+ downloaded_profile_image_ = gfx::ImageSkia(); |
+ downloaded_profile_image_data_url_.clear(); |
+ profile_image_url_ = GURL(); |
+ profile_image_requested_ = false; |
+ |
+ if (UserManager::Get()->IsLoggedInAsRegularUser()) { |
+ TryToInitDownloadedProfileImage(); |
+ |
+ // Schedule an initial download of the profile data (full name and |
+ // optionally image). |
+ profile_download_one_shot_timer_.Start( |
+ FROM_HERE, |
+ base::TimeDelta::FromSeconds(kProfileDataDownloadDelaySec), |
+ base::Bind(&UserImageManagerImpl::DownloadProfileData, |
+ base::Unretained(this), |
+ kProfileDownloadReasonLoggedIn)); |
+ // Schedule periodic refreshes of the profile data. |
+ profile_download_periodic_timer_.Start( |
+ FROM_HERE, |
+ base::TimeDelta::FromSeconds(kProfileRefreshIntervalSec), |
+ base::Bind(&UserImageManagerImpl::DownloadProfileData, |
+ base::Unretained(this), |
+ kProfileDownloadReasonScheduled)); |
+ } else { |
+ profile_download_one_shot_timer_.Stop(); |
+ profile_download_periodic_timer_.Stop(); |
} |
- CommandLine* command_line = CommandLine::ForCurrentProcess(); |
+ |
+ const CommandLine* command_line = CommandLine::ForCurrentProcess(); |
+ if (user_image_sync_observer_.get() && |
+ !command_line->HasSwitch(::switches::kMultiProfiles)) { |
+ NOTREACHED() << "User logged in more than once."; |
+ } |
+ |
if (user->CanSyncImage() && |
!command_line->HasSwitch(chromeos::switches::kDisableUserImageSync)) { |
- if (user_image_sync_observer_.get() && |
- !command_line->HasSwitch(::switches::kMultiProfiles)) |
- NOTREACHED() << "User logged in second time."; |
user_image_sync_observer_.reset(new UserImageSyncObserver(user)); |
+ } else { |
+ user_image_sync_observer_.reset(); |
} |
} |
-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::SaveUserDefaultImageIndex(const std::string& user_id, |
+ int default_image_index) { |
+ linked_ptr<Job>& job = jobs_[user_id]; |
+ job.reset(new Job(this, user_id)); |
+ job->SetToDefaultImage(default_image_index); |
} |
-void UserImageManagerImpl::SaveUserImage(const std::string& username, |
+void UserImageManagerImpl::SaveUserImage(const std::string& user_id, |
const UserImage& user_image) { |
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
- SaveUserImageInternal(username, User::kExternalImageIndex, |
- GURL(), user_image); |
+ linked_ptr<Job>& job = jobs_[user_id]; |
+ job.reset(new Job(this, user_id)); |
+ job->SetToImage(User::kExternalImageIndex, user_image); |
} |
-void UserImageManagerImpl::SaveUserImageFromFile(const std::string& username, |
+void UserImageManagerImpl::SaveUserImageFromFile(const std::string& user_id, |
const base::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)); |
+ linked_ptr<Job>& job = jobs_[user_id]; |
+ job.reset(new Job(this, user_id)); |
+ job->SetToPath(path, User::kExternalImageIndex, GURL(), true); |
} |
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); |
- } |
+ const std::string& user_id) { |
+ // Use the profile image if it has been downloaded already. Otherwise, use a |
+ // stub image (gray avatar). |
+ linked_ptr<Job>& job = jobs_[user_id]; |
+ job.reset(new Job(this, user_id)); |
+ job->SetToImage(User::kProfileImageIndex, |
+ downloaded_profile_image_.isNull() ? |
+ UserImage() : |
+ UserImage::CreateAndEncode(downloaded_profile_image_)); |
} |
-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::DeleteUserImage(const std::string& user_id) { |
+ jobs_.erase(user_id); |
+ DeleteUserImageAndLocalStateEntry(user_id, kUserImages); |
+ DeleteUserImageAndLocalStateEntry(user_id, kUserImageProperties); |
} |
void UserImageManagerImpl::DownloadProfileImage(const std::string& reason) { |
- DownloadProfileData(reason, true); |
+ profile_image_requested_ = true; |
+ DownloadProfileData(reason); |
} |
UserImageSyncObserver* UserImageManagerImpl::GetSyncObserver() const { |
@@ -428,230 +637,73 @@ UserImageSyncObserver* UserImageManagerImpl::GetSyncObserver() const { |
} |
void UserImageManagerImpl::Shutdown() { |
- profile_image_downloader_.reset(); |
+ profile_downloader_.reset(); |
user_image_sync_observer_.reset(); |
} |
const gfx::ImageSkia& UserImageManagerImpl::DownloadedProfileImage() const { |
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
return downloaded_profile_image_; |
} |
-base::FilePath UserImageManagerImpl::GetImagePathForUser( |
- const std::string& username) { |
- std::string filename = username + kSafeImagePathExtension; |
- base::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) { |
+void UserImageManagerImpl::SetInitialUserImage(const std::string& user_id) { |
// 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 if data stored or cached outside the user's cryptohome is to be |
- // treated as ephemeral. |
- if (UserManager::Get()->IsUserNonCryptohomeDataEphemeral(username)) |
- return; |
- |
- base::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 base::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 if data stored or cached outside the user's cryptohome is to be |
- // treated as ephemeral. |
- if (UserManager::Get()->IsUserNonCryptohomeDataEphemeral(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 base::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(); |
+ SaveUserDefaultImageIndex(user_id, |
+ base::RandInt(kFirstDefaultImageIndex, |
+ kDefaultImagesCount - 1)); |
+} |
+ |
+void UserImageManagerImpl::TryToInitDownloadedProfileImage() { |
+ const User* user = UserManager::Get()->GetLoggedInUser(); |
+ if (user->image_index() == User::kProfileImageIndex && |
+ downloaded_profile_image_.isNull() && |
+ !user->image_is_stub()) { |
+ // Initialize the |downloaded_profile_image_| for the currently logged-in |
+ // user if it has not been initialized already, the user image is the |
+ // profile image and the user image has been loaded successfully. |
+ VLOG(1) << "Profile image initialized from disk."; |
+ downloaded_profile_image_ = user->image(); |
downloaded_profile_image_data_url_ = |
webui::GetBitmapDataUrl(*downloaded_profile_image_.bitmap()); |
- profile_image_url_ = logged_in_user->image_url(); |
+ profile_image_url_ = user->image_url(); |
} |
} |
-void UserImageManagerImpl::DownloadProfileData(const std::string& reason, |
- bool download_image) { |
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+bool UserImageManagerImpl::NeedProfileImage() const { |
+ return UserManager::Get()->IsLoggedInAsRegularUser() && |
+ (UserManager::Get()->GetLoggedInUser()->image_index() == |
+ User::kProfileImageIndex || |
+ profile_image_requested_); |
+} |
+void UserImageManagerImpl::DownloadProfileData(const std::string& reason) { |
// GAIA profiles exist for regular users only. |
if (!UserManager::Get()->IsLoggedInAsRegularUser()) |
return; |
- // Mark profile picture as needed. |
- downloading_profile_image_ |= download_image; |
- |
- // Another download is already in progress |
- if (profile_image_downloader_.get()) |
+ // If a download is already in progress, allow it to continue, with one |
+ // exception: If the current download does not include the profile image but |
+ // the image has since become necessary, start a new download that includes |
+ // the profile image. |
+ if (profile_downloader_ && |
+ (downloading_profile_image_ || !NeedProfileImage())) { |
return; |
+ } |
+ downloading_profile_image_ = NeedProfileImage(); |
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); |
-} |
- |
-void UserImageManagerImpl::DownloadProfileDataRetry(bool download_image) { |
- DownloadProfileData(kProfileDownloadReasonRetry, download_image); |
+ profile_image_load_start_time_ = base::TimeTicks::Now(); |
+ profile_downloader_.reset(new ProfileDownloader(this)); |
+ profile_downloader_->Start(); |
} |
-// 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(); |
} |
@@ -662,21 +714,19 @@ Profile* UserImageManagerImpl::GetBrowserProfile() { |
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()); |
+ // Ensure that the |profile_downloader_| is deleted when this method returns. |
+ scoped_ptr<ProfileDownloader> profile_downloader( |
+ profile_downloader_.release()); |
+ DCHECK_EQ(downloader, profile_downloader.get()); |
- UserManager* user_manager = UserManager::Get(); |
- const User* user = user_manager->GetLoggedInUser(); |
+ const User* user = UserManager::Get()->GetLoggedInUser(); |
+ const std::string& user_id = user->email(); |
- user_manager->UpdateUserAccountData(user->email(), |
- downloader->GetProfileFullName(), |
- downloader->GetProfileLocale()); |
+ UserManager::Get()->UpdateUserAccountData(user_id, |
+ downloader->GetProfileFullName(), |
+ downloader->GetProfileLocale()); |
- bool requested_image = downloading_profile_image_; |
- downloading_profile_image_ = false; |
- if (!requested_image) |
+ if (!downloading_profile_image_) |
return; |
ProfileDownloadResult result = kDownloadFailure; |
@@ -695,11 +745,17 @@ void UserImageManagerImpl::OnProfileDownloadSuccess( |
} |
UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult", |
- result, kDownloadResultsCount); |
- |
+ 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); |
+ AddProfileImageTimeHistogram( |
+ result, |
+ profile_image_download_reason_, |
+ base::TimeTicks::Now() - profile_image_load_start_time_); |
+ |
+ // Ignore the image if it is no longer needed. |
+ if (!NeedProfileImage()) |
+ return; |
if (result == kDownloadDefault) { |
content::NotificationService::current()->Notify( |
@@ -708,16 +764,19 @@ void UserImageManagerImpl::OnProfileDownloadSuccess( |
content::NotificationService::NoDetails()); |
} |
- // Nothing to do if picture is cached or the default avatar. |
+ // Nothing to do if the picture is cached or is the default avatar. |
if (result != kDownloadSuccess) |
return; |
+ profile_image_requested_ = false; |
+ |
// Check if this image is not the same as already downloaded. |
- SkBitmap new_bitmap(downloader->GetProfilePicture()); |
- std::string new_image_data_url = webui::GetBitmapDataUrl(new_bitmap); |
+ const std::string new_image_data_url = |
+ webui::GetBitmapDataUrl(SkBitmap(downloader->GetProfilePicture())); |
if (!downloaded_profile_image_data_url_.empty() && |
- new_image_data_url == downloaded_profile_image_data_url_) |
+ new_image_data_url == downloaded_profile_image_data_url_) { |
return; |
+ } |
downloaded_profile_image_data_url_ = new_image_data_url; |
downloaded_profile_image_ = gfx::ImageSkia::CreateFrom1xBitmap( |
@@ -725,12 +784,12 @@ void UserImageManagerImpl::OnProfileDownloadSuccess( |
profile_image_url_ = GURL(downloader->GetProfilePictureURL()); |
if (user->image_index() == User::kProfileImageIndex) { |
- VLOG(1) << "Updating profile image for logged-in user"; |
+ 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()); |
+ // This will persist |downloaded_profile_image_| to disk. |
+ SaveUserImageFromProfileImage(user_id); |
} |
content::NotificationService::current()->Notify( |
@@ -742,78 +801,132 @@ void UserImageManagerImpl::OnProfileDownloadSuccess( |
void UserImageManagerImpl::OnProfileDownloadFailure( |
ProfileDownloader* downloader, |
ProfileDownloaderDelegate::FailureReason reason) { |
- DCHECK_EQ(downloader, profile_image_downloader_.get()); |
- profile_image_downloader_.reset(); |
+ DCHECK_EQ(downloader, profile_downloader_.get()); |
+ profile_downloader_.reset(); |
- 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); |
+ if (downloading_profile_image_) { |
+ UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult", |
+ kDownloadFailure, |
+ kDownloadResultsCount); |
+ DCHECK(!profile_image_load_start_time_.is_null()); |
+ AddProfileImageTimeHistogram( |
+ kDownloadFailure, |
+ profile_image_download_reason_, |
+ base::TimeTicks::Now() - profile_image_load_start_time_); |
+ } |
UserManager* user_manager = UserManager::Get(); |
const User* user = user_manager->GetLoggedInUser(); |
- // Need note that at least one attempt finished. |
user_manager->UpdateUserAccountData(user->email(), string16(), ""); |
- // Retry download after some time if a network error has occured. |
if (reason == ProfileDownloaderDelegate::NETWORK_ERROR) { |
- BrowserThread::PostDelayedTask( |
- BrowserThread::UI, |
+ // Retry download after a delay if a network error occurred. |
+ profile_download_one_shot_timer_.Start( |
FROM_HERE, |
- base::Bind(&UserImageManagerImpl::DownloadProfileDataRetry, |
+ base::TimeDelta::FromSeconds(kProfileDataDownloadRetryIntervalSec), |
+ base::Bind(&UserImageManagerImpl::DownloadProfileData, |
base::Unretained(this), |
- downloading_profile_image_), |
- base::TimeDelta::FromSeconds(kProfileDataDownloadRetryIntervalSec)); |
+ kProfileDownloadReasonRetry)); |
} |
- downloading_profile_image_ = false; |
- |
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()); |
+void UserImageManagerImpl::DeleteUserImageAndLocalStateEntry( |
+ const std::string& user_id, |
+ const char* prefs_dict_root) { |
+ DictionaryPrefUpdate update(g_browser_process->local_state(), |
+ prefs_dict_root); |
+ const base::DictionaryValue* image_properties; |
+ if (!update->GetDictionaryWithoutPathExpansion(user_id, &image_properties)) |
return; |
+ |
+ std::string image_path; |
+ image_properties->GetString(kImagePathNodeName, &image_path); |
+ if (!image_path.empty()) { |
+ background_task_runner_->PostTask( |
+ FROM_HERE, |
+ base::Bind(base::IgnoreResult(&base::DeleteFile), |
+ base::FilePath(image_path), |
+ false)); |
} |
- if (user->HasDefaultImage()) { |
- SaveUserDefaultImageIndex(user->email(), user->image_index()); |
+ update->RemoveWithoutPathExpansion(user_id, NULL); |
+} |
+ |
+void UserImageManagerImpl::OnJobChangedUserImage(const User* user) { |
+ if (user == UserManager::Get()->GetLoggedInUser()) |
+ TryToInitDownloadedProfileImage(); |
+ |
+ content::NotificationService::current()->Notify( |
+ chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED, |
+ content::Source<UserImageManagerImpl>(this), |
+ content::Details<const User>(user)); |
+} |
+ |
+void UserImageManagerImpl::OnJobDone(const std::string& user_id) { |
+ std::map<std::string, linked_ptr<Job> >::iterator it = |
+ jobs_.find(user_id); |
+ if (it != jobs_.end()) { |
+ // The Job for |user_id| is done and must be destroyed. Since control will |
+ // return to the Job when this method finishes, it cannot be destroyed from |
+ // here. Instead, ownership is dropped and the Job destroys itself. |
+ it->second.release(); |
+ jobs_.erase(it); |
} else { |
- SaveUserImageInternal(user->email(), user->image_index(), |
- user->image_url(), user->user_image()); |
+ NOTREACHED(); |
+ } |
+ |
+ if (users_to_migrate_.find(user_id) == users_to_migrate_.end()) |
+ return; |
+ // Migration completed for |user_id|. |
+ users_to_migrate_.erase(user_id); |
+ |
+ const DictionaryValue* prefs_images_unsafe = |
+ g_browser_process->local_state()->GetDictionary(kUserImages); |
+ const base::DictionaryValue* image_properties = NULL; |
+ if (!prefs_images_unsafe->GetDictionaryWithoutPathExpansion( |
+ user_id, &image_properties)) { |
+ NOTREACHED(); |
+ return; |
} |
+ |
+ int image_index = User::kInvalidImageIndex; |
+ image_properties->GetInteger(kImageIndexNodeName, &image_index); |
UMA_HISTOGRAM_ENUMERATION("UserImage.Migration", |
- ImageIndexToHistogramIndex(user->image_index()), |
+ ImageIndexToHistogramIndex(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); |
+ std::string image_path; |
+ image_properties->GetString(kImagePathNodeName, &image_path); |
+ if (!image_path.empty()) { |
+ // If an old image exists, delete it and remove |user_id| from the old prefs |
+ // dictionary only after the deletion has completed. This ensures that no |
+ // orphaned image is left behind if the browser crashes before the deletion |
+ // has been performed: In that case, local state will be unchanged and the |
+ // migration will be run again on the user's next login. |
+ background_task_runner_->PostTaskAndReply( |
+ FROM_HERE, |
+ base::Bind(base::IgnoreResult(&base::DeleteFile), |
+ base::FilePath(image_path), |
+ false), |
+ base::Bind(&UserImageManagerImpl::UpdateLocalStateAfterMigration, |
+ weak_factory_.GetWeakPtr(), |
+ user_id)); |
+ } else { |
+ // If no old image exists, remove |user_id| from the old prefs dictionary. |
+ UpdateLocalStateAfterMigration(user_id); |
} |
} |
+void UserImageManagerImpl::UpdateLocalStateAfterMigration( |
+ const std::string& user_id) { |
+ DictionaryPrefUpdate update(g_browser_process->local_state(), |
+ kUserImages); |
+ update->RemoveWithoutPathExpansion(user_id, NULL); |
+} |
+ |
} // namespace chromeos |