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 "platform/PlatformExport.h"
27#include "platform/animation/AnimationUtilities.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/Vector.h"
34#include <cstring>
35
36namespace blink {
37
38// FIXME: This enum makes it hard to tell in general what values may be
39// appropriate for any given Length.
40enum LengthType {
41    Auto, Percent, Fixed,
42    Intrinsic, MinIntrinsic,
43    MinContent, MaxContent, FillAvailable, FitContent,
44    Calculated,
45    ExtendToZoom, DeviceWidth, DeviceHeight,
46    MaxSizeNone
47};
48
49enum ValueRange {
50    ValueRangeAll,
51    ValueRangeNonNegative
52};
53
54struct PixelsAndPercent {
55    PixelsAndPercent(float pixels, float percent)
56        : pixels(pixels)
57        , percent(percent)
58    {
59    }
60    float pixels;
61    float percent;
62};
63
64class CalculationValue;
65
66class PLATFORM_EXPORT Length {
67    WTF_MAKE_FAST_ALLOCATED;
68public:
69    Length()
70        :  m_intValue(0), m_quirk(false), m_type(Auto), m_isFloat(false)
71    {
72    }
73
74    Length(LengthType t)
75        : m_intValue(0), m_quirk(false), m_type(t), m_isFloat(false)
76    {
77        ASSERT(t != Calculated);
78    }
79
80    Length(int v, LengthType t, bool q = false)
81        : m_intValue(v), m_quirk(q), m_type(t), m_isFloat(false)
82    {
83        ASSERT(t != Calculated);
84    }
85
86    Length(LayoutUnit v, LengthType t, bool q = false)
87        : m_floatValue(v.toFloat()), m_quirk(q), m_type(t), m_isFloat(true)
88    {
89        ASSERT(t != Calculated);
90    }
91
92    Length(float v, LengthType t, bool q = false)
93        : m_floatValue(v), m_quirk(q), m_type(t), m_isFloat(true)
94    {
95        ASSERT(t != Calculated);
96    }
97
98    Length(double v, LengthType t, bool q = false)
99        : m_quirk(q), m_type(t), m_isFloat(true)
100    {
101        m_floatValue = static_cast<float>(v);
102    }
103
104    explicit Length(PassRefPtr<CalculationValue>);
105
106    Length(const Length& length)
107    {
108        memcpy(this, &length, sizeof(Length));
109        if (isCalculated())
110            incrementCalculatedRef();
111    }
112
113    Length& operator=(const Length& length)
114    {
115        if (length.isCalculated())
116            length.incrementCalculatedRef();
117        if (isCalculated())
118            decrementCalculatedRef();
119        memcpy(this, &length, sizeof(Length));
120        return *this;
121    }
122
123#if COMPILER_SUPPORTS(CXX_RVALUE_REFERENCES)
124    Length(Length&& length)
125    {
126        memcpy(this, &length, sizeof(Length));
127
128        // Reset |length|'s type to Auto to make sure its destructor
129        // won't call decrementCalculatedRef() as we don't call
130        // incrementCalculatedRef() here.
131        length.m_type = Auto;
132    }
133
134    Length& operator=(Length&& length)
135    {
136        if (this == &length)
137            return *this;
138
139        if (isCalculated())
140            decrementCalculatedRef();
141
142        memcpy(this, &length, sizeof(Length));
143
144        // Reset |length|'s type to Auto to make sure its destructor
145        // won't call decrementCalculatedRef() as we don't call
146        // incrementCalculatedRef() here.
147        length.m_type = Auto;
148
149        return *this;
150    }
151#endif
152
153    ~Length()
154    {
155        if (isCalculated())
156            decrementCalculatedRef();
157    }
158
159    bool operator==(const Length& o) const { return (m_type == o.m_type) && (m_quirk == o.m_quirk) && (isMaxSizeNone() || (getFloatValue() == o.getFloatValue()) || isCalculatedEqual(o)); }
160    bool operator!=(const Length& o) const { return !(*this == o); }
161
162    const Length& operator*=(float v)
163    {
164        if (isCalculated()) {
165            ASSERT_NOT_REACHED();
166            return *this;
167        }
168
169        if (m_isFloat)
170            m_floatValue = static_cast<float>(m_floatValue * v);
171        else
172            m_intValue = static_cast<int>(m_intValue * v);
173
174        return *this;
175    }
176
177    inline float value() const
178    {
179        return getFloatValue();
180    }
181
182    int intValue() const
183    {
184        if (isCalculated()) {
185            ASSERT_NOT_REACHED();
186            return 0;
187        }
188        return getIntValue();
189    }
190
191    float percent() const
192    {
193        ASSERT(type() == Percent);
194        return getFloatValue();
195    }
196    PixelsAndPercent pixelsAndPercent() const;
197
198    CalculationValue& calculationValue() const;
199
200    LengthType type() const { return static_cast<LengthType>(m_type); }
201    bool quirk() const { return m_quirk; }
202
203    void setQuirk(bool quirk)
204    {
205        m_quirk = quirk;
206    }
207
208    void setValue(LengthType t, int value)
209    {
210        m_type = t;
211        m_intValue = value;
212        m_isFloat = false;
213    }
214
215    void setValue(int value)
216    {
217        if (isCalculated()) {
218            ASSERT_NOT_REACHED();
219            return;
220        }
221        setValue(Fixed, value);
222    }
223
224    void setValue(LengthType t, float value)
225    {
226        m_type = t;
227        m_floatValue = value;
228        m_isFloat = true;
229    }
230
231    void setValue(LengthType t, LayoutUnit value)
232    {
233        m_type = t;
234        m_floatValue = value.toFloat();
235        m_isFloat = true;
236    }
237
238    void setValue(float value)
239    {
240        *this = Length(value, Fixed);
241    }
242
243    bool isMaxSizeNone() const { return type() == MaxSizeNone; }
244
245    // FIXME calc: https://bugs.webkit.org/show_bug.cgi?id=80357. A calculated Length
246    // always contains a percentage, and without a maxValue passed to these functions
247    // it's impossible to determine the sign or zero-ness. We assume all calc values
248    // are positive and non-zero for now.
249    bool isZero() const
250    {
251        ASSERT(!isMaxSizeNone());
252        if (isCalculated())
253            return false;
254
255        return m_isFloat ? !m_floatValue : !m_intValue;
256    }
257    bool isPositive() const
258    {
259        if (isMaxSizeNone())
260            return false;
261        if (isCalculated())
262            return true;
263
264        return getFloatValue() > 0;
265    }
266    bool isNegative() const
267    {
268        if (isMaxSizeNone() || isCalculated())
269            return false;
270
271        return getFloatValue() < 0;
272    }
273
274    bool isAuto() const { return type() == Auto; }
275    bool isPercent() const { return type() == Percent || type() == Calculated; }
276    bool isFixed() const { return type() == Fixed; }
277    bool isIntrinsicOrAuto() const { return type() == Auto || isLegacyIntrinsic() || isIntrinsic(); }
278    bool isLegacyIntrinsic() const { return type() == Intrinsic || type() == MinIntrinsic; }
279    bool isIntrinsic() const { return type() == MinContent || type() == MaxContent || type() == FillAvailable || type() == FitContent; }
280    bool isSpecified() const { return type() == Fixed || type() == Percent || type() == Calculated; }
281    bool isSpecifiedOrIntrinsic() const { return isSpecified() || isIntrinsic(); }
282    bool isCalculated() const { return type() == Calculated; }
283    bool isCalculatedEqual(const Length&) const;
284    bool isMinContent() const { return type() == MinContent; }
285    bool isMaxContent() const { return type() == MaxContent; }
286    bool isFillAvailable() const { return type() == FillAvailable; }
287    bool isFitContent() const { return type() == FitContent; }
288
289    Length blend(const Length& from, double progress, ValueRange range) const
290    {
291        ASSERT(isSpecified() && from.isSpecified());
292
293        if (progress == 0.0)
294            return from;
295
296        if (progress == 1.0)
297            return *this;
298
299        if (from.type() == Calculated || type() == Calculated)
300            return blendMixedTypes(from, progress, range);
301
302        if (!from.isZero() && !isZero() && from.type() != type())
303            return blendMixedTypes(from, progress, range);
304
305        if (from.isZero() && isZero())
306            return *this;
307
308        LengthType resultType = type();
309        if (isZero())
310            resultType = from.type();
311
312        float blendedValue = blink::blend(from.value(), value(), progress);
313        if (range == ValueRangeNonNegative)
314            blendedValue = clampTo<float>(blendedValue, 0);
315        return Length(blendedValue, resultType);
316    }
317
318    float getFloatValue() const
319    {
320        ASSERT(!isMaxSizeNone());
321        return m_isFloat ? m_floatValue : m_intValue;
322    }
323    float nonNanCalculatedValue(int maxValue) const;
324
325    Length subtractFromOneHundredPercent() const;
326
327private:
328    int getIntValue() const
329    {
330        ASSERT(!isMaxSizeNone());
331        return m_isFloat ? static_cast<int>(m_floatValue) : m_intValue;
332    }
333
334    Length blendMixedTypes(const Length& from, double progress, ValueRange) const;
335
336    int calculationHandle() const
337    {
338        ASSERT(isCalculated());
339        return getIntValue();
340    }
341    void incrementCalculatedRef() const;
342    void decrementCalculatedRef() const;
343
344    union {
345        int m_intValue;
346        float m_floatValue;
347    };
348    bool m_quirk;
349    unsigned char m_type;
350    bool m_isFloat;
351};
352
353PLATFORM_EXPORT Vector<Length> parseHTMLAreaElementCoords(const String&);
354
355} // namespace blink
356
357#endif // Length_h
358