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 "platform/Length.h"
27
28 #include "platform/CalculationValue.h"
29 #include "wtf/ASCIICType.h"
30 #include "wtf/text/StringBuffer.h"
31 #include "wtf/text/WTFString.h"
32
33 using namespace WTF;
34
35 namespace WebCore {
36
37 template<typename CharType>
splitLength(const CharType * data,unsigned length,unsigned & intLength,unsigned & doubleLength)38 static unsigned splitLength(const CharType* data, unsigned length, unsigned& intLength, unsigned& doubleLength)
39 {
40 ASSERT(length);
41
42 unsigned i = 0;
43 while (i < length && isSpaceOrNewline(data[i]))
44 ++i;
45 if (i < length && (data[i] == '+' || data[i] == '-'))
46 ++i;
47 while (i < length && isASCIIDigit(data[i]))
48 ++i;
49 intLength = i;
50 while (i < length && (isASCIIDigit(data[i]) || data[i] == '.'))
51 ++i;
52 doubleLength = i;
53
54 // IE quirk: Skip whitespace between the number and the % character (20 % => 20%).
55 while (i < length && isSpaceOrNewline(data[i]))
56 ++i;
57
58 return i;
59 }
60
61 template<typename CharType>
parseHTMLAreaCoordinate(const CharType * data,unsigned length)62 static Length parseHTMLAreaCoordinate(const CharType* data, unsigned length)
63 {
64 unsigned intLength;
65 unsigned doubleLength;
66 splitLength(data, length, intLength, doubleLength);
67
68 bool ok;
69 int r = charactersToIntStrict(data, intLength, &ok);
70 if (ok)
71 return Length(r, Fixed);
72 return Length(0, Fixed);
73 }
74
75 // FIXME: Per HTML5, this should follow the "rules for parsing a list of integers".
parseHTMLAreaElementCoords(const String & string)76 Vector<Length> parseHTMLAreaElementCoords(const String& string)
77 {
78 unsigned length = string.length();
79 StringBuffer<LChar> spacified(length);
80 for (unsigned i = 0; i < length; i++) {
81 UChar cc = string[i];
82 if (cc > '9' || (cc < '0' && cc != '-' && cc != '.'))
83 spacified[i] = ' ';
84 else
85 spacified[i] = cc;
86 }
87 RefPtr<StringImpl> str = spacified.release();
88 str = str->simplifyWhiteSpace();
89 ASSERT(str->is8Bit());
90
91 if (!str->length())
92 return Vector<Length>();
93
94 unsigned len = str->count(' ') + 1;
95 Vector<Length> r(len);
96
97 unsigned i = 0;
98 unsigned pos = 0;
99 size_t pos2;
100
101 while ((pos2 = str->find(' ', pos)) != kNotFound) {
102 r[i++] = parseHTMLAreaCoordinate(str->characters8() + pos, pos2 - pos);
103 pos = pos2 + 1;
104 }
105 r[i] = parseHTMLAreaCoordinate(str->characters8() + pos, str->length() - pos);
106
107 ASSERT(i == len - 1);
108
109 return r;
110 }
111
112 class CalculationValueHandleMap {
113 WTF_MAKE_FAST_ALLOCATED;
114 public:
CalculationValueHandleMap()115 CalculationValueHandleMap()
116 : m_index(1)
117 {
118 }
119
insert(PassRefPtr<CalculationValue> calcValue)120 int insert(PassRefPtr<CalculationValue> calcValue)
121 {
122 ASSERT(m_index);
123 // FIXME calc(): https://bugs.webkit.org/show_bug.cgi?id=80489
124 // This monotonically increasing handle generation scheme is potentially wasteful
125 // of the handle space. Consider reusing empty handles.
126 while (m_map.contains(m_index))
127 m_index++;
128
129 m_map.set(m_index, calcValue);
130
131 return m_index;
132 }
133
remove(int index)134 void remove(int index)
135 {
136 ASSERT(m_map.contains(index));
137 m_map.remove(index);
138 }
139
get(int index)140 CalculationValue* get(int index)
141 {
142 ASSERT(m_map.contains(index));
143 return m_map.get(index);
144 }
145
decrementRef(int index)146 void decrementRef(int index)
147 {
148 ASSERT(m_map.contains(index));
149 CalculationValue* value = m_map.get(index);
150 if (value->hasOneRef()) {
151 // Force the CalculationValue destructor early to avoid a potential recursive call inside HashMap remove().
152 m_map.set(index, 0);
153 m_map.remove(index);
154 } else {
155 value->deref();
156 }
157 }
158
159 private:
160 int m_index;
161 HashMap<int, RefPtr<CalculationValue> > m_map;
162 };
163
calcHandles()164 static CalculationValueHandleMap& calcHandles()
165 {
166 DEFINE_STATIC_LOCAL(CalculationValueHandleMap, handleMap, ());
167 return handleMap;
168 }
169
Length(PassRefPtr<CalculationValue> calc)170 Length::Length(PassRefPtr<CalculationValue> calc)
171 : m_quirk(false)
172 , m_type(Calculated)
173 , m_isFloat(false)
174 {
175 m_intValue = calcHandles().insert(calc);
176 }
177
blendMixedTypes(const Length & from,double progress,ValueRange range) const178 Length Length::blendMixedTypes(const Length& from, double progress, ValueRange range) const
179 {
180 return Length(CalculationValue::create(adoptPtr(new CalcExpressionBlendLength(from, *this, progress)), range));
181 }
182
calculationValue() const183 CalculationValue* Length::calculationValue() const
184 {
185 ASSERT(isCalculated());
186 return calcHandles().get(calculationHandle());
187 }
188
incrementCalculatedRef() const189 void Length::incrementCalculatedRef() const
190 {
191 ASSERT(isCalculated());
192 calculationValue()->ref();
193 }
194
decrementCalculatedRef() const195 void Length::decrementCalculatedRef() const
196 {
197 ASSERT(isCalculated());
198 calcHandles().decrementRef(calculationHandle());
199 }
200
nonNanCalculatedValue(int maxValue) const201 float Length::nonNanCalculatedValue(int maxValue) const
202 {
203 ASSERT(isCalculated());
204 float result = calculationValue()->evaluate(maxValue);
205 if (std::isnan(result))
206 return 0;
207 return result;
208 }
209
isCalculatedEqual(const Length & o) const210 bool Length::isCalculatedEqual(const Length& o) const
211 {
212 return isCalculated() && (calculationValue() == o.calculationValue() || *calculationValue() == *o.calculationValue());
213 }
214
215 struct SameSizeAsLength {
216 int32_t value;
217 int32_t metaData;
218 };
219 COMPILE_ASSERT(sizeof(Length) == sizeof(SameSizeAsLength), length_should_stay_small);
220
221 } // namespace WebCore
222