ExtensionInstallDialogView: fix scrolling behavior on Views (Win,Linux)
[chromium-blink-merge.git] / components / wallpaper / wallpaper_manager_base.cc
blob3e0b014eca98e714f0cc948906aaf64a9e20632e
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 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();
285 // static
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;
295 // static
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);
305 // static
306 void WallpaperManagerBase::RegisterPrefs(PrefRegistrySimple* registry) {
307 registry->RegisterDictionaryPref(kUsersWallpaperInfo);
308 registry->RegisterDictionaryPref(kUserWallpapers);
309 registry->RegisterDictionaryPref(kUserWallpapersProperties);
312 void WallpaperManagerBase::EnsureLoggedInUserWallpaperLoaded() {
313 WallpaperInfo info;
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_)
318 return;
320 SetUserWallpaperNow(
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();
357 return true;
360 return GetUserWallpaperInfo(
361 user_manager::UserManager::Get()->GetLoggedInUser()->email(), info);
364 // static
365 bool WallpaperManagerBase::ResizeImage(
366 const gfx::ImageSkia& image,
367 WallpaperLayout layout,
368 int preferred_width,
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();
375 int resized_width;
376 int resized_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))
382 return false;
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) {
387 resized_width =
388 gfx::ToRoundedInt(static_cast<double>(width) * vertical_ratio);
389 resized_height = preferred_height;
390 } else {
391 resized_width = preferred_width;
392 resized_height =
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;
398 } else {
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,
413 &(*output)->data());
415 if (output_skia) {
416 resized_image.MakeThreadSafe();
417 *output_skia = resized_image;
420 return true;
423 // static
424 bool WallpaperManagerBase::ResizeAndSaveWallpaper(const gfx::ImageSkia& image,
425 const base::FilePath& path,
426 WallpaperLayout layout,
427 int preferred_width,
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);
434 return false;
436 scoped_refptr<base::RefCountedBytes> data;
437 if (ResizeImage(image, layout, preferred_width, preferred_height, &data,
438 output_skia)) {
439 return SaveWallpaperInternal(
440 path, reinterpret_cast<const char*>(data->front()), data->size());
442 return false;
445 bool WallpaperManagerBase::IsPolicyControlled(
446 const std::string& user_id) const {
447 WallpaperInfo info;
448 if (!GetUserWallpaperInfo(user_id, &info))
449 return false;
450 return info.type == user_manager::User::POLICY;
453 void WallpaperManagerBase::OnPolicySet(const std::string& policy,
454 const std::string& user_id) {
455 WallpaperInfo info;
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) {
463 WallpaperInfo info;
464 GetUserWallpaperInfo(user_id, &info);
465 info.type = user_manager::User::DEFAULT;
466 SetUserWallpaperInfo(user_id, info, true /* is_persistent */);
467 SetDefaultWallpaperNow(user_id);
470 // static
471 base::FilePath WallpaperManagerBase::GetCustomWallpaperPath(
472 const char* sub_dir,
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);
488 task_runner_ =
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);
505 if (!user) {
506 NOTREACHED() << "Unknown user.";
507 return;
510 if (user->username_hash().empty()) {
511 cryptohome::AsyncMethodCaller::GetInstance()->AsyncGetSanitizedUsername(
512 user_id,
513 base::Bind(&WallpaperManagerBase::SetCustomWallpaperOnSanitizedUsername,
514 weak_factory_.GetWeakPtr(), user_id, user_image.image(),
515 true /* update wallpaper */));
516 } else {
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)
531 return;
532 SetCustomWallpaper(user_id, user_id_hash, "policy-controlled.jpeg",
533 WALLPAPER_LAYOUT_CENTER_CROPPED,
534 user_manager::User::POLICY, image, update_wallpaper);
537 // static
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) {
543 base::DeleteFile(
544 GetCustomWallpaperDir(kOriginalWallpaperSubDir).Append(user_id_hash),
545 true /* recursive */);
546 base::DeleteFile(
547 GetCustomWallpaperDir(kSmallWallpaperSubDir).Append(user_id_hash),
548 true /* recursive */);
549 base::DeleteFile(
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,
566 NULL);
567 ResizeAndSaveWallpaper(*image, large_wallpaper_path, layout,
568 kLargeWallpaperMaxWidth, kLargeWallpaperMaxHeight,
569 NULL);
572 // static
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,
578 user_id_hash)) {
579 // Consider success if the original wallpaper is moved to the new directory.
580 // Original wallpaper is the fallback if the correct resolution wallpaper
581 // can not be found.
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,
590 user_id_hash);
593 // static
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,
614 info.location);
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())));
624 } else {
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());
654 if (clear_cache)
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);
662 return;
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;
690 return true;
692 return false;
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;
701 return true;
703 return false;
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.
718 it++;
719 for (int cached = 0; it != users.end() && cached < kMaxWallpapersToCache;
720 ++it, ++cached) {
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())
730 return;
731 WallpaperInfo info;
732 if (GetUserWallpaperInfo(user_id, &info)) {
733 if (info.location.empty())
734 return;
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,
745 gfx::ImageSkia());
746 task_runner_->PostTask(
747 FROM_HERE,
748 base::Bind(&WallpaperManagerBase::GetCustomWallpaperInternal, user_id,
749 info, wallpaper_path, false /* do not update wallpaper */,
750 base::Passed(MovableOnDestroyCallbackHolder()),
751 weak_factory_.GetWeakPtr()));
752 return;
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();
802 return command_line;
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;
819 } else {
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.
826 return;
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
835 // resolution.
836 if (info.layout != WALLPAPER_LAYOUT_STRETCH &&
837 resolution == WALLPAPER_RESOLUTION_SMALL) {
838 file_name = base::FilePath(file_name)
839 .InsertBeforeExtension(kSmallWallpaperSuffix)
840 .value();
842 DCHECK(dir_chromeos_wallpapers_path_id != -1);
843 CHECK(PathService::Get(dir_chromeos_wallpapers_path_id,
844 &wallpaper_dir));
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())
853 return;
855 loaded_wallpapers_for_test_++;
856 StartLoad(user_id, info, update_wallpaper, wallpaper_path,
857 on_finish.Pass());
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,
868 on_finish.Pass());
869 } else {
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) {
880 WallpaperInfo info;
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();
886 bool is_persistent =
887 !user_manager::UserManager::Get()->IsUserNonCryptohomeDataEphemeral(
888 user_id);
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(
898 FROM_HERE,
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);
920 } else {
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))
937 delay -= interval;
939 return delay;
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.";
951 return;
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,
985 bool* success,
986 gfx::ImageSkia* small_wallpaper_image,
987 gfx::ImageSkia* large_wallpaper_image) {
988 *success = true;
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() << "'";
1010 return;
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();
1048 const char*
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_++;
1057 SkBitmap bitmap;
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