Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(71)

Side by Side Diff: ui/views/widget/desktop_aura/x11_topmost_window_finder_unittest.cc

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

Powered by Google App Engine
This is Rietveld 408576698