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