Commit b786c57c authored by Anders Hartvoll Ruud's avatar Anders Hartvoll Ruud Committed by Commit Bot

[css-properties-values-api] Support CSSStyleValues in StylePropertyMap.set.

In StyleValueToCSSValue, when checking whether a certain CSSStyleValue
matches the property in question, we now ask the registration (via
CSSOMTypes) if the CSSStyleValue matches. If it doesn't match, we throw
a TypeError like for normal properties. If it does match, the CSSStyleValue
is stringified, tokenized, and set on the style rule as tokens.

I have postponed support for <color> and <transform-function>, because
CSSUnsupportedStyleValue currently does not handle registered custom
properties at all. This is appropriate to fix in a separate CL.

Note that, because the string version of StylePropertyMap.set also uses
StyleValueToCSSValue, it will no longer be possible to set registered
custom properties with a string--even if the syntax is matched.
A subsequent CL will fix this.

R=futhark@chromium.org

Bug: 641877
Change-Id: Ie0cc2f87e39f8f59015824bfd1b81efaf402c326
Reviewed-on: https://chromium-review.googlesource.com/1175822
Commit-Queue: Anders Ruud <andruud@chromium.org>
Reviewed-by: default avatarRune Lillesveen <futhark@chromium.org>
Cr-Commit-Position: refs/heads/master@{#583695}
parent ecdb6a3b
<!DOCTYPE html> <!DOCTYPE html>
<link rel="author" title="Anders Hartvoll Ruud" href="andruud@chromium.org"> <link rel="author" title="Anders Hartvoll Ruud" href="andruud@chromium.org">
<!-- TODO(andruud): Add Typed OM details to spec and link to it here. --> <!-- TODO(andruud): Add Typed OM details to spec and link to it here. -->
<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1/#supported-syntax-strings" /> <link rel="help" href="https://github.com/w3c/css-houdini-drafts/pull/783" />
<meta name="assert" content="Verifies that registered custom properties interact correctly with CSS Typed OM" /> <meta name="assert" content="Verifies that registered custom properties interact correctly with CSS Typed OM" />
<script src="/resources/testharness.js"></script> <script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script> <script src="/resources/testharnessreport.js"></script>
<style id=style>
div {}
</style>
<div id=target></div> <div id=target></div>
<script> <script>
...@@ -43,7 +45,7 @@ function assert_computed_type(name, value, expected) { ...@@ -43,7 +45,7 @@ function assert_computed_type(name, value, expected) {
} }
if (value != null) { if (value != null) {
target.attributeStyleMap.set(name, value); target.style = `${name}: ${value}`;
} }
let computedValue = target.computedStyleMap().get(name); let computedValue = target.computedStyleMap().get(name);
...@@ -52,7 +54,7 @@ function assert_computed_type(name, value, expected) { ...@@ -52,7 +54,7 @@ function assert_computed_type(name, value, expected) {
assert_true(computedValue instanceof expected); assert_true(computedValue instanceof expected);
if (value != null) { if (value != null) {
target.attributeStyleMap.delete(name); target.style = '';
} }
} }
...@@ -177,7 +179,7 @@ test(function(){ ...@@ -177,7 +179,7 @@ test(function(){
assert_equals(target.computedStyleMap().getAll(name).length, 2); assert_equals(target.computedStyleMap().getAll(name).length, 2);
assert_true(target.computedStyleMap().getAll(name).every(x => x instanceof CSSUnitValue)); assert_true(target.computedStyleMap().getAll(name).every(x => x instanceof CSSUnitValue));
target.attributeStyleMap.set(name, '10px 20px 30px'); target.style = `${name}: 10px 20px 30px`;
assert_equals(target.computedStyleMap().getAll(name).length, 3); assert_equals(target.computedStyleMap().getAll(name).length, 3);
assert_true(target.computedStyleMap().getAll(name).every(x => x instanceof CSSUnitValue)); assert_true(target.computedStyleMap().getAll(name).every(x => x instanceof CSSUnitValue));
}, 'All computed values correctly reified in space-separated list'); }, 'All computed values correctly reified in space-separated list');
...@@ -187,7 +189,7 @@ test(function(){ ...@@ -187,7 +189,7 @@ test(function(){
assert_equals(target.computedStyleMap().getAll(name).length, 2); assert_equals(target.computedStyleMap().getAll(name).length, 2);
assert_true(target.computedStyleMap().getAll(name).every(x => x instanceof CSSUnitValue)); assert_true(target.computedStyleMap().getAll(name).every(x => x instanceof CSSUnitValue));
target.attributeStyleMap.set(name, '10px, 20px, 30px'); target.style = `${name}: 10px, 20px, 30px`;
assert_equals(target.computedStyleMap().getAll(name).length, 3); assert_equals(target.computedStyleMap().getAll(name).length, 3);
assert_true(target.computedStyleMap().getAll(name).every(x => x instanceof CSSUnitValue)); assert_true(target.computedStyleMap().getAll(name).every(x => x instanceof CSSUnitValue));
}, 'All computed values correctly reified in comma-separated list'); }, 'All computed values correctly reified in comma-separated list');
...@@ -285,7 +287,7 @@ test(function(){ ...@@ -285,7 +287,7 @@ test(function(){
test(function(){ test(function(){
let name = gen_prop('<length>+', '0px'); let name = gen_prop('<length>+', '0px');
target.attributeStyleMap.clear(); target.attributeStyleMap.clear();
target.attributeStyleMap.set(name, '10px 20px 30px'); target.style = `${name}: 10px 20px 30px`;
assert_equals(target.attributeStyleMap.getAll(name).length, 3); assert_equals(target.attributeStyleMap.getAll(name).length, 3);
assert_true(target.attributeStyleMap.getAll(name).every(x => x instanceof CSSUnitValue)); assert_true(target.attributeStyleMap.getAll(name).every(x => x instanceof CSSUnitValue));
}, 'attributeStyleMap.getAll returns a list of CSSUnitValues for <length>+'); }, 'attributeStyleMap.getAll returns a list of CSSUnitValues for <length>+');
...@@ -293,9 +295,145 @@ test(function(){ ...@@ -293,9 +295,145 @@ test(function(){
test(function(){ test(function(){
let name = gen_prop('<length>#', '0px'); let name = gen_prop('<length>#', '0px');
target.attributeStyleMap.clear(); target.attributeStyleMap.clear();
target.attributeStyleMap.set(name, '10px, 20px, 30px'); target.style = `${name}: 10px, 20px, 30px`;
assert_equals(target.attributeStyleMap.getAll(name).length, 3); assert_equals(target.attributeStyleMap.getAll(name).length, 3);
assert_true(target.attributeStyleMap.getAll(name).every(x => x instanceof CSSUnitValue)); assert_true(target.attributeStyleMap.getAll(name).every(x => x instanceof CSSUnitValue));
}, 'attributeStyleMap.getAll returns a list of CSSUnitValues for <length>#'); }, 'attributeStyleMap.getAll returns a list of CSSUnitValues for <length>#');
// StylePropertyMap.set
function test_style_property_map_set_using_property_map(propertyMapName, propertyMap, options) {
test(function(){
let name = gen_prop(options.syntax, options.initialValue);
propertyMap.clear();
for (let value of options.shouldAccept)
propertyMap.set(name, value);
for (let value of options.shouldReject) {
assert_throws(new TypeError(), () => propertyMap.set(name, value));
}
}, `${propertyMapName}.set accepts correct CSSUnitValues for ${options.syntax}`);
}
// Verify that the correct CSSStyleValues are accepted/rejected for a registered
// property with the specified syntax.
//
// The same test is performed twice: once for attributeStyleMap, and once
// for styleMap.
function test_style_property_map_set(options) {
test_style_property_map_set_using_property_map('attributeStyleMap', target.attributeStyleMap, options);
test_style_property_map_set_using_property_map('styleMap', style.sheet.rules[0].styleMap, options);
}
let unparsed = x => new CSSUnparsedValue([x]);
let keyword = x => new CSSKeywordValue(x);
let sum = (a, b) => new CSSMathSum(a, b);
let url_image = x => CSSStyleValue.parse('background-image', x);
test_style_property_map_set({
syntax: '*',
initialValue: 'none',
shouldAccept: [unparsed('thing')],
shouldReject: [CSS.px(15), keyword('none')],
});
test_style_property_map_set({
syntax: '<angle>',
initialValue: '0deg',
shouldAccept: [CSS.deg(42), CSS.turn(2)],
shouldReject: [unparsed('42deg'), CSS.px(15)],
});
test_style_property_map_set({
syntax: '<custom-ident>',
initialValue: 'none',
shouldAccept: [keyword('foo')],
shouldReject: [unparsed('foo'), CSS.px(15)],
});
test_style_property_map_set({
syntax: '<image>',
initialValue: 'url(a)',
shouldAccept: [url_image('url(b)')],
shouldReject: [unparsed('url(b)'), CSS.px(100)],
});
test_style_property_map_set({
syntax: '<integer>',
initialValue: '0',
shouldAccept: [CSS.number(1), CSS.number(-42)],
shouldReject: [unparsed('42'), CSS.px(100)],
});
test_style_property_map_set({
syntax: '<length-percentage>',
initialValue: '0px',
shouldAccept: [CSS.percent(10), CSS.px(1), CSS.em(1)],
shouldReject: [unparsed('10%'), unparsed('10px'), CSS.dpi(1)],
});
test_style_property_map_set({
syntax: '<length>',
initialValue: '0px',
shouldAccept: [CSS.px(10), CSS.em(10), CSS.vh(200), sum(CSS.px(10), CSS.em(20))],
shouldReject: [unparsed('10px'), CSS.percent(1)],
});
test_style_property_map_set({
syntax: '<number>',
initialValue: '0',
shouldAccept: [CSS.number(1337), CSS.number(-42.5)],
shouldReject: [unparsed('42'), CSS.px(15)],
});
test_style_property_map_set({
syntax: '<percentage>',
initialValue: '0%',
shouldAccept: [CSS.percent(10)],
shouldReject: [unparsed('10%'), CSS.px(1)],
});
test_style_property_map_set({
syntax: '<resolution>',
initialValue: '0dpi',
shouldAccept: [CSS.dpi(100), CSS.dpcm(10), CSS.dppx(50)],
shouldReject: [unparsed('42'), CSS.px(15)],
});
test_style_property_map_set({
syntax: '<time>',
initialValue: '0s',
shouldAccept: [CSS.s(42), CSS.ms(16)],
shouldReject: [unparsed('42s'), CSS.px(15)],
});
test_style_property_map_set({
syntax: '<url>',
initialValue: 'url(a)',
shouldAccept: [url_image('url(b)')],
shouldReject: [unparsed('url(b)'), CSS.px(100)],
});
test_style_property_map_set({
syntax: '<transform-list>',
initialValue: 'translateX(0px)',
shouldAccept: [CSSStyleValue.parse('transform', 'translateX(10px)')],
shouldReject: [unparsed('transformX(10px'), CSS.px(100)],
});
test_style_property_map_set({
syntax: 'none | thing | THING',
initialValue: 'none',
shouldAccept: [keyword('thing'), keyword('THING')],
shouldReject: [unparsed('thing'), CSS.px(15), keyword('notathing')],
});
test_style_property_map_set({
syntax: '<angle> | <length>',
initialValue: '0deg',
shouldAccept: [CSS.deg(42), CSS.turn(2), CSS.px(10), CSS.em(10)],
shouldReject: [unparsed('42deg'), unparsed('20px'), CSS.s(1)],
});
</script> </script>
...@@ -7,73 +7,77 @@ ...@@ -7,73 +7,77 @@
#include "third_party/blink/renderer/core/css/cssom/cssom_types.h" #include "third_party/blink/renderer/core/css/cssom/cssom_types.h"
#include "third_party/blink/renderer/core/css/cssom/cssom_keywords.h"
#include "third_party/blink/renderer/core/css/cssom/css_keyword_value.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/cssom/css_numeric_value.h"
#include "third_party/blink/renderer/core/css/cssom/css_style_value.h" #include "third_party/blink/renderer/core/css/cssom/css_style_value.h"
#include "third_party/blink/renderer/core/css/cssom/css_unsupported_style_value.h" #include "third_party/blink/renderer/core/css/cssom/css_unsupported_style_value.h"
#include "third_party/blink/renderer/core/css/cssom/cssom_keywords.h"
#include "third_party/blink/renderer/core/css/properties/css_property.h" #include "third_party/blink/renderer/core/css/properties/css_property.h"
#include "third_party/blink/renderer/core/css/property_registration.h"
namespace blink { namespace blink {
namespace { bool CSSOMTypes::IsCSSStyleValueLength(const CSSStyleValue& value) {
bool IsCSSStyleValueLength(const CSSStyleValue& value) {
if (!value.IsNumericValue()) if (!value.IsNumericValue())
return false; return false;
return static_cast<const CSSNumericValue&>(value).Type(). return static_cast<const CSSNumericValue&>(value).Type().
MatchesBaseType(CSSNumericValueType::BaseType::kLength); MatchesBaseType(CSSNumericValueType::BaseType::kLength);
} }
bool IsCSSStyleValueNumber(const CSSStyleValue& value) { bool CSSOMTypes::IsCSSStyleValueNumber(const CSSStyleValue& value) {
if (!value.IsNumericValue()) if (!value.IsNumericValue())
return false; return false;
return static_cast<const CSSNumericValue&>(value).Type(). return static_cast<const CSSNumericValue&>(value).Type().
MatchesNumber(); MatchesNumber();
} }
bool IsCSSStyleValueTime(const CSSStyleValue& value) { bool CSSOMTypes::IsCSSStyleValueTime(const CSSStyleValue& value) {
if (!value.IsNumericValue()) if (!value.IsNumericValue())
return false; return false;
return static_cast<const CSSNumericValue&>(value).Type(). return static_cast<const CSSNumericValue&>(value).Type().
MatchesBaseType(CSSNumericValueType::BaseType::kTime); MatchesBaseType(CSSNumericValueType::BaseType::kTime);
} }
bool IsCSSStyleValueAngle(const CSSStyleValue& value) { bool CSSOMTypes::IsCSSStyleValueAngle(const CSSStyleValue& value) {
if (!value.IsNumericValue()) if (!value.IsNumericValue())
return false; return false;
return static_cast<const CSSNumericValue&>(value).Type(). return static_cast<const CSSNumericValue&>(value).Type().
MatchesBaseType(CSSNumericValueType::BaseType::kAngle); MatchesBaseType(CSSNumericValueType::BaseType::kAngle);
} }
bool IsCSSStyleValuePercentage(const CSSStyleValue& value) { bool CSSOMTypes::IsCSSStyleValuePercentage(const CSSStyleValue& value) {
if (!value.IsNumericValue()) if (!value.IsNumericValue())
return false; return false;
return static_cast<const CSSNumericValue&>(value).Type(). return static_cast<const CSSNumericValue&>(value).Type().
MatchesPercentage(); MatchesPercentage();
} }
bool IsCSSStyleValueFlex(const CSSStyleValue& value) { bool CSSOMTypes::IsCSSStyleValueResolution(const CSSStyleValue& value) {
if (!value.IsNumericValue())
return false;
return static_cast<const CSSNumericValue&>(value).Type().
MatchesBaseType(CSSNumericValueType::BaseType::kResolution);
}
bool CSSOMTypes::IsCSSStyleValueFlex(const CSSStyleValue& value) {
if (!value.IsNumericValue()) if (!value.IsNumericValue())
return false; return false;
return static_cast<const CSSNumericValue&>(value).Type(). return static_cast<const CSSNumericValue&>(value).Type().
MatchesBaseType(CSSNumericValueType::BaseType::kFlex); MatchesBaseType(CSSNumericValueType::BaseType::kFlex);
} }
bool IsCSSStyleValueImage(const CSSStyleValue& value) { bool CSSOMTypes::IsCSSStyleValueImage(const CSSStyleValue& value) {
return value.GetType() == CSSStyleValue::kURLImageType; return value.GetType() == CSSStyleValue::kURLImageType;
} }
bool IsCSSStyleValueTransform(const CSSStyleValue& value) { bool CSSOMTypes::IsCSSStyleValueTransform(const CSSStyleValue& value) {
return value.GetType() == CSSStyleValue::kTransformType; return value.GetType() == CSSStyleValue::kTransformType;
} }
bool IsCSSStyleValuePosition(const CSSStyleValue& value) { bool CSSOMTypes::IsCSSStyleValuePosition(const CSSStyleValue& value) {
return value.GetType() == CSSStyleValue::kPositionType; return value.GetType() == CSSStyleValue::kPositionType;
} }
}
bool CSSOMTypes::IsPropertySupported(CSSPropertyID id) { bool CSSOMTypes::IsPropertySupported(CSSPropertyID id) {
switch (id) { switch (id) {
case CSSPropertyVariable: case CSSPropertyVariable:
...@@ -87,7 +91,13 @@ bool CSSOMTypes::IsPropertySupported(CSSPropertyID id) { ...@@ -87,7 +91,13 @@ bool CSSOMTypes::IsPropertySupported(CSSPropertyID id) {
} }
bool CSSOMTypes::PropertyCanTake(CSSPropertyID id, bool CSSOMTypes::PropertyCanTake(CSSPropertyID id,
const PropertyRegistration* registration,
const CSSStyleValue& value) { const CSSStyleValue& value) {
if (id == CSSPropertyVariable && registration) {
return registration->Syntax().CanTake(value);
}
if (value.GetType() == CSSStyleValue::kKeywordType) { if (value.GetType() == CSSStyleValue::kKeywordType) {
return CSSOMKeywords::ValidKeywordForProperty( return CSSOMKeywords::ValidKeywordForProperty(
id, ToCSSKeywordValue(value)); id, ToCSSKeywordValue(value));
...@@ -107,7 +117,7 @@ bool CSSOMTypes::PropertyCanTake(CSSPropertyID id, ...@@ -107,7 +117,7 @@ bool CSSOMTypes::PropertyCanTake(CSSPropertyID id,
case {{property.property_id}}: case {{property.property_id}}:
return ( return (
{% for type in property.typedom_types if type != 'Keyword' %} {% for type in property.typedom_types if type != 'Keyword' %}
{{ "|| " if not loop.first }}IsCSSStyleValue{{type}}(value) {{ "|| " if not loop.first }}CSSOMTypes::IsCSSStyleValue{{type}}(value)
{% endfor %} {% endfor %}
); );
{% endif %} {% endif %}
......
...@@ -8,6 +8,9 @@ ...@@ -8,6 +8,9 @@
#include "third_party/blink/renderer/core/css/css_uri_value.h" #include "third_party/blink/renderer/core/css/css_uri_value.h"
#include "third_party/blink/renderer/core/css/css_value_list.h" #include "third_party/blink/renderer/core/css/css_value_list.h"
#include "third_party/blink/renderer/core/css/css_variable_reference_value.h" #include "third_party/blink/renderer/core/css/css_variable_reference_value.h"
#include "third_party/blink/renderer/core/css/cssom/css_keyword_value.h"
#include "third_party/blink/renderer/core/css/cssom/css_style_value.h"
#include "third_party/blink/renderer/core/css/cssom/cssom_types.h"
#include "third_party/blink/renderer/core/css/parser/css_parser_idioms.h" #include "third_party/blink/renderer/core/css/parser/css_parser_idioms.h"
#include "third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h" #include "third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h"
#include "third_party/blink/renderer/core/css/parser/css_variable_parser.h" #include "third_party/blink/renderer/core/css/parser/css_variable_parser.h"
...@@ -223,6 +226,64 @@ const CSSValue* ConsumeSyntaxComponent(const CSSSyntaxComponent& syntax, ...@@ -223,6 +226,64 @@ const CSSValue* ConsumeSyntaxComponent(const CSSSyntaxComponent& syntax,
return result; return result;
} }
bool CSSSyntaxComponent::CanTake(const CSSStyleValue& value) const {
switch (type_) {
case CSSSyntaxType::kTokenStream:
return value.GetType() == CSSStyleValue::kUnparsedType;
case CSSSyntaxType::kIdent:
return value.GetType() == CSSStyleValue::kKeywordType &&
static_cast<const CSSKeywordValue&>(value).value() == string_;
case CSSSyntaxType::kLength:
return CSSOMTypes::IsCSSStyleValueLength(value);
case CSSSyntaxType::kInteger:
// TODO(andruud): Support rounding.
// https://drafts.css-houdini.org/css-typed-om-1/#numeric-objects
FALLTHROUGH;
case CSSSyntaxType::kNumber:
return CSSOMTypes::IsCSSStyleValueNumber(value);
case CSSSyntaxType::kPercentage:
return CSSOMTypes::IsCSSStyleValuePercentage(value);
case CSSSyntaxType::kLengthPercentage:
// TODO(andruud): Support calc(X% + Ypx).
return CSSOMTypes::IsCSSStyleValueLength(value) ||
CSSOMTypes::IsCSSStyleValuePercentage(value);
case CSSSyntaxType::kColor:
// TODO(andruud): Support custom properties in CSSUnsupportedStyleValue.
return false;
case CSSSyntaxType::kImage:
case CSSSyntaxType::kUrl:
return value.GetType() == CSSStyleValue::kURLImageType;
case CSSSyntaxType::kAngle:
return CSSOMTypes::IsCSSStyleValueAngle(value);
case CSSSyntaxType::kTime:
return CSSOMTypes::IsCSSStyleValueTime(value);
case CSSSyntaxType::kResolution:
return CSSOMTypes::IsCSSStyleValueResolution(value);
case CSSSyntaxType::kTransformFunction:
// TODO(andruud): Currently not supported by Typed OM.
// https://github.com/w3c/css-houdini-drafts/issues/290
// For now, this should accept a CSSUnsupportedStyleValue, such that
// <transform-function> values can be moved from one registered property
// to another.
// TODO(andruud): Support custom properties in CSSUnsupportedStyleValue.
return false;
case CSSSyntaxType::kTransformList:
return value.GetType() == CSSStyleValue::kTransformType;
case CSSSyntaxType::kCustomIdent:
return value.GetType() == CSSStyleValue::kKeywordType;
default:
return false;
}
}
bool CSSSyntaxDescriptor::CanTake(const CSSStyleValue& value) const {
for (const CSSSyntaxComponent& component : syntax_components_) {
if (component.CanTake(value))
return true;
}
return false;
}
const CSSValue* CSSSyntaxDescriptor::Parse(CSSParserTokenRange range, const CSSValue* CSSSyntaxDescriptor::Parse(CSSParserTokenRange range,
const CSSParserContext* context, const CSSParserContext* context,
bool is_animation_tainted) const { bool is_animation_tainted) const {
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
namespace blink { namespace blink {
class CSSParserContext; class CSSParserContext;
class CSSStyleValue;
class CSSValue; class CSSValue;
enum class CSSSyntaxType { enum class CSSSyntaxType {
...@@ -45,6 +46,8 @@ struct CSSSyntaxComponent { ...@@ -45,6 +46,8 @@ struct CSSSyntaxComponent {
bool IsRepeatable() const { return repeat_ != CSSSyntaxRepeat::kNone; } bool IsRepeatable() const { return repeat_ != CSSSyntaxRepeat::kNone; }
bool CanTake(const CSSStyleValue&) const;
CSSSyntaxType type_; CSSSyntaxType type_;
String string_; // Only used when type_ is CSSSyntaxType::kIdent String string_; // Only used when type_ is CSSSyntaxType::kIdent
CSSSyntaxRepeat repeat_; CSSSyntaxRepeat repeat_;
...@@ -57,6 +60,7 @@ class CORE_EXPORT CSSSyntaxDescriptor { ...@@ -57,6 +60,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;
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 {
return syntax_components_.size() == 1 && return syntax_components_.size() == 1 &&
......
...@@ -11,6 +11,8 @@ ...@@ -11,6 +11,8 @@
namespace blink { namespace blink {
class PropertyRegistration;
// This class provides utility functions for determining whether a property // This class provides utility functions for determining whether a property
// can accept a CSSStyleValue type or instance. Its implementation is generated // can accept a CSSStyleValue type or instance. Its implementation is generated
// using input from CSSProperties.json5 and the script // using input from CSSProperties.json5 and the script
...@@ -19,9 +21,21 @@ class CSSOMTypes { ...@@ -19,9 +21,21 @@ class CSSOMTypes {
STATIC_ONLY(CSSOMTypes); STATIC_ONLY(CSSOMTypes);
public: public:
static bool IsCSSStyleValueLength(const CSSStyleValue&);
static bool IsCSSStyleValueNumber(const CSSStyleValue&);
static bool IsCSSStyleValueTime(const CSSStyleValue&);
static bool IsCSSStyleValueAngle(const CSSStyleValue&);
static bool IsCSSStyleValuePercentage(const CSSStyleValue&);
static bool IsCSSStyleValueResolution(const CSSStyleValue&);
static bool IsCSSStyleValueFlex(const CSSStyleValue&);
static bool IsCSSStyleValueImage(const CSSStyleValue&);
static bool IsCSSStyleValueTransform(const CSSStyleValue&);
static bool IsCSSStyleValuePosition(const CSSStyleValue&);
static bool IsPropertySupported(CSSPropertyID); static bool IsPropertySupported(CSSPropertyID);
static bool PropertyCanTake(CSSPropertyID, const CSSStyleValue&); static bool PropertyCanTake(CSSPropertyID,
static bool PropertyCanTakeType(CSSPropertyID, CSSStyleValue::StyleValueType); const PropertyRegistration*,
const CSSStyleValue&);
}; };
} // namespace blink } // namespace blink
......
...@@ -12,7 +12,9 @@ ...@@ -12,7 +12,9 @@
#include "third_party/blink/renderer/core/css/cssom/style_value_factory.h" #include "third_party/blink/renderer/core/css/cssom/style_value_factory.h"
#include "third_party/blink/renderer/core/css/parser/css_parser.h" #include "third_party/blink/renderer/core/css/parser/css_parser.h"
#include "third_party/blink/renderer/core/css/parser/css_parser_context.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/properties/css_property.h" #include "third_party/blink/renderer/core/css/properties/css_property.h"
#include "third_party/blink/renderer/core/css/property_registry.h"
#include "third_party/blink/renderer/core/style_property_shorthand.h" #include "third_party/blink/renderer/core/style_property_shorthand.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h" #include "third_party/blink/renderer/platform/bindings/exception_state.h"
...@@ -38,10 +40,11 @@ CSSValueList* CssValueListForPropertyID(CSSPropertyID property_id) { ...@@ -38,10 +40,11 @@ CSSValueList* CssValueListForPropertyID(CSSPropertyID property_id) {
const CSSValue* StyleValueToCSSValue( const CSSValue* StyleValueToCSSValue(
const CSSProperty& property, const CSSProperty& property,
const PropertyRegistration* registration,
const CSSStyleValue& style_value, const CSSStyleValue& style_value,
const ExecutionContext& execution_context) { const ExecutionContext& execution_context) {
const CSSPropertyID property_id = property.PropertyID(); const CSSPropertyID property_id = property.PropertyID();
if (!CSSOMTypes::PropertyCanTake(property_id, style_value)) if (!CSSOMTypes::PropertyCanTake(property_id, registration, style_value))
return nullptr; return nullptr;
if (style_value.GetType() == CSSStyleValue::kUnknownType) { if (style_value.GetType() == CSSStyleValue::kUnknownType) {
...@@ -54,6 +57,17 @@ const CSSValue* StyleValueToCSSValue( ...@@ -54,6 +57,17 @@ const CSSValue* StyleValueToCSSValue(
// TODO(https://crbug.com/545324): Move this into a method on // TODO(https://crbug.com/545324): Move this into a method on
// CSSProperty when there are more of these cases. // CSSProperty when there are more of these cases.
switch (property_id) { switch (property_id) {
case CSSPropertyVariable:
if (registration &&
style_value.GetType() != CSSStyleValue::kUnparsedType) {
CSSTokenizer tokenizer(style_value.toString());
CSSParserTokenRange range(tokenizer.TokenizeToEOF());
CSSParserContext* context = CSSParserContext::Create(execution_context);
scoped_refptr<CSSVariableData> variable_data = CSSVariableData::Create(
range, false, false, context->BaseURL(), context->Charset());
return CSSVariableReferenceValue::Create(variable_data, *context);
}
break;
case CSSPropertyBorderBottomLeftRadius: case CSSPropertyBorderBottomLeftRadius:
case CSSPropertyBorderBottomRightRadius: case CSSPropertyBorderBottomRightRadius:
case CSSPropertyBorderTopLeftRadius: case CSSPropertyBorderTopLeftRadius:
...@@ -179,6 +193,7 @@ const CSSValue* StyleValueToCSSValue( ...@@ -179,6 +193,7 @@ const CSSValue* StyleValueToCSSValue(
const CSSValue* CoerceStyleValueOrString( const CSSValue* CoerceStyleValueOrString(
const CSSProperty& property, const CSSProperty& property,
const PropertyRegistration* registration,
const CSSStyleValueOrString& value, const CSSStyleValueOrString& value,
const ExecutionContext& execution_context) { const ExecutionContext& execution_context) {
DCHECK(!property.IsRepeated()); DCHECK(!property.IsRepeated());
...@@ -187,8 +202,8 @@ const CSSValue* CoerceStyleValueOrString( ...@@ -187,8 +202,8 @@ const CSSValue* CoerceStyleValueOrString(
if (!value.GetAsCSSStyleValue()) if (!value.GetAsCSSStyleValue())
return nullptr; return nullptr;
return StyleValueToCSSValue(property, *value.GetAsCSSStyleValue(), return StyleValueToCSSValue(property, registration,
execution_context); *value.GetAsCSSStyleValue(), execution_context);
} else { } else {
DCHECK(value.IsString()); DCHECK(value.IsString());
const auto values = StyleValueFactory::FromString( const auto values = StyleValueFactory::FromString(
...@@ -197,7 +212,8 @@ const CSSValue* CoerceStyleValueOrString( ...@@ -197,7 +212,8 @@ const CSSValue* CoerceStyleValueOrString(
if (values.size() != 1U) if (values.size() != 1U)
return nullptr; return nullptr;
return StyleValueToCSSValue(property, *values[0], execution_context); return StyleValueToCSSValue(property, registration, *values[0],
execution_context);
} }
} }
...@@ -218,7 +234,7 @@ const CSSValue* CoerceStyleValuesOrStrings( ...@@ -218,7 +234,7 @@ const CSSValue* CoerceStyleValuesOrStrings(
return nullptr; return nullptr;
css_values.push_back(StyleValueToCSSValue( css_values.push_back(StyleValueToCSSValue(
property, *value.GetAsCSSStyleValue(), execution_context)); property, nullptr, *value.GetAsCSSStyleValue(), execution_context));
} else { } else {
DCHECK(value.IsString()); DCHECK(value.IsString());
if (!parser_context) if (!parser_context)
...@@ -231,8 +247,8 @@ const CSSValue* CoerceStyleValuesOrStrings( ...@@ -231,8 +247,8 @@ const CSSValue* CoerceStyleValuesOrStrings(
for (const auto& subvalue : subvalues) { for (const auto& subvalue : subvalues) {
DCHECK(subvalue); DCHECK(subvalue);
css_values.push_back( css_values.push_back(StyleValueToCSSValue(property, nullptr, *subvalue,
StyleValueToCSSValue(property, *subvalue, execution_context)); execution_context));
} }
} }
} }
...@@ -273,8 +289,10 @@ void StylePropertyMap::set(const ExecutionContext* execution_context, ...@@ -273,8 +289,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, *style_value)) if (style_value &&
CSSOMTypes::PropertyCanTake(property_id, nullptr, *style_value)) {
css_text = style_value->toString(); css_text = style_value->toString();
}
} else { } else {
css_text = values[0].GetAsString(); css_text = values[0].GetAsString();
} }
...@@ -287,11 +305,23 @@ void StylePropertyMap::set(const ExecutionContext* execution_context, ...@@ -287,11 +305,23 @@ void StylePropertyMap::set(const ExecutionContext* execution_context,
return; return;
} }
const PropertyRegistration* registration = nullptr;
if (property_id == CSSPropertyVariable && execution_context->IsDocument()) {
const PropertyRegistry* registry =
ToDocument(*execution_context).GetPropertyRegistry();
if (registry) {
registration = registry->Registration(AtomicString(property_name));
}
}
const CSSValue* result = nullptr; const CSSValue* result = nullptr;
if (property.IsRepeated()) if (property.IsRepeated()) {
result = CoerceStyleValuesOrStrings(property, values, *execution_context); result = CoerceStyleValuesOrStrings(property, values, *execution_context);
else if (values.size() == 1U) } else if (values.size() == 1U) {
result = CoerceStyleValueOrString(property, values[0], *execution_context); result = CoerceStyleValueOrString(property, registration, values[0],
*execution_context);
}
if (!result) { if (!result) {
exception_state.ThrowTypeError("Invalid type for property"); exception_state.ThrowTypeError("Invalid type for property");
......
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