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 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/Assertions.h> 31#include <wtf/OwnArrayPtr.h> 32#include <wtf/text/StringBuffer.h> 33#include <wtf/text/WTFString.h> 34 35using namespace WTF; 36 37namespace WebCore { 38 39static 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 80static int countCharacter(const UChar* data, unsigned length, UChar character) 81{ 82 int count = 0; 83 for (int i = 0; i < static_cast<int>(length); ++i) 84 count += data[i] == character; 85 return count; 86} 87 88PassOwnArrayPtr<Length> newCoordsArray(const String& string, int& len) 89{ 90 unsigned length = string.length(); 91 const UChar* data = string.characters(); 92 StringBuffer<UChar> spacified(length); 93 for (unsigned i = 0; i < length; i++) { 94 UChar cc = data[i]; 95 if (cc > '9' || (cc < '0' && cc != '-' && cc != '*' && cc != '.')) 96 spacified[i] = ' '; 97 else 98 spacified[i] = cc; 99 } 100 RefPtr<StringImpl> str = StringImpl::adopt(spacified); 101 102 str = str->simplifyWhiteSpace(); 103 104 len = countCharacter(str->characters(), str->length(), ' ') + 1; 105 OwnArrayPtr<Length> r = adoptArrayPtr(new Length[len]); 106 107 int i = 0; 108 unsigned pos = 0; 109 size_t pos2; 110 111 while ((pos2 = str->find(' ', pos)) != notFound) { 112 r[i++] = parseLength(str->characters() + pos, pos2 - pos); 113 pos = pos2+1; 114 } 115 r[i] = parseLength(str->characters() + pos, str->length() - pos); 116 117 ASSERT(i == len - 1); 118 119 return r.release(); 120} 121 122PassOwnArrayPtr<Length> newLengthArray(const String& string, int& len) 123{ 124 RefPtr<StringImpl> str = string.impl()->simplifyWhiteSpace(); 125 if (!str->length()) { 126 len = 1; 127 return nullptr; 128 } 129 130 len = countCharacter(str->characters(), str->length(), ',') + 1; 131 OwnArrayPtr<Length> r = adoptArrayPtr(new Length[len]); 132 133 int i = 0; 134 unsigned pos = 0; 135 size_t pos2; 136 137 while ((pos2 = str->find(',', pos)) != notFound) { 138 r[i++] = parseLength(str->characters() + 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(str->characters() + pos, str->length() - pos); 147 else 148 len--; 149 150 return r.release(); 151} 152 153class CalculationValueHandleMap { 154 WTF_MAKE_FAST_ALLOCATED; 155public: 156 CalculationValueHandleMap() 157 : m_index(1) 158 { 159 } 160 161 int insert(PassRefPtr<CalculationValue> calcValue) 162 { 163 ASSERT(m_index); 164 // FIXME calc(): https://bugs.webkit.org/show_bug.cgi?id=80489 165 // This monotonically increasing handle generation scheme is potentially wasteful 166 // of the handle space. Consider reusing empty handles. 167 while (m_map.contains(m_index)) 168 m_index++; 169 170 m_map.set(m_index, calcValue); 171 172 return m_index; 173 } 174 175 void remove(int index) 176 { 177 ASSERT(m_map.contains(index)); 178 m_map.remove(index); 179 } 180 181 PassRefPtr<CalculationValue> get(int index) 182 { 183 ASSERT(m_map.contains(index)); 184 return m_map.get(index); 185 } 186 187private: 188 int m_index; 189 HashMap<int, RefPtr<CalculationValue> > m_map; 190}; 191 192static CalculationValueHandleMap& calcHandles() 193{ 194 DEFINE_STATIC_LOCAL(CalculationValueHandleMap, handleMap, ()); 195 return handleMap; 196} 197 198Length::Length(PassRefPtr<CalculationValue> calc) 199 : m_quirk(false) 200 , m_type(Calculated) 201 , m_isFloat(false) 202{ 203 m_intValue = calcHandles().insert(calc); 204} 205 206Length Length::blendMixedTypes(const Length& from, double progress) const 207{ 208 if (progress <= 0.0) 209 return from; 210 211 if (progress >= 1.0) 212 return *this; 213 214 OwnPtr<CalcExpressionNode> blend = adoptPtr(new CalcExpressionBlendLength(from, *this, progress)); 215 return Length(CalculationValue::create(blend.release(), CalculationRangeAll)); 216} 217 218PassRefPtr<CalculationValue> Length::calculationValue() const 219{ 220 ASSERT(isCalculated()); 221 return calcHandles().get(calculationHandle()); 222} 223 224void Length::incrementCalculatedRef() const 225{ 226 ASSERT(isCalculated()); 227 calculationValue()->ref(); 228} 229 230void Length::decrementCalculatedRef() const 231{ 232 ASSERT(isCalculated()); 233 RefPtr<CalculationValue> calcLength = calculationValue(); 234 if (calcLength->hasOneRef()) 235 calcHandles().remove(calculationHandle()); 236 calcLength->deref(); 237} 238 239float Length::nonNanCalculatedValue(int maxValue) const 240{ 241 ASSERT(isCalculated()); 242 float result = calculationValue()->evaluate(maxValue); 243 if (std::isnan(result)) 244 return 0; 245 return result; 246} 247 248bool Length::isCalculatedEqual(const Length& o) const 249{ 250 return isCalculated() && (calculationValue() == o.calculationValue() || *calculationValue() == *o.calculationValue()); 251} 252 253struct SameSizeAsLength { 254 int32_t value; 255 int32_t metaData; 256}; 257COMPILE_ASSERT(sizeof(Length) == sizeof(SameSizeAsLength), length_should_stay_small); 258 259} // namespace WebCore 260