blob: 93ddf9d92595f0df4a689af3f2b7f335156ca00a [file] [log] [blame]
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/login/user_image_manager_impl.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/debug/trace_event.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/path_service.h"
#include "base/prefs/pref_registry_simple.h"
#include "base/prefs/pref_service.h"
#include "base/rand_util.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"
#include "chrome/browser/chromeos/login/helper.h"
#include "chrome/browser/chromeos/login/user_image.h"
#include "chrome/browser/chromeos/login/user_image_sync_observer.h"
#include "chrome/browser/chromeos/login/user_manager.h"
#include "chrome/browser/prefs/scoped_user_pref_update.h"
#include "chrome/browser/profiles/profile_downloader.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#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/gfx/image/image_skia.h"
#include "ui/webui/web_ui_util.h"
using content::BrowserThread;
namespace chromeos {
namespace {
// A dictionary that maps usernames to old user image data with images stored in
// PNG format. Deprecated.
// TODO(ivankr): remove this const char after migration is gone.
const char kUserImages[] = "UserImages";
// A dictionary that maps usernames to user image data with images stored in
// JPEG format.
const char kUserImageProperties[] = "user_image_info";
// Names of user image properties.
const char kImagePathNodeName[] = "path";
const char kImageIndexNodeName[] = "index";
const char kImageURLNodeName[] = "url";
// Delay betweeen user login and user image migration.
const int kUserImageMigrationDelaySec = 50;
// Delay betweeen user login and attempt to update user's profile data.
const int kProfileDataDownloadDelaySec = 10;
// Interval betweeen retries to update user's profile data.
const int kProfileDataDownloadRetryIntervalSec = 300;
// Delay betweeen subsequent profile refresh attempts (24 hrs).
const int kProfileRefreshIntervalSec = 24 * 3600;
const char kSafeImagePathExtension[] = ".jpg";
// Enum for reporting histograms about profile picture download.
enum ProfileDownloadResult {
kDownloadSuccessChanged,
kDownloadSuccess,
kDownloadFailure,
kDownloadDefault,
kDownloadCached,
// Must be the last, convenient count.
kDownloadResultsCount
};
// Time histogram prefix for a cached profile image download.
const char kProfileDownloadCachedTime[] =
"UserImage.ProfileDownloadTime.Cached";
// Time histogram prefix for the default profile image download.
const char kProfileDownloadDefaultTime[] =
"UserImage.ProfileDownloadTime.Default";
// Time histogram prefix for a failed profile image download.
const char kProfileDownloadFailureTime[] =
"UserImage.ProfileDownloadTime.Failure";
// Time histogram prefix for a successful profile image download.
const char kProfileDownloadSuccessTime[] =
"UserImage.ProfileDownloadTime.Success";
// Time histogram suffix for a profile image download after login.
const char kProfileDownloadReasonLoggedIn[] = "LoggedIn";
// Time histogram suffix for a scheduled profile image download.
const char kProfileDownloadReasonScheduled[] = "Scheduled";
// 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.
// Separate histograms are reported for each download |reason| and |result|.
void AddProfileImageTimeHistogram(ProfileDownloadResult result,
const std::string& download_reason,
const base::TimeDelta& time_delta) {
std::string histogram_name;
switch (result) {
case kDownloadFailure:
histogram_name = kProfileDownloadFailureTime;
break;
case kDownloadDefault:
histogram_name = kProfileDownloadDefaultTime;
break;
case kDownloadSuccess:
histogram_name = kProfileDownloadSuccessTime;
break;
case kDownloadCached:
histogram_name = kProfileDownloadCachedTime;
break;
default:
NOTREACHED();
}
if (!download_reason.empty()) {
histogram_name += ".";
histogram_name += download_reason;
}
static const base::TimeDelta min_time = base::TimeDelta::FromMilliseconds(1);
static const base::TimeDelta max_time = base::TimeDelta::FromSeconds(50);
const size_t bucket_count(50);
base::HistogramBase* counter = base::Histogram::FactoryTimeGet(
histogram_name, min_time, max_time, bucket_count,
base::HistogramBase::kUmaTargetedHistogramFlag);
counter->AddTime(time_delta);
DVLOG(1) << "Profile image download time: " << time_delta.InSecondsF();
}
// Deletes image file.
void DeleteImageFile(const std::string& image_path) {
if (image_path.empty())
return;
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) {
case User::kExternalImageIndex:
// TODO(ivankr): Distinguish this from selected from file.
return kHistogramImageFromCamera;
case User::kProfileImageIndex:
return kHistogramImageFromProfile;
default:
return image_index;
}
}
} // namespace
// static
int UserImageManagerImpl::user_image_migration_delay_sec =
kUserImageMigrationDelaySec;
// static
void UserImageManager::RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterDictionaryPref(kUserImages);
registry->RegisterDictionaryPref(kUserImageProperties);
}
UserImageManagerImpl::UserImageManagerImpl()
: image_loader_(new UserImageLoader(ImageDecoder::ROBUST_JPEG_CODEC)),
unsafe_image_loader_(new UserImageLoader(ImageDecoder::DEFAULT_CODEC)),
last_image_set_async_(false),
downloaded_profile_image_data_url_(content::kAboutBlankURL),
downloading_profile_image_(false),
migrate_current_user_on_load_(false) {
}
UserImageManagerImpl::~UserImageManagerImpl() {
}
void UserImageManagerImpl::LoadUserImages(const UserList& users) {
PrefService* local_state = g_browser_process->local_state();
const DictionaryValue* prefs_images_unsafe =
local_state->GetDictionary(kUserImages);
const DictionaryValue* prefs_images =
local_state->GetDictionary(kUserImageProperties);
if (!prefs_images && !prefs_images_unsafe)
return;
for (UserList::const_iterator it = users.begin(); it != users.end(); ++it) {
User* user = *it;
const base::DictionaryValue* image_properties = NULL;
bool needs_migration = false; // |true| if user has image in old format.
bool safe_source = false; // |true| if image loaded from safe source.
if (prefs_images_unsafe) {
needs_migration = prefs_images_unsafe->GetDictionaryWithoutPathExpansion(
user->email(), &image_properties);
}
if (prefs_images) {
safe_source = prefs_images->GetDictionaryWithoutPathExpansion(
user->email(), &image_properties);
}
if (needs_migration)
users_to_migrate_.insert(user->email());
if (!image_properties) {
SetInitialUserImage(user->email());
} else {
int image_index = User::kInvalidImageIndex;
image_properties->GetInteger(kImageIndexNodeName, &image_index);
if (image_index >= 0 && image_index < kDefaultImagesCount) {
user->SetImage(UserImage(GetDefaultImage(image_index)),
image_index);
} else if (image_index == User::kExternalImageIndex ||
image_index == User::kProfileImageIndex) {
std::string image_path;
image_properties->GetString(kImagePathNodeName, &image_path);
// Path may be empty for profile images (meaning that the image
// hasn't been downloaded for the first time yet, in which case a
// download will be scheduled for |kProfileDataDownloadDelayMs|
// after user logs in).
DCHECK(!image_path.empty() || image_index == User::kProfileImageIndex);
std::string image_url;
image_properties->GetString(kImageURLNodeName, &image_url);
GURL image_gurl(image_url);
// Until image has been loaded, use the stub image (gray avatar).
user->SetStubImage(image_index, true);
user->SetImageURL(image_gurl);
if (!image_path.empty()) {
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();
}
}
}
}
void UserImageManagerImpl::UserLoggedIn(const std::string& email,
bool user_is_new,
bool user_is_local) {
User* user = UserManager::Get()->GetLoggedInUser();
if (user_is_new) {
if (!user_is_local)
SetInitialUserImage(email);
} 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)) {
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)) {
std::string image_path;
image_properties->GetString(kImagePathNodeName, &image_path);
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()));
} 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));
}
}
}
}
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);
}
CommandLine* command_line = CommandLine::ForCurrentProcess();
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));
}
}
void UserImageManagerImpl::SaveUserDefaultImageIndex(
const std::string& username,
int image_index) {
DCHECK(image_index >= 0 && image_index < kDefaultImagesCount);
SetUserImage(username, image_index, GURL(),
UserImage(GetDefaultImage(image_index)));
SaveImageToLocalState(username, "", image_index, GURL(), false);
}
void UserImageManagerImpl::SaveUserImage(const std::string& username,
const UserImage& user_image) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
SaveUserImageInternal(username, User::kExternalImageIndex,
GURL(), user_image);
}
void UserImageManagerImpl::SaveUserImageFromFile(const std::string& username,
const 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));
}
void UserImageManagerImpl::SaveUserImageFromProfileImage(
const std::string& username) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (!downloaded_profile_image_.isNull()) {
// Profile image has already been downloaded, so save it to file right now.
DCHECK(profile_image_url_.is_valid());
SaveUserImageInternal(
username,
User::kProfileImageIndex, profile_image_url_,
UserImage::CreateAndEncode(downloaded_profile_image_));
} else {
// No profile image - use the stub image (gray avatar).
SetUserImage(username, User::kProfileImageIndex, GURL(), UserImage());
SaveImageToLocalState(username, "", User::kProfileImageIndex,
GURL(), false);
}
}
void UserImageManagerImpl::DeleteUserImage(const std::string& username) {
// Delete from the old dictionary, if present.
DeleteOldUserImage(username);
PrefService* prefs = g_browser_process->local_state();
DictionaryPrefUpdate prefs_images_update(prefs, kUserImageProperties);
const base::DictionaryValue* image_properties;
if (prefs_images_update->GetDictionaryWithoutPathExpansion(
username, &image_properties)) {
std::string image_path;
image_properties->GetString(kImageURLNodeName, &image_path);
prefs_images_update->RemoveWithoutPathExpansion(username, NULL);
DeleteImageFile(image_path);
}
}
void UserImageManagerImpl::DownloadProfileImage(const std::string& reason) {
DownloadProfileData(reason, true);
}
UserImageSyncObserver* UserImageManagerImpl::GetSyncObserver() const {
return user_image_sync_observer_.get();
}
void UserImageManagerImpl::Shutdown() {
profile_image_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) {
// 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();
downloaded_profile_image_data_url_ =
webui::GetBitmapDataUrl(*downloaded_profile_image_.bitmap());
profile_image_url_ = logged_in_user->image_url();
}
}
void UserImageManagerImpl::DownloadProfileData(const std::string& reason,
bool download_image) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// 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())
return;
profile_image_download_reason_ = reason;
profile_image_load_start_time_ = base::Time::Now();
profile_image_downloader_.reset(new ProfileDownloader(this));
profile_image_downloader_->Start();
}
void UserImageManagerImpl::DownloadProfileDataScheduled() {
const User* logged_in_user = UserManager::Get()->GetLoggedInUser();
// If current user image is profile image, it needs to be refreshed.
bool download_profile_image =
logged_in_user->image_index() == User::kProfileImageIndex;
DownloadProfileData(kProfileDownloadReasonScheduled, download_profile_image);
}
void UserImageManagerImpl::DownloadProfileDataRetry(bool download_image) {
DownloadProfileData(kProfileDownloadReasonRetry, download_image);
}
// ProfileDownloaderDelegate override.
bool UserImageManagerImpl::NeedsProfilePicture() const {
return downloading_profile_image_;
}
// ProfileDownloaderDelegate override.
int UserImageManagerImpl::GetDesiredImageSideLength() const {
return GetCurrentUserImageSize();
}
// ProfileDownloaderDelegate override.
std::string UserImageManagerImpl::GetCachedPictureURL() const {
return profile_image_url_.spec();
}
Profile* UserImageManagerImpl::GetBrowserProfile() {
return ProfileManager::GetDefaultProfile();
}
void UserImageManagerImpl::OnProfileDownloadSuccess(
ProfileDownloader* downloader) {
// Make sure that |ProfileDownloader| gets deleted after return.
scoped_ptr<ProfileDownloader> profile_image_downloader(
profile_image_downloader_.release());
DCHECK_EQ(downloader, profile_image_downloader.get());
UserManager* user_manager = UserManager::Get();
const User* user = user_manager->GetLoggedInUser();
user_manager->UpdateUserAccountData(user->email(),
downloader->GetProfileFullName(),
downloader->GetProfileLocale());
bool requested_image = downloading_profile_image_;
downloading_profile_image_ = false;
if (!requested_image)
return;
ProfileDownloadResult result = kDownloadFailure;
switch (downloader->GetProfilePictureStatus()) {
case ProfileDownloader::PICTURE_SUCCESS:
result = kDownloadSuccess;
break;
case ProfileDownloader::PICTURE_CACHED:
result = kDownloadCached;
break;
case ProfileDownloader::PICTURE_DEFAULT:
result = kDownloadDefault;
break;
default:
NOTREACHED();
}
UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult",
result, kDownloadResultsCount);
DCHECK(!profile_image_load_start_time_.is_null());
base::TimeDelta delta = base::Time::Now() - profile_image_load_start_time_;
AddProfileImageTimeHistogram(result, profile_image_download_reason_, delta);
if (result == kDownloadDefault) {
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED,
content::Source<UserImageManager>(this),
content::NotificationService::NoDetails());
}
// Nothing to do if picture is cached or the default avatar.
if (result != kDownloadSuccess)
return;
// Check if this image is not the same as already downloaded.
SkBitmap new_bitmap(downloader->GetProfilePicture());
std::string new_image_data_url = webui::GetBitmapDataUrl(new_bitmap);
if (!downloaded_profile_image_data_url_.empty() &&
new_image_data_url == downloaded_profile_image_data_url_)
return;
downloaded_profile_image_data_url_ = new_image_data_url;
downloaded_profile_image_ = gfx::ImageSkia::CreateFrom1xBitmap(
downloader->GetProfilePicture());
profile_image_url_ = GURL(downloader->GetProfilePictureURL());
if (user->image_index() == User::kProfileImageIndex) {
VLOG(1) << "Updating profile image for logged-in user";
UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult",
kDownloadSuccessChanged,
kDownloadResultsCount);
// This will persist |downloaded_profile_image_| to file.
SaveUserImageFromProfileImage(user->email());
}
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED,
content::Source<UserImageManager>(this),
content::Details<const gfx::ImageSkia>(&downloaded_profile_image_));
}
void UserImageManagerImpl::OnProfileDownloadFailure(
ProfileDownloader* downloader,
ProfileDownloaderDelegate::FailureReason reason) {
DCHECK_EQ(downloader, profile_image_downloader_.get());
profile_image_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);
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,
FROM_HERE,
base::Bind(&UserImageManagerImpl::DownloadProfileDataRetry,
base::Unretained(this),
downloading_profile_image_),
base::TimeDelta::FromSeconds(kProfileDataDownloadRetryIntervalSec));
}
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());
return;
}
if (user->HasDefaultImage()) {
SaveUserDefaultImageIndex(user->email(), user->image_index());
} else {
SaveUserImageInternal(user->email(), user->image_index(),
user->image_url(), user->user_image());
}
UMA_HISTOGRAM_ENUMERATION("UserImage.Migration",
ImageIndexToHistogramIndex(user->image_index()),
kHistogramImagesCount);
}
void UserImageManagerImpl::DeleteOldUserImage(const std::string& username) {
PrefService* prefs = g_browser_process->local_state();
DictionaryPrefUpdate prefs_images_update(prefs, kUserImages);
const base::DictionaryValue* image_properties;
if (prefs_images_update->GetDictionaryWithoutPathExpansion(
username, &image_properties)) {
std::string image_path;
image_properties->GetString(kImagePathNodeName, &image_path);
prefs_images_update->RemoveWithoutPathExpansion(username, NULL);
DeleteImageFile(image_path);
}
}
} // namespace chromeos