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, &center);
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