1// Copyright 2014 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 "config.h"
6#include "core/animation/LengthStyleInterpolation.h"
7
8#include "core/css/CSSCalculationValue.h"
9#include "core/css/resolver/StyleBuilder.h"
10
11namespace blink {
12
13bool LengthStyleInterpolation::canCreateFrom(const CSSValue& value)
14{
15    if (value.isPrimitiveValue()) {
16        const CSSPrimitiveValue& primitiveValue = blink::toCSSPrimitiveValue(value);
17        if (primitiveValue.cssCalcValue())
18            return true;
19
20        CSSPrimitiveValue::LengthUnitType type;
21        // Only returns true if the type is a primitive length unit.
22        return CSSPrimitiveValue::unitTypeToLengthUnitType(primitiveValue.primitiveType(), type);
23    }
24    return value.isCalcValue();
25}
26
27PassOwnPtrWillBeRawPtr<InterpolableValue> LengthStyleInterpolation::lengthToInterpolableValue(CSSValue* value)
28{
29    OwnPtrWillBeRawPtr<InterpolableList> result = InterpolableList::create(CSSPrimitiveValue::LengthUnitTypeCount);
30    CSSPrimitiveValue* primitive = toCSSPrimitiveValue(value);
31
32    CSSLengthArray array;
33    for (size_t i = 0; i < CSSPrimitiveValue::LengthUnitTypeCount; i++)
34        array.append(0);
35    primitive->accumulateLengthArray(array);
36
37    for (size_t i = 0; i < CSSPrimitiveValue::LengthUnitTypeCount; i++)
38        result->set(i, InterpolableNumber::create(array.at(i)));
39
40    return result.release();
41}
42
43namespace {
44
45static CSSPrimitiveValue::UnitType toUnitType(int lengthUnitType)
46{
47    return static_cast<CSSPrimitiveValue::UnitType>(CSSPrimitiveValue::lengthUnitTypeToUnitType(static_cast<CSSPrimitiveValue::LengthUnitType>(lengthUnitType)));
48}
49
50static PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> constructCalcExpression(PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> previous, InterpolableList* list, size_t position)
51{
52    while (position != CSSPrimitiveValue::LengthUnitTypeCount) {
53        const InterpolableNumber *subValue = toInterpolableNumber(list->get(position));
54        if (subValue->value()) {
55            RefPtrWillBeRawPtr<CSSCalcExpressionNode> next;
56            if (previous)
57                next = CSSCalcValue::createExpressionNode(previous, CSSCalcValue::createExpressionNode(CSSPrimitiveValue::create(subValue->value(), toUnitType(position))), CalcAdd);
58            else
59                next = CSSCalcValue::createExpressionNode(CSSPrimitiveValue::create(subValue->value(), toUnitType(position)));
60            return constructCalcExpression(next, list, position + 1);
61        }
62        position++;
63    }
64    return previous;
65}
66
67}
68
69PassRefPtrWillBeRawPtr<CSSValue> LengthStyleInterpolation::interpolableValueToLength(InterpolableValue* value, ValueRange range)
70{
71    InterpolableList* listValue = toInterpolableList(value);
72    unsigned unitCount = 0;
73    for (size_t i = 0; i < CSSPrimitiveValue::LengthUnitTypeCount; i++) {
74        const InterpolableNumber* subValue = toInterpolableNumber(listValue->get(i));
75        if (subValue->value()) {
76            unitCount++;
77        }
78    }
79
80    switch (unitCount) {
81    case 0:
82        return CSSPrimitiveValue::create(0, CSSPrimitiveValue::CSS_PX);
83    case 1:
84        for (size_t i = 0; i < CSSPrimitiveValue::LengthUnitTypeCount; i++) {
85            const InterpolableNumber* subValue = toInterpolableNumber(listValue->get(i));
86            double value = subValue->value();
87            if (value) {
88                if (range == ValueRangeNonNegative && value < 0)
89                    value = 0;
90                return CSSPrimitiveValue::create(value, toUnitType(i));
91            }
92        }
93        ASSERT_NOT_REACHED();
94    default:
95        return CSSPrimitiveValue::create(CSSCalcValue::create(constructCalcExpression(nullptr, listValue, 0), range));
96    }
97}
98
99void LengthStyleInterpolation::apply(StyleResolverState& state) const
100{
101    StyleBuilder::applyProperty(m_id, state, interpolableValueToLength(m_cachedValue.get(), m_range).get());
102}
103
104void LengthStyleInterpolation::trace(Visitor* visitor)
105{
106    StyleInterpolation::trace(visitor);
107}
108
109}
110