1 /* 2 * Copyright (C) 2007 Nikolas Zimmermann <zimmermann (at) kde.org> 3 * Copyright (C) 2010 Rob Buis <rwlbuis (at) 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 "core/XLinkNames.h" 26 #include "core/rendering/svg/RenderSVGResource.h" 27 #include "core/rendering/svg/RenderSVGTextPath.h" 28 29 namespace WebCore { 30 31 template<> const SVGEnumerationStringEntries& getStaticStringEntries<SVGTextPathMethodType>() 32 { 33 DEFINE_STATIC_LOCAL(SVGEnumerationStringEntries, entries, ()); 34 if (entries.isEmpty()) { 35 entries.append(std::make_pair(SVGTextPathMethodAlign, "align")); 36 entries.append(std::make_pair(SVGTextPathMethodStretch, "stretch")); 37 } 38 return entries; 39 } 40 41 template<> const SVGEnumerationStringEntries& getStaticStringEntries<SVGTextPathSpacingType>() 42 { 43 DEFINE_STATIC_LOCAL(SVGEnumerationStringEntries, entries, ()); 44 if (entries.isEmpty()) { 45 entries.append(std::make_pair(SVGTextPathSpacingAuto, "auto")); 46 entries.append(std::make_pair(SVGTextPathSpacingExact, "exact")); 47 } 48 return entries; 49 } 50 51 inline SVGTextPathElement::SVGTextPathElement(Document& document) 52 : SVGTextContentElement(SVGNames::textPathTag, document) 53 , SVGURIReference(this) 54 , m_startOffset(SVGAnimatedLength::create(this, SVGNames::startOffsetAttr, SVGLength::create(LengthModeOther), AllowNegativeLengths)) 55 , m_method(SVGAnimatedEnumeration<SVGTextPathMethodType>::create(this, SVGNames::methodAttr, SVGTextPathMethodAlign)) 56 , m_spacing(SVGAnimatedEnumeration<SVGTextPathSpacingType>::create(this, SVGNames::spacingAttr, SVGTextPathSpacingExact)) 57 { 58 ScriptWrappable::init(this); 59 60 addToPropertyMap(m_startOffset); 61 addToPropertyMap(m_method); 62 addToPropertyMap(m_spacing); 63 } 64 65 DEFINE_NODE_FACTORY(SVGTextPathElement) 66 67 SVGTextPathElement::~SVGTextPathElement() 68 { 69 #if !ENABLE(OILPAN) 70 clearResourceReferences(); 71 #endif 72 } 73 74 void SVGTextPathElement::clearResourceReferences() 75 { 76 document().accessSVGExtensions().removeAllTargetReferencesForElement(this); 77 } 78 79 bool SVGTextPathElement::isSupportedAttribute(const QualifiedName& attrName) 80 { 81 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ()); 82 if (supportedAttributes.isEmpty()) { 83 SVGURIReference::addSupportedAttributes(supportedAttributes); 84 supportedAttributes.add(SVGNames::startOffsetAttr); 85 supportedAttributes.add(SVGNames::methodAttr); 86 supportedAttributes.add(SVGNames::spacingAttr); 87 } 88 return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName); 89 } 90 91 void SVGTextPathElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 92 { 93 SVGParsingError parseError = NoError; 94 95 if (!isSupportedAttribute(name)) 96 SVGTextContentElement::parseAttribute(name, value); 97 else if (name == SVGNames::startOffsetAttr) 98 m_startOffset->setBaseValueAsString(value, parseError); 99 else if (name == SVGNames::methodAttr) 100 m_method->setBaseValueAsString(value, parseError); 101 else if (name == SVGNames::spacingAttr) 102 m_spacing->setBaseValueAsString(value, parseError); 103 else if (SVGURIReference::parseAttribute(name, value, parseError)) { 104 } else 105 ASSERT_NOT_REACHED(); 106 107 reportAttributeParsingError(parseError, name, value); 108 } 109 110 void SVGTextPathElement::svgAttributeChanged(const QualifiedName& attrName) 111 { 112 if (!isSupportedAttribute(attrName)) { 113 SVGTextContentElement::svgAttributeChanged(attrName); 114 return; 115 } 116 117 SVGElement::InvalidationGuard invalidationGuard(this); 118 119 if (SVGURIReference::isKnownAttribute(attrName)) { 120 buildPendingResource(); 121 return; 122 } 123 124 if (attrName == SVGNames::startOffsetAttr) 125 updateRelativeLengthsInformation(); 126 127 if (RenderObject* object = renderer()) 128 RenderSVGResource::markForLayoutAndParentResourceInvalidation(object); 129 } 130 131 RenderObject* SVGTextPathElement::createRenderer(RenderStyle*) 132 { 133 return new RenderSVGTextPath(this); 134 } 135 136 bool SVGTextPathElement::rendererIsNeeded(const RenderStyle& style) 137 { 138 if (parentNode() && (isSVGAElement(*parentNode()) || isSVGTextElement(*parentNode()))) 139 return Element::rendererIsNeeded(style); 140 141 return false; 142 } 143 144 void SVGTextPathElement::buildPendingResource() 145 { 146 clearResourceReferences(); 147 if (!inDocument()) 148 return; 149 150 AtomicString id; 151 Element* target = SVGURIReference::targetElementFromIRIString(hrefString(), treeScope(), &id); 152 if (!target) { 153 // Do not register as pending if we are already pending this resource. 154 if (document().accessSVGExtensions().isElementPendingResource(this, id)) 155 return; 156 157 if (!id.isEmpty()) { 158 document().accessSVGExtensions().addPendingResource(id, this); 159 ASSERT(hasPendingResources()); 160 } 161 } else if (isSVGPathElement(*target)) { 162 // Register us with the target in the dependencies map. Any change of hrefElement 163 // that leads to relayout/repainting now informs us, so we can react to it. 164 document().accessSVGExtensions().addElementReferencingTarget(this, toSVGElement((target))); 165 } 166 } 167 168 Node::InsertionNotificationRequest SVGTextPathElement::insertedInto(ContainerNode* rootParent) 169 { 170 SVGTextContentElement::insertedInto(rootParent); 171 buildPendingResource(); 172 return InsertionDone; 173 } 174 175 void SVGTextPathElement::removedFrom(ContainerNode* rootParent) 176 { 177 SVGTextContentElement::removedFrom(rootParent); 178 if (rootParent->inDocument()) 179 clearResourceReferences(); 180 } 181 182 bool SVGTextPathElement::selfHasRelativeLengths() const 183 { 184 return m_startOffset->currentValue()->isRelative() 185 || SVGTextContentElement::selfHasRelativeLengths(); 186 } 187 188 } 189