Commit 57c4e7d9 authored by Anders Hartvoll Ruud's avatar Anders Hartvoll Ruud Committed by Commit Bot

[css-properties-values-api] Support calc() values for <integer>.

For custom properties registered with <integer>, it should according to
CSS Values and Units Level 4 be possible to apply non-integral values,
and then have the value round to the nearest integer computed value time.

To do this, this CL ...:

 * Changes the parsing of <integer> values such that non-integral calc()
   expressions are allowed.
 * Recognizes such calc()-expressions computed-value time, resolves them,
   and rounds them to the nearest integer.
 * Wraps values such as CSS.number(1.5) in calc() if necessary, when
   setting such values using Typed OM.

R=futhark@chromium.org

Bug: 641877
Change-Id: I2d4d7c4d72c9b2069f2fe10b1be1b4b94d5900e2
Reviewed-on: https://chromium-review.googlesource.com/c/1280663
Commit-Queue: Anders Ruud <andruud@chromium.org>
Reviewed-by: default avatarRune Lillesveen <futhark@chromium.org>
Cr-Commit-Position: refs/heads/master@{#600716}
parent 64cbe663
This is a testharness.js-based test. This is a testharness.js-based test.
Found 126 tests; 125 PASS, 1 FAIL, 0 TIMEOUT, 0 NOTRUN. Found 130 tests; 129 PASS, 1 FAIL, 0 TIMEOUT, 0 NOTRUN.
PASS syntax:'*', initialValue:'a' is valid PASS syntax:'*', initialValue:'a' is valid
PASS syntax:' * ', initialValue:'b' is valid PASS syntax:' * ', initialValue:'b' is valid
PASS syntax:'<length>', initialValue:'2px' is valid PASS syntax:'<length>', initialValue:'2px' is valid
...@@ -30,6 +30,10 @@ PASS syntax:'<number>', initialValue:'-109' is valid ...@@ -30,6 +30,10 @@ PASS syntax:'<number>', initialValue:'-109' is valid
PASS syntax:'<number>', initialValue:'2.3e4' is valid PASS syntax:'<number>', initialValue:'2.3e4' is valid
PASS syntax:'<integer>', initialValue:'-109' is valid PASS syntax:'<integer>', initialValue:'-109' is valid
PASS syntax:'<integer>', initialValue:'19' is valid PASS syntax:'<integer>', initialValue:'19' is valid
PASS syntax:'<integer>', initialValue:'calc(1)' is valid
PASS syntax:'<integer>', initialValue:'calc(1 + 2)' is valid
PASS syntax:'<integer>', initialValue:'calc(3.1415)' is valid
PASS syntax:'<integer>', initialValue:'calc(3.1415 + 3.1415)' is valid
PASS syntax:'<angle>', initialValue:'10deg' is valid PASS syntax:'<angle>', initialValue:'10deg' is valid
PASS syntax:'<angle>', initialValue:'20.5rad' is valid PASS syntax:'<angle>', initialValue:'20.5rad' is valid
PASS syntax:'<angle>', initialValue:'calc(50grad + 3.14159rad)' is valid PASS syntax:'<angle>', initialValue:'calc(50grad + 3.14159rad)' is valid
......
...@@ -55,6 +55,10 @@ assert_valid("<number>", "-109"); ...@@ -55,6 +55,10 @@ assert_valid("<number>", "-109");
assert_valid("<number>", "2.3e4"); assert_valid("<number>", "2.3e4");
assert_valid("<integer>", "-109"); assert_valid("<integer>", "-109");
assert_valid("<integer>", "19"); assert_valid("<integer>", "19");
assert_valid("<integer>", "calc(1)");
assert_valid("<integer>", "calc(1 + 2)");
assert_valid("<integer>", "calc(3.1415)");
assert_valid("<integer>", "calc(3.1415 + 3.1415)");
assert_valid("<angle>", "10deg"); assert_valid("<angle>", "10deg");
assert_valid("<angle>", "20.5rad"); assert_valid("<angle>", "20.5rad");
......
...@@ -111,5 +111,17 @@ for (let element of [divWithFontSizeSet, divWithFontSizeInherited]) { ...@@ -111,5 +111,17 @@ for (let element of [divWithFontSizeSet, divWithFontSizeInherited]) {
assert_computed_value('<transform-function>', 'translateX(calc(11em + 10%))', 'translateX(calc(110px + 10%))'); assert_computed_value('<transform-function>', 'translateX(calc(11em + 10%))', 'translateX(calc(110px + 10%))');
assert_computed_value('<transform-function>+', 'translateX(10%) scale(2)', 'translateX(10%) scale(2)'); assert_computed_value('<transform-function>+', 'translateX(10%) scale(2)', 'translateX(10%) scale(2)');
}, "<transform-function> values are computed correctly for " + id); }, "<transform-function> values are computed correctly for " + id);
test(function() {
assert_computed_value('<integer>', '15', '15');
assert_computed_value('<integer>', 'calc(15 + 15)', '30');
assert_computed_value('<integer>', 'calc(2.4)', '2');
assert_computed_value('<integer>', 'calc(2.6)', '3');
assert_computed_value('<integer>', 'calc(2.6 + 3.1)', '6');
}, "<integer> values are computed correctly for " + id);
test(function() {
assert_computed_value('<integer>+', '15 calc(2.4) calc(2.6)', '15 2 3');
}, "<integer>+ values are computed correctly for " + id);
} }
</script> </script>
...@@ -382,8 +382,8 @@ test_style_property_map_set({ ...@@ -382,8 +382,8 @@ test_style_property_map_set({
test_style_property_map_set({ test_style_property_map_set({
syntax: '<integer>', syntax: '<integer>',
initialValue: '0', initialValue: '0',
shouldAccept: [CSS.number(1), CSS.number(-42), '1', '-42'], shouldAccept: [CSS.number(1), CSS.number(-42), '1', '-42', 'calc(2.4)'],
shouldReject: [unparsed('42'), CSS.px(100), '50px', [CSS.number(42), '42']], shouldReject: [unparsed('42'), CSS.px(100), '50px', [CSS.number(42), '42'], 'calc(2px + 1px)'],
}); });
test_style_property_map_set({ test_style_property_map_set({
...@@ -482,8 +482,8 @@ test_style_property_map_set({ ...@@ -482,8 +482,8 @@ test_style_property_map_set({
test_style_property_map_set({ test_style_property_map_set({
syntax: '<integer>+', syntax: '<integer>+',
initialValue: '0', initialValue: '0',
shouldAccept: [CSS.number(42), [CSS.number(42), '42'], '42 42'], shouldAccept: [CSS.number(42), [CSS.number(42), '42'], '42 42', 'calc(2.4) calc(2.6)'],
shouldReject: [[CSS.number(42), keyword('noint')], '42 noint'], shouldReject: [[CSS.number(42), keyword('noint')], '42 noint', 'calc(2px + 2px)'],
}); });
test_style_property_map_set({ test_style_property_map_set({
......
...@@ -93,7 +93,8 @@ bool CSSOMTypes::IsPropertySupported(CSSPropertyID id) { ...@@ -93,7 +93,8 @@ bool CSSOMTypes::IsPropertySupported(CSSPropertyID id) {
bool CSSOMTypes::PropertyCanTake(CSSPropertyID id, bool CSSOMTypes::PropertyCanTake(CSSPropertyID id,
const AtomicString& custom_property_name, const AtomicString& custom_property_name,
const PropertyRegistration* registration, const PropertyRegistration* registration,
const CSSStyleValue& value) { const CSSStyleValue& value,
const CSSSyntaxComponent*& match) {
DCHECK_EQ(id == CSSPropertyVariable, !custom_property_name.IsNull()); DCHECK_EQ(id == CSSPropertyVariable, !custom_property_name.IsNull());
if (id == CSSPropertyVariable && registration) { if (id == CSSPropertyVariable && registration) {
...@@ -101,7 +102,8 @@ bool CSSOMTypes::PropertyCanTake(CSSPropertyID id, ...@@ -101,7 +102,8 @@ bool CSSOMTypes::PropertyCanTake(CSSPropertyID id,
return ToCSSUnsupportedStyleValue(value).GetCustomPropertyName() == return ToCSSUnsupportedStyleValue(value).GetCustomPropertyName() ==
custom_property_name; custom_property_name;
} }
return registration->Syntax().CanTake(value); match = registration->Syntax().Match(value);
return match != nullptr;
} }
if (value.GetType() == CSSStyleValue::kKeywordType) { if (value.GetType() == CSSStyleValue::kKeywordType) {
......
...@@ -179,7 +179,7 @@ const CSSValue* ConsumeSingleType(const CSSSyntaxComponent& syntax, ...@@ -179,7 +179,7 @@ const CSSValue* ConsumeSingleType(const CSSSyntaxComponent& syntax,
case CSSSyntaxType::kUrl: case CSSSyntaxType::kUrl:
return ConsumeUrl(range, context); return ConsumeUrl(range, context);
case CSSSyntaxType::kInteger: case CSSSyntaxType::kInteger:
return ConsumeInteger(range); return ConsumeIntegerOrNumberCalc(range);
case CSSSyntaxType::kAngle: case CSSSyntaxType::kAngle:
return ConsumeAngle(range, context, base::Optional<WebFeature>()); return ConsumeAngle(range, context, base::Optional<WebFeature>());
case CSSSyntaxType::kTime: case CSSSyntaxType::kTime:
...@@ -278,12 +278,17 @@ bool CSSSyntaxComponent::CanTake(const CSSStyleValue& value) const { ...@@ -278,12 +278,17 @@ bool CSSSyntaxComponent::CanTake(const CSSStyleValue& value) const {
} }
} }
bool CSSSyntaxDescriptor::CanTake(const CSSStyleValue& value) const { const CSSSyntaxComponent* CSSSyntaxDescriptor::Match(
const CSSStyleValue& value) const {
for (const CSSSyntaxComponent& component : syntax_components_) { for (const CSSSyntaxComponent& component : syntax_components_) {
if (component.CanTake(value)) if (component.CanTake(value))
return true; return &component;
} }
return false; return nullptr;
}
bool CSSSyntaxDescriptor::CanTake(const CSSStyleValue& value) const {
return Match(value);
} }
const CSSValue* CSSSyntaxDescriptor::Parse(CSSParserTokenRange range, const CSSValue* CSSSyntaxDescriptor::Parse(CSSParserTokenRange range,
......
...@@ -49,6 +49,7 @@ class CSSSyntaxComponent { ...@@ -49,6 +49,7 @@ class CSSSyntaxComponent {
const String& GetString() const { return string_; } const String& GetString() const { return string_; }
CSSSyntaxRepeat GetRepeat() const { return repeat_; } CSSSyntaxRepeat GetRepeat() const { return repeat_; }
bool IsRepeatable() const { return repeat_ != CSSSyntaxRepeat::kNone; } bool IsRepeatable() const { return repeat_ != CSSSyntaxRepeat::kNone; }
bool IsInteger() const { return type_ == CSSSyntaxType::kInteger; }
char Separator() const { char Separator() const {
DCHECK(IsRepeatable()); DCHECK(IsRepeatable());
return repeat_ == CSSSyntaxRepeat::kSpaceSeparated ? ' ' : ','; return repeat_ == CSSSyntaxRepeat::kSpaceSeparated ? ' ' : ',';
...@@ -69,6 +70,7 @@ class CORE_EXPORT CSSSyntaxDescriptor { ...@@ -69,6 +70,7 @@ class CORE_EXPORT CSSSyntaxDescriptor {
const CSSValue* Parse(CSSParserTokenRange, const CSSValue* Parse(CSSParserTokenRange,
const CSSParserContext*, const CSSParserContext*,
bool is_animation_tainted) const; bool is_animation_tainted) const;
const CSSSyntaxComponent* Match(const CSSStyleValue&) const;
bool CanTake(const CSSStyleValue&) const; bool CanTake(const CSSStyleValue&) const;
bool IsValid() const { return !syntax_components_.IsEmpty(); } bool IsValid() const { return !syntax_components_.IsEmpty(); }
bool IsTokenStream() const { bool IsTokenStream() const {
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
namespace blink { namespace blink {
class CSSSyntaxComponent;
class ExceptionState; class ExceptionState;
class ExecutionContext; class ExecutionContext;
enum class SecureContextMode; enum class SecureContextMode;
...@@ -66,7 +67,9 @@ class CORE_EXPORT CSSStyleValue : public ScriptWrappable { ...@@ -66,7 +67,9 @@ class CORE_EXPORT CSSStyleValue : public ScriptWrappable {
virtual const CSSValue* ToCSSValue() const = 0; virtual const CSSValue* ToCSSValue() const = 0;
// FIXME: We should make this a method on CSSProperty instead. // FIXME: We should make this a method on CSSProperty instead.
virtual const CSSValue* ToCSSValueWithProperty(CSSPropertyID) const { virtual const CSSValue* ToCSSValueWithProperty(
CSSPropertyID,
const CSSSyntaxComponent*) const {
return ToCSSValue(); return ToCSSValue();
} }
virtual String toString() const; virtual String toString() const;
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "third_party/blink/renderer/core/animation/length_property_functions.h" #include "third_party/blink/renderer/core/animation/length_property_functions.h"
#include "third_party/blink/renderer/core/css/css_calculation_value.h" #include "third_party/blink/renderer/core/css/css_calculation_value.h"
#include "third_party/blink/renderer/core/css/css_resolution_units.h" #include "third_party/blink/renderer/core/css/css_resolution_units.h"
#include "third_party/blink/renderer/core/css/css_syntax_descriptor.h"
#include "third_party/blink/renderer/core/css/cssom/css_math_invert.h" #include "third_party/blink/renderer/core/css/cssom/css_math_invert.h"
#include "third_party/blink/renderer/core/css/cssom/css_math_max.h" #include "third_party/blink/renderer/core/css/cssom/css_math_max.h"
#include "third_party/blink/renderer/core/css/cssom/css_math_min.h" #include "third_party/blink/renderer/core/css/cssom/css_math_min.h"
...@@ -37,7 +38,8 @@ CSSPrimitiveValue::UnitType ToCanonicalUnitIfPossible( ...@@ -37,7 +38,8 @@ CSSPrimitiveValue::UnitType ToCanonicalUnitIfPossible(
bool IsValueOutOfRangeForProperty(CSSPropertyID property_id, bool IsValueOutOfRangeForProperty(CSSPropertyID property_id,
double value, double value,
CSSPrimitiveValue::UnitType unit) { CSSPrimitiveValue::UnitType unit,
const CSSSyntaxComponent* match) {
// FIXME: Avoid this CSSProperty::Get call as it can be costly. // FIXME: Avoid this CSSProperty::Get call as it can be costly.
// The caller often has a CSSProperty already, so we can just pass it here. // The caller often has a CSSProperty already, so we can just pass it here.
if (LengthPropertyFunctions::GetValueRange(CSSProperty::Get(property_id)) == if (LengthPropertyFunctions::GetValueRange(CSSProperty::Get(property_id)) ==
...@@ -47,6 +49,10 @@ bool IsValueOutOfRangeForProperty(CSSPropertyID property_id, ...@@ -47,6 +49,10 @@ bool IsValueOutOfRangeForProperty(CSSPropertyID property_id,
// For non-length properties and special cases. // For non-length properties and special cases.
switch (property_id) { switch (property_id) {
case CSSPropertyVariable:
if (match && match->IsInteger())
return round(value) != value;
return false;
case CSSPropertyOrder: case CSSPropertyOrder:
case CSSPropertyZIndex: case CSSPropertyZIndex:
return round(value) != value; return round(value) != value;
...@@ -167,8 +173,9 @@ const CSSPrimitiveValue* CSSUnitValue::ToCSSValue() const { ...@@ -167,8 +173,9 @@ const CSSPrimitiveValue* CSSUnitValue::ToCSSValue() const {
} }
const CSSPrimitiveValue* CSSUnitValue::ToCSSValueWithProperty( const CSSPrimitiveValue* CSSUnitValue::ToCSSValueWithProperty(
CSSPropertyID property_id) const { CSSPropertyID property_id,
if (IsValueOutOfRangeForProperty(property_id, value_, unit_)) { const CSSSyntaxComponent* match) const {
if (IsValueOutOfRangeForProperty(property_id, value_, unit_, match)) {
// Wrap out of range values with a calc. // Wrap out of range values with a calc.
CSSCalcExpressionNode* node = ToCalcExpressionNode(); CSSCalcExpressionNode* node = ToCalcExpressionNode();
node->SetIsNestedCalc(); node->SetIsNestedCalc();
......
...@@ -46,7 +46,9 @@ class CORE_EXPORT CSSUnitValue final : public CSSNumericValue { ...@@ -46,7 +46,9 @@ class CORE_EXPORT CSSUnitValue final : public CSSNumericValue {
// From CSSStyleValue. // From CSSStyleValue.
StyleValueType GetType() const final; StyleValueType GetType() const final;
const CSSPrimitiveValue* ToCSSValue() const final; const CSSPrimitiveValue* ToCSSValue() const final;
const CSSPrimitiveValue* ToCSSValueWithProperty(CSSPropertyID) const final; const CSSPrimitiveValue* ToCSSValueWithProperty(
CSSPropertyID,
const CSSSyntaxComponent*) const final;
CSSCalcExpressionNode* ToCalcExpressionNode() const final; CSSCalcExpressionNode* ToCalcExpressionNode() const final;
private: private:
......
...@@ -33,10 +33,15 @@ class CSSOMTypes { ...@@ -33,10 +33,15 @@ class CSSOMTypes {
static bool IsCSSStyleValuePosition(const CSSStyleValue&); static bool IsCSSStyleValuePosition(const CSSStyleValue&);
static bool IsPropertySupported(CSSPropertyID); static bool IsPropertySupported(CSSPropertyID);
// For registered custom properties, if the CSSStyleValue is accepted
// because it matches the registered grammar (and not because it is
// a CSSUnsupportedStyleValue with matching name), 'match' will be set
// to the component that was matched.
static bool PropertyCanTake(CSSPropertyID, static bool PropertyCanTake(CSSPropertyID,
const AtomicString& custom_property_name, const AtomicString& custom_property_name,
const PropertyRegistration*, const PropertyRegistration*,
const CSSStyleValue&); const CSSStyleValue&,
const CSSSyntaxComponent*& match);
}; };
} // namespace blink } // namespace blink
......
...@@ -53,6 +53,16 @@ CSSValueList* CssValueListForPropertyID(CSSPropertyID property_id) { ...@@ -53,6 +53,16 @@ CSSValueList* CssValueListForPropertyID(CSSPropertyID property_id) {
} }
} }
String StyleValueToString(const CSSProperty& property,
const CSSStyleValue& style_value,
const CSSSyntaxComponent* syntax_component) {
if (style_value.GetType() == CSSStyleValue::kUnknownType)
return style_value.toString();
return style_value
.ToCSSValueWithProperty(property.PropertyID(), syntax_component)
->CssText();
}
const CSSVariableReferenceValue* CreateVariableReferenceValue( const CSSVariableReferenceValue* CreateVariableReferenceValue(
const String& value, const String& value,
const CSSParserContext& context) { const CSSParserContext& context) {
...@@ -78,14 +88,16 @@ const CSSVariableReferenceValue* CreateVariableReferenceValue( ...@@ -78,14 +88,16 @@ const CSSVariableReferenceValue* CreateVariableReferenceValue(
StringBuilder builder; StringBuilder builder;
for (const auto& value : values) { for (const auto& value : values) {
const CSSSyntaxComponent* syntax_component = nullptr;
if (!CSSOMTypes::PropertyCanTake(property.PropertyID(), if (!CSSOMTypes::PropertyCanTake(property.PropertyID(),
custom_property_name, &registration, custom_property_name, &registration,
*value)) { *value, syntax_component)) {
return nullptr; return nullptr;
} }
if (!builder.IsEmpty()) if (!builder.IsEmpty())
builder.Append(separator); builder.Append(separator);
builder.Append(value->toString()); builder.Append(StyleValueToString(property, *value, syntax_component));
} }
return CreateVariableReferenceValue(builder.ToString(), context); return CreateVariableReferenceValue(builder.ToString(), context);
...@@ -100,9 +112,12 @@ const CSSValue* StyleValueToCSSValue( ...@@ -100,9 +112,12 @@ const CSSValue* StyleValueToCSSValue(
DCHECK_EQ(property.IDEquals(CSSPropertyVariable), DCHECK_EQ(property.IDEquals(CSSPropertyVariable),
!custom_property_name.IsNull()); !custom_property_name.IsNull());
const CSSSyntaxComponent* syntax_component = nullptr;
const CSSPropertyID property_id = property.PropertyID(); const CSSPropertyID property_id = property.PropertyID();
if (!CSSOMTypes::PropertyCanTake(property_id, custom_property_name, if (!CSSOMTypes::PropertyCanTake(property_id, custom_property_name,
registration, style_value)) { registration, style_value,
syntax_component)) {
return nullptr; return nullptr;
} }
...@@ -123,7 +138,9 @@ const CSSValue* StyleValueToCSSValue( ...@@ -123,7 +138,9 @@ const CSSValue* StyleValueToCSSValue(
if (registration && if (registration &&
style_value.GetType() != CSSStyleValue::kUnparsedType) { style_value.GetType() != CSSStyleValue::kUnparsedType) {
CSSParserContext* context = CSSParserContext::Create(execution_context); CSSParserContext* context = CSSParserContext::Create(execution_context);
return CreateVariableReferenceValue(style_value.toString(), *context); String string =
StyleValueToString(property, style_value, syntax_component);
return CreateVariableReferenceValue(string, *context);
} }
break; break;
case CSSPropertyBorderBottomLeftRadius: case CSSPropertyBorderBottomLeftRadius:
...@@ -246,7 +263,7 @@ const CSSValue* StyleValueToCSSValue( ...@@ -246,7 +263,7 @@ const CSSValue* StyleValueToCSSValue(
break; break;
} }
return style_value.ToCSSValueWithProperty(property_id); return style_value.ToCSSValueWithProperty(property_id, syntax_component);
} }
const CSSValue* CoerceStyleValueOrString( const CSSValue* CoerceStyleValueOrString(
...@@ -342,8 +359,10 @@ void StylePropertyMap::set(const ExecutionContext* execution_context, ...@@ -342,8 +359,10 @@ void StylePropertyMap::set(const ExecutionContext* execution_context,
String css_text; String css_text;
if (values[0].IsCSSStyleValue()) { if (values[0].IsCSSStyleValue()) {
CSSStyleValue* style_value = values[0].GetAsCSSStyleValue(); CSSStyleValue* style_value = values[0].GetAsCSSStyleValue();
if (style_value && CSSOMTypes::PropertyCanTake(property_id, g_null_atom, const CSSSyntaxComponent* syntax_component = nullptr;
nullptr, *style_value)) { if (style_value &&
CSSOMTypes::PropertyCanTake(property_id, g_null_atom, nullptr,
*style_value, syntax_component)) {
css_text = style_value->toString(); css_text = style_value->toString();
} }
} else { } else {
......
...@@ -245,6 +245,30 @@ CSSPrimitiveValue* ConsumeInteger(CSSParserTokenRange& range, ...@@ -245,6 +245,30 @@ CSSPrimitiveValue* ConsumeInteger(CSSParserTokenRange& range,
return nullptr; return nullptr;
} }
// This implements the behavior defined in [1], where calc() expressions
// are valid when <integer> is expected, even if the calc()-expression does
// not result in an integral value.
//
// TODO(andruud): Eventually this behavior should just be part of
// ConsumeInteger, and this function can be removed. For now, having a separate
// function with this behavior allows us to implement [1] gradually.
//
// [1] https://drafts.csswg.org/css-values-4/#calc-type-checking
CSSPrimitiveValue* ConsumeIntegerOrNumberCalc(CSSParserTokenRange& range) {
CSSParserTokenRange int_range(range);
if (CSSPrimitiveValue* value = ConsumeInteger(int_range)) {
range = int_range;
return value;
}
CalcParser calc_parser(range);
if (const CSSCalcValue* calculation = calc_parser.Value()) {
if (calculation->Category() != kCalcNumber)
return nullptr;
return calc_parser.ConsumeValue();
}
return nullptr;
}
CSSPrimitiveValue* ConsumePositiveInteger(CSSParserTokenRange& range) { CSSPrimitiveValue* ConsumePositiveInteger(CSSParserTokenRange& range) {
return ConsumeInteger(range, 1); return ConsumeInteger(range, 1);
} }
......
...@@ -45,6 +45,7 @@ enum class UnitlessQuirk { kAllow, kForbid }; ...@@ -45,6 +45,7 @@ enum class UnitlessQuirk { kAllow, kForbid };
CSSPrimitiveValue* ConsumeInteger( CSSPrimitiveValue* ConsumeInteger(
CSSParserTokenRange&, CSSParserTokenRange&,
double minimum_value = -std::numeric_limits<double>::max()); double minimum_value = -std::numeric_limits<double>::max());
CSSPrimitiveValue* ConsumeIntegerOrNumberCalc(CSSParserTokenRange&);
CSSPrimitiveValue* ConsumePositiveInteger(CSSParserTokenRange&); CSSPrimitiveValue* ConsumePositiveInteger(CSSParserTokenRange&);
bool ConsumeNumberRaw(CSSParserTokenRange&, double& result); bool ConsumeNumberRaw(CSSParserTokenRange&, double& result);
CSSPrimitiveValue* ConsumeNumber(CSSParserTokenRange&, ValueRange); CSSPrimitiveValue* ConsumeNumber(CSSParserTokenRange&, ValueRange);
......
...@@ -1701,6 +1701,18 @@ static const CSSValue& ComputeRegisteredPropertyValue( ...@@ -1701,6 +1701,18 @@ static const CSSValue& ComputeRegisteredPropertyValue(
css_to_length_conversion_data.CopyWithAdjustedZoom(1)); css_to_length_conversion_data.CopyWithAdjustedZoom(1));
return *CSSPrimitiveValue::Create(length, 1); return *CSSPrimitiveValue::Create(length, 1);
} }
// If we encounter a calculated number that was not resolved during
// parsing, it means that a calc()-expression was allowed in place of
// an integer. Such calc()-for-integers must be rounded at computed value
// time.
// https://drafts.csswg.org/css-values-4/#calc-type-checking
if (primitive_value.IsCalculated() &&
(primitive_value.TypeWithCalcResolved() ==
CSSPrimitiveValue::UnitType::kNumber)) {
double double_value = primitive_value.CssCalcValue()->DoubleValue();
auto unit_type = CSSPrimitiveValue::UnitType::kInteger;
return *CSSPrimitiveValue::Create(std::round(double_value), unit_type);
}
} }
return value; return value;
} }
......
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