1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "third_party/blink/renderer/core/animation/css_color_interpolation_type.h"
6
7 #include <memory>
8 #include <utility>
9
10 #include "base/memory/ptr_util.h"
11 #include "third_party/blink/renderer/core/animation/color_property_functions.h"
12 #include "third_party/blink/renderer/core/animation/interpolable_value.h"
13 #include "third_party/blink/renderer/core/css/css_color_value.h"
14 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
15 #include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
16 #include "third_party/blink/renderer/core/layout/layout_theme.h"
17 #include "third_party/blink/renderer/core/style/computed_style.h"
18
19 namespace blink {
20
21 enum InterpolableColorIndex : unsigned {
22 kRed,
23 kGreen,
24 kBlue,
25 kAlpha,
26 kCurrentcolor,
27 kWebkitActivelink,
28 kWebkitLink,
29 kQuirkInherit,
30 kInterpolableColorIndexCount,
31 };
32
CreateInterpolableColorForIndex(InterpolableColorIndex index)33 static std::unique_ptr<InterpolableValue> CreateInterpolableColorForIndex(
34 InterpolableColorIndex index) {
35 DCHECK_LT(index, kInterpolableColorIndexCount);
36 auto list = std::make_unique<InterpolableList>(kInterpolableColorIndexCount);
37 for (unsigned i = 0; i < kInterpolableColorIndexCount; i++)
38 list->Set(i, std::make_unique<InterpolableNumber>(i == index));
39 return std::move(list);
40 }
41
42 std::unique_ptr<InterpolableValue>
CreateInterpolableColor(const Color & color)43 CSSColorInterpolationType::CreateInterpolableColor(const Color& color) {
44 auto list = std::make_unique<InterpolableList>(kInterpolableColorIndexCount);
45 list->Set(kRed,
46 std::make_unique<InterpolableNumber>(color.Red() * color.Alpha()));
47 list->Set(kGreen, std::make_unique<InterpolableNumber>(color.Green() *
48 color.Alpha()));
49 list->Set(kBlue,
50 std::make_unique<InterpolableNumber>(color.Blue() * color.Alpha()));
51 list->Set(kAlpha, std::make_unique<InterpolableNumber>(color.Alpha()));
52 list->Set(kCurrentcolor, std::make_unique<InterpolableNumber>(0));
53 list->Set(kWebkitActivelink, std::make_unique<InterpolableNumber>(0));
54 list->Set(kWebkitLink, std::make_unique<InterpolableNumber>(0));
55 list->Set(kQuirkInherit, std::make_unique<InterpolableNumber>(0));
56 return std::move(list);
57 }
58
59 std::unique_ptr<InterpolableValue>
CreateInterpolableColor(CSSValueID keyword)60 CSSColorInterpolationType::CreateInterpolableColor(CSSValueID keyword) {
61 switch (keyword) {
62 case CSSValueID::kCurrentcolor:
63 return CreateInterpolableColorForIndex(kCurrentcolor);
64 case CSSValueID::kWebkitActivelink:
65 return CreateInterpolableColorForIndex(kWebkitActivelink);
66 case CSSValueID::kWebkitLink:
67 return CreateInterpolableColorForIndex(kWebkitLink);
68 case CSSValueID::kInternalQuirkInherit:
69 return CreateInterpolableColorForIndex(kQuirkInherit);
70 case CSSValueID::kWebkitFocusRingColor:
71 return CreateInterpolableColor(LayoutTheme::GetTheme().FocusRingColor());
72 default:
73 DCHECK(StyleColor::IsColorKeyword(keyword));
74 // TODO(crbug.com/929098) Need to pass an appropriate color scheme here.
75 return CreateInterpolableColor(StyleColor::ColorFromKeyword(
76 keyword, ComputedStyle::InitialStyle().UsedColorScheme()));
77 }
78 }
79
80 std::unique_ptr<InterpolableValue>
CreateInterpolableColor(const StyleColor & color)81 CSSColorInterpolationType::CreateInterpolableColor(const StyleColor& color) {
82 if (color.IsCurrentColor())
83 return CreateInterpolableColorForIndex(kCurrentcolor);
84 return CreateInterpolableColor(color.GetColor());
85 }
86
87 std::unique_ptr<InterpolableValue>
MaybeCreateInterpolableColor(const CSSValue & value)88 CSSColorInterpolationType::MaybeCreateInterpolableColor(const CSSValue& value) {
89 if (auto* color_value = DynamicTo<cssvalue::CSSColorValue>(value))
90 return CreateInterpolableColor(color_value->Value());
91 auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
92 if (!identifier_value)
93 return nullptr;
94 if (!StyleColor::IsColorKeyword(identifier_value->GetValueID()))
95 return nullptr;
96 return CreateInterpolableColor(identifier_value->GetValueID());
97 }
98
AddPremultipliedColor(double & red,double & green,double & blue,double & alpha,double fraction,const Color & color)99 static void AddPremultipliedColor(double& red,
100 double& green,
101 double& blue,
102 double& alpha,
103 double fraction,
104 const Color& color) {
105 double color_alpha = color.Alpha();
106 red += fraction * color.Red() * color_alpha;
107 green += fraction * color.Green() * color_alpha;
108 blue += fraction * color.Blue() * color_alpha;
109 alpha += fraction * color_alpha;
110 }
111
ResolveInterpolableColor(const InterpolableValue & interpolable_color,const StyleResolverState & state,bool is_visited,bool is_text_decoration)112 Color CSSColorInterpolationType::ResolveInterpolableColor(
113 const InterpolableValue& interpolable_color,
114 const StyleResolverState& state,
115 bool is_visited,
116 bool is_text_decoration) {
117 const auto& list = To<InterpolableList>(interpolable_color);
118 DCHECK_EQ(list.length(), kInterpolableColorIndexCount);
119
120 double red = To<InterpolableNumber>(list.Get(kRed))->Value();
121 double green = To<InterpolableNumber>(list.Get(kGreen))->Value();
122 double blue = To<InterpolableNumber>(list.Get(kBlue))->Value();
123 double alpha = To<InterpolableNumber>(list.Get(kAlpha))->Value();
124
125 if (double currentcolor_fraction =
126 To<InterpolableNumber>(list.Get(kCurrentcolor))->Value()) {
127 auto current_color_getter = is_visited
128 ? ColorPropertyFunctions::GetVisitedColor
129 : ColorPropertyFunctions::GetUnvisitedColor;
130 StyleColor current_style_color = StyleColor::CurrentColor();
131 if (is_text_decoration) {
132 current_style_color =
133 current_color_getter(
134 CSSProperty::Get(CSSPropertyID::kWebkitTextFillColor),
135 *state.Style())
136 .Access();
137 }
138 if (current_style_color.IsCurrentColor()) {
139 current_style_color =
140 current_color_getter(CSSProperty::Get(CSSPropertyID::kColor),
141 *state.Style())
142 .Access();
143 }
144 AddPremultipliedColor(red, green, blue, alpha, currentcolor_fraction,
145 current_style_color.GetColor());
146 }
147 const TextLinkColors& colors = state.GetDocument().GetTextLinkColors();
148 if (double webkit_activelink_fraction =
149 To<InterpolableNumber>(list.Get(kWebkitActivelink))->Value())
150 AddPremultipliedColor(red, green, blue, alpha, webkit_activelink_fraction,
151 colors.ActiveLinkColor());
152 if (double webkit_link_fraction =
153 To<InterpolableNumber>(list.Get(kWebkitLink))->Value())
154 AddPremultipliedColor(
155 red, green, blue, alpha, webkit_link_fraction,
156 is_visited ? colors.VisitedLinkColor() : colors.LinkColor());
157 if (double quirk_inherit_fraction =
158 To<InterpolableNumber>(list.Get(kQuirkInherit))->Value())
159 AddPremultipliedColor(red, green, blue, alpha, quirk_inherit_fraction,
160 colors.TextColor());
161
162 alpha = clampTo<double>(alpha, 0, 255);
163 if (alpha == 0)
164 return Color::kTransparent;
165
166 return MakeRGBA(
167 clampTo<int>(round(red / alpha)), clampTo<int>(round(green / alpha)),
168 clampTo<int>(round(blue / alpha)), clampTo<int>(round(alpha)));
169 }
170
171 class InheritedColorChecker
172 : public CSSInterpolationType::CSSConversionChecker {
173 public:
InheritedColorChecker(const CSSProperty & property,const OptionalStyleColor & color)174 InheritedColorChecker(const CSSProperty& property,
175 const OptionalStyleColor& color)
176 : property_(property), color_(color) {}
177
178 private:
IsValid(const StyleResolverState & state,const InterpolationValue & underlying) const179 bool IsValid(const StyleResolverState& state,
180 const InterpolationValue& underlying) const final {
181 return color_ == ColorPropertyFunctions::GetUnvisitedColor(
182 property_, *state.ParentStyle());
183 }
184
185 const CSSProperty& property_;
186 const OptionalStyleColor color_;
187 };
188
MaybeConvertNeutral(const InterpolationValue &,ConversionCheckers &) const189 InterpolationValue CSSColorInterpolationType::MaybeConvertNeutral(
190 const InterpolationValue&,
191 ConversionCheckers&) const {
192 return ConvertStyleColorPair(StyleColor(Color::kTransparent),
193 StyleColor(Color::kTransparent));
194 }
195
MaybeConvertInitial(const StyleResolverState &,ConversionCheckers & conversion_checkers) const196 InterpolationValue CSSColorInterpolationType::MaybeConvertInitial(
197 const StyleResolverState&,
198 ConversionCheckers& conversion_checkers) const {
199 OptionalStyleColor initial_color =
200 ColorPropertyFunctions::GetInitialColor(CssProperty());
201 if (initial_color.IsNull())
202 return nullptr;
203 return ConvertStyleColorPair(initial_color.Access(), initial_color.Access());
204 }
205
MaybeConvertInherit(const StyleResolverState & state,ConversionCheckers & conversion_checkers) const206 InterpolationValue CSSColorInterpolationType::MaybeConvertInherit(
207 const StyleResolverState& state,
208 ConversionCheckers& conversion_checkers) const {
209 if (!state.ParentStyle())
210 return nullptr;
211 // Visited color can never explicitly inherit from parent visited color so
212 // only use the unvisited color.
213 OptionalStyleColor inherited_color =
214 ColorPropertyFunctions::GetUnvisitedColor(CssProperty(),
215 *state.ParentStyle());
216 conversion_checkers.push_back(
217 std::make_unique<InheritedColorChecker>(CssProperty(), inherited_color));
218 return ConvertStyleColorPair(inherited_color, inherited_color);
219 }
220
221 enum InterpolableColorPairIndex : unsigned {
222 kUnvisited,
223 kVisited,
224 kInterpolableColorPairIndexCount,
225 };
226
MaybeConvertValue(const CSSValue & value,const StyleResolverState * state,ConversionCheckers & conversion_checkers) const227 InterpolationValue CSSColorInterpolationType::MaybeConvertValue(
228 const CSSValue& value,
229 const StyleResolverState* state,
230 ConversionCheckers& conversion_checkers) const {
231 if (CssProperty().PropertyID() == CSSPropertyID::kColor) {
232 auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
233 if (identifier_value &&
234 identifier_value->GetValueID() == CSSValueID::kCurrentcolor) {
235 DCHECK(state);
236 return MaybeConvertInherit(*state, conversion_checkers);
237 }
238 }
239
240 std::unique_ptr<InterpolableValue> interpolable_color =
241 MaybeCreateInterpolableColor(value);
242 if (!interpolable_color)
243 return nullptr;
244 auto color_pair =
245 std::make_unique<InterpolableList>(kInterpolableColorPairIndexCount);
246 color_pair->Set(kUnvisited, interpolable_color->Clone());
247 color_pair->Set(kVisited, std::move(interpolable_color));
248 return InterpolationValue(std::move(color_pair));
249 }
250
ConvertStyleColorPair(const OptionalStyleColor & unvisited_color,const OptionalStyleColor & visited_color) const251 InterpolationValue CSSColorInterpolationType::ConvertStyleColorPair(
252 const OptionalStyleColor& unvisited_color,
253 const OptionalStyleColor& visited_color) const {
254 if (unvisited_color.IsNull() || visited_color.IsNull()) {
255 return nullptr;
256 }
257 auto color_pair =
258 std::make_unique<InterpolableList>(kInterpolableColorPairIndexCount);
259 color_pair->Set(kUnvisited,
260 CreateInterpolableColor(unvisited_color.Access()));
261 color_pair->Set(kVisited, CreateInterpolableColor(visited_color.Access()));
262 return InterpolationValue(std::move(color_pair));
263 }
264
265 InterpolationValue
MaybeConvertStandardPropertyUnderlyingValue(const ComputedStyle & style) const266 CSSColorInterpolationType::MaybeConvertStandardPropertyUnderlyingValue(
267 const ComputedStyle& style) const {
268 return ConvertStyleColorPair(
269 ColorPropertyFunctions::GetUnvisitedColor(CssProperty(), style),
270 ColorPropertyFunctions::GetVisitedColor(CssProperty(), style));
271 }
272
ApplyStandardPropertyValue(const InterpolableValue & interpolable_value,const NonInterpolableValue *,StyleResolverState & state) const273 void CSSColorInterpolationType::ApplyStandardPropertyValue(
274 const InterpolableValue& interpolable_value,
275 const NonInterpolableValue*,
276 StyleResolverState& state) const {
277 const auto& color_pair = To<InterpolableList>(interpolable_value);
278 DCHECK_EQ(color_pair.length(), kInterpolableColorPairIndexCount);
279 ColorPropertyFunctions::SetUnvisitedColor(
280 CssProperty(), *state.Style(),
281 ResolveInterpolableColor(
282 *color_pair.Get(kUnvisited), state, false,
283 CssProperty().PropertyID() == CSSPropertyID::kTextDecorationColor));
284 ColorPropertyFunctions::SetVisitedColor(
285 CssProperty(), *state.Style(),
286 ResolveInterpolableColor(
287 *color_pair.Get(kVisited), state, true,
288 CssProperty().PropertyID() == CSSPropertyID::kTextDecorationColor));
289 }
290
CreateCSSValue(const InterpolableValue & interpolable_value,const NonInterpolableValue *,const StyleResolverState & state) const291 const CSSValue* CSSColorInterpolationType::CreateCSSValue(
292 const InterpolableValue& interpolable_value,
293 const NonInterpolableValue*,
294 const StyleResolverState& state) const {
295 const auto& color_pair = To<InterpolableList>(interpolable_value);
296 Color color = ResolveInterpolableColor(*color_pair.Get(kUnvisited), state);
297 return cssvalue::CSSColorValue::Create(color.Rgb());
298 }
299
Composite(UnderlyingValueOwner & underlying_value_owner,double underlying_fraction,const InterpolationValue & value,double interpolation_fraction) const300 void CSSColorInterpolationType::Composite(
301 UnderlyingValueOwner& underlying_value_owner,
302 double underlying_fraction,
303 const InterpolationValue& value,
304 double interpolation_fraction) const {
305 DCHECK(!underlying_value_owner.Value().non_interpolable_value);
306 DCHECK(!value.non_interpolable_value);
307 auto& underlying_list = To<InterpolableList>(
308 *underlying_value_owner.MutableValue().interpolable_value);
309 const auto& other_list = To<InterpolableList>(*value.interpolable_value);
310 // Both lists should have kUnvisited and kVisited.
311 DCHECK(underlying_list.length() == kInterpolableColorPairIndexCount);
312 DCHECK(other_list.length() == kInterpolableColorPairIndexCount);
313 for (wtf_size_t i = 0; i < underlying_list.length(); i++) {
314 auto& underlying = To<InterpolableList>(*underlying_list.GetMutable(i));
315 const auto& other = To<InterpolableList>(*other_list.Get(i));
316 DCHECK(underlying.length() == kInterpolableColorIndexCount);
317 DCHECK(other.length() == kInterpolableColorIndexCount);
318 for (wtf_size_t j = 0; j < underlying.length(); j++) {
319 DCHECK(underlying.Get(j)->IsNumber());
320 DCHECK(other.Get(j)->IsNumber());
321 auto& underlying_number =
322 To<InterpolableNumber>(*underlying.GetMutable(j));
323 const auto& other_number = To<InterpolableNumber>(*other.Get(j));
324 if (j != kAlpha || underlying_number.Value() != other_number.Value())
325 underlying_number.ScaleAndAdd(underlying_fraction, other_number);
326 }
327 }
328 }
329
330 } // namespace blink
331