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