blob: 12e17d7152d8ff5b0d54804f7fcdd89bbd2d638d [file] [log] [blame]
// 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 "platform/scroll/ScrollableArea.h"
#include "platform/graphics/Color.h"
#include "platform/graphics/GraphicsLayer.h"
#include "platform/scroll/ScrollbarTestSuite.h"
#include "platform/scroll/ScrollbarTheme.h"
#include "platform/scroll/ScrollbarThemeMock.h"
#include "platform/testing/FakeGraphicsLayer.h"
#include "platform/testing/FakeGraphicsLayerClient.h"
#include "platform/testing/TestingPlatformSupport.h"
#include "public/platform/Platform.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace blink {
namespace {
using testing::_;
using testing::Return;
class ScrollbarThemeWithMockInvalidation : public ScrollbarThemeMock {
public:
MOCK_CONST_METHOD0(ShouldRepaintAllPartsOnInvalidation, bool());
MOCK_CONST_METHOD3(InvalidateOnThumbPositionChange,
ScrollbarPart(const ScrollbarThemeClient&, float, float));
};
} // namespace
using ScrollableAreaTest = testing::Test;
TEST_F(ScrollableAreaTest, ScrollAnimatorCurrentPositionShouldBeSync) {
ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
platform;
MockScrollableArea* scrollable_area =
MockScrollableArea::Create(ScrollOffset(0, 100));
scrollable_area->SetScrollOffset(ScrollOffset(0, 10000), kCompositorScroll);
EXPECT_EQ(100.0,
scrollable_area->GetScrollAnimator().CurrentOffset().Height());
}
TEST_F(ScrollableAreaTest, ScrollbarTrackAndThumbRepaint) {
ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
platform;
ScrollbarThemeWithMockInvalidation theme;
MockScrollableArea* scrollable_area =
MockScrollableArea::Create(ScrollOffset(0, 100));
Scrollbar* scrollbar = Scrollbar::CreateForTesting(
scrollable_area, kHorizontalScrollbar, kRegularScrollbar, &theme);
EXPECT_CALL(theme, ShouldRepaintAllPartsOnInvalidation())
.WillRepeatedly(Return(true));
EXPECT_TRUE(scrollbar->TrackNeedsRepaint());
EXPECT_TRUE(scrollbar->ThumbNeedsRepaint());
scrollbar->SetNeedsPaintInvalidation(kNoPart);
EXPECT_TRUE(scrollbar->TrackNeedsRepaint());
EXPECT_TRUE(scrollbar->ThumbNeedsRepaint());
scrollbar->ClearTrackNeedsRepaint();
scrollbar->ClearThumbNeedsRepaint();
EXPECT_FALSE(scrollbar->TrackNeedsRepaint());
EXPECT_FALSE(scrollbar->ThumbNeedsRepaint());
scrollbar->SetNeedsPaintInvalidation(kThumbPart);
EXPECT_TRUE(scrollbar->TrackNeedsRepaint());
EXPECT_TRUE(scrollbar->ThumbNeedsRepaint());
// When not all parts are repainted on invalidation,
// setNeedsPaintInvalidation sets repaint bits only on the requested parts.
EXPECT_CALL(theme, ShouldRepaintAllPartsOnInvalidation())
.WillRepeatedly(Return(false));
scrollbar->ClearTrackNeedsRepaint();
scrollbar->ClearThumbNeedsRepaint();
EXPECT_FALSE(scrollbar->TrackNeedsRepaint());
EXPECT_FALSE(scrollbar->ThumbNeedsRepaint());
scrollbar->SetNeedsPaintInvalidation(kThumbPart);
EXPECT_FALSE(scrollbar->TrackNeedsRepaint());
EXPECT_TRUE(scrollbar->ThumbNeedsRepaint());
// Forced GC in order to finalize objects depending on the mock object.
ThreadState::Current()->CollectAllGarbage();
}
TEST_F(ScrollableAreaTest, ScrollbarGraphicsLayerInvalidation) {
ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
platform;
ScrollbarTheme::SetMockScrollbarsEnabled(true);
MockScrollableArea* scrollable_area =
MockScrollableArea::Create(ScrollOffset(0, 100));
FakeGraphicsLayerClient graphics_layer_client;
graphics_layer_client.SetIsTrackingRasterInvalidations(true);
FakeGraphicsLayer graphics_layer(&graphics_layer_client);
graphics_layer.SetDrawsContent(true);
graphics_layer.SetSize(FloatSize(111, 222));
EXPECT_CALL(*scrollable_area, LayerForHorizontalScrollbar())
.WillRepeatedly(Return(&graphics_layer));
Scrollbar* scrollbar = Scrollbar::Create(
scrollable_area, kHorizontalScrollbar, kRegularScrollbar, nullptr);
graphics_layer.ResetTrackedRasterInvalidations();
scrollbar->SetNeedsPaintInvalidation(kNoPart);
EXPECT_TRUE(graphics_layer.HasTrackedRasterInvalidations());
// Forced GC in order to finalize objects depending on the mock object.
ThreadState::Current()->CollectAllGarbage();
}
TEST_F(ScrollableAreaTest, InvalidatesNonCompositedScrollbarsWhenThumbMoves) {
ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
platform;
ScrollbarThemeWithMockInvalidation theme;
MockScrollableArea* scrollable_area =
MockScrollableArea::Create(ScrollOffset(100, 100));
Scrollbar* horizontal_scrollbar = Scrollbar::CreateForTesting(
scrollable_area, kHorizontalScrollbar, kRegularScrollbar, &theme);
Scrollbar* vertical_scrollbar = Scrollbar::CreateForTesting(
scrollable_area, kVerticalScrollbar, kRegularScrollbar, &theme);
EXPECT_CALL(*scrollable_area, HorizontalScrollbar())
.WillRepeatedly(Return(horizontal_scrollbar));
EXPECT_CALL(*scrollable_area, VerticalScrollbar())
.WillRepeatedly(Return(vertical_scrollbar));
// Regardless of whether the theme invalidates any parts, non-composited
// scrollbars have to be repainted if the thumb moves.
EXPECT_CALL(*scrollable_area, LayerForHorizontalScrollbar())
.WillRepeatedly(Return(nullptr));
EXPECT_CALL(*scrollable_area, LayerForVerticalScrollbar())
.WillRepeatedly(Return(nullptr));
ASSERT_FALSE(scrollable_area->HasLayerForVerticalScrollbar());
ASSERT_FALSE(scrollable_area->HasLayerForHorizontalScrollbar());
EXPECT_CALL(theme, ShouldRepaintAllPartsOnInvalidation())
.WillRepeatedly(Return(false));
EXPECT_CALL(theme, InvalidateOnThumbPositionChange(_, _, _))
.WillRepeatedly(Return(kNoPart));
// A scroll in each direction should only invalidate one scrollbar.
scrollable_area->SetScrollOffset(ScrollOffset(0, 50), kProgrammaticScroll);
EXPECT_FALSE(scrollable_area->HorizontalScrollbarNeedsPaintInvalidation());
EXPECT_TRUE(scrollable_area->VerticalScrollbarNeedsPaintInvalidation());
scrollable_area->ClearNeedsPaintInvalidationForScrollControls();
scrollable_area->SetScrollOffset(ScrollOffset(50, 50), kProgrammaticScroll);
EXPECT_TRUE(scrollable_area->HorizontalScrollbarNeedsPaintInvalidation());
EXPECT_FALSE(scrollable_area->VerticalScrollbarNeedsPaintInvalidation());
scrollable_area->ClearNeedsPaintInvalidationForScrollControls();
// Forced GC in order to finalize objects depending on the mock object.
ThreadState::Current()->CollectAllGarbage();
}
TEST_F(ScrollableAreaTest, InvalidatesCompositedScrollbarsIfPartsNeedRepaint) {
ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
platform;
ScrollbarThemeWithMockInvalidation theme;
MockScrollableArea* scrollable_area =
MockScrollableArea::Create(ScrollOffset(100, 100));
Scrollbar* horizontal_scrollbar = Scrollbar::CreateForTesting(
scrollable_area, kHorizontalScrollbar, kRegularScrollbar, &theme);
horizontal_scrollbar->ClearTrackNeedsRepaint();
horizontal_scrollbar->ClearThumbNeedsRepaint();
Scrollbar* vertical_scrollbar = Scrollbar::CreateForTesting(
scrollable_area, kVerticalScrollbar, kRegularScrollbar, &theme);
vertical_scrollbar->ClearTrackNeedsRepaint();
vertical_scrollbar->ClearThumbNeedsRepaint();
EXPECT_CALL(*scrollable_area, HorizontalScrollbar())
.WillRepeatedly(Return(horizontal_scrollbar));
EXPECT_CALL(*scrollable_area, VerticalScrollbar())
.WillRepeatedly(Return(vertical_scrollbar));
// Composited scrollbars only need repainting when parts become invalid
// (e.g. if the track changes appearance when the thumb reaches the end).
FakeGraphicsLayerClient graphics_layer_client;
graphics_layer_client.SetIsTrackingRasterInvalidations(true);
FakeGraphicsLayer layer_for_horizontal_scrollbar(&graphics_layer_client);
layer_for_horizontal_scrollbar.SetDrawsContent(true);
layer_for_horizontal_scrollbar.SetSize(FloatSize(10, 10));
FakeGraphicsLayer layer_for_vertical_scrollbar(&graphics_layer_client);
layer_for_vertical_scrollbar.SetDrawsContent(true);
layer_for_vertical_scrollbar.SetSize(FloatSize(10, 10));
EXPECT_CALL(*scrollable_area, LayerForHorizontalScrollbar())
.WillRepeatedly(Return(&layer_for_horizontal_scrollbar));
EXPECT_CALL(*scrollable_area, LayerForVerticalScrollbar())
.WillRepeatedly(Return(&layer_for_vertical_scrollbar));
ASSERT_TRUE(scrollable_area->HasLayerForHorizontalScrollbar());
ASSERT_TRUE(scrollable_area->HasLayerForVerticalScrollbar());
EXPECT_CALL(theme, ShouldRepaintAllPartsOnInvalidation())
.WillRepeatedly(Return(false));
// First, we'll scroll horizontally, and the theme will require repainting
// the back button (i.e. the track).
EXPECT_CALL(theme, InvalidateOnThumbPositionChange(_, _, _))
.WillOnce(Return(kBackButtonStartPart));
scrollable_area->SetScrollOffset(ScrollOffset(50, 0), kProgrammaticScroll);
EXPECT_TRUE(layer_for_horizontal_scrollbar.HasTrackedRasterInvalidations());
EXPECT_FALSE(layer_for_vertical_scrollbar.HasTrackedRasterInvalidations());
EXPECT_TRUE(horizontal_scrollbar->TrackNeedsRepaint());
EXPECT_FALSE(horizontal_scrollbar->ThumbNeedsRepaint());
layer_for_horizontal_scrollbar.ResetTrackedRasterInvalidations();
horizontal_scrollbar->ClearTrackNeedsRepaint();
// Next, we'll scroll vertically, but invalidate the thumb.
EXPECT_CALL(theme, InvalidateOnThumbPositionChange(_, _, _))
.WillOnce(Return(kThumbPart));
scrollable_area->SetScrollOffset(ScrollOffset(50, 50), kProgrammaticScroll);
EXPECT_FALSE(layer_for_horizontal_scrollbar.HasTrackedRasterInvalidations());
EXPECT_TRUE(layer_for_vertical_scrollbar.HasTrackedRasterInvalidations());
EXPECT_FALSE(vertical_scrollbar->TrackNeedsRepaint());
EXPECT_TRUE(vertical_scrollbar->ThumbNeedsRepaint());
layer_for_vertical_scrollbar.ResetTrackedRasterInvalidations();
vertical_scrollbar->ClearThumbNeedsRepaint();
// Next we'll scroll in both, but the thumb position moving requires no
// invalidations. Nonetheless the GraphicsLayer should be invalidated,
// because we still need to update the underlying layer (though no
// rasterization will be required).
EXPECT_CALL(theme, InvalidateOnThumbPositionChange(_, _, _))
.Times(2)
.WillRepeatedly(Return(kNoPart));
scrollable_area->SetScrollOffset(ScrollOffset(70, 70), kProgrammaticScroll);
EXPECT_TRUE(layer_for_horizontal_scrollbar.HasTrackedRasterInvalidations());
EXPECT_TRUE(layer_for_vertical_scrollbar.HasTrackedRasterInvalidations());
EXPECT_FALSE(horizontal_scrollbar->TrackNeedsRepaint());
EXPECT_FALSE(horizontal_scrollbar->ThumbNeedsRepaint());
EXPECT_FALSE(vertical_scrollbar->TrackNeedsRepaint());
EXPECT_FALSE(vertical_scrollbar->ThumbNeedsRepaint());
// Forced GC in order to finalize objects depending on the mock object.
ThreadState::Current()->CollectAllGarbage();
}
TEST_F(ScrollableAreaTest, RecalculatesScrollbarOverlayIfBackgroundChanges) {
ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
platform;
MockScrollableArea* scrollable_area =
MockScrollableArea::Create(ScrollOffset(0, 100));
EXPECT_EQ(kScrollbarOverlayColorThemeDark,
scrollable_area->GetScrollbarOverlayColorTheme());
scrollable_area->RecalculateScrollbarOverlayColorTheme(Color(34, 85, 51));
EXPECT_EQ(kScrollbarOverlayColorThemeLight,
scrollable_area->GetScrollbarOverlayColorTheme());
scrollable_area->RecalculateScrollbarOverlayColorTheme(Color(236, 143, 185));
EXPECT_EQ(kScrollbarOverlayColorThemeDark,
scrollable_area->GetScrollbarOverlayColorTheme());
}
TEST_F(ScrollableAreaTest, ScrollableAreaDidScroll) {
ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
platform;
MockScrollableArea* scrollable_area =
MockScrollableArea::Create(ScrollOffset(100, 100));
scrollable_area->SetScrollOrigin(IntPoint(20, 30));
scrollable_area->DidScroll(gfx::ScrollOffset(40, 51));
// After calling didScroll, the new offset should account for scroll origin.
EXPECT_EQ(20, scrollable_area->ScrollOffsetInt().Width());
EXPECT_EQ(21, scrollable_area->ScrollOffsetInt().Height());
}
} // namespace blink