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