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