1/*
2    Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3    Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
4    Copyright (C) 2011 Rik Cabanier (cabanier@adobe.com)
5    Copyright (C) 2011 Adobe Systems Incorporated. All rights reserved.
6
7    This library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Library General Public
9    License as published by the Free Software Foundation; either
10    version 2 of the License, or (at your option) any later version.
11
12    This library is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Library General Public License for more details.
16
17    You should have received a copy of the GNU Library General Public License
18    along with this library; see the file COPYING.LIB.  If not, write to
19    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20    Boston, MA 02110-1301, USA.
21*/
22
23#ifndef Length_h
24#define Length_h
25
26#include "AnimationUtilities.h"
27#include <string.h>
28#include <wtf/Assertions.h>
29#include <wtf/FastAllocBase.h>
30#include <wtf/Forward.h>
31#include <wtf/HashMap.h>
32#include <wtf/MathExtras.h>
33#include <wtf/PassOwnArrayPtr.h>
34
35namespace WebCore {
36
37enum LengthType {
38    Auto, Relative, Percent, Fixed,
39    Intrinsic, MinIntrinsic,
40    MinContent, MaxContent, FillAvailable, FitContent,
41    Calculated,
42    ViewportPercentageWidth, ViewportPercentageHeight, ViewportPercentageMin, ViewportPercentageMax,
43    Undefined
44};
45
46class CalculationValue;
47
48struct Length {
49    WTF_MAKE_FAST_ALLOCATED;
50public:
51    Length()
52        :  m_intValue(0), m_quirk(false), m_type(Auto), m_isFloat(false)
53    {
54    }
55
56    Length(LengthType t)
57        : m_intValue(0), m_quirk(false), m_type(t), m_isFloat(false)
58    {
59        ASSERT(t != Calculated);
60    }
61
62    Length(int v, LengthType t, bool q = false)
63        : m_intValue(v), m_quirk(q), m_type(t), m_isFloat(false)
64    {
65        ASSERT(t != Calculated);
66    }
67
68    Length(LayoutUnit v, LengthType t, bool q = false)
69        : m_floatValue(v.toFloat()), m_quirk(q), m_type(t), m_isFloat(true)
70    {
71        ASSERT(t != Calculated);
72    }
73
74    Length(float v, LengthType t, bool q = false)
75        : m_floatValue(v), m_quirk(q), m_type(t), m_isFloat(true)
76    {
77        ASSERT(t != Calculated);
78    }
79
80    Length(double v, LengthType t, bool q = false)
81        : m_quirk(q), m_type(t), m_isFloat(true)
82    {
83        m_floatValue = static_cast<float>(v);
84    }
85
86    explicit Length(PassRefPtr<CalculationValue>);
87
88    Length(const Length& length)
89    {
90        initFromLength(length);
91    }
92
93    Length& operator=(const Length& length)
94    {
95        initFromLength(length);
96        return *this;
97    }
98
99    ~Length()
100    {
101        if (isCalculated())
102            decrementCalculatedRef();
103    }
104
105    bool operator==(const Length& o) const { return (m_type == o.m_type) && (m_quirk == o.m_quirk) && (isUndefined() || (getFloatValue() == o.getFloatValue()) || isCalculatedEqual(o)); }
106    bool operator!=(const Length& o) const { return !(*this == o); }
107
108    const Length& operator*=(float v)
109    {
110        if (isCalculated()) {
111            ASSERT_NOT_REACHED();
112            return *this;
113        }
114
115        if (m_isFloat)
116            m_floatValue = static_cast<float>(m_floatValue * v);
117        else
118            m_intValue = static_cast<int>(m_intValue * v);
119
120        return *this;
121    }
122
123    inline float value() const
124    {
125        return getFloatValue();
126    }
127
128     int intValue() const
129     {
130        if (isCalculated()) {
131            ASSERT_NOT_REACHED();
132            return 0;
133        }
134        return getIntValue();
135    }
136
137    float percent() const
138    {
139        ASSERT(type() == Percent);
140        return getFloatValue();
141    }
142
143    PassRefPtr<CalculationValue> calculationValue() const;
144
145    LengthType type() const { return static_cast<LengthType>(m_type); }
146    bool quirk() const { return m_quirk; }
147
148    void setQuirk(bool quirk)
149    {
150        m_quirk = quirk;
151    }
152
153    void setValue(LengthType t, int value)
154    {
155        m_type = t;
156        m_intValue = value;
157        m_isFloat = false;
158    }
159
160    void setValue(int value)
161    {
162        if (isCalculated()) {
163            ASSERT_NOT_REACHED();
164            return;
165        }
166        setValue(Fixed, value);
167    }
168
169    void setValue(LengthType t, float value)
170    {
171        m_type = t;
172        m_floatValue = value;
173        m_isFloat = true;
174    }
175
176    void setValue(LengthType t, LayoutUnit value)
177    {
178        m_type = t;
179        m_floatValue = value;
180        m_isFloat = true;
181    }
182
183    void setValue(float value)
184    {
185        *this = Length(value, Fixed);
186    }
187
188    bool isUndefined() const { return type() == Undefined; }
189
190    // FIXME calc: https://bugs.webkit.org/show_bug.cgi?id=80357. A calculated Length
191    // always contains a percentage, and without a maxValue passed to these functions
192    // it's impossible to determine the sign or zero-ness. We assume all calc values
193    // are positive and non-zero for now.
194    bool isZero() const
195    {
196        ASSERT(!isUndefined());
197        if (isCalculated())
198            return false;
199
200        return m_isFloat ? !m_floatValue : !m_intValue;
201    }
202    bool isPositive() const
203    {
204        if (isUndefined())
205            return false;
206        if (isCalculated())
207            return true;
208
209        return getFloatValue() > 0;
210    }
211    bool isNegative() const
212    {
213        if (isUndefined() || isCalculated())
214            return false;
215
216        return getFloatValue() < 0;
217    }
218
219    bool isAuto() const { return type() == Auto; }
220    bool isRelative() const { return type() == Relative; }
221    bool isPercent() const { return type() == Percent || type() == Calculated; }
222    bool isFixed() const { return type() == Fixed; }
223    bool isIntrinsicOrAuto() const { return type() == Auto || isLegacyIntrinsic() || isIntrinsic(); }
224    bool isLegacyIntrinsic() const { return type() == Intrinsic || type() == MinIntrinsic; }
225    bool isIntrinsic() const { return type() == MinContent || type() == MaxContent || type() == FillAvailable || type() == FitContent; }
226    bool isSpecified() const { return type() == Fixed || type() == Percent || type() == Calculated || isViewportPercentage(); }
227    bool isSpecifiedOrIntrinsic() const { return isSpecified() || isIntrinsic(); }
228    bool isCalculated() const { return type() == Calculated; }
229    bool isCalculatedEqual(const Length&) const;
230    bool isMinContent() const { return type() == MinContent; }
231    bool isMaxContent() const { return type() == MaxContent; }
232
233    Length blend(const Length& from, double progress) const
234    {
235        // Blend two lengths to produce a new length that is in between them.  Used for animation.
236        if (from.type() == Calculated || type() == Calculated)
237            return blendMixedTypes(from, progress);
238
239        if (!from.isZero() && !isZero() && from.type() != type())
240            return blendMixedTypes(from, progress);
241
242        if (from.isZero() && isZero())
243            return *this;
244
245        LengthType resultType = type();
246        if (isZero())
247            resultType = from.type();
248
249        if (resultType == Percent) {
250            float fromPercent = from.isZero() ? 0 : from.percent();
251            float toPercent = isZero() ? 0 : percent();
252            return Length(WebCore::blend(fromPercent, toPercent, progress), Percent);
253        }
254
255        float fromValue = from.isZero() ? 0 : from.value();
256        float toValue = isZero() ? 0 : value();
257        return Length(WebCore::blend(fromValue, toValue, progress), resultType);
258    }
259
260    float getFloatValue() const
261    {
262        ASSERT(!isUndefined());
263        return m_isFloat ? m_floatValue : m_intValue;
264    }
265    float nonNanCalculatedValue(int maxValue) const;
266
267    bool isViewportPercentage() const
268    {
269        LengthType lengthType = type();
270        return lengthType >= ViewportPercentageWidth && lengthType <= ViewportPercentageMax;
271    }
272    float viewportPercentageLength() const
273    {
274        ASSERT(isViewportPercentage());
275        return getFloatValue();
276    }
277private:
278    int getIntValue() const
279    {
280        ASSERT(!isUndefined());
281        return m_isFloat ? static_cast<int>(m_floatValue) : m_intValue;
282    }
283    void initFromLength(const Length& length)
284    {
285        memcpy(this, &length, sizeof(Length));
286        if (isCalculated())
287            incrementCalculatedRef();
288    }
289
290    Length blendMixedTypes(const Length& from, double progress) const;
291
292    int calculationHandle() const
293    {
294        ASSERT(isCalculated());
295        return getIntValue();
296    }
297    void incrementCalculatedRef() const;
298    void decrementCalculatedRef() const;
299
300    union {
301        int m_intValue;
302        float m_floatValue;
303    };
304    bool m_quirk;
305    unsigned char m_type;
306    bool m_isFloat;
307};
308
309PassOwnArrayPtr<Length> newCoordsArray(const String&, int& len);
310PassOwnArrayPtr<Length> newLengthArray(const String&, int& len);
311
312} // namespace WebCore
313
314#endif // Length_h
315