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