| 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..c73979667b43ae26781ebbb41b7c5788fe067888 100644
|
| --- a/chrome/browser/chromeos/login/user_image_manager_impl.cc
|
| +++ b/chrome/browser/chromeos/login/user_image_manager_impl.cc
|
| @@ -10,6 +10,7 @@
|
| #include "base/file_util.h"
|
| #include "base/files/file_path.h"
|
| #include "base/logging.h"
|
| +#include "base/message_loop/message_loop_proxy.h"
|
| #include "base/metrics/histogram.h"
|
| #include "base/path_service.h"
|
| #include "base/prefs/pref_registry_simple.h"
|
| @@ -17,10 +18,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 +35,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 +56,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;
|
| -
|
| // Delay betweeen user login and attempt to update user's profile data.
|
| const int kProfileDataDownloadDelaySec = 10;
|
|
|
| @@ -104,7 +98,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 +137,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 +150,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 +182,258 @@ 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.
|
| + DCHECK(!image_path_.empty());
|
| + parent_->image_loader_->Start(image_path_.value(),
|
| + 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_);
|
| + run_ = true;
|
| +
|
| + image_index_ = image_index;
|
| + image_url_ = image_url;
|
| +
|
| + DCHECK(!path.empty());
|
| + 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 = UserManager::Get()->FindUserAndModify(user_id_);
|
| + 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_);
|
| +}
|
| +
|
| 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,
|
| + &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,130 @@ 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()) {
|
| + base::MessageLoopProxy::current()->DeleteSoon(FROM_HERE,
|
| + 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
|
|
|