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, 2014 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/HashMap.h>
31#include <wtf/NeverDestroyed.h>
32#include <wtf/StdLibExtras.h>
33#include <wtf/text/StringBuffer.h>
34#include <wtf/text/StringView.h>
35
36using namespace WTF;
37
38namespace WebCore {
39
40static Length parseLength(const UChar* data, unsigned length)
41{
42    if (length == 0)
43        return Length(1, Relative);
44
45    unsigned i = 0;
46    while (i < length && isSpaceOrNewline(data[i]))
47        ++i;
48    if (i < length && (data[i] == '+' || data[i] == '-'))
49        ++i;
50    while (i < length && isASCIIDigit(data[i]))
51        ++i;
52    unsigned intLength = i;
53    while (i < length && (isASCIIDigit(data[i]) || data[i] == '.'))
54        ++i;
55    unsigned doubleLength = i;
56
57    // IE quirk: Skip whitespace between the number and the % character (20 % => 20%).
58    while (i < length && isSpaceOrNewline(data[i]))
59        ++i;
60
61    bool ok;
62    UChar next = (i < length) ? data[i] : ' ';
63    if (next == '%') {
64        // IE quirk: accept decimal fractions for percentages.
65        double r = charactersToDouble(data, doubleLength, &ok);
66        if (ok)
67            return Length(r, Percent);
68        return Length(1, Relative);
69    }
70    int r = charactersToIntStrict(data, intLength, &ok);
71    if (next == '*') {
72        if (ok)
73            return Length(r, Relative);
74        return Length(1, Relative);
75    }
76    if (ok)
77        return Length(r, Fixed);
78    return Length(0, Relative);
79}
80
81static unsigned countCharacter(StringImpl& string, UChar character)
82{
83    unsigned count = 0;
84    unsigned length = string.length();
85    for (unsigned i = 0; i < length; ++i)
86        count += string[i] == character;
87    return count;
88}
89
90std::unique_ptr<Length[]> newCoordsArray(const String& string, int& len)
91{
92    unsigned length = string.length();
93    StringBuffer<UChar> spacified(length);
94    for (unsigned i = 0; i < length; i++) {
95        UChar cc = string[i];
96        if (cc > '9' || (cc < '0' && cc != '-' && cc != '*' && cc != '.'))
97            spacified[i] = ' ';
98        else
99            spacified[i] = cc;
100    }
101    RefPtr<StringImpl> str = StringImpl::adopt(spacified);
102
103    str = str->simplifyWhiteSpace();
104
105    len = countCharacter(*str, ' ') + 1;
106    auto r = std::make_unique<Length[]>(len);
107
108    int i = 0;
109    unsigned pos = 0;
110    size_t pos2;
111
112    auto upconvertedCharacters = StringView(str.get()).upconvertedCharacters();
113    while ((pos2 = str->find(' ', pos)) != notFound) {
114        r[i++] = parseLength(upconvertedCharacters + pos, pos2 - pos);
115        pos = pos2+1;
116    }
117    r[i] = parseLength(upconvertedCharacters + pos, str->length() - pos);
118
119    ASSERT(i == len - 1);
120
121    return r;
122}
123
124std::unique_ptr<Length[]> newLengthArray(const String& string, int& len)
125{
126    RefPtr<StringImpl> str = string.impl()->simplifyWhiteSpace();
127    if (!str->length()) {
128        len = 1;
129        return nullptr;
130    }
131
132    len = countCharacter(*str, ',') + 1;
133    auto r = std::make_unique<Length[]>(len);
134
135    int i = 0;
136    unsigned pos = 0;
137    size_t pos2;
138
139    auto upconvertedCharacters = StringView(str.get()).upconvertedCharacters();
140    while ((pos2 = str->find(',', pos)) != notFound) {
141        r[i++] = parseLength(upconvertedCharacters + pos, pos2 - pos);
142        pos = pos2+1;
143    }
144
145    ASSERT(i == len - 1);
146
147    // IE Quirk: If the last comma is the last char skip it and reduce len by one.
148    if (str->length()-pos > 0)
149        r[i] = parseLength(upconvertedCharacters + pos, str->length() - pos);
150    else
151        len--;
152
153    return r;
154}
155
156class CalculationValueMap {
157public:
158    CalculationValueMap();
159
160    unsigned insert(PassRef<CalculationValue>);
161    void ref(unsigned handle);
162    void deref(unsigned handle);
163
164    CalculationValue& get(unsigned handle) const;
165
166private:
167    struct Entry {
168        uint64_t referenceCountMinusOne;
169        CalculationValue* value;
170        Entry();
171        Entry(CalculationValue&);
172    };
173
174    unsigned m_nextAvailableHandle;
175    HashMap<unsigned, Entry> m_map;
176};
177
178inline CalculationValueMap::Entry::Entry()
179    : referenceCountMinusOne(0)
180    , value(nullptr)
181{
182}
183
184inline CalculationValueMap::Entry::Entry(CalculationValue& value)
185    : referenceCountMinusOne(0)
186    , value(&value)
187{
188}
189
190inline CalculationValueMap::CalculationValueMap()
191    : m_nextAvailableHandle(1)
192{
193}
194
195inline unsigned CalculationValueMap::insert(PassRef<CalculationValue> value)
196{
197    ASSERT(m_nextAvailableHandle);
198
199    // The leakRef below is balanced by the adoptRef in the deref member function.
200    Entry leakedValue = value.leakRef();
201
202    // FIXME: This monotonically increasing handle generation scheme is potentially wasteful
203    // of the handle space. Consider reusing empty handles. https://bugs.webkit.org/show_bug.cgi?id=80489
204    while (!m_map.isValidKey(m_nextAvailableHandle) || !m_map.add(m_nextAvailableHandle, leakedValue).isNewEntry)
205        ++m_nextAvailableHandle;
206
207    return m_nextAvailableHandle++;
208}
209
210inline CalculationValue& CalculationValueMap::get(unsigned handle) const
211{
212    ASSERT(m_map.contains(handle));
213
214    return *m_map.find(handle)->value.value;
215}
216
217inline void CalculationValueMap::ref(unsigned handle)
218{
219    ASSERT(m_map.contains(handle));
220
221    ++m_map.find(handle)->value.referenceCountMinusOne;
222}
223
224inline void CalculationValueMap::deref(unsigned handle)
225{
226    ASSERT(m_map.contains(handle));
227
228    auto it = m_map.find(handle);
229    if (it->value.referenceCountMinusOne) {
230        --it->value.referenceCountMinusOne;
231        return;
232    }
233
234    // The adoptRef here is balanced by the leakRef in the insert member function.
235    Ref<CalculationValue> value { adoptRef(*it->value.value) };
236
237    m_map.remove(it);
238}
239
240static CalculationValueMap& calculationValues()
241{
242    static NeverDestroyed<CalculationValueMap> map;
243    return map;
244}
245
246Length::Length(PassRef<CalculationValue> value)
247    : m_hasQuirk(false)
248    , m_type(Calculated)
249    , m_isFloat(false)
250{
251    m_calculationValueHandle = calculationValues().insert(WTF::move(value));
252}
253
254Length Length::blendMixedTypes(const Length& from, double progress) const
255{
256    if (progress <= 0.0)
257        return from;
258
259    if (progress >= 1.0)
260        return *this;
261
262    auto blend = std::make_unique<CalcExpressionBlendLength>(from, *this, progress);
263    return Length(CalculationValue::create(WTF::move(blend), CalculationRangeAll));
264}
265
266CalculationValue& Length::calculationValue() const
267{
268    ASSERT(isCalculated());
269    return calculationValues().get(m_calculationValueHandle);
270}
271
272void Length::ref() const
273{
274    ASSERT(isCalculated());
275    calculationValues().ref(m_calculationValueHandle);
276}
277
278void Length::deref() const
279{
280    ASSERT(isCalculated());
281    calculationValues().deref(m_calculationValueHandle);
282}
283
284float Length::nonNanCalculatedValue(int maxValue) const
285{
286    ASSERT(isCalculated());
287    float result = calculationValue().evaluate(maxValue);
288    if (std::isnan(result))
289        return 0;
290    return result;
291}
292
293bool Length::isCalculatedEqual(const Length& other) const
294{
295    return calculationValue() == other.calculationValue();
296}
297
298struct SameSizeAsLength {
299    int32_t value;
300    int32_t metaData;
301};
302COMPILE_ASSERT(sizeof(Length) == sizeof(SameSizeAsLength), length_should_stay_small);
303
304} // namespace WebCore
305