Home | History | Annotate | Download | only in desktop_background
      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 
     36 using ash::internal::DesktopBackgroundWidgetController;
     37 using content::BrowserThread;
     38 
     39 namespace ash {
     40 namespace {
     41 
     42 // How long to wait reloading the wallpaper after the max display has
     43 // changed?
     44 const int kWallpaperReloadDelayMs = 2000;
     45 
     46 }  // namespace
     47 
     48 const int kSmallWallpaperMaxWidth = 1366;
     49 const int kSmallWallpaperMaxHeight = 800;
     50 const int kLargeWallpaperMaxWidth = 2560;
     51 const int kLargeWallpaperMaxHeight = 1700;
     52 const int kWallpaperThumbnailWidth = 108;
     53 const int kWallpaperThumbnailHeight = 68;
     54 
     55 // DesktopBackgroundController::WallpaperLoader wraps background wallpaper
     56 // loading.
     57 class 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 
    155 DesktopBackgroundController::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 
    165 DesktopBackgroundController::~DesktopBackgroundController() {
    166   CancelPendingWallpaperOperation();
    167   Shell::GetInstance()->display_controller()->RemoveObserver(this);
    168 }
    169 
    170 gfx::ImageSkia DesktopBackgroundController::GetWallpaper() const {
    171   if (current_wallpaper_)
    172     return current_wallpaper_->image();
    173   return gfx::ImageSkia();
    174 }
    175 
    176 void DesktopBackgroundController::AddObserver(
    177     DesktopBackgroundControllerObserver* observer) {
    178   observers_.AddObserver(observer);
    179 }
    180 
    181 void DesktopBackgroundController::RemoveObserver(
    182     DesktopBackgroundControllerObserver* observer) {
    183   observers_.RemoveObserver(observer);
    184 }
    185 
    186 WallpaperLayout DesktopBackgroundController::GetWallpaperLayout() const {
    187   if (current_wallpaper_)
    188     return current_wallpaper_->layout();
    189   return WALLPAPER_LAYOUT_CENTER_CROPPED;
    190 }
    191 
    192 void 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 
    209 bool 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 
    248 void 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 
    267 void 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 
    276 void DesktopBackgroundController::CreateEmptyWallpaper() {
    277   current_wallpaper_.reset(NULL);
    278   SetDesktopBackgroundImageMode();
    279 }
    280 
    281 WallpaperResolution 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 
    289 bool DesktopBackgroundController::MoveDesktopToLockedContainer() {
    290   if (locked_)
    291     return false;
    292   locked_ = true;
    293   return ReparentBackgroundWidgets(GetBackgroundContainerId(false),
    294                                    GetBackgroundContainerId(true));
    295 }
    296 
    297 bool DesktopBackgroundController::MoveDesktopToUnlockedContainer() {
    298   if (!locked_)
    299     return false;
    300   locked_ = false;
    301   return ReparentBackgroundWidgets(GetBackgroundContainerId(true),
    302                                    GetBackgroundContainerId(false));
    303 }
    304 
    305 void 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 
    320 bool 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 
    330 bool 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 
    337 void DesktopBackgroundController::SetDesktopBackgroundImageMode() {
    338   desktop_background_mode_ = BACKGROUND_IMAGE;
    339   InstallDesktopControllerForAllWindows();
    340 }
    341 
    342 void 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 
    357 void 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 
    380 void 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 
    389 bool 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 
    427 int DesktopBackgroundController::GetBackgroundContainerId(bool locked) {
    428   return locked ? internal::kShellWindowId_LockScreenBackgroundContainer :
    429                   internal::kShellWindowId_DesktopBackgroundContainer;
    430 }
    431 
    432 void 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
    441 gfx::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