1/*
2 * Copyright (C) 2007, 2008, 2009, 2013, 2016 Apple Inc. All rights reserved.
3 * Copyright (C) 2012, 2013 Adobe Systems Incorporated. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of Apple Inc. ("Apple") nor the names of
15 * its contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include "config.h"
31#include "CSSPropertyAnimation.h"
32
33#include "CSSComputedStyleDeclaration.h"
34#include "CSSCrossfadeValue.h"
35#include "CSSFilterImageValue.h"
36#include "CSSImageGeneratorValue.h"
37#include "CSSImageValue.h"
38#include "CSSPrimitiveValue.h"
39#include "CSSPropertyBlendingClient.h"
40#include "CSSPropertyNames.h"
41#include "CachedImage.h"
42#include "CalculationValue.h"
43#include "ClipPathOperation.h"
44#include "FloatConversion.h"
45#include "FontCascade.h"
46#include "FontSelectionAlgorithm.h"
47#include "FontTaggedSettings.h"
48#include "GapLength.h"
49#include "IdentityTransformOperation.h"
50#include "Logging.h"
51#include "Matrix3DTransformOperation.h"
52#include "MatrixTransformOperation.h"
53#include "RenderBox.h"
54#include "RenderStyle.h"
55#include "StyleCachedImage.h"
56#include "StyleGeneratedImage.h"
57#include "StylePropertyShorthand.h"
58#include "StyleResolver.h"
59#include <algorithm>
60#include <memory>
61#include <wtf/MathExtras.h>
62#include <wtf/NeverDestroyed.h>
63#include <wtf/Noncopyable.h>
64#include <wtf/PointerComparison.h>
65#include <wtf/RefCounted.h>
66#include <wtf/text/TextStream.h>
67
68namespace WebCore {
69
70static inline int blendFunc(const CSSPropertyBlendingClient*, int from, int to, double progress)
71{
72 return blend(from, to, progress);
73}
74
75static inline double blendFunc(const CSSPropertyBlendingClient*, double from, double to, double progress)
76{
77 return blend(from, to, progress);
78}
79
80static inline float blendFunc(const CSSPropertyBlendingClient*, float from, float to, double progress)
81{
82 return narrowPrecisionToFloat(from + (to - from) * progress);
83}
84
85static inline Color blendFunc(const CSSPropertyBlendingClient*, const Color& from, const Color& to, double progress)
86{
87 return blend(from, to, progress);
88}
89
90static inline Length blendFunc(const CSSPropertyBlendingClient*, const Length& from, const Length& to, double progress)
91{
92 return blend(from, to, progress);
93}
94
95static inline GapLength blendFunc(const CSSPropertyBlendingClient*, const GapLength& from, const GapLength& to, double progress)
96{
97 return (from.isNormal() || to.isNormal()) ? to : blend(from.length(), to.length(), progress);
98}
99
100static inline LengthSize blendFunc(const CSSPropertyBlendingClient* anim, const LengthSize& from, const LengthSize& to, double progress)
101{
102 return { blendFunc(anim, from.width, to.width, progress), blendFunc(anim, from.height, to.height, progress) };
103}
104
105static inline ShadowStyle blendFunc(const CSSPropertyBlendingClient* anim, ShadowStyle from, ShadowStyle to, double progress)
106{
107 if (from == to)
108 return to;
109
110 double fromVal = from == Normal ? 1 : 0;
111 double toVal = to == Normal ? 1 : 0;
112 double result = blendFunc(anim, fromVal, toVal, progress);
113 return result > 0 ? Normal : Inset;
114}
115
116static inline std::unique_ptr<ShadowData> blendFunc(const CSSPropertyBlendingClient* anim, const ShadowData* from, const ShadowData* to, double progress)
117{
118 ASSERT(from && to);
119 if (from->style() != to->style())
120 return std::make_unique<ShadowData>(*to);
121
122 return std::make_unique<ShadowData>(blend(from->location(), to->location(), progress),
123 blend(from->radius(), to->radius(), progress),
124 blend(from->spread(), to->spread(), progress),
125 blendFunc(anim, from->style(), to->style(), progress),
126 from->isWebkitBoxShadow(),
127 blend(from->color(), to->color(), progress));
128}
129
130static inline TransformOperations blendFunc(const CSSPropertyBlendingClient* animation, const TransformOperations& from, const TransformOperations& to, double progress)
131{
132 if (animation->transformFunctionListsMatch())
133 return to.blendByMatchingOperations(from, progress);
134 return to.blendByUsingMatrixInterpolation(from, progress, is<RenderBox>(animation->renderer()) ? downcast<RenderBox>(*animation->renderer()).borderBoxRect().size() : LayoutSize());
135}
136
137static inline RefPtr<ClipPathOperation> blendFunc(const CSSPropertyBlendingClient*, ClipPathOperation* from, ClipPathOperation* to, double progress)
138{
139 if (!from || !to)
140 return to;
141
142 // Other clip-path operations than BasicShapes can not be animated.
143 if (from->type() != ClipPathOperation::Shape || to->type() != ClipPathOperation::Shape)
144 return to;
145
146 const BasicShape& fromShape = downcast<ShapeClipPathOperation>(*from).basicShape();
147 const BasicShape& toShape = downcast<ShapeClipPathOperation>(*to).basicShape();
148
149 if (!fromShape.canBlend(toShape))
150 return to;
151
152 return ShapeClipPathOperation::create(toShape.blend(fromShape, progress));
153}
154
155static inline RefPtr<ShapeValue> blendFunc(const CSSPropertyBlendingClient*, ShapeValue* from, ShapeValue* to, double progress)
156{
157 if (!from || !to)
158 return to;
159
160 if (from->type() != ShapeValue::Type::Shape || to->type() != ShapeValue::Type::Shape)
161 return to;
162
163 if (from->cssBox() != to->cssBox())
164 return to;
165
166 const BasicShape& fromShape = *from->shape();
167 const BasicShape& toShape = *to->shape();
168
169 if (!fromShape.canBlend(toShape))
170 return to;
171
172 return ShapeValue::create(toShape.blend(fromShape, progress), to->cssBox());
173}
174
175static inline RefPtr<FilterOperation> blendFunc(const CSSPropertyBlendingClient*, FilterOperation* fromOp, FilterOperation* toOp, double progress, bool blendToPassthrough = false)
176{
177 ASSERT(toOp);
178 return toOp->blend(fromOp, progress, blendToPassthrough);
179}
180
181static inline FilterOperations blendFilterOperations(const CSSPropertyBlendingClient* anim, const FilterOperations& from, const FilterOperations& to, double progress)
182{
183 FilterOperations result;
184 size_t fromSize = from.operations().size();
185 size_t toSize = to.operations().size();
186 size_t size = std::max(fromSize, toSize);
187 for (size_t i = 0; i < size; i++) {
188 RefPtr<FilterOperation> fromOp = (i < fromSize) ? from.operations()[i].get() : 0;
189 RefPtr<FilterOperation> toOp = (i < toSize) ? to.operations()[i].get() : 0;
190 RefPtr<FilterOperation> blendedOp = toOp ? blendFunc(anim, fromOp.get(), toOp.get(), progress) : (fromOp ? blendFunc(anim, 0, fromOp.get(), progress, true) : 0);
191 if (blendedOp)
192 result.operations().append(blendedOp);
193 else {
194 auto identityOp = PassthroughFilterOperation::create();
195 if (progress > 0.5)
196 result.operations().append(toOp ? toOp : WTFMove(identityOp));
197 else
198 result.operations().append(fromOp ? fromOp : WTFMove(identityOp));
199 }
200 }
201 return result;
202}
203
204static inline FilterOperations blendFunc(const CSSPropertyBlendingClient* anim, const FilterOperations& from, const FilterOperations& to, double progress, CSSPropertyID propertyID = CSSPropertyFilter)
205{
206 FilterOperations result;
207
208 // If we have a filter function list, use that to do a per-function animation.
209
210 bool listsMatch = false;
211 switch (propertyID) {
212 case CSSPropertyFilter:
213 listsMatch = anim->filterFunctionListsMatch();
214 break;
215#if ENABLE(FILTERS_LEVEL_2)
216 case CSSPropertyWebkitBackdropFilter:
217 listsMatch = anim->backdropFilterFunctionListsMatch();
218 break;
219#endif
220 case CSSPropertyAppleColorFilter:
221 listsMatch = anim->colorFilterFunctionListsMatch();
222 break;
223 default:
224 break;
225 }
226
227 if (listsMatch)
228 result = blendFilterOperations(anim, from, to, progress);
229 else {
230 // If the filter function lists don't match, we could try to cross-fade, but don't yet have a way to represent that in CSS.
231 // For now we'll just fail to animate.
232 result = to;
233 }
234
235 return result;
236}
237
238static inline RefPtr<StyleImage> blendFilter(const CSSPropertyBlendingClient* anim, CachedImage* image, const FilterOperations& from, const FilterOperations& to, double progress)
239{
240 ASSERT(image);
241 FilterOperations filterResult = blendFilterOperations(anim, from, to, progress);
242
243 auto imageValue = CSSImageValue::create(*image);
244 auto filterValue = ComputedStyleExtractor::valueForFilter(anim->currentStyle(), filterResult, DoNotAdjustPixelValues);
245
246 auto result = CSSFilterImageValue::create(WTFMove(imageValue), WTFMove(filterValue));
247 result.get().setFilterOperations(filterResult);
248 return StyleGeneratedImage::create(WTFMove(result));
249}
250
251static inline Visibility blendFunc(const CSSPropertyBlendingClient* anim, Visibility from, Visibility to, double progress)
252{
253 // Any non-zero result means we consider the object to be visible. Only at 0 do we consider the object to be
254 // invisible. The invisible value we use (Visibility::Hidden vs. Visibility::Collapse) depends on the specified from/to values.
255 double fromVal = from == Visibility::Visible ? 1. : 0.;
256 double toVal = to == Visibility::Visible ? 1. : 0.;
257 if (fromVal == toVal)
258 return to;
259 double result = blendFunc(anim, fromVal, toVal, progress);
260 return result > 0. ? Visibility::Visible : (to != Visibility::Visible ? to : from);
261}
262
263static inline TextUnderlineOffset blendFunc(const CSSPropertyBlendingClient* anim, const TextUnderlineOffset& from, const TextUnderlineOffset& to, double progress)
264{
265 if (from.isLength() && to.isLength())
266 return TextUnderlineOffset::createWithLength(blendFunc(anim, from.lengthValue(), to.lengthValue(), progress));
267 return TextUnderlineOffset::createWithAuto();
268}
269
270static inline TextDecorationThickness blendFunc(const CSSPropertyBlendingClient* anim, const TextDecorationThickness& from, const TextDecorationThickness& to, double progress)
271{
272 if (from.isLength() && to.isLength())
273 return TextDecorationThickness::createWithLength(blendFunc(anim, from.lengthValue(), to.lengthValue(), progress));
274 return TextDecorationThickness::createWithAuto();
275}
276
277static inline LengthBox blendFunc(const CSSPropertyBlendingClient* anim, const LengthBox& from, const LengthBox& to, double progress)
278{
279 LengthBox result(blendFunc(anim, from.top(), to.top(), progress),
280 blendFunc(anim, from.right(), to.right(), progress),
281 blendFunc(anim, from.bottom(), to.bottom(), progress),
282 blendFunc(anim, from.left(), to.left(), progress));
283 return result;
284}
285
286static inline SVGLengthValue blendFunc(const CSSPropertyBlendingClient*, const SVGLengthValue& from, const SVGLengthValue& to, double progress)
287{
288 return to.blend(from, narrowPrecisionToFloat(progress));
289}
290
291static inline Vector<SVGLengthValue> blendFunc(const CSSPropertyBlendingClient*, const Vector<SVGLengthValue>& from, const Vector<SVGLengthValue>& to, double progress)
292{
293 size_t fromLength = from.size();
294 size_t toLength = to.size();
295 if (!fromLength)
296 return !progress ? from : to;
297 if (!toLength)
298 return progress == 1 ? from : to;
299 size_t resultLength = fromLength;
300 if (fromLength != toLength) {
301 if (!remainder(std::max(fromLength, toLength), std::min(fromLength, toLength)))
302 resultLength = std::max(fromLength, toLength);
303 else
304 resultLength = fromLength * toLength;
305 }
306 Vector<SVGLengthValue> result(resultLength);
307 for (size_t i = 0; i < resultLength; ++i)
308 result[i] = to[i % toLength].blend(from[i % fromLength], narrowPrecisionToFloat(progress));
309 return result;
310}
311
312static inline RefPtr<StyleImage> crossfadeBlend(const CSSPropertyBlendingClient*, StyleCachedImage* fromStyleImage, StyleCachedImage* toStyleImage, double progress)
313{
314 // If progress is at one of the extremes, we want getComputedStyle to show the image,
315 // not a completed cross-fade, so we hand back one of the existing images.
316 if (!progress)
317 return fromStyleImage;
318 if (progress == 1)
319 return toStyleImage;
320 if (!fromStyleImage->cachedImage() || !toStyleImage->cachedImage())
321 return toStyleImage;
322
323 auto fromImageValue = CSSImageValue::create(*fromStyleImage->cachedImage());
324 auto toImageValue = CSSImageValue::create(*toStyleImage->cachedImage());
325 auto percentageValue = CSSPrimitiveValue::create(progress, CSSPrimitiveValue::CSS_NUMBER);
326
327 auto crossfadeValue = CSSCrossfadeValue::create(WTFMove(fromImageValue), WTFMove(toImageValue), WTFMove(percentageValue));
328 return StyleGeneratedImage::create(WTFMove(crossfadeValue));
329}
330
331static inline RefPtr<StyleImage> blendFunc(const CSSPropertyBlendingClient* anim, StyleImage* from, StyleImage* to, double progress)
332{
333 if (!from || !to)
334 return to;
335
336 // Animation between two generated images. Cross fade for all other cases.
337 if (is<StyleGeneratedImage>(*from) && is<StyleGeneratedImage>(*to)) {
338 CSSImageGeneratorValue& fromGenerated = downcast<StyleGeneratedImage>(*from).imageValue();
339 CSSImageGeneratorValue& toGenerated = downcast<StyleGeneratedImage>(*to).imageValue();
340
341 if (is<CSSFilterImageValue>(fromGenerated) && is<CSSFilterImageValue>(toGenerated)) {
342 // Animation of generated images just possible if input images are equal.
343 // Otherwise fall back to cross fade animation.
344 CSSFilterImageValue& fromFilter = downcast<CSSFilterImageValue>(fromGenerated);
345 CSSFilterImageValue& toFilter = downcast<CSSFilterImageValue>(toGenerated);
346 if (fromFilter.equalInputImages(toFilter) && fromFilter.cachedImage())
347 return blendFilter(anim, fromFilter.cachedImage(), fromFilter.filterOperations(), toFilter.filterOperations(), progress);
348 }
349
350 if (is<CSSCrossfadeValue>(fromGenerated) && is<CSSCrossfadeValue>(toGenerated)) {
351 CSSCrossfadeValue& fromCrossfade = downcast<CSSCrossfadeValue>(fromGenerated);
352 CSSCrossfadeValue& toCrossfade = downcast<CSSCrossfadeValue>(toGenerated);
353 if (fromCrossfade.equalInputImages(toCrossfade)) {
354 if (auto crossfadeBlend = toCrossfade.blend(fromCrossfade, progress))
355 return StyleGeneratedImage::create(*crossfadeBlend);
356 }
357 }
358
359 // FIXME: Add support for animation between two *gradient() functions.
360 // https://bugs.webkit.org/show_bug.cgi?id=119956
361 } else if (is<StyleGeneratedImage>(*from) && is<StyleCachedImage>(*to)) {
362 CSSImageGeneratorValue& fromGenerated = downcast<StyleGeneratedImage>(*from).imageValue();
363 if (is<CSSFilterImageValue>(fromGenerated)) {
364 CSSFilterImageValue& fromFilter = downcast<CSSFilterImageValue>(fromGenerated);
365 if (fromFilter.cachedImage() && downcast<StyleCachedImage>(*to).cachedImage() == fromFilter.cachedImage())
366 return blendFilter(anim, fromFilter.cachedImage(), fromFilter.filterOperations(), FilterOperations(), progress);
367 }
368 // FIXME: Add interpolation between cross-fade and image source.
369 } else if (is<StyleCachedImage>(*from) && is<StyleGeneratedImage>(*to)) {
370 CSSImageGeneratorValue& toGenerated = downcast<StyleGeneratedImage>(*to).imageValue();
371 if (is<CSSFilterImageValue>(toGenerated)) {
372 CSSFilterImageValue& toFilter = downcast<CSSFilterImageValue>(toGenerated);
373 if (toFilter.cachedImage() && downcast<StyleCachedImage>(*from).cachedImage() == toFilter.cachedImage())
374 return blendFilter(anim, toFilter.cachedImage(), FilterOperations(), toFilter.filterOperations(), progress);
375 }
376 // FIXME: Add interpolation between image source and cross-fade.
377 }
378
379 // FIXME: Add support cross fade between cached and generated images.
380 // https://bugs.webkit.org/show_bug.cgi?id=78293
381 if (is<StyleCachedImage>(*from) && is<StyleCachedImage>(*to))
382 return crossfadeBlend(anim, downcast<StyleCachedImage>(from), downcast<StyleCachedImage>(to), progress);
383
384 return to;
385}
386
387static inline NinePieceImage blendFunc(const CSSPropertyBlendingClient* anim, const NinePieceImage& from, const NinePieceImage& to, double progress)
388{
389 if (!from.hasImage() || !to.hasImage())
390 return to;
391
392 // FIXME (74112): Support transitioning between NinePieceImages that differ by more than image content.
393
394 if (from.imageSlices() != to.imageSlices() || from.borderSlices() != to.borderSlices() || from.outset() != to.outset() || from.fill() != to.fill() || from.horizontalRule() != to.horizontalRule() || from.verticalRule() != to.verticalRule())
395 return to;
396
397 if (auto* renderer = anim->renderer()) {
398 if (from.image()->imageSize(renderer, 1.0) != to.image()->imageSize(renderer, 1.0))
399 return to;
400 }
401
402 return NinePieceImage(blendFunc(anim, from.image(), to.image(), progress),
403 from.imageSlices(), from.fill(), from.borderSlices(), from.outset(), from.horizontalRule(), from.verticalRule());
404}
405
406#if ENABLE(VARIATION_FONTS)
407
408static inline FontVariationSettings blendFunc(const CSSPropertyBlendingClient* anim, const FontVariationSettings& from, const FontVariationSettings& to, double progress)
409{
410 if (from.size() != to.size())
411 return FontVariationSettings();
412 FontVariationSettings result;
413 unsigned size = from.size();
414 for (unsigned i = 0; i < size; ++i) {
415 auto& fromItem = from.at(i);
416 auto& toItem = to.at(i);
417 if (fromItem.tag() != toItem.tag())
418 return FontVariationSettings();
419 float interpolated = blendFunc(anim, fromItem.value(), toItem.value(), progress);
420 result.insert({ fromItem.tag(), interpolated });
421 }
422 return result;
423}
424
425#endif
426
427static inline FontSelectionValue blendFunc(const CSSPropertyBlendingClient* anim, FontSelectionValue from, FontSelectionValue to, double progress)
428{
429 return FontSelectionValue(blendFunc(anim, static_cast<float>(from), static_cast<float>(to), progress));
430}
431
432static inline Optional<FontSelectionValue> blendFunc(const CSSPropertyBlendingClient* anim, Optional<FontSelectionValue> from, Optional<FontSelectionValue> to, double progress)
433{
434 return FontSelectionValue(blendFunc(anim, static_cast<float>(from.value()), static_cast<float>(to.value()), progress));
435}
436
437class AnimationPropertyWrapperBase {
438 WTF_MAKE_NONCOPYABLE(AnimationPropertyWrapperBase);
439 WTF_MAKE_FAST_ALLOCATED;
440public:
441 AnimationPropertyWrapperBase(CSSPropertyID prop)
442 : m_prop(prop)
443 {
444 }
445 virtual ~AnimationPropertyWrapperBase() = default;
446
447 virtual bool isShorthandWrapper() const { return false; }
448 virtual bool equals(const RenderStyle* a, const RenderStyle* b) const = 0;
449 virtual bool canInterpolate(const RenderStyle*, const RenderStyle*) const { return true; }
450 virtual void blend(const CSSPropertyBlendingClient*, RenderStyle*, const RenderStyle*, const RenderStyle*, double) const = 0;
451
452#if !LOG_DISABLED
453 virtual void logBlend(const RenderStyle* a, const RenderStyle* b, const RenderStyle* result, double) const = 0;
454#endif
455
456 CSSPropertyID property() const { return m_prop; }
457
458 virtual bool animationIsAccelerated() const { return false; }
459
460private:
461 CSSPropertyID m_prop;
462};
463
464template <typename T>
465class PropertyWrapperGetter : public AnimationPropertyWrapperBase {
466 WTF_MAKE_FAST_ALLOCATED;
467public:
468 PropertyWrapperGetter(CSSPropertyID prop, T (RenderStyle::*getter)() const)
469 : AnimationPropertyWrapperBase(prop)
470 , m_getter(getter)
471 {
472 }
473
474 bool equals(const RenderStyle* a, const RenderStyle* b) const override
475 {
476 if (a == b)
477 return true;
478 if (!a || !b)
479 return false;
480 return (a->*m_getter)() == (b->*m_getter)();
481 }
482
483 T value(const RenderStyle* a) const
484 {
485 return (a->*m_getter)();
486 }
487
488#if !LOG_DISABLED
489 void logBlend(const RenderStyle* a, const RenderStyle* b, const RenderStyle* result, double progress) const final
490 {
491 LOG_WITH_STREAM(Animations, stream << " blending " << getPropertyName(property()) << " from " << value(a) << " to " << value(b) << " at " << TextStream::FormatNumberRespectingIntegers(progress) << " -> " << value(result));
492 }
493#endif
494
495protected:
496 T (RenderStyle::*m_getter)() const;
497};
498
499template <typename T>
500class PropertyWrapper : public PropertyWrapperGetter<T> {
501 WTF_MAKE_FAST_ALLOCATED;
502public:
503 PropertyWrapper(CSSPropertyID prop, T (RenderStyle::*getter)() const, void (RenderStyle::*setter)(T))
504 : PropertyWrapperGetter<T>(prop, getter)
505 , m_setter(setter)
506 {
507 }
508
509 void blend(const CSSPropertyBlendingClient* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override
510 {
511 (dst->*m_setter)(blendFunc(anim, (a->*PropertyWrapperGetter<T>::m_getter)(), (b->*PropertyWrapperGetter<T>::m_getter)(), progress));
512 }
513
514protected:
515 void (RenderStyle::*m_setter)(T);
516};
517
518template <typename T>
519class RefCountedPropertyWrapper : public PropertyWrapperGetter<T*> {
520 WTF_MAKE_FAST_ALLOCATED;
521public:
522 RefCountedPropertyWrapper(CSSPropertyID prop, T* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(RefPtr<T>&&))
523 : PropertyWrapperGetter<T*>(prop, getter)
524 , m_setter(setter)
525 {
526 }
527
528 void blend(const CSSPropertyBlendingClient* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override
529 {
530 (dst->*m_setter)(blendFunc(anim, (a->*PropertyWrapperGetter<T*>::m_getter)(), (b->*PropertyWrapperGetter<T*>::m_getter)(), progress));
531 }
532
533protected:
534 void (RenderStyle::*m_setter)(RefPtr<T>&&);
535};
536
537class LengthPropertyWrapper : public PropertyWrapperGetter<const Length&> {
538 WTF_MAKE_FAST_ALLOCATED;
539public:
540 LengthPropertyWrapper(CSSPropertyID prop, const Length& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(Length&&))
541 : PropertyWrapperGetter<const Length&>(prop, getter)
542 , m_setter(setter)
543 {
544 }
545
546 bool canInterpolate(const RenderStyle* a, const RenderStyle* b) const override
547 {
548 return !(a->*PropertyWrapperGetter<const Length&>::m_getter)().isAuto() && !(b->*PropertyWrapperGetter<const Length&>::m_getter)().isAuto();
549 }
550
551 void blend(const CSSPropertyBlendingClient* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override
552 {
553 (dst->*m_setter)(blendFunc(anim, (a->*PropertyWrapperGetter<const Length&>::m_getter)(), (b->*PropertyWrapperGetter<const Length&>::m_getter)(), progress));
554 }
555
556protected:
557 void (RenderStyle::*m_setter)(Length&&);
558};
559
560template <typename T>
561class LengthVariantPropertyWrapper : public PropertyWrapperGetter<const T&> {
562 WTF_MAKE_FAST_ALLOCATED;
563public:
564 LengthVariantPropertyWrapper(CSSPropertyID prop, const T& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(T&&))
565 : PropertyWrapperGetter<const T&>(prop, getter)
566 , m_setter(setter)
567 {
568 }
569
570 void blend(const CSSPropertyBlendingClient* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override
571 {
572 (dst->*m_setter)(blendFunc(anim, (a->*PropertyWrapperGetter<const T&>::m_getter)(), (b->*PropertyWrapperGetter<const T&>::m_getter)(), progress));
573 }
574
575protected:
576 void (RenderStyle::*m_setter)(T&&);
577};
578
579class PropertyWrapperClipPath : public RefCountedPropertyWrapper<ClipPathOperation> {
580 WTF_MAKE_FAST_ALLOCATED;
581public:
582 PropertyWrapperClipPath(CSSPropertyID prop, ClipPathOperation* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(RefPtr<ClipPathOperation>&&))
583 : RefCountedPropertyWrapper<ClipPathOperation>(prop, getter, setter)
584 {
585 }
586
587 bool equals(const RenderStyle* a, const RenderStyle* b) const override
588 {
589 // If the style pointers are the same, don't bother doing the test.
590 // If either is null, return false. If both are null, return true.
591 if (a == b)
592 return true;
593 if (!a || !b)
594 return false;
595
596 ClipPathOperation* clipPathA = (a->*m_getter)();
597 ClipPathOperation* clipPathB = (b->*m_getter)();
598 if (clipPathA == clipPathB)
599 return true;
600 if (!clipPathA || !clipPathB)
601 return false;
602 return *clipPathA == *clipPathB;
603 }
604};
605
606#if ENABLE(VARIATION_FONTS)
607class PropertyWrapperFontVariationSettings : public PropertyWrapper<FontVariationSettings> {
608 WTF_MAKE_FAST_ALLOCATED;
609public:
610 PropertyWrapperFontVariationSettings(CSSPropertyID prop, FontVariationSettings (RenderStyle::*getter)() const, void (RenderStyle::*setter)(FontVariationSettings))
611 : PropertyWrapper<FontVariationSettings>(prop, getter, setter)
612 {
613 }
614
615 bool equals(const RenderStyle* a, const RenderStyle* b) const override
616 {
617 // If the style pointers are the same, don't bother doing the test.
618 // If either is null, return false. If both are null, return true.
619 if (a == b)
620 return true;
621 if (!a || !b)
622 return false;
623
624 const FontVariationSettings& variationSettingsA = (a->*m_getter)();
625 const FontVariationSettings& variationSettingsB = (b->*m_getter)();
626 return variationSettingsA == variationSettingsB;
627 }
628};
629#endif
630
631class PropertyWrapperShape : public RefCountedPropertyWrapper<ShapeValue> {
632 WTF_MAKE_FAST_ALLOCATED;
633public:
634 PropertyWrapperShape(CSSPropertyID prop, ShapeValue* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(RefPtr<ShapeValue>&&))
635 : RefCountedPropertyWrapper<ShapeValue>(prop, getter, setter)
636 {
637 }
638
639 bool equals(const RenderStyle* a, const RenderStyle* b) const override
640 {
641 // If the style pointers are the same, don't bother doing the test.
642 // If either is null, return false. If both are null, return true.
643 if (a == b)
644 return true;
645 if (!a || !b)
646 return false;
647
648 ShapeValue* shapeA = (a->*m_getter)();
649 ShapeValue* shapeB = (b->*m_getter)();
650 if (shapeA == shapeB)
651 return true;
652 if (!shapeA || !shapeB)
653 return false;
654 return *shapeA == *shapeB;
655 }
656};
657
658class StyleImagePropertyWrapper : public RefCountedPropertyWrapper<StyleImage> {
659 WTF_MAKE_FAST_ALLOCATED;
660public:
661 StyleImagePropertyWrapper(CSSPropertyID prop, StyleImage* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(RefPtr<StyleImage>&&))
662 : RefCountedPropertyWrapper<StyleImage>(prop, getter, setter)
663 {
664 }
665
666 bool equals(const RenderStyle* a, const RenderStyle* b) const override
667 {
668 if (a == b)
669 return true;
670 if (!a || !b)
671 return false;
672
673 StyleImage* imageA = (a->*m_getter)();
674 StyleImage* imageB = (b->*m_getter)();
675 return arePointingToEqualData(imageA, imageB);
676 }
677};
678
679class PropertyWrapperColor : public PropertyWrapperGetter<const Color&> {
680 WTF_MAKE_FAST_ALLOCATED;
681public:
682 PropertyWrapperColor(CSSPropertyID prop, const Color& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&))
683 : PropertyWrapperGetter<const Color&>(prop, getter)
684 , m_setter(setter)
685 {
686 }
687
688 void blend(const CSSPropertyBlendingClient* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override
689 {
690 (dst->*m_setter)(blendFunc(anim, (a->*PropertyWrapperGetter<const Color&>::m_getter)(), (b->*PropertyWrapperGetter<const Color&>::m_getter)(), progress));
691 }
692
693protected:
694 void (RenderStyle::*m_setter)(const Color&);
695};
696
697class PropertyWrapperAcceleratedOpacity : public PropertyWrapper<float> {
698 WTF_MAKE_FAST_ALLOCATED;
699public:
700 PropertyWrapperAcceleratedOpacity()
701 : PropertyWrapper<float>(CSSPropertyOpacity, &RenderStyle::opacity, &RenderStyle::setOpacity)
702 {
703 }
704
705 bool animationIsAccelerated() const override { return true; }
706
707 void blend(const CSSPropertyBlendingClient* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override
708 {
709 dst->setOpacity(blendFunc(anim, a->opacity(), b->opacity(), progress));
710 }
711};
712
713class PropertyWrapperAcceleratedTransform : public PropertyWrapper<const TransformOperations&> {
714 WTF_MAKE_FAST_ALLOCATED;
715public:
716 PropertyWrapperAcceleratedTransform()
717 : PropertyWrapper<const TransformOperations&>(CSSPropertyTransform, &RenderStyle::transform, &RenderStyle::setTransform)
718 {
719 }
720
721 bool animationIsAccelerated() const override { return true; }
722
723 void blend(const CSSPropertyBlendingClient* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override
724 {
725 dst->setTransform(blendFunc(anim, a->transform(), b->transform(), progress));
726 }
727};
728
729class PropertyWrapperFilter : public PropertyWrapper<const FilterOperations&> {
730 WTF_MAKE_FAST_ALLOCATED;
731public:
732 PropertyWrapperFilter(CSSPropertyID propertyID, const FilterOperations& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const FilterOperations&))
733 : PropertyWrapper<const FilterOperations&>(propertyID, getter, setter)
734 {
735 }
736
737 bool animationIsAccelerated() const override
738 {
739 return property() == CSSPropertyFilter
740#if ENABLE(FILTERS_LEVEL_2)
741 || property() == CSSPropertyWebkitBackdropFilter
742#endif
743 ;
744 }
745
746 void blend(const CSSPropertyBlendingClient* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override
747 {
748 (dst->*m_setter)(blendFunc(anim, (a->*PropertyWrapperGetter<const FilterOperations&>::m_getter)(), (b->*PropertyWrapperGetter<const FilterOperations&>::m_getter)(), progress, property()));
749 }
750};
751
752static inline size_t shadowListLength(const ShadowData* shadow)
753{
754 size_t count;
755 for (count = 0; shadow; shadow = shadow->next())
756 ++count;
757 return count;
758}
759
760static inline const ShadowData* shadowForBlending(const ShadowData* srcShadow, const ShadowData* otherShadow)
761{
762 static NeverDestroyed<ShadowData> defaultShadowData(IntPoint(), 0, 0, Normal, false, Color::transparent);
763 static NeverDestroyed<ShadowData> defaultInsetShadowData(IntPoint(), 0, 0, Inset, false, Color::transparent);
764 static NeverDestroyed<ShadowData> defaultWebKitBoxShadowData(IntPoint(), 0, 0, Normal, true, Color::transparent);
765 static NeverDestroyed<ShadowData> defaultInsetWebKitBoxShadowData(IntPoint(), 0, 0, Inset, true, Color::transparent);
766
767 if (srcShadow)
768 return srcShadow;
769
770 if (otherShadow->style() == Inset)
771 return otherShadow->isWebkitBoxShadow() ? &defaultInsetWebKitBoxShadowData.get() : &defaultInsetShadowData.get();
772
773 return otherShadow->isWebkitBoxShadow() ? &defaultWebKitBoxShadowData.get() : &defaultShadowData.get();
774}
775
776class PropertyWrapperShadow : public AnimationPropertyWrapperBase {
777 WTF_MAKE_FAST_ALLOCATED;
778public:
779 PropertyWrapperShadow(CSSPropertyID prop, const ShadowData* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(std::unique_ptr<ShadowData>, bool))
780 : AnimationPropertyWrapperBase(prop)
781 , m_getter(getter)
782 , m_setter(setter)
783 {
784 }
785
786 bool equals(const RenderStyle* a, const RenderStyle* b) const override
787 {
788 if (a == b)
789 return true;
790 if (!a || !b)
791 return false;
792
793 const ShadowData* shadowA = (a->*m_getter)();
794 const ShadowData* shadowB = (b->*m_getter)();
795
796 while (true) {
797 // end of both lists
798 if (!shadowA && !shadowB)
799 return true;
800
801 // end of just one of the lists
802 if (!shadowA || !shadowB)
803 return false;
804
805 if (*shadowA != *shadowB)
806 return false;
807
808 shadowA = shadowA->next();
809 shadowB = shadowB->next();
810 }
811
812 return true;
813 }
814
815 void blend(const CSSPropertyBlendingClient* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override
816 {
817 const ShadowData* shadowA = (a->*m_getter)();
818 const ShadowData* shadowB = (b->*m_getter)();
819
820 int fromLength = shadowListLength(shadowA);
821 int toLength = shadowListLength(shadowB);
822
823 if (fromLength == toLength || (fromLength <= 1 && toLength <= 1)) {
824 (dst->*m_setter)(blendSimpleOrMatchedShadowLists(anim, progress, shadowA, shadowB), false);
825 return;
826 }
827
828 (dst->*m_setter)(blendMismatchedShadowLists(anim, progress, shadowA, shadowB, fromLength, toLength), false);
829 }
830
831#if !LOG_DISABLED
832 void logBlend(const RenderStyle*, const RenderStyle*, const RenderStyle*, double progress) const final
833 {
834 // FIXME: better logging.
835 LOG_WITH_STREAM(Animations, stream << " blending ShadowData at " << TextStream::FormatNumberRespectingIntegers(progress));
836 }
837#endif
838
839private:
840 std::unique_ptr<ShadowData> blendSimpleOrMatchedShadowLists(const CSSPropertyBlendingClient* anim, double progress, const ShadowData* shadowA, const ShadowData* shadowB) const
841 {
842 std::unique_ptr<ShadowData> newShadowData;
843 ShadowData* lastShadow = 0;
844
845 while (shadowA || shadowB) {
846 const ShadowData* srcShadow = shadowForBlending(shadowA, shadowB);
847 const ShadowData* dstShadow = shadowForBlending(shadowB, shadowA);
848
849 std::unique_ptr<ShadowData> blendedShadow = blendFunc(anim, srcShadow, dstShadow, progress);
850 ShadowData* blendedShadowPtr = blendedShadow.get();
851
852 if (!lastShadow)
853 newShadowData = WTFMove(blendedShadow);
854 else
855 lastShadow->setNext(WTFMove(blendedShadow));
856
857 lastShadow = blendedShadowPtr;
858
859 shadowA = shadowA ? shadowA->next() : 0;
860 shadowB = shadowB ? shadowB->next() : 0;
861 }
862
863 return newShadowData;
864 }
865
866 std::unique_ptr<ShadowData> blendMismatchedShadowLists(const CSSPropertyBlendingClient* anim, double progress, const ShadowData* shadowA, const ShadowData* shadowB, int fromLength, int toLength) const
867 {
868 // The shadows in ShadowData are stored in reverse order, so when animating mismatched lists,
869 // reverse them and match from the end.
870 Vector<const ShadowData*, 4> fromShadows(fromLength);
871 for (int i = fromLength - 1; i >= 0; --i) {
872 fromShadows[i] = shadowA;
873 shadowA = shadowA->next();
874 }
875
876 Vector<const ShadowData*, 4> toShadows(toLength);
877 for (int i = toLength - 1; i >= 0; --i) {
878 toShadows[i] = shadowB;
879 shadowB = shadowB->next();
880 }
881
882 std::unique_ptr<ShadowData> newShadowData;
883
884 int maxLength = std::max(fromLength, toLength);
885 for (int i = 0; i < maxLength; ++i) {
886 const ShadowData* fromShadow = i < fromLength ? fromShadows[i] : 0;
887 const ShadowData* toShadow = i < toLength ? toShadows[i] : 0;
888
889 const ShadowData* srcShadow = shadowForBlending(fromShadow, toShadow);
890 const ShadowData* dstShadow = shadowForBlending(toShadow, fromShadow);
891
892 std::unique_ptr<ShadowData> blendedShadow = blendFunc(anim, srcShadow, dstShadow, progress);
893 // Insert at the start of the list to preserve the order.
894 blendedShadow->setNext(WTFMove(newShadowData));
895 newShadowData = WTFMove(blendedShadow);
896 }
897
898 return newShadowData;
899 }
900
901 const ShadowData* (RenderStyle::*m_getter)() const;
902 void (RenderStyle::*m_setter)(std::unique_ptr<ShadowData>, bool);
903};
904
905class PropertyWrapperMaybeInvalidColor : public AnimationPropertyWrapperBase {
906 WTF_MAKE_FAST_ALLOCATED;
907public:
908 PropertyWrapperMaybeInvalidColor(CSSPropertyID prop, const Color& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&))
909 : AnimationPropertyWrapperBase(prop)
910 , m_getter(getter)
911 , m_setter(setter)
912 {
913 }
914
915 bool equals(const RenderStyle* a, const RenderStyle* b) const override
916 {
917 if (a == b)
918 return true;
919 if (!a || !b)
920 return false;
921
922 Color fromColor = value(a);
923 Color toColor = value(b);
924
925 if (!fromColor.isValid() && !toColor.isValid())
926 return true;
927
928 if (!fromColor.isValid())
929 fromColor = a->color();
930 if (!toColor.isValid())
931 toColor = b->color();
932
933 return fromColor == toColor;
934 }
935
936 void blend(const CSSPropertyBlendingClient* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override
937 {
938 Color fromColor = value(a);
939 Color toColor = value(b);
940
941 if (!fromColor.isValid() && !toColor.isValid())
942 return;
943
944 if (!fromColor.isValid())
945 fromColor = a->color();
946 if (!toColor.isValid())
947 toColor = b->color();
948 (dst->*m_setter)(blendFunc(anim, fromColor, toColor, progress));
949 }
950
951 Color value(const RenderStyle* a) const
952 {
953 return (a->*m_getter)();
954 }
955
956#if !LOG_DISABLED
957 void logBlend(const RenderStyle* a, const RenderStyle* b, const RenderStyle* result, double progress) const final
958 {
959 // FIXME: better logging.
960 LOG_WITH_STREAM(Animations, stream << " blending " << getPropertyName(property()) << " from " << value(a) << " to " << value(b) << " at " << TextStream::FormatNumberRespectingIntegers(progress) << " -> " << value(result));
961 }
962#endif
963
964private:
965 const Color& (RenderStyle::*m_getter)() const;
966 void (RenderStyle::*m_setter)(const Color&);
967};
968
969
970enum MaybeInvalidColorTag { MaybeInvalidColor };
971class PropertyWrapperVisitedAffectedColor : public AnimationPropertyWrapperBase {
972 WTF_MAKE_FAST_ALLOCATED;
973public:
974 PropertyWrapperVisitedAffectedColor(CSSPropertyID prop, const Color& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&),
975 const Color& (RenderStyle::*visitedGetter)() const, void (RenderStyle::*visitedSetter)(const Color&))
976 : AnimationPropertyWrapperBase(prop)
977 , m_wrapper(std::make_unique<PropertyWrapperColor>(prop, getter, setter))
978 , m_visitedWrapper(std::make_unique<PropertyWrapperColor>(prop, visitedGetter, visitedSetter))
979 {
980 }
981 PropertyWrapperVisitedAffectedColor(CSSPropertyID prop, MaybeInvalidColorTag, const Color& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&),
982 const Color& (RenderStyle::*visitedGetter)() const, void (RenderStyle::*visitedSetter)(const Color&))
983 : AnimationPropertyWrapperBase(prop)
984 , m_wrapper(std::make_unique<PropertyWrapperMaybeInvalidColor>(prop, getter, setter))
985 , m_visitedWrapper(std::make_unique<PropertyWrapperMaybeInvalidColor>(prop, visitedGetter, visitedSetter))
986 {
987 }
988 bool equals(const RenderStyle* a, const RenderStyle* b) const override
989 {
990 return m_wrapper->equals(a, b) && m_visitedWrapper->equals(a, b);
991 }
992 void blend(const CSSPropertyBlendingClient* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override
993 {
994 m_wrapper->blend(anim, dst, a, b, progress);
995 m_visitedWrapper->blend(anim, dst, a, b, progress);
996 }
997
998#if !LOG_DISABLED
999 void logBlend(const RenderStyle* a, const RenderStyle* b, const RenderStyle* result, double progress) const final
1000 {
1001 m_wrapper->logBlend(a, b, result, progress);
1002 m_visitedWrapper->logBlend(a, b, result, progress);
1003 }
1004#endif
1005
1006private:
1007 std::unique_ptr<AnimationPropertyWrapperBase> m_wrapper;
1008 std::unique_ptr<AnimationPropertyWrapperBase> m_visitedWrapper;
1009};
1010
1011// Wrapper base class for an animatable property in a FillLayer
1012class FillLayerAnimationPropertyWrapperBase {
1013 WTF_MAKE_FAST_ALLOCATED;
1014public:
1015 FillLayerAnimationPropertyWrapperBase(CSSPropertyID property)
1016 : m_property(property)
1017 {
1018 }
1019 virtual ~FillLayerAnimationPropertyWrapperBase() = default;
1020
1021 CSSPropertyID property() const { return m_property; }
1022
1023 virtual bool equals(const FillLayer*, const FillLayer*) const = 0;
1024 virtual void blend(const CSSPropertyBlendingClient*, FillLayer*, const FillLayer*, const FillLayer*, double) const = 0;
1025
1026#if !LOG_DISABLED
1027 virtual void logBlend(const FillLayer* result, const FillLayer*, const FillLayer*, double) const = 0;
1028#endif
1029private:
1030 CSSPropertyID m_property;
1031};
1032
1033template <typename T>
1034class FillLayerPropertyWrapperGetter : public FillLayerAnimationPropertyWrapperBase {
1035 WTF_MAKE_FAST_ALLOCATED;
1036 WTF_MAKE_NONCOPYABLE(FillLayerPropertyWrapperGetter);
1037public:
1038 FillLayerPropertyWrapperGetter(CSSPropertyID property, T (FillLayer::*getter)() const)
1039 : FillLayerAnimationPropertyWrapperBase(property)
1040 , m_getter(getter)
1041 {
1042 }
1043
1044 bool equals(const FillLayer* a, const FillLayer* b) const override
1045 {
1046 if (a == b)
1047 return true;
1048 if (!a || !b)
1049 return false;
1050 return (a->*m_getter)() == (b->*m_getter)();
1051 }
1052
1053 T value(const FillLayer* layer) const
1054 {
1055 return (layer->*m_getter)();
1056 }
1057
1058#if !LOG_DISABLED
1059 void logBlend(const FillLayer* result, const FillLayer* a, const FillLayer* b, double progress) const override
1060 {
1061 LOG_WITH_STREAM(Animations, stream << " blending " << getPropertyName(property()) << " from " << value(a) << " to " << value(b) << " at " << TextStream::FormatNumberRespectingIntegers(progress) << " -> " << value(result));
1062 }
1063#endif
1064
1065protected:
1066 T (FillLayer::*m_getter)() const;
1067};
1068
1069template <typename T>
1070class FillLayerPropertyWrapper : public FillLayerPropertyWrapperGetter<const T&> {
1071 WTF_MAKE_FAST_ALLOCATED;
1072public:
1073 FillLayerPropertyWrapper(CSSPropertyID property, const T& (FillLayer::*getter)() const, void (FillLayer::*setter)(T))
1074 : FillLayerPropertyWrapperGetter<const T&>(property, getter)
1075 , m_setter(setter)
1076 {
1077 }
1078
1079 void blend(const CSSPropertyBlendingClient* anim, FillLayer* dst, const FillLayer* a, const FillLayer* b, double progress) const override
1080 {
1081 (dst->*m_setter)(blendFunc(anim, (a->*FillLayerPropertyWrapperGetter<const T&>::m_getter)(), (b->*FillLayerPropertyWrapperGetter<const T&>::m_getter)(), progress));
1082 }
1083
1084#if !LOG_DISABLED
1085 void logBlend(const FillLayer* result, const FillLayer* a, const FillLayer* b, double progress) const override
1086 {
1087 LOG_WITH_STREAM(Animations, stream << " blending " << getPropertyName(FillLayerPropertyWrapperGetter<const T&>::property())
1088 << " from " << FillLayerPropertyWrapperGetter<const T&>::value(a)
1089 << " to " << FillLayerPropertyWrapperGetter<const T&>::value(b)
1090 << " at " << TextStream::FormatNumberRespectingIntegers(progress) << " -> " << FillLayerPropertyWrapperGetter<const T&>::value(result));
1091 }
1092#endif
1093
1094protected:
1095 void (FillLayer::*m_setter)(T);
1096};
1097
1098class FillLayerPositionPropertyWrapper : public FillLayerPropertyWrapperGetter<const Length&> {
1099 WTF_MAKE_FAST_ALLOCATED;
1100public:
1101 FillLayerPositionPropertyWrapper(CSSPropertyID property, const Length& (FillLayer::*lengthGetter)() const, void (FillLayer::*lengthSetter)(Length), Edge (FillLayer::*originGetter)() const, void (FillLayer::*originSetter)(Edge), Edge farEdge)
1102 : FillLayerPropertyWrapperGetter<const Length&>(property, lengthGetter)
1103 , m_lengthSetter(lengthSetter)
1104 , m_originGetter(originGetter)
1105 , m_originSetter(originSetter)
1106 , m_farEdge(farEdge)
1107 {
1108 }
1109
1110 bool equals(const FillLayer* a, const FillLayer* b) const override
1111 {
1112 if (a == b)
1113 return true;
1114 if (!a || !b)
1115 return false;
1116
1117 Length fromLength = (a->*FillLayerPropertyWrapperGetter<const Length&>::m_getter)();
1118 Length toLength = (b->*FillLayerPropertyWrapperGetter<const Length&>::m_getter)();
1119
1120 Edge fromEdge = (a->*m_originGetter)();
1121 Edge toEdge = (b->*m_originGetter)();
1122
1123 return fromLength == toLength && fromEdge == toEdge;
1124 }
1125
1126 void blend(const CSSPropertyBlendingClient* anim, FillLayer* dst, const FillLayer* a, const FillLayer* b, double progress) const override
1127 {
1128 Length fromLength = (a->*FillLayerPropertyWrapperGetter<const Length&>::m_getter)();
1129 Length toLength = (b->*FillLayerPropertyWrapperGetter<const Length&>::m_getter)();
1130
1131 Edge fromEdge = (a->*m_originGetter)();
1132 Edge toEdge = (b->*m_originGetter)();
1133
1134 if (fromEdge != toEdge) {
1135 // Convert the right/bottom into a calc expression,
1136 if (fromEdge == m_farEdge)
1137 fromLength = convertTo100PercentMinusLength(fromLength);
1138 else if (toEdge == m_farEdge) {
1139 toLength = convertTo100PercentMinusLength(toLength);
1140 (dst->*m_originSetter)(fromEdge); // Now we have a calc(100% - l), it's relative to the left/top edge.
1141 }
1142 }
1143
1144 (dst->*m_lengthSetter)(blendFunc(anim, fromLength, toLength, progress));
1145 }
1146
1147#if !LOG_DISABLED
1148 void logBlend(const FillLayer* result, const FillLayer* a, const FillLayer* b, double progress) const override
1149 {
1150 LOG_WITH_STREAM(Animations, stream << " blending " << getPropertyName(property()) << " from " << value(a) << " to " << value(b) << " at " << TextStream::FormatNumberRespectingIntegers(progress) << " -> " << value(result));
1151 }
1152#endif
1153
1154protected:
1155 void (FillLayer::*m_lengthSetter)(Length);
1156 Edge (FillLayer::*m_originGetter)() const;
1157 void (FillLayer::*m_originSetter)(Edge);
1158 Edge m_farEdge;
1159};
1160
1161template <typename T>
1162class FillLayerRefCountedPropertyWrapper : public FillLayerPropertyWrapperGetter<T*> {
1163 WTF_MAKE_FAST_ALLOCATED;
1164public:
1165 FillLayerRefCountedPropertyWrapper(CSSPropertyID property, T* (FillLayer::*getter)() const, void (FillLayer::*setter)(RefPtr<T>&&))
1166 : FillLayerPropertyWrapperGetter<T*>(property, getter)
1167 , m_setter(setter)
1168 {
1169 }
1170
1171 void blend(const CSSPropertyBlendingClient* anim, FillLayer* dst, const FillLayer* a, const FillLayer* b, double progress) const override
1172 {
1173 (dst->*m_setter)(blendFunc(anim, (a->*FillLayerPropertyWrapperGetter<T*>::m_getter)(), (b->*FillLayerPropertyWrapperGetter<T*>::m_getter)(), progress));
1174 }
1175
1176#if !LOG_DISABLED
1177 void logBlend(const FillLayer* result, const FillLayer* a, const FillLayer* b, double progress) const override
1178 {
1179 LOG_WITH_STREAM(Animations, stream << " blending " << getPropertyName(FillLayerPropertyWrapperGetter<T*>::property())
1180 << " from " << FillLayerPropertyWrapperGetter<T*>::value(a)
1181 << " to " << FillLayerPropertyWrapperGetter<T*>::value(b)
1182 << " at " << TextStream::FormatNumberRespectingIntegers(progress) << " -> " << FillLayerPropertyWrapperGetter<T*>::value(result));
1183 }
1184#endif
1185
1186protected:
1187 void (FillLayer::*m_setter)(RefPtr<T>&&);
1188};
1189
1190class FillLayerStyleImagePropertyWrapper : public FillLayerRefCountedPropertyWrapper<StyleImage> {
1191 WTF_MAKE_FAST_ALLOCATED;
1192public:
1193 FillLayerStyleImagePropertyWrapper(CSSPropertyID property, StyleImage* (FillLayer::*getter)() const, void (FillLayer::*setter)(RefPtr<StyleImage>&&))
1194 : FillLayerRefCountedPropertyWrapper<StyleImage>(property, getter, setter)
1195 {
1196 }
1197
1198 bool equals(const FillLayer* a, const FillLayer* b) const override
1199 {
1200 if (a == b)
1201 return true;
1202 if (!a || !b)
1203 return false;
1204
1205 StyleImage* imageA = (a->*m_getter)();
1206 StyleImage* imageB = (b->*m_getter)();
1207 return arePointingToEqualData(imageA, imageB);
1208 }
1209
1210#if !LOG_DISABLED
1211 void logBlend(const FillLayer* result, const FillLayer* a, const FillLayer* b, double progress) const override
1212 {
1213 LOG_WITH_STREAM(Animations, stream << " blending " << getPropertyName(property()) << " from " << value(a) << " to " << value(b) << " at " << TextStream::FormatNumberRespectingIntegers(progress) << " -> " << value(result));
1214 }
1215#endif
1216};
1217
1218class FillLayersPropertyWrapper : public AnimationPropertyWrapperBase {
1219 WTF_MAKE_FAST_ALLOCATED;
1220public:
1221 typedef const FillLayer& (RenderStyle::*LayersGetter)() const;
1222 typedef FillLayer& (RenderStyle::*LayersAccessor)();
1223
1224 FillLayersPropertyWrapper(CSSPropertyID property, LayersGetter getter, LayersAccessor accessor)
1225 : AnimationPropertyWrapperBase(property)
1226 , m_layersGetter(getter)
1227 , m_layersAccessor(accessor)
1228 {
1229 switch (property) {
1230 case CSSPropertyBackgroundPositionX:
1231 case CSSPropertyWebkitMaskPositionX:
1232 m_fillLayerPropertyWrapper = std::make_unique<FillLayerPositionPropertyWrapper>(property, &FillLayer::xPosition, &FillLayer::setXPosition, &FillLayer::backgroundXOrigin, &FillLayer::setBackgroundXOrigin, Edge::Right);
1233 break;
1234 case CSSPropertyBackgroundPositionY:
1235 case CSSPropertyWebkitMaskPositionY:
1236 m_fillLayerPropertyWrapper = std::make_unique<FillLayerPositionPropertyWrapper>(property, &FillLayer::yPosition, &FillLayer::setYPosition, &FillLayer::backgroundYOrigin, &FillLayer::setBackgroundYOrigin, Edge::Bottom);
1237 break;
1238 case CSSPropertyBackgroundSize:
1239 case CSSPropertyWebkitBackgroundSize:
1240 case CSSPropertyWebkitMaskSize:
1241 m_fillLayerPropertyWrapper = std::make_unique<FillLayerPropertyWrapper<LengthSize>>(property, &FillLayer::sizeLength, &FillLayer::setSizeLength);
1242 break;
1243 case CSSPropertyBackgroundImage:
1244 m_fillLayerPropertyWrapper = std::make_unique<FillLayerStyleImagePropertyWrapper>(property, &FillLayer::image, &FillLayer::setImage);
1245 break;
1246 default:
1247 break;
1248 }
1249 }
1250
1251 bool equals(const RenderStyle* a, const RenderStyle* b) const override
1252 {
1253 if (a == b)
1254 return true;
1255 if (!a || !b)
1256 return false;
1257
1258 auto* fromLayer = &(a->*m_layersGetter)();
1259 auto* toLayer = &(b->*m_layersGetter)();
1260
1261 while (fromLayer && toLayer) {
1262 if (!m_fillLayerPropertyWrapper->equals(fromLayer, toLayer))
1263 return false;
1264
1265 fromLayer = fromLayer->next();
1266 toLayer = toLayer->next();
1267 }
1268
1269 return true;
1270 }
1271
1272 void blend(const CSSPropertyBlendingClient* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override
1273 {
1274 auto* aLayer = &(a->*m_layersGetter)();
1275 auto* bLayer = &(b->*m_layersGetter)();
1276 auto* dstLayer = &(dst->*m_layersAccessor)();
1277
1278 while (aLayer && bLayer && dstLayer) {
1279 m_fillLayerPropertyWrapper->blend(anim, dstLayer, aLayer, bLayer, progress);
1280 aLayer = aLayer->next();
1281 bLayer = bLayer->next();
1282 dstLayer = dstLayer->next();
1283 }
1284 }
1285
1286#if !LOG_DISABLED
1287 void logBlend(const RenderStyle* from, const RenderStyle* to, const RenderStyle* result, double progress) const final
1288 {
1289 auto* aLayer = &(from->*m_layersGetter)();
1290 auto* bLayer = &(to->*m_layersGetter)();
1291 auto* dstLayer = &(result->*m_layersGetter)();
1292
1293 while (aLayer && bLayer && dstLayer) {
1294 m_fillLayerPropertyWrapper->logBlend(dstLayer, aLayer, bLayer, progress);
1295 aLayer = aLayer->next();
1296 bLayer = bLayer->next();
1297 dstLayer = dstLayer->next();
1298 }
1299 }
1300#endif
1301
1302private:
1303 std::unique_ptr<FillLayerAnimationPropertyWrapperBase> m_fillLayerPropertyWrapper;
1304
1305 LayersGetter m_layersGetter;
1306 LayersAccessor m_layersAccessor;
1307};
1308
1309class ShorthandPropertyWrapper : public AnimationPropertyWrapperBase {
1310 WTF_MAKE_FAST_ALLOCATED;
1311public:
1312 ShorthandPropertyWrapper(CSSPropertyID property, Vector<AnimationPropertyWrapperBase*> longhandWrappers)
1313 : AnimationPropertyWrapperBase(property)
1314 , m_propertyWrappers(WTFMove(longhandWrappers))
1315 {
1316 }
1317
1318 bool isShorthandWrapper() const override { return true; }
1319
1320 bool equals(const RenderStyle* a, const RenderStyle* b) const override
1321 {
1322 if (a == b)
1323 return true;
1324 if (!a || !b)
1325 return false;
1326
1327 for (auto& wrapper : m_propertyWrappers) {
1328 if (!wrapper->equals(a, b))
1329 return false;
1330 }
1331 return true;
1332 }
1333
1334 void blend(const CSSPropertyBlendingClient* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override
1335 {
1336 for (auto& wrapper : m_propertyWrappers)
1337 wrapper->blend(anim, dst, a, b, progress);
1338 }
1339
1340#if !LOG_DISABLED
1341 void logBlend(const RenderStyle* a, const RenderStyle* b, const RenderStyle* dst, double progress) const final
1342 {
1343 for (auto& wrapper : m_propertyWrappers)
1344 wrapper->logBlend(a, b, dst, progress);
1345 }
1346#endif
1347
1348 const Vector<AnimationPropertyWrapperBase*>& propertyWrappers() const { return m_propertyWrappers; }
1349
1350private:
1351 Vector<AnimationPropertyWrapperBase*> m_propertyWrappers;
1352};
1353
1354class PropertyWrapperFlex : public AnimationPropertyWrapperBase {
1355 WTF_MAKE_FAST_ALLOCATED;
1356public:
1357 PropertyWrapperFlex()
1358 : AnimationPropertyWrapperBase(CSSPropertyFlex)
1359 {
1360 }
1361
1362 bool equals(const RenderStyle* a, const RenderStyle* b) const override
1363 {
1364 if (a == b)
1365 return true;
1366 if (!a || !b)
1367 return false;
1368
1369 return a->flexBasis() == b->flexBasis() && a->flexGrow() == b->flexGrow() && a->flexShrink() == b->flexShrink();
1370 }
1371
1372 void blend(const CSSPropertyBlendingClient* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override
1373 {
1374 dst->setFlexBasis(blendFunc(anim, a->flexBasis(), b->flexBasis(), progress));
1375 dst->setFlexGrow(blendFunc(anim, a->flexGrow(), b->flexGrow(), progress));
1376 dst->setFlexShrink(blendFunc(anim, a->flexShrink(), b->flexShrink(), progress));
1377 }
1378
1379#if !LOG_DISABLED
1380 void logBlend(const RenderStyle*, const RenderStyle*, const RenderStyle*, double progress) const final
1381 {
1382 // FIXME: better logging.
1383 LOG_WITH_STREAM(Animations, stream << " blending flex at " << TextStream::FormatNumberRespectingIntegers(progress));
1384 }
1385#endif
1386};
1387
1388class PropertyWrapperSVGPaint : public AnimationPropertyWrapperBase {
1389 WTF_MAKE_FAST_ALLOCATED;
1390public:
1391 PropertyWrapperSVGPaint(CSSPropertyID prop, SVGPaintType (RenderStyle::*paintTypeGetter)() const, Color (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&))
1392 : AnimationPropertyWrapperBase(prop)
1393 , m_paintTypeGetter(paintTypeGetter)
1394 , m_getter(getter)
1395 , m_setter(setter)
1396 {
1397 }
1398
1399 bool equals(const RenderStyle* a, const RenderStyle* b) const override
1400 {
1401 if (a == b)
1402 return true;
1403 if (!a || !b)
1404 return false;
1405
1406 if ((a->*m_paintTypeGetter)() != (b->*m_paintTypeGetter)())
1407 return false;
1408
1409 // We only support animations between SVGPaints that are pure Color values.
1410 // For everything else we must return true for this method, otherwise
1411 // we will try to animate between values forever.
1412 if ((a->*m_paintTypeGetter)() == SVGPaintType::RGBColor) {
1413 Color fromColor = (a->*m_getter)();
1414 Color toColor = (b->*m_getter)();
1415
1416 if (!fromColor.isValid() && !toColor.isValid())
1417 return true;
1418
1419 if (!fromColor.isValid())
1420 fromColor = Color();
1421 if (!toColor.isValid())
1422 toColor = Color();
1423
1424 return fromColor == toColor;
1425 }
1426 return true;
1427 }
1428
1429 void blend(const CSSPropertyBlendingClient* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override
1430 {
1431 if ((a->*m_paintTypeGetter)() != SVGPaintType::RGBColor
1432 || (b->*m_paintTypeGetter)() != SVGPaintType::RGBColor)
1433 return;
1434
1435 Color fromColor = (a->*m_getter)();
1436 Color toColor = (b->*m_getter)();
1437
1438 if (!fromColor.isValid() && !toColor.isValid())
1439 return;
1440
1441 if (!fromColor.isValid())
1442 fromColor = Color();
1443 if (!toColor.isValid())
1444 toColor = Color();
1445 (dst->*m_setter)(blendFunc(anim, fromColor, toColor, progress));
1446 }
1447
1448#if !LOG_DISABLED
1449 void logBlend(const RenderStyle*, const RenderStyle*, const RenderStyle*, double progress) const final
1450 {
1451 // FIXME: better logging.
1452 LOG_WITH_STREAM(Animations, stream << " blending SVGPaint at " << TextStream::FormatNumberRespectingIntegers(progress));
1453 }
1454#endif
1455
1456private:
1457 SVGPaintType (RenderStyle::*m_paintTypeGetter)() const;
1458 Color (RenderStyle::*m_getter)() const;
1459 void (RenderStyle::*m_setter)(const Color&);
1460};
1461
1462class PropertyWrapperFontStyle : public PropertyWrapper<Optional<FontSelectionValue>> {
1463 WTF_MAKE_FAST_ALLOCATED;
1464public:
1465 PropertyWrapperFontStyle()
1466 : PropertyWrapper<Optional<FontSelectionValue>>(CSSPropertyFontStyle, &RenderStyle::fontItalic, &RenderStyle::setFontItalic)
1467 {
1468 }
1469
1470 bool canInterpolate(const RenderStyle* from, const RenderStyle* to) const override
1471 {
1472 return from->fontItalic() && to->fontItalic() && from->fontDescription().fontStyleAxis() == FontStyleAxis::slnt && to->fontDescription().fontStyleAxis() == FontStyleAxis::slnt;
1473 }
1474
1475 void blend(const CSSPropertyBlendingClient* anim, RenderStyle* dst, const RenderStyle* from, const RenderStyle* to, double progress) const override
1476 {
1477 auto discrete = !canInterpolate(from, to);
1478
1479 auto blendedStyleAxis = FontStyleAxis::slnt;
1480 if (discrete)
1481 blendedStyleAxis = (progress < 0.5 ? from : to)->fontDescription().fontStyleAxis();
1482
1483 auto fromFontItalic = from->fontItalic();
1484 auto toFontItalic = to->fontItalic();
1485 auto blendedFontItalic = progress < 0.5 ? fromFontItalic : toFontItalic;
1486 if (!discrete)
1487 blendedFontItalic = blendFunc(anim, fromFontItalic, toFontItalic, progress);
1488
1489 FontSelector* currentFontSelector = dst->fontCascade().fontSelector();
1490 auto description = dst->fontDescription();
1491 description.setItalic(blendedFontItalic);
1492 description.setFontStyleAxis(blendedStyleAxis);
1493 dst->setFontDescription(WTFMove(description));
1494 dst->fontCascade().update(currentFontSelector);
1495 }
1496};
1497
1498class CSSPropertyAnimationWrapperMap {
1499 WTF_MAKE_FAST_ALLOCATED;
1500public:
1501 static CSSPropertyAnimationWrapperMap& singleton()
1502 {
1503 // FIXME: This data is never destroyed. Maybe we should ref count it and toss it when the last CSSAnimationController is destroyed?
1504 static NeverDestroyed<CSSPropertyAnimationWrapperMap> map;
1505 return map;
1506 }
1507
1508 AnimationPropertyWrapperBase* wrapperForProperty(CSSPropertyID propertyID)
1509 {
1510 if (propertyID < firstCSSProperty || propertyID > lastCSSProperty)
1511 return nullptr;
1512
1513 unsigned wrapperIndex = indexFromPropertyID(propertyID);
1514 if (wrapperIndex == cInvalidPropertyWrapperIndex)
1515 return nullptr;
1516
1517 return m_propertyWrappers[wrapperIndex].get();
1518 }
1519
1520 AnimationPropertyWrapperBase* wrapperForIndex(unsigned index)
1521 {
1522 ASSERT(index < m_propertyWrappers.size());
1523 return m_propertyWrappers[index].get();
1524 }
1525
1526 unsigned size()
1527 {
1528 return m_propertyWrappers.size();
1529 }
1530
1531private:
1532 CSSPropertyAnimationWrapperMap();
1533 ~CSSPropertyAnimationWrapperMap() = delete;
1534
1535 unsigned char& indexFromPropertyID(CSSPropertyID propertyID)
1536 {
1537 return m_propertyToIdMap[propertyID - firstCSSProperty];
1538 }
1539
1540 Vector<std::unique_ptr<AnimationPropertyWrapperBase>> m_propertyWrappers;
1541 unsigned char m_propertyToIdMap[numCSSProperties];
1542
1543 static const unsigned char cInvalidPropertyWrapperIndex = UCHAR_MAX;
1544
1545 friend class WTF::NeverDestroyed<CSSPropertyAnimationWrapperMap>;
1546};
1547
1548CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap()
1549{
1550 // build the list of property wrappers to do the comparisons and blends
1551 AnimationPropertyWrapperBase* animatableLonghandPropertyWrappers[] = {
1552 new LengthPropertyWrapper(CSSPropertyLeft, &RenderStyle::left, &RenderStyle::setLeft),
1553 new LengthPropertyWrapper(CSSPropertyRight, &RenderStyle::right, &RenderStyle::setRight),
1554 new LengthPropertyWrapper(CSSPropertyTop, &RenderStyle::top, &RenderStyle::setTop),
1555 new LengthPropertyWrapper(CSSPropertyBottom, &RenderStyle::bottom, &RenderStyle::setBottom),
1556
1557 new LengthPropertyWrapper(CSSPropertyWidth, &RenderStyle::width, &RenderStyle::setWidth),
1558 new LengthPropertyWrapper(CSSPropertyMinWidth, &RenderStyle::minWidth, &RenderStyle::setMinWidth),
1559 new LengthPropertyWrapper(CSSPropertyMaxWidth, &RenderStyle::maxWidth, &RenderStyle::setMaxWidth),
1560
1561 new LengthPropertyWrapper(CSSPropertyHeight, &RenderStyle::height, &RenderStyle::setHeight),
1562 new LengthPropertyWrapper(CSSPropertyMinHeight, &RenderStyle::minHeight, &RenderStyle::setMinHeight),
1563 new LengthPropertyWrapper(CSSPropertyMaxHeight, &RenderStyle::maxHeight, &RenderStyle::setMaxHeight),
1564
1565 new PropertyWrapperFlex(),
1566
1567 new PropertyWrapper<float>(CSSPropertyBorderLeftWidth, &RenderStyle::borderLeftWidth, &RenderStyle::setBorderLeftWidth),
1568 new PropertyWrapper<float>(CSSPropertyBorderRightWidth, &RenderStyle::borderRightWidth, &RenderStyle::setBorderRightWidth),
1569 new PropertyWrapper<float>(CSSPropertyBorderTopWidth, &RenderStyle::borderTopWidth, &RenderStyle::setBorderTopWidth),
1570 new PropertyWrapper<float>(CSSPropertyBorderBottomWidth, &RenderStyle::borderBottomWidth, &RenderStyle::setBorderBottomWidth),
1571 new LengthPropertyWrapper(CSSPropertyMarginLeft, &RenderStyle::marginLeft, &RenderStyle::setMarginLeft),
1572 new LengthPropertyWrapper(CSSPropertyMarginRight, &RenderStyle::marginRight, &RenderStyle::setMarginRight),
1573 new LengthPropertyWrapper(CSSPropertyMarginTop, &RenderStyle::marginTop, &RenderStyle::setMarginTop),
1574 new LengthPropertyWrapper(CSSPropertyMarginBottom, &RenderStyle::marginBottom, &RenderStyle::setMarginBottom),
1575 new LengthPropertyWrapper(CSSPropertyPaddingLeft, &RenderStyle::paddingLeft, &RenderStyle::setPaddingLeft),
1576 new LengthPropertyWrapper(CSSPropertyPaddingRight, &RenderStyle::paddingRight, &RenderStyle::setPaddingRight),
1577 new LengthPropertyWrapper(CSSPropertyPaddingTop, &RenderStyle::paddingTop, &RenderStyle::setPaddingTop),
1578 new LengthPropertyWrapper(CSSPropertyPaddingBottom, &RenderStyle::paddingBottom, &RenderStyle::setPaddingBottom),
1579
1580 new PropertyWrapperVisitedAffectedColor(CSSPropertyCaretColor, &RenderStyle::caretColor, &RenderStyle::setCaretColor, &RenderStyle::visitedLinkCaretColor, &RenderStyle::setVisitedLinkCaretColor),
1581
1582 new PropertyWrapperVisitedAffectedColor(CSSPropertyColor, &RenderStyle::color, &RenderStyle::setColor, &RenderStyle::visitedLinkColor, &RenderStyle::setVisitedLinkColor),
1583
1584 new PropertyWrapperVisitedAffectedColor(CSSPropertyBackgroundColor, &RenderStyle::backgroundColor, &RenderStyle::setBackgroundColor, &RenderStyle::visitedLinkBackgroundColor, &RenderStyle::setVisitedLinkBackgroundColor),
1585
1586 new FillLayersPropertyWrapper(CSSPropertyBackgroundImage, &RenderStyle::backgroundLayers, &RenderStyle::ensureBackgroundLayers),
1587 new StyleImagePropertyWrapper(CSSPropertyListStyleImage, &RenderStyle::listStyleImage, &RenderStyle::setListStyleImage),
1588 new StyleImagePropertyWrapper(CSSPropertyWebkitMaskImage, &RenderStyle::maskImage, &RenderStyle::setMaskImage),
1589
1590 new StyleImagePropertyWrapper(CSSPropertyBorderImageSource, &RenderStyle::borderImageSource, &RenderStyle::setBorderImageSource),
1591 new LengthVariantPropertyWrapper<LengthBox>(CSSPropertyBorderImageSlice, &RenderStyle::borderImageSlices, &RenderStyle::setBorderImageSlices),
1592 new LengthVariantPropertyWrapper<LengthBox>(CSSPropertyBorderImageWidth, &RenderStyle::borderImageWidth, &RenderStyle::setBorderImageWidth),
1593 new LengthVariantPropertyWrapper<LengthBox>(CSSPropertyBorderImageOutset, &RenderStyle::borderImageOutset, &RenderStyle::setBorderImageOutset),
1594
1595 new StyleImagePropertyWrapper(CSSPropertyWebkitMaskBoxImageSource, &RenderStyle::maskBoxImageSource, &RenderStyle::setMaskBoxImageSource),
1596 new PropertyWrapper<const NinePieceImage&>(CSSPropertyWebkitMaskBoxImage, &RenderStyle::maskBoxImage, &RenderStyle::setMaskBoxImage),
1597
1598 new FillLayersPropertyWrapper(CSSPropertyBackgroundPositionX, &RenderStyle::backgroundLayers, &RenderStyle::ensureBackgroundLayers),
1599 new FillLayersPropertyWrapper(CSSPropertyBackgroundPositionY, &RenderStyle::backgroundLayers, &RenderStyle::ensureBackgroundLayers),
1600 new FillLayersPropertyWrapper(CSSPropertyBackgroundSize, &RenderStyle::backgroundLayers, &RenderStyle::ensureBackgroundLayers),
1601 new FillLayersPropertyWrapper(CSSPropertyWebkitBackgroundSize, &RenderStyle::backgroundLayers, &RenderStyle::ensureBackgroundLayers),
1602
1603 new FillLayersPropertyWrapper(CSSPropertyWebkitMaskPositionX, &RenderStyle::maskLayers, &RenderStyle::ensureMaskLayers),
1604 new FillLayersPropertyWrapper(CSSPropertyWebkitMaskPositionY, &RenderStyle::maskLayers, &RenderStyle::ensureMaskLayers),
1605 new FillLayersPropertyWrapper(CSSPropertyWebkitMaskSize, &RenderStyle::maskLayers, &RenderStyle::ensureMaskLayers),
1606
1607 new PropertyWrapper<float>(CSSPropertyFontSize, &RenderStyle::computedFontSize, &RenderStyle::setFontSize),
1608 new PropertyWrapper<unsigned short>(CSSPropertyColumnRuleWidth, &RenderStyle::columnRuleWidth, &RenderStyle::setColumnRuleWidth),
1609 new LengthVariantPropertyWrapper<GapLength>(CSSPropertyColumnGap, &RenderStyle::columnGap, &RenderStyle::setColumnGap),
1610 new LengthVariantPropertyWrapper<GapLength>(CSSPropertyRowGap, &RenderStyle::rowGap, &RenderStyle::setRowGap),
1611 new PropertyWrapper<unsigned short>(CSSPropertyColumnCount, &RenderStyle::columnCount, &RenderStyle::setColumnCount),
1612 new PropertyWrapper<float>(CSSPropertyColumnWidth, &RenderStyle::columnWidth, &RenderStyle::setColumnWidth),
1613 new PropertyWrapper<float>(CSSPropertyWebkitBorderHorizontalSpacing, &RenderStyle::horizontalBorderSpacing, &RenderStyle::setHorizontalBorderSpacing),
1614 new PropertyWrapper<float>(CSSPropertyWebkitBorderVerticalSpacing, &RenderStyle::verticalBorderSpacing, &RenderStyle::setVerticalBorderSpacing),
1615 new PropertyWrapper<int>(CSSPropertyZIndex, &RenderStyle::zIndex, &RenderStyle::setZIndex),
1616 new PropertyWrapper<short>(CSSPropertyOrphans, &RenderStyle::orphans, &RenderStyle::setOrphans),
1617 new PropertyWrapper<short>(CSSPropertyWidows, &RenderStyle::widows, &RenderStyle::setWidows),
1618 new LengthPropertyWrapper(CSSPropertyLineHeight, &RenderStyle::specifiedLineHeight, &RenderStyle::setLineHeight),
1619 new PropertyWrapper<float>(CSSPropertyOutlineOffset, &RenderStyle::outlineOffset, &RenderStyle::setOutlineOffset),
1620 new PropertyWrapper<float>(CSSPropertyOutlineWidth, &RenderStyle::outlineWidth, &RenderStyle::setOutlineWidth),
1621 new PropertyWrapper<float>(CSSPropertyLetterSpacing, &RenderStyle::letterSpacing, &RenderStyle::setLetterSpacing),
1622 new LengthPropertyWrapper(CSSPropertyWordSpacing, &RenderStyle::wordSpacing, &RenderStyle::setWordSpacing),
1623 new LengthPropertyWrapper(CSSPropertyTextIndent, &RenderStyle::textIndent, &RenderStyle::setTextIndent),
1624
1625 new PropertyWrapper<float>(CSSPropertyPerspective, &RenderStyle::perspective, &RenderStyle::setPerspective),
1626 new LengthPropertyWrapper(CSSPropertyPerspectiveOriginX, &RenderStyle::perspectiveOriginX, &RenderStyle::setPerspectiveOriginX),
1627 new LengthPropertyWrapper(CSSPropertyPerspectiveOriginY, &RenderStyle::perspectiveOriginY, &RenderStyle::setPerspectiveOriginY),
1628 new LengthPropertyWrapper(CSSPropertyTransformOriginX, &RenderStyle::transformOriginX, &RenderStyle::setTransformOriginX),
1629 new LengthPropertyWrapper(CSSPropertyTransformOriginY, &RenderStyle::transformOriginY, &RenderStyle::setTransformOriginY),
1630 new PropertyWrapper<float>(CSSPropertyTransformOriginZ, &RenderStyle::transformOriginZ, &RenderStyle::setTransformOriginZ),
1631 new LengthVariantPropertyWrapper<LengthSize>(CSSPropertyBorderTopLeftRadius, &RenderStyle::borderTopLeftRadius, &RenderStyle::setBorderTopLeftRadius),
1632 new LengthVariantPropertyWrapper<LengthSize>(CSSPropertyBorderTopRightRadius, &RenderStyle::borderTopRightRadius, &RenderStyle::setBorderTopRightRadius),
1633 new LengthVariantPropertyWrapper<LengthSize>(CSSPropertyBorderBottomLeftRadius, &RenderStyle::borderBottomLeftRadius, &RenderStyle::setBorderBottomLeftRadius),
1634 new LengthVariantPropertyWrapper<LengthSize>(CSSPropertyBorderBottomRightRadius, &RenderStyle::borderBottomRightRadius, &RenderStyle::setBorderBottomRightRadius),
1635 new PropertyWrapper<Visibility>(CSSPropertyVisibility, &RenderStyle::visibility, &RenderStyle::setVisibility),
1636 new PropertyWrapper<float>(CSSPropertyZoom, &RenderStyle::zoom, &RenderStyle::setZoomWithoutReturnValue),
1637
1638 new LengthVariantPropertyWrapper<LengthBox>(CSSPropertyClip, &RenderStyle::clip, &RenderStyle::setClip),
1639
1640 new PropertyWrapperAcceleratedOpacity(),
1641 new PropertyWrapperAcceleratedTransform(),
1642
1643 new PropertyWrapperFilter(CSSPropertyFilter, &RenderStyle::filter, &RenderStyle::setFilter),
1644#if ENABLE(FILTERS_LEVEL_2)
1645 new PropertyWrapperFilter(CSSPropertyWebkitBackdropFilter, &RenderStyle::backdropFilter, &RenderStyle::setBackdropFilter),
1646#endif
1647 new PropertyWrapperFilter(CSSPropertyAppleColorFilter, &RenderStyle::appleColorFilter, &RenderStyle::setAppleColorFilter),
1648
1649 new PropertyWrapperClipPath(CSSPropertyWebkitClipPath, &RenderStyle::clipPath, &RenderStyle::setClipPath),
1650
1651 new PropertyWrapperShape(CSSPropertyShapeOutside, &RenderStyle::shapeOutside, &RenderStyle::setShapeOutside),
1652 new LengthPropertyWrapper(CSSPropertyShapeMargin, &RenderStyle::shapeMargin, &RenderStyle::setShapeMargin),
1653 new PropertyWrapper<float>(CSSPropertyShapeImageThreshold, &RenderStyle::shapeImageThreshold, &RenderStyle::setShapeImageThreshold),
1654
1655 new PropertyWrapperVisitedAffectedColor(CSSPropertyColumnRuleColor, MaybeInvalidColor, &RenderStyle::columnRuleColor, &RenderStyle::setColumnRuleColor, &RenderStyle::visitedLinkColumnRuleColor, &RenderStyle::setVisitedLinkColumnRuleColor),
1656 new PropertyWrapperVisitedAffectedColor(CSSPropertyWebkitTextStrokeColor, MaybeInvalidColor, &RenderStyle::textStrokeColor, &RenderStyle::setTextStrokeColor, &RenderStyle::visitedLinkTextStrokeColor, &RenderStyle::setVisitedLinkTextStrokeColor),
1657 new PropertyWrapperVisitedAffectedColor(CSSPropertyWebkitTextFillColor, MaybeInvalidColor, &RenderStyle::textFillColor, &RenderStyle::setTextFillColor, &RenderStyle::visitedLinkTextFillColor, &RenderStyle::setVisitedLinkTextFillColor),
1658 new PropertyWrapperVisitedAffectedColor(CSSPropertyBorderLeftColor, MaybeInvalidColor, &RenderStyle::borderLeftColor, &RenderStyle::setBorderLeftColor, &RenderStyle::visitedLinkBorderLeftColor, &RenderStyle::setVisitedLinkBorderLeftColor),
1659 new PropertyWrapperVisitedAffectedColor(CSSPropertyBorderRightColor, MaybeInvalidColor, &RenderStyle::borderRightColor, &RenderStyle::setBorderRightColor, &RenderStyle::visitedLinkBorderRightColor, &RenderStyle::setVisitedLinkBorderRightColor),
1660 new PropertyWrapperVisitedAffectedColor(CSSPropertyBorderTopColor, MaybeInvalidColor, &RenderStyle::borderTopColor, &RenderStyle::setBorderTopColor, &RenderStyle::visitedLinkBorderTopColor, &RenderStyle::setVisitedLinkBorderTopColor),
1661 new PropertyWrapperVisitedAffectedColor(CSSPropertyBorderBottomColor, MaybeInvalidColor, &RenderStyle::borderBottomColor, &RenderStyle::setBorderBottomColor, &RenderStyle::visitedLinkBorderBottomColor, &RenderStyle::setVisitedLinkBorderBottomColor),
1662 new PropertyWrapperVisitedAffectedColor(CSSPropertyOutlineColor, MaybeInvalidColor, &RenderStyle::outlineColor, &RenderStyle::setOutlineColor, &RenderStyle::visitedLinkOutlineColor, &RenderStyle::setVisitedLinkOutlineColor),
1663
1664 new PropertyWrapperShadow(CSSPropertyBoxShadow, &RenderStyle::boxShadow, &RenderStyle::setBoxShadow),
1665 new PropertyWrapperShadow(CSSPropertyWebkitBoxShadow, &RenderStyle::boxShadow, &RenderStyle::setBoxShadow),
1666 new PropertyWrapperShadow(CSSPropertyTextShadow, &RenderStyle::textShadow, &RenderStyle::setTextShadow),
1667
1668 new PropertyWrapperSVGPaint(CSSPropertyFill, &RenderStyle::fillPaintType, &RenderStyle::fillPaintColor, &RenderStyle::setFillPaintColor),
1669 new PropertyWrapper<float>(CSSPropertyFillOpacity, &RenderStyle::fillOpacity, &RenderStyle::setFillOpacity),
1670
1671 new PropertyWrapperSVGPaint(CSSPropertyStroke, &RenderStyle::strokePaintType, &RenderStyle::strokePaintColor, &RenderStyle::setStrokePaintColor),
1672 new PropertyWrapper<float>(CSSPropertyStrokeOpacity, &RenderStyle::strokeOpacity, &RenderStyle::setStrokeOpacity),
1673 new PropertyWrapper<Vector<SVGLengthValue>>(CSSPropertyStrokeDasharray, &RenderStyle::strokeDashArray, &RenderStyle::setStrokeDashArray),
1674 new PropertyWrapper<float>(CSSPropertyStrokeMiterlimit, &RenderStyle::strokeMiterLimit, &RenderStyle::setStrokeMiterLimit),
1675
1676 new LengthPropertyWrapper(CSSPropertyCx, &RenderStyle::cx, &RenderStyle::setCx),
1677 new LengthPropertyWrapper(CSSPropertyCy, &RenderStyle::cy, &RenderStyle::setCy),
1678 new LengthPropertyWrapper(CSSPropertyR, &RenderStyle::r, &RenderStyle::setR),
1679 new LengthPropertyWrapper(CSSPropertyRx, &RenderStyle::rx, &RenderStyle::setRx),
1680 new LengthPropertyWrapper(CSSPropertyRy, &RenderStyle::ry, &RenderStyle::setRy),
1681 new LengthPropertyWrapper(CSSPropertyStrokeDashoffset, &RenderStyle::strokeDashOffset, &RenderStyle::setStrokeDashOffset),
1682 new LengthPropertyWrapper(CSSPropertyStrokeWidth, &RenderStyle::strokeWidth, &RenderStyle::setStrokeWidth),
1683 new LengthPropertyWrapper(CSSPropertyX, &RenderStyle::x, &RenderStyle::setX),
1684 new LengthPropertyWrapper(CSSPropertyY, &RenderStyle::y, &RenderStyle::setY),
1685
1686 new PropertyWrapper<float>(CSSPropertyFloodOpacity, &RenderStyle::floodOpacity, &RenderStyle::setFloodOpacity),
1687 new PropertyWrapperMaybeInvalidColor(CSSPropertyFloodColor, &RenderStyle::floodColor, &RenderStyle::setFloodColor),
1688
1689 new PropertyWrapper<float>(CSSPropertyStopOpacity, &RenderStyle::stopOpacity, &RenderStyle::setStopOpacity),
1690 new PropertyWrapperMaybeInvalidColor(CSSPropertyStopColor, &RenderStyle::stopColor, &RenderStyle::setStopColor),
1691
1692 new PropertyWrapperMaybeInvalidColor(CSSPropertyLightingColor, &RenderStyle::lightingColor, &RenderStyle::setLightingColor),
1693
1694 new PropertyWrapper<SVGLengthValue>(CSSPropertyBaselineShift, &RenderStyle::baselineShiftValue, &RenderStyle::setBaselineShiftValue),
1695 new PropertyWrapper<SVGLengthValue>(CSSPropertyKerning, &RenderStyle::kerning, &RenderStyle::setKerning),
1696#if ENABLE(VARIATION_FONTS)
1697 new PropertyWrapperFontVariationSettings(CSSPropertyFontVariationSettings, &RenderStyle::fontVariationSettings, &RenderStyle::setFontVariationSettings),
1698#endif
1699 new PropertyWrapper<FontSelectionValue>(CSSPropertyFontWeight, &RenderStyle::fontWeight, &RenderStyle::setFontWeight),
1700 new PropertyWrapper<FontSelectionValue>(CSSPropertyFontStretch, &RenderStyle::fontStretch, &RenderStyle::setFontStretch),
1701 new PropertyWrapperFontStyle(),
1702 new PropertyWrapper<TextDecorationThickness>(CSSPropertyTextDecorationThickness, &RenderStyle::textDecorationThickness, &RenderStyle::setTextDecorationThickness),
1703 new PropertyWrapper<TextUnderlineOffset>(CSSPropertyTextUnderlineOffset, &RenderStyle::textUnderlineOffset, &RenderStyle::setTextUnderlineOffset),
1704 };
1705 const unsigned animatableLonghandPropertiesCount = WTF_ARRAY_LENGTH(animatableLonghandPropertyWrappers);
1706
1707 static const CSSPropertyID animatableShorthandProperties[] = {
1708 CSSPropertyBackground, // for background-color, background-position, background-image
1709 CSSPropertyBackgroundPosition,
1710 CSSPropertyFont, // for font-size, font-weight
1711 CSSPropertyWebkitMask, // for mask-position
1712 CSSPropertyWebkitMaskPosition,
1713 CSSPropertyBorderTop, CSSPropertyBorderRight, CSSPropertyBorderBottom, CSSPropertyBorderLeft,
1714 CSSPropertyBorderColor,
1715 CSSPropertyBorderRadius,
1716 CSSPropertyBorderWidth,
1717 CSSPropertyBorder,
1718 CSSPropertyBorderImage,
1719 CSSPropertyBorderSpacing,
1720 CSSPropertyListStyle, // for list-style-image
1721 CSSPropertyMargin,
1722 CSSPropertyOutline,
1723 CSSPropertyPadding,
1724 CSSPropertyWebkitTextStroke,
1725 CSSPropertyColumnRule,
1726 CSSPropertyWebkitBorderRadius,
1727 CSSPropertyTransformOrigin
1728 };
1729 const unsigned animatableShorthandPropertiesCount = WTF_ARRAY_LENGTH(animatableShorthandProperties);
1730
1731 // TODO:
1732 //
1733 // CSSPropertyVerticalAlign
1734 //
1735 // Compound properties that have components that should be animatable:
1736 //
1737 // CSSPropertyColumns
1738 // CSSPropertyWebkitBoxReflect
1739
1740 // Make sure unused slots have a value
1741 for (int i = 0; i < numCSSProperties; ++i)
1742 m_propertyToIdMap[i] = cInvalidPropertyWrapperIndex;
1743
1744 COMPILE_ASSERT(animatableLonghandPropertiesCount + animatableShorthandPropertiesCount < UCHAR_MAX, numberOfAnimatablePropertiesMustBeLessThanUCharMax);
1745 m_propertyWrappers.reserveInitialCapacity(animatableLonghandPropertiesCount + animatableShorthandPropertiesCount);
1746
1747 // First we put the non-shorthand property wrappers into the map, so the shorthand-building
1748 // code can find them.
1749
1750 for (unsigned i = 0; i < animatableLonghandPropertiesCount; ++i) {
1751 AnimationPropertyWrapperBase* wrapper = animatableLonghandPropertyWrappers[i];
1752 m_propertyWrappers.uncheckedAppend(std::unique_ptr<AnimationPropertyWrapperBase>(wrapper));
1753 indexFromPropertyID(wrapper->property()) = i;
1754 }
1755
1756 for (size_t i = 0; i < animatableShorthandPropertiesCount; ++i) {
1757 CSSPropertyID propertyID = animatableShorthandProperties[i];
1758 StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
1759 if (!shorthand.length())
1760 continue;
1761
1762 Vector<AnimationPropertyWrapperBase*> longhandWrappers;
1763 longhandWrappers.reserveInitialCapacity(shorthand.length());
1764 const CSSPropertyID* properties = shorthand.properties();
1765 for (unsigned j = 0; j < shorthand.length(); ++j) {
1766 unsigned wrapperIndex = indexFromPropertyID(properties[j]);
1767 if (wrapperIndex == cInvalidPropertyWrapperIndex)
1768 continue;
1769 ASSERT(m_propertyWrappers[wrapperIndex]);
1770 longhandWrappers.uncheckedAppend(m_propertyWrappers[wrapperIndex].get());
1771 }
1772
1773 m_propertyWrappers.uncheckedAppend(std::make_unique<ShorthandPropertyWrapper>(propertyID, WTFMove(longhandWrappers)));
1774 indexFromPropertyID(propertyID) = animatableLonghandPropertiesCount + i;
1775 }
1776}
1777
1778static bool gatherEnclosingShorthandProperties(CSSPropertyID property, AnimationPropertyWrapperBase* wrapper, HashSet<CSSPropertyID>& propertySet)
1779{
1780 if (!wrapper->isShorthandWrapper())
1781 return false;
1782
1783 ShorthandPropertyWrapper* shorthandWrapper = static_cast<ShorthandPropertyWrapper*>(wrapper);
1784 bool contained = false;
1785 for (auto& currWrapper : shorthandWrapper->propertyWrappers()) {
1786 if (gatherEnclosingShorthandProperties(property, currWrapper, propertySet) || currWrapper->property() == property)
1787 contained = true;
1788 }
1789
1790 if (contained)
1791 propertySet.add(wrapper->property());
1792
1793 return contained;
1794}
1795
1796// Returns true if we need to start animation timers
1797bool CSSPropertyAnimation::blendProperties(const CSSPropertyBlendingClient* anim, CSSPropertyID prop, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress)
1798{
1799 ASSERT(prop != CSSPropertyInvalid);
1800
1801 AnimationPropertyWrapperBase* wrapper = CSSPropertyAnimationWrapperMap::singleton().wrapperForProperty(prop);
1802 if (wrapper) {
1803 wrapper->blend(anim, dst, a, b, progress);
1804#if !LOG_DISABLED
1805 wrapper->logBlend(a, b, dst, progress);
1806#endif
1807 return !wrapper->animationIsAccelerated() || !anim->isAccelerated();
1808 }
1809 return false;
1810}
1811
1812bool CSSPropertyAnimation::isPropertyAnimatable(CSSPropertyID prop)
1813{
1814 return CSSPropertyAnimationWrapperMap::singleton().wrapperForProperty(prop);
1815}
1816
1817bool CSSPropertyAnimation::animationOfPropertyIsAccelerated(CSSPropertyID prop)
1818{
1819 AnimationPropertyWrapperBase* wrapper = CSSPropertyAnimationWrapperMap::singleton().wrapperForProperty(prop);
1820 return wrapper ? wrapper->animationIsAccelerated() : false;
1821}
1822
1823// Note: this is inefficient. It's only called from pauseTransitionAtTime().
1824HashSet<CSSPropertyID> CSSPropertyAnimation::animatableShorthandsAffectingProperty(CSSPropertyID property)
1825{
1826 CSSPropertyAnimationWrapperMap& map = CSSPropertyAnimationWrapperMap::singleton();
1827
1828 HashSet<CSSPropertyID> foundProperties;
1829 for (unsigned i = 0; i < map.size(); ++i)
1830 gatherEnclosingShorthandProperties(property, map.wrapperForIndex(i), foundProperties);
1831
1832 return foundProperties;
1833}
1834
1835bool CSSPropertyAnimation::propertiesEqual(CSSPropertyID prop, const RenderStyle* a, const RenderStyle* b)
1836{
1837 AnimationPropertyWrapperBase* wrapper = CSSPropertyAnimationWrapperMap::singleton().wrapperForProperty(prop);
1838 if (wrapper)
1839 return wrapper->equals(a, b);
1840 return true;
1841}
1842
1843bool CSSPropertyAnimation::canPropertyBeInterpolated(CSSPropertyID prop, const RenderStyle* a, const RenderStyle* b)
1844{
1845 AnimationPropertyWrapperBase* wrapper = CSSPropertyAnimationWrapperMap::singleton().wrapperForProperty(prop);
1846 if (wrapper)
1847 return wrapper->canInterpolate(a, b);
1848 return false;
1849}
1850
1851CSSPropertyID CSSPropertyAnimation::getPropertyAtIndex(int i, Optional<bool>& isShorthand)
1852{
1853 CSSPropertyAnimationWrapperMap& map = CSSPropertyAnimationWrapperMap::singleton();
1854
1855 if (i < 0 || static_cast<unsigned>(i) >= map.size())
1856 return CSSPropertyInvalid;
1857
1858 AnimationPropertyWrapperBase* wrapper = map.wrapperForIndex(i);
1859 isShorthand = wrapper->isShorthandWrapper();
1860 return wrapper->property();
1861}
1862
1863int CSSPropertyAnimation::getNumProperties()
1864{
1865 return CSSPropertyAnimationWrapperMap::singleton().size();
1866}
1867
1868}
1869