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
57using content::BrowserThread;
58
59namespace chromeos {
60
61namespace {
62
63// The amount of delay before starts to move custom wallpapers to the new place.
64const int kMoveCustomWallpaperDelaySeconds = 30;
65
66// Default quality for encoding wallpaper.
67const 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.
71const char kUserWallpapers[] = "UserWallpapers";
72
73const int kCacheWallpaperDelayMs = 500;
74
75// A dictionary pref that maps usernames to wallpaper properties.
76const char kUserWallpapersProperties[] = "UserWallpapersProperties";
77
78// Names of nodes with info about wallpaper in |kUserWallpapersProperties|
79// dictionary.
80const char kNewWallpaperDateNodeName[] = "date";
81const char kNewWallpaperLayoutNodeName[] = "layout";
82const char kNewWallpaperLocationNodeName[] = "file";
83const char kNewWallpaperTypeNodeName[] = "type";
84
85// Maximum number of wallpapers cached by CacheUsersWallpapers().
86const int kMaxWallpapersToCache = 3;
87
88// Maximum number of entries in WallpaperManager::last_load_times_ .
89const size_t kLastLoadsStatsMsMaxSize = 4;
90
91// Minimum delay between wallpaper loads, milliseconds.
92const unsigned kLoadMinDelayMs = 50;
93
94// Default wallpaper load delay, milliseconds.
95const unsigned kLoadDefaultDelayMs = 200;
96
97// Maximum wallpaper load delay, milliseconds.
98const unsigned kLoadMaxDelayMs = 2000;
99
100// When no wallpaper image is specified, the screen is filled with a solid
101// color.
102const SkColor kDefaultWallpaperColor = SK_ColorGRAY;
103
104// For our scaling ratios we need to round positive numbers.
105int RoundPositive(double x) {
106  return static_cast<int>(floor(x + 0.5));
107}
108
109// Returns custom wallpaper directory by appending corresponding |sub_dir|.
110base::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
117bool 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().
130base::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.
144bool 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|.
153void 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
168void 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.
190bool 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.
199int 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
216const char kWallpaperSequenceTokenName[] = "wallpaper-sequence";
217
218const char kSmallWallpaperSuffix[] = "_small";
219const char kLargeWallpaperSuffix[] = "_large";
220
221const char kSmallWallpaperSubDir[] = "small";
222const char kLargeWallpaperSubDir[] = "large";
223const char kOriginalWallpaperSubDir[] = "original";
224const char kThumbnailWallpaperSubDir[] = "thumb";
225
226const int kSmallWallpaperMaxWidth = 1366;
227const int kSmallWallpaperMaxHeight = 800;
228const int kLargeWallpaperMaxWidth = 2560;
229const int kLargeWallpaperMaxHeight = 1700;
230const int kWallpaperThumbnailWidth = 108;
231const int kWallpaperThumbnailHeight = 68;
232
233static WallpaperManager* g_wallpaper_manager = NULL;
234
235class 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
275WallpaperManager::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
287base::Closure
288WallpaperManager::CustomizedWallpaperRescaledFiles::CreateCheckerClosure() {
289  return base::Bind(&WallpaperManager::CustomizedWallpaperRescaledFiles::
290                        CheckCustomizedWallpaperFilesExist,
291                    base::Unretained(this));
292}
293
294void 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
302bool 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).
309class 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
324WallpaperManager::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
338WallpaperManager::PendingWallpaper::~PendingWallpaper() {}
339
340void WallpaperManager::PendingWallpaper::ResetSetWallpaperImage(
341    const gfx::ImageSkia& image,
342    const WallpaperInfo& info) {
343  SetMode(image, info, base::FilePath(), false);
344}
345
346void WallpaperManager::PendingWallpaper::ResetLoadWallpaper(
347    const WallpaperInfo& info) {
348  SetMode(gfx::ImageSkia(), info, base::FilePath(), false);
349}
350
351void WallpaperManager::PendingWallpaper::ResetSetCustomWallpaper(
352    const WallpaperInfo& info,
353    const base::FilePath& wallpaper_path) {
354  SetMode(gfx::ImageSkia(), info, wallpaper_path, false);
355}
356
357void WallpaperManager::PendingWallpaper::ResetSetDefaultWallpaper() {
358  SetMode(gfx::ImageSkia(), WallpaperInfo(), base::FilePath(), true);
359}
360
361void 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
372void 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
410void 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
437WallpaperManager::TestApi::TestApi(WallpaperManager* wallpaper_manager)
438    : wallpaper_manager_(wallpaper_manager) {
439}
440
441WallpaperManager::TestApi::~TestApi() {
442}
443
444base::FilePath WallpaperManager::TestApi::current_wallpaper_path() {
445  return wallpaper_manager_->current_wallpaper_path_;
446}
447
448bool WallpaperManager::TestApi::GetWallpaperFromCache(
449    const std::string& user_id, gfx::ImageSkia* image) {
450  return wallpaper_manager_->GetWallpaperFromCache(user_id, image);
451}
452
453void 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
459void WallpaperManager::TestApi::ClearDisposableWallpaperCache() {
460  wallpaper_manager_->ClearDisposableWallpaperCache();
461}
462
463// static
464WallpaperManager* WallpaperManager::Get() {
465  if (!g_wallpaper_manager)
466    g_wallpaper_manager = new WallpaperManager();
467  return g_wallpaper_manager;
468}
469
470WallpaperManager::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
497WallpaperManager::~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
506void WallpaperManager::Shutdown() {
507  show_user_name_on_signin_subscription_.reset();
508}
509
510// static
511void WallpaperManager::RegisterPrefs(PrefRegistrySimple* registry) {
512  registry->RegisterDictionaryPref(prefs::kUsersWallpaperInfo);
513  registry->RegisterDictionaryPref(kUserWallpapers);
514  registry->RegisterDictionaryPref(kUserWallpapersProperties);
515}
516
517void 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
525void 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
543void 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
567bool 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
585void 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
627void 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
671void 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
682bool 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
744bool 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
769bool 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
776void 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
784void 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
793void 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
808WallpaperManager::WallpaperResolution
809WallpaperManager::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
820base::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
828void 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
857void 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
874void 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
954void WallpaperManager::SetDefaultWallpaperNow(const std::string& user_id) {
955  GetPendingWallpaper(user_id, false)->ResetSetDefaultWallpaper();
956}
957
958void WallpaperManager::SetDefaultWallpaperDelayed(const std::string& user_id) {
959  GetPendingWallpaper(user_id, true)->ResetSetDefaultWallpaper();
960}
961
962void 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
1014void 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
1058void 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
1083void 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
1129void 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
1140void 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
1161void WallpaperManager::SetUserWallpaperDelayed(const std::string& user_id) {
1162  ScheduleSetUserWallpaper(user_id, true);
1163}
1164
1165void WallpaperManager::SetUserWallpaperNow(const std::string& user_id) {
1166  ScheduleSetUserWallpaper(user_id, false);
1167}
1168
1169void 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
1242void 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
1261void 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
1277void WallpaperManager::AddObserver(WallpaperManager::Observer* observer) {
1278  observers_.AddObserver(observer);
1279}
1280
1281void WallpaperManager::RemoveObserver(WallpaperManager::Observer* observer) {
1282  observers_.RemoveObserver(observer);
1283}
1284
1285void WallpaperManager::NotifyAnimationFinished() {
1286  FOR_EACH_OBSERVER(
1287      Observer, observers_, OnWallpaperAnimationFinished(last_selected_user_));
1288}
1289
1290// WallpaperManager, private: --------------------------------------------------
1291
1292bool 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
1303void 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
1320void 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
1353void 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
1362void 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
1397void WallpaperManager::SetCommandLineForTesting(
1398    base::CommandLine* command_line) {
1399  command_line_for_testing_ = command_line;
1400  SetDefaultWallpaperPathsFromCommandLine(command_line);
1401}
1402
1403CommandLine* WallpaperManager::GetCommandLine() {
1404  CommandLine* command_line = command_line_for_testing_ ?
1405      command_line_for_testing_ : CommandLine::ForCurrentProcess();
1406  return command_line;
1407}
1408
1409void 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
1438void 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
1502bool 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
1547void 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
1563void 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
1576void 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
1608void 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
1626void 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
1636base::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
1665void 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
1693void 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
1736void 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
1760void 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
1783WallpaperManager::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
1796void 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
1812void 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
1847size_t WallpaperManager::GetPendingListSizeForTesting() const {
1848  return loading_.size();
1849}
1850
1851void 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
1864void 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
1875void 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
1891const char* WallpaperManager::GetCustomWallpaperSubdirForCurrentResolution() {
1892  WallpaperResolution resolution = GetAppropriateResolution();
1893  return resolution == WALLPAPER_RESOLUTION_SMALL ? kSmallWallpaperSubDir
1894                                                  : kLargeWallpaperSubDir;
1895}
1896
1897void 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
1942void 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