Adds fake hardware video encoder.
[chromium-blink-merge.git] / components / wallpaper / wallpaper_manager_base.cc
blob28c7fa7002d6640f4f414ed67a4801db6f7f058f
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"
7 #include <numeric>
8 #include <vector>
9 #include "base/bind.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;
44 namespace wallpaper {
46 namespace {
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
67 // color.
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);
85 return false;
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.
103 // static
104 void EnsureCustomWallpaperDirectories(const std::string& user_id_hash) {
105 base::FilePath dir;
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,
127 const char* data,
128 int size) {
129 int written_bytes = base::WriteFile(path, data, size);
130 return written_bytes == size;
133 } // namespace
135 MovableOnDestroyCallback::MovableOnDestroyCallback(
136 const base::Closure& callback)
137 : callback_(callback) {
140 MovableOnDestroyCallback::~MovableOnDestroyCallback() {
141 if (!callback_.is_null())
142 callback_.Run();
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),
155 layout(in_layout),
156 type(in_type),
157 date(in_date) {
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()
187 const {
188 return path_downloaded_;
191 const base::FilePath&
192 WallpaperManagerBase::CustomizedWallpaperRescaledFiles::path_rescaled_small()
193 const {
194 return path_rescaled_small_;
197 const base::FilePath&
198 WallpaperManagerBase::CustomizedWallpaperRescaledFiles::path_rescaled_large()
199 const {
200 return path_rescaled_large_;
203 bool WallpaperManagerBase::CustomizedWallpaperRescaledFiles::downloaded_exists()
204 const {
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) {
230 base::Closure
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()
246 const {
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 base::FilePath WallpaperManagerBase::TestApi::current_wallpaper_path() {
261 return wallpaper_manager_->current_wallpaper_path_;
264 bool WallpaperManagerBase::TestApi::GetWallpaperFromCache(
265 const std::string& user_id,
266 gfx::ImageSkia* image) {
267 return wallpaper_manager_->GetWallpaperFromCache(user_id, image);
270 void WallpaperManagerBase::TestApi::SetWallpaperCache(
271 const std::string& user_id,
272 const gfx::ImageSkia& image) {
273 DCHECK(!image.isNull());
274 wallpaper_manager_->wallpaper_cache_[user_id] = image;
277 void WallpaperManagerBase::TestApi::ClearDisposableWallpaperCache() {
278 wallpaper_manager_->ClearDisposableWallpaperCache();
281 // static
282 void WallpaperManagerBase::SetPathIds(
283 int dir_user_data_enum,
284 int dir_chromeos_wallpapers_enum,
285 int dir_chromeos_custom_wallpapers_enum) {
286 dir_user_data_path_id = dir_user_data_enum;
287 dir_chromeos_wallpapers_path_id = dir_chromeos_wallpapers_enum;
288 dir_chromeos_custom_wallpapers_path_id = dir_chromeos_custom_wallpapers_enum;
291 // static
292 base::FilePath WallpaperManagerBase::GetCustomWallpaperDir(
293 const char* sub_dir) {
294 base::FilePath custom_wallpaper_dir;
295 DCHECK(dir_chromeos_custom_wallpapers_path_id != -1);
296 CHECK(PathService::Get(dir_chromeos_custom_wallpapers_path_id,
297 &custom_wallpaper_dir));
298 return custom_wallpaper_dir.Append(sub_dir);
301 // static
302 void WallpaperManagerBase::RegisterPrefs(PrefRegistrySimple* registry) {
303 registry->RegisterDictionaryPref(kUsersWallpaperInfo);
304 registry->RegisterDictionaryPref(kUserWallpapers);
305 registry->RegisterDictionaryPref(kUserWallpapersProperties);
308 void WallpaperManagerBase::EnsureLoggedInUserWallpaperLoaded() {
309 WallpaperInfo info;
310 if (GetLoggedInUserWallpaperInfo(&info)) {
311 UMA_HISTOGRAM_ENUMERATION("Ash.Wallpaper.Type", info.type,
312 user_manager::User::WALLPAPER_TYPE_COUNT);
313 if (info == current_user_wallpaper_info_)
314 return;
316 SetUserWallpaperNow(
317 user_manager::UserManager::Get()->GetLoggedInUser()->email());
320 void WallpaperManagerBase::ClearDisposableWallpaperCache() {
321 // Cancel callback for previous cache requests.
322 weak_factory_.InvalidateWeakPtrs();
323 // Keep the wallpaper of logged in users in cache at multi-profile mode.
324 std::set<std::string> logged_in_users_names;
325 const user_manager::UserList& logged_users =
326 user_manager::UserManager::Get()->GetLoggedInUsers();
327 for (user_manager::UserList::const_iterator it = logged_users.begin();
328 it != logged_users.end(); ++it) {
329 logged_in_users_names.insert((*it)->email());
332 CustomWallpaperMap logged_in_users_cache;
333 for (CustomWallpaperMap::iterator it = wallpaper_cache_.begin();
334 it != wallpaper_cache_.end(); ++it) {
335 if (logged_in_users_names.find(it->first) != logged_in_users_names.end()) {
336 logged_in_users_cache.insert(*it);
339 wallpaper_cache_ = logged_in_users_cache;
342 bool WallpaperManagerBase::GetLoggedInUserWallpaperInfo(WallpaperInfo* info) {
343 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
345 if (user_manager::UserManager::Get()->IsLoggedInAsStub()) {
346 info->location = current_user_wallpaper_info_.location = "";
347 info->layout = current_user_wallpaper_info_.layout =
348 WALLPAPER_LAYOUT_CENTER_CROPPED;
349 info->type = current_user_wallpaper_info_.type =
350 user_manager::User::DEFAULT;
351 info->date = current_user_wallpaper_info_.date =
352 base::Time::Now().LocalMidnight();
353 return true;
356 return GetUserWallpaperInfo(
357 user_manager::UserManager::Get()->GetLoggedInUser()->email(), info);
360 // static
361 bool WallpaperManagerBase::ResizeImage(
362 const gfx::ImageSkia& image,
363 WallpaperLayout layout,
364 int preferred_width,
365 int preferred_height,
366 scoped_refptr<base::RefCountedBytes>* output,
367 gfx::ImageSkia* output_skia) {
368 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
369 int width = image.width();
370 int height = image.height();
371 int resized_width;
372 int resized_height;
373 *output = new base::RefCountedBytes();
375 if (layout == WALLPAPER_LAYOUT_CENTER_CROPPED) {
376 // Do not resize custom wallpaper if it is smaller than preferred size.
377 if (!(width > preferred_width && height > preferred_height))
378 return false;
380 double horizontal_ratio = static_cast<double>(preferred_width) / width;
381 double vertical_ratio = static_cast<double>(preferred_height) / height;
382 if (vertical_ratio > horizontal_ratio) {
383 resized_width =
384 gfx::ToRoundedInt(static_cast<double>(width) * vertical_ratio);
385 resized_height = preferred_height;
386 } else {
387 resized_width = preferred_width;
388 resized_height =
389 gfx::ToRoundedInt(static_cast<double>(height) * horizontal_ratio);
391 } else if (layout == WALLPAPER_LAYOUT_STRETCH) {
392 resized_width = preferred_width;
393 resized_height = preferred_height;
394 } else {
395 resized_width = width;
396 resized_height = height;
399 gfx::ImageSkia resized_image = gfx::ImageSkiaOperations::CreateResizedImage(
400 image, skia::ImageOperations::RESIZE_LANCZOS3,
401 gfx::Size(resized_width, resized_height));
403 SkBitmap bitmap = *(resized_image.bitmap());
404 SkAutoLockPixels lock_input(bitmap);
405 gfx::JPEGCodec::Encode(
406 reinterpret_cast<unsigned char*>(bitmap.getAddr32(0, 0)),
407 gfx::JPEGCodec::FORMAT_SkBitmap, bitmap.width(), bitmap.height(),
408 bitmap.width() * bitmap.bytesPerPixel(), kDefaultEncodingQuality,
409 &(*output)->data());
411 if (output_skia) {
412 resized_image.MakeThreadSafe();
413 *output_skia = resized_image;
416 return true;
419 // static
420 bool WallpaperManagerBase::ResizeAndSaveWallpaper(const gfx::ImageSkia& image,
421 const base::FilePath& path,
422 WallpaperLayout layout,
423 int preferred_width,
424 int preferred_height,
425 gfx::ImageSkia* output_skia) {
426 if (layout == WALLPAPER_LAYOUT_CENTER) {
427 // TODO(bshe): Generates cropped custom wallpaper for CENTER layout.
428 if (base::PathExists(path))
429 base::DeleteFile(path, false);
430 return false;
432 scoped_refptr<base::RefCountedBytes> data;
433 if (ResizeImage(image, layout, preferred_width, preferred_height, &data,
434 output_skia)) {
435 return SaveWallpaperInternal(
436 path, reinterpret_cast<const char*>(data->front()), data->size());
438 return false;
441 bool WallpaperManagerBase::IsPolicyControlled(
442 const std::string& user_id) const {
443 WallpaperInfo info;
444 if (!GetUserWallpaperInfo(user_id, &info))
445 return false;
446 return info.type == user_manager::User::POLICY;
449 void WallpaperManagerBase::OnPolicySet(const std::string& policy,
450 const std::string& user_id) {
451 WallpaperInfo info;
452 GetUserWallpaperInfo(user_id, &info);
453 info.type = user_manager::User::POLICY;
454 SetUserWallpaperInfo(user_id, info, true /* is_persistent */);
457 void WallpaperManagerBase::OnPolicyCleared(const std::string& policy,
458 const std::string& user_id) {
459 WallpaperInfo info;
460 GetUserWallpaperInfo(user_id, &info);
461 info.type = user_manager::User::DEFAULT;
462 SetUserWallpaperInfo(user_id, info, true /* is_persistent */);
463 SetDefaultWallpaperNow(user_id);
466 // static
467 base::FilePath WallpaperManagerBase::GetCustomWallpaperPath(
468 const char* sub_dir,
469 const std::string& user_id_hash,
470 const std::string& file) {
471 base::FilePath custom_wallpaper_path = GetCustomWallpaperDir(sub_dir);
472 return custom_wallpaper_path.Append(user_id_hash).Append(file);
475 WallpaperManagerBase::WallpaperManagerBase()
476 : loaded_wallpapers_(0),
477 command_line_for_testing_(NULL),
478 should_cache_wallpaper_(false),
479 weak_factory_(this) {
480 SetDefaultWallpaperPathsFromCommandLine(
481 base::CommandLine::ForCurrentProcess());
482 sequence_token_ = BrowserThread::GetBlockingPool()->GetNamedSequenceToken(
483 kWallpaperSequenceTokenName);
484 task_runner_ =
485 BrowserThread::GetBlockingPool()
486 ->GetSequencedTaskRunnerWithShutdownBehavior(
487 sequence_token_, base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
490 WallpaperManagerBase::~WallpaperManagerBase() {
491 // TODO(bshe): Lifetime of WallpaperManagerBase needs more consideration.
492 // http://crbug.com/171694
493 weak_factory_.InvalidateWeakPtrs();
496 void WallpaperManagerBase::SetPolicyControlledWallpaper(
497 const std::string& user_id,
498 const user_manager::UserImage& user_image) {
499 const user_manager::User* user =
500 user_manager::UserManager::Get()->FindUser(user_id);
501 if (!user) {
502 NOTREACHED() << "Unknown user.";
503 return;
506 if (user->username_hash().empty()) {
507 cryptohome::AsyncMethodCaller::GetInstance()->AsyncGetSanitizedUsername(
508 user_id,
509 base::Bind(&WallpaperManagerBase::SetCustomWallpaperOnSanitizedUsername,
510 weak_factory_.GetWeakPtr(), user_id, user_image.image(),
511 true /* update wallpaper */));
512 } else {
513 SetCustomWallpaper(user_id, user->username_hash(), "policy-controlled.jpeg",
514 WALLPAPER_LAYOUT_CENTER_CROPPED,
515 user_manager::User::POLICY, user_image.image(),
516 true /* update wallpaper */);
520 void WallpaperManagerBase::SetCustomWallpaperOnSanitizedUsername(
521 const std::string& user_id,
522 const gfx::ImageSkia& image,
523 bool update_wallpaper,
524 bool cryptohome_success,
525 const std::string& user_id_hash) {
526 if (!cryptohome_success)
527 return;
528 SetCustomWallpaper(user_id, user_id_hash, "policy-controlled.jpeg",
529 WALLPAPER_LAYOUT_CENTER_CROPPED,
530 user_manager::User::POLICY, image, update_wallpaper);
533 // static
534 void WallpaperManagerBase::SaveCustomWallpaper(
535 const std::string& user_id_hash,
536 const base::FilePath& original_path,
537 WallpaperLayout layout,
538 scoped_ptr<gfx::ImageSkia> image) {
539 base::DeleteFile(
540 GetCustomWallpaperDir(kOriginalWallpaperSubDir).Append(user_id_hash),
541 true /* recursive */);
542 base::DeleteFile(
543 GetCustomWallpaperDir(kSmallWallpaperSubDir).Append(user_id_hash),
544 true /* recursive */);
545 base::DeleteFile(
546 GetCustomWallpaperDir(kLargeWallpaperSubDir).Append(user_id_hash),
547 true /* recursive */);
548 EnsureCustomWallpaperDirectories(user_id_hash);
549 std::string file_name = original_path.BaseName().value();
550 base::FilePath small_wallpaper_path =
551 GetCustomWallpaperPath(kSmallWallpaperSubDir, user_id_hash, file_name);
552 base::FilePath large_wallpaper_path =
553 GetCustomWallpaperPath(kLargeWallpaperSubDir, user_id_hash, file_name);
555 // Re-encode orginal file to jpeg format and saves the result in case that
556 // resized wallpaper is not generated (i.e. chrome shutdown before resized
557 // wallpaper is saved).
558 ResizeAndSaveWallpaper(*image, original_path, WALLPAPER_LAYOUT_STRETCH,
559 image->width(), image->height(), NULL);
560 ResizeAndSaveWallpaper(*image, small_wallpaper_path, layout,
561 kSmallWallpaperMaxWidth, kSmallWallpaperMaxHeight,
562 NULL);
563 ResizeAndSaveWallpaper(*image, large_wallpaper_path, layout,
564 kLargeWallpaperMaxWidth, kLargeWallpaperMaxHeight,
565 NULL);
568 // static
569 void WallpaperManagerBase::MoveCustomWallpapersOnWorker(
570 const std::string& user_id,
571 const std::string& user_id_hash,
572 base::WeakPtr<WallpaperManagerBase> weak_ptr) {
573 if (MoveCustomWallpaperDirectory(kOriginalWallpaperSubDir, user_id,
574 user_id_hash)) {
575 // Consider success if the original wallpaper is moved to the new directory.
576 // Original wallpaper is the fallback if the correct resolution wallpaper
577 // can not be found.
578 BrowserThread::PostTask(
579 BrowserThread::UI, FROM_HERE,
580 base::Bind(&WallpaperManagerBase::MoveCustomWallpapersSuccess, weak_ptr,
581 user_id, user_id_hash));
583 MoveCustomWallpaperDirectory(kLargeWallpaperSubDir, user_id, user_id_hash);
584 MoveCustomWallpaperDirectory(kSmallWallpaperSubDir, user_id, user_id_hash);
585 MoveCustomWallpaperDirectory(kThumbnailWallpaperSubDir, user_id,
586 user_id_hash);
589 // static
590 void WallpaperManagerBase::GetCustomWallpaperInternal(
591 const std::string& user_id,
592 const WallpaperInfo& info,
593 const base::FilePath& wallpaper_path,
594 bool update_wallpaper,
595 MovableOnDestroyCallbackHolder on_finish,
596 base::WeakPtr<WallpaperManagerBase> weak_ptr) {
597 base::FilePath valid_path = wallpaper_path;
598 if (!base::PathExists(wallpaper_path)) {
599 // Falls back on original file if the correct resolution file does not
600 // exist. This may happen when the original custom wallpaper is small or
601 // browser shutdown before resized wallpaper saved.
602 valid_path = GetCustomWallpaperDir(kOriginalWallpaperSubDir);
603 valid_path = valid_path.Append(info.location);
606 if (!base::PathExists(valid_path)) {
607 // Falls back to custom wallpaper that uses email as part of its file path.
608 // Note that email is used instead of user_id_hash here.
609 valid_path = GetCustomWallpaperPath(kOriginalWallpaperSubDir, user_id,
610 info.location);
613 if (!base::PathExists(valid_path)) {
614 LOG(ERROR) << "Failed to load previously selected custom wallpaper. "
615 << "Fallback to default wallpaper";
616 BrowserThread::PostTask(
617 BrowserThread::UI, FROM_HERE,
618 base::Bind(&WallpaperManagerBase::DoSetDefaultWallpaper, weak_ptr,
619 user_id, base::Passed(on_finish.Pass())));
620 } else {
621 BrowserThread::PostTask(
622 BrowserThread::UI, FROM_HERE,
623 base::Bind(&WallpaperManagerBase::StartLoad, weak_ptr, user_id, info,
624 update_wallpaper, valid_path,
625 base::Passed(on_finish.Pass())));
629 void WallpaperManagerBase::InitInitialUserWallpaper(const std::string& user_id,
630 bool is_persistent) {
631 current_user_wallpaper_info_.location = "";
632 current_user_wallpaper_info_.layout = WALLPAPER_LAYOUT_CENTER_CROPPED;
633 current_user_wallpaper_info_.type = user_manager::User::DEFAULT;
634 current_user_wallpaper_info_.date = base::Time::Now().LocalMidnight();
636 WallpaperInfo info = current_user_wallpaper_info_;
637 SetUserWallpaperInfo(user_id, info, is_persistent);
640 void WallpaperManagerBase::SetUserWallpaperDelayed(const std::string& user_id) {
641 ScheduleSetUserWallpaper(user_id, true);
644 void WallpaperManagerBase::SetUserWallpaperNow(const std::string& user_id) {
645 ScheduleSetUserWallpaper(user_id, false);
648 void WallpaperManagerBase::UpdateWallpaper(bool clear_cache) {
649 FOR_EACH_OBSERVER(Observer, observers_, OnUpdateWallpaperForTesting());
650 if (clear_cache)
651 wallpaper_cache_.clear();
652 current_wallpaper_path_.clear();
653 // For GAIA login flow, the last_selected_user_ may not be set before user
654 // login. If UpdateWallpaper is called at GAIA login screen, no wallpaper will
655 // be set. It could result a black screen on external monitors.
656 // See http://crbug.com/265689 for detail.
657 if (last_selected_user_.empty()) {
658 SetDefaultWallpaperNow(chromeos::login::kSignInUser);
659 return;
661 SetUserWallpaperNow(last_selected_user_);
664 void WallpaperManagerBase::AddObserver(
665 WallpaperManagerBase::Observer* observer) {
666 observers_.AddObserver(observer);
669 void WallpaperManagerBase::RemoveObserver(
670 WallpaperManagerBase::Observer* observer) {
671 observers_.RemoveObserver(observer);
674 void WallpaperManagerBase::NotifyAnimationFinished() {
675 FOR_EACH_OBSERVER(Observer, observers_,
676 OnWallpaperAnimationFinished(last_selected_user_));
679 // WallpaperManager, protected: -----------------------------------------------
681 bool WallpaperManagerBase::GetWallpaperFromCache(const std::string& user_id,
682 gfx::ImageSkia* image) {
683 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
684 CustomWallpaperMap::const_iterator it = wallpaper_cache_.find(user_id);
685 if (it != wallpaper_cache_.end()) {
686 *image = (*it).second;
687 return true;
689 return false;
692 int WallpaperManagerBase::loaded_wallpapers() const {
693 return loaded_wallpapers_;
696 void WallpaperManagerBase::CacheUsersWallpapers() {
697 // TODO(dpolukhin): crbug.com/408734.
698 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
699 user_manager::UserList users = user_manager::UserManager::Get()->GetUsers();
701 if (!users.empty()) {
702 user_manager::UserList::const_iterator it = users.begin();
703 // Skip the wallpaper of first user in the list. It should have been cached.
704 it++;
705 for (int cached = 0; it != users.end() && cached < kMaxWallpapersToCache;
706 ++it, ++cached) {
707 std::string user_id = (*it)->email();
708 CacheUserWallpaper(user_id);
713 void WallpaperManagerBase::CacheUserWallpaper(const std::string& user_id) {
714 if (wallpaper_cache_.find(user_id) != wallpaper_cache_.end())
715 return;
716 WallpaperInfo info;
717 if (GetUserWallpaperInfo(user_id, &info)) {
718 if (info.location.empty())
719 return;
721 base::FilePath wallpaper_dir;
722 base::FilePath wallpaper_path;
723 if (info.type == user_manager::User::CUSTOMIZED ||
724 info.type == user_manager::User::POLICY) {
725 const char* sub_dir = GetCustomWallpaperSubdirForCurrentResolution();
726 base::FilePath wallpaper_path = GetCustomWallpaperDir(sub_dir);
727 wallpaper_path = wallpaper_path.Append(info.location);
728 task_runner_->PostTask(
729 FROM_HERE,
730 base::Bind(&WallpaperManagerBase::GetCustomWallpaperInternal, user_id,
731 info, wallpaper_path, false /* do not update wallpaper */,
732 base::Passed(MovableOnDestroyCallbackHolder()),
733 weak_factory_.GetWeakPtr()));
734 return;
736 LoadWallpaper(user_id, info, false /* do not update wallpaper */,
737 MovableOnDestroyCallbackHolder().Pass());
741 void WallpaperManagerBase::DeleteUserWallpapers(
742 const std::string& user_id,
743 const std::string& path_to_file) {
744 std::vector<base::FilePath> file_to_remove;
745 // Remove small user wallpaper.
746 base::FilePath wallpaper_path = GetCustomWallpaperDir(kSmallWallpaperSubDir);
747 // Remove old directory if exists
748 file_to_remove.push_back(wallpaper_path.Append(user_id));
749 wallpaper_path = wallpaper_path.Append(path_to_file).DirName();
750 file_to_remove.push_back(wallpaper_path);
752 // Remove large user wallpaper.
753 wallpaper_path = GetCustomWallpaperDir(kLargeWallpaperSubDir);
754 file_to_remove.push_back(wallpaper_path.Append(user_id));
755 wallpaper_path = wallpaper_path.Append(path_to_file);
756 file_to_remove.push_back(wallpaper_path);
758 // Remove user wallpaper thumbnail.
759 wallpaper_path = GetCustomWallpaperDir(kThumbnailWallpaperSubDir);
760 file_to_remove.push_back(wallpaper_path.Append(user_id));
761 wallpaper_path = wallpaper_path.Append(path_to_file);
762 file_to_remove.push_back(wallpaper_path);
764 // Remove original user wallpaper.
765 wallpaper_path = GetCustomWallpaperDir(kOriginalWallpaperSubDir);
766 file_to_remove.push_back(wallpaper_path.Append(user_id));
767 wallpaper_path = wallpaper_path.Append(path_to_file);
768 file_to_remove.push_back(wallpaper_path);
770 base::WorkerPool::PostTask(
771 FROM_HERE, base::Bind(&DeleteWallpaperInList, file_to_remove), false);
774 void WallpaperManagerBase::SetCommandLineForTesting(
775 base::CommandLine* command_line) {
776 command_line_for_testing_ = command_line;
777 SetDefaultWallpaperPathsFromCommandLine(command_line);
780 base::CommandLine* WallpaperManagerBase::GetCommandLine() {
781 base::CommandLine* command_line =
782 command_line_for_testing_ ? command_line_for_testing_
783 : base::CommandLine::ForCurrentProcess();
784 return command_line;
787 void WallpaperManagerBase::LoadWallpaper(
788 const std::string& user_id,
789 const WallpaperInfo& info,
790 bool update_wallpaper,
791 MovableOnDestroyCallbackHolder on_finish) {
792 base::FilePath wallpaper_dir;
793 base::FilePath wallpaper_path;
795 // Do a sanity check that file path information is not empty.
796 if (info.type == user_manager::User::ONLINE ||
797 info.type == user_manager::User::DEFAULT) {
798 if (info.location.empty()) {
799 if (base::SysInfo::IsRunningOnChromeOS()) {
800 NOTREACHED() << "User wallpaper info appears to be broken: " << user_id;
801 } else {
802 // Filename might be empty on debug configurations when stub users
803 // were created directly in Local State (for testing). Ignore such
804 // errors i.e. allowsuch type of debug configurations on the desktop.
805 LOG(WARNING) << "User wallpaper info is empty: " << user_id;
807 // |on_finish| callback will get called on destruction.
808 return;
813 if (info.type == user_manager::User::ONLINE) {
814 std::string file_name = GURL(info.location).ExtractFileName();
815 WallpaperResolution resolution = GetAppropriateResolution();
816 // Only solid color wallpapers have stretch layout and they have only one
817 // resolution.
818 if (info.layout != WALLPAPER_LAYOUT_STRETCH &&
819 resolution == WALLPAPER_RESOLUTION_SMALL) {
820 file_name = base::FilePath(file_name)
821 .InsertBeforeExtension(kSmallWallpaperSuffix)
822 .value();
824 DCHECK(dir_chromeos_wallpapers_path_id != -1);
825 CHECK(PathService::Get(dir_chromeos_wallpapers_path_id,
826 &wallpaper_dir));
827 wallpaper_path = wallpaper_dir.Append(file_name);
828 if (current_wallpaper_path_ == wallpaper_path)
829 return;
831 if (update_wallpaper)
832 current_wallpaper_path_ = wallpaper_path;
834 loaded_wallpapers_++;
835 StartLoad(user_id, info, update_wallpaper, wallpaper_path,
836 on_finish.Pass());
837 } else if (info.type == user_manager::User::DEFAULT) {
838 // Default wallpapers are migrated from M21 user profiles. A code refactor
839 // overlooked that case and caused these wallpapers not being loaded at all.
840 // On some slow devices, it caused login webui not visible after upgrade to
841 // M26 from M21. See crosbug.com/38429 for details.
842 base::FilePath user_data_dir;
843 DCHECK(dir_user_data_path_id != -1);
844 PathService::Get(dir_user_data_path_id, &user_data_dir);
845 wallpaper_path = user_data_dir.Append(info.location);
846 StartLoad(user_id, info, update_wallpaper, wallpaper_path,
847 on_finish.Pass());
848 } else {
849 // In unexpected cases, revert to default wallpaper to fail safely. See
850 // crosbug.com/38429.
851 LOG(ERROR) << "Wallpaper reverts to default unexpected.";
852 DoSetDefaultWallpaper(user_id, on_finish.Pass());
856 void WallpaperManagerBase::MoveCustomWallpapersSuccess(
857 const std::string& user_id,
858 const std::string& user_id_hash) {
859 WallpaperInfo info;
860 GetUserWallpaperInfo(user_id, &info);
861 if (info.type == user_manager::User::CUSTOMIZED) {
862 // New file field should include user id hash in addition to file name.
863 // This is needed because at login screen, user id hash is not available.
864 info.location = base::FilePath(user_id_hash).Append(info.location).value();
865 bool is_persistent =
866 !user_manager::UserManager::Get()->IsUserNonCryptohomeDataEphemeral(
867 user_id);
868 SetUserWallpaperInfo(user_id, info, is_persistent);
872 void WallpaperManagerBase::MoveLoggedInUserCustomWallpaper() {
873 const user_manager::User* logged_in_user =
874 user_manager::UserManager::Get()->GetLoggedInUser();
875 if (logged_in_user) {
876 task_runner_->PostTask(
877 FROM_HERE,
878 base::Bind(&WallpaperManagerBase::MoveCustomWallpapersOnWorker,
879 logged_in_user->email(), logged_in_user->username_hash(),
880 weak_factory_.GetWeakPtr()));
884 void WallpaperManagerBase::SaveLastLoadTime(const base::TimeDelta elapsed) {
885 while (last_load_times_.size() >= kLastLoadsStatsMsMaxSize)
886 last_load_times_.pop_front();
888 if (elapsed > base::TimeDelta::FromMicroseconds(0)) {
889 last_load_times_.push_back(elapsed);
890 last_load_finished_at_ = base::Time::Now();
894 base::TimeDelta WallpaperManagerBase::GetWallpaperLoadDelay() const {
895 base::TimeDelta delay;
897 if (last_load_times_.size() == 0) {
898 delay = base::TimeDelta::FromMilliseconds(kLoadDefaultDelayMs);
899 } else {
900 delay = std::accumulate(last_load_times_.begin(), last_load_times_.end(),
901 base::TimeDelta(), std::plus<base::TimeDelta>()) /
902 last_load_times_.size();
905 if (delay < base::TimeDelta::FromMilliseconds(kLoadMinDelayMs))
906 delay = base::TimeDelta::FromMilliseconds(kLoadMinDelayMs);
907 else if (delay > base::TimeDelta::FromMilliseconds(kLoadMaxDelayMs))
908 delay = base::TimeDelta::FromMilliseconds(kLoadMaxDelayMs);
910 // If we had ever loaded wallpaper, adjust wait delay by time since last load.
911 if (!last_load_finished_at_.is_null()) {
912 const base::TimeDelta interval = base::Time::Now() - last_load_finished_at_;
913 if (interval > delay)
914 delay = base::TimeDelta::FromMilliseconds(0);
915 else if (interval > base::TimeDelta::FromMilliseconds(0))
916 delay -= interval;
918 return delay;
921 void WallpaperManagerBase::OnCustomizedDefaultWallpaperDecoded(
922 const GURL& wallpaper_url,
923 scoped_ptr<CustomizedWallpaperRescaledFiles> rescaled_files,
924 const user_manager::UserImage& wallpaper) {
925 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
927 // If decoded wallpaper is empty, we have probably failed to decode the file.
928 if (wallpaper.image().isNull()) {
929 LOG(WARNING) << "Failed to decode customized wallpaper.";
930 return;
933 wallpaper.image().EnsureRepsForSupportedScales();
934 scoped_ptr<gfx::ImageSkia> deep_copy(wallpaper.image().DeepCopy());
936 scoped_ptr<bool> success(new bool(false));
937 scoped_ptr<gfx::ImageSkia> small_wallpaper_image(new gfx::ImageSkia);
938 scoped_ptr<gfx::ImageSkia> large_wallpaper_image(new gfx::ImageSkia);
940 // TODO(bshe): This may break if RawImage becomes RefCountedMemory.
941 base::Closure resize_closure = base::Bind(
942 &WallpaperManagerBase::ResizeCustomizedDefaultWallpaper,
943 base::Passed(&deep_copy), wallpaper.raw_image(),
944 base::Unretained(rescaled_files.get()), base::Unretained(success.get()),
945 base::Unretained(small_wallpaper_image.get()),
946 base::Unretained(large_wallpaper_image.get()));
947 base::Closure on_resized_closure = base::Bind(
948 &WallpaperManagerBase::OnCustomizedDefaultWallpaperResized,
949 weak_factory_.GetWeakPtr(), wallpaper_url,
950 base::Passed(rescaled_files.Pass()), base::Passed(success.Pass()),
951 base::Passed(small_wallpaper_image.Pass()),
952 base::Passed(large_wallpaper_image.Pass()));
954 if (!task_runner_->PostTaskAndReply(FROM_HERE, resize_closure,
955 on_resized_closure)) {
956 LOG(WARNING) << "Failed to start Customized Wallpaper resize.";
960 void WallpaperManagerBase::ResizeCustomizedDefaultWallpaper(
961 scoped_ptr<gfx::ImageSkia> image,
962 const user_manager::UserImage::RawImage& raw_image,
963 const CustomizedWallpaperRescaledFiles* rescaled_files,
964 bool* success,
965 gfx::ImageSkia* small_wallpaper_image,
966 gfx::ImageSkia* large_wallpaper_image) {
967 *success = true;
969 *success &= ResizeAndSaveWallpaper(
970 *image, rescaled_files->path_rescaled_small(), WALLPAPER_LAYOUT_STRETCH,
971 kSmallWallpaperMaxWidth, kSmallWallpaperMaxHeight, small_wallpaper_image);
973 *success &= ResizeAndSaveWallpaper(
974 *image, rescaled_files->path_rescaled_large(), WALLPAPER_LAYOUT_STRETCH,
975 kLargeWallpaperMaxWidth, kLargeWallpaperMaxHeight, large_wallpaper_image);
978 void WallpaperManagerBase::SetCustomizedDefaultWallpaper(
979 const GURL& wallpaper_url,
980 const base::FilePath& downloaded_file,
981 const base::FilePath& resized_directory) {
982 // Should fail if this ever happens in tests.
983 DCHECK(wallpaper_url.is_valid());
984 if (!wallpaper_url.is_valid()) {
985 if (!wallpaper_url.is_empty()) {
986 LOG(WARNING) << "Invalid Customized Wallpaper URL '"
987 << wallpaper_url.spec() << "'";
989 return;
991 std::string downloaded_file_name = downloaded_file.BaseName().value();
992 scoped_ptr<CustomizedWallpaperRescaledFiles> rescaled_files(
993 new CustomizedWallpaperRescaledFiles(
994 downloaded_file, resized_directory.Append(downloaded_file_name +
995 kSmallWallpaperSuffix),
996 resized_directory.Append(downloaded_file_name +
997 kLargeWallpaperSuffix)));
999 base::Closure check_file_exists = rescaled_files->CreateCheckerClosure();
1000 base::Closure on_checked_closure =
1001 base::Bind(&WallpaperManagerBase::SetCustomizedDefaultWallpaperAfterCheck,
1002 weak_factory_.GetWeakPtr(), wallpaper_url, downloaded_file,
1003 base::Passed(rescaled_files.Pass()));
1004 if (!BrowserThread::PostBlockingPoolTaskAndReply(FROM_HERE, check_file_exists,
1005 on_checked_closure)) {
1006 LOG(WARNING) << "Failed to start check CheckCustomizedWallpaperFilesExist.";
1010 void WallpaperManagerBase::SetDefaultWallpaperPathsFromCommandLine(
1011 base::CommandLine* command_line) {
1012 default_small_wallpaper_file_ = command_line->GetSwitchValuePath(
1013 chromeos::switches::kDefaultWallpaperSmall);
1014 default_large_wallpaper_file_ = command_line->GetSwitchValuePath(
1015 chromeos::switches::kDefaultWallpaperLarge);
1016 guest_small_wallpaper_file_ = command_line->GetSwitchValuePath(
1017 chromeos::switches::kGuestWallpaperSmall);
1018 guest_large_wallpaper_file_ = command_line->GetSwitchValuePath(
1019 chromeos::switches::kGuestWallpaperLarge);
1020 default_wallpaper_image_.reset();
1023 const char*
1024 WallpaperManagerBase::GetCustomWallpaperSubdirForCurrentResolution() {
1025 WallpaperResolution resolution = GetAppropriateResolution();
1026 return resolution == WALLPAPER_RESOLUTION_SMALL ? kSmallWallpaperSubDir
1027 : kLargeWallpaperSubDir;
1030 void WallpaperManagerBase::CreateSolidDefaultWallpaper() {
1031 loaded_wallpapers_++;
1032 SkBitmap bitmap;
1033 bitmap.allocN32Pixels(1, 1);
1034 bitmap.eraseColor(kDefaultWallpaperColor);
1035 const gfx::ImageSkia image = gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
1036 default_wallpaper_image_.reset(new user_manager::UserImage(image));
1039 } // namespace wallpaper