Make FrameView and Frame TextStream-loggable
[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-2019 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/MallocPtr.h>
32 #include <wtf/NeverDestroyed.h>
33 #include <wtf/StdLibExtras.h>
34 #include <wtf/text/StringView.h>
35 #include <wtf/text/TextStream.h>
36
37 namespace WebCore {
38
39 static 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
80 static unsigned countCharacter(StringImpl& string, UChar character)
81 {
82     unsigned count = 0;
83     unsigned length = string.length();
84     for (unsigned i = 0; i < length; ++i)
85         count += string[i] == character;
86     return count;
87 }
88
89 UniqueArray<Length> newCoordsArray(const String& string, int& len)
90 {
91     unsigned length = string.length();
92     LChar* spacifiedCharacters;
93     auto str = StringImpl::createUninitialized(length, spacifiedCharacters);
94     for (unsigned i = 0; i < length; i++) {
95         UChar cc = string[i];
96         if (cc > '9' || (cc < '0' && cc != '-' && cc != '*' && cc != '.'))
97             spacifiedCharacters[i] = ' ';
98         else
99             spacifiedCharacters[i] = cc;
100     }
101     str = str->simplifyWhiteSpace();
102
103     len = countCharacter(str, ' ') + 1;
104     auto r = makeUniqueArray<Length>(len);
105
106     int i = 0;
107     unsigned pos = 0;
108     size_t pos2;
109
110     while ((pos2 = str->find(' ', pos)) != notFound) {
111         r[i++] = parseLength(str->characters16() + pos, pos2 - pos);
112         pos = pos2+1;
113     }
114     r[i] = parseLength(str->characters16() + pos, str->length() - pos);
115
116     ASSERT(i == len - 1);
117
118     return r;
119 }
120
121 UniqueArray<Length> newLengthArray(const String& string, int& len)
122 {
123     RefPtr<StringImpl> str = string.impl()->simplifyWhiteSpace();
124     if (!str->length()) {
125         len = 1;
126         return nullptr;
127     }
128
129     len = countCharacter(*str, ',') + 1;
130     auto r = makeUniqueArray<Length>(len);
131
132     int i = 0;
133     unsigned pos = 0;
134     size_t pos2;
135
136     auto upconvertedCharacters = StringView(str.get()).upconvertedCharacters();
137     while ((pos2 = str->find(',', pos)) != notFound) {
138         r[i++] = parseLength(upconvertedCharacters + 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(upconvertedCharacters + pos, str->length() - pos);
147     else
148         len--;
149
150     return r;
151 }
152
153 class CalculationValueMap {
154 public:
155     CalculationValueMap();
156
157     unsigned insert(Ref<CalculationValue>&&);
158     void ref(unsigned handle);
159     void deref(unsigned handle);
160
161     CalculationValue& get(unsigned handle) const;
162
163 private:
164     struct Entry {
165         uint64_t referenceCountMinusOne;
166         CalculationValue* value;
167         Entry();
168         Entry(CalculationValue&);
169     };
170
171     unsigned m_nextAvailableHandle;
172     HashMap<unsigned, Entry> m_map;
173 };
174
175 inline CalculationValueMap::Entry::Entry()
176     : referenceCountMinusOne(0)
177     , value(nullptr)
178 {
179 }
180
181 inline CalculationValueMap::Entry::Entry(CalculationValue& value)
182     : referenceCountMinusOne(0)
183     , value(&value)
184 {
185 }
186
187 inline CalculationValueMap::CalculationValueMap()
188     : m_nextAvailableHandle(1)
189 {
190 }
191     
192 inline unsigned CalculationValueMap::insert(Ref<CalculationValue>&& value)
193 {
194     ASSERT(m_nextAvailableHandle);
195
196     // The leakRef below is balanced by the adoptRef in the deref member function.
197     Entry leakedValue = value.leakRef();
198
199     // FIXME: This monotonically increasing handle generation scheme is potentially wasteful
200     // of the handle space. Consider reusing empty handles. https://bugs.webkit.org/show_bug.cgi?id=80489
201     while (!m_map.isValidKey(m_nextAvailableHandle) || !m_map.add(m_nextAvailableHandle, leakedValue).isNewEntry)
202         ++m_nextAvailableHandle;
203
204     return m_nextAvailableHandle++;
205 }
206
207 inline CalculationValue& CalculationValueMap::get(unsigned handle) const
208 {
209     ASSERT(m_map.contains(handle));
210
211     return *m_map.find(handle)->value.value;
212 }
213
214 inline void CalculationValueMap::ref(unsigned handle)
215 {
216     ASSERT(m_map.contains(handle));
217
218     ++m_map.find(handle)->value.referenceCountMinusOne;
219 }
220
221 inline void CalculationValueMap::deref(unsigned handle)
222 {
223     ASSERT(m_map.contains(handle));
224
225     auto it = m_map.find(handle);
226     if (it->value.referenceCountMinusOne) {
227         --it->value.referenceCountMinusOne;
228         return;
229     }
230
231     // The adoptRef here is balanced by the leakRef in the insert member function.
232     Ref<CalculationValue> value { adoptRef(*it->value.value) };
233
234     m_map.remove(it);
235 }
236
237 static CalculationValueMap& calculationValues()
238 {
239     static NeverDestroyed<CalculationValueMap> map;
240     return map;
241 }
242
243 Length::Length(Ref<CalculationValue>&& value)
244     : m_hasQuirk(false)
245     , m_type(Calculated)
246     , m_isFloat(false)
247 {
248     m_calculationValueHandle = calculationValues().insert(WTFMove(value));
249 }
250
251 CalculationValue& Length::calculationValue() const
252 {
253     ASSERT(isCalculated());
254     return calculationValues().get(m_calculationValueHandle);
255 }
256     
257 void Length::ref() const
258 {
259     ASSERT(isCalculated());
260     calculationValues().ref(m_calculationValueHandle);
261 }
262
263 void Length::deref() const
264 {
265     ASSERT(isCalculated());
266     calculationValues().deref(m_calculationValueHandle);
267 }
268
269 float Length::nonNanCalculatedValue(int maxValue) const
270 {
271     ASSERT(isCalculated());
272     float result = calculationValue().evaluate(maxValue);
273     if (std::isnan(result))
274         return 0;
275     return result;
276 }
277
278 bool Length::isCalculatedEqual(const Length& other) const
279 {
280     return calculationValue() == other.calculationValue();
281 }
282
283 Length convertTo100PercentMinusLength(const Length& length)
284 {
285     if (length.isPercent())
286         return Length(100 - length.value(), Percent);
287     
288     // Turn this into a calc expression: calc(100% - length)
289     Vector<std::unique_ptr<CalcExpressionNode>> lengths;
290     lengths.reserveInitialCapacity(2);
291     lengths.uncheckedAppend(makeUnique<CalcExpressionLength>(Length(100, Percent)));
292     lengths.uncheckedAppend(makeUnique<CalcExpressionLength>(length));
293     auto op = makeUnique<CalcExpressionOperation>(WTFMove(lengths), CalcOperator::Subtract);
294     return Length(CalculationValue::create(WTFMove(op), ValueRangeAll));
295 }
296
297 static Length blendMixedTypes(const Length& from, const Length& to, double progress)
298 {
299     if (progress <= 0.0)
300         return from;
301         
302     if (progress >= 1.0)
303         return to;
304         
305     auto blend = makeUnique<CalcExpressionBlendLength>(from, to, progress);
306     return Length(CalculationValue::create(WTFMove(blend), ValueRangeAll));
307 }
308
309 Length blend(const Length& from, const Length& to, double progress)
310 {
311     if (from.isAuto() || to.isAuto())
312         return progress < 0.5 ? from : to;
313
314     if (from.isUndefined() || to.isUndefined())
315         return to;
316
317     if (from.type() == Calculated || to.type() == Calculated)
318         return blendMixedTypes(from, to, progress);
319
320     if (!from.isZero() && !to.isZero() && from.type() != to.type())
321         return blendMixedTypes(from, to, progress);
322
323     LengthType resultType = to.type();
324     if (to.isZero())
325         resultType = from.type();
326
327     if (resultType == Percent) {
328         float fromPercent = from.isZero() ? 0 : from.percent();
329         float toPercent = to.isZero() ? 0 : to.percent();
330         return Length(WebCore::blend(fromPercent, toPercent, progress), Percent);
331     }
332
333     float fromValue = from.isZero() ? 0 : from.value();
334     float toValue = to.isZero() ? 0 : to.value();
335     return Length(WebCore::blend(fromValue, toValue, progress), resultType);
336 }
337
338 struct SameSizeAsLength {
339     int32_t value;
340     int32_t metaData;
341 };
342 COMPILE_ASSERT(sizeof(Length) == sizeof(SameSizeAsLength), length_should_stay_small);
343
344 static TextStream& operator<<(TextStream& ts, LengthType type)
345 {
346     switch (type) {
347     case Auto: ts << "auto"; break;
348     case Relative: ts << "relative"; break;
349     case Percent: ts << "percent"; break;
350     case Fixed: ts << "fixed"; break;
351     case Intrinsic: ts << "intrinsic"; break;
352     case MinIntrinsic: ts << "min-intrinsic"; break;
353     case MinContent: ts << "min-content"; break;
354     case MaxContent: ts << "max-content"; break;
355     case FillAvailable: ts << "fill-available"; break;
356     case FitContent: ts << "fit-content"; break;
357     case Calculated: ts << "calc"; break;
358     case Undefined: ts << "undefined"; break;
359     }
360     return ts;
361 }
362
363 TextStream& operator<<(TextStream& ts, Length length)
364 {
365     switch (length.type()) {
366     case Auto:
367     case Undefined:
368         ts << length.type();
369         break;
370     case Fixed:
371         ts << TextStream::FormatNumberRespectingIntegers(length.value()) << "px";
372         break;
373     case Relative:
374     case Intrinsic:
375     case MinIntrinsic:
376     case MinContent:
377     case MaxContent:
378     case FillAvailable:
379     case FitContent:
380         ts << length.type() << " " << TextStream::FormatNumberRespectingIntegers(length.value());
381         break;
382     case Percent:
383         ts << TextStream::FormatNumberRespectingIntegers(length.percent()) << "%";
384         break;
385     case Calculated:
386         ts << length.calculationValue();
387         break;
388     }
389     
390     if (length.hasQuirk())
391         ts << " has-quirk";
392
393     return ts;
394 }
395
396 } // namespace WebCore