Disable TabDragController tests that fail with a real compositor.
[chromium-blink-merge.git] / ash / desktop_background / desktop_background_controller.cc
blob81174c2db2b08739bf5ce6027ea2485d376ba982
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.
5 #include "ash/desktop_background/desktop_background_controller.h"
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"
36 using ash::internal::DesktopBackgroundWidgetController;
37 using content::BrowserThread;
39 namespace ash {
40 namespace {
42 // How long to wait reloading the wallpaper after the max display has
43 // changed?
44 const int kWallpaperReloadDelayMs = 2000;
46 } // namespace
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;
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) {
73 void LoadOnWorkerPoolThread() {
74 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
75 if (cancel_flag_.IsSet())
76 return;
78 if (!file_path_.empty())
79 file_bitmap_ = LoadSkBitmapFromJPEGFile(file_path_);
81 if (cancel_flag_.IsSet())
82 return;
84 if (file_bitmap_) {
85 gfx::ImageSkia image = gfx::ImageSkia::CreateFrom1xBitmap(*file_bitmap_);
86 wallpaper_resizer_.reset(new WallpaperResizer(
87 image, GetMaxDisplaySizeInNative(), file_layout_));
88 } else {
89 wallpaper_resizer_.reset(new WallpaperResizer(
90 resource_id_, GetMaxDisplaySizeInNative(), resource_layout_));
94 const base::FilePath& file_path() const { return file_path_; }
95 int resource_id() const { return resource_id_; }
97 void Cancel() {
98 cancel_flag_.Set();
101 bool IsCanceled() {
102 return cancel_flag_.IsSet();
105 WallpaperResizer* ReleaseWallpaperResizer() {
106 return wallpaper_resizer_.release();
109 private:
110 friend class base::RefCountedThreadSafe<
111 DesktopBackgroundController::WallpaperLoader>;
113 // Loads a JPEG image from |path|, a trusted file -- note that the image
114 // is not loaded in a sandboxed process. Returns an empty pointer on
115 // error.
116 static scoped_ptr<SkBitmap> LoadSkBitmapFromJPEGFile(
117 const base::FilePath& path) {
118 std::string data;
119 if (!base::ReadFileToString(path, &data)) {
120 LOG(ERROR) << "Unable to read data from " << path.value();
121 return scoped_ptr<SkBitmap>();
124 scoped_ptr<SkBitmap> bitmap(gfx::JPEGCodec::Decode(
125 reinterpret_cast<const unsigned char*>(data.data()), data.size()));
126 if (!bitmap)
127 LOG(ERROR) << "Unable to decode JPEG data from " << path.value();
128 return bitmap.Pass();
131 ~WallpaperLoader() {}
133 base::CancellationFlag cancel_flag_;
135 // Bitmap loaded from |file_path_|.
136 scoped_ptr<SkBitmap> file_bitmap_;
138 scoped_ptr<WallpaperResizer> wallpaper_resizer_;
140 // Path to a trusted JPEG file.
141 base::FilePath file_path_;
143 // Layout to be used when displaying the image from |file_path_|.
144 WallpaperLayout file_layout_;
146 // ID of an image resource to use if |file_path_| is empty or unloadable.
147 int resource_id_;
149 // Layout to be used when displaying |resource_id_|.
150 WallpaperLayout resource_layout_;
152 DISALLOW_COPY_AND_ASSIGN(WallpaperLoader);
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);
165 DesktopBackgroundController::~DesktopBackgroundController() {
166 CancelDefaultWallpaperLoader();
167 Shell::GetInstance()->display_controller()->RemoveObserver(this);
170 gfx::ImageSkia DesktopBackgroundController::GetWallpaper() const {
171 if (current_wallpaper_)
172 return current_wallpaper_->image();
173 return gfx::ImageSkia();
176 void DesktopBackgroundController::AddObserver(
177 DesktopBackgroundControllerObserver* observer) {
178 observers_.AddObserver(observer);
181 void DesktopBackgroundController::RemoveObserver(
182 DesktopBackgroundControllerObserver* observer) {
183 observers_.RemoveObserver(observer);
186 WallpaperLayout DesktopBackgroundController::GetWallpaperLayout() const {
187 if (current_wallpaper_)
188 return current_wallpaper_->layout();
189 return WALLPAPER_LAYOUT_CENTER_CROPPED;
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;
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();
206 InstallDesktopController(root_window);
209 bool DesktopBackgroundController::SetDefaultWallpaper(bool is_guest) {
210 const bool use_large =
211 GetAppropriateResolution() == WALLPAPER_RESOLUTION_LARGE;
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;
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;
230 file_path = command_line->GetSwitchValuePath(switch_name);
232 if (DefaultWallpaperIsAlreadyLoadingOrLoaded(file_path, resource_id))
233 return false;
235 CancelDefaultWallpaperLoader();
236 default_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,
241 default_wallpaper_loader_),
242 base::Bind(&DesktopBackgroundController::OnDefaultWallpaperLoadCompleted,
243 weak_ptr_factory_.GetWeakPtr(),
244 default_wallpaper_loader_),
245 true /* task_is_slow */);
246 return true;
249 void DesktopBackgroundController::SetCustomWallpaper(
250 const gfx::ImageSkia& image,
251 WallpaperLayout layout) {
252 CancelDefaultWallpaperLoader();
254 if (CustomWallpaperIsAlreadyLoaded(image))
255 return;
257 current_wallpaper_.reset(new WallpaperResizer(
258 image, GetMaxDisplaySizeInNative(), layout));
259 current_wallpaper_->StartResize();
261 current_default_wallpaper_path_ = base::FilePath();
262 current_default_wallpaper_resource_id_ = -1;
264 FOR_EACH_OBSERVER(DesktopBackgroundControllerObserver, observers_,
265 OnWallpaperDataChanged());
266 SetDesktopBackgroundImageMode();
269 void DesktopBackgroundController::CancelDefaultWallpaperLoader() {
270 // Set canceled flag of previous request to skip unneeded loading.
271 if (default_wallpaper_loader_.get())
272 default_wallpaper_loader_->Cancel();
274 // Cancel reply callback for previous request.
275 weak_ptr_factory_.InvalidateWeakPtrs();
278 void DesktopBackgroundController::CreateEmptyWallpaper() {
279 current_wallpaper_.reset(NULL);
280 SetDesktopBackgroundImageMode();
283 WallpaperResolution DesktopBackgroundController::GetAppropriateResolution() {
284 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
285 gfx::Size size = GetMaxDisplaySizeInNative();
286 return (size.width() > kSmallWallpaperMaxWidth ||
287 size.height() > kSmallWallpaperMaxHeight) ?
288 WALLPAPER_RESOLUTION_LARGE : WALLPAPER_RESOLUTION_SMALL;
291 bool DesktopBackgroundController::MoveDesktopToLockedContainer() {
292 if (locked_)
293 return false;
294 locked_ = true;
295 return ReparentBackgroundWidgets(GetBackgroundContainerId(false),
296 GetBackgroundContainerId(true));
299 bool DesktopBackgroundController::MoveDesktopToUnlockedContainer() {
300 if (!locked_)
301 return false;
302 locked_ = false;
303 return ReparentBackgroundWidgets(GetBackgroundContainerId(true),
304 GetBackgroundContainerId(false));
307 void DesktopBackgroundController::OnDisplayConfigurationChanged() {
308 gfx::Size max_display_size = GetMaxDisplaySizeInNative();
309 if (current_max_display_size_ != max_display_size) {
310 current_max_display_size_ = max_display_size;
311 if (desktop_background_mode_ == BACKGROUND_IMAGE &&
312 current_wallpaper_.get()) {
313 timer_.Stop();
314 timer_.Start(FROM_HERE,
315 base::TimeDelta::FromMilliseconds(wallpaper_reload_delay_),
316 this,
317 &DesktopBackgroundController::UpdateWallpaper);
322 bool DesktopBackgroundController::DefaultWallpaperIsAlreadyLoadingOrLoaded(
323 const base::FilePath& image_file,
324 int image_resource_id) const {
325 return (default_wallpaper_loader_.get() &&
326 !default_wallpaper_loader_->IsCanceled() &&
327 default_wallpaper_loader_->file_path() == image_file &&
328 default_wallpaper_loader_->resource_id() == image_resource_id) ||
329 (current_wallpaper_.get() &&
330 current_default_wallpaper_path_ == image_file &&
331 current_default_wallpaper_resource_id_ == image_resource_id);
334 bool DesktopBackgroundController::CustomWallpaperIsAlreadyLoaded(
335 const gfx::ImageSkia& image) const {
336 return current_wallpaper_.get() &&
337 (WallpaperResizer::GetImageId(image) ==
338 current_wallpaper_->original_image_id());
341 void DesktopBackgroundController::SetDesktopBackgroundImageMode() {
342 desktop_background_mode_ = BACKGROUND_IMAGE;
343 InstallDesktopControllerForAllWindows();
346 void DesktopBackgroundController::OnDefaultWallpaperLoadCompleted(
347 scoped_refptr<WallpaperLoader> loader) {
348 current_wallpaper_.reset(loader->ReleaseWallpaperResizer());
349 current_wallpaper_->StartResize();
350 current_default_wallpaper_path_ = loader->file_path();
351 current_default_wallpaper_resource_id_ = loader->resource_id();
352 FOR_EACH_OBSERVER(DesktopBackgroundControllerObserver, observers_,
353 OnWallpaperDataChanged());
355 SetDesktopBackgroundImageMode();
357 DCHECK(loader.get() == default_wallpaper_loader_.get());
358 default_wallpaper_loader_ = NULL;
361 void DesktopBackgroundController::InstallDesktopController(
362 aura::Window* root_window) {
363 internal::DesktopBackgroundWidgetController* component = NULL;
364 int container_id = GetBackgroundContainerId(locked_);
366 switch (desktop_background_mode_) {
367 case BACKGROUND_IMAGE: {
368 views::Widget* widget = internal::CreateDesktopBackground(root_window,
369 container_id);
370 component = new internal::DesktopBackgroundWidgetController(widget);
371 break;
373 case BACKGROUND_NONE:
374 NOTREACHED();
375 return;
377 internal::GetRootWindowController(root_window)->
378 SetAnimatingWallpaperController(
379 new internal::AnimatingDesktopController(component));
381 component->StartAnimating(internal::GetRootWindowController(root_window));
384 void DesktopBackgroundController::InstallDesktopControllerForAllWindows() {
385 aura::Window::Windows root_windows = Shell::GetAllRootWindows();
386 for (aura::Window::Windows::iterator iter = root_windows.begin();
387 iter != root_windows.end(); ++iter) {
388 InstallDesktopController(*iter);
390 current_max_display_size_ = GetMaxDisplaySizeInNative();
393 bool DesktopBackgroundController::ReparentBackgroundWidgets(int src_container,
394 int dst_container) {
395 bool moved = false;
396 Shell::RootWindowControllerList controllers =
397 Shell::GetAllRootWindowControllers();
398 for (Shell::RootWindowControllerList::iterator iter = controllers.begin();
399 iter != controllers.end(); ++iter) {
400 internal::RootWindowController* root_window_controller = *iter;
401 // In the steady state (no animation playing) the background widget
402 // controller exists in the RootWindowController.
403 DesktopBackgroundWidgetController* desktop_controller =
404 root_window_controller->wallpaper_controller();
405 if (desktop_controller) {
406 moved |= desktop_controller->Reparent(
407 root_window_controller->root_window(),
408 src_container,
409 dst_container);
411 // During desktop show animations the controller lives in
412 // AnimatingDesktopController owned by RootWindowController.
413 // NOTE: If a wallpaper load happens during a desktop show animation there
414 // can temporarily be two desktop background widgets. We must reparent
415 // both of them - one above and one here.
416 DesktopBackgroundWidgetController* animating_controller =
417 root_window_controller->animating_wallpaper_controller() ?
418 root_window_controller->animating_wallpaper_controller()->
419 GetController(false) :
420 NULL;
421 if (animating_controller) {
422 moved |= animating_controller->Reparent(
423 root_window_controller->root_window(),
424 src_container,
425 dst_container);
428 return moved;
431 int DesktopBackgroundController::GetBackgroundContainerId(bool locked) {
432 return locked ? internal::kShellWindowId_LockScreenBackgroundContainer :
433 internal::kShellWindowId_DesktopBackgroundContainer;
436 void DesktopBackgroundController::UpdateWallpaper() {
437 current_wallpaper_.reset(NULL);
438 current_default_wallpaper_path_ = base::FilePath();
439 current_default_wallpaper_resource_id_ = -1;
440 ash::Shell::GetInstance()->user_wallpaper_delegate()->
441 UpdateWallpaper();
444 // static
445 gfx::Size DesktopBackgroundController::GetMaxDisplaySizeInNative() {
446 int width = 0;
447 int height = 0;
448 std::vector<gfx::Display> displays = Shell::GetScreen()->GetAllDisplays();
449 internal::DisplayManager* display_manager =
450 Shell::GetInstance()->display_manager();
452 for (std::vector<gfx::Display>::iterator iter = displays.begin();
453 iter != displays.end(); ++iter) {
454 // Don't use size_in_pixel because we want to use the native pixel size.
455 gfx::Size size_in_pixel =
456 display_manager->GetDisplayInfo(iter->id()).bounds_in_native().size();
457 if (iter->rotation() == gfx::Display::ROTATE_90 ||
458 iter->rotation() == gfx::Display::ROTATE_270) {
459 size_in_pixel = gfx::Size(size_in_pixel.height(), size_in_pixel.width());
461 width = std::max(size_in_pixel.width(), width);
462 height = std::max(size_in_pixel.height(), height);
464 return gfx::Size(width, height);
467 } // namespace ash