Have is<>(T*) function do a null check on the pointer argument
[WebKit-https.git] / Source / WebCore / svg / SVGLengthContext.cpp
1 /*
2  * Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <zimmermann@kde.org>
3  * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
4  * Copyright (C) 2007 Apple Inc. All rights reserved.
5  * Copyright (C) Research In Motion Limited 2011. All rights reserved.
6  * Copyright (C) 2014 Adobe Systems Incorporated. All rights reserved.
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 #include "config.h"
25 #include "SVGLengthContext.h"
26
27 #include "CSSHelper.h"
28 #include "ExceptionCode.h"
29 #include "FontMetrics.h"
30 #include "Frame.h"
31 #include "LengthFunctions.h"
32 #include "RenderSVGRoot.h"
33 #include "RenderSVGViewportContainer.h"
34 #include "RenderView.h"
35 #include "SVGNames.h"
36 #include "SVGSVGElement.h"
37
38 namespace WebCore {
39
40 SVGLengthContext::SVGLengthContext(const SVGElement* context)
41     : m_context(context)
42 {
43 }
44
45 SVGLengthContext::SVGLengthContext(const SVGElement* context, const FloatRect& viewport)
46     : m_context(context)
47     , m_overridenViewport(viewport)
48 {
49 }
50
51 FloatRect SVGLengthContext::resolveRectangle(const SVGElement* context, SVGUnitTypes::SVGUnitType type, const FloatRect& viewport, const SVGLength& x, const SVGLength& y, const SVGLength& width, const SVGLength& height)
52 {
53     ASSERT(type != SVGUnitTypes::SVG_UNIT_TYPE_UNKNOWN);
54     if (type == SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) {
55         SVGLengthContext lengthContext(context);
56         return FloatRect(x.value(lengthContext), y.value(lengthContext), width.value(lengthContext), height.value(lengthContext));
57     }
58
59     SVGLengthContext lengthContext(context, viewport);
60     return FloatRect(x.value(lengthContext) + viewport.x(),
61                      y.value(lengthContext) + viewport.y(),
62                      width.value(lengthContext),
63                      height.value(lengthContext));
64 }
65
66 FloatPoint SVGLengthContext::resolvePoint(const SVGElement* context, SVGUnitTypes::SVGUnitType type, const SVGLength& x, const SVGLength& y)
67 {
68     ASSERT(type != SVGUnitTypes::SVG_UNIT_TYPE_UNKNOWN);
69     if (type == SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) {
70         SVGLengthContext lengthContext(context);
71         return FloatPoint(x.value(lengthContext), y.value(lengthContext));
72     }
73
74     // FIXME: valueAsPercentage() won't be correct for eg. cm units. They need to be resolved in user space and then be considered in objectBoundingBox space.
75     return FloatPoint(x.valueAsPercentage(), y.valueAsPercentage());
76 }
77
78 float SVGLengthContext::resolveLength(const SVGElement* context, SVGUnitTypes::SVGUnitType type, const SVGLength& x)
79 {
80     ASSERT(type != SVGUnitTypes::SVG_UNIT_TYPE_UNKNOWN);
81     if (type == SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) {
82         SVGLengthContext lengthContext(context);
83         return x.value(lengthContext);
84     }
85
86     // FIXME: valueAsPercentage() won't be correct for eg. cm units. They need to be resolved in user space and then be considered in objectBoundingBox space.
87     return x.valueAsPercentage();
88 }
89
90 float SVGLengthContext::valueForLength(const Length& length, SVGLengthMode mode)
91 {
92     if (length.isPercent() && !length.isCalculated())
93         return convertValueFromPercentageToUserUnits(length.value() / 100, mode, IGNORE_EXCEPTION);
94     if (length.isAuto())
95         return 0;
96
97     FloatSize viewportSize;
98     determineViewport(viewportSize);
99
100     switch (mode) {
101     case LengthModeWidth:
102         return floatValueForLength(length, viewportSize.width());
103     case LengthModeHeight:
104         return floatValueForLength(length, viewportSize.height());
105     case LengthModeOther:
106         return floatValueForLength(length, sqrtf(viewportSize.diagonalLengthSquared() / 2));
107     };
108     return 0;
109 }
110
111 float SVGLengthContext::convertValueToUserUnits(float value, SVGLengthMode mode, SVGLengthType fromUnit, ExceptionCode& ec) const
112 {
113     // If the SVGLengthContext carries a custom viewport, force resolving against it.
114     if (!m_overridenViewport.isEmpty()) {
115         // 100% = 100.0 instead of 1.0 for historical reasons, this could eventually be changed
116         if (fromUnit == LengthTypePercentage)
117             value /= 100;
118         return convertValueFromPercentageToUserUnits(value, mode, ec);
119     }
120
121     switch (fromUnit) {
122     case LengthTypeUnknown:
123         ec = NOT_SUPPORTED_ERR;
124         return 0;
125     case LengthTypeNumber:
126         return value;
127     case LengthTypePX:
128         return value;
129     case LengthTypePercentage:
130         return convertValueFromPercentageToUserUnits(value / 100, mode, ec);
131     case LengthTypeEMS:
132         return convertValueFromEMSToUserUnits(value, ec);
133     case LengthTypeEXS:
134         return convertValueFromEXSToUserUnits(value, ec);
135     case LengthTypeCM:
136         return value * cssPixelsPerInch / 2.54f;
137     case LengthTypeMM:
138         return value * cssPixelsPerInch / 25.4f;
139     case LengthTypeIN:
140         return value * cssPixelsPerInch;
141     case LengthTypePT:
142         return value * cssPixelsPerInch / 72;
143     case LengthTypePC:
144         return value * cssPixelsPerInch / 6;
145     }
146
147     ASSERT_NOT_REACHED();
148     return 0;
149 }
150
151 float SVGLengthContext::convertValueFromUserUnits(float value, SVGLengthMode mode, SVGLengthType toUnit, ExceptionCode& ec) const
152 {
153     switch (toUnit) {
154     case LengthTypeUnknown:
155         ec = NOT_SUPPORTED_ERR;
156         return 0;
157     case LengthTypeNumber:
158         return value;
159     case LengthTypePercentage:
160         return convertValueFromUserUnitsToPercentage(value * 100, mode, ec);
161     case LengthTypeEMS:
162         return convertValueFromUserUnitsToEMS(value, ec);
163     case LengthTypeEXS:
164         return convertValueFromUserUnitsToEXS(value, ec);
165     case LengthTypePX:
166         return value;
167     case LengthTypeCM:
168         return value * 2.54f / cssPixelsPerInch;
169     case LengthTypeMM:
170         return value * 25.4f / cssPixelsPerInch;
171     case LengthTypeIN:
172         return value / cssPixelsPerInch;
173     case LengthTypePT:
174         return value * 72 / cssPixelsPerInch;
175     case LengthTypePC:
176         return value * 6 / cssPixelsPerInch;
177     }
178
179     ASSERT_NOT_REACHED();
180     return 0;
181 }
182
183 float SVGLengthContext::convertValueFromUserUnitsToPercentage(float value, SVGLengthMode mode, ExceptionCode& ec) const
184 {
185     FloatSize viewportSize;
186     if (!determineViewport(viewportSize)) {
187         ec = NOT_SUPPORTED_ERR;
188         return 0;
189     }
190
191     switch (mode) {
192     case LengthModeWidth:
193         return value / viewportSize.width() * 100;
194     case LengthModeHeight:
195         return value / viewportSize.height() * 100;
196     case LengthModeOther:
197         return value / (sqrtf(viewportSize.diagonalLengthSquared() / 2)) * 100;
198     };
199
200     ASSERT_NOT_REACHED();
201     return 0;
202 }
203
204 float SVGLengthContext::convertValueFromPercentageToUserUnits(float value, SVGLengthMode mode, ExceptionCode& ec) const
205 {
206     FloatSize viewportSize;
207     if (!determineViewport(viewportSize)) {
208         ec = NOT_SUPPORTED_ERR;
209         return 0;
210     }
211
212     switch (mode) {
213     case LengthModeWidth:
214         return value * viewportSize.width();
215     case LengthModeHeight:
216         return value * viewportSize.height();
217     case LengthModeOther:
218         return value * sqrtf(viewportSize.diagonalLengthSquared() / 2);
219     };
220
221     ASSERT_NOT_REACHED();
222     return 0;
223 }
224
225 static inline RenderStyle* renderStyleForLengthResolving(const SVGElement* context)
226 {
227     if (!context)
228         return nullptr;
229
230     const ContainerNode* currentContext = context;
231     do {
232         if (currentContext->renderer())
233             return &currentContext->renderer()->style();
234         currentContext = currentContext->parentNode();
235     } while (currentContext);
236
237     // There must be at least a RenderSVGRoot renderer, carrying a style.
238     ASSERT_NOT_REACHED();
239     return 0;
240 }
241
242 float SVGLengthContext::convertValueFromUserUnitsToEMS(float value, ExceptionCode& ec) const
243 {
244     RenderStyle* style = renderStyleForLengthResolving(m_context);
245     if (!style) {
246         ec = NOT_SUPPORTED_ERR;
247         return 0;
248     }
249
250     float fontSize = style->fontSize();
251     if (!fontSize) {
252         ec = NOT_SUPPORTED_ERR;
253         return 0;
254     }
255
256     return value / fontSize;
257 }
258
259 float SVGLengthContext::convertValueFromEMSToUserUnits(float value, ExceptionCode& ec) const
260 {
261     RenderStyle* style = renderStyleForLengthResolving(m_context);
262     if (!style) {
263         ec = NOT_SUPPORTED_ERR;
264         return 0;
265     }
266
267     return value * style->fontSize();
268 }
269
270 float SVGLengthContext::convertValueFromUserUnitsToEXS(float value, ExceptionCode& ec) const
271 {
272     RenderStyle* style = renderStyleForLengthResolving(m_context);
273     if (!style) {
274         ec = NOT_SUPPORTED_ERR;
275         return 0;
276     }
277
278     // Use of ceil allows a pixel match to the W3Cs expected output of coords-units-03-b.svg
279     // if this causes problems in real world cases maybe it would be best to remove this
280     float xHeight = ceilf(style->fontMetrics().xHeight());
281     if (!xHeight) {
282         ec = NOT_SUPPORTED_ERR;
283         return 0;
284     }
285
286     return value / xHeight;
287 }
288
289 float SVGLengthContext::convertValueFromEXSToUserUnits(float value, ExceptionCode& ec) const
290 {
291     RenderStyle* style = renderStyleForLengthResolving(m_context);
292     if (!style) {
293         ec = NOT_SUPPORTED_ERR;
294         return 0;
295     }
296
297     // Use of ceil allows a pixel match to the W3Cs expected output of coords-units-03-b.svg
298     // if this causes problems in real world cases maybe it would be best to remove this
299     return value * ceilf(style->fontMetrics().xHeight());
300 }
301
302 bool SVGLengthContext::determineViewport(FloatSize& viewportSize) const
303 {
304     if (!m_context)
305         return false;
306
307     // If an overriden viewport is given, it has precedence.
308     if (!m_overridenViewport.isEmpty()) {
309         viewportSize = m_overridenViewport.size();
310         return true;
311     }
312
313     // Root <svg> element lengths are resolved against the top level viewport.
314     if (m_context->isOutermostSVGSVGElement()) {
315         viewportSize = downcast<SVGSVGElement>(*m_context).currentViewportSize();
316         return true;
317     }
318
319     // Take size from nearest viewport element.
320     SVGElement* viewportElement = m_context->viewportElement();
321     if (!is<SVGSVGElement>(viewportElement))
322         return false;
323
324     const SVGSVGElement& svg = downcast<SVGSVGElement>(*viewportElement);
325     viewportSize = svg.currentViewBoxRect().size();
326     if (viewportSize.isEmpty())
327         viewportSize = svg.currentViewportSize();
328
329     return true;
330 }
331
332 }