Home | History | Annotate | Download | only in svg
      1 /*
      2     Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <zimmermann (at) kde.org>
      3                   2004, 2005, 2006, 2007, 2008 Rob Buis <buis (at) kde.org>
      4                   2007 Apple Inc.  All rights reserved.
      5 
      6     This library is free software; you can redistribute it and/or
      7     modify it under the terms of the GNU Library General Public
      8     License as published by the Free Software Foundation; either
      9     version 2 of the License, or (at your option) any later version.
     10 
     11     This library is distributed in the hope that it will be useful,
     12     but WITHOUT ANY WARRANTY; without even the implied warranty of
     13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14     Library General Public License for more details.
     15 
     16     You should have received a copy of the GNU Library General Public License
     17     along with this library; see the file COPYING.LIB.  If not, write to
     18     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     19     Boston, MA 02110-1301, USA.
     20 */
     21 
     22 #include "config.h"
     23 
     24 #if ENABLE(SVG)
     25 #include "SVGSVGElement.h"
     26 
     27 #include "AffineTransform.h"
     28 #include "CSSHelper.h"
     29 #include "CSSPropertyNames.h"
     30 #include "Document.h"
     31 #include "EventListener.h"
     32 #include "EventNames.h"
     33 #include "FloatConversion.h"
     34 #include "FloatRect.h"
     35 #include "Frame.h"
     36 #include "HTMLNames.h"
     37 #include "MappedAttribute.h"
     38 #include "RenderSVGRoot.h"
     39 #include "RenderSVGViewportContainer.h"
     40 #include "SMILTimeContainer.h"
     41 #include "SVGAngle.h"
     42 #include "SVGLength.h"
     43 #include "SVGNames.h"
     44 #include "SVGPreserveAspectRatio.h"
     45 #include "SVGTransform.h"
     46 #include "SVGTransformList.h"
     47 #include "SVGViewElement.h"
     48 #include "SVGViewSpec.h"
     49 #include "SVGZoomEvent.h"
     50 #include "ScriptEventListener.h"
     51 #include "SelectionController.h"
     52 #include <wtf/StdLibExtras.h>
     53 
     54 namespace WebCore {
     55 
     56 using namespace HTMLNames;
     57 using namespace SVGNames;
     58 
     59 SVGSVGElement::SVGSVGElement(const QualifiedName& tagName, Document* doc)
     60     : SVGStyledLocatableElement(tagName, doc)
     61     , SVGTests()
     62     , SVGLangSpace()
     63     , SVGExternalResourcesRequired()
     64     , SVGFitToViewBox()
     65     , SVGZoomAndPan()
     66     , m_x(LengthModeWidth)
     67     , m_y(LengthModeHeight)
     68     , m_width(LengthModeWidth, "100%")
     69     , m_height(LengthModeHeight, "100%")
     70     , m_useCurrentView(false)
     71     , m_timeContainer(SMILTimeContainer::create(this))
     72     , m_scale(1)
     73     , m_viewSpec(0)
     74     , m_containerSize(300, 150)
     75     , m_hasSetContainerSize(false)
     76 {
     77     doc->registerForDocumentActivationCallbacks(this);
     78 }
     79 
     80 SVGSVGElement::~SVGSVGElement()
     81 {
     82     document()->unregisterForDocumentActivationCallbacks(this);
     83     // There are cases where removedFromDocument() is not called.
     84     // see ContainerNode::removeAllChildren, called by its destructor.
     85     document()->accessSVGExtensions()->removeTimeContainer(this);
     86 }
     87 
     88 const AtomicString& SVGSVGElement::contentScriptType() const
     89 {
     90     DEFINE_STATIC_LOCAL(const AtomicString, defaultValue, ("text/ecmascript"));
     91     const AtomicString& n = getAttribute(contentScriptTypeAttr);
     92     return n.isNull() ? defaultValue : n;
     93 }
     94 
     95 void SVGSVGElement::setContentScriptType(const AtomicString& type)
     96 {
     97     setAttribute(SVGNames::contentScriptTypeAttr, type);
     98 }
     99 
    100 const AtomicString& SVGSVGElement::contentStyleType() const
    101 {
    102     DEFINE_STATIC_LOCAL(const AtomicString, defaultValue, ("text/css"));
    103     const AtomicString& n = getAttribute(contentStyleTypeAttr);
    104     return n.isNull() ? defaultValue : n;
    105 }
    106 
    107 void SVGSVGElement::setContentStyleType(const AtomicString& type)
    108 {
    109     setAttribute(SVGNames::contentStyleTypeAttr, type);
    110 }
    111 
    112 FloatRect SVGSVGElement::viewport() const
    113 {
    114     double _x = 0.0;
    115     double _y = 0.0;
    116     if (!isOutermostSVG()) {
    117         _x = x().value(this);
    118         _y = y().value(this);
    119     }
    120     float w = width().value(this);
    121     float h = height().value(this);
    122     AffineTransform viewBox = viewBoxToViewTransform(w, h);
    123     double wDouble = w;
    124     double hDouble = h;
    125     viewBox.map(_x, _y, _x, _y);
    126     viewBox.map(w, h, wDouble, hDouble);
    127     return FloatRect::narrowPrecision(_x, _y, wDouble, hDouble);
    128 }
    129 
    130 int SVGSVGElement::relativeWidthValue() const
    131 {
    132     SVGLength w = width();
    133     if (w.unitType() != LengthTypePercentage)
    134         return 0;
    135 
    136     return static_cast<int>(w.valueAsPercentage() * m_containerSize.width());
    137 }
    138 
    139 int SVGSVGElement::relativeHeightValue() const
    140 {
    141     SVGLength h = height();
    142     if (h.unitType() != LengthTypePercentage)
    143         return 0;
    144 
    145     return static_cast<int>(h.valueAsPercentage() * m_containerSize.height());
    146 }
    147 
    148 float SVGSVGElement::pixelUnitToMillimeterX() const
    149 {
    150     // 2.54 / cssPixelsPerInch gives CM.
    151     return (2.54f / cssPixelsPerInch) * 10.0f;
    152 }
    153 
    154 float SVGSVGElement::pixelUnitToMillimeterY() const
    155 {
    156     // 2.54 / cssPixelsPerInch gives CM.
    157     return (2.54f / cssPixelsPerInch) * 10.0f;
    158 }
    159 
    160 float SVGSVGElement::screenPixelToMillimeterX() const
    161 {
    162     return pixelUnitToMillimeterX();
    163 }
    164 
    165 float SVGSVGElement::screenPixelToMillimeterY() const
    166 {
    167     return pixelUnitToMillimeterY();
    168 }
    169 
    170 bool SVGSVGElement::useCurrentView() const
    171 {
    172     return m_useCurrentView;
    173 }
    174 
    175 void SVGSVGElement::setUseCurrentView(bool currentView)
    176 {
    177     m_useCurrentView = currentView;
    178 }
    179 
    180 SVGViewSpec* SVGSVGElement::currentView() const
    181 {
    182     if (!m_viewSpec)
    183         m_viewSpec.set(new SVGViewSpec(this));
    184 
    185     return m_viewSpec.get();
    186 }
    187 
    188 float SVGSVGElement::currentScale() const
    189 {
    190     if (document() && parentNode() == document())
    191         return document()->frame() ? document()->frame()->zoomFactor() : 1;
    192     return m_scale;
    193 }
    194 
    195 void SVGSVGElement::setCurrentScale(float scale)
    196 {
    197     if (document() && parentNode() == document()) {
    198         if (document()->frame())
    199             document()->frame()->setZoomFactor(scale, true);
    200         return;
    201     }
    202 
    203     m_scale = scale;
    204     if (renderer())
    205         renderer()->setNeedsLayout(true);
    206 }
    207 
    208 FloatPoint SVGSVGElement::currentTranslate() const
    209 {
    210     return m_translation;
    211 }
    212 
    213 void SVGSVGElement::setCurrentTranslate(const FloatPoint &translation)
    214 {
    215     m_translation = translation;
    216     if (parentNode() == document() && document()->renderer())
    217         document()->renderer()->repaint();
    218 }
    219 
    220 void SVGSVGElement::parseMappedAttribute(MappedAttribute* attr)
    221 {
    222     if (!nearestViewportElement()) {
    223         bool setListener = true;
    224 
    225         // Only handle events if we're the outermost <svg> element
    226         if (attr->name() == onunloadAttr)
    227             document()->setWindowAttributeEventListener(eventNames().unloadEvent, createAttributeEventListener(document()->frame(), attr));
    228         else if (attr->name() == onresizeAttr)
    229             document()->setWindowAttributeEventListener(eventNames().resizeEvent, createAttributeEventListener(document()->frame(), attr));
    230         else if (attr->name() == onscrollAttr)
    231             document()->setWindowAttributeEventListener(eventNames().scrollEvent, createAttributeEventListener(document()->frame(), attr));
    232         else if (attr->name() == SVGNames::onzoomAttr)
    233             document()->setWindowAttributeEventListener(eventNames().zoomEvent, createAttributeEventListener(document()->frame(), attr));
    234         else
    235             setListener = false;
    236 
    237         if (setListener)
    238             return;
    239     }
    240 
    241     if (attr->name() == onabortAttr)
    242         document()->setWindowAttributeEventListener(eventNames().abortEvent, createAttributeEventListener(document()->frame(), attr));
    243     else if (attr->name() == onerrorAttr)
    244         document()->setWindowAttributeEventListener(eventNames().errorEvent, createAttributeEventListener(document()->frame(), attr));
    245     else if (attr->name() == SVGNames::xAttr)
    246         setXBaseValue(SVGLength(LengthModeWidth, attr->value()));
    247     else if (attr->name() == SVGNames::yAttr)
    248         setYBaseValue(SVGLength(LengthModeHeight, attr->value()));
    249     else if (attr->name() == SVGNames::widthAttr) {
    250         setWidthBaseValue(SVGLength(LengthModeWidth, attr->value()));
    251         addCSSProperty(attr, CSSPropertyWidth, attr->value());
    252         if (widthBaseValue().value(this) < 0.0)
    253             document()->accessSVGExtensions()->reportError("A negative value for svg attribute <width> is not allowed");
    254     } else if (attr->name() == SVGNames::heightAttr) {
    255         setHeightBaseValue(SVGLength(LengthModeHeight, attr->value()));
    256         addCSSProperty(attr, CSSPropertyHeight, attr->value());
    257         if (heightBaseValue().value(this) < 0.0)
    258             document()->accessSVGExtensions()->reportError("A negative value for svg attribute <height> is not allowed");
    259     } else {
    260         if (SVGTests::parseMappedAttribute(attr))
    261             return;
    262         if (SVGLangSpace::parseMappedAttribute(attr))
    263             return;
    264         if (SVGExternalResourcesRequired::parseMappedAttribute(attr))
    265             return;
    266         if (SVGFitToViewBox::parseMappedAttribute(document(), attr))
    267             return;
    268         if (SVGZoomAndPan::parseMappedAttribute(attr))
    269             return;
    270 
    271         SVGStyledLocatableElement::parseMappedAttribute(attr);
    272     }
    273 }
    274 
    275 // This hack will not handle the case where we're setting a width/height
    276 // on a root <svg> via svg.width.baseValue = when it has none.
    277 static void updateCSSForAttribute(SVGSVGElement* element, const QualifiedName& attrName, CSSPropertyID property, const SVGLength& value)
    278 {
    279     Attribute* attribute = element->attributes(false)->getAttributeItem(attrName);
    280     if (!attribute || !attribute->isMappedAttribute())
    281         return;
    282     element->addCSSProperty(static_cast<MappedAttribute*>(attribute), property, value.valueAsString());
    283 }
    284 
    285 void SVGSVGElement::svgAttributeChanged(const QualifiedName& attrName)
    286 {
    287     SVGStyledElement::svgAttributeChanged(attrName);
    288 
    289     // FIXME: Ugly, ugly hack to around that parseMappedAttribute is not called
    290     // when svg.width.baseValue = 100 is evaluated.
    291     // Thus the CSS length value for width is not updated, and width() calcWidth()
    292     // calculations on RenderSVGRoot will be wrong.
    293     // https://bugs.webkit.org/show_bug.cgi?id=25387
    294     if (attrName == SVGNames::widthAttr)
    295         updateCSSForAttribute(this, attrName, CSSPropertyWidth, widthBaseValue());
    296     else if (attrName == SVGNames::heightAttr)
    297         updateCSSForAttribute(this, attrName, CSSPropertyHeight, heightBaseValue());
    298 
    299     if (!renderer())
    300         return;
    301 
    302     if (attrName == SVGNames::xAttr || attrName == SVGNames::yAttr ||
    303         SVGTests::isKnownAttribute(attrName) ||
    304         SVGLangSpace::isKnownAttribute(attrName) ||
    305         SVGExternalResourcesRequired::isKnownAttribute(attrName) ||
    306         SVGFitToViewBox::isKnownAttribute(attrName) ||
    307         SVGZoomAndPan::isKnownAttribute(attrName) ||
    308         SVGStyledLocatableElement::isKnownAttribute(attrName))
    309         renderer()->setNeedsLayout(true);
    310 }
    311 
    312 void SVGSVGElement::synchronizeProperty(const QualifiedName& attrName)
    313 {
    314     SVGStyledElement::synchronizeProperty(attrName);
    315 
    316     if (attrName == anyQName()) {
    317         synchronizeX();
    318         synchronizeY();
    319         synchronizeWidth();
    320         synchronizeHeight();
    321         synchronizeExternalResourcesRequired();
    322         synchronizeViewBox();
    323         synchronizePreserveAspectRatio();
    324         return;
    325     }
    326 
    327     if (attrName == SVGNames::xAttr)
    328         synchronizeX();
    329     else if (attrName == SVGNames::yAttr)
    330         synchronizeY();
    331     else if (attrName == SVGNames::widthAttr)
    332         synchronizeWidth();
    333     else if (attrName == SVGNames::heightAttr)
    334         synchronizeHeight();
    335     else if (SVGExternalResourcesRequired::isKnownAttribute(attrName))
    336         synchronizeExternalResourcesRequired();
    337     else if (SVGFitToViewBox::isKnownAttribute(attrName)) {
    338         synchronizeViewBox();
    339         synchronizePreserveAspectRatio();
    340     }
    341 }
    342 
    343 unsigned SVGSVGElement::suspendRedraw(unsigned /* maxWaitMilliseconds */)
    344 {
    345     // FIXME: Implement me (see bug 11275)
    346     return 0;
    347 }
    348 
    349 void SVGSVGElement::unsuspendRedraw(unsigned /* suspendHandleId */)
    350 {
    351     // FIXME: Implement me (see bug 11275)
    352 }
    353 
    354 void SVGSVGElement::unsuspendRedrawAll()
    355 {
    356     // FIXME: Implement me (see bug 11275)
    357 }
    358 
    359 void SVGSVGElement::forceRedraw()
    360 {
    361     // FIXME: Implement me (see bug 11275)
    362 }
    363 
    364 NodeList* SVGSVGElement::getIntersectionList(const FloatRect&, SVGElement*)
    365 {
    366     // FIXME: Implement me (see bug 11274)
    367     return 0;
    368 }
    369 
    370 NodeList* SVGSVGElement::getEnclosureList(const FloatRect&, SVGElement*)
    371 {
    372     // FIXME: Implement me (see bug 11274)
    373     return 0;
    374 }
    375 
    376 bool SVGSVGElement::checkIntersection(SVGElement*, const FloatRect& rect)
    377 {
    378     // TODO : take into account pointer-events?
    379     // FIXME: Why is element ignored??
    380     // FIXME: Implement me (see bug 11274)
    381     return rect.intersects(getBBox());
    382 }
    383 
    384 bool SVGSVGElement::checkEnclosure(SVGElement*, const FloatRect& rect)
    385 {
    386     // TODO : take into account pointer-events?
    387     // FIXME: Why is element ignored??
    388     // FIXME: Implement me (see bug 11274)
    389     return rect.contains(getBBox());
    390 }
    391 
    392 void SVGSVGElement::deselectAll()
    393 {
    394     document()->frame()->selection()->clear();
    395 }
    396 
    397 float SVGSVGElement::createSVGNumber()
    398 {
    399     return 0.0f;
    400 }
    401 
    402 SVGLength SVGSVGElement::createSVGLength()
    403 {
    404     return SVGLength();
    405 }
    406 
    407 SVGAngle SVGSVGElement::createSVGAngle()
    408 {
    409     return SVGAngle();
    410 }
    411 
    412 FloatPoint SVGSVGElement::createSVGPoint()
    413 {
    414     return FloatPoint();
    415 }
    416 
    417 AffineTransform SVGSVGElement::createSVGMatrix()
    418 {
    419     return AffineTransform();
    420 }
    421 
    422 FloatRect SVGSVGElement::createSVGRect()
    423 {
    424     return FloatRect();
    425 }
    426 
    427 SVGTransform SVGSVGElement::createSVGTransform()
    428 {
    429     return SVGTransform();
    430 }
    431 
    432 SVGTransform SVGSVGElement::createSVGTransformFromMatrix(const AffineTransform& matrix)
    433 {
    434     return SVGTransform(matrix);
    435 }
    436 
    437 AffineTransform SVGSVGElement::getCTM() const
    438 {
    439     AffineTransform mat;
    440     if (!isOutermostSVG())
    441         mat.translate(x().value(this), y().value(this));
    442 
    443     if (attributes()->getAttributeItem(SVGNames::viewBoxAttr)) {
    444         AffineTransform viewBox = viewBoxToViewTransform(width().value(this), height().value(this));
    445         mat = viewBox * mat;
    446     }
    447 
    448     return mat;
    449 }
    450 
    451 AffineTransform SVGSVGElement::getScreenCTM() const
    452 {
    453     document()->updateLayoutIgnorePendingStylesheets();
    454     FloatPoint rootLocation;
    455 
    456     if (RenderObject* renderer = this->renderer()) {
    457         if (isOutermostSVG()) {
    458             // FIXME: This doesn't work correctly with CSS transforms.
    459             FloatPoint point;
    460             if (renderer->parent())
    461                 point = renderer->localToAbsolute(point, false, true);
    462             rootLocation.move(point.x(), point.y());
    463         } else
    464             rootLocation.move(x().value(this), y().value(this));
    465     }
    466 
    467     AffineTransform mat = SVGStyledLocatableElement::getScreenCTM();
    468     mat.translate(rootLocation.x(), rootLocation.y());
    469 
    470     if (attributes()->getAttributeItem(SVGNames::viewBoxAttr)) {
    471         AffineTransform viewBox = viewBoxToViewTransform(width().value(this), height().value(this));
    472         mat = viewBox * mat;
    473     }
    474 
    475     return mat;
    476 }
    477 
    478 RenderObject* SVGSVGElement::createRenderer(RenderArena* arena, RenderStyle*)
    479 {
    480     if (isOutermostSVG())
    481         return new (arena) RenderSVGRoot(this);
    482     else
    483         return new (arena) RenderSVGViewportContainer(this);
    484 }
    485 
    486 void SVGSVGElement::insertedIntoDocument()
    487 {
    488     document()->accessSVGExtensions()->addTimeContainer(this);
    489     SVGStyledLocatableElement::insertedIntoDocument();
    490 }
    491 
    492 void SVGSVGElement::removedFromDocument()
    493 {
    494     document()->accessSVGExtensions()->removeTimeContainer(this);
    495     SVGStyledLocatableElement::removedFromDocument();
    496 }
    497 
    498 void SVGSVGElement::pauseAnimations()
    499 {
    500     if (!m_timeContainer->isPaused())
    501         m_timeContainer->pause();
    502 }
    503 
    504 void SVGSVGElement::unpauseAnimations()
    505 {
    506     if (m_timeContainer->isPaused())
    507         m_timeContainer->resume();
    508 }
    509 
    510 bool SVGSVGElement::animationsPaused() const
    511 {
    512     return m_timeContainer->isPaused();
    513 }
    514 
    515 float SVGSVGElement::getCurrentTime() const
    516 {
    517     return narrowPrecisionToFloat(m_timeContainer->elapsed().value());
    518 }
    519 
    520 void SVGSVGElement::setCurrentTime(float /* seconds */)
    521 {
    522     // FIXME: Implement me, bug 12073
    523 }
    524 
    525 bool SVGSVGElement::hasRelativeValues() const
    526 {
    527     return (x().isRelative() || width().isRelative() ||
    528             y().isRelative() || height().isRelative());
    529 }
    530 
    531 bool SVGSVGElement::isOutermostSVG() const
    532 {
    533     // Element may not be in the document, pretend we're outermost for viewport(), getCTM(), etc.
    534     if (!parentNode())
    535         return true;
    536 
    537     // This is true whenever this is the outermost SVG, even if there are HTML elements outside it
    538     return !parentNode()->isSVGElement();
    539 }
    540 
    541 AffineTransform SVGSVGElement::viewBoxToViewTransform(float viewWidth, float viewHeight) const
    542 {
    543     FloatRect viewBoxRect;
    544     if (useCurrentView()) {
    545         if (currentView()) // what if we should use it but it is not set?
    546             viewBoxRect = currentView()->viewBox();
    547     } else
    548         viewBoxRect = viewBox();
    549 
    550     AffineTransform ctm = SVGFitToViewBox::viewBoxToViewTransform(viewBoxRect, preserveAspectRatio(), viewWidth, viewHeight);
    551     if (useCurrentView() && currentView())
    552         return currentView()->transform()->concatenate().matrix() * ctm;
    553 
    554     return ctm;
    555 }
    556 
    557 void SVGSVGElement::inheritViewAttributes(SVGViewElement* viewElement)
    558 {
    559     setUseCurrentView(true);
    560     if (viewElement->hasAttribute(SVGNames::viewBoxAttr))
    561         currentView()->setViewBox(viewElement->viewBox());
    562     else
    563         currentView()->setViewBox(viewBox());
    564 
    565     SVGPreserveAspectRatio aspectRatio;
    566     if (viewElement->hasAttribute(SVGNames::preserveAspectRatioAttr))
    567         aspectRatio = viewElement->preserveAspectRatioBaseValue();
    568     else
    569         aspectRatio = preserveAspectRatioBaseValue();
    570     currentView()->setPreserveAspectRatioBaseValue(aspectRatio);
    571 
    572     if (viewElement->hasAttribute(SVGNames::zoomAndPanAttr))
    573         currentView()->setZoomAndPan(viewElement->zoomAndPan());
    574     renderer()->setNeedsLayout(true);
    575 }
    576 
    577 void SVGSVGElement::documentWillBecomeInactive()
    578 {
    579     pauseAnimations();
    580 }
    581 
    582 void SVGSVGElement::documentDidBecomeActive()
    583 {
    584     unpauseAnimations();
    585 }
    586 
    587 }
    588 
    589 #endif // ENABLE(SVG)
    590 
    591 // vim:ts=4:noet
    592