#include "config.h"
#if ENABLE(SVG)
#include "SVGElement.h"
#include "Attribute.h"
#include "CSSCursorImageValue.h"
#include "DOMImplementation.h"
#include "Document.h"
#include "Event.h"
#include "EventListener.h"
#include "EventNames.h"
#include "FrameView.h"
#include "HTMLNames.h"
#include "NodeRenderingContext.h"
#include "RegisteredEventListener.h"
#include "RenderObject.h"
#include "ShadowRoot.h"
#include "SVGCursorElement.h"
#include "SVGDocumentExtensions.h"
#include "SVGElementInstance.h"
#include "SVGElementRareData.h"
#include "SVGNames.h"
#include "SVGSVGElement.h"
#include "SVGStyledLocatableElement.h"
#include "SVGTextElement.h"
#include "SVGURIReference.h"
#include "SVGUseElement.h"
#include "ScriptEventListener.h"
#include "StyleResolver.h"
#include "XMLNames.h"
namespace WebCore {
using namespace HTMLNames;
SVGElement::SVGElement(const QualifiedName& tagName, Document* document, ConstructionType constructionType)
: StyledElement(tagName, document, constructionType)
{
setHasCustomStyleForRenderer();
setHasCustomWillOrDidRecalcStyle();
}
PassRefPtr<SVGElement> SVGElement::create(const QualifiedName& tagName, Document* document)
{
return adoptRef(new SVGElement(tagName, document));
}
SVGElement::~SVGElement()
{
if (!hasRareSVGData())
ASSERT(!SVGElementRareData::rareDataMap().contains(this));
else {
SVGElementRareData::SVGElementRareDataMap& rareDataMap = SVGElementRareData::rareDataMap();
SVGElementRareData::SVGElementRareDataMap::iterator it = rareDataMap.find(this);
ASSERT(it != rareDataMap.end());
SVGElementRareData* rareData = it->second;
rareData->destroyAnimatedSMILStyleProperties();
if (SVGCursorElement* cursorElement = rareData->cursorElement())
cursorElement->removeClient(this);
if (CSSCursorImageValue* cursorImageValue = rareData->cursorImageValue())
cursorImageValue->removeReferencedElement(this);
delete rareData;
rareDataMap.remove(it);
}
document()->accessSVGExtensions()->removeAllAnimationElementsFromTarget(this);
document()->accessSVGExtensions()->removeAllElementReferencesForTarget(this);
}
bool SVGElement::willRecalcStyle(StyleChange change)
{
if (!hasRareSVGData() || styleChangeType() == SyntheticStyleChange)
return true;
if (change > NoChange || needsStyleRecalc())
rareSVGData()->setNeedsOverrideComputedStyleUpdate();
return true;
}
SVGElementRareData* SVGElement::rareSVGData() const
{
ASSERT(hasRareSVGData());
return SVGElementRareData::rareDataFromMap(this);
}
SVGElementRareData* SVGElement::ensureRareSVGData()
{
if (hasRareSVGData())
return rareSVGData();
ASSERT(!SVGElementRareData::rareDataMap().contains(this));
SVGElementRareData* data = new SVGElementRareData;
SVGElementRareData::rareDataMap().set(this, data);
setHasRareSVGData();
return data;
}
bool SVGElement::isOutermostSVGSVGElement() const
{
if (!hasTagName(SVGNames::svgTag))
return false;
if (isInShadowTree() && parentOrHostElement() && parentOrHostElement()->isSVGElement())
return false;
if (!parentNode())
return true;
if (parentNode()->hasTagName(SVGNames::foreignObjectTag))
return true;
return !parentNode()->isSVGElement();
}
void SVGElement::reportAttributeParsingError(SVGParsingError error, Attribute* attribute)
{
if (error == NoError)
return;
String errorString = "<" + tagName() + "> attribute " + attribute->name().toString() + "=\"" + attribute->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();
}
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(Node* rootParent)
{
if (rootParent->inDocument()) {
document()->accessSVGExtensions()->removeAllAnimationElementsFromTarget(this);
document()->accessSVGExtensions()->removeAllElementReferencesForTarget(this);
}
StyledElement::removedFrom(rootParent);
}
SVGSVGElement* SVGElement::ownerSVGElement() const
{
ContainerNode* n = parentOrHostNode();
while (n) {
if (n->hasTagName(SVGNames::svgTag))
return static_cast<SVGSVGElement*>(n);
n = n->parentOrHostNode();
}
return 0;
}
SVGElement* SVGElement::viewportElement() const
{
ContainerNode* n = parentOrHostNode();
while (n) {
if (n->hasTagName(SVGNames::svgTag) || n->hasTagName(SVGNames::imageTag) || n->hasTagName(SVGNames::symbolTag))
return static_cast<SVGElement*>(n);
n = n->parentOrHostNode();
}
return 0;
}
SVGDocumentExtensions* SVGElement::accessDocumentSVGExtensions()
{
return document() ? document()->accessSVGExtensions() : 0;
}
void SVGElement::mapInstanceToElement(SVGElementInstance* instance)
{
ASSERT(instance);
HashSet<SVGElementInstance*>& instances = ensureRareSVGData()->elementInstances();
ASSERT(!instances.contains(instance));
instances.add(instance);
}
void SVGElement::removeInstanceMapping(SVGElementInstance* instance)
{
ASSERT(instance);
ASSERT(hasRareSVGData());
HashSet<SVGElementInstance*>& instances = rareSVGData()->elementInstances();
ASSERT(instances.contains(instance));
instances.remove(instance);
}
const HashSet<SVGElementInstance*>& SVGElement::instancesForElement() const
{
if (!hasRareSVGData()) {
DEFINE_STATIC_LOCAL(HashSet<SVGElementInstance*>, emptyInstances, ());
return emptyInstances;
}
return rareSVGData()->elementInstances();
}
bool SVGElement::boundingBox(FloatRect& rect, SVGLocatable::StyleUpdateStrategy styleUpdateStrategy)
{
if (isStyledLocatable()) {
rect = static_cast<SVGStyledLocatableElement*>(this)->getBBox(styleUpdateStrategy);
return true;
}
if (hasTagName(SVGNames::textTag)) {
rect = static_cast<SVGTextElement*>(this)->getBBox(styleUpdateStrategy);
return true;
}
return false;
}
void SVGElement::setCursorElement(SVGCursorElement* cursorElement)
{
SVGElementRareData* rareData = ensureRareSVGData();
if (SVGCursorElement* oldCursorElement = rareData->cursorElement()) {
if (cursorElement == oldCursorElement)
return;
oldCursorElement->removeReferencedElement(this);
}
rareData->setCursorElement(cursorElement);
}
void SVGElement::cursorElementRemoved()
{
ASSERT(hasRareSVGData());
rareSVGData()->setCursorElement(0);
}
void SVGElement::setCursorImageValue(CSSCursorImageValue* cursorImageValue)
{
SVGElementRareData* rareData = ensureRareSVGData();
if (CSSCursorImageValue* oldCursorImageValue = rareData->cursorImageValue()) {
if (cursorImageValue == oldCursorImageValue)
return;
oldCursorImageValue->removeReferencedElement(this);
}
rareData->setCursorImageValue(cursorImageValue);
}
void SVGElement::cursorImageValueRemoved()
{
ASSERT(hasRareSVGData());
rareSVGData()->setCursorImageValue(0);
}
SVGElement* SVGElement::correspondingElement()
{
ASSERT(!hasRareSVGData() || !rareSVGData()->correspondingElement() || shadowTreeRootNode());
return hasRareSVGData() ? rareSVGData()->correspondingElement() : 0;
}
void SVGElement::setCorrespondingElement(SVGElement* correspondingElement)
{
ensureRareSVGData()->setCorrespondingElement(correspondingElement);
}
void SVGElement::parseAttribute(Attribute* attr)
{
if (attr->name() == onloadAttr)
setAttributeEventListener(eventNames().loadEvent, createAttributeEventListener(this, attr));
else if (attr->name() == onclickAttr)
setAttributeEventListener(eventNames().clickEvent, createAttributeEventListener(this, attr));
else if (attr->name() == onmousedownAttr)
setAttributeEventListener(eventNames().mousedownEvent, createAttributeEventListener(this, attr));
else if (attr->name() == onmousemoveAttr)
setAttributeEventListener(eventNames().mousemoveEvent, createAttributeEventListener(this, attr));
else if (attr->name() == onmouseoutAttr)
setAttributeEventListener(eventNames().mouseoutEvent, createAttributeEventListener(this, attr));
else if (attr->name() == onmouseoverAttr)
setAttributeEventListener(eventNames().mouseoverEvent, createAttributeEventListener(this, attr));
else if (attr->name() == onmouseupAttr)
setAttributeEventListener(eventNames().mouseupEvent, createAttributeEventListener(this, attr));
else if (attr->name() == SVGNames::onfocusinAttr)
setAttributeEventListener(eventNames().focusinEvent, createAttributeEventListener(this, attr));
else if (attr->name() == SVGNames::onfocusoutAttr)
setAttributeEventListener(eventNames().focusoutEvent, createAttributeEventListener(this, attr));
else if (attr->name() == SVGNames::onactivateAttr)
setAttributeEventListener(eventNames().DOMActivateEvent, createAttributeEventListener(this, attr));
else
StyledElement::parseAttribute(attr);
}
void SVGElement::animatedPropertyTypeForAttribute(const QualifiedName& attributeName, Vector<AnimatedPropertyType>& propertyTypes)
{
localAttributeToPropertyMap().animatedPropertyTypeForAttribute(attributeName, propertyTypes);
}
bool SVGElement::haveLoadedRequiredResources()
{
Node* child = firstChild();
while (child) {
if (child->isSVGElement() && !static_cast<SVGElement*>(child)->haveLoadedRequiredResources())
return false;
child = child->nextSibling();
}
return true;
}
static bool hasLoadListener(Element* element)
{
if (element->hasEventListeners(eventNames().loadEvent))
return true;
for (element = element->parentOrHostElement(); element; element = element->parentOrHostElement()) {
const EventListenerVector& entry = element->getEventListeners(eventNames().loadEvent);
for (size_t i = 0; i < entry.size(); ++i) {
if (entry[i].useCapture)
return true;
}
}
return false;
}
void SVGElement::sendSVGLoadEventIfPossible(bool sendParentLoadEvents)
{
RefPtr<SVGElement> currentTarget = this;
while (currentTarget && currentTarget->haveLoadedRequiredResources()) {
RefPtr<Element> parent;
if (sendParentLoadEvents)
parent = currentTarget->parentOrHostElement(); if (hasLoadListener(currentTarget.get()))
currentTarget->dispatchEvent(Event::create(eventNames().loadEvent, false, false));
currentTarget = (parent && parent->isSVGElement()) ? static_pointer_cast<SVGElement>(parent) : RefPtr<SVGElement>();
SVGElement* element = static_cast<SVGElement*>(currentTarget.get());
if (!element || !element->isOutermostSVGSVGElement())
continue;
ASSERT(sendParentLoadEvents);
if (!document()->loadEventFinished())
break;
}
}
void SVGElement::finishParsingChildren()
{
StyledElement::finishParsingChildren();
if (isOutermostSVGSVGElement())
return;
sendSVGLoadEventIfPossible();
}
bool SVGElement::childShouldCreateRenderer(const NodeRenderingContext& childContext) const
{
if (childContext.node()->isSVGElement())
return static_cast<SVGElement*>(childContext.node())->isValid();
return false;
}
void SVGElement::attributeChanged(Attribute* attr)
{
ASSERT(attr);
if (!attr)
return;
StyledElement::attributeChanged(attr);
if (isSynchronizingSVGAttributes())
return;
if (isIdAttributeName(attr->name())) {
document()->accessSVGExtensions()->removeAllAnimationElementsFromTarget(this);
document()->accessSVGExtensions()->removeAllElementReferencesForTarget(this);
}
if (attr->name() != HTMLNames::styleAttr)
svgAttributeChanged(attr->name());
}
void SVGElement::updateAnimatedSVGAttribute(const QualifiedName& name) const
{
if (isSynchronizingSVGAttributes() || areSVGAttributesValid())
return;
setIsSynchronizingSVGAttributes();
SVGElement* nonConstThis = const_cast<SVGElement*>(this);
if (name == anyQName()) {
nonConstThis->localAttributeToPropertyMap().synchronizeProperties(nonConstThis);
setAreSVGAttributesValid();
} else
nonConstThis->localAttributeToPropertyMap().synchronizeProperty(nonConstThis, name);
clearIsSynchronizingSVGAttributes();
}
SVGAttributeToPropertyMap& SVGElement::localAttributeToPropertyMap()
{
ASSERT_NOT_REACHED();
DEFINE_STATIC_LOCAL(SVGAttributeToPropertyMap, dummyMap, ());
return dummyMap;
}
void SVGElement::synchronizeRequiredFeatures(void* contextElement)
{
ASSERT(contextElement);
static_cast<SVGElement*>(contextElement)->synchronizeRequiredFeatures();
}
void SVGElement::synchronizeRequiredExtensions(void* contextElement)
{
ASSERT(contextElement);
static_cast<SVGElement*>(contextElement)->synchronizeRequiredExtensions();
}
void SVGElement::synchronizeSystemLanguage(void* contextElement)
{
ASSERT(contextElement);
static_cast<SVGElement*>(contextElement)->synchronizeSystemLanguage();
}
PassRefPtr<RenderStyle> SVGElement::customStyleForRenderer()
{
if (!correspondingElement())
return document()->styleResolver()->styleForElement(this);
RenderStyle* style = 0;
if (Element* parent = parentOrHostElement()) {
if (RenderObject* renderer = parent->renderer())
style = renderer->style();
}
return document()->styleResolver()->styleForElement(correspondingElement(), style, DisallowStyleSharing);
}
StylePropertySet* SVGElement::animatedSMILStyleProperties() const
{
if (hasRareSVGData())
return rareSVGData()->animatedSMILStyleProperties();
return 0;
}
StylePropertySet* SVGElement::ensureAnimatedSMILStyleProperties()
{
return ensureRareSVGData()->ensureAnimatedSMILStyleProperties();
}
void SVGElement::setUseOverrideComputedStyle(bool value)
{
if (hasRareSVGData())
rareSVGData()->setUseOverrideComputedStyle(value);
}
RenderStyle* SVGElement::computedStyle(PseudoId pseudoElementSpecifier)
{
if (!hasRareSVGData() || !rareSVGData()->useOverrideComputedStyle())
return Element::computedStyle(pseudoElementSpecifier);
RenderStyle* parentStyle = 0;
if (Element* parent = parentOrHostElement()) {
if (RenderObject* renderer = parent->renderer())
parentStyle = renderer->style();
}
return rareSVGData()->overrideComputedStyle(this, parentStyle);
}
#ifndef NDEBUG
bool SVGElement::isAnimatableAttribute(const QualifiedName& name)
{
DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, animatableAttributes, ());
if (animatableAttributes.isEmpty()) {
animatableAttributes.add(HTMLNames::classAttr);
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);
}
return animatableAttributes.contains(name);
}
#endif
}
#endif // ENABLE(SVG)