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.
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.
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.
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.
23 #include "SVGTRefElement.h"
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"
36 #include "StyleInheritedData.h"
38 #include "XLinkNames.h"
39 #include <wtf/NeverDestroyed.h>
43 // Animated property definitions
44 DEFINE_ANIMATED_STRING(SVGTRefElement, XLinkNames::hrefAttr, Href, href)
46 BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGTRefElement)
47 REGISTER_LOCAL_ANIMATED_PROPERTY(href)
48 REGISTER_PARENT_ANIMATED_PROPERTIES(SVGTextPositioningElement)
49 END_REGISTER_ANIMATED_PROPERTIES
51 PassRefPtr<SVGTRefElement> SVGTRefElement::create(const QualifiedName& tagName, Document& document)
53 RefPtr<SVGTRefElement> element = adoptRef(new SVGTRefElement(tagName, document));
54 element->ensureUserAgentShadowRoot();
55 return element.release();
58 class SVGTRefTargetEventListener : public EventListener {
60 static PassRef<SVGTRefTargetEventListener> create(SVGTRefElement& trefElement)
62 return adoptRef(*new SVGTRefTargetEventListener(trefElement));
65 static const SVGTRefTargetEventListener* cast(const EventListener* listener)
67 return listener->type() == SVGTRefTargetEventListenerType
68 ? static_cast<const SVGTRefTargetEventListener*>(listener) : 0;
71 void attach(PassRefPtr<Element> target);
73 bool isAttached() const { return m_target.get(); }
76 explicit SVGTRefTargetEventListener(SVGTRefElement& trefElement);
78 virtual void handleEvent(ScriptExecutionContext*, Event*) override;
79 virtual bool operator==(const EventListener&) override;
81 SVGTRefElement& m_trefElement;
82 RefPtr<Element> m_target;
85 SVGTRefTargetEventListener::SVGTRefTargetEventListener(SVGTRefElement& trefElement)
86 : EventListener(SVGTRefTargetEventListenerType)
87 , m_trefElement(trefElement)
92 void SVGTRefTargetEventListener::attach(PassRefPtr<Element> target)
94 ASSERT(!isAttached());
96 ASSERT(target->inDocument());
98 target->addEventListener(eventNames().DOMSubtreeModifiedEvent, this, false);
99 target->addEventListener(eventNames().DOMNodeRemovedFromDocumentEvent, this, false);
103 void SVGTRefTargetEventListener::detach()
108 m_target->removeEventListener(eventNames().DOMSubtreeModifiedEvent, this, false);
109 m_target->removeEventListener(eventNames().DOMNodeRemovedFromDocumentEvent, this, false);
113 bool SVGTRefTargetEventListener::operator==(const EventListener& listener)
115 if (const SVGTRefTargetEventListener* targetListener = SVGTRefTargetEventListener::cast(&listener))
116 return &m_trefElement == &targetListener->m_trefElement;
120 void SVGTRefTargetEventListener::handleEvent(ScriptExecutionContext*, Event* event)
122 ASSERT(isAttached());
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();
130 inline SVGTRefElement::SVGTRefElement(const QualifiedName& tagName, Document& document)
131 : SVGTextPositioningElement(tagName, document)
132 , m_targetListener(SVGTRefTargetEventListener::create(*this))
134 ASSERT(hasTagName(SVGNames::trefTag));
135 registerAnimatedPropertiesForSVGTRefElement();
138 SVGTRefElement::~SVGTRefElement()
140 m_targetListener->detach();
143 void SVGTRefElement::updateReferencedText(Element* target)
147 textContent = target->textContent();
149 ASSERT(shadowRoot());
150 ShadowRoot* root = shadowRoot();
151 if (!root->firstChild())
152 root->appendChild(Text::create(document(), textContent), ASSERT_NO_EXCEPTION);
154 ASSERT(root->firstChild()->isTextNode());
155 root->firstChild()->setTextContent(textContent, ASSERT_NO_EXCEPTION);
159 void SVGTRefElement::detachTarget()
161 // Remove active listeners and clear the text content.
162 m_targetListener->detach();
166 ASSERT(shadowRoot());
167 Node* container = shadowRoot()->firstChild();
169 container->setTextContent(emptyContent, IGNORE_EXCEPTION);
174 // Mark the referenced ID as pending.
176 SVGURIReference::targetElementFromIRIString(href(), document(), &id);
178 document().accessSVGExtensions().addPendingResource(id, this);
181 bool SVGTRefElement::isSupportedAttribute(const QualifiedName& attrName)
183 static NeverDestroyed<HashSet<QualifiedName>> supportedAttributes;
184 if (supportedAttributes.get().isEmpty())
185 SVGURIReference::addSupportedAttributes(supportedAttributes);
186 return supportedAttributes.get().contains<SVGAttributeHashTranslator>(attrName);
189 void SVGTRefElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
191 if (!isSupportedAttribute(name)) {
192 SVGTextPositioningElement::parseAttribute(name, value);
196 if (SVGURIReference::parseAttribute(name, value))
199 ASSERT_NOT_REACHED();
202 void SVGTRefElement::svgAttributeChanged(const QualifiedName& attrName)
204 if (!isSupportedAttribute(attrName)) {
205 SVGTextPositioningElement::svgAttributeChanged(attrName);
209 SVGElementInstance::InvalidationGuard invalidationGuard(this);
211 if (SVGURIReference::isKnownAttribute(attrName)) {
212 buildPendingResource();
213 if (auto renderer = this->renderer())
214 RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer);
218 ASSERT_NOT_REACHED();
221 RenderPtr<RenderElement> SVGTRefElement::createElementRenderer(PassRef<RenderStyle> style)
223 return createRenderer<RenderSVGInline>(*this, WTF::move(style));
226 bool SVGTRefElement::childShouldCreateRenderer(const Node& child) const
228 return child.isInShadowTree();
231 bool SVGTRefElement::rendererIsNeeded(const RenderStyle& style)
234 && (parentNode()->hasTagName(SVGNames::aTag)
235 #if ENABLE(SVG_FONTS)
236 || parentNode()->hasTagName(SVGNames::altGlyphTag)
238 || parentNode()->hasTagName(SVGNames::textTag)
239 || parentNode()->hasTagName(SVGNames::textPathTag)
240 || parentNode()->hasTagName(SVGNames::tspanTag)))
241 return StyledElement::rendererIsNeeded(style);
246 void SVGTRefElement::clearTarget()
248 m_targetListener->detach();
251 void SVGTRefElement::buildPendingResource()
253 // Remove any existing event listener.
254 m_targetListener->detach();
256 // If we're not yet in a document, this function will be called again from insertedInto().
261 RefPtr<Element> target = SVGURIReference::targetElementFromIRIString(href(), document(), &id);
266 document().accessSVGExtensions().addPendingResource(id, this);
267 ASSERT(hasPendingResources());
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);
278 updateReferencedText(target.get());
281 Node::InsertionNotificationRequest SVGTRefElement::insertedInto(ContainerNode& rootParent)
283 SVGElement::insertedInto(rootParent);
284 if (rootParent.inDocument())
285 return InsertionShouldCallDidNotifySubtreeInsertions;
286 return InsertionDone;
289 void SVGTRefElement::didNotifySubtreeInsertions(ContainerNode*)
291 buildPendingResource();
294 void SVGTRefElement::removedFrom(ContainerNode& rootParent)
296 SVGElement::removedFrom(rootParent);
297 if (rootParent.inDocument())
298 m_targetListener->detach();