Home | History | Annotate | Download | only in wallpaper
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h"
      6 
      7 #include <numeric>
      8 #include <vector>
      9 
     10 #include "ash/ash_constants.h"
     11 #include "ash/ash_switches.h"
     12 #include "ash/desktop_background/desktop_background_controller.h"
     13 #include "ash/shell.h"
     14 #include "base/bind.h"
     15 #include "base/command_line.h"
     16 #include "base/debug/trace_event.h"
     17 #include "base/files/file_enumerator.h"
     18 #include "base/files/file_path.h"
     19 #include "base/files/file_util.h"
     20 #include "base/logging.h"
     21 #include "base/metrics/histogram.h"
     22 #include "base/path_service.h"
     23 #include "base/prefs/pref_registry_simple.h"
     24 #include "base/prefs/pref_service.h"
     25 #include "base/prefs/scoped_user_pref_update.h"
     26 #include "base/strings/string_number_conversions.h"
     27 #include "base/strings/string_util.h"
     28 #include "base/strings/stringprintf.h"
     29 #include "base/sys_info.h"
     30 #include "base/threading/worker_pool.h"
     31 #include "base/time/time.h"
     32 #include "base/values.h"
     33 #include "chrome/browser/browser_process.h"
     34 #include "chrome/browser/chrome_notification_types.h"
     35 #include "chrome/browser/chromeos/customization_document.h"
     36 #include "chrome/browser/chromeos/login/startup_utils.h"
     37 #include "chrome/browser/chromeos/login/wizard_controller.h"
     38 #include "chrome/browser/chromeos/settings/cros_settings.h"
     39 #include "chrome/common/chrome_paths.h"
     40 #include "chrome/common/chrome_switches.h"
     41 #include "chrome/common/pref_names.h"
     42 #include "chromeos/chromeos_switches.h"
     43 #include "chromeos/cryptohome/async_method_caller.h"
     44 #include "chromeos/dbus/dbus_thread_manager.h"
     45 #include "chromeos/login/user_names.h"
     46 #include "components/user_manager/user.h"
     47 #include "components/user_manager/user_image/user_image.h"
     48 #include "components/user_manager/user_manager.h"
     49 #include "components/user_manager/user_type.h"
     50 #include "content/public/browser/browser_thread.h"
     51 #include "content/public/browser/notification_service.h"
     52 #include "third_party/skia/include/core/SkColor.h"
     53 #include "ui/gfx/codec/jpeg_codec.h"
     54 #include "ui/gfx/image/image_skia_operations.h"
     55 #include "ui/gfx/skia_util.h"
     56 
     57 using content::BrowserThread;
     58 
     59 namespace chromeos {
     60 
     61 namespace {
     62 
     63 // The amount of delay before starts to move custom wallpapers to the new place.
     64 const int kMoveCustomWallpaperDelaySeconds = 30;
     65 
     66 // Default quality for encoding wallpaper.
     67 const int kDefaultEncodingQuality = 90;
     68 
     69 // A dictionary pref that maps usernames to file paths to their wallpapers.
     70 // Deprecated. Will remove this const char after done migration.
     71 const char kUserWallpapers[] = "UserWallpapers";
     72 
     73 const int kCacheWallpaperDelayMs = 500;
     74 
     75 // A dictionary pref that maps usernames to wallpaper properties.
     76 const char kUserWallpapersProperties[] = "UserWallpapersProperties";
     77 
     78 // Names of nodes with info about wallpaper in |kUserWallpapersProperties|
     79 // dictionary.
     80 const char kNewWallpaperDateNodeName[] = "date";
     81 const char kNewWallpaperLayoutNodeName[] = "layout";
     82 const char kNewWallpaperLocationNodeName[] = "file";
     83 const char kNewWallpaperTypeNodeName[] = "type";
     84 
     85 // Maximum number of wallpapers cached by CacheUsersWallpapers().
     86 const int kMaxWallpapersToCache = 3;
     87 
     88 // Maximum number of entries in WallpaperManager::last_load_times_ .
     89 const size_t kLastLoadsStatsMsMaxSize = 4;
     90 
     91 // Minimum delay between wallpaper loads, milliseconds.
     92 const unsigned kLoadMinDelayMs = 50;
     93 
     94 // Default wallpaper load delay, milliseconds.
     95 const unsigned kLoadDefaultDelayMs = 200;
     96 
     97 // Maximum wallpaper load delay, milliseconds.
     98 const unsigned kLoadMaxDelayMs = 2000;
     99 
    100 // When no wallpaper image is specified, the screen is filled with a solid
    101 // color.
    102 const SkColor kDefaultWallpaperColor = SK_ColorGRAY;
    103 
    104 // For our scaling ratios we need to round positive numbers.
    105 int RoundPositive(double x) {
    106   return static_cast<int>(floor(x + 0.5));
    107 }
    108 
    109 // Returns custom wallpaper directory by appending corresponding |sub_dir|.
    110 base::FilePath GetCustomWallpaperDir(const char* sub_dir) {
    111   base::FilePath custom_wallpaper_dir;
    112   CHECK(PathService::Get(chrome::DIR_CHROMEOS_CUSTOM_WALLPAPERS,
    113                          &custom_wallpaper_dir));
    114   return custom_wallpaper_dir.Append(sub_dir);
    115 }
    116 
    117 bool MoveCustomWallpaperDirectory(const char* sub_dir,
    118                                   const std::string& user_id,
    119                                   const std::string& user_id_hash) {
    120   base::FilePath base_path = GetCustomWallpaperDir(sub_dir);
    121   base::FilePath to_path = base_path.Append(user_id_hash);
    122   base::FilePath from_path = base_path.Append(user_id);
    123   if (base::PathExists(from_path))
    124     return base::Move(from_path, to_path);
    125   return false;
    126 }
    127 
    128 // These global default values are used to set customized default
    129 // wallpaper path in WallpaperManager::InitializeWallpaper().
    130 base::FilePath GetCustomizedWallpaperDefaultRescaledFileName(
    131     const std::string& suffix) {
    132   const base::FilePath default_downloaded_file_name =
    133       ServicesCustomizationDocument::GetCustomizedWallpaperDownloadedFileName();
    134   const base::FilePath default_cache_dir =
    135       ServicesCustomizationDocument::GetCustomizedWallpaperCacheDir();
    136   if (default_downloaded_file_name.empty() || default_cache_dir.empty())
    137     return base::FilePath();
    138   return default_cache_dir.Append(
    139       default_downloaded_file_name.BaseName().value() + suffix);
    140 }
    141 
    142 // Whether DesktopBackgroundController should start with customized default
    143 // wallpaper in WallpaperManager::InitializeWallpaper() or not.
    144 bool ShouldUseCustomizedDefaultWallpaper() {
    145   PrefService* pref_service = g_browser_process->local_state();
    146 
    147   return !(pref_service->FindPreference(
    148                              prefs::kCustomizationDefaultWallpaperURL)
    149                ->IsDefaultValue());
    150 }
    151 
    152 // Deletes a list of wallpaper files in |file_list|.
    153 void DeleteWallpaperInList(const std::vector<base::FilePath>& file_list) {
    154   for (std::vector<base::FilePath>::const_iterator it = file_list.begin();
    155        it != file_list.end(); ++it) {
    156     base::FilePath path = *it;
    157     // Some users may still have legacy wallpapers with png extension. We need
    158     // to delete these wallpapers too.
    159     if (!base::DeleteFile(path, true) &&
    160         !base::DeleteFile(path.AddExtension(".png"), false)) {
    161       LOG(ERROR) << "Failed to remove user wallpaper at " << path.value();
    162     }
    163   }
    164 }
    165 
    166 // Creates all new custom wallpaper directories for |user_id_hash| if not exist.
    167 // static
    168 void EnsureCustomWallpaperDirectories(const std::string& user_id_hash) {
    169   base::FilePath dir;
    170   dir = GetCustomWallpaperDir(kSmallWallpaperSubDir);
    171   dir = dir.Append(user_id_hash);
    172   if (!base::PathExists(dir))
    173     base::CreateDirectory(dir);
    174   dir = GetCustomWallpaperDir(kLargeWallpaperSubDir);
    175   dir = dir.Append(user_id_hash);
    176   if (!base::PathExists(dir))
    177     base::CreateDirectory(dir);
    178   dir = GetCustomWallpaperDir(kOriginalWallpaperSubDir);
    179   dir = dir.Append(user_id_hash);
    180   if (!base::PathExists(dir))
    181     base::CreateDirectory(dir);
    182   dir = GetCustomWallpaperDir(kThumbnailWallpaperSubDir);
    183   dir = dir.Append(user_id_hash);
    184   if (!base::PathExists(dir))
    185     base::CreateDirectory(dir);
    186 }
    187 
    188 // Saves wallpaper image raw |data| to |path| (absolute path) in file system.
    189 // Returns true on success.
    190 bool SaveWallpaperInternal(const base::FilePath& path,
    191                            const char* data,
    192                            int size) {
    193   int written_bytes = base::WriteFile(path, data, size);
    194   return written_bytes == size;
    195 }
    196 
    197 // Returns index of the first public session user found in |users|
    198 // or -1 otherwise.
    199 int FindPublicSession(const user_manager::UserList& users) {
    200   int index = -1;
    201   int i = 0;
    202   for (user_manager::UserList::const_iterator it = users.begin();
    203        it != users.end();
    204        ++it, ++i) {
    205     if ((*it)->GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT) {
    206       index = i;
    207       break;
    208     }
    209   }
    210 
    211   return index;
    212 }
    213 
    214 }  // namespace
    215 
    216 const char kWallpaperSequenceTokenName[] = "wallpaper-sequence";
    217 
    218 const char kSmallWallpaperSuffix[] = "_small";
    219 const char kLargeWallpaperSuffix[] = "_large";
    220 
    221 const char kSmallWallpaperSubDir[] = "small";
    222 const char kLargeWallpaperSubDir[] = "large";
    223 const char kOriginalWallpaperSubDir[] = "original";
    224 const char kThumbnailWallpaperSubDir[] = "thumb";
    225 
    226 const int kSmallWallpaperMaxWidth = 1366;
    227 const int kSmallWallpaperMaxHeight = 800;
    228 const int kLargeWallpaperMaxWidth = 2560;
    229 const int kLargeWallpaperMaxHeight = 1700;
    230 const int kWallpaperThumbnailWidth = 108;
    231 const int kWallpaperThumbnailHeight = 68;
    232 
    233 static WallpaperManager* g_wallpaper_manager = NULL;
    234 
    235 class WallpaperManager::CustomizedWallpaperRescaledFiles {
    236  public:
    237   CustomizedWallpaperRescaledFiles(const base::FilePath& path_downloaded,
    238                                    const base::FilePath& path_rescaled_small,
    239                                    const base::FilePath& path_rescaled_large);
    240 
    241   bool AllSizesExist() const;
    242 
    243   // Closure will hold unretained pointer to this object. So caller must
    244   // make sure that the closure will be destoyed before this object.
    245   // Closure must be called on BlockingPool.
    246   base::Closure CreateCheckerClosure();
    247 
    248   const base::FilePath& path_downloaded() const { return path_downloaded_; }
    249   const base::FilePath& path_rescaled_small() const {
    250     return path_rescaled_small_;
    251   }
    252   const base::FilePath& path_rescaled_large() const {
    253     return path_rescaled_large_;
    254   }
    255 
    256   const bool downloaded_exists() const { return downloaded_exists_; }
    257   const bool rescaled_small_exists() const { return rescaled_small_exists_; }
    258   const bool rescaled_large_exists() const { return rescaled_large_exists_; }
    259 
    260  private:
    261   // Must be called on BlockingPool.
    262   void CheckCustomizedWallpaperFilesExist();
    263 
    264   const base::FilePath path_downloaded_;
    265   const base::FilePath path_rescaled_small_;
    266   const base::FilePath path_rescaled_large_;
    267 
    268   bool downloaded_exists_;
    269   bool rescaled_small_exists_;
    270   bool rescaled_large_exists_;
    271 
    272   DISALLOW_COPY_AND_ASSIGN(CustomizedWallpaperRescaledFiles);
    273 };
    274 
    275 WallpaperManager::CustomizedWallpaperRescaledFiles::
    276     CustomizedWallpaperRescaledFiles(const base::FilePath& path_downloaded,
    277                                      const base::FilePath& path_rescaled_small,
    278                                      const base::FilePath& path_rescaled_large)
    279     : path_downloaded_(path_downloaded),
    280       path_rescaled_small_(path_rescaled_small),
    281       path_rescaled_large_(path_rescaled_large),
    282       downloaded_exists_(false),
    283       rescaled_small_exists_(false),
    284       rescaled_large_exists_(false) {
    285 }
    286 
    287 base::Closure
    288 WallpaperManager::CustomizedWallpaperRescaledFiles::CreateCheckerClosure() {
    289   return base::Bind(&WallpaperManager::CustomizedWallpaperRescaledFiles::
    290                         CheckCustomizedWallpaperFilesExist,
    291                     base::Unretained(this));
    292 }
    293 
    294 void WallpaperManager::CustomizedWallpaperRescaledFiles::
    295     CheckCustomizedWallpaperFilesExist() {
    296   DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
    297   downloaded_exists_ = base::PathExists(path_downloaded_);
    298   rescaled_small_exists_ = base::PathExists(path_rescaled_small_);
    299   rescaled_large_exists_ = base::PathExists(path_rescaled_large_);
    300 }
    301 
    302 bool WallpaperManager::CustomizedWallpaperRescaledFiles::AllSizesExist() const {
    303   return rescaled_small_exists_ && rescaled_large_exists_;
    304 }
    305 
    306 // This object is passed between several threads while wallpaper is being
    307 // loaded. It will notify callback when last reference to it is removed
    308 // (thus indicating that the last load action has finished).
    309 class MovableOnDestroyCallback {
    310  public:
    311   explicit MovableOnDestroyCallback(const base::Closure& callback)
    312       : callback_(callback) {
    313   }
    314 
    315   ~MovableOnDestroyCallback() {
    316     if (!callback_.is_null())
    317       callback_.Run();
    318   }
    319 
    320  private:
    321   base::Closure callback_;
    322 };
    323 
    324 WallpaperManager::PendingWallpaper::PendingWallpaper(
    325     const base::TimeDelta delay,
    326     const std::string& user_id)
    327     : user_id_(user_id),
    328       default_(false),
    329       on_finish_(new MovableOnDestroyCallback(
    330           base::Bind(&WallpaperManager::PendingWallpaper::OnWallpaperSet,
    331                      this))) {
    332   timer.Start(
    333       FROM_HERE,
    334       delay,
    335       base::Bind(&WallpaperManager::PendingWallpaper::ProcessRequest, this));
    336 }
    337 
    338 WallpaperManager::PendingWallpaper::~PendingWallpaper() {}
    339 
    340 void WallpaperManager::PendingWallpaper::ResetSetWallpaperImage(
    341     const gfx::ImageSkia& image,
    342     const WallpaperInfo& info) {
    343   SetMode(image, info, base::FilePath(), false);
    344 }
    345 
    346 void WallpaperManager::PendingWallpaper::ResetLoadWallpaper(
    347     const WallpaperInfo& info) {
    348   SetMode(gfx::ImageSkia(), info, base::FilePath(), false);
    349 }
    350 
    351 void WallpaperManager::PendingWallpaper::ResetSetCustomWallpaper(
    352     const WallpaperInfo& info,
    353     const base::FilePath& wallpaper_path) {
    354   SetMode(gfx::ImageSkia(), info, wallpaper_path, false);
    355 }
    356 
    357 void WallpaperManager::PendingWallpaper::ResetSetDefaultWallpaper() {
    358   SetMode(gfx::ImageSkia(), WallpaperInfo(), base::FilePath(), true);
    359 }
    360 
    361 void WallpaperManager::PendingWallpaper::SetMode(
    362     const gfx::ImageSkia& image,
    363     const WallpaperInfo& info,
    364     const base::FilePath& wallpaper_path,
    365     const bool is_default) {
    366   user_wallpaper_ = image;
    367   info_ = info;
    368   wallpaper_path_ = wallpaper_path;
    369   default_ = is_default;
    370 }
    371 
    372 void WallpaperManager::PendingWallpaper::ProcessRequest() {
    373   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    374 
    375   timer.Stop();  // Erase reference to self.
    376 
    377   WallpaperManager* manager = WallpaperManager::Get();
    378   if (manager->pending_inactive_ == this)
    379     manager->pending_inactive_ = NULL;
    380 
    381   started_load_at_ = base::Time::Now();
    382 
    383   if (default_) {
    384     manager->DoSetDefaultWallpaper(user_id_, on_finish_.Pass());
    385   } else if (!user_wallpaper_.isNull()) {
    386     ash::Shell::GetInstance()
    387         ->desktop_background_controller()
    388         ->SetWallpaperImage(user_wallpaper_, info_.layout);
    389   } else if (!wallpaper_path_.empty()) {
    390     manager->task_runner_->PostTask(
    391         FROM_HERE,
    392         base::Bind(&WallpaperManager::GetCustomWallpaperInternal,
    393                    user_id_,
    394                    info_,
    395                    wallpaper_path_,
    396                    true /* update wallpaper */,
    397                    base::Passed(on_finish_.Pass()),
    398                    manager->weak_factory_.GetWeakPtr()));
    399   } else if (!info_.location.empty()) {
    400     manager->LoadWallpaper(user_id_, info_, true, on_finish_.Pass());
    401   } else {
    402     // PendingWallpaper was created and never initialized?
    403     NOTREACHED();
    404     // Error. Do not record time.
    405     started_load_at_ = base::Time();
    406   }
    407   on_finish_.reset();
    408 }
    409 
    410 void WallpaperManager::PendingWallpaper::OnWallpaperSet() {
    411   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    412 
    413   // The only known case for this check to fail is global destruction during
    414   // wallpaper load. It should never happen.
    415   if (!BrowserThread::CurrentlyOn(BrowserThread::UI))
    416     return; // We are in a process of global destruction.
    417 
    418   timer.Stop();  // Erase reference to self.
    419 
    420   WallpaperManager* manager = WallpaperManager::Get();
    421   if (!started_load_at_.is_null()) {
    422     const base::TimeDelta elapsed = base::Time::Now() - started_load_at_;
    423     manager->SaveLastLoadTime(elapsed);
    424   }
    425   if (manager->pending_inactive_ == this) {
    426     // ProcessRequest() was never executed.
    427     manager->pending_inactive_ = NULL;
    428   }
    429 
    430   // Destroy self.
    431   manager->RemovePendingWallpaperFromList(this);
    432 }
    433 
    434 // WallpaperManager, public: ---------------------------------------------------
    435 
    436 // TestApi. For testing purpose
    437 WallpaperManager::TestApi::TestApi(WallpaperManager* wallpaper_manager)
    438     : wallpaper_manager_(wallpaper_manager) {
    439 }
    440 
    441 WallpaperManager::TestApi::~TestApi() {
    442 }
    443 
    444 base::FilePath WallpaperManager::TestApi::current_wallpaper_path() {
    445   return wallpaper_manager_->current_wallpaper_path_;
    446 }
    447 
    448 bool WallpaperManager::TestApi::GetWallpaperFromCache(
    449     const std::string& user_id, gfx::ImageSkia* image) {
    450   return wallpaper_manager_->GetWallpaperFromCache(user_id, image);
    451 }
    452 
    453 void WallpaperManager::TestApi::SetWallpaperCache(const std::string& user_id,
    454                                                   const gfx::ImageSkia& image) {
    455   DCHECK(!image.isNull());
    456   wallpaper_manager_->wallpaper_cache_[user_id] = image;
    457 }
    458 
    459 void WallpaperManager::TestApi::ClearDisposableWallpaperCache() {
    460   wallpaper_manager_->ClearDisposableWallpaperCache();
    461 }
    462 
    463 // static
    464 WallpaperManager* WallpaperManager::Get() {
    465   if (!g_wallpaper_manager)
    466     g_wallpaper_manager = new WallpaperManager();
    467   return g_wallpaper_manager;
    468 }
    469 
    470 WallpaperManager::WallpaperManager()
    471     : loaded_wallpapers_(0),
    472       command_line_for_testing_(NULL),
    473       should_cache_wallpaper_(false),
    474       pending_inactive_(NULL),
    475       weak_factory_(this) {
    476   SetDefaultWallpaperPathsFromCommandLine(
    477       base::CommandLine::ForCurrentProcess());
    478   registrar_.Add(this,
    479                  chrome::NOTIFICATION_LOGIN_USER_CHANGED,
    480                  content::NotificationService::AllSources());
    481   registrar_.Add(this,
    482                  chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
    483                  content::NotificationService::AllSources());
    484   registrar_.Add(this,
    485                  chrome::NOTIFICATION_WALLPAPER_ANIMATION_FINISHED,
    486                  content::NotificationService::AllSources());
    487   sequence_token_ = BrowserThread::GetBlockingPool()->
    488       GetNamedSequenceToken(kWallpaperSequenceTokenName);
    489   task_runner_ = BrowserThread::GetBlockingPool()->
    490       GetSequencedTaskRunnerWithShutdownBehavior(
    491           sequence_token_,
    492           base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
    493   wallpaper_loader_ = new UserImageLoader(ImageDecoder::ROBUST_JPEG_CODEC,
    494                                           task_runner_);
    495 }
    496 
    497 WallpaperManager::~WallpaperManager() {
    498   // TODO(bshe): Lifetime of WallpaperManager needs more consideration.
    499   // http://crbug.com/171694
    500   DCHECK(!show_user_name_on_signin_subscription_);
    501 
    502   ClearObsoleteWallpaperPrefs();
    503   weak_factory_.InvalidateWeakPtrs();
    504 }
    505 
    506 void WallpaperManager::Shutdown() {
    507   show_user_name_on_signin_subscription_.reset();
    508 }
    509 
    510 // static
    511 void WallpaperManager::RegisterPrefs(PrefRegistrySimple* registry) {
    512   registry->RegisterDictionaryPref(prefs::kUsersWallpaperInfo);
    513   registry->RegisterDictionaryPref(kUserWallpapers);
    514   registry->RegisterDictionaryPref(kUserWallpapersProperties);
    515 }
    516 
    517 void WallpaperManager::AddObservers() {
    518   show_user_name_on_signin_subscription_ =
    519       CrosSettings::Get()->AddSettingsObserver(
    520           kAccountsPrefShowUserNamesOnSignIn,
    521           base::Bind(&WallpaperManager::InitializeRegisteredDeviceWallpaper,
    522                      weak_factory_.GetWeakPtr()));
    523 }
    524 
    525 void WallpaperManager::EnsureLoggedInUserWallpaperLoaded() {
    526   // Some browser tests do not have a shell instance. As no wallpaper is needed
    527   // in these tests anyway, avoid loading one, preventing crashes and speeding
    528   // up the tests.
    529   if (!ash::Shell::HasInstance())
    530     return;
    531 
    532   WallpaperInfo info;
    533   if (GetLoggedInUserWallpaperInfo(&info)) {
    534     UMA_HISTOGRAM_ENUMERATION("Ash.Wallpaper.Type", info.type,
    535                               user_manager::User::WALLPAPER_TYPE_COUNT);
    536     if (info == current_user_wallpaper_info_)
    537       return;
    538   }
    539   SetUserWallpaperNow(
    540       user_manager::UserManager::Get()->GetLoggedInUser()->email());
    541 }
    542 
    543 void WallpaperManager::ClearDisposableWallpaperCache() {
    544   // Cancel callback for previous cache requests.
    545   weak_factory_.InvalidateWeakPtrs();
    546   // Keep the wallpaper of logged in users in cache at multi-profile mode.
    547   std::set<std::string> logged_in_users_names;
    548   const user_manager::UserList& logged_users =
    549       user_manager::UserManager::Get()->GetLoggedInUsers();
    550   for (user_manager::UserList::const_iterator it = logged_users.begin();
    551        it != logged_users.end();
    552        ++it) {
    553     logged_in_users_names.insert((*it)->email());
    554   }
    555 
    556   CustomWallpaperMap logged_in_users_cache;
    557   for (CustomWallpaperMap::iterator it = wallpaper_cache_.begin();
    558        it != wallpaper_cache_.end(); ++it) {
    559     if (logged_in_users_names.find(it->first) !=
    560         logged_in_users_names.end()) {
    561       logged_in_users_cache.insert(*it);
    562     }
    563   }
    564   wallpaper_cache_ = logged_in_users_cache;
    565 }
    566 
    567 bool WallpaperManager::GetLoggedInUserWallpaperInfo(WallpaperInfo* info) {
    568   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    569 
    570   if (user_manager::UserManager::Get()->IsLoggedInAsStub()) {
    571     info->location = current_user_wallpaper_info_.location = "";
    572     info->layout = current_user_wallpaper_info_.layout =
    573         ash::WALLPAPER_LAYOUT_CENTER_CROPPED;
    574     info->type = current_user_wallpaper_info_.type =
    575         user_manager::User::DEFAULT;
    576     info->date = current_user_wallpaper_info_.date =
    577         base::Time::Now().LocalMidnight();
    578     return true;
    579   }
    580 
    581   return GetUserWallpaperInfo(
    582       user_manager::UserManager::Get()->GetLoggedInUser()->email(), info);
    583 }
    584 
    585 void WallpaperManager::InitializeWallpaper() {
    586   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    587   user_manager::UserManager* user_manager = user_manager::UserManager::Get();
    588 
    589   // Apply device customization.
    590   if (ShouldUseCustomizedDefaultWallpaper()) {
    591     SetDefaultWallpaperPath(
    592         GetCustomizedWallpaperDefaultRescaledFileName(kSmallWallpaperSuffix),
    593         scoped_ptr<gfx::ImageSkia>().Pass(),
    594         GetCustomizedWallpaperDefaultRescaledFileName(kLargeWallpaperSuffix),
    595         scoped_ptr<gfx::ImageSkia>().Pass());
    596   }
    597 
    598   CommandLine* command_line = GetCommandLine();
    599   if (command_line->HasSwitch(chromeos::switches::kGuestSession)) {
    600     // Guest wallpaper should be initialized when guest login.
    601     // Note: This maybe called before login. So IsLoggedInAsGuest can not be
    602     // used here to determine if current user is guest.
    603     return;
    604   }
    605 
    606   if (command_line->HasSwitch(::switches::kTestType))
    607     WizardController::SetZeroDelays();
    608 
    609   // Zero delays is also set in autotests.
    610   if (WizardController::IsZeroDelayEnabled()) {
    611     // Ensure tests have some sort of wallpaper.
    612     ash::Shell::GetInstance()->desktop_background_controller()->
    613         CreateEmptyWallpaper();
    614     return;
    615   }
    616 
    617   if (!user_manager->IsUserLoggedIn()) {
    618     if (!StartupUtils::IsDeviceRegistered())
    619       SetDefaultWallpaperDelayed(chromeos::login::kSignInUser);
    620     else
    621       InitializeRegisteredDeviceWallpaper();
    622     return;
    623   }
    624   SetUserWallpaperDelayed(user_manager->GetLoggedInUser()->email());
    625 }
    626 
    627 void WallpaperManager::Observe(int type,
    628                                const content::NotificationSource& source,
    629                                const content::NotificationDetails& details) {
    630   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    631   switch (type) {
    632     case chrome::NOTIFICATION_LOGIN_USER_CHANGED: {
    633       ClearDisposableWallpaperCache();
    634       BrowserThread::PostDelayedTask(
    635           BrowserThread::UI,
    636           FROM_HERE,
    637           base::Bind(&WallpaperManager::MoveLoggedInUserCustomWallpaper,
    638                      weak_factory_.GetWeakPtr()),
    639           base::TimeDelta::FromSeconds(kMoveCustomWallpaperDelaySeconds));
    640       break;
    641     }
    642     case chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE: {
    643       if (!GetCommandLine()->HasSwitch(switches::kDisableBootAnimation)) {
    644         BrowserThread::PostDelayedTask(
    645             BrowserThread::UI, FROM_HERE,
    646             base::Bind(&WallpaperManager::CacheUsersWallpapers,
    647                        weak_factory_.GetWeakPtr()),
    648             base::TimeDelta::FromMilliseconds(kCacheWallpaperDelayMs));
    649       } else {
    650         should_cache_wallpaper_ = true;
    651       }
    652       break;
    653     }
    654     case chrome::NOTIFICATION_WALLPAPER_ANIMATION_FINISHED: {
    655       NotifyAnimationFinished();
    656       if (should_cache_wallpaper_) {
    657         BrowserThread::PostDelayedTask(
    658             BrowserThread::UI, FROM_HERE,
    659             base::Bind(&WallpaperManager::CacheUsersWallpapers,
    660                        weak_factory_.GetWeakPtr()),
    661             base::TimeDelta::FromMilliseconds(kCacheWallpaperDelayMs));
    662         should_cache_wallpaper_ = false;
    663       }
    664       break;
    665     }
    666     default:
    667       NOTREACHED() << "Unexpected notification " << type;
    668   }
    669 }
    670 
    671 void WallpaperManager::RemoveUserWallpaperInfo(const std::string& user_id) {
    672   WallpaperInfo info;
    673   GetUserWallpaperInfo(user_id, &info);
    674   PrefService* prefs = g_browser_process->local_state();
    675   DictionaryPrefUpdate prefs_wallpapers_info_update(prefs,
    676       prefs::kUsersWallpaperInfo);
    677   prefs_wallpapers_info_update->RemoveWithoutPathExpansion(user_id, NULL);
    678   DeleteUserWallpapers(user_id, info.location);
    679 }
    680 
    681 // static
    682 bool WallpaperManager::ResizeImage(const gfx::ImageSkia& image,
    683                                    ash::WallpaperLayout layout,
    684                                    int preferred_width,
    685                                    int preferred_height,
    686                                    scoped_refptr<base::RefCountedBytes>* output,
    687                                    gfx::ImageSkia* output_skia) {
    688   DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
    689   int width = image.width();
    690   int height = image.height();
    691   int resized_width;
    692   int resized_height;
    693   *output = new base::RefCountedBytes();
    694 
    695   if (layout == ash::WALLPAPER_LAYOUT_CENTER_CROPPED) {
    696     // Do not resize custom wallpaper if it is smaller than preferred size.
    697     if (!(width > preferred_width && height > preferred_height))
    698       return false;
    699 
    700     double horizontal_ratio = static_cast<double>(preferred_width) / width;
    701     double vertical_ratio = static_cast<double>(preferred_height) / height;
    702     if (vertical_ratio > horizontal_ratio) {
    703       resized_width =
    704           RoundPositive(static_cast<double>(width) * vertical_ratio);
    705       resized_height = preferred_height;
    706     } else {
    707       resized_width = preferred_width;
    708       resized_height =
    709           RoundPositive(static_cast<double>(height) * horizontal_ratio);
    710     }
    711   } else if (layout == ash::WALLPAPER_LAYOUT_STRETCH) {
    712     resized_width = preferred_width;
    713     resized_height = preferred_height;
    714   } else {
    715     resized_width = width;
    716     resized_height = height;
    717   }
    718 
    719   gfx::ImageSkia resized_image = gfx::ImageSkiaOperations::CreateResizedImage(
    720       image,
    721       skia::ImageOperations::RESIZE_LANCZOS3,
    722       gfx::Size(resized_width, resized_height));
    723 
    724   SkBitmap bitmap = *(resized_image.bitmap());
    725   SkAutoLockPixels lock_input(bitmap);
    726   gfx::JPEGCodec::Encode(
    727       reinterpret_cast<unsigned char*>(bitmap.getAddr32(0, 0)),
    728       gfx::JPEGCodec::FORMAT_SkBitmap,
    729       bitmap.width(),
    730       bitmap.height(),
    731       bitmap.width() * bitmap.bytesPerPixel(),
    732       kDefaultEncodingQuality,
    733       &(*output)->data());
    734 
    735   if (output_skia) {
    736     resized_image.MakeThreadSafe();
    737     *output_skia = resized_image;
    738   }
    739 
    740   return true;
    741 }
    742 
    743 // static
    744 bool WallpaperManager::ResizeAndSaveWallpaper(const gfx::ImageSkia& image,
    745                                               const base::FilePath& path,
    746                                               ash::WallpaperLayout layout,
    747                                               int preferred_width,
    748                                               int preferred_height,
    749                                               gfx::ImageSkia* output_skia) {
    750   if (layout == ash::WALLPAPER_LAYOUT_CENTER) {
    751     // TODO(bshe): Generates cropped custom wallpaper for CENTER layout.
    752     if (base::PathExists(path))
    753       base::DeleteFile(path, false);
    754     return false;
    755   }
    756   scoped_refptr<base::RefCountedBytes> data;
    757   if (ResizeImage(image,
    758                   layout,
    759                   preferred_width,
    760                   preferred_height,
    761                   &data,
    762                   output_skia)) {
    763     return SaveWallpaperInternal(
    764         path, reinterpret_cast<const char*>(data->front()), data->size());
    765   }
    766   return false;
    767 }
    768 
    769 bool WallpaperManager::IsPolicyControlled(const std::string& user_id) const {
    770   chromeos::WallpaperInfo info;
    771   if (!GetUserWallpaperInfo(user_id, &info))
    772     return false;
    773   return info.type == user_manager::User::POLICY;
    774 }
    775 
    776 void WallpaperManager::OnPolicySet(const std::string& policy,
    777                                    const std::string& user_id) {
    778   WallpaperInfo info;
    779   GetUserWallpaperInfo(user_id, &info);
    780   info.type = user_manager::User::POLICY;
    781   SetUserWallpaperInfo(user_id, info, true /* is_persistent */);
    782 }
    783 
    784 void WallpaperManager::OnPolicyCleared(const std::string& policy,
    785                                        const std::string& user_id) {
    786   WallpaperInfo info;
    787   GetUserWallpaperInfo(user_id, &info);
    788   info.type = user_manager::User::DEFAULT;
    789   SetUserWallpaperInfo(user_id, info, true /* is_persistent */);
    790   SetDefaultWallpaperNow(user_id);
    791 }
    792 
    793 void WallpaperManager::OnPolicyFetched(const std::string& policy,
    794                                        const std::string& user_id,
    795                                        scoped_ptr<std::string> data) {
    796   if (!data)
    797     return;
    798 
    799   wallpaper_loader_->Start(
    800       data.Pass(),
    801       0,  // Do not crop.
    802       base::Bind(&WallpaperManager::SetPolicyControlledWallpaper,
    803                  weak_factory_.GetWeakPtr(),
    804                  user_id));
    805 }
    806 
    807 // static
    808 WallpaperManager::WallpaperResolution
    809 WallpaperManager::GetAppropriateResolution() {
    810   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    811   gfx::Size size =
    812       ash::DesktopBackgroundController::GetMaxDisplaySizeInNative();
    813   return (size.width() > kSmallWallpaperMaxWidth ||
    814           size.height() > kSmallWallpaperMaxHeight)
    815              ? WALLPAPER_RESOLUTION_LARGE
    816              : WALLPAPER_RESOLUTION_SMALL;
    817 }
    818 
    819 // static
    820 base::FilePath WallpaperManager::GetCustomWallpaperPath(
    821     const char* sub_dir,
    822     const std::string& user_id_hash,
    823     const std::string& file) {
    824   base::FilePath custom_wallpaper_path = GetCustomWallpaperDir(sub_dir);
    825   return custom_wallpaper_path.Append(user_id_hash).Append(file);
    826 }
    827 
    828 void WallpaperManager::SetPolicyControlledWallpaper(
    829     const std::string& user_id,
    830     const user_manager::UserImage& user_image) {
    831   const user_manager::User* user =
    832       user_manager::UserManager::Get()->FindUser(user_id);
    833   if (!user) {
    834     NOTREACHED() << "Unknown user.";
    835     return;
    836   }
    837 
    838   if (user->username_hash().empty()) {
    839     cryptohome::AsyncMethodCaller::GetInstance()->AsyncGetSanitizedUsername(
    840         user_id,
    841         base::Bind(&WallpaperManager::SetCustomWallpaperOnSanitizedUsername,
    842                    weak_factory_.GetWeakPtr(),
    843                    user_id,
    844                    user_image.image(),
    845                    true /* update wallpaper */));
    846   } else {
    847     SetCustomWallpaper(user_id,
    848                        user->username_hash(),
    849                        "policy-controlled.jpeg",
    850                        ash::WALLPAPER_LAYOUT_CENTER_CROPPED,
    851                        user_manager::User::POLICY,
    852                        user_image.image(),
    853                        true /* update wallpaper */);
    854   }
    855 }
    856 
    857 void WallpaperManager::SetCustomWallpaperOnSanitizedUsername(
    858     const std::string& user_id,
    859     const gfx::ImageSkia& image,
    860     bool update_wallpaper,
    861     bool cryptohome_success,
    862     const std::string& user_id_hash) {
    863   if (!cryptohome_success)
    864     return;
    865   SetCustomWallpaper(user_id,
    866                      user_id_hash,
    867                      "policy-controlled.jpeg",
    868                      ash::WALLPAPER_LAYOUT_CENTER_CROPPED,
    869                      user_manager::User::POLICY,
    870                      image,
    871                      update_wallpaper);
    872 }
    873 
    874 void WallpaperManager::SetCustomWallpaper(
    875     const std::string& user_id,
    876     const std::string& user_id_hash,
    877     const std::string& file,
    878     ash::WallpaperLayout layout,
    879     user_manager::User::WallpaperType type,
    880     const gfx::ImageSkia& image,
    881     bool update_wallpaper) {
    882   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    883 
    884   // There is no visible background in kiosk mode.
    885   if (user_manager::UserManager::Get()->IsLoggedInAsKioskApp())
    886     return;
    887 
    888   // Don't allow custom wallpapers while policy is in effect.
    889   if (type != user_manager::User::POLICY && IsPolicyControlled(user_id))
    890     return;
    891 
    892   base::FilePath wallpaper_path =
    893       GetCustomWallpaperPath(kOriginalWallpaperSubDir, user_id_hash, file);
    894 
    895   // If decoded wallpaper is empty, we have probably failed to decode the file.
    896   // Use default wallpaper in this case.
    897   if (image.isNull()) {
    898     SetDefaultWallpaperDelayed(user_id);
    899     return;
    900   }
    901 
    902   const user_manager::User* user =
    903       user_manager::UserManager::Get()->FindUser(user_id);
    904   CHECK(user);
    905   bool is_persistent =
    906       !user_manager::UserManager::Get()->IsUserNonCryptohomeDataEphemeral(
    907           user_id) ||
    908       (type == user_manager::User::POLICY &&
    909        user->GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT);
    910 
    911   WallpaperInfo wallpaper_info = {
    912       wallpaper_path.value(),
    913       layout,
    914       type,
    915       // Date field is not used.
    916       base::Time::Now().LocalMidnight()
    917   };
    918   if (is_persistent) {
    919     image.EnsureRepsForSupportedScales();
    920     scoped_ptr<gfx::ImageSkia> deep_copy(image.DeepCopy());
    921     // Block shutdown on this task. Otherwise, we may lose the custom wallpaper
    922     // that the user selected.
    923     scoped_refptr<base::SequencedTaskRunner> blocking_task_runner =
    924         BrowserThread::GetBlockingPool()
    925             ->GetSequencedTaskRunnerWithShutdownBehavior(
    926                 sequence_token_, base::SequencedWorkerPool::BLOCK_SHUTDOWN);
    927     // TODO(bshe): This may break if RawImage becomes RefCountedMemory.
    928     blocking_task_runner->PostTask(
    929         FROM_HERE,
    930         base::Bind(&WallpaperManager::SaveCustomWallpaper,
    931                    user_id_hash,
    932                    base::FilePath(wallpaper_info.location),
    933                    wallpaper_info.layout,
    934                    base::Passed(deep_copy.Pass())));
    935   }
    936 
    937   std::string relative_path = base::FilePath(user_id_hash).Append(file).value();
    938   // User's custom wallpaper path is determined by relative path and the
    939   // appropriate wallpaper resolution in GetCustomWallpaperInternal.
    940   WallpaperInfo info = {
    941       relative_path,
    942       layout,
    943       type,
    944       base::Time::Now().LocalMidnight()
    945   };
    946   SetUserWallpaperInfo(user_id, info, is_persistent);
    947   if (update_wallpaper) {
    948     GetPendingWallpaper(user_id, false)->ResetSetWallpaperImage(image, info);
    949   }
    950 
    951   wallpaper_cache_[user_id] = image;
    952 }
    953 
    954 void WallpaperManager::SetDefaultWallpaperNow(const std::string& user_id) {
    955   GetPendingWallpaper(user_id, false)->ResetSetDefaultWallpaper();
    956 }
    957 
    958 void WallpaperManager::SetDefaultWallpaperDelayed(const std::string& user_id) {
    959   GetPendingWallpaper(user_id, true)->ResetSetDefaultWallpaper();
    960 }
    961 
    962 void WallpaperManager::DoSetDefaultWallpaper(
    963     const std::string& user_id,
    964     MovableOnDestroyCallbackHolder on_finish) {
    965   // There is no visible background in kiosk mode.
    966   if (user_manager::UserManager::Get()->IsLoggedInAsKioskApp())
    967     return;
    968   current_wallpaper_path_.clear();
    969   wallpaper_cache_.erase(user_id);
    970   // Some browser tests do not have a shell instance. As no wallpaper is needed
    971   // in these tests anyway, avoid loading one, preventing crashes and speeding
    972   // up the tests.
    973   if (!ash::Shell::HasInstance())
    974     return;
    975 
    976   WallpaperResolution resolution = GetAppropriateResolution();
    977   const bool use_small = (resolution == WALLPAPER_RESOLUTION_SMALL);
    978 
    979   const base::FilePath* file = NULL;
    980 
    981   if (user_manager::UserManager::Get()->IsLoggedInAsGuest()) {
    982     file =
    983         use_small ? &guest_small_wallpaper_file_ : &guest_large_wallpaper_file_;
    984   } else {
    985     file = use_small ? &default_small_wallpaper_file_
    986                      : &default_large_wallpaper_file_;
    987   }
    988   ash::WallpaperLayout layout = use_small
    989                                     ? ash::WALLPAPER_LAYOUT_CENTER
    990                                     : ash::WALLPAPER_LAYOUT_CENTER_CROPPED;
    991   DCHECK(file);
    992   if (!default_wallpaper_image_.get() ||
    993       default_wallpaper_image_->file_path() != file->value()) {
    994     default_wallpaper_image_.reset();
    995     if (!file->empty()) {
    996       loaded_wallpapers_++;
    997       StartLoadAndSetDefaultWallpaper(
    998           *file, layout, on_finish.Pass(), &default_wallpaper_image_);
    999       return;
   1000     }
   1001 
   1002     CreateSolidDefaultWallpaper();
   1003   }
   1004   // 1x1 wallpaper is actually solid color, so it should be stretched.
   1005   if (default_wallpaper_image_->image().width() == 1 &&
   1006       default_wallpaper_image_->image().height() == 1)
   1007     layout = ash::WALLPAPER_LAYOUT_STRETCH;
   1008 
   1009   ash::Shell::GetInstance()->desktop_background_controller()->SetWallpaperImage(
   1010       default_wallpaper_image_->image(), layout);
   1011 }
   1012 
   1013 // static
   1014 void WallpaperManager::SaveCustomWallpaper(const std::string& user_id_hash,
   1015                                            const base::FilePath& original_path,
   1016                                            ash::WallpaperLayout layout,
   1017                                            scoped_ptr<gfx::ImageSkia> image) {
   1018   base::DeleteFile(
   1019       GetCustomWallpaperDir(kOriginalWallpaperSubDir).Append(user_id_hash),
   1020       true /* recursive */);
   1021   base::DeleteFile(
   1022       GetCustomWallpaperDir(kSmallWallpaperSubDir).Append(user_id_hash),
   1023       true /* recursive */);
   1024   base::DeleteFile(
   1025       GetCustomWallpaperDir(kLargeWallpaperSubDir).Append(user_id_hash),
   1026       true /* recursive */);
   1027   EnsureCustomWallpaperDirectories(user_id_hash);
   1028   std::string file_name = original_path.BaseName().value();
   1029   base::FilePath small_wallpaper_path =
   1030       GetCustomWallpaperPath(kSmallWallpaperSubDir, user_id_hash, file_name);
   1031   base::FilePath large_wallpaper_path =
   1032       GetCustomWallpaperPath(kLargeWallpaperSubDir, user_id_hash, file_name);
   1033 
   1034   // Re-encode orginal file to jpeg format and saves the result in case that
   1035   // resized wallpaper is not generated (i.e. chrome shutdown before resized
   1036   // wallpaper is saved).
   1037   ResizeAndSaveWallpaper(*image,
   1038                          original_path,
   1039                          ash::WALLPAPER_LAYOUT_STRETCH,
   1040                          image->width(),
   1041                          image->height(),
   1042                          NULL);
   1043   ResizeAndSaveWallpaper(*image,
   1044                          small_wallpaper_path,
   1045                          layout,
   1046                          kSmallWallpaperMaxWidth,
   1047                          kSmallWallpaperMaxHeight,
   1048                          NULL);
   1049   ResizeAndSaveWallpaper(*image,
   1050                          large_wallpaper_path,
   1051                          layout,
   1052                          kLargeWallpaperMaxWidth,
   1053                          kLargeWallpaperMaxHeight,
   1054                          NULL);
   1055 }
   1056 
   1057 // static
   1058 void WallpaperManager::MoveCustomWallpapersOnWorker(
   1059     const std::string& user_id,
   1060     const std::string& user_id_hash,
   1061     base::WeakPtr<WallpaperManager> weak_ptr) {
   1062 
   1063   if (MoveCustomWallpaperDirectory(
   1064           kOriginalWallpaperSubDir, user_id, user_id_hash)) {
   1065     // Consider success if the original wallpaper is moved to the new directory.
   1066     // Original wallpaper is the fallback if the correct resolution wallpaper
   1067     // can not be found.
   1068     BrowserThread::PostTask(
   1069         BrowserThread::UI,
   1070         FROM_HERE,
   1071         base::Bind(&WallpaperManager::MoveCustomWallpapersSuccess,
   1072                    weak_ptr,
   1073                    user_id,
   1074                    user_id_hash));
   1075   }
   1076   MoveCustomWallpaperDirectory(kLargeWallpaperSubDir, user_id, user_id_hash);
   1077   MoveCustomWallpaperDirectory(kSmallWallpaperSubDir, user_id, user_id_hash);
   1078   MoveCustomWallpaperDirectory(
   1079       kThumbnailWallpaperSubDir, user_id, user_id_hash);
   1080 }
   1081 
   1082 // static
   1083 void WallpaperManager::GetCustomWallpaperInternal(
   1084     const std::string& user_id,
   1085     const WallpaperInfo& info,
   1086     const base::FilePath& wallpaper_path,
   1087     bool update_wallpaper,
   1088     MovableOnDestroyCallbackHolder on_finish,
   1089     base::WeakPtr<WallpaperManager> weak_ptr) {
   1090 
   1091   base::FilePath valid_path = wallpaper_path;
   1092   if (!base::PathExists(wallpaper_path)) {
   1093     // Falls back on original file if the correct resolution file does not
   1094     // exist. This may happen when the original custom wallpaper is small or
   1095     // browser shutdown before resized wallpaper saved.
   1096     valid_path = GetCustomWallpaperDir(kOriginalWallpaperSubDir);
   1097     valid_path = valid_path.Append(info.location);
   1098   }
   1099 
   1100   if (!base::PathExists(valid_path)) {
   1101     // Falls back to custom wallpaper that uses email as part of its file path.
   1102     // Note that email is used instead of user_id_hash here.
   1103     valid_path = GetCustomWallpaperPath(kOriginalWallpaperSubDir,
   1104                                         user_id, info.location);
   1105   }
   1106 
   1107   if (!base::PathExists(valid_path)) {
   1108     LOG(ERROR) << "Failed to load previously selected custom wallpaper. " <<
   1109                   "Fallback to default wallpaper";
   1110     BrowserThread::PostTask(BrowserThread::UI,
   1111                             FROM_HERE,
   1112                             base::Bind(&WallpaperManager::DoSetDefaultWallpaper,
   1113                                        weak_ptr,
   1114                                        user_id,
   1115                                        base::Passed(on_finish.Pass())));
   1116   } else {
   1117     BrowserThread::PostTask(BrowserThread::UI,
   1118                             FROM_HERE,
   1119                             base::Bind(&WallpaperManager::StartLoad,
   1120                                        weak_ptr,
   1121                                        user_id,
   1122                                        info,
   1123                                        update_wallpaper,
   1124                                        valid_path,
   1125                                        base::Passed(on_finish.Pass())));
   1126   }
   1127 }
   1128 
   1129 void WallpaperManager::InitInitialUserWallpaper(const std::string& user_id,
   1130                                                 bool is_persistent) {
   1131   current_user_wallpaper_info_.location = "";
   1132   current_user_wallpaper_info_.layout = ash::WALLPAPER_LAYOUT_CENTER_CROPPED;
   1133   current_user_wallpaper_info_.type = user_manager::User::DEFAULT;
   1134   current_user_wallpaper_info_.date = base::Time::Now().LocalMidnight();
   1135 
   1136   WallpaperInfo info = current_user_wallpaper_info_;
   1137   SetUserWallpaperInfo(user_id, info, is_persistent);
   1138 }
   1139 
   1140 void WallpaperManager::SetUserWallpaperInfo(const std::string& user_id,
   1141                                             const WallpaperInfo& info,
   1142                                             bool is_persistent) {
   1143   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   1144   current_user_wallpaper_info_ = info;
   1145   if (!is_persistent)
   1146     return;
   1147 
   1148   PrefService* local_state = g_browser_process->local_state();
   1149   DictionaryPrefUpdate wallpaper_update(local_state,
   1150                                         prefs::kUsersWallpaperInfo);
   1151 
   1152   base::DictionaryValue* wallpaper_info_dict = new base::DictionaryValue();
   1153   wallpaper_info_dict->SetString(kNewWallpaperDateNodeName,
   1154       base::Int64ToString(info.date.ToInternalValue()));
   1155   wallpaper_info_dict->SetString(kNewWallpaperLocationNodeName, info.location);
   1156   wallpaper_info_dict->SetInteger(kNewWallpaperLayoutNodeName, info.layout);
   1157   wallpaper_info_dict->SetInteger(kNewWallpaperTypeNodeName, info.type);
   1158   wallpaper_update->SetWithoutPathExpansion(user_id, wallpaper_info_dict);
   1159 }
   1160 
   1161 void WallpaperManager::SetUserWallpaperDelayed(const std::string& user_id) {
   1162   ScheduleSetUserWallpaper(user_id, true);
   1163 }
   1164 
   1165 void WallpaperManager::SetUserWallpaperNow(const std::string& user_id) {
   1166   ScheduleSetUserWallpaper(user_id, false);
   1167 }
   1168 
   1169 void WallpaperManager::ScheduleSetUserWallpaper(const std::string& user_id,
   1170                                                 bool delayed) {
   1171   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   1172   // Some unit tests come here without a UserManager or without a pref system.
   1173   if (!user_manager::UserManager::IsInitialized() ||
   1174       !g_browser_process->local_state()) {
   1175     return;
   1176   }
   1177 
   1178   // There is no visible background in kiosk mode.
   1179   if (user_manager::UserManager::Get()->IsLoggedInAsKioskApp())
   1180     return;
   1181   // Guest user, regular user in ephemeral mode, or kiosk app.
   1182   const user_manager::User* user =
   1183       user_manager::UserManager::Get()->FindUser(user_id);
   1184   if (user_manager::UserManager::Get()->IsUserNonCryptohomeDataEphemeral(
   1185           user_id) ||
   1186       (user != NULL && user->GetType() == user_manager::USER_TYPE_KIOSK_APP)) {
   1187     InitInitialUserWallpaper(user_id, false);
   1188     GetPendingWallpaper(user_id, delayed)->ResetSetDefaultWallpaper();
   1189     return;
   1190   }
   1191 
   1192   if (!user_manager::UserManager::Get()->IsKnownUser(user_id))
   1193     return;
   1194 
   1195   last_selected_user_ = user_id;
   1196 
   1197   WallpaperInfo info;
   1198 
   1199   if (!GetUserWallpaperInfo(user_id, &info)) {
   1200     InitInitialUserWallpaper(user_id, true);
   1201     GetUserWallpaperInfo(user_id, &info);
   1202   }
   1203 
   1204   gfx::ImageSkia user_wallpaper;
   1205   current_user_wallpaper_info_ = info;
   1206   if (GetWallpaperFromCache(user_id, &user_wallpaper)) {
   1207     GetPendingWallpaper(user_id, delayed)
   1208         ->ResetSetWallpaperImage(user_wallpaper, info);
   1209   } else {
   1210     if (info.location.empty()) {
   1211       // Uses default built-in wallpaper when file is empty. Eventually, we
   1212       // will only ship one built-in wallpaper in ChromeOS image.
   1213       GetPendingWallpaper(user_id, delayed)->ResetSetDefaultWallpaper();
   1214       return;
   1215     }
   1216 
   1217     if (info.type == user_manager::User::CUSTOMIZED ||
   1218         info.type == user_manager::User::POLICY) {
   1219       const char* sub_dir = GetCustomWallpaperSubdirForCurrentResolution();
   1220       // Wallpaper is not resized when layout is ash::WALLPAPER_LAYOUT_CENTER.
   1221       // Original wallpaper should be used in this case.
   1222       // TODO(bshe): Generates cropped custom wallpaper for CENTER layout.
   1223       if (info.layout == ash::WALLPAPER_LAYOUT_CENTER)
   1224         sub_dir = kOriginalWallpaperSubDir;
   1225       base::FilePath wallpaper_path = GetCustomWallpaperDir(sub_dir);
   1226       wallpaper_path = wallpaper_path.Append(info.location);
   1227       if (current_wallpaper_path_ == wallpaper_path)
   1228         return;
   1229       current_wallpaper_path_ = wallpaper_path;
   1230       loaded_wallpapers_++;
   1231 
   1232       GetPendingWallpaper(user_id, delayed)
   1233           ->ResetSetCustomWallpaper(info, wallpaper_path);
   1234       return;
   1235     }
   1236 
   1237     // Load downloaded ONLINE or converted DEFAULT wallpapers.
   1238     GetPendingWallpaper(user_id, delayed)->ResetLoadWallpaper(info);
   1239   }
   1240 }
   1241 
   1242 void WallpaperManager::SetWallpaperFromImageSkia(const std::string& user_id,
   1243                                                  const gfx::ImageSkia& image,
   1244                                                  ash::WallpaperLayout layout,
   1245                                                  bool update_wallpaper) {
   1246   DCHECK(user_manager::UserManager::Get()->IsUserLoggedIn());
   1247 
   1248   // There is no visible background in kiosk mode.
   1249   if (user_manager::UserManager::Get()->IsLoggedInAsKioskApp())
   1250     return;
   1251   WallpaperInfo info;
   1252   info.layout = layout;
   1253   wallpaper_cache_[user_id] = image;
   1254 
   1255   if (update_wallpaper) {
   1256     GetPendingWallpaper(last_selected_user_, false /* Not delayed */)
   1257         ->ResetSetWallpaperImage(image, info);
   1258   }
   1259 }
   1260 
   1261 void WallpaperManager::UpdateWallpaper(bool clear_cache) {
   1262   FOR_EACH_OBSERVER(Observer, observers_, OnUpdateWallpaperForTesting());
   1263   if (clear_cache)
   1264     wallpaper_cache_.clear();
   1265   current_wallpaper_path_.clear();
   1266   // For GAIA login flow, the last_selected_user_ may not be set before user
   1267   // login. If UpdateWallpaper is called at GAIA login screen, no wallpaper will
   1268   // be set. It could result a black screen on external monitors.
   1269   // See http://crbug.com/265689 for detail.
   1270   if (last_selected_user_.empty()) {
   1271     SetDefaultWallpaperNow(chromeos::login::kSignInUser);
   1272     return;
   1273   }
   1274   SetUserWallpaperNow(last_selected_user_);
   1275 }
   1276 
   1277 void WallpaperManager::AddObserver(WallpaperManager::Observer* observer) {
   1278   observers_.AddObserver(observer);
   1279 }
   1280 
   1281 void WallpaperManager::RemoveObserver(WallpaperManager::Observer* observer) {
   1282   observers_.RemoveObserver(observer);
   1283 }
   1284 
   1285 void WallpaperManager::NotifyAnimationFinished() {
   1286   FOR_EACH_OBSERVER(
   1287       Observer, observers_, OnWallpaperAnimationFinished(last_selected_user_));
   1288 }
   1289 
   1290 // WallpaperManager, private: --------------------------------------------------
   1291 
   1292 bool WallpaperManager::GetWallpaperFromCache(const std::string& user_id,
   1293                                              gfx::ImageSkia* image) {
   1294   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   1295   CustomWallpaperMap::const_iterator it = wallpaper_cache_.find(user_id);
   1296   if (it != wallpaper_cache_.end()) {
   1297     *image = (*it).second;
   1298     return true;
   1299   }
   1300   return false;
   1301 }
   1302 
   1303 void WallpaperManager::CacheUsersWallpapers() {
   1304   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   1305   user_manager::UserList users = user_manager::UserManager::Get()->GetUsers();
   1306 
   1307   if (!users.empty()) {
   1308     user_manager::UserList::const_iterator it = users.begin();
   1309     // Skip the wallpaper of first user in the list. It should have been cached.
   1310     it++;
   1311     for (int cached = 0;
   1312          it != users.end() && cached < kMaxWallpapersToCache;
   1313          ++it, ++cached) {
   1314       std::string user_id = (*it)->email();
   1315       CacheUserWallpaper(user_id);
   1316     }
   1317   }
   1318 }
   1319 
   1320 void WallpaperManager::CacheUserWallpaper(const std::string& user_id) {
   1321   if (wallpaper_cache_.find(user_id) != wallpaper_cache_.end())
   1322     return;
   1323   WallpaperInfo info;
   1324   if (GetUserWallpaperInfo(user_id, &info)) {
   1325     if (info.location.empty())
   1326       return;
   1327 
   1328     base::FilePath wallpaper_dir;
   1329     base::FilePath wallpaper_path;
   1330     if (info.type == user_manager::User::CUSTOMIZED ||
   1331         info.type == user_manager::User::POLICY) {
   1332       const char* sub_dir = GetCustomWallpaperSubdirForCurrentResolution();
   1333       base::FilePath wallpaper_path = GetCustomWallpaperDir(sub_dir);
   1334       wallpaper_path = wallpaper_path.Append(info.location);
   1335       task_runner_->PostTask(
   1336           FROM_HERE,
   1337           base::Bind(&WallpaperManager::GetCustomWallpaperInternal,
   1338                      user_id,
   1339                      info,
   1340                      wallpaper_path,
   1341                      false /* do not update wallpaper */,
   1342                      base::Passed(MovableOnDestroyCallbackHolder()),
   1343                      weak_factory_.GetWeakPtr()));
   1344       return;
   1345     }
   1346     LoadWallpaper(user_id,
   1347                   info,
   1348                   false /* do not update wallpaper */,
   1349                   MovableOnDestroyCallbackHolder().Pass());
   1350   }
   1351 }
   1352 
   1353 void WallpaperManager::ClearObsoleteWallpaperPrefs() {
   1354   PrefService* prefs = g_browser_process->local_state();
   1355   DictionaryPrefUpdate wallpaper_properties_pref(prefs,
   1356       kUserWallpapersProperties);
   1357   wallpaper_properties_pref->Clear();
   1358   DictionaryPrefUpdate wallpapers_pref(prefs, kUserWallpapers);
   1359   wallpapers_pref->Clear();
   1360 }
   1361 
   1362 void WallpaperManager::DeleteUserWallpapers(const std::string& user_id,
   1363                                             const std::string& path_to_file) {
   1364   std::vector<base::FilePath> file_to_remove;
   1365   // Remove small user wallpaper.
   1366   base::FilePath wallpaper_path =
   1367       GetCustomWallpaperDir(kSmallWallpaperSubDir);
   1368   // Remove old directory if exists
   1369   file_to_remove.push_back(wallpaper_path.Append(user_id));
   1370   wallpaper_path = wallpaper_path.Append(path_to_file).DirName();
   1371   file_to_remove.push_back(wallpaper_path);
   1372 
   1373   // Remove large user wallpaper.
   1374   wallpaper_path = GetCustomWallpaperDir(kLargeWallpaperSubDir);
   1375   file_to_remove.push_back(wallpaper_path.Append(user_id));
   1376   wallpaper_path = wallpaper_path.Append(path_to_file);
   1377   file_to_remove.push_back(wallpaper_path);
   1378 
   1379   // Remove user wallpaper thumbnail.
   1380   wallpaper_path = GetCustomWallpaperDir(kThumbnailWallpaperSubDir);
   1381   file_to_remove.push_back(wallpaper_path.Append(user_id));
   1382   wallpaper_path = wallpaper_path.Append(path_to_file);
   1383   file_to_remove.push_back(wallpaper_path);
   1384 
   1385   // Remove original user wallpaper.
   1386   wallpaper_path = GetCustomWallpaperDir(kOriginalWallpaperSubDir);
   1387   file_to_remove.push_back(wallpaper_path.Append(user_id));
   1388   wallpaper_path = wallpaper_path.Append(path_to_file);
   1389   file_to_remove.push_back(wallpaper_path);
   1390 
   1391   base::WorkerPool::PostTask(
   1392       FROM_HERE,
   1393       base::Bind(&DeleteWallpaperInList, file_to_remove),
   1394       false);
   1395 }
   1396 
   1397 void WallpaperManager::SetCommandLineForTesting(
   1398     base::CommandLine* command_line) {
   1399   command_line_for_testing_ = command_line;
   1400   SetDefaultWallpaperPathsFromCommandLine(command_line);
   1401 }
   1402 
   1403 CommandLine* WallpaperManager::GetCommandLine() {
   1404   CommandLine* command_line = command_line_for_testing_ ?
   1405       command_line_for_testing_ : CommandLine::ForCurrentProcess();
   1406   return command_line;
   1407 }
   1408 
   1409 void WallpaperManager::InitializeRegisteredDeviceWallpaper() {
   1410   if (user_manager::UserManager::Get()->IsUserLoggedIn())
   1411     return;
   1412 
   1413   bool disable_boot_animation =
   1414       GetCommandLine()->HasSwitch(switches::kDisableBootAnimation);
   1415   bool show_users = true;
   1416   bool result = CrosSettings::Get()->GetBoolean(
   1417       kAccountsPrefShowUserNamesOnSignIn, &show_users);
   1418   DCHECK(result) << "Unable to fetch setting "
   1419                  << kAccountsPrefShowUserNamesOnSignIn;
   1420   const user_manager::UserList& users =
   1421       user_manager::UserManager::Get()->GetUsers();
   1422   int public_session_user_index = FindPublicSession(users);
   1423   if ((!show_users && public_session_user_index == -1) || users.empty()) {
   1424     // Boot into sign in form, preload default wallpaper.
   1425     SetDefaultWallpaperDelayed(chromeos::login::kSignInUser);
   1426     return;
   1427   }
   1428 
   1429   if (!disable_boot_animation) {
   1430     int index = public_session_user_index != -1 ? public_session_user_index : 0;
   1431     // Normal boot, load user wallpaper.
   1432     // If normal boot animation is disabled wallpaper would be set
   1433     // asynchronously once user pods are loaded.
   1434     SetUserWallpaperDelayed(users[index]->email());
   1435   }
   1436 }
   1437 
   1438 void WallpaperManager::LoadWallpaper(const std::string& user_id,
   1439                                      const WallpaperInfo& info,
   1440                                      bool update_wallpaper,
   1441                                      MovableOnDestroyCallbackHolder on_finish) {
   1442   base::FilePath wallpaper_dir;
   1443   base::FilePath wallpaper_path;
   1444 
   1445   // Do a sanity check that file path information is not empty.
   1446   if (info.type == user_manager::User::ONLINE ||
   1447       info.type == user_manager::User::DEFAULT) {
   1448     if (info.location.empty()) {
   1449       if (base::SysInfo::IsRunningOnChromeOS()) {
   1450         NOTREACHED() << "User wallpaper info appears to be broken: " << user_id;
   1451       } else {
   1452         // Filename might be empty on debug configurations when stub users
   1453         // were created directly in Local State (for testing). Ignore such
   1454         // errors i.e. allowsuch type of debug configurations on the desktop.
   1455         LOG(WARNING) << "User wallpaper info is empty: " << user_id;
   1456 
   1457         // |on_finish| callback will get called on destruction.
   1458         return;
   1459       }
   1460     }
   1461   }
   1462 
   1463   if (info.type == user_manager::User::ONLINE) {
   1464     std::string file_name = GURL(info.location).ExtractFileName();
   1465     WallpaperResolution resolution = GetAppropriateResolution();
   1466     // Only solid color wallpapers have stretch layout and they have only one
   1467     // resolution.
   1468     if (info.layout != ash::WALLPAPER_LAYOUT_STRETCH &&
   1469         resolution == WALLPAPER_RESOLUTION_SMALL) {
   1470       file_name = base::FilePath(file_name).InsertBeforeExtension(
   1471           kSmallWallpaperSuffix).value();
   1472     }
   1473     CHECK(PathService::Get(chrome::DIR_CHROMEOS_WALLPAPERS, &wallpaper_dir));
   1474     wallpaper_path = wallpaper_dir.Append(file_name);
   1475     if (current_wallpaper_path_ == wallpaper_path)
   1476       return;
   1477 
   1478     if (update_wallpaper)
   1479       current_wallpaper_path_ = wallpaper_path;
   1480 
   1481     loaded_wallpapers_++;
   1482     StartLoad(
   1483         user_id, info, update_wallpaper, wallpaper_path, on_finish.Pass());
   1484   } else if (info.type == user_manager::User::DEFAULT) {
   1485     // Default wallpapers are migrated from M21 user profiles. A code refactor
   1486     // overlooked that case and caused these wallpapers not being loaded at all.
   1487     // On some slow devices, it caused login webui not visible after upgrade to
   1488     // M26 from M21. See crosbug.com/38429 for details.
   1489     base::FilePath user_data_dir;
   1490     PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
   1491     wallpaper_path = user_data_dir.Append(info.location);
   1492     StartLoad(
   1493         user_id, info, update_wallpaper, wallpaper_path, on_finish.Pass());
   1494   } else {
   1495     // In unexpected cases, revert to default wallpaper to fail safely. See
   1496     // crosbug.com/38429.
   1497     LOG(ERROR) << "Wallpaper reverts to default unexpected.";
   1498     DoSetDefaultWallpaper(user_id, on_finish.Pass());
   1499   }
   1500 }
   1501 
   1502 bool WallpaperManager::GetUserWallpaperInfo(const std::string& user_id,
   1503                                             WallpaperInfo* info) const {
   1504   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   1505 
   1506   if (user_manager::UserManager::Get()->IsUserNonCryptohomeDataEphemeral(
   1507           user_id)) {
   1508     // Default to the values cached in memory.
   1509     *info = current_user_wallpaper_info_;
   1510 
   1511     // Ephemeral users do not save anything to local state. But we have got
   1512     // wallpaper info from memory. Returns true.
   1513     return true;
   1514   }
   1515 
   1516   const base::DictionaryValue* info_dict;
   1517   if (!g_browser_process->local_state()->
   1518           GetDictionary(prefs::kUsersWallpaperInfo)->
   1519               GetDictionaryWithoutPathExpansion(user_id, &info_dict)) {
   1520     return false;
   1521   }
   1522 
   1523   // Use temporary variables to keep |info| untouched in the error case.
   1524   std::string location;
   1525   if (!info_dict->GetString(kNewWallpaperLocationNodeName, &location))
   1526     return false;
   1527   int layout;
   1528   if (!info_dict->GetInteger(kNewWallpaperLayoutNodeName, &layout))
   1529     return false;
   1530   int type;
   1531   if (!info_dict->GetInteger(kNewWallpaperTypeNodeName, &type))
   1532     return false;
   1533   std::string date_string;
   1534   if (!info_dict->GetString(kNewWallpaperDateNodeName, &date_string))
   1535     return false;
   1536   int64 date_val;
   1537   if (!base::StringToInt64(date_string, &date_val))
   1538     return false;
   1539 
   1540   info->location = location;
   1541   info->layout = static_cast<ash::WallpaperLayout>(layout);
   1542   info->type = static_cast<user_manager::User::WallpaperType>(type);
   1543   info->date = base::Time::FromInternalValue(date_val);
   1544   return true;
   1545 }
   1546 
   1547 void WallpaperManager::MoveCustomWallpapersSuccess(
   1548     const std::string& user_id,
   1549     const std::string& user_id_hash) {
   1550   WallpaperInfo info;
   1551   GetUserWallpaperInfo(user_id, &info);
   1552   if (info.type == user_manager::User::CUSTOMIZED) {
   1553     // New file field should include user id hash in addition to file name.
   1554     // This is needed because at login screen, user id hash is not available.
   1555     info.location = base::FilePath(user_id_hash).Append(info.location).value();
   1556     bool is_persistent =
   1557         !user_manager::UserManager::Get()->IsUserNonCryptohomeDataEphemeral(
   1558             user_id);
   1559     SetUserWallpaperInfo(user_id, info, is_persistent);
   1560   }
   1561 }
   1562 
   1563 void WallpaperManager::MoveLoggedInUserCustomWallpaper() {
   1564   const user_manager::User* logged_in_user =
   1565       user_manager::UserManager::Get()->GetLoggedInUser();
   1566   if (logged_in_user) {
   1567     task_runner_->PostTask(
   1568         FROM_HERE,
   1569         base::Bind(&WallpaperManager::MoveCustomWallpapersOnWorker,
   1570                    logged_in_user->email(),
   1571                    logged_in_user->username_hash(),
   1572                    weak_factory_.GetWeakPtr()));
   1573   }
   1574 }
   1575 
   1576 void WallpaperManager::OnWallpaperDecoded(
   1577     const std::string& user_id,
   1578     ash::WallpaperLayout layout,
   1579     bool update_wallpaper,
   1580     MovableOnDestroyCallbackHolder on_finish,
   1581     const user_manager::UserImage& user_image) {
   1582   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   1583   TRACE_EVENT_ASYNC_END0("ui", "LoadAndDecodeWallpaper", this);
   1584 
   1585   // If decoded wallpaper is empty, we have probably failed to decode the file.
   1586   // Use default wallpaper in this case.
   1587   if (user_image.image().isNull()) {
   1588     // Updates user pref to default wallpaper.
   1589     WallpaperInfo info = {"", ash::WALLPAPER_LAYOUT_CENTER_CROPPED,
   1590                           user_manager::User::DEFAULT,
   1591                           base::Time::Now().LocalMidnight()};
   1592     SetUserWallpaperInfo(user_id, info, true);
   1593 
   1594     if (update_wallpaper)
   1595       DoSetDefaultWallpaper(user_id, on_finish.Pass());
   1596     return;
   1597   }
   1598 
   1599   wallpaper_cache_[user_id] = user_image.image();
   1600 
   1601   if (update_wallpaper) {
   1602     ash::Shell::GetInstance()
   1603         ->desktop_background_controller()
   1604         ->SetWallpaperImage(user_image.image(), layout);
   1605   }
   1606 }
   1607 
   1608 void WallpaperManager::StartLoad(const std::string& user_id,
   1609                                  const WallpaperInfo& info,
   1610                                  bool update_wallpaper,
   1611                                  const base::FilePath& wallpaper_path,
   1612                                  MovableOnDestroyCallbackHolder on_finish) {
   1613   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   1614   TRACE_EVENT_ASYNC_BEGIN0("ui", "LoadAndDecodeWallpaper", this);
   1615 
   1616   wallpaper_loader_->Start(wallpaper_path.value(),
   1617                            0,  // Do not crop.
   1618                            base::Bind(&WallpaperManager::OnWallpaperDecoded,
   1619                                       weak_factory_.GetWeakPtr(),
   1620                                       user_id,
   1621                                       info.layout,
   1622                                       update_wallpaper,
   1623                                       base::Passed(on_finish.Pass())));
   1624 }
   1625 
   1626 void WallpaperManager::SaveLastLoadTime(const base::TimeDelta elapsed) {
   1627   while (last_load_times_.size() >= kLastLoadsStatsMsMaxSize)
   1628     last_load_times_.pop_front();
   1629 
   1630   if (elapsed > base::TimeDelta::FromMicroseconds(0)) {
   1631     last_load_times_.push_back(elapsed);
   1632     last_load_finished_at_ = base::Time::Now();
   1633   }
   1634 }
   1635 
   1636 base::TimeDelta WallpaperManager::GetWallpaperLoadDelay() const {
   1637   base::TimeDelta delay;
   1638 
   1639   if (last_load_times_.size() == 0) {
   1640     delay = base::TimeDelta::FromMilliseconds(kLoadDefaultDelayMs);
   1641   } else {
   1642     delay = std::accumulate(last_load_times_.begin(),
   1643                             last_load_times_.end(),
   1644                             base::TimeDelta(),
   1645                             std::plus<base::TimeDelta>()) /
   1646             last_load_times_.size();
   1647   }
   1648 
   1649   if (delay < base::TimeDelta::FromMilliseconds(kLoadMinDelayMs))
   1650     delay = base::TimeDelta::FromMilliseconds(kLoadMinDelayMs);
   1651   else if (delay > base::TimeDelta::FromMilliseconds(kLoadMaxDelayMs))
   1652     delay = base::TimeDelta::FromMilliseconds(kLoadMaxDelayMs);
   1653 
   1654   // If we had ever loaded wallpaper, adjust wait delay by time since last load.
   1655   if (!last_load_finished_at_.is_null()) {
   1656     const base::TimeDelta interval = base::Time::Now() - last_load_finished_at_;
   1657     if (interval > delay)
   1658       delay = base::TimeDelta::FromMilliseconds(0);
   1659     else if (interval > base::TimeDelta::FromMilliseconds(0))
   1660       delay -= interval;
   1661   }
   1662   return delay;
   1663 }
   1664 
   1665 void WallpaperManager::SetCustomizedDefaultWallpaperAfterCheck(
   1666     const GURL& wallpaper_url,
   1667     const base::FilePath& downloaded_file,
   1668     scoped_ptr<CustomizedWallpaperRescaledFiles> rescaled_files) {
   1669   PrefService* pref_service = g_browser_process->local_state();
   1670 
   1671   std::string current_url =
   1672       pref_service->GetString(prefs::kCustomizationDefaultWallpaperURL);
   1673   if (current_url != wallpaper_url.spec() || !rescaled_files->AllSizesExist()) {
   1674     DCHECK(rescaled_files->downloaded_exists());
   1675 
   1676     // Either resized images do not exist or cached version is incorrect.
   1677     // Need to start resize again.
   1678     wallpaper_loader_->Start(
   1679         downloaded_file.value(),
   1680         0,  // Do not crop.
   1681         base::Bind(&WallpaperManager::OnCustomizedDefaultWallpaperDecoded,
   1682                    weak_factory_.GetWeakPtr(),
   1683                    wallpaper_url,
   1684                    base::Passed(rescaled_files.Pass())));
   1685   } else {
   1686     SetDefaultWallpaperPath(rescaled_files->path_rescaled_small(),
   1687                             scoped_ptr<gfx::ImageSkia>().Pass(),
   1688                             rescaled_files->path_rescaled_large(),
   1689                             scoped_ptr<gfx::ImageSkia>().Pass());
   1690   }
   1691 }
   1692 
   1693 void WallpaperManager::OnCustomizedDefaultWallpaperDecoded(
   1694     const GURL& wallpaper_url,
   1695     scoped_ptr<CustomizedWallpaperRescaledFiles> rescaled_files,
   1696     const user_manager::UserImage& wallpaper) {
   1697   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   1698 
   1699   // If decoded wallpaper is empty, we have probably failed to decode the file.
   1700   if (wallpaper.image().isNull()) {
   1701     LOG(WARNING) << "Failed to decode customized wallpaper.";
   1702     return;
   1703   }
   1704 
   1705   wallpaper.image().EnsureRepsForSupportedScales();
   1706   scoped_ptr<gfx::ImageSkia> deep_copy(wallpaper.image().DeepCopy());
   1707 
   1708   scoped_ptr<bool> success(new bool(false));
   1709   scoped_ptr<gfx::ImageSkia> small_wallpaper_image(new gfx::ImageSkia);
   1710   scoped_ptr<gfx::ImageSkia> large_wallpaper_image(new gfx::ImageSkia);
   1711 
   1712   // TODO(bshe): This may break if RawImage becomes RefCountedMemory.
   1713   base::Closure resize_closure =
   1714       base::Bind(&WallpaperManager::ResizeCustomizedDefaultWallpaper,
   1715                  base::Passed(&deep_copy),
   1716                  wallpaper.raw_image(),
   1717                  base::Unretained(rescaled_files.get()),
   1718                  base::Unretained(success.get()),
   1719                  base::Unretained(small_wallpaper_image.get()),
   1720                  base::Unretained(large_wallpaper_image.get()));
   1721   base::Closure on_resized_closure =
   1722       base::Bind(&WallpaperManager::OnCustomizedDefaultWallpaperResized,
   1723                  weak_factory_.GetWeakPtr(),
   1724                  wallpaper_url,
   1725                  base::Passed(rescaled_files.Pass()),
   1726                  base::Passed(success.Pass()),
   1727                  base::Passed(small_wallpaper_image.Pass()),
   1728                  base::Passed(large_wallpaper_image.Pass()));
   1729 
   1730   if (!task_runner_->PostTaskAndReply(
   1731           FROM_HERE, resize_closure, on_resized_closure)) {
   1732     LOG(WARNING) << "Failed to start Customized Wallpaper resize.";
   1733   }
   1734 }
   1735 
   1736 void WallpaperManager::ResizeCustomizedDefaultWallpaper(
   1737     scoped_ptr<gfx::ImageSkia> image,
   1738     const user_manager::UserImage::RawImage& raw_image,
   1739     const CustomizedWallpaperRescaledFiles* rescaled_files,
   1740     bool* success,
   1741     gfx::ImageSkia* small_wallpaper_image,
   1742     gfx::ImageSkia* large_wallpaper_image) {
   1743   *success = true;
   1744 
   1745   *success &= ResizeAndSaveWallpaper(*image,
   1746                                      rescaled_files->path_rescaled_small(),
   1747                                      ash::WALLPAPER_LAYOUT_STRETCH,
   1748                                      kSmallWallpaperMaxWidth,
   1749                                      kSmallWallpaperMaxHeight,
   1750                                      small_wallpaper_image);
   1751 
   1752   *success &= ResizeAndSaveWallpaper(*image,
   1753                                      rescaled_files->path_rescaled_large(),
   1754                                      ash::WALLPAPER_LAYOUT_STRETCH,
   1755                                      kLargeWallpaperMaxWidth,
   1756                                      kLargeWallpaperMaxHeight,
   1757                                      large_wallpaper_image);
   1758 }
   1759 
   1760 void WallpaperManager::OnCustomizedDefaultWallpaperResized(
   1761     const GURL& wallpaper_url,
   1762     scoped_ptr<CustomizedWallpaperRescaledFiles> rescaled_files,
   1763     scoped_ptr<bool> success,
   1764     scoped_ptr<gfx::ImageSkia> small_wallpaper_image,
   1765     scoped_ptr<gfx::ImageSkia> large_wallpaper_image) {
   1766   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   1767   DCHECK(rescaled_files);
   1768   DCHECK(success.get());
   1769   if (!*success) {
   1770     LOG(WARNING) << "Failed to save resized customized default wallpaper";
   1771     return;
   1772   }
   1773   PrefService* pref_service = g_browser_process->local_state();
   1774   pref_service->SetString(prefs::kCustomizationDefaultWallpaperURL,
   1775                           wallpaper_url.spec());
   1776   SetDefaultWallpaperPath(rescaled_files->path_rescaled_small(),
   1777                           small_wallpaper_image.Pass(),
   1778                           rescaled_files->path_rescaled_large(),
   1779                           large_wallpaper_image.Pass());
   1780   VLOG(1) << "Customized default wallpaper applied.";
   1781 }
   1782 
   1783 WallpaperManager::PendingWallpaper* WallpaperManager::GetPendingWallpaper(
   1784     const std::string& user_id,
   1785     bool delayed) {
   1786   if (!pending_inactive_) {
   1787     loading_.push_back(new WallpaperManager::PendingWallpaper(
   1788         (delayed ? GetWallpaperLoadDelay()
   1789                  : base::TimeDelta::FromMilliseconds(0)),
   1790         user_id));
   1791     pending_inactive_ = loading_.back().get();
   1792   }
   1793   return pending_inactive_;
   1794 }
   1795 
   1796 void WallpaperManager::RemovePendingWallpaperFromList(
   1797     PendingWallpaper* pending) {
   1798   DCHECK(loading_.size() > 0);
   1799   for (WallpaperManager::PendingList::iterator i = loading_.begin();
   1800        i != loading_.end();
   1801        ++i) {
   1802     if (i->get() == pending) {
   1803       loading_.erase(i);
   1804       break;
   1805     }
   1806   }
   1807 
   1808   if (loading_.empty())
   1809     FOR_EACH_OBSERVER(Observer, observers_, OnPendingListEmptyForTesting());
   1810 }
   1811 
   1812 void WallpaperManager::SetCustomizedDefaultWallpaper(
   1813     const GURL& wallpaper_url,
   1814     const base::FilePath& downloaded_file,
   1815     const base::FilePath& resized_directory) {
   1816   // Should fail if this ever happens in tests.
   1817   DCHECK(wallpaper_url.is_valid());
   1818   if (!wallpaper_url.is_valid()) {
   1819     if (!wallpaper_url.is_empty()) {
   1820       LOG(WARNING) << "Invalid Customized Wallpaper URL '"
   1821                    << wallpaper_url.spec() << "'";
   1822     }
   1823     return;
   1824   }
   1825   std::string downloaded_file_name = downloaded_file.BaseName().value();
   1826   scoped_ptr<CustomizedWallpaperRescaledFiles> rescaled_files(
   1827       new CustomizedWallpaperRescaledFiles(
   1828           downloaded_file,
   1829           resized_directory.Append(downloaded_file_name +
   1830                                    kSmallWallpaperSuffix),
   1831           resized_directory.Append(downloaded_file_name +
   1832                                    kLargeWallpaperSuffix)));
   1833 
   1834   base::Closure check_file_exists = rescaled_files->CreateCheckerClosure();
   1835   base::Closure on_checked_closure =
   1836       base::Bind(&WallpaperManager::SetCustomizedDefaultWallpaperAfterCheck,
   1837                  weak_factory_.GetWeakPtr(),
   1838                  wallpaper_url,
   1839                  downloaded_file,
   1840                  base::Passed(rescaled_files.Pass()));
   1841   if (!BrowserThread::PostBlockingPoolTaskAndReply(
   1842           FROM_HERE, check_file_exists, on_checked_closure)) {
   1843     LOG(WARNING) << "Failed to start check CheckCustomizedWallpaperFilesExist.";
   1844   }
   1845 }
   1846 
   1847 size_t WallpaperManager::GetPendingListSizeForTesting() const {
   1848   return loading_.size();
   1849 }
   1850 
   1851 void WallpaperManager::SetDefaultWallpaperPathsFromCommandLine(
   1852     base::CommandLine* command_line) {
   1853   default_small_wallpaper_file_ = command_line->GetSwitchValuePath(
   1854       ash::switches::kAshDefaultWallpaperSmall);
   1855   default_large_wallpaper_file_ = command_line->GetSwitchValuePath(
   1856       ash::switches::kAshDefaultWallpaperLarge);
   1857   guest_small_wallpaper_file_ =
   1858       command_line->GetSwitchValuePath(ash::switches::kAshGuestWallpaperSmall);
   1859   guest_large_wallpaper_file_ =
   1860       command_line->GetSwitchValuePath(ash::switches::kAshGuestWallpaperLarge);
   1861   default_wallpaper_image_.reset();
   1862 }
   1863 
   1864 void WallpaperManager::OnDefaultWallpaperDecoded(
   1865     const base::FilePath& path,
   1866     const ash::WallpaperLayout layout,
   1867     scoped_ptr<user_manager::UserImage>* result_out,
   1868     MovableOnDestroyCallbackHolder on_finish,
   1869     const user_manager::UserImage& user_image) {
   1870   result_out->reset(new user_manager::UserImage(user_image));
   1871   ash::Shell::GetInstance()->desktop_background_controller()->SetWallpaperImage(
   1872       user_image.image(), layout);
   1873 }
   1874 
   1875 void WallpaperManager::StartLoadAndSetDefaultWallpaper(
   1876     const base::FilePath& path,
   1877     const ash::WallpaperLayout layout,
   1878     MovableOnDestroyCallbackHolder on_finish,
   1879     scoped_ptr<user_manager::UserImage>* result_out) {
   1880   wallpaper_loader_->Start(
   1881       path.value(),
   1882       0,  // Do not crop.
   1883       base::Bind(&WallpaperManager::OnDefaultWallpaperDecoded,
   1884                  weak_factory_.GetWeakPtr(),
   1885                  path,
   1886                  layout,
   1887                  base::Unretained(result_out),
   1888                  base::Passed(on_finish.Pass())));
   1889 }
   1890 
   1891 const char* WallpaperManager::GetCustomWallpaperSubdirForCurrentResolution() {
   1892   WallpaperResolution resolution = GetAppropriateResolution();
   1893   return resolution == WALLPAPER_RESOLUTION_SMALL ? kSmallWallpaperSubDir
   1894                                                   : kLargeWallpaperSubDir;
   1895 }
   1896 
   1897 void WallpaperManager::SetDefaultWallpaperPath(
   1898     const base::FilePath& default_small_wallpaper_file,
   1899     scoped_ptr<gfx::ImageSkia> small_wallpaper_image,
   1900     const base::FilePath& default_large_wallpaper_file,
   1901     scoped_ptr<gfx::ImageSkia> large_wallpaper_image) {
   1902   default_small_wallpaper_file_ = default_small_wallpaper_file;
   1903   default_large_wallpaper_file_ = default_large_wallpaper_file;
   1904 
   1905   ash::DesktopBackgroundController* dbc =
   1906       ash::Shell::GetInstance()->desktop_background_controller();
   1907 
   1908   // |need_update_screen| is true if the previous default wallpaper is visible
   1909   // now, so we need to update wallpaper on the screen.
   1910   //
   1911   // Layout is ignored here, so ash::WALLPAPER_LAYOUT_CENTER is used
   1912   // as a placeholder only.
   1913   const bool need_update_screen =
   1914       default_wallpaper_image_.get() &&
   1915       dbc->WallpaperIsAlreadyLoaded(default_wallpaper_image_->image(),
   1916                                     false /* compare_layouts */,
   1917                                     ash::WALLPAPER_LAYOUT_CENTER);
   1918 
   1919   default_wallpaper_image_.reset();
   1920   if (GetAppropriateResolution() == WALLPAPER_RESOLUTION_SMALL) {
   1921     if (small_wallpaper_image) {
   1922       default_wallpaper_image_.reset(
   1923           new user_manager::UserImage(*small_wallpaper_image));
   1924       default_wallpaper_image_->set_file_path(
   1925           default_small_wallpaper_file.value());
   1926     }
   1927   } else {
   1928     if (large_wallpaper_image) {
   1929       default_wallpaper_image_.reset(
   1930           new user_manager::UserImage(*large_wallpaper_image));
   1931       default_wallpaper_image_->set_file_path(
   1932           default_large_wallpaper_file.value());
   1933     }
   1934   }
   1935 
   1936   if (need_update_screen) {
   1937     DoSetDefaultWallpaper(std::string(),
   1938                           MovableOnDestroyCallbackHolder().Pass());
   1939   }
   1940 }
   1941 
   1942 void WallpaperManager::CreateSolidDefaultWallpaper() {
   1943   loaded_wallpapers_++;
   1944   SkBitmap bitmap;
   1945   bitmap.allocN32Pixels(1, 1);
   1946   bitmap.eraseColor(kDefaultWallpaperColor);
   1947   const gfx::ImageSkia image = gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
   1948   default_wallpaper_image_.reset(new user_manager::UserImage(image));
   1949 }
   1950 
   1951 }  // namespace chromeos
   1952