| // Copyright 2018 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 "ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h" |
| |
| #include "ui/aura/client/aura_constants.h" |
| #include "ui/aura/window_tree_host_platform.h" |
| #include "ui/base/hit_test.h" |
| #include "ui/platform_window/platform_window.h" |
| #include "ui/platform_window/wm/wm_move_resize_handler.h" |
| #include "ui/views/test/widget_test.h" |
| #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" |
| #include "ui/views/widget/desktop_aura/window_event_filter_linux.h" |
| #include "ui/views/widget/widget_delegate.h" |
| #include "ui/views/window/native_frame_view.h" |
| |
| #if defined(USE_X11) |
| #include "ui/aura/env.h" |
| #include "ui/base/ime/input_method.h" |
| #include "ui/base/x/test/x11_property_change_waiter.h" |
| #include "ui/base/x/x11_util.h" |
| #include "ui/gfx/x/x11_atom_cache.h" |
| #include "ui/gfx/x/xproto.h" |
| #include "ui/gfx/x/xproto_util.h" |
| #include "ui/views/controls/textfield/textfield.h" |
| #endif // defined(USE_X11) |
| |
| namespace views { |
| |
| namespace { |
| |
| bool IsNonClientComponent(int hittest) { |
| switch (hittest) { |
| case HTBOTTOM: |
| case HTBOTTOMLEFT: |
| case HTBOTTOMRIGHT: |
| case HTCAPTION: |
| case HTLEFT: |
| case HTRIGHT: |
| case HTTOP: |
| case HTTOPLEFT: |
| case HTTOPRIGHT: |
| return true; |
| default: |
| return false; |
| } |
| return true; |
| } |
| |
| #if defined(USE_X11) |
| // Creates a widget with the given bounds. |
| std::unique_ptr<Widget> CreateWidget(const gfx::Rect& bounds) { |
| std::unique_ptr<Widget> widget(new Widget); |
| Widget::InitParams params(Widget::InitParams::TYPE_WINDOW); |
| params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| params.remove_standard_frame = true; |
| params.native_widget = new DesktopNativeWidgetAura(widget.get()); |
| params.bounds = bounds; |
| widget->Init(std::move(params)); |
| return widget; |
| } |
| |
| // Dispatches a XMotionEvent targeted at |host|'s X window with location |
| // |point_in_screen|. |
| void DispatchMouseMotionEvent(DesktopWindowTreeHostLinux* desktop_host, |
| const gfx::Point& point_in_screen) { |
| gfx::Rect bounds_in_screen = desktop_host->window()->GetBoundsInScreen(); |
| |
| auto* connection = x11::Connection::Get(); |
| x11::MotionNotifyEvent xev{ |
| .detail = x11::Motion::Normal, |
| .root = connection->default_root(), |
| .event = static_cast<x11::Window>(desktop_host->GetAcceleratedWidget()), |
| .root_x = point_in_screen.x(), |
| .root_y = point_in_screen.y(), |
| .event_x = point_in_screen.x() - bounds_in_screen.x(), |
| .event_y = point_in_screen.y() - bounds_in_screen.y(), |
| .same_screen = true, |
| }; |
| |
| connection->DispatchEvent(x11::Event{xev}); |
| } |
| |
| // Blocks till |window| gets activated. |
| class ActivationWaiter : public ui::X11PropertyChangeWaiter { |
| public: |
| explicit ActivationWaiter(x11::Window window) |
| : ui::X11PropertyChangeWaiter(ui::GetX11RootWindow(), |
| "_NET_ACTIVE_WINDOW"), |
| window_(window) {} |
| |
| ~ActivationWaiter() override = default; |
| |
| ActivationWaiter(const ActivationWaiter&) = delete; |
| ActivationWaiter& operator=(ActivationWaiter&) = delete; |
| |
| private: |
| // ui::X11PropertyChangeWaiter: |
| bool ShouldKeepOnWaiting() override { |
| x11::Window window = x11::Window::None; |
| x11::GetProperty(ui::GetX11RootWindow(), x11::GetAtom("_NET_ACTIVE_WINDOW"), |
| &window); |
| return window != window_; |
| } |
| |
| x11::Window window_; |
| }; |
| #endif // defined(USE_X11) |
| |
| // An event handler which counts the number of mouse moves it has seen. |
| class MouseMoveCounterHandler : public ui::EventHandler { |
| public: |
| MouseMoveCounterHandler() = default; |
| ~MouseMoveCounterHandler() override = default; |
| |
| // ui::EventHandler: |
| void OnMouseEvent(ui::MouseEvent* event) override { |
| if (event->type() == ui::ET_MOUSE_MOVED) |
| ++count_; |
| } |
| |
| int num_mouse_moves() const { return count_; } |
| |
| private: |
| int count_ = 0; |
| |
| DISALLOW_COPY_AND_ASSIGN(MouseMoveCounterHandler); |
| }; |
| |
| // A fake handler, which just stores the hittest and pointer location values. |
| class FakeWmMoveResizeHandler : public ui::WmMoveResizeHandler { |
| public: |
| using SetBoundsCallback = base::RepeatingCallback<void(gfx::Rect)>; |
| explicit FakeWmMoveResizeHandler(ui::PlatformWindow* window) |
| : platform_window_(window), hittest_(-1) {} |
| ~FakeWmMoveResizeHandler() override = default; |
| |
| void Reset() { |
| hittest_ = -1; |
| pointer_location_in_px_ = gfx::Point(); |
| } |
| |
| int hittest() const { return hittest_; } |
| gfx::Point pointer_location_in_px() const { return pointer_location_in_px_; } |
| |
| void set_bounds(const gfx::Rect& bounds) { bounds_ = bounds; } |
| |
| // ui::WmMoveResizeHandler |
| void DispatchHostWindowDragMovement( |
| int hittest, |
| const gfx::Point& pointer_location_in_px) override { |
| hittest_ = hittest; |
| pointer_location_in_px_ = pointer_location_in_px; |
| |
| platform_window_->SetBounds(bounds_); |
| } |
| |
| private: |
| ui::PlatformWindow* platform_window_; |
| gfx::Rect bounds_; |
| |
| int hittest_ = -1; |
| gfx::Point pointer_location_in_px_; |
| |
| DISALLOW_COPY_AND_ASSIGN(FakeWmMoveResizeHandler); |
| }; |
| |
| void SetExpectationBasedOnHittestValue( |
| int hittest, |
| const FakeWmMoveResizeHandler& handler, |
| const gfx::Point& pointer_location_in_px) { |
| if (IsNonClientComponent(hittest)) { |
| // Ensure both the pointer location and the hit test value are passed to the |
| // fake move/resize handler. |
| EXPECT_EQ(handler.pointer_location_in_px().ToString(), |
| pointer_location_in_px.ToString()); |
| EXPECT_EQ(handler.hittest(), hittest); |
| return; |
| } |
| |
| // Ensure the handler does not receive the hittest value or the pointer |
| // location. |
| EXPECT_TRUE(handler.pointer_location_in_px().IsOrigin()); |
| EXPECT_NE(handler.hittest(), hittest); |
| } |
| |
| // This is used to return a customized result to NonClientHitTest. |
| class HitTestNonClientFrameView : public NativeFrameView { |
| public: |
| explicit HitTestNonClientFrameView(Widget* widget) |
| : NativeFrameView(widget) {} |
| ~HitTestNonClientFrameView() override = default; |
| |
| void set_hit_test_result(int component) { hit_test_result_ = component; } |
| |
| // NonClientFrameView overrides: |
| int NonClientHitTest(const gfx::Point& point) override { |
| return hit_test_result_; |
| } |
| |
| private: |
| int hit_test_result_ = HTNOWHERE; |
| |
| DISALLOW_COPY_AND_ASSIGN(HitTestNonClientFrameView); |
| }; |
| |
| // This is used to return HitTestNonClientFrameView on create call. |
| class HitTestWidgetDelegate : public WidgetDelegate { |
| public: |
| HitTestWidgetDelegate() { |
| SetCanResize(true); |
| SetOwnedByWidget(true); |
| } |
| ~HitTestWidgetDelegate() override = default; |
| |
| HitTestNonClientFrameView* frame_view() { return frame_view_; } |
| |
| // WidgetDelegate: |
| std::unique_ptr<NonClientFrameView> CreateNonClientFrameView( |
| Widget* widget) override { |
| DCHECK(!frame_view_); |
| auto frame_view = std::make_unique<HitTestNonClientFrameView>(widget); |
| frame_view_ = frame_view.get(); |
| return frame_view; |
| } |
| |
| private: |
| HitTestNonClientFrameView* frame_view_ = nullptr; |
| |
| DISALLOW_COPY_AND_ASSIGN(HitTestWidgetDelegate); |
| }; |
| |
| // Test host that can intercept calls to the real host. |
| class TestDesktopWindowTreeHostLinux : public DesktopWindowTreeHostLinux { |
| public: |
| TestDesktopWindowTreeHostLinux( |
| internal::NativeWidgetDelegate* native_widget_delegate, |
| DesktopNativeWidgetAura* desktop_native_widget_aura) |
| : DesktopWindowTreeHostLinux(native_widget_delegate, |
| desktop_native_widget_aura) {} |
| ~TestDesktopWindowTreeHostLinux() override = default; |
| |
| // PlatformWindowDelegate: |
| // Instead of making these tests friends of the host, override the dispatch |
| // method to make it public and nothing else. |
| void DispatchEvent(ui::Event* event) override { |
| DesktopWindowTreeHostLinux::DispatchEvent(event); |
| } |
| |
| void ResetCalledMaximize() { called_maximize_ = false; } |
| bool called_maximize() const { return called_maximize_; } |
| // DesktopWindowTreeHost |
| void Maximize() override { |
| called_maximize_ = true; |
| DesktopWindowTreeHostLinux::Maximize(); |
| } |
| |
| private: |
| bool called_maximize_ = false; |
| |
| DISALLOW_COPY_AND_ASSIGN(TestDesktopWindowTreeHostLinux); |
| }; |
| |
| } // namespace |
| |
| class DesktopWindowTreeHostLinuxTest |
| : public test::DesktopWidgetTestInteractive { |
| public: |
| DesktopWindowTreeHostLinuxTest() = default; |
| ~DesktopWindowTreeHostLinuxTest() override = default; |
| |
| protected: |
| Widget* BuildTopLevelDesktopWidget(const gfx::Rect& bounds) { |
| Widget* toplevel = new Widget; |
| delegate_ = new HitTestWidgetDelegate(); |
| Widget::InitParams toplevel_params = |
| CreateParams(Widget::InitParams::TYPE_WINDOW); |
| auto* native_widget = new DesktopNativeWidgetAura(toplevel); |
| toplevel_params.native_widget = native_widget; |
| host_ = new TestDesktopWindowTreeHostLinux(toplevel, native_widget); |
| toplevel_params.desktop_window_tree_host = host_; |
| toplevel_params.delegate = delegate_; |
| toplevel_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| toplevel_params.bounds = bounds; |
| toplevel_params.remove_standard_frame = true; |
| toplevel->Init(std::move(toplevel_params)); |
| return toplevel; |
| } |
| |
| void GenerateAndDispatchMouseEvent(ui::EventType event_type, |
| const gfx::Point& click_location, |
| int flags) { |
| DCHECK(host_); |
| std::unique_ptr<ui::MouseEvent> mouse_event( |
| GenerateMouseEvent(event_type, click_location, flags)); |
| host_->DispatchEvent(mouse_event.get()); |
| } |
| |
| void GenerateAndDispatchClickMouseEvent(const gfx::Point& click_location, |
| int flags) { |
| DCHECK(host_); |
| GenerateAndDispatchMouseEvent(ui::ET_MOUSE_PRESSED, click_location, flags); |
| GenerateAndDispatchMouseEvent(ui::ET_MOUSE_RELEASED, click_location, flags); |
| } |
| |
| ui::MouseEvent* GenerateMouseEvent(ui::EventType event_type, |
| const gfx::Point& click_location, |
| int flags) { |
| int flag = 0; |
| if (flags & ui::EF_LEFT_MOUSE_BUTTON) |
| flag = ui::EF_LEFT_MOUSE_BUTTON; |
| else if (flags & ui::EF_RIGHT_MOUSE_BUTTON) |
| flag = ui::EF_RIGHT_MOUSE_BUTTON; |
| |
| if (!flag) { |
| NOTREACHED() |
| << "Other mouse clicks are not supported yet. Add the new one."; |
| } |
| return new ui::MouseEvent(event_type, click_location, click_location, |
| base::TimeTicks::Now(), flags, flag); |
| } |
| |
| HitTestWidgetDelegate* delegate_ = nullptr; |
| TestDesktopWindowTreeHostLinux* host_ = nullptr; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(DesktopWindowTreeHostLinuxTest); |
| }; |
| |
| TEST_F(DesktopWindowTreeHostLinuxTest, HitTest) { |
| gfx::Rect bounds(0, 0, 100, 100); |
| std::unique_ptr<Widget> widget(BuildTopLevelDesktopWidget(bounds)); |
| widget->Show(); |
| |
| // Install a fake move/resize handler to intercept the move/resize call. |
| auto handler = |
| std::make_unique<FakeWmMoveResizeHandler>(host_->platform_window()); |
| host_->DestroyNonClientEventFilter(); |
| host_->non_client_window_event_filter_ = |
| std::make_unique<WindowEventFilterLinux>(host_, handler.get()); |
| |
| // It is not important to use pointer locations corresponding to the hittests |
| // values used in the browser itself, because we fake the hit test results, |
| // which non client frame view sends back. Thus, just make sure the content |
| // window is able to receive these events. |
| gfx::Point pointer_location_in_px(10, 10); |
| |
| constexpr int hittest_values[] = { |
| HTBOTTOM, HTBOTTOMLEFT, HTBOTTOMRIGHT, HTCAPTION, HTLEFT, |
| HTRIGHT, HTTOP, HTTOPLEFT, HTTOPRIGHT, HTNOWHERE, |
| HTBORDER, HTCLIENT, HTCLOSE, HTERROR, HTGROWBOX, |
| HTHELP, HTHSCROLL, HTMENU, HTMAXBUTTON, HTMINBUTTON, |
| HTREDUCE, HTSIZE, HTSYSMENU, HTTRANSPARENT, HTVSCROLL, |
| HTZOOM, |
| }; |
| |
| aura::Window* window = widget->GetNativeWindow(); |
| auto* frame_view = delegate_->frame_view(); |
| for (int hittest : hittest_values) { |
| handler->Reset(); |
| |
| // Set the desired hit test result value, which will be returned, when |
| // WindowEventFilter starts to perform hit testing. |
| frame_view->set_hit_test_result(hittest); |
| |
| gfx::Rect bounds = window->GetBoundsInScreen(); |
| |
| // The wm move/resize handler receives pointer location in the global screen |
| // coordinate, whereas event dispatcher receives event locations on a local |
| // system coordinate. Thus, add an offset of a new possible origin value of |
| // a window to the expected pointer location. |
| gfx::Point expected_pointer_location_in_px(pointer_location_in_px); |
| expected_pointer_location_in_px.Offset(bounds.x(), bounds.y()); |
| |
| if (hittest == HTCAPTION) { |
| // Move the window on HTCAPTION hit test value. |
| bounds = |
| gfx::Rect(gfx::Point(bounds.x() + 2, bounds.y() + 4), bounds.size()); |
| handler->set_bounds(bounds); |
| } else if (IsNonClientComponent(hittest)) { |
| // Resize the window on other than HTCAPTION non client hit test values. |
| bounds = gfx::Rect( |
| gfx::Point(bounds.origin()), |
| gfx::Size(bounds.size().width() + 5, bounds.size().height() + 10)); |
| handler->set_bounds(bounds); |
| } |
| |
| // Send mouse down event and make sure the WindowEventFilter calls the |
| // move/resize handler to start interactive move/resize with the |hittest| |
| // value we specified. |
| GenerateAndDispatchMouseEvent(ui::ET_MOUSE_PRESSED, pointer_location_in_px, |
| ui::EF_LEFT_MOUSE_BUTTON); |
| |
| // The test expectation is based on the hit test component. If it is a |
| // non-client component, which results in a call to move/resize, the |
| // handler must receive the hittest value and the pointer location in |
| // global screen coordinate system. In other cases, it must not. |
| SetExpectationBasedOnHittestValue(hittest, *handler.get(), |
| expected_pointer_location_in_px); |
| // Make sure the bounds of the content window are correct. |
| EXPECT_EQ(window->GetBoundsInScreen().ToString(), bounds.ToString()); |
| |
| // Dispatch mouse release event to release a mouse pressed handler and be |
| // able to consume future events. |
| GenerateAndDispatchMouseEvent(ui::ET_MOUSE_RELEASED, pointer_location_in_px, |
| ui::EF_LEFT_MOUSE_BUTTON); |
| } |
| } |
| |
| // Tests that the window is maximized in response to a double click event. |
| TEST_F(DesktopWindowTreeHostLinuxTest, DoubleClickHeaderMaximizes) { |
| gfx::Rect bounds(0, 0, 100, 100); |
| std::unique_ptr<Widget> widget(BuildTopLevelDesktopWidget(bounds)); |
| widget->Show(); |
| |
| aura::Window* window = widget->GetNativeWindow(); |
| window->SetProperty(aura::client::kResizeBehaviorKey, |
| aura::client::kResizeBehaviorCanMaximize); |
| |
| RunPendingMessages(); |
| |
| host_->ResetCalledMaximize(); |
| |
| auto* frame_view = delegate_->frame_view(); |
| // Set the desired hit test result value, which will be returned, when |
| // WindowEventFilter starts to perform hit testing. |
| frame_view->set_hit_test_result(HTCAPTION); |
| |
| host_->ResetCalledMaximize(); |
| |
| int flags = ui::EF_LEFT_MOUSE_BUTTON; |
| GenerateAndDispatchClickMouseEvent(gfx::Point(), flags); |
| flags |= ui::EF_IS_DOUBLE_CLICK; |
| GenerateAndDispatchClickMouseEvent(gfx::Point(), flags); |
| |
| EXPECT_TRUE(host_->called_maximize()); |
| |
| widget->CloseNow(); |
| } |
| |
| // Tests that the window does not maximize in response to a double click event, |
| // if the first click was to a different target component than that of the |
| // second click. |
| TEST_F(DesktopWindowTreeHostLinuxTest, |
| DoubleClickTwoDifferentTargetsDoesntMaximizes) { |
| gfx::Rect bounds(0, 0, 100, 100); |
| std::unique_ptr<Widget> widget(BuildTopLevelDesktopWidget(bounds)); |
| widget->Show(); |
| |
| aura::Window* window = widget->GetNativeWindow(); |
| window->SetProperty(aura::client::kResizeBehaviorKey, |
| aura::client::kResizeBehaviorCanMaximize); |
| |
| RunPendingMessages(); |
| |
| host_->ResetCalledMaximize(); |
| |
| auto* frame_view = delegate_->frame_view(); |
| |
| frame_view->set_hit_test_result(HTCLIENT); |
| int flags = ui::EF_LEFT_MOUSE_BUTTON; |
| GenerateAndDispatchClickMouseEvent(gfx::Point(), flags); |
| |
| frame_view->set_hit_test_result(HTCLIENT); |
| flags |= ui::EF_IS_DOUBLE_CLICK; |
| GenerateAndDispatchClickMouseEvent(gfx::Point(), flags); |
| |
| EXPECT_FALSE(host_->called_maximize()); |
| |
| widget->CloseNow(); |
| } |
| |
| // Tests that the window does not maximize in response to a double click event, |
| // if the double click was interrupted by a right click. |
| TEST_F(DesktopWindowTreeHostLinuxTest, |
| RightClickDuringDoubleClickDoesntMaximize) { |
| gfx::Rect bounds(0, 0, 100, 100); |
| std::unique_ptr<Widget> widget(BuildTopLevelDesktopWidget(bounds)); |
| widget->Show(); |
| |
| aura::Window* window = widget->GetNativeWindow(); |
| window->SetProperty(aura::client::kResizeBehaviorKey, |
| aura::client::kResizeBehaviorCanMaximize); |
| |
| RunPendingMessages(); |
| |
| host_->ResetCalledMaximize(); |
| |
| auto* frame_view = delegate_->frame_view(); |
| |
| frame_view->set_hit_test_result(HTCLIENT); |
| int flags_left_button = ui::EF_LEFT_MOUSE_BUTTON; |
| GenerateAndDispatchClickMouseEvent(gfx::Point(), flags_left_button); |
| |
| frame_view->set_hit_test_result(HTCAPTION); |
| GenerateAndDispatchClickMouseEvent(gfx::Point(), ui::EF_RIGHT_MOUSE_BUTTON); |
| EXPECT_FALSE(host_->called_maximize()); |
| |
| flags_left_button |= ui::EF_IS_DOUBLE_CLICK; |
| GenerateAndDispatchClickMouseEvent(gfx::Point(), flags_left_button); |
| EXPECT_FALSE(host_->called_maximize()); |
| |
| widget->CloseNow(); |
| } |
| |
| #if defined(USE_X11) |
| // Test that calling Widget::Deactivate() sets the widget as inactive wrt to |
| // Chrome even if it not possible to deactivate the window wrt to the x server. |
| // This behavior is required by several interactive_ui_tests. |
| TEST_F(DesktopWindowTreeHostLinuxTest, Deactivate) { |
| std::unique_ptr<Widget> widget(CreateWidget(gfx::Rect(100, 100, 100, 100))); |
| |
| ActivationWaiter waiter(static_cast<x11::Window>( |
| widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget())); |
| widget->Show(); |
| widget->Activate(); |
| waiter.Wait(); |
| |
| widget->Deactivate(); |
| // Regardless of whether |widget|'s X11 window eventually gets deactivated, |
| // |widget|'s "active" state should change. |
| EXPECT_FALSE(widget->IsActive()); |
| |
| // |widget|'s X11 window should still be active. Reactivating |widget| should |
| // update the widget's "active" state. |
| // Note: Activating a widget whose X11 window is not active does not |
| // synchronously update the widget's "active" state. |
| widget->Activate(); |
| EXPECT_TRUE(widget->IsActive()); |
| } |
| |
| // Chrome attempts to make mouse capture look synchronous on Linux. Test that |
| // Chrome synchronously switches the window that mouse events are forwarded to |
| // when capture is changed. |
| TEST_F(DesktopWindowTreeHostLinuxTest, CaptureEventForwarding) { |
| std::unique_ptr<Widget> widget1(CreateWidget(gfx::Rect(100, 100, 100, 100))); |
| aura::Window* window1 = widget1->GetNativeWindow(); |
| DesktopWindowTreeHostLinux* host1 = |
| static_cast<DesktopWindowTreeHostLinux*>(window1->GetHost()); |
| widget1->Show(); |
| |
| std::unique_ptr<Widget> widget2(CreateWidget(gfx::Rect(200, 100, 100, 100))); |
| aura::Window* window2 = widget2->GetNativeWindow(); |
| DesktopWindowTreeHostLinux* host2 = |
| static_cast<DesktopWindowTreeHostLinux*>(window2->GetHost()); |
| widget2->Show(); |
| |
| MouseMoveCounterHandler recorder1; |
| window1->AddPreTargetHandler(&recorder1); |
| MouseMoveCounterHandler recorder2; |
| window2->AddPreTargetHandler(&recorder2); |
| |
| // Move the mouse to the center of |widget2|. |
| gfx::Point point_in_screen = widget2->GetWindowBoundsInScreen().CenterPoint(); |
| DispatchMouseMotionEvent(host2, point_in_screen); |
| EXPECT_EQ(0, recorder1.num_mouse_moves()); |
| EXPECT_EQ(1, recorder2.num_mouse_moves()); |
| EXPECT_EQ(point_in_screen.ToString(), |
| aura::Env::GetInstance()->last_mouse_location().ToString()); |
| |
| // Set capture to |widget1|. Because DesktopWindowTreeHostX11 calls |
| // XGrabPointer() with owner == False, the X server sends events to |widget2| |
| // as long as the mouse is hovered over |widget2|. Verify that Chrome |
| // redirects mouse events to |widget1|. |
| widget1->SetCapture(nullptr); |
| point_in_screen += gfx::Vector2d(1, 0); |
| DispatchMouseMotionEvent(host2, point_in_screen); |
| EXPECT_EQ(1, recorder1.num_mouse_moves()); |
| EXPECT_EQ(1, recorder2.num_mouse_moves()); |
| // If the event's location was correctly changed to be relative to |widget1|, |
| // Env's last mouse position will be correct. |
| EXPECT_EQ(point_in_screen.ToString(), |
| aura::Env::GetInstance()->last_mouse_location().ToString()); |
| |
| // Set capture to |widget2|. Subsequent events sent to |widget2| should not be |
| // forwarded. |
| widget2->SetCapture(nullptr); |
| point_in_screen += gfx::Vector2d(1, 0); |
| DispatchMouseMotionEvent(host2, point_in_screen); |
| EXPECT_EQ(1, recorder1.num_mouse_moves()); |
| EXPECT_EQ(2, recorder2.num_mouse_moves()); |
| EXPECT_EQ(point_in_screen.ToString(), |
| aura::Env::GetInstance()->last_mouse_location().ToString()); |
| |
| // If the mouse is not hovered over |widget1| or |widget2|, the X server will |
| // send events to the window which has capture. Test the mouse events sent to |
| // |widget2| are not forwarded. |
| DispatchMouseMotionEvent(host2, point_in_screen); |
| EXPECT_EQ(1, recorder1.num_mouse_moves()); |
| EXPECT_EQ(3, recorder2.num_mouse_moves()); |
| EXPECT_EQ(point_in_screen.ToString(), |
| aura::Env::GetInstance()->last_mouse_location().ToString()); |
| |
| // Release capture. Test that when capture is released, mouse events are no |
| // longer forwarded to other widgets. |
| widget2->ReleaseCapture(); |
| point_in_screen = widget1->GetWindowBoundsInScreen().CenterPoint(); |
| DispatchMouseMotionEvent(host1, point_in_screen); |
| EXPECT_EQ(2, recorder1.num_mouse_moves()); |
| EXPECT_EQ(3, recorder2.num_mouse_moves()); |
| EXPECT_EQ(point_in_screen.ToString(), |
| aura::Env::GetInstance()->last_mouse_location().ToString()); |
| |
| // Cleanup |
| window1->RemovePreTargetHandler(&recorder1); |
| window2->RemovePreTargetHandler(&recorder2); |
| } |
| |
| TEST_F(DesktopWindowTreeHostLinuxTest, InputMethodFocus) { |
| std::unique_ptr<Widget> widget(CreateWidget(gfx::Rect(100, 100, 100, 100))); |
| |
| // Waiter should be created as early as possible so that PropertyNotify has |
| // time to be set before widget is activated. |
| ActivationWaiter waiter(static_cast<x11::Window>( |
| widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget())); |
| |
| std::unique_ptr<Textfield> textfield(new Textfield); |
| textfield->SetBounds(0, 0, 200, 20); |
| widget->GetRootView()->AddChildView(textfield.get()); |
| widget->ShowInactive(); |
| textfield->RequestFocus(); |
| |
| EXPECT_FALSE(widget->IsActive()); |
| // TODO(shuchen): uncomment the below check once the |
| // "default-focused-input-method" logic is removed in aura::WindowTreeHost. |
| // EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE, |
| // widget->GetInputMethod()->GetTextInputType()); |
| |
| widget->Activate(); |
| waiter.Wait(); |
| |
| EXPECT_TRUE(widget->IsActive()); |
| EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT, |
| widget->GetInputMethod()->GetTextInputType()); |
| |
| widget->Deactivate(); |
| |
| EXPECT_FALSE(widget->IsActive()); |
| EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE, |
| widget->GetInputMethod()->GetTextInputType()); |
| } |
| |
| #endif // defined(USE_X11) |
| |
| } // namespace views |