1 // Copyright 2014 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/window_state.h"
6 
7 #include <utility>
8 
9 #include "ash/metrics/pip_uma.h"
10 #include "ash/public/cpp/app_types.h"
11 #include "ash/public/cpp/shelf_config.h"
12 #include "ash/public/cpp/window_properties.h"
13 #include "ash/test/ash_test_base.h"
14 #include "ash/wm/pip/pip_positioner.h"
15 #include "ash/wm/window_state_util.h"
16 #include "ash/wm/window_util.h"
17 #include "ash/wm/wm_event.h"
18 #include "base/test/metrics/histogram_tester.h"
19 #include "ui/aura/client/aura_constants.h"
20 #include "ui/aura/test/test_window_delegate.h"
21 #include "ui/aura/window.h"
22 #include "ui/base/hit_test.h"
23 #include "ui/display/screen.h"
24 #include "ui/events/test/event_generator.h"
25 #include "ui/views/widget/widget.h"
26 #include "ui/wm/core/window_util.h"
27 
28 using chromeos::WindowStateType;
29 
30 namespace ash {
31 namespace {
32 
33 class AlwaysMaximizeTestState : public WindowState::State {
34  public:
AlwaysMaximizeTestState(WindowStateType initial_state_type)35   explicit AlwaysMaximizeTestState(WindowStateType initial_state_type)
36       : state_type_(initial_state_type) {}
37   ~AlwaysMaximizeTestState() override = default;
38 
39   // WindowState::State overrides:
OnWMEvent(WindowState * window_state,const WMEvent * event)40   void OnWMEvent(WindowState* window_state, const WMEvent* event) override {
41     // We don't do anything here.
42   }
GetType() const43   WindowStateType GetType() const override { return state_type_; }
AttachState(WindowState * window_state,WindowState::State * previous_state)44   void AttachState(WindowState* window_state,
45                    WindowState::State* previous_state) override {
46     // We always maximize.
47     if (state_type_ != WindowStateType::kMaximized) {
48       window_state->Maximize();
49       state_type_ = WindowStateType::kMaximized;
50     }
51   }
DetachState(WindowState * window_state)52   void DetachState(WindowState* window_state) override {}
53 
54  private:
55   WindowStateType state_type_;
56 
57   DISALLOW_COPY_AND_ASSIGN(AlwaysMaximizeTestState);
58 };
59 
60 using WindowStateTest = AshTestBase;
61 using Sample = base::HistogramBase::Sample;
62 
63 // Test that a window gets properly snapped to the display's edges in a
64 // multi monitor environment.
TEST_F(WindowStateTest,SnapWindowBasic)65 TEST_F(WindowStateTest, SnapWindowBasic) {
66   UpdateDisplay("0+0-500x400, 0+500-600x400");
67   const gfx::Rect kPrimaryDisplayWorkAreaBounds =
68       display::Screen::GetScreen()->GetPrimaryDisplay().work_area();
69   const gfx::Rect kSecondaryDisplayWorkAreaBounds =
70       GetSecondaryDisplay().work_area();
71 
72   std::unique_ptr<aura::Window> window(
73       CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100)));
74   WindowState* window_state = WindowState::Get(window.get());
75   const WMEvent snap_left(WM_EVENT_SNAP_LEFT);
76   window_state->OnWMEvent(&snap_left);
77   gfx::Rect expected = gfx::Rect(kPrimaryDisplayWorkAreaBounds.x(),
78                                  kPrimaryDisplayWorkAreaBounds.y(),
79                                  kPrimaryDisplayWorkAreaBounds.width() / 2,
80                                  kPrimaryDisplayWorkAreaBounds.height());
81   EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
82 
83   const WMEvent snap_right(WM_EVENT_SNAP_RIGHT);
84   window_state->OnWMEvent(&snap_right);
85   expected.set_x(kPrimaryDisplayWorkAreaBounds.right() - expected.width());
86   EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
87 
88   // Move the window to the secondary display.
89   window->SetBoundsInScreen(gfx::Rect(600, 0, 100, 100), GetSecondaryDisplay());
90 
91   window_state->OnWMEvent(&snap_right);
92   expected = gfx::Rect(kSecondaryDisplayWorkAreaBounds.x() +
93                            kSecondaryDisplayWorkAreaBounds.width() / 2,
94                        kSecondaryDisplayWorkAreaBounds.y(),
95                        kSecondaryDisplayWorkAreaBounds.width() / 2,
96                        kSecondaryDisplayWorkAreaBounds.height());
97   EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
98 
99   window_state->OnWMEvent(&snap_left);
100   expected.set_x(kSecondaryDisplayWorkAreaBounds.x());
101   EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
102 }
103 
104 // Test how the minimum and maximum size specified by the aura::WindowDelegate
105 // affect snapping.
TEST_F(WindowStateTest,SnapWindowMinimumSize)106 TEST_F(WindowStateTest, SnapWindowMinimumSize) {
107   UpdateDisplay("0+0-600x900");
108   const gfx::Rect kWorkAreaBounds =
109       display::Screen::GetScreen()->GetPrimaryDisplay().work_area();
110 
111   aura::test::TestWindowDelegate delegate;
112   std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate(
113       &delegate, -1, gfx::Rect(0, 100, kWorkAreaBounds.width() - 1, 100)));
114 
115   // It should be possible to snap a window with a minimum size.
116   delegate.set_minimum_size(gfx::Size(kWorkAreaBounds.width() - 1, 0));
117   WindowState* window_state = WindowState::Get(window.get());
118   EXPECT_TRUE(window_state->CanSnap());
119   const WMEvent snap_right(WM_EVENT_SNAP_RIGHT);
120   window_state->OnWMEvent(&snap_right);
121   gfx::Rect expected =
122       gfx::Rect(kWorkAreaBounds.x() + 1, kWorkAreaBounds.y(),
123                 kWorkAreaBounds.width() - 1, kWorkAreaBounds.height());
124   EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
125 
126   // It should not be possible to snap a window with a maximum size defined.
127   delegate.set_maximum_size(gfx::Size(kWorkAreaBounds.width() - 1, 0));
128   EXPECT_FALSE(window_state->CanSnap());
129   delegate.set_maximum_size(gfx::Size(0, kWorkAreaBounds.height() - 1));
130   EXPECT_FALSE(window_state->CanSnap());
131   delegate.set_maximum_size(gfx::Size());
132   window->SetProperty(aura::client::kResizeBehaviorKey,
133                       aura::client::kResizeBehaviorCanResize);
134   // It should be possible to snap a window with a maximum size, if it
135   // can be maximized.
136   EXPECT_TRUE(window_state->CanSnap());
137 }
138 
139 // Test that a window's state type can be changed to PIP via a WM transition
140 // event.
TEST_F(WindowStateTest,CanTransitionToPipWindow)141 TEST_F(WindowStateTest, CanTransitionToPipWindow) {
142   std::unique_ptr<aura::Window> window(
143       CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100)));
144 
145   WindowState* window_state = WindowState::Get(window.get());
146   EXPECT_FALSE(window_state->IsPip());
147 
148   const WMEvent enter_pip(WM_EVENT_PIP);
149   window_state->OnWMEvent(&enter_pip);
150   EXPECT_TRUE(window_state->IsPip());
151 }
152 
153 // Test that a PIP window cannot be snapped.
TEST_F(WindowStateTest,PipWindowCannotSnap)154 TEST_F(WindowStateTest, PipWindowCannotSnap) {
155   std::unique_ptr<aura::Window> window(
156       CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100)));
157 
158   WindowState* window_state = WindowState::Get(window.get());
159   EXPECT_TRUE(window_state->CanSnap());
160 
161   const WMEvent enter_pip(WM_EVENT_PIP);
162   window_state->OnWMEvent(&enter_pip);
163 
164   EXPECT_FALSE(window_state->CanSnap());
165 }
166 
TEST_F(WindowStateTest,ChromePipWindowUmaMetrics)167 TEST_F(WindowStateTest, ChromePipWindowUmaMetrics) {
168   base::HistogramTester histograms;
169   std::unique_ptr<aura::Window> window(
170       CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100)));
171 
172   WindowState* window_state = WindowState::Get(window.get());
173   const WMEvent enter_pip(WM_EVENT_PIP);
174   window_state->OnWMEvent(&enter_pip);
175 
176   EXPECT_EQ(1, histograms.GetBucketCount(kAshPipEventsHistogramName,
177                                          Sample(AshPipEvents::PIP_START)));
178   EXPECT_EQ(1,
179             histograms.GetBucketCount(kAshPipEventsHistogramName,
180                                       Sample(AshPipEvents::CHROME_PIP_START)));
181   histograms.ExpectTotalCount(kAshPipEventsHistogramName, 2);
182 
183   const WMEvent enter_normal(WM_EVENT_NORMAL);
184   window_state->OnWMEvent(&enter_normal);
185 
186   EXPECT_EQ(1, histograms.GetBucketCount(kAshPipEventsHistogramName,
187                                          Sample(AshPipEvents::PIP_END)));
188   EXPECT_EQ(1, histograms.GetBucketCount(kAshPipEventsHistogramName,
189                                          Sample(AshPipEvents::CHROME_PIP_END)));
190   histograms.ExpectTotalCount(kAshPipEventsHistogramName, 4);
191 }
192 
TEST_F(WindowStateTest,AndroidPipWindowUmaMetrics)193 TEST_F(WindowStateTest, AndroidPipWindowUmaMetrics) {
194   base::HistogramTester histograms;
195   std::unique_ptr<aura::Window> window(
196       CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100)));
197   window->SetProperty(aura::client::kAppType,
198                       static_cast<int>(ash::AppType::ARC_APP));
199 
200   WindowState* window_state = WindowState::Get(window.get());
201   const WMEvent enter_pip(WM_EVENT_PIP);
202   window_state->OnWMEvent(&enter_pip);
203 
204   EXPECT_EQ(1, histograms.GetBucketCount(kAshPipEventsHistogramName,
205                                          Sample(AshPipEvents::PIP_START)));
206   EXPECT_EQ(1,
207             histograms.GetBucketCount(kAshPipEventsHistogramName,
208                                       Sample(AshPipEvents::ANDROID_PIP_START)));
209   histograms.ExpectTotalCount(kAshPipEventsHistogramName, 2);
210 
211   const WMEvent enter_normal(WM_EVENT_NORMAL);
212   window_state->OnWMEvent(&enter_normal);
213 
214   EXPECT_EQ(1, histograms.GetBucketCount(kAshPipEventsHistogramName,
215                                          Sample(AshPipEvents::PIP_END)));
216   EXPECT_EQ(1,
217             histograms.GetBucketCount(kAshPipEventsHistogramName,
218                                       Sample(AshPipEvents::ANDROID_PIP_END)));
219   histograms.ExpectTotalCount(kAshPipEventsHistogramName, 4);
220 
221   // Check time count:
222   histograms.ExpectTotalCount(kAshPipAndroidPipUseTimeHistogramName, 1);
223 }
224 
TEST_F(WindowStateTest,ChromePipWindowUmaMetricsCountsExitOnDestroy)225 TEST_F(WindowStateTest, ChromePipWindowUmaMetricsCountsExitOnDestroy) {
226   base::HistogramTester histograms;
227   std::unique_ptr<aura::Window> window(
228       CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100)));
229 
230   WindowState* window_state = WindowState::Get(window.get());
231   const WMEvent enter_pip(WM_EVENT_PIP);
232   window_state->OnWMEvent(&enter_pip);
233 
234   // Destroy the window.
235   window.reset();
236 
237   EXPECT_EQ(1, histograms.GetBucketCount(kAshPipEventsHistogramName,
238                                          Sample(AshPipEvents::PIP_END)));
239   EXPECT_EQ(1, histograms.GetBucketCount(kAshPipEventsHistogramName,
240                                          Sample(AshPipEvents::CHROME_PIP_END)));
241   histograms.ExpectTotalCount(kAshPipEventsHistogramName, 4);
242 }
243 
TEST_F(WindowStateTest,AndroidPipWindowUmaMetricsCountsExitOnDestroy)244 TEST_F(WindowStateTest, AndroidPipWindowUmaMetricsCountsExitOnDestroy) {
245   base::HistogramTester histograms;
246   std::unique_ptr<aura::Window> window(
247       CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100)));
248   window->SetProperty(aura::client::kAppType,
249                       static_cast<int>(ash::AppType::ARC_APP));
250 
251   WindowState* window_state = WindowState::Get(window.get());
252   const WMEvent enter_pip(WM_EVENT_PIP);
253   window_state->OnWMEvent(&enter_pip);
254 
255   // Destroy the window.
256   window.reset();
257 
258   EXPECT_EQ(1, histograms.GetBucketCount(kAshPipEventsHistogramName,
259                                          Sample(AshPipEvents::PIP_END)));
260   EXPECT_EQ(1,
261             histograms.GetBucketCount(kAshPipEventsHistogramName,
262                                       Sample(AshPipEvents::ANDROID_PIP_END)));
263   histograms.ExpectTotalCount(kAshPipEventsHistogramName, 4);
264 }
265 
266 // Test that modal window dialogs can be snapped.
TEST_F(WindowStateTest,SnapModalWindowWithoutMaximumSizeLimit)267 TEST_F(WindowStateTest, SnapModalWindowWithoutMaximumSizeLimit) {
268   UpdateDisplay("0+0-600x900");
269   const gfx::Rect kWorkAreaBounds =
270       display::Screen::GetScreen()->GetPrimaryDisplay().work_area();
271 
272   aura::test::TestWindowDelegate parent_delegate;
273   std::unique_ptr<aura::Window> parent_window(
274       CreateTestWindowInShellWithDelegate(
275           &parent_delegate, -1,
276           gfx::Rect(kWorkAreaBounds.width(), 0, kWorkAreaBounds.width() / 2,
277                     kWorkAreaBounds.height() - 1)));
278 
279   aura::test::TestWindowDelegate delegate;
280   std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate(
281       &delegate, -1, gfx::Rect(100, 100, 400, 500)));
282 
283   delegate.set_minimum_size(gfx::Size(200, 300));
284   WindowState* window_state = WindowState::Get(window.get());
285   EXPECT_TRUE(window_state->CanSnap());
286 
287   window->SetProperty(aura::client::kResizeBehaviorKey,
288                       aura::client::kResizeBehaviorCanResize |
289                           aura::client::kResizeBehaviorCanMaximize);
290   delegate.set_maximum_size(gfx::Size());
291   EXPECT_TRUE(window_state->CanSnap());
292 
293   ::wm::AddTransientChild(parent_window.get(), window.get());
294   EXPECT_TRUE(window_state->CanSnap());
295 
296   delegate.set_maximum_size(gfx::Size());
297   EXPECT_TRUE(window_state->CanSnap());
298 
299   window->SetProperty(aura::client::kResizeBehaviorKey,
300                       aura::client::kResizeBehaviorCanResize);
301   EXPECT_TRUE(window_state->CanSnap());
302 
303   // It should be possible to snap a modal window without maximum size.
304   window->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW);
305   EXPECT_TRUE(window_state->CanSnap());
306 
307   delegate.set_maximum_size(gfx::Size(300, 400));
308   EXPECT_FALSE(window_state->CanSnap());
309 
310   ::wm::RemoveTransientChild(parent_window.get(), window.get());
311 }
312 
313 // Test that the minimum size specified by aura::WindowDelegate gets respected.
TEST_F(WindowStateTest,TestRespectMinimumSize)314 TEST_F(WindowStateTest, TestRespectMinimumSize) {
315   UpdateDisplay("0+0-1024x768");
316 
317   aura::test::TestWindowDelegate delegate;
318   const gfx::Size minimum_size(gfx::Size(500, 300));
319   delegate.set_minimum_size(minimum_size);
320 
321   std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate(
322       &delegate, -1, gfx::Rect(0, 100, 100, 100)));
323 
324   // Check that the window has the correct minimum size.
325   EXPECT_EQ(minimum_size.ToString(), window->bounds().size().ToString());
326 
327   // Set the size to something bigger - that should work.
328   gfx::Rect bigger_bounds(700, 500, 700, 500);
329   window->SetBounds(bigger_bounds);
330   EXPECT_EQ(bigger_bounds.ToString(), window->bounds().ToString());
331 
332   // Set the size to something smaller - that should only resize to the smallest
333   // possible size.
334   gfx::Rect smaller_bounds(700, 500, 100, 100);
335   window->SetBounds(smaller_bounds);
336   EXPECT_EQ(minimum_size.ToString(), window->bounds().size().ToString());
337 }
338 
339 // Test that the minimum window size specified by aura::WindowDelegate does not
340 // exceed the screen size.
TEST_F(WindowStateTest,TestIgnoreTooBigMinimumSize)341 TEST_F(WindowStateTest, TestIgnoreTooBigMinimumSize) {
342   UpdateDisplay("0+0-1024x768");
343   const gfx::Size work_area_size =
344       display::Screen::GetScreen()->GetPrimaryDisplay().work_area().size();
345   const gfx::Size illegal_size(1280, 960);
346   const gfx::Rect illegal_bounds(gfx::Point(0, 0), illegal_size);
347 
348   aura::test::TestWindowDelegate delegate;
349   const gfx::Size minimum_size(illegal_size);
350   delegate.set_minimum_size(minimum_size);
351 
352   // The creation should force the window to respect the screen size.
353   std::unique_ptr<aura::Window> window(
354       CreateTestWindowInShellWithDelegate(&delegate, -1, illegal_bounds));
355   EXPECT_EQ(work_area_size.ToString(), window->bounds().size().ToString());
356 
357   // Trying to set the size to something bigger then the screen size should be
358   // ignored.
359   window->SetBounds(illegal_bounds);
360   EXPECT_EQ(work_area_size.ToString(), window->bounds().size().ToString());
361 
362   // Maximizing the window should not allow it to go bigger than that either.
363   WindowState* window_state = WindowState::Get(window.get());
364   window_state->Maximize();
365   EXPECT_EQ(work_area_size.ToString(), window->bounds().size().ToString());
366 }
367 
368 // Tests UpdateSnappedWidthRatio. (1) It should have ratio reset when window
369 // enters snapped state; (2) it should update ratio on bounds event when
370 // snapped.
TEST_F(WindowStateTest,UpdateSnapWidthRatioTest)371 TEST_F(WindowStateTest, UpdateSnapWidthRatioTest) {
372   UpdateDisplay("0+0-900x600");
373   const gfx::Rect kWorkAreaBounds =
374       display::Screen::GetScreen()->GetPrimaryDisplay().work_area();
375   aura::test::TestWindowDelegate delegate;
376   std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate(
377       &delegate, -1, gfx::Rect(100, 100, 100, 100)));
378   delegate.set_window_component(HTRIGHT);
379   WindowState* window_state = WindowState::Get(window.get());
380   const WMEvent cycle_snap_left(WM_EVENT_CYCLE_SNAP_LEFT);
381   window_state->OnWMEvent(&cycle_snap_left);
382   EXPECT_EQ(WindowStateType::kLeftSnapped, window_state->GetStateType());
383   gfx::Rect expected =
384       gfx::Rect(kWorkAreaBounds.x(), kWorkAreaBounds.y(),
385                 kWorkAreaBounds.width() / 2, kWorkAreaBounds.height());
386   EXPECT_EQ(expected, window->GetBoundsInScreen());
387   EXPECT_EQ(0.5f, *window_state->snapped_width_ratio());
388 
389   // Drag to change snapped window width.
390   const int kIncreasedWidth = 225;
391   ui::test::EventGenerator* generator = GetEventGenerator();
392   generator->MoveMouseTo(window->bounds().right(), window->bounds().y());
393   generator->PressLeftButton();
394   generator->MoveMouseTo(window->bounds().right() + kIncreasedWidth,
395                          window->bounds().y());
396   generator->ReleaseLeftButton();
397   expected.set_width(expected.width() + kIncreasedWidth);
398   EXPECT_EQ(expected, window->GetBoundsInScreen());
399   EXPECT_EQ(WindowStateType::kLeftSnapped, window_state->GetStateType());
400   EXPECT_EQ(0.75f, *window_state->snapped_width_ratio());
401 
402   // Another cycle snap left event will restore window state to normal.
403   window_state->OnWMEvent(&cycle_snap_left);
404   EXPECT_EQ(WindowStateType::kNormal, window_state->GetStateType());
405   EXPECT_FALSE(window_state->snapped_width_ratio());
406 
407   // Another cycle snap left event will snap window and reset snapped width
408   // ratio.
409   window_state->OnWMEvent(&cycle_snap_left);
410   EXPECT_EQ(WindowStateType::kLeftSnapped, window_state->GetStateType());
411   EXPECT_EQ(0.5f, *window_state->snapped_width_ratio());
412 }
413 
414 // Test that snapping left/right preserves the restore bounds.
TEST_F(WindowStateTest,RestoreBounds)415 TEST_F(WindowStateTest, RestoreBounds) {
416   std::unique_ptr<aura::Window> window(
417       CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100)));
418   WindowState* window_state = WindowState::Get(window.get());
419 
420   EXPECT_TRUE(window_state->IsNormalStateType());
421 
422   // 1) Start with restored window with restore bounds set.
423   gfx::Rect restore_bounds = window->GetBoundsInScreen();
424   restore_bounds.set_width(restore_bounds.width() + 1);
425   window_state->SetRestoreBoundsInScreen(restore_bounds);
426   const WMEvent snap_left(WM_EVENT_SNAP_LEFT);
427   window_state->OnWMEvent(&snap_left);
428   const WMEvent snap_right(WM_EVENT_SNAP_RIGHT);
429   window_state->OnWMEvent(&snap_right);
430   EXPECT_NE(restore_bounds.ToString(), window->GetBoundsInScreen().ToString());
431   EXPECT_EQ(restore_bounds.ToString(),
432             window_state->GetRestoreBoundsInScreen().ToString());
433   window_state->Restore();
434   EXPECT_EQ(restore_bounds.ToString(), window->GetBoundsInScreen().ToString());
435 
436   // 2) Start with restored bounds set as a result of maximizing the window.
437   window_state->Maximize();
438   gfx::Rect maximized_bounds = window->GetBoundsInScreen();
439   EXPECT_NE(maximized_bounds.ToString(), restore_bounds.ToString());
440   EXPECT_EQ(restore_bounds.ToString(),
441             window_state->GetRestoreBoundsInScreen().ToString());
442 
443   window_state->OnWMEvent(&snap_left);
444   EXPECT_NE(restore_bounds.ToString(), window->GetBoundsInScreen().ToString());
445   EXPECT_NE(maximized_bounds.ToString(),
446             window->GetBoundsInScreen().ToString());
447   EXPECT_EQ(restore_bounds.ToString(),
448             window_state->GetRestoreBoundsInScreen().ToString());
449 
450   window_state->Restore();
451   EXPECT_EQ(restore_bounds.ToString(), window->GetBoundsInScreen().ToString());
452 }
453 
454 // Test that maximizing an auto managed window, then snapping it puts the window
455 // at the snapped bounds and not at the auto-managed (centered) bounds.
TEST_F(WindowStateTest,AutoManaged)456 TEST_F(WindowStateTest, AutoManaged) {
457   std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
458   WindowState* window_state = WindowState::Get(window.get());
459   window_state->SetWindowPositionManaged(true);
460   window->Hide();
461   window->SetBounds(gfx::Rect(100, 100, 100, 100));
462   window->Show();
463 
464   window_state->Maximize();
465   const WMEvent snap_right(WM_EVENT_SNAP_RIGHT);
466   window_state->OnWMEvent(&snap_right);
467 
468   const gfx::Rect kWorkAreaBounds =
469       display::Screen::GetScreen()->GetPrimaryDisplay().work_area();
470   gfx::Rect expected_snapped_bounds(
471       kWorkAreaBounds.x() + kWorkAreaBounds.width() / 2, kWorkAreaBounds.y(),
472       kWorkAreaBounds.width() / 2, kWorkAreaBounds.height());
473   EXPECT_EQ(expected_snapped_bounds.ToString(),
474             window->GetBoundsInScreen().ToString());
475 
476   // The window should still be auto managed despite being right maximized.
477   EXPECT_TRUE(window_state->GetWindowPositionManaged());
478 }
479 
480 // Test that the replacement of a State object works as expected.
TEST_F(WindowStateTest,SimpleStateSwap)481 TEST_F(WindowStateTest, SimpleStateSwap) {
482   std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
483   WindowState* window_state = WindowState::Get(window.get());
484   EXPECT_FALSE(window_state->IsMaximized());
485   window_state->SetStateObject(std::unique_ptr<WindowState::State>(
486       new AlwaysMaximizeTestState(window_state->GetStateType())));
487   EXPECT_TRUE(window_state->IsMaximized());
488 }
489 
490 // Test that the replacement of a state object, following a restore with the
491 // original one restores the window to its original state.
TEST_F(WindowStateTest,StateSwapRestore)492 TEST_F(WindowStateTest, StateSwapRestore) {
493   std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
494   WindowState* window_state = WindowState::Get(window.get());
495   EXPECT_FALSE(window_state->IsMaximized());
496   std::unique_ptr<WindowState::State> old(
497       window_state->SetStateObject(std::unique_ptr<WindowState::State>(
498           new AlwaysMaximizeTestState(window_state->GetStateType()))));
499   EXPECT_TRUE(window_state->IsMaximized());
500   window_state->SetStateObject(std::move(old));
501   EXPECT_FALSE(window_state->IsMaximized());
502 }
503 
504 // Tests that a window that had same bounds as the work area shrinks after the
505 // window is maximized and then restored.
TEST_F(WindowStateTest,RestoredWindowBoundsShrink)506 TEST_F(WindowStateTest, RestoredWindowBoundsShrink) {
507   UpdateDisplay("0+0-600x900");
508   std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
509   WindowState* window_state = WindowState::Get(window.get());
510   EXPECT_FALSE(window_state->IsMaximized());
511   gfx::Rect work_area =
512       display::Screen::GetScreen()->GetPrimaryDisplay().work_area();
513 
514   window->SetBounds(work_area);
515   window_state->Maximize();
516   EXPECT_TRUE(window_state->IsMaximized());
517   EXPECT_EQ(work_area.ToString(), window->bounds().ToString());
518 
519   window_state->Restore();
520   EXPECT_FALSE(window_state->IsMaximized());
521   EXPECT_NE(work_area.ToString(), window->bounds().ToString());
522   EXPECT_TRUE(work_area.Contains(window->bounds()));
523 }
524 
TEST_F(WindowStateTest,DoNotResizeMaximizedWindowInFullscreen)525 TEST_F(WindowStateTest, DoNotResizeMaximizedWindowInFullscreen) {
526   const int shelf_inset_first = 600 - ShelfConfig::Get()->shelf_size();
527   const int shelf_inset_second = 700 - ShelfConfig::Get()->shelf_size();
528   std::unique_ptr<aura::Window> maximized(CreateTestWindowInShellWithId(0));
529   std::unique_ptr<aura::Window> fullscreen(CreateTestWindowInShellWithId(1));
530   WindowState* maximized_state = WindowState::Get(maximized.get());
531   maximized_state->Maximize();
532   ASSERT_TRUE(maximized_state->IsMaximized());
533   EXPECT_EQ(gfx::Rect(0, 0, 800, shelf_inset_first).ToString(),
534             maximized->GetBoundsInScreen().ToString());
535 
536   // Entering fullscreen mode will not update the maximized window's size
537   // under fullscreen.
538   WMEvent fullscreen_event(WM_EVENT_FULLSCREEN);
539   WindowState* fullscreen_state = WindowState::Get(fullscreen.get());
540   fullscreen_state->OnWMEvent(&fullscreen_event);
541   ASSERT_TRUE(fullscreen_state->IsFullscreen());
542   ASSERT_TRUE(maximized_state->IsMaximized());
543   EXPECT_EQ(gfx::Rect(0, 0, 800, shelf_inset_first).ToString(),
544             maximized->GetBoundsInScreen().ToString());
545 
546   // Updating display size will update the maximum window size.
547   UpdateDisplay("900x700");
548   EXPECT_EQ("0,0 900x700", maximized->GetBoundsInScreen().ToString());
549   fullscreen.reset();
550 
551   // Exiting fullscreen will update the maximized window to the work area.
552   EXPECT_EQ(gfx::Rect(0, 0, 900, shelf_inset_second).ToString(),
553             maximized->GetBoundsInScreen().ToString());
554 }
555 
TEST_F(WindowStateTest,TrustedPinned)556 TEST_F(WindowStateTest, TrustedPinned) {
557   std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
558   WindowState* window_state = WindowState::Get(window.get());
559   EXPECT_FALSE(window_state->IsTrustedPinned());
560   window_util::PinWindow(window.get(), true /* trusted */);
561   EXPECT_TRUE(window_state->IsTrustedPinned());
562 
563   gfx::Rect work_area =
564       display::Screen::GetScreen()->GetPrimaryDisplay().work_area();
565   EXPECT_EQ(work_area.ToString(), window->bounds().ToString());
566 
567   // Sending non-unpin/non-workspace related event should be ignored.
568   {
569     const WMEvent fullscreen_event(WM_EVENT_FULLSCREEN);
570     window_state->OnWMEvent(&fullscreen_event);
571   }
572   EXPECT_TRUE(window_state->IsTrustedPinned());
573 
574   // Update display triggers workspace event.
575   UpdateDisplay("300x200");
576   EXPECT_EQ("0,0 300x200", window->GetBoundsInScreen().ToString());
577 
578   // Unpin should work.
579   window_state->Restore();
580   EXPECT_FALSE(window_state->IsTrustedPinned());
581 }
582 
TEST_F(WindowStateTest,AllowSetBoundsDirect)583 TEST_F(WindowStateTest, AllowSetBoundsDirect) {
584   std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
585   WindowState* window_state = WindowState::Get(window.get());
586   EXPECT_FALSE(window_state->IsMaximized());
587   gfx::Rect work_area =
588       display::Screen::GetScreen()->GetPrimaryDisplay().work_area();
589   gfx::Rect original_bounds(50, 50, 200, 200);
590   window->SetBounds(original_bounds);
591   ASSERT_EQ(original_bounds, window->bounds());
592 
593   window_state->set_allow_set_bounds_direct(true);
594   window_state->Maximize();
595 
596   EXPECT_TRUE(window_state->IsMaximized());
597   EXPECT_EQ(work_area, window->bounds());
598 
599   gfx::Rect new_bounds(10, 10, 300, 300);
600   window->SetBounds(new_bounds);
601   EXPECT_EQ(new_bounds, window->bounds());
602 
603   window_state->Restore();
604   EXPECT_FALSE(window_state->IsMaximized());
605   EXPECT_EQ(original_bounds, window->bounds());
606 
607   window_state->set_allow_set_bounds_direct(false);
608   window_state->Maximize();
609 
610   EXPECT_TRUE(window_state->IsMaximized());
611   EXPECT_EQ(work_area, window->bounds());
612   window->SetBounds(new_bounds);
613   EXPECT_EQ(work_area, window->bounds());
614 }
615 
TEST_F(WindowStateTest,FullscreenMinimizedSwitching)616 TEST_F(WindowStateTest, FullscreenMinimizedSwitching) {
617   std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
618   WindowState* window_state = WindowState::Get(window.get());
619 
620   ToggleFullScreen(window_state, nullptr);
621   ASSERT_TRUE(window_state->IsFullscreen());
622 
623   // Toggling the fullscreen window should restore to normal.
624   ToggleFullScreen(window_state, nullptr);
625   ASSERT_TRUE(window_state->IsNormalStateType());
626 
627   window_state->Maximize();
628   ASSERT_TRUE(window_state->IsMaximized());
629 
630   ToggleFullScreen(window_state, nullptr);
631   ASSERT_TRUE(window_state->IsFullscreen());
632 
633   // Toggling the fullscreen window should restore to maximized.
634   ToggleFullScreen(window_state, nullptr);
635   ASSERT_TRUE(window_state->IsMaximized());
636 
637   ToggleFullScreen(window_state, nullptr);
638   ASSERT_TRUE(window_state->IsFullscreen());
639 
640   // Minimize from fullscreen.
641   window_state->Minimize();
642   ASSERT_TRUE(window_state->IsMinimized());
643 
644   // Unminimize should restore to fullscreen.
645   window_state->Unminimize();
646   ASSERT_TRUE(window_state->IsFullscreen());
647 
648   // Toggling the fullscreen window should restore to maximized.
649   ToggleFullScreen(window_state, nullptr);
650   ASSERT_TRUE(window_state->IsMaximized());
651 
652   // Minimize from fullscreen.
653   window_state->Minimize();
654   ASSERT_TRUE(window_state->IsMinimized());
655 
656   // Fullscreen a minimized window.
657   ToggleFullScreen(window_state, nullptr);
658   ASSERT_TRUE(window_state->IsFullscreen());
659 
660   // Toggling the fullscreen window should not return to minimized. It should
661   // return to the state before minimizing and fullscreen.
662   ToggleFullScreen(window_state, nullptr);
663   ASSERT_TRUE(window_state->IsMaximized());
664 }
665 
TEST_F(WindowStateTest,CanConsumeSystemKeys)666 TEST_F(WindowStateTest, CanConsumeSystemKeys) {
667   std::unique_ptr<aura::Window> window(
668       CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100)));
669   WindowState* window_state = WindowState::Get(window.get());
670 
671   EXPECT_FALSE(window_state->CanConsumeSystemKeys());
672 
673   window->SetProperty(kCanConsumeSystemKeysKey, true);
674   EXPECT_TRUE(window_state->CanConsumeSystemKeys());
675 }
676 
TEST_F(WindowStateTest,RestoreStateAfterEnteringPipViaOcculusionAndDismissingPip)677 TEST_F(WindowStateTest,
678        RestoreStateAfterEnteringPipViaOcculusionAndDismissingPip) {
679   std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
680   WindowState* window_state = WindowState::Get(window.get());
681   window->Show();
682   EXPECT_TRUE(window->layer()->visible());
683 
684   // Ensure a maximized window gets maximized again after it enters PIP via
685   // occlusion, gets minimized, and unminimized.
686   window_state->Maximize();
687   EXPECT_TRUE(window_state->IsMaximized());
688 
689   const WMEvent enter_pip(WM_EVENT_PIP);
690   window_state->OnWMEvent(&enter_pip);
691   EXPECT_TRUE(window_state->IsPip());
692 
693   window_state->Minimize();
694   EXPECT_TRUE(window_state->IsMinimized());
695 
696   window_state->Unminimize();
697   EXPECT_TRUE(window_state->IsMaximized());
698 
699   // Ensure a freeform window gets freeform again after it enters PIP via
700   // occulusion, gets minimized, and unminimized.
701   ::wm::SetWindowState(window.get(), ui::SHOW_STATE_NORMAL);
702 
703   window_state->OnWMEvent(&enter_pip);
704   EXPECT_TRUE(window_state->IsPip());
705 
706   window_state->Minimize();
707   EXPECT_TRUE(window_state->IsMinimized());
708 
709   window_state->Unminimize();
710   EXPECT_TRUE(window_state->GetStateType() == WindowStateType::kNormal);
711 }
712 
TEST_F(WindowStateTest,RestoreStateAfterEnterPipViaMinimizeAndDismissingPip)713 TEST_F(WindowStateTest, RestoreStateAfterEnterPipViaMinimizeAndDismissingPip) {
714   std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
715   WindowState* window_state = WindowState::Get(window.get());
716   window->Show();
717   EXPECT_TRUE(window->layer()->visible());
718 
719   // Ensure a maximized window gets maximized again after it enters PIP via
720   // minimize, gets minimized, and unminimized.
721   window_state->Maximize();
722   EXPECT_TRUE(window_state->IsMaximized());
723 
724   window_state->Minimize();
725   EXPECT_TRUE(window_state->IsMinimized());
726 
727   const WMEvent enter_pip(WM_EVENT_PIP);
728   window_state->OnWMEvent(&enter_pip);
729   EXPECT_TRUE(window_state->IsPip());
730 
731   window_state->Minimize();
732   EXPECT_TRUE(window_state->IsMinimized());
733 
734   window_state->Unminimize();
735   EXPECT_TRUE(window_state->IsMaximized());
736 
737   // Ensure a freeform window gets freeform again after it enters PIP via
738   // minimize, gets minimized, and unminimized.
739   ::wm::SetWindowState(window.get(), ui::SHOW_STATE_NORMAL);
740 
741   window_state->Minimize();
742   EXPECT_TRUE(window_state->IsMinimized());
743 
744   window_state->OnWMEvent(&enter_pip);
745   EXPECT_TRUE(window_state->IsPip());
746 
747   window_state->Minimize();
748   EXPECT_TRUE(window_state->IsMinimized());
749 
750   window_state->Unminimize();
751   EXPECT_TRUE(window_state->GetStateType() == WindowStateType::kNormal);
752 }
753 
TEST_F(WindowStateTest,SetBoundsUpdatesSizeOfPipRestoreBounds)754 TEST_F(WindowStateTest, SetBoundsUpdatesSizeOfPipRestoreBounds) {
755   std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
756   WindowState* window_state = WindowState::Get(window.get());
757   window->Show();
758   window->SetBounds(gfx::Rect(0, 0, 50, 50));
759 
760   const WMEvent enter_pip(WM_EVENT_PIP);
761   window_state->OnWMEvent(&enter_pip);
762 
763   EXPECT_TRUE(window_state->IsPip());
764   EXPECT_TRUE(window_state->HasRestoreBounds());
765   EXPECT_EQ(gfx::Rect(8, 8, 50, 50), window_state->GetRestoreBoundsInScreen());
766   window_state->window()->SetBounds(gfx::Rect(100, 100, 100, 100));
767   // SetBounds only updates the size of the restore bounds.
768   EXPECT_EQ(gfx::Rect(8, 8, 100, 100),
769             window_state->GetRestoreBoundsInScreen());
770 }
771 
TEST_F(WindowStateTest,SetBoundsSnapsPipBoundsToScreenEdge)772 TEST_F(WindowStateTest, SetBoundsSnapsPipBoundsToScreenEdge) {
773   UpdateDisplay("600x900");
774 
775   aura::test::TestWindowDelegate delegate;
776   delegate.set_minimum_size(gfx::Size(51, 51));
777   std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate(
778       &delegate, -1, gfx::Rect(0, 0, 50, 50)));
779   WindowState* window_state = WindowState::Get(window.get());
780   window->Show();
781 
782   const WMEvent enter_pip(WM_EVENT_PIP);
783   window_state->OnWMEvent(&enter_pip);
784   window->SetBounds(gfx::Rect(542, 50, 50, 50));
785   EXPECT_TRUE(window_state->IsPip());
786   // Ensure that the PIP window is along the right edge of the screen even when
787   // the new bounds is adjusted by the minimum size.
788   // 541 (left origin) + 51 (PIP width) + 8 (PIP insets) == 600.
789   EXPECT_EQ(gfx::Rect(541, 50, 51, 51),
790             window_state->window()->GetBoundsInScreen());
791 
792   PipPositioner::SaveSnapFraction(window_state,
793                                   window_state->window()->GetBoundsInScreen());
794   EXPECT_TRUE(PipPositioner::HasSnapFraction(window_state));
795   EXPECT_EQ(gfx::Rect(541, 50, 51, 51),
796             PipPositioner::GetPositionAfterMovementAreaChange(window_state));
797 }
798 
799 // Make sure the window is transparent only when it is in normal state.
TEST_F(WindowStateTest,OpacityChange)800 TEST_F(WindowStateTest, OpacityChange) {
801   std::unique_ptr<aura::Window> window = CreateAppWindow();
802   WindowState* window_state = WindowState::Get(window.get());
803   EXPECT_TRUE(window_state->IsNormalStateType());
804   EXPECT_TRUE(window->transparent());
805 
806   window_state->Maximize();
807   EXPECT_TRUE(window_state->IsMaximized());
808   EXPECT_FALSE(window->transparent());
809 
810   window_state->Restore();
811   EXPECT_TRUE(window_state->IsNormalStateType());
812   EXPECT_TRUE(window->transparent());
813 
814   window_state->Minimize();
815   EXPECT_TRUE(window_state->IsMinimized());
816   EXPECT_FALSE(window->transparent());
817 
818   window_state->Unminimize();
819   EXPECT_TRUE(window_state->IsNormalStateType());
820   EXPECT_TRUE(window->transparent());
821 
822   ToggleFullScreen(window_state, nullptr);
823   ASSERT_TRUE(window_state->IsFullscreen());
824   EXPECT_FALSE(window->transparent());
825 
826   window_state->Restore();
827   EXPECT_TRUE(window_state->IsNormalStateType());
828   EXPECT_TRUE(window->transparent());
829 
830   const WMEvent snap_left(WM_EVENT_SNAP_LEFT);
831   window_state->OnWMEvent(&snap_left);
832   EXPECT_FALSE(window->transparent());
833 
834   window_state->Restore();
835   EXPECT_TRUE(window->transparent());
836 
837   const WMEvent snap_right(WM_EVENT_SNAP_RIGHT);
838   window_state->OnWMEvent(&snap_left);
839   EXPECT_FALSE(window->transparent());
840 
841   window_state->OnWMEvent(&snap_left);
842   EXPECT_FALSE(window->transparent());
843 }
844 
845 // TODO(skuhne): Add more unit test to verify the correctness for the restore
846 // operation.
847 
848 }  // namespace
849 }  // namespace ash
850