#include "cc/input/single_scrollbar_animation_controller_thinning.h"
#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "cc/layers/solid_color_scrollbar_layer_impl.h"
#include "cc/test/layer_tree_impl_test_base.h"
#include "cc/trees/layer_tree_impl.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/test/geometry_util.h"
using ::testing::_;
using ::testing::Bool;
using ::testing::Mock;
using ::testing::NiceMock;
namespace cc {
namespace {
const float kIdleThicknessScale =
SingleScrollbarAnimationControllerThinning::kIdleThicknessScale;
class MockSingleScrollbarAnimationControllerClient
: public ScrollbarAnimationControllerClient {
public:
MockSingleScrollbarAnimationControllerClient(LayerTreeHostImpl* host_impl,
bool is_fluent)
: host_impl_(host_impl), is_fluent_(is_fluent) {}
~MockSingleScrollbarAnimationControllerClient() override = default;
ScrollbarSet ScrollbarsFor(ElementId scroll_element_id) const override {
return host_impl_->ScrollbarsFor(scroll_element_id);
}
bool IsFluentScrollbar() const override { return is_fluent_; }
MOCK_METHOD2(PostDelayedScrollbarAnimationTask,
void(base::OnceClosure start_fade, base::TimeDelta delay));
MOCK_METHOD0(SetNeedsRedrawForScrollbarAnimation, void());
MOCK_METHOD0(SetNeedsAnimateForScrollbarAnimation, void());
MOCK_METHOD0(DidChangeScrollbarVisibility, void());
private:
raw_ptr<LayerTreeHostImpl> host_impl_;
bool is_fluent_;
};
class SingleScrollbarAnimationControllerThinningTest
: public LayerTreeImplTestBase,
public testing::Test,
public testing::WithParamInterface<bool> {
public:
explicit SingleScrollbarAnimationControllerThinningTest(
bool is_fluent = GetParam())
: client_(host_impl(), is_fluent) {}
protected:
const base::TimeDelta kThinningDuration = base::Seconds(2);
const int kThumbThickness = 10;
void SetUp() override {
root_layer()->SetBounds(gfx::Size(100, 100));
auto* scroll_layer = AddLayer<LayerImpl>();
scroll_layer->SetBounds(gfx::Size(200, 200));
scroll_layer->SetElementId(
LayerIdToElementIdForTesting(scroll_layer->id()));
const int kTrackStart = 0;
const int kTrackLength = 100;
const bool kIsLeftSideVerticalScrollbar = false;
scrollbar_layer_ = AddLayer<SolidColorScrollbarLayerImpl>(
ScrollbarOrientation::VERTICAL, kThumbThickness, kTrackStart,
kIsLeftSideVerticalScrollbar);
scrollbar_layer_->SetBounds(gfx::Size(kThumbThickness, kTrackLength));
scrollbar_layer_->SetScrollElementId(scroll_layer->element_id());
CopyProperties(root_layer(), scroll_layer);
CreateTransformNode(scroll_layer);
CreateScrollNode(scroll_layer, gfx::Size(100, 100));
CopyProperties(scroll_layer, scrollbar_layer_);
scrollbar_layer_->SetOffsetToTransformParent(gfx::Vector2dF(90, 0));
CreateEffectNode(scrollbar_layer_).has_potential_opacity_animation = true;
UpdateActiveTreeDrawProperties();
scrollbar_controller_ = SingleScrollbarAnimationControllerThinning::Create(
scroll_layer->element_id(), ScrollbarOrientation::VERTICAL, &client_,
kThinningDuration);
mouse_move_distance_to_trigger_fade_in_ =
scrollbar_controller_->MouseMoveDistanceToTriggerFadeIn();
mouse_move_distance_to_trigger_expand_ =
scrollbar_controller_->MouseMoveDistanceToTriggerExpand();
}
float mouse_move_distance_to_trigger_fade_in_;
float mouse_move_distance_to_trigger_expand_;
std::unique_ptr<SingleScrollbarAnimationControllerThinning>
scrollbar_controller_;
raw_ptr<SolidColorScrollbarLayerImpl> scrollbar_layer_;
NiceMock<MockSingleScrollbarAnimationControllerClient> client_;
};
gfx::PointF NearScrollbar(float offset_x, float offset_y) {
gfx::PointF p(90, 0);
p.Offset(offset_x, offset_y);
return p;
}
class SingleScrollbarAnimationControllerThinningFluentTest
: public SingleScrollbarAnimationControllerThinningTest {
public:
SingleScrollbarAnimationControllerThinningFluentTest()
: SingleScrollbarAnimationControllerThinningTest( true) {}
};
TEST_P(SingleScrollbarAnimationControllerThinningTest, Idle) {
EXPECT_FLOAT_EQ(kIdleThicknessScale,
scrollbar_layer_->thumb_thickness_scale_factor());
}
TEST_P(SingleScrollbarAnimationControllerThinningTest, MouseNear) {
base::TimeTicks time;
time += base::Seconds(1);
scrollbar_controller_->DidMouseMove(
NearScrollbar(-mouse_move_distance_to_trigger_expand_, 0));
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(kIdleThicknessScale,
scrollbar_layer_->thumb_thickness_scale_factor());
time += kThinningDuration;
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor());
scrollbar_controller_->DidMouseMove(
NearScrollbar(-mouse_move_distance_to_trigger_expand_ + 1, 0));
scrollbar_controller_->Animate(time);
time += base::Seconds(10);
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor());
scrollbar_controller_->DidMouseMove(
NearScrollbar(-mouse_move_distance_to_trigger_expand_ - 1, 0));
scrollbar_controller_->Animate(time);
time += kThinningDuration;
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(kIdleThicknessScale,
scrollbar_layer_->thumb_thickness_scale_factor());
scrollbar_controller_->DidMouseMove(
NearScrollbar(-mouse_move_distance_to_trigger_fade_in_ - 1, 0));
scrollbar_controller_->Animate(time);
time += kThinningDuration;
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(kIdleThicknessScale,
scrollbar_layer_->thumb_thickness_scale_factor());
}
TEST_P(SingleScrollbarAnimationControllerThinningTest, MouseOver) {
base::TimeTicks time;
time += base::Seconds(1);
scrollbar_controller_->DidMouseMove(NearScrollbar(0, 0));
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(kIdleThicknessScale,
scrollbar_layer_->thumb_thickness_scale_factor());
time += kThinningDuration;
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor());
scrollbar_controller_->DidMouseMove(NearScrollbar(0, 0));
scrollbar_controller_->Animate(time);
time += base::Seconds(10);
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor());
scrollbar_controller_->DidMouseMove(
NearScrollbar(-mouse_move_distance_to_trigger_expand_, 0));
scrollbar_controller_->Animate(time);
time += base::Seconds(10);
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor());
scrollbar_controller_->DidMouseMove(
NearScrollbar(-mouse_move_distance_to_trigger_expand_ - 1, 0));
scrollbar_controller_->Animate(time);
time += kThinningDuration;
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(kIdleThicknessScale,
scrollbar_layer_->thumb_thickness_scale_factor());
}
TEST_P(SingleScrollbarAnimationControllerThinningTest,
MouseNearThenAwayWhileAnimating) {
base::TimeTicks time;
time += base::Seconds(1);
scrollbar_controller_->DidMouseMove(NearScrollbar(0, 0));
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(kIdleThicknessScale,
scrollbar_layer_->thumb_thickness_scale_factor());
time += kThinningDuration;
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor());
time += base::Seconds(1);
scrollbar_controller_->DidMouseLeave();
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor());
time += kThinningDuration / 2;
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(1.0f - (1.0f - kIdleThicknessScale) / 2.0f,
scrollbar_layer_->thumb_thickness_scale_factor());
scrollbar_controller_->DidMouseMove(
NearScrollbar(-mouse_move_distance_to_trigger_expand_, 0));
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(1.0f - (1.0f - kIdleThicknessScale) / 2.0f,
scrollbar_layer_->thumb_thickness_scale_factor());
time += kThinningDuration / 4;
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(1.0f - (1.0f - kIdleThicknessScale) / 2.0f,
scrollbar_layer_->thumb_thickness_scale_factor());
time += kThinningDuration / 4;
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(1.0f - (1.0f - kIdleThicknessScale) / 2.0f,
scrollbar_layer_->thumb_thickness_scale_factor());
time += kThinningDuration / 4;
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(kIdleThicknessScale + 3 * (1.0f - kIdleThicknessScale) / 4.0f,
scrollbar_layer_->thumb_thickness_scale_factor());
time += kThinningDuration / 4;
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor());
}
TEST_P(SingleScrollbarAnimationControllerThinningTest,
MouseCaptureAndReleaseOutOfBar) {
base::TimeTicks time;
time += base::Seconds(1);
scrollbar_controller_->DidMouseMove(NearScrollbar(0, 0));
scrollbar_controller_->Animate(time);
time += kThinningDuration;
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor());
scrollbar_controller_->DidMouseDown();
time += base::Seconds(1);
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor());
time += base::Seconds(10);
scrollbar_controller_->Animate(time);
scrollbar_controller_->DidMouseMove(
NearScrollbar(-mouse_move_distance_to_trigger_expand_ - 1, 0));
time += kThinningDuration;
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor());
scrollbar_controller_->DidMouseUp();
time += base::Seconds(1);
scrollbar_controller_->Animate(time);
time += kThinningDuration;
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(kIdleThicknessScale,
scrollbar_layer_->thumb_thickness_scale_factor());
}
TEST_P(SingleScrollbarAnimationControllerThinningTest,
MouseCaptureAndReleaseOnBar) {
base::TimeTicks time;
time += base::Seconds(1);
scrollbar_controller_->DidMouseMove(NearScrollbar(0, 0));
scrollbar_controller_->Animate(time);
time += kThinningDuration;
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor());
scrollbar_controller_->DidMouseDown();
time += base::Seconds(1);
scrollbar_controller_->Animate(time);
time += base::Seconds(10);
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor());
scrollbar_controller_->DidMouseMove(
NearScrollbar(mouse_move_distance_to_trigger_expand_ + 1, 0));
time += base::Seconds(1);
scrollbar_controller_->Animate(time);
time += base::Seconds(10);
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor());
scrollbar_controller_->DidMouseMove(
NearScrollbar(-mouse_move_distance_to_trigger_expand_, 0));
scrollbar_controller_->DidMouseUp();
time += base::Seconds(1);
scrollbar_controller_->Animate(time);
time += base::Seconds(10);
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor());
}
TEST_P(SingleScrollbarAnimationControllerThinningTest, ThicknessAnimated) {
base::TimeTicks time;
time += base::Seconds(1);
scrollbar_controller_->DidMouseMove(
NearScrollbar(-mouse_move_distance_to_trigger_expand_, 0));
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(kIdleThicknessScale,
scrollbar_layer_->thumb_thickness_scale_factor());
time += kThinningDuration / 2;
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(kIdleThicknessScale + (1.0f - kIdleThicknessScale) / 2.0f,
scrollbar_layer_->thumb_thickness_scale_factor());
time += kThinningDuration / 2;
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor());
time += base::Seconds(1);
scrollbar_controller_->DidMouseMove(
NearScrollbar(-mouse_move_distance_to_trigger_expand_ - 1, 0));
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor());
time += kThinningDuration / 2;
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(1.0f - (1.0f - kIdleThicknessScale) / 2.0f,
scrollbar_layer_->thumb_thickness_scale_factor());
time += kThinningDuration / 2;
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(kIdleThicknessScale,
scrollbar_layer_->thumb_thickness_scale_factor());
}
TEST_F(SingleScrollbarAnimationControllerThinningFluentTest,
FluentMouseOverTrack) {
base::TimeTicks time;
time += base::Seconds(1);
scrollbar_controller_->DidMouseMove(NearScrollbar(0, 75));
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(kIdleThicknessScale,
scrollbar_layer_->thumb_thickness_scale_factor());
time += kThinningDuration;
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor());
scrollbar_controller_->DidMouseMove(NearScrollbar(0, 75));
scrollbar_controller_->Animate(time);
time += base::Seconds(10);
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor());
scrollbar_controller_->DidMouseMove(NearScrollbar(-1, 75));
scrollbar_controller_->Animate(time);
time += kThinningDuration;
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(kIdleThicknessScale,
scrollbar_layer_->thumb_thickness_scale_factor());
scrollbar_controller_->DidMouseMove(NearScrollbar(0, 75));
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(kIdleThicknessScale,
scrollbar_layer_->thumb_thickness_scale_factor());
time += kThinningDuration;
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor());
scrollbar_controller_->DidMouseMove(NearScrollbar(kThumbThickness + 1, 75));
scrollbar_controller_->Animate(time);
time += kThinningDuration;
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(kIdleThicknessScale,
scrollbar_layer_->thumb_thickness_scale_factor());
}
TEST_P(SingleScrollbarAnimationControllerThinningTest,
HoverTrackAndMoveThumbUnderPointer) {
EXPECT_POINTF_EQ(
gfx::PointF(-1, -1),
scrollbar_controller_->device_viewport_last_pointer_location());
gfx::PointF near_scrollbar = NearScrollbar(0, 90);
scrollbar_controller_->DidMouseMove(near_scrollbar);
EXPECT_POINTF_EQ(
near_scrollbar,
scrollbar_controller_->device_viewport_last_pointer_location());
EXPECT_FALSE(scrollbar_controller_->mouse_is_near_scrollbar_thumb());
EXPECT_FALSE(scrollbar_controller_->mouse_is_over_scrollbar_thumb());
EXPECT_TRUE(scrollbar_controller_->mouse_is_near_scrollbar_track());
scrollbar_controller_->DidMouseDown();
EXPECT_FALSE(scrollbar_controller_->captured());
EXPECT_TRUE(scrollbar_layer_->SetCurrentPos(100));
scrollbar_controller_->DidScrollUpdate();
EXPECT_TRUE(scrollbar_controller_->mouse_is_near_scrollbar_thumb());
EXPECT_TRUE(scrollbar_controller_->mouse_is_over_scrollbar_thumb());
EXPECT_TRUE(scrollbar_controller_->mouse_is_near_scrollbar_track());
scrollbar_controller_->DidMouseDown();
EXPECT_TRUE(scrollbar_controller_->captured());
}
TEST_P(SingleScrollbarAnimationControllerThinningTest,
DidScrollUpdateQueuesAnimations) {
if (client_.IsFluentScrollbar())
return;
base::TimeTicks time;
time += base::Seconds(1);
scrollbar_controller_->DidMouseMove(NearScrollbar(0, 90));
EXPECT_FALSE(scrollbar_controller_->Animate(time));
EXPECT_FLOAT_EQ(kIdleThicknessScale,
scrollbar_layer_->thumb_thickness_scale_factor());
EXPECT_TRUE(scrollbar_layer_->SetCurrentPos(100));
scrollbar_controller_->DidScrollUpdate();
EXPECT_TRUE(scrollbar_controller_->Animate(time));
time += kThinningDuration;
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor());
EXPECT_TRUE(scrollbar_layer_->SetCurrentPos(0));
scrollbar_controller_->DidScrollUpdate();
scrollbar_controller_->Animate(time);
time += kThinningDuration;
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(kIdleThicknessScale,
scrollbar_layer_->thumb_thickness_scale_factor());
}
INSTANTIATE_TEST_SUITE_P(All,
SingleScrollbarAnimationControllerThinningTest,
Bool());
}
}