<commit_before>// 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/message_loop/message_loop_proxy.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/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/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/chromeos/policy/device_local_account_policy_service.h"
#include "chrome/browser/chromeos/settings/cros_settings.h"
#include "chrome/browser/policy/browser_policy_connector.h"
#include "chrome/browser/profiles/profile_downloader.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/chrome_paths.h"
#include "chromeos/chromeos_switches.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
#include "policy/policy_constants.h"
#include "ui/gfx/image/image_skia.h"

namespace chromeos {

namespace {

// 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 user_ids 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 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 profile image download when the user chooses the
// profile image but it has not been downloaded yet.
const char kProfileDownloadReasonProfileImageChosen[] = "ProfileImageChosen";
// 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";

static bool g_ignore_profile_data_download_delay_ = false;

// 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,
                                  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();
}

// 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;
  }
}

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;
  }

  if (!encoded_image->size() ||
      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) {
  registry->RegisterDictionaryPref(kUserImages);
  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 corresponding to |parent|.
  explicit Job(UserImageManagerImpl* parent);
  ~Job();

  // Loads the image at |image_path| or one of the default images,
  // depending on |image_index|, and updates the user object with the
  // new image.
  void LoadImage(base::FilePath image_path,
                 const int image_index,
                 const GURL& image_url);

  // Sets the user image in local state to the default image indicated
  // by |default_image_index|. Also updates the user object with the
  // new image.
  void SetToDefaultImage(int default_image_index);

  // Saves the |user_image| to disk and sets the user image in local
  // state to that image. Also updates the user with the new image.
  void SetToImage(int image_index, const UserImage& user_image);

  // Decodes the JPEG image |data|, crops and resizes the image, saves
  // it to disk and sets the user image in local state to that image.
  // Also updates the user object with the new image.
  void SetToImageData(scoped_ptr<std::string> data);

  // Loads the image at |path|, transcodes it to JPEG format, saves
  // the image to disk and sets the user image in local state to that
  // image.  If |resize| is true, the image is cropped and resized
  // before transcoding.  Also updates the user object 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 with |user_image_|.
  void UpdateUser();

  // Saves |user_image_| to disk in JPEG format. Local state will be updated
  // when a callback indicates that the image has been saved.
  void SaveImageAndUpdateLocalState();

  // Called back after the |user_image_| has been saved to
  // disk. Updates the user image information in local state. The
  // information is only updated if |success| is true (indicating that
  // the image was saved successfully) or the user image is the
  // profile image (indicating that even if the image could not be
  // saved because it is not available right now, it will be
  // downloaded eventually).
  void OnSaveImageDone(bool success);

  // Updates the user image 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();

  const std::string& user_id() const { return parent_->user_id(); }

  UserImageManagerImpl* parent_;

  // 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)
    : parent_(parent),
      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::SetToImageData(scoped_ptr<std::string> data) {
  DCHECK(!run_);
  run_ = true;

  image_index_ = User::kExternalImageIndex;

  // This method uses the image_loader_, not the unsafe_image_loader_:
  // * This is necessary because the method is used to update the user image
  //   whenever the policy for a user is set. In the case of device-local
  //   accounts, policy may change at any time, even if the user is not
  //   currently logged in (and thus, the unsafe_image_loader_ may not be used).
  // * This is possible because only JPEG |data| is accepted. No support for
  //   other image file formats is needed.
  // * This is safe because the image_loader_ employs a hardened JPEG decoder
  //   that protects against malicious invalid image data being used to attack
  //   the login screen or another user session currently in progress.
  parent_->image_loader_->Start(data.Pass(),
                                login::kMaxUserImageSize,
                                base::Bind(&Job::OnLoadImageDone,
                                           weak_factory_.GetWeakPtr(),
                                           true));
}

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 = parent_->user_manager_->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();
}

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 || image_index_ == User::kProfileImageIndex)
    UpdateLocalState();
  NotifyJobDone();
}

void UserImageManagerImpl::Job::UpdateLocalState() {
  // Ignore if data stored or cached outside the user's cryptohome is to be
  // treated as ephemeral.
  if (parent_->user_manager_->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 base::StringValue(image_url_.spec()));
  DictionaryPrefUpdate update(g_browser_process->local_state(),
                              kUserImageProperties);
  update->SetWithoutPathExpansion(user_id(), entry.release());

  parent_->user_manager_->NotifyLocalStateChanged();
}

