1 // Copyright (c) 2013 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.
5 #include "chrome/browser/chromeos/login/wallpaper_manager.h"
10 #include "base/command_line.h"
11 #include "base/debug/trace_event.h"
12 #include "base/file_util.h"
13 #include "base/files/file_enumerator.h"
14 #include "base/files/file_path.h"
15 #include "base/logging.h"
16 #include "base/metrics/histogram.h"
17 #include "base/path_service.h"
18 #include "base/prefs/pref_registry_simple.h"
19 #include "base/prefs/pref_service.h"
20 #include "base/prefs/scoped_user_pref_update.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/strings/string_util.h"
23 #include "base/strings/stringprintf.h"
24 #include "base/threading/worker_pool.h"
25 #include "base/time/time.h"
26 #include "base/values.h"
27 #include "chrome/browser/browser_process.h"
28 #include "chrome/browser/chrome_notification_types.h"
29 #include "chrome/browser/chromeos/login/startup_utils.h"
30 #include "chrome/browser/chromeos/login/user.h"
31 #include "chrome/browser/chromeos/login/user_manager.h"
32 #include "chrome/browser/chromeos/login/wizard_controller.h"
33 #include "chrome/browser/chromeos/settings/cros_settings.h"
34 #include "chrome/common/chrome_paths.h"
35 #include "chrome/common/chrome_switches.h"
36 #include "chrome/common/pref_names.h"
37 #include "chromeos/chromeos_switches.h"
38 #include "chromeos/dbus/dbus_thread_manager.h"
39 #include "content/public/browser/browser_thread.h"
40 #include "content/public/browser/notification_service.h"
41 #include "ui/base/resource/resource_bundle.h"
42 #include "ui/gfx/codec/jpeg_codec.h"
43 #include "ui/gfx/image/image_skia_operations.h"
44 #include "ui/gfx/skia_util.h"
46 using content::BrowserThread;
50 // The amount of delay before starts to move custom wallpapers to the new place.
51 const int kMoveCustomWallpaperDelaySeconds = 30;
53 // Default quality for encoding wallpaper.
54 const int kDefaultEncodingQuality = 90;
56 // A dictionary pref that maps usernames to file paths to their wallpapers.
57 // Deprecated. Will remove this const char after done migration.
58 const char kUserWallpapers[] = "UserWallpapers";
60 const int kCacheWallpaperDelayMs = 500;
62 // A dictionary pref that maps usernames to wallpaper properties.
63 const char kUserWallpapersProperties[] = "UserWallpapersProperties";
65 // Names of nodes with info about wallpaper in |kUserWallpapersProperties|
67 const char kNewWallpaperDateNodeName[] = "date";
68 const char kNewWallpaperLayoutNodeName[] = "layout";
69 const char kNewWallpaperFileNodeName[] = "file";
70 const char kNewWallpaperTypeNodeName[] = "type";
72 // File path suffix of the original custom wallpaper.
73 const char kOriginalCustomWallpaperSuffix[] = "_wallpaper";
75 // Maximum number of wallpapers cached by CacheUsersWallpapers().
76 const int kMaxWallpapersToCache = 3;
78 // For our scaling ratios we need to round positive numbers.
79 int RoundPositive(double x) {
80 return static_cast<int>(floor(x + 0.5));
83 // Returns custom wallpaper directory by appending corresponding |sub_dir|.
84 base::FilePath GetCustomWallpaperDir(const char* sub_dir) {
85 base::FilePath custom_wallpaper_dir;
86 CHECK(PathService::Get(chrome::DIR_CHROMEOS_CUSTOM_WALLPAPERS,
87 &custom_wallpaper_dir));
88 return custom_wallpaper_dir.Append(sub_dir);
91 bool MoveCustomWallpaperDirectory(const char* sub_dir,
92 const std::string& email,
93 const std::string& user_id_hash) {
94 base::FilePath base_path = GetCustomWallpaperDir(sub_dir);
95 base::FilePath to_path = base_path.Append(user_id_hash);
96 base::FilePath from_path = base_path.Append(email);
97 if (base::PathExists(from_path))
98 return base::Move(from_path, to_path);
106 const char kWallpaperSequenceTokenName[] = "wallpaper-sequence";
108 const char kSmallWallpaperSuffix[] = "_small";
109 const char kLargeWallpaperSuffix[] = "_large";
111 const char kSmallWallpaperSubDir[] = "small";
112 const char kLargeWallpaperSubDir[] = "large";
113 const char kOriginalWallpaperSubDir[] = "original";
114 const char kThumbnailWallpaperSubDir[] = "thumb";
116 static WallpaperManager* g_wallpaper_manager = NULL;
118 // WallpaperManager, public: ---------------------------------------------------
120 // TestApi. For testing purpose
121 WallpaperManager::TestApi::TestApi(WallpaperManager* wallpaper_manager)
122 : wallpaper_manager_(wallpaper_manager) {
125 WallpaperManager::TestApi::~TestApi() {
128 base::FilePath WallpaperManager::TestApi::current_wallpaper_path() {
129 return wallpaper_manager_->current_wallpaper_path_;
133 WallpaperManager* WallpaperManager::Get() {
134 if (!g_wallpaper_manager)
135 g_wallpaper_manager = new WallpaperManager();
136 return g_wallpaper_manager;
139 WallpaperManager::WallpaperManager()
140 : loaded_wallpapers_(0),
141 wallpaper_loader_(new UserImageLoader(ImageDecoder::ROBUST_JPEG_CODEC)),
142 command_line_for_testing_(NULL),
143 should_cache_wallpaper_(false),
144 weak_factory_(this) {
146 chrome::NOTIFICATION_LOGIN_USER_CHANGED,
147 content::NotificationService::AllSources());
149 chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
150 content::NotificationService::AllSources());
152 chrome::NOTIFICATION_WALLPAPER_ANIMATION_FINISHED,
153 content::NotificationService::AllSources());
154 sequence_token_ = BrowserThread::GetBlockingPool()->
155 GetNamedSequenceToken(kWallpaperSequenceTokenName);
156 task_runner_ = BrowserThread::GetBlockingPool()->
157 GetSequencedTaskRunnerWithShutdownBehavior(
159 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
162 WallpaperManager::~WallpaperManager() {
163 // TODO(bshe): Lifetime of WallpaperManager needs more consideration.
164 // http://crbug.com/171694
165 DCHECK(!show_user_name_on_signin_subscription_);
166 ClearObsoleteWallpaperPrefs();
167 weak_factory_.InvalidateWeakPtrs();
170 void WallpaperManager::Shutdown() {
171 show_user_name_on_signin_subscription_.reset();
175 void WallpaperManager::RegisterPrefs(PrefRegistrySimple* registry) {
176 registry->RegisterDictionaryPref(prefs::kUsersWallpaperInfo);
177 registry->RegisterDictionaryPref(kUserWallpapers);
178 registry->RegisterDictionaryPref(kUserWallpapersProperties);
181 void WallpaperManager::AddObservers() {
182 show_user_name_on_signin_subscription_ =
183 CrosSettings::Get()->AddSettingsObserver(
184 kAccountsPrefShowUserNamesOnSignIn,
185 base::Bind(&WallpaperManager::InitializeRegisteredDeviceWallpaper,
186 base::Unretained(this)));
189 void WallpaperManager::EnsureLoggedInUserWallpaperLoaded() {
190 // Some browser tests do not have a shell instance. As no wallpaper is needed
191 // in these tests anyway, avoid loading one, preventing crashes and speeding
193 if (!ash::Shell::HasInstance())
197 if (GetLoggedInUserWallpaperInfo(&info)) {
198 // TODO(sschmitz): We need an index for default wallpapers for the new UI.
199 RecordUma(info.type, -1);
200 if (info == current_user_wallpaper_info_)
203 SetUserWallpaper(UserManager::Get()->GetLoggedInUser()->email());
206 void WallpaperManager::ClearWallpaperCache() {
207 // Cancel callback for previous cache requests.
208 weak_factory_.InvalidateWeakPtrs();
209 wallpaper_cache_.clear();
212 base::FilePath WallpaperManager::GetCustomWallpaperPath(
214 const std::string& user_id_hash,
215 const std::string& file) {
216 base::FilePath custom_wallpaper_path = GetCustomWallpaperDir(sub_dir);
217 return custom_wallpaper_path.Append(user_id_hash).Append(file);
220 bool WallpaperManager::GetWallpaperFromCache(const std::string& email,
221 gfx::ImageSkia* wallpaper) {
222 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
223 CustomWallpaperMap::const_iterator it = wallpaper_cache_.find(email);
224 if (it != wallpaper_cache_.end()) {
225 *wallpaper = (*it).second;
231 base::FilePath WallpaperManager::GetOriginalWallpaperPathForUser(
232 const std::string& username) {
233 std::string filename = username + kOriginalCustomWallpaperSuffix;
234 base::FilePath user_data_dir;
235 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
236 return user_data_dir.AppendASCII(filename);
239 bool WallpaperManager::GetLoggedInUserWallpaperInfo(WallpaperInfo* info) {
240 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
242 if (UserManager::Get()->IsLoggedInAsStub()) {
243 info->file = current_user_wallpaper_info_.file = "";
244 info->layout = current_user_wallpaper_info_.layout =
245 ash::WALLPAPER_LAYOUT_CENTER_CROPPED;
246 info->type = current_user_wallpaper_info_.type = User::DEFAULT;
250 return GetUserWallpaperInfo(UserManager::Get()->GetLoggedInUser()->email(),
254 void WallpaperManager::InitializeWallpaper() {
255 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
256 UserManager* user_manager = UserManager::Get();
258 CommandLine* command_line = GetComandLine();
259 if (command_line->HasSwitch(chromeos::switches::kGuestSession)) {
260 // Guest wallpaper should be initialized when guest login.
261 // Note: This maybe called before login. So IsLoggedInAsGuest can not be
262 // used here to determine if current user is guest.
266 if (command_line->HasSwitch(::switches::kTestType))
267 WizardController::SetZeroDelays();
269 // Zero delays is also set in autotests.
270 if (WizardController::IsZeroDelayEnabled()) {
271 // Ensure tests have some sort of wallpaper.
272 ash::Shell::GetInstance()->desktop_background_controller()->
273 CreateEmptyWallpaper();
277 if (!user_manager->IsUserLoggedIn()) {
278 if (!StartupUtils::IsDeviceRegistered())
279 SetDefaultWallpaper();
281 InitializeRegisteredDeviceWallpaper();
284 SetUserWallpaper(user_manager->GetLoggedInUser()->email());
287 void WallpaperManager::Observe(int type,
288 const content::NotificationSource& source,
289 const content::NotificationDetails& details) {
290 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
292 case chrome::NOTIFICATION_LOGIN_USER_CHANGED: {
293 ClearWallpaperCache();
294 BrowserThread::PostDelayedTask(
297 base::Bind(&WallpaperManager::MoveLoggedInUserCustomWallpaper,
298 weak_factory_.GetWeakPtr()),
299 base::TimeDelta::FromSeconds(kMoveCustomWallpaperDelaySeconds));
302 case chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE: {
303 if (!GetComandLine()->HasSwitch(switches::kDisableBootAnimation)) {
304 BrowserThread::PostDelayedTask(
305 BrowserThread::UI, FROM_HERE,
306 base::Bind(&WallpaperManager::CacheUsersWallpapers,
307 weak_factory_.GetWeakPtr()),
308 base::TimeDelta::FromMilliseconds(kCacheWallpaperDelayMs));
310 should_cache_wallpaper_ = true;
314 case chrome::NOTIFICATION_WALLPAPER_ANIMATION_FINISHED: {
315 if (should_cache_wallpaper_) {
316 BrowserThread::PostDelayedTask(
317 BrowserThread::UI, FROM_HERE,
318 base::Bind(&WallpaperManager::CacheUsersWallpapers,
319 weak_factory_.GetWeakPtr()),
320 base::TimeDelta::FromMilliseconds(kCacheWallpaperDelayMs));
321 should_cache_wallpaper_ = false;
326 NOTREACHED() << "Unexpected notification " << type;
330 void WallpaperManager::RemoveUserWallpaperInfo(const std::string& email) {
332 GetUserWallpaperInfo(email, &info);
333 PrefService* prefs = g_browser_process->local_state();
334 DictionaryPrefUpdate prefs_wallpapers_info_update(prefs,
335 prefs::kUsersWallpaperInfo);
336 prefs_wallpapers_info_update->RemoveWithoutPathExpansion(email, NULL);
337 DeleteUserWallpapers(email, info.file);
340 bool WallpaperManager::ResizeWallpaper(
341 const UserImage& wallpaper,
342 ash::WallpaperLayout layout,
344 int preferred_height,
345 scoped_refptr<base::RefCountedBytes>* output) {
346 DCHECK(BrowserThread::GetBlockingPool()->
347 IsRunningSequenceOnCurrentThread(sequence_token_));
348 int width = wallpaper.image().width();
349 int height = wallpaper.image().height();
352 *output = new base::RefCountedBytes();
354 if (layout == ash::WALLPAPER_LAYOUT_CENTER_CROPPED) {
355 // Do not resize custom wallpaper if it is smaller than preferred size.
356 if (!(width > preferred_width && height > preferred_height))
359 double horizontal_ratio = static_cast<double>(preferred_width) / width;
360 double vertical_ratio = static_cast<double>(preferred_height) / height;
361 if (vertical_ratio > horizontal_ratio) {
363 RoundPositive(static_cast<double>(width) * vertical_ratio);
364 resized_height = preferred_height;
366 resized_width = preferred_width;
368 RoundPositive(static_cast<double>(height) * horizontal_ratio);
370 } else if (layout == ash::WALLPAPER_LAYOUT_STRETCH) {
371 resized_width = preferred_width;
372 resized_height = preferred_height;
374 resized_width = width;
375 resized_height = height;
378 gfx::ImageSkia resized_image = gfx::ImageSkiaOperations::CreateResizedImage(
380 skia::ImageOperations::RESIZE_LANCZOS3,
381 gfx::Size(resized_width, resized_height));
383 SkBitmap image = *(resized_image.bitmap());
384 SkAutoLockPixels lock_input(image);
385 gfx::JPEGCodec::Encode(
386 reinterpret_cast<unsigned char*>(image.getAddr32(0, 0)),
387 gfx::JPEGCodec::FORMAT_SkBitmap,
390 image.width() * image.bytesPerPixel(),
391 kDefaultEncodingQuality, &(*output)->data());
395 void WallpaperManager::ResizeAndSaveWallpaper(const UserImage& wallpaper,
396 const base::FilePath& path,
397 ash::WallpaperLayout layout,
399 int preferred_height) {
400 if (layout == ash::WALLPAPER_LAYOUT_CENTER) {
401 // TODO(bshe): Generates cropped custom wallpaper for CENTER layout.
402 if (base::PathExists(path))
403 base::DeleteFile(path, false);
406 scoped_refptr<base::RefCountedBytes> data;
407 if (ResizeWallpaper(wallpaper, layout, preferred_width, preferred_height,
409 SaveWallpaperInternal(path,
410 reinterpret_cast<const char*>(data->front()),
415 void WallpaperManager::SetCustomWallpaper(const std::string& username,
416 const std::string& user_id_hash,
417 const std::string& file,
418 ash::WallpaperLayout layout,
419 User::WallpaperType type,
420 const UserImage& wallpaper) {
421 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
423 base::FilePath wallpaper_path =
424 GetCustomWallpaperPath(kOriginalWallpaperSubDir, user_id_hash, file);
426 // If decoded wallpaper is empty, we are probably failed to decode the file.
427 // Use default wallpaper in this case.
428 if (wallpaper.image().isNull()) {
429 SetDefaultWallpaper();
434 !UserManager::Get()->IsUserNonCryptohomeDataEphemeral(username);
436 wallpaper.image().EnsureRepsForSupportedScales();
437 scoped_ptr<gfx::ImageSkia> deep_copy(wallpaper.image().DeepCopy());
439 WallpaperInfo wallpaper_info = {
440 wallpaper_path.value(),
443 // Date field is not used.
444 base::Time::Now().LocalMidnight()
446 // Block shutdown on this task. Otherwise, we may lost the custom wallpaper
448 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner =
449 BrowserThread::GetBlockingPool()->
450 GetSequencedTaskRunnerWithShutdownBehavior(sequence_token_,
451 base::SequencedWorkerPool::BLOCK_SHUTDOWN);
452 // TODO(bshe): This may break if RawImage becomes RefCountedMemory.
453 blocking_task_runner->PostTask(FROM_HERE,
454 base::Bind(&WallpaperManager::ProcessCustomWallpaper,
455 base::Unretained(this),
459 base::Passed(&deep_copy),
460 wallpaper.raw_image()));
461 ash::Shell::GetInstance()->desktop_background_controller()->
462 SetCustomWallpaper(wallpaper.image(), layout);
464 std::string relative_path = base::FilePath(user_id_hash).Append(file).value();
465 // User's custom wallpaper path is determined by relative path and the
466 // appropriate wallpaper resolution in GetCustomWallpaperInternal.
467 WallpaperInfo info = {
471 base::Time::Now().LocalMidnight()
473 SetUserWallpaperInfo(username, info, is_persistent);
476 void WallpaperManager::SetDefaultWallpaper() {
477 current_wallpaper_path_.clear();
478 if (ash::Shell::GetInstance()->desktop_background_controller()->
479 SetDefaultWallpaper(UserManager::Get()->IsLoggedInAsGuest()))
480 loaded_wallpapers_++;
483 void WallpaperManager::SetInitialUserWallpaper(const std::string& username,
484 bool is_persistent) {
485 current_user_wallpaper_info_.file = "";
486 current_user_wallpaper_info_.layout = ash::WALLPAPER_LAYOUT_CENTER_CROPPED;
487 current_user_wallpaper_info_.type = User::DEFAULT;
488 current_user_wallpaper_info_.date = base::Time::Now().LocalMidnight();
490 WallpaperInfo info = current_user_wallpaper_info_;
491 SetUserWallpaperInfo(username, info, is_persistent);
492 SetLastSelectedUser(username);
494 // Some browser tests do not have a shell instance. As no wallpaper is needed
495 // in these tests anyway, avoid loading one, preventing crashes and speeding
497 if (ash::Shell::HasInstance())
498 SetDefaultWallpaper();
501 void WallpaperManager::SetUserWallpaperInfo(const std::string& username,
502 const WallpaperInfo& info,
503 bool is_persistent) {
504 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
505 current_user_wallpaper_info_ = info;
509 PrefService* local_state = g_browser_process->local_state();
510 DictionaryPrefUpdate wallpaper_update(local_state,
511 prefs::kUsersWallpaperInfo);
513 base::DictionaryValue* wallpaper_info_dict = new base::DictionaryValue();
514 wallpaper_info_dict->SetString(kNewWallpaperDateNodeName,
515 base::Int64ToString(info.date.ToInternalValue()));
516 wallpaper_info_dict->SetString(kNewWallpaperFileNodeName, info.file);
517 wallpaper_info_dict->SetInteger(kNewWallpaperLayoutNodeName, info.layout);
518 wallpaper_info_dict->SetInteger(kNewWallpaperTypeNodeName, info.type);
519 wallpaper_update->SetWithoutPathExpansion(username, wallpaper_info_dict);
522 void WallpaperManager::SetLastSelectedUser(
523 const std::string& last_selected_user) {
524 last_selected_user_ = last_selected_user;
527 void WallpaperManager::SetUserWallpaper(const std::string& email) {
528 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
529 if (email == UserManager::kGuestUserName) {
530 SetDefaultWallpaper();
534 if (!UserManager::Get()->IsKnownUser(email))
537 SetLastSelectedUser(email);
541 if (GetUserWallpaperInfo(email, &info)) {
542 gfx::ImageSkia user_wallpaper;
543 current_user_wallpaper_info_ = info;
544 if (GetWallpaperFromCache(email, &user_wallpaper)) {
545 ash::Shell::GetInstance()->desktop_background_controller()->
546 SetCustomWallpaper(user_wallpaper, info.layout);
548 if (info.type == User::CUSTOMIZED) {
549 ash::WallpaperResolution resolution = ash::Shell::GetInstance()->
550 desktop_background_controller()->GetAppropriateResolution();
551 const char* sub_dir = (resolution == ash::WALLPAPER_RESOLUTION_SMALL) ?
552 kSmallWallpaperSubDir : kLargeWallpaperSubDir;
553 // Wallpaper is not resized when layout is ash::WALLPAPER_LAYOUT_CENTER.
554 // Original wallpaper should be used in this case.
555 // TODO(bshe): Generates cropped custom wallpaper for CENTER layout.
556 if (info.layout == ash::WALLPAPER_LAYOUT_CENTER)
557 sub_dir = kOriginalWallpaperSubDir;
558 base::FilePath wallpaper_path = GetCustomWallpaperDir(sub_dir);
559 wallpaper_path = wallpaper_path.Append(info.file);
560 if (current_wallpaper_path_ == wallpaper_path)
562 current_wallpaper_path_ = wallpaper_path;
563 loaded_wallpapers_++;
565 task_runner_->PostTask(FROM_HERE,
566 base::Bind(&WallpaperManager::GetCustomWallpaperInternal,
567 base::Unretained(this), email, info, wallpaper_path,
568 true /* update wallpaper */));
572 if (info.file.empty()) {
573 // Uses default built-in wallpaper when file is empty. Eventually, we
574 // will only ship one built-in wallpaper in ChromeOS image.
575 SetDefaultWallpaper();
579 // Load downloaded ONLINE or converted DEFAULT wallpapers.
580 LoadWallpaper(email, info, true /* update wallpaper */);
583 SetInitialUserWallpaper(email, true);
587 void WallpaperManager::SetWallpaperFromImageSkia(
588 const gfx::ImageSkia& wallpaper,
589 ash::WallpaperLayout layout) {
590 ash::Shell::GetInstance()->desktop_background_controller()->
591 SetCustomWallpaper(wallpaper, layout);
594 void WallpaperManager::UpdateWallpaper() {
595 ClearWallpaperCache();
596 current_wallpaper_path_.clear();
597 // For GAIA login flow, the last_selected_user_ may not be set before user
598 // login. If UpdateWallpaper is called at GAIA login screen, no wallpaper will
599 // be set. It could result a black screen on external monitors.
600 // See http://crbug.com/265689 for detail.
601 if (last_selected_user_.empty()) {
602 SetDefaultWallpaper();
605 SetUserWallpaper(last_selected_user_);
608 // WallpaperManager, private: --------------------------------------------------
610 void WallpaperManager::CacheUsersWallpapers() {
611 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
612 UserList users = UserManager::Get()->GetUsers();
614 if (!users.empty()) {
615 UserList::const_iterator it = users.begin();
616 // Skip the wallpaper of first user in the list. It should have been cached.
619 it != users.end() && cached < kMaxWallpapersToCache;
621 std::string user_email = (*it)->email();
622 CacheUserWallpaper(user_email);
627 void WallpaperManager::CacheUserWallpaper(const std::string& email) {
628 if (wallpaper_cache_.find(email) == wallpaper_cache_.end())
631 if (GetUserWallpaperInfo(email, &info)) {
632 base::FilePath wallpaper_dir;
633 base::FilePath wallpaper_path;
634 if (info.type == User::CUSTOMIZED) {
635 ash::WallpaperResolution resolution = ash::Shell::GetInstance()->
636 desktop_background_controller()->GetAppropriateResolution();
637 const char* sub_dir = (resolution == ash::WALLPAPER_RESOLUTION_SMALL) ?
638 kSmallWallpaperSubDir : kLargeWallpaperSubDir;
639 base::FilePath wallpaper_path = GetCustomWallpaperDir(sub_dir);
640 wallpaper_path = wallpaper_path.Append(info.file);
641 task_runner_->PostTask(FROM_HERE,
642 base::Bind(&WallpaperManager::GetCustomWallpaperInternal,
643 base::Unretained(this), email, info, wallpaper_path,
644 false /* do not update wallpaper */));
647 LoadWallpaper(email, info, false /* do not update wallpaper */);
651 void WallpaperManager::ClearObsoleteWallpaperPrefs() {
652 PrefService* prefs = g_browser_process->local_state();
653 DictionaryPrefUpdate wallpaper_properties_pref(prefs,
654 kUserWallpapersProperties);
655 wallpaper_properties_pref->Clear();
656 DictionaryPrefUpdate wallpapers_pref(prefs, kUserWallpapers);
657 wallpapers_pref->Clear();
660 void WallpaperManager::DeleteAllExcept(const base::FilePath& path) {
661 base::FilePath dir = path.DirName();
662 if (base::DirectoryExists(dir)) {
663 base::FileEnumerator files(dir, false, base::FileEnumerator::FILES);
664 for (base::FilePath current = files.Next(); !current.empty();
665 current = files.Next()) {
667 base::DeleteFile(current, false);
672 void WallpaperManager::DeleteWallpaperInList(
673 const std::vector<base::FilePath>& file_list) {
674 for (std::vector<base::FilePath>::const_iterator it = file_list.begin();
675 it != file_list.end(); ++it) {
676 base::FilePath path = *it;
677 // Some users may still have legacy wallpapers with png extension. We need
678 // to delete these wallpapers too.
679 if (!base::DeleteFile(path, true) &&
680 !base::DeleteFile(path.AddExtension(".png"), false)) {
681 LOG(ERROR) << "Failed to remove user wallpaper at " << path.value();
686 void WallpaperManager::DeleteUserWallpapers(const std::string& email,
687 const std::string& path_to_file) {
688 std::vector<base::FilePath> file_to_remove;
689 // Remove small user wallpaper.
690 base::FilePath wallpaper_path =
691 GetCustomWallpaperDir(kSmallWallpaperSubDir);
692 // Remove old directory if exists
693 file_to_remove.push_back(wallpaper_path.Append(email));
694 wallpaper_path = wallpaper_path.Append(path_to_file).DirName();
695 file_to_remove.push_back(wallpaper_path);
697 // Remove large user wallpaper.
698 wallpaper_path = GetCustomWallpaperDir(kLargeWallpaperSubDir);
699 file_to_remove.push_back(wallpaper_path.Append(email));
700 wallpaper_path = wallpaper_path.Append(path_to_file);
701 file_to_remove.push_back(wallpaper_path);
703 // Remove user wallpaper thumbnail.
704 wallpaper_path = GetCustomWallpaperDir(kThumbnailWallpaperSubDir);
705 file_to_remove.push_back(wallpaper_path.Append(email));
706 wallpaper_path = wallpaper_path.Append(path_to_file);
707 file_to_remove.push_back(wallpaper_path);
709 // Remove original user wallpaper.
710 wallpaper_path = GetCustomWallpaperDir(kOriginalWallpaperSubDir);
711 file_to_remove.push_back(wallpaper_path.Append(email));
712 wallpaper_path = wallpaper_path.Append(path_to_file);
713 file_to_remove.push_back(wallpaper_path);
715 base::WorkerPool::PostTask(
717 base::Bind(&WallpaperManager::DeleteWallpaperInList,
718 base::Unretained(this),
723 void WallpaperManager::EnsureCustomWallpaperDirectories(
724 const std::string& user_id_hash) {
726 dir = GetCustomWallpaperDir(kSmallWallpaperSubDir);
727 dir = dir.Append(user_id_hash);
728 if (!base::PathExists(dir))
729 file_util::CreateDirectory(dir);
730 dir = GetCustomWallpaperDir(kLargeWallpaperSubDir);
731 dir = dir.Append(user_id_hash);
732 if (!base::PathExists(dir))
733 file_util::CreateDirectory(dir);
734 dir = GetCustomWallpaperDir(kOriginalWallpaperSubDir);
735 dir = dir.Append(user_id_hash);
736 if (!base::PathExists(dir))
737 file_util::CreateDirectory(dir);
738 dir = GetCustomWallpaperDir(kThumbnailWallpaperSubDir);
739 dir = dir.Append(user_id_hash);
740 if (!base::PathExists(dir))
741 file_util::CreateDirectory(dir);
744 CommandLine* WallpaperManager::GetComandLine() {
745 CommandLine* command_line = command_line_for_testing_ ?
746 command_line_for_testing_ : CommandLine::ForCurrentProcess();
750 void WallpaperManager::InitializeRegisteredDeviceWallpaper() {
751 if (UserManager::Get()->IsUserLoggedIn())
754 bool disable_boot_animation = GetComandLine()->
755 HasSwitch(switches::kDisableBootAnimation);
756 bool show_users = true;
757 bool result = CrosSettings::Get()->GetBoolean(
758 kAccountsPrefShowUserNamesOnSignIn, &show_users);
759 DCHECK(result) << "Unable to fetch setting "
760 << kAccountsPrefShowUserNamesOnSignIn;
761 const chromeos::UserList& users = UserManager::Get()->GetUsers();
762 if (!show_users || users.empty()) {
763 // Boot into sign in form, preload default wallpaper.
764 SetDefaultWallpaper();
768 if (!disable_boot_animation) {
769 // Normal boot, load user wallpaper.
770 // If normal boot animation is disabled wallpaper would be set
771 // asynchronously once user pods are loaded.
772 SetUserWallpaper(users[0]->email());
776 void WallpaperManager::LoadWallpaper(const std::string& email,
777 const WallpaperInfo& info,
778 bool update_wallpaper) {
779 base::FilePath wallpaper_dir;
780 base::FilePath wallpaper_path;
781 if (info.type == User::ONLINE) {
782 std::string file_name = GURL(info.file).ExtractFileName();
783 ash::WallpaperResolution resolution = ash::Shell::GetInstance()->
784 desktop_background_controller()->GetAppropriateResolution();
785 // Only solid color wallpapers have stretch layout and they have only one
787 if (info.layout != ash::WALLPAPER_LAYOUT_STRETCH &&
788 resolution == ash::WALLPAPER_RESOLUTION_SMALL) {
789 file_name = base::FilePath(file_name).InsertBeforeExtension(
790 kSmallWallpaperSuffix).value();
792 CHECK(PathService::Get(chrome::DIR_CHROMEOS_WALLPAPERS, &wallpaper_dir));
793 wallpaper_path = wallpaper_dir.Append(file_name);
794 if (current_wallpaper_path_ == wallpaper_path)
796 if (update_wallpaper)
797 current_wallpaper_path_ = wallpaper_path;
798 loaded_wallpapers_++;
799 StartLoad(email, info, update_wallpaper, wallpaper_path);
800 } else if (info.type == User::DEFAULT) {
801 // Default wallpapers are migrated from M21 user profiles. A code refactor
802 // overlooked that case and caused these wallpapers not being loaded at all.
803 // On some slow devices, it caused login webui not visible after upgrade to
804 // M26 from M21. See crosbug.com/38429 for details.
805 base::FilePath user_data_dir;
806 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
807 wallpaper_path = user_data_dir.Append(info.file);
808 StartLoad(email, info, update_wallpaper, wallpaper_path);
810 // In unexpected cases, revert to default wallpaper to fail safely. See
811 // crosbug.com/38429.
812 LOG(ERROR) << "Wallpaper reverts to default unexpected.";
813 SetDefaultWallpaper();
817 bool WallpaperManager::GetUserWallpaperInfo(const std::string& email,
818 WallpaperInfo* info){
819 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
821 if (UserManager::Get()->IsUserNonCryptohomeDataEphemeral(email)) {
822 // Default to the values cached in memory.
823 *info = current_user_wallpaper_info_;
825 // Ephemeral users do not save anything to local state. But we have got
826 // wallpaper info from memory. Returns true.
830 const DictionaryValue* user_wallpapers = g_browser_process->local_state()->
831 GetDictionary(prefs::kUsersWallpaperInfo);
832 const base::DictionaryValue* wallpaper_info_dict;
833 if (user_wallpapers->GetDictionaryWithoutPathExpansion(
834 email, &wallpaper_info_dict)) {
836 info->layout = ash::WALLPAPER_LAYOUT_CENTER_CROPPED;
837 info->type = User::UNKNOWN;
838 info->date = base::Time::Now().LocalMidnight();
839 wallpaper_info_dict->GetString(kNewWallpaperFileNodeName, &(info->file));
841 wallpaper_info_dict->GetInteger(kNewWallpaperLayoutNodeName, &temp);
842 info->layout = static_cast<ash::WallpaperLayout>(temp);
843 wallpaper_info_dict->GetInteger(kNewWallpaperTypeNodeName, &temp);
844 info->type = static_cast<User::WallpaperType>(temp);
845 std::string date_string;
847 if (!(wallpaper_info_dict->GetString(kNewWallpaperDateNodeName,
849 base::StringToInt64(date_string, &val)))
851 info->date = base::Time::FromInternalValue(val);
858 void WallpaperManager::MoveCustomWallpapersOnWorker(
859 const std::string& email,
860 const std::string& user_id_hash) {
861 DCHECK(BrowserThread::GetBlockingPool()->
862 IsRunningSequenceOnCurrentThread(sequence_token_));
863 if (MoveCustomWallpaperDirectory(kOriginalWallpaperSubDir,
866 // Consider success if the original wallpaper is moved to the new directory.
867 // Original wallpaper is the fallback if the correct resolution wallpaper
869 BrowserThread::PostTask(
870 BrowserThread::UI, FROM_HERE,
871 base::Bind(&WallpaperManager::MoveCustomWallpapersSuccess,
872 base::Unretained(this),
876 MoveCustomWallpaperDirectory(kLargeWallpaperSubDir, email, user_id_hash);
877 MoveCustomWallpaperDirectory(kSmallWallpaperSubDir, email, user_id_hash);
878 MoveCustomWallpaperDirectory(kThumbnailWallpaperSubDir, email, user_id_hash);
881 void WallpaperManager::MoveCustomWallpapersSuccess(
882 const std::string& email,
883 const std::string& user_id_hash) {
885 GetUserWallpaperInfo(email, &info);
886 if (info.type == User::CUSTOMIZED) {
887 // New file field should include user id hash in addition to file name.
888 // This is needed because at login screen, user id hash is not available.
889 std::string relative_path =
890 base::FilePath(user_id_hash).Append(info.file).value();
891 info.file = relative_path;
893 !UserManager::Get()->IsUserNonCryptohomeDataEphemeral(email);
894 SetUserWallpaperInfo(email, info, is_persistent);
898 void WallpaperManager::MoveLoggedInUserCustomWallpaper() {
899 const User* logged_in_user = UserManager::Get()->GetLoggedInUser();
900 task_runner_->PostTask(
902 base::Bind(&WallpaperManager::MoveCustomWallpapersOnWorker,
903 base::Unretained(this),
904 logged_in_user->email(),
905 logged_in_user->username_hash()));
908 void WallpaperManager::GetCustomWallpaperInternal(
909 const std::string& email,
910 const WallpaperInfo& info,
911 const base::FilePath& wallpaper_path,
912 bool update_wallpaper) {
913 DCHECK(BrowserThread::GetBlockingPool()->
914 IsRunningSequenceOnCurrentThread(sequence_token_));
916 base::FilePath valid_path = wallpaper_path;
917 if (!base::PathExists(wallpaper_path)) {
918 // Falls back on original file if the correct resoltuion file does not
919 // exist. This may happen when the original custom wallpaper is small or
920 // browser shutdown before resized wallpaper saved.
921 valid_path = GetCustomWallpaperDir(kOriginalWallpaperSubDir);
922 valid_path = valid_path.Append(info.file);
925 if (!base::PathExists(valid_path)) {
926 // Falls back to custom wallpaper that uses email as part of its file path.
927 // Note that email is used instead of user_id_hash here.
928 valid_path = GetCustomWallpaperPath(kOriginalWallpaperSubDir, email,
932 if (!base::PathExists(valid_path)) {
933 LOG(ERROR) << "Failed to load previously selected custom wallpaper. " <<
934 "Fallback to default wallpaper";
935 BrowserThread::PostTask(BrowserThread::UI,
937 base::Bind(&WallpaperManager::SetDefaultWallpaper,
938 base::Unretained(this)));
940 BrowserThread::PostTask(BrowserThread::UI,
942 base::Bind(&WallpaperManager::StartLoad,
943 base::Unretained(this),
951 void WallpaperManager::OnWallpaperDecoded(const std::string& email,
952 ash::WallpaperLayout layout,
953 bool update_wallpaper,
954 const UserImage& wallpaper) {
955 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
956 TRACE_EVENT_ASYNC_END0("ui", "LoadAndDecodeWallpaper", this);
958 // If decoded wallpaper is empty, we are probably failed to decode the file.
959 // Use default wallpaper in this case.
960 if (wallpaper.image().isNull()) {
961 // Updates user pref to default wallpaper.
962 WallpaperInfo info = {
964 ash::WALLPAPER_LAYOUT_CENTER_CROPPED,
966 base::Time::Now().LocalMidnight()
968 SetUserWallpaperInfo(email, info, true);
970 if (update_wallpaper)
971 SetDefaultWallpaper();
975 // Only cache user wallpaper at login screen.
976 if (!UserManager::Get()->IsUserLoggedIn()) {
977 wallpaper_cache_.insert(std::make_pair(email, wallpaper.image()));
979 if (update_wallpaper) {
980 ash::Shell::GetInstance()->desktop_background_controller()->
981 SetCustomWallpaper(wallpaper.image(), layout);
985 void WallpaperManager::ProcessCustomWallpaper(
986 const std::string& user_id_hash,
988 const WallpaperInfo& info,
989 scoped_ptr<gfx::ImageSkia> image,
990 const UserImage::RawImage& raw_image) {
991 DCHECK(BrowserThread::GetBlockingPool()->
992 IsRunningSequenceOnCurrentThread(sequence_token_));
993 UserImage wallpaper(*image.get(), raw_image);
995 SaveCustomWallpaper(user_id_hash, base::FilePath(info.file), info.layout,
1000 void WallpaperManager::SaveCustomWallpaper(const std::string& user_id_hash,
1001 const base::FilePath& original_path,
1002 ash::WallpaperLayout layout,
1003 const UserImage& wallpaper) {
1004 DCHECK(BrowserThread::GetBlockingPool()->
1005 IsRunningSequenceOnCurrentThread(sequence_token_));
1006 EnsureCustomWallpaperDirectories(user_id_hash);
1007 std::string file_name = original_path.BaseName().value();
1008 base::FilePath small_wallpaper_path =
1009 GetCustomWallpaperPath(kSmallWallpaperSubDir, user_id_hash, file_name);
1010 base::FilePath large_wallpaper_path =
1011 GetCustomWallpaperPath(kLargeWallpaperSubDir, user_id_hash, file_name);
1013 // Re-encode orginal file to jpeg format and saves the result in case that
1014 // resized wallpaper is not generated (i.e. chrome shutdown before resized
1015 // wallpaper is saved).
1016 ResizeAndSaveWallpaper(wallpaper, original_path,
1017 ash::WALLPAPER_LAYOUT_STRETCH,
1018 wallpaper.image().width(),
1019 wallpaper.image().height());
1020 DeleteAllExcept(original_path);
1022 ResizeAndSaveWallpaper(wallpaper, small_wallpaper_path, layout,
1023 ash::kSmallWallpaperMaxWidth,
1024 ash::kSmallWallpaperMaxHeight);
1025 DeleteAllExcept(small_wallpaper_path);
1026 ResizeAndSaveWallpaper(wallpaper, large_wallpaper_path, layout,
1027 ash::kLargeWallpaperMaxWidth,
1028 ash::kLargeWallpaperMaxHeight);
1029 DeleteAllExcept(large_wallpaper_path);
1032 void WallpaperManager::RecordUma(User::WallpaperType type, int index) {
1033 UMA_HISTOGRAM_ENUMERATION("Ash.Wallpaper.Type", type,
1034 User::WALLPAPER_TYPE_COUNT);
1037 void WallpaperManager::SaveWallpaperInternal(const base::FilePath& path,
1040 int written_bytes = file_util::WriteFile(path, data, size);
1041 DCHECK(written_bytes == size);
1044 void WallpaperManager::StartLoad(const std::string& email,
1045 const WallpaperInfo& info,
1046 bool update_wallpaper,
1047 const base::FilePath& wallpaper_path) {
1048 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1049 TRACE_EVENT_ASYNC_BEGIN0("ui", "LoadAndDecodeWallpaper", this);
1051 // All wallpaper related operation should run on the same thread. So we pass
1052 // |sequence_token_| here.
1053 wallpaper_loader_->Start(wallpaper_path.value(), 0, sequence_token_,
1054 base::Bind(&WallpaperManager::OnWallpaperDecoded,
1055 base::Unretained(this),
1061 } // namespace chromeos