Android: Get rid of extra dup()s on launching child processes
[chromium-blink-merge.git] / ui / views / widget / desktop_aura / desktop_window_tree_host_x11_unittest.cc
blobaec6aa2968b23f500743fe3dbd11964aa150aca3
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 <vector>
7 #include <X11/extensions/shape.h>
8 #include <X11/Xlib.h>
10 // Get rid of X11 macros which conflict with gtest.
11 #undef Bool
12 #undef None
14 #include "base/memory/scoped_ptr.h"
15 #include "base/run_loop.h"
16 #include "ui/aura/window.h"
17 #include "ui/aura/window_tree_host.h"
18 #include "ui/base/hit_test.h"
19 #include "ui/base/x/x11_util.h"
20 #include "ui/events/platform/x11/x11_event_source.h"
21 #include "ui/gfx/geometry/point.h"
22 #include "ui/gfx/geometry/rect.h"
23 #include "ui/gfx/path.h"
24 #include "ui/gfx/x/x11_atom_cache.h"
25 #include "ui/views/test/views_test_base.h"
26 #include "ui/views/test/x11_property_change_waiter.h"
27 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
28 #include "ui/views/widget/widget_delegate.h"
29 #include "ui/views/window/non_client_view.h"
31 namespace views {
33 namespace {
35 // Blocks till the window state hint, |hint|, is set or unset.
36 class WMStateWaiter : public X11PropertyChangeWaiter {
37 public:
38 WMStateWaiter(XID window,
39 const char* hint,
40 bool wait_till_set)
41 : X11PropertyChangeWaiter(window, "_NET_WM_STATE"),
42 hint_(hint),
43 wait_till_set_(wait_till_set) {
45 const char* kAtomsToCache[] = {
46 hint,
47 NULL
49 atom_cache_.reset(new ui::X11AtomCache(gfx::GetXDisplay(), kAtomsToCache));
52 ~WMStateWaiter() override {}
54 private:
55 // X11PropertyChangeWaiter:
56 bool ShouldKeepOnWaiting(const ui::PlatformEvent& event) override {
57 std::vector<Atom> hints;
58 if (ui::GetAtomArrayProperty(xwindow(), "_NET_WM_STATE", &hints)) {
59 std::vector<Atom>::iterator it = std::find(
60 hints.begin(),
61 hints.end(),
62 atom_cache_->GetAtom(hint_));
63 bool hint_set = (it != hints.end());
64 return hint_set != wait_till_set_;
66 return true;
69 scoped_ptr<ui::X11AtomCache> atom_cache_;
71 // The name of the hint to wait to get set or unset.
72 const char* hint_;
74 // Whether we are waiting for |hint| to be set or unset.
75 bool wait_till_set_;
77 DISALLOW_COPY_AND_ASSIGN(WMStateWaiter);
80 // A NonClientFrameView with a window mask with the bottom right corner cut out.
81 class ShapedNonClientFrameView : public NonClientFrameView {
82 public:
83 explicit ShapedNonClientFrameView() {
86 ~ShapedNonClientFrameView() override {}
88 // NonClientFrameView:
89 gfx::Rect GetBoundsForClientView() const override { return bounds(); }
90 gfx::Rect GetWindowBoundsForClientBounds(
91 const gfx::Rect& client_bounds) const override {
92 return client_bounds;
94 int NonClientHitTest(const gfx::Point& point) override { return HTNOWHERE; }
95 void GetWindowMask(const gfx::Size& size, gfx::Path* window_mask) override {
96 int right = size.width();
97 int bottom = size.height();
99 window_mask->moveTo(0, 0);
100 window_mask->lineTo(0, bottom);
101 window_mask->lineTo(right, bottom);
102 window_mask->lineTo(right, 10);
103 window_mask->lineTo(right - 10, 10);
104 window_mask->lineTo(right - 10, 0);
105 window_mask->close();
107 void ResetWindowControls() override {}
108 void UpdateWindowIcon() override {}
109 void UpdateWindowTitle() override {}
110 void SizeConstraintsChanged() override {}
112 private:
113 DISALLOW_COPY_AND_ASSIGN(ShapedNonClientFrameView);
116 class ShapedWidgetDelegate : public WidgetDelegateView {
117 public:
118 ShapedWidgetDelegate() {
121 ~ShapedWidgetDelegate() override {}
123 // WidgetDelegateView:
124 NonClientFrameView* CreateNonClientFrameView(Widget* widget) override {
125 return new ShapedNonClientFrameView;
128 private:
129 DISALLOW_COPY_AND_ASSIGN(ShapedWidgetDelegate);
132 // Creates a widget of size 100x100.
133 scoped_ptr<Widget> CreateWidget(WidgetDelegate* delegate) {
134 scoped_ptr<Widget> widget(new Widget);
135 Widget::InitParams params(Widget::InitParams::TYPE_WINDOW);
136 params.delegate = delegate;
137 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
138 params.remove_standard_frame = true;
139 params.native_widget = new DesktopNativeWidgetAura(widget.get());
140 params.bounds = gfx::Rect(100, 100, 100, 100);
141 widget->Init(params);
142 return widget.Pass();
145 // Returns the list of rectangles which describe |xid|'s bounding region via the
146 // X shape extension.
147 std::vector<gfx::Rect> GetShapeRects(XID xid) {
148 int dummy;
149 int shape_rects_size;
150 XRectangle* shape_rects = XShapeGetRectangles(gfx::GetXDisplay(),
151 xid,
152 ShapeBounding,
153 &shape_rects_size,
154 &dummy);
156 std::vector<gfx::Rect> shape_vector;
157 for (int i = 0; i < shape_rects_size; ++i) {
158 shape_vector.push_back(gfx::Rect(
159 shape_rects[i].x,
160 shape_rects[i].y,
161 shape_rects[i].width,
162 shape_rects[i].height));
164 XFree(shape_rects);
165 return shape_vector;
168 // Returns true if one of |rects| contains point (x,y).
169 bool ShapeRectContainsPoint(const std::vector<gfx::Rect>& shape_rects,
170 int x,
171 int y) {
172 gfx::Point point(x, y);
173 for (size_t i = 0; i < shape_rects.size(); ++i) {
174 if (shape_rects[i].Contains(point))
175 return true;
177 return false;
180 // Flush the message loop.
181 void RunAllPendingInMessageLoop() {
182 base::RunLoop run_loop;
183 run_loop.RunUntilIdle();
186 } // namespace
188 class DesktopWindowTreeHostX11Test : public ViewsTestBase {
189 public:
190 DesktopWindowTreeHostX11Test() {
192 ~DesktopWindowTreeHostX11Test() override {}
194 void SetUp() override {
195 ViewsTestBase::SetUp();
197 // Make X11 synchronous for our display connection. This does not force the
198 // window manager to behave synchronously.
199 XSynchronize(gfx::GetXDisplay(), True);
202 void TearDown() override {
203 XSynchronize(gfx::GetXDisplay(), False);
204 ViewsTestBase::TearDown();
207 private:
208 DISALLOW_COPY_AND_ASSIGN(DesktopWindowTreeHostX11Test);
211 // Tests that the shape is properly set on the x window.
212 TEST_F(DesktopWindowTreeHostX11Test, Shape) {
213 if (!ui::IsShapeExtensionAvailable())
214 return;
216 // 1) Test setting the window shape via the NonClientFrameView. This technique
217 // is used to get rounded corners on Chrome windows when not using the native
218 // window frame.
219 scoped_ptr<Widget> widget1 = CreateWidget(new ShapedWidgetDelegate());
220 widget1->Show();
221 ui::X11EventSource::GetInstance()->DispatchXEvents();
223 XID xid1 = widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget();
224 std::vector<gfx::Rect> shape_rects = GetShapeRects(xid1);
225 ASSERT_FALSE(shape_rects.empty());
227 // The widget was supposed to be 100x100, but the WM might have ignored this
228 // suggestion.
229 int widget_width = widget1->GetWindowBoundsInScreen().width();
230 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, widget_width - 15, 5));
231 EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, widget_width - 5, 5));
232 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, widget_width - 5, 15));
233 EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, widget_width + 5, 15));
235 // Changing widget's size should update the shape.
236 widget1->SetBounds(gfx::Rect(100, 100, 200, 200));
237 ui::X11EventSource::GetInstance()->DispatchXEvents();
239 if (widget1->GetWindowBoundsInScreen().width() == 200) {
240 shape_rects = GetShapeRects(xid1);
241 ASSERT_FALSE(shape_rects.empty());
242 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 85, 5));
243 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 95, 5));
244 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 185, 5));
245 EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 195, 5));
246 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 195, 15));
247 EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 205, 15));
250 if (ui::WmSupportsHint(ui::GetAtom("_NET_WM_STATE_MAXIMIZED_VERT"))) {
251 // The shape should be changed to a rectangle which fills the entire screen
252 // when |widget1| is maximized.
254 WMStateWaiter waiter(xid1, "_NET_WM_STATE_MAXIMIZED_VERT", true);
255 widget1->Maximize();
256 waiter.Wait();
259 // Ensure that the task which is posted when a window is resized is run.
260 RunAllPendingInMessageLoop();
262 // xvfb does not support Xrandr so we cannot check the maximized window's
263 // bounds.
264 gfx::Rect maximized_bounds;
265 ui::GetOuterWindowBounds(xid1, &maximized_bounds);
267 shape_rects = GetShapeRects(xid1);
268 ASSERT_FALSE(shape_rects.empty());
269 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects,
270 maximized_bounds.width() - 1,
271 5));
272 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects,
273 maximized_bounds.width() - 1,
274 15));
277 // 2) Test setting the window shape via Widget::SetShape().
278 gfx::Path shape2;
279 shape2.moveTo(10, 0);
280 shape2.lineTo(10, 10);
281 shape2.lineTo(0, 10);
282 shape2.lineTo(0, 100);
283 shape2.lineTo(100, 100);
284 shape2.lineTo(100, 0);
285 shape2.close();
287 scoped_ptr<Widget> widget2(CreateWidget(NULL));
288 widget2->Show();
289 widget2->SetShape(shape2.CreateNativeRegion());
290 ui::X11EventSource::GetInstance()->DispatchXEvents();
292 XID xid2 = widget2->GetNativeWindow()->GetHost()->GetAcceleratedWidget();
293 shape_rects = GetShapeRects(xid2);
294 ASSERT_FALSE(shape_rects.empty());
295 EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 5, 5));
296 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 15, 5));
297 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 95, 15));
298 EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 105, 15));
300 // Changing the widget's size should not affect the shape.
301 widget2->SetBounds(gfx::Rect(100, 100, 200, 200));
302 shape_rects = GetShapeRects(xid2);
303 ASSERT_FALSE(shape_rects.empty());
304 EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 5, 5));
305 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 15, 5));
306 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 95, 15));
307 EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 105, 15));
309 // Setting the shape to NULL resets the shape back to the entire
310 // window bounds.
311 widget2->SetShape(NULL);
312 shape_rects = GetShapeRects(xid2);
313 ASSERT_FALSE(shape_rects.empty());
314 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 5, 5));
315 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 15, 5));
316 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 95, 15));
317 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 105, 15));
318 EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 500, 500));
321 // Test that the widget ignores changes in fullscreen state initiated by the
322 // window manager (e.g. via a window manager accelerator key).
323 TEST_F(DesktopWindowTreeHostX11Test, WindowManagerTogglesFullscreen) {
324 if (!ui::WmSupportsHint(ui::GetAtom("_NET_WM_STATE_FULLSCREEN")))
325 return;
327 scoped_ptr<Widget> widget = CreateWidget(new ShapedWidgetDelegate());
328 XID xid = widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget();
329 widget->Show();
330 ui::X11EventSource::GetInstance()->DispatchXEvents();
332 gfx::Rect initial_bounds = widget->GetWindowBoundsInScreen();
334 WMStateWaiter waiter(xid, "_NET_WM_STATE_FULLSCREEN", true);
335 widget->SetFullscreen(true);
336 waiter.Wait();
338 EXPECT_TRUE(widget->IsFullscreen());
340 // Emulate the window manager exiting fullscreen via a window manager
341 // accelerator key. It should not affect the widget's fullscreen state.
343 const char* kAtomsToCache[] = {
344 "_NET_WM_STATE",
345 "_NET_WM_STATE_FULLSCREEN",
346 NULL
348 Display* display = gfx::GetXDisplay();
349 ui::X11AtomCache atom_cache(display, kAtomsToCache);
351 XEvent xclient;
352 memset(&xclient, 0, sizeof(xclient));
353 xclient.type = ClientMessage;
354 xclient.xclient.window = xid;
355 xclient.xclient.message_type = atom_cache.GetAtom("_NET_WM_STATE");
356 xclient.xclient.format = 32;
357 xclient.xclient.data.l[0] = 0;
358 xclient.xclient.data.l[1] = atom_cache.GetAtom("_NET_WM_STATE_FULLSCREEN");
359 xclient.xclient.data.l[2] = 0;
360 xclient.xclient.data.l[3] = 1;
361 xclient.xclient.data.l[4] = 0;
362 XSendEvent(display, DefaultRootWindow(display), False,
363 SubstructureRedirectMask | SubstructureNotifyMask,
364 &xclient);
366 WMStateWaiter waiter(xid, "_NET_WM_STATE_FULLSCREEN", false);
367 waiter.Wait();
369 EXPECT_TRUE(widget->IsFullscreen());
371 // Calling Widget::SetFullscreen(false) should clear the widget's fullscreen
372 // state and clean things up.
373 widget->SetFullscreen(false);
374 EXPECT_FALSE(widget->IsFullscreen());
375 EXPECT_EQ(initial_bounds.ToString(),
376 widget->GetWindowBoundsInScreen().ToString());
379 // Tests that the minimization information is propagated to the content window.
380 TEST_F(DesktopWindowTreeHostX11Test, ToggleMinimizePropogateToContentWindow) {
381 Widget widget;
382 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
383 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
384 params.native_widget = new DesktopNativeWidgetAura(&widget);
385 widget.Init(params);
386 widget.Show();
387 ui::X11EventSource::GetInstance()->DispatchXEvents();
389 XID xid = widget.GetNativeWindow()->GetHost()->GetAcceleratedWidget();
390 Display* display = gfx::GetXDisplay();
392 // Minimize by sending _NET_WM_STATE_HIDDEN
394 const char* kAtomsToCache[] = {
395 "_NET_WM_STATE",
396 "_NET_WM_STATE_HIDDEN",
397 NULL
400 ui::X11AtomCache atom_cache(display, kAtomsToCache);
402 std::vector< ::Atom> atom_list;
403 atom_list.push_back(atom_cache.GetAtom("_NET_WM_STATE_HIDDEN"));
404 ui::SetAtomArrayProperty(xid, "_NET_WM_STATE", "ATOM", atom_list);
406 XEvent xevent;
407 memset(&xevent, 0, sizeof(xevent));
408 xevent.type = PropertyNotify;
409 xevent.xproperty.type = PropertyNotify;
410 xevent.xproperty.send_event = 1;
411 xevent.xproperty.display = display;
412 xevent.xproperty.window = xid;
413 xevent.xproperty.atom = atom_cache.GetAtom("_NET_WM_STATE");
414 xevent.xproperty.state = 0;
415 XSendEvent(display, DefaultRootWindow(display), False,
416 SubstructureRedirectMask | SubstructureNotifyMask,
417 &xevent);
419 WMStateWaiter waiter(xid, "_NET_WM_STATE_HIDDEN", true);
420 waiter.Wait();
422 EXPECT_FALSE(widget.GetNativeWindow()->IsVisible());
424 // Show from minimized by sending _NET_WM_STATE_FOCUSED
426 const char* kAtomsToCache[] = {
427 "_NET_WM_STATE",
428 "_NET_WM_STATE_FOCUSED",
429 NULL
432 ui::X11AtomCache atom_cache(display, kAtomsToCache);
434 std::vector< ::Atom> atom_list;
435 atom_list.push_back(atom_cache.GetAtom("_NET_WM_STATE_FOCUSED"));
436 ui::SetAtomArrayProperty(xid, "_NET_WM_STATE", "ATOM", atom_list);
438 XEvent xevent;
439 memset(&xevent, 0, sizeof(xevent));
440 xevent.type = PropertyNotify;
441 xevent.xproperty.type = PropertyNotify;
442 xevent.xproperty.send_event = 1;
443 xevent.xproperty.display = display;
444 xevent.xproperty.window = xid;
445 xevent.xproperty.atom = atom_cache.GetAtom("_NET_WM_STATE");
446 xevent.xproperty.state = 0;
447 XSendEvent(display, DefaultRootWindow(display), False,
448 SubstructureRedirectMask | SubstructureNotifyMask,
449 &xevent);
451 WMStateWaiter waiter(xid, "_NET_WM_STATE_FOCUSED", true);
452 waiter.Wait();
454 EXPECT_TRUE(widget.GetNativeWindow()->IsVisible());
457 } // namespace views