1 /* 2 * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org) 3 * (C) 1999 Antti Koivisto (koivisto (at) kde.org) 4 * (C) 2001 Dirk Mueller ( mueller (at) kde.org ) 5 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. 6 * Copyright (C) 2006 Andrew Wellington (proton (at) 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 "platform/Length.h" 27 28 #include "platform/CalculationValue.h" 29 #include "platform/animation/AnimationUtilities.h" 30 #include "wtf/ASCIICType.h" 31 #include "wtf/text/StringBuffer.h" 32 #include "wtf/text/WTFString.h" 33 34 using namespace WTF; 35 36 namespace blink { 37 38 template<typename CharType> 39 static unsigned splitLength(const CharType* data, unsigned length, unsigned& intLength, unsigned& doubleLength) 40 { 41 ASSERT(length); 42 43 unsigned i = 0; 44 while (i < length && isSpaceOrNewline(data[i])) 45 ++i; 46 if (i < length && (data[i] == '+' || data[i] == '-')) 47 ++i; 48 while (i < length && isASCIIDigit(data[i])) 49 ++i; 50 intLength = i; 51 while (i < length && (isASCIIDigit(data[i]) || data[i] == '.')) 52 ++i; 53 doubleLength = i; 54 55 // IE quirk: Skip whitespace between the number and the % character (20 % => 20%). 56 while (i < length && isSpaceOrNewline(data[i])) 57 ++i; 58 59 return i; 60 } 61 62 template<typename CharType> 63 static Length parseHTMLAreaCoordinate(const CharType* data, unsigned length) 64 { 65 unsigned intLength; 66 unsigned doubleLength; 67 splitLength(data, length, intLength, doubleLength); 68 69 bool ok; 70 int r = charactersToIntStrict(data, intLength, &ok); 71 if (ok) 72 return Length(r, Fixed); 73 return Length(0, Fixed); 74 } 75 76 // FIXME: Per HTML5, this should follow the "rules for parsing a list of integers". 77 Vector<Length> parseHTMLAreaElementCoords(const String& string) 78 { 79 unsigned length = string.length(); 80 StringBuffer<LChar> spacified(length); 81 for (unsigned i = 0; i < length; i++) { 82 UChar cc = string[i]; 83 if (cc > '9' || (cc < '0' && cc != '-' && cc != '.')) 84 spacified[i] = ' '; 85 else 86 spacified[i] = cc; 87 } 88 RefPtr<StringImpl> str = spacified.release(); 89 str = str->simplifyWhiteSpace(); 90 ASSERT(str->is8Bit()); 91 92 if (!str->length()) 93 return Vector<Length>(); 94 95 unsigned len = str->count(' ') + 1; 96 Vector<Length> r(len); 97 98 unsigned i = 0; 99 unsigned pos = 0; 100 size_t pos2; 101 102 while ((pos2 = str->find(' ', pos)) != kNotFound) { 103 r[i++] = parseHTMLAreaCoordinate(str->characters8() + pos, pos2 - pos); 104 pos = pos2 + 1; 105 } 106 r[i] = parseHTMLAreaCoordinate(str->characters8() + pos, str->length() - pos); 107 108 ASSERT(i == len - 1); 109 110 return r; 111 } 112 113 class CalculationValueHandleMap { 114 WTF_MAKE_FAST_ALLOCATED; 115 public: 116 CalculationValueHandleMap() 117 : m_index(1) 118 { 119 } 120 121 int insert(PassRefPtr<CalculationValue> calcValue) 122 { 123 ASSERT(m_index); 124 // FIXME calc(): https://bugs.webkit.org/show_bug.cgi?id=80489 125 // This monotonically increasing handle generation scheme is potentially wasteful 126 // of the handle space. Consider reusing empty handles. 127 while (m_map.contains(m_index)) 128 m_index++; 129 130 m_map.set(m_index, calcValue); 131 132 return m_index; 133 } 134 135 void remove(int index) 136 { 137 ASSERT(m_map.contains(index)); 138 m_map.remove(index); 139 } 140 141 CalculationValue& get(int index) 142 { 143 ASSERT(m_map.contains(index)); 144 return *m_map.get(index); 145 } 146 147 void decrementRef(int index) 148 { 149 ASSERT(m_map.contains(index)); 150 CalculationValue* value = m_map.get(index); 151 if (value->hasOneRef()) { 152 // Force the CalculationValue destructor early to avoid a potential recursive call inside HashMap remove(). 153 m_map.set(index, nullptr); 154 m_map.remove(index); 155 } else { 156 value->deref(); 157 } 158 } 159 160 private: 161 int m_index; 162 HashMap<int, RefPtr<CalculationValue> > m_map; 163 }; 164 165 static CalculationValueHandleMap& calcHandles() 166 { 167 DEFINE_STATIC_LOCAL(CalculationValueHandleMap, handleMap, ()); 168 return handleMap; 169 } 170 171 Length::Length(PassRefPtr<CalculationValue> calc) 172 : m_quirk(false) 173 , m_type(Calculated) 174 , m_isFloat(false) 175 { 176 m_intValue = calcHandles().insert(calc); 177 } 178 179 Length Length::blendMixedTypes(const Length& from, double progress, ValueRange range) const 180 { 181 ASSERT(from.isSpecified()); 182 ASSERT(isSpecified()); 183 PixelsAndPercent fromPixelsAndPercent = from.pixelsAndPercent(); 184 PixelsAndPercent toPixelsAndPercent = pixelsAndPercent(); 185 const float pixels = blink::blend(fromPixelsAndPercent.pixels, toPixelsAndPercent.pixels, progress); 186 const float percent = blink::blend(fromPixelsAndPercent.percent, toPixelsAndPercent.percent, progress); 187 return Length(CalculationValue::create(PixelsAndPercent(pixels, percent), range)); 188 } 189 190 PixelsAndPercent Length::pixelsAndPercent() const 191 { 192 switch (type()) { 193 case Fixed: 194 return PixelsAndPercent(value(), 0); 195 case Percent: 196 return PixelsAndPercent(0, value()); 197 case Calculated: 198 return calculationValue().pixelsAndPercent(); 199 default: 200 ASSERT_NOT_REACHED(); 201 return PixelsAndPercent(0, 0); 202 } 203 } 204 205 Length Length::subtractFromOneHundredPercent() const 206 { 207 PixelsAndPercent result = pixelsAndPercent(); 208 result.pixels = -result.pixels; 209 result.percent = 100 - result.percent; 210 if (result.pixels && result.percent) 211 return Length(CalculationValue::create(result, ValueRangeAll)); 212 if (result.percent) 213 return Length(result.percent, Percent); 214 return Length(result.pixels, Fixed); 215 } 216 217 CalculationValue& Length::calculationValue() const 218 { 219 ASSERT(isCalculated()); 220 return calcHandles().get(calculationHandle()); 221 } 222 223 void Length::incrementCalculatedRef() const 224 { 225 ASSERT(isCalculated()); 226 calculationValue().ref(); 227 } 228 229 void Length::decrementCalculatedRef() const 230 { 231 ASSERT(isCalculated()); 232 calcHandles().decrementRef(calculationHandle()); 233 } 234 235 float Length::nonNanCalculatedValue(int maxValue) const 236 { 237 ASSERT(isCalculated()); 238 float result = calculationValue().evaluate(maxValue); 239 if (std::isnan(result)) 240 return 0; 241 return result; 242 } 243 244 bool Length::isCalculatedEqual(const Length& o) const 245 { 246 return isCalculated() && (&calculationValue() == &o.calculationValue() || calculationValue() == o.calculationValue()); 247 } 248 249 struct SameSizeAsLength { 250 int32_t value; 251 int32_t metaData; 252 }; 253 COMPILE_ASSERT(sizeof(Length) == sizeof(SameSizeAsLength), length_should_stay_small); 254 255 } // namespace blink 256