summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/svg/SVGStyledElement.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/svg/SVGStyledElement.cpp')
-rw-r--r--Source/WebCore/svg/SVGStyledElement.cpp387
1 files changed, 387 insertions, 0 deletions
diff --git a/Source/WebCore/svg/SVGStyledElement.cpp b/Source/WebCore/svg/SVGStyledElement.cpp
new file mode 100644
index 0000000..58248d8
--- /dev/null
+++ b/Source/WebCore/svg/SVGStyledElement.cpp
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005, 2007, 2008 Rob Buis <buis@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#if ENABLE(SVG)
+#include "SVGStyledElement.h"
+
+#include "Attr.h"
+#include "CSSParser.h"
+#include "CSSStyleSelector.h"
+#include "Document.h"
+#include "HTMLNames.h"
+#include "PlatformString.h"
+#include "RenderObject.h"
+#include "RenderSVGResource.h"
+#include "RenderSVGResourceClipper.h"
+#include "RenderSVGResourceFilter.h"
+#include "RenderSVGResourceMasker.h"
+#include "SVGElement.h"
+#include "SVGElementInstance.h"
+#include "SVGElementRareData.h"
+#include "SVGNames.h"
+#include "SVGRenderStyle.h"
+#include "SVGRenderSupport.h"
+#include "SVGSVGElement.h"
+#include "SVGUseElement.h"
+#include <wtf/Assertions.h>
+
+namespace WebCore {
+
+// Animated property definitions
+DEFINE_ANIMATED_STRING(SVGStyledElement, HTMLNames::classAttr, ClassName, className)
+
+using namespace SVGNames;
+
+void mapAttributeToCSSProperty(HashMap<AtomicStringImpl*, int>* propertyNameToIdMap, const QualifiedName& attrName)
+{
+ int propertyId = cssPropertyID(attrName.localName());
+ ASSERT(propertyId > 0);
+ propertyNameToIdMap->set(attrName.localName().impl(), propertyId);
+}
+
+SVGStyledElement::SVGStyledElement(const QualifiedName& tagName, Document* document)
+ : SVGElement(tagName, document)
+{
+}
+
+SVGStyledElement::~SVGStyledElement()
+{
+}
+
+String SVGStyledElement::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 (hasTagName(SVGNames::svgTag)) {
+ const SVGSVGElement* svg = static_cast<const SVGSVGElement*>(this);
+ if (svg->isOutermostSVG())
+ return String();
+ }
+
+ // Walk up the tree, to find out whether we're inside a <use> shadow tree, to find the right title.
+ Node* parent = const_cast<SVGStyledElement*>(this);
+ while (parent) {
+ if (!parent->isShadowRoot()) {
+ parent = parent->parentNodeGuaranteedHostFree();
+ continue;
+ }
+
+ // Get the <use> element.
+ Element* shadowParent = parent->shadowHost();
+ if (shadowParent && shadowParent->isSVGElement() && shadowParent->hasTagName(SVGNames::useTag)) {
+ SVGUseElement* useElement = static_cast<SVGUseElement*>(shadowParent);
+ // If the <use> title is not empty we found the title to use.
+ String useTitle(useElement->title());
+ if (useTitle.isEmpty())
+ break;
+ return useTitle;
+ }
+ parent = parent->parentNode();
+ }
+
+ // 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.
+ Element* titleElement = firstElementChild();
+ for (; titleElement; titleElement = titleElement->nextElementSibling()) {
+ if (titleElement->hasTagName(SVGNames::titleTag) && titleElement->isSVGElement())
+ break;
+ }
+
+ // If a title child was found, return the text contents.
+ if (titleElement)
+ return titleElement->innerText();
+
+ // Otherwise return a null/empty string.
+ return String();
+}
+
+bool SVGStyledElement::rendererIsNeeded(RenderStyle* style)
+{
+ // http://www.w3.org/TR/SVG/extend.html#PrivateData
+ // Prevent anything other than SVG renderers from appearing in our render tree
+ // Spec: SVG allows inclusion of elements from foreign namespaces anywhere
+ // with the SVG content. In general, the SVG user agent will include the unknown
+ // elements in the DOM but will otherwise ignore unknown elements.
+ if (!parentNode() || parentNode()->isSVGElement())
+ return StyledElement::rendererIsNeeded(style);
+
+ return false;
+}
+
+int SVGStyledElement::cssPropertyIdForSVGAttributeName(const QualifiedName& attrName)
+{
+ if (!attrName.namespaceURI().isNull())
+ return 0;
+
+ static HashMap<AtomicStringImpl*, int>* propertyNameToIdMap = 0;
+ if (!propertyNameToIdMap) {
+ propertyNameToIdMap = new HashMap<AtomicStringImpl*, int>;
+ // This is a list of all base CSS and SVG CSS properties which are exposed as SVG XML attributes
+ mapAttributeToCSSProperty(propertyNameToIdMap, alignment_baselineAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, baseline_shiftAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, clipAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, clip_pathAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, clip_ruleAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, SVGNames::colorAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, color_interpolationAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, color_interpolation_filtersAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, color_profileAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, color_renderingAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, cursorAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, SVGNames::directionAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, displayAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, dominant_baselineAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, enable_backgroundAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, fillAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, fill_opacityAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, fill_ruleAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, filterAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, flood_colorAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, flood_opacityAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, font_familyAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, font_sizeAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, font_stretchAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, font_styleAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, font_variantAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, font_weightAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, glyph_orientation_horizontalAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, glyph_orientation_verticalAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, image_renderingAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, kerningAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, letter_spacingAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, lighting_colorAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, marker_endAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, marker_midAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, marker_startAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, maskAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, opacityAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, overflowAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, pointer_eventsAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, shape_renderingAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, stop_colorAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, stop_opacityAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, strokeAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, stroke_dasharrayAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, stroke_dashoffsetAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, stroke_linecapAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, stroke_linejoinAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, stroke_miterlimitAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, stroke_opacityAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, stroke_widthAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, text_anchorAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, text_decorationAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, text_renderingAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, unicode_bidiAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, vector_effectAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, visibilityAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, word_spacingAttr);
+ mapAttributeToCSSProperty(propertyNameToIdMap, writing_modeAttr);
+ }
+
+ return propertyNameToIdMap->get(attrName.localName().impl());
+}
+
+bool SVGStyledElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const
+{
+ if (SVGStyledElement::cssPropertyIdForSVGAttributeName(attrName) > 0) {
+ result = eSVG;
+ return false;
+ }
+ return SVGElement::mapToEntry(attrName, result);
+}
+
+void SVGStyledElement::parseMappedAttribute(Attribute* attr)
+{
+ const QualifiedName& attrName = attr->name();
+ // NOTE: Any subclass which overrides parseMappedAttribute for a property handled by
+ // cssPropertyIdForSVGAttributeName will also have to override mapToEntry to disable the default eSVG mapping
+ int propId = SVGStyledElement::cssPropertyIdForSVGAttributeName(attrName);
+ if (propId > 0) {
+ addCSSProperty(attr, propId, attr->value());
+ setNeedsStyleRecalc();
+ return;
+ }
+
+ // SVG animation has currently requires special storage of values so we set
+ // the className here. svgAttributeChanged actually causes the resulting
+ // style updates (instead of StyledElement::parseMappedAttribute). We don't
+ // tell StyledElement about the change to avoid parsing the class list twice
+ if (attrName.matches(HTMLNames::classAttr))
+ setClassNameBaseValue(attr->value());
+ else
+ // id is handled by StyledElement which SVGElement inherits from
+ SVGElement::parseMappedAttribute(attr);
+}
+
+bool SVGStyledElement::isKnownAttribute(const QualifiedName& attrName)
+{
+ return isIdAttributeName(attrName);
+}
+
+void SVGStyledElement::svgAttributeChanged(const QualifiedName& attrName)
+{
+ SVGElement::svgAttributeChanged(attrName);
+
+ if (attrName.matches(HTMLNames::classAttr))
+ classAttributeChanged(className());
+
+ RenderObject* object = renderer();
+
+ if (isIdAttributeName(attrName)) {
+ // Notify resources about id changes, this is important as we cache resources by id in SVGDocumentExtensions
+ if (object && object->isSVGResourceContainer())
+ object->toRenderSVGResourceContainer()->idChanged();
+ }
+
+ // Invalidate all SVGElementInstances associated with us
+ SVGElementInstance::invalidateAllInstancesOfElement(this);
+}
+
+void SVGStyledElement::synchronizeProperty(const QualifiedName& attrName)
+{
+ SVGElement::synchronizeProperty(attrName);
+
+ if (attrName == anyQName() || attrName.matches(HTMLNames::classAttr))
+ synchronizeClassName();
+}
+
+void SVGStyledElement::attach()
+{
+ SVGElement::attach();
+
+ if (RenderObject* object = renderer())
+ object->updateFromElement();
+}
+
+void SVGStyledElement::insertedIntoDocument()
+{
+ SVGElement::insertedIntoDocument();
+ updateRelativeLengthsInformation();
+}
+
+void SVGStyledElement::removedFromDocument()
+{
+ updateRelativeLengthsInformation(false, this);
+ SVGElement::removedFromDocument();
+}
+
+void SVGStyledElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
+{
+ SVGElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
+
+ // Invalidate all SVGElementInstances associated with us
+ if (!changedByParser)
+ SVGElementInstance::invalidateAllInstancesOfElement(this);
+}
+
+PassRefPtr<RenderStyle> SVGStyledElement::resolveStyle(RenderStyle* parentStyle)
+{
+ if (renderer())
+ return renderer()->style();
+ return document()->styleSelector()->styleForElement(this, parentStyle);
+}
+
+PassRefPtr<CSSValue> SVGStyledElement::getPresentationAttribute(const String& name)
+{
+ if (!attributeMap())
+ return 0;
+
+ QualifiedName attributeName(nullAtom, name, nullAtom);
+ Attribute* attr = attributeMap()->getAttributeItem(attributeName);
+ if (!attr || !attr->isMappedAttribute() || !attr->style())
+ return 0;
+
+ Attribute* cssSVGAttr = attr;
+ // This function returns a pointer to a CSSValue which can be mutated from JavaScript.
+ // If the associated MappedAttribute uses the same CSSMappedAttributeDeclaration
+ // as StyledElement's mappedAttributeDecls cache, create a new CSSMappedAttributeDeclaration
+ // before returning so that any modifications to the CSSValue will not affect other attributes.
+ MappedAttributeEntry entry;
+ mapToEntry(attributeName, entry);
+ if (getMappedAttributeDecl(entry, cssSVGAttr) == cssSVGAttr->decl()) {
+ cssSVGAttr->setDecl(0);
+ int propId = SVGStyledElement::cssPropertyIdForSVGAttributeName(cssSVGAttr->name());
+ addCSSProperty(cssSVGAttr, propId, cssSVGAttr->value());
+ }
+ return cssSVGAttr->style()->getPropertyCSSValue(name);
+}
+
+bool SVGStyledElement::instanceUpdatesBlocked() const
+{
+ return hasRareSVGData() && rareSVGData()->instanceUpdatesBlocked();
+}
+
+void SVGStyledElement::setInstanceUpdatesBlocked(bool value)
+{
+ if (hasRareSVGData())
+ rareSVGData()->setInstanceUpdatesBlocked(value);
+}
+
+AffineTransform SVGStyledElement::localCoordinateSpaceTransform(SVGLocatable::CTMScope) const
+{
+ // To be overriden by SVGStyledLocatableElement/SVGStyledTransformableElement (or as special case SVGTextElement)
+ ASSERT_NOT_REACHED();
+ return AffineTransform();
+}
+
+void SVGStyledElement::updateRelativeLengthsInformation(bool hasRelativeLengths, SVGStyledElement* element)
+{
+ // If we're not yet in a document, this function will be called again from insertedIntoDocument(). Do nothing now.
+ if (!inDocument())
+ return;
+
+ // An element wants to notify us that its own relative lengths state changed.
+ // Register it in the relative length map, and register us in the parent relative length map.
+ // Register the parent in the grandparents map, etc. Repeat procedure until the root of the SVG tree.
+
+ if (hasRelativeLengths)
+ m_elementsWithRelativeLengths.add(element);
+ else {
+ if (!m_elementsWithRelativeLengths.contains(element)) {
+ // We were never registered. Do nothing.
+ return;
+ }
+
+ m_elementsWithRelativeLengths.remove(element);
+ }
+
+ // Find first styled parent node, and notify it that we've changed our relative length state.
+ ContainerNode* node = parentNode();
+ while (node) {
+ if (!node->isSVGElement())
+ break;
+
+ SVGElement* element = static_cast<SVGElement*>(node);
+ if (!element->isStyled()) {
+ node = node->parentNode();
+ continue;
+ }
+
+ // Register us in the parent element map.
+ static_cast<SVGStyledElement*>(element)->updateRelativeLengthsInformation(hasRelativeLengths, this);
+ break;
+ }
+}
+
+}
+
+#endif // ENABLE(SVG)