diff options
Diffstat (limited to 'chromium/content/browser/media/capture/mouse_cursor_overlay_controller_browsertest.cc')
-rw-r--r-- | chromium/content/browser/media/capture/mouse_cursor_overlay_controller_browsertest.cc | 260 |
1 files changed, 260 insertions, 0 deletions
diff --git a/chromium/content/browser/media/capture/mouse_cursor_overlay_controller_browsertest.cc b/chromium/content/browser/media/capture/mouse_cursor_overlay_controller_browsertest.cc new file mode 100644 index 00000000000..5a05b17ff0b --- /dev/null +++ b/chromium/content/browser/media/capture/mouse_cursor_overlay_controller_browsertest.cc @@ -0,0 +1,260 @@ +// 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 "content/browser/media/capture/mouse_cursor_overlay_controller.h" + +#include "base/macros.h" +#include "base/run_loop.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/web_contents.h" +#include "content/public/test/content_browser_test.h" +#include "content/shell/browser/shell.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "ui/gfx/geometry/point_f.h" +#include "ui/gfx/geometry/rect_f.h" +#include "ui/gfx/geometry/size.h" +#include "ui/gfx/geometry/size_f.h" + +namespace content { +namespace { + +class FakeOverlay : public MouseCursorOverlayController::Overlay { + public: + FakeOverlay() = default; + ~FakeOverlay() final = default; + + const SkBitmap& image() const { return image_; } + const gfx::RectF& bounds() const { return bounds_; } + + void SetImageAndBounds(const SkBitmap& image, + const gfx::RectF& bounds) final { + image_ = image; + bounds_ = bounds; + } + + void SetBounds(const gfx::RectF& bounds) final { bounds_ = bounds; } + + private: + SkBitmap image_; + gfx::RectF bounds_; + + DISALLOW_COPY_AND_ASSIGN(FakeOverlay); +}; + +} // namespace + +class MouseCursorOverlayControllerBrowserTest : public ContentBrowserTest { + public: + MouseCursorOverlayControllerBrowserTest() = default; + ~MouseCursorOverlayControllerBrowserTest() override = default; + + void SetUpOnMainThread() final { + ContentBrowserTest::SetUpOnMainThread(); + controller_.SetTargetView(shell()->web_contents()->GetNativeView()); + controller_.DisconnectFromToolkitForTesting(); + base::RunLoop().RunUntilIdle(); + } + + FakeOverlay* Start() { + auto overlay_ptr = std::make_unique<FakeOverlay>(); + FakeOverlay* const overlay = overlay_ptr.get(); + DCHECK_CURRENTLY_ON(BrowserThread::UI); + controller_.Start(std::move(overlay_ptr), + BrowserThread::GetTaskRunnerForThread(BrowserThread::UI)); + return overlay; + } + + gfx::PointF GetLowerRightMostPointInsideView() { + const gfx::Size& view_size = GetAbsoluteViewSize(); + return gfx::PointF(1.0f - 1.0f / view_size.width(), + 1.0f - 1.0f / view_size.height()); + } + + void SimulateMouseTravel(float from_x, float from_y, float to_x, float to_y) { + constexpr int kNumMoves = 10; + for (int i = kNumMoves; i >= 0; --i) { + const float t = static_cast<float>(i) / kNumMoves; + const float x = t * from_x + (1.0f - t) * to_x; + const float y = t * from_y + (1.0f - t) * to_y; + controller_.OnMouseMoved(ToAbsoluteLocationInView(x, y)); + } + base::RunLoop().RunUntilIdle(); + } + + void SimulateMouseClick(float x, float y) { + controller_.OnMouseClicked(ToAbsoluteLocationInView(x, y)); + base::RunLoop().RunUntilIdle(); + } + + void SimulateMouseHasGoneIdle() { + controller_.OnMouseHasGoneIdle(); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(controller_.mouse_activity_ended_timer_.IsRunning()); + } + + void SimulateUnintentionalMouseMovement(float x, float y) { + const gfx::Size& view_size = GetAbsoluteViewSize(); + const float distance_x = + (0.5f * MouseCursorOverlayController::kMinMovementPixels) / + view_size.width(); + const float distance_y = + (0.5f * MouseCursorOverlayController::kMinMovementPixels) / + view_size.height(); + DoSquareDance(x, y, distance_x, distance_y); + } + + void SimulateBarelyIntentionalMouseMovement(float x, float y) { + const gfx::Size& view_size = GetAbsoluteViewSize(); + const float distance_x = + (1.5f * MouseCursorOverlayController::kMinMovementPixels) / + view_size.width(); + const float distance_y = + (1.5f * MouseCursorOverlayController::kMinMovementPixels) / + view_size.height(); + DoSquareDance(x, y, distance_x, distance_y); + } + + void ExpectOverlayPositionedAt(const FakeOverlay& overlay, + float expected_x, + float expected_y) { + const gfx::SizeF& overlay_size = GetExpectedOverlaySize(); + // The position will be slightly off because of the hotspot offset. + EXPECT_NEAR(expected_x, overlay.bounds().x(), overlay_size.width() / 2.0f); + EXPECT_NEAR(expected_y, overlay.bounds().y(), overlay_size.height() / 2.0f); + } + + void ExpectOverlaySizeMatchesCurrentCursor(const FakeOverlay& overlay) const { + const gfx::SizeF& expected_size = GetExpectedOverlaySize(); + EXPECT_FALSE(expected_size.IsEmpty()); + EXPECT_FALSE(overlay.image().drawsNothing()); + EXPECT_NEAR(expected_size.width(), overlay.bounds().width(), 0.001); + EXPECT_NEAR(expected_size.height(), overlay.bounds().height(), 0.001); + } + + bool IsUserInteractingWithView() const { + return controller_.IsUserInteractingWithView(); + } + + private: + gfx::Size GetAbsoluteViewSize() const { + const gfx::Size view_size = + shell()->web_contents()->GetContainerBounds().size(); + CHECK(!view_size.IsEmpty()); + return view_size; + } + + gfx::PointF ToAbsoluteLocationInView(float relative_x, float relative_y) { + const gfx::Size& view_size = GetAbsoluteViewSize(); + return gfx::PointF(relative_x * view_size.width(), + relative_y * view_size.height()); + } + + gfx::SizeF GetExpectedOverlaySize() const { + const gfx::Size& view_size = GetAbsoluteViewSize(); + const SkBitmap image = + controller_.GetCursorImage(controller_.GetCurrentCursorOrDefault()); + return gfx::SizeF(static_cast<float>(image.width()) / view_size.width(), + static_cast<float>(image.height()) / view_size.height()); + } + + void DoSquareDance(float x, float y, float distance_x, float distance_y) { + SimulateMouseTravel(x, y, x + distance_x, y); + SimulateMouseTravel(x + distance_x, y, x + distance_x, y + distance_y); + SimulateMouseTravel(x + distance_x, y + distance_y, x, y + distance_y); + SimulateMouseTravel(x, y + distance_y, x, y); + base::RunLoop().RunUntilIdle(); + } + + MouseCursorOverlayController controller_; + + DISALLOW_COPY_AND_ASSIGN(MouseCursorOverlayControllerBrowserTest); +}; + +IN_PROC_BROWSER_TEST_F(MouseCursorOverlayControllerBrowserTest, + PositionsOverlayOnMouseMoves) { + FakeOverlay* const overlay = Start(); + + // Cursor not showing at start. + EXPECT_TRUE(overlay->image().drawsNothing()); + EXPECT_TRUE(overlay->bounds().IsEmpty()); + EXPECT_FALSE(IsUserInteractingWithView()); + + // Move to upper-leftmost corner. + { + SCOPED_TRACE(testing::Message() << "upper-leftmost corner of view"); + SimulateMouseTravel(0.5f, 0.5f, 0.0f, 0.0f); + ExpectOverlayPositionedAt(*overlay, 0.0f, 0.0f); + ExpectOverlaySizeMatchesCurrentCursor(*overlay); + EXPECT_TRUE(IsUserInteractingWithView()); + } + + // Move to middle. + { + SCOPED_TRACE(testing::Message() << "center of view"); + SimulateMouseTravel(0.0f, 0.0f, 0.5f, 0.5f); + ExpectOverlayPositionedAt(*overlay, 0.5f, 0.5f); + ExpectOverlaySizeMatchesCurrentCursor(*overlay); + EXPECT_TRUE(IsUserInteractingWithView()); + } + + // Move to lower-rightmost corner. + { + SCOPED_TRACE(testing::Message() << "lower-rightmost corner of view"); + const gfx::PointF lower_right = GetLowerRightMostPointInsideView(); + SimulateMouseTravel(0.5f, 0.5f, lower_right.x(), lower_right.y()); + ExpectOverlayPositionedAt(*overlay, lower_right.x(), lower_right.y()); + ExpectOverlaySizeMatchesCurrentCursor(*overlay); + EXPECT_TRUE(IsUserInteractingWithView()); + } +} + +IN_PROC_BROWSER_TEST_F(MouseCursorOverlayControllerBrowserTest, + PositionsOverlayOnMouseClicks) { + FakeOverlay* const overlay = Start(); + + // Cursor not showing at start. + EXPECT_TRUE(overlay->bounds().IsEmpty()); + EXPECT_FALSE(IsUserInteractingWithView()); + + // Click in the middle of the view. + SimulateMouseClick(0.5f, 0.5f); + ExpectOverlayPositionedAt(*overlay, 0.5f, 0.5f); + ExpectOverlaySizeMatchesCurrentCursor(*overlay); + EXPECT_TRUE(IsUserInteractingWithView()); +} + +IN_PROC_BROWSER_TEST_F(MouseCursorOverlayControllerBrowserTest, + CursorHidesWhenMouseStopsMoving) { + FakeOverlay* const overlay = Start(); + + // Cursor not showing at start. + EXPECT_TRUE(overlay->bounds().IsEmpty()); + EXPECT_FALSE(IsUserInteractingWithView()); + + // Move to middle. + SimulateMouseTravel(0.0f, 0.0f, 0.5f, 0.5f); + ExpectOverlayPositionedAt(*overlay, 0.5f, 0.5f); + ExpectOverlaySizeMatchesCurrentCursor(*overlay); + EXPECT_TRUE(IsUserInteractingWithView()); + + // Simulate no movement for the timeout period. + SimulateMouseHasGoneIdle(); + EXPECT_TRUE(overlay->bounds().IsEmpty()); + EXPECT_FALSE(IsUserInteractingWithView()); + + // Move the mouse a little, but not enough to trip the "intentionally moved" + // logic. + SimulateUnintentionalMouseMovement(0.5f, 0.5f); + EXPECT_TRUE(overlay->bounds().IsEmpty()); + EXPECT_FALSE(IsUserInteractingWithView()); + + // Move the mouse just a bit more, to trip the "intentionally moved" logic. + SimulateBarelyIntentionalMouseMovement(0.5f, 0.5f); + ExpectOverlayPositionedAt(*overlay, 0.5f, 0.5f); + ExpectOverlaySizeMatchesCurrentCursor(*overlay); + EXPECT_TRUE(IsUserInteractingWithView()); +} + +} // namespace content |