| Index: ash/wm/pinned_controller.cc | 
| diff --git a/ash/wm/pinned_controller.cc b/ash/wm/pinned_controller.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..dacb2ba58831a008ca1bca752472fff008eb643d | 
| --- /dev/null | 
| +++ b/ash/wm/pinned_controller.cc | 
| @@ -0,0 +1,239 @@ | 
| +// Copyright 2016 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 <algorithm> | 
| +#include <vector> | 
| + | 
| +#include "ash/aura/wm_window_aura.h" | 
| +#include "ash/common/shell_window_ids.h" | 
| +#include "ash/common/wm/window_state.h" | 
| +#include "ash/common/wm_window.h" | 
| +#include "ash/shell.h" | 
| +#include "ash/wm/dim_window.h" | 
| +#include "ash/wm/pinned_controller.h" | 
| +#include "base/auto_reset.h" | 
| +#include "base/logging.h" | 
| + | 
| +namespace ash { | 
| +namespace wm { | 
| +namespace { | 
| + | 
| +WmWindow* CreateDimWindow(WmWindow* container) { | 
| +  DimWindow* dim_window = new DimWindow(WmWindowAura::GetAuraWindow(container)); | 
| +  dim_window->SetDimOpacity(1);  // Set whole black. | 
| +  dim_window->Show(); | 
| +  WmWindow* result = WmWindowAura::Get(dim_window); | 
| +  result->SetFullscreen(); | 
| +  return result; | 
| +} | 
| + | 
| +// Returns a list of WmWindows corresponding to SystemModalContainers, | 
| +// except ones whose root is shared with |pinned_window|. | 
| +std::vector<WmWindow*> GetSystemModalWindowsExceptPinned( | 
| +    WmWindow* pinned_window) { | 
| +  WmWindow* pinned_root = pinned_window->GetRootWindow(); | 
| + | 
| +  ash::Shell* shell = ash::Shell::GetInstance(); | 
| +  std::vector<WmWindow*> result; | 
| +  for (WmWindow* system_modal : | 
| +       WmWindowAura::FromAuraWindows(shell->GetContainersFromAllRootWindows( | 
| +           kShellWindowId_SystemModalContainer, nullptr))) { | 
| +    if (system_modal->GetRootWindow() == pinned_root) | 
| +      continue; | 
| +    result.push_back(system_modal); | 
| +  } | 
| +  return result; | 
| +} | 
| + | 
| +template <typename T> | 
| +bool Contains(const std::vector<T>& container, const T& target) { | 
| +  return std::find(container.begin(), container.end(), target) != | 
| +         container.end(); | 
| +} | 
| + | 
| +}  // namespace | 
| + | 
| +PinnedController::PinnedController() = default; | 
| +PinnedController::~PinnedController() = default; | 
| + | 
| +bool PinnedController::IsPinned() const { | 
| +  return pinned_window_ != nullptr; | 
| +} | 
| + | 
| +void PinnedController::OnPinnedStateChanged(WmWindow* pinned_window) { | 
| +  if (pinned_window->GetWindowState()->IsPinned()) { | 
| +    if (pinned_window_) { | 
| +      LOG(ERROR) << "Pinned mode is enabled, while it is already in " | 
| +                 << "the pinned mode"; | 
| +      return; | 
| +    } | 
| + | 
| +    WmWindow* container = pinned_window->GetParent(); | 
| +    std::vector<WmWindow*> system_modal_containers = | 
| +        GetSystemModalWindowsExceptPinned(pinned_window); | 
| + | 
| +    // Set up the container which has the pinned window. | 
| +    pinned_window_ = pinned_window; | 
| +    background_window_ = CreateDimWindow(container); | 
| +    container->StackChildAtTop(pinned_window); | 
| +    container->StackChildBelow(background_window_, pinned_window); | 
| + | 
| +    // Set the dim windows to the system containers, other than the one which | 
| +    // the root window of the pinned window holds. | 
| +    for (WmWindow* system_modal : system_modal_containers) { | 
| +      WmWindow* dim_window = CreateDimWindow(system_modal); | 
| +      system_modal->StackChildAtTop(dim_window); | 
| +      dim_windows_.push_back(dim_window); | 
| +    } | 
| + | 
| +    // Set observers. | 
| +    container->AddObserver(this); | 
| +    for (WmWindow* child : container->GetChildren()) | 
| +      child->AddObserver(this); | 
| +    for (WmWindow* dim_window : dim_windows_) | 
| +      dim_window->AddObserver(this); | 
| +    for (WmWindow* system_modal : system_modal_containers) | 
| +      system_modal->AddObserver(this); | 
| +  } else { | 
| +    if (pinned_window != pinned_window_) { | 
| +      LOG(ERROR) << "Pinned mode is being disabled, but for the different " | 
| +                 << "target window."; | 
| +      return; | 
| +    } | 
| + | 
| +    WmWindow* container = pinned_window->GetParent(); | 
| +    std::vector<WmWindow*> system_modal_containers = | 
| +        GetSystemModalWindowsExceptPinned(pinned_window_); | 
| + | 
| +    // Unset observers. | 
| +    for (WmWindow* system_modal : system_modal_containers) | 
| +      system_modal->RemoveObserver(this); | 
| +    for (WmWindow* dim_window : dim_windows_) | 
| +      dim_window->RemoveObserver(this); | 
| +    for (WmWindow* child : container->GetChildren()) | 
| +      child->RemoveObserver(this); | 
| +    container->RemoveObserver(this); | 
| + | 
| +    // Delete the dim windows. This works, but do not use RAII, because | 
| +    // the window is owned by the parent. | 
| +    for (WmWindow* dim_window : dim_windows_) { | 
| +      dim_window->RemoveObserver(this); | 
| +      delete WmWindowAura::GetAuraWindow(dim_window); | 
| +    } | 
| +    dim_windows_.clear(); | 
| +    delete WmWindowAura::GetAuraWindow(background_window_); | 
| +    background_window_ = nullptr; | 
| + | 
| +    pinned_window_ = nullptr; | 
| +  } | 
| +} | 
| + | 
| +void PinnedController::OnWindowTreeChanging(WmWindow* window, | 
| +                                            const TreeChangeParams& params) { | 
| +  DCHECK(IsPinned()); | 
| +  DCHECK(params.target != pinned_window_); | 
| +  DCHECK(params.target != background_window_); | 
| +  DCHECK(!Contains(dim_windows_, params.target)); | 
| + | 
| +  // Here, only events to remove a window directly from a container should be | 
| +  // handled. | 
| +  if ((window != pinned_window_->GetParent() && | 
| +       window->GetShellWindowId() != kShellWindowId_SystemModalContainer) || | 
| +      window != params.old_parent) | 
| +    return; | 
| + | 
| +  // If a window is being removed from the parent, stop observing it. | 
| +  params.target->RemoveObserver(this); | 
| +} | 
| + | 
| +void PinnedController::OnWindowTreeChanged(WmWindow* window, | 
| +                                           const TreeChangeParams& params) { | 
| +  DCHECK(IsPinned()); | 
| +  DCHECK(params.target != pinned_window_); | 
| +  DCHECK(params.target != background_window_); | 
| +  DCHECK(!Contains(dim_windows_, params.target)); | 
| + | 
| +  // Here, only events to add a window directly into a container should be | 
| +  // handled. | 
| +  if ((window != pinned_window_->GetParent() && | 
| +       window->GetShellWindowId() != kShellWindowId_SystemModalContainer) || | 
| +      window != params.new_parent) | 
| +    return; | 
| + | 
| +  // Observe the window to see the stacking change. | 
| +  // Also, keep the pinned window at the top. | 
| +  params.target->AddObserver(this); | 
| +  if (window == pinned_window_->GetParent()) | 
| +    KeepPinnedWindowOnTop(); | 
| +  else | 
| +    KeepDimWindowOnTop(window); | 
| +} | 
| + | 
| +void PinnedController::OnWindowStackingChanged(WmWindow* window) { | 
| +  DCHECK(IsPinned()); | 
| + | 
| +  if (in_restacking_) | 
| +    return; | 
| + | 
| +  if (window->GetParent() == pinned_window_->GetParent()) | 
| +    KeepPinnedWindowOnTop(); | 
| +  else if (window->GetParent()->GetShellWindowId() == | 
| +           kShellWindowId_SystemModalContainer) | 
| +    KeepDimWindowOnTop(window->GetParent()); | 
| +} | 
| + | 
| +void PinnedController::OnWindowDestroying(WmWindow* window) { | 
| +  DCHECK(IsPinned()); | 
| + | 
| +  if (window == pinned_window_) { | 
| +    pinned_window_->GetWindowState()->Restore(); | 
| +  } else if (window == background_window_) { | 
| +    background_window_ = nullptr; | 
| +  } else if (Contains(dim_windows_, window)) { | 
| +    dim_windows_.erase( | 
| +        std::find(dim_windows_.begin(), dim_windows_.end(), window)); | 
| +  } | 
| +} | 
| + | 
| +void PinnedController::OnDisplayConfigurationChanged() { | 
| +  // Note: this is called on display attached or detached. | 
| +  if (!IsPinned()) | 
| +    return; | 
| + | 
| +  std::vector<WmWindow*> system_modal_containers = | 
| +      GetSystemModalWindowsExceptPinned(pinned_window_); | 
| +  for (WmWindow* system_modal : system_modal_containers) { | 
| +    if (Contains(dim_windows_, system_modal->GetChildren().back())) | 
| +      continue; | 
| +    WmWindow* dim_window = CreateDimWindow(system_modal); | 
| +    system_modal->StackChildAtTop(dim_window); | 
| +    dim_windows_.push_back(dim_window); | 
| +    system_modal->AddObserver(this); | 
| +    dim_window->AddObserver(this); | 
| +  } | 
| +} | 
| + | 
| +void PinnedController::KeepPinnedWindowOnTop() { | 
| +  DCHECK(!in_restacking_); | 
| + | 
| +  base::AutoReset<bool> auto_reset(&in_restacking_, true); | 
| +  WmWindow* container = pinned_window_->GetParent(); | 
| +  container->StackChildAtTop(pinned_window_); | 
| +  container->StackChildBelow(background_window_, pinned_window_); | 
| +} | 
| + | 
| +void PinnedController::KeepDimWindowOnTop(WmWindow* container) { | 
| +  DCHECK(!in_restacking_); | 
| + | 
| +  base::AutoReset<bool> auto_reset(&in_restacking_, true); | 
| +  for (WmWindow* dim_window : dim_windows_) { | 
| +    if (dim_window->GetParent() == container) { | 
| +      container->StackChildAtTop(dim_window); | 
| +      break; | 
| +    } | 
| +  } | 
| +} | 
| + | 
| +}  // namespace wm | 
| +}  // namespace ash | 
|  |