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