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 "ash/ash_switches.h"
11 #include "ash/desktop_background/desktop_background_controller.h"
12 #include "ash/shell.h"
13 #include "base/command_line.h"
14 #include "base/debug/trace_event.h"
15 #include "base/file_util.h"
16 #include "base/files/file_enumerator.h"
17 #include "base/files/file_path.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/threading/worker_pool.h"
28 #include "base/time/time.h"
29 #include "base/values.h"
30 #include "chrome/browser/browser_process.h"
31 #include "chrome/browser/chrome_notification_types.h"
32 #include "chrome/browser/chromeos/customization_document.h"
33 #include "chrome/browser/chromeos/login/startup_utils.h"
34 #include "chrome/browser/chromeos/login/user.h"
35 #include "chrome/browser/chromeos/login/user_image.h"
36 #include "chrome/browser/chromeos/login/user_manager.h"
37 #include "chrome/browser/chromeos/login/wizard_controller.h"
38 #include "chrome/browser/chromeos/settings/cros_settings.h"
39 #include "chrome/common/chrome_paths.h"
40 #include "chrome/common/chrome_switches.h"
41 #include "chrome/common/pref_names.h"
42 #include "chromeos/chromeos_switches.h"
43 #include "chromeos/dbus/dbus_thread_manager.h"
44 #include "content/public/browser/browser_thread.h"
45 #include "content/public/browser/notification_service.h"
46 #include "grit/ash_resources.h"
47 #include "ui/base/resource/resource_bundle.h"
48 #include "ui/gfx/codec/jpeg_codec.h"
49 #include "ui/gfx/image/image_skia_operations.h"
50 #include "ui/gfx/skia_util.h"
52 using content::BrowserThread;
58 // The amount of delay before starts to move custom wallpapers to the new place.
59 const int kMoveCustomWallpaperDelaySeconds = 30;
61 // Default quality for encoding wallpaper.
62 const int kDefaultEncodingQuality = 90;
64 // A dictionary pref that maps usernames to file paths to their wallpapers.
65 // Deprecated. Will remove this const char after done migration.
66 const char kUserWallpapers[] = "UserWallpapers";
68 const int kCacheWallpaperDelayMs = 500;
70 // A dictionary pref that maps usernames to wallpaper properties.
71 const char kUserWallpapersProperties[] = "UserWallpapersProperties";
73 // Names of nodes with info about wallpaper in |kUserWallpapersProperties|
75 const char kNewWallpaperDateNodeName[] = "date";
76 const char kNewWallpaperLayoutNodeName[] = "layout";
77 const char kNewWallpaperFileNodeName[] = "file";
78 const char kNewWallpaperTypeNodeName[] = "type";
80 // Maximum number of wallpapers cached by CacheUsersWallpapers().
81 const int kMaxWallpapersToCache = 3;
83 // Maximum number of entries in WallpaperManager::last_load_times_ .
84 const size_t kLastLoadsStatsMsMaxSize = 4;
86 // Minimum delay between wallpaper loads, milliseconds.
87 const unsigned kLoadMinDelayMs = 50;
89 // Default wallpaper load delay, milliseconds.
90 const unsigned kLoadDefaultDelayMs = 200;
92 // Maximum wallpaper load delay, milliseconds.
93 const unsigned kLoadMaxDelayMs = 2000;
95 // For our scaling ratios we need to round positive numbers.
96 int RoundPositive(double x) {
97 return static_cast<int>(floor(x + 0.5));
100 // Returns custom wallpaper directory by appending corresponding |sub_dir|.
101 base::FilePath GetCustomWallpaperDir(const char* sub_dir) {
102 base::FilePath custom_wallpaper_dir;
103 CHECK(PathService::Get(chrome::DIR_CHROMEOS_CUSTOM_WALLPAPERS,
104 &custom_wallpaper_dir));
105 return custom_wallpaper_dir.Append(sub_dir);
108 bool MoveCustomWallpaperDirectory(const char* sub_dir,
109 const std::string& user_id,
110 const std::string& user_id_hash) {
111 base::FilePath base_path = GetCustomWallpaperDir(sub_dir);
112 base::FilePath to_path = base_path.Append(user_id_hash);
113 base::FilePath from_path = base_path.Append(user_id);
114 if (base::PathExists(from_path))
115 return base::Move(from_path, to_path);
119 // These global default values are used to set customized default
120 // wallpaper path in WallpaperManager::InitializeWallpaper().
121 base::FilePath GetCustomizedWallpaperDefaultRescaledFileName(
122 const std::string& suffix) {
123 const base::FilePath default_downloaded_file_name =
124 ServicesCustomizationDocument::GetCustomizedWallpaperDownloadedFileName();
125 const base::FilePath default_cache_dir =
126 ServicesCustomizationDocument::GetCustomizedWallpaperCacheDir();
127 if (default_downloaded_file_name.empty() || default_cache_dir.empty())
128 return base::FilePath();
129 return default_cache_dir.Append(
130 default_downloaded_file_name.BaseName().value() + suffix);
133 // Whether DesktopBackgroundController should start with customized default
134 // wallpaper in WallpaperManager::InitializeWallpaper() or not.
135 bool ShouldUseCustomizedDefaultWallpaper() {
136 PrefService* pref_service = g_browser_process->local_state();
138 return !(pref_service->FindPreference(
139 prefs::kCustomizationDefaultWallpaperURL)
143 // Deletes everything else except |path| in the same directory.
144 void DeleteAllExcept(const base::FilePath& path) {
145 base::FilePath dir = path.DirName();
146 if (base::DirectoryExists(dir)) {
147 base::FileEnumerator files(dir, false, base::FileEnumerator::FILES);
148 for (base::FilePath current = files.Next(); !current.empty();
149 current = files.Next()) {
151 base::DeleteFile(current, false);
156 // Deletes a list of wallpaper files in |file_list|.
157 void DeleteWallpaperInList(const std::vector<base::FilePath>& file_list) {
158 for (std::vector<base::FilePath>::const_iterator it = file_list.begin();
159 it != file_list.end(); ++it) {
160 base::FilePath path = *it;
161 // Some users may still have legacy wallpapers with png extension. We need
162 // to delete these wallpapers too.
163 if (!base::DeleteFile(path, true) &&
164 !base::DeleteFile(path.AddExtension(".png"), false)) {
165 LOG(ERROR) << "Failed to remove user wallpaper at " << path.value();
170 // Creates all new custom wallpaper directories for |user_id_hash| if not exist.
171 void EnsureCustomWallpaperDirectories(const std::string& user_id_hash) {
173 dir = GetCustomWallpaperDir(kSmallWallpaperSubDir);
174 dir = dir.Append(user_id_hash);
175 if (!base::PathExists(dir))
176 base::CreateDirectory(dir);
177 dir = GetCustomWallpaperDir(kLargeWallpaperSubDir);
178 dir = dir.Append(user_id_hash);
179 if (!base::PathExists(dir))
180 base::CreateDirectory(dir);
181 dir = GetCustomWallpaperDir(kOriginalWallpaperSubDir);
182 dir = dir.Append(user_id_hash);
183 if (!base::PathExists(dir))
184 base::CreateDirectory(dir);
185 dir = GetCustomWallpaperDir(kThumbnailWallpaperSubDir);
186 dir = dir.Append(user_id_hash);
187 if (!base::PathExists(dir))
188 base::CreateDirectory(dir);
191 // Saves wallpaper image raw |data| to |path| (absolute path) in file system.
192 // Returns true on success.
193 bool SaveWallpaperInternal(const base::FilePath& path,
196 int written_bytes = base::WriteFile(path, data, size);
197 return written_bytes == size;
200 // Returns index of the first public session user found in |users|
202 int FindPublicSession(const chromeos::UserList& users) {
205 for (UserList::const_iterator it = users.begin();
206 it != users.end(); ++it, ++i) {
207 if ((*it)->GetType() == User::USER_TYPE_PUBLIC_ACCOUNT) {
218 const char kWallpaperSequenceTokenName[] = "wallpaper-sequence";
220 const char kSmallWallpaperSuffix[] = "_small";
221 const char kLargeWallpaperSuffix[] = "_large";
223 const char kSmallWallpaperSubDir[] = "small";
224 const char kLargeWallpaperSubDir[] = "large";
225 const char kOriginalWallpaperSubDir[] = "original";
226 const char kThumbnailWallpaperSubDir[] = "thumb";
228 const int kSmallWallpaperMaxWidth = 1366;
229 const int kSmallWallpaperMaxHeight = 800;
230 const int kLargeWallpaperMaxWidth = 2560;
231 const int kLargeWallpaperMaxHeight = 1700;
232 const int kWallpaperThumbnailWidth = 108;
233 const int kWallpaperThumbnailHeight = 68;
235 static WallpaperManager* g_wallpaper_manager = NULL;
237 class WallpaperManager::CustomizedWallpaperRescaledFiles {
239 CustomizedWallpaperRescaledFiles(const base::FilePath& path_downloaded,
240 const base::FilePath& path_rescaled_small,
241 const base::FilePath& path_rescaled_large);
243 bool AllSizesExist() const;
245 // Closure will hold unretained pointer to this object. So caller must
246 // make sure that the closure will be destoyed before this object.
247 // Closure must be called on BlockingPool.
248 base::Closure CreateCheckerClosure();
250 const base::FilePath& path_downloaded() const { return path_downloaded_; }
251 const base::FilePath& path_rescaled_small() const {
252 return path_rescaled_small_;
254 const base::FilePath& path_rescaled_large() const {
255 return path_rescaled_large_;
258 const bool downloaded_exists() const { return downloaded_exists_; }
259 const bool rescaled_small_exists() const { return rescaled_small_exists_; }
260 const bool rescaled_large_exists() const { return rescaled_large_exists_; }
263 // Must be called on BlockingPool.
264 void CheckCustomizedWallpaperFilesExist();
266 const base::FilePath path_downloaded_;
267 const base::FilePath path_rescaled_small_;
268 const base::FilePath path_rescaled_large_;
270 bool downloaded_exists_;
271 bool rescaled_small_exists_;
272 bool rescaled_large_exists_;
274 DISALLOW_COPY_AND_ASSIGN(CustomizedWallpaperRescaledFiles);
277 WallpaperManager::CustomizedWallpaperRescaledFiles::
278 CustomizedWallpaperRescaledFiles(const base::FilePath& path_downloaded,
279 const base::FilePath& path_rescaled_small,
280 const base::FilePath& path_rescaled_large)
281 : path_downloaded_(path_downloaded),
282 path_rescaled_small_(path_rescaled_small),
283 path_rescaled_large_(path_rescaled_large),
284 downloaded_exists_(false),
285 rescaled_small_exists_(false),
286 rescaled_large_exists_(false) {
290 WallpaperManager::CustomizedWallpaperRescaledFiles::CreateCheckerClosure() {
291 return base::Bind(&WallpaperManager::CustomizedWallpaperRescaledFiles::
292 CheckCustomizedWallpaperFilesExist,
293 base::Unretained(this));
296 void WallpaperManager::CustomizedWallpaperRescaledFiles::
297 CheckCustomizedWallpaperFilesExist() {
298 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
299 downloaded_exists_ = base::PathExists(path_downloaded_);
300 rescaled_small_exists_ = base::PathExists(path_rescaled_small_);
301 rescaled_large_exists_ = base::PathExists(path_rescaled_large_);
304 bool WallpaperManager::CustomizedWallpaperRescaledFiles::AllSizesExist() const {
305 return rescaled_small_exists_ && rescaled_large_exists_;
308 // This object is passed between several threads while wallpaper is being
309 // loaded. It will notify callback when last reference to it is removed
310 // (thus indicating that the last load action has finished).
311 class MovableOnDestroyCallback {
313 explicit MovableOnDestroyCallback(const base::Closure& callback)
314 : callback_(callback) {
317 ~MovableOnDestroyCallback() {
318 if (!callback_.is_null())
323 base::Closure callback_;
326 WallpaperManager::PendingWallpaper::PendingWallpaper(
327 const base::TimeDelta delay,
328 const std::string& user_id)
331 on_finish_(new MovableOnDestroyCallback(
332 base::Bind(&WallpaperManager::PendingWallpaper::OnWallpaperSet,
337 base::Bind(&WallpaperManager::PendingWallpaper::ProcessRequest, this));
340 WallpaperManager::PendingWallpaper::~PendingWallpaper() {}
342 void WallpaperManager::PendingWallpaper::ResetSetWallpaperImage(
343 const gfx::ImageSkia& image,
344 const WallpaperInfo& info) {
345 SetMode(image, info, base::FilePath(), false);
348 void WallpaperManager::PendingWallpaper::ResetLoadWallpaper(
349 const WallpaperInfo& info) {
350 SetMode(gfx::ImageSkia(), info, base::FilePath(), false);
353 void WallpaperManager::PendingWallpaper::ResetSetCustomWallpaper(
354 const WallpaperInfo& info,
355 const base::FilePath& wallpaper_path) {
356 SetMode(gfx::ImageSkia(), info, wallpaper_path, false);
359 void WallpaperManager::PendingWallpaper::ResetSetDefaultWallpaper() {
360 SetMode(gfx::ImageSkia(), WallpaperInfo(), base::FilePath(), true);
363 void WallpaperManager::PendingWallpaper::SetMode(
364 const gfx::ImageSkia& image,
365 const WallpaperInfo& info,
366 const base::FilePath& wallpaper_path,
367 const bool is_default) {
368 user_wallpaper_ = image;
370 wallpaper_path_ = wallpaper_path;
371 default_ = is_default;
374 void WallpaperManager::PendingWallpaper::ProcessRequest() {
375 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
377 timer.Stop(); // Erase reference to self.
379 WallpaperManager* manager = WallpaperManager::Get();
380 if (manager->pending_inactive_ == this)
381 manager->pending_inactive_ = NULL;
383 started_load_at_ = base::Time::Now();
386 manager->DoSetDefaultWallpaper(user_id_, on_finish_.Pass());
387 } else if (!user_wallpaper_.isNull()) {
388 ash::Shell::GetInstance()
389 ->desktop_background_controller()
390 ->SetWallpaperImage(user_wallpaper_, info_.layout);
391 } else if (!wallpaper_path_.empty()) {
392 manager->task_runner_->PostTask(
394 base::Bind(&WallpaperManager::GetCustomWallpaperInternal,
395 base::Unretained(manager),
399 true /* update wallpaper */,
400 base::Passed(on_finish_.Pass())));
401 } else if (!info_.file.empty()) {
402 manager->LoadWallpaper(user_id_, info_, true, on_finish_.Pass());
404 // PendingWallpaper was created and never initialized?
406 // Error. Do not record time.
407 started_load_at_ = base::Time();
412 void WallpaperManager::PendingWallpaper::OnWallpaperSet() {
413 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
415 // The only known case for this check to fail is global destruction during
416 // wallpaper load. It should never happen.
417 if (!BrowserThread::CurrentlyOn(BrowserThread::UI))
418 return; // We are in a process of global destruction.
420 timer.Stop(); // Erase reference to self.
422 WallpaperManager* manager = WallpaperManager::Get();
423 if (!started_load_at_.is_null()) {
424 const base::TimeDelta elapsed = base::Time::Now() - started_load_at_;
425 manager->SaveLastLoadTime(elapsed);
427 if (manager->pending_inactive_ == this) {
428 // ProcessRequest() was never executed.
429 manager->pending_inactive_ = NULL;
433 DCHECK(manager->loading_.size() > 0);
435 for (WallpaperManager::PendingList::iterator i = manager->loading_.begin();
436 i != manager->loading_.end();
438 if (i->get() == this) {
439 manager->loading_.erase(i);
444 // WallpaperManager, public: ---------------------------------------------------
446 // TestApi. For testing purpose
447 WallpaperManager::TestApi::TestApi(WallpaperManager* wallpaper_manager)
448 : wallpaper_manager_(wallpaper_manager) {
451 WallpaperManager::TestApi::~TestApi() {
454 base::FilePath WallpaperManager::TestApi::current_wallpaper_path() {
455 return wallpaper_manager_->current_wallpaper_path_;
458 bool WallpaperManager::TestApi::GetWallpaperFromCache(
459 const std::string& user_id, gfx::ImageSkia* image) {
460 return wallpaper_manager_->GetWallpaperFromCache(user_id, image);
463 void WallpaperManager::TestApi::SetWallpaperCache(const std::string& user_id,
464 const gfx::ImageSkia& image) {
465 DCHECK(!image.isNull());
466 wallpaper_manager_->wallpaper_cache_[user_id] = image;
469 void WallpaperManager::TestApi::ClearDisposableWallpaperCache() {
470 wallpaper_manager_->ClearDisposableWallpaperCache();
474 WallpaperManager* WallpaperManager::Get() {
475 if (!g_wallpaper_manager)
476 g_wallpaper_manager = new WallpaperManager();
477 return g_wallpaper_manager;
480 WallpaperManager::WallpaperManager()
481 : loaded_wallpapers_(0),
482 command_line_for_testing_(NULL),
483 should_cache_wallpaper_(false),
485 pending_inactive_(NULL) {
486 SetDefaultWallpaperPathsFromCommandLine(
487 base::CommandLine::ForCurrentProcess());
489 chrome::NOTIFICATION_LOGIN_USER_CHANGED,
490 content::NotificationService::AllSources());
492 chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
493 content::NotificationService::AllSources());
495 chrome::NOTIFICATION_WALLPAPER_ANIMATION_FINISHED,
496 content::NotificationService::AllSources());
497 sequence_token_ = BrowserThread::GetBlockingPool()->
498 GetNamedSequenceToken(kWallpaperSequenceTokenName);
499 task_runner_ = BrowserThread::GetBlockingPool()->
500 GetSequencedTaskRunnerWithShutdownBehavior(
502 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
503 wallpaper_loader_ = new UserImageLoader(ImageDecoder::ROBUST_JPEG_CODEC,
507 WallpaperManager::~WallpaperManager() {
508 // TODO(bshe): Lifetime of WallpaperManager needs more consideration.
509 // http://crbug.com/171694
510 DCHECK(!show_user_name_on_signin_subscription_);
512 ClearObsoleteWallpaperPrefs();
513 weak_factory_.InvalidateWeakPtrs();
516 void WallpaperManager::Shutdown() {
517 show_user_name_on_signin_subscription_.reset();
521 void WallpaperManager::RegisterPrefs(PrefRegistrySimple* registry) {
522 registry->RegisterDictionaryPref(prefs::kUsersWallpaperInfo);
523 registry->RegisterDictionaryPref(kUserWallpapers);
524 registry->RegisterDictionaryPref(kUserWallpapersProperties);
527 void WallpaperManager::AddObservers() {
528 show_user_name_on_signin_subscription_ =
529 CrosSettings::Get()->AddSettingsObserver(
530 kAccountsPrefShowUserNamesOnSignIn,
531 base::Bind(&WallpaperManager::InitializeRegisteredDeviceWallpaper,
532 base::Unretained(this)));
535 void WallpaperManager::EnsureLoggedInUserWallpaperLoaded() {
536 // Some browser tests do not have a shell instance. As no wallpaper is needed
537 // in these tests anyway, avoid loading one, preventing crashes and speeding
539 if (!ash::Shell::HasInstance())
543 if (GetLoggedInUserWallpaperInfo(&info)) {
544 // TODO(sschmitz): We need an index for default wallpapers for the new UI.
545 RecordUma(info.type, -1);
546 if (info == current_user_wallpaper_info_)
549 SetUserWallpaperNow(UserManager::Get()->GetLoggedInUser()->email());
552 void WallpaperManager::ClearDisposableWallpaperCache() {
553 // Cancel callback for previous cache requests.
554 weak_factory_.InvalidateWeakPtrs();
555 if (!UserManager::IsMultipleProfilesAllowed()) {
556 wallpaper_cache_.clear();
558 // Keep the wallpaper of logged in users in cache at multi-profile mode.
559 std::set<std::string> logged_in_users_names;
560 const UserList& logged_users = UserManager::Get()->GetLoggedInUsers();
561 for (UserList::const_iterator it = logged_users.begin();
562 it != logged_users.end();
564 logged_in_users_names.insert((*it)->email());
567 CustomWallpaperMap logged_in_users_cache;
568 for (CustomWallpaperMap::iterator it = wallpaper_cache_.begin();
569 it != wallpaper_cache_.end(); ++it) {
570 if (logged_in_users_names.find(it->first) !=
571 logged_in_users_names.end()) {
572 logged_in_users_cache.insert(*it);
575 wallpaper_cache_ = logged_in_users_cache;
579 base::FilePath WallpaperManager::GetCustomWallpaperPath(
581 const std::string& user_id_hash,
582 const std::string& file) const {
583 base::FilePath custom_wallpaper_path = GetCustomWallpaperDir(sub_dir);
584 return custom_wallpaper_path.Append(user_id_hash).Append(file);
587 bool WallpaperManager::GetLoggedInUserWallpaperInfo(WallpaperInfo* info) {
588 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
590 if (UserManager::Get()->IsLoggedInAsStub()) {
591 info->file = current_user_wallpaper_info_.file = "";
592 info->layout = current_user_wallpaper_info_.layout =
593 ash::WALLPAPER_LAYOUT_CENTER_CROPPED;
594 info->type = current_user_wallpaper_info_.type = User::DEFAULT;
595 info->date = current_user_wallpaper_info_.date =
596 base::Time::Now().LocalMidnight();
600 return GetUserWallpaperInfo(UserManager::Get()->GetLoggedInUser()->email(),
604 void WallpaperManager::InitializeWallpaper() {
605 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
606 UserManager* user_manager = UserManager::Get();
608 // Apply device customization.
609 if (ShouldUseCustomizedDefaultWallpaper()) {
610 SetDefaultWallpaperPath(
611 GetCustomizedWallpaperDefaultRescaledFileName(kSmallWallpaperSuffix),
612 scoped_ptr<gfx::ImageSkia>().Pass(),
613 GetCustomizedWallpaperDefaultRescaledFileName(kLargeWallpaperSuffix),
614 scoped_ptr<gfx::ImageSkia>().Pass());
617 CommandLine* command_line = GetCommandLine();
618 if (command_line->HasSwitch(chromeos::switches::kGuestSession)) {
619 // Guest wallpaper should be initialized when guest login.
620 // Note: This maybe called before login. So IsLoggedInAsGuest can not be
621 // used here to determine if current user is guest.
625 if (command_line->HasSwitch(::switches::kTestType))
626 WizardController::SetZeroDelays();
628 // Zero delays is also set in autotests.
629 if (WizardController::IsZeroDelayEnabled()) {
630 // Ensure tests have some sort of wallpaper.
631 ash::Shell::GetInstance()->desktop_background_controller()->
632 CreateEmptyWallpaper();
636 if (!user_manager->IsUserLoggedIn()) {
637 if (!StartupUtils::IsDeviceRegistered())
638 SetDefaultWallpaperDelayed(UserManager::kSignInUser);
640 InitializeRegisteredDeviceWallpaper();
643 SetUserWallpaperDelayed(user_manager->GetLoggedInUser()->email());
646 void WallpaperManager::Observe(int type,
647 const content::NotificationSource& source,
648 const content::NotificationDetails& details) {
649 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
651 case chrome::NOTIFICATION_LOGIN_USER_CHANGED: {
652 ClearDisposableWallpaperCache();
653 BrowserThread::PostDelayedTask(
656 base::Bind(&WallpaperManager::MoveLoggedInUserCustomWallpaper,
657 weak_factory_.GetWeakPtr()),
658 base::TimeDelta::FromSeconds(kMoveCustomWallpaperDelaySeconds));
661 case chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE: {
662 if (!GetCommandLine()->HasSwitch(switches::kDisableBootAnimation)) {
663 BrowserThread::PostDelayedTask(
664 BrowserThread::UI, FROM_HERE,
665 base::Bind(&WallpaperManager::CacheUsersWallpapers,
666 weak_factory_.GetWeakPtr()),
667 base::TimeDelta::FromMilliseconds(kCacheWallpaperDelayMs));
669 should_cache_wallpaper_ = true;
673 case chrome::NOTIFICATION_WALLPAPER_ANIMATION_FINISHED: {
674 NotifyAnimationFinished();
675 if (should_cache_wallpaper_) {
676 BrowserThread::PostDelayedTask(
677 BrowserThread::UI, FROM_HERE,
678 base::Bind(&WallpaperManager::CacheUsersWallpapers,
679 weak_factory_.GetWeakPtr()),
680 base::TimeDelta::FromMilliseconds(kCacheWallpaperDelayMs));
681 should_cache_wallpaper_ = false;
686 NOTREACHED() << "Unexpected notification " << type;
690 void WallpaperManager::RemoveUserWallpaperInfo(const std::string& user_id) {
692 GetUserWallpaperInfo(user_id, &info);
693 PrefService* prefs = g_browser_process->local_state();
694 DictionaryPrefUpdate prefs_wallpapers_info_update(prefs,
695 prefs::kUsersWallpaperInfo);
696 prefs_wallpapers_info_update->RemoveWithoutPathExpansion(user_id, NULL);
697 DeleteUserWallpapers(user_id, info.file);
701 bool WallpaperManager::ResizeImage(const gfx::ImageSkia& image,
702 ash::WallpaperLayout layout,
704 int preferred_height,
705 scoped_refptr<base::RefCountedBytes>* output,
706 gfx::ImageSkia* output_skia) {
707 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
708 int width = image.width();
709 int height = image.height();
712 *output = new base::RefCountedBytes();
714 if (layout == ash::WALLPAPER_LAYOUT_CENTER_CROPPED) {
715 // Do not resize custom wallpaper if it is smaller than preferred size.
716 if (!(width > preferred_width && height > preferred_height))
719 double horizontal_ratio = static_cast<double>(preferred_width) / width;
720 double vertical_ratio = static_cast<double>(preferred_height) / height;
721 if (vertical_ratio > horizontal_ratio) {
723 RoundPositive(static_cast<double>(width) * vertical_ratio);
724 resized_height = preferred_height;
726 resized_width = preferred_width;
728 RoundPositive(static_cast<double>(height) * horizontal_ratio);
730 } else if (layout == ash::WALLPAPER_LAYOUT_STRETCH) {
731 resized_width = preferred_width;
732 resized_height = preferred_height;
734 resized_width = width;
735 resized_height = height;
738 gfx::ImageSkia resized_image = gfx::ImageSkiaOperations::CreateResizedImage(
740 skia::ImageOperations::RESIZE_LANCZOS3,
741 gfx::Size(resized_width, resized_height));
743 SkBitmap bitmap = *(resized_image.bitmap());
744 SkAutoLockPixels lock_input(bitmap);
745 gfx::JPEGCodec::Encode(
746 reinterpret_cast<unsigned char*>(bitmap.getAddr32(0, 0)),
747 gfx::JPEGCodec::FORMAT_SkBitmap,
750 bitmap.width() * bitmap.bytesPerPixel(),
751 kDefaultEncodingQuality,
755 resized_image.MakeThreadSafe();
756 *output_skia = resized_image;
763 bool WallpaperManager::ResizeAndSaveWallpaper(const gfx::ImageSkia& image,
764 const base::FilePath& path,
765 ash::WallpaperLayout layout,
767 int preferred_height,
768 gfx::ImageSkia* output_skia) {
769 if (layout == ash::WALLPAPER_LAYOUT_CENTER) {
770 // TODO(bshe): Generates cropped custom wallpaper for CENTER layout.
771 if (base::PathExists(path))
772 base::DeleteFile(path, false);
775 scoped_refptr<base::RefCountedBytes> data;
776 if (ResizeImage(image,
782 return SaveWallpaperInternal(
783 path, reinterpret_cast<const char*>(data->front()), data->size());
788 bool WallpaperManager::IsPolicyControlled(const std::string& user_id) const {
789 chromeos::WallpaperInfo info;
790 if (!GetUserWallpaperInfo(user_id, &info))
792 return info.type == chromeos::User::POLICY;
795 void WallpaperManager::OnPolicySet(const std::string& policy,
796 const std::string& user_id) {
798 GetUserWallpaperInfo(user_id, &info);
799 info.type = User::POLICY;
800 SetUserWallpaperInfo(user_id, info, true /* is_persistent */);
803 void WallpaperManager::OnPolicyCleared(const std::string& policy,
804 const std::string& user_id) {
806 GetUserWallpaperInfo(user_id, &info);
807 info.type = User::DEFAULT;
808 SetUserWallpaperInfo(user_id, info, true /* is_persistent */);
809 SetDefaultWallpaperNow(user_id);
812 void WallpaperManager::OnPolicyFetched(const std::string& policy,
813 const std::string& user_id,
814 scoped_ptr<std::string> data) {
818 wallpaper_loader_->Start(
821 base::Bind(&WallpaperManager::SetPolicyControlledWallpaper,
822 weak_factory_.GetWeakPtr(),
827 WallpaperManager::WallpaperResolution
828 WallpaperManager::GetAppropriateResolution() {
829 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
831 ash::DesktopBackgroundController::GetMaxDisplaySizeInNative();
832 return (size.width() > kSmallWallpaperMaxWidth ||
833 size.height() > kSmallWallpaperMaxHeight)
834 ? WALLPAPER_RESOLUTION_LARGE
835 : WALLPAPER_RESOLUTION_SMALL;
838 WallpaperManager::WallpaperResolution
839 WallpaperManager::GetAppropriateResolutionForTesting() {
841 ash::DesktopBackgroundController::GetMaxDisplaySizeInNative();
842 const WallpaperResolution result = (size.width() > kSmallWallpaperMaxWidth ||
843 size.height() > kSmallWallpaperMaxHeight)
844 ? WALLPAPER_RESOLUTION_LARGE
845 : WALLPAPER_RESOLUTION_SMALL;
847 LOG(ERROR) << "WallpaperManager::GetAppropriateResolution(): width()="
849 << " vs kSmallWallpaperMaxWidth=" << kSmallWallpaperMaxWidth
850 << ", height()=" << size.height()
851 << " vs kSmallWallpaperMaxHeight=" << kSmallWallpaperMaxHeight
852 << ", result = " << (result == WALLPAPER_RESOLUTION_LARGE
853 ? "WALLPAPER_RESOLUTION_LARGE"
854 : "WALLPAPER_RESOLUTION_SMALL");
858 void WallpaperManager::SetPolicyControlledWallpaper(
859 const std::string& user_id,
860 const UserImage& user_image) {
861 const User *user = chromeos::UserManager::Get()->FindUser(user_id);
863 NOTREACHED() << "Unknown user.";
866 SetCustomWallpaper(user_id,
867 user->username_hash(),
868 "policy-controlled.jpeg",
869 ash::WALLPAPER_LAYOUT_CENTER_CROPPED,
872 true /* update wallpaper */);
875 void WallpaperManager::SetCustomWallpaper(const std::string& user_id,
876 const std::string& user_id_hash,
877 const std::string& file,
878 ash::WallpaperLayout layout,
879 User::WallpaperType type,
880 const gfx::ImageSkia& image,
881 bool update_wallpaper) {
882 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
883 DCHECK(UserManager::Get()->IsUserLoggedIn());
885 // There is no visible background in kiosk mode.
886 if (UserManager::Get()->IsLoggedInAsKioskApp())
889 // Don't allow custom wallpapers while policy is in effect.
890 if (type != User::POLICY && IsPolicyControlled(user_id))
893 base::FilePath wallpaper_path =
894 GetCustomWallpaperPath(kOriginalWallpaperSubDir, user_id_hash, file);
896 // If decoded wallpaper is empty, we have probably failed to decode the file.
897 // Use default wallpaper in this case.
898 if (image.isNull()) {
899 SetDefaultWallpaperDelayed(user_id);
904 !UserManager::Get()->IsUserNonCryptohomeDataEphemeral(user_id);
906 WallpaperInfo wallpaper_info = {
907 wallpaper_path.value(),
910 // Date field is not used.
911 base::Time::Now().LocalMidnight()
914 image.EnsureRepsForSupportedScales();
915 scoped_ptr<gfx::ImageSkia> deep_copy(image.DeepCopy());
916 // Block shutdown on this task. Otherwise, we may lose the custom wallpaper
917 // that the user selected.
918 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner =
919 BrowserThread::GetBlockingPool()
920 ->GetSequencedTaskRunnerWithShutdownBehavior(
921 sequence_token_, base::SequencedWorkerPool::BLOCK_SHUTDOWN);
922 // TODO(bshe): This may break if RawImage becomes RefCountedMemory.
923 blocking_task_runner->PostTask(
925 base::Bind(&WallpaperManager::SaveCustomWallpaper,
926 base::Unretained(this),
928 base::FilePath(wallpaper_info.file),
929 wallpaper_info.layout,
930 base::Passed(deep_copy.Pass())));
933 std::string relative_path = base::FilePath(user_id_hash).Append(file).value();
934 // User's custom wallpaper path is determined by relative path and the
935 // appropriate wallpaper resolution in GetCustomWallpaperInternal.
936 WallpaperInfo info = {
940 base::Time::Now().LocalMidnight()
942 SetUserWallpaperInfo(user_id, info, is_persistent);
943 if (update_wallpaper) {
944 GetPendingWallpaper(user_id, false)->ResetSetWallpaperImage(image, info);
947 if (UserManager::IsMultipleProfilesAllowed())
948 wallpaper_cache_[user_id] = image;
951 void WallpaperManager::SetDefaultWallpaperNow(const std::string& user_id) {
952 GetPendingWallpaper(user_id, false)->ResetSetDefaultWallpaper();
955 void WallpaperManager::SetDefaultWallpaperDelayed(const std::string& user_id) {
956 GetPendingWallpaper(user_id, true)->ResetSetDefaultWallpaper();
959 void WallpaperManager::DoSetDefaultWallpaper(
960 const std::string& user_id,
961 MovableOnDestroyCallbackHolder on_finish) {
962 // There is no visible background in kiosk mode.
963 if (UserManager::Get()->IsLoggedInAsKioskApp())
965 current_wallpaper_path_.clear();
966 wallpaper_cache_.erase(user_id);
967 // Some browser tests do not have a shell instance. As no wallpaper is needed
968 // in these tests anyway, avoid loading one, preventing crashes and speeding
970 if (!ash::Shell::HasInstance())
973 WallpaperResolution resolution = GetAppropriateResolution();
974 const bool use_small = (resolution == WALLPAPER_RESOLUTION_SMALL);
976 const base::FilePath* file = NULL;
978 if (UserManager::Get()->IsLoggedInAsGuest()) {
980 use_small ? &guest_small_wallpaper_file_ : &guest_large_wallpaper_file_;
982 file = use_small ? &default_small_wallpaper_file_
983 : &default_large_wallpaper_file_;
985 const ash::WallpaperLayout layout =
986 use_small ? ash::WALLPAPER_LAYOUT_CENTER
987 : ash::WALLPAPER_LAYOUT_CENTER_CROPPED;
989 if (!default_wallpaper_image_.get() ||
990 default_wallpaper_image_->file_path() != file->value()) {
991 default_wallpaper_image_.reset();
992 if (!file->empty()) {
993 loaded_wallpapers_++;
994 StartLoadAndSetDefaultWallpaper(
995 *file, layout, on_finish.Pass(), &default_wallpaper_image_);
999 const int resource_id = use_small ? IDR_AURA_WALLPAPER_DEFAULT_SMALL
1000 : IDR_AURA_WALLPAPER_DEFAULT_LARGE;
1002 loaded_wallpapers_ += ash::Shell::GetInstance()
1003 ->desktop_background_controller()
1004 ->SetWallpaperResource(resource_id, layout);
1007 ash::Shell::GetInstance()->desktop_background_controller()->SetWallpaperImage(
1008 default_wallpaper_image_->image(), layout);
1011 void WallpaperManager::InitInitialUserWallpaper(const std::string& user_id,
1012 bool is_persistent) {
1013 current_user_wallpaper_info_.file = "";
1014 current_user_wallpaper_info_.layout = ash::WALLPAPER_LAYOUT_CENTER_CROPPED;
1015 current_user_wallpaper_info_.type = User::DEFAULT;
1016 current_user_wallpaper_info_.date = base::Time::Now().LocalMidnight();
1018 WallpaperInfo info = current_user_wallpaper_info_;
1019 SetUserWallpaperInfo(user_id, info, is_persistent);
1022 void WallpaperManager::SetUserWallpaperInfo(const std::string& user_id,
1023 const WallpaperInfo& info,
1024 bool is_persistent) {
1025 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1026 current_user_wallpaper_info_ = info;
1030 PrefService* local_state = g_browser_process->local_state();
1031 DictionaryPrefUpdate wallpaper_update(local_state,
1032 prefs::kUsersWallpaperInfo);
1034 base::DictionaryValue* wallpaper_info_dict = new base::DictionaryValue();
1035 wallpaper_info_dict->SetString(kNewWallpaperDateNodeName,
1036 base::Int64ToString(info.date.ToInternalValue()));
1037 wallpaper_info_dict->SetString(kNewWallpaperFileNodeName, info.file);
1038 wallpaper_info_dict->SetInteger(kNewWallpaperLayoutNodeName, info.layout);
1039 wallpaper_info_dict->SetInteger(kNewWallpaperTypeNodeName, info.type);
1040 wallpaper_update->SetWithoutPathExpansion(user_id, wallpaper_info_dict);
1043 void WallpaperManager::SetUserWallpaperDelayed(const std::string& user_id) {
1044 ScheduleSetUserWallpaper(user_id, true);
1047 void WallpaperManager::SetUserWallpaperNow(const std::string& user_id) {
1048 ScheduleSetUserWallpaper(user_id, false);
1051 void WallpaperManager::ScheduleSetUserWallpaper(const std::string& user_id,
1053 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1054 // Some unit tests come here without a UserManager or without a pref system.
1055 if (!UserManager::IsInitialized() || !g_browser_process->local_state())
1057 // There is no visible background in kiosk mode.
1058 if (UserManager::Get()->IsLoggedInAsKioskApp())
1060 // Guest user, regular user in ephemeral mode, or kiosk app.
1061 const User* user = UserManager::Get()->FindUser(user_id);
1062 if (UserManager::Get()->IsUserNonCryptohomeDataEphemeral(user_id) ||
1063 (user != NULL && user->GetType() == User::USER_TYPE_KIOSK_APP)) {
1064 InitInitialUserWallpaper(user_id, false);
1065 GetPendingWallpaper(user_id, delayed)->ResetSetDefaultWallpaper();
1069 if (!UserManager::Get()->IsKnownUser(user_id))
1072 last_selected_user_ = user_id;
1076 if (!GetUserWallpaperInfo(user_id, &info)) {
1077 InitInitialUserWallpaper(user_id, true);
1078 GetUserWallpaperInfo(user_id, &info);
1081 gfx::ImageSkia user_wallpaper;
1082 current_user_wallpaper_info_ = info;
1083 if (GetWallpaperFromCache(user_id, &user_wallpaper)) {
1084 GetPendingWallpaper(user_id, delayed)
1085 ->ResetSetWallpaperImage(user_wallpaper, info);
1087 if (info.type == User::CUSTOMIZED || info.type == User::POLICY) {
1088 const char* sub_dir = GetCustomWallpaperSubdirForCurrentResolution();
1089 // Wallpaper is not resized when layout is ash::WALLPAPER_LAYOUT_CENTER.
1090 // Original wallpaper should be used in this case.
1091 // TODO(bshe): Generates cropped custom wallpaper for CENTER layout.
1092 if (info.layout == ash::WALLPAPER_LAYOUT_CENTER)
1093 sub_dir = kOriginalWallpaperSubDir;
1094 base::FilePath wallpaper_path = GetCustomWallpaperDir(sub_dir);
1095 wallpaper_path = wallpaper_path.Append(info.file);
1096 if (current_wallpaper_path_ == wallpaper_path)
1098 current_wallpaper_path_ = wallpaper_path;
1099 loaded_wallpapers_++;
1101 GetPendingWallpaper(user_id, delayed)
1102 ->ResetSetCustomWallpaper(info, wallpaper_path);
1106 if (info.file.empty()) {
1107 // Uses default built-in wallpaper when file is empty. Eventually, we
1108 // will only ship one built-in wallpaper in ChromeOS image.
1109 GetPendingWallpaper(user_id, delayed)->ResetSetDefaultWallpaper();
1113 // Load downloaded ONLINE or converted DEFAULT wallpapers.
1114 GetPendingWallpaper(user_id, delayed)->ResetLoadWallpaper(info);
1118 void WallpaperManager::SetWallpaperFromImageSkia(const std::string& user_id,
1119 const gfx::ImageSkia& image,
1120 ash::WallpaperLayout layout,
1121 bool update_wallpaper) {
1122 DCHECK(UserManager::Get()->IsUserLoggedIn());
1124 // There is no visible background in kiosk mode.
1125 if (UserManager::Get()->IsLoggedInAsKioskApp())
1128 info.layout = layout;
1129 if (UserManager::IsMultipleProfilesAllowed())
1130 wallpaper_cache_[user_id] = image;
1132 if (update_wallpaper) {
1133 GetPendingWallpaper(last_selected_user_, false /* Not delayed */)
1134 ->ResetSetWallpaperImage(image, info);
1138 void WallpaperManager::UpdateWallpaper(bool clear_cache) {
1139 FOR_EACH_OBSERVER(Observer, observers_, OnUpdateWallpaperForTesting());
1141 wallpaper_cache_.clear();
1142 current_wallpaper_path_.clear();
1143 // For GAIA login flow, the last_selected_user_ may not be set before user
1144 // login. If UpdateWallpaper is called at GAIA login screen, no wallpaper will
1145 // be set. It could result a black screen on external monitors.
1146 // See http://crbug.com/265689 for detail.
1147 if (last_selected_user_.empty()) {
1148 SetDefaultWallpaperNow(UserManager::kSignInUser);
1151 SetUserWallpaperNow(last_selected_user_);
1154 void WallpaperManager::AddObserver(WallpaperManager::Observer* observer) {
1155 observers_.AddObserver(observer);
1158 void WallpaperManager::RemoveObserver(WallpaperManager::Observer* observer) {
1159 observers_.RemoveObserver(observer);
1162 void WallpaperManager::NotifyAnimationFinished() {
1164 Observer, observers_, OnWallpaperAnimationFinished(last_selected_user_));
1167 // WallpaperManager, private: --------------------------------------------------
1169 bool WallpaperManager::GetWallpaperFromCache(const std::string& user_id,
1170 gfx::ImageSkia* image) {
1171 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1172 CustomWallpaperMap::const_iterator it = wallpaper_cache_.find(user_id);
1173 if (it != wallpaper_cache_.end()) {
1174 *image = (*it).second;
1180 void WallpaperManager::CacheUsersWallpapers() {
1181 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1182 UserList users = UserManager::Get()->GetUsers();
1184 if (!users.empty()) {
1185 UserList::const_iterator it = users.begin();
1186 // Skip the wallpaper of first user in the list. It should have been cached.
1188 for (int cached = 0;
1189 it != users.end() && cached < kMaxWallpapersToCache;
1191 std::string user_id = (*it)->email();
1192 CacheUserWallpaper(user_id);
1197 void WallpaperManager::CacheUserWallpaper(const std::string& user_id) {
1198 if (wallpaper_cache_.find(user_id) != wallpaper_cache_.end())
1201 if (GetUserWallpaperInfo(user_id, &info)) {
1202 base::FilePath wallpaper_dir;
1203 base::FilePath wallpaper_path;
1204 if (info.type == User::CUSTOMIZED || info.type == User::POLICY) {
1205 const char* sub_dir = GetCustomWallpaperSubdirForCurrentResolution();
1206 base::FilePath wallpaper_path = GetCustomWallpaperDir(sub_dir);
1207 wallpaper_path = wallpaper_path.Append(info.file);
1208 task_runner_->PostTask(
1210 base::Bind(&WallpaperManager::GetCustomWallpaperInternal,
1211 base::Unretained(this),
1215 false /* do not update wallpaper */,
1216 base::Passed(MovableOnDestroyCallbackHolder())));
1219 LoadWallpaper(user_id,
1221 false /* do not update wallpaper */,
1222 MovableOnDestroyCallbackHolder().Pass());
1226 void WallpaperManager::ClearObsoleteWallpaperPrefs() {
1227 PrefService* prefs = g_browser_process->local_state();
1228 DictionaryPrefUpdate wallpaper_properties_pref(prefs,
1229 kUserWallpapersProperties);
1230 wallpaper_properties_pref->Clear();
1231 DictionaryPrefUpdate wallpapers_pref(prefs, kUserWallpapers);
1232 wallpapers_pref->Clear();
1235 void WallpaperManager::DeleteUserWallpapers(const std::string& user_id,
1236 const std::string& path_to_file) {
1237 std::vector<base::FilePath> file_to_remove;
1238 // Remove small user wallpaper.
1239 base::FilePath wallpaper_path =
1240 GetCustomWallpaperDir(kSmallWallpaperSubDir);
1241 // Remove old directory if exists
1242 file_to_remove.push_back(wallpaper_path.Append(user_id));
1243 wallpaper_path = wallpaper_path.Append(path_to_file).DirName();
1244 file_to_remove.push_back(wallpaper_path);
1246 // Remove large user wallpaper.
1247 wallpaper_path = GetCustomWallpaperDir(kLargeWallpaperSubDir);
1248 file_to_remove.push_back(wallpaper_path.Append(user_id));
1249 wallpaper_path = wallpaper_path.Append(path_to_file);
1250 file_to_remove.push_back(wallpaper_path);
1252 // Remove user wallpaper thumbnail.
1253 wallpaper_path = GetCustomWallpaperDir(kThumbnailWallpaperSubDir);
1254 file_to_remove.push_back(wallpaper_path.Append(user_id));
1255 wallpaper_path = wallpaper_path.Append(path_to_file);
1256 file_to_remove.push_back(wallpaper_path);
1258 // Remove original user wallpaper.
1259 wallpaper_path = GetCustomWallpaperDir(kOriginalWallpaperSubDir);
1260 file_to_remove.push_back(wallpaper_path.Append(user_id));
1261 wallpaper_path = wallpaper_path.Append(path_to_file);
1262 file_to_remove.push_back(wallpaper_path);
1264 base::WorkerPool::PostTask(
1266 base::Bind(&DeleteWallpaperInList, file_to_remove),
1270 void WallpaperManager::SetCommandLineForTesting(
1271 base::CommandLine* command_line) {
1272 command_line_for_testing_ = command_line;
1273 SetDefaultWallpaperPathsFromCommandLine(command_line);
1276 CommandLine* WallpaperManager::GetCommandLine() {
1277 CommandLine* command_line = command_line_for_testing_ ?
1278 command_line_for_testing_ : CommandLine::ForCurrentProcess();
1279 return command_line;
1282 void WallpaperManager::InitializeRegisteredDeviceWallpaper() {
1283 if (UserManager::Get()->IsUserLoggedIn())
1286 bool disable_boot_animation =
1287 GetCommandLine()->HasSwitch(switches::kDisableBootAnimation);
1288 bool show_users = true;
1289 bool result = CrosSettings::Get()->GetBoolean(
1290 kAccountsPrefShowUserNamesOnSignIn, &show_users);
1291 DCHECK(result) << "Unable to fetch setting "
1292 << kAccountsPrefShowUserNamesOnSignIn;
1293 const chromeos::UserList& users = UserManager::Get()->GetUsers();
1294 int public_session_user_index = FindPublicSession(users);
1295 if ((!show_users && public_session_user_index == -1) || users.empty()) {
1296 // Boot into sign in form, preload default wallpaper.
1297 SetDefaultWallpaperDelayed(UserManager::kSignInUser);
1301 if (!disable_boot_animation) {
1302 int index = public_session_user_index != -1 ? public_session_user_index : 0;
1303 // Normal boot, load user wallpaper.
1304 // If normal boot animation is disabled wallpaper would be set
1305 // asynchronously once user pods are loaded.
1306 SetUserWallpaperDelayed(users[index]->email());
1310 void WallpaperManager::LoadWallpaper(const std::string& user_id,
1311 const WallpaperInfo& info,
1312 bool update_wallpaper,
1313 MovableOnDestroyCallbackHolder on_finish) {
1314 base::FilePath wallpaper_dir;
1315 base::FilePath wallpaper_path;
1316 if (info.type == User::ONLINE) {
1317 std::string file_name = GURL(info.file).ExtractFileName();
1318 WallpaperResolution resolution = GetAppropriateResolution();
1319 // Only solid color wallpapers have stretch layout and they have only one
1321 if (info.layout != ash::WALLPAPER_LAYOUT_STRETCH &&
1322 resolution == WALLPAPER_RESOLUTION_SMALL) {
1323 file_name = base::FilePath(file_name).InsertBeforeExtension(
1324 kSmallWallpaperSuffix).value();
1326 CHECK(PathService::Get(chrome::DIR_CHROMEOS_WALLPAPERS, &wallpaper_dir));
1327 wallpaper_path = wallpaper_dir.Append(file_name);
1328 if (current_wallpaper_path_ == wallpaper_path)
1331 if (update_wallpaper)
1332 current_wallpaper_path_ = wallpaper_path;
1334 loaded_wallpapers_++;
1336 user_id, info, update_wallpaper, wallpaper_path, on_finish.Pass());
1337 } else if (info.type == User::DEFAULT) {
1338 // Default wallpapers are migrated from M21 user profiles. A code refactor
1339 // overlooked that case and caused these wallpapers not being loaded at all.
1340 // On some slow devices, it caused login webui not visible after upgrade to
1341 // M26 from M21. See crosbug.com/38429 for details.
1342 base::FilePath user_data_dir;
1343 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
1344 wallpaper_path = user_data_dir.Append(info.file);
1346 user_id, info, update_wallpaper, wallpaper_path, on_finish.Pass());
1348 // In unexpected cases, revert to default wallpaper to fail safely. See
1349 // crosbug.com/38429.
1350 LOG(ERROR) << "Wallpaper reverts to default unexpected.";
1351 DoSetDefaultWallpaper(user_id, on_finish.Pass());
1355 bool WallpaperManager::GetUserWallpaperInfo(const std::string& user_id,
1356 WallpaperInfo* info) const {
1357 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1359 if (UserManager::Get()->IsUserNonCryptohomeDataEphemeral(user_id)) {
1360 // Default to the values cached in memory.
1361 *info = current_user_wallpaper_info_;
1363 // Ephemeral users do not save anything to local state. But we have got
1364 // wallpaper info from memory. Returns true.
1368 const base::DictionaryValue* info_dict;
1369 if (!g_browser_process->local_state()->
1370 GetDictionary(prefs::kUsersWallpaperInfo)->
1371 GetDictionaryWithoutPathExpansion(user_id, &info_dict)) {
1375 // Use temporary variables to keep |info| untouched in the error case.
1377 if (!info_dict->GetString(kNewWallpaperFileNodeName, &file))
1380 if (!info_dict->GetInteger(kNewWallpaperLayoutNodeName, &layout))
1383 if (!info_dict->GetInteger(kNewWallpaperTypeNodeName, &type))
1385 std::string date_string;
1386 if (!info_dict->GetString(kNewWallpaperDateNodeName, &date_string))
1389 if (!base::StringToInt64(date_string, &date_val))
1393 info->layout = static_cast<ash::WallpaperLayout>(layout);
1394 info->type = static_cast<User::WallpaperType>(type);
1395 info->date = base::Time::FromInternalValue(date_val);
1399 void WallpaperManager::MoveCustomWallpapersOnWorker(
1400 const std::string& user_id,
1401 const std::string& user_id_hash) {
1402 DCHECK(BrowserThread::GetBlockingPool()->
1403 IsRunningSequenceOnCurrentThread(sequence_token_));
1404 if (MoveCustomWallpaperDirectory(
1405 kOriginalWallpaperSubDir, user_id, user_id_hash)) {
1406 // Consider success if the original wallpaper is moved to the new directory.
1407 // Original wallpaper is the fallback if the correct resolution wallpaper
1408 // can not be found.
1409 BrowserThread::PostTask(
1412 base::Bind(&WallpaperManager::MoveCustomWallpapersSuccess,
1413 base::Unretained(this),
1417 MoveCustomWallpaperDirectory(kLargeWallpaperSubDir, user_id, user_id_hash);
1418 MoveCustomWallpaperDirectory(kSmallWallpaperSubDir, user_id, user_id_hash);
1419 MoveCustomWallpaperDirectory(
1420 kThumbnailWallpaperSubDir, user_id, user_id_hash);
1423 void WallpaperManager::MoveCustomWallpapersSuccess(
1424 const std::string& user_id,
1425 const std::string& user_id_hash) {
1427 GetUserWallpaperInfo(user_id, &info);
1428 if (info.type == User::CUSTOMIZED) {
1429 // New file field should include user id hash in addition to file name.
1430 // This is needed because at login screen, user id hash is not available.
1431 std::string relative_path =
1432 base::FilePath(user_id_hash).Append(info.file).value();
1433 info.file = relative_path;
1434 bool is_persistent =
1435 !UserManager::Get()->IsUserNonCryptohomeDataEphemeral(user_id);
1436 SetUserWallpaperInfo(user_id, info, is_persistent);
1440 void WallpaperManager::MoveLoggedInUserCustomWallpaper() {
1441 const User* logged_in_user = UserManager::Get()->GetLoggedInUser();
1442 task_runner_->PostTask(
1444 base::Bind(&WallpaperManager::MoveCustomWallpapersOnWorker,
1445 base::Unretained(this),
1446 logged_in_user->email(),
1447 logged_in_user->username_hash()));
1450 void WallpaperManager::GetCustomWallpaperInternal(
1451 const std::string& user_id,
1452 const WallpaperInfo& info,
1453 const base::FilePath& wallpaper_path,
1454 bool update_wallpaper,
1455 MovableOnDestroyCallbackHolder on_finish) {
1456 DCHECK(BrowserThread::GetBlockingPool()->
1457 IsRunningSequenceOnCurrentThread(sequence_token_));
1459 base::FilePath valid_path = wallpaper_path;
1460 if (!base::PathExists(wallpaper_path)) {
1461 // Falls back on original file if the correct resolution file does not
1462 // exist. This may happen when the original custom wallpaper is small or
1463 // browser shutdown before resized wallpaper saved.
1464 valid_path = GetCustomWallpaperDir(kOriginalWallpaperSubDir);
1465 valid_path = valid_path.Append(info.file);
1468 if (!base::PathExists(valid_path)) {
1469 // Falls back to custom wallpaper that uses email as part of its file path.
1470 // Note that email is used instead of user_id_hash here.
1472 GetCustomWallpaperPath(kOriginalWallpaperSubDir, user_id, info.file);
1475 if (!base::PathExists(valid_path)) {
1476 LOG(ERROR) << "Failed to load previously selected custom wallpaper. " <<
1477 "Fallback to default wallpaper";
1478 BrowserThread::PostTask(BrowserThread::UI,
1480 base::Bind(&WallpaperManager::DoSetDefaultWallpaper,
1481 base::Unretained(this),
1483 base::Passed(on_finish.Pass())));
1485 BrowserThread::PostTask(BrowserThread::UI,
1487 base::Bind(&WallpaperManager::StartLoad,
1488 base::Unretained(this),
1493 base::Passed(on_finish.Pass())));
1497 void WallpaperManager::OnWallpaperDecoded(
1498 const std::string& user_id,
1499 ash::WallpaperLayout layout,
1500 bool update_wallpaper,
1501 MovableOnDestroyCallbackHolder on_finish,
1502 const UserImage& user_image) {
1503 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1504 TRACE_EVENT_ASYNC_END0("ui", "LoadAndDecodeWallpaper", this);
1506 // If decoded wallpaper is empty, we have probably failed to decode the file.
1507 // Use default wallpaper in this case.
1508 if (user_image.image().isNull()) {
1509 // Updates user pref to default wallpaper.
1510 WallpaperInfo info = {
1512 ash::WALLPAPER_LAYOUT_CENTER_CROPPED,
1514 base::Time::Now().LocalMidnight()
1516 SetUserWallpaperInfo(user_id, info, true);
1518 if (update_wallpaper)
1519 DoSetDefaultWallpaper(user_id, on_finish.Pass());
1523 // Only cache the user wallpaper at login screen and for multi profile users.
1524 if (!UserManager::Get()->IsUserLoggedIn() ||
1525 UserManager::IsMultipleProfilesAllowed()) {
1526 wallpaper_cache_[user_id] = user_image.image();
1529 if (update_wallpaper) {
1530 ash::Shell::GetInstance()
1531 ->desktop_background_controller()
1532 ->SetWallpaperImage(user_image.image(), layout);
1536 void WallpaperManager::SaveCustomWallpaper(
1537 const std::string& user_id_hash,
1538 const base::FilePath& original_path,
1539 ash::WallpaperLayout layout,
1540 scoped_ptr<gfx::ImageSkia> image) const {
1541 DCHECK(BrowserThread::GetBlockingPool()->
1542 IsRunningSequenceOnCurrentThread(sequence_token_));
1543 EnsureCustomWallpaperDirectories(user_id_hash);
1544 std::string file_name = original_path.BaseName().value();
1545 base::FilePath small_wallpaper_path =
1546 GetCustomWallpaperPath(kSmallWallpaperSubDir, user_id_hash, file_name);
1547 base::FilePath large_wallpaper_path =
1548 GetCustomWallpaperPath(kLargeWallpaperSubDir, user_id_hash, file_name);
1550 // Re-encode orginal file to jpeg format and saves the result in case that
1551 // resized wallpaper is not generated (i.e. chrome shutdown before resized
1552 // wallpaper is saved).
1553 ResizeAndSaveWallpaper(*image,
1555 ash::WALLPAPER_LAYOUT_STRETCH,
1559 DeleteAllExcept(original_path);
1561 ResizeAndSaveWallpaper(*image,
1562 small_wallpaper_path,
1564 kSmallWallpaperMaxWidth,
1565 kSmallWallpaperMaxHeight,
1567 DeleteAllExcept(small_wallpaper_path);
1568 ResizeAndSaveWallpaper(*image,
1569 large_wallpaper_path,
1571 kLargeWallpaperMaxWidth,
1572 kLargeWallpaperMaxHeight,
1574 DeleteAllExcept(large_wallpaper_path);
1577 void WallpaperManager::RecordUma(User::WallpaperType type, int index) const {
1578 UMA_HISTOGRAM_ENUMERATION("Ash.Wallpaper.Type", type,
1579 User::WALLPAPER_TYPE_COUNT);
1582 void WallpaperManager::StartLoad(const std::string& user_id,
1583 const WallpaperInfo& info,
1584 bool update_wallpaper,
1585 const base::FilePath& wallpaper_path,
1586 MovableOnDestroyCallbackHolder on_finish) {
1587 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1588 TRACE_EVENT_ASYNC_BEGIN0("ui", "LoadAndDecodeWallpaper", this);
1590 wallpaper_loader_->Start(wallpaper_path.value(),
1592 base::Bind(&WallpaperManager::OnWallpaperDecoded,
1593 base::Unretained(this),
1597 base::Passed(on_finish.Pass())));
1600 void WallpaperManager::SaveLastLoadTime(const base::TimeDelta elapsed) {
1601 while (last_load_times_.size() >= kLastLoadsStatsMsMaxSize)
1602 last_load_times_.pop_front();
1604 if (elapsed > base::TimeDelta::FromMicroseconds(0)) {
1605 last_load_times_.push_back(elapsed);
1606 last_load_finished_at_ = base::Time::Now();
1610 base::TimeDelta WallpaperManager::GetWallpaperLoadDelay() const {
1611 base::TimeDelta delay;
1613 if (last_load_times_.size() == 0) {
1614 delay = base::TimeDelta::FromMilliseconds(kLoadDefaultDelayMs);
1616 delay = std::accumulate(last_load_times_.begin(),
1617 last_load_times_.end(),
1619 std::plus<base::TimeDelta>()) /
1620 last_load_times_.size();
1623 if (delay < base::TimeDelta::FromMilliseconds(kLoadMinDelayMs))
1624 delay = base::TimeDelta::FromMilliseconds(kLoadMinDelayMs);
1625 else if (delay > base::TimeDelta::FromMilliseconds(kLoadMaxDelayMs))
1626 delay = base::TimeDelta::FromMilliseconds(kLoadMaxDelayMs);
1628 // If we had ever loaded wallpaper, adjust wait delay by time since last load.
1629 if (!last_load_finished_at_.is_null()) {
1630 const base::TimeDelta interval = base::Time::Now() - last_load_finished_at_;
1631 if (interval > delay)
1632 delay = base::TimeDelta::FromMilliseconds(0);
1633 else if (interval > base::TimeDelta::FromMilliseconds(0))
1639 void WallpaperManager::SetCustomizedDefaultWallpaperAfterCheck(
1640 const GURL& wallpaper_url,
1641 const base::FilePath& downloaded_file,
1642 scoped_ptr<CustomizedWallpaperRescaledFiles> rescaled_files) {
1643 PrefService* pref_service = g_browser_process->local_state();
1645 std::string current_url =
1646 pref_service->GetString(prefs::kCustomizationDefaultWallpaperURL);
1647 if (current_url != wallpaper_url.spec() || !rescaled_files->AllSizesExist()) {
1648 DCHECK(rescaled_files->downloaded_exists());
1650 // Either resized images do not exist or cached version is incorrect.
1651 // Need to start resize again.
1652 wallpaper_loader_->Start(
1653 downloaded_file.value(),
1655 base::Bind(&WallpaperManager::OnCustomizedDefaultWallpaperDecoded,
1656 weak_factory_.GetWeakPtr(),
1658 base::Passed(rescaled_files.Pass())));
1660 SetDefaultWallpaperPath(rescaled_files->path_rescaled_small(),
1661 scoped_ptr<gfx::ImageSkia>().Pass(),
1662 rescaled_files->path_rescaled_large(),
1663 scoped_ptr<gfx::ImageSkia>().Pass());
1667 void WallpaperManager::OnCustomizedDefaultWallpaperDecoded(
1668 const GURL& wallpaper_url,
1669 scoped_ptr<CustomizedWallpaperRescaledFiles> rescaled_files,
1670 const UserImage& wallpaper) {
1671 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1673 // If decoded wallpaper is empty, we have probably failed to decode the file.
1674 if (wallpaper.image().isNull()) {
1675 LOG(WARNING) << "Failed to decode customized wallpaper.";
1679 wallpaper.image().EnsureRepsForSupportedScales();
1680 scoped_ptr<gfx::ImageSkia> deep_copy(wallpaper.image().DeepCopy());
1682 scoped_ptr<bool> success(new bool(false));
1683 scoped_ptr<gfx::ImageSkia> small_wallpaper_image(new gfx::ImageSkia);
1684 scoped_ptr<gfx::ImageSkia> large_wallpaper_image(new gfx::ImageSkia);
1686 // TODO(bshe): This may break if RawImage becomes RefCountedMemory.
1687 base::Closure resize_closure =
1688 base::Bind(&WallpaperManager::ResizeCustomizedDefaultWallpaper,
1689 base::Unretained(this),
1690 base::Passed(&deep_copy),
1691 wallpaper.raw_image(),
1692 base::Unretained(rescaled_files.get()),
1693 base::Unretained(success.get()),
1694 base::Unretained(small_wallpaper_image.get()),
1695 base::Unretained(large_wallpaper_image.get()));
1696 base::Closure on_resized_closure =
1697 base::Bind(&WallpaperManager::OnCustomizedDefaultWallpaperResized,
1698 weak_factory_.GetWeakPtr(),
1700 base::Passed(rescaled_files.Pass()),
1701 base::Passed(success.Pass()),
1702 base::Passed(small_wallpaper_image.Pass()),
1703 base::Passed(large_wallpaper_image.Pass()));
1705 if (!task_runner_->PostTaskAndReply(
1706 FROM_HERE, resize_closure, on_resized_closure)) {
1707 LOG(WARNING) << "Failed to start Customized Wallpaper resize.";
1711 void WallpaperManager::ResizeCustomizedDefaultWallpaper(
1712 scoped_ptr<gfx::ImageSkia> image,
1713 const UserImage::RawImage& raw_image,
1714 const CustomizedWallpaperRescaledFiles* rescaled_files,
1716 gfx::ImageSkia* small_wallpaper_image,
1717 gfx::ImageSkia* large_wallpaper_image) {
1718 DCHECK(BrowserThread::GetBlockingPool()->IsRunningSequenceOnCurrentThread(
1723 *success &= ResizeAndSaveWallpaper(*image,
1724 rescaled_files->path_rescaled_small(),
1725 ash::WALLPAPER_LAYOUT_STRETCH,
1726 kSmallWallpaperMaxWidth,
1727 kSmallWallpaperMaxHeight,
1728 small_wallpaper_image);
1730 *success &= ResizeAndSaveWallpaper(*image,
1731 rescaled_files->path_rescaled_large(),
1732 ash::WALLPAPER_LAYOUT_STRETCH,
1733 kLargeWallpaperMaxWidth,
1734 kLargeWallpaperMaxHeight,
1735 large_wallpaper_image);
1738 void WallpaperManager::OnCustomizedDefaultWallpaperResized(
1739 const GURL& wallpaper_url,
1740 scoped_ptr<CustomizedWallpaperRescaledFiles> rescaled_files,
1741 scoped_ptr<bool> success,
1742 scoped_ptr<gfx::ImageSkia> small_wallpaper_image,
1743 scoped_ptr<gfx::ImageSkia> large_wallpaper_image) {
1744 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1745 DCHECK(rescaled_files);
1746 DCHECK(success.get());
1748 LOG(WARNING) << "Failed to save resized customized default wallpaper";
1751 PrefService* pref_service = g_browser_process->local_state();
1752 pref_service->SetString(prefs::kCustomizationDefaultWallpaperURL,
1753 wallpaper_url.spec());
1754 SetDefaultWallpaperPath(rescaled_files->path_rescaled_small(),
1755 small_wallpaper_image.Pass(),
1756 rescaled_files->path_rescaled_large(),
1757 large_wallpaper_image.Pass());
1758 VLOG(1) << "Customized default wallpaper applied.";
1761 WallpaperManager::PendingWallpaper* WallpaperManager::GetPendingWallpaper(
1762 const std::string& user_id,
1764 if (!pending_inactive_) {
1765 loading_.push_back(new WallpaperManager::PendingWallpaper(
1766 (delayed ? GetWallpaperLoadDelay()
1767 : base::TimeDelta::FromMilliseconds(0)),
1769 pending_inactive_ = loading_.back();
1771 return pending_inactive_;
1774 void WallpaperManager::SetCustomizedDefaultWallpaper(
1775 const GURL& wallpaper_url,
1776 const base::FilePath& downloaded_file,
1777 const base::FilePath& resized_directory) {
1778 // Should fail if this ever happens in tests.
1779 DCHECK(wallpaper_url.is_valid());
1780 if (!wallpaper_url.is_valid()) {
1781 if (!wallpaper_url.is_empty()) {
1782 LOG(WARNING) << "Invalid Customized Wallpaper URL '"
1783 << wallpaper_url.spec() << "'";
1787 std::string downloaded_file_name = downloaded_file.BaseName().value();
1788 scoped_ptr<CustomizedWallpaperRescaledFiles> rescaled_files(
1789 new CustomizedWallpaperRescaledFiles(
1791 resized_directory.Append(downloaded_file_name +
1792 kSmallWallpaperSuffix),
1793 resized_directory.Append(downloaded_file_name +
1794 kLargeWallpaperSuffix)));
1796 base::Closure check_file_exists = rescaled_files->CreateCheckerClosure();
1797 base::Closure on_checked_closure =
1798 base::Bind(&WallpaperManager::SetCustomizedDefaultWallpaperAfterCheck,
1799 weak_factory_.GetWeakPtr(),
1802 base::Passed(rescaled_files.Pass()));
1803 if (!BrowserThread::PostBlockingPoolTaskAndReply(
1804 FROM_HERE, check_file_exists, on_checked_closure)) {
1805 LOG(WARNING) << "Failed to start check CheckCustomizedWallpaperFilesExist.";
1809 void WallpaperManager::SetDefaultWallpaperPathsFromCommandLine(
1810 base::CommandLine* command_line) {
1811 default_small_wallpaper_file_ = command_line->GetSwitchValuePath(
1812 ash::switches::kAshDefaultWallpaperSmall);
1813 default_large_wallpaper_file_ = command_line->GetSwitchValuePath(
1814 ash::switches::kAshDefaultWallpaperLarge);
1815 guest_small_wallpaper_file_ =
1816 command_line->GetSwitchValuePath(ash::switches::kAshGuestWallpaperSmall);
1817 guest_large_wallpaper_file_ =
1818 command_line->GetSwitchValuePath(ash::switches::kAshGuestWallpaperLarge);
1819 default_wallpaper_image_.reset();
1822 void WallpaperManager::OnDefaultWallpaperDecoded(
1823 const base::FilePath& path,
1824 const ash::WallpaperLayout layout,
1825 scoped_ptr<chromeos::UserImage>* result_out,
1826 MovableOnDestroyCallbackHolder on_finish,
1827 const UserImage& user_image) {
1828 result_out->reset(new UserImage(user_image));
1829 ash::Shell::GetInstance()->desktop_background_controller()->SetWallpaperImage(
1830 user_image.image(), layout);
1833 void WallpaperManager::StartLoadAndSetDefaultWallpaper(
1834 const base::FilePath& path,
1835 const ash::WallpaperLayout layout,
1836 MovableOnDestroyCallbackHolder on_finish,
1837 scoped_ptr<chromeos::UserImage>* result_out) {
1838 wallpaper_loader_->Start(
1841 base::Bind(&WallpaperManager::OnDefaultWallpaperDecoded,
1842 weak_factory_.GetWeakPtr(),
1845 base::Unretained(result_out),
1846 base::Passed(on_finish.Pass())));
1849 const char* WallpaperManager::GetCustomWallpaperSubdirForCurrentResolution() {
1850 WallpaperResolution resolution = GetAppropriateResolution();
1851 return resolution == WALLPAPER_RESOLUTION_SMALL ? kSmallWallpaperSubDir
1852 : kLargeWallpaperSubDir;
1855 void WallpaperManager::SetDefaultWallpaperPath(
1856 const base::FilePath& default_small_wallpaper_file,
1857 scoped_ptr<gfx::ImageSkia> small_wallpaper_image,
1858 const base::FilePath& default_large_wallpaper_file,
1859 scoped_ptr<gfx::ImageSkia> large_wallpaper_image) {
1860 default_small_wallpaper_file_ = default_small_wallpaper_file;
1861 default_large_wallpaper_file_ = default_large_wallpaper_file;
1863 ash::DesktopBackgroundController* dbc =
1864 ash::Shell::GetInstance()->desktop_background_controller();
1866 // |need_update_screen| is true if the previous default wallpaper is visible
1867 // now, so we need to update wallpaper on the screen.
1869 // Layout is ignored here, so ash::WALLPAPER_LAYOUT_CENTER is used
1870 // as a placeholder only.
1871 const bool need_update_screen =
1872 default_wallpaper_image_.get() &&
1873 dbc->WallpaperIsAlreadyLoaded(
1874 &(default_wallpaper_image_->image()),
1875 ash::DesktopBackgroundController::kInvalidResourceID,
1876 false /* compare_layouts */,
1877 ash::WALLPAPER_LAYOUT_CENTER);
1879 default_wallpaper_image_.reset();
1880 if (GetAppropriateResolution() == WALLPAPER_RESOLUTION_SMALL) {
1881 if (small_wallpaper_image) {
1882 default_wallpaper_image_.reset(new UserImage(*small_wallpaper_image));
1883 default_wallpaper_image_->set_file_path(
1884 default_small_wallpaper_file.value());
1887 if (large_wallpaper_image) {
1888 default_wallpaper_image_.reset(new UserImage(*large_wallpaper_image));
1889 default_wallpaper_image_->set_file_path(
1890 default_large_wallpaper_file.value());
1894 if (need_update_screen) {
1895 DoSetDefaultWallpaper(std::string(),
1896 MovableOnDestroyCallbackHolder().Pass());
1900 } // namespace chromeos