Commit 89abdb5d authored by Majid Valipour's avatar Majid Valipour Committed by Commit Bot

[scroll-timeline] Add element-based offset IDL

Implement basic element-based IDL and processing.
- Change existing start and end offset to accept a dictionary
  at the moment it only has target and threshold but no
  edge or rootMargin.
- Add ScrollTimelineOffset that can handle both the existing string
  or the new dictionary based offset.
- Update ScrollTimeline to use new the offset class.
- Implement basic validation logic for new element-based offset.
- Add test verify correct parsing and validation on construction.


This is the first patch in a series. Follow up patch [1] would use this
new offset input to produce the resolved offset.

[1] https://chromium-review.googlesource.com/c/chromium/src/+/2100887


Test: wpt/scroll-animations/constructor.html
Bug: 1023375
Change-Id: Ie14fb2127d089a39379c498b4b15c86e6b2dd272
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2070673
Commit-Queue: Yi Gu <yigu@chromium.org>
Reviewed-by: default avatarMajid Valipour <majidvp@chromium.org>
Reviewed-by: default avatarYutaka Hirano <yhirano@chromium.org>
Reviewed-by: default avatarYi Gu <yigu@chromium.org>
Auto-Submit: Majid Valipour <majidvp@chromium.org>
Cr-Commit-Position: refs/heads/master@{#756082}
parent 6058f825
...@@ -86,6 +86,8 @@ bindings_core_generated_union_type_files = [ ...@@ -86,6 +86,8 @@ bindings_core_generated_union_type_files = [
"$bindings_core_v8_output_dir/string_or_element_creation_options.h", "$bindings_core_v8_output_dir/string_or_element_creation_options.h",
"$bindings_core_v8_output_dir/string_or_performance_measure_options.cc", "$bindings_core_v8_output_dir/string_or_performance_measure_options.cc",
"$bindings_core_v8_output_dir/string_or_performance_measure_options.h", "$bindings_core_v8_output_dir/string_or_performance_measure_options.h",
"$bindings_core_v8_output_dir/string_or_scroll_timeline_element_based_offset.cc",
"$bindings_core_v8_output_dir/string_or_scroll_timeline_element_based_offset.h",
"$bindings_core_v8_output_dir/string_or_string_sequence.cc", "$bindings_core_v8_output_dir/string_or_string_sequence.cc",
"$bindings_core_v8_output_dir/string_or_string_sequence.h", "$bindings_core_v8_output_dir/string_or_string_sequence.h",
"$bindings_core_v8_output_dir/string_or_trusted_html_or_trusted_script_or_trusted_script_url.cc", "$bindings_core_v8_output_dir/string_or_trusted_html_or_trusted_script_or_trusted_script_url.cc",
......
...@@ -714,6 +714,8 @@ generated_interface_sources_in_core = [ ...@@ -714,6 +714,8 @@ generated_interface_sources_in_core = [
"$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_scroll_state.h", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_scroll_state.h",
"$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_scroll_timeline.cc", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_scroll_timeline.cc",
"$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_scroll_timeline.h", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_scroll_timeline.h",
"$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_scroll_timeline_element_based_offset.cc",
"$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_scroll_timeline_element_based_offset.h",
"$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_security_policy_violation_event.cc", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_security_policy_violation_event.cc",
"$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_security_policy_violation_event.h", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_security_policy_violation_event.h",
"$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_selection.cc", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_selection.cc",
...@@ -726,6 +728,8 @@ generated_interface_sources_in_core = [ ...@@ -726,6 +728,8 @@ generated_interface_sources_in_core = [
"$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_shared_worker_global_scope.h", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_shared_worker_global_scope.h",
"$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_static_range.cc", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_static_range.cc",
"$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_static_range.h", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_static_range.h",
"$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_string_or_scroll_timeline_element_based_offset.cc",
"$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_string_or_scroll_timeline_element_based_offset.h",
"$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_style_media.cc", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_style_media.cc",
"$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_style_media.h", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_style_media.h",
"$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_style_property_map.cc", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_style_property_map.cc",
......
...@@ -26,6 +26,7 @@ static_idl_files_in_core = get_path_info( ...@@ -26,6 +26,7 @@ static_idl_files_in_core = get_path_info(
"//third_party/blink/renderer/core/animation/keyframe_effect_options.idl", "//third_party/blink/renderer/core/animation/keyframe_effect_options.idl",
"//third_party/blink/renderer/core/animation/optional_effect_timing.idl", "//third_party/blink/renderer/core/animation/optional_effect_timing.idl",
"//third_party/blink/renderer/core/animation/scroll_timeline.idl", "//third_party/blink/renderer/core/animation/scroll_timeline.idl",
"//third_party/blink/renderer/core/animation/scroll_timeline_element_based_offset.idl",
"//third_party/blink/renderer/core/animation/scroll_timeline_options.idl", "//third_party/blink/renderer/core/animation/scroll_timeline_options.idl",
"//third_party/blink/renderer/core/aom/accessible_node.idl", "//third_party/blink/renderer/core/aom/accessible_node.idl",
"//third_party/blink/renderer/core/aom/accessible_node_list.idl", "//third_party/blink/renderer/core/aom/accessible_node_list.idl",
......
...@@ -206,6 +206,8 @@ blink_core_sources("animation") { ...@@ -206,6 +206,8 @@ blink_core_sources("animation") {
"sampled_effect.h", "sampled_effect.h",
"scroll_timeline.cc", "scroll_timeline.cc",
"scroll_timeline.h", "scroll_timeline.h",
"scroll_timeline_offset.cc",
"scroll_timeline_offset.h",
"scroll_timeline_util.cc", "scroll_timeline_util.cc",
"scroll_timeline_util.h", "scroll_timeline_util.h",
"side_index.h", "side_index.h",
......
...@@ -1986,25 +1986,11 @@ void Animation::DetachCompositorTimeline() { ...@@ -1986,25 +1986,11 @@ void Animation::DetachCompositorTimeline() {
void Animation::UpdateCompositorScrollTimeline() { void Animation::UpdateCompositorScrollTimeline() {
if (!compositor_animation_ || !timeline_) if (!compositor_animation_ || !timeline_)
return; return;
Node* scroll_source = To<ScrollTimeline>(*timeline_).ResolvedScrollSource(); auto& timeline = To<ScrollTimeline>(*timeline_);
LayoutBox* box = scroll_source ? scroll_source->GetLayoutBox() : nullptr; Node* scroll_source = timeline.ResolvedScrollSource();
auto start_scroll_offset = timeline.GetResolvedStartScrollOffset();
base::Optional<double> start_scroll_offset; auto end_scroll_offset = timeline.GetResolvedEndScrollOffset();
base::Optional<double> end_scroll_offset;
if (box) {
double current_offset;
double max_offset;
To<ScrollTimeline>(*timeline_)
.GetCurrentAndMaxOffset(box, current_offset, max_offset);
double resolved_start_scroll_offset = 0;
double resolved_end_scroll_offset = max_offset;
To<ScrollTimeline>(*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_->GetAnimation()->UpdateScrollTimeline( compositor_animation_->GetAnimation()->UpdateScrollTimeline(
scroll_timeline_util::GetCompositorScrollElementId(scroll_source), scroll_timeline_util::GetCompositorScrollElementId(scroll_source),
start_scroll_offset, end_scroll_offset); start_scroll_offset, end_scroll_offset);
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#define THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_SCROLL_TIMELINE_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_SCROLL_TIMELINE_H_
#include "third_party/blink/renderer/core/animation/animation_timeline.h" #include "third_party/blink/renderer/core/animation/animation_timeline.h"
#include "third_party/blink/renderer/core/animation/scroll_timeline_offset.h"
#include "third_party/blink/renderer/core/animation/timing.h" #include "third_party/blink/renderer/core/animation/timing.h"
#include "third_party/blink/renderer/core/css/css_primitive_value.h" #include "third_party/blink/renderer/core/css/css_primitive_value.h"
#include "third_party/blink/renderer/core/dom/element.h" #include "third_party/blink/renderer/core/dom/element.h"
...@@ -43,8 +44,8 @@ class CORE_EXPORT ScrollTimeline : public AnimationTimeline { ...@@ -43,8 +44,8 @@ class CORE_EXPORT ScrollTimeline : public AnimationTimeline {
ScrollTimeline(Document*, ScrollTimeline(Document*,
Element*, Element*,
ScrollDirection, ScrollDirection,
CSSPrimitiveValue*, ScrollTimelineOffset*,
CSSPrimitiveValue*, ScrollTimelineOffset*,
double); double);
// AnimationTimeline implementation. // AnimationTimeline implementation.
...@@ -61,25 +62,35 @@ class CORE_EXPORT ScrollTimeline : public AnimationTimeline { ...@@ -61,25 +62,35 @@ class CORE_EXPORT ScrollTimeline : public AnimationTimeline {
// IDL API implementation. // IDL API implementation.
Element* scrollSource(); Element* scrollSource();
String orientation(); String orientation();
String startScrollOffset(); void startScrollOffset(
String endScrollOffset(); StringOrScrollTimelineElementBasedOffset& result) const;
void endScrollOffset(StringOrScrollTimelineElementBasedOffset& result) const;
void timeRange(DoubleOrScrollTimelineAutoKeyword&); void timeRange(DoubleOrScrollTimelineAutoKeyword&);
// Returns the Node that should actually have the ScrollableArea (if one // Returns the Node that should actually have the ScrollableArea (if one
// exists). This can differ from |scrollSource| when |scroll_source_| is the // exists). This can differ from |scrollSource| when |scroll_source_| is the
// Document's scrollingElement, and it may be null if the document was removed // Document's scrollingElement, and it may be null if the document was
// before the ScrollTimeline was created. // removed before the ScrollTimeline was created.
Node* ResolvedScrollSource() const { return resolved_scroll_source_; } Node* ResolvedScrollSource() const { return resolved_scroll_source_; }
// Return the latest resolved start scroll offset. This will be nullopt when
// timeline is inactive.
base::Optional<double> GetResolvedStartScrollOffset() const {
return timeline_state_snapshotted_.start_offset;
}
// Return the latest resolved end scroll offset. This will be nullopt when
// timeline is inactive.
base::Optional<double> GetResolvedEndScrollOffset() const {
return timeline_state_snapshotted_.end_offset;
}
ScrollDirection GetOrientation() const { return orientation_; } ScrollDirection GetOrientation() const { return orientation_; }
void GetCurrentAndMaxOffset(const LayoutBox*, void GetCurrentAndMaxOffset(const LayoutBox*,
double& current_offset, double& current_offset,
double& max_offset) const; double& max_offset) const;
void ResolveScrollStartAndEnd(const LayoutBox*,
double max_offset,
double& resolved_start_scroll_offset,
double& resolved_end_scroll_offset) const;
// Invalidates scroll timeline as a result of scroller properties change. // Invalidates scroll timeline as a result of scroller properties change.
// This may lead the timeline to request a new animation frame. // This may lead the timeline to request a new animation frame.
virtual void Invalidate(); virtual void Invalidate();
...@@ -113,17 +124,45 @@ class CORE_EXPORT ScrollTimeline : public AnimationTimeline { ...@@ -113,17 +124,45 @@ class CORE_EXPORT ScrollTimeline : public AnimationTimeline {
bool ComputeIsActive() const; bool ComputeIsActive() const;
PhaseAndTime ComputeCurrentPhaseAndTime() const; PhaseAndTime ComputeCurrentPhaseAndTime() const;
// Resolve scroll offsets The resolution process turns length-based values
// into concrete length values resolving percentages and zoom factor. For
// element-based values it computes the corresponding length value that maps
// to the particular element intersection. See
// |ScrollTimelineOffset::ResolveOffset()| for more details.
void ResolveScrollOffsets(double* start_offset, double* end_offset) const;
struct TimelineState {
TimelinePhase phase;
base::Optional<base::TimeDelta> current_time;
// The resolved version of start and end offset. These values are nullopts
// when timeline is inactive (e.g., when source does not overflow).
base::Optional<double> start_offset;
base::Optional<double> end_offset;
bool operator==(const TimelineState& other) const {
return phase == other.phase && current_time == other.current_time &&
start_offset == other.start_offset &&
end_offset == other.end_offset;
}
};
TimelineState ComputeTimelineState() const;
// Use |scroll_source_| only to implement the web-exposed API but use // Use |scroll_source_| only to implement the web-exposed API but use
// resolved_scroll_source_ to actually access the scroll related properties. // resolved_scroll_source_ to actually access the scroll related properties.
Member<Element> scroll_source_; Member<Element> scroll_source_;
Member<Node> resolved_scroll_source_; Member<Node> resolved_scroll_source_;
ScrollDirection orientation_; ScrollDirection orientation_;
Member<CSSPrimitiveValue> start_scroll_offset_;
Member<CSSPrimitiveValue> end_scroll_offset_; // These define the total range of the scroller that the ScrollTimeline is
// active within.
Member<ScrollTimelineOffset> start_scroll_offset_;
Member<ScrollTimelineOffset> end_scroll_offset_;
double time_range_; double time_range_;
// Snapshotted value produced by the last SnapshotState call. // Snapshotted value produced by the last SnapshotState call.
PhaseAndTime phase_and_time_snapshotted_; TimelineState timeline_state_snapshotted_;
}; };
template <> template <>
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
// found in the LICENSE file. // found in the LICENSE file.
// https://wicg.github.io/scroll-animations/#scrolltimeline-interface // https://wicg.github.io/scroll-animations/#scrolltimeline-interface
[ [
RuntimeEnabled=ScrollTimeline, RuntimeEnabled=ScrollTimeline,
Exposed=Window Exposed=Window
...@@ -11,7 +10,7 @@ ...@@ -11,7 +10,7 @@
[CallWith=Document, RaisesException, MeasureAs=ScrollTimelineConstructor] constructor(optional ScrollTimelineOptions options = {}); [CallWith=Document, RaisesException, MeasureAs=ScrollTimelineConstructor] constructor(optional ScrollTimelineOptions options = {});
readonly attribute Element? scrollSource; readonly attribute Element? scrollSource;
readonly attribute ScrollDirection orientation; readonly attribute ScrollDirection orientation;
readonly attribute DOMString startScrollOffset; readonly attribute (DOMString or ScrollTimelineElementBasedOffset) startScrollOffset;
readonly attribute DOMString endScrollOffset; readonly attribute (DOMString or ScrollTimelineElementBasedOffset) endScrollOffset;
readonly attribute (double or ScrollTimelineAutoKeyword) timeRange; readonly attribute (double or ScrollTimelineAutoKeyword) timeRange;
}; };
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Experimental IDL of element-based offsets based on this proposal:
// https://github.com/w3c/csswg-drafts/issues/4337
dictionary ScrollTimelineElementBasedOffset {
Element target;
double threshold = 0.0;
// TODO(majidvp): Add other values from proposal. http://crbug.com/1023375
// Edge edge = "start";
// DOMString rootMargin;
};
\ No newline at end of file
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/animation/scroll_timeline_offset.h"
#include "third_party/blink/renderer/bindings/core/v8/string_or_scroll_timeline_element_based_offset.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_scroll_timeline_element_based_offset.h"
#include "third_party/blink/renderer/core/css/css_to_length_conversion_data.h"
#include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
#include "third_party/blink/renderer/core/css/parser/css_tokenizer.h"
#include "third_party/blink/renderer/core/dom/node_computed_style.h"
#include "third_party/blink/renderer/core/layout/layout_box.h"
#include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/platform/geometry/length_functions.h"
namespace blink {
namespace {
bool StringToScrollOffset(String scroll_offset,
const CSSParserContext& context,
CSSPrimitiveValue** result) {
CSSTokenizer tokenizer(scroll_offset);
const auto tokens = tokenizer.TokenizeToEOF();
CSSParserTokenRange range(tokens);
CSSValue* value = css_parsing_utils::ConsumeScrollOffset(range, context);
if (!value)
return false;
// We support 'auto', but for simplicity just store it as nullptr.
*result = DynamicTo<CSSPrimitiveValue>(value);
return true;
}
bool ValidateIntersectionBasedOffset(ScrollTimelineElementBasedOffset* offset) {
if (!offset->hasTarget())
return false;
if (offset->hasThreshold()) {
if (offset->threshold() < 0 || offset->threshold() > 1)
return false;
}
return true;
}
} // namespace
// static
ScrollTimelineOffset* ScrollTimelineOffset::Create(
const StringOrScrollTimelineElementBasedOffset& input_offset,
const CSSParserContext& context) {
if (input_offset.IsString()) {
CSSPrimitiveValue* offset = nullptr;
if (!StringToScrollOffset(input_offset.GetAsString(), context, &offset)) {
return nullptr;
}
return MakeGarbageCollected<ScrollTimelineOffset>(offset);
} else if (input_offset.IsScrollTimelineElementBasedOffset()) {
auto* offset = input_offset.GetAsScrollTimelineElementBasedOffset();
if (!ValidateIntersectionBasedOffset(offset))
return nullptr;
return MakeGarbageCollected<ScrollTimelineOffset>(offset);
} else {
// The default case is "auto" which we initialized with null
return MakeGarbageCollected<ScrollTimelineOffset>();
}
}
double ScrollTimelineOffset::ResolveOffset(Node* scroll_source,
ScrollOrientation orientation,
double max_offset,
double default_offset) {
const LayoutBox* root_box = scroll_source->GetLayoutBox();
DCHECK(root_box);
Document& document = root_box->GetDocument();
if (scroll_based_) {
// Resolve scroll based offset.
const ComputedStyle& computed_style = root_box->StyleRef();
const ComputedStyle* root_style =
document.documentElement()
? document.documentElement()->GetComputedStyle()
: document.GetComputedStyle();
CSSToLengthConversionData conversion_data = CSSToLengthConversionData(
&computed_style, root_style, document.GetLayoutView(),
computed_style.EffectiveZoom());
double resolved = FloatValueForLength(
scroll_based_->ConvertToLength(conversion_data), max_offset);
return resolved;
} else if (element_based) {
// TODO(majidvp): turn element based info into an offset.
// http://crbug.com/1023375
return default_offset;
} else {
return default_offset;
}
}
StringOrScrollTimelineElementBasedOffset
ScrollTimelineOffset::ToStringOrScrollTimelineElementBasedOffset() const {
StringOrScrollTimelineElementBasedOffset result;
if (scroll_based_) {
result.SetString(scroll_based_->CssText());
} else if (element_based) {
result.SetScrollTimelineElementBasedOffset(element_based);
} else {
// we are dealing with default value
result.SetString("auto");
}
return result;
}
ScrollTimelineOffset::ScrollTimelineOffset(CSSPrimitiveValue* offset)
: scroll_based_(offset), element_based(nullptr) {}
ScrollTimelineOffset::ScrollTimelineOffset(
ScrollTimelineElementBasedOffset* offset)
: scroll_based_(nullptr), element_based(offset) {}
void ScrollTimelineOffset::Trace(blink::Visitor* visitor) {
visitor->Trace(scroll_based_);
visitor->Trace(element_based);
}
} // namespace blink
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_SCROLL_TIMELINE_OFFSET_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_SCROLL_TIMELINE_OFFSET_H_
#include "third_party/blink/renderer/core/animation/scroll_timeline_element_based_offset.h"
#include "third_party/blink/renderer/core/css/properties/css_parsing_utils.h"
#include "third_party/blink/renderer/core/scroll/scroll_types.h"
namespace blink {
class StringOrScrollTimelineElementBasedOffset;
// Represent a scroll timeline start/end offset which can be an
// scroll offset or an element based offset
class CORE_EXPORT ScrollTimelineOffset final
: public GarbageCollected<ScrollTimelineOffset> {
public:
static ScrollTimelineOffset* Create(
const StringOrScrollTimelineElementBasedOffset& offset,
const CSSParserContext& context);
// Create a default offset representing 'auto'.
ScrollTimelineOffset() = default;
// Create a scroll based offset.
explicit ScrollTimelineOffset(CSSPrimitiveValue*);
// Create an element based offset.
explicit ScrollTimelineOffset(ScrollTimelineElementBasedOffset*);
void Trace(blink::Visitor*);
// Resolves this offset against the scroll source and in the given orientation
// returning eqiuvalent concrete scroll offset.
//
// - Length-based values are converted into concrete length values resolving
// percentages and zoom factor.
// - Element-based values are resolved to the equivalent scroll offset that
// satisfy the requirement.
// - Auto value simply returns the |detfault_offset|.
//
// max offset is expected to be the maximum scroll offset in the scroll
// orientation.
double ResolveOffset(Node* scroll_source,
ScrollOrientation,
double max_offset,
double default_offset);
StringOrScrollTimelineElementBasedOffset
ToStringOrScrollTimelineElementBasedOffset() const;
private:
// We either have an scroll or element based offset so at any time one of
// these is null. If both are null, it represents the default value of
// 'auto'.
Member<CSSPrimitiveValue> scroll_based_;
Member<ScrollTimelineElementBasedOffset> element_based;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_SCROLL_TIMELINE_OFFSET_H_
...@@ -16,7 +16,8 @@ enum ScrollTimelineAutoKeyword { "auto" }; ...@@ -16,7 +16,8 @@ enum ScrollTimelineAutoKeyword { "auto" };
dictionary ScrollTimelineOptions { dictionary ScrollTimelineOptions {
Element? scrollSource = null; Element? scrollSource = null;
ScrollDirection orientation = "block"; ScrollDirection orientation = "block";
DOMString startScrollOffset = "auto"; (DOMString or ScrollTimelineElementBasedOffset) startScrollOffset = "auto";
DOMString endScrollOffset = "auto"; (DOMString or ScrollTimelineElementBasedOffset) endScrollOffset = "auto";
(double or ScrollTimelineAutoKeyword) timeRange = "auto"; (double or ScrollTimelineAutoKeyword) timeRange = "auto";
}; };
...@@ -17,6 +17,16 @@ ...@@ -17,6 +17,16 @@
namespace blink { namespace blink {
namespace {
StringOrScrollTimelineElementBasedOffset OffsetFromString(const String& value) {
StringOrScrollTimelineElementBasedOffset result;
result.SetString(value);
return result;
}
} // namespace
class ScrollTimelineTest : public RenderingTest { class ScrollTimelineTest : public RenderingTest {
void SetUp() override { void SetUp() override {
EnableCompositing(); EnableCompositing();
...@@ -34,15 +44,19 @@ class ScrollTimelineTest : public RenderingTest { ...@@ -34,15 +44,19 @@ class ScrollTimelineTest : public RenderingTest {
class TestScrollTimeline : public ScrollTimeline { class TestScrollTimeline : public ScrollTimeline {
public: public:
TestScrollTimeline( TestScrollTimeline(Document* document,
Document* document, Element* scroll_source,
Element* scroll_source, ScrollTimelineOffset* start_scroll_offset =
CSSPrimitiveValue* start_scroll_offset = MakeGarbageCollected<ScrollTimelineOffset>(
CSSNumericLiteralValue::Create(10.0, CSSNumericLiteralValue::Create(
CSSPrimitiveValue::UnitType::kPixels), 10.0,
CSSPrimitiveValue* end_scroll_offset = CSSPrimitiveValue::UnitType::kPixels)),
CSSNumericLiteralValue::Create(90.0,
CSSPrimitiveValue::UnitType::kPixels)) ScrollTimelineOffset* end_scroll_offset =
MakeGarbageCollected<ScrollTimelineOffset>(
CSSNumericLiteralValue::Create(
90.0,
CSSPrimitiveValue::UnitType::kPixels)))
: ScrollTimeline(document, : ScrollTimeline(document,
scroll_source, scroll_source,
ScrollTimeline::Vertical, ScrollTimeline::Vertical,
...@@ -110,8 +124,8 @@ TEST_F(ScrollTimelineTest, ...@@ -110,8 +124,8 @@ TEST_F(ScrollTimelineTest,
DoubleOrScrollTimelineAutoKeyword::FromDouble(100); DoubleOrScrollTimelineAutoKeyword::FromDouble(100);
options->setTimeRange(time_range); options->setTimeRange(time_range);
options->setScrollSource(GetElementById("scroller")); options->setScrollSource(GetElementById("scroller"));
options->setStartScrollOffset("10px"); options->setStartScrollOffset(OffsetFromString("10px"));
options->setEndScrollOffset("90px"); options->setEndScrollOffset(OffsetFromString("90px"));
ScrollTimeline* scroll_timeline = ScrollTimeline* scroll_timeline =
ScrollTimeline::Create(GetDocument(), options, ASSERT_NO_EXCEPTION); ScrollTimeline::Create(GetDocument(), options, ASSERT_NO_EXCEPTION);
...@@ -187,8 +201,8 @@ TEST_F(ScrollTimelineTest, ...@@ -187,8 +201,8 @@ TEST_F(ScrollTimelineTest,
DoubleOrScrollTimelineAutoKeyword::FromDouble(100); DoubleOrScrollTimelineAutoKeyword::FromDouble(100);
options->setTimeRange(time_range); options->setTimeRange(time_range);
options->setScrollSource(GetElementById("scroller")); options->setScrollSource(GetElementById("scroller"));
options->setStartScrollOffset("80px"); options->setStartScrollOffset(OffsetFromString("80px"));
options->setEndScrollOffset("40px"); options->setEndScrollOffset(OffsetFromString("40px"));
ScrollTimeline* scroll_timeline = ScrollTimeline* scroll_timeline =
ScrollTimeline::Create(GetDocument(), options, ASSERT_NO_EXCEPTION); ScrollTimeline::Create(GetDocument(), options, ASSERT_NO_EXCEPTION);
...@@ -245,8 +259,8 @@ TEST_F(ScrollTimelineTest, PhasesAreCorrectWhenUsingOffsets) { ...@@ -245,8 +259,8 @@ TEST_F(ScrollTimelineTest, PhasesAreCorrectWhenUsingOffsets) {
DoubleOrScrollTimelineAutoKeyword::FromDouble(100); DoubleOrScrollTimelineAutoKeyword::FromDouble(100);
options->setTimeRange(time_range); options->setTimeRange(time_range);
options->setScrollSource(GetElementById("scroller")); options->setScrollSource(GetElementById("scroller"));
options->setStartScrollOffset("10px"); options->setStartScrollOffset(OffsetFromString("10px"));
options->setEndScrollOffset("90px"); options->setEndScrollOffset(OffsetFromString("90px"));
ScrollTimeline* scroll_timeline = ScrollTimeline* scroll_timeline =
ScrollTimeline::Create(GetDocument(), options, ASSERT_NO_EXCEPTION); ScrollTimeline::Create(GetDocument(), options, ASSERT_NO_EXCEPTION);
...@@ -340,8 +354,8 @@ TEST_F(ScrollTimelineTest, AttachOrDetachAnimationWithNullScrollSource) { ...@@ -340,8 +354,8 @@ TEST_F(ScrollTimelineTest, AttachOrDetachAnimationWithNullScrollSource) {
// scrollSource. The alternative approach would require us to remove the // scrollSource. The alternative approach would require us to remove the
// documentElement from the document. // documentElement from the document.
Element* scroll_source = nullptr; Element* scroll_source = nullptr;
CSSPrimitiveValue* start_scroll_offset = nullptr; ScrollTimelineOffset* start_scroll_offset = nullptr;
CSSPrimitiveValue* end_scroll_offset = nullptr; ScrollTimelineOffset* end_scroll_offset = nullptr;
Persistent<ScrollTimeline> scroll_timeline = Persistent<ScrollTimeline> scroll_timeline =
MakeGarbageCollected<ScrollTimeline>( MakeGarbageCollected<ScrollTimeline>(
&GetDocument(), scroll_source, ScrollTimeline::Block, &GetDocument(), scroll_source, ScrollTimeline::Block,
...@@ -437,9 +451,13 @@ TEST_F(ScrollTimelineTest, ScheduleFrameWhenScrollerLayoutChanges) { ...@@ -437,9 +451,13 @@ TEST_F(ScrollTimelineTest, ScheduleFrameWhenScrollerLayoutChanges) {
scrollable_area->SetScrollOffset(ScrollOffset(0, 20), scrollable_area->SetScrollOffset(ScrollOffset(0, 20),
mojom::blink::ScrollType::kProgrammatic); mojom::blink::ScrollType::kProgrammatic);
Element* scroller_element = GetElementById("scroller"); Element* scroller_element = GetElementById("scroller");
// Use empty offsets as 'auto'.
TestScrollTimeline* scroll_timeline = TestScrollTimeline* scroll_timeline =
MakeGarbageCollected<TestScrollTimeline>(&GetDocument(), scroller_element, MakeGarbageCollected<TestScrollTimeline>(
nullptr, nullptr); &GetDocument(), scroller_element,
MakeGarbageCollected<ScrollTimelineOffset>(),
MakeGarbageCollected<ScrollTimelineOffset>());
NonThrowableExceptionState exception_state; NonThrowableExceptionState exception_state;
Timing timing; Timing timing;
timing.iteration_duration = AnimationTimeDelta::FromSecondsD(30); timing.iteration_duration = AnimationTimeDelta::FromSecondsD(30);
...@@ -452,7 +470,9 @@ TEST_F(ScrollTimelineTest, ScheduleFrameWhenScrollerLayoutChanges) { ...@@ -452,7 +470,9 @@ TEST_F(ScrollTimelineTest, ScheduleFrameWhenScrollerLayoutChanges) {
scroll_timeline, exception_state); scroll_timeline, exception_state);
scroll_animation->play(); scroll_animation->play();
UpdateAllLifecyclePhasesForTest(); UpdateAllLifecyclePhasesForTest();
// Validate that frame is scheduled when scroller layout changes. // Validate that frame is scheduled when scroller layout changes that causes
// current time to change. Here we change the scroller max offset which
// affects current time because endScrollOffset is 'auto'.
Element* spacer_element = GetElementById("spacer"); Element* spacer_element = GetElementById("spacer");
spacer_element->setAttribute(html_names::kStyleAttr, "height:1000px;"); spacer_element->setAttribute(html_names::kStyleAttr, "height:1000px;");
scroll_timeline->ResetNextServiceScheduled(); scroll_timeline->ResetNextServiceScheduled();
......
...@@ -36,21 +36,8 @@ scoped_refptr<CompositorScrollTimeline> ToCompositorScrollTimeline( ...@@ -36,21 +36,8 @@ scoped_refptr<CompositorScrollTimeline> ToCompositorScrollTimeline(
CompositorScrollTimeline::ScrollDirection orientation = ConvertOrientation( CompositorScrollTimeline::ScrollDirection orientation = ConvertOrientation(
scroll_timeline->GetOrientation(), box ? box->Style() : nullptr); scroll_timeline->GetOrientation(), box ? box->Style() : nullptr);
base::Optional<double> start_scroll_offset; auto start_scroll_offset = scroll_timeline->GetResolvedStartScrollOffset();
base::Optional<double> end_scroll_offset; auto end_scroll_offset = scroll_timeline->GetResolvedEndScrollOffset();
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 CompositorScrollTimeline::Create( return CompositorScrollTimeline::Create(
element_id, orientation, start_scroll_offset, end_scroll_offset, element_id, orientation, start_scroll_offset, end_scroll_offset,
......
...@@ -13,6 +13,16 @@ ...@@ -13,6 +13,16 @@
namespace blink { namespace blink {
namespace {
StringOrScrollTimelineElementBasedOffset OffsetFromString(const String& value) {
StringOrScrollTimelineElementBasedOffset result;
result.SetString(value);
return result;
}
} // namespace
namespace scroll_timeline_util { namespace scroll_timeline_util {
using ScrollTimelineUtilTest = PageTestBase; using ScrollTimelineUtilTest = PageTestBase;
...@@ -47,8 +57,8 @@ TEST_F(ScrollTimelineUtilTest, ToCompositorScrollTimeline) { ...@@ -47,8 +57,8 @@ TEST_F(ScrollTimelineUtilTest, ToCompositorScrollTimeline) {
options->setTimeRange( options->setTimeRange(
DoubleOrScrollTimelineAutoKeyword::FromDouble(time_range)); DoubleOrScrollTimelineAutoKeyword::FromDouble(time_range));
options->setOrientation("block"); options->setOrientation("block");
options->setStartScrollOffset("50px"); options->setStartScrollOffset(OffsetFromString("50px"));
options->setEndScrollOffset("auto"); options->setEndScrollOffset(OffsetFromString("auto"));
ScrollTimeline* timeline = ScrollTimeline* timeline =
ScrollTimeline::Create(GetDocument(), options, ASSERT_NO_EXCEPTION); ScrollTimeline::Create(GetDocument(), options, ASSERT_NO_EXCEPTION);
...@@ -80,8 +90,10 @@ TEST_F(ScrollTimelineUtilTest, ToCompositorScrollTimelineNullScrollSource) { ...@@ -80,8 +90,10 @@ TEST_F(ScrollTimelineUtilTest, ToCompositorScrollTimelineNullScrollSource) {
// scrollSource. The alternative approach would require us to remove the // scrollSource. The alternative approach would require us to remove the
// documentElement from the document. // documentElement from the document.
Element* scroll_source = nullptr; Element* scroll_source = nullptr;
CSSPrimitiveValue* start_scroll_offset = nullptr; ScrollTimelineOffset* start_scroll_offset =
CSSPrimitiveValue* end_scroll_offset = nullptr; MakeGarbageCollected<ScrollTimelineOffset>();
ScrollTimelineOffset* end_scroll_offset =
MakeGarbageCollected<ScrollTimelineOffset>();
ScrollTimeline* timeline = MakeGarbageCollected<ScrollTimeline>( ScrollTimeline* timeline = MakeGarbageCollected<ScrollTimeline>(
&GetDocument(), scroll_source, ScrollTimeline::Block, start_scroll_offset, &GetDocument(), scroll_source, ScrollTimeline::Block, start_scroll_offset,
end_scroll_offset, 100); end_scroll_offset, 100);
......
...@@ -612,6 +612,7 @@ core_dictionary_idl_files = ...@@ -612,6 +612,7 @@ core_dictionary_idl_files =
"animation/keyframe_animation_options.idl", "animation/keyframe_animation_options.idl",
"animation/keyframe_effect_options.idl", "animation/keyframe_effect_options.idl",
"animation/optional_effect_timing.idl", "animation/optional_effect_timing.idl",
"animation/scroll_timeline_element_based_offset.idl",
"animation/scroll_timeline_options.idl", "animation/scroll_timeline_options.idl",
"css/css_style_sheet_init.idl", "css/css_style_sheet_init.idl",
"css/font_face_descriptors.idl", "css/font_face_descriptors.idl",
......
...@@ -1486,15 +1486,18 @@ TEST_P(PaintLayerScrollableAreaTest, SetSnapContainerDataNeedsUpdate) { ...@@ -1486,15 +1486,18 @@ TEST_P(PaintLayerScrollableAreaTest, SetSnapContainerDataNeedsUpdate) {
class ScrollTimelineForTest : public ScrollTimeline { class ScrollTimelineForTest : public ScrollTimeline {
public: public:
ScrollTimelineForTest( ScrollTimelineForTest(Document* document,
Document* document, Element* scroll_source,
Element* scroll_source, ScrollTimelineOffset* start_scroll_offset =
CSSPrimitiveValue* start_scroll_offset = MakeGarbageCollected<ScrollTimelineOffset>(
CSSNumericLiteralValue::Create(10.0, CSSNumericLiteralValue::Create(
CSSPrimitiveValue::UnitType::kPixels), 10.0,
CSSPrimitiveValue* end_scroll_offset = CSSPrimitiveValue::UnitType::kPixels)),
CSSNumericLiteralValue::Create(90.0, ScrollTimelineOffset* end_scroll_offset =
CSSPrimitiveValue::UnitType::kPixels)) MakeGarbageCollected<ScrollTimelineOffset>(
CSSNumericLiteralValue::Create(
90.0,
CSSPrimitiveValue::UnitType::kPixels)))
: ScrollTimeline(document, : ScrollTimeline(document,
scroll_source, scroll_source,
ScrollTimeline::Vertical, ScrollTimeline::Vertical,
......
...@@ -681,26 +681,11 @@ bool WorkletAnimation::UpdateOnCompositor() { ...@@ -681,26 +681,11 @@ bool WorkletAnimation::UpdateOnCompositor() {
} }
if (timeline_->IsScrollTimeline()) { if (timeline_->IsScrollTimeline()) {
Node* scroll_source = To<ScrollTimeline>(*timeline_).ResolvedScrollSource(); auto& timeline = To<ScrollTimeline>(*timeline_);
LayoutBox* box = scroll_source ? scroll_source->GetLayoutBox() : nullptr; Node* scroll_source = timeline.ResolvedScrollSource();
auto start_scroll_offset = timeline.GetResolvedStartScrollOffset();
base::Optional<double> start_scroll_offset; auto end_scroll_offset = timeline.GetResolvedEndScrollOffset();
base::Optional<double> end_scroll_offset;
if (box) {
double current_offset;
double max_offset;
To<ScrollTimeline>(*timeline_)
.GetCurrentAndMaxOffset(box, current_offset, max_offset);
double resolved_start_scroll_offset = 0;
double resolved_end_scroll_offset = max_offset;
To<ScrollTimeline>(*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( compositor_animation_->UpdateScrollTimeline(
scroll_timeline_util::GetCompositorScrollElementId(scroll_source), scroll_timeline_util::GetCompositorScrollElementId(scroll_source),
start_scroll_offset, end_scroll_offset); start_scroll_offset, end_scroll_offset);
......
This is a testharness.js-based test. This is a testharness.js-based test.
Found 52 tests; 50 PASS, 2 FAIL, 0 TIMEOUT, 0 NOTRUN. Found 60 tests; 58 PASS, 2 FAIL, 0 TIMEOUT, 0 NOTRUN.
PASS A ScrollTimeline can be created with a scrollSource PASS A ScrollTimeline can be created with a scrollSource
PASS A ScrollTimeline can be created with a non-scrolling scrollSource PASS A ScrollTimeline can be created with a non-scrolling scrollSource
PASS A ScrollTimeline created with a null scrollSource should use the document.scrollingElement PASS A ScrollTimeline created with a null scrollSource should use the document.scrollingElement
...@@ -42,6 +42,14 @@ PASS 'deg' is an invalid scroll offset unit ...@@ -42,6 +42,14 @@ PASS 'deg' is an invalid scroll offset unit
PASS 's' is an invalid scroll offset unit PASS 's' is an invalid scroll offset unit
PASS 'Hz' is an invalid scroll offset unit PASS 'Hz' is an invalid scroll offset unit
PASS 'dpi' is an invalid scroll offset unit PASS 'dpi' is an invalid scroll offset unit
PASS '{"target":{}}' is a valid scroll offset value
PASS '{"target":{},"threshold":0}' is a valid scroll offset value
PASS '{"target":{},"threshold":0.5}' is a valid scroll offset value
PASS '{"target":{},"threshold":1}' is a valid scroll offset value
PASS '{}' is an invalid scroll offset value
PASS '{"target":{},"threshold":"test"}' is an invalid scroll offset value
PASS '{"target":{},"threshold":2}' is an invalid scroll offset value
PASS '{"target":{},"threshold":-0.2}' is an invalid scroll offset value
FAIL A ScrollTimeline created with the default timeRange should default to 'auto' Failed to construct 'ScrollTimeline': 'auto' value for timeRange not yet supported FAIL A ScrollTimeline created with the default timeRange should default to 'auto' Failed to construct 'ScrollTimeline': 'auto' value for timeRange not yet supported
FAIL 'auto' is a valid timeRange value Failed to construct 'ScrollTimeline': 'auto' value for timeRange not yet supported FAIL 'auto' is a valid timeRange value Failed to construct 'ScrollTimeline': 'auto' value for timeRange not yet supported
PASS '0' is a valid timeRange value PASS '0' is a valid timeRange value
......
...@@ -197,6 +197,52 @@ for (const suffix of gInvalidScrollOffsetSuffixes) { ...@@ -197,6 +197,52 @@ for (const suffix of gInvalidScrollOffsetSuffixes) {
}, '\'' + suffix + '\' is an invalid scroll offset unit'); }, '\'' + suffix + '\' is an invalid scroll offset unit');
} }
const offset_target = document.createElement('div');
const gValidElementBasedScrollOffsetValues = [
{target: offset_target},
{target: offset_target, threshold: 0},
{target: offset_target, threshold: 0.5},
{target: offset_target, threshold: 1},
];
for (let offset of gValidElementBasedScrollOffsetValues) {
test(function() {
const scrollTimeline = new ScrollTimeline(
{timeRange: 100, startScrollOffset: offset, endScrollOffset: offset});
// Special case unspecified threshold since it gets initialized to 0.
if (!offset.hasOwnProperty('threshold'))
offset.threshold = 0;
assert_equals(scrollTimeline.startScrollOffset.target, offset.target);
assert_equals(scrollTimeline.startScrollOffset.threshold, offset.threshold);
assert_equals(scrollTimeline.endScrollOffset.target, offset.target);
assert_equals(scrollTimeline.endScrollOffset.threshold, offset.threshold);
}, '\'' + JSON.stringify(offset) + '\' is a valid scroll offset value');
}
const gInvalidElementBasedScrollOffsetValues = [
{}, // empty
{target: offset_target, threshold: "test"},
{target: offset_target, threshold: 2},
{target: offset_target, threshold: -0.2},
];
for (let offset of gInvalidElementBasedScrollOffsetValues) {
test(function() {
const constructorFunc = function() {
new ScrollTimeline(
{timeRange: 100, startScrollOffset: offset, endScrollOffset: offset})
};
assert_throws_js(TypeError, constructorFunc);
}, '\'' + JSON.stringify(offset) + '\' is an invalid scroll offset value');
}
// timeRange // timeRange
test(function() { test(function() {
......
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