REGRESSION (Safari 11): custom <font-face> tag crashes a page
[WebKit-https.git] / Source / WebCore / svg / SVGFontFaceElement.cpp
1 /*
2  * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
3  * Copyright (C) 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
4  * Copyright (C) 2008 Apple Inc. All rights reserved.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 #include "config.h"
23
24 #if ENABLE(SVG_FONTS)
25 #include "SVGFontFaceElement.h"
26
27 #include "CSSFontFaceSrcValue.h"
28 #include "CSSParser.h"
29 #include "CSSPropertyNames.h"
30 #include "CSSStyleSheet.h"
31 #include "CSSValueKeywords.h"
32 #include "CSSValueList.h"
33 #include "Document.h"
34 #include "ElementIterator.h"
35 #include "FontCascade.h"
36 #include "SVGDocumentExtensions.h"
37 #include "SVGFontElement.h"
38 #include "SVGFontFaceSrcElement.h"
39 #include "SVGGlyphElement.h"
40 #include "SVGNames.h"
41 #include "StyleProperties.h"
42 #include "StyleResolver.h"
43 #include "StyleRule.h"
44 #include "StyleScope.h"
45 #include <math.h>
46
47 namespace WebCore {
48
49 using namespace SVGNames;
50
51 inline SVGFontFaceElement::SVGFontFaceElement(const QualifiedName& tagName, Document& document)
52     : SVGElement(tagName, document)
53     , m_fontFaceRule(StyleRuleFontFace::create(MutableStyleProperties::create(HTMLStandardMode)))
54     , m_fontElement(nullptr)
55 {
56     ASSERT(hasTagName(font_faceTag));
57 }
58
59 Ref<SVGFontFaceElement> SVGFontFaceElement::create(const QualifiedName& tagName, Document& document)
60 {
61     return adoptRef(*new SVGFontFaceElement(tagName, document));
62 }
63
64 void SVGFontFaceElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
65 {    
66     CSSPropertyID propertyId = cssPropertyIdForSVGAttributeName(name);
67     if (propertyId > 0) {
68         // FIXME: Parse using the @font-face descriptor grammars, not the property grammars.
69         auto& properties = m_fontFaceRule->mutableProperties();
70         bool valueChanged = properties.setProperty(propertyId, value);
71
72         if (valueChanged) {
73             // The above parser is designed for the font-face properties, not descriptors, and the properties accept the global keywords, but descriptors don't.
74             // Rather than invasively modifying the parser for the properties to have a special mode, we can simply detect the error condition after-the-fact and
75             // avoid it explicitly.
76             if (auto parsedValue = properties.getPropertyCSSValue(propertyId)) {
77                 if (parsedValue->isGlobalKeyword())
78                     properties.removeProperty(propertyId);
79             }
80         }
81
82         rebuildFontFace();
83         return;
84     }
85     
86     SVGElement::parseAttribute(name, value);
87 }
88
89 unsigned SVGFontFaceElement::unitsPerEm() const
90 {
91     const AtomicString& value = attributeWithoutSynchronization(units_per_emAttr);
92     if (value.isEmpty())
93         return FontMetrics::defaultUnitsPerEm;
94
95     return static_cast<unsigned>(ceilf(value.toFloat()));
96 }
97
98 int SVGFontFaceElement::xHeight() const
99 {
100     return static_cast<int>(ceilf(attributeWithoutSynchronization(x_heightAttr).toFloat()));
101 }
102
103 int SVGFontFaceElement::capHeight() const
104 {
105     return static_cast<int>(ceilf(attributeWithoutSynchronization(cap_heightAttr).toFloat()));
106 }
107
108 float SVGFontFaceElement::horizontalOriginX() const
109 {
110     if (!m_fontElement)
111         return 0.0f;
112
113     // Spec: The X-coordinate in the font coordinate system of the origin of a glyph to be used when
114     // drawing horizontally oriented text. (Note that the origin applies to all glyphs in the font.)
115     // If the attribute is not specified, the effect is as if a value of "0" were specified.
116     return m_fontElement->attributeWithoutSynchronization(horiz_origin_xAttr).toFloat();
117 }
118
119 float SVGFontFaceElement::horizontalOriginY() const
120 {
121     if (!m_fontElement)
122         return 0.0f;
123
124     // Spec: The Y-coordinate in the font coordinate system of the origin of a glyph to be used when
125     // drawing horizontally oriented text. (Note that the origin applies to all glyphs in the font.)
126     // If the attribute is not specified, the effect is as if a value of "0" were specified.
127     return m_fontElement->attributeWithoutSynchronization(horiz_origin_yAttr).toFloat();
128 }
129
130 float SVGFontFaceElement::horizontalAdvanceX() const
131 {
132     if (!m_fontElement)
133         return 0.0f;
134
135     // Spec: The default horizontal advance after rendering a glyph in horizontal orientation. Glyph
136     // widths are required to be non-negative, even if the glyph is typically rendered right-to-left,
137     // as in Hebrew and Arabic scripts.
138     return m_fontElement->attributeWithoutSynchronization(horiz_adv_xAttr).toFloat();
139 }
140
141 float SVGFontFaceElement::verticalOriginX() const
142 {
143     if (!m_fontElement)
144         return 0.0f;
145
146     // Spec: The default X-coordinate in the font coordinate system of the origin of a glyph to be used when
147     // drawing vertically oriented text. If the attribute is not specified, the effect is as if the attribute
148     // were set to half of the effective value of attribute horiz-adv-x.
149     const AtomicString& value = m_fontElement->attributeWithoutSynchronization(vert_origin_xAttr);
150     if (value.isEmpty())
151         return horizontalAdvanceX() / 2.0f;
152
153     return value.toFloat();
154 }
155
156 float SVGFontFaceElement::verticalOriginY() const
157 {
158     if (!m_fontElement)
159         return 0.0f;
160
161     // Spec: The default Y-coordinate in the font coordinate system of the origin of a glyph to be used when
162     // drawing vertically oriented text. If the attribute is not specified, the effect is as if the attribute
163     // were set to the position specified by the font's ascent attribute.             
164     const AtomicString& value = m_fontElement->attributeWithoutSynchronization(vert_origin_yAttr);
165     if (value.isEmpty())
166         return ascent();
167
168     return value.toFloat();
169 }
170
171 float SVGFontFaceElement::verticalAdvanceY() const
172 {
173     if (!m_fontElement)
174         return 0.0f;
175
176     // Spec: The default vertical advance after rendering a glyph in vertical orientation. If the attribute is
177     // not specified, the effect is as if a value equivalent of one em were specified (see units-per-em).                    
178     const AtomicString& value = m_fontElement->attributeWithoutSynchronization(vert_adv_yAttr);
179        if (value.isEmpty())
180         return 1.0f;
181
182     return value.toFloat();
183 }
184
185 int SVGFontFaceElement::ascent() const
186 {
187     // Spec: Same syntax and semantics as the 'ascent' descriptor within an @font-face rule. The maximum
188     // unaccented height of the font within the font coordinate system. If the attribute is not specified,
189     // the effect is as if the attribute were set to the difference between the units-per-em value and the
190     // vert-origin-y value for the corresponding font.
191     const AtomicString& ascentValue = attributeWithoutSynchronization(ascentAttr);
192     if (!ascentValue.isEmpty())
193         return static_cast<int>(ceilf(ascentValue.toFloat()));
194
195     if (m_fontElement) {
196         const AtomicString& vertOriginY = m_fontElement->attributeWithoutSynchronization(vert_origin_yAttr);
197         if (!vertOriginY.isEmpty())
198             return static_cast<int>(unitsPerEm()) - static_cast<int>(ceilf(vertOriginY.toFloat()));
199     }
200
201     // Match Batiks default value
202     return static_cast<int>(ceilf(unitsPerEm() * 0.8f));
203 }
204
205 int SVGFontFaceElement::descent() const
206 {
207     // Spec: Same syntax and semantics as the 'descent' descriptor within an @font-face rule. The maximum
208     // unaccented depth of the font within the font coordinate system. If the attribute is not specified,
209     // the effect is as if the attribute were set to the vert-origin-y value for the corresponding font.
210     const AtomicString& descentValue = attributeWithoutSynchronization(descentAttr);
211     if (!descentValue.isEmpty()) {
212         // 14 different W3C SVG 1.1 testcases use a negative descent value,
213         // where a positive was meant to be used  Including:
214         // animate-elem-24-t.svg, fonts-elem-01-t.svg, fonts-elem-02-t.svg (and 11 others)
215         int descent = static_cast<int>(ceilf(descentValue.toFloat()));
216         return descent < 0 ? -descent : descent;
217     }
218
219     if (m_fontElement) {
220         const AtomicString& vertOriginY = m_fontElement->attributeWithoutSynchronization(vert_origin_yAttr);
221         if (!vertOriginY.isEmpty())
222             return static_cast<int>(ceilf(vertOriginY.toFloat()));
223     }
224
225     // Match Batiks default value
226     return static_cast<int>(ceilf(unitsPerEm() * 0.2f));
227 }
228
229 String SVGFontFaceElement::fontFamily() const
230 {
231     return m_fontFaceRule->properties().getPropertyValue(CSSPropertyFontFamily);
232 }
233
234 SVGFontElement* SVGFontFaceElement::associatedFontElement() const
235 {
236     ASSERT(parentNode() == m_fontElement);
237     ASSERT(!parentNode() || is<SVGFontElement>(*parentNode()));
238     return m_fontElement;
239 }
240
241 void SVGFontFaceElement::rebuildFontFace()
242 {
243     if (!isConnected()) {
244         ASSERT(!m_fontElement);
245         return;
246     }
247
248     // we currently ignore all but the first src element, alternatively we could concat them
249     auto srcElement = childrenOfType<SVGFontFaceSrcElement>(*this).first();
250
251     bool describesParentFont = is<SVGFontElement>(*parentNode());
252     RefPtr<CSSValueList> list;
253
254     if (describesParentFont) {
255         m_fontElement = downcast<SVGFontElement>(parentNode());
256
257         list = CSSValueList::createCommaSeparated();
258         list->append(CSSFontFaceSrcValue::createLocal(fontFamily()));
259     } else {
260         m_fontElement = nullptr;
261         if (srcElement)
262             list = srcElement->srcValue();
263     }
264
265     if (!list || !list->length())
266         return;
267
268     // Parse in-memory CSS rules
269     m_fontFaceRule->mutableProperties().addParsedProperty(CSSProperty(CSSPropertySrc, list));
270
271     if (describesParentFont) {    
272         // Traverse parsed CSS values and associate CSSFontFaceSrcValue elements with ourselves.
273         RefPtr<CSSValue> src = m_fontFaceRule->properties().getPropertyCSSValue(CSSPropertySrc);
274         CSSValueList* srcList = downcast<CSSValueList>(src.get());
275
276         unsigned srcLength = srcList ? srcList->length() : 0;
277         for (unsigned i = 0; i < srcLength; ++i) {
278             if (auto item = makeRefPtr(downcast<CSSFontFaceSrcValue>(srcList->itemWithoutBoundsCheck(i))))
279                 item->setSVGFontFaceElement(this);
280         }
281     }
282
283     document().styleScope().didChangeStyleSheetEnvironment();
284 }
285
286 Node::InsertedIntoAncestorResult SVGFontFaceElement::insertedIntoAncestor(InsertionType insertionType, ContainerNode& parentOfInsertedTree)
287 {
288     SVGElement::insertedIntoAncestor(insertionType, parentOfInsertedTree);
289     if (!insertionType.connectedToDocument) {
290         ASSERT(!m_fontElement);
291         return InsertedIntoAncestorResult::Done;
292     }
293     document().accessSVGExtensions().registerSVGFontFaceElement(this);
294
295     rebuildFontFace();
296     return InsertedIntoAncestorResult::Done;
297 }
298
299 void SVGFontFaceElement::removedFromAncestor(RemovalType removalType, ContainerNode& oldParentOfRemovedTree)
300 {
301     SVGElement::removedFromAncestor(removalType, oldParentOfRemovedTree);
302
303     if (removalType.disconnectedFromDocument) {
304         m_fontElement = nullptr;
305         document().accessSVGExtensions().unregisterSVGFontFaceElement(this);
306         m_fontFaceRule->mutableProperties().clear();
307
308         document().styleScope().didChangeStyleSheetEnvironment();
309     } else
310         ASSERT(!m_fontElement);
311 }
312
313 void SVGFontFaceElement::childrenChanged(const ChildChange& change)
314 {
315     SVGElement::childrenChanged(change);
316     rebuildFontFace();
317 }
318
319 } // namespace WebCore
320
321 #endif // ENABLE(SVG_FONTS)