Popular sites on the NTP: check that experiment group StartsWith (rather than IS...
[chromium-blink-merge.git] / chrome / browser / chromeos / login / users / wallpaper / wallpaper_manager.cc
blob1c4aa18b8dac898954dd03cac5ef3f3b3de7347b
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 WallpaperManager* wallpaper_manager = nullptr;
69 // The amount of delay before starts to move custom wallpapers to the new place.
70 const int kMoveCustomWallpaperDelaySeconds = 30;
72 const int kCacheWallpaperDelayMs = 500;
74 // Names of nodes with info about wallpaper in |kUserWallpapersProperties|
75 // dictionary.
76 const char kNewWallpaperDateNodeName[] = "date";
77 const char kNewWallpaperLayoutNodeName[] = "layout";
78 const char kNewWallpaperLocationNodeName[] = "file";
79 const char kNewWallpaperTypeNodeName[] = "type";
81 // These global default values are used to set customized default
82 // wallpaper path in WallpaperManager::InitializeWallpaper().
83 base::FilePath GetCustomizedWallpaperDefaultRescaledFileName(
84 const std::string& suffix) {
85 const base::FilePath default_downloaded_file_name =
86 ServicesCustomizationDocument::GetCustomizedWallpaperDownloadedFileName();
87 const base::FilePath default_cache_dir =
88 ServicesCustomizationDocument::GetCustomizedWallpaperCacheDir();
89 if (default_downloaded_file_name.empty() || default_cache_dir.empty())
90 return base::FilePath();
91 return default_cache_dir.Append(
92 default_downloaded_file_name.BaseName().value() + suffix);
95 // Whether DesktopBackgroundController should start with customized default
96 // wallpaper in WallpaperManager::InitializeWallpaper() or not.
97 bool ShouldUseCustomizedDefaultWallpaper() {
98 PrefService* pref_service = g_browser_process->local_state();
100 return !(pref_service->FindPreference(
101 prefs::kCustomizationDefaultWallpaperURL)->IsDefaultValue());
104 // Returns index of the first public session user found in |users|
105 // or -1 otherwise.
106 int FindPublicSession(const user_manager::UserList& users) {
107 int index = -1;
108 int i = 0;
109 for (user_manager::UserList::const_iterator it = users.begin();
110 it != users.end();
111 ++it, ++i) {
112 if ((*it)->GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT) {
113 index = i;
114 break;
118 return index;
121 } // namespace
123 // This is "wallpaper either scheduled to load, or loading right now".
125 // While enqueued, it defines moment in the future, when it will be loaded.
126 // Enqueued but not started request might be updated by subsequent load
127 // request. Therefore it's created empty, and updated being enqueued.
129 // PendingWallpaper is owned by WallpaperManager, but reference to this object
130 // is passed to other threads by PostTask() calls, therefore it is
131 // RefCountedThreadSafe.
132 class WallpaperManager::PendingWallpaper :
133 public base::RefCountedThreadSafe<PendingWallpaper> {
134 public:
135 // Do LoadWallpaper() - image not found in cache.
136 PendingWallpaper(
137 const base::TimeDelta delay,
138 const std::string& user_id)
139 : user_id_(user_id),
140 default_(false),
141 on_finish_(new MovableOnDestroyCallback(
142 base::Bind(&WallpaperManager::PendingWallpaper::OnWallpaperSet,
143 this))) {
144 timer.Start(
145 FROM_HERE,
146 delay,
147 base::Bind(&WallpaperManager::PendingWallpaper::ProcessRequest, this));
150 // There are 4 cases in SetUserWallpaper:
151 // 1) gfx::ImageSkia is found in cache.
152 // - Schedule task to (probably) resize it and install:
153 // call ash::Shell::GetInstance()->desktop_background_controller()->
154 // SetCustomWallpaper(user_wallpaper, layout);
155 // 2) WallpaperInfo is found in cache
156 // - need to LoadWallpaper(), resize and install.
157 // 3) wallpaper path is not NULL, load image URL, then resize, etc...
158 // 4) SetDefaultWallpaper (either on some error, or when user is new).
159 void ResetSetWallpaperImage(const gfx::ImageSkia& image,
160 const wallpaper::WallpaperInfo& info) {
161 SetMode(image, info, base::FilePath(), false);
164 void ResetLoadWallpaper(const wallpaper::WallpaperInfo& info) {
165 SetMode(gfx::ImageSkia(), info, base::FilePath(), false);
168 void ResetSetCustomWallpaper(const wallpaper::WallpaperInfo& info,
169 const base::FilePath& wallpaper_path) {
170 SetMode(gfx::ImageSkia(), info, wallpaper_path, false);
173 void ResetSetDefaultWallpaper() {
174 SetMode(gfx::ImageSkia(), WallpaperInfo(), base::FilePath(), true);
177 private:
178 friend class base::RefCountedThreadSafe<PendingWallpaper>;
180 ~PendingWallpaper() {}
182 // All Reset*() methods use SetMode() to set object to new state.
183 void SetMode(const gfx::ImageSkia& image,
184 const wallpaper::WallpaperInfo& info,
185 const base::FilePath& wallpaper_path,
186 const bool is_default) {
187 user_wallpaper_ = image;
188 info_ = info;
189 wallpaper_path_ = wallpaper_path;
190 default_ = is_default;
193 // This method is usually triggered by timer to actually load request.
194 void ProcessRequest() {
195 DCHECK_CURRENTLY_ON(BrowserThread::UI);
197 timer.Stop(); // Erase reference to self.
199 WallpaperManager* manager = WallpaperManager::Get();
200 if (manager->pending_inactive_ == this)
201 manager->pending_inactive_ = NULL;
203 started_load_at_ = base::Time::Now();
205 if (default_) {
206 manager->DoSetDefaultWallpaper(user_id_, on_finish_.Pass());
207 } else if (!user_wallpaper_.isNull()) {
208 ash::Shell::GetInstance()
209 ->desktop_background_controller()
210 ->SetWallpaperImage(user_wallpaper_, info_.layout);
211 } else if (!wallpaper_path_.empty()) {
212 manager->task_runner_->PostTask(
213 FROM_HERE,
214 base::Bind(&WallpaperManager::GetCustomWallpaperInternal,
215 user_id_,
216 info_,
217 wallpaper_path_,
218 true /* update wallpaper */,
219 base::Passed(on_finish_.Pass()),
220 manager->weak_factory_.GetWeakPtr()));
221 } else if (!info_.location.empty()) {
222 manager->LoadWallpaper(user_id_, info_, true, on_finish_.Pass());
223 } else {
224 // PendingWallpaper was created and never initialized?
225 NOTREACHED();
226 // Error. Do not record time.
227 started_load_at_ = base::Time();
229 on_finish_.reset();
232 // This method is called by callback, when load request is finished.
233 void OnWallpaperSet() {
234 DCHECK_CURRENTLY_ON(BrowserThread::UI);
236 // The only known case for this check to fail is global destruction during
237 // wallpaper load. It should never happen.
238 if (!BrowserThread::CurrentlyOn(BrowserThread::UI))
239 return; // We are in a process of global destruction.
241 timer.Stop(); // Erase reference to self.
243 WallpaperManager* manager = WallpaperManager::Get();
244 if (!started_load_at_.is_null()) {
245 const base::TimeDelta elapsed = base::Time::Now() - started_load_at_;
246 manager->SaveLastLoadTime(elapsed);
248 if (manager->pending_inactive_ == this) {
249 // ProcessRequest() was never executed.
250 manager->pending_inactive_ = NULL;
253 // Destroy self.
254 manager->RemovePendingWallpaperFromList(this);
257 std::string user_id_;
258 wallpaper::WallpaperInfo info_;
259 gfx::ImageSkia user_wallpaper_;
260 base::FilePath wallpaper_path_;
262 // Load default wallpaper instead of user image.
263 bool default_;
265 // This is "on destroy" callback that will call OnWallpaperSet() when
266 // image will be loaded.
267 wallpaper::MovableOnDestroyCallbackHolder on_finish_;
268 base::OneShotTimer<WallpaperManager::PendingWallpaper> timer;
270 // Load start time to calculate duration.
271 base::Time started_load_at_;
273 DISALLOW_COPY_AND_ASSIGN(PendingWallpaper);
276 // WallpaperManager, public: ---------------------------------------------------
278 WallpaperManager::~WallpaperManager() {
279 show_user_name_on_signin_subscription_.reset();
280 user_manager::UserManager::Get()->RemoveSessionStateObserver(this);
281 weak_factory_.InvalidateWeakPtrs();
284 // static
285 void WallpaperManager::Initialize() {
286 CHECK(!wallpaper_manager);
287 wallpaper_manager = new WallpaperManager();
290 // static
291 WallpaperManager* WallpaperManager::Get() {
292 DCHECK(wallpaper_manager);
293 return wallpaper_manager;
296 // static
297 void WallpaperManager::Shutdown() {
298 CHECK(wallpaper_manager);
299 delete wallpaper_manager;
300 wallpaper_manager = nullptr;
303 WallpaperManager::WallpaperResolution
304 WallpaperManager::GetAppropriateResolution() {
305 DCHECK_CURRENTLY_ON(BrowserThread::UI);
306 gfx::Size size =
307 ash::DesktopBackgroundController::GetMaxDisplaySizeInNative();
308 return (size.width() > wallpaper::kSmallWallpaperMaxWidth ||
309 size.height() > wallpaper::kSmallWallpaperMaxHeight)
310 ? WALLPAPER_RESOLUTION_LARGE
311 : WALLPAPER_RESOLUTION_SMALL;
314 void WallpaperManager::AddObservers() {
315 show_user_name_on_signin_subscription_ =
316 CrosSettings::Get()->AddSettingsObserver(
317 kAccountsPrefShowUserNamesOnSignIn,
318 base::Bind(&WallpaperManager::InitializeRegisteredDeviceWallpaper,
319 weak_factory_.GetWeakPtr()));
322 void WallpaperManager::EnsureLoggedInUserWallpaperLoaded() {
323 // Some browser tests do not have a shell instance. As no wallpaper is needed
324 // in these tests anyway, avoid loading one, preventing crashes and speeding
325 // up the tests.
326 if (!ash::Shell::HasInstance())
327 return;
329 WallpaperInfo info;
330 if (GetLoggedInUserWallpaperInfo(&info)) {
331 UMA_HISTOGRAM_ENUMERATION("Ash.Wallpaper.Type", info.type,
332 user_manager::User::WALLPAPER_TYPE_COUNT);
333 if (info == current_user_wallpaper_info_)
334 return;
336 SetUserWallpaperNow(
337 user_manager::UserManager::Get()->GetLoggedInUser()->email());
340 void WallpaperManager::InitializeWallpaper() {
341 DCHECK_CURRENTLY_ON(BrowserThread::UI);
342 user_manager::UserManager* user_manager = user_manager::UserManager::Get();
344 // Apply device customization.
345 if (ShouldUseCustomizedDefaultWallpaper()) {
346 SetDefaultWallpaperPath(GetCustomizedWallpaperDefaultRescaledFileName(
347 wallpaper::kSmallWallpaperSuffix),
348 scoped_ptr<gfx::ImageSkia>().Pass(),
349 GetCustomizedWallpaperDefaultRescaledFileName(
350 wallpaper::kLargeWallpaperSuffix),
351 scoped_ptr<gfx::ImageSkia>().Pass());
354 base::CommandLine* command_line = GetCommandLine();
355 if (command_line->HasSwitch(chromeos::switches::kGuestSession)) {
356 // Guest wallpaper should be initialized when guest login.
357 // Note: This maybe called before login. So IsLoggedInAsGuest can not be
358 // used here to determine if current user is guest.
359 return;
362 if (command_line->HasSwitch(::switches::kTestType))
363 WizardController::SetZeroDelays();
365 // Zero delays is also set in autotests.
366 if (WizardController::IsZeroDelayEnabled()) {
367 // Ensure tests have some sort of wallpaper.
368 ash::Shell::GetInstance()->desktop_background_controller()->
369 CreateEmptyWallpaper();
370 return;
373 if (!user_manager->IsUserLoggedIn()) {
374 if (!StartupUtils::IsDeviceRegistered())
375 SetDefaultWallpaperDelayed(chromeos::login::kSignInUser);
376 else
377 InitializeRegisteredDeviceWallpaper();
378 return;
380 SetUserWallpaperDelayed(user_manager->GetLoggedInUser()->email());
383 void WallpaperManager::Observe(int type,
384 const content::NotificationSource& source,
385 const content::NotificationDetails& details) {
386 DCHECK_CURRENTLY_ON(BrowserThread::UI);
387 switch (type) {
388 case chrome::NOTIFICATION_LOGIN_USER_CHANGED: {
389 ClearDisposableWallpaperCache();
390 BrowserThread::PostDelayedTask(
391 BrowserThread::UI,
392 FROM_HERE,
393 base::Bind(&WallpaperManager::MoveLoggedInUserCustomWallpaper,
394 weak_factory_.GetWeakPtr()),
395 base::TimeDelta::FromSeconds(kMoveCustomWallpaperDelaySeconds));
396 break;
398 case chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE: {
399 if (!GetCommandLine()->HasSwitch(switches::kDisableBootAnimation)) {
400 BrowserThread::PostDelayedTask(
401 BrowserThread::UI, FROM_HERE,
402 base::Bind(&WallpaperManager::CacheUsersWallpapers,
403 weak_factory_.GetWeakPtr()),
404 base::TimeDelta::FromMilliseconds(kCacheWallpaperDelayMs));
405 } else {
406 should_cache_wallpaper_ = true;
408 break;
410 case chrome::NOTIFICATION_WALLPAPER_ANIMATION_FINISHED: {
411 NotifyAnimationFinished();
412 if (should_cache_wallpaper_) {
413 BrowserThread::PostDelayedTask(
414 BrowserThread::UI, FROM_HERE,
415 base::Bind(&WallpaperManager::CacheUsersWallpapers,
416 weak_factory_.GetWeakPtr()),
417 base::TimeDelta::FromMilliseconds(kCacheWallpaperDelayMs));
418 should_cache_wallpaper_ = false;
420 break;
422 default:
423 NOTREACHED() << "Unexpected notification " << type;
427 void WallpaperManager::RemoveUserWallpaperInfo(const std::string& user_id) {
428 WallpaperInfo info;
429 GetUserWallpaperInfo(user_id, &info);
430 PrefService* prefs = g_browser_process->local_state();
431 DictionaryPrefUpdate prefs_wallpapers_info_update(
432 prefs, wallpaper::kUsersWallpaperInfo);
433 prefs_wallpapers_info_update->RemoveWithoutPathExpansion(user_id, NULL);
434 DeleteUserWallpapers(user_id, info.location);
437 void WallpaperManager::OnPolicyFetched(const std::string& policy,
438 const std::string& user_id,
439 scoped_ptr<std::string> data) {
440 if (!data)
441 return;
443 wallpaper_loader_->Start(
444 data.Pass(),
445 0, // Do not crop.
446 base::Bind(&WallpaperManager::SetPolicyControlledWallpaper,
447 weak_factory_.GetWeakPtr(),
448 user_id));
451 void WallpaperManager::SetCustomWallpaper(
452 const std::string& user_id,
453 const std::string& user_id_hash,
454 const std::string& file,
455 wallpaper::WallpaperLayout layout,
456 user_manager::User::WallpaperType type,
457 const gfx::ImageSkia& image,
458 bool update_wallpaper) {
459 DCHECK_CURRENTLY_ON(BrowserThread::UI);
461 // There is no visible background in kiosk mode.
462 if (user_manager::UserManager::Get()->IsLoggedInAsKioskApp())
463 return;
465 // Don't allow custom wallpapers while policy is in effect.
466 if (type != user_manager::User::POLICY && IsPolicyControlled(user_id))
467 return;
469 base::FilePath wallpaper_path = GetCustomWallpaperPath(
470 wallpaper::kOriginalWallpaperSubDir, user_id_hash, file);
472 // If decoded wallpaper is empty, we have probably failed to decode the file.
473 // Use default wallpaper in this case.
474 if (image.isNull()) {
475 SetDefaultWallpaperDelayed(user_id);
476 return;
479 const user_manager::User* user =
480 user_manager::UserManager::Get()->FindUser(user_id);
481 CHECK(user);
482 bool is_persistent =
483 !user_manager::UserManager::Get()->IsUserNonCryptohomeDataEphemeral(
484 user_id) ||
485 (type == user_manager::User::POLICY &&
486 user->GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT);
488 WallpaperInfo wallpaper_info = {
489 wallpaper_path.value(),
490 layout,
491 type,
492 // Date field is not used.
493 base::Time::Now().LocalMidnight()
495 if (is_persistent) {
496 image.EnsureRepsForSupportedScales();
497 scoped_ptr<gfx::ImageSkia> deep_copy(image.DeepCopy());
498 // Block shutdown on this task. Otherwise, we may lose the custom wallpaper
499 // that the user selected.
500 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner =
501 BrowserThread::GetBlockingPool()
502 ->GetSequencedTaskRunnerWithShutdownBehavior(
503 sequence_token_, base::SequencedWorkerPool::BLOCK_SHUTDOWN);
504 // TODO(bshe): This may break if RawImage becomes RefCountedMemory.
505 blocking_task_runner->PostTask(
506 FROM_HERE,
507 base::Bind(&WallpaperManager::SaveCustomWallpaper,
508 user_id_hash,
509 base::FilePath(wallpaper_info.location),
510 wallpaper_info.layout,
511 base::Passed(deep_copy.Pass())));
514 std::string relative_path = base::FilePath(user_id_hash).Append(file).value();
515 // User's custom wallpaper path is determined by relative path and the
516 // appropriate wallpaper resolution in GetCustomWallpaperInternal.
517 WallpaperInfo info = {
518 relative_path,
519 layout,
520 type,
521 base::Time::Now().LocalMidnight()
523 SetUserWallpaperInfo(user_id, info, is_persistent);
524 if (update_wallpaper) {
525 GetPendingWallpaper(user_id, false)->ResetSetWallpaperImage(image, info);
528 wallpaper_cache_[user_id] = CustomWallpaperElement(wallpaper_path, image);
531 void WallpaperManager::SetDefaultWallpaperNow(const std::string& user_id) {
532 GetPendingWallpaper(user_id, false)->ResetSetDefaultWallpaper();
535 void WallpaperManager::SetDefaultWallpaperDelayed(const std::string& user_id) {
536 GetPendingWallpaper(user_id, true)->ResetSetDefaultWallpaper();
539 void WallpaperManager::DoSetDefaultWallpaper(
540 const std::string& user_id,
541 MovableOnDestroyCallbackHolder on_finish) {
542 // There is no visible background in kiosk mode.
543 if (user_manager::UserManager::Get()->IsLoggedInAsKioskApp())
544 return;
545 wallpaper_cache_.erase(user_id);
546 // Some browser tests do not have a shell instance. As no wallpaper is needed
547 // in these tests anyway, avoid loading one, preventing crashes and speeding
548 // up the tests.
549 if (!ash::Shell::HasInstance())
550 return;
552 WallpaperResolution resolution = GetAppropriateResolution();
553 const bool use_small = (resolution == WALLPAPER_RESOLUTION_SMALL);
555 const base::FilePath* file = NULL;
557 const user_manager::User* user =
558 user_manager::UserManager::Get()->FindUser(user_id);
560 if (user_manager::UserManager::Get()->IsLoggedInAsGuest()) {
561 file =
562 use_small ? &guest_small_wallpaper_file_ : &guest_large_wallpaper_file_;
563 } else if (user && user->GetType() == user_manager::USER_TYPE_CHILD) {
564 file =
565 use_small ? &child_small_wallpaper_file_ : &child_large_wallpaper_file_;
566 } else {
567 file = use_small ? &default_small_wallpaper_file_
568 : &default_large_wallpaper_file_;
570 wallpaper::WallpaperLayout layout =
571 use_small ? wallpaper::WALLPAPER_LAYOUT_CENTER
572 : wallpaper::WALLPAPER_LAYOUT_CENTER_CROPPED;
573 DCHECK(file);
574 if (!default_wallpaper_image_.get() ||
575 default_wallpaper_image_->file_path() != file->value()) {
576 default_wallpaper_image_.reset();
577 if (!file->empty()) {
578 loaded_wallpapers_for_test_++;
579 StartLoadAndSetDefaultWallpaper(
580 *file, layout, on_finish.Pass(), &default_wallpaper_image_);
581 return;
584 CreateSolidDefaultWallpaper();
586 // 1x1 wallpaper is actually solid color, so it should be stretched.
587 if (default_wallpaper_image_->image().width() == 1 &&
588 default_wallpaper_image_->image().height() == 1)
589 layout = wallpaper::WALLPAPER_LAYOUT_STRETCH;
591 ash::Shell::GetInstance()->desktop_background_controller()->SetWallpaperImage(
592 default_wallpaper_image_->image(), layout);
595 void WallpaperManager::SetUserWallpaperInfo(const std::string& user_id,
596 const WallpaperInfo& info,
597 bool is_persistent) {
598 DCHECK_CURRENTLY_ON(BrowserThread::UI);
599 current_user_wallpaper_info_ = info;
600 if (!is_persistent)
601 return;
603 PrefService* local_state = g_browser_process->local_state();
604 DictionaryPrefUpdate wallpaper_update(local_state,
605 wallpaper::kUsersWallpaperInfo);
607 base::DictionaryValue* wallpaper_info_dict = new base::DictionaryValue();
608 wallpaper_info_dict->SetString(kNewWallpaperDateNodeName,
609 base::Int64ToString(info.date.ToInternalValue()));
610 wallpaper_info_dict->SetString(kNewWallpaperLocationNodeName, info.location);
611 wallpaper_info_dict->SetInteger(kNewWallpaperLayoutNodeName, info.layout);
612 wallpaper_info_dict->SetInteger(kNewWallpaperTypeNodeName, info.type);
613 wallpaper_update->SetWithoutPathExpansion(user_id, wallpaper_info_dict);
616 void WallpaperManager::ScheduleSetUserWallpaper(const std::string& user_id,
617 bool delayed) {
618 DCHECK_CURRENTLY_ON(BrowserThread::UI);
619 // Some unit tests come here without a UserManager or without a pref system.q
620 if (!user_manager::UserManager::IsInitialized() ||
621 !g_browser_process->local_state()) {
622 return;
625 // There is no visible background in kiosk mode.
626 if (user_manager::UserManager::Get()->IsLoggedInAsKioskApp())
627 return;
628 // Guest user, regular user in ephemeral mode, or kiosk app.
629 const user_manager::User* user =
630 user_manager::UserManager::Get()->FindUser(user_id);
631 if (user_manager::UserManager::Get()->IsUserNonCryptohomeDataEphemeral(
632 user_id) ||
633 (user != NULL && user->GetType() == user_manager::USER_TYPE_KIOSK_APP)) {
634 InitInitialUserWallpaper(user_id, false);
635 GetPendingWallpaper(user_id, delayed)->ResetSetDefaultWallpaper();
636 return;
639 if (!user_manager::UserManager::Get()->IsKnownUser(user_id))
640 return;
642 last_selected_user_ = user_id;
644 WallpaperInfo info;
646 if (!GetUserWallpaperInfo(user_id, &info)) {
647 InitInitialUserWallpaper(user_id, true);
648 GetUserWallpaperInfo(user_id, &info);
651 gfx::ImageSkia user_wallpaper;
652 current_user_wallpaper_info_ = info;
653 if (GetWallpaperFromCache(user_id, &user_wallpaper)) {
654 GetPendingWallpaper(user_id, delayed)
655 ->ResetSetWallpaperImage(user_wallpaper, info);
656 } else {
657 if (info.location.empty()) {
658 // Uses default built-in wallpaper when file is empty. Eventually, we
659 // will only ship one built-in wallpaper in ChromeOS image.
660 GetPendingWallpaper(user_id, delayed)->ResetSetDefaultWallpaper();
661 return;
664 if (info.type == user_manager::User::CUSTOMIZED ||
665 info.type == user_manager::User::POLICY) {
666 const char* sub_dir = GetCustomWallpaperSubdirForCurrentResolution();
667 // Wallpaper is not resized when layout is
668 // wallpaper::WALLPAPER_LAYOUT_CENTER.
669 // Original wallpaper should be used in this case.
670 // TODO(bshe): Generates cropped custom wallpaper for CENTER layout.
671 if (info.layout == wallpaper::WALLPAPER_LAYOUT_CENTER)
672 sub_dir = wallpaper::kOriginalWallpaperSubDir;
673 base::FilePath wallpaper_path = GetCustomWallpaperDir(sub_dir);
674 wallpaper_path = wallpaper_path.Append(info.location);
675 CustomWallpaperMap::iterator it = wallpaper_cache_.find(user_id);
676 // Do not try to load the wallpaper if the path is the same. Since loading
677 // could still be in progress, we ignore the existence of the image.
678 if (it != wallpaper_cache_.end() && it->second.first == wallpaper_path)
679 return;
681 // Set the new path and reset the existing image - the image will be
682 // added once it becomes available.
683 wallpaper_cache_[user_id] =
684 CustomWallpaperElement(wallpaper_path, gfx::ImageSkia());
685 loaded_wallpapers_for_test_++;
687 GetPendingWallpaper(user_id, delayed)
688 ->ResetSetCustomWallpaper(info, wallpaper_path);
689 return;
692 // Load downloaded ONLINE or converted DEFAULT wallpapers.
693 GetPendingWallpaper(user_id, delayed)->ResetLoadWallpaper(info);
697 void WallpaperManager::SetWallpaperFromImageSkia(
698 const std::string& user_id,
699 const gfx::ImageSkia& image,
700 wallpaper::WallpaperLayout layout,
701 bool update_wallpaper) {
702 DCHECK(user_manager::UserManager::Get()->IsUserLoggedIn());
704 // There is no visible background in kiosk mode.
705 if (user_manager::UserManager::Get()->IsLoggedInAsKioskApp())
706 return;
707 WallpaperInfo info;
708 info.layout = layout;
710 // This is an API call and we do not know the path. So we set the image, but
711 // no path.
712 wallpaper_cache_[user_id] = CustomWallpaperElement(base::FilePath(), image);
714 if (update_wallpaper) {
715 GetPendingWallpaper(last_selected_user_, false /* Not delayed */)
716 ->ResetSetWallpaperImage(image, info);
721 // WallpaperManager, private: --------------------------------------------------
723 WallpaperManager::WallpaperManager()
724 : pending_inactive_(NULL), weak_factory_(this) {
725 wallpaper::WallpaperManagerBase::SetPathIds(
726 chrome::DIR_USER_DATA, chrome::DIR_CHROMEOS_WALLPAPERS,
727 chrome::DIR_CHROMEOS_CUSTOM_WALLPAPERS);
728 SetDefaultWallpaperPathsFromCommandLine(
729 base::CommandLine::ForCurrentProcess());
730 registrar_.Add(this, chrome::NOTIFICATION_LOGIN_USER_CHANGED,
731 content::NotificationService::AllSources());
732 registrar_.Add(this, chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
733 content::NotificationService::AllSources());
734 registrar_.Add(this, chrome::NOTIFICATION_WALLPAPER_ANIMATION_FINISHED,
735 content::NotificationService::AllSources());
736 sequence_token_ = BrowserThread::GetBlockingPool()->GetNamedSequenceToken(
737 wallpaper::kWallpaperSequenceTokenName);
738 task_runner_ =
739 BrowserThread::GetBlockingPool()
740 ->GetSequencedTaskRunnerWithShutdownBehavior(
741 sequence_token_, base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
742 wallpaper_loader_ =
743 new UserImageLoader(ImageDecoder::ROBUST_JPEG_CODEC, task_runner_);
745 user_manager::UserManager::Get()->AddSessionStateObserver(this);
748 WallpaperManager::PendingWallpaper* WallpaperManager::GetPendingWallpaper(
749 const std::string& user_id,
750 bool delayed) {
751 if (!pending_inactive_) {
752 loading_.push_back(new WallpaperManager::PendingWallpaper(
753 (delayed ? GetWallpaperLoadDelay()
754 : base::TimeDelta::FromMilliseconds(0)),
755 user_id));
756 pending_inactive_ = loading_.back().get();
758 return pending_inactive_;
761 void WallpaperManager::RemovePendingWallpaperFromList(
762 PendingWallpaper* pending) {
763 DCHECK(loading_.size() > 0);
764 for (WallpaperManager::PendingList::iterator i = loading_.begin();
765 i != loading_.end();
766 ++i) {
767 if (i->get() == pending) {
768 loading_.erase(i);
769 break;
773 if (loading_.empty())
774 FOR_EACH_OBSERVER(Observer, observers_, OnPendingListEmptyForTesting());
777 void WallpaperManager::InitializeRegisteredDeviceWallpaper() {
778 if (user_manager::UserManager::Get()->IsUserLoggedIn())
779 return;
781 bool disable_boot_animation =
782 GetCommandLine()->HasSwitch(switches::kDisableBootAnimation);
783 bool show_users = true;
784 bool result = CrosSettings::Get()->GetBoolean(
785 kAccountsPrefShowUserNamesOnSignIn, &show_users);
786 DCHECK(result) << "Unable to fetch setting "
787 << kAccountsPrefShowUserNamesOnSignIn;
788 const user_manager::UserList& users =
789 user_manager::UserManager::Get()->GetUsers();
790 int public_session_user_index = FindPublicSession(users);
791 if ((!show_users && public_session_user_index == -1) || users.empty()) {
792 // Boot into sign in form, preload default wallpaper.
793 SetDefaultWallpaperDelayed(chromeos::login::kSignInUser);
794 return;
797 if (!disable_boot_animation) {
798 int index = public_session_user_index != -1 ? public_session_user_index : 0;
799 // Normal boot, load user wallpaper.
800 // If normal boot animation is disabled wallpaper would be set
801 // asynchronously once user pods are loaded.
802 SetUserWallpaperDelayed(users[index]->email());
806 bool WallpaperManager::GetUserWallpaperInfo(const std::string& user_id,
807 WallpaperInfo* info) const {
808 DCHECK_CURRENTLY_ON(BrowserThread::UI);
810 if (user_manager::UserManager::Get()->IsUserNonCryptohomeDataEphemeral(
811 user_id)) {
812 // Default to the values cached in memory.
813 *info = current_user_wallpaper_info_;
815 // Ephemeral users do not save anything to local state. But we have got
816 // wallpaper info from memory. Returns true.
817 return true;
820 const base::DictionaryValue* info_dict;
821 if (!g_browser_process->local_state()
822 ->GetDictionary(wallpaper::kUsersWallpaperInfo)
823 ->GetDictionaryWithoutPathExpansion(user_id, &info_dict)) {
824 return false;
827 // Use temporary variables to keep |info| untouched in the error case.
828 std::string location;
829 if (!info_dict->GetString(kNewWallpaperLocationNodeName, &location))
830 return false;
831 int layout;
832 if (!info_dict->GetInteger(kNewWallpaperLayoutNodeName, &layout))
833 return false;
834 int type;
835 if (!info_dict->GetInteger(kNewWallpaperTypeNodeName, &type))
836 return false;
837 std::string date_string;
838 if (!info_dict->GetString(kNewWallpaperDateNodeName, &date_string))
839 return false;
840 int64 date_val;
841 if (!base::StringToInt64(date_string, &date_val))
842 return false;
844 info->location = location;
845 info->layout = static_cast<wallpaper::WallpaperLayout>(layout);
846 info->type = static_cast<user_manager::User::WallpaperType>(type);
847 info->date = base::Time::FromInternalValue(date_val);
848 return true;
851 void WallpaperManager::OnWallpaperDecoded(
852 const std::string& user_id,
853 wallpaper::WallpaperLayout layout,
854 bool update_wallpaper,
855 MovableOnDestroyCallbackHolder on_finish,
856 const user_manager::UserImage& user_image) {
857 DCHECK_CURRENTLY_ON(BrowserThread::UI);
858 TRACE_EVENT_ASYNC_END0("ui", "LoadAndDecodeWallpaper", this);
860 // If decoded wallpaper is empty, we have probably failed to decode the file.
861 // Use default wallpaper in this case.
862 if (user_image.image().isNull()) {
863 // Updates user pref to default wallpaper.
864 WallpaperInfo info = {"",
865 wallpaper::WALLPAPER_LAYOUT_CENTER_CROPPED,
866 user_manager::User::DEFAULT,
867 base::Time::Now().LocalMidnight()};
868 SetUserWallpaperInfo(user_id, info, true);
870 if (update_wallpaper)
871 DoSetDefaultWallpaper(user_id, on_finish.Pass());
872 return;
875 // Update the image, but keep the path which was set earlier.
876 wallpaper_cache_[user_id].second = user_image.image();
878 if (update_wallpaper) {
879 ash::Shell::GetInstance()
880 ->desktop_background_controller()
881 ->SetWallpaperImage(user_image.image(), layout);
885 void WallpaperManager::StartLoad(const std::string& user_id,
886 const WallpaperInfo& info,
887 bool update_wallpaper,
888 const base::FilePath& wallpaper_path,
889 MovableOnDestroyCallbackHolder on_finish) {
890 DCHECK_CURRENTLY_ON(BrowserThread::UI);
891 TRACE_EVENT_ASYNC_BEGIN0("ui", "LoadAndDecodeWallpaper", this);
892 if (update_wallpaper) {
893 // We are now about to change the wallpaper, so update the path and remove
894 // the existing image.
895 wallpaper_cache_[user_id] = CustomWallpaperElement(wallpaper_path,
896 gfx::ImageSkia());
898 wallpaper_loader_->Start(wallpaper_path.value(),
899 0, // Do not crop.
900 base::Bind(&WallpaperManager::OnWallpaperDecoded,
901 weak_factory_.GetWeakPtr(),
902 user_id,
903 info.layout,
904 update_wallpaper,
905 base::Passed(on_finish.Pass())));
908 void WallpaperManager::SetCustomizedDefaultWallpaperAfterCheck(
909 const GURL& wallpaper_url,
910 const base::FilePath& downloaded_file,
911 scoped_ptr<CustomizedWallpaperRescaledFiles> rescaled_files) {
912 PrefService* pref_service = g_browser_process->local_state();
914 std::string current_url =
915 pref_service->GetString(prefs::kCustomizationDefaultWallpaperURL);
916 if (current_url != wallpaper_url.spec() || !rescaled_files->AllSizesExist()) {
917 DCHECK(rescaled_files->downloaded_exists());
919 // Either resized images do not exist or cached version is incorrect.
920 // Need to start resize again.
921 wallpaper_loader_->Start(
922 downloaded_file.value(),
923 0, // Do not crop.
924 base::Bind(&WallpaperManager::OnCustomizedDefaultWallpaperDecoded,
925 weak_factory_.GetWeakPtr(),
926 wallpaper_url,
927 base::Passed(rescaled_files.Pass())));
928 } else {
929 SetDefaultWallpaperPath(rescaled_files->path_rescaled_small(),
930 scoped_ptr<gfx::ImageSkia>().Pass(),
931 rescaled_files->path_rescaled_large(),
932 scoped_ptr<gfx::ImageSkia>().Pass());
936 void WallpaperManager::OnCustomizedDefaultWallpaperResized(
937 const GURL& wallpaper_url,
938 scoped_ptr<CustomizedWallpaperRescaledFiles> rescaled_files,
939 scoped_ptr<bool> success,
940 scoped_ptr<gfx::ImageSkia> small_wallpaper_image,
941 scoped_ptr<gfx::ImageSkia> large_wallpaper_image) {
942 DCHECK_CURRENTLY_ON(BrowserThread::UI);
943 DCHECK(rescaled_files);
944 DCHECK(success.get());
945 if (!*success) {
946 LOG(WARNING) << "Failed to save resized customized default wallpaper";
947 return;
949 PrefService* pref_service = g_browser_process->local_state();
950 pref_service->SetString(prefs::kCustomizationDefaultWallpaperURL,
951 wallpaper_url.spec());
952 SetDefaultWallpaperPath(rescaled_files->path_rescaled_small(),
953 small_wallpaper_image.Pass(),
954 rescaled_files->path_rescaled_large(),
955 large_wallpaper_image.Pass());
956 VLOG(1) << "Customized default wallpaper applied.";
959 size_t WallpaperManager::GetPendingListSizeForTesting() const {
960 return loading_.size();
963 void WallpaperManager::UserChangedChildStatus(user_manager::User* user) {
964 SetUserWallpaperNow(user->email());
967 void WallpaperManager::OnDefaultWallpaperDecoded(
968 const base::FilePath& path,
969 const wallpaper::WallpaperLayout layout,
970 scoped_ptr<user_manager::UserImage>* result_out,
971 MovableOnDestroyCallbackHolder on_finish,
972 const user_manager::UserImage& user_image) {
973 result_out->reset(new user_manager::UserImage(user_image));
974 ash::Shell::GetInstance()->desktop_background_controller()->SetWallpaperImage(
975 user_image.image(), layout);
978 void WallpaperManager::StartLoadAndSetDefaultWallpaper(
979 const base::FilePath& path,
980 const wallpaper::WallpaperLayout layout,
981 MovableOnDestroyCallbackHolder on_finish,
982 scoped_ptr<user_manager::UserImage>* result_out) {
983 wallpaper_loader_->Start(
984 path.value(),
985 0, // Do not crop.
986 base::Bind(&WallpaperManager::OnDefaultWallpaperDecoded,
987 weak_factory_.GetWeakPtr(),
988 path,
989 layout,
990 base::Unretained(result_out),
991 base::Passed(on_finish.Pass())));
994 void WallpaperManager::SetDefaultWallpaperPath(
995 const base::FilePath& default_small_wallpaper_file,
996 scoped_ptr<gfx::ImageSkia> small_wallpaper_image,
997 const base::FilePath& default_large_wallpaper_file,
998 scoped_ptr<gfx::ImageSkia> large_wallpaper_image) {
999 default_small_wallpaper_file_ = default_small_wallpaper_file;
1000 default_large_wallpaper_file_ = default_large_wallpaper_file;
1002 ash::DesktopBackgroundController* dbc =
1003 ash::Shell::GetInstance()->desktop_background_controller();
1005 // |need_update_screen| is true if the previous default wallpaper is visible
1006 // now, so we need to update wallpaper on the screen.
1008 // Layout is ignored here, so wallpaper::WALLPAPER_LAYOUT_CENTER is used
1009 // as a placeholder only.
1010 const bool need_update_screen =
1011 default_wallpaper_image_.get() &&
1012 dbc->WallpaperIsAlreadyLoaded(default_wallpaper_image_->image(),
1013 false /* compare_layouts */,
1014 wallpaper::WALLPAPER_LAYOUT_CENTER);
1016 default_wallpaper_image_.reset();
1017 if (GetAppropriateResolution() == WALLPAPER_RESOLUTION_SMALL) {
1018 if (small_wallpaper_image) {
1019 default_wallpaper_image_.reset(
1020 new user_manager::UserImage(*small_wallpaper_image));
1021 default_wallpaper_image_->set_file_path(
1022 default_small_wallpaper_file.value());
1024 } else {
1025 if (large_wallpaper_image) {
1026 default_wallpaper_image_.reset(
1027 new user_manager::UserImage(*large_wallpaper_image));
1028 default_wallpaper_image_->set_file_path(
1029 default_large_wallpaper_file.value());
1033 if (need_update_screen) {
1034 DoSetDefaultWallpaper(std::string(),
1035 MovableOnDestroyCallbackHolder().Pass());
1039 } // namespace chromeos