1/*
2    Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3    Copyright (C) 2006, 2008, 2014 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 <memory>
28#include <string.h>
29#include <wtf/Assertions.h>
30#include <wtf/FastMalloc.h>
31#include <wtf/Forward.h>
32
33namespace WebCore {
34
35enum LengthType {
36    Auto, Relative, Percent, Fixed,
37    Intrinsic, MinIntrinsic,
38    MinContent, MaxContent, FillAvailable, FitContent,
39    Calculated,
40    Undefined
41};
42
43class CalculationValue;
44
45struct Length {
46    WTF_MAKE_FAST_ALLOCATED;
47public:
48    Length(LengthType = Auto);
49
50    Length(int value, LengthType, bool hasQuirk = false);
51    Length(LayoutUnit value, LengthType, bool hasQuirk = false);
52    Length(float value, LengthType, bool hasQuirk = false);
53    Length(double value, LengthType, bool hasQuirk = false);
54
55    explicit Length(PassRef<CalculationValue>);
56
57    Length(const Length&);
58    Length(Length&&);
59    Length& operator=(const Length&);
60    Length& operator=(Length&&);
61
62    ~Length();
63
64    void setValue(LengthType, int value);
65    void setValue(LengthType, float value);
66    void setValue(LengthType, LayoutUnit value);
67    Length& operator*=(float);
68
69    void setHasQuirk(bool);
70
71    bool operator==(const Length&) const;
72    bool operator!=(const Length&) const;
73
74    float value() const;
75    int intValue() const;
76    float percent() const;
77    float viewportPercentageLength() const;
78    CalculationValue& calculationValue() const;
79
80    LengthType type() const;
81
82    bool isAuto() const;
83    bool isCalculated() const;
84    bool isFixed() const;
85    bool isMaxContent() const;
86    bool isMinContent() const;
87    bool isPercentNotCalculated() const; // FIXME: Rename to isPercent.
88    bool isRelative() const;
89    bool isUndefined() const;
90
91    bool hasQuirk() const;
92
93    // FIXME calc: https://bugs.webkit.org/show_bug.cgi?id=80357. A calculated Length
94    // always contains a percentage, and without a maxValue passed to these functions
95    // it's impossible to determine the sign or zero-ness. The following three functions
96    // act as if all calculated values are positive.
97    bool isZero() const;
98    bool isPositive() const;
99    bool isNegative() const;
100
101    bool isPercent() const; // Returns true for both Percent and Calculated. FIXME: Find a better name for this.
102
103    bool isIntrinsic() const;
104    bool isIntrinsicOrAuto() const;
105    bool isSpecified() const;
106    bool isSpecifiedOrIntrinsic() const;
107
108    // Blend two lengths to produce a new length that is in between them. Used for animation.
109    // FIXME: Why is this a member function?
110    Length blend(const Length& from, double progress) const;
111
112    float nonNanCalculatedValue(int maxValue) const;
113
114private:
115    bool isLegacyIntrinsic() const;
116
117    bool isCalculatedEqual(const Length&) const;
118    Length blendMixedTypes(const Length& from, double progress) const;
119
120    void ref() const;
121    void deref() const;
122
123    union {
124        int m_intValue;
125        float m_floatValue;
126        unsigned m_calculationValueHandle;
127    };
128    bool m_hasQuirk;
129    unsigned char m_type;
130    bool m_isFloat;
131};
132
133std::unique_ptr<Length[]> newCoordsArray(const String&, int& length);
134std::unique_ptr<Length[]> newLengthArray(const String&, int& length);
135
136inline Length::Length(LengthType type)
137    : m_intValue(0), m_hasQuirk(false), m_type(type), m_isFloat(false)
138{
139    ASSERT(type != Calculated);
140}
141
142inline Length::Length(int value, LengthType type, bool hasQuirk)
143    : m_intValue(value), m_hasQuirk(hasQuirk), m_type(type), m_isFloat(false)
144{
145    ASSERT(type != Calculated);
146}
147
148inline Length::Length(LayoutUnit value, LengthType type, bool hasQuirk)
149    : m_floatValue(value.toFloat()), m_hasQuirk(hasQuirk), m_type(type), m_isFloat(true)
150{
151    ASSERT(type != Calculated);
152}
153
154inline Length::Length(float value, LengthType type, bool hasQuirk)
155    : m_floatValue(value), m_hasQuirk(hasQuirk), m_type(type), m_isFloat(true)
156{
157    ASSERT(type != Calculated);
158}
159
160inline Length::Length(double value, LengthType type, bool hasQuirk)
161    : m_floatValue(static_cast<float>(value)), m_hasQuirk(hasQuirk), m_type(type), m_isFloat(true)
162{
163    ASSERT(type != Calculated);
164}
165
166inline Length::Length(const Length& other)
167{
168    if (other.isCalculated())
169        other.ref();
170
171    memcpy(this, &other, sizeof(Length));
172}
173
174inline Length::Length(Length&& other)
175{
176    memcpy(this, &other, sizeof(Length));
177    other.m_type = Auto;
178}
179
180inline Length& Length::operator=(const Length& other)
181{
182    if (other.isCalculated())
183        other.ref();
184    if (isCalculated())
185        deref();
186
187    memcpy(this, &other, sizeof(Length));
188    return *this;
189}
190
191inline Length& Length::operator=(Length&& other)
192{
193    if (this == &other)
194        return *this;
195
196    if (isCalculated())
197        deref();
198
199    memcpy(this, &other, sizeof(Length));
200    other.m_type = Auto;
201    return *this;
202}
203
204inline Length::~Length()
205{
206    if (isCalculated())
207        deref();
208}
209
210inline bool Length::operator==(const Length& other) const
211{
212    // FIXME: This might be too long to be inline.
213    if (type() != other.type() || hasQuirk() != other.hasQuirk())
214        return false;
215    if (isUndefined())
216        return true;
217    if (isCalculated())
218        return isCalculatedEqual(other);
219    return value() == other.value();
220}
221
222inline bool Length::operator!=(const Length& other) const
223{
224    return !(*this == other);
225}
226
227inline Length& Length::operator*=(float value)
228{
229    ASSERT(!isCalculated());
230    if (isCalculated())
231        return *this;
232
233    if (m_isFloat)
234        m_floatValue *= value;
235    else
236        m_intValue *= value;
237
238    return *this;
239}
240
241inline float Length::value() const
242{
243    ASSERT(!isUndefined());
244    ASSERT(!isCalculated());
245    return m_isFloat ? m_floatValue : m_intValue;
246}
247
248inline int Length::intValue() const
249{
250    ASSERT(!isUndefined());
251    ASSERT(!isCalculated());
252    // FIXME: Makes no sense to return 0 here but not in the value() function above.
253    if (isCalculated())
254        return 0;
255    return m_isFloat ? static_cast<int>(m_floatValue) : m_intValue;
256}
257
258inline float Length::percent() const
259{
260    ASSERT(isPercentNotCalculated());
261    return value();
262}
263
264inline LengthType Length::type() const
265{
266    return static_cast<LengthType>(m_type);
267}
268
269inline bool Length::hasQuirk() const
270{
271    return m_hasQuirk;
272}
273
274inline void Length::setHasQuirk(bool hasQuirk)
275{
276    m_hasQuirk = hasQuirk;
277}
278
279inline void Length::setValue(LengthType type, int value)
280{
281    ASSERT(m_type != Calculated);
282    ASSERT(type != Calculated);
283    m_type = type;
284    m_intValue = value;
285    m_isFloat = false;
286}
287
288inline void Length::setValue(LengthType type, float value)
289{
290    ASSERT(m_type != Calculated);
291    ASSERT(type != Calculated);
292    m_type = type;
293    m_floatValue = value;
294    m_isFloat = true;
295}
296
297inline void Length::setValue(LengthType type, LayoutUnit value)
298{
299    ASSERT(m_type != Calculated);
300    ASSERT(type != Calculated);
301    m_type = type;
302    m_floatValue = value;
303    m_isFloat = true;
304}
305
306inline bool Length::isAuto() const
307{
308    return type() == Auto;
309}
310
311inline bool Length::isFixed() const
312{
313    return type() == Fixed;
314}
315
316inline bool Length::isMaxContent() const
317{
318    return type() == MaxContent;
319}
320
321inline bool Length::isMinContent() const
322{
323    return type() == MinContent;
324}
325
326inline bool Length::isNegative() const
327{
328    if (isUndefined() || isCalculated())
329        return false;
330    return m_isFloat ? (m_floatValue < 0) : (m_intValue < 0);
331}
332
333inline bool Length::isPercentNotCalculated() const
334{
335    return type() == Percent;
336}
337
338inline bool Length::isRelative() const
339{
340    return type() == Relative;
341}
342
343inline bool Length::isUndefined() const
344{
345    return type() == Undefined;
346}
347
348inline bool Length::isPercent() const
349{
350    return isPercentNotCalculated() || isCalculated();
351}
352
353inline bool Length::isPositive() const
354{
355    if (isUndefined())
356        return false;
357    if (isCalculated())
358        return true;
359    return m_isFloat ? (m_floatValue > 0) : (m_intValue > 0);
360}
361
362inline bool Length::isZero() const
363{
364    ASSERT(!isUndefined());
365    if (isCalculated())
366        return false;
367    return m_isFloat ? !m_floatValue : !m_intValue;
368}
369
370inline bool Length::isCalculated() const
371{
372    return type() == Calculated;
373}
374
375inline bool Length::isLegacyIntrinsic() const
376{
377    return type() == Intrinsic || type() == MinIntrinsic;
378}
379
380inline bool Length::isIntrinsic() const
381{
382    return type() == MinContent || type() == MaxContent || type() == FillAvailable || type() == FitContent;
383}
384
385inline bool Length::isIntrinsicOrAuto() const
386{
387    return isAuto() || isIntrinsic() || isLegacyIntrinsic();
388}
389
390inline bool Length::isSpecified() const
391{
392    return isFixed() || isPercent();
393}
394
395inline bool Length::isSpecifiedOrIntrinsic() const
396{
397    return isSpecified() || isIntrinsic();
398}
399
400// FIXME: Does this need to be in the header? Is it valuable to inline? Does it get inlined?
401inline Length Length::blend(const Length& from, double progress) const
402{
403    if (from.type() == Calculated || type() == Calculated)
404        return blendMixedTypes(from, progress);
405
406    if (!from.isZero() && !isZero() && from.type() != type())
407        return blendMixedTypes(from, progress);
408
409    if (from.isZero() && isZero())
410        return *this;
411
412    LengthType resultType = type();
413    if (isZero())
414        resultType = from.type();
415
416    if (resultType == Percent) {
417        float fromPercent = from.isZero() ? 0 : from.percent();
418        float toPercent = isZero() ? 0 : percent();
419        return Length(WebCore::blend(fromPercent, toPercent, progress), Percent);
420    }
421
422    float fromValue = from.isZero() ? 0 : from.value();
423    float toValue = isZero() ? 0 : value();
424    return Length(WebCore::blend(fromValue, toValue, progress), resultType);
425}
426
427} // namespace WebCore
428
429#endif // Length_h
430