Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3824)

Unified Diff: chrome/browser/chromeos/login/user_image_manager_impl.cc

Issue 69863006: Address races in UserImageManagerImpl (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698