root/ash/root_window_controller.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. CreateContainer
  2. ToRelativeValue
  3. MoveOriginRelativeToSize
  4. ReparentWindow
  5. ReparentAllWindows
  6. SetUsesScreenCoordinates
  7. DescendantShouldStayInSameRootWindow
  8. SetUsesEasyResizeTargeter
  9. GetMinimumSize
  10. GetMaximumSize
  11. OnBoundsChanged
  12. GetCursor
  13. GetNonClientComponent
  14. ShouldDescendIntoChildForEventHandling
  15. CanFocus
  16. OnCaptureLost
  17. OnPaint
  18. OnDeviceScaleFactorChanged
  19. OnWindowDestroying
  20. OnWindowDestroyed
  21. OnWindowTargetVisibilityChanged
  22. HasHitTestMask
  23. GetHitTestMask
  24. CreateForPrimaryDisplay
  25. CreateForSecondaryDisplay
  26. CreateForVirtualKeyboardDisplay
  27. ForShelf
  28. ForWindow
  29. ForTargetRootWindow
  30. GetContainerForWindow
  31. SetWallpaperController
  32. SetAnimatingWallpaperController
  33. Shutdown
  34. GetSystemModalLayoutManager
  35. GetContainer
  36. GetContainer
  37. ShowShelf
  38. OnShelfCreated
  39. UpdateAfterLoginStatusChange
  40. HandleInitialDesktopBackgroundAnimationStarted
  41. OnWallpaperAnimationFinished
  42. CloseChildWindows
  43. MoveWindowsTo
  44. GetShelfLayoutManager
  45. GetSystemTray
  46. ShowContextMenu
  47. UpdateShelfVisibility
  48. GetWindowForFullscreenMode
  49. ActivateKeyboard
  50. DeactivateKeyboard
  51. IsVirtualKeyboardWindow
  52. touch_hud_projection_
  53. Init
  54. InitLayoutManagers
  55. InitTouchHuds
  56. CreateSystemBackground
  57. CreateContainersInRootWindow
  58. EnableTouchHudProjection
  59. DisableTouchHudProjection
  60. OnLoginStateChanged
  61. OnTouchHudProjectionToggled
  62. GetRootWindowController

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/root_window_controller.h"

#include <queue>
#include <vector>

#include "ash/ash_constants.h"
#include "ash/ash_switches.h"
#include "ash/desktop_background/desktop_background_controller.h"
#include "ash/desktop_background/desktop_background_widget_controller.h"
#include "ash/desktop_background/user_wallpaper_delegate.h"
#include "ash/display/display_manager.h"
#include "ash/focus_cycler.h"
#include "ash/high_contrast/high_contrast_controller.h"
#include "ash/root_window_settings.h"
#include "ash/session_state_delegate.h"
#include "ash/shelf/shelf_layout_manager.h"
#include "ash/shelf/shelf_types.h"
#include "ash/shelf/shelf_widget.h"
#include "ash/shell.h"
#include "ash/shell_delegate.h"
#include "ash/shell_factory.h"
#include "ash/shell_window_ids.h"
#include "ash/switchable_windows.h"
#include "ash/system/status_area_widget.h"
#include "ash/system/tray/system_tray_delegate.h"
#include "ash/touch/touch_hud_debug.h"
#include "ash/touch/touch_hud_projection.h"
#include "ash/touch/touch_observer_hud.h"
#include "ash/wm/always_on_top_controller.h"
#include "ash/wm/dock/docked_window_layout_manager.h"
#include "ash/wm/panels/panel_layout_manager.h"
#include "ash/wm/panels/panel_window_event_handler.h"
#include "ash/wm/root_window_layout_manager.h"
#include "ash/wm/screen_dimmer.h"
#include "ash/wm/stacking_controller.h"
#include "ash/wm/status_area_layout_manager.h"
#include "ash/wm/system_background_controller.h"
#include "ash/wm/system_modal_container_layout_manager.h"
#include "ash/wm/window_properties.h"
#include "ash/wm/window_state.h"
#include "ash/wm/window_util.h"
#include "ash/wm/workspace/workspace_layout_manager.h"
#include "ash/wm/workspace_controller.h"
#include "base/command_line.h"
#include "base/time/time.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/client/screen_position_client.h"
#include "ui/aura/window.h"
#include "ui/aura/window_delegate.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/aura/window_observer.h"
#include "ui/aura/window_tracker.h"
#include "ui/base/hit_test.h"
#include "ui/base/models/menu_model.h"
#include "ui/gfx/display.h"
#include "ui/gfx/screen.h"
#include "ui/keyboard/keyboard_controller.h"
#include "ui/keyboard/keyboard_util.h"
#include "ui/views/controls/menu/menu_runner.h"
#include "ui/views/view_model.h"
#include "ui/views/view_model_utils.h"
#include "ui/wm/core/capture_controller.h"
#include "ui/wm/core/easy_resize_window_targeter.h"
#include "ui/wm/core/visibility_controller.h"
#include "ui/wm/core/window_util.h"
#include "ui/wm/public/drag_drop_client.h"
#include "ui/wm/public/tooltip_client.h"
#include "ui/wm/public/window_types.h"

