Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / ui / views / widget / desktop_aura / x11_topmost_window_finder_interactive_uitest.cc
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ui/views/widget/desktop_aura/x11_topmost_window_finder.h"
6
7 #include <algorithm>
8 #include <vector>
9 #include <X11/extensions/shape.h>
10 #include <X11/Xlib.h>
11 #include <X11/Xregion.h>
12
13 // Get rid of X11 macros which conflict with gtest.
14 #undef Bool
15 #undef None
16
17 #include "base/memory/scoped_ptr.h"
18 #include "base/path_service.h"
19 #include "third_party/skia/include/core/SkRect.h"
20 #include "third_party/skia/include/core/SkRegion.h"
21 #include "ui/aura/window.h"
22 #include "ui/aura/window_tree_host.h"
23 #include "ui/base/resource/resource_bundle.h"
24 #include "ui/base/ui_base_paths.h"
25 #include "ui/events/platform/x11/x11_event_source.h"
26 #include "ui/gfx/path.h"
27 #include "ui/gfx/path_x11.h"
28 #include "ui/gfx/x/x11_atom_cache.h"
29 #include "ui/gl/gl_surface.h"
30 #include "ui/views/test/views_test_base.h"
31 #include "ui/views/test/x11_property_change_waiter.h"
32 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
33 #include "ui/views/widget/desktop_aura/x11_desktop_handler.h"
34 #include "ui/views/widget/widget.h"
35
36 namespace views {
37
38 namespace {
39
40 // Waits till |window| is minimized.
41 class MinimizeWaiter : public X11PropertyChangeWaiter {
42  public:
43   explicit MinimizeWaiter(XID window)
44       : X11PropertyChangeWaiter(window, "_NET_WM_STATE") {
45     const char* kAtomsToCache[] = { "_NET_WM_STATE_HIDDEN", NULL };
46     atom_cache_.reset(new ui::X11AtomCache(gfx::GetXDisplay(), kAtomsToCache));
47   }
48
49   ~MinimizeWaiter() override {}
50
51  private:
52   // X11PropertyChangeWaiter:
53   bool ShouldKeepOnWaiting(const ui::PlatformEvent& event) override {
54     std::vector<Atom> wm_states;
55     if (ui::GetAtomArrayProperty(xwindow(), "_NET_WM_STATE", &wm_states)) {
56       std::vector<Atom>::iterator it = std::find(
57           wm_states.begin(),
58           wm_states.end(),
59           atom_cache_->GetAtom("_NET_WM_STATE_HIDDEN"));
60       return it == wm_states.end();
61     }
62     return true;
63   }
64
65   scoped_ptr<ui::X11AtomCache> atom_cache_;
66
67   DISALLOW_COPY_AND_ASSIGN(MinimizeWaiter);
68 };
69
70 // Waits till |_NET_CLIENT_LIST_STACKING| is updated to include
71 // |expected_windows|.
72 class StackingClientListWaiter : public X11PropertyChangeWaiter {
73  public:
74   StackingClientListWaiter(XID* expected_windows, size_t count)
75       : X11PropertyChangeWaiter(ui::GetX11RootWindow(),
76                                 "_NET_CLIENT_LIST_STACKING"),
77         expected_windows_(expected_windows, expected_windows + count) {
78   }
79
80   ~StackingClientListWaiter() override {}
81
82   // X11PropertyChangeWaiter:
83   void Wait() override {
84     // StackingClientListWaiter may be created after
85     // _NET_CLIENT_LIST_STACKING already contains |expected_windows|.
86     if (!ShouldKeepOnWaiting(NULL))
87       return;
88
89     X11PropertyChangeWaiter::Wait();
90   }
91
92  private:
93   // X11PropertyChangeWaiter:
94   bool ShouldKeepOnWaiting(const ui::PlatformEvent& event) override {
95     std::vector<XID> stack;
96     ui::GetXWindowStack(ui::GetX11RootWindow(), &stack);
97     for (size_t i = 0; i < expected_windows_.size(); ++i) {
98       std::vector<XID>::iterator it = std::find(
99           stack.begin(), stack.end(), expected_windows_[i]);
100       if (it == stack.end())
101         return true;
102     }
103     return false;
104   }
105
106   std::vector<XID> expected_windows_;
107
108   DISALLOW_COPY_AND_ASSIGN(StackingClientListWaiter);
109 };
110
111 }  // namespace
112
113 class X11TopmostWindowFinderTest : public ViewsTestBase {
114  public:
115   X11TopmostWindowFinderTest() {
116   }
117
118   ~X11TopmostWindowFinderTest() override {}
119
120   // Creates and shows a Widget with |bounds|. The caller takes ownership of
121   // the returned widget.
122   scoped_ptr<Widget> CreateAndShowWidget(const gfx::Rect& bounds) {
123     scoped_ptr<Widget> toplevel(new Widget);
124     Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
125     params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
126     params.native_widget = new DesktopNativeWidgetAura(toplevel.get());
127     params.bounds = bounds;
128     params.remove_standard_frame = true;
129     toplevel->Init(params);
130     toplevel->Show();
131     return toplevel.Pass();
132   }
133
134   // Creates and shows an X window with |bounds|.
135   XID CreateAndShowXWindow(const gfx::Rect& bounds) {
136     XID root = DefaultRootWindow(xdisplay());
137     XID xid = XCreateSimpleWindow(xdisplay(),
138                                   root,
139                                   0, 0, 1, 1,
140                                   0,   // border_width
141                                   0,   // border
142                                   0);  // background
143
144     ui::SetUseOSWindowFrame(xid, false);
145     ShowAndSetXWindowBounds(xid, bounds);
146     return xid;
147   }
148
149   // Shows |xid| and sets its bounds.
150   void ShowAndSetXWindowBounds(XID xid, const gfx::Rect& bounds) {
151     XMapWindow(xdisplay(), xid);
152
153     XWindowChanges changes = {0};
154     changes.x = bounds.x();
155     changes.y = bounds.y();
156     changes.width = bounds.width();
157     changes.height = bounds.height();
158     XConfigureWindow(xdisplay(),
159                      xid,
160                      CWX | CWY | CWWidth | CWHeight,
161                      &changes);
162   }
163
164   Display* xdisplay() {
165     return gfx::GetXDisplay();
166   }
167
168   // Returns the topmost X window at the passed in screen position.
169   XID FindTopmostXWindowAt(int screen_x, int screen_y) {
170     X11TopmostWindowFinder finder;
171     return finder.FindWindowAt(gfx::Point(screen_x, screen_y));
172   }
173
174   // Returns the topmost aura::Window at the passed in screen position. Returns
175   // NULL if the topmost window does not have an associated aura::Window.
176   aura::Window* FindTopmostLocalProcessWindowAt(int screen_x, int screen_y) {
177     X11TopmostWindowFinder finder;
178     return finder.FindLocalProcessWindowAt(gfx::Point(screen_x, screen_y),
179                                            std::set<aura::Window*>());
180   }
181
182   // Returns the topmost aura::Window at the passed in screen position ignoring
183   // |ignore_window|. Returns NULL if the topmost window does not have an
184   // associated aura::Window.
185   aura::Window* FindTopmostLocalProcessWindowWithIgnore(
186       int screen_x,
187       int screen_y,
188       aura::Window* ignore_window) {
189     std::set<aura::Window*> ignore;
190     ignore.insert(ignore_window);
191     X11TopmostWindowFinder finder;
192     return finder.FindLocalProcessWindowAt(gfx::Point(screen_x, screen_y),
193                                            ignore);
194   }
195
196   static void SetUpTestCase() {
197     gfx::GLSurface::InitializeOneOffForTests();
198     ui::RegisterPathProvider();
199     base::FilePath ui_test_pak_path;
200     ASSERT_TRUE(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path));
201     ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path);
202   }
203
204   // ViewsTestBase:
205   void SetUp() override {
206     ViewsTestBase::SetUp();
207
208     // Make X11 synchronous for our display connection. This does not force the
209     // window manager to behave synchronously.
210     XSynchronize(xdisplay(), True);
211
212     // Ensure that the X11DesktopHandler exists. The X11DesktopHandler is
213     // necessary to properly track menu windows.
214     X11DesktopHandler::get();
215   }
216
217   void TearDown() override {
218     XSynchronize(xdisplay(), False);
219     ViewsTestBase::TearDown();
220   }
221
222  private:
223   DISALLOW_COPY_AND_ASSIGN(X11TopmostWindowFinderTest);
224 };
225
226 TEST_F(X11TopmostWindowFinderTest, Basic) {
227   // Avoid positioning test windows at 0x0 because window managers often have a
228   // panel/launcher along one of the screen edges and do not allow windows to
229   // position themselves to overlap the panel/launcher.
230   scoped_ptr<Widget> widget1(
231       CreateAndShowWidget(gfx::Rect(100, 100, 200, 100)));
232   aura::Window* window1 = widget1->GetNativeWindow();
233   XID xid1 = window1->GetHost()->GetAcceleratedWidget();
234
235   XID xid2 = CreateAndShowXWindow(gfx::Rect(200, 100, 100, 200));
236
237   scoped_ptr<Widget> widget3(
238       CreateAndShowWidget(gfx::Rect(100, 190, 200, 110)));
239   aura::Window* window3 = widget3->GetNativeWindow();
240   XID xid3 = window3->GetHost()->GetAcceleratedWidget();
241
242   XID xids[] = { xid1, xid2, xid3 };
243   StackingClientListWaiter waiter(xids, arraysize(xids));
244   waiter.Wait();
245   ui::X11EventSource::GetInstance()->DispatchXEvents();
246
247   EXPECT_EQ(xid1, FindTopmostXWindowAt(150, 150));
248   EXPECT_EQ(window1, FindTopmostLocalProcessWindowAt(150, 150));
249
250   EXPECT_EQ(xid2, FindTopmostXWindowAt(250, 150));
251   EXPECT_EQ(NULL, FindTopmostLocalProcessWindowAt(250, 150));
252
253   EXPECT_EQ(xid3, FindTopmostXWindowAt(250, 250));
254   EXPECT_EQ(window3, FindTopmostLocalProcessWindowAt(250, 250));
255
256   EXPECT_EQ(xid3, FindTopmostXWindowAt(150, 250));
257   EXPECT_EQ(window3, FindTopmostLocalProcessWindowAt(150, 250));
258
259   EXPECT_EQ(xid3, FindTopmostXWindowAt(150, 195));
260   EXPECT_EQ(window3, FindTopmostLocalProcessWindowAt(150, 195));
261
262   EXPECT_NE(xid1, FindTopmostXWindowAt(1000, 1000));
263   EXPECT_NE(xid2, FindTopmostXWindowAt(1000, 1000));
264   EXPECT_NE(xid3, FindTopmostXWindowAt(1000, 1000));
265   EXPECT_EQ(NULL, FindTopmostLocalProcessWindowAt(1000, 1000));
266
267   EXPECT_EQ(window1,
268             FindTopmostLocalProcessWindowWithIgnore(150, 150, window3));
269   EXPECT_EQ(NULL,
270             FindTopmostLocalProcessWindowWithIgnore(250, 250, window3));
271   EXPECT_EQ(NULL,
272             FindTopmostLocalProcessWindowWithIgnore(150, 250, window3));
273   EXPECT_EQ(window1,
274             FindTopmostLocalProcessWindowWithIgnore(150, 195, window3));
275
276   XDestroyWindow(xdisplay(), xid2);
277 }
278
279 // Test that the minimized state is properly handled.
280 TEST_F(X11TopmostWindowFinderTest, Minimized) {
281   scoped_ptr<Widget> widget1(
282       CreateAndShowWidget(gfx::Rect(100, 100, 100, 100)));
283   aura::Window* window1 = widget1->GetNativeWindow();
284   XID xid1 = window1->GetHost()->GetAcceleratedWidget();
285   XID xid2 = CreateAndShowXWindow(gfx::Rect(300, 100, 100, 100));
286
287   XID xids[] = { xid1, xid2 };
288   StackingClientListWaiter stack_waiter(xids, arraysize(xids));
289   stack_waiter.Wait();
290   ui::X11EventSource::GetInstance()->DispatchXEvents();
291
292   EXPECT_EQ(xid1, FindTopmostXWindowAt(150, 150));
293   {
294     MinimizeWaiter minimize_waiter(xid1);
295     XIconifyWindow(xdisplay(), xid1, 0);
296     minimize_waiter.Wait();
297   }
298   EXPECT_NE(xid1, FindTopmostXWindowAt(150, 150));
299   EXPECT_NE(xid2, FindTopmostXWindowAt(150, 150));
300
301   // Repeat test for an X window which does not belong to a views::Widget
302   // because the code path is different.
303   EXPECT_EQ(xid2, FindTopmostXWindowAt(350, 150));
304   {
305     MinimizeWaiter minimize_waiter(xid2);
306     XIconifyWindow(xdisplay(), xid2, 0);
307     minimize_waiter.Wait();
308   }
309   EXPECT_NE(xid1, FindTopmostXWindowAt(350, 150));
310   EXPECT_NE(xid2, FindTopmostXWindowAt(350, 150));
311
312   XDestroyWindow(xdisplay(), xid2);
313 }
314
315 // Test that non-rectangular windows are properly handled.
316 TEST_F(X11TopmostWindowFinderTest, NonRectangular) {
317   if (!ui::IsShapeExtensionAvailable())
318     return;
319
320   scoped_ptr<Widget> widget1(
321       CreateAndShowWidget(gfx::Rect(100, 100, 100, 100)));
322   XID xid1 = widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget();
323   SkRegion* skregion1 = new SkRegion;
324   skregion1->op(SkIRect::MakeXYWH(0, 10, 10, 90), SkRegion::kUnion_Op);
325   skregion1->op(SkIRect::MakeXYWH(10, 0, 90, 100), SkRegion::kUnion_Op);
326   // Widget takes ownership of |skregion1|.
327   widget1->SetShape(skregion1);
328
329   SkRegion skregion2;
330   skregion2.op(SkIRect::MakeXYWH(0, 10, 10, 90), SkRegion::kUnion_Op);
331   skregion2.op(SkIRect::MakeXYWH(10, 0, 90, 100), SkRegion::kUnion_Op);
332   XID xid2 = CreateAndShowXWindow(gfx::Rect(300, 100, 100, 100));
333   REGION* region2 = gfx::CreateRegionFromSkRegion(skregion2);
334   XShapeCombineRegion(xdisplay(), xid2, ShapeBounding, 0, 0, region2,
335       false);
336   XDestroyRegion(region2);
337
338   XID xids[] = { xid1, xid2 };
339   StackingClientListWaiter stack_waiter(xids, arraysize(xids));
340   stack_waiter.Wait();
341   ui::X11EventSource::GetInstance()->DispatchXEvents();
342
343   EXPECT_EQ(xid1, FindTopmostXWindowAt(105, 120));
344   EXPECT_NE(xid1, FindTopmostXWindowAt(105, 105));
345   EXPECT_NE(xid2, FindTopmostXWindowAt(105, 105));
346
347   // Repeat test for an X window which does not belong to a views::Widget
348   // because the code path is different.
349   EXPECT_EQ(xid2, FindTopmostXWindowAt(305, 120));
350   EXPECT_NE(xid1, FindTopmostXWindowAt(305, 105));
351   EXPECT_NE(xid2, FindTopmostXWindowAt(305, 105));
352
353   XDestroyWindow(xdisplay(), xid2);
354 }
355
356 // Test that a window with an empty shape are properly handled.
357 TEST_F(X11TopmostWindowFinderTest, NonRectangularEmptyShape) {
358   if (!ui::IsShapeExtensionAvailable())
359     return;
360
361   scoped_ptr<Widget> widget1(
362       CreateAndShowWidget(gfx::Rect(100, 100, 100, 100)));
363   XID xid1 = widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget();
364   SkRegion* skregion1 = new SkRegion;
365   skregion1->op(SkIRect::MakeXYWH(0, 0, 0, 0), SkRegion::kUnion_Op);
366   // Widget takes ownership of |skregion1|.
367   widget1->SetShape(skregion1);
368
369   XID xids[] = { xid1 };
370   StackingClientListWaiter stack_waiter(xids, arraysize(xids));
371   stack_waiter.Wait();
372   ui::X11EventSource::GetInstance()->DispatchXEvents();
373
374   EXPECT_NE(xid1, FindTopmostXWindowAt(105, 105));
375 }
376
377 // Test that setting a Null shape removes the shape.
378 TEST_F(X11TopmostWindowFinderTest, NonRectangularNullShape) {
379   if (!ui::IsShapeExtensionAvailable())
380     return;
381
382   scoped_ptr<Widget> widget1(
383       CreateAndShowWidget(gfx::Rect(100, 100, 100, 100)));
384   XID xid1 = widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget();
385   SkRegion* skregion1 = new SkRegion;
386   skregion1->op(SkIRect::MakeXYWH(0, 0, 0, 0), SkRegion::kUnion_Op);
387   // Widget takes ownership of |skregion1|.
388   widget1->SetShape(skregion1);
389
390   // Remove the shape - this is now just a normal window.
391   widget1->SetShape(NULL);
392
393   XID xids[] = { xid1 };
394   StackingClientListWaiter stack_waiter(xids, arraysize(xids));
395   stack_waiter.Wait();
396   ui::X11EventSource::GetInstance()->DispatchXEvents();
397
398   EXPECT_EQ(xid1, FindTopmostXWindowAt(105, 105));
399 }
400
401 // Test that the TopmostWindowFinder finds windows which belong to menus
402 // (which may or may not belong to Chrome).
403 TEST_F(X11TopmostWindowFinderTest, Menu) {
404   XID xid = CreateAndShowXWindow(gfx::Rect(100, 100, 100, 100));
405
406   XID root = DefaultRootWindow(xdisplay());
407   XSetWindowAttributes swa;
408   swa.override_redirect = True;
409   XID menu_xid = XCreateWindow(xdisplay(),
410                                root,
411                                0, 0, 1, 1,
412                                0,                  // border width
413                                CopyFromParent,     // depth
414                                InputOutput,
415                                CopyFromParent,     // visual
416                                CWOverrideRedirect,
417                                &swa);
418   {
419     const char* kAtomsToCache[] = { "_NET_WM_WINDOW_TYPE_MENU", NULL };
420     ui::X11AtomCache atom_cache(gfx::GetXDisplay(), kAtomsToCache);
421     ui::SetAtomProperty(menu_xid,
422                         "_NET_WM_WINDOW_TYPE",
423                         "ATOM",
424                         atom_cache.GetAtom("_NET_WM_WINDOW_TYPE_MENU"));
425   }
426   ui::SetUseOSWindowFrame(menu_xid, false);
427   ShowAndSetXWindowBounds(menu_xid, gfx::Rect(140, 110, 100, 100));
428   ui::X11EventSource::GetInstance()->DispatchXEvents();
429
430   // |menu_xid| is never added to _NET_CLIENT_LIST_STACKING.
431   XID xids[] = { xid };
432   StackingClientListWaiter stack_waiter(xids, arraysize(xids));
433   stack_waiter.Wait();
434
435   EXPECT_EQ(xid, FindTopmostXWindowAt(110, 110));
436   EXPECT_EQ(menu_xid, FindTopmostXWindowAt(150, 120));
437   EXPECT_EQ(menu_xid, FindTopmostXWindowAt(210, 120));
438
439   XDestroyWindow(xdisplay(), xid);
440   XDestroyWindow(xdisplay(), menu_xid);
441 }
442
443 }  // namespace views