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