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 "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>
splitLength(const CharType * data,unsigned length,unsigned & intLength,unsigned & doubleLength)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>
parseHTMLAreaCoordinate(const CharType * data,unsigned length)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".
parseHTMLAreaElementCoords(const String & string)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:
CalculationValueHandleMap()116 CalculationValueHandleMap()
117 : m_index(1)
118 {
119 }
120
insert(PassRefPtr<CalculationValue> calcValue)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
remove(int index)135 void remove(int index)
136 {
137 ASSERT(m_map.contains(index));
138 m_map.remove(index);
139 }
140
get(int index)141 CalculationValue& get(int index)
142 {
143 ASSERT(m_map.contains(index));
144 return *m_map.get(index);
145 }
146
decrementRef(int index)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
calcHandles()165 static CalculationValueHandleMap& calcHandles()
166 {
167 DEFINE_STATIC_LOCAL(CalculationValueHandleMap, handleMap, ());
168 return handleMap;
169 }
170
Length(PassRefPtr<CalculationValue> calc)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
blendMixedTypes(const Length & from,double progress,ValueRange range) const179 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
pixelsAndPercent() const190 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
subtractFromOneHundredPercent() const205 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
calculationValue() const217 CalculationValue& Length::calculationValue() const
218 {
219 ASSERT(isCalculated());
220 return calcHandles().get(calculationHandle());
221 }
222
incrementCalculatedRef() const223 void Length::incrementCalculatedRef() const
224 {
225 ASSERT(isCalculated());
226 calculationValue().ref();
227 }
228
decrementCalculatedRef() const229 void Length::decrementCalculatedRef() const
230 {
231 ASSERT(isCalculated());
232 calcHandles().decrementRef(calculationHandle());
233 }
234
nonNanCalculatedValue(int maxValue) const235 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
isCalculatedEqual(const Length & o) const244 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