// Copyright 2015 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 "components/exo/pointer.h" #include "ash/public/cpp/shell_window_ids.h" #include "ash/shell.h" #include "ash/wm/desks/desks_util.h" #include "ash/wm/window_positioning_utils.h" #include "base/bind.h" #include "base/run_loop.h" #include "base/test/scoped_feature_list.h" #include "components/exo/buffer.h" #include "components/exo/data_source.h" #include "components/exo/data_source_delegate.h" #include "components/exo/pointer_constraint_delegate.h" #include "components/exo/pointer_delegate.h" #include "components/exo/relative_pointer_delegate.h" #include "components/exo/seat.h" #include "components/exo/shell_surface.h" #include "components/exo/sub_surface.h" #include "components/exo/surface.h" #include "components/exo/test/exo_test_base.h" #include "components/exo/test/exo_test_helper.h" #include "components/viz/common/quads/compositor_frame.h" #include "components/viz/service/surfaces/surface.h" #include "components/viz/service/surfaces/surface_manager.h" #include "testing/gmock/include/gmock/gmock.h" #include "ui/aura/client/cursor_client.h" #include "ui/aura/client/focus_client.h" #include "ui/base/cursor/mojom/cursor_type.mojom-shared.h" #include "ui/events/event_utils.h" #include "ui/events/test/event_generator.h" #include "ui/views/widget/widget.h" #if defined(OS_CHROMEOS) #include "chromeos/constants/chromeos_features.h" #endif namespace exo { namespace { class MockPointerDelegate : public PointerDelegate { public: MockPointerDelegate() {} // Overridden from PointerDelegate: MOCK_METHOD1(OnPointerDestroying, void(Pointer*)); MOCK_CONST_METHOD1(CanAcceptPointerEventsForSurface, bool(Surface*)); MOCK_METHOD3(OnPointerEnter, void(Surface*, const gfx::PointF&, int)); MOCK_METHOD1(OnPointerLeave, void(Surface*)); MOCK_METHOD2(OnPointerMotion, void(base::TimeTicks, const gfx::PointF&)); MOCK_METHOD3(OnPointerButton, void(base::TimeTicks, int, bool)); MOCK_METHOD3(OnPointerScroll, void(base::TimeTicks, const gfx::Vector2dF&, bool)); MOCK_METHOD1(OnPointerScrollStop, void(base::TimeTicks)); MOCK_METHOD0(OnPointerFrame, void()); }; class MockRelativePointerDelegate : public RelativePointerDelegate { public: MockRelativePointerDelegate() = default; ~MockRelativePointerDelegate() = default; // Overridden from RelativePointerDelegate: MOCK_METHOD1(OnPointerDestroying, void(Pointer*)); MOCK_METHOD2(OnPointerRelativeMotion, void(base::TimeTicks, const gfx::PointF&)); }; class MockPointerConstraintDelegate : public PointerConstraintDelegate { public: MockPointerConstraintDelegate() = default; ~MockPointerConstraintDelegate() = default; // Overridden from PointerConstraintDelegate: MOCK_METHOD0(OnConstraintBroken, void()); MOCK_METHOD0(GetConstrainedSurface, Surface*()); }; class TestDataSourceDelegate : public DataSourceDelegate { public: TestDataSourceDelegate() {} // Overridden from DataSourceDelegate: void OnDataSourceDestroying(DataSource* device) override {} void OnTarget(const base::Optional& mime_type) override {} void OnSend(const std::string& mime_type, base::ScopedFD fd) override {} void OnCancelled() override {} void OnDndDropPerformed() override {} void OnDndFinished() override {} void OnAction(DndAction dnd_action) override {} bool CanAcceptDataEventsForSurface(Surface* surface) const override { return true; } DISALLOW_COPY_AND_ASSIGN(TestDataSourceDelegate); }; class PointerTest : public test::ExoTestBase { public: PointerTest() = default; void SetUp() override { test::ExoTestBase::SetUp(); // Sometimes underlying infra (i.e. X11 / Xvfb) may emit pointer events // which can break MockPointerDelegate's expectations, so they should be // consumed before starting. See https://crbug.com/854674. base::RunLoop().RunUntilIdle(); } private: DISALLOW_COPY_AND_ASSIGN(PointerTest); }; TEST_F(PointerTest, SetCursor) { std::unique_ptr surface(new Surface); std::unique_ptr shell_surface(new ShellSurface(surface.get())); gfx::Size buffer_size(10, 10); std::unique_ptr buffer( new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size))); surface->Attach(buffer.get()); surface->Commit(); MockPointerDelegate delegate; Seat seat; std::unique_ptr pointer(new Pointer(&delegate, &seat)); ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow()); EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get())) .WillRepeatedly(testing::Return(true)); EXPECT_CALL(delegate, OnPointerFrame()).Times(1); EXPECT_CALL(delegate, OnPointerEnter(surface.get(), gfx::PointF(), 0)); generator.MoveMouseTo(surface->window()->GetBoundsInScreen().origin()); std::unique_ptr pointer_surface(new Surface); std::unique_ptr pointer_buffer( new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size))); pointer_surface->Attach(pointer_buffer.get()); pointer_surface->Commit(); // Set pointer surface. pointer->SetCursor(pointer_surface.get(), gfx::Point(5, 5)); base::RunLoop().RunUntilIdle(); const viz::RenderPass* last_render_pass; { viz::SurfaceId surface_id = pointer->host_window()->GetSurfaceId(); viz::SurfaceManager* surface_manager = GetSurfaceManager(); ASSERT_TRUE(surface_manager->GetSurfaceForId(surface_id)->HasActiveFrame()); const viz::CompositorFrame& frame = surface_manager->GetSurfaceForId(surface_id)->GetActiveFrame(); EXPECT_EQ(gfx::Rect(0, 0, 10, 10), frame.render_pass_list.back()->output_rect); last_render_pass = frame.render_pass_list.back().get(); } // Adjust hotspot. pointer->SetCursor(pointer_surface.get(), gfx::Point()); base::RunLoop().RunUntilIdle(); // Verify that adjustment to hotspot resulted in new frame. { viz::SurfaceId surface_id = pointer->host_window()->GetSurfaceId(); viz::SurfaceManager* surface_manager = GetSurfaceManager(); ASSERT_TRUE(surface_manager->GetSurfaceForId(surface_id)->HasActiveFrame()); const viz::CompositorFrame& frame = surface_manager->GetSurfaceForId(surface_id)->GetActiveFrame(); EXPECT_TRUE(frame.render_pass_list.back().get() != last_render_pass); } // Unset pointer surface. pointer->SetCursor(nullptr, gfx::Point()); EXPECT_CALL(delegate, OnPointerDestroying(pointer.get())); pointer.reset(); } TEST_F(PointerTest, SetCursorNull) { std::unique_ptr surface(new Surface); std::unique_ptr shell_surface(new ShellSurface(surface.get())); gfx::Size buffer_size(10, 10); std::unique_ptr buffer( new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size))); surface->Attach(buffer.get()); surface->Commit(); MockPointerDelegate delegate; Seat seat; std::unique_ptr pointer(new Pointer(&delegate, &seat)); ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow()); EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get())) .WillRepeatedly(testing::Return(true)); EXPECT_CALL(delegate, OnPointerFrame()).Times(1); EXPECT_CALL(delegate, OnPointerEnter(surface.get(), gfx::PointF(), 0)); generator.MoveMouseTo(surface->window()->GetBoundsInScreen().origin()); pointer->SetCursor(nullptr, gfx::Point()); base::RunLoop().RunUntilIdle(); EXPECT_EQ(nullptr, pointer->root_surface()); aura::client::CursorClient* cursor_client = aura::client::GetCursorClient( shell_surface->GetWidget()->GetNativeWindow()->GetRootWindow()); EXPECT_EQ(ui::mojom::CursorType::kNone, cursor_client->GetCursor().type()); EXPECT_CALL(delegate, OnPointerDestroying(pointer.get())); pointer.reset(); } TEST_F(PointerTest, SetCursorType) { std::unique_ptr surface(new Surface); std::unique_ptr shell_surface(new ShellSurface(surface.get())); gfx::Size buffer_size(10, 10); std::unique_ptr buffer( new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size))); surface->Attach(buffer.get()); surface->Commit(); MockPointerDelegate delegate; Seat seat; std::unique_ptr pointer(new Pointer(&delegate, &seat)); ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow()); EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get())) .WillRepeatedly(testing::Return(true)); EXPECT_CALL(delegate, OnPointerFrame()).Times(1); EXPECT_CALL(delegate, OnPointerEnter(surface.get(), gfx::PointF(), 0)); generator.MoveMouseTo(surface->window()->GetBoundsInScreen().origin()); pointer->SetCursorType(ui::mojom::CursorType::kIBeam); base::RunLoop().RunUntilIdle(); EXPECT_EQ(nullptr, pointer->root_surface()); aura::client::CursorClient* cursor_client = aura::client::GetCursorClient( shell_surface->GetWidget()->GetNativeWindow()->GetRootWindow()); EXPECT_EQ(ui::mojom::CursorType::kIBeam, cursor_client->GetCursor().type()); // Set the pointer with surface after setting pointer type. std::unique_ptr pointer_surface(new Surface); std::unique_ptr pointer_buffer( new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size))); pointer_surface->Attach(pointer_buffer.get()); pointer_surface->Commit(); pointer->SetCursor(pointer_surface.get(), gfx::Point()); base::RunLoop().RunUntilIdle(); { viz::SurfaceId surface_id = pointer->host_window()->GetSurfaceId(); viz::SurfaceManager* surface_manager = GetSurfaceManager(); ASSERT_TRUE(surface_manager->GetSurfaceForId(surface_id)->HasActiveFrame()); const viz::CompositorFrame& frame = surface_manager->GetSurfaceForId(surface_id)->GetActiveFrame(); EXPECT_EQ(gfx::Rect(0, 0, 10, 10), frame.render_pass_list.back()->output_rect); } // Set the pointer type after the pointer surface is specified. pointer->SetCursorType(ui::mojom::CursorType::kCross); base::RunLoop().RunUntilIdle(); EXPECT_EQ(nullptr, pointer->root_surface()); EXPECT_EQ(ui::mojom::CursorType::kCross, cursor_client->GetCursor().type()); EXPECT_CALL(delegate, OnPointerDestroying(pointer.get())); pointer.reset(); } TEST_F(PointerTest, SetCursorTypeOutsideOfSurface) { std::unique_ptr surface(new Surface); std::unique_ptr shell_surface(new ShellSurface(surface.get())); gfx::Size buffer_size(10, 10); std::unique_ptr buffer( new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size))); surface->Attach(buffer.get()); surface->Commit(); MockPointerDelegate delegate; Seat seat; std::unique_ptr pointer(new Pointer(&delegate, &seat)); ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow()); EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get())) .WillRepeatedly(testing::Return(true)); generator.MoveMouseTo(surface->window()->GetBoundsInScreen().origin() - gfx::Vector2d(1, 1)); pointer->SetCursorType(ui::mojom::CursorType::kIBeam); base::RunLoop().RunUntilIdle(); EXPECT_EQ(nullptr, pointer->root_surface()); aura::client::CursorClient* cursor_client = aura::client::GetCursorClient( shell_surface->GetWidget()->GetNativeWindow()->GetRootWindow()); // The cursor type shouldn't be the specified one, since the pointer is // located outside of the surface. EXPECT_NE(ui::mojom::CursorType::kIBeam, cursor_client->GetCursor().type()); EXPECT_CALL(delegate, OnPointerDestroying(pointer.get())); pointer.reset(); } TEST_F(PointerTest, SetCursorAndSetCursorType) { std::unique_ptr surface(new Surface); std::unique_ptr shell_surface(new ShellSurface(surface.get())); gfx::Size buffer_size(10, 10); std::unique_ptr buffer( new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size))); surface->Attach(buffer.get()); surface->Commit(); MockPointerDelegate delegate; Seat seat; std::unique_ptr pointer(new Pointer(&delegate, &seat)); ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow()); EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get())) .WillRepeatedly(testing::Return(true)); EXPECT_CALL(delegate, OnPointerFrame()).Times(1); EXPECT_CALL(delegate, OnPointerEnter(surface.get(), gfx::PointF(), 0)); generator.MoveMouseTo(surface->window()->GetBoundsInScreen().origin()); std::unique_ptr pointer_surface(new Surface); std::unique_ptr pointer_buffer( new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size))); pointer_surface->Attach(pointer_buffer.get()); pointer_surface->Commit(); // Set pointer surface. pointer->SetCursor(pointer_surface.get(), gfx::Point()); EXPECT_EQ(1u, pointer->GetActivePresentationCallbacksForTesting().size()); base::RunLoop().RunUntilIdle(); { viz::SurfaceId surface_id = pointer->host_window()->GetSurfaceId(); viz::SurfaceManager* surface_manager = GetSurfaceManager(); ASSERT_TRUE(surface_manager->GetSurfaceForId(surface_id)->HasActiveFrame()); const viz::CompositorFrame& frame = surface_manager->GetSurfaceForId(surface_id)->GetActiveFrame(); EXPECT_EQ(gfx::Rect(0, 0, 10, 10), frame.render_pass_list.back()->output_rect); } // Set the cursor type to the kNone through SetCursorType. pointer->SetCursorType(ui::mojom::CursorType::kNone); EXPECT_TRUE(pointer->GetActivePresentationCallbacksForTesting().empty()); base::RunLoop().RunUntilIdle(); EXPECT_EQ(nullptr, pointer->root_surface()); // Set the same pointer surface again. pointer->SetCursor(pointer_surface.get(), gfx::Point()); EXPECT_EQ(1u, pointer->GetActivePresentationCallbacksForTesting().size()); auto& list = pointer->GetActivePresentationCallbacksForTesting().begin()->second; base::RunLoop runloop; list.push_back(base::BindRepeating( [](base::RepeatingClosure callback, const gfx::PresentationFeedback&) { callback.Run(); }, runloop.QuitClosure())); runloop.Run(); { viz::SurfaceId surface_id = pointer->host_window()->GetSurfaceId(); viz::SurfaceManager* surface_manager = GetSurfaceManager(); ASSERT_TRUE(surface_manager->GetSurfaceForId(surface_id)->HasActiveFrame()); const viz::CompositorFrame& frame = surface_manager->GetSurfaceForId(surface_id)->GetActiveFrame(); EXPECT_EQ(gfx::Rect(0, 0, 10, 10), frame.render_pass_list.back()->output_rect); } EXPECT_CALL(delegate, OnPointerDestroying(pointer.get())); pointer.reset(); } TEST_F(PointerTest, SetCursorNullAndSetCursorType) { std::unique_ptr surface(new Surface); std::unique_ptr shell_surface(new ShellSurface(surface.get())); gfx::Size buffer_size(10, 10); std::unique_ptr buffer( new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size))); surface->Attach(buffer.get()); surface->Commit(); MockPointerDelegate delegate; Seat seat; std::unique_ptr pointer(new Pointer(&delegate, &seat)); ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow()); EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get())) .WillRepeatedly(testing::Return(true)); EXPECT_CALL(delegate, OnPointerFrame()).Times(1); EXPECT_CALL(delegate, OnPointerEnter(surface.get(), gfx::PointF(), 0)); generator.MoveMouseTo(surface->window()->GetBoundsInScreen().origin()); // Set nullptr surface. pointer->SetCursor(nullptr, gfx::Point()); base::RunLoop().RunUntilIdle(); EXPECT_EQ(nullptr, pointer->root_surface()); aura::client::CursorClient* cursor_client = aura::client::GetCursorClient( shell_surface->GetWidget()->GetNativeWindow()->GetRootWindow()); EXPECT_EQ(ui::mojom::CursorType::kNone, cursor_client->GetCursor().type()); // Set the cursor type. pointer->SetCursorType(ui::mojom::CursorType::kIBeam); base::RunLoop().RunUntilIdle(); EXPECT_EQ(nullptr, pointer->root_surface()); EXPECT_EQ(ui::mojom::CursorType::kIBeam, cursor_client->GetCursor().type()); // Set nullptr surface again. pointer->SetCursor(nullptr, gfx::Point()); base::RunLoop().RunUntilIdle(); EXPECT_EQ(nullptr, pointer->root_surface()); EXPECT_EQ(ui::mojom::CursorType::kNone, cursor_client->GetCursor().type()); EXPECT_CALL(delegate, OnPointerDestroying(pointer.get())); pointer.reset(); } TEST_F(PointerTest, OnPointerEnter) { std::unique_ptr surface(new Surface); std::unique_ptr shell_surface(new ShellSurface(surface.get())); gfx::Size buffer_size(10, 10); std::unique_ptr buffer( new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size))); surface->Attach(buffer.get()); surface->Commit(); MockPointerDelegate delegate; Seat seat; std::unique_ptr pointer(new Pointer(&delegate, &seat)); ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow()); EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get())) .WillRepeatedly(testing::Return(true)); EXPECT_CALL(delegate, OnPointerFrame()).Times(1); EXPECT_CALL(delegate, OnPointerEnter(surface.get(), gfx::PointF(), 0)); generator.MoveMouseTo(surface->window()->GetBoundsInScreen().origin()); EXPECT_CALL(delegate, OnPointerDestroying(pointer.get())); pointer.reset(); } TEST_F(PointerTest, OnPointerLeave) { std::unique_ptr surface(new Surface); std::unique_ptr shell_surface(new ShellSurface(surface.get())); gfx::Size buffer_size(10, 10); std::unique_ptr buffer( new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size))); surface->Attach(buffer.get()); surface->Commit(); MockPointerDelegate delegate; Seat seat; std::unique_ptr pointer(new Pointer(&delegate, &seat)); ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow()); EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get())) .WillRepeatedly(testing::Return(true)); EXPECT_CALL(delegate, OnPointerFrame()).Times(4); EXPECT_CALL(delegate, OnPointerEnter(surface.get(), gfx::PointF(), 0)); generator.MoveMouseTo(surface->window()->GetBoundsInScreen().origin()); EXPECT_CALL(delegate, OnPointerLeave(surface.get())); generator.MoveMouseTo(surface->window()->GetBoundsInScreen().bottom_right()); EXPECT_CALL(delegate, OnPointerEnter(surface.get(), gfx::PointF(), 0)); generator.MoveMouseTo(surface->window()->GetBoundsInScreen().origin()); EXPECT_CALL(delegate, OnPointerLeave(surface.get())); shell_surface.reset(); surface.reset(); EXPECT_CALL(delegate, OnPointerDestroying(pointer.get())); pointer.reset(); } TEST_F(PointerTest, OnPointerMotion) { std::unique_ptr surface(new Surface); std::unique_ptr shell_surface(new ShellSurface(surface.get())); gfx::Size buffer_size(10, 10); std::unique_ptr buffer( new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size))); surface->Attach(buffer.get()); surface->Commit(); MockPointerDelegate delegate; Seat seat; std::unique_ptr pointer(new Pointer(&delegate, &seat)); ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow()); EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get())) .WillRepeatedly(testing::Return(true)); EXPECT_CALL(delegate, OnPointerFrame()).Times(6); EXPECT_CALL(delegate, OnPointerEnter(surface.get(), gfx::PointF(), 0)); generator.MoveMouseTo(surface->window()->GetBoundsInScreen().origin()); EXPECT_CALL(delegate, OnPointerMotion(testing::_, gfx::PointF(1, 1))); generator.MoveMouseTo(surface->window()->GetBoundsInScreen().origin() + gfx::Vector2d(1, 1)); std::unique_ptr sub_surface(new Surface); std::unique_ptr sub( new SubSurface(sub_surface.get(), surface.get())); surface->SetSubSurfacePosition(sub_surface.get(), gfx::Point(5, 5)); gfx::Size sub_buffer_size(5, 5); std::unique_ptr sub_buffer( new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(sub_buffer_size))); sub_surface->Attach(sub_buffer.get()); sub_surface->Commit(); surface->Commit(); EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(sub_surface.get())) .WillRepeatedly(testing::Return(true)); EXPECT_CALL(delegate, OnPointerLeave(surface.get())); EXPECT_CALL(delegate, OnPointerEnter(sub_surface.get(), gfx::PointF(), 0)); generator.MoveMouseTo(sub_surface->window()->GetBoundsInScreen().origin()); EXPECT_CALL(delegate, OnPointerMotion(testing::_, gfx::PointF(1, 1))); generator.MoveMouseTo(sub_surface->window()->GetBoundsInScreen().origin() + gfx::Vector2d(1, 1)); std::unique_ptr child_surface(new Surface); std::unique_ptr child_shell_surface( new ShellSurface(child_surface.get(), gfx::Point(9, 9), true, false, ash::desks_util::GetActiveDeskContainerId())); child_shell_surface->DisableMovement(); child_shell_surface->SetParent(shell_surface.get()); gfx::Size child_buffer_size(15, 15); std::unique_ptr child_buffer( new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(child_buffer_size))); child_surface->Attach(child_buffer.get()); child_surface->Commit(); EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(child_surface.get())) .WillRepeatedly(testing::Return(true)); EXPECT_CALL(delegate, OnPointerLeave(sub_surface.get())); EXPECT_CALL(delegate, OnPointerEnter(child_surface.get(), gfx::PointF(), 0)); generator.MoveMouseTo(child_surface->window()->GetBoundsInScreen().origin()); EXPECT_CALL(delegate, OnPointerMotion(testing::_, gfx::PointF(10, 10))); generator.MoveMouseTo(child_surface->window()->GetBoundsInScreen().origin() + gfx::Vector2d(10, 10)); EXPECT_CALL(delegate, OnPointerDestroying(pointer.get())); pointer.reset(); } TEST_F(PointerTest, OnPointerButton) { std::unique_ptr surface(new Surface); std::unique_ptr shell_surface(new ShellSurface(surface.get())); gfx::Size buffer_size(10, 10); std::unique_ptr buffer( new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size))); surface->Attach(buffer.get()); surface->Commit(); MockPointerDelegate delegate; Seat seat; std::unique_ptr pointer(new Pointer(&delegate, &seat)); ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow()); EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get())) .WillRepeatedly(testing::Return(true)); EXPECT_CALL(delegate, OnPointerFrame()).Times(3); EXPECT_CALL(delegate, OnPointerEnter(surface.get(), gfx::PointF(), 0)); generator.MoveMouseTo(surface->window()->GetBoundsInScreen().origin()); EXPECT_CALL(delegate, OnPointerButton(testing::_, ui::EF_LEFT_MOUSE_BUTTON, true)); EXPECT_CALL(delegate, OnPointerButton(testing::_, ui::EF_LEFT_MOUSE_BUTTON, false)); generator.ClickLeftButton(); EXPECT_CALL(delegate, OnPointerDestroying(pointer.get())); pointer.reset(); } TEST_F(PointerTest, OnPointerScroll) { std::unique_ptr surface(new Surface); std::unique_ptr shell_surface(new ShellSurface(surface.get())); gfx::Size buffer_size(10, 10); std::unique_ptr buffer( new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size))); surface->Attach(buffer.get()); surface->Commit(); MockPointerDelegate delegate; Seat seat; std::unique_ptr pointer(new Pointer(&delegate, &seat)); ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow()); gfx::Point location = surface->window()->GetBoundsInScreen().origin(); EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get())) .WillRepeatedly(testing::Return(true)); EXPECT_CALL(delegate, OnPointerFrame()).Times(3); EXPECT_CALL(delegate, OnPointerEnter(surface.get(), gfx::PointF(), 0)); generator.MoveMouseTo(location); { // Expect fling stop followed by scroll and scroll stop. testing::InSequence sequence; EXPECT_CALL(delegate, OnPointerScroll(testing::_, gfx::Vector2dF(1.2, 1.2), false)); EXPECT_CALL(delegate, OnPointerScrollStop(testing::_)); } generator.ScrollSequence(location, base::TimeDelta(), 1, 1, 1, 1); EXPECT_CALL(delegate, OnPointerDestroying(pointer.get())); pointer.reset(); } TEST_F(PointerTest, OnPointerScrollWithThreeFinger) { std::unique_ptr surface(new Surface); std::unique_ptr shell_surface(new ShellSurface(surface.get())); gfx::Size buffer_size(10, 10); std::unique_ptr buffer( new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size))); surface->Attach(buffer.get()); surface->Commit(); MockPointerDelegate delegate; Seat seat; std::unique_ptr pointer(new Pointer(&delegate, &seat)); ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow()); gfx::Point location = surface->window()->GetBoundsInScreen().origin(); EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get())) .WillRepeatedly(testing::Return(true)); EXPECT_CALL(delegate, OnPointerFrame()).Times(2); EXPECT_CALL(delegate, OnPointerEnter(surface.get(), gfx::PointF(), 0)); generator.MoveMouseTo(location); { // Expect no scroll. testing::InSequence sequence; EXPECT_CALL(delegate, OnPointerScrollStop(testing::_)); } // Three fingers scroll. generator.ScrollSequence(location, base::TimeDelta(), 1, 1, 1, 3 /* num_fingers */); EXPECT_CALL(delegate, OnPointerDestroying(pointer.get())); pointer.reset(); } TEST_F(PointerTest, OnPointerScrollDiscrete) { std::unique_ptr surface(new Surface); std::unique_ptr shell_surface(new ShellSurface(surface.get())); gfx::Size buffer_size(10, 10); std::unique_ptr buffer( new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size))); surface->Attach(buffer.get()); surface->Commit(); MockPointerDelegate delegate; Seat seat; std::unique_ptr pointer(new Pointer(&delegate, &seat)); ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow()); EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get())) .WillRepeatedly(testing::Return(true)); EXPECT_CALL(delegate, OnPointerFrame()).Times(2); EXPECT_CALL(delegate, OnPointerEnter(surface.get(), gfx::PointF(), 0)); generator.MoveMouseTo(surface->window()->GetBoundsInScreen().origin()); EXPECT_CALL(delegate, OnPointerScroll(testing::_, gfx::Vector2dF(1, 1), true)); generator.MoveMouseWheel(1, 1); EXPECT_CALL(delegate, OnPointerDestroying(pointer.get())); pointer.reset(); } TEST_F(PointerTest, RegisterPointerEventsOnModal) { // Create modal surface. std::unique_ptr surface(new Surface); std::unique_ptr shell_surface( new ShellSurface(surface.get(), gfx::Point(), true, false, ash::kShellWindowId_SystemModalContainer)); shell_surface->DisableMovement(); std::unique_ptr buffer( new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(gfx::Size(5, 5)))); surface->Attach(buffer.get()); surface->Commit(); ash::CenterWindow(shell_surface->GetWidget()->GetNativeWindow()); // Make the window modal. shell_surface->SetSystemModal(true); EXPECT_TRUE(ash::Shell::IsSystemModalWindowOpen()); MockPointerDelegate delegate; Seat seat; std::unique_ptr pointer(new Pointer(&delegate, &seat)); ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow()); EXPECT_CALL(delegate, OnPointerFrame()).Times(testing::AnyNumber()); EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get())) .WillRepeatedly(testing::Return(true)); // Pointer events on modal window should be registered. gfx::Point origin = surface->window()->GetBoundsInScreen().origin(); { testing::InSequence sequence; EXPECT_CALL(delegate, OnPointerEnter(surface.get(), gfx::PointF(), 0)); generator.MoveMouseTo(origin); EXPECT_CALL(delegate, OnPointerMotion(testing::_, gfx::PointF(1, 1))); generator.MoveMouseTo(origin + gfx::Vector2d(1, 1)); EXPECT_CALL(delegate, OnPointerButton(testing::_, ui::EF_LEFT_MOUSE_BUTTON, true)); EXPECT_CALL(delegate, OnPointerButton(testing::_, ui::EF_LEFT_MOUSE_BUTTON, false)); generator.ClickLeftButton(); EXPECT_CALL(delegate, OnPointerScroll(testing::_, gfx::Vector2dF(1.2, 1.2), false)); EXPECT_CALL(delegate, OnPointerScrollStop(testing::_)); generator.ScrollSequence(origin, base::TimeDelta(), 1, 1, 1, 1); } EXPECT_CALL(delegate, OnPointerDestroying(pointer.get())); pointer.reset(); } TEST_F(PointerTest, IgnorePointerEventsOnNonModalWhenModalIsOpen) { // Create surface for non-modal window. std::unique_ptr surface(new Surface); std::unique_ptr shell_surface(new ShellSurface(surface.get())); std::unique_ptr buffer( new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(gfx::Size(10, 10)))); surface->Attach(buffer.get()); surface->Commit(); // Create surface for modal window. std::unique_ptr surface2(new Surface); std::unique_ptr shell_surface2( new ShellSurface(surface2.get(), gfx::Point(), true, false, ash::kShellWindowId_SystemModalContainer)); shell_surface2->DisableMovement(); std::unique_ptr buffer2( new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(gfx::Size(5, 5)))); surface2->Attach(buffer2.get()); surface2->Commit(); ash::CenterWindow(shell_surface2->GetWidget()->GetNativeWindow()); // Make the window modal. shell_surface2->SetSystemModal(true); EXPECT_TRUE(ash::Shell::IsSystemModalWindowOpen()); MockPointerDelegate delegate; Seat seat; std::unique_ptr pointer(new Pointer(&delegate, &seat)); ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow()); EXPECT_CALL(delegate, OnPointerFrame()).Times(testing::AnyNumber()); EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get())) .WillRepeatedly(testing::Return(true)); EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface2.get())) .WillRepeatedly(testing::Return(true)); // Check if pointer events on non-modal window are ignored. gfx::Point nonModalOrigin = surface->window()->GetBoundsInScreen().origin(); { testing::InSequence sequence; EXPECT_CALL(delegate, OnPointerEnter(surface.get(), gfx::PointF(), 0)) .Times(0); generator.MoveMouseTo(nonModalOrigin); EXPECT_CALL(delegate, OnPointerMotion(testing::_, gfx::PointF(1, 1))) .Times(0); generator.MoveMouseTo(nonModalOrigin + gfx::Vector2d(1, 1)); EXPECT_CALL(delegate, OnPointerButton(testing::_, ui::EF_LEFT_MOUSE_BUTTON, true)) .Times(0); EXPECT_CALL(delegate, OnPointerButton(testing::_, ui::EF_LEFT_MOUSE_BUTTON, false)) .Times(0); generator.ClickLeftButton(); EXPECT_CALL(delegate, OnPointerScroll(testing::_, gfx::Vector2dF(1.2, 1.2), false)) .Times(0); EXPECT_CALL(delegate, OnPointerScrollStop(testing::_)).Times(0); generator.ScrollSequence(nonModalOrigin, base::TimeDelta(), 1, 1, 1, 1); EXPECT_CALL(delegate, OnPointerLeave(surface.get())).Times(0); generator.MoveMouseTo( surface->window()->GetBoundsInScreen().bottom_right()); } EXPECT_CALL(delegate, OnPointerDestroying(pointer.get())); pointer.reset(); } TEST_F(PointerTest, IgnorePointerLeaveOnModal) { // Create modal surface. std::unique_ptr surface(new Surface); std::unique_ptr shell_surface( new ShellSurface(surface.get(), gfx::Point(), true, false, ash::kShellWindowId_SystemModalContainer)); shell_surface->DisableMovement(); std::unique_ptr buffer( new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(gfx::Size(5, 5)))); surface->Attach(buffer.get()); surface->Commit(); ash::CenterWindow(shell_surface->GetWidget()->GetNativeWindow()); // Make the window modal. shell_surface->SetSystemModal(true); EXPECT_TRUE(ash::Shell::IsSystemModalWindowOpen()); MockPointerDelegate delegate; Seat seat; std::unique_ptr pointer(new Pointer(&delegate, &seat)); ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow()); EXPECT_CALL(delegate, OnPointerFrame()).Times(testing::AnyNumber()); EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get())) .WillRepeatedly(testing::Return(true)); gfx::Point origin = surface->window()->GetBoundsInScreen().origin(); { testing::InSequence sequence; EXPECT_CALL(delegate, OnPointerEnter(surface.get(), gfx::PointF(), 0)); generator.MoveMouseTo(origin); // OnPointerLeave should not be called on the modal surface when the pointer // moves out of its bounds. EXPECT_CALL(delegate, OnPointerLeave(surface.get())).Times(0); generator.MoveMouseTo( surface->window()->GetBoundsInScreen().bottom_right()); } EXPECT_CALL(delegate, OnPointerDestroying(pointer.get())); pointer.reset(); } TEST_F(PointerTest, RegisterPointerEventsOnNonModal) { // Create surface for non-modal window. std::unique_ptr surface(new Surface); std::unique_ptr shell_surface(new ShellSurface(surface.get())); std::unique_ptr buffer( new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(gfx::Size(10, 10)))); surface->Attach(buffer.get()); surface->Commit(); // Create another surface for a non-modal window. std::unique_ptr surface2(new Surface); std::unique_ptr shell_surface2( new ShellSurface(surface2.get(), gfx::Point(), true, false, ash::kShellWindowId_SystemModalContainer)); shell_surface2->DisableMovement(); std::unique_ptr buffer2( new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(gfx::Size(5, 5)))); surface2->Attach(buffer2.get()); surface2->Commit(); ash::CenterWindow(shell_surface2->GetWidget()->GetNativeWindow()); MockPointerDelegate delegate; Seat seat; std::unique_ptr pointer(new Pointer(&delegate, &seat)); ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow()); EXPECT_CALL(delegate, OnPointerFrame()).Times(testing::AnyNumber()); EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get())) .WillRepeatedly(testing::Return(true)); EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface2.get())) .WillRepeatedly(testing::Return(true)); // Ensure second window is non-modal. shell_surface2->SetSystemModal(false); EXPECT_FALSE(ash::Shell::IsSystemModalWindowOpen()); // Check if pointer events on first non-modal window are registered. gfx::Point firstWindowOrigin = surface->window()->GetBoundsInScreen().origin(); { testing::InSequence sequence; EXPECT_CALL(delegate, OnPointerEnter(surface.get(), gfx::PointF(), 0)); generator.MoveMouseTo(firstWindowOrigin); EXPECT_CALL(delegate, OnPointerMotion(testing::_, gfx::PointF(1, 1))); generator.MoveMouseTo(firstWindowOrigin + gfx::Vector2d(1, 1)); EXPECT_CALL(delegate, OnPointerButton(testing::_, ui::EF_LEFT_MOUSE_BUTTON, true)); EXPECT_CALL(delegate, OnPointerButton(testing::_, ui::EF_LEFT_MOUSE_BUTTON, false)); generator.ClickLeftButton(); EXPECT_CALL(delegate, OnPointerScroll(testing::_, gfx::Vector2dF(1.2, 1.2), false)); EXPECT_CALL(delegate, OnPointerScrollStop(testing::_)); generator.ScrollSequence(firstWindowOrigin, base::TimeDelta(), 1, 1, 1, 1); EXPECT_CALL(delegate, OnPointerLeave(surface.get())); generator.MoveMouseTo( surface->window()->GetBoundsInScreen().bottom_right()); } EXPECT_CALL(delegate, OnPointerDestroying(pointer.get())); pointer.reset(); } TEST_F(PointerTest, DragDropAbort) { Seat seat; MockPointerDelegate pointer_delegate; std::unique_ptr pointer(new Pointer(&pointer_delegate, &seat)); TestDataSourceDelegate data_source_delegate; DataSource source(&data_source_delegate); Surface origin, icon; // Make origin into a real window so the pointer can click it ShellSurface shell_surface(&origin); Buffer buffer(exo_test_helper()->CreateGpuMemoryBuffer(gfx::Size(10, 10))); origin.Attach(&buffer); origin.Commit(); ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow()); EXPECT_CALL(pointer_delegate, CanAcceptPointerEventsForSurface(&origin)) .WillRepeatedly(testing::Return(true)); EXPECT_CALL(pointer_delegate, OnPointerFrame()).Times(3); EXPECT_CALL(pointer_delegate, OnPointerEnter(&origin, gfx::PointF(), 0)); generator.MoveMouseTo(origin.window()->GetBoundsInScreen().origin()); seat.StartDrag(&source, &origin, &icon, ui::DragDropTypes::DragEventSource::DRAG_EVENT_SOURCE_MOUSE); EXPECT_TRUE(seat.get_drag_drop_operation_for_testing()); EXPECT_CALL(pointer_delegate, OnPointerButton).Times(2); generator.PressLeftButton(); EXPECT_TRUE(seat.get_drag_drop_operation_for_testing()); generator.ReleaseLeftButton(); EXPECT_FALSE(seat.get_drag_drop_operation_for_testing()); EXPECT_CALL(pointer_delegate, OnPointerDestroying(pointer.get())); pointer.reset(); } TEST_F(PointerTest, OnPointerRelativeMotion) { auto surface = std::make_unique(); auto shell_surface = std::make_unique(surface.get()); gfx::Size buffer_size(10, 10); std::unique_ptr buffer( new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size))); surface->Attach(buffer.get()); surface->Commit(); MockPointerDelegate delegate; MockRelativePointerDelegate relative_delegate; Seat seat; auto pointer = std::make_unique(&delegate, &seat); ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow()); pointer->RegisterRelativePointerDelegate(&relative_delegate); EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get())) .WillRepeatedly(testing::Return(true)); EXPECT_CALL(delegate, OnPointerFrame()).Times(9); EXPECT_CALL(delegate, OnPointerEnter(surface.get(), gfx::PointF(), 0)); generator.MoveMouseTo(surface->window()->GetBoundsInScreen().origin()); EXPECT_CALL(delegate, OnPointerMotion(testing::_, gfx::PointF(1, 1))); EXPECT_CALL(relative_delegate, OnPointerRelativeMotion(testing::_, gfx::PointF(1, 1))); generator.MoveMouseTo(surface->window()->GetBoundsInScreen().origin() + gfx::Vector2d(1, 1)); EXPECT_CALL(delegate, OnPointerMotion(testing::_, gfx::PointF(2, 2))); EXPECT_CALL(relative_delegate, OnPointerRelativeMotion(testing::_, gfx::PointF(1, 1))); generator.MoveMouseTo(surface->window()->GetBoundsInScreen().origin() + gfx::Vector2d(2, 2)); auto sub_surface = std::make_unique(); auto sub = std::make_unique(sub_surface.get(), surface.get()); surface->SetSubSurfacePosition(sub_surface.get(), gfx::Point(5, 5)); gfx::Size sub_buffer_size(5, 5); auto sub_buffer = std::make_unique( exo_test_helper()->CreateGpuMemoryBuffer(sub_buffer_size)); sub_surface->Attach(sub_buffer.get()); sub_surface->Commit(); surface->Commit(); EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(sub_surface.get())) .WillRepeatedly(testing::Return(true)); EXPECT_CALL(delegate, OnPointerLeave(surface.get())); EXPECT_CALL(delegate, OnPointerEnter(sub_surface.get(), gfx::PointF(), 0)); // OnPointerMotion will not be called, because the pointer location is already // sent with OnPointerEnter, but we should still receive // OnPointerRelativeMotion. EXPECT_CALL(relative_delegate, OnPointerRelativeMotion(testing::_, gfx::PointF(3, 3))); generator.MoveMouseTo(sub_surface->window()->GetBoundsInScreen().origin()); EXPECT_CALL(delegate, OnPointerMotion(testing::_, gfx::PointF(1, 1))); EXPECT_CALL(relative_delegate, OnPointerRelativeMotion(testing::_, gfx::PointF(1, 1))); generator.MoveMouseTo(sub_surface->window()->GetBoundsInScreen().origin() + gfx::Vector2d(1, 1)); const gfx::Point child_surface_origin = sub_surface->window()->GetBoundsInScreen().origin() + gfx::Vector2d(10, 10); auto child_surface = std::make_unique(); auto child_shell_surface = std::make_unique( child_surface.get(), child_surface_origin, true, false, ash::desks_util::GetActiveDeskContainerId()); child_shell_surface->DisableMovement(); child_shell_surface->SetParent(shell_surface.get()); gfx::Size child_buffer_size(15, 15); auto child_buffer = std::make_unique( exo_test_helper()->CreateGpuMemoryBuffer(child_buffer_size)); child_surface->Attach(child_buffer.get()); child_surface->Commit(); EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(child_surface.get())) .WillRepeatedly(testing::Return(true)); EXPECT_CALL(delegate, OnPointerLeave(sub_surface.get())); EXPECT_CALL(delegate, OnPointerEnter(child_surface.get(), gfx::PointF(), 0)); // OnPointerMotion will not be called, because the pointer location is already // sent with OnPointerEnter, but we should still receive // OnPointerRelativeMotion. EXPECT_CALL(relative_delegate, OnPointerRelativeMotion(testing::_, gfx::PointF(9, 9))); generator.MoveMouseTo(child_surface->window()->GetBoundsInScreen().origin()); EXPECT_CALL(delegate, OnPointerMotion(testing::_, gfx::PointF(10, 10))); EXPECT_CALL(relative_delegate, OnPointerRelativeMotion(testing::_, gfx::PointF(10, 10))); generator.MoveMouseTo(child_surface->window()->GetBoundsInScreen().origin() + gfx::Vector2d(10, 10)); EXPECT_CALL(delegate, OnPointerDestroying(pointer.get())); EXPECT_CALL(relative_delegate, OnPointerDestroying(pointer.get())); pointer.reset(); } #if defined(OS_CHROMEOS) TEST_F(PointerTest, ConstrainPointer) { auto scoped_feature_list = std::make_unique(); scoped_feature_list->InitAndEnableFeature( chromeos::features::kExoPointerLock); auto surface = std::make_unique(); auto shell_surface = std::make_unique(surface.get()); gfx::Size buffer_size(10, 10); auto buffer = std::make_unique( exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)); surface->Attach(buffer.get()); surface->Commit(); MockPointerDelegate delegate; MockPointerConstraintDelegate constraint_delegate; Seat seat; auto pointer = std::make_unique(&delegate, &seat); aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow()) ->FocusWindow(surface->window()); ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow()); EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get())) .WillRepeatedly(testing::Return(true)); EXPECT_CALL(constraint_delegate, GetConstrainedSurface()) .WillRepeatedly(testing::Return(surface.get())); EXPECT_TRUE(pointer->ConstrainPointer(&constraint_delegate)); EXPECT_CALL(delegate, OnPointerEnter(surface.get(), gfx::PointF(), 0)); EXPECT_CALL(delegate, OnPointerFrame()); generator.MoveMouseTo(surface->window()->GetBoundsInScreen().origin()); EXPECT_CALL(delegate, OnPointerMotion(testing::_, testing::_)).Times(0); generator.MoveMouseTo(surface->window()->GetBoundsInScreen().origin() + gfx::Vector2d(-1, -1)); auto child_surface = std::make_unique(); auto child_shell_surface = std::make_unique( child_surface.get(), gfx::Point(), true, false, ash::desks_util::GetActiveDeskContainerId()); child_shell_surface->DisableMovement(); child_shell_surface->SetParent(shell_surface.get()); gfx::Size child_buffer_size(15, 15); auto child_buffer = std::make_unique( exo_test_helper()->CreateGpuMemoryBuffer(child_buffer_size)); child_surface->Attach(child_buffer.get()); child_surface->Commit(); EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(child_surface.get())) .WillRepeatedly(testing::Return(true)); generator.MoveMouseTo(child_surface->window()->GetBoundsInScreen().origin()); EXPECT_CALL(delegate, OnPointerLeave(surface.get())); EXPECT_CALL(delegate, OnPointerEnter(child_surface.get(), gfx::PointF(), 0)); EXPECT_CALL(delegate, OnPointerFrame()); // Moving the cursor to a different surface should change the focus when // the pointer is unconstrained. pointer->UnconstrainPointer(); generator.MoveMouseTo(child_surface->window()->GetBoundsInScreen().origin()); EXPECT_CALL(delegate, OnPointerDestroying(pointer.get())); pointer.reset(); } #endif #if defined(OS_CHROMEOS) TEST_F(PointerTest, ConstrainPointerFailsWhenSurfaceIsNotFocused) { auto scoped_feature_list = std::make_unique(); scoped_feature_list->InitAndEnableFeature( chromeos::features::kExoPointerLock); auto surface = std::make_unique(); auto shell_surface = std::make_unique(surface.get()); gfx::Size buffer_size(10, 10); auto buffer = std::make_unique( exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)); surface->Attach(buffer.get()); surface->Commit(); MockPointerDelegate delegate; MockPointerConstraintDelegate constraint_delegate; Seat seat; auto pointer = std::make_unique(&delegate, &seat); aura::client::FocusClient* focus_client = aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow()); focus_client->FocusWindow(nullptr); ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow()); EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get())) .WillRepeatedly(testing::Return(true)); EXPECT_CALL(constraint_delegate, GetConstrainedSurface()) .WillRepeatedly(testing::Return(surface.get())); EXPECT_FALSE(pointer->ConstrainPointer(&constraint_delegate)); auto child_surface = std::make_unique(); auto child_shell_surface = std::make_unique( child_surface.get(), gfx::Point(), true, false, ash::desks_util::GetActiveDeskContainerId()); child_shell_surface->DisableMovement(); child_shell_surface->SetParent(shell_surface.get()); gfx::Size child_buffer_size(15, 15); auto child_buffer = std::make_unique( exo_test_helper()->CreateGpuMemoryBuffer(child_buffer_size)); child_surface->Attach(child_buffer.get()); child_surface->Commit(); EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(child_surface.get())) .WillRepeatedly(testing::Return(true)); focus_client->FocusWindow(child_surface->window()); EXPECT_FALSE(pointer->ConstrainPointer(&constraint_delegate)); focus_client->FocusWindow(surface->window()); EXPECT_TRUE(pointer->ConstrainPointer(&constraint_delegate)); EXPECT_CALL(delegate, OnPointerEnter(surface.get(), gfx::PointF(), 0)); EXPECT_CALL(delegate, OnPointerFrame()); generator.MoveMouseTo(surface->window()->GetBoundsInScreen().origin()); pointer->UnconstrainPointer(); EXPECT_CALL(delegate, OnPointerDestroying(pointer.get())); pointer.reset(); } #endif #if defined(OS_CHROMEOS) TEST_F(PointerTest, UnconstrainPointerWhenSurfaceIsDestroyed) { auto scoped_feature_list = std::make_unique(); scoped_feature_list->InitAndEnableFeature( chromeos::features::kExoPointerLock); auto surface = std::make_unique(); auto shell_surface = std::make_unique(surface.get()); gfx::Size buffer_size(10, 10); auto buffer = std::make_unique( exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)); surface->Attach(buffer.get()); surface->Commit(); MockPointerDelegate delegate; MockPointerConstraintDelegate constraint_delegate; Seat seat; auto pointer = std::make_unique(&delegate, &seat); aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow()) ->FocusWindow(surface->window()); ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow()); EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get())) .WillRepeatedly(testing::Return(true)); EXPECT_CALL(constraint_delegate, GetConstrainedSurface()) .WillRepeatedly(testing::Return(surface.get())); EXPECT_TRUE(pointer->ConstrainPointer(&constraint_delegate)); EXPECT_CALL(delegate, OnPointerEnter(surface.get(), gfx::PointF(), 0)); EXPECT_CALL(delegate, OnPointerFrame()); generator.MoveMouseTo(surface->window()->GetBoundsInScreen().origin()); // Constraint should be broken if surface is destroyed. EXPECT_CALL(constraint_delegate, OnConstraintBroken()); EXPECT_CALL(delegate, OnPointerLeave(surface.get())); EXPECT_CALL(delegate, OnPointerFrame()); pointer->OnSurfaceDestroying(surface.get()); EXPECT_CALL(delegate, OnPointerDestroying(pointer.get())); pointer.reset(); } #endif #if defined(OS_CHROMEOS) TEST_F(PointerTest, UnconstrainPointerWhenWindowLosesFocus) { auto scoped_feature_list = std::make_unique(); scoped_feature_list->InitAndEnableFeature( chromeos::features::kExoPointerLock); auto surface = std::make_unique(); auto shell_surface = std::make_unique(surface.get()); gfx::Size buffer_size(10, 10); auto buffer = std::make_unique( exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)); surface->Attach(buffer.get()); surface->Commit(); MockPointerDelegate delegate; MockPointerConstraintDelegate constraint_delegate; Seat seat; auto pointer = std::make_unique(&delegate, &seat); aura::client::FocusClient* focus_client = aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow()); focus_client->FocusWindow(surface->window()); ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow()); EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get())) .WillRepeatedly(testing::Return(true)); EXPECT_CALL(constraint_delegate, GetConstrainedSurface()) .WillRepeatedly(testing::Return(surface.get())); EXPECT_TRUE(pointer->ConstrainPointer(&constraint_delegate)); EXPECT_CALL(delegate, OnPointerEnter(surface.get(), gfx::PointF(), 0)); EXPECT_CALL(delegate, OnPointerFrame()); generator.MoveMouseTo(surface->window()->GetBoundsInScreen().origin()); EXPECT_CALL(constraint_delegate, OnConstraintBroken()); focus_client->FocusWindow(nullptr); EXPECT_CALL(delegate, OnPointerDestroying(pointer.get())); pointer.reset(); } #endif } // namespace } // namespace exo