void UserImageManagerImpl::Job::NotifyJobDone() {
  parent_->OnJobDone();
}

UserImageManagerImpl::UserImageManagerImpl(const std::string& user_id,
                                           CrosSettings* cros_settings,
                                           UserManager* user_manager)
    : UserImageManager(user_id),
      user_manager_(user_manager),
      downloading_profile_image_(false),
      profile_image_requested_(false),
      has_managed_image_(false),
      user_needs_migration_(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,
                                      background_task_runner_);
  unsafe_image_loader_ = new UserImageLoader(ImageDecoder::DEFAULT_CODEC,
                                             background_task_runner_);
}

UserImageManagerImpl::~UserImageManagerImpl() {}

void UserImageManagerImpl::LoadUserImage() {
  PrefService* local_state = g_browser_process->local_state();
  const base::DictionaryValue* prefs_images_unsafe =
      local_state->GetDictionary(kUserImages);
  const base::DictionaryValue* prefs_images =
      local_state->GetDictionary(kUserImageProperties);
  if (!prefs_images && !prefs_images_unsafe)
    return;
  User* user = GetUserAndModify();
  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_id(), &image_properties);
    if (needs_migration)
      user_needs_migration_ = true;
  }
  if (prefs_images) {
    prefs_images->GetDictionaryWithoutPathExpansion(user_id(),
                                                    &image_properties);
  }

  // If the user image for |user_id| is managed by policy and the policy-set
  // image is being loaded and persisted right now, let that job continue. It
  // will update the user image when done.
  if (IsUserImageManaged() && job_.get())
    return;

  if (!image_properties) {
    SetInitialUserImage();
    return;
  }

  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);
    return;
  }

  if (image_index != User::kExternalImageIndex &&
      image_index != User::kProfileImageIndex) {
    NOTREACHED();
    return;
  }

  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);
    return;
  }

  job_.reset(new Job(this));
  job_->LoadImage(base::FilePath(image_path), image_index, image_url);
}
<commit_msg>Fix User->image() without ImageSkia representation after login<commit_after>// 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/message_loop/message_loop_proxy.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/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/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/chromeos/policy/device_local_account_policy_service.h"
#include "chrome/browser/chromeos/settings/cros_settings.h"
#include "chrome/browser/policy/browser_policy_connector.h"
#include "chrome/browser/profiles/profile_downloader.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/chrome_paths.h"
#include "chromeos/chromeos_switches.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
#include "policy/policy_constants.h"
#include "ui/gfx/image/image_skia.h"

namespace chromeos {

namespace {

// 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 user_ids 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 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 profile image download when the user chooses the
// profile image but it has not been downloaded yet.
const char kProfileDownloadReasonProfileImageChosen[] = "ProfileImageChosen";
// 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";

static bool g_ignore_profile_data_download_delay_ = false;

// 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,
                                  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();
}

// 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;
  }
}

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;
  }

  if (!encoded_image->size() ||
      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) {
  registry->RegisterDictionaryPref(kUserImages);
  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 corresponding to |parent|.
  explicit Job(UserImageManagerImpl* parent);
  ~Job();

  // Loads the image at |image_path| or one of the default images,
  // depending on |image_index|, and updates the user object with the
  // new image.
  void LoadImage(base::FilePath image_path,
                 const int image_index,
                 const GURL& image_url);

  // Sets the user image in local state to the default image indicated
  // by |default_image_index|. Also updates the user object with the
  // new image.
  void SetToDefaultImage(int default_image_index);

  // Saves the |user_image| to disk and sets the user image in local
  // state to that image. Also updates the user with the new image.
  void SetToImage(int image_index, const UserImage& user_image);

  // Decodes the JPEG image |data|, crops and resizes the image, saves
  // it to disk and sets the user image in local state to that image.
  // Also updates the user object with the new image.
  void SetToImageData(scoped_ptr<std::string> data);

  // Loads the image at |path|, transcodes it to JPEG format, saves
  // the image to disk and sets the user image in local state to that
  // image.  If |resize| is true, the image is cropped and resized
  // before transcoding.  Also updates the user object 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 with |user_image_|.
  void UpdateUser();

  // Saves |user_image_| to disk in JPEG format. Local state will be updated
  // when a callback indicates that the image has been saved.
  void SaveImageAndUpdateLocalState();

  // Called back after the |user_image_| has been saved to
  // disk. Updates the user image information in local state. The
  // information is only updated if |success| is true (indicating that
  // the image was saved successfully) or the user image is the
  // profile image (indicating that even if the image could not be
  // saved because it is not available right now, it will be
  // downloaded eventually).
  void OnSaveImageDone(bool success);

  // Updates the user image 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();

  const std::string& user_id() const { return parent_->user_id(); }

  UserImageManagerImpl* parent_;

  // 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)
    : parent_(parent),
      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::SetToImageData(scoped_ptr<std::string> data) {
  DCHECK(!run_);
  run_ = true;

  image_index_ = User::kExternalImageIndex;

  // This method uses the image_loader_, not the unsafe_image_loader_:
  // * This is necessary because the method is used to update the user image
  //   whenever the policy for a user is set. In the case of device-local
  //   accounts, policy may change at any time, even if the user is not
  //   currently logged in (and thus, the unsafe_image_loader_ may not be used).
  // * This is possible because only JPEG |data| is accepted. No support for
  //   other image file formats is needed.
  // * This is safe because the image_loader_ employs a hardened JPEG decoder
  //   that protects against malicious invalid image data being used to attack
  //   the login screen or another user session currently in progress.
  parent_->image_loader_->Start(data.Pass(),
                                login::kMaxUserImageSize,
                                base::Bind(&Job::OnLoadImageDone,
                                           weak_factory_.GetWeakPtr(),
                                           true));
}

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 = parent_->user_manager_->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();
}

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 || image_index_ == User::kProfileImageIndex)
    UpdateLocalState();
  NotifyJobDone();
}

