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