1/*
2 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2004, 2005, 2006, 2008 Rob Buis <buis@kde.org>
4 * Copyright (C) 2008-2019 Apple Inc. All rights reserved.
5 * Copyright (C) 2008 Alp Toker <alp@atoker.com>
6 * Copyright (C) 2009 Cameron McCormack <cam@mcc.id.au>
7 * Copyright (C) 2013 Samsung Electronics. All rights reserved.
8 * Copyright (C) 2014 Adobe Systems Incorporated. All rights reserved.
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
19 *
20 * You should have received a copy of the GNU Library General Public License
21 * along with this library; see the file COPYING.LIB. If not, write to
22 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
24 */
25
26#include "config.h"
27#include "SVGElement.h"
28
29#include "CSSPropertyParser.h"
30#include "DeprecatedCSSOMValue.h"
31#include "Document.h"
32#include "ElementIterator.h"
33#include "Event.h"
34#include "EventNames.h"
35#include "HTMLElement.h"
36#include "HTMLNames.h"
37#include "HTMLParserIdioms.h"
38#include "RenderObject.h"
39#include "RenderSVGResource.h"
40#include "RenderSVGResourceFilter.h"
41#include "RenderSVGResourceMasker.h"
42#include "SVGDocumentExtensions.h"
43#include "SVGElementRareData.h"
44#include "SVGGraphicsElement.h"
45#include "SVGImageElement.h"
46#include "SVGNames.h"
47#include "SVGPropertyAnimatorFactory.h"
48#include "SVGRenderStyle.h"
49#include "SVGRenderSupport.h"
50#include "SVGSVGElement.h"
51#include "SVGTitleElement.h"
52#include "SVGUseElement.h"
53#include "ShadowRoot.h"
54#include "XMLNames.h"
55#include <wtf/Assertions.h>
56#include <wtf/HashMap.h>
57#include <wtf/IsoMallocInlines.h>
58#include <wtf/NeverDestroyed.h>
59#include <wtf/StdLibExtras.h>
60#include <wtf/text/WTFString.h>
61
62
63namespace WebCore {
64
65WTF_MAKE_ISO_ALLOCATED_IMPL(SVGElement);
66
67static NEVER_INLINE HashMap<AtomicStringImpl*, CSSPropertyID> createAttributeNameToCSSPropertyIDMap()
68{
69 using namespace HTMLNames;
70 using namespace SVGNames;
71
72 // This list should include all base CSS and SVG CSS properties which are exposed as SVG XML attributes.
73 static const QualifiedName* const attributeNames[] = {
74 &alignment_baselineAttr.get(),
75 &baseline_shiftAttr.get(),
76 &buffered_renderingAttr.get(),
77 &clipAttr.get(),
78 &clip_pathAttr.get(),
79 &clip_ruleAttr.get(),
80 &SVGNames::colorAttr.get(),
81 &color_interpolationAttr.get(),
82 &color_interpolation_filtersAttr.get(),
83 &color_profileAttr.get(),
84 &color_renderingAttr.get(),
85 &cursorAttr.get(),
86 &cxAttr.get(),
87 &cyAttr.get(),
88 &SVGNames::directionAttr.get(),
89 &displayAttr.get(),
90 &dominant_baselineAttr.get(),
91 &enable_backgroundAttr.get(),
92 &fillAttr.get(),
93 &fill_opacityAttr.get(),
94 &fill_ruleAttr.get(),
95 &filterAttr.get(),
96 &flood_colorAttr.get(),
97 &flood_opacityAttr.get(),
98 &font_familyAttr.get(),
99 &font_sizeAttr.get(),
100 &font_stretchAttr.get(),
101 &font_styleAttr.get(),
102 &font_variantAttr.get(),
103 &font_weightAttr.get(),
104 &glyph_orientation_horizontalAttr.get(),
105 &glyph_orientation_verticalAttr.get(),
106 &image_renderingAttr.get(),
107 &SVGNames::heightAttr.get(),
108 &kerningAttr.get(),
109 &letter_spacingAttr.get(),
110 &lighting_colorAttr.get(),
111 &marker_endAttr.get(),
112 &marker_midAttr.get(),
113 &marker_startAttr.get(),
114 &maskAttr.get(),
115 &mask_typeAttr.get(),
116 &opacityAttr.get(),
117 &overflowAttr.get(),
118 &paint_orderAttr.get(),
119 &pointer_eventsAttr.get(),
120 &rAttr.get(),
121 &rxAttr.get(),
122 &ryAttr.get(),
123 &shape_renderingAttr.get(),
124 &stop_colorAttr.get(),
125 &stop_opacityAttr.get(),
126 &strokeAttr.get(),
127 &stroke_dasharrayAttr.get(),
128 &stroke_dashoffsetAttr.get(),
129 &stroke_linecapAttr.get(),
130 &stroke_linejoinAttr.get(),
131 &stroke_miterlimitAttr.get(),
132 &stroke_opacityAttr.get(),
133 &stroke_widthAttr.get(),
134 &text_anchorAttr.get(),
135 &text_decorationAttr.get(),
136 &text_renderingAttr.get(),
137 &unicode_bidiAttr.get(),
138 &vector_effectAttr.get(),
139 &visibilityAttr.get(),
140 &SVGNames::widthAttr.get(),
141 &word_spacingAttr.get(),
142 &writing_modeAttr.get(),
143 &xAttr.get(),
144 &yAttr.get(),
145 };
146
147 HashMap<AtomicStringImpl*, CSSPropertyID> map;
148
149 for (auto& name : attributeNames) {
150 const AtomicString& localName = name->localName();
151 map.add(localName.impl(), cssPropertyID(localName));
152 }
153
154 // FIXME: When CSS supports "transform-origin" this special case can be removed,
155 // and we can add transform_originAttr to the table above instead.
156 map.add(transform_originAttr->localName().impl(), CSSPropertyTransformOrigin);
157
158 return map;
159}
160
161SVGElement::SVGElement(const QualifiedName& tagName, Document& document)
162 : StyledElement(tagName, document, CreateSVGElement)
163 , SVGLangSpace(this)
164 , m_propertyAnimatorFactory(std::make_unique<SVGPropertyAnimatorFactory>())
165{
166 static std::once_flag onceFlag;
167 std::call_once(onceFlag, [] {
168 PropertyRegistry::registerProperty<HTMLNames::classAttr, &SVGElement::m_className>();
169 });
170}
171
172SVGElement::~SVGElement()
173{
174 if (m_svgRareData) {
175 for (SVGElement* instance : m_svgRareData->instances())
176 instance->m_svgRareData->setCorrespondingElement(nullptr);
177 if (auto correspondingElement = makeRefPtr(m_svgRareData->correspondingElement()))
178 correspondingElement->m_svgRareData->instances().remove(this);
179
180 m_svgRareData = nullptr;
181 }
182 document().accessSVGExtensions().rebuildAllElementReferencesForTarget(*this);
183 document().accessSVGExtensions().removeAllElementReferencesForTarget(*this);
184}
185
186int SVGElement::tabIndex() const
187{
188 if (supportsFocus())
189 return Element::tabIndex();
190 return -1;
191}
192
193void SVGElement::willRecalcStyle(Style::Change change)
194{
195 if (!m_svgRareData || styleResolutionShouldRecompositeLayer())
196 return;
197 // If the style changes because of a regular property change (not induced by SMIL animations themselves)
198 // reset the "computed style without SMIL style properties", so the base value change gets reflected.
199 if (change > Style::NoChange || needsStyleRecalc())
200 m_svgRareData->setNeedsOverrideComputedStyleUpdate();
201}
202
203SVGElementRareData& SVGElement::ensureSVGRareData()
204{
205 if (!m_svgRareData)
206 m_svgRareData = std::make_unique<SVGElementRareData>();
207 return *m_svgRareData;
208}
209
210bool SVGElement::isOutermostSVGSVGElement() const
211{
212 if (!is<SVGSVGElement>(*this))
213 return false;
214
215 // If we're living in a shadow tree, we're a <svg> element that got created as replacement
216 // for a <symbol> element or a cloned <svg> element in the referenced tree. In that case
217 // we're always an inner <svg> element.
218 if (isInShadowTree() && parentOrShadowHostElement() && parentOrShadowHostElement()->isSVGElement())
219 return false;
220
221 // Element may not be in the document, pretend we're outermost for viewport(), getCTM(), etc.
222 if (!parentNode())
223 return true;
224
225 // We act like an outermost SVG element, if we're a direct child of a <foreignObject> element.
226 if (parentNode()->hasTagName(SVGNames::foreignObjectTag))
227 return true;
228
229 // This is true whenever this is the outermost SVG, even if there are HTML elements outside it
230 return !parentNode()->isSVGElement();
231}
232
233void SVGElement::reportAttributeParsingError(SVGParsingError error, const QualifiedName& name, const AtomicString& value)
234{
235 if (error == NoError)
236 return;
237
238 String errorString = "<" + tagName() + "> attribute " + name.toString() + "=\"" + value + "\"";
239 SVGDocumentExtensions& extensions = document().accessSVGExtensions();
240
241 if (error == NegativeValueForbiddenError) {
242 extensions.reportError("Invalid negative value for " + errorString);
243 return;
244 }
245
246 if (error == ParsingAttributeFailedError) {
247 extensions.reportError("Invalid value for " + errorString);
248 return;
249 }
250
251 ASSERT_NOT_REACHED();
252}
253
254void SVGElement::removedFromAncestor(RemovalType removalType, ContainerNode& oldParentOfRemovedTree)
255{
256 if (removalType.disconnectedFromDocument)
257 updateRelativeLengthsInformation(false, this);
258
259 StyledElement::removedFromAncestor(removalType, oldParentOfRemovedTree);
260
261 if (removalType.disconnectedFromDocument) {
262 document().accessSVGExtensions().clearTargetDependencies(*this);
263 document().accessSVGExtensions().removeAllElementReferencesForTarget(*this);
264 }
265 invalidateInstances();
266}
267
268SVGSVGElement* SVGElement::ownerSVGElement() const
269{
270 ContainerNode* node = parentOrShadowHostNode();
271 while (node) {
272 if (is<SVGSVGElement>(*node))
273 return downcast<SVGSVGElement>(node);
274
275 node = node->parentOrShadowHostNode();
276 }
277
278 return nullptr;
279}
280
281SVGElement* SVGElement::viewportElement() const
282{
283 // This function needs shadow tree support - as RenderSVGContainer uses this function
284 // to determine the "overflow" property. <use> on <symbol> wouldn't work otherwhise.
285 ContainerNode* node = parentOrShadowHostNode();
286 while (node) {
287 if (is<SVGSVGElement>(*node) || is<SVGImageElement>(*node) || node->hasTagName(SVGNames::symbolTag))
288 return downcast<SVGElement>(node);
289
290 node = node->parentOrShadowHostNode();
291 }
292
293 return nullptr;
294}
295
296const HashSet<SVGElement*>& SVGElement::instances() const
297{
298 if (!m_svgRareData) {
299 static NeverDestroyed<HashSet<SVGElement*>> emptyInstances;
300 return emptyInstances;
301 }
302 return m_svgRareData->instances();
303}
304
305bool SVGElement::getBoundingBox(FloatRect& rect, SVGLocatable::StyleUpdateStrategy styleUpdateStrategy)
306{
307 if (is<SVGGraphicsElement>(*this)) {
308 rect = downcast<SVGGraphicsElement>(*this).getBBox(styleUpdateStrategy);
309 return true;
310 }
311 return false;
312}
313
314SVGElement* SVGElement::correspondingElement() const
315{
316 return m_svgRareData ? m_svgRareData->correspondingElement() : nullptr;
317}
318
319RefPtr<SVGUseElement> SVGElement::correspondingUseElement() const
320{
321 auto* root = containingShadowRoot();
322 if (!root)
323 return nullptr;
324 if (root->mode() != ShadowRootMode::UserAgent)
325 return nullptr;
326 auto* host = root->host();
327 if (!is<SVGUseElement>(host))
328 return nullptr;
329 return &downcast<SVGUseElement>(*host);
330}
331
332void SVGElement::setCorrespondingElement(SVGElement* correspondingElement)
333{
334 if (m_svgRareData) {
335 if (auto oldCorrespondingElement = makeRefPtr(m_svgRareData->correspondingElement()))
336 oldCorrespondingElement->m_svgRareData->instances().remove(this);
337 }
338 if (m_svgRareData || correspondingElement)
339 ensureSVGRareData().setCorrespondingElement(correspondingElement);
340 if (correspondingElement)
341 correspondingElement->ensureSVGRareData().instances().add(this);
342}
343
344void SVGElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
345{
346 if (name == HTMLNames::classAttr) {
347 m_className->setBaseValInternal(value);
348 return;
349 }
350
351 if (name == HTMLNames::tabindexAttr) {
352 if (value.isEmpty())
353 clearTabIndexExplicitlyIfNeeded();
354 else if (auto optionalTabIndex = parseHTMLInteger(value))
355 setTabIndexExplicitly(optionalTabIndex.value());
356 return;
357 }
358
359 auto& eventName = HTMLElement::eventNameForEventHandlerAttribute(name);
360 if (!eventName.isNull()) {
361 setAttributeEventListener(eventName, name, value);
362 return;
363 }
364
365 SVGLangSpace::parseAttribute(name, value);
366}
367
368bool SVGElement::haveLoadedRequiredResources()
369{
370 for (auto& child : childrenOfType<SVGElement>(*this)) {
371 if (!child.haveLoadedRequiredResources())
372 return false;
373 }
374 return true;
375}
376
377bool SVGElement::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, const AddEventListenerOptions& options)
378{
379 // Add event listener to regular DOM element
380 if (!Node::addEventListener(eventType, listener.copyRef(), options))
381 return false;
382
383 if (containingShadowRoot())
384 return true;
385
386 // Add event listener to all shadow tree DOM element instances
387 ASSERT(!instanceUpdatesBlocked());
388 for (auto* instance : instances()) {
389 ASSERT(instance->correspondingElement() == this);
390 bool result = instance->Node::addEventListener(eventType, listener.copyRef(), options);
391 ASSERT_UNUSED(result, result);
392 }
393
394 return true;
395}
396
397bool SVGElement::removeEventListener(const AtomicString& eventType, EventListener& listener, const ListenerOptions& options)
398{
399 if (containingShadowRoot())
400 return Node::removeEventListener(eventType, listener, options);
401
402 // EventTarget::removeEventListener creates a Ref around the given EventListener
403 // object when creating a temporary RegisteredEventListener object used to look up the
404 // event listener in a cache. If we want to be able to call removeEventListener() multiple
405 // times on different nodes, we have to delay its immediate destruction, which would happen
406 // after the first call below.
407 Ref<EventListener> protector(listener);
408
409 // Remove event listener from regular DOM element
410 if (!Node::removeEventListener(eventType, listener, options))
411 return false;
412
413 // Remove event listener from all shadow tree DOM element instances
414 ASSERT(!instanceUpdatesBlocked());
415 for (auto& instance : instances()) {
416 ASSERT(instance->correspondingElement() == this);
417
418 if (instance->Node::removeEventListener(eventType, listener, options))
419 continue;
420
421 // This case can only be hit for event listeners created from markup
422 ASSERT(listener.wasCreatedFromMarkup());
423
424 // If the event listener 'listener' has been created from markup and has been fired before
425 // then JSLazyEventListener::parseCode() has been called and m_jsFunction of that listener
426 // has been created (read: it's not 0 anymore). During shadow tree creation, the event
427 // listener DOM attribute has been cloned, and another event listener has been setup in
428 // the shadow tree. If that event listener has not been used yet, m_jsFunction is still 0,
429 // and tryRemoveEventListener() above will fail. Work around that very rare problem.
430 ASSERT(instance->eventTargetData());
431 instance->eventTargetData()->eventListenerMap.removeFirstEventListenerCreatedFromMarkup(eventType);
432 }
433
434 return true;
435}
436
437static bool hasLoadListener(Element* element)
438{
439 if (element->hasEventListeners(eventNames().loadEvent))
440 return true;
441
442 for (element = element->parentOrShadowHostElement(); element; element = element->parentOrShadowHostElement()) {
443 if (element->hasCapturingEventListeners(eventNames().loadEvent))
444 return true;
445 }
446
447 return false;
448}
449
450void SVGElement::sendSVGLoadEventIfPossible(bool sendParentLoadEvents)
451{
452 if (!isConnected() || !document().frame())
453 return;
454
455 RefPtr<SVGElement> currentTarget = this;
456 while (currentTarget && currentTarget->haveLoadedRequiredResources()) {
457 RefPtr<Element> parent;
458 if (sendParentLoadEvents)
459 parent = currentTarget->parentOrShadowHostElement(); // save the next parent to dispatch too incase dispatching the event changes the tree
460 if (hasLoadListener(currentTarget.get()))
461 currentTarget->dispatchEvent(Event::create(eventNames().loadEvent, Event::CanBubble::No, Event::IsCancelable::No));
462 currentTarget = (parent && parent->isSVGElement()) ? static_pointer_cast<SVGElement>(parent) : RefPtr<SVGElement>();
463 SVGElement* element = currentTarget.get();
464 if (!element || !element->isOutermostSVGSVGElement())
465 continue;
466
467 // Consider <svg onload="foo()"><image xlink:href="foo.png" externalResourcesRequired="true"/></svg>.
468 // If foo.png is not yet loaded, the first SVGLoad event will go to the <svg> element, sent through
469 // Document::implicitClose(). Then the SVGLoad event will fire for <image>, once its loaded.
470 ASSERT(sendParentLoadEvents);
471
472 // If the load event was not sent yet by Document::implicitClose(), but the <image> from the example
473 // above, just appeared, don't send the SVGLoad event to the outermost <svg>, but wait for the document
474 // to be "ready to render", first.
475 if (!document().loadEventFinished())
476 break;
477 }
478}
479
480void SVGElement::sendSVGLoadEventIfPossibleAsynchronously()
481{
482 svgLoadEventTimer()->startOneShot(0_s);
483}
484
485void SVGElement::svgLoadEventTimerFired()
486{
487 sendSVGLoadEventIfPossible();
488}
489
490Timer* SVGElement::svgLoadEventTimer()
491{
492 ASSERT_NOT_REACHED();
493 return nullptr;
494}
495
496void SVGElement::finishParsingChildren()
497{
498 StyledElement::finishParsingChildren();
499
500 // The outermost SVGSVGElement SVGLoad event is fired through Document::dispatchWindowLoadEvent.
501 if (isOutermostSVGSVGElement())
502 return;
503
504 // finishParsingChildren() is called when the close tag is reached for an element (e.g. </svg>)
505 // we send SVGLoad events here if we can, otherwise they'll be sent when any required loads finish
506 sendSVGLoadEventIfPossible();
507
508 // Notify all the elements which have references to this element to rebuild their shadow and render
509 // trees, e.g. a <use> element references a target element before this target element is defined.
510 invalidateInstances();
511}
512
513bool SVGElement::childShouldCreateRenderer(const Node& child) const
514{
515 if (!child.isSVGElement())
516 return false;
517 auto& svgChild = downcast<SVGElement>(child);
518
519 static const QualifiedName* const invalidTextContent[] {
520#if ENABLE(SVG_FONTS)
521 &SVGNames::altGlyphTag.get(),
522#endif
523 &SVGNames::textPathTag.get(),
524 &SVGNames::trefTag.get(),
525 &SVGNames::tspanTag.get(),
526 };
527 auto& name = svgChild.localName();
528 for (auto* tag : invalidTextContent) {
529 if (name == tag->localName())
530 return false;
531 }
532
533 return svgChild.isValid();
534}
535
536void SVGElement::attributeChanged(const QualifiedName& name, const AtomicString& oldValue, const AtomicString& newValue, AttributeModificationReason)
537{
538 StyledElement::attributeChanged(name, oldValue, newValue);
539
540 if (name == HTMLNames::idAttr)
541 document().accessSVGExtensions().rebuildAllElementReferencesForTarget(*this);
542
543 // Changes to the style attribute are processed lazily (see Element::getAttribute() and related methods),
544 // so we don't want changes to the style attribute to result in extra work here except invalidateInstances().
545 if (name == HTMLNames::styleAttr)
546 invalidateInstances();
547 else
548 svgAttributeChanged(name);
549}
550
551void SVGElement::synchronizeAttribute(const QualifiedName& name)
552{
553 // If the value of the property has changed, serialize the new value to the attribute.
554 if (auto value = propertyRegistry().synchronize(name))
555 setSynchronizedLazyAttribute(name, *value);
556}
557
558void SVGElement::synchronizeAllAttributes()
559{
560 // SVGPropertyRegistry::synchronizeAllAttributes() returns the new values of
561 // the properties which have changed but not committed yet.
562 auto map = propertyRegistry().synchronizeAllAttributes();
563 for (const auto& entry : map)
564 setSynchronizedLazyAttribute(entry.key, entry.value);
565}
566
567void SVGElement::synchronizeAllAnimatedSVGAttribute(SVGElement& svgElement)
568{
569 svgElement.synchronizeAllAttributes();
570}
571
572void SVGElement::commitPropertyChange(SVGProperty* property)
573{
574 // We want to dirty the top-level property when a descendant changes. For example
575 // a change in an SVGLength item in SVGLengthList should set the dirty flag on
576 // SVGLengthList and not the SVGLength.
577 property->setDirty();
578
579 invalidateSVGAttributes();
580 svgAttributeChanged(propertyRegistry().propertyAttributeName(*property));
581}
582
583void SVGElement::commitPropertyChange(SVGAnimatedProperty& animatedProperty)
584{
585 QualifiedName attributeName = propertyRegistry().animatedPropertyAttributeName(animatedProperty);
586 ASSERT(attributeName != nullQName());
587
588 // A change in a style property, e.g SVGRectElement::x should be serialized to
589 // the attribute immediately. Otherwise it is okay to be lazy in this regard.
590 if (!propertyRegistry().isAnimatedStylePropertyAttribute(attributeName))
591 animatedProperty.setDirty();
592 else
593 setSynchronizedLazyAttribute(attributeName, animatedProperty.baseValAsString());
594
595 invalidateSVGAttributes();
596 svgAttributeChanged(attributeName);
597}
598
599bool SVGElement::isAnimatedPropertyAttribute(const QualifiedName& attributeName) const
600{
601 return propertyRegistry().isAnimatedPropertyAttribute(attributeName);
602}
603
604bool SVGElement::isAnimatedAttribute(const QualifiedName& attributeName) const
605{
606 return SVGPropertyAnimatorFactory::isKnownAttribute(attributeName) || isAnimatedPropertyAttribute(attributeName);
607}
608
609bool SVGElement::isAnimatedStyleAttribute(const QualifiedName& attributeName) const
610{
611 return SVGPropertyAnimatorFactory::isKnownAttribute(attributeName) || propertyRegistry().isAnimatedStylePropertyAttribute(attributeName);
612}
613
614std::unique_ptr<SVGAttributeAnimator> SVGElement::createAnimator(const QualifiedName& attributeName, AnimationMode animationMode, CalcMode calcMode, bool isAccumulated, bool isAdditive)
615{
616 // Property animator, e.g. "fill" or "fill-opacity".
617 if (auto animator = propertyAnimatorFactory().createAnimator(attributeName, animationMode, calcMode, isAccumulated, isAdditive))
618 return animator;
619
620 // Animated property animator.
621 auto animator = propertyRegistry().createAnimator(attributeName, animationMode, calcMode, isAccumulated, isAdditive);
622 if (!animator)
623 return animator;
624 for (auto* instance : instances())
625 instance->propertyRegistry().appendAnimatedInstance(attributeName, *animator);
626 return animator;
627}
628
629void SVGElement::animatorWillBeDeleted(const QualifiedName& attributeName)
630{
631 propertyAnimatorFactory().animatorWillBeDeleted(attributeName);
632}
633
634Optional<ElementStyle> SVGElement::resolveCustomStyle(const RenderStyle& parentStyle, const RenderStyle*)
635{
636 // If the element is in a <use> tree we get the style from the definition tree.
637 if (auto styleElement = makeRefPtr(this->correspondingElement())) {
638 Optional<ElementStyle> style = styleElement->resolveStyle(&parentStyle);
639 StyleResolver::adjustSVGElementStyle(*this, *style->renderStyle);
640 return style;
641 }
642
643 return resolveStyle(&parentStyle);
644}
645
646MutableStyleProperties* SVGElement::animatedSMILStyleProperties() const
647{
648 if (m_svgRareData)
649 return m_svgRareData->animatedSMILStyleProperties();
650 return 0;
651}
652
653MutableStyleProperties& SVGElement::ensureAnimatedSMILStyleProperties()
654{
655 return ensureSVGRareData().ensureAnimatedSMILStyleProperties();
656}
657
658void SVGElement::setUseOverrideComputedStyle(bool value)
659{
660 if (m_svgRareData)
661 m_svgRareData->setUseOverrideComputedStyle(value);
662}
663
664const RenderStyle* SVGElement::computedStyle(PseudoId pseudoElementSpecifier)
665{
666 if (!m_svgRareData || !m_svgRareData->useOverrideComputedStyle())
667 return Element::computedStyle(pseudoElementSpecifier);
668
669 const RenderStyle* parentStyle = nullptr;
670 if (auto parent = makeRefPtr(parentOrShadowHostElement())) {
671 if (auto renderer = parent->renderer())
672 parentStyle = &renderer->style();
673 }
674
675 return m_svgRareData->overrideComputedStyle(*this, parentStyle);
676}
677
678QualifiedName SVGElement::animatableAttributeForName(const AtomicString& localName)
679{
680 static const auto animatableAttributes = makeNeverDestroyed([] {
681 static const QualifiedName* const names[] = {
682 &HTMLNames::classAttr.get(),
683 &SVGNames::amplitudeAttr.get(),
684 &SVGNames::azimuthAttr.get(),
685 &SVGNames::baseFrequencyAttr.get(),
686 &SVGNames::biasAttr.get(),
687 &SVGNames::clipPathUnitsAttr.get(),
688 &SVGNames::cxAttr.get(),
689 &SVGNames::cyAttr.get(),
690 &SVGNames::diffuseConstantAttr.get(),
691 &SVGNames::divisorAttr.get(),
692 &SVGNames::dxAttr.get(),
693 &SVGNames::dyAttr.get(),
694 &SVGNames::edgeModeAttr.get(),
695 &SVGNames::elevationAttr.get(),
696 &SVGNames::exponentAttr.get(),
697 &SVGNames::externalResourcesRequiredAttr.get(),
698 &SVGNames::filterUnitsAttr.get(),
699 &SVGNames::fxAttr.get(),
700 &SVGNames::fyAttr.get(),
701 &SVGNames::gradientTransformAttr.get(),
702 &SVGNames::gradientUnitsAttr.get(),
703 &SVGNames::heightAttr.get(),
704 &SVGNames::in2Attr.get(),
705 &SVGNames::inAttr.get(),
706 &SVGNames::interceptAttr.get(),
707 &SVGNames::k1Attr.get(),
708 &SVGNames::k2Attr.get(),
709 &SVGNames::k3Attr.get(),
710 &SVGNames::k4Attr.get(),
711 &SVGNames::kernelMatrixAttr.get(),
712 &SVGNames::kernelUnitLengthAttr.get(),
713 &SVGNames::lengthAdjustAttr.get(),
714 &SVGNames::limitingConeAngleAttr.get(),
715 &SVGNames::markerHeightAttr.get(),
716 &SVGNames::markerUnitsAttr.get(),
717 &SVGNames::markerWidthAttr.get(),
718 &SVGNames::maskContentUnitsAttr.get(),
719 &SVGNames::maskUnitsAttr.get(),
720 &SVGNames::methodAttr.get(),
721 &SVGNames::modeAttr.get(),
722 &SVGNames::numOctavesAttr.get(),
723 &SVGNames::offsetAttr.get(),
724 &SVGNames::operatorAttr.get(),
725 &SVGNames::orderAttr.get(),
726 &SVGNames::orientAttr.get(),
727 &SVGNames::pathLengthAttr.get(),
728 &SVGNames::patternContentUnitsAttr.get(),
729 &SVGNames::patternTransformAttr.get(),
730 &SVGNames::patternUnitsAttr.get(),
731 &SVGNames::pointsAtXAttr.get(),
732 &SVGNames::pointsAtYAttr.get(),
733 &SVGNames::pointsAtZAttr.get(),
734 &SVGNames::preserveAlphaAttr.get(),
735 &SVGNames::preserveAspectRatioAttr.get(),
736 &SVGNames::primitiveUnitsAttr.get(),
737 &SVGNames::radiusAttr.get(),
738 &SVGNames::rAttr.get(),
739 &SVGNames::refXAttr.get(),
740 &SVGNames::refYAttr.get(),
741 &SVGNames::resultAttr.get(),
742 &SVGNames::rotateAttr.get(),
743 &SVGNames::rxAttr.get(),
744 &SVGNames::ryAttr.get(),
745 &SVGNames::scaleAttr.get(),
746 &SVGNames::seedAttr.get(),
747 &SVGNames::slopeAttr.get(),
748 &SVGNames::spacingAttr.get(),
749 &SVGNames::specularConstantAttr.get(),
750 &SVGNames::specularExponentAttr.get(),
751 &SVGNames::spreadMethodAttr.get(),
752 &SVGNames::startOffsetAttr.get(),
753 &SVGNames::stdDeviationAttr.get(),
754 &SVGNames::stitchTilesAttr.get(),
755 &SVGNames::surfaceScaleAttr.get(),
756 &SVGNames::tableValuesAttr.get(),
757 &SVGNames::targetAttr.get(),
758 &SVGNames::targetXAttr.get(),
759 &SVGNames::targetYAttr.get(),
760 &SVGNames::transformAttr.get(),
761 &SVGNames::typeAttr.get(),
762 &SVGNames::valuesAttr.get(),
763 &SVGNames::viewBoxAttr.get(),
764 &SVGNames::widthAttr.get(),
765 &SVGNames::x1Attr.get(),
766 &SVGNames::x2Attr.get(),
767 &SVGNames::xAttr.get(),
768 &SVGNames::xChannelSelectorAttr.get(),
769 &SVGNames::y1Attr.get(),
770 &SVGNames::y2Attr.get(),
771 &SVGNames::yAttr.get(),
772 &SVGNames::yChannelSelectorAttr.get(),
773 &SVGNames::zAttr.get(),
774 &SVGNames::hrefAttr.get(),
775 };
776 HashMap<AtomicString, QualifiedName> map;
777 for (auto& name : names) {
778 auto addResult = map.add(name->localName(), *name);
779 ASSERT_UNUSED(addResult, addResult.isNewEntry);
780 }
781 return map;
782 }());
783 return animatableAttributes.get().get(localName);
784}
785
786#ifndef NDEBUG
787
788bool SVGElement::isAnimatableAttribute(const QualifiedName& name) const
789{
790 if (animatableAttributeForName(name.localName()) == name)
791 return !filterOutAnimatableAttribute(name);
792 return false;
793}
794
795bool SVGElement::filterOutAnimatableAttribute(const QualifiedName&) const
796{
797 return false;
798}
799
800#endif
801
802String SVGElement::title() const
803{
804 // According to spec, for stand-alone SVG documents we should not return a title when
805 // hovering over the rootmost SVG element (the first <title> element is the title of
806 // the document, not a tooltip) so we instantly return.
807 if (isOutermostSVGSVGElement() && document().topDocument().isSVGDocument())
808 return String();
809 auto firstTitle = childrenOfType<SVGTitleElement>(*this).first();
810 return firstTitle ? const_cast<SVGTitleElement*>(firstTitle)->innerText() : String();
811}
812
813bool SVGElement::rendererIsNeeded(const RenderStyle& style)
814{
815 // http://www.w3.org/TR/SVG/extend.html#PrivateData
816 // Prevent anything other than SVG renderers from appearing in our render tree
817 // Spec: SVG allows inclusion of elements from foreign namespaces anywhere
818 // with the SVG content. In general, the SVG user agent will include the unknown
819 // elements in the DOM but will otherwise ignore unknown elements.
820 if (!parentOrShadowHostElement() || parentOrShadowHostElement()->isSVGElement())
821 return StyledElement::rendererIsNeeded(style);
822
823 return false;
824}
825
826CSSPropertyID SVGElement::cssPropertyIdForSVGAttributeName(const QualifiedName& attrName)
827{
828 if (!attrName.namespaceURI().isNull())
829 return CSSPropertyInvalid;
830
831 static const auto properties = makeNeverDestroyed(createAttributeNameToCSSPropertyIDMap());
832 return properties.get().get(attrName.localName().impl());
833}
834
835bool SVGElement::isPresentationAttribute(const QualifiedName& name) const
836{
837 if (cssPropertyIdForSVGAttributeName(name) > 0)
838 return true;
839 return StyledElement::isPresentationAttribute(name);
840}
841
842void SVGElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStyleProperties& style)
843{
844 CSSPropertyID propertyID = cssPropertyIdForSVGAttributeName(name);
845 if (propertyID > 0)
846 addPropertyToPresentationAttributeStyle(style, propertyID, value);
847}
848
849void SVGElement::svgAttributeChanged(const QualifiedName& attrName)
850{
851 CSSPropertyID propId = cssPropertyIdForSVGAttributeName(attrName);
852 if (propId > 0) {
853 invalidateInstances();
854 return;
855 }
856
857 if (attrName == HTMLNames::classAttr) {
858 classAttributeChanged(className());
859 invalidateInstances();
860 return;
861 }
862
863 if (attrName == HTMLNames::idAttr) {
864 auto renderer = this->renderer();
865 // Notify resources about id changes, this is important as we cache resources by id in SVGDocumentExtensions
866 if (is<RenderSVGResourceContainer>(renderer))
867 downcast<RenderSVGResourceContainer>(*renderer).idChanged();
868 if (isConnected())
869 buildPendingResourcesIfNeeded();
870 invalidateInstances();
871 return;
872 }
873
874 SVGLangSpace::svgAttributeChanged(attrName);
875}
876
877Node::InsertedIntoAncestorResult SVGElement::insertedIntoAncestor(InsertionType insertionType, ContainerNode& parentOfInsertedTree)
878{
879 StyledElement::insertedIntoAncestor(insertionType, parentOfInsertedTree);
880 updateRelativeLengthsInformation();
881 buildPendingResourcesIfNeeded();
882 return InsertedIntoAncestorResult::Done;
883}
884
885void SVGElement::buildPendingResourcesIfNeeded()
886{
887 if (!needsPendingResourceHandling() || !isConnected() || isInShadowTree())
888 return;
889
890 SVGDocumentExtensions& extensions = document().accessSVGExtensions();
891 String resourceId = getIdAttribute();
892 if (!extensions.isIdOfPendingResource(resourceId))
893 return;
894
895 // Mark pending resources as pending for removal.
896 extensions.markPendingResourcesForRemoval(resourceId);
897
898 // Rebuild pending resources for each client of a pending resource that is being removed.
899 while (auto clientElement = extensions.removeElementFromPendingResourcesForRemovalMap(resourceId)) {
900 ASSERT(clientElement->hasPendingResources());
901 if (clientElement->hasPendingResources()) {
902 clientElement->buildPendingResource();
903 extensions.clearHasPendingResourcesIfPossible(*clientElement);
904 }
905 }
906}
907
908void SVGElement::childrenChanged(const ChildChange& change)
909{
910 StyledElement::childrenChanged(change);
911
912 if (change.source == ChildChangeSource::Parser)
913 return;
914 invalidateInstances();
915}
916
917RefPtr<DeprecatedCSSOMValue> SVGElement::getPresentationAttribute(const String& name)
918{
919 if (!hasAttributesWithoutUpdate())
920 return 0;
921
922 QualifiedName attributeName(nullAtom(), name, nullAtom());
923 const Attribute* attribute = findAttributeByName(attributeName);
924 if (!attribute)
925 return 0;
926
927 auto style = MutableStyleProperties::create(SVGAttributeMode);
928 CSSPropertyID propertyID = cssPropertyIdForSVGAttributeName(attribute->name());
929 style->setProperty(propertyID, attribute->value());
930 auto cssValue = style->getPropertyCSSValue(propertyID);
931 if (!cssValue)
932 return nullptr;
933 return cssValue->createDeprecatedCSSOMWrapper(style->ensureCSSStyleDeclaration());
934}
935
936bool SVGElement::instanceUpdatesBlocked() const
937{
938 return m_svgRareData && m_svgRareData->instanceUpdatesBlocked();
939}
940
941void SVGElement::setInstanceUpdatesBlocked(bool value)
942{
943 // Catch any callers that calls setInstanceUpdatesBlocked(true) twice in a row.
944 // That probably indicates nested use of InstanceUpdateBlocker and a bug.
945 ASSERT(!value || !instanceUpdatesBlocked());
946
947 if (m_svgRareData)
948 m_svgRareData->setInstanceUpdatesBlocked(value);
949}
950
951AffineTransform SVGElement::localCoordinateSpaceTransform(SVGLocatable::CTMScope) const
952{
953 // To be overridden by SVGGraphicsElement (or as special case SVGTextElement and SVGPatternElement)
954 return AffineTransform();
955}
956
957void SVGElement::updateRelativeLengthsInformation(bool hasRelativeLengths, SVGElement* element)
958{
959 // If we're not yet in a document, this function will be called again from insertedIntoAncestor(). Do nothing now.
960 if (!isConnected())
961 return;
962
963 // An element wants to notify us that its own relative lengths state changed.
964 // Register it in the relative length map, and register us in the parent relative length map.
965 // Register the parent in the grandparents map, etc. Repeat procedure until the root of the SVG tree.
966
967 if (hasRelativeLengths)
968 m_elementsWithRelativeLengths.add(element);
969 else {
970 if (!m_elementsWithRelativeLengths.contains(element)) {
971 // We were never registered. Do nothing.
972 return;
973 }
974
975 m_elementsWithRelativeLengths.remove(element);
976 }
977
978 if (!element->isSVGGraphicsElement())
979 return;
980
981 // Find first styled parent node, and notify it that we've changed our relative length state.
982 auto node = makeRefPtr(parentNode());
983 while (node) {
984 if (!node->isSVGElement())
985 break;
986
987 // Register us in the parent element map.
988 downcast<SVGElement>(*node).updateRelativeLengthsInformation(hasRelativeLengths, this);
989 break;
990 }
991}
992
993bool SVGElement::hasFocusEventListeners() const
994{
995 Element* eventTarget = const_cast<SVGElement*>(this);
996 return eventTarget->hasEventListeners(eventNames().focusinEvent)
997 || eventTarget->hasEventListeners(eventNames().focusoutEvent)
998 || eventTarget->hasEventListeners(eventNames().focusEvent)
999 || eventTarget->hasEventListeners(eventNames().blurEvent);
1000}
1001
1002bool SVGElement::isMouseFocusable() const
1003{
1004 if (!isFocusable())
1005 return false;
1006 Element* eventTarget = const_cast<SVGElement*>(this);
1007 return hasFocusEventListeners()
1008 || eventTarget->hasEventListeners(eventNames().keydownEvent)
1009 || eventTarget->hasEventListeners(eventNames().keyupEvent)
1010 || eventTarget->hasEventListeners(eventNames().keypressEvent);
1011}
1012
1013void SVGElement::accessKeyAction(bool sendMouseEvents)
1014{
1015 dispatchSimulatedClick(0, sendMouseEvents ? SendMouseUpDownEvents : SendNoEvents);
1016}
1017
1018void SVGElement::invalidateInstances()
1019{
1020 if (instanceUpdatesBlocked())
1021 return;
1022
1023 auto& instances = this->instances();
1024 while (!instances.isEmpty()) {
1025 auto instance = makeRefPtr(*instances.begin());
1026 if (auto useElement = instance->correspondingUseElement())
1027 useElement->invalidateShadowTree();
1028 instance->setCorrespondingElement(nullptr);
1029 } while (!instances.isEmpty());
1030}
1031
1032}
1033