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

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

Powered by Google App Engine
This is Rietveld 408576698