Android: Get rid of extra dup()s on launching child processes
[chromium-blink-merge.git] / ui / views / widget / desktop_aura / x11_topmost_window_finder_interactive_uitest.cc
blob0a234d5ba31c2d14354131570acb5c685b4507ee
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 ~MinimizeWaiter() override {}
51 private:
52 // X11PropertyChangeWaiter:
53 bool ShouldKeepOnWaiting(const ui::PlatformEvent& event) override {
54 std::vector<Atom> wm_states;
55 if (ui::GetAtomArrayProperty(xwindow(), "_NET_WM_STATE", &wm_states)) {
56 std::vector<Atom>::iterator it = std::find(
57 wm_states.begin(),
58 wm_states.end(),
59 atom_cache_->GetAtom("_NET_WM_STATE_HIDDEN"));
60 return it == wm_states.end();
62 return true;
65 scoped_ptr<ui::X11AtomCache> atom_cache_;
67 DISALLOW_COPY_AND_ASSIGN(MinimizeWaiter);
70 // Waits till |_NET_CLIENT_LIST_STACKING| is updated to include
71 // |expected_windows|.
72 class StackingClientListWaiter : public X11PropertyChangeWaiter {
73 public:
74 StackingClientListWaiter(XID* expected_windows, size_t count)
75 : X11PropertyChangeWaiter(ui::GetX11RootWindow(),
76 "_NET_CLIENT_LIST_STACKING"),
77 expected_windows_(expected_windows, expected_windows + count) {
80 ~StackingClientListWaiter() override {}
82 // X11PropertyChangeWaiter:
83 void Wait() override {
84 // StackingClientListWaiter may be created after
85 // _NET_CLIENT_LIST_STACKING already contains |expected_windows|.
86 if (!ShouldKeepOnWaiting(NULL))
87 return;
89 X11PropertyChangeWaiter::Wait();
92 private:
93 // X11PropertyChangeWaiter:
94 bool ShouldKeepOnWaiting(const ui::PlatformEvent& event) override {
95 std::vector<XID> stack;
96 ui::GetXWindowStack(ui::GetX11RootWindow(), &stack);
97 for (size_t i = 0; i < expected_windows_.size(); ++i) {
98 std::vector<XID>::iterator it = std::find(
99 stack.begin(), stack.end(), expected_windows_[i]);
100 if (it == stack.end())
101 return true;
103 return false;
106 std::vector<XID> expected_windows_;
108 DISALLOW_COPY_AND_ASSIGN(StackingClientListWaiter);
111 } // namespace
113 class X11TopmostWindowFinderTest : public ViewsTestBase {
114 public:
115 X11TopmostWindowFinderTest() {
118 ~X11TopmostWindowFinderTest() override {}
120 // Creates and shows a Widget with |bounds|. The caller takes ownership of
121 // the returned widget.
122 scoped_ptr<Widget> CreateAndShowWidget(const gfx::Rect& bounds) {
123 scoped_ptr<Widget> toplevel(new Widget);
124 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
125 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
126 params.native_widget = new DesktopNativeWidgetAura(toplevel.get());
127 params.bounds = bounds;
128 params.remove_standard_frame = true;
129 toplevel->Init(params);
130 toplevel->Show();
131 return toplevel.Pass();
134 // Creates and shows an X window with |bounds|.
135 XID CreateAndShowXWindow(const gfx::Rect& bounds) {
136 XID root = DefaultRootWindow(xdisplay());
137 XID xid = XCreateSimpleWindow(xdisplay(),
138 root,
139 0, 0, 1, 1,
140 0, // border_width
141 0, // border
142 0); // background
144 ui::SetUseOSWindowFrame(xid, false);
145 ShowAndSetXWindowBounds(xid, bounds);
146 return xid;
149 // Shows |xid| and sets its bounds.
150 void ShowAndSetXWindowBounds(XID xid, const gfx::Rect& bounds) {
151 XMapWindow(xdisplay(), xid);
153 XWindowChanges changes = {0};
154 changes.x = bounds.x();
155 changes.y = bounds.y();
156 changes.width = bounds.width();
157 changes.height = bounds.height();
158 XConfigureWindow(xdisplay(),
159 xid,
160 CWX | CWY | CWWidth | CWHeight,
161 &changes);
164 Display* xdisplay() {
165 return gfx::GetXDisplay();
168 // Returns the topmost X window at the passed in screen position.
169 XID FindTopmostXWindowAt(int screen_x, int screen_y) {
170 X11TopmostWindowFinder finder;
171 return finder.FindWindowAt(gfx::Point(screen_x, screen_y));
174 // Returns the topmost aura::Window at the passed in screen position. Returns
175 // NULL if the topmost window does not have an associated aura::Window.
176 aura::Window* FindTopmostLocalProcessWindowAt(int screen_x, int screen_y) {
177 X11TopmostWindowFinder finder;
178 return finder.FindLocalProcessWindowAt(gfx::Point(screen_x, screen_y),
179 std::set<aura::Window*>());
182 // Returns the topmost aura::Window at the passed in screen position ignoring
183 // |ignore_window|. Returns NULL if the topmost window does not have an
184 // associated aura::Window.
185 aura::Window* FindTopmostLocalProcessWindowWithIgnore(
186 int screen_x,
187 int screen_y,
188 aura::Window* ignore_window) {
189 std::set<aura::Window*> ignore;
190 ignore.insert(ignore_window);
191 X11TopmostWindowFinder finder;
192 return finder.FindLocalProcessWindowAt(gfx::Point(screen_x, screen_y),
193 ignore);
196 static void SetUpTestCase() {
197 gfx::GLSurface::InitializeOneOffForTests();
198 ui::RegisterPathProvider();
199 base::FilePath ui_test_pak_path;
200 ASSERT_TRUE(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path));
201 ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path);
204 // ViewsTestBase:
205 void SetUp() override {
206 ViewsTestBase::SetUp();
208 // Make X11 synchronous for our display connection. This does not force the
209 // window manager to behave synchronously.
210 XSynchronize(xdisplay(), True);
212 // Ensure that the X11DesktopHandler exists. The X11DesktopHandler is
213 // necessary to properly track menu windows.
214 X11DesktopHandler::get();
217 void TearDown() override {
218 XSynchronize(xdisplay(), False);
219 ViewsTestBase::TearDown();
222 private:
223 DISALLOW_COPY_AND_ASSIGN(X11TopmostWindowFinderTest);
226 TEST_F(X11TopmostWindowFinderTest, Basic) {
227 // Avoid positioning test windows at 0x0 because window managers often have a
228 // panel/launcher along one of the screen edges and do not allow windows to
229 // position themselves to overlap the panel/launcher.
230 scoped_ptr<Widget> widget1(
231 CreateAndShowWidget(gfx::Rect(100, 100, 200, 100)));
232 aura::Window* window1 = widget1->GetNativeWindow();
233 XID xid1 = window1->GetHost()->GetAcceleratedWidget();
235 XID xid2 = CreateAndShowXWindow(gfx::Rect(200, 100, 100, 200));
237 scoped_ptr<Widget> widget3(
238 CreateAndShowWidget(gfx::Rect(100, 190, 200, 110)));
239 aura::Window* window3 = widget3->GetNativeWindow();
240 XID xid3 = window3->GetHost()->GetAcceleratedWidget();
242 XID xids[] = { xid1, xid2, xid3 };
243 StackingClientListWaiter waiter(xids, arraysize(xids));
244 waiter.Wait();
245 ui::X11EventSource::GetInstance()->DispatchXEvents();
247 EXPECT_EQ(xid1, FindTopmostXWindowAt(150, 150));
248 EXPECT_EQ(window1, FindTopmostLocalProcessWindowAt(150, 150));
250 EXPECT_EQ(xid2, FindTopmostXWindowAt(250, 150));
251 EXPECT_EQ(NULL, FindTopmostLocalProcessWindowAt(250, 150));
253 EXPECT_EQ(xid3, FindTopmostXWindowAt(250, 250));
254 EXPECT_EQ(window3, FindTopmostLocalProcessWindowAt(250, 250));
256 EXPECT_EQ(xid3, FindTopmostXWindowAt(150, 250));
257 EXPECT_EQ(window3, FindTopmostLocalProcessWindowAt(150, 250));
259 EXPECT_EQ(xid3, FindTopmostXWindowAt(150, 195));
260 EXPECT_EQ(window3, FindTopmostLocalProcessWindowAt(150, 195));
262 EXPECT_NE(xid1, FindTopmostXWindowAt(1000, 1000));
263 EXPECT_NE(xid2, FindTopmostXWindowAt(1000, 1000));
264 EXPECT_NE(xid3, FindTopmostXWindowAt(1000, 1000));
265 EXPECT_EQ(NULL, FindTopmostLocalProcessWindowAt(1000, 1000));
267 EXPECT_EQ(window1,
268 FindTopmostLocalProcessWindowWithIgnore(150, 150, window3));
269 EXPECT_EQ(NULL,
270 FindTopmostLocalProcessWindowWithIgnore(250, 250, window3));
271 EXPECT_EQ(NULL,
272 FindTopmostLocalProcessWindowWithIgnore(150, 250, window3));
273 EXPECT_EQ(window1,
274 FindTopmostLocalProcessWindowWithIgnore(150, 195, window3));
276 XDestroyWindow(xdisplay(), xid2);
279 // Test that the minimized state is properly handled.
280 TEST_F(X11TopmostWindowFinderTest, Minimized) {
281 scoped_ptr<Widget> widget1(
282 CreateAndShowWidget(gfx::Rect(100, 100, 100, 100)));
283 aura::Window* window1 = widget1->GetNativeWindow();
284 XID xid1 = window1->GetHost()->GetAcceleratedWidget();
285 XID xid2 = CreateAndShowXWindow(gfx::Rect(300, 100, 100, 100));
287 XID xids[] = { xid1, xid2 };
288 StackingClientListWaiter stack_waiter(xids, arraysize(xids));
289 stack_waiter.Wait();
290 ui::X11EventSource::GetInstance()->DispatchXEvents();
292 EXPECT_EQ(xid1, FindTopmostXWindowAt(150, 150));
294 MinimizeWaiter minimize_waiter(xid1);
295 XIconifyWindow(xdisplay(), xid1, 0);
296 minimize_waiter.Wait();
298 EXPECT_NE(xid1, FindTopmostXWindowAt(150, 150));
299 EXPECT_NE(xid2, FindTopmostXWindowAt(150, 150));
301 // Repeat test for an X window which does not belong to a views::Widget
302 // because the code path is different.
303 EXPECT_EQ(xid2, FindTopmostXWindowAt(350, 150));
305 MinimizeWaiter minimize_waiter(xid2);
306 XIconifyWindow(xdisplay(), xid2, 0);
307 minimize_waiter.Wait();
309 EXPECT_NE(xid1, FindTopmostXWindowAt(350, 150));
310 EXPECT_NE(xid2, FindTopmostXWindowAt(350, 150));
312 XDestroyWindow(xdisplay(), xid2);
315 // Test that non-rectangular windows are properly handled.
316 TEST_F(X11TopmostWindowFinderTest, NonRectangular) {
317 if (!ui::IsShapeExtensionAvailable())
318 return;
320 scoped_ptr<Widget> widget1(
321 CreateAndShowWidget(gfx::Rect(100, 100, 100, 100)));
322 XID xid1 = widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget();
323 SkRegion* skregion1 = new SkRegion;
324 skregion1->op(SkIRect::MakeXYWH(0, 10, 10, 90), SkRegion::kUnion_Op);
325 skregion1->op(SkIRect::MakeXYWH(10, 0, 90, 100), SkRegion::kUnion_Op);
326 // Widget takes ownership of |skregion1|.
327 widget1->SetShape(skregion1);
329 SkRegion skregion2;
330 skregion2.op(SkIRect::MakeXYWH(0, 10, 10, 90), SkRegion::kUnion_Op);
331 skregion2.op(SkIRect::MakeXYWH(10, 0, 90, 100), SkRegion::kUnion_Op);
332 XID xid2 = CreateAndShowXWindow(gfx::Rect(300, 100, 100, 100));
333 REGION* region2 = gfx::CreateRegionFromSkRegion(skregion2);
334 XShapeCombineRegion(xdisplay(), xid2, ShapeBounding, 0, 0, region2,
335 false);
336 XDestroyRegion(region2);
338 XID xids[] = { xid1, xid2 };
339 StackingClientListWaiter stack_waiter(xids, arraysize(xids));
340 stack_waiter.Wait();
341 ui::X11EventSource::GetInstance()->DispatchXEvents();
343 EXPECT_EQ(xid1, FindTopmostXWindowAt(105, 120));
344 EXPECT_NE(xid1, FindTopmostXWindowAt(105, 105));
345 EXPECT_NE(xid2, FindTopmostXWindowAt(105, 105));
347 // Repeat test for an X window which does not belong to a views::Widget
348 // because the code path is different.
349 EXPECT_EQ(xid2, FindTopmostXWindowAt(305, 120));
350 EXPECT_NE(xid1, FindTopmostXWindowAt(305, 105));
351 EXPECT_NE(xid2, FindTopmostXWindowAt(305, 105));
353 XDestroyWindow(xdisplay(), xid2);
356 // Test that a window with an empty shape are properly handled.
357 TEST_F(X11TopmostWindowFinderTest, NonRectangularEmptyShape) {
358 if (!ui::IsShapeExtensionAvailable())
359 return;
361 scoped_ptr<Widget> widget1(
362 CreateAndShowWidget(gfx::Rect(100, 100, 100, 100)));
363 XID xid1 = widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget();
364 SkRegion* skregion1 = new SkRegion;
365 skregion1->op(SkIRect::MakeXYWH(0, 0, 0, 0), SkRegion::kUnion_Op);
366 // Widget takes ownership of |skregion1|.
367 widget1->SetShape(skregion1);
369 XID xids[] = { xid1 };
370 StackingClientListWaiter stack_waiter(xids, arraysize(xids));
371 stack_waiter.Wait();
372 ui::X11EventSource::GetInstance()->DispatchXEvents();
374 EXPECT_NE(xid1, FindTopmostXWindowAt(105, 105));
377 // Test that setting a Null shape removes the shape.
378 TEST_F(X11TopmostWindowFinderTest, NonRectangularNullShape) {
379 if (!ui::IsShapeExtensionAvailable())
380 return;
382 scoped_ptr<Widget> widget1(
383 CreateAndShowWidget(gfx::Rect(100, 100, 100, 100)));
384 XID xid1 = widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget();
385 SkRegion* skregion1 = new SkRegion;
386 skregion1->op(SkIRect::MakeXYWH(0, 0, 0, 0), SkRegion::kUnion_Op);
387 // Widget takes ownership of |skregion1|.
388 widget1->SetShape(skregion1);
390 // Remove the shape - this is now just a normal window.
391 widget1->SetShape(NULL);
393 XID xids[] = { xid1 };
394 StackingClientListWaiter stack_waiter(xids, arraysize(xids));
395 stack_waiter.Wait();
396 ui::X11EventSource::GetInstance()->DispatchXEvents();
398 EXPECT_EQ(xid1, FindTopmostXWindowAt(105, 105));
401 // Test that the TopmostWindowFinder finds windows which belong to menus
402 // (which may or may not belong to Chrome).
403 TEST_F(X11TopmostWindowFinderTest, Menu) {
404 XID xid = CreateAndShowXWindow(gfx::Rect(100, 100, 100, 100));
406 XID root = DefaultRootWindow(xdisplay());
407 XSetWindowAttributes swa;
408 swa.override_redirect = True;
409 XID menu_xid = XCreateWindow(xdisplay(),
410 root,
411 0, 0, 1, 1,
412 0, // border width
413 CopyFromParent, // depth
414 InputOutput,
415 CopyFromParent, // visual
416 CWOverrideRedirect,
417 &swa);
419 const char* kAtomsToCache[] = { "_NET_WM_WINDOW_TYPE_MENU", NULL };
420 ui::X11AtomCache atom_cache(gfx::GetXDisplay(), kAtomsToCache);
421 ui::SetAtomProperty(menu_xid,
422 "_NET_WM_WINDOW_TYPE",
423 "ATOM",
424 atom_cache.GetAtom("_NET_WM_WINDOW_TYPE_MENU"));
426 ui::SetUseOSWindowFrame(menu_xid, false);
427 ShowAndSetXWindowBounds(menu_xid, gfx::Rect(140, 110, 100, 100));
428 ui::X11EventSource::GetInstance()->DispatchXEvents();
430 // |menu_xid| is never added to _NET_CLIENT_LIST_STACKING.
431 XID xids[] = { xid };
432 StackingClientListWaiter stack_waiter(xids, arraysize(xids));
433 stack_waiter.Wait();
435 EXPECT_EQ(xid, FindTopmostXWindowAt(110, 110));
436 EXPECT_EQ(menu_xid, FindTopmostXWindowAt(150, 120));
437 EXPECT_EQ(menu_xid, FindTopmostXWindowAt(210, 120));
439 XDestroyWindow(xdisplay(), xid);
440 XDestroyWindow(xdisplay(), menu_xid);
443 } // namespace views