Have Document::accessSVGExtensions() return a reference
[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 #include <wtf/NeverDestroyed.h>
40
41 namespace WebCore {
42
43 // Animated property definitions
44 DEFINE_ANIMATED_STRING(SVGTRefElement, XLinkNames::hrefAttr, Href, href)
45
46 BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGTRefElement)
47     REGISTER_LOCAL_ANIMATED_PROPERTY(href)
48     REGISTER_PARENT_ANIMATED_PROPERTIES(SVGTextPositioningElement)
49 END_REGISTER_ANIMATED_PROPERTIES
50
51 PassRefPtr<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
58 class SVGTRefTargetEventListener : public EventListener {
59 public:
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
75 private:
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
85 SVGTRefTargetEventListener::SVGTRefTargetEventListener(SVGTRefElement& trefElement)
86     : EventListener(SVGTRefTargetEventListenerType)
87     , m_trefElement(trefElement)
88     , m_target(0)
89 {
90 }
91
92 void 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
103 void 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
113 bool 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
120 void 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
130 inline 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
138 SVGTRefElement::~SVGTRefElement()
139 {
140     m_targetListener->detach();
141 }
142
143 void 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
159 void 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
181 bool 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
189 void 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
202 void 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
221 RenderPtr<RenderElement> SVGTRefElement::createElementRenderer(PassRef<RenderStyle> style)
222 {
223     return createRenderer<RenderSVGInline>(*this, WTF::move(style));
224 }
225
226 bool SVGTRefElement::childShouldCreateRenderer(const Node& child) const
227 {
228     return child.isInShadowTree();
229 }
230
231 bool 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
246 void SVGTRefElement::clearTarget()
247 {
248     m_targetListener->detach();
249 }
250
251 void 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
281 Node::InsertionNotificationRequest SVGTRefElement::insertedInto(ContainerNode& rootParent)
282 {
283     SVGElement::insertedInto(rootParent);
284     if (rootParent.inDocument())
285         return InsertionShouldCallDidNotifySubtreeInsertions;
286     return InsertionDone;
287 }
288
289 void SVGTRefElement::didNotifySubtreeInsertions(ContainerNode*)
290 {
291     buildPendingResource();
292 }
293
294 void SVGTRefElement::removedFrom(ContainerNode& rootParent)
295 {
296     SVGElement::removedFrom(rootParent);
297     if (rootParent.inDocument())
298         m_targetListener->detach();
299 }
300
301 }