#include "cc/trees/layer_tree_host_impl.h"
#include <stddef.h>
#include <algorithm>
#include <array>
#include <cmath>
#include <memory>
#include <optional>
#include <utility>
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/memory_pressure_listener.h"
#include "base/memory/ptr_util.h"
#include "base/numerics/angle_conversions.h"
#include "base/run_loop.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/bind.h"
#include "base/test/gtest_util.h"
#include "base/test/simple_test_tick_clock.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "cc/animation/animation.h"
#include "cc/animation/animation_host.h"
#include "cc/animation/animation_id_provider.h"
#include "cc/base/features.h"
#include "cc/input/browser_controls_offset_manager.h"
#include "cc/input/input_handler.h"
#include "cc/input/main_thread_scrolling_reason.h"
#include "cc/input/page_scale_animation.h"
#include "cc/input/scroll_utils.h"
#include "cc/input/scrollbar_controller.h"
#include "cc/layers/append_quads_context.h"
#include "cc/layers/append_quads_data.h"
#include "cc/layers/layer_impl.h"
#include "cc/layers/nine_patch_thumb_scrollbar_layer_impl.h"
#include "cc/layers/painted_scrollbar_layer_impl.h"
#include "cc/layers/render_surface_impl.h"
#include "cc/layers/solid_color_layer_impl.h"
#include "cc/layers/solid_color_scrollbar_layer_impl.h"
#include "cc/layers/surface_layer_impl.h"
#include "cc/layers/video_layer_impl.h"
#include "cc/layers/viewport.h"
#include "cc/metrics/compositor_frame_reporting_controller.h"
#include "cc/metrics/frame_info.h"
#include "cc/resources/ui_resource_bitmap.h"
#include "cc/resources/ui_resource_manager.h"
#include "cc/test/animation_test_common.h"
#include "cc/test/fake_frame_info.h"
#include "cc/test/fake_impl_task_runner_provider.h"
#include "cc/test/fake_layer_tree_frame_sink.h"
#include "cc/test/fake_layer_tree_host_impl.h"
#include "cc/test/fake_picture_layer_impl.h"
#include "cc/test/fake_raster_source.h"
#include "cc/test/fake_recording_source.h"
#include "cc/test/fake_video_frame_provider.h"
#include "cc/test/layer_test_common.h"
#include "cc/test/layer_tree_test.h"
#include "cc/test/mock_latency_info_swap_promise_monitor.h"
#include "cc/test/skia_common.h"
#include "cc/test/test_layer_tree_frame_sink.h"
#include "cc/test/test_paint_worklet_layer_painter.h"
#include "cc/test/test_task_graph_runner.h"
#include "cc/trees/clip_node.h"
#include "cc/trees/compositor_commit_data.h"
#include "cc/trees/draw_property_utils.h"
#include "cc/trees/effect_node.h"
#include "cc/trees/frame_data.h"
#include "cc/trees/latency_info_swap_promise.h"
#include "cc/trees/layer_tree_host_impl_client.h"
#include "cc/trees/layer_tree_host_impl_test_base.h"
#include "cc/trees/layer_tree_impl.h"
#include "cc/trees/mutator_host.h"
#include "cc/trees/render_frame_metadata.h"
#include "cc/trees/render_frame_metadata_observer.h"
#include "cc/trees/scroll_node.h"
#include "cc/trees/transform_node.h"
#include "cc/view_transition/view_transition_request.h"
#include "components/viz/client/client_resource_provider.h"
#include "components/viz/common/frame_sinks/begin_frame_args.h"
#include "components/viz/common/frame_sinks/copy_output_request.h"
#include "components/viz/common/frame_sinks/copy_output_result.h"
#include "components/viz/common/frame_timing_details.h"
#include "components/viz/common/quads/compositor_frame_metadata.h"
#include "components/viz/common/quads/compositor_render_pass_draw_quad.h"
#include "components/viz/common/quads/solid_color_draw_quad.h"
#include "components/viz/common/quads/texture_draw_quad.h"
#include "components/viz/common/quads/tile_draw_quad.h"
#include "components/viz/common/surfaces/frame_sink_id.h"
#include "components/viz/common/surfaces/region_capture_bounds.h"
#include "components/viz/service/display/skia_output_surface.h"
#include "components/viz/test/begin_frame_args_test.h"
#include "components/viz/test/fake_output_surface.h"
#include "components/viz/test/fake_skia_output_surface.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "gpu/command_buffer/client/client_shared_image.h"
#include "media/base/media.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkMallocPixelRef.h"
#include "ui/events/types/scroll_input_type.h"
#include "ui/gfx/geometry/point_conversions.h"
#include "ui/gfx/geometry/size_conversions.h"
#include "ui/gfx/geometry/test/geometry_util.h"
#include "ui/gfx/geometry/transform_operations.h"
#include "ui/gfx/geometry/vector2d_conversions.h"
#include "ui/latency/latency_info.h"
#define EXPECT_SCOPED(statements) \
{ \
SCOPED_TRACE(""); \
statements; \
}
using media::VideoFrame;
using ::testing::_;
using ::testing::AnyNumber;
using ::testing::AtLeast;
using ::testing::ElementsAre;
using ::testing::Mock;
using ::testing::Pointee;
using ::testing::Pointer;
using ::testing::Pointwise;
using ::testing::Property;
using ::testing::Range;
using ::testing::Return;
using ::testing::StrictMock;
using ScrollThread = cc::InputHandler::ScrollThread;
namespace cc {
namespace {
viz::SurfaceId MakeSurfaceId(const viz::FrameSinkId& frame_sink_id,
uint32_t parent_id) {
return viz::SurfaceId(
frame_sink_id,
viz::LocalSurfaceId(parent_id,
base::UnguessableToken::CreateForTesting(0, 1u)));
}
void ClearMainThreadDeltasForTesting(LayerTreeHostImpl* host) {
host->active_tree()->ApplySentScrollAndScaleDeltasFromAbortedCommit(
false, false);
}
MATCHER(UniquePtrMatches, negation ? "do not match" : "match") {
return std::get<0>(arg).get() == std::get<1>(arg);
}
}
using CommitToPendingTreeLayerTreeHostImplTest = LayerTreeHostImplTestBase;
class CommitToActiveTreeLayerTreeHostImplTest
: public LayerTreeHostImplTestBase {
public:
LayerTreeSettings DefaultSettings() override {
LayerTreeSettings settings = LayerTreeHostImplTestBase::DefaultSettings();
settings.commit_to_active_tree = true;
return settings;
}
};
INSTANTIATE_COMMIT_TO_TREE_TEST_P(LayerTreeHostImplTest);
class PendingTreeLayerTreeHostImplTest : public LayerTreeHostImplTest {};
INSTANTIATE_COMMIT_TO_PENDING_TREE_TEST_P(PendingTreeLayerTreeHostImplTest);
class CompositorFrameProducingLayerTreeHostImplTest
: public LayerTreeHostImplTest {};
INSTANTIATE_COMPOSITOR_FRAME_PRODUCING_TREE_TEST_P(
CompositorFrameProducingLayerTreeHostImplTest);
class ClientModeLayerTreeHostImplTest : public LayerTreeHostImplTest {};
INSTANTIATE_CLIENT_MODE_TREE_TEST_P(ClientModeLayerTreeHostImplTest);
class OccludedSurfaceThrottlingLayerTreeHostImplTest
: public LayerTreeHostImplTest {
public:
LayerTreeSettings DefaultSettings() override {
LayerTreeSettings settings = LayerTreeHostImplTest::DefaultSettings();
settings.enable_compositing_based_throttling = true;
return settings;
}
};
INSTANTIATE_COMMIT_TO_TREE_TEST_P(
OccludedSurfaceThrottlingLayerTreeHostImplTest);
class LayerTreeHostImplTimelinesTest : public LayerTreeHostImplTest {
public:
void SetUp() override {
CreateHostImpl(DefaultSettings(), CreateLayerTreeFrameSink());
host_impl_->set_force_smooth_wheel_scrolling_for_testing(true);
}
};
INSTANTIATE_CLIENT_MODE_TREE_TEST_P(LayerTreeHostImplTimelinesTest);
class FluentOverlayScrollbarLayerTreeHostImplTest
: public CommitToPendingTreeLayerTreeHostImplTest {
public:
void SetUp() override {
LayerTreeSettings settings = DefaultSettings();
settings.enable_fluent_overlay_scrollbar = true;
settings.enable_fluent_scrollbar = true;
settings.scrollbar_animator = LayerTreeSettings::AURA_OVERLAY;
settings.scrollbar_fade_delay = base::Milliseconds(500);
settings.scrollbar_fade_duration = base::Milliseconds(300);
settings.idle_thickness_scale = 0.4f;
CreateHostImpl(settings, CreateLayerTreeFrameSink());
}
PaintedScrollbarLayerImpl* CreateAndRegisterPaintedScrollbarLayer() {
const gfx::Size viewport_size = gfx::Size(360, 600);
const gfx::Size content_size = gfx::Size(345, 3800);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
LayerImpl* scroll_layer = OuterViewportScrollLayer();
LayerTreeImpl* layer_tree_impl = host_impl_->active_tree();
auto* scrollbar = AddLayer<PaintedScrollbarLayerImpl>(
layer_tree_impl, ScrollbarOrientation::kVertical, false, true);
auto& effect_node = CreateEffectNode(
scrollbar, layer_tree_impl->root_layer()->effect_tree_index());
SetupScrollbarLayerCommon(scroll_layer, scrollbar);
scrollbar->SetEffectTreeIndex(effect_node.id);
scrollbar->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
scrollbar->SetBounds(gfx::Size(15, 600));
scrollbar->SetThumbThickness(9);
scrollbar->SetThumbLength(50);
scrollbar->SetTrackRect(gfx::Rect(0, 12, 15, 575));
scrollbar->SetForwardButtonRect(gfx::Rect(0, 584, 15, 16));
scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(345, 0));
UIResourceId ui_resource_id = 1;
UIResourceBitmap bitmap(gfx::Size(1, 1), true);
host_impl_->CreateUIResource(ui_resource_id, bitmap);
scrollbar->set_track_and_buttons_ui_resource_id(ui_resource_id);
scrollbar->SetThumbColor(SkColors::kRed);
scrollbar->set_is_web_test(true);
UpdateDrawProperties(host_impl_->active_tree());
return scrollbar;
}
};
class FluentOverlayScrollbarOpacityLayerTreeHostImplTest
: public FluentOverlayScrollbarLayerTreeHostImplTest,
public testing::WithParamInterface<int> {
public:
constexpr static int kParamSteps = 10;
void VerifyCorrectOpacityForThickness(PaintedScrollbarLayerImpl* scrollbar,
float thickness,
float expected_opacity) {
scrollbar->SetThumbThicknessScaleFactor(thickness);
auto render_pass = viz::CompositorRenderPass::Create();
AppendQuadsData append_quads_data;
scrollbar->AppendQuads(AppendQuadsContext{DRAW_MODE_HARDWARE, {}, false},
render_pass.get(), &append_quads_data);
if (expected_opacity == 0.f) {
EXPECT_EQ(render_pass->quad_list.size(), 1u);
} else {
EXPECT_EQ(render_pass->quad_list.size(), 2u);
EXPECT_FLOAT_EQ(expected_opacity,
render_pass->shared_quad_state_list.back()->opacity);
}
}
};
class TestInputHandlerClient : public InputHandlerClient {
public:
TestInputHandlerClient()
: page_scale_factor_(0),
min_page_scale_factor_(-1),
max_page_scale_factor_(-1) {}
~TestInputHandlerClient() override = default;
void WillShutdown() override {}
void Animate(base::TimeTicks time) override {}
void ReconcileElasticOverscrollAndRootScroll() override {}
void SetPrefersReducedMotion(bool prefers_reduced_motion) override {}
void UpdateRootLayerStateForSynchronousInputHandler(
const gfx::PointF& total_scroll_offset,
const gfx::PointF& max_scroll_offset,
const gfx::SizeF& scrollable_size,
float page_scale_factor,
float min_page_scale_factor,
float max_page_scale_factor) override {
DCHECK(total_scroll_offset.x() <= max_scroll_offset.x());
DCHECK(total_scroll_offset.y() <= max_scroll_offset.y());
last_set_scroll_offset_ = total_scroll_offset;
max_scroll_offset_ = max_scroll_offset;
scrollable_size_ = scrollable_size;
page_scale_factor_ = page_scale_factor;
min_page_scale_factor_ = min_page_scale_factor;
max_page_scale_factor_ = max_page_scale_factor;
}
void DeliverInputForBeginFrame(const viz::BeginFrameArgs& args) override {}
void DeliverInputForHighLatencyMode() override {}
void DeliverInputForDeadline() override {}
void DidFinishImplFrame() override {}
bool HasQueuedInput() const override { return false; }
void SetScrollEventDispatchMode(
InputHandlerClient::ScrollEventDispatchMode mode,
double scroll_deadline_ratio) override {}
gfx::PointF last_set_scroll_offset() { return last_set_scroll_offset_; }
gfx::PointF max_scroll_offset() const { return max_scroll_offset_; }
gfx::SizeF scrollable_size() const { return scrollable_size_; }
float page_scale_factor() const { return page_scale_factor_; }
float min_page_scale_factor() const { return min_page_scale_factor_; }
float max_page_scale_factor() const { return max_page_scale_factor_; }
private:
gfx::PointF last_set_scroll_offset_;
gfx::PointF max_scroll_offset_;
gfx::SizeF scrollable_size_;
float page_scale_factor_;
float min_page_scale_factor_;
float max_page_scale_factor_;
};
TEST_P(LayerTreeHostImplTest, LocalAndExternalPinchState) {
EXPECT_FALSE(GetInputHandler().pinch_gesture_active());
GetInputHandler().PinchGestureBegin(gfx::Point(),
ui::ScrollInputType::kTouchscreen);
EXPECT_TRUE(GetInputHandler().pinch_gesture_active());
GetInputHandler().PinchGestureEnd(gfx::Point());
EXPECT_FALSE(GetInputHandler().pinch_gesture_active());
GetInputHandler().set_external_pinch_gesture_active(true);
EXPECT_TRUE(GetInputHandler().pinch_gesture_active());
GetInputHandler().set_external_pinch_gesture_active(false);
EXPECT_FALSE(GetInputHandler().pinch_gesture_active());
GetInputHandler().PinchGestureBegin(gfx::Point(),
ui::ScrollInputType::kTouchscreen);
EXPECT_TRUE(GetInputHandler().pinch_gesture_active());
GetInputHandler().set_external_pinch_gesture_active(false);
EXPECT_TRUE(GetInputHandler().pinch_gesture_active());
GetInputHandler().PinchGestureEnd(gfx::Point());
EXPECT_FALSE(GetInputHandler().pinch_gesture_active());
}
TEST_P(LayerTreeHostImplTest, NotifyIfCanDrawChanged) {
EXPECT_FALSE(host_impl_->CanDraw());
on_can_draw_state_changed_called_ = false;
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
EXPECT_TRUE(host_impl_->CanDraw());
EXPECT_TRUE(on_can_draw_state_changed_called_);
on_can_draw_state_changed_called_ = false;
ClearLayersAndPropertyTrees(host_impl_->active_tree());
EXPECT_FALSE(host_impl_->CanDraw());
EXPECT_TRUE(on_can_draw_state_changed_called_);
on_can_draw_state_changed_called_ = false;
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
EXPECT_TRUE(host_impl_->CanDraw());
EXPECT_TRUE(on_can_draw_state_changed_called_);
on_can_draw_state_changed_called_ = false;
host_impl_->active_tree()->SetDeviceViewportRect(gfx::Rect());
EXPECT_FALSE(host_impl_->CanDraw());
EXPECT_TRUE(on_can_draw_state_changed_called_);
on_can_draw_state_changed_called_ = false;
host_impl_->active_tree()->SetDeviceViewportRect(gfx::Rect(100, 100));
EXPECT_TRUE(host_impl_->CanDraw());
EXPECT_TRUE(on_can_draw_state_changed_called_);
on_can_draw_state_changed_called_ = false;
}
TEST_P(CompositorFrameProducingLayerTreeHostImplTest,
ResourcelessDrawWithEmptyViewport) {
CreateHostImpl(DefaultSettings(), FakeLayerTreeFrameSink::CreateSoftware());
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
EXPECT_TRUE(host_impl_->CanDraw());
host_impl_->active_tree()->SetDeviceViewportRect(gfx::Rect());
EXPECT_FALSE(host_impl_->CanDraw());
auto* fake_layer_tree_frame_sink =
static_cast<FakeLayerTreeFrameSink*>(host_impl_->layer_tree_frame_sink());
EXPECT_EQ(fake_layer_tree_frame_sink->num_sent_frames(), 0u);
gfx::Transform identity;
gfx::Rect viewport(100, 100);
const bool resourceless_software_draw = true;
host_impl_->OnDraw(identity, viewport, resourceless_software_draw, false);
ASSERT_EQ(fake_layer_tree_frame_sink->num_sent_frames(), 1u);
}
TEST_P(LayerTreeHostImplTest, ScrollDeltaNoLayers) {
host_impl_->active_tree()->SetRootLayerForTesting(nullptr);
std::unique_ptr<CompositorCommitData> commit_data =
host_impl_->ProcessCompositorDeltas(
nullptr);
ASSERT_EQ(commit_data->scrolls.size(), 0u);
}
TEST_P(LayerTreeHostImplTest, ScrollDeltaTreeButNoChanges) {
LayerImpl* root = SetupDefaultRootLayer(gfx::Size(10, 10));
{
LayerImpl* child1 = AddLayerInActiveTree();
CopyProperties(root, child1);
LayerImpl* child2 = AddLayerInActiveTree();
CopyProperties(root, child2);
LayerImpl* grand_child1 = AddLayerInActiveTree();
CopyProperties(child2, grand_child1);
LayerImpl* grand_child2 = AddLayerInActiveTree();
CopyProperties(child2, grand_child2);
LayerImpl* great_grand_child = AddLayerInActiveTree();
CopyProperties(grand_child1, great_grand_child);
}
ExpectClearedScrollDeltasRecursive(root);
std::unique_ptr<CompositorCommitData> commit_data;
std::unique_ptr<AnimationHost> main_thread_animation_host(
AnimationHost::CreateForTesting(ThreadInstance::kMain));
commit_data = host_impl_->ProcessCompositorDeltas(
nullptr);
ASSERT_EQ(commit_data->scrolls.size(), 0u);
ExpectClearedScrollDeltasRecursive(root);
commit_data =
host_impl_->ProcessCompositorDeltas(main_thread_animation_host.get());
ASSERT_EQ(commit_data->scrolls.size(), 0u);
ExpectClearedScrollDeltasRecursive(root);
}
TEST_F(CommitToActiveTreeLayerTreeHostImplTest, ScrollDeltaRepeatedScrolls) {
gfx::PointF scroll_offset(20, 30);
auto* root = SetupDefaultRootLayer(gfx::Size(110, 110));
root->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
CreateScrollNode(root, gfx::Size(10, 10));
root->layer_tree_impl()
->property_trees()
->scroll_tree_mutable()
.UpdateScrollOffsetBaseForTesting(root->element_id(), scroll_offset);
UpdateDrawProperties(host_impl_->active_tree());
gfx::Vector2dF scroll_delta(11, -15);
std::unique_ptr<CompositorCommitData> commit_data1;
std::unique_ptr<AnimationHost> main_thread_animation_host(
AnimationHost::CreateForTesting(ThreadInstance::kMain));
root->ScrollBy(scroll_delta);
commit_data1 = host_impl_->ProcessCompositorDeltas(
nullptr);
ASSERT_EQ(commit_data1->scrolls.size(), 1u);
EXPECT_TRUE(
ScrollInfoContains(*commit_data1, root->element_id(), scroll_delta));
std::unique_ptr<CompositorCommitData> commit_data2;
gfx::Vector2dF scroll_delta2(-5, 27);
root->ScrollBy(scroll_delta2);
commit_data2 =
host_impl_->ProcessCompositorDeltas(main_thread_animation_host.get());
ASSERT_EQ(commit_data2->scrolls.size(), 1u);
EXPECT_TRUE(
ScrollInfoContains(*commit_data2, root->element_id(), scroll_delta2));
PushScrollOffsetsToPendingTree(
{{root->element_id(), gfx::PointAtOffsetFromOrigin(scroll_delta)}});
ClearNonScrollSyncTreeDeltasForTesting();
EXPECT_EQ(host_impl_->sync_tree()
->property_trees()
->scroll_tree()
.GetScrollOffsetDeltaForTesting(root->element_id()),
scroll_delta2);
PushScrollOffsetsToPendingTree(
{{root->element_id(), gfx::PointAtOffsetFromOrigin(scroll_delta2)}});
EXPECT_EQ(host_impl_->sync_tree()
->property_trees()
->scroll_tree()
.GetScrollOffsetDeltaForTesting(root->element_id()),
gfx::Vector2dF(0, 0));
}
TEST_F(CommitToPendingTreeLayerTreeHostImplTest, SyncedScrollAbortedCommit) {
CreateHostImpl(DefaultSettings(), CreateLayerTreeFrameSink());
CreatePendingTree();
gfx::PointF scroll_offset(20, 30);
auto* root = SetupDefaultRootLayer(gfx::Size(110, 110));
auto& scroll_tree =
root->layer_tree_impl()->property_trees()->scroll_tree_mutable();
root->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
std::unique_ptr<AnimationHost> main_thread_animation_host(
AnimationHost::CreateForTesting(ThreadInstance::kMain));
PushScrollOffsetsToPendingTree({{root->element_id(), gfx::PointF(0, 0)}});
ClearNonScrollSyncTreeDeltasForTesting();
host_impl_->active_tree()
->property_trees()
->scroll_tree_mutable()
.PushScrollUpdatesFromPendingTree(
host_impl_->pending_tree()->property_trees(),
host_impl_->active_tree());
CreateScrollNode(root, gfx::Size(10, 10));
auto* synced_scroll = scroll_tree.GetSyncedScrollOffset(root->element_id());
ASSERT_TRUE(synced_scroll);
scroll_tree.UpdateScrollOffsetBaseForTesting(root->element_id(),
scroll_offset);
UpdateDrawProperties(host_impl_->active_tree());
gfx::Vector2dF scroll_delta(11, -15);
root->ScrollBy(scroll_delta);
EXPECT_EQ(scroll_delta, synced_scroll->UnsentDelta());
host_impl_->ProcessCompositorDeltas( nullptr);
EXPECT_EQ(scroll_delta, synced_scroll->reflected_delta_in_main_tree());
EXPECT_EQ(gfx::Vector2dF(),
synced_scroll->next_reflected_delta_in_main_tree());
std::unique_ptr<CompositorCommitData> commit_data2;
gfx::Vector2dF scroll_delta2(-5, 27);
root->ScrollBy(scroll_delta2);
EXPECT_EQ(scroll_delta2, synced_scroll->UnsentDelta());
host_impl_->ProcessCompositorDeltas(main_thread_animation_host.get());
EXPECT_EQ(scroll_delta, synced_scroll->reflected_delta_in_main_tree());
EXPECT_EQ(scroll_delta2, synced_scroll->next_reflected_delta_in_main_tree());
root->layer_tree_impl()->ApplySentScrollAndScaleDeltasFromAbortedCommit(
true, true);
EXPECT_EQ(scroll_delta + scroll_delta2,
synced_scroll->reflected_delta_in_main_tree());
EXPECT_EQ(gfx::Vector2dF(),
synced_scroll->next_reflected_delta_in_main_tree());
gfx::Vector2dF scroll_delta3(-2, -13);
root->ScrollBy(scroll_delta3);
EXPECT_EQ(scroll_delta3, synced_scroll->UnsentDelta());
host_impl_->ProcessCompositorDeltas(main_thread_animation_host.get());
EXPECT_EQ(scroll_delta + scroll_delta2,
synced_scroll->reflected_delta_in_main_tree());
EXPECT_EQ(scroll_delta3, synced_scroll->next_reflected_delta_in_main_tree());
PushScrollOffsetsToPendingTree(
{{root->element_id(), scroll_offset + scroll_delta + scroll_delta2}});
EXPECT_EQ(scroll_offset + scroll_delta + scroll_delta2,
synced_scroll->PendingBase());
EXPECT_EQ(scroll_delta3, synced_scroll->PendingDelta());
EXPECT_EQ(scroll_delta3, synced_scroll->reflected_delta_in_main_tree());
EXPECT_EQ(gfx::Vector2dF(),
synced_scroll->next_reflected_delta_in_main_tree());
}
TEST_P(LayerTreeHostImplTest, ScrollBeforeRootLayerAttached) {
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(), gfx::Vector2dF(0, 1),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
EXPECT_EQ(ScrollThread::kScrollIgnored, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
status = GetInputHandler().RootScrollBegin(
BeginState(gfx::Point(), gfx::Vector2dF(0, 1),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
EXPECT_EQ(ScrollThread::kScrollIgnored, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
}
TEST_P(LayerTreeHostImplTest, ScrollUpdateAndEndNoOpWithoutBegin) {
SetupViewportLayersOuterScrolls(gfx::Size(100, 100), gfx::Size(1000, 1000));
{
EXPECT_FALSE(host_impl_->CurrentlyScrollingNode());
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2d(0, 10), ui::ScrollInputType::kTouchscreen));
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2d(0, 10), ui::ScrollInputType::kTouchscreen));
EXPECT_FALSE(host_impl_->CurrentlyScrollingNode());
GetInputHandler().ScrollEnd();
}
{
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_TRUE(host_impl_->CurrentlyScrollingNode());
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2d(0, 10), ui::ScrollInputType::kTouchscreen));
EXPECT_POINTF_EQ(gfx::PointF(0, 10),
CurrentScrollOffset(OuterViewportScrollLayer()));
GetInputHandler().ScrollEnd();
}
}
TEST_P(LayerTreeHostImplTest, TargetMainThreadScroller) {
SetupViewportLayersOuterScrolls(gfx::Size(100, 100), gfx::Size(1000, 1000));
ScrollStateData scroll_state_data;
scroll_state_data.set_current_native_scrolling_element(
host_impl_->OuterViewportScrollNode()->element_id);
std::unique_ptr<ScrollState> scroll_state(new ScrollState(scroll_state_data));
{
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
scroll_state.get(), ui::ScrollInputType::kWheel);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(host_impl_->OuterViewportScrollNode(),
host_impl_->CurrentlyScrollingNode());
GetInputHandler().ScrollEnd();
}
host_impl_->OuterViewportScrollNode()->main_thread_repaint_reasons =
MainThreadScrollingReason::kPreferNonCompositedScrolling;
{
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
scroll_state.get(), ui::ScrollInputType::kWheel);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
EXPECT_EQ(
MainThreadScrollingReason::kPreferNonCompositedScrolling,
host_impl_->CurrentlyScrollingNode()->main_thread_repaint_reasons);
}
}
TEST_P(LayerTreeHostImplTest, ScrollRootCallsCommitAndRedraw) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
EXPECT_TRUE(host_impl_->CurrentlyScrollingNode());
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2d(0, 10), ui::ScrollInputType::kTouchscreen));
GetInputHandler().ScrollEnd();
EXPECT_FALSE(host_impl_->CurrentlyScrollingNode());
EXPECT_TRUE(did_request_redraw_);
EXPECT_TRUE(did_request_commit_);
}
TEST_P(LayerTreeHostImplTest, ActivelyScrollingOnlyAfterScrollMovement) {
SetupViewportLayersOuterScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
{
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
EXPECT_EQ(host_impl_->GetActivelyScrollingType(),
ActivelyScrollingType::kNone);
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(), gfx::Vector2d(0, -10),
ui::ScrollInputType::kTouchscreen));
EXPECT_EQ(host_impl_->GetActivelyScrollingType(),
ActivelyScrollingType::kNone);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2d(0, 10), ui::ScrollInputType::kTouchscreen));
EXPECT_EQ(host_impl_->GetActivelyScrollingType(),
ActivelyScrollingType::kPrecise);
GetInputHandler().ScrollEnd();
EXPECT_EQ(host_impl_->GetActivelyScrollingType(),
ActivelyScrollingType::kNone);
}
ASSERT_EQ(10, CurrentScrollOffset(OuterViewportScrollLayer()).y());
{
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(host_impl_->GetActivelyScrollingType(),
ActivelyScrollingType::kNone);
GetInputHandler().ScrollUpdate(
AnimatedUpdateState(gfx::Point(), gfx::Vector2dF(0, 10)));
EXPECT_EQ(host_impl_->GetActivelyScrollingType(),
ActivelyScrollingType::kAnimated);
base::TimeTicks cur_time = base::TimeTicks() + base::Milliseconds(100);
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
#define ANIMATE(time_ms) \
cur_time += base::Milliseconds(time_ms); \
begin_frame_args.frame_time = (cur_time); \
begin_frame_args.frame_id.sequence_number++; \
host_impl_->WillBeginImplFrame(begin_frame_args); \
host_impl_->Animate(); \
host_impl_->UpdateAnimationState(true); \
host_impl_->DidFinishImplFrame(begin_frame_args);
ANIMATE(0);
EXPECT_EQ(host_impl_->GetActivelyScrollingType(),
ActivelyScrollingType::kAnimated);
ANIMATE(200);
EXPECT_EQ(host_impl_->GetActivelyScrollingType(),
ActivelyScrollingType::kAnimated);
ANIMATE(1000);
EXPECT_EQ(host_impl_->GetActivelyScrollingType(),
ActivelyScrollingType::kAnimated);
#undef ANIMATE
ASSERT_EQ(20, CurrentScrollOffset(OuterViewportScrollLayer()).y());
GetInputHandler().ScrollEnd();
EXPECT_EQ(host_impl_->GetActivelyScrollingType(),
ActivelyScrollingType::kNone);
}
}
TEST_P(LayerTreeHostImplTest, ScrollWithoutRootLayer) {
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
EXPECT_EQ(ScrollThread::kScrollIgnored, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
}
TEST_P(LayerTreeHostImplTest, ScrollWithoutRenderer) {
auto compositor_context = viz::TestContextProvider::CreateRaster();
compositor_context->UnboundTestRasterInterface()->LoseContextCHROMIUM(
GL_GUILTY_CONTEXT_RESET_ARB, GL_INNOCENT_CONTEXT_RESET_ARB);
EXPECT_FALSE(CreateHostImpl(
DefaultSettings(),
FakeLayerTreeFrameSink::Create3d(std::move(compositor_context))));
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
}
TEST_P(LayerTreeHostImplTest, ReplaceTreeWhileScrolling) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
gfx::Vector2dF scroll_delta(0, 10);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), scroll_delta,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
ClearLayersAndPropertyTrees(host_impl_->active_tree());
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
auto* scroll_layer = InnerViewportScrollLayer();
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(), scroll_delta, ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
std::unique_ptr<CompositorCommitData> commit_data =
host_impl_->ProcessCompositorDeltas(
nullptr);
EXPECT_TRUE(ScrollInfoContains(*commit_data, scroll_layer->element_id(),
scroll_delta));
}
TEST_F(CommitToPendingTreeLayerTreeHostImplTest,
ActivateTreeScrollingNodeDisappeared) {
SetupViewportLayersOuterScrolls(gfx::Size(100, 100), gfx::Size(1000, 1000));
auto status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(30, 30), gfx::Vector2d(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
GetInputHandler().ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10),
ui::ScrollInputType::kWheel));
EXPECT_TRUE(host_impl_->active_tree()->CurrentlyScrollingNode());
CreatePendingTree();
PropertyTrees pending_property_trees(*host_impl_);
pending_property_trees.set_sequence_number(
host_impl_->active_tree()->property_trees()->sequence_number() + 1);
host_impl_->pending_tree()->SetPropertyTrees(pending_property_trees);
SetupRootLayer<LayerImpl>(host_impl_->pending_tree(), gfx::Size(100, 100));
host_impl_->ActivateSyncTree();
EXPECT_FALSE(host_impl_->active_tree()->CurrentlyScrollingNode());
}
TEST_P(LayerTreeHostImplTest, ScrollBlocksOnWheelEventHandlers) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
auto* scroll = InnerViewportScrollLayer();
scroll->SetWheelEventHandlerRegion(Region(gfx::Rect(20, 20)));
DrawFrame();
host_impl_->active_tree()->set_event_listener_properties(
EventListenerClass::kMouseWheel, EventListenerProperties::kBlocking);
EXPECT_EQ(EventListenerProperties::kBlocking,
GetInputHandler().GetEventListenerProperties(
EventListenerClass::kMouseWheel));
EXPECT_TRUE(
GetInputHandler().HasBlockingWheelEventHandlerAt(gfx::Point(10, 10)));
EXPECT_FALSE(
GetInputHandler().HasBlockingWheelEventHandlerAt(gfx::Point(30, 30)));
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(), gfx::Vector2d(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
GetInputHandler().ScrollEnd();
}
TEST_P(LayerTreeHostImplTest, ScrollBlocksOnTouchEventHandlers) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
LayerImpl* root = root_layer();
LayerImpl* scroll = InnerViewportScrollLayer();
LayerImpl* child = AddLayerInActiveTree();
child->SetDrawsContent(true);
child->SetBounds(gfx::Size(50, 50));
CopyProperties(scroll, child);
child->SetOffsetToTransformParent(gfx::Vector2dF(0, 20));
TouchAction touch_action;
TouchActionRegion touch_action_region;
touch_action_region.Union(TouchAction::kPanLeft, gfx::Rect(0, 0, 100, 100));
touch_action_region.Union(TouchAction::kPanRight,
gfx::Rect(25, 25, 100, 100));
root->SetTouchActionRegion(std::move(touch_action_region));
EXPECT_EQ(InputHandler::TouchStartOrMoveEventListenerType::kHandler,
GetInputHandler().EventListenerTypeForTouchStartOrMoveAt(
gfx::Rect(gfx::Point(10, 10), gfx::Size()), &touch_action));
EXPECT_EQ(TouchAction::kPanLeft, touch_action);
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(), gfx::Vector2d(0, 10),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
GetInputHandler().ScrollEnd();
EXPECT_EQ(InputHandler::TouchStartOrMoveEventListenerType::kHandler,
GetInputHandler().EventListenerTypeForTouchStartOrMoveAt(
gfx::Rect(gfx::Point(10, 30), gfx::Size()), &touch_action));
root->SetTouchActionRegion(TouchActionRegion());
EXPECT_EQ(InputHandler::TouchStartOrMoveEventListenerType::kNoHandler,
GetInputHandler().EventListenerTypeForTouchStartOrMoveAt(
gfx::Rect(gfx::Point(10, 30), gfx::Size()), &touch_action));
EXPECT_EQ(TouchAction::kAuto, touch_action);
touch_action_region = TouchActionRegion();
touch_action_region.Union(TouchAction::kPanX, gfx::Rect(0, 0, 50, 50));
child->SetTouchActionRegion(std::move(touch_action_region));
EXPECT_EQ(InputHandler::TouchStartOrMoveEventListenerType::kHandler,
GetInputHandler().EventListenerTypeForTouchStartOrMoveAt(
gfx::Rect(gfx::Point(10, 30), gfx::Size()), &touch_action));
EXPECT_EQ(TouchAction::kPanX, touch_action);
}
TEST_P(LayerTreeHostImplTest, ShouldScrollOnMainThread) {
SetupViewportLayersOuterScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
host_impl_->OuterViewportScrollNode()->main_thread_repaint_reasons =
MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects;
DrawFrame();
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(), gfx::Vector2d(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
EXPECT_EQ(MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects,
host_impl_->CurrentlyScrollingNode()->main_thread_repaint_reasons);
status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(), gfx::Vector2d(0, 10),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
EXPECT_EQ(MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects,
host_impl_->CurrentlyScrollingNode()->main_thread_repaint_reasons);
}
TEST_P(LayerTreeHostImplTest, ScrollWithOverlappingNonScrollableLayer) {
CreateAndTestNonScrollableLayers(false);
}
TEST_P(LayerTreeHostImplTest,
ScrollWithOverlappingTransparentNonScrollableLayer) {
CreateAndTestNonScrollableLayers(true);
}
TEST_P(LayerTreeHostImplTest, ScrolledOverlappingDrawnScrollbarLayer) {
LayerTreeImpl* layer_tree_impl = host_impl_->active_tree();
gfx::Size content_size = gfx::Size(360, 600);
gfx::Size scroll_content_size = gfx::Size(345, 3800);
gfx::Size scrollbar_size = gfx::Size(15, 600);
SetupViewportLayersNoScrolls(content_size);
LayerImpl* scroll = AddScrollableLayer(OuterViewportScrollLayer(),
content_size, scroll_content_size);
auto* drawn_scrollbar = AddLayer<PaintedScrollbarLayerImpl>(
layer_tree_impl, ScrollbarOrientation::kVertical, false, true);
SetupScrollbarLayer(scroll, drawn_scrollbar);
drawn_scrollbar->SetBounds(scrollbar_size);
drawn_scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(345, 0));
LayerImpl* squash1 = AddLayerInActiveTree();
squash1->SetBounds(gfx::Size(140, 200));
squash1->SetDrawsContent(true);
squash1->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
CopyProperties(scroll, squash1);
squash1->SetOffsetToTransformParent(gfx::Vector2dF(220, 0));
LayerImpl* squash2 = AddLayerInActiveTree();
squash2->SetBounds(gfx::Size(140, 200));
squash2->SetDrawsContent(true);
squash2->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
CopyProperties(scroll, squash2);
squash2->SetScrollTreeIndex(OuterViewportScrollLayer()->scroll_tree_index());
squash2->SetOffsetToTransformParent(gfx::Vector2dF(220, 200));
UpdateDrawProperties(layer_tree_impl);
layer_tree_impl->DidBecomeActive();
auto status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(350, 150), gfx::Vector2d(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
GetInputHandler().ScrollEnd();
status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(350, 350), gfx::Vector2d(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest,
status.main_thread_hit_test_reasons);
GetInputHandler().ScrollEnd();
status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(350, 500), gfx::Vector2d(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
GetInputHandler().ScrollEnd();
}
gfx::PresentationFeedback ExampleFeedback() {
auto feedback = gfx::PresentationFeedback(
base::TimeTicks() + base::Milliseconds(42), base::Microseconds(18),
gfx::PresentationFeedback::Flags::kVSync |
gfx::PresentationFeedback::Flags::kHWCompletion);
#if BUILDFLAG(IS_MAC)
feedback.ca_layer_error_code = gfx::kCALayerFailedQuadBlendMode;
#endif
return feedback;
}
class LayerTreeHostImplTestInvokePresentationCallbacks
: public LayerTreeHostImplTest {
public:
void DidPresentCompositorFrameOnImplThread(
uint32_t frame_token,
PresentationTimeCallbackBuffer::PendingCallbacks activated,
const viz::FrameTimingDetails& details) override {
host_impl_->NotifyDidPresentCompositorFrameOnImplThread(
frame_token, std::move(activated.compositor_successful_callbacks),
details);
for (auto& callback : activated.main_callbacks)
std::move(callback).Run(details.presentation_feedback);
for (auto& callback : activated.main_successful_callbacks)
std::move(callback).Run(details);
}
};
INSTANTIATE_COMMIT_TO_TREE_TEST_P(
LayerTreeHostImplTestInvokePresentationCallbacks);
TEST_P(LayerTreeHostImplTestInvokePresentationCallbacks,
PresentationFeedbackCallbacksFire) {
bool compositor_successful_callback_fired = false;
bool main_callback_fired = false;
bool main_successful_callback_fired = false;
base::TimeTicks compositor_successful_callback_presentation_timestamp;
gfx::PresentationFeedback main_callback_presentation_feedback;
base::TimeTicks main_successful_callback_presentation_timestamp;
constexpr uint32_t frame_token_1 = 1;
constexpr uint32_t frame_token_2 = 2;
constexpr uint32_t frame_token_3 = 3;
host_impl_
->RegisterCompositorThreadSuccessfulPresentationTimeCallbackForTesting(
frame_token_1,
base::BindLambdaForTesting(
[&](base::TimeTicks presentation_timestamp) {
DCHECK(compositor_successful_callback_presentation_timestamp
.is_null());
DCHECK(!presentation_timestamp.is_null());
compositor_successful_callback_fired = true;
compositor_successful_callback_presentation_timestamp =
presentation_timestamp;
}));
host_impl_->RegisterMainThreadPresentationTimeCallbackForTesting(
frame_token_2, base::BindLambdaForTesting(
[&](const gfx::PresentationFeedback& feedback) {
main_callback_fired = true;
main_callback_presentation_feedback = feedback;
}));
host_impl_->RegisterMainThreadSuccessfulPresentationTimeCallbackForTesting(
frame_token_2,
base::BindLambdaForTesting([&](const viz::FrameTimingDetails& details) {
base::TimeTicks presentation_timestamp =
details.presentation_feedback.timestamp;
DCHECK(main_successful_callback_presentation_timestamp.is_null());
DCHECK(!presentation_timestamp.is_null());
main_successful_callback_fired = true;
main_successful_callback_presentation_timestamp =
presentation_timestamp;
}));
viz::FrameTimingDetails mock_details;
mock_details.presentation_feedback = ExampleFeedback();
host_impl_->DidPresentCompositorFrame(frame_token_1, mock_details);
EXPECT_TRUE(compositor_successful_callback_fired);
EXPECT_FALSE(main_callback_fired);
EXPECT_FALSE(main_successful_callback_fired);
EXPECT_EQ(compositor_successful_callback_presentation_timestamp,
mock_details.presentation_feedback.timestamp);
EXPECT_EQ(main_callback_presentation_feedback, gfx::PresentationFeedback());
EXPECT_TRUE(main_successful_callback_presentation_timestamp.is_null());
mock_details.presentation_feedback = gfx::PresentationFeedback::Failure();
host_impl_->DidPresentCompositorFrame(frame_token_2, mock_details);
EXPECT_TRUE(main_callback_fired);
EXPECT_FALSE(main_successful_callback_fired);
EXPECT_EQ(main_callback_presentation_feedback,
mock_details.presentation_feedback);
EXPECT_TRUE(main_successful_callback_presentation_timestamp.is_null());
mock_details.presentation_feedback = ExampleFeedback();
host_impl_->DidPresentCompositorFrame(frame_token_3, mock_details);
EXPECT_TRUE(main_successful_callback_fired);
EXPECT_EQ(main_successful_callback_presentation_timestamp,
mock_details.presentation_feedback.timestamp);
}
TEST_P(LayerTreeHostImplTest, MainThreadScrollHitTestRegionBasic) {
SetupViewportLayersInnerScrolls(gfx::Size(100, 100), gfx::Size(200, 200));
LayerImpl* outer_scroll = OuterViewportScrollLayer();
outer_scroll->SetMainThreadScrollHitTestRegion(gfx::Rect(0, 0, 50, 50));
DrawFrame();
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(25, 25), gfx::Vector2d(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kMainThreadScrollHitTestRegion,
status.main_thread_hit_test_reasons);
status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(25, 25), gfx::Vector2d(0, 10),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kMainThreadScrollHitTestRegion,
status.main_thread_hit_test_reasons);
status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(75, 75), gfx::Vector2d(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
GetInputHandler().ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10),
ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(75, 75), gfx::Vector2d(0, 10),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2d(0, 10), ui::ScrollInputType::kTouchscreen));
GetInputHandler().ScrollEnd();
}
TEST_P(LayerTreeHostImplTest, MainThreadScrollHitTestRegionInNonScrollingRoot) {
LayerImpl* root_layer = SetupDefaultRootLayer(gfx::Size(50, 50));
auto AddNewLayer = [&]() {
LayerImpl* layer = AddLayerInActiveTree();
layer->SetBounds(gfx::Size(50, 50));
layer->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
CopyProperties(root_layer, layer);
return layer;
};
LayerImpl* layers[3] = {AddNewLayer(), AddNewLayer(), AddNewLayer()};
DrawFrame();
auto& input_handler = GetInputHandler();
std::unique_ptr<ScrollState> begin_state = BeginState(
gfx::Point(20, 20), gfx::Vector2dF(0, 10), ui::ScrollInputType::kWheel);
InputHandler::ScrollStatus status;
for (LayerImpl* layer : layers) {
layer->SetMainThreadScrollHitTestRegion(gfx::Rect(10, 10, 20, 20));
status = input_handler.ScrollBegin(begin_state.get(),
ui::ScrollInputType::kWheel);
input_handler.ScrollEnd();
layer->SetMainThreadScrollHitTestRegion(Region());
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kMainThreadScrollHitTestRegion,
status.main_thread_hit_test_reasons);
}
}
TEST_P(LayerTreeHostImplTest, MainThreadScrollHitTestRegionWithOffset) {
SetupViewportLayersInnerScrolls(gfx::Size(100, 100), gfx::Size(200, 200));
LayerImpl* outer_scroll = OuterViewportScrollLayer();
outer_scroll->SetMainThreadScrollHitTestRegion(gfx::Rect(0, 0, 50, 50));
SetPostTranslation(outer_scroll, gfx::Vector2dF(-25, 0));
outer_scroll->SetDrawsContent(true);
DrawFrame();
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(40, 10), gfx::Vector2d(0, 1),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
GetInputHandler().ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 1),
ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(10, 10), gfx::Vector2d(0, 1),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kMainThreadScrollHitTestRegion,
status.main_thread_hit_test_reasons);
}
TEST_P(LayerTreeHostImplTest,
LayerOverlapsMainThreadScrollHitTestRegionInLayer) {
SetupViewportLayersOuterScrolls(gfx::Size(100, 100), gfx::Size(200, 200));
LayerImpl* outer_scroll = OuterViewportScrollLayer();
auto* layer_b = AddLayer<LayerImpl>(host_impl_->active_tree());
layer_b->SetBounds(gfx::Size(50, 100));
layer_b->SetDrawsContent(true);
layer_b->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
CopyProperties(outer_scroll, layer_b);
layer_b->SetOffsetToTransformParent(gfx::Vector2dF(50, 0));
layer_b->SetMainThreadScrollHitTestRegion(gfx::Rect(0, 0, 50, 100));
{
ASSERT_EQ(layer_b, host_impl_->active_tree()->FindLayerThatIsHitByPoint(
gfx::PointF(60, 50)));
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(60, 50), gfx::Vector2d(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
ASSERT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
ASSERT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
ASSERT_EQ(MainThreadScrollingReason::kMainThreadScrollHitTestRegion,
status.main_thread_hit_test_reasons);
GetInputHandler().ScrollEnd();
}
auto* layer_c = AddLayer<LayerImpl>(host_impl_->active_tree());
layer_c->SetBounds(gfx::Size(50, 50));
layer_c->SetDrawsContent(true);
layer_c->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
CopyProperties(outer_scroll, layer_c);
layer_c->SetOffsetToTransformParent(gfx::Vector2dF(25, 25));
{
ASSERT_EQ(layer_c, host_impl_->active_tree()->FindLayerThatIsHitByPoint(
gfx::PointF(40, 50)));
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(40, 50), gfx::Vector2d(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
ASSERT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
ASSERT_EQ(host_impl_->CurrentlyScrollingNode(),
host_impl_->OuterViewportScrollNode());
GetInputHandler().ScrollEnd();
}
{
ASSERT_EQ(layer_c, host_impl_->active_tree()->FindLayerThatIsHitByPoint(
gfx::PointF(60, 50)));
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(60, 50), gfx::Vector2d(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kMainThreadScrollHitTestRegion,
status.main_thread_hit_test_reasons);
GetInputHandler().ScrollEnd();
}
}
TEST_P(LayerTreeHostImplTest,
LayerOverlapsMainThreadScrollHitTestRegionInNonScrollAncestorLayer) {
SetupViewportLayersOuterScrolls(gfx::Size(100, 100), gfx::Size(200, 200));
LayerImpl* outer_scroll = OuterViewportScrollLayer();
LayerImpl* layer_a = AddScrollableLayer(outer_scroll, gfx::Size(100, 100),
gfx::Size(200, 200));
auto* layer_b = AddLayer<LayerImpl>(host_impl_->active_tree());
layer_b->SetBounds(gfx::Size(50, 100));
layer_b->SetDrawsContent(true);
layer_b->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
CopyProperties(outer_scroll, layer_b);
layer_b->SetOffsetToTransformParent(gfx::Vector2dF(50, 0));
{
ASSERT_EQ(layer_b, host_impl_->active_tree()->FindLayerThatIsHitByPoint(
gfx::PointF(75, 50)));
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(75, 50), gfx::Vector2d(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
ASSERT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
ASSERT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
ASSERT_EQ(MainThreadScrollingReason::kFailedHitTest,
status.main_thread_hit_test_reasons);
GetInputHandler().ScrollEnd();
}
auto* layer_c = AddLayer<LayerImpl>(host_impl_->active_tree());
layer_c->SetBounds(gfx::Size(50, 50));
layer_c->SetDrawsContent(true);
layer_c->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
CopyProperties(layer_a, layer_c);
layer_c->SetOffsetToTransformParent(gfx::Vector2dF(25, 25));
{
ASSERT_EQ(layer_c, host_impl_->active_tree()->FindLayerThatIsHitByPoint(
gfx::PointF(40, 50)));
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(40, 50), gfx::Vector2d(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
ASSERT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
ASSERT_EQ(host_impl_->CurrentlyScrollingNode()->id,
layer_a->scroll_tree_index());
GetInputHandler().ScrollEnd();
}
{
ASSERT_EQ(layer_c, host_impl_->active_tree()->FindLayerThatIsHitByPoint(
gfx::PointF(60, 50)));
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(60, 50), gfx::Vector2d(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest,
status.main_thread_hit_test_reasons);
GetInputHandler().ScrollEnd();
}
}
TEST_P(LayerTreeHostImplTest,
FixedLayerOverlapsMainThreadScrollHitTestRegionInLayer) {
SetupViewportLayersOuterScrolls(gfx::Size(100, 100), gfx::Size(200, 200));
LayerImpl* outer_scroll = OuterViewportScrollLayer();
LayerImpl* inner_scroll = InnerViewportScrollLayer();
auto* layer_b = AddLayer<LayerImpl>(host_impl_->active_tree());
layer_b->SetBounds(gfx::Size(50, 100));
layer_b->SetDrawsContent(true);
layer_b->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
CopyProperties(outer_scroll, layer_b);
layer_b->SetOffsetToTransformParent(gfx::Vector2dF(50, 0));
layer_b->SetMainThreadScrollHitTestRegion(gfx::Rect(0, 0, 50, 100));
{
ASSERT_EQ(layer_b, host_impl_->active_tree()->FindLayerThatIsHitByPoint(
gfx::PointF(60, 50)));
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(60, 50), gfx::Vector2d(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
ASSERT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
ASSERT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
ASSERT_EQ(MainThreadScrollingReason::kMainThreadScrollHitTestRegion,
status.main_thread_hit_test_reasons);
GetInputHandler().ScrollEnd();
}
auto* layer_c = AddLayer<LayerImpl>(host_impl_->active_tree());
layer_c->SetBounds(gfx::Size(50, 50));
layer_c->SetDrawsContent(true);
layer_c->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
CopyProperties(inner_scroll, layer_c);
layer_c->SetOffsetToTransformParent(gfx::Vector2dF(25, 25));
{
ASSERT_EQ(layer_c, host_impl_->active_tree()->FindLayerThatIsHitByPoint(
gfx::PointF(40, 50)));
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(40, 50), gfx::Vector2d(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
ASSERT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
ASSERT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
ASSERT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
ASSERT_EQ(host_impl_->CurrentlyScrollingNode(),
host_impl_->OuterViewportScrollNode());
GetInputHandler().ScrollEnd();
}
{
ASSERT_EQ(layer_c, host_impl_->active_tree()->FindLayerThatIsHitByPoint(
gfx::PointF(60, 50)));
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(60, 50), gfx::Vector2d(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kMainThreadScrollHitTestRegion,
status.main_thread_hit_test_reasons);
GetInputHandler().ScrollEnd();
}
}
TEST_P(LayerTreeHostImplTest, FixedLayerOverNonFixedLayer) {
SetupViewportLayersOuterScrolls(gfx::Size(100, 100), gfx::Size(200, 200));
LayerImpl* outer_scroll = OuterViewportScrollLayer();
LayerImpl* inner_scroll = InnerViewportScrollLayer();
auto* layer_b = AddLayer<LayerImpl>(host_impl_->active_tree());
layer_b->SetBounds(gfx::Size(50, 100));
layer_b->SetDrawsContent(true);
layer_b->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
CopyProperties(outer_scroll, layer_b);
layer_b->SetOffsetToTransformParent(gfx::Vector2dF(50, 0));
auto* layer_c = AddLayer<LayerImpl>(host_impl_->active_tree());
layer_c->SetBounds(gfx::Size(50, 50));
layer_c->SetDrawsContent(true);
layer_c->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
CopyProperties(inner_scroll, layer_c);
layer_c->SetOffsetToTransformParent(gfx::Vector2dF(25, 25));
{
ASSERT_EQ(layer_c, host_impl_->active_tree()->FindLayerThatIsHitByPoint(
gfx::PointF(60, 50)));
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(60, 50), gfx::Vector2d(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
ASSERT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
ASSERT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
ASSERT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
ASSERT_EQ(host_impl_->CurrentlyScrollingNode(),
host_impl_->OuterViewportScrollNode());
GetInputHandler().ScrollEnd();
}
{
GetScrollNode(InnerViewportScrollLayer())
->prevent_viewport_scrolling_from_inner = true;
ASSERT_EQ(layer_c, host_impl_->active_tree()->FindLayerThatIsHitByPoint(
gfx::PointF(60, 50)));
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(60, 50), gfx::Vector2d(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest,
status.main_thread_hit_test_reasons);
GetInputHandler().ScrollEnd();
}
}
TEST_P(LayerTreeHostImplTest, ScrollHandlerNotPresent) {
SetupViewportLayersInnerScrolls(gfx::Size(100, 100), gfx::Size(200, 200));
EXPECT_FALSE(host_impl_->active_tree()->have_scroll_event_handlers());
DrawFrame();
EXPECT_FALSE(host_impl_->ScrollAffectsScrollHandler());
GetInputHandler().ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 10),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
EXPECT_FALSE(host_impl_->ScrollAffectsScrollHandler());
GetInputHandler().ScrollEnd();
EXPECT_FALSE(host_impl_->ScrollAffectsScrollHandler());
}
TEST_P(LayerTreeHostImplTest, ScrollHandlerPresent) {
SetupViewportLayersInnerScrolls(gfx::Size(100, 100), gfx::Size(200, 200));
host_impl_->active_tree()->set_have_scroll_event_handlers(true);
DrawFrame();
EXPECT_FALSE(host_impl_->ScrollAffectsScrollHandler());
GetInputHandler().ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 10),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
EXPECT_TRUE(host_impl_->ScrollAffectsScrollHandler());
GetInputHandler().ScrollEnd();
EXPECT_FALSE(host_impl_->ScrollAffectsScrollHandler());
}
TEST_P(LayerTreeHostImplTest, ScrollUpdateReturnsCorrectValue) {
SetupViewportLayersInnerScrolls(gfx::Size(100, 100), gfx::Size(200, 200));
DrawFrame();
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(), gfx::Vector2d(-10, 0),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
EXPECT_FALSE(
GetInputHandler()
.ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(-10, 0),
ui::ScrollInputType::kTouchscreen))
.did_scroll);
EXPECT_FALSE(
GetInputHandler()
.ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, -10),
ui::ScrollInputType::kTouchscreen))
.did_scroll);
EXPECT_FALSE(
GetInputHandler()
.ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(-10, -10),
ui::ScrollInputType::kTouchscreen))
.did_scroll);
EXPECT_TRUE(GetInputHandler()
.ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(10, 0),
ui::ScrollInputType::kTouchscreen))
.did_scroll);
EXPECT_TRUE(GetInputHandler()
.ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10),
ui::ScrollInputType::kTouchscreen))
.did_scroll);
EXPECT_TRUE(GetInputHandler()
.ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(10, 10),
ui::ScrollInputType::kTouchscreen))
.did_scroll);
EXPECT_TRUE(GetInputHandler()
.ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(-10, 0),
ui::ScrollInputType::kTouchscreen))
.did_scroll);
EXPECT_TRUE(GetInputHandler()
.ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, -10),
ui::ScrollInputType::kTouchscreen))
.did_scroll);
EXPECT_TRUE(
GetInputHandler()
.ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(-10, -10),
ui::ScrollInputType::kTouchscreen))
.did_scroll);
EXPECT_TRUE(
GetInputHandler()
.ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(10, -10),
ui::ScrollInputType::kTouchscreen))
.did_scroll);
EXPECT_TRUE(GetInputHandler()
.ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(-10, 0),
ui::ScrollInputType::kTouchscreen))
.did_scroll);
EXPECT_TRUE(
GetInputHandler()
.ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(-10, 10),
ui::ScrollInputType::kTouchscreen))
.did_scroll);
EXPECT_TRUE(
GetInputHandler()
.ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(5000, 5000),
ui::ScrollInputType::kTouchscreen))
.did_scroll);
}
TEST_P(LayerTreeHostImplTest, ScrollSnapOnX) {
LayerImpl* overflow = CreateLayerForSnapping();
gfx::Point pointer_position(10, 10);
gfx::Vector2dF x_delta(20, 0);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(pointer_position, x_delta,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(overflow));
EXPECT_EQ(TargetSnapAreaElementIds(),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
GetInputHandler().ScrollUpdate(
UpdateState(pointer_position, x_delta, ui::ScrollInputType::kWheel));
EXPECT_EQ(TargetSnapAreaElementIds(),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
GetInputHandler().ScrollEnd(true);
EXPECT_TRUE(
GetInputHandler().animating_for_snap_for_testing(overflow->element_id()));
EXPECT_EQ(TargetSnapAreaElementIds(),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
base::TimeTicks start_time = base::TimeTicks() + base::Milliseconds(100);
BeginImplFrameAndAnimate(begin_frame_args, start_time);
BeginImplFrameAndAnimate(begin_frame_args,
start_time + base::Milliseconds(50));
BeginImplFrameAndAnimate(begin_frame_args,
start_time + base::Milliseconds(1000));
EXPECT_FALSE(
GetInputHandler().animating_for_snap_for_testing(overflow->element_id()));
EXPECT_POINTF_EQ(gfx::PointF(50, 0), CurrentScrollOffset(overflow));
EXPECT_EQ(TargetSnapAreaElementIds(ElementId(10), ElementId()),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
}
TEST_P(LayerTreeHostImplTest, ScrollSnapOnY) {
LayerImpl* overflow = CreateLayerForSnapping();
gfx::Point pointer_position(10, 10);
gfx::Vector2dF y_delta(0, 20);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(pointer_position, y_delta,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(overflow));
EXPECT_EQ(TargetSnapAreaElementIds(),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
GetInputHandler().ScrollUpdate(
UpdateState(pointer_position, y_delta, ui::ScrollInputType::kWheel));
EXPECT_EQ(TargetSnapAreaElementIds(),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
GetInputHandler().ScrollEnd(true);
EXPECT_TRUE(
GetInputHandler().animating_for_snap_for_testing(overflow->element_id()));
EXPECT_EQ(TargetSnapAreaElementIds(),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
base::TimeTicks start_time = base::TimeTicks() + base::Milliseconds(100);
BeginImplFrameAndAnimate(begin_frame_args, start_time);
BeginImplFrameAndAnimate(begin_frame_args,
start_time + base::Milliseconds(50));
BeginImplFrameAndAnimate(begin_frame_args,
start_time + base::Milliseconds(1000));
EXPECT_FALSE(
GetInputHandler().animating_for_snap_for_testing(overflow->element_id()));
EXPECT_POINTF_EQ(gfx::PointF(0, 50), CurrentScrollOffset(overflow));
EXPECT_EQ(TargetSnapAreaElementIds(ElementId(), ElementId(10)),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
}
TEST_P(LayerTreeHostImplTest, ScrollSnapOnBoth) {
LayerImpl* overflow = CreateLayerForSnapping();
gfx::Point pointer_position(10, 10);
gfx::Vector2dF delta(20, 20);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(pointer_position, delta,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(overflow));
EXPECT_EQ(TargetSnapAreaElementIds(),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
GetInputHandler().ScrollUpdate(
UpdateState(pointer_position, delta, ui::ScrollInputType::kWheel));
EXPECT_EQ(TargetSnapAreaElementIds(),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
GetInputHandler().ScrollEnd(true);
EXPECT_TRUE(
GetInputHandler().animating_for_snap_for_testing(overflow->element_id()));
EXPECT_EQ(TargetSnapAreaElementIds(),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
base::TimeTicks start_time = base::TimeTicks() + base::Milliseconds(100);
BeginImplFrameAndAnimate(begin_frame_args, start_time);
BeginImplFrameAndAnimate(begin_frame_args,
start_time + base::Milliseconds(50));
BeginImplFrameAndAnimate(begin_frame_args,
start_time + base::Milliseconds(1000));
EXPECT_FALSE(
GetInputHandler().animating_for_snap_for_testing(overflow->element_id()));
EXPECT_POINTF_EQ(gfx::PointF(50, 50), CurrentScrollOffset(overflow));
EXPECT_EQ(TargetSnapAreaElementIds(ElementId(10), ElementId(10)),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
}
TEST_P(LayerTreeHostImplTest, SnapAfterEmptyScroll) {
CreateLayerForSnapping();
gfx::Point pointer_position(10, 10);
gfx::Vector2dF y_delta(0, 20);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(pointer_position, y_delta,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
GetInputHandler().ScrollEnd(true);
}
TEST_P(LayerTreeHostImplTest, ScrollSnapAfterAnimatedScroll) {
LayerImpl* overflow = CreateLayerForSnapping();
gfx::Point pointer_position(10, 10);
gfx::Vector2dF delta(20, 20);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(pointer_position, delta,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
GetInputHandler().ScrollUpdate(AnimatedUpdateState(pointer_position, delta));
GetInputHandler().ScrollEnd();
EXPECT_EQ(overflow->scroll_tree_index(),
host_impl_->CurrentlyScrollingNode()->id);
EXPECT_EQ(TargetSnapAreaElementIds(),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
base::TimeTicks start_time = base::TimeTicks() + base::Milliseconds(100);
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
BeginImplFrameAndAnimate(begin_frame_args, start_time);
BeginImplFrameAndAnimate(begin_frame_args,
start_time + base::Milliseconds(50));
EXPECT_FALSE(
GetInputHandler().animating_for_snap_for_testing(overflow->element_id()));
gfx::PointF current_offset = CurrentScrollOffset(overflow);
EXPECT_LT(0, current_offset.x());
EXPECT_GT(20, current_offset.x());
EXPECT_LT(0, current_offset.y());
EXPECT_GT(20, current_offset.y());
EXPECT_EQ(TargetSnapAreaElementIds(),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
BeginImplFrameAndAnimate(begin_frame_args,
start_time + base::Milliseconds(1000));
EXPECT_TRUE(
GetInputHandler().animating_for_snap_for_testing(overflow->element_id()));
EXPECT_EQ(TargetSnapAreaElementIds(),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
BeginImplFrameAndAnimate(begin_frame_args,
start_time + base::Milliseconds(1500));
EXPECT_POINTF_EQ(gfx::PointF(50, 50), CurrentScrollOffset(overflow));
EXPECT_FALSE(
GetInputHandler().animating_for_snap_for_testing(overflow->element_id()));
EXPECT_EQ(TargetSnapAreaElementIds(ElementId(10), ElementId(10)),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
}
TEST_P(LayerTreeHostImplTest, SnapAnimationCancelledByScroll) {
LayerImpl* overflow = CreateLayerForSnapping();
gfx::Point pointer_position(10, 10);
gfx::Vector2dF x_delta(20, 0);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(pointer_position, x_delta,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(overflow));
GetInputHandler().ScrollUpdate(
UpdateState(pointer_position, x_delta, ui::ScrollInputType::kWheel));
EXPECT_FALSE(
GetInputHandler().animating_for_snap_for_testing(overflow->element_id()));
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
GetInputHandler().ScrollEnd(true);
base::TimeTicks start_time = base::TimeTicks() + base::Milliseconds(100);
BeginImplFrameAndAnimate(begin_frame_args, start_time);
BeginImplFrameAndAnimate(begin_frame_args,
start_time + base::Milliseconds(100));
EXPECT_TRUE(
GetInputHandler().animating_for_snap_for_testing(overflow->element_id()));
EXPECT_EQ(TargetSnapAreaElementIds(),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
gfx::PointF current_offset = CurrentScrollOffset(overflow);
EXPECT_GT(50, current_offset.x());
EXPECT_LT(20, current_offset.x());
EXPECT_EQ(0, current_offset.y());
auto begin_state =
BeginState(pointer_position, x_delta, ui::ScrollInputType::kWheel);
begin_state->data()->delta_granularity =
ui::ScrollGranularity::kScrollByPrecisePixel;
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(begin_state.get(), ui::ScrollInputType::kWheel)
.thread);
EXPECT_FALSE(
GetInputHandler().animating_for_snap_for_testing(overflow->element_id()));
EXPECT_EQ(TargetSnapAreaElementIds(),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
BeginImplFrameAndAnimate(begin_frame_args,
start_time + base::Milliseconds(150));
EXPECT_POINTF_EQ(current_offset, CurrentScrollOffset(overflow));
BeginImplFrameAndAnimate(begin_frame_args,
start_time + base::Milliseconds(1000));
EXPECT_EQ(TargetSnapAreaElementIds(),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
}
TEST_P(LayerTreeHostImplTest,
SnapAnimationShouldNotStartWhenScrollEndsAtSnapTarget) {
LayerImpl* overflow = CreateLayerForSnapping();
gfx::Point pointer_position(10, 10);
gfx::Vector2dF x_delta(50, 0);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(pointer_position, x_delta,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(overflow));
GetInputHandler().ScrollUpdate(
UpdateState(pointer_position, x_delta, ui::ScrollInputType::kWheel));
EXPECT_FALSE(
GetInputHandler().animating_for_snap_for_testing(overflow->element_id()));
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
GetInputHandler().ScrollEnd(true);
EXPECT_FALSE(
GetInputHandler().animating_for_snap_for_testing(overflow->element_id()));
EXPECT_EQ(TargetSnapAreaElementIds(ElementId(10), ElementId()),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
base::TimeTicks start_time = base::TimeTicks() + base::Milliseconds(100);
BeginImplFrameAndAnimate(begin_frame_args, start_time);
EXPECT_FALSE(
GetInputHandler().animating_for_snap_for_testing(overflow->element_id()));
BeginImplFrameAndAnimate(begin_frame_args,
start_time + base::Milliseconds(100));
EXPECT_FALSE(
GetInputHandler().animating_for_snap_for_testing(overflow->element_id()));
EXPECT_POINTF_EQ(gfx::PointF(50, 0), CurrentScrollOffset(overflow));
EXPECT_EQ(TargetSnapAreaElementIds(ElementId(10), ElementId()),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
}
TEST_P(LayerTreeHostImplTest,
GetSnapFlingInfoAndSetAnimatingSnapTargetWhenZoomed) {
LayerImpl* overflow = CreateLayerForSnapping();
host_impl_->active_tree()->PushPageScaleFromMainThread(0.2f, 0.1f, 5);
gfx::Point pointer_position(2, 2);
gfx::Vector2dF delta(4, 4);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(pointer_position, delta,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(overflow));
InputHandlerScrollResult result = GetInputHandler().ScrollUpdate(
UpdateState(pointer_position, delta, ui::ScrollInputType::kWheel));
EXPECT_POINTF_EQ(gfx::PointF(20, 20), CurrentScrollOffset(overflow));
EXPECT_POINTF_EQ(gfx::PointF(4, 4), result.current_visual_offset);
gfx::PointF initial_offset, target_offset;
EXPECT_TRUE(GetInputHandler().GetSnapFlingInfoAndSetAnimatingSnapTarget(
gfx::Vector2dF(1, 1), gfx::Vector2dF(10, 10), &initial_offset,
&target_offset));
EXPECT_TRUE(
GetInputHandler().animating_for_snap_for_testing(overflow->element_id()));
EXPECT_POINTF_EQ(gfx::PointF(4, 4), initial_offset);
EXPECT_POINTF_EQ(gfx::PointF(10, 10), target_offset);
EXPECT_EQ(TargetSnapAreaElementIds(),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
GetInputHandler().ScrollUpdate(UpdateState(
pointer_position, gfx::Vector2dF(6, 6), ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEndForSnapFling(true );
EXPECT_FALSE(
GetInputHandler().animating_for_snap_for_testing(overflow->element_id()));
EXPECT_EQ(TargetSnapAreaElementIds(ElementId(10), ElementId(10)),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
}
TEST_P(LayerTreeHostImplTest, SnapFlingAnimationEndWithoutFinishing) {
LayerImpl* overflow = CreateLayerForSnapping();
host_impl_->active_tree()->PushPageScaleFromMainThread(0.2f, 0.1f, 5.f);
gfx::Point pointer_position(2, 2);
gfx::Vector2dF delta(4, 4);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(pointer_position, delta,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(overflow));
InputHandlerScrollResult result = GetInputHandler().ScrollUpdate(
UpdateState(pointer_position, delta, ui::ScrollInputType::kWheel));
EXPECT_POINTF_EQ(gfx::PointF(20, 20), CurrentScrollOffset(overflow));
EXPECT_POINTF_EQ(gfx::PointF(4, 4), result.current_visual_offset);
gfx::PointF initial_offset, target_offset;
EXPECT_TRUE(GetInputHandler().GetSnapFlingInfoAndSetAnimatingSnapTarget(
gfx::Vector2dF(1, 1), gfx::Vector2dF(10, 10), &initial_offset,
&target_offset));
EXPECT_TRUE(
GetInputHandler().animating_for_snap_for_testing(overflow->element_id()));
EXPECT_POINTF_EQ(gfx::PointF(4, 4), initial_offset);
EXPECT_POINTF_EQ(gfx::PointF(10, 10), target_offset);
EXPECT_EQ(TargetSnapAreaElementIds(),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
GetInputHandler().ScrollEndForSnapFling(false );
EXPECT_TRUE(
GetInputHandler().animating_for_snap_for_testing(overflow->element_id()));
EXPECT_EQ(TargetSnapAreaElementIds(),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
}
TEST_P(LayerTreeHostImplTest, NativeFlingInSnapArea) {
gfx::Size view_size(100, 100);
gfx::Size overflow_size(100, 1000);
gfx::RectF snap_area_1(0, 0, 100, 700);
gfx::RectF snap_area_2(0, 700, 100, 100);
SetupViewportLayersInnerScrolls(view_size, view_size);
LayerImpl* overflow =
AddScrollableLayer(OuterViewportScrollLayer(), view_size, overflow_size);
SnapContainerData container(
ScrollSnapType(false, SnapAxis::kY, SnapStrictness::kMandatory),
gfx::RectF(0, 0, 100, 100), gfx::PointF(0, 900));
ScrollSnapAlign start = ScrollSnapAlign(SnapAlignment::kStart);
container.AddSnapAreaData(
SnapAreaData(start, snap_area_1, false, false, ElementId(10)));
container.AddSnapAreaData(
SnapAreaData(start, snap_area_2, false, false, ElementId(20)));
GetScrollNode(overflow)->snap_container_data.emplace(container);
DrawFrame();
auto& handler = GetInputHandler();
gfx::PointF initial_offset, target_offset;
gfx::Point position(50, 50);
ui::ScrollInputType type = ui::ScrollInputType::kTouchscreen;
handler.ScrollBegin(&*BeginState(position, gfx::Vector2dF(0, 100), type),
type);
handler.ScrollUpdate(UpdateState(position, gfx::Vector2dF(0, 100), type));
EXPECT_POINTF_EQ(gfx::PointF(0, 100), CurrentScrollOffset(overflow));
EXPECT_FALSE(handler.GetSnapFlingInfoAndSetAnimatingSnapTarget(
gfx::Vector2dF(0, 10), gfx::Vector2dF(0, 400), &initial_offset,
&target_offset));
EXPECT_FALSE(handler.animating_for_snap_for_testing(overflow->element_id()));
handler.ScrollUpdate(UpdateState(position, gfx::Vector2dF(0, 450), type));
EXPECT_POINTF_EQ(gfx::PointF(0, 550), CurrentScrollOffset(overflow));
EXPECT_TRUE(handler.GetSnapFlingInfoAndSetAnimatingSnapTarget(
gfx::Vector2dF(0, 100), gfx::Vector2dF(0, 4000), &initial_offset,
&target_offset));
EXPECT_TRUE(handler.animating_for_snap_for_testing(overflow->element_id()));
EXPECT_POINTF_EQ(gfx::PointF(0, 550), initial_offset);
EXPECT_POINTF_EQ(gfx::PointF(0, 600), target_offset);
GetInputHandler().ScrollEndForSnapFling(false );
EXPECT_POINTF_EQ(gfx::PointF(0, 550), CurrentScrollOffset(overflow));
handler.ScrollBegin(&*BeginState(position, gfx::Vector2dF(0, 100), type),
type);
EXPECT_TRUE(handler.GetSnapFlingInfoAndSetAnimatingSnapTarget(
gfx::Vector2dF(0, 10), gfx::Vector2dF(0, 400), &initial_offset,
&target_offset));
EXPECT_TRUE(handler.animating_for_snap_for_testing(overflow->element_id()));
EXPECT_POINTF_EQ(gfx::PointF(0, 550), initial_offset);
EXPECT_POINTF_EQ(gfx::PointF(0, 700), target_offset);
}
TEST_P(LayerTreeHostImplTest, OverscrollBehaviorPreventsPropagation) {
const gfx::Size kViewportSize(100, 100);
const gfx::Size kContentSize(200, 200);
SetupViewportLayersOuterScrolls(kViewportSize, kContentSize);
LayerImpl* scroll_layer = OuterViewportScrollLayer();
gfx::Size overflow_size(400, 400);
LayerImpl* overflow = AddScrollableLayer(OuterViewportScrollLayer(),
gfx::Size(100, 100), overflow_size);
SetScrollOffset(scroll_layer, gfx::PointF(30, 30));
DrawFrame();
gfx::Point pointer_position(50, 50);
gfx::Vector2dF x_delta(-10, 0);
gfx::Vector2dF y_delta(0, -10);
gfx::Vector2dF diagonal_delta(-10, -10);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(pointer_position, x_delta,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
EXPECT_POINTF_EQ(gfx::PointF(30, 30), CurrentScrollOffset(scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(overflow));
GetInputHandler().ScrollUpdate(
UpdateState(pointer_position, x_delta, ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(gfx::PointF(20, 30), CurrentScrollOffset(scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(overflow));
GetScrollNode(overflow)->overscroll_behavior = OverscrollBehavior(
OverscrollBehavior::Type::kContain, OverscrollBehavior::Type::kAuto);
DrawFrame();
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(pointer_position, x_delta,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
EXPECT_POINTF_EQ(gfx::PointF(20, 30), CurrentScrollOffset(scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(overflow));
GetInputHandler().ScrollUpdate(
UpdateState(pointer_position, x_delta, ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(gfx::PointF(20, 30), CurrentScrollOffset(scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(overflow));
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(pointer_position, y_delta,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
EXPECT_POINTF_EQ(gfx::PointF(20, 30), CurrentScrollOffset(scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(overflow));
GetInputHandler().ScrollUpdate(
UpdateState(pointer_position, y_delta, ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(gfx::PointF(20, 20), CurrentScrollOffset(scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(overflow));
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(pointer_position, diagonal_delta,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
EXPECT_POINTF_EQ(gfx::PointF(20, 20), CurrentScrollOffset(scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(overflow));
GetInputHandler().ScrollUpdate(UpdateState(pointer_position, diagonal_delta,
ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(gfx::PointF(20, 20), CurrentScrollOffset(scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(overflow));
GetScrollNode(overflow)->overscroll_behavior = OverscrollBehavior(
OverscrollBehavior::Type::kAuto, OverscrollBehavior::Type::kContain);
DrawFrame();
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(pointer_position, x_delta,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
EXPECT_POINTF_EQ(gfx::PointF(20, 20), CurrentScrollOffset(scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(overflow));
GetInputHandler().ScrollUpdate(
UpdateState(pointer_position, x_delta, ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(gfx::PointF(10, 20), CurrentScrollOffset(scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(overflow));
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(pointer_position, y_delta,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
EXPECT_POINTF_EQ(gfx::PointF(10, 20), CurrentScrollOffset(scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(overflow));
GetInputHandler().ScrollUpdate(
UpdateState(pointer_position, y_delta, ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(gfx::PointF(10, 20), CurrentScrollOffset(scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(overflow));
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(pointer_position, diagonal_delta,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
EXPECT_POINTF_EQ(gfx::PointF(10, 20), CurrentScrollOffset(scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(overflow));
GetInputHandler().ScrollUpdate(UpdateState(pointer_position, diagonal_delta,
ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(gfx::PointF(10, 20), CurrentScrollOffset(scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(overflow));
GetScrollNode(overflow)->overscroll_behavior = OverscrollBehavior(
OverscrollBehavior::Type::kContain, OverscrollBehavior::Type::kContain);
DrawFrame();
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(pointer_position, x_delta,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
EXPECT_POINTF_EQ(gfx::PointF(10, 20), CurrentScrollOffset(scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(overflow));
GetInputHandler().ScrollUpdate(
UpdateState(pointer_position, x_delta, ui::ScrollInputType::kWheel));
GetInputHandler().ScrollUpdate(
UpdateState(pointer_position, -x_delta, ui::ScrollInputType::kWheel));
GetInputHandler().ScrollUpdate(
UpdateState(pointer_position, y_delta, ui::ScrollInputType::kWheel));
GetInputHandler().ScrollUpdate(
UpdateState(pointer_position, -y_delta, ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(gfx::PointF(10, 20), CurrentScrollOffset(scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(10, 10), CurrentScrollOffset(overflow));
}
TEST_P(LayerTreeHostImplTest, ScrollWithUserUnscrollableLayers) {
const gfx::Size kViewportSize(100, 100);
const gfx::Size kContentSize(200, 200);
SetupViewportLayersOuterScrolls(kViewportSize, kContentSize);
LayerImpl* scroll_layer = OuterViewportScrollLayer();
gfx::Size overflow_size(400, 400);
LayerImpl* overflow =
AddScrollableLayer(scroll_layer, gfx::Size(100, 100), overflow_size);
DrawFrame();
gfx::Point scroll_position(10, 10);
gfx::Vector2dF scroll_delta(10, 10);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(scroll_position, scroll_delta,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(overflow));
GetInputHandler().ScrollUpdate(
UpdateState(scroll_position, scroll_delta, ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(10, 10), CurrentScrollOffset(overflow));
GetScrollNode(overflow)->user_scrollable_horizontal = false;
DrawFrame();
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(scroll_position, scroll_delta,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(10, 10), CurrentScrollOffset(overflow));
GetInputHandler().ScrollUpdate(
UpdateState(scroll_position, scroll_delta, ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(10, 20), CurrentScrollOffset(overflow));
GetScrollNode(overflow)->user_scrollable_vertical = false;
DrawFrame();
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(scroll_position, scroll_delta,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(10, 20), CurrentScrollOffset(overflow));
GetInputHandler().ScrollUpdate(
UpdateState(scroll_position, scroll_delta, ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(gfx::PointF(10, 10), CurrentScrollOffset(scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(10, 20), CurrentScrollOffset(overflow));
}
TEST_P(LayerTreeHostImplTest, ScrollNodeWithoutScrollLayer) {
SetupViewportLayersInnerScrolls(gfx::Size(100, 100), gfx::Size(200, 200));
ScrollNode* scroll_node = host_impl_->OuterViewportScrollNode();
scroll_node->element_id = ElementId(42);
DrawFrame();
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(25, 25), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
}
TEST_F(CommitToPendingTreeLayerTreeHostImplTest,
AnimationSchedulingPendingTree) {
CreatePendingTree();
auto* root =
SetupRootLayer<LayerImpl>(host_impl_->pending_tree(), gfx::Size(50, 50));
root->SetNeedsPushProperties();
auto* child = AddLayer<LayerImpl>(host_impl_->pending_tree());
child->SetBounds(gfx::Size(10, 10));
child->SetDrawsContent(true);
child->SetNeedsPushProperties();
host_impl_->pending_tree()->SetElementIdsForTesting();
CopyProperties(root, child);
CreateTransformNode(child);
scoped_refptr<Animation> animation =
Animation::Create(AnimationIdProvider::NextAnimationId());
timeline()->AttachAnimation(animation);
animation->AttachElement(child->element_id());
AddAnimatedTransformToAnimation(animation.get(), 10.0, 3, 0);
animation->GetKeyframeModel(TargetProperty::TRANSFORM)
->set_affects_active_elements(false);
UpdateDrawProperties(host_impl_->pending_tree());
EXPECT_FALSE(did_request_next_frame_);
EXPECT_FALSE(did_request_redraw_);
EXPECT_FALSE(did_request_commit_);
host_impl_->AnimatePendingTreeAfterCommit();
EXPECT_TRUE(did_request_next_frame_);
EXPECT_FALSE(did_request_redraw_);
EXPECT_FALSE(did_request_commit_);
did_request_next_frame_ = false;
did_request_redraw_ = false;
did_request_commit_ = false;
host_impl_->ActivateSyncTree();
EXPECT_TRUE(did_request_next_frame_);
EXPECT_FALSE(did_request_redraw_);
EXPECT_FALSE(did_request_commit_);
}
TEST_F(CommitToPendingTreeLayerTreeHostImplTest,
AnimationSchedulingActiveTree) {
LayerImpl* root = SetupDefaultRootLayer(gfx::Size(50, 50));
LayerImpl* child = AddLayerInActiveTree();
child->SetBounds(gfx::Size(10, 10));
child->SetDrawsContent(true);
host_impl_->active_tree()->SetElementIdsForTesting();
CopyProperties(root, child);
CreateTransformNode(child);
gfx::TransformOperations start;
start.AppendTranslate(6, 7, 0);
gfx::TransformOperations end;
end.AppendTranslate(8, 9, 0);
AddAnimatedTransformToElementWithAnimation(child->element_id(), timeline(),
4.0, start, end);
UpdateDrawProperties(host_impl_->active_tree());
base::TimeTicks now = base::TimeTicks::Now();
host_impl_->WillBeginImplFrame(
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2, now));
EXPECT_TRUE(did_request_next_frame_);
EXPECT_TRUE(did_request_redraw_);
EXPECT_FALSE(did_request_commit_);
did_request_next_frame_ = false;
did_request_redraw_ = false;
did_request_commit_ = false;
host_impl_->ActivateAnimations();
EXPECT_TRUE(did_request_next_frame_);
EXPECT_FALSE(did_request_redraw_);
EXPECT_FALSE(did_request_commit_);
did_request_next_frame_ = false;
did_request_redraw_ = false;
did_request_commit_ = false;
host_impl_->Animate();
EXPECT_TRUE(did_request_next_frame_);
EXPECT_TRUE(did_request_redraw_);
EXPECT_FALSE(did_request_commit_);
}
TEST_F(CommitToActiveTreeLayerTreeHostImplTest,
AnimationSchedulingCommitToActiveTree) {
auto* root = SetupDefaultRootLayer(gfx::Size(50, 50));
auto* child = AddLayerInActiveTree();
child->SetBounds(gfx::Size(10, 10));
child->SetDrawsContent(true);
host_impl_->active_tree()->SetElementIdsForTesting();
CopyProperties(root, child);
CreateTransformNode(child);
AddAnimatedTransformToElementWithAnimation(child->element_id(), timeline(),
10.0, 3, 0);
UpdateDrawProperties(host_impl_->active_tree());
EXPECT_FALSE(did_request_next_frame_);
EXPECT_FALSE(did_request_redraw_);
EXPECT_FALSE(did_request_commit_);
host_impl_->CommitComplete();
EXPECT_TRUE(did_request_next_frame_);
EXPECT_TRUE(did_request_redraw_);
EXPECT_FALSE(did_request_commit_);
host_impl_->ReleaseLayerTreeFrameSink();
host_impl_ = nullptr;
}
TEST_P(ClientModeLayerTreeHostImplTest, AnimationSchedulingOnLayerDestruction) {
LayerImpl* root = SetupDefaultRootLayer(gfx::Size(50, 50));
LayerImpl* child = AddLayerInActiveTree();
child->SetBounds(gfx::Size(10, 10));
child->SetDrawsContent(true);
host_impl_->active_tree()->SetElementIdsForTesting();
CopyProperties(root, child);
CreateTransformNode(child);
gfx::TransformOperations start;
start.AppendTranslate(6, 7, 0);
gfx::TransformOperations end;
end.AppendTranslate(8, 9, 0);
scoped_refptr<Animation> animation =
Animation::Create(AnimationIdProvider::NextAnimationId());
timeline()->AttachAnimation(animation);
animation->AttachElement(child->element_id());
int keyframe_model_id =
AddAnimatedTransformToAnimation(animation.get(), 4.0, start, end);
UpdateDrawProperties(host_impl_->active_tree());
base::TimeTicks now = base::TimeTicks::Now();
host_impl_->WillBeginImplFrame(
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2, now));
EXPECT_TRUE(did_request_next_frame_);
did_request_next_frame_ = false;
host_impl_->ActivateAnimations();
EXPECT_TRUE(did_request_next_frame_);
did_request_next_frame_ = false;
host_impl_->Animate();
EXPECT_TRUE(did_request_next_frame_);
did_request_next_frame_ = false;
animation->RemoveKeyframeModel(keyframe_model_id);
host_impl_->ActivateAnimations();
EXPECT_FALSE(did_request_next_frame_);
host_impl_->active_tree()->property_trees()->clear();
host_impl_->UpdateAnimationState(true);
EXPECT_FALSE(did_request_next_frame_);
host_impl_->Animate();
EXPECT_FALSE(did_request_next_frame_);
}
class IncompleteRecordingLayer : public LayerImpl {
public:
static std::unique_ptr<IncompleteRecordingLayer> Create(
LayerTreeImpl* tree_impl,
int id) {
return std::make_unique<IncompleteRecordingLayer>(tree_impl, id);
}
IncompleteRecordingLayer(LayerTreeImpl* layer_tree_impl, int id)
: LayerImpl(layer_tree_impl, id) {}
void AppendQuads(const AppendQuadsContext& context,
viz::CompositorRenderPass* render_pass,
AppendQuadsData* append_quads_data) override {
append_quads_data->checkerboarded_needs_record = true;
append_quads_data->visible_layer_area += 200;
}
};
TEST_P(CompositorFrameProducingLayerTreeHostImplTest,
ScrollCheckerboardsIncompleteRecordingPerScroll) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeatureWithParameters(
features::kNewContentForCheckerboardedScrolls,
{{"mode", features::kNewContentForCheckerboardedScrollsPerScroll}});
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 0.25f, 4);
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(500, 500);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
LayerImpl* outer_scroll_layer = OuterViewportScrollLayer();
outer_scroll_layer->SetDrawsContent(true);
LayerImpl* inner_scroll_layer = InnerViewportScrollLayer();
inner_scroll_layer->SetDrawsContent(true);
auto* scroll_layer =
AddLayer<IncompleteRecordingLayer>(host_impl_->active_tree());
CopyProperties(inner_scroll_layer, scroll_layer);
scroll_layer->SetBounds(gfx::Size(500, 500));
scroll_layer->SetDrawsContent(true);
scroll_layer->SetHitTestOpaqueness(HitTestOpaqueness::kTransparent);
host_impl_->active_tree()->SetElementIdsForTesting();
UpdateDrawProperties(host_impl_->active_tree());
DrawFrame();
EXPECT_FALSE(host_impl_->PrioritizeNewContentDueToCheckerboarding());
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(250, 250), gfx::Vector2dF(),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
DrawFrame();
EXPECT_FALSE(host_impl_->PrioritizeNewContentDueToCheckerboarding());
gfx::Vector2dF scroll_delta(0, 10);
GetInputHandler().ScrollUpdate(UpdateState(gfx::Point(10, 10), scroll_delta,
ui::ScrollInputType::kWheel));
host_impl_->SetFullViewportDamage();
DrawFrame();
EXPECT_TRUE(host_impl_->PrioritizeNewContentDueToCheckerboarding());
GetInputHandler().ScrollEnd();
EXPECT_FALSE(host_impl_->PrioritizeNewContentDueToCheckerboarding());
}
TEST_P(CompositorFrameProducingLayerTreeHostImplTest,
ScrollCheckerboardsIncompleteRecordingPerFrame) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeatureWithParameters(
features::kNewContentForCheckerboardedScrolls,
{{"mode", features::kNewContentForCheckerboardedScrollsPerFrame}});
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 0.25f, 4);
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(500, 500);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
LayerImpl* outer_scroll_layer = OuterViewportScrollLayer();
outer_scroll_layer->SetDrawsContent(true);
LayerImpl* inner_scroll_layer = InnerViewportScrollLayer();
inner_scroll_layer->SetDrawsContent(true);
auto* scroll_layer =
AddLayer<IncompleteRecordingLayer>(host_impl_->active_tree());
CopyProperties(inner_scroll_layer, scroll_layer);
scroll_layer->SetBounds(gfx::Size(500, 500));
scroll_layer->SetDrawsContent(true);
scroll_layer->SetHitTestOpaqueness(HitTestOpaqueness::kTransparent);
host_impl_->active_tree()->SetElementIdsForTesting();
UpdateDrawProperties(host_impl_->active_tree());
DrawFrame();
EXPECT_FALSE(host_impl_->PrioritizeNewContentDueToCheckerboarding());
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(250, 250), gfx::Vector2dF(),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
DrawFrame();
EXPECT_FALSE(host_impl_->PrioritizeNewContentDueToCheckerboarding());
gfx::Vector2dF scroll_delta(0, 10);
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree_mutable()
.SetScrollingContentsCullRect(
host_impl_->CurrentlyScrollingNode()->element_id, gfx::Rect(50, 50));
GetInputHandler().ScrollUpdate(UpdateState(gfx::Point(10, 10), scroll_delta,
ui::ScrollInputType::kWheel));
EXPECT_TRUE(host_impl_->PrioritizeNewContentDueToCheckerboarding());
host_impl_->SetFullViewportDamage();
DrawFrame();
EXPECT_TRUE(host_impl_->PrioritizeNewContentDueToCheckerboarding());
}
TEST_P(LayerTreeHostImplTest,
ScrollCheckerboardsIncompleteRecordingOutOfScreen) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeatureWithParameters(
features::kNewContentForCheckerboardedScrolls,
{{"mode", features::kNewContentForCheckerboardedScrollsPerFrame}});
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 0.25f, 4);
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(500, 500);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
LayerImpl* outer_scroll_layer = OuterViewportScrollLayer();
outer_scroll_layer->SetDrawsContent(true);
LayerImpl* inner_scroll_layer = InnerViewportScrollLayer();
inner_scroll_layer->SetDrawsContent(true);
auto* scroll_layer =
AddLayer<IncompleteRecordingLayer>(host_impl_->active_tree());
CopyProperties(inner_scroll_layer, scroll_layer);
scroll_layer->SetBounds(gfx::Size(250, 250));
scroll_layer->SetDrawsContent(true);
scroll_layer->SetHitTestOpaqueness(HitTestOpaqueness::kTransparent);
host_impl_->active_tree()->SetElementIdsForTesting();
UpdateDrawProperties(host_impl_->active_tree());
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(260, 260), gfx::Vector2dF(),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
const gfx::Vector2dF scroll_delta(0, 20);
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree_mutable()
.SetScrollingContentsCullRect(
host_impl_->CurrentlyScrollingNode()->element_id,
gfx::Rect(scroll_delta.x(), scroll_delta.y(), viewport_size.width(),
viewport_size.height()));
GetInputHandler().ScrollUpdate(UpdateState(gfx::Point(10, 10), scroll_delta,
ui::ScrollInputType::kWheel));
EXPECT_FALSE(host_impl_->PrioritizeNewContentDueToCheckerboarding());
}
TEST_P(LayerTreeHostImplTest, ImplPinchZoom) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
LayerImpl* scroll_layer = InnerViewportScrollLayer();
EXPECT_EQ(gfx::Size(50, 50), root_layer()->bounds());
float min_page_scale = 1, max_page_scale = 4;
float page_scale_factor = 1;
{
host_impl_->active_tree()->PushPageScaleFromMainThread(
page_scale_factor, min_page_scale, max_page_scale);
host_impl_->active_tree()->SetPageScaleOnActiveTree(page_scale_factor);
SetScrollOffsetDelta(scroll_layer, gfx::Vector2d());
float page_scale_delta = 2;
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(50, 50), gfx::Vector2dF(),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().PinchGestureBegin(gfx::Point(50, 50),
ui::ScrollInputType::kWheel);
GetInputHandler().PinchGestureUpdate(page_scale_delta, gfx::Point(50, 50));
GetInputHandler().PinchGestureEnd(gfx::Point(50, 50));
GetInputHandler().ScrollEnd();
EXPECT_FALSE(did_request_next_frame_);
EXPECT_TRUE(did_request_redraw_);
EXPECT_TRUE(did_request_commit_);
EXPECT_EQ(gfx::Size(50, 50), root_layer()->bounds());
std::unique_ptr<CompositorCommitData> commit_data =
host_impl_->ProcessCompositorDeltas(
nullptr);
EXPECT_EQ(commit_data->page_scale_delta, page_scale_delta);
EXPECT_EQ(gfx::PointF(75.0, 75.0), MaxScrollOffset(scroll_layer));
ClearMainThreadDeltasForTesting(host_impl_.get());
}
{
host_impl_->active_tree()->PushPageScaleFromMainThread(
page_scale_factor, min_page_scale, max_page_scale);
host_impl_->active_tree()->SetPageScaleOnActiveTree(page_scale_factor);
SetScrollOffsetDelta(scroll_layer, gfx::Vector2d());
float page_scale_delta = 2;
GetInputHandler().ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().PinchGestureBegin(gfx::Point(),
ui::ScrollInputType::kWheel);
GetInputHandler().PinchGestureUpdate(page_scale_delta, gfx::Point());
GetInputHandler().PinchGestureEnd(gfx::Point());
GetInputHandler().ScrollEnd();
gfx::Vector2d scroll_delta(0, 10);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(5, 5), scroll_delta,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(), scroll_delta, ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
std::unique_ptr<CompositorCommitData> commit_data =
host_impl_->ProcessCompositorDeltas(
nullptr);
EXPECT_TRUE(ScrollInfoContains(
*commit_data.get(), scroll_layer->element_id(),
gfx::Vector2dF(0, scroll_delta.y() / page_scale_delta)));
}
}
TEST_P(LayerTreeHostImplTest, ViewportScrollbarGeometry) {
const gfx::Size viewport_size(412, 604);
const gfx::Size content_size(981, 1439);
const float minimum_scale =
viewport_size.width() / static_cast<float>(content_size.width());
const gfx::Size outer_viewport_size = content_size;
LayerTreeImpl* active_tree = host_impl_->active_tree();
active_tree->PushPageScaleFromMainThread(1, minimum_scale, 4);
SetupViewportLayersInnerScrolls(viewport_size, content_size);
LayerImpl* scroll = OuterViewportScrollLayer();
ASSERT_EQ(GetScrollNode(scroll)->container_bounds, outer_viewport_size);
scroll->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
ClipNode* outer_clip = host_impl_->active_tree()->OuterViewportClipNode();
ASSERT_EQ(gfx::SizeF(outer_viewport_size), outer_clip->clip.size());
auto* v_scrollbar = AddLayer<PaintedScrollbarLayerImpl>(
active_tree, ScrollbarOrientation::kVertical, false, true);
auto* h_scrollbar = AddLayer<PaintedScrollbarLayerImpl>(
active_tree, ScrollbarOrientation::kHorizontal, false, true);
SetupScrollbarLayer(scroll, v_scrollbar);
SetupScrollbarLayer(scroll, h_scrollbar);
host_impl_->active_tree()->DidBecomeActive();
host_impl_->active_tree()->SetPageScaleOnActiveTree(0);
EXPECT_FALSE(v_scrollbar->CanScrollOrientation());
EXPECT_FALSE(h_scrollbar->CanScrollOrientation());
host_impl_->active_tree()->SetPageScaleOnActiveTree(minimum_scale * 1.05f);
EXPECT_TRUE(v_scrollbar->CanScrollOrientation());
EXPECT_TRUE(h_scrollbar->CanScrollOrientation());
}
TEST_P(LayerTreeHostImplTest, ViewportScrollOrder) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 0.25f, 4);
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(500, 500);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
LayerImpl* outer_scroll_layer = OuterViewportScrollLayer();
outer_scroll_layer->SetDrawsContent(true);
LayerImpl* inner_scroll_layer = InnerViewportScrollLayer();
inner_scroll_layer->SetDrawsContent(true);
UpdateDrawProperties(host_impl_->active_tree());
EXPECT_POINTF_EQ(gfx::PointF(500, 500), MaxScrollOffset(outer_scroll_layer));
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(250, 250), gfx::Vector2dF(),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().PinchGestureBegin(gfx::Point(),
ui::ScrollInputType::kWheel);
GetInputHandler().PinchGestureUpdate(2, gfx::Point(0, 0));
GetInputHandler().PinchGestureEnd(gfx::Point(0, 0));
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(outer_scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(inner_scroll_layer));
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(0, 0), gfx::Vector2dF(100, 100),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(0, 0), gfx::Vector2dF(100, 100),
ui::ScrollInputType::kTouchscreen));
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(gfx::PointF(50, 50),
CurrentScrollOffset(inner_scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(outer_scroll_layer));
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(0, 0), gfx::Vector2dF(1000, 1000),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(0, 0), gfx::Vector2dF(1000, 1000),
ui::ScrollInputType::kTouchscreen));
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(gfx::PointF(250, 250),
CurrentScrollOffset(inner_scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(300, 300),
CurrentScrollOffset(outer_scroll_layer));
}
TEST_P(LayerTreeHostImplTest, ScrollViewportWithFractionalAmounts) {
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 1, 2);
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(500, 500);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
LayerImpl* outer_scroll_layer = OuterViewportScrollLayer();
outer_scroll_layer->SetDrawsContent(true);
LayerImpl* inner_scroll_layer = InnerViewportScrollLayer();
inner_scroll_layer->SetDrawsContent(true);
UpdateDrawProperties(host_impl_->active_tree());
EXPECT_POINTF_EQ(gfx::PointF(500, 500), MaxScrollOffset(outer_scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(outer_scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(inner_scroll_layer));
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(250, 250), gfx::Vector2dF(0.125f, 0.125f),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(250, 250), gfx::Vector2dF(0.125f, 0.125f),
ui::ScrollInputType::kTouchscreen));
EXPECT_POINTF_EQ(gfx::PointF(0.125f, 0.125f),
CurrentScrollOffset(outer_scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(inner_scroll_layer));
GetInputHandler().ScrollEnd();
host_impl_->active_tree()->PushPageScaleFromMainThread(2, 1, 2);
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(250, 250), gfx::Vector2dF(0.5f, 0.5f),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(250, 250), gfx::Vector2dF(0.5f, 0.5f),
ui::ScrollInputType::kTouchscreen));
EXPECT_POINTF_EQ(gfx::PointF(0.125f, 0.125f),
CurrentScrollOffset(outer_scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0.25f, 0.25f),
CurrentScrollOffset(inner_scroll_layer));
GetInputHandler().ScrollEnd();
}
TEST_P(LayerTreeHostImplTest, ScrollDuringPinchGesture) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 1, 2);
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(500, 500);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
LayerImpl* outer_scroll_layer = OuterViewportScrollLayer();
outer_scroll_layer->SetDrawsContent(true);
LayerImpl* inner_scroll_layer = InnerViewportScrollLayer();
inner_scroll_layer->SetDrawsContent(true);
UpdateDrawProperties(host_impl_->active_tree());
EXPECT_POINTF_EQ(gfx::PointF(500, 500), MaxScrollOffset(outer_scroll_layer));
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(250, 250), gfx::Vector2dF(),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().PinchGestureBegin(gfx::Point(250, 250),
ui::ScrollInputType::kWheel);
GetInputHandler().PinchGestureUpdate(2, gfx::Point(250, 250));
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(outer_scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(125, 125),
CurrentScrollOffset(inner_scroll_layer));
DrawFrame();
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(250, 250), gfx::Vector2dF(10, 10),
ui::ScrollInputType::kTouchscreen));
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(outer_scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(130, 130),
CurrentScrollOffset(inner_scroll_layer));
DrawFrame();
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(250, 250), gfx::Vector2dF(400, 400),
ui::ScrollInputType::kTouchscreen));
EXPECT_POINTF_EQ(gfx::PointF(80, 80),
CurrentScrollOffset(outer_scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(250, 250),
CurrentScrollOffset(inner_scroll_layer));
GetInputHandler().PinchGestureEnd(gfx::Point(250, 250));
GetInputHandler().ScrollEnd();
}
TEST_P(LayerTreeHostImplTest, PinchZoomSnapsToScreenEdge) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 1, 2);
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(500, 500);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
int offsetFromEdge = Viewport::kPinchZoomSnapMarginDips - 5;
gfx::Point anchor(viewport_size.width() - offsetFromEdge,
viewport_size.height() - offsetFromEdge);
GetInputHandler().ScrollBegin(
BeginState(anchor, gfx::Vector2dF(), ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().PinchGestureBegin(anchor, ui::ScrollInputType::kWheel);
GetInputHandler().PinchGestureUpdate(2, anchor);
GetInputHandler().PinchGestureEnd(anchor);
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(gfx::PointF(250, 250),
CurrentScrollOffset(InnerViewportScrollLayer()));
host_impl_->active_tree()->SetPageScaleOnActiveTree(1);
SetScrollOffsetDelta(InnerViewportScrollLayer(), gfx::Vector2d());
SetScrollOffsetDelta(OuterViewportScrollLayer(), gfx::Vector2d());
anchor = gfx::Point(offsetFromEdge, offsetFromEdge);
GetInputHandler().ScrollBegin(
BeginState(anchor, gfx::Vector2dF(), ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().PinchGestureBegin(gfx::Point(100, 100),
ui::ScrollInputType::kWheel);
GetInputHandler().PinchGestureUpdate(2, anchor);
GetInputHandler().PinchGestureEnd(anchor);
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(gfx::PointF(0, 0),
CurrentScrollOffset(InnerViewportScrollLayer()));
host_impl_->active_tree()->SetPageScaleOnActiveTree(1);
SetScrollOffsetDelta(InnerViewportScrollLayer(), gfx::Vector2d());
SetScrollOffsetDelta(OuterViewportScrollLayer(), gfx::Vector2d());
offsetFromEdge = Viewport::kPinchZoomSnapMarginDips;
anchor = gfx::Point(offsetFromEdge, offsetFromEdge);
GetInputHandler().ScrollBegin(
BeginState(anchor, gfx::Vector2dF(), ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().PinchGestureBegin(anchor, ui::ScrollInputType::kWheel);
GetInputHandler().PinchGestureUpdate(2, anchor);
GetInputHandler().PinchGestureEnd(anchor);
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(gfx::PointF(50, 50),
CurrentScrollOffset(InnerViewportScrollLayer()));
host_impl_->active_tree()->SetPageScaleOnActiveTree(1);
SetScrollOffsetDelta(InnerViewportScrollLayer(), gfx::Vector2d());
SetScrollOffsetDelta(OuterViewportScrollLayer(), gfx::Vector2d());
offsetFromEdge = Viewport::kPinchZoomSnapMarginDips;
anchor = gfx::Point(viewport_size.width() - offsetFromEdge,
viewport_size.height() - offsetFromEdge);
GetInputHandler().ScrollBegin(
BeginState(anchor, gfx::Vector2dF(), ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().PinchGestureBegin(anchor, ui::ScrollInputType::kWheel);
GetInputHandler().PinchGestureUpdate(2, anchor);
GetInputHandler().PinchGestureEnd(anchor);
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(gfx::PointF(200, 200),
CurrentScrollOffset(InnerViewportScrollLayer()));
}
TEST_P(LayerTreeHostImplTest, ImplPinchZoomWheelBubbleBetweenViewports) {
const gfx::Size content_size(200, 200);
const gfx::Size viewport_size(100, 100);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
LayerImpl* outer_scroll_layer = OuterViewportScrollLayer();
LayerImpl* inner_scroll_layer = InnerViewportScrollLayer();
float min_page_scale = 1, max_page_scale = 4;
float page_scale_factor = 2;
host_impl_->active_tree()->PushPageScaleFromMainThread(
page_scale_factor, min_page_scale, max_page_scale);
host_impl_->active_tree()->SetPageScaleOnActiveTree(page_scale_factor);
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(0, 0), gfx::Vector2dF(10, 20),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(0, 0), gfx::Vector2dF(10, 20), ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(gfx::PointF(5, 10), CurrentScrollOffset(inner_scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(outer_scroll_layer));
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(0, 0), gfx::Vector2dF(100, 100),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(0, 0), gfx::Vector2dF(100, 100), ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(gfx::PointF(50, 50),
CurrentScrollOffset(inner_scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(5, 10), CurrentScrollOffset(outer_scroll_layer));
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(0, 0), gfx::Vector2dF(190, 180),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(0, 0), gfx::Vector2dF(190, 180), ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(gfx::PointF(100, 100),
CurrentScrollOffset(outer_scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(50, 50),
CurrentScrollOffset(inner_scroll_layer));
}
TEST_P(LayerTreeHostImplTest, ScrollWithSwapPromises) {
ui::LatencyInfo latency_info;
latency_info.set_trace_id(5);
latency_info.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT);
std::unique_ptr<SwapPromise> swap_promise(
new LatencyInfoSwapPromise(latency_info));
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 10),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2d(0, 10), ui::ScrollInputType::kTouchscreen));
host_impl_->QueueSwapPromiseForMainThreadScrollUpdate(
std::move(swap_promise));
GetInputHandler().ScrollEnd();
std::unique_ptr<CompositorCommitData> commit_data =
host_impl_->ProcessCompositorDeltas(
nullptr);
EXPECT_EQ(1u, commit_data->swap_promises.size());
EXPECT_EQ(latency_info.trace_id(),
commit_data->swap_promises[0]->GetTraceId());
}
TEST_P(LayerTreeHostImplTest, ScrollDoesntBubble) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
LayerImpl* viewport_scroll = InnerViewportScrollLayer();
LayerImpl* scroll_parent =
AddScrollableLayer(viewport_scroll, gfx::Size(5, 5), gfx::Size(10, 10));
LayerImpl* scroll_child_clip = AddLayerInActiveTree();
CopyProperties(scroll_parent, scroll_child_clip);
scroll_child_clip->SetEffectTreeIndex(viewport_scroll->effect_tree_index());
LayerImpl* scroll_child =
AddScrollableLayer(scroll_child_clip, gfx::Size(5, 5), gfx::Size(10, 10));
GetTransformNode(scroll_child)->post_translation = gfx::Vector2d(20, 20);
DrawFrame();
{
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(21, 21), gfx::Vector2d(5, 5),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(21, 21), gfx::Vector2d(5, 5),
ui::ScrollInputType::kTouchscreen));
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(21, 21), gfx::Vector2d(100, 100),
ui::ScrollInputType::kTouchscreen));
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(gfx::PointF(5, 5), CurrentScrollOffset(scroll_child));
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(scroll_parent));
EXPECT_POINTF_EQ(gfx::PointF(0, 0),
CurrentScrollOffset(InnerViewportScrollLayer()));
EXPECT_POINTF_EQ(gfx::PointF(0, 0),
CurrentScrollOffset(OuterViewportScrollLayer()));
}
{
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(21, 21), gfx::Vector2d(3, 4),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(21, 21), gfx::Vector2d(3, 4),
ui::ScrollInputType::kTouchscreen));
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(21, 21), gfx::Vector2d(2, 1),
ui::ScrollInputType::kTouchscreen));
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(21, 21), gfx::Vector2d(2, 1),
ui::ScrollInputType::kTouchscreen));
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(21, 21), gfx::Vector2d(2, 1),
ui::ScrollInputType::kTouchscreen));
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(gfx::PointF(5, 5), CurrentScrollOffset(scroll_parent));
EXPECT_POINTF_EQ(gfx::PointF(0, 0),
CurrentScrollOffset(InnerViewportScrollLayer()));
EXPECT_POINTF_EQ(gfx::PointF(0, 0),
CurrentScrollOffset(OuterViewportScrollLayer()));
}
}
TEST_P(LayerTreeHostImplTest, PinchGesture) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
LayerImpl* scroll_layer = InnerViewportScrollLayer();
DCHECK(scroll_layer);
float min_page_scale = 1;
float max_page_scale = 4;
{
host_impl_->active_tree()->PushPageScaleFromMainThread(1, min_page_scale,
max_page_scale);
SetScrollOffsetDelta(scroll_layer, gfx::Vector2d());
float page_scale_delta = 2;
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(50, 50), gfx::Vector2dF(),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().PinchGestureBegin(gfx::Point(50, 50),
ui::ScrollInputType::kWheel);
GetInputHandler().PinchGestureUpdate(page_scale_delta, gfx::Point(50, 50));
GetInputHandler().PinchGestureEnd(gfx::Point(50, 50));
GetInputHandler().ScrollEnd();
EXPECT_FALSE(did_request_next_frame_);
EXPECT_TRUE(did_request_redraw_);
EXPECT_TRUE(did_request_commit_);
std::unique_ptr<CompositorCommitData> commit_data =
host_impl_->ProcessCompositorDeltas(
nullptr);
EXPECT_EQ(commit_data->page_scale_delta, page_scale_delta);
ClearMainThreadDeltasForTesting(host_impl_.get());
}
{
host_impl_->active_tree()->PushPageScaleFromMainThread(1, min_page_scale,
max_page_scale);
SetScrollOffsetDelta(scroll_layer, gfx::Vector2d());
float page_scale_delta = 10;
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(50, 50), gfx::Vector2dF(),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().PinchGestureBegin(gfx::Point(50, 50),
ui::ScrollInputType::kWheel);
GetInputHandler().PinchGestureUpdate(page_scale_delta, gfx::Point(50, 50));
GetInputHandler().PinchGestureEnd(gfx::Point(50, 50));
GetInputHandler().ScrollEnd();
std::unique_ptr<CompositorCommitData> commit_data =
host_impl_->ProcessCompositorDeltas(
nullptr);
EXPECT_EQ(commit_data->page_scale_delta, max_page_scale);
ClearMainThreadDeltasForTesting(host_impl_.get());
}
{
host_impl_->active_tree()->PushPageScaleFromMainThread(1, min_page_scale,
max_page_scale);
SetScrollOffsetDelta(scroll_layer, gfx::Vector2d());
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree_mutable()
.CollectScrollDeltasForTesting();
ClearMainThreadDeltasForTesting(host_impl_.get());
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree_mutable()
.UpdateScrollOffsetBaseForTesting(scroll_layer->element_id(),
gfx::PointF(50, 50));
float page_scale_delta = 0.1f;
GetInputHandler().ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().PinchGestureBegin(gfx::Point(),
ui::ScrollInputType::kWheel);
GetInputHandler().PinchGestureUpdate(page_scale_delta, gfx::Point());
GetInputHandler().PinchGestureEnd(gfx::Point());
GetInputHandler().ScrollEnd();
std::unique_ptr<CompositorCommitData> commit_data =
host_impl_->ProcessCompositorDeltas(
nullptr);
EXPECT_EQ(commit_data->page_scale_delta, min_page_scale);
EXPECT_TRUE(commit_data->scrolls.empty());
ClearMainThreadDeltasForTesting(host_impl_.get());
}
{
host_impl_->active_tree()->PushPageScaleFromMainThread(1, min_page_scale,
max_page_scale);
SetScrollOffsetDelta(scroll_layer, gfx::Vector2d());
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree_mutable()
.CollectScrollDeltasForTesting();
ClearMainThreadDeltasForTesting(host_impl_.get());
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree_mutable()
.UpdateScrollOffsetBaseForTesting(scroll_layer->element_id(),
gfx::PointF(20, 20));
float page_scale_delta = 1;
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(10, 10), gfx::Vector2dF(),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().PinchGestureBegin(gfx::Point(10, 10),
ui::ScrollInputType::kWheel);
GetInputHandler().PinchGestureUpdate(page_scale_delta, gfx::Point(10, 10));
GetInputHandler().PinchGestureUpdate(page_scale_delta, gfx::Point(20, 20));
GetInputHandler().PinchGestureEnd(gfx::Point(20, 20));
GetInputHandler().ScrollEnd();
std::unique_ptr<CompositorCommitData> commit_data =
host_impl_->ProcessCompositorDeltas(
nullptr);
EXPECT_EQ(commit_data->page_scale_delta, page_scale_delta);
EXPECT_TRUE(commit_data->scrolls.empty());
ClearMainThreadDeltasForTesting(host_impl_.get());
}
{
host_impl_->active_tree()->PushPageScaleFromMainThread(1, min_page_scale,
max_page_scale);
SetScrollOffsetDelta(scroll_layer, gfx::Vector2d());
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree_mutable()
.CollectScrollDeltasForTesting();
ClearMainThreadDeltasForTesting(host_impl_.get());
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree_mutable()
.UpdateScrollOffsetBaseForTesting(scroll_layer->element_id(),
gfx::PointF(20, 20));
float page_scale_delta = 1;
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(10, 10), gfx::Vector2dF(-10, -10),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().PinchGestureBegin(gfx::Point(10, 10),
ui::ScrollInputType::kWheel);
GetInputHandler().PinchGestureUpdate(page_scale_delta, gfx::Point(10, 10));
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(10, 10), gfx::Vector2d(-10, -10),
ui::ScrollInputType::kTouchscreen));
GetInputHandler().PinchGestureUpdate(page_scale_delta, gfx::Point(20, 20));
GetInputHandler().PinchGestureEnd(gfx::Point(20, 20));
GetInputHandler().ScrollEnd();
std::unique_ptr<CompositorCommitData> commit_data =
host_impl_->ProcessCompositorDeltas(
nullptr);
EXPECT_EQ(commit_data->page_scale_delta, page_scale_delta);
EXPECT_TRUE(ScrollInfoContains(*commit_data, scroll_layer->element_id(),
gfx::Vector2dF(-10, -10)));
ClearMainThreadDeltasForTesting(host_impl_.get());
}
{
host_impl_->active_tree()->PushPageScaleFromMainThread(0.5f, 0.5f, 4);
SetScrollOffsetDelta(scroll_layer, gfx::Vector2d());
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree_mutable()
.CollectScrollDeltasForTesting();
ClearMainThreadDeltasForTesting(host_impl_.get());
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree_mutable()
.UpdateScrollOffsetBaseForTesting(scroll_layer->element_id(),
gfx::PointF(0, 0));
GetInputHandler().ScrollBegin(BeginState(gfx::Point(0, 0), gfx::Vector2dF(),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().PinchGestureBegin(gfx::Point(0, 0),
ui::ScrollInputType::kWheel);
GetInputHandler().PinchGestureUpdate(2, gfx::Point(0, 0));
GetInputHandler().PinchGestureUpdate(1, gfx::Point(0, 0));
DrawFrame();
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(0, 0), gfx::Vector2d(10, 10),
ui::ScrollInputType::kTouchscreen));
GetInputHandler().PinchGestureUpdate(1, gfx::Point(10, 10));
GetInputHandler().PinchGestureEnd(gfx::Point(10, 10));
GetInputHandler().ScrollEnd();
std::unique_ptr<CompositorCommitData> commit_data =
host_impl_->ProcessCompositorDeltas(
nullptr);
EXPECT_EQ(commit_data->page_scale_delta, 2);
EXPECT_TRUE(ScrollInfoContains(*commit_data, scroll_layer->element_id(),
gfx::Vector2dF(10, 10)));
}
}
TEST_P(LayerTreeHostImplTest, SyncSubpixelScrollDelta) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
LayerImpl* scroll_layer = InnerViewportScrollLayer();
DCHECK(scroll_layer);
float min_page_scale = 1;
float max_page_scale = 4;
host_impl_->active_tree()->PushPageScaleFromMainThread(1, min_page_scale,
max_page_scale);
SetScrollOffsetDelta(scroll_layer, gfx::Vector2d());
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree_mutable()
.CollectScrollDeltasForTesting();
ClearMainThreadDeltasForTesting(host_impl_.get());
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree_mutable()
.UpdateScrollOffsetBaseForTesting(scroll_layer->element_id(),
gfx::PointF(0, 20));
float page_scale_delta = 1;
GetInputHandler().ScrollBegin(BeginState(gfx::Point(10, 10), gfx::Vector2dF(),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().PinchGestureBegin(gfx::Point(10, 10),
ui::ScrollInputType::kWheel);
GetInputHandler().PinchGestureUpdate(page_scale_delta, gfx::Point(10, 10));
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(10, 10), gfx::Vector2dF(0, -1.001f),
ui::ScrollInputType::kTouchscreen));
GetInputHandler().PinchGestureUpdate(page_scale_delta, gfx::Point(10, 9));
GetInputHandler().PinchGestureEnd(gfx::Point(10, 9));
GetInputHandler().ScrollEnd();
std::unique_ptr<CompositorCommitData> commit_data =
host_impl_->ProcessCompositorDeltas(
nullptr);
EXPECT_EQ(commit_data->page_scale_delta, page_scale_delta);
EXPECT_TRUE(ScrollInfoContains(*commit_data, scroll_layer->element_id(),
gfx::Vector2dF(0, -1)));
draw_property_utils::ComputeTransforms(
&scroll_layer->layer_tree_impl()
->property_trees()
->transform_tree_mutable(),
scroll_layer->layer_tree_impl()->viewport_property_ids());
EXPECT_VECTOR2DF_EQ(gfx::Vector2dF(0, -19),
scroll_layer->ScreenSpaceTransform().To2dTranslation());
}
TEST_P(LayerTreeHostImplTest, SyncSubpixelScrollFromFractionalActiveBase) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
LayerImpl* scroll_layer = InnerViewportScrollLayer();
DCHECK(scroll_layer);
SetScrollOffsetDelta(scroll_layer, gfx::Vector2d());
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree_mutable()
.CollectScrollDeltasForTesting();
ClearMainThreadDeltasForTesting(host_impl_.get());
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree_mutable()
.UpdateScrollOffsetBaseForTesting(scroll_layer->element_id(),
gfx::PointF(0, 20.5f));
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(10, 10), gfx::Vector2dF(0, -1),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(10, 10), gfx::Vector2dF(0, -1), ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
gfx::PointF active_base =
host_impl_->active_tree()
->property_trees()
->scroll_tree()
.GetScrollOffsetBaseForTesting(scroll_layer->element_id());
EXPECT_POINTF_EQ(active_base, gfx::PointF(0, 20.5));
std::unique_ptr<CompositorCommitData> commit_data =
host_impl_->ProcessCompositorDeltas(
nullptr);
EXPECT_VECTOR2DF_EQ(gfx::Vector2dF(0, -1),
commit_data->inner_viewport_scroll.scroll_delta);
}
TEST_P(LayerTreeHostImplTest, PinchZoomTriggersPageScaleAnimation) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
float min_page_scale = 1;
float max_page_scale = 4;
float page_scale_delta = 1.04f;
base::TimeTicks start_time = base::TimeTicks() + base::Seconds(1);
base::TimeDelta duration = base::Milliseconds(200);
base::TimeTicks halfway_through_animation = start_time + duration / 2;
base::TimeTicks end_time = start_time + duration;
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
{
host_impl_->active_tree()->PushPageScaleFromMainThread(1, min_page_scale,
max_page_scale);
did_request_redraw_ = false;
did_request_next_frame_ = false;
GetInputHandler().PinchGestureBegin(gfx::Point(50, 50),
ui::ScrollInputType::kWheel);
GetInputHandler().PinchGestureUpdate(page_scale_delta, gfx::Point(50, 50));
GetInputHandler().PinchGestureEnd(gfx::Point(50, 50));
host_impl_->ActivateSyncTree();
EXPECT_TRUE(did_request_redraw_);
EXPECT_TRUE(did_request_next_frame_);
did_request_redraw_ = false;
did_request_next_frame_ = false;
begin_frame_args.frame_time = start_time;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_TRUE(did_request_redraw_);
EXPECT_TRUE(did_request_next_frame_);
host_impl_->DidFinishImplFrame(begin_frame_args);
did_request_redraw_ = false;
did_request_next_frame_ = false;
begin_frame_args.frame_time = halfway_through_animation;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_TRUE(did_request_redraw_);
EXPECT_TRUE(did_request_next_frame_);
host_impl_->DidFinishImplFrame(begin_frame_args);
did_request_redraw_ = false;
did_request_next_frame_ = false;
did_request_commit_ = false;
begin_frame_args.frame_time = end_time;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_TRUE(did_request_commit_);
EXPECT_FALSE(did_request_next_frame_);
host_impl_->DidFinishImplFrame(begin_frame_args);
std::unique_ptr<CompositorCommitData> commit_data =
host_impl_->ProcessCompositorDeltas(
nullptr);
EXPECT_EQ(commit_data->page_scale_delta, 1);
ClearMainThreadDeltasForTesting(host_impl_.get());
}
start_time += base::Seconds(10);
halfway_through_animation += base::Seconds(10);
end_time += base::Seconds(10);
page_scale_delta = 1.06f;
{
host_impl_->active_tree()->PushPageScaleFromMainThread(1, min_page_scale,
max_page_scale);
did_request_redraw_ = false;
did_request_next_frame_ = false;
GetInputHandler().PinchGestureBegin(gfx::Point(50, 50),
ui::ScrollInputType::kWheel);
GetInputHandler().PinchGestureUpdate(page_scale_delta, gfx::Point(50, 50));
GetInputHandler().PinchGestureEnd(gfx::Point(50, 50));
host_impl_->ActivateSyncTree();
EXPECT_TRUE(did_request_redraw_);
EXPECT_FALSE(did_request_next_frame_);
did_request_redraw_ = false;
did_request_next_frame_ = false;
begin_frame_args.frame_time = start_time;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_FALSE(did_request_redraw_);
EXPECT_FALSE(did_request_next_frame_);
host_impl_->DidFinishImplFrame(begin_frame_args);
did_request_redraw_ = false;
did_request_next_frame_ = false;
begin_frame_args.frame_time = halfway_through_animation;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_FALSE(did_request_redraw_);
EXPECT_FALSE(did_request_next_frame_);
host_impl_->DidFinishImplFrame(begin_frame_args);
did_request_redraw_ = false;
did_request_next_frame_ = false;
did_request_commit_ = false;
begin_frame_args.frame_time = end_time;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_FALSE(did_request_commit_);
EXPECT_FALSE(did_request_next_frame_);
host_impl_->DidFinishImplFrame(begin_frame_args);
std::unique_ptr<CompositorCommitData> commit_data =
host_impl_->ProcessCompositorDeltas(
nullptr);
EXPECT_EQ(commit_data->page_scale_delta, page_scale_delta);
}
}
TEST_P(LayerTreeHostImplTest, PageScaleAnimation) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
LayerImpl* scroll_layer = InnerViewportScrollLayer();
DCHECK(scroll_layer);
float min_page_scale = 0.5f;
float max_page_scale = 4;
base::TimeTicks start_time = base::TimeTicks() + base::Seconds(1);
base::TimeDelta duration = base::Milliseconds(100);
base::TimeTicks halfway_through_animation = start_time + duration / 2;
base::TimeTicks end_time = start_time + duration;
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
{
host_impl_->active_tree()->PushPageScaleFromMainThread(1, min_page_scale,
max_page_scale);
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree_mutable()
.UpdateScrollOffsetBaseForTesting(scroll_layer->element_id(),
gfx::PointF(50, 50));
did_request_redraw_ = false;
did_request_next_frame_ = false;
host_impl_->active_tree()->SetPendingPageScaleAnimation(
std::make_unique<PendingPageScaleAnimation>(gfx::Point(), false, 2,
duration));
host_impl_->ActivateSyncTree();
EXPECT_FALSE(did_request_redraw_);
EXPECT_TRUE(did_request_next_frame_);
did_request_redraw_ = false;
did_request_next_frame_ = false;
begin_frame_args.frame_time = start_time;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_TRUE(did_request_redraw_);
EXPECT_TRUE(did_request_next_frame_);
host_impl_->DidFinishImplFrame(begin_frame_args);
did_request_redraw_ = false;
did_request_next_frame_ = false;
begin_frame_args.frame_time = halfway_through_animation;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_TRUE(did_request_redraw_);
EXPECT_TRUE(did_request_next_frame_);
host_impl_->DidFinishImplFrame(begin_frame_args);
did_request_redraw_ = false;
did_request_next_frame_ = false;
did_request_commit_ = false;
begin_frame_args.frame_time = end_time;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_TRUE(did_request_commit_);
EXPECT_FALSE(did_request_next_frame_);
host_impl_->DidFinishImplFrame(begin_frame_args);
std::unique_ptr<CompositorCommitData> commit_data =
host_impl_->ProcessCompositorDeltas(
nullptr);
EXPECT_EQ(commit_data->page_scale_delta, 2);
EXPECT_TRUE(ScrollInfoContains(*commit_data, scroll_layer->element_id(),
gfx::Vector2dF(-50, -50)));
ClearMainThreadDeltasForTesting(host_impl_.get());
}
start_time += base::Seconds(10);
halfway_through_animation += base::Seconds(10);
end_time += base::Seconds(10);
{
host_impl_->active_tree()->PushPageScaleFromMainThread(1, min_page_scale,
max_page_scale);
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree_mutable()
.UpdateScrollOffsetBaseForTesting(scroll_layer->element_id(),
gfx::PointF(50, 50));
did_request_redraw_ = false;
did_request_next_frame_ = false;
host_impl_->active_tree()->SetPendingPageScaleAnimation(
std::make_unique<PendingPageScaleAnimation>(gfx::Point(25, 25), true,
min_page_scale, duration));
host_impl_->ActivateSyncTree();
EXPECT_FALSE(did_request_redraw_);
EXPECT_TRUE(did_request_next_frame_);
did_request_redraw_ = false;
did_request_next_frame_ = false;
begin_frame_args.frame_time = start_time;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_TRUE(did_request_redraw_);
EXPECT_TRUE(did_request_next_frame_);
host_impl_->DidFinishImplFrame(begin_frame_args);
did_request_redraw_ = false;
did_request_commit_ = false;
did_request_next_frame_ = false;
begin_frame_args.frame_time = end_time;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_TRUE(did_request_redraw_);
EXPECT_FALSE(did_request_next_frame_);
EXPECT_TRUE(did_request_commit_);
host_impl_->DidFinishImplFrame(begin_frame_args);
std::unique_ptr<CompositorCommitData> commit_data =
host_impl_->ProcessCompositorDeltas(
nullptr);
EXPECT_EQ(commit_data->page_scale_delta, min_page_scale);
EXPECT_TRUE(ScrollInfoContains(*commit_data, scroll_layer->element_id(),
gfx::Vector2dF(-50, -50)));
}
}
TEST_P(LayerTreeHostImplTest, PageScaleAnimationNoOp) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
LayerImpl* scroll_layer = InnerViewportScrollLayer();
DCHECK(scroll_layer);
float min_page_scale = 0.5f;
float max_page_scale = 4;
base::TimeTicks start_time = base::TimeTicks() + base::Seconds(1);
base::TimeDelta duration = base::Milliseconds(100);
base::TimeTicks halfway_through_animation = start_time + duration / 2;
base::TimeTicks end_time = start_time + duration;
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
{
host_impl_->active_tree()->PushPageScaleFromMainThread(1, min_page_scale,
max_page_scale);
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree_mutable()
.UpdateScrollOffsetBaseForTesting(scroll_layer->element_id(),
gfx::PointF(50, 50));
host_impl_->active_tree()->SetPendingPageScaleAnimation(
std::make_unique<PendingPageScaleAnimation>(gfx::Point(), true, 1,
duration));
host_impl_->ActivateSyncTree();
begin_frame_args.frame_time = start_time;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
host_impl_->DidFinishImplFrame(begin_frame_args);
begin_frame_args.frame_time = halfway_through_animation;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_TRUE(did_request_redraw_);
host_impl_->DidFinishImplFrame(begin_frame_args);
begin_frame_args.frame_time = end_time;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_TRUE(did_request_commit_);
host_impl_->DidFinishImplFrame(begin_frame_args);
std::unique_ptr<CompositorCommitData> commit_data =
host_impl_->ProcessCompositorDeltas(
nullptr);
EXPECT_EQ(commit_data->page_scale_delta, 1);
ExpectNone(*commit_data, scroll_layer->element_id());
}
}
TEST_P(LayerTreeHostImplTest, PageScaleAnimationTransferedOnSyncTreeActivate) {
EnsureSyncTree();
host_impl_->sync_tree()->PushPageScaleFromMainThread(1, 1, 1);
SetupViewportLayers(host_impl_->sync_tree(), gfx::Size(50, 50),
gfx::Size(100, 100), gfx::Size(100, 100));
host_impl_->ActivateSyncTree();
DrawFrame();
LayerImpl* scroll_layer = InnerViewportScrollLayer();
DCHECK(scroll_layer);
float min_page_scale = 0.5f;
float max_page_scale = 4;
EnsureSyncTree();
host_impl_->sync_tree()->PushPageScaleFromMainThread(1, min_page_scale,
max_page_scale);
host_impl_->ActivateSyncTree();
base::TimeTicks start_time = base::TimeTicks() + base::Seconds(1);
base::TimeDelta duration = base::Milliseconds(100);
base::TimeTicks third_through_animation = start_time + duration / 3;
base::TimeTicks halfway_through_animation = start_time + duration / 2;
base::TimeTicks end_time = start_time + duration;
float target_scale = 2;
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree_mutable()
.UpdateScrollOffsetBaseForTesting(scroll_layer->element_id(),
gfx::PointF(50, 50));
EnsureSyncTree();
host_impl_->sync_tree()->SetPendingPageScaleAnimation(
std::make_unique<PendingPageScaleAnimation>(gfx::Point(), false,
target_scale, duration));
std::unique_ptr<PendingPageScaleAnimation> psa =
host_impl_->sync_tree()->TakePendingPageScaleAnimation();
EXPECT_EQ(target_scale, psa->scale);
EXPECT_EQ(duration, psa->duration);
EXPECT_EQ(nullptr, host_impl_->sync_tree()->TakePendingPageScaleAnimation());
did_request_redraw_ = false;
did_request_next_frame_ = false;
host_impl_->sync_tree()->SetPendingPageScaleAnimation(
std::make_unique<PendingPageScaleAnimation>(gfx::Point(), false,
target_scale, duration));
begin_frame_args.frame_time = halfway_through_animation;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_FALSE(did_request_next_frame_);
EXPECT_FALSE(did_request_redraw_);
host_impl_->DidFinishImplFrame(begin_frame_args);
host_impl_->ActivateSyncTree();
EnsureSyncTree();
EXPECT_EQ(nullptr,
host_impl_->sync_tree()->TakePendingPageScaleAnimation().get());
EXPECT_FALSE(did_request_redraw_);
EXPECT_TRUE(did_request_next_frame_);
start_time += base::Seconds(10);
third_through_animation += base::Seconds(10);
halfway_through_animation += base::Seconds(10);
end_time += base::Seconds(10);
did_request_redraw_ = false;
did_request_next_frame_ = false;
begin_frame_args.frame_time = start_time;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_TRUE(did_request_redraw_);
EXPECT_TRUE(did_request_next_frame_);
host_impl_->DidFinishImplFrame(begin_frame_args);
did_request_redraw_ = false;
did_request_next_frame_ = false;
begin_frame_args.frame_time = third_through_animation;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_TRUE(did_request_redraw_);
EXPECT_TRUE(did_request_next_frame_);
host_impl_->DidFinishImplFrame(begin_frame_args);
host_impl_->ActivateSyncTree();
did_request_redraw_ = false;
did_request_next_frame_ = false;
begin_frame_args.frame_time = halfway_through_animation;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_TRUE(did_request_redraw_);
EXPECT_TRUE(did_request_next_frame_);
host_impl_->DidFinishImplFrame(begin_frame_args);
did_request_redraw_ = false;
did_request_next_frame_ = false;
did_request_commit_ = false;
begin_frame_args.frame_time = end_time;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_TRUE(did_request_commit_);
EXPECT_FALSE(did_request_next_frame_);
host_impl_->DidFinishImplFrame(begin_frame_args);
std::unique_ptr<CompositorCommitData> commit_data =
host_impl_->ProcessCompositorDeltas(
nullptr);
EXPECT_EQ(commit_data->page_scale_delta, target_scale);
EXPECT_TRUE(ScrollInfoContains(*commit_data, scroll_layer->element_id(),
gfx::Vector2dF(-50, -50)));
}
TEST_P(LayerTreeHostImplTest, PageScaleAnimationCompletedNotification) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
LayerImpl* scroll_layer = InnerViewportScrollLayer();
DCHECK(scroll_layer);
base::TimeTicks start_time = base::TimeTicks() + base::Seconds(1);
base::TimeDelta duration = base::Milliseconds(100);
base::TimeTicks halfway_through_animation = start_time + duration / 2;
base::TimeTicks end_time = start_time + duration;
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 0.5f, 4);
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree_mutable()
.UpdateScrollOffsetBaseForTesting(scroll_layer->element_id(),
gfx::PointF(50, 50));
did_complete_page_scale_animation_ = false;
host_impl_->active_tree()->SetPendingPageScaleAnimation(
std::make_unique<PendingPageScaleAnimation>(gfx::Point(), false, 2,
duration));
host_impl_->ActivateSyncTree();
begin_frame_args.frame_time = start_time;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_FALSE(did_complete_page_scale_animation_);
host_impl_->DidFinishImplFrame(begin_frame_args);
begin_frame_args.frame_time = halfway_through_animation;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_FALSE(did_complete_page_scale_animation_);
host_impl_->DidFinishImplFrame(begin_frame_args);
begin_frame_args.frame_time = end_time;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_TRUE(did_complete_page_scale_animation_);
host_impl_->DidFinishImplFrame(begin_frame_args);
}
TEST_P(LayerTreeHostImplTest, MaxScrollOffsetAffectedByViewportBoundsDelta) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 0.5f, 4);
DrawFrame();
LayerImpl* inner_scroll = InnerViewportScrollLayer();
DCHECK(inner_scroll);
EXPECT_EQ(gfx::SizeF(50, 50),
host_impl_->active_tree()->ScrollableViewportSize());
EXPECT_EQ(gfx::PointF(50, 50), MaxScrollOffset(inner_scroll));
PropertyTrees* property_trees = host_impl_->active_tree()->property_trees();
property_trees->SetInnerViewportContainerBoundsDelta(gfx::Vector2dF(15, 15));
property_trees->SetOuterViewportContainerBoundsDelta(gfx::Vector2dF(7, 7));
EXPECT_EQ(gfx::SizeF(65, 65),
host_impl_->active_tree()->ScrollableViewportSize());
EXPECT_EQ(gfx::PointF(42, 42), MaxScrollOffset(inner_scroll));
property_trees->SetInnerViewportContainerBoundsDelta(gfx::Vector2dF());
property_trees->SetOuterViewportContainerBoundsDelta(gfx::Vector2dF());
inner_scroll->SetBounds(gfx::Size());
GetScrollNode(inner_scroll)->bounds = inner_scroll->bounds();
DrawFrame();
property_trees->SetOuterViewportContainerBoundsDelta(gfx::Vector2dF(60, 60));
EXPECT_EQ(gfx::PointF(10, 10), MaxScrollOffset(inner_scroll));
}
TEST_P(LayerTreeHostImplTest, AnimatedGranularityCausesSmoothScroll) {
gfx::Size viewport_size(300, 200);
gfx::Size content_size(1000, 1000);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
gfx::Point position(295, 195);
gfx::Vector2dF offset(0, 50);
std::vector<ui::ScrollInputType> types = {ui::ScrollInputType::kScrollbar,
ui::ScrollInputType::kWheel};
for (auto type : types) {
auto begin_state = BeginState(position, offset, type);
begin_state->data()->set_current_native_scrolling_element(
host_impl_->OuterViewportScrollNode()->element_id);
begin_state->data()->delta_granularity =
ui::ScrollGranularity::kScrollByPixel;
auto update_state = UpdateState(position, offset, type);
update_state.data()->delta_granularity =
ui::ScrollGranularity::kScrollByPixel;
ASSERT_FALSE(GetImplAnimationHost()->HasImplOnlyScrollAnimatingElement());
{
GetInputHandler().ScrollBegin(begin_state.get(), type);
ASSERT_EQ(host_impl_->CurrentlyScrollingNode(),
host_impl_->OuterViewportScrollNode());
GetInputHandler().ScrollUpdate(update_state);
EXPECT_TRUE(GetImplAnimationHost()->HasImplOnlyScrollAnimatingElement());
GetInputHandler().ScrollEnd();
}
GetImplAnimationHost()->ScrollAnimationAbort(
host_impl_->OuterViewportScrollNode()->element_id);
ASSERT_FALSE(GetImplAnimationHost()->HasImplOnlyScrollAnimatingElement());
{
begin_state->data()->delta_granularity =
ui::ScrollGranularity::kScrollByPrecisePixel;
update_state.data()->delta_granularity =
ui::ScrollGranularity::kScrollByPrecisePixel;
GetInputHandler().ScrollBegin(begin_state.get(), type);
ASSERT_EQ(host_impl_->CurrentlyScrollingNode(),
host_impl_->OuterViewportScrollNode());
GetInputHandler().ScrollUpdate(update_state);
EXPECT_FALSE(GetImplAnimationHost()->HasImplOnlyScrollAnimatingElement());
GetInputHandler().ScrollEnd();
}
}
}
TEST_P(LayerTreeHostImplTest, NonAnimatedGranularityCausesInstantScroll) {
LayerTreeSettings settings = DefaultSettings();
settings.enable_smooth_scroll = false;
CreateHostImpl(settings, CreateLayerTreeFrameSink());
gfx::Size viewport_size(300, 200);
gfx::Size content_size(1000, 1000);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
gfx::Point position(295, 195);
gfx::Vector2dF offset(0, 50);
std::vector<ui::ScrollInputType> types = {ui::ScrollInputType::kScrollbar,
ui::ScrollInputType::kWheel};
for (auto type : types) {
auto begin_state = BeginState(position, offset, type);
begin_state->data()->set_current_native_scrolling_element(
host_impl_->OuterViewportScrollNode()->element_id);
begin_state->data()->delta_granularity =
ui::ScrollGranularity::kScrollByPixel;
auto update_state = UpdateState(position, offset, type);
update_state.data()->delta_granularity =
ui::ScrollGranularity::kScrollByPixel;
ASSERT_FALSE(GetImplAnimationHost()->HasImplOnlyScrollAnimatingElement());
{
GetInputHandler().ScrollBegin(begin_state.get(), type);
ASSERT_EQ(host_impl_->CurrentlyScrollingNode(),
host_impl_->OuterViewportScrollNode());
GetInputHandler().ScrollUpdate(update_state);
EXPECT_FALSE(GetImplAnimationHost()->HasImplOnlyScrollAnimatingElement());
GetInputHandler().ScrollEnd();
}
}
}
class LayerTreeHostImplOverridePhysicalTime : public LayerTreeHostImpl {
public:
LayerTreeHostImplOverridePhysicalTime(
const LayerTreeSettings& settings,
LayerTreeHostImplClient* client,
TaskRunnerProvider* task_runner_provider,
TaskGraphRunner* task_graph_runner,
RenderingStatsInstrumentation* rendering_stats_instrumentation)
: LayerTreeHostImpl(
settings,
client,
task_runner_provider,
rendering_stats_instrumentation,
task_graph_runner,
AnimationHost::CreateForTesting(ThreadInstance::kImpl),
nullptr,
0,
nullptr,
nullptr) {}
const viz::BeginFrameArgs& CurrentBeginFrameArgs() const override {
return current_begin_frame_args_;
}
void SetCurrentPhysicalTimeTicksForTest(base::TimeTicks fake_now) {
current_begin_frame_args_ = viz::CreateBeginFrameArgsForTesting(
BEGINFRAME_FROM_HERE, 0, 1, fake_now);
}
private:
viz::BeginFrameArgs current_begin_frame_args_;
};
class LayerTreeHostImplTestScrollbarAnimation : public LayerTreeHostImplTest {
protected:
void SetupLayers(LayerTreeSettings settings) {
host_impl_->ReleaseLayerTreeFrameSink();
host_impl_ = nullptr;
gfx::Size viewport_size(50, 50);
gfx::Size content_size(100, 100);
LayerTreeHostImplOverridePhysicalTime* host_impl_override_time =
new LayerTreeHostImplOverridePhysicalTime(
settings, this, &task_runner_provider_, &task_graph_runner_,
&stats_instrumentation_);
InputHandler::Create(
static_cast<CompositorDelegateForInput&>(*host_impl_override_time));
host_impl_ = base::WrapUnique(host_impl_override_time);
layer_tree_frame_sink_ = CreateLayerTreeFrameSink();
host_impl_->SetVisible(true);
host_impl_->InitializeFrameSink(layer_tree_frame_sink_.get());
SetupViewportLayersInnerScrolls(viewport_size, content_size);
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 1, 4);
auto* scrollbar = AddLayer<SolidColorScrollbarLayerImpl>(
host_impl_->active_tree(), ScrollbarOrientation::kVertical, 10, 0,
false);
SetupScrollbarLayer(OuterViewportScrollLayer(), scrollbar);
host_impl_->active_tree()->DidBecomeActive();
host_impl_->active_tree()->UpdateAllScrollbarGeometriesForTesting();
host_impl_->active_tree()->HandleScrollbarShowRequests();
host_impl_->active_tree()->SetLocalSurfaceIdFromParent(viz::LocalSurfaceId(
1, base::UnguessableToken::CreateForTesting(2u, 3u)));
DrawFrame();
did_request_redraw_ = false;
}
void RunTest(LayerTreeSettings::ScrollbarAnimator animator) {
LayerTreeSettings settings = DefaultSettings();
settings.scrollbar_animator = animator;
settings.scrollbar_fade_delay = base::Milliseconds(20);
settings.scrollbar_fade_duration = base::Milliseconds(20);
bool expecting_animations = animator != LayerTreeSettings::NO_ANIMATOR;
SetupLayers(settings);
base::TimeTicks fake_now = base::TimeTicks::Now();
if (animator == LayerTreeSettings::AURA_OVERLAY) {
EXPECT_FALSE(did_request_next_frame_);
EXPECT_FALSE(did_request_redraw_);
EXPECT_FALSE(animation_task_.is_null());
requested_animation_delay_ = base::TimeDelta();
animation_task_.Reset();
} else {
EXPECT_FALSE(did_request_next_frame_);
EXPECT_FALSE(did_request_redraw_);
EXPECT_TRUE(animation_task_.is_null());
EXPECT_EQ(base::TimeDelta(), requested_animation_delay_);
}
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(), gfx::Vector2dF(), ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
GetInputHandler().ScrollEnd();
EXPECT_FALSE(did_request_next_frame_);
EXPECT_FALSE(did_request_redraw_);
EXPECT_EQ(base::TimeDelta(), requested_animation_delay_);
EXPECT_TRUE(animation_task_.is_null());
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(), gfx::Vector2dF(), ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2dF(0, 0), ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
EXPECT_FALSE(did_request_next_frame_);
EXPECT_FALSE(did_request_redraw_);
if (animator == LayerTreeSettings::AURA_OVERLAY) {
EXPECT_EQ(base::Milliseconds(20), requested_animation_delay_);
EXPECT_FALSE(animation_task_.is_null());
requested_animation_delay_ = base::TimeDelta();
animation_task_.Reset();
} else {
EXPECT_EQ(base::TimeDelta(), requested_animation_delay_);
EXPECT_TRUE(animation_task_.is_null());
}
viz::BeginFrameArgs begin_frame_args = viz::CreateBeginFrameArgsForTesting(
BEGINFRAME_FROM_HERE, 0, 2, fake_now);
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_FALSE(did_request_next_frame_);
did_request_next_frame_ = false;
EXPECT_FALSE(did_request_redraw_);
did_request_redraw_ = false;
EXPECT_EQ(base::TimeDelta(), requested_animation_delay_);
EXPECT_TRUE(animation_task_.is_null());
host_impl_->DidFinishImplFrame(begin_frame_args);
GetInputHandler().ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 5),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2dF(0, 5), ui::ScrollInputType::kWheel));
EXPECT_FALSE(did_request_next_frame_);
EXPECT_TRUE(did_request_redraw_);
did_request_redraw_ = false;
if (expecting_animations) {
EXPECT_EQ(base::Milliseconds(20), requested_animation_delay_);
EXPECT_FALSE(animation_task_.is_null());
} else {
EXPECT_EQ(base::TimeDelta(), requested_animation_delay_);
EXPECT_TRUE(animation_task_.is_null());
}
GetInputHandler().ScrollEnd();
EXPECT_FALSE(did_request_next_frame_);
EXPECT_FALSE(did_request_redraw_);
if (expecting_animations) {
EXPECT_EQ(base::Milliseconds(20), requested_animation_delay_);
EXPECT_FALSE(animation_task_.is_null());
} else {
EXPECT_EQ(base::TimeDelta(), requested_animation_delay_);
EXPECT_TRUE(animation_task_.is_null());
}
if (expecting_animations) {
begin_frame_args = viz::CreateBeginFrameArgsForTesting(
BEGINFRAME_FROM_HERE, 0, 3, fake_now);
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_FALSE(did_request_next_frame_);
did_request_next_frame_ = false;
EXPECT_FALSE(did_request_redraw_);
did_request_redraw_ = false;
host_impl_->DidFinishImplFrame(begin_frame_args);
fake_now += requested_animation_delay_;
requested_animation_delay_ = base::TimeDelta();
std::move(animation_task_).Run();
EXPECT_TRUE(did_request_next_frame_);
did_request_next_frame_ = false;
EXPECT_FALSE(did_request_redraw_);
begin_frame_args = viz::CreateBeginFrameArgsForTesting(
BEGINFRAME_FROM_HERE, 0, 4, fake_now);
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_TRUE(did_request_next_frame_);
did_request_next_frame_ = false;
EXPECT_TRUE(did_request_redraw_);
did_request_redraw_ = false;
EXPECT_EQ(base::TimeDelta(), requested_animation_delay_);
EXPECT_TRUE(animation_task_.is_null());
host_impl_->DidFinishImplFrame(begin_frame_args);
}
if (host_impl_->active_tree()
->property_trees()
->scroll_tree_mutable()
.UpdateScrollOffsetBaseForTesting(
InnerViewportScrollLayer()->element_id(), gfx::PointF(5, 5)))
host_impl_->active_tree()->DidUpdateScrollOffset(
InnerViewportScrollLayer()->element_id(),
false);
EXPECT_FALSE(did_request_next_frame_);
EXPECT_FALSE(did_request_redraw_);
EXPECT_EQ(base::TimeDelta(), requested_animation_delay_);
EXPECT_TRUE(animation_task_.is_null());
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 1, 4);
host_impl_->active_tree()->SetPageScaleOnActiveTree(1.1f);
EXPECT_FALSE(did_request_next_frame_);
EXPECT_FALSE(did_request_redraw_);
if (expecting_animations) {
EXPECT_EQ(base::Milliseconds(20), requested_animation_delay_);
EXPECT_FALSE(animation_task_.is_null());
requested_animation_delay_ = base::TimeDelta();
animation_task_.Reset();
} else {
EXPECT_EQ(base::TimeDelta(), requested_animation_delay_);
EXPECT_TRUE(animation_task_.is_null());
}
}
};
INSTANTIATE_COMMIT_TO_TREE_TEST_P(LayerTreeHostImplTestScrollbarAnimation);
TEST_P(LayerTreeHostImplTestScrollbarAnimation, Android) {
RunTest(LayerTreeSettings::ANDROID_OVERLAY);
}
TEST_P(LayerTreeHostImplTestScrollbarAnimation, AuraOverlay) {
RunTest(LayerTreeSettings::AURA_OVERLAY);
}
TEST_P(LayerTreeHostImplTestScrollbarAnimation, NoAnimator) {
RunTest(LayerTreeSettings::NO_ANIMATOR);
}
class LayerTreeHostImplTestScrollbarOpacity
: public CommitToPendingTreeLayerTreeHostImplTest {
protected:
void RunTest(LayerTreeSettings::ScrollbarAnimator animator) {
LayerTreeSettings settings = DefaultSettings();
settings.scrollbar_animator = animator;
settings.scrollbar_fade_delay = base::Milliseconds(20);
settings.scrollbar_fade_duration = base::Milliseconds(20);
gfx::Size viewport_size(50, 50);
gfx::Size content_size(100, 100);
bool expecting_animations = animator != LayerTreeSettings::NO_ANIMATOR;
CreateHostImpl(settings, CreateLayerTreeFrameSink());
CreatePendingTree();
SetupViewportLayers(host_impl_->pending_tree(), viewport_size, content_size,
content_size);
LayerImpl* scroll =
host_impl_->pending_tree()->OuterViewportScrollLayerForTesting();
auto* scrollbar = AddLayer<SolidColorScrollbarLayerImpl>(
host_impl_->pending_tree(), ScrollbarOrientation::kVertical, 10, 0,
false);
SetupScrollbarLayer(scroll, scrollbar);
scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(90, 0));
host_impl_->pending_tree()->PushPageScaleFromMainThread(1, 1, 1);
UpdateDrawProperties(host_impl_->pending_tree());
host_impl_->ActivateSyncTree();
LayerImpl* active_scrollbar_layer =
host_impl_->active_tree()->LayerById(scrollbar->id());
EffectNode* active_tree_node = GetEffectNode(active_scrollbar_layer);
EXPECT_FLOAT_EQ(active_scrollbar_layer->Opacity(),
active_tree_node->opacity);
if (expecting_animations) {
host_impl_->ScrollbarAnimationControllerForElementId(scroll->element_id())
->DidMouseMove(gfx::PointF(0, 90));
} else {
EXPECT_EQ(nullptr, host_impl_->ScrollbarAnimationControllerForElementId(
scroll->element_id()));
}
GetInputHandler().ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 5),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2dF(0, 5), ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
CreatePendingTree();
auto* pending_root = host_impl_->pending_tree()->root_layer();
auto& new_effect_node = CreateEffectNode(
GetPropertyTrees(pending_root), pending_root->effect_tree_index(),
pending_root->transform_tree_index(), pending_root->clip_tree_index());
new_effect_node.render_surface_reason = RenderSurfaceReason::kTest;
new_effect_node.element_id = ElementId(123);
LayerImpl* pending_scrollbar_layer =
host_impl_->pending_tree()->LayerById(scrollbar->id());
GetEffectNode(pending_scrollbar_layer)->parent_id = new_effect_node.id;
pending_scrollbar_layer->SetNeedsPushProperties();
UpdateDrawProperties(host_impl_->pending_tree());
EffectNode* pending_tree_node = GetEffectNode(pending_scrollbar_layer);
if (expecting_animations) {
EXPECT_FLOAT_EQ(1, active_tree_node->opacity);
EXPECT_FLOAT_EQ(1, active_scrollbar_layer->Opacity());
} else {
EXPECT_FLOAT_EQ(0, active_tree_node->opacity);
EXPECT_FLOAT_EQ(0, active_scrollbar_layer->Opacity());
}
EXPECT_FLOAT_EQ(0, pending_tree_node->opacity);
host_impl_->ActivateSyncTree();
active_tree_node = GetEffectNode(active_scrollbar_layer);
if (expecting_animations) {
EXPECT_FLOAT_EQ(1, active_tree_node->opacity);
EXPECT_FLOAT_EQ(1, active_scrollbar_layer->Opacity());
} else {
EXPECT_FLOAT_EQ(0, active_tree_node->opacity);
EXPECT_FLOAT_EQ(0, active_scrollbar_layer->Opacity());
}
}
};
TEST_F(LayerTreeHostImplTestScrollbarOpacity, Android) {
RunTest(LayerTreeSettings::ANDROID_OVERLAY);
}
TEST_F(LayerTreeHostImplTestScrollbarOpacity, AuraOverlay) {
RunTest(LayerTreeSettings::AURA_OVERLAY);
}
TEST_F(LayerTreeHostImplTestScrollbarOpacity, NoAnimator) {
RunTest(LayerTreeSettings::NO_ANIMATOR);
}
class LayerTreeHostImplTestMultiScrollable : public LayerTreeHostImplTest {
public:
void SetUpLayers(LayerTreeSettings settings) {
is_aura_scrollbar_ =
settings.scrollbar_animator == LayerTreeSettings::AURA_OVERLAY;
gfx::Size viewport_size(300, 200);
gfx::Size content_size(1000, 1000);
gfx::Size child_layer_size(250, 150);
gfx::Size scrollbar_size_1(gfx::Size(15, viewport_size.height()));
gfx::Size scrollbar_size_2(gfx::Size(15, child_layer_size.height()));
CreateHostImpl(settings, CreateLayerTreeFrameSink());
host_impl_->active_tree()->SetDeviceScaleFactor(1);
SetupViewportLayers(host_impl_->active_tree(), viewport_size, content_size,
content_size);
LayerImpl* root_scroll = OuterViewportScrollLayer();
scrollbar_1_ = AddLayer<SolidColorScrollbarLayerImpl>(
host_impl_->active_tree(), ScrollbarOrientation::kVertical, 15, 0,
true);
SetupScrollbarLayer(root_scroll, scrollbar_1_);
scrollbar_1_->SetBounds(scrollbar_size_1);
TouchActionRegion touch_action_region;
touch_action_region.Union(TouchAction::kNone, gfx::Rect(scrollbar_size_1));
scrollbar_1_->SetTouchActionRegion(touch_action_region);
auto* child =
AddScrollableLayer(root_scroll, gfx::Size(100, 100), child_layer_size);
GetTransformNode(child)->post_translation = gfx::Vector2dF(50, 50);
scrollbar_2_ = AddLayer<SolidColorScrollbarLayerImpl>(
host_impl_->active_tree(), ScrollbarOrientation::kVertical, 15, 0,
true);
SetupScrollbarLayer(child, scrollbar_2_);
scrollbar_2_->SetBounds(scrollbar_size_2);
host_impl_->active_tree()->UpdateAllScrollbarGeometriesForTesting();
UpdateDrawProperties(host_impl_->active_tree());
host_impl_->active_tree()->DidBecomeActive();
ResetScrollbars();
}
void ResetScrollbars() {
GetEffectNode(scrollbar_1_.get())->opacity = 0;
GetEffectNode(scrollbar_2_.get())->opacity = 0;
UpdateDrawProperties(host_impl_->active_tree());
if (is_aura_scrollbar_)
animation_task_.Reset();
}
bool is_aura_scrollbar_;
raw_ptr<SolidColorScrollbarLayerImpl> scrollbar_1_;
raw_ptr<SolidColorScrollbarLayerImpl> scrollbar_2_;
};
INSTANTIATE_CLIENT_MODE_TREE_TEST_P(LayerTreeHostImplTestMultiScrollable);
TEST_P(LayerTreeHostImplTestMultiScrollable,
ScrollbarFlashAfterAnyScrollUpdate) {
LayerTreeSettings settings = DefaultSettings();
settings.scrollbar_fade_delay = base::Milliseconds(500);
settings.scrollbar_fade_duration = base::Milliseconds(300);
settings.scrollbar_animator = LayerTreeSettings::AURA_OVERLAY;
settings.scrollbar_flash_after_any_scroll_update = true;
settings.scrollbar_flash_once_after_scroll_update = false;
SetUpLayers(settings);
EXPECT_EQ(scrollbar_1_->Opacity(), 0);
EXPECT_EQ(scrollbar_2_->Opacity(), 0);
GetInputHandler().RootScrollBegin(
BeginState(gfx::Point(20, 20), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(20, 20), gfx::Vector2d(0, 10), ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
EXPECT_TRUE(scrollbar_1_->Opacity());
EXPECT_TRUE(scrollbar_2_->Opacity());
EXPECT_FALSE(animation_task_.is_null());
ResetScrollbars();
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(70, 70), gfx::Vector2dF(0, 100),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
GetInputHandler().ScrollUpdate(
AnimatedUpdateState(gfx::Point(70, 70), gfx::Vector2d(0, 100)));
GetInputHandler().ScrollEnd();
EXPECT_TRUE(scrollbar_1_->Opacity());
EXPECT_TRUE(scrollbar_2_->Opacity());
EXPECT_FALSE(animation_task_.is_null());
}
TEST_P(LayerTreeHostImplTestMultiScrollable,
ScrollbarFlashOnceAfterAnyScrollUpdate) {
LayerTreeSettings settings = DefaultSettings();
settings.scrollbar_fade_delay = base::Milliseconds(500);
settings.scrollbar_fade_duration = base::Milliseconds(300);
settings.scrollbar_animator = LayerTreeSettings::AURA_OVERLAY;
settings.scrollbar_flash_after_any_scroll_update = false;
settings.scrollbar_flash_once_after_scroll_update = true;
SetUpLayers(settings);
EXPECT_EQ(scrollbar_1_->Opacity(), 0);
EXPECT_EQ(scrollbar_2_->Opacity(), 0);
GetInputHandler().RootScrollBegin(
BeginState(gfx::Point(20, 20), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(20, 20), gfx::Vector2d(0, 10), ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
EXPECT_TRUE(scrollbar_1_->Opacity());
EXPECT_TRUE(scrollbar_2_->Opacity());
EXPECT_FALSE(animation_task_.is_null());
ResetScrollbars();
GetInputHandler().RootScrollBegin(
BeginState(gfx::Point(70, 70), gfx::Vector2dF(0, 100),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(70, 70), gfx::Vector2d(0, 100), ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
EXPECT_TRUE(scrollbar_1_->Opacity());
EXPECT_FALSE(scrollbar_2_->Opacity());
EXPECT_FALSE(animation_task_.is_null());
ResetScrollbars();
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(70, 70), gfx::Vector2dF(0, 100),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
GetInputHandler().ScrollUpdate(
AnimatedUpdateState(gfx::Point(70, 70), gfx::Vector2d(0, 100)));
GetInputHandler().ScrollEnd();
EXPECT_FALSE(scrollbar_1_->Opacity());
EXPECT_TRUE(scrollbar_2_->Opacity());
EXPECT_FALSE(animation_task_.is_null());
}
TEST_P(LayerTreeHostImplTestMultiScrollable,
ScrollbarFlashOnceEnteredViewport) {
LayerTreeSettings settings = DefaultSettings();
settings.scrollbar_fade_delay = base::Milliseconds(500);
settings.scrollbar_fade_duration = base::Milliseconds(300);
settings.scrollbar_animator = LayerTreeSettings::AURA_OVERLAY;
settings.scrollbar_flash_after_any_scroll_update = false;
settings.scrollbar_flash_once_after_scroll_update = false;
settings.scrollbar_flash_once_visible_on_viewport = true;
SetUpLayers(settings);
raw_ptr<SolidColorScrollbarLayerImpl> scrollbar3 = nullptr;
{
LayerImpl* root_scroll = OuterViewportScrollLayer();
auto* child = AddScrollableLayer(root_scroll, gfx::Size(100, 100),
gfx::Size(250, 150));
GetTransformNode(child)->post_translation = gfx::Vector2dF(50, 50);
scrollbar3 = AddLayer<SolidColorScrollbarLayerImpl>(
host_impl_->active_tree(), ScrollbarOrientation::kVertical, 15, 0,
true);
SetupScrollbarLayer(child, scrollbar3);
scrollbar3->SetBounds(gfx::Size(50, 150));
scrollbar3->SetOffsetToTransformParent(gfx::Vector2dF(10, 210));
host_impl_->active_tree()->UpdateAllScrollbarGeometriesForTesting();
UpdateDrawProperties(host_impl_->active_tree());
host_impl_->active_tree()->DidBecomeActive();
}
EXPECT_EQ(scrollbar_1_->Opacity(), 0);
EXPECT_EQ(scrollbar_2_->Opacity(), 0);
EXPECT_EQ(scrollbar3->Opacity(), 0);
GetInputHandler().RootScrollBegin(
BeginState(gfx::Point(20, 20), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(20, 20), gfx::Vector2d(0, 10), ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
EXPECT_TRUE(scrollbar_1_->Opacity());
EXPECT_FALSE(scrollbar_2_->Opacity());
EXPECT_FALSE(scrollbar3->Opacity());
EXPECT_FALSE(animation_task_.is_null());
GetEffectNode(scrollbar3.get())->opacity = 0;
ResetScrollbars();
GetInputHandler().RootScrollBegin(
BeginState(gfx::Point(150, 100), gfx::Vector2dF(0, 200),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
GetInputHandler().ScrollUpdate(UpdateState(gfx::Point(150, 100),
gfx::Vector2d(0, 200),
ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
EXPECT_TRUE(scrollbar_1_->Opacity());
EXPECT_TRUE(scrollbar_2_->Opacity());
EXPECT_FALSE(scrollbar3->Opacity());
EXPECT_FALSE(animation_task_.is_null());
GetEffectNode(scrollbar3.get())->opacity = 0;
ResetScrollbars();
GetInputHandler().RootScrollBegin(
BeginState(gfx::Point(150, 100), gfx::Vector2dF(0, 30),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(150, 100), gfx::Vector2d(0, 30), ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
EXPECT_TRUE(scrollbar_1_->Opacity());
EXPECT_FALSE(scrollbar_2_->Opacity());
EXPECT_TRUE(scrollbar3->Opacity());
EXPECT_FALSE(animation_task_.is_null());
GetEffectNode(scrollbar3.get())->opacity = 0;
ResetScrollbars();
GetInputHandler().RootScrollBegin(
BeginState(gfx::Point(150, 100), gfx::Vector2dF(0, 300),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
GetInputHandler().ScrollUpdate(UpdateState(gfx::Point(150, 100),
gfx::Vector2d(0, 300),
ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
EXPECT_TRUE(scrollbar_1_->Opacity());
EXPECT_FALSE(scrollbar_2_->Opacity());
EXPECT_FALSE(scrollbar3->Opacity());
EXPECT_FALSE(animation_task_.is_null());
GetEffectNode(scrollbar3.get())->opacity = 0;
ResetScrollbars();
GetInputHandler().RootScrollBegin(
BeginState(gfx::Point(150, 100), gfx::Vector2dF(0, -310),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
GetInputHandler().ScrollUpdate(UpdateState(gfx::Point(150, 100),
gfx::Vector2d(0, -310),
ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
EXPECT_TRUE(scrollbar_1_->Opacity());
EXPECT_FALSE(scrollbar_2_->Opacity());
EXPECT_FALSE(scrollbar3->Opacity());
EXPECT_FALSE(animation_task_.is_null());
GetEffectNode(scrollbar3.get())->opacity = 0;
ResetScrollbars();
GetInputHandler().RootScrollBegin(
BeginState(gfx::Point(150, 100), gfx::Vector2dF(0, 25),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(150, 100), gfx::Vector2d(0, 25), ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
EXPECT_TRUE(scrollbar_1_->Opacity());
EXPECT_FALSE(scrollbar_2_->Opacity());
EXPECT_TRUE(scrollbar3->Opacity());
EXPECT_FALSE(animation_task_.is_null());
GetEffectNode(scrollbar3.get())->opacity = 0;
ResetScrollbars();
}
TEST_P(LayerTreeHostImplTestMultiScrollable, ScrollbarFlashWhenMouseEnter) {
LayerTreeSettings settings = DefaultSettings();
settings.scrollbar_fade_delay = base::Milliseconds(500);
settings.scrollbar_fade_duration = base::Milliseconds(300);
settings.scrollbar_animator = LayerTreeSettings::AURA_OVERLAY;
settings.scrollbar_flash_when_mouse_enter = true;
SetUpLayers(settings);
constexpr size_t kNumberOfRepeats = 3;
for (size_t i = 0; i < kNumberOfRepeats; i++) {
ScrollbarAnimationController* scrollbar_animation_controller =
host_impl_->ScrollbarAnimationControllerForElementId(
scrollbar_1_->scroll_element_id());
const float kMouseMoveDistanceToTriggerFadeIn =
scrollbar_animation_controller
->GetScrollbarAnimationController(ScrollbarOrientation::kVertical)
.MouseMoveDistanceToTriggerFadeIn();
const int thumb_thickness = scrollbar_1_->ThumbThickness();
GetInputHandler().MouseMoveAt(
gfx::Point(thumb_thickness + kMouseMoveDistanceToTriggerFadeIn + 1, 1));
EXPECT_FALSE(scrollbar_animation_controller->MouseIsNearScrollbar(
ScrollbarOrientation::kVertical));
EXPECT_FALSE(scrollbar_animation_controller->MouseIsNearScrollbarThumb(
ScrollbarOrientation::kVertical));
EXPECT_FALSE(scrollbar_1_->Opacity());
EXPECT_FALSE(scrollbar_2_->Opacity());
ResetScrollbars();
ScrollbarAnimationController* scrollbar_animation_controller2 =
host_impl_->ScrollbarAnimationControllerForElementId(
scrollbar_2_->scroll_element_id());
const float kMouseMoveDistanceToTriggerFadeInChild =
scrollbar_animation_controller2
->GetScrollbarAnimationController(ScrollbarOrientation::kVertical)
.MouseMoveDistanceToTriggerFadeIn();
const int kThumbThicknessChild = scrollbar_2_->ThumbThickness();
GetInputHandler().MouseMoveAt(gfx::Point(
kThumbThicknessChild + kMouseMoveDistanceToTriggerFadeInChild + 50,
50));
EXPECT_FALSE(scrollbar_animation_controller2->MouseIsNearScrollbar(
ScrollbarOrientation::kVertical));
EXPECT_FALSE(scrollbar_animation_controller2->MouseIsNearScrollbarThumb(
ScrollbarOrientation::kVertical));
EXPECT_FALSE(scrollbar_1_->Opacity());
EXPECT_TRUE(scrollbar_2_->Opacity());
ResetScrollbars();
}
}
TEST_P(LayerTreeHostImplTest, ScrollHitTestOnScrollbar) {
LayerTreeSettings settings = DefaultSettings();
settings.scrollbar_fade_delay = base::Milliseconds(500);
settings.scrollbar_fade_duration = base::Milliseconds(300);
settings.scrollbar_animator = LayerTreeSettings::NO_ANIMATOR;
gfx::Size viewport_size(300, 200);
gfx::Size content_size(1000, 1000);
gfx::Size child_layer_size(250, 150);
gfx::Size scrollbar_size_1(gfx::Size(15, viewport_size.height()));
gfx::Size scrollbar_size_2(gfx::Size(15, child_layer_size.height()));
CreateHostImpl(settings, CreateLayerTreeFrameSink());
host_impl_->active_tree()->SetDeviceScaleFactor(1);
SetupViewportLayersInnerScrolls(viewport_size, content_size);
LayerImpl* root_scroll = OuterViewportScrollLayer();
auto* scrollbar_1 = AddLayer<PaintedScrollbarLayerImpl>(
host_impl_->active_tree(), ScrollbarOrientation::kVertical, true, true);
SetupScrollbarLayer(root_scroll, scrollbar_1);
scrollbar_1->SetBounds(scrollbar_size_1);
TouchActionRegion touch_action_region;
touch_action_region.Union(TouchAction::kNone, gfx::Rect(scrollbar_size_1));
scrollbar_1->SetTouchActionRegion(touch_action_region);
LayerImpl* child =
AddScrollableLayer(root_scroll, gfx::Size(100, 100), child_layer_size);
GetTransformNode(child)->post_translation = gfx::Vector2dF(50, 50);
auto* scrollbar_2 = AddLayer<PaintedScrollbarLayerImpl>(
host_impl_->active_tree(), ScrollbarOrientation::kVertical, true, true);
SetupScrollbarLayer(child, scrollbar_2);
scrollbar_2->SetBounds(scrollbar_size_2);
scrollbar_2->SetOffsetToTransformParent(gfx::Vector2dF(50, 50));
UpdateDrawProperties(host_impl_->active_tree());
host_impl_->active_tree()->DidBecomeActive();
{
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(1, 1), gfx::Vector2dF(),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
GetInputHandler().ScrollEnd();
}
{
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(1, 1), gfx::Vector2dF(),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
}
{
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(51, 51), gfx::Vector2dF(),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
GetInputHandler().ScrollEnd();
}
{
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(51, 51), gfx::Vector2dF(),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
}
}
TEST_P(LayerTreeHostImplTest, NullScrollerLayerForScrollbarLayer) {
LayerTreeImpl* layer_tree_impl = host_impl_->active_tree();
SetupDefaultRootLayer(gfx::Size(200, 200));
LayerImpl* scroll = AddScrollableLayer(root_layer(), gfx::Size(100, 100),
gfx::Size(1000, 1000));
auto* scrollbar = AddLayer<PaintedScrollbarLayerImpl>(
layer_tree_impl, ScrollbarOrientation::kVertical, false, true);
SetupScrollbarLayer(scroll, scrollbar);
scrollbar->SetBounds(gfx::Size(15, 100));
scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(85, 0));
UpdateDrawProperties(layer_tree_impl);
auto status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(90, 50), gfx::Vector2d(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
GetInputHandler().ScrollEnd();
scrollbar->SetScrollElementId(
ElementId(scrollbar->scroll_element_id().GetInternalValue() + 123));
UpdateDrawProperties(layer_tree_impl);
status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(90, 50), gfx::Vector2d(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
EXPECT_EQ(ScrollThread::kScrollIgnored, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
GetInputHandler().ScrollEnd();
}
TEST_F(CommitToPendingTreeLayerTreeHostImplTest,
ScrollbarVisibilityChangeCausesRedrawAndCommit) {
LayerTreeSettings settings = DefaultSettings();
settings.scrollbar_animator = LayerTreeSettings::AURA_OVERLAY;
settings.scrollbar_fade_delay = base::Milliseconds(20);
settings.scrollbar_fade_duration = base::Milliseconds(20);
gfx::Size viewport_size(50, 50);
gfx::Size content_size(100, 100);
CreateHostImpl(settings, CreateLayerTreeFrameSink());
CreatePendingTree();
SetupViewportLayers(host_impl_->pending_tree(), viewport_size, content_size,
content_size);
LayerImpl* scroll =
host_impl_->pending_tree()->OuterViewportScrollLayerForTesting();
auto* scrollbar = AddLayer<SolidColorScrollbarLayerImpl>(
host_impl_->pending_tree(), ScrollbarOrientation::kVertical, 10, 0,
false);
SetupScrollbarLayer(scroll, scrollbar);
scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(90, 0));
host_impl_->pending_tree()->PushPageScaleFromMainThread(1, 1, 1);
host_impl_->ActivateSyncTree();
ScrollbarAnimationController* scrollbar_controller =
host_impl_->ScrollbarAnimationControllerForElementId(
scroll->element_id());
ASSERT_FALSE(scrollbar_controller->ScrollbarsHidden());
EXPECT_TRUE(scrollbar_controller->visibility_changed());
ClearMainThreadDeltasForTesting(host_impl_.get());
auto commit_data = host_impl_->ProcessCompositorDeltas(
nullptr);
using ScrollbarsInfo = CompositorCommitData::ScrollbarsUpdateInfo;
EXPECT_THAT(commit_data->scrollbars, testing::ElementsAre(ScrollbarsInfo{
scroll->element_id(), false}));
EXPECT_FALSE(scrollbar_controller->visibility_changed());
{
ASSERT_FALSE(animation_task_.is_null());
ASSERT_FALSE(animation_task_.IsCancelled());
std::move(animation_task_).Run();
base::TimeTicks fake_now = base::TimeTicks::Now();
scrollbar_controller->Animate(fake_now);
fake_now += settings.scrollbar_fade_delay;
scrollbar_controller->Animate(fake_now);
ASSERT_TRUE(scrollbar_controller->ScrollbarsHidden());
ClearMainThreadDeltasForTesting(host_impl_.get());
commit_data = host_impl_->ProcessCompositorDeltas(
nullptr);
EXPECT_THAT(commit_data->scrollbars, testing::ElementsAre(ScrollbarsInfo{
scroll->element_id(), true}));
EXPECT_FALSE(scrollbar_controller->visibility_changed());
}
{
animation_task_.Reset();
scrollbar_controller->DidMouseMove(gfx::PointF(90, 0));
ASSERT_FALSE(animation_task_.is_null());
ASSERT_FALSE(animation_task_.IsCancelled());
}
{
did_request_redraw_ = false;
did_request_commit_ = false;
ASSERT_TRUE(scrollbar_controller->ScrollbarsHidden());
EXPECT_FALSE(scrollbar_controller->visibility_changed());
ClearMainThreadDeltasForTesting(host_impl_.get());
commit_data = host_impl_->ProcessCompositorDeltas(
nullptr);
EXPECT_TRUE(commit_data->scrollbars.empty());
std::move(animation_task_).Run();
base::TimeTicks fake_now = base::TimeTicks::Now();
scrollbar_controller->Animate(fake_now);
fake_now += settings.scrollbar_fade_duration;
scrollbar_controller->Animate(fake_now);
ASSERT_FALSE(scrollbar_controller->ScrollbarsHidden());
EXPECT_TRUE(scrollbar_controller->visibility_changed());
ClearMainThreadDeltasForTesting(host_impl_.get());
commit_data = host_impl_->ProcessCompositorDeltas(
nullptr);
EXPECT_THAT(commit_data->scrollbars, testing::ElementsAre(ScrollbarsInfo{
scroll->element_id(), false}));
EXPECT_FALSE(scrollbar_controller->visibility_changed());
EXPECT_TRUE(did_request_redraw_);
EXPECT_TRUE(did_request_commit_);
}
}
TEST_P(LayerTreeHostImplTest, ScrollbarInnerLargerThanOuter) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
gfx::Size inner_viewport_size(315, 200);
gfx::Size outer_viewport_size(300, 200);
gfx::Size content_size(1000, 1000);
SetupViewportLayers(host_impl_->active_tree(), inner_viewport_size,
outer_viewport_size, content_size);
LayerImpl* root_scroll = OuterViewportScrollLayer();
auto* horiz_scrollbar = AddLayer<PaintedScrollbarLayerImpl>(
host_impl_->active_tree(), ScrollbarOrientation::kHorizontal, true, true);
SetupScrollbarLayer(root_scroll, horiz_scrollbar);
LayerImpl* child = AddLayerInActiveTree();
child->SetBounds(content_size);
child->SetBounds(inner_viewport_size);
host_impl_->active_tree()->UpdateAllScrollbarGeometriesForTesting();
EXPECT_EQ(300, horiz_scrollbar->clip_layer_length());
}
TEST_P(LayerTreeHostImplTest, ScrollbarDeviceLargerThanOuter) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
auto* active_tree = host_impl_->active_tree();
gfx::Size device_size(600, 800);
gfx::Size outer_viewport_size(300, 400);
gfx::Size content_size(300, 1000);
SetupViewportLayers(active_tree, device_size,
outer_viewport_size, content_size);
active_tree->PushPageScaleFromMainThread( 2,
2,
4);
LayerImpl* root_scroll = OuterViewportScrollLayer();
auto* horiz_scrollbar = AddLayer<PaintedScrollbarLayerImpl>(
active_tree, ScrollbarOrientation::kHorizontal, true, true);
auto* vert_scrollbar = AddLayer<PaintedScrollbarLayerImpl>(
active_tree, ScrollbarOrientation::kVertical, true, true);
SetupScrollbarLayer(root_scroll, horiz_scrollbar);
SetupScrollbarLayer(root_scroll, vert_scrollbar);
LayerImpl* child = AddLayerInActiveTree();
child->SetBounds(content_size);
host_impl_->active_tree()->UpdateAllScrollbarGeometriesForTesting();
EXPECT_EQ(300, horiz_scrollbar->clip_layer_length());
EXPECT_EQ(300, horiz_scrollbar->scroll_layer_length());
EXPECT_EQ(400, vert_scrollbar->clip_layer_length());
EXPECT_EQ(1000, vert_scrollbar->scroll_layer_length());
}
TEST_P(LayerTreeHostImplTest, ScrollbarRegistration) {
LayerTreeSettings settings = DefaultSettings();
settings.scrollbar_animator = LayerTreeSettings::ANDROID_OVERLAY;
settings.scrollbar_fade_delay = base::Milliseconds(20);
settings.scrollbar_fade_duration = base::Milliseconds(20);
CreateHostImpl(settings, CreateLayerTreeFrameSink());
gfx::Size viewport_size(300, 200);
gfx::Size content_size(1000, 1000);
SetupViewportLayersInnerScrolls(viewport_size, content_size);
auto* container = InnerViewportScrollLayer();
auto* root_scroll = OuterViewportScrollLayer();
auto* vert_1_scrollbar = AddLayer<SolidColorScrollbarLayerImpl>(
host_impl_->active_tree(), ScrollbarOrientation::kVertical, 5, 5, true);
CopyProperties(container, vert_1_scrollbar);
auto* horiz_1_scrollbar = AddLayer<SolidColorScrollbarLayerImpl>(
host_impl_->active_tree(), ScrollbarOrientation::kHorizontal, 5, 5, true);
CopyProperties(container, horiz_1_scrollbar);
auto* vert_2_scrollbar = AddLayer<SolidColorScrollbarLayerImpl>(
host_impl_->active_tree(), ScrollbarOrientation::kVertical, 5, 5, true);
CopyProperties(container, vert_2_scrollbar);
auto* horiz_2_scrollbar = AddLayer<SolidColorScrollbarLayerImpl>(
host_impl_->active_tree(), ScrollbarOrientation::kHorizontal, 5, 5, true);
CopyProperties(container, horiz_2_scrollbar);
UpdateDrawProperties(host_impl_->active_tree());
EXPECT_EQ(0ul, host_impl_->ScrollbarsFor(root_scroll->element_id()).size());
EXPECT_EQ(nullptr, host_impl_->ScrollbarAnimationControllerForElementId(
root_scroll->element_id()));
vert_1_scrollbar->SetScrollElementId(root_scroll->element_id());
EXPECT_EQ(1ul, host_impl_->ScrollbarsFor(root_scroll->element_id()).size());
EXPECT_TRUE(host_impl_->ScrollbarAnimationControllerForElementId(
root_scroll->element_id()));
horiz_1_scrollbar->SetScrollElementId(root_scroll->element_id());
EXPECT_EQ(2ul, host_impl_->ScrollbarsFor(root_scroll->element_id()).size());
EXPECT_TRUE(host_impl_->ScrollbarAnimationControllerForElementId(
root_scroll->element_id()));
animation_task_.Reset();
GetInputHandler().ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(10, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2d(10, 10), ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
EXPECT_FALSE(animation_task_.is_null());
animation_task_.Reset();
LayerImpl* child =
AddScrollableLayer(root_scroll, viewport_size, gfx::Size(200, 200));
ElementId child_scroll_element_id = child->element_id();
EXPECT_EQ(0ul, host_impl_->ScrollbarsFor(child_scroll_element_id).size());
EXPECT_EQ(nullptr, host_impl_->ScrollbarAnimationControllerForElementId(
child_scroll_element_id));
vert_2_scrollbar->SetScrollElementId(child_scroll_element_id);
EXPECT_EQ(1ul, host_impl_->ScrollbarsFor(child_scroll_element_id).size());
EXPECT_TRUE(host_impl_->ScrollbarAnimationControllerForElementId(
child_scroll_element_id));
horiz_2_scrollbar->SetScrollElementId(child_scroll_element_id);
EXPECT_EQ(2ul, host_impl_->ScrollbarsFor(child_scroll_element_id).size());
EXPECT_TRUE(host_impl_->ScrollbarAnimationControllerForElementId(
child_scroll_element_id));
animation_task_.Reset();
host_impl_->active_tree()->RequestShowScrollbars(child_scroll_element_id);
UpdateDrawProperties(host_impl_->active_tree());
host_impl_->active_tree()->HandleScrollbarShowRequests();
EXPECT_FALSE(animation_task_.is_null());
animation_task_.Reset();
ElementId root_scroll_element_id = root_scroll->element_id();
host_impl_->active_tree()->DetachLayers();
EXPECT_EQ(0ul, host_impl_->ScrollbarsFor(root_scroll_element_id).size());
EXPECT_EQ(nullptr, host_impl_->ScrollbarAnimationControllerForElementId(
root_scroll_element_id));
EXPECT_EQ(0ul, host_impl_->ScrollbarsFor(child_scroll_element_id).size());
EXPECT_EQ(nullptr, host_impl_->ScrollbarAnimationControllerForElementId(
root_scroll_element_id));
}
TEST_P(LayerTreeHostImplTest, ScrollBeforeMouseMove) {
LayerTreeSettings settings = DefaultSettings();
settings.scrollbar_animator = LayerTreeSettings::AURA_OVERLAY;
settings.scrollbar_fade_delay = base::Milliseconds(20);
settings.scrollbar_fade_duration = base::Milliseconds(20);
CreateHostImpl(settings, CreateLayerTreeFrameSink());
gfx::Size viewport_size(300, 200);
gfx::Size content_size(1000, 1000);
SetupViewportLayersInnerScrolls(viewport_size, content_size);
auto* root_scroll = OuterViewportScrollLayer();
const int kScrollbarThickness = 5;
auto* vert_scrollbar = AddLayer<SolidColorScrollbarLayerImpl>(
host_impl_->active_tree(), ScrollbarOrientation::kVertical,
kScrollbarThickness, 0, false);
SetupScrollbarLayer(root_scroll, vert_scrollbar);
vert_scrollbar->SetBounds(gfx::Size(10, 200));
vert_scrollbar->SetOffsetToTransformParent(
gfx::Vector2dF(300 - kScrollbarThickness, 0));
host_impl_->active_tree()->UpdateAllScrollbarGeometriesForTesting();
EXPECT_EQ(1ul, host_impl_->ScrollbarsFor(root_scroll->element_id()).size());
auto* scrollbar_controller =
host_impl_->ScrollbarAnimationControllerForElementId(
root_scroll->element_id());
const float kDistanceToTriggerThumb =
vert_scrollbar->ComputeThumbQuadRect().height() +
scrollbar_controller
->GetScrollbarAnimationController(ScrollbarOrientation::kVertical)
.MouseMoveDistanceToTriggerExpand();
auto near_thumb_at_top = gfx::Point(295, kDistanceToTriggerThumb - 1);
GetInputHandler().MouseMoveAt(near_thumb_at_top);
EXPECT_TRUE(scrollbar_controller->MouseIsNearScrollbarThumb(
ScrollbarOrientation::kVertical));
GetInputHandler().MouseMoveAt(gfx::Point(295, kDistanceToTriggerThumb + 1));
EXPECT_FALSE(scrollbar_controller->MouseIsNearScrollbarThumb(
ScrollbarOrientation::kVertical));
GetInputHandler().ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 800),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2d(0, 800), ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
GetInputHandler().MouseMoveAt(near_thumb_at_top);
EXPECT_FALSE(scrollbar_controller->MouseIsNearScrollbarThumb(
ScrollbarOrientation::kVertical));
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(), gfx::Vector2dF(0, -800),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2d(0, -800), ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
GetInputHandler().MouseMoveAt(near_thumb_at_top);
EXPECT_TRUE(scrollbar_controller->MouseIsNearScrollbarThumb(
ScrollbarOrientation::kVertical));
}
TEST_P(LayerTreeHostImplTest, MouseMoveAtWithDeviceScaleOf1) {
SetupMouseMoveAtWithDeviceScale(1);
}
TEST_P(LayerTreeHostImplTest, MouseMoveAtWithDeviceScaleOf2) {
SetupMouseMoveAtWithDeviceScale(2);
}
TEST_P(CompositorFrameProducingLayerTreeHostImplTest,
ActivationDependenciesInMetadata) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
LayerImpl* root = root_layer();
std::vector<viz::SurfaceId> primary_surfaces = {
MakeSurfaceId(viz::FrameSinkId(1, 1), 1),
MakeSurfaceId(viz::FrameSinkId(2, 2), 2),
MakeSurfaceId(viz::FrameSinkId(3, 3), 3)};
std::vector<viz::SurfaceId> fallback_surfaces = {
MakeSurfaceId(viz::FrameSinkId(4, 4), 1),
MakeSurfaceId(viz::FrameSinkId(4, 4), 2),
MakeSurfaceId(viz::FrameSinkId(4, 4), 3)};
for (size_t i = 0; i < primary_surfaces.size(); ++i) {
auto* child = AddLayer<SurfaceLayerImpl>(host_impl_->active_tree());
child->SetBounds(gfx::Size(1, 1));
child->SetDrawsContent(true);
child->SetRange(
viz::SurfaceRange(fallback_surfaces[i], primary_surfaces[i]), 2u);
CopyProperties(root, child);
child->SetOffsetToTransformParent(gfx::Vector2dF(25.0f * i, 0));
}
base::flat_set<viz::SurfaceRange> surfaces_set;
for (size_t i = 0; i < fallback_surfaces.size(); ++i) {
surfaces_set.insert(
viz::SurfaceRange(fallback_surfaces[i], primary_surfaces[i]));
}
host_impl_->active_tree()->SetSurfaceRanges(std::move(surfaces_set));
host_impl_->SetFullViewportDamage();
DrawFrame();
{
auto* fake_layer_tree_frame_sink = static_cast<FakeLayerTreeFrameSink*>(
host_impl_->layer_tree_frame_sink());
const viz::CompositorFrameMetadata& metadata =
fake_layer_tree_frame_sink->last_sent_frame()->metadata;
EXPECT_THAT(metadata.activation_dependencies,
testing::UnorderedElementsAre(primary_surfaces[0],
primary_surfaces[1]));
EXPECT_THAT(
metadata.referenced_surfaces,
testing::UnorderedElementsAre(
viz::SurfaceRange(fallback_surfaces[0], primary_surfaces[0]),
viz::SurfaceRange(fallback_surfaces[1], primary_surfaces[1]),
viz::SurfaceRange(fallback_surfaces[2], primary_surfaces[2])));
EXPECT_EQ(2u, metadata.deadline.deadline_in_frames());
EXPECT_FALSE(metadata.deadline.use_default_lower_bound_deadline());
}
host_impl_->SetFullViewportDamage();
DrawFrame();
{
auto* fake_layer_tree_frame_sink = static_cast<FakeLayerTreeFrameSink*>(
host_impl_->layer_tree_frame_sink());
const viz::CompositorFrameMetadata& metadata =
fake_layer_tree_frame_sink->last_sent_frame()->metadata;
EXPECT_THAT(metadata.activation_dependencies,
testing::UnorderedElementsAre(primary_surfaces[0],
primary_surfaces[1]));
EXPECT_THAT(
metadata.referenced_surfaces,
testing::UnorderedElementsAre(
viz::SurfaceRange(fallback_surfaces[0], primary_surfaces[0]),
viz::SurfaceRange(fallback_surfaces[1], primary_surfaces[1]),
viz::SurfaceRange(fallback_surfaces[2], primary_surfaces[2])));
EXPECT_EQ(0u, metadata.deadline.deadline_in_frames());
EXPECT_FALSE(metadata.deadline.use_default_lower_bound_deadline());
}
}
TEST_P(CompositorFrameProducingLayerTreeHostImplTest,
SurfaceReferencesChangeCausesDamage) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
auto* fake_layer_tree_frame_sink =
static_cast<FakeLayerTreeFrameSink*>(host_impl_->layer_tree_frame_sink());
host_impl_->active_tree()->SetSurfaceRanges({});
host_impl_->SetFullViewportDamage();
DrawFrame();
{
const viz::CompositorFrameMetadata& metadata =
fake_layer_tree_frame_sink->last_sent_frame()->metadata;
EXPECT_THAT(metadata.referenced_surfaces, testing::IsEmpty());
}
const viz::SurfaceId surface_id = MakeSurfaceId(viz::FrameSinkId(1, 1), 1);
host_impl_->active_tree()->SetSurfaceRanges({viz::SurfaceRange(surface_id)});
DrawFrame();
{
const viz::CompositorFrameMetadata& metadata =
fake_layer_tree_frame_sink->last_sent_frame()->metadata;
EXPECT_THAT(metadata.referenced_surfaces,
testing::UnorderedElementsAre(viz::SurfaceRange(surface_id)));
}
}
TEST_P(LayerTreeHostImplTest, CompositorFrameMetadata) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 0.5f, 4);
DrawFrame();
{
viz::CompositorFrameMetadata metadata =
host_impl_->MakeCompositorFrameMetadata();
EXPECT_EQ(gfx::PointF(), metadata.root_scroll_offset);
EXPECT_EQ(1, metadata.page_scale_factor);
EXPECT_EQ(gfx::SizeF(50, 50), metadata.scrollable_viewport_size);
EXPECT_EQ(0.5f, metadata.min_page_scale_factor);
}
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
GetInputHandler().ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10),
ui::ScrollInputType::kWheel));
{
viz::CompositorFrameMetadata metadata =
host_impl_->MakeCompositorFrameMetadata();
EXPECT_EQ(gfx::PointF(0, 10), metadata.root_scroll_offset);
}
GetInputHandler().ScrollEnd();
{
viz::CompositorFrameMetadata metadata =
host_impl_->MakeCompositorFrameMetadata();
EXPECT_EQ(gfx::PointF(0, 10), metadata.root_scroll_offset);
}
GetInputHandler().ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().PinchGestureBegin(gfx::Point(),
ui::ScrollInputType::kWheel);
GetInputHandler().PinchGestureUpdate(2, gfx::Point());
GetInputHandler().PinchGestureEnd(gfx::Point());
GetInputHandler().ScrollEnd();
{
viz::CompositorFrameMetadata metadata =
host_impl_->MakeCompositorFrameMetadata();
EXPECT_EQ(gfx::PointF(0, 10), metadata.root_scroll_offset);
EXPECT_EQ(2, metadata.page_scale_factor);
EXPECT_EQ(gfx::SizeF(25, 25), metadata.scrollable_viewport_size);
EXPECT_EQ(0.5f, metadata.min_page_scale_factor);
}
host_impl_->ProcessCompositorDeltas( nullptr);
host_impl_->active_tree()->PushPageScaleFromMainThread(4, 0.5f, 4);
host_impl_->active_tree()->SetPageScaleOnActiveTree(4);
{
viz::CompositorFrameMetadata metadata =
host_impl_->MakeCompositorFrameMetadata();
EXPECT_EQ(gfx::PointF(0, 10), metadata.root_scroll_offset);
EXPECT_EQ(4, metadata.page_scale_factor);
EXPECT_EQ(gfx::SizeF(12.5f, 12.5f), metadata.scrollable_viewport_size);
EXPECT_EQ(0.5f, metadata.min_page_scale_factor);
}
}
TEST_P(CompositorFrameProducingLayerTreeHostImplTest,
DamageShouldNotCareAboutContributingLayers) {
auto* root = SetupRootLayer<DidDrawCheckLayer>(host_impl_->active_tree(),
gfx::Size(10, 10));
auto* layer = AddLayer<SolidColorLayerImpl>(host_impl_->active_tree());
layer->SetBounds(gfx::Size(10, 10));
layer->SetDrawsContent(true);
layer->SetBackgroundColor(SkColors::kRed);
CopyProperties(root, layer);
UpdateDrawProperties(host_impl_->active_tree());
{
TestFrameData frame;
auto args = viz::CreateBeginFrameArgsForTesting(
BEGINFRAME_FROM_HERE, viz::BeginFrameArgs::kManualSourceId, 1,
base::TimeTicks() + base::Milliseconds(1));
host_impl_->WillBeginImplFrame(args);
EXPECT_EQ(DrawResult::kSuccess, host_impl_->PrepareToDraw(&frame));
EXPECT_FALSE(frame.has_no_damage);
EXPECT_NE(frame.render_passes.size(), 0u);
size_t total_quad_count = 0;
for (const auto& pass : frame.render_passes)
total_quad_count += pass->quad_list.size();
EXPECT_NE(total_quad_count, 0u);
host_impl_->DrawLayers(&frame);
host_impl_->DidDrawAllLayers(frame);
host_impl_->DidFinishImplFrame(args);
}
layer->SetDrawsContent(false);
GetEffectNode(root)->opacity = 0;
UpdateDrawProperties(host_impl_->active_tree());
EXPECT_EQ(host_impl_->active_tree()->background_color(),
SkColors::kTransparent);
{
TestFrameData frame;
auto args = viz::CreateBeginFrameArgsForTesting(
BEGINFRAME_FROM_HERE, viz::BeginFrameArgs::kManualSourceId, 1,
base::TimeTicks() + base::Milliseconds(1));
host_impl_->WillBeginImplFrame(args);
EXPECT_EQ(DrawResult::kSuccess, host_impl_->PrepareToDraw(&frame));
EXPECT_FALSE(frame.has_no_damage);
EXPECT_NE(frame.render_passes.size(), 0u);
size_t total_quad_count = 0;
for (const auto& pass : frame.render_passes)
total_quad_count += pass->quad_list.size();
EXPECT_EQ(total_quad_count, 0u);
host_impl_->DrawLayers(&frame);
host_impl_->DidDrawAllLayers(frame);
host_impl_->DidFinishImplFrame(args);
}
{
TestFrameData frame;
auto args = viz::CreateBeginFrameArgsForTesting(
BEGINFRAME_FROM_HERE, viz::BeginFrameArgs::kManualSourceId, 1,
base::TimeTicks() + base::Milliseconds(1));
host_impl_->WillBeginImplFrame(args);
EXPECT_EQ(DrawResult::kSuccess, host_impl_->PrepareToDraw(&frame));
EXPECT_TRUE(frame.has_no_damage);
EXPECT_EQ(frame.render_passes.size(), 0u);
size_t total_quad_count = 0;
for (const auto& pass : frame.render_passes)
total_quad_count += pass->quad_list.size();
EXPECT_EQ(total_quad_count, 0u);
host_impl_->DrawLayers(&frame);
host_impl_->DidDrawAllLayers(frame);
host_impl_->DidFinishImplFrame(args);
}
}
TEST_P(LayerTreeHostImplTest, WillDrawReturningFalseDoesNotCall) {
auto* root = SetupRootLayer<DidDrawCheckLayer>(host_impl_->active_tree(),
gfx::Size(10, 10));
auto* layer = AddLayer<DidDrawCheckLayer>(host_impl_->active_tree());
CopyProperties(root, layer);
DrawFrame();
EXPECT_TRUE(layer->will_draw_returned_true());
EXPECT_EQ(layer->append_quads_called(),
!host_impl_->GetSettings().TreesInVizInClientProcess());
EXPECT_TRUE(layer->did_draw_called());
host_impl_->SetViewportDamage(gfx::Rect(10, 10));
layer->set_will_draw_returns_false();
layer->ClearDidDrawCheck();
DrawFrame();
EXPECT_FALSE(layer->will_draw_returned_true());
EXPECT_FALSE(layer->append_quads_called());
EXPECT_FALSE(layer->did_draw_called());
}
TEST_P(LayerTreeHostImplTest, DidDrawNotCalledOnHiddenLayer) {
auto* root = SetupRootLayer<DidDrawCheckLayer>(host_impl_->active_tree(),
gfx::Size(10, 10));
CreateClipNode(root);
auto* layer = AddLayer<DidDrawCheckLayer>(host_impl_->active_tree());
layer->SetBounds(gfx::Size(10, 10));
CopyProperties(root, layer);
layer->SetOffsetToTransformParent(gfx::Vector2dF(100, 100));
UpdateDrawProperties(host_impl_->active_tree());
EXPECT_FALSE(layer->will_draw_returned_true());
EXPECT_FALSE(layer->did_draw_called());
DrawFrame();
EXPECT_FALSE(layer->will_draw_returned_true());
EXPECT_FALSE(layer->did_draw_called());
EXPECT_TRUE(layer->visible_layer_rect().IsEmpty());
layer->SetOffsetToTransformParent(gfx::Vector2dF());
layer->NoteLayerPropertyChanged();
UpdateDrawProperties(host_impl_->active_tree());
EXPECT_FALSE(layer->will_draw_returned_true());
EXPECT_FALSE(layer->did_draw_called());
DrawFrame();
EXPECT_TRUE(layer->will_draw_returned_true());
EXPECT_TRUE(layer->did_draw_called());
EXPECT_FALSE(layer->visible_layer_rect().IsEmpty());
}
TEST_P(LayerTreeHostImplTest, WillDrawNotCalledOnOccludedLayer) {
gfx::Size big_size(1000, 1000);
auto* root =
SetupRootLayer<DidDrawCheckLayer>(host_impl_->active_tree(), big_size);
auto* occluded_layer = AddLayer<DidDrawCheckLayer>(host_impl_->active_tree());
CopyProperties(root, occluded_layer);
auto* top_layer = AddLayer<DidDrawCheckLayer>(host_impl_->active_tree());
top_layer->SetBounds(big_size);
top_layer->SetContentsOpaque(true);
CopyProperties(occluded_layer, top_layer);
UpdateDrawProperties(host_impl_->active_tree());
EXPECT_FALSE(occluded_layer->will_draw_returned_true());
EXPECT_FALSE(occluded_layer->did_draw_called());
EXPECT_FALSE(top_layer->will_draw_returned_true());
EXPECT_FALSE(top_layer->did_draw_called());
DrawFrame();
EXPECT_FALSE(occluded_layer->will_draw_returned_true());
EXPECT_FALSE(occluded_layer->did_draw_called());
EXPECT_TRUE(top_layer->will_draw_returned_true());
EXPECT_TRUE(top_layer->did_draw_called());
}
TEST_P(LayerTreeHostImplTest, DidDrawCalledOnAllLayers) {
auto* root = SetupRootLayer<DidDrawCheckLayer>(host_impl_->active_tree(),
gfx::Size(10, 10));
auto* layer1 = AddLayer<DidDrawCheckLayer>(host_impl_->active_tree());
auto* layer2 = AddLayer<DidDrawCheckLayer>(host_impl_->active_tree());
CopyProperties(root, layer1);
CreateTransformNode(layer1).flattens_inherited_transform = true;
CreateEffectNode(layer1).render_surface_reason = RenderSurfaceReason::kTest;
CopyProperties(layer1, layer2);
UpdateDrawProperties(host_impl_->active_tree());
EXPECT_FALSE(root->did_draw_called());
EXPECT_FALSE(layer1->did_draw_called());
EXPECT_FALSE(layer2->did_draw_called());
DrawFrame();
EXPECT_TRUE(root->did_draw_called());
EXPECT_TRUE(layer1->did_draw_called());
EXPECT_TRUE(layer2->did_draw_called());
EXPECT_NE(GetRenderSurface(root), GetRenderSurface(layer1));
EXPECT_TRUE(GetRenderSurface(layer1));
}
class MissingTextureAnimatingLayer : public DidDrawCheckLayer {
public:
static std::unique_ptr<MissingTextureAnimatingLayer> Create(
LayerTreeImpl* tree_impl,
int id,
bool tile_missing,
bool had_incomplete_tile,
bool animating,
scoped_refptr<AnimationTimeline> timeline) {
return base::WrapUnique(new MissingTextureAnimatingLayer(
tree_impl, id, tile_missing, had_incomplete_tile, animating, timeline));
}
void AppendQuads(const AppendQuadsContext& context,
viz::CompositorRenderPass* render_pass,
AppendQuadsData* append_quads_data) override {
LayerImpl::AppendQuads(context, render_pass, append_quads_data);
if (had_incomplete_tile_) {
append_quads_data->checkerboarded_needs_raster = true;
}
if (tile_missing_) {
append_quads_data->num_missing_tiles++;
append_quads_data->checkerboarded_needs_raster = true;
}
}
private:
MissingTextureAnimatingLayer(LayerTreeImpl* tree_impl,
int id,
bool tile_missing,
bool had_incomplete_tile,
bool animating,
scoped_refptr<AnimationTimeline> timeline)
: DidDrawCheckLayer(tree_impl, id),
tile_missing_(tile_missing),
had_incomplete_tile_(had_incomplete_tile) {
if (animating) {
this->SetElementId(LayerIdToElementIdForTesting(id));
AddAnimatedTransformToElementWithAnimation(this->element_id(), timeline,
10.0, 3, 0);
}
}
bool tile_missing_;
bool had_incomplete_tile_;
};
struct PrepareToDrawSuccessTestCase {
explicit PrepareToDrawSuccessTestCase(DrawResult result)
: expected_result(result) {}
struct State {
bool has_missing_tile = false;
bool has_incomplete_tile = false;
bool is_animating = false;
};
bool high_res_required = false;
bool has_view_transition_save_directive = false;
State layer_before;
State layer_between;
State layer_after;
DrawResult expected_result;
};
class LayerTreeHostImplPrepareToDrawTest : public LayerTreeHostImplTest {
public:
void CreateLayerFromState(DidDrawCheckLayer* root,
const scoped_refptr<AnimationTimeline>& timeline,
const PrepareToDrawSuccessTestCase::State& state) {
auto* layer = AddLayer<MissingTextureAnimatingLayer>(
root->layer_tree_impl(), state.has_missing_tile,
state.has_incomplete_tile, state.is_animating, timeline);
CopyProperties(root, layer);
if (state.is_animating)
CreateTransformNode(layer).has_potential_animation = true;
}
};
INSTANTIATE_COMMIT_TO_TREE_TEST_P(LayerTreeHostImplPrepareToDrawTest);
class DisabledForVizClientLayerTreeHostImplPrepareToDrawTest
: public LayerTreeHostImplPrepareToDrawTest {};
INSTANTIATE_COMPOSITOR_FRAME_PRODUCING_TREE_TEST_P(
DisabledForVizClientLayerTreeHostImplPrepareToDrawTest);
TEST_P(DisabledForVizClientLayerTreeHostImplPrepareToDrawTest,
PrepareToDrawSucceedsAndFails) {
CreateHostImpl(DefaultSettings(), CreateLayerTreeFrameSink());
std::vector<PrepareToDrawSuccessTestCase> cases;
cases.push_back(PrepareToDrawSuccessTestCase(DrawResult::kSuccess));
cases.push_back(PrepareToDrawSuccessTestCase(DrawResult::kSuccess));
cases.back().layer_before.is_animating = true;
cases.push_back(PrepareToDrawSuccessTestCase(DrawResult::kSuccess));
cases.back().layer_between.is_animating = true;
cases.push_back(PrepareToDrawSuccessTestCase(DrawResult::kSuccess));
cases.back().layer_after.is_animating = true;
cases.push_back(PrepareToDrawSuccessTestCase(DrawResult::kSuccess));
cases.back().layer_before.has_missing_tile = true;
cases.push_back(PrepareToDrawSuccessTestCase(DrawResult::kSuccess));
cases.back().layer_between.has_missing_tile = true;
cases.push_back(PrepareToDrawSuccessTestCase(DrawResult::kSuccess));
cases.back().layer_after.has_missing_tile = true;
cases.push_back(PrepareToDrawSuccessTestCase(DrawResult::kSuccess));
cases.back().layer_before.has_incomplete_tile = true;
cases.push_back(PrepareToDrawSuccessTestCase(DrawResult::kSuccess));
cases.back().layer_between.has_incomplete_tile = true;
cases.push_back(PrepareToDrawSuccessTestCase(DrawResult::kSuccess));
cases.back().layer_after.has_incomplete_tile = true;
cases.push_back(PrepareToDrawSuccessTestCase(
CommitsToActiveTree() ? DrawResult::kSuccess
: DrawResult::kAbortedCheckerboardAnimations));
cases.back().layer_between.has_missing_tile = true;
cases.back().layer_between.is_animating = true;
cases.push_back(PrepareToDrawSuccessTestCase(DrawResult::kSuccess));
cases.back().layer_between.has_incomplete_tile = true;
cases.back().layer_between.is_animating = true;
cases.push_back(PrepareToDrawSuccessTestCase(DrawResult::kSuccess));
cases.back().high_res_required = true;
cases.push_back(
PrepareToDrawSuccessTestCase(DrawResult::kAbortedMissingHighResContent));
cases.back().high_res_required = true;
cases.back().layer_between.has_incomplete_tile = true;
cases.push_back(
PrepareToDrawSuccessTestCase(DrawResult::kAbortedMissingHighResContent));
cases.back().high_res_required = true;
cases.back().layer_between.has_missing_tile = true;
cases.push_back(
PrepareToDrawSuccessTestCase(DrawResult::kAbortedMissingHighResContent));
cases.back().high_res_required = true;
cases.back().layer_between.has_missing_tile = true;
cases.back().layer_after.has_missing_tile = true;
cases.back().layer_after.is_animating = true;
cases.push_back(
PrepareToDrawSuccessTestCase(DrawResult::kAbortedMissingHighResContent));
cases.back().high_res_required = true;
cases.back().layer_between.has_missing_tile = true;
cases.back().layer_before.has_missing_tile = true;
cases.back().layer_before.is_animating = true;
cases.push_back(PrepareToDrawSuccessTestCase(DrawResult::kSuccess));
cases.back().has_view_transition_save_directive = true;
cases.back().layer_between.has_missing_tile = true;
cases.back().layer_between.is_animating = true;
auto* root = SetupRootLayer<DidDrawCheckLayer>(host_impl_->active_tree(),
gfx::Size(10, 10));
UpdateDrawProperties(host_impl_->active_tree());
DrawFrame();
for (size_t i = 0; i < cases.size(); ++i) {
const auto& testcase = cases[i];
host_impl_->active_tree()->DetachLayersKeepingRootLayerForTesting();
timeline()->ClearAnimations();
std::ostringstream scope;
scope << "Test case: " << i;
SCOPED_TRACE(scope.str());
CreateLayerFromState(root, timeline(), testcase.layer_before);
CreateLayerFromState(root, timeline(), testcase.layer_between);
CreateLayerFromState(root, timeline(), testcase.layer_after);
UpdateDrawProperties(host_impl_->active_tree());
if (testcase.has_view_transition_save_directive) {
host_impl_->active_tree()->AddViewTransitionRequest(
ViewTransitionRequest::CreateCapture(
blink::ViewTransitionToken(), false, {},
base::DoNothingAs<void(
const viz::ViewTransitionElementResourceRects&)>()));
}
if (testcase.high_res_required)
host_impl_->SetRequiresHighResToDraw();
TestFrameData frame;
auto args = viz::CreateBeginFrameArgsForTesting(
BEGINFRAME_FROM_HERE, viz::BeginFrameArgs::kManualSourceId, 1,
base::TimeTicks() + base::Milliseconds(1));
host_impl_->WillBeginImplFrame(args);
EXPECT_EQ(testcase.expected_result, host_impl_->PrepareToDraw(&frame));
host_impl_->DrawLayers(&frame);
host_impl_->DidDrawAllLayers(frame);
host_impl_->DidFinishImplFrame(args);
}
}
TEST_P(LayerTreeHostImplPrepareToDrawTest,
PrepareToDrawWhenDrawAndSwapFullViewportEveryFrame) {
CreateHostImpl(DefaultSettings(), FakeLayerTreeFrameSink::CreateSoftware());
const gfx::Transform external_transform;
const gfx::Rect external_viewport;
const bool resourceless_software_draw = true;
host_impl_->SetExternalTilePriorityConstraints(external_viewport,
external_transform);
std::vector<PrepareToDrawSuccessTestCase> cases;
cases.push_back(PrepareToDrawSuccessTestCase(DrawResult::kSuccess));
cases.push_back(PrepareToDrawSuccessTestCase(DrawResult::kSuccess));
cases.back().layer_between.has_missing_tile = true;
cases.back().layer_between.is_animating = true;
cases.push_back(PrepareToDrawSuccessTestCase(DrawResult::kSuccess));
cases.back().high_res_required = true;
cases.back().layer_between.has_incomplete_tile = true;
cases.push_back(PrepareToDrawSuccessTestCase(DrawResult::kSuccess));
cases.back().high_res_required = true;
cases.back().layer_between.has_missing_tile = true;
auto* root = SetupRootLayer<DidDrawCheckLayer>(host_impl_->active_tree(),
gfx::Size(10, 10));
UpdateDrawProperties(host_impl_->active_tree());
host_impl_->OnDraw(external_transform, external_viewport,
resourceless_software_draw, false);
for (size_t i = 0; i < cases.size(); ++i) {
const auto& testcase = cases[i];
host_impl_->active_tree()->DetachLayersKeepingRootLayerForTesting();
std::ostringstream scope;
scope << "Test case: " << i;
SCOPED_TRACE(scope.str());
CreateLayerFromState(root, timeline(), testcase.layer_before);
CreateLayerFromState(root, timeline(), testcase.layer_between);
CreateLayerFromState(root, timeline(), testcase.layer_after);
if (testcase.high_res_required)
host_impl_->SetRequiresHighResToDraw();
host_impl_->OnDraw(external_transform, external_viewport,
resourceless_software_draw, false);
}
}
TEST_P(LayerTreeHostImplTest, ScrollRootIgnored) {
SetupDefaultRootLayer(gfx::Size(10, 10));
DrawFrame();
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
EXPECT_EQ(ScrollThread::kScrollIgnored, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
EXPECT_FALSE(did_request_redraw_);
EXPECT_FALSE(did_request_commit_);
}
TEST_F(CommitToPendingTreeLayerTreeHostImplTest, ClampingAfterActivation) {
CreatePendingTree();
host_impl_->pending_tree()->PushPageScaleFromMainThread(1, 1, 1);
SetupViewportLayers(host_impl_->pending_tree(), gfx::Size(50, 50),
gfx::Size(100, 100), gfx::Size(100, 100));
host_impl_->ActivateSyncTree();
CreatePendingTree();
const gfx::PointF pending_scroll(-100, -100);
LayerImpl* active_outer_layer = OuterViewportScrollLayer();
LayerImpl* pending_outer_layer =
host_impl_->pending_tree()->OuterViewportScrollLayerForTesting();
pending_outer_layer->layer_tree_impl()
->property_trees()
->scroll_tree_mutable()
.UpdateScrollOffsetBaseForTesting(pending_outer_layer->element_id(),
pending_scroll);
host_impl_->ActivateSyncTree();
EXPECT_EQ(CurrentScrollOffset(active_outer_layer), gfx::PointF(0, 0));
}
class LayerTreeHostImplBrowserControlsTest : public LayerTreeHostImplTest {
public:
LayerTreeHostImplBrowserControlsTest()
: layer_size_(10, 10), clip_size_(layer_size_), top_controls_height_(50) {
viewport_size_ = gfx::Size(clip_size_.width(),
clip_size_.height() + top_controls_height_);
}
bool CreateHostImpl(
const LayerTreeSettings& settings,
std::unique_ptr<LayerTreeFrameSink> layer_tree_frame_sink) override {
bool init = LayerTreeHostImplTest::CreateHostImpl(
settings, std::move(layer_tree_frame_sink));
if (init) {
host_impl_->active_tree()->SetBrowserControlsParams(
{top_controls_height_, 0, 0, 0, false, false});
host_impl_->active_tree()->SetCurrentBrowserControlsShownRatio(1.f, 1.f);
host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, 1.f, 1.f);
}
return init;
}
protected:
void SetupBrowserControlsAndScrollLayerWithVirtualViewport(
const gfx::Size& inner_viewport_size,
const gfx::Size& outer_viewport_size,
const gfx::Size& scroll_layer_size) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
host_impl_->active_tree(), inner_viewport_size, outer_viewport_size,
scroll_layer_size);
}
void SetupBrowserControlsAndScrollLayerWithVirtualViewport(
LayerTreeImpl* tree_impl,
const gfx::Size& inner_viewport_size,
const gfx::Size& outer_viewport_size,
const gfx::Size& scroll_layer_size) {
tree_impl->SetBrowserControlsParams(
{top_controls_height_, 0, 0, 0, false, true});
tree_impl->SetCurrentBrowserControlsShownRatio(1.f, 1.f);
tree_impl->PushPageScaleFromMainThread(1.f, 1.f, 1.f);
host_impl_->DidChangeBrowserControlsPosition();
SetupViewportLayers(tree_impl, inner_viewport_size, outer_viewport_size,
scroll_layer_size);
}
gfx::Size layer_size_;
gfx::Size clip_size_;
gfx::Size viewport_size_;
float top_controls_height_;
};
INSTANTIATE_CLIENT_MODE_TREE_TEST_P(LayerTreeHostImplBrowserControlsTest);
#define EXPECT_VIEWPORT_GEOMETRIES(expected_browser_controls_shown_ratio) \
do { \
auto* tree = host_impl_->active_tree(); \
auto* property_trees = tree->property_trees(); \
EXPECT_EQ(expected_browser_controls_shown_ratio, \
tree->CurrentTopControlsShownRatio()); \
EXPECT_EQ( \
tree->top_controls_height() * expected_browser_controls_shown_ratio, \
host_impl_->browser_controls_manager()->ContentTopOffset()); \
int delta = \
(tree->top_controls_height() + tree->bottom_controls_height()) * \
(1 - expected_browser_controls_shown_ratio); \
int scaled_delta = delta / tree->min_page_scale_factor(); \
gfx::Size inner_scroll_bounds = tree->InnerViewportScrollNode()->bounds; \
inner_scroll_bounds.Enlarge(0, scaled_delta); \
EXPECT_EQ(inner_scroll_bounds, InnerViewportScrollLayer()->bounds()); \
EXPECT_EQ(gfx::RectF(gfx::SizeF(inner_scroll_bounds)), \
tree->OuterViewportClipNode()->clip); \
EXPECT_EQ(gfx::Vector2dF(0, delta), \
property_trees->inner_viewport_container_bounds_delta()); \
EXPECT_EQ(gfx::Vector2dF(0, scaled_delta), \
property_trees->outer_viewport_container_bounds_delta()); \
} while (false)
TEST_P(LayerTreeHostImplBrowserControlsTest,
HidingBrowserControlsExpandsScrollableSize) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
gfx::Size(50, 50), gfx::Size(50, 50), gfx::Size(50, 50));
LayerTreeImpl* active_tree = host_impl_->active_tree();
DrawFrame();
EXPECT_VIEWPORT_GEOMETRIES(1);
EXPECT_EQ(gfx::SizeF(50, 50), active_tree->ScrollableSize());
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 25),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
host_impl_->browser_controls_manager()->ScrollBegin();
host_impl_->browser_controls_manager()->ScrollBy(gfx::Vector2dF(0, 25));
EXPECT_VIEWPORT_GEOMETRIES(0.5f);
EXPECT_EQ(gfx::SizeF(50, 75), active_tree->ScrollableSize());
host_impl_->browser_controls_manager()->ScrollBy(gfx::Vector2dF(0, 25));
EXPECT_VIEWPORT_GEOMETRIES(0);
EXPECT_EQ(gfx::SizeF(50, 100), active_tree->ScrollableSize());
host_impl_->browser_controls_manager()->ScrollBy(gfx::Vector2dF(0, 25));
EXPECT_VIEWPORT_GEOMETRIES(0);
EXPECT_EQ(gfx::SizeF(50, 100), active_tree->ScrollableSize());
host_impl_->browser_controls_manager()->ScrollEnd();
GetInputHandler().ScrollEnd();
}
TEST_P(LayerTreeHostImplBrowserControlsTest,
HidingBrowserControlsExpandsClipAncestorsOfReplacedOuterScroller) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
gfx::Size(180, 180), gfx::Size(180, 180), gfx::Size(180, 180));
LayerTreeImpl* active_tree = host_impl_->active_tree();
PropertyTrees* property_trees = active_tree->property_trees();
LayerImpl* original_outer_scroll = OuterViewportScrollLayer();
LayerImpl* parent_clip_layer = AddLayerInActiveTree();
CopyProperties(original_outer_scroll, parent_clip_layer);
parent_clip_layer->SetBounds(gfx::Size(160, 160));
CreateClipNode(parent_clip_layer);
LayerImpl* clip_layer = AddLayerInActiveTree();
clip_layer->SetBounds(gfx::Size(150, 150));
CopyProperties(parent_clip_layer, clip_layer);
CreateClipNode(clip_layer);
LayerImpl* scroll_layer =
AddScrollableLayer(clip_layer, gfx::Size(150, 150), gfx::Size(300, 300));
GetScrollNode(scroll_layer)->scrolls_outer_viewport = true;
ClipNode* original_outer_clip = GetClipNode(original_outer_scroll);
ClipNode* parent_clip = GetClipNode(parent_clip_layer);
ClipNode* scroll_clip = GetClipNode(clip_layer);
auto viewport_property_ids = active_tree->ViewportPropertyIdsForTesting();
viewport_property_ids.outer_clip = clip_layer->clip_tree_index();
viewport_property_ids.outer_scroll = scroll_layer->scroll_tree_index();
active_tree->SetViewportPropertyIds(viewport_property_ids);
UpdateDrawProperties(active_tree);
EXPECT_EQ(scroll_layer, OuterViewportScrollLayer());
EXPECT_EQ(GetScrollNode(scroll_layer),
active_tree->OuterViewportScrollNode());
EXPECT_EQ(1, active_tree->CurrentTopControlsShownRatio());
EXPECT_EQ(50, host_impl_->browser_controls_manager()->ContentTopOffset());
EXPECT_EQ(gfx::Vector2dF(),
property_trees->inner_viewport_container_bounds_delta());
EXPECT_EQ(gfx::Vector2dF(),
property_trees->outer_viewport_container_bounds_delta());
EXPECT_EQ(gfx::SizeF(300, 300), active_tree->ScrollableSize());
EXPECT_EQ(gfx::RectF(0, 0, 180, 180), original_outer_clip->clip);
EXPECT_EQ(gfx::RectF(0, 0, 160, 160), parent_clip->clip);
EXPECT_EQ(gfx::RectF(0, 0, 150, 150), scroll_clip->clip);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 25),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
host_impl_->browser_controls_manager()->ScrollBy(gfx::Vector2dF(0, 25));
EXPECT_EQ(0.5f, active_tree->CurrentTopControlsShownRatio());
EXPECT_EQ(25, host_impl_->browser_controls_manager()->ContentTopOffset());
EXPECT_EQ(gfx::Vector2dF(0, 25),
property_trees->inner_viewport_container_bounds_delta());
EXPECT_EQ(gfx::Vector2dF(0, 25),
property_trees->outer_viewport_container_bounds_delta());
EXPECT_EQ(gfx::SizeF(300, 300), active_tree->ScrollableSize());
EXPECT_EQ(gfx::RectF(0, 0, 150, 175), scroll_clip->clip);
EXPECT_EQ(gfx::RectF(0, 0, 160, 175), parent_clip->clip);
EXPECT_EQ(gfx::RectF(0, 0, 180, 180), original_outer_clip->clip);
host_impl_->browser_controls_manager()->ScrollBy(gfx::Vector2dF(0, 25));
EXPECT_EQ(0, active_tree->CurrentTopControlsShownRatio());
EXPECT_EQ(0, host_impl_->browser_controls_manager()->ContentTopOffset());
EXPECT_EQ(gfx::Vector2dF(0, 50),
property_trees->inner_viewport_container_bounds_delta());
EXPECT_EQ(gfx::Vector2dF(0, 50),
property_trees->outer_viewport_container_bounds_delta());
EXPECT_EQ(gfx::SizeF(300, 300), active_tree->ScrollableSize());
EXPECT_EQ(gfx::RectF(0, 0, 150, 200), scroll_clip->clip);
EXPECT_EQ(gfx::RectF(0, 0, 160, 200), parent_clip->clip);
EXPECT_EQ(gfx::RectF(0, 0, 180, 200), original_outer_clip->clip);
host_impl_->browser_controls_manager()->ScrollBy(gfx::Vector2dF(0, 25));
EXPECT_EQ(0, active_tree->CurrentTopControlsShownRatio());
EXPECT_EQ(0, host_impl_->browser_controls_manager()->ContentTopOffset());
EXPECT_EQ(gfx::Vector2dF(0, 50),
property_trees->inner_viewport_container_bounds_delta());
EXPECT_EQ(gfx::Vector2dF(0, 50),
property_trees->outer_viewport_container_bounds_delta());
EXPECT_EQ(gfx::SizeF(300, 300), active_tree->ScrollableSize());
EXPECT_EQ(gfx::RectF(0, 0, 150, 200), scroll_clip->clip);
EXPECT_EQ(gfx::RectF(0, 0, 160, 200), parent_clip->clip);
EXPECT_EQ(gfx::RectF(0, 0, 180, 200), original_outer_clip->clip);
host_impl_->browser_controls_manager()->ScrollEnd();
GetInputHandler().ScrollEnd();
}
TEST_P(LayerTreeHostImplBrowserControlsTest,
HidingBrowserControlsAdjustsSnapFling) {
gfx::Size view_size(100, 100);
gfx::Size content_size(100, 1000);
gfx::RectF snap_area_1(0, 0, 100, 700);
SetupBrowserControlsAndScrollLayerWithVirtualViewport(view_size, view_size,
content_size);
SnapContainerData container(
ScrollSnapType(false, SnapAxis::kY, SnapStrictness::kMandatory),
gfx::RectF(0, 0, 100, 100), gfx::PointF(0, 900));
ScrollSnapAlign start = ScrollSnapAlign(SnapAlignment::kStart);
container.AddSnapAreaData(
SnapAreaData(start, snap_area_1, false, false, ElementId(10)));
host_impl_->OuterViewportScrollNode()->snap_container_data.emplace(container);
DrawFrame();
EXPECT_VIEWPORT_GEOMETRIES(1.0f);
LayerTreeImpl* active_tree = host_impl_->active_tree();
PropertyTrees* property_trees = active_tree->property_trees();
LayerImpl* outer_scroll_layer = OuterViewportScrollLayer();
InputHandler& handler = GetInputHandler();
gfx::PointF initial_offset, target_offset;
gfx::Point position(50, 50);
ui::ScrollInputType type = ui::ScrollInputType::kTouchscreen;
handler.ScrollBegin(&*BeginState(position, gfx::Vector2dF(0, 15), type),
type);
handler.ScrollUpdate(UpdateState(position, gfx::Vector2dF(0, 15), type));
EXPECT_FLOAT_EQ(0.7, active_tree->CurrentTopControlsShownRatio());
EXPECT_EQ(gfx::Vector2dF(0, 15),
property_trees->outer_viewport_container_bounds_delta());
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(outer_scroll_layer));
EXPECT_FALSE(handler.GetSnapFlingInfoAndSetAnimatingSnapTarget(
gfx::Vector2dF(0, 30), gfx::Vector2dF(0, 600), &initial_offset,
&target_offset));
handler.ScrollUpdate(UpdateState(position, gfx::Vector2dF(0, 435), type));
EXPECT_POINTF_EQ(gfx::PointF(0, 400),
CurrentScrollOffset(outer_scroll_layer));
EXPECT_TRUE(handler.GetSnapFlingInfoAndSetAnimatingSnapTarget(
gfx::Vector2dF(0, 300), gfx::Vector2dF(0, 1000), &initial_offset,
&target_offset));
EXPECT_TRUE(handler.animating_for_snap_for_testing(
host_impl_->OuterViewportScrollNode()->element_id));
EXPECT_POINTF_EQ(gfx::PointF(0, 400), initial_offset);
EXPECT_POINTF_EQ(gfx::PointF(0, 550), target_offset);
handler.ScrollEnd();
}
TEST_P(LayerTreeHostImplBrowserControlsTest,
MovingBrowserControlsOuterClipDeltaScaled) {
gfx::Size inner_size = gfx::Size(100, 100);
gfx::Size outer_size = gfx::Size(100, 100);
gfx::Size content_size = gfx::Size(200, 1000);
SetupBrowserControlsAndScrollLayerWithVirtualViewport(inner_size, outer_size,
content_size);
LayerTreeImpl* active_tree = host_impl_->active_tree();
LayerImpl* content = AddLayerInActiveTree();
content->SetBounds(gfx::Size(100, 100));
CopyProperties(OuterViewportScrollLayer(), content);
active_tree->PushPageScaleFromMainThread(0.5f, 0.5f, 4);
DrawFrame();
EXPECT_VIEWPORT_GEOMETRIES(1.0f);
EXPECT_EQ(gfx::SizeF(200, 1000), active_tree->ScrollableSize());
ASSERT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 25),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(0, 0), gfx::Vector2dF(0, 25),
ui::ScrollInputType::kTouchscreen));
EXPECT_VIEWPORT_GEOMETRIES(0.5f);
GetInputHandler().ScrollEnd();
}
TEST_P(LayerTreeHostImplBrowserControlsTest,
HidingBrowserControlsAdjustsScrollbarPosition) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
gfx::Size(50, 50), gfx::Size(50, 50), gfx::Size(50, 50));
LayerTreeImpl* active_tree = host_impl_->active_tree();
gfx::Size scrollbar_size(gfx::Size(50, 15));
auto* scrollbar_layer = AddLayer<SolidColorScrollbarLayerImpl>(
host_impl_->active_tree(), ScrollbarOrientation::kHorizontal, 3, 20,
false);
SetupScrollbarLayer(OuterViewportScrollLayer(), scrollbar_layer);
scrollbar_layer->SetBounds(scrollbar_size);
TouchActionRegion touch_action_region;
touch_action_region.Union(TouchAction::kNone, gfx::Rect(scrollbar_size));
scrollbar_layer->SetTouchActionRegion(touch_action_region);
scrollbar_layer->SetOffsetToTransformParent(gfx::Vector2dF(0, 35));
host_impl_->active_tree()->UpdateAllScrollbarGeometriesForTesting();
UpdateDrawProperties(host_impl_->active_tree());
DrawFrame();
EXPECT_VIEWPORT_GEOMETRIES(1.0f);
EXPECT_EQ(gfx::SizeF(50, 50), active_tree->ScrollableSize());
EXPECT_EQ(gfx::Size(50, 15), scrollbar_layer->bounds());
EXPECT_EQ(gfx::Rect(20, 0, 10, 3), scrollbar_layer->ComputeThumbQuadRect());
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 25),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
host_impl_->browser_controls_manager()->ScrollBegin();
{
host_impl_->browser_controls_manager()->ScrollBy(gfx::Vector2dF(0, 25));
EXPECT_VIEWPORT_GEOMETRIES(0.5f);
EXPECT_EQ(gfx::SizeF(50, 75), active_tree->ScrollableSize());
EXPECT_EQ(gfx::Size(50, 15), scrollbar_layer->bounds());
EXPECT_EQ(gfx::Rect(20, 25, 10, 3),
scrollbar_layer->ComputeThumbQuadRect());
}
{
host_impl_->browser_controls_manager()->ScrollBy(gfx::Vector2dF(0, 25));
EXPECT_VIEWPORT_GEOMETRIES(0);
EXPECT_EQ(gfx::SizeF(50, 100), active_tree->ScrollableSize());
EXPECT_EQ(gfx::Size(50, 15), scrollbar_layer->bounds());
EXPECT_EQ(gfx::Rect(20, 50, 10, 3),
scrollbar_layer->ComputeThumbQuadRect());
}
{
host_impl_->browser_controls_manager()->ScrollBy(gfx::Vector2dF(0, 25));
EXPECT_VIEWPORT_GEOMETRIES(0);
EXPECT_EQ(gfx::SizeF(50, 100), active_tree->ScrollableSize());
EXPECT_EQ(gfx::Size(50, 15), scrollbar_layer->bounds());
EXPECT_EQ(gfx::Rect(20, 50, 10, 3),
scrollbar_layer->ComputeThumbQuadRect());
}
host_impl_->browser_controls_manager()->ScrollEnd();
GetInputHandler().ScrollEnd();
}
TEST_P(LayerTreeHostImplBrowserControlsTest,
ScrollBrowserControlsByFractionalAmount) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
gfx::Size(10, 10), gfx::Size(10, 10), gfx::Size(10, 10));
DrawFrame();
gfx::Vector2dF top_controls_scroll_delta(0, 5.25f);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), top_controls_scroll_delta,
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
host_impl_->browser_controls_manager()->ScrollBegin();
host_impl_->browser_controls_manager()->ScrollBy(top_controls_scroll_delta);
host_impl_->browser_controls_manager()->ScrollEnd();
GetInputHandler().ScrollEnd();
auto* property_trees = host_impl_->active_tree()->property_trees();
EXPECT_FLOAT_EQ(top_controls_scroll_delta.y(),
property_trees->inner_viewport_container_bounds_delta().y());
}
TEST_P(LayerTreeHostImplBrowserControlsTest,
BrowserControlsOuterViewportBecomesScrollable) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
gfx::Size(10, 50), gfx::Size(10, 50), gfx::Size(10, 100));
DrawFrame();
LayerImpl* inner_scroll = InnerViewportScrollLayer();
inner_scroll->SetDrawsContent(true);
LayerImpl* outer_scroll = OuterViewportScrollLayer();
outer_scroll->SetDrawsContent(true);
outer_scroll->SetDrawsContent(true);
host_impl_->active_tree()->PushPageScaleFromMainThread(2, 1, 2);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 50),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2dF(0, 50), ui::ScrollInputType::kTouchscreen));
EXPECT_EQ(0, host_impl_->active_tree()->CurrentTopControlsShownRatio());
EXPECT_EQ(0, CurrentScrollOffset(inner_scroll).y());
EXPECT_EQ(100, inner_scroll->bounds().height());
EXPECT_EQ(100, outer_scroll->bounds().height());
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2dF(0, 100), ui::ScrollInputType::kTouchscreen));
EXPECT_EQ(50, CurrentScrollOffset(inner_scroll).y());
EXPECT_EQ(0, CurrentScrollOffset(outer_scroll).y());
EXPECT_EQ(gfx::PointF(), MaxScrollOffset(outer_scroll));
GetInputHandler().ScrollEnd();
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, -50),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id,
outer_scroll->scroll_tree_index());
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2dF(0, -50), ui::ScrollInputType::kTouchscreen));
EXPECT_EQ(1, host_impl_->active_tree()->CurrentTopControlsShownRatio());
EXPECT_EQ(50, inner_scroll->bounds().height());
EXPECT_EQ(100, outer_scroll->bounds().height());
EXPECT_EQ(25, CurrentScrollOffset(outer_scroll).y());
EXPECT_EQ(25, CurrentScrollOffset(inner_scroll).y());
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2dF(0, -20), ui::ScrollInputType::kTouchscreen));
EXPECT_EQ(25, CurrentScrollOffset(outer_scroll).y());
EXPECT_EQ(15, CurrentScrollOffset(inner_scroll).y());
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2dF(0, -30), ui::ScrollInputType::kTouchscreen));
EXPECT_EQ(25, CurrentScrollOffset(outer_scroll).y());
EXPECT_EQ(0, CurrentScrollOffset(inner_scroll).y());
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(), gfx::Vector2dF(0.f, -50),
ui::ScrollInputType::kTouchscreen));
GetInputHandler().ScrollEnd();
EXPECT_EQ(0, CurrentScrollOffset(outer_scroll).y());
EXPECT_EQ(0, CurrentScrollOffset(inner_scroll).y());
}
TEST_P(LayerTreeHostImplBrowserControlsTest, FixedContainerDelta) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
gfx::Size(100, 100), gfx::Size(100, 100), gfx::Size(100, 100));
DrawFrame();
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 1, 2);
float page_scale = 1.5f;
host_impl_->active_tree()->PushPageScaleFromMainThread(page_scale, 1, 2);
gfx::Vector2dF top_controls_scroll_delta(0, 20);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), top_controls_scroll_delta,
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
host_impl_->browser_controls_manager()->ScrollBegin();
host_impl_->browser_controls_manager()->ScrollBy(top_controls_scroll_delta);
EXPECT_FLOAT_EQ(top_controls_height_ - top_controls_scroll_delta.y(),
host_impl_->browser_controls_manager()->ContentTopOffset());
auto* property_trees = host_impl_->active_tree()->property_trees();
EXPECT_FLOAT_EQ(top_controls_scroll_delta.y(),
property_trees->outer_viewport_container_bounds_delta().y());
GetInputHandler().ScrollEnd();
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), top_controls_scroll_delta,
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
host_impl_->browser_controls_manager()->ScrollBegin();
host_impl_->browser_controls_manager()->ScrollBy(top_controls_scroll_delta);
host_impl_->browser_controls_manager()->ScrollBy(top_controls_scroll_delta);
host_impl_->browser_controls_manager()->ScrollBy(top_controls_scroll_delta);
EXPECT_EQ(0, host_impl_->browser_controls_manager()->ContentTopOffset());
EXPECT_VECTOR2DF_EQ(gfx::Vector2dF(0, top_controls_height_),
property_trees->outer_viewport_container_bounds_delta());
GetInputHandler().ScrollEnd();
EXPECT_EQ(
ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), -top_controls_scroll_delta,
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
host_impl_->browser_controls_manager()->ScrollBegin();
host_impl_->browser_controls_manager()->ScrollBy(-top_controls_scroll_delta);
EXPECT_EQ(top_controls_scroll_delta.y(),
host_impl_->browser_controls_manager()->ContentTopOffset());
EXPECT_VECTOR2DF_EQ(
gfx::Vector2dF(0, top_controls_height_ - top_controls_scroll_delta.y()),
property_trees->outer_viewport_container_bounds_delta());
host_impl_->browser_controls_manager()->ScrollEnd();
GetInputHandler().ScrollEnd();
}
TEST_P(LayerTreeHostImplBrowserControlsTest, BrowserControlsPushUnsentRatio) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
gfx::Size(10, 50), gfx::Size(10, 50), gfx::Size(10, 100));
DrawFrame();
LayerImpl* inner_scroll = InnerViewportScrollLayer();
inner_scroll->SetDrawsContent(true);
LayerImpl* outer_scroll = OuterViewportScrollLayer();
outer_scroll->SetDrawsContent(true);
host_impl_->active_tree()->PushBrowserControlsFromMainThread(1, 1);
ASSERT_EQ(1.0f, host_impl_->active_tree()->CurrentTopControlsShownRatio());
host_impl_->active_tree()->SetCurrentBrowserControlsShownRatio(0.5f, 0.5f);
ASSERT_EQ(0.5f, host_impl_->active_tree()->CurrentTopControlsShownRatio());
host_impl_->active_tree()->PushBrowserControlsFromMainThread(0, 0);
ASSERT_EQ(0, host_impl_->active_tree()->CurrentTopControlsShownRatio());
}
TEST_P(LayerTreeHostImplBrowserControlsTest,
BrowserControlsScrollableSublayer) {
gfx::Size sub_content_size(100, 400);
gfx::Size sub_content_layer_size(100, 300);
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
gfx::Size(100, 50), gfx::Size(100, 100), gfx::Size(100, 100));
DrawFrame();
EXPECT_EQ(1, host_impl_->active_tree()->CurrentTopControlsShownRatio());
LayerImpl* outer_viewport_scroll_layer = OuterViewportScrollLayer();
LayerImpl* child = AddLayerInActiveTree();
child->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
child->SetElementId(LayerIdToElementIdForTesting(child->id()));
child->SetBounds(sub_content_size);
child->SetDrawsContent(true);
CopyProperties(outer_viewport_scroll_layer, child);
CreateTransformNode(child);
CreateScrollNode(child, sub_content_layer_size);
UpdateDrawProperties(host_impl_->active_tree());
SetScrollOffsetDelta(child, gfx::Vector2dF(0, 100));
gfx::Vector2dF scroll_delta(0, 25);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), scroll_delta,
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), scroll_delta, ui::ScrollInputType::kTouchscreen));
GetInputHandler().ScrollEnd();
EXPECT_EQ(scroll_delta.y(),
top_controls_height_ -
host_impl_->browser_controls_manager()->ContentTopOffset());
}
TEST_P(LayerTreeHostImplBrowserControlsTest,
PositionBrowserControlsToActiveTreeExplicitly) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
layer_size_, layer_size_, layer_size_);
DrawFrame();
host_impl_->active_tree()->SetCurrentBrowserControlsShownRatio(0, 0);
host_impl_->active_tree()->top_controls_shown_ratio()->PushMainToPending(
30 / top_controls_height_);
host_impl_->active_tree()->top_controls_shown_ratio()->PushPendingToActive();
EXPECT_FLOAT_EQ(30,
host_impl_->browser_controls_manager()->ContentTopOffset());
EXPECT_FLOAT_EQ(-20,
host_impl_->browser_controls_manager()->ControlsTopOffset());
host_impl_->active_tree()->SetCurrentBrowserControlsShownRatio(0, 0);
EXPECT_FLOAT_EQ(0,
host_impl_->browser_controls_manager()->ContentTopOffset());
EXPECT_FLOAT_EQ(-50,
host_impl_->browser_controls_manager()->ControlsTopOffset());
host_impl_->DidChangeBrowserControlsPosition();
auto* property_trees = host_impl_->active_tree()->property_trees();
EXPECT_EQ(gfx::Vector2dF(0, 50),
property_trees->inner_viewport_container_bounds_delta());
}
TEST_P(LayerTreeHostImplBrowserControlsTest,
PositionBrowserControlsToPendingTreeExplicitly) {
EnsureSyncTree();
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
host_impl_->sync_tree(), layer_size_, layer_size_, layer_size_);
host_impl_->SetCurrentBrowserControlsShownRatio(0, 0);
EXPECT_FLOAT_EQ(top_controls_height_,
host_impl_->sync_tree()
->property_trees()
->inner_viewport_container_bounds_delta()
.y());
host_impl_->SetCurrentBrowserControlsShownRatio(0.5f, 0.5f);
EXPECT_FLOAT_EQ(0.5f * top_controls_height_,
host_impl_->sync_tree()
->property_trees()
->inner_viewport_container_bounds_delta()
.y());
host_impl_->SetCurrentBrowserControlsShownRatio(1, 1);
EXPECT_FLOAT_EQ(0, host_impl_->sync_tree()
->property_trees()
->inner_viewport_container_bounds_delta()
.y());
host_impl_->sync_tree()->PushBrowserControlsFromMainThread(-0.5f, -0.5f);
EXPECT_FLOAT_EQ(0.5f * top_controls_height_,
host_impl_->sync_tree()
->property_trees()
->inner_viewport_container_bounds_delta()
.y());
host_impl_->sync_tree()->PushBrowserControlsFromMainThread(-1, -1);
EXPECT_FLOAT_EQ(top_controls_height_,
host_impl_->sync_tree()
->property_trees()
->inner_viewport_container_bounds_delta()
.y());
}
TEST_P(LayerTreeHostImplBrowserControlsTest, ApplyDeltaOnTreeActivation) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
layer_size_, layer_size_, layer_size_);
DrawFrame();
host_impl_->active_tree()->top_controls_shown_ratio()->PushMainToPending(
20 / top_controls_height_);
host_impl_->active_tree()->top_controls_shown_ratio()->PushPendingToActive();
host_impl_->active_tree()->SetCurrentBrowserControlsShownRatio(
15 / top_controls_height_, 15 / top_controls_height_);
host_impl_->active_tree()->top_controls_shown_ratio()->PullDeltaForMainThread(
false);
host_impl_->active_tree()->SetCurrentBrowserControlsShownRatio(0, 0);
EnsureSyncTree();
host_impl_->sync_tree()->PushBrowserControlsFromMainThread(
15 / top_controls_height_, 15 / top_controls_height_);
host_impl_->DidChangeBrowserControlsPosition();
auto* property_trees = host_impl_->active_tree()->property_trees();
EXPECT_EQ(gfx::Vector2dF(0, 50),
property_trees->inner_viewport_container_bounds_delta());
EXPECT_EQ(0, host_impl_->browser_controls_manager()->ContentTopOffset());
host_impl_->ActivateSyncTree();
EXPECT_EQ(0, host_impl_->browser_controls_manager()->ContentTopOffset());
EXPECT_EQ(CommitsToActiveTree() ? gfx::Vector2dF(0, 50) : gfx::Vector2dF(),
property_trees->inner_viewport_container_bounds_delta());
EXPECT_FLOAT_EQ(
-15, host_impl_->active_tree()->top_controls_shown_ratio()->Delta() *
top_controls_height_);
EXPECT_FLOAT_EQ(
15, host_impl_->active_tree()->top_controls_shown_ratio()->ActiveBase() *
top_controls_height_);
}
TEST_P(LayerTreeHostImplBrowserControlsTest,
BrowserControlsLayoutHeightChanged) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
layer_size_, layer_size_, layer_size_);
DrawFrame();
EnsureSyncTree();
host_impl_->sync_tree()->PushBrowserControlsFromMainThread(1, 1);
host_impl_->sync_tree()->SetBrowserControlsParams(
{top_controls_height_, 0, 0, 0, false, true});
host_impl_->active_tree()->top_controls_shown_ratio()->PushMainToPending(1);
host_impl_->active_tree()->top_controls_shown_ratio()->PushPendingToActive();
host_impl_->active_tree()->SetCurrentBrowserControlsShownRatio(0, 0);
host_impl_->DidChangeBrowserControlsPosition();
auto* property_trees = host_impl_->active_tree()->property_trees();
EXPECT_EQ(gfx::Vector2dF(0, 50),
property_trees->inner_viewport_container_bounds_delta());
EXPECT_EQ(0, host_impl_->browser_controls_manager()->ContentTopOffset());
host_impl_->ActivateSyncTree();
EXPECT_EQ(0, host_impl_->browser_controls_manager()->ContentTopOffset());
EXPECT_EQ(CommitsToActiveTree() ? gfx::Vector2dF(0, 50) : gfx::Vector2dF(),
property_trees->inner_viewport_container_bounds_delta());
host_impl_->active_tree()->SetCurrentBrowserControlsShownRatio(1, 1);
host_impl_->DidChangeBrowserControlsPosition();
EXPECT_EQ(1, host_impl_->browser_controls_manager()->TopControlsShownRatio());
EXPECT_EQ(50, host_impl_->browser_controls_manager()->TopControlsHeight());
EXPECT_EQ(50, host_impl_->browser_controls_manager()->ContentTopOffset());
EXPECT_EQ(gfx::Vector2dF(),
property_trees->inner_viewport_container_bounds_delta());
}
TEST_P(LayerTreeHostImplBrowserControlsTest,
BrowserControlsViewportOffsetClamping) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
gfx::Size(100, 100), gfx::Size(200, 200), gfx::Size(200, 400));
DrawFrame();
EXPECT_EQ(1, host_impl_->active_tree()->CurrentTopControlsShownRatio());
LayerImpl* outer_scroll = OuterViewportScrollLayer();
LayerImpl* inner_scroll = InnerViewportScrollLayer();
SetScrollOffsetDelta(outer_scroll, gfx::Vector2dF(0, 200));
SetScrollOffsetDelta(inner_scroll, gfx::Vector2dF(100, 100));
gfx::PointF viewport_offset = host_impl_->active_tree()->TotalScrollOffset();
EXPECT_EQ(host_impl_->active_tree()->TotalMaxScrollOffset(), viewport_offset);
gfx::Vector2dF scroll_delta(0, 25);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), scroll_delta,
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), scroll_delta, ui::ScrollInputType::kTouchscreen));
EXPECT_EQ(1, host_impl_->active_tree()->CurrentTopControlsShownRatio());
host_impl_->browser_controls_manager()->ScrollBy(scroll_delta);
GetInputHandler().ScrollEnd();
EXPECT_FLOAT_EQ(
scroll_delta.y(),
top_controls_height_ -
host_impl_->browser_controls_manager()->ContentTopOffset());
EXPECT_EQ(host_impl_->active_tree()->TotalMaxScrollOffset(),
host_impl_->active_tree()->TotalScrollOffset());
viewport_offset = host_impl_->active_tree()->TotalScrollOffset();
scroll_delta = gfx::Vector2dF(0, -25);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), scroll_delta,
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), scroll_delta, ui::ScrollInputType::kTouchscreen));
GetInputHandler().ScrollEnd();
EXPECT_EQ(viewport_offset, host_impl_->active_tree()->TotalScrollOffset());
SetScrollOffsetDelta(outer_scroll, gfx::Vector2dF(0, 200));
SetScrollOffsetDelta(inner_scroll, gfx::Vector2dF(100, 100));
EXPECT_EQ(host_impl_->active_tree()->TotalMaxScrollOffset(),
host_impl_->active_tree()->TotalScrollOffset());
}
TEST_P(LayerTreeHostImplBrowserControlsTest, BrowserControlsAspectRatio) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
gfx::Size(100, 100), gfx::Size(200, 200), gfx::Size(200, 400));
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 0.5f, 2);
DrawFrame();
EXPECT_FLOAT_EQ(top_controls_height_,
host_impl_->browser_controls_manager()->ContentTopOffset());
gfx::Vector2dF scroll_delta(0, 25);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), scroll_delta,
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), scroll_delta, ui::ScrollInputType::kTouchscreen));
GetInputHandler().ScrollEnd();
EXPECT_FLOAT_EQ(
scroll_delta.y(),
top_controls_height_ -
host_impl_->browser_controls_manager()->ContentTopOffset());
auto* property_trees = host_impl_->active_tree()->property_trees();
EXPECT_EQ(gfx::Vector2dF(0, 25),
property_trees->inner_viewport_container_bounds_delta());
float aspect_ratio = 100.0f / 125.0f;
auto expected = gfx::ToCeiledSize(gfx::SizeF(200, 200 / aspect_ratio));
EXPECT_EQ(expected, InnerViewportScrollLayer()->bounds());
}
TEST_P(LayerTreeHostImplBrowserControlsTest, NoShrinkNotUserScrollable) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
gfx::Size(100, 100), gfx::Size(100, 100), gfx::Size(100, 200));
GetScrollNode(OuterViewportScrollLayer())->user_scrollable_vertical = false;
DrawFrame();
gfx::Vector2dF delta(0, 15);
ui::ScrollInputType type = ui::ScrollInputType::kTouchscreen;
auto& handler = GetInputHandler();
handler.ScrollBegin(BeginState(gfx::Point(), delta, type).get(), type);
handler.ScrollUpdate(UpdateState(gfx::Point(), delta, type));
handler.ScrollEnd();
EXPECT_EQ(top_controls_height_,
host_impl_->browser_controls_manager()->ContentTopOffset());
}
TEST_P(LayerTreeHostImplBrowserControlsTest,
BrowserControlsScrollOuterViewport) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
gfx::Size(100, 100), gfx::Size(200, 200), gfx::Size(200, 400));
DrawFrame();
EXPECT_EQ(top_controls_height_,
host_impl_->browser_controls_manager()->ContentTopOffset());
gfx::Vector2dF scroll_delta(0, 15);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), scroll_delta,
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), scroll_delta, ui::ScrollInputType::kTouchscreen));
EXPECT_EQ(OuterViewportScrollLayer()->scroll_tree_index(),
host_impl_->CurrentlyScrollingNode()->id);
GetInputHandler().ScrollEnd();
EXPECT_FLOAT_EQ(
scroll_delta.y(),
top_controls_height_ -
host_impl_->browser_controls_manager()->ContentTopOffset());
scroll_delta = gfx::Vector2dF(0, 50);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), scroll_delta,
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), scroll_delta, ui::ScrollInputType::kTouchscreen));
EXPECT_EQ(0, host_impl_->browser_controls_manager()->ContentTopOffset());
EXPECT_EQ(OuterViewportScrollLayer()->scroll_tree_index(),
host_impl_->CurrentlyScrollingNode()->id);
GetInputHandler().ScrollEnd();
gfx::Vector2dF inner_viewport_offset(0, 25);
SetScrollOffsetDelta(OuterViewportScrollLayer(), gfx::Vector2dF());
SetScrollOffsetDelta(InnerViewportScrollLayer(), inner_viewport_offset);
scroll_delta = gfx::Vector2dF(0, -65);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), scroll_delta,
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), scroll_delta, ui::ScrollInputType::kTouchscreen));
EXPECT_EQ(top_controls_height_,
host_impl_->browser_controls_manager()->ContentTopOffset());
EXPECT_FLOAT_EQ(
inner_viewport_offset.y() + (scroll_delta.y() + top_controls_height_),
ScrollDelta(InnerViewportScrollLayer()).y());
GetInputHandler().ScrollEnd();
}
TEST_P(LayerTreeHostImplBrowserControlsTest,
ScrollNonScrollableRootWithBrowserControls) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
layer_size_, layer_size_, layer_size_);
DrawFrame();
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 50),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
host_impl_->browser_controls_manager()->ScrollBegin();
host_impl_->browser_controls_manager()->ScrollBy(gfx::Vector2dF(0, 50));
host_impl_->browser_controls_manager()->ScrollEnd();
EXPECT_EQ(0, host_impl_->browser_controls_manager()->ContentTopOffset());
auto* property_trees = host_impl_->active_tree()->property_trees();
EXPECT_EQ(gfx::Vector2dF(0, 50),
property_trees->inner_viewport_container_bounds_delta());
GetInputHandler().ScrollEnd();
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, -25),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
float scroll_increment_y = -25;
host_impl_->browser_controls_manager()->ScrollBegin();
host_impl_->browser_controls_manager()->ScrollBy(
gfx::Vector2dF(0, scroll_increment_y));
EXPECT_FLOAT_EQ(-scroll_increment_y,
host_impl_->browser_controls_manager()->ContentTopOffset());
EXPECT_EQ(gfx::Vector2dF(0, 25),
property_trees->inner_viewport_container_bounds_delta());
host_impl_->browser_controls_manager()->ScrollBy(
gfx::Vector2dF(0, scroll_increment_y));
host_impl_->browser_controls_manager()->ScrollEnd();
EXPECT_FLOAT_EQ(-2 * scroll_increment_y,
host_impl_->browser_controls_manager()->ContentTopOffset());
EXPECT_EQ(gfx::Vector2dF(),
property_trees->inner_viewport_container_bounds_delta());
GetInputHandler().ScrollEnd();
EXPECT_EQ(gfx::PointF(), MaxScrollOffset(InnerViewportScrollLayer()));
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
}
TEST_P(LayerTreeHostImplBrowserControlsTest,
ViewportBoundsDeltaOnTreeActivation) {
if (CommitsToActiveTree()) {
GTEST_SKIP();
}
const gfx::Size inner_viewport_size(1000, 1000);
const gfx::Size outer_viewport_size(1000, 1000);
const gfx::Size content_size(2000, 2000);
{
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
inner_viewport_size, outer_viewport_size, content_size);
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 1, 1);
host_impl_->active_tree()->SetBrowserControlsParams(
{top_controls_height_, 0, 0, 0, false, false});
host_impl_->active_tree()->PushBrowserControlsFromMainThread(0, 0);
CreatePendingTree();
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
host_impl_->pending_tree(), inner_viewport_size, outer_viewport_size,
content_size);
host_impl_->pending_tree()->SetBrowserControlsParams(
{top_controls_height_, 0, 0, 0, false, false});
UpdateDrawProperties(host_impl_->pending_tree());
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(75, 75), gfx::Vector2dF(0, 2000),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(), gfx::Vector2d(0, 2000),
ui::ScrollInputType::kTouchscreen));
GetInputHandler().ScrollEnd();
}
LayerImpl* outer_scroll = OuterViewportScrollLayer();
ASSERT_FLOAT_EQ(0,
host_impl_->browser_controls_manager()->ContentTopOffset());
ASSERT_EQ(1000, MaxScrollOffset(outer_scroll).y());
ASSERT_EQ(1000, CurrentScrollOffset(outer_scroll).y());
host_impl_->browser_controls_manager()->UpdateBrowserControlsState(
BrowserControlsState::kBoth, BrowserControlsState::kShown, true,
std::nullopt);
base::TimeTicks start_time = base::TimeTicks::Now();
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
{
begin_frame_args.frame_time = start_time;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
host_impl_->DidFinishImplFrame(begin_frame_args);
float delta =
host_impl_->active_tree()->top_controls_shown_ratio()->Delta();
ASSERT_EQ(delta, 0);
}
{
begin_frame_args.frame_time = start_time + base::Milliseconds(50);
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
host_impl_->DidFinishImplFrame(begin_frame_args);
}
{
float delta =
host_impl_->active_tree()->top_controls_shown_ratio()->Delta();
ASSERT_GT(delta, 0);
ASSERT_LT(delta, 1);
host_impl_->active_tree()
->top_controls_shown_ratio()
->PullDeltaForMainThread( false);
host_impl_->active_tree()->top_controls_shown_ratio()->PushMainToPending(
delta);
}
{
begin_frame_args.frame_time = start_time + base::Milliseconds(200);
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
host_impl_->DidFinishImplFrame(begin_frame_args);
ASSERT_EQ(50, host_impl_->browser_controls_manager()->ContentTopOffset());
ASSERT_EQ(1050, MaxScrollOffset(outer_scroll).y());
ASSERT_NEAR(1050, CurrentScrollOffset(outer_scroll).y(), 1);
}
{
host_impl_->pending_tree()
->property_trees()
->scroll_tree_mutable()
.SetScrollOffsetDeltaForTesting(outer_scroll->element_id(),
gfx::Vector2dF(0, 1050));
host_impl_->ActivateSyncTree();
EXPECT_NEAR(1050, CurrentScrollOffset(outer_scroll).y(), 1);
}
}
TEST_P(LayerTreeHostImplTest, ScrollNonCompositedRoot) {
gfx::Size surface_size(10, 10);
gfx::Size contents_size(20, 20);
SetupViewportLayersNoScrolls(surface_size);
LayerImpl* scroll_container_layer = AddContentLayer();
CreateEffectNode(scroll_container_layer).render_surface_reason =
RenderSurfaceReason::kTest;
LayerImpl* scroll_layer =
AddScrollableLayer(scroll_container_layer, surface_size, contents_size);
LayerImpl* content_layer = AddLayerInActiveTree();
content_layer->SetDrawsContent(true);
content_layer->SetBounds(contents_size);
CopyProperties(scroll_layer, content_layer);
DrawFrame();
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(5, 5), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
GetInputHandler().ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10),
ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
EXPECT_TRUE(did_request_redraw_);
EXPECT_TRUE(did_request_commit_);
}
TEST_P(LayerTreeHostImplTest, ScrollChildCallsCommitAndRedraw) {
gfx::Size surface_size(10, 10);
gfx::Size contents_size(20, 20);
SetupViewportLayersNoScrolls(surface_size);
LayerImpl* content_root = AddContentLayer();
CreateEffectNode(content_root).render_surface_reason =
RenderSurfaceReason::kTest;
AddScrollableLayer(content_root, surface_size, contents_size);
DrawFrame();
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(5, 5), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
GetInputHandler().ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10),
ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
EXPECT_TRUE(did_request_redraw_);
EXPECT_TRUE(did_request_commit_);
}
TEST_P(LayerTreeHostImplTest, ScrollMissesChild) {
gfx::Size viewport_size(5, 5);
gfx::Size surface_size(10, 10);
SetupViewportLayersOuterScrolls(viewport_size, surface_size);
AddScrollableLayer(OuterViewportScrollLayer(), viewport_size, surface_size);
DrawFrame();
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(15, 5), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(host_impl_->CurrentlyScrollingNode(),
host_impl_->OuterViewportScrollNode());
EXPECT_FALSE(did_request_redraw_);
EXPECT_FALSE(did_request_commit_);
}
TEST_P(LayerTreeHostImplTest, ScrollMissesBackfacingChild) {
gfx::Size viewport_size(5, 5);
gfx::Size surface_size(10, 10);
SetupViewportLayersOuterScrolls(viewport_size, surface_size);
LayerImpl* child = AddScrollableLayer(OuterViewportScrollLayer(),
viewport_size, surface_size);
gfx::Transform matrix;
matrix.RotateAboutXAxis(180.0);
GetTransformNode(child)->local = matrix;
CreateEffectNode(child).double_sided = false;
DrawFrame();
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(5, 5), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(host_impl_->CurrentlyScrollingNode(),
host_impl_->OuterViewportScrollNode());
EXPECT_FALSE(did_request_redraw_);
EXPECT_FALSE(did_request_commit_);
}
TEST_P(LayerTreeHostImplTest, ScrollLayerWithMainThreadReason) {
gfx::Size scroll_container_size(5, 5);
gfx::Size surface_size(10, 10);
LayerImpl* root = SetupDefaultRootLayer(surface_size);
LayerImpl* scroll_layer =
AddScrollableLayer(root, scroll_container_size, surface_size);
LayerImpl* content_layer =
AddScrollableLayer(scroll_layer, scroll_container_size, surface_size);
GetScrollNode(content_layer)->main_thread_repaint_reasons =
MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects;
DrawFrame();
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(5, 5), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
EXPECT_EQ(MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects,
host_impl_->CurrentlyScrollingNode()->main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects,
status.main_thread_repaint_reasons);
}
TEST_P(LayerTreeHostImplTest, ScrollRootAndChangePageScaleOnMainThread) {
gfx::Size inner_viewport_size(20, 20);
gfx::Size outer_viewport_size(40, 40);
gfx::Size content_size(80, 80);
SetupViewportLayers(host_impl_->active_tree(), inner_viewport_size,
outer_viewport_size, content_size);
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 1, 2);
DrawFrame();
gfx::Vector2d scroll_delta(0, 10);
gfx::Vector2dF expected_scroll_delta(scroll_delta);
LayerImpl* outer_scroll = OuterViewportScrollLayer();
gfx::PointF expected_max_scroll = MaxScrollOffset(outer_scroll);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(5, 5), scroll_delta,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(), scroll_delta, ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
float page_scale = 2;
host_impl_->active_tree()->PushPageScaleFromMainThread(page_scale, 1, 2);
std::unique_ptr<CompositorCommitData> commit_data =
host_impl_->ProcessCompositorDeltas(
nullptr);
LayerImpl* inner_scroll = InnerViewportScrollLayer();
EXPECT_TRUE(ScrollInfoContains(*commit_data.get(), inner_scroll->element_id(),
expected_scroll_delta));
EXPECT_EQ(expected_max_scroll, MaxScrollOffset(outer_scroll));
EXPECT_EQ(1, host_impl_->active_tree()->page_scale_delta());
}
TEST_P(LayerTreeHostImplTest, ScrollRootAndChangePageScaleOnImplThread) {
gfx::Size inner_viewport_size(20, 20);
gfx::Size outer_viewport_size(40, 40);
gfx::Size content_size(80, 80);
SetupViewportLayers(host_impl_->active_tree(), inner_viewport_size,
outer_viewport_size, content_size);
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 1, 2);
DrawFrame();
gfx::Vector2d scroll_delta(0, 10);
gfx::Vector2dF expected_scroll_delta(scroll_delta);
LayerImpl* outer_scroll = OuterViewportScrollLayer();
gfx::PointF expected_max_scroll = MaxScrollOffset(outer_scroll);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(5, 5), scroll_delta,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(), scroll_delta, ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
float page_scale = 2;
GetInputHandler().ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().PinchGestureBegin(gfx::Point(),
ui::ScrollInputType::kWheel);
GetInputHandler().PinchGestureUpdate(page_scale, gfx::Point());
GetInputHandler().PinchGestureEnd(gfx::Point());
GetInputHandler().ScrollEnd();
DrawOneFrame();
std::unique_ptr<CompositorCommitData> commit_data =
host_impl_->ProcessCompositorDeltas(
nullptr);
LayerImpl* inner_scroll = InnerViewportScrollLayer();
EXPECT_TRUE(ScrollInfoContains(*commit_data.get(), inner_scroll->element_id(),
expected_scroll_delta));
EXPECT_EQ(expected_max_scroll, MaxScrollOffset(outer_scroll));
EXPECT_EQ(page_scale, host_impl_->active_tree()->current_page_scale_factor());
}
TEST_P(LayerTreeHostImplTest, PageScaleDeltaAppliedToRootScrollLayerOnly) {
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 1, 2);
gfx::Size viewport_size(5, 5);
gfx::Size surface_size(10, 10);
float default_page_scale = 1;
gfx::Transform default_page_scale_matrix;
default_page_scale_matrix.Scale(default_page_scale, default_page_scale);
float new_page_scale = 2;
gfx::Transform new_page_scale_matrix;
new_page_scale_matrix.Scale(new_page_scale, new_page_scale);
SetupViewportLayersInnerScrolls(viewport_size, surface_size);
LayerImpl* root = root_layer();
auto* inner_scroll = InnerViewportScrollLayer();
auto* outer_scroll = OuterViewportScrollLayer();
LayerImpl* scrollable_child_clip = AddLayerInActiveTree();
CopyProperties(inner_scroll, scrollable_child_clip);
AddScrollableLayer(scrollable_child_clip, viewport_size, surface_size);
UpdateDrawProperties(host_impl_->active_tree());
GetInputHandler().ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().PinchGestureBegin(gfx::Point(),
ui::ScrollInputType::kWheel);
GetInputHandler().PinchGestureUpdate(new_page_scale, gfx::Point());
GetInputHandler().PinchGestureEnd(gfx::Point());
GetInputHandler().ScrollEnd();
DrawOneFrame();
DrawFrame();
EXPECT_EQ(1, root->DrawTransform().rc(0, 0));
EXPECT_EQ(1, root->DrawTransform().rc(1, 1));
EXPECT_EQ(new_page_scale, inner_scroll->DrawTransform().rc(0, 0));
EXPECT_EQ(new_page_scale, inner_scroll->DrawTransform().rc(1, 1));
EXPECT_EQ(new_page_scale, outer_scroll->DrawTransform().rc(0, 0));
EXPECT_EQ(new_page_scale, outer_scroll->DrawTransform().rc(1, 1));
}
TEST_P(LayerTreeHostImplTest, ScrollChildAndChangePageScaleOnMainThread) {
SetupViewportLayers(host_impl_->active_tree(), gfx::Size(15, 15),
gfx::Size(30, 30), gfx::Size(50, 50));
LayerImpl* outer_scroll = OuterViewportScrollLayer();
LayerImpl* inner_scroll = InnerViewportScrollLayer();
DrawFrame();
gfx::Vector2d scroll_delta(0, 10);
gfx::Vector2dF expected_scroll_delta(scroll_delta);
gfx::PointF expected_max_scroll(MaxScrollOffset(outer_scroll));
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(5, 5), scroll_delta,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(), scroll_delta, ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
float page_scale = 2;
host_impl_->active_tree()->PushPageScaleFromMainThread(page_scale, 1,
page_scale);
DrawOneFrame();
std::unique_ptr<CompositorCommitData> commit_data =
host_impl_->ProcessCompositorDeltas(
nullptr);
EXPECT_TRUE(ScrollInfoContains(*commit_data.get(), inner_scroll->element_id(),
expected_scroll_delta));
EXPECT_EQ(MaxScrollOffset(outer_scroll), expected_max_scroll);
EXPECT_EQ(1, host_impl_->active_tree()->page_scale_delta());
}
TEST_P(LayerTreeHostImplTest, ScrollChildBeyondLimit) {
gfx::Size surface_size(10, 10);
gfx::Size content_size(20, 20);
SetupViewportLayersNoScrolls(surface_size);
LayerImpl* top = AddContentLayer();
CreateEffectNode(top).render_surface_reason = RenderSurfaceReason::kTest;
LayerImpl* child_layer = AddScrollableLayer(top, surface_size, content_size);
LayerImpl* grand_child_layer =
AddScrollableLayer(child_layer, surface_size, content_size);
UpdateDrawProperties(host_impl_->active_tree());
host_impl_->active_tree()->DidBecomeActive();
grand_child_layer->layer_tree_impl()
->property_trees()
->scroll_tree_mutable()
.UpdateScrollOffsetBaseForTesting(grand_child_layer->element_id(),
gfx::PointF(0, 5));
child_layer->layer_tree_impl()
->property_trees()
->scroll_tree_mutable()
.UpdateScrollOffsetBaseForTesting(child_layer->element_id(),
gfx::PointF(3, 0));
DrawFrame();
{
gfx::Vector2d scroll_delta(-8, -7);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), scroll_delta,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(), scroll_delta, ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
std::unique_ptr<CompositorCommitData> commit_data =
host_impl_->ProcessCompositorDeltas(
nullptr);
EXPECT_TRUE(ScrollInfoContains(*commit_data.get(),
grand_child_layer->element_id(),
gfx::Vector2dF(0, -5)));
ExpectNone(*commit_data.get(), child_layer->element_id());
}
}
TEST_P(LayerTreeHostImplTimelinesTest, ScrollAnimatedLatchToChild) {
gfx::Size surface_size(100, 100);
gfx::Size content_size(150, 150);
SetupViewportLayersNoScrolls(surface_size);
LayerImpl* top = AddContentLayer();
CreateEffectNode(top).render_surface_reason = RenderSurfaceReason::kTest;
LayerImpl* child_layer = AddScrollableLayer(top, surface_size, content_size);
LayerImpl* grand_child_layer =
AddScrollableLayer(child_layer, surface_size, content_size);
UpdateDrawProperties(host_impl_->active_tree());
host_impl_->active_tree()->DidBecomeActive();
grand_child_layer->layer_tree_impl()
->property_trees()
->scroll_tree_mutable()
.UpdateScrollOffsetBaseForTesting(grand_child_layer->element_id(),
gfx::PointF(0, 30));
child_layer->layer_tree_impl()
->property_trees()
->scroll_tree_mutable()
.UpdateScrollOffsetBaseForTesting(child_layer->element_id(),
gfx::PointF(0, 50));
DrawFrame();
base::TimeTicks start_time = base::TimeTicks() + base::Milliseconds(10);
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, -100),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
GetInputHandler().ScrollUpdate(
AnimatedUpdateState(gfx::Point(), gfx::Vector2d(0, -100)));
begin_frame_args.frame_time = start_time;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
EXPECT_NE(gfx::PointF(0, 30), CurrentScrollOffset(grand_child_layer));
host_impl_->DidFinishImplFrame(begin_frame_args);
begin_frame_args.frame_time = start_time + base::Milliseconds(200);
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
EXPECT_EQ(gfx::PointF(0, 0), CurrentScrollOffset(grand_child_layer));
EXPECT_EQ(gfx::PointF(0, 50), CurrentScrollOffset(child_layer));
host_impl_->DidFinishImplFrame(begin_frame_args);
GetInputHandler().ScrollUpdate(
AnimatedUpdateState(gfx::Point(), gfx::Vector2d(0, -100)));
begin_frame_args.frame_time = start_time + base::Milliseconds(250);
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
host_impl_->DidFinishImplFrame(begin_frame_args);
begin_frame_args.frame_time = start_time + base::Milliseconds(450);
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
EXPECT_EQ(gfx::PointF(0, 0), CurrentScrollOffset(grand_child_layer));
EXPECT_EQ(gfx::PointF(0, 50), CurrentScrollOffset(child_layer));
host_impl_->DidFinishImplFrame(begin_frame_args);
host_impl_->ReleaseLayerTreeFrameSink();
host_impl_ = nullptr;
}
TEST_F(CommitToActiveTreeLayerTreeHostImplTest, ScrollWithoutBubbling) {
gfx::Size surface_size(20, 20);
gfx::Size viewport_size(10, 10);
SetupViewportLayersNoScrolls(surface_size);
LayerImpl* child_layer = AddScrollableLayer(InnerViewportScrollLayer(),
viewport_size, surface_size);
LayerImpl* grand_child_layer =
AddScrollableLayer(child_layer, viewport_size, surface_size);
UpdateDrawProperties(host_impl_->active_tree());
host_impl_->active_tree()->DidBecomeActive();
gfx::PointF grand_child_base(0, 2);
gfx::Vector2dF grand_child_delta;
grand_child_layer->layer_tree_impl()
->property_trees()
->scroll_tree_mutable()
.UpdateScrollOffsetBaseForTesting(grand_child_layer->element_id(),
grand_child_base);
gfx::PointF child_base(0, 3);
gfx::Vector2dF child_delta;
child_layer->layer_tree_impl()
->property_trees()
->scroll_tree_mutable()
.UpdateScrollOffsetBaseForTesting(child_layer->element_id(), child_base);
DrawFrame();
{
gfx::Vector2d scroll_delta(0, -10);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), scroll_delta,
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), scroll_delta, ui::ScrollInputType::kTouchscreen));
GetInputHandler().ScrollEnd();
std::unique_ptr<CompositorCommitData> commit_data =
host_impl_->ProcessCompositorDeltas(
nullptr);
grand_child_delta = gfx::Vector2dF(0, -2);
EXPECT_TRUE(ScrollInfoContains(*commit_data.get(),
grand_child_layer->element_id(),
grand_child_delta));
ExpectNone(*commit_data.get(), child_layer->element_id());
grand_child_base += grand_child_delta;
child_base += child_delta;
PushScrollOffsetsToPendingTree(
{{child_layer->element_id(), child_base},
{grand_child_layer->element_id(), grand_child_base}});
scroll_delta = gfx::Vector2d(0, -3);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(5, 5), scroll_delta,
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id,
child_layer->scroll_tree_index());
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), scroll_delta, ui::ScrollInputType::kTouchscreen));
EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id,
child_layer->scroll_tree_index());
GetInputHandler().ScrollEnd();
ClearMainThreadDeltasForTesting(host_impl_.get());
commit_data = host_impl_->ProcessCompositorDeltas(
nullptr);
child_delta = gfx::Vector2dF(0, -3);
EXPECT_TRUE(ScrollInfoContains(*commit_data.get(),
child_layer->element_id(), child_delta));
grand_child_delta = gfx::Vector2dF();
ExpectNone(*commit_data.get(), grand_child_layer->element_id());
child_base += child_delta;
grand_child_base += grand_child_delta;
PushScrollOffsetsToPendingTree(
{{grand_child_layer->element_id(), grand_child_base},
{child_layer->element_id(), child_base}});
scroll_delta = gfx::Vector2d(0, 7);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(5, 5), scroll_delta,
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id,
grand_child_layer->scroll_tree_index());
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), scroll_delta, ui::ScrollInputType::kTouchscreen));
EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id,
grand_child_layer->scroll_tree_index());
GetInputHandler().ScrollEnd();
ClearMainThreadDeltasForTesting(host_impl_.get());
commit_data = host_impl_->ProcessCompositorDeltas(
nullptr);
grand_child_delta = gfx::Vector2dF(0, 7);
EXPECT_TRUE(ScrollInfoContains(*commit_data.get(),
grand_child_layer->element_id(),
grand_child_delta));
child_delta = gfx::Vector2dF();
ExpectNone(*commit_data.get(), child_layer->element_id());
grand_child_base += grand_child_delta;
child_base += child_delta;
PushScrollOffsetsToPendingTree(
{{grand_child_layer->element_id(), grand_child_base},
{child_layer->element_id(), child_base}});
host_impl_->active_tree()->PushPageScaleFromMainThread(2, 2, 2);
host_impl_->active_tree()->SetPageScaleOnActiveTree(2);
scroll_delta = gfx::Vector2d(0, -2);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(1, 1), scroll_delta,
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
EXPECT_EQ(grand_child_layer->scroll_tree_index(),
host_impl_->CurrentlyScrollingNode()->id);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), scroll_delta, ui::ScrollInputType::kTouchscreen));
GetInputHandler().ScrollEnd();
ClearMainThreadDeltasForTesting(host_impl_.get());
commit_data = host_impl_->ProcessCompositorDeltas(
nullptr);
EXPECT_TRUE(ScrollInfoContains(*commit_data.get(),
grand_child_layer->element_id(),
gfx::Vector2dF(0, -1)));
}
}
TEST_P(LayerTreeHostImplTest, ChildrenOfInnerScrollNodeCanScrollOnThread) {
gfx::Size viewport_size(10, 10);
gfx::Size content_size(20, 20);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
{
LayerImpl* fixed_layer = AddLayerInActiveTree();
fixed_layer->SetBounds(viewport_size);
fixed_layer->SetDrawsContent(true);
CopyProperties(InnerViewportScrollLayer(), fixed_layer);
}
host_impl_->active_tree()->DidBecomeActive();
DrawFrame();
{
gfx::Vector2dF scroll_delta(0, 4);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(5, 5), scroll_delta,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(), scroll_delta, ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
std::unique_ptr<CompositorCommitData> commit_data =
host_impl_->ProcessCompositorDeltas(
nullptr);
ASSERT_EQ(commit_data->scrolls.size(), 1u);
EXPECT_TRUE(ScrollInfoContains(
*commit_data.get(), host_impl_->OuterViewportScrollNode()->element_id,
scroll_delta));
}
}
TEST_P(LayerTreeHostImplTest, ScrollEventBubbling) {
gfx::Size viewport_size(10, 10);
gfx::Size content_size(20, 20);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
LayerImpl* scroll_child_clip = AddContentLayer();
AddScrollableLayer(scroll_child_clip, gfx::Size(10, 10), gfx::Size(10, 10));
UpdateDrawProperties(host_impl_->active_tree());
host_impl_->active_tree()->DidBecomeActive();
DrawFrame();
{
gfx::Vector2dF scroll_delta(0, 4);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(5, 5), scroll_delta,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(), scroll_delta, ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
std::unique_ptr<CompositorCommitData> commit_data =
host_impl_->ProcessCompositorDeltas(
nullptr);
ASSERT_EQ(commit_data->scrolls.size(), 1u);
EXPECT_TRUE(ScrollInfoContains(
*commit_data.get(), host_impl_->OuterViewportScrollNode()->element_id,
scroll_delta));
}
}
TEST_P(LayerTreeHostImplTest, ScrollBeforeRedraw) {
gfx::Size surface_size(10, 10);
SetupViewportLayersNoScrolls(surface_size);
UpdateDrawProperties(host_impl_->active_tree());
host_impl_->active_tree()->DidBecomeActive();
DrawFrame();
ClearLayersAndPropertyTrees(host_impl_->active_tree());
SetupViewportLayersNoScrolls(surface_size);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(5, 5), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
}
TEST_F(CommitToActiveTreeLayerTreeHostImplTest, ScrollAxisAlignedRotatedLayer) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
auto* scroll_layer = InnerViewportScrollLayer();
scroll_layer->SetDrawsContent(true);
gfx::Transform rotate_transform;
rotate_transform.Rotate(-90.0);
host_impl_->OnDraw(rotate_transform, gfx::Rect(0, 0, 50, 50), false, false);
DrawFrame();
gfx::Vector2d gesture_scroll_delta(10, 0);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gesture_scroll_delta,
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gesture_scroll_delta, ui::ScrollInputType::kTouchscreen));
GetInputHandler().ScrollEnd();
std::unique_ptr<CompositorCommitData> commit_data =
host_impl_->ProcessCompositorDeltas(
nullptr);
EXPECT_TRUE(ScrollInfoContains(*commit_data.get(), scroll_layer->element_id(),
gfx::Vector2dF(0, gesture_scroll_delta.x())));
PushScrollOffsetsToPendingTree(
{{scroll_layer->element_id(), gfx::PointF(10, 0)}});
ClearNonScrollSyncTreeDeltasForTesting();
SetScrollOffsetDelta(scroll_layer, gfx::Vector2dF());
gfx::Vector2dF wheel_scroll_delta(0, 10);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), wheel_scroll_delta,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
GetInputHandler().ScrollUpdate(UpdateState(gfx::Point(), wheel_scroll_delta,
ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
commit_data = host_impl_->ProcessCompositorDeltas(
nullptr);
EXPECT_TRUE(ScrollInfoContains(*commit_data.get(), scroll_layer->element_id(),
wheel_scroll_delta));
}
TEST_F(CommitToActiveTreeLayerTreeHostImplTest,
ScrollNonAxisAlignedRotatedLayer) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
auto* scroll_layer = InnerViewportScrollLayer();
float child_layer_angle = -20;
gfx::Size content_size = scroll_layer->bounds();
gfx::Size scroll_container_bounds(content_size.width(),
content_size.height() / 2);
LayerImpl* clip_layer = AddLayerInActiveTree();
clip_layer->SetBounds(scroll_container_bounds);
CopyProperties(scroll_layer, clip_layer);
gfx::Transform rotate_transform;
rotate_transform.Translate(-50.0, -50.0);
rotate_transform.Rotate(child_layer_angle);
rotate_transform.Translate(50.0, 50.0);
auto& clip_layer_transform_node = CreateTransformNode(clip_layer);
clip_layer_transform_node.origin = gfx::Point3F(
clip_layer->bounds().width() * 0.5f, clip_layer->bounds().height(), 0);
clip_layer_transform_node.local = rotate_transform;
LayerImpl* child =
AddScrollableLayer(clip_layer, scroll_container_bounds, content_size);
ElementId child_scroll_id = child->element_id();
DrawFrame();
{
gfx::Vector2d gesture_scroll_delta(0, 10);
EXPECT_EQ(
ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(1, 1), gesture_scroll_delta,
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gesture_scroll_delta, ui::ScrollInputType::kTouchscreen));
GetInputHandler().ScrollEnd();
gfx::Vector2dF expected_scroll_delta(
0, std::floor(gesture_scroll_delta.y() *
std::cos(base::DegToRad(child_layer_angle))));
std::unique_ptr<CompositorCommitData> commit_data =
host_impl_->ProcessCompositorDeltas(
nullptr);
EXPECT_TRUE(ScrollInfoContains(*commit_data.get(), child_scroll_id,
expected_scroll_delta));
EXPECT_EQ(commit_data->scrolls.size(), 1u);
PushScrollOffsetsToPendingTree(
{{child_scroll_id,
gfx::PointAtOffsetFromOrigin(expected_scroll_delta)}});
ClearNonScrollSyncTreeDeltasForTesting();
}
{
SetScrollOffsetDelta(child, gfx::Vector2dF());
gfx::Vector2d gesture_scroll_delta(10, 0);
EXPECT_EQ(
ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(1, 1), gesture_scroll_delta,
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gesture_scroll_delta, ui::ScrollInputType::kTouchscreen));
GetInputHandler().ScrollEnd();
gfx::Vector2dF expected_scroll_delta(
0, std::floor(-gesture_scroll_delta.x() *
std::sin(base::DegToRad(child_layer_angle))));
std::unique_ptr<CompositorCommitData> commit_data =
host_impl_->ProcessCompositorDeltas(
nullptr);
EXPECT_TRUE(ScrollInfoContains(*commit_data.get(), child_scroll_id,
expected_scroll_delta));
ExpectNone(*commit_data.get(), scroll_layer->element_id());
PushScrollOffsetsToPendingTree(
{{child_scroll_id,
gfx::PointAtOffsetFromOrigin(expected_scroll_delta)}});
}
}
TEST_F(CommitToActiveTreeLayerTreeHostImplTest,
ScrollPerspectiveTransformedLayer) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
auto* scroll_layer = InnerViewportScrollLayer();
LayerImpl* clip_layer = AddLayerInActiveTree();
clip_layer->SetBounds(gfx::Size(50, 50));
CopyProperties(scroll_layer, clip_layer);
gfx::Transform perspective_transform;
perspective_transform.Translate(-50.0, -50.0);
perspective_transform.ApplyPerspectiveDepth(20);
perspective_transform.RotateAboutXAxis(45);
perspective_transform.Translate(50.0, 50.0);
auto& clip_layer_transform_node = CreateTransformNode(clip_layer);
clip_layer_transform_node.origin = gfx::Point3F(
clip_layer->bounds().width(), clip_layer->bounds().height(), 0);
clip_layer_transform_node.local = perspective_transform;
LayerImpl* child = AddScrollableLayer(clip_layer, clip_layer->bounds(),
scroll_layer->bounds());
UpdateDrawProperties(host_impl_->active_tree());
std::unique_ptr<CompositorCommitData> commit_data;
std::array<gfx::Vector2dF, 4> gesture_scroll_deltas;
gesture_scroll_deltas[0] = gfx::Vector2dF(4, 10);
gesture_scroll_deltas[1] = gfx::Vector2dF(4, 10);
gesture_scroll_deltas[2] = gfx::Vector2dF(10, 0);
gesture_scroll_deltas[3] = gfx::Vector2dF(10, 0);
std::array<gfx::Vector2dF, 4> expected_scroll_deltas;
expected_scroll_deltas[0] = gfx::Vector2dF(2, 9);
expected_scroll_deltas[1] = gfx::Vector2dF(1, 4);
expected_scroll_deltas[2] = gfx::Vector2dF(5, 0);
expected_scroll_deltas[3] = gfx::Vector2dF(5, 0);
gfx::Point viewport_point(1, 1);
for (int i = 0; i < 4; ++i) {
SetScrollOffsetDelta(child, gfx::Vector2dF());
DrawFrame();
EXPECT_EQ(
ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(viewport_point, gesture_scroll_deltas[i],
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
GetInputHandler().ScrollUpdate(
UpdateState(viewport_point, gesture_scroll_deltas[i],
ui::ScrollInputType::kTouchscreen));
viewport_point += gfx::ToFlooredVector2d(gesture_scroll_deltas[i]);
GetInputHandler().ScrollEnd();
commit_data = host_impl_->ProcessCompositorDeltas(
nullptr);
EXPECT_TRUE(ScrollInfoContains(*commit_data.get(), child->element_id(),
expected_scroll_deltas[i]));
EXPECT_EQ(commit_data->scrolls.size(), 1u);
PushScrollOffsetsToPendingTree(
{{child->element_id(),
gfx::PointAtOffsetFromOrigin(expected_scroll_deltas[i])}});
ClearMainThreadDeltasForTesting(host_impl_.get());
}
}
TEST_F(CommitToActiveTreeLayerTreeHostImplTest, ScrollScaledLayer) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
auto* scroll_layer = InnerViewportScrollLayer();
int scale = 2;
gfx::Transform scale_transform;
scale_transform.Scale(scale, scale);
host_impl_->OnDraw(scale_transform, gfx::Rect(0, 0, 50, 50), false, false);
DrawFrame();
gfx::Vector2d scroll_delta(0, 10);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), scroll_delta,
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), scroll_delta, ui::ScrollInputType::kTouchscreen));
GetInputHandler().ScrollEnd();
std::unique_ptr<CompositorCommitData> commit_data =
host_impl_->ProcessCompositorDeltas(
nullptr);
EXPECT_TRUE(ScrollInfoContains(*commit_data.get(), scroll_layer->element_id(),
gfx::Vector2dF(0, scroll_delta.y() / scale)));
PushScrollOffsetsToPendingTree(
{{scroll_layer->element_id(), gfx::PointAtOffsetFromOrigin(gfx::Vector2dF(
0, scroll_delta.y() / scale))}});
ClearNonScrollSyncTreeDeltasForTesting();
SetScrollOffsetDelta(scroll_layer, gfx::Vector2dF());
gfx::Vector2dF wheel_scroll_delta(0, 10);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), wheel_scroll_delta,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
GetInputHandler().ScrollUpdate(UpdateState(gfx::Point(), wheel_scroll_delta,
ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
commit_data = host_impl_->ProcessCompositorDeltas(
nullptr);
EXPECT_TRUE(ScrollInfoContains(*commit_data.get(), scroll_layer->element_id(),
wheel_scroll_delta));
}
TEST_P(LayerTreeHostImplTest, ScrollViewportRounding) {
int width = 332;
int height = 20;
int scale = 3;
gfx::Size container_bounds = gfx::Size(width * scale - 1, height * scale);
SetupViewportLayersInnerScrolls(container_bounds, gfx::Size(width, height));
UpdateDrawProperties(host_impl_->active_tree());
host_impl_->active_tree()->SetDeviceScaleFactor(scale);
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 0.5f, 4);
LayerImpl* inner_viewport_scroll_layer = InnerViewportScrollLayer();
EXPECT_EQ(gfx::PointF(0, 0), MaxScrollOffset(inner_viewport_scroll_layer));
}
TEST_F(CommitToPendingTreeLayerTreeHostImplTest,
RootLayerScrollOffsetDelegation) {
TestInputHandlerClient scroll_watcher;
SetupViewportLayersInnerScrolls(gfx::Size(10, 20), gfx::Size(100, 100));
auto* scroll_layer = InnerViewportScrollLayer();
UpdateDrawProperties(host_impl_->active_tree());
GetInputHandler().BindToClient(&scroll_watcher);
gfx::Vector2dF initial_scroll_delta(10, 10);
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree_mutable()
.UpdateScrollOffsetBaseForTesting(scroll_layer->element_id(),
gfx::PointF());
SetScrollOffsetDelta(scroll_layer, initial_scroll_delta);
EXPECT_EQ(gfx::PointF(), scroll_watcher.last_set_scroll_offset());
GetInputHandler().RequestUpdateForSynchronousInputHandler();
EXPECT_EQ(initial_scroll_delta,
scroll_watcher.last_set_scroll_offset().OffsetFromOrigin());
EXPECT_EQ(gfx::SizeF(100, 100), scroll_watcher.scrollable_size());
EXPECT_EQ(gfx::PointF(90, 80), scroll_watcher.max_scroll_offset());
EXPECT_EQ(1, scroll_watcher.page_scale_factor());
EXPECT_EQ(1, scroll_watcher.min_page_scale_factor());
EXPECT_EQ(1, scroll_watcher.max_page_scale_factor());
host_impl_->active_tree()->PushPageScaleFromMainThread(2, 0.5f, 4);
EXPECT_EQ(1, scroll_watcher.page_scale_factor());
EXPECT_EQ(1, scroll_watcher.min_page_scale_factor());
EXPECT_EQ(1, scroll_watcher.max_page_scale_factor());
host_impl_->ActivateSyncTree();
EXPECT_EQ(2, scroll_watcher.page_scale_factor());
EXPECT_EQ(.5f, scroll_watcher.min_page_scale_factor());
EXPECT_EQ(4, scroll_watcher.max_page_scale_factor());
host_impl_->LayerTreeHostImpl::StartPageScaleAnimation(
gfx::Point(0, 0), false, 1, base::TimeDelta());
host_impl_->Animate();
EXPECT_EQ(1, scroll_watcher.page_scale_factor());
EXPECT_EQ(.5f, scroll_watcher.min_page_scale_factor());
EXPECT_EQ(4, scroll_watcher.max_page_scale_factor());
GetInputHandler().ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().PinchGestureBegin(gfx::Point(),
ui::ScrollInputType::kWheel);
GetInputHandler().PinchGestureUpdate(2, gfx::Point());
GetInputHandler().PinchGestureUpdate(.5f, gfx::Point());
GetInputHandler().PinchGestureEnd(gfx::Point());
GetInputHandler().ScrollEnd();
gfx::Vector2dF scroll_delta(0, 10);
gfx::PointF current_offset(7, 8);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), scroll_delta,
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
GetInputHandler().SetSynchronousInputHandlerRootScrollOffset(current_offset);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), scroll_delta, ui::ScrollInputType::kTouchscreen));
EXPECT_EQ(current_offset + scroll_delta,
scroll_watcher.last_set_scroll_offset());
current_offset = gfx::PointF(42, 41);
GetInputHandler().SetSynchronousInputHandlerRootScrollOffset(current_offset);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), scroll_delta, ui::ScrollInputType::kTouchscreen));
EXPECT_EQ(current_offset + scroll_delta,
scroll_watcher.last_set_scroll_offset());
GetInputHandler().ScrollEnd();
GetInputHandler().SetSynchronousInputHandlerRootScrollOffset(gfx::PointF());
gfx::Size new_viewport_size(21, 12);
gfx::Size new_content_size(42, 24);
CreatePendingTree();
host_impl_->pending_tree()->PushPageScaleFromMainThread(1, 1, 1);
SetupViewportLayers(host_impl_->pending_tree(), new_viewport_size,
new_content_size, new_content_size);
host_impl_->ActivateSyncTree();
EXPECT_EQ(gfx::SizeF(new_content_size), scroll_watcher.scrollable_size());
host_impl_->ReleaseLayerTreeFrameSink();
host_impl_ = nullptr;
}
void CheckLayerScrollOffset(LayerImpl* layer, gfx::Point scroll_offset) {
const gfx::Transform target_space_transform =
layer->draw_properties().target_space_transform;
EXPECT_TRUE(target_space_transform.IsScaleOrTranslation());
gfx::Point translated_point = target_space_transform.MapPoint(gfx::Point());
EXPECT_EQ(-scroll_offset.x(), translated_point.x());
EXPECT_EQ(-scroll_offset.y(), translated_point.y());
}
TEST_P(LayerTreeHostImplTest,
ExternalRootLayerScrollOffsetDelegationReflectedInNextDraw) {
SetupViewportLayersInnerScrolls(gfx::Size(10, 20), gfx::Size(100, 100));
auto* scroll_layer = InnerViewportScrollLayer();
scroll_layer->SetDrawsContent(true);
DrawFrame();
CheckLayerScrollOffset(scroll_layer, gfx::Point(0, 0));
EXPECT_FALSE(host_impl_->active_tree()->needs_update_draw_properties());
gfx::PointF scroll_offset(10, 10);
GetInputHandler().SetSynchronousInputHandlerRootScrollOffset(scroll_offset);
CheckLayerScrollOffset(scroll_layer, gfx::Point(0, 0));
EXPECT_TRUE(host_impl_->active_tree()->needs_update_draw_properties());
TestFrameData frame;
auto args = viz::CreateBeginFrameArgsForTesting(
BEGINFRAME_FROM_HERE, viz::BeginFrameArgs::kManualSourceId, 1,
base::TimeTicks() + base::Milliseconds(1));
host_impl_->WillBeginImplFrame(args);
EXPECT_EQ(DrawResult::kSuccess, host_impl_->PrepareToDraw(&frame));
host_impl_->DrawLayers(&frame);
host_impl_->DidDrawAllLayers(frame);
host_impl_->DidFinishImplFrame(args);
EXPECT_FALSE(frame.has_no_damage);
CheckLayerScrollOffset(scroll_layer, gfx::ToRoundedPoint(scroll_offset));
}
TEST_P(LayerTreeHostImplTest, ViewportUserScrollable) {
gfx::Size viewport_size(100, 100);
gfx::Size content_size(200, 200);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
auto* outer_scroll = OuterViewportScrollLayer();
auto* inner_scroll = InnerViewportScrollLayer();
ScrollTree& scroll_tree =
host_impl_->active_tree()->property_trees()->scroll_tree_mutable();
ElementId inner_element_id = inner_scroll->element_id();
ElementId outer_element_id = outer_scroll->element_id();
DrawFrame();
float page_scale_factor = 2;
host_impl_->active_tree()->PushPageScaleFromMainThread(
page_scale_factor, page_scale_factor, page_scale_factor);
GetScrollNode(outer_scroll)->user_scrollable_vertical = true;
GetScrollNode(outer_scroll)->user_scrollable_horizontal = false;
gfx::Vector2dF scroll_delta(30 * page_scale_factor, 0);
{
auto begin_state = BeginState(gfx::Point(), scroll_delta,
ui::ScrollInputType::kTouchscreen);
EXPECT_EQ(
ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(begin_state.get(), ui::ScrollInputType::kTouchscreen)
.thread);
auto update_state = UpdateState(gfx::Point(), scroll_delta,
ui::ScrollInputType::kTouchscreen);
GetInputHandler().ScrollUpdate(update_state);
EXPECT_POINTF_EQ(gfx::PointF(30, 0),
scroll_tree.current_scroll_offset(inner_element_id));
EXPECT_POINTF_EQ(gfx::PointF(0, 0),
scroll_tree.current_scroll_offset(outer_element_id));
update_state = UpdateState(gfx::Point(), scroll_delta,
ui::ScrollInputType::kTouchscreen);
GetInputHandler().ScrollUpdate(update_state);
update_state = UpdateState(gfx::Point(), scroll_delta,
ui::ScrollInputType::kTouchscreen);
GetInputHandler().ScrollUpdate(update_state);
update_state = UpdateState(gfx::Point(), scroll_delta,
ui::ScrollInputType::kTouchscreen);
GetInputHandler().ScrollUpdate(update_state);
EXPECT_POINTF_EQ(gfx::PointF(50, 0),
scroll_tree.current_scroll_offset(inner_element_id));
EXPECT_POINTF_EQ(gfx::PointF(0, 0),
scroll_tree.current_scroll_offset(outer_element_id));
GetInputHandler().ScrollEnd();
}
SetScrollOffset(outer_scroll, gfx::PointF(0, 0));
SetScrollOffset(inner_scroll, gfx::PointF(0, 0));
{
auto begin_state =
BeginState(gfx::Point(), scroll_delta, ui::ScrollInputType::kWheel);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(begin_state.get(), ui::ScrollInputType::kWheel)
.thread);
auto update_state = AnimatedUpdateState(gfx::Point(), scroll_delta);
GetInputHandler().ScrollUpdate(update_state);
base::TimeTicks cur_time = base::TimeTicks() + base::Milliseconds(100);
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
#define ANIMATE(time_ms) \
cur_time += base::Milliseconds(time_ms); \
begin_frame_args.frame_time = (cur_time); \
begin_frame_args.frame_id.sequence_number++; \
host_impl_->WillBeginImplFrame(begin_frame_args); \
host_impl_->Animate(); \
host_impl_->UpdateAnimationState(true); \
host_impl_->DidFinishImplFrame(begin_frame_args);
ANIMATE(0);
ANIMATE(200);
EXPECT_POINTF_EQ(gfx::PointF(30, 0),
scroll_tree.current_scroll_offset(inner_element_id));
EXPECT_POINTF_EQ(gfx::PointF(0, 0),
scroll_tree.current_scroll_offset(outer_element_id));
update_state = AnimatedUpdateState(gfx::Point(), scroll_delta);
GetInputHandler().ScrollUpdate(update_state);
ANIMATE(10);
ANIMATE(200);
EXPECT_POINTF_EQ(gfx::PointF(50, 0),
scroll_tree.current_scroll_offset(inner_element_id));
EXPECT_POINTF_EQ(gfx::PointF(0, 0),
scroll_tree.current_scroll_offset(outer_element_id));
update_state = AnimatedUpdateState(gfx::Point(), scroll_delta);
GetInputHandler().ScrollUpdate(update_state);
ANIMATE(10);
ANIMATE(200);
EXPECT_POINTF_EQ(gfx::PointF(50, 0),
scroll_tree.current_scroll_offset(inner_element_id));
EXPECT_POINTF_EQ(gfx::PointF(0, 0),
scroll_tree.current_scroll_offset(outer_element_id));
SetScrollOffset(inner_scroll, gfx::PointF(50, 50));
update_state = AnimatedUpdateState(gfx::Point(), gfx::Vector2dF(0, 100));
GetInputHandler().ScrollUpdate(update_state);
ANIMATE(16);
ANIMATE(64);
ASSERT_LT(0, scroll_tree.current_scroll_offset(outer_element_id).y());
ASSERT_GT(50, scroll_tree.current_scroll_offset(outer_element_id).y());
ASSERT_EQ(0, scroll_tree.current_scroll_offset(outer_element_id).x());
EXPECT_POINTF_EQ(gfx::PointF(50, 50),
scroll_tree.current_scroll_offset(inner_element_id));
update_state = AnimatedUpdateState(gfx::Point(), gfx::Vector2dF(100, 100));
GetInputHandler().ScrollUpdate(update_state);
ANIMATE(200);
EXPECT_POINTF_EQ(gfx::PointF(0, 100),
scroll_tree.current_scroll_offset(outer_element_id));
EXPECT_POINTF_EQ(gfx::PointF(50, 50),
scroll_tree.current_scroll_offset(inner_element_id));
#undef ANIMATE
GetInputHandler().ScrollEnd();
}
}
TEST_P(LayerTreeHostImplTest, SetRootScrollOffsetUserScrollable) {
gfx::Size viewport_size(100, 100);
gfx::Size content_size(200, 200);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
auto* outer_scroll = OuterViewportScrollLayer();
auto* inner_scroll = InnerViewportScrollLayer();
ScrollTree& scroll_tree =
host_impl_->active_tree()->property_trees()->scroll_tree_mutable();
ElementId inner_element_id = inner_scroll->element_id();
ElementId outer_element_id = outer_scroll->element_id();
DrawFrame();
float page_scale_factor = 2;
host_impl_->active_tree()->PushPageScaleFromMainThread(
page_scale_factor, page_scale_factor, page_scale_factor);
{
ASSERT_FALSE(did_request_redraw_);
GetScrollNode(inner_scroll)->user_scrollable_vertical = false;
GetScrollNode(inner_scroll)->user_scrollable_horizontal = false;
gfx::PointF scroll_offset(25, 30);
GetInputHandler().SetSynchronousInputHandlerRootScrollOffset(scroll_offset);
EXPECT_POINTF_EQ(gfx::PointF(0, 0),
scroll_tree.current_scroll_offset(inner_element_id));
EXPECT_POINTF_EQ(scroll_offset,
scroll_tree.current_scroll_offset(outer_element_id));
EXPECT_TRUE(did_request_redraw_);
did_request_redraw_ = false;
GetScrollNode(inner_scroll)->user_scrollable_vertical = true;
GetScrollNode(inner_scroll)->user_scrollable_horizontal = true;
SetScrollOffset(outer_scroll, gfx::PointF(0, 0));
}
{
ASSERT_FALSE(did_request_redraw_);
GetScrollNode(outer_scroll)->user_scrollable_vertical = false;
GetScrollNode(outer_scroll)->user_scrollable_horizontal = false;
gfx::PointF scroll_offset(120, 140);
GetInputHandler().SetSynchronousInputHandlerRootScrollOffset(scroll_offset);
EXPECT_POINTF_EQ(gfx::PointF(50, 50),
scroll_tree.current_scroll_offset(inner_element_id));
EXPECT_POINTF_EQ(gfx::PointF(0, 0),
scroll_tree.current_scroll_offset(outer_element_id));
EXPECT_TRUE(did_request_redraw_);
did_request_redraw_ = false;
GetScrollNode(outer_scroll)->user_scrollable_vertical = true;
GetScrollNode(outer_scroll)->user_scrollable_horizontal = true;
SetScrollOffset(inner_scroll, gfx::PointF(0, 0));
}
{
ASSERT_FALSE(did_request_redraw_);
GetScrollNode(inner_scroll)->user_scrollable_vertical = false;
GetScrollNode(inner_scroll)->user_scrollable_horizontal = false;
GetScrollNode(outer_scroll)->user_scrollable_vertical = false;
GetScrollNode(outer_scroll)->user_scrollable_horizontal = false;
gfx::PointF scroll_offset(60, 70);
GetInputHandler().SetSynchronousInputHandlerRootScrollOffset(scroll_offset);
EXPECT_POINTF_EQ(gfx::PointF(0, 0),
scroll_tree.current_scroll_offset(inner_element_id));
EXPECT_POINTF_EQ(gfx::PointF(0, 0),
scroll_tree.current_scroll_offset(outer_element_id));
EXPECT_FALSE(did_request_redraw_);
GetScrollNode(inner_scroll)->user_scrollable_vertical = true;
GetScrollNode(inner_scroll)->user_scrollable_horizontal = true;
GetScrollNode(outer_scroll)->user_scrollable_vertical = true;
GetScrollNode(outer_scroll)->user_scrollable_horizontal = true;
}
{
ASSERT_FALSE(did_request_redraw_);
GetScrollNode(outer_scroll)->user_scrollable_vertical = false;
GetScrollNode(outer_scroll)->user_scrollable_horizontal = false;
SetScrollOffset(inner_scroll, gfx::PointF(50, 50));
gfx::PointF scroll_offset(60, 70);
GetInputHandler().SetSynchronousInputHandlerRootScrollOffset(scroll_offset);
EXPECT_POINTF_EQ(gfx::PointF(50, 50),
scroll_tree.current_scroll_offset(inner_element_id));
EXPECT_POINTF_EQ(gfx::PointF(0, 0),
scroll_tree.current_scroll_offset(outer_element_id));
EXPECT_FALSE(did_request_redraw_);
GetScrollNode(outer_scroll)->user_scrollable_vertical = true;
GetScrollNode(outer_scroll)->user_scrollable_horizontal = true;
}
}
TEST_P(LayerTreeHostImplTest, SetRootScrollOffsetNoViewportCrash) {
auto* inner_scroll = InnerViewportScrollLayer();
ASSERT_FALSE(inner_scroll);
gfx::PointF scroll_offset(25, 30);
GetInputHandler().SetSynchronousInputHandlerRootScrollOffset(scroll_offset);
}
TEST_P(LayerTreeHostImplTest, OverscrollRoot) {
InputHandlerScrollResult scroll_result;
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 0.5f, 4);
DrawFrame();
EXPECT_EQ(gfx::Vector2dF(),
GetInputHandler().accumulated_root_overscroll_for_testing());
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
scroll_result = GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2d(0, 10), ui::ScrollInputType::kWheel));
EXPECT_TRUE(scroll_result.did_scroll);
EXPECT_FALSE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF(), scroll_result.unused_scroll_delta);
EXPECT_EQ(gfx::Vector2dF(),
GetInputHandler().accumulated_root_overscroll_for_testing());
scroll_result = GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2d(0, 50), ui::ScrollInputType::kWheel));
EXPECT_TRUE(scroll_result.did_scroll);
EXPECT_TRUE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF(0, 10), scroll_result.unused_scroll_delta);
EXPECT_EQ(gfx::Vector2dF(0, 10),
GetInputHandler().accumulated_root_overscroll_for_testing());
EXPECT_EQ(scroll_result.accumulated_root_overscroll,
GetInputHandler().accumulated_root_overscroll_for_testing());
scroll_result = GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2d(0, -50), ui::ScrollInputType::kWheel));
EXPECT_TRUE(scroll_result.did_scroll);
EXPECT_FALSE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF(), scroll_result.unused_scroll_delta);
EXPECT_EQ(gfx::Vector2dF(0, 0),
GetInputHandler().accumulated_root_overscroll_for_testing());
EXPECT_EQ(scroll_result.accumulated_root_overscroll,
GetInputHandler().accumulated_root_overscroll_for_testing());
scroll_result = GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2d(0, -10), ui::ScrollInputType::kWheel));
EXPECT_FALSE(scroll_result.did_scroll);
EXPECT_TRUE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF(0, -10), scroll_result.unused_scroll_delta);
EXPECT_EQ(gfx::Vector2dF(0, -10),
GetInputHandler().accumulated_root_overscroll_for_testing());
EXPECT_EQ(scroll_result.accumulated_root_overscroll,
GetInputHandler().accumulated_root_overscroll_for_testing());
scroll_result = GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2d(10, 0), ui::ScrollInputType::kWheel));
EXPECT_TRUE(scroll_result.did_scroll);
EXPECT_FALSE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF(0, 0), scroll_result.unused_scroll_delta);
EXPECT_EQ(gfx::Vector2dF(0, -10),
GetInputHandler().accumulated_root_overscroll_for_testing());
EXPECT_EQ(scroll_result.accumulated_root_overscroll,
GetInputHandler().accumulated_root_overscroll_for_testing());
scroll_result = GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2d(-15, 0), ui::ScrollInputType::kWheel));
EXPECT_TRUE(scroll_result.did_scroll);
EXPECT_TRUE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF(-5, 0), scroll_result.unused_scroll_delta);
EXPECT_EQ(gfx::Vector2dF(-5, -10),
GetInputHandler().accumulated_root_overscroll_for_testing());
EXPECT_EQ(scroll_result.accumulated_root_overscroll,
GetInputHandler().accumulated_root_overscroll_for_testing());
scroll_result = GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2d(0, 60), ui::ScrollInputType::kWheel));
EXPECT_TRUE(scroll_result.did_scroll);
EXPECT_TRUE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF(0, 10), scroll_result.unused_scroll_delta);
EXPECT_EQ(gfx::Vector2dF(-5, 10),
GetInputHandler().accumulated_root_overscroll_for_testing());
EXPECT_EQ(scroll_result.accumulated_root_overscroll,
GetInputHandler().accumulated_root_overscroll_for_testing());
scroll_result = GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2d(10, -60), ui::ScrollInputType::kWheel));
EXPECT_TRUE(scroll_result.did_scroll);
EXPECT_TRUE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF(0, -10), scroll_result.unused_scroll_delta);
EXPECT_EQ(gfx::Vector2dF(0, -10),
GetInputHandler().accumulated_root_overscroll_for_testing());
EXPECT_EQ(scroll_result.accumulated_root_overscroll,
GetInputHandler().accumulated_root_overscroll_for_testing());
scroll_result = GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2d(0, -20), ui::ScrollInputType::kWheel));
EXPECT_FALSE(scroll_result.did_scroll);
EXPECT_TRUE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF(0, -20), scroll_result.unused_scroll_delta);
EXPECT_EQ(gfx::Vector2dF(0, -30),
GetInputHandler().accumulated_root_overscroll_for_testing());
EXPECT_EQ(scroll_result.accumulated_root_overscroll,
GetInputHandler().accumulated_root_overscroll_for_testing());
scroll_result = GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2d(0, -20), ui::ScrollInputType::kWheel));
EXPECT_FALSE(scroll_result.did_scroll);
EXPECT_TRUE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF(0, -20), scroll_result.unused_scroll_delta);
EXPECT_EQ(gfx::Vector2dF(0, -50),
GetInputHandler().accumulated_root_overscroll_for_testing());
EXPECT_EQ(scroll_result.accumulated_root_overscroll,
GetInputHandler().accumulated_root_overscroll_for_testing());
scroll_result = GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2d(0, 10), ui::ScrollInputType::kWheel));
EXPECT_TRUE(scroll_result.did_scroll);
EXPECT_FALSE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF(0, 0), scroll_result.unused_scroll_delta);
EXPECT_EQ(gfx::Vector2dF(0, 0),
GetInputHandler().accumulated_root_overscroll_for_testing());
EXPECT_EQ(scroll_result.accumulated_root_overscroll,
GetInputHandler().accumulated_root_overscroll_for_testing());
scroll_result = GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2d(0, -20), ui::ScrollInputType::kWheel));
EXPECT_TRUE(scroll_result.did_scroll);
EXPECT_TRUE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF(0, -10), scroll_result.unused_scroll_delta);
EXPECT_EQ(gfx::Vector2dF(0, -10),
GetInputHandler().accumulated_root_overscroll_for_testing());
EXPECT_EQ(scroll_result.accumulated_root_overscroll,
GetInputHandler().accumulated_root_overscroll_for_testing());
GetInputHandler().ScrollEnd();
}
TEST_P(LayerTreeHostImplTest, OverscrollChildWithoutBubbling) {
InputHandlerScrollResult scroll_result;
gfx::Size scroll_container_size(5, 5);
gfx::Size surface_size(10, 10);
SetupViewportLayersNoScrolls(surface_size);
LayerImpl* root = AddScrollableLayer(OuterViewportScrollLayer(),
scroll_container_size, surface_size);
LayerImpl* child_layer =
AddScrollableLayer(root, scroll_container_size, surface_size);
LayerImpl* grand_child_layer =
AddScrollableLayer(child_layer, scroll_container_size, surface_size);
UpdateDrawProperties(host_impl_->active_tree());
host_impl_->active_tree()->DidBecomeActive();
child_layer->layer_tree_impl()
->property_trees()
->scroll_tree_mutable()
.UpdateScrollOffsetBaseForTesting(child_layer->element_id(),
gfx::PointF(0, 3));
grand_child_layer->layer_tree_impl()
->property_trees()
->scroll_tree_mutable()
.UpdateScrollOffsetBaseForTesting(grand_child_layer->element_id(),
gfx::PointF(0, 2));
DrawFrame();
{
gfx::Vector2d scroll_delta(0, -10);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), scroll_delta,
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
scroll_result = GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), scroll_delta, ui::ScrollInputType::kTouchscreen));
EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id,
grand_child_layer->scroll_tree_index());
EXPECT_TRUE(scroll_result.did_scroll);
EXPECT_FALSE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF(),
GetInputHandler().accumulated_root_overscroll_for_testing());
GetInputHandler().ScrollEnd();
scroll_delta = gfx::Vector2d(0, -30);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(5, 5), scroll_delta,
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id,
child_layer->scroll_tree_index());
EXPECT_EQ(gfx::Vector2dF(),
GetInputHandler().accumulated_root_overscroll_for_testing());
GetInputHandler().ScrollEnd();
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(5, 5), scroll_delta,
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
scroll_result = GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), scroll_delta, ui::ScrollInputType::kTouchscreen));
EXPECT_TRUE(scroll_result.did_scroll);
EXPECT_FALSE(scroll_result.did_overscroll_root);
EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id,
child_layer->scroll_tree_index());
EXPECT_EQ(gfx::Vector2dF(),
GetInputHandler().accumulated_root_overscroll_for_testing());
GetInputHandler().ScrollEnd();
scroll_delta = gfx::Vector2d(0, 70);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(5, 5), scroll_delta,
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id,
grand_child_layer->scroll_tree_index());
scroll_result = GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), scroll_delta, ui::ScrollInputType::kTouchscreen));
EXPECT_TRUE(scroll_result.did_scroll);
EXPECT_FALSE(scroll_result.did_overscroll_root);
EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id,
grand_child_layer->scroll_tree_index());
EXPECT_EQ(gfx::Vector2dF(),
GetInputHandler().accumulated_root_overscroll_for_testing());
GetInputHandler().ScrollEnd();
}
}
TEST_P(LayerTreeHostImplTest, OverscrollChildEventBubbling) {
InputHandlerScrollResult scroll_result;
SetupViewportLayersInnerScrolls(gfx::Size(10, 10), gfx::Size(20, 20));
DrawFrame();
{
gfx::Vector2d scroll_delta(0, 8);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(5, 5), scroll_delta,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
scroll_result = GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(), scroll_delta, ui::ScrollInputType::kWheel));
EXPECT_TRUE(scroll_result.did_scroll);
EXPECT_FALSE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF(),
GetInputHandler().accumulated_root_overscroll_for_testing());
scroll_result = GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(), scroll_delta, ui::ScrollInputType::kWheel));
EXPECT_TRUE(scroll_result.did_scroll);
EXPECT_TRUE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF(0, 6),
GetInputHandler().accumulated_root_overscroll_for_testing());
scroll_result = GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(), scroll_delta, ui::ScrollInputType::kWheel));
EXPECT_FALSE(scroll_result.did_scroll);
EXPECT_TRUE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF(0, 14),
GetInputHandler().accumulated_root_overscroll_for_testing());
GetInputHandler().ScrollEnd();
}
}
TEST_P(LayerTreeHostImplTest, OverscrollAlways) {
InputHandlerScrollResult scroll_result;
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
SetupViewportLayersNoScrolls(gfx::Size(50, 50));
UpdateDrawProperties(host_impl_->active_tree());
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 0.5f, 4);
DrawFrame();
EXPECT_EQ(gfx::Vector2dF(),
GetInputHandler().accumulated_root_overscroll_for_testing());
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
scroll_result = GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2d(0, 10), ui::ScrollInputType::kWheel));
EXPECT_FALSE(scroll_result.did_scroll);
EXPECT_TRUE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF(0, 10),
GetInputHandler().accumulated_root_overscroll_for_testing());
}
TEST_P(LayerTreeHostImplTest, NoOverscrollWhenNotAtEdge) {
InputHandlerScrollResult scroll_result;
gfx::Size viewport_size(100, 100);
gfx::Size content_size(200, 200);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
DrawFrame();
{
EXPECT_EQ(
ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(0, 0), gfx::Vector2dF(0, 100),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
scroll_result = GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2dF(0, 100), ui::ScrollInputType::kWheel));
EXPECT_TRUE(scroll_result.did_scroll);
EXPECT_FALSE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF(),
GetInputHandler().accumulated_root_overscroll_for_testing());
scroll_result = GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2dF(0, -2.30f), ui::ScrollInputType::kWheel));
EXPECT_TRUE(scroll_result.did_scroll);
EXPECT_FALSE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF(),
GetInputHandler().accumulated_root_overscroll_for_testing());
GetInputHandler().ScrollEnd();
EXPECT_EQ(
ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(0, 0), gfx::Vector2dF(0, 20),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
scroll_result = GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(), gfx::Vector2dF(0, 20),
ui::ScrollInputType::kTouchscreen));
EXPECT_TRUE(scroll_result.did_scroll);
EXPECT_TRUE(scroll_result.did_overscroll_root);
EXPECT_VECTOR2DF_EQ(
gfx::Vector2dF(0.000000f, 17.699997f),
GetInputHandler().accumulated_root_overscroll_for_testing());
scroll_result = GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(), gfx::Vector2dF(0.02f, -0.01f),
ui::ScrollInputType::kTouchscreen));
EXPECT_FALSE(scroll_result.did_scroll);
EXPECT_FALSE(scroll_result.did_overscroll_root);
EXPECT_VECTOR2DF_EQ(
gfx::Vector2dF(0.000000f, 17.699997f),
GetInputHandler().accumulated_root_overscroll_for_testing());
GetInputHandler().ScrollEnd();
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(
BeginState(gfx::Point(0, 0), gfx::Vector2dF(-0.12f, 0.1f),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
scroll_result = GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(), gfx::Vector2dF(-0.12f, 0.1f),
ui::ScrollInputType::kWheel));
EXPECT_FALSE(scroll_result.did_scroll);
EXPECT_FALSE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF(),
GetInputHandler().accumulated_root_overscroll_for_testing());
GetInputHandler().ScrollEnd();
}
}
TEST_P(LayerTreeHostImplTest, NoOverscrollOnNonViewportLayers) {
const gfx::Size content_size(200, 200);
const gfx::Size viewport_size(100, 100);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
LayerImpl* outer_scroll_layer = OuterViewportScrollLayer();
LayerImpl* content_layer = AddContentLayer();
LayerImpl* scroll_layer =
AddScrollableLayer(content_layer, content_size, gfx::Size(400, 400));
InputHandlerScrollResult scroll_result;
DrawFrame();
{
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(0, 0), gfx::Vector2dF(100, 100),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(0, 0), gfx::Vector2dF(100, 100),
ui::ScrollInputType::kTouchscreen));
EXPECT_POINTF_EQ(gfx::PointF(100, 100), CurrentScrollOffset(scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0),
CurrentScrollOffset(outer_scroll_layer));
}
{
InputHandlerScrollResult result = GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(0, 0), gfx::Vector2dF(120, 140),
ui::ScrollInputType::kTouchscreen));
EXPECT_POINTF_EQ(gfx::PointF(200, 200), CurrentScrollOffset(scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0),
CurrentScrollOffset(outer_scroll_layer));
EXPECT_FALSE(result.did_overscroll_root);
}
{
InputHandlerScrollResult result = GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(0, 0), gfx::Vector2dF(20, 40),
ui::ScrollInputType::kTouchscreen));
EXPECT_POINTF_EQ(gfx::PointF(200, 200), CurrentScrollOffset(scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0),
CurrentScrollOffset(outer_scroll_layer));
EXPECT_FALSE(result.did_overscroll_root);
}
GetInputHandler().ScrollEnd();
}
TEST_P(LayerTreeHostImplTest, ScrollFromOuterViewportSibling) {
const gfx::Size viewport_size(100, 100);
SetupViewportLayersNoScrolls(viewport_size);
host_impl_->active_tree()->SetBrowserControlsParams(
{10, 0, 0, 0, false, false});
host_impl_->active_tree()->SetCurrentBrowserControlsShownRatio(1.f, 1.f);
LayerImpl* outer_scroll_layer = OuterViewportScrollLayer();
LayerImpl* inner_scroll_layer = InnerViewportScrollLayer();
LayerImpl* scroll_layer = AddScrollableLayer(
inner_scroll_layer, viewport_size, gfx::Size(400, 400));
float min_page_scale = 1, max_page_scale = 4;
float page_scale_factor = 2;
host_impl_->active_tree()->PushPageScaleFromMainThread(
page_scale_factor, min_page_scale, max_page_scale);
host_impl_->active_tree()->SetPageScaleOnActiveTree(page_scale_factor);
{
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(0, 0), gfx::Vector2dF(1000, 1000),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(0, 0), gfx::Vector2dF(1000, 1000),
ui::ScrollInputType::kTouchscreen));
GetInputHandler().ScrollEnd();
EXPECT_EQ(1, host_impl_->active_tree()->CurrentTopControlsShownRatio());
EXPECT_POINTF_EQ(gfx::PointF(300, 300), CurrentScrollOffset(scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0),
CurrentScrollOffset(inner_scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0),
CurrentScrollOffset(outer_scroll_layer));
}
{
gfx::Vector2d scroll_delta(0, 10);
GetInputHandler().ScrollBegin(BeginState(gfx::Point(0, 0), scroll_delta,
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), scroll_delta, ui::ScrollInputType::kTouchscreen));
EXPECT_EQ(0, host_impl_->active_tree()->CurrentTopControlsShownRatio());
EXPECT_POINTF_EQ(gfx::PointF(0, 0),
CurrentScrollOffset(inner_scroll_layer));
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), scroll_delta, ui::ScrollInputType::kTouchscreen));
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), scroll_delta, ui::ScrollInputType::kTouchscreen));
EXPECT_POINTF_EQ(gfx::PointF(0, 10),
CurrentScrollOffset(inner_scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0),
CurrentScrollOffset(outer_scroll_layer));
GetInputHandler().ScrollEnd();
}
}
TEST_P(LayerTreeHostImplTest, ScrollChainingWithReplacedOuterViewport) {
const gfx::Size content_size(200, 200);
const gfx::Size viewport_size(100, 100);
LayerTreeImpl* layer_tree_impl = host_impl_->active_tree();
SetupViewportLayersOuterScrolls(viewport_size, content_size);
LayerImpl* outer_scroll_layer = OuterViewportScrollLayer();
LayerImpl* inner_scroll_layer = InnerViewportScrollLayer();
LayerImpl* content_layer = AddContentLayer();
LayerImpl* scroll_layer =
AddScrollableLayer(content_layer, content_size, gfx::Size(400, 400));
GetScrollNode(scroll_layer)->scrolls_outer_viewport = true;
LayerImpl* child_scroll_layer = AddScrollableLayer(
scroll_layer, gfx::Size(300, 300), gfx::Size(500, 500));
auto viewport_property_ids = layer_tree_impl->ViewportPropertyIdsForTesting();
viewport_property_ids.outer_scroll = scroll_layer->scroll_tree_index();
layer_tree_impl->SetViewportPropertyIds(viewport_property_ids);
UpdateDrawProperties(layer_tree_impl);
{
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(0, 0), gfx::Vector2dF(200, 200),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(0, 0), gfx::Vector2dF(200, 200),
ui::ScrollInputType::kTouchscreen));
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(gfx::PointF(200, 200),
CurrentScrollOffset(child_scroll_layer));
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(0, 0), gfx::Vector2dF(200, 200),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(0, 0), gfx::Vector2dF(200, 200),
ui::ScrollInputType::kTouchscreen));
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(gfx::PointF(0, 0),
CurrentScrollOffset(outer_scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(200, 200), CurrentScrollOffset(scroll_layer));
}
{
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(0, 0), gfx::Vector2dF(100, 100),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(0, 0), gfx::Vector2dF(100, 100),
ui::ScrollInputType::kTouchscreen));
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(gfx::PointF(0, 0),
CurrentScrollOffset(outer_scroll_layer));
}
float min_page_scale = 1, max_page_scale = 4;
float page_scale_factor = 2;
host_impl_->active_tree()->PushPageScaleFromMainThread(
page_scale_factor, min_page_scale, max_page_scale);
host_impl_->active_tree()->SetPageScaleOnActiveTree(page_scale_factor);
scroll_layer->SetCurrentScrollOffset(gfx::PointF(0, 0));
{
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(0, 0), gfx::Vector2dF(100, 100),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(0, 0), gfx::Vector2dF(100, 100),
ui::ScrollInputType::kTouchscreen));
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(gfx::PointF(50, 50),
CurrentScrollOffset(inner_scroll_layer));
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(0, 0), gfx::Vector2dF(100, 100),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(0, 0), gfx::Vector2dF(100, 100),
ui::ScrollInputType::kTouchscreen));
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(gfx::PointF(0, 0),
CurrentScrollOffset(outer_scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(50, 50), CurrentScrollOffset(scroll_layer));
}
}
TEST_P(LayerTreeHostImplTest, RootScrollerScrollNonDescendant) {
const gfx::Size content_size(300, 300);
const gfx::Size viewport_size(300, 300);
LayerTreeImpl* layer_tree_impl = host_impl_->active_tree();
SetupViewportLayersInnerScrolls(viewport_size, content_size);
LayerImpl* inner_scroll_layer = InnerViewportScrollLayer();
LayerImpl* content_layer = AddContentLayer();
LayerImpl* outer_scroll_layer =
AddScrollableLayer(content_layer, content_size, gfx::Size(1200, 1200));
LayerImpl* sibling_scroll_layer = AddScrollableLayer(
content_layer, gfx::Size(600, 600), gfx::Size(1200, 1200));
GetScrollNode(InnerViewportScrollLayer())
->prevent_viewport_scrolling_from_inner = true;
GetScrollNode(OuterViewportScrollLayer())->scrolls_outer_viewport = false;
GetScrollNode(outer_scroll_layer)->scrolls_outer_viewport = true;
auto viewport_property_ids = layer_tree_impl->ViewportPropertyIdsForTesting();
viewport_property_ids.outer_scroll = outer_scroll_layer->scroll_tree_index();
layer_tree_impl->SetViewportPropertyIds(viewport_property_ids);
ASSERT_EQ(outer_scroll_layer,
layer_tree_impl->OuterViewportScrollLayerForTesting());
{
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(0, 0), gfx::Vector2dF(1000, 1000),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(0, 0), gfx::Vector2dF(1000, 1000),
ui::ScrollInputType::kTouchscreen));
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(gfx::PointF(600, 600),
CurrentScrollOffset(sibling_scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0),
CurrentScrollOffset(outer_scroll_layer));
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(0, 0), gfx::Vector2dF(1000, 1000),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(0, 0), gfx::Vector2dF(1000, 1000),
ui::ScrollInputType::kTouchscreen));
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(gfx::PointF(600, 600),
CurrentScrollOffset(sibling_scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0),
CurrentScrollOffset(outer_scroll_layer));
}
float min_page_scale = 1, max_page_scale = 4;
float page_scale_factor = 1;
host_impl_->active_tree()->PushPageScaleFromMainThread(
page_scale_factor, min_page_scale, max_page_scale);
gfx::PointF viewport_bottom_right(viewport_size.width(),
viewport_size.height());
sibling_scroll_layer->SetCurrentScrollOffset(gfx::PointF());
{
page_scale_factor = 2;
gfx::Point anchor(viewport_size.width() / 2, viewport_size.height() / 2);
GetInputHandler().ScrollBegin(
BeginState(anchor, gfx::Vector2dF(), ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().PinchGestureBegin(anchor, ui::ScrollInputType::kWheel);
GetInputHandler().PinchGestureUpdate(page_scale_factor, anchor);
GetInputHandler().PinchGestureEnd(anchor);
EXPECT_POINTF_EQ(gfx::PointF(anchor.x() / 2, anchor.y() / 2),
CurrentScrollOffset(inner_scroll_layer));
GetInputHandler().ScrollUpdate(
UpdateState(anchor, viewport_bottom_right.OffsetFromOrigin(),
ui::ScrollInputType::kTouchscreen));
EXPECT_POINTF_EQ(
gfx::ScalePoint(viewport_bottom_right, 1 / page_scale_factor),
CurrentScrollOffset(inner_scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0),
CurrentScrollOffset(sibling_scroll_layer));
GetInputHandler().ScrollEnd();
}
sibling_scroll_layer->SetCurrentScrollOffset(gfx::PointF());
inner_scroll_layer->SetCurrentScrollOffset(gfx::PointF());
outer_scroll_layer->SetCurrentScrollOffset(gfx::PointF());
{
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(0, 0), gfx::Vector2dF(2000, 2000),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(0, 0), gfx::Vector2dF(2000, 2000),
ui::ScrollInputType::kTouchscreen));
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(gfx::PointF(600, 600),
CurrentScrollOffset(sibling_scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0),
CurrentScrollOffset(inner_scroll_layer));
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(0, 0), gfx::Vector2dF(2000, 2000),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(0, 0), gfx::Vector2dF(2000, 2000),
ui::ScrollInputType::kTouchscreen));
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(ScalePoint(viewport_bottom_right, 1 / page_scale_factor),
CurrentScrollOffset(inner_scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0),
CurrentScrollOffset(outer_scroll_layer));
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(0, 0), gfx::Vector2dF(2000, 2000),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(0, 0), gfx::Vector2dF(2000, 2000),
ui::ScrollInputType::kTouchscreen));
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(gfx::PointF(0, 0),
CurrentScrollOffset(outer_scroll_layer));
}
}
TEST_P(LayerTreeHostImplTest, OverscrollOnImplThread) {
InputHandlerScrollResult scroll_result;
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
const gfx::Size content_size(50, 50);
SetupViewportLayersNoScrolls(content_size);
LayerImpl* scroll_layer = InnerViewportScrollLayer();
ScrollNode* scroll_node = GetScrollNode(scroll_layer);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
scroll_node->main_thread_repaint_reasons);
DrawFrame();
EXPECT_EQ(nullptr, host_impl_->active_tree()->FindLayerThatIsHitByPoint(
gfx::PointF(0, 60)));
EXPECT_EQ(
ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(0, 60), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
GetInputHandler().ScrollEnd();
EXPECT_NE(nullptr, host_impl_->active_tree()->FindLayerThatIsHitByPoint(
gfx::PointF(0, 0)));
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(0, 0), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
}
class BlendStateCheckLayer : public LayerImpl {
public:
static std::unique_ptr<BlendStateCheckLayer> Create(
LayerTreeImpl* tree_impl,
int id,
viz::ClientResourceProvider* resource_provider) {
return base::WrapUnique(
new BlendStateCheckLayer(tree_impl, id, resource_provider));
}
BlendStateCheckLayer(LayerTreeImpl* tree_impl,
int id,
viz::ClientResourceProvider* resource_provider)
: LayerImpl(tree_impl, id),
resource_provider_(resource_provider),
blend_(false),
has_render_surface_(false),
comparison_layer_(nullptr),
quads_appended_(false),
quad_rect_(5, 5, 5, 5),
quad_visible_rect_(5, 5, 5, 5),
shared_image_interface_(
base::MakeRefCounted<gpu::TestSharedImageInterface>()) {
auto shared_image =
shared_image_interface_->CreateSharedImageForSoftwareCompositor(
{viz::SinglePlaneFormat::kBGRA_8888, gfx::Size(1, 1),
gfx::ColorSpace(), gpu::SHARED_IMAGE_USAGE_CPU_WRITE_ONLY,
"BlendStateCheckLayerTest"});
auto sync_token = shared_image_interface_->GenUnverifiedSyncToken();
viz::TransferableResource resource = viz::TransferableResource::Make(
shared_image,
viz::TransferableResource::ResourceSource::kTileRasterTask, sync_token);
resource_id_ = resource_provider_->ImportResource(std::move(resource),
base::DoNothing());
SetBounds(gfx::Size(10, 10));
SetDrawsContent(true);
}
void ReleaseResources() override {
resource_provider_->RemoveImportedResource(resource_id_);
}
void AppendQuads(const AppendQuadsContext& context,
viz::CompositorRenderPass* render_pass,
AppendQuadsData* append_quads_data) override {
quads_appended_ = true;
gfx::Rect opaque_rect;
if (contents_opaque())
opaque_rect = quad_rect_;
else
opaque_rect = opaque_content_rect_;
gfx::Rect visible_quad_rect = quad_visible_rect_;
bool needs_blending = !opaque_rect.Contains(visible_quad_rect);
viz::SharedQuadState* shared_quad_state =
render_pass->CreateAndAppendSharedQuadState();
PopulateSharedQuadState(shared_quad_state, contents_opaque());
auto* test_blending_draw_quad =
render_pass->CreateAndAppendDrawQuad<viz::TileDrawQuad>();
test_blending_draw_quad->SetNew(
shared_quad_state, quad_rect_, visible_quad_rect, needs_blending,
resource_id_, gfx::RectF(0, 0, 1, 1), false, false);
EXPECT_EQ(blend_, test_blending_draw_quad->ShouldDrawWithBlending());
EXPECT_EQ(has_render_surface_,
GetRenderSurface(this) != GetRenderSurface(comparison_layer_));
}
void SetExpectation(bool blend,
bool has_render_surface,
LayerImpl* comparison_layer) {
blend_ = blend;
has_render_surface_ = has_render_surface;
comparison_layer_ = comparison_layer;
quads_appended_ = false;
}
bool quads_appended() const { return quads_appended_; }
void SetQuadRect(const gfx::Rect& rect) { quad_rect_ = rect; }
void SetQuadVisibleRect(const gfx::Rect& rect) { quad_visible_rect_ = rect; }
void SetOpaqueContentRect(const gfx::Rect& rect) {
opaque_content_rect_ = rect;
}
private:
raw_ptr<viz::ClientResourceProvider> resource_provider_;
bool blend_;
bool has_render_surface_;
raw_ptr<LayerImpl> comparison_layer_;
bool quads_appended_;
gfx::Rect quad_rect_;
gfx::Rect opaque_content_rect_;
gfx::Rect quad_visible_rect_;
viz::ResourceId resource_id_;
scoped_refptr<gpu::TestSharedImageInterface> shared_image_interface_;
};
TEST_P(LayerTreeHostImplTest, MayThrottleIfUnusedFrames) {
viz::CompositorFrameMetadata metadata;
metadata = host_impl_->MakeCompositorFrameMetadata();
EXPECT_TRUE(metadata.may_throttle_if_undrawn_frames);
host_impl_->SetMayThrottleIfUndrawnFrames(false);
metadata = host_impl_->MakeCompositorFrameMetadata();
EXPECT_FALSE(metadata.may_throttle_if_undrawn_frames);
host_impl_->SetMayThrottleIfUndrawnFrames(true);
metadata = host_impl_->MakeCompositorFrameMetadata();
EXPECT_TRUE(metadata.may_throttle_if_undrawn_frames);
}
class LayerTreeHostImplViewportCoveredTest
: public CommitToPendingTreeLayerTreeHostImplTest {
protected:
LayerTreeHostImplViewportCoveredTest()
: gutter_quad_material_(viz::DrawQuad::Material::kSolidColor),
child_(nullptr),
did_activate_pending_tree_(false) {}
std::unique_ptr<LayerTreeFrameSink> CreateFakeLayerTreeFrameSink(
bool software) {
if (software)
return FakeLayerTreeFrameSink::CreateSoftware();
return FakeLayerTreeFrameSink::Create3d();
}
void SetupActiveTreeLayers() {
host_impl_->active_tree()->set_background_color(SkColors::kGray);
LayerImpl* root = SetupDefaultRootLayer(viewport_size_);
child_ = AddLayer<BlendStateCheckLayer>(host_impl_->active_tree(),
host_impl_->resource_provider());
child_->SetExpectation(false, false, root);
child_->SetContentsOpaque(true);
CopyProperties(root, child_);
UpdateDrawProperties(host_impl_->active_tree());
}
void SetLayerGeometry(const gfx::Rect& layer_rect) {
child_->SetBounds(layer_rect.size());
child_->SetQuadRect(gfx::Rect(layer_rect.size()));
child_->SetQuadVisibleRect(gfx::Rect(layer_rect.size()));
child_->SetOffsetToTransformParent(
gfx::Vector2dF(layer_rect.OffsetFromOrigin()));
}
void TestLayerCoversFullViewport() {
SetLayerGeometry(gfx::Rect(viewport_size_));
TestFrameData frame;
EXPECT_EQ(DrawResult::kSuccess, host_impl_->PrepareToDraw(&frame));
ASSERT_EQ(1u, frame.render_passes.size());
EXPECT_EQ(0u, CountGutterQuads(frame.render_passes[0]->quad_list));
EXPECT_EQ(1u, frame.render_passes[0]->quad_list.size());
ValidateTextureDrawQuads(frame.render_passes[0]->quad_list);
VerifyQuadsExactlyCoverViewport(frame.render_passes[0]->quad_list);
host_impl_->DidDrawAllLayers(frame);
}
void SetUpEmptylayer() { SetLayerGeometry(gfx::Rect()); }
void VerifyEmptyLayerRenderPasses(
const viz::CompositorRenderPassList& render_passes) {
ASSERT_EQ(1u, render_passes.size());
EXPECT_EQ(1u, CountGutterQuads(render_passes[0]->quad_list));
EXPECT_EQ(1u, render_passes[0]->quad_list.size());
ValidateTextureDrawQuads(render_passes[0]->quad_list);
VerifyQuadsExactlyCoverViewport(render_passes[0]->quad_list);
}
void TestEmptyLayer() {
SetUpEmptylayer();
DrawFrame();
}
void TestEmptyLayerWithOnDraw() {
SetUpEmptylayer();
gfx::Transform identity;
gfx::Rect viewport(viewport_size_);
bool resourceless_software_draw = true;
host_impl_->OnDraw(identity, viewport, resourceless_software_draw, false);
VerifyEmptyLayerRenderPasses(last_on_draw_render_passes_);
}
void SetUpLayerInMiddleOfViewport() {
SetLayerGeometry(gfx::Rect(500, 500, 200, 200));
}
void VerifyLayerInMiddleOfViewport(
const viz::CompositorRenderPassList& render_passes) {
ASSERT_EQ(1u, render_passes.size());
EXPECT_EQ(4u, CountGutterQuads(render_passes[0]->quad_list));
EXPECT_EQ(5u, render_passes[0]->quad_list.size());
ValidateTextureDrawQuads(render_passes[0]->quad_list);
VerifyQuadsExactlyCoverViewport(render_passes[0]->quad_list);
}
void TestLayerInMiddleOfViewport() {
SetUpLayerInMiddleOfViewport();
DrawFrame();
}
void TestLayerInMiddleOfViewportWithOnDraw() {
SetUpLayerInMiddleOfViewport();
gfx::Transform identity;
gfx::Rect viewport(viewport_size_);
bool resourceless_software_draw = true;
host_impl_->OnDraw(identity, viewport, resourceless_software_draw, false);
VerifyLayerInMiddleOfViewport(last_on_draw_render_passes_);
}
void SetUpLayerIsLargerThanViewport() {
SetLayerGeometry(
gfx::Rect(viewport_size_.width() + 10, viewport_size_.height() + 10));
}
void VerifyLayerIsLargerThanViewport(
const viz::CompositorRenderPassList& render_passes) {
ASSERT_EQ(1u, render_passes.size());
EXPECT_EQ(0u, CountGutterQuads(render_passes[0]->quad_list));
EXPECT_EQ(1u, render_passes[0]->quad_list.size());
ValidateTextureDrawQuads(render_passes[0]->quad_list);
}
void TestLayerIsLargerThanViewport() {
SetUpLayerIsLargerThanViewport();
DrawFrame();
}
void TestLayerIsLargerThanViewportWithOnDraw() {
SetUpLayerIsLargerThanViewport();
gfx::Transform identity;
gfx::Rect viewport(viewport_size_);
bool resourceless_software_draw = true;
host_impl_->OnDraw(identity, viewport, resourceless_software_draw, false);
VerifyLayerIsLargerThanViewport(last_on_draw_render_passes_);
}
void DidActivateSyncTree() override {
CommitToPendingTreeLayerTreeHostImplTest::DidActivateSyncTree();
did_activate_pending_tree_ = true;
}
void set_gutter_quad_material(viz::DrawQuad::Material material) {
gutter_quad_material_ = material;
}
void set_gutter_texture_size(const gfx::Size& gutter_texture_size) {
gutter_texture_size_ = gutter_texture_size;
}
protected:
size_t CountGutterQuads(const viz::QuadList& quad_list) {
size_t num_gutter_quads = 0;
for (auto* quad : quad_list) {
num_gutter_quads += (quad->material == gutter_quad_material_) ? 1 : 0;
}
return num_gutter_quads;
}
void VerifyQuadsExactlyCoverViewport(const viz::QuadList& quad_list) {
VerifyQuadsExactlyCoverRect(quad_list,
gfx::Rect(DipSizeToPixelSize(viewport_size_)));
}
void ValidateTextureDrawQuads(const viz::QuadList& quad_list) {
for (auto* quad : quad_list) {
if (quad->material != viz::DrawQuad::Material::kTextureContent)
continue;
const viz::TextureDrawQuad* texture_quad =
viz::TextureDrawQuad::MaterialCast(quad);
gfx::SizeF gutter_texture_size_pixels =
gfx::ScaleSize(gfx::SizeF(gutter_texture_size_),
host_impl_->active_tree()->device_scale_factor());
const gfx::RectF texture_quad_tex_coords(
texture_quad->GetNormalizedTexCoords(
gfx::ToRoundedSize(gutter_texture_size_pixels)));
EXPECT_EQ(texture_quad_tex_coords.x(),
texture_quad->rect.x() / gutter_texture_size_pixels.width());
EXPECT_EQ(texture_quad_tex_coords.y(),
texture_quad->rect.y() / gutter_texture_size_pixels.height());
EXPECT_EQ(
texture_quad_tex_coords.right(),
texture_quad->rect.right() / gutter_texture_size_pixels.width());
EXPECT_EQ(
texture_quad_tex_coords.bottom(),
texture_quad->rect.bottom() / gutter_texture_size_pixels.height());
}
}
viz::DrawQuad::Material gutter_quad_material_;
gfx::Size gutter_texture_size_;
gfx::Size viewport_size_;
raw_ptr<BlendStateCheckLayer> child_;
bool did_activate_pending_tree_;
};
TEST_F(LayerTreeHostImplViewportCoveredTest, ViewportCovered) {
viewport_size_ = gfx::Size(1000, 1000);
bool software = false;
CreateHostImpl(DefaultSettings(), CreateFakeLayerTreeFrameSink(software));
SetupActiveTreeLayers();
EXPECT_SCOPED(TestLayerCoversFullViewport());
EXPECT_SCOPED(TestEmptyLayer());
EXPECT_SCOPED(TestLayerInMiddleOfViewport());
EXPECT_SCOPED(TestLayerIsLargerThanViewport());
}
TEST_F(LayerTreeHostImplViewportCoveredTest, ViewportCoveredScaled) {
viewport_size_ = gfx::Size(1000, 1000);
bool software = false;
CreateHostImpl(DefaultSettings(), CreateFakeLayerTreeFrameSink(software));
host_impl_->active_tree()->SetDeviceScaleFactor(2);
SetupActiveTreeLayers();
EXPECT_SCOPED(TestLayerCoversFullViewport());
EXPECT_SCOPED(TestEmptyLayer());
EXPECT_SCOPED(TestLayerInMiddleOfViewport());
EXPECT_SCOPED(TestLayerIsLargerThanViewport());
}
TEST_F(LayerTreeHostImplViewportCoveredTest, ActiveTreeGrowViewportInvalid) {
viewport_size_ = gfx::Size(1000, 1000);
bool software = true;
CreateHostImpl(DefaultSettings(), CreateFakeLayerTreeFrameSink(software));
CreatePendingTree();
SetupActiveTreeLayers();
EXPECT_SCOPED(TestEmptyLayerWithOnDraw());
EXPECT_SCOPED(TestLayerInMiddleOfViewportWithOnDraw());
EXPECT_SCOPED(TestLayerIsLargerThanViewportWithOnDraw());
}
TEST_F(LayerTreeHostImplViewportCoveredTest, ActiveTreeShrinkViewportInvalid) {
viewport_size_ = gfx::Size(1000, 1000);
bool software = true;
CreateHostImpl(DefaultSettings(), CreateFakeLayerTreeFrameSink(software));
CreatePendingTree();
gfx::Size larger_viewport(viewport_size_.width() + 100,
viewport_size_.height() + 100);
host_impl_->active_tree()->SetDeviceViewportRect(
gfx::Rect(DipSizeToPixelSize(larger_viewport)));
host_impl_->ActivateSyncTree();
EXPECT_TRUE(did_activate_pending_tree_);
CreatePendingTree();
host_impl_->active_tree()->SetDeviceViewportRect(
gfx::Rect(DipSizeToPixelSize(viewport_size_)));
SetupActiveTreeLayers();
EXPECT_SCOPED(TestEmptyLayerWithOnDraw());
EXPECT_SCOPED(TestLayerInMiddleOfViewportWithOnDraw());
EXPECT_SCOPED(TestLayerIsLargerThanViewportWithOnDraw());
}
class FakeDrawableLayerImpl : public LayerImpl {
public:
static std::unique_ptr<LayerImpl> Create(LayerTreeImpl* tree_impl, int id) {
return base::WrapUnique(new FakeDrawableLayerImpl(tree_impl, id));
}
protected:
FakeDrawableLayerImpl(LayerTreeImpl* tree_impl, int id)
: LayerImpl(tree_impl, id) {}
};
TEST_P(CompositorFrameProducingLayerTreeHostImplTest,
PartialSwapReceivesDamageRect) {
std::unique_ptr<FakeLayerTreeFrameSink> layer_tree_frame_sink =
FakeLayerTreeFrameSink::Create3d();
FakeLayerTreeFrameSink* fake_layer_tree_frame_sink =
layer_tree_frame_sink.get();
LayerTreeSettings settings = DefaultSettings();
std::unique_ptr<LayerTreeHostImpl> layer_tree_host_impl =
LayerTreeHostImpl::Create(
settings, this, &task_runner_provider_, &stats_instrumentation_,
&task_graph_runner_,
AnimationHost::CreateForTesting(ThreadInstance::kImpl), nullptr, 0,
nullptr, nullptr);
if (layer_tree_host_impl->settings().trees_in_viz_in_viz_process) {
layer_tree_host_impl->set_next_frame_token_from_client(1u);
}
layer_tree_host_impl->SetVisible(true);
layer_tree_host_impl->InitializeFrameSink(layer_tree_frame_sink.get());
LayerImpl* root = SetupRootLayer<LayerImpl>(
layer_tree_host_impl->active_tree(), gfx::Size(500, 500));
LayerImpl* child = AddLayer<LayerImpl>(layer_tree_host_impl->active_tree());
child->SetBounds(gfx::Size(14, 15));
child->SetDrawsContent(true);
CopyProperties(root, child);
child->SetOffsetToTransformParent(gfx::Vector2dF(12, 13));
layer_tree_host_impl->active_tree()->SetLocalSurfaceIdFromParent(
viz::LocalSurfaceId(1, base::UnguessableToken::CreateForTesting(2u, 3u)));
UpdateDrawProperties(layer_tree_host_impl->active_tree());
TestFrameData frame;
auto args = viz::CreateBeginFrameArgsForTesting(
BEGINFRAME_FROM_HERE, viz::BeginFrameArgs::kManualSourceId, 1,
base::TimeTicks() + base::Milliseconds(1));
layer_tree_host_impl->WillBeginImplFrame(args);
EXPECT_EQ(DrawResult::kSuccess, layer_tree_host_impl->PrepareToDraw(&frame));
layer_tree_host_impl->DrawLayers(&frame);
layer_tree_host_impl->DidDrawAllLayers(frame);
layer_tree_host_impl->DidFinishImplFrame(args);
gfx::Rect expected_swap_rect(500, 500);
EXPECT_EQ(expected_swap_rect, fake_layer_tree_frame_sink->last_swap_rect());
child->SetOffsetToTransformParent(gfx::Vector2dF());
child->NoteLayerPropertyChanged();
args = viz::CreateBeginFrameArgsForTesting(
BEGINFRAME_FROM_HERE, viz::BeginFrameArgs::kManualSourceId, 1,
base::TimeTicks() + base::Milliseconds(1));
layer_tree_host_impl->WillBeginImplFrame(args);
EXPECT_EQ(DrawResult::kSuccess, layer_tree_host_impl->PrepareToDraw(&frame));
layer_tree_host_impl->DrawLayers(&frame);
layer_tree_host_impl->DidDrawAllLayers(frame);
layer_tree_host_impl->DidFinishImplFrame(args);
expected_swap_rect = gfx::Rect(26, 28);
EXPECT_EQ(expected_swap_rect, fake_layer_tree_frame_sink->last_swap_rect());
layer_tree_host_impl->active_tree()->SetDeviceViewportRect(gfx::Rect(10, 10));
root->SetBackgroundColor(SkColors::kBlack);
args = viz::CreateBeginFrameArgsForTesting(
BEGINFRAME_FROM_HERE, viz::BeginFrameArgs::kManualSourceId, 1,
base::TimeTicks() + base::Milliseconds(1));
layer_tree_host_impl->WillBeginImplFrame(args);
EXPECT_EQ(DrawResult::kSuccess, layer_tree_host_impl->PrepareToDraw(&frame));
layer_tree_host_impl->DrawLayers(&frame);
layer_tree_host_impl->DidDrawAllLayers(frame);
layer_tree_host_impl->DidFinishImplFrame(args);
expected_swap_rect = gfx::Rect(10, 10);
EXPECT_EQ(expected_swap_rect, fake_layer_tree_frame_sink->last_swap_rect());
layer_tree_host_impl->ReleaseLayerTreeFrameSink();
}
TEST_P(LayerTreeHostImplTest, RootLayerDoesntCreateExtraSurface) {
LayerImpl* root = SetupDefaultRootLayer(gfx::Size(10, 10));
LayerImpl* child = AddLayerInActiveTree();
child->SetBounds(gfx::Size(10, 10));
child->SetDrawsContent(true);
root->SetBounds(gfx::Size(10, 10));
root->SetDrawsContent(true);
CopyProperties(root, child);
UpdateDrawProperties(host_impl_->active_tree());
TestFrameData frame;
EXPECT_EQ(DrawResult::kSuccess, host_impl_->PrepareToDraw(&frame));
EXPECT_EQ(1u, frame.render_surface_list->size());
EXPECT_EQ(1u, frame.render_passes.size());
host_impl_->DidDrawAllLayers(frame);
}
class FakeLayerWithQuads : public LayerImpl {
public:
static std::unique_ptr<LayerImpl> Create(LayerTreeImpl* tree_impl, int id) {
return base::WrapUnique(new FakeLayerWithQuads(tree_impl, id));
}
void AppendQuads(const AppendQuadsContext& context,
viz::CompositorRenderPass* render_pass,
AppendQuadsData* append_quads_data) override {
viz::SharedQuadState* shared_quad_state =
render_pass->CreateAndAppendSharedQuadState();
PopulateSharedQuadState(shared_quad_state, contents_opaque());
SkColor4f gray = SkColors::kGray;
gfx::Rect quad_rect(bounds());
gfx::Rect visible_quad_rect(quad_rect);
auto* my_quad =
render_pass->CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>();
my_quad->SetNew(shared_quad_state, quad_rect, visible_quad_rect, gray,
false);
}
private:
FakeLayerWithQuads(LayerTreeImpl* tree_impl, int id)
: LayerImpl(tree_impl, id) {}
};
TEST_P(LayerTreeHostImplTest, LayersFreeTextures) {
scoped_refptr<viz::TestContextProvider> context_provider =
viz::TestContextProvider::CreateRaster();
gpu::TestSharedImageInterface* sii = context_provider->SharedImageInterface();
std::unique_ptr<LayerTreeFrameSink> layer_tree_frame_sink(
FakeLayerTreeFrameSink::Create3d(context_provider));
CreateHostImpl(DefaultSettings(), std::move(layer_tree_frame_sink));
LayerImpl* root_layer = SetupDefaultRootLayer(gfx::Size(10, 10));
scoped_refptr<VideoFrame> softwareFrame = media::VideoFrame::CreateColorFrame(
gfx::Size(4, 4), 0x80, 0x80, 0x80, base::TimeDelta());
FakeVideoFrameProvider provider;
provider.set_frame(softwareFrame);
auto* video_layer = AddLayer<VideoLayerImpl>(
host_impl_->active_tree(), &provider, media::VIDEO_ROTATION_0);
video_layer->SetBounds(gfx::Size(10, 10));
video_layer->SetDrawsContent(true);
CopyProperties(root_layer, video_layer);
UpdateDrawProperties(host_impl_->active_tree());
EXPECT_EQ(0u, sii->shared_image_count());
DrawFrame();
EXPECT_GT(sii->shared_image_count(), 0u);
ClearLayersAndPropertyTrees(host_impl_->active_tree());
auto* fake_sink =
static_cast<FakeLayerTreeFrameSink*>(host_impl_->layer_tree_frame_sink());
fake_sink->ReturnResourcesHeldByParent();
if (fake_sink->last_sent_frame()) {
fake_sink->last_sent_frame()->resource_list.clear();
}
EXPECT_EQ(0u, sii->shared_image_count());
}
TEST_P(LayerTreeHostImplTest, HasTransparentBackground) {
SetupDefaultRootLayer(gfx::Size(10, 10));
host_impl_->active_tree()->set_background_color(SkColors::kWhite);
UpdateDrawProperties(host_impl_->active_tree());
TestFrameData frame;
auto args = viz::CreateBeginFrameArgsForTesting(
BEGINFRAME_FROM_HERE, viz::BeginFrameArgs::kManualSourceId, 1,
base::TimeTicks() + base::Milliseconds(1));
host_impl_->WillBeginImplFrame(args);
EXPECT_EQ(DrawResult::kSuccess, host_impl_->PrepareToDraw(&frame));
{
const auto& root_pass = frame.render_passes.back();
ASSERT_EQ(1u, root_pass->quad_list.size());
EXPECT_EQ(viz::DrawQuad::Material::kSolidColor,
root_pass->quad_list.front()->material);
}
host_impl_->DrawLayers(&frame);
host_impl_->DidDrawAllLayers(frame);
host_impl_->DidFinishImplFrame(args);
host_impl_->SetFullViewportDamage();
host_impl_->active_tree()->set_background_color(SkColors::kTransparent);
host_impl_->SetFullViewportDamage();
args = viz::CreateBeginFrameArgsForTesting(
BEGINFRAME_FROM_HERE, viz::BeginFrameArgs::kManualSourceId, 1,
base::TimeTicks() + base::Milliseconds(1));
host_impl_->WillBeginImplFrame(args);
EXPECT_EQ(DrawResult::kSuccess, host_impl_->PrepareToDraw(&frame));
{
const auto& root_pass = frame.render_passes.back();
ASSERT_EQ(0u, root_pass->quad_list.size());
}
host_impl_->DrawLayers(&frame);
host_impl_->DidDrawAllLayers(frame);
host_impl_->DidFinishImplFrame(args);
host_impl_->SetFullViewportDamage();
host_impl_->active_tree()->set_background_color({1.0f, 0.0f, 0.0f, 0.1f});
host_impl_->SetFullViewportDamage();
host_impl_->WillBeginImplFrame(viz::CreateBeginFrameArgsForTesting(
BEGINFRAME_FROM_HERE, viz::BeginFrameArgs::kManualSourceId, 1,
base::TimeTicks() + base::Milliseconds(1)));
EXPECT_EQ(DrawResult::kSuccess, host_impl_->PrepareToDraw(&frame));
{
const auto& root_pass = frame.render_passes.back();
ASSERT_EQ(0u, root_pass->quad_list.size());
}
host_impl_->DrawLayers(&frame);
host_impl_->DidDrawAllLayers(frame);
host_impl_->DidFinishImplFrame(args);
}
class LayerTreeHostImplTestDrawAndTestDamage : public LayerTreeHostImplTest {
protected:
std::unique_ptr<LayerTreeFrameSink> CreateLayerTreeFrameSink() override {
return FakeLayerTreeFrameSink::Create3d();
}
void DrawFrameAndTestDamage(const gfx::Rect& expected_damage,
const LayerImpl* child) {
bool expect_to_draw = !expected_damage.IsEmpty();
TestFrameData frame;
auto args = viz::CreateBeginFrameArgsForTesting(
BEGINFRAME_FROM_HERE, viz::BeginFrameArgs::kManualSourceId, 1,
base::TimeTicks() + base::Milliseconds(1));
host_impl_->WillBeginImplFrame(args);
EXPECT_EQ(DrawResult::kSuccess, host_impl_->PrepareToDraw(&frame));
if (!expect_to_draw) {
ASSERT_EQ(0u, frame.render_passes.size());
} else {
ASSERT_EQ(1u, frame.render_passes.size());
const viz::CompositorRenderPass* root_render_pass =
frame.render_passes.back().get();
EXPECT_EQ(expected_damage, root_render_pass->damage_rect);
ASSERT_EQ(2u, root_render_pass->quad_list.size());
gfx::Rect expected_child_visible_rect(child->bounds());
EXPECT_EQ(expected_child_visible_rect,
root_render_pass->quad_list.front()->visible_rect);
LayerImpl* root = root_layer();
gfx::Rect expected_root_visible_rect(root->bounds());
EXPECT_EQ(expected_root_visible_rect,
root_render_pass->quad_list.ElementAt(1)->visible_rect);
}
EXPECT_EQ(expect_to_draw, host_impl_->DrawLayers(&frame).has_value());
host_impl_->DidDrawAllLayers(frame);
host_impl_->DidFinishImplFrame(args);
}
};
INSTANTIATE_COMPOSITOR_FRAME_PRODUCING_TREE_TEST_P(
LayerTreeHostImplTestDrawAndTestDamage);
TEST_P(LayerTreeHostImplTestDrawAndTestDamage, FrameIncludesDamageRect) {
auto* root = SetupRootLayer<SolidColorLayerImpl>(host_impl_->active_tree(),
gfx::Size(10, 10));
root->SetDrawsContent(true);
root->SetBackgroundColor(SkColors::kRed);
auto* child = AddLayer<SolidColorLayerImpl>(host_impl_->active_tree());
child->SetBounds(gfx::Size(1, 1));
child->SetDrawsContent(true);
child->SetBackgroundColor(SkColors::kRed);
CopyProperties(root, child);
child->SetOffsetToTransformParent(gfx::Vector2dF(9, 9));
UpdateDrawProperties(host_impl_->active_tree());
gfx::Rect full_frame_damage(
host_impl_->active_tree()->GetDeviceViewport().size());
DrawFrameAndTestDamage(full_frame_damage, child);
gfx::Rect small_damage = gfx::Rect(0, 0, 1, 1);
root->UnionUpdateRect(small_damage);
DrawFrameAndTestDamage(small_damage, child);
gfx::Rect no_damage;
DrawFrameAndTestDamage(no_damage, child);
}
TEST_F(CommitToPendingTreeLayerTreeHostImplTest, FarAwayQuadsDontNeedAA) {
float device_scale_factor = 4 / 3;
gfx::Size root_size(2000, 1000);
CreatePendingTree();
host_impl_->pending_tree()->SetDeviceScaleFactor(device_scale_factor);
host_impl_->pending_tree()->PushPageScaleFromMainThread(1, 1 / 16, 16);
auto* root = SetupRootLayer<LayerImpl>(host_impl_->pending_tree(), root_size);
root->SetNeedsPushProperties();
gfx::Size content_layer_bounds(100001, 100);
auto* scrolling_layer =
AddScrollableLayer(root, content_layer_bounds, gfx::Size());
scrolling_layer->SetNeedsPushProperties();
scoped_refptr<FakeRasterSource> raster_source(
FakeRasterSource::CreateFilled(content_layer_bounds));
auto* content_layer =
AddLayer<FakePictureLayerImpl>(host_impl_->pending_tree(), raster_source);
CopyProperties(scrolling_layer, content_layer);
content_layer->SetBounds(content_layer_bounds);
content_layer->SetDrawsContent(true);
content_layer->SetNeedsPushProperties();
UpdateDrawProperties(host_impl_->pending_tree());
gfx::PointF scroll_offset(100000, 0);
scrolling_layer->layer_tree_impl()
->property_trees()
->scroll_tree_mutable()
.UpdateScrollOffsetBaseForTesting(scrolling_layer->element_id(),
scroll_offset);
host_impl_->ActivateSyncTree();
UpdateDrawProperties(host_impl_->active_tree());
ASSERT_EQ(1u, host_impl_->active_tree()->GetRenderSurfaceList().size());
TestFrameData frame;
auto args = viz::CreateBeginFrameArgsForTesting(
BEGINFRAME_FROM_HERE, viz::BeginFrameArgs::kManualSourceId, 1,
base::TimeTicks() + base::Milliseconds(1));
host_impl_->WillBeginImplFrame(args);
EXPECT_EQ(DrawResult::kSuccess, host_impl_->PrepareToDraw(&frame));
ASSERT_EQ(1u, frame.render_passes.size());
ASSERT_LE(1u, frame.render_passes[0]->quad_list.size());
const viz::DrawQuad* quad = frame.render_passes[0]->quad_list.front();
bool clipped = false;
MathUtil::MapQuad(
quad->shared_quad_state->quad_to_target_transform,
gfx::QuadF(gfx::RectF(quad->shared_quad_state->visible_quad_layer_rect)),
&clipped);
EXPECT_FALSE(clipped);
host_impl_->DrawLayers(&frame);
host_impl_->DidDrawAllLayers(frame);
host_impl_->DidFinishImplFrame(args);
}
class CompositorFrameMetadataTest : public LayerTreeHostImplTest {
public:
CompositorFrameMetadataTest() = default;
void DidReceiveCompositorFrameAckOnImplThread() override { acks_received_++; }
int acks_received_ = 0;
};
INSTANTIATE_COMMIT_TO_TREE_TEST_P(CompositorFrameMetadataTest);
TEST_P(CompositorFrameMetadataTest, CompositorFrameAckCountsAsSwapComplete) {
SetupRootLayer<FakeLayerWithQuads>(host_impl_->active_tree(),
gfx::Size(10, 10));
UpdateDrawProperties(host_impl_->active_tree());
DrawFrame();
host_impl_->ReclaimResources(std::vector<viz::ReturnedResource>());
host_impl_->DidReceiveCompositorFrameAck();
EXPECT_EQ(acks_received_, 1);
}
class CountingSoftwareDevice : public viz::SoftwareOutputDevice {
public:
CountingSoftwareDevice() : frames_began_(0), frames_ended_(0) {}
SkCanvas* BeginPaint(const gfx::Rect& damage_rect) override {
++frames_began_;
return viz::SoftwareOutputDevice::BeginPaint(damage_rect);
}
void EndPaint() override {
viz::SoftwareOutputDevice::EndPaint();
++frames_ended_;
}
int frames_began_, frames_ended_;
};
TEST_P(LayerTreeHostImplTest,
ForcedDrawToSoftwareDeviceSkipsUnsupportedLayers) {
set_reduce_memory_result(false);
EXPECT_TRUE(CreateHostImpl(DefaultSettings(),
FakeLayerTreeFrameSink::CreateSoftware()));
const gfx::Transform external_transform;
const gfx::Rect external_viewport;
const bool resourceless_software_draw = true;
host_impl_->SetExternalTilePriorityConstraints(external_viewport,
external_transform);
auto* root = SetupRootLayer<SolidColorLayerImpl>(host_impl_->active_tree(),
gfx::Size(10, 10));
root->SetDrawsContent(true);
FakeVideoFrameProvider provider;
LayerImpl* video_layer = AddLayer<VideoLayerImpl>(
host_impl_->active_tree(), &provider, media::VIDEO_ROTATION_0);
video_layer->SetBounds(gfx::Size(10, 10));
video_layer->SetDrawsContent(true);
CopyProperties(root, video_layer);
UpdateDrawProperties(host_impl_->active_tree());
host_impl_->OnDraw(external_transform, external_viewport,
resourceless_software_draw, false);
EXPECT_EQ(1u, last_on_draw_frame_->will_draw_layers.size());
EXPECT_EQ(host_impl_->active_tree()->root_layer(),
last_on_draw_frame_->will_draw_layers[0]);
}
TEST_P(ClientModeLayerTreeHostImplTest, MemoryLimits) {
host_impl_->ReleaseLayerTreeFrameSink();
host_impl_ = nullptr;
const size_t kGpuByteLimit = 1234321;
const size_t kGpuResourceLimit = 2345432;
const gpu::MemoryAllocation::PriorityCutoff kGpuCutoff =
gpu::MemoryAllocation::CUTOFF_ALLOW_EVERYTHING;
const TileMemoryLimitPolicy kGpuTileCutoff =
ManagedMemoryPolicy::PriorityCutoffToTileMemoryLimitPolicy(kGpuCutoff);
const TileMemoryLimitPolicy kNothingTileCutoff =
ManagedMemoryPolicy::PriorityCutoffToTileMemoryLimitPolicy(
gpu::MemoryAllocation::CUTOFF_ALLOW_NOTHING);
EXPECT_NE(kGpuTileCutoff, kNothingTileCutoff);
LayerTreeSettings settings = DefaultSettings();
settings.memory_policy =
ManagedMemoryPolicy(kGpuByteLimit, kGpuCutoff, kGpuResourceLimit);
host_impl_ = LayerTreeHostImpl::Create(
settings, this, &task_runner_provider_, &stats_instrumentation_,
&task_graph_runner_,
AnimationHost::CreateForTesting(ThreadInstance::kImpl), nullptr, 0,
nullptr, nullptr);
InputHandler::Create(static_cast<CompositorDelegateForInput&>(*host_impl_));
layer_tree_frame_sink_ = FakeLayerTreeFrameSink::Create3d();
host_impl_->SetVisible(true);
host_impl_->InitializeFrameSink(layer_tree_frame_sink_.get());
{
const auto& state = host_impl_->global_tile_state();
EXPECT_EQ(kGpuByteLimit, state.hard_memory_limit_in_bytes);
EXPECT_EQ(kGpuResourceLimit, state.num_resources_limit);
EXPECT_EQ(kGpuTileCutoff, state.memory_limit_policy);
}
host_impl_->SetVisible(false);
{
const auto& state = host_impl_->global_tile_state();
EXPECT_EQ(0u, state.hard_memory_limit_in_bytes);
EXPECT_EQ(kGpuResourceLimit, state.num_resources_limit);
EXPECT_EQ(kNothingTileCutoff, state.memory_limit_policy);
}
host_impl_->SetVisible(true);
{
const auto& state = host_impl_->global_tile_state();
EXPECT_EQ(kGpuByteLimit, state.hard_memory_limit_in_bytes);
EXPECT_EQ(kGpuResourceLimit, state.num_resources_limit);
}
host_impl_->ReleaseLayerTreeFrameSink();
layer_tree_frame_sink_ = FakeLayerTreeFrameSink::CreateSoftware();
host_impl_->InitializeFrameSink(layer_tree_frame_sink_.get());
{
const auto& state = host_impl_->global_tile_state();
EXPECT_EQ(kGpuByteLimit, state.hard_memory_limit_in_bytes);
EXPECT_EQ(kGpuResourceLimit, state.num_resources_limit);
EXPECT_EQ(kGpuTileCutoff, state.memory_limit_policy);
}
host_impl_->SetVisible(false);
{
const auto& state = host_impl_->global_tile_state();
EXPECT_EQ(0u, state.hard_memory_limit_in_bytes);
EXPECT_EQ(kGpuResourceLimit, state.num_resources_limit);
EXPECT_EQ(kNothingTileCutoff, state.memory_limit_policy);
}
host_impl_->SetVisible(true);
{
const auto& state = host_impl_->global_tile_state();
EXPECT_EQ(kGpuByteLimit, state.hard_memory_limit_in_bytes);
EXPECT_EQ(kGpuResourceLimit, state.num_resources_limit);
EXPECT_EQ(kGpuTileCutoff, state.memory_limit_policy);
}
}
namespace {
void ExpectFullDamageAndDraw(LayerTreeHostImpl* host_impl) {
gfx::Rect full_frame_damage(
host_impl->active_tree()->GetDeviceViewport().size());
TestFrameData frame;
auto args = viz::CreateBeginFrameArgsForTesting(
BEGINFRAME_FROM_HERE, viz::BeginFrameArgs::kManualSourceId, 1,
base::TimeTicks() + base::Milliseconds(1));
host_impl->WillBeginImplFrame(args);
EXPECT_EQ(DrawResult::kSuccess, host_impl->PrepareToDraw(&frame));
ASSERT_EQ(1u, frame.render_passes.size());
const viz::CompositorRenderPass* root_render_pass =
frame.render_passes.back().get();
EXPECT_EQ(full_frame_damage, root_render_pass->damage_rect);
EXPECT_TRUE(host_impl->DrawLayers(&frame));
host_impl->DidDrawAllLayers(frame);
host_impl->DidFinishImplFrame(args);
}
}
TEST_P(LayerTreeHostImplTestDrawAndTestDamage,
RequireHighResAndRedrawWhenVisible) {
ASSERT_TRUE(host_impl_->active_tree());
LayerImpl* root = SetupRootLayer<SolidColorLayerImpl>(
host_impl_->active_tree(), gfx::Size(10, 10));
root->SetBackgroundColor(SkColors::kRed);
UpdateDrawProperties(host_impl_->active_tree());
EXPECT_TRUE(host_impl_->RequiresHighResToDraw());
EXPECT_SCOPED(ExpectFullDamageAndDraw(host_impl_.get()));
host_impl_->ResetRequiresHighResToDraw();
host_impl_->SetVisible(false);
EXPECT_FALSE(host_impl_->RequiresHighResToDraw());
host_impl_->SetVisible(true);
EXPECT_TRUE(host_impl_->RequiresHighResToDraw());
host_impl_->SetVisible(false);
EXPECT_TRUE(host_impl_->RequiresHighResToDraw());
host_impl_->ResetRequiresHighResToDraw();
EXPECT_FALSE(host_impl_->RequiresHighResToDraw());
did_request_redraw_ = false;
host_impl_->SetVisible(true);
EXPECT_TRUE(host_impl_->RequiresHighResToDraw());
EXPECT_TRUE(did_request_redraw_);
EXPECT_SCOPED(ExpectFullDamageAndDraw(host_impl_.get()));
}
class LayerTreeHostImplTestPrepareTiles : public LayerTreeHostImplTest {
public:
void SetUp() override {
fake_host_impl_ = new FakeLayerTreeHostImpl(
LayerTreeSettings(), &task_runner_provider_, &task_graph_runner_);
host_impl_.reset(fake_host_impl_);
layer_tree_frame_sink_ = CreateLayerTreeFrameSink();
host_impl_->SetVisible(true);
host_impl_->InitializeFrameSink(layer_tree_frame_sink_.get());
host_impl_->active_tree()->SetDeviceViewportRect(gfx::Rect(10, 10));
}
raw_ptr<FakeLayerTreeHostImpl> fake_host_impl_;
};
INSTANTIATE_COMMIT_TO_TREE_TEST_P(LayerTreeHostImplTestPrepareTiles);
TEST_P(LayerTreeHostImplTestPrepareTiles, PrepareTilesWhenInvisible) {
EXPECT_TRUE(fake_host_impl_->prepare_tiles_needed());
host_impl_->SetVisible(false);
EXPECT_FALSE(fake_host_impl_->prepare_tiles_needed());
host_impl_->SetVisible(true);
EXPECT_TRUE(fake_host_impl_->prepare_tiles_needed());
}
TEST_P(ClientModeLayerTreeHostImplTest, UIResourceManagement) {
auto test_context_provider = viz::TestContextProvider::CreateRaster();
gpu::TestSharedImageInterface* sii =
test_context_provider->SharedImageInterface();
CreateHostImpl(DefaultSettings(), FakeLayerTreeFrameSink::Create3d(
std::move(test_context_provider)));
EXPECT_EQ(0u, sii->shared_image_count());
UIResourceId ui_resource_id = 1;
bool is_opaque = false;
UIResourceBitmap bitmap(gfx::Size(1, 1), is_opaque);
host_impl_->CreateUIResource(ui_resource_id, bitmap);
EXPECT_EQ(1u, sii->shared_image_count());
viz::ResourceId id1 = host_impl_->ResourceIdForUIResource(ui_resource_id);
EXPECT_NE(viz::kInvalidResourceId, id1);
host_impl_->CreateUIResource(ui_resource_id, bitmap);
EXPECT_EQ(1u, sii->shared_image_count());
viz::ResourceId id2 = host_impl_->ResourceIdForUIResource(ui_resource_id);
EXPECT_NE(viz::kInvalidResourceId, id2);
EXPECT_NE(id1, id2);
host_impl_->DeleteUIResource(-1);
EXPECT_EQ(1u, sii->shared_image_count());
EXPECT_EQ(viz::kInvalidResourceId, host_impl_->ResourceIdForUIResource(-1));
EXPECT_EQ(1u, sii->shared_image_count());
host_impl_->DeleteUIResource(ui_resource_id);
EXPECT_EQ(viz::kInvalidResourceId,
host_impl_->ResourceIdForUIResource(ui_resource_id));
EXPECT_EQ(0u, sii->shared_image_count());
host_impl_->DeleteUIResource(ui_resource_id);
EXPECT_EQ(0u, sii->shared_image_count());
}
TEST_P(ClientModeLayerTreeHostImplTest, CreateETC1UIResource) {
auto test_context_provider = viz::TestContextProvider::CreateRaster();
gpu::TestSharedImageInterface* sii =
test_context_provider->SharedImageInterface();
CreateHostImpl(DefaultSettings(), FakeLayerTreeFrameSink::Create3d(
std::move(test_context_provider)));
EXPECT_EQ(0u, sii->shared_image_count());
gfx::Size size(4, 4);
SkImageInfo info =
SkImageInfo::Make(4, 2, kAlpha_8_SkColorType, kPremul_SkAlphaType);
sk_sp<SkPixelRef> pixel_ref(SkMallocPixelRef::MakeAllocate(info, 0));
pixel_ref->setImmutable();
UIResourceBitmap bitmap(std::move(pixel_ref), size);
UIResourceId ui_resource_id = 1;
host_impl_->CreateUIResource(ui_resource_id, bitmap);
EXPECT_EQ(1u, sii->shared_image_count());
viz::ResourceId id1 = host_impl_->ResourceIdForUIResource(ui_resource_id);
EXPECT_NE(viz::kInvalidResourceId, id1);
}
TEST_P(LayerTreeHostImplTest, ScrollHitTestIsNotReliable) {
gfx::Size viewport_size(50, 50);
gfx::Size content_size(100, 100);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
LayerImpl* occluder_layer = AddLayerInActiveTree();
occluder_layer->SetDrawsContent(true);
occluder_layer->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
occluder_layer->SetBounds(content_size);
CopyProperties(root_layer(), occluder_layer);
occluder_layer->SetTransformTreeIndex(
host_impl_->active_tree()->PageScaleTransformNode()->id);
DrawFrame();
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest,
status.main_thread_hit_test_reasons);
}
TEST_P(LayerTreeHostImplTest, ScrollHitTestAncestorMismatch) {
gfx::Size viewport_size(50, 50);
gfx::Size content_size(100, 100);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
LayerImpl* scroll_layer = OuterViewportScrollLayer();
LayerImpl* child_scroll_clip = AddLayerInActiveTree();
CopyProperties(scroll_layer, child_scroll_clip);
LayerImpl* child_scroll =
AddScrollableLayer(child_scroll_clip, viewport_size, content_size);
child_scroll->SetOffsetToTransformParent(gfx::Vector2dF(10, 10));
LayerImpl* occluder_layer = AddLayerInActiveTree();
occluder_layer->SetDrawsContent(true);
occluder_layer->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
occluder_layer->SetBounds(content_size);
CopyProperties(child_scroll, occluder_layer);
occluder_layer->SetOffsetToTransformParent(gfx::Vector2dF(-10, -10));
DrawFrame();
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest,
status.main_thread_hit_test_reasons);
}
TEST_P(LayerTreeHostImplTest, ScrollInvisibleScroller) {
gfx::Size viewport_size(50, 50);
gfx::Size content_size(100, 100);
SetupViewportLayersInnerScrolls(viewport_size, content_size);
LayerImpl* scroll_layer = InnerViewportScrollLayer();
LayerImpl* child_scroll =
AddScrollableLayer(scroll_layer, viewport_size, content_size);
child_scroll->SetDrawsContent(false);
DrawFrame();
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
EXPECT_EQ(child_scroll->scroll_tree_index(),
host_impl_->CurrentlyScrollingNode()->id);
}
TEST_F(CommitToPendingTreeLayerTreeHostImplTest,
LatencyInfoPassedToCompositorFrameMetadata) {
CreateHostImpl(DefaultSettings(), CreateLayerTreeFrameSink());
SetupRootLayer<SolidColorLayerImpl>(host_impl_->active_tree(),
gfx::Size(10, 10));
UpdateDrawProperties(host_impl_->active_tree());
auto* fake_layer_tree_frame_sink =
static_cast<FakeLayerTreeFrameSink*>(host_impl_->layer_tree_frame_sink());
ui::LatencyInfo latency_info;
latency_info.set_trace_id(5);
latency_info.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT);
std::unique_ptr<SwapPromise> swap_promise(
std::make_unique<LatencyInfoSwapPromise>(latency_info));
host_impl_->active_tree()->QueuePinnedSwapPromise(std::move(swap_promise));
host_impl_->SetFullViewportDamage();
host_impl_->SetNeedsRedraw(false,
false);
DrawFrame();
const auto& metadata_latency_after =
fake_layer_tree_frame_sink->last_sent_frame()->metadata.latency_info;
EXPECT_EQ(1u, metadata_latency_after.size());
EXPECT_TRUE(metadata_latency_after[0].FindLatency(
ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, nullptr));
}
TEST_F(CommitToPendingTreeLayerTreeHostImplTest,
CompositorFrameMetadataFrameIntervalInputs) {
CreateHostImpl(DefaultSettings(), CreateLayerTreeFrameSink());
SetupRootLayer<SolidColorLayerImpl>(host_impl_->active_tree(),
gfx::Size(10, 10));
UpdateDrawProperties(host_impl_->active_tree());
auto* fake_layer_tree_frame_sink =
static_cast<FakeLayerTreeFrameSink*>(host_impl_->layer_tree_frame_sink());
host_impl_->NotifyInputEvent(false);
host_impl_->SetFullViewportDamage();
host_impl_->SetNeedsRedraw(false,
false);
auto args = viz::CreateBeginFrameArgsForTesting(
BEGINFRAME_FROM_HERE, viz::BeginFrameArgs::kManualSourceId, 1,
base::TimeTicks() + base::Milliseconds(1234));
DrawFrameWithArgs(args);
const auto& frame_interval_inputs =
fake_layer_tree_frame_sink->last_sent_frame()
->metadata.frame_interval_inputs;
EXPECT_TRUE(frame_interval_inputs.has_input);
EXPECT_EQ(args.frame_time, frame_interval_inputs.frame_time);
}
#if BUILDFLAG(IS_ANDROID)
TEST_P(LayerTreeHostImplTest, SelectionBoundsPassedToCompositorFrameMetadata) {
LayerImpl* root = SetupRootLayer<SolidColorLayerImpl>(
host_impl_->active_tree(), gfx::Size(10, 10));
UpdateDrawProperties(host_impl_->active_tree());
gfx::Point selection_start(5, 0);
gfx::Point selection_end(5, 5);
LayerSelection selection;
selection.start.type = gfx::SelectionBound::CENTER;
selection.start.layer_id = root->id();
selection.start.edge_end = selection_end;
selection.start.edge_start = selection_start;
selection.end = selection.start;
host_impl_->active_tree()->RegisterSelection(selection);
host_impl_->SetNeedsRedraw(false,
false);
RenderFrameMetadata metadata = StartDrawAndProduceRenderFrameMetadata();
const viz::Selection<gfx::SelectionBound>& selection_after =
metadata.selection;
EXPECT_EQ(selection.start.type, selection_after.start.type());
EXPECT_EQ(selection.end.type, selection_after.end.type());
EXPECT_EQ(gfx::PointF(selection_end), selection_after.start.edge_end());
EXPECT_EQ(gfx::PointF(selection_start), selection_after.start.edge_start());
EXPECT_TRUE(selection_after.start.visible());
EXPECT_TRUE(selection_after.end.visible());
}
TEST_P(LayerTreeHostImplTest, HiddenSelectionBoundsStayHidden) {
LayerImpl* root = SetupDefaultRootLayer(gfx::Size(10, 10));
UpdateDrawProperties(host_impl_->active_tree());
gfx::Point selection_start(5, 0);
gfx::Point selection_end(5, 5);
LayerSelection selection;
selection.start.hidden = true;
selection.start.type = gfx::SelectionBound::CENTER;
selection.start.layer_id = root->id();
selection.start.edge_end = selection_end;
selection.start.edge_start = selection_start;
selection.end = selection.start;
host_impl_->active_tree()->RegisterSelection(selection);
host_impl_->SetNeedsRedraw(false,
false);
RenderFrameMetadata metadata = StartDrawAndProduceRenderFrameMetadata();
const viz::Selection<gfx::SelectionBound>& selection_after =
metadata.selection;
EXPECT_EQ(selection.start.type, selection_after.start.type());
EXPECT_EQ(selection.end.type, selection_after.end.type());
EXPECT_EQ(gfx::PointF(selection_end), selection_after.start.edge_end());
EXPECT_EQ(gfx::PointF(selection_start), selection_after.start.edge_start());
EXPECT_FALSE(selection_after.start.visible());
EXPECT_FALSE(selection_after.end.visible());
}
#endif
TEST_P(LayerTreeHostImplTest, SimpleSwapPromiseMonitor) {
{
StrictMock<MockLatencyInfoSwapPromiseMonitor> monitor(host_impl_.get());
EXPECT_CALL(monitor, OnSetNeedsCommitOnMain()).Times(0);
EXPECT_CALL(monitor, OnSetNeedsRedrawOnImpl()).Times(1);
host_impl_->SetNeedsRedraw(false,
false);
}
{
StrictMock<MockLatencyInfoSwapPromiseMonitor> monitor(host_impl_.get());
EXPECT_CALL(monitor, OnSetNeedsCommitOnMain()).Times(0);
EXPECT_CALL(monitor, OnSetNeedsRedrawOnImpl()).Times(1);
host_impl_->SetFullViewportDamage();
host_impl_->SetNeedsRedraw(false,
false);
}
{
StrictMock<MockLatencyInfoSwapPromiseMonitor> monitor(host_impl_.get());
EXPECT_CALL(monitor, OnSetNeedsCommitOnMain()).Times(0);
EXPECT_CALL(monitor, OnSetNeedsRedrawOnImpl()).Times(1);
host_impl_->SetNeedsRedraw(false,
false);
}
{
StrictMock<MockLatencyInfoSwapPromiseMonitor> monitor(host_impl_.get());
EXPECT_CALL(monitor, OnSetNeedsCommitOnMain()).Times(0);
EXPECT_CALL(monitor, OnSetNeedsRedrawOnImpl()).Times(1);
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
EXPECT_TRUE(
GetInputHandler()
.ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10),
ui::ScrollInputType::kTouchscreen))
.did_scroll);
GetInputHandler().ScrollEnd();
EXPECT_TRUE(Mock::VerifyAndClearExpectations(&monitor));
EXPECT_CALL(monitor, OnSetNeedsCommitOnMain()).Times(0);
EXPECT_CALL(monitor, OnSetNeedsRedrawOnImpl()).Times(1);
host_impl_->active_tree()->set_have_scroll_event_handlers(true);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
EXPECT_TRUE(
GetInputHandler()
.ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10),
ui::ScrollInputType::kTouchscreen))
.did_scroll);
GetInputHandler().ScrollEnd();
}
}
class LayerTreeHostImplWithBrowserControlsTest : public LayerTreeHostImplTest {
public:
void SetUp() override {
CreateHostImpl(DefaultSettings(), CreateLayerTreeFrameSink());
host_impl_->active_tree()->SetBrowserControlsParams(
{static_cast<float>(top_controls_height_), 0, 0, 0, false, false});
host_impl_->active_tree()->SetCurrentBrowserControlsShownRatio(1.f, 1.f);
}
protected:
void Scroll(float y) {
ASSERT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 50),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
ASSERT_TRUE(
GetInputHandler()
.ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2dF(0, y),
ui::ScrollInputType::kTouchscreen))
.did_scroll);
GetInputHandler().ScrollEnd();
}
void RunAnimation() {
viz::BeginFrameArgs begin_frame_args = viz::CreateBeginFrameArgsForTesting(
BEGINFRAME_FROM_HERE, 0, 1, base::TimeTicks::Now());
do {
did_request_next_frame_ = false;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
host_impl_->DidFinishImplFrame(begin_frame_args);
begin_frame_args.frame_time += base::Milliseconds(5);
begin_frame_args.frame_id.sequence_number++;
} while (did_request_next_frame_);
}
static const int top_controls_height_;
};
INSTANTIATE_COMMIT_TO_TREE_TEST_P(LayerTreeHostImplWithBrowserControlsTest);
const int LayerTreeHostImplWithBrowserControlsTest::top_controls_height_ = 50;
TEST_P(LayerTreeHostImplWithBrowserControlsTest, NoIdleAnimations) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
auto* scroll_layer = InnerViewportScrollLayer();
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree_mutable()
.UpdateScrollOffsetBaseForTesting(scroll_layer->element_id(),
gfx::PointF(0, 10));
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2);
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_FALSE(did_request_redraw_);
host_impl_->DidFinishImplFrame(begin_frame_args);
}
TEST_P(LayerTreeHostImplWithBrowserControlsTest,
BrowserControlsHeightIsCommitted) {
if (CommitsToActiveTree()) {
GTEST_SKIP();
}
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
EXPECT_FALSE(did_request_redraw_);
CreatePendingTree();
host_impl_->sync_tree()->SetBrowserControlsParams(
{100, 0, 0, 0, false, false});
host_impl_->ActivateSyncTree();
EXPECT_EQ(100, host_impl_->browser_controls_manager()->TopControlsHeight());
}
TEST_P(LayerTreeHostImplWithBrowserControlsTest,
BrowserControlsStayFullyVisibleOnHeightChange) {
if (CommitsToActiveTree()) {
GTEST_SKIP();
}
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
EXPECT_EQ(0, host_impl_->browser_controls_manager()->ControlsTopOffset());
CreatePendingTree();
host_impl_->sync_tree()->SetBrowserControlsParams({0, 0, 0, 0, false, false});
host_impl_->ActivateSyncTree();
EXPECT_EQ(0, host_impl_->browser_controls_manager()->ControlsTopOffset());
CreatePendingTree();
host_impl_->sync_tree()->SetBrowserControlsParams(
{50, 0, 0, 0, false, false});
host_impl_->ActivateSyncTree();
EXPECT_EQ(0, host_impl_->browser_controls_manager()->ControlsTopOffset());
}
TEST_P(LayerTreeHostImplWithBrowserControlsTest,
BrowserControlsAnimationScheduling) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
auto* scroll_layer = InnerViewportScrollLayer();
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree_mutable()
.UpdateScrollOffsetBaseForTesting(scroll_layer->element_id(),
gfx::PointF(0, 10));
host_impl_->DidChangeBrowserControlsPosition();
EXPECT_TRUE(did_request_next_frame_);
EXPECT_TRUE(did_request_redraw_);
}
TEST_P(LayerTreeHostImplWithBrowserControlsTest,
ScrollHandledByBrowserControls) {
InputHandlerScrollResult result;
SetupViewportLayersInnerScrolls(gfx::Size(50, 100), gfx::Size(100, 200));
auto* scroll_layer = InnerViewportScrollLayer();
UpdateDrawProperties(host_impl_->active_tree());
host_impl_->browser_controls_manager()->UpdateBrowserControlsState(
BrowserControlsState::kBoth, BrowserControlsState::kShown, false,
std::nullopt);
DrawFrame();
const float residue = 10;
float offset = top_controls_height_ - residue;
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, offset),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
EXPECT_EQ(0, host_impl_->browser_controls_manager()->ControlsTopOffset());
EXPECT_EQ(gfx::PointF(), CurrentScrollOffset(scroll_layer));
result = GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(), gfx::Vector2d(0, offset),
ui::ScrollInputType::kTouchscreen));
EXPECT_EQ(result.unused_scroll_delta, gfx::Vector2d(0, 0));
EXPECT_TRUE(result.did_scroll);
EXPECT_FLOAT_EQ(-offset,
host_impl_->browser_controls_manager()->ControlsTopOffset());
EXPECT_EQ(gfx::PointF(), CurrentScrollOffset(scroll_layer));
const float content_scroll = 20;
offset = residue + content_scroll;
result = GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(), gfx::Vector2d(0, offset),
ui::ScrollInputType::kTouchscreen));
EXPECT_TRUE(result.did_scroll);
EXPECT_EQ(result.unused_scroll_delta, gfx::Vector2d(0, 0));
EXPECT_EQ(-top_controls_height_,
host_impl_->browser_controls_manager()->ControlsTopOffset());
EXPECT_EQ(gfx::PointF(0, content_scroll), CurrentScrollOffset(scroll_layer));
offset = -content_scroll;
result = GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(), gfx::Vector2d(0, offset),
ui::ScrollInputType::kTouchscreen));
EXPECT_TRUE(result.did_scroll);
EXPECT_EQ(result.unused_scroll_delta, gfx::Vector2d(0, 0));
EXPECT_EQ(-top_controls_height_,
host_impl_->browser_controls_manager()->ControlsTopOffset());
EXPECT_EQ(gfx::PointF(), CurrentScrollOffset(scroll_layer));
offset = -top_controls_height_;
result = GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(), gfx::Vector2d(0, offset),
ui::ScrollInputType::kTouchscreen));
EXPECT_TRUE(result.did_scroll);
EXPECT_EQ(result.unused_scroll_delta, gfx::Vector2d(0, 0));
EXPECT_EQ(0, host_impl_->browser_controls_manager()->ControlsTopOffset());
EXPECT_EQ(gfx::PointF(), CurrentScrollOffset(scroll_layer));
result = GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(), gfx::Vector2d(0, offset),
ui::ScrollInputType::kTouchscreen));
EXPECT_FALSE(result.did_scroll);
EXPECT_EQ(result.unused_scroll_delta, gfx::Vector2d(0, -50));
EXPECT_EQ(0, host_impl_->browser_controls_manager()->ControlsTopOffset());
EXPECT_EQ(gfx::PointF(), CurrentScrollOffset(scroll_layer));
GetInputHandler().ScrollEnd();
}
TEST_P(LayerTreeHostImplWithBrowserControlsTest,
WheelUnhandledByBrowserControls) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 100), gfx::Size(100, 200));
host_impl_->active_tree()->SetBrowserControlsParams(
{top_controls_height_, 0, 0, 0, false, true});
host_impl_->browser_controls_manager()->UpdateBrowserControlsState(
BrowserControlsState::kBoth, BrowserControlsState::kShown, false,
std::nullopt);
DrawFrame();
LayerImpl* viewport_layer = InnerViewportScrollLayer();
const float delta = top_controls_height_;
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, delta),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
EXPECT_EQ(0, host_impl_->browser_controls_manager()->ControlsTopOffset());
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(viewport_layer));
EXPECT_TRUE(
GetInputHandler()
.ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, delta),
ui::ScrollInputType::kWheel))
.did_scroll);
EXPECT_FLOAT_EQ(0,
host_impl_->browser_controls_manager()->ControlsTopOffset());
EXPECT_POINTF_EQ(gfx::PointF(0, delta), CurrentScrollOffset(viewport_layer));
EXPECT_TRUE(
GetInputHandler()
.ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, delta),
ui::ScrollInputType::kWheel))
.did_scroll);
EXPECT_FLOAT_EQ(0,
host_impl_->browser_controls_manager()->ControlsTopOffset());
EXPECT_POINTF_EQ(gfx::PointF(0, delta * 2),
CurrentScrollOffset(viewport_layer));
}
TEST_P(LayerTreeHostImplWithBrowserControlsTest,
BrowserControlsAnimationAtOrigin) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 100), gfx::Size(100, 200));
auto* scroll_layer = InnerViewportScrollLayer();
UpdateDrawProperties(host_impl_->active_tree());
host_impl_->browser_controls_manager()->UpdateBrowserControlsState(
BrowserControlsState::kBoth, BrowserControlsState::kShown, false,
std::nullopt);
DrawFrame();
const float residue = 35;
float offset = top_controls_height_ - residue;
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, offset),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
EXPECT_EQ(0, host_impl_->browser_controls_manager()->ControlsTopOffset());
EXPECT_EQ(gfx::PointF(), CurrentScrollOffset(scroll_layer));
EXPECT_TRUE(
GetInputHandler()
.ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, offset),
ui::ScrollInputType::kTouchscreen))
.did_scroll);
EXPECT_FLOAT_EQ(-offset,
host_impl_->browser_controls_manager()->ControlsTopOffset());
EXPECT_EQ(gfx::PointF(), CurrentScrollOffset(scroll_layer));
did_request_redraw_ = false;
did_request_next_frame_ = false;
did_request_commit_ = false;
GetInputHandler().ScrollEnd();
ASSERT_TRUE(host_impl_->browser_controls_manager()->HasAnimation());
EXPECT_TRUE(did_request_next_frame_);
EXPECT_TRUE(did_request_redraw_);
EXPECT_TRUE(did_request_commit_);
viz::BeginFrameArgs begin_frame_args = viz::CreateBeginFrameArgsForTesting(
BEGINFRAME_FROM_HERE, 0, 1, base::TimeTicks::Now());
while (did_request_next_frame_) {
did_request_redraw_ = false;
did_request_next_frame_ = false;
did_request_commit_ = false;
float old_offset =
host_impl_->browser_controls_manager()->ControlsTopOffset();
begin_frame_args.frame_time += base::Milliseconds(5);
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_EQ(gfx::PointF(), CurrentScrollOffset(scroll_layer));
float new_offset =
host_impl_->browser_controls_manager()->ControlsTopOffset();
EXPECT_FALSE(did_request_commit_);
if (new_offset != old_offset)
EXPECT_TRUE(did_request_redraw_);
if (new_offset != 0) {
EXPECT_TRUE(host_impl_->browser_controls_manager()->HasAnimation());
EXPECT_TRUE(did_request_next_frame_);
}
host_impl_->DidFinishImplFrame(begin_frame_args);
}
EXPECT_FALSE(host_impl_->browser_controls_manager()->HasAnimation());
}
TEST_P(LayerTreeHostImplWithBrowserControlsTest,
BrowserControlsAnimationAfterScroll) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 100), gfx::Size(100, 200));
auto* scroll_layer = InnerViewportScrollLayer();
UpdateDrawProperties(host_impl_->active_tree());
host_impl_->browser_controls_manager()->UpdateBrowserControlsState(
BrowserControlsState::kBoth, BrowserControlsState::kShown, false,
std::nullopt);
float initial_scroll_offset = 50;
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree_mutable()
.UpdateScrollOffsetBaseForTesting(scroll_layer->element_id(),
gfx::PointF(0, initial_scroll_offset));
DrawFrame();
const float residue = 15;
float offset = top_controls_height_ - residue;
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, offset),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
EXPECT_EQ(0, host_impl_->browser_controls_manager()->ControlsTopOffset());
EXPECT_EQ(gfx::PointF(0, initial_scroll_offset),
CurrentScrollOffset(scroll_layer));
EXPECT_TRUE(
GetInputHandler()
.ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, offset),
ui::ScrollInputType::kTouchscreen))
.did_scroll);
EXPECT_FLOAT_EQ(-offset,
host_impl_->browser_controls_manager()->ControlsTopOffset());
EXPECT_EQ(gfx::PointF(0, initial_scroll_offset),
CurrentScrollOffset(scroll_layer));
did_request_redraw_ = false;
did_request_next_frame_ = false;
did_request_commit_ = false;
GetInputHandler().ScrollEnd();
ASSERT_TRUE(host_impl_->browser_controls_manager()->HasAnimation());
EXPECT_TRUE(did_request_next_frame_);
EXPECT_TRUE(did_request_redraw_);
EXPECT_TRUE(did_request_commit_);
viz::BeginFrameArgs begin_frame_args = viz::CreateBeginFrameArgsForTesting(
BEGINFRAME_FROM_HERE, 0, 1, base::TimeTicks::Now());
while (did_request_next_frame_) {
did_request_redraw_ = false;
did_request_next_frame_ = false;
did_request_commit_ = false;
float old_offset =
host_impl_->browser_controls_manager()->ControlsTopOffset();
begin_frame_args.frame_time += base::Milliseconds(5);
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
float new_offset =
host_impl_->browser_controls_manager()->ControlsTopOffset();
if (new_offset != old_offset) {
EXPECT_TRUE(did_request_redraw_);
EXPECT_TRUE(did_request_commit_);
}
host_impl_->DidFinishImplFrame(begin_frame_args);
}
EXPECT_FALSE(host_impl_->browser_controls_manager()->HasAnimation());
EXPECT_EQ(-top_controls_height_,
host_impl_->browser_controls_manager()->ControlsTopOffset());
}
TEST_P(LayerTreeHostImplWithBrowserControlsTest,
BrowserControlsScrollDeltaInOverScroll) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 100), gfx::Size(100, 200));
auto* scroll_layer = InnerViewportScrollLayer();
UpdateDrawProperties(host_impl_->active_tree());
host_impl_->browser_controls_manager()->UpdateBrowserControlsState(
BrowserControlsState::kBoth, BrowserControlsState::kShown, false,
std::nullopt);
DrawFrame();
float offset = 50;
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, offset),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
EXPECT_EQ(0, host_impl_->browser_controls_manager()->ControlsTopOffset());
EXPECT_TRUE(
GetInputHandler()
.ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, offset),
ui::ScrollInputType::kTouchscreen))
.did_scroll);
EXPECT_EQ(-offset,
host_impl_->browser_controls_manager()->ControlsTopOffset());
EXPECT_EQ(gfx::PointF(), CurrentScrollOffset(scroll_layer));
EXPECT_TRUE(
GetInputHandler()
.ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, offset),
ui::ScrollInputType::kTouchscreen))
.did_scroll);
EXPECT_EQ(gfx::PointF(0, offset), CurrentScrollOffset(scroll_layer));
EXPECT_TRUE(
GetInputHandler()
.ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, offset),
ui::ScrollInputType::kTouchscreen))
.did_scroll);
EXPECT_EQ(gfx::PointF(0, MaxScrollOffset(scroll_layer).y()),
CurrentScrollOffset(scroll_layer));
float overscrollamount = 10;
EXPECT_FALSE(GetInputHandler()
.ScrollUpdate(UpdateState(gfx::Point(),
gfx::Vector2d(0, overscrollamount),
ui::ScrollInputType::kTouchscreen))
.did_scroll);
EXPECT_EQ(gfx::PointF(0, 2 * offset), CurrentScrollOffset(scroll_layer));
EXPECT_EQ(gfx::Vector2dF(0, overscrollamount),
GetInputHandler().accumulated_root_overscroll_for_testing());
EXPECT_TRUE(
GetInputHandler()
.ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, -2 * offset),
ui::ScrollInputType::kTouchscreen))
.did_scroll);
EXPECT_EQ(gfx::PointF(0, 0), CurrentScrollOffset(scroll_layer));
EXPECT_EQ(-offset,
host_impl_->browser_controls_manager()->ControlsTopOffset());
EXPECT_TRUE(
GetInputHandler()
.ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, -offset),
ui::ScrollInputType::kTouchscreen))
.did_scroll);
EXPECT_EQ(gfx::PointF(0, 0), CurrentScrollOffset(scroll_layer));
EXPECT_EQ(0, host_impl_->browser_controls_manager()->ControlsTopOffset());
GetInputHandler().ScrollEnd();
}
TEST_P(LayerTreeHostImplWithBrowserControlsTest,
AnimationDoesntScrollUnscrolledViewport) {
SetupViewportLayersInnerScrolls(gfx::Size(100, 100), gfx::Size(100, 200));
LayerTreeImpl* layer_tree_impl = host_impl_->active_tree();
ASSERT_EQ(1, layer_tree_impl->CurrentTopControlsShownRatio());
Scroll(10.f);
EXPECT_EQ(0.8f, host_impl_->active_tree()->CurrentTopControlsShownRatio());
EXPECT_EQ(0.f, host_impl_->viewport().TotalScrollOffset().y());
RunAnimation();
EXPECT_EQ(1, layer_tree_impl->CurrentTopControlsShownRatio());
EXPECT_EQ(0.f, host_impl_->viewport().TotalScrollOffset().y());
}
TEST_P(LayerTreeHostImplWithBrowserControlsTest,
AnimationScrollsScrolledViewport) {
SetupViewportLayersInnerScrolls(gfx::Size(100, 100), gfx::Size(100, 200));
LayerTreeImpl* layer_tree_impl = host_impl_->active_tree();
ASSERT_EQ(1, layer_tree_impl->CurrentTopControlsShownRatio());
Scroll(60.f);
EXPECT_EQ(0.f, host_impl_->active_tree()->CurrentTopControlsShownRatio());
EXPECT_EQ(10.f, host_impl_->viewport().TotalScrollOffset().y());
Scroll(-40.f);
EXPECT_EQ(0.8f, host_impl_->active_tree()->CurrentTopControlsShownRatio());
EXPECT_EQ(10.f, host_impl_->viewport().TotalScrollOffset().y());
RunAnimation();
EXPECT_EQ(1, layer_tree_impl->CurrentTopControlsShownRatio());
EXPECT_EQ(20.f, host_impl_->viewport().TotalScrollOffset().y());
}
TEST_P(LayerTreeHostImplWithBrowserControlsTest,
AnimationScrollsViewportWhenOnlyExpandTopControlsAtPageTopIsSet) {
SetupViewportLayersInnerScrolls(gfx::Size(100, 100), gfx::Size(100, 200));
LayerTreeImpl* layer_tree_impl = host_impl_->active_tree();
layer_tree_impl->SetBrowserControlsParams(
{50.f, 0.f, 0.f, 0.f, false, false,
true});
ASSERT_EQ(1, layer_tree_impl->CurrentTopControlsShownRatio());
Scroll(60.f);
EXPECT_EQ(0.f, host_impl_->active_tree()->CurrentTopControlsShownRatio());
EXPECT_EQ(10.f, host_impl_->viewport().TotalScrollOffset().y());
Scroll(-50.f);
EXPECT_EQ(0.8f, host_impl_->active_tree()->CurrentTopControlsShownRatio());
EXPECT_EQ(0.f, host_impl_->viewport().TotalScrollOffset().y());
RunAnimation();
EXPECT_EQ(1, layer_tree_impl->CurrentTopControlsShownRatio());
EXPECT_EQ(0.f, host_impl_->viewport().TotalScrollOffset().y());
}
TEST_P(LayerTreeHostImplBrowserControlsTest,
ReplacedOuterViewportScrollsBrowserControls) {
const gfx::Size scroll_content_size(400, 400);
const gfx::Size root_layer_size(200, 200);
const gfx::Size viewport_size(100, 100);
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
viewport_size, viewport_size, root_layer_size);
LayerTreeImpl* layer_tree_impl = host_impl_->active_tree();
LayerImpl* outer_scroll = OuterViewportScrollLayer();
LayerImpl* clip_layer = AddLayerInActiveTree();
clip_layer->SetBounds(root_layer_size);
CopyProperties(outer_scroll, clip_layer);
CreateClipNode(clip_layer);
LayerImpl* scroll_layer =
AddScrollableLayer(clip_layer, root_layer_size, scroll_content_size);
GetScrollNode(scroll_layer)->scrolls_outer_viewport = true;
auto viewport_property_ids = layer_tree_impl->ViewportPropertyIdsForTesting();
viewport_property_ids.outer_clip = clip_layer->clip_tree_index();
viewport_property_ids.outer_scroll = scroll_layer->scroll_tree_index();
layer_tree_impl->SetViewportPropertyIds(viewport_property_ids);
DrawFrame();
ASSERT_EQ(1, layer_tree_impl->CurrentTopControlsShownRatio());
{
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(0, 0), gfx::Vector2dF(100, 100),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(0, 0), gfx::Vector2dF(100, 100),
ui::ScrollInputType::kTouchscreen));
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(outer_scroll));
EXPECT_POINTF_EQ(gfx::PointF(100, 50), CurrentScrollOffset(scroll_layer));
EXPECT_EQ(0, layer_tree_impl->CurrentTopControlsShownRatio());
}
}
TEST_P(LayerTreeHostImplTest, RootScrollBothInnerAndOuterLayer) {
gfx::Size content_size = gfx::Size(100, 160);
gfx::Size outer_viewport = gfx::Size(50, 80);
gfx::Size inner_viewport = gfx::Size(25, 40);
SetupViewportLayers(host_impl_->active_tree(), inner_viewport, outer_viewport,
content_size);
LayerImpl* outer_scroll = OuterViewportScrollLayer();
LayerImpl* inner_scroll = InnerViewportScrollLayer();
DrawFrame();
{
gfx::PointF inner_expected;
gfx::PointF outer_expected;
EXPECT_EQ(inner_expected, CurrentScrollOffset(inner_scroll));
EXPECT_EQ(outer_expected, CurrentScrollOffset(outer_scroll));
gfx::PointF current_offset(70, 100);
GetInputHandler().SetSynchronousInputHandlerRootScrollOffset(
current_offset);
EXPECT_EQ(gfx::PointF(25, 40), MaxScrollOffset(inner_scroll));
EXPECT_EQ(gfx::PointF(50, 80), MaxScrollOffset(outer_scroll));
EXPECT_EQ(gfx::PointF(25, 40), CurrentScrollOffset(inner_scroll));
EXPECT_EQ(gfx::PointF(45, 60), CurrentScrollOffset(outer_scroll));
}
}
TEST_P(LayerTreeHostImplTest, DiagonalScrollBubblesPerfectlyToInner) {
gfx::Size content_size = gfx::Size(200, 320);
gfx::Size outer_viewport = gfx::Size(100, 160);
gfx::Size inner_viewport = gfx::Size(50, 80);
SetupViewportLayers(host_impl_->active_tree(), inner_viewport, outer_viewport,
content_size);
LayerImpl* outer_scroll = OuterViewportScrollLayer();
LayerImpl* inner_scroll = InnerViewportScrollLayer();
DrawFrame();
{
gfx::PointF inner_expected;
gfx::PointF outer_expected;
EXPECT_POINTF_EQ(inner_expected, CurrentScrollOffset(inner_scroll));
EXPECT_POINTF_EQ(outer_expected, CurrentScrollOffset(outer_scroll));
gfx::Vector2dF scroll_delta(inner_viewport.width() / 2,
inner_viewport.height() / 2);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), scroll_delta,
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), scroll_delta, ui::ScrollInputType::kTouchscreen));
inner_expected += scroll_delta;
EXPECT_POINTF_EQ(inner_expected, CurrentScrollOffset(inner_scroll));
EXPECT_POINTF_EQ(outer_expected, CurrentScrollOffset(outer_scroll));
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(), gfx::ScaleVector2d(scroll_delta, 2),
ui::ScrollInputType::kTouchscreen));
outer_expected += scroll_delta;
inner_expected += scroll_delta;
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(inner_expected, CurrentScrollOffset(inner_scroll));
EXPECT_POINTF_EQ(outer_expected, CurrentScrollOffset(outer_scroll));
}
}
TEST_P(LayerTreeHostImplTest,
ScrollBeginEventThatTargetsViewportLayerSkipsHitTest) {
gfx::Size content_size = gfx::Size(100, 160);
gfx::Size outer_viewport = gfx::Size(50, 80);
gfx::Size inner_viewport = gfx::Size(25, 40);
SetupViewportLayers(host_impl_->active_tree(), inner_viewport, outer_viewport,
content_size);
LayerImpl* outer_scroll = OuterViewportScrollLayer();
LayerImpl* child_scroll =
AddScrollableLayer(outer_scroll, inner_viewport, outer_viewport);
UpdateDrawProperties(host_impl_->active_tree());
DrawFrame();
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.RootScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
EXPECT_EQ(host_impl_->CurrentlyScrollingNode(),
host_impl_->OuterViewportScrollNode());
GetInputHandler().ScrollEnd();
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id,
child_scroll->scroll_tree_index());
GetInputHandler().ScrollEnd();
}
TEST_P(LayerTreeHostImplTest, NoOverscrollWhenInnerViewportCantScroll) {
InputHandlerScrollResult scroll_result;
gfx::Size content_size = gfx::Size(100, 160);
gfx::Size outer_viewport = gfx::Size(50, 80);
gfx::Size inner_viewport = gfx::Size(25, 40);
SetupViewportLayers(host_impl_->active_tree(), inner_viewport, outer_viewport,
content_size);
LayerImpl* inner_scroll = InnerViewportScrollLayer();
GetScrollNode(inner_scroll)->user_scrollable_horizontal = false;
GetScrollNode(inner_scroll)->user_scrollable_vertical = false;
DrawFrame();
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(inner_scroll));
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 100),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
scroll_result = GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2dF(0, 100), ui::ScrollInputType::kTouchscreen));
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(inner_scroll));
EXPECT_FALSE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF(),
GetInputHandler().accumulated_root_overscroll_for_testing());
}
class LayerTreeHostImplWithImplicitLimitsTest : public LayerTreeHostImplTest {
public:
LayerTreeSettings DefaultSettings() override {
LayerTreeSettings settings = LayerTreeHostImplTest::DefaultSettings();
settings.max_memory_for_prepaint_percentage = 50;
return settings;
}
};
INSTANTIATE_CLIENT_MODE_TREE_TEST_P(LayerTreeHostImplWithImplicitLimitsTest);
TEST_P(LayerTreeHostImplWithImplicitLimitsTest, ImplicitMemoryLimits) {
ManagedMemoryPolicy mem_policy(300 * 1024 * 1024);
host_impl_->SetMemoryPolicy(mem_policy);
EXPECT_EQ(host_impl_->global_tile_state().hard_memory_limit_in_bytes,
300u * 1024u * 1024u);
EXPECT_EQ(host_impl_->global_tile_state().soft_memory_limit_in_bytes,
150u * 1024u * 1024u);
}
TEST_P(LayerTreeHostImplTest, ExternalTransformReflectedInNextDraw) {
const gfx::Size viewport_size(50, 50);
const gfx::Size layer_size(100, 100);
gfx::Transform external_transform;
const gfx::Rect external_viewport(layer_size);
const bool resourceless_software_draw = false;
SetupViewportLayersInnerScrolls(viewport_size, layer_size);
auto* layer = InnerViewportScrollLayer();
layer->SetDrawsContent(true);
host_impl_->SetExternalTilePriorityConstraints(external_viewport,
external_transform);
host_impl_->OnDraw(external_transform, external_viewport,
resourceless_software_draw, false);
EXPECT_TRANSFORM_EQ(external_transform,
layer->draw_properties().target_space_transform);
external_transform.Translate(20, 20);
host_impl_->SetExternalTilePriorityConstraints(external_viewport,
external_transform);
host_impl_->OnDraw(external_transform, external_viewport,
resourceless_software_draw, false);
EXPECT_TRANSFORM_EQ(external_transform,
layer->draw_properties().target_space_transform);
}
TEST_P(LayerTreeHostImplTest, ExternalTransformSetNeedsRedraw) {
const gfx::Size viewport_size(100, 100);
SetupDefaultRootLayer(viewport_size);
UpdateDrawProperties(host_impl_->active_tree());
const gfx::Transform transform_for_tile_priority;
const gfx::Transform draw_transform;
const gfx::Rect viewport_for_tile_priority1(viewport_size);
const gfx::Rect viewport_for_tile_priority2(50, 50);
const gfx::Rect draw_viewport(viewport_size);
bool resourceless_software_draw = false;
host_impl_->SetExternalTilePriorityConstraints(viewport_for_tile_priority1,
transform_for_tile_priority);
host_impl_->OnDraw(draw_transform, draw_viewport, resourceless_software_draw,
false);
last_on_draw_frame_.reset();
did_request_redraw_ = false;
host_impl_->SetExternalTilePriorityConstraints(viewport_for_tile_priority2,
transform_for_tile_priority);
EXPECT_TRUE(did_request_redraw_);
host_impl_->OnDraw(draw_transform, draw_viewport, resourceless_software_draw,
false);
EXPECT_FALSE(last_on_draw_frame_->has_no_damage);
}
TEST_P(ClientModeLayerTreeHostImplTest, OnMemoryPressure) {
gfx::Size size(200, 200);
viz::SharedImageFormat format = viz::SinglePlaneFormat::kRGBA_8888;
gfx::ColorSpace color_space = gfx::ColorSpace::CreateSRGB();
ResourcePool::InUsePoolResource resource =
host_impl_->resource_pool()->AcquireResource(size, format, color_space);
size_t current_memory_usage =
host_impl_->resource_pool()->GetTotalMemoryUsageForTesting();
EXPECT_EQ(current_memory_usage, 0u);
resource.set_backing(std::make_unique<ResourcePool::Backing>(
resource.size(), resource.format(), resource.color_space()));
host_impl_->resource_pool()->ReleaseResource(std::move(resource));
current_memory_usage =
host_impl_->resource_pool()->GetTotalMemoryUsageForTesting();
base::MemoryPressureListener::SimulatePressureNotificationAsync(
base::MEMORY_PRESSURE_LEVEL_CRITICAL);
base::RunLoop().RunUntilIdle();
size_t memory_usage_after_memory_pressure =
host_impl_->resource_pool()->GetTotalMemoryUsageForTesting();
EXPECT_LT(memory_usage_after_memory_pressure, current_memory_usage);
}
TEST_P(LayerTreeHostImplTest, OnDrawConstraintSetNeedsRedraw) {
const gfx::Size viewport_size(100, 100);
SetupDefaultRootLayer(viewport_size);
UpdateDrawProperties(host_impl_->active_tree());
const gfx::Transform draw_transform;
const gfx::Rect draw_viewport1(viewport_size);
const gfx::Rect draw_viewport2(50, 50);
bool resourceless_software_draw = false;
host_impl_->OnDraw(draw_transform, draw_viewport1, resourceless_software_draw,
false);
last_on_draw_frame_.reset();
did_request_redraw_ = false;
host_impl_->OnDraw(draw_transform, draw_viewport1, resourceless_software_draw,
false);
EXPECT_FALSE(did_request_redraw_);
EXPECT_TRUE(last_on_draw_frame_->has_no_damage);
last_on_draw_frame_.reset();
did_request_redraw_ = false;
host_impl_->OnDraw(draw_transform, draw_viewport2, resourceless_software_draw,
false);
EXPECT_TRUE(did_request_redraw_);
EXPECT_FALSE(last_on_draw_frame_->has_no_damage);
}
TEST_P(LayerTreeHostImplTest, FullViewportDamageAfterOnDraw) {
const gfx::Size viewport_size(100, 100);
SetupDefaultRootLayer(viewport_size);
UpdateDrawProperties(host_impl_->active_tree());
const gfx::Transform draw_transform;
const gfx::Rect draw_viewport(gfx::Point(5, 5), viewport_size);
bool resourceless_software_draw = false;
host_impl_->OnDraw(draw_transform, draw_viewport, resourceless_software_draw,
false);
EXPECT_EQ(draw_viewport, host_impl_->active_tree()->GetDeviceViewport());
host_impl_->SetFullViewportDamage();
EXPECT_EQ(host_impl_->active_tree()->internal_device_viewport(),
host_impl_->viewport_damage_rect_for_testing());
}
class ResourcelessSoftwareLayerTreeHostImplTest
: public CommitToPendingTreeLayerTreeHostImplTest {
protected:
std::unique_ptr<LayerTreeFrameSink> CreateLayerTreeFrameSink() override {
return FakeLayerTreeFrameSink::Create3d();
}
};
TEST_F(ResourcelessSoftwareLayerTreeHostImplTest,
ResourcelessSoftwareSetNeedsRedraw) {
const gfx::Size viewport_size(100, 100);
SetupDefaultRootLayer(viewport_size);
UpdateDrawProperties(host_impl_->active_tree());
const gfx::Transform draw_transform;
const gfx::Rect draw_viewport(viewport_size);
bool resourceless_software_draw = false;
host_impl_->OnDraw(draw_transform, draw_viewport, resourceless_software_draw,
false);
last_on_draw_frame_.reset();
resourceless_software_draw = true;
host_impl_->OnDraw(draw_transform, draw_viewport, resourceless_software_draw,
false);
EXPECT_FALSE(last_on_draw_frame_->has_no_damage);
last_on_draw_frame_.reset();
resourceless_software_draw = false;
host_impl_->OnDraw(draw_transform, draw_viewport, resourceless_software_draw,
false);
EXPECT_FALSE(last_on_draw_frame_->has_no_damage);
}
TEST_F(ResourcelessSoftwareLayerTreeHostImplTest,
ResourcelessSoftwareDrawSkipsUpdateTiles) {
const gfx::Size viewport_size(100, 100);
CreatePendingTree();
scoped_refptr<FakeRasterSource> raster_source(
FakeRasterSource::CreateFilled(viewport_size));
auto* root = SetupRootLayer<FakePictureLayerImpl>(
host_impl_->pending_tree(), viewport_size, raster_source);
root->SetBounds(viewport_size);
root->SetDrawsContent(true);
UpdateDrawProperties(host_impl_->pending_tree());
host_impl_->ActivateSyncTree();
const gfx::Transform draw_transform;
const gfx::Rect draw_viewport(viewport_size);
bool resourceless_software_draw = false;
did_request_prepare_tiles_ = false;
host_impl_->OnDraw(draw_transform, draw_viewport, resourceless_software_draw,
false);
EXPECT_TRUE(did_request_prepare_tiles_);
host_impl_->PrepareTiles();
const gfx::Rect new_draw_viewport(50, 50);
resourceless_software_draw = true;
did_request_prepare_tiles_ = false;
host_impl_->OnDraw(draw_transform, new_draw_viewport,
resourceless_software_draw, false);
EXPECT_FALSE(did_request_prepare_tiles_);
}
TEST_F(CommitToPendingTreeLayerTreeHostImplTest,
ExternalTileConstraintReflectedInPendingTree) {
const gfx::Size layer_size(100, 100);
CreatePendingTree();
SetupRootLayer<LayerImpl>(host_impl_->pending_tree(), layer_size);
UpdateDrawProperties(host_impl_->pending_tree());
host_impl_->pending_tree()->root_layer()->SetNeedsPushProperties();
host_impl_->ActivateSyncTree();
UpdateDrawProperties(host_impl_->active_tree());
CreatePendingTree();
UpdateDrawProperties(host_impl_->pending_tree());
UpdateDrawProperties(host_impl_->active_tree());
EXPECT_FALSE(host_impl_->pending_tree()->needs_update_draw_properties());
EXPECT_FALSE(host_impl_->active_tree()->needs_update_draw_properties());
gfx::Transform external_transform;
gfx::Rect external_viewport(10, 20);
host_impl_->SetExternalTilePriorityConstraints(external_viewport,
external_transform);
EXPECT_TRUE(host_impl_->pending_tree()->needs_update_draw_properties());
EXPECT_TRUE(host_impl_->active_tree()->needs_update_draw_properties());
}
TEST_P(LayerTreeHostImplTest, ExternalViewportAffectsVisibleRects) {
const gfx::Size viewport_size(50, 50);
const gfx::Size layer_size(100, 100);
SetupViewportLayersInnerScrolls(viewport_size, layer_size);
LayerImpl* content_layer = AddContentLayer();
host_impl_->active_tree()->SetDeviceViewportRect(gfx::Rect(90, 90));
UpdateDrawProperties(host_impl_->active_tree());
EXPECT_EQ(gfx::Rect(90, 90), content_layer->visible_layer_rect());
gfx::Transform external_transform;
gfx::Rect external_viewport(10, 20);
bool resourceless_software_draw = false;
host_impl_->SetExternalTilePriorityConstraints(external_viewport,
external_transform);
host_impl_->OnDraw(external_transform, external_viewport,
resourceless_software_draw, false);
EXPECT_EQ(gfx::Rect(10, 20), content_layer->visible_layer_rect());
external_viewport = gfx::Rect();
host_impl_->SetExternalTilePriorityConstraints(external_viewport,
external_transform);
host_impl_->OnDraw(external_transform, external_viewport,
resourceless_software_draw, false);
EXPECT_EQ(gfx::Rect(90, 90), content_layer->visible_layer_rect());
}
TEST_P(LayerTreeHostImplTest, ExternalTransformAffectsVisibleRects) {
const gfx::Size viewport_size(50, 50);
const gfx::Size layer_size(100, 100);
SetupViewportLayersInnerScrolls(viewport_size, layer_size);
LayerImpl* content_layer = AddContentLayer();
host_impl_->active_tree()->SetDeviceViewportRect(gfx::Rect(50, 50));
UpdateDrawProperties(host_impl_->active_tree());
EXPECT_EQ(gfx::Rect(50, 50), content_layer->visible_layer_rect());
gfx::Transform external_transform;
external_transform.Translate(10, 10);
external_transform.Scale(2, 2);
gfx::Rect external_viewport;
bool resourceless_software_draw = false;
host_impl_->SetExternalTilePriorityConstraints(external_viewport,
external_transform);
host_impl_->OnDraw(external_transform, external_viewport,
resourceless_software_draw, false);
EXPECT_EQ(gfx::Rect(20, 20), content_layer->visible_layer_rect());
external_transform = gfx::Transform();
host_impl_->SetExternalTilePriorityConstraints(external_viewport,
external_transform);
host_impl_->OnDraw(external_transform, external_viewport,
resourceless_software_draw, false);
EXPECT_EQ(gfx::Rect(50, 50), content_layer->visible_layer_rect());
}
TEST_P(LayerTreeHostImplTest, ExternalTransformAffectsSublayerScaleFactor) {
const gfx::Size viewport_size(50, 50);
const gfx::Size layer_size(100, 100);
SetupViewportLayersInnerScrolls(viewport_size, layer_size);
LayerImpl* test_layer = AddContentLayer();
gfx::Transform perspective_transform;
perspective_transform.ApplyPerspectiveDepth(2);
CreateTransformNode(test_layer).local = perspective_transform;
CreateEffectNode(test_layer).render_surface_reason =
RenderSurfaceReason::kTest;
host_impl_->active_tree()->SetDeviceViewportRect(gfx::Rect(50, 50));
UpdateDrawProperties(host_impl_->active_tree());
EffectNode* node = GetEffectNode(test_layer);
EXPECT_EQ(node->surface_contents_scale, gfx::Vector2dF(1, 1));
gfx::Transform external_transform;
external_transform.Translate(10, 10);
external_transform.Scale(2, 2);
gfx::Rect external_viewport;
bool resourceless_software_draw = false;
host_impl_->SetExternalTilePriorityConstraints(external_viewport,
external_transform);
host_impl_->OnDraw(external_transform, external_viewport,
resourceless_software_draw, false);
node = GetEffectNode(test_layer);
EXPECT_EQ(node->surface_contents_scale, gfx::Vector2dF(2, 2));
external_transform = gfx::Transform();
host_impl_->SetExternalTilePriorityConstraints(external_viewport,
external_transform);
host_impl_->OnDraw(external_transform, external_viewport,
resourceless_software_draw, false);
node = GetEffectNode(test_layer);
EXPECT_EQ(node->surface_contents_scale, gfx::Vector2dF(1, 1));
}
TEST_F(CommitToPendingTreeLayerTreeHostImplTest, OneScrollForFirstScrollDelay) {
CreateHostImpl(DefaultSettings(), CreateLayerTreeFrameSink());
SetupRootLayer<SolidColorLayerImpl>(host_impl_->active_tree(),
gfx::Size(10, 10));
UpdateDrawProperties(host_impl_->active_tree());
EXPECT_EQ(first_scroll_observed, 0);
ui::LatencyInfo latency_info;
latency_info.set_trace_id(5);
latency_info.AddLatencyNumber(
ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT);
std::unique_ptr<SwapPromise> swap_promise(
new LatencyInfoSwapPromise(latency_info));
host_impl_->active_tree()->QueuePinnedSwapPromise(std::move(swap_promise));
host_impl_->SetFullViewportDamage();
host_impl_->SetNeedsRedraw(false,
false);
DrawFrame();
constexpr uint32_t frame_token_1 = 1;
viz::FrameTimingDetails mock_details;
mock_details.presentation_feedback = ExampleFeedback();
host_impl_->DidPresentCompositorFrame(frame_token_1, mock_details);
EXPECT_EQ(first_scroll_observed, 1);
}
TEST_F(CommitToPendingTreeLayerTreeHostImplTest,
OtherInputsForFirstScrollDelay) {
CreateHostImpl(DefaultSettings(), CreateLayerTreeFrameSink());
SetupRootLayer<SolidColorLayerImpl>(host_impl_->active_tree(),
gfx::Size(10, 10));
UpdateDrawProperties(host_impl_->active_tree());
EXPECT_EQ(first_scroll_observed, 0);
ui::LatencyInfo latency_info;
latency_info.set_trace_id(5);
latency_info.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT);
std::unique_ptr<SwapPromise> swap_promise(
new LatencyInfoSwapPromise(latency_info));
host_impl_->active_tree()->QueuePinnedSwapPromise(std::move(swap_promise));
host_impl_->SetFullViewportDamage();
host_impl_->SetNeedsRedraw(false,
false);
DrawFrame();
constexpr uint32_t frame_token_1 = 1;
viz::FrameTimingDetails mock_details;
mock_details.presentation_feedback = ExampleFeedback();
host_impl_->DidPresentCompositorFrame(frame_token_1, mock_details);
EXPECT_EQ(first_scroll_observed, 0);
}
TEST_F(CommitToPendingTreeLayerTreeHostImplTest,
MultipleScrollsForFirstScrollDelay) {
CreateHostImpl(DefaultSettings(), CreateLayerTreeFrameSink());
SetupRootLayer<SolidColorLayerImpl>(host_impl_->active_tree(),
gfx::Size(10, 10));
UpdateDrawProperties(host_impl_->active_tree());
EXPECT_EQ(first_scroll_observed, 0);
ui::LatencyInfo latency_info;
latency_info.set_trace_id(5);
latency_info.AddLatencyNumber(
ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT);
std::unique_ptr<SwapPromise> swap_promise(
new LatencyInfoSwapPromise(latency_info));
host_impl_->active_tree()->QueuePinnedSwapPromise(std::move(swap_promise));
DrawFrame();
constexpr uint32_t frame_token_1 = 1;
viz::FrameTimingDetails mock_details;
mock_details.presentation_feedback = ExampleFeedback();
host_impl_->DidPresentCompositorFrame(frame_token_1, mock_details);
EXPECT_EQ(first_scroll_observed, 1);
ui::LatencyInfo latency_info2;
latency_info2.set_trace_id(6);
latency_info2.AddLatencyNumber(
ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT);
std::unique_ptr<SwapPromise> swap_promise2(
new LatencyInfoSwapPromise(latency_info2));
host_impl_->active_tree()->QueuePinnedSwapPromise(std::move(swap_promise2));
host_impl_->SetFullViewportDamage();
host_impl_->SetNeedsRedraw(false,
false);
DrawFrame();
constexpr uint32_t frame_token_2 = 2;
viz::FrameTimingDetails mock_details2;
mock_details2.presentation_feedback = ExampleFeedback();
host_impl_->DidPresentCompositorFrame(frame_token_2, mock_details2);
EXPECT_EQ(first_scroll_observed, 1);
}
TEST_P(LayerTreeHostImplTest, ScrollAnimated) {
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(50, 100);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
DrawFrame();
base::TimeTicks start_time = base::TimeTicks() + base::Milliseconds(100);
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
{
StrictMock<MockLatencyInfoSwapPromiseMonitor> monitor(host_impl_.get());
EXPECT_CALL(monitor, OnSetNeedsCommitOnMain()).Times(0);
EXPECT_CALL(monitor, OnSetNeedsRedrawOnImpl()).Times(AtLeast(1));
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 50),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
GetInputHandler().ScrollUpdate(
AnimatedUpdateState(gfx::Point(), gfx::Vector2d(0, 50)));
}
LayerImpl* scrolling_layer = OuterViewportScrollLayer();
EXPECT_EQ(scrolling_layer->scroll_tree_index(),
host_impl_->CurrentlyScrollingNode()->id);
begin_frame_args.frame_time = start_time;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
EXPECT_NE(gfx::PointF(), CurrentScrollOffset(scrolling_layer));
host_impl_->DidFinishImplFrame(begin_frame_args);
begin_frame_args.frame_time = start_time + base::Milliseconds(50);
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
float y = CurrentScrollOffset(scrolling_layer).y();
EXPECT_TRUE(y > 1 && y < 49);
{
StrictMock<MockLatencyInfoSwapPromiseMonitor> monitor(host_impl_.get());
EXPECT_CALL(monitor, OnSetNeedsCommitOnMain()).Times(0);
EXPECT_CALL(monitor, OnSetNeedsRedrawOnImpl()).Times(1);
GetInputHandler().ScrollUpdate(
AnimatedUpdateState(gfx::Point(), gfx::Vector2d(0, 50)));
GetInputHandler().ScrollEnd();
}
host_impl_->DidFinishImplFrame(begin_frame_args);
begin_frame_args.frame_time = start_time + base::Milliseconds(200);
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
y = CurrentScrollOffset(scrolling_layer).y();
EXPECT_TRUE(y > 50 && y < 100);
EXPECT_EQ(scrolling_layer->scroll_tree_index(),
host_impl_->CurrentlyScrollingNode()->id);
host_impl_->DidFinishImplFrame(begin_frame_args);
begin_frame_args.frame_time = start_time + base::Milliseconds(250);
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
EXPECT_POINTF_EQ(gfx::PointF(0, 100), CurrentScrollOffset(scrolling_layer));
EXPECT_EQ(nullptr, host_impl_->CurrentlyScrollingNode());
host_impl_->DidFinishImplFrame(begin_frame_args);
}
TEST_P(LayerTreeHostImplTest, ScrollAnimatedLatching) {
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(50, 100);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
AddScrollableLayer(OuterViewportScrollLayer(), gfx::Size(10, 10),
gfx::Size(20, 20));
LayerImpl* outer_scroll = OuterViewportScrollLayer();
DrawFrame();
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
{
gfx::Point position(40, 40);
auto begin_state =
BeginState(position, gfx::Vector2d(0, 50), ui::ScrollInputType::kWheel);
GetInputHandler().ScrollBegin(begin_state.get(),
ui::ScrollInputType::kWheel);
auto update_state = AnimatedUpdateState(position, gfx::Vector2d(0, 50));
GetInputHandler().ScrollUpdate(update_state);
EXPECT_EQ(outer_scroll->scroll_tree_index(),
host_impl_->CurrentlyScrollingNode()->id);
base::TimeTicks start_time = base::TimeTicks() + base::Milliseconds(100);
begin_frame_args.frame_time = start_time;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
host_impl_->DidFinishImplFrame(begin_frame_args);
EXPECT_NE(gfx::PointF(), CurrentScrollOffset(outer_scroll));
EXPECT_TRUE(GetImplAnimationHost()->HasImplOnlyScrollAnimatingElement());
}
{
GetInputHandler().ScrollEnd();
EXPECT_TRUE(GetImplAnimationHost()->HasImplOnlyScrollAnimatingElement());
EXPECT_EQ(outer_scroll->scroll_tree_index(),
host_impl_->CurrentlyScrollingNode()->id);
begin_frame_args.frame_time += base::Milliseconds(5);
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
host_impl_->DidFinishImplFrame(begin_frame_args);
EXPECT_TRUE(GetImplAnimationHost()->HasImplOnlyScrollAnimatingElement());
EXPECT_EQ(outer_scroll->scroll_tree_index(),
host_impl_->CurrentlyScrollingNode()->id);
}
{
gfx::Point position(10, 10);
auto begin_state =
BeginState(position, gfx::Vector2d(0, 50), ui::ScrollInputType::kWheel);
GetInputHandler().ScrollBegin(begin_state.get(),
ui::ScrollInputType::kWheel);
EXPECT_EQ(outer_scroll->scroll_tree_index(),
host_impl_->CurrentlyScrollingNode()->id);
}
{
begin_frame_args.frame_time += base::Milliseconds(200);
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
host_impl_->DidFinishImplFrame(begin_frame_args);
EXPECT_FALSE(GetImplAnimationHost()->HasImplOnlyScrollAnimatingElement());
EXPECT_EQ(outer_scroll->scroll_tree_index(),
host_impl_->CurrentlyScrollingNode()->id);
}
{
GetInputHandler().ScrollEnd();
EXPECT_FALSE(host_impl_->CurrentlyScrollingNode());
}
}
TEST_P(LayerTreeHostImplTest, ScrollAnimatedWhileZoomed) {
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(50, 100);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
LayerImpl* scrolling_layer = InnerViewportScrollLayer();
DrawFrame();
{
float min_page_scale = 1, max_page_scale = 4;
float page_scale_factor = 2;
host_impl_->active_tree()->PushPageScaleFromMainThread(
page_scale_factor, min_page_scale, max_page_scale);
host_impl_->active_tree()->SetPageScaleOnActiveTree(page_scale_factor);
}
{
EXPECT_EQ(
ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(10, 10), gfx::Vector2d(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
GetInputHandler().ScrollUpdate(
AnimatedUpdateState(gfx::Point(10, 10), gfx::Vector2d(0, 10)));
GetInputHandler().ScrollUpdate(
AnimatedUpdateState(gfx::Point(10, 10), gfx::Vector2d(0, 20)));
EXPECT_EQ(OuterViewportScrollLayer()->scroll_tree_index(),
host_impl_->CurrentlyScrollingNode()->id);
}
base::TimeTicks start_time = base::TimeTicks() + base::Milliseconds(100);
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
{
begin_frame_args.frame_time = start_time;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
EXPECT_NE(0, CurrentScrollOffset(scrolling_layer).y());
host_impl_->DidFinishImplFrame(begin_frame_args);
}
{
begin_frame_args.frame_time = start_time + base::Milliseconds(1000);
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
EXPECT_EQ(15, CurrentScrollOffset(scrolling_layer).y());
host_impl_->DidFinishImplFrame(begin_frame_args);
}
}
TEST_P(LayerTreeHostImplTest, ScrollAnimatedUpdateInnerViewport) {
const gfx::Size content_size(210, 1000);
const gfx::Size viewport_size(200, 200);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
DrawFrame();
{
float min_page_scale = 1, max_page_scale = 4;
float page_scale_factor = 2;
host_impl_->active_tree()->PushPageScaleFromMainThread(
page_scale_factor, min_page_scale, max_page_scale);
host_impl_->active_tree()->SetPageScaleOnActiveTree(page_scale_factor);
SetScrollOffsetDelta(InnerViewportScrollLayer(), gfx::Vector2d(50, 50));
SetScrollOffsetDelta(OuterViewportScrollLayer(), gfx::Vector2d(5, 400));
}
{
auto begin_state = BeginState(gfx::Point(10, 10), gfx::Vector2d(0, 60),
ui::ScrollInputType::kWheel);
GetInputHandler().ScrollBegin(begin_state.get(),
ui::ScrollInputType::kWheel);
GetInputHandler().ScrollUpdate(
AnimatedUpdateState(gfx::Point(10, 10), gfx::Vector2d(0, 60)));
EXPECT_EQ(OuterViewportScrollLayer()->scroll_tree_index(),
host_impl_->CurrentlyScrollingNode()->id);
ASSERT_TRUE(InnerViewportScrollLayer()->element_id());
EXPECT_TRUE(GetImplAnimationHost()->ElementHasImplOnlyScrollAnimation(
InnerViewportScrollLayer()->element_id()));
}
base::TimeTicks start_time = base::TimeTicks() + base::Milliseconds(100);
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
{
begin_frame_args.frame_time = start_time;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
EXPECT_LT(50, CurrentScrollOffset(InnerViewportScrollLayer()).y());
EXPECT_EQ(400, CurrentScrollOffset(OuterViewportScrollLayer()).y());
EXPECT_EQ(50, CurrentScrollOffset(InnerViewportScrollLayer()).x());
EXPECT_EQ(5, CurrentScrollOffset(OuterViewportScrollLayer()).x());
host_impl_->DidFinishImplFrame(begin_frame_args);
}
{
GetInputHandler().ScrollUpdate(
AnimatedUpdateState(gfx::Point(10, 10), gfx::Vector2d(0, 60)));
GetInputHandler().ScrollUpdate(
AnimatedUpdateState(gfx::Point(10, 10), gfx::Vector2d(0, 60)));
}
{
begin_frame_args.frame_time = start_time + base::Milliseconds(1000);
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
EXPECT_EQ(100, CurrentScrollOffset(InnerViewportScrollLayer()).y());
EXPECT_EQ(400, CurrentScrollOffset(OuterViewportScrollLayer()).y());
EXPECT_EQ(50, CurrentScrollOffset(InnerViewportScrollLayer()).x());
EXPECT_EQ(5, CurrentScrollOffset(OuterViewportScrollLayer()).x());
host_impl_->DidFinishImplFrame(begin_frame_args);
}
}
TEST_P(FluentOverlayScrollbarOpacityLayerTreeHostImplTest,
PaintedOverlayScrollbarTrackOpacityTest) {
auto* scrollbar = CreateAndRegisterPaintedScrollbarLayer();
scrollbar->SetOverlayScrollbarLayerOpacityAnimated(
1.f, false);
UpdateDrawProperties(host_impl_->active_tree());
EXPECT_EQ(1.f, scrollbar->draw_properties().opacity);
int const step = GetParam();
float const thickness_scale_step =
(1 - scrollbar->GetIdleThicknessScale()) / kParamSteps;
VerifyCorrectOpacityForThickness(
scrollbar,
scrollbar->GetIdleThicknessScale() + thickness_scale_step * step,
step / static_cast<float>(kParamSteps));
}
INSTANTIATE_TEST_SUITE_P(
ScaleOpacity,
FluentOverlayScrollbarOpacityLayerTreeHostImplTest,
Range(0,
FluentOverlayScrollbarOpacityLayerTreeHostImplTest::kParamSteps + 1));
TEST_F(FluentOverlayScrollbarLayerTreeHostImplTest,
FluentScrollbarFlashAfterScrollUpdate) {
auto* root_scrollbar = CreateAndRegisterPaintedScrollbarLayer();
LayerImpl* root_scroll = OuterViewportScrollLayer();
gfx::Size child_layer_size(250, 150);
gfx::Size child_scrollbar_size(gfx::Size(15, child_layer_size.height()));
auto* child =
AddScrollableLayer(root_scroll, gfx::Size(100, 100), child_layer_size);
GetTransformNode(child)->post_translation = gfx::Vector2dF(50, 50);
auto* child_scrollbar = AddLayer<PaintedScrollbarLayerImpl>(
host_impl_->active_tree(), ScrollbarOrientation::kVertical, false, true);
auto& effect_node =
CreateEffectNode(child_scrollbar, child->effect_tree_index());
SetupScrollbarLayerCommon(child, child_scrollbar);
child_scrollbar->SetEffectTreeIndex(effect_node.id);
child_scrollbar->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
child_scrollbar->SetBounds(child_scrollbar_size);
auto reset_scrollbars = [root_scrollbar, child_scrollbar](
LayerTreeImpl* active_tree,
base::OnceClosure& animation_task) {
GetEffectNode(root_scrollbar)->opacity = 0;
GetEffectNode(child_scrollbar)->opacity = 0;
active_tree->UpdateAllScrollbarGeometriesForTesting();
UpdateDrawProperties(active_tree);
animation_task.Reset();
};
reset_scrollbars(host_impl_->active_tree(), animation_task_);
host_impl_->active_tree()->DidBecomeActive();
EXPECT_EQ(root_scrollbar->Opacity(), 0);
EXPECT_EQ(child_scrollbar->Opacity(), 0);
GetInputHandler().RootScrollBegin(
BeginState(gfx::Point(20, 20), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(20, 20), gfx::Vector2d(0, 10), ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
EXPECT_EQ(root_scrollbar->Opacity(), 1.f);
EXPECT_EQ(child_scrollbar->Opacity(), 0);
EXPECT_FALSE(animation_task_.is_null());
reset_scrollbars(host_impl_->active_tree(), animation_task_);
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(70, 70), gfx::Vector2dF(0, -100),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
GetInputHandler().ScrollUpdate(
AnimatedUpdateState(gfx::Point(70, 70), gfx::Vector2d(0, -100)));
GetInputHandler().ScrollEnd();
EXPECT_TRUE(root_scrollbar->Opacity());
EXPECT_TRUE(child_scrollbar->Opacity());
EXPECT_FALSE(animation_task_.is_null());
}
TEST_F(FluentOverlayScrollbarLayerTreeHostImplTest,
PaintedOverlayLayerOnLoadOpacityTest) {
EXPECT_FLOAT_EQ(CreateAndRegisterPaintedScrollbarLayer()->Opacity(), 0.f);
}
TEST_F(FluentOverlayScrollbarLayerTreeHostImplTest,
DoesntGetHitTestedWhenInvisible) {
EXPECT_FLOAT_EQ(CreateAndRegisterPaintedScrollbarLayer()->Opacity(), 0.f);
InputHandlerPointerResult result =
GetInputHandler().MouseMoveAt(gfx::Point(352, 300));
EXPECT_FLOAT_EQ(result.scroll_delta.y(), 0u);
}
TEST_P(LayerTreeHostImplTest, FadedOutPaintedOverlayScrollbarHitTest) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
const gfx::Size viewport_size = gfx::Size(360, 600);
const gfx::Size content_size = gfx::Size(345, 3800);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
LayerImpl* scroll_layer = OuterViewportScrollLayer();
LayerTreeImpl* layer_tree_impl = host_impl_->active_tree();
auto* scrollbar = AddLayer<NinePatchThumbScrollbarLayerImpl>(
layer_tree_impl, ScrollbarOrientation::kVertical, false);
SetupScrollbarLayerCommon(scroll_layer, scrollbar);
scrollbar->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
const gfx::Size scrollbar_size = gfx::Size(15, 600);
scrollbar->SetBounds(scrollbar_size);
scrollbar->SetThumbThickness(15);
scrollbar->SetThumbLength(50);
scrollbar->SetTrackStart(0);
scrollbar->SetTrackLength(575);
scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(345, 0));
layer_tree_impl->UpdateAllScrollbarGeometriesForTesting();
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(350, 18), gfx::Vector2dF(),
ui::ScrollInputType::kScrollbar)
.get(),
ui::ScrollInputType::kScrollbar);
TestInputHandlerClient input_handler_client;
GetInputHandler().BindToClient(&input_handler_client);
auto& scrollbar_effect_node = CreateEffectNode(scrollbar);
scrollbar_effect_node.opacity = 0.8;
GetInputHandler().MouseDown(gfx::PointF(350, 18),
false);
InputHandlerPointerResult result =
GetInputHandler().MouseMoveAt(gfx::Point(350, 28));
EXPECT_GT(result.scroll_delta.y(), 0u);
GetInputHandler().MouseUp(gfx::PointF(350, 28));
scrollbar_effect_node.opacity = 0;
GetInputHandler().MouseDown(gfx::PointF(350, 18),
false);
result = GetInputHandler().MouseMoveAt(gfx::Point(350, 28));
EXPECT_EQ(result.scroll_delta.y(), 0u);
GetInputHandler().MouseUp(gfx::PointF(350, 28));
host_impl_->ReleaseLayerTreeFrameSink();
host_impl_ = nullptr;
}
TEST_P(LayerTreeHostImplTest, ScrollOnLargeThumb) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
const gfx::Size viewport_size = gfx::Size(360, 600);
const gfx::Size content_size = gfx::Size(345, 3800);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
LayerImpl* scroll_layer = OuterViewportScrollLayer();
LayerTreeImpl* layer_tree_impl = host_impl_->active_tree();
layer_tree_impl->set_painted_device_scale_factor(2.5f);
auto* scrollbar = AddLayer<PaintedScrollbarLayerImpl>(
layer_tree_impl, ScrollbarOrientation::kVertical, false, true);
SetupScrollbarLayerCommon(scroll_layer, scrollbar);
scrollbar->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
const gfx::Size scrollbar_size = gfx::Size(15, 600);
scrollbar->SetBounds(scrollbar_size);
scrollbar->SetThumbThickness(15);
scrollbar->SetThumbLength(575);
scrollbar->SetTrackRect(gfx::Rect(0, 15, 15, 575));
scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(345, 0));
TestInputHandlerClient input_handler_client;
GetInputHandler().BindToClient(&input_handler_client);
GetInputHandler().MouseDown(gfx::PointF(350, 300),
false);
EXPECT_TRUE(GetInputHandler()
.scrollbar_controller_for_testing()
->drag_state_.has_value());
InputHandlerPointerResult res =
GetInputHandler().MouseMoveAt(gfx::Point(350, 600));
EXPECT_EQ(res.scroll_delta.y(), 0);
res = GetInputHandler().MouseMoveAt(gfx::Point(350, 0));
EXPECT_EQ(res.scroll_delta.y(), 0);
GetInputHandler().MouseUp(gfx::PointF(350, 0));
EXPECT_TRUE(!GetInputHandler()
.scrollbar_controller_for_testing()
->drag_state_.has_value());
host_impl_->ReleaseLayerTreeFrameSink();
host_impl_ = nullptr;
}
TEST_P(LayerTreeHostImplTest, AutoscrollOnDeletedScrollbar) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
const gfx::Size viewport_size = gfx::Size(360, 600);
const gfx::Size content_size = gfx::Size(345, 3800);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
LayerImpl* scroll_layer = OuterViewportScrollLayer();
LayerTreeImpl* layer_tree_impl = host_impl_->active_tree();
auto* scrollbar = AddLayer<PaintedScrollbarLayerImpl>(
layer_tree_impl, ScrollbarOrientation::kVertical, false, true);
SetupScrollbarLayerCommon(scroll_layer, scrollbar);
scrollbar->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
const gfx::Size scrollbar_size = gfx::Size(15, 600);
scrollbar->SetBounds(scrollbar_size);
scrollbar->SetThumbThickness(15);
scrollbar->SetThumbLength(50);
scrollbar->SetTrackRect(gfx::Rect(0, 15, 15, 575));
scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(345, 0));
TestInputHandlerClient input_handler_client;
GetInputHandler().BindToClient(&input_handler_client);
GetInputHandler().MouseDown(gfx::PointF(350, 580),
false);
EXPECT_TRUE(!host_impl_->CurrentlyScrollingNode());
host_impl_->DidUnregisterScrollbarLayer(scroll_layer->element_id(),
ScrollbarOrientation::kHorizontal);
EXPECT_TRUE(GetInputHandler()
.scrollbar_controller_for_testing()
->AutoscrollTaskIsScheduled());
layer_tree_impl->UnregisterScrollbar(scrollbar);
EXPECT_TRUE(GetInputHandler()
.scrollbar_controller_for_testing()
->AutoscrollTaskIsScheduled());
GetInputHandler()
.scrollbar_controller_for_testing()
->cancelable_autoscroll_task_->callback()
.Run();
GetInputHandler()
.scrollbar_controller_for_testing()
->cancelable_autoscroll_task_.reset();
EXPECT_EQ(GetInputHandler()
.scrollbar_controller_for_testing()
->autoscroll_state_->status,
ScrollbarController::AutoScrollStatus::kAutoscrollReady);
layer_tree_impl->RegisterScrollbar(scrollbar);
EXPECT_TRUE(GetInputHandler()
.scrollbar_controller_for_testing()
->AutoscrollTaskIsScheduled());
GetInputHandler()
.scrollbar_controller_for_testing()
->cancelable_autoscroll_task_->callback()
.Run();
GetInputHandler()
.scrollbar_controller_for_testing()
->cancelable_autoscroll_task_.reset();
EXPECT_EQ(GetInputHandler()
.scrollbar_controller_for_testing()
->autoscroll_state_->status,
ScrollbarController::AutoScrollStatus::kAutoscrollScrolling);
GetInputHandler().MouseUp(gfx::PointF(350, 580));
GetInputHandler().ScrollEnd();
EXPECT_TRUE(!host_impl_->CurrentlyScrollingNode());
host_impl_->ReleaseLayerTreeFrameSink();
host_impl_ = nullptr;
}
TEST_P(LayerTreeHostImplTest, PointerMoveOutOfSequence) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
const gfx::Size viewport_size = gfx::Size(360, 600);
const gfx::Size content_size = gfx::Size(345, 3800);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
LayerImpl* scroll_layer = OuterViewportScrollLayer();
LayerTreeImpl* layer_tree_impl = host_impl_->active_tree();
auto* scrollbar = AddLayer<PaintedScrollbarLayerImpl>(
layer_tree_impl, ScrollbarOrientation::kVertical, false, true);
SetupScrollbarLayerCommon(scroll_layer, scrollbar);
scrollbar->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
const gfx::Size scrollbar_size = gfx::Size(15, 600);
scrollbar->SetBounds(scrollbar_size);
scrollbar->SetThumbThickness(15);
scrollbar->SetThumbLength(50);
scrollbar->SetTrackRect(gfx::Rect(0, 15, 15, 575));
scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(345, 0));
layer_tree_impl->UpdateAllScrollbarGeometriesForTesting();
TestInputHandlerClient input_handler_client;
GetInputHandler().BindToClient(&input_handler_client);
GetInputHandler().MouseDown(gfx::PointF(350, 18),
false);
EXPECT_TRUE(!host_impl_->CurrentlyScrollingNode());
InputHandlerPointerResult result =
GetInputHandler().MouseMoveAt(gfx::Point(350, 19));
EXPECT_GT(result.scroll_delta.y(), 0u);
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
begin_frame_args.frame_time = base::TimeTicks::Now();
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(350, 18), gfx::Vector2dF(),
ui::ScrollInputType::kScrollbar)
.get(),
ui::ScrollInputType::kScrollbar);
EXPECT_TRUE(host_impl_->CurrentlyScrollingNode());
result = GetInputHandler().MouseMoveAt(gfx::Point(350, 20));
EXPECT_GT(result.scroll_delta.y(), 0u);
GetInputHandler().MouseUp(gfx::PointF(350, 20));
GetInputHandler().ScrollEnd();
EXPECT_TRUE(!host_impl_->CurrentlyScrollingNode());
host_impl_->ReleaseLayerTreeFrameSink();
host_impl_ = nullptr;
}
TEST_P(LayerTreeHostImplTest, FadedOutPaintedScrollbarHitTest) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
const gfx::Size viewport_size = gfx::Size(360, 600);
const gfx::Size content_size = gfx::Size(345, 3800);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
LayerImpl* scroll_layer = OuterViewportScrollLayer();
LayerTreeImpl* layer_tree_impl = host_impl_->active_tree();
auto* scrollbar = AddLayer<PaintedScrollbarLayerImpl>(
layer_tree_impl, ScrollbarOrientation::kVertical, false, true);
SetupScrollbarLayerCommon(scroll_layer, scrollbar);
scrollbar->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
const gfx::Size scrollbar_size = gfx::Size(15, 600);
scrollbar->SetBounds(scrollbar_size);
scrollbar->SetThumbThickness(15);
scrollbar->SetThumbLength(50);
scrollbar->SetTrackRect(gfx::Rect(0, 15, 15, 575));
scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(345, 0));
TestInputHandlerClient input_handler_client;
GetInputHandler().BindToClient(&input_handler_client);
scrollbar->SetScrollbarPaintedOpacity(0);
InputHandlerPointerResult result = GetInputHandler().MouseDown(
gfx::PointF(350, 100), false);
EXPECT_EQ(result.scroll_delta.y(), 0u);
scrollbar->SetScrollbarPaintedOpacity(1);
result = GetInputHandler().MouseDown(gfx::PointF(350, 100),
false);
EXPECT_GT(result.scroll_delta.y(), 0u);
host_impl_->ReleaseLayerTreeFrameSink();
host_impl_ = nullptr;
}
TEST_P(LayerTreeHostImplTest, SingleGSUForScrollbarThumbDragPerFrame) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
const gfx::Size viewport_size = gfx::Size(360, 600);
const gfx::Size content_size = gfx::Size(345, 3800);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
LayerImpl* scroll_layer = OuterViewportScrollLayer();
LayerTreeImpl* layer_tree_impl = host_impl_->active_tree();
auto* scrollbar = AddLayer<PaintedScrollbarLayerImpl>(
layer_tree_impl, ScrollbarOrientation::kVertical, false, true);
SetupScrollbarLayer(scroll_layer, scrollbar);
const gfx::Size scrollbar_size = gfx::Size(15, 600);
scrollbar->SetBounds(scrollbar_size);
scrollbar->SetThumbThickness(15);
scrollbar->SetThumbLength(50);
scrollbar->SetTrackRect(gfx::Rect(0, 15, 15, 575));
scrollbar->SetBackButtonRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(15, 15)));
scrollbar->SetForwardButtonRect(
gfx::Rect(gfx::Point(0, 570), gfx::Size(15, 15)));
scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(345, 0));
layer_tree_impl->UpdateAllScrollbarGeometriesForTesting();
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(350, 18), gfx::Vector2dF(),
ui::ScrollInputType::kScrollbar)
.get(),
ui::ScrollInputType::kScrollbar);
TestInputHandlerClient input_handler_client;
GetInputHandler().BindToClient(&input_handler_client);
base::TimeTicks start_time = base::TimeTicks() + base::Milliseconds(200);
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
begin_frame_args.frame_time = start_time;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
InputHandlerPointerResult result = GetInputHandler().MouseDown(
gfx::PointF(350, 18), false);
EXPECT_EQ(result.scroll_delta.y(), 0u);
result = GetInputHandler().MouseMoveAt(gfx::Point(350, 19));
EXPECT_GT(result.scroll_delta.y(), 0u);
result = GetInputHandler().MouseMoveAt(gfx::Point(350, 20));
EXPECT_EQ(result.scroll_delta.y(), 0u);
host_impl_->DidFinishImplFrame(begin_frame_args);
begin_frame_args.frame_time = start_time + base::Milliseconds(250);
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
result = GetInputHandler().MouseMoveAt(gfx::Point(350, 21));
EXPECT_GT(result.scroll_delta.y(), 0u);
result = GetInputHandler().MouseUp(gfx::PointF(350, 21));
EXPECT_EQ(result.scroll_delta.y(), 0u);
host_impl_->ReleaseLayerTreeFrameSink();
host_impl_ = nullptr;
}
TEST_P(LayerTreeHostImplTest,
ClearTrackingManagerOnLayerTreeFrameSinkLoseContext) {
const gfx::Size content_size(1000, 10000);
const gfx::Size viewport_size(500, 500);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
DrawFrame();
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(250, 250), gfx::Vector2dF(),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
for (int i = 0; i < 30; i++) {
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2d(0, 10), ui::ScrollInputType::kTouchscreen));
EventMetrics::List events_metrics;
events_metrics.push_back(ScrollUpdateEventMetrics::Create(
ui::EventType::kGestureScrollUpdate, ui::ScrollInputType::kTouchscreen,
false,
i == 0 ? ScrollUpdateEventMetrics::ScrollUpdateType::kStarted
: ScrollUpdateEventMetrics::ScrollUpdateType::kContinued,
10.0f, base::TimeTicks::Now(),
base::TimeTicks::Now() + base::Milliseconds(1), base::TimeTicks(),
base::IdType64<class ui::LatencyInfo>(123)));
host_impl_->active_tree()->AppendEventsMetricsFromMainThread(
std::move(events_metrics));
DrawFrame();
}
host_impl_->DidLoseLayerTreeFrameSink();
}
TEST_P(LayerTreeHostImplTest, FrameCounterReset) {
FrameSorter* frame_sorter = host_impl_->frame_sorter_for_testing();
EXPECT_EQ(frame_sorter->total_frames(), 0u);
FrameInfo frame_info;
frame_info.final_state = FrameInfo::FrameFinalState::kPresentedAll;
frame_sorter->AddFrameInfoToBuffer(frame_info);
EXPECT_EQ(frame_sorter->total_frames(), 1u);
auto interval = base::Milliseconds(16);
base::TimeTicks now = base::TimeTicks::Now();
auto deadline = now + interval;
viz::BeginFrameArgs args = viz::BeginFrameArgs::Create(
BEGINFRAME_FROM_HERE, 1u , 2u , now,
deadline, interval, viz::BeginFrameArgs::NORMAL);
frame_sorter->AddNewFrame(args);
frame_sorter->AddFrameResult(
args, CreateFakeFrameInfo(FrameInfo::FrameFinalState::kDropped));
EXPECT_EQ(frame_sorter->total_dropped(), 0u);
BeginMainFrameMetrics begin_frame_metrics;
begin_frame_metrics.should_measure_smoothness = true;
host_impl_->ReadyToCommit(true,
&begin_frame_metrics, false);
frame_sorter->AddNewFrame(args);
frame_sorter->AddFrameResult(
args, CreateFakeFrameInfo(FrameInfo::FrameFinalState::kDropped));
frame_sorter->AddFrameInfoToBuffer(frame_info);
host_impl_->SetActiveURL(GURL(), 1u);
EXPECT_EQ(frame_sorter->total_frames(), 0u);
EXPECT_EQ(frame_sorter->total_dropped(), 0u);
}
TEST_P(LayerTreeHostImplTest, FrameCounterNotReset) {
FrameSorter* frame_sorter = host_impl_->frame_sorter_for_testing();
EXPECT_EQ(frame_sorter->total_frames(), 0u);
auto interval = base::Milliseconds(16);
base::TimeTicks now = base::TimeTicks::Now();
auto deadline = now + interval;
viz::BeginFrameArgs arg1 = viz::BeginFrameArgs::Create(
BEGINFRAME_FROM_HERE, 1u , 1u , now,
deadline, interval, viz::BeginFrameArgs::NORMAL);
BeginMainFrameMetrics begin_frame_metrics;
begin_frame_metrics.should_measure_smoothness = true;
host_impl_->ReadyToCommit(true,
&begin_frame_metrics, false);
EXPECT_EQ(frame_sorter->total_frames(), 0u);
FrameInfo frame_info;
frame_info.final_state = FrameInfo::FrameFinalState::kPresentedAll;
frame_sorter->AddFrameInfoToBuffer(frame_info);
EXPECT_EQ(frame_sorter->total_frames(), 1u);
now = deadline;
deadline = now + interval;
viz::BeginFrameArgs arg2 = viz::BeginFrameArgs::Create(
BEGINFRAME_FROM_HERE, 1u , 2u , now,
deadline, interval, viz::BeginFrameArgs::NORMAL);
host_impl_->ReadyToCommit(true,
&begin_frame_metrics, false);
EXPECT_EQ(frame_sorter->total_frames(), 1u);
}
TEST_P(LayerTreeHostImplTest, AutoscrollTaskAbort) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
const gfx::Size viewport_size = gfx::Size(360, 600);
const gfx::Size content_size = gfx::Size(345, 3800);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
LayerImpl* scroll_layer = OuterViewportScrollLayer();
LayerTreeImpl* layer_tree_impl = host_impl_->active_tree();
auto* scrollbar = AddLayer<PaintedScrollbarLayerImpl>(
layer_tree_impl, ScrollbarOrientation::kVertical,
false,
false);
SetupScrollbarLayer(scroll_layer, scrollbar);
const gfx::Size scrollbar_size = gfx::Size(15, 600);
scrollbar->SetBounds(scrollbar_size);
host_impl_->set_force_smooth_wheel_scrolling_for_testing(true);
scrollbar->SetThumbThickness(15);
scrollbar->SetThumbLength(50);
scrollbar->SetTrackRect(gfx::Rect(0, 15, 15, 575));
scrollbar->SetBackButtonRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(15, 15)));
scrollbar->SetForwardButtonRect(
gfx::Rect(gfx::Point(0, 570), gfx::Size(15, 15)));
scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(345, 0));
TestInputHandlerClient input_handler_client;
GetInputHandler().BindToClient(&input_handler_client);
{
InputHandlerPointerResult result = GetInputHandler().MouseDown(
gfx::PointF(350, 575), false);
EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll);
auto begin_state = BeginState(gfx::Point(350, 575), gfx::Vector2d(0, 40),
ui::ScrollInputType::kScrollbar);
EXPECT_EQ(
ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(begin_state.get(), ui::ScrollInputType::kScrollbar)
.thread);
EXPECT_TRUE(GetInputHandler()
.scrollbar_controller_for_testing()
->AutoscrollTaskIsScheduled());
}
{
InputHandlerPointerResult result = GetInputHandler().MouseDown(
gfx::PointF(350, 575), false);
EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll);
GetInputHandler().ScrollEnd();
EXPECT_FALSE(GetInputHandler()
.scrollbar_controller_for_testing()
->AutoscrollTaskIsScheduled());
}
host_impl_->ReleaseLayerTreeFrameSink();
host_impl_ = nullptr;
}
TEST_P(LayerTreeHostImplTest, JumpOnScrollbarClick) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
const gfx::Size viewport_size = gfx::Size(360, 600);
const gfx::Size content_size = gfx::Size(345, 3800);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
LayerImpl* scroll_layer = OuterViewportScrollLayer();
LayerTreeImpl* layer_tree_impl = host_impl_->active_tree();
auto* scrollbar = AddLayer<PaintedScrollbarLayerImpl>(
layer_tree_impl, ScrollbarOrientation::kVertical,
false,
false);
SetupScrollbarLayer(scroll_layer, scrollbar);
const gfx::Size scrollbar_size = gfx::Size(15, 600);
scrollbar->SetBounds(scrollbar_size);
host_impl_->set_force_smooth_wheel_scrolling_for_testing(true);
scrollbar->SetThumbThickness(15);
scrollbar->SetThumbLength(50);
scrollbar->SetTrackRect(gfx::Rect(0, 15, 15, 575));
scrollbar->SetBackButtonRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(15, 15)));
scrollbar->SetForwardButtonRect(
gfx::Rect(gfx::Point(0, 570), gfx::Size(15, 15)));
scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(345, 0));
layer_tree_impl->UpdateAllScrollbarGeometriesForTesting();
TestInputHandlerClient input_handler_client;
GetInputHandler().BindToClient(&input_handler_client);
{
scrollbar->SetJumpOnTrackClick(false);
InputHandlerPointerResult result = GetInputHandler().MouseDown(
gfx::PointF(350, 400), false);
EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll);
EXPECT_EQ(result.scroll_delta.y(),
std::max(viewport_size.height() * kMinFractionToStepWhenPaging,
static_cast<float>(viewport_size.height() -
kMaxOverlapBetweenPages)));
result = GetInputHandler().MouseUp(gfx::PointF(350, 400));
EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll);
EXPECT_EQ(result.scroll_delta.y(), 0);
}
{
scrollbar->SetJumpOnTrackClick(false);
InputHandlerPointerResult result = GetInputHandler().MouseDown(
gfx::PointF(350, 400), true);
EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll);
EXPECT_FLOAT_EQ(result.scroll_delta.y(), 2194.2856f);
result = GetInputHandler().MouseUp(gfx::PointF(350, 400));
EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll);
EXPECT_EQ(result.scroll_delta.y(), 0);
}
{
scrollbar->SetJumpOnTrackClick(true);
InputHandlerPointerResult result = GetInputHandler().MouseDown(
gfx::PointF(350, 400), false);
EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll);
EXPECT_FLOAT_EQ(result.scroll_delta.y(), 2194.2856f);
result = GetInputHandler().MouseUp(gfx::PointF(350, 400));
EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll);
EXPECT_EQ(result.scroll_delta.y(), 0);
}
{
scrollbar->SetJumpOnTrackClick(true);
InputHandlerPointerResult result = GetInputHandler().MouseDown(
gfx::PointF(350, 400), true);
EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll);
EXPECT_EQ(result.scroll_delta.y(),
std::max(viewport_size.height() * kMinFractionToStepWhenPaging,
static_cast<float>(viewport_size.height() -
kMaxOverlapBetweenPages)));
result = GetInputHandler().MouseUp(gfx::PointF(350, 400));
EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll);
EXPECT_EQ(result.scroll_delta.y(), 0);
}
host_impl_->ReleaseLayerTreeFrameSink();
host_impl_ = nullptr;
}
TEST_P(LayerTreeHostImplTest, ThumbDragAfterJumpClickOrThumbClick) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
const gfx::Size viewport_size = gfx::Size(360, 600);
const gfx::Size content_size = gfx::Size(345, 3800);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
LayerImpl* scroll_layer = OuterViewportScrollLayer();
LayerTreeImpl* layer_tree_impl = host_impl_->active_tree();
auto* scrollbar = AddLayer<PaintedScrollbarLayerImpl>(
layer_tree_impl, ScrollbarOrientation::kVertical,
false,
false);
SetupScrollbarLayer(scroll_layer, scrollbar);
const gfx::Size scrollbar_size = gfx::Size(15, 600);
scrollbar->SetBounds(scrollbar_size);
host_impl_->set_force_smooth_wheel_scrolling_for_testing(true);
const int thumb_len = 50;
scrollbar->SetThumbThickness(15);
scrollbar->SetThumbLength(thumb_len);
scrollbar->SetTrackRect(gfx::Rect(0, 15, 15, 575));
scrollbar->SetBackButtonRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(15, 15)));
scrollbar->SetForwardButtonRect(
gfx::Rect(gfx::Point(0, 570), gfx::Size(15, 15)));
scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(345, 0));
layer_tree_impl->UpdateAllScrollbarGeometriesForTesting();
TestInputHandlerClient input_handler_client;
GetInputHandler().BindToClient(&input_handler_client);
{
scrollbar->SetJumpOnTrackClick(false);
EXPECT_FALSE(GetInputHandler()
.scrollbar_controller_for_testing()
->drag_state_.has_value());
const InputHandlerPointerResult result =
GetInputHandler().scrollbar_controller_for_testing()->HandlePointerDown(
gfx::PointF(350, 560), true);
EXPECT_EQ(0, result.scroll_delta.x());
EXPECT_FLOAT_EQ(result.scroll_delta.y(), 3169.5239f);
EXPECT_TRUE(GetInputHandler()
.scrollbar_controller_for_testing()
->drag_state_.has_value());
EXPECT_FLOAT_EQ(GetInputHandler()
.scrollbar_controller_for_testing()
->drag_state_->scroll_position_at_start_,
0.0f);
EXPECT_FLOAT_EQ(GetInputHandler()
.scrollbar_controller_for_testing()
->drag_state_->drag_origin.y(),
15.0f + thumb_len / 2.0f);
GetInputHandler().scrollbar_controller_for_testing()->HandlePointerUp(
gfx::PointF(350, 560));
}
{
scrollbar->SetJumpOnTrackClick(false);
EXPECT_FALSE(GetInputHandler()
.scrollbar_controller_for_testing()
->drag_state_.has_value());
const InputHandlerPointerResult result =
GetInputHandler().scrollbar_controller_for_testing()->HandlePointerDown(
gfx::PointF(350, 60), true);
EXPECT_EQ(0, result.scroll_delta.x());
EXPECT_EQ(0, result.scroll_delta.y());
EXPECT_TRUE(GetInputHandler()
.scrollbar_controller_for_testing()
->drag_state_.has_value());
EXPECT_FLOAT_EQ(GetInputHandler()
.scrollbar_controller_for_testing()
->drag_state_->scroll_position_at_start_,
0.0f);
EXPECT_FLOAT_EQ(GetInputHandler()
.scrollbar_controller_for_testing()
->drag_state_->drag_origin.y(),
60.0f);
GetInputHandler().scrollbar_controller_for_testing()->HandlePointerUp(
gfx::PointF(350, 60));
}
{
scrollbar->SetJumpOnTrackClick(false);
EXPECT_FALSE(GetInputHandler()
.scrollbar_controller_for_testing()
->drag_state_.has_value());
const InputHandlerPointerResult result =
GetInputHandler().scrollbar_controller_for_testing()->HandlePointerDown(
gfx::PointF(350, 580), true);
EXPECT_EQ(0, result.scroll_delta.x());
EXPECT_EQ(result.scroll_delta.y(),
kPixelsPerLineStep * GetInputHandler()
.scrollbar_controller_for_testing()
->ScreenSpaceScaleFactor());
EXPECT_FALSE(GetInputHandler()
.scrollbar_controller_for_testing()
->drag_state_.has_value());
GetInputHandler().scrollbar_controller_for_testing()->HandlePointerUp(
gfx::PointF(350, 580));
}
host_impl_->ReleaseLayerTreeFrameSink();
host_impl_ = nullptr;
}
TEST_P(LayerTreeHostImplTest, AbortAnimatedScrollBeforeStartingAutoscroll) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
const gfx::Size viewport_size = gfx::Size(360, 600);
const gfx::Size content_size = gfx::Size(345, 3800);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
LayerImpl* scroll_layer = OuterViewportScrollLayer();
LayerTreeImpl* layer_tree_impl = host_impl_->active_tree();
auto* scrollbar = AddLayer<PaintedScrollbarLayerImpl>(
layer_tree_impl, ScrollbarOrientation::kVertical,
false,
false);
SetupScrollbarLayer(scroll_layer, scrollbar);
const gfx::Size scrollbar_size = gfx::Size(15, 600);
scrollbar->SetBounds(scrollbar_size);
host_impl_->set_force_smooth_wheel_scrolling_for_testing(true);
scrollbar->SetThumbThickness(15);
scrollbar->SetThumbLength(50);
scrollbar->SetTrackRect(gfx::Rect(0, 15, 15, 575));
scrollbar->SetBackButtonRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(15, 15)));
scrollbar->SetForwardButtonRect(
gfx::Rect(gfx::Point(0, 570), gfx::Size(15, 15)));
scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(345, 0));
TestInputHandlerClient input_handler_client;
GetInputHandler().BindToClient(&input_handler_client);
{
GetInputHandler().scrollbar_controller_for_testing()->HandlePointerDown(
gfx::PointF(350, 560), false);
auto begin_state = BeginState(gfx::Point(350, 560), gfx::Vector2d(0, 40),
ui::ScrollInputType::kScrollbar);
EXPECT_EQ(
ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(begin_state.get(), ui::ScrollInputType::kScrollbar)
.thread);
auto update_state = UpdateState(gfx::Point(350, 560), gfx::Vector2dF(0, 40),
ui::ScrollInputType::kScrollbar);
update_state.data()->delta_granularity =
ui::ScrollGranularity::kScrollByPixel;
GetInputHandler().ScrollUpdate(update_state);
EXPECT_TRUE(GetInputHandler()
.scrollbar_controller_for_testing()
->ScrollbarScrollIsActive());
EXPECT_TRUE(GetImplAnimationHost()->HasImplOnlyScrollAnimatingElement());
}
{
GetInputHandler().scrollbar_controller_for_testing()->autoscroll_state_ =
ScrollbarController::AutoScrollState();
GetInputHandler()
.scrollbar_controller_for_testing()
->autoscroll_state_->velocity = 800;
GetInputHandler()
.scrollbar_controller_for_testing()
->autoscroll_state_->pressed_scrollbar_part =
ScrollbarPart::kForwardTrack;
GetInputHandler().scrollbar_controller_for_testing()->StartAutoScroll();
EXPECT_TRUE(GetImplAnimationHost()->HasImplOnlyScrollAnimatingElement());
}
host_impl_->ReleaseLayerTreeFrameSink();
host_impl_ = nullptr;
}
TEST_P(LayerTreeHostImplTest, AnimatedScrollYielding) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
const gfx::Size viewport_size = gfx::Size(360, 600);
const gfx::Size content_size = gfx::Size(345, 3800);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
LayerImpl* scroll_layer = OuterViewportScrollLayer();
LayerTreeImpl* layer_tree_impl = host_impl_->active_tree();
auto* scrollbar = AddLayer<PaintedScrollbarLayerImpl>(
layer_tree_impl, ScrollbarOrientation::kVertical,
false,
false);
SetupScrollbarLayer(scroll_layer, scrollbar);
const gfx::Size scrollbar_size = gfx::Size(15, 600);
scrollbar->SetBounds(scrollbar_size);
host_impl_->set_force_smooth_wheel_scrolling_for_testing(true);
scrollbar->SetThumbThickness(15);
scrollbar->SetThumbLength(50);
scrollbar->SetTrackRect(gfx::Rect(0, 15, 15, 575));
scrollbar->SetBackButtonRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(15, 15)));
scrollbar->SetForwardButtonRect(
gfx::Rect(gfx::Point(0, 570), gfx::Size(15, 15)));
scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(345, 0));
TestInputHandlerClient input_handler_client;
GetInputHandler().BindToClient(&input_handler_client);
{
GetInputHandler().scrollbar_controller_for_testing()->HandlePointerDown(
gfx::PointF(350, 560), false);
auto begin_state = BeginState(gfx::Point(350, 560), gfx::Vector2d(0, 40),
ui::ScrollInputType::kScrollbar);
EXPECT_EQ(
ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(begin_state.get(), ui::ScrollInputType::kScrollbar)
.thread);
auto update_state = UpdateState(gfx::Point(350, 560), gfx::Vector2dF(0, 40),
ui::ScrollInputType::kScrollbar);
update_state.data()->delta_granularity =
ui::ScrollGranularity::kScrollByPixel;
GetInputHandler().ScrollUpdate(update_state);
EXPECT_TRUE(GetInputHandler()
.scrollbar_controller_for_testing()
->ScrollbarScrollIsActive());
EXPECT_TRUE(GetImplAnimationHost()->HasImplOnlyScrollAnimatingElement());
}
{
auto begin_state = BeginState(gfx::Point(), gfx::Vector2d(350, 560),
ui::ScrollInputType::kWheel);
begin_state->data()->delta_granularity =
ui::ScrollGranularity::kScrollByPixel;
GetInputHandler().ScrollEnd();
EXPECT_FALSE(
GetInputHandler().scrollbar_controller_for_testing()->ScrollbarLayer());
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(begin_state.get(), ui::ScrollInputType::kWheel)
.thread);
EXPECT_FALSE(GetImplAnimationHost()->HasImplOnlyScrollAnimatingElement());
GetInputHandler().ScrollUpdate(
AnimatedUpdateState(gfx::Point(350, 560), gfx::Vector2d(0, 40)));
EXPECT_TRUE(GetImplAnimationHost()->HasImplOnlyScrollAnimatingElement());
GetInputHandler().scrollbar_controller_for_testing()->WillBeginImplFrame();
}
host_impl_->ReleaseLayerTreeFrameSink();
host_impl_ = nullptr;
}
TEST_P(LayerTreeHostImplTest, ThumbDragScrollerLengthIncrease) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
const gfx::Size viewport_size = gfx::Size(360, 600);
const gfx::Size content_size = gfx::Size(345, 3800);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
LayerImpl* scroll_layer = OuterViewportScrollLayer();
LayerTreeImpl* layer_tree_impl = host_impl_->active_tree();
auto* scrollbar = AddLayer<PaintedScrollbarLayerImpl>(
layer_tree_impl, ScrollbarOrientation::kVertical,
false,
false);
SetupScrollbarLayer(scroll_layer, scrollbar);
const gfx::Size scrollbar_size = gfx::Size(15, 600);
scrollbar->SetBounds(scrollbar_size);
scrollbar->SetThumbThickness(15);
scrollbar->SetThumbLength(50);
scrollbar->SetTrackRect(gfx::Rect(0, 15, 15, 575));
scrollbar->SetBackButtonRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(15, 15)));
scrollbar->SetForwardButtonRect(
gfx::Rect(gfx::Point(0, 570), gfx::Size(15, 15)));
scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(345, 0));
layer_tree_impl->UpdateAllScrollbarGeometriesForTesting();
TestInputHandlerClient input_handler_client;
GetInputHandler().BindToClient(&input_handler_client);
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
base::TimeTicks start_time = base::TimeTicks() + base::Milliseconds(200);
begin_frame_args.frame_time = start_time;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
InputHandlerPointerResult result = GetInputHandler().MouseDown(
gfx::PointF(350, 18), false);
EXPECT_EQ(result.scroll_delta.y(), 0);
EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll);
host_impl_->DidFinishImplFrame(begin_frame_args);
begin_frame_args.frame_time = start_time + base::Milliseconds(250);
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
result = GetInputHandler().MouseMoveAt(gfx::Point(350, 20));
EXPECT_FLOAT_EQ(result.scroll_delta.y(), 12.190476f);
scrollbar->SetScrollLayerLength(7000);
host_impl_->DidFinishImplFrame(begin_frame_args);
begin_frame_args.frame_time = start_time + base::Milliseconds(300);
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
result = GetInputHandler().MouseMoveAt(gfx::Point(350, 22));
EXPECT_EQ(result.scroll_delta.y(), 0);
host_impl_->DidFinishImplFrame(begin_frame_args);
begin_frame_args.frame_time = start_time + base::Milliseconds(350);
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
result = GetInputHandler().MouseMoveAt(gfx::Point(350, 26));
EXPECT_FLOAT_EQ(result.scroll_delta.y(), 48.761906f);
GetInputHandler().MouseUp(gfx::PointF(350, 26));
host_impl_->DidFinishImplFrame(begin_frame_args);
host_impl_->ReleaseLayerTreeFrameSink();
host_impl_ = nullptr;
}
TEST_P(LayerTreeHostImplTest, MainThreadFallback) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
const gfx::Size viewport_size = gfx::Size(360, 600);
const gfx::Size content_size = gfx::Size(345, 3800);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
LayerImpl* scroll_layer = OuterViewportScrollLayer();
LayerTreeImpl* layer_tree_impl = host_impl_->active_tree();
auto* scrollbar = AddLayer<PaintedScrollbarLayerImpl>(
layer_tree_impl, ScrollbarOrientation::kVertical, false, true);
SetupScrollbarLayer(scroll_layer, scrollbar);
const gfx::Size scrollbar_size = gfx::Size(15, 600);
scrollbar->SetBounds(scrollbar_size);
scrollbar->SetThumbThickness(15);
scrollbar->SetThumbLength(50);
scrollbar->SetTrackRect(gfx::Rect(0, 15, 15, 575));
scrollbar->SetBackButtonRect(
gfx::Rect(gfx::Point(345, 0), gfx::Size(15, 15)));
scrollbar->SetForwardButtonRect(
gfx::Rect(gfx::Point(345, 570), gfx::Size(15, 15)));
scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(345, 0));
TestInputHandlerClient input_handler_client;
GetInputHandler().BindToClient(&input_handler_client);
InputHandlerPointerResult compositor_threaded_scrolling_result =
GetInputHandler().MouseDown(gfx::PointF(350, 500),
false);
GetInputHandler().MouseUp(gfx::PointF(350, 500));
EXPECT_EQ(compositor_threaded_scrolling_result.scroll_delta.y(),
std::max(viewport_size.height() * kMinFractionToStepWhenPaging,
static_cast<float>(viewport_size.height() -
kMaxOverlapBetweenPages)));
EXPECT_FALSE(GetScrollNode(scroll_layer)->main_thread_repaint_reasons);
GetScrollNode(scroll_layer)->main_thread_repaint_reasons =
MainThreadScrollingReason::kPreferNonCompositedScrolling;
compositor_threaded_scrolling_result = GetInputHandler().MouseDown(
gfx::PointF(350, 500), false);
GetInputHandler().MouseUp(gfx::PointF(350, 500));
EXPECT_EQ(compositor_threaded_scrolling_result.scroll_delta.y(),
std::max(viewport_size.height() * kMinFractionToStepWhenPaging,
static_cast<float>(viewport_size.height() -
kMaxOverlapBetweenPages)));
host_impl_->ReleaseLayerTreeFrameSink();
host_impl_ = nullptr;
}
TEST_P(LayerTreeHostImplTest, SecondScrollAnimatedBeginNotIgnored) {
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(50, 100);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
GetInputHandler().ScrollEnd();
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
}
TEST_P(ClientModeLayerTreeHostImplTest,
AnimatedScrollUpdateTargetBeforeStarting) {
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(50, 100);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
DrawFrame();
base::TimeTicks start_time = base::TimeTicks() + base::Milliseconds(200);
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
begin_frame_args.frame_time = start_time;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->UpdateAnimationState(true);
host_impl_->DidFinishImplFrame(begin_frame_args);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 50),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
GetInputHandler().ScrollUpdate(
AnimatedUpdateState(gfx::Point(), gfx::Vector2d(0, 50)));
GetInputHandler().ScrollUpdate(
AnimatedUpdateState(gfx::Point(), gfx::Vector2d(0, 100)));
begin_frame_args.frame_time = start_time + base::Milliseconds(250);
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->UpdateAnimationState(true);
host_impl_->DidFinishImplFrame(begin_frame_args);
begin_frame_args.frame_time = start_time + base::Milliseconds(300);
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->UpdateAnimationState(true);
host_impl_->DidFinishImplFrame(begin_frame_args);
LayerImpl* scrolling_layer = OuterViewportScrollLayer();
EXPECT_EQ(scrolling_layer->scroll_tree_index(),
host_impl_->CurrentlyScrollingNode()->id);
float y = CurrentScrollOffset(scrolling_layer).y();
EXPECT_TRUE(y > 1 && y < 49);
}
TEST_P(ClientModeLayerTreeHostImplTest, ScrollAnimatedWithDelay) {
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(50, 100);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
DrawFrame();
base::TimeTicks start_time = base::TimeTicks() + base::Milliseconds(100);
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 100),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
GetInputHandler().ScrollUpdate(
AnimatedUpdateState(gfx::Point(), gfx::Vector2d(0, 100)),
base::Milliseconds(100));
LayerImpl* scrolling_layer = OuterViewportScrollLayer();
EXPECT_EQ(scrolling_layer->scroll_tree_index(),
host_impl_->CurrentlyScrollingNode()->id);
begin_frame_args.frame_time = start_time;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->UpdateAnimationState(true);
EXPECT_NE(gfx::PointF(), CurrentScrollOffset(scrolling_layer));
host_impl_->DidFinishImplFrame(begin_frame_args);
base::TimeTicks half_way_time =
start_time - begin_frame_args.interval + base::Milliseconds(50);
begin_frame_args.frame_time = half_way_time;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->UpdateAnimationState(true);
EXPECT_EQ(50, CurrentScrollOffset(scrolling_layer).y());
host_impl_->DidFinishImplFrame(begin_frame_args);
GetInputHandler().ScrollUpdate(
AnimatedUpdateState(gfx::Point(), gfx::Vector2d(0, 100)),
base::Milliseconds(150));
begin_frame_args.frame_time = start_time + base::Milliseconds(100);
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->UpdateAnimationState(true);
EXPECT_LT(100, CurrentScrollOffset(scrolling_layer).y());
}
TEST_P(LayerTreeHostImplTimelinesTest, ScrollAnimatedAborted) {
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(500, 500);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
DrawFrame();
base::TimeTicks start_time = base::TimeTicks() + base::Milliseconds(100);
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 50),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
GetInputHandler().ScrollUpdate(
AnimatedUpdateState(gfx::Point(), gfx::Vector2d(0, 50)));
LayerImpl* scrolling_layer = OuterViewportScrollLayer();
EXPECT_EQ(scrolling_layer->scroll_tree_index(),
host_impl_->CurrentlyScrollingNode()->id);
begin_frame_args.frame_time = start_time;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
EXPECT_TRUE(GetImplAnimationHost()->HasAnyAnimationTargetingProperty(
scrolling_layer->element_id(), TargetProperty::SCROLL_OFFSET));
EXPECT_NE(gfx::PointF(), CurrentScrollOffset(scrolling_layer));
host_impl_->DidFinishImplFrame(begin_frame_args);
begin_frame_args.frame_time = start_time + base::Milliseconds(50);
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
float y = CurrentScrollOffset(scrolling_layer).y();
EXPECT_TRUE(y > 1 && y < 49);
GetInputHandler().ScrollEnd();
auto begin_state = BeginState(gfx::Point(0, y), gfx::Vector2dF(0, 50),
ui::ScrollInputType::kWheel);
begin_state->data()->delta_granularity =
ui::ScrollGranularity::kScrollByPrecisePixel;
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(begin_state.get(), ui::ScrollInputType::kWheel)
.thread);
auto update_state = UpdateState(gfx::Point(0, y), gfx::Vector2d(0, 50),
ui::ScrollInputType::kWheel);
update_state.data()->delta_granularity =
ui::ScrollGranularity::kScrollByPrecisePixel;
GetInputHandler().ScrollUpdate(update_state);
GetInputHandler().ScrollEnd();
EXPECT_FALSE(GetImplAnimationHost()->HasTickingKeyframeModelForTesting(
scrolling_layer->element_id()));
EXPECT_POINTF_EQ(gfx::PointF(0, y + 50),
CurrentScrollOffset(scrolling_layer));
EXPECT_EQ(nullptr, host_impl_->CurrentlyScrollingNode());
host_impl_->DidFinishImplFrame(begin_frame_args);
}
TEST_P(LayerTreeHostImplTimelinesTest, ScrollAnimated) {
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(500, 500);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
DrawFrame();
base::TimeTicks start_time = base::TimeTicks() + base::Milliseconds(100);
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 50),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
GetInputHandler().ScrollUpdate(
AnimatedUpdateState(gfx::Point(), gfx::Vector2d(0, 50)));
LayerImpl* scrolling_layer = OuterViewportScrollLayer();
EXPECT_EQ(scrolling_layer->scroll_tree_index(),
host_impl_->CurrentlyScrollingNode()->id);
begin_frame_args.frame_time = start_time;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
EXPECT_NE(gfx::PointF(), CurrentScrollOffset(scrolling_layer));
host_impl_->DidFinishImplFrame(begin_frame_args);
begin_frame_args.frame_time = start_time + base::Milliseconds(50);
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
float y = CurrentScrollOffset(scrolling_layer).y();
EXPECT_TRUE(y > 1 && y < 49);
GetInputHandler().ScrollUpdate(
AnimatedUpdateState(gfx::Point(), gfx::Vector2d(0, 50)));
GetInputHandler().ScrollEnd();
host_impl_->DidFinishImplFrame(begin_frame_args);
begin_frame_args.frame_time = start_time + base::Milliseconds(200);
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
y = CurrentScrollOffset(scrolling_layer).y();
EXPECT_TRUE(y > 50 && y < 100);
EXPECT_EQ(scrolling_layer->scroll_tree_index(),
host_impl_->CurrentlyScrollingNode()->id);
host_impl_->DidFinishImplFrame(begin_frame_args);
begin_frame_args.frame_time = start_time + base::Milliseconds(250);
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
EXPECT_POINTF_EQ(gfx::PointF(0, 100), CurrentScrollOffset(scrolling_layer));
EXPECT_EQ(nullptr, host_impl_->CurrentlyScrollingNode());
host_impl_->DidFinishImplFrame(begin_frame_args);
}
TEST_P(LayerTreeHostImplTimelinesTest, ImplPinchZoomScrollAnimated) {
const gfx::Size content_size(200, 200);
const gfx::Size viewport_size(100, 100);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
UpdateDrawProperties(host_impl_->active_tree());
LayerImpl* outer_scroll_layer = OuterViewportScrollLayer();
LayerImpl* inner_scroll_layer = InnerViewportScrollLayer();
float min_page_scale = 1, max_page_scale = 4;
float page_scale_factor = 2;
host_impl_->active_tree()->PushPageScaleFromMainThread(
page_scale_factor, min_page_scale, max_page_scale);
host_impl_->active_tree()->SetPageScaleOnActiveTree(page_scale_factor);
base::TimeTicks start_time = base::TimeTicks() + base::Milliseconds(250);
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(10, 20),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
GetInputHandler().ScrollUpdate(
AnimatedUpdateState(gfx::Point(), gfx::Vector2d(10, 20)));
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
EXPECT_EQ(outer_scroll_layer->scroll_tree_index(),
host_impl_->CurrentlyScrollingNode()->id);
begin_frame_args.frame_id.sequence_number++;
BeginImplFrameAndAnimate(begin_frame_args, start_time);
EXPECT_POINTF_EQ(gfx::PointF(5, 10), CurrentScrollOffset(inner_scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(outer_scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(outer_scroll_layer));
GetInputHandler().ScrollUpdate(
AnimatedUpdateState(gfx::Point(), gfx::Vector2d(100, 100)));
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
EXPECT_EQ(outer_scroll_layer->scroll_tree_index(),
host_impl_->CurrentlyScrollingNode()->id);
begin_frame_args.frame_id.sequence_number++;
BeginImplFrameAndAnimate(begin_frame_args,
start_time + base::Milliseconds(350));
EXPECT_POINTF_EQ(gfx::PointF(50, 50),
CurrentScrollOffset(inner_scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(5, 10), CurrentScrollOffset(outer_scroll_layer));
GetInputHandler().ScrollUpdate(
AnimatedUpdateState(gfx::Point(), gfx::Vector2d(190, 180)));
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
EXPECT_EQ(outer_scroll_layer->scroll_tree_index(),
host_impl_->CurrentlyScrollingNode()->id);
begin_frame_args.frame_id.sequence_number++;
BeginImplFrameAndAnimate(begin_frame_args,
start_time + base::Milliseconds(850));
EXPECT_POINTF_EQ(gfx::PointF(50, 50),
CurrentScrollOffset(inner_scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(100, 100),
CurrentScrollOffset(outer_scroll_layer));
GetInputHandler().ScrollUpdate(
AnimatedUpdateState(gfx::Point(), gfx::Vector2d(-110, -120)));
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
EXPECT_EQ(outer_scroll_layer->scroll_tree_index(),
host_impl_->CurrentlyScrollingNode()->id);
begin_frame_args.frame_id.sequence_number++;
BeginImplFrameAndAnimate(begin_frame_args,
start_time + base::Milliseconds(1200));
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(inner_scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(95, 90),
CurrentScrollOffset(outer_scroll_layer));
}
TEST_P(LayerTreeHostImplTimelinesTest, ImplPinchZoomScrollAnimatedUpdate) {
const gfx::Size content_size(200, 200);
const gfx::Size viewport_size(100, 100);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
LayerImpl* outer_scroll_layer = OuterViewportScrollLayer();
LayerImpl* inner_scroll_layer = InnerViewportScrollLayer();
float min_page_scale = 1, max_page_scale = 4;
float page_scale_factor = 2;
host_impl_->active_tree()->PushPageScaleFromMainThread(
page_scale_factor, min_page_scale, max_page_scale);
host_impl_->active_tree()->SetPageScaleOnActiveTree(page_scale_factor);
base::TimeTicks start_time = base::TimeTicks() + base::Milliseconds(50);
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(90, 90),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
GetInputHandler().ScrollUpdate(
AnimatedUpdateState(gfx::Point(), gfx::Vector2d(90, 90)));
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
EXPECT_EQ(outer_scroll_layer->scroll_tree_index(),
host_impl_->CurrentlyScrollingNode()->id);
begin_frame_args.frame_id.sequence_number++;
BeginImplFrameAndAnimate(begin_frame_args, start_time);
float inner_x = CurrentScrollOffset(inner_scroll_layer).x();
float inner_y = CurrentScrollOffset(inner_scroll_layer).y();
EXPECT_TRUE(inner_x > 0 && inner_x < 45);
EXPECT_TRUE(inner_y > 0 && inner_y < 45);
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(outer_scroll_layer));
GetInputHandler().ScrollUpdate(
AnimatedUpdateState(gfx::Point(), gfx::Vector2d(50, 50)));
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
EXPECT_EQ(outer_scroll_layer->scroll_tree_index(),
host_impl_->CurrentlyScrollingNode()->id);
begin_frame_args.frame_id.sequence_number++;
BeginImplFrameAndAnimate(begin_frame_args,
start_time + base::Milliseconds(350));
EXPECT_POINTF_EQ(gfx::PointF(50, 50),
CurrentScrollOffset(inner_scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(outer_scroll_layer));
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(outer_scroll_layer));
}
TEST_P(LayerTreeHostImplTimelinesTest, ScrollAnimatedNotUserScrollable) {
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(500, 500);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
LayerImpl* scrolling_layer = OuterViewportScrollLayer();
GetScrollNode(scrolling_layer)->user_scrollable_vertical = true;
GetScrollNode(scrolling_layer)->user_scrollable_horizontal = false;
DrawFrame();
base::TimeTicks start_time = base::TimeTicks() + base::Milliseconds(100);
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(50, 50),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
GetInputHandler().ScrollUpdate(
AnimatedUpdateState(gfx::Point(), gfx::Vector2d(50, 50)));
EXPECT_EQ(scrolling_layer->scroll_tree_index(),
host_impl_->CurrentlyScrollingNode()->id);
begin_frame_args.frame_time = start_time;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
EXPECT_NE(gfx::PointF(), CurrentScrollOffset(scrolling_layer));
host_impl_->DidFinishImplFrame(begin_frame_args);
begin_frame_args.frame_time = start_time + base::Milliseconds(50);
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
EXPECT_EQ(0, CurrentScrollOffset(scrolling_layer).x());
float y = CurrentScrollOffset(scrolling_layer).y();
EXPECT_TRUE(y > 1 && y < 49);
GetInputHandler().ScrollUpdate(
AnimatedUpdateState(gfx::Point(), gfx::Vector2d(50, 50)));
host_impl_->DidFinishImplFrame(begin_frame_args);
begin_frame_args.frame_time = start_time + base::Milliseconds(200);
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
y = CurrentScrollOffset(scrolling_layer).y();
EXPECT_TRUE(y > 50 && y < 100);
EXPECT_EQ(scrolling_layer->scroll_tree_index(),
host_impl_->CurrentlyScrollingNode()->id);
host_impl_->DidFinishImplFrame(begin_frame_args);
begin_frame_args.frame_time = start_time + base::Milliseconds(250);
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
EXPECT_POINTF_EQ(gfx::PointF(0, 100), CurrentScrollOffset(scrolling_layer));
EXPECT_EQ(scrolling_layer->scroll_tree_index(),
host_impl_->CurrentlyScrollingNode()->id);
host_impl_->DidFinishImplFrame(begin_frame_args);
GetInputHandler().ScrollEnd();
EXPECT_EQ(nullptr, host_impl_->CurrentlyScrollingNode());
}
TEST_P(LayerTreeHostImplTimelinesTest, ScrollAnimatedChangingBounds) {
const gfx::Size old_content_size(1000, 1000);
const gfx::Size new_content_size(750, 750);
const gfx::Size viewport_size(500, 500);
SetupViewportLayersOuterScrolls(viewport_size, old_content_size);
LayerImpl* scrolling_layer = OuterViewportScrollLayer();
LayerImpl* content_layer = AddContentLayer();
DrawFrame();
base::TimeTicks start_time = base::TimeTicks() + base::Milliseconds(100);
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(), gfx::Vector2d(500, 500),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
GetInputHandler().ScrollUpdate(
AnimatedUpdateState(gfx::Point(), gfx::Vector2d(500, 500)));
EXPECT_EQ(scrolling_layer->scroll_tree_index(),
host_impl_->CurrentlyScrollingNode()->id);
begin_frame_args.frame_time = start_time;
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
host_impl_->DidFinishImplFrame(begin_frame_args);
content_layer->SetBounds(new_content_size);
scrolling_layer->SetBounds(new_content_size);
GetScrollNode(scrolling_layer)->bounds = new_content_size;
begin_frame_args.frame_time = start_time + base::Milliseconds(200);
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
host_impl_->DidFinishImplFrame(begin_frame_args);
EXPECT_EQ(gfx::PointF(250, 250), CurrentScrollOffset(scrolling_layer));
}
TEST_F(CommitToPendingTreeLayerTreeHostImplTest,
InvalidLayerNotAddedToRasterQueue) {
CreatePendingTree();
scoped_refptr<RasterSource> raster_source_with_tiles(
FakeRasterSource::CreateFilled(gfx::Size(10, 10)));
auto* layer = SetupRootLayer<FakePictureLayerImpl>(host_impl_->pending_tree(),
gfx::Size(10, 10));
layer->set_gpu_raster_max_texture_size(
host_impl_->active_tree()->GetDeviceViewport().size());
layer->SetDrawsContent(true);
layer->tilings()->AddTiling(gfx::AxisTransform2d(), raster_source_with_tiles);
layer->SetRasterSourceForTesting(raster_source_with_tiles);
layer->tilings()->tiling_at(0)->set_resolution(
TileResolution::HIGH_RESOLUTION);
layer->tilings()->tiling_at(0)->CreateAllTilesForTesting();
layer->tilings()->UpdateTilePriorities(gfx::Rect(gfx::Size(10, 10)), 1, 1.0,
Occlusion(), true);
layer->set_has_valid_tile_priorities(true);
std::unique_ptr<RasterTilePriorityQueue> non_empty_raster_priority_queue_all =
host_impl_->BuildRasterQueue(TreePriority::SAME_PRIORITY_FOR_BOTH_TREES,
RasterTilePriorityQueue::Type::ALL);
EXPECT_FALSE(non_empty_raster_priority_queue_all->IsEmpty());
layer->set_has_valid_tile_priorities(false);
std::unique_ptr<RasterTilePriorityQueue> empty_raster_priority_queue_all =
host_impl_->BuildRasterQueue(TreePriority::SAME_PRIORITY_FOR_BOTH_TREES,
RasterTilePriorityQueue::Type::ALL);
EXPECT_TRUE(empty_raster_priority_queue_all->IsEmpty());
}
TEST_F(CommitToPendingTreeLayerTreeHostImplTest, DidBecomeActive) {
CreatePendingTree();
host_impl_->ActivateSyncTree();
CreatePendingTree();
LayerTreeImpl* pending_tree = host_impl_->pending_tree();
auto* pending_layer =
SetupRootLayer<FakePictureLayerImpl>(pending_tree, gfx::Size(10, 10));
EXPECT_EQ(0u, pending_layer->did_become_active_call_count());
pending_tree->DidBecomeActive();
EXPECT_EQ(1u, pending_layer->did_become_active_call_count());
std::unique_ptr<FakePictureLayerImpl> mask_layer =
FakePictureLayerImpl::Create(pending_tree, next_layer_id_++);
FakePictureLayerImpl* raw_mask_layer = mask_layer.get();
SetupMaskProperties(pending_layer, raw_mask_layer);
pending_tree->AddLayer(std::move(mask_layer));
EXPECT_EQ(1u, pending_layer->did_become_active_call_count());
EXPECT_EQ(0u, raw_mask_layer->did_become_active_call_count());
pending_tree->DidBecomeActive();
EXPECT_EQ(2u, pending_layer->did_become_active_call_count());
EXPECT_EQ(1u, raw_mask_layer->did_become_active_call_count());
EXPECT_EQ(2u, pending_layer->did_become_active_call_count());
EXPECT_EQ(1u, raw_mask_layer->did_become_active_call_count());
pending_tree->DidBecomeActive();
EXPECT_EQ(3u, pending_layer->did_become_active_call_count());
EXPECT_EQ(2u, raw_mask_layer->did_become_active_call_count());
}
TEST_P(LayerTreeHostImplTest, WheelScrollWithPageScaleFactorOnInnerLayer) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
auto* scroll_layer = InnerViewportScrollLayer();
host_impl_->active_tree()->SetDeviceViewportRect(gfx::Rect(50, 50));
DrawFrame();
EXPECT_EQ(scroll_layer, InnerViewportScrollLayer());
float min_page_scale = 1, max_page_scale = 4;
float page_scale_factor = 1;
{
host_impl_->active_tree()->PushPageScaleFromMainThread(
page_scale_factor, min_page_scale, max_page_scale);
host_impl_->active_tree()->SetPageScaleOnActiveTree(page_scale_factor);
SetScrollOffsetDelta(scroll_layer, gfx::Vector2d());
float page_scale_delta = 2;
GetInputHandler().ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().PinchGestureBegin(gfx::Point(),
ui::ScrollInputType::kWheel);
GetInputHandler().PinchGestureUpdate(page_scale_delta, gfx::Point());
GetInputHandler().PinchGestureEnd(gfx::Point());
GetInputHandler().ScrollEnd();
gfx::Vector2dF scroll_delta(0, 5);
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), scroll_delta,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(scroll_layer));
GetInputHandler().ScrollUpdate(
UpdateState(gfx::Point(), scroll_delta, ui::ScrollInputType::kWheel));
GetInputHandler().ScrollEnd();
EXPECT_POINTF_EQ(gfx::PointF(0, 2.5), CurrentScrollOffset(scroll_layer));
}
}
class LayerTreeHostImplCountingLostSurfaces : public LayerTreeHostImplTest {
public:
void DidLoseLayerTreeFrameSinkOnImplThread() override {
num_lost_surfaces_++;
}
protected:
int num_lost_surfaces_ = 0;
};
INSTANTIATE_COMMIT_TO_TREE_TEST_P(LayerTreeHostImplCountingLostSurfaces);
TEST_P(LayerTreeHostImplCountingLostSurfaces, TwiceLostSurface) {
EXPECT_EQ(0, num_lost_surfaces_);
host_impl_->DidLoseLayerTreeFrameSink();
EXPECT_EQ(1, num_lost_surfaces_);
host_impl_->DidLoseLayerTreeFrameSink();
EXPECT_EQ(1, num_lost_surfaces_);
}
size_t CountRenderPassesWithId(const viz::CompositorRenderPassList& list,
viz::CompositorRenderPassId id) {
return std::ranges::count(list, id, &viz::CompositorRenderPass::id);
}
TEST_P(LayerTreeHostImplTest, RemoveUnreferencedRenderPass) {
TestFrameData frame;
frame.render_passes.push_back(viz::CompositorRenderPass::Create());
viz::CompositorRenderPass* pass3 = frame.render_passes.back().get();
frame.render_passes.push_back(viz::CompositorRenderPass::Create());
viz::CompositorRenderPass* pass2 = frame.render_passes.back().get();
frame.render_passes.push_back(viz::CompositorRenderPass::Create());
viz::CompositorRenderPass* pass1 = frame.render_passes.back().get();
pass1->SetNew(viz::CompositorRenderPassId{1}, gfx::Rect(), gfx::Rect(),
gfx::Transform());
pass2->SetNew(viz::CompositorRenderPassId{2}, gfx::Rect(), gfx::Rect(),
gfx::Transform());
pass3->SetNew(viz::CompositorRenderPassId{3}, gfx::Rect(), gfx::Rect(),
gfx::Transform());
auto* color_quad = pass1->CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>();
color_quad->material = viz::DrawQuad::Material::kSolidColor;
color_quad = pass2->CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>();
color_quad->material = viz::DrawQuad::Material::kSolidColor;
color_quad = pass3->CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>();
color_quad->material = viz::DrawQuad::Material::kSolidColor;
auto* rpdq =
pass2->CreateAndAppendDrawQuad<viz::CompositorRenderPassDrawQuad>();
rpdq->material = viz::DrawQuad::Material::kCompositorRenderPass;
rpdq->render_pass_id = pass3->id;
FakeLayerTreeHostImpl::RemoveRenderPasses(&frame);
EXPECT_EQ(1u, frame.render_passes.size());
EXPECT_EQ(1u, CountRenderPassesWithId(frame.render_passes,
viz::CompositorRenderPassId{1u}));
EXPECT_EQ(0u, CountRenderPassesWithId(frame.render_passes,
viz::CompositorRenderPassId{2u}));
EXPECT_EQ(0u, CountRenderPassesWithId(frame.render_passes,
viz::CompositorRenderPassId{3u}));
EXPECT_EQ(viz::CompositorRenderPassId{1u}, frame.render_passes[0]->id);
}
TEST_P(LayerTreeHostImplTest, RemoveEmptyRenderPass) {
TestFrameData frame;
frame.render_passes.push_back(viz::CompositorRenderPass::Create());
viz::CompositorRenderPass* pass3 = frame.render_passes.back().get();
frame.render_passes.push_back(viz::CompositorRenderPass::Create());
viz::CompositorRenderPass* pass2 = frame.render_passes.back().get();
frame.render_passes.push_back(viz::CompositorRenderPass::Create());
viz::CompositorRenderPass* pass1 = frame.render_passes.back().get();
pass1->SetNew(viz::CompositorRenderPassId{1}, gfx::Rect(), gfx::Rect(),
gfx::Transform());
pass2->SetNew(viz::CompositorRenderPassId{2}, gfx::Rect(), gfx::Rect(),
gfx::Transform());
pass3->SetNew(viz::CompositorRenderPassId{3}, gfx::Rect(), gfx::Rect(),
gfx::Transform());
auto* color_quad = pass1->CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>();
color_quad->material = viz::DrawQuad::Material::kSolidColor;
auto* rpdq =
pass2->CreateAndAppendDrawQuad<viz::CompositorRenderPassDrawQuad>();
rpdq->material = viz::DrawQuad::Material::kCompositorRenderPass;
rpdq->render_pass_id = pass3->id;
rpdq = pass1->CreateAndAppendDrawQuad<viz::CompositorRenderPassDrawQuad>();
rpdq->material = viz::DrawQuad::Material::kCompositorRenderPass;
rpdq->render_pass_id = pass2->id;
FakeLayerTreeHostImpl::RemoveRenderPasses(&frame);
EXPECT_EQ(1u, frame.render_passes.size());
EXPECT_EQ(1u, CountRenderPassesWithId(frame.render_passes,
viz::CompositorRenderPassId{1u}));
EXPECT_EQ(0u, CountRenderPassesWithId(frame.render_passes,
viz::CompositorRenderPassId{2u}));
EXPECT_EQ(0u, CountRenderPassesWithId(frame.render_passes,
viz::CompositorRenderPassId{3u}));
EXPECT_EQ(viz::CompositorRenderPassId{1u}, frame.render_passes[0]->id);
EXPECT_EQ(1u, pass1->quad_list.size());
EXPECT_EQ(viz::DrawQuad::Material::kSolidColor,
pass1->quad_list.ElementAt(0)->material);
}
TEST_P(LayerTreeHostImplTest, DoNotRemoveEmptyRootRenderPass) {
TestFrameData frame;
frame.render_passes.push_back(viz::CompositorRenderPass::Create());
viz::CompositorRenderPass* pass3 = frame.render_passes.back().get();
frame.render_passes.push_back(viz::CompositorRenderPass::Create());
viz::CompositorRenderPass* pass2 = frame.render_passes.back().get();
frame.render_passes.push_back(viz::CompositorRenderPass::Create());
viz::CompositorRenderPass* pass1 = frame.render_passes.back().get();
pass1->SetNew(viz::CompositorRenderPassId{1}, gfx::Rect(), gfx::Rect(),
gfx::Transform());
pass2->SetNew(viz::CompositorRenderPassId{2}, gfx::Rect(), gfx::Rect(),
gfx::Transform());
pass3->SetNew(viz::CompositorRenderPassId{3}, gfx::Rect(), gfx::Rect(),
gfx::Transform());
auto* rpdq =
pass2->CreateAndAppendDrawQuad<viz::CompositorRenderPassDrawQuad>();
rpdq->material = viz::DrawQuad::Material::kCompositorRenderPass;
rpdq->render_pass_id = pass3->id;
rpdq = pass1->CreateAndAppendDrawQuad<viz::CompositorRenderPassDrawQuad>();
rpdq->material = viz::DrawQuad::Material::kCompositorRenderPass;
rpdq->render_pass_id = pass2->id;
FakeLayerTreeHostImpl::RemoveRenderPasses(&frame);
EXPECT_EQ(1u, frame.render_passes.size());
EXPECT_EQ(1u, CountRenderPassesWithId(frame.render_passes,
viz::CompositorRenderPassId{1u}));
EXPECT_EQ(0u, CountRenderPassesWithId(frame.render_passes,
viz::CompositorRenderPassId{2u}));
EXPECT_EQ(0u, CountRenderPassesWithId(frame.render_passes,
viz::CompositorRenderPassId{3u}));
EXPECT_EQ(viz::CompositorRenderPassId{1u}, frame.render_passes[0]->id);
EXPECT_EQ(0u, pass1->quad_list.size());
}
class FakeVideoFrameController : public VideoFrameController {
public:
void OnBeginFrame(const viz::BeginFrameArgs& args) override {
begin_frame_args_ = args;
did_draw_frame_ = false;
}
void DidDrawFrame() override { did_draw_frame_ = true; }
const viz::BeginFrameArgs& begin_frame_args() const {
return begin_frame_args_;
}
bool did_draw_frame() const { return did_draw_frame_; }
private:
viz::BeginFrameArgs begin_frame_args_;
bool did_draw_frame_ = false;
};
TEST_P(LayerTreeHostImplTest, AddVideoFrameControllerInsideFrame) {
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2);
FakeVideoFrameController controller;
host_impl_->WillBeginImplFrame(begin_frame_args);
EXPECT_FALSE(controller.begin_frame_args().IsValid());
host_impl_->AddVideoFrameController(&controller);
EXPECT_TRUE(controller.begin_frame_args().IsValid());
host_impl_->DidFinishImplFrame(begin_frame_args);
EXPECT_FALSE(controller.did_draw_frame());
TestFrameData frame;
host_impl_->DidDrawAllLayers(frame);
EXPECT_TRUE(controller.did_draw_frame());
controller.OnBeginFrame(begin_frame_args);
EXPECT_FALSE(controller.did_draw_frame());
host_impl_->RemoveVideoFrameController(&controller);
host_impl_->DidDrawAllLayers(frame);
EXPECT_FALSE(controller.did_draw_frame());
}
TEST_P(LayerTreeHostImplTest, AddVideoFrameControllerOutsideFrame) {
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2);
FakeVideoFrameController controller;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->DidFinishImplFrame(begin_frame_args);
EXPECT_FALSE(controller.begin_frame_args().IsValid());
host_impl_->AddVideoFrameController(&controller);
EXPECT_FALSE(controller.begin_frame_args().IsValid());
begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 3);
EXPECT_FALSE(controller.begin_frame_args().IsValid());
host_impl_->WillBeginImplFrame(begin_frame_args);
EXPECT_TRUE(controller.begin_frame_args().IsValid());
EXPECT_FALSE(controller.did_draw_frame());
TestFrameData frame;
host_impl_->DidDrawAllLayers(frame);
EXPECT_TRUE(controller.did_draw_frame());
controller.OnBeginFrame(begin_frame_args);
EXPECT_FALSE(controller.did_draw_frame());
host_impl_->RemoveVideoFrameController(&controller);
host_impl_->DidDrawAllLayers(frame);
EXPECT_FALSE(controller.did_draw_frame());
}
class GpuRasterizationDisabledLayerTreeHostImplTest
: public LayerTreeHostImplTest {
public:
std::unique_ptr<LayerTreeFrameSink> CreateLayerTreeFrameSink() override {
return FakeLayerTreeFrameSink::Create3d();
}
};
class MsaaIsSlowLayerTreeHostImplTest
: public CommitToActiveTreeLayerTreeHostImplTest {
public:
void CreateHostImplWithCaps(bool msaa_is_slow, bool avoid_stencil_buffers) {
LayerTreeSettings settings = DefaultSettings();
settings.gpu_rasterization_msaa_sample_count = 4;
auto frame_sink =
FakeLayerTreeFrameSink::Builder()
.AllContexts(&viz::TestRasterInterface::set_msaa_is_slow,
msaa_is_slow)
.AllContexts(&viz::TestRasterInterface::set_gpu_rasterization, true)
.AllContexts(&viz::TestRasterInterface::set_avoid_stencil_buffers,
avoid_stencil_buffers)
.Build();
EXPECT_TRUE(CreateHostImpl(settings, std::move(frame_sink)));
}
};
TEST_F(MsaaIsSlowLayerTreeHostImplTest, GpuRasterizationStatusMsaaIsSlow) {
CreateHostImplWithCaps(false, false);
host_impl_->CommitComplete();
EXPECT_TRUE(host_impl_->use_gpu_rasterization());
EXPECT_TRUE(host_impl_->can_use_msaa());
CreateHostImplWithCaps(true, false);
host_impl_->CommitComplete();
EXPECT_TRUE(host_impl_->use_gpu_rasterization());
EXPECT_FALSE(host_impl_->can_use_msaa());
CreateHostImplWithCaps(false, true);
host_impl_->CommitComplete();
EXPECT_TRUE(host_impl_->use_gpu_rasterization());
EXPECT_FALSE(host_impl_->can_use_msaa());
CreateHostImplWithCaps(true, true);
host_impl_->CommitComplete();
EXPECT_TRUE(host_impl_->use_gpu_rasterization());
EXPECT_FALSE(host_impl_->can_use_msaa());
}
TEST_F(CommitToPendingTreeLayerTreeHostImplTest,
UpdatePageScaleFactorOnActiveTree) {
CreatePendingTree();
host_impl_->pending_tree()->PushPageScaleFromMainThread(1, 1, 3);
SetupViewportLayers(host_impl_->pending_tree(), gfx::Size(50, 50),
gfx::Size(100, 100), gfx::Size(100, 100));
host_impl_->ActivateSyncTree();
DrawFrame();
CreatePendingTree();
host_impl_->active_tree()->SetPageScaleOnActiveTree(2);
TransformNode* active_tree_node =
host_impl_->active_tree()->PageScaleTransformNode();
EXPECT_TRUE(active_tree_node->local.IsScale2d());
EXPECT_EQ(gfx::Vector2dF(2, 2), active_tree_node->local.To2dScale());
EXPECT_EQ(gfx::Point3F(), active_tree_node->origin);
EXPECT_EQ(2, host_impl_->active_tree()->current_page_scale_factor());
EXPECT_EQ(2, host_impl_->active_tree()
->property_trees()
->transform_tree()
.page_scale_factor());
TransformNode* pending_tree_node =
host_impl_->pending_tree()->PageScaleTransformNode();
EXPECT_TRUE(pending_tree_node->local.IsScale2d());
EXPECT_EQ(gfx::Vector2dF(2, 2), pending_tree_node->local.To2dScale());
EXPECT_EQ(gfx::Point3F(), pending_tree_node->origin);
EXPECT_EQ(2, host_impl_->pending_tree()->current_page_scale_factor());
EXPECT_EQ(2, host_impl_->pending_tree()
->property_trees()
->transform_tree()
.page_scale_factor());
host_impl_->pending_tree()->set_needs_update_draw_properties();
UpdateDrawProperties(host_impl_->pending_tree());
pending_tree_node = host_impl_->pending_tree()->PageScaleTransformNode();
EXPECT_TRUE(pending_tree_node->local.IsScale2d());
EXPECT_EQ(gfx::Vector2dF(2, 2), pending_tree_node->local.To2dScale());
EXPECT_EQ(gfx::Point3F(), pending_tree_node->origin);
host_impl_->ActivateSyncTree();
UpdateDrawProperties(host_impl_->active_tree());
active_tree_node = host_impl_->active_tree()->PageScaleTransformNode();
EXPECT_TRUE(active_tree_node->local.IsScale2d());
EXPECT_EQ(gfx::Vector2dF(2, 2), active_tree_node->local.To2dScale());
EXPECT_EQ(gfx::Point3F(), active_tree_node->origin);
}
TEST_P(LayerTreeHostImplTest, SubLayerScaleForNodeInSubtreeOfPageScaleLayer) {
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 1, 3);
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
LayerImpl* in_subtree_of_page_scale_layer = AddLayerInActiveTree();
CopyProperties(root_layer(), in_subtree_of_page_scale_layer);
in_subtree_of_page_scale_layer->SetTransformTreeIndex(
host_impl_->active_tree()->PageScaleTransformNode()->id);
CreateEffectNode(in_subtree_of_page_scale_layer).render_surface_reason =
RenderSurfaceReason::kTest;
DrawFrame();
EffectNode* node = GetEffectNode(in_subtree_of_page_scale_layer);
EXPECT_EQ(node->surface_contents_scale, gfx::Vector2dF(1, 1));
host_impl_->active_tree()->SetPageScaleOnActiveTree(2);
DrawFrame();
node = GetEffectNode(in_subtree_of_page_scale_layer);
EXPECT_EQ(node->surface_contents_scale, gfx::Vector2dF(2, 2));
}
TEST_P(LayerTreeHostImplTest, RecomputeGpuRasterOnLayerTreeFrameSinkChange) {
host_impl_->ReleaseLayerTreeFrameSink();
host_impl_ = nullptr;
host_impl_ = LayerTreeHostImpl::Create(
DefaultSettings(), this, &task_runner_provider_, &stats_instrumentation_,
&task_graph_runner_,
AnimationHost::CreateForTesting(ThreadInstance::kImpl), nullptr, 0,
nullptr, nullptr);
InputHandler::Create(static_cast<CompositorDelegateForInput&>(*host_impl_));
host_impl_->SetVisible(true);
auto gpu_raster_layer_tree_frame_sink =
FakeLayerTreeFrameSink::Create3dForGpuRasterization();
host_impl_->InitializeFrameSink(gpu_raster_layer_tree_frame_sink.get());
EXPECT_TRUE(host_impl_->use_gpu_rasterization());
layer_tree_frame_sink_ = FakeLayerTreeFrameSink::CreateSoftware();
host_impl_->InitializeFrameSink(layer_tree_frame_sink_.get());
EXPECT_FALSE(host_impl_->use_gpu_rasterization());
}
TEST_P(LayerTreeHostImplTest,
LayerTreeHostImplTestScrollbarStatesInMainThreadScrolling) {
SetupMouseMoveAtTestScrollbarStates(true);
}
TEST_P(LayerTreeHostImplTest,
LayerTreeHostImplTestScrollbarStatesInNotMainThreadScrolling) {
SetupMouseMoveAtTestScrollbarStates(false);
}
TEST_F(CommitToPendingTreeLayerTreeHostImplTest,
CheckerImagingTileInvalidation) {
LayerTreeSettings settings = LegacySWSettings();
settings.enable_checker_imaging = true;
settings.min_image_bytes_to_checker = 512 * 1024;
settings.default_tile_size = gfx::Size(256, 256);
settings.max_untiled_layer_size = gfx::Size(256, 256);
CreateHostImpl(settings, CreateLayerTreeFrameSink());
gfx::Size layer_size = gfx::Size(750, 750);
FakeRecordingSource recording_source(layer_size);
PaintImage checkerable_image =
PaintImageBuilder::WithCopy(
CreateDiscardablePaintImage(gfx::Size(500, 500)))
.set_decoding_mode(PaintImage::DecodingMode::kAsync)
.TakePaintImage();
recording_source.add_draw_image(checkerable_image, gfx::Point(0, 0));
SkColor non_solid_color = SkColorSetARGB(128, 45, 56, 67);
PaintFlags non_solid_flags;
non_solid_flags.setColor(non_solid_color);
recording_source.add_draw_rect_with_flags(gfx::Rect(510, 0, 200, 600),
non_solid_flags);
recording_source.add_draw_rect_with_flags(gfx::Rect(0, 510, 200, 400),
non_solid_flags);
recording_source.Rerecord();
scoped_refptr<RasterSource> raster_source =
recording_source.CreateRasterSource();
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->BeginCommit(0, BeginMainFrameTraceId{1});
LayerTreeImpl* pending_tree = host_impl_->pending_tree();
auto* root = SetupRootLayer<FakePictureLayerImpl>(pending_tree, layer_size,
raster_source);
root->SetDrawsContent(true);
UpdateDrawProperties(pending_tree);
host_impl_->CommitComplete();
EXPECT_EQ(root->num_tilings(), 1U);
const PictureLayerTiling* tiling = root->tilings()->tiling_at(0);
EXPECT_EQ(tiling->AllTilesForTesting().size(), 9U);
for (auto* tile : tiling->AllTilesForTesting())
EXPECT_TRUE(tile->HasRasterTask());
while (!did_notify_ready_to_activate_)
base::RunLoop().RunUntilIdle();
for (auto* tile : tiling->AllTilesForTesting())
EXPECT_FALSE(tile->HasRasterTask());
while (!did_request_impl_side_invalidation_)
base::RunLoop().RunUntilIdle();
host_impl_->InvalidateContentOnImplSide();
pending_tree = host_impl_->pending_tree();
root = static_cast<FakePictureLayerImpl*>(pending_tree->root_layer());
for (auto* tile : root->tilings()->tiling_at(0)->AllTilesForTesting()) {
if (tile->tiling_i_index() < 2 && tile->tiling_j_index() < 2)
EXPECT_TRUE(tile->HasRasterTask());
else
EXPECT_FALSE(tile->HasRasterTask());
}
const auto expected_invalidation =
ImageRectsToRegion(root->discardable_image_map()->GetRectsForImage(
checkerable_image.stable_id()));
EXPECT_EQ(expected_invalidation, *(root->GetPendingInvalidation()));
}
TEST_P(LayerTreeHostImplTest, RasterColorSpace) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
auto wcg_params =
host_impl_->GetTargetColorParams(gfx::ContentColorUsage::kWideColorGamut);
EXPECT_EQ(wcg_params.color_space, gfx::ColorSpace::CreateSRGB());
host_impl_->active_tree()->SetDisplayColorSpaces(
gfx::DisplayColorSpaces(gfx::ColorSpace::CreateDisplayP3D65()));
wcg_params =
host_impl_->GetTargetColorParams(gfx::ContentColorUsage::kWideColorGamut);
EXPECT_EQ(wcg_params.color_space, gfx::ColorSpace::CreateDisplayP3D65());
}
TEST_P(LayerTreeHostImplTest, RasterColorSpaceSoftware) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, FakeLayerTreeFrameSink::CreateSoftware());
auto wcg_params =
host_impl_->GetTargetColorParams(gfx::ContentColorUsage::kWideColorGamut);
EXPECT_EQ(wcg_params.color_space, gfx::ColorSpace::CreateSRGB());
host_impl_->active_tree()->SetDisplayColorSpaces(
gfx::DisplayColorSpaces(gfx::ColorSpace::CreateDisplayP3D65()));
wcg_params =
host_impl_->GetTargetColorParams(gfx::ContentColorUsage::kWideColorGamut);
EXPECT_EQ(wcg_params.color_space, gfx::ColorSpace::CreateSRGB());
}
TEST_P(LayerTreeHostImplTest, RasterColorSpaceHDR) {
constexpr float kCustomWhiteLevel = 200.f;
constexpr float kHDRMaxLuminanceRelative = 2.f;
auto hdr = gfx::ColorSpace::CreateHDR10();
gfx::DisplayColorSpaces display_cs(hdr);
display_cs.SetSDRMaxLuminanceNits(kCustomWhiteLevel);
display_cs.SetHDRMaxLuminanceRelative(kHDRMaxLuminanceRelative);
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
host_impl_->active_tree()->SetDisplayColorSpaces(display_cs);
const auto srgb_params =
host_impl_->GetTargetColorParams(gfx::ContentColorUsage::kSRGB);
const auto wcg_params =
host_impl_->GetTargetColorParams(gfx::ContentColorUsage::kWideColorGamut);
const auto hdr_params =
host_impl_->GetTargetColorParams(gfx::ContentColorUsage::kHDR);
const auto srgb = gfx::ColorSpace::CreateSRGB();
const auto p3 = gfx::ColorSpace::CreateDisplayP3D65();
EXPECT_EQ(srgb_params.color_space, srgb);
EXPECT_EQ(srgb_params.GetHdrHeadroom(), 0.f);
EXPECT_EQ(wcg_params.color_space, p3);
EXPECT_EQ(wcg_params.GetHdrHeadroom(), 0.f);
EXPECT_EQ(hdr_params.color_space, p3.GetAsHDR());
EXPECT_EQ(hdr_params.GetHdrHeadroom(), std::log2(kHDRMaxLuminanceRelative));
}
TEST_F(CommitToPendingTreeLayerTreeHostImplTest,
UpdatedTilingsForNonDrawingLayers) {
gfx::Size layer_bounds(500, 500);
CreatePendingTree();
auto* root =
SetupRootLayer<LayerImpl>(host_impl_->pending_tree(), layer_bounds);
scoped_refptr<FakeRasterSource> raster_source(
FakeRasterSource::CreateFilled(layer_bounds));
auto* animated_transform_layer =
AddLayer<FakePictureLayerImpl>(host_impl_->pending_tree(), raster_source);
animated_transform_layer->SetBounds(layer_bounds);
animated_transform_layer->SetDrawsContent(true);
host_impl_->pending_tree()->SetElementIdsForTesting();
gfx::Transform singular;
singular.Scale3d(6, 6, 0);
CopyProperties(root, animated_transform_layer);
CreateTransformNode(animated_transform_layer).local = singular;
UpdateDrawProperties(host_impl_->pending_tree());
EXPECT_FALSE(animated_transform_layer->HasValidTilePriorities());
EXPECT_EQ(animated_transform_layer->tilings()->num_tilings(), 0u);
UpdateDrawProperties(host_impl_->pending_tree());
EXPECT_FALSE(animated_transform_layer->raster_even_if_not_drawn());
EXPECT_FALSE(animated_transform_layer->contributes_to_drawn_render_surface());
EXPECT_EQ(animated_transform_layer->tilings()->num_tilings(), 0u);
gfx::TransformOperations start_transform_operations;
start_transform_operations.AppendMatrix(singular);
gfx::TransformOperations end_transform_operations;
AddAnimatedTransformToElementWithAnimation(
animated_transform_layer->element_id(), timeline(), 10.0,
start_transform_operations, end_transform_operations);
UpdateDrawProperties(host_impl_->pending_tree());
EXPECT_TRUE(animated_transform_layer->raster_even_if_not_drawn());
EXPECT_FALSE(animated_transform_layer->contributes_to_drawn_render_surface());
ASSERT_EQ(animated_transform_layer->tilings()->num_tilings(), 1u);
EXPECT_FALSE(animated_transform_layer->tilings()
->tiling_at(0)
->can_require_tiles_for_activation());
}
TEST_F(CommitToPendingTreeLayerTreeHostImplTest,
RasterTilePrioritizationForNonDrawingLayers) {
gfx::Size layer_bounds(500, 500);
CreatePendingTree();
auto* root =
SetupRootLayer<LayerImpl>(host_impl_->pending_tree(), layer_bounds);
root->SetBounds(layer_bounds);
scoped_refptr<FakeRasterSource> raster_source(
FakeRasterSource::CreateFilled(layer_bounds));
auto* hidden_layer =
AddLayer<FakePictureLayerImpl>(host_impl_->pending_tree(), raster_source);
hidden_layer->SetBounds(layer_bounds);
hidden_layer->SetDrawsContent(true);
hidden_layer->set_contributes_to_drawn_render_surface(true);
CopyProperties(root, hidden_layer);
auto* drawing_layer =
AddLayer<FakePictureLayerImpl>(host_impl_->pending_tree(), raster_source);
drawing_layer->SetBounds(layer_bounds);
drawing_layer->SetDrawsContent(true);
drawing_layer->set_contributes_to_drawn_render_surface(true);
CopyProperties(root, drawing_layer);
gfx::Rect layer_rect(0, 0, 500, 500);
hidden_layer->tilings()->AddTiling(gfx::AxisTransform2d(), raster_source);
PictureLayerTiling* hidden_tiling = hidden_layer->tilings()->tiling_at(0);
hidden_tiling->set_resolution(TileResolution::HIGH_RESOLUTION);
hidden_tiling->CreateAllTilesForTesting();
hidden_tiling->SetTilePriorityRectsForTesting(
layer_rect,
layer_rect,
layer_rect,
layer_rect);
drawing_layer->tilings()->AddTiling(gfx::AxisTransform2d(), raster_source);
PictureLayerTiling* drawing_tiling = drawing_layer->tilings()->tiling_at(0);
drawing_tiling->set_resolution(TileResolution::HIGH_RESOLUTION);
drawing_tiling->CreateAllTilesForTesting();
drawing_tiling->SetTilePriorityRectsForTesting(
layer_rect,
layer_rect,
layer_rect,
layer_rect);
hidden_layer->set_contributes_to_drawn_render_surface(false);
hidden_layer->set_raster_even_if_not_drawn(true);
std::unique_ptr<RasterTilePriorityQueue> queue =
host_impl_->BuildRasterQueue(TreePriority::SMOOTHNESS_TAKES_PRIORITY,
RasterTilePriorityQueue::Type::ALL);
EXPECT_EQ(queue->Top().tile()->layer_id(), 3);
}
TEST_F(CommitToPendingTreeLayerTreeHostImplTest,
DrawAfterDroppingTileResources) {
LayerTreeSettings settings = DefaultSettings();
settings.using_synchronous_renderer_compositor = true;
CreateHostImpl(settings, CreateLayerTreeFrameSink());
CreatePendingTree();
gfx::Size bounds(100, 100);
scoped_refptr<FakeRasterSource> raster_source(
FakeRasterSource::CreateFilled(bounds));
auto* root = SetupRootLayer<FakePictureLayerImpl>(host_impl_->pending_tree(),
bounds, raster_source);
root->SetDrawsContent(true);
host_impl_->ActivateSyncTree();
FakePictureLayerImpl* layer = static_cast<FakePictureLayerImpl*>(
host_impl_->active_tree()->FindActiveTreeLayerById(root->id()));
DrawFrame();
EXPECT_FALSE(host_impl_->active_tree()->needs_update_draw_properties());
EXPECT_LT(0, layer->raster_page_scale());
EXPECT_GT(layer->tilings()->num_tilings(), 0u);
const ManagedMemoryPolicy policy = host_impl_->ActualManagedMemoryPolicy();
const ManagedMemoryPolicy zero_policy(0u);
host_impl_->SetMemoryPolicy(zero_policy);
EXPECT_EQ(0, layer->raster_page_scale());
EXPECT_EQ(layer->tilings()->num_tilings(), 0u);
host_impl_->SetMemoryPolicy(policy);
DrawFrame();
EXPECT_LT(0, layer->raster_page_scale());
EXPECT_GT(layer->tilings()->num_tilings(), 0u);
}
TEST_P(LayerTreeHostImplTest, AllowedTouchActionTest1) {
AllowedTouchActionTestHelper(1.0f, 1.0f);
}
TEST_P(LayerTreeHostImplTest, AllowedTouchActionTest2) {
AllowedTouchActionTestHelper(1.0f, 0.789f);
}
TEST_P(LayerTreeHostImplTest, AllowedTouchActionTest3) {
AllowedTouchActionTestHelper(2.345f, 1.0f);
}
TEST_P(LayerTreeHostImplTest, AllowedTouchActionTest4) {
AllowedTouchActionTestHelper(2.654f, 0.678f);
}
class TestRenderFrameMetadataObserver : public RenderFrameMetadataObserver {
public:
explicit TestRenderFrameMetadataObserver(bool increment_counter)
: increment_counter_(increment_counter) {}
TestRenderFrameMetadataObserver(const TestRenderFrameMetadataObserver&) =
delete;
~TestRenderFrameMetadataObserver() override {}
TestRenderFrameMetadataObserver& operator=(
const TestRenderFrameMetadataObserver&) = delete;
void BindToCurrentSequence() override {}
void OnRenderFrameSubmission(
const RenderFrameMetadata& render_frame_metadata,
viz::CompositorFrameMetadata* compositor_frame_metadata,
bool force_send) override {
if (increment_counter_)
compositor_frame_metadata->send_frame_token_to_embedder = true;
last_metadata_ = render_frame_metadata;
}
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
void DidEndScroll() override {}
#endif
const std::optional<RenderFrameMetadata>& last_metadata() const {
return last_metadata_;
}
private:
bool increment_counter_;
std::optional<RenderFrameMetadata> last_metadata_;
};
TEST_P(LayerTreeHostImplTest, RenderFrameMetadata) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
host_impl_->active_tree()->SetDeviceViewportRect(gfx::Rect(50, 50));
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 0.5f, 4);
{
RenderFrameMetadata metadata = StartDrawAndProduceRenderFrameMetadata();
EXPECT_EQ(gfx::PointF(), metadata.root_scroll_offset);
EXPECT_EQ(1, metadata.page_scale_factor);
#if BUILDFLAG(IS_ANDROID)
EXPECT_EQ(gfx::SizeF(50, 50), metadata.scrollable_viewport_size);
EXPECT_EQ(0.5f, metadata.min_page_scale_factor);
EXPECT_EQ(4, metadata.max_page_scale_factor);
EXPECT_EQ(gfx::SizeF(100, 100), metadata.root_layer_size);
EXPECT_FALSE(metadata.root_overflow_y_hidden);
#endif
}
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel)
.thread);
GetInputHandler().ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10),
ui::ScrollInputType::kWheel));
{
RenderFrameMetadata metadata = StartDrawAndProduceRenderFrameMetadata();
EXPECT_EQ(gfx::PointF(0, 10), metadata.root_scroll_offset);
}
GetInputHandler().ScrollEnd();
{
RenderFrameMetadata metadata = StartDrawAndProduceRenderFrameMetadata();
EXPECT_EQ(gfx::PointF(0, 10), metadata.root_scroll_offset);
}
#if BUILDFLAG(IS_ANDROID)
{
UpdateDrawProperties(host_impl_->active_tree());
host_impl_->OuterViewportScrollNode()->user_scrollable_horizontal = false;
RenderFrameMetadata metadata = StartDrawAndProduceRenderFrameMetadata();
EXPECT_FALSE(metadata.root_overflow_y_hidden);
}
{
UpdateDrawProperties(host_impl_->active_tree());
host_impl_->OuterViewportScrollNode()->user_scrollable_vertical = false;
RenderFrameMetadata metadata = StartDrawAndProduceRenderFrameMetadata();
EXPECT_TRUE(metadata.root_overflow_y_hidden);
}
{
UpdateDrawProperties(host_impl_->active_tree());
host_impl_->OuterViewportScrollNode()->user_scrollable_horizontal = true;
host_impl_->OuterViewportScrollNode()->user_scrollable_vertical = true;
RenderFrameMetadata metadata = StartDrawAndProduceRenderFrameMetadata();
EXPECT_FALSE(metadata.root_overflow_y_hidden);
}
{
UpdateDrawProperties(host_impl_->active_tree());
host_impl_->OuterViewportScrollNode()->user_scrollable_horizontal = false;
RenderFrameMetadata metadata = StartDrawAndProduceRenderFrameMetadata();
EXPECT_FALSE(metadata.root_overflow_y_hidden);
}
{
UpdateDrawProperties(host_impl_->active_tree());
host_impl_->OuterViewportScrollNode()->user_scrollable_vertical = false;
RenderFrameMetadata metadata = StartDrawAndProduceRenderFrameMetadata();
EXPECT_TRUE(metadata.root_overflow_y_hidden);
}
#endif
GetInputHandler().ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
GetInputHandler().PinchGestureBegin(gfx::Point(),
ui::ScrollInputType::kWheel);
GetInputHandler().PinchGestureUpdate(2, gfx::Point());
GetInputHandler().PinchGestureEnd(gfx::Point());
GetInputHandler().ScrollEnd();
{
RenderFrameMetadata metadata = StartDrawAndProduceRenderFrameMetadata();
EXPECT_EQ(gfx::PointF(0, 10), metadata.root_scroll_offset);
EXPECT_EQ(2, metadata.page_scale_factor);
#if BUILDFLAG(IS_ANDROID)
EXPECT_EQ(gfx::SizeF(25, 25), metadata.scrollable_viewport_size);
EXPECT_EQ(0.5f, metadata.min_page_scale_factor);
EXPECT_EQ(4, metadata.max_page_scale_factor);
EXPECT_EQ(gfx::SizeF(100, 100), metadata.root_layer_size);
#endif
}
host_impl_->ProcessCompositorDeltas( nullptr);
host_impl_->active_tree()->PushPageScaleFromMainThread(4, 0.5f, 4);
host_impl_->active_tree()->SetPageScaleOnActiveTree(4);
{
RenderFrameMetadata metadata = StartDrawAndProduceRenderFrameMetadata();
EXPECT_EQ(gfx::PointF(0, 10), metadata.root_scroll_offset);
EXPECT_EQ(4, metadata.page_scale_factor);
#if BUILDFLAG(IS_ANDROID)
EXPECT_EQ(gfx::SizeF(12.5f, 12.5f), metadata.scrollable_viewport_size);
EXPECT_EQ(0.5f, metadata.min_page_scale_factor);
EXPECT_EQ(4, metadata.max_page_scale_factor);
EXPECT_EQ(gfx::SizeF(100, 100), metadata.root_layer_size);
#endif
}
}
TEST_P(ClientModeLayerTreeHostImplTest,
SelectionBoundsPassedToRenderFrameMetadata) {
LayerImpl* root = SetupDefaultRootLayer(gfx::Size(10, 10));
UpdateDrawProperties(host_impl_->active_tree());
auto observer = std::make_unique<TestRenderFrameMetadataObserver>(false);
auto* observer_ptr = observer.get();
host_impl_->SetRenderFrameObserver(std::move(observer));
EXPECT_FALSE(observer_ptr->last_metadata());
host_impl_->SetNeedsRedraw(false,
false);
DrawFrame();
ASSERT_TRUE(observer_ptr->last_metadata());
const viz::Selection<gfx::SelectionBound>& selection_1 =
observer_ptr->last_metadata()->selection;
EXPECT_EQ(gfx::SelectionBound::EMPTY, selection_1.start.type());
EXPECT_EQ(gfx::SelectionBound::EMPTY, selection_1.end.type());
EXPECT_EQ(gfx::PointF(), selection_1.start.edge_end());
EXPECT_EQ(gfx::PointF(), selection_1.start.edge_start());
EXPECT_FALSE(selection_1.start.visible());
EXPECT_FALSE(selection_1.end.visible());
gfx::Point selection_start(5, 0);
gfx::Point selection_end(5, 5);
LayerSelection selection;
selection.start.type = gfx::SelectionBound::CENTER;
selection.start.layer_id = root->id();
selection.start.edge_end = selection_end;
selection.start.edge_start = selection_start;
selection.end = selection.start;
host_impl_->active_tree()->RegisterSelection(selection);
host_impl_->SetNeedsRedraw(false,
false);
DrawFrame();
ASSERT_TRUE(observer_ptr->last_metadata());
const viz::Selection<gfx::SelectionBound>& selection_2 =
observer_ptr->last_metadata()->selection;
EXPECT_EQ(selection.start.type, selection_2.start.type());
EXPECT_EQ(selection.end.type, selection_2.end.type());
EXPECT_EQ(gfx::PointF(selection_end), selection_2.start.edge_end());
EXPECT_EQ(gfx::PointF(selection_start), selection_2.start.edge_start());
EXPECT_TRUE(selection_2.start.visible());
EXPECT_TRUE(selection_2.end.visible());
}
TEST_P(ClientModeLayerTreeHostImplTest,
VerticalScrollDirectionChangesPassedToRenderFrameMetadata) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
host_impl_->active_tree()->SetDeviceViewportRect(gfx::Rect(50, 50));
auto observer = std::make_unique<TestRenderFrameMetadataObserver>(false);
auto* observer_ptr = observer.get();
host_impl_->SetRenderFrameObserver(std::move(observer));
EXPECT_FALSE(observer_ptr->last_metadata());
typedef struct {
gfx::Vector2d scroll_delta;
viz::VerticalScrollDirection expected_vertical_scroll_direction;
} TestLeg;
std::vector<TestLeg> test_legs;
test_legs.push_back({gfx::Vector2d(),
viz::
VerticalScrollDirection::kNull});
test_legs.push_back({gfx::Vector2d(10, 0),
viz::
VerticalScrollDirection::kNull});
test_legs.push_back({gfx::Vector2d(0, 10),
viz::
VerticalScrollDirection::kDown});
test_legs.push_back({gfx::Vector2d(0, 10),
viz::
VerticalScrollDirection::kNull});
test_legs.push_back({gfx::Vector2d(-10, 0),
viz::
VerticalScrollDirection::kNull});
test_legs.push_back({gfx::Vector2d(0, -10),
viz::
VerticalScrollDirection::kUp});
test_legs.push_back({gfx::Vector2d(0, -10),
viz::
VerticalScrollDirection::kNull});
for (auto& test_leg : test_legs) {
if (!test_leg.scroll_delta.IsZero()) {
GetInputHandler().ScrollBegin(
BeginState(gfx::Point(), test_leg.scroll_delta,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), test_leg.scroll_delta, ui::ScrollInputType::kWheel));
}
host_impl_->SetNeedsRedraw(false,
false);
DrawFrame();
EXPECT_EQ(test_leg.expected_vertical_scroll_direction,
observer_ptr->last_metadata()->new_vertical_scroll_direction);
if (!test_leg.scroll_delta.IsZero())
GetInputHandler().ScrollEnd();
}
}
TEST_P(LayerTreeHostImplTest, ScrollUpdateDoesNotSetScrollingNode) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
UpdateDrawProperties(host_impl_->active_tree());
ScrollTree& scroll_tree =
host_impl_->active_tree()->property_trees()->scroll_tree_mutable();
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
ScrollNode* scroll_node = scroll_tree.CurrentlyScrollingNode();
EXPECT_TRUE(scroll_node);
ScrollStateData scroll_state_data;
ScrollState scroll_state(scroll_state_data);
GetInputHandler().ScrollUpdate(scroll_state);
EXPECT_EQ(scroll_node, scroll_tree.CurrentlyScrollingNode());
host_impl_->active_tree()->SetCurrentlyScrollingNode(nullptr);
EXPECT_FALSE(scroll_tree.CurrentlyScrollingNode());
GetInputHandler().ScrollUpdate(scroll_state);
EXPECT_EQ(nullptr, scroll_tree.CurrentlyScrollingNode());
}
class HitTestRegionListGeneratingLayerTreeHostImplTest
: public LayerTreeHostImplTest {
public:
bool CreateHostImpl(
const LayerTreeSettings& settings,
std::unique_ptr<LayerTreeFrameSink> layer_tree_frame_sink) override {
LayerTreeSettings new_settings = settings;
return LayerTreeHostImplTest::CreateHostImpl(
new_settings, std::move(layer_tree_frame_sink));
}
};
INSTANTIATE_COMMIT_TO_TREE_TEST_P(
HitTestRegionListGeneratingLayerTreeHostImplTest);
TEST_P(HitTestRegionListGeneratingLayerTreeHostImplTest, BuildHitTestData) {
auto* root = SetupDefaultRootLayer(gfx::Size(1024, 768));
auto* intermediate_layer = AddLayerInActiveTree();
auto* surface_child1 = AddLayer<SurfaceLayerImpl>(host_impl_->active_tree());
auto* surface_child2 = AddLayer<SurfaceLayerImpl>(host_impl_->active_tree());
auto* overlapping_layer = AddLayerInActiveTree();
intermediate_layer->SetBounds(gfx::Size(200, 200));
surface_child1->SetBounds(gfx::Size(100, 100));
gfx::Transform rotate;
rotate.Rotate(45);
surface_child1->SetDrawsContent(true);
surface_child1->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
surface_child1->SetSurfaceHitTestable(true);
surface_child2->SetBounds(gfx::Size(100, 100));
surface_child2->SetDrawsContent(true);
surface_child2->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
surface_child2->SetSurfaceHitTestable(true);
overlapping_layer->SetBounds(gfx::Size(200, 200));
overlapping_layer->SetDrawsContent(true);
overlapping_layer->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
viz::LocalSurfaceId child_local_surface_id(2,
base::UnguessableToken::Create());
viz::FrameSinkId frame_sink_id(2, 0);
viz::SurfaceId child_surface_id(frame_sink_id, child_local_surface_id);
surface_child1->SetRange(viz::SurfaceRange(std::nullopt, child_surface_id),
std::nullopt);
surface_child2->SetRange(viz::SurfaceRange(std::nullopt, child_surface_id),
std::nullopt);
CopyProperties(root, intermediate_layer);
intermediate_layer->SetOffsetToTransformParent(gfx::Vector2dF(200, 300));
CopyProperties(root, surface_child2);
surface_child2->SetOffsetToTransformParent(gfx::Vector2dF(450, 300));
CopyProperties(root, overlapping_layer);
overlapping_layer->SetOffsetToTransformParent(gfx::Vector2dF(500, 350));
CopyProperties(intermediate_layer, surface_child1);
auto& surface_child1_transform_node = CreateTransformNode(surface_child1);
surface_child1_transform_node.post_translation = gfx::Vector2dF(250, 350);
surface_child1_transform_node.local = rotate;
UpdateDrawProperties(host_impl_->active_tree());
draw_property_utils::ComputeEffects(
&host_impl_->active_tree()->property_trees()->effect_tree_mutable());
constexpr gfx::Rect kFrameRect(0, 0, 1024, 768);
std::optional<viz::HitTestRegionList> hit_test_region_list =
host_impl_->BuildHitTestData();
ASSERT_TRUE(hit_test_region_list);
uint32_t expected_flags = viz::HitTestRegionFlags::kHitTestMouse |
viz::HitTestRegionFlags::kHitTestTouch |
viz::HitTestRegionFlags::kHitTestMine;
EXPECT_EQ(expected_flags, hit_test_region_list->flags);
EXPECT_EQ(kFrameRect, hit_test_region_list->bounds);
EXPECT_EQ(2u, hit_test_region_list->regions.size());
EXPECT_EQ(child_surface_id.frame_sink_id(),
hit_test_region_list->regions[1].frame_sink_id);
expected_flags = viz::HitTestRegionFlags::kHitTestMouse |
viz::HitTestRegionFlags::kHitTestTouch |
viz::HitTestRegionFlags::kHitTestChildSurface;
EXPECT_EQ(expected_flags, hit_test_region_list->regions[1].flags);
gfx::Transform child1_transform;
child1_transform.Rotate(-45);
child1_transform.Translate(-250, -350);
EXPECT_TRUE(child1_transform.ApproximatelyEqual(
hit_test_region_list->regions[1].transform));
EXPECT_EQ(gfx::Rect(0, 0, 100, 100), hit_test_region_list->regions[1].rect);
EXPECT_EQ(child_surface_id.frame_sink_id(),
hit_test_region_list->regions[0].frame_sink_id);
expected_flags = viz::HitTestRegionFlags::kHitTestMouse |
viz::HitTestRegionFlags::kHitTestTouch |
viz::HitTestRegionFlags::kHitTestChildSurface |
viz::HitTestRegionFlags::kHitTestAsk;
EXPECT_EQ(expected_flags, hit_test_region_list->regions[0].flags);
gfx::Transform child2_transform;
child2_transform.Translate(-450, -300);
EXPECT_TRUE(child2_transform.ApproximatelyEqual(
hit_test_region_list->regions[0].transform));
EXPECT_EQ(gfx::Rect(0, 0, 100, 100), hit_test_region_list->regions[0].rect);
}
TEST_P(HitTestRegionListGeneratingLayerTreeHostImplTest, PointerEvents) {
auto* root = SetupDefaultRootLayer(gfx::Size(1024, 768));
auto* surface_child1 = AddLayer<SurfaceLayerImpl>(host_impl_->active_tree());
auto* surface_child2 = AddLayer<SurfaceLayerImpl>(host_impl_->active_tree());
surface_child1->SetBounds(gfx::Size(100, 100));
surface_child1->SetDrawsContent(true);
surface_child1->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
surface_child1->SetSurfaceHitTestable(true);
surface_child1->SetHasPointerEventsNone(false);
CopyProperties(root, surface_child1);
surface_child2->SetBounds(gfx::Size(100, 100));
surface_child2->SetDrawsContent(true);
surface_child2->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
surface_child2->SetSurfaceHitTestable(false);
surface_child2->SetHasPointerEventsNone(true);
CopyProperties(root, surface_child2);
surface_child2->SetOffsetToTransformParent(gfx::Vector2dF(50, 50));
viz::LocalSurfaceId child_local_surface_id(2,
base::UnguessableToken::Create());
viz::FrameSinkId frame_sink_id(2, 0);
viz::SurfaceId child_surface_id(frame_sink_id, child_local_surface_id);
surface_child1->SetRange(viz::SurfaceRange(std::nullopt, child_surface_id),
std::nullopt);
constexpr gfx::Rect kFrameRect(0, 0, 1024, 768);
UpdateDrawProperties(host_impl_->active_tree());
std::optional<viz::HitTestRegionList> hit_test_region_list =
host_impl_->BuildHitTestData();
ASSERT_TRUE(hit_test_region_list);
uint32_t expected_flags = viz::HitTestRegionFlags::kHitTestMouse |
viz::HitTestRegionFlags::kHitTestTouch |
viz::HitTestRegionFlags::kHitTestMine;
EXPECT_EQ(expected_flags, hit_test_region_list->flags);
EXPECT_EQ(kFrameRect, hit_test_region_list->bounds);
EXPECT_EQ(1u, hit_test_region_list->regions.size());
EXPECT_EQ(child_surface_id.frame_sink_id(),
hit_test_region_list->regions[0].frame_sink_id);
expected_flags = viz::HitTestRegionFlags::kHitTestMouse |
viz::HitTestRegionFlags::kHitTestTouch |
viz::HitTestRegionFlags::kHitTestChildSurface;
EXPECT_EQ(expected_flags, hit_test_region_list->regions[0].flags);
gfx::Transform child1_transform;
EXPECT_TRUE(child1_transform.ApproximatelyEqual(
hit_test_region_list->regions[0].transform));
EXPECT_EQ(gfx::Rect(0, 0, 100, 100), hit_test_region_list->regions[0].rect);
}
TEST_P(HitTestRegionListGeneratingLayerTreeHostImplTest, ComplexPage) {
LayerImpl* root = SetupDefaultRootLayer(gfx::Size(1024, 768));
auto* surface_child = AddLayer<SurfaceLayerImpl>(host_impl_->active_tree());
surface_child->SetBounds(gfx::Size(100, 100));
surface_child->SetDrawsContent(true);
surface_child->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
surface_child->SetSurfaceHitTestable(true);
surface_child->SetHasPointerEventsNone(false);
viz::LocalSurfaceId child_local_surface_id(2,
base::UnguessableToken::Create());
viz::FrameSinkId frame_sink_id(2, 0);
viz::SurfaceId child_surface_id(frame_sink_id, child_local_surface_id);
surface_child->SetRange(viz::SurfaceRange(std::nullopt, child_surface_id),
std::nullopt);
CopyProperties(root, surface_child);
for (size_t i = 0; i <= 100; ++i) {
LayerImpl* layer = AddLayerInActiveTree();
layer->SetBounds(gfx::Size(1, 1));
layer->SetDrawsContent(true);
layer->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
CopyProperties(root, layer);
}
constexpr gfx::Rect kFrameRect(0, 0, 1024, 768);
UpdateDrawProperties(host_impl_->active_tree());
std::optional<viz::HitTestRegionList> hit_test_region_list =
host_impl_->BuildHitTestData();
ASSERT_TRUE(hit_test_region_list);
uint32_t expected_flags = viz::HitTestRegionFlags::kHitTestMouse |
viz::HitTestRegionFlags::kHitTestTouch |
viz::HitTestRegionFlags::kHitTestMine;
EXPECT_EQ(expected_flags, hit_test_region_list->flags);
EXPECT_EQ(kFrameRect, hit_test_region_list->bounds);
EXPECT_EQ(1u, hit_test_region_list->regions.size());
EXPECT_EQ(child_surface_id.frame_sink_id(),
hit_test_region_list->regions[0].frame_sink_id);
expected_flags = viz::HitTestRegionFlags::kHitTestMouse |
viz::HitTestRegionFlags::kHitTestTouch |
viz::HitTestRegionFlags::kHitTestChildSurface |
viz::HitTestRegionFlags::kHitTestAsk;
EXPECT_EQ(expected_flags, hit_test_region_list->regions[0].flags);
gfx::Transform child1_transform;
EXPECT_TRUE(child1_transform.ApproximatelyEqual(
hit_test_region_list->regions[0].transform));
EXPECT_EQ(gfx::Rect(0, 0, 100, 100), hit_test_region_list->regions[0].rect);
}
TEST_P(HitTestRegionListGeneratingLayerTreeHostImplTest, InvalidFrameSinkId) {
LayerImpl* root = SetupDefaultRootLayer(gfx::Size(1024, 768));
auto* surface_child1 = AddLayer<SurfaceLayerImpl>(host_impl_->active_tree());
host_impl_->active_tree()->SetDeviceViewportRect(gfx::Rect(1024, 768));
surface_child1->SetBounds(gfx::Size(100, 100));
surface_child1->SetDrawsContent(true);
surface_child1->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
surface_child1->SetSurfaceHitTestable(true);
surface_child1->SetHasPointerEventsNone(false);
CopyProperties(root, surface_child1);
viz::LocalSurfaceId child_local_surface_id(2,
base::UnguessableToken::Create());
viz::FrameSinkId frame_sink_id(2, 0);
viz::SurfaceId child_surface_id(frame_sink_id, child_local_surface_id);
surface_child1->SetRange(viz::SurfaceRange(std::nullopt, child_surface_id),
std::nullopt);
auto* surface_child2 = AddLayer<SurfaceLayerImpl>(host_impl_->active_tree());
surface_child2->SetBounds(gfx::Size(50, 50));
surface_child2->SetDrawsContent(true);
surface_child2->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
surface_child2->SetSurfaceHitTestable(true);
surface_child2->SetHasPointerEventsNone(false);
CopyProperties(root, surface_child2);
surface_child2->SetRange(viz::SurfaceRange(std::nullopt, viz::SurfaceId()),
std::nullopt);
constexpr gfx::Rect kFrameRect(0, 0, 1024, 768);
UpdateDrawProperties(host_impl_->active_tree());
std::optional<viz::HitTestRegionList> hit_test_region_list =
host_impl_->BuildHitTestData();
ASSERT_TRUE(hit_test_region_list);
uint32_t expected_flags = viz::HitTestRegionFlags::kHitTestMouse |
viz::HitTestRegionFlags::kHitTestTouch |
viz::HitTestRegionFlags::kHitTestMine;
EXPECT_EQ(expected_flags, hit_test_region_list->flags);
EXPECT_EQ(kFrameRect, hit_test_region_list->bounds);
EXPECT_EQ(1u, hit_test_region_list->regions.size());
EXPECT_EQ(child_surface_id.frame_sink_id(),
hit_test_region_list->regions[0].frame_sink_id);
expected_flags = viz::HitTestRegionFlags::kHitTestMouse |
viz::HitTestRegionFlags::kHitTestTouch |
viz::HitTestRegionFlags::kHitTestChildSurface |
viz::HitTestRegionFlags::kHitTestAsk;
EXPECT_EQ(expected_flags, hit_test_region_list->regions[0].flags);
gfx::Transform child1_transform;
EXPECT_TRUE(child1_transform.ApproximatelyEqual(
hit_test_region_list->regions[0].transform));
EXPECT_EQ(gfx::Rect(0, 0, 100, 100), hit_test_region_list->regions[0].rect);
}
TEST_P(ClientModeLayerTreeHostImplTest,
ImplThreadPhaseUponImplSideInvalidation) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
auto args = viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
host_impl_->WillBeginImplFrame(args);
host_impl_->InvalidateContentOnImplSide();
host_impl_->DidFinishImplFrame(args);
settings.using_synchronous_renderer_compositor = true;
CreateHostImpl(settings, CreateLayerTreeFrameSink());
host_impl_->InvalidateContentOnImplSide();
}
TEST_P(LayerTreeHostImplTest, SkipOnDrawDoesNotUpdateDrawParams) {
EXPECT_TRUE(CreateHostImpl(DefaultSettings(),
FakeLayerTreeFrameSink::CreateSoftware()));
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
auto* layer = InnerViewportScrollLayer();
layer->SetDrawsContent(true);
gfx::Transform transform;
transform.Translate(20, 20);
gfx::Rect viewport(0, 0, 50, 50);
const bool resourceless_software_draw = false;
bool skip_draw = false;
host_impl_->OnDraw(transform, viewport, resourceless_software_draw,
skip_draw);
EXPECT_EQ(transform, host_impl_->DrawTransform());
EXPECT_EQ(viewport, host_impl_->active_tree()->GetDeviceViewport());
skip_draw = true;
gfx::Transform new_transform;
gfx::Rect new_viewport;
host_impl_->OnDraw(new_transform, new_viewport, resourceless_software_draw,
skip_draw);
EXPECT_EQ(transform, host_impl_->DrawTransform());
EXPECT_EQ(viewport, host_impl_->active_tree()->GetDeviceViewport());
}
TEST_P(LayerTreeHostImplTest, TouchScrollOnAndroidScrollbar) {
LayerTreeImpl* layer_tree_impl = host_impl_->active_tree();
gfx::Size viewport_size = gfx::Size(360, 600);
gfx::Size scroll_content_size = gfx::Size(360, 3800);
gfx::Size scrollbar_size = gfx::Size(15, 600);
SetupViewportLayersNoScrolls(viewport_size);
LayerImpl* content = AddScrollableLayer(OuterViewportScrollLayer(),
viewport_size, scroll_content_size);
auto* scrollbar = AddLayer<SolidColorScrollbarLayerImpl>(
layer_tree_impl, ScrollbarOrientation::kVertical, 10, 0, false);
SetupScrollbarLayer(content, scrollbar);
scrollbar->SetBounds(scrollbar_size);
scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(345, 0));
UpdateDrawProperties(layer_tree_impl);
layer_tree_impl->DidBecomeActive();
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(350, 50), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
}
TEST_F(CommitToPendingTreeLayerTreeHostImplTest,
CommitWithNoPaintWorkletLayerPainter) {
ASSERT_FALSE(host_impl_->GetPaintWorkletLayerPainterForTesting());
host_impl_->CreatePendingTree();
ASSERT_FALSE(did_prepare_tiles_);
host_impl_->CommitComplete();
EXPECT_TRUE(did_prepare_tiles_);
}
TEST_F(CommitToPendingTreeLayerTreeHostImplTest, CommitWithNoPaintWorklets) {
host_impl_->SetPaintWorkletLayerPainter(
std::make_unique<TestPaintWorkletLayerPainter>());
host_impl_->CreatePendingTree();
ASSERT_FALSE(did_prepare_tiles_);
host_impl_->CommitComplete();
EXPECT_TRUE(did_prepare_tiles_);
}
TEST_F(CommitToPendingTreeLayerTreeHostImplTest, CommitWithDirtyPaintWorklets) {
auto painter_owned = std::make_unique<TestPaintWorkletLayerPainter>();
TestPaintWorkletLayerPainter* painter = painter_owned.get();
host_impl_->SetPaintWorkletLayerPainter(std::move(painter_owned));
host_impl_->CreatePendingTree();
auto* root = SetupRootLayer<PictureLayerImpl>(host_impl_->pending_tree(),
gfx::Size(100, 100));
root->SetNeedsPushProperties();
scoped_refptr<RasterSource> raster_source_with_pws =
FakeRasterSource::CreateFilledWithPaintWorklet(root->bounds());
root->SetRasterSourceForTesting(raster_source_with_pws);
UpdateDrawProperties(host_impl_->pending_tree());
did_prepare_tiles_ = false;
host_impl_->CommitComplete();
EXPECT_FALSE(did_prepare_tiles_);
ASSERT_EQ(root->GetPaintWorkletRecordMap().size(), 1u);
scoped_refptr<const PaintWorkletInput> input =
root->GetPaintWorkletRecordMap().begin()->first;
int worklet_id = input->WorkletId();
PaintWorkletJob painted_job(worklet_id, input, {});
PaintRecord record;
painted_job.SetOutput(record);
auto painted_job_vector = base::MakeRefCounted<PaintWorkletJobVector>();
painted_job_vector->data.push_back(std::move(painted_job));
PaintWorkletJobMap painted_job_map;
painted_job_map[worklet_id] = std::move(painted_job_vector);
std::move(painter->TakeDoneCallback()).Run(std::move(painted_job_map));
EXPECT_TRUE(root->GetPaintWorkletRecordMap()
.find(input)
->second.second->EqualsForTesting(record));
EXPECT_TRUE(did_prepare_tiles_);
}
TEST_F(CommitToPendingTreeLayerTreeHostImplTest,
CommitWithNoDirtyPaintWorklets) {
host_impl_->SetPaintWorkletLayerPainter(
std::make_unique<TestPaintWorkletLayerPainter>());
host_impl_->CreatePendingTree();
auto* root = SetupRootLayer<PictureLayerImpl>(host_impl_->pending_tree(),
gfx::Size(100, 100));
root->SetNeedsPushProperties();
scoped_refptr<RasterSource> raster_source_with_pws =
FakeRasterSource::CreateFilledWithPaintWorklet(root->bounds());
root->SetRasterSourceForTesting(raster_source_with_pws);
UpdateDrawProperties(host_impl_->pending_tree());
ASSERT_EQ(root->GetPaintWorkletRecordMap().size(), 1u);
root->SetPaintWorkletRecord(root->GetPaintWorkletRecordMap().begin()->first,
PaintRecord());
ASSERT_FALSE(did_prepare_tiles_);
host_impl_->CommitComplete();
EXPECT_TRUE(did_prepare_tiles_);
}
class ForceActivateAfterPaintWorkletPaintLayerTreeHostImplTest
: public CommitToPendingTreeLayerTreeHostImplTest {
public:
void NotifyPaintWorkletStateChange(
Scheduler::PaintWorkletState state) override {
if (state == Scheduler::PaintWorkletState::IDLE) {
host_impl_->ActivateSyncTree();
ASSERT_FALSE(host_impl_->pending_tree());
}
}
};
TEST_F(ForceActivateAfterPaintWorkletPaintLayerTreeHostImplTest,
ForceActivationAfterPaintWorkletsFinishPainting) {
auto painter_owned = std::make_unique<TestPaintWorkletLayerPainter>();
TestPaintWorkletLayerPainter* painter = painter_owned.get();
host_impl_->SetPaintWorkletLayerPainter(std::move(painter_owned));
host_impl_->CreatePendingTree();
auto* root = SetupRootLayer<PictureLayerImpl>(host_impl_->pending_tree(),
gfx::Size(100, 100));
root->SetNeedsPushProperties();
scoped_refptr<RasterSource> raster_source_with_pws =
FakeRasterSource::CreateFilledWithPaintWorklet(root->bounds());
root->SetRasterSourceForTesting(raster_source_with_pws);
UpdateDrawProperties(host_impl_->pending_tree());
did_prepare_tiles_ = false;
host_impl_->CommitComplete();
EXPECT_FALSE(did_prepare_tiles_);
ASSERT_EQ(root->GetPaintWorkletRecordMap().size(), 1u);
scoped_refptr<const PaintWorkletInput> input =
root->GetPaintWorkletRecordMap().begin()->first;
int worklet_id = input->WorkletId();
PaintWorkletJob painted_job(worklet_id, input, {});
PaintRecord record;
painted_job.SetOutput(record);
auto painted_job_vector = base::MakeRefCounted<PaintWorkletJobVector>();
painted_job_vector->data.push_back(std::move(painted_job));
PaintWorkletJobMap painted_job_map;
painted_job_map[worklet_id] = std::move(painted_job_vector);
std::move(painter->TakeDoneCallback()).Run(std::move(painted_job_map));
EXPECT_TRUE(root->GetPaintWorkletRecordMap()
.find(input)
->second.second->EqualsForTesting(record));
EXPECT_FALSE(did_prepare_tiles_);
}
TEST_P(LayerTreeHostImplTest, PageBasedScroll) {
const gfx::Size kViewportSize(100, 100);
const gfx::Size kContentSize(300, 300);
SetupViewportLayersOuterScrolls(kViewportSize, kContentSize);
DrawFrame();
const gfx::Vector2dF kPageDelta(2, 1);
auto begin_state =
BeginState(gfx::Point(), kPageDelta, ui::ScrollInputType::kWheel);
begin_state->data()->delta_granularity = ui::ScrollGranularity::kScrollByPage;
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(begin_state.get(), ui::ScrollInputType::kWheel)
.thread);
auto update_state =
UpdateState(gfx::Point(), kPageDelta, ui::ScrollInputType::kWheel);
update_state.data()->delta_granularity = ui::ScrollGranularity::kScrollByPage;
GetInputHandler().ScrollUpdate(update_state);
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
base::TimeTicks start_time = base::TimeTicks() + base::Milliseconds(100);
BeginImplFrameAndAnimate(begin_frame_args, start_time);
BeginImplFrameAndAnimate(begin_frame_args,
start_time + base::Milliseconds(50));
BeginImplFrameAndAnimate(begin_frame_args,
start_time + base::Milliseconds(2000));
const gfx::PointF kExpectedOffset(
kPageDelta.x() * kViewportSize.width() * kMinFractionToStepWhenPaging,
kPageDelta.y() * kViewportSize.height() * kMinFractionToStepWhenPaging);
const gfx::PointF kCurrentOffset =
host_impl_->active_tree()
->property_trees()
->scroll_tree()
.current_scroll_offset(
host_impl_->OuterViewportScrollNode()->element_id);
EXPECT_EQ(kExpectedOffset, kCurrentOffset);
GetInputHandler().ScrollEnd();
}
TEST_P(LayerTreeHostImplTest, PageBasedScrollSnap) {
gfx::Size view_size(100, 100);
gfx::Size overflow_size(100, 300);
gfx::RectF snap_area_1(0, 0, 100, 20);
gfx::RectF snap_area_2(0, 20, 100, 40);
gfx::RectF snap_area_3(0, 60, 100, 60);
gfx::RectF snap_area_4(0, 120, 100, 100);
gfx::RectF snap_area_5(0, 220, 100, 80);
SetupViewportLayersInnerScrolls(view_size, view_size);
LayerImpl* overflow =
AddScrollableLayer(OuterViewportScrollLayer(), view_size, overflow_size);
SnapContainerData container(
ScrollSnapType(false, SnapAxis::kY, SnapStrictness::kMandatory),
gfx::RectF(0, 0, 100, 100), gfx::PointF(0, 200));
ScrollSnapAlign start = ScrollSnapAlign(SnapAlignment::kStart);
container.AddSnapAreaData(
SnapAreaData(start, snap_area_1, false, false, ElementId(10)));
container.AddSnapAreaData(
SnapAreaData(start, snap_area_2, false, false, ElementId(20)));
container.AddSnapAreaData(
SnapAreaData(start, snap_area_3, false, false, ElementId(30)));
container.AddSnapAreaData(
SnapAreaData(start, snap_area_4, false, false, ElementId(40)));
container.AddSnapAreaData(
SnapAreaData(start, snap_area_5, false, false, ElementId(50)));
GetScrollNode(overflow)->snap_container_data.emplace(container);
DrawFrame();
gfx::Point position(95, 75);
gfx::Vector2dF kPageDelta(0, 1);
auto begin_state = BeginState(
position, kPageDelta, ui::ScrollInputType::kScrollbar);
begin_state->data()->delta_granularity = ui::ScrollGranularity::kScrollByPage;
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(begin_state.get(), ui::ScrollInputType::kScrollbar)
.thread);
auto update_state = UpdateState(
position, kPageDelta, ui::ScrollInputType::kScrollbar);
update_state.data()->delta_granularity = ui::ScrollGranularity::kScrollByPage;
GetInputHandler().ScrollUpdate(update_state);
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
base::TimeTicks start_time = base::TimeTicks() + base::Milliseconds(100);
BeginImplFrameAndAnimate(begin_frame_args, start_time);
BeginImplFrameAndAnimate(begin_frame_args,
start_time + base::Milliseconds(50));
BeginImplFrameAndAnimate(begin_frame_args,
start_time + base::Milliseconds(2000));
EXPECT_POINTF_EQ(gfx::PointF(0, 60), CurrentScrollOffset(overflow));
GetInputHandler().ScrollEnd();
}
class UnifiedScrollingTest : public LayerTreeHostImplTest {
public:
using ScrollStatus = InputHandler::ScrollStatus;
LayerTreeSettings DefaultSettings() override {
auto settings = LayerTreeHostImplTest::DefaultSettings();
settings.enable_hit_test_opaqueness = true;
return settings;
}
void SetUp() override {
LayerTreeHostImplTest::SetUp();
cur_time_ = base::TimeTicks() + base::Milliseconds(100);
begin_frame_args_ =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
SetupViewportLayersOuterScrolls(gfx::Size(100, 100), gfx::Size(200, 200));
}
void CreateNonCompositedScrollerAndMainThreadScrollHitTestRegion() {
gfx::Size scrollable_content_bounds(100, 100);
gfx::Size container_bounds(50, 50);
CreateScrollNodeForNonCompositedScroller(
GetPropertyTrees(), host_impl_->OuterViewportScrollNode()->id,
ScrollerElementId(), scrollable_content_bounds, container_bounds);
OuterViewportScrollLayer()->SetMainThreadScrollHitTestRegion(
gfx::Rect(50, 50));
host_impl_->active_tree()->set_needs_update_draw_properties();
UpdateDrawProperties(host_impl_->active_tree());
host_impl_->active_tree()->DidBecomeActive();
DrawFrame();
}
void CreateScroller(
uint32_t main_thread_repaint_reasons,
HitTestOpaqueness hit_test_opaqueness = HitTestOpaqueness::kOpaque) {
gfx::Size scrollable_content_bounds(100, 100);
gfx::Size container_bounds(50, 50);
LayerImpl* layer =
AddScrollableLayer(OuterViewportScrollLayer(), container_bounds,
scrollable_content_bounds);
layer->SetHitTestOpaqueness(hit_test_opaqueness);
scroller_layer_ = layer;
GetScrollNode(layer)->main_thread_repaint_reasons =
main_thread_repaint_reasons;
GetScrollNode(layer)->is_composited = true;
UpdateDrawProperties(host_impl_->active_tree());
host_impl_->active_tree()->DidBecomeActive();
DrawFrame();
}
void CreateLayerCoveringWholeViewport(const LayerImpl* parent_scroller,
HitTestOpaqueness opaqueness) {
LayerImpl* layer = AddLayerInActiveTree();
layer->SetBounds(gfx::Size(100, 100));
layer->SetDrawsContent(true);
layer->SetHitTestOpaqueness(opaqueness);
CopyProperties(parent_scroller, layer);
UpdateDrawProperties(host_impl_->active_tree());
}
void CreateLayerCoveringWholeViewportEscapingScrollers(
HitTestOpaqueness opaqueness) {
CreateLayerCoveringWholeViewport(OuterViewportScrollLayer(), opaqueness);
}
void CreateLayerCoveringWholeViewportInScroller(
HitTestOpaqueness opaqueness) {
CreateLayerCoveringWholeViewport(scroller_layer_.get(), opaqueness);
}
ScrollStatus ScrollBegin(const gfx::Vector2d& delta) {
auto scroll_state =
BeginState(gfx::Point(25, 25), delta, ui::ScrollInputType::kWheel);
ScrollStatus status = GetInputHandler().ScrollBegin(
scroll_state.get(), ui::ScrollInputType::kWheel);
if (status.main_thread_hit_test_reasons) {
to_be_continued_scroll_begin_ = std::move(scroll_state);
}
return status;
}
ScrollStatus ContinuedScrollBegin(ElementId element_id) {
DCHECK(to_be_continued_scroll_begin_)
<< "ContinuedScrollBegin needs to come after a ScrollBegin that "
"requested a main frame";
std::unique_ptr<ScrollState> scroll_state =
std::move(to_be_continued_scroll_begin_);
scroll_state->data()->set_current_native_scrolling_element(element_id);
scroll_state->data()->main_thread_hit_tested_reasons =
MainThreadScrollingReason::kFailedHitTest;
return GetInputHandler().ScrollBegin(scroll_state.get(),
ui::ScrollInputType::kWheel);
}
InputHandlerScrollResult ScrollUpdate(const gfx::Vector2d& delta) {
auto scroll_state =
UpdateState(gfx::Point(25, 25), delta, ui::ScrollInputType::kWheel);
return GetInputHandler().ScrollUpdate(scroll_state);
}
InputHandlerScrollResult AnimatedScrollUpdate(const gfx::Vector2d& delta) {
auto scroll_state = AnimatedUpdateState(gfx::Point(25, 25), delta);
return GetInputHandler().ScrollUpdate(scroll_state);
}
void ScrollEnd() { return GetInputHandler().ScrollEnd(); }
void StartAnimation() { BeginFrame(base::TimeDelta()); }
void BeginFrame(base::TimeDelta forward) {
cur_time_ += forward;
begin_frame_args_.frame_time = cur_time_;
begin_frame_args_.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args_);
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
host_impl_->DidFinishImplFrame(begin_frame_args_);
}
gfx::PointF GetScrollOffset(ScrollNode* node) {
return GetPropertyTrees()->scroll_tree().current_scroll_offset(
node->element_id);
}
gfx::PointF ScrollerOffset() {
return GetPropertyTrees()->scroll_tree().current_scroll_offset(
ScrollerElementId());
}
PropertyTrees* GetPropertyTrees() {
return host_impl_->active_tree()->property_trees();
}
ScrollNode* CurrentlyScrollingNode() {
return host_impl_->CurrentlyScrollingNode();
}
ScrollNode* ScrollerNode() {
ScrollNode* node =
GetPropertyTrees()->scroll_tree_mutable().FindNodeFromElementId(
ScrollerElementId());
DCHECK(node);
return node;
}
LayerImpl* ScrollerLayer() const { return scroller_layer_.get(); }
ElementId ScrollerElementId() const {
if (scroller_layer_)
return scroller_layer_->element_id();
return ElementId(1234);
}
base::TimeDelta kFrameInterval = base::Milliseconds(16);
void TestNonCompositedScrollingState(bool mutates_transform_tree);
private:
raw_ptr<LayerImpl> scroller_layer_ = nullptr;
base::TimeTicks cur_time_;
viz::BeginFrameArgs begin_frame_args_;
std::unique_ptr<ScrollState> to_be_continued_scroll_begin_;
base::test::ScopedFeatureList scoped_feature_list;
};
INSTANTIATE_COMMIT_TO_TREE_TEST_P(UnifiedScrollingTest);
TEST_P(UnifiedScrollingTest, UnifiedScrollMainThreadScrollHitTestRegion) {
CreateNonCompositedScrollerAndMainThreadScrollHitTestRegion();
{
ScrollStatus status = ScrollBegin(gfx::Vector2d(0, 10));
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kMainThreadScrollHitTestRegion,
status.main_thread_hit_test_reasons);
EXPECT_FALSE(CurrentlyScrollingNode());
}
{
ScrollStatus status = ContinuedScrollBegin(ScrollerElementId());
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
EXPECT_TRUE(CurrentlyScrollingNode());
EXPECT_EQ(ScrollerNode(), CurrentlyScrollingNode());
}
{
EXPECT_TRUE(ScrollUpdate(gfx::Vector2d(0, 10)).did_scroll);
EXPECT_EQ(gfx::PointF(0, 10), ScrollerOffset());
}
{
EXPECT_TRUE(ScrollUpdate(gfx::Vector2d(0, 1000)).did_scroll);
EXPECT_EQ(gfx::PointF(0, 50), ScrollerOffset());
}
{
EXPECT_FALSE(ScrollUpdate(gfx::Vector2d(0, 10)).did_scroll);
EXPECT_EQ(gfx::PointF(0, 50), ScrollerOffset());
}
GetInputHandler().ScrollEnd();
}
TEST_P(UnifiedScrollingTest, MainThreadHitTestLatchBubbling) {
CreateNonCompositedScrollerAndMainThreadScrollHitTestRegion();
{
ScrollBegin(gfx::Vector2d(0, 1000));
ContinuedScrollBegin(ScrollerElementId());
ScrollUpdate(gfx::Vector2d(0, 1000));
ScrollEnd();
ASSERT_EQ(gfx::PointF(0, 50), ScrollerOffset());
}
{
ScrollStatus status = ScrollBegin(gfx::Vector2d(0, 10));
ASSERT_EQ(MainThreadScrollingReason::kMainThreadScrollHitTestRegion,
status.main_thread_hit_test_reasons);
status = ContinuedScrollBegin(ScrollerElementId());
EXPECT_TRUE(CurrentlyScrollingNode());
EXPECT_EQ(host_impl_->OuterViewportScrollNode(), CurrentlyScrollingNode());
}
}
using UnifiedScrollingDeathTest = UnifiedScrollingTest;
INSTANTIATE_COMMIT_TO_TREE_TEST_P(UnifiedScrollingDeathTest);
TEST_P(UnifiedScrollingDeathTest, EmptyMainThreadHitTest) {
GTEST_FLAG_SET(death_test_style, "threadsafe");
CreateNonCompositedScrollerAndMainThreadScrollHitTestRegion();
{
ElementId kInvalidId;
DCHECK(!kInvalidId);
ScrollStatus status = ScrollBegin(gfx::Vector2d(0, 10));
EXPECT_CHECK_DEATH({ status = ContinuedScrollBegin(kInvalidId); });
}
}
TEST_P(UnifiedScrollingTest, MainThreadHitTestScrollNodeNotFound) {
CreateNonCompositedScrollerAndMainThreadScrollHitTestRegion();
{
ElementId kMixed(42);
DCHECK(!GetPropertyTrees()->scroll_tree().FindNodeFromElementId(kMixed));
ScrollStatus status = ScrollBegin(gfx::Vector2d(0, 10));
status = ContinuedScrollBegin(kMixed);
EXPECT_EQ(ScrollThread::kScrollIgnored, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
}
}
TEST_P(UnifiedScrollingTest, NonCompositedScrollOnCompositor) {
gfx::Size scrollable_content_bounds(100, 100);
gfx::Size container_bounds(50, 50);
CreateScrollNodeForNonCompositedScroller(
GetPropertyTrees(), host_impl_->OuterViewportScrollNode()->id,
ScrollerElementId(), scrollable_content_bounds, container_bounds);
OuterViewportScrollLayer()->SetNonCompositedScrollHitTestRects(
{ScrollHitTestRect{ScrollerElementId(), gfx::Rect(container_bounds)}});
host_impl_->active_tree()->set_needs_update_draw_properties();
UpdateDrawProperties(host_impl_->active_tree());
host_impl_->active_tree()->DidBecomeActive();
DrawFrame();
ScrollStatus status = ScrollBegin(gfx::Vector2d(10, 10));
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
EXPECT_EQ(ScrollerNode(), CurrentlyScrollingNode());
GetInputHandler().ScrollEnd();
}
TEST_P(UnifiedScrollingTest,
LayerMixedHitTestOpaquenessCausesMainThreadHitTest) {
CreateScroller(MainThreadScrollingReason::kNotScrollingOnMain);
CreateLayerCoveringWholeViewportEscapingScrollers(HitTestOpaqueness::kMixed);
{
ScrollStatus status = ScrollBegin(gfx::Vector2d(0, 10));
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest,
status.main_thread_hit_test_reasons);
}
{
ScrollStatus status = ContinuedScrollBegin(ScrollerElementId());
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
EXPECT_TRUE(CurrentlyScrollingNode());
EXPECT_EQ(ScrollerNode(), CurrentlyScrollingNode());
}
}
TEST_P(UnifiedScrollingTest,
LayerMixedHitTestOpaquenessCausesMainThreadHitTest2) {
CreateScroller(MainThreadScrollingReason::kNotScrollingOnMain);
CreateLayerCoveringWholeViewportInScroller(HitTestOpaqueness::kMixed);
CreateLayerCoveringWholeViewportEscapingScrollers(HitTestOpaqueness::kMixed);
CreateLayerCoveringWholeViewportInScroller(HitTestOpaqueness::kMixed);
{
ScrollStatus status = ScrollBegin(gfx::Vector2d(0, 10));
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest,
status.main_thread_hit_test_reasons);
}
{
ScrollStatus status = ContinuedScrollBegin(ScrollerElementId());
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
EXPECT_TRUE(CurrentlyScrollingNode());
EXPECT_EQ(ScrollerNode(), CurrentlyScrollingNode());
}
}
TEST_P(UnifiedScrollingTest, LayerOpaqueToHitTestScrollsOnCompositor) {
CreateScroller(MainThreadScrollingReason::kNotScrollingOnMain);
CreateLayerCoveringWholeViewportEscapingScrollers(HitTestOpaqueness::kOpaque);
{
ScrollStatus status = ScrollBegin(gfx::Vector2d(0, 10));
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
EXPECT_EQ(host_impl_->OuterViewportScrollNode(), CurrentlyScrollingNode());
}
}
TEST_P(UnifiedScrollingTest, FixedLayerOpaqueToHitTestScrollsOnCompositor) {
CreateScroller(MainThreadScrollingReason::kNotScrollingOnMain);
CreateLayerCoveringWholeViewport(InnerViewportScrollLayer(),
HitTestOpaqueness::kOpaque);
{
ScrollStatus status = ScrollBegin(gfx::Vector2d(0, 10));
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
EXPECT_EQ(host_impl_->OuterViewportScrollNode(), CurrentlyScrollingNode());
}
}
TEST_P(UnifiedScrollingTest,
LayerOpaqueToHitTestEscapingScrollersWithMixedToHitTestLayers) {
CreateScroller(MainThreadScrollingReason::kNotScrollingOnMain);
CreateLayerCoveringWholeViewportInScroller(HitTestOpaqueness::kMixed);
CreateLayerCoveringWholeViewportEscapingScrollers(HitTestOpaqueness::kOpaque);
CreateLayerCoveringWholeViewportInScroller(HitTestOpaqueness::kMixed);
{
ScrollStatus status = ScrollBegin(gfx::Vector2d(0, 10));
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest,
status.main_thread_hit_test_reasons);
}
}
TEST_P(UnifiedScrollingTest,
ReliableScrollHitTestWithOpaqueAndMixedToHitTestLayers) {
CreateScroller(MainThreadScrollingReason::kNotScrollingOnMain);
CreateLayerCoveringWholeViewportInScroller(HitTestOpaqueness::kMixed);
CreateLayerCoveringWholeViewportInScroller(HitTestOpaqueness::kOpaque);
CreateLayerCoveringWholeViewportInScroller(HitTestOpaqueness::kMixed);
{
ScrollStatus status = ScrollBegin(gfx::Vector2d(0, 10));
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
EXPECT_EQ(ScrollerNode(), CurrentlyScrollingNode());
}
}
TEST_P(UnifiedScrollingTest, MainThreadScrollingReasonsScrollOnCompositor) {
CreateScroller(
MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects);
{
ScrollStatus status = ScrollBegin(gfx::Vector2d(0, 10));
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_hit_test_reasons);
}
}
TEST_P(UnifiedScrollingTest, UnreliableHitTestOnNonOpaqueToHitTestScroller) {
CreateScroller(MainThreadScrollingReason::kNotScrollingOnMain,
HitTestOpaqueness::kMixed);
{
ScrollStatus status = ScrollBegin(gfx::Vector2d(0, 10));
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest,
status.main_thread_hit_test_reasons);
}
}
TEST_P(UnifiedScrollingTest, ScrollbarLayerClippedByRoundedCorner) {
CreateScroller(MainThreadScrollingReason::kNotScrollingOnMain,
HitTestOpaqueness::kMixed);
auto* scrollbar_layer = AddLayer<PaintedScrollbarLayerImpl>(
host_impl_->active_tree(), ScrollbarOrientation::kVertical, false, true);
CreateEffectNode(ScrollerLayer()).node_or_ancestor_has_fast_rounded_corner =
true;
SetupScrollbarLayer(ScrollerLayer(), scrollbar_layer);
scrollbar_layer->SetBounds(gfx::Size(10, 50));
scrollbar_layer->SetOffsetToTransformParent(gfx::Vector2dF(40, 0));
auto status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(45, 20), gfx::Vector2d(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
EXPECT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_repaint_reasons);
EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest,
status.main_thread_hit_test_reasons);
}
void UnifiedScrollingTest::TestNonCompositedScrollingState(
bool mutates_transform_tree) {
const ScrollTree& scroll_tree = GetPropertyTrees()->scroll_tree();
TransformTree& transform_tree = GetPropertyTrees()->transform_tree_mutable();
TransformNode* transform_node =
transform_tree.Node(ScrollerNode()->transform_id);
{
ASSERT_EQ(transform_node->element_id, ScrollerElementId());
ASSERT_TRUE(transform_node->scrolls);
ASSERT_EQ(gfx::PointF(0, 0), transform_node->scroll_offset());
ASSERT_FALSE(transform_node->transform_changed());
ASSERT_FALSE(transform_node->needs_local_transform_update);
ASSERT_FALSE(transform_tree.needs_update());
}
{
ScrollStatus status = ScrollBegin(gfx::Vector2d(0, 10));
if (status.main_thread_hit_test_reasons) {
ContinuedScrollBegin(ScrollerElementId());
}
ASSERT_EQ(ScrollerNode(), CurrentlyScrollingNode());
did_request_commit_ = false;
ScrollUpdate(gfx::Vector2d(0, 10));
ASSERT_EQ(gfx::PointF(0, 10), ScrollerOffset());
EXPECT_TRUE(did_request_commit_);
EXPECT_EQ(mutates_transform_tree, transform_node->transform_changed());
EXPECT_EQ(mutates_transform_tree,
transform_node->needs_local_transform_update);
EXPECT_EQ(mutates_transform_tree, transform_tree.needs_update());
if (mutates_transform_tree) {
EXPECT_EQ(gfx::PointF(0, 10), transform_node->scroll_offset());
EXPECT_EQ(gfx::PointF(0, 10),
scroll_tree.GetScrollOffsetForScrollTimeline(*ScrollerNode()));
} else {
EXPECT_EQ(gfx::PointF(0, 0), transform_node->scroll_offset());
EXPECT_EQ(gfx::PointF(0, 0),
scroll_tree.GetScrollOffsetForScrollTimeline(*ScrollerNode()));
}
}
{
did_request_commit_ = false;
AnimatedScrollUpdate(gfx::Vector2d(0, 10));
ASSERT_TRUE(
host_impl_->mutator_host()->HasImplOnlyScrollAnimatingElement());
ASSERT_EQ(gfx::PointF(0, 10), ScrollerOffset());
StartAnimation();
BeginFrame(kFrameInterval);
BeginFrame(base::Milliseconds(500));
BeginFrame(kFrameInterval);
ASSERT_EQ(gfx::PointF(0, 20), ScrollerOffset());
EXPECT_TRUE(did_request_commit_);
EXPECT_EQ(mutates_transform_tree, transform_node->transform_changed());
EXPECT_EQ(mutates_transform_tree,
transform_node->needs_local_transform_update);
EXPECT_EQ(mutates_transform_tree, transform_tree.needs_update());
if (mutates_transform_tree) {
EXPECT_EQ(gfx::PointF(0, 20), transform_node->scroll_offset());
EXPECT_EQ(gfx::PointF(0, 20),
scroll_tree.GetScrollOffsetForScrollTimeline(*ScrollerNode()));
} else {
EXPECT_EQ(gfx::PointF(0, 0), transform_node->scroll_offset());
EXPECT_EQ(gfx::PointF(0, 0),
scroll_tree.GetScrollOffsetForScrollTimeline(*ScrollerNode()));
}
}
}
TEST_P(UnifiedScrollingTest, MainThreadReasonsScrollDoesntAffectTransform) {
CreateScroller(
MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects);
TestNonCompositedScrollingState(false);
ASSERT_EQ(ScrollerNode()->main_thread_repaint_reasons,
MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects);
TransformTree& tree = GetPropertyTrees()->transform_tree_mutable();
TransformNode* transform_node = tree.Node(ScrollerNode()->transform_id);
{
ScrollerNode()->main_thread_repaint_reasons =
MainThreadScrollingReason::kNotScrollingOnMain;
UpdateDrawProperties(host_impl_->active_tree());
host_impl_->active_tree()->DidBecomeActive();
ScrollUpdate(gfx::Vector2d(0, 10));
ASSERT_EQ(gfx::PointF(0, 30), ScrollerOffset());
EXPECT_EQ(gfx::PointF(0, 30), transform_node->scroll_offset());
EXPECT_TRUE(transform_node->transform_changed());
EXPECT_TRUE(transform_node->needs_local_transform_update);
EXPECT_TRUE(tree.needs_update());
}
ScrollEnd();
}
TEST_P(UnifiedScrollingTest, NonCompositedScrollerDoesntAffectTransform) {
CreateNonCompositedScrollerAndMainThreadScrollHitTestRegion();
TestNonCompositedScrollingState(false);
ASSERT_FALSE(ScrollerNode()->is_composited);
TransformTree& tree = GetPropertyTrees()->transform_tree_mutable();
TransformNode* transform_node = tree.Node(ScrollerNode()->transform_id);
{
ScrollerNode()->is_composited = true;
UpdateDrawProperties(host_impl_->active_tree());
host_impl_->active_tree()->DidBecomeActive();
ScrollUpdate(gfx::Vector2d(0, 10));
ASSERT_EQ(gfx::PointF(0, 30), ScrollerOffset());
EXPECT_EQ(gfx::PointF(0, 30), transform_node->scroll_offset());
EXPECT_TRUE(transform_node->transform_changed());
EXPECT_TRUE(transform_node->needs_local_transform_update);
EXPECT_TRUE(tree.needs_update());
}
ScrollEnd();
}
TEST_P(UnifiedScrollingTest, CompositedWithSquashedLayerMutatesTransform) {
CreateScroller(MainThreadScrollingReason::kNotScrollingOnMain);
CreateLayerCoveringWholeViewportEscapingScrollers(HitTestOpaqueness::kMixed);
TestNonCompositedScrollingState(true);
ScrollEnd();
}
TEST_P(ClientModeLayerTreeHostImplTest, NonCompositedScrollUsesRaster) {
gfx::Size scrollable_content_bounds(100, 100);
gfx::Size container_bounds(50, 50);
if (!CommitsToActiveTree()) {
CreatePendingTree();
}
auto* sync_tree_root = SetupRootLayer<LayerImpl>(host_impl_->sync_tree(),
scrollable_content_bounds);
sync_tree_root->SetNeedsPushProperties();
auto* scrolling_layer =
AddScrollableLayer(sync_tree_root, container_bounds, gfx::Size());
scrolling_layer->SetNeedsPushProperties();
CreateScrollNodeForNonCompositedScroller(
host_impl_->sync_tree()->property_trees(), sync_tree_root->id(),
scrolling_layer->element_id(), scrollable_content_bounds,
container_bounds);
host_impl_->sync_tree()->set_needs_update_draw_properties();
UpdateDrawProperties(host_impl_->sync_tree());
host_impl_->ActivateSyncTree();
DrawFrame();
ScrollStateData scroll_state_data;
scroll_state_data.set_current_native_scrolling_element(
scrolling_layer->element_id());
scroll_state_data.is_beginning = true;
std::unique_ptr<ScrollState> scroll_state(new ScrollState(scroll_state_data));
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
scroll_state.get(), ui::ScrollInputType::kWheel);
EXPECT_EQ(true, status.raster_inducing);
host_impl_->active_tree()->DidUpdateScrollOffset(
scrolling_layer->element_id(), true);
{
host_impl_->NotifyInputEvent(false);
host_impl_->SetFullViewportDamage();
host_impl_->SetNeedsRedraw(false,
false);
TestFrameData frame;
auto args = viz::CreateBeginFrameArgsForTesting(
BEGINFRAME_FROM_HERE, viz::BeginFrameArgs::kManualSourceId, 1,
base::TimeTicks() + base::Milliseconds(1));
host_impl_->WillBeginImplFrame(args);
EXPECT_EQ(DrawResult::kSuccess, host_impl_->PrepareToDraw(&frame));
host_impl_->InvalidateContentOnImplSide();
if (!CommitsToActiveTree()) {
host_impl_->ActivateSyncTree();
}
std::optional<SubmitInfo> draw_layers_state =
host_impl_->DrawLayers(&frame);
EXPECT_EQ(true, draw_layers_state->invalidate_raster_scroll);
host_impl_->DidDrawAllLayers(frame);
host_impl_->DidFinishImplFrame(args);
}
}
TEST_P(PendingTreeLayerTreeHostImplTest,
ActivatedPendingTreeRetainsRasterMetrics) {
gfx::Size scrollable_content_bounds(100, 100);
gfx::Size container_bounds(50, 50);
if (!CommitsToActiveTree()) {
CreatePendingTree();
}
auto* sync_tree_root = SetupRootLayer<LayerImpl>(host_impl_->sync_tree(),
scrollable_content_bounds);
sync_tree_root->SetNeedsPushProperties();
auto* scrolling_layer =
AddScrollableLayer(sync_tree_root, container_bounds, gfx::Size());
scrolling_layer->SetNeedsPushProperties();
CreateScrollNodeForNonCompositedScroller(
host_impl_->sync_tree()->property_trees(), sync_tree_root->id(),
scrolling_layer->element_id(), scrollable_content_bounds,
container_bounds);
host_impl_->sync_tree()->set_needs_update_draw_properties();
UpdateDrawProperties(host_impl_->sync_tree());
host_impl_->ActivateSyncTree();
DrawFrame();
ScrollStateData scroll_state_data;
scroll_state_data.set_current_native_scrolling_element(
scrolling_layer->element_id());
scroll_state_data.is_beginning = true;
std::unique_ptr<ScrollState> scroll_state(new ScrollState(scroll_state_data));
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
scroll_state.get(), ui::ScrollInputType::kTouchscreen);
EXPECT_EQ(true, status.raster_inducing);
GetInputHandler().RecordScrollBegin(
ui::ScrollInputType::kTouchscreen,
ScrollBeginThreadState::kRasterInducingScroll);
{
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2d(0, 10), ui::ScrollInputType::kTouchscreen));
std::unique_ptr<EventMetrics> metrics = ScrollUpdateEventMetrics::Create(
ui::EventType::kGestureScrollUpdate, ui::ScrollInputType::kTouchscreen,
false,
ScrollUpdateEventMetrics::ScrollUpdateType::kContinued,
10.0f, base::TimeTicks::Now(),
base::TimeTicks::Now() + base::Milliseconds(1), base::TimeTicks(),
base::IdType64<class ui::LatencyInfo>(123));
auto done_callback = base::BindOnce(
[](std::unique_ptr<EventMetrics> metrics, bool handled) {
metrics->SetDispatchStageTimestamp(
EventMetrics::DispatchStage::kRendererCompositorStarted);
return handled ? std::move(metrics) : nullptr;
},
std::move(metrics));
auto scoped_event_monitor =
host_impl_->GetScopedEventMetricsMonitor(std::move(done_callback));
host_impl_->SetNeedsOneBeginImplFrame();
TestFrameData frame;
auto args = viz::CreateBeginFrameArgsForTesting(
BEGINFRAME_FROM_HERE, viz::BeginFrameArgs::kManualSourceId, 1,
base::TimeTicks() + base::Milliseconds(1));
host_impl_->WillBeginImplFrame(args);
EXPECT_EQ(DrawResult::kSuccess, host_impl_->PrepareToDraw(&frame));
}
host_impl_->InvalidateContentOnImplSide();
if (!CommitsToActiveTree()) {
EXPECT_EQ((size_t)1,
host_impl_->pending_tree()
->events_metrics_from_raster_thread_count_for_testing());
host_impl_->ActivateSyncTree();
EXPECT_EQ((size_t)1,
host_impl_->active_tree()
->events_metrics_from_raster_thread_count_for_testing());
}
}
TEST_P(OccludedSurfaceThrottlingLayerTreeHostImplTest,
ThrottleOccludedSurface) {
LayerTreeImpl* tree = host_impl_->active_tree();
gfx::Rect viewport_rect(0, 0, 800, 600);
auto* root = SetupRootLayer<LayerImpl>(tree, viewport_rect.size());
auto* occluded = AddLayer<SurfaceLayerImpl>(tree);
occluded->SetBounds(gfx::Size(400, 300));
occluded->SetDrawsContent(true);
viz::SurfaceId start = MakeSurfaceId(viz::FrameSinkId(1, 2), 1);
viz::SurfaceId end = MakeSurfaceId(viz::FrameSinkId(3, 4), 1);
occluded->SetRange(viz::SurfaceRange(start, end), 2u);
CopyProperties(root, occluded);
auto* occluder = AddLayer<SolidColorLayerImpl>(tree);
occluder->SetBounds(gfx::Size(400, 400));
occluder->SetDrawsContent(true);
occluder->SetContentsOpaque(true);
CopyProperties(root, occluder);
DrawFrame();
EXPECT_EQ(host_impl_->GetFrameSinksToThrottleForTesting(),
base::flat_set<viz::FrameSinkId>{end.frame_sink_id()});
}
TEST_P(LayerTreeHostImplTest, FrameElementIdHitTestSimple) {
SetupDefaultRootLayer();
LayerImpl* frame_layer = AddLayerInActiveTree();
frame_layer->SetBounds(gfx::Size(50, 50));
frame_layer->SetDrawsContent(true);
frame_layer->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
CopyProperties(root_layer(), frame_layer);
CreateTransformNode(frame_layer).visible_frame_element_id = ElementId(0x10);
UpdateDrawProperties(host_impl_->active_tree());
EXPECT_EQ(GetInputHandler().FindFrameElementIdAtPoint(gfx::PointF(10, 10)),
ElementId(0x10));
}
TEST_P(LayerTreeHostImplTest, FrameElementIdHitTestInheritance) {
SetupDefaultRootLayer();
LayerImpl* frame_layer = AddLayerInActiveTree();
frame_layer->SetBounds(gfx::Size(50, 50));
frame_layer->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
CopyProperties(root_layer(), frame_layer);
CreateTransformNode(frame_layer, root_layer()->transform_tree_index())
.visible_frame_element_id = ElementId(0x20);
LayerImpl* child_layer = AddLayerInActiveTree();
child_layer->SetBounds(gfx::Size(50, 50));
child_layer->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
CopyProperties(root_layer(), child_layer);
auto& child_node =
CreateTransformNode(child_layer, frame_layer->transform_tree_index());
child_node.parent_frame_id = frame_layer->transform_tree_index();
child_layer->SetOffsetToTransformParent(gfx::Vector2dF(25, 25));
UpdateDrawProperties(host_impl_->active_tree());
EXPECT_EQ(GetInputHandler().FindFrameElementIdAtPoint(gfx::PointF(15, 15)),
ElementId(0x20));
EXPECT_EQ(GetInputHandler().FindFrameElementIdAtPoint(gfx::PointF(60, 60)),
ElementId(0x20));
}
TEST_P(LayerTreeHostImplTest, FrameElementIdHitTestOverlap) {
SetupDefaultRootLayer();
LayerImpl* frame_layer = AddLayerInActiveTree();
frame_layer->SetBounds(gfx::Size(50, 50));
frame_layer->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
CopyProperties(root_layer(), frame_layer);
CreateTransformNode(frame_layer).visible_frame_element_id = ElementId(0x10);
LayerImpl* occluding_frame_layer = AddLayerInActiveTree();
occluding_frame_layer->SetBounds(gfx::Size(50, 50));
occluding_frame_layer->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
CopyProperties(root_layer(), occluding_frame_layer);
auto& occluding_frame_node = CreateTransformNode(
occluding_frame_layer, frame_layer->transform_tree_index());
occluding_frame_node.visible_frame_element_id = ElementId(0x20);
occluding_frame_node.parent_frame_id = frame_layer->transform_tree_index();
occluding_frame_layer->SetOffsetToTransformParent(gfx::Vector2dF(25, 25));
UpdateDrawProperties(host_impl_->active_tree());
EXPECT_EQ(GetInputHandler().FindFrameElementIdAtPoint(gfx::PointF(15, 15)),
ElementId(0x10));
EXPECT_EQ(GetInputHandler().FindFrameElementIdAtPoint(gfx::PointF(30, 30)),
ElementId(0x20));
}
TEST_P(LayerTreeHostImplTest, FrameElementIdHitTestOverlapSimpleClip) {
SetupDefaultRootLayer();
LayerImpl* frame_layer = AddLayerInActiveTree();
frame_layer->SetBounds(gfx::Size(50, 50));
frame_layer->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
CopyProperties(root_layer(), frame_layer);
CreateTransformNode(frame_layer).visible_frame_element_id = ElementId(0x10);
LayerImpl* clipped_frame_layer = AddLayerInActiveTree();
clipped_frame_layer->SetBounds(gfx::Size(50, 50));
clipped_frame_layer->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
CopyProperties(root_layer(), clipped_frame_layer);
CreateTransformNode(clipped_frame_layer).visible_frame_element_id =
ElementId(0x20);
clipped_frame_layer->SetOffsetToTransformParent(gfx::Vector2dF(25, 25));
auto& clip_node = CreateClipNode(clipped_frame_layer);
clip_node.clip = gfx::RectF(40, 40, 10, 10);
UpdateDrawProperties(host_impl_->active_tree());
EXPECT_EQ(GetInputHandler().FindFrameElementIdAtPoint(gfx::PointF(30, 30)),
ElementId(0x10));
}
TEST_P(LayerTreeHostImplTest, FrameElementIdHitTestOverlapRoundedCorners) {
SetupDefaultRootLayer();
LayerImpl* frame_layer = AddLayerInActiveTree();
frame_layer->SetBounds(gfx::Size(50, 50));
frame_layer->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
CopyProperties(root_layer(), frame_layer);
CreateTransformNode(frame_layer).visible_frame_element_id = ElementId(0x10);
LayerImpl* rounded_frame_layer = AddLayerInActiveTree();
rounded_frame_layer->SetBounds(gfx::Size(50, 50));
rounded_frame_layer->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
CopyProperties(root_layer(), rounded_frame_layer);
CreateTransformNode(rounded_frame_layer, frame_layer->transform_tree_index())
.visible_frame_element_id = ElementId(0x20);
rounded_frame_layer->SetOffsetToTransformParent(gfx::Vector2dF(25, 25));
CreateEffectNode(rounded_frame_layer).mask_filter_info =
gfx::MaskFilterInfo(gfx::RRectF(25, 25, 50, 50, 5));
UpdateDrawProperties(host_impl_->active_tree());
EXPECT_FALSE(
GetInputHandler().FindFrameElementIdAtPoint(gfx::PointF(30, 30)));
}
TEST_P(LayerTreeHostImplTest, FrameElementIdHitTestOverlapSibling) {
SetupDefaultRootLayer();
LayerImpl* frame_layer = AddLayerInActiveTree();
frame_layer->SetBounds(gfx::Size(50, 50));
frame_layer->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
CopyProperties(root_layer(), frame_layer);
CreateTransformNode(frame_layer, root_layer()->transform_tree_index())
.visible_frame_element_id = ElementId(0x20);
LayerImpl* sibling_frame_layer = AddLayerInActiveTree();
sibling_frame_layer->SetBounds(gfx::Size(50, 50));
sibling_frame_layer->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
CopyProperties(root_layer(), sibling_frame_layer);
CreateTransformNode(sibling_frame_layer, root_layer()->transform_tree_index())
.visible_frame_element_id = ElementId(0x30);
sibling_frame_layer->SetOffsetToTransformParent(gfx::Vector2dF(25, 25));
UpdateDrawProperties(host_impl_->active_tree());
EXPECT_EQ(GetInputHandler().FindFrameElementIdAtPoint(gfx::PointF(15, 15)),
ElementId(0x20));
EXPECT_EQ(GetInputHandler().FindFrameElementIdAtPoint(gfx::PointF(60, 60)),
ElementId(0x30));
EXPECT_FALSE(
GetInputHandler().FindFrameElementIdAtPoint(gfx::PointF(30, 30)));
}
TEST_P(LayerTreeHostImplTest, ViewTransitionRequestCausesDamage) {
const gfx::Size viewport_size(100, 100);
SetupDefaultRootLayer(viewport_size);
UpdateDrawProperties(host_impl_->active_tree());
const gfx::Transform draw_transform;
const gfx::Rect draw_viewport(viewport_size);
bool resourceless_software_draw = false;
host_impl_->OnDraw(draw_transform, draw_viewport, resourceless_software_draw,
false);
last_on_draw_frame_.reset();
did_request_redraw_ = false;
host_impl_->OnDraw(draw_transform, draw_viewport, resourceless_software_draw,
false);
EXPECT_FALSE(did_request_redraw_);
EXPECT_TRUE(last_on_draw_frame_->has_no_damage);
last_on_draw_frame_.reset();
did_request_redraw_ = false;
host_impl_->active_tree()->AddViewTransitionRequest(
ViewTransitionRequest::CreateAnimateRenderer(
blink::ViewTransitionToken(), false));
host_impl_->OnDraw(draw_transform, draw_viewport, resourceless_software_draw,
false);
EXPECT_TRUE(did_request_redraw_);
EXPECT_FALSE(last_on_draw_frame_->has_no_damage);
}
TEST_P(LayerTreeHostImplTest, CollectRegionCaptureBounds) {
const auto kFirstId = viz::RegionCaptureCropId::CreateRandom();
const auto kSecondId = viz::RegionCaptureCropId::CreateRandom();
const auto kThirdId = viz::RegionCaptureCropId::CreateRandom();
const auto kFourthId = viz::RegionCaptureCropId::CreateRandom();
const viz::RegionCaptureBounds kRootBounds{
{{kFirstId, gfx::Rect{0, 0, 250, 250}}, {kSecondId, gfx::Rect{}}}};
const viz::RegionCaptureBounds kChildBounds{
{{kThirdId, gfx::Rect{5, 6, 300, 400}}}};
const viz::RegionCaptureBounds kSecondChildBounds{
{{kFourthId, gfx::Rect{20, 10, 400, 500}}}};
LayerImpl* root_layer = SetupDefaultRootLayer(gfx::Size(350, 360));
root_layer->SetCaptureBounds(kRootBounds);
LayerImpl* child_layer = AddLayerInActiveTree();
CopyProperties(root_layer, child_layer);
child_layer->SetCaptureBounds(kChildBounds);
gfx::Transform child_layer_transform;
child_layer_transform.Scale(2.0, 3.0);
child_layer->draw_properties().screen_space_transform =
std::move(child_layer_transform);
LayerImpl* second_child_layer = AddLayerInActiveTree();
CopyProperties(root_layer, second_child_layer);
second_child_layer->SetCaptureBounds(kSecondChildBounds);
gfx::Transform second_layer_transform;
second_layer_transform.Rotate(45);
second_child_layer->draw_properties().screen_space_transform =
std::move(second_layer_transform);
UpdateDrawProperties(host_impl_->active_tree());
DrawFrame();
child_layer->set_contributes_to_drawn_render_surface(true);
second_child_layer->set_contributes_to_drawn_render_surface(true);
const viz::RegionCaptureBounds collected_bounds =
host_impl_->CollectRegionCaptureBounds();
EXPECT_EQ(4u, collected_bounds.bounds().size());
EXPECT_EQ((gfx::Rect{0, 0, 250, 250}),
collected_bounds.bounds().find(kFirstId)->second);
EXPECT_EQ((gfx::Rect{}), collected_bounds.bounds().find(kSecondId)->second);
EXPECT_EQ((gfx::Rect{10, 18, 340, 342}),
collected_bounds.bounds().find(kThirdId)->second);
EXPECT_EQ((gfx::Rect{0, 21, 290, 339}),
collected_bounds.bounds().find(kFourthId)->second);
}
TEST_P(LayerTreeHostImplTest, RecomputeRasterCapsOnLayerTreeFrameSinkUpdate) {
host_impl_->ReleaseLayerTreeFrameSink();
host_impl_ = nullptr;
host_impl_ = LayerTreeHostImpl::Create(
DefaultSettings(), this, &task_runner_provider_, &stats_instrumentation_,
&task_graph_runner_,
AnimationHost::CreateForTesting(ThreadInstance::kImpl), nullptr, 0,
nullptr, nullptr);
InputHandler::Create(static_cast<CompositorDelegateForInput&>(*host_impl_));
host_impl_->SetVisible(true);
auto gpu_raster_layer_tree_frame_sink =
FakeLayerTreeFrameSink::Create3dForGpuRasterization();
host_impl_->InitializeFrameSink(gpu_raster_layer_tree_frame_sink.get());
EXPECT_TRUE(host_impl_->use_gpu_rasterization());
EXPECT_TRUE(host_impl_->can_use_msaa());
layer_tree_frame_sink_ = FakeLayerTreeFrameSink::CreateSoftware();
host_impl_->InitializeFrameSink(layer_tree_frame_sink_.get());
EXPECT_FALSE(host_impl_->use_gpu_rasterization());
EXPECT_FALSE(host_impl_->can_use_msaa());
}
TEST_P(LayerTreeHostImplBrowserControlsTest,
BrowserControlsActivelyScrollingType) {
if (!CommitsToActiveTree()) {
GTEST_SKIP();
}
gfx::Size inner_size = gfx::Size(100, 100);
gfx::Size outer_size = gfx::Size(100, 100);
gfx::Size content_size = gfx::Size(100, 200);
SetupBrowserControlsAndScrollLayerWithVirtualViewport(inner_size, outer_size,
content_size);
LayerTreeImpl* active_tree = host_impl_->active_tree();
scoped_refptr<FakeRasterSource> raster_source(
FakeRasterSource::CreateFilled(content_size));
auto* picture_layer =
AddLayer<FakePictureLayerImpl>(host_impl_->active_tree(), raster_source);
CopyProperties(OuterViewportScrollLayer(), picture_layer);
picture_layer->SetBounds(content_size);
picture_layer->SetDrawsContent(true);
picture_layer->SetNeedsPushProperties();
active_tree->PushPageScaleFromMainThread(1.0f, 1.0f, 2.0f);
DrawFrame();
EXPECT_EQ(ScrollThread::kScrollOnImplThread,
GetInputHandler()
.ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 50),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen)
.thread);
EXPECT_EQ(1, host_impl_->active_tree()->CurrentTopControlsShownRatio());
EXPECT_EQ(host_impl_->active_tree()->GetActivelyScrollingType(),
ActivelyScrollingType::kNone);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2dF(0, 25), ui::ScrollInputType::kTouchscreen));
EXPECT_GT(host_impl_->active_tree()->CurrentTopControlsShownRatio(), 0);
EXPECT_LT(host_impl_->active_tree()->CurrentTopControlsShownRatio(), 1);
EXPECT_EQ(host_impl_->active_tree()->GetActivelyScrollingType(),
ActivelyScrollingType::kPrecise);
GetInputHandler().ScrollUpdate(UpdateState(
gfx::Point(), gfx::Vector2dF(0, 30), ui::ScrollInputType::kTouchscreen));
EXPECT_EQ(0, host_impl_->active_tree()->CurrentTopControlsShownRatio());
EXPECT_EQ(host_impl_->active_tree()->GetActivelyScrollingType(),
ActivelyScrollingType::kPrecise);
GetInputHandler().ScrollEnd();
EXPECT_EQ(0, host_impl_->active_tree()->CurrentTopControlsShownRatio());
EXPECT_EQ(host_impl_->active_tree()->GetActivelyScrollingType(),
ActivelyScrollingType::kNone);
}
TEST_P(LayerTreeHostImplTest, AnimatedScrollSnapStrategyCurrentOffset) {
LayerImpl* snapping_layer = CreateLayerForSnapping();
gfx::Point pointer_position(10, 10);
gfx::Vector2dF delta(0, 10);
GetInputHandler().ScrollBegin(
BeginState(pointer_position, delta, ui::ScrollInputType::kWheel).get(),
ui::ScrollInputType::kWheel);
GetInputHandler().ScrollUpdate(AnimatedUpdateState(pointer_position, delta));
EXPECT_NE(GetInputHandler().snap_strategy_for_testing(), nullptr);
EXPECT_VECTOR2DF_EQ(GetInputHandler()
.snap_strategy_for_testing()
->current_position()
.OffsetFromOrigin(),
delta);
EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(snapping_layer));
}
TEST_P(LayerTreeHostImplTest, NonAnimatedScrollSnapStrategyCurrentOffset) {
LayerImpl* snapping_layer = CreateLayerForSnapping();
gfx::Point pointer_position(10, 10);
gfx::Vector2dF delta(0, 10);
GetInputHandler().ScrollBegin(
BeginState(pointer_position, delta, ui::ScrollInputType::kWheel).get(),
ui::ScrollInputType::kWheel);
GetInputHandler().ScrollUpdate(
UpdateState(pointer_position, delta, ui::ScrollInputType::kWheel));
EXPECT_NE(GetInputHandler().snap_strategy_for_testing(), nullptr);
EXPECT_VECTOR2DF_EQ(GetInputHandler()
.snap_strategy_for_testing()
->current_position()
.OffsetFromOrigin(),
delta);
EXPECT_POINTF_EQ(gfx::PointF(0, 10), CurrentScrollOffset(snapping_layer));
}
TEST_P(LayerTreeHostImplTest, AnimatedViewportScrollSnapStrategyCurrentOffset) {
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(200, 200);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
DrawFrame();
float min_page_scale = 1, max_page_scale = 4;
float page_scale_factor = 2;
host_impl_->active_tree()->PushPageScaleFromMainThread(
page_scale_factor, min_page_scale, max_page_scale);
host_impl_->active_tree()->SetPageScaleOnActiveTree(page_scale_factor);
SetScrollOffsetDelta(InnerViewportScrollLayer(), gfx::Vector2d(50, 50));
SetScrollOffsetDelta(OuterViewportScrollLayer(), gfx::Vector2d(5, 400));
const gfx::Vector2dF scroll_delta = gfx::Vector2dF(0, 60);
const gfx::Point pointer_position = gfx::Point(10, 10);
auto begin_state = BeginState(pointer_position, gfx::Vector2d(0, 60),
ui::ScrollInputType::kWheel);
GetInputHandler().ScrollBegin(begin_state.get(), ui::ScrollInputType::kWheel);
GetInputHandler().ScrollUpdate(
AnimatedUpdateState(pointer_position, scroll_delta));
EXPECT_VECTOR2DF_EQ(
GetInputHandler()
.snap_strategy_for_testing()
->current_position()
.OffsetFromOrigin(),
CurrentScrollOffset(InnerViewportScrollLayer()).OffsetFromOrigin() +
CurrentScrollOffset(OuterViewportScrollLayer()).OffsetFromOrigin() +
scroll_delta);
EXPECT_POINTF_EQ(gfx::PointF(50, 50),
CurrentScrollOffset(InnerViewportScrollLayer()));
EXPECT_POINTF_EQ(gfx::PointF(5, 400),
CurrentScrollOffset(OuterViewportScrollLayer()));
}
TEST_P(LayerTreeHostImplTest,
NonAnimatedViewportScrollSnapStrategyCurrentOffset) {
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(200, 200);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
DrawFrame();
float min_page_scale = 1, max_page_scale = 4;
float page_scale_factor = 2;
host_impl_->active_tree()->PushPageScaleFromMainThread(
page_scale_factor, min_page_scale, max_page_scale);
host_impl_->active_tree()->SetPageScaleOnActiveTree(page_scale_factor);
SetScrollOffsetDelta(InnerViewportScrollLayer(), gfx::Vector2d(50, 50));
SetScrollOffsetDelta(OuterViewportScrollLayer(), gfx::Vector2d(5, 400));
const gfx::Vector2dF scroll_delta = gfx::Vector2dF(0, 60);
const gfx::Point pointer_position = gfx::Point(10, 10);
auto begin_state =
BeginState(pointer_position, scroll_delta, ui::ScrollInputType::kWheel);
GetInputHandler().ScrollBegin(begin_state.get(), ui::ScrollInputType::kWheel);
GetInputHandler().ScrollUpdate(
UpdateState(pointer_position, scroll_delta, ui::ScrollInputType::kWheel));
EXPECT_VECTOR2DF_EQ(
GetInputHandler()
.snap_strategy_for_testing()
->current_position()
.OffsetFromOrigin(),
CurrentScrollOffset(InnerViewportScrollLayer()).OffsetFromOrigin() +
CurrentScrollOffset(OuterViewportScrollLayer()).OffsetFromOrigin());
EXPECT_POINTF_EQ(gfx::PointF(50, 80),
CurrentScrollOffset(InnerViewportScrollLayer()));
EXPECT_POINTF_EQ(gfx::PointF(5, 400),
CurrentScrollOffset(OuterViewportScrollLayer()));
}
TEST_P(LayerTreeHostImplTest, FlingSnapStrategyCurrentOffset) {
gfx::Size viewport_size(100, 100);
gfx::Size content_size(100, 5000);
gfx::RectF snap_area_1(0, 0, 50, 1000);
gfx::RectF snap_area_2(0, 1200, 50, 1000);
SetupViewportLayersInnerScrolls(viewport_size, viewport_size);
LayerImpl* snapping_layer = AddScrollableLayer(OuterViewportScrollLayer(),
viewport_size, content_size);
SnapContainerData container(
ScrollSnapType(false, SnapAxis::kY, SnapStrictness::kMandatory),
gfx::RectF(0, 0, 100, 100), gfx::PointF(0, 4900));
ScrollSnapAlign start = ScrollSnapAlign(SnapAlignment::kStart);
container.AddSnapAreaData(
SnapAreaData(start, snap_area_1, false, false, ElementId(10)));
container.AddSnapAreaData(
SnapAreaData(start, snap_area_2, false, false, ElementId(20)));
GetScrollNode(snapping_layer)->snap_container_data.emplace(container);
DrawFrame();
auto& handler = GetInputHandler();
gfx::PointF initial_offset, target_offset;
gfx::Point position(50, 50);
ui::ScrollInputType type = ui::ScrollInputType::kTouchscreen;
handler.ScrollBegin(BeginState(position, gfx::Vector2dF(0, 950), type).get(),
type);
handler.ScrollUpdate(UpdateState(position, gfx::Vector2dF(0, 950), type));
gfx::Vector2dF fling_displacement = gfx::Vector2dF(0, 400);
handler.GetSnapFlingInfoAndSetAnimatingSnapTarget(
gfx::Vector2dF(0, 100), fling_displacement, &initial_offset,
&target_offset);
EXPECT_EQ(handler.snap_strategy_for_testing()->current_position(),
initial_offset);
EXPECT_EQ(handler.snap_strategy_for_testing()->intended_position(),
initial_offset);
auto scroll_update_state =
UpdateState(position, gfx::Vector2dF(0, 100), type);
scroll_update_state.set_is_in_inertial_phase(true);
handler.ScrollUpdate(scroll_update_state);
EXPECT_EQ(handler.snap_strategy_for_testing()->current_position(),
initial_offset + gfx::Vector2dF(0, 100));
EXPECT_EQ(handler.snap_strategy_for_testing()->intended_position(),
initial_offset + gfx::Vector2dF(0, 100));
const auto* old_snap_strategy = handler.snap_strategy_for_testing().get();
handler.ScrollEndForSnapFling(true);
EXPECT_TRUE(handler.snap_strategy_for_testing().get());
EXPECT_NE(handler.snap_strategy_for_testing().get(), old_snap_strategy);
}
namespace {
class FakeLayerImpl : public LayerImpl {
public:
static std::unique_ptr<FakeLayerImpl> Create(LayerTreeImpl* tree_impl,
int id) {
return base::WrapUnique(new FakeLayerImpl(tree_impl, id));
}
~FakeLayerImpl() override = default;
void SetInInvisibleLayerTree() override {
has_been_in_invisible_layer_tree_ = true;
}
bool has_been_in_invisible_layer_tree() const {
return has_been_in_invisible_layer_tree_;
}
protected:
FakeLayerImpl(LayerTreeImpl* tree_impl, int id) : LayerImpl(tree_impl, id) {}
bool has_been_in_invisible_layer_tree_ = false;
};
}
TEST_P(LayerTreeHostImplTest, VisbilityUpdateToLayers) {
LayerTreeImpl* active_tree = host_impl_->active_tree();
auto* layer = AddLayer<FakeLayerImpl>(active_tree);
EXPECT_FALSE(layer->has_been_in_invisible_layer_tree());
host_impl_->SetVisible(false);
EXPECT_TRUE(layer->has_been_in_invisible_layer_tree());
}
struct PreservationTestCase {
enum class Preserve {
kAllMetrics,
kScrollUpdatesAndEndsOnly,
};
std::string test_name;
bool enable_feature;
FrameSkippedReason reason;
Preserve should_preserve;
bool should_metrics_cause_frame_update;
};
class LayerTreeHostImplEventMetricPreservationTest
: public LayerTreeHostImplTestBase,
public testing::WithParamInterface<PreservationTestCase> {};
INSTANTIATE_TEST_SUITE_P(
LayerTreeHostImplEventMetricPreservationTest,
LayerTreeHostImplEventMetricPreservationTest,
testing::ValuesIn<PreservationTestCase>({
{.test_name = "FeatureDisabledWaitingOnMain",
.enable_feature = false,
.reason = FrameSkippedReason::kWaitingOnMain,
.should_preserve =
PreservationTestCase::Preserve::kScrollUpdatesAndEndsOnly,
.should_metrics_cause_frame_update = false},
{.test_name = "FeatureDisabledNoDamage",
.enable_feature = false,
.reason = FrameSkippedReason::kNoDamage,
.should_preserve =
PreservationTestCase::Preserve::kScrollUpdatesAndEndsOnly,
.should_metrics_cause_frame_update = false},
{.test_name = "FeatureEnabledWaitingOnMain",
.enable_feature = true,
.reason = FrameSkippedReason::kWaitingOnMain,
.should_preserve = PreservationTestCase::Preserve::kAllMetrics,
.should_metrics_cause_frame_update = true},
{.test_name = "FeatureEnabledNoDamage",
.enable_feature = true,
.reason = FrameSkippedReason::kNoDamage,
.should_preserve =
PreservationTestCase::Preserve::kScrollUpdatesAndEndsOnly,
.should_metrics_cause_frame_update = false},
}),
[](const testing::TestParamInfo<
LayerTreeHostImplEventMetricPreservationTest::ParamType>& info) {
return info.param.test_name;
});
TEST_P(LayerTreeHostImplEventMetricPreservationTest, PreserveMetrics) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatureState(
features::kDropMetricsFromNonProducedFramesOnlyIfTheyHadNoDamage,
GetParam().enable_feature);
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
std::vector<EventMetrics*> expected_preserved_metrics_ptrs;
{
TestFrameData frame;
auto args = viz::CreateBeginFrameArgsForTesting(
BEGINFRAME_FROM_HERE, viz::BeginFrameArgs::kManualSourceId, 1,
base::TimeTicks() + base::Milliseconds(16));
host_impl_->WillBeginImplFrame(args);
base::SimpleTestTickClock tick_clock;
auto metrics_array = std::to_array<std::unique_ptr<EventMetrics>>(
{EventMetrics::CreateForTesting(
ui::EventType::kTouchMoved,
base::TimeTicks() + base::Milliseconds(11),
base::TimeTicks() +
base::Milliseconds(12),
&tick_clock,
std::nullopt),
ScrollUpdateEventMetrics::CreateForTesting(
ui::EventType::kGestureScrollUpdate,
ui::ScrollInputType::kTouchscreen, false,
ScrollUpdateEventMetrics::ScrollUpdateType::kContinued,
4.2f,
base::TimeTicks() + base::Milliseconds(13),
base::TimeTicks() +
base::Milliseconds(14),
&tick_clock,
std::nullopt),
EventMetrics::CreateForTesting(
ui::EventType::kTouchReleased,
base::TimeTicks() + base::Milliseconds(15),
base::TimeTicks() +
base::Milliseconds(16),
&tick_clock,
std::nullopt),
ScrollEventMetrics::CreateForTesting(
ui::EventType::kGestureScrollEnd,
ui::ScrollInputType::kTouchscreen,
false,
base::TimeTicks() + base::Milliseconds(17),
base::TimeTicks() +
base::Milliseconds(18),
&tick_clock)});
switch (GetParam().should_preserve) {
case PreservationTestCase::Preserve::kAllMetrics:
std::transform(metrics_array.cbegin(), metrics_array.cend(),
std::back_inserter(expected_preserved_metrics_ptrs),
[](const auto& metrics) { return metrics.get(); });
break;
case PreservationTestCase::Preserve::kScrollUpdatesAndEndsOnly:
expected_preserved_metrics_ptrs.push_back(metrics_array[1].get());
expected_preserved_metrics_ptrs.push_back(metrics_array[3].get());
}
for (auto& metrics : metrics_array) {
EXPECT_NE(metrics, nullptr);
auto scoped_monitor =
host_impl_->GetScopedEventMetricsMonitor(base::BindOnce(
[](std::unique_ptr<EventMetrics> metrics, bool handled) {
bool keep_metrics =
handled ||
EventMetrics::ShouldKeepEvenWithoutCausingFrameUpdate(
metrics->type());
std::unique_ptr<EventMetrics> result =
keep_metrics ? std::move(metrics) : nullptr;
return result;
},
std::move(metrics)));
scoped_monitor->SetSaveMetrics();
}
host_impl_->DidFinishImplFrame(args);
host_impl_->DidNotProduceFrame(viz::BeginFrameAck(), GetParam().reason);
}
{
TestFrameData frame;
auto args = viz::CreateBeginFrameArgsForTesting(
BEGINFRAME_FROM_HERE, viz::BeginFrameArgs::kManualSourceId, 1,
base::TimeTicks() + base::Milliseconds(32));
host_impl_->WillBeginImplFrame(args);
host_impl_->PrepareToDraw(&frame);
std::optional<SubmitInfo> submit_info = host_impl_->DrawLayers(&frame);
EXPECT_THAT(
submit_info->events_metrics.impl_event_metrics,
AllOf(Pointwise(UniquePtrMatches(), expected_preserved_metrics_ptrs),
Each(Pointee(
Property(&EventMetrics::caused_frame_update,
GetParam().should_metrics_cause_frame_update)))));
}
}
class ConcurrentImplOnlyScrollAnimationsTest : public LayerTreeHostImplTest {
public:
gfx::PointF CreateAndTickScrollAnimations();
void CompleteScrollAnimations();
protected:
gfx::PointF current_offset1_ = gfx::PointF(0., 1.);
gfx::PointF current_offset2_ = gfx::PointF(0., 3.);
gfx::PointF target_offset1_ = gfx::PointF(0., 2.);
gfx::PointF target_offset2_ = gfx::PointF(0., 4.);
raw_ptr<LayerImpl> scroller1_;
raw_ptr<LayerImpl> scroller2_;
base::test::ScopedFeatureList scoped_feature_list_;
};
INSTANTIATE_COMMIT_TO_TREE_TEST_P(ConcurrentImplOnlyScrollAnimationsTest);
gfx::PointF
ConcurrentImplOnlyScrollAnimationsTest::CreateAndTickScrollAnimations() {
gfx::Size content_size = gfx::Size(360, 600);
gfx::Size scroll_content_size = gfx::Size(3600, 3800);
SetupViewportLayersNoScrolls(content_size);
scroller1_ = AddScrollableLayer(OuterViewportScrollLayer(), content_size,
scroll_content_size);
scroller2_ = AddScrollableLayer(OuterViewportScrollLayer(), content_size,
scroll_content_size);
AnimationHost* animation_host = GetImplAnimationHost();
animation_host->ImplOnlyScrollAnimationCreate(
scroller1_->element_id(), target_offset1_, current_offset1_,
base::TimeDelta(), base::TimeDelta());
animation_host->ImplOnlyScrollAnimationCreate(
scroller2_->element_id(), target_offset2_, current_offset2_,
base::TimeDelta(), base::TimeDelta());
animation_host->TickAnimations(base::TimeTicks() + base::Milliseconds(200),
host_impl_->GetScrollTree(), true);
EXPECT_EQ(host_impl_->GetScrollTree().current_scroll_offset(
scroller1_->element_id()),
current_offset1_);
EXPECT_EQ(host_impl_->GetScrollTree().current_scroll_offset(
scroller2_->element_id()),
current_offset2_);
animation_host->UpdateAnimationState(true, nullptr);
animation_host->TickAnimations(base::TimeTicks() + base::Milliseconds(300),
host_impl_->GetScrollTree(), true);
EXPECT_GT(host_impl_->GetScrollTree()
.current_scroll_offset(scroller1_->element_id())
.y(),
current_offset1_.y());
gfx::PointF intermediate_scroller2_offset =
host_impl_->GetScrollTree().current_scroll_offset(
scroller2_->element_id());
EXPECT_GT(intermediate_scroller2_offset.y(), current_offset2_.y());
return intermediate_scroller2_offset;
}
void ConcurrentImplOnlyScrollAnimationsTest::CompleteScrollAnimations() {
AnimationHost* animation_host = GetImplAnimationHost();
animation_host->TickAnimations(base::TimeTicks() + base::Milliseconds(600),
host_impl_->GetScrollTree(), true);
animation_host->UpdateAnimationState(true, nullptr);
EXPECT_FALSE(animation_host->ElementHasImplOnlyScrollAnimation(
scroller1_->element_id()));
EXPECT_FALSE(animation_host->ElementHasImplOnlyScrollAnimation(
scroller2_->element_id()));
EXPECT_EQ(host_impl_->GetScrollTree().current_scroll_offset(
scroller1_->element_id()),
target_offset1_);
EXPECT_EQ(host_impl_->GetScrollTree().current_scroll_offset(
scroller2_->element_id()),
target_offset2_);
}
TEST_P(ConcurrentImplOnlyScrollAnimationsTest, Create) {
CreateAndTickScrollAnimations();
CompleteScrollAnimations();
}
TEST_P(ConcurrentImplOnlyScrollAnimationsTest, Update) {
CreateAndTickScrollAnimations();
AnimationHost* animation_host = GetImplAnimationHost();
gfx::Vector2dF update_delta1(1., 1.);
gfx::Vector2dF update_delta2(2., 2.);
target_offset1_ += update_delta1;
target_offset2_ += update_delta2;
animation_host->ImplOnlyScrollAnimationUpdateTarget(
update_delta1, gfx::PointF(1000., 1000.),
base::TimeTicks() + base::Milliseconds(400), base::TimeDelta(),
scroller1_->element_id());
animation_host->ImplOnlyScrollAnimationUpdateTarget(
update_delta2, gfx::PointF(1000., 1000.),
base::TimeTicks() + base::Milliseconds(400), base::TimeDelta(),
scroller2_->element_id());
EXPECT_TRUE(animation_host->ElementHasImplOnlyScrollAnimation(
scroller1_->element_id()));
EXPECT_TRUE(animation_host->ElementHasImplOnlyScrollAnimation(
scroller2_->element_id()));
CompleteScrollAnimations();
}
TEST_P(ConcurrentImplOnlyScrollAnimationsTest, Abort) {
target_offset2_ = CreateAndTickScrollAnimations();
AnimationHost* animation_host = GetImplAnimationHost();
animation_host->ScrollAnimationAbort(scroller2_->element_id());
EXPECT_TRUE(animation_host->ElementHasImplOnlyScrollAnimation(
scroller1_->element_id()));
EXPECT_FALSE(animation_host->ElementHasImplOnlyScrollAnimation(
scroller2_->element_id()));
CompleteScrollAnimations();
}
TEST_P(ConcurrentImplOnlyScrollAnimationsTest, RemovedByCommit) {
target_offset2_ = CreateAndTickScrollAnimations();
AnimationHost* animation_host = GetImplAnimationHost();
host_impl_->active_tree()
->property_trees()
->scroll_tree_mutable()
.RemoveNodes(1);
host_impl_->active_tree()
->property_trees()
->transform_tree_mutable()
.RemoveNodes(1);
animation_host->HandleRemovedScrollAnimatingElements(true);
EXPECT_TRUE(animation_host->ElementHasImplOnlyScrollAnimation(
scroller1_->element_id()));
EXPECT_FALSE(animation_host->ElementHasImplOnlyScrollAnimation(
scroller2_->element_id()));
CompleteScrollAnimations();
}
class ConcurrentSnapAnimationsTest : public LayerTreeHostImplTest {
public:
void SetUp() override {
LayerTreeHostImplTest::SetUp();
gfx::Size viewport_size(100, 100);
gfx::Size content_size(100, 5000);
gfx::RectF snap_area_1(0, 0, 50, 50);
gfx::RectF snap_area_2(0, 1200, 50, 50);
SetupViewportLayersInnerScrolls(viewport_size, viewport_size);
snapping_layer1_ = AddScrollableLayer(OuterViewportScrollLayer(),
viewport_size, content_size);
snapping_layer2_ = AddScrollableLayer(OuterViewportScrollLayer(),
viewport_size, content_size);
SnapContainerData container1(
ScrollSnapType(false, SnapAxis::kY, SnapStrictness::kMandatory),
gfx::RectF(0, 0, 100, 100), gfx::PointF(0, 4900));
SnapContainerData container2(
ScrollSnapType(false, SnapAxis::kY, SnapStrictness::kMandatory),
gfx::RectF(100, 100, 100, 100), gfx::PointF(0, 4900));
ScrollSnapAlign start_alignment = ScrollSnapAlign(SnapAlignment::kStart);
container1.AddSnapAreaData(SnapAreaData(start_alignment, snap_area_1, false,
false, ElementId(10)));
container1.AddSnapAreaData(SnapAreaData(start_alignment, snap_area_2, false,
false, ElementId(20)));
container2.AddSnapAreaData(SnapAreaData(start_alignment, snap_area_1, false,
false, ElementId(30)));
container2.AddSnapAreaData(SnapAreaData(start_alignment, snap_area_2, false,
false, ElementId(40)));
scroll_node1_ = GetScrollNode(snapping_layer1_.get());
scroll_node2_ = GetScrollNode(snapping_layer2_.get());
container1_id_ = scroll_node1_->element_id;
container2_id_ = scroll_node2_->element_id;
scroll_node1_->snap_container_data.emplace(container1);
scroll_node2_->snap_container_data.emplace(container2);
DrawFrame();
}
raw_ptr<LayerImpl> snapping_layer1_ = nullptr;
raw_ptr<LayerImpl> snapping_layer2_ = nullptr;
raw_ptr<ScrollNode> scroll_node1_ = nullptr;
raw_ptr<ScrollNode> scroll_node2_ = nullptr;
ElementId container1_id_;
ElementId container2_id_;
base::test::ScopedFeatureList scoped_feature_list_;
};
INSTANTIATE_COMMIT_TO_TREE_TEST_P(ConcurrentSnapAnimationsTest);
TEST_P(ConcurrentSnapAnimationsTest, TrackAnimatingSnapTargetIds) {
auto& handler = GetInputHandler();
gfx::Point position(50, 50);
ui::ScrollInputType type = ui::ScrollInputType::kTouchscreen;
base::flat_map<ElementId, InputHandler::SnapAnimationData>& snap_state_map =
handler.get_snap_animation_data_map_for_testing();
EXPECT_FALSE(snap_state_map.contains(container1_id_));
EXPECT_FALSE(snap_state_map.contains(container2_id_));
handler.ScrollBegin(BeginState(position, gfx::Vector2dF(0, 150), type).get(),
type);
host_impl_->GetScrollTree().set_currently_scrolling_node(scroll_node1_->id);
EXPECT_FALSE(snap_state_map.contains(container1_id_));
EXPECT_FALSE(snap_state_map.contains(container2_id_));
handler.ScrollUpdate(UpdateState(position, gfx::Vector2dF(0, 150), type));
EXPECT_FALSE(snap_state_map.contains(container1_id_));
EXPECT_FALSE(snap_state_map.contains(container2_id_));
handler.ScrollEnd(true);
EXPECT_TRUE(snap_state_map.contains(container1_id_));
EXPECT_EQ(snap_state_map.at(container1_id_).animating_snap_target_ids_.y,
ElementId(10));
EXPECT_FALSE(snap_state_map.contains(container2_id_));
handler.ScrollBegin(BeginState(position, gfx::Vector2dF(0, 150), type).get(),
type);
host_impl_->GetScrollTree().set_currently_scrolling_node(scroll_node2_->id);
handler.ScrollUpdate(UpdateState(position, gfx::Vector2dF(0, 150), type));
EXPECT_TRUE(snap_state_map.contains(container1_id_));
EXPECT_EQ(snap_state_map.at(container1_id_).animating_snap_target_ids_.y,
ElementId(10));
EXPECT_FALSE(snap_state_map.contains(container2_id_));
handler.ScrollEnd(true);
EXPECT_TRUE(snap_state_map.contains(container1_id_));
EXPECT_EQ(snap_state_map.at(container1_id_).animating_snap_target_ids_.y,
ElementId(10));
EXPECT_TRUE(snap_state_map.contains(container2_id_));
EXPECT_EQ(snap_state_map.at(container2_id_).animating_snap_target_ids_.y,
ElementId(30));
AnimationHost* animation_host = GetImplAnimationHost();
int t = 1;
while (animation_host->HasImplOnlyScrollAnimatingElement()) {
animation_host->TickAnimations(
base::TimeTicks() + base::Milliseconds(t++ * 100),
host_impl_->GetScrollTree(), true);
animation_host->UpdateAnimationState(true, nullptr);
}
handler.ScrollOffsetAnimationFinished(container1_id_);
EXPECT_FALSE(snap_state_map.contains(container1_id_));
EXPECT_TRUE(snap_state_map.contains(container2_id_));
EXPECT_EQ(snap_state_map.at(container2_id_).animating_snap_target_ids_.y,
ElementId(30));
handler.ScrollOffsetAnimationFinished(container2_id_);
EXPECT_FALSE(snap_state_map.contains(container1_id_));
EXPECT_FALSE(snap_state_map.contains(container2_id_));
}
class ElasticOverscrollTest : public LayerTreeHostImplTest {
public:
LayerTreeSettings DefaultSettings() override {
auto settings = LayerTreeHostImplTest::DefaultSettings();
settings.enable_elastic_overscroll = true;
return settings;
}
};
TEST_P(ElasticOverscrollTest, ElasticOverscrollWithoutViewport) {
ASSERT_NE(nullptr,
host_impl_->GetInputHandler().CreateScrollElasticityHelper());
host_impl_->GetInputHandler().DestroyScrollElasticityHelper();
}
INSTANTIATE_COMMIT_TO_TREE_TEST_P(ElasticOverscrollTest);
class OverscrollEffectTest : public LayerTreeHostImplTest {
public:
OverscrollEffectTest() {
scoped_feature_list_.InitAndEnableFeature(
features::kOverscrollEffectOnNonRootScrollers);
}
void SetUp() override {
LayerTreeHostImplTest::SetUp();
SetupViewportLayers(host_impl_->active_tree(), gfx::Size(100, 100),
gfx::Size(100, 100), gfx::Size(200, 200));
host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, 0.1f, 10.f);
host_impl_->active_tree()->UpdateDrawProperties(false, false);
}
protected:
void VerifyOverscrollBehavior(LayerImpl* layer) {
InputHandler& handler = GetInputHandler();
ScrollNode* scroll_node = GetScrollNode(layer);
const gfx::Vector2dF delta(10, 10);
const gfx::Point hit_test_point(20, 20);
auto run_scroll = [&](const OverscrollBehavior& behavior) {
scroll_node->overscroll_behavior = behavior;
layer->SetCurrentScrollOffset(gfx::PointF(100, 100));
handler.ScrollBegin(BeginState(hit_test_point, gfx::Vector2dF(),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
auto result = handler.ScrollUpdate(
UpdateState(hit_test_point, delta, ui::ScrollInputType::kTouchscreen),
base::TimeDelta());
handler.ScrollEnd();
return result.unused_scroll_delta;
};
EXPECT_VECTOR2DF_EQ(
delta, run_scroll(OverscrollBehavior(OverscrollBehavior::Type::kAuto)));
EXPECT_VECTOR2DF_EQ(
gfx::Vector2dF(0, 0),
run_scroll(OverscrollBehavior(OverscrollBehavior::Type::kNone)));
EXPECT_VECTOR2DF_EQ(
gfx::Vector2dF(0, delta.y()),
run_scroll(OverscrollBehavior(OverscrollBehavior::Type::kNone,
OverscrollBehavior::Type::kAuto)));
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
INSTANTIATE_COMMIT_TO_TREE_TEST_P(OverscrollEffectTest);
TEST_P(OverscrollEffectTest, RespectsOverscrollBehaviorOnChild) {
LayerImpl* outer_scroll = OuterViewportScrollLayer();
LayerImpl* child = AddScrollableLayer(outer_scroll, gfx::Size(100, 100),
gfx::Size(200, 200));
child->SetOffsetToTransformParent(gfx::Vector2dF(10, 10));
host_impl_->active_tree()->UpdateDrawProperties(false, false);
VerifyOverscrollBehavior(child);
}
TEST_P(OverscrollEffectTest, RespectsOverscrollBehaviorOnRoot) {
VerifyOverscrollBehavior(OuterViewportScrollLayer());
}
}