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