1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ash/wm/system_modal_container_layout_manager.h"
6
7 #include <memory>
8
9 #include "ash/keyboard/keyboard_controller_impl.h"
10 #include "ash/keyboard/ui/keyboard_ui.h"
11 #include "ash/keyboard/ui/keyboard_ui_controller.h"
12 #include "ash/keyboard/ui/test/keyboard_test_util.h"
13 #include "ash/public/cpp/keyboard/keyboard_switches.h"
14 #include "ash/public/cpp/shelf_config.h"
15 #include "ash/public/cpp/shell_window_ids.h"
16 #include "ash/root_window_controller.h"
17 #include "ash/shell.h"
18 #include "ash/test/ash_test_base.h"
19 #include "ash/window_factory.h"
20 #include "ash/wm/container_finder.h"
21 #include "ash/wm/window_util.h"
22 #include "base/command_line.h"
23 #include "base/compiler_specific.h"
24 #include "base/run_loop.h"
25 #include "components/session_manager/session_manager_types.h"
26 #include "ui/aura/client/aura_constants.h"
27 #include "ui/aura/test/test_window_delegate.h"
28 #include "ui/aura/window.h"
29 #include "ui/aura/window_event_dispatcher.h"
30 #include "ui/base/hit_test.h"
31 #include "ui/compositor/layer.h"
32 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
33 #include "ui/compositor/test/layer_animator_test_controller.h"
34 #include "ui/events/test/event_generator.h"
35 #include "ui/views/test/capture_tracking_view.h"
36 #include "ui/views/widget/widget.h"
37 #include "ui/views/widget/widget_delegate.h"
38 #include "ui/wm/core/window_util.h"
39
40 namespace ash {
41
42 namespace {
43
GetModalContainer()44 aura::Window* GetModalContainer() {
45 return Shell::GetPrimaryRootWindowController()->GetContainer(
46 ash::kShellWindowId_SystemModalContainer);
47 }
48
AllRootWindowsHaveModalBackgroundsForContainer(int container_id)49 bool AllRootWindowsHaveModalBackgroundsForContainer(int container_id) {
50 aura::Window::Windows containers =
51 GetContainersForAllRootWindows(container_id);
52 bool has_modal_screen = !containers.empty();
53 for (aura::Window* container : containers) {
54 has_modal_screen &= static_cast<SystemModalContainerLayoutManager*>(
55 container->layout_manager())
56 ->has_window_dimmer();
57 }
58 return has_modal_screen;
59 }
60
AllRootWindowsHaveLockedModalBackgrounds()61 bool AllRootWindowsHaveLockedModalBackgrounds() {
62 return AllRootWindowsHaveModalBackgroundsForContainer(
63 kShellWindowId_LockSystemModalContainer);
64 }
65
AllRootWindowsHaveModalBackgrounds()66 bool AllRootWindowsHaveModalBackgrounds() {
67 return AllRootWindowsHaveModalBackgroundsForContainer(
68 kShellWindowId_SystemModalContainer);
69 }
70
71 class TestWindow : public views::WidgetDelegateView {
72 public:
TestWindow(bool modal)73 explicit TestWindow(bool modal) {
74 SetModalType(modal ? ui::MODAL_TYPE_SYSTEM : ui::MODAL_TYPE_NONE);
75 SetPreferredSize(gfx::Size(50, 50));
76 }
77 ~TestWindow() override = default;
78
79 // The window needs be closed from widget in order for
80 // aura::client::kModalKey property to be reset.
CloseTestWindow(aura::Window * window)81 static void CloseTestWindow(aura::Window* window) {
82 views::Widget::GetWidgetForNativeWindow(window)->Close();
83 }
84
85 private:
86 DISALLOW_COPY_AND_ASSIGN(TestWindow);
87 };
88
89 class EventTestWindow : public TestWindow {
90 public:
EventTestWindow(bool modal)91 explicit EventTestWindow(bool modal) : TestWindow(modal), mouse_presses_(0) {}
92 ~EventTestWindow() override = default;
93
ShowTestWindowWithContext(aura::Window * context)94 aura::Window* ShowTestWindowWithContext(aura::Window* context) {
95 views::Widget* widget =
96 views::Widget::CreateWindowWithContext(this, context);
97 widget->Show();
98 return widget->GetNativeView();
99 }
100
ShowTestWindowWithParent(aura::Window * parent)101 aura::Window* ShowTestWindowWithParent(aura::Window* parent) {
102 DCHECK(parent);
103 views::Widget* widget = views::Widget::CreateWindowWithParent(this, parent);
104 widget->Show();
105 return widget->GetNativeView();
106 }
107
108 // Overridden from views::View:
OnMousePressed(const ui::MouseEvent & event)109 bool OnMousePressed(const ui::MouseEvent& event) override {
110 mouse_presses_++;
111 return false;
112 }
113
mouse_presses() const114 int mouse_presses() const { return mouse_presses_; }
115
116 private:
117 int mouse_presses_;
118
119 DISALLOW_COPY_AND_ASSIGN(EventTestWindow);
120 };
121
122 class TransientWindowObserver : public aura::WindowObserver {
123 public:
TransientWindowObserver()124 TransientWindowObserver() : destroyed_(false) {}
125 ~TransientWindowObserver() override = default;
126
destroyed() const127 bool destroyed() const { return destroyed_; }
128
129 // Overridden from aura::WindowObserver:
OnWindowDestroyed(aura::Window * window)130 void OnWindowDestroyed(aura::Window* window) override { destroyed_ = true; }
131
132 private:
133 bool destroyed_;
134
135 DISALLOW_COPY_AND_ASSIGN(TransientWindowObserver);
136 };
137
138 } // namespace
139
140 class SystemModalContainerLayoutManagerTest : public AshTestBase {
141 public:
SetUp()142 void SetUp() override {
143 // Allow a virtual keyboard (and initialize it per default).
144 base::CommandLine::ForCurrentProcess()->AppendSwitch(
145 keyboard::switches::kEnableVirtualKeyboard);
146 AshTestBase::SetUp();
147 }
148
ShowToplevelTestWindow(bool modal)149 aura::Window* ShowToplevelTestWindow(bool modal) {
150 views::Widget* widget = views::Widget::CreateWindowWithContext(
151 new TestWindow(modal), GetContext());
152 widget->Show();
153 return widget->GetNativeView();
154 }
155
ShowTestWindowWithParent(aura::Window * parent,bool modal)156 aura::Window* ShowTestWindowWithParent(aura::Window* parent, bool modal) {
157 views::Widget* widget =
158 views::Widget::CreateWindowWithParent(new TestWindow(modal), parent);
159 widget->Show();
160 return widget->GetNativeView();
161 }
162
163 // Show or hide the keyboard.
ShowKeyboard(bool show)164 void ShowKeyboard(bool show) {
165 auto* keyboard = keyboard::KeyboardUIController::Get();
166 ASSERT_TRUE(keyboard->IsEnabled());
167 if (show == keyboard->IsKeyboardVisible())
168 return;
169
170 if (show) {
171 keyboard->ShowKeyboard(true /* lock */);
172 ASSERT_TRUE(keyboard::WaitUntilShown());
173 } else {
174 keyboard->HideKeyboardByUser();
175 }
176
177 DCHECK_EQ(show, keyboard->IsKeyboardVisible());
178 }
179 };
180
TEST_F(SystemModalContainerLayoutManagerTest,NonModalTransient)181 TEST_F(SystemModalContainerLayoutManagerTest, NonModalTransient) {
182 std::unique_ptr<aura::Window> parent(ShowToplevelTestWindow(false));
183 aura::Window* transient = ShowTestWindowWithParent(parent.get(), false);
184 TransientWindowObserver destruction_observer;
185 transient->AddObserver(&destruction_observer);
186
187 EXPECT_EQ(parent.get(), ::wm::GetTransientParent(transient));
188 EXPECT_EQ(parent->parent(), transient->parent());
189
190 // The transient should be destroyed with its parent.
191 parent.reset();
192 EXPECT_TRUE(destruction_observer.destroyed());
193 }
194
TEST_F(SystemModalContainerLayoutManagerTest,ModalTransient)195 TEST_F(SystemModalContainerLayoutManagerTest, ModalTransient) {
196 std::unique_ptr<aura::Window> parent(ShowToplevelTestWindow(false));
197 // parent should be active.
198 EXPECT_TRUE(wm::IsActiveWindow(parent.get()));
199 aura::Window* t1 = ShowTestWindowWithParent(parent.get(), true);
200
201 TransientWindowObserver do1;
202 t1->AddObserver(&do1);
203
204 EXPECT_EQ(parent.get(), ::wm::GetTransientParent(t1));
205 EXPECT_EQ(GetModalContainer(), t1->parent());
206
207 // t1 should now be active.
208 EXPECT_TRUE(wm::IsActiveWindow(t1));
209
210 // Attempting to click the parent should result in no activation change.
211 ui::test::EventGenerator e1(Shell::GetPrimaryRootWindow(), parent.get());
212 e1.ClickLeftButton();
213 EXPECT_TRUE(wm::IsActiveWindow(t1));
214
215 // Now open another modal transient parented to the original modal transient.
216 aura::Window* t2 = ShowTestWindowWithParent(t1, true);
217 TransientWindowObserver do2;
218 t2->AddObserver(&do2);
219
220 EXPECT_TRUE(wm::IsActiveWindow(t2));
221
222 EXPECT_EQ(t1, ::wm::GetTransientParent(t2));
223 EXPECT_EQ(GetModalContainer(), t2->parent());
224
225 // t2 should still be active, even after clicking on t1.
226 ui::test::EventGenerator e2(Shell::GetPrimaryRootWindow(), t1);
227 e2.ClickLeftButton();
228 EXPECT_TRUE(wm::IsActiveWindow(t2));
229
230 // Both transients should be destroyed with parent.
231 parent.reset();
232 EXPECT_TRUE(do1.destroyed());
233 EXPECT_TRUE(do2.destroyed());
234 }
235
TEST_F(SystemModalContainerLayoutManagerTest,ModalNonTransient)236 TEST_F(SystemModalContainerLayoutManagerTest, ModalNonTransient) {
237 std::unique_ptr<aura::Window> t1(ShowToplevelTestWindow(true));
238 // parent should be active.
239 EXPECT_TRUE(wm::IsActiveWindow(t1.get()));
240 TransientWindowObserver do1;
241 t1->AddObserver(&do1);
242
243 EXPECT_EQ(NULL, ::wm::GetTransientParent(t1.get()));
244 EXPECT_EQ(GetModalContainer(), t1->parent());
245
246 // t1 should now be active.
247 EXPECT_TRUE(wm::IsActiveWindow(t1.get()));
248
249 // Attempting to click the parent should result in no activation change.
250 ui::test::EventGenerator e1(Shell::GetPrimaryRootWindow(),
251 Shell::GetPrimaryRootWindow());
252 e1.ClickLeftButton();
253 EXPECT_TRUE(wm::IsActiveWindow(t1.get()));
254
255 // Now open another modal transient parented to the original modal transient.
256 aura::Window* t2 = ShowTestWindowWithParent(t1.get(), true);
257 TransientWindowObserver do2;
258 t2->AddObserver(&do2);
259
260 EXPECT_TRUE(wm::IsActiveWindow(t2));
261
262 EXPECT_EQ(t1.get(), ::wm::GetTransientParent(t2));
263 EXPECT_EQ(GetModalContainer(), t2->parent());
264
265 // t2 should still be active, even after clicking on t1.
266 ui::test::EventGenerator e2(Shell::GetPrimaryRootWindow(), t1.get());
267 e2.ClickLeftButton();
268 EXPECT_TRUE(wm::IsActiveWindow(t2));
269
270 // Both transients should be destroyed with parent.
271 t1.reset();
272 EXPECT_TRUE(do1.destroyed());
273 EXPECT_TRUE(do2.destroyed());
274 }
275
276 // Tests that we can activate an unrelated window after a modal window is closed
277 // for a window.
TEST_F(SystemModalContainerLayoutManagerTest,CanActivateAfterEndModalSession)278 TEST_F(SystemModalContainerLayoutManagerTest, CanActivateAfterEndModalSession) {
279 std::unique_ptr<aura::Window> unrelated(ShowToplevelTestWindow(false));
280 unrelated->SetBounds(gfx::Rect(100, 100, 50, 50));
281 std::unique_ptr<aura::Window> parent(ShowToplevelTestWindow(false));
282 // parent should be active.
283 EXPECT_TRUE(wm::IsActiveWindow(parent.get()));
284
285 std::unique_ptr<aura::Window> transient(
286 ShowTestWindowWithParent(parent.get(), true));
287 // t1 should now be active.
288 EXPECT_TRUE(wm::IsActiveWindow(transient.get()));
289
290 // Attempting to click the parent should result in no activation change.
291 ui::test::EventGenerator e1(Shell::GetPrimaryRootWindow(), parent.get());
292 e1.ClickLeftButton();
293 EXPECT_TRUE(wm::IsActiveWindow(transient.get()));
294
295 // Now close the transient.
296 transient->Hide();
297 TestWindow::CloseTestWindow(transient.release());
298
299 base::RunLoop().RunUntilIdle();
300
301 // parent should now be active again.
302 EXPECT_TRUE(wm::IsActiveWindow(parent.get()));
303
304 // Attempting to click unrelated should activate it.
305 ui::test::EventGenerator e2(Shell::GetPrimaryRootWindow(), unrelated.get());
306 e2.ClickLeftButton();
307 EXPECT_TRUE(wm::IsActiveWindow(unrelated.get()));
308 }
309
TEST_F(SystemModalContainerLayoutManagerTest,EventFocusContainers)310 TEST_F(SystemModalContainerLayoutManagerTest, EventFocusContainers) {
311 // Create a normal window and attempt to receive a click event.
312 EventTestWindow* main_delegate = new EventTestWindow(false);
313 std::unique_ptr<aura::Window> main(
314 main_delegate->ShowTestWindowWithContext(GetContext()));
315 EXPECT_TRUE(wm::IsActiveWindow(main.get()));
316 ui::test::EventGenerator e1(Shell::GetPrimaryRootWindow(), main.get());
317 e1.ClickLeftButton();
318 EXPECT_EQ(1, main_delegate->mouse_presses());
319
320 EventTestWindow* status_delegate = new EventTestWindow(false);
321 std::unique_ptr<aura::Window> status_control(
322 status_delegate->ShowTestWindowWithParent(
323 Shell::GetPrimaryRootWindowController()->GetContainer(
324 kShellWindowId_ShelfContainer)));
325 status_control->SetBounds(main->bounds());
326
327 // Make sure that status window can receive event.
328 e1.ClickLeftButton();
329 EXPECT_EQ(1, status_delegate->mouse_presses());
330
331 // Create a modal window for the main window and verify that the main window
332 // no longer receives mouse events.
333 EventTestWindow* transient_delegate = new EventTestWindow(true);
334 aura::Window* transient =
335 transient_delegate->ShowTestWindowWithParent(main.get());
336 EXPECT_TRUE(wm::IsActiveWindow(transient));
337 e1.ClickLeftButton();
338
339 EXPECT_EQ(0, transient_delegate->mouse_presses());
340 EXPECT_EQ(1, status_delegate->mouse_presses());
341
342 status_control->Hide();
343 e1.ClickLeftButton();
344 EXPECT_EQ(1, transient_delegate->mouse_presses());
345 EXPECT_EQ(1, status_delegate->mouse_presses());
346
347 for (int block_reason = FIRST_BLOCK_REASON;
348 block_reason < NUMBER_OF_BLOCK_REASONS; ++block_reason) {
349 // Create a window in the lock screen container and ensure that it receives
350 // the mouse event instead of the modal window (crbug.com/110920).
351 BlockUserSession(static_cast<UserSessionBlockReason>(block_reason));
352
353 EventTestWindow* lock_delegate = new EventTestWindow(false);
354 std::unique_ptr<aura::Window> lock(lock_delegate->ShowTestWindowWithParent(
355 Shell::GetPrimaryRootWindowController()->GetContainer(
356 kShellWindowId_LockScreenContainer)));
357 // BlockUserSession could change the workspace size. Make sure |lock| has
358 // the same bounds as |main| so that |lock| gets the generated mouse events.
359 lock->SetBounds(main->bounds());
360
361 EXPECT_TRUE(wm::IsActiveWindow(lock.get()));
362 e1.ClickLeftButton();
363 EXPECT_EQ(1, lock_delegate->mouse_presses());
364 EXPECT_EQ(1, status_delegate->mouse_presses());
365
366 // Make sure that a modal container created by the lock screen can still
367 // receive mouse events.
368 EventTestWindow* lock_modal_delegate = new EventTestWindow(true);
369 aura::Window* lock_modal =
370 lock_modal_delegate->ShowTestWindowWithParent(lock.get());
371 EXPECT_TRUE(wm::IsActiveWindow(lock_modal));
372 e1.ClickLeftButton();
373
374 // Verify that none of the other containers received any more mouse presses.
375 EXPECT_EQ(1, lock_modal_delegate->mouse_presses());
376 EXPECT_EQ(1, lock_delegate->mouse_presses());
377 EXPECT_EQ(1, status_delegate->mouse_presses());
378 EXPECT_EQ(1, main_delegate->mouse_presses());
379 EXPECT_EQ(1, transient_delegate->mouse_presses());
380
381 // Showing status will block events.
382 status_control->Show();
383 e1.ClickLeftButton();
384
385 // Verify that none of the other containers received any more mouse presses.
386 EXPECT_EQ(1, lock_modal_delegate->mouse_presses());
387 EXPECT_EQ(1, lock_delegate->mouse_presses());
388 EXPECT_EQ(1, status_delegate->mouse_presses());
389 EXPECT_EQ(1, main_delegate->mouse_presses());
390 EXPECT_EQ(1, transient_delegate->mouse_presses());
391 status_control->Hide();
392
393 // Close |lock| before unlocking so that Shell::OnLockStateChanged does
394 // not DCHECK on finding a system modal in Lock layer when unlocked.
395 lock.reset();
396
397 UnblockUserSession();
398 }
399 }
400
401 // Test if a user can still click the status area in lock screen
402 // even if the user session has system modal dialog.
TEST_F(SystemModalContainerLayoutManagerTest,ClickStatusWithModalDialogInLockedState)403 TEST_F(SystemModalContainerLayoutManagerTest,
404 ClickStatusWithModalDialogInLockedState) {
405 // Create a normal window and attempt to receive a click event.
406 EventTestWindow* main_delegate = new EventTestWindow(false);
407 std::unique_ptr<aura::Window> main(
408 main_delegate->ShowTestWindowWithContext(GetContext()));
409 ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), main.get());
410
411 // A window for status area to test if it could receive an event
412 // under each condition.
413 EventTestWindow* status_delegate = new EventTestWindow(false);
414 std::unique_ptr<aura::Window> status_control(
415 status_delegate->ShowTestWindowWithParent(
416 Shell::GetPrimaryRootWindowController()->GetContainer(
417 kShellWindowId_ShelfContainer)));
418
419 status_control->SetBounds(main->bounds());
420 status_control->SetName("Status Control");
421
422 // Events are blocked on all windows because status window is above the modal
423 // window.
424 EventTestWindow* modal_delegate = new EventTestWindow(true);
425 aura::Window* modal = modal_delegate->ShowTestWindowWithParent(main.get());
426 modal->SetName("Modal Window");
427 EXPECT_TRUE(wm::IsActiveWindow(modal));
428 generator.ClickLeftButton();
429
430 EXPECT_EQ(0, main_delegate->mouse_presses());
431 EXPECT_EQ(0, modal_delegate->mouse_presses());
432 EXPECT_EQ(0, status_delegate->mouse_presses());
433
434 BlockUserSession(BLOCKED_BY_LOCK_SCREEN);
435
436 // In lock screen, a window in status area container should be able to receive
437 // the event even with a system modal dialog in the user session.
438 generator.ClickLeftButton();
439 EXPECT_EQ(0, main_delegate->mouse_presses());
440 EXPECT_EQ(0, modal_delegate->mouse_presses());
441 EXPECT_EQ(1, status_delegate->mouse_presses());
442 }
443
TEST_F(SystemModalContainerLayoutManagerTest,ModalTransientChildEvents)444 TEST_F(SystemModalContainerLayoutManagerTest, ModalTransientChildEvents) {
445 // Create a normal window and attempt to receive a click event.
446 EventTestWindow* main_delegate = new EventTestWindow(false);
447 std::unique_ptr<aura::Window> main(
448 main_delegate->ShowTestWindowWithContext(GetContext()));
449 EXPECT_TRUE(wm::IsActiveWindow(main.get()));
450 ui::test::EventGenerator e1(Shell::GetPrimaryRootWindow(), main.get());
451 e1.ClickLeftButton();
452 EXPECT_EQ(1, main_delegate->mouse_presses());
453
454 // Create a modal window for the main window and verify that the main window
455 // no longer receives mouse events.
456 EventTestWindow* modal1_delegate = new EventTestWindow(true);
457 aura::Window* modal1 = modal1_delegate->ShowTestWindowWithParent(main.get());
458 EXPECT_TRUE(wm::IsActiveWindow(modal1));
459 e1.ClickLeftButton();
460 EXPECT_EQ(1, modal1_delegate->mouse_presses());
461 EXPECT_EQ(1, main_delegate->mouse_presses());
462
463 // Create a non-modal transient child of modal1 and verify it receives mouse
464 // events instead of main and modal1.
465 EventTestWindow* modal1_transient_delegate = new EventTestWindow(false);
466 aura::Window* modal1_transient =
467 modal1_transient_delegate->ShowTestWindowWithParent(modal1);
468 EXPECT_TRUE(wm::IsActiveWindow(modal1_transient));
469 e1.ClickLeftButton();
470 EXPECT_EQ(1, modal1_transient_delegate->mouse_presses());
471 EXPECT_EQ(1, modal1_delegate->mouse_presses());
472 EXPECT_EQ(1, main_delegate->mouse_presses());
473
474 // Create a child control for modal1_transient and it receives mouse events.
475 aura::test::EventCountDelegate control_delegate;
476 control_delegate.set_window_component(HTCLIENT);
477 std::unique_ptr<aura::Window> child =
478 window_factory::NewWindow(&control_delegate);
479 child->SetType(aura::client::WINDOW_TYPE_CONTROL);
480 child->Init(ui::LAYER_TEXTURED);
481 modal1_transient->AddChild(child.get());
482 child->SetBounds(gfx::Rect(100, 100));
483 child->Show();
484 e1.ClickLeftButton();
485 EXPECT_EQ("1 1", control_delegate.GetMouseButtonCountsAndReset());
486 EXPECT_EQ(1, modal1_transient_delegate->mouse_presses());
487 EXPECT_EQ(1, modal1_delegate->mouse_presses());
488 EXPECT_EQ(1, main_delegate->mouse_presses());
489
490 // Create another modal window for the main window and it receives mouse
491 // events instead of all others.
492 EventTestWindow* modal2_delegate = new EventTestWindow(true);
493 aura::Window* modal2 = modal2_delegate->ShowTestWindowWithParent(main.get());
494 EXPECT_TRUE(wm::IsActiveWindow(modal2));
495 e1.ClickLeftButton();
496 EXPECT_EQ(1, modal2_delegate->mouse_presses());
497 EXPECT_EQ("0 0", control_delegate.GetMouseButtonCountsAndReset());
498 EXPECT_EQ(1, modal1_transient_delegate->mouse_presses());
499 EXPECT_EQ(1, modal1_delegate->mouse_presses());
500 EXPECT_EQ(1, main_delegate->mouse_presses());
501
502 // Create a non-modal transient child of modal2 and it receives events.
503 EventTestWindow* modal2_transient_delegate = new EventTestWindow(false);
504 aura::Window* modal2_transient =
505 modal2_transient_delegate->ShowTestWindowWithParent(modal2);
506 EXPECT_TRUE(wm::IsActiveWindow(modal2_transient));
507 e1.ClickLeftButton();
508 EXPECT_EQ(1, modal2_transient_delegate->mouse_presses());
509 EXPECT_EQ(1, modal2_delegate->mouse_presses());
510 EXPECT_EQ("0 0", control_delegate.GetMouseButtonCountsAndReset());
511 EXPECT_EQ(1, modal1_transient_delegate->mouse_presses());
512 EXPECT_EQ(1, modal1_delegate->mouse_presses());
513 EXPECT_EQ(1, main_delegate->mouse_presses());
514
515 // Create a non-modal transient grandchild of modal2 and it receives events.
516 EventTestWindow* modal2_transient_grandchild_delegate =
517 new EventTestWindow(false);
518 aura::Window* modal2_transient_grandchild =
519 modal2_transient_grandchild_delegate->ShowTestWindowWithParent(
520 modal2_transient);
521 EXPECT_TRUE(wm::IsActiveWindow(modal2_transient_grandchild));
522 e1.ClickLeftButton();
523 EXPECT_EQ(1, modal2_transient_grandchild_delegate->mouse_presses());
524 EXPECT_EQ(1, modal2_transient_delegate->mouse_presses());
525 EXPECT_EQ(1, modal2_delegate->mouse_presses());
526 EXPECT_EQ("0 0", control_delegate.GetMouseButtonCountsAndReset());
527 EXPECT_EQ(1, modal1_transient_delegate->mouse_presses());
528 EXPECT_EQ(1, modal1_delegate->mouse_presses());
529 EXPECT_EQ(1, main_delegate->mouse_presses());
530 }
531
532 // Makes sure we don't crash if a modal window is shown while the parent window
533 // is hidden.
TEST_F(SystemModalContainerLayoutManagerTest,ShowModalWhileHidden)534 TEST_F(SystemModalContainerLayoutManagerTest, ShowModalWhileHidden) {
535 // Hide the lock screen.
536 Shell::GetPrimaryRootWindowController()
537 ->GetContainer(kShellWindowId_SystemModalContainer)
538 ->layer()
539 ->SetOpacity(0);
540
541 // Create a modal window.
542 std::unique_ptr<aura::Window> parent(ShowToplevelTestWindow(false));
543 std::unique_ptr<aura::Window> modal_window(
544 ShowTestWindowWithParent(parent.get(), true));
545 }
546
547 // Verifies we generate a capture lost when showing a modal window.
TEST_F(SystemModalContainerLayoutManagerTest,ChangeCapture)548 TEST_F(SystemModalContainerLayoutManagerTest, ChangeCapture) {
549 std::unique_ptr<aura::Window> widget_window(ShowToplevelTestWindow(false));
550 views::test::CaptureTrackingView* view = new views::test::CaptureTrackingView;
551 views::View* contents_view =
552 views::Widget::GetWidgetForNativeView(widget_window.get())
553 ->GetContentsView();
554 contents_view->AddChildView(view);
555 view->SetBoundsRect(contents_view->bounds());
556
557 gfx::Point center(view->width() / 2, view->height() / 2);
558 views::View::ConvertPointToScreen(view, ¢er);
559 ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), center);
560 generator.PressLeftButton();
561 EXPECT_TRUE(view->got_press());
562 std::unique_ptr<aura::Window> modal_window(
563 ShowTestWindowWithParent(widget_window.get(), true));
564 EXPECT_TRUE(view->got_capture_lost());
565 }
566
567 // Verifies that the window gets moved into the visible screen area upon screen
568 // resize.
TEST_F(SystemModalContainerLayoutManagerTest,KeepVisible)569 TEST_F(SystemModalContainerLayoutManagerTest, KeepVisible) {
570 GetModalContainer()->SetBounds(gfx::Rect(0, 0, 1024, 768));
571 std::unique_ptr<aura::Window> main(
572 ShowTestWindowWithParent(GetModalContainer(), true));
573 const int shelf_height = ShelfConfig::Get()->shelf_size();
574 main->SetBounds(gfx::Rect(924, 668 - shelf_height, 100, 100));
575 // We set now the bounds of the root window to something new which will
576 // Then trigger the repos operation.
577 GetModalContainer()->SetBounds(gfx::Rect(0, 0, 800, 600));
578
579 gfx::Rect bounds = main->bounds();
580 EXPECT_EQ(bounds, gfx::Rect(700, 500 - shelf_height, 100, 100));
581 }
582
583 // Verifies that centered windows will remain centered after the visible screen
584 // area changed.
TEST_F(SystemModalContainerLayoutManagerTest,KeepCentered)585 TEST_F(SystemModalContainerLayoutManagerTest, KeepCentered) {
586 GetModalContainer()->SetBounds(gfx::Rect(0, 0, 800, 600));
587 std::unique_ptr<aura::Window> main(
588 ShowTestWindowWithParent(GetModalContainer(), true));
589 // Center the window.
590 main->SetBounds(gfx::Rect((800 - 512) / 2, (600 - 256) / 2, 512, 256));
591
592 // We set now the bounds of the root window to something new which will
593 // Then trigger the reposition operation.
594 GetModalContainer()->SetBounds(gfx::Rect(0, 0, 600, 400));
595
596 // The window should still be centered.
597 gfx::Rect bounds = main->bounds();
598 EXPECT_EQ(bounds.ToString(),
599 gfx::Rect((600 - 512) / 2, (400 - 256) / 2, 512, 256).ToString());
600 }
601
602 // Verifies that centered windows will remain centered in the secondary screen
603 // with correct global position in screen coordinate system and local position
604 // relative to root window.
TEST_F(SystemModalContainerLayoutManagerTest,KeepCenteredSecondaryScreen)605 TEST_F(SystemModalContainerLayoutManagerTest, KeepCenteredSecondaryScreen) {
606 UpdateDisplay("800x600,800+0-800x600");
607
608 // Create a lock modal window in a lock state.
609 GetSessionControllerClient()->SetSessionState(
610 session_manager::SessionState::OOBE);
611 aura::Window::Windows root_windows = Shell::GetAllRootWindows();
612 aura::Window* secondary_display_modal_container = Shell::GetContainer(
613 root_windows[1], kShellWindowId_LockSystemModalContainer);
614 secondary_display_modal_container->SetBounds(gfx::Rect(0, 0, 800, 600));
615 std::unique_ptr<aura::Window> modal(
616 ShowTestWindowWithParent(secondary_display_modal_container, true));
617
618 // Center the window.
619 modal->SetBounds(gfx::Rect((800 - 512) / 2, (600 - 256) / 2, 512, 256));
620
621 // We set now the bounds of the root window to something new which will
622 // Then trigger the reposition operation.
623 secondary_display_modal_container->SetBounds(gfx::Rect(0, 0, 600, 400));
624
625 // The window should still be centered with global and local coordinates.
626 gfx::Rect modal_bounds_in_screen = modal->GetBoundsInScreen();
627 EXPECT_EQ(
628 modal_bounds_in_screen.ToString(),
629 gfx::Rect(800 + (600 - 512) / 2, (400 - 256) / 2, 512, 256).ToString());
630 gfx::Rect modal_bounds_in_root = modal->bounds();
631 EXPECT_EQ(modal_bounds_in_root.ToString(),
632 gfx::Rect((600 - 512) / 2, (400 - 256) / 2, 512, 256).ToString());
633 }
634
TEST_F(SystemModalContainerLayoutManagerTest,ShowNormalBackgroundOrLocked)635 TEST_F(SystemModalContainerLayoutManagerTest, ShowNormalBackgroundOrLocked) {
636 std::unique_ptr<aura::Window> parent(ShowToplevelTestWindow(false));
637 std::unique_ptr<aura::Window> modal_window(
638 ShowTestWindowWithParent(parent.get(), true));
639
640 // Normal system modal window. Shows normal system modal background and not
641 // locked.
642 EXPECT_TRUE(AllRootWindowsHaveModalBackgrounds());
643 EXPECT_FALSE(AllRootWindowsHaveLockedModalBackgrounds());
644
645 TestWindow::CloseTestWindow(modal_window.release());
646 EXPECT_FALSE(AllRootWindowsHaveModalBackgrounds());
647 EXPECT_FALSE(AllRootWindowsHaveLockedModalBackgrounds());
648
649 for (int block_reason = FIRST_BLOCK_REASON;
650 block_reason < NUMBER_OF_BLOCK_REASONS; ++block_reason) {
651 // Normal system modal window while blocked. Shows blocked system modal
652 // background.
653 BlockUserSession(static_cast<UserSessionBlockReason>(block_reason));
654 std::unique_ptr<aura::Window> lock_parent(ShowTestWindowWithParent(
655 Shell::GetPrimaryRootWindowController()->GetContainer(
656 kShellWindowId_LockScreenContainer),
657 false));
658 std::unique_ptr<aura::Window> lock_modal_window(
659 ShowTestWindowWithParent(lock_parent.get(), true));
660 EXPECT_FALSE(AllRootWindowsHaveModalBackgrounds());
661 EXPECT_TRUE(AllRootWindowsHaveLockedModalBackgrounds());
662 TestWindow::CloseTestWindow(lock_modal_window.release());
663
664 // Normal system modal window while blocked, but it belongs to the normal
665 // window. Shouldn't show blocked system modal background, but normal.
666 std::unique_ptr<aura::Window> modal_window(
667 ShowTestWindowWithParent(parent.get(), true));
668 EXPECT_TRUE(AllRootWindowsHaveModalBackgrounds());
669 EXPECT_FALSE(AllRootWindowsHaveLockedModalBackgrounds());
670 TestWindow::CloseTestWindow(modal_window.release());
671
672 // Close |lock_parent| before unlocking so that Shell::OnLockStateChanged
673 // does not DCHECK on finding a system modal in Lock layer when unlocked.
674 lock_parent.reset();
675
676 UnblockUserSession();
677 // Here we should check the behavior of the locked system modal dialog when
678 // unlocked, but such case isn't handled very well right now.
679 // See crbug.com/157660
680 // TODO(mukai): add the test case when the bug is fixed.
681 }
682 }
683
TEST_F(SystemModalContainerLayoutManagerTest,MultiDisplays)684 TEST_F(SystemModalContainerLayoutManagerTest, MultiDisplays) {
685 UpdateDisplay("500x500,500x500");
686
687 std::unique_ptr<aura::Window> normal(ShowToplevelTestWindow(false));
688 normal->SetBounds(gfx::Rect(100, 100, 50, 50));
689
690 aura::Window::Windows root_windows = Shell::GetAllRootWindows();
691 EXPECT_EQ(2U, root_windows.size());
692 aura::Window* container1 =
693 Shell::GetContainer(root_windows[0], kShellWindowId_SystemModalContainer);
694 aura::Window* container2 =
695 Shell::GetContainer(root_windows[1], kShellWindowId_SystemModalContainer);
696
697 std::unique_ptr<aura::Window> modal1(
698 ShowTestWindowWithParent(container1, true));
699 EXPECT_TRUE(AllRootWindowsHaveModalBackgrounds());
700 EXPECT_TRUE(wm::IsActiveWindow(modal1.get()));
701
702 std::unique_ptr<aura::Window> modal11(
703 ShowTestWindowWithParent(container1, true));
704 EXPECT_TRUE(wm::IsActiveWindow(modal11.get()));
705
706 std::unique_ptr<aura::Window> modal2(
707 ShowTestWindowWithParent(container2, true));
708 EXPECT_TRUE(wm::IsActiveWindow(modal2.get()));
709
710 // Sanity check if they're on the correct containers.
711 EXPECT_EQ(container1, modal1->parent());
712 EXPECT_EQ(container1, modal11->parent());
713 EXPECT_EQ(container2, modal2->parent());
714
715 TestWindow::CloseTestWindow(modal2.release());
716 EXPECT_TRUE(AllRootWindowsHaveModalBackgrounds());
717 EXPECT_TRUE(wm::IsActiveWindow(modal11.get()));
718
719 TestWindow::CloseTestWindow(modal11.release());
720 EXPECT_TRUE(AllRootWindowsHaveModalBackgrounds());
721 EXPECT_TRUE(wm::IsActiveWindow(modal1.get()));
722
723 UpdateDisplay("500x500");
724 EXPECT_TRUE(AllRootWindowsHaveModalBackgrounds());
725 EXPECT_TRUE(wm::IsActiveWindow(modal1.get()));
726
727 UpdateDisplay("500x500,600x600");
728 EXPECT_TRUE(AllRootWindowsHaveModalBackgrounds());
729 EXPECT_TRUE(wm::IsActiveWindow(modal1.get()));
730
731 // No more modal screen.
732 modal1->Hide();
733 TestWindow::CloseTestWindow(modal1.release());
734 EXPECT_FALSE(AllRootWindowsHaveModalBackgrounds());
735 EXPECT_TRUE(wm::IsActiveWindow(normal.get()));
736 }
737
738 // Test that with the visible keyboard, an existing system modal dialog gets
739 // positioned into the visible area.
TEST_F(SystemModalContainerLayoutManagerTest,SystemModalDialogGetPushedFromKeyboard)740 TEST_F(SystemModalContainerLayoutManagerTest,
741 SystemModalDialogGetPushedFromKeyboard) {
742 const gfx::Rect& container_bounds = GetModalContainer()->bounds();
743 // Place the window at the bottom of the screen.
744 gfx::Size modal_size(100, 100);
745 gfx::Point modal_origin = gfx::Point(
746 (container_bounds.right() - modal_size.width()) / 2, // X centered
747 container_bounds.bottom() - modal_size.height()); // at bottom
748 gfx::Rect modal_bounds = gfx::Rect(modal_origin, modal_size);
749
750 // Create a modal window.
751 std::unique_ptr<aura::Window> parent(ShowToplevelTestWindow(false));
752 std::unique_ptr<aura::Window> modal_window(
753 ShowTestWindowWithParent(parent.get(), true));
754 modal_window->SetBounds(modal_bounds);
755
756 EXPECT_EQ(modal_bounds.ToString(), modal_window->bounds().ToString());
757
758 // The keyboard gets shown and the dialog should get pushed.
759 ShowKeyboard(true);
760 EXPECT_NE(modal_bounds.ToString(), modal_window->bounds().ToString());
761 EXPECT_GT(modal_bounds.y(), modal_window->bounds().y());
762 EXPECT_EQ(modal_size.ToString(), modal_window->bounds().size().ToString());
763 EXPECT_EQ(modal_origin.x(), modal_window->bounds().x());
764
765 // After the keyboard is gone, the window will remain where it was.
766 ShowKeyboard(false);
767 EXPECT_NE(modal_bounds.ToString(), modal_window->bounds().ToString());
768 EXPECT_EQ(modal_size.ToString(), modal_window->bounds().size().ToString());
769 EXPECT_EQ(modal_origin.x(), modal_window->bounds().x());
770 }
771
772 // Test that windows will not get cropped through the visible virtual keyboard -
773 // if centered.
TEST_F(SystemModalContainerLayoutManagerTest,SystemModalDialogGetPushedButNotCroppedFromKeyboard)774 TEST_F(SystemModalContainerLayoutManagerTest,
775 SystemModalDialogGetPushedButNotCroppedFromKeyboard) {
776 const gfx::Rect& container_bounds = GetModalContainer()->bounds();
777 const gfx::Size screen_size = Shell::GetPrimaryRootWindow()->bounds().size();
778 // Place the window at the bottom of the screen.
779 gfx::Size modal_size(100, screen_size.height() - 70);
780 gfx::Point modal_origin = gfx::Point(
781 (container_bounds.right() - modal_size.width()) / 2, // X centered
782 container_bounds.bottom() - modal_size.height()); // at bottom
783 gfx::Rect modal_bounds = gfx::Rect(modal_origin, modal_size);
784
785 // Create a modal window.
786 std::unique_ptr<aura::Window> parent(ShowToplevelTestWindow(false));
787 std::unique_ptr<aura::Window> modal_window(
788 ShowTestWindowWithParent(parent.get(), true));
789 modal_window->SetBounds(modal_bounds);
790
791 EXPECT_EQ(modal_bounds.ToString(), modal_window->bounds().ToString());
792
793 // The keyboard gets shown and the dialog should get pushed up, but not get
794 // cropped (and aligned to the top).
795 ShowKeyboard(true);
796 EXPECT_EQ(modal_size.ToString(), modal_window->bounds().size().ToString());
797 EXPECT_EQ(modal_origin.x(), modal_window->bounds().x());
798 EXPECT_EQ(0, modal_window->bounds().y());
799
800 ShowKeyboard(false);
801 }
802
803 // Test that windows will not get cropped through the visible virtual keyboard -
804 // if not centered.
TEST_F(SystemModalContainerLayoutManagerTest,SystemModalDialogGetPushedButNotCroppedFromKeyboardIfNotCentered)805 TEST_F(SystemModalContainerLayoutManagerTest,
806 SystemModalDialogGetPushedButNotCroppedFromKeyboardIfNotCentered) {
807 const gfx::Size screen_size = Shell::GetPrimaryRootWindow()->bounds().size();
808 // Place the window at the bottom of the screen.
809 gfx::Size modal_size(100, screen_size.height() - 70);
810 gfx::Point modal_origin = gfx::Point(10, 20);
811 gfx::Rect modal_bounds = gfx::Rect(modal_origin, modal_size);
812
813 // Create a modal window.
814 std::unique_ptr<aura::Window> parent(ShowToplevelTestWindow(false));
815 std::unique_ptr<aura::Window> modal_window(
816 ShowTestWindowWithParent(parent.get(), true));
817 modal_window->SetBounds(modal_bounds);
818
819 EXPECT_EQ(modal_bounds.ToString(), modal_window->bounds().ToString());
820
821 // The keyboard gets shown and the dialog should get pushed up, but not get
822 // cropped (and aligned to the top).
823 ShowKeyboard(true);
824 EXPECT_EQ(modal_size.ToString(), modal_window->bounds().size().ToString());
825 EXPECT_EQ(modal_origin.x(), modal_window->bounds().x());
826 EXPECT_EQ(0, modal_window->bounds().y());
827
828 ShowKeyboard(false);
829 }
830
TEST_F(SystemModalContainerLayoutManagerTest,UpdateModalType)831 TEST_F(SystemModalContainerLayoutManagerTest, UpdateModalType) {
832 aura::Window* modal_container = Shell::GetContainer(
833 Shell::GetPrimaryRootWindow(), kShellWindowId_SystemModalContainer);
834 aura::Window* window = ShowTestWindowWithParent(modal_container, false);
835 EXPECT_FALSE(Shell::IsSystemModalWindowOpen());
836
837 window->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_SYSTEM);
838 EXPECT_TRUE(Shell::IsSystemModalWindowOpen());
839
840 // Setting twice should not cause error.
841 window->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_SYSTEM);
842 EXPECT_TRUE(Shell::IsSystemModalWindowOpen());
843
844 window->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_NONE);
845 EXPECT_FALSE(Shell::IsSystemModalWindowOpen());
846
847 window->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_SYSTEM);
848 EXPECT_TRUE(Shell::IsSystemModalWindowOpen());
849
850 window_util::CloseWidgetForWindow(window);
851 EXPECT_FALSE(Shell::IsSystemModalWindowOpen());
852 }
853
TEST_F(SystemModalContainerLayoutManagerTest,VisibilityChange)854 TEST_F(SystemModalContainerLayoutManagerTest, VisibilityChange) {
855 std::unique_ptr<aura::Window> window(ShowToplevelTestWindow(false));
856 std::unique_ptr<aura::Window> modal_window(
857 views::Widget::CreateWindowWithContext(new TestWindow(true), GetContext())
858 ->GetNativeWindow());
859 SystemModalContainerLayoutManager* layout_manager =
860 Shell::GetPrimaryRootWindowController()->GetSystemModalLayoutManager(
861 modal_window.get());
862
863 EXPECT_FALSE(Shell::IsSystemModalWindowOpen());
864 EXPECT_FALSE(layout_manager->has_window_dimmer());
865
866 modal_window->Show();
867 EXPECT_TRUE(Shell::IsSystemModalWindowOpen());
868 EXPECT_TRUE(layout_manager->has_window_dimmer());
869
870 // Make sure that a child visibility change should not cause
871 // inconsistent state.
872 std::unique_ptr<aura::Window> child = window_factory::NewWindow();
873 child->SetType(aura::client::WINDOW_TYPE_CONTROL);
874 child->Init(ui::LAYER_TEXTURED);
875 modal_window->AddChild(child.get());
876 child->Show();
877 EXPECT_TRUE(Shell::IsSystemModalWindowOpen());
878 EXPECT_TRUE(layout_manager->has_window_dimmer());
879
880 modal_window->Hide();
881 EXPECT_FALSE(Shell::IsSystemModalWindowOpen());
882 EXPECT_FALSE(layout_manager->has_window_dimmer());
883
884 modal_window->Show();
885 EXPECT_TRUE(Shell::IsSystemModalWindowOpen());
886 EXPECT_TRUE(layout_manager->has_window_dimmer());
887 }
888
889 namespace {
890
891 class InputTestDelegate : public aura::test::TestWindowDelegate {
892 public:
893 InputTestDelegate() = default;
894 ~InputTestDelegate() override = default;
895
RunTest(AshTestBase * test_base)896 void RunTest(AshTestBase* test_base) {
897 std::unique_ptr<aura::Window> window(
898 test_base->CreateTestWindowInShellWithDelegate(
899 this, 0, gfx::Rect(0, 0, 100, 100)));
900 window->Show();
901
902 GenerateEvents(window.get());
903
904 EXPECT_EQ(2, mouse_event_count_);
905 EXPECT_EQ(3, scroll_event_count_);
906 EXPECT_EQ(4, touch_event_count_);
907 EXPECT_EQ(10, gesture_event_count_);
908 Reset();
909
910 views::Widget* widget = views::Widget::CreateWindowWithContext(
911 new TestWindow(true), Shell::GetPrimaryRootWindow(),
912 gfx::Rect(200, 200, 100, 100));
913 widget->Show();
914 EXPECT_TRUE(Shell::IsSystemModalWindowOpen());
915
916 // Events should be blocked.
917 GenerateEvents(window.get());
918
919 EXPECT_EQ(0, mouse_event_count_);
920 EXPECT_EQ(0, scroll_event_count_);
921 EXPECT_EQ(0, touch_event_count_);
922 EXPECT_EQ(0, gesture_event_count_);
923 Reset();
924
925 widget->Close();
926 EXPECT_FALSE(Shell::IsSystemModalWindowOpen());
927
928 GenerateEvents(window.get());
929
930 EXPECT_EQ(2, mouse_event_count_);
931 EXPECT_EQ(3, scroll_event_count_);
932 EXPECT_EQ(4, touch_event_count_);
933 EXPECT_EQ(10, gesture_event_count_);
934 }
935
936 private:
937 // ui::EventHandler:
OnMouseEvent(ui::MouseEvent * event)938 void OnMouseEvent(ui::MouseEvent* event) override { mouse_event_count_++; }
OnScrollEvent(ui::ScrollEvent * event)939 void OnScrollEvent(ui::ScrollEvent* event) override { scroll_event_count_++; }
OnTouchEvent(ui::TouchEvent * event)940 void OnTouchEvent(ui::TouchEvent* event) override { touch_event_count_++; }
OnGestureEvent(ui::GestureEvent * event)941 void OnGestureEvent(ui::GestureEvent* event) override {
942 gesture_event_count_++;
943 }
944
GenerateEvents(aura::Window * window)945 void GenerateEvents(aura::Window* window) {
946 ui::test::EventGenerator event_generator(Shell::GetPrimaryRootWindow(),
947 window);
948 event_generator.ClickLeftButton();
949 event_generator.ScrollSequence(window->bounds().CenterPoint(),
950 base::TimeDelta(), 0, 10, 1, 2);
951 event_generator.PressTouch();
952 event_generator.ReleaseTouch();
953 event_generator.GestureTapAt(window->bounds().CenterPoint());
954 }
955
Reset()956 void Reset() {
957 mouse_event_count_ = 0;
958 scroll_event_count_ = 0;
959 touch_event_count_ = 0;
960 gesture_event_count_ = 0;
961 }
962
963 int mouse_event_count_ = 0;
964 int scroll_event_count_ = 0;
965 int touch_event_count_ = 0;
966 int gesture_event_count_ = 0;
967
968 DISALLOW_COPY_AND_ASSIGN(InputTestDelegate);
969 };
970
971 } // namespace
972
TEST_F(SystemModalContainerLayoutManagerTest,BlockAllEvents)973 TEST_F(SystemModalContainerLayoutManagerTest, BlockAllEvents) {
974 InputTestDelegate delegate;
975 delegate.RunTest(this);
976 }
977
978 // Make sure that events are properly blocked in multi displays environment.
TEST_F(SystemModalContainerLayoutManagerTest,BlockEventsInMultiDisplays)979 TEST_F(SystemModalContainerLayoutManagerTest, BlockEventsInMultiDisplays) {
980 UpdateDisplay("500x500, 500x500");
981 InputTestDelegate delegate;
982 delegate.RunTest(this);
983 }
984
985 } // namespace ash
986