1 | /* |
2 | * Copyright (C) 2011 Google Inc. All rights reserved. |
3 | * Copyright (C) 2014 Apple Inc. All rights reserved. |
4 | * |
5 | * Redistribution and use in source and binary forms, with or without |
6 | * modification, are permitted provided that the following conditions are |
7 | * met: |
8 | * |
9 | * * Redistributions of source code must retain the above copyright |
10 | * notice, this list of conditions and the following disclaimer. |
11 | * * Redistributions in binary form must reproduce the above |
12 | * copyright notice, this list of conditions and the following disclaimer |
13 | * in the documentation and/or other materials provided with the |
14 | * distribution. |
15 | * * Neither the name of Google Inc. nor the names of its |
16 | * contributors may be used to endorse or promote products derived from |
17 | * this software without specific prior written permission. |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
30 | */ |
31 | |
32 | #include "config.h" |
33 | #include "CalculationValue.h" |
34 | |
35 | #include "LengthFunctions.h" |
36 | #include <limits> |
37 | #include <wtf/text/TextStream.h> |
38 | |
39 | namespace WebCore { |
40 | |
41 | Ref<CalculationValue> CalculationValue::create(std::unique_ptr<CalcExpressionNode> value, ValueRange range) |
42 | { |
43 | return adoptRef(*new CalculationValue(WTFMove(value), range)); |
44 | } |
45 | |
46 | float CalcExpressionNumber::evaluate(float) const |
47 | { |
48 | return m_value; |
49 | } |
50 | |
51 | void CalcExpressionNumber::dump(TextStream& ts) const |
52 | { |
53 | ts << TextStream::FormatNumberRespectingIntegers(m_value); |
54 | } |
55 | |
56 | bool CalcExpressionNumber::operator==(const CalcExpressionNode& other) const |
57 | { |
58 | return other.type() == CalcExpressionNodeType::Number && *this == toCalcExpressionNumber(other); |
59 | } |
60 | |
61 | float CalculationValue::evaluate(float maxValue) const |
62 | { |
63 | float result = m_expression->evaluate(maxValue); |
64 | // FIXME: This test was originally needed when we did not detect division by zero at parse time. |
65 | // It's possible that this is now unneeded code and can be removed. |
66 | if (std::isnan(result)) |
67 | return 0; |
68 | return m_shouldClampToNonNegative && result < 0 ? 0 : result; |
69 | } |
70 | |
71 | float CalcExpressionOperation::evaluate(float maxValue) const |
72 | { |
73 | switch (m_operator) { |
74 | case CalcOperator::Add: { |
75 | ASSERT(m_children.size() == 2); |
76 | float left = m_children[0]->evaluate(maxValue); |
77 | float right = m_children[1]->evaluate(maxValue); |
78 | return left + right; |
79 | } |
80 | case CalcOperator::Subtract: { |
81 | ASSERT(m_children.size() == 2); |
82 | float left = m_children[0]->evaluate(maxValue); |
83 | float right = m_children[1]->evaluate(maxValue); |
84 | return left - right; |
85 | } |
86 | case CalcOperator::Multiply: { |
87 | ASSERT(m_children.size() == 2); |
88 | float left = m_children[0]->evaluate(maxValue); |
89 | float right = m_children[1]->evaluate(maxValue); |
90 | return left * right; |
91 | } |
92 | case CalcOperator::Divide: { |
93 | ASSERT(m_children.size() == 1 || m_children.size() == 2); |
94 | if (m_children.size() == 1) |
95 | return std::numeric_limits<float>::quiet_NaN(); |
96 | float left = m_children[0]->evaluate(maxValue); |
97 | float right = m_children[1]->evaluate(maxValue); |
98 | return left / right; |
99 | } |
100 | case CalcOperator::Min: { |
101 | if (m_children.isEmpty()) |
102 | return std::numeric_limits<float>::quiet_NaN(); |
103 | float minimum = m_children[0]->evaluate(maxValue); |
104 | for (auto& child : m_children) |
105 | minimum = std::min(minimum, child->evaluate(maxValue)); |
106 | return minimum; |
107 | } |
108 | case CalcOperator::Max: { |
109 | if (m_children.isEmpty()) |
110 | return std::numeric_limits<float>::quiet_NaN(); |
111 | float maximum = m_children[0]->evaluate(maxValue); |
112 | for (auto& child : m_children) |
113 | maximum = std::max(maximum, child->evaluate(maxValue)); |
114 | return maximum; |
115 | } |
116 | } |
117 | ASSERT_NOT_REACHED(); |
118 | return std::numeric_limits<float>::quiet_NaN(); |
119 | } |
120 | |
121 | bool CalcExpressionOperation::operator==(const CalcExpressionNode& other) const |
122 | { |
123 | return other.type() == CalcExpressionNodeType::Operation && *this == toCalcExpressionOperation(other); |
124 | } |
125 | |
126 | bool operator==(const CalcExpressionOperation& a, const CalcExpressionOperation& b) |
127 | { |
128 | if (a.getOperator() != b.getOperator()) |
129 | return false; |
130 | // Maybe Vectors of unique_ptrs should always do deep compare? |
131 | if (a.children().size() != b.children().size()) |
132 | return false; |
133 | for (unsigned i = 0; i < a.children().size(); ++i) { |
134 | if (!(*a.children()[i] == *b.children()[i])) |
135 | return false; |
136 | } |
137 | return true; |
138 | } |
139 | |
140 | void CalcExpressionOperation::dump(TextStream& ts) const |
141 | { |
142 | if (m_operator == CalcOperator::Min || m_operator == CalcOperator::Max) { |
143 | ts << m_operator << "(" ; |
144 | size_t childrenCount = m_children.size(); |
145 | for (size_t i = 0; i < childrenCount; i++) { |
146 | ts << m_children[i].get(); |
147 | if (i < childrenCount - 1) |
148 | ts << ", " ; |
149 | } |
150 | ts << ")" ; |
151 | } else |
152 | ts << m_children[0].get() << " " << m_operator << " " << m_children[1].get(); |
153 | } |
154 | |
155 | float CalcExpressionLength::evaluate(float maxValue) const |
156 | { |
157 | return floatValueForLength(m_length, maxValue); |
158 | } |
159 | |
160 | bool CalcExpressionLength::operator==(const CalcExpressionNode& other) const |
161 | { |
162 | return other.type() == CalcExpressionNodeType::Length && *this == toCalcExpressionLength(other); |
163 | } |
164 | |
165 | void CalcExpressionLength::dump(TextStream& ts) const |
166 | { |
167 | ts << m_length; |
168 | } |
169 | |
170 | CalcExpressionBlendLength::CalcExpressionBlendLength(Length from, Length to, float progress) |
171 | : CalcExpressionNode(CalcExpressionNodeType::BlendLength) |
172 | , m_from(from) |
173 | , m_to(to) |
174 | , m_progress(progress) |
175 | { |
176 | // Flatten nesting of CalcExpressionBlendLength as a speculative fix for rdar://problem/30533005. |
177 | // CalcExpressionBlendLength is only used as a result of animation and they don't nest in normal cases. |
178 | if (m_from.isCalculated() && m_from.calculationValue().expression().type() == CalcExpressionNodeType::BlendLength) |
179 | m_from = toCalcExpressionBlendLength(m_from.calculationValue().expression()).from(); |
180 | if (m_to.isCalculated() && m_to.calculationValue().expression().type() == CalcExpressionNodeType::BlendLength) |
181 | m_to = toCalcExpressionBlendLength(m_to.calculationValue().expression()).to(); |
182 | } |
183 | |
184 | float CalcExpressionBlendLength::evaluate(float maxValue) const |
185 | { |
186 | return (1.0f - m_progress) * floatValueForLength(m_from, maxValue) + m_progress * floatValueForLength(m_to, maxValue); |
187 | } |
188 | |
189 | bool CalcExpressionBlendLength::operator==(const CalcExpressionNode& other) const |
190 | { |
191 | return other.type() == CalcExpressionNodeType::BlendLength && *this == toCalcExpressionBlendLength(other); |
192 | } |
193 | |
194 | void CalcExpressionBlendLength::dump(TextStream& ts) const |
195 | { |
196 | ts << "blend(" << m_from << ", " << m_to << ", " << m_progress << ")" ; |
197 | } |
198 | |
199 | TextStream& operator<<(TextStream& ts, CalcOperator op) |
200 | { |
201 | switch (op) { |
202 | case CalcOperator::Add: ts << "+" ; break; |
203 | case CalcOperator::Subtract: ts << "-" ; break; |
204 | case CalcOperator::Multiply: ts << "*" ; break; |
205 | case CalcOperator::Divide: ts << "/" ; break; |
206 | case CalcOperator::Min: ts << "max" ; break; |
207 | case CalcOperator::Max: ts << "min" ; break; |
208 | } |
209 | return ts; |
210 | } |
211 | |
212 | TextStream& operator<<(TextStream& ts, const CalculationValue& value) |
213 | { |
214 | ts << "calc(" ; |
215 | ts << value.expression(); |
216 | ts << ")" ; |
217 | return ts; |
218 | } |
219 | |
220 | TextStream& operator<<(TextStream& ts, const CalcExpressionNode& expressionNode) |
221 | { |
222 | expressionNode.dump(ts); |
223 | return ts; |
224 | } |
225 | |
226 | } // namespace WebCore |
227 | |