Commit 4aa028f8 authored by Anders Hartvoll Ruud's avatar Anders Hartvoll Ruud Committed by Commit Bot

[css-properties-values-api] Reify specified values as CSSUnparsedValue

According to a recent spec change, the syntax of a registered custom
property must be ignored until computed-value time. This means that
we can unfortunately not reify specified values according to the
syntax; instead all custom properties reify as CSSUnparsedValue,
regardless of their registration status.

This means that we can remove large parts of the Typed OM code which
deals with PropertyRegistation for specified values and matching of
incoming CSSStyleValues against registered syntax.

The typedom.html test was also mostly re-written, as most of the things
it tested are now not relevant anymore, or at least no longer as
interesting as before. (The new reworked test basically verifies that
registered custom properties behave as unregistered for
attributeStyleMap and styleMap. For computedStyleMap, we should of
course still reify according to the syntax).

Bug: 641877

Change-Id: I4da7c25b8f066dd01388b6229bebe181513e9fd9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1713557
Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org>
Reviewed-by: default avatarRune Lillesveen <futhark@chromium.org>
Cr-Commit-Position: refs/heads/master@{#680595}
parent 28f009ce
...@@ -14,7 +14,6 @@ ...@@ -14,7 +14,6 @@
#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/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 {
...@@ -93,27 +92,18 @@ bool CSSOMTypes::IsPropertySupported(CSSPropertyID id) { ...@@ -93,27 +92,18 @@ 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 CSSStyleValue& value) {
const CSSStyleValue& value,
const CSSSyntaxComponent*& match) {
DCHECK_EQ(id == CSSPropertyID::kVariable, !custom_property_name.IsNull()); DCHECK_EQ(id == CSSPropertyID::kVariable, !custom_property_name.IsNull());
if (id == CSSPropertyID::kVariable && registration) {
if (auto* unsupported_style_value =
DynamicTo<CSSUnsupportedStyleValue>(value)) {
return unsupported_style_value->IsValidFor(
CSSPropertyName(custom_property_name));
}
match = registration->Syntax().Match(value);
return match != nullptr;
}
if (auto* css_keyword_value = DynamicTo<CSSKeywordValue>(value)) { if (auto* css_keyword_value = DynamicTo<CSSKeywordValue>(value)) {
return CSSOMKeywords::ValidKeywordForProperty(id, *css_keyword_value); return CSSOMKeywords::ValidKeywordForProperty(id, *css_keyword_value);
} }
if (auto* unsupported_style_value = if (auto* unsupported_style_value =
DynamicTo<CSSUnsupportedStyleValue>(value)) { DynamicTo<CSSUnsupportedStyleValue>(value)) {
return unsupported_style_value->IsValidFor(CSSPropertyName(id)); auto name = (id == CSSPropertyID::kVariable)
? CSSPropertyName(custom_property_name)
: CSSPropertyName(id);
return unsupported_style_value->IsValidFor(name);
} }
if (value.GetType() == CSSStyleValue::kUnparsedType) { if (value.GetType() == CSSStyleValue::kUnparsedType) {
return true; return true;
......
...@@ -181,7 +181,6 @@ blink_core_sources("css") { ...@@ -181,7 +181,6 @@ blink_core_sources("css") {
"css_style_sheet.h", "css_style_sheet.h",
"css_supports_rule.cc", "css_supports_rule.cc",
"css_supports_rule.h", "css_supports_rule.h",
"css_syntax_component.cc",
"css_syntax_component.h", "css_syntax_component.h",
"css_syntax_descriptor.cc", "css_syntax_descriptor.cc",
"css_syntax_descriptor.h", "css_syntax_descriptor.h",
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/css/css_syntax_component.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"
namespace blink {
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;
}
}
} // namespace blink
...@@ -12,8 +12,6 @@ ...@@ -12,8 +12,6 @@
namespace blink { namespace blink {
class CSSStyleValue;
enum class CSSSyntaxType { enum class CSSSyntaxType {
kTokenStream, kTokenStream,
kIdent, kIdent,
...@@ -58,8 +56,6 @@ class CSSSyntaxComponent { ...@@ -58,8 +56,6 @@ class CSSSyntaxComponent {
return repeat_ == CSSSyntaxRepeat::kSpaceSeparated ? ' ' : ','; return repeat_ == CSSSyntaxRepeat::kSpaceSeparated ? ' ' : ',';
} }
bool CanTake(const CSSStyleValue&) const;
private: private:
CSSSyntaxType type_; CSSSyntaxType type_;
String string_; // Only used when type_ is CSSSyntaxType::kIdent String string_; // Only used when type_ is CSSSyntaxType::kIdent
......
...@@ -127,19 +127,6 @@ const CSSValue* ConsumeSyntaxComponent(const CSSSyntaxComponent& syntax, ...@@ -127,19 +127,6 @@ const CSSValue* ConsumeSyntaxComponent(const CSSSyntaxComponent& syntax,
} // namespace } // namespace
const CSSSyntaxComponent* CSSSyntaxDescriptor::Match(
const CSSStyleValue& value) const {
for (const CSSSyntaxComponent& component : syntax_components_) {
if (component.CanTake(value))
return &component;
}
return nullptr;
}
bool CSSSyntaxDescriptor::CanTake(const CSSStyleValue& value) const {
return Match(value);
}
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 {
......
...@@ -19,8 +19,6 @@ class CORE_EXPORT CSSSyntaxDescriptor { ...@@ -19,8 +19,6 @@ 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 IsTokenStream() const { bool IsTokenStream() const {
return syntax_components_.size() == 1 && return syntax_components_.size() == 1 &&
syntax_components_[0].GetType() == CSSSyntaxType::kTokenStream; syntax_components_[0].GetType() == CSSSyntaxType::kTokenStream;
......
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
#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_context.h" #include "third_party/blink/renderer/core/css/parser/css_parser_context.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"
#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"
#include "third_party/blink/renderer/platform/heap/heap.h" #include "third_party/blink/renderer/platform/heap/heap.h"
...@@ -35,14 +34,8 @@ CSSStyleValueVector ParseCSSStyleValue( ...@@ -35,14 +34,8 @@ CSSStyleValueVector ParseCSSStyleValue(
AtomicString custom_property_name = property_id == CSSPropertyID::kVariable AtomicString custom_property_name = property_id == CSSPropertyID::kVariable
? AtomicString(property_name) ? AtomicString(property_name)
: g_null_atom; : g_null_atom;
const PropertyRegistration* registration =
(property_id == CSSPropertyID::kVariable)
? PropertyRegistration::From(execution_context, custom_property_name)
: nullptr;
const auto style_values = StyleValueFactory::FromString( const auto style_values = StyleValueFactory::FromString(
property_id, custom_property_name, registration, value, property_id, custom_property_name, value,
MakeGarbageCollected<CSSParserContext>(*execution_context)); MakeGarbageCollected<CSSParserContext>(*execution_context));
if (style_values.IsEmpty()) { if (style_values.IsEmpty()) {
exception_state.ThrowTypeError("The value provided ('" + value + exception_state.ThrowTypeError("The value provided ('" + value +
......
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
namespace blink { namespace blink {
class CSSSyntaxComponent;
class ExceptionState; class ExceptionState;
class ExecutionContext; class ExecutionContext;
enum class SecureContextMode; enum class SecureContextMode;
...@@ -66,9 +65,7 @@ class CORE_EXPORT CSSStyleValue : public ScriptWrappable { ...@@ -66,9 +65,7 @@ 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( virtual const CSSValue* ToCSSValueWithProperty(CSSPropertyID) const {
CSSPropertyID,
const CSSSyntaxComponent*) const {
return ToCSSValue(); return ToCSSValue();
} }
virtual String toString() const; virtual String toString() const;
......
...@@ -40,8 +40,7 @@ CSSPrimitiveValue::UnitType ToCanonicalUnitIfPossible( ...@@ -40,8 +40,7 @@ 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)) ==
...@@ -51,10 +50,6 @@ bool IsValueOutOfRangeForProperty(CSSPropertyID property_id, ...@@ -51,10 +50,6 @@ 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 CSSPropertyID::kVariable:
if (match && match->IsInteger())
return round(value) != value;
return false;
case CSSPropertyID::kOrder: case CSSPropertyID::kOrder:
case CSSPropertyID::kZIndex: case CSSPropertyID::kZIndex:
return round(value) != value; return round(value) != value;
...@@ -175,9 +170,8 @@ const CSSNumericLiteralValue* CSSUnitValue::ToCSSValue() const { ...@@ -175,9 +170,8 @@ const CSSNumericLiteralValue* CSSUnitValue::ToCSSValue() const {
} }
const CSSPrimitiveValue* CSSUnitValue::ToCSSValueWithProperty( const CSSPrimitiveValue* CSSUnitValue::ToCSSValueWithProperty(
CSSPropertyID property_id, CSSPropertyID property_id) const {
const CSSSyntaxComponent* match) const { if (IsValueOutOfRangeForProperty(property_id, value_, unit_)) {
if (IsValueOutOfRangeForProperty(property_id, value_, unit_, match)) {
// Wrap out of range values with a calc. // Wrap out of range values with a calc.
CSSMathExpressionNode* node = ToCalcExpressionNode(); CSSMathExpressionNode* node = ToCalcExpressionNode();
node->SetIsNestedCalc(); node->SetIsNestedCalc();
......
...@@ -52,9 +52,7 @@ class CORE_EXPORT CSSUnitValue final : public CSSNumericValue { ...@@ -52,9 +52,7 @@ class CORE_EXPORT CSSUnitValue final : public CSSNumericValue {
// From CSSStyleValue. // From CSSStyleValue.
StyleValueType GetType() const final; StyleValueType GetType() const final;
const CSSNumericLiteralValue* ToCSSValue() const final; const CSSNumericLiteralValue* ToCSSValue() const final;
const CSSPrimitiveValue* ToCSSValueWithProperty( const CSSPrimitiveValue* ToCSSValueWithProperty(CSSPropertyID) const final;
CSSPropertyID,
const CSSSyntaxComponent*) const final;
CSSMathExpressionNode* ToCalcExpressionNode() const final; CSSMathExpressionNode* ToCalcExpressionNode() const final;
private: private:
......
...@@ -11,8 +11,6 @@ ...@@ -11,8 +11,6 @@
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 css_properties.json5 and the script // using input from css_properties.json5 and the script
...@@ -33,15 +31,9 @@ class CSSOMTypes { ...@@ -33,15 +31,9 @@ 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 CSSStyleValue&);
const CSSStyleValue&,
const CSSSyntaxComponent*& match);
}; };
} // namespace blink } // namespace blink
......
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
#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/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"
#include "third_party/blink/renderer/platform/heap/heap.h" #include "third_party/blink/renderer/platform/heap/heap.h"
...@@ -24,21 +23,6 @@ namespace blink { ...@@ -24,21 +23,6 @@ namespace blink {
namespace { namespace {
bool IsListValuedProperty(const CSSProperty& property,
const PropertyRegistration* registration) {
if (property.IsRepeated())
return true;
// TODO(andruud): The concept of "list-valued properties" doesn't fully work
// in all cases. See https://github.com/w3c/css-houdini-drafts/issues/823
// For now we only consider a custom property list-valued if it has a single
// syntax component that is repeatable (e.g. <length>+).
if (property.IDEquals(CSSPropertyID::kVariable) && registration) {
const auto& components = registration->Syntax().Components();
return components.size() == 1 && components[0].IsRepeatable();
}
return false;
}
CSSValueList* CssValueListForPropertyID(CSSPropertyID property_id) { CSSValueList* CssValueListForPropertyID(CSSPropertyID property_id) {
DCHECK(CSSProperty::Get(property_id).IsRepeated()); DCHECK(CSSProperty::Get(property_id).IsRepeated());
char separator = CSSProperty::Get(property_id).RepetitionSeparator(); char separator = CSSProperty::Get(property_id).RepetitionSeparator();
...@@ -55,79 +39,21 @@ CSSValueList* CssValueListForPropertyID(CSSPropertyID property_id) { ...@@ -55,79 +39,21 @@ 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 String& value,
const CSSParserContext& context) {
CSSTokenizer tokenizer(value);
const auto tokens = tokenizer.TokenizeToEOF();
CSSParserTokenRange range(tokens);
scoped_refptr<CSSVariableData> variable_data = CSSVariableData::Create(
range, false, false, context.BaseURL(), context.Charset());
return MakeGarbageCollected<CSSVariableReferenceValue>(variable_data,
context);
}
const CSSVariableReferenceValue* CreateVariableReferenceValue(
const CSSProperty& property,
const AtomicString& custom_property_name,
const PropertyRegistration& registration,
const CSSStyleValueVector& values,
const CSSParserContext& context) {
DCHECK(IsListValuedProperty(property, &registration));
DCHECK_EQ(registration.Syntax().Components().size(), 1U);
char separator = registration.Syntax().Components()[0].Separator();
StringBuilder builder;
for (const auto& value : values) {
const CSSSyntaxComponent* syntax_component = nullptr;
if (!CSSOMTypes::PropertyCanTake(property.PropertyID(),
custom_property_name, &registration,
*value, syntax_component)) {
return nullptr;
}
if (!builder.IsEmpty())
builder.Append(separator);
builder.Append(StyleValueToString(property, *value, syntax_component));
}
return CreateVariableReferenceValue(builder.ToString(), context);
}
const CSSValue* StyleValueToCSSValue( const CSSValue* StyleValueToCSSValue(
const CSSProperty& property, const CSSProperty& property,
const AtomicString& custom_property_name, const AtomicString& custom_property_name,
const PropertyRegistration* registration,
const CSSStyleValue& style_value, const CSSStyleValue& style_value,
const ExecutionContext& execution_context) { const ExecutionContext& execution_context) {
DCHECK_EQ(property.IDEquals(CSSPropertyID::kVariable), DCHECK_EQ(property.IDEquals(CSSPropertyID::kVariable),
!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, style_value)) {
syntax_component)) {
return nullptr; return nullptr;
} }
if (style_value.GetType() == CSSStyleValue::kUnknownType && if (style_value.GetType() == CSSStyleValue::kUnknownType) {
// Registered custom properties must enter the CSSPropertyID::kVariable
// switch-case below, for proper parsing according to registered syntax.
!(property_id == CSSPropertyID::kVariable && registration)) {
return CSSParser::ParseSingleValue( return CSSParser::ParseSingleValue(
property.PropertyID(), style_value.toString(), property.PropertyID(), style_value.toString(),
MakeGarbageCollected<CSSParserContext>(execution_context)); MakeGarbageCollected<CSSParserContext>(execution_context));
...@@ -137,16 +63,6 @@ const CSSValue* StyleValueToCSSValue( ...@@ -137,16 +63,6 @@ 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 CSSPropertyID::kVariable:
if (registration &&
style_value.GetType() != CSSStyleValue::kUnparsedType) {
auto* context =
MakeGarbageCollected<CSSParserContext>(execution_context);
String string =
StyleValueToString(property, style_value, syntax_component);
return CreateVariableReferenceValue(string, *context);
}
break;
case CSSPropertyID::kBorderBottomLeftRadius: case CSSPropertyID::kBorderBottomLeftRadius:
case CSSPropertyID::kBorderBottomRightRadius: case CSSPropertyID::kBorderBottomRightRadius:
case CSSPropertyID::kBorderTopLeftRadius: case CSSPropertyID::kBorderTopLeftRadius:
...@@ -269,16 +185,15 @@ const CSSValue* StyleValueToCSSValue( ...@@ -269,16 +185,15 @@ const CSSValue* StyleValueToCSSValue(
break; break;
} }
return style_value.ToCSSValueWithProperty(property_id, syntax_component); return style_value.ToCSSValueWithProperty(property_id);
} }
const CSSValue* CoerceStyleValueOrString( const CSSValue* CoerceStyleValueOrString(
const CSSProperty& property, const CSSProperty& property,
const AtomicString& custom_property_name, const AtomicString& custom_property_name,
const PropertyRegistration* registration,
const CSSStyleValueOrString& value, const CSSStyleValueOrString& value,
const ExecutionContext& execution_context) { const ExecutionContext& execution_context) {
DCHECK(!IsListValuedProperty(property, registration)); DCHECK(!property.IsRepeated());
DCHECK_EQ(property.IDEquals(CSSPropertyID::kVariable), DCHECK_EQ(property.IDEquals(CSSPropertyID::kVariable),
!custom_property_name.IsNull()); !custom_property_name.IsNull());
...@@ -286,29 +201,27 @@ const CSSValue* CoerceStyleValueOrString( ...@@ -286,29 +201,27 @@ const CSSValue* CoerceStyleValueOrString(
if (!value.GetAsCSSStyleValue()) if (!value.GetAsCSSStyleValue())
return nullptr; return nullptr;
return StyleValueToCSSValue(property, custom_property_name, registration, return StyleValueToCSSValue(property, custom_property_name,
*value.GetAsCSSStyleValue(), execution_context); *value.GetAsCSSStyleValue(), execution_context);
} else { } else {
DCHECK(value.IsString()); DCHECK(value.IsString());
const auto values = StyleValueFactory::FromString( const auto values = StyleValueFactory::FromString(
property.PropertyID(), custom_property_name, registration, property.PropertyID(), custom_property_name, value.GetAsString(),
value.GetAsString(),
MakeGarbageCollected<CSSParserContext>(execution_context)); MakeGarbageCollected<CSSParserContext>(execution_context));
if (values.size() != 1U) if (values.size() != 1U)
return nullptr; return nullptr;
return StyleValueToCSSValue(property, custom_property_name, registration, return StyleValueToCSSValue(property, custom_property_name, *values[0],
*values[0], execution_context); execution_context);
} }
} }
const CSSValue* CoerceStyleValuesOrStrings( const CSSValue* CoerceStyleValuesOrStrings(
const CSSProperty& property, const CSSProperty& property,
const AtomicString& custom_property_name, const AtomicString& custom_property_name,
const PropertyRegistration* registration,
const HeapVector<CSSStyleValueOrString>& values, const HeapVector<CSSStyleValueOrString>& values,
const ExecutionContext& execution_context) { const ExecutionContext& execution_context) {
DCHECK(IsListValuedProperty(property, registration)); DCHECK(property.IsRepeated());
DCHECK_EQ(property.IDEquals(CSSPropertyID::kVariable), DCHECK_EQ(property.IDEquals(CSSPropertyID::kVariable),
!custom_property_name.IsNull()); !custom_property_name.IsNull());
if (values.IsEmpty()) if (values.IsEmpty())
...@@ -316,23 +229,15 @@ const CSSValue* CoerceStyleValuesOrStrings( ...@@ -316,23 +229,15 @@ const CSSValue* CoerceStyleValuesOrStrings(
CSSStyleValueVector style_values = CSSStyleValueVector style_values =
StyleValueFactory::CoerceStyleValuesOrStrings( StyleValueFactory::CoerceStyleValuesOrStrings(
property, custom_property_name, registration, values, property, custom_property_name, values, execution_context);
execution_context);
if (style_values.IsEmpty()) if (style_values.IsEmpty())
return nullptr; return nullptr;
if (property.IDEquals(CSSPropertyID::kVariable) && registration) {
auto* context = MakeGarbageCollected<CSSParserContext>(execution_context);
return CreateVariableReferenceValue(property, custom_property_name,
*registration, style_values, *context);
}
CSSValueList* result = CssValueListForPropertyID(property.PropertyID()); CSSValueList* result = CssValueListForPropertyID(property.PropertyID());
for (const auto& style_value : style_values) { for (const auto& style_value : style_values) {
const CSSValue* css_value = const CSSValue* css_value = StyleValueToCSSValue(
StyleValueToCSSValue(property, custom_property_name, nullptr, property, custom_property_name, *style_value, execution_context);
*style_value, execution_context);
if (!css_value) if (!css_value)
return nullptr; return nullptr;
if (css_value->IsCSSWideKeyword() || css_value->IsVariableReferenceValue()) if (css_value->IsCSSWideKeyword() || css_value->IsVariableReferenceValue())
...@@ -366,10 +271,8 @@ void StylePropertyMap::set(const ExecutionContext* execution_context, ...@@ -366,10 +271,8 @@ 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();
const CSSSyntaxComponent* syntax_component = nullptr;
if (style_value && if (style_value &&
CSSOMTypes::PropertyCanTake(property_id, g_null_atom, nullptr, CSSOMTypes::PropertyCanTake(property_id, g_null_atom, *style_value)) {
*style_value, syntax_component)) {
css_text = style_value->toString(); css_text = style_value->toString();
} }
} else { } else {
...@@ -388,26 +291,13 @@ void StylePropertyMap::set(const ExecutionContext* execution_context, ...@@ -388,26 +291,13 @@ void StylePropertyMap::set(const ExecutionContext* execution_context,
? AtomicString(property_name) ? AtomicString(property_name)
: g_null_atom; : g_null_atom;
const PropertyRegistration* registration = nullptr;
if (property_id == CSSPropertyID::kVariable &&
IsA<Document>(execution_context)) {
const PropertyRegistry* registry =
To<Document>(*execution_context).GetPropertyRegistry();
if (registry) {
registration = registry->Registration(custom_property_name);
}
}
const CSSValue* result = nullptr; const CSSValue* result = nullptr;
if (IsListValuedProperty(property, registration)) { if (property.IsRepeated()) {
result = result = CoerceStyleValuesOrStrings(property, custom_property_name, values,
CoerceStyleValuesOrStrings(property, custom_property_name, registration, *execution_context);
values, *execution_context);
} else if (values.size() == 1U) { } else if (values.size() == 1U) {
result = result = CoerceStyleValueOrString(property, custom_property_name, values[0],
CoerceStyleValueOrString(property, custom_property_name, registration, *execution_context);
values[0], *execution_context);
} }
if (!result) { if (!result) {
...@@ -438,47 +328,6 @@ void StylePropertyMap::append(const ExecutionContext* execution_context, ...@@ -438,47 +328,6 @@ void StylePropertyMap::append(const ExecutionContext* execution_context,
const CSSProperty& property = CSSProperty::Get(property_id); const CSSProperty& property = CSSProperty::Get(property_id);
if (property_id == CSSPropertyID::kVariable) { if (property_id == CSSPropertyID::kVariable) {
AtomicString custom_property_name(property_name);
const PropertyRegistration* registration =
PropertyRegistration::From(execution_context, custom_property_name);
if (registration && IsListValuedProperty(property, registration)) {
CSSStyleValueVector style_values;
// Add existing CSSStyleValues:
if (const CSSValue* css_value =
GetCustomProperty(*execution_context, custom_property_name)) {
DCHECK(css_value->IsValueList());
style_values = StyleValueFactory::CssValueToStyleValueVector(
CSSPropertyName(custom_property_name), *css_value);
}
// Append incoming CSSStyleValues:
CSSStyleValueVector incoming_style_values =
StyleValueFactory::CoerceStyleValuesOrStrings(
property, custom_property_name, registration, values,
*execution_context);
const CSSValue* result = nullptr;
if (!incoming_style_values.IsEmpty()) {
style_values.AppendVector(incoming_style_values);
auto* context =
MakeGarbageCollected<CSSParserContext>(*execution_context);
result =
CreateVariableReferenceValue(property, custom_property_name,
*registration, style_values, *context);
}
if (!result) {
exception_state.ThrowTypeError("Invalid type for property");
return;
}
SetCustomProperty(custom_property_name, *result);
return;
}
exception_state.ThrowTypeError( exception_state.ThrowTypeError(
"Appending to custom properties is not supported"); "Appending to custom properties is not supported");
return; return;
...@@ -497,7 +346,7 @@ void StylePropertyMap::append(const ExecutionContext* execution_context, ...@@ -497,7 +346,7 @@ void StylePropertyMap::append(const ExecutionContext* execution_context,
} }
const CSSValue* result = CoerceStyleValuesOrStrings( const CSSValue* result = CoerceStyleValuesOrStrings(
property, g_null_atom, nullptr, values, *execution_context); property, g_null_atom, values, *execution_context);
const auto* result_value_list = DynamicTo<CSSValueList>(result); const auto* result_value_list = DynamicTo<CSSValueList>(result);
if (!result_value_list) { if (!result_value_list) {
exception_state.ThrowTypeError("Invalid type for property"); exception_state.ThrowTypeError("Invalid type for property");
......
...@@ -73,9 +73,8 @@ CSSStyleValue* StylePropertyMapReadOnlyMainThread::get( ...@@ -73,9 +73,8 @@ CSSStyleValue* StylePropertyMapReadOnlyMainThread::get(
if (property.IsShorthand()) if (property.IsShorthand())
return GetShorthandProperty(property); return GetShorthandProperty(property);
const CSSValue* value = const CSSValue* value = (name->IsCustomProperty())
(name->IsCustomProperty()) ? GetCustomProperty(name->ToAtomicString())
? GetCustomProperty(*execution_context, name->ToAtomicString())
: GetProperty(name->Id()); : GetProperty(name->Id());
if (!value) if (!value)
return nullptr; return nullptr;
...@@ -110,9 +109,8 @@ CSSStyleValueVector StylePropertyMapReadOnlyMainThread::getAll( ...@@ -110,9 +109,8 @@ CSSStyleValueVector StylePropertyMapReadOnlyMainThread::getAll(
return values; return values;
} }
const CSSValue* value = const CSSValue* value = (name->IsCustomProperty())
(name->IsCustomProperty()) ? GetCustomProperty(name->ToAtomicString())
? GetCustomProperty(*execution_context, name->ToAtomicString())
: GetProperty(name->Id()); : GetProperty(name->Id());
if (!value) if (!value)
return CSSStyleValueVector(); return CSSStyleValueVector();
...@@ -127,41 +125,14 @@ bool StylePropertyMapReadOnlyMainThread::has( ...@@ -127,41 +125,14 @@ bool StylePropertyMapReadOnlyMainThread::has(
return !getAll(execution_context, property_name, exception_state).IsEmpty(); return !getAll(execution_context, property_name, exception_state).IsEmpty();
} }
const CSSValue* StylePropertyMapReadOnlyMainThread::GetCustomProperty(
const ExecutionContext& execution_context,
const AtomicString& property_name) const {
const CSSValue* value = GetCustomProperty(property_name);
const auto* document = DynamicTo<Document>(execution_context);
if (!document)
return value;
return PropertyRegistry::ParseIfRegistered(*document, property_name, value);
}
StylePropertyMapReadOnlyMainThread::IterationSource* StylePropertyMapReadOnlyMainThread::IterationSource*
StylePropertyMapReadOnlyMainThread::StartIteration(ScriptState* script_state, StylePropertyMapReadOnlyMainThread::StartIteration(ScriptState* script_state,
ExceptionState&) { ExceptionState&) {
HeapVector<StylePropertyMapReadOnlyMainThread::StylePropertyMapEntry> result; HeapVector<StylePropertyMapReadOnlyMainThread::StylePropertyMapEntry> result;
const ExecutionContext& execution_context = ForEachProperty([&result](const CSSPropertyName& name,
*ExecutionContext::From(script_state); const CSSValue& value) {
auto values = StyleValueFactory::CssValueToStyleValueVector(name, value);
ForEachProperty([&result, &execution_context](const CSSPropertyName& name,
const CSSValue& css_value) {
const CSSValue* value = &css_value;
// TODO(andruud): Refactor this. ForEachProperty should yield the correct,
// already-parsed value in the first place.
if (name.IsCustomProperty()) {
const auto* document = DynamicTo<Document>(execution_context);
if (document) {
value = PropertyRegistry::ParseIfRegistered(
*document, name.ToAtomicString(), value);
}
}
auto values = StyleValueFactory::CssValueToStyleValueVector(name, *value);
result.emplace_back(name.ToAtomicString(), std::move(values)); result.emplace_back(name.ToAtomicString(), std::move(values));
}); });
......
...@@ -43,9 +43,6 @@ class CORE_EXPORT StylePropertyMapReadOnlyMainThread ...@@ -43,9 +43,6 @@ class CORE_EXPORT StylePropertyMapReadOnlyMainThread
virtual String SerializationForShorthand(const CSSProperty&) const = 0; virtual String SerializationForShorthand(const CSSProperty&) const = 0;
const CSSValue* GetCustomProperty(const ExecutionContext&,
const AtomicString&) const;
private: private:
IterationSource* StartIteration(ScriptState*, ExceptionState&) override; IterationSource* StartIteration(ScriptState*, ExceptionState&) override;
......
...@@ -26,7 +26,6 @@ ...@@ -26,7 +26,6 @@
#include "third_party/blink/renderer/core/css/parser/css_tokenizer.h" #include "third_party/blink/renderer/core/css/parser/css_tokenizer.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"
#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"
#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/heap/heap.h" #include "third_party/blink/renderer/platform/heap/heap.h"
...@@ -244,7 +243,6 @@ CSSStyleValueVector UnsupportedCSSValue(const CSSPropertyName& name, ...@@ -244,7 +243,6 @@ CSSStyleValueVector UnsupportedCSSValue(const CSSPropertyName& name,
CSSStyleValueVector StyleValueFactory::FromString( CSSStyleValueVector StyleValueFactory::FromString(
CSSPropertyID property_id, CSSPropertyID property_id,
const AtomicString& custom_property_name, const AtomicString& custom_property_name,
const PropertyRegistration* registration,
const String& css_text, const String& css_text,
const CSSParserContext* parser_context) { const CSSParserContext* parser_context) {
DCHECK_NE(property_id, CSSPropertyID::kInvalid); DCHECK_NE(property_id, CSSPropertyID::kInvalid);
...@@ -277,17 +275,6 @@ CSSStyleValueVector StyleValueFactory::FromString( ...@@ -277,17 +275,6 @@ CSSStyleValueVector StyleValueFactory::FromString(
return result; return result;
} }
if (property_id == CSSPropertyID::kVariable && registration) {
const bool is_animation_tainted = false;
const CSSValue* value = registration->Syntax().Parse(tokens, parser_context,
is_animation_tainted);
if (!value)
return CSSStyleValueVector();
return StyleValueFactory::CssValueToStyleValueVector(
CSSPropertyName(custom_property_name), *value);
}
if ((property_id == CSSPropertyID::kVariable && !tokens.IsEmpty()) || if ((property_id == CSSPropertyID::kVariable && !tokens.IsEmpty()) ||
CSSVariableParser::ContainsValidVariableReferences(range)) { CSSVariableParser::ContainsValidVariableReferences(range)) {
const auto variable_data = CSSVariableData::Create( const auto variable_data = CSSVariableData::Create(
...@@ -316,7 +303,6 @@ CSSStyleValue* StyleValueFactory::CssValueToStyleValue( ...@@ -316,7 +303,6 @@ CSSStyleValue* StyleValueFactory::CssValueToStyleValue(
CSSStyleValueVector StyleValueFactory::CoerceStyleValuesOrStrings( CSSStyleValueVector StyleValueFactory::CoerceStyleValuesOrStrings(
const CSSProperty& property, const CSSProperty& property,
const AtomicString& custom_property_name, const AtomicString& custom_property_name,
const PropertyRegistration* registration,
const HeapVector<CSSStyleValueOrString>& values, const HeapVector<CSSStyleValueOrString>& values,
const ExecutionContext& execution_context) { const ExecutionContext& execution_context) {
const CSSParserContext* parser_context = nullptr; const CSSParserContext* parser_context = nullptr;
...@@ -335,8 +321,8 @@ CSSStyleValueVector StyleValueFactory::CoerceStyleValuesOrStrings( ...@@ -335,8 +321,8 @@ CSSStyleValueVector StyleValueFactory::CoerceStyleValuesOrStrings(
} }
const auto subvalues = StyleValueFactory::FromString( const auto subvalues = StyleValueFactory::FromString(
property.PropertyID(), custom_property_name, registration, property.PropertyID(), custom_property_name, value.GetAsString(),
value.GetAsString(), parser_context); parser_context);
if (subvalues.IsEmpty()) if (subvalues.IsEmpty())
return CSSStyleValueVector(); return CSSStyleValueVector();
......
...@@ -17,7 +17,6 @@ class CSSProperty; ...@@ -17,7 +17,6 @@ class CSSProperty;
class CSSPropertyName; class CSSPropertyName;
class CSSValue; class CSSValue;
class ExecutionContext; class ExecutionContext;
class PropertyRegistration;
class CORE_EXPORT StyleValueFactory { class CORE_EXPORT StyleValueFactory {
STATIC_ONLY(StyleValueFactory); STATIC_ONLY(StyleValueFactory);
...@@ -26,7 +25,6 @@ class CORE_EXPORT StyleValueFactory { ...@@ -26,7 +25,6 @@ class CORE_EXPORT StyleValueFactory {
static CSSStyleValueVector FromString( static CSSStyleValueVector FromString(
CSSPropertyID, CSSPropertyID,
const AtomicString& custom_property_name, const AtomicString& custom_property_name,
const PropertyRegistration*,
const String&, const String&,
const CSSParserContext*); const CSSParserContext*);
static CSSStyleValue* CssValueToStyleValue(const CSSPropertyName&, static CSSStyleValue* CssValueToStyleValue(const CSSPropertyName&,
...@@ -37,7 +35,6 @@ class CORE_EXPORT StyleValueFactory { ...@@ -37,7 +35,6 @@ class CORE_EXPORT StyleValueFactory {
static CSSStyleValueVector CoerceStyleValuesOrStrings( static CSSStyleValueVector CoerceStyleValuesOrStrings(
const CSSProperty& property, const CSSProperty& property,
const AtomicString& custom_property_name, const AtomicString& custom_property_name,
const PropertyRegistration*,
const HeapVector<CSSStyleValueOrString>& values, const HeapVector<CSSStyleValueOrString>& values,
const ExecutionContext&); const ExecutionContext&);
// Reify a CSSStyleValue without the context of a CSS property. For most // Reify a CSSStyleValue without the context of a CSS property. For most
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
// found in the LICENSE file. // found in the LICENSE file.
#include "third_party/blink/renderer/core/css/property_registry.h" #include "third_party/blink/renderer/core/css/property_registry.h"
#include "third_party/blink/renderer/core/css/css_custom_property_declaration.h"
namespace blink { namespace blink {
...@@ -28,37 +27,6 @@ PropertyRegistry::RegistrationMap::const_iterator PropertyRegistry::end() ...@@ -28,37 +27,6 @@ PropertyRegistry::RegistrationMap::const_iterator PropertyRegistry::end()
return registrations_.end(); return registrations_.end();
} }
const CSSValue* PropertyRegistry::ParseIfRegistered(
const Document& document,
const AtomicString& property_name,
const CSSValue* value) {
auto* custom_property_declaration =
DynamicTo<CSSCustomPropertyDeclaration>(value);
if (!custom_property_declaration)
return value;
const PropertyRegistry* registry = document.GetPropertyRegistry();
if (!registry)
return value;
const PropertyRegistration* registration =
registry->Registration(property_name);
if (!registration)
return value;
CSSVariableData* tokens = custom_property_declaration->Value();
if (!tokens || tokens->NeedsVariableResolution())
return value;
const CSSValue* parsed_value = tokens->ParseForSyntax(
registration->Syntax(), document.GetSecureContextMode());
return parsed_value ? parsed_value : value;
}
void PropertyRegistry::MarkReferenced(const AtomicString& property_name) const { void PropertyRegistry::MarkReferenced(const AtomicString& property_name) const {
const PropertyRegistration* registration = Registration(property_name); const PropertyRegistration* registration = Registration(property_name);
if (registration) { if (registration) {
......
...@@ -25,15 +25,6 @@ class CORE_EXPORT PropertyRegistry : public GarbageCollected<PropertyRegistry> { ...@@ -25,15 +25,6 @@ class CORE_EXPORT PropertyRegistry : public GarbageCollected<PropertyRegistry> {
void Trace(blink::Visitor* visitor) { visitor->Trace(registrations_); } void Trace(blink::Visitor* visitor) { visitor->Trace(registrations_); }
// Parse the incoming value and return the parsed result, if:
// 1. A registration with the specified name exists, and
// 2. The incoming value is a CSSCustomPropertyDeclaration, has no
// unresolved var-references and matches the registered syntax.
// Otherwise the incoming value is returned.
static const CSSValue* ParseIfRegistered(const Document& document,
const AtomicString& property_name,
const CSSValue*);
void MarkReferenced(const AtomicString&) const; void MarkReferenced(const AtomicString&) const;
bool WasReferenced(const AtomicString&) const; bool WasReferenced(const AtomicString&) const;
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
<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>
<script src="./resources/utils.js"></script>
<style id=style> <style id=style>
div {} div {}
</style> </style>
...@@ -10,28 +11,6 @@ ...@@ -10,28 +11,6 @@
<script> <script>
// Properties are generated on demand, as `--prop-${g_counter}`.
let g_counter = 1;
// Generate a new property name.
function gen_name() {
let name = `--prop-${g_counter}`;
g_counter++;
return name;
}
// Generate a property and return its name.
function gen_prop(syntax, initialValue) {
let name = gen_name();
CSS.registerProperty({
name: name,
syntax: syntax,
initialValue: initialValue,
inherits: false
});
return name;
}
// Cleans style rules used for testing between every test. // Cleans style rules used for testing between every test.
add_result_callback(function(){ add_result_callback(function(){
target.attributeStyleMap.clear(); target.attributeStyleMap.clear();
...@@ -39,16 +18,78 @@ add_result_callback(function(){ ...@@ -39,16 +18,78 @@ add_result_callback(function(){
style.sheet.rules[0].styleMap.clear(); style.sheet.rules[0].styleMap.clear();
}); });
// In the following utility functions, the 'map' parameter (if present)
// can be any StylePropertyMap. (Not StylePropertyMapReadOnly).
// Verifies that get()/getAll() reifies the specified property to a
// CSSUnparsedValue, with a string serialization equal to 'value'.
function verify_map_get_unparsed(map, name, value) {
map.set(name, value);
let specifiedValue = map.get(name);
assert_true(specifiedValue instanceof CSSUnparsedValue);
assert_equals(specifiedValue.toString(), value);
let allSpecifiedValues = map.getAll(name);
assert_equals(allSpecifiedValues.length, 1);
assert_true(allSpecifiedValues[0] instanceof CSSUnparsedValue);
assert_equals(allSpecifiedValues[0].toString(), value);
}
// Verifies that the specified value is accepted by set().
function verify_map_set(map, name, value) {
map.set(name, value);
assert_equals(map.get(name).toString(), value.toString());
}
// Verifies that the specified value is NOT accepted by set().
function verify_map_not_set(map, name, value) {
assert_throws(new TypeError(), () => {
map.set(name, value);
});
}
// Verifies that the specified value is NOT accepted by append().
function verify_map_no_append(map, name, value) {
assert_throws(new TypeError(), () => {
map.append(name, value);
});
}
// Verifies that the property 'name' shows up on iteration, that it's reified
// as a CSSUnparsedValue, and that the string representation is equal to
// 'value'.
function verify_map_iteration_unparsed(map, name, value) {
map.set(name, value);
let result = Array.from(map).filter(e => e[0] == name)[0];
assert_equals(result.length, 2);
let iter_value = result[1];
assert_equals(iter_value.length, 1);
assert_true(iter_value[0] instanceof CSSUnparsedValue);
assert_equals(iter_value[0].toString(), value);
}
// Verifies that CSSStyleValue.parse/parseAll results in a CSSStyleValue with
// the 'expected' type.
function verify_parsed_type(prop, value, expected) {
let parse_value = CSSStyleValue.parse(prop, value);
let parse_all_value = CSSStyleValue.parseAll(prop, value);
assert_true(parse_value instanceof expected);
assert_true(parse_all_value.every(x => x instanceof expected))
}
// On the target element, verify that computed value of 'name' is an instance // On the target element, verify that computed value of 'name' is an instance
// of 'expected' and not an instance of CSSUnparsedValue. // of 'expected' and not an instance of CSSUnparsedValue.
// //
// If 'value' is non-null, that value is first set on the attributeStyleMap // If 'value' is non-null, that value is first set using the style attribute
// of the target. // of the target element.
function assert_computed_type(name, value, expected) { function verify_computed_type(name, value, expected) {
if (expected == CSSUnparsedValue) { if (expected == CSSUnparsedValue) {
throw 'CSSUnparsedValue may not be used as expected type'; throw 'CSSUnparsedValue may not be used as expected type';
} }
try {
if (value != null) { if (value != null) {
target.style = `${name}: ${value}`; target.style = `${name}: ${value}`;
} }
...@@ -57,36 +98,43 @@ function assert_computed_type(name, value, expected) { ...@@ -57,36 +98,43 @@ function assert_computed_type(name, value, expected) {
assert_false(computedValue instanceof CSSUnparsedValue); assert_false(computedValue instanceof CSSUnparsedValue);
assert_true(computedValue instanceof expected); assert_true(computedValue instanceof expected);
} finally {
if (value != null) { if (value != null) {
target.style = ''; target.style = '';
} }
}
} }
function assert_attribute_get_type(styleDecl, propertyMap, syntax, value, expected) { // Verifies that the property 'name' shows up on iteration, that it's reified
let name = gen_name(); // to the specified type, and that the string representation is equal to 'value'.
styleDecl.setProperty(name, value); function verify_computed_iteration_type(name, value, type) {
target.attributeStyleMap.set(name, value);
assert_true(propertyMap.get(name) instanceof CSSUnparsedValue); let result = Array.from(target.computedStyleMap()).filter(e => e[0] == name)[0];
assert_equals(result.length, 2);
CSS.registerProperty({ let iter_value = result[1];
name: name, assert_equals(iter_value.length, 1);
syntax: syntax, assert_true(iter_value[0] instanceof type);
initialValue: value, assert_equals(iter_value[0].toString(), value);
inherits: false }
});
if (expected == CSSStyleValue) { // Run the same test twice: once for each StylePropertyMap.
assert_false(propertyMap.get(name) instanceof CSSUnparsedValue); //
} // https://drafts.css-houdini.org/css-typed-om-1/#stylepropertymap
function test_specified_maps(func, description) {
test(function(){
func(target.attributeStyleMap)
}, description + ' [attributeStyleMap]');
assert_true(propertyMap.get(name) instanceof expected); test(function(){
let rule = style.sheet.rules[0];
func(rule.styleMap)
}, description + ' [styleMap]');
} }
// computedStyleMap // StylePropertyMapReadOnly.get
test(function(){ test(function(){
let name = gen_prop('*', 'if(){}'); let name = generate_property('*', 'if(){}');
assert_true(target.computedStyleMap().get(name) instanceof CSSUnparsedValue); assert_true(target.computedStyleMap().get(name) instanceof CSSUnparsedValue);
target.attributeStyleMap.set(name, 'as{}df'); target.attributeStyleMap.set(name, 'as{}df');
...@@ -95,92 +143,94 @@ test(function(){ ...@@ -95,92 +143,94 @@ test(function(){
}, 'Computed * is reified as CSSUnparsedValue'); }, 'Computed * is reified as CSSUnparsedValue');
test(function(){ test(function(){
assert_computed_type(gen_prop('<angle>', '42deg'), null, CSSUnitValue); verify_computed_type(generate_property('<angle>'), null, CSSUnitValue);
assert_computed_type(gen_prop('<angle> | fail', 'fail'), '42deg', CSSUnitValue); verify_computed_type(generate_property('fail | <angle> '), '42deg', CSSUnitValue);
}, 'Computed <angle> is reified as CSSUnitValue'); }, 'Computed <angle> is reified as CSSUnitValue');
test(function(){ test(function(){
assert_computed_type(gen_prop('<color>', '#fefefe'), null, CSSStyleValue); verify_computed_type(generate_property('<color>'), null, CSSStyleValue);
assert_computed_type(gen_prop('<color> | fail', 'fail'), null, CSSStyleValue); verify_computed_type(generate_property('fail | <color> '), null, CSSStyleValue);
}, 'Computed <color> is reified as CSSStyleValue'); }, 'Computed <color> is reified as CSSStyleValue');
test(function(){ test(function(){
assert_computed_type(gen_prop('<custom-ident>', 'none'), null, CSSKeywordValue); verify_computed_type(generate_property('<custom-ident>'), null, CSSKeywordValue);
assert_computed_type(gen_prop('<custom-ident> | <length>', '10px'), 'none', CSSKeywordValue); verify_computed_type(generate_property('<custom-ident> | <length>'), 'none', CSSKeywordValue);
}, 'Computed <custom-ident> is reified as CSSKeywordValue'); }, 'Computed <custom-ident> is reified as CSSKeywordValue');
test(function(){ test(function(){
assert_computed_type(gen_prop('<image>', 'url(thing.png)'), null, CSSImageValue); verify_computed_type(generate_property('<image>'), null, CSSImageValue);
assert_computed_type(gen_prop('<image> | fail', 'fail'), 'url(thing.png)', CSSImageValue); verify_computed_type(generate_property('fail | <image> '), 'url(thing.png)', CSSImageValue);
}, 'Computed <image> [url] is reified as CSSImageValue'); }, 'Computed <image> [url] is reified as CSSImageValue');
test(function(){ test(function(){
assert_computed_type(gen_prop('<integer>', '100'), null, CSSUnitValue); verify_computed_type(generate_property('<integer>'), null, CSSUnitValue);
assert_computed_type(gen_prop('<integer> | fail', 'fail'), '100', CSSUnitValue); verify_computed_type(generate_property('fail | <integer> '), '100', CSSUnitValue);
}, 'Computed <integer> is reified as CSSUnitValue'); }, 'Computed <integer> is reified as CSSUnitValue');
test(function(){ test(function(){
assert_computed_type(gen_prop('<length-percentage>', '10%'), null, CSSUnitValue); verify_computed_type(generate_property('<length-percentage>'), null, CSSUnitValue);
assert_computed_type(gen_prop('<length-percentage> | fail', 'fail'), '10%', CSSUnitValue); verify_computed_type(generate_property('fail | <length-percentage> '), '10%', CSSUnitValue);
}, 'Computed <length-percentage> [%] is reified as CSSUnitValue'); }, 'Computed <length-percentage> [%] is reified as CSSUnitValue');
test(function(){ test(function(){
assert_computed_type(gen_prop('<length-percentage>', '10px'), null, CSSUnitValue); verify_computed_type(generate_property('<length-percentage>'), null, CSSUnitValue);
assert_computed_type(gen_prop('<length-percentage> | fail', 'fail'), '10px', CSSUnitValue); verify_computed_type(generate_property('fail | <length-percentage> '), '10px', CSSUnitValue);
}, 'Computed <length-percentage> [px] is reified as CSSUnitValue'); }, 'Computed <length-percentage> [px] is reified as CSSUnitValue');
test(function(){ test(function(){
assert_computed_type(gen_prop('<length-percentage>', 'calc(10px + 10%)'), null, CSSMathSum); verify_computed_type(generate_property({syntax: '<length-percentage>', initialValue: 'calc(10% + 10px)'}), null, CSSMathSum);
assert_computed_type(gen_prop('<length-percentage> | fail', 'fail'), 'calc(10px + 10%)', CSSMathSum); verify_computed_type(generate_property('fail | <length-percentage> '), 'calc(10px + 10%)', CSSMathSum);
}, 'Computed <length-percentage> [px + %] is reified as CSSMathSum'); }, 'Computed <length-percentage> [px + %] is reified as CSSMathSum');
test(function(){ test(function(){
assert_computed_type(gen_prop('<length>', '10px'), null, CSSUnitValue); verify_computed_type(generate_property('<length>'), null, CSSUnitValue);
assert_computed_type(gen_prop('<length> | fail', 'fail'), '10px', CSSUnitValue); verify_computed_type(generate_property('fail | <length> '), '10px', CSSUnitValue);
}, 'Computed <length> is reified as CSSUnitValue'); }, 'Computed <length> is reified as CSSUnitValue');
test(function(){ test(function(){
assert_computed_type(gen_prop('<number>', '42'), null, CSSUnitValue); verify_computed_type(generate_property('<number>'), null, CSSUnitValue);
assert_computed_type(gen_prop('<number> | fail', 'fail'), '42', CSSUnitValue); verify_computed_type(generate_property('fail | <number> '), '42', CSSUnitValue);
}, 'Computed <number> is reified as CSSUnitValue'); }, 'Computed <number> is reified as CSSUnitValue');
test(function(){ test(function(){
assert_computed_type(gen_prop('<percentage>', '10%'), null, CSSUnitValue); verify_computed_type(generate_property('<percentage>'), null, CSSUnitValue);
assert_computed_type(gen_prop('<percentage> | fail', 'fail'), '10%', CSSUnitValue); verify_computed_type(generate_property('fail | <percentage> '), '10%', CSSUnitValue);
}, 'Computed <percentage> is reified as CSSUnitValue'); }, 'Computed <percentage> is reified as CSSUnitValue');
test(function(){ test(function(){
assert_computed_type(gen_prop('<resolution>', '300dpi'), null, CSSUnitValue); verify_computed_type(generate_property('<resolution>'), null, CSSUnitValue);
assert_computed_type(gen_prop('<resolution> | fail', 'fail'), '300dpi', CSSUnitValue); verify_computed_type(generate_property('fail | <resolution> '), '300dpi', CSSUnitValue);
}, 'Computed <resolution> is reified as CSSUnitValue'); }, 'Computed <resolution> is reified as CSSUnitValue');
test(function(){ test(function(){
assert_computed_type(gen_prop('<time>', '42s'), null, CSSUnitValue); verify_computed_type(generate_property('<time>'), null, CSSUnitValue);
assert_computed_type(gen_prop('<time> | fail', 'fail'), '42s', CSSUnitValue); verify_computed_type(generate_property('fail | <time> '), '42s', CSSUnitValue);
}, 'Computed <time> is reified as CSSUnitValue'); }, 'Computed <time> is reified as CSSUnitValue');
test(function(){ test(function(){
assert_computed_type(gen_prop('<url>', 'url(a)'), null, CSSStyleValue); verify_computed_type(generate_property('<url>'), null, CSSStyleValue);
assert_computed_type(gen_prop('<url> | fail', 'fail'), 'url(a)', CSSStyleValue); verify_computed_type(generate_property('fail | <url> '), 'url(a)', CSSStyleValue);
}, 'Computed <url> is reified as CSSStyleValue'); }, 'Computed <url> is reified as CSSStyleValue');
test(function(){ test(function(){
assert_computed_type(gen_prop('thing1 | THING2', 'thing1'), null, CSSKeywordValue); verify_computed_type(generate_property('thing1 | THING2'), null, CSSKeywordValue);
assert_computed_type(gen_prop('thing1 | THING2 | <url>', 'url(fail)'), 'THING2', CSSKeywordValue); verify_computed_type(generate_property('thing1 | THING2 | <url>'), 'THING2', CSSKeywordValue);
}, 'Computed ident is reified as CSSKeywordValue'); }, 'Computed ident is reified as CSSKeywordValue');
test(function(){ test(function(){
assert_computed_type(gen_prop('<length>+', '10px 20px'), null, CSSUnitValue); verify_computed_type(generate_property('<length>+'), null, CSSUnitValue);
assert_computed_type(gen_prop('<length>+', '0px 0px'), '10px 20px', CSSUnitValue); verify_computed_type(generate_property('<length>+'), '10px 20px', CSSUnitValue);
}, 'First computed value correctly reified in space-separated list'); }, 'First computed value correctly reified in space-separated list');
test(function(){ test(function(){
assert_computed_type(gen_prop('<length>#', '10px, 20px'), null, CSSUnitValue); verify_computed_type(generate_property('<length>#'), null, CSSUnitValue);
assert_computed_type(gen_prop('<length>#', '0px, 0px'), '10px, 20px', CSSUnitValue); verify_computed_type(generate_property('<length>#'), '10px, 20px', CSSUnitValue);
}, 'First computed value correctly reified in comma-separated list'); }, 'First computed value correctly reified in comma-separated list');
// StylePropertyMapReadOnly.getAll
test(function(){ test(function(){
let name = gen_prop('<length>+', '10px 20px'); let name = generate_property({syntax: '<length>+', initialValue: '10px 20px'});
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));
...@@ -190,7 +240,7 @@ test(function(){ ...@@ -190,7 +240,7 @@ test(function(){
}, 'All computed values correctly reified in space-separated list'); }, 'All computed values correctly reified in space-separated list');
test(function(){ test(function(){
let name = gen_prop('<length>#', '10px, 20px'); let name = generate_property({syntax: '<length>#', initialValue: '10px, 20px'});
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));
...@@ -199,680 +249,601 @@ test(function(){ ...@@ -199,680 +249,601 @@ test(function(){
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');
// attributeStyleMap.get / styleMap.get // StylePropertyMap.get/All
function test_style_property_map_get(test_fn, name_fn) { test_specified_maps(function(map){
let rule = style.sheet.rules[0]; verify_map_get_unparsed(map, generate_property('*'), 'foo');
}, 'Specified * is reified as CSSUnparsedValue from get/getAll');
test(function(){ test_specified_maps(function(map){
target.attributeStyleMap.clear(); verify_map_get_unparsed(map, generate_property('foo'), 'foo');
test_fn(target.style, target.attributeStyleMap); }, 'Specified foo is reified as CSSUnparsedValue from get/getAll');
}, name_fn('attributeStyleMap'));
test(function(){ test_specified_maps(function(map){
rule.styleMap.clear(); verify_map_get_unparsed(map, generate_property('<angle>'), '10deg');
test_fn(rule.style, rule.styleMap); }, 'Specified <angle> is reified as CSSUnparsedValue from get/getAll');
}, name_fn('styleMap'));
} test_specified_maps(function(map){
verify_map_get_unparsed(map, generate_property('<color>'), 'green');
}, 'Specified <color> is reified as CSSUnparsedValue from get/getAll');
test_specified_maps(function(map){
verify_map_get_unparsed(map, generate_property('<custom-ident>'), 'foo');
}, 'Specified <custom-ident> is reified as CSSUnparsedValue from get/getAll');
test_specified_maps(function(map){
verify_map_get_unparsed(map, generate_property('<image>'), 'url("a")');
}, 'Specified <image> is reified as CSSUnparsedValue from get/getAll');
test_specified_maps(function(map){
verify_map_get_unparsed(map, generate_property('<integer>'), '1');
}, 'Specified <integer> is reified as CSSUnparsedValue from get/getAll');
test_specified_maps(function(map){
verify_map_get_unparsed(map, generate_property('<length-percentage>'), 'calc(10% + 10px)');
}, 'Specified <length-percentage> is reified as CSSUnparsedValue from get/getAll');
test_specified_maps(function(map){
verify_map_get_unparsed(map, generate_property('<length>'), '10px');
}, 'Specified <length> is reified as CSSUnparsedValue from get/getAll');
test_specified_maps(function(map){
verify_map_get_unparsed(map, generate_property('<number>'), '1');
}, 'Specified <number> is reified as CSSUnparsedValue from get/getAll');
test_specified_maps(function(map){
verify_map_get_unparsed(map, generate_property('<percentage>'), '10%');
}, 'Specified <percentage> is reified as CSSUnparsedValue from get/getAll');
test_specified_maps(function(map){
verify_map_get_unparsed(map, generate_property('<resolution>'), '10dpi');
}, 'Specified <resolution> is reified as CSSUnparsedValue from get/getAll');
test_specified_maps(function(map){
verify_map_get_unparsed(map, generate_property('<time>'), '1s');
}, 'Specified <time> is reified as CSSUnparsedValue from get/getAll');
test_specified_maps(function(map){
verify_map_get_unparsed(map, generate_property('<transform-function>'), 'matrix(0, 0, 0, 0, 0, 0)');
}, 'Specified <transform-function> is reified as CSSUnparsedValue from get/getAll');
test_style_property_map_get(function(styleDecl, propertyMap){ test_specified_maps(function(map){
let name1 = gen_prop('<length>', '100px'); verify_map_get_unparsed(map, generate_property('<transform-list>'), 'matrix(0, 0, 0, 0, 0, 0)');
let name2 = gen_prop('<length>', '0px'); }, 'Specified <transform-list> is reified as CSSUnparsedValue from get/getAll');
styleDecl.setProperty(name2, `var(${name1})`);
assert_true(propertyMap.get(name2) instanceof CSSUnparsedValue); test_specified_maps(function(map){
}, name => `StylePropertyMap.get returns CSSUnparsedValue for value with var references (${name})`); verify_map_get_unparsed(map, generate_property('<url>'), 'url("a")');
}, 'Specified <url> is reified as CSSUnparsedValue from get/getAll');
test_style_property_map_get(function(styleDecl, propertyMap){
let name1 = gen_prop('<length>', '100px'); test_specified_maps(function(map){
let name2 = gen_prop('<length>#', '0px'); verify_map_get_unparsed(map, generate_property('<length>+'), '10px 11px');
styleDecl.setProperty(name2, `1px, var(${name1}), 3px`); }, 'Specified <length>+ is reified as CSSUnparsedValue from get/getAll');
assert_true(propertyMap.get(name2) instanceof CSSUnparsedValue);
}, name => `StylePropertyMap.get returns CSSUnparsedValue for value with var references in list (${name})`); test_specified_maps(function(map){
verify_map_get_unparsed(map, generate_property('<length>#'), '10px, 11px');
test_style_property_map_get(function(styleDecl, propertyMap){ }, 'Specified <length># is reified as CSSUnparsedValue from get/getAll');
assert_attribute_get_type(styleDecl, propertyMap, '*', 'if(){}', CSSUnparsedValue);
}, name => `StylePropertyMap.get returns CSSUnparsedValue for * (${name})`);
test_style_property_map_get(function(styleDecl, propertyMap){
assert_attribute_get_type(styleDecl, propertyMap, '<angle>', '42deg', CSSUnitValue);
}, name => `StylePropertyMap.get returns CSSUnitValue for <angle> (${name})`);
test_style_property_map_get(function(styleDecl, propertyMap){
assert_attribute_get_type(styleDecl, propertyMap, '<color>', '#fefefe', CSSStyleValue);
}, name => `StylePropertyMap.get returns CSSStyleValue for <color> (${name})`);
test_style_property_map_get(function(styleDecl, propertyMap){
assert_attribute_get_type(styleDecl, propertyMap, '<custom-ident>', 'none', CSSKeywordValue);
}, name => `StylePropertyMap.get returns CSSKeywordValue for <custom-ident> (${name})`);
test_style_property_map_get(function(styleDecl, propertyMap){
assert_attribute_get_type(styleDecl, propertyMap, '<image>', 'url(thing.png)', CSSImageValue);
}, name => `StylePropertyMap.get returns CSSImageValue for <image> (${name})`);
test_style_property_map_get(function(styleDecl, propertyMap){
assert_attribute_get_type(styleDecl, propertyMap, '<integer>', '100', CSSUnitValue);
}, name => `StylePropertyMap.get returns CSSUnitValue for <integer> (${name})`);
test_style_property_map_get(function(styleDecl, propertyMap){
assert_attribute_get_type(styleDecl, propertyMap, '<length-percentage>', '10%', CSSUnitValue);
}, name => `StylePropertyMap.get returns CSSUnitValue for <length-percentage> [10%] (${name})`);
test_style_property_map_get(function(styleDecl, propertyMap){
assert_attribute_get_type(styleDecl, propertyMap, '<length-percentage>', '10px', CSSUnitValue);
}, name => `StylePropertyMap.get returns CSSUnitValue for <length-percentage> [10px] (${name})`);
test_style_property_map_get(function(styleDecl, propertyMap){
assert_attribute_get_type(styleDecl, propertyMap, '<length-percentage>', 'calc(10px + 10%)', CSSMathSum);
}, name => `StylePropertyMap.get returns CSSMathSum for <length-percentage> [calc(10px + 10%)] (${name})`);
test_style_property_map_get(function(styleDecl, propertyMap){
assert_attribute_get_type(styleDecl, propertyMap, '<length>', '10px', CSSUnitValue);
}, name => `StylePropertyMap.get returns CSSUnitValue for <length> (${name})`);
test_style_property_map_get(function(styleDecl, propertyMap){
assert_attribute_get_type(styleDecl, propertyMap, '<number>', '42', CSSUnitValue);
}, name => `StylePropertyMap.get returns CSSUnitValue for <number> (${name})`);
test_style_property_map_get(function(styleDecl, propertyMap){
assert_attribute_get_type(styleDecl, propertyMap, '<percentage>', '10%', CSSUnitValue);
}, name => `StylePropertyMap.get returns CSSUnitValue for <percentage> (${name})`);
test_style_property_map_get(function(styleDecl, propertyMap){
assert_attribute_get_type(styleDecl, propertyMap, '<resolution>', '300dpi', CSSUnitValue);
}, name => `StylePropertyMap.get returns CSSUnitValue for <resolution> (${name})`);
test_style_property_map_get(function(styleDecl, propertyMap){
assert_attribute_get_type(styleDecl, propertyMap, '<time>', '42s', CSSUnitValue);
}, name => `StylePropertyMap.get returns CSSUnitValue for <time> (${name})`);
test_style_property_map_get(function(styleDecl, propertyMap){
assert_attribute_get_type(styleDecl, propertyMap, '<url>', 'url(a)', CSSStyleValue);
}, name => `StylePropertyMap.get returns CSSStyleValue for <url> (${name})`);
test_style_property_map_get(function(styleDecl, propertyMap){
assert_attribute_get_type(styleDecl, propertyMap, 'thing1 | THING2', 'thing1', CSSKeywordValue);
}, name => `StylePropertyMap.get returns CSSKeywordValue for thing1 | THING2 (${name})`);
test_style_property_map_get(function(styleDecl, propertyMap){
assert_attribute_get_type(styleDecl, propertyMap, '<length>+', '10px 20px', CSSUnitValue);
}, name => `StylePropertyMap.get returns CSSUnitValue for <length>+ (${name})`);
test_style_property_map_get(function(styleDecl, propertyMap){
assert_attribute_get_type(styleDecl, propertyMap, '<length>#', '10px 20px', CSSUnitValue);
}, name => `StylePropertyMap.get returns CSSUnitValue for <length># (${name})`);
// attributeStyleMap.getAll
test_style_property_map_get(function(styleDecl, propertyMap){
let name = gen_prop('<length>+', '0px');
styleDecl.setProperty(name, '10px 20px 30px');
assert_equals(propertyMap.getAll(name).length, 3);
assert_true(propertyMap.getAll(name).every(x => x instanceof CSSUnitValue));
}, name => `StylePropertyMap.getAll returns a list of CSSUnitValues for <length>+ (${name})`);
test_style_property_map_get(function(styleDecl, propertyMap){
let name = gen_prop('<length>#', '0px');
styleDecl.setProperty(name, '10px, 20px, 30px');
assert_equals(propertyMap.getAll(name).length, 3);
assert_true(propertyMap.getAll(name).every(x => x instanceof CSSUnitValue));
}, name => `StylePropertyMap.getAll returns a list of CSSUnitValues for <length># (${name})`);
// StylePropertyMap.set // StylePropertyMap.set
function test_style_property_map_set_using_property_map(propertyMapName, propertyMap, options) { // The following strings are valid for the specified syntax, and should be
test(function(){ // accepted by set().
let name = gen_prop(options.syntax, options.initialValue);
propertyMap.clear();
let ensureArray = v => v.constructor === Array ? v : [v]; test_specified_maps(function(map){
verify_map_set(map, generate_property('*'), 'foo');
}, 'Specified string "foo" accepted by set() for syntax *');
for (let value of options.shouldAccept) test_specified_maps(function(map){
propertyMap.set(name, ...ensureArray(value)); verify_map_set(map, generate_property('foo'), 'foo');
}, 'Specified string "foo" accepted by set() for syntax foo');
for (let value of options.shouldReject) { test_specified_maps(function(map){
assert_throws(new TypeError(), () => propertyMap.set(name, ...ensureArray(value))); verify_map_set(map, generate_property('<angle>'), '10deg');
} }, 'Specified string "10deg" accepted by set() for syntax <angle>');
}, `StylePropertyMap.set accepts correct CSSStyleValues for ${options.syntax} (${propertyMapName})`);
}
// Verify that the correct CSSStyleValues are accepted/rejected for a registered test_specified_maps(function(map){
// property with the specified syntax. verify_map_set(map, generate_property('<color>'), 'green');
// }, 'Specified string "green" accepted by set() for syntax <color>');
// 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]); test_specified_maps(function(map){
let keyword = x => new CSSKeywordValue(x); verify_map_set(map, generate_property('<custom-ident>'), 'foo');
let sum = (a, b) => new CSSMathSum(a, b); }, 'Specified string "foo" accepted by set() for syntax <custom-ident>');
let url_image = x => CSSStyleValue.parse('background-image', x);
test_style_property_map_set({ test_specified_maps(function(map){
syntax: '*', verify_map_set(map, generate_property('<image>'), 'url("a")');
initialValue: 'none', }, 'Specified string "url("a")" accepted by set() for syntax <image>');
shouldAccept: [unparsed('thing')],
shouldReject: [CSS.px(15), keyword('none')],
});
test_style_property_map_set({ test_specified_maps(function(map){
syntax: '<angle>', verify_map_set(map, generate_property('<integer>'), '1');
initialValue: '0deg', }, 'Specified string "1" accepted by set() for syntax <integer>');
shouldAccept: [CSS.deg(42), CSS.turn(2), '42deg'],
shouldReject: [unparsed('42deg'), CSS.px(15), '50px', [CSS.deg(15), '10deg']],
});
test_style_property_map_set({ test_specified_maps(function(map){
syntax: '<custom-ident>', verify_map_set(map, generate_property('<length-percentage>'), 'calc(10% + 10px)');
initialValue: 'none', }, 'Specified string "calc(10% + 10px)" accepted by set() for syntax <length-percentage>');
shouldAccept: [keyword('foo'), 'foo'],
shouldReject: [unparsed('foo'), CSS.px(15), '15px', [keyword('foo'), 'foo']],
});
test_style_property_map_set({ test_specified_maps(function(map){
syntax: '<image>', verify_map_set(map, generate_property('<length>'), '10px');
initialValue: 'url(a)', }, 'Specified string "10px" accepted by set() for syntax <length>');
shouldAccept: [url_image('url(b)'), 'url(b)'],
shouldReject: [unparsed('url(b)'), CSS.px(100), '50px', [url_image('url(1)'), 'url(2)']],
});
test_style_property_map_set({ test_specified_maps(function(map){
syntax: '<integer>', verify_map_set(map, generate_property('<number>'), '1');
initialValue: '0', }, 'Specified string "1" accepted by set() for syntax <number>');
shouldAccept: [CSS.number(1), CSS.number(-42), '1', '-42', 'calc(2.4)'],
shouldReject: [unparsed('42'), CSS.px(100), '50px', [CSS.number(42), '42'], 'calc(2px + 1px)'],
});
test_style_property_map_set({ test_specified_maps(function(map){
syntax: '<length-percentage>', verify_map_set(map, generate_property('<percentage>'), '10%');
initialValue: '0px', }, 'Specified string "10%" accepted by set() for syntax <percentage>');
shouldAccept: [CSS.percent(10), CSS.px(1), CSS.em(1), '10px', '10%'],
shouldReject: [unparsed('10%'), unparsed('10px'), CSS.dpi(1), 'url(b)', [CSS.percent(10), '10%']],
});
test_style_property_map_set({ test_specified_maps(function(map){
syntax: '<length>', verify_map_set(map, generate_property('<resolution>'), '10dpi');
initialValue: '0px', }, 'Specified string "10dpi" accepted by set() for syntax <resolution>');
shouldAccept: [CSS.px(10), CSS.em(10), CSS.vh(200), sum(CSS.px(10), CSS.em(20)), '10em', 'calc(10px + 10em)'],
shouldReject: [unparsed('10px'), CSS.percent(1), 'url(b)', [CSS.em(10), '10px']],
});
test_style_property_map_set({ test_specified_maps(function(map){
syntax: '<number>', verify_map_set(map, generate_property('<time>'), '1s');
initialValue: '0', }, 'Specified string "1s" accepted by set() for syntax <time>');
shouldAccept: [CSS.number(1337), CSS.number(-42.5), '1337', '-42.5'],
shouldReject: [unparsed('42'), CSS.px(15), '#fef', [CSS.number(-42.5), '42.5']],
});
test_style_property_map_set({ test_specified_maps(function(map){
syntax: '<percentage>', verify_map_set(map, generate_property('<transform-function>'), 'matrix(0, 0, 0, 0, 0, 0)');
initialValue: '0%', }, 'Specified string "matrix(0, 0, 0, 0, 0, 0)" accepted by set() for syntax <transform-function>');
shouldAccept: [CSS.percent(10), '10%'],
shouldReject: [unparsed('10%'), CSS.px(1), '#fef', [CSS.percent(10), '1%']],
});
test_style_property_map_set({ test_specified_maps(function(map){
syntax: '<resolution>', verify_map_set(map, generate_property('<transform-list>'), 'matrix(0, 0, 0, 0, 0, 0)');
initialValue: '0dpi', }, 'Specified string "matrix(0, 0, 0, 0, 0, 0)" accepted by set() for syntax <transform-list>');
shouldAccept: [CSS.dpi(100), CSS.dpcm(10), CSS.dppx(50), '100dpi'],
shouldReject: [unparsed('42'), CSS.px(15), '#fef', [CSS.dpi(1), '2dpi']],
});
test_style_property_map_set({ test_specified_maps(function(map){
syntax: '<time>', verify_map_set(map, generate_property('<url>'), 'url("a")');
initialValue: '0s', }, 'Specified string "url("a")" accepted by set() for syntax <url>');
shouldAccept: [CSS.s(42), CSS.ms(16), '16ms'],
shouldReject: [unparsed('42s'), CSS.px(15), '#fef', [CSS.s(5), '6s']],
});
test_style_property_map_set({ test_specified_maps(function(map){
syntax: '<url>', verify_map_set(map, generate_property('<length>+'), '10px 11px');
initialValue: 'url(a)', }, 'Specified string "10px 11px" accepted by set() for syntax <length>+');
shouldAccept: [url_image('url(b)')],
shouldReject: [unparsed('url(b)'), CSS.px(100), '#fef', [url_image('url(1)'), 'url(2)']],
});
test_style_property_map_set({ test_specified_maps(function(map){
syntax: '<transform-list>', verify_map_set(map, generate_property('<length>#'), '10px, 11px');
initialValue: 'translateX(0px)', }, 'Specified string "10px, 11px" accepted by set() for syntax <length>#');
shouldAccept: [CSSStyleValue.parse('transform', 'translateX(10px)')],
shouldReject: [unparsed('transformX(10px'), CSS.px(100), '#fef'],
});
test_style_property_map_set({ // The following strings are invalid for the specified syntax, but should
syntax: 'none | thing | THING', // should be accepted by set().
initialValue: 'none',
shouldAccept: [keyword('thing'), keyword('THING'), 'thing'],
shouldReject: [unparsed('thing'), CSS.px(15), keyword('notathing'), 'notathing', [keyword('thing'), keyword('thing')]],
});
test_style_property_map_set({ test_specified_maps(function(map){
syntax: '<angle> | <length>', verify_map_set(map, generate_property('foo'), 'bar');
initialValue: '0deg', }, 'Specified string "bar" accepted by set() for syntax foo');
shouldAccept: [CSS.deg(42), CSS.turn(2), CSS.px(10), CSS.em(10), '10deg', '10px'],
shouldReject: [unparsed('42deg'), unparsed('20px'), CSS.s(1), '#fef', [CSS.deg(42), '21deg']],
});
// StylePropertyMap.set for list-valued properties: test_specified_maps(function(map){
verify_map_set(map, generate_property('<angle>'), '10px');
}, 'Specified string "10px" accepted by set() for syntax <angle>');
test_style_property_map_set({ test_specified_maps(function(map){
syntax: '<angle>+', verify_map_set(map, generate_property('<color>'), '10px');
initialValue: '0deg', }, 'Specified string "10px" accepted by set() for syntax <color>');
shouldAccept: [CSS.deg(15), [CSS.deg(15), '10deg'], '15deg 10deg'],
shouldReject: [[CSS.deg(15), CSS.px(10)], '15deg 10px'],
});
test_style_property_map_set({ test_specified_maps(function(map){
syntax: '<custom-ident>+', verify_map_set(map, generate_property('<custom-ident>'), '10px');
initialValue: 'none', }, 'Specified string "10px" accepted by set() for syntax <custom-ident>');
shouldAccept: [keyword('foo'), [keyword('foo'), 'bar'], 'foo bar'],
shouldReject: [[keyword('foo'), CSS.px(10)], 'foo 10px'],
});
test_style_property_map_set({ test_specified_maps(function(map){
syntax: '<image>+', verify_map_set(map, generate_property('<image>'), 'a');
initialValue: 'url(a)', }, 'Specified string "a" accepted by set() for syntax <image>');
shouldAccept: [url_image('url(1)'), [url_image('url(1)'), 'url(2)'], 'url(1) url(2)'],
shouldReject: [[url_image('url(1)'), CSS.px(10)], 'url(1) 10px'],
});
test_style_property_map_set({ test_specified_maps(function(map){
syntax: '<integer>+', verify_map_set(map, generate_property('<integer>'), 'float');
initialValue: '0', }, 'Specified string "float" accepted by set() for syntax <integer>');
shouldAccept: [CSS.number(42), [CSS.number(42), '42'], '42 42', 'calc(2.4) calc(2.6)'],
shouldReject: [[CSS.number(42), keyword('noint')], '42 noint', 'calc(2px + 2px)'],
});
test_style_property_map_set({ test_specified_maps(function(map){
syntax: '<length-percentage>+', verify_map_set(map, generate_property('<length-percentage>'), 'red');
initialValue: '0px', }, 'Specified string "red" accepted by set() for syntax <length-percentage>');
shouldAccept: [CSS.percent(10), [CSS.percent(10), '10%']],
shouldReject: [[CSS.percent(10), keyword('nolength')], '10% nolength'],
});
test_style_property_map_set({ test_specified_maps(function(map){
syntax: '<length>+', verify_map_set(map, generate_property('<length>'), 'red');
initialValue: '0px', }, 'Specified string "red" accepted by set() for syntax <length>');
shouldAccept: [CSS.em(10), [CSS.em(10), '10px']],
shouldReject: [[CSS.em(10), keyword('nolength'), '10em nolength']],
});
test_style_property_map_set({ test_specified_maps(function(map){
syntax: '<number>+', verify_map_set(map, generate_property('<number>'), 'red');
initialValue: '0', }, 'Specified string "red" accepted by set() for syntax <number>');
shouldAccept: [CSS.number(-42.5), [CSS.number(-42.5), '42.5'], '-42.5 42.5'],
shouldReject: [[CSS.number(-42.5), CSS.px(10)], '-42.5 10px'],
});
test_style_property_map_set({ test_specified_maps(function(map){
syntax: '<percentage>+', verify_map_set(map, generate_property('<percentage>'), 'var(--x)');
initialValue: '0%', }, 'Specified string "var(--x)" accepted by set() for syntax <percentage>');
shouldAccept: [CSS.percent(10), [CSS.percent(10), '1%'], '10% 1%'],
shouldReject: [[CSS.percent(10), keyword('foo')], '10% foo'],
});
test_style_property_map_set({ test_specified_maps(function(map){
syntax: '<resolution>+', verify_map_set(map, generate_property('<resolution>'), 'blue');
initialValue: '0dpi', }, 'Specified string "blue" accepted by set() for syntax <resolution>');
shouldAccept: [CSS.dpi(1), [CSS.dpi(1), '2dpi'], '1dpi 2dpi'],
shouldReject: [[CSS.dpi(1), keyword('foo')], '1dpi foo'],
});
test_style_property_map_set({ test_specified_maps(function(map){
syntax: '<time>+', verify_map_set(map, generate_property('<time>'), '1meter');
initialValue: '0s', }, 'Specified string "1meter" accepted by set() for syntax <time>');
shouldAccept: [CSS.s(5), [CSS.s(5), '6s'], '5s 6s'],
shouldReject: [[CSS.s(5), keyword('foo')], '5s foo'],
});
test_style_property_map_set({ test_specified_maps(function(map){
syntax: '<url>+', verify_map_set(map, generate_property('<transform-function>'), 'foo(0)');
initialValue: 'url(a)', }, 'Specified string "foo(0)" accepted by set() for syntax <transform-function>');
shouldAccept: [url_image('url(1)'), [url_image('url(1)'), 'url(2)'], 'url(1) url(2)'],
shouldReject: [[url_image('url(1)'), CSS.px(10)], 'url(1) 10px'],
});
test_style_property_map_set({ test_specified_maps(function(map){
syntax: 'thing+', verify_map_set(map, generate_property('<transform-list>'), 'bar(1)');
initialValue: 'thing', }, 'Specified string "bar(1)" accepted by set() for syntax <transform-list>');
shouldAccept: [keyword('thing'), [keyword('thing'), 'thing'], 'thing thing'],
shouldReject: [[keyword('thing'), CSS.px(10)], 'thing 10px'],
});
test_style_property_map_set({ test_specified_maps(function(map){
syntax: '<length>#', verify_map_set(map, generate_property('<url>'), 'a');
initialValue: '0px', }, 'Specified string "a" accepted by set() for syntax <url>');
shouldAccept: [CSS.em(10), [CSS.em(10), '10px']],
shouldReject: [[CSS.em(10), keyword('nolength'), '10em nolength']],
});
function test_append_for_property_map(propertyMapName, propertyMap, options) { test_specified_maps(function(map){
test(function(){ verify_map_set(map, generate_property('<length>+'), 'a b');
let name = gen_prop(options.syntax, options.initialValue); }, 'Specified string "a b" accepted by set() for syntax <length>+');
let ensureArray = v => v.constructor === Array ? v : [v]; test_specified_maps(function(map){
verify_map_set(map, generate_property('<length>#'), 'a, b');
}, 'Specified string "a, b" accepted by set() for syntax <length>#');
for (let value of options.values) { // CSSUnparsedValue should always be accepted by any custom property,
propertyMap.clear(); // regardless of registation status.
if (value.base !== null) const unparsed = CSSStyleValue.parse('--x', 'foo bar thing');
propertyMap.set(name, ...ensureArray(value.base));
// If 'null' is expected, it means we expect the append to fail. test_specified_maps(function(map){
if (value.expect !== null) { verify_map_set(map, generate_property('*'), unparsed);
propertyMap.append(name, ...ensureArray(value.append)); }, 'CSSUnparsedValue is accepted via set() for syntax *');
let actual = Array.from(propertyMap.getAll(name)).join(' ');
assert_equals(actual, value.expect); test_specified_maps(function(map){
} else { verify_map_set(map, generate_property('foo'), unparsed);
assert_throws(new TypeError(), () => propertyMap.append(name, ...ensureArray(value.append))); }, 'CSSUnparsedValue is accepted via set() for syntax foo');
}
} test_specified_maps(function(map){
}, `StylePropertyMap.append accepts correct CSSStyleValues for ${options.syntax} (${propertyMapName})`); verify_map_set(map, generate_property('<angle>'), unparsed);
} }, 'CSSUnparsedValue is accepted via set() for syntax <angle>');
test_specified_maps(function(map){
verify_map_set(map, generate_property('<color>'), unparsed);
}, 'CSSUnparsedValue is accepted via set() for syntax <color>');
test_specified_maps(function(map){
verify_map_set(map, generate_property('<custom-ident>'), unparsed);
}, 'CSSUnparsedValue is accepted via set() for syntax <custom-ident>');
test_specified_maps(function(map){
verify_map_set(map, generate_property('<image>'), unparsed);
}, 'CSSUnparsedValue is accepted via set() for syntax <image>');
// Verify that the correct CSSStyleValues are accepted/rejected when test_specified_maps(function(map){
// appending values to list-valued properties. verify_map_set(map, generate_property('<integer>'), unparsed);
}, 'CSSUnparsedValue is accepted via set() for syntax <integer>');
test_specified_maps(function(map){
verify_map_set(map, generate_property('<length-percentage>'), unparsed);
}, 'CSSUnparsedValue is accepted via set() for syntax <length-percentage>');
test_specified_maps(function(map){
verify_map_set(map, generate_property('<length>'), unparsed);
}, 'CSSUnparsedValue is accepted via set() for syntax <length>');
test_specified_maps(function(map){
verify_map_set(map, generate_property('<number>'), unparsed);
}, 'CSSUnparsedValue is accepted via set() for syntax <number>');
test_specified_maps(function(map){
verify_map_set(map, generate_property('<percentage>'), unparsed);
}, 'CSSUnparsedValue is accepted via set() for syntax <percentage>');
test_specified_maps(function(map){
verify_map_set(map, generate_property('<resolution>'), unparsed);
}, 'CSSUnparsedValue is accepted via set() for syntax <resolution>');
test_specified_maps(function(map){
verify_map_set(map, generate_property('<time>'), unparsed);
}, 'CSSUnparsedValue is accepted via set() for syntax <time>');
test_specified_maps(function(map){
verify_map_set(map, generate_property('<transform-function>'), unparsed);
}, 'CSSUnparsedValue is accepted via set() for syntax <transform-function>');
test_specified_maps(function(map){
verify_map_set(map, generate_property('<transform-list>'), unparsed);
}, 'CSSUnparsedValue is accepted via set() for syntax <transform-list>');
test_specified_maps(function(map){
verify_map_set(map, generate_property('<url>'), unparsed);
}, 'CSSUnparsedValue is accepted via set() for syntax <url>');
test_specified_maps(function(map){
verify_map_set(map, generate_property('<length>+'), unparsed);
}, 'CSSUnparsedValue is accepted via set() for syntax <length>+');
test_specified_maps(function(map){
verify_map_set(map, generate_property('<length>#'), unparsed);
}, 'CSSUnparsedValue is accepted via set() for syntax <length>#');
// CSSStyleValues which aren't CSSUnparsedValues aren't accepted by set(),
// even if they're a value which is compatible with the syntax.
// //
// The same test is performed twice: once for attributeStyleMap, and once // https://drafts.css-houdini.org/css-properties-values-api-1/#cssom
// for styleMap. const zero_matrix = CSSStyleValue.parse('transform', 'matrix(0, 0, 0, 0, 0, 0)');
function test_append(options) { const image_value = CSSStyleValue.parse('background-image', 'url(a)');
test_append_for_property_map('attributeStyleMap', target.attributeStyleMap, options);
test_append_for_property_map('styleMap', style.sheet.rules[0].styleMap, options);
}
test_append({ test_specified_maps(function(map){
syntax: '<angle>+', verify_map_not_set(map, generate_property('*'), new CSSKeywordValue('foo'));
initialValue: '0deg', }, 'CSSKeywordValue rejected by set() for syntax *');
values: [
{ base: [CSS.deg(1)], append: [CSS.px(1)], expect: null },
{ base: [CSS.deg(1)], append: [CSS.deg(2), CSS.px(1)], expect: null },
{ base: [CSS.deg(1)], append: [CSS.deg(2), '1px'], expect: null },
{ base: [CSS.deg(1)], append: [CSS.turn(2), CSS.deg(3)], expect: '1deg 2turn 3deg' },
{ base: [CSS.deg(1), CSS.deg(2)], append: [CSS.deg(3)], expect: '1deg 2deg 3deg' },
{ base: [CSS.deg(1)], append: [CSS.deg(2), '3deg'], expect: '1deg 2deg 3deg' },
{ base: [CSS.deg(1)], append: [CSS.deg(2), '3turn 4deg'], expect: '1deg 2deg 3turn 4deg' },
{ base: null, append: [CSS.deg(1), '2deg'], expect: '1deg 2deg' },
],
});
test_append({ test_specified_maps(function(map){
syntax: '<custom-ident>+', verify_map_not_set(map, generate_property('foo'), new CSSKeywordValue('foo'));
initialValue: 'none', }, 'CSSKeywordValue rejected by set() for syntax foo');
values: [
{ base: [keyword('foo')], append: [CSS.px(1)], expect: null },
{ base: [keyword('foo')], append: [keyword('bar'), CSS.px(1)], expect: null },
{ base: [keyword('foo')], append: [keyword('bar'), '1px'], expect: null },
{ base: [keyword('foo')], append: [keyword('bar'), keyword('baz')], expect: 'foo bar baz' },
{ base: [keyword('foo'), keyword('bar')], append: [keyword('baz')], expect: 'foo bar baz' },
{ base: [keyword('foo')], append: [keyword('bar'), 'baz'], expect: 'foo bar baz' },
{ base: [keyword('foo')], append: [keyword('bar'), 'baz zim'], expect: 'foo bar baz zim' },
{ base: null, append: [keyword('foo'), 'bar'], expect: 'foo bar' },
],
});
['<image>+', '<url>+'].forEach((syntax) => { test_specified_maps(function(map){
test_append({ verify_map_not_set(map, generate_property('<angle>'), CSS.deg(10));
syntax: syntax, }, 'CSSUnitValue rejected by set() for syntax <angle>');
initialValue: 'url(0)',
values: [
{ base: [url_image('url("1")')], append: [CSS.px(1)], expect: null },
{ base: [url_image('url("1")')], append: [url_image('url("2")'), CSS.px(1)], expect: null },
{ base: [url_image('url("1")')], append: [url_image('url("2")'), '1px'], expect: null },
{ base: [url_image('url("1")')], append: [url_image('url("2")'), url_image('url("3")')], expect: 'url("1") url("2") url("3")' },
{ base: [url_image('url("1")'), url_image('url("2")')], append: [url_image('url("3")')], expect: 'url("1") url("2") url("3")' },
{ base: [url_image('url("1")')], append: [url_image('url("2")'), 'url("3")'], expect: 'url("1") url("2") url("3")' },
{ base: [url_image('url("1")')], append: [url_image('url("2")'), 'url("3") url("4")'], expect: 'url("1") url("2") url("3") url("4")' },
{ base: null, append: [url_image('url("1")'), 'url("2")'], expect: 'url("1") url("2")' },
],
});
});
test_append({ test_specified_maps(function(map){
syntax: '<integer>+', verify_map_not_set(map, generate_property('<custom-ident>'), new CSSKeywordValue('foo'));
initialValue: '0', }, 'CSSKeywordValue rejected by set() for syntax <custom-ident>');
values: [
{ base: [CSS.number(1)], append: [CSS.px(1)], expect: null },
{ base: [CSS.number(1)], append: [CSS.number(2), CSS.px(1)], expect: null },
{ base: [CSS.number(1)], append: [CSS.number(2), 'noint'], expect: null },
{ base: [CSS.number(1)], append: [CSS.number(2), CSS.number(3)], expect: '1 2 3' },
{ base: [CSS.number(1), CSS.number(2)], append: [CSS.number(3)], expect: '1 2 3' },
{ base: [CSS.number(1)], append: [CSS.number(2), '3'], expect: '1 2 3' },
{ base: [CSS.number(1)], append: [CSS.number(2), '3 4'], expect: '1 2 3 4' },
{ base: null, append: [CSS.number(1), '2'], expect: '1 2' },
],
});
test_append({ test_specified_maps(function(map){
syntax: '<length-percentage>+', verify_map_not_set(map, generate_property('<image>'), image_value);
initialValue: '0px', }, 'CSSImageValue rejected by set() for syntax <image>');
values: [
{ base: [CSS.px(1)], append: [keyword('nolength')], expect: null },
{ base: [CSS.px(1)], append: [CSS.px(2), keyword('nolength')], expect: null },
{ base: [CSS.px(1)], append: [CSS.px(2), 'nolength'], expect: null },
{ base: [CSS.px(1)], append: [CSS.px(2), CSS.percent(3)], expect: '1px 2px 3%' },
{ base: [CSS.px(1), CSS.px(2)], append: [CSS.percent(3)], expect: '1px 2px 3%' },
{ base: [CSS.px(1)], append: [CSS.percent(2), '3px'], expect: '1px 2% 3px' },
{ base: [CSS.px(1)], append: [CSS.px(2), '3% 4px'], expect: '1px 2px 3% 4px' },
{ base: null, append: [CSS.px(1), '2%'], expect: '1px 2%' },
],
});
test_append({ test_specified_maps(function(map){
syntax: '<length>+', verify_map_not_set(map, generate_property('<integer>'), CSS.number(1));
initialValue: '0', }, 'CSSUnitValue rejected by set() for syntax <integer>');
values: [
{ base: [CSS.px(1)], append: [keyword('nolength')], expect: null },
{ base: [CSS.px(1)], append: [CSS.px(2), keyword('nolength')], expect: null },
{ base: [CSS.px(1)], append: [CSS.px(2), 'nolength'], expect: null },
{ base: [CSS.px(1)], append: [CSS.em(2), CSS.px(3)], expect: '1px 2em 3px' },
{ base: [CSS.px(1), CSS.em(2)], append: [CSS.vh(3)], expect: '1px 2em 3vh' },
{ base: [CSS.px(1)], append: [CSS.em(2), '3px'], expect: '1px 2em 3px' },
{ base: [CSS.px(1)], append: [CSS.px(2), '3em 4cm'], expect: '1px 2px 3em 4cm' },
{ base: null, append: [CSS.vh(1), '2px'], expect: '1vh 2px' },
],
});
test_append({ test_specified_maps(function(map){
syntax: '<number>+', verify_map_not_set(map, generate_property('<length-percentage>'), CSS.px(10));
initialValue: '0', }, 'CSSUnitValue rejected by set() for syntax <length-percentage>');
values: [
{ base: [CSS.number(-1)], append: [keyword('NaN')], expect: null },
{ base: [CSS.number(-1)], append: [CSS.number(2.5), keyword('NaN')], expect: null },
{ base: [CSS.number(-1)], append: [CSS.number(2.5), '1px'], expect: null },
{ base: [CSS.number(-1)], append: [CSS.number(2.5), CSS.number(3.2)], expect: '-1 2.5 3.2' },
{ base: [CSS.number(-1), CSS.number(2.5)], append: [CSS.number(3.2)], expect: '-1 2.5 3.2' },
{ base: [CSS.number(-1)], append: [CSS.number(2.5), '3.2'], expect: '-1 2.5 3.2' },
{ base: [CSS.number(-1)], append: [CSS.number(2.5), '3.2 4'], expect: '-1 2.5 3.2 4' },
{ base: null, append: [CSS.number(-1), '2.5'], expect: '-1 2.5' },
],
});
test_append({ test_specified_maps(function(map){
syntax: '<percentage>+', verify_map_not_set(map, generate_property('<length>'), CSS.px(10));
initialValue: '0%', }, 'CSSUnitValue rejected by set() for syntax <length>');
values: [
{ base: [CSS.percent(1)], append: [CSS.px(1)], expect: null },
{ base: [CSS.percent(1)], append: [CSS.percent(2), CSS.px(1)], expect: null },
{ base: [CSS.percent(1)], append: [CSS.percent(2), '1px'], expect: null },
{ base: [CSS.percent(1)], append: [CSS.percent(2), CSS.percent(3)], expect: '1% 2% 3%' },
{ base: [CSS.percent(1), CSS.percent(2)], append: [CSS.percent(3)], expect: '1% 2% 3%' },
{ base: [CSS.percent(1)], append: [CSS.percent(2), '3%'], expect: '1% 2% 3%' },
{ base: [CSS.percent(1)], append: [CSS.percent(2), '3% 4%'], expect: '1% 2% 3% 4%' },
{ base: null, append: [CSS.percent(1), '2%'], expect: '1% 2%' },
],
});
test_append({ test_specified_maps(function(map){
syntax: '<resolution>+', verify_map_not_set(map, generate_property('<number>'), CSS.number(10));
initialValue: '0dpi', }, 'CSSUnitValue rejected by set() for syntax <number>');
values: [
{ base: [CSS.dpi(1)], append: [CSS.px(1)], expect: null },
{ base: [CSS.dpi(1)], append: [CSS.dpi(2), CSS.px(1)], expect: null },
{ base: [CSS.dpi(1)], append: [CSS.dpi(2), '1px'], expect: null },
{ base: [CSS.dpi(1)], append: [CSS.dpi(2), CSS.dpi(3)], expect: '1dpi 2dpi 3dpi' },
{ base: [CSS.dpi(1), CSS.dpi(2)], append: [CSS.dpi(3)], expect: '1dpi 2dpi 3dpi' },
{ base: [CSS.dpi(1)], append: [CSS.dpi(2), '3dpi'], expect: '1dpi 2dpi 3dpi' },
{ base: [CSS.dpi(1)], append: [CSS.dpi(2), '3dpi 4dpi'], expect: '1dpi 2dpi 3dpi 4dpi' },
{ base: null, append: [CSS.dpi(1), '2dpi'], expect: '1dpi 2dpi' },
],
});
test_append({ test_specified_maps(function(map){
syntax: '<time>+', verify_map_not_set(map, generate_property('<percentage>'), CSS.percent(10));
initialValue: '0s', }, 'CSSUnitValue rejected by set() for syntax <percentage>');
values: [
{ base: [CSS.s(1)], append: [CSS.px(1)], expect: null },
{ base: [CSS.s(1)], append: [CSS.s(2), CSS.px(1)], expect: null },
{ base: [CSS.s(1)], append: [CSS.ms(2), '1px'], expect: null },
{ base: [CSS.s(1)], append: [CSS.ms(2), CSS.s(3)], expect: '1s 2ms 3s' },
{ base: [CSS.s(1), CSS.s(2)], append: [CSS.s(3)], expect: '1s 2s 3s' },
{ base: [CSS.s(1)], append: [CSS.s(2), '3s'], expect: '1s 2s 3s' },
{ base: [CSS.s(1)], append: [CSS.s(2), '3ms 4s'], expect: '1s 2s 3ms 4s' },
{ base: null, append: [CSS.s(1), '2s'], expect: '1s 2s' },
],
});
test_append({ test_specified_maps(function(map){
syntax: 'foo+', verify_map_not_set(map, generate_property('<resolution>'), CSS.dpi(10));
initialValue: 'foo', }, 'CSSUnitValue rejected by set() for syntax <resolution>');
values: [
{ base: [keyword('foo')], append: [CSS.px(1)], expect: null },
{ base: [keyword('foo')], append: [keyword('foo'), CSS.px(1)], expect: null },
{ base: [keyword('foo')], append: [keyword('foo'), '1px'], expect: null },
{ base: [keyword('foo')], append: [keyword('foo'), keyword('foo')], expect: 'foo foo foo' },
{ base: [keyword('foo'), keyword('foo')], append: [keyword('foo')], expect: 'foo foo foo' },
{ base: [keyword('foo')], append: [keyword('foo'), 'foo'], expect: 'foo foo foo' },
{ base: [keyword('foo')], append: [keyword('foo'), 'foo foo'], expect: 'foo foo foo foo' },
{ base: null, append: [keyword('foo'), keyword('foo')], expect: 'foo foo' },
],
});
// CSSStyleValue.parse/parseAll test_specified_maps(function(map){
verify_map_not_set(map, generate_property('<time>'), CSS.s(10));
}, 'CSSUnitValue rejected by set() for syntax <time>');
function assert_parsed_type(prop, value, expected) { test_specified_maps(function(map){
let parse_value = CSSStyleValue.parse(prop, value); verify_map_not_set(map, generate_property('<transform-list>'), zero_matrix);
let parse_all_value = CSSStyleValue.parseAll(prop, value); }, 'CSSTransformValue rejected by set() for syntax <transform-list>');
assert_true(parse_value instanceof expected); test_specified_maps(function(map){
assert_true(parse_all_value.every(x => x instanceof expected)) verify_map_not_set(map, generate_property('<length>+'), CSS.px(10), CSS.px(10));
}, 'CSSUnitValue rejected by set() for syntax <length>+');
// If CSSStyleValue is expected, the values must be exactly CSSStyleValue. test_specified_maps(function(map){
// This is because CSSUnparsedValues are also CSSStyleValues, which would be verify_map_not_set(map, generate_property('<length>#'), CSS.px(10), CSS.px(10));
// wrong in this case. }, 'CSSUnitValue rejected by set() for syntax <length>#');
if (expected == CSSStyleValue) {
assert_equals(parse_value.constructor, CSSStyleValue);
assert_true(parse_all_value.every(x => x.constructor == CSSStyleValue));
}
}
test(function(){ // <color> has no CSSStyleValue subclass yet.
assert_parsed_type(gen_prop('*', 'if(){}'), 'while(){}', CSSUnparsedValue); // <url> has no CSSStyleValue subclass yet.
}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for *'); // <transform-function> has no CSSStyleValue subclass yet.
// StylePropertyMap.append
// It is not allowed to append CSSStyleValues to custom properties, even
// when the string matches the syntax of the custom property.
test_specified_maps(function(map){
verify_map_no_append(map, generate_property('*'), 'a');
}, 'Appending a string to * is not allowed');
test_specified_maps(function(map){
verify_map_no_append(map, generate_property('foo+'), 'foo');
}, 'Appending a string to foo+ is not allowed');
test_specified_maps(function(map){
verify_map_no_append(map, generate_property('<angle>+'), '10deg');
}, 'Appending a string to <angle>+ is not allowed');
test_specified_maps(function(map){
verify_map_no_append(map, generate_property('<color>+'), 'red');
}, 'Appending a string to <color>+ is not allowed');
test_specified_maps(function(map){
verify_map_no_append(map, generate_property('<custom-ident>+'), 'foo');
}, 'Appending a string to <custom-ident>+ is not allowed');
test_specified_maps(function(map){
verify_map_no_append(map, generate_property('<image>+'), 'url(a)');
}, 'Appending a string to <image>+ is not allowed');
test_specified_maps(function(map){
verify_map_no_append(map, generate_property('<integer>+'), 'a');
}, 'Appending a string to <integer>+ is not allowed');
test_specified_maps(function(map){
verify_map_no_append(map, generate_property('<length-percentage>+'), 'calc(10*% + 10px)');
}, 'Appending a string to <length-percentage>+ is not allowed');
test_specified_maps(function(map){
verify_map_no_append(map, generate_property('<length>+'), '10px');
}, 'Appending a string to <length>+ is not allowed');
test_specified_maps(function(map){
verify_map_no_append(map, generate_property('<number>+'), '1.3');
}, 'Appending a string to <number>+ is not allowed');
test_specified_maps(function(map){
verify_map_no_append(map, generate_property('<percentage>+'), '10%');
}, 'Appending a string to <percentage>+ is not allowed');
test_specified_maps(function(map){
verify_map_no_append(map, generate_property('<resolution>+'), '10dpi');
}, 'Appending a string to <resolution>+ is not allowed');
test_specified_maps(function(map){
verify_map_no_append(map, generate_property('<time>+'), '1s');
}, 'Appending a string to <time>+ is not allowed');
test_specified_maps(function(map){
verify_map_no_append(map, generate_property('<transform-function>+'), 'matrix(0, 0, 0, 0, 0, 0)');
}, 'Appending a string to <transform-function>+ is not allowed');
test_specified_maps(function(map){
verify_map_no_append(map, generate_property('<transform-list>'), 'matrix(0, 0, 0, 0, 0, 0)');
}, 'Appending a string to <transform-list> is not allowed');
test_specified_maps(function(map){
verify_map_no_append(map, generate_property('<url>+'), 'url(a)');
}, 'Appending a string to <url>+ is not allowed');
test_specified_maps(function(map){
verify_map_no_append(map, generate_property('<length>#'), '10px');
}, 'Appending a string to <length># is not allowed');
// It is not allowed to append CSSStyleValues to custom properties, even
// when the CSSStyleValue matches the syntax of the custom property.
test_specified_maps(function(map) {
verify_map_no_append(map, generate_property('*'), new CSSKeywordValue('foo'));
}, 'Appending a CSSKeywordValue to * is not allowed');
test_specified_maps(function(map) {
verify_map_no_append(map, generate_property('foo+'), new CSSKeywordValue('foo'));
}, 'Appending a CSSKeywordValue to foo+ is not allowed');
test_specified_maps(function(map) {
verify_map_no_append(map, generate_property('<angle>+'), CSS.deg(10));
}, 'Appending a CSSUnitValue to <angle>+ is not allowed');
test_specified_maps(function(map) {
verify_map_no_append(map, generate_property('<custom-ident>+'), new CSSKeywordValue('foo'));
}, 'Appending a CSSKeywordValue to <custom-ident>+ is not allowed');
test_specified_maps(function(map) {
verify_map_no_append(map, generate_property('<image>+'), image_value);
}, 'Appending a CSSImageValue to <image>+ is not allowed');
test_specified_maps(function(map) {
verify_map_no_append(map, generate_property('<integer>+'), CSS.number(1));
}, 'Appending a CSSUnitValue to <integer>+ is not allowed');
test_specified_maps(function(map) {
verify_map_no_append(map, generate_property('<length-percentage>+'), CSS.px(10));
}, 'Appending a CSSUnitValue to <length-percentage>+ is not allowed');
test_specified_maps(function(map) {
verify_map_no_append(map, generate_property('<length>+'), CSS.px(10));
}, 'Appending a CSSUnitValue to <length>+ is not allowed');
test_specified_maps(function(map) {
verify_map_no_append(map, generate_property('<number>+'), CSS.number(10));
}, 'Appending a CSSUnitValue to <number>+ is not allowed');
test_specified_maps(function(map) {
verify_map_no_append(map, generate_property('<percentage>+'), CSS.percent(10));
}, 'Appending a CSSUnitValue to <percentage>+ is not allowed');
test_specified_maps(function(map) {
verify_map_no_append(map, generate_property('<resolution>+'), CSS.dpi(10));
}, 'Appending a CSSUnitValue to <resolution>+ is not allowed');
test_specified_maps(function(map) {
verify_map_no_append(map, generate_property('<time>+'), CSS.s(10));
}, 'Appending a CSSUnitValue to <time>+ is not allowed');
test_specified_maps(function(map) {
verify_map_no_append(map, generate_property('<transform-list>'), zero_matrix);
}, 'Appending a CSSKeywordValue to <transform-list> is not allowed');
test_specified_maps(function(map) {
verify_map_no_append(map, generate_property('<length>#'), CSS.px(10));
}, 'Appending a CSSUnitValue to <length># is not allowed');
// <color> has no CSSStyleValue subclass yet.
// <url> has no CSSStyleValue subclass yet.
// <transform-function> has no CSSStyleValue subclass yet.
// CSSStyleValue.parse/parseAll
test(function(){ test(function(){
assert_parsed_type(gen_prop('<angle> | fail', 'fail'), '42deg', CSSUnitValue); verify_parsed_type(generate_property('*'), 'while(){}', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSUnitValue for <angle>'); }, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax *', CSSUnparsedValue);
test(function(){ test(function(){
assert_parsed_type(gen_prop('<color> | fail', 'fail'), '#fefefe', CSSStyleValue); verify_parsed_type(generate_property('<angle>'), '42deg', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSStyleValue for <color>'); }, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <angle>');
test(function(){ test(function(){
assert_parsed_type(gen_prop('<custom-ident> | <length>', '10px'), 'none', CSSKeywordValue); verify_parsed_type(generate_property('<color>'), '#fefefe', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSKeywordValue for <custom-ident>'); }, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <color>');
test(function(){ test(function(){
assert_parsed_type(gen_prop('<image> | fail', 'fail'), 'url(thing.png)', CSSImageValue); verify_parsed_type(generate_property('<custom-ident> | <length>'), 'none', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSImageValue for <image> [url]'); }, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <custom-ident> | <length>');
test(function(){ test(function(){
assert_parsed_type(gen_prop('<integer> | fail', 'fail'), '100', CSSUnitValue); verify_parsed_type(generate_property('<image>'), 'url(thing.png)', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSUnitValue for <integer>'); }, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <image>');
test(function(){ test(function(){
assert_parsed_type(gen_prop('<length-percentage> | fail', 'fail'), '10%', CSSUnitValue); verify_parsed_type(generate_property('<integer>'), '100', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSUnitValue for <length-percentage> [%]'); }, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <integer>');
test(function(){ test(function(){
assert_parsed_type(gen_prop('<length-percentage> | fail', 'fail'), '10px', CSSUnitValue); verify_parsed_type(generate_property('<length-percentage>'), '10%', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSUnitValue for <length-percentage> [px]'); }, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <length-percentage> (10%)');
test(function(){ test(function(){
assert_parsed_type(gen_prop('<length-percentage> | fail', 'fail'), 'calc(10px + 10%)', CSSMathSum); verify_parsed_type(generate_property('<length-percentage>'), '10px', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSMathSum for <length-percentage> [px + %]'); }, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <length-percentage> (10px)');
test(function(){ test(function(){
assert_parsed_type(gen_prop('<length> | fail', 'fail'), '10px', CSSUnitValue); verify_parsed_type(generate_property('<length-percentage>'), 'calc(10px + 10%)', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSUnitValue for <length>'); }, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <length-percentage> (calc(10px + 10%))');
test(function(){ test(function(){
assert_parsed_type(gen_prop('<number> | fail', 'fail'), '42', CSSUnitValue); verify_parsed_type(generate_property('<length>'), '10px', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSUnitValue for <number>'); }, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <length>');
test(function(){ test(function(){
assert_parsed_type(gen_prop('<percentage> | fail', 'fail'), '10%', CSSUnitValue); verify_parsed_type(generate_property('<number>'), '42', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSUnitValue for <percentage>'); }, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <number>');
test(function(){ test(function(){
assert_parsed_type(gen_prop('<resolution> | fail', 'fail'), '300dpi', CSSUnitValue); verify_parsed_type(generate_property('<percentage>'), '10%', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSUnitValue for <resolution>'); }, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <percentage>');
test(function(){ test(function(){
assert_parsed_type(gen_prop('<time> | fail', 'fail'), '42s', CSSUnitValue); verify_parsed_type(generate_property('<resolution>'), '300dpi', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSUnitValue for <time>'); }, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <resolution>');
test(function(){ test(function(){
assert_parsed_type(gen_prop('<url> | fail', 'fail'), 'url(a)', CSSStyleValue); verify_parsed_type(generate_property('<time>'), '42s', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSStyleValue for <url>'); }, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <time>');
test(function(){ test(function(){
assert_parsed_type(gen_prop('thing1 | THING2 | <url>', 'url(fail)'), 'THING2', CSSKeywordValue); verify_parsed_type(generate_property('<transform-function>'), 'matrix(0, 0, 0, 0, 0, 0)', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSKeywordValue for ident'); }, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <transform-function>');
test(function(){ test(function(){
assert_parsed_type(gen_prop('<length>+ | fail', 'fail'), '10px 20px', CSSUnitValue); verify_parsed_type(generate_property('<transform-list>'), 'matrix(0, 0, 0, 0, 0, 0)', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns list of CSSUnitValues for <length>+'); }, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <transform-list>');
test(function(){ test(function(){
assert_parsed_type(gen_prop('<length># | fail', 'fail'), '10px, 20px', CSSUnitValue); verify_parsed_type(generate_property('<url>'), 'url(a)', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns list of CSSUnitValues for <length>#'); }, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <url>');
// Direct CSSStyleValue objects: test(function(){
verify_parsed_type(generate_property('thing1 | THING2 | <url>'), 'THING2', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax thing1 | THING2 | <url>');
function gen_all_props() { test(function(){
return [ verify_parsed_type(generate_property('<length>+'), '10px 20px', CSSUnparsedValue);
gen_prop('*', 'foo'), }, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <length>+');
gen_prop('foo', 'foo'),
gen_prop('<angle>', '0deg'),
gen_prop('<color>', 'rgb(1, 2, 3)'),
gen_prop('<custom-ident>', 'thing'),
gen_prop('<image>', 'url(a)'),
gen_prop('<integer>', '0'),
gen_prop('<length-percentage>', 'calc(10px + 10%)'),
gen_prop('<length>', '0px'),
gen_prop('<number>', '0.5'),
gen_prop('<percentage>', '0%'),
gen_prop('<resolution>', '0dpi'),
gen_prop('<time>', '0s'),
gen_prop('<transform-function>', 'rotateX(0deg)'),
gen_prop('<transform-list>', 'rotateX(0deg)'),
gen_prop('<url>', 'url(a)')
];
}
test(function(){ test(function(){
let props0 = gen_all_props(); verify_parsed_type(generate_property('<length>#'), '10px, 20px', CSSUnparsedValue);
let props1 = gen_all_props(); }, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <length>#');
// Direct CSSStyleValue objects:
for (let i = 0; i < props0.length; i++) { test_specified_maps(function(map){
let prop0 = props0[i]; for (let syntax of all_syntaxes()) {
let prop1 = props1[i]; let name = generate_property(syntax);
// Abuse computedStyleMap to get the initialValue (just to get some let initialValue = target.computedStyleMap().get(name);
// value that will parse for prop0/1's syntax).
let initialValue = target.computedStyleMap().get(prop0);
// We only care about direct CSSStyleValue instances in this test. // We only care about direct CSSStyleValue instances in this test.
// Ultimately, in some future version of CSS TypedOM, we may have no // Ultimately, in some future version of CSS TypedOM, we may have no
...@@ -881,139 +852,142 @@ test(function(){ ...@@ -881,139 +852,142 @@ test(function(){
continue; continue;
} }
let value = CSSStyleValue.parse(prop0, initialValue.toString()); // Verify that direct CSSStyleValues are rejected by set(). Two things
// should prevent this: 1) direct CSSStyleValues are not
// CSSUnparsedValues, and 2) direct CSSStyleValues are only valid for
// the property they were reified from.
verify_map_not_set(map, generate_property(syntax), initialValue);
}
}, 'Direct CSSStyleValue may not be set');
// A value parsed for prop0 must be assignable to prop0. // StylePropertyMap iteration
target.attributeStyleMap.clear();
target.attributeStyleMap.set(prop0, value); // Don't throw.
// A value parsed for prop0 must not be assignable to prop1, even if test_specified_maps(function(map){
// the properties have compatible syntaxes. verify_map_iteration_unparsed(map, generate_property('*'), 'foo');
assert_throws(new TypeError(), () => { }, 'Specified * is reified CSSUnparsedValue by iterator');
target.attributeStyleMap.clear();
target.attributeStyleMap.set(prop1, value); test_specified_maps(function(map){
}); verify_map_iteration_unparsed(map, generate_property('foo'), 'foo');
} }, 'Specified foo is reified CSSUnparsedValue by iterator');
}, 'Direct CSSStyleValue instances are tied to their associated property');
test_specified_maps(function(map){
verify_map_iteration_unparsed(map, generate_property('<angle>'), '10deg');
}, 'Specified <angle> is reified CSSUnparsedValue by iterator');
test_specified_maps(function(map){
verify_map_iteration_unparsed(map, generate_property('<color>'), 'green');
}, 'Specified <color> is reified CSSUnparsedValue by iterator');
test_specified_maps(function(map){
verify_map_iteration_unparsed(map, generate_property('<custom-ident>'), 'foo');
}, 'Specified <custom-ident> is reified CSSUnparsedValue by iterator');
test_specified_maps(function(map){
verify_map_iteration_unparsed(map, generate_property('<image>'), 'url("a")');
}, 'Specified <image> is reified CSSUnparsedValue by iterator');
test_specified_maps(function(map){
verify_map_iteration_unparsed(map, generate_property('<integer>'), '1');
}, 'Specified <integer> is reified CSSUnparsedValue by iterator');
test_specified_maps(function(map){
verify_map_iteration_unparsed(map, generate_property('<length-percentage>'), 'calc(10% + 10px)');
}, 'Specified <length-percentage> is reified CSSUnparsedValue by iterator');
test_specified_maps(function(map){
verify_map_iteration_unparsed(map, generate_property('<length>'), '10px');
}, 'Specified <length> is reified CSSUnparsedValue by iterator');
test_specified_maps(function(map){
verify_map_iteration_unparsed(map, generate_property('<number>'), '1');
}, 'Specified <number> is reified CSSUnparsedValue by iterator');
test_specified_maps(function(map){
verify_map_iteration_unparsed(map, generate_property('<percentage>'), '10%');
}, 'Specified <percentage> is reified CSSUnparsedValue by iterator');
test_specified_maps(function(map){
verify_map_iteration_unparsed(map, generate_property('<resolution>'), '10dpi');
}, 'Specified <resolution> is reified CSSUnparsedValue by iterator');
test_specified_maps(function(map){
verify_map_iteration_unparsed(map, generate_property('<time>'), '1s');
}, 'Specified <time> is reified CSSUnparsedValue by iterator');
test_specified_maps(function(map){
verify_map_iteration_unparsed(map, generate_property('<transform-function>'), 'matrix(0, 0, 0, 0, 0, 0)');
}, 'Specified <transform-function> is reified CSSUnparsedValue by iterator');
test_specified_maps(function(map){
verify_map_iteration_unparsed(map, generate_property('<transform-list>'), 'matrix(0, 0, 0, 0, 0, 0)');
}, 'Specified <transform-list> is reified CSSUnparsedValue by iterator');
test_specified_maps(function(map){
verify_map_iteration_unparsed(map, generate_property('<url>'), 'url("a")');
}, 'Specified <url> is reified CSSUnparsedValue by iterator');
test_specified_maps(function(map){
verify_map_iteration_unparsed(map, generate_property('<length>+'), '10px 11px');
}, 'Specified <length>+ is reified CSSUnparsedValue by iterator');
test_specified_maps(function(map){
verify_map_iteration_unparsed(map, generate_property('<length>#'), '10px, 11px');
}, 'Specified <length># is reified CSSUnparsedValue by iterator');
// StylePropertyMapReadOnly iteration // StylePropertyMapReadOnly iteration
test(function(){ test(function(){
let name = gen_prop('<length>', '10px'); let name = generate_property({syntax: '<length>', initialValue: '10px'});
let result = Array.from(target.computedStyleMap()).filter(e => e[0] == name)[0]; let result = Array.from(target.computedStyleMap()).filter(e => e[0] == name)[0];
assert_true(typeof(result) !== 'undefined'); assert_true(typeof(result) !== 'undefined');
}, 'Registered property with initial value show up on iteration of computedStyleMap'); }, 'Registered property with initial value show up on iteration of computedStyleMap');
// Verifies that iterating a StylePropertyMap[ReadOnly] yields correctly test(function(){
// typed objects for a given syntax/value. verify_computed_iteration_type(generate_property('*'), 'thing', CSSUnparsedValue);
function test_iteration_type_for_property_map(propertyMapName, propertyMap, options) { }, 'Computed * is reified as CSSUnparsedValue by iterator');
test(function(){
let name = gen_prop(options.syntax, options.initialValue);
if (propertyMap instanceof StylePropertyMap) {
// Only set the value if the propertyMap is mutable.
propertyMap.set(name, options.value);
}
let result = Array.from(propertyMap).filter(e => e[0] == name)[0];
let value = result[1];
assert_true(options.expect(value));
}, `Iteration on ${propertyMapName} produces correct type for ${options.syntax}`);
}
function test_iteration_type(options) {
test_iteration_type_for_property_map('computedStyleMap', target.computedStyleMap(), options);
test_iteration_type_for_property_map('attributeStyleMap', target.attributeStyleMap, options);
test_iteration_type_for_property_map('styleMap', style.sheet.rules[0].styleMap, options);
}
test_iteration_type({
syntax: '*',
initialValue: 'none',
value: 'thing',
expect: v => v.length == 1 && v[0] instanceof CSSUnparsedValue,
});
test_iteration_type({
syntax: '<angle>',
initialValue: '0deg',
value: '42deg',
expect: v => v.length == 1 && v[0] instanceof CSSUnitValue,
});
test_iteration_type({ test(function(){
syntax: '<custom-ident>', verify_computed_iteration_type(generate_property('<angle>'), '42deg', CSSUnitValue);
initialValue: 'none', }, 'Computed <angle> is reified as CSSUnitValue by iterator');
value: 'thing',
expect: v => v.length == 1 && v[0] instanceof CSSKeywordValue,
});
test_iteration_type({ test(function(){
syntax: '<image>', verify_computed_iteration_type(generate_property('<custom-ident>'), 'thing', CSSKeywordValue);
initialValue: 'url(a)', }, 'Computed <custom-ident> is reified as CSSKeywordValue by iterator');
value: 'url(b)',
expect: v => v.length == 1 && v[0] instanceof CSSImageValue,
});
test_iteration_type({ test(function(){
syntax: '<integer>', verify_computed_iteration_type(generate_property('<image>'), 'url(\"a\")', CSSImageValue);
initialValue: '0', }, 'Computed <image> is reified as CSSImageValue by iterator');
value: '100',
expect: v => v.length == 1 && v[0] instanceof CSSUnitValue,
});
test_iteration_type({ test(function(){
syntax: '<length>', verify_computed_iteration_type(generate_property('<integer>'), '100', CSSUnitValue);
initialValue: '0px', }, 'Computed <integer> is reified as CSSUnitValue by iterator');
value: '10px',
expect: v => v.length == 1 && v[0] instanceof CSSUnitValue,
});
test_iteration_type({ test(function(){
syntax: '<number>', verify_computed_iteration_type(generate_property('<length>'), '10px', CSSUnitValue);
initialValue: '0', }, 'Computed <length> is reified as CSSUnitValue by iterator');
value: '42',
expect: v => v.length == 1 && v[0] instanceof CSSUnitValue,
});
test_iteration_type({ test(function(){
syntax: '<percentage>', verify_computed_iteration_type(generate_property('<number>'), '42', CSSUnitValue);
initialValue: '0%', }, 'Computed <number> is reified as CSSUnitValue by iterator');
value: '10%',
expect: v => v.length == 1 && v[0] instanceof CSSUnitValue,
});
test_iteration_type({ test(function(){
syntax: '<resolution>', verify_computed_iteration_type(generate_property('<percentage>'), '10%', CSSUnitValue);
initialValue: '0dpi', }, 'Computed <percentage> is reified as CSSUnitValue by iterator');
value: '300dpi',
expect: v => v.length == 1 && v[0] instanceof CSSUnitValue,
});
test_iteration_type({ test(function(){
syntax: '<time>', verify_computed_iteration_type(generate_property('<resolution>'), '300dppx', CSSUnitValue);
initialValue: '0s', }, 'Computed <resolution> is reified as CSSUnitValue by iterator');
value: '10s',
expect: v => v.length == 1 && v[0] instanceof CSSUnitValue,
});
test_iteration_type({ test(function(){
syntax: '<url>', verify_computed_iteration_type(generate_property('<time>'), '10s', CSSUnitValue);
initialValue: 'url(a)', }, 'Computed <time> is reified as CSSUnitValue by iterator');
value: 'url(b)',
expect: v => v.length == 1 && v[0].constructor === CSSStyleValue,
});
test_iteration_type({ test(function(){
syntax: 'none | thing | THING', verify_computed_iteration_type(generate_property('none | thing | THING'), 'THING', CSSKeywordValue);
initialValue: 'none', }, 'Computed none | thing | THING is reified as CSSKeywordValue by iterator');
value: 'THING',
expect: v => v.length == 1 && v[0] instanceof CSSKeywordValue,
});
test_iteration_type({ test(function(){
syntax: '<angle> | <length>', verify_computed_iteration_type(generate_property('<angle> | <length>'), '10px', CSSUnitValue);
initialValue: '0deg', }, 'Computed <angle> | <length> is reified as CSSUnitValue by iterator');
value: '10px',
expect: v => v.length == 1 && v[0] instanceof CSSUnitValue,
});
</script> </script>
...@@ -22,23 +22,23 @@ CSS.registerProperty({ ...@@ -22,23 +22,23 @@ CSS.registerProperty({
test(() => { test(() => {
let value = CSSStyleValue.parse('--tf', 'translateX(0px)'); let value = CSSStyleValue.parse('--tf', 'translateX(0px)');
assert_equals(value.constructor, CSSStyleValue); assert_true(value instanceof CSSUnparsedValue);
}, 'Result of CSSStyleValue.parse for <transform-function> is a direct CSSStyleValue'); }, 'Result of CSSStyleValue.parse for <transform-function> is a CSSUnparsedValue');
test(() => { test(() => {
target.style = '--tf: translateX(0px)'; target.style = '--tf: translateX(0px)';
let value = target.attributeStyleMap.get('--tf'); let value = target.attributeStyleMap.get('--tf');
assert_equals(value.constructor, CSSStyleValue); assert_true(value instanceof CSSUnparsedValue);
target.style = ''; target.style = '';
}, 'Result of attributeStyleMap.get for <transform-function> is a direct CSSStyleValue'); }, 'Result of attributeStyleMap.get for <transform-function> is a CSSUnparsedValue');
test(() => { test(() => {
style.textContent = '#target { --tf: translateX(0px); }'; style.textContent = '#target { --tf: translateX(0px); }';
let styleMap = style.sheet.rules[0].styleMap; let styleMap = style.sheet.rules[0].styleMap;
let value = styleMap.get('--tf'); let value = styleMap.get('--tf');
assert_equals(value.constructor, CSSStyleValue); assert_true(value instanceof CSSUnparsedValue);
style.textContent = ''; style.textContent = '';
}, 'Result of styleMap.get for <transform-function> is a direct CSSStyleValue'); }, 'Result of styleMap.get for <transform-function> is a CSSUnparsedValue');
test(() => { test(() => {
let value = target.computedStyleMap().get('--tf'); let value = target.computedStyleMap().get('--tf');
......
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