// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "components/wallpaper/wallpaper_manager_base.h" #include #include #include "base/bind.h" #include "base/command_line.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/logging.h" #include "base/metrics/histogram.h" #include "base/path_service.h" #include "base/prefs/pref_registry_simple.h" #include "base/prefs/pref_service.h" #include "base/prefs/scoped_user_pref_update.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/sys_info.h" #include "base/threading/worker_pool.h" #include "base/time/time.h" #include "base/values.h" #include "chromeos/chromeos_switches.h" #include "chromeos/cryptohome/async_method_caller.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/login/user_names.h" #include "components/user_manager/user.h" #include "components/user_manager/user_image/user_image.h" #include "components/user_manager/user_manager.h" #include "components/user_manager/user_type.h" #include "components/wallpaper/wallpaper_layout.h" #include "content/public/browser/browser_thread.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/gfx/codec/jpeg_codec.h" #include "ui/gfx/geometry/safe_integer_conversions.h" #include "ui/gfx/image/image_skia_operations.h" #include "ui/gfx/skia_util.h" using content::BrowserThread; namespace wallpaper { namespace { // Default quality for encoding wallpaper. const int kDefaultEncodingQuality = 90; // Maximum number of wallpapers cached by CacheUsersWallpapers(). const int kMaxWallpapersToCache = 3; // Maximum number of entries in WallpaperManagerBase::last_load_times_ . const size_t kLastLoadsStatsMsMaxSize = 4; // Minimum delay between wallpaper loads, milliseconds. const unsigned kLoadMinDelayMs = 50; // Default wallpaper load delay, milliseconds. const unsigned kLoadDefaultDelayMs = 200; // Maximum wallpaper load delay, milliseconds. const unsigned kLoadMaxDelayMs = 2000; // When no wallpaper image is specified, the screen is filled with a solid // color. const SkColor kDefaultWallpaperColor = SK_ColorGRAY; // The path ids for directories. int dir_user_data_path_id = -1; // chrome::DIR_USER_DATA int dir_chromeos_wallpapers_path_id = -1; // chrome::DIR_CHROMEOS_WALLPAPERS int dir_chromeos_custom_wallpapers_path_id = -1; // chrome::DIR_CHROMEOS_CUSTOM_WALLPAPERS bool MoveCustomWallpaperDirectory(const char* sub_dir, const std::string& user_id, const std::string& user_id_hash) { base::FilePath base_path = WallpaperManagerBase::GetCustomWallpaperDir(sub_dir); base::FilePath to_path = base_path.Append(user_id_hash); base::FilePath from_path = base_path.Append(user_id); if (base::PathExists(from_path)) return base::Move(from_path, to_path); return false; } // Deletes a list of wallpaper files in |file_list|. void DeleteWallpaperInList(const std::vector& file_list) { for (std::vector::const_iterator it = file_list.begin(); it != file_list.end(); ++it) { base::FilePath path = *it; // Some users may still have legacy wallpapers with png extension. We need // to delete these wallpapers too. if (!base::DeleteFile(path, true) && !base::DeleteFile(path.AddExtension(".png"), false)) { LOG(ERROR) << "Failed to remove user wallpaper at " << path.value(); } } } // Creates all new custom wallpaper directories for |user_id_hash| if not exist. // static void EnsureCustomWallpaperDirectories(const std::string& user_id_hash) { base::FilePath dir; dir = WallpaperManagerBase::GetCustomWallpaperDir(kSmallWallpaperSubDir); dir = dir.Append(user_id_hash); if (!base::PathExists(dir)) base::CreateDirectory(dir); dir = WallpaperManagerBase::GetCustomWallpaperDir(kLargeWallpaperSubDir); dir = dir.Append(user_id_hash); if (!base::PathExists(dir)) base::CreateDirectory(dir); dir = WallpaperManagerBase::GetCustomWallpaperDir(kOriginalWallpaperSubDir); dir = dir.Append(user_id_hash); if (!base::PathExists(dir)) base::CreateDirectory(dir); dir = WallpaperManagerBase::GetCustomWallpaperDir(kThumbnailWallpaperSubDir); dir = dir.Append(user_id_hash); if (!base::PathExists(dir)) base::CreateDirectory(dir); } // Saves wallpaper image raw |data| to |path| (absolute path) in file system. // Returns true on success. bool SaveWallpaperInternal(const base::FilePath& path, const char* data, int size) { int written_bytes = base::WriteFile(path, data, size); return written_bytes == size; } } // namespace MovableOnDestroyCallback::MovableOnDestroyCallback( const base::Closure& callback) : callback_(callback) { } MovableOnDestroyCallback::~MovableOnDestroyCallback() { if (!callback_.is_null()) callback_.Run(); } WallpaperInfo::WallpaperInfo() : layout(WALLPAPER_LAYOUT_CENTER), type(user_manager::User::WALLPAPER_TYPE_COUNT) { } WallpaperInfo::WallpaperInfo(const std::string& in_location, WallpaperLayout in_layout, user_manager::User::WallpaperType in_type, const base::Time& in_date) : location(in_location), layout(in_layout), type(in_type), date(in_date) { } WallpaperInfo::~WallpaperInfo() { } const char kWallpaperSequenceTokenName[] = "wallpaper-sequence"; const char kSmallWallpaperSuffix[] = "_small"; const char kLargeWallpaperSuffix[] = "_large"; const char kSmallWallpaperSubDir[] = "small"; const char kLargeWallpaperSubDir[] = "large"; const char kOriginalWallpaperSubDir[] = "original"; const char kThumbnailWallpaperSubDir[] = "thumb"; const int kSmallWallpaperMaxWidth = 1366; const int kSmallWallpaperMaxHeight = 800; const int kLargeWallpaperMaxWidth = 2560; const int kLargeWallpaperMaxHeight = 1700; const int kWallpaperThumbnailWidth = 108; const int kWallpaperThumbnailHeight = 68; const char kUsersWallpaperInfo[] = "user_wallpaper_info"; const char kUserWallpapers[] = "UserWallpapers"; const char kUserWallpapersProperties[] = "UserWallpapersProperties"; const base::FilePath& WallpaperManagerBase::CustomizedWallpaperRescaledFiles::path_downloaded() const { return path_downloaded_; } const base::FilePath& WallpaperManagerBase::CustomizedWallpaperRescaledFiles::path_rescaled_small() const { return path_rescaled_small_; } const base::FilePath& WallpaperManagerBase::CustomizedWallpaperRescaledFiles::path_rescaled_large() const { return path_rescaled_large_; } bool WallpaperManagerBase::CustomizedWallpaperRescaledFiles::downloaded_exists() const { return downloaded_exists_; } bool WallpaperManagerBase::CustomizedWallpaperRescaledFiles:: rescaled_small_exists() const { return rescaled_small_exists_; } bool WallpaperManagerBase::CustomizedWallpaperRescaledFiles:: rescaled_large_exists() const { return rescaled_large_exists_; } WallpaperManagerBase::CustomizedWallpaperRescaledFiles:: CustomizedWallpaperRescaledFiles(const base::FilePath& path_downloaded, const base::FilePath& path_rescaled_small, const base::FilePath& path_rescaled_large) : path_downloaded_(path_downloaded), path_rescaled_small_(path_rescaled_small), path_rescaled_large_(path_rescaled_large), downloaded_exists_(false), rescaled_small_exists_(false), rescaled_large_exists_(false) { } base::Closure WallpaperManagerBase::CustomizedWallpaperRescaledFiles::CreateCheckerClosure() { return base::Bind(&WallpaperManagerBase::CustomizedWallpaperRescaledFiles:: CheckCustomizedWallpaperFilesExist, base::Unretained(this)); } void WallpaperManagerBase::CustomizedWallpaperRescaledFiles:: CheckCustomizedWallpaperFilesExist() { DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); downloaded_exists_ = base::PathExists(path_downloaded_); rescaled_small_exists_ = base::PathExists(path_rescaled_small_); rescaled_large_exists_ = base::PathExists(path_rescaled_large_); } bool WallpaperManagerBase::CustomizedWallpaperRescaledFiles::AllSizesExist() const { return rescaled_small_exists_ && rescaled_large_exists_; } // WallpaperManagerBase, public: // TestApi. For testing purpose WallpaperManagerBase::TestApi::TestApi(WallpaperManagerBase* wallpaper_manager) : wallpaper_manager_(wallpaper_manager) { } WallpaperManagerBase::TestApi::~TestApi() { } bool WallpaperManagerBase::TestApi::GetWallpaperFromCache( const std::string& user_id, gfx::ImageSkia* image) { return wallpaper_manager_->GetWallpaperFromCache(user_id, image); } bool WallpaperManagerBase::TestApi::GetPathFromCache( const std::string& user_id, base::FilePath* path) { return wallpaper_manager_->GetPathFromCache(user_id, path); } void WallpaperManagerBase::TestApi::SetWallpaperCache( const std::string& user_id, const base::FilePath& path, const gfx::ImageSkia& image) { DCHECK(!image.isNull()); wallpaper_manager_->wallpaper_cache_[user_id] = CustomWallpaperElement(path, image); } void WallpaperManagerBase::TestApi::ClearDisposableWallpaperCache() { wallpaper_manager_->ClearDisposableWallpaperCache(); } // static void WallpaperManagerBase::SetPathIds( int dir_user_data_enum, int dir_chromeos_wallpapers_enum, int dir_chromeos_custom_wallpapers_enum) { dir_user_data_path_id = dir_user_data_enum; dir_chromeos_wallpapers_path_id = dir_chromeos_wallpapers_enum; dir_chromeos_custom_wallpapers_path_id = dir_chromeos_custom_wallpapers_enum; } // static base::FilePath WallpaperManagerBase::GetCustomWallpaperDir( const char* sub_dir) { base::FilePath custom_wallpaper_dir; DCHECK(dir_chromeos_custom_wallpapers_path_id != -1); CHECK(PathService::Get(dir_chromeos_custom_wallpapers_path_id, &custom_wallpaper_dir)); return custom_wallpaper_dir.Append(sub_dir); } // static void WallpaperManagerBase::RegisterPrefs(PrefRegistrySimple* registry) { registry->RegisterDictionaryPref(kUsersWallpaperInfo); registry->RegisterDictionaryPref(kUserWallpapers); registry->RegisterDictionaryPref(kUserWallpapersProperties); } void WallpaperManagerBase::EnsureLoggedInUserWallpaperLoaded() { WallpaperInfo info; if (GetLoggedInUserWallpaperInfo(&info)) { UMA_HISTOGRAM_ENUMERATION("Ash.Wallpaper.Type", info.type, user_manager::User::WALLPAPER_TYPE_COUNT); if (info == current_user_wallpaper_info_) return; } SetUserWallpaperNow( user_manager::UserManager::Get()->GetLoggedInUser()->email()); } void WallpaperManagerBase::ClearDisposableWallpaperCache() { // Cancel callback for previous cache requests. weak_factory_.InvalidateWeakPtrs(); // Keep the wallpaper of logged in users in cache at multi-profile mode. std::set logged_in_users_names; const user_manager::UserList& logged_users = user_manager::UserManager::Get()->GetLoggedInUsers(); for (user_manager::UserList::const_iterator it = logged_users.begin(); it != logged_users.end(); ++it) { logged_in_users_names.insert((*it)->email()); } CustomWallpaperMap logged_in_users_cache; for (CustomWallpaperMap::iterator it = wallpaper_cache_.begin(); it != wallpaper_cache_.end(); ++it) { if (logged_in_users_names.find(it->first) != logged_in_users_names.end()) { logged_in_users_cache.insert(*it); } } wallpaper_cache_ = logged_in_users_cache; } bool WallpaperManagerBase::GetLoggedInUserWallpaperInfo(WallpaperInfo* info) { DCHECK_CURRENTLY_ON(BrowserThread::UI); if (user_manager::UserManager::Get()->IsLoggedInAsStub()) { info->location = current_user_wallpaper_info_.location = ""; info->layout = current_user_wallpaper_info_.layout = WALLPAPER_LAYOUT_CENTER_CROPPED; info->type = current_user_wallpaper_info_.type = user_manager::User::DEFAULT; info->date = current_user_wallpaper_info_.date = base::Time::Now().LocalMidnight(); return true; } return GetUserWallpaperInfo( user_manager::UserManager::Get()->GetLoggedInUser()->email(), info); } // static bool WallpaperManagerBase::ResizeImage( const gfx::ImageSkia& image, WallpaperLayout layout, int preferred_width, int preferred_height, scoped_refptr* output, gfx::ImageSkia* output_skia) { DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); int width = image.width(); int height = image.height(); int resized_width; int resized_height; *output = new base::RefCountedBytes(); if (layout == WALLPAPER_LAYOUT_CENTER_CROPPED) { // Do not resize custom wallpaper if it is smaller than preferred size. if (!(width > preferred_width && height > preferred_height)) return false; double horizontal_ratio = static_cast(preferred_width) / width; double vertical_ratio = static_cast(preferred_height) / height; if (vertical_ratio > horizontal_ratio) { resized_width = gfx::ToRoundedInt(static_cast(width) * vertical_ratio); resized_height = preferred_height; } else { resized_width = preferred_width; resized_height = gfx::ToRoundedInt(static_cast(height) * horizontal_ratio); } } else if (layout == WALLPAPER_LAYOUT_STRETCH) { resized_width = preferred_width; resized_height = preferred_height; } else { resized_width = width; resized_height = height; } gfx::ImageSkia resized_image = gfx::ImageSkiaOperations::CreateResizedImage( image, skia::ImageOperations::RESIZE_LANCZOS3, gfx::Size(resized_width, resized_height)); SkBitmap bitmap = *(resized_image.bitmap()); SkAutoLockPixels lock_input(bitmap); gfx::JPEGCodec::Encode( reinterpret_cast(bitmap.getAddr32(0, 0)), gfx::JPEGCodec::FORMAT_SkBitmap, bitmap.width(), bitmap.height(), bitmap.width() * bitmap.bytesPerPixel(), kDefaultEncodingQuality, &(*output)->data()); if (output_skia) { resized_image.MakeThreadSafe(); *output_skia = resized_image; } return true; } // static bool WallpaperManagerBase::ResizeAndSaveWallpaper(const gfx::ImageSkia& image, const base::FilePath& path, WallpaperLayout layout, int preferred_width, int preferred_height, gfx::ImageSkia* output_skia) { if (layout == WALLPAPER_LAYOUT_CENTER) { // TODO(bshe): Generates cropped custom wallpaper for CENTER layout. if (base::PathExists(path)) base::DeleteFile(path, false); return false; } scoped_refptr data; if (ResizeImage(image, layout, preferred_width, preferred_height, &data, output_skia)) { return SaveWallpaperInternal( path, reinterpret_cast(data->front()), data->size()); } return false; } bool WallpaperManagerBase::IsPolicyControlled( const std::string& user_id) const { WallpaperInfo info; if (!GetUserWallpaperInfo(user_id, &info)) return false; return info.type == user_manager::User::POLICY; } void WallpaperManagerBase::OnPolicySet(const std::string& policy, const std::string& user_id) { WallpaperInfo info; GetUserWallpaperInfo(user_id, &info); info.type = user_manager::User::POLICY; SetUserWallpaperInfo(user_id, info, true /* is_persistent */); } void WallpaperManagerBase::OnPolicyCleared(const std::string& policy, const std::string& user_id) { WallpaperInfo info; GetUserWallpaperInfo(user_id, &info); info.type = user_manager::User::DEFAULT; SetUserWallpaperInfo(user_id, info, true /* is_persistent */); SetDefaultWallpaperNow(user_id); } // static base::FilePath WallpaperManagerBase::GetCustomWallpaperPath( const char* sub_dir, const std::string& user_id_hash, const std::string& file) { base::FilePath custom_wallpaper_path = GetCustomWallpaperDir(sub_dir); return custom_wallpaper_path.Append(user_id_hash).Append(file); } WallpaperManagerBase::WallpaperManagerBase() : loaded_wallpapers_for_test_(0), command_line_for_testing_(NULL), should_cache_wallpaper_(false), weak_factory_(this) { SetDefaultWallpaperPathsFromCommandLine( base::CommandLine::ForCurrentProcess()); sequence_token_ = BrowserThread::GetBlockingPool()->GetNamedSequenceToken( kWallpaperSequenceTokenName); task_runner_ = BrowserThread::GetBlockingPool() ->GetSequencedTaskRunnerWithShutdownBehavior( sequence_token_, base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); } WallpaperManagerBase::~WallpaperManagerBase() { // TODO(bshe): Lifetime of WallpaperManagerBase needs more consideration. // http://crbug.com/171694 weak_factory_.InvalidateWeakPtrs(); } void WallpaperManagerBase::SetPolicyControlledWallpaper( const std::string& user_id, const user_manager::UserImage& user_image) { const user_manager::User* user = user_manager::UserManager::Get()->FindUser(user_id); if (!user) { NOTREACHED() << "Unknown user."; return; } if (user->username_hash().empty()) { cryptohome::AsyncMethodCaller::GetInstance()->AsyncGetSanitizedUsername( user_id, base::Bind(&WallpaperManagerBase::SetCustomWallpaperOnSanitizedUsername, weak_factory_.GetWeakPtr(), user_id, user_image.image(), true /* update wallpaper */)); } else { SetCustomWallpaper(user_id, user->username_hash(), "policy-controlled.jpeg", WALLPAPER_LAYOUT_CENTER_CROPPED, user_manager::User::POLICY, user_image.image(), true /* update wallpaper */); } } void WallpaperManagerBase::SetCustomWallpaperOnSanitizedUsername( const std::string& user_id, const gfx::ImageSkia& image, bool update_wallpaper, bool cryptohome_success, const std::string& user_id_hash) { if (!cryptohome_success) return; SetCustomWallpaper(user_id, user_id_hash, "policy-controlled.jpeg", WALLPAPER_LAYOUT_CENTER_CROPPED, user_manager::User::POLICY, image, update_wallpaper); } // static void WallpaperManagerBase::SaveCustomWallpaper( const std::string& user_id_hash, const base::FilePath& original_path, WallpaperLayout layout, scoped_ptr image) { base::DeleteFile( GetCustomWallpaperDir(kOriginalWallpaperSubDir).Append(user_id_hash), true /* recursive */); base::DeleteFile( GetCustomWallpaperDir(kSmallWallpaperSubDir).Append(user_id_hash), true /* recursive */); base::DeleteFile( GetCustomWallpaperDir(kLargeWallpaperSubDir).Append(user_id_hash), true /* recursive */); EnsureCustomWallpaperDirectories(user_id_hash); std::string file_name = original_path.BaseName().value(); base::FilePath small_wallpaper_path = GetCustomWallpaperPath(kSmallWallpaperSubDir, user_id_hash, file_name); base::FilePath large_wallpaper_path = GetCustomWallpaperPath(kLargeWallpaperSubDir, user_id_hash, file_name); // Re-encode orginal file to jpeg format and saves the result in case that // resized wallpaper is not generated (i.e. chrome shutdown before resized // wallpaper is saved). ResizeAndSaveWallpaper(*image, original_path, WALLPAPER_LAYOUT_STRETCH, image->width(), image->height(), NULL); ResizeAndSaveWallpaper(*image, small_wallpaper_path, layout, kSmallWallpaperMaxWidth, kSmallWallpaperMaxHeight, NULL); ResizeAndSaveWallpaper(*image, large_wallpaper_path, layout, kLargeWallpaperMaxWidth, kLargeWallpaperMaxHeight, NULL); } // static void WallpaperManagerBase::MoveCustomWallpapersOnWorker( const std::string& user_id, const std::string& user_id_hash, base::WeakPtr weak_ptr) { if (MoveCustomWallpaperDirectory(kOriginalWallpaperSubDir, user_id, user_id_hash)) { // Consider success if the original wallpaper is moved to the new directory. // Original wallpaper is the fallback if the correct resolution wallpaper // can not be found. BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&WallpaperManagerBase::MoveCustomWallpapersSuccess, weak_ptr, user_id, user_id_hash)); } MoveCustomWallpaperDirectory(kLargeWallpaperSubDir, user_id, user_id_hash); MoveCustomWallpaperDirectory(kSmallWallpaperSubDir, user_id, user_id_hash); MoveCustomWallpaperDirectory(kThumbnailWallpaperSubDir, user_id, user_id_hash); } // static void WallpaperManagerBase::GetCustomWallpaperInternal( const std::string& user_id, const WallpaperInfo& info, const base::FilePath& wallpaper_path, bool update_wallpaper, MovableOnDestroyCallbackHolder on_finish, base::WeakPtr weak_ptr) { base::FilePath valid_path = wallpaper_path; if (!base::PathExists(wallpaper_path)) { // Falls back on original file if the correct resolution file does not // exist. This may happen when the original custom wallpaper is small or // browser shutdown before resized wallpaper saved. valid_path = GetCustomWallpaperDir(kOriginalWallpaperSubDir); valid_path = valid_path.Append(info.location); } if (!base::PathExists(valid_path)) { // Falls back to custom wallpaper that uses email as part of its file path. // Note that email is used instead of user_id_hash here. valid_path = GetCustomWallpaperPath(kOriginalWallpaperSubDir, user_id, info.location); } if (!base::PathExists(valid_path)) { LOG(ERROR) << "Failed to load previously selected custom wallpaper. " << "Fallback to default wallpaper"; BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&WallpaperManagerBase::DoSetDefaultWallpaper, weak_ptr, user_id, base::Passed(on_finish.Pass()))); } else { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&WallpaperManagerBase::StartLoad, weak_ptr, user_id, info, update_wallpaper, valid_path, base::Passed(on_finish.Pass()))); } } void WallpaperManagerBase::InitInitialUserWallpaper(const std::string& user_id, bool is_persistent) { current_user_wallpaper_info_.location = ""; current_user_wallpaper_info_.layout = WALLPAPER_LAYOUT_CENTER_CROPPED; current_user_wallpaper_info_.type = user_manager::User::DEFAULT; current_user_wallpaper_info_.date = base::Time::Now().LocalMidnight(); WallpaperInfo info = current_user_wallpaper_info_; SetUserWallpaperInfo(user_id, info, is_persistent); } void WallpaperManagerBase::SetUserWallpaperDelayed(const std::string& user_id) { ScheduleSetUserWallpaper(user_id, true); } void WallpaperManagerBase::SetUserWallpaperNow(const std::string& user_id) { ScheduleSetUserWallpaper(user_id, false); } void WallpaperManagerBase::UpdateWallpaper(bool clear_cache) { FOR_EACH_OBSERVER(Observer, observers_, OnUpdateWallpaperForTesting()); if (clear_cache) wallpaper_cache_.clear(); // For GAIA login flow, the last_selected_user_ may not be set before user // login. If UpdateWallpaper is called at GAIA login screen, no wallpaper will // be set. It could result a black screen on external monitors. // See http://crbug.com/265689 for detail. if (last_selected_user_.empty()) { SetDefaultWallpaperNow(chromeos::login::kSignInUser); return; } SetUserWallpaperNow(last_selected_user_); } void WallpaperManagerBase::AddObserver( WallpaperManagerBase::Observer* observer) { observers_.AddObserver(observer); } void WallpaperManagerBase::RemoveObserver( WallpaperManagerBase::Observer* observer) { observers_.RemoveObserver(observer); } void WallpaperManagerBase::NotifyAnimationFinished() { FOR_EACH_OBSERVER(Observer, observers_, OnWallpaperAnimationFinished(last_selected_user_)); } // WallpaperManager, protected: ----------------------------------------------- bool WallpaperManagerBase::GetWallpaperFromCache(const std::string& user_id, gfx::ImageSkia* image) { DCHECK_CURRENTLY_ON(BrowserThread::UI); CustomWallpaperMap::const_iterator it = wallpaper_cache_.find(user_id); if (it != wallpaper_cache_.end() && !(*it).second.second.isNull()) { *image = (*it).second.second; return true; } return false; } bool WallpaperManagerBase::GetPathFromCache(const std::string& user_id, base::FilePath* path) { DCHECK_CURRENTLY_ON(BrowserThread::UI); CustomWallpaperMap::const_iterator it = wallpaper_cache_.find(user_id); if (it != wallpaper_cache_.end()) { *path = (*it).second.first; return true; } return false; } int WallpaperManagerBase::loaded_wallpapers_for_test() const { return loaded_wallpapers_for_test_; } void WallpaperManagerBase::CacheUsersWallpapers() { // TODO(dpolukhin): crbug.com/408734. DCHECK_CURRENTLY_ON(BrowserThread::UI); user_manager::UserList users = user_manager::UserManager::Get()->GetUsers(); if (!users.empty()) { user_manager::UserList::const_iterator it = users.begin(); // Skip the wallpaper of first user in the list. It should have been cached. it++; for (int cached = 0; it != users.end() && cached < kMaxWallpapersToCache; ++it, ++cached) { std::string user_id = (*it)->email(); CacheUserWallpaper(user_id); } } } void WallpaperManagerBase::CacheUserWallpaper(const std::string& user_id) { CustomWallpaperMap::iterator it = wallpaper_cache_.find(user_id); if (it != wallpaper_cache_.end() && !it->second.second.isNull()) return; WallpaperInfo info; if (GetUserWallpaperInfo(user_id, &info)) { if (info.location.empty()) return; base::FilePath wallpaper_dir; base::FilePath wallpaper_path; if (info.type == user_manager::User::CUSTOMIZED || info.type == user_manager::User::POLICY) { const char* sub_dir = GetCustomWallpaperSubdirForCurrentResolution(); base::FilePath wallpaper_path = GetCustomWallpaperDir(sub_dir); wallpaper_path = wallpaper_path.Append(info.location); // Set the path to the cache. wallpaper_cache_[user_id] = CustomWallpaperElement(wallpaper_path, gfx::ImageSkia()); task_runner_->PostTask( FROM_HERE, base::Bind(&WallpaperManagerBase::GetCustomWallpaperInternal, user_id, info, wallpaper_path, false /* do not update wallpaper */, base::Passed(MovableOnDestroyCallbackHolder()), weak_factory_.GetWeakPtr())); return; } LoadWallpaper(user_id, info, false /* do not update wallpaper */, MovableOnDestroyCallbackHolder().Pass()); } } void WallpaperManagerBase::DeleteUserWallpapers( const std::string& user_id, const std::string& path_to_file) { std::vector file_to_remove; // Remove small user wallpaper. base::FilePath wallpaper_path = GetCustomWallpaperDir(kSmallWallpaperSubDir); // Remove old directory if exists file_to_remove.push_back(wallpaper_path.Append(user_id)); wallpaper_path = wallpaper_path.Append(path_to_file).DirName(); file_to_remove.push_back(wallpaper_path); // Remove large user wallpaper. wallpaper_path = GetCustomWallpaperDir(kLargeWallpaperSubDir); file_to_remove.push_back(wallpaper_path.Append(user_id)); wallpaper_path = wallpaper_path.Append(path_to_file); file_to_remove.push_back(wallpaper_path); // Remove user wallpaper thumbnail. wallpaper_path = GetCustomWallpaperDir(kThumbnailWallpaperSubDir); file_to_remove.push_back(wallpaper_path.Append(user_id)); wallpaper_path = wallpaper_path.Append(path_to_file); file_to_remove.push_back(wallpaper_path); // Remove original user wallpaper. wallpaper_path = GetCustomWallpaperDir(kOriginalWallpaperSubDir); file_to_remove.push_back(wallpaper_path.Append(user_id)); wallpaper_path = wallpaper_path.Append(path_to_file); file_to_remove.push_back(wallpaper_path); base::WorkerPool::PostTask( FROM_HERE, base::Bind(&DeleteWallpaperInList, file_to_remove), false); } void WallpaperManagerBase::SetCommandLineForTesting( base::CommandLine* command_line) { command_line_for_testing_ = command_line; SetDefaultWallpaperPathsFromCommandLine(command_line); } base::CommandLine* WallpaperManagerBase::GetCommandLine() { base::CommandLine* command_line = command_line_for_testing_ ? command_line_for_testing_ : base::CommandLine::ForCurrentProcess(); return command_line; } void WallpaperManagerBase::LoadWallpaper( const std::string& user_id, const WallpaperInfo& info, bool update_wallpaper, MovableOnDestroyCallbackHolder on_finish) { base::FilePath wallpaper_dir; base::FilePath wallpaper_path; // Do a sanity check that file path information is not empty. if (info.type == user_manager::User::ONLINE || info.type == user_manager::User::DEFAULT) { if (info.location.empty()) { if (base::SysInfo::IsRunningOnChromeOS()) { NOTREACHED() << "User wallpaper info appears to be broken: " << user_id; } else { // Filename might be empty on debug configurations when stub users // were created directly in Local State (for testing). Ignore such // errors i.e. allowsuch type of debug configurations on the desktop. LOG(WARNING) << "User wallpaper info is empty: " << user_id; // |on_finish| callback will get called on destruction. return; } } } if (info.type == user_manager::User::ONLINE) { std::string file_name = GURL(info.location).ExtractFileName(); WallpaperResolution resolution = GetAppropriateResolution(); // Only solid color wallpapers have stretch layout and they have only one // resolution. if (info.layout != WALLPAPER_LAYOUT_STRETCH && resolution == WALLPAPER_RESOLUTION_SMALL) { file_name = base::FilePath(file_name) .InsertBeforeExtension(kSmallWallpaperSuffix) .value(); } DCHECK(dir_chromeos_wallpapers_path_id != -1); CHECK(PathService::Get(dir_chromeos_wallpapers_path_id, &wallpaper_dir)); wallpaper_path = wallpaper_dir.Append(file_name); // If the wallpaper exists and it contains already the correct image we can // return immediately. CustomWallpaperMap::iterator it = wallpaper_cache_.find(user_id); if (it != wallpaper_cache_.end() && it->second.first == wallpaper_path && !it->second.second.isNull()) return; loaded_wallpapers_for_test_++; StartLoad(user_id, info, update_wallpaper, wallpaper_path, on_finish.Pass()); } else if (info.type == user_manager::User::DEFAULT) { // Default wallpapers are migrated from M21 user profiles. A code refactor // overlooked that case and caused these wallpapers not being loaded at all. // On some slow devices, it caused login webui not visible after upgrade to // M26 from M21. See crosbug.com/38429 for details. base::FilePath user_data_dir; DCHECK(dir_user_data_path_id != -1); PathService::Get(dir_user_data_path_id, &user_data_dir); wallpaper_path = user_data_dir.Append(info.location); StartLoad(user_id, info, update_wallpaper, wallpaper_path, on_finish.Pass()); } else { // In unexpected cases, revert to default wallpaper to fail safely. See // crosbug.com/38429. LOG(ERROR) << "Wallpaper reverts to default unexpected."; DoSetDefaultWallpaper(user_id, on_finish.Pass()); } } void WallpaperManagerBase::MoveCustomWallpapersSuccess( const std::string& user_id, const std::string& user_id_hash) { WallpaperInfo info; GetUserWallpaperInfo(user_id, &info); if (info.type == user_manager::User::CUSTOMIZED) { // New file field should include user id hash in addition to file name. // This is needed because at login screen, user id hash is not available. info.location = base::FilePath(user_id_hash).Append(info.location).value(); bool is_persistent = !user_manager::UserManager::Get()->IsUserNonCryptohomeDataEphemeral( user_id); SetUserWallpaperInfo(user_id, info, is_persistent); } } void WallpaperManagerBase::MoveLoggedInUserCustomWallpaper() { const user_manager::User* logged_in_user = user_manager::UserManager::Get()->GetLoggedInUser(); if (logged_in_user) { task_runner_->PostTask( FROM_HERE, base::Bind(&WallpaperManagerBase::MoveCustomWallpapersOnWorker, logged_in_user->email(), logged_in_user->username_hash(), weak_factory_.GetWeakPtr())); } } void WallpaperManagerBase::SaveLastLoadTime(const base::TimeDelta elapsed) { while (last_load_times_.size() >= kLastLoadsStatsMsMaxSize) last_load_times_.pop_front(); if (elapsed > base::TimeDelta::FromMicroseconds(0)) { last_load_times_.push_back(elapsed); last_load_finished_at_ = base::Time::Now(); } } base::TimeDelta WallpaperManagerBase::GetWallpaperLoadDelay() const { base::TimeDelta delay; if (last_load_times_.size() == 0) { delay = base::TimeDelta::FromMilliseconds(kLoadDefaultDelayMs); } else { delay = std::accumulate(last_load_times_.begin(), last_load_times_.end(), base::TimeDelta(), std::plus()) / last_load_times_.size(); } if (delay < base::TimeDelta::FromMilliseconds(kLoadMinDelayMs)) delay = base::TimeDelta::FromMilliseconds(kLoadMinDelayMs); else if (delay > base::TimeDelta::FromMilliseconds(kLoadMaxDelayMs)) delay = base::TimeDelta::FromMilliseconds(kLoadMaxDelayMs); // If we had ever loaded wallpaper, adjust wait delay by time since last load. if (!last_load_finished_at_.is_null()) { const base::TimeDelta interval = base::Time::Now() - last_load_finished_at_; if (interval > delay) delay = base::TimeDelta::FromMilliseconds(0); else if (interval > base::TimeDelta::FromMilliseconds(0)) delay -= interval; } return delay; } void WallpaperManagerBase::OnCustomizedDefaultWallpaperDecoded( const GURL& wallpaper_url, scoped_ptr rescaled_files, const user_manager::UserImage& wallpaper) { DCHECK_CURRENTLY_ON(BrowserThread::UI); // If decoded wallpaper is empty, we have probably failed to decode the file. if (wallpaper.image().isNull()) { LOG(WARNING) << "Failed to decode customized wallpaper."; return; } wallpaper.image().EnsureRepsForSupportedScales(); scoped_ptr deep_copy(wallpaper.image().DeepCopy()); scoped_ptr success(new bool(false)); scoped_ptr small_wallpaper_image(new gfx::ImageSkia); scoped_ptr large_wallpaper_image(new gfx::ImageSkia); // TODO(bshe): This may break if RawImage becomes RefCountedMemory. base::Closure resize_closure = base::Bind( &WallpaperManagerBase::ResizeCustomizedDefaultWallpaper, base::Passed(&deep_copy), wallpaper.raw_image(), base::Unretained(rescaled_files.get()), base::Unretained(success.get()), base::Unretained(small_wallpaper_image.get()), base::Unretained(large_wallpaper_image.get())); base::Closure on_resized_closure = base::Bind( &WallpaperManagerBase::OnCustomizedDefaultWallpaperResized, weak_factory_.GetWeakPtr(), wallpaper_url, base::Passed(rescaled_files.Pass()), base::Passed(success.Pass()), base::Passed(small_wallpaper_image.Pass()), base::Passed(large_wallpaper_image.Pass())); if (!task_runner_->PostTaskAndReply(FROM_HERE, resize_closure, on_resized_closure)) { LOG(WARNING) << "Failed to start Customized Wallpaper resize."; } } void WallpaperManagerBase::ResizeCustomizedDefaultWallpaper( scoped_ptr image, const user_manager::UserImage::RawImage& raw_image, const CustomizedWallpaperRescaledFiles* rescaled_files, bool* success, gfx::ImageSkia* small_wallpaper_image, gfx::ImageSkia* large_wallpaper_image) { *success = true; *success &= ResizeAndSaveWallpaper( *image, rescaled_files->path_rescaled_small(), WALLPAPER_LAYOUT_STRETCH, kSmallWallpaperMaxWidth, kSmallWallpaperMaxHeight, small_wallpaper_image); *success &= ResizeAndSaveWallpaper( *image, rescaled_files->path_rescaled_large(), WALLPAPER_LAYOUT_STRETCH, kLargeWallpaperMaxWidth, kLargeWallpaperMaxHeight, large_wallpaper_image); } void WallpaperManagerBase::SetCustomizedDefaultWallpaper( const GURL& wallpaper_url, const base::FilePath& downloaded_file, const base::FilePath& resized_directory) { // Should fail if this ever happens in tests. DCHECK(wallpaper_url.is_valid()); if (!wallpaper_url.is_valid()) { if (!wallpaper_url.is_empty()) { LOG(WARNING) << "Invalid Customized Wallpaper URL '" << wallpaper_url.spec() << "'"; } return; } std::string downloaded_file_name = downloaded_file.BaseName().value(); scoped_ptr rescaled_files( new CustomizedWallpaperRescaledFiles( downloaded_file, resized_directory.Append(downloaded_file_name + kSmallWallpaperSuffix), resized_directory.Append(downloaded_file_name + kLargeWallpaperSuffix))); base::Closure check_file_exists = rescaled_files->CreateCheckerClosure(); base::Closure on_checked_closure = base::Bind(&WallpaperManagerBase::SetCustomizedDefaultWallpaperAfterCheck, weak_factory_.GetWeakPtr(), wallpaper_url, downloaded_file, base::Passed(rescaled_files.Pass())); if (!BrowserThread::PostBlockingPoolTaskAndReply(FROM_HERE, check_file_exists, on_checked_closure)) { LOG(WARNING) << "Failed to start check CheckCustomizedWallpaperFilesExist."; } } void WallpaperManagerBase::SetDefaultWallpaperPathsFromCommandLine( base::CommandLine* command_line) { default_small_wallpaper_file_ = command_line->GetSwitchValuePath( chromeos::switches::kDefaultWallpaperSmall); default_large_wallpaper_file_ = command_line->GetSwitchValuePath( chromeos::switches::kDefaultWallpaperLarge); guest_small_wallpaper_file_ = command_line->GetSwitchValuePath( chromeos::switches::kGuestWallpaperSmall); guest_large_wallpaper_file_ = command_line->GetSwitchValuePath( chromeos::switches::kGuestWallpaperLarge); child_small_wallpaper_file_ = command_line->GetSwitchValuePath( chromeos::switches::kChildWallpaperSmall); child_large_wallpaper_file_ = command_line->GetSwitchValuePath( chromeos::switches::kChildWallpaperLarge); default_wallpaper_image_.reset(); } const char* WallpaperManagerBase::GetCustomWallpaperSubdirForCurrentResolution() { WallpaperResolution resolution = GetAppropriateResolution(); return resolution == WALLPAPER_RESOLUTION_SMALL ? kSmallWallpaperSubDir : kLargeWallpaperSubDir; } void WallpaperManagerBase::CreateSolidDefaultWallpaper() { loaded_wallpapers_for_test_++; SkBitmap bitmap; bitmap.allocN32Pixels(1, 1); bitmap.eraseColor(kDefaultWallpaperColor); const gfx::ImageSkia image = gfx::ImageSkia::CreateFrom1xBitmap(bitmap); default_wallpaper_image_.reset(new user_manager::UserImage(image)); } } // namespace wallpaper