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)
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.
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.
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.
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>
41 static Length parseLength(const UChar* data, unsigned length)
44 return Length(1, Relative);
47 while (i < length && isSpaceOrNewline(data[i]))
49 if (i < length && (data[i] == '+' || data[i] == '-'))
51 while (i < length && isASCIIDigit(data[i]))
53 unsigned intLength = i;
54 while (i < length && (isASCIIDigit(data[i]) || data[i] == '.'))
56 unsigned doubleLength = i;
58 // IE quirk: Skip whitespace between the number and the % character (20 % => 20%).
59 while (i < length && isSpaceOrNewline(data[i]))
63 UChar next = (i < length) ? data[i] : ' ';
65 // IE quirk: accept decimal fractions for percentages.
66 double r = charactersToDouble(data, doubleLength, &ok);
68 return Length(r, Percent);
69 return Length(1, Relative);
71 int r = charactersToIntStrict(data, intLength, &ok);
74 return Length(r, Relative);
75 return Length(1, Relative);
78 return Length(r, Fixed);
79 return Length(0, Relative);
82 static unsigned countCharacter(StringImpl& string, UChar character)
85 unsigned length = string.length();
86 for (unsigned i = 0; i < length; ++i)
87 count += string[i] == character;
91 std::unique_ptr<Length[]> newCoordsArray(const String& string, int& len)
93 unsigned length = string.length();
94 StringBuffer<UChar> spacified(length);
95 for (unsigned i = 0; i < length; i++) {
97 if (cc > '9' || (cc < '0' && cc != '-' && cc != '*' && cc != '.'))
102 RefPtr<StringImpl> str = StringImpl::adopt(spacified);
104 str = str->simplifyWhiteSpace();
106 len = countCharacter(*str, ' ') + 1;
107 auto r = std::make_unique<Length[]>(len);
113 auto upconvertedCharacters = StringView(str.get()).upconvertedCharacters();
114 while ((pos2 = str->find(' ', pos)) != notFound) {
115 r[i++] = parseLength(upconvertedCharacters + pos, pos2 - pos);
118 r[i] = parseLength(upconvertedCharacters + pos, str->length() - pos);
120 ASSERT(i == len - 1);
125 std::unique_ptr<Length[]> newLengthArray(const String& string, int& len)
127 RefPtr<StringImpl> str = string.impl()->simplifyWhiteSpace();
128 if (!str->length()) {
133 len = countCharacter(*str, ',') + 1;
134 auto r = std::make_unique<Length[]>(len);
140 auto upconvertedCharacters = StringView(str.get()).upconvertedCharacters();
141 while ((pos2 = str->find(',', pos)) != notFound) {
142 r[i++] = parseLength(upconvertedCharacters + pos, pos2 - pos);
146 ASSERT(i == len - 1);
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);
157 class CalculationValueMap {
159 CalculationValueMap();
161 unsigned insert(Ref<CalculationValue>&&);
162 void ref(unsigned handle);
163 void deref(unsigned handle);
165 CalculationValue& get(unsigned handle) const;
169 uint64_t referenceCountMinusOne;
170 CalculationValue* value;
172 Entry(CalculationValue&);
175 unsigned m_nextAvailableHandle;
176 HashMap<unsigned, Entry> m_map;
179 inline CalculationValueMap::Entry::Entry()
180 : referenceCountMinusOne(0)
185 inline CalculationValueMap::Entry::Entry(CalculationValue& value)
186 : referenceCountMinusOne(0)
191 inline CalculationValueMap::CalculationValueMap()
192 : m_nextAvailableHandle(1)
196 inline unsigned CalculationValueMap::insert(Ref<CalculationValue>&& value)
198 ASSERT(m_nextAvailableHandle);
200 // The leakRef below is balanced by the adoptRef in the deref member function.
201 Entry leakedValue = value.leakRef();
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;
208 return m_nextAvailableHandle++;
211 inline CalculationValue& CalculationValueMap::get(unsigned handle) const
213 ASSERT(m_map.contains(handle));
215 return *m_map.find(handle)->value.value;
218 inline void CalculationValueMap::ref(unsigned handle)
220 ASSERT(m_map.contains(handle));
222 ++m_map.find(handle)->value.referenceCountMinusOne;
225 inline void CalculationValueMap::deref(unsigned handle)
227 ASSERT(m_map.contains(handle));
229 auto it = m_map.find(handle);
230 if (it->value.referenceCountMinusOne) {
231 --it->value.referenceCountMinusOne;
235 // The adoptRef here is balanced by the leakRef in the insert member function.
236 Ref<CalculationValue> value { adoptRef(*it->value.value) };
241 static CalculationValueMap& calculationValues()
243 static NeverDestroyed<CalculationValueMap> map;
247 Length::Length(Ref<CalculationValue>&& value)
252 m_calculationValueHandle = calculationValues().insert(WTFMove(value));
255 CalculationValue& Length::calculationValue() const
257 ASSERT(isCalculated());
258 return calculationValues().get(m_calculationValueHandle);
261 void Length::ref() const
263 ASSERT(isCalculated());
264 calculationValues().ref(m_calculationValueHandle);
267 void Length::deref() const
269 ASSERT(isCalculated());
270 calculationValues().deref(m_calculationValueHandle);
273 float Length::nonNanCalculatedValue(int maxValue) const
275 ASSERT(isCalculated());
276 float result = calculationValue().evaluate(maxValue);
277 if (std::isnan(result))
282 bool Length::isCalculatedEqual(const Length& other) const
284 return calculationValue() == other.calculationValue();
287 static Length blendMixedTypes(const Length& from, const Length& to, double progress)
295 auto blend = std::make_unique<CalcExpressionBlendLength>(from, to, progress);
296 return Length(CalculationValue::create(WTFMove(blend), CalculationRangeAll));
299 Length blend(const Length& from, const Length& to, double progress)
301 if (from.isAuto() || to.isAuto())
304 if (from.type() == Calculated || to.type() == Calculated)
305 return blendMixedTypes(from, to, progress);
307 if (!from.isZero() && !to.isZero() && from.type() != to.type())
308 return blendMixedTypes(from, to, progress);
310 LengthType resultType = to.type();
312 resultType = from.type();
314 if (resultType == Percent) {
315 float fromPercent = from.isZero() ? 0 : from.percent();
316 float toPercent = to.isZero() ? 0 : to.percent();
317 return Length(WebCore::blend(fromPercent, toPercent, progress), Percent);
320 float fromValue = from.isZero() ? 0 : from.value();
321 float toValue = to.isZero() ? 0 : to.value();
322 return Length(WebCore::blend(fromValue, toValue, progress), resultType);
325 struct SameSizeAsLength {
329 COMPILE_ASSERT(sizeof(Length) == sizeof(SameSizeAsLength), length_should_stay_small);
331 static TextStream& operator<<(TextStream& ts, LengthType type)
334 case Auto: ts << "auto"; break;
335 case Relative: ts << "relative"; break;
336 case Percent: ts << "percent"; break;
337 case Fixed: ts << "fixed"; break;
338 case Intrinsic: ts << "intrinsic"; break;
339 case MinIntrinsic: ts << "min-intrinsic"; break;
340 case MinContent: ts << "min-content"; break;
341 case MaxContent: ts << "max-content"; break;
342 case FillAvailable: ts << "fill-available"; break;
343 case FitContent: ts << "fit-content"; break;
344 case Calculated: ts << "calc"; break;
345 case Undefined: ts << "undefined"; break;
350 TextStream& operator<<(TextStream& ts, Length length)
352 switch (length.type()) {
358 ts << TextStream::FormatNumberRespectingIntegers(length.value()) << "px";
367 ts << length.type() << " " << TextStream::FormatNumberRespectingIntegers(length.value());
370 ts << TextStream::FormatNumberRespectingIntegers(length.percent()) << "%";
373 // FIXME: dump CalculationValue.
378 if (length.hasQuirk())
384 } // namespace WebCore