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