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