blob: e25fab221ed3624b6717931308e64b9e25d1f252 [file] [log] [blame]
// 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 <stddef.h>
#include <memory>
#include <vector>
#include "base/command_line.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/stl_util.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/display/display_switches.h"
#include "ui/events/devices/x11/touch_factory_x11.h"
#include "ui/events/platform/x11/x11_event_source_glib.h"
#include "ui/events/test/events_test_utils_x11.h"
#include "ui/events/test/platform_event_source_test_api.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/path.h"
#include "ui/gfx/x/x11.h"
#include "ui/gfx/x/x11_atom_cache.h"
#include "ui/views/test/views_test_base.h"
#include "ui/views/test/x11_property_change_waiter.h"
#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
#include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h"
#include "ui/views/widget/widget_delegate.h"
#include "ui/views/window/non_client_view.h"
namespace views {
namespace {
const int kPointerDeviceId = 1;
// Blocks till the window state hint, |hint|, is set or unset.
class WMStateWaiter : public X11PropertyChangeWaiter {
public:
WMStateWaiter(XID window, const char* hint, bool wait_till_set)
: X11PropertyChangeWaiter(window, "_NET_WM_STATE"),
hint_(hint),
wait_till_set_(wait_till_set) {}
~WMStateWaiter() override {}
private:
// X11PropertyChangeWaiter:
bool ShouldKeepOnWaiting(const ui::PlatformEvent& event) override {
std::vector<Atom> hints;
if (ui::GetAtomArrayProperty(xwindow(), "_NET_WM_STATE", &hints))
return base::ContainsValue(hints, gfx::GetAtom(hint_)) != wait_till_set_;
return true;
}
// The name of the hint to wait to get set or unset.
const char* hint_;
// Whether we are waiting for |hint| to be set or unset.
bool wait_till_set_;
DISALLOW_COPY_AND_ASSIGN(WMStateWaiter);
};
// A NonClientFrameView with a window mask with the bottom right corner cut out.
class ShapedNonClientFrameView : public NonClientFrameView {
public:
ShapedNonClientFrameView() {
}
~ShapedNonClientFrameView() override {}
// NonClientFrameView:
gfx::Rect GetBoundsForClientView() const override { return bounds(); }
gfx::Rect GetWindowBoundsForClientBounds(
const gfx::Rect& client_bounds) const override {
return client_bounds;
}
int NonClientHitTest(const gfx::Point& point) override {
// Fake bottom for non client event test.
if (point == gfx::Point(500, 500))
return HTBOTTOM;
return HTNOWHERE;
}
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();
}
void ResetWindowControls() override {}
void UpdateWindowIcon() override {}
void UpdateWindowTitle() override {}
void SizeConstraintsChanged() override {}
private:
DISALLOW_COPY_AND_ASSIGN(ShapedNonClientFrameView);
};
class ShapedWidgetDelegate : public WidgetDelegateView {
public:
ShapedWidgetDelegate() {
}
~ShapedWidgetDelegate() override {}
// WidgetDelegateView:
NonClientFrameView* CreateNonClientFrameView(Widget* widget) override {
return new ShapedNonClientFrameView;
}
private:
DISALLOW_COPY_AND_ASSIGN(ShapedWidgetDelegate);
};
// Creates a widget of size 100x100.
std::unique_ptr<Widget> CreateWidget(WidgetDelegate* delegate) {
std::unique_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;
}
// 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;
gfx::XScopedPtr<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) {
const XRectangle& rect = shape_rects[i];
shape_vector.push_back(gfx::Rect(rect.x, rect.y, rect.width, rect.height));
}
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;
}
// Flush the message loop.
void RunAllPendingInMessageLoop() {
base::RunLoop run_loop;
run_loop.RunUntilIdle();
}
} // namespace
class DesktopWindowTreeHostX11Test : public ViewsTestBase {
public:
DesktopWindowTreeHostX11Test()
: event_source_(ui::PlatformEventSource::GetInstance()) {}
~DesktopWindowTreeHostX11Test() override {}
void SetUp() override {
std::vector<int> pointer_devices;
pointer_devices.push_back(kPointerDeviceId);
ui::TouchFactory::GetInstance()->SetPointerDeviceForTest(pointer_devices);
ViewsTestBase::SetUp();
// Make X11 synchronous for our display connection. This does not force the
// window manager to behave synchronously.
XSynchronize(gfx::GetXDisplay(), x11::True);
}
void TearDown() override {
XSynchronize(gfx::GetXDisplay(), x11::False);
ViewsTestBase::TearDown();
}
void DispatchSingleEventToWidget(XEvent* event, Widget* widget) {
DCHECK_EQ(GenericEvent, event->type);
XIDeviceEvent* device_event =
static_cast<XIDeviceEvent*>(event->xcookie.data);
device_event->event =
widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget();
event_source_.Dispatch(event);
}
private:
ui::test::PlatformEventSourceTestAPI event_source_;
DISALLOW_COPY_AND_ASSIGN(DesktopWindowTreeHostX11Test);
};
// https://crbug.com/898742: Test is flaky.
// Tests that the shape is properly set on the x window.
TEST_F(DesktopWindowTreeHostX11Test, DISABLED_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.
std::unique_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());
// The widget was supposed to be 100x100, but the WM might have ignored this
// suggestion.
int widget_width = widget1->GetWindowBoundsInScreen().width();
EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, widget_width - 15, 5));
EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, widget_width - 5, 5));
EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, widget_width - 5, 15));
EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, widget_width + 5, 15));
// Changing widget's size should update the shape.
widget1->SetBounds(gfx::Rect(100, 100, 200, 200));
ui::X11EventSource::GetInstance()->DispatchXEvents();
if (widget1->GetWindowBoundsInScreen().width() == 200) {
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));
}
if (ui::WmSupportsHint(gfx::GetAtom("_NET_WM_STATE_MAXIMIZED_VERT"))) {
// The shape should be changed to a rectangle which fills the entire screen
// when |widget1| is maximized.
{
WMStateWaiter waiter(xid1, "_NET_WM_STATE_MAXIMIZED_VERT", true);
widget1->Maximize();
waiter.Wait();
}
// Ensure that the task which is posted when a window is resized is run.
RunAllPendingInMessageLoop();
// xvfb does not support Xrandr so we cannot check the maximized window's
// bounds.
gfx::Rect maximized_bounds;
ui::GetOuterWindowBounds(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().
auto shape_region = std::make_unique<Widget::ShapeRects>();
shape_region->emplace_back(10, 0, 90, 10);
shape_region->emplace_back(0, 10, 10, 90);
shape_region->emplace_back(10, 10, 90, 90);
std::unique_ptr<Widget> widget2(CreateWidget(nullptr));
widget2->Show();
widget2->SetShape(std::move(shape_region));
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));
// Setting the shape to NULL resets the shape back to the entire
// window bounds.
widget2->SetShape(NULL);
shape_rects = GetShapeRects(xid2);
ASSERT_FALSE(shape_rects.empty());
EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 5, 5));
EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 15, 5));
EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 95, 15));
EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 105, 15));
EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 500, 500));
}
// Test that the widget ignores changes in fullscreen state initiated by the
// window manager (e.g. via a window manager accelerator key).
TEST_F(DesktopWindowTreeHostX11Test, WindowManagerTogglesFullscreen) {
if (!ui::WmSupportsHint(gfx::GetAtom("_NET_WM_STATE_FULLSCREEN")))
return;
std::unique_ptr<Widget> widget = CreateWidget(new ShapedWidgetDelegate());
XID xid = widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget();
widget->Show();
ui::X11EventSource::GetInstance()->DispatchXEvents();
gfx::Rect initial_bounds = widget->GetWindowBoundsInScreen();
{
WMStateWaiter waiter(xid, "_NET_WM_STATE_FULLSCREEN", true);
widget->SetFullscreen(true);
waiter.Wait();
}
EXPECT_TRUE(widget->IsFullscreen());
// Emulate the window manager exiting fullscreen via a window manager
// accelerator key. It should not affect the widget's fullscreen state.
{
Display* display = gfx::GetXDisplay();
XEvent xclient;
memset(&xclient, 0, sizeof(xclient));
xclient.type = ClientMessage;
xclient.xclient.window = xid;
xclient.xclient.message_type = gfx::GetAtom("_NET_WM_STATE");
xclient.xclient.format = 32;
xclient.xclient.data.l[0] = 0;
xclient.xclient.data.l[1] = gfx::GetAtom("_NET_WM_STATE_FULLSCREEN");
xclient.xclient.data.l[2] = 0;
xclient.xclient.data.l[3] = 1;
xclient.xclient.data.l[4] = 0;
XSendEvent(display, DefaultRootWindow(display), x11::False,
SubstructureRedirectMask | SubstructureNotifyMask, &xclient);
WMStateWaiter waiter(xid, "_NET_WM_STATE_FULLSCREEN", false);
waiter.Wait();
}
EXPECT_TRUE(widget->IsFullscreen());
// Calling Widget::SetFullscreen(false) should clear the widget's fullscreen
// state and clean things up.
widget->SetFullscreen(false);
EXPECT_FALSE(widget->IsFullscreen());
EXPECT_EQ(initial_bounds.ToString(),
widget->GetWindowBoundsInScreen().ToString());
}
// Tests that the minimization information is propagated to the content window.
TEST_F(DesktopWindowTreeHostX11Test, ToggleMinimizePropogateToContentWindow) {
Widget widget;
Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params.native_widget = new DesktopNativeWidgetAura(&widget);
widget.Init(params);
widget.Show();
ui::X11EventSource::GetInstance()->DispatchXEvents();
XID xid = widget.GetNativeWindow()->GetHost()->GetAcceleratedWidget();
Display* display = gfx::GetXDisplay();
// Minimize by sending _NET_WM_STATE_HIDDEN
{
std::vector< ::Atom> atom_list;
atom_list.push_back(gfx::GetAtom("_NET_WM_STATE_HIDDEN"));
ui::SetAtomArrayProperty(xid, "_NET_WM_STATE", "ATOM", atom_list);
XEvent xevent;
memset(&xevent, 0, sizeof(xevent));
xevent.type = PropertyNotify;
xevent.xproperty.type = PropertyNotify;
xevent.xproperty.send_event = 1;
xevent.xproperty.display = display;
xevent.xproperty.window = xid;
xevent.xproperty.atom = gfx::GetAtom("_NET_WM_STATE");
xevent.xproperty.state = 0;
XSendEvent(display, DefaultRootWindow(display), x11::False,
SubstructureRedirectMask | SubstructureNotifyMask, &xevent);
WMStateWaiter waiter(xid, "_NET_WM_STATE_HIDDEN", true);
waiter.Wait();
}
EXPECT_FALSE(widget.GetNativeWindow()->IsVisible());
// Show from minimized by sending _NET_WM_STATE_FOCUSED
{
std::vector< ::Atom> atom_list;
atom_list.push_back(gfx::GetAtom("_NET_WM_STATE_FOCUSED"));
ui::SetAtomArrayProperty(xid, "_NET_WM_STATE", "ATOM", atom_list);
XEvent xevent;
memset(&xevent, 0, sizeof(xevent));
xevent.type = PropertyNotify;
xevent.xproperty.type = PropertyNotify;
xevent.xproperty.send_event = 1;
xevent.xproperty.display = display;
xevent.xproperty.window = xid;
xevent.xproperty.atom = gfx::GetAtom("_NET_WM_STATE");
xevent.xproperty.state = 0;
XSendEvent(display, DefaultRootWindow(display), x11::False,
SubstructureRedirectMask | SubstructureNotifyMask, &xevent);
WMStateWaiter waiter(xid, "_NET_WM_STATE_FOCUSED", true);
waiter.Wait();
}
EXPECT_TRUE(widget.GetNativeWindow()->IsVisible());
}
TEST_F(DesktopWindowTreeHostX11Test, ChildWindowDestructionDuringTearDown) {
Widget parent_widget;
Widget::InitParams parent_params =
CreateParams(Widget::InitParams::TYPE_WINDOW);
parent_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
parent_params.native_widget = new DesktopNativeWidgetAura(&parent_widget);
parent_widget.Init(parent_params);
parent_widget.Show();
ui::X11EventSource::GetInstance()->DispatchXEvents();
Widget child_widget;
Widget::InitParams child_params =
CreateParams(Widget::InitParams::TYPE_WINDOW);
child_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
child_params.native_widget = new DesktopNativeWidgetAura(&child_widget);
child_params.parent = parent_widget.GetNativeWindow();
child_widget.Init(child_params);
child_widget.Show();
ui::X11EventSource::GetInstance()->DispatchXEvents();
// Sanity check that the two widgets each have their own XID.
ASSERT_NE(parent_widget.GetNativeWindow()->GetHost()->GetAcceleratedWidget(),
child_widget.GetNativeWindow()->GetHost()->GetAcceleratedWidget());
Widget::CloseAllSecondaryWidgets();
EXPECT_TRUE(DesktopWindowTreeHostX11::GetAllOpenWindows().empty());
}
// A Widget that allows setting the min/max size for the widget.
class CustomSizeWidget : public Widget {
public:
CustomSizeWidget() {}
~CustomSizeWidget() override {}
void set_min_size(const gfx::Size& size) { min_size_ = size; }
void set_max_size(const gfx::Size& size) { max_size_ = size; }
// Widget:
gfx::Size GetMinimumSize() const override { return min_size_; }
gfx::Size GetMaximumSize() const override { return max_size_; }
private:
gfx::Size min_size_;
gfx::Size max_size_;
DISALLOW_COPY_AND_ASSIGN(CustomSizeWidget);
};
TEST_F(DesktopWindowTreeHostX11Test, SetBoundsWithMinMax) {
CustomSizeWidget widget;
Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params.native_widget = new DesktopNativeWidgetAura(&widget);
params.bounds = gfx::Rect(200, 100);
widget.Init(params);
widget.Show();
ui::X11EventSource::GetInstance()->DispatchXEvents();
EXPECT_EQ(gfx::Size(200, 100).ToString(),
widget.GetWindowBoundsInScreen().size().ToString());
widget.SetBounds(gfx::Rect(300, 200));
EXPECT_EQ(gfx::Size(300, 200).ToString(),
widget.GetWindowBoundsInScreen().size().ToString());
widget.set_min_size(gfx::Size(100, 100));
widget.SetBounds(gfx::Rect(50, 500));
EXPECT_EQ(gfx::Size(100, 500).ToString(),
widget.GetWindowBoundsInScreen().size().ToString());
}
class MouseEventRecorder : public ui::EventHandler {
public:
MouseEventRecorder() {}
~MouseEventRecorder() override {}
void Reset() { mouse_events_.clear(); }
const std::vector<ui::MouseEvent>& mouse_events() const {
return mouse_events_;
}
private:
// ui::EventHandler:
void OnMouseEvent(ui::MouseEvent* mouse) override {
mouse_events_.push_back(*mouse);
}
std::vector<ui::MouseEvent> mouse_events_;
DISALLOW_COPY_AND_ASSIGN(MouseEventRecorder);
};
class DesktopWindowTreeHostX11HighDPITest
: public DesktopWindowTreeHostX11Test {
public:
DesktopWindowTreeHostX11HighDPITest() {}
~DesktopWindowTreeHostX11HighDPITest() override {}
void PretendCapture(views::Widget* capture_widget) {
DesktopWindowTreeHostX11* capture_host = nullptr;
if (capture_widget) {
capture_host = static_cast<DesktopWindowTreeHostX11*>(
capture_widget->GetNativeWindow()->GetHost());
}
DesktopWindowTreeHostX11::g_current_capture = capture_host;
if (capture_widget)
capture_widget->GetNativeWindow()->SetCapture();
}
private:
void SetUp() override {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
command_line->AppendSwitchASCII(switches::kForceDeviceScaleFactor, "2");
DesktopWindowTreeHostX11Test::SetUp();
}
DISALLOW_COPY_AND_ASSIGN(DesktopWindowTreeHostX11HighDPITest);
};
// https://crbug.com/702687
TEST_F(DesktopWindowTreeHostX11HighDPITest,
DISABLED_LocatedEventDispatchWithCapture) {
Widget first;
Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params.native_widget = new DesktopNativeWidgetAura(&first);
params.bounds = gfx::Rect(0, 0, 50, 50);
first.Init(params);
first.Show();
Widget second;
params = CreateParams(Widget::InitParams::TYPE_WINDOW);
params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params.native_widget = new DesktopNativeWidgetAura(&second);
params.bounds = gfx::Rect(50, 50, 50, 50);
second.Init(params);
second.Show();
ui::X11EventSource::GetInstance()->DispatchXEvents();
MouseEventRecorder first_recorder, second_recorder;
first.GetNativeWindow()->AddPreTargetHandler(&first_recorder);
second.GetNativeWindow()->AddPreTargetHandler(&second_recorder);
// Dispatch an event on |first|. Verify it gets the event.
ui::ScopedXI2Event event;
event.InitGenericButtonEvent(kPointerDeviceId, ui::ET_MOUSEWHEEL,
gfx::Point(50, 50), ui::EF_NONE);
DispatchSingleEventToWidget(event, &first);
ASSERT_EQ(1u, first_recorder.mouse_events().size());
EXPECT_EQ(ui::ET_MOUSEWHEEL, first_recorder.mouse_events()[0].type());
EXPECT_EQ(gfx::Point(25, 25).ToString(),
first_recorder.mouse_events()[0].location().ToString());
ASSERT_EQ(0u, second_recorder.mouse_events().size());
first_recorder.Reset();
second_recorder.Reset();
// Set a capture on |second|, and dispatch the same event to |first|. This
// event should reach |second| instead.
PretendCapture(&second);
event.InitGenericButtonEvent(kPointerDeviceId, ui::ET_MOUSEWHEEL,
gfx::Point(50, 50), ui::EF_NONE);
DispatchSingleEventToWidget(event, &first);
ASSERT_EQ(0u, first_recorder.mouse_events().size());
ASSERT_EQ(1u, second_recorder.mouse_events().size());
EXPECT_EQ(ui::ET_MOUSEWHEEL, second_recorder.mouse_events()[0].type());
EXPECT_EQ(gfx::Point(-25, -25).ToString(),
second_recorder.mouse_events()[0].location().ToString());
PretendCapture(nullptr);
first.GetNativeWindow()->RemovePreTargetHandler(&first_recorder);
second.GetNativeWindow()->RemovePreTargetHandler(&second_recorder);
}
TEST_F(DesktopWindowTreeHostX11Test, MouseNCEvents) {
std::unique_ptr<Widget> widget = CreateWidget(new ShapedWidgetDelegate());
widget->Show();
ui::X11EventSource::GetInstance()->DispatchXEvents();
widget->SetBounds(gfx::Rect(100, 100, 501, 501));
ui::X11EventSource::GetInstance()->DispatchXEvents();
MouseEventRecorder recorder;
widget->GetNativeWindow()->AddPreTargetHandler(&recorder);
ui::ScopedXI2Event event;
event.InitGenericButtonEvent(kPointerDeviceId, ui::ET_MOUSE_PRESSED,
gfx::Point(500, 500), ui::EF_LEFT_MOUSE_BUTTON);
DispatchSingleEventToWidget(event, widget.get());
ASSERT_EQ(1u, recorder.mouse_events().size());
EXPECT_EQ(ui::ET_MOUSE_PRESSED, recorder.mouse_events()[0].type());
EXPECT_TRUE(recorder.mouse_events()[0].flags() & ui::EF_IS_NON_CLIENT);
widget->GetNativeWindow()->RemovePreTargetHandler(&recorder);
}
TEST_F(DesktopWindowTreeHostX11HighDPITest, MouseNCEvents) {
std::unique_ptr<Widget> widget = CreateWidget(new ShapedWidgetDelegate());
widget->Show();
ui::X11EventSource::GetInstance()->DispatchXEvents();
widget->SetBounds(gfx::Rect(100, 100, 1000, 1000));
ui::X11EventSource::GetInstance()->DispatchXEvents();
MouseEventRecorder recorder;
widget->GetNativeWindow()->AddPreTargetHandler(&recorder);
ui::ScopedXI2Event event;
event.InitGenericButtonEvent(kPointerDeviceId, ui::ET_MOUSE_PRESSED,
gfx::Point(1001, 1001),
ui::EF_LEFT_MOUSE_BUTTON);
DispatchSingleEventToWidget(event, widget.get());
ASSERT_EQ(1u, recorder.mouse_events().size());
EXPECT_EQ(ui::ET_MOUSE_PRESSED, recorder.mouse_events()[0].type());
EXPECT_TRUE(recorder.mouse_events()[0].flags() & ui::EF_IS_NON_CLIENT);
widget->GetNativeWindow()->RemovePreTargetHandler(&recorder);
}
} // namespace views