OLD | NEW |
| (Empty) |
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/common/wm/workspace/workspace_layout_manager.h" | |
6 | |
7 #include <string> | |
8 #include <utility> | |
9 | |
10 #include "ash/common/session/session_state_delegate.h" | |
11 #include "ash/common/shelf/shelf_constants.h" | |
12 #include "ash/common/shelf/shelf_layout_manager.h" | |
13 #include "ash/common/shelf/wm_shelf.h" | |
14 #include "ash/common/shell_observer.h" | |
15 #include "ash/common/test/ash_test.h" | |
16 #include "ash/common/test/test_session_state_delegate.h" | |
17 #include "ash/common/wm/fullscreen_window_finder.h" | |
18 #include "ash/common/wm/maximize_mode/workspace_backdrop_delegate.h" | |
19 #include "ash/common/wm/window_state.h" | |
20 #include "ash/common/wm/wm_event.h" | |
21 #include "ash/common/wm/wm_screen_util.h" | |
22 #include "ash/common/wm/workspace/workspace_window_resizer.h" | |
23 #include "ash/common/wm_shell.h" | |
24 #include "ash/common/wm_window.h" | |
25 #include "ash/public/cpp/shell_window_ids.h" | |
26 #include "ash/root_window_controller.h" | |
27 #include "ash/wm/window_state_aura.h" | |
28 #include "base/command_line.h" | |
29 #include "base/run_loop.h" | |
30 #include "ui/aura/env.h" | |
31 #include "ui/base/ui_base_switches.h" | |
32 #include "ui/base/ui_base_types.h" | |
33 #include "ui/compositor/scoped_animation_duration_scale_mode.h" | |
34 #include "ui/display/display.h" | |
35 #include "ui/display/screen.h" | |
36 #include "ui/gfx/geometry/insets.h" | |
37 #include "ui/views/widget/widget.h" | |
38 #include "ui/views/widget/widget_delegate.h" | |
39 | |
40 namespace ash { | |
41 namespace { | |
42 | |
43 class MaximizeDelegateView : public views::WidgetDelegateView { | |
44 public: | |
45 explicit MaximizeDelegateView(const gfx::Rect& initial_bounds) | |
46 : initial_bounds_(initial_bounds) {} | |
47 ~MaximizeDelegateView() override {} | |
48 | |
49 bool GetSavedWindowPlacement(const views::Widget* widget, | |
50 gfx::Rect* bounds, | |
51 ui::WindowShowState* show_state) const override { | |
52 *bounds = initial_bounds_; | |
53 *show_state = ui::SHOW_STATE_MAXIMIZED; | |
54 return true; | |
55 } | |
56 | |
57 private: | |
58 const gfx::Rect initial_bounds_; | |
59 | |
60 DISALLOW_COPY_AND_ASSIGN(MaximizeDelegateView); | |
61 }; | |
62 | |
63 class TestShellObserver : public ShellObserver { | |
64 public: | |
65 TestShellObserver() : call_count_(0), is_fullscreen_(false) { | |
66 WmShell::Get()->AddShellObserver(this); | |
67 } | |
68 | |
69 ~TestShellObserver() override { WmShell::Get()->RemoveShellObserver(this); } | |
70 | |
71 void OnFullscreenStateChanged(bool is_fullscreen, | |
72 WmWindow* root_window) override { | |
73 call_count_++; | |
74 is_fullscreen_ = is_fullscreen; | |
75 } | |
76 | |
77 int call_count() const { return call_count_; } | |
78 | |
79 bool is_fullscreen() const { return is_fullscreen_; } | |
80 | |
81 private: | |
82 int call_count_; | |
83 bool is_fullscreen_; | |
84 | |
85 DISALLOW_COPY_AND_ASSIGN(TestShellObserver); | |
86 }; | |
87 | |
88 } // namespace | |
89 | |
90 using WorkspaceLayoutManagerTest = AshTest; | |
91 | |
92 // Verifies that a window containing a restore coordinate will be restored to | |
93 // to the size prior to minimize, keeping the restore rectangle in tact (if | |
94 // there is one). | |
95 TEST_F(WorkspaceLayoutManagerTest, RestoreFromMinimizeKeepsRestore) { | |
96 std::unique_ptr<WindowOwner> window_owner( | |
97 CreateTestWindow(gfx::Rect(1, 2, 3, 4))); | |
98 WmWindow* window = window_owner->window(); | |
99 gfx::Rect bounds(10, 15, 25, 35); | |
100 window->SetBounds(bounds); | |
101 | |
102 wm::WindowState* window_state = window->GetWindowState(); | |
103 | |
104 // This will not be used for un-minimizing window. | |
105 window_state->SetRestoreBoundsInScreen(gfx::Rect(0, 0, 100, 100)); | |
106 window_state->Minimize(); | |
107 window_state->Restore(); | |
108 EXPECT_EQ("0,0 100x100", window_state->GetRestoreBoundsInScreen().ToString()); | |
109 EXPECT_EQ("10,15 25x35", window->GetBounds().ToString()); | |
110 | |
111 UpdateDisplay("400x300,500x400"); | |
112 window->SetBoundsInScreen(gfx::Rect(600, 0, 100, 100), GetSecondaryDisplay()); | |
113 EXPECT_EQ(WmShell::Get()->GetAllRootWindows()[1], window->GetRootWindow()); | |
114 window_state->Minimize(); | |
115 // This will not be used for un-minimizing window. | |
116 window_state->SetRestoreBoundsInScreen(gfx::Rect(0, 0, 100, 100)); | |
117 window_state->Restore(); | |
118 EXPECT_EQ("600,0 100x100", window->GetBoundsInScreen().ToString()); | |
119 | |
120 // Make sure the unminimized window moves inside the display when | |
121 // 2nd display is disconnected. | |
122 window_state->Minimize(); | |
123 UpdateDisplay("400x300"); | |
124 window_state->Restore(); | |
125 EXPECT_EQ(WmShell::Get()->GetPrimaryRootWindow(), window->GetRootWindow()); | |
126 EXPECT_TRUE(WmShell::Get()->GetPrimaryRootWindow()->GetBounds().Intersects( | |
127 window->GetBounds())); | |
128 } | |
129 | |
130 TEST_F(WorkspaceLayoutManagerTest, KeepMinimumVisibilityInDisplays) { | |
131 UpdateDisplay("300x400,400x500"); | |
132 WmWindow::Windows root_windows = WmShell::Get()->GetAllRootWindows(); | |
133 | |
134 if (!SetSecondaryDisplayPlacement(display::DisplayPlacement::TOP, 0)) | |
135 return; | |
136 | |
137 EXPECT_EQ("0,-500 400x500", root_windows[1]->GetBoundsInScreen().ToString()); | |
138 | |
139 std::unique_ptr<WindowOwner> window1_owner( | |
140 CreateTestWindow(gfx::Rect(10, -400, 200, 200))); | |
141 EXPECT_EQ("10,-400 200x200", | |
142 window1_owner->window()->GetBoundsInScreen().ToString()); | |
143 | |
144 // Make sure the caption is visible. | |
145 std::unique_ptr<WindowOwner> window2_owner( | |
146 CreateTestWindow(gfx::Rect(10, -600, 200, 200))); | |
147 EXPECT_EQ("10,-500 200x200", | |
148 window2_owner->window()->GetBoundsInScreen().ToString()); | |
149 } | |
150 | |
151 TEST_F(WorkspaceLayoutManagerTest, NoMinimumVisibilityForPopupWindows) { | |
152 UpdateDisplay("300x400"); | |
153 | |
154 // Create a popup window out of display boundaries and make sure it is not | |
155 // moved to have minimum visibility. | |
156 std::unique_ptr<WindowOwner> window_owner( | |
157 CreateTestWindow(gfx::Rect(400, 100, 50, 50), ui::wm::WINDOW_TYPE_POPUP)); | |
158 EXPECT_EQ("400,100 50x50", | |
159 window_owner->window()->GetBoundsInScreen().ToString()); | |
160 } | |
161 | |
162 TEST_F(WorkspaceLayoutManagerTest, KeepRestoredWindowInDisplay) { | |
163 std::unique_ptr<WindowOwner> window_owner( | |
164 CreateTestWindow(gfx::Rect(1, 2, 30, 40))); | |
165 WmWindow* window = window_owner->window(); | |
166 wm::WindowState* window_state = window->GetWindowState(); | |
167 | |
168 // Maximized -> Normal transition. | |
169 window_state->Maximize(); | |
170 window_state->SetRestoreBoundsInScreen(gfx::Rect(-100, -100, 30, 40)); | |
171 window_state->Restore(); | |
172 EXPECT_TRUE(WmShell::Get()->GetPrimaryRootWindow()->GetBounds().Intersects( | |
173 window->GetBounds())); | |
174 // Y bounds should not be negative. | |
175 EXPECT_EQ("-5,0 30x40", window->GetBounds().ToString()); | |
176 | |
177 // Minimized -> Normal transition. | |
178 window->SetBounds(gfx::Rect(-100, -100, 30, 40)); | |
179 window_state->Minimize(); | |
180 EXPECT_FALSE(WmShell::Get()->GetPrimaryRootWindow()->GetBounds().Intersects( | |
181 window->GetBounds())); | |
182 EXPECT_EQ("-100,-100 30x40", window->GetBounds().ToString()); | |
183 window->Show(); | |
184 EXPECT_TRUE(WmShell::Get()->GetPrimaryRootWindow()->GetBounds().Intersects( | |
185 window->GetBounds())); | |
186 // Y bounds should not be negative. | |
187 EXPECT_EQ("-5,0 30x40", window->GetBounds().ToString()); | |
188 | |
189 // Fullscreen -> Normal transition. | |
190 window->SetBounds(gfx::Rect(0, 0, 30, 40)); // reset bounds. | |
191 ASSERT_EQ("0,0 30x40", window->GetBounds().ToString()); | |
192 window->SetShowState(ui::SHOW_STATE_FULLSCREEN); | |
193 EXPECT_EQ(window->GetBounds(), window->GetRootWindow()->GetBounds()); | |
194 window_state->SetRestoreBoundsInScreen(gfx::Rect(-100, -100, 30, 40)); | |
195 window_state->Restore(); | |
196 EXPECT_TRUE(WmShell::Get()->GetPrimaryRootWindow()->GetBounds().Intersects( | |
197 window->GetBounds())); | |
198 // Y bounds should not be negative. | |
199 EXPECT_EQ("-5,0 30x40", window->GetBounds().ToString()); | |
200 } | |
201 | |
202 TEST_F(WorkspaceLayoutManagerTest, MaximizeInDisplayToBeRestored) { | |
203 UpdateDisplay("300x400,400x500"); | |
204 | |
205 WmWindow::Windows root_windows = WmShell::Get()->GetAllRootWindows(); | |
206 | |
207 std::unique_ptr<WindowOwner> window_owner( | |
208 CreateTestWindow(gfx::Rect(1, 2, 30, 40))); | |
209 WmWindow* window = window_owner->window(); | |
210 EXPECT_EQ(root_windows[0], window->GetRootWindow()); | |
211 | |
212 wm::WindowState* window_state = window->GetWindowState(); | |
213 window_state->SetRestoreBoundsInScreen(gfx::Rect(400, 0, 30, 40)); | |
214 // Maximize the window in 2nd display as the restore bounds | |
215 // is inside 2nd display. | |
216 window_state->Maximize(); | |
217 EXPECT_EQ(root_windows[1], window->GetRootWindow()); | |
218 EXPECT_EQ( | |
219 gfx::Rect(300, 0, 400, 500 - GetShelfConstant(SHELF_SIZE)).ToString(), | |
220 window->GetBoundsInScreen().ToString()); | |
221 | |
222 window_state->Restore(); | |
223 EXPECT_EQ(root_windows[1], window->GetRootWindow()); | |
224 EXPECT_EQ("400,0 30x40", window->GetBoundsInScreen().ToString()); | |
225 | |
226 // If the restore bounds intersects with the current display, | |
227 // don't move. | |
228 window_state->SetRestoreBoundsInScreen(gfx::Rect(295, 0, 30, 40)); | |
229 window_state->Maximize(); | |
230 EXPECT_EQ(root_windows[1], window->GetRootWindow()); | |
231 EXPECT_EQ( | |
232 gfx::Rect(300, 0, 400, 500 - GetShelfConstant(SHELF_SIZE)).ToString(), | |
233 window->GetBoundsInScreen().ToString()); | |
234 | |
235 window_state->Restore(); | |
236 EXPECT_EQ(root_windows[1], window->GetRootWindow()); | |
237 EXPECT_EQ("295,0 30x40", window->GetBoundsInScreen().ToString()); | |
238 | |
239 // Restoring widget state. | |
240 std::unique_ptr<views::Widget> w1(new views::Widget); | |
241 views::Widget::InitParams params; | |
242 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | |
243 params.delegate = new MaximizeDelegateView(gfx::Rect(400, 0, 30, 40)); | |
244 ConfigureWidgetInitParamsForDisplay(root_windows[0], ¶ms); | |
245 w1->Init(params); | |
246 EXPECT_EQ(root_windows[0], | |
247 WmWindow::Get(w1->GetNativeWindow())->GetRootWindow()); | |
248 w1->Show(); | |
249 EXPECT_TRUE(w1->IsMaximized()); | |
250 EXPECT_EQ(root_windows[1], | |
251 WmWindow::Get(w1->GetNativeWindow())->GetRootWindow()); | |
252 EXPECT_EQ( | |
253 gfx::Rect(300, 0, 400, 500 - GetShelfConstant(SHELF_SIZE)).ToString(), | |
254 w1->GetWindowBoundsInScreen().ToString()); | |
255 w1->Restore(); | |
256 EXPECT_EQ(root_windows[1], | |
257 WmWindow::Get(w1->GetNativeWindow())->GetRootWindow()); | |
258 EXPECT_EQ("400,0 30x40", w1->GetWindowBoundsInScreen().ToString()); | |
259 } | |
260 | |
261 TEST_F(WorkspaceLayoutManagerTest, FullscreenInDisplayToBeRestored) { | |
262 UpdateDisplay("300x400,400x500"); | |
263 | |
264 WmWindow::Windows root_windows = WmShell::Get()->GetAllRootWindows(); | |
265 | |
266 std::unique_ptr<WindowOwner> window_owner( | |
267 CreateTestWindow(gfx::Rect(1, 2, 30, 40))); | |
268 WmWindow* window = window_owner->window(); | |
269 EXPECT_EQ(root_windows[0], window->GetRootWindow()); | |
270 | |
271 wm::WindowState* window_state = window->GetWindowState(); | |
272 window_state->SetRestoreBoundsInScreen(gfx::Rect(400, 0, 30, 40)); | |
273 // Maximize the window in 2nd display as the restore bounds | |
274 // is inside 2nd display. | |
275 window->SetShowState(ui::SHOW_STATE_FULLSCREEN); | |
276 EXPECT_EQ(root_windows[1], window->GetRootWindow()); | |
277 EXPECT_EQ("300,0 400x500", window->GetBoundsInScreen().ToString()); | |
278 | |
279 window_state->Restore(); | |
280 EXPECT_EQ(root_windows[1], window->GetRootWindow()); | |
281 EXPECT_EQ("400,0 30x40", window->GetBoundsInScreen().ToString()); | |
282 | |
283 // If the restore bounds intersects with the current display, | |
284 // don't move. | |
285 window_state->SetRestoreBoundsInScreen(gfx::Rect(295, 0, 30, 40)); | |
286 window->SetShowState(ui::SHOW_STATE_FULLSCREEN); | |
287 EXPECT_EQ(root_windows[1], window->GetRootWindow()); | |
288 EXPECT_EQ("300,0 400x500", window->GetBoundsInScreen().ToString()); | |
289 | |
290 window_state->Restore(); | |
291 EXPECT_EQ(root_windows[1], window->GetRootWindow()); | |
292 EXPECT_EQ("295,0 30x40", window->GetBoundsInScreen().ToString()); | |
293 } | |
294 | |
295 // aura::WindowObserver implementation used by | |
296 // DontClobberRestoreBoundsWindowObserver. This code mirrors what | |
297 // BrowserFrameAsh does. In particular when this code sees the window was | |
298 // maximized it changes the bounds of a secondary window. The secondary window | |
299 // mirrors the status window. | |
300 class DontClobberRestoreBoundsWindowObserver : public aura::WindowObserver { | |
301 public: | |
302 DontClobberRestoreBoundsWindowObserver() : window_(nullptr) {} | |
303 | |
304 void set_window(WmWindow* window) { window_ = window; } | |
305 | |
306 // aura::WindowObserver: | |
307 void OnWindowPropertyChanged(aura::Window* window, | |
308 const void* key, | |
309 intptr_t old) override { | |
310 if (!window_) | |
311 return; | |
312 | |
313 if (wm::GetWindowState(window)->IsMaximized()) { | |
314 WmWindow* w = window_; | |
315 window_ = nullptr; | |
316 | |
317 gfx::Rect shelf_bounds(AshTest::GetPrimaryShelf()->GetIdealBounds()); | |
318 const gfx::Rect& window_bounds(w->GetBounds()); | |
319 w->SetBounds(gfx::Rect(window_bounds.x(), shelf_bounds.y() - 1, | |
320 window_bounds.width(), window_bounds.height())); | |
321 } | |
322 } | |
323 | |
324 private: | |
325 WmWindow* window_; | |
326 | |
327 DISALLOW_COPY_AND_ASSIGN(DontClobberRestoreBoundsWindowObserver); | |
328 }; | |
329 | |
330 // Creates a window, maximized the window and from within the maximized | |
331 // notification sets the bounds of a window to overlap the shelf. Verifies this | |
332 // doesn't effect the restore bounds. | |
333 TEST_F(WorkspaceLayoutManagerTest, DontClobberRestoreBounds) { | |
334 DontClobberRestoreBoundsWindowObserver window_observer; | |
335 WindowOwner window_owner(WmShell::Get()->NewWindow(ui::wm::WINDOW_TYPE_NORMAL, | |
336 ui::LAYER_TEXTURED)); | |
337 WmWindow* window = window_owner.window(); | |
338 window->SetBounds(gfx::Rect(10, 20, 30, 40)); | |
339 // NOTE: for this test to exercise the failure the observer needs to be added | |
340 // before the parent set. This mimics what BrowserFrameAsh does. | |
341 window->aura_window()->AddObserver(&window_observer); | |
342 ParentWindowInPrimaryRootWindow(window); | |
343 window->Show(); | |
344 | |
345 wm::WindowState* window_state = window->GetWindowState(); | |
346 window_state->Activate(); | |
347 | |
348 std::unique_ptr<WindowOwner> window2_owner( | |
349 CreateTestWindow(gfx::Rect(12, 20, 30, 40))); | |
350 WmWindow* window2 = window2_owner->window(); | |
351 AddTransientChild(window, window2); | |
352 window2->Show(); | |
353 | |
354 window_observer.set_window(window2); | |
355 window_state->Maximize(); | |
356 EXPECT_EQ("10,20 30x40", window_state->GetRestoreBoundsInScreen().ToString()); | |
357 window->aura_window()->RemoveObserver(&window_observer); | |
358 } | |
359 | |
360 // Verifies when a window is maximized all descendant windows have a size. | |
361 TEST_F(WorkspaceLayoutManagerTest, ChildBoundsResetOnMaximize) { | |
362 std::unique_ptr<WindowOwner> window_owner( | |
363 CreateTestWindow(gfx::Rect(10, 20, 30, 40))); | |
364 WmWindow* window = window_owner->window(); | |
365 window->Show(); | |
366 wm::WindowState* window_state = window->GetWindowState(); | |
367 window_state->Activate(); | |
368 std::unique_ptr<WindowOwner> child_window_owner( | |
369 CreateChildWindow(window, gfx::Rect(5, 6, 7, 8))); | |
370 WmWindow* child_window = child_window_owner->window(); | |
371 window_state->Maximize(); | |
372 EXPECT_EQ("5,6 7x8", child_window->GetBounds().ToString()); | |
373 } | |
374 | |
375 // Verifies a window created with maximized state has the maximized | |
376 // bounds. | |
377 TEST_F(WorkspaceLayoutManagerTest, MaximizeWithEmptySize) { | |
378 WindowOwner window_owner(WmShell::Get()->NewWindow(ui::wm::WINDOW_TYPE_NORMAL, | |
379 ui::LAYER_TEXTURED)); | |
380 WmWindow* window = window_owner.window(); | |
381 window->GetWindowState()->Maximize(); | |
382 WmWindow* default_container = | |
383 WmShell::Get()->GetPrimaryRootWindowController()->GetWmContainer( | |
384 kShellWindowId_DefaultContainer); | |
385 default_container->AddChild(window); | |
386 window->Show(); | |
387 gfx::Rect work_area( | |
388 display::Screen::GetScreen()->GetPrimaryDisplay().work_area()); | |
389 EXPECT_EQ(work_area.ToString(), window->GetBoundsInScreen().ToString()); | |
390 } | |
391 | |
392 TEST_F(WorkspaceLayoutManagerTest, WindowShouldBeOnScreenWhenAdded) { | |
393 // TODO: fix. This test verifies that when a window is added the bounds are | |
394 // adjusted. CreateTestWindow() for mus adds, then sets the bounds (this comes | |
395 // from NativeWidgetAura), which means this test now fails for aura-mus. | |
396 if (aura::Env::GetInstance()->mode() == aura::Env::Mode::MUS) | |
397 return; | |
398 | |
399 // Normal window bounds shouldn't be changed. | |
400 gfx::Rect window_bounds(100, 100, 200, 200); | |
401 std::unique_ptr<WindowOwner> window_owner(CreateTestWindow(window_bounds)); | |
402 WmWindow* window = window_owner->window(); | |
403 EXPECT_EQ(window_bounds, window->GetBounds()); | |
404 | |
405 // If the window is out of the workspace, it would be moved on screen. | |
406 gfx::Rect root_window_bounds = | |
407 WmShell::Get()->GetPrimaryRootWindow()->GetBounds(); | |
408 window_bounds.Offset(root_window_bounds.width(), root_window_bounds.height()); | |
409 ASSERT_FALSE(window_bounds.Intersects(root_window_bounds)); | |
410 std::unique_ptr<WindowOwner> out_window_owner( | |
411 CreateTestWindow(window_bounds)); | |
412 WmWindow* out_window = out_window_owner->window(); | |
413 EXPECT_EQ(window_bounds.size(), out_window->GetBounds().size()); | |
414 gfx::Rect bounds = out_window->GetBounds(); | |
415 bounds.Intersect(root_window_bounds); | |
416 | |
417 // 30% of the window edge must be visible. | |
418 EXPECT_GT(bounds.width(), out_window->GetBounds().width() * 0.29); | |
419 EXPECT_GT(bounds.height(), out_window->GetBounds().height() * 0.29); | |
420 | |
421 WmWindow* parent = out_window->GetParent(); | |
422 parent->RemoveChild(out_window); | |
423 out_window->SetBounds(gfx::Rect(-200, -200, 200, 200)); | |
424 // UserHasChangedWindowPositionOrSize flag shouldn't turn off this behavior. | |
425 window->GetWindowState()->set_bounds_changed_by_user(true); | |
426 parent->AddChild(out_window); | |
427 EXPECT_GT(bounds.width(), out_window->GetBounds().width() * 0.29); | |
428 EXPECT_GT(bounds.height(), out_window->GetBounds().height() * 0.29); | |
429 | |
430 // Make sure we always make more than 1/3 of the window edge visible even | |
431 // if the initial bounds intersects with display. | |
432 window_bounds.SetRect(-150, -150, 200, 200); | |
433 bounds = window_bounds; | |
434 bounds.Intersect(root_window_bounds); | |
435 | |
436 // Make sure that the initial bounds' visible area is less than 26% | |
437 // so that the auto adjustment logic kicks in. | |
438 ASSERT_LT(bounds.width(), out_window->GetBounds().width() * 0.26); | |
439 ASSERT_LT(bounds.height(), out_window->GetBounds().height() * 0.26); | |
440 ASSERT_TRUE(window_bounds.Intersects(root_window_bounds)); | |
441 | |
442 std::unique_ptr<WindowOwner> partially_out_window_owner( | |
443 CreateTestWindow(window_bounds)); | |
444 WmWindow* partially_out_window = partially_out_window_owner->window(); | |
445 EXPECT_EQ(window_bounds.size(), partially_out_window->GetBounds().size()); | |
446 bounds = partially_out_window->GetBounds(); | |
447 bounds.Intersect(root_window_bounds); | |
448 EXPECT_GT(bounds.width(), out_window->GetBounds().width() * 0.29); | |
449 EXPECT_GT(bounds.height(), out_window->GetBounds().height() * 0.29); | |
450 | |
451 // Make sure the window whose 30% width/height is bigger than display | |
452 // will be placed correctly. | |
453 window_bounds.SetRect(-1900, -1900, 3000, 3000); | |
454 std::unique_ptr<WindowOwner> window_bigger_than_display_owner( | |
455 CreateTestWindow(window_bounds)); | |
456 WmWindow* window_bigger_than_display = | |
457 window_bigger_than_display_owner->window(); | |
458 EXPECT_GE(root_window_bounds.width(), | |
459 window_bigger_than_display->GetBounds().width()); | |
460 EXPECT_GE(root_window_bounds.height(), | |
461 window_bigger_than_display->GetBounds().height()); | |
462 | |
463 bounds = window_bigger_than_display->GetBounds(); | |
464 bounds.Intersect(root_window_bounds); | |
465 EXPECT_GT(bounds.width(), out_window->GetBounds().width() * 0.29); | |
466 EXPECT_GT(bounds.height(), out_window->GetBounds().height() * 0.29); | |
467 } | |
468 | |
469 // Verifies the size of a window is enforced to be smaller than the work area. | |
470 TEST_F(WorkspaceLayoutManagerTest, SizeToWorkArea) { | |
471 // Normal window bounds shouldn't be changed. | |
472 gfx::Size work_area( | |
473 display::Screen::GetScreen()->GetPrimaryDisplay().work_area().size()); | |
474 const gfx::Rect window_bounds(100, 101, work_area.width() + 1, | |
475 work_area.height() + 2); | |
476 std::unique_ptr<WindowOwner> window_owner(CreateTestWindow(window_bounds)); | |
477 WmWindow* window = window_owner->window(); | |
478 // TODO: fix. This test verifies that when a window is added the bounds are | |
479 // adjusted. CreateTestWindow() for mus adds, then sets the bounds (this comes | |
480 // from NativeWidgetAura), which means this test now fails for aura-mus. | |
481 if (aura::Env::GetInstance()->mode() != aura::Env::Mode::MUS) { | |
482 EXPECT_EQ(gfx::Rect(gfx::Point(100, 101), work_area).ToString(), | |
483 window->GetBounds().ToString()); | |
484 } | |
485 | |
486 // Directly setting the bounds triggers a slightly different code path. Verify | |
487 // that too. | |
488 window->SetBounds(window_bounds); | |
489 EXPECT_EQ(gfx::Rect(gfx::Point(100, 101), work_area).ToString(), | |
490 window->GetBounds().ToString()); | |
491 } | |
492 | |
493 TEST_F(WorkspaceLayoutManagerTest, NotifyFullscreenChanges) { | |
494 TestShellObserver observer; | |
495 std::unique_ptr<WindowOwner> window1_owner( | |
496 CreateTestWindow(gfx::Rect(1, 2, 30, 40))); | |
497 WmWindow* window1 = window1_owner->window(); | |
498 std::unique_ptr<WindowOwner> window2_owner( | |
499 CreateTestWindow(gfx::Rect(1, 2, 30, 40))); | |
500 WmWindow* window2 = window2_owner->window(); | |
501 wm::WindowState* window_state1 = window1->GetWindowState(); | |
502 wm::WindowState* window_state2 = window2->GetWindowState(); | |
503 window_state2->Activate(); | |
504 | |
505 const wm::WMEvent toggle_fullscreen_event(wm::WM_EVENT_TOGGLE_FULLSCREEN); | |
506 window_state2->OnWMEvent(&toggle_fullscreen_event); | |
507 EXPECT_EQ(1, observer.call_count()); | |
508 EXPECT_TRUE(observer.is_fullscreen()); | |
509 | |
510 // When window1 moves to the front the fullscreen state should change. | |
511 window_state1->Activate(); | |
512 EXPECT_EQ(2, observer.call_count()); | |
513 EXPECT_FALSE(observer.is_fullscreen()); | |
514 | |
515 // It should change back if window2 becomes active again. | |
516 window_state2->Activate(); | |
517 EXPECT_EQ(3, observer.call_count()); | |
518 EXPECT_TRUE(observer.is_fullscreen()); | |
519 | |
520 window_state2->OnWMEvent(&toggle_fullscreen_event); | |
521 EXPECT_EQ(4, observer.call_count()); | |
522 EXPECT_FALSE(observer.is_fullscreen()); | |
523 | |
524 window_state2->OnWMEvent(&toggle_fullscreen_event); | |
525 EXPECT_EQ(5, observer.call_count()); | |
526 EXPECT_TRUE(observer.is_fullscreen()); | |
527 | |
528 // Closing the window should change the fullscreen state. | |
529 window2_owner.reset(); | |
530 EXPECT_EQ(6, observer.call_count()); | |
531 EXPECT_FALSE(observer.is_fullscreen()); | |
532 } | |
533 | |
534 // For crbug.com/673803, snapped window may not adjust snapped bounds on work | |
535 // area changed properly if window's layer is doing animation. We should use | |
536 // GetTargetBounds to check if snapped bounds need to be changed. | |
537 TEST_F(WorkspaceLayoutManagerTest, | |
538 SnappedWindowMayNotAdjustBoundsOnWorkAreaChanged) { | |
539 UpdateDisplay("300x400"); | |
540 std::unique_ptr<WindowOwner> window_owner( | |
541 CreateTestWindow(gfx::Rect(10, 20, 100, 200))); | |
542 WmWindow* window = window_owner->window(); | |
543 wm::WindowState* window_state = window->GetWindowState(); | |
544 gfx::Insets insets(0, 0, 50, 0); | |
545 WmShell::Get()->SetDisplayWorkAreaInsets(window, insets); | |
546 const wm::WMEvent snap_left(wm::WM_EVENT_SNAP_LEFT); | |
547 window_state->OnWMEvent(&snap_left); | |
548 EXPECT_EQ(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED, window_state->GetStateType()); | |
549 const gfx::Rect kWorkAreaBounds = | |
550 display::Screen::GetScreen()->GetPrimaryDisplay().work_area(); | |
551 gfx::Rect expected_bounds = | |
552 gfx::Rect(kWorkAreaBounds.x(), kWorkAreaBounds.y(), | |
553 kWorkAreaBounds.width() / 2, kWorkAreaBounds.height()); | |
554 EXPECT_EQ(expected_bounds.ToString(), window->GetBounds().ToString()); | |
555 | |
556 ui::ScopedAnimationDurationScaleMode test_duration_mode( | |
557 ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); | |
558 // The following two SetDisplayWorkAreaInsets calls simulate the case of | |
559 // crbug.com/673803 that work area first becomes fullscreen and then returns | |
560 // to the original state. | |
561 WmShell::Get()->SetDisplayWorkAreaInsets(window, gfx::Insets(0, 0, 0, 0)); | |
562 ui::LayerAnimator* animator = window->GetLayer()->GetAnimator(); | |
563 EXPECT_TRUE(animator->is_animating()); | |
564 WmShell::Get()->SetDisplayWorkAreaInsets(window, insets); | |
565 animator->StopAnimating(); | |
566 EXPECT_FALSE(animator->is_animating()); | |
567 EXPECT_EQ(expected_bounds.ToString(), window->GetBounds().ToString()); | |
568 } | |
569 | |
570 // Do not adjust window bounds to ensure minimum visibility for transient | |
571 // windows (crbug.com/624806). | |
572 TEST_F(WorkspaceLayoutManagerTest, | |
573 DoNotAdjustTransientWindowBoundsToEnsureMinimumVisibility) { | |
574 UpdateDisplay("300x400"); | |
575 WindowOwner window_owner(WmShell::Get()->NewWindow(ui::wm::WINDOW_TYPE_NORMAL, | |
576 ui::LAYER_TEXTURED)); | |
577 WmWindow* window = window_owner.window(); | |
578 window->SetBounds(gfx::Rect(10, 0, 100, 200)); | |
579 ParentWindowInPrimaryRootWindow(window); | |
580 window->Show(); | |
581 | |
582 std::unique_ptr<WindowOwner> window2_owner( | |
583 CreateTestWindow(gfx::Rect(10, 0, 40, 20))); | |
584 WmWindow* window2 = window2_owner->window(); | |
585 AddTransientChild(window, window2); | |
586 window2->Show(); | |
587 | |
588 gfx::Rect expected_bounds = window2->GetBounds(); | |
589 WmShell::Get()->SetDisplayWorkAreaInsets(window, gfx::Insets(50, 0, 0, 0)); | |
590 EXPECT_EQ(expected_bounds.ToString(), window2->GetBounds().ToString()); | |
591 } | |
592 | |
593 // Following "Solo" tests were originally written for BaseLayoutManager. | |
594 using WorkspaceLayoutManagerSoloTest = AshTest; | |
595 | |
596 // Tests normal->maximize->normal. | |
597 TEST_F(WorkspaceLayoutManagerSoloTest, Maximize) { | |
598 gfx::Rect bounds(100, 100, 200, 200); | |
599 std::unique_ptr<WindowOwner> window_owner(CreateTestWindow(bounds)); | |
600 WmWindow* window = window_owner->window(); | |
601 window->SetShowState(ui::SHOW_STATE_MAXIMIZED); | |
602 // Maximized window fills the work area, not the whole display. | |
603 EXPECT_EQ(wm::GetMaximizedWindowBoundsInParent(window).ToString(), | |
604 window->GetBounds().ToString()); | |
605 window->SetShowState(ui::SHOW_STATE_NORMAL); | |
606 EXPECT_EQ(bounds.ToString(), window->GetBounds().ToString()); | |
607 } | |
608 | |
609 // Tests normal->minimize->normal. | |
610 TEST_F(WorkspaceLayoutManagerSoloTest, Minimize) { | |
611 gfx::Rect bounds(100, 100, 200, 200); | |
612 std::unique_ptr<WindowOwner> window_owner(CreateTestWindow(bounds)); | |
613 WmWindow* window = window_owner->window(); | |
614 window->SetShowState(ui::SHOW_STATE_MINIMIZED); | |
615 EXPECT_FALSE(window->IsVisible()); | |
616 EXPECT_TRUE(window->GetWindowState()->IsMinimized()); | |
617 EXPECT_EQ(bounds, window->GetBounds()); | |
618 window->SetShowState(ui::SHOW_STATE_NORMAL); | |
619 EXPECT_TRUE(window->IsVisible()); | |
620 EXPECT_FALSE(window->GetWindowState()->IsMinimized()); | |
621 EXPECT_EQ(bounds, window->GetBounds()); | |
622 } | |
623 | |
624 // A aura::WindowObserver which sets the focus when the window becomes visible. | |
625 class FocusDuringUnminimizeWindowObserver : public aura::WindowObserver { | |
626 public: | |
627 FocusDuringUnminimizeWindowObserver() | |
628 : window_(nullptr), show_state_(ui::SHOW_STATE_END) {} | |
629 ~FocusDuringUnminimizeWindowObserver() override { SetWindow(nullptr); } | |
630 | |
631 void SetWindow(WmWindow* window) { | |
632 if (window_) | |
633 window_->aura_window()->RemoveObserver(this); | |
634 window_ = window; | |
635 if (window_) | |
636 window_->aura_window()->AddObserver(this); | |
637 } | |
638 | |
639 // aura::WindowObserver: | |
640 void OnWindowVisibilityChanged(aura::Window* window, bool visible) override { | |
641 if (window_) { | |
642 if (visible) | |
643 window_->SetFocused(); | |
644 show_state_ = window_->GetShowState(); | |
645 } | |
646 } | |
647 | |
648 ui::WindowShowState GetShowStateAndReset() { | |
649 ui::WindowShowState ret = show_state_; | |
650 show_state_ = ui::SHOW_STATE_END; | |
651 return ret; | |
652 } | |
653 | |
654 private: | |
655 WmWindow* window_; | |
656 ui::WindowShowState show_state_; | |
657 | |
658 DISALLOW_COPY_AND_ASSIGN(FocusDuringUnminimizeWindowObserver); | |
659 }; | |
660 | |
661 // Make sure that the window's show state is correct in | |
662 // WindowObserver::OnWindowTargetVisibilityChanged(), and setting focus in this | |
663 // callback doesn't cause DCHECK error. See crbug.com/168383. | |
664 TEST_F(WorkspaceLayoutManagerSoloTest, FocusDuringUnminimize) { | |
665 FocusDuringUnminimizeWindowObserver observer; | |
666 std::unique_ptr<WindowOwner> window_owner( | |
667 CreateTestWindow(gfx::Rect(100, 100, 100, 100))); | |
668 WmWindow* window = window_owner->window(); | |
669 observer.SetWindow(window); | |
670 window->SetShowState(ui::SHOW_STATE_MINIMIZED); | |
671 EXPECT_FALSE(window->IsVisible()); | |
672 EXPECT_EQ(ui::SHOW_STATE_MINIMIZED, observer.GetShowStateAndReset()); | |
673 window->Show(); | |
674 EXPECT_TRUE(window->IsVisible()); | |
675 EXPECT_EQ(ui::SHOW_STATE_NORMAL, observer.GetShowStateAndReset()); | |
676 observer.SetWindow(nullptr); | |
677 } | |
678 | |
679 // Tests maximized window size during root window resize. | |
680 TEST_F(WorkspaceLayoutManagerSoloTest, MaximizeRootWindowResize) { | |
681 gfx::Rect bounds(100, 100, 200, 200); | |
682 std::unique_ptr<WindowOwner> window_owner(CreateTestWindow(bounds)); | |
683 WmWindow* window = window_owner->window(); | |
684 window->SetShowState(ui::SHOW_STATE_MAXIMIZED); | |
685 gfx::Rect initial_work_area_bounds = | |
686 wm::GetMaximizedWindowBoundsInParent(window); | |
687 EXPECT_EQ(initial_work_area_bounds.ToString(), | |
688 window->GetBounds().ToString()); | |
689 // Enlarge the root window. We should still match the work area size. | |
690 UpdateDisplay("900x700"); | |
691 EXPECT_EQ(wm::GetMaximizedWindowBoundsInParent(window).ToString(), | |
692 window->GetBounds().ToString()); | |
693 EXPECT_NE(initial_work_area_bounds.ToString(), | |
694 wm::GetMaximizedWindowBoundsInParent(window).ToString()); | |
695 } | |
696 | |
697 // Tests normal->fullscreen->normal. | |
698 TEST_F(WorkspaceLayoutManagerSoloTest, Fullscreen) { | |
699 gfx::Rect bounds(100, 100, 200, 200); | |
700 std::unique_ptr<WindowOwner> window_owner(CreateTestWindow(bounds)); | |
701 WmWindow* window = window_owner->window(); | |
702 window->SetShowState(ui::SHOW_STATE_FULLSCREEN); | |
703 // Fullscreen window fills the whole display. | |
704 EXPECT_EQ(window->GetDisplayNearestWindow().bounds().ToString(), | |
705 window->GetBounds().ToString()); | |
706 window->SetShowState(ui::SHOW_STATE_NORMAL); | |
707 EXPECT_EQ(bounds.ToString(), window->GetBounds().ToString()); | |
708 } | |
709 | |
710 // Tests that fullscreen window causes always_on_top windows to stack below. | |
711 TEST_F(WorkspaceLayoutManagerSoloTest, FullscreenSuspendsAlwaysOnTop) { | |
712 gfx::Rect bounds(100, 100, 200, 200); | |
713 std::unique_ptr<WindowOwner> fullscreen_window_owner( | |
714 CreateTestWindow(bounds)); | |
715 WmWindow* fullscreen_window = fullscreen_window_owner->window(); | |
716 std::unique_ptr<WindowOwner> always_on_top_window1_owner( | |
717 CreateTestWindow(bounds)); | |
718 WmWindow* always_on_top_window1 = always_on_top_window1_owner->window(); | |
719 std::unique_ptr<WindowOwner> always_on_top_window2_owner( | |
720 CreateTestWindow(bounds)); | |
721 WmWindow* always_on_top_window2 = always_on_top_window2_owner->window(); | |
722 always_on_top_window1->SetAlwaysOnTop(true); | |
723 always_on_top_window2->SetAlwaysOnTop(true); | |
724 // Making a window fullscreen temporarily suspends always on top state. | |
725 fullscreen_window->SetShowState(ui::SHOW_STATE_FULLSCREEN); | |
726 EXPECT_FALSE(always_on_top_window1->IsAlwaysOnTop()); | |
727 EXPECT_FALSE(always_on_top_window2->IsAlwaysOnTop()); | |
728 EXPECT_NE(nullptr, wm::GetWindowForFullscreenMode(fullscreen_window)); | |
729 | |
730 // Adding a new always-on-top window is not affected by fullscreen. | |
731 std::unique_ptr<WindowOwner> always_on_top_window3_owner( | |
732 CreateTestWindow(bounds)); | |
733 WmWindow* always_on_top_window3 = always_on_top_window3_owner->window(); | |
734 always_on_top_window3->SetAlwaysOnTop(true); | |
735 EXPECT_TRUE(always_on_top_window3->IsAlwaysOnTop()); | |
736 | |
737 // Making fullscreen window normal restores always on top windows. | |
738 fullscreen_window->SetShowState(ui::SHOW_STATE_NORMAL); | |
739 EXPECT_TRUE(always_on_top_window1->IsAlwaysOnTop()); | |
740 EXPECT_TRUE(always_on_top_window2->IsAlwaysOnTop()); | |
741 EXPECT_TRUE(always_on_top_window3->IsAlwaysOnTop()); | |
742 EXPECT_EQ(nullptr, wm::GetWindowForFullscreenMode(fullscreen_window)); | |
743 } | |
744 | |
745 // Similary, pinned window causes always_on_top_ windows to stack below. | |
746 TEST_F(WorkspaceLayoutManagerSoloTest, PinnedSuspendsAlwaysOnTop) { | |
747 // TODO: mash doesn't support pinning yet http://crbug.com/622486. | |
748 if (WmShell::Get()->IsRunningInMash()) | |
749 return; | |
750 | |
751 gfx::Rect bounds(100, 100, 200, 200); | |
752 std::unique_ptr<WindowOwner> pinned_window_owner(CreateTestWindow(bounds)); | |
753 WmWindow* pinned_window = pinned_window_owner->window(); | |
754 std::unique_ptr<WindowOwner> always_on_top_window1_owner( | |
755 CreateTestWindow(bounds)); | |
756 WmWindow* always_on_top_window1 = always_on_top_window1_owner->window(); | |
757 std::unique_ptr<WindowOwner> always_on_top_window2_owner( | |
758 CreateTestWindow(bounds)); | |
759 WmWindow* always_on_top_window2 = always_on_top_window2_owner->window(); | |
760 always_on_top_window1->SetAlwaysOnTop(true); | |
761 always_on_top_window2->SetAlwaysOnTop(true); | |
762 | |
763 // Making a window pinned temporarily suspends always on top state. | |
764 const bool trusted = false; | |
765 pinned_window->SetPinned(trusted); | |
766 EXPECT_FALSE(always_on_top_window1->IsAlwaysOnTop()); | |
767 EXPECT_FALSE(always_on_top_window2->IsAlwaysOnTop()); | |
768 | |
769 // Adding a new always-on-top window also is affected by pinned mode. | |
770 std::unique_ptr<WindowOwner> always_on_top_window3_owner( | |
771 CreateTestWindow(bounds)); | |
772 WmWindow* always_on_top_window3 = always_on_top_window3_owner->window(); | |
773 always_on_top_window3->SetAlwaysOnTop(true); | |
774 EXPECT_FALSE(always_on_top_window3->IsAlwaysOnTop()); | |
775 | |
776 // Making pinned window normal restores always on top windows. | |
777 pinned_window->GetWindowState()->Restore(); | |
778 EXPECT_TRUE(always_on_top_window1->IsAlwaysOnTop()); | |
779 EXPECT_TRUE(always_on_top_window2->IsAlwaysOnTop()); | |
780 EXPECT_TRUE(always_on_top_window3->IsAlwaysOnTop()); | |
781 } | |
782 | |
783 // Tests fullscreen window size during root window resize. | |
784 TEST_F(WorkspaceLayoutManagerSoloTest, FullscreenRootWindowResize) { | |
785 gfx::Rect bounds(100, 100, 200, 200); | |
786 std::unique_ptr<WindowOwner> window_owner(CreateTestWindow(bounds)); | |
787 WmWindow* window = window_owner->window(); | |
788 // Fullscreen window fills the whole display. | |
789 window->SetShowState(ui::SHOW_STATE_FULLSCREEN); | |
790 EXPECT_EQ(window->GetDisplayNearestWindow().bounds().ToString(), | |
791 window->GetBounds().ToString()); | |
792 // Enlarge the root window. We should still match the display size. | |
793 UpdateDisplay("800x600"); | |
794 EXPECT_EQ(window->GetDisplayNearestWindow().bounds().ToString(), | |
795 window->GetBounds().ToString()); | |
796 } | |
797 | |
798 // Tests that when the screen gets smaller the windows aren't bigger than | |
799 // the screen. | |
800 TEST_F(WorkspaceLayoutManagerSoloTest, RootWindowResizeShrinksWindows) { | |
801 std::unique_ptr<WindowOwner> window_owner( | |
802 CreateTestWindow(gfx::Rect(10, 20, 500, 400))); | |
803 WmWindow* window = window_owner->window(); | |
804 gfx::Rect work_area = window->GetDisplayNearestWindow().work_area(); | |
805 // Invariant: Window is smaller than work area. | |
806 EXPECT_LE(window->GetBounds().width(), work_area.width()); | |
807 EXPECT_LE(window->GetBounds().height(), work_area.height()); | |
808 | |
809 // Make the root window narrower than our window. | |
810 UpdateDisplay("300x400"); | |
811 work_area = window->GetDisplayNearestWindow().work_area(); | |
812 EXPECT_LE(window->GetBounds().width(), work_area.width()); | |
813 EXPECT_LE(window->GetBounds().height(), work_area.height()); | |
814 | |
815 // Make the root window shorter than our window. | |
816 UpdateDisplay("300x200"); | |
817 work_area = window->GetDisplayNearestWindow().work_area(); | |
818 EXPECT_LE(window->GetBounds().width(), work_area.width()); | |
819 EXPECT_LE(window->GetBounds().height(), work_area.height()); | |
820 | |
821 // Enlarging the root window does not change the window bounds. | |
822 gfx::Rect old_bounds = window->GetBounds(); | |
823 UpdateDisplay("800x600"); | |
824 EXPECT_EQ(old_bounds.width(), window->GetBounds().width()); | |
825 EXPECT_EQ(old_bounds.height(), window->GetBounds().height()); | |
826 } | |
827 | |
828 // Verifies maximizing sets the restore bounds, and restoring | |
829 // restores the bounds. | |
830 TEST_F(WorkspaceLayoutManagerSoloTest, MaximizeSetsRestoreBounds) { | |
831 const gfx::Rect initial_bounds(10, 20, 30, 40); | |
832 std::unique_ptr<WindowOwner> window_owner(CreateTestWindow(initial_bounds)); | |
833 WmWindow* window = window_owner->window(); | |
834 EXPECT_EQ(initial_bounds, window->GetBounds()); | |
835 wm::WindowState* window_state = window->GetWindowState(); | |
836 | |
837 // Maximize it, which will keep the previous restore bounds. | |
838 window->SetShowState(ui::SHOW_STATE_MAXIMIZED); | |
839 EXPECT_EQ("10,20 30x40", window_state->GetRestoreBoundsInParent().ToString()); | |
840 | |
841 // Restore it, which should restore bounds and reset restore bounds. | |
842 window->SetShowState(ui::SHOW_STATE_NORMAL); | |
843 EXPECT_EQ("10,20 30x40", window->GetBounds().ToString()); | |
844 EXPECT_FALSE(window_state->HasRestoreBounds()); | |
845 } | |
846 | |
847 // Verifies maximizing keeps the restore bounds if set. | |
848 TEST_F(WorkspaceLayoutManagerSoloTest, MaximizeResetsRestoreBounds) { | |
849 std::unique_ptr<WindowOwner> window_owner( | |
850 CreateTestWindow(gfx::Rect(1, 2, 3, 4))); | |
851 WmWindow* window = window_owner->window(); | |
852 wm::WindowState* window_state = window->GetWindowState(); | |
853 window_state->SetRestoreBoundsInParent(gfx::Rect(10, 11, 12, 13)); | |
854 | |
855 // Maximize it, which will keep the previous restore bounds. | |
856 window->SetShowState(ui::SHOW_STATE_MAXIMIZED); | |
857 EXPECT_EQ("10,11 12x13", window_state->GetRestoreBoundsInParent().ToString()); | |
858 } | |
859 | |
860 // Verifies that the restore bounds do not get reset when restoring to a | |
861 // maximzied state from a minimized state. | |
862 TEST_F(WorkspaceLayoutManagerSoloTest, | |
863 BoundsAfterRestoringToMaximizeFromMinimize) { | |
864 std::unique_ptr<WindowOwner> window_owner( | |
865 CreateTestWindow(gfx::Rect(1, 2, 3, 4))); | |
866 WmWindow* window = window_owner->window(); | |
867 gfx::Rect bounds(10, 15, 25, 35); | |
868 window->SetBounds(bounds); | |
869 | |
870 wm::WindowState* window_state = window->GetWindowState(); | |
871 // Maximize it, which should reset restore bounds. | |
872 window_state->Maximize(); | |
873 EXPECT_EQ(bounds.ToString(), | |
874 window_state->GetRestoreBoundsInParent().ToString()); | |
875 // Minimize the window. The restore bounds should not change. | |
876 window_state->Minimize(); | |
877 EXPECT_EQ(bounds.ToString(), | |
878 window_state->GetRestoreBoundsInParent().ToString()); | |
879 | |
880 // Show the window again. The window should be maximized, and the restore | |
881 // bounds should not change. | |
882 window->Show(); | |
883 EXPECT_EQ(bounds.ToString(), | |
884 window_state->GetRestoreBoundsInParent().ToString()); | |
885 EXPECT_TRUE(window_state->IsMaximized()); | |
886 | |
887 window_state->Restore(); | |
888 EXPECT_EQ(bounds.ToString(), window->GetBounds().ToString()); | |
889 } | |
890 | |
891 // Verify if the window is not resized during screen lock. See: crbug.com/173127 | |
892 TEST_F(WorkspaceLayoutManagerSoloTest, NotResizeWhenScreenIsLocked) { | |
893 test::TestSessionStateDelegate::SetCanLockScreen(true); | |
894 std::unique_ptr<WindowOwner> window_owner( | |
895 CreateTestWindow(gfx::Rect(1, 2, 3, 4))); | |
896 WmWindow* window = window_owner->window(); | |
897 // window with AlwaysOnTop will be managed by BaseLayoutManager. | |
898 window->SetAlwaysOnTop(true); | |
899 window->Show(); | |
900 | |
901 WmShelf* shelf = GetPrimaryShelf(); | |
902 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS); | |
903 | |
904 window->SetBounds(wm::GetMaximizedWindowBoundsInParent(window)); | |
905 gfx::Rect window_bounds = window->GetBounds(); | |
906 EXPECT_EQ(wm::GetMaximizedWindowBoundsInParent(window).ToString(), | |
907 window_bounds.ToString()); | |
908 | |
909 // The window size should not get touched while we are in lock screen. | |
910 WmShell::Get()->GetSessionStateDelegate()->LockScreen(); | |
911 ShelfLayoutManager* shelf_layout_manager = shelf->shelf_layout_manager(); | |
912 shelf_layout_manager->UpdateVisibilityState(); | |
913 EXPECT_EQ(window_bounds.ToString(), window->GetBounds().ToString()); | |
914 | |
915 // Coming out of the lock screen the window size should still remain. | |
916 WmShell::Get()->GetSessionStateDelegate()->UnlockScreen(); | |
917 shelf_layout_manager->UpdateVisibilityState(); | |
918 EXPECT_EQ(wm::GetMaximizedWindowBoundsInParent(window).ToString(), | |
919 window_bounds.ToString()); | |
920 EXPECT_EQ(window_bounds.ToString(), window->GetBounds().ToString()); | |
921 } | |
922 | |
923 // Following tests are written to test the backdrop functionality. | |
924 | |
925 namespace { | |
926 | |
927 WorkspaceLayoutManager* GetWorkspaceLayoutManager(WmWindow* container) { | |
928 return static_cast<WorkspaceLayoutManager*>(container->GetLayoutManager()); | |
929 } | |
930 | |
931 class WorkspaceLayoutManagerBackdropTest : public AshTest { | |
932 public: | |
933 WorkspaceLayoutManagerBackdropTest() : default_container_(nullptr) {} | |
934 ~WorkspaceLayoutManagerBackdropTest() override {} | |
935 | |
936 void SetUp() override { | |
937 AshTest::SetUp(); | |
938 UpdateDisplay("800x600"); | |
939 default_container_ = | |
940 WmShell::Get()->GetPrimaryRootWindowController()->GetWmContainer( | |
941 kShellWindowId_DefaultContainer); | |
942 } | |
943 | |
944 // Turn the top window back drop on / off. | |
945 void ShowTopWindowBackdrop(bool show) { | |
946 std::unique_ptr<WorkspaceLayoutManagerBackdropDelegate> backdrop; | |
947 if (show) | |
948 backdrop.reset(new WorkspaceBackdropDelegate(default_container_)); | |
949 GetWorkspaceLayoutManager(default_container_) | |
950 ->SetMaximizeBackdropDelegate(std::move(backdrop)); | |
951 // Closing and / or opening can be a delayed operation. | |
952 base::RunLoop().RunUntilIdle(); | |
953 } | |
954 | |
955 // Return the default container. | |
956 WmWindow* default_container() { return default_container_; } | |
957 | |
958 // Return the order of windows (top most first) as they are in the default | |
959 // container. If the window is visible it will be a big letter, otherwise a | |
960 // small one. The backdrop will be an X and unknown windows will be shown as | |
961 // '!'. | |
962 std::string GetWindowOrderAsString(WmWindow* backdrop, | |
963 WmWindow* wa, | |
964 WmWindow* wb, | |
965 WmWindow* wc) { | |
966 std::string result; | |
967 WmWindow::Windows children = default_container()->GetChildren(); | |
968 for (int i = static_cast<int>(children.size()) - 1; i >= 0; --i) { | |
969 if (!result.empty()) | |
970 result += ","; | |
971 if (children[i] == wa) | |
972 result += children[i]->IsVisible() ? "A" : "a"; | |
973 else if (children[i] == wb) | |
974 result += children[i]->IsVisible() ? "B" : "b"; | |
975 else if (children[i] == wc) | |
976 result += children[i]->IsVisible() ? "C" : "c"; | |
977 else if (children[i] == backdrop) | |
978 result += children[i]->IsVisible() ? "X" : "x"; | |
979 else | |
980 result += "!"; | |
981 } | |
982 return result; | |
983 } | |
984 | |
985 private: | |
986 // The default container. | |
987 WmWindow* default_container_; | |
988 | |
989 DISALLOW_COPY_AND_ASSIGN(WorkspaceLayoutManagerBackdropTest); | |
990 }; | |
991 | |
992 } // namespace | |
993 | |
994 // Check that creating the BackDrop without destroying it does not lead into | |
995 // a crash. | |
996 TEST_F(WorkspaceLayoutManagerBackdropTest, BackdropCrashTest) { | |
997 ShowTopWindowBackdrop(true); | |
998 } | |
999 | |
1000 // Verify basic assumptions about the backdrop. | |
1001 TEST_F(WorkspaceLayoutManagerBackdropTest, BasicBackdropTests) { | |
1002 // Create a backdrop and see that there is one window (the backdrop) and | |
1003 // that the size is the same as the default container as well as that it is | |
1004 // not visible. | |
1005 ShowTopWindowBackdrop(true); | |
1006 ASSERT_EQ(1U, default_container()->GetChildren().size()); | |
1007 EXPECT_FALSE(default_container()->GetChildren()[0]->IsVisible()); | |
1008 | |
1009 { | |
1010 // Add a window and make sure that the backdrop is the second child. | |
1011 std::unique_ptr<WindowOwner> window_owner( | |
1012 CreateTestWindow(gfx::Rect(1, 2, 3, 4))); | |
1013 WmWindow* window = window_owner->window(); | |
1014 window->Show(); | |
1015 ASSERT_EQ(2U, default_container()->GetChildren().size()); | |
1016 EXPECT_TRUE(default_container()->GetChildren()[0]->IsVisible()); | |
1017 EXPECT_TRUE(default_container()->GetChildren()[1]->IsVisible()); | |
1018 EXPECT_EQ(window, default_container()->GetChildren()[1]); | |
1019 EXPECT_EQ(default_container()->GetBounds().ToString(), | |
1020 default_container()->GetChildren()[0]->GetBounds().ToString()); | |
1021 } | |
1022 | |
1023 // With the window gone the backdrop should be invisible again. | |
1024 ASSERT_EQ(1U, default_container()->GetChildren().size()); | |
1025 EXPECT_FALSE(default_container()->GetChildren()[0]->IsVisible()); | |
1026 | |
1027 // Destroying the Backdrop should empty the container. | |
1028 ShowTopWindowBackdrop(false); | |
1029 ASSERT_EQ(0U, default_container()->GetChildren().size()); | |
1030 } | |
1031 | |
1032 // Verify that the backdrop gets properly created and placed. | |
1033 TEST_F(WorkspaceLayoutManagerBackdropTest, VerifyBackdropAndItsStacking) { | |
1034 std::unique_ptr<WindowOwner> window1_owner( | |
1035 CreateTestWindow(gfx::Rect(1, 2, 3, 4))); | |
1036 WmWindow* window1 = window1_owner->window(); | |
1037 window1->Show(); | |
1038 | |
1039 // Get the default container and check that only a single window is in there. | |
1040 ASSERT_EQ(1U, default_container()->GetChildren().size()); | |
1041 EXPECT_EQ(window1, default_container()->GetChildren()[0]); | |
1042 EXPECT_EQ("A", GetWindowOrderAsString(nullptr, window1, nullptr, nullptr)); | |
1043 | |
1044 // Create 2 more windows and check that they are also in the container. | |
1045 std::unique_ptr<WindowOwner> window2_owner( | |
1046 CreateTestWindow(gfx::Rect(10, 2, 3, 4))); | |
1047 WmWindow* window2 = window2_owner->window(); | |
1048 std::unique_ptr<WindowOwner> window3_owner( | |
1049 CreateTestWindow(gfx::Rect(20, 2, 3, 4))); | |
1050 WmWindow* window3 = window3_owner->window(); | |
1051 window2->Show(); | |
1052 window3->Show(); | |
1053 | |
1054 WmWindow* backdrop = nullptr; | |
1055 EXPECT_EQ("C,B,A", | |
1056 GetWindowOrderAsString(backdrop, window1, window2, window3)); | |
1057 | |
1058 // Turn on the backdrop mode and check that the window shows up where it | |
1059 // should be (second highest number). | |
1060 ShowTopWindowBackdrop(true); | |
1061 backdrop = default_container()->GetChildren()[2]; | |
1062 EXPECT_EQ("C,X,B,A", | |
1063 GetWindowOrderAsString(backdrop, window1, window2, window3)); | |
1064 | |
1065 // Switch the order of windows and check that it still remains in that | |
1066 // location. | |
1067 default_container()->StackChildAtTop(window2); | |
1068 EXPECT_EQ("B,X,C,A", | |
1069 GetWindowOrderAsString(backdrop, window1, window2, window3)); | |
1070 | |
1071 // Make the top window invisible and check. | |
1072 window2->Hide(); | |
1073 EXPECT_EQ("b,C,X,A", | |
1074 GetWindowOrderAsString(backdrop, window1, window2, window3)); | |
1075 // Then delete window after window and see that everything is in order. | |
1076 window1_owner.reset(); | |
1077 EXPECT_EQ("b,C,X", | |
1078 GetWindowOrderAsString(backdrop, window1, window2, window3)); | |
1079 window3_owner.reset(); | |
1080 EXPECT_EQ("b,x", GetWindowOrderAsString(backdrop, window1, window2, window3)); | |
1081 ShowTopWindowBackdrop(false); | |
1082 EXPECT_EQ("b", GetWindowOrderAsString(nullptr, window1, window2, window3)); | |
1083 } | |
1084 | |
1085 // Tests that when hidding the shelf, that the backdrop resizes to fill the | |
1086 // entire workspace area. | |
1087 TEST_F(WorkspaceLayoutManagerBackdropTest, ShelfVisibilityChangesBounds) { | |
1088 WmShelf* shelf = GetPrimaryShelf(); | |
1089 ShelfLayoutManager* shelf_layout_manager = shelf->shelf_layout_manager(); | |
1090 ShowTopWindowBackdrop(true); | |
1091 RunAllPendingInMessageLoop(); | |
1092 | |
1093 ASSERT_EQ(SHELF_VISIBLE, shelf_layout_manager->visibility_state()); | |
1094 gfx::Rect initial_bounds = default_container()->GetChildren()[0]->GetBounds(); | |
1095 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_ALWAYS_HIDDEN); | |
1096 shelf_layout_manager->UpdateVisibilityState(); | |
1097 | |
1098 // When the shelf is re-shown WorkspaceLayoutManager shrinks all children | |
1099 // including the backdrop. | |
1100 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER); | |
1101 shelf_layout_manager->UpdateVisibilityState(); | |
1102 gfx::Rect reduced_bounds = default_container()->GetChildren()[0]->GetBounds(); | |
1103 EXPECT_LT(reduced_bounds.height(), initial_bounds.height()); | |
1104 | |
1105 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_ALWAYS_HIDDEN); | |
1106 shelf_layout_manager->UpdateVisibilityState(); | |
1107 | |
1108 EXPECT_GT(default_container()->GetChildren()[0]->GetBounds().height(), | |
1109 reduced_bounds.height()); | |
1110 } | |
1111 | |
1112 class WorkspaceLayoutManagerKeyboardTest : public AshTest { | |
1113 public: | |
1114 WorkspaceLayoutManagerKeyboardTest() : layout_manager_(nullptr) {} | |
1115 ~WorkspaceLayoutManagerKeyboardTest() override {} | |
1116 | |
1117 void SetUp() override { | |
1118 AshTest::SetUp(); | |
1119 UpdateDisplay("800x600"); | |
1120 WmWindow* default_container = | |
1121 WmShell::Get()->GetPrimaryRootWindowController()->GetWmContainer( | |
1122 kShellWindowId_DefaultContainer); | |
1123 layout_manager_ = GetWorkspaceLayoutManager(default_container); | |
1124 } | |
1125 | |
1126 void ShowKeyboard() { | |
1127 layout_manager_->OnKeyboardBoundsChanging(keyboard_bounds_); | |
1128 restore_work_area_insets_ = | |
1129 display::Screen::GetScreen()->GetPrimaryDisplay().GetWorkAreaInsets(); | |
1130 WmShell::Get()->SetDisplayWorkAreaInsets( | |
1131 WmShell::Get()->GetPrimaryRootWindow(), | |
1132 gfx::Insets(0, 0, keyboard_bounds_.height(), 0)); | |
1133 } | |
1134 | |
1135 void HideKeyboard() { | |
1136 WmShell::Get()->SetDisplayWorkAreaInsets( | |
1137 WmShell::Get()->GetPrimaryRootWindow(), restore_work_area_insets_); | |
1138 layout_manager_->OnKeyboardBoundsChanging(gfx::Rect()); | |
1139 } | |
1140 | |
1141 // Initializes the keyboard bounds using the bottom half of the work area. | |
1142 void InitKeyboardBounds() { | |
1143 gfx::Rect work_area( | |
1144 display::Screen::GetScreen()->GetPrimaryDisplay().work_area()); | |
1145 keyboard_bounds_.SetRect(work_area.x(), | |
1146 work_area.y() + work_area.height() / 2, | |
1147 work_area.width(), work_area.height() / 2); | |
1148 } | |
1149 | |
1150 void EnableNewVKMode() { | |
1151 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); | |
1152 if (!command_line->HasSwitch(::switches::kUseNewVirtualKeyboardBehavior)) | |
1153 command_line->AppendSwitch(::switches::kUseNewVirtualKeyboardBehavior); | |
1154 } | |
1155 | |
1156 const gfx::Rect& keyboard_bounds() const { return keyboard_bounds_; } | |
1157 | |
1158 private: | |
1159 gfx::Insets restore_work_area_insets_; | |
1160 gfx::Rect keyboard_bounds_; | |
1161 WorkspaceLayoutManager* layout_manager_; | |
1162 | |
1163 DISALLOW_COPY_AND_ASSIGN(WorkspaceLayoutManagerKeyboardTest); | |
1164 }; | |
1165 | |
1166 // Tests that when a child window gains focus the top level window containing it | |
1167 // is resized to fit the remaining workspace area. | |
1168 TEST_F(WorkspaceLayoutManagerKeyboardTest, ChildWindowFocused) { | |
1169 InitKeyboardBounds(); | |
1170 | |
1171 gfx::Rect work_area( | |
1172 display::Screen::GetScreen()->GetPrimaryDisplay().work_area()); | |
1173 | |
1174 std::unique_ptr<WindowOwner> parent_window_owner( | |
1175 CreateToplevelTestWindow(work_area)); | |
1176 WmWindow* parent_window = parent_window_owner->window(); | |
1177 std::unique_ptr<WindowOwner> window_owner(CreateTestWindow(work_area)); | |
1178 WmWindow* window = window_owner->window(); | |
1179 parent_window->AddChild(window); | |
1180 | |
1181 window->Activate(); | |
1182 | |
1183 int available_height = | |
1184 display::Screen::GetScreen()->GetPrimaryDisplay().bounds().height() - | |
1185 keyboard_bounds().height(); | |
1186 | |
1187 gfx::Rect initial_window_bounds(50, 50, 100, 500); | |
1188 parent_window->SetBounds(initial_window_bounds); | |
1189 EXPECT_EQ(initial_window_bounds.ToString(), | |
1190 parent_window->GetBounds().ToString()); | |
1191 ShowKeyboard(); | |
1192 EXPECT_EQ(gfx::Rect(50, 0, 100, available_height).ToString(), | |
1193 parent_window->GetBounds().ToString()); | |
1194 HideKeyboard(); | |
1195 EXPECT_EQ(initial_window_bounds.ToString(), | |
1196 parent_window->GetBounds().ToString()); | |
1197 } | |
1198 | |
1199 TEST_F(WorkspaceLayoutManagerKeyboardTest, AdjustWindowForA11yKeyboard) { | |
1200 InitKeyboardBounds(); | |
1201 gfx::Rect work_area( | |
1202 display::Screen::GetScreen()->GetPrimaryDisplay().work_area()); | |
1203 | |
1204 std::unique_ptr<WindowOwner> window_owner( | |
1205 CreateToplevelTestWindow(work_area)); | |
1206 WmWindow* window = window_owner->window(); | |
1207 // The additional SetBounds() is needed as the aura-mus case uses Widget, | |
1208 // which alters the supplied bounds. | |
1209 window->SetBounds(work_area); | |
1210 | |
1211 int available_height = | |
1212 display::Screen::GetScreen()->GetPrimaryDisplay().bounds().height() - | |
1213 keyboard_bounds().height(); | |
1214 | |
1215 window->Activate(); | |
1216 | |
1217 EXPECT_EQ(gfx::Rect(work_area).ToString(), window->GetBounds().ToString()); | |
1218 ShowKeyboard(); | |
1219 EXPECT_EQ(gfx::Rect(work_area.origin(), | |
1220 gfx::Size(work_area.width(), available_height)) | |
1221 .ToString(), | |
1222 window->GetBounds().ToString()); | |
1223 HideKeyboard(); | |
1224 EXPECT_EQ(gfx::Rect(work_area).ToString(), window->GetBounds().ToString()); | |
1225 | |
1226 gfx::Rect small_window_bound(50, 50, 100, 500); | |
1227 window->SetBounds(small_window_bound); | |
1228 EXPECT_EQ(small_window_bound.ToString(), window->GetBounds().ToString()); | |
1229 ShowKeyboard(); | |
1230 EXPECT_EQ(gfx::Rect(50, 0, 100, available_height).ToString(), | |
1231 window->GetBounds().ToString()); | |
1232 HideKeyboard(); | |
1233 EXPECT_EQ(small_window_bound.ToString(), window->GetBounds().ToString()); | |
1234 | |
1235 gfx::Rect occluded_window_bounds( | |
1236 50, keyboard_bounds().y() + keyboard_bounds().height() / 2, 50, | |
1237 keyboard_bounds().height() / 2); | |
1238 window->SetBounds(occluded_window_bounds); | |
1239 EXPECT_EQ(occluded_window_bounds.ToString(), | |
1240 occluded_window_bounds.ToString()); | |
1241 ShowKeyboard(); | |
1242 EXPECT_EQ( | |
1243 gfx::Rect(50, keyboard_bounds().y() - keyboard_bounds().height() / 2, | |
1244 occluded_window_bounds.width(), occluded_window_bounds.height()) | |
1245 .ToString(), | |
1246 window->GetBounds().ToString()); | |
1247 HideKeyboard(); | |
1248 EXPECT_EQ(occluded_window_bounds.ToString(), window->GetBounds().ToString()); | |
1249 } | |
1250 | |
1251 TEST_F(WorkspaceLayoutManagerKeyboardTest, IgnoreKeyboardBoundsChange) { | |
1252 InitKeyboardBounds(); | |
1253 | |
1254 std::unique_ptr<WindowOwner> window_owner( | |
1255 CreateTestWindow(keyboard_bounds())); | |
1256 WmWindow* window = window_owner->window(); | |
1257 // The additional SetBounds() is needed as the aura-mus case uses Widget, | |
1258 // which alters the supplied bounds. | |
1259 window->SetBounds(keyboard_bounds()); | |
1260 window->GetWindowState()->set_ignore_keyboard_bounds_change(true); | |
1261 window->Activate(); | |
1262 | |
1263 EXPECT_EQ(keyboard_bounds(), window->GetBounds()); | |
1264 ShowKeyboard(); | |
1265 EXPECT_EQ(keyboard_bounds(), window->GetBounds()); | |
1266 } | |
1267 | |
1268 } // namespace ash | |
OLD | NEW |