// Copyright (c) 2013 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 "chrome/browser/chromeos/login/wallpaper_manager.h" #include <vector> #include "ash/shell.h" #include "base/command_line.h" #include "base/debug/trace_event.h" #include "base/file_util.h" #include "base/files/file_enumerator.h" #include "base/files/file_path.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/threading/worker_pool.h" #include "base/time/time.h" #include "base/values.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/chromeos/login/startup_utils.h" #include "chrome/browser/chromeos/login/user.h" #include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/chromeos/login/wizard_controller.h" #include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/pref_names.h" #include "chromeos/chromeos_switches.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/notification_service.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/codec/jpeg_codec.h" #include "ui/gfx/image/image_skia_operations.h" #include "ui/gfx/skia_util.h" using content::BrowserThread; namespace { // The amount of delay before starts to move custom wallpapers to the new place. const int kMoveCustomWallpaperDelaySeconds = 30; // Default quality for encoding wallpaper. const int kDefaultEncodingQuality = 90; // A dictionary pref that maps usernames to file paths to their wallpapers. // Deprecated. Will remove this const char after done migration. const char kUserWallpapers[] = "UserWallpapers"; const int kCacheWallpaperDelayMs = 500; // A dictionary pref that maps usernames to wallpaper properties. const char kUserWallpapersProperties[] = "UserWallpapersProperties"; // Names of nodes with info about wallpaper in |kUserWallpapersProperties| // dictionary. const char kNewWallpaperDateNodeName[] = "date"; const char kNewWallpaperLayoutNodeName[] = "layout"; const char kNewWallpaperFileNodeName[] = "file"; const char kNewWallpaperTypeNodeName[] = "type"; // File path suffix of the original custom wallpaper. const char kOriginalCustomWallpaperSuffix[] = "_wallpaper"; // Maximum number of wallpapers cached by CacheUsersWallpapers(). const int kMaxWallpapersToCache = 3; // For our scaling ratios we need to round positive numbers. int RoundPositive(double x) { return static_cast<int>(floor(x + 0.5)); } // Returns custom wallpaper directory by appending corresponding |sub_dir|. base::FilePath GetCustomWallpaperDir(const char* sub_dir) { base::FilePath custom_wallpaper_dir; CHECK(PathService::Get(chrome::DIR_CHROMEOS_CUSTOM_WALLPAPERS, &custom_wallpaper_dir)); return custom_wallpaper_dir.Append(sub_dir); } bool MoveCustomWallpaperDirectory(const char* sub_dir, const std::string& email, const std::string& user_id_hash) { base::FilePath base_path = GetCustomWallpaperDir(sub_dir); base::FilePath to_path = base_path.Append(user_id_hash); base::FilePath from_path = base_path.Append(email); if (base::PathExists(from_path)) return base::Move(from_path, to_path); return false; } } // namespace namespace chromeos { 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"; static WallpaperManager* g_wallpaper_manager = NULL; // WallpaperManager, public: --------------------------------------------------- // TestApi. For testing purpose WallpaperManager::TestApi::TestApi(WallpaperManager* wallpaper_manager) : wallpaper_manager_(wallpaper_manager) { } WallpaperManager::TestApi::~TestApi() { } base::FilePath WallpaperManager::TestApi::current_wallpaper_path() { return wallpaper_manager_->current_wallpaper_path_; } // static WallpaperManager* WallpaperManager::Get() { if (!g_wallpaper_manager) g_wallpaper_manager = new WallpaperManager(); return g_wallpaper_manager; } WallpaperManager::WallpaperManager() : loaded_wallpapers_(0), command_line_for_testing_(NULL), should_cache_wallpaper_(false), weak_factory_(this) { registrar_.Add(this, chrome::NOTIFICATION_LOGIN_USER_CHANGED, content::NotificationService::AllSources()); registrar_.Add(this, chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE, content::NotificationService::AllSources()); registrar_.Add(this, chrome::NOTIFICATION_WALLPAPER_ANIMATION_FINISHED, content::NotificationService::AllSources()); sequence_token_ = BrowserThread::GetBlockingPool()-> GetNamedSequenceToken(kWallpaperSequenceTokenName); task_runner_ = BrowserThread::GetBlockingPool()-> GetSequencedTaskRunnerWithShutdownBehavior( sequence_token_, base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); wallpaper_loader_ = new UserImageLoader(ImageDecoder::ROBUST_JPEG_CODEC, task_runner_); } WallpaperManager::~WallpaperManager() { // TODO(bshe): Lifetime of WallpaperManager needs more consideration. // http://crbug.com/171694 DCHECK(!show_user_name_on_signin_subscription_); ClearObsoleteWallpaperPrefs(); weak_factory_.InvalidateWeakPtrs(); } void WallpaperManager::Shutdown() { show_user_name_on_signin_subscription_.reset(); } // static void WallpaperManager::RegisterPrefs(PrefRegistrySimple* registry) { registry->RegisterDictionaryPref(prefs::kUsersWallpaperInfo); registry->RegisterDictionaryPref(kUserWallpapers); registry->RegisterDictionaryPref(kUserWallpapersProperties); } void WallpaperManager::AddObservers() { show_user_name_on_signin_subscription_ = CrosSettings::Get()->AddSettingsObserver( kAccountsPrefShowUserNamesOnSignIn, base::Bind(&WallpaperManager::InitializeRegisteredDeviceWallpaper, base::Unretained(this))); } void WallpaperManager::EnsureLoggedInUserWallpaperLoaded() { // Some browser tests do not have a shell instance. As no wallpaper is needed // in these tests anyway, avoid loading one, preventing crashes and speeding // up the tests. if (!ash::Shell::HasInstance()) return; WallpaperInfo info; if (GetLoggedInUserWallpaperInfo(&info)) { // TODO(sschmitz): We need an index for default wallpapers for the new UI. RecordUma(info.type, -1); if (info == current_user_wallpaper_info_) return; } SetUserWallpaper(UserManager::Get()->GetLoggedInUser()->email()); } void WallpaperManager::ClearWallpaperCache() { // Cancel callback for previous cache requests. weak_factory_.InvalidateWeakPtrs(); wallpaper_cache_.clear(); } base::FilePath WallpaperManager::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); } bool WallpaperManager::GetWallpaperFromCache(const std::string& email, gfx::ImageSkia* wallpaper) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); CustomWallpaperMap::const_iterator it = wallpaper_cache_.find(email); if (it != wallpaper_cache_.end()) { *wallpaper = (*it).second; return true; } return false; } base::FilePath WallpaperManager::GetOriginalWallpaperPathForUser( const std::string& username) { std::string filename = username + kOriginalCustomWallpaperSuffix; base::FilePath user_data_dir; PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); return user_data_dir.AppendASCII(filename); } bool WallpaperManager::GetLoggedInUserWallpaperInfo(WallpaperInfo* info) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (UserManager::Get()->IsLoggedInAsStub()) { info->file = current_user_wallpaper_info_.file = ""; info->layout = current_user_wallpaper_info_.layout = ash::WALLPAPER_LAYOUT_CENTER_CROPPED; info->type = current_user_wallpaper_info_.type = User::DEFAULT; return true; } return GetUserWallpaperInfo(UserManager::Get()->GetLoggedInUser()->email(), info); } void WallpaperManager::InitializeWallpaper() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); UserManager* user_manager = UserManager::Get(); CommandLine* command_line = GetComandLine(); if (command_line->HasSwitch(chromeos::switches::kGuestSession)) { // Guest wallpaper should be initialized when guest login. // Note: This maybe called before login. So IsLoggedInAsGuest can not be // used here to determine if current user is guest. return; } if (command_line->HasSwitch(::switches::kTestType)) WizardController::SetZeroDelays(); // Zero delays is also set in autotests. if (WizardController::IsZeroDelayEnabled()) { // Ensure tests have some sort of wallpaper. ash::Shell::GetInstance()->desktop_background_controller()-> CreateEmptyWallpaper(); return; } if (!user_manager->IsUserLoggedIn()) { if (!StartupUtils::IsDeviceRegistered()) SetDefaultWallpaper(); else InitializeRegisteredDeviceWallpaper(); return; } SetUserWallpaper(user_manager->GetLoggedInUser()->email()); } void WallpaperManager::Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); switch (type) { case chrome::NOTIFICATION_LOGIN_USER_CHANGED: { ClearWallpaperCache(); BrowserThread::PostDelayedTask( BrowserThread::UI, FROM_HERE, base::Bind(&WallpaperManager::MoveLoggedInUserCustomWallpaper, weak_factory_.GetWeakPtr()), base::TimeDelta::FromSeconds(kMoveCustomWallpaperDelaySeconds)); break; } case chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE: { if (!GetComandLine()->HasSwitch(switches::kDisableBootAnimation)) { BrowserThread::PostDelayedTask( BrowserThread::UI, FROM_HERE, base::Bind(&WallpaperManager::CacheUsersWallpapers, weak_factory_.GetWeakPtr()), base::TimeDelta::FromMilliseconds(kCacheWallpaperDelayMs)); } else { should_cache_wallpaper_ = true; } break; } case chrome::NOTIFICATION_WALLPAPER_ANIMATION_FINISHED: { NotifyAnimationFinished(); if (should_cache_wallpaper_) { BrowserThread::PostDelayedTask( BrowserThread::UI, FROM_HERE, base::Bind(&WallpaperManager::CacheUsersWallpapers, weak_factory_.GetWeakPtr()), base::TimeDelta::FromMilliseconds(kCacheWallpaperDelayMs)); should_cache_wallpaper_ = false; } break; } default: NOTREACHED() << "Unexpected notification " << type; } } void WallpaperManager::RemoveUserWallpaperInfo(const std::string& email) { WallpaperInfo info; GetUserWallpaperInfo(email, &info); PrefService* prefs = g_browser_process->local_state(); DictionaryPrefUpdate prefs_wallpapers_info_update(prefs, prefs::kUsersWallpaperInfo); prefs_wallpapers_info_update->RemoveWithoutPathExpansion(email, NULL); DeleteUserWallpapers(email, info.file); } bool WallpaperManager::ResizeWallpaper( const UserImage& wallpaper, ash::WallpaperLayout layout, int preferred_width, int preferred_height, scoped_refptr<base::RefCountedBytes>* output) { DCHECK(BrowserThread::GetBlockingPool()-> IsRunningSequenceOnCurrentThread(sequence_token_)); int width = wallpaper.image().width(); int height = wallpaper.image().height(); int resized_width; int resized_height; *output = new base::RefCountedBytes(); if (layout == ash::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<double>(preferred_width) / width; double vertical_ratio = static_cast<double>(preferred_height) / height; if (vertical_ratio > horizontal_ratio) { resized_width = RoundPositive(static_cast<double>(width) * vertical_ratio); resized_height = preferred_height; } else { resized_width = preferred_width; resized_height = RoundPositive(static_cast<double>(height) * horizontal_ratio); } } else if (layout == ash::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( wallpaper.image(), skia::ImageOperations::RESIZE_LANCZOS3, gfx::Size(resized_width, resized_height)); SkBitmap image = *(resized_image.bitmap()); SkAutoLockPixels lock_input(image); gfx::JPEGCodec::Encode( reinterpret_cast<unsigned char*>(image.getAddr32(0, 0)), gfx::JPEGCodec::FORMAT_SkBitmap, image.width(), image.height(), image.width() * image.bytesPerPixel(), kDefaultEncodingQuality, &(*output)->data()); return true; } void WallpaperManager::ResizeAndSaveWallpaper(const UserImage& wallpaper, const base::FilePath& path, ash::WallpaperLayout layout, int preferred_width, int preferred_height) { if (layout == ash::WALLPAPER_LAYOUT_CENTER) { // TODO(bshe): Generates cropped custom wallpaper for CENTER layout. if (base::PathExists(path)) base::DeleteFile(path, false); return; } scoped_refptr<base::RefCountedBytes> data; if (ResizeWallpaper(wallpaper, layout, preferred_width, preferred_height, &data)) { SaveWallpaperInternal(path, reinterpret_cast<const char*>(data->front()), data->size()); } } void WallpaperManager::SetCustomWallpaper(const std::string& username, const std::string& user_id_hash, const std::string& file, ash::WallpaperLayout layout, User::WallpaperType type, const UserImage& wallpaper) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); base::FilePath wallpaper_path = GetCustomWallpaperPath(kOriginalWallpaperSubDir, user_id_hash, file); // If decoded wallpaper is empty, we are probably failed to decode the file. // Use default wallpaper in this case. if (wallpaper.image().isNull()) { SetDefaultWallpaper(); return; } bool is_persistent = !UserManager::Get()->IsUserNonCryptohomeDataEphemeral(username); wallpaper.image().EnsureRepsForSupportedScales(); scoped_ptr<gfx::ImageSkia> deep_copy(wallpaper.image().DeepCopy()); WallpaperInfo wallpaper_info = { wallpaper_path.value(), layout, type, // Date field is not used. base::Time::Now().LocalMidnight() }; // Block shutdown on this task. Otherwise, we may lost the custom wallpaper // user selected. scoped_refptr<base::SequencedTaskRunner> blocking_task_runner = BrowserThread::GetBlockingPool()-> GetSequencedTaskRunnerWithShutdownBehavior(sequence_token_, base::SequencedWorkerPool::BLOCK_SHUTDOWN); // TODO(bshe): This may break if RawImage becomes RefCountedMemory. blocking_task_runner->PostTask(FROM_HERE, base::Bind(&WallpaperManager::ProcessCustomWallpaper, base::Unretained(this), user_id_hash, is_persistent, wallpaper_info, base::Passed(&deep_copy), wallpaper.raw_image())); ash::Shell::GetInstance()->desktop_background_controller()-> SetCustomWallpaper(wallpaper.image(), layout); std::string relative_path = base::FilePath(user_id_hash).Append(file).value(); // User's custom wallpaper path is determined by relative path and the // appropriate wallpaper resolution in GetCustomWallpaperInternal. WallpaperInfo info = { relative_path, layout, User::CUSTOMIZED, base::Time::Now().LocalMidnight() }; SetUserWallpaperInfo(username, info, is_persistent); } void WallpaperManager::SetDefaultWallpaper() { current_wallpaper_path_.clear(); if (ash::Shell::GetInstance()->desktop_background_controller()-> SetDefaultWallpaper(UserManager::Get()->IsLoggedInAsGuest())) loaded_wallpapers_++; } void WallpaperManager::SetInitialUserWallpaper(const std::string& username, bool is_persistent) { current_user_wallpaper_info_.file = ""; current_user_wallpaper_info_.layout = ash::WALLPAPER_LAYOUT_CENTER_CROPPED; current_user_wallpaper_info_.type = User::DEFAULT; current_user_wallpaper_info_.date = base::Time::Now().LocalMidnight(); WallpaperInfo info = current_user_wallpaper_info_; SetUserWallpaperInfo(username, info, is_persistent); SetLastSelectedUser(username); // Some browser tests do not have a shell instance. As no wallpaper is needed // in these tests anyway, avoid loading one, preventing crashes and speeding // up the tests. if (ash::Shell::HasInstance()) SetDefaultWallpaper(); } void WallpaperManager::SetUserWallpaperInfo(const std::string& username, const WallpaperInfo& info, bool is_persistent) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); current_user_wallpaper_info_ = info; if (!is_persistent) return; PrefService* local_state = g_browser_process->local_state(); DictionaryPrefUpdate wallpaper_update(local_state, prefs::kUsersWallpaperInfo); base::DictionaryValue* wallpaper_info_dict = new base::DictionaryValue(); wallpaper_info_dict->SetString(kNewWallpaperDateNodeName, base::Int64ToString(info.date.ToInternalValue())); wallpaper_info_dict->SetString(kNewWallpaperFileNodeName, info.file); wallpaper_info_dict->SetInteger(kNewWallpaperLayoutNodeName, info.layout); wallpaper_info_dict->SetInteger(kNewWallpaperTypeNodeName, info.type); wallpaper_update->SetWithoutPathExpansion(username, wallpaper_info_dict); } void WallpaperManager::SetLastSelectedUser( const std::string& last_selected_user) { last_selected_user_ = last_selected_user; } void WallpaperManager::SetUserWallpaper(const std::string& email) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (email == UserManager::kGuestUserName) { SetDefaultWallpaper(); return; } if (!UserManager::Get()->IsKnownUser(email)) return; SetLastSelectedUser(email); WallpaperInfo info; if (GetUserWallpaperInfo(email, &info)) { gfx::ImageSkia user_wallpaper; current_user_wallpaper_info_ = info; if (GetWallpaperFromCache(email, &user_wallpaper)) { ash::Shell::GetInstance()->desktop_background_controller()-> SetCustomWallpaper(user_wallpaper, info.layout); } else { if (info.type == User::CUSTOMIZED) { ash::WallpaperResolution resolution = ash::Shell::GetInstance()-> desktop_background_controller()->GetAppropriateResolution(); const char* sub_dir = (resolution == ash::WALLPAPER_RESOLUTION_SMALL) ? kSmallWallpaperSubDir : kLargeWallpaperSubDir; // Wallpaper is not resized when layout is ash::WALLPAPER_LAYOUT_CENTER. // Original wallpaper should be used in this case. // TODO(bshe): Generates cropped custom wallpaper for CENTER layout. if (info.layout == ash::WALLPAPER_LAYOUT_CENTER) sub_dir = kOriginalWallpaperSubDir; base::FilePath wallpaper_path = GetCustomWallpaperDir(sub_dir); wallpaper_path = wallpaper_path.Append(info.file); if (current_wallpaper_path_ == wallpaper_path) return; current_wallpaper_path_ = wallpaper_path; loaded_wallpapers_++; task_runner_->PostTask(FROM_HERE, base::Bind(&WallpaperManager::GetCustomWallpaperInternal, base::Unretained(this), email, info, wallpaper_path, true /* update wallpaper */)); return; } if (info.file.empty()) { // Uses default built-in wallpaper when file is empty. Eventually, we // will only ship one built-in wallpaper in ChromeOS image. SetDefaultWallpaper(); return; } // Load downloaded ONLINE or converted DEFAULT wallpapers. LoadWallpaper(email, info, true /* update wallpaper */); } } else { SetInitialUserWallpaper(email, true); } } void WallpaperManager::SetWallpaperFromImageSkia( const gfx::ImageSkia& wallpaper, ash::WallpaperLayout layout) { ash::Shell::GetInstance()->desktop_background_controller()-> SetCustomWallpaper(wallpaper, layout); } void WallpaperManager::UpdateWallpaper() { ClearWallpaperCache(); current_wallpaper_path_.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()) { SetDefaultWallpaper(); return; } SetUserWallpaper(last_selected_user_); } void WallpaperManager::AddObserver(WallpaperManager::Observer* observer) { observers_.AddObserver(observer); } void WallpaperManager::RemoveObserver(WallpaperManager::Observer* observer) { observers_.RemoveObserver(observer); } void WallpaperManager::NotifyAnimationFinished() { FOR_EACH_OBSERVER( Observer, observers_, OnWallpaperAnimationFinished(last_selected_user_)); } // WallpaperManager, private: -------------------------------------------------- void WallpaperManager::CacheUsersWallpapers() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); UserList users = UserManager::Get()->GetUsers(); if (!users.empty()) { 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_email = (*it)->email(); CacheUserWallpaper(user_email); } } } void WallpaperManager::CacheUserWallpaper(const std::string& email) { if (wallpaper_cache_.find(email) == wallpaper_cache_.end()) return; WallpaperInfo info; if (GetUserWallpaperInfo(email, &info)) { base::FilePath wallpaper_dir; base::FilePath wallpaper_path; if (info.type == User::CUSTOMIZED) { ash::WallpaperResolution resolution = ash::Shell::GetInstance()-> desktop_background_controller()->GetAppropriateResolution(); const char* sub_dir = (resolution == ash::WALLPAPER_RESOLUTION_SMALL) ? kSmallWallpaperSubDir : kLargeWallpaperSubDir; base::FilePath wallpaper_path = GetCustomWallpaperDir(sub_dir); wallpaper_path = wallpaper_path.Append(info.file); task_runner_->PostTask(FROM_HERE, base::Bind(&WallpaperManager::GetCustomWallpaperInternal, base::Unretained(this), email, info, wallpaper_path, false /* do not update wallpaper */)); return; } LoadWallpaper(email, info, false /* do not update wallpaper */); } } void WallpaperManager::ClearObsoleteWallpaperPrefs() { PrefService* prefs = g_browser_process->local_state(); DictionaryPrefUpdate wallpaper_properties_pref(prefs, kUserWallpapersProperties); wallpaper_properties_pref->Clear(); DictionaryPrefUpdate wallpapers_pref(prefs, kUserWallpapers); wallpapers_pref->Clear(); } void WallpaperManager::DeleteAllExcept(const base::FilePath& path) { base::FilePath dir = path.DirName(); if (base::DirectoryExists(dir)) { base::FileEnumerator files(dir, false, base::FileEnumerator::FILES); for (base::FilePath current = files.Next(); !current.empty(); current = files.Next()) { if (current != path) base::DeleteFile(current, false); } } } void WallpaperManager::DeleteWallpaperInList( const std::vector<base::FilePath>& file_list) { for (std::vector<base::FilePath>::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(); } } } void WallpaperManager::DeleteUserWallpapers(const std::string& email, const std::string& path_to_file) { std::vector<base::FilePath> 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(email)); 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(email)); 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(email)); 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(email)); wallpaper_path = wallpaper_path.Append(path_to_file); file_to_remove.push_back(wallpaper_path); base::WorkerPool::PostTask( FROM_HERE, base::Bind(&WallpaperManager::DeleteWallpaperInList, base::Unretained(this), file_to_remove), false); } void WallpaperManager::EnsureCustomWallpaperDirectories( const std::string& user_id_hash) { base::FilePath dir; dir = GetCustomWallpaperDir(kSmallWallpaperSubDir); dir = dir.Append(user_id_hash); if (!base::PathExists(dir)) base::CreateDirectory(dir); dir = GetCustomWallpaperDir(kLargeWallpaperSubDir); dir = dir.Append(user_id_hash); if (!base::PathExists(dir)) base::CreateDirectory(dir); dir = GetCustomWallpaperDir(kOriginalWallpaperSubDir); dir = dir.Append(user_id_hash); if (!base::PathExists(dir)) base::CreateDirectory(dir); dir = GetCustomWallpaperDir(kThumbnailWallpaperSubDir); dir = dir.Append(user_id_hash); if (!base::PathExists(dir)) base::CreateDirectory(dir); } CommandLine* WallpaperManager::GetComandLine() { CommandLine* command_line = command_line_for_testing_ ? command_line_for_testing_ : CommandLine::ForCurrentProcess(); return command_line; } void WallpaperManager::InitializeRegisteredDeviceWallpaper() { if (UserManager::Get()->IsUserLoggedIn()) return; bool disable_boot_animation = GetComandLine()-> HasSwitch(switches::kDisableBootAnimation); bool show_users = true; bool result = CrosSettings::Get()->GetBoolean( kAccountsPrefShowUserNamesOnSignIn, &show_users); DCHECK(result) << "Unable to fetch setting " << kAccountsPrefShowUserNamesOnSignIn; const chromeos::UserList& users = UserManager::Get()->GetUsers(); if (!show_users || users.empty()) { // Boot into sign in form, preload default wallpaper. SetDefaultWallpaper(); return; } if (!disable_boot_animation) { // Normal boot, load user wallpaper. // If normal boot animation is disabled wallpaper would be set // asynchronously once user pods are loaded. SetUserWallpaper(users[0]->email()); } } void WallpaperManager::LoadWallpaper(const std::string& email, const WallpaperInfo& info, bool update_wallpaper) { base::FilePath wallpaper_dir; base::FilePath wallpaper_path; if (info.type == User::ONLINE) { std::string file_name = GURL(info.file).ExtractFileName(); ash::WallpaperResolution resolution = ash::Shell::GetInstance()-> desktop_background_controller()->GetAppropriateResolution(); // Only solid color wallpapers have stretch layout and they have only one // resolution. if (info.layout != ash::WALLPAPER_LAYOUT_STRETCH && resolution == ash::WALLPAPER_RESOLUTION_SMALL) { file_name = base::FilePath(file_name).InsertBeforeExtension( kSmallWallpaperSuffix).value(); } CHECK(PathService::Get(chrome::DIR_CHROMEOS_WALLPAPERS, &wallpaper_dir)); wallpaper_path = wallpaper_dir.Append(file_name); if (current_wallpaper_path_ == wallpaper_path) return; if (update_wallpaper) current_wallpaper_path_ = wallpaper_path; loaded_wallpapers_++; StartLoad(email, info, update_wallpaper, wallpaper_path); } else if (info.type == 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; PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); wallpaper_path = user_data_dir.Append(info.file); StartLoad(email, info, update_wallpaper, wallpaper_path); } else { // In unexpected cases, revert to default wallpaper to fail safely. See // crosbug.com/38429. LOG(ERROR) << "Wallpaper reverts to default unexpected."; SetDefaultWallpaper(); } } bool WallpaperManager::GetUserWallpaperInfo(const std::string& email, WallpaperInfo* info){ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (UserManager::Get()->IsUserNonCryptohomeDataEphemeral(email)) { // Default to the values cached in memory. *info = current_user_wallpaper_info_; // Ephemeral users do not save anything to local state. But we have got // wallpaper info from memory. Returns true. return true; } const DictionaryValue* user_wallpapers = g_browser_process->local_state()-> GetDictionary(prefs::kUsersWallpaperInfo); const base::DictionaryValue* wallpaper_info_dict; if (user_wallpapers->GetDictionaryWithoutPathExpansion( email, &wallpaper_info_dict)) { info->file = ""; info->layout = ash::WALLPAPER_LAYOUT_CENTER_CROPPED; info->type = User::UNKNOWN; info->date = base::Time::Now().LocalMidnight(); wallpaper_info_dict->GetString(kNewWallpaperFileNodeName, &(info->file)); int temp; wallpaper_info_dict->GetInteger(kNewWallpaperLayoutNodeName, &temp); info->layout = static_cast<ash::WallpaperLayout>(temp); wallpaper_info_dict->GetInteger(kNewWallpaperTypeNodeName, &temp); info->type = static_cast<User::WallpaperType>(temp); std::string date_string; int64 val; if (!(wallpaper_info_dict->GetString(kNewWallpaperDateNodeName, &date_string) && base::StringToInt64(date_string, &val))) val = 0; info->date = base::Time::FromInternalValue(val); return true; } return false; } void WallpaperManager::MoveCustomWallpapersOnWorker( const std::string& email, const std::string& user_id_hash) { DCHECK(BrowserThread::GetBlockingPool()-> IsRunningSequenceOnCurrentThread(sequence_token_)); if (MoveCustomWallpaperDirectory(kOriginalWallpaperSubDir, email, 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(&WallpaperManager::MoveCustomWallpapersSuccess, base::Unretained(this), email, user_id_hash)); } MoveCustomWallpaperDirectory(kLargeWallpaperSubDir, email, user_id_hash); MoveCustomWallpaperDirectory(kSmallWallpaperSubDir, email, user_id_hash); MoveCustomWallpaperDirectory(kThumbnailWallpaperSubDir, email, user_id_hash); } void WallpaperManager::MoveCustomWallpapersSuccess( const std::string& email, const std::string& user_id_hash) { WallpaperInfo info; GetUserWallpaperInfo(email, &info); if (info.type == 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. std::string relative_path = base::FilePath(user_id_hash).Append(info.file).value(); info.file = relative_path; bool is_persistent = !UserManager::Get()->IsUserNonCryptohomeDataEphemeral(email); SetUserWallpaperInfo(email, info, is_persistent); } } void WallpaperManager::MoveLoggedInUserCustomWallpaper() { const User* logged_in_user = UserManager::Get()->GetLoggedInUser(); task_runner_->PostTask( FROM_HERE, base::Bind(&WallpaperManager::MoveCustomWallpapersOnWorker, base::Unretained(this), logged_in_user->email(), logged_in_user->username_hash())); } void WallpaperManager::GetCustomWallpaperInternal( const std::string& email, const WallpaperInfo& info, const base::FilePath& wallpaper_path, bool update_wallpaper) { DCHECK(BrowserThread::GetBlockingPool()-> IsRunningSequenceOnCurrentThread(sequence_token_)); base::FilePath valid_path = wallpaper_path; if (!base::PathExists(wallpaper_path)) { // Falls back on original file if the correct resoltuion 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.file); } 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, email, info.file); } 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(&WallpaperManager::SetDefaultWallpaper, base::Unretained(this))); } else { BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(&WallpaperManager::StartLoad, base::Unretained(this), email, info, update_wallpaper, valid_path)); } } void WallpaperManager::OnWallpaperDecoded(const std::string& email, ash::WallpaperLayout layout, bool update_wallpaper, const UserImage& wallpaper) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); TRACE_EVENT_ASYNC_END0("ui", "LoadAndDecodeWallpaper", this); // If decoded wallpaper is empty, we are probably failed to decode the file. // Use default wallpaper in this case. if (wallpaper.image().isNull()) { // Updates user pref to default wallpaper. WallpaperInfo info = { "", ash::WALLPAPER_LAYOUT_CENTER_CROPPED, User::DEFAULT, base::Time::Now().LocalMidnight() }; SetUserWallpaperInfo(email, info, true); if (update_wallpaper) SetDefaultWallpaper(); return; } // Only cache user wallpaper at login screen. if (!UserManager::Get()->IsUserLoggedIn()) { wallpaper_cache_.insert(std::make_pair(email, wallpaper.image())); } if (update_wallpaper) { ash::Shell::GetInstance()->desktop_background_controller()-> SetCustomWallpaper(wallpaper.image(), layout); } } void WallpaperManager::ProcessCustomWallpaper( const std::string& user_id_hash, bool persistent, const WallpaperInfo& info, scoped_ptr<gfx::ImageSkia> image, const UserImage::RawImage& raw_image) { DCHECK(BrowserThread::GetBlockingPool()-> IsRunningSequenceOnCurrentThread(sequence_token_)); UserImage wallpaper(*image.get(), raw_image); if (persistent) { SaveCustomWallpaper(user_id_hash, base::FilePath(info.file), info.layout, wallpaper); } } void WallpaperManager::SaveCustomWallpaper(const std::string& user_id_hash, const base::FilePath& original_path, ash::WallpaperLayout layout, const UserImage& wallpaper) { DCHECK(BrowserThread::GetBlockingPool()-> IsRunningSequenceOnCurrentThread(sequence_token_)); 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(wallpaper, original_path, ash::WALLPAPER_LAYOUT_STRETCH, wallpaper.image().width(), wallpaper.image().height()); DeleteAllExcept(original_path); ResizeAndSaveWallpaper(wallpaper, small_wallpaper_path, layout, ash::kSmallWallpaperMaxWidth, ash::kSmallWallpaperMaxHeight); DeleteAllExcept(small_wallpaper_path); ResizeAndSaveWallpaper(wallpaper, large_wallpaper_path, layout, ash::kLargeWallpaperMaxWidth, ash::kLargeWallpaperMaxHeight); DeleteAllExcept(large_wallpaper_path); } void WallpaperManager::RecordUma(User::WallpaperType type, int index) { UMA_HISTOGRAM_ENUMERATION("Ash.Wallpaper.Type", type, User::WALLPAPER_TYPE_COUNT); } void WallpaperManager::SaveWallpaperInternal(const base::FilePath& path, const char* data, int size) { int written_bytes = file_util::WriteFile(path, data, size); DCHECK(written_bytes == size); } void WallpaperManager::StartLoad(const std::string& email, const WallpaperInfo& info, bool update_wallpaper, const base::FilePath& wallpaper_path) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); TRACE_EVENT_ASYNC_BEGIN0("ui", "LoadAndDecodeWallpaper", this); wallpaper_loader_->Start(wallpaper_path.value(), 0, base::Bind(&WallpaperManager::OnWallpaperDecoded, base::Unretained(this), email, info.layout, update_wallpaper)); } } // namespace chromeos