void UserImageManagerImpl::Job::UpdateLocalState() {
  // Ignore if data stored or cached outside the user's cryptohome is to be
  // treated as ephemeral.
  if (parent_->user_manager_->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 base::StringValue(image_url_.spec()));
  DictionaryPrefUpdate update(g_browser_process->local_state(),
                              kUserImageProperties);
  update->SetWithoutPathExpansion(user_id(), entry.release());

  parent_->user_manager_->NotifyLocalStateChanged();
}

void UserImageManagerImpl::Job::NotifyJobDone() {
  parent_->OnJobDone();
}

UserImageManagerImpl::UserImageManagerImpl(const std::string& user_id,
                                           CrosSettings* cros_settings,
                                           UserManager* user_manager)
    : UserImageManager(user_id),
      user_manager_(user_manager),
      downloading_profile_image_(false),
      profile_image_requested_(false),
      has_managed_image_(false),
      user_needs_migration_(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,
                                      background_task_runner_);
  unsafe_image_loader_ = new UserImageLoader(ImageDecoder::DEFAULT_CODEC,
                                             background_task_runner_);
}

UserImageManagerImpl::~UserImageManagerImpl() {}

void UserImageManagerImpl::LoadUserImage() {
  PrefService* local_state = g_browser_process->local_state();
  const base::DictionaryValue* prefs_images_unsafe =
      local_state->GetDictionary(kUserImages);
  const base::DictionaryValue* prefs_images =
      local_state->GetDictionary(kUserImageProperties);
  if (!prefs_images && !prefs_images_unsafe)
    return;
  User* user = GetUserAndModify();
  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_id(), &image_properties);
    if (needs_migration)
      user_needs_migration_ = true;
  }
  if (prefs_images) {
    prefs_images->GetDictionaryWithoutPathExpansion(user_id(),
                                                    &image_properties);
  }

  // If the user image for |user_id| is managed by policy and the policy-set
  // image is being loaded and persisted right now, let that job continue. It
  // will update the user image when done.
  if (IsUserImageManaged() && job_.get())
    return;

  if (!image_properties) {
    SetInitialUserImage();
    return;
  }

  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);
    return;
  }

  if (image_index != User::kExternalImageIndex &&
      image_index != User::kProfileImageIndex) {
    NOTREACHED();
    return;
  }

  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);
  user->SetStubImage(image_index, true);
  DCHECK(!image_path.empty() || image_index == User::kProfileImageIndex);
  if (image_path.empty() || needs_migration) {
    // Return 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);
    return;
  }

  job_.reset(new Job(this));
  job_->LoadImage(base::FilePath(image_path), image_index, image_url);
}

{'fraction_non_alphanumeric': 0.09635467027654225, 'fraction_numerical': 0.0014503964416940631, 'mean_word_length': 3.457866379310345, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 107, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 186, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6076642', 'n_tokens_mistral': 11898, 'n_tokens_neox': 11619, 'n_words': 3231}