Skip to content

Commit b11f18c

Browse files
author
ci_lynx
committed
[Feature] Add text decoration extension and textservice support
- Add CSS definitions for text decoration thickness and text decoration width/gap. - Parse and store the new values in computed style, style diff, and prop bundle paths. - Add parser, computed style, style resolver, and prop bundle tests.
1 parent e1c8e2f commit b11f18c

17 files changed

Lines changed: 577 additions & 1 deletion

core/renderer/css/computed_css_style.cc

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3110,6 +3110,70 @@ bool ComputedCSSStyle::SetTextDecorationColor(const tasm::CSSValue& value,
31103110
return old_value_color != text_attributes_->decoration_color;
31113111
}
31123112

3113+
bool ComputedCSSStyle::SetTextDecorationThickness(const tasm::CSSValue& value,
3114+
const bool reset) {
3115+
PrepareOptionalForTextAttributes();
3116+
auto old_value = text_attributes_->text_decoration_thickness;
3117+
if (reset) {
3118+
text_attributes_->text_decoration_thickness.reset();
3119+
} else {
3120+
float thickness = 0.f;
3121+
CSS_HANDLER_FAIL_IF_NOT(
3122+
CalculateCSSValueToFloat(value, thickness, length_context_,
3123+
parser_configs_, true) &&
3124+
thickness >= 0.f,
3125+
parser_configs_.enable_css_strict_mode, tasm::TYPE_ERROR,
3126+
tasm::CSSProperty::GetPropertyName(
3127+
tasm::kPropertyIDTextDecorationThickness)
3128+
.c_str())
3129+
text_attributes_->text_decoration_thickness = thickness;
3130+
}
3131+
return old_value != text_attributes_->text_decoration_thickness;
3132+
}
3133+
3134+
bool ComputedCSSStyle::SetXTextDecorationWidth(const tasm::CSSValue& value,
3135+
const bool reset) {
3136+
PrepareOptionalForTextAttributes();
3137+
auto old_value = text_attributes_->text_decoration_width;
3138+
if (reset) {
3139+
text_attributes_->text_decoration_width.reset();
3140+
} else {
3141+
float width = 0.f;
3142+
CSS_HANDLER_FAIL_IF_NOT(
3143+
CalculateCSSValueToFloat(value, width, length_context_, parser_configs_,
3144+
true) &&
3145+
width >= 0.f,
3146+
parser_configs_.enable_css_strict_mode, tasm::TYPE_ERROR,
3147+
tasm::CSSProperty::GetPropertyName(
3148+
tasm::kPropertyIDXTextDecorationWidth)
3149+
.c_str())
3150+
3151+
text_attributes_->text_decoration_width = width;
3152+
}
3153+
return old_value != text_attributes_->text_decoration_width;
3154+
}
3155+
3156+
bool ComputedCSSStyle::SetXTextDecorationGap(const tasm::CSSValue& value,
3157+
const bool reset) {
3158+
PrepareOptionalForTextAttributes();
3159+
auto old_value = text_attributes_->text_decoration_gap;
3160+
if (reset) {
3161+
text_attributes_->text_decoration_gap.reset();
3162+
} else {
3163+
float gap = 0.f;
3164+
CSS_HANDLER_FAIL_IF_NOT(
3165+
CalculateCSSValueToFloat(value, gap, length_context_, parser_configs_,
3166+
true) &&
3167+
gap >= 0.f,
3168+
parser_configs_.enable_css_strict_mode, tasm::TYPE_ERROR,
3169+
tasm::CSSProperty::GetPropertyName(tasm::kPropertyIDXTextDecorationGap)
3170+
.c_str())
3171+
3172+
text_attributes_->text_decoration_gap = gap;
3173+
}
3174+
return old_value != text_attributes_->text_decoration_gap;
3175+
}
3176+
31133177
bool ComputedCSSStyle::SetZIndex(const tasm::CSSValue& value,
31143178
const bool reset) {
31153179
// If not enable z-index, return false default.
@@ -4348,6 +4412,28 @@ lepus_value ComputedCSSStyle::TextDecorationToLepus() {
43484412
}
43494413
}
43504414

