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