#if defined(OS_CHROMEOS)
#include "ash/wm/boot_splash_screen_chromeos.h"
#endif

namespace ash {
namespace {

#if defined(OS_CHROMEOS)
// Duration for the animation that hides the boot splash screen, in
// milliseconds.  This should be short enough in relation to
// wm/window_animation.cc's brightness/grayscale fade animation that the login
// background image animation isn't hidden by the splash screen animation.
const int kBootSplashScreenHideDurationMs = 500;
#endif

// Creates a new window for use as a container.
aura::Window* CreateContainer(int window_id,
                              const char* name,
                              aura::Window* parent) {
  aura::Window* container = new aura::Window(NULL);
  container->set_id(window_id);
  container->SetName(name);
  container->Init(aura::WINDOW_LAYER_NOT_DRAWN);
  parent->AddChild(container);
  if (window_id != kShellWindowId_UnparentedControlContainer)
    container->Show();
  return container;
}

float ToRelativeValue(int value, int src, int dst) {
  return static_cast<float>(value) / static_cast<float>(src) * dst;
}

void MoveOriginRelativeToSize(const gfx::Size& src_size,
                                const gfx::Size& dst_size,
                                gfx::Rect* bounds_in_out) {
  gfx::Point origin = bounds_in_out->origin();
  bounds_in_out->set_origin(gfx::Point(
      ToRelativeValue(origin.x(), src_size.width(), dst_size.width()),
      ToRelativeValue(origin.y(), src_size.height(), dst_size.height())));
}

// Reparents |window| to |new_parent|.
void ReparentWindow(aura::Window* window, aura::Window* new_parent) {
  const gfx::Size src_size = window->parent()->bounds().size();
  const gfx::Size dst_size = new_parent->bounds().size();
  // Update the restore bounds to make it relative to the display.
  wm::WindowState* state = wm::GetWindowState(window);
  gfx::Rect restore_bounds;
  bool has_restore_bounds = state->HasRestoreBounds();

  bool update_bounds = (state->IsNormalOrSnapped() || state->IsMinimized()) &&
                       new_parent->id() != kShellWindowId_DockedContainer;
  gfx::Rect local_bounds;
  if (update_bounds) {
    local_bounds = state->window()->bounds();
    MoveOriginRelativeToSize(src_size, dst_size, &local_bounds);
  }

  if (has_restore_bounds) {
    restore_bounds = state->GetRestoreBoundsInParent();
    MoveOriginRelativeToSize(src_size, dst_size, &restore_bounds);
  }

  new_parent->AddChild(window);

  // Docked windows have bounds handled by the layout manager in AddChild().
  if (update_bounds)
    window->SetBounds(local_bounds);

  if (has_restore_bounds)
    state->SetRestoreBoundsInParent(restore_bounds);
}

// Reparents the appropriate set of windows from |src| to |dst|.
void ReparentAllWindows(aura::Window* src, aura::Window* dst) {
  // Set of windows to move.
  const int kContainerIdsToMove[] = {
      kShellWindowId_DefaultContainer,
      kShellWindowId_DockedContainer,
      kShellWindowId_PanelContainer,
      kShellWindowId_AlwaysOnTopContainer,
      kShellWindowId_SystemModalContainer,
      kShellWindowId_LockSystemModalContainer,
      kShellWindowId_InputMethodContainer,
      kShellWindowId_UnparentedControlContainer, };
  for (size_t i = 0; i < arraysize(kContainerIdsToMove); i++) {
    int id = kContainerIdsToMove[i];
    aura::Window* src_container = Shell::GetContainer(src, id);
    aura::Window* dst_container = Shell::GetContainer(dst, id);
    while (!src_container->children().empty()) {
      // Restart iteration from the source container windows each time as they
      // may change as a result of moving other windows.
      aura::Window::Windows::const_iterator iter =
          src_container->children().begin();
      while (iter != src_container->children().end() &&
             SystemModalContainerLayoutManager::IsModalBackground(*iter)) {
        ++iter;
      }
      // If the entire window list is modal background windows then stop.
      if (iter == src_container->children().end())
        break;
      ReparentWindow(*iter, dst_container);
    }
  }
}

// Mark the container window so that a widget added to this container will
// use the virtual screeen coordinates instead of parent.
void SetUsesScreenCoordinates(aura::Window* container) {
  container->SetProperty(kUsesScreenCoordinatesKey, true);
}

// Mark the container window so that a widget added to this container will
// say in the same root window regardless of the bounds specified.
void DescendantShouldStayInSameRootWindow(aura::Window* container) {
  container->SetProperty(kStayInSameRootWindowKey, true);
}

void SetUsesEasyResizeTargeter(aura::Window* container) {
  gfx::Insets mouse_extend(-kResizeOutsideBoundsSize,
                           -kResizeOutsideBoundsSize,
                           -kResizeOutsideBoundsSize,
                           -kResizeOutsideBoundsSize);
  gfx::Insets touch_extend = mouse_extend.Scale(
      kResizeOutsideBoundsScaleForTouch);
  container->SetEventTargeter(scoped_ptr<ui::EventTargeter>(
      new ::wm::EasyResizeWindowTargeter(container, mouse_extend,
                                         touch_extend)));
}

// A window delegate which does nothing. Used to create a window that
// is a event target, but do nothing.
class EmptyWindowDelegate : public aura::WindowDelegate {
 public:
  EmptyWindowDelegate() {}
  virtual ~EmptyWindowDelegate() {}