4415+
lepus_value ComputedCSSStyle::TextDecorationThicknessToLepus() {
4416+
if (text_attributes_ &&
4417+
text_attributes_->text_decoration_thickness.has_value()) {
4418+
return lepus_value(*text_attributes_->text_decoration_thickness);
4419+
}
4420+
return lepus_value();
4421+
}
4422+
4423+
lepus_value ComputedCSSStyle::XTextDecorationWidthToLepus() {
4424+
if (text_attributes_ && text_attributes_->text_decoration_width.has_value()) {
4425+
return lepus_value(*text_attributes_->text_decoration_width);
4426+
}
4427+
return lepus_value();
4428+
}
4429+
4430+
lepus_value ComputedCSSStyle::XTextDecorationGapToLepus() {
4431+
if (text_attributes_ && text_attributes_->text_decoration_gap.has_value()) {
4432+
return lepus_value(*text_attributes_->text_decoration_gap);
4433+
}
4434+
return lepus_value();
4435+
}
4436+
43514437
lepus_value ComputedCSSStyle::TextDecorationColorToLepus() {
43524438
if (text_attributes_) {
43534439
return lepus_value(text_attributes_->decoration_color.has_value()
@@ -4948,6 +5034,27 @@ void ComputedCSSStyle::InheritNormalPropertiesFrom(
49485034
from.text_attributes_->text_decoration_color);
49495035
}
49505036
break;
5037+
case tasm::kPropertyIDTextDecorationThickness:
5038+
if (from.text_attributes_.has_value()) {
5039+
auto* attrs = ensure_text_attrs(from.text_attributes_->font_size);
5040+
copy_flex_optional(attrs->text_decoration_thickness,
5041+
from.text_attributes_->text_decoration_thickness);
5042+
}
5043+
break;
5044+
case tasm::kPropertyIDXTextDecorationWidth:
5045+
if (from.text_attributes_.has_value()) {
5046+
auto* attrs = ensure_text_attrs(from.text_attributes_->font_size);
5047+
copy_flex_optional(attrs->text_decoration_width,
5048+
from.text_attributes_->text_decoration_width);
5049+
}
5050+
break;
5051+
case tasm::kPropertyIDXTextDecorationGap:
5052+
if (from.text_attributes_.has_value()) {
5053+
auto* attrs = ensure_text_attrs(from.text_attributes_->font_size);
5054+
copy_flex_optional(attrs->text_decoration_gap,
5055+
from.text_attributes_->text_decoration_gap);
5056+
}
5057+
break;
49515058
case tasm::kPropertyIDTextShadow:
49525059
if (from.text_attributes_.has_value()) {
49535060
auto* attrs = ensure_text_attrs(from.text_attributes_->font_size);
@@ -5312,6 +5419,15 @@ bool ComputedCSSStyle::HasNonDefaultInheritedResolvedValue(
53125419
return text_attributes_.has_value() &&
53135420
text_attributes_->decoration_color.value_or(
53145421
DefaultColor::DEFAULT_COLOR) != DefaultColor::DEFAULT_COLOR;
5422+
case tasm::kPropertyIDTextDecorationThickness:
5423+
return text_attributes_.has_value() &&
5424+
text_attributes_->text_decoration_thickness.has_value();
5425+
case tasm::kPropertyIDXTextDecorationWidth:
5426+
return text_attributes_.has_value() &&
5427+
text_attributes_->text_decoration_width.has_value();
5428+
case tasm::kPropertyIDXTextDecorationGap:
5429+
return text_attributes_.has_value() &&
5430+
text_attributes_->text_decoration_gap.has_value();
53155431
case tasm::kPropertyIDCaretColor:
53165432
return !caret_color_.empty();
53175433
case tasm::kPropertyIDTextIndent:

core/renderer/css/computed_css_style.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,9 @@ class ComputedCSSStyle {
755755
V(TextAlign) \
756756
V(TextOverflow) \
757757
V(TextDecoration) \
758+
V(TextDecorationThickness) \
759+
V(XTextDecorationWidth) \
760+
V(XTextDecorationGap) \
758761
V(TextDecorationColor) \
759762
V(ZIndex) \
760763
V(ImageRendering) \

core/renderer/css/computed_css_style_unittest.cc

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,58 @@ TEST(ComputedCSSStyleTest, ResetClearsOptionalStateAndDirtyBits) {
8383
EXPECT_TRUE(style.IsClean());
8484
}
8585

86+
TEST(ComputedCSSStyleTest, StoresTextDecorationExtensionValues) {
87+
starlight::ComputedCSSStyle style{1.f, 1.f};
88+
89+
EXPECT_TRUE(style.SetValue(CSSPropertyID::kPropertyIDTextDecorationThickness,
90+
CSSValue(2.0, CSSValuePattern::PX), false));
91+
ASSERT_TRUE(style.text_attributes_.has_value());
92+
ASSERT_TRUE(style.text_attributes_->text_decoration_thickness.has_value());
93+
EXPECT_FLOAT_EQ(*style.text_attributes_->text_decoration_thickness, 2.f);
94+
95+
EXPECT_TRUE(style.SetValue(CSSPropertyID::kPropertyIDXTextDecorationWidth,
96+
CSSValue(8.0, CSSValuePattern::PX), false));
97+
ASSERT_TRUE(style.text_attributes_->text_decoration_width.has_value());
98+
EXPECT_FLOAT_EQ(*style.text_attributes_->text_decoration_width, 8.f);
99+
100+
EXPECT_TRUE(style.SetValue(CSSPropertyID::kPropertyIDXTextDecorationGap,
101+
CSSValue(4.0, CSSValuePattern::PX), false));
102+
ASSERT_TRUE(style.text_attributes_->text_decoration_gap.has_value());
103+
EXPECT_FLOAT_EQ(*style.text_attributes_->text_decoration_gap, 4.f);
104+
105+
EXPECT_TRUE(
106+
style.ResetValue(CSSPropertyID::kPropertyIDTextDecorationThickness));
107+
EXPECT_FALSE(style.text_attributes_->text_decoration_thickness.has_value());
108+
109+
EXPECT_TRUE(style.ResetValue(CSSPropertyID::kPropertyIDXTextDecorationWidth));
110+
EXPECT_FALSE(style.text_attributes_->text_decoration_width.has_value());
111+
112+
EXPECT_TRUE(style.ResetValue(CSSPropertyID::kPropertyIDXTextDecorationGap));
113+
EXPECT_FALSE(style.text_attributes_->text_decoration_gap.has_value());
114+
}
115+
116+
TEST(ComputedCSSStyleTest, RejectsInvalidTextDecorationExtensionValues) {
117+
starlight::ComputedCSSStyle style{1.f, 1.f};
118+
119+
EXPECT_FALSE(style.SetValue(CSSPropertyID::kPropertyIDTextDecorationThickness,
120+
CSSValue(-1.0, CSSValuePattern::PX), false));
121+
ASSERT_TRUE(style.text_attributes_.has_value());
122+
EXPECT_FALSE(style.text_attributes_->text_decoration_thickness.has_value());
123+
124+
EXPECT_FALSE(style.SetValue(CSSPropertyID::kPropertyIDXTextDecorationWidth,
125+
CSSValue(-1.0, CSSValuePattern::PX), false));
126+
EXPECT_FALSE(style.text_attributes_->text_decoration_width.has_value());
127+
128+
EXPECT_FALSE(style.SetValue(CSSPropertyID::kPropertyIDXTextDecorationGap,
129+
CSSValue(-1.0, CSSValuePattern::PX), false));
130+
EXPECT_FALSE(style.text_attributes_->text_decoration_gap.has_value());
131+
132+
EXPECT_TRUE(style.SetValue(CSSPropertyID::kPropertyIDXTextDecorationGap,
133+
CSSValue(0.0, CSSValuePattern::PX), false));
134+
ASSERT_TRUE(style.text_attributes_->text_decoration_gap.has_value());
135+
EXPECT_FLOAT_EQ(*style.text_attributes_->text_decoration_gap, 0.f);
136+
}
137+
86138
TEST(ComputedCSSStyleTest, IsPlatformPropertyMatchesGetterSurface) {
87139
EXPECT_TRUE(starlight::ComputedCSSStyle::IsPlatformProperty(
88140
CSSPropertyID::kPropertyIDOpacity));

core/renderer/css/parser/length_handler_unittest.cc

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,34 @@ TEST(LengthHandler, Process) {
9191
EXPECT_TRUE(css_value.IsPx());
9292
EXPECT_EQ(css_value.GetNumber(), 10);
9393
}
94+
95+
TEST(LengthHandler, TextDecorationPatternLengths) {
96+
StyleMap output;
97+
CSSParserConfigs configs;
98+
99+
EXPECT_TRUE(
100+
UnitHandler::Process(CSSPropertyID::kPropertyIDXTextDecorationWidth,
101+
lepus::Value("8px"), output, configs));
102+
ASSERT_TRUE(output.find(CSSPropertyID::kPropertyIDXTextDecorationWidth) !=
103+
output.end());
104+
EXPECT_TRUE(output[CSSPropertyID::kPropertyIDXTextDecorationWidth].IsPx());
105+
EXPECT_EQ(output[CSSPropertyID::kPropertyIDXTextDecorationWidth].GetNumber(),
106+
8);
107+
108+
EXPECT_TRUE(UnitHandler::Process(CSSPropertyID::kPropertyIDXTextDecorationGap,
109+
lepus::Value("4px"), output, configs));
110+
ASSERT_TRUE(output.find(CSSPropertyID::kPropertyIDXTextDecorationGap) !=
111+
output.end());
112+
EXPECT_TRUE(output[CSSPropertyID::kPropertyIDXTextDecorationGap].IsPx());
113+
EXPECT_EQ(output[CSSPropertyID::kPropertyIDXTextDecorationGap].GetNumber(),
114+
4);
115+
116+
output.clear();
117+
EXPECT_FALSE(
118+
UnitHandler::Process(CSSPropertyID::kPropertyIDXTextDecorationWidth,
119+
lepus::Value("8px 0.5"), output, configs));
120+
EXPECT_TRUE(output.empty());
121+
}
94122
} // namespace test
95123

96124
} // namespace tasm

core/renderer/css/text_attributes.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ void TextAttributes::Apply(const TextAttributes& rhs) {
1717
font_size = rhs.font_size;
1818
color = rhs.color;
1919
decoration_color = rhs.decoration_color;
20+
text_decoration_color = rhs.text_decoration_color;
21+
text_decoration_style = rhs.text_decoration_style;
22+
text_decoration_thickness = rhs.text_decoration_thickness;
23+
text_decoration_width = rhs.text_decoration_width;
24+
text_decoration_gap = rhs.text_decoration_gap;
2025
text_gradient = rhs.text_gradient;
2126
white_space = rhs.white_space;
2227
text_overflow = rhs.text_overflow;

core/renderer/css/text_attributes.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ class TextAttributes {
9494
base::flex_optional<uint32_t> color;
9595
base::flex_optional<uint32_t> decoration_color;
9696
base::flex_optional<uint32_t> text_decoration_color;
97+
base::flex_optional<float> text_decoration_thickness;
98+
base::flex_optional<float> text_decoration_width;
99+
base::flex_optional<float> text_decoration_gap;
97100
uint8_t text_decoration_style{
98101
DefaultComputedStyle::DEFAULT_TEXT_DECORATION_STYLE};
99102
// TODO(linxs) this type has changed.
@@ -134,6 +137,9 @@ class TextAttributes {
134137
letter_spacing == rhs.letter_spacing &&
135138
line_spacing == rhs.line_spacing &&
136139
text_stroke_width == rhs.text_stroke_width &&
140+
text_decoration_thickness == rhs.text_decoration_thickness &&
141+
text_decoration_width == rhs.text_decoration_width &&
142+
text_decoration_gap == rhs.text_decoration_gap &&
137143
auto_font_size_min_size == rhs.auto_font_size_min_size &&
138144
auto_font_size_max_size == rhs.auto_font_size_max_size &&
139145
auto_font_size_step_granularity ==

core/renderer/dom/fiber/text_element_unittest.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ namespace testing {
2828

2929
class TextElementTest : public FiberElementTest {};
3030

31+
TEST(TextPropsTest, TextDecorationExtensionPropertiesAreWanted) {
32+
EXPECT_EQ(IsTextMeasurerWanted(kPropertyIDTextDecorationThickness), 1);
33+
EXPECT_EQ(IsTextMeasurerWanted(kPropertyIDXTextDecorationWidth), 1);
34+
EXPECT_EQ(IsTextMeasurerWanted(kPropertyIDXTextDecorationGap), 1);
35+
}
36+
3137
TEST_P(TextElementTest, TestInlineText) {
3238
auto config = std::make_shared<PageConfig>();
3339
config->SetEnableFiberArch(true);

core/renderer/dom/fiber/text_props.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ enum TextPropertyKeyID {
3535
kTextPropAutoFontSize = 17,
3636
kTextPropAutoFontSizePresetSizes = 18,
3737
kTextPropAutoFontSizeLineRanges = 19,
38+
kTextPropTextDecorationThickness = 20,
39+
kTextPropTextDecorationWidth = 21,
40+
kTextPropTextDecorationGap = 22,
3841

3942
// attributes
4043
kTextPropTextMaxLine = 99,
@@ -136,6 +139,9 @@ static const uint8_t kUtf8ToUtf16Units[256] = {
136139
V(TextAlign, 1) \
137140
V(VerticalAlign, 1) \
138141
V(TextDecoration, 1) \
142+
V(TextDecorationThickness, 1) \
143+
V(XTextDecorationWidth, 1) \
144+
V(XTextDecorationGap, 1) \
139145
V(Overflow, 1) \
140146
V(OverflowX, 1) \
141147
V(OverflowY, 1) \

core/renderer/dom/style_resolver.cc

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2670,6 +2670,33 @@ bool StyleResolver::ComputeStyleDiff(
26702670
new_style.MarkChanged(kPropertyIDTextDecorationColor);
26712671
changed = true;
26722672
}
2673+
if (n.text_decoration_thickness != o.text_decoration_thickness) {
2674+
if (!n.text_decoration_thickness.has_value() &&
2675+
o.text_decoration_thickness.has_value()) {
2676+
new_style.MarkReset(kPropertyIDTextDecorationThickness);
2677+
} else {
2678+
new_style.MarkChanged(kPropertyIDTextDecorationThickness);
2679+
}
2680+
changed = true;
2681+
}
2682+
if (n.text_decoration_width != o.text_decoration_width) {
2683+
if (!n.text_decoration_width.has_value() &&
2684+
o.text_decoration_width.has_value()) {
2685+
new_style.MarkReset(kPropertyIDXTextDecorationWidth);
2686+
} else {
2687+
new_style.MarkChanged(kPropertyIDXTextDecorationWidth);
2688+
}
2689+
changed = true;
2690+
}
2691+
if (n.text_decoration_gap != o.text_decoration_gap) {
2692+
if (!n.text_decoration_gap.has_value() &&
2693+
o.text_decoration_gap.has_value()) {
2694+
new_style.MarkReset(kPropertyIDXTextDecorationGap);
2695+
} else {
2696+
new_style.MarkChanged(kPropertyIDXTextDecorationGap);
2697+
}
2698+
changed = true;
2699+
}
26732700
changed |= DiffOptionalAndMark(
26742701
new_style, n.text_shadow, o.text_shadow,
26752702
SharedDefaultProvider<

0 commit comments

Comments
 (0)