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