02f0de1b34b689918cb9ce028d94fd7bef0fe814
[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 PassRefPtr<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     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     ASSERT(m_trefElement);
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     DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
184     if (supportedAttributes.isEmpty())
185         SVGURIReference::addSupportedAttributes(supportedAttributes);
186     return supportedAttributes.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, std::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::buildPendingResource()
247 {
248     // Remove any existing event listener.
249     m_targetListener->detach();
250
251     // If we're not yet in a document, this function will be called again from insertedInto().
252     if (!inDocument())
253         return;
254
255     String id;
256     RefPtr<Element> target = SVGURIReference::targetElementFromIRIString(href(), document(), &id);
257     if (!target.get()) {
258         if (id.isEmpty())
259             return;
260
261         document().accessSVGExtensions()->addPendingResource(id, this);
262         ASSERT(hasPendingResources());
263         return;
264     }
265
266     // Don't set up event listeners if this is a shadow tree node.
267     // SVGUseElement::transferEventListenersToShadowTree() handles this task, and addEventListener()
268     // expects every element instance to have an associated shadow tree element - which is not the
269     // case when we land here from SVGUseElement::buildShadowTree().
270     if (!isInShadowTree())
271         m_targetListener->attach(target);
272
273     updateReferencedText(target.get());
274 }
275
276 Node::InsertionNotificationRequest SVGTRefElement::insertedInto(ContainerNode& rootParent)
277 {
278     SVGElement::insertedInto(rootParent);
279     if (rootParent.inDocument())
280         buildPendingResource();
281     return InsertionDone;
282 }
283
284 void SVGTRefElement::removedFrom(ContainerNode& rootParent)
285 {
286     SVGElement::removedFrom(rootParent);
287     if (rootParent.inDocument())
288         m_targetListener->detach();
289 }
290
291 }