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

Unified Diff: ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc

Issue 268673017: Fix 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 side-by-side diff with in-line comments
Download patch
Index: ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..d2001697538e260e2a67cfb9b3ad35cc18460238
--- /dev/null
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc
@@ -0,0 +1,338 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <vector>
+
+#include <X11/extensions/shape.h>
+#include <X11/Xlib.h>
+
+// Get rid of X11 macros which conflict with gtest.
+#undef Bool
+#undef None
+
+#include "base/memory/scoped_ptr.h"
+#include "base/run_loop.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_tree_host.h"
+#include "ui/base/hit_test.h"
+#include "ui/base/x/x11_util.h"
+#include "ui/events/platform/platform_event_dispatcher.h"
+#include "ui/events/platform/platform_event_source.h"
+#include "ui/events/platform/scoped_event_dispatcher.h"
+#include "ui/events/platform/x11/x11_event_source.h"
+#include "ui/gfx/path.h"
+#include "ui/gfx/point.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/x/x11_atom_cache.h"
+#include "ui/views/test/views_test_base.h"
+#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
+#include "ui/views/widget/widget_delegate.h"
+#include "ui/views/window/non_client_view.h"
+
+namespace views {
+
+namespace {
+
+// Blocks till |window| becomes maximized.
+class MaximizeWaiter : public ui::PlatformEventDispatcher {
+ public:
+ explicit MaximizeWaiter(XID window)
+ : xwindow_(window),
+ wait_(true) {
+ const char* kAtomsToCache[] = {
+ "_NET_WM_STATE",
+ "_NET_WM_STATE_MAXIMIZED_VERT",
+ NULL
+ };
+ atom_cache_.reset(new ui::X11AtomCache(gfx::GetXDisplay(), kAtomsToCache));
+
+ // Override the dispatcher so that we get events before
+ // DesktopWindowTreeHostX11 does. We must do this because
+ // DesktopWindowTreeHostX11 stops propagation.
+ dispatcher_ = ui::PlatformEventSource::GetInstance()->
+ OverrideDispatcher(this).Pass();
+ }
+
+ virtual ~MaximizeWaiter() {
+ }
+
+ void Wait() {
+ if (wait_) {
+ base::RunLoop run_loop;
+ quit_closure_ = run_loop.QuitClosure();
+ run_loop.Run();
+ }
+ dispatcher_.reset();
+ }
+
+ virtual bool CanDispatchEvent(const ui::PlatformEvent& event) OVERRIDE {
+ NOTREACHED();
+ return true;
+ }
+
+ virtual uint32_t DispatchEvent(const ui::PlatformEvent& event) OVERRIDE {
+ if (event->type != PropertyNotify ||
+ event->xproperty.window != xwindow_ ||
+ event->xproperty.atom != atom_cache_->GetAtom("_NET_WM_STATE")) {
+ return ui::POST_DISPATCH_PERFORM_DEFAULT;
+ }
+
+ std::vector<Atom> wm_states;
+ if (!ui::GetAtomArrayProperty(xwindow_, "_NET_WM_STATE", &wm_states))
+ return ui::POST_DISPATCH_PERFORM_DEFAULT;
+
+ std::vector<Atom>::iterator it = std::find(
+ wm_states.begin(),
+ wm_states.end(),
+ atom_cache_->GetAtom("_NET_WM_STATE_MAXIMIZED_VERT"));
+ if (it == wm_states.end())
+ return ui::POST_DISPATCH_PERFORM_DEFAULT;
+
+ wait_ = false;
+ if (!quit_closure_.is_null())
+ quit_closure_.Run();
+ return ui::POST_DISPATCH_PERFORM_DEFAULT;
+ }
+
+ // The window we are waiting to get maximized.
+ XID xwindow_;
+
+ // Whether Wait() should block.
+ bool wait_;
+
+ // Ends the run loop.
+ base::Closure quit_closure_;
+
+ scoped_ptr<ui::ScopedEventDispatcher> dispatcher_;
+
+ scoped_ptr<ui::X11AtomCache> atom_cache_;
+
+ DISALLOW_COPY_AND_ASSIGN(MaximizeWaiter);
+};
+
+// A NonClientFrameView with a window mask with the bottom right corner cut out.
+class ShapedNonClientFrameView : public NonClientFrameView {
+ public:
+ explicit ShapedNonClientFrameView() {
+ }
+
+ virtual ~ShapedNonClientFrameView() {
+ }
+
+ // NonClientFrameView:
+ virtual gfx::Rect GetBoundsForClientView() const OVERRIDE {
+ return bounds();
+ }
+ virtual gfx::Rect GetWindowBoundsForClientBounds(
+ const gfx::Rect& client_bounds) const OVERRIDE {
+ return client_bounds;
+ }
+ virtual int NonClientHitTest(const gfx::Point& point) OVERRIDE {
+ return HTNOWHERE;
+ }
+ virtual void GetWindowMask(const gfx::Size& size,
+ gfx::Path* window_mask) OVERRIDE {
+ int right = size.width();
+ int bottom = size.height();
+
+ window_mask->moveTo(0, 0);
+ window_mask->lineTo(0, bottom);
+ window_mask->lineTo(right, bottom);
+ window_mask->lineTo(right, 10);
+ window_mask->lineTo(right - 10, 10);
+ window_mask->lineTo(right - 10, 0);
+ window_mask->close();
+ }
+ virtual void ResetWindowControls() OVERRIDE {
+ }
+ virtual void UpdateWindowIcon() OVERRIDE {
+ }
+ virtual void UpdateWindowTitle() OVERRIDE {
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ShapedNonClientFrameView);
+};
+
+class ShapedWidgetDelegate : public WidgetDelegateView {
+ public:
+ ShapedWidgetDelegate() {
+ }
+
+ virtual ~ShapedWidgetDelegate() {
+ }
+
+ // WidgetDelegateView:
+ virtual NonClientFrameView* CreateNonClientFrameView(
+ Widget* widget) OVERRIDE {
+ return new ShapedNonClientFrameView;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ShapedWidgetDelegate);
+};
+
+// Creates a widget of size 100x100.
+scoped_ptr<Widget> CreateWidget(WidgetDelegate* delegate) {
+ scoped_ptr<Widget> widget(new Widget);
+ Widget::InitParams params(Widget::InitParams::TYPE_WINDOW);
+ params.delegate = delegate;
+ params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ params.remove_standard_frame = true;
+ params.native_widget = new DesktopNativeWidgetAura(widget.get());
+ params.bounds = gfx::Rect(100, 100, 100, 100);
+ widget->Init(params);
+ return widget.Pass();
+}
+
+// Returns the list of rectangles which describe |xid|'s bounding region via the
+// X shape extension.
+std::vector<gfx::Rect> GetShapeRects(XID xid) {
+ int dummy;
+ int shape_rects_size;
+ XRectangle* shape_rects = XShapeGetRectangles(gfx::GetXDisplay(),
+ xid,
+ ShapeBounding,
+ &shape_rects_size,
+ &dummy);
+
+ std::vector<gfx::Rect> shape_vector;
+ for (int i = 0; i < shape_rects_size; ++i) {
+ shape_vector.push_back(gfx::Rect(
+ shape_rects[i].x,
+ shape_rects[i].y,
+ shape_rects[i].width,
+ shape_rects[i].height));
+ }
+ XFree(shape_rects);
+ return shape_vector;
+}
+
+// Returns true if one of |rects| contains point (x,y).
+bool ShapeRectContainsPoint(const std::vector<gfx::Rect>& shape_rects,
+ int x,
+ int y) {
+ gfx::Point point(x, y);
+ for (size_t i = 0; i < shape_rects.size(); ++i) {
+ if (shape_rects[i].Contains(point))
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
+class DesktopWindowTreeHostX11Test : public ViewsTestBase {
+ public:
+ DesktopWindowTreeHostX11Test() {
+ }
+ virtual ~DesktopWindowTreeHostX11Test() {
+ }
+
+ virtual void SetUp() OVERRIDE {
+ ViewsTestBase::SetUp();
+
+ // Make X11 synchronous for our display connection. This does not force the
+ // window manager to behave synchronously.
+ XSynchronize(gfx::GetXDisplay(), True);
+ }
+
+ virtual void TearDown() OVERRIDE {
+ XSynchronize(gfx::GetXDisplay(), False);
+ ViewsTestBase::TearDown();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DesktopWindowTreeHostX11Test);
+};
+
+// Tests that the shape is properly set on the x window.
+TEST_F(DesktopWindowTreeHostX11Test, Shape) {
+ if (!ui::IsShapeExtensionAvailable())
+ return;
+
+ // 1) Test setting the window shape via the NonClientFrameView. This technique
+ // is used to get rounded corners on Chrome windows when not using the native
+ // window frame.
+ scoped_ptr<Widget> widget1 = CreateWidget(new ShapedWidgetDelegate());
+ widget1->Show();
+ ui::X11EventSource::GetInstance()->DispatchXEvents();
+
+ XID xid1 = widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget();
+ std::vector<gfx::Rect> shape_rects = GetShapeRects(xid1);
+ ASSERT_FALSE(shape_rects.empty());
+ EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 85, 5));
+ EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 95, 5));
+ EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 95, 15));
+ EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 105, 15));
+
+ // Changing widget's size should update the shape.
+ widget1->SetBounds(gfx::Rect(100, 100, 200, 200));
+ ui::X11EventSource::GetInstance()->DispatchXEvents();
+
+ shape_rects = GetShapeRects(xid1);
+ ASSERT_FALSE(shape_rects.empty());
+ EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 85, 5));
+ EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 95, 5));
+ EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 185, 5));
+ EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 195, 5));
+ EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 195, 15));
+ EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 205, 15));
+
+ // The shape should be changed to a rectangle which fills the entire screen
+ // when |widget1| is maximized.
+ {
+ MaximizeWaiter waiter(xid1);
+ widget1->Maximize();
+ waiter.Wait();
+ }
+
+ // xvfb does not support Xrandr so we cannot check the maximized window's
+ // bounds.
+ gfx::Rect maximized_bounds;
+ ui::GetWindowRect(xid1, &maximized_bounds);
+
+ shape_rects = GetShapeRects(xid1);
+ ASSERT_FALSE(shape_rects.empty());
+ EXPECT_TRUE(ShapeRectContainsPoint(shape_rects,
+ maximized_bounds.width() - 1,
+ 5));
+ EXPECT_TRUE(ShapeRectContainsPoint(shape_rects,
+ maximized_bounds.width() - 1,
+ 15));
+
+ // 2) Test setting the window shape via Widget::SetShape().
+ gfx::Path shape2;
+ shape2.moveTo(10, 0);
+ shape2.lineTo(10, 10);
+ shape2.lineTo(0, 10);
+ shape2.lineTo(0, 100);
+ shape2.lineTo(100, 100);
+ shape2.lineTo(100, 0);
+ shape2.close();
+
+ scoped_ptr<Widget> widget2(CreateWidget(NULL));
+ widget2->Show();
+ widget2->SetShape(shape2.CreateNativeRegion());
+ ui::X11EventSource::GetInstance()->DispatchXEvents();
+
+ XID xid2 = widget2->GetNativeWindow()->GetHost()->GetAcceleratedWidget();
+ shape_rects = GetShapeRects(xid2);
+ ASSERT_FALSE(shape_rects.empty());
+ EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 5, 5));
+ EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 15, 5));
+ EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 95, 15));
+ EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 105, 15));
+
+ // Changing the widget's size should not affect the shape.
+ widget2->SetBounds(gfx::Rect(100, 100, 200, 200));
+ shape_rects = GetShapeRects(xid2);
+ ASSERT_FALSE(shape_rects.empty());
+ EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 5, 5));
+ EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 15, 5));
+ EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 95, 15));
+ EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 105, 15));
+}
+
+} // namespace views

Powered by Google App Engine
This is Rietveld 408576698