1 /*
2 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
4 * Copyright (C) Research In Motion Limited 2009-2010. 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 #include "core/svg/SVGMarkerElement.h"
25
26 #include "SVGNames.h"
27 #include "core/rendering/svg/RenderSVGResourceMarker.h"
28 #include "core/svg/SVGElementInstance.h"
29
30 namespace WebCore {
31
32 // Define custom animated property 'orientType'.
orientTypePropertyInfo()33 const SVGPropertyInfo* SVGMarkerElement::orientTypePropertyInfo()
34 {
35 static const SVGPropertyInfo* s_propertyInfo = 0;
36 if (!s_propertyInfo) {
37 s_propertyInfo = new SVGPropertyInfo(AnimatedEnumeration,
38 PropertyIsReadWrite,
39 SVGNames::orientAttr,
40 orientTypeIdentifier(),
41 &SVGMarkerElement::synchronizeOrientType,
42 &SVGMarkerElement::lookupOrCreateOrientTypeWrapper);
43 }
44 return s_propertyInfo;
45 }
46
47 // Animated property definitions
DEFINE_ANIMATED_LENGTH(SVGMarkerElement,SVGNames::refXAttr,RefX,refX)48 DEFINE_ANIMATED_LENGTH(SVGMarkerElement, SVGNames::refXAttr, RefX, refX)
49 DEFINE_ANIMATED_LENGTH(SVGMarkerElement, SVGNames::refYAttr, RefY, refY)
50 DEFINE_ANIMATED_LENGTH(SVGMarkerElement, SVGNames::markerWidthAttr, MarkerWidth, markerWidth)
51 DEFINE_ANIMATED_LENGTH(SVGMarkerElement, SVGNames::markerHeightAttr, MarkerHeight, markerHeight)
52 DEFINE_ANIMATED_ENUMERATION(SVGMarkerElement, SVGNames::markerUnitsAttr, MarkerUnits, markerUnits, SVGMarkerUnitsType)
53 DEFINE_ANIMATED_ANGLE_AND_ENUMERATION(SVGMarkerElement, SVGNames::orientAttr, orientAngleIdentifier(), OrientAngle, orientAngle)
54 DEFINE_ANIMATED_BOOLEAN(SVGMarkerElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
55 DEFINE_ANIMATED_RECT(SVGMarkerElement, SVGNames::viewBoxAttr, ViewBox, viewBox)
56 DEFINE_ANIMATED_PRESERVEASPECTRATIO(SVGMarkerElement, SVGNames::preserveAspectRatioAttr, PreserveAspectRatio, preserveAspectRatio)
57
58 BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGMarkerElement)
59 REGISTER_LOCAL_ANIMATED_PROPERTY(refX)
60 REGISTER_LOCAL_ANIMATED_PROPERTY(refY)
61 REGISTER_LOCAL_ANIMATED_PROPERTY(markerWidth)
62 REGISTER_LOCAL_ANIMATED_PROPERTY(markerHeight)
63 REGISTER_LOCAL_ANIMATED_PROPERTY(markerUnits)
64 REGISTER_LOCAL_ANIMATED_PROPERTY(orientAngle)
65 REGISTER_LOCAL_ANIMATED_PROPERTY(orientType)
66 REGISTER_LOCAL_ANIMATED_PROPERTY(externalResourcesRequired)
67 REGISTER_LOCAL_ANIMATED_PROPERTY(viewBox)
68 REGISTER_LOCAL_ANIMATED_PROPERTY(preserveAspectRatio)
69 REGISTER_PARENT_ANIMATED_PROPERTIES(SVGElement)
70 END_REGISTER_ANIMATED_PROPERTIES
71
72 inline SVGMarkerElement::SVGMarkerElement(Document& document)
73 : SVGElement(SVGNames::markerTag, document)
74 , m_orientType(SVGMarkerOrientAngle)
75 , m_refX(LengthModeWidth)
76 , m_refY(LengthModeHeight)
77 , m_markerWidth(LengthModeWidth, "3")
78 , m_markerHeight(LengthModeHeight, "3")
79 , m_markerUnits(SVGMarkerUnitsStrokeWidth)
80 {
81 // Spec: If the markerWidth/markerHeight attribute is not specified, the effect is as if a value of "3" were specified.
82 ScriptWrappable::init(this);
83 registerAnimatedPropertiesForSVGMarkerElement();
84 }
85
create(Document & document)86 PassRefPtr<SVGMarkerElement> SVGMarkerElement::create(Document& document)
87 {
88 return adoptRef(new SVGMarkerElement(document));
89 }
90
orientTypeIdentifier()91 const AtomicString& SVGMarkerElement::orientTypeIdentifier()
92 {
93 DEFINE_STATIC_LOCAL(AtomicString, s_identifier, ("SVGOrientType", AtomicString::ConstructFromLiteral));
94 return s_identifier;
95 }
96
orientAngleIdentifier()97 const AtomicString& SVGMarkerElement::orientAngleIdentifier()
98 {
99 DEFINE_STATIC_LOCAL(AtomicString, s_identifier, ("SVGOrientAngle", AtomicString::ConstructFromLiteral));
100 return s_identifier;
101 }
102
viewBoxToViewTransform(float viewWidth,float viewHeight) const103 AffineTransform SVGMarkerElement::viewBoxToViewTransform(float viewWidth, float viewHeight) const
104 {
105 return SVGFitToViewBox::viewBoxToViewTransform(viewBoxCurrentValue(), preserveAspectRatioCurrentValue(), viewWidth, viewHeight);
106 }
107
isSupportedAttribute(const QualifiedName & attrName)108 bool SVGMarkerElement::isSupportedAttribute(const QualifiedName& attrName)
109 {
110 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
111 if (supportedAttributes.isEmpty()) {
112 SVGExternalResourcesRequired::addSupportedAttributes(supportedAttributes);
113 SVGFitToViewBox::addSupportedAttributes(supportedAttributes);
114 supportedAttributes.add(SVGNames::markerUnitsAttr);
115 supportedAttributes.add(SVGNames::refXAttr);
116 supportedAttributes.add(SVGNames::refYAttr);
117 supportedAttributes.add(SVGNames::markerWidthAttr);
118 supportedAttributes.add(SVGNames::markerHeightAttr);
119 supportedAttributes.add(SVGNames::orientAttr);
120 }
121 return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName);
122 }
123
parseAttribute(const QualifiedName & name,const AtomicString & value)124 void SVGMarkerElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
125 {
126 SVGParsingError parseError = NoError;
127
128 if (!isSupportedAttribute(name))
129 SVGElement::parseAttribute(name, value);
130 else if (name == SVGNames::markerUnitsAttr) {
131 SVGMarkerUnitsType propertyValue = SVGPropertyTraits<SVGMarkerUnitsType>::fromString(value);
132 if (propertyValue > 0)
133 setMarkerUnitsBaseValue(propertyValue);
134 } else if (name == SVGNames::refXAttr)
135 setRefXBaseValue(SVGLength::construct(LengthModeWidth, value, parseError));
136 else if (name == SVGNames::refYAttr)
137 setRefYBaseValue(SVGLength::construct(LengthModeHeight, value, parseError));
138 else if (name == SVGNames::markerWidthAttr)
139 setMarkerWidthBaseValue(SVGLength::construct(LengthModeWidth, value, parseError));
140 else if (name == SVGNames::markerHeightAttr)
141 setMarkerHeightBaseValue(SVGLength::construct(LengthModeHeight, value, parseError));
142 else if (name == SVGNames::orientAttr) {
143 SVGAngle angle;
144 SVGMarkerOrientType orientType = SVGPropertyTraits<SVGMarkerOrientType>::fromString(value, angle);
145 if (orientType > 0)
146 setOrientTypeBaseValue(orientType);
147 if (orientType == SVGMarkerOrientAngle)
148 setOrientAngleBaseValue(angle);
149 } else if (SVGExternalResourcesRequired::parseAttribute(name, value)
150 || SVGFitToViewBox::parseAttribute(this, name, value)) {
151 } else
152 ASSERT_NOT_REACHED();
153
154 reportAttributeParsingError(parseError, name, value);
155 }
156
svgAttributeChanged(const QualifiedName & attrName)157 void SVGMarkerElement::svgAttributeChanged(const QualifiedName& attrName)
158 {
159 if (!isSupportedAttribute(attrName)) {
160 SVGElement::svgAttributeChanged(attrName);
161 return;
162 }
163
164 SVGElementInstance::InvalidationGuard invalidationGuard(this);
165
166 if (attrName == SVGNames::refXAttr
167 || attrName == SVGNames::refYAttr
168 || attrName == SVGNames::markerWidthAttr
169 || attrName == SVGNames::markerHeightAttr)
170 updateRelativeLengthsInformation();
171
172 RenderSVGResourceContainer* renderer = toRenderSVGResourceContainer(this->renderer());
173 if (renderer)
174 renderer->invalidateCacheAndMarkForLayout();
175 }
176
childrenChanged(bool changedByParser,Node * beforeChange,Node * afterChange,int childCountDelta)177 void SVGMarkerElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
178 {
179 SVGElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
180
181 if (changedByParser)
182 return;
183
184 if (RenderObject* object = renderer())
185 object->setNeedsLayout();
186 }
187
setOrientToAuto()188 void SVGMarkerElement::setOrientToAuto()
189 {
190 setOrientTypeBaseValue(SVGMarkerOrientAuto);
191 setOrientAngleBaseValue(SVGAngle());
192
193 // Mark orientAttr dirty - the next XML DOM access of that attribute kicks in synchronization.
194 m_orientAngle.shouldSynchronize = true;
195 m_orientType.shouldSynchronize = true;
196 invalidateSVGAttributes();
197 svgAttributeChanged(orientAnglePropertyInfo()->attributeName);
198 }
199
setOrientToAngle(const SVGAngle & angle)200 void SVGMarkerElement::setOrientToAngle(const SVGAngle& angle)
201 {
202 setOrientTypeBaseValue(SVGMarkerOrientAngle);
203 setOrientAngleBaseValue(angle);
204
205 // Mark orientAttr dirty - the next XML DOM access of that attribute kicks in synchronization.
206 m_orientAngle.shouldSynchronize = true;
207 m_orientType.shouldSynchronize = true;
208 invalidateSVGAttributes();
209 svgAttributeChanged(orientAnglePropertyInfo()->attributeName);
210 }
211
createRenderer(RenderStyle *)212 RenderObject* SVGMarkerElement::createRenderer(RenderStyle*)
213 {
214 return new RenderSVGResourceMarker(this);
215 }
216
selfHasRelativeLengths() const217 bool SVGMarkerElement::selfHasRelativeLengths() const
218 {
219 return refXCurrentValue().isRelative()
220 || refYCurrentValue().isRelative()
221 || markerWidthCurrentValue().isRelative()
222 || markerHeightCurrentValue().isRelative();
223 }
224
synchronizeOrientType(SVGElement * contextElement)225 void SVGMarkerElement::synchronizeOrientType(SVGElement* contextElement)
226 {
227 ASSERT(contextElement);
228 SVGMarkerElement* ownerType = toSVGMarkerElement(contextElement);
229 if (!ownerType->m_orientType.shouldSynchronize)
230 return;
231
232 // If orient is not auto, the previous call to synchronizeOrientAngle already set the orientAttr to the right angle.
233 if (ownerType->m_orientType.value != SVGMarkerOrientAuto)
234 return;
235
236 DEFINE_STATIC_LOCAL(AtomicString, autoString, ("auto", AtomicString::ConstructFromLiteral));
237 ownerType->m_orientType.synchronize(ownerType, orientTypePropertyInfo()->attributeName, autoString);
238 }
239
lookupOrCreateOrientTypeWrapper(SVGElement * contextElement)240 PassRefPtr<SVGAnimatedProperty> SVGMarkerElement::lookupOrCreateOrientTypeWrapper(SVGElement* contextElement)
241 {
242 ASSERT(contextElement);
243 SVGMarkerElement* ownerType = toSVGMarkerElement(contextElement);
244 return SVGAnimatedProperty::lookupOrCreateWrapper<SVGMarkerElement, SVGAnimatedEnumerationPropertyTearOff<SVGMarkerOrientType>, SVGMarkerOrientType>
245 (ownerType, orientTypePropertyInfo(), ownerType->m_orientType.value);
246 }
247
orientType()248 PassRefPtr<SVGAnimatedEnumerationPropertyTearOff<SVGMarkerOrientType> > SVGMarkerElement::orientType()
249 {
250 m_orientType.shouldSynchronize = true;
251 return static_pointer_cast<SVGAnimatedEnumerationPropertyTearOff<SVGMarkerOrientType> >(lookupOrCreateOrientTypeWrapper(this));
252 }
253
254 }
255