  // aura::WindowDelegate overrides:
  virtual gfx::Size GetMinimumSize() const OVERRIDE {
    return gfx::Size();
  }
  virtual gfx::Size GetMaximumSize() const OVERRIDE {
    return gfx::Size();
  }
  virtual void OnBoundsChanged(const gfx::Rect& old_bounds,
                               const gfx::Rect& new_bounds) OVERRIDE {
  }
  virtual gfx::NativeCursor GetCursor(const gfx::Point& point) OVERRIDE {
    return gfx::kNullCursor;
  }
  virtual int GetNonClientComponent(
      const gfx::Point& point) const OVERRIDE {
    return HTNOWHERE;
  }
  virtual bool ShouldDescendIntoChildForEventHandling(
      aura::Window* child,
      const gfx::Point& location) OVERRIDE {
    return false;
  }
  virtual bool CanFocus() OVERRIDE {
    return false;
  }
  virtual void OnCaptureLost() OVERRIDE {
  }
  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
  }
  virtual void OnDeviceScaleFactorChanged(
      float device_scale_factor) OVERRIDE {
  }
  virtual void OnWindowDestroying(aura::Window* window) OVERRIDE {}
  virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE {
    delete this;
  }
  virtual void OnWindowTargetVisibilityChanged(bool visible) OVERRIDE {
  }
  virtual bool HasHitTestMask() const OVERRIDE {
    return false;
  }
  virtual void GetHitTestMask(gfx::Path* mask) const OVERRIDE {}

 private:
  DISALLOW_COPY_AND_ASSIGN(EmptyWindowDelegate);
};

}  // namespace

void RootWindowController::CreateForPrimaryDisplay(aura::WindowTreeHost* host) {
  RootWindowController* controller = new RootWindowController(host);
  controller->Init(RootWindowController::PRIMARY,
                   Shell::GetInstance()->delegate()->IsFirstRunAfterBoot());
}

void RootWindowController::CreateForSecondaryDisplay(
    aura::WindowTreeHost* host) {
  RootWindowController* controller = new RootWindowController(host);
  controller->Init(RootWindowController::SECONDARY, false /* first run */);
}

void RootWindowController::CreateForVirtualKeyboardDisplay(
    aura::WindowTreeHost* host) {
  RootWindowController* controller = new RootWindowController(host);
  controller->Init(RootWindowController::VIRTUAL_KEYBOARD,
                   false /* first run */);
}

// static
RootWindowController* RootWindowController::ForShelf(aura::Window* window) {
  return GetRootWindowController(window->GetRootWindow());
}

// static
RootWindowController* RootWindowController::ForWindow(
    const aura::Window* window) {
  return GetRootWindowController(window->GetRootWindow());
}

// static
RootWindowController* RootWindowController::ForTargetRootWindow() {
  return GetRootWindowController(Shell::GetTargetRootWindow());
}

// static
aura::Window* RootWindowController::GetContainerForWindow(
    aura::Window* window) {
  aura::Window* container = window->parent();
  while (container && container->type() != ui::wm::WINDOW_TYPE_UNKNOWN)
    container = container->parent();
  return container;
}

RootWindowController::~RootWindowController() {
  Shutdown();
  host_.reset();
  // The CaptureClient needs to be around for as long as the RootWindow is
  // valid.
  capture_client_.reset();
}

void RootWindowController::SetWallpaperController(
    DesktopBackgroundWidgetController* controller) {
  wallpaper_controller_.reset(controller);
}

void RootWindowController::SetAnimatingWallpaperController(
    AnimatingDesktopController* controller) {
  if (animating_wallpaper_controller_.get())
    animating_wallpaper_controller_->StopAnimating();
  animating_wallpaper_controller_.reset(controller);
}

void RootWindowController::Shutdown() {
  Shell::GetInstance()->RemoveShellObserver(this);

  if (animating_wallpaper_controller_.get())
    animating_wallpaper_controller_->StopAnimating();
  wallpaper_controller_.reset();
  animating_wallpaper_controller_.reset();

  // Change the target root window before closing child windows. If any child
  // being removed triggers a relayout of the shelf it will try to build a
  // window list adding windows from the target root window's containers which
  // may have already gone away.
  if (Shell::GetTargetRootWindow() == root_window()) {
    Shell::GetInstance()->set_target_root_window(
        Shell::GetPrimaryRootWindow() == root_window() ?
        NULL : Shell::GetPrimaryRootWindow());
  }

  CloseChildWindows();
  GetRootWindowSettings(root_window())->controller = NULL;
  screen_dimmer_.reset();
  workspace_controller_.reset();
  // Forget with the display ID so that display lookup
  // ends up with invalid display.
  GetRootWindowSettings(root_window())->display_id =
      gfx::Display::kInvalidDisplayID;
  GetRootWindowSettings(root_window())->shutdown = true;

  system_background_.reset();
  aura::client::SetScreenPositionClient(root_window(), NULL);
}

SystemModalContainerLayoutManager*
RootWindowController::GetSystemModalLayoutManager(aura::Window* window) {
  aura::Window* modal_container = NULL;
  if (window) {
    aura::Window* window_container = GetContainerForWindow(window);
    if (window_container &&
        window_container->id() >= kShellWindowId_LockScreenContainer) {
      modal_container = GetContainer(kShellWindowId_LockSystemModalContainer);
    } else {
      modal_container = GetContainer(kShellWindowId_SystemModalContainer);
    }
  } else {
    int modal_window_id = Shell::GetInstance()->session_state_delegate()
        ->IsUserSessionBlocked() ? kShellWindowId_LockSystemModalContainer :
                                   kShellWindowId_SystemModalContainer;
    modal_container = GetContainer(modal_window_id);
  }
  return modal_container ? static_cast<SystemModalContainerLayoutManager*>(
      modal_container->layout_manager()) : NULL;
}

aura::Window* RootWindowController::GetContainer(int container_id) {
  return root_window()->GetChildById(container_id);
}

const aura::Window* RootWindowController::GetContainer(int container_id) const {
  return host_->window()->GetChildById(container_id);
}

void RootWindowController::ShowShelf() {
  if (!shelf_->shelf())
    return;
  shelf_->shelf()->SetVisible(true);
  shelf_->status_area_widget()->Show();
}

void RootWindowController::OnShelfCreated() {
  if (panel_layout_manager_)
    panel_layout_manager_->SetShelf(shelf_->shelf());
  if (docked_layout_manager_) {
    docked_layout_manager_->SetShelf(shelf_->shelf());
    if (shelf_->shelf_layout_manager())
      docked_layout_manager_->AddObserver(shelf_->shelf_layout_manager());
  }
}

void RootWindowController::UpdateAfterLoginStatusChange(
    user::LoginStatus status) {
  if (status != user::LOGGED_IN_NONE)
    mouse_event_target_.reset();
  if (shelf_->status_area_widget())
    shelf_->status_area_widget()->UpdateAfterLoginStatusChange(status);
}

void RootWindowController::HandleInitialDesktopBackgroundAnimationStarted() {
#if defined(OS_CHROMEOS)
  if (CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kAshAnimateFromBootSplashScreen) &&
      boot_splash_screen_.get()) {
    // Make the splash screen fade out so it doesn't obscure the desktop
    // wallpaper's brightness/grayscale animation.
    boot_splash_screen_->StartHideAnimation(
        base::TimeDelta::FromMilliseconds(kBootSplashScreenHideDurationMs));
  }
#endif
}

void RootWindowController::OnWallpaperAnimationFinished(views::Widget* widget) {
  // Make sure the wallpaper is visible.
  system_background_->SetColor(SK_ColorBLACK);
#if defined(OS_CHROMEOS)
  boot_splash_screen_.reset();
#endif

  Shell::GetInstance()->user_wallpaper_delegate()->
      OnWallpaperAnimationFinished();
  // Only removes old component when wallpaper animation finished. If we
  // remove the old one before the new wallpaper is done fading in there will
  // be a white flash during the animation.
  if (animating_wallpaper_controller()) {
    DesktopBackgroundWidgetController* controller =
        animating_wallpaper_controller()->GetController(true);
    // |desktop_widget_| should be the same animating widget we try to move
    // to |kDesktopController|. Otherwise, we may close |desktop_widget_|
    // before move it to |kDesktopController|.
    DCHECK_EQ(controller->widget(), widget);
    // Release the old controller and close its background widget.
    SetWallpaperController(controller);
  }
}

void RootWindowController::CloseChildWindows() {
  mouse_event_target_.reset();

  // Deactivate keyboard container before closing child windows and shutting
  // down associated layout managers.
  DeactivateKeyboard(Shell::GetInstance()->keyboard_controller());

  // panel_layout_manager_ needs to be shut down before windows are destroyed.
  if (panel_layout_manager_) {
    panel_layout_manager_->Shutdown();
    panel_layout_manager_ = NULL;
  }
  // docked_layout_manager_ needs to be shut down before windows are destroyed.
  if (docked_layout_manager_) {
    if (shelf_ && shelf_->shelf_layout_manager())
      docked_layout_manager_->RemoveObserver(shelf_->shelf_layout_manager());
    docked_layout_manager_->Shutdown();
    docked_layout_manager_ = NULL;
  }

  aura::client::SetDragDropClient(root_window(), NULL);

  // TODO(harrym): Remove when Status Area Widget is a child view.
  if (shelf_) {
    shelf_->ShutdownStatusAreaWidget();

    if (shelf_->shelf_layout_manager())
      shelf_->shelf_layout_manager()->PrepareForShutdown();
  }

  // Close background widget first as it depends on tooltip.
  wallpaper_controller_.reset();
  animating_wallpaper_controller_.reset();

  workspace_controller_.reset();
  aura::client::SetTooltipClient(root_window(), NULL);

  // Explicitly destroy top level windows. We do this as during part of
  // destruction such windows may query the RootWindow for state.
  std::queue<aura::Window*> non_toplevel_windows;
  non_toplevel_windows.push(root_window());
  while (!non_toplevel_windows.empty()) {
    aura::Window* non_toplevel_window = non_toplevel_windows.front();
    non_toplevel_windows.pop();
    aura::WindowTracker toplevel_windows;
    for (size_t i = 0; i < non_toplevel_window->children().size(); ++i) {
      aura::Window* child = non_toplevel_window->children()[i];
      if (!child->owned_by_parent())
        continue;
      if (child->delegate())
        toplevel_windows.Add(child);
      else
        non_toplevel_windows.push(child);
    }
    while (!toplevel_windows.windows().empty())
      delete *toplevel_windows.windows().begin();
  }
  // And then remove the containers.
  while (!root_window()->children().empty()) {
    aura::Window* window = root_window()->children()[0];
    if (window->owned_by_parent()) {
      delete window;
    } else {
      root_window()->RemoveChild(window);
    }
  }

  shelf_.reset();
}

void RootWindowController::MoveWindowsTo(aura::Window* dst) {
  // Forget the shelf early so that shelf don't update itself using wrong
  // display info.
  workspace_controller_->SetShelf(NULL);
  ReparentAllWindows(root_window(), dst);
}

ShelfLayoutManager* RootWindowController::GetShelfLayoutManager() {
  return shelf_->shelf_layout_manager();
}

SystemTray* RootWindowController::GetSystemTray() {
  // We assume in throughout the code that this will not return NULL. If code
  // triggers this for valid reasons, it should test status_area_widget first.
  CHECK(shelf_->status_area_widget());
  return shelf_->status_area_widget()->system_tray();
}

void RootWindowController::ShowContextMenu(const gfx::Point& location_in_screen,
                                           ui::MenuSourceType source_type) {
  DCHECK(Shell::GetInstance()->delegate());
  scoped_ptr<ui::MenuModel> menu_model(
      Shell::GetInstance()->delegate()->CreateContextMenu(root_window(),
                                                          NULL,
                                                          NULL));
  if (!menu_model)
    return;

  // Background controller may not be set yet if user clicked on status are
  // before initial animation completion. See crbug.com/222218
  if (!wallpaper_controller_.get())
    return;

  views::MenuRunner menu_runner(menu_model.get());
  if (menu_runner.RunMenuAt(wallpaper_controller_->widget(),
          NULL, gfx::Rect(location_in_screen, gfx::Size()),
          views::MenuItemView::TOPLEFT, source_type,
          views::MenuRunner::CONTEXT_MENU) ==
      views::MenuRunner::MENU_DELETED) {
    return;
  }

  Shell::GetInstance()->UpdateShelfVisibility();
}

void RootWindowController::UpdateShelfVisibility() {
  shelf_->shelf_layout_manager()->UpdateVisibilityState();
}

const aura::Window* RootWindowController::GetWindowForFullscreenMode() const {
  const aura::Window* topmost_window = NULL;
  const aura::Window* active_window = wm::GetActiveWindow();
  if (active_window && active_window->GetRootWindow() == root_window() &&
      IsSwitchableContainer(active_window->parent())) {
    // Use the active window when it is on the current root window to determine
    // the fullscreen state to allow temporarily using a panel or docked window
    // (which are always above the default container) while a fullscreen
    // window is open. We only use the active window when in a switchable
    // container as the launcher should not exit fullscreen mode.
    topmost_window = active_window;
  } else {
    // Otherwise, use the topmost window on the root window's default container
    // when there is no active window on this root window.
    const aura::Window::Windows& windows =
        GetContainer(kShellWindowId_DefaultContainer)->children();
    for (aura::Window::Windows::const_reverse_iterator iter = windows.rbegin();
         iter != windows.rend(); ++iter) {
      if (((*iter)->type() == ui::wm::WINDOW_TYPE_NORMAL ||
           (*iter)->type() == ui::wm::WINDOW_TYPE_PANEL) &&
          (*iter)->layer()->GetTargetVisibility()) {
        topmost_window = *iter;
        break;
      }
    }
  }
  while (topmost_window) {
    if (wm::GetWindowState(topmost_window)->IsFullscreen())
      return topmost_window;
    topmost_window = ::wm::GetTransientParent(topmost_window);
  }
  return NULL;
}

void RootWindowController::ActivateKeyboard(
    keyboard::KeyboardController* keyboard_controller) {
  if (!keyboard::IsKeyboardEnabled() ||
      GetContainer(kShellWindowId_VirtualKeyboardContainer)) {
    return;
  }
  DCHECK(keyboard_controller);
  if (!keyboard::IsKeyboardUsabilityExperimentEnabled()) {
    keyboard_controller->AddObserver(shelf()->shelf_layout_manager());
    keyboard_controller->AddObserver(panel_layout_manager_);
    keyboard_controller->AddObserver(docked_layout_manager_);
    Shell::GetInstance()->delegate()->VirtualKeyboardActivated(true);
  }
  aura::Window* parent = GetContainer(
      kShellWindowId_VirtualKeyboardParentContainer);
  DCHECK(parent);
  aura::Window* keyboard_container =
      keyboard_controller->GetContainerWindow();
  keyboard_container->set_id(kShellWindowId_VirtualKeyboardContainer);
  parent->AddChild(keyboard_container);
  // TODO(oshima): Bounds of keyboard container should be handled by
  // RootWindowLayoutManager. Remove this after fixed RootWindowLayoutManager.
  keyboard_container->SetBounds(parent->bounds());
}

void RootWindowController::DeactivateKeyboard(
    keyboard::KeyboardController* keyboard_controller) {
  if (!keyboard_controller ||
      !keyboard_controller->keyboard_container_initialized()) {
    return;
  }
  aura::Window* keyboard_container =
      keyboard_controller->GetContainerWindow();
  if (keyboard_container->GetRootWindow() == root_window()) {
    aura::Window* parent = GetContainer(
        kShellWindowId_VirtualKeyboardParentContainer);
    DCHECK(parent);
    parent->RemoveChild(keyboard_container);
    if (!keyboard::IsKeyboardUsabilityExperimentEnabled()) {
      // Virtual keyboard may be deactivated while still showing, notify all
      // observers that keyboard bounds changed to 0 before remove them.
      keyboard_controller->NotifyKeyboardBoundsChanging(gfx::Rect());
      keyboard_controller->RemoveObserver(shelf()->shelf_layout_manager());
      keyboard_controller->RemoveObserver(panel_layout_manager_);
      keyboard_controller->RemoveObserver(docked_layout_manager_);
      Shell::GetInstance()->delegate()->VirtualKeyboardActivated(false);
    }
  }
}

bool RootWindowController::IsVirtualKeyboardWindow(aura::Window* window) {
  aura::Window* parent = GetContainer(
      kShellWindowId_VirtualKeyboardParentContainer);
  return parent ? parent->Contains(window) : false;
}

////////////////////////////////////////////////////////////////////////////////
// RootWindowController, private:

RootWindowController::RootWindowController(aura::WindowTreeHost* host)
    : host_(host),
      root_window_layout_(NULL),
      docked_layout_manager_(NULL),
      panel_layout_manager_(NULL),
      touch_hud_debug_(NULL),
      touch_hud_projection_(NULL) {
  GetRootWindowSettings(root_window())->controller = this;
  screen_dimmer_.reset(new ScreenDimmer(root_window()));

  stacking_controller_.reset(new StackingController);
  aura::client::SetWindowTreeClient(root_window(), stacking_controller_.get());
  capture_client_.reset(new ::wm::ScopedCaptureClient(root_window()));
}

void RootWindowController::Init(RootWindowType root_window_type,
                                bool first_run_after_boot) {
  Shell* shell = Shell::GetInstance();
  shell->InitRootWindow(root_window());

  host_->SetCursor(ui::kCursorPointer);
  CreateContainersInRootWindow(root_window());

  if (root_window_type == VIRTUAL_KEYBOARD) {
    shell->InitKeyboard();
    return;
  }

  CreateSystemBackground(first_run_after_boot);

  InitLayoutManagers();
  InitTouchHuds();

  if (Shell::GetPrimaryRootWindowController()->
      GetSystemModalLayoutManager(NULL)->has_modal_background()) {
    GetSystemModalLayoutManager(NULL)->CreateModalBackground();
  }

  shell->AddShellObserver(this);

  if (root_window_type == PRIMARY) {
    root_window_layout()->OnWindowResized();
    if (!keyboard::IsKeyboardUsabilityExperimentEnabled())
      shell->InitKeyboard();
  } else {
    root_window_layout()->OnWindowResized();
    shell->desktop_background_controller()->OnRootWindowAdded(root_window());
    shell->high_contrast_controller()->OnRootWindowAdded(root_window());
    host_->Show();

    // Create a shelf if a user is already logged in.
    if (shell->session_state_delegate()->NumberOfLoggedInUsers())
      shelf()->CreateShelf();
  }
}

void RootWindowController::InitLayoutManagers() {
  root_window_layout_ = new RootWindowLayoutManager(root_window());
  root_window()->SetLayoutManager(root_window_layout_);

  aura::Window* default_container =
      GetContainer(kShellWindowId_DefaultContainer);
  // Workspace manager has its own layout managers.
  workspace_controller_.reset(
      new WorkspaceController(default_container));

  aura::Window* always_on_top_container =
      GetContainer(kShellWindowId_AlwaysOnTopContainer);
  always_on_top_container->SetLayoutManager(
      new WorkspaceLayoutManager(always_on_top_container));
  always_on_top_controller_.reset(new AlwaysOnTopController);
  always_on_top_controller_->SetAlwaysOnTopContainer(always_on_top_container);

  DCHECK(!shelf_.get());
  aura::Window* shelf_container = GetContainer(kShellWindowId_ShelfContainer);
  // TODO(harrym): Remove when status area is view.
  aura::Window* status_container = GetContainer(kShellWindowId_StatusContainer);
  shelf_.reset(new ShelfWidget(
      shelf_container, status_container, workspace_controller()));

  if (!Shell::GetInstance()->session_state_delegate()->
      IsActiveUserSessionStarted()) {
    // This window exists only to be a event target on login screen.
    // It does not have to handle events, nor be visible.
    mouse_event_target_.reset(new aura::Window(new EmptyWindowDelegate));
    mouse_event_target_->Init(aura::WINDOW_LAYER_NOT_DRAWN);

    aura::Window* lock_background_container =
        GetContainer(kShellWindowId_LockScreenBackgroundContainer);
    lock_background_container->AddChild(mouse_event_target_.get());
    mouse_event_target_->Show();
  }

  // Create Docked windows layout manager
  aura::Window* docked_container = GetContainer(kShellWindowId_DockedContainer);
  docked_layout_manager_ =
      new DockedWindowLayoutManager(docked_container, workspace_controller());
  docked_container->SetLayoutManager(docked_layout_manager_);

  // Create Panel layout manager
  aura::Window* panel_container = GetContainer(kShellWindowId_PanelContainer);
  panel_layout_manager_ = new PanelLayoutManager(panel_container);
  panel_container->SetLayoutManager(panel_layout_manager_);
  panel_container_handler_.reset(new PanelWindowEventHandler);
  panel_container->AddPreTargetHandler(panel_container_handler_.get());
}

void RootWindowController::InitTouchHuds() {
  CommandLine* command_line = CommandLine::ForCurrentProcess();
  if (command_line->HasSwitch(switches::kAshTouchHud))
    set_touch_hud_debug(new TouchHudDebug(root_window()));
  if (Shell::GetInstance()->is_touch_hud_projection_enabled())
    EnableTouchHudProjection();
}

void RootWindowController::CreateSystemBackground(
    bool is_first_run_after_boot) {
  SkColor color = SK_ColorBLACK;
#if defined(OS_CHROMEOS)
  if (is_first_run_after_boot)
    color = kChromeOsBootColor;
#endif
  system_background_.reset(
    new SystemBackgroundController(root_window(), color));

#if defined(OS_CHROMEOS)
  // Make a copy of the system's boot splash screen so we can composite it
  // onscreen until the desktop background is ready.
  if (is_first_run_after_boot &&
      (CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kAshCopyHostBackgroundAtBoot) ||
       CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kAshAnimateFromBootSplashScreen)))
    boot_splash_screen_.reset(new BootSplashScreen(host()));
#endif
}

void RootWindowController::CreateContainersInRootWindow(
    aura::Window* root_window) {
  // These containers are just used by PowerButtonController to animate groups
  // of containers simultaneously without messing up the current transformations
  // on those containers. These are direct children of the root window; all of
  // the other containers are their children.

  // The desktop background container is not part of the lock animation, so it
  // is not included in those animate groups.
  // When screen is locked desktop background is moved to lock screen background
  // container (moved back on unlock). We want to make sure that there's an
  // opaque layer occluding the non-lock-screen layers.
  aura::Window* desktop_background_container = CreateContainer(
      kShellWindowId_DesktopBackgroundContainer,
      "DesktopBackgroundContainer",
      root_window);
  ::wm::SetChildWindowVisibilityChangesAnimated(desktop_background_container);

  aura::Window* non_lock_screen_containers = CreateContainer(
      kShellWindowId_NonLockScreenContainersContainer,
      "NonLockScreenContainersContainer",
      root_window);

  aura::Window* lock_background_containers = CreateContainer(
      kShellWindowId_LockScreenBackgroundContainer,
      "LockScreenBackgroundContainer",
      root_window);
  ::wm::SetChildWindowVisibilityChangesAnimated(lock_background_containers);

  aura::Window* lock_screen_containers = CreateContainer(
      kShellWindowId_LockScreenContainersContainer,
      "LockScreenContainersContainer",
      root_window);
  aura::Window* lock_screen_related_containers = CreateContainer(
      kShellWindowId_LockScreenRelatedContainersContainer,
      "LockScreenRelatedContainersContainer",
      root_window);

  CreateContainer(kShellWindowId_UnparentedControlContainer,
                  "UnparentedControlContainer",
                  non_lock_screen_containers);

  aura::Window* default_container = CreateContainer(
      kShellWindowId_DefaultContainer,
      "DefaultContainer",
      non_lock_screen_containers);
  ::wm::SetChildWindowVisibilityChangesAnimated(default_container);
  SetUsesScreenCoordinates(default_container);
  SetUsesEasyResizeTargeter(default_container);

  aura::Window* always_on_top_container = CreateContainer(
      kShellWindowId_AlwaysOnTopContainer,
      "AlwaysOnTopContainer",
      non_lock_screen_containers);
  ::wm::SetChildWindowVisibilityChangesAnimated(always_on_top_container);
  SetUsesScreenCoordinates(always_on_top_container);

  aura::Window* docked_container = CreateContainer(
      kShellWindowId_DockedContainer,
      "DockedContainer",
      non_lock_screen_containers);
  ::wm::SetChildWindowVisibilityChangesAnimated(docked_container);
  SetUsesScreenCoordinates(docked_container);
  SetUsesEasyResizeTargeter(docked_container);

  aura::Window* shelf_container =
      CreateContainer(kShellWindowId_ShelfContainer,
                      "ShelfContainer",
                      non_lock_screen_containers);
  SetUsesScreenCoordinates(shelf_container);
  DescendantShouldStayInSameRootWindow(shelf_container);

  aura::Window* panel_container = CreateContainer(
      kShellWindowId_PanelContainer,
      "PanelContainer",
      non_lock_screen_containers);
  SetUsesScreenCoordinates(panel_container);
  SetUsesEasyResizeTargeter(panel_container);

  aura::Window* shelf_bubble_container =
      CreateContainer(kShellWindowId_ShelfBubbleContainer,
                      "ShelfBubbleContainer",
                      non_lock_screen_containers);
  SetUsesScreenCoordinates(shelf_bubble_container);
  DescendantShouldStayInSameRootWindow(shelf_bubble_container);

  aura::Window* app_list_container =
      CreateContainer(kShellWindowId_AppListContainer,
                      "AppListContainer",
                      non_lock_screen_containers);
  SetUsesScreenCoordinates(app_list_container);

  aura::Window* modal_container = CreateContainer(
      kShellWindowId_SystemModalContainer,
      "SystemModalContainer",
      non_lock_screen_containers);
  modal_container->SetLayoutManager(
      new SystemModalContainerLayoutManager(modal_container));
  ::wm::SetChildWindowVisibilityChangesAnimated(modal_container);
  SetUsesScreenCoordinates(modal_container);
  SetUsesEasyResizeTargeter(modal_container);

  aura::Window* input_method_container = CreateContainer(
      kShellWindowId_InputMethodContainer,
      "InputMethodContainer",
      non_lock_screen_containers);
  ::wm::SetChildWindowVisibilityChangesAnimated(input_method_container);
  SetUsesScreenCoordinates(input_method_container);

  // TODO(beng): Figure out if we can make this use
  // SystemModalContainerEventFilter instead of stops_event_propagation.
  aura::Window* lock_container = CreateContainer(
      kShellWindowId_LockScreenContainer,
      "LockScreenContainer",
      lock_screen_containers);
  lock_container->SetLayoutManager(new WorkspaceLayoutManager(lock_container));
  SetUsesScreenCoordinates(lock_container);
  // TODO(beng): stopsevents

  aura::Window* lock_modal_container = CreateContainer(
      kShellWindowId_LockSystemModalContainer,
      "LockSystemModalContainer",
      lock_screen_containers);
  lock_modal_container->SetLayoutManager(
      new SystemModalContainerLayoutManager(lock_modal_container));
  ::wm::SetChildWindowVisibilityChangesAnimated(lock_modal_container);
  SetUsesScreenCoordinates(lock_modal_container);
  SetUsesEasyResizeTargeter(lock_modal_container);

  aura::Window* status_container =
      CreateContainer(kShellWindowId_StatusContainer,
                      "StatusContainer",
                      lock_screen_related_containers);
  SetUsesScreenCoordinates(status_container);
  DescendantShouldStayInSameRootWindow(status_container);

  aura::Window* settings_bubble_container = CreateContainer(
      kShellWindowId_SettingBubbleContainer,
      "SettingBubbleContainer",
      lock_screen_related_containers);
  ::wm::SetChildWindowVisibilityChangesAnimated(settings_bubble_container);
  SetUsesScreenCoordinates(settings_bubble_container);
  DescendantShouldStayInSameRootWindow(settings_bubble_container);

  aura::Window* menu_container = CreateContainer(
      kShellWindowId_MenuContainer,
      "MenuContainer",
      lock_screen_related_containers);
  ::wm::SetChildWindowVisibilityChangesAnimated(menu_container);
  SetUsesScreenCoordinates(menu_container);

  aura::Window* drag_drop_container = CreateContainer(
      kShellWindowId_DragImageAndTooltipContainer,
      "DragImageAndTooltipContainer",
      lock_screen_related_containers);
  ::wm::SetChildWindowVisibilityChangesAnimated(drag_drop_container);
  SetUsesScreenCoordinates(drag_drop_container);

  aura::Window* overlay_container = CreateContainer(
      kShellWindowId_OverlayContainer,
      "OverlayContainer",
      lock_screen_related_containers);
  SetUsesScreenCoordinates(overlay_container);

  aura::Window* virtual_keyboard_parent_container = CreateContainer(
      kShellWindowId_VirtualKeyboardParentContainer,
      "VirtualKeyboardParentContainer",
      root_window);
  SetUsesScreenCoordinates(virtual_keyboard_parent_container);

#if defined(OS_CHROMEOS)
  aura::Window* mouse_cursor_container = CreateContainer(
      kShellWindowId_MouseCursorContainer,
      "MouseCursorContainer",
      root_window);
  SetUsesScreenCoordinates(mouse_cursor_container);
#endif

  CreateContainer(kShellWindowId_PowerButtonAnimationContainer,
                  "PowerButtonAnimationContainer", root_window);
}

void RootWindowController::EnableTouchHudProjection() {
  if (touch_hud_projection_)
    return;
  set_touch_hud_projection(new TouchHudProjection(root_window()));
}

void RootWindowController::DisableTouchHudProjection() {
  if (!touch_hud_projection_)
    return;
  touch_hud_projection_->Remove();
}

void RootWindowController::OnLoginStateChanged(user::LoginStatus status) {
  shelf_->shelf_layout_manager()->UpdateVisibilityState();
}

void RootWindowController::OnTouchHudProjectionToggled(bool enabled) {
  if (enabled)
    EnableTouchHudProjection();
  else
    DisableTouchHudProjection();
}

RootWindowController* GetRootWindowController(
    const aura::Window* root_window) {
  return root_window ? GetRootWindowSettings(root_window)->controller : NULL;
}

}  // namespace ash

/* [<][>][^][v][top][bottom][index][help] */