diff options
Diffstat (limited to 'Source/WebCore/svg/SVGElement.cpp')
-rw-r--r-- | Source/WebCore/svg/SVGElement.cpp | 829 |
1 files changed, 410 insertions, 419 deletions
diff --git a/Source/WebCore/svg/SVGElement.cpp b/Source/WebCore/svg/SVGElement.cpp index bdb7ea1fc..49195090c 100644 --- a/Source/WebCore/svg/SVGElement.cpp +++ b/Source/WebCore/svg/SVGElement.cpp @@ -1,10 +1,11 @@ /* * Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org> * Copyright (C) 2004, 2005, 2006, 2008 Rob Buis <buis@kde.org> - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2014 Apple Inc. All rights reserved. * Copyright (C) 2008 Alp Toker <alp@atoker.com> * Copyright (C) 2009 Cameron McCormack <cam@mcc.id.au> * Copyright (C) 2013 Samsung Electronics. All rights reserved. + * Copyright (C) 2014 Adobe Systems Incorporated. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -23,27 +24,22 @@ */ #include "config.h" - -#if ENABLE(SVG) #include "SVGElement.h" -#include "Attr.h" -#include "CSSCursorImageValue.h" -#include "CSSParser.h" -#include "DOMImplementation.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 "RenderSVGResourceClipper.h" #include "RenderSVGResourceFilter.h" #include "RenderSVGResourceMasker.h" -#include "SVGCursorElement.h" #include "SVGDocumentExtensions.h" -#include "SVGElementInstance.h" #include "SVGElementRareData.h" #include "SVGGraphicsElement.h" #include "SVGImageElement.h" @@ -72,11 +68,11 @@ BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGElement) REGISTER_LOCAL_ANIMATED_PROPERTY(className) END_REGISTER_ANIMATED_PROPERTIES -using namespace HTMLNames; -using namespace SVGNames; - static NEVER_INLINE void populateAttributeNameToCSSPropertyIDMap(HashMap<AtomicStringImpl*, CSSPropertyID>& map) { + using namespace HTMLNames; + using namespace SVGNames; + // This list should include all base CSS and SVG CSS properties which are exposed as SVG XML attributes. static const QualifiedName* const attributeNames[] = { &alignment_baselineAttr, @@ -91,6 +87,8 @@ static NEVER_INLINE void populateAttributeNameToCSSPropertyIDMap(HashMap<AtomicS &color_profileAttr, &color_renderingAttr, &cursorAttr, + &cxAttr, + &cyAttr, &SVGNames::directionAttr, &displayAttr, &dominant_baselineAttr, @@ -110,6 +108,7 @@ static NEVER_INLINE void populateAttributeNameToCSSPropertyIDMap(HashMap<AtomicS &glyph_orientation_horizontalAttr, &glyph_orientation_verticalAttr, &image_renderingAttr, + &SVGNames::heightAttr, &kerningAttr, &letter_spacingAttr, &lighting_colorAttr, @@ -120,7 +119,11 @@ static NEVER_INLINE void populateAttributeNameToCSSPropertyIDMap(HashMap<AtomicS &mask_typeAttr, &opacityAttr, &overflowAttr, + &paint_orderAttr, &pointer_eventsAttr, + &rAttr, + &rxAttr, + &ryAttr, &shape_renderingAttr, &stop_colorAttr, &stop_opacityAttr, @@ -138,22 +141,28 @@ static NEVER_INLINE void populateAttributeNameToCSSPropertyIDMap(HashMap<AtomicS &unicode_bidiAttr, &vector_effectAttr, &visibilityAttr, + &SVGNames::widthAttr, &word_spacingAttr, &writing_modeAttr, + &xAttr, + &yAttr, }; - for (unsigned i = 0; i < WTF_ARRAY_LENGTH(attributeNames); ++i) { - const AtomicString& localName = attributeNames[i]->localName(); + for (auto& name : attributeNames) { + const AtomicString& localName = name->localName(); map.add(localName.impl(), cssPropertyID(localName)); } // FIXME: When CSS supports "transform-origin" this special case can be removed, // and we can add transform_originAttr to the table above instead. - map.add(transform_originAttr.localName().impl(), CSSPropertyWebkitTransformOrigin); + map.add(transform_originAttr.localName().impl(), CSSPropertyTransformOrigin); } static NEVER_INLINE void populateAttributeNameToAnimatedPropertyTypeMap(HashMap<QualifiedName::QualifiedNameImpl*, AnimatedPropertyType>& map) { + using namespace HTMLNames; + using namespace SVGNames; + struct TableEntry { const QualifiedName& attributeName; AnimatedPropertyType type; @@ -197,6 +206,7 @@ static NEVER_INLINE void populateAttributeNameToAnimatedPropertyTypeMap(HashMap< { mask_typeAttr, AnimatedString }, { opacityAttr, AnimatedNumber }, { overflowAttr, AnimatedString }, + { paint_orderAttr, AnimatedString }, { pointer_eventsAttr, AnimatedString }, { shape_renderingAttr, AnimatedString }, { stop_colorAttr, AnimatedColor }, @@ -217,8 +227,8 @@ static NEVER_INLINE void populateAttributeNameToAnimatedPropertyTypeMap(HashMap< { word_spacingAttr, AnimatedLength }, }; - for (unsigned i = 0; i < WTF_ARRAY_LENGTH(table); ++i) - map.add(table[i].attributeName.impl(), table[i].type); + for (auto& entry : table) + map.add(entry.attributeName.impl(), entry.type); } static inline HashMap<QualifiedName::QualifiedNameImpl*, AnimatedPropertyType>& attributeNameToAnimatedPropertyTypeMap() @@ -229,6 +239,40 @@ static inline HashMap<QualifiedName::QualifiedNameImpl*, AnimatedPropertyType>& return map; } +static NEVER_INLINE void populateCSSPropertyWithSVGDOMNameToAnimatedPropertyTypeMap(HashMap<QualifiedName::QualifiedNameImpl*, AnimatedPropertyType>& map) +{ + using namespace HTMLNames; + using namespace SVGNames; + + struct TableEntry { + const QualifiedName& attributeName; + AnimatedPropertyType type; + }; + + static const TableEntry table[] = { + { cxAttr, AnimatedLength }, + { cyAttr, AnimatedLength }, + { rAttr, AnimatedLength }, + { rxAttr, AnimatedLength }, + { ryAttr, AnimatedLength }, + { SVGNames::heightAttr, AnimatedLength }, + { SVGNames::widthAttr, AnimatedLength }, + { xAttr, AnimatedLength }, + { yAttr, AnimatedLength }, + }; + + for (auto& entry : table) + map.add(entry.attributeName.impl(), entry.type); +} + +static inline HashMap<QualifiedName::QualifiedNameImpl*, AnimatedPropertyType>& cssPropertyWithSVGDOMNameToAnimatedPropertyTypeMap() +{ + static NeverDestroyed<HashMap<QualifiedName::QualifiedNameImpl*, AnimatedPropertyType>> map; + if (map.get().isEmpty()) + populateCSSPropertyWithSVGDOMNameToAnimatedPropertyTypeMap(map); + return map; +} + SVGElement::SVGElement(const QualifiedName& tagName, Document& document) : StyledElement(tagName, document, CreateSVGElement) { @@ -238,27 +282,32 @@ SVGElement::SVGElement(const QualifiedName& tagName, Document& document) SVGElement::~SVGElement() { if (m_svgRareData) { - m_svgRareData->destroyAnimatedSMILStyleProperties(); - if (SVGCursorElement* cursorElement = m_svgRareData->cursorElement()) - cursorElement->removeClient(this); - if (CSSCursorImageValue* cursorImageValue = m_svgRareData->cursorImageValue()) - cursorImageValue->removeReferencedElement(this); + for (SVGElement* instance : m_svgRareData->instances()) + instance->m_svgRareData->setCorrespondingElement(nullptr); + if (SVGElement* correspondingElement = m_svgRareData->correspondingElement()) + correspondingElement->m_svgRareData->instances().remove(this); m_svgRareData = nullptr; } - document().accessSVGExtensions()->rebuildAllElementReferencesForTarget(this); - document().accessSVGExtensions()->removeAllElementReferencesForTarget(this); + document().accessSVGExtensions().rebuildAllElementReferencesForTarget(*this); + document().accessSVGExtensions().removeAllElementReferencesForTarget(this); } -bool SVGElement::willRecalcStyle(Style::Change change) +int SVGElement::tabIndex() const { - if (!m_svgRareData || styleChangeType() == SyntheticStyleChange) - return true; + if (supportsFocus()) + return Element::tabIndex(); + return -1; +} + +void SVGElement::willRecalcStyle(Style::Change change) +{ + if (!m_svgRareData || styleResolutionShouldRecompositeLayer()) + return; // If the style changes because of a regular property change (not induced by SMIL animations themselves) // reset the "computed style without SMIL style properties", so the base value change gets reflected. if (change > Style::NoChange || needsStyleRecalc()) m_svgRareData->setNeedsOverrideComputedStyleUpdate(); - return true; } SVGElementRareData& SVGElement::ensureSVGRareData() @@ -270,7 +319,7 @@ SVGElementRareData& SVGElement::ensureSVGRareData() bool SVGElement::isOutermostSVGSVGElement() const { - if (!isSVGSVGElement(this)) + if (!is<SVGSVGElement>(*this)) return false; // If we're living in a shadow tree, we're a <svg> element that got created as replacement @@ -297,233 +346,161 @@ void SVGElement::reportAttributeParsingError(SVGParsingError error, const Qualif return; String errorString = "<" + tagName() + "> attribute " + name.toString() + "=\"" + value + "\""; - SVGDocumentExtensions* extensions = document().accessSVGExtensions(); + SVGDocumentExtensions& extensions = document().accessSVGExtensions(); if (error == NegativeValueForbiddenError) { - extensions->reportError("Invalid negative value for " + errorString); + extensions.reportError("Invalid negative value for " + errorString); return; } if (error == ParsingAttributeFailedError) { - extensions->reportError("Invalid value for " + errorString); + extensions.reportError("Invalid value for " + errorString); return; } ASSERT_NOT_REACHED(); } - -bool SVGElement::isSupported(StringImpl* feature, StringImpl* version) const -{ - return DOMImplementation::hasFeature(feature, version); -} - -String SVGElement::xmlbase() const -{ - return fastGetAttribute(XMLNames::baseAttr); -} - -void SVGElement::setXmlbase(const String& value, ExceptionCode&) -{ - setAttribute(XMLNames::baseAttr, value); -} - void SVGElement::removedFrom(ContainerNode& rootParent) { - bool wasInDocument = rootParent.inDocument(); + bool wasInDocument = rootParent.isConnected(); if (wasInDocument) updateRelativeLengthsInformation(false, this); StyledElement::removedFrom(rootParent); if (wasInDocument) { - document().accessSVGExtensions()->rebuildAllElementReferencesForTarget(this); - document().accessSVGExtensions()->removeAllElementReferencesForTarget(this); + document().accessSVGExtensions().clearTargetDependencies(*this); + document().accessSVGExtensions().removeAllElementReferencesForTarget(this); } - SVGElementInstance::invalidateAllInstancesOfElement(this); + invalidateInstances(); } SVGSVGElement* SVGElement::ownerSVGElement() const { - ContainerNode* n = parentOrShadowHostNode(); - while (n) { - if (n->hasTagName(SVGNames::svgTag)) - return toSVGSVGElement(n); + ContainerNode* node = parentOrShadowHostNode(); + while (node) { + if (is<SVGSVGElement>(*node)) + return downcast<SVGSVGElement>(node); - n = n->parentOrShadowHostNode(); + node = node->parentOrShadowHostNode(); } - return 0; + return nullptr; } SVGElement* SVGElement::viewportElement() const { // This function needs shadow tree support - as RenderSVGContainer uses this function // to determine the "overflow" property. <use> on <symbol> wouldn't work otherwhise. - ContainerNode* n = parentOrShadowHostNode(); - while (n) { - if (n->hasTagName(SVGNames::svgTag) || isSVGImageElement(n) || n->hasTagName(SVGNames::symbolTag)) - return toSVGElement(n); + ContainerNode* node = parentOrShadowHostNode(); + while (node) { + if (is<SVGSVGElement>(*node) || is<SVGImageElement>(*node) || node->hasTagName(SVGNames::symbolTag)) + return downcast<SVGElement>(node); - n = n->parentOrShadowHostNode(); + node = node->parentOrShadowHostNode(); } - return 0; -} - -SVGDocumentExtensions* SVGElement::accessDocumentSVGExtensions() -{ - // This function is provided for use by SVGAnimatedProperty to avoid - // global inclusion of Document.h in SVG code. - return document().accessSVGExtensions(); -} - -void SVGElement::mapInstanceToElement(SVGElementInstance* instance) -{ - ASSERT(instance); - - HashSet<SVGElementInstance*>& instances = ensureSVGRareData().elementInstances(); - ASSERT(!instances.contains(instance)); - - instances.add(instance); + return nullptr; } -void SVGElement::removeInstanceMapping(SVGElementInstance* instance) -{ - ASSERT(instance); - ASSERT(m_svgRareData); - - HashSet<SVGElementInstance*>& instances = m_svgRareData->elementInstances(); - ASSERT(instances.contains(instance)); - - instances.remove(instance); -} - -const HashSet<SVGElementInstance*>& SVGElement::instancesForElement() const +const HashSet<SVGElement*>& SVGElement::instances() const { if (!m_svgRareData) { - DEFINE_STATIC_LOCAL(HashSet<SVGElementInstance*>, emptyInstances, ()); + static NeverDestroyed<HashSet<SVGElement*>> emptyInstances; return emptyInstances; } - return m_svgRareData->elementInstances(); + return m_svgRareData->instances(); } bool SVGElement::getBoundingBox(FloatRect& rect, SVGLocatable::StyleUpdateStrategy styleUpdateStrategy) { - if (isSVGGraphicsElement()) { - rect = toSVGGraphicsElement(this)->getBBox(styleUpdateStrategy); + if (is<SVGGraphicsElement>(*this)) { + rect = downcast<SVGGraphicsElement>(*this).getBBox(styleUpdateStrategy); return true; } return false; } -void SVGElement::setCursorElement(SVGCursorElement* cursorElement) +SVGElement* SVGElement::correspondingElement() const { - SVGElementRareData& rareData = ensureSVGRareData(); - if (SVGCursorElement* oldCursorElement = rareData.cursorElement()) { - if (cursorElement == oldCursorElement) - return; - oldCursorElement->removeReferencedElement(this); - } - rareData.setCursorElement(cursorElement); + return m_svgRareData ? m_svgRareData->correspondingElement() : nullptr; } -void SVGElement::cursorElementRemoved() +SVGUseElement* SVGElement::correspondingUseElement() const { - ASSERT(m_svgRareData); - m_svgRareData->setCursorElement(0); + 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::setCursorImageValue(CSSCursorImageValue* cursorImageValue) +void SVGElement::setCorrespondingElement(SVGElement* correspondingElement) { - SVGElementRareData& rareData = ensureSVGRareData(); - if (CSSCursorImageValue* oldCursorImageValue = rareData.cursorImageValue()) { - if (cursorImageValue == oldCursorImageValue) - return; - oldCursorImageValue->removeReferencedElement(this); + if (m_svgRareData) { + if (SVGElement* oldCorrespondingElement = m_svgRareData->correspondingElement()) + oldCorrespondingElement->m_svgRareData->instances().remove(this); } - rareData.setCursorImageValue(cursorImageValue); + if (m_svgRareData || correspondingElement) + ensureSVGRareData().setCorrespondingElement(correspondingElement); + if (correspondingElement) + correspondingElement->ensureSVGRareData().instances().add(this); } -void SVGElement::cursorImageValueRemoved() +void SVGElement::parseAttribute(const QualifiedName& name, const AtomicString& value) { - ASSERT(m_svgRareData); - m_svgRareData->setCursorImageValue(0); -} + if (name == HTMLNames::classAttr) { + setClassNameBaseValue(value); + return; + } -SVGElement* SVGElement::correspondingElement() -{ - ASSERT(!m_svgRareData || !m_svgRareData->correspondingElement() || containingShadowRoot()); - return m_svgRareData ? m_svgRareData->correspondingElement() : 0; -} + if (name == HTMLNames::tabindexAttr) { + if (value.isEmpty()) + clearTabIndexExplicitlyIfNeeded(); + else if (std::optional<int> tabIndex = parseHTMLInteger(value)) + setTabIndexExplicitly(tabIndex.value()); + return; + } -void SVGElement::setCorrespondingElement(SVGElement* correspondingElement) -{ - ensureSVGRareData().setCorrespondingElement(correspondingElement); -} + auto& eventName = HTMLElement::eventNameForEventHandlerAttribute(name); + if (!eventName.isNull()) { + setAttributeEventListener(eventName, name, value); + return; + } -void SVGElement::parseAttribute(const QualifiedName& name, const AtomicString& value) -{ - // standard events - if (name == onloadAttr) - setAttributeEventListener(eventNames().loadEvent, name, value); - else if (name == onclickAttr) - setAttributeEventListener(eventNames().clickEvent, name, value); - else if (name == onmousedownAttr) - setAttributeEventListener(eventNames().mousedownEvent, name, value); - else if (name == onmouseenterAttr) - setAttributeEventListener(eventNames().mouseenterEvent, name, value); - else if (name == onmouseleaveAttr) - setAttributeEventListener(eventNames().mouseleaveEvent, name, value); - else if (name == onmousemoveAttr) - setAttributeEventListener(eventNames().mousemoveEvent, name, value); - else if (name == onmouseoutAttr) - setAttributeEventListener(eventNames().mouseoutEvent, name, value); - else if (name == onmouseoverAttr) - setAttributeEventListener(eventNames().mouseoverEvent, name, value); - else if (name == onmouseupAttr) - setAttributeEventListener(eventNames().mouseupEvent, name, value); - else if (name == SVGNames::onfocusinAttr) - setAttributeEventListener(eventNames().focusinEvent, name, value); - else if (name == SVGNames::onfocusoutAttr) - setAttributeEventListener(eventNames().focusoutEvent, name, value); - else if (name == SVGNames::onactivateAttr) - setAttributeEventListener(eventNames().DOMActivateEvent, name, value); - else if (name == HTMLNames::classAttr) - setClassNameBaseValue(value); -#if ENABLE(TOUCH_EVENTS) - else if (name == ontouchstartAttr) - setAttributeEventListener(eventNames().touchstartEvent, name, value); - else if (name == ontouchmoveAttr) - setAttributeEventListener(eventNames().touchmoveEvent, name, value); - else if (name == ontouchendAttr) - setAttributeEventListener(eventNames().touchendEvent, name, value); - else if (name == ontouchcancelAttr) - setAttributeEventListener(eventNames().touchcancelEvent, name, value); -#endif -#if ENABLE(IOS_GESTURE_EVENTS) - else if (name == ongesturestartAttr) - setAttributeEventListener(eventNames().gesturestartEvent, name, value); - else if (name == ongesturechangeAttr) - setAttributeEventListener(eventNames().gesturechangeEvent, name, value); - else if (name == ongestureendAttr) - setAttributeEventListener(eventNames().gestureendEvent, name, value); -#endif - else if (SVGLangSpace::parseAttribute(name, value)) { - } else - StyledElement::parseAttribute(name, value); + SVGLangSpace::parseAttribute(name, value); } -void SVGElement::animatedPropertyTypeForAttribute(const QualifiedName& attributeName, Vector<AnimatedPropertyType>& propertyTypes) +Vector<AnimatedPropertyType> SVGElement::animatedPropertyTypesForAttribute(const QualifiedName& attributeName) { - localAttributeToPropertyMap().animatedPropertyTypeForAttribute(attributeName, propertyTypes); - if (!propertyTypes.isEmpty()) - return; + auto types = localAttributeToPropertyMap().types(attributeName); + if (!types.isEmpty()) + return types; + + { + auto& map = attributeNameToAnimatedPropertyTypeMap(); + auto it = map.find(attributeName.impl()); + if (it != map.end()) { + types.append(it->value); + return types; + } + } + + { + auto& map = cssPropertyWithSVGDOMNameToAnimatedPropertyTypeMap(); + auto it = map.find(attributeName.impl()); + if (it != map.end()) { + types.append(it->value); + return types; + } + } - auto& map = attributeNameToAnimatedPropertyTypeMap(); - auto it = map.find(attributeName.impl()); - if (it != map.end()) - propertyTypes.append(it->value); + return types; } bool SVGElement::haveLoadedRequiredResources() @@ -535,82 +512,61 @@ bool SVGElement::haveLoadedRequiredResources() return true; } -static inline void collectInstancesForSVGElement(SVGElement* element, HashSet<SVGElementInstance*>& instances) -{ - ASSERT(element); - if (element->containingShadowRoot()) - return; - - ASSERT(!element->instanceUpdatesBlocked()); - - instances = element->instancesForElement(); -} - -bool SVGElement::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> prpListener, bool useCapture) -{ - RefPtr<EventListener> listener = prpListener; - +bool SVGElement::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, const AddEventListenerOptions& options) +{ // Add event listener to regular DOM element - if (!Node::addEventListener(eventType, listener, useCapture)) + if (!Node::addEventListener(eventType, listener.copyRef(), options)) return false; + if (containingShadowRoot()) + return true; + // Add event listener to all shadow tree DOM element instances - HashSet<SVGElementInstance*> instances; - collectInstancesForSVGElement(this, instances); - const HashSet<SVGElementInstance*>::const_iterator end = instances.end(); - for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) { - ASSERT((*it)->shadowTreeElement()); - ASSERT((*it)->correspondingElement() == this); - - bool result = (*it)->shadowTreeElement()->Node::addEventListener(eventType, listener, useCapture); + 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 AtomicString& eventType, EventListener* listener, bool useCapture) +bool SVGElement::removeEventListener(const AtomicString& eventType, EventListener& listener, const ListenerOptions& options) { - HashSet<SVGElementInstance*> instances; - collectInstancesForSVGElement(this, instances); - if (instances.isEmpty()) - return Node::removeEventListener(eventType, listener, useCapture); + if (containingShadowRoot()) + return Node::removeEventListener(eventType, listener, options); - // EventTarget::removeEventListener creates a PassRefPtr around the given EventListener + // EventTarget::removeEventListener creates a Ref around the given EventListener // object when creating a temporary RegisteredEventListener object used to look up the // event listener in a cache. If we want to be able to call removeEventListener() multiple // times on different nodes, we have to delay its immediate destruction, which would happen // after the first call below. - RefPtr<EventListener> protector(listener); + Ref<EventListener> protector(listener); // Remove event listener from regular DOM element - if (!Node::removeEventListener(eventType, listener, useCapture)) + if (!Node::removeEventListener(eventType, listener, options)) return false; // Remove event listener from all shadow tree DOM element instances - const HashSet<SVGElementInstance*>::const_iterator end = instances.end(); - for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) { - ASSERT((*it)->correspondingElement() == this); - - SVGElement* shadowTreeElement = (*it)->shadowTreeElement(); - ASSERT(shadowTreeElement); + ASSERT(!instanceUpdatesBlocked()); + for (auto& instance : instances()) { + ASSERT(instance->correspondingElement() == this); - if (shadowTreeElement->Node::removeEventListener(eventType, listener, useCapture)) + if (instance->Node::removeEventListener(eventType, listener, options)) continue; // This case can only be hit for event listeners created from markup - ASSERT(listener->wasCreatedFromMarkup()); + ASSERT(listener.wasCreatedFromMarkup()); // If the event listener 'listener' has been created from markup and has been fired before // then JSLazyEventListener::parseCode() has been called and m_jsFunction of that listener // has been created (read: it's not 0 anymore). During shadow tree creation, the event // listener DOM attribute has been cloned, and another event listener has been setup in // the shadow tree. If that event listener has not been used yet, m_jsFunction is still 0, - // and tryRemoveEventListener() above will fail. Work around that very seldom problem. - EventTargetData* data = shadowTreeElement->eventTargetData(); - ASSERT(data); - - data->eventListenerMap.removeFirstEventListenerCreatedFromMarkup(eventType); + // and tryRemoveEventListener() above will fail. Work around that very rare problem. + ASSERT(instance->eventTargetData()); + instance->eventTargetData()->eventListenerMap.removeFirstEventListenerCreatedFromMarkup(eventType); } return true; @@ -622,11 +578,8 @@ static bool hasLoadListener(Element* element) return true; for (element = element->parentOrShadowHostElement(); element; element = element->parentOrShadowHostElement()) { - const EventListenerVector& entry = element->getEventListeners(eventNames().loadEvent); - for (size_t i = 0; i < entry.size(); ++i) { - if (entry[i].useCapture) - return true; - } + if (element->hasCapturingEventListeners(eventNames().loadEvent)) + return true; } return false; @@ -642,6 +595,9 @@ bool SVGElement::shouldMoveToFlowThread(const RenderStyle& styleToUse) const void SVGElement::sendSVGLoadEventIfPossible(bool sendParentLoadEvents) { + if (!isConnected() || !document().frame()) + return; + RefPtr<SVGElement> currentTarget = this; while (currentTarget && currentTarget->haveLoadedRequiredResources()) { RefPtr<Element> parent; @@ -672,12 +628,12 @@ void SVGElement::sendSVGLoadEventIfPossibleAsynchronously() svgLoadEventTimer()->startOneShot(0); } -void SVGElement::svgLoadEventTimerFired(Timer<SVGElement>*) +void SVGElement::svgLoadEventTimerFired() { sendSVGLoadEventIfPossible(); } -Timer<SVGElement>* SVGElement::svgLoadEventTimer() +Timer* SVGElement::svgLoadEventTimer() { ASSERT_NOT_REACHED(); return 0; @@ -694,23 +650,27 @@ void SVGElement::finishParsingChildren() // finishParsingChildren() is called when the close tag is reached for an element (e.g. </svg>) // we send SVGLoad events here if we can, otherwise they'll be sent when any required loads finish sendSVGLoadEventIfPossible(); + + // Notify all the elements which have references to this element to rebuild their shadow and render + // trees, e.g. a <use> element references a target element before this target element is defined. + invalidateInstances(); } bool SVGElement::childShouldCreateRenderer(const Node& child) const { - DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, invalidTextContent, ()); + static NeverDestroyed<HashSet<QualifiedName>> invalidTextContent; - if (invalidTextContent.isEmpty()) { - invalidTextContent.add(SVGNames::textPathTag); + if (invalidTextContent.get().isEmpty()) { + invalidTextContent.get().add(SVGNames::textPathTag); #if ENABLE(SVG_FONTS) - invalidTextContent.add(SVGNames::altGlyphTag); + invalidTextContent.get().add(SVGNames::altGlyphTag); #endif - invalidTextContent.add(SVGNames::trefTag); - invalidTextContent.add(SVGNames::tspanTag); + invalidTextContent.get().add(SVGNames::trefTag); + invalidTextContent.get().add(SVGNames::tspanTag); } if (child.isSVGElement()) { - const SVGElement& svgChild = toSVGElement(child); - if (invalidTextContent.contains(svgChild.tagQName())) + const SVGElement& svgChild = downcast<SVGElement>(child); + if (invalidTextContent.get().contains(svgChild.tagQName())) return false; return svgChild.isValid(); @@ -722,8 +682,8 @@ void SVGElement::attributeChanged(const QualifiedName& name, const AtomicString& { StyledElement::attributeChanged(name, oldValue, newValue); - if (isIdAttributeName(name)) - document().accessSVGExtensions()->rebuildAllElementReferencesForTarget(this); + if (name == HTMLNames::idAttr) + document().accessSVGExtensions().rebuildAllElementReferencesForTarget(*this); // Changes to the style attribute are processed lazily (see Element::getAttribute() and related methods), // so we don't want changes to the style attribute to result in extra work here. @@ -731,17 +691,25 @@ void SVGElement::attributeChanged(const QualifiedName& name, const AtomicString& svgAttributeChanged(name); } +void SVGElement::synchronizeAllAnimatedSVGAttribute(SVGElement* svgElement) +{ + ASSERT(svgElement->elementData()); + ASSERT(svgElement->elementData()->animatedSVGAttributesAreDirty()); + + svgElement->localAttributeToPropertyMap().synchronizeProperties(*svgElement); + svgElement->elementData()->setAnimatedSVGAttributesAreDirty(false); +} + void SVGElement::synchronizeAnimatedSVGAttribute(const QualifiedName& name) const { if (!elementData() || !elementData()->animatedSVGAttributesAreDirty()) return; SVGElement* nonConstThis = const_cast<SVGElement*>(this); - if (name == anyQName()) { - nonConstThis->localAttributeToPropertyMap().synchronizeProperties(nonConstThis); - elementData()->setAnimatedSVGAttributesAreDirty(false); - } else - nonConstThis->localAttributeToPropertyMap().synchronizeProperty(nonConstThis, name); + if (name == anyQName()) + synchronizeAllAnimatedSVGAttribute(nonConstThis); + else + nonConstThis->localAttributeToPropertyMap().synchronizeProperty(*nonConstThis, name); } void SVGElement::synchronizeRequiredFeatures(SVGElement* contextElement) @@ -762,18 +730,13 @@ void SVGElement::synchronizeSystemLanguage(SVGElement* contextElement) contextElement->synchronizeSystemLanguage(); } -PassRefPtr<RenderStyle> SVGElement::customStyleForRenderer() +std::optional<ElementStyle> SVGElement::resolveCustomStyle(const RenderStyle& parentStyle, const RenderStyle*) { - if (!correspondingElement()) - return document().ensureStyleResolver().styleForElement(this); - - RenderStyle* style = 0; - if (Element* parent = parentOrShadowHostElement()) { - if (auto renderer = parent->renderer()) - style = &renderer->style(); - } + // If the element is in a <use> tree we get the style from the definition tree. + if (auto* styleElement = this->correspondingElement()) + return styleElement->resolveStyle(&parentStyle); - return document().ensureStyleResolver().styleForElement(correspondingElement(), style, DisallowStyleSharing); + return resolveStyle(&parentStyle); } MutableStyleProperties* SVGElement::animatedSMILStyleProperties() const @@ -794,155 +757,152 @@ void SVGElement::setUseOverrideComputedStyle(bool value) m_svgRareData->setUseOverrideComputedStyle(value); } -RenderStyle* SVGElement::computedStyle(PseudoId pseudoElementSpecifier) +const RenderStyle* SVGElement::computedStyle(PseudoId pseudoElementSpecifier) { if (!m_svgRareData || !m_svgRareData->useOverrideComputedStyle()) return Element::computedStyle(pseudoElementSpecifier); - RenderStyle* parentStyle = nullptr; + const RenderStyle* parentStyle = nullptr; if (Element* parent = parentOrShadowHostElement()) { if (auto renderer = parent->renderer()) parentStyle = &renderer->style(); } - return m_svgRareData->overrideComputedStyle(this, parentStyle); + return m_svgRareData->overrideComputedStyle(*this, parentStyle); } -#ifndef NDEBUG -bool SVGElement::isAnimatableAttribute(const QualifiedName& name) const +static void addQualifiedName(HashMap<AtomicString, QualifiedName>& map, const QualifiedName& name) { - DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, animatableAttributes, ()); + HashMap<AtomicString, QualifiedName>::AddResult addResult = map.add(name.localName(), name); + ASSERT_UNUSED(addResult, addResult.isNewEntry); +} + +QualifiedName SVGElement::animatableAttributeForName(const AtomicString& localName) +{ + static NeverDestroyed<HashMap<AtomicString, QualifiedName>> neverDestroyedAnimatableAttributes; + HashMap<AtomicString, QualifiedName>& animatableAttributes = neverDestroyedAnimatableAttributes; if (animatableAttributes.isEmpty()) { - animatableAttributes.add(XLinkNames::hrefAttr); - animatableAttributes.add(SVGNames::amplitudeAttr); - animatableAttributes.add(SVGNames::azimuthAttr); - animatableAttributes.add(SVGNames::baseFrequencyAttr); - animatableAttributes.add(SVGNames::biasAttr); - animatableAttributes.add(SVGNames::clipPathUnitsAttr); - animatableAttributes.add(SVGNames::cxAttr); - animatableAttributes.add(SVGNames::cyAttr); - animatableAttributes.add(SVGNames::diffuseConstantAttr); - animatableAttributes.add(SVGNames::divisorAttr); - animatableAttributes.add(SVGNames::dxAttr); - animatableAttributes.add(SVGNames::dyAttr); - animatableAttributes.add(SVGNames::edgeModeAttr); - animatableAttributes.add(SVGNames::elevationAttr); - animatableAttributes.add(SVGNames::exponentAttr); - animatableAttributes.add(SVGNames::externalResourcesRequiredAttr); - animatableAttributes.add(SVGNames::filterResAttr); - animatableAttributes.add(SVGNames::filterUnitsAttr); - animatableAttributes.add(SVGNames::fxAttr); - animatableAttributes.add(SVGNames::fyAttr); - animatableAttributes.add(SVGNames::gradientTransformAttr); - animatableAttributes.add(SVGNames::gradientUnitsAttr); - animatableAttributes.add(SVGNames::heightAttr); - animatableAttributes.add(SVGNames::in2Attr); - animatableAttributes.add(SVGNames::inAttr); - animatableAttributes.add(SVGNames::interceptAttr); - animatableAttributes.add(SVGNames::k1Attr); - animatableAttributes.add(SVGNames::k2Attr); - animatableAttributes.add(SVGNames::k3Attr); - animatableAttributes.add(SVGNames::k4Attr); - animatableAttributes.add(SVGNames::kernelMatrixAttr); - animatableAttributes.add(SVGNames::kernelUnitLengthAttr); - animatableAttributes.add(SVGNames::lengthAdjustAttr); - animatableAttributes.add(SVGNames::limitingConeAngleAttr); - animatableAttributes.add(SVGNames::markerHeightAttr); - animatableAttributes.add(SVGNames::markerUnitsAttr); - animatableAttributes.add(SVGNames::markerWidthAttr); - animatableAttributes.add(SVGNames::maskContentUnitsAttr); - animatableAttributes.add(SVGNames::maskUnitsAttr); - animatableAttributes.add(SVGNames::methodAttr); - animatableAttributes.add(SVGNames::modeAttr); - animatableAttributes.add(SVGNames::numOctavesAttr); - animatableAttributes.add(SVGNames::offsetAttr); - animatableAttributes.add(SVGNames::operatorAttr); - animatableAttributes.add(SVGNames::orderAttr); - animatableAttributes.add(SVGNames::orientAttr); - animatableAttributes.add(SVGNames::pathLengthAttr); - animatableAttributes.add(SVGNames::patternContentUnitsAttr); - animatableAttributes.add(SVGNames::patternTransformAttr); - animatableAttributes.add(SVGNames::patternUnitsAttr); - animatableAttributes.add(SVGNames::pointsAtXAttr); - animatableAttributes.add(SVGNames::pointsAtYAttr); - animatableAttributes.add(SVGNames::pointsAtZAttr); - animatableAttributes.add(SVGNames::preserveAlphaAttr); - animatableAttributes.add(SVGNames::preserveAspectRatioAttr); - animatableAttributes.add(SVGNames::primitiveUnitsAttr); - animatableAttributes.add(SVGNames::radiusAttr); - animatableAttributes.add(SVGNames::rAttr); - animatableAttributes.add(SVGNames::refXAttr); - animatableAttributes.add(SVGNames::refYAttr); - animatableAttributes.add(SVGNames::resultAttr); - animatableAttributes.add(SVGNames::rotateAttr); - animatableAttributes.add(SVGNames::rxAttr); - animatableAttributes.add(SVGNames::ryAttr); - animatableAttributes.add(SVGNames::scaleAttr); - animatableAttributes.add(SVGNames::seedAttr); - animatableAttributes.add(SVGNames::slopeAttr); - animatableAttributes.add(SVGNames::spacingAttr); - animatableAttributes.add(SVGNames::specularConstantAttr); - animatableAttributes.add(SVGNames::specularExponentAttr); - animatableAttributes.add(SVGNames::spreadMethodAttr); - animatableAttributes.add(SVGNames::startOffsetAttr); - animatableAttributes.add(SVGNames::stdDeviationAttr); - animatableAttributes.add(SVGNames::stitchTilesAttr); - animatableAttributes.add(SVGNames::surfaceScaleAttr); - animatableAttributes.add(SVGNames::tableValuesAttr); - animatableAttributes.add(SVGNames::targetAttr); - animatableAttributes.add(SVGNames::targetXAttr); - animatableAttributes.add(SVGNames::targetYAttr); - animatableAttributes.add(SVGNames::transformAttr); - animatableAttributes.add(SVGNames::typeAttr); - animatableAttributes.add(SVGNames::valuesAttr); - animatableAttributes.add(SVGNames::viewBoxAttr); - animatableAttributes.add(SVGNames::widthAttr); - animatableAttributes.add(SVGNames::x1Attr); - animatableAttributes.add(SVGNames::x2Attr); - animatableAttributes.add(SVGNames::xAttr); - animatableAttributes.add(SVGNames::xChannelSelectorAttr); - animatableAttributes.add(SVGNames::y1Attr); - animatableAttributes.add(SVGNames::y2Attr); - animatableAttributes.add(SVGNames::yAttr); - animatableAttributes.add(SVGNames::yChannelSelectorAttr); - animatableAttributes.add(SVGNames::zAttr); + addQualifiedName(animatableAttributes, HTMLNames::classAttr); + addQualifiedName(animatableAttributes, SVGNames::amplitudeAttr); + addQualifiedName(animatableAttributes, SVGNames::azimuthAttr); + addQualifiedName(animatableAttributes, SVGNames::baseFrequencyAttr); + addQualifiedName(animatableAttributes, SVGNames::biasAttr); + addQualifiedName(animatableAttributes, SVGNames::clipPathUnitsAttr); + addQualifiedName(animatableAttributes, SVGNames::cxAttr); + addQualifiedName(animatableAttributes, SVGNames::cyAttr); + addQualifiedName(animatableAttributes, SVGNames::diffuseConstantAttr); + addQualifiedName(animatableAttributes, SVGNames::divisorAttr); + addQualifiedName(animatableAttributes, SVGNames::dxAttr); + addQualifiedName(animatableAttributes, SVGNames::dyAttr); + addQualifiedName(animatableAttributes, SVGNames::edgeModeAttr); + addQualifiedName(animatableAttributes, SVGNames::elevationAttr); + addQualifiedName(animatableAttributes, SVGNames::exponentAttr); + addQualifiedName(animatableAttributes, SVGNames::externalResourcesRequiredAttr); + addQualifiedName(animatableAttributes, SVGNames::filterResAttr); + addQualifiedName(animatableAttributes, SVGNames::filterUnitsAttr); + addQualifiedName(animatableAttributes, SVGNames::fxAttr); + addQualifiedName(animatableAttributes, SVGNames::fyAttr); + addQualifiedName(animatableAttributes, SVGNames::gradientTransformAttr); + addQualifiedName(animatableAttributes, SVGNames::gradientUnitsAttr); + addQualifiedName(animatableAttributes, SVGNames::heightAttr); + addQualifiedName(animatableAttributes, SVGNames::in2Attr); + addQualifiedName(animatableAttributes, SVGNames::inAttr); + addQualifiedName(animatableAttributes, SVGNames::interceptAttr); + addQualifiedName(animatableAttributes, SVGNames::k1Attr); + addQualifiedName(animatableAttributes, SVGNames::k2Attr); + addQualifiedName(animatableAttributes, SVGNames::k3Attr); + addQualifiedName(animatableAttributes, SVGNames::k4Attr); + addQualifiedName(animatableAttributes, SVGNames::kernelMatrixAttr); + addQualifiedName(animatableAttributes, SVGNames::kernelUnitLengthAttr); + addQualifiedName(animatableAttributes, SVGNames::lengthAdjustAttr); + addQualifiedName(animatableAttributes, SVGNames::limitingConeAngleAttr); + addQualifiedName(animatableAttributes, SVGNames::markerHeightAttr); + addQualifiedName(animatableAttributes, SVGNames::markerUnitsAttr); + addQualifiedName(animatableAttributes, SVGNames::markerWidthAttr); + addQualifiedName(animatableAttributes, SVGNames::maskContentUnitsAttr); + addQualifiedName(animatableAttributes, SVGNames::maskUnitsAttr); + addQualifiedName(animatableAttributes, SVGNames::methodAttr); + addQualifiedName(animatableAttributes, SVGNames::modeAttr); + addQualifiedName(animatableAttributes, SVGNames::numOctavesAttr); + addQualifiedName(animatableAttributes, SVGNames::offsetAttr); + addQualifiedName(animatableAttributes, SVGNames::operatorAttr); + addQualifiedName(animatableAttributes, SVGNames::orderAttr); + addQualifiedName(animatableAttributes, SVGNames::orientAttr); + addQualifiedName(animatableAttributes, SVGNames::pathLengthAttr); + addQualifiedName(animatableAttributes, SVGNames::patternContentUnitsAttr); + addQualifiedName(animatableAttributes, SVGNames::patternTransformAttr); + addQualifiedName(animatableAttributes, SVGNames::patternUnitsAttr); + addQualifiedName(animatableAttributes, SVGNames::pointsAtXAttr); + addQualifiedName(animatableAttributes, SVGNames::pointsAtYAttr); + addQualifiedName(animatableAttributes, SVGNames::pointsAtZAttr); + addQualifiedName(animatableAttributes, SVGNames::preserveAlphaAttr); + addQualifiedName(animatableAttributes, SVGNames::preserveAspectRatioAttr); + addQualifiedName(animatableAttributes, SVGNames::primitiveUnitsAttr); + addQualifiedName(animatableAttributes, SVGNames::radiusAttr); + addQualifiedName(animatableAttributes, SVGNames::rAttr); + addQualifiedName(animatableAttributes, SVGNames::refXAttr); + addQualifiedName(animatableAttributes, SVGNames::refYAttr); + addQualifiedName(animatableAttributes, SVGNames::resultAttr); + addQualifiedName(animatableAttributes, SVGNames::rotateAttr); + addQualifiedName(animatableAttributes, SVGNames::rxAttr); + addQualifiedName(animatableAttributes, SVGNames::ryAttr); + addQualifiedName(animatableAttributes, SVGNames::scaleAttr); + addQualifiedName(animatableAttributes, SVGNames::seedAttr); + addQualifiedName(animatableAttributes, SVGNames::slopeAttr); + addQualifiedName(animatableAttributes, SVGNames::spacingAttr); + addQualifiedName(animatableAttributes, SVGNames::specularConstantAttr); + addQualifiedName(animatableAttributes, SVGNames::specularExponentAttr); + addQualifiedName(animatableAttributes, SVGNames::spreadMethodAttr); + addQualifiedName(animatableAttributes, SVGNames::startOffsetAttr); + addQualifiedName(animatableAttributes, SVGNames::stdDeviationAttr); + addQualifiedName(animatableAttributes, SVGNames::stitchTilesAttr); + addQualifiedName(animatableAttributes, SVGNames::surfaceScaleAttr); + addQualifiedName(animatableAttributes, SVGNames::tableValuesAttr); + addQualifiedName(animatableAttributes, SVGNames::targetAttr); + addQualifiedName(animatableAttributes, SVGNames::targetXAttr); + addQualifiedName(animatableAttributes, SVGNames::targetYAttr); + addQualifiedName(animatableAttributes, SVGNames::transformAttr); + addQualifiedName(animatableAttributes, SVGNames::typeAttr); + addQualifiedName(animatableAttributes, SVGNames::valuesAttr); + addQualifiedName(animatableAttributes, SVGNames::viewBoxAttr); + addQualifiedName(animatableAttributes, SVGNames::widthAttr); + addQualifiedName(animatableAttributes, SVGNames::x1Attr); + addQualifiedName(animatableAttributes, SVGNames::x2Attr); + addQualifiedName(animatableAttributes, SVGNames::xAttr); + addQualifiedName(animatableAttributes, SVGNames::xChannelSelectorAttr); + addQualifiedName(animatableAttributes, SVGNames::y1Attr); + addQualifiedName(animatableAttributes, SVGNames::y2Attr); + addQualifiedName(animatableAttributes, SVGNames::yAttr); + addQualifiedName(animatableAttributes, SVGNames::yChannelSelectorAttr); + addQualifiedName(animatableAttributes, SVGNames::zAttr); + addQualifiedName(animatableAttributes, XLinkNames::hrefAttr); } + return animatableAttributes.get(localName); +} - if (name == classAttr) - return true; +#ifndef NDEBUG +bool SVGElement::isAnimatableAttribute(const QualifiedName& name) const +{ + if (SVGElement::animatableAttributeForName(name.localName()) == name) + return !filterOutAnimatableAttribute(name); + return false; +} - return animatableAttributes.contains(name); +bool SVGElement::filterOutAnimatableAttribute(const QualifiedName&) const +{ + return false; } #endif String SVGElement::title() const { - // According to spec, we should not return titles when hovering over root <svg> elements (those - // <title> elements are the title of the document, not a tooltip) so we instantly return. - if (isOutermostSVGSVGElement()) + // According to spec, for stand-alone SVG documents we should not return a title when + // hovering over the rootmost SVG element (the first <title> element is the title of + // the document, not a tooltip) so we instantly return. + if (isOutermostSVGSVGElement() && document().topDocument().isSVGDocument()) return String(); - - // Walk up the tree, to find out whether we're inside a <use> shadow tree, to find the right title. - if (isInShadowTree()) { - Element* shadowHostElement = toShadowRoot(treeScope().rootNode())->hostElement(); - // At this time, SVG nodes are not allowed in non-<use> shadow trees, so any shadow root we do - // have should be a use. The assert and following test is here to catch future shadow DOM changes - // that do enable SVG in a shadow tree. - ASSERT(!shadowHostElement || shadowHostElement->hasTagName(SVGNames::useTag)); - if (shadowHostElement && shadowHostElement->hasTagName(SVGNames::useTag)) { - SVGUseElement* useElement = toSVGUseElement(shadowHostElement); - - // If the <use> title is not empty we found the title to use. - String useTitle(useElement->title()); - if (!useTitle.isEmpty()) - return useTitle; - } - } - - // If we aren't an instance in a <use> or the <use> title was not found, then find the first - // <title> child of this element. - auto firstTitle = descendantsOfType<SVGTitleElement>(*this).first(); + auto firstTitle = childrenOfType<SVGTitleElement>(*this).first(); return firstTitle ? const_cast<SVGTitleElement*>(firstTitle)->innerText() : String(); } @@ -973,7 +933,13 @@ CSSPropertyID SVGElement::cssPropertyIdForSVGAttributeName(const QualifiedName& bool SVGElement::isAnimatableCSSProperty(const QualifiedName& attributeName) { - return attributeNameToAnimatedPropertyTypeMap().contains(attributeName.impl()); + return attributeNameToAnimatedPropertyTypeMap().contains(attributeName.impl()) + || cssPropertyWithSVGDOMNameToAnimatedPropertyTypeMap().contains(attributeName.impl()); +} + +bool SVGElement::isPresentationAttributeWithSVGDOM(const QualifiedName& attributeName) +{ + return !localAttributeToPropertyMap().types(attributeName).isEmpty(); } bool SVGElement::isPresentationAttribute(const QualifiedName& name) const @@ -992,31 +958,31 @@ void SVGElement::collectStyleForPresentationAttribute(const QualifiedName& name, bool SVGElement::isKnownAttribute(const QualifiedName& attrName) { - return isIdAttributeName(attrName); + return attrName == HTMLNames::idAttr; } void SVGElement::svgAttributeChanged(const QualifiedName& attrName) { CSSPropertyID propId = cssPropertyIdForSVGAttributeName(attrName); if (propId > 0) { - SVGElementInstance::invalidateAllInstancesOfElement(this); + invalidateInstances(); return; } if (attrName == HTMLNames::classAttr) { classAttributeChanged(className()); - SVGElementInstance::invalidateAllInstancesOfElement(this); + invalidateInstances(); return; } - if (isIdAttributeName(attrName)) { - RenderObject* object = renderer(); + if (attrName == HTMLNames::idAttr) { + auto renderer = this->renderer(); // Notify resources about id changes, this is important as we cache resources by id in SVGDocumentExtensions - if (object && object->isSVGResourceContainer()) - object->toRenderSVGResourceContainer()->idChanged(); - if (inDocument()) + if (is<RenderSVGResourceContainer>(renderer)) + downcast<RenderSVGResourceContainer>(*renderer).idChanged(); + if (isConnected()) buildPendingResourcesIfNeeded(); - SVGElementInstance::invalidateAllInstancesOfElement(this); + invalidateInstances(); return; } } @@ -1031,23 +997,23 @@ Node::InsertionNotificationRequest SVGElement::insertedInto(ContainerNode& rootP void SVGElement::buildPendingResourcesIfNeeded() { - if (!needsPendingResourceHandling() || !inDocument() || isInShadowTree()) + if (!needsPendingResourceHandling() || !isConnected() || isInShadowTree()) return; - SVGDocumentExtensions* extensions = document().accessSVGExtensions(); + SVGDocumentExtensions& extensions = document().accessSVGExtensions(); String resourceId = getIdAttribute(); - if (!extensions->isIdOfPendingResource(resourceId)) + if (!extensions.isIdOfPendingResource(resourceId)) return; // Mark pending resources as pending for removal. - extensions->markPendingResourcesForRemoval(resourceId); + extensions.markPendingResourcesForRemoval(resourceId); // Rebuild pending resources for each client of a pending resource that is being removed. - while (Element* clientElement = extensions->removeElementFromPendingResourcesForRemovalMap(resourceId)) { + while (Element* clientElement = extensions.removeElementFromPendingResourcesForRemovalMap(resourceId)) { ASSERT(clientElement->hasPendingResources()); if (clientElement->hasPendingResources()) { clientElement->buildPendingResource(); - extensions->clearHasPendingResourcesIfPossible(clientElement); + extensions.clearHasPendingResourcesIfPossible(clientElement); } } } @@ -1058,10 +1024,10 @@ void SVGElement::childrenChanged(const ChildChange& change) if (change.source == ChildChangeSourceParser) return; - SVGElementInstance::invalidateAllInstancesOfElement(this); + invalidateInstances(); } -PassRefPtr<CSSValue> SVGElement::getPresentationAttribute(const String& name) +RefPtr<DeprecatedCSSOMValue> SVGElement::getPresentationAttribute(const String& name) { if (!hasAttributesWithoutUpdate()) return 0; @@ -1074,8 +1040,10 @@ PassRefPtr<CSSValue> SVGElement::getPresentationAttribute(const String& name) RefPtr<MutableStyleProperties> style = MutableStyleProperties::create(SVGAttributeMode); CSSPropertyID propertyID = cssPropertyIdForSVGAttributeName(attribute->name()); style->setProperty(propertyID, attribute->value()); - RefPtr<CSSValue> cssValue = style->getPropertyCSSValue(propertyID); - return cssValue ? cssValue->cloneForCSSOM() : 0; + auto cssValue = style->getPropertyCSSValue(propertyID); + if (!cssValue) + return nullptr; + return cssValue->createDeprecatedCSSOMWrapper(); } bool SVGElement::instanceUpdatesBlocked() const @@ -1085,6 +1053,10 @@ bool SVGElement::instanceUpdatesBlocked() const void SVGElement::setInstanceUpdatesBlocked(bool value) { + // Catch any callers that calls setInstanceUpdatesBlocked(true) twice in a row. + // That probably indicates nested use of InstanceUpdateBlocker and a bug. + ASSERT(!value || !instanceUpdatesBlocked()); + if (m_svgRareData) m_svgRareData->setInstanceUpdatesBlocked(value); } @@ -1098,7 +1070,7 @@ AffineTransform SVGElement::localCoordinateSpaceTransform(SVGLocatable::CTMScope void SVGElement::updateRelativeLengthsInformation(bool hasRelativeLengths, SVGElement* element) { // If we're not yet in a document, this function will be called again from insertedInto(). Do nothing now. - if (!inDocument()) + if (!isConnected()) return; // An element wants to notify us that its own relative lengths state changed. @@ -1126,22 +1098,29 @@ void SVGElement::updateRelativeLengthsInformation(bool hasRelativeLengths, SVGEl break; // Register us in the parent element map. - toSVGElement(node)->updateRelativeLengthsInformation(hasRelativeLengths, this); + downcast<SVGElement>(*node).updateRelativeLengthsInformation(hasRelativeLengths, this); break; } } -bool SVGElement::isMouseFocusable() const +bool SVGElement::hasFocusEventListeners() const { - if (!isFocusable()) - return false; Element* eventTarget = const_cast<SVGElement*>(this); - return eventTarget->hasEventListeners(eventNames().focusinEvent) || eventTarget->hasEventListeners(eventNames().focusoutEvent); + return eventTarget->hasEventListeners(eventNames().focusinEvent) + || eventTarget->hasEventListeners(eventNames().focusoutEvent) + || eventTarget->hasEventListeners(eventNames().focusEvent) + || eventTarget->hasEventListeners(eventNames().blurEvent); } -bool SVGElement::isKeyboardFocusable(KeyboardEvent*) const +bool SVGElement::isMouseFocusable() const { - return isMouseFocusable(); + if (!isFocusable()) + return false; + Element* eventTarget = const_cast<SVGElement*>(this); + return hasFocusEventListeners() + || eventTarget->hasEventListeners(eventNames().keydownEvent) + || eventTarget->hasEventListeners(eventNames().keyupEvent) + || eventTarget->hasEventListeners(eventNames().keypressEvent); } void SVGElement::accessKeyAction(bool sendMouseEvents) @@ -1149,6 +1128,18 @@ void SVGElement::accessKeyAction(bool sendMouseEvents) dispatchSimulatedClick(0, sendMouseEvents ? SendMouseUpDownEvents : SendNoEvents); } +void SVGElement::invalidateInstances() +{ + if (instanceUpdatesBlocked()) + return; + + auto& instances = this->instances(); + while (!instances.isEmpty()) { + SVGElement* instance = *instances.begin(); + if (SVGUseElement* useElement = instance->correspondingUseElement()) + useElement->invalidateShadowTree(); + instance->setCorrespondingElement(nullptr); + } while (!instances.isEmpty()); } -#endif // ENABLE(SVG) +} |