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