Commit 411aca65 authored by Stephen McGruer's avatar Stephen McGruer Committed by Commit Bot

Add compositor support for {start,end}ScrollOffset on ScrollTimelines

This CL pushes the start/end scroll offset information through to the
compositor and updates the currentTime algorithm to make use of it.
Updates to the start/end scroll offset are wired through
blink::WorkletAnimation::UpdateOnCompositor, however the animation is
not invalidated yet if the scroller properties would change (e.g. due
to layout changing). This will be done in a follow-up CL.

Bug: 885196
Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;master.tryserver.blink:linux_trusty_blink_rel
Change-Id: I67da2c1ef26b66314af5891a2356920164fa0b0f
Reviewed-on: https://chromium-review.googlesource.com/c/1271398
Commit-Queue: Stephen McGruer <smcgruer@chromium.org>
Reviewed-by: default avatarMajid Valipour <majidvp@chromium.org>
Cr-Commit-Position: refs/heads/master@{#602028}
parent a2c28559
......@@ -263,7 +263,7 @@ void CreateScrollingNodeForElement(ElementId element_id,
// A scrolling node in cc has a corresponding transform node (See
// |ScrollNode::transform_id|). This setup here creates both nodes and links
// them as they would normally be. This more complete setup is necessary here
// because ScrollTimelin depends on both nodes for its calculations.
// because ScrollTimeline depends on both nodes for its calculations.
TransformNode transform_node;
transform_node.scrolls = true;
transform_node.source_node_id = TransformTree::kRootNodeId;
......@@ -329,7 +329,7 @@ TEST_F(AnimationHostTest, LayerTreeMutatorUpdateReflectsScrollAnimations) {
// Create scroll timeline that links scroll animation and worklet animation
// together. Use timerange so that we have 1:1 time & scroll mapping.
auto scroll_timeline = std::make_unique<ScrollTimeline>(
element_id, ScrollTimeline::Vertical, 100);
element_id, ScrollTimeline::Vertical, base::nullopt, base::nullopt, 100);
// Create a worklet animation that is bound to the scroll timeline.
scoped_refptr<WorkletAnimation> worklet_animation(
......
......@@ -13,17 +13,22 @@ namespace cc {
ScrollTimeline::ScrollTimeline(base::Optional<ElementId> scroller_id,
ScrollDirection orientation,
base::Optional<double> start_scroll_offset,
base::Optional<double> end_scroll_offset,
double time_range)
: active_id_(),
pending_id_(scroller_id),
orientation_(orientation),
start_scroll_offset_(start_scroll_offset),
end_scroll_offset_(end_scroll_offset),
time_range_(time_range) {}
ScrollTimeline::~ScrollTimeline() {}
std::unique_ptr<ScrollTimeline> ScrollTimeline::CreateImplInstance() const {
return std::make_unique<ScrollTimeline>(pending_id_, orientation_,
time_range_);
start_scroll_offset_,
end_scroll_offset_, time_range_);
}
double ScrollTimeline::CurrentTime(const ScrollTree& scroll_tree,
......@@ -56,32 +61,69 @@ double ScrollTimeline::CurrentTime(const ScrollTree& scroll_tree,
double current_offset = (orientation_ == Vertical) ? offset.y() : offset.x();
double max_offset = (orientation_ == Vertical) ? scroll_dimensions.y()
: scroll_dimensions.x();
DCHECK_GE(current_offset, 0);
DCHECK_GE(max_offset, 0);
double resolved_start_scroll_offset = start_scroll_offset_.value_or(0);
double resolved_end_scroll_offset = end_scroll_offset_.value_or(max_offset);
// 3. If current scroll offset is less than startScrollOffset, return an
// unresolved time value if fill is none or forwards, or 0 otherwise.
// TODO(smcgruer): Implement |startScrollOffset| and |fill|.
// TODO(smcgruer): Implement |fill|.
if (current_offset < resolved_start_scroll_offset) {
return std::numeric_limits<double>::quiet_NaN();
}
// 4. If current scroll offset is greater than or equal to endScrollOffset,
// return an unresolved time value if fill is none or backwards, or the
// effective time range otherwise.
// TODO(smcgruer): Implement |endScrollOffset| and |fill|.
// TODO(smcgruer): Implement |fill|.
//
// Note we deliberately break the spec here by only returning if the current
// offset is strictly greater, as that is more in line with the web animation
// spec. See https://github.com/WICG/scroll-animations/issues/19
if (current_offset > resolved_end_scroll_offset) {
return std::numeric_limits<double>::quiet_NaN();
}
// This is not by the spec, but avoids both negative current time and a
// division by zero issue. See
// https://github.com/WICG/scroll-animations/issues/20 and
// https://github.com/WICG/scroll-animations/issues/21
if (resolved_start_scroll_offset >= resolved_end_scroll_offset) {
return std::numeric_limits<double>::quiet_NaN();
}
// 5. Return the result of evaluating the following expression:
// ((current scroll offset - startScrollOffset) /
// (endScrollOffset - startScrollOffset)) * effective time range
return (std::abs(current_offset) / max_offset) * time_range_;
return ((current_offset - resolved_start_scroll_offset) /
(resolved_end_scroll_offset - resolved_start_scroll_offset)) *
time_range_;
}
void ScrollTimeline::PushPropertiesTo(ScrollTimeline* impl_timeline) {
DCHECK(impl_timeline);
impl_timeline->pending_id_ = pending_id_;
// TODO(smcgruer): This leads to incorrect behavior in the current design,
// because we end up using the pending start/end scroll offset for the active
// tree too. Instead we need to either split these (like pending_id_ and
// active_id_) or have a ScrollTimeline per tree.
impl_timeline->start_scroll_offset_ = start_scroll_offset_;
impl_timeline->end_scroll_offset_ = end_scroll_offset_;
}
void ScrollTimeline::PromoteScrollTimelinePendingToActive() {
active_id_ = pending_id_;
}
void ScrollTimeline::UpdateStartAndEndScrollOffsets(
base::Optional<double> start_scroll_offset,
base::Optional<double> end_scroll_offset) {
start_scroll_offset_ = start_scroll_offset;
end_scroll_offset_ = end_scroll_offset;
}
void ScrollTimeline::SetScrollerId(base::Optional<ElementId> pending_id) {
// When the scroller id changes it will first be modified in the pending tree.
// Then later (when the pending tree is promoted to active)
......
......@@ -29,6 +29,8 @@ class CC_ANIMATION_EXPORT ScrollTimeline {
ScrollTimeline(base::Optional<ElementId> scroller_id,
ScrollDirection orientation,
base::Optional<double> start_scroll_offset,
base::Optional<double> end_scroll_offset,
double time_range);
virtual ~ScrollTimeline();
......@@ -43,6 +45,9 @@ class CC_ANIMATION_EXPORT ScrollTimeline {
bool is_active_tree) const;
void SetScrollerId(base::Optional<ElementId> scroller_id);
void UpdateStartAndEndScrollOffsets(
base::Optional<double> start_scroll_offset,
base::Optional<double> end_scroll_offset);
void PushPropertiesTo(ScrollTimeline* impl_timeline);
......@@ -59,6 +64,9 @@ class CC_ANIMATION_EXPORT ScrollTimeline {
// it should base its current time on - either the horizontal or vertical.
ScrollDirection orientation_;
base::Optional<double> start_scroll_offset_;
base::Optional<double> end_scroll_offset_;
// A ScrollTimeline maps from the scroll offset in the scroller to a time
// value based on a 'time range'. See the implementation of CurrentTime or the
// spec for details.
......
......@@ -11,6 +11,8 @@
namespace cc {
namespace {
void SetScrollOffset(PropertyTrees* property_trees,
ElementId scroller_id,
gfx::ScrollOffset offset) {
......@@ -48,6 +50,19 @@ void CreateScrollingElement(PropertyTrees* property_trees,
property_trees->element_id_to_scroll_node_index[scroller_id] = scroll_node_id;
}
// Helper method to calculate the current time, implementing only step 5 of
// https://wicg.github.io/scroll-animations/#current-time-algorithm
double CalculateCurrentTime(double current_scroll_offset,
double start_scroll_offset,
double end_scroll_offset,
double effective_time_range) {
return ((current_scroll_offset - start_scroll_offset) /
(end_scroll_offset - start_scroll_offset)) *
effective_time_range;
}
} // namespace
class ScrollTimelineTest : public ::testing::Test {
public:
ScrollTimelineTest()
......@@ -84,9 +99,9 @@ TEST_F(ScrollTimelineTest, BasicCurrentTimeCalculations) {
double time_range = content_size().height() - container_size().height();
ScrollTimeline vertical_timeline(scroller_id(), ScrollTimeline::Vertical,
time_range);
base::nullopt, base::nullopt, time_range);
ScrollTimeline horizontal_timeline(scroller_id(), ScrollTimeline::Horizontal,
time_range);
base::nullopt, base::nullopt, time_range);
// Unscrolled, both timelines should read a current time of 0.
SetScrollOffset(&property_trees(), scroller_id(), gfx::ScrollOffset());
......@@ -105,7 +120,8 @@ TEST_F(ScrollTimelineTest, BasicCurrentTimeCalculations) {
TEST_F(ScrollTimelineTest, CurrentTimeIsAdjustedForTimeRange) {
// Here we set a time range to 100, which gives the current time the form of
// 'percentage scrolled'.
ScrollTimeline timeline(scroller_id(), ScrollTimeline::Vertical, 100);
ScrollTimeline timeline(scroller_id(), ScrollTimeline::Vertical,
base::nullopt, base::nullopt, 100);
double halfwayY = (content_size().height() - container_size().height()) / 2.;
SetScrollOffset(&property_trees(), scroller_id(),
......@@ -137,7 +153,8 @@ TEST_F(ScrollTimelineTest, ActiveTimeIsSetOnlyAfterPromotion) {
double halfwayY = (content_size().height() - container_size().height()) / 2.;
SetScrollOffset(&pending_tree, scroller_id, gfx::ScrollOffset(0, halfwayY));
ScrollTimeline main_timeline(scroller_id, ScrollTimeline::Vertical, 100);
ScrollTimeline main_timeline(scroller_id, ScrollTimeline::Vertical,
base::nullopt, base::nullopt, 100);
// Now create an impl version of the ScrollTimeline. Initilly this should only
// have a pending scroller id, as the active tree may not yet have the
......@@ -163,7 +180,8 @@ TEST_F(ScrollTimelineTest, ActiveTimeIsSetOnlyAfterPromotion) {
TEST_F(ScrollTimelineTest, CurrentTimeIsAdjustedForPixelSnapping) {
double time_range = content_size().height() - container_size().height();
ScrollTimeline timeline(scroller_id(), ScrollTimeline::Vertical, time_range);
ScrollTimeline timeline(scroller_id(), ScrollTimeline::Vertical,
base::nullopt, base::nullopt, time_range);
SetScrollOffset(&property_trees(), scroller_id(), gfx::ScrollOffset(0, 50));
......@@ -176,4 +194,85 @@ TEST_F(ScrollTimelineTest, CurrentTimeIsAdjustedForPixelSnapping) {
EXPECT_FLOAT_EQ(49.5, timeline.CurrentTime(scroll_tree(), false));
}
TEST_F(ScrollTimelineTest, CurrentTimeHandlesStartScrollOffset) {
double time_range = content_size().height() - container_size().height();
const double start_scroll_offset = 20;
ScrollTimeline timeline(scroller_id(), ScrollTimeline::Vertical,
start_scroll_offset, base::nullopt, time_range);
// Unscrolled, the timeline should read a current time of unresolved, since
// the current offset (0) will be less than the startScrollOffset.
SetScrollOffset(&property_trees(), scroller_id(), gfx::ScrollOffset());
EXPECT_TRUE(std::isnan(timeline.CurrentTime(scroll_tree(), false)));
SetScrollOffset(&property_trees(), scroller_id(), gfx::ScrollOffset(0, 19));
EXPECT_TRUE(std::isnan(timeline.CurrentTime(scroll_tree(), false)));
SetScrollOffset(&property_trees(), scroller_id(), gfx::ScrollOffset(0, 20));
EXPECT_FLOAT_EQ(0, timeline.CurrentTime(scroll_tree(), false));
SetScrollOffset(&property_trees(), scroller_id(), gfx::ScrollOffset(0, 50));
EXPECT_FLOAT_EQ(
CalculateCurrentTime(50, start_scroll_offset, time_range, time_range),
timeline.CurrentTime(scroll_tree(), false));
SetScrollOffset(&property_trees(), scroller_id(), gfx::ScrollOffset(0, 200));
EXPECT_FLOAT_EQ(
CalculateCurrentTime(200, start_scroll_offset, time_range, time_range),
timeline.CurrentTime(scroll_tree(), false));
}
TEST_F(ScrollTimelineTest, CurrentTimeHandlesEndScrollOffset) {
double time_range = content_size().height() - container_size().height();
const double end_scroll_offset = time_range - 20;
ScrollTimeline timeline(scroller_id(), ScrollTimeline::Vertical,
base::nullopt, end_scroll_offset, time_range);
SetScrollOffset(&property_trees(), scroller_id(),
gfx::ScrollOffset(0, time_range));
EXPECT_TRUE(std::isnan(timeline.CurrentTime(scroll_tree(), false)));
SetScrollOffset(&property_trees(), scroller_id(),
gfx::ScrollOffset(0, time_range - 20));
EXPECT_FLOAT_EQ(
CalculateCurrentTime(time_range - 20, 0, end_scroll_offset, time_range),
timeline.CurrentTime(scroll_tree(), false));
SetScrollOffset(&property_trees(), scroller_id(),
gfx::ScrollOffset(0, time_range - 50));
EXPECT_FLOAT_EQ(
CalculateCurrentTime(time_range - 50, 0, end_scroll_offset, time_range),
timeline.CurrentTime(scroll_tree(), false));
SetScrollOffset(&property_trees(), scroller_id(),
gfx::ScrollOffset(0, time_range - 200));
EXPECT_FLOAT_EQ(
CalculateCurrentTime(time_range - 200, 0, end_scroll_offset, time_range),
timeline.CurrentTime(scroll_tree(), false));
}
TEST_F(ScrollTimelineTest, CurrentTimeHandlesCombinedStartAndEndScrollOffset) {
double time_range = content_size().height() - container_size().height();
double start_scroll_offset = 20;
double end_scroll_offset = time_range - 50;
ScrollTimeline timeline(scroller_id(), ScrollTimeline::Vertical,
start_scroll_offset, end_scroll_offset, time_range);
SetScrollOffset(&property_trees(), scroller_id(),
gfx::ScrollOffset(0, time_range - 150));
EXPECT_FLOAT_EQ(CalculateCurrentTime(time_range - 150, start_scroll_offset,
end_scroll_offset, time_range),
timeline.CurrentTime(scroll_tree(), false));
}
TEST_F(ScrollTimelineTest, CurrentTimeHandlesEqualStartAndEndScrollOffset) {
double time_range = content_size().height() - container_size().height();
ScrollTimeline timeline(scroller_id(), ScrollTimeline::Vertical, 20, 20,
time_range);
SetScrollOffset(&property_trees(), scroller_id(), gfx::ScrollOffset(0, 150));
EXPECT_TRUE(std::isnan(timeline.CurrentTime(scroll_tree(), false)));
}
TEST_F(ScrollTimelineTest,
CurrentTimeHandlesStartOffsetLargerThanEndScrollOffset) {
double time_range = content_size().height() - container_size().height();
ScrollTimeline timeline(scroller_id(), ScrollTimeline::Vertical, 50, 10,
time_range);
SetScrollOffset(&property_trees(), scroller_id(), gfx::ScrollOffset(0, 150));
EXPECT_TRUE(std::isnan(timeline.CurrentTime(scroll_tree(), false)));
}
} // namespace cc
......@@ -166,11 +166,15 @@ bool WorkletAnimation::NeedsUpdate(base::TimeTicks monotonic_time,
return needs_update;
}
void WorkletAnimation::SetScrollSourceId(
base::Optional<ElementId> scroller_id) {
void WorkletAnimation::UpdateScrollTimeline(
base::Optional<ElementId> scroller_id,
base::Optional<double> start_scroll_offset,
base::Optional<double> end_scroll_offset) {
// Calling this method implies that we are a ScrollTimeline based animation,
// so the below call is done unchecked.
scroll_timeline_->SetScrollerId(scroller_id);
scroll_timeline_->UpdateStartAndEndScrollOffsets(start_scroll_offset,
end_scroll_offset);
SetNeedsPushProperties();
}
......
......@@ -60,10 +60,11 @@ class CC_ANIMATION_EXPORT WorkletAnimation final
void PushPropertiesTo(Animation* animation_impl) override;
// Should be called when the scroll source of the ScrollTimeline attached to
// this animation has a change in ElementId. Such a change happens when the
// scroll source changes compositing state.
void SetScrollSourceId(base::Optional<ElementId> scroller_id);
// Should be called when the ScrollTimeline attached to this animation has a
// change, such as when the scroll source changes ElementId.
void UpdateScrollTimeline(base::Optional<ElementId> scroller_id,
base::Optional<double> start_scroll_offset,
base::Optional<double> end_scroll_offset);
// Should be called when the pending tree is promoted to active, as this may
// require updating the ElementId for the ScrollTimeline scroll source.
......
......@@ -49,7 +49,11 @@ class WorkletAnimationTest : public AnimationTimelinesTest {
class MockScrollTimeline : public ScrollTimeline {
public:
MockScrollTimeline()
: ScrollTimeline(ElementId(), ScrollTimeline::Vertical, 0) {}
: ScrollTimeline(ElementId(),
ScrollTimeline::Vertical,
base::nullopt,
base::nullopt,
0) {}
MOCK_CONST_METHOD2(CurrentTime, double(const ScrollTree&, bool));
};
......
......@@ -126,48 +126,9 @@ double ScrollTimeline::currentTime(bool& is_null) {
// 2. Otherwise, let current scroll offset be the current scroll offset of
// scrollSource in the direction specified by orientation.
// Depending on the writing-mode and direction, the scroll origin shifts and
// the scroll offset may be negative. The easiest way to deal with this is to
// use only the magnitude of the scroll offset, and compare it to (max-offset
// - min_offset).
PaintLayerScrollableArea* scrollable_area = layout_box->GetScrollableArea();
// Using the absolute value of the scroll offset only makes sense if either
// the max or min scroll offset for a given axis is 0. This should be
// guaranteed by the scroll origin code, but these DCHECKs ensure that.
DCHECK(scrollable_area->MaximumScrollOffset().Height() == 0 ||
scrollable_area->MinimumScrollOffset().Height() == 0);
DCHECK(scrollable_area->MaximumScrollOffset().Width() == 0 ||
scrollable_area->MinimumScrollOffset().Width() == 0);
ScrollOffset scroll_offset = scrollable_area->GetScrollOffset();
ScrollOffset scroll_dimensions = scrollable_area->MaximumScrollOffset() -
scrollable_area->MinimumScrollOffset();
double current_offset;
double max_offset;
bool is_horizontal = layout_box->IsHorizontalWritingMode();
if (orientation_ == Block) {
current_offset =
is_horizontal ? scroll_offset.Height() : scroll_offset.Width();
max_offset =
is_horizontal ? scroll_dimensions.Height() : scroll_dimensions.Width();
} else if (orientation_ == Inline) {
current_offset =
is_horizontal ? scroll_offset.Width() : scroll_offset.Height();
max_offset =
is_horizontal ? scroll_dimensions.Width() : scroll_dimensions.Height();
} else if (orientation_ == Horizontal) {
current_offset = scroll_offset.Width();
max_offset = scroll_dimensions.Width();
} else {
DCHECK(orientation_ == Vertical);
current_offset = scroll_offset.Height();
max_offset = scroll_dimensions.Height();
}
// When using a rtl direction, current_offset grows correctly from 0 to
// max_offset, but is negative. Since our offsets are all just deltas along
// the orientation direction, we can just take the absolute current_offset and
// use that everywhere.
current_offset = std::abs(current_offset);
GetCurrentAndMaxOffset(layout_box, current_offset, max_offset);
double resolved_start_scroll_offset = 0;
double resolved_end_scroll_offset = max_offset;
......@@ -211,30 +172,6 @@ double ScrollTimeline::currentTime(bool& is_null) {
time_range_;
}
void ScrollTimeline::ResolveScrollStartAndEnd(
const LayoutBox* layout_box,
double max_offset,
double& resolved_start_scroll_offset,
double& resolved_end_scroll_offset) {
const ComputedStyle& computed_style = layout_box->StyleRef();
Document& document = layout_box->GetDocument();
const ComputedStyle* root_style =
document.documentElement()
? document.documentElement()->GetComputedStyle()
: document.GetComputedStyle();
CSSToLengthConversionData conversion_data = CSSToLengthConversionData(
&computed_style, root_style, document.GetLayoutView(),
computed_style.EffectiveZoom());
if (start_scroll_offset_) {
resolved_start_scroll_offset = FloatValueForLength(
start_scroll_offset_->ConvertToLength(conversion_data), max_offset);
}
if (end_scroll_offset_) {
resolved_end_scroll_offset = FloatValueForLength(
end_scroll_offset_->ConvertToLength(conversion_data), max_offset);
}
}
Element* ScrollTimeline::scrollSource() {
return scroll_source_.Get();
}
......@@ -275,6 +212,80 @@ Node* ScrollTimeline::ResolvedScrollSource() const {
return scroll_source_;
}
void ScrollTimeline::GetCurrentAndMaxOffset(const LayoutBox* layout_box,
double& current_offset,
double& max_offset) const {
DCHECK(layout_box);
// Depending on the writing-mode and direction, the scroll origin shifts and
// the scroll offset may be negative. The easiest way to deal with this is to
// use only the magnitude of the scroll offset, and compare it to (max_offset
// - min_offset).
PaintLayerScrollableArea* scrollable_area = layout_box->GetScrollableArea();
if (!scrollable_area)
return;
// Using the absolute value of the scroll offset only makes sense if either
// the max or min scroll offset for a given axis is 0. This should be
// guaranteed by the scroll origin code, but these DCHECKs ensure that.
DCHECK(scrollable_area->MaximumScrollOffset().Height() == 0 ||
scrollable_area->MinimumScrollOffset().Height() == 0);
DCHECK(scrollable_area->MaximumScrollOffset().Width() == 0 ||
scrollable_area->MinimumScrollOffset().Width() == 0);
ScrollOffset scroll_offset = scrollable_area->GetScrollOffset();
ScrollOffset scroll_dimensions = scrollable_area->MaximumScrollOffset() -
scrollable_area->MinimumScrollOffset();
bool is_horizontal = layout_box->IsHorizontalWritingMode();
if (orientation_ == Block) {
current_offset =
is_horizontal ? scroll_offset.Height() : scroll_offset.Width();
max_offset =
is_horizontal ? scroll_dimensions.Height() : scroll_dimensions.Width();
} else if (orientation_ == Inline) {
current_offset =
is_horizontal ? scroll_offset.Width() : scroll_offset.Height();
max_offset =
is_horizontal ? scroll_dimensions.Width() : scroll_dimensions.Height();
} else if (orientation_ == Horizontal) {
current_offset = scroll_offset.Width();
max_offset = scroll_dimensions.Width();
} else {
DCHECK(orientation_ == Vertical);
current_offset = scroll_offset.Height();
max_offset = scroll_dimensions.Height();
}
// When using a rtl direction, current_offset grows correctly from 0 to
// max_offset, but is negative. Since our offsets are all just deltas along
// the orientation direction, we can just take the absolute current_offset and
// use that everywhere.
current_offset = std::abs(current_offset);
}
void ScrollTimeline::ResolveScrollStartAndEnd(
const LayoutBox* layout_box,
double max_offset,
double& resolved_start_scroll_offset,
double& resolved_end_scroll_offset) const {
DCHECK(layout_box);
const ComputedStyle& computed_style = layout_box->StyleRef();
Document& document = layout_box->GetDocument();
const ComputedStyle* root_style =
document.documentElement()
? document.documentElement()->GetComputedStyle()
: document.GetComputedStyle();
CSSToLengthConversionData conversion_data = CSSToLengthConversionData(
&computed_style, root_style, document.GetLayoutView(),
computed_style.EffectiveZoom());
if (start_scroll_offset_) {
resolved_start_scroll_offset = FloatValueForLength(
start_scroll_offset_->ConvertToLength(conversion_data), max_offset);
}
if (end_scroll_offset_) {
resolved_end_scroll_offset = FloatValueForLength(
end_scroll_offset_->ConvertToLength(conversion_data), max_offset);
}
}
void ScrollTimeline::AttachAnimation() {
Node* resolved_scroll_source = ResolvedScrollSource();
GetActiveScrollTimelineSet().insert(resolved_scroll_source);
......
......@@ -57,6 +57,14 @@ class CORE_EXPORT ScrollTimeline final : public AnimationTimeline {
ScrollDirection GetOrientation() const { return orientation_; }
void GetCurrentAndMaxOffset(const LayoutBox*,
double& current_offset,
double& max_offset) const;
void ResolveScrollStartAndEnd(const LayoutBox*,
double max_offset,
double& resolved_start_scroll_offset,
double& resolved_end_scroll_offset) const;
// Must be called when this ScrollTimeline is attached/detached from an
// animation.
void AttachAnimation();
......@@ -78,11 +86,6 @@ class CORE_EXPORT ScrollTimeline final : public AnimationTimeline {
CSSPrimitiveValue*,
double);
void ResolveScrollStartAndEnd(const LayoutBox*,
double max_offset,
double& resolved_start_scroll_offset,
double& resolved_end_scroll_offset);
Member<Element> scroll_source_;
ScrollDirection orientation_;
Member<CSSPrimitiveValue> start_scroll_offset_;
......
......@@ -177,15 +177,33 @@ std::unique_ptr<CompositorScrollTimeline> ToCompositorScrollTimeline(
DCHECK(time_range.IsDouble());
// TODO(smcgruer): If the scroll source later gets a LayoutBox (e.g. was
// display:none and now isn't), we need to update the compositor with the
// writing mode to get the correct ScrollDirection conversion.
// display:none and now isn't), we need to update the compositor to have the
// correct orientation and start/end offset information.
LayoutBox* box = scroll_source->GetLayoutBox();
CompositorScrollTimeline::ScrollDirection orientation =
ConvertOrientation(scroll_timeline->GetOrientation(),
box ? box->IsHorizontalWritingMode() : true);
return std::make_unique<CompositorScrollTimeline>(element_id, orientation,
time_range.GetAsDouble());
base::Optional<double> start_scroll_offset;
base::Optional<double> end_scroll_offset;
if (box) {
double current_offset;
double max_offset;
scroll_timeline->GetCurrentAndMaxOffset(box, current_offset, max_offset);
double resolved_start_scroll_offset = 0;
double resolved_end_scroll_offset = max_offset;
scroll_timeline->ResolveScrollStartAndEnd(box, max_offset,
resolved_start_scroll_offset,
resolved_end_scroll_offset);
start_scroll_offset = resolved_start_scroll_offset;
end_scroll_offset = resolved_end_scroll_offset;
}
return std::make_unique<CompositorScrollTimeline>(
element_id, orientation, start_scroll_offset, end_scroll_offset,
time_range.GetAsDouble());
}
void StartEffectOnCompositor(CompositorAnimation* animation,
......@@ -523,9 +541,28 @@ void WorkletAnimation::UpdateOnCompositor() {
}
if (timeline_->IsScrollTimeline()) {
Element* scroll_source = ToScrollTimeline(timeline_)->scrollSource();
compositor_animation_->UpdateScrollTimelineId(
GetCompositorScrollElementId(*scroll_source));
Node* scroll_source = ToScrollTimeline(timeline_)->ResolvedScrollSource();
LayoutBox* box = scroll_source->GetLayoutBox();
base::Optional<double> start_scroll_offset;
base::Optional<double> end_scroll_offset;
if (box) {
double current_offset;
double max_offset;
ToScrollTimeline(timeline_)->GetCurrentAndMaxOffset(box, current_offset,
max_offset);
double resolved_start_scroll_offset = 0;
double resolved_end_scroll_offset = max_offset;
ToScrollTimeline(timeline_)->ResolveScrollStartAndEnd(
box, max_offset, resolved_start_scroll_offset,
resolved_end_scroll_offset);
start_scroll_offset = resolved_start_scroll_offset;
end_scroll_offset = resolved_end_scroll_offset;
}
compositor_animation_->UpdateScrollTimeline(
GetCompositorScrollElementId(*scroll_source), start_scroll_offset,
end_scroll_offset);
}
}
......
......@@ -81,9 +81,13 @@ void CompositorAnimation::AbortKeyframeModel(int keyframe_model_id) {
animation_->AbortKeyframeModel(keyframe_model_id);
}
void CompositorAnimation::UpdateScrollTimelineId(
base::Optional<cc::ElementId> element_id) {
cc::ToWorkletAnimation(animation_.get())->SetScrollSourceId(element_id);
void CompositorAnimation::UpdateScrollTimeline(
base::Optional<cc::ElementId> element_id,
base::Optional<double> start_scroll_offset,
base::Optional<double> end_scroll_offset) {
cc::ToWorkletAnimation(animation_.get())
->UpdateScrollTimeline(element_id, start_scroll_offset,
end_scroll_offset);
}
void CompositorAnimation::NotifyAnimationStarted(base::TimeTicks monotonic_time,
......
......@@ -61,7 +61,9 @@ class PLATFORM_EXPORT CompositorAnimation : public cc::AnimationDelegate {
void PauseKeyframeModel(int keyframe_model_id, double time_offset);
void AbortKeyframeModel(int keyframe_model_id);
void UpdateScrollTimelineId(base::Optional<cc::ElementId>);
void UpdateScrollTimeline(base::Optional<cc::ElementId>,
base::Optional<double> start_scroll_offset,
base::Optional<double> end_scroll_offset);
private:
// cc::AnimationDelegate implementation.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment