1/*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4 *           (C) 2001 Dirk Mueller ( mueller@kde.org )
5 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
6 * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net)
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB.  If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 *
23 */
24
25#include "config.h"
26#include "Length.h"
27
28#include "CalculationValue.h"
29#include <wtf/ASCIICType.h>
30#include <wtf/Assertions.h>
31#include <wtf/OwnArrayPtr.h>
32#include <wtf/text/StringBuffer.h>
33#include <wtf/text/WTFString.h>
34
35using namespace WTF;
36
37namespace WebCore {
38
39static Length parseLength(const UChar* data, unsigned length)
40{
41    if (length == 0)
42        return Length(1, Relative);
43
44    unsigned i = 0;
45    while (i < length && isSpaceOrNewline(data[i]))
46        ++i;
47    if (i < length && (data[i] == '+' || data[i] == '-'))
48        ++i;
49    while (i < length && isASCIIDigit(data[i]))
50        ++i;
51    unsigned intLength = i;
52    while (i < length && (isASCIIDigit(data[i]) || data[i] == '.'))
53        ++i;
54    unsigned doubleLength = i;
55
56    // IE quirk: Skip whitespace between the number and the % character (20 % => 20%).
57    while (i < length && isSpaceOrNewline(data[i]))
58        ++i;
59
60    bool ok;
61    UChar next = (i < length) ? data[i] : ' ';
62    if (next == '%') {
63        // IE quirk: accept decimal fractions for percentages.
64        double r = charactersToDouble(data, doubleLength, &ok);
65        if (ok)
66            return Length(r, Percent);
67        return Length(1, Relative);
68    }
69    int r = charactersToIntStrict(data, intLength, &ok);
70    if (next == '*') {
71        if (ok)
72            return Length(r, Relative);
73        return Length(1, Relative);
74    }
75    if (ok)
76        return Length(r, Fixed);
77    return Length(0, Relative);
78}
79
80static int countCharacter(const UChar* data, unsigned length, UChar character)
81{
82    int count = 0;
83    for (int i = 0; i < static_cast<int>(length); ++i)
84        count += data[i] == character;
85    return count;
86}
87
88PassOwnArrayPtr<Length> newCoordsArray(const String& string, int& len)
89{
90    unsigned length = string.length();
91    const UChar* data = string.characters();
92    StringBuffer<UChar> spacified(length);
93    for (unsigned i = 0; i < length; i++) {
94        UChar cc = data[i];
95        if (cc > '9' || (cc < '0' && cc != '-' && cc != '*' && cc != '.'))
96            spacified[i] = ' ';
97        else
98            spacified[i] = cc;
99    }
100    RefPtr<StringImpl> str = StringImpl::adopt(spacified);
101
102    str = str->simplifyWhiteSpace();
103
104    len = countCharacter(str->characters(), str->length(), ' ') + 1;
105    OwnArrayPtr<Length> r = adoptArrayPtr(new Length[len]);
106
107    int i = 0;
108    unsigned pos = 0;
109    size_t pos2;
110
111    while ((pos2 = str->find(' ', pos)) != notFound) {
112        r[i++] = parseLength(str->characters() + pos, pos2 - pos);
113        pos = pos2+1;
114    }
115    r[i] = parseLength(str->characters() + pos, str->length() - pos);
116
117    ASSERT(i == len - 1);
118
119    return r.release();
120}
121
122PassOwnArrayPtr<Length> newLengthArray(const String& string, int& len)
123{
124    RefPtr<StringImpl> str = string.impl()->simplifyWhiteSpace();
125    if (!str->length()) {
126        len = 1;
127        return nullptr;
128    }
129
130    len = countCharacter(str->characters(), str->length(), ',') + 1;
131    OwnArrayPtr<Length> r = adoptArrayPtr(new Length[len]);
132
133    int i = 0;
134    unsigned pos = 0;
135    size_t pos2;
136
137    while ((pos2 = str->find(',', pos)) != notFound) {
138        r[i++] = parseLength(str->characters() + pos, pos2 - pos);
139        pos = pos2+1;
140    }
141
142    ASSERT(i == len - 1);
143
144    // IE Quirk: If the last comma is the last char skip it and reduce len by one.
145    if (str->length()-pos > 0)
146        r[i] = parseLength(str->characters() + pos, str->length() - pos);
147    else
148        len--;
149
150    return r.release();
151}
152
153class CalculationValueHandleMap {
154    WTF_MAKE_FAST_ALLOCATED;
155public:
156    CalculationValueHandleMap()
157        : m_index(1)
158    {
159    }
160
161    int insert(PassRefPtr<CalculationValue> calcValue)
162    {
163        ASSERT(m_index);
164        // FIXME calc(): https://bugs.webkit.org/show_bug.cgi?id=80489
165        // This monotonically increasing handle generation scheme is potentially wasteful
166        // of the handle space. Consider reusing empty handles.
167        while (m_map.contains(m_index))
168            m_index++;
169
170        m_map.set(m_index, calcValue);
171
172        return m_index;
173    }
174
175    void remove(int index)
176    {
177        ASSERT(m_map.contains(index));
178        m_map.remove(index);
179    }
180
181    PassRefPtr<CalculationValue> get(int index)
182    {
183        ASSERT(m_map.contains(index));
184        return m_map.get(index);
185    }
186
187private:
188    int m_index;
189    HashMap<int, RefPtr<CalculationValue> > m_map;
190};
191
192static CalculationValueHandleMap& calcHandles()
193{
194    DEFINE_STATIC_LOCAL(CalculationValueHandleMap, handleMap, ());
195    return handleMap;
196}
197
198Length::Length(PassRefPtr<CalculationValue> calc)
199    : m_quirk(false)
200    , m_type(Calculated)
201    , m_isFloat(false)
202{
203    m_intValue = calcHandles().insert(calc);
204}
205
206Length Length::blendMixedTypes(const Length& from, double progress) const
207{
208    if (progress <= 0.0)
209        return from;
210
211    if (progress >= 1.0)
212        return *this;
213
214    OwnPtr<CalcExpressionNode> blend = adoptPtr(new CalcExpressionBlendLength(from, *this, progress));
215    return Length(CalculationValue::create(blend.release(), CalculationRangeAll));
216}
217
218PassRefPtr<CalculationValue> Length::calculationValue() const
219{
220    ASSERT(isCalculated());
221    return calcHandles().get(calculationHandle());
222}
223
224void Length::incrementCalculatedRef() const
225{
226    ASSERT(isCalculated());
227    calculationValue()->ref();
228}
229
230void Length::decrementCalculatedRef() const
231{
232    ASSERT(isCalculated());
233    RefPtr<CalculationValue> calcLength = calculationValue();
234    if (calcLength->hasOneRef())
235        calcHandles().remove(calculationHandle());
236    calcLength->deref();
237}
238
239float Length::nonNanCalculatedValue(int maxValue) const
240{
241    ASSERT(isCalculated());
242    float result = calculationValue()->evaluate(maxValue);
243    if (std::isnan(result))
244        return 0;
245    return result;
246}
247
248bool Length::isCalculatedEqual(const Length& o) const
249{
250    return isCalculated() && (calculationValue() == o.calculationValue() || *calculationValue() == *o.calculationValue());
251}
252
253struct SameSizeAsLength {
254    int32_t value;
255    int32_t metaData;
256};
257COMPILE_ASSERT(sizeof(Length) == sizeof(SameSizeAsLength), length_should_stay_small);
258
259} // namespace WebCore
260