ozone: evdev: Sync caps lock LED state to evdev
[chromium-blink-merge.git] / chrome / browser / chromeos / login / users / wallpaper / wallpaper_manager.cc
blobef44c3902be4dc0fb73a1f9fddc69fbf93d22ff9
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.
5 #include "chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h"
7 #include <numeric>
8 #include <vector>
10 #include "ash/ash_constants.h"
11 #include "ash/ash_switches.h"
12 #include "ash/shell.h"
13 #include "base/bind.h"
14 #include "base/command_line.h"
15 #include "base/files/file_enumerator.h"
16 #include "base/files/file_path.h"
17 #include "base/files/file_util.h"
18 #include "base/logging.h"
19 #include "base/metrics/histogram.h"
20 #include "base/path_service.h"
21 #include "base/prefs/pref_registry_simple.h"
22 #include "base/prefs/pref_service.h"
23 #include "base/prefs/scoped_user_pref_update.h"
24 #include "base/strings/string_number_conversions.h"
25 #include "base/strings/string_util.h"
26 #include "base/strings/stringprintf.h"
27 #include "base/sys_info.h"
28 #include "base/threading/worker_pool.h"
29 #include "base/time/time.h"
30 #include "base/trace_event/trace_event.h"
31 #include "base/values.h"
32 #include "chrome/browser/browser_process.h"
33 #include "chrome/browser/chrome_notification_types.h"
34 #include "chrome/browser/chromeos/customization/customization_document.h"
35 #include "chrome/browser/chromeos/login/startup_utils.h"
36 #include "chrome/browser/chromeos/login/wizard_controller.h"
37 #include "chrome/browser/chromeos/settings/cros_settings.h"
38 #include "chrome/common/chrome_paths.h"
39 #include "chrome/common/chrome_switches.h"
40 #include "chrome/common/pref_names.h"
41 #include "chromeos/chromeos_switches.h"
42 #include "chromeos/cryptohome/async_method_caller.h"
43 #include "chromeos/dbus/dbus_thread_manager.h"
44 #include "chromeos/login/user_names.h"
45 #include "components/user_manager/user.h"
46 #include "components/user_manager/user_image/user_image.h"
47 #include "components/user_manager/user_manager.h"
48 #include "components/user_manager/user_type.h"
49 #include "components/wallpaper/wallpaper_layout.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"
57 using content::BrowserThread;
58 using wallpaper::WallpaperManagerBase;
59 using wallpaper::WallpaperInfo;
60 using wallpaper::MovableOnDestroyCallback;
61 using wallpaper::MovableOnDestroyCallbackHolder;
63 namespace chromeos {
65 namespace {
67 // The amount of delay before starts to move custom wallpapers to the new place.
68 const int kMoveCustomWallpaperDelaySeconds = 30;
70 const int kCacheWallpaperDelayMs = 500;
72 // Names of nodes with info about wallpaper in |kUserWallpapersProperties|
73 // dictionary.
74 const char kNewWallpaperDateNodeName[] = "date";
75 const char kNewWallpaperLayoutNodeName[] = "layout";
76 const char kNewWallpaperLocationNodeName[] = "file";
77 const char kNewWallpaperTypeNodeName[] = "type";
79 // These global default values are used to set customized default
80 // wallpaper path in WallpaperManager::InitializeWallpaper().
81 base::FilePath GetCustomizedWallpaperDefaultRescaledFileName(
82 const std::string& suffix) {
83 const base::FilePath default_downloaded_file_name =
84 ServicesCustomizationDocument::GetCustomizedWallpaperDownloadedFileName();
85 const base::FilePath default_cache_dir =
86 ServicesCustomizationDocument::GetCustomizedWallpaperCacheDir();
87 if (default_downloaded_file_name.empty() || default_cache_dir.empty())
88 return base::FilePath();
89 return default_cache_dir.Append(
90 default_downloaded_file_name.BaseName().value() + suffix);
93 // Whether DesktopBackgroundController should start with customized default
94 // wallpaper in WallpaperManager::InitializeWallpaper() or not.
95 bool ShouldUseCustomizedDefaultWallpaper() {
96 PrefService* pref_service = g_browser_process->local_state();
98 return !(pref_service->FindPreference(
99 prefs::kCustomizationDefaultWallpaperURL)->IsDefaultValue());
102 // Returns index of the first public session user found in |users|
103 // or -1 otherwise.
104 int FindPublicSession(const user_manager::UserList& users) {
105 int index = -1;
106 int i = 0;
107 for (user_manager::UserList::const_iterator it = users.begin();
108 it != users.end();
109 ++it, ++i) {
110 if ((*it)->GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT) {
111 index = i;
112 break;
116 return index;
119 } // namespace
121 // This is "wallpaper either scheduled to load, or loading right now".
123 // While enqueued, it defines moment in the future, when it will be loaded.
124 // Enqueued but not started request might be updated by subsequent load
125 // request. Therefore it's created empty, and updated being enqueued.
127 // PendingWallpaper is owned by WallpaperManager, but reference to this object
128 // is passed to other threads by PostTask() calls, therefore it is
129 // RefCountedThreadSafe.
130 class WallpaperManager::PendingWallpaper :
131 public base::RefCountedThreadSafe<PendingWallpaper> {
132 public:
133 // Do LoadWallpaper() - image not found in cache.
134 PendingWallpaper(
135 const base::TimeDelta delay,
136 const std::string& user_id)
137 : user_id_(user_id),
138 default_(false),
139 on_finish_(new MovableOnDestroyCallback(
140 base::Bind(&WallpaperManager::PendingWallpaper::OnWallpaperSet,
141 this))) {
142 timer.Start(
143 FROM_HERE,
144 delay,
145 base::Bind(&WallpaperManager::PendingWallpaper::ProcessRequest, this));
148 // There are 4 cases in SetUserWallpaper:
149 // 1) gfx::ImageSkia is found in cache.
150 // - Schedule task to (probably) resize it and install:
151 // call ash::Shell::GetInstance()->desktop_background_controller()->
152 // SetCustomWallpaper(user_wallpaper, layout);
153 // 2) WallpaperInfo is found in cache
154 // - need to LoadWallpaper(), resize and install.
155 // 3) wallpaper path is not NULL, load image URL, then resize, etc...
156 // 4) SetDefaultWallpaper (either on some error, or when user is new).
157 void ResetSetWallpaperImage(const gfx::ImageSkia& image,
158 const wallpaper::WallpaperInfo& info) {
159 SetMode(image, info, base::FilePath(), false);
162 void ResetLoadWallpaper(const wallpaper::WallpaperInfo& info) {
163 SetMode(gfx::ImageSkia(), info, base::FilePath(), false);
166 void ResetSetCustomWallpaper(const wallpaper::WallpaperInfo& info,
167 const base::FilePath& wallpaper_path) {
168 SetMode(gfx::ImageSkia(), info, wallpaper_path, false);
171 void ResetSetDefaultWallpaper() {
172 SetMode(gfx::ImageSkia(), WallpaperInfo(), base::FilePath(), true);
175 private:
176 friend class base::RefCountedThreadSafe<PendingWallpaper>;
178 ~PendingWallpaper() {}
180 // All Reset*() methods use SetMode() to set object to new state.
181 void SetMode(const gfx::ImageSkia& image,
182 const wallpaper::WallpaperInfo& info,
183 const base::FilePath& wallpaper_path,
184 const bool is_default) {
185 user_wallpaper_ = image;
186 info_ = info;
187 wallpaper_path_ = wallpaper_path;
188 default_ = is_default;
191 // This method is usually triggered by timer to actually load request.
192 void ProcessRequest() {
193 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
195 timer.Stop(); // Erase reference to self.
197 WallpaperManager* manager = WallpaperManager::Get();
198 if (manager->pending_inactive_ == this)
199 manager->pending_inactive_ = NULL;
201 started_load_at_ = base::Time::Now();
203 if (default_) {
204 manager->DoSetDefaultWallpaper(user_id_, on_finish_.Pass());
205 } else if (!user_wallpaper_.isNull()) {
206 ash::Shell::GetInstance()
207 ->desktop_background_controller()
208 ->SetWallpaperImage(user_wallpaper_, info_.layout);
209 } else if (!wallpaper_path_.empty()) {
210 manager->task_runner_->PostTask(
211 FROM_HERE,
212 base::Bind(&WallpaperManager::GetCustomWallpaperInternal,
213 user_id_,
214 info_,
215 wallpaper_path_,
216 true /* update wallpaper */,
217 base::Passed(on_finish_.Pass()),
218 manager->weak_factory_.GetWeakPtr()));
219 } else if (!info_.location.empty()) {
220 manager->LoadWallpaper(user_id_, info_, true, on_finish_.Pass());
221 } else {
222 // PendingWallpaper was created and never initialized?
223 NOTREACHED();
224 // Error. Do not record time.
225 started_load_at_ = base::Time();
227 on_finish_.reset();
230 // This method is called by callback, when load request is finished.
231 void OnWallpaperSet() {
232 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
234 // The only known case for this check to fail is global destruction during
235 // wallpaper load. It should never happen.
236 if (!BrowserThread::CurrentlyOn(BrowserThread::UI))
237 return; // We are in a process of global destruction.
239 timer.Stop(); // Erase reference to self.
241 WallpaperManager* manager = WallpaperManager::Get();
242 if (!started_load_at_.is_null()) {
243 const base::TimeDelta elapsed = base::Time::Now() - started_load_at_;
244 manager->SaveLastLoadTime(elapsed);
246 if (manager->pending_inactive_ == this) {
247 // ProcessRequest() was never executed.
248 manager->pending_inactive_ = NULL;
251 // Destroy self.
252 manager->RemovePendingWallpaperFromList(this);
255 std::string user_id_;
256 wallpaper::WallpaperInfo info_;
257 gfx::ImageSkia user_wallpaper_;
258 base::FilePath wallpaper_path_;
260 // Load default wallpaper instead of user image.
261 bool default_;
263 // This is "on destroy" callback that will call OnWallpaperSet() when
264 // image will be loaded.
265 wallpaper::MovableOnDestroyCallbackHolder on_finish_;
266 base::OneShotTimer<WallpaperManager::PendingWallpaper> timer;
268 // Load start time to calculate duration.
269 base::Time started_load_at_;
271 DISALLOW_COPY_AND_ASSIGN(PendingWallpaper);
274 static WallpaperManager* g_wallpaper_manager = NULL;
276 // WallpaperManager, public: ---------------------------------------------------
278 // static
279 WallpaperManager* WallpaperManager::Get() {
280 if (!g_wallpaper_manager)
281 g_wallpaper_manager = new WallpaperManager();
282 return g_wallpaper_manager;
285 WallpaperManager::WallpaperManager()
286 : pending_inactive_(NULL), weak_factory_(this) {
287 wallpaper::WallpaperManagerBase::SetPathIds(
288 chrome::DIR_USER_DATA,
289 chrome::DIR_CHROMEOS_WALLPAPERS,
290 chrome::DIR_CHROMEOS_CUSTOM_WALLPAPERS);
291 SetDefaultWallpaperPathsFromCommandLine(
292 base::CommandLine::ForCurrentProcess());
293 registrar_.Add(this,
294 chrome::NOTIFICATION_LOGIN_USER_CHANGED,
295 content::NotificationService::AllSources());
296 registrar_.Add(this,
297 chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
298 content::NotificationService::AllSources());
299 registrar_.Add(this,
300 chrome::NOTIFICATION_WALLPAPER_ANIMATION_FINISHED,
301 content::NotificationService::AllSources());
302 sequence_token_ = BrowserThread::GetBlockingPool()->GetNamedSequenceToken(
303 wallpaper::kWallpaperSequenceTokenName);
304 task_runner_ = BrowserThread::GetBlockingPool()->
305 GetSequencedTaskRunnerWithShutdownBehavior(
306 sequence_token_,
307 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
308 wallpaper_loader_ = new UserImageLoader(ImageDecoder::ROBUST_JPEG_CODEC,
309 task_runner_);
311 user_manager::UserManager::Get()->AddSessionStateObserver(this);
314 WallpaperManager::~WallpaperManager() {
315 // TODO(bshe): Lifetime of WallpaperManager needs more consideration.
316 // http://crbug.com/171694
317 DCHECK(!show_user_name_on_signin_subscription_);
319 user_manager::UserManager::Get()->RemoveSessionStateObserver(this);
321 ClearObsoleteWallpaperPrefs();
322 weak_factory_.InvalidateWeakPtrs();
325 WallpaperManager::WallpaperResolution
326 WallpaperManager::GetAppropriateResolution() {
327 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
328 gfx::Size size =
329 ash::DesktopBackgroundController::GetMaxDisplaySizeInNative();
330 return (size.width() > wallpaper::kSmallWallpaperMaxWidth ||
331 size.height() > wallpaper::kSmallWallpaperMaxHeight)
332 ? WALLPAPER_RESOLUTION_LARGE
333 : WALLPAPER_RESOLUTION_SMALL;
336 void WallpaperManager::Shutdown() {
337 show_user_name_on_signin_subscription_.reset();
340 void WallpaperManager::AddObservers() {
341 show_user_name_on_signin_subscription_ =
342 CrosSettings::Get()->AddSettingsObserver(
343 kAccountsPrefShowUserNamesOnSignIn,
344 base::Bind(&WallpaperManager::InitializeRegisteredDeviceWallpaper,
345 weak_factory_.GetWeakPtr()));
348 void WallpaperManager::EnsureLoggedInUserWallpaperLoaded() {
349 // Some browser tests do not have a shell instance. As no wallpaper is needed
350 // in these tests anyway, avoid loading one, preventing crashes and speeding
351 // up the tests.
352 if (!ash::Shell::HasInstance())
353 return;
355 WallpaperInfo info;
356 if (GetLoggedInUserWallpaperInfo(&info)) {
357 UMA_HISTOGRAM_ENUMERATION("Ash.Wallpaper.Type", info.type,
358 user_manager::User::WALLPAPER_TYPE_COUNT);
359 if (info == current_user_wallpaper_info_)
360 return;
362 SetUserWallpaperNow(
363 user_manager::UserManager::Get()->GetLoggedInUser()->email());
366 void WallpaperManager::InitializeWallpaper() {
367 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
368 user_manager::UserManager* user_manager = user_manager::UserManager::Get();
370 // Apply device customization.
371 if (ShouldUseCustomizedDefaultWallpaper()) {
372 SetDefaultWallpaperPath(GetCustomizedWallpaperDefaultRescaledFileName(
373 wallpaper::kSmallWallpaperSuffix),
374 scoped_ptr<gfx::ImageSkia>().Pass(),
375 GetCustomizedWallpaperDefaultRescaledFileName(
376 wallpaper::kLargeWallpaperSuffix),
377 scoped_ptr<gfx::ImageSkia>().Pass());
380 base::CommandLine* command_line = GetCommandLine();
381 if (command_line->HasSwitch(chromeos::switches::kGuestSession)) {
382 // Guest wallpaper should be initialized when guest login.
383 // Note: This maybe called before login. So IsLoggedInAsGuest can not be
384 // used here to determine if current user is guest.
385 return;
388 if (command_line->HasSwitch(::switches::kTestType))
389 WizardController::SetZeroDelays();
391 // Zero delays is also set in autotests.
392 if (WizardController::IsZeroDelayEnabled()) {
393 // Ensure tests have some sort of wallpaper.
394 ash::Shell::GetInstance()->desktop_background_controller()->
395 CreateEmptyWallpaper();
396 return;
399 if (!user_manager->IsUserLoggedIn()) {
400 if (!StartupUtils::IsDeviceRegistered())
401 SetDefaultWallpaperDelayed(chromeos::login::kSignInUser);
402 else
403 InitializeRegisteredDeviceWallpaper();
404 return;
406 SetUserWallpaperDelayed(user_manager->GetLoggedInUser()->email());
409 void WallpaperManager::Observe(int type,
410 const content::NotificationSource& source,
411 const content::NotificationDetails& details) {
412 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
413 switch (type) {
414 case chrome::NOTIFICATION_LOGIN_USER_CHANGED: {
415 ClearDisposableWallpaperCache();
416 BrowserThread::PostDelayedTask(
417 BrowserThread::UI,
418 FROM_HERE,
419 base::Bind(&WallpaperManager::MoveLoggedInUserCustomWallpaper,
420 weak_factory_.GetWeakPtr()),
421 base::TimeDelta::FromSeconds(kMoveCustomWallpaperDelaySeconds));
422 break;
424 case chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE: {
425 if (!GetCommandLine()->HasSwitch(switches::kDisableBootAnimation)) {
426 BrowserThread::PostDelayedTask(
427 BrowserThread::UI, FROM_HERE,
428 base::Bind(&WallpaperManager::CacheUsersWallpapers,
429 weak_factory_.GetWeakPtr()),
430 base::TimeDelta::FromMilliseconds(kCacheWallpaperDelayMs));
431 } else {
432 should_cache_wallpaper_ = true;
434 break;
436 case chrome::NOTIFICATION_WALLPAPER_ANIMATION_FINISHED: {
437 NotifyAnimationFinished();
438 if (should_cache_wallpaper_) {
439 BrowserThread::PostDelayedTask(
440 BrowserThread::UI, FROM_HERE,
441 base::Bind(&WallpaperManager::CacheUsersWallpapers,
442 weak_factory_.GetWeakPtr()),
443 base::TimeDelta::FromMilliseconds(kCacheWallpaperDelayMs));
444 should_cache_wallpaper_ = false;
446 break;
448 default:
449 NOTREACHED() << "Unexpected notification " << type;
453 void WallpaperManager::RemoveUserWallpaperInfo(const std::string& user_id) {
454 WallpaperInfo info;
455 GetUserWallpaperInfo(user_id, &info);
456 PrefService* prefs = g_browser_process->local_state();
457 DictionaryPrefUpdate prefs_wallpapers_info_update(
458 prefs, wallpaper::kUsersWallpaperInfo);
459 prefs_wallpapers_info_update->RemoveWithoutPathExpansion(user_id, NULL);
460 DeleteUserWallpapers(user_id, info.location);
463 void WallpaperManager::OnPolicyFetched(const std::string& policy,
464 const std::string& user_id,
465 scoped_ptr<std::string> data) {
466 if (!data)
467 return;
469 wallpaper_loader_->Start(
470 data.Pass(),
471 0, // Do not crop.
472 base::Bind(&WallpaperManager::SetPolicyControlledWallpaper,
473 weak_factory_.GetWeakPtr(),
474 user_id));
477 void WallpaperManager::SetCustomWallpaper(
478 const std::string& user_id,
479 const std::string& user_id_hash,
480 const std::string& file,
481 wallpaper::WallpaperLayout layout,
482 user_manager::User::WallpaperType type,
483 const gfx::ImageSkia& image,
484 bool update_wallpaper) {
485 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
487 // There is no visible background in kiosk mode.
488 if (user_manager::UserManager::Get()->IsLoggedInAsKioskApp())
489 return;
491 // Don't allow custom wallpapers while policy is in effect.
492 if (type != user_manager::User::POLICY && IsPolicyControlled(user_id))
493 return;
495 base::FilePath wallpaper_path = GetCustomWallpaperPath(
496 wallpaper::kOriginalWallpaperSubDir, user_id_hash, file);
498 // If decoded wallpaper is empty, we have probably failed to decode the file.
499 // Use default wallpaper in this case.
500 if (image.isNull()) {
501 SetDefaultWallpaperDelayed(user_id);
502 return;
505 const user_manager::User* user =
506 user_manager::UserManager::Get()->FindUser(user_id);
507 CHECK(user);
508 bool is_persistent =
509 !user_manager::UserManager::Get()->IsUserNonCryptohomeDataEphemeral(
510 user_id) ||
511 (type == user_manager::User::POLICY &&
512 user->GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT);
514 WallpaperInfo wallpaper_info = {
515 wallpaper_path.value(),
516 layout,
517 type,
518 // Date field is not used.
519 base::Time::Now().LocalMidnight()
521 if (is_persistent) {
522 image.EnsureRepsForSupportedScales();
523 scoped_ptr<gfx::ImageSkia> deep_copy(image.DeepCopy());
524 // Block shutdown on this task. Otherwise, we may lose the custom wallpaper
525 // that the user selected.
526 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner =
527 BrowserThread::GetBlockingPool()
528 ->GetSequencedTaskRunnerWithShutdownBehavior(
529 sequence_token_, base::SequencedWorkerPool::BLOCK_SHUTDOWN);
530 // TODO(bshe): This may break if RawImage becomes RefCountedMemory.
531 blocking_task_runner->PostTask(
532 FROM_HERE,
533 base::Bind(&WallpaperManager::SaveCustomWallpaper,
534 user_id_hash,
535 base::FilePath(wallpaper_info.location),
536 wallpaper_info.layout,
537 base::Passed(deep_copy.Pass())));
540 std::string relative_path = base::FilePath(user_id_hash).Append(file).value();
541 // User's custom wallpaper path is determined by relative path and the
542 // appropriate wallpaper resolution in GetCustomWallpaperInternal.
543 WallpaperInfo info = {
544 relative_path,
545 layout,
546 type,
547 base::Time::Now().LocalMidnight()
549 SetUserWallpaperInfo(user_id, info, is_persistent);
550 if (update_wallpaper) {
551 GetPendingWallpaper(user_id, false)->ResetSetWallpaperImage(image, info);
554 wallpaper_cache_[user_id] = CustomWallpaperElement(wallpaper_path, image);
557 void WallpaperManager::SetDefaultWallpaperNow(const std::string& user_id) {
558 GetPendingWallpaper(user_id, false)->ResetSetDefaultWallpaper();
561 void WallpaperManager::SetDefaultWallpaperDelayed(const std::string& user_id) {
562 GetPendingWallpaper(user_id, true)->ResetSetDefaultWallpaper();
565 void WallpaperManager::DoSetDefaultWallpaper(
566 const std::string& user_id,
567 MovableOnDestroyCallbackHolder on_finish) {
568 // There is no visible background in kiosk mode.
569 if (user_manager::UserManager::Get()->IsLoggedInAsKioskApp())
570 return;
571 wallpaper_cache_.erase(user_id);
572 // Some browser tests do not have a shell instance. As no wallpaper is needed
573 // in these tests anyway, avoid loading one, preventing crashes and speeding
574 // up the tests.
575 if (!ash::Shell::HasInstance())
576 return;
578 WallpaperResolution resolution = GetAppropriateResolution();
579 const bool use_small = (resolution == WALLPAPER_RESOLUTION_SMALL);
581 const base::FilePath* file = NULL;
583 const user_manager::User* user =
584 user_manager::UserManager::Get()->FindUser(user_id);
586 if (user_manager::UserManager::Get()->IsLoggedInAsGuest()) {
587 file =
588 use_small ? &guest_small_wallpaper_file_ : &guest_large_wallpaper_file_;
589 } else if (user && user->GetType() == user_manager::USER_TYPE_CHILD) {
590 file =
591 use_small ? &child_small_wallpaper_file_ : &child_large_wallpaper_file_;
592 } else {
593 file = use_small ? &default_small_wallpaper_file_
594 : &default_large_wallpaper_file_;
596 wallpaper::WallpaperLayout layout =
597 use_small ? wallpaper::WALLPAPER_LAYOUT_CENTER
598 : wallpaper::WALLPAPER_LAYOUT_CENTER_CROPPED;
599 DCHECK(file);
600 if (!default_wallpaper_image_.get() ||
601 default_wallpaper_image_->file_path() != file->value()) {
602 default_wallpaper_image_.reset();
603 if (!file->empty()) {
604 loaded_wallpapers_for_test_++;
605 StartLoadAndSetDefaultWallpaper(
606 *file, layout, on_finish.Pass(), &default_wallpaper_image_);
607 return;
610 CreateSolidDefaultWallpaper();
612 // 1x1 wallpaper is actually solid color, so it should be stretched.
613 if (default_wallpaper_image_->image().width() == 1 &&
614 default_wallpaper_image_->image().height() == 1)
615 layout = wallpaper::WALLPAPER_LAYOUT_STRETCH;
617 ash::Shell::GetInstance()->desktop_background_controller()->SetWallpaperImage(
618 default_wallpaper_image_->image(), layout);
621 void WallpaperManager::SetUserWallpaperInfo(const std::string& user_id,
622 const WallpaperInfo& info,
623 bool is_persistent) {
624 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
625 current_user_wallpaper_info_ = info;
626 if (!is_persistent)
627 return;
629 PrefService* local_state = g_browser_process->local_state();
630 DictionaryPrefUpdate wallpaper_update(local_state,
631 wallpaper::kUsersWallpaperInfo);
633 base::DictionaryValue* wallpaper_info_dict = new base::DictionaryValue();
634 wallpaper_info_dict->SetString(kNewWallpaperDateNodeName,
635 base::Int64ToString(info.date.ToInternalValue()));
636 wallpaper_info_dict->SetString(kNewWallpaperLocationNodeName, info.location);
637 wallpaper_info_dict->SetInteger(kNewWallpaperLayoutNodeName, info.layout);
638 wallpaper_info_dict->SetInteger(kNewWallpaperTypeNodeName, info.type);
639 wallpaper_update->SetWithoutPathExpansion(user_id, wallpaper_info_dict);
642 void WallpaperManager::ScheduleSetUserWallpaper(const std::string& user_id,
643 bool delayed) {
644 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
645 // Some unit tests come here without a UserManager or without a pref system.q
646 if (!user_manager::UserManager::IsInitialized() ||
647 !g_browser_process->local_state()) {
648 return;
651 // There is no visible background in kiosk mode.
652 if (user_manager::UserManager::Get()->IsLoggedInAsKioskApp())
653 return;
654 // Guest user, regular user in ephemeral mode, or kiosk app.
655 const user_manager::User* user =
656 user_manager::UserManager::Get()->FindUser(user_id);
657 if (user_manager::UserManager::Get()->IsUserNonCryptohomeDataEphemeral(
658 user_id) ||
659 (user != NULL && user->GetType() == user_manager::USER_TYPE_KIOSK_APP)) {
660 InitInitialUserWallpaper(user_id, false);
661 GetPendingWallpaper(user_id, delayed)->ResetSetDefaultWallpaper();
662 return;
665 if (!user_manager::UserManager::Get()->IsKnownUser(user_id))
666 return;
668 last_selected_user_ = user_id;
670 WallpaperInfo info;
672 if (!GetUserWallpaperInfo(user_id, &info)) {
673 InitInitialUserWallpaper(user_id, true);
674 GetUserWallpaperInfo(user_id, &info);
677 gfx::ImageSkia user_wallpaper;
678 current_user_wallpaper_info_ = info;
679 if (GetWallpaperFromCache(user_id, &user_wallpaper)) {
680 GetPendingWallpaper(user_id, delayed)
681 ->ResetSetWallpaperImage(user_wallpaper, info);
682 } else {
683 if (info.location.empty()) {
684 // Uses default built-in wallpaper when file is empty. Eventually, we
685 // will only ship one built-in wallpaper in ChromeOS image.
686 GetPendingWallpaper(user_id, delayed)->ResetSetDefaultWallpaper();
687 return;
690 if (info.type == user_manager::User::CUSTOMIZED ||
691 info.type == user_manager::User::POLICY) {
692 const char* sub_dir = GetCustomWallpaperSubdirForCurrentResolution();
693 // Wallpaper is not resized when layout is
694 // wallpaper::WALLPAPER_LAYOUT_CENTER.
695 // Original wallpaper should be used in this case.
696 // TODO(bshe): Generates cropped custom wallpaper for CENTER layout.
697 if (info.layout == wallpaper::WALLPAPER_LAYOUT_CENTER)
698 sub_dir = wallpaper::kOriginalWallpaperSubDir;
699 base::FilePath wallpaper_path = GetCustomWallpaperDir(sub_dir);
700 wallpaper_path = wallpaper_path.Append(info.location);
701 CustomWallpaperMap::iterator it = wallpaper_cache_.find(user_id);
702 // Do not try to load the wallpaper if the path is the same. Since loading
703 // could still be in progress, we ignore the existence of the image.
704 if (it != wallpaper_cache_.end() && it->second.first == wallpaper_path)
705 return;
707 // Set the new path and reset the existing image - the image will be
708 // added once it becomes available.
709 wallpaper_cache_[user_id] =
710 CustomWallpaperElement(wallpaper_path, gfx::ImageSkia());
711 loaded_wallpapers_for_test_++;
713 GetPendingWallpaper(user_id, delayed)
714 ->ResetSetCustomWallpaper(info, wallpaper_path);
715 return;
718 // Load downloaded ONLINE or converted DEFAULT wallpapers.
719 GetPendingWallpaper(user_id, delayed)->ResetLoadWallpaper(info);
723 void WallpaperManager::SetWallpaperFromImageSkia(
724 const std::string& user_id,
725 const gfx::ImageSkia& image,
726 wallpaper::WallpaperLayout layout,
727 bool update_wallpaper) {
728 DCHECK(user_manager::UserManager::Get()->IsUserLoggedIn());
730 // There is no visible background in kiosk mode.
731 if (user_manager::UserManager::Get()->IsLoggedInAsKioskApp())
732 return;
733 WallpaperInfo info;
734 info.layout = layout;
736 // This is an API call and we do not know the path. So we set the image, but
737 // no path.
738 wallpaper_cache_[user_id] = CustomWallpaperElement(base::FilePath(), image);
740 if (update_wallpaper) {
741 GetPendingWallpaper(last_selected_user_, false /* Not delayed */)
742 ->ResetSetWallpaperImage(image, info);
747 // WallpaperManager, private: --------------------------------------------------
749 WallpaperManager::PendingWallpaper* WallpaperManager::GetPendingWallpaper(
750 const std::string& user_id,
751 bool delayed) {
752 if (!pending_inactive_) {
753 loading_.push_back(new WallpaperManager::PendingWallpaper(
754 (delayed ? GetWallpaperLoadDelay()
755 : base::TimeDelta::FromMilliseconds(0)),
756 user_id));
757 pending_inactive_ = loading_.back().get();
759 return pending_inactive_;
762 void WallpaperManager::RemovePendingWallpaperFromList(
763 PendingWallpaper* pending) {
764 DCHECK(loading_.size() > 0);
765 for (WallpaperManager::PendingList::iterator i = loading_.begin();
766 i != loading_.end();
767 ++i) {
768 if (i->get() == pending) {
769 loading_.erase(i);
770 break;
774 if (loading_.empty())
775 FOR_EACH_OBSERVER(Observer, observers_, OnPendingListEmptyForTesting());
778 void WallpaperManager::ClearObsoleteWallpaperPrefs() {
779 PrefService* prefs = g_browser_process->local_state();
780 DictionaryPrefUpdate wallpaper_properties_pref(prefs,
781 wallpaper::kUserWallpapersProperties);
782 wallpaper_properties_pref->Clear();
783 DictionaryPrefUpdate wallpapers_pref(prefs, wallpaper::kUserWallpapers);
784 wallpapers_pref->Clear();
787 void WallpaperManager::InitializeRegisteredDeviceWallpaper() {
788 if (user_manager::UserManager::Get()->IsUserLoggedIn())
789 return;
791 bool disable_boot_animation =
792 GetCommandLine()->HasSwitch(switches::kDisableBootAnimation);
793 bool show_users = true;
794 bool result = CrosSettings::Get()->GetBoolean(
795 kAccountsPrefShowUserNamesOnSignIn, &show_users);
796 DCHECK(result) << "Unable to fetch setting "
797 << kAccountsPrefShowUserNamesOnSignIn;
798 const user_manager::UserList& users =
799 user_manager::UserManager::Get()->GetUsers();
800 int public_session_user_index = FindPublicSession(users);
801 if ((!show_users && public_session_user_index == -1) || users.empty()) {
802 // Boot into sign in form, preload default wallpaper.
803 SetDefaultWallpaperDelayed(chromeos::login::kSignInUser);
804 return;
807 if (!disable_boot_animation) {
808 int index = public_session_user_index != -1 ? public_session_user_index : 0;
809 // Normal boot, load user wallpaper.
810 // If normal boot animation is disabled wallpaper would be set
811 // asynchronously once user pods are loaded.
812 SetUserWallpaperDelayed(users[index]->email());
816 bool WallpaperManager::GetUserWallpaperInfo(const std::string& user_id,
817 WallpaperInfo* info) const {
818 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
820 if (user_manager::UserManager::Get()->IsUserNonCryptohomeDataEphemeral(
821 user_id)) {
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.
827 return true;
830 const base::DictionaryValue* info_dict;
831 if (!g_browser_process->local_state()
832 ->GetDictionary(wallpaper::kUsersWallpaperInfo)
833 ->GetDictionaryWithoutPathExpansion(user_id, &info_dict)) {
834 return false;
837 // Use temporary variables to keep |info| untouched in the error case.
838 std::string location;
839 if (!info_dict->GetString(kNewWallpaperLocationNodeName, &location))
840 return false;
841 int layout;
842 if (!info_dict->GetInteger(kNewWallpaperLayoutNodeName, &layout))
843 return false;
844 int type;
845 if (!info_dict->GetInteger(kNewWallpaperTypeNodeName, &type))
846 return false;
847 std::string date_string;
848 if (!info_dict->GetString(kNewWallpaperDateNodeName, &date_string))
849 return false;
850 int64 date_val;
851 if (!base::StringToInt64(date_string, &date_val))
852 return false;
854 info->location = location;
855 info->layout = static_cast<wallpaper::WallpaperLayout>(layout);
856 info->type = static_cast<user_manager::User::WallpaperType>(type);
857 info->date = base::Time::FromInternalValue(date_val);
858 return true;
861 void WallpaperManager::OnWallpaperDecoded(
862 const std::string& user_id,
863 wallpaper::WallpaperLayout layout,
864 bool update_wallpaper,
865 MovableOnDestroyCallbackHolder on_finish,
866 const user_manager::UserImage& user_image) {
867 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
868 TRACE_EVENT_ASYNC_END0("ui", "LoadAndDecodeWallpaper", this);
870 // If decoded wallpaper is empty, we have probably failed to decode the file.
871 // Use default wallpaper in this case.
872 if (user_image.image().isNull()) {
873 // Updates user pref to default wallpaper.
874 WallpaperInfo info = {"",
875 wallpaper::WALLPAPER_LAYOUT_CENTER_CROPPED,
876 user_manager::User::DEFAULT,
877 base::Time::Now().LocalMidnight()};
878 SetUserWallpaperInfo(user_id, info, true);
880 if (update_wallpaper)
881 DoSetDefaultWallpaper(user_id, on_finish.Pass());
882 return;
885 // Update the image, but keep the path which was set earlier.
886 wallpaper_cache_[user_id].second = user_image.image();
888 if (update_wallpaper) {
889 ash::Shell::GetInstance()
890 ->desktop_background_controller()
891 ->SetWallpaperImage(user_image.image(), layout);
895 void WallpaperManager::StartLoad(const std::string& user_id,
896 const WallpaperInfo& info,
897 bool update_wallpaper,
898 const base::FilePath& wallpaper_path,
899 MovableOnDestroyCallbackHolder on_finish) {
900 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
901 TRACE_EVENT_ASYNC_BEGIN0("ui", "LoadAndDecodeWallpaper", this);
902 if (update_wallpaper) {
903 // We are now about to change the wallpaper, so update the path and remove
904 // the existing image.
905 wallpaper_cache_[user_id] = CustomWallpaperElement(wallpaper_path,
906 gfx::ImageSkia());
908 wallpaper_loader_->Start(wallpaper_path.value(),
909 0, // Do not crop.
910 base::Bind(&WallpaperManager::OnWallpaperDecoded,
911 weak_factory_.GetWeakPtr(),
912 user_id,
913 info.layout,
914 update_wallpaper,
915 base::Passed(on_finish.Pass())));
918 void WallpaperManager::SetCustomizedDefaultWallpaperAfterCheck(
919 const GURL& wallpaper_url,
920 const base::FilePath& downloaded_file,
921 scoped_ptr<CustomizedWallpaperRescaledFiles> rescaled_files) {
922 PrefService* pref_service = g_browser_process->local_state();
924 std::string current_url =
925 pref_service->GetString(prefs::kCustomizationDefaultWallpaperURL);
926 if (current_url != wallpaper_url.spec() || !rescaled_files->AllSizesExist()) {
927 DCHECK(rescaled_files->downloaded_exists());
929 // Either resized images do not exist or cached version is incorrect.
930 // Need to start resize again.
931 wallpaper_loader_->Start(
932 downloaded_file.value(),
933 0, // Do not crop.
934 base::Bind(&WallpaperManager::OnCustomizedDefaultWallpaperDecoded,
935 weak_factory_.GetWeakPtr(),
936 wallpaper_url,
937 base::Passed(rescaled_files.Pass())));
938 } else {
939 SetDefaultWallpaperPath(rescaled_files->path_rescaled_small(),
940 scoped_ptr<gfx::ImageSkia>().Pass(),
941 rescaled_files->path_rescaled_large(),
942 scoped_ptr<gfx::ImageSkia>().Pass());
946 void WallpaperManager::OnCustomizedDefaultWallpaperResized(
947 const GURL& wallpaper_url,
948 scoped_ptr<CustomizedWallpaperRescaledFiles> rescaled_files,
949 scoped_ptr<bool> success,
950 scoped_ptr<gfx::ImageSkia> small_wallpaper_image,
951 scoped_ptr<gfx::ImageSkia> large_wallpaper_image) {
952 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
953 DCHECK(rescaled_files);
954 DCHECK(success.get());
955 if (!*success) {
956 LOG(WARNING) << "Failed to save resized customized default wallpaper";
957 return;
959 PrefService* pref_service = g_browser_process->local_state();
960 pref_service->SetString(prefs::kCustomizationDefaultWallpaperURL,
961 wallpaper_url.spec());
962 SetDefaultWallpaperPath(rescaled_files->path_rescaled_small(),
963 small_wallpaper_image.Pass(),
964 rescaled_files->path_rescaled_large(),
965 large_wallpaper_image.Pass());
966 VLOG(1) << "Customized default wallpaper applied.";
969 size_t WallpaperManager::GetPendingListSizeForTesting() const {
970 return loading_.size();
973 void WallpaperManager::UserChangedChildStatus(user_manager::User* user) {
974 SetUserWallpaperNow(user->email());
977 void WallpaperManager::OnDefaultWallpaperDecoded(
978 const base::FilePath& path,
979 const wallpaper::WallpaperLayout layout,
980 scoped_ptr<user_manager::UserImage>* result_out,
981 MovableOnDestroyCallbackHolder on_finish,
982 const user_manager::UserImage& user_image) {
983 result_out->reset(new user_manager::UserImage(user_image));
984 ash::Shell::GetInstance()->desktop_background_controller()->SetWallpaperImage(
985 user_image.image(), layout);
988 void WallpaperManager::StartLoadAndSetDefaultWallpaper(
989 const base::FilePath& path,
990 const wallpaper::WallpaperLayout layout,
991 MovableOnDestroyCallbackHolder on_finish,
992 scoped_ptr<user_manager::UserImage>* result_out) {
993 wallpaper_loader_->Start(
994 path.value(),
995 0, // Do not crop.
996 base::Bind(&WallpaperManager::OnDefaultWallpaperDecoded,
997 weak_factory_.GetWeakPtr(),
998 path,
999 layout,
1000 base::Unretained(result_out),
1001 base::Passed(on_finish.Pass())));
1004 void WallpaperManager::SetDefaultWallpaperPath(
1005 const base::FilePath& default_small_wallpaper_file,
1006 scoped_ptr<gfx::ImageSkia> small_wallpaper_image,
1007 const base::FilePath& default_large_wallpaper_file,
1008 scoped_ptr<gfx::ImageSkia> large_wallpaper_image) {
1009 default_small_wallpaper_file_ = default_small_wallpaper_file;
1010 default_large_wallpaper_file_ = default_large_wallpaper_file;
1012 ash::DesktopBackgroundController* dbc =
1013 ash::Shell::GetInstance()->desktop_background_controller();
1015 // |need_update_screen| is true if the previous default wallpaper is visible
1016 // now, so we need to update wallpaper on the screen.
1018 // Layout is ignored here, so wallpaper::WALLPAPER_LAYOUT_CENTER is used
1019 // as a placeholder only.
1020 const bool need_update_screen =
1021 default_wallpaper_image_.get() &&
1022 dbc->WallpaperIsAlreadyLoaded(default_wallpaper_image_->image(),
1023 false /* compare_layouts */,
1024 wallpaper::WALLPAPER_LAYOUT_CENTER);
1026 default_wallpaper_image_.reset();
1027 if (GetAppropriateResolution() == WALLPAPER_RESOLUTION_SMALL) {
1028 if (small_wallpaper_image) {
1029 default_wallpaper_image_.reset(
1030 new user_manager::UserImage(*small_wallpaper_image));
1031 default_wallpaper_image_->set_file_path(
1032 default_small_wallpaper_file.value());
1034 } else {
1035 if (large_wallpaper_image) {
1036 default_wallpaper_image_.reset(
1037 new user_manager::UserImage(*large_wallpaper_image));
1038 default_wallpaper_image_->set_file_path(
1039 default_large_wallpaper_file.value());
1043 if (need_update_screen) {
1044 DoSetDefaultWallpaper(std::string(),
1045 MovableOnDestroyCallbackHolder().Pass());
1049 } // namespace chromeos