• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
WallpaperLoader(const base::FilePath & file_path,WallpaperLayout file_layout,int resource_id,WallpaperLayout resource_layout)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 
LoadOnWorkerPoolThread(scoped_refptr<WallpaperLoader> loader)73   static void LoadOnWorkerPoolThread(scoped_refptr<WallpaperLoader> loader) {
74     DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
75     loader->LoadWallpaper();
76   }
77 
file_path() const78   const base::FilePath& file_path() const { return file_path_; }
resource_id() const79   int resource_id() const { return resource_id_; }
80 
Cancel()81   void Cancel() {
82     cancel_flag_.Set();
83   }
84 
ReleaseWallpaperResizer()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.
LoadSkBitmapFromJPEGFile(const base::FilePath & path)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 
LoadWallpaper()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 
~WallpaperLoader()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 
DesktopBackgroundController()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 
~DesktopBackgroundController()165 DesktopBackgroundController::~DesktopBackgroundController() {
166   CancelPendingWallpaperOperation();
167   Shell::GetInstance()->display_controller()->RemoveObserver(this);
168 }
169 
GetWallpaper() const170 gfx::ImageSkia DesktopBackgroundController::GetWallpaper() const {
171   if (current_wallpaper_)
172     return current_wallpaper_->image();
173   return gfx::ImageSkia();
174 }
175 
AddObserver(DesktopBackgroundControllerObserver * observer)176 void DesktopBackgroundController::AddObserver(
177     DesktopBackgroundControllerObserver* observer) {
178   observers_.AddObserver(observer);
179 }
180 
RemoveObserver(DesktopBackgroundControllerObserver * observer)181 void DesktopBackgroundController::RemoveObserver(
182     DesktopBackgroundControllerObserver* observer) {
183   observers_.RemoveObserver(observer);
184 }
185 
GetWallpaperLayout() const186 WallpaperLayout DesktopBackgroundController::GetWallpaperLayout() const {
187   if (current_wallpaper_)
188     return current_wallpaper_->layout();
189   return WALLPAPER_LAYOUT_CENTER_CROPPED;
190 }
191 
OnRootWindowAdded(aura::Window * root_window)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 
SetDefaultWallpaper(bool is_guest)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 
SetCustomWallpaper(const gfx::ImageSkia & image,WallpaperLayout layout)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 
CancelPendingWallpaperOperation()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 
CreateEmptyWallpaper()276 void DesktopBackgroundController::CreateEmptyWallpaper() {
277   current_wallpaper_.reset(NULL);
278   SetDesktopBackgroundImageMode();
279 }
280 
GetAppropriateResolution()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 
MoveDesktopToLockedContainer()289 bool DesktopBackgroundController::MoveDesktopToLockedContainer() {
290   if (locked_)
291     return false;
292   locked_ = true;
293   return ReparentBackgroundWidgets(GetBackgroundContainerId(false),
294                                    GetBackgroundContainerId(true));
295 }
296 
MoveDesktopToUnlockedContainer()297 bool DesktopBackgroundController::MoveDesktopToUnlockedContainer() {
298   if (!locked_)
299     return false;
300   locked_ = false;
301   return ReparentBackgroundWidgets(GetBackgroundContainerId(true),
302                                    GetBackgroundContainerId(false));
303 }
304 
OnDisplayConfigurationChanged()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 
DefaultWallpaperIsAlreadyLoadingOrLoaded(const base::FilePath & image_file,int image_resource_id) const320 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 
CustomWallpaperIsAlreadyLoaded(const gfx::ImageSkia & image) const330 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 
SetDesktopBackgroundImageMode()337 void DesktopBackgroundController::SetDesktopBackgroundImageMode() {
338   desktop_background_mode_ = BACKGROUND_IMAGE;
339   InstallDesktopControllerForAllWindows();
340 }
341 
OnDefaultWallpaperLoadCompleted(scoped_refptr<WallpaperLoader> loader)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 
InstallDesktopController(aura::Window * root_window)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 
InstallDesktopControllerForAllWindows()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 
ReparentBackgroundWidgets(int src_container,int dst_container)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 
GetBackgroundContainerId(bool locked)427 int DesktopBackgroundController::GetBackgroundContainerId(bool locked) {
428   return locked ? internal::kShellWindowId_LockScreenBackgroundContainer :
429                   internal::kShellWindowId_DesktopBackgroundContainer;
430 }
431 
UpdateWallpaper()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
GetMaxDisplaySizeInNative()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