MacViews: Get c/b/ui/views/tabs to build on Mac
[chromium-blink-merge.git] / ui / views / widget / desktop_aura / x11_topmost_window_finder_interactive_uitest.cc
blobedf9bca7e7618a52e6698b868bde866da452faee
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.
5 #include "ui/views/widget/desktop_aura/x11_topmost_window_finder.h"
7 #include <algorithm>
8 #include <vector>
9 #include <X11/extensions/shape.h>
10 #include <X11/Xlib.h>
11 #include <X11/Xregion.h>
13 // Get rid of X11 macros which conflict with gtest.
14 #undef Bool
15 #undef None
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"
36 namespace views {
38 namespace {
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));
49 virtual ~MinimizeWaiter() {
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();
63 return true;
66 scoped_ptr<ui::X11AtomCache> atom_cache_;
68 DISALLOW_COPY_AND_ASSIGN(MinimizeWaiter);
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) {
81 virtual ~StackingClientListWaiter() {
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;
91 X11PropertyChangeWaiter::Wait();
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;
105 return false;
108 std::vector<XID> expected_windows_;
110 DISALLOW_COPY_AND_ASSIGN(StackingClientListWaiter);
113 } // namespace
115 class X11TopmostWindowFinderTest : public ViewsTestBase {
116 public:
117 X11TopmostWindowFinderTest() {
120 virtual ~X11TopmostWindowFinderTest() {
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();
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
147 ui::SetUseOSWindowFrame(xid, false);
148 ShowAndSetXWindowBounds(xid, bounds);
149 return xid;
152 // Shows |xid| and sets its bounds.
153 void ShowAndSetXWindowBounds(XID xid, const gfx::Rect& bounds) {
154 XMapWindow(xdisplay(), xid);
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);
167 Display* xdisplay() {
168 return gfx::GetXDisplay();
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));
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*>());
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);
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);
207 // ViewsTestBase:
208 virtual void SetUp() override {
209 ViewsTestBase::SetUp();
211 // Make X11 synchronous for our display connection. This does not force the
212 // window manager to behave synchronously.
213 XSynchronize(xdisplay(), True);
215 // Ensure that the X11DesktopHandler exists. The X11DesktopHandler is
216 // necessary to properly track menu windows.
217 X11DesktopHandler::get();
220 virtual void TearDown() override {
221 XSynchronize(xdisplay(), False);
222 ViewsTestBase::TearDown();
225 private:
226 DISALLOW_COPY_AND_ASSIGN(X11TopmostWindowFinderTest);
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();
238 XID xid2 = CreateAndShowXWindow(gfx::Rect(200, 100, 100, 200));
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();
245 XID xids[] = { xid1, xid2, xid3 };
246 StackingClientListWaiter waiter(xids, arraysize(xids));
247 waiter.Wait();
248 ui::X11EventSource::GetInstance()->DispatchXEvents();
250 EXPECT_EQ(xid1, FindTopmostXWindowAt(150, 150));
251 EXPECT_EQ(window1, FindTopmostLocalProcessWindowAt(150, 150));
253 EXPECT_EQ(xid2, FindTopmostXWindowAt(250, 150));
254 EXPECT_EQ(NULL, FindTopmostLocalProcessWindowAt(250, 150));
256 EXPECT_EQ(xid3, FindTopmostXWindowAt(250, 250));
257 EXPECT_EQ(window3, FindTopmostLocalProcessWindowAt(250, 250));
259 EXPECT_EQ(xid3, FindTopmostXWindowAt(150, 250));
260 EXPECT_EQ(window3, FindTopmostLocalProcessWindowAt(150, 250));
262 EXPECT_EQ(xid3, FindTopmostXWindowAt(150, 195));
263 EXPECT_EQ(window3, FindTopmostLocalProcessWindowAt(150, 195));
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));
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));
279 XDestroyWindow(xdisplay(), xid2);
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));
290 XID xids[] = { xid1, xid2 };
291 StackingClientListWaiter stack_waiter(xids, arraysize(xids));
292 stack_waiter.Wait();
293 ui::X11EventSource::GetInstance()->DispatchXEvents();
295 EXPECT_EQ(xid1, FindTopmostXWindowAt(150, 150));
297 MinimizeWaiter minimize_waiter(xid1);
298 XIconifyWindow(xdisplay(), xid1, 0);
299 minimize_waiter.Wait();
301 EXPECT_NE(xid1, FindTopmostXWindowAt(150, 150));
302 EXPECT_NE(xid2, FindTopmostXWindowAt(150, 150));
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));
308 MinimizeWaiter minimize_waiter(xid2);
309 XIconifyWindow(xdisplay(), xid2, 0);
310 minimize_waiter.Wait();
312 EXPECT_NE(xid1, FindTopmostXWindowAt(350, 150));
313 EXPECT_NE(xid2, FindTopmostXWindowAt(350, 150));
315 XDestroyWindow(xdisplay(), xid2);
318 // Test that non-rectangular windows are properly handled.
319 TEST_F(X11TopmostWindowFinderTest, NonRectangular) {
320 if (!ui::IsShapeExtensionAvailable())
321 return;
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);
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);
341 XID xids[] = { xid1, xid2 };
342 StackingClientListWaiter stack_waiter(xids, arraysize(xids));
343 stack_waiter.Wait();
344 ui::X11EventSource::GetInstance()->DispatchXEvents();
346 EXPECT_EQ(xid1, FindTopmostXWindowAt(105, 120));
347 EXPECT_NE(xid1, FindTopmostXWindowAt(105, 105));
348 EXPECT_NE(xid2, FindTopmostXWindowAt(105, 105));
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));
356 XDestroyWindow(xdisplay(), xid2);
359 // Test that a window with an empty shape are properly handled.
360 TEST_F(X11TopmostWindowFinderTest, NonRectangularEmptyShape) {
361 if (!ui::IsShapeExtensionAvailable())
362 return;
364 scoped_ptr<Widget> widget1(
365 CreateAndShowWidget(gfx::Rect(100, 100, 100, 100)));
366 XID xid1 = widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget();
367 SkRegion* skregion1 = new SkRegion;
368 skregion1->op(SkIRect::MakeXYWH(0, 0, 0, 0), SkRegion::kUnion_Op);
369 // Widget takes ownership of |skregion1|.
370 widget1->SetShape(skregion1);
372 XID xids[] = { xid1 };
373 StackingClientListWaiter stack_waiter(xids, arraysize(xids));
374 stack_waiter.Wait();
375 ui::X11EventSource::GetInstance()->DispatchXEvents();
377 EXPECT_NE(xid1, FindTopmostXWindowAt(105, 105));
380 // Test that setting a Null shape removes the shape.
381 TEST_F(X11TopmostWindowFinderTest, NonRectangularNullShape) {
382 if (!ui::IsShapeExtensionAvailable())
383 return;
385 scoped_ptr<Widget> widget1(
386 CreateAndShowWidget(gfx::Rect(100, 100, 100, 100)));
387 XID xid1 = widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget();
388 SkRegion* skregion1 = new SkRegion;
389 skregion1->op(SkIRect::MakeXYWH(0, 0, 0, 0), SkRegion::kUnion_Op);
390 // Widget takes ownership of |skregion1|.
391 widget1->SetShape(skregion1);
393 // Remove the shape - this is now just a normal window.
394 widget1->SetShape(NULL);
396 XID xids[] = { xid1 };
397 StackingClientListWaiter stack_waiter(xids, arraysize(xids));
398 stack_waiter.Wait();
399 ui::X11EventSource::GetInstance()->DispatchXEvents();
401 EXPECT_EQ(xid1, FindTopmostXWindowAt(105, 105));
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));
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);
422 const char* kAtomsToCache[] = { "_NET_WM_WINDOW_TYPE_MENU", NULL };
423 ui::X11AtomCache atom_cache(gfx::GetXDisplay(), kAtomsToCache);
424 ui::SetAtomProperty(menu_xid,
425 "_NET_WM_WINDOW_TYPE",
426 "ATOM",
427 atom_cache.GetAtom("_NET_WM_WINDOW_TYPE_MENU"));
429 ui::SetUseOSWindowFrame(menu_xid, false);
430 ShowAndSetXWindowBounds(menu_xid, gfx::Rect(140, 110, 100, 100));
431 ui::X11EventSource::GetInstance()->DispatchXEvents();
433 // |menu_xid| is never added to _NET_CLIENT_LIST_STACKING.
434 XID xids[] = { xid };
435 StackingClientListWaiter stack_waiter(xids, arraysize(xids));
436 stack_waiter.Wait();
438 EXPECT_EQ(xid, FindTopmostXWindowAt(110, 110));
439 EXPECT_EQ(menu_xid, FindTopmostXWindowAt(150, 120));
440 EXPECT_EQ(menu_xid, FindTopmostXWindowAt(210, 120));
442 XDestroyWindow(xdisplay(), xid);
443 XDestroyWindow(xdisplay(), menu_xid);
446 } // namespace views