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 "ui/wm/core/window_animations.h"
6
7#include "base/time/time.h"
8#include "ui/aura/test/aura_test_base.h"
9#include "ui/aura/test/test_windows.h"
10#include "ui/aura/window.h"
11#include "ui/compositor/layer.h"
12#include "ui/compositor/layer_animator.h"
13#include "ui/compositor/scoped_animation_duration_scale_mode.h"
14#include "ui/gfx/animation/animation_container_element.h"
15#include "ui/gfx/vector2d.h"
16#include "ui/wm/core/transient_window_manager.h"
17#include "ui/wm/core/transient_window_stacking_client.h"
18#include "ui/wm/core/window_util.h"
19#include "ui/wm/public/animation_host.h"
20
21using aura::Window;
22using ui::Layer;
23
24namespace wm {
25namespace {
26
27template<typename T>int GetZPosition(const T* child) {
28  const T* parent = child->parent();
29  const std::vector<T*> children = parent->children();
30  typename std::vector<T*>::const_iterator iter =
31      std::find(children.begin(), children.end(), child);
32  DCHECK(iter != children.end());
33  return iter - children.begin();
34}
35
36int GetWindowZPosition(const aura::Window* child) {
37  return GetZPosition<aura::Window>(child);
38}
39
40int GetLayerZPosition(const ui::Layer* child) {
41  return GetZPosition<ui::Layer>(child);
42}
43
44}  // namespace
45
46class WindowAnimationsTest : public aura::test::AuraTestBase {
47 public:
48  WindowAnimationsTest() {}
49
50  virtual void TearDown() OVERRIDE {
51    AuraTestBase::TearDown();
52  }
53
54 private:
55  DISALLOW_COPY_AND_ASSIGN(WindowAnimationsTest);
56};
57
58TEST_F(WindowAnimationsTest, LayerTargetVisibility) {
59  scoped_ptr<aura::Window> window(
60      aura::test::CreateTestWindowWithId(0, NULL));
61
62  // Layer target visibility changes according to Show/Hide.
63  window->Show();
64  EXPECT_TRUE(window->layer()->GetTargetVisibility());
65  window->Hide();
66  EXPECT_FALSE(window->layer()->GetTargetVisibility());
67  window->Show();
68  EXPECT_TRUE(window->layer()->GetTargetVisibility());
69}
70
71TEST_F(WindowAnimationsTest, LayerTargetVisibility_AnimateShow) {
72  // Tests if opacity and transform are reset when only show animation is
73  // enabled.  See also LayerTargetVisibility_AnimateHide.
74  // Since the window is not visible after Hide() is called, opacity and
75  // transform shouldn't matter in case of ANIMATE_SHOW, but we reset them
76  // to keep consistency.
77
78  scoped_ptr<aura::Window> window(aura::test::CreateTestWindowWithId(0, NULL));
79  SetWindowVisibilityAnimationTransition(window.get(), ANIMATE_SHOW);
80
81  // Layer target visibility and opacity change according to Show/Hide.
82  window->Show();
83  AnimateOnChildWindowVisibilityChanged(window.get(), true);
84  EXPECT_TRUE(window->layer()->GetTargetVisibility());
85  EXPECT_EQ(1, window->layer()->opacity());
86
87  window->Hide();
88  AnimateOnChildWindowVisibilityChanged(window.get(), false);
89  EXPECT_FALSE(window->layer()->GetTargetVisibility());
90  EXPECT_EQ(0, window->layer()->opacity());
91  EXPECT_EQ(gfx::Transform(), window->layer()->transform());
92
93  window->Show();
94  AnimateOnChildWindowVisibilityChanged(window.get(), true);
95  EXPECT_TRUE(window->layer()->GetTargetVisibility());
96  EXPECT_EQ(1, window->layer()->opacity());
97}
98
99TEST_F(WindowAnimationsTest, LayerTargetVisibility_AnimateHide) {
100  // Tests if opacity and transform are reset when only hide animation is
101  // enabled.  Hide animation changes opacity and transform in addition to
102  // visibility, so we need to reset not only visibility but also opacity
103  // and transform to show the window.
104
105  scoped_ptr<aura::Window> window(aura::test::CreateTestWindowWithId(0, NULL));
106  SetWindowVisibilityAnimationTransition(window.get(), ANIMATE_HIDE);
107
108  // Layer target visibility and opacity change according to Show/Hide.
109  window->Show();
110  AnimateOnChildWindowVisibilityChanged(window.get(), true);
111  EXPECT_TRUE(window->layer()->GetTargetVisibility());
112  EXPECT_EQ(1, window->layer()->opacity());
113  EXPECT_EQ(gfx::Transform(), window->layer()->transform());
114
115  window->Hide();
116  AnimateOnChildWindowVisibilityChanged(window.get(), false);
117  EXPECT_FALSE(window->layer()->GetTargetVisibility());
118  EXPECT_EQ(0, window->layer()->opacity());
119
120  window->Show();
121  AnimateOnChildWindowVisibilityChanged(window.get(), true);
122  EXPECT_TRUE(window->layer()->GetTargetVisibility());
123  EXPECT_EQ(1, window->layer()->opacity());
124  EXPECT_EQ(gfx::Transform(), window->layer()->transform());
125}
126
127TEST_F(WindowAnimationsTest, HideAnimationDetachLayers) {
128  scoped_ptr<aura::Window> parent(aura::test::CreateTestWindowWithId(0, NULL));
129
130  scoped_ptr<aura::Window> other(
131      aura::test::CreateTestWindowWithId(1, parent.get()));
132
133  scoped_ptr<aura::Window> animating_window(
134      aura::test::CreateTestWindowWithId(2, parent.get()));
135  SetWindowVisibilityAnimationTransition(animating_window.get(), ANIMATE_HIDE);
136
137  EXPECT_EQ(0, GetWindowZPosition(other.get()));
138  EXPECT_EQ(1, GetWindowZPosition(animating_window.get()));
139  EXPECT_EQ(0, GetLayerZPosition(other->layer()));
140  EXPECT_EQ(1, GetLayerZPosition(animating_window->layer()));
141
142  {
143    ui::ScopedAnimationDurationScaleMode scale_mode(
144        ui::ScopedAnimationDurationScaleMode::FAST_DURATION);
145    ui::Layer* animating_layer = animating_window->layer();
146
147    animating_window->Hide();
148    EXPECT_TRUE(AnimateOnChildWindowVisibilityChanged(
149        animating_window.get(), false));
150    EXPECT_TRUE(animating_layer->GetAnimator()->is_animating());
151    EXPECT_FALSE(animating_layer->delegate());
152
153    // Make sure the Hide animation create another layer, and both are in
154    // the parent layer.
155    EXPECT_NE(animating_window->layer(), animating_layer);
156    EXPECT_TRUE(
157        std::find(parent->layer()->children().begin(),
158                  parent->layer()->children().end(),
159                  animating_layer) !=
160        parent->layer()->children().end());
161    EXPECT_TRUE(
162        std::find(parent->layer()->children().begin(),
163                  parent->layer()->children().end(),
164                  animating_window->layer()) !=
165        parent->layer()->children().end());
166    // Current layer must be already hidden.
167    EXPECT_FALSE(animating_window->layer()->visible());
168
169    EXPECT_EQ(1, GetWindowZPosition(animating_window.get()));
170    EXPECT_EQ(1, GetLayerZPosition(animating_window->layer()));
171    EXPECT_EQ(2, GetLayerZPosition(animating_layer));
172
173    parent->StackChildAtTop(other.get());
174    EXPECT_EQ(0, GetWindowZPosition(animating_window.get()));
175    EXPECT_EQ(1, GetWindowZPosition(other.get()));
176
177    EXPECT_EQ(0, GetLayerZPosition(animating_window->layer()));
178    EXPECT_EQ(1, GetLayerZPosition(other->layer()));
179    // Make sure the animating layer is on top.
180    EXPECT_EQ(2, GetLayerZPosition(animating_layer));
181
182    // Animating layer must be gone
183    animating_layer->GetAnimator()->StopAnimating();
184    EXPECT_TRUE(
185        std::find(parent->layer()->children().begin(),
186                  parent->layer()->children().end(),
187                  animating_layer) ==
188        parent->layer()->children().end());
189  }
190}
191
192TEST_F(WindowAnimationsTest, HideAnimationDetachLayersWithTransientChildren) {
193  TransientWindowStackingClient transient_stacking_client;
194
195  scoped_ptr<aura::Window> parent(aura::test::CreateTestWindowWithId(0, NULL));
196
197  scoped_ptr<aura::Window> other(
198      aura::test::CreateTestWindowWithId(1, parent.get()));
199
200  scoped_ptr<aura::Window> animating_window(
201      aura::test::CreateTestWindowWithId(2, parent.get()));
202  SetWindowVisibilityAnimationTransition(animating_window.get(), ANIMATE_HIDE);
203
204  scoped_ptr<aura::Window> transient1(
205      aura::test::CreateTestWindowWithId(3, parent.get()));
206  scoped_ptr<aura::Window> transient2(
207      aura::test::CreateTestWindowWithId(4, parent.get()));
208
209  TransientWindowManager::Get(animating_window.get());
210  AddTransientChild(animating_window.get(), transient1.get());
211  AddTransientChild(animating_window.get(), transient2.get());
212
213  EXPECT_EQ(0, GetWindowZPosition(other.get()));
214  EXPECT_EQ(1, GetWindowZPosition(animating_window.get()));
215  EXPECT_EQ(2, GetWindowZPosition(transient1.get()));
216  EXPECT_EQ(3, GetWindowZPosition(transient2.get()));
217
218  {
219    ui::ScopedAnimationDurationScaleMode scale_mode(
220        ui::ScopedAnimationDurationScaleMode::FAST_DURATION);
221    ui::Layer* animating_layer = animating_window->layer();
222
223    animating_window->Hide();
224    EXPECT_TRUE(AnimateOnChildWindowVisibilityChanged(
225        animating_window.get(), false));
226    EXPECT_TRUE(animating_layer->GetAnimator()->is_animating());
227    EXPECT_FALSE(animating_layer->delegate());
228
229    EXPECT_EQ(1, GetWindowZPosition(animating_window.get()));
230    EXPECT_EQ(2, GetWindowZPosition(transient1.get()));
231    EXPECT_EQ(3, GetWindowZPosition(transient2.get()));
232
233    EXPECT_EQ(1, GetLayerZPosition(animating_window->layer()));
234    EXPECT_EQ(2, GetLayerZPosition(transient1->layer()));
235    EXPECT_EQ(3, GetLayerZPosition(transient2->layer()));
236    EXPECT_EQ(4, GetLayerZPosition(animating_layer));
237
238    parent->StackChildAtTop(other.get());
239
240    EXPECT_EQ(0, GetWindowZPosition(animating_window.get()));
241    EXPECT_EQ(1, GetWindowZPosition(transient1.get()));
242    EXPECT_EQ(2, GetWindowZPosition(transient2.get()));
243    EXPECT_EQ(3, GetWindowZPosition(other.get()));
244
245    EXPECT_EQ(0, GetLayerZPosition(animating_window->layer()));
246    EXPECT_EQ(1, GetLayerZPosition(transient1->layer()));
247    EXPECT_EQ(2, GetLayerZPosition(transient2->layer()));
248    EXPECT_EQ(3, GetLayerZPosition(other->layer()));
249    // Make sure the animating layer is on top of all windows.
250    EXPECT_EQ(4, GetLayerZPosition(animating_layer));
251  }
252}
253
254// A simple AnimationHost implementation for the NotifyHideCompleted test.
255class NotifyHideCompletedAnimationHost : public aura::client::AnimationHost {
256 public:
257  NotifyHideCompletedAnimationHost() : hide_completed_(false) {}
258  virtual ~NotifyHideCompletedAnimationHost() {}
259
260  // Overridden from TestWindowDelegate:
261  virtual void OnWindowHidingAnimationCompleted() OVERRIDE {
262    hide_completed_ = true;
263  }
264
265  virtual void SetHostTransitionOffsets(
266      const gfx::Vector2d& top_left,
267      const gfx::Vector2d& bottom_right) OVERRIDE {}
268
269  bool hide_completed() const { return hide_completed_; }
270
271 private:
272  bool hide_completed_;
273
274  DISALLOW_COPY_AND_ASSIGN(NotifyHideCompletedAnimationHost);
275};
276
277TEST_F(WindowAnimationsTest, NotifyHideCompleted) {
278  NotifyHideCompletedAnimationHost animation_host;
279  scoped_ptr<aura::Window> window(aura::test::CreateTestWindowWithId(0, NULL));
280  aura::client::SetAnimationHost(window.get(), &animation_host);
281  wm::SetWindowVisibilityAnimationType(
282      window.get(), WINDOW_VISIBILITY_ANIMATION_TYPE_FADE);
283  AnimateOnChildWindowVisibilityChanged(window.get(), true);
284  EXPECT_TRUE(window->layer()->visible());
285
286  EXPECT_FALSE(animation_host.hide_completed());
287  AnimateOnChildWindowVisibilityChanged(window.get(), false);
288  EXPECT_TRUE(animation_host.hide_completed());
289}
290
291// The rotation animation for hiding a window should not leak the animation
292// observer.
293TEST_F(WindowAnimationsTest, RotateHideNoLeak) {
294  ui::ScopedAnimationDurationScaleMode scale_mode(
295      ui::ScopedAnimationDurationScaleMode::FAST_DURATION);
296
297  scoped_ptr<aura::Window> window(aura::test::CreateTestWindowWithId(0, NULL));
298  ui::Layer* animating_layer = window->layer();
299  wm::SetWindowVisibilityAnimationType(window.get(),
300                                       WINDOW_VISIBILITY_ANIMATION_TYPE_ROTATE);
301
302  AnimateOnChildWindowVisibilityChanged(window.get(), true);
303  AnimateOnChildWindowVisibilityChanged(window.get(), false);
304
305  animating_layer->GetAnimator()->StopAnimating();
306}
307
308}  // namespace wm
309