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 "components/wallpaper/wallpaper_manager_base.h"
10 #include "base/command_line.h"
11 #include "base/files/file_path.h"
12 #include "base/files/file_util.h"
13 #include "base/logging.h"
14 #include "base/metrics/histogram.h"
15 #include "base/path_service.h"
16 #include "base/prefs/pref_registry_simple.h"
17 #include "base/prefs/pref_service.h"
18 #include "base/prefs/scoped_user_pref_update.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/stringprintf.h"
22 #include "base/sys_info.h"
23 #include "base/threading/worker_pool.h"
24 #include "base/time/time.h"
25 #include "base/values.h"
26 #include "chromeos/chromeos_switches.h"
27 #include "chromeos/cryptohome/async_method_caller.h"
28 #include "chromeos/dbus/dbus_thread_manager.h"
29 #include "chromeos/login/user_names.h"
30 #include "components/user_manager/user.h"
31 #include "components/user_manager/user_image/user_image.h"
32 #include "components/user_manager/user_manager.h"
33 #include "components/user_manager/user_type.h"
34 #include "components/wallpaper/wallpaper_layout.h"
35 #include "content/public/browser/browser_thread.h"
36 #include "third_party/skia/include/core/SkColor.h"
37 #include "ui/gfx/codec/jpeg_codec.h"
38 #include "ui/gfx/geometry/safe_integer_conversions.h"
39 #include "ui/gfx/image/image_skia_operations.h"
40 #include "ui/gfx/skia_util.h"
42 using content::BrowserThread
;
48 // Default quality for encoding wallpaper.
49 const int kDefaultEncodingQuality
= 90;
51 // Maximum number of wallpapers cached by CacheUsersWallpapers().
52 const int kMaxWallpapersToCache
= 3;
54 // Maximum number of entries in WallpaperManagerBase::last_load_times_ .
55 const size_t kLastLoadsStatsMsMaxSize
= 4;
57 // Minimum delay between wallpaper loads, milliseconds.
58 const unsigned kLoadMinDelayMs
= 50;
60 // Default wallpaper load delay, milliseconds.
61 const unsigned kLoadDefaultDelayMs
= 200;
63 // Maximum wallpaper load delay, milliseconds.
64 const unsigned kLoadMaxDelayMs
= 2000;
66 // When no wallpaper image is specified, the screen is filled with a solid
68 const SkColor kDefaultWallpaperColor
= SK_ColorGRAY
;
70 // The path ids for directories.
71 int dir_user_data_path_id
= -1; // chrome::DIR_USER_DATA
72 int dir_chromeos_wallpapers_path_id
= -1; // chrome::DIR_CHROMEOS_WALLPAPERS
73 int dir_chromeos_custom_wallpapers_path_id
=
74 -1; // chrome::DIR_CHROMEOS_CUSTOM_WALLPAPERS
76 bool MoveCustomWallpaperDirectory(const char* sub_dir
,
77 const std::string
& user_id
,
78 const std::string
& user_id_hash
) {
79 base::FilePath base_path
=
80 WallpaperManagerBase::GetCustomWallpaperDir(sub_dir
);
81 base::FilePath to_path
= base_path
.Append(user_id_hash
);
82 base::FilePath from_path
= base_path
.Append(user_id
);
83 if (base::PathExists(from_path
))
84 return base::Move(from_path
, to_path
);
88 // Deletes a list of wallpaper files in |file_list|.
89 void DeleteWallpaperInList(const std::vector
<base::FilePath
>& file_list
) {
90 for (std::vector
<base::FilePath
>::const_iterator it
= file_list
.begin();
91 it
!= file_list
.end(); ++it
) {
92 base::FilePath path
= *it
;
93 // Some users may still have legacy wallpapers with png extension. We need
94 // to delete these wallpapers too.
95 if (!base::DeleteFile(path
, true) &&
96 !base::DeleteFile(path
.AddExtension(".png"), false)) {
97 LOG(ERROR
) << "Failed to remove user wallpaper at " << path
.value();
102 // Creates all new custom wallpaper directories for |user_id_hash| if not exist.
104 void EnsureCustomWallpaperDirectories(const std::string
& user_id_hash
) {
106 dir
= WallpaperManagerBase::GetCustomWallpaperDir(kSmallWallpaperSubDir
);
107 dir
= dir
.Append(user_id_hash
);
108 if (!base::PathExists(dir
))
109 base::CreateDirectory(dir
);
110 dir
= WallpaperManagerBase::GetCustomWallpaperDir(kLargeWallpaperSubDir
);
111 dir
= dir
.Append(user_id_hash
);
112 if (!base::PathExists(dir
))
113 base::CreateDirectory(dir
);
114 dir
= WallpaperManagerBase::GetCustomWallpaperDir(kOriginalWallpaperSubDir
);
115 dir
= dir
.Append(user_id_hash
);
116 if (!base::PathExists(dir
))
117 base::CreateDirectory(dir
);
118 dir
= WallpaperManagerBase::GetCustomWallpaperDir(kThumbnailWallpaperSubDir
);
119 dir
= dir
.Append(user_id_hash
);
120 if (!base::PathExists(dir
))
121 base::CreateDirectory(dir
);
124 // Saves wallpaper image raw |data| to |path| (absolute path) in file system.
125 // Returns true on success.
126 bool SaveWallpaperInternal(const base::FilePath
& path
,
129 int written_bytes
= base::WriteFile(path
, data
, size
);
130 return written_bytes
== size
;
135 MovableOnDestroyCallback::MovableOnDestroyCallback(
136 const base::Closure
& callback
)
137 : callback_(callback
) {
140 MovableOnDestroyCallback::~MovableOnDestroyCallback() {
141 if (!callback_
.is_null())
145 WallpaperInfo::WallpaperInfo()
146 : layout(WALLPAPER_LAYOUT_CENTER
),
147 type(user_manager::User::WALLPAPER_TYPE_COUNT
) {
150 WallpaperInfo::WallpaperInfo(const std::string
& in_location
,
151 WallpaperLayout in_layout
,
152 user_manager::User::WallpaperType in_type
,
153 const base::Time
& in_date
)
154 : location(in_location
),
160 WallpaperInfo::~WallpaperInfo() {
163 const char kWallpaperSequenceTokenName
[] = "wallpaper-sequence";
165 const char kSmallWallpaperSuffix
[] = "_small";
166 const char kLargeWallpaperSuffix
[] = "_large";
168 const char kSmallWallpaperSubDir
[] = "small";
169 const char kLargeWallpaperSubDir
[] = "large";
170 const char kOriginalWallpaperSubDir
[] = "original";
171 const char kThumbnailWallpaperSubDir
[] = "thumb";
173 const int kSmallWallpaperMaxWidth
= 1366;
174 const int kSmallWallpaperMaxHeight
= 800;
175 const int kLargeWallpaperMaxWidth
= 2560;
176 const int kLargeWallpaperMaxHeight
= 1700;
177 const int kWallpaperThumbnailWidth
= 108;
178 const int kWallpaperThumbnailHeight
= 68;
180 const char kUsersWallpaperInfo
[] = "user_wallpaper_info";
182 const char kUserWallpapers
[] = "UserWallpapers";
183 const char kUserWallpapersProperties
[] = "UserWallpapersProperties";
185 const base::FilePath
&
186 WallpaperManagerBase::CustomizedWallpaperRescaledFiles::path_downloaded()
188 return path_downloaded_
;
191 const base::FilePath
&
192 WallpaperManagerBase::CustomizedWallpaperRescaledFiles::path_rescaled_small()
194 return path_rescaled_small_
;
197 const base::FilePath
&
198 WallpaperManagerBase::CustomizedWallpaperRescaledFiles::path_rescaled_large()
200 return path_rescaled_large_
;
203 bool WallpaperManagerBase::CustomizedWallpaperRescaledFiles::downloaded_exists()
205 return downloaded_exists_
;
208 bool WallpaperManagerBase::CustomizedWallpaperRescaledFiles::
209 rescaled_small_exists() const {
210 return rescaled_small_exists_
;
213 bool WallpaperManagerBase::CustomizedWallpaperRescaledFiles::
214 rescaled_large_exists() const {
215 return rescaled_large_exists_
;
218 WallpaperManagerBase::CustomizedWallpaperRescaledFiles::
219 CustomizedWallpaperRescaledFiles(const base::FilePath
& path_downloaded
,
220 const base::FilePath
& path_rescaled_small
,
221 const base::FilePath
& path_rescaled_large
)
222 : path_downloaded_(path_downloaded
),
223 path_rescaled_small_(path_rescaled_small
),
224 path_rescaled_large_(path_rescaled_large
),
225 downloaded_exists_(false),
226 rescaled_small_exists_(false),
227 rescaled_large_exists_(false) {
231 WallpaperManagerBase::CustomizedWallpaperRescaledFiles::CreateCheckerClosure() {
232 return base::Bind(&WallpaperManagerBase::CustomizedWallpaperRescaledFiles::
233 CheckCustomizedWallpaperFilesExist
,
234 base::Unretained(this));
237 void WallpaperManagerBase::CustomizedWallpaperRescaledFiles::
238 CheckCustomizedWallpaperFilesExist() {
239 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
240 downloaded_exists_
= base::PathExists(path_downloaded_
);
241 rescaled_small_exists_
= base::PathExists(path_rescaled_small_
);
242 rescaled_large_exists_
= base::PathExists(path_rescaled_large_
);
245 bool WallpaperManagerBase::CustomizedWallpaperRescaledFiles::AllSizesExist()
247 return rescaled_small_exists_
&& rescaled_large_exists_
;
250 // WallpaperManagerBase, public:
252 // TestApi. For testing purpose
253 WallpaperManagerBase::TestApi::TestApi(WallpaperManagerBase
* wallpaper_manager
)
254 : wallpaper_manager_(wallpaper_manager
) {
257 WallpaperManagerBase::TestApi::~TestApi() {
260 bool WallpaperManagerBase::TestApi::GetWallpaperFromCache(
261 const std::string
& user_id
,
262 gfx::ImageSkia
* image
) {
263 return wallpaper_manager_
->GetWallpaperFromCache(user_id
, image
);
266 bool WallpaperManagerBase::TestApi::GetPathFromCache(
267 const std::string
& user_id
,
268 base::FilePath
* path
) {
269 return wallpaper_manager_
->GetPathFromCache(user_id
, path
);
272 void WallpaperManagerBase::TestApi::SetWallpaperCache(
273 const std::string
& user_id
,
274 const base::FilePath
& path
,
275 const gfx::ImageSkia
& image
) {
276 DCHECK(!image
.isNull());
277 wallpaper_manager_
->wallpaper_cache_
[user_id
] =
278 CustomWallpaperElement(path
, image
);
281 void WallpaperManagerBase::TestApi::ClearDisposableWallpaperCache() {
282 wallpaper_manager_
->ClearDisposableWallpaperCache();
286 void WallpaperManagerBase::SetPathIds(
287 int dir_user_data_enum
,
288 int dir_chromeos_wallpapers_enum
,
289 int dir_chromeos_custom_wallpapers_enum
) {
290 dir_user_data_path_id
= dir_user_data_enum
;
291 dir_chromeos_wallpapers_path_id
= dir_chromeos_wallpapers_enum
;
292 dir_chromeos_custom_wallpapers_path_id
= dir_chromeos_custom_wallpapers_enum
;
296 base::FilePath
WallpaperManagerBase::GetCustomWallpaperDir(
297 const char* sub_dir
) {
298 base::FilePath custom_wallpaper_dir
;
299 DCHECK(dir_chromeos_custom_wallpapers_path_id
!= -1);
300 CHECK(PathService::Get(dir_chromeos_custom_wallpapers_path_id
,
301 &custom_wallpaper_dir
));
302 return custom_wallpaper_dir
.Append(sub_dir
);
306 void WallpaperManagerBase::RegisterPrefs(PrefRegistrySimple
* registry
) {
307 registry
->RegisterDictionaryPref(kUsersWallpaperInfo
);
308 registry
->RegisterDictionaryPref(kUserWallpapers
);
309 registry
->RegisterDictionaryPref(kUserWallpapersProperties
);
312 void WallpaperManagerBase::EnsureLoggedInUserWallpaperLoaded() {
314 if (GetLoggedInUserWallpaperInfo(&info
)) {
315 UMA_HISTOGRAM_ENUMERATION("Ash.Wallpaper.Type", info
.type
,
316 user_manager::User::WALLPAPER_TYPE_COUNT
);
317 if (info
== current_user_wallpaper_info_
)
321 user_manager::UserManager::Get()->GetLoggedInUser()->email());
324 void WallpaperManagerBase::ClearDisposableWallpaperCache() {
325 // Cancel callback for previous cache requests.
326 weak_factory_
.InvalidateWeakPtrs();
327 // Keep the wallpaper of logged in users in cache at multi-profile mode.
328 std::set
<std::string
> logged_in_users_names
;
329 const user_manager::UserList
& logged_users
=
330 user_manager::UserManager::Get()->GetLoggedInUsers();
331 for (user_manager::UserList::const_iterator it
= logged_users
.begin();
332 it
!= logged_users
.end(); ++it
) {
333 logged_in_users_names
.insert((*it
)->email());
336 CustomWallpaperMap logged_in_users_cache
;
337 for (CustomWallpaperMap::iterator it
= wallpaper_cache_
.begin();
338 it
!= wallpaper_cache_
.end(); ++it
) {
339 if (logged_in_users_names
.find(it
->first
) != logged_in_users_names
.end()) {
340 logged_in_users_cache
.insert(*it
);
343 wallpaper_cache_
= logged_in_users_cache
;
346 bool WallpaperManagerBase::GetLoggedInUserWallpaperInfo(WallpaperInfo
* info
) {
347 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
349 if (user_manager::UserManager::Get()->IsLoggedInAsStub()) {
350 info
->location
= current_user_wallpaper_info_
.location
= "";
351 info
->layout
= current_user_wallpaper_info_
.layout
=
352 WALLPAPER_LAYOUT_CENTER_CROPPED
;
353 info
->type
= current_user_wallpaper_info_
.type
=
354 user_manager::User::DEFAULT
;
355 info
->date
= current_user_wallpaper_info_
.date
=
356 base::Time::Now().LocalMidnight();
360 return GetUserWallpaperInfo(
361 user_manager::UserManager::Get()->GetLoggedInUser()->email(), info
);
365 bool WallpaperManagerBase::ResizeImage(
366 const gfx::ImageSkia
& image
,
367 WallpaperLayout layout
,
369 int preferred_height
,
370 scoped_refptr
<base::RefCountedBytes
>* output
,
371 gfx::ImageSkia
* output_skia
) {
372 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI
));
373 int width
= image
.width();
374 int height
= image
.height();
377 *output
= new base::RefCountedBytes();
379 if (layout
== WALLPAPER_LAYOUT_CENTER_CROPPED
) {
380 // Do not resize custom wallpaper if it is smaller than preferred size.
381 if (!(width
> preferred_width
&& height
> preferred_height
))
384 double horizontal_ratio
= static_cast<double>(preferred_width
) / width
;
385 double vertical_ratio
= static_cast<double>(preferred_height
) / height
;
386 if (vertical_ratio
> horizontal_ratio
) {
388 gfx::ToRoundedInt(static_cast<double>(width
) * vertical_ratio
);
389 resized_height
= preferred_height
;
391 resized_width
= preferred_width
;
393 gfx::ToRoundedInt(static_cast<double>(height
) * horizontal_ratio
);
395 } else if (layout
== WALLPAPER_LAYOUT_STRETCH
) {
396 resized_width
= preferred_width
;
397 resized_height
= preferred_height
;
399 resized_width
= width
;
400 resized_height
= height
;
403 gfx::ImageSkia resized_image
= gfx::ImageSkiaOperations::CreateResizedImage(
404 image
, skia::ImageOperations::RESIZE_LANCZOS3
,
405 gfx::Size(resized_width
, resized_height
));
407 SkBitmap bitmap
= *(resized_image
.bitmap());
408 SkAutoLockPixels
lock_input(bitmap
);
409 gfx::JPEGCodec::Encode(
410 reinterpret_cast<unsigned char*>(bitmap
.getAddr32(0, 0)),
411 gfx::JPEGCodec::FORMAT_SkBitmap
, bitmap
.width(), bitmap
.height(),
412 bitmap
.width() * bitmap
.bytesPerPixel(), kDefaultEncodingQuality
,
416 resized_image
.MakeThreadSafe();
417 *output_skia
= resized_image
;
424 bool WallpaperManagerBase::ResizeAndSaveWallpaper(const gfx::ImageSkia
& image
,
425 const base::FilePath
& path
,
426 WallpaperLayout layout
,
428 int preferred_height
,
429 gfx::ImageSkia
* output_skia
) {
430 if (layout
== WALLPAPER_LAYOUT_CENTER
) {
431 // TODO(bshe): Generates cropped custom wallpaper for CENTER layout.
432 if (base::PathExists(path
))
433 base::DeleteFile(path
, false);
436 scoped_refptr
<base::RefCountedBytes
> data
;
437 if (ResizeImage(image
, layout
, preferred_width
, preferred_height
, &data
,
439 return SaveWallpaperInternal(
440 path
, reinterpret_cast<const char*>(data
->front()), data
->size());
445 bool WallpaperManagerBase::IsPolicyControlled(
446 const std::string
& user_id
) const {
448 if (!GetUserWallpaperInfo(user_id
, &info
))
450 return info
.type
== user_manager::User::POLICY
;
453 void WallpaperManagerBase::OnPolicySet(const std::string
& policy
,
454 const std::string
& user_id
) {
456 GetUserWallpaperInfo(user_id
, &info
);
457 info
.type
= user_manager::User::POLICY
;
458 SetUserWallpaperInfo(user_id
, info
, true /* is_persistent */);
461 void WallpaperManagerBase::OnPolicyCleared(const std::string
& policy
,
462 const std::string
& user_id
) {
464 GetUserWallpaperInfo(user_id
, &info
);
465 info
.type
= user_manager::User::DEFAULT
;
466 SetUserWallpaperInfo(user_id
, info
, true /* is_persistent */);
467 SetDefaultWallpaperNow(user_id
);
471 base::FilePath
WallpaperManagerBase::GetCustomWallpaperPath(
473 const std::string
& user_id_hash
,
474 const std::string
& file
) {
475 base::FilePath custom_wallpaper_path
= GetCustomWallpaperDir(sub_dir
);
476 return custom_wallpaper_path
.Append(user_id_hash
).Append(file
);
479 WallpaperManagerBase::WallpaperManagerBase()
480 : loaded_wallpapers_for_test_(0),
481 command_line_for_testing_(NULL
),
482 should_cache_wallpaper_(false),
483 weak_factory_(this) {
484 SetDefaultWallpaperPathsFromCommandLine(
485 base::CommandLine::ForCurrentProcess());
486 sequence_token_
= BrowserThread::GetBlockingPool()->GetNamedSequenceToken(
487 kWallpaperSequenceTokenName
);
489 BrowserThread::GetBlockingPool()
490 ->GetSequencedTaskRunnerWithShutdownBehavior(
491 sequence_token_
, base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN
);
494 WallpaperManagerBase::~WallpaperManagerBase() {
495 // TODO(bshe): Lifetime of WallpaperManagerBase needs more consideration.
496 // http://crbug.com/171694
497 weak_factory_
.InvalidateWeakPtrs();
500 void WallpaperManagerBase::SetPolicyControlledWallpaper(
501 const std::string
& user_id
,
502 const user_manager::UserImage
& user_image
) {
503 const user_manager::User
* user
=
504 user_manager::UserManager::Get()->FindUser(user_id
);
506 NOTREACHED() << "Unknown user.";
510 if (user
->username_hash().empty()) {
511 cryptohome::AsyncMethodCaller::GetInstance()->AsyncGetSanitizedUsername(
513 base::Bind(&WallpaperManagerBase::SetCustomWallpaperOnSanitizedUsername
,
514 weak_factory_
.GetWeakPtr(), user_id
, user_image
.image(),
515 true /* update wallpaper */));
517 SetCustomWallpaper(user_id
, user
->username_hash(), "policy-controlled.jpeg",
518 WALLPAPER_LAYOUT_CENTER_CROPPED
,
519 user_manager::User::POLICY
, user_image
.image(),
520 true /* update wallpaper */);
524 void WallpaperManagerBase::SetCustomWallpaperOnSanitizedUsername(
525 const std::string
& user_id
,
526 const gfx::ImageSkia
& image
,
527 bool update_wallpaper
,
528 bool cryptohome_success
,
529 const std::string
& user_id_hash
) {
530 if (!cryptohome_success
)
532 SetCustomWallpaper(user_id
, user_id_hash
, "policy-controlled.jpeg",
533 WALLPAPER_LAYOUT_CENTER_CROPPED
,
534 user_manager::User::POLICY
, image
, update_wallpaper
);
538 void WallpaperManagerBase::SaveCustomWallpaper(
539 const std::string
& user_id_hash
,
540 const base::FilePath
& original_path
,
541 WallpaperLayout layout
,
542 scoped_ptr
<gfx::ImageSkia
> image
) {
544 GetCustomWallpaperDir(kOriginalWallpaperSubDir
).Append(user_id_hash
),
545 true /* recursive */);
547 GetCustomWallpaperDir(kSmallWallpaperSubDir
).Append(user_id_hash
),
548 true /* recursive */);
550 GetCustomWallpaperDir(kLargeWallpaperSubDir
).Append(user_id_hash
),
551 true /* recursive */);
552 EnsureCustomWallpaperDirectories(user_id_hash
);
553 std::string file_name
= original_path
.BaseName().value();
554 base::FilePath small_wallpaper_path
=
555 GetCustomWallpaperPath(kSmallWallpaperSubDir
, user_id_hash
, file_name
);
556 base::FilePath large_wallpaper_path
=
557 GetCustomWallpaperPath(kLargeWallpaperSubDir
, user_id_hash
, file_name
);
559 // Re-encode orginal file to jpeg format and saves the result in case that
560 // resized wallpaper is not generated (i.e. chrome shutdown before resized
561 // wallpaper is saved).
562 ResizeAndSaveWallpaper(*image
, original_path
, WALLPAPER_LAYOUT_STRETCH
,
563 image
->width(), image
->height(), NULL
);
564 ResizeAndSaveWallpaper(*image
, small_wallpaper_path
, layout
,
565 kSmallWallpaperMaxWidth
, kSmallWallpaperMaxHeight
,
567 ResizeAndSaveWallpaper(*image
, large_wallpaper_path
, layout
,
568 kLargeWallpaperMaxWidth
, kLargeWallpaperMaxHeight
,
573 void WallpaperManagerBase::MoveCustomWallpapersOnWorker(
574 const std::string
& user_id
,
575 const std::string
& user_id_hash
,
576 base::WeakPtr
<WallpaperManagerBase
> weak_ptr
) {
577 if (MoveCustomWallpaperDirectory(kOriginalWallpaperSubDir
, user_id
,
579 // Consider success if the original wallpaper is moved to the new directory.
580 // Original wallpaper is the fallback if the correct resolution wallpaper
582 BrowserThread::PostTask(
583 BrowserThread::UI
, FROM_HERE
,
584 base::Bind(&WallpaperManagerBase::MoveCustomWallpapersSuccess
, weak_ptr
,
585 user_id
, user_id_hash
));
587 MoveCustomWallpaperDirectory(kLargeWallpaperSubDir
, user_id
, user_id_hash
);
588 MoveCustomWallpaperDirectory(kSmallWallpaperSubDir
, user_id
, user_id_hash
);
589 MoveCustomWallpaperDirectory(kThumbnailWallpaperSubDir
, user_id
,
594 void WallpaperManagerBase::GetCustomWallpaperInternal(
595 const std::string
& user_id
,
596 const WallpaperInfo
& info
,
597 const base::FilePath
& wallpaper_path
,
598 bool update_wallpaper
,
599 MovableOnDestroyCallbackHolder on_finish
,
600 base::WeakPtr
<WallpaperManagerBase
> weak_ptr
) {
601 base::FilePath valid_path
= wallpaper_path
;
602 if (!base::PathExists(wallpaper_path
)) {
603 // Falls back on original file if the correct resolution file does not
604 // exist. This may happen when the original custom wallpaper is small or
605 // browser shutdown before resized wallpaper saved.
606 valid_path
= GetCustomWallpaperDir(kOriginalWallpaperSubDir
);
607 valid_path
= valid_path
.Append(info
.location
);
610 if (!base::PathExists(valid_path
)) {
611 // Falls back to custom wallpaper that uses email as part of its file path.
612 // Note that email is used instead of user_id_hash here.
613 valid_path
= GetCustomWallpaperPath(kOriginalWallpaperSubDir
, user_id
,
617 if (!base::PathExists(valid_path
)) {
618 LOG(ERROR
) << "Failed to load previously selected custom wallpaper. "
619 << "Fallback to default wallpaper";
620 BrowserThread::PostTask(
621 BrowserThread::UI
, FROM_HERE
,
622 base::Bind(&WallpaperManagerBase::DoSetDefaultWallpaper
, weak_ptr
,
623 user_id
, base::Passed(on_finish
.Pass())));
625 BrowserThread::PostTask(
626 BrowserThread::UI
, FROM_HERE
,
627 base::Bind(&WallpaperManagerBase::StartLoad
, weak_ptr
, user_id
, info
,
628 update_wallpaper
, valid_path
,
629 base::Passed(on_finish
.Pass())));
633 void WallpaperManagerBase::InitInitialUserWallpaper(const std::string
& user_id
,
634 bool is_persistent
) {
635 current_user_wallpaper_info_
.location
= "";
636 current_user_wallpaper_info_
.layout
= WALLPAPER_LAYOUT_CENTER_CROPPED
;
637 current_user_wallpaper_info_
.type
= user_manager::User::DEFAULT
;
638 current_user_wallpaper_info_
.date
= base::Time::Now().LocalMidnight();
640 WallpaperInfo info
= current_user_wallpaper_info_
;
641 SetUserWallpaperInfo(user_id
, info
, is_persistent
);
644 void WallpaperManagerBase::SetUserWallpaperDelayed(const std::string
& user_id
) {
645 ScheduleSetUserWallpaper(user_id
, true);
648 void WallpaperManagerBase::SetUserWallpaperNow(const std::string
& user_id
) {
649 ScheduleSetUserWallpaper(user_id
, false);
652 void WallpaperManagerBase::UpdateWallpaper(bool clear_cache
) {
653 FOR_EACH_OBSERVER(Observer
, observers_
, OnUpdateWallpaperForTesting());
655 wallpaper_cache_
.clear();
656 // For GAIA login flow, the last_selected_user_ may not be set before user
657 // login. If UpdateWallpaper is called at GAIA login screen, no wallpaper will
658 // be set. It could result a black screen on external monitors.
659 // See http://crbug.com/265689 for detail.
660 if (last_selected_user_
.empty()) {
661 SetDefaultWallpaperNow(chromeos::login::kSignInUser
);
664 SetUserWallpaperNow(last_selected_user_
);
667 void WallpaperManagerBase::AddObserver(
668 WallpaperManagerBase::Observer
* observer
) {
669 observers_
.AddObserver(observer
);
672 void WallpaperManagerBase::RemoveObserver(
673 WallpaperManagerBase::Observer
* observer
) {
674 observers_
.RemoveObserver(observer
);
677 void WallpaperManagerBase::NotifyAnimationFinished() {
678 FOR_EACH_OBSERVER(Observer
, observers_
,
679 OnWallpaperAnimationFinished(last_selected_user_
));
682 // WallpaperManager, protected: -----------------------------------------------
684 bool WallpaperManagerBase::GetWallpaperFromCache(const std::string
& user_id
,
685 gfx::ImageSkia
* image
) {
686 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
687 CustomWallpaperMap::const_iterator it
= wallpaper_cache_
.find(user_id
);
688 if (it
!= wallpaper_cache_
.end() && !(*it
).second
.second
.isNull()) {
689 *image
= (*it
).second
.second
;
695 bool WallpaperManagerBase::GetPathFromCache(const std::string
& user_id
,
696 base::FilePath
* path
) {
697 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
698 CustomWallpaperMap::const_iterator it
= wallpaper_cache_
.find(user_id
);
699 if (it
!= wallpaper_cache_
.end()) {
700 *path
= (*it
).second
.first
;
706 int WallpaperManagerBase::loaded_wallpapers_for_test() const {
707 return loaded_wallpapers_for_test_
;
710 void WallpaperManagerBase::CacheUsersWallpapers() {
711 // TODO(dpolukhin): crbug.com/408734.
712 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
713 user_manager::UserList users
= user_manager::UserManager::Get()->GetUsers();
715 if (!users
.empty()) {
716 user_manager::UserList::const_iterator it
= users
.begin();
717 // Skip the wallpaper of first user in the list. It should have been cached.
719 for (int cached
= 0; it
!= users
.end() && cached
< kMaxWallpapersToCache
;
721 std::string user_id
= (*it
)->email();
722 CacheUserWallpaper(user_id
);
727 void WallpaperManagerBase::CacheUserWallpaper(const std::string
& user_id
) {
728 CustomWallpaperMap::iterator it
= wallpaper_cache_
.find(user_id
);
729 if (it
!= wallpaper_cache_
.end() && !it
->second
.second
.isNull())
732 if (GetUserWallpaperInfo(user_id
, &info
)) {
733 if (info
.location
.empty())
736 base::FilePath wallpaper_dir
;
737 base::FilePath wallpaper_path
;
738 if (info
.type
== user_manager::User::CUSTOMIZED
||
739 info
.type
== user_manager::User::POLICY
) {
740 const char* sub_dir
= GetCustomWallpaperSubdirForCurrentResolution();
741 base::FilePath wallpaper_path
= GetCustomWallpaperDir(sub_dir
);
742 wallpaper_path
= wallpaper_path
.Append(info
.location
);
743 // Set the path to the cache.
744 wallpaper_cache_
[user_id
] = CustomWallpaperElement(wallpaper_path
,
746 task_runner_
->PostTask(
748 base::Bind(&WallpaperManagerBase::GetCustomWallpaperInternal
, user_id
,
749 info
, wallpaper_path
, false /* do not update wallpaper */,
750 base::Passed(MovableOnDestroyCallbackHolder()),
751 weak_factory_
.GetWeakPtr()));
754 LoadWallpaper(user_id
, info
, false /* do not update wallpaper */,
755 MovableOnDestroyCallbackHolder().Pass());
759 void WallpaperManagerBase::DeleteUserWallpapers(
760 const std::string
& user_id
,
761 const std::string
& path_to_file
) {
762 std::vector
<base::FilePath
> file_to_remove
;
763 // Remove small user wallpaper.
764 base::FilePath wallpaper_path
= GetCustomWallpaperDir(kSmallWallpaperSubDir
);
765 // Remove old directory if exists
766 file_to_remove
.push_back(wallpaper_path
.Append(user_id
));
767 wallpaper_path
= wallpaper_path
.Append(path_to_file
).DirName();
768 file_to_remove
.push_back(wallpaper_path
);
770 // Remove large user wallpaper.
771 wallpaper_path
= GetCustomWallpaperDir(kLargeWallpaperSubDir
);
772 file_to_remove
.push_back(wallpaper_path
.Append(user_id
));
773 wallpaper_path
= wallpaper_path
.Append(path_to_file
);
774 file_to_remove
.push_back(wallpaper_path
);
776 // Remove user wallpaper thumbnail.
777 wallpaper_path
= GetCustomWallpaperDir(kThumbnailWallpaperSubDir
);
778 file_to_remove
.push_back(wallpaper_path
.Append(user_id
));
779 wallpaper_path
= wallpaper_path
.Append(path_to_file
);
780 file_to_remove
.push_back(wallpaper_path
);
782 // Remove original user wallpaper.
783 wallpaper_path
= GetCustomWallpaperDir(kOriginalWallpaperSubDir
);
784 file_to_remove
.push_back(wallpaper_path
.Append(user_id
));
785 wallpaper_path
= wallpaper_path
.Append(path_to_file
);
786 file_to_remove
.push_back(wallpaper_path
);
788 base::WorkerPool::PostTask(
789 FROM_HERE
, base::Bind(&DeleteWallpaperInList
, file_to_remove
), false);
792 void WallpaperManagerBase::SetCommandLineForTesting(
793 base::CommandLine
* command_line
) {
794 command_line_for_testing_
= command_line
;
795 SetDefaultWallpaperPathsFromCommandLine(command_line
);
798 base::CommandLine
* WallpaperManagerBase::GetCommandLine() {
799 base::CommandLine
* command_line
=
800 command_line_for_testing_
? command_line_for_testing_
801 : base::CommandLine::ForCurrentProcess();
805 void WallpaperManagerBase::LoadWallpaper(
806 const std::string
& user_id
,
807 const WallpaperInfo
& info
,
808 bool update_wallpaper
,
809 MovableOnDestroyCallbackHolder on_finish
) {
810 base::FilePath wallpaper_dir
;
811 base::FilePath wallpaper_path
;
813 // Do a sanity check that file path information is not empty.
814 if (info
.type
== user_manager::User::ONLINE
||
815 info
.type
== user_manager::User::DEFAULT
) {
816 if (info
.location
.empty()) {
817 if (base::SysInfo::IsRunningOnChromeOS()) {
818 NOTREACHED() << "User wallpaper info appears to be broken: " << user_id
;
820 // Filename might be empty on debug configurations when stub users
821 // were created directly in Local State (for testing). Ignore such
822 // errors i.e. allowsuch type of debug configurations on the desktop.
823 LOG(WARNING
) << "User wallpaper info is empty: " << user_id
;
825 // |on_finish| callback will get called on destruction.
831 if (info
.type
== user_manager::User::ONLINE
) {
832 std::string file_name
= GURL(info
.location
).ExtractFileName();
833 WallpaperResolution resolution
= GetAppropriateResolution();
834 // Only solid color wallpapers have stretch layout and they have only one
836 if (info
.layout
!= WALLPAPER_LAYOUT_STRETCH
&&
837 resolution
== WALLPAPER_RESOLUTION_SMALL
) {
838 file_name
= base::FilePath(file_name
)
839 .InsertBeforeExtension(kSmallWallpaperSuffix
)
842 DCHECK(dir_chromeos_wallpapers_path_id
!= -1);
843 CHECK(PathService::Get(dir_chromeos_wallpapers_path_id
,
845 wallpaper_path
= wallpaper_dir
.Append(file_name
);
847 // If the wallpaper exists and it contains already the correct image we can
848 // return immediately.
849 CustomWallpaperMap::iterator it
= wallpaper_cache_
.find(user_id
);
850 if (it
!= wallpaper_cache_
.end() &&
851 it
->second
.first
== wallpaper_path
&&
852 !it
->second
.second
.isNull())
855 loaded_wallpapers_for_test_
++;
856 StartLoad(user_id
, info
, update_wallpaper
, wallpaper_path
,
858 } else if (info
.type
== user_manager::User::DEFAULT
) {
859 // Default wallpapers are migrated from M21 user profiles. A code refactor
860 // overlooked that case and caused these wallpapers not being loaded at all.
861 // On some slow devices, it caused login webui not visible after upgrade to
862 // M26 from M21. See crosbug.com/38429 for details.
863 base::FilePath user_data_dir
;
864 DCHECK(dir_user_data_path_id
!= -1);
865 PathService::Get(dir_user_data_path_id
, &user_data_dir
);
866 wallpaper_path
= user_data_dir
.Append(info
.location
);
867 StartLoad(user_id
, info
, update_wallpaper
, wallpaper_path
,
870 // In unexpected cases, revert to default wallpaper to fail safely. See
871 // crosbug.com/38429.
872 LOG(ERROR
) << "Wallpaper reverts to default unexpected.";
873 DoSetDefaultWallpaper(user_id
, on_finish
.Pass());
877 void WallpaperManagerBase::MoveCustomWallpapersSuccess(
878 const std::string
& user_id
,
879 const std::string
& user_id_hash
) {
881 GetUserWallpaperInfo(user_id
, &info
);
882 if (info
.type
== user_manager::User::CUSTOMIZED
) {
883 // New file field should include user id hash in addition to file name.
884 // This is needed because at login screen, user id hash is not available.
885 info
.location
= base::FilePath(user_id_hash
).Append(info
.location
).value();
887 !user_manager::UserManager::Get()->IsUserNonCryptohomeDataEphemeral(
889 SetUserWallpaperInfo(user_id
, info
, is_persistent
);
893 void WallpaperManagerBase::MoveLoggedInUserCustomWallpaper() {
894 const user_manager::User
* logged_in_user
=
895 user_manager::UserManager::Get()->GetLoggedInUser();
896 if (logged_in_user
) {
897 task_runner_
->PostTask(
899 base::Bind(&WallpaperManagerBase::MoveCustomWallpapersOnWorker
,
900 logged_in_user
->email(), logged_in_user
->username_hash(),
901 weak_factory_
.GetWeakPtr()));
905 void WallpaperManagerBase::SaveLastLoadTime(const base::TimeDelta elapsed
) {
906 while (last_load_times_
.size() >= kLastLoadsStatsMsMaxSize
)
907 last_load_times_
.pop_front();
909 if (elapsed
> base::TimeDelta::FromMicroseconds(0)) {
910 last_load_times_
.push_back(elapsed
);
911 last_load_finished_at_
= base::Time::Now();
915 base::TimeDelta
WallpaperManagerBase::GetWallpaperLoadDelay() const {
916 base::TimeDelta delay
;
918 if (last_load_times_
.size() == 0) {
919 delay
= base::TimeDelta::FromMilliseconds(kLoadDefaultDelayMs
);
921 delay
= std::accumulate(last_load_times_
.begin(), last_load_times_
.end(),
922 base::TimeDelta(), std::plus
<base::TimeDelta
>()) /
923 last_load_times_
.size();
926 if (delay
< base::TimeDelta::FromMilliseconds(kLoadMinDelayMs
))
927 delay
= base::TimeDelta::FromMilliseconds(kLoadMinDelayMs
);
928 else if (delay
> base::TimeDelta::FromMilliseconds(kLoadMaxDelayMs
))
929 delay
= base::TimeDelta::FromMilliseconds(kLoadMaxDelayMs
);
931 // If we had ever loaded wallpaper, adjust wait delay by time since last load.
932 if (!last_load_finished_at_
.is_null()) {
933 const base::TimeDelta interval
= base::Time::Now() - last_load_finished_at_
;
934 if (interval
> delay
)
935 delay
= base::TimeDelta::FromMilliseconds(0);
936 else if (interval
> base::TimeDelta::FromMilliseconds(0))
942 void WallpaperManagerBase::OnCustomizedDefaultWallpaperDecoded(
943 const GURL
& wallpaper_url
,
944 scoped_ptr
<CustomizedWallpaperRescaledFiles
> rescaled_files
,
945 const user_manager::UserImage
& wallpaper
) {
946 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
948 // If decoded wallpaper is empty, we have probably failed to decode the file.
949 if (wallpaper
.image().isNull()) {
950 LOG(WARNING
) << "Failed to decode customized wallpaper.";
954 wallpaper
.image().EnsureRepsForSupportedScales();
955 scoped_ptr
<gfx::ImageSkia
> deep_copy(wallpaper
.image().DeepCopy());
957 scoped_ptr
<bool> success(new bool(false));
958 scoped_ptr
<gfx::ImageSkia
> small_wallpaper_image(new gfx::ImageSkia
);
959 scoped_ptr
<gfx::ImageSkia
> large_wallpaper_image(new gfx::ImageSkia
);
961 // TODO(bshe): This may break if RawImage becomes RefCountedMemory.
962 base::Closure resize_closure
= base::Bind(
963 &WallpaperManagerBase::ResizeCustomizedDefaultWallpaper
,
964 base::Passed(&deep_copy
), wallpaper
.raw_image(),
965 base::Unretained(rescaled_files
.get()), base::Unretained(success
.get()),
966 base::Unretained(small_wallpaper_image
.get()),
967 base::Unretained(large_wallpaper_image
.get()));
968 base::Closure on_resized_closure
= base::Bind(
969 &WallpaperManagerBase::OnCustomizedDefaultWallpaperResized
,
970 weak_factory_
.GetWeakPtr(), wallpaper_url
,
971 base::Passed(rescaled_files
.Pass()), base::Passed(success
.Pass()),
972 base::Passed(small_wallpaper_image
.Pass()),
973 base::Passed(large_wallpaper_image
.Pass()));
975 if (!task_runner_
->PostTaskAndReply(FROM_HERE
, resize_closure
,
976 on_resized_closure
)) {
977 LOG(WARNING
) << "Failed to start Customized Wallpaper resize.";
981 void WallpaperManagerBase::ResizeCustomizedDefaultWallpaper(
982 scoped_ptr
<gfx::ImageSkia
> image
,
983 const user_manager::UserImage::RawImage
& raw_image
,
984 const CustomizedWallpaperRescaledFiles
* rescaled_files
,
986 gfx::ImageSkia
* small_wallpaper_image
,
987 gfx::ImageSkia
* large_wallpaper_image
) {
990 *success
&= ResizeAndSaveWallpaper(
991 *image
, rescaled_files
->path_rescaled_small(), WALLPAPER_LAYOUT_STRETCH
,
992 kSmallWallpaperMaxWidth
, kSmallWallpaperMaxHeight
, small_wallpaper_image
);
994 *success
&= ResizeAndSaveWallpaper(
995 *image
, rescaled_files
->path_rescaled_large(), WALLPAPER_LAYOUT_STRETCH
,
996 kLargeWallpaperMaxWidth
, kLargeWallpaperMaxHeight
, large_wallpaper_image
);
999 void WallpaperManagerBase::SetCustomizedDefaultWallpaper(
1000 const GURL
& wallpaper_url
,
1001 const base::FilePath
& downloaded_file
,
1002 const base::FilePath
& resized_directory
) {
1003 // Should fail if this ever happens in tests.
1004 DCHECK(wallpaper_url
.is_valid());
1005 if (!wallpaper_url
.is_valid()) {
1006 if (!wallpaper_url
.is_empty()) {
1007 LOG(WARNING
) << "Invalid Customized Wallpaper URL '"
1008 << wallpaper_url
.spec() << "'";
1012 std::string downloaded_file_name
= downloaded_file
.BaseName().value();
1013 scoped_ptr
<CustomizedWallpaperRescaledFiles
> rescaled_files(
1014 new CustomizedWallpaperRescaledFiles(
1015 downloaded_file
, resized_directory
.Append(downloaded_file_name
+
1016 kSmallWallpaperSuffix
),
1017 resized_directory
.Append(downloaded_file_name
+
1018 kLargeWallpaperSuffix
)));
1020 base::Closure check_file_exists
= rescaled_files
->CreateCheckerClosure();
1021 base::Closure on_checked_closure
=
1022 base::Bind(&WallpaperManagerBase::SetCustomizedDefaultWallpaperAfterCheck
,
1023 weak_factory_
.GetWeakPtr(), wallpaper_url
, downloaded_file
,
1024 base::Passed(rescaled_files
.Pass()));
1025 if (!BrowserThread::PostBlockingPoolTaskAndReply(FROM_HERE
, check_file_exists
,
1026 on_checked_closure
)) {
1027 LOG(WARNING
) << "Failed to start check CheckCustomizedWallpaperFilesExist.";
1031 void WallpaperManagerBase::SetDefaultWallpaperPathsFromCommandLine(
1032 base::CommandLine
* command_line
) {
1033 default_small_wallpaper_file_
= command_line
->GetSwitchValuePath(
1034 chromeos::switches::kDefaultWallpaperSmall
);
1035 default_large_wallpaper_file_
= command_line
->GetSwitchValuePath(
1036 chromeos::switches::kDefaultWallpaperLarge
);
1037 guest_small_wallpaper_file_
= command_line
->GetSwitchValuePath(
1038 chromeos::switches::kGuestWallpaperSmall
);
1039 guest_large_wallpaper_file_
= command_line
->GetSwitchValuePath(
1040 chromeos::switches::kGuestWallpaperLarge
);
1041 child_small_wallpaper_file_
= command_line
->GetSwitchValuePath(
1042 chromeos::switches::kChildWallpaperSmall
);
1043 child_large_wallpaper_file_
= command_line
->GetSwitchValuePath(
1044 chromeos::switches::kChildWallpaperLarge
);
1045 default_wallpaper_image_
.reset();
1049 WallpaperManagerBase::GetCustomWallpaperSubdirForCurrentResolution() {
1050 WallpaperResolution resolution
= GetAppropriateResolution();
1051 return resolution
== WALLPAPER_RESOLUTION_SMALL
? kSmallWallpaperSubDir
1052 : kLargeWallpaperSubDir
;
1055 void WallpaperManagerBase::CreateSolidDefaultWallpaper() {
1056 loaded_wallpapers_for_test_
++;
1058 bitmap
.allocN32Pixels(1, 1);
1059 bitmap
.eraseColor(kDefaultWallpaperColor
);
1060 const gfx::ImageSkia image
= gfx::ImageSkia::CreateFrom1xBitmap(bitmap
);
1061 default_wallpaper_image_
.reset(new user_manager::UserImage(image
));
1064 } // namespace wallpaper