1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ui/wm/core/window_modality_controller.h"
6
7 #include "ash/shell.h"
8 #include "ash/test/ash_test_base.h"
9 #include "ash/wm/test_child_modal_parent.h"
10 #include "ash/wm/window_util.h"
11 #include "base/stl_util.h"
12 #include "ui/aura/client/aura_constants.h"
13 #include "ui/aura/client/capture_client.h"
14 #include "ui/aura/test/test_window_delegate.h"
15 #include "ui/aura/test/test_windows.h"
16 #include "ui/aura/window.h"
17 #include "ui/aura/window_event_dispatcher.h"
18 #include "ui/base/ui_base_types.h"
19 #include "ui/events/test/event_generator.h"
20 #include "ui/views/test/capture_tracking_view.h"
21 #include "ui/views/widget/widget.h"
22 #include "ui/wm/core/window_util.h"
23
24 namespace ash {
25
26 using WindowModalityControllerTest = AshTestBase;
27
28 namespace {
29
ValidateStacking(aura::Window * parent,int ids[],int count)30 bool ValidateStacking(aura::Window* parent, int ids[], int count) {
31 for (int i = 0; i < count; ++i) {
32 if (parent->children().at(i)->id() != ids[i])
33 return false;
34 }
35 return true;
36 }
37
38 } // namespace
39
40 // Creates three windows, w1, w11, and w12. w11 is a non-modal transient, w12 is
41 // a modal transient.
42 // Validates:
43 // - it should be possible to activate w12 even when w11 is open.
44 // - activating w1 activates w12 and updates stacking order appropriately.
45 // - closing a window passes focus up the stack.
TEST_F(WindowModalityControllerTest,BasicActivation)46 TEST_F(WindowModalityControllerTest, BasicActivation) {
47 aura::test::TestWindowDelegate d;
48 std::unique_ptr<aura::Window> w1(
49 CreateTestWindowInShellWithDelegate(&d, -1, gfx::Rect()));
50 std::unique_ptr<aura::Window> w11(
51 CreateTestWindowInShellWithDelegate(&d, -11, gfx::Rect()));
52 std::unique_ptr<aura::Window> w12(
53 CreateTestWindowInShellWithDelegate(&d, -12, gfx::Rect()));
54
55 ::wm::AddTransientChild(w1.get(), w11.get());
56 wm::ActivateWindow(w1.get());
57 EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
58 wm::ActivateWindow(w11.get());
59 EXPECT_TRUE(wm::IsActiveWindow(w11.get()));
60
61 w12->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW);
62 ::wm::AddTransientChild(w1.get(), w12.get());
63 wm::ActivateWindow(w12.get());
64 EXPECT_TRUE(wm::IsActiveWindow(w12.get()));
65
66 wm::ActivateWindow(w11.get());
67 EXPECT_TRUE(wm::IsActiveWindow(w11.get()));
68
69 int check1[] = {-1, -12, -11};
70 EXPECT_TRUE(ValidateStacking(w1->parent(), check1, base::size(check1)));
71
72 wm::ActivateWindow(w1.get());
73 EXPECT_TRUE(wm::IsActiveWindow(w12.get()));
74 // Transient children are always stacked above their transient parent, which
75 // is why this order is not -11, -1, -12.
76 int check2[] = {-1, -11, -12};
77 EXPECT_TRUE(ValidateStacking(w1->parent(), check2, base::size(check2)));
78
79 w12.reset();
80 EXPECT_TRUE(wm::IsActiveWindow(w11.get()));
81 w11.reset();
82 EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
83 }
84
85 // Create two toplevel windows w1 and w2, and nest two modals w11 and w111 below
86 // w1.
87 // Validates:
88 // - activating w1 while w11/w111 is showing always activates most deeply nested
89 // descendant.
90 // - closing a window passes focus up the stack.
TEST_F(WindowModalityControllerTest,NestedModals)91 TEST_F(WindowModalityControllerTest, NestedModals) {
92 aura::test::TestWindowDelegate d;
93 std::unique_ptr<aura::Window> w1(
94 CreateTestWindowInShellWithDelegate(&d, -1, gfx::Rect()));
95 std::unique_ptr<aura::Window> w11(
96 CreateTestWindowInShellWithDelegate(&d, -11, gfx::Rect()));
97 std::unique_ptr<aura::Window> w111(
98 CreateTestWindowInShellWithDelegate(&d, -111, gfx::Rect()));
99 std::unique_ptr<aura::Window> w2(
100 CreateTestWindowInShellWithDelegate(&d, -2, gfx::Rect()));
101
102 ::wm::AddTransientChild(w1.get(), w11.get());
103 ::wm::AddTransientChild(w11.get(), w111.get());
104
105 wm::ActivateWindow(w1.get());
106 EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
107 wm::ActivateWindow(w2.get());
108 EXPECT_TRUE(wm::IsActiveWindow(w2.get()));
109
110 // Set up modality.
111 w11->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW);
112 w111->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW);
113
114 wm::ActivateWindow(w1.get());
115 EXPECT_TRUE(wm::IsActiveWindow(w111.get()));
116 int check1[] = {-2, -1, -11, -111};
117 EXPECT_TRUE(ValidateStacking(w1->parent(), check1, base::size(check1)));
118
119 wm::ActivateWindow(w11.get());
120 EXPECT_TRUE(wm::IsActiveWindow(w111.get()));
121 EXPECT_TRUE(ValidateStacking(w1->parent(), check1, base::size(check1)));
122
123 wm::ActivateWindow(w111.get());
124 EXPECT_TRUE(wm::IsActiveWindow(w111.get()));
125 EXPECT_TRUE(ValidateStacking(w1->parent(), check1, base::size(check1)));
126
127 wm::ActivateWindow(w2.get());
128 EXPECT_TRUE(wm::IsActiveWindow(w2.get()));
129 int check2[] = {-1, -11, -111, -2};
130 EXPECT_TRUE(ValidateStacking(w1->parent(), check2, base::size(check2)));
131
132 w2.reset();
133 EXPECT_TRUE(wm::IsActiveWindow(w111.get()));
134 w111.reset();
135 EXPECT_TRUE(wm::IsActiveWindow(w11.get()));
136 w11.reset();
137 EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
138 }
139
140 // Create two toplevel windows w1 and w2, and nest two modals w11 and w111 below
141 // w1.
142 // Validates:
143 // - destroying w11 while w111 is focused activates w1.
TEST_F(WindowModalityControllerTest,NestedModalsOuterClosed)144 TEST_F(WindowModalityControllerTest, NestedModalsOuterClosed) {
145 aura::test::TestWindowDelegate d;
146 std::unique_ptr<aura::Window> w1(
147 CreateTestWindowInShellWithDelegate(&d, -1, gfx::Rect()));
148 std::unique_ptr<aura::Window> w11(
149 CreateTestWindowInShellWithDelegate(&d, -11, gfx::Rect()));
150 // |w111| will be owned and deleted by |w11|.
151 aura::Window* w111 =
152 CreateTestWindowInShellWithDelegate(&d, -111, gfx::Rect());
153 std::unique_ptr<aura::Window> w2(
154 CreateTestWindowInShellWithDelegate(&d, -2, gfx::Rect()));
155
156 ::wm::AddTransientChild(w1.get(), w11.get());
157 ::wm::AddTransientChild(w11.get(), w111);
158
159 wm::ActivateWindow(w1.get());
160 EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
161 wm::ActivateWindow(w2.get());
162 EXPECT_TRUE(wm::IsActiveWindow(w2.get()));
163
164 // Set up modality.
165 w11->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW);
166 w111->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW);
167
168 wm::ActivateWindow(w1.get());
169 EXPECT_TRUE(wm::IsActiveWindow(w111));
170
171 w111->Hide();
172 EXPECT_TRUE(wm::IsActiveWindow(w11.get()));
173
174 // TODO(oshima): Re-showing doesn't set the focus back to
175 // modal window. There is no such use case right now, but it
176 // probably should.
177
178 w11.reset();
179 EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
180 }
181
182 // Modality also prevents events from being passed to the transient parent.
TEST_F(WindowModalityControllerTest,Events)183 TEST_F(WindowModalityControllerTest, Events) {
184 aura::test::TestWindowDelegate d;
185 std::unique_ptr<aura::Window> w1(
186 CreateTestWindowInShellWithDelegate(&d, -1, gfx::Rect(0, 0, 100, 100)));
187 std::unique_ptr<aura::Window> w11(
188 CreateTestWindowInShellWithDelegate(&d, -11, gfx::Rect(20, 20, 50, 50)));
189 std::unique_ptr<aura::Window> w111(
190 CreateTestWindowInShellWithDelegate(&d, -111, gfx::Rect(20, 20, 50, 50)));
191
192 ::wm::AddTransientChild(w1.get(), w11.get());
193
194 // Add a non-modal child to the modal window in order to ensure modality still
195 // works in this case. This is a regression test for https://crbug.com/456697.
196 ::wm::AddTransientChild(w11.get(), w111.get());
197
198 {
199 // Clicking a point within w1 should activate that window.
200 ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
201 gfx::Point(10, 10));
202 generator.ClickLeftButton();
203 EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
204 }
205
206 w11->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW);
207
208 {
209 // Clicking a point within w1 should activate w11.
210 ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
211 gfx::Point(10, 10));
212 generator.ClickLeftButton();
213 EXPECT_TRUE(wm::IsActiveWindow(w11.get()));
214 }
215 }
216
217 // Events on modal parent activate.
TEST_F(WindowModalityControllerTest,EventsForEclipsedWindows)218 TEST_F(WindowModalityControllerTest, EventsForEclipsedWindows) {
219 aura::test::TestWindowDelegate d;
220 std::unique_ptr<aura::Window> w1(
221 CreateTestWindowInShellWithDelegate(&d, -1, gfx::Rect(0, 0, 100, 100)));
222 std::unique_ptr<aura::Window> w11(
223 CreateTestWindowInShellWithDelegate(&d, -11, gfx::Rect(20, 20, 50, 50)));
224 ::wm::AddTransientChild(w1.get(), w11.get());
225 std::unique_ptr<aura::Window> w2(
226 CreateTestWindowInShellWithDelegate(&d, -2, gfx::Rect(0, 0, 50, 50)));
227
228 w11->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW);
229
230 // Partially eclipse w1 with w2.
231 wm::ActivateWindow(w2.get());
232 {
233 // Clicking a point on w1 that is not eclipsed by w2.
234 ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
235 gfx::Point(90, 90));
236 generator.ClickLeftButton();
237 EXPECT_TRUE(wm::IsActiveWindow(w11.get()));
238 }
239 }
240
241 // Creates windows w1 and non activatiable child w11. Creates transient window
242 // w2 and adds it as a transeint child of w1. Ensures that w2 is parented to
243 // the parent of w1, and that GetModalTransient(w11) returns w2.
TEST_F(WindowModalityControllerTest,GetModalTransient)244 TEST_F(WindowModalityControllerTest, GetModalTransient) {
245 aura::test::TestWindowDelegate d;
246 std::unique_ptr<aura::Window> w1(
247 CreateTestWindowInShellWithDelegate(&d, -1, gfx::Rect()));
248 std::unique_ptr<aura::Window> w11(
249 aura::test::CreateTestWindowWithDelegate(&d, -11, gfx::Rect(), w1.get()));
250 std::unique_ptr<aura::Window> w2(
251 CreateTestWindowInShellWithDelegate(&d, -2, gfx::Rect()));
252 w2->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW);
253
254 aura::Window* wt;
255 wt = ::wm::GetModalTransient(w1.get());
256 ASSERT_EQ(nullptr, wt);
257
258 // Parent w2 to w1. It should get parented to the parent of w1.
259 ::wm::AddTransientChild(w1.get(), w2.get());
260 ASSERT_EQ(2U, w1->parent()->children().size());
261 EXPECT_EQ(-2, w1->parent()->children().at(1)->id());
262
263 // Request the modal transient window for w1, it should be w2.
264 wt = ::wm::GetModalTransient(w1.get());
265 ASSERT_NE(nullptr, wt);
266 EXPECT_EQ(-2, wt->id());
267
268 // Request the modal transient window for w11, it should also be w2.
269 wt = ::wm::GetModalTransient(w11.get());
270 ASSERT_NE(nullptr, wt);
271 EXPECT_EQ(-2, wt->id());
272 }
273
274 // Verifies we generate a capture lost when showing a modal window.
TEST_F(WindowModalityControllerTest,ChangeCapture)275 TEST_F(WindowModalityControllerTest, ChangeCapture) {
276 views::Widget* widget = views::Widget::CreateWindowWithContext(
277 nullptr, Shell::GetPrimaryRootWindow(), gfx::Rect(0, 0, 200, 200));
278 std::unique_ptr<aura::Window> widget_window(widget->GetNativeView());
279 views::test::CaptureTrackingView* view = new views::test::CaptureTrackingView;
280 widget->client_view()->AddChildView(view);
281 view->SetBoundsRect(widget->client_view()->GetLocalBounds());
282 widget->Show();
283
284 gfx::Point center(view->width() / 2, view->height() / 2);
285 views::View::ConvertPointToScreen(view, ¢er);
286 ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), center);
287 generator.PressLeftButton();
288 EXPECT_TRUE(view->got_press());
289
290 views::Widget* modal_widget = views::Widget::CreateWindowWithParent(
291 nullptr, widget->GetNativeView(), gfx::Rect(50, 50, 200, 200));
292 std::unique_ptr<aura::Window> modal_window(modal_widget->GetNativeView());
293 modal_window->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW);
294 views::test::CaptureTrackingView* modal_view =
295 new views::test::CaptureTrackingView;
296 modal_widget->client_view()->AddChildView(modal_view);
297 modal_view->SetBoundsRect(modal_widget->client_view()->GetLocalBounds());
298 modal_widget->Show();
299
300 EXPECT_TRUE(view->got_capture_lost());
301 generator.ReleaseLeftButton();
302
303 view->reset();
304
305 EXPECT_FALSE(modal_view->got_capture_lost());
306 EXPECT_FALSE(modal_view->got_press());
307
308 gfx::Point modal_center(modal_view->width() / 2, modal_view->height() / 2);
309 views::View::ConvertPointToScreen(modal_view, &modal_center);
310 generator.MoveMouseTo(modal_center, 1);
311 generator.PressLeftButton();
312 EXPECT_TRUE(modal_view->got_press());
313 EXPECT_FALSE(modal_view->got_capture_lost());
314 EXPECT_FALSE(view->got_capture_lost());
315 EXPECT_FALSE(view->got_press());
316 }
317
318 // Test that when a child modal window becomes visible, we only release the
319 // capture window if the current capture window is in the hierarchy of the child
320 // modal window's modal parent window.
TEST_F(WindowModalityControllerTest,ReleaseCapture)321 TEST_F(WindowModalityControllerTest, ReleaseCapture) {
322 // Create a window hierarchy like this:
323 // _______________w0______________
324 // | | |
325 // w1 <------ w3 w2
326 // | (modal to)
327 // w11
328
329 aura::test::TestWindowDelegate d;
330 std::unique_ptr<aura::Window> w1(
331 CreateTestWindowInShellWithDelegate(&d, -1, gfx::Rect()));
332 std::unique_ptr<aura::Window> w11(
333 aura::test::CreateTestWindowWithDelegate(&d, -11, gfx::Rect(), w1.get()));
334 std::unique_ptr<aura::Window> w2(
335 CreateTestWindowInShellWithDelegate(&d, -2, gfx::Rect()));
336 std::unique_ptr<aura::Window> w3(
337 CreateTestWindowInShellWithDelegate(&d, -2, gfx::Rect()));
338 w3->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_CHILD);
339 ::wm::SetModalParent(w3.get(), w1.get());
340
341 // w1's capture should be released when w3 becomes visible.
342 w3->Hide();
343 aura::client::GetCaptureClient(Shell::GetPrimaryRootWindow())
344 ->SetCapture(w1.get());
345 EXPECT_EQ(w1.get(),
346 aura::client::GetCaptureClient(Shell::GetPrimaryRootWindow())
347 ->GetGlobalCaptureWindow());
348 w3->Show();
349 EXPECT_NE(w1.get(),
350 aura::client::GetCaptureClient(Shell::GetPrimaryRootWindow())
351 ->GetGlobalCaptureWindow());
352
353 // w11's capture should be released when w3 becomes visible.
354 w3->Hide();
355 aura::client::GetCaptureClient(Shell::GetPrimaryRootWindow())
356 ->SetCapture(w11.get());
357 EXPECT_EQ(w11.get(),
358 aura::client::GetCaptureClient(Shell::GetPrimaryRootWindow())
359 ->GetGlobalCaptureWindow());
360 w3->Show();
361 EXPECT_NE(w11.get(),
362 aura::client::GetCaptureClient(Shell::GetPrimaryRootWindow())
363 ->GetGlobalCaptureWindow());
364
365 // w2's capture should not be released when w3 becomes visible.
366 w3->Hide();
367 aura::client::GetCaptureClient(Shell::GetPrimaryRootWindow())
368 ->SetCapture(w2.get());
369 EXPECT_EQ(w2.get(),
370 aura::client::GetCaptureClient(Shell::GetPrimaryRootWindow())
371 ->GetGlobalCaptureWindow());
372 w3->Show();
373 EXPECT_EQ(w2.get(),
374 aura::client::GetCaptureClient(Shell::GetPrimaryRootWindow())
375 ->GetGlobalCaptureWindow());
376 }
377
378 class TouchTrackerWindowDelegate : public aura::test::TestWindowDelegate {
379 public:
TouchTrackerWindowDelegate()380 TouchTrackerWindowDelegate()
381 : received_touch_(false), last_event_type_(ui::ET_UNKNOWN) {}
382 ~TouchTrackerWindowDelegate() override = default;
383
reset()384 void reset() {
385 received_touch_ = false;
386 last_event_type_ = ui::ET_UNKNOWN;
387 }
388
received_touch() const389 bool received_touch() const { return received_touch_; }
last_event_type() const390 ui::EventType last_event_type() const { return last_event_type_; }
391
392 private:
393 // Overridden from aura::test::TestWindowDelegate.
OnTouchEvent(ui::TouchEvent * event)394 void OnTouchEvent(ui::TouchEvent* event) override {
395 received_touch_ = true;
396 last_event_type_ = event->type();
397 aura::test::TestWindowDelegate::OnTouchEvent(event);
398 }
399
400 bool received_touch_;
401 ui::EventType last_event_type_;
402
403 DISALLOW_COPY_AND_ASSIGN(TouchTrackerWindowDelegate);
404 };
405
406 // Modality should prevent events from being passed to transient window tree
407 // rooted to the top level window.
TEST_F(WindowModalityControllerTest,TouchEvent)408 TEST_F(WindowModalityControllerTest, TouchEvent) {
409 TouchTrackerWindowDelegate d1;
410 std::unique_ptr<aura::Window> w1(
411 CreateTestWindowInShellWithDelegate(&d1, -1, gfx::Rect(0, 0, 100, 100)));
412 TouchTrackerWindowDelegate d11;
413 std::unique_ptr<aura::Window> w11(CreateTestWindowInShellWithDelegate(
414 &d11, -11, gfx::Rect(20, 20, 20, 20)));
415 TouchTrackerWindowDelegate d12;
416 std::unique_ptr<aura::Window> w12(CreateTestWindowInShellWithDelegate(
417 &d12, -12, gfx::Rect(40, 20, 20, 20)));
418 TouchTrackerWindowDelegate d2;
419 std::unique_ptr<aura::Window> w2(CreateTestWindowInShellWithDelegate(
420 &d2, -2, gfx::Rect(100, 0, 100, 100)));
421
422 // Make |w11| and |w12| non-resizable to avoid touch events inside its
423 // transient parent |w1| from going to them because of
424 // EasyResizeWindowTargeter.
425 w11->SetProperty(aura::client::kResizeBehaviorKey,
426 aura::client::kResizeBehaviorCanMaximize |
427 aura::client::kResizeBehaviorCanMinimize);
428 w12->SetProperty(aura::client::kResizeBehaviorKey,
429 aura::client::kResizeBehaviorCanMaximize |
430 aura::client::kResizeBehaviorCanMinimize);
431 ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
432 gfx::Point(10, 10));
433
434 ::wm::AddTransientChild(w1.get(), w11.get());
435 ::wm::AddTransientChild(w1.get(), w12.get());
436 d1.reset();
437 d11.reset();
438 d12.reset();
439 d2.reset();
440
441 {
442 // Adding a modal window while a touch is down in top level transient window
443 // should fire a touch cancel.
444 generator.PressTouch();
445 generator.MoveTouch(gfx::Point(10, 15));
446 EXPECT_TRUE(d1.received_touch());
447 EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
448 d1.reset();
449 d11.reset();
450 d12.reset();
451 d2.reset();
452
453 w11->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW);
454 EXPECT_TRUE(d1.received_touch());
455 EXPECT_EQ(ui::ET_TOUCH_CANCELLED, d1.last_event_type());
456 EXPECT_FALSE(d11.received_touch());
457 EXPECT_FALSE(d12.received_touch());
458 EXPECT_FALSE(d2.received_touch());
459 w11->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_NONE);
460 }
461
462 {
463 // Adding a modal window while a touch is down in window tree rooted to top
464 // level transient window should fire a touch cancel.
465 generator.MoveTouch(gfx::Point(50, 30));
466 generator.PressTouch();
467 generator.MoveTouch(gfx::Point(50, 35));
468 EXPECT_TRUE(d12.received_touch());
469 EXPECT_TRUE(wm::IsActiveWindow(w12.get()));
470 d1.reset();
471 d11.reset();
472 d12.reset();
473 d2.reset();
474
475 w11->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW);
476 EXPECT_FALSE(d1.received_touch());
477 EXPECT_FALSE(d11.received_touch());
478 EXPECT_TRUE(d12.received_touch());
479 EXPECT_EQ(ui::ET_TOUCH_CANCELLED, d12.last_event_type());
480 EXPECT_FALSE(d2.received_touch());
481 w11->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_NONE);
482 }
483
484 {
485 // Adding a modal window while a touch is down in other transient window
486 // tree should not fire a touch cancel.
487 wm::ActivateWindow(w2.get());
488 generator.MoveTouch(gfx::Point(110, 10));
489 generator.PressTouch();
490 generator.MoveTouch(gfx::Point(110, 15));
491 EXPECT_TRUE(d2.received_touch());
492 EXPECT_TRUE(wm::IsActiveWindow(w2.get()));
493 d1.reset();
494 d11.reset();
495 d12.reset();
496 d2.reset();
497
498 w11->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW);
499 EXPECT_FALSE(d1.received_touch());
500 EXPECT_FALSE(d11.received_touch());
501 EXPECT_FALSE(d12.received_touch());
502 EXPECT_FALSE(d2.received_touch());
503 w11->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_NONE);
504 }
505
506 {
507 // Adding a child type modal window while a touch is down in other transient
508 // window tree should not fire a touch cancel. (See
509 // https://crbug.com/900321)
510 wm::ActivateWindow(w2.get());
511 generator.MoveTouch(gfx::Point(110, 10));
512 generator.PressTouch();
513 generator.MoveTouch(gfx::Point(110, 15));
514 EXPECT_TRUE(d2.received_touch());
515 EXPECT_TRUE(wm::IsActiveWindow(w2.get()));
516 d1.reset();
517 d11.reset();
518 d12.reset();
519 d2.reset();
520
521 w11->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_CHILD);
522 EXPECT_FALSE(d1.received_touch());
523 EXPECT_FALSE(d11.received_touch());
524 EXPECT_FALSE(d12.received_touch());
525 EXPECT_FALSE(d2.received_touch());
526 w11->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_NONE);
527 }
528 }
529
530 // Child-modal test.
531 // Creates:
532 // - A |top_level| window that hosts a |modal_parent| window within itself. The
533 // |top_level| and |modal_parent| windows are not the same window. The
534 // |modal_parent| window is not activatable, because it's contained within the
535 // |top_level| window.
536 // - A |modal_child| window with parent window |top_level|, but is modal to
537 // |modal_parent| window.
538 // Validates:
539 // - Clicking on the |modal_parent| should activate the |modal_child| window.
540 // - Clicking on the |top_level| window outside of the |modal_parent| bounds
541 // should activate the |top_level| window.
542 // - Clicking on the |modal_child| while |top_level| is active should activate
543 // the |modal_child| window.
544 // - Focus should follow the active window.
TEST_F(WindowModalityControllerTest,ChildModal)545 TEST_F(WindowModalityControllerTest, ChildModal) {
546 TestChildModalParent* delegate = TestChildModalParent::Show(GetContext());
547 aura::Window* top_level = delegate->GetWidget()->GetNativeView();
548 EXPECT_TRUE(wm::IsActiveWindow(top_level));
549
550 aura::Window* modal_parent = delegate->GetModalParent();
551 EXPECT_NE(nullptr, modal_parent);
552 EXPECT_NE(top_level, modal_parent);
553 EXPECT_FALSE(wm::IsActiveWindow(modal_parent));
554
555 aura::Window* modal_child = delegate->ShowModalChild();
556 EXPECT_NE(nullptr, modal_child);
557 EXPECT_TRUE(wm::IsActiveWindow(modal_child));
558 EXPECT_FALSE(wm::IsActiveWindow(modal_parent));
559 EXPECT_FALSE(wm::IsActiveWindow(top_level));
560
561 EXPECT_TRUE(modal_child->HasFocus());
562 EXPECT_FALSE(modal_parent->HasFocus());
563 EXPECT_FALSE(top_level->HasFocus());
564
565 wm::ActivateWindow(modal_parent);
566
567 EXPECT_TRUE(wm::IsActiveWindow(modal_child));
568 EXPECT_FALSE(wm::IsActiveWindow(modal_parent));
569 EXPECT_FALSE(wm::IsActiveWindow(top_level));
570
571 EXPECT_TRUE(modal_child->HasFocus());
572 EXPECT_FALSE(modal_parent->HasFocus());
573 EXPECT_FALSE(top_level->HasFocus());
574
575 wm::ActivateWindow(top_level);
576
577 EXPECT_FALSE(wm::IsActiveWindow(modal_child));
578 EXPECT_FALSE(wm::IsActiveWindow(modal_parent));
579 EXPECT_TRUE(wm::IsActiveWindow(top_level));
580
581 EXPECT_FALSE(modal_child->HasFocus());
582 EXPECT_FALSE(modal_parent->HasFocus());
583 EXPECT_TRUE(top_level->HasFocus());
584
585 wm::ActivateWindow(modal_child);
586
587 EXPECT_TRUE(wm::IsActiveWindow(modal_child));
588 EXPECT_FALSE(wm::IsActiveWindow(modal_parent));
589 EXPECT_FALSE(wm::IsActiveWindow(top_level));
590
591 EXPECT_TRUE(modal_child->HasFocus());
592 EXPECT_FALSE(modal_parent->HasFocus());
593 EXPECT_FALSE(top_level->HasFocus());
594 }
595
596 // Same as |ChildModal| test, but using |EventGenerator| rather than bypassing
597 // it by calling |ActivateWindow|.
TEST_F(WindowModalityControllerTest,ChildModalEventGenerator)598 TEST_F(WindowModalityControllerTest, ChildModalEventGenerator) {
599 TestChildModalParent* delegate = TestChildModalParent::Show(GetContext());
600 aura::Window* top_level = delegate->GetWidget()->GetNativeView();
601 EXPECT_TRUE(wm::IsActiveWindow(top_level));
602
603 aura::Window* modal_parent = delegate->GetModalParent();
604 EXPECT_NE(nullptr, modal_parent);
605 EXPECT_NE(top_level, modal_parent);
606 EXPECT_FALSE(wm::IsActiveWindow(modal_parent));
607
608 aura::Window* modal_child = delegate->ShowModalChild();
609 EXPECT_NE(nullptr, modal_child);
610 EXPECT_TRUE(wm::IsActiveWindow(modal_child));
611 EXPECT_FALSE(wm::IsActiveWindow(modal_parent));
612 EXPECT_FALSE(wm::IsActiveWindow(top_level));
613
614 EXPECT_TRUE(modal_child->HasFocus());
615 EXPECT_FALSE(modal_parent->HasFocus());
616 EXPECT_FALSE(top_level->HasFocus());
617
618 {
619 ui::test::EventGenerator generator(
620 Shell::GetPrimaryRootWindow(),
621 top_level->bounds().origin() +
622 gfx::Vector2d(10, top_level->bounds().height() - 10));
623 generator.ClickLeftButton();
624 generator.ClickLeftButton();
625
626 EXPECT_TRUE(wm::IsActiveWindow(modal_child));
627 EXPECT_FALSE(wm::IsActiveWindow(modal_parent));
628 EXPECT_FALSE(wm::IsActiveWindow(top_level));
629
630 EXPECT_TRUE(modal_child->HasFocus());
631 EXPECT_FALSE(modal_parent->HasFocus());
632 EXPECT_FALSE(top_level->HasFocus());
633 }
634
635 {
636 ui::test::EventGenerator generator(
637 Shell::GetPrimaryRootWindow(),
638 top_level->bounds().origin() + gfx::Vector2d(10, 10));
639 generator.ClickLeftButton();
640
641 EXPECT_FALSE(wm::IsActiveWindow(modal_child));
642 EXPECT_FALSE(wm::IsActiveWindow(modal_parent));
643 EXPECT_TRUE(wm::IsActiveWindow(top_level));
644
645 EXPECT_FALSE(modal_child->HasFocus());
646 EXPECT_FALSE(modal_parent->HasFocus());
647 EXPECT_TRUE(top_level->HasFocus());
648 }
649
650 {
651 ui::test::EventGenerator generator(
652 Shell::GetPrimaryRootWindow(),
653 modal_child->bounds().origin() + gfx::Vector2d(10, 10));
654 generator.ClickLeftButton();
655
656 EXPECT_TRUE(wm::IsActiveWindow(modal_child));
657 EXPECT_FALSE(wm::IsActiveWindow(modal_parent));
658 EXPECT_FALSE(wm::IsActiveWindow(top_level));
659
660 EXPECT_TRUE(modal_child->HasFocus());
661 EXPECT_FALSE(modal_parent->HasFocus());
662 EXPECT_FALSE(top_level->HasFocus());
663 }
664 }
665
666 // Window-modal test for the case when the originally clicked window is an
667 // ancestor of the modal parent.
TEST_F(WindowModalityControllerTest,WindowModalAncestor)668 TEST_F(WindowModalityControllerTest, WindowModalAncestor) {
669 aura::test::TestWindowDelegate d;
670 std::unique_ptr<aura::Window> w1(
671 CreateTestWindowInShellWithDelegate(&d, -1, gfx::Rect()));
672 std::unique_ptr<aura::Window> w2(
673 aura::test::CreateTestWindowWithDelegate(&d, -11, gfx::Rect(), w1.get()));
674 std::unique_ptr<aura::Window> w3(
675 aura::test::CreateTestWindowWithDelegate(&d, -11, gfx::Rect(), w2.get()));
676 std::unique_ptr<aura::Window> w4(
677 CreateTestWindowInShellWithDelegate(&d, -2, gfx::Rect()));
678 w4->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW);
679 ::wm::AddTransientChild(w1.get(), w4.get());
680
681 wm::ActivateWindow(w1.get());
682 EXPECT_TRUE(wm::IsActiveWindow(w4.get()));
683
684 wm::ActivateWindow(w2.get());
685 EXPECT_TRUE(wm::IsActiveWindow(w4.get()));
686
687 wm::ActivateWindow(w3.get());
688 EXPECT_TRUE(wm::IsActiveWindow(w4.get()));
689
690 wm::ActivateWindow(w4.get());
691 EXPECT_TRUE(wm::IsActiveWindow(w4.get()));
692 }
693
694 // Child-modal test for the case when the originally clicked window is an
695 // ancestor of the modal parent.
TEST_F(WindowModalityControllerTest,ChildModalAncestor)696 TEST_F(WindowModalityControllerTest, ChildModalAncestor) {
697 aura::test::TestWindowDelegate d;
698 std::unique_ptr<aura::Window> w1(
699 CreateTestWindowInShellWithDelegate(&d, -1, gfx::Rect()));
700 std::unique_ptr<aura::Window> w2(
701 aura::test::CreateTestWindowWithDelegate(&d, -11, gfx::Rect(), w1.get()));
702 std::unique_ptr<aura::Window> w3(
703 aura::test::CreateTestWindowWithDelegate(&d, -11, gfx::Rect(), w2.get()));
704 std::unique_ptr<aura::Window> w4(
705 CreateTestWindowInShellWithDelegate(&d, -2, gfx::Rect()));
706 w4->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_CHILD);
707 ::wm::SetModalParent(w4.get(), w2.get());
708 ::wm::AddTransientChild(w1.get(), w4.get());
709
710 wm::ActivateWindow(w1.get());
711 EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
712
713 wm::ActivateWindow(w2.get());
714 EXPECT_TRUE(wm::IsActiveWindow(w4.get()));
715
716 wm::ActivateWindow(w3.get());
717 EXPECT_TRUE(wm::IsActiveWindow(w4.get()));
718
719 wm::ActivateWindow(w4.get());
720 EXPECT_TRUE(wm::IsActiveWindow(w4.get()));
721 }
722
723 } // namespace ash
724