1/* 2 * Copyright (C) 2004, 2005 Nikolas Zimmermann <zimmermann@kde.org> 3 * Copyright (C) 2004, 2005, 2006 Rob Buis <buis@kde.org> 4 * Copyright (C) Research In Motion Limited 2011. 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 "SVGTRefElement.h" 24 25#include "EventListener.h" 26#include "EventNames.h" 27#include "ExceptionCodePlaceholder.h" 28#include "MutationEvent.h" 29#include "RenderSVGInline.h" 30#include "RenderSVGInlineText.h" 31#include "RenderSVGResource.h" 32#include "ShadowRoot.h" 33#include "SVGDocument.h" 34#include "SVGElementInstance.h" 35#include "SVGNames.h" 36#include "StyleInheritedData.h" 37#include "Text.h" 38#include "XLinkNames.h" 39#include <wtf/NeverDestroyed.h> 40 41namespace WebCore { 42 43// Animated property definitions 44DEFINE_ANIMATED_STRING(SVGTRefElement, XLinkNames::hrefAttr, Href, href) 45 46BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGTRefElement) 47 REGISTER_LOCAL_ANIMATED_PROPERTY(href) 48 REGISTER_PARENT_ANIMATED_PROPERTIES(SVGTextPositioningElement) 49END_REGISTER_ANIMATED_PROPERTIES 50 51PassRefPtr<SVGTRefElement> SVGTRefElement::create(const QualifiedName& tagName, Document& document) 52{ 53 RefPtr<SVGTRefElement> element = adoptRef(new SVGTRefElement(tagName, document)); 54 element->ensureUserAgentShadowRoot(); 55 return element.release(); 56} 57 58class SVGTRefTargetEventListener : public EventListener { 59public: 60 static PassRef<SVGTRefTargetEventListener> create(SVGTRefElement& trefElement) 61 { 62 return adoptRef(*new SVGTRefTargetEventListener(trefElement)); 63 } 64 65 static const SVGTRefTargetEventListener* cast(const EventListener* listener) 66 { 67 return listener->type() == SVGTRefTargetEventListenerType 68 ? static_cast<const SVGTRefTargetEventListener*>(listener) : 0; 69 } 70 71 void attach(PassRefPtr<Element> target); 72 void detach(); 73 bool isAttached() const { return m_target.get(); } 74 75private: 76 explicit SVGTRefTargetEventListener(SVGTRefElement& trefElement); 77 78 virtual void handleEvent(ScriptExecutionContext*, Event*) override; 79 virtual bool operator==(const EventListener&) override; 80 81 SVGTRefElement& m_trefElement; 82 RefPtr<Element> m_target; 83}; 84 85SVGTRefTargetEventListener::SVGTRefTargetEventListener(SVGTRefElement& trefElement) 86 : EventListener(SVGTRefTargetEventListenerType) 87 , m_trefElement(trefElement) 88 , m_target(0) 89{ 90} 91 92void SVGTRefTargetEventListener::attach(PassRefPtr<Element> target) 93{ 94 ASSERT(!isAttached()); 95 ASSERT(target.get()); 96 ASSERT(target->inDocument()); 97 98 target->addEventListener(eventNames().DOMSubtreeModifiedEvent, this, false); 99 target->addEventListener(eventNames().DOMNodeRemovedFromDocumentEvent, this, false); 100 m_target = target; 101} 102 103void SVGTRefTargetEventListener::detach() 104{ 105 if (!isAttached()) 106 return; 107 108 m_target->removeEventListener(eventNames().DOMSubtreeModifiedEvent, this, false); 109 m_target->removeEventListener(eventNames().DOMNodeRemovedFromDocumentEvent, this, false); 110 m_target.clear(); 111} 112 113bool SVGTRefTargetEventListener::operator==(const EventListener& listener) 114{ 115 if (const SVGTRefTargetEventListener* targetListener = SVGTRefTargetEventListener::cast(&listener)) 116 return &m_trefElement == &targetListener->m_trefElement; 117 return false; 118} 119 120void SVGTRefTargetEventListener::handleEvent(ScriptExecutionContext*, Event* event) 121{ 122 ASSERT(isAttached()); 123 124 if (event->type() == eventNames().DOMSubtreeModifiedEvent && &m_trefElement != event->target()) 125 m_trefElement.updateReferencedText(m_target.get()); 126 else if (event->type() == eventNames().DOMNodeRemovedFromDocumentEvent) 127 m_trefElement.detachTarget(); 128} 129 130inline SVGTRefElement::SVGTRefElement(const QualifiedName& tagName, Document& document) 131 : SVGTextPositioningElement(tagName, document) 132 , m_targetListener(SVGTRefTargetEventListener::create(*this)) 133{ 134 ASSERT(hasTagName(SVGNames::trefTag)); 135 registerAnimatedPropertiesForSVGTRefElement(); 136} 137 138SVGTRefElement::~SVGTRefElement() 139{ 140 m_targetListener->detach(); 141} 142 143void SVGTRefElement::updateReferencedText(Element* target) 144{ 145 String textContent; 146 if (target) 147 textContent = target->textContent(); 148 149 ASSERT(shadowRoot()); 150 ShadowRoot* root = shadowRoot(); 151 if (!root->firstChild()) 152 root->appendChild(Text::create(document(), textContent), ASSERT_NO_EXCEPTION); 153 else { 154 ASSERT(root->firstChild()->isTextNode()); 155 root->firstChild()->setTextContent(textContent, ASSERT_NO_EXCEPTION); 156 } 157} 158 159void SVGTRefElement::detachTarget() 160{ 161 // Remove active listeners and clear the text content. 162 m_targetListener->detach(); 163 164 String emptyContent; 165 166 ASSERT(shadowRoot()); 167 Node* container = shadowRoot()->firstChild(); 168 if (container) 169 container->setTextContent(emptyContent, IGNORE_EXCEPTION); 170 171 if (!inDocument()) 172 return; 173 174 // Mark the referenced ID as pending. 175 String id; 176 SVGURIReference::targetElementFromIRIString(href(), document(), &id); 177 if (!id.isEmpty()) 178 document().accessSVGExtensions()->addPendingResource(id, this); 179} 180 181bool SVGTRefElement::isSupportedAttribute(const QualifiedName& attrName) 182{ 183 static NeverDestroyed<HashSet<QualifiedName>> supportedAttributes; 184 if (supportedAttributes.get().isEmpty()) 185 SVGURIReference::addSupportedAttributes(supportedAttributes); 186 return supportedAttributes.get().contains<SVGAttributeHashTranslator>(attrName); 187} 188 189void SVGTRefElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 190{ 191 if (!isSupportedAttribute(name)) { 192 SVGTextPositioningElement::parseAttribute(name, value); 193 return; 194 } 195 196 if (SVGURIReference::parseAttribute(name, value)) 197 return; 198 199 ASSERT_NOT_REACHED(); 200} 201 202void SVGTRefElement::svgAttributeChanged(const QualifiedName& attrName) 203{ 204 if (!isSupportedAttribute(attrName)) { 205 SVGTextPositioningElement::svgAttributeChanged(attrName); 206 return; 207 } 208 209 SVGElementInstance::InvalidationGuard invalidationGuard(this); 210 211 if (SVGURIReference::isKnownAttribute(attrName)) { 212 buildPendingResource(); 213 if (auto renderer = this->renderer()) 214 RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer); 215 return; 216 } 217 218 ASSERT_NOT_REACHED(); 219} 220 221RenderPtr<RenderElement> SVGTRefElement::createElementRenderer(PassRef<RenderStyle> style) 222{ 223 return createRenderer<RenderSVGInline>(*this, WTF::move(style)); 224} 225 226bool SVGTRefElement::childShouldCreateRenderer(const Node& child) const 227{ 228 return child.isInShadowTree(); 229} 230 231bool SVGTRefElement::rendererIsNeeded(const RenderStyle& style) 232{ 233 if (parentNode() 234 && (parentNode()->hasTagName(SVGNames::aTag) 235#if ENABLE(SVG_FONTS) 236 || parentNode()->hasTagName(SVGNames::altGlyphTag) 237#endif 238 || parentNode()->hasTagName(SVGNames::textTag) 239 || parentNode()->hasTagName(SVGNames::textPathTag) 240 || parentNode()->hasTagName(SVGNames::tspanTag))) 241 return StyledElement::rendererIsNeeded(style); 242 243 return false; 244} 245 246void SVGTRefElement::clearTarget() 247{ 248 m_targetListener->detach(); 249} 250 251void SVGTRefElement::buildPendingResource() 252{ 253 // Remove any existing event listener. 254 m_targetListener->detach(); 255 256 // If we're not yet in a document, this function will be called again from insertedInto(). 257 if (!inDocument()) 258 return; 259 260 String id; 261 RefPtr<Element> target = SVGURIReference::targetElementFromIRIString(href(), document(), &id); 262 if (!target.get()) { 263 if (id.isEmpty()) 264 return; 265 266 document().accessSVGExtensions()->addPendingResource(id, this); 267 ASSERT(hasPendingResources()); 268 return; 269 } 270 271 // Don't set up event listeners if this is a shadow tree node. 272 // SVGUseElement::transferEventListenersToShadowTree() handles this task, and addEventListener() 273 // expects every element instance to have an associated shadow tree element - which is not the 274 // case when we land here from SVGUseElement::buildShadowTree(). 275 if (!isInShadowTree()) 276 m_targetListener->attach(target); 277 278 updateReferencedText(target.get()); 279} 280 281Node::InsertionNotificationRequest SVGTRefElement::insertedInto(ContainerNode& rootParent) 282{ 283 SVGElement::insertedInto(rootParent); 284 if (rootParent.inDocument()) 285 return InsertionShouldCallDidNotifySubtreeInsertions; 286 return InsertionDone; 287} 288 289void SVGTRefElement::didNotifySubtreeInsertions(ContainerNode*) 290{ 291 buildPendingResource(); 292} 293 294void SVGTRefElement::removedFrom(ContainerNode& rootParent) 295{ 296 SVGElement::removedFrom(rootParent); 297 if (rootParent.inDocument()) 298 m_targetListener->detach(); 299} 300 301} 302