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 <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/StringBuffer.h>
35 #include <wtf/text/StringView.h>
36 #include <wtf/text/TextStream.h>
42 static Length parseLength(const UChar* data, unsigned length)
45 return Length(1, Relative);
48 while (i < length && isSpaceOrNewline(data[i]))
50 if (i < length && (data[i] == '+' || data[i] == '-'))
52 while (i < length && isASCIIDigit(data[i]))
54 unsigned intLength = i;
55 while (i < length && (isASCIIDigit(data[i]) || data[i] == '.'))
57 unsigned doubleLength = i;
59 // IE quirk: Skip whitespace between the number and the % character (20 % => 20%).
60 while (i < length && isSpaceOrNewline(data[i]))
64 UChar next = (i < length) ? data[i] : ' ';
66 // IE quirk: accept decimal fractions for percentages.
67 double r = charactersToDouble(data, doubleLength, &ok);
69 return Length(r, Percent);
70 return Length(1, Relative);
72 int r = charactersToIntStrict(data, intLength, &ok);
75 return Length(r, Relative);
76 return Length(1, Relative);
79 return Length(r, Fixed);
80 return Length(0, Relative);
83 static unsigned countCharacter(StringImpl& string, UChar character)
86 unsigned length = string.length();
87 for (unsigned i = 0; i < length; ++i)
88 count += string[i] == character;
92 UniqueArray<Length> newCoordsArray(const String& string, int& len)
94 unsigned length = string.length();
95 StringBuffer<UChar> spacified(length);
96 for (unsigned i = 0; i < length; i++) {
98 if (cc > '9' || (cc < '0' && cc != '-' && cc != '*' && cc != '.'))
103 RefPtr<StringImpl> str = StringImpl::adopt(WTFMove(spacified));
105 str = str->simplifyWhiteSpace();
107 len = countCharacter(*str, ' ') + 1;
108 auto r = makeUniqueArray<Length>(len);
114 auto upconvertedCharacters = StringView(str.get()).upconvertedCharacters();
115 while ((pos2 = str->find(' ', pos)) != notFound) {
116 r[i++] = parseLength(upconvertedCharacters + pos, pos2 - pos);
119 r[i] = parseLength(upconvertedCharacters + pos, str->length() - pos);
121 ASSERT(i == len - 1);
126 UniqueArray<Length> newLengthArray(const String& string, int& len)
128 RefPtr<StringImpl> str = string.impl()->simplifyWhiteSpace();
129 if (!str->length()) {
134 len = countCharacter(*str, ',') + 1;
135 auto r = makeUniqueArray<Length>(len);
141 auto upconvertedCharacters = StringView(str.get()).upconvertedCharacters();
142 while ((pos2 = str->find(',', pos)) != notFound) {
143 r[i++] = parseLength(upconvertedCharacters + pos, pos2 - pos);
147 ASSERT(i == len - 1);
149 // IE Quirk: If the last comma is the last char skip it and reduce len by one.
150 if (str->length()-pos > 0)
151 r[i] = parseLength(upconvertedCharacters + pos, str->length() - pos);
158 class CalculationValueMap {
160 CalculationValueMap();
162 unsigned insert(Ref<CalculationValue>&&);
163 void ref(unsigned handle);
164 void deref(unsigned handle);
166 CalculationValue& get(unsigned handle) const;
170 uint64_t referenceCountMinusOne;
171 CalculationValue* value;
173 Entry(CalculationValue&);
176 unsigned m_nextAvailableHandle;
177 HashMap<unsigned, Entry> m_map;
180 inline CalculationValueMap::Entry::Entry()
181 : referenceCountMinusOne(0)
186 inline CalculationValueMap::Entry::Entry(CalculationValue& value)
187 : referenceCountMinusOne(0)
192 inline CalculationValueMap::CalculationValueMap()
193 : m_nextAvailableHandle(1)
197 inline unsigned CalculationValueMap::insert(Ref<CalculationValue>&& value)
199 ASSERT(m_nextAvailableHandle);
201 // The leakRef below is balanced by the adoptRef in the deref member function.
202 Entry leakedValue = value.leakRef();
204 // FIXME: This monotonically increasing handle generation scheme is potentially wasteful
205 // of the handle space. Consider reusing empty handles. https://bugs.webkit.org/show_bug.cgi?id=80489
206 while (!m_map.isValidKey(m_nextAvailableHandle) || !m_map.add(m_nextAvailableHandle, leakedValue).isNewEntry)
207 ++m_nextAvailableHandle;
209 return m_nextAvailableHandle++;
212 inline CalculationValue& CalculationValueMap::get(unsigned handle) const
214 ASSERT(m_map.contains(handle));
216 return *m_map.find(handle)->value.value;
219 inline void CalculationValueMap::ref(unsigned handle)
221 ASSERT(m_map.contains(handle));
223 ++m_map.find(handle)->value.referenceCountMinusOne;
226 inline void CalculationValueMap::deref(unsigned handle)
228 ASSERT(m_map.contains(handle));
230 auto it = m_map.find(handle);
231 if (it->value.referenceCountMinusOne) {
232 --it->value.referenceCountMinusOne;
236 // The adoptRef here is balanced by the leakRef in the insert member function.
237 Ref<CalculationValue> value { adoptRef(*it->value.value) };
242 static CalculationValueMap& calculationValues()
244 static NeverDestroyed<CalculationValueMap> map;
248 Length::Length(Ref<CalculationValue>&& value)
253 m_calculationValueHandle = calculationValues().insert(WTFMove(value));
256 CalculationValue& Length::calculationValue() const
258 ASSERT(isCalculated());
259 return calculationValues().get(m_calculationValueHandle);
262 void Length::ref() const
264 ASSERT(isCalculated());
265 calculationValues().ref(m_calculationValueHandle);
268 void Length::deref() const
270 ASSERT(isCalculated());
271 calculationValues().deref(m_calculationValueHandle);
274 float Length::nonNanCalculatedValue(int maxValue) const
276 ASSERT(isCalculated());
277 float result = calculationValue().evaluate(maxValue);
278 if (std::isnan(result))
283 bool Length::isCalculatedEqual(const Length& other) const
285 return calculationValue() == other.calculationValue();
288 Length convertTo100PercentMinusLength(const Length& length)
290 if (length.isPercent())
291 return Length(100 - length.value(), Percent);
293 // Turn this into a calc expression: calc(100% - length)
294 Vector<std::unique_ptr<CalcExpressionNode>> lengths;
295 lengths.reserveInitialCapacity(2);
296 lengths.uncheckedAppend(std::make_unique<CalcExpressionLength>(Length(100, Percent)));
297 lengths.uncheckedAppend(std::make_unique<CalcExpressionLength>(length));
298 auto op = std::make_unique<CalcExpressionOperation>(WTFMove(lengths), CalcSubtract);
299 return Length(CalculationValue::create(WTFMove(op), ValueRangeAll));
302 static Length blendMixedTypes(const Length& from, const Length& to, double progress)
310 auto blend = std::make_unique<CalcExpressionBlendLength>(from, to, progress);
311 return Length(CalculationValue::create(WTFMove(blend), ValueRangeAll));
314 Length blend(const Length& from, const Length& to, double progress)
316 if (from.isAuto() || to.isAuto())
317 return progress < 0.5 ? from : to;
319 if (from.isUndefined() || to.isUndefined())
322 if (from.type() == Calculated || to.type() == Calculated)
323 return blendMixedTypes(from, to, progress);
325 if (!from.isZero() && !to.isZero() && from.type() != to.type())
326 return blendMixedTypes(from, to, progress);
328 LengthType resultType = to.type();
330 resultType = from.type();
332 if (resultType == Percent) {
333 float fromPercent = from.isZero() ? 0 : from.percent();
334 float toPercent = to.isZero() ? 0 : to.percent();
335 return Length(WebCore::blend(fromPercent, toPercent, progress), Percent);
338 float fromValue = from.isZero() ? 0 : from.value();
339 float toValue = to.isZero() ? 0 : to.value();
340 return Length(WebCore::blend(fromValue, toValue, progress), resultType);
343 struct SameSizeAsLength {
347 COMPILE_ASSERT(sizeof(Length) == sizeof(SameSizeAsLength), length_should_stay_small);
349 static TextStream& operator<<(TextStream& ts, LengthType type)
352 case Auto: ts << "auto"; break;
353 case Relative: ts << "relative"; break;
354 case Percent: ts << "percent"; break;
355 case Fixed: ts << "fixed"; break;
356 case Intrinsic: ts << "intrinsic"; break;
357 case MinIntrinsic: ts << "min-intrinsic"; break;
358 case MinContent: ts << "min-content"; break;
359 case MaxContent: ts << "max-content"; break;
360 case FillAvailable: ts << "fill-available"; break;
361 case FitContent: ts << "fit-content"; break;
362 case Calculated: ts << "calc"; break;
363 case Undefined: ts << "undefined"; break;
368 TextStream& operator<<(TextStream& ts, Length length)
370 switch (length.type()) {
376 ts << TextStream::FormatNumberRespectingIntegers(length.value()) << "px";
385 ts << length.type() << " " << TextStream::FormatNumberRespectingIntegers(length.value());
388 ts << TextStream::FormatNumberRespectingIntegers(length.percent()) << "%";
391 ts << length.calculationValue();
395 if (length.hasQuirk())
401 } // namespace WebCore