1/* 2 * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> 3 * Copyright (C) 2010 Rob Buis <rwlbuis@gmail.com> 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 23#include "core/svg/SVGTextPathElement.h" 24 25#include "XLinkNames.h" 26#include "core/rendering/svg/RenderSVGResource.h" 27#include "core/rendering/svg/RenderSVGTextPath.h" 28#include "core/svg/SVGElementInstance.h" 29 30namespace WebCore { 31 32// Animated property definitions 33DEFINE_ANIMATED_LENGTH(SVGTextPathElement, SVGNames::startOffsetAttr, StartOffset, startOffset) 34DEFINE_ANIMATED_ENUMERATION(SVGTextPathElement, SVGNames::methodAttr, Method, method, SVGTextPathMethodType) 35DEFINE_ANIMATED_ENUMERATION(SVGTextPathElement, SVGNames::spacingAttr, Spacing, spacing, SVGTextPathSpacingType) 36DEFINE_ANIMATED_STRING(SVGTextPathElement, XLinkNames::hrefAttr, Href, href) 37 38BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGTextPathElement) 39 REGISTER_LOCAL_ANIMATED_PROPERTY(startOffset) 40 REGISTER_LOCAL_ANIMATED_PROPERTY(method) 41 REGISTER_LOCAL_ANIMATED_PROPERTY(spacing) 42 REGISTER_LOCAL_ANIMATED_PROPERTY(href) 43 REGISTER_PARENT_ANIMATED_PROPERTIES(SVGTextContentElement) 44END_REGISTER_ANIMATED_PROPERTIES 45 46inline SVGTextPathElement::SVGTextPathElement(Document& document) 47 : SVGTextContentElement(SVGNames::textPathTag, document) 48 , m_startOffset(LengthModeOther) 49 , m_method(SVGTextPathMethodAlign) 50 , m_spacing(SVGTextPathSpacingExact) 51{ 52 ScriptWrappable::init(this); 53 registerAnimatedPropertiesForSVGTextPathElement(); 54} 55 56PassRefPtr<SVGTextPathElement> SVGTextPathElement::create(Document& document) 57{ 58 return adoptRef(new SVGTextPathElement(document)); 59} 60 61SVGTextPathElement::~SVGTextPathElement() 62{ 63 clearResourceReferences(); 64} 65 66void SVGTextPathElement::clearResourceReferences() 67{ 68 document().accessSVGExtensions()->removeAllTargetReferencesForElement(this); 69} 70 71bool SVGTextPathElement::isSupportedAttribute(const QualifiedName& attrName) 72{ 73 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ()); 74 if (supportedAttributes.isEmpty()) { 75 SVGURIReference::addSupportedAttributes(supportedAttributes); 76 supportedAttributes.add(SVGNames::startOffsetAttr); 77 supportedAttributes.add(SVGNames::methodAttr); 78 supportedAttributes.add(SVGNames::spacingAttr); 79 } 80 return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName); 81} 82 83void SVGTextPathElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 84{ 85 SVGParsingError parseError = NoError; 86 87 if (!isSupportedAttribute(name)) 88 SVGTextContentElement::parseAttribute(name, value); 89 else if (name == SVGNames::startOffsetAttr) 90 setStartOffsetBaseValue(SVGLength::construct(LengthModeOther, value, parseError)); 91 else if (name == SVGNames::methodAttr) { 92 SVGTextPathMethodType propertyValue = SVGPropertyTraits<SVGTextPathMethodType>::fromString(value); 93 if (propertyValue > 0) 94 setMethodBaseValue(propertyValue); 95 } else if (name == SVGNames::spacingAttr) { 96 SVGTextPathSpacingType propertyValue = SVGPropertyTraits<SVGTextPathSpacingType>::fromString(value); 97 if (propertyValue > 0) 98 setSpacingBaseValue(propertyValue); 99 } else if (SVGURIReference::parseAttribute(name, value)) { 100 } else 101 ASSERT_NOT_REACHED(); 102 103 reportAttributeParsingError(parseError, name, value); 104} 105 106void SVGTextPathElement::svgAttributeChanged(const QualifiedName& attrName) 107{ 108 if (!isSupportedAttribute(attrName)) { 109 SVGTextContentElement::svgAttributeChanged(attrName); 110 return; 111 } 112 113 SVGElementInstance::InvalidationGuard invalidationGuard(this); 114 115 if (SVGURIReference::isKnownAttribute(attrName)) { 116 buildPendingResource(); 117 return; 118 } 119 120 if (attrName == SVGNames::startOffsetAttr) 121 updateRelativeLengthsInformation(); 122 123 if (RenderObject* object = renderer()) 124 RenderSVGResource::markForLayoutAndParentResourceInvalidation(object); 125} 126 127RenderObject* SVGTextPathElement::createRenderer(RenderStyle*) 128{ 129 return new RenderSVGTextPath(this); 130} 131 132bool SVGTextPathElement::childShouldCreateRenderer(const Node& child) const 133{ 134 if (child.isTextNode() 135 || child.hasTagName(SVGNames::aTag) 136 || child.hasTagName(SVGNames::tspanTag)) 137 return true; 138 139 return false; 140} 141 142bool SVGTextPathElement::rendererIsNeeded(const RenderStyle& style) 143{ 144 if (parentNode() 145 && (parentNode()->hasTagName(SVGNames::aTag) 146 || parentNode()->hasTagName(SVGNames::textTag))) 147 return Element::rendererIsNeeded(style); 148 149 return false; 150} 151 152void SVGTextPathElement::buildPendingResource() 153{ 154 clearResourceReferences(); 155 if (!inDocument()) 156 return; 157 158 String id; 159 Element* target = SVGURIReference::targetElementFromIRIString(hrefCurrentValue(), document(), &id); 160 if (!target) { 161 // Do not register as pending if we are already pending this resource. 162 if (document().accessSVGExtensions()->isElementPendingResource(this, id)) 163 return; 164 165 if (!id.isEmpty()) { 166 document().accessSVGExtensions()->addPendingResource(id, this); 167 ASSERT(hasPendingResources()); 168 } 169 } else if (target->hasTagName(SVGNames::pathTag)) { 170 // Register us with the target in the dependencies map. Any change of hrefElement 171 // that leads to relayout/repainting now informs us, so we can react to it. 172 document().accessSVGExtensions()->addElementReferencingTarget(this, toSVGElement(target)); 173 } 174} 175 176Node::InsertionNotificationRequest SVGTextPathElement::insertedInto(ContainerNode* rootParent) 177{ 178 SVGTextContentElement::insertedInto(rootParent); 179 buildPendingResource(); 180 return InsertionDone; 181} 182 183void SVGTextPathElement::removedFrom(ContainerNode* rootParent) 184{ 185 SVGTextContentElement::removedFrom(rootParent); 186 if (rootParent->inDocument()) 187 clearResourceReferences(); 188} 189 190bool SVGTextPathElement::selfHasRelativeLengths() const 191{ 192 return startOffsetCurrentValue().isRelative() 193 || SVGTextContentElement::selfHasRelativeLengths(); 194} 195 196} 197