Improve use of NeverDestroyed
[WebKit-https.git] / Source / WebCore / svg / SVGTextContentElement.cpp
1 /*
2  * Copyright (C) 2004, 2005, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3  * Copyright (C) 2004, 2005, 2006, 2007, 2008 Rob Buis <buis@kde.org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this library; see the file COPYING.LIB.  If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 #include "config.h"
22 #include "SVGTextContentElement.h"
23
24 #include "CSSPropertyNames.h"
25 #include "CSSValueKeywords.h"
26 #include "Frame.h"
27 #include "FrameSelection.h"
28 #include "RenderObject.h"
29 #include "RenderSVGResource.h"
30 #include "RenderSVGText.h"
31 #include "SVGDocumentExtensions.h"
32 #include "SVGNames.h"
33 #include "SVGPoint.h"
34 #include "SVGRect.h"
35 #include "SVGTextQuery.h"
36 #include "XMLNames.h"
37 #include <wtf/NeverDestroyed.h>
38
39 namespace WebCore {
40  
41 // Define custom animated property 'textLength'.
42 const SVGPropertyInfo* SVGTextContentElement::textLengthPropertyInfo()
43 {
44     static const SVGPropertyInfo* s_propertyInfo = nullptr;
45     if (!s_propertyInfo) {
46         s_propertyInfo = new SVGPropertyInfo(AnimatedLength,
47                                              PropertyIsReadWrite,
48                                              SVGNames::textLengthAttr,
49                                              SVGNames::textLengthAttr.localName(),
50                                              &SVGTextContentElement::synchronizeTextLength,
51                                              &SVGTextContentElement::lookupOrCreateTextLengthWrapper);
52     }
53     return s_propertyInfo;
54 }
55
56 // Animated property definitions
57 DEFINE_ANIMATED_ENUMERATION(SVGTextContentElement, SVGNames::lengthAdjustAttr, LengthAdjust, lengthAdjust, SVGLengthAdjustType)
58 DEFINE_ANIMATED_BOOLEAN(SVGTextContentElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
59
60 BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGTextContentElement)
61     REGISTER_LOCAL_ANIMATED_PROPERTY(textLength)
62     REGISTER_LOCAL_ANIMATED_PROPERTY(lengthAdjust)
63     REGISTER_LOCAL_ANIMATED_PROPERTY(externalResourcesRequired)
64     REGISTER_PARENT_ANIMATED_PROPERTIES(SVGGraphicsElement)
65 END_REGISTER_ANIMATED_PROPERTIES
66
67 SVGTextContentElement::SVGTextContentElement(const QualifiedName& tagName, Document& document)
68     : SVGGraphicsElement(tagName, document)
69     , m_textLength(LengthModeOther)
70     , m_specifiedTextLength(LengthModeOther)
71     , m_lengthAdjust(SVGLengthAdjustSpacing)
72 {
73     registerAnimatedPropertiesForSVGTextContentElement();
74 }
75
76 void SVGTextContentElement::synchronizeTextLength(SVGElement* contextElement)
77 {
78     ASSERT(contextElement);
79     SVGTextContentElement& ownerType = downcast<SVGTextContentElement>(*contextElement);
80     if (!ownerType.m_textLength.shouldSynchronize)
81         return;
82     AtomicString value(SVGPropertyTraits<SVGLengthValue>::toString(ownerType.m_specifiedTextLength));
83     ownerType.m_textLength.synchronize(&ownerType, textLengthPropertyInfo()->attributeName, value);
84 }
85
86 Ref<SVGAnimatedProperty> SVGTextContentElement::lookupOrCreateTextLengthWrapper(SVGElement* contextElement)
87 {
88     ASSERT(contextElement);
89     SVGTextContentElement& ownerType = downcast<SVGTextContentElement>(*contextElement);
90     return SVGAnimatedProperty::lookupOrCreateWrapper<SVGTextContentElement, SVGAnimatedLength, SVGLengthValue>
91         (&ownerType, textLengthPropertyInfo(), ownerType.m_textLength.value);
92 }
93
94 Ref<SVGAnimatedLength> SVGTextContentElement::textLengthAnimated()
95 {
96     static NeverDestroyed<SVGLengthValue> defaultTextLength(LengthModeOther);
97     if (m_specifiedTextLength == defaultTextLength)
98         m_textLength.value.newValueSpecifiedUnits(LengthTypeNumber, getComputedTextLength());
99
100     m_textLength.shouldSynchronize = true;
101     return static_reference_cast<SVGAnimatedLength>(lookupOrCreateTextLengthWrapper(this));
102 }
103
104 unsigned SVGTextContentElement::getNumberOfChars()
105 {
106     document().updateLayoutIgnorePendingStylesheets();
107     return SVGTextQuery(renderer()).numberOfCharacters();
108 }
109
110 float SVGTextContentElement::getComputedTextLength()
111 {
112     document().updateLayoutIgnorePendingStylesheets();
113     return SVGTextQuery(renderer()).textLength();
114 }
115
116 ExceptionOr<float> SVGTextContentElement::getSubStringLength(unsigned charnum, unsigned nchars)
117 {
118     unsigned numberOfChars = getNumberOfChars();
119     if (charnum >= numberOfChars)
120         return Exception { INDEX_SIZE_ERR };
121
122     nchars = std::min(nchars, numberOfChars - charnum);
123     return SVGTextQuery(renderer()).subStringLength(charnum, nchars);
124 }
125
126 ExceptionOr<Ref<SVGPoint>> SVGTextContentElement::getStartPositionOfChar(unsigned charnum)
127 {
128     if (charnum > getNumberOfChars())
129         return Exception { INDEX_SIZE_ERR };
130
131     return SVGPoint::create(SVGTextQuery(renderer()).startPositionOfCharacter(charnum));
132 }
133
134 ExceptionOr<Ref<SVGPoint>> SVGTextContentElement::getEndPositionOfChar(unsigned charnum)
135 {
136     if (charnum > getNumberOfChars())
137         return Exception { INDEX_SIZE_ERR };
138
139     return SVGPoint::create(SVGTextQuery(renderer()).endPositionOfCharacter(charnum));
140 }
141
142 ExceptionOr<Ref<SVGRect>> SVGTextContentElement::getExtentOfChar(unsigned charnum)
143 {
144     if (charnum > getNumberOfChars())
145         return Exception { INDEX_SIZE_ERR };
146
147     return SVGRect::create(SVGTextQuery(renderer()).extentOfCharacter(charnum));
148 }
149
150 ExceptionOr<float> SVGTextContentElement::getRotationOfChar(unsigned charnum)
151 {
152     if (charnum > getNumberOfChars())
153         return Exception { INDEX_SIZE_ERR };
154
155     return SVGTextQuery(renderer()).rotationOfCharacter(charnum);
156 }
157
158 int SVGTextContentElement::getCharNumAtPosition(SVGPoint& point)
159 {
160     document().updateLayoutIgnorePendingStylesheets();
161     return SVGTextQuery(renderer()).characterNumberAtPosition(point.propertyReference());
162 }
163
164 ExceptionOr<void> SVGTextContentElement::selectSubString(unsigned charnum, unsigned nchars)
165 {
166     unsigned numberOfChars = getNumberOfChars();
167     if (charnum >= numberOfChars)
168         return Exception { INDEX_SIZE_ERR };
169
170     nchars = std::min(nchars, numberOfChars - charnum);
171
172     ASSERT(document().frame());
173
174     FrameSelection& selection = document().frame()->selection();
175
176     // Find selection start
177     VisiblePosition start(firstPositionInNode(const_cast<SVGTextContentElement*>(this)));
178     for (unsigned i = 0; i < charnum; ++i)
179         start = start.next();
180
181     // Find selection end
182     VisiblePosition end(start);
183     for (unsigned i = 0; i < nchars; ++i)
184         end = end.next();
185
186     selection.setSelection(VisibleSelection(start, end));
187
188     return { };
189 }
190
191 bool SVGTextContentElement::isSupportedAttribute(const QualifiedName& attrName)
192 {
193     static const auto supportedAttributes = makeNeverDestroyed([] {
194         HashSet<QualifiedName> set;
195         SVGLangSpace::addSupportedAttributes(set);
196         SVGExternalResourcesRequired::addSupportedAttributes(set);
197         set.add({ SVGNames::lengthAdjustAttr, SVGNames::textLengthAttr });
198         return set;
199     }());
200     return supportedAttributes.get().contains<SVGAttributeHashTranslator>(attrName);
201 }
202
203 bool SVGTextContentElement::isPresentationAttribute(const QualifiedName& name) const
204 {
205     if (name.matches(XMLNames::spaceAttr))
206         return true;
207     return SVGGraphicsElement::isPresentationAttribute(name);
208 }
209
210 void SVGTextContentElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStyleProperties& style)
211 {
212     if (name.matches(XMLNames::spaceAttr)) {
213         if (value == "preserve")
214             addPropertyToPresentationAttributeStyle(style, CSSPropertyWhiteSpace, CSSValuePre);
215         else
216             addPropertyToPresentationAttributeStyle(style, CSSPropertyWhiteSpace, CSSValueNowrap);
217         return;
218     }
219
220     SVGGraphicsElement::collectStyleForPresentationAttribute(name, value, style);
221 }
222
223 void SVGTextContentElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
224 {
225     SVGParsingError parseError = NoError;
226
227     if (name == SVGNames::lengthAdjustAttr) {
228         auto propertyValue = SVGPropertyTraits<SVGLengthAdjustType>::fromString(value);
229         if (propertyValue > 0)
230             setLengthAdjustBaseValue(propertyValue);
231     } else if (name == SVGNames::textLengthAttr)
232         m_textLength.value = SVGLengthValue::construct(LengthModeOther, value, parseError, ForbidNegativeLengths);
233
234     reportAttributeParsingError(parseError, name, value);
235
236     SVGGraphicsElement::parseAttribute(name, value);
237     SVGExternalResourcesRequired::parseAttribute(name, value);
238 }
239
240 void SVGTextContentElement::svgAttributeChanged(const QualifiedName& attrName)
241 {
242     if (!isSupportedAttribute(attrName)) {
243         SVGGraphicsElement::svgAttributeChanged(attrName);
244         return;
245     }
246
247     if (attrName == SVGNames::textLengthAttr)
248         m_specifiedTextLength = m_textLength.value;
249
250     if (auto renderer = this->renderer()) {
251         InstanceInvalidationGuard guard(*this);
252         RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer);
253     }
254 }
255
256 bool SVGTextContentElement::selfHasRelativeLengths() const
257 {
258     // Any element of the <text> subtree is advertized as using relative lengths.
259     // On any window size change, we have to relayout the text subtree, as the
260     // effective 'on-screen' font size may change.
261     return true;
262 }
263
264 SVGTextContentElement* SVGTextContentElement::elementFromRenderer(RenderObject* renderer)
265 {
266     if (!renderer)
267         return nullptr;
268
269     if (!renderer->isSVGText() && !renderer->isSVGInline())
270         return nullptr;
271
272     SVGElement* element = downcast<SVGElement>(renderer->node());
273     ASSERT(element);
274
275     if (!is<SVGTextContentElement>(*element))
276         return nullptr;
277
278     return downcast<SVGTextContentElement>(element);
279 }
280
281 }