diff options
author | Steve Block <steveblock@google.com> | 2011-05-06 11:45:16 +0100 |
---|---|---|
committer | Steve Block <steveblock@google.com> | 2011-05-12 13:44:10 +0100 |
commit | cad810f21b803229eb11403f9209855525a25d57 (patch) | |
tree | 29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /Source/WebCore/svg/SVGSVGElement.cpp | |
parent | 121b0cf4517156d0ac5111caf9830c51b69bae8f (diff) | |
download | external_webkit-cad810f21b803229eb11403f9209855525a25d57.zip external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.gz external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.bz2 |
Merge WebKit at r75315: Initial merge by git.
Change-Id: I570314b346ce101c935ed22a626b48c2af266b84
Diffstat (limited to 'Source/WebCore/svg/SVGSVGElement.cpp')
-rw-r--r-- | Source/WebCore/svg/SVGSVGElement.cpp | 656 |
1 files changed, 656 insertions, 0 deletions
diff --git a/Source/WebCore/svg/SVGSVGElement.cpp b/Source/WebCore/svg/SVGSVGElement.cpp new file mode 100644 index 0000000..7e04f5d --- /dev/null +++ b/Source/WebCore/svg/SVGSVGElement.cpp @@ -0,0 +1,656 @@ +/* + * Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <zimmermann@kde.org> + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2010 Rob Buis <buis@kde.org> + * Copyright (C) 2007 Apple Inc. 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 + * 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 "SVGSVGElement.h" + +#include "AffineTransform.h" +#include "Attribute.h" +#include "CSSHelper.h" +#include "CSSPropertyNames.h" +#include "Document.h" +#include "EventListener.h" +#include "EventNames.h" +#include "FloatConversion.h" +#include "FloatRect.h" +#include "FrameView.h" +#include "HTMLNames.h" +#include "RenderSVGResource.h" +#include "RenderSVGRoot.h" +#include "RenderSVGViewportContainer.h" +#include "SMILTimeContainer.h" +#include "SVGAngle.h" +#include "SVGNames.h" +#include "SVGPreserveAspectRatio.h" +#include "SVGTransform.h" +#include "SVGTransformList.h" +#include "SVGViewElement.h" +#include "SVGViewSpec.h" +#include "SVGZoomEvent.h" +#include "ScriptEventListener.h" +#include "SelectionController.h" +#include <wtf/StdLibExtras.h> + +namespace WebCore { + +// Animated property definitions +DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::xAttr, X, x) +DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::yAttr, Y, y) +DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::widthAttr, Width, width) +DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::heightAttr, Height, height) +DEFINE_ANIMATED_BOOLEAN(SVGSVGElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired) +DEFINE_ANIMATED_PRESERVEASPECTRATIO(SVGSVGElement, SVGNames::preserveAspectRatioAttr, PreserveAspectRatio, preserveAspectRatio) +DEFINE_ANIMATED_RECT(SVGSVGElement, SVGNames::viewBoxAttr, ViewBox, viewBox) + +inline SVGSVGElement::SVGSVGElement(const QualifiedName& tagName, Document* doc) + : SVGStyledLocatableElement(tagName, doc) + , m_x(LengthModeWidth) + , m_y(LengthModeHeight) + , m_width(LengthModeWidth, "100%") + , m_height(LengthModeHeight, "100%") + , m_useCurrentView(false) + , m_timeContainer(SMILTimeContainer::create(this)) + , m_scale(1) + , m_viewSpec(0) + , m_containerSize(300, 150) + , m_hasSetContainerSize(false) +{ + doc->registerForDocumentActivationCallbacks(this); +} + +PassRefPtr<SVGSVGElement> SVGSVGElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new SVGSVGElement(tagName, document)); +} + +SVGSVGElement::~SVGSVGElement() +{ + document()->unregisterForDocumentActivationCallbacks(this); + // There are cases where removedFromDocument() is not called. + // see ContainerNode::removeAllChildren, called by its destructor. + document()->accessSVGExtensions()->removeTimeContainer(this); +} + +void SVGSVGElement::willMoveToNewOwnerDocument() +{ + document()->unregisterForDocumentActivationCallbacks(this); + SVGStyledLocatableElement::willMoveToNewOwnerDocument(); +} + +void SVGSVGElement::didMoveToNewOwnerDocument() +{ + document()->registerForDocumentActivationCallbacks(this); + SVGStyledLocatableElement::didMoveToNewOwnerDocument(); +} + +const AtomicString& SVGSVGElement::contentScriptType() const +{ + DEFINE_STATIC_LOCAL(const AtomicString, defaultValue, ("text/ecmascript")); + const AtomicString& n = getAttribute(SVGNames::contentScriptTypeAttr); + return n.isNull() ? defaultValue : n; +} + +void SVGSVGElement::setContentScriptType(const AtomicString& type) +{ + setAttribute(SVGNames::contentScriptTypeAttr, type); +} + +const AtomicString& SVGSVGElement::contentStyleType() const +{ + DEFINE_STATIC_LOCAL(const AtomicString, defaultValue, ("text/css")); + const AtomicString& n = getAttribute(SVGNames::contentStyleTypeAttr); + return n.isNull() ? defaultValue : n; +} + +void SVGSVGElement::setContentStyleType(const AtomicString& type) +{ + setAttribute(SVGNames::contentStyleTypeAttr, type); +} + +FloatRect SVGSVGElement::viewport() const +{ + double x = 0; + double y = 0; + if (!isOutermostSVG()) { + x = this->x().value(this); + y = this->y().value(this); + } + float w = width().value(this); + float h = height().value(this); + AffineTransform viewBox = viewBoxToViewTransform(w, h); + double wDouble = w; + double hDouble = h; + viewBox.map(x, y, x, y); + viewBox.map(w, h, wDouble, hDouble); + return FloatRect::narrowPrecision(x, y, wDouble, hDouble); +} + +int SVGSVGElement::relativeWidthValue() const +{ + SVGLength w = width(); + if (w.unitType() != LengthTypePercentage) + return 0; + + return static_cast<int>(w.valueAsPercentage() * m_containerSize.width()); +} + +int SVGSVGElement::relativeHeightValue() const +{ + SVGLength h = height(); + if (h.unitType() != LengthTypePercentage) + return 0; + + return static_cast<int>(h.valueAsPercentage() * m_containerSize.height()); +} + +float SVGSVGElement::pixelUnitToMillimeterX() const +{ + // 2.54 / cssPixelsPerInch gives CM. + return (2.54f / cssPixelsPerInch) * 10.0f; +} + +float SVGSVGElement::pixelUnitToMillimeterY() const +{ + // 2.54 / cssPixelsPerInch gives CM. + return (2.54f / cssPixelsPerInch) * 10.0f; +} + +float SVGSVGElement::screenPixelToMillimeterX() const +{ + return pixelUnitToMillimeterX(); +} + +float SVGSVGElement::screenPixelToMillimeterY() const +{ + return pixelUnitToMillimeterY(); +} + +bool SVGSVGElement::useCurrentView() const +{ + return m_useCurrentView; +} + +void SVGSVGElement::setUseCurrentView(bool currentView) +{ + m_useCurrentView = currentView; +} + +SVGViewSpec* SVGSVGElement::currentView() const +{ + if (!m_viewSpec) + m_viewSpec = adoptPtr(new SVGViewSpec(const_cast<SVGSVGElement*>(this))); + return m_viewSpec.get(); +} + +float SVGSVGElement::currentScale() const +{ + // Only the page zoom factor is relevant for SVG + if (Frame* frame = document()->frame()) + return frame->pageZoomFactor(); + return m_scale; +} + +void SVGSVGElement::setCurrentScale(float scale) +{ + if (Frame* frame = document()->frame()) { + // Calling setCurrentScale() on the outermost <svg> element in a standalone SVG document + // is allowed to change the page zoom factor, influencing the document size, scrollbars etc. + if (parentNode() == document()) + frame->setPageZoomFactor(scale); + return; + } + + m_scale = scale; + if (RenderObject* object = renderer()) + RenderSVGResource::markForLayoutAndParentResourceInvalidation(object); +} + +void SVGSVGElement::setCurrentTranslate(const FloatPoint& translation) +{ + m_translation = translation; + updateCurrentTranslate(); +} + +void SVGSVGElement::updateCurrentTranslate() +{ + if (RenderObject* object = renderer()) + object->setNeedsLayout(true); + + if (parentNode() == document() && document()->renderer()) + document()->renderer()->repaint(); +} + +void SVGSVGElement::parseMappedAttribute(Attribute* attr) +{ + if (!nearestViewportElement()) { + bool setListener = true; + + // Only handle events if we're the outermost <svg> element + if (attr->name() == HTMLNames::onunloadAttr) + document()->setWindowAttributeEventListener(eventNames().unloadEvent, createAttributeEventListener(document()->frame(), attr)); + else if (attr->name() == HTMLNames::onresizeAttr) + document()->setWindowAttributeEventListener(eventNames().resizeEvent, createAttributeEventListener(document()->frame(), attr)); + else if (attr->name() == HTMLNames::onscrollAttr) + document()->setWindowAttributeEventListener(eventNames().scrollEvent, createAttributeEventListener(document()->frame(), attr)); + else if (attr->name() == SVGNames::onzoomAttr) + document()->setWindowAttributeEventListener(eventNames().zoomEvent, createAttributeEventListener(document()->frame(), attr)); + else + setListener = false; + + if (setListener) + return; + } + + if (attr->name() == HTMLNames::onabortAttr) + document()->setWindowAttributeEventListener(eventNames().abortEvent, createAttributeEventListener(document()->frame(), attr)); + else if (attr->name() == HTMLNames::onerrorAttr) + document()->setWindowAttributeEventListener(eventNames().errorEvent, createAttributeEventListener(document()->frame(), attr)); + else if (attr->name() == SVGNames::xAttr) + setXBaseValue(SVGLength(LengthModeWidth, attr->value())); + else if (attr->name() == SVGNames::yAttr) + setYBaseValue(SVGLength(LengthModeHeight, attr->value())); + else if (attr->name() == SVGNames::widthAttr) { + setWidthBaseValue(SVGLength(LengthModeWidth, attr->value())); + addCSSProperty(attr, CSSPropertyWidth, attr->value()); + if (widthBaseValue().value(this) < 0.0) + document()->accessSVGExtensions()->reportError("A negative value for svg attribute <width> is not allowed"); + } else if (attr->name() == SVGNames::heightAttr) { + setHeightBaseValue(SVGLength(LengthModeHeight, attr->value())); + addCSSProperty(attr, CSSPropertyHeight, attr->value()); + if (heightBaseValue().value(this) < 0.0) + document()->accessSVGExtensions()->reportError("A negative value for svg attribute <height> is not allowed"); + } else { + if (SVGTests::parseMappedAttribute(attr)) + return; + if (SVGLangSpace::parseMappedAttribute(attr)) + return; + if (SVGExternalResourcesRequired::parseMappedAttribute(attr)) + return; + if (SVGFitToViewBox::parseMappedAttribute(document(), attr)) + return; + if (SVGZoomAndPan::parseMappedAttribute(attr)) + return; + + SVGStyledLocatableElement::parseMappedAttribute(attr); + } +} + +// This hack will not handle the case where we're setting a width/height +// on a root <svg> via svg.width.baseValue = when it has none. +static void updateCSSForAttribute(SVGSVGElement* element, const QualifiedName& attrName, CSSPropertyID property, const SVGLength& value) +{ + Attribute* attribute = element->attributes(false)->getAttributeItem(attrName); + if (!attribute || !attribute->isMappedAttribute()) + return; + element->addCSSProperty(attribute, property, value.valueAsString()); +} + +void SVGSVGElement::svgAttributeChanged(const QualifiedName& attrName) +{ + SVGStyledElement::svgAttributeChanged(attrName); + + // FIXME: Ugly, ugly hack to around that parseMappedAttribute is not called + // when svg.width.baseValue = 100 is evaluated. + // Thus the CSS length value for width is not updated, and width() computeLogicalWidth() + // calculations on RenderSVGRoot will be wrong. + // https://bugs.webkit.org/show_bug.cgi?id=25387 + bool updateRelativeLengths = false; + if (attrName == SVGNames::widthAttr) { + updateCSSForAttribute(this, attrName, CSSPropertyWidth, widthBaseValue()); + updateRelativeLengths = true; + } else if (attrName == SVGNames::heightAttr) { + updateCSSForAttribute(this, attrName, CSSPropertyHeight, heightBaseValue()); + updateRelativeLengths = true; + } + + if (updateRelativeLengths + || attrName == SVGNames::xAttr + || attrName == SVGNames::yAttr + || SVGFitToViewBox::isKnownAttribute(attrName)) { + updateRelativeLengths = true; + updateRelativeLengthsInformation(); + } + + if (SVGTests::handleAttributeChange(this, attrName)) + return; + + if (!renderer()) + return; + + if (updateRelativeLengths + || SVGLangSpace::isKnownAttribute(attrName) + || SVGExternalResourcesRequired::isKnownAttribute(attrName) + || SVGZoomAndPan::isKnownAttribute(attrName) + || SVGStyledLocatableElement::isKnownAttribute(attrName)) + RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer()); +} + +void SVGSVGElement::synchronizeProperty(const QualifiedName& attrName) +{ + SVGStyledElement::synchronizeProperty(attrName); + + if (attrName == anyQName()) { + synchronizeX(); + synchronizeY(); + synchronizeWidth(); + synchronizeHeight(); + synchronizeExternalResourcesRequired(); + synchronizeViewBox(); + synchronizePreserveAspectRatio(); + SVGTests::synchronizeProperties(this, attrName); + return; + } + + if (attrName == SVGNames::xAttr) + synchronizeX(); + else if (attrName == SVGNames::yAttr) + synchronizeY(); + else if (attrName == SVGNames::widthAttr) + synchronizeWidth(); + else if (attrName == SVGNames::heightAttr) + synchronizeHeight(); + else if (SVGExternalResourcesRequired::isKnownAttribute(attrName)) + synchronizeExternalResourcesRequired(); + else if (attrName == SVGNames::viewBoxAttr) + synchronizeViewBox(); + else if (attrName == SVGNames::preserveAspectRatioAttr) + synchronizePreserveAspectRatio(); + else if (SVGTests::isKnownAttribute(attrName)) + SVGTests::synchronizeProperties(this, attrName); +} + +unsigned SVGSVGElement::suspendRedraw(unsigned /* maxWaitMilliseconds */) +{ + // FIXME: Implement me (see bug 11275) + return 0; +} + +void SVGSVGElement::unsuspendRedraw(unsigned /* suspendHandleId */) +{ + // FIXME: Implement me (see bug 11275) +} + +void SVGSVGElement::unsuspendRedrawAll() +{ + // FIXME: Implement me (see bug 11275) +} + +void SVGSVGElement::forceRedraw() +{ + // FIXME: Implement me (see bug 11275) +} + +NodeList* SVGSVGElement::getIntersectionList(const FloatRect&, SVGElement*) +{ + // FIXME: Implement me (see bug 11274) + return 0; +} + +NodeList* SVGSVGElement::getEnclosureList(const FloatRect&, SVGElement*) +{ + // FIXME: Implement me (see bug 11274) + return 0; +} + +bool SVGSVGElement::checkIntersection(SVGElement*, const FloatRect& rect) +{ + // TODO : take into account pointer-events? + // FIXME: Why is element ignored?? + // FIXME: Implement me (see bug 11274) + return rect.intersects(getBBox()); +} + +bool SVGSVGElement::checkEnclosure(SVGElement*, const FloatRect& rect) +{ + // TODO : take into account pointer-events? + // FIXME: Why is element ignored?? + // FIXME: Implement me (see bug 11274) + return rect.contains(getBBox()); +} + +void SVGSVGElement::deselectAll() +{ + if (Frame* frame = document()->frame()) + frame->selection()->clear(); +} + +float SVGSVGElement::createSVGNumber() +{ + return 0.0f; +} + +SVGLength SVGSVGElement::createSVGLength() +{ + return SVGLength(); +} + +SVGAngle SVGSVGElement::createSVGAngle() +{ + return SVGAngle(); +} + +FloatPoint SVGSVGElement::createSVGPoint() +{ + return FloatPoint(); +} + +SVGMatrix SVGSVGElement::createSVGMatrix() +{ + return SVGMatrix(); +} + +FloatRect SVGSVGElement::createSVGRect() +{ + return FloatRect(); +} + +SVGTransform SVGSVGElement::createSVGTransform() +{ + return SVGTransform(SVGTransform::SVG_TRANSFORM_MATRIX); +} + +SVGTransform SVGSVGElement::createSVGTransformFromMatrix(const SVGMatrix& matrix) +{ + return SVGTransform(static_cast<const AffineTransform&>(matrix)); +} + +AffineTransform SVGSVGElement::localCoordinateSpaceTransform(SVGLocatable::CTMScope mode) const +{ + AffineTransform viewBoxTransform; + if (attributes()->getAttributeItem(SVGNames::viewBoxAttr)) + viewBoxTransform = viewBoxToViewTransform(width().value(this), height().value(this)); + + AffineTransform transform; + if (!isOutermostSVG()) + transform.translate(x().value(this), y().value(this)); + else if (mode == SVGLocatable::ScreenScope) { + if (RenderObject* renderer = this->renderer()) { + // Translate in our CSS parent coordinate space + // FIXME: This doesn't work correctly with CSS transforms. + FloatPoint location = renderer->localToAbsolute(FloatPoint(), false, true); + + // Be careful here! localToAbsolute() includes the x/y offset coming from the viewBoxToViewTransform(), because + // RenderSVGRoot::localToBorderBoxTransform() (called through mapLocalToContainer(), called from localToAbsolute()) + // also takes the viewBoxToViewTransform() into account, so we have to subtract it here (original cause of bug #27183) + transform.translate(location.x() - viewBoxTransform.e(), location.y() - viewBoxTransform.f()); + + // Respect scroll offset. + if (FrameView* view = document()->view()) { + IntSize scrollOffset = view->scrollOffset(); + transform.translate(-scrollOffset.width(), -scrollOffset.height()); + } + } + } + + return transform.multLeft(viewBoxTransform); +} + +RenderObject* SVGSVGElement::createRenderer(RenderArena* arena, RenderStyle*) +{ + if (isOutermostSVG()) + return new (arena) RenderSVGRoot(this); + + return new (arena) RenderSVGViewportContainer(this); +} + +void SVGSVGElement::insertedIntoDocument() +{ + document()->accessSVGExtensions()->addTimeContainer(this); + SVGStyledLocatableElement::insertedIntoDocument(); +} + +void SVGSVGElement::removedFromDocument() +{ + document()->accessSVGExtensions()->removeTimeContainer(this); + SVGStyledLocatableElement::removedFromDocument(); +} + +void SVGSVGElement::pauseAnimations() +{ + if (!m_timeContainer->isPaused()) + m_timeContainer->pause(); +} + +void SVGSVGElement::unpauseAnimations() +{ + if (m_timeContainer->isPaused()) + m_timeContainer->resume(); +} + +bool SVGSVGElement::animationsPaused() const +{ + return m_timeContainer->isPaused(); +} + +float SVGSVGElement::getCurrentTime() const +{ + return narrowPrecisionToFloat(m_timeContainer->elapsed().value()); +} + +void SVGSVGElement::setCurrentTime(float /* seconds */) +{ + // FIXME: Implement me, bug 12073 +} + +bool SVGSVGElement::selfHasRelativeLengths() const +{ + return x().isRelative() + || y().isRelative() + || width().isRelative() + || height().isRelative() + || hasAttribute(SVGNames::viewBoxAttr); +} + +bool SVGSVGElement::isOutermostSVG() const +{ + // Element may not be in the document, pretend we're outermost for viewport(), getCTM(), etc. + if (!parentNode()) + return true; + +#if ENABLE(SVG_FOREIGN_OBJECT) + // We act like an outermost SVG element, if we're a direct child of a <foreignObject> element. + if (parentNode()->hasTagName(SVGNames::foreignObjectTag)) + return true; +#endif + + // This is true whenever this is the outermost SVG, even if there are HTML elements outside it + return !parentNode()->isSVGElement(); +} + +AffineTransform SVGSVGElement::viewBoxToViewTransform(float viewWidth, float viewHeight) const +{ + FloatRect viewBoxRect; + if (useCurrentView()) { + if (currentView()) // what if we should use it but it is not set? + viewBoxRect = currentView()->viewBox(); + } else + viewBoxRect = viewBox(); + + AffineTransform ctm = SVGFitToViewBox::viewBoxToViewTransform(viewBoxRect, preserveAspectRatio(), viewWidth, viewHeight); + if (useCurrentView() && currentView()) { + AffineTransform transform; + if (!currentView()->transform().concatenate(transform)) + return ctm; + + return transform * ctm; + } + + return ctm; +} + +void SVGSVGElement::inheritViewAttributes(SVGViewElement* viewElement) +{ + setUseCurrentView(true); + if (viewElement->hasAttribute(SVGNames::viewBoxAttr)) + currentView()->setViewBoxBaseValue(viewElement->viewBox()); + else + currentView()->setViewBoxBaseValue(viewBox()); + + SVGPreserveAspectRatio aspectRatio; + if (viewElement->hasAttribute(SVGNames::preserveAspectRatioAttr)) + aspectRatio = viewElement->preserveAspectRatioBaseValue(); + else + aspectRatio = preserveAspectRatioBaseValue(); + currentView()->setPreserveAspectRatioBaseValue(aspectRatio); + + if (viewElement->hasAttribute(SVGNames::zoomAndPanAttr)) + currentView()->setZoomAndPan(viewElement->zoomAndPan()); + + if (RenderObject* object = renderer()) + RenderSVGResource::markForLayoutAndParentResourceInvalidation(object); +} + +void SVGSVGElement::documentWillBecomeInactive() +{ + pauseAnimations(); +} + +void SVGSVGElement::documentDidBecomeActive() +{ + unpauseAnimations(); +} + +// getElementById on SVGSVGElement is restricted to only the child subtree defined by the <svg> element. +// See http://www.w3.org/TR/SVG11/struct.html#InterfaceSVGSVGElement +Element* SVGSVGElement::getElementById(const AtomicString& id) const +{ + Element* element = document()->getElementById(id); + if (element && element->isDescendantOf(this)) + return element; + + // Fall back to traversing our subtree. Duplicate ids are allowed, the first found will + // be returned. + for (Node* node = traverseNextNode(this); node; node = node->traverseNextNode(this)) { + if (!node->isElementNode()) + continue; + + Element* element = static_cast<Element*>(node); + if (element->hasID() && element->getIdAttribute() == id) + return element; + } + return 0; +} + +} + +#endif // ENABLE(SVG) |