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 * Copyright (C) 2018 Apple Inc. All rights reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23#include "config.h"
24#include "SVGTRefElement.h"
25
26#include "EventListener.h"
27#include "EventNames.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 "SVGDocumentExtensions.h"
35#include "SVGNames.h"
36#include "ScriptDisallowedScope.h"
37#include "StyleInheritedData.h"
38#include "Text.h"
39#include <wtf/IsoMallocInlines.h>
40
41namespace WebCore {
42
43WTF_MAKE_ISO_ALLOCATED_IMPL(SVGTRefElement);
44
45Ref<SVGTRefElement> SVGTRefElement::create(const QualifiedName& tagName, Document& document)
46{
47 Ref<SVGTRefElement> element = adoptRef(*new SVGTRefElement(tagName, document));
48 element->ensureUserAgentShadowRoot();
49 return element;
50}
51
52class SVGTRefTargetEventListener final : public EventListener {
53public:
54 static Ref<SVGTRefTargetEventListener> create(SVGTRefElement& trefElement)
55 {
56 return adoptRef(*new SVGTRefTargetEventListener(trefElement));
57 }
58
59 static const SVGTRefTargetEventListener* cast(const EventListener* listener)
60 {
61 return listener->type() == SVGTRefTargetEventListenerType ? static_cast<const SVGTRefTargetEventListener*>(listener) : nullptr;
62 }
63
64 void attach(RefPtr<Element>&& target);
65 void detach();
66 bool isAttached() const { return m_target.get(); }
67
68private:
69 explicit SVGTRefTargetEventListener(SVGTRefElement& trefElement);
70
71 void handleEvent(ScriptExecutionContext&, Event&) final;
72 bool operator==(const EventListener&) const final;
73
74 SVGTRefElement& m_trefElement;
75 RefPtr<Element> m_target;
76};
77
78SVGTRefTargetEventListener::SVGTRefTargetEventListener(SVGTRefElement& trefElement)
79 : EventListener(SVGTRefTargetEventListenerType)
80 , m_trefElement(trefElement)
81 , m_target(nullptr)
82{
83}
84
85void SVGTRefTargetEventListener::attach(RefPtr<Element>&& target)
86{
87 ASSERT(!isAttached());
88 ASSERT(target.get());
89 ASSERT(target->isConnected());
90
91 target->addEventListener(eventNames().DOMSubtreeModifiedEvent, *this, false);
92 target->addEventListener(eventNames().DOMNodeRemovedFromDocumentEvent, *this, false);
93 m_target = WTFMove(target);
94}
95
96void SVGTRefTargetEventListener::detach()
97{
98 if (!isAttached())
99 return;
100
101 m_target->removeEventListener(eventNames().DOMSubtreeModifiedEvent, *this, false);
102 m_target->removeEventListener(eventNames().DOMNodeRemovedFromDocumentEvent, *this, false);
103 m_target = nullptr;
104}
105
106bool SVGTRefTargetEventListener::operator==(const EventListener& listener) const
107{
108 if (const SVGTRefTargetEventListener* targetListener = SVGTRefTargetEventListener::cast(&listener))
109 return &m_trefElement == &targetListener->m_trefElement;
110 return false;
111}
112
113void SVGTRefTargetEventListener::handleEvent(ScriptExecutionContext&, Event& event)
114{
115 if (!isAttached())
116 return;
117
118 if (event.type() == eventNames().DOMSubtreeModifiedEvent && &m_trefElement != event.target())
119 m_trefElement.updateReferencedText(m_target.get());
120 else if (event.type() == eventNames().DOMNodeRemovedFromDocumentEvent)
121 m_trefElement.detachTarget();
122}
123
124inline SVGTRefElement::SVGTRefElement(const QualifiedName& tagName, Document& document)
125 : SVGTextPositioningElement(tagName, document)
126 , SVGURIReference(this)
127 , m_targetListener(SVGTRefTargetEventListener::create(*this))
128{
129 ASSERT(hasTagName(SVGNames::trefTag));
130}
131
132SVGTRefElement::~SVGTRefElement()
133{
134 m_targetListener->detach();
135}
136
137void SVGTRefElement::updateReferencedText(Element* target)
138{
139 String textContent;
140 if (target)
141 textContent = target->textContent();
142
143 auto root = userAgentShadowRoot();
144 ASSERT(root);
145 ScriptDisallowedScope::EventAllowedScope allowedScope(*root);
146 if (!root->firstChild())
147 root->appendChild(Text::create(document(), textContent));
148 else {
149 ASSERT(root->firstChild()->isTextNode());
150 root->firstChild()->setTextContent(textContent);
151 }
152}
153
154void SVGTRefElement::detachTarget()
155{
156 // Remove active listeners and clear the text content.
157 m_targetListener->detach();
158
159 String emptyContent;
160
161 ASSERT(shadowRoot());
162 auto container = makeRefPtr(shadowRoot()->firstChild());
163 if (container)
164 container->setTextContent(emptyContent);
165
166 if (!isConnected())
167 return;
168
169 // Mark the referenced ID as pending.
170 auto target = SVGURIReference::targetElementFromIRIString(href(), document());
171 if (!target.identifier.isEmpty())
172 document().accessSVGExtensions().addPendingResource(target.identifier, *this);
173}
174
175void SVGTRefElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
176{
177 SVGTextPositioningElement::parseAttribute(name, value);
178 SVGURIReference::parseAttribute(name, value);
179}
180
181void SVGTRefElement::svgAttributeChanged(const QualifiedName& attrName)
182{
183 if (SVGURIReference::isKnownAttribute(attrName)) {
184 InstanceInvalidationGuard guard(*this);
185 buildPendingResource();
186 if (auto renderer = this->renderer())
187 RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer);
188 return;
189 }
190
191 SVGTextPositioningElement::svgAttributeChanged(attrName);
192}
193
194RenderPtr<RenderElement> SVGTRefElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition&)
195{
196 return createRenderer<RenderSVGInline>(*this, WTFMove(style));
197}
198
199bool SVGTRefElement::childShouldCreateRenderer(const Node& child) const
200{
201 return child.isInShadowTree();
202}
203
204bool SVGTRefElement::rendererIsNeeded(const RenderStyle& style)
205{
206 if (parentNode()
207 && (parentNode()->hasTagName(SVGNames::aTag)
208#if ENABLE(SVG_FONTS)
209 || parentNode()->hasTagName(SVGNames::altGlyphTag)
210#endif
211 || parentNode()->hasTagName(SVGNames::textTag)
212 || parentNode()->hasTagName(SVGNames::textPathTag)
213 || parentNode()->hasTagName(SVGNames::tspanTag)))
214 return StyledElement::rendererIsNeeded(style);
215
216 return false;
217}
218
219void SVGTRefElement::clearTarget()
220{
221 m_targetListener->detach();
222}
223
224void SVGTRefElement::buildPendingResource()
225{
226 // Remove any existing event listener.
227 m_targetListener->detach();
228
229 // If we're not yet in a document, this function will be called again from insertedIntoAncestor().
230 if (!isConnected())
231 return;
232
233 auto target = SVGURIReference::targetElementFromIRIString(href(), treeScope());
234 if (!target.element) {
235 if (target.identifier.isEmpty())
236 return;
237
238 document().accessSVGExtensions().addPendingResource(target.identifier, *this);
239 ASSERT(hasPendingResources());
240 return;
241 }
242
243 // Don't set up event listeners if this is a shadow tree node.
244 // SVGUseElement::transferEventListenersToShadowTree() handles this task, and addEventListener()
245 // expects every element instance to have an associated shadow tree element - which is not the
246 // case when we land here from SVGUseElement::buildShadowTree().
247 if (!isInShadowTree())
248 m_targetListener->attach(target.element.copyRef());
249
250 updateReferencedText(target.element.get());
251}
252
253Node::InsertedIntoAncestorResult SVGTRefElement::insertedIntoAncestor(InsertionType insertionType, ContainerNode& parentOfInsertedTree)
254{
255 SVGElement::insertedIntoAncestor(insertionType, parentOfInsertedTree);
256 if (insertionType.connectedToDocument)
257 return InsertedIntoAncestorResult::NeedsPostInsertionCallback;
258 return InsertedIntoAncestorResult::Done;
259}
260
261void SVGTRefElement::didFinishInsertingNode()
262{
263 buildPendingResource();
264}
265
266void SVGTRefElement::removedFromAncestor(RemovalType removalType, ContainerNode& oldParentOfRemovedTree)
267{
268 SVGElement::removedFromAncestor(removalType, oldParentOfRemovedTree);
269 if (removalType.disconnectedFromDocument)
270 m_targetListener->detach();
271}
272
273}
274