#include "config.h"
#include "SVGElement.h"
#include "CSSPropertyParser.h"
#include "DeprecatedCSSOMValue.h"
#include "Document.h"
#include "ElementIterator.h"
#include "Event.h"
#include "EventNames.h"
#include "HTMLElement.h"
#include "HTMLNames.h"
#include "HTMLParserIdioms.h"
#include "RenderObject.h"
#include "RenderSVGResource.h"
#include "RenderSVGResourceFilter.h"
#include "RenderSVGResourceMasker.h"
#include "SVGDocumentExtensions.h"
#include "SVGElementRareData.h"
#include "SVGGraphicsElement.h"
#include "SVGImageElement.h"
#include "SVGNames.h"
#include "SVGPropertyAnimatorFactory.h"
#include "SVGRenderStyle.h"
#include "SVGRenderSupport.h"
#include "SVGSVGElement.h"
#include "SVGTitleElement.h"
#include "SVGUseElement.h"
#include "ShadowRoot.h"
#include "StyleAdjuster.h"
#include "XMLNames.h"
#include <wtf/Assertions.h>
#include <wtf/HashMap.h>
#include <wtf/IsoMallocInlines.h>
#include <wtf/NeverDestroyed.h>
#include <wtf/StdLibExtras.h>
#include <wtf/text/WTFString.h>
namespace WebCore {
WTF_MAKE_ISO_ALLOCATED_IMPL(SVGElement);
static NEVER_INLINE HashMap<AtomStringImpl*, CSSPropertyID> createAttributeNameToCSSPropertyIDMap()
{
using namespace HTMLNames;
using namespace SVGNames;
static const QualifiedName* const attributeNames[] = {
&alignment_baselineAttr.get(),
&baseline_shiftAttr.get(),
&buffered_renderingAttr.get(),
&clipAttr.get(),
&clip_pathAttr.get(),
&clip_ruleAttr.get(),
&SVGNames::colorAttr.get(),
&color_interpolationAttr.get(),
&color_interpolation_filtersAttr.get(),
&color_profileAttr.get(),
&color_renderingAttr.get(),
&cursorAttr.get(),
&cxAttr.get(),
&cyAttr.get(),
&SVGNames::directionAttr.get(),
&displayAttr.get(),
&dominant_baselineAttr.get(),
&enable_backgroundAttr.get(),
&fillAttr.get(),
&fill_opacityAttr.get(),
&fill_ruleAttr.get(),
&filterAttr.get(),
&flood_colorAttr.get(),
&flood_opacityAttr.get(),
&font_familyAttr.get(),
&font_sizeAttr.get(),
&font_stretchAttr.get(),
&font_styleAttr.get(),
&font_variantAttr.get(),
&font_weightAttr.get(),
&glyph_orientation_horizontalAttr.get(),
&glyph_orientation_verticalAttr.get(),
&image_renderingAttr.get(),
&SVGNames::heightAttr.get(),
&kerningAttr.get(),
&letter_spacingAttr.get(),
&lighting_colorAttr.get(),
&marker_endAttr.get(),
&marker_midAttr.get(),
&marker_startAttr.get(),
&maskAttr.get(),
&mask_typeAttr.get(),
&opacityAttr.get(),
&overflowAttr.get(),
&paint_orderAttr.get(),
&pointer_eventsAttr.get(),
&rAttr.get(),
&rxAttr.get(),
&ryAttr.get(),
&shape_renderingAttr.get(),
&stop_colorAttr.get(),
&stop_opacityAttr.get(),
&strokeAttr.get(),
&stroke_dasharrayAttr.get(),
&stroke_dashoffsetAttr.get(),
&stroke_linecapAttr.get(),
&stroke_linejoinAttr.get(),
&stroke_miterlimitAttr.get(),
&stroke_opacityAttr.get(),
&stroke_widthAttr.get(),
&text_anchorAttr.get(),
&text_decorationAttr.get(),
&text_renderingAttr.get(),
&unicode_bidiAttr.get(),
&vector_effectAttr.get(),
&visibilityAttr.get(),
&SVGNames::widthAttr.get(),
&word_spacingAttr.get(),
&writing_modeAttr.get(),
&xAttr.get(),
&yAttr.get(),
};
HashMap<AtomStringImpl*, CSSPropertyID> map;
for (auto& name : attributeNames) {
const AtomString& localName = name->localName();
map.add(localName.impl(), cssPropertyID(localName));
}
map.add(transform_originAttr->localName().impl(), CSSPropertyTransformOrigin);
return map;
}
SVGElement::SVGElement(const QualifiedName& tagName, Document& document)
: StyledElement(tagName, document, CreateSVGElement)
, m_propertyAnimatorFactory(makeUnique<SVGPropertyAnimatorFactory>())
{
static std::once_flag onceFlag;
std::call_once(onceFlag, [] {
PropertyRegistry::registerProperty<HTMLNames::classAttr, &SVGElement::m_className>();
});
}
SVGElement::~SVGElement()
{
if (m_svgRareData) {
for (SVGElement* instance : m_svgRareData->instances())
instance->m_svgRareData->setCorrespondingElement(nullptr);
if (auto correspondingElement = makeRefPtr(m_svgRareData->correspondingElement()))
correspondingElement->m_svgRareData->instances().remove(this);
m_svgRareData = nullptr;
}
document().accessSVGExtensions().rebuildAllElementReferencesForTarget(*this);
document().accessSVGExtensions().removeAllElementReferencesForTarget(*this);
}
void SVGElement::willRecalcStyle(Style::Change change)
{
if (!m_svgRareData || styleResolutionShouldRecompositeLayer())
return;
if (change > Style::NoChange || needsStyleRecalc())
m_svgRareData->setNeedsOverrideComputedStyleUpdate();
}
SVGElementRareData& SVGElement::ensureSVGRareData()
{
if (!m_svgRareData)
m_svgRareData = makeUnique<SVGElementRareData>();
return *m_svgRareData;
}
bool SVGElement::isOutermostSVGSVGElement() const
{
if (!is<SVGSVGElement>(*this))
return false;
if (isInShadowTree() && parentOrShadowHostElement() && parentOrShadowHostElement()->isSVGElement())
return false;
if (!parentNode())
return true;
if (parentNode()->hasTagName(SVGNames::foreignObjectTag))
return true;
return !parentNode()->isSVGElement();
}
void SVGElement::reportAttributeParsingError(SVGParsingError error, const QualifiedName& name, const AtomString& value)
{
if (error == NoError)
return;
String errorString = "<" + tagName() + "> attribute " + name.toString() + "=\"" + value + "\"";
SVGDocumentExtensions& extensions = document().accessSVGExtensions();
if (error == NegativeValueForbiddenError) {
extensions.reportError("Invalid negative value for " + errorString);
return;
}
if (error == ParsingAttributeFailedError) {
extensions.reportError("Invalid value for " + errorString);
return;
}
ASSERT_NOT_REACHED();
}
void SVGElement::removedFromAncestor(RemovalType removalType, ContainerNode& oldParentOfRemovedTree)
{
if (removalType.disconnectedFromDocument)
updateRelativeLengthsInformation(false, this);
StyledElement::removedFromAncestor(removalType, oldParentOfRemovedTree);
if (removalType.disconnectedFromDocument) {
document().accessSVGExtensions().clearTargetDependencies(*this);
document().accessSVGExtensions().removeAllElementReferencesForTarget(*this);
}
invalidateInstances();
}
SVGSVGElement* SVGElement::ownerSVGElement() const
{
ContainerNode* node = parentOrShadowHostNode();
while (node) {
if (is<SVGSVGElement>(*node))
return downcast<SVGSVGElement>(node);
node = node->parentOrShadowHostNode();
}
return nullptr;
}
SVGElement* SVGElement::viewportElement() const
{
ContainerNode* node = parentOrShadowHostNode();
while (node) {
if (is<SVGSVGElement>(*node) || is<SVGImageElement>(*node) || node->hasTagName(SVGNames::symbolTag))
return downcast<SVGElement>(node);
node = node->parentOrShadowHostNode();
}
return nullptr;
}
const HashSet<SVGElement*>& SVGElement::instances() const
{
if (!m_svgRareData) {
static NeverDestroyed<HashSet<SVGElement*>> emptyInstances;
return emptyInstances;
}
return m_svgRareData->instances();
}
bool SVGElement::getBoundingBox(FloatRect& rect, SVGLocatable::StyleUpdateStrategy styleUpdateStrategy)
{
if (is<SVGGraphicsElement>(*this)) {
rect = downcast<SVGGraphicsElement>(*this).getBBox(styleUpdateStrategy);
return true;
}
return false;
}
SVGElement* SVGElement::correspondingElement() const
{
return m_svgRareData ? m_svgRareData->correspondingElement() : nullptr;
}
RefPtr<SVGUseElement> SVGElement::correspondingUseElement() const
{
auto* root = containingShadowRoot();
if (!root)
return nullptr;
if (root->mode() != ShadowRootMode::UserAgent)
return nullptr;
auto* host = root->host();
if (!is<SVGUseElement>(host))
return nullptr;
return &downcast<SVGUseElement>(*host);
}
void SVGElement::setCorrespondingElement(SVGElement* correspondingElement)
{
if (m_svgRareData) {
if (auto oldCorrespondingElement = makeRefPtr(m_svgRareData->correspondingElement()))
oldCorrespondingElement->m_svgRareData->instances().remove(this);
}
if (m_svgRareData || correspondingElement)
ensureSVGRareData().setCorrespondingElement(correspondingElement);
if (correspondingElement)
correspondingElement->ensureSVGRareData().instances().add(this);
}
void SVGElement::parseAttribute(const QualifiedName& name, const AtomString& value)
{
if (name == HTMLNames::classAttr) {
m_className->setBaseValInternal(value);
return;
}
if (name == HTMLNames::tabindexAttr) {
if (value.isEmpty())
clearTabIndexExplicitlyIfNeeded();
else if (auto optionalTabIndex = parseHTMLInteger(value))
setTabIndexExplicitly(optionalTabIndex.value());
return;
}
auto& eventName = HTMLElement::eventNameForEventHandlerAttribute(name);
if (!eventName.isNull()) {
setAttributeEventListener(eventName, name, value);
return;
}
}
bool SVGElement::haveLoadedRequiredResources()
{
for (auto& child : childrenOfType<SVGElement>(*this)) {
if (!child.haveLoadedRequiredResources())
return false;
}
return true;
}
bool SVGElement::addEventListener(const AtomString& eventType, Ref<EventListener>&& listener, const AddEventListenerOptions& options)
{
if (!Node::addEventListener(eventType, listener.copyRef(), options))
return false;
if (containingShadowRoot())
return true;
ASSERT(!instanceUpdatesBlocked());
for (auto* instance : instances()) {
ASSERT(instance->correspondingElement() == this);
bool result = instance->Node::addEventListener(eventType, listener.copyRef(), options);
ASSERT_UNUSED(result, result);
}
return true;
}
bool SVGElement::removeEventListener(const AtomString& eventType, EventListener& listener, const ListenerOptions& options)
{
if (containingShadowRoot())
return Node::removeEventListener(eventType, listener, options);
Ref<EventListener> protector(listener);
if (!Node::removeEventListener(eventType, listener, options))
return false;
ASSERT(!instanceUpdatesBlocked());
for (auto& instance : instances()) {
ASSERT(instance->correspondingElement() == this);
if (instance->Node::removeEventListener(eventType, listener, options))
continue;
ASSERT(listener.wasCreatedFromMarkup());
ASSERT(instance->eventTargetData());
instance->eventTargetData()->eventListenerMap.removeFirstEventListenerCreatedFromMarkup(eventType);
}
return true;
}
static bool hasLoadListener(Element* element)
{
if (element->hasEventListeners(eventNames().loadEvent))
return true;
for (element = element->parentOrShadowHostElement(); element; element = element->parentOrShadowHostElement()) {
if (element->hasCapturingEventListeners(eventNames().loadEvent))
return true;
}
return false;
}
void SVGElement::sendLoadEventIfPossible()
{
if (!isConnected() || !document().frame())
return;
if (!haveLoadedRequiredResources() || !hasLoadListener(this))
return;
dispatchEvent(Event::create(eventNames().loadEvent, Event::CanBubble::No, Event::IsCancelable::No));
}
void SVGElement::loadEventTimerFired()
{
sendLoadEventIfPossible();
}
Timer* SVGElement::loadEventTimer()
{
ASSERT_NOT_REACHED();
return nullptr;
}
void SVGElement::finishParsingChildren()
{
StyledElement::finishParsingChildren();
if (isOutermostSVGSVGElement())
return;
invalidateInstances();
}
bool SVGElement::childShouldCreateRenderer(const Node& child) const
{
if (!child.isSVGElement())
return false;
auto& svgChild = downcast<SVGElement>(child);
static const QualifiedName* const invalidTextContent[] {
#if ENABLE(SVG_FONTS)
&SVGNames::altGlyphTag.get(),
#endif
&SVGNames::textPathTag.get(),
&SVGNames::trefTag.get(),
&SVGNames::tspanTag.get(),
};
auto& name = svgChild.localName();
for (auto* tag : invalidTextContent) {
if (name == tag->localName())
return false;
}
return svgChild.isValid();
}
void SVGElement::attributeChanged(const QualifiedName& name, const AtomString& oldValue, const AtomString& newValue, AttributeModificationReason)
{
StyledElement::attributeChanged(name, oldValue, newValue);
if (name == HTMLNames::idAttr)
document().accessSVGExtensions().rebuildAllElementReferencesForTarget(*this);
if (name == HTMLNames::styleAttr)
invalidateInstances();
else
svgAttributeChanged(name);
}
void SVGElement::synchronizeAttribute(const QualifiedName& name)
{
if (auto value = propertyRegistry().synchronize(name))
setSynchronizedLazyAttribute(name, *value);
}
void SVGElement::synchronizeAllAttributes()
{
auto map = propertyRegistry().synchronizeAllAttributes();
for (const auto& entry : map)
setSynchronizedLazyAttribute(entry.key, entry.value);
}
void SVGElement::synchronizeAllAnimatedSVGAttribute(SVGElement& svgElement)
{
svgElement.synchronizeAllAttributes();
}
void SVGElement::commitPropertyChange(SVGProperty* property)
{
property->setDirty();
invalidateSVGAttributes();
svgAttributeChanged(propertyRegistry().propertyAttributeName(*property));
}
void SVGElement::commitPropertyChange(SVGAnimatedProperty& animatedProperty)
{
QualifiedName attributeName = propertyRegistry().animatedPropertyAttributeName(animatedProperty);
ASSERT(attributeName != nullQName());
if (!propertyRegistry().isAnimatedStylePropertyAttribute(attributeName))
propertyRegistry().setAnimatedPropertDirty(attributeName, animatedProperty);
else
setSynchronizedLazyAttribute(attributeName, animatedProperty.baseValAsString());
invalidateSVGAttributes();
svgAttributeChanged(attributeName);
}
bool SVGElement::isAnimatedPropertyAttribute(const QualifiedName& attributeName) const
{
return propertyRegistry().isAnimatedPropertyAttribute(attributeName);
}
bool SVGElement::isAnimatedAttribute(const QualifiedName& attributeName) const
{
return SVGPropertyAnimatorFactory::isKnownAttribute(attributeName) || isAnimatedPropertyAttribute(attributeName);
}
bool SVGElement::isAnimatedStyleAttribute(const QualifiedName& attributeName) const
{
return SVGPropertyAnimatorFactory::isKnownAttribute(attributeName) || propertyRegistry().isAnimatedStylePropertyAttribute(attributeName);
}
RefPtr<SVGAttributeAnimator> SVGElement::createAnimator(const QualifiedName& attributeName, AnimationMode animationMode, CalcMode calcMode, bool isAccumulated, bool isAdditive)
{
if (auto animator = propertyAnimatorFactory().createAnimator(attributeName, animationMode, calcMode, isAccumulated, isAdditive))
return animator;
auto animator = propertyRegistry().createAnimator(attributeName, animationMode, calcMode, isAccumulated, isAdditive);
if (!animator)
return animator;
for (auto* instance : instances())
instance->propertyRegistry().appendAnimatedInstance(attributeName, *animator);
return animator;
}
void SVGElement::animatorWillBeDeleted(const QualifiedName& attributeName)
{
propertyAnimatorFactory().animatorWillBeDeleted(attributeName);
}
Optional<Style::ElementStyle> SVGElement::resolveCustomStyle(const RenderStyle& parentStyle, const RenderStyle*)
{
if (auto styleElement = makeRefPtr(this->correspondingElement())) {
auto style = styleElement->resolveStyle(&parentStyle);
Style::Adjuster::adjustSVGElementStyle(*style.renderStyle, *this);
return style;
}
return resolveStyle(&parentStyle);
}
MutableStyleProperties* SVGElement::animatedSMILStyleProperties() const
{
if (m_svgRareData)
return m_svgRareData->animatedSMILStyleProperties();
return 0;
}
MutableStyleProperties& SVGElement::ensureAnimatedSMILStyleProperties()
{
return ensureSVGRareData().ensureAnimatedSMILStyleProperties();
}
void SVGElement::setUseOverrideComputedStyle(bool value)
{
if (m_svgRareData)
m_svgRareData->setUseOverrideComputedStyle(value);
}
const RenderStyle* SVGElement::computedStyle(PseudoId pseudoElementSpecifier)
{
if (!m_svgRareData || !m_svgRareData->useOverrideComputedStyle())
return Element::computedStyle(pseudoElementSpecifier);
const RenderStyle* parentStyle = nullptr;
if (auto parent = makeRefPtr(parentOrShadowHostElement())) {
if (auto renderer = parent->renderer())
parentStyle = &renderer->style();
}
return m_svgRareData->overrideComputedStyle(*this, parentStyle);
}
QualifiedName SVGElement::animatableAttributeForName(const AtomString& localName)
{
static const auto animatableAttributes = makeNeverDestroyed([] {
static const QualifiedName* const names[] = {
&HTMLNames::classAttr.get(),
&SVGNames::amplitudeAttr.get(),
&SVGNames::azimuthAttr.get(),
&SVGNames::baseFrequencyAttr.get(),
&SVGNames::biasAttr.get(),
&SVGNames::clipPathUnitsAttr.get(),
&SVGNames::cxAttr.get(),
&SVGNames::cyAttr.get(),
&SVGNames::diffuseConstantAttr.get(),
&SVGNames::divisorAttr.get(),
&SVGNames::dxAttr.get(),
&SVGNames::dyAttr.get(),
&SVGNames::edgeModeAttr.get(),
&SVGNames::elevationAttr.get(),
&SVGNames::exponentAttr.get(),
&SVGNames::externalResourcesRequiredAttr.get(),
&SVGNames::filterUnitsAttr.get(),
&SVGNames::fxAttr.get(),
&SVGNames::fyAttr.get(),
&SVGNames::gradientTransformAttr.get(),
&SVGNames::gradientUnitsAttr.get(),
&SVGNames::heightAttr.get(),
&SVGNames::in2Attr.get(),
&SVGNames::inAttr.get(),
&SVGNames::interceptAttr.get(),
&SVGNames::k1Attr.get(),
&SVGNames::k2Attr.get(),
&SVGNames::k3Attr.get(),
&SVGNames::k4Attr.get(),
&SVGNames::kernelMatrixAttr.get(),
&SVGNames::kernelUnitLengthAttr.get(),
&SVGNames::lengthAdjustAttr.get(),
&SVGNames::limitingConeAngleAttr.get(),
&SVGNames::markerHeightAttr.get(),
&SVGNames::markerUnitsAttr.get(),
&SVGNames::markerWidthAttr.get(),
&SVGNames::maskContentUnitsAttr.get(),
&SVGNames::maskUnitsAttr.get(),
&SVGNames::methodAttr.get(),
&SVGNames::modeAttr.get(),
&SVGNames::numOctavesAttr.get(),
&SVGNames::offsetAttr.get(),
&SVGNames::operatorAttr.get(),
&SVGNames::orderAttr.get(),
&SVGNames::orientAttr.get(),
&SVGNames::pathLengthAttr.get(),
&SVGNames::patternContentUnitsAttr.get(),
&SVGNames::patternTransformAttr.get(),
&SVGNames::patternUnitsAttr.get(),
&SVGNames::pointsAtXAttr.get(),
&SVGNames::pointsAtYAttr.get(),
&SVGNames::pointsAtZAttr.get(),
&SVGNames::preserveAlphaAttr.get(),
&SVGNames::preserveAspectRatioAttr.get(),
&SVGNames::primitiveUnitsAttr.get(),
&SVGNames::radiusAttr.get(),
&SVGNames::rAttr.get(),
&SVGNames::refXAttr.get(),
&SVGNames::refYAttr.get(),
&SVGNames::resultAttr.get(),
&SVGNames::rotateAttr.get(),
&SVGNames::rxAttr.get(),
&SVGNames::ryAttr.get(),
&SVGNames::scaleAttr.get(),
&SVGNames::seedAttr.get(),
&SVGNames::slopeAttr.get(),
&SVGNames::spacingAttr.get(),
&SVGNames::specularConstantAttr.get(),
&SVGNames::specularExponentAttr.get(),
&SVGNames::spreadMethodAttr.get(),
&SVGNames::startOffsetAttr.get(),
&SVGNames::stdDeviationAttr.get(),
&SVGNames::stitchTilesAttr.get(),
&SVGNames::surfaceScaleAttr.get(),
&SVGNames::tableValuesAttr.get(),
&SVGNames::targetAttr.get(),
&SVGNames::targetXAttr.get(),
&SVGNames::targetYAttr.get(),
&SVGNames::transformAttr.get(),
&SVGNames::typeAttr.get(),
&SVGNames::valuesAttr.get(),
&SVGNames::viewBoxAttr.get(),
&SVGNames::widthAttr.get(),
&SVGNames::x1Attr.get(),
&SVGNames::x2Attr.get(),
&SVGNames::xAttr.get(),
&SVGNames::xChannelSelectorAttr.get(),
&SVGNames::y1Attr.get(),
&SVGNames::y2Attr.get(),
&SVGNames::yAttr.get(),
&SVGNames::yChannelSelectorAttr.get(),
&SVGNames::zAttr.get(),
&SVGNames::hrefAttr.get(),
};
HashMap<AtomString, QualifiedName> map;
for (auto& name : names) {
auto addResult = map.add(name->localName(), *name);
ASSERT_UNUSED(addResult, addResult.isNewEntry);
}
return map;
}());
return animatableAttributes.get().get(localName);
}
#ifndef NDEBUG
bool SVGElement::isAnimatableAttribute(const QualifiedName& name) const
{
if (animatableAttributeForName(name.localName()) == name)
return !filterOutAnimatableAttribute(name);
return false;
}
bool SVGElement::filterOutAnimatableAttribute(const QualifiedName&) const
{
return false;
}
#endif
String SVGElement::title() const
{
if (isOutermostSVGSVGElement() && document().topDocument().isSVGDocument())
return String();
auto firstTitle = childrenOfType<SVGTitleElement>(*this).first();
return firstTitle ? const_cast<SVGTitleElement*>(firstTitle)->innerText() : String();
}
bool SVGElement::rendererIsNeeded(const RenderStyle& style)
{
if (!parentOrShadowHostElement() || parentOrShadowHostElement()->isSVGElement())
return StyledElement::rendererIsNeeded(style);
return false;
}
CSSPropertyID SVGElement::cssPropertyIdForSVGAttributeName(const QualifiedName& attrName)
{
if (!attrName.namespaceURI().isNull())
return CSSPropertyInvalid;
static const auto properties = makeNeverDestroyed(createAttributeNameToCSSPropertyIDMap());
return properties.get().get(attrName.localName().impl());
}
bool SVGElement::isPresentationAttribute(const QualifiedName& name) const
{
if (cssPropertyIdForSVGAttributeName(name) > 0)
return true;
return StyledElement::isPresentationAttribute(name);
}
void SVGElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomString& value, MutableStyleProperties& style)
{
CSSPropertyID propertyID = cssPropertyIdForSVGAttributeName(name);
if (propertyID > 0)
addPropertyToPresentationAttributeStyle(style, propertyID, value);
}
void SVGElement::svgAttributeChanged(const QualifiedName& attrName)
{
CSSPropertyID propId = cssPropertyIdForSVGAttributeName(attrName);
if (propId > 0) {
invalidateInstances();
return;
}
if (attrName == HTMLNames::classAttr) {
classAttributeChanged(className());
invalidateInstances();
return;
}
if (attrName == HTMLNames::idAttr) {
auto renderer = this->renderer();
if (is<RenderSVGResourceContainer>(renderer))
downcast<RenderSVGResourceContainer>(*renderer).idChanged();
if (isConnected())
buildPendingResourcesIfNeeded();
invalidateInstances();
return;
}
}
Node::InsertedIntoAncestorResult SVGElement::insertedIntoAncestor(InsertionType insertionType, ContainerNode& parentOfInsertedTree)
{
StyledElement::insertedIntoAncestor(insertionType, parentOfInsertedTree);
updateRelativeLengthsInformation();
if (needsPendingResourceHandling() && insertionType.connectedToDocument && !isInShadowTree()) {
SVGDocumentExtensions& extensions = document().accessSVGExtensions();
String resourceId = getIdAttribute();
if (extensions.isIdOfPendingResource(resourceId))
return InsertedIntoAncestorResult::NeedsPostInsertionCallback;
}
return InsertedIntoAncestorResult::Done;
}
void SVGElement::didFinishInsertingNode()
{
buildPendingResourcesIfNeeded();
}
void SVGElement::buildPendingResourcesIfNeeded()
{
if (!needsPendingResourceHandling() || !isConnected() || isInShadowTree())
return;
SVGDocumentExtensions& extensions = document().accessSVGExtensions();
String resourceId = getIdAttribute();
if (!extensions.isIdOfPendingResource(resourceId))
return;
extensions.markPendingResourcesForRemoval(resourceId);
while (auto clientElement = extensions.removeElementFromPendingResourcesForRemovalMap(resourceId)) {
ASSERT(clientElement->hasPendingResources());
if (clientElement->hasPendingResources()) {
clientElement->buildPendingResource();
extensions.clearHasPendingResourcesIfPossible(*clientElement);
}
}
}
void SVGElement::childrenChanged(const ChildChange& change)
{
StyledElement::childrenChanged(change);
if (change.source == ChildChangeSource::Parser)
return;
invalidateInstances();
}
bool SVGElement::instanceUpdatesBlocked() const
{
return m_svgRareData && m_svgRareData->instanceUpdatesBlocked();
}
void SVGElement::setInstanceUpdatesBlocked(bool value)
{
ASSERT(!value || !instanceUpdatesBlocked());
if (m_svgRareData)
m_svgRareData->setInstanceUpdatesBlocked(value);
}
AffineTransform SVGElement::localCoordinateSpaceTransform(SVGLocatable::CTMScope) const
{
return AffineTransform();
}
void SVGElement::updateRelativeLengthsInformation(bool hasRelativeLengths, SVGElement* element)
{
if (!isConnected())
return;
if (hasRelativeLengths)
m_elementsWithRelativeLengths.add(element);
else {
if (!m_elementsWithRelativeLengths.contains(element)) {
return;
}
m_elementsWithRelativeLengths.remove(element);
}
if (!element->isSVGGraphicsElement())
return;
auto node = makeRefPtr(parentNode());
while (node) {
if (!node->isSVGElement())
break;
downcast<SVGElement>(*node).updateRelativeLengthsInformation(hasRelativeLengths, this);
break;
}
}
void SVGElement::accessKeyAction(bool sendMouseEvents)
{
dispatchSimulatedClick(0, sendMouseEvents ? SendMouseUpDownEvents : SendNoEvents);
}
void SVGElement::invalidateInstances()
{
if (instanceUpdatesBlocked())
return;
auto& instances = this->instances();
while (!instances.isEmpty()) {
auto instance = makeRefPtr(*instances.begin());
if (auto useElement = instance->correspondingUseElement())
useElement->invalidateShadowTree();
instance->setCorrespondingElement(nullptr);
} while (!instances.isEmpty());
}
}