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