1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "ash/desktop_background/desktop_background_controller.h" 6 7#include "ash/ash_switches.h" 8#include "ash/desktop_background/desktop_background_controller_observer.h" 9#include "ash/desktop_background/desktop_background_view.h" 10#include "ash/desktop_background/desktop_background_widget_controller.h" 11#include "ash/desktop_background/user_wallpaper_delegate.h" 12#include "ash/desktop_background/wallpaper_resizer.h" 13#include "ash/display/display_info.h" 14#include "ash/display/display_manager.h" 15#include "ash/root_window_controller.h" 16#include "ash/shell.h" 17#include "ash/shell_factory.h" 18#include "ash/shell_window_ids.h" 19#include "ash/wm/root_window_layout_manager.h" 20#include "base/bind.h" 21#include "base/command_line.h" 22#include "base/file_util.h" 23#include "base/logging.h" 24#include "base/synchronization/cancellation_flag.h" 25#include "base/threading/worker_pool.h" 26#include "content/public/browser/browser_thread.h" 27#include "grit/ash_resources.h" 28#include "ui/aura/root_window.h" 29#include "ui/aura/window.h" 30#include "ui/compositor/layer.h" 31#include "ui/gfx/codec/jpeg_codec.h" 32#include "ui/gfx/image/image_skia.h" 33#include "ui/gfx/rect.h" 34#include "ui/views/widget/widget.h" 35 36using ash::internal::DesktopBackgroundWidgetController; 37using content::BrowserThread; 38 39namespace ash { 40namespace { 41 42// How long to wait reloading the wallpaper after the max display has 43// changed? 44const int kWallpaperReloadDelayMs = 2000; 45 46} // namespace 47 48const int kSmallWallpaperMaxWidth = 1366; 49const int kSmallWallpaperMaxHeight = 800; 50const int kLargeWallpaperMaxWidth = 2560; 51const int kLargeWallpaperMaxHeight = 1700; 52const int kWallpaperThumbnailWidth = 108; 53const int kWallpaperThumbnailHeight = 68; 54 55// DesktopBackgroundController::WallpaperLoader wraps background wallpaper 56// loading. 57class DesktopBackgroundController::WallpaperLoader 58 : public base::RefCountedThreadSafe< 59 DesktopBackgroundController::WallpaperLoader> { 60 public: 61 // If set, |file_path| must be a trusted (i.e. read-only, 62 // non-user-controlled) file containing a JPEG image. 63 WallpaperLoader(const base::FilePath& file_path, 64 WallpaperLayout file_layout, 65 int resource_id, 66 WallpaperLayout resource_layout) 67 : file_path_(file_path), 68 file_layout_(file_layout), 69 resource_id_(resource_id), 70 resource_layout_(resource_layout) { 71 } 72 73 static void LoadOnWorkerPoolThread(scoped_refptr<WallpaperLoader> loader) { 74 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); 75 loader->LoadWallpaper(); 76 } 77 78 const base::FilePath& file_path() const { return file_path_; } 79 int resource_id() const { return resource_id_; } 80 81 void Cancel() { 82 cancel_flag_.Set(); 83 } 84 85 WallpaperResizer* ReleaseWallpaperResizer() { 86 return wallpaper_resizer_.release(); 87 } 88 89 private: 90 friend class base::RefCountedThreadSafe< 91 DesktopBackgroundController::WallpaperLoader>; 92 93 // Loads a JPEG image from |path|, a trusted file -- note that the image 94 // is not loaded in a sandboxed process. Returns an empty pointer on 95 // error. 96 static scoped_ptr<SkBitmap> LoadSkBitmapFromJPEGFile( 97 const base::FilePath& path) { 98 std::string data; 99 if (!base::ReadFileToString(path, &data)) { 100 LOG(ERROR) << "Unable to read data from " << path.value(); 101 return scoped_ptr<SkBitmap>(); 102 } 103 104 scoped_ptr<SkBitmap> bitmap(gfx::JPEGCodec::Decode( 105 reinterpret_cast<const unsigned char*>(data.data()), data.size())); 106 if (!bitmap) 107 LOG(ERROR) << "Unable to decode JPEG data from " << path.value(); 108 return bitmap.Pass(); 109 } 110 111 void LoadWallpaper() { 112 if (cancel_flag_.IsSet()) 113 return; 114 115 if (!file_path_.empty()) 116 file_bitmap_ = LoadSkBitmapFromJPEGFile(file_path_); 117 118 if (cancel_flag_.IsSet()) 119 return; 120 121 if (file_bitmap_) { 122 gfx::ImageSkia image = gfx::ImageSkia::CreateFrom1xBitmap(*file_bitmap_); 123 wallpaper_resizer_.reset(new WallpaperResizer( 124 image, GetMaxDisplaySizeInNative(), file_layout_)); 125 } else { 126 wallpaper_resizer_.reset(new WallpaperResizer( 127 resource_id_, GetMaxDisplaySizeInNative(), resource_layout_)); 128 } 129 } 130 131 ~WallpaperLoader() {} 132 133 base::CancellationFlag cancel_flag_; 134 135 // Bitmap loaded from |file_path_|. 136 scoped_ptr<SkBitmap> file_bitmap_; 137 138 scoped_ptr<WallpaperResizer> wallpaper_resizer_; 139 140 // Path to a trusted JPEG file. 141 base::FilePath file_path_; 142 143 // Layout to be used when displaying the image from |file_path_|. 144 WallpaperLayout file_layout_; 145 146 // ID of an image resource to use if |file_path_| is empty or unloadable. 147 int resource_id_; 148 149 // Layout to be used when displaying |resource_id_|. 150 WallpaperLayout resource_layout_; 151 152 DISALLOW_COPY_AND_ASSIGN(WallpaperLoader); 153}; 154 155DesktopBackgroundController::DesktopBackgroundController() 156 : command_line_for_testing_(NULL), 157 locked_(false), 158 desktop_background_mode_(BACKGROUND_NONE), 159 current_default_wallpaper_resource_id_(-1), 160 weak_ptr_factory_(this), 161 wallpaper_reload_delay_(kWallpaperReloadDelayMs) { 162 Shell::GetInstance()->display_controller()->AddObserver(this); 163} 164 165DesktopBackgroundController::~DesktopBackgroundController() { 166 CancelPendingWallpaperOperation(); 167 Shell::GetInstance()->display_controller()->RemoveObserver(this); 168} 169 170gfx::ImageSkia DesktopBackgroundController::GetWallpaper() const { 171 if (current_wallpaper_) 172 return current_wallpaper_->image(); 173 return gfx::ImageSkia(); 174} 175 176void DesktopBackgroundController::AddObserver( 177 DesktopBackgroundControllerObserver* observer) { 178 observers_.AddObserver(observer); 179} 180 181void DesktopBackgroundController::RemoveObserver( 182 DesktopBackgroundControllerObserver* observer) { 183 observers_.RemoveObserver(observer); 184} 185 186WallpaperLayout DesktopBackgroundController::GetWallpaperLayout() const { 187 if (current_wallpaper_) 188 return current_wallpaper_->layout(); 189 return WALLPAPER_LAYOUT_CENTER_CROPPED; 190} 191 192void DesktopBackgroundController::OnRootWindowAdded(aura::Window* root_window) { 193 // The background hasn't been set yet. 194 if (desktop_background_mode_ == BACKGROUND_NONE) 195 return; 196 197 // Handle resolution change for "built-in" images. 198 gfx::Size max_display_size = GetMaxDisplaySizeInNative(); 199 if (current_max_display_size_ != max_display_size) { 200 current_max_display_size_ = max_display_size; 201 if (desktop_background_mode_ == BACKGROUND_IMAGE && 202 current_wallpaper_.get()) 203 UpdateWallpaper(); 204 } 205 206 InstallDesktopController(root_window); 207} 208 209bool DesktopBackgroundController::SetDefaultWallpaper(bool is_guest) { 210 const bool use_large = 211 GetAppropriateResolution() == WALLPAPER_RESOLUTION_LARGE; 212 213 base::FilePath file_path; 214 WallpaperLayout file_layout = use_large ? WALLPAPER_LAYOUT_CENTER_CROPPED : 215 WALLPAPER_LAYOUT_CENTER; 216 int resource_id = use_large ? IDR_AURA_WALLPAPER_DEFAULT_LARGE : 217 IDR_AURA_WALLPAPER_DEFAULT_SMALL; 218 WallpaperLayout resource_layout = WALLPAPER_LAYOUT_TILE; 219 220 CommandLine* command_line = command_line_for_testing_ ? 221 command_line_for_testing_ : CommandLine::ForCurrentProcess(); 222 const char* switch_name = NULL; 223 if (is_guest) { 224 switch_name = use_large ? switches::kAshGuestWallpaperLarge : 225 switches::kAshGuestWallpaperSmall; 226 } else { 227 switch_name = use_large ? switches::kAshDefaultWallpaperLarge : 228 switches::kAshDefaultWallpaperSmall; 229 } 230 file_path = command_line->GetSwitchValuePath(switch_name); 231 232 if (DefaultWallpaperIsAlreadyLoadingOrLoaded(file_path, resource_id)) 233 return false; 234 235 CancelPendingWallpaperOperation(); 236 wallpaper_loader_ = new WallpaperLoader( 237 file_path, file_layout, resource_id, resource_layout); 238 base::WorkerPool::PostTaskAndReply( 239 FROM_HERE, 240 base::Bind(&WallpaperLoader::LoadOnWorkerPoolThread, wallpaper_loader_), 241 base::Bind(&DesktopBackgroundController::OnDefaultWallpaperLoadCompleted, 242 weak_ptr_factory_.GetWeakPtr(), 243 wallpaper_loader_), 244 true /* task_is_slow */); 245 return true; 246} 247 248void DesktopBackgroundController::SetCustomWallpaper( 249 const gfx::ImageSkia& image, 250 WallpaperLayout layout) { 251 CancelPendingWallpaperOperation(); 252 if (CustomWallpaperIsAlreadyLoaded(image)) 253 return; 254 255 current_wallpaper_.reset(new WallpaperResizer( 256 image, GetMaxDisplaySizeInNative(), layout)); 257 current_wallpaper_->StartResize(); 258 259 current_default_wallpaper_path_ = base::FilePath(); 260 current_default_wallpaper_resource_id_ = -1; 261 262 FOR_EACH_OBSERVER(DesktopBackgroundControllerObserver, observers_, 263 OnWallpaperDataChanged()); 264 SetDesktopBackgroundImageMode(); 265} 266 267void DesktopBackgroundController::CancelPendingWallpaperOperation() { 268 // Set canceled flag of previous request to skip unneeded loading. 269 if (wallpaper_loader_.get()) 270 wallpaper_loader_->Cancel(); 271 272 // Cancel reply callback for previous request. 273 weak_ptr_factory_.InvalidateWeakPtrs(); 274} 275 276void DesktopBackgroundController::CreateEmptyWallpaper() { 277 current_wallpaper_.reset(NULL); 278 SetDesktopBackgroundImageMode(); 279} 280 281WallpaperResolution DesktopBackgroundController::GetAppropriateResolution() { 282 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 283 gfx::Size size = GetMaxDisplaySizeInNative(); 284 return (size.width() > kSmallWallpaperMaxWidth || 285 size.height() > kSmallWallpaperMaxHeight) ? 286 WALLPAPER_RESOLUTION_LARGE : WALLPAPER_RESOLUTION_SMALL; 287} 288 289bool DesktopBackgroundController::MoveDesktopToLockedContainer() { 290 if (locked_) 291 return false; 292 locked_ = true; 293 return ReparentBackgroundWidgets(GetBackgroundContainerId(false), 294 GetBackgroundContainerId(true)); 295} 296 297bool DesktopBackgroundController::MoveDesktopToUnlockedContainer() { 298 if (!locked_) 299 return false; 300 locked_ = false; 301 return ReparentBackgroundWidgets(GetBackgroundContainerId(true), 302 GetBackgroundContainerId(false)); 303} 304 305void DesktopBackgroundController::OnDisplayConfigurationChanged() { 306 gfx::Size max_display_size = GetMaxDisplaySizeInNative(); 307 if (current_max_display_size_ != max_display_size) { 308 current_max_display_size_ = max_display_size; 309 if (desktop_background_mode_ == BACKGROUND_IMAGE && 310 current_wallpaper_.get()) { 311 timer_.Stop(); 312 timer_.Start(FROM_HERE, 313 base::TimeDelta::FromMilliseconds(wallpaper_reload_delay_), 314 this, 315 &DesktopBackgroundController::UpdateWallpaper); 316 } 317 } 318} 319 320bool DesktopBackgroundController::DefaultWallpaperIsAlreadyLoadingOrLoaded( 321 const base::FilePath& image_file, int image_resource_id) const { 322 return (wallpaper_loader_.get() && 323 wallpaper_loader_->file_path() == image_file && 324 wallpaper_loader_->resource_id() == image_resource_id) || 325 (current_wallpaper_.get() && 326 current_default_wallpaper_path_ == image_file && 327 current_default_wallpaper_resource_id_ == image_resource_id); 328} 329 330bool DesktopBackgroundController::CustomWallpaperIsAlreadyLoaded( 331 const gfx::ImageSkia& image) const { 332 return current_wallpaper_.get() && 333 (WallpaperResizer::GetImageId(image) == 334 current_wallpaper_->original_image_id()); 335} 336 337void DesktopBackgroundController::SetDesktopBackgroundImageMode() { 338 desktop_background_mode_ = BACKGROUND_IMAGE; 339 InstallDesktopControllerForAllWindows(); 340} 341 342void DesktopBackgroundController::OnDefaultWallpaperLoadCompleted( 343 scoped_refptr<WallpaperLoader> loader) { 344 current_wallpaper_.reset(loader->ReleaseWallpaperResizer()); 345 current_wallpaper_->StartResize(); 346 current_default_wallpaper_path_ = loader->file_path(); 347 current_default_wallpaper_resource_id_ = loader->resource_id(); 348 FOR_EACH_OBSERVER(DesktopBackgroundControllerObserver, observers_, 349 OnWallpaperDataChanged()); 350 351 SetDesktopBackgroundImageMode(); 352 353 DCHECK(loader.get() == wallpaper_loader_.get()); 354 wallpaper_loader_ = NULL; 355} 356 357void DesktopBackgroundController::InstallDesktopController( 358 aura::Window* root_window) { 359 internal::DesktopBackgroundWidgetController* component = NULL; 360 int container_id = GetBackgroundContainerId(locked_); 361 362 switch (desktop_background_mode_) { 363 case BACKGROUND_IMAGE: { 364 views::Widget* widget = internal::CreateDesktopBackground(root_window, 365 container_id); 366 component = new internal::DesktopBackgroundWidgetController(widget); 367 break; 368 } 369 case BACKGROUND_NONE: 370 NOTREACHED(); 371 return; 372 } 373 internal::GetRootWindowController(root_window)-> 374 SetAnimatingWallpaperController( 375 new internal::AnimatingDesktopController(component)); 376 377 component->StartAnimating(internal::GetRootWindowController(root_window)); 378} 379 380void DesktopBackgroundController::InstallDesktopControllerForAllWindows() { 381 aura::Window::Windows root_windows = Shell::GetAllRootWindows(); 382 for (aura::Window::Windows::iterator iter = root_windows.begin(); 383 iter != root_windows.end(); ++iter) { 384 InstallDesktopController(*iter); 385 } 386 current_max_display_size_ = GetMaxDisplaySizeInNative(); 387} 388 389bool DesktopBackgroundController::ReparentBackgroundWidgets(int src_container, 390 int dst_container) { 391 bool moved = false; 392 Shell::RootWindowControllerList controllers = 393 Shell::GetAllRootWindowControllers(); 394 for (Shell::RootWindowControllerList::iterator iter = controllers.begin(); 395 iter != controllers.end(); ++iter) { 396 internal::RootWindowController* root_window_controller = *iter; 397 // In the steady state (no animation playing) the background widget 398 // controller exists in the RootWindowController. 399 DesktopBackgroundWidgetController* desktop_controller = 400 root_window_controller->wallpaper_controller(); 401 if (desktop_controller) { 402 moved |= desktop_controller->Reparent( 403 root_window_controller->root_window(), 404 src_container, 405 dst_container); 406 } 407 // During desktop show animations the controller lives in 408 // AnimatingDesktopController owned by RootWindowController. 409 // NOTE: If a wallpaper load happens during a desktop show animation there 410 // can temporarily be two desktop background widgets. We must reparent 411 // both of them - one above and one here. 412 DesktopBackgroundWidgetController* animating_controller = 413 root_window_controller->animating_wallpaper_controller() ? 414 root_window_controller->animating_wallpaper_controller()-> 415 GetController(false) : 416 NULL; 417 if (animating_controller) { 418 moved |= animating_controller->Reparent( 419 root_window_controller->root_window(), 420 src_container, 421 dst_container); 422 } 423 } 424 return moved; 425} 426 427int DesktopBackgroundController::GetBackgroundContainerId(bool locked) { 428 return locked ? internal::kShellWindowId_LockScreenBackgroundContainer : 429 internal::kShellWindowId_DesktopBackgroundContainer; 430} 431 432void DesktopBackgroundController::UpdateWallpaper() { 433 current_wallpaper_.reset(NULL); 434 current_default_wallpaper_path_ = base::FilePath(); 435 current_default_wallpaper_resource_id_ = -1; 436 ash::Shell::GetInstance()->user_wallpaper_delegate()-> 437 UpdateWallpaper(); 438} 439 440// static 441gfx::Size DesktopBackgroundController::GetMaxDisplaySizeInNative() { 442 int width = 0; 443 int height = 0; 444 std::vector<gfx::Display> displays = Shell::GetScreen()->GetAllDisplays(); 445 internal::DisplayManager* display_manager = 446 Shell::GetInstance()->display_manager(); 447 448 for (std::vector<gfx::Display>::iterator iter = displays.begin(); 449 iter != displays.end(); ++iter) { 450 // Don't use size_in_pixel because we want to use the native pixel size. 451 gfx::Size size_in_pixel = 452 display_manager->GetDisplayInfo(iter->id()).bounds_in_native().size(); 453 if (iter->rotation() == gfx::Display::ROTATE_90 || 454 iter->rotation() == gfx::Display::ROTATE_270) { 455 size_in_pixel = gfx::Size(size_in_pixel.height(), size_in_pixel.width()); 456 } 457 width = std::max(size_in_pixel.width(), width); 458 height = std::max(size_in_pixel.height(), height); 459 } 460 return gfx::Size(width, height); 461} 462 463} // namespace ash 464