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/wm/always_on_top_controller.h"
6 
7 #include "ash/public/cpp/shell_window_ids.h"
8 #include "ash/public/cpp/window_properties.h"
9 #include "ash/wm/desks/desks_util.h"
10 #include "ash/wm/window_state.h"
11 #include "ash/wm/workspace/workspace_layout_manager.h"
12 #include "ui/aura/client/aura_constants.h"
13 #include "ui/aura/window.h"
14 
15 namespace ash {
16 
DEFINE_UI_CLASS_PROPERTY_KEY(bool,kDisallowReparentKey,false)17 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kDisallowReparentKey, false)
18 
19 AlwaysOnTopController::AlwaysOnTopController(
20     aura::Window* always_on_top_container,
21     aura::Window* pip_container)
22     : always_on_top_container_(always_on_top_container),
23       pip_container_(pip_container) {
24   DCHECK(!desks_util::IsDeskContainer(always_on_top_container_));
25   DCHECK(!desks_util::IsDeskContainer(pip_container_));
26   always_on_top_container_->SetLayoutManager(
27       new WorkspaceLayoutManager(always_on_top_container_));
28   pip_container_->SetLayoutManager(new WorkspaceLayoutManager(pip_container_));
29   // Container should be empty.
30   DCHECK(always_on_top_container_->children().empty());
31   DCHECK(pip_container_->children().empty());
32   always_on_top_container_->AddObserver(this);
33   pip_container->AddObserver(this);
34 }
35 
~AlwaysOnTopController()36 AlwaysOnTopController::~AlwaysOnTopController() {
37   // At this point, all windows should be removed and AlwaysOnTopController
38   // will have removed itself as an observer in OnWindowDestroying.
39   DCHECK(!always_on_top_container_);
40   DCHECK(!pip_container_);
41 }
42 
GetContainer(aura::Window * window) const43 aura::Window* AlwaysOnTopController::GetContainer(aura::Window* window) const {
44   DCHECK(always_on_top_container_);
45   DCHECK(pip_container_);
46 
47   // On other platforms, there are different window levels. For now, treat any
48   // window with non-normal level as "always on top". Perhaps the nuance of
49   // multiple levels will be needed later.
50   if (window->GetProperty(aura::client::kZOrderingKey) ==
51       ui::ZOrderLevel::kNormal) {
52     aura::Window* root = always_on_top_container_->GetRootWindow();
53 
54     // TODO(afakhry): Do we need to worry about the context of |window| here? Or
55     // is it safe to assume that |window| should always be parented to the
56     // active desks' container.
57     return desks_util::GetActiveDeskContainerForRoot(root);
58   }
59   if (window->parent() && WindowState::Get(window)->IsPip())
60     return pip_container_;
61 
62   return always_on_top_container_;
63 }
64 
SetLayoutManagerForTest(std::unique_ptr<WorkspaceLayoutManager> layout_manager)65 void AlwaysOnTopController::SetLayoutManagerForTest(
66     std::unique_ptr<WorkspaceLayoutManager> layout_manager) {
67   always_on_top_container_->SetLayoutManager(layout_manager.release());
68 }
69 
SetDisallowReparent(aura::Window * window)70 void AlwaysOnTopController::SetDisallowReparent(aura::Window* window) {
71   window->SetProperty(kDisallowReparentKey, true);
72 }
73 
AddWindow(aura::Window * window)74 void AlwaysOnTopController::AddWindow(aura::Window* window) {
75   window->AddObserver(this);
76   WindowState::Get(window)->AddObserver(this);
77 }
78 
RemoveWindow(aura::Window * window)79 void AlwaysOnTopController::RemoveWindow(aura::Window* window) {
80   window->RemoveObserver(this);
81   WindowState::Get(window)->RemoveObserver(this);
82 }
83 
ReparentWindow(aura::Window * window)84 void AlwaysOnTopController::ReparentWindow(aura::Window* window) {
85   DCHECK(window->type() == aura::client::WINDOW_TYPE_NORMAL ||
86          window->type() == aura::client::WINDOW_TYPE_POPUP);
87   aura::Window* container = GetContainer(window);
88   if (window->parent() != container &&
89       !window->GetProperty(kDisallowReparentKey))
90     container->AddChild(window);
91 }
92 
OnWindowHierarchyChanged(const HierarchyChangeParams & params)93 void AlwaysOnTopController::OnWindowHierarchyChanged(
94     const HierarchyChangeParams& params) {
95   if (params.old_parent == always_on_top_container_ ||
96       params.old_parent == pip_container_) {
97     RemoveWindow(params.target);
98   }
99 
100   if (params.new_parent == always_on_top_container_ ||
101       params.new_parent == pip_container_) {
102     AddWindow(params.target);
103   }
104 }
105 
OnWindowPropertyChanged(aura::Window * window,const void * key,intptr_t old)106 void AlwaysOnTopController::OnWindowPropertyChanged(aura::Window* window,
107                                                     const void* key,
108                                                     intptr_t old) {
109   if (window != always_on_top_container_ && window != pip_container_ &&
110       key == aura::client::kZOrderingKey) {
111     ReparentWindow(window);
112   }
113 }
114 
OnWindowDestroying(aura::Window * window)115 void AlwaysOnTopController::OnWindowDestroying(aura::Window* window) {
116   if (window == always_on_top_container_) {
117     always_on_top_container_->RemoveObserver(this);
118     always_on_top_container_ = nullptr;
119   } else if (window == pip_container_) {
120     pip_container_->RemoveObserver(this);
121     pip_container_ = nullptr;
122   } else {
123     RemoveWindow(window);
124   }
125 }
126 
OnPreWindowStateTypeChange(WindowState * window_state,chromeos::WindowStateType old_type)127 void AlwaysOnTopController::OnPreWindowStateTypeChange(
128     WindowState* window_state,
129     chromeos::WindowStateType old_type) {
130   ReparentWindow(window_state->window());
131 }
132 
133 }  // namespace ash
134