blob: cb86acc82c18e0dca4bcf37844825a361f9a291a [file] [log] [blame]
// 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 "chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h"
#include <numeric>
#include <vector>
#include "ash/ash_constants.h"
#include "ash/ash_switches.h"
#include "ash/desktop_background/desktop_background_controller.h"
#include "ash/shell.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/debug/trace_event.h"
#include "base/files/file_enumerator.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 "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/chromeos/customization_document.h"
#include "chrome/browser/chromeos/login/startup_utils.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/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 "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
#include "third_party/skia/include/core/SkColor.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 chromeos {
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 kNewWallpaperLocationNodeName[] = "file";
const char kNewWallpaperTypeNodeName[] = "type";
#if !defined(USE_ATHENA)
// Maximum number of wallpapers cached by CacheUsersWallpapers().
const int kMaxWallpapersToCache = 3;
#endif
// Maximum number of entries in WallpaperManager::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;
// 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& user_id,
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(user_id);
if (base::PathExists(from_path))
return base::Move(from_path, to_path);
return false;
}
// These global default values are used to set customized default
// wallpaper path in WallpaperManager::InitializeWallpaper().
base::FilePath GetCustomizedWallpaperDefaultRescaledFileName(
const std::string& suffix) {
const base::FilePath default_downloaded_file_name =
ServicesCustomizationDocument::GetCustomizedWallpaperDownloadedFileName();
const base::FilePath default_cache_dir =
ServicesCustomizationDocument::GetCustomizedWallpaperCacheDir();
if (default_downloaded_file_name.empty() || default_cache_dir.empty())
return base::FilePath();
return default_cache_dir.Append(
default_downloaded_file_name.BaseName().value() + suffix);
}
// Whether DesktopBackgroundController should start with customized default
// wallpaper in WallpaperManager::InitializeWallpaper() or not.
bool ShouldUseCustomizedDefaultWallpaper() {
PrefService* pref_service = g_browser_process->local_state();
return !(pref_service->FindPreference(
prefs::kCustomizationDefaultWallpaperURL)
->IsDefaultValue());
}
// Deletes a list of wallpaper files in |file_list|.
void 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();
}
}
}
// 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 = 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);
}
// 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;
}
// Returns index of the first public session user found in |users|
// or -1 otherwise.
int FindPublicSession(const user_manager::UserList& users) {
int index = -1;
int i = 0;
for (user_manager::UserList::const_iterator it = users.begin();
it != users.end();
++it, ++i) {
if ((*it)->GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT) {
index = i;
break;
}
}
return index;
}
} // namespace
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;
static WallpaperManager* g_wallpaper_manager = NULL;
class WallpaperManager::CustomizedWallpaperRescaledFiles {
public:
CustomizedWallpaperRescaledFiles(const base::FilePath& path_downloaded,
const base::FilePath& path_rescaled_small,
const base::FilePath& path_rescaled_large);
bool AllSizesExist() const;
// Closure will hold unretained pointer to this object. So caller must
// make sure that the closure will be destoyed before this object.
// Closure must be called on BlockingPool.
base::Closure CreateCheckerClosure();
const base::FilePath& path_downloaded() const { return path_downloaded_; }
const base::FilePath& path_rescaled_small() const {
return path_rescaled_small_;
}
const base::FilePath& path_rescaled_large() const {
return path_rescaled_large_;
}
bool downloaded_exists() const { return downloaded_exists_; }
bool rescaled_small_exists() const { return rescaled_small_exists_; }
bool rescaled_large_exists() const { return rescaled_large_exists_; }
private:
// Must be called on BlockingPool.
void CheckCustomizedWallpaperFilesExist();
const base::FilePath path_downloaded_;
const base::FilePath path_rescaled_small_;
const base::FilePath path_rescaled_large_;
bool downloaded_exists_;
bool rescaled_small_exists_;
bool rescaled_large_exists_;
DISALLOW_COPY_AND_ASSIGN(CustomizedWallpaperRescaledFiles);
};
WallpaperManager::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
WallpaperManager::CustomizedWallpaperRescaledFiles::CreateCheckerClosure() {
return base::Bind(&WallpaperManager::CustomizedWallpaperRescaledFiles::
CheckCustomizedWallpaperFilesExist,
base::Unretained(this));
}
void WallpaperManager::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 WallpaperManager::CustomizedWallpaperRescaledFiles::AllSizesExist() const {
return rescaled_small_exists_ && rescaled_large_exists_;
}
// This object is passed between several threads while wallpaper is being
// loaded. It will notify callback when last reference to it is removed
// (thus indicating that the last load action has finished).
class MovableOnDestroyCallback {
public:
explicit MovableOnDestroyCallback(const base::Closure& callback)
: callback_(callback) {
}
~MovableOnDestroyCallback() {
if (!callback_.is_null())
callback_.Run();
}
private:
base::Closure callback_;
};
WallpaperManager::PendingWallpaper::PendingWallpaper(
const base::TimeDelta delay,
const std::string& user_id)
: user_id_(user_id),
default_(false),
on_finish_(new MovableOnDestroyCallback(
base::Bind(&WallpaperManager::PendingWallpaper::OnWallpaperSet,
this))) {
timer.Start(
FROM_HERE,
delay,
base::Bind(&WallpaperManager::PendingWallpaper::ProcessRequest, this));
}
WallpaperManager::PendingWallpaper::~PendingWallpaper() {}
void WallpaperManager::PendingWallpaper::ResetSetWallpaperImage(
const gfx::ImageSkia& image,
const WallpaperInfo& info) {
SetMode(image, info, base::FilePath(), false);
}
void WallpaperManager::PendingWallpaper::ResetLoadWallpaper(
const WallpaperInfo& info) {
SetMode(gfx::ImageSkia(), info, base::FilePath(), false);
}
void WallpaperManager::PendingWallpaper::ResetSetCustomWallpaper(
const WallpaperInfo& info,
const base::FilePath& wallpaper_path) {
SetMode(gfx::ImageSkia(), info, wallpaper_path, false);
}
void WallpaperManager::PendingWallpaper::ResetSetDefaultWallpaper() {
SetMode(gfx::ImageSkia(), WallpaperInfo(), base::FilePath(), true);
}
void WallpaperManager::PendingWallpaper::SetMode(
const gfx::ImageSkia& image,
const WallpaperInfo& info,
const base::FilePath& wallpaper_path,
const bool is_default) {
user_wallpaper_ = image;
info_ = info;
wallpaper_path_ = wallpaper_path;
default_ = is_default;
}
void WallpaperManager::PendingWallpaper::ProcessRequest() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
timer.Stop(); // Erase reference to self.
WallpaperManager* manager = WallpaperManager::Get();
if (manager->pending_inactive_ == this)
manager->pending_inactive_ = NULL;
started_load_at_ = base::Time::Now();
if (default_) {
manager->DoSetDefaultWallpaper(user_id_, on_finish_.Pass());
} else if (!user_wallpaper_.isNull()) {
ash::Shell::GetInstance()
->desktop_background_controller()
->SetWallpaperImage(user_wallpaper_, info_.layout);
} else if (!wallpaper_path_.empty()) {
manager->task_runner_->PostTask(
FROM_HERE,
base::Bind(&WallpaperManager::GetCustomWallpaperInternal,
user_id_,
info_,
wallpaper_path_,
true /* update wallpaper */,
base::Passed(on_finish_.Pass()),
manager->weak_factory_.GetWeakPtr()));
} else if (!info_.location.empty()) {
manager->LoadWallpaper(user_id_, info_, true, on_finish_.Pass());
} else {
// PendingWallpaper was created and never initialized?
NOTREACHED();
// Error. Do not record time.
started_load_at_ = base::Time();
}
on_finish_.reset();
}
void WallpaperManager::PendingWallpaper::OnWallpaperSet() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// The only known case for this check to fail is global destruction during
// wallpaper load. It should never happen.
if (!BrowserThread::CurrentlyOn(BrowserThread::UI))
return; // We are in a process of global destruction.
timer.Stop(); // Erase reference to self.
WallpaperManager* manager = WallpaperManager::Get();
if (!started_load_at_.is_null()) {
const base::TimeDelta elapsed = base::Time::Now() - started_load_at_;
manager->SaveLastLoadTime(elapsed);
}
if (manager->pending_inactive_ == this) {
// ProcessRequest() was never executed.
manager->pending_inactive_ = NULL;
}
// Destroy self.
manager->RemovePendingWallpaperFromList(this);
}
// 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_;
}
bool WallpaperManager::TestApi::GetWallpaperFromCache(
const std::string& user_id, gfx::ImageSkia* image) {
return wallpaper_manager_->GetWallpaperFromCache(user_id, image);
}
void WallpaperManager::TestApi::SetWallpaperCache(const std::string& user_id,
const gfx::ImageSkia& image) {
DCHECK(!image.isNull());
wallpaper_manager_->wallpaper_cache_[user_id] = image;
}
void WallpaperManager::TestApi::ClearDisposableWallpaperCache() {
wallpaper_manager_->ClearDisposableWallpaperCache();
}
// 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),
pending_inactive_(NULL),
weak_factory_(this) {
SetDefaultWallpaperPathsFromCommandLine(
base::CommandLine::ForCurrentProcess());
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,
weak_factory_.GetWeakPtr()));
}
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)) {
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 WallpaperManager::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<std::string> 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 WallpaperManager::GetLoggedInUserWallpaperInfo(WallpaperInfo* info) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (user_manager::UserManager::Get()->IsLoggedInAsStub()) {
info->location = current_user_wallpaper_info_.location = "";
info->layout = current_user_wallpaper_info_.layout =
ash::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);
}
void WallpaperManager::InitializeWallpaper() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
// Apply device customization.
if (ShouldUseCustomizedDefaultWallpaper()) {
SetDefaultWallpaperPath(
GetCustomizedWallpaperDefaultRescaledFileName(kSmallWallpaperSuffix),
scoped_ptr<gfx::ImageSkia>().Pass(),
GetCustomizedWallpaperDefaultRescaledFileName(kLargeWallpaperSuffix),
scoped_ptr<gfx::ImageSkia>().Pass());
}
CommandLine* command_line = GetCommandLine();
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())
SetDefaultWallpaperDelayed(chromeos::login::kSignInUser);
else
InitializeRegisteredDeviceWallpaper();
return;
}
SetUserWallpaperDelayed(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: {
ClearDisposableWallpaperCache();
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 (!GetCommandLine()->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& user_id) {
WallpaperInfo info;
GetUserWallpaperInfo(user_id, &info);
PrefService* prefs = g_browser_process->local_state();
DictionaryPrefUpdate prefs_wallpapers_info_update(prefs,
prefs::kUsersWallpaperInfo);
prefs_wallpapers_info_update->RemoveWithoutPathExpansion(user_id, NULL);
DeleteUserWallpapers(user_id, info.location);
}
// static
bool WallpaperManager::ResizeImage(const gfx::ImageSkia& image,
ash::WallpaperLayout layout,
int preferred_width,
int preferred_height,
scoped_refptr<base::RefCountedBytes>* 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 == 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(
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<unsigned char*>(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 WallpaperManager::ResizeAndSaveWallpaper(const gfx::ImageSkia& image,
const base::FilePath& path,
ash::WallpaperLayout layout,
int preferred_width,
int preferred_height,
gfx::ImageSkia* output_skia) {
if (layout == ash::WALLPAPER_LAYOUT_CENTER) {
// TODO(bshe): Generates cropped custom wallpaper for CENTER layout.
if (base::PathExists(path))
base::DeleteFile(path, false);
return false;
}
scoped_refptr<base::RefCountedBytes> data;
if (ResizeImage(image,
layout,
preferred_width,
preferred_height,
&data,
output_skia)) {
return SaveWallpaperInternal(
path, reinterpret_cast<const char*>(data->front()), data->size());
}
return false;
}
bool WallpaperManager::IsPolicyControlled(const std::string& user_id) const {
chromeos::WallpaperInfo info;
if (!GetUserWallpaperInfo(user_id, &info))
return false;
return info.type == user_manager::User::POLICY;
}
void WallpaperManager::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 WallpaperManager::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);
}
void WallpaperManager::OnPolicyFetched(const std::string& policy,
const std::string& user_id,
scoped_ptr<std::string> data) {
if (!data)
return;
wallpaper_loader_->Start(
data.Pass(),
0, // Do not crop.
base::Bind(&WallpaperManager::SetPolicyControlledWallpaper,
weak_factory_.GetWeakPtr(),
user_id));
}
// static
WallpaperManager::WallpaperResolution
WallpaperManager::GetAppropriateResolution() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
gfx::Size size =
ash::DesktopBackgroundController::GetMaxDisplaySizeInNative();
return (size.width() > kSmallWallpaperMaxWidth ||
size.height() > kSmallWallpaperMaxHeight)
? WALLPAPER_RESOLUTION_LARGE
: WALLPAPER_RESOLUTION_SMALL;
}
// static
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);
}
void WallpaperManager::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(&WallpaperManager::SetCustomWallpaperOnSanitizedUsername,
weak_factory_.GetWeakPtr(),
user_id,
user_image.image(),
true /* update wallpaper */));
} else {
SetCustomWallpaper(user_id,
user->username_hash(),
"policy-controlled.jpeg",
ash::WALLPAPER_LAYOUT_CENTER_CROPPED,
user_manager::User::POLICY,
user_image.image(),
true /* update wallpaper */);
}
}
void WallpaperManager::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",
ash::WALLPAPER_LAYOUT_CENTER_CROPPED,
user_manager::User::POLICY,
image,
update_wallpaper);
}
void WallpaperManager::SetCustomWallpaper(
const std::string& user_id,
const std::string& user_id_hash,
const std::string& file,
ash::WallpaperLayout layout,
user_manager::User::WallpaperType type,
const gfx::ImageSkia& image,
bool update_wallpaper) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// There is no visible background in kiosk mode.
if (user_manager::UserManager::Get()->IsLoggedInAsKioskApp())
return;
// Don't allow custom wallpapers while policy is in effect.
if (type != user_manager::User::POLICY && IsPolicyControlled(user_id))
return;
base::FilePath wallpaper_path =
GetCustomWallpaperPath(kOriginalWallpaperSubDir, user_id_hash, file);
// If decoded wallpaper is empty, we have probably failed to decode the file.
// Use default wallpaper in this case.
if (image.isNull()) {
SetDefaultWallpaperDelayed(user_id);
return;
}
const user_manager::User* user =
user_manager::UserManager::Get()->FindUser(user_id);
CHECK(user);
bool is_persistent =
!user_manager::UserManager::Get()->IsUserNonCryptohomeDataEphemeral(
user_id) ||
(type == user_manager::User::POLICY &&
user->GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT);
WallpaperInfo wallpaper_info = {
wallpaper_path.value(),
layout,
type,
// Date field is not used.
base::Time::Now().LocalMidnight()
};
if (is_persistent) {
image.EnsureRepsForSupportedScales();
scoped_ptr<gfx::ImageSkia> deep_copy(image.DeepCopy());
// Block shutdown on this task. Otherwise, we may lose the custom wallpaper
// that the 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::SaveCustomWallpaper,
user_id_hash,
base::FilePath(wallpaper_info.location),
wallpaper_info.layout,
base::Passed(deep_copy.Pass())));
}
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,
type,
base::Time::Now().LocalMidnight()
};
SetUserWallpaperInfo(user_id, info, is_persistent);
if (update_wallpaper) {
GetPendingWallpaper(user_id, false)->ResetSetWallpaperImage(image, info);
}
wallpaper_cache_[user_id] = image;
}
void WallpaperManager::SetDefaultWallpaperNow(const std::string& user_id) {
GetPendingWallpaper(user_id, false)->ResetSetDefaultWallpaper();
}
void WallpaperManager::SetDefaultWallpaperDelayed(const std::string& user_id) {
GetPendingWallpaper(user_id, true)->ResetSetDefaultWallpaper();
}
void WallpaperManager::DoSetDefaultWallpaper(
const std::string& user_id,
MovableOnDestroyCallbackHolder on_finish) {
// There is no visible background in kiosk mode.
if (user_manager::UserManager::Get()->IsLoggedInAsKioskApp())
return;
current_wallpaper_path_.clear();
wallpaper_cache_.erase(user_id);
// 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;
WallpaperResolution resolution = GetAppropriateResolution();
const bool use_small = (resolution == WALLPAPER_RESOLUTION_SMALL);
const base::FilePath* file = NULL;
if (user_manager::UserManager::Get()->IsLoggedInAsGuest()) {
file =
use_small ? &guest_small_wallpaper_file_ : &guest_large_wallpaper_file_;
} else {
file = use_small ? &default_small_wallpaper_file_
: &default_large_wallpaper_file_;
}
ash::WallpaperLayout layout = use_small
? ash::WALLPAPER_LAYOUT_CENTER
: ash::WALLPAPER_LAYOUT_CENTER_CROPPED;
DCHECK(file);
if (!default_wallpaper_image_.get() ||
default_wallpaper_image_->file_path() != file->value()) {
default_wallpaper_image_.reset();
if (!file->empty()) {
loaded_wallpapers_++;
StartLoadAndSetDefaultWallpaper(
*file, layout, on_finish.Pass(), &default_wallpaper_image_);
return;
}
CreateSolidDefaultWallpaper();
}
// 1x1 wallpaper is actually solid color, so it should be stretched.
if (default_wallpaper_image_->image().width() == 1 &&
default_wallpaper_image_->image().height() == 1)
layout = ash::WALLPAPER_LAYOUT_STRETCH;
ash::Shell::GetInstance()->desktop_background_controller()->SetWallpaperImage(
default_wallpaper_image_->image(), layout);
}
// static
void WallpaperManager::SaveCustomWallpaper(const std::string& user_id_hash,
const base::FilePath& original_path,
ash::WallpaperLayout layout,
scoped_ptr<gfx::ImageSkia> 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,
ash::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 WallpaperManager::MoveCustomWallpapersOnWorker(
const std::string& user_id,
const std::string& user_id_hash,
base::WeakPtr<WallpaperManager> 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(&WallpaperManager::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 WallpaperManager::GetCustomWallpaperInternal(
const std::string& user_id,
const WallpaperInfo& info,
const base::FilePath& wallpaper_path,
bool update_wallpaper,
MovableOnDestroyCallbackHolder on_finish,
base::WeakPtr<WallpaperManager> 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(&WallpaperManager::DoSetDefaultWallpaper,
weak_ptr,
user_id,
base::Passed(on_finish.Pass())));
} else {
BrowserThread::PostTask(BrowserThread::UI,
FROM_HERE,
base::Bind(&WallpaperManager::StartLoad,
weak_ptr,
user_id,
info,
update_wallpaper,
valid_path,
base::Passed(on_finish.Pass())));
}
}
void WallpaperManager::InitInitialUserWallpaper(const std::string& user_id,
bool is_persistent) {
current_user_wallpaper_info_.location = "";
current_user_wallpaper_info_.layout = ash::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 WallpaperManager::SetUserWallpaperInfo(const std::string& user_id,
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(kNewWallpaperLocationNodeName, info.location);
wallpaper_info_dict->SetInteger(kNewWallpaperLayoutNodeName, info.layout);
wallpaper_info_dict->SetInteger(kNewWallpaperTypeNodeName, info.type);
wallpaper_update->SetWithoutPathExpansion(user_id, wallpaper_info_dict);
}
void WallpaperManager::SetUserWallpaperDelayed(const std::string& user_id) {
ScheduleSetUserWallpaper(user_id, true);
}
void WallpaperManager::SetUserWallpaperNow(const std::string& user_id) {
ScheduleSetUserWallpaper(user_id, false);
}
void WallpaperManager::ScheduleSetUserWallpaper(const std::string& user_id,
bool delayed) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// Some unit tests come here without a UserManager or without a pref system.
if (!user_manager::UserManager::IsInitialized() ||
!g_browser_process->local_state()) {
return;
}
// There is no visible background in kiosk mode.
if (user_manager::UserManager::Get()->IsLoggedInAsKioskApp())
return;
// Guest user, regular user in ephemeral mode, or kiosk app.
const user_manager::User* user =
user_manager::UserManager::Get()->FindUser(user_id);
if (user_manager::UserManager::Get()->IsUserNonCryptohomeDataEphemeral(
user_id) ||
(user != NULL && user->GetType() == user_manager::USER_TYPE_KIOSK_APP)) {
InitInitialUserWallpaper(user_id, false);
GetPendingWallpaper(user_id, delayed)->ResetSetDefaultWallpaper();
return;
}
if (!user_manager::UserManager::Get()->IsKnownUser(user_id))
return;
last_selected_user_ = user_id;
WallpaperInfo info;
if (!GetUserWallpaperInfo(user_id, &info)) {
InitInitialUserWallpaper(user_id, true);
GetUserWallpaperInfo(user_id, &info);
}
gfx::ImageSkia user_wallpaper;
current_user_wallpaper_info_ = info;
if (GetWallpaperFromCache(user_id, &user_wallpaper)) {
GetPendingWallpaper(user_id, delayed)
->ResetSetWallpaperImage(user_wallpaper, info);
} else {
if (info.location.empty()) {
// Uses default built-in wallpaper when file is empty. Eventually, we
// will only ship one built-in wallpaper in ChromeOS image.
GetPendingWallpaper(user_id, delayed)->ResetSetDefaultWallpaper();
return;
}
if (info.type == user_manager::User::CUSTOMIZED ||
info.type == user_manager::User::POLICY) {
const char* sub_dir = GetCustomWallpaperSubdirForCurrentResolution();
// 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.location);
if (current_wallpaper_path_ == wallpaper_path)
return;
current_wallpaper_path_ = wallpaper_path;
loaded_wallpapers_++;
GetPendingWallpaper(user_id, delayed)
->ResetSetCustomWallpaper(info, wallpaper_path);
return;
}
// Load downloaded ONLINE or converted DEFAULT wallpapers.
GetPendingWallpaper(user_id, delayed)->ResetLoadWallpaper(info);
}
}
void WallpaperManager::SetWallpaperFromImageSkia(const std::string& user_id,
const gfx::ImageSkia& image,
ash::WallpaperLayout layout,
bool update_wallpaper) {
DCHECK(user_manager::UserManager::Get()->IsUserLoggedIn());
// There is no visible background in kiosk mode.
if (user_manager::UserManager::Get()->IsLoggedInAsKioskApp())
return;
WallpaperInfo info;
info.layout = layout;
wallpaper_cache_[user_id] = image;
if (update_wallpaper) {
GetPendingWallpaper(last_selected_user_, false /* Not delayed */)
->ResetSetWallpaperImage(image, info);
}
}
void WallpaperManager::UpdateWallpaper(bool clear_cache) {
FOR_EACH_OBSERVER(Observer, observers_, OnUpdateWallpaperForTesting());
if (clear_cache)
wallpaper_cache_.clear();
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()) {
SetDefaultWallpaperNow(chromeos::login::kSignInUser);
return;
}
SetUserWallpaperNow(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: --------------------------------------------------
bool WallpaperManager::GetWallpaperFromCache(const std::string& user_id,
gfx::ImageSkia* image) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
CustomWallpaperMap::const_iterator it = wallpaper_cache_.find(user_id);
if (it != wallpaper_cache_.end()) {
*image = (*it).second;
return true;
}
return false;
}
void WallpaperManager::CacheUsersWallpapers() {
#if !defined(USE_ATHENA)
// TODO(dpolukhin): crbug.com/408734.
DCHECK(BrowserThread::CurrentlyOn(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);
}
}
#endif
}
void WallpaperManager::CacheUserWallpaper(const std::string& user_id) {
if (wallpaper_cache_.find(user_id) != wallpaper_cache_.end())
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);
task_runner_->PostTask(
FROM_HERE,
base::Bind(&WallpaperManager::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 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::DeleteUserWallpapers(const std::string& user_id,
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(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 WallpaperManager::SetCommandLineForTesting(
base::CommandLine* command_line) {
command_line_for_testing_ = command_line;
SetDefaultWallpaperPathsFromCommandLine(command_line);
}
CommandLine* WallpaperManager::GetCommandLine() {
CommandLine* command_line = command_line_for_testing_ ?
command_line_for_testing_ : CommandLine::ForCurrentProcess();
return command_line;
}
void WallpaperManager::InitializeRegisteredDeviceWallpaper() {
if (user_manager::UserManager::Get()->IsUserLoggedIn())
return;
bool disable_boot_animation =
GetCommandLine()->HasSwitch(switches::kDisableBootAnimation);
bool show_users = true;
bool result = CrosSettings::Get()->GetBoolean(
kAccountsPrefShowUserNamesOnSignIn, &show_users);
DCHECK(result) << "Unable to fetch setting "
<< kAccountsPrefShowUserNamesOnSignIn;
const user_manager::UserList& users =
user_manager::UserManager::Get()->GetUsers();
int public_session_user_index = FindPublicSession(users);
if ((!show_users && public_session_user_index == -1) || users.empty()) {
// Boot into sign in form, preload default wallpaper.
SetDefaultWallpaperDelayed(chromeos::login::kSignInUser);
return;
}
if (!disable_boot_animation) {
int index = public_session_user_index != -1 ? public_session_user_index : 0;
// Normal boot, load user wallpaper.
// If normal boot animation is disabled wallpaper would be set
// asynchronously once user pods are loaded.
SetUserWallpaperDelayed(users[index]->email());
}
}
void WallpaperManager::LoadWallpaper(const std::string& user_id,
const WallpaperInfo& info,
bool update_wallpaper,
MovableOnDestroyCallbackHolder on_finish) {
#if defined(USE_ATHENA)
// For Athena builds ignore all wallpaper load requests for now since they
// might result in crash (there's no ash::Shell instance).
// http://crbug.com/408734
return;
#endif
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 != ash::WALLPAPER_LAYOUT_STRETCH &&
resolution == 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(
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;
PathService::Get(chrome::DIR_USER_DATA, &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());
}
}
bool WallpaperManager::GetUserWallpaperInfo(const std::string& user_id,
WallpaperInfo* info) const {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (user_manager::UserManager::Get()->IsUserNonCryptohomeDataEphemeral(
user_id)) {
// 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 base::DictionaryValue* info_dict;
if (!g_browser_process->local_state()->
GetDictionary(prefs::kUsersWallpaperInfo)->
GetDictionaryWithoutPathExpansion(user_id, &info_dict)) {
return false;
}
// Use temporary variables to keep |info| untouched in the error case.
std::string location;
if (!info_dict->GetString(kNewWallpaperLocationNodeName, &location))
return false;
int layout;
if (!info_dict->GetInteger(kNewWallpaperLayoutNodeName, &layout))
return false;
int type;
if (!info_dict->GetInteger(kNewWallpaperTypeNodeName, &type))
return false;
std::string date_string;
if (!info_dict->GetString(kNewWallpaperDateNodeName, &date_string))
return false;
int64 date_val;
if (!base::StringToInt64(date_string, &date_val))
return false;
info->location = location;
info->layout = static_cast<ash::WallpaperLayout>(layout);
info->type = static_cast<user_manager::User::WallpaperType>(type);
info->date = base::Time::FromInternalValue(date_val);
return true;
}
void WallpaperManager::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 WallpaperManager::MoveLoggedInUserCustomWallpaper() {
const user_manager::User* logged_in_user =
user_manager::UserManager::Get()->GetLoggedInUser();
if (logged_in_user) {
task_runner_->PostTask(
FROM_HERE,
base::Bind(&WallpaperManager::MoveCustomWallpapersOnWorker,
logged_in_user->email(),
logged_in_user->username_hash(),
weak_factory_.GetWeakPtr()));
}
}
void WallpaperManager::OnWallpaperDecoded(
const std::string& user_id,
ash::WallpaperLayout layout,
bool update_wallpaper,
MovableOnDestroyCallbackHolder on_finish,
const user_manager::UserImage& user_image) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
TRACE_EVENT_ASYNC_END0("ui", "LoadAndDecodeWallpaper", this);
// If decoded wallpaper is empty, we have probably failed to decode the file.
// Use default wallpaper in this case.
if (user_image.image().isNull()) {
// Updates user pref to default wallpaper.
WallpaperInfo info = {"", ash::WALLPAPER_LAYOUT_CENTER_CROPPED,
user_manager::User::DEFAULT,
base::Time::Now().LocalMidnight()};
SetUserWallpaperInfo(user_id, info, true);
if (update_wallpaper)
DoSetDefaultWallpaper(user_id, on_finish.Pass());
return;
}
wallpaper_cache_[user_id] = user_image.image();
if (update_wallpaper) {
ash::Shell::GetInstance()
->desktop_background_controller()
->SetWallpaperImage(user_image.image(), layout);
}
}
void WallpaperManager::StartLoad(const std::string& user_id,
const WallpaperInfo& info,
bool update_wallpaper,
const base::FilePath& wallpaper_path,
MovableOnDestroyCallbackHolder on_finish) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
TRACE_EVENT_ASYNC_BEGIN0("ui", "LoadAndDecodeWallpaper", this);
wallpaper_loader_->Start(wallpaper_path.value(),
0, // Do not crop.
base::Bind(&WallpaperManager::OnWallpaperDecoded,
weak_factory_.GetWeakPtr(),
user_id,
info.layout,
update_wallpaper,
base::Passed(on_finish.Pass())));
}
void WallpaperManager::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 WallpaperManager::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<base::TimeDelta>()) /
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 WallpaperManager::SetCustomizedDefaultWallpaperAfterCheck(
const GURL& wallpaper_url,
const base::FilePath& downloaded_file,
scoped_ptr<CustomizedWallpaperRescaledFiles> rescaled_files) {
PrefService* pref_service = g_browser_process->local_state();
std::string current_url =
pref_service->GetString(prefs::kCustomizationDefaultWallpaperURL);
if (current_url != wallpaper_url.spec() || !rescaled_files->AllSizesExist()) {
DCHECK(rescaled_files->downloaded_exists());
// Either resized images do not exist or cached version is incorrect.
// Need to start resize again.
wallpaper_loader_->Start(
downloaded_file.value(),
0, // Do not crop.
base::Bind(&WallpaperManager::OnCustomizedDefaultWallpaperDecoded,
weak_factory_.GetWeakPtr(),
wallpaper_url,
base::Passed(rescaled_files.Pass())));
} else {
SetDefaultWallpaperPath(rescaled_files->path_rescaled_small(),
scoped_ptr<gfx::ImageSkia>().Pass(),
rescaled_files->path_rescaled_large(),
scoped_ptr<gfx::ImageSkia>().Pass());
}
}
void WallpaperManager::OnCustomizedDefaultWallpaperDecoded(
const GURL& wallpaper_url,
scoped_ptr<CustomizedWallpaperRescaledFiles> rescaled_files,
const user_manager::UserImage& wallpaper) {
DCHECK(BrowserThread::CurrentlyOn(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<gfx::ImageSkia> deep_copy(wallpaper.image().DeepCopy());
scoped_ptr<bool> success(new bool(false));
scoped_ptr<gfx::ImageSkia> small_wallpaper_image(new gfx::ImageSkia);
scoped_ptr<gfx::ImageSkia> large_wallpaper_image(new gfx::ImageSkia);
// TODO(bshe): This may break if RawImage becomes RefCountedMemory.
base::Closure resize_closure =
base::Bind(&WallpaperManager::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(&WallpaperManager::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 WallpaperManager::ResizeCustomizedDefaultWallpaper(
scoped_ptr<gfx::ImageSkia> 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(),
ash::WALLPAPER_LAYOUT_STRETCH,
kSmallWallpaperMaxWidth,
kSmallWallpaperMaxHeight,
small_wallpaper_image);
*success &= ResizeAndSaveWallpaper(*image,
rescaled_files->path_rescaled_large(),
ash::WALLPAPER_LAYOUT_STRETCH,
kLargeWallpaperMaxWidth,
kLargeWallpaperMaxHeight,
large_wallpaper_image);
}
void WallpaperManager::OnCustomizedDefaultWallpaperResized(
const GURL& wallpaper_url,
scoped_ptr<CustomizedWallpaperRescaledFiles> rescaled_files,
scoped_ptr<bool> success,
scoped_ptr<gfx::ImageSkia> small_wallpaper_image,
scoped_ptr<gfx::ImageSkia> large_wallpaper_image) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(rescaled_files);
DCHECK(success.get());
if (!*success) {
LOG(WARNING) << "Failed to save resized customized default wallpaper";
return;
}
PrefService* pref_service = g_browser_process->local_state();
pref_service->SetString(prefs::kCustomizationDefaultWallpaperURL,
wallpaper_url.spec());
SetDefaultWallpaperPath(rescaled_files->path_rescaled_small(),
small_wallpaper_image.Pass(),
rescaled_files->path_rescaled_large(),
large_wallpaper_image.Pass());
VLOG(1) << "Customized default wallpaper applied.";
}
WallpaperManager::PendingWallpaper* WallpaperManager::GetPendingWallpaper(
const std::string& user_id,
bool delayed) {
if (!pending_inactive_) {
loading_.push_back(new WallpaperManager::PendingWallpaper(
(delayed ? GetWallpaperLoadDelay()
: base::TimeDelta::FromMilliseconds(0)),
user_id));
pending_inactive_ = loading_.back().get();
}
return pending_inactive_;
}
void WallpaperManager::RemovePendingWallpaperFromList(
PendingWallpaper* pending) {
DCHECK(loading_.size() > 0);
for (WallpaperManager::PendingList::iterator i = loading_.begin();
i != loading_.end();
++i) {
if (i->get() == pending) {
loading_.erase(i);
break;
}
}
if (loading_.empty())
FOR_EACH_OBSERVER(Observer, observers_, OnPendingListEmptyForTesting());
}
void WallpaperManager::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<CustomizedWallpaperRescaledFiles> 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(&WallpaperManager::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.";
}
}
size_t WallpaperManager::GetPendingListSizeForTesting() const {
return loading_.size();
}
void WallpaperManager::SetDefaultWallpaperPathsFromCommandLine(
base::CommandLine* command_line) {
default_small_wallpaper_file_ = command_line->GetSwitchValuePath(
ash::switches::kAshDefaultWallpaperSmall);
default_large_wallpaper_file_ = command_line->GetSwitchValuePath(
ash::switches::kAshDefaultWallpaperLarge);
guest_small_wallpaper_file_ =
command_line->GetSwitchValuePath(ash::switches::kAshGuestWallpaperSmall);
guest_large_wallpaper_file_ =
command_line->GetSwitchValuePath(ash::switches::kAshGuestWallpaperLarge);
default_wallpaper_image_.reset();
}
void WallpaperManager::OnDefaultWallpaperDecoded(
const base::FilePath& path,
const ash::WallpaperLayout layout,
scoped_ptr<user_manager::UserImage>* result_out,
MovableOnDestroyCallbackHolder on_finish,
const user_manager::UserImage& user_image) {
result_out->reset(new user_manager::UserImage(user_image));
ash::Shell::GetInstance()->desktop_background_controller()->SetWallpaperImage(
user_image.image(), layout);
}
void WallpaperManager::StartLoadAndSetDefaultWallpaper(
const base::FilePath& path,
const ash::WallpaperLayout layout,
MovableOnDestroyCallbackHolder on_finish,
scoped_ptr<user_manager::UserImage>* result_out) {
wallpaper_loader_->Start(
path.value(),
0, // Do not crop.
base::Bind(&WallpaperManager::OnDefaultWallpaperDecoded,
weak_factory_.GetWeakPtr(),
path,
layout,
base::Unretained(result_out),
base::Passed(on_finish.Pass())));
}
const char* WallpaperManager::GetCustomWallpaperSubdirForCurrentResolution() {
WallpaperResolution resolution = GetAppropriateResolution();
return resolution == WALLPAPER_RESOLUTION_SMALL ? kSmallWallpaperSubDir
: kLargeWallpaperSubDir;
}
void WallpaperManager::SetDefaultWallpaperPath(
const base::FilePath& default_small_wallpaper_file,
scoped_ptr<gfx::ImageSkia> small_wallpaper_image,
const base::FilePath& default_large_wallpaper_file,
scoped_ptr<gfx::ImageSkia> large_wallpaper_image) {
default_small_wallpaper_file_ = default_small_wallpaper_file;
default_large_wallpaper_file_ = default_large_wallpaper_file;
ash::DesktopBackgroundController* dbc =
ash::Shell::GetInstance()->desktop_background_controller();
// |need_update_screen| is true if the previous default wallpaper is visible
// now, so we need to update wallpaper on the screen.
//
// Layout is ignored here, so ash::WALLPAPER_LAYOUT_CENTER is used
// as a placeholder only.
const bool need_update_screen =
default_wallpaper_image_.get() &&
dbc->WallpaperIsAlreadyLoaded(default_wallpaper_image_->image(),
false /* compare_layouts */,
ash::WALLPAPER_LAYOUT_CENTER);
default_wallpaper_image_.reset();
if (GetAppropriateResolution() == WALLPAPER_RESOLUTION_SMALL) {
if (small_wallpaper_image) {
default_wallpaper_image_.reset(
new user_manager::UserImage(*small_wallpaper_image));
default_wallpaper_image_->set_file_path(
default_small_wallpaper_file.value());
}
} else {
if (large_wallpaper_image) {
default_wallpaper_image_.reset(
new user_manager::UserImage(*large_wallpaper_image));
default_wallpaper_image_->set_file_path(
default_large_wallpaper_file.value());
}
}
if (need_update_screen) {
DoSetDefaultWallpaper(std::string(),
MovableOnDestroyCallbackHolder().Pass());
}
}
void WallpaperManager::CreateSolidDefaultWallpaper() {
loaded_wallpapers_++;
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 chromeos