Commit 88444780 authored by Darren Shen's avatar Darren Shen Committed by Commit Bot

[css-typed-om] Implement CSSNumericValue matching.

The spec defines what it means for a type to 'match' something. This
patch implements this concept and tests it on CSSPositionValue, which
only accept CSSNumericValues that 'match' a <length-percentage>.

https://drafts.css-houdini.org/css-typed-om-1/#cssnumericvalue-match

Bug: 776173
Change-Id: I4f893fa879da1221b37851a593c20d018794d566
Reviewed-on: https://chromium-review.googlesource.com/788482
Commit-Queue: Darren Shen <shend@chromium.org>
Reviewed-by: default avatarRenée Wright <rjwright@chromium.org>
Cr-Commit-Position: refs/heads/master@{#519956}
parent a1270f58
This is a testharness.js-based test.
PASS Constructing a CSSPositionValue with a keyword throws a TypeError
PASS Constructing a CSSPositionValue with a double throws a TypeError
PASS Constructing a CSSPositionValue with a unitless zero throws a TypeError
PASS Constructing a CSSPositionValue with a string length throws a TypeError
PASS Constructing a CSSPositionValue with a number CSSUnitValue throws a TypeError
PASS Constructing a CSSPositionValue with a time dimension CSSUnitValue throws a TypeError
PASS Constructing a CSSPositionValue with a CSSMathValue of angle type throws a TypeError
PASS Updating CSSKeywordValue.x with a keyword throws a TypeError
PASS Updating CSSKeywordValue.x with a double throws a TypeError
PASS Updating CSSKeywordValue.x with a unitless zero throws a TypeError
PASS Updating CSSKeywordValue.x with a string length throws a TypeError
PASS Updating CSSKeywordValue.x with a number CSSUnitValue throws a TypeError
PASS Updating CSSKeywordValue.x with a time dimension CSSUnitValue throws a TypeError
PASS Updating CSSKeywordValue.x with a CSSMathValue of angle type throws a TypeError
PASS Updating CSSKeywordValue.y with a keyword throws a TypeError
PASS Updating CSSKeywordValue.y with a double throws a TypeError
PASS Updating CSSKeywordValue.y with a unitless zero throws a TypeError
PASS Updating CSSKeywordValue.y with a string length throws a TypeError
PASS Updating CSSKeywordValue.y with a number CSSUnitValue throws a TypeError
PASS Updating CSSKeywordValue.y with a time dimension CSSUnitValue throws a TypeError
PASS Updating CSSKeywordValue.y with a CSSMathValue of angle type throws a TypeError
PASS CSSPositionValue can be constructed from a length CSSUnitValue and a length CSSUnitValue
PASS CSSPositionValue can be constructed from a length CSSUnitValue and a percent CSSUnitValue
FAIL CSSPositionValue can be constructed from a length CSSUnitValue and a CSSMathValue of length type Failed to construct 'CSSPositionValue': Must pass length or percentage to y in CSSPositionValue
FAIL CSSPositionValue can be constructed from a length CSSUnitValue and a CSSMathValue of percent type Failed to construct 'CSSPositionValue': Must pass length or percentage to y in CSSPositionValue
PASS CSSPositionValue can be constructed from a percent CSSUnitValue and a length CSSUnitValue
PASS CSSPositionValue can be constructed from a percent CSSUnitValue and a percent CSSUnitValue
FAIL CSSPositionValue can be constructed from a percent CSSUnitValue and a CSSMathValue of length type Failed to construct 'CSSPositionValue': Must pass length or percentage to y in CSSPositionValue
FAIL CSSPositionValue can be constructed from a percent CSSUnitValue and a CSSMathValue of percent type Failed to construct 'CSSPositionValue': Must pass length or percentage to y in CSSPositionValue
FAIL CSSPositionValue can be constructed from a CSSMathValue of length type and a length CSSUnitValue Failed to construct 'CSSPositionValue': Must pass length or percentage to x in CSSPositionValue
FAIL CSSPositionValue can be constructed from a CSSMathValue of length type and a percent CSSUnitValue Failed to construct 'CSSPositionValue': Must pass length or percentage to x in CSSPositionValue
FAIL CSSPositionValue can be constructed from a CSSMathValue of length type and a CSSMathValue of length type Failed to construct 'CSSPositionValue': Must pass length or percentage to x in CSSPositionValue
FAIL CSSPositionValue can be constructed from a CSSMathValue of length type and a CSSMathValue of percent type Failed to construct 'CSSPositionValue': Must pass length or percentage to x in CSSPositionValue
FAIL CSSPositionValue can be constructed from a CSSMathValue of percent type and a length CSSUnitValue Failed to construct 'CSSPositionValue': Must pass length or percentage to x in CSSPositionValue
FAIL CSSPositionValue can be constructed from a CSSMathValue of percent type and a percent CSSUnitValue Failed to construct 'CSSPositionValue': Must pass length or percentage to x in CSSPositionValue
FAIL CSSPositionValue can be constructed from a CSSMathValue of percent type and a CSSMathValue of length type Failed to construct 'CSSPositionValue': Must pass length or percentage to x in CSSPositionValue
FAIL CSSPositionValue can be constructed from a CSSMathValue of percent type and a CSSMathValue of percent type Failed to construct 'CSSPositionValue': Must pass length or percentage to x in CSSPositionValue
PASS CSSKeywordValue.x can be updated to a length CSSUnitValue
PASS CSSKeywordValue.x can be updated to a percent CSSUnitValue
FAIL CSSKeywordValue.x can be updated to a CSSMathValue of length type Failed to set the 'x' property on 'CSSPositionValue': Must pass length or percentage to x in CSSPositionValue
FAIL CSSKeywordValue.x can be updated to a CSSMathValue of percent type Failed to set the 'x' property on 'CSSPositionValue': Must pass length or percentage to x in CSSPositionValue
PASS CSSKeywordValue.y can be updated to a length CSSUnitValue
PASS CSSKeywordValue.y can be updated to a percent CSSUnitValue
FAIL CSSKeywordValue.y can be updated to a CSSMathValue of length type Failed to set the 'y' property on 'CSSPositionValue': Must pass length or percentage to y in CSSPositionValue
FAIL CSSKeywordValue.y can be updated to a CSSMathValue of percent type Failed to set the 'y' property on 'CSSPositionValue': Must pass length or percentage to y in CSSPositionValue
Harness: the test ran to completion.
...@@ -31,7 +31,7 @@ for (const attr of ['x', 'y']) { ...@@ -31,7 +31,7 @@ for (const attr of ['x', 'y']) {
const position = new CSSPositionValue(CSS.px(0), CSS.px(0)); const position = new CSSPositionValue(CSS.px(0), CSS.px(0));
assert_throws(new TypeError(), () => position[attr] = value); assert_throws(new TypeError(), () => position[attr] = value);
assert_style_value_equals(position[attr], CSS.px(0)); assert_style_value_equals(position[attr], CSS.px(0));
}, 'Updating CSSKeywordValue.' + attr + ' with ' + desc + ' throws a TypeError'); }, 'Updating CSSPositionValue.' + attr + ' with ' + desc + ' throws a TypeError');
} }
} }
...@@ -58,7 +58,7 @@ for (const attr of ['x', 'y']) { ...@@ -58,7 +58,7 @@ for (const attr of ['x', 'y']) {
const position = new CSSPositionValue(CSS.px(0), CSS.px(0)); const position = new CSSPositionValue(CSS.px(0), CSS.px(0));
position[attr] = value; position[attr] = value;
assert_style_value_equals(position[attr], value); assert_style_value_equals(position[attr], value);
}, 'CSSKeywordValue.' + attr + ' can be updated to ' + desc); }, 'CSSPositionValue.' + attr + ' can be updated to ' + desc);
} }
} }
......
...@@ -123,8 +123,11 @@ CSSNumericValueType CSSNumericValueType::Multiply(CSSNumericValueType type1, ...@@ -123,8 +123,11 @@ CSSNumericValueType CSSNumericValueType::Multiply(CSSNumericValueType type1,
else if (type2.HasPercentHint()) else if (type2.HasPercentHint())
type1.ApplyPercentHint(type2.PercentHint()); type1.ApplyPercentHint(type2.PercentHint());
for (unsigned i = 0; i < kNumBaseTypes; ++i) for (unsigned i = 0; i < kNumBaseTypes; ++i) {
type1.exponents_[i] += type2.exponents_[i]; const auto base_type = static_cast<BaseType>(i);
type1.SetExponent(base_type,
type1.Exponent(base_type) + type2.Exponent(base_type));
}
error = false; error = false;
return type1; return type1;
......
...@@ -46,19 +46,54 @@ class CORE_EXPORT CSSNumericValueType { ...@@ -46,19 +46,54 @@ class CORE_EXPORT CSSNumericValueType {
return exponents_[static_cast<unsigned>(type)]; return exponents_[static_cast<unsigned>(type)];
} }
void SetExponent(BaseType type, int value) { void SetExponent(BaseType type, int new_value) {
DCHECK_LT(type, BaseType::kNumBaseTypes); DCHECK_LT(type, BaseType::kNumBaseTypes);
exponents_[static_cast<unsigned>(type)] = value; int& old_value = exponents_[static_cast<unsigned>(type)];
if (old_value == 0 && new_value != 0)
num_non_zero_entries_++;
else if (old_value != 0 && new_value == 0)
num_non_zero_entries_--;
old_value = new_value;
} }
bool HasPercentHint() const { return has_percent_hint_; } bool HasPercentHint() const { return has_percent_hint_; }
BaseType PercentHint() const { return percent_hint_; } BaseType PercentHint() const { return percent_hint_; }
void ApplyPercentHint(BaseType hint); void ApplyPercentHint(BaseType hint);
bool MatchesBaseType(BaseType base_type) const {
DCHECK_NE(base_type, BaseType::kPercent);
return IsOnlyNonZeroEntry(base_type, 1) && !HasPercentHint();
}
bool MatchesPercentage() const {
return IsOnlyNonZeroEntry(BaseType::kPercent, 1);
}
bool MatchesBaseTypePercentage(BaseType base_type) const {
DCHECK_NE(base_type, BaseType::kPercent);
return IsOnlyNonZeroEntry(base_type, 1) ||
IsOnlyNonZeroEntry(BaseType::kPercent, 1);
}
bool MatchesNumber() const {
return !HasNonZeroEntries() && !HasPercentHint();
}
bool MatchesNumberPercentage() const {
return !HasNonZeroEntries() || IsOnlyNonZeroEntry(BaseType::kPercent, 1);
}
private: private:
bool HasNonZeroEntries() const { return num_non_zero_entries_ > 0; }
bool IsOnlyNonZeroEntry(BaseType base_type, int value) const {
DCHECK_NE(value, 0);
return num_non_zero_entries_ == 1 && Exponent(base_type) == value;
}
std::array<int, kNumBaseTypes> exponents_{}; // zero-initialize std::array<int, kNumBaseTypes> exponents_{}; // zero-initialize
BaseType percent_hint_ = BaseType::kPercent; BaseType percent_hint_ = BaseType::kPercent;
bool has_percent_hint_ = false; bool has_percent_hint_ = false;
unsigned num_non_zero_entries_ = 0;
}; };
} // namespace blink } // namespace blink
......
...@@ -202,4 +202,62 @@ TEST(CSSNumericValueType, MultiplyAddsPowersAndSetsPercentHint) { ...@@ -202,4 +202,62 @@ TEST(CSSNumericValueType, MultiplyAddsPowersAndSetsPercentHint) {
EXPECT_EQ(BaseType::kAngle, result.PercentHint()); EXPECT_EQ(BaseType::kAngle, result.PercentHint());
} }
TEST(CSSNumericValueType, MatchesBaseTypePercentage) {
CSSNumericValueType type;
EXPECT_FALSE(type.MatchesBaseType(BaseType::kLength));
EXPECT_FALSE(type.MatchesBaseTypePercentage(BaseType::kLength));
type.SetExponent(BaseType::kLength, 1);
EXPECT_TRUE(type.MatchesBaseType(BaseType::kLength));
EXPECT_TRUE(type.MatchesBaseTypePercentage(BaseType::kLength));
type.SetExponent(BaseType::kLength, 2);
EXPECT_FALSE(type.MatchesBaseType(BaseType::kLength));
EXPECT_FALSE(type.MatchesBaseTypePercentage(BaseType::kLength));
type.SetExponent(BaseType::kLength, 1);
EXPECT_TRUE(type.MatchesBaseType(BaseType::kLength));
EXPECT_TRUE(type.MatchesBaseTypePercentage(BaseType::kLength));
type.ApplyPercentHint(BaseType::kLength);
EXPECT_FALSE(type.MatchesBaseType(BaseType::kLength));
EXPECT_TRUE(type.MatchesBaseTypePercentage(BaseType::kLength));
}
TEST(CSSNumericValueType, MatchesPercentage) {
CSSNumericValueType type;
EXPECT_FALSE(type.MatchesPercentage());
type.SetExponent(BaseType::kPercent, 1);
EXPECT_TRUE(type.MatchesPercentage());
type.SetExponent(BaseType::kPercent, 2);
EXPECT_FALSE(type.MatchesPercentage());
type.ApplyPercentHint(BaseType::kLength);
EXPECT_FALSE(type.MatchesPercentage());
type.SetExponent(BaseType::kLength, 0);
type.SetExponent(BaseType::kPercent, 1);
EXPECT_TRUE(type.MatchesPercentage());
}
TEST(CSSNumericValueType, MatchesNumberPercentage) {
CSSNumericValueType type;
EXPECT_TRUE(type.MatchesNumber());
EXPECT_TRUE(type.MatchesNumberPercentage());
type.SetExponent(BaseType::kLength, 1);
EXPECT_FALSE(type.MatchesNumber());
EXPECT_FALSE(type.MatchesNumberPercentage());
type.SetExponent(BaseType::kLength, 0);
EXPECT_TRUE(type.MatchesNumber());
EXPECT_TRUE(type.MatchesNumberPercentage());
type.SetExponent(BaseType::kPercent, 1);
EXPECT_FALSE(type.MatchesNumber());
EXPECT_TRUE(type.MatchesNumberPercentage());
}
} // namespace blink } // namespace blink
...@@ -10,17 +10,24 @@ ...@@ -10,17 +10,24 @@
namespace blink { namespace blink {
namespace {
bool IsValidCoordinate(CSSNumericValue* v) {
return v->Type().MatchesBaseTypePercentage(
CSSNumericValueType::BaseType::kLength);
}
} // namespace
CSSPositionValue* CSSPositionValue::Create(CSSNumericValue* x, CSSPositionValue* CSSPositionValue::Create(CSSNumericValue* x,
CSSNumericValue* y, CSSNumericValue* y,
ExceptionState& exception_state) { ExceptionState& exception_state) {
if (x->GetType() != CSSStyleValue::StyleValueType::kLengthType && if (!IsValidCoordinate(x)) {
x->GetType() != CSSStyleValue::StyleValueType::kPercentType) {
exception_state.ThrowTypeError( exception_state.ThrowTypeError(
"Must pass length or percentage to x in CSSPositionValue"); "Must pass length or percentage to x in CSSPositionValue");
return nullptr; return nullptr;
} }
if (y->GetType() != CSSStyleValue::StyleValueType::kLengthType && if (!IsValidCoordinate(y)) {
y->GetType() != CSSStyleValue::StyleValueType::kPercentType) {
exception_state.ThrowTypeError( exception_state.ThrowTypeError(
"Must pass length or percentage to y in CSSPositionValue"); "Must pass length or percentage to y in CSSPositionValue");
return nullptr; return nullptr;
...@@ -30,8 +37,7 @@ CSSPositionValue* CSSPositionValue::Create(CSSNumericValue* x, ...@@ -30,8 +37,7 @@ CSSPositionValue* CSSPositionValue::Create(CSSNumericValue* x,
void CSSPositionValue::setX(CSSNumericValue* x, void CSSPositionValue::setX(CSSNumericValue* x,
ExceptionState& exception_state) { ExceptionState& exception_state) {
if (x->GetType() != CSSStyleValue::StyleValueType::kLengthType && if (!IsValidCoordinate(x)) {
x->GetType() != CSSStyleValue::StyleValueType::kPercentType) {
exception_state.ThrowTypeError( exception_state.ThrowTypeError(
"Must pass length or percentage to x in CSSPositionValue"); "Must pass length or percentage to x in CSSPositionValue");
return; return;
...@@ -41,8 +47,7 @@ void CSSPositionValue::setX(CSSNumericValue* x, ...@@ -41,8 +47,7 @@ void CSSPositionValue::setX(CSSNumericValue* x,
void CSSPositionValue::setY(CSSNumericValue* y, void CSSPositionValue::setY(CSSNumericValue* y,
ExceptionState& exception_state) { ExceptionState& exception_state) {
if (y->GetType() != CSSStyleValue::StyleValueType::kLengthType && if (!IsValidCoordinate(y)) {
y->GetType() != CSSStyleValue::StyleValueType::kPercentType) {
exception_state.ThrowTypeError( exception_state.ThrowTypeError(
"Must pass length or percentage to y in CSSPositionValue"); "Must pass length or percentage to y in CSSPositionValue");
return; return;
......
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