Commit 29c57aa5 authored by Anders Hartvoll Ruud's avatar Anders Hartvoll Ruud Committed by Commit Bot

[scroll-animations] Use CSSStyleValues to specify offsets

The spec changed a while ago to use CSSNumericValue/CSSKeywordish for
specifying container-based offsets [1]. This CL makes that switch.

[1] https://github.com/w3c/csswg-drafts/pull/5300

Bug: 1109769

Change-Id: I1d14d68008098e39035aecba940c0e02645f9d99
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2390740
Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org>
Reviewed-by: default avatarYuki Shiino <yukishiino@chromium.org>
Reviewed-by: default avatarKevin Ellis <kevers@chromium.org>
Cr-Commit-Position: refs/heads/master@{#806346}
parent ae41d1d5
......@@ -22,6 +22,10 @@ bindings_core_generated_union_type_files = [
"$bindings_core_v8_output_dir/byte_string_sequence_sequence_or_byte_string_byte_string_record.h",
"$bindings_core_v8_output_dir/composite_operation_or_auto_or_composite_operation_or_auto_sequence.cc",
"$bindings_core_v8_output_dir/composite_operation_or_auto_or_composite_operation_or_auto_sequence.h",
"$bindings_core_v8_output_dir/css_numeric_value_or_string_or_css_keyword_value_or_scroll_timeline_element_based_offset.cc",
"$bindings_core_v8_output_dir/css_numeric_value_or_string_or_css_keyword_value_or_scroll_timeline_element_based_offset.h",
"$bindings_core_v8_output_dir/css_numeric_value_or_string_or_css_keyword_value.h",
"$bindings_core_v8_output_dir/css_numeric_value_or_string_or_css_keyword_value.cc",
"$bindings_core_v8_output_dir/css_style_value_or_string.cc",
"$bindings_core_v8_output_dir/css_style_value_or_string.h",
"$bindings_core_v8_output_dir/document_or_xml_http_request_body_init.cc",
......@@ -82,14 +86,14 @@ bindings_core_generated_union_type_files = [
"$bindings_core_v8_output_dir/string_or_array_buffer_or_array_buffer_view.h",
"$bindings_core_v8_output_dir/string_or_css_variable_reference_value.cc",
"$bindings_core_v8_output_dir/string_or_css_variable_reference_value.h",
"$bindings_core_v8_output_dir/string_or_css_keyword_value.cc",
"$bindings_core_v8_output_dir/string_or_css_keyword_value.h",
"$bindings_core_v8_output_dir/string_or_double.cc",
"$bindings_core_v8_output_dir/string_or_double.h",
"$bindings_core_v8_output_dir/string_or_element_creation_options.cc",
"$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.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.h",
"$bindings_core_v8_output_dir/string_or_trusted_html_or_trusted_script_or_trusted_script_url.cc",
......
......@@ -8,6 +8,9 @@
#include "third_party/blink/renderer/core/animation/css_interpolation_environment.h"
#include "third_party/blink/renderer/core/animation/css_interpolation_types_map.h"
#include "third_party/blink/renderer/core/animation/invalidatable_interpolation.h"
#include "third_party/blink/renderer/core/css/css_test_helpers.h"
#include "third_party/blink/renderer/core/css/cssom/css_keyword_value.h"
#include "third_party/blink/renderer/core/css/cssom/css_numeric_value.h"
#include "third_party/blink/renderer/core/css/resolver/style_cascade.h"
#include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
#include "third_party/blink/renderer/core/dom/document.h"
......@@ -81,5 +84,22 @@ void EnsureInterpolatedValueCached(ActiveInterpolations* interpolations,
cascade.Apply();
}
ScrollTimelineOffsetValue OffsetFromString(Document& document,
const String& string) {
ScrollTimelineOffsetValue result;
const CSSValue* value = css_test_helpers::ParseValue(
document, "<length-percentage> | auto", string);
if (const auto* primitive = DynamicTo<CSSPrimitiveValue>(value))
result.SetCSSNumericValue(CSSNumericValue::FromCSSValue(*primitive));
else if (DynamicTo<CSSIdentifierValue>(value))
result.SetCSSKeywordValue(CSSKeywordValue::Create("auto"));
else
result.SetString(string);
return result;
}
} // namespace animation_test_helpers
} // namespace blink
......@@ -5,7 +5,9 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_ANIMATION_TEST_HELPERS_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_ANIMATION_TEST_HELPERS_H_
#include "third_party/blink/renderer/bindings/core/v8/css_numeric_value_or_string_or_css_keyword_value_or_scroll_timeline_element_based_offset.h"
#include "third_party/blink/renderer/core/animation/interpolation.h"
#include "third_party/blink/renderer/core/animation/scroll_timeline_offset.h"
#include "third_party/blink/renderer/platform/wtf/text/string_view.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
#include "v8/include/v8.h"
......@@ -42,6 +44,14 @@ KeyframeEffect* CreateSimpleKeyframeEffectForTest(Element*,
// InvalidatableInterpolation.
void EnsureInterpolatedValueCached(ActiveInterpolations*, Document&, Element*);
// Returns one of the following:
//
// - A CSSNumericValue, if the incoming string can be parsed as a
// <length-percentage>.
// - A CSSKeywordValue. if the incoming string can be parsed as 'auto'.
// - Otherwise, the incoming string.
ScrollTimelineOffsetValue OffsetFromString(Document&, const String&);
} // namespace animation_test_helpers
} // namespace blink
......
......@@ -99,10 +99,6 @@ ScrollTimeline* ScrollTimeline::Create(Document& document,
? options->scrollSource()
: document.scrollingElement();
// TODO(xiaochengh): Try reusing an existing context in document.
const CSSParserContext* context =
MakeGarbageCollected<CSSParserContext>(document);
ScrollDirection orientation;
if (!StringToScrollDirection(options->orientation(), orientation)) {
exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
......@@ -111,14 +107,14 @@ ScrollTimeline* ScrollTimeline::Create(Document& document,
}
ScrollTimelineOffset* start_scroll_offset =
ScrollTimelineOffset::Create(options->startScrollOffset(), *context);
ScrollTimelineOffset::Create(options->startScrollOffset());
if (!start_scroll_offset) {
exception_state.ThrowTypeError("Invalid start offset.");
return nullptr;
}
ScrollTimelineOffset* end_scroll_offset =
ScrollTimelineOffset::Create(options->endScrollOffset(), *context);
ScrollTimelineOffset::Create(options->endScrollOffset());
if (!end_scroll_offset) {
exception_state.ThrowTypeError("Invalid end offset");
return nullptr;
......@@ -148,7 +144,7 @@ ScrollTimeline* ScrollTimeline::Create(Document& document,
} else {
for (auto& offset : options->scrollOffsets()) {
ScrollTimelineOffset* scroll_offset =
ScrollTimelineOffset::Create(offset, *context);
ScrollTimelineOffset::Create(offset);
if (!scroll_offset) {
exception_state.ThrowTypeError("Invalid scroll offset");
return nullptr;
......@@ -408,36 +404,33 @@ String ScrollTimeline::orientation() {
// TODO(crbug.com/1094014): scrollOffsets will replace start and end
// offsets once spec decision on multiple scroll offsets is finalized.
// https://github.com/w3c/csswg-drafts/issues/4912
void ScrollTimeline::startScrollOffset(
StringOrScrollTimelineElementBasedOffset& out) const {
void ScrollTimeline::startScrollOffset(ScrollTimelineOffsetValue& out) const {
if (StartScrollOffset()) {
out = StartScrollOffset()->ToStringOrScrollTimelineElementBasedOffset();
out = StartScrollOffset()->ToScrollTimelineOffsetValue();
} else {
ScrollTimelineOffset scrollOffset;
out = scrollOffset.ToStringOrScrollTimelineElementBasedOffset();
out = scrollOffset.ToScrollTimelineOffsetValue();
}
}
void ScrollTimeline::endScrollOffset(
StringOrScrollTimelineElementBasedOffset& out) const {
void ScrollTimeline::endScrollOffset(ScrollTimelineOffsetValue& out) const {
if (EndScrollOffset()) {
out = EndScrollOffset()->ToStringOrScrollTimelineElementBasedOffset();
out = EndScrollOffset()->ToScrollTimelineOffsetValue();
} else {
ScrollTimelineOffset scrollOffset;
out = scrollOffset.ToStringOrScrollTimelineElementBasedOffset();
out = scrollOffset.ToScrollTimelineOffsetValue();
}
}
const HeapVector<StringOrScrollTimelineElementBasedOffset>
ScrollTimeline::scrollOffsets() const {
HeapVector<StringOrScrollTimelineElementBasedOffset> scroll_offsets;
const HeapVector<ScrollTimelineOffsetValue> ScrollTimeline::scrollOffsets()
const {
HeapVector<ScrollTimelineOffsetValue> scroll_offsets;
if (!scroll_offsets_)
return scroll_offsets;
for (auto& offset : *scroll_offsets_) {
scroll_offsets.push_back(
offset->ToStringOrScrollTimelineElementBasedOffset());
scroll_offsets.push_back(offset->ToScrollTimelineOffsetValue());
// 'auto' can only be the end offset.
DCHECK(!offset->IsDefaultValue() || scroll_offsets.size() == 2);
}
......
......@@ -65,11 +65,9 @@ class CORE_EXPORT ScrollTimeline : public AnimationTimeline {
// TODO(crbug.com/1094014): scrollOffsets will replace start and end
// offsets once spec decision on multiple scroll offsets is finalized.
// https://github.com/w3c/csswg-drafts/issues/4912
void startScrollOffset(
StringOrScrollTimelineElementBasedOffset& result) const;
void endScrollOffset(StringOrScrollTimelineElementBasedOffset& result) const;
const HeapVector<StringOrScrollTimelineElementBasedOffset> scrollOffsets()
const;
void startScrollOffset(ScrollTimelineOffsetValue& result) const;
void endScrollOffset(ScrollTimelineOffsetValue& result) const;
const HeapVector<ScrollTimelineOffsetValue> scrollOffsets() const;
void timeRange(DoubleOrScrollTimelineAutoKeyword&);
......
......@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
typedef (CSSNumericValue or CSSKeywordish) ScrollTimelineContainerBasedOffset;
typedef (ScrollTimelineContainerBasedOffset or ScrollTimelineElementBasedOffset) ScrollTimelineOffset;
// https://wicg.github.io/scroll-animations/#scrolltimeline-interface
[
RuntimeEnabled=ScrollTimeline,
......@@ -10,8 +13,8 @@
[CallWith=Document, RaisesException, MeasureAs=ScrollTimelineConstructor] constructor(optional ScrollTimelineOptions options = {});
readonly attribute Element? scrollSource;
readonly attribute ScrollDirection orientation;
readonly attribute (DOMString or ScrollTimelineElementBasedOffset) startScrollOffset;
readonly attribute (DOMString or ScrollTimelineElementBasedOffset) endScrollOffset;
readonly attribute FrozenArray<(DOMString or ScrollTimelineElementBasedOffset)> scrollOffsets;
readonly attribute ScrollTimelineOffset startScrollOffset;
readonly attribute ScrollTimelineOffset endScrollOffset;
readonly attribute FrozenArray<ScrollTimelineOffset> scrollOffsets;
readonly attribute (double or ScrollTimelineAutoKeyword) timeRange;
};
......@@ -5,11 +5,11 @@
#include "third_party/blink/renderer/core/animation/scroll_timeline_offset.h"
#include "base/optional.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/css_numeric_value_or_string_or_css_keyword_value_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/css/cssom/css_keyword_value.h"
#include "third_party/blink/renderer/core/css/cssom/css_numeric_value.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"
......@@ -21,21 +21,6 @@ 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 ValidateElementBasedOffset(ScrollTimelineElementBasedOffset* offset) {
if (!offset->hasTarget())
return false;
......@@ -77,29 +62,46 @@ bool ElementBasedOffsetsEqual(ScrollTimelineElementBasedOffset* o1,
(o1->threshold() == o2->threshold());
}
CSSKeywordValue* GetCSSKeywordValue(const ScrollTimelineOffsetValue& offset) {
if (offset.IsCSSKeywordValue())
return offset.GetAsCSSKeywordValue();
// CSSKeywordish:
if (offset.IsString() && !offset.GetAsString().IsEmpty())
return CSSKeywordValue::Create(offset.GetAsString());
return nullptr;
}
} // 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)) {
const ScrollTimelineOffsetValue& input_offset) {
if (input_offset.IsCSSNumericValue()) {
auto* numeric = input_offset.GetAsCSSNumericValue();
const auto& offset = To<CSSPrimitiveValue>(*numeric->ToCSSValue());
bool matches_length_percentage = offset.IsLength() ||
offset.IsPercentage() ||
offset.IsCalculatedPercentageWithLength();
if (!matches_length_percentage)
return nullptr;
}
return MakeGarbageCollected<ScrollTimelineOffset>(&offset);
}
return MakeGarbageCollected<ScrollTimelineOffset>(offset);
} else if (input_offset.IsScrollTimelineElementBasedOffset()) {
if (input_offset.IsScrollTimelineElementBasedOffset()) {
auto* offset = input_offset.GetAsScrollTimelineElementBasedOffset();
if (!ValidateElementBasedOffset(offset))
return nullptr;
return MakeGarbageCollected<ScrollTimelineOffset>(offset);
} else {
// The default case is "auto" which we initialized with null
}
if (auto* keyword = GetCSSKeywordValue(input_offset)) {
if (keyword->KeywordValueID() != CSSValueID::kAuto)
return nullptr;
return MakeGarbageCollected<ScrollTimelineOffset>();
}
return nullptr;
}
base::Optional<double> ScrollTimelineOffset::ResolveOffset(
......@@ -211,16 +213,17 @@ base::Optional<double> ScrollTimelineOffset::ResolveOffset(
}
}
StringOrScrollTimelineElementBasedOffset
ScrollTimelineOffset::ToStringOrScrollTimelineElementBasedOffset() const {
StringOrScrollTimelineElementBasedOffset result;
ScrollTimelineOffsetValue ScrollTimelineOffset::ToScrollTimelineOffsetValue()
const {
ScrollTimelineOffsetValue result;
if (length_based_offset_) {
result.SetString(length_based_offset_->CssText());
result.SetCSSNumericValue(
CSSNumericValue::FromCSSValue(*length_based_offset_.Get()));
} else if (element_based_offset_) {
result.SetScrollTimelineElementBasedOffset(element_based_offset_);
} else {
// This is the default value (i.e., 'auto' value)
result.SetString("auto");
result.SetCSSKeywordValue(CSSKeywordValue::Create("auto"));
}
return result;
......
......@@ -6,21 +6,24 @@
#define THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_SCROLL_TIMELINE_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_style_sheet.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;
class
CSSNumericValueOrStringOrCSSKeywordValueOrScrollTimelineElementBasedOffset;
using ScrollTimelineOffsetValue =
CSSNumericValueOrStringOrCSSKeywordValueOrScrollTimelineElementBasedOffset;
// 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);
static ScrollTimelineOffset* Create(const ScrollTimelineOffsetValue& offset);
// Create a default offset representing 'auto'.
ScrollTimelineOffset() = default;
......@@ -49,8 +52,7 @@ class CORE_EXPORT ScrollTimelineOffset final
double max_offset,
double default_offset);
StringOrScrollTimelineElementBasedOffset
ToStringOrScrollTimelineElementBasedOffset() const;
ScrollTimelineOffsetValue ToScrollTimelineOffsetValue() const;
bool IsDefaultValue() const {
return !length_based_offset_ && !element_based_offset_;
}
......
......@@ -5,7 +5,8 @@
#include "third_party/blink/renderer/core/animation/scroll_timeline_offset.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/bindings/core/v8/string_or_scroll_timeline_element_based_offset.h"
#include "third_party/blink/renderer/core/animation/animation_test_helpers.h"
#include "third_party/blink/renderer/core/animation/scroll_timeline_offset.h"
#include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/html/html_element.h"
......@@ -16,9 +17,8 @@ namespace blink {
class ScrollTimelineOffsetTest : public PageTestBase {
public:
ScrollTimelineOffset* ScrollBasedOffsetFrom(String string) {
StringOrScrollTimelineElementBasedOffset param;
param.SetString(string);
return ScrollTimelineOffset::Create(param, *CreateCSSParserContext());
return ScrollTimelineOffset::Create(
animation_test_helpers::OffsetFromString(GetDocument(), string));
}
ScrollTimelineOffset* ElementBasedOffsetFrom(Element* target,
......@@ -27,16 +27,12 @@ class ScrollTimelineOffsetTest : public PageTestBase {
auto* inner = CreateElementBasedOffset(target, edge, threshold);
if (!inner)
return nullptr;
StringOrScrollTimelineElementBasedOffset param;
ScrollTimelineOffsetValue param;
param.SetScrollTimelineElementBasedOffset(inner);
return ScrollTimelineOffset::Create(param, *CreateCSSParserContext());
return ScrollTimelineOffset::Create(param);
}
private:
const CSSParserContext* CreateCSSParserContext() {
return MakeGarbageCollected<CSSParserContext>(GetDocument());
}
ScrollTimelineElementBasedOffset* CreateElementBasedOffset(Element* target,
String edge,
double threshold) {
......
......@@ -16,8 +16,8 @@ enum ScrollTimelineAutoKeyword { "auto" };
dictionary ScrollTimelineOptions {
Element? scrollSource;
ScrollDirection orientation = "block";
(DOMString or ScrollTimelineElementBasedOffset) startScrollOffset = "auto";
(DOMString or ScrollTimelineElementBasedOffset) endScrollOffset = "auto";
sequence<(DOMString or ScrollTimelineElementBasedOffset)> scrollOffsets = [];
ScrollTimelineOffset startScrollOffset = "auto";
ScrollTimelineOffset endScrollOffset = "auto";
sequence<ScrollTimelineOffset> scrollOffsets = [];
(double or ScrollTimelineAutoKeyword) timeRange = "auto";
};
......@@ -6,6 +6,7 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_scroll_timeline_options.h"
#include "third_party/blink/renderer/core/animation/animation_test_helpers.h"
#include "third_party/blink/renderer/core/animation/document_animations.h"
#include "third_party/blink/renderer/core/animation/keyframe_effect.h"
#include "third_party/blink/renderer/core/animation/keyframe_effect_model.h"
......@@ -29,12 +30,6 @@ static constexpr double time_error_ms = 0.001 + 1e-13;
#define EXPECT_TIME_NEAR(expected, value) \
EXPECT_NEAR(expected, value, time_error_ms)
StringOrScrollTimelineElementBasedOffset OffsetFromString(const String& value) {
StringOrScrollTimelineElementBasedOffset result;
result.SetString(value);
return result;
}
HeapVector<Member<ScrollTimelineOffset>>* CreateScrollOffsets(
ScrollTimelineOffset* start_scroll_offset =
MakeGarbageCollected<ScrollTimelineOffset>(
......@@ -77,6 +72,10 @@ class ScrollTimelineTest : public RenderingTest {
}
return count;
}
ScrollTimelineOffsetValue OffsetFromString(const String& value) {
return animation_test_helpers::OffsetFromString(GetDocument(), value);
}
};
class TestScrollTimeline : public ScrollTimeline {
......@@ -807,7 +806,7 @@ TEST_F(ScrollTimelineTest, MultipleScrollOffsetsCurrentTimeCalculations) {
options->setTimeRange(
DoubleOrScrollTimelineAutoKeyword::FromDouble(time_range));
options->setScrollSource(GetElementById("scroller"));
HeapVector<StringOrScrollTimelineElementBasedOffset> scroll_offsets;
HeapVector<ScrollTimelineOffsetValue> scroll_offsets;
scroll_offsets.push_back(OffsetFromString("10px"));
scroll_offsets.push_back(OffsetFromString("20px"));
scroll_offsets.push_back(OffsetFromString("40px"));
......
......@@ -6,6 +6,7 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_scroll_timeline_options.h"
#include "third_party/blink/renderer/core/animation/animation_test_helpers.h"
#include "third_party/blink/renderer/core/animation/document_timeline.h"
#include "third_party/blink/renderer/core/html/html_div_element.h"
#include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
......@@ -15,12 +16,6 @@ namespace blink {
namespace {
StringOrScrollTimelineElementBasedOffset OffsetFromString(const String& value) {
StringOrScrollTimelineElementBasedOffset result;
result.SetString(value);
return result;
}
HeapVector<Member<ScrollTimelineOffset>>* CreateScrollOffsets(
ScrollTimelineOffset* start_scroll_offset,
ScrollTimelineOffset* end_scroll_offset) {
......@@ -42,6 +37,8 @@ using ScrollTimelineUtilTest = PageTestBase;
// are tested in the GetOrientation* tests, and complex start/end scroll offset
// resolutions are tested in blink::ScrollTimelineTest.
TEST_F(ScrollTimelineUtilTest, ToCompositorScrollTimeline) {
using animation_test_helpers::OffsetFromString;
SetBodyInnerHTML(R"HTML(
<style>
#scroller {
......@@ -67,8 +64,8 @@ TEST_F(ScrollTimelineUtilTest, ToCompositorScrollTimeline) {
options->setTimeRange(
DoubleOrScrollTimelineAutoKeyword::FromDouble(time_range));
options->setOrientation("block");
options->setStartScrollOffset(OffsetFromString("50px"));
options->setEndScrollOffset(OffsetFromString("auto"));
options->setStartScrollOffset(OffsetFromString(GetDocument(), "50px"));
options->setEndScrollOffset(OffsetFromString(GetDocument(), "auto"));
ScrollTimeline* timeline =
ScrollTimeline::Create(GetDocument(), options, ASSERT_NO_EXCEPTION);
......
......@@ -10,6 +10,7 @@
#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
#include "third_party/blink/renderer/core/css/css_rule_list.h"
#include "third_party/blink/renderer/core/css/css_style_sheet.h"
#include "third_party/blink/renderer/core/css/css_syntax_definition.h"
#include "third_party/blink/renderer/core/css/css_syntax_string_parser.h"
#include "third_party/blink/renderer/core/css/css_variable_data.h"
#include "third_party/blink/renderer/core/css/parser/css_parser.h"
......@@ -146,5 +147,17 @@ StyleRuleBase* ParseRule(Document& document, String text) {
return CSSParser::ParseRule(context, sheet->Contents(), text);
}
const CSSValue* ParseValue(Document& document, String syntax, String value) {
auto syntax_definition = CSSSyntaxStringParser(syntax).Parse();
if (!syntax_definition.has_value())
return nullptr;
const auto* context = MakeGarbageCollected<CSSParserContext>(document);
CSSTokenizer tokenizer(value);
auto tokens = tokenizer.TokenizeToEOF();
CSSParserTokenRange range(tokens);
return syntax_definition->Parse(range, *context,
/* is_animation_tainted */ false);
}
} // namespace css_test_helpers
} // namespace blink
......@@ -68,6 +68,10 @@ const CSSPropertyValueSet* ParseDeclarationBlock(
CSSParserMode mode = kHTMLStandardMode);
StyleRuleBase* ParseRule(Document& document, String text);
// Parse a value according to syntax defined by:
// https://drafts.css-houdini.org/css-properties-values-api-1/#syntax-strings
const CSSValue* ParseValue(Document&, String syntax, String value);
} // namespace css_test_helpers
} // namespace blink
......
......@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
typedef (DOMString or CSSKeywordValue) CSSKeywordish;
// CSSKeywordValue represents CSS Values that are specified as keywords, for
// example "initial".
// https://drafts.css-houdini.org/css-typed-om/#keywordvalue-objects
......
This is a testharness.js-based test.
Found 61 tests; 59 PASS, 2 FAIL, 0 TIMEOUT, 0 NOTRUN.
Found 67 tests; 65 PASS, 2 FAIL, 0 TIMEOUT, 0 NOTRUN.
PASS A ScrollTimeline can be created with a scrollSource
PASS A ScrollTimeline can be created with a non-scrolling scrollSource
PASS A ScrollTimeline created with a null scrollSource should have no scrollSource
......@@ -10,11 +10,12 @@ PASS 'inline' is a valid orientation value
PASS 'horizontal' is a valid orientation value
PASS 'vertical' is a valid orientation value
PASS Creating a ScrollTimeline with an invalid orientation value should throw
PASS A ScrollTimeline created with the default startScrollOffset should default to 'auto'
PASS A ScrollTimeline created with the default endScrollOffset should default to 'auto'
PASS A ScrollTimeline created with the default startScrollOffset should default to CSSKeywordValue(auto)
PASS A ScrollTimeline created with the default endScrollOffset should default to CSSKeywordValue(auto)
PASS CSSKeywordValue(auto) is a valid scroll offset value
PASS CSSUnitValue(0px) is a valid scroll offset value
PASS CSSMathSum(calc(100% + -80px)) is a valid scroll offset value
PASS 'auto' is a valid scroll offset value
PASS '0' is a valid scroll offset value
PASS 'calc(100% - 80px)' is a valid scroll offset value
PASS 'em' is a valid scroll offset unit
PASS 'ex' is a valid scroll offset unit
PASS 'ch' is a valid scroll offset unit
......@@ -38,7 +39,12 @@ PASS '#ff0000' is an invalid scroll offset value
PASS 'rgb(0, 128, 0)' is an invalid scroll offset value
PASS 'url("http://www.example.com/pinkish.gif")' is an invalid scroll offset value
PASS 'this_is_garbage' is an invalid scroll offset value
PASS CSSUnitValue(0) is an invalid scroll offset value
PASS '100px 5%' is an invalid scroll offset value
PASS '0' is an invalid scroll offset value
PASS '10px' is an invalid scroll offset value
PASS '10%' is an invalid scroll offset value
PASS 'calc(100% - 80px)' is an invalid scroll offset value
PASS 'deg' is an invalid scroll offset unit
PASS 's' is an invalid scroll offset unit
PASS 'Hz' is an invalid scroll offset unit
......
......@@ -25,6 +25,16 @@
<script>
'use strict';
function formatOffset(v) {
if (typeof(v) == 'object')
return `${v.constructor.name}(${v.toString()})`;
return `'${v.toString()}'`;
}
function assert_offsets_equal(a, b) {
assert_equals(formatOffset(a), formatOffset(b));
}
// TODO(smcgruer): In many of the tests below, timeRange is specified when it
// should not need to be. This is an artifact of the initial Chrome
// implementation which doesn't support timeRange: 'auto'. These should be
......@@ -96,17 +106,18 @@ test(t => {
// startScrollOffset and endScrollOffset
test(t => {
assert_equals(new ScrollTimeline({timeRange: 100}).startScrollOffset, 'auto');
}, 'A ScrollTimeline created with the default startScrollOffset should default to \'auto\'');
assert_offsets_equal(new ScrollTimeline({timeRange: 100}).startScrollOffset, new CSSKeywordValue('auto'));
}, 'A ScrollTimeline created with the default startScrollOffset should default to CSSKeywordValue(auto)');
test(t => {
assert_equals(new ScrollTimeline({timeRange: 100}).endScrollOffset, 'auto');
}, 'A ScrollTimeline created with the default endScrollOffset should default to \'auto\'');
assert_offsets_equal(new ScrollTimeline({timeRange: 100}).endScrollOffset, new CSSKeywordValue('auto'));
}, 'A ScrollTimeline created with the default endScrollOffset should default to CSSKeywordValue(auto)');
const gValidScrollOffsetValues = [
'auto',
0,
'calc(100% - 80px)',
new CSSKeywordValue('auto'),
CSS.px(0),
CSS.percent(100).sub(CSS.px(80)),
"auto",
];
const gValidScrollOffsetSuffixes = [
......@@ -136,23 +147,24 @@ for (let offset of gValidScrollOffsetValues) {
const scrollTimeline = new ScrollTimeline(
{timeRange: 100, startScrollOffset: offset, endScrollOffset: offset});
// Special case for 0; this is a valid value, but like computed style will
// be output as '0px' when queried.
if (offset === 0) offset = '0px';
// Special case for 'auto'. This is valid input because of CSSKeywordish,
// but when read back we expect a real CSSKeywordValue.
if (offset === 'auto')
offset = new CSSKeywordValue('auto');
assert_equals(scrollTimeline.startScrollOffset, offset);
assert_equals(scrollTimeline.endScrollOffset, offset);
}, '\'' + offset + '\' is a valid scroll offset value');
assert_offsets_equal(scrollTimeline.startScrollOffset, offset);
assert_offsets_equal(scrollTimeline.endScrollOffset, offset);
}, formatOffset(offset) + ' is a valid scroll offset value');
}
for (const suffix of gValidScrollOffsetSuffixes) {
test(function() {
const offset = '75' + suffix;
const offset = new CSSUnitValue(75, suffix);
const scrollTimeline = new ScrollTimeline(
{timeRange: 100, startScrollOffset: offset, endScrollOffset: offset});
assert_equals(scrollTimeline.startScrollOffset, offset);
assert_equals(scrollTimeline.endScrollOffset, offset);
assert_offsets_equal(scrollTimeline.startScrollOffset, offset);
assert_offsets_equal(scrollTimeline.endScrollOffset, offset);
}, '\'' + suffix + '\' is a valid scroll offset unit');
}
......@@ -166,8 +178,14 @@ const gInvalidScrollOffsetValues = [
'rgb(0, 128, 0)',
'url("http://www.example.com/pinkish.gif")',
'this_is_garbage',
CSS.number(0),
// Multiple valid values.
'100px 5%',
// Values that would be valid if represented with CSS Typed OM:
0,
'10px',
'10%',
'calc(100% - 80px)',
];
const gInvalidScrollOffsetSuffixes = [
......@@ -184,7 +202,7 @@ for (const offset of gInvalidScrollOffsetValues) {
{timeRange: 100, startScrollOffset: offset, endScrollOffset: offset})
};
assert_throws_js(TypeError, constructorFunc);
}, '\'' + offset + '\' is an invalid scroll offset value');
}, formatOffset(offset) + ' is an invalid scroll offset value');
}
for (const suffix of gInvalidScrollOffsetSuffixes) {
......
......@@ -25,6 +25,16 @@
<script>
'use strict';
function formatOffset(v) {
if (typeof(v) == 'object')
return `${v.constructor.name}(${v.toString()})`;
return `'${v.toString()}'`;
}
function assert_offsets_equal(a, b) {
assert_equals(formatOffset(a), formatOffset(b));
}
test(t => {
assert_array_equals(new ScrollTimeline({timeRange: 100}).scrollOffsets, []);
}, 'A ScrollTimeline created with the default scrollOffsets should default to []');
......@@ -34,7 +44,9 @@ test(t => {
}, 'A ScrollTimeline created with empty scrollOffsets should resolve to []');
test(t => {
assert_array_equals(new ScrollTimeline({timeRange: 100, scrollOffsets: ['20%', 'auto']}).scrollOffsets, ['20%', 'auto']);
let offsets = new ScrollTimeline({timeRange: 100, scrollOffsets: [CSS.percent(20), 'auto']}).scrollOffsets;
assert_offsets_equal(offsets[0], CSS.percent(20));
assert_offsets_equal(offsets[1], new CSSKeywordValue('auto'));
}, 'A ScrollTimeline created with last \'auto\' offset in scrollOffsets should be allowed.');
test(t => {
......@@ -52,8 +64,8 @@ test(t => {
}, 'Creating a ScrollTimeline with an scrollOffsets value of [\'auto\'] should throw');
const gValidScrollOffsetValues = [
0,
'calc(100% - 80px)',
CSS.px(0),
CSS.percent(100).sub(CSS.px(80)),
];
const gValidScrollOffsetSuffixes = [
......@@ -83,21 +95,24 @@ for (let offset of gValidScrollOffsetValues) {
const scrollTimeline = new ScrollTimeline(
{timeRange: 100, scrollOffsets: [offset, offset]});
// Special case for 0; this is a valid value, but like computed style will
// be output as '0px' when queried.
if (offset === 0) offset = '0px';
// Special case for 'auto'. This is valid input because of CSSKeywordish,
// but when read back we expect a real CSSKeywordValue.
if (offset === 'auto')
offset = new CSSKeywordValue('auto');
assert_array_equals(scrollTimeline.scrollOffsets, [offset, offset]);
assert_offsets_equal(scrollTimeline.scrollOffsets[0], offset);
assert_offsets_equal(scrollTimeline.scrollOffsets[1], offset);
}, '\'' + offset + '\' is a valid scroll offset value');
}
for (const suffix of gValidScrollOffsetSuffixes) {
test(function() {
const offset = '75' + suffix;
const offset = new CSSUnitValue(75, suffix);
const scrollTimeline = new ScrollTimeline(
{timeRange: 100, scrollOffsets: [offset, offset]});
assert_array_equals(scrollTimeline.scrollOffsets, [offset, offset]);
assert_offsets_equal(scrollTimeline.scrollOffsets[0], offset);
assert_offsets_equal(scrollTimeline.scrollOffsets[1], offset);
}, '\'' + suffix + '\' is a valid scroll offset unit');
}
......@@ -111,8 +126,14 @@ const gInvalidScrollOffsetValues = [
'rgb(0, 128, 0)',
'url("http://www.example.com/pinkish.gif")',
'this_is_garbage',
CSS.number(0),
// Multiple valid values.
'100px 5%',
// Values that would be valid if represented with CSS Typed OM:
0,
'10px',
'10%',
'calc(100% - 80px)',
];
const gInvalidScrollOffsetSuffixes = [
......@@ -129,7 +150,7 @@ for (const offset of gInvalidScrollOffsetValues) {
{timeRange: 100, scrollOffsets: ['0px', offset]})
};
assert_throws_js(TypeError, constructorFunc);
}, '\'' + offset + '\' is an invalid scroll offset value in scrollOffsets');
}, formatOffset(offset) + ' is an invalid scroll offset value in scrollOffsets');
}
for (const suffix of gInvalidScrollOffsetSuffixes) {
......
......@@ -190,19 +190,19 @@ promise_test(async t => {
scrollSource: scroller,
timeRange: scrollerSize,
orientation: 'horizontal',
startScrollOffset: '20px'
startScrollOffset: CSS.px(20)
});
const percentageScrollTimeline = new ScrollTimeline({
scrollSource: scroller,
timeRange: scrollerSize,
orientation: 'horizontal',
startScrollOffset: '20%'
startScrollOffset: CSS.percent(20)
});
const calcScrollTimeline = new ScrollTimeline({
scrollSource: scroller,
timeRange: scrollerSize,
orientation: 'horizontal',
startScrollOffset: 'calc(20% - 5px)'
startScrollOffset: CSS.percent(20).sub(CSS.px(5))
});
// Unscrolled, all timelines should read a current time of 0, since
......@@ -293,19 +293,19 @@ promise_test(async t => {
scrollSource: scroller,
timeRange: scrollerSize,
orientation: 'horizontal',
endScrollOffset: (scrollerSize - 20) + 'px'
endScrollOffset: CSS.px(scrollerSize - 20)
});
const percentageScrollTimeline = new ScrollTimeline({
scrollSource: scroller,
timeRange: scrollerSize,
orientation: 'horizontal',
endScrollOffset: '80%'
endScrollOffset: CSS.percent(80)
});
const calcScrollTimeline = new ScrollTimeline({
scrollSource: scroller,
timeRange: scrollerSize,
orientation: 'horizontal',
endScrollOffset: 'calc(80% + 5px)'
endScrollOffset: CSS.percent(80).add(CSS.px(5))
});
// With direction rtl offsets are inverted, such that scrollLeft == 0
......@@ -393,19 +393,19 @@ promise_test(async t => {
scrollSource: scroller,
timeRange: scrollerSize,
orientation: 'block',
endScrollOffset: scrollerSize + 'px'
endScrollOffset: CSS.px(scrollerSize)
});
const inclusivePercentageScrollTimeline = new ScrollTimeline({
scrollSource: scroller,
timeRange: scrollerSize,
orientation: 'block',
endScrollOffset: '100%'
endScrollOffset: CSS.percent(100)
});
const inclusiveCalcScrollTimeline = new ScrollTimeline({
scrollSource: scroller,
timeRange: scrollerSize,
orientation: 'block',
endScrollOffset: 'calc(80% + ' + (0.2 * scrollerSize) + 'px)'
endScrollOffset: CSS.percent(80).sub(CSS.px(0.2 * scrollerSize))
});
// With direction rtl offsets are inverted, such that scrollLeft ==
......
......@@ -97,19 +97,19 @@ promise_test(async t => {
scrollSource: scroller,
timeRange: scrollerSize,
orientation: 'block',
startScrollOffset: '20px'
startScrollOffset: CSS.px(20)
});
const percentageScrollTimeline = new ScrollTimeline({
scrollSource: scroller,
timeRange: scrollerSize,
orientation: 'block',
startScrollOffset: '20%'
startScrollOffset: CSS.percent(20)
});
const calcScrollTimeline = new ScrollTimeline({
scrollSource: scroller,
timeRange: scrollerSize,
orientation: 'block',
startScrollOffset: 'calc(20% - 5px)'
startScrollOffset: CSS.percent(20).sub(CSS.px(5))
});
// Unscrolled all timelines should read a current time of 0, as the
......@@ -242,19 +242,19 @@ promise_test(async t => {
scrollSource: scroller,
timeRange: scrollerSize,
orientation: 'block',
endScrollOffset: scrollerSize + 'px'
endScrollOffset: CSS.px(scrollerSize)
});
const inclusivePercentageScrollTimeline = new ScrollTimeline({
scrollSource: scroller,
timeRange: scrollerSize,
orientation: 'block',
endScrollOffset: '100%'
endScrollOffset: CSS.percent(100)
});
const inclusiveCalcScrollTimeline = new ScrollTimeline({
scrollSource: scroller,
timeRange: scrollerSize,
orientation: 'block',
endScrollOffset: 'calc(80% + ' + (0.2 * scrollerSize) + 'px)'
endScrollOffset: CSS.percent(80).add(CSS.px(0.2 * scrollerSize))
});
scroller.scrollTop = scrollerSize;
......@@ -290,19 +290,19 @@ promise_test(async t => {
scrollSource: scroller,
timeRange: scrollerSize,
orientation: 'block',
endScrollOffset: (scrollerSize - 20) + 'px'
endScrollOffset: CSS.px(scrollerSize - 20)
});
const percentageScrollTimeline = new ScrollTimeline({
scrollSource: scroller,
timeRange: scrollerSize,
orientation: 'block',
endScrollOffset: '80%'
endScrollOffset: CSS.percent(80)
});
const calcScrollTimeline = new ScrollTimeline({
scrollSource: scroller,
timeRange: scrollerSize,
orientation: 'block',
endScrollOffset: 'calc(80% + 5px)'
endScrollOffset: CSS.percent(80).add(CSS.px(5))
});
// Check the length-based ScrollTimeline.
......@@ -403,8 +403,8 @@ promise_test(async t => {
scrollSource: scroller,
timeRange: scrollerSize,
orientation: 'block',
startScrollOffset: '20px',
endScrollOffset: (scrollerSize - 50) + 'px'
startScrollOffset: CSS.px(20),
endScrollOffset: CSS.px(scrollerSize - 50)
});
scroller.scrollTop = 150;
......@@ -427,8 +427,8 @@ promise_test(async t => {
scrollSource: scroller,
timeRange: scrollerSize,
orientation: 'block',
startScrollOffset: '20px',
endScrollOffset: '20px',
startScrollOffset: CSS.px(20),
endScrollOffset: CSS.px(20)
});
scroller.scrollTop = 150;
......@@ -449,8 +449,8 @@ promise_test(async t => {
scrollSource: scroller,
timeRange: scrollerSize,
orientation: 'block',
startScrollOffset: '50px',
endScrollOffset: '10px',
startScrollOffset: CSS.px(50),
endScrollOffset: CSS.px(10)
});
scroller.scrollTop = 40;
......@@ -474,7 +474,7 @@ promise_test(async t => {
scrollSource: scroller,
timeRange: scrollerSize,
orientation: 'block',
scrollOffsets: ['10px', '20px', '40px', '70px', '90px'],
scrollOffsets: [CSS.px(10), CSS.px(20), CSS.px(40), CSS.px(70), CSS.px(90)],
});
var offset = 0;
......
......@@ -64,8 +64,8 @@ layout changes on percentage-based scroll offset">
const timeline = new ScrollTimeline({
scrollSource: scroller,
timeRange: 1000,
startScrollOffset: '20%',
endScrollOffset: '80%'
startScrollOffset: CSS.percent(20),
endScrollOffset: CSS.percent(80)
});
const animation = new Animation(effect, timeline);
animation.play();
......
......@@ -92,7 +92,7 @@
function create_scroll_timeline_fill_test(delay, scroll_percentage, expected){
return async t => {
const target = createDiv(t);
const timeline = createScrollTimelineWithOffsets(t, "20%", "80%");
const timeline = createScrollTimelineWithOffsets(t, CSS.percent(20), CSS.percent(80));
const effect = new KeyframeEffect(
target,
{
......@@ -190,7 +190,7 @@
promise_test(async t => {
const animation = new Animation(
createKeyframeEffectOpacity(t),
createScrollTimelineWithOffsets(t, "20%", "80%")
createScrollTimelineWithOffsets(t, CSS.percent(20), CSS.percent(80))
);
const scroller = animation.timeline.scrollSource;
......@@ -218,7 +218,7 @@
promise_test(async t => {
const animation = new Animation(
createKeyframeEffectOpacity(t),
createScrollTimelineWithOffsets(t, "20%", "80%")
createScrollTimelineWithOffsets(t, CSS.percent(20), CSS.percent(80))
);
const scroller = animation.timeline.scrollSource;
......@@ -259,7 +259,7 @@
promise_test(async t => {
const animation = new Animation(
createKeyframeEffectOpacity(t),
createScrollTimelineWithOffsets(t, "20%", "80%")
createScrollTimelineWithOffsets(t, CSS.percent(20), CSS.percent(80))
);
const scroller = animation.timeline.scrollSource;
const maxScroll = scroller.scrollHeight - scroller.clientHeight;
......@@ -317,7 +317,7 @@ promise_test(async t => {
promise_test(async t => {
const animation = new Animation(
createKeyframeEffectOpacity(t),
createScrollTimelineWithOffsets(t, "20%", "80%")
createScrollTimelineWithOffsets(t, CSS.percent(20), CSS.percent(80))
);
const scroller = animation.timeline.scrollSource;
const maxScroll = scroller.scrollHeight - scroller.clientHeight;
......
......@@ -58,7 +58,7 @@
for (const test_case_key in test_cases){
const test_case = test_cases[test_case_key];
promise_test(async t => {
const timeline = createScrollTimelineWithOffsets(t, "20%", "80%");
const timeline = createScrollTimelineWithOffsets(t, CSS.percent(20), CSS.percent(80));
const scroller = timeline.scrollSource;
const maxScroll = scroller.scrollHeight - scroller.clientHeight;
......@@ -111,7 +111,7 @@
const test_case =
test_cases_start_offset_greater_than_end_offset[test_case_key];
promise_test(async t => {
const timeline = createScrollTimelineWithOffsets(t, "80%", "20%");
const timeline = createScrollTimelineWithOffsets(t, CSS.percent(80), CSS.percent(20));
const scroller = timeline.scrollSource;
const maxScroll = scroller.scrollHeight - scroller.clientHeight;
......@@ -154,7 +154,7 @@
const test_case =
test_cases_start_offset_equal_to_end_offset[test_case_key];
promise_test(async t => {
const timeline = createScrollTimelineWithOffsets(t, "50%", "50%");
const timeline = createScrollTimelineWithOffsets(t, CSS.percent(50), CSS.percent(50));
const scroller = timeline.scrollSource;
const maxScroll = scroller.scrollHeight - scroller.clientHeight;
......
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