Home | History | Annotate | Download | only in svg
      1 /*
      2  * Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <zimmermann (at) kde.org>
      3  * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2010 Rob Buis <buis (at) kde.org>
      4  * Copyright (C) 2007 Apple Inc. All rights reserved.
      5  * Copyright (C) 2014 Google, Inc.
      6  *
      7  * This library is free software; you can redistribute it and/or
      8  * modify it under the terms of the GNU Library General Public
      9  * License as published by the Free Software Foundation; either
     10  * version 2 of the License, or (at your option) any later version.
     11  *
     12  * This library is distributed in the hope that it will be useful,
     13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15  * Library General Public License for more details.
     16  *
     17  * You should have received a copy of the GNU Library General Public License
     18  * along with this library; see the file COPYING.LIB.  If not, write to
     19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     20  * Boston, MA 02110-1301, USA.
     21  */
     22 
     23 #include "config.h"
     24 
     25 #include "core/svg/SVGSVGElement.h"
     26 
     27 #include "bindings/v8/ScriptEventListener.h"
     28 #include "core/HTMLNames.h"
     29 #include "core/SVGNames.h"
     30 #include "core/css/CSSHelper.h"
     31 #include "core/dom/Document.h"
     32 #include "core/dom/ElementTraversal.h"
     33 #include "core/dom/StaticNodeList.h"
     34 #include "core/editing/FrameSelection.h"
     35 #include "core/events/EventListener.h"
     36 #include "core/frame/LocalFrame.h"
     37 #include "core/page/FrameTree.h"
     38 #include "core/frame/FrameView.h"
     39 #include "core/frame/UseCounter.h"
     40 #include "core/rendering/RenderObject.h"
     41 #include "core/rendering/RenderPart.h"
     42 #include "core/rendering/svg/RenderSVGModelObject.h"
     43 #include "core/rendering/svg/RenderSVGResource.h"
     44 #include "core/rendering/svg/RenderSVGRoot.h"
     45 #include "core/rendering/svg/RenderSVGViewportContainer.h"
     46 #include "core/svg/SVGAngleTearOff.h"
     47 #include "core/svg/SVGNumberTearOff.h"
     48 #include "core/svg/SVGPreserveAspectRatio.h"
     49 #include "core/svg/SVGRectTearOff.h"
     50 #include "core/svg/SVGTransform.h"
     51 #include "core/svg/SVGTransformList.h"
     52 #include "core/svg/SVGTransformTearOff.h"
     53 #include "core/svg/SVGViewElement.h"
     54 #include "core/svg/SVGViewSpec.h"
     55 #include "core/svg/animation/SMILTimeContainer.h"
     56 #include "platform/FloatConversion.h"
     57 #include "platform/LengthFunctions.h"
     58 #include "platform/geometry/FloatRect.h"
     59 #include "platform/transforms/AffineTransform.h"
     60 #include "wtf/StdLibExtras.h"
     61 
     62 namespace WebCore {
     63 
     64 inline SVGSVGElement::SVGSVGElement(Document& doc)
     65     : SVGGraphicsElement(SVGNames::svgTag, doc)
     66     , SVGFitToViewBox(this)
     67     , m_x(SVGAnimatedLength::create(this, SVGNames::xAttr, SVGLength::create(LengthModeWidth), AllowNegativeLengths))
     68     , m_y(SVGAnimatedLength::create(this, SVGNames::yAttr, SVGLength::create(LengthModeHeight), AllowNegativeLengths))
     69     , m_width(SVGAnimatedLength::create(this, SVGNames::widthAttr, SVGLength::create(LengthModeWidth), ForbidNegativeLengths))
     70     , m_height(SVGAnimatedLength::create(this, SVGNames::heightAttr, SVGLength::create(LengthModeHeight), ForbidNegativeLengths))
     71     , m_useCurrentView(false)
     72     , m_timeContainer(SMILTimeContainer::create(*this))
     73     , m_translation(SVGPoint::create())
     74 {
     75     ScriptWrappable::init(this);
     76 
     77     m_width->setDefaultValueAsString("100%");
     78     m_height->setDefaultValueAsString("100%");
     79 
     80     addToPropertyMap(m_x);
     81     addToPropertyMap(m_y);
     82     addToPropertyMap(m_width);
     83     addToPropertyMap(m_height);
     84 
     85     UseCounter::count(doc, UseCounter::SVGSVGElement);
     86 }
     87 
     88 DEFINE_NODE_FACTORY(SVGSVGElement)
     89 
     90 SVGSVGElement::~SVGSVGElement()
     91 {
     92 #if !ENABLE(OILPAN)
     93     if (m_viewSpec)
     94         m_viewSpec->detachContextElement();
     95 
     96     // There are cases where removedFromDocument() is not called.
     97     // see ContainerNode::removeAllChildren, called by its destructor.
     98     // With Oilpan, either removedFrom is called or the document
     99     // is dead as well and there is no reason to clear the extensions.
    100     document().accessSVGExtensions().removeTimeContainer(this);
    101 
    102     ASSERT(inDocument() || !accessDocumentSVGExtensions().isSVGRootWithRelativeLengthDescendents(this));
    103 #endif
    104 }
    105 
    106 PassRefPtr<SVGRectTearOff> SVGSVGElement::viewport() const
    107 {
    108     // FIXME: This method doesn't follow the spec and is basically untested. Parent documents are not considered here.
    109     // As we have no test coverage for this, we're going to disable it completly for now.
    110     return SVGRectTearOff::create(SVGRect::create(), 0, PropertyIsNotAnimVal);
    111 }
    112 
    113 float SVGSVGElement::pixelUnitToMillimeterX() const
    114 {
    115     return 1 / cssPixelsPerMillimeter;
    116 }
    117 
    118 float SVGSVGElement::pixelUnitToMillimeterY() const
    119 {
    120     return 1 / cssPixelsPerMillimeter;
    121 }
    122 
    123 float SVGSVGElement::screenPixelToMillimeterX() const
    124 {
    125     return pixelUnitToMillimeterX();
    126 }
    127 
    128 float SVGSVGElement::screenPixelToMillimeterY() const
    129 {
    130     return pixelUnitToMillimeterY();
    131 }
    132 
    133 SVGViewSpec* SVGSVGElement::currentView()
    134 {
    135     if (!m_viewSpec)
    136         m_viewSpec = SVGViewSpec::create(this);
    137     return m_viewSpec.get();
    138 }
    139 
    140 float SVGSVGElement::currentScale() const
    141 {
    142     if (!inDocument() || !isOutermostSVGSVGElement())
    143         return 1;
    144 
    145     LocalFrame* frame = document().frame();
    146     if (!frame)
    147         return 1;
    148 
    149     const FrameTree& frameTree = frame->tree();
    150 
    151     // The behaviour of currentScale() is undefined, when we're dealing with non-standalone SVG documents.
    152     // If the svg is embedded, the scaling is handled by the host renderer, so when asking from inside
    153     // the SVG document, a scale value of 1 seems reasonable, as it doesn't know anything about the parent scale.
    154     return frameTree.parent() ? 1 : frame->pageZoomFactor();
    155 }
    156 
    157 void SVGSVGElement::setCurrentScale(float scale)
    158 {
    159     if (!inDocument() || !isOutermostSVGSVGElement())
    160         return;
    161 
    162     LocalFrame* frame = document().frame();
    163     if (!frame)
    164         return;
    165 
    166     const FrameTree& frameTree = frame->tree();
    167 
    168     // The behaviour of setCurrentScale() is undefined, when we're dealing with non-standalone SVG documents.
    169     // We choose the ignore this call, it's pretty useless to support calling setCurrentScale() from within
    170     // an embedded SVG document, for the same reasons as in currentScale() - needs resolution by SVG WG.
    171     if (frameTree.parent())
    172         return;
    173 
    174     frame->setPageZoomFactor(scale);
    175 }
    176 
    177 class SVGCurrentTranslateTearOff : public SVGPointTearOff {
    178 public:
    179     static PassRefPtr<SVGCurrentTranslateTearOff> create(SVGSVGElement* contextElement)
    180     {
    181         return adoptRef(new SVGCurrentTranslateTearOff(contextElement));
    182     }
    183 
    184     virtual void commitChange() OVERRIDE
    185     {
    186         ASSERT(contextElement());
    187         toSVGSVGElement(contextElement())->updateCurrentTranslate();
    188     }
    189 
    190 private:
    191     SVGCurrentTranslateTearOff(SVGSVGElement* contextElement)
    192         : SVGPointTearOff(contextElement->m_translation, contextElement, PropertyIsNotAnimVal)
    193     {
    194     }
    195 };
    196 
    197 PassRefPtr<SVGPointTearOff> SVGSVGElement::currentTranslateFromJavascript()
    198 {
    199     return SVGCurrentTranslateTearOff::create(this);
    200 }
    201 
    202 void SVGSVGElement::setCurrentTranslate(const FloatPoint& point)
    203 {
    204     m_translation->setValue(point);
    205     updateCurrentTranslate();
    206 }
    207 
    208 void SVGSVGElement::updateCurrentTranslate()
    209 {
    210     if (RenderObject* object = renderer())
    211         object->setNeedsLayoutAndFullPaintInvalidation();
    212 }
    213 
    214 void SVGSVGElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
    215 {
    216     SVGParsingError parseError = NoError;
    217 
    218     if (!nearestViewportElement()) {
    219         bool setListener = true;
    220 
    221         // Only handle events if we're the outermost <svg> element
    222         if (name == HTMLNames::onunloadAttr)
    223             document().setWindowAttributeEventListener(EventTypeNames::unload, createAttributeEventListener(document().frame(), name, value, eventParameterName()));
    224         else if (name == HTMLNames::onresizeAttr)
    225             document().setWindowAttributeEventListener(EventTypeNames::resize, createAttributeEventListener(document().frame(), name, value, eventParameterName()));
    226         else if (name == HTMLNames::onscrollAttr)
    227             document().setWindowAttributeEventListener(EventTypeNames::scroll, createAttributeEventListener(document().frame(), name, value, eventParameterName()));
    228         else if (name == SVGNames::onzoomAttr)
    229             document().setWindowAttributeEventListener(EventTypeNames::zoom, createAttributeEventListener(document().frame(), name, value, eventParameterName()));
    230         else
    231             setListener = false;
    232 
    233         if (setListener)
    234             return;
    235     }
    236 
    237     if (name == HTMLNames::onabortAttr) {
    238         document().setWindowAttributeEventListener(EventTypeNames::abort, createAttributeEventListener(document().frame(), name, value, eventParameterName()));
    239     } else if (name == HTMLNames::onerrorAttr) {
    240         document().setWindowAttributeEventListener(EventTypeNames::error, createAttributeEventListener(document().frame(), name, value, eventParameterName()));
    241     } else if (name == SVGNames::xAttr) {
    242         m_x->setBaseValueAsString(value, parseError);
    243     } else if (name == SVGNames::yAttr) {
    244         m_y->setBaseValueAsString(value, parseError);
    245     } else if (name == SVGNames::widthAttr) {
    246         m_width->setBaseValueAsString(value, parseError);
    247     } else if (name == SVGNames::heightAttr) {
    248         m_height->setBaseValueAsString(value, parseError);
    249     } else if (SVGFitToViewBox::parseAttribute(name, value, document(), parseError)) {
    250     } else if (SVGZoomAndPan::parseAttribute(name, value)) {
    251     } else {
    252         SVGGraphicsElement::parseAttribute(name, value);
    253     }
    254 
    255     reportAttributeParsingError(parseError, name, value);
    256 }
    257 
    258 bool SVGSVGElement::isPresentationAttribute(const QualifiedName& name) const
    259 {
    260     if (isOutermostSVGSVGElement() && (name == SVGNames::widthAttr || name == SVGNames::heightAttr))
    261         return true;
    262     return SVGGraphicsElement::isPresentationAttribute(name);
    263 }
    264 
    265 void SVGSVGElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style)
    266 {
    267     if (isOutermostSVGSVGElement() && (name == SVGNames::widthAttr || name == SVGNames::heightAttr)) {
    268         RefPtr<SVGLength> length = SVGLength::create(LengthModeOther);
    269         TrackExceptionState exceptionState;
    270         length->setValueAsString(value, exceptionState);
    271         if (!exceptionState.hadException()) {
    272             if (name == SVGNames::widthAttr)
    273                 addPropertyToPresentationAttributeStyle(style, CSSPropertyWidth, value);
    274             else if (name == SVGNames::heightAttr)
    275                 addPropertyToPresentationAttributeStyle(style, CSSPropertyHeight, value);
    276         }
    277     } else {
    278         SVGGraphicsElement::collectStyleForPresentationAttribute(name, value, style);
    279     }
    280 }
    281 
    282 void SVGSVGElement::svgAttributeChanged(const QualifiedName& attrName)
    283 {
    284     bool updateRelativeLengthsOrViewBox = false;
    285     bool widthChanged = attrName == SVGNames::widthAttr;
    286     bool heightChanged = attrName == SVGNames::heightAttr;
    287     if (widthChanged || heightChanged
    288         || attrName == SVGNames::xAttr
    289         || attrName == SVGNames::yAttr) {
    290         updateRelativeLengthsOrViewBox = true;
    291         updateRelativeLengthsInformation();
    292         invalidateRelativeLengthClients();
    293 
    294         // At the SVG/HTML boundary (aka RenderSVGRoot), the width and
    295         // height attributes can affect the replaced size so we need
    296         // to mark it for updating.
    297         //
    298         // FIXME: For width/height animated as XML attributes on SVG
    299         // roots, there is an attribute synchronization missing. See
    300         // http://crbug.com/364807
    301         if (widthChanged || heightChanged) {
    302             RenderObject* renderObject = renderer();
    303             if (renderObject && renderObject->isSVGRoot()) {
    304                 invalidateSVGPresentationAttributeStyle();
    305                 setNeedsStyleRecalc(LocalStyleChange);
    306             }
    307         }
    308     }
    309 
    310     if (SVGFitToViewBox::isKnownAttribute(attrName)) {
    311         updateRelativeLengthsOrViewBox = true;
    312         if (RenderObject* object = renderer())
    313             object->setNeedsTransformUpdate();
    314     }
    315 
    316     SVGElement::InvalidationGuard invalidationGuard(this);
    317 
    318     if (updateRelativeLengthsOrViewBox
    319         || SVGZoomAndPan::isKnownAttribute(attrName)) {
    320         if (renderer())
    321             RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer());
    322         return;
    323     }
    324 
    325     SVGGraphicsElement::svgAttributeChanged(attrName);
    326 }
    327 
    328 // FloatRect::intersects does not consider horizontal or vertical lines (because of isEmpty()).
    329 static bool intersectsAllowingEmpty(const FloatRect& r1, const FloatRect& r2)
    330 {
    331     if (r1.width() < 0 || r1.height() < 0 || r2.width() < 0 || r2.height() < 0)
    332         return false;
    333 
    334     return r1.x() < r2.maxX() && r2.x() < r1.maxX()
    335         && r1.y() < r2.maxY() && r2.y() < r1.maxY();
    336 }
    337 
    338 // One of the element types that can cause graphics to be drawn onto the target canvas.
    339 // Specifically: circle, ellipse, image, line, path, polygon, polyline, rect, text and use.
    340 static bool isIntersectionOrEnclosureTarget(RenderObject* renderer)
    341 {
    342     return renderer->isSVGShape()
    343         || renderer->isSVGText()
    344         || renderer->isSVGImage()
    345         || isSVGUseElement(*renderer->node());
    346 }
    347 
    348 bool SVGSVGElement::checkIntersectionOrEnclosure(const SVGElement& element, const FloatRect& rect,
    349     CheckIntersectionOrEnclosure mode) const
    350 {
    351     RenderObject* renderer = element.renderer();
    352     ASSERT(!renderer || renderer->style());
    353     if (!renderer || renderer->style()->pointerEvents() == PE_NONE)
    354         return false;
    355 
    356     if (!isIntersectionOrEnclosureTarget(renderer))
    357         return false;
    358 
    359     AffineTransform ctm = toSVGGraphicsElement(element).computeCTM(AncestorScope, DisallowStyleUpdate, this);
    360     FloatRect mappedRepaintRect = ctm.mapRect(renderer->paintInvalidationRectInLocalCoordinates());
    361 
    362     bool result = false;
    363     switch (mode) {
    364     case CheckIntersection:
    365         result = intersectsAllowingEmpty(rect, mappedRepaintRect);
    366         break;
    367     case CheckEnclosure:
    368         result = rect.contains(mappedRepaintRect);
    369         break;
    370     default:
    371         ASSERT_NOT_REACHED();
    372         break;
    373     }
    374 
    375     return result;
    376 }
    377 
    378 PassRefPtrWillBeRawPtr<StaticNodeList> SVGSVGElement::collectIntersectionOrEnclosureList(const FloatRect& rect,
    379     SVGElement* referenceElement, CheckIntersectionOrEnclosure mode) const
    380 {
    381     WillBeHeapVector<RefPtrWillBeMember<Node> > nodes;
    382 
    383     const SVGElement* root = this;
    384     if (referenceElement) {
    385         // Only the common subtree needs to be traversed.
    386         if (contains(referenceElement)) {
    387             root = referenceElement;
    388         } else if (!isDescendantOf(referenceElement)) {
    389             // No common subtree.
    390             return StaticNodeList::adopt(nodes);
    391         }
    392     }
    393 
    394     for (SVGGraphicsElement* element = Traversal<SVGGraphicsElement>::firstWithin(*root); element;
    395         element = Traversal<SVGGraphicsElement>::next(*element, root)) {
    396         if (checkIntersectionOrEnclosure(*element, rect, mode))
    397             nodes.append(element);
    398     }
    399 
    400     return StaticNodeList::adopt(nodes);
    401 }
    402 
    403 PassRefPtrWillBeRawPtr<StaticNodeList> SVGSVGElement::getIntersectionList(PassRefPtr<SVGRectTearOff> rect, SVGElement* referenceElement) const
    404 {
    405     document().updateLayoutIgnorePendingStylesheets();
    406 
    407     return collectIntersectionOrEnclosureList(rect->target()->value(), referenceElement, CheckIntersection);
    408 }
    409 
    410 PassRefPtrWillBeRawPtr<StaticNodeList> SVGSVGElement::getEnclosureList(PassRefPtr<SVGRectTearOff> rect, SVGElement* referenceElement) const
    411 {
    412     document().updateLayoutIgnorePendingStylesheets();
    413 
    414     return collectIntersectionOrEnclosureList(rect->target()->value(), referenceElement, CheckEnclosure);
    415 }
    416 
    417 bool SVGSVGElement::checkIntersection(SVGElement* element, PassRefPtr<SVGRectTearOff> rect) const
    418 {
    419     ASSERT(element);
    420     document().updateLayoutIgnorePendingStylesheets();
    421 
    422     return checkIntersectionOrEnclosure(*element, rect->target()->value(), CheckIntersection);
    423 }
    424 
    425 bool SVGSVGElement::checkEnclosure(SVGElement* element, PassRefPtr<SVGRectTearOff> rect) const
    426 {
    427     ASSERT(element);
    428     document().updateLayoutIgnorePendingStylesheets();
    429 
    430     return checkIntersectionOrEnclosure(*element, rect->target()->value(), CheckEnclosure);
    431 }
    432 
    433 void SVGSVGElement::deselectAll()
    434 {
    435     if (LocalFrame* frame = document().frame())
    436         frame->selection().clear();
    437 }
    438 
    439 PassRefPtr<SVGNumberTearOff> SVGSVGElement::createSVGNumber()
    440 {
    441     return SVGNumberTearOff::create(SVGNumber::create(0.0f), 0, PropertyIsNotAnimVal);
    442 }
    443 
    444 PassRefPtr<SVGLengthTearOff> SVGSVGElement::createSVGLength()
    445 {
    446     return SVGLengthTearOff::create(SVGLength::create(), 0, PropertyIsNotAnimVal);
    447 }
    448 
    449 PassRefPtr<SVGAngleTearOff> SVGSVGElement::createSVGAngle()
    450 {
    451     return SVGAngleTearOff::create(SVGAngle::create(), 0, PropertyIsNotAnimVal);
    452 }
    453 
    454 PassRefPtr<SVGPointTearOff> SVGSVGElement::createSVGPoint()
    455 {
    456     return SVGPointTearOff::create(SVGPoint::create(), 0, PropertyIsNotAnimVal);
    457 }
    458 
    459 PassRefPtr<SVGMatrixTearOff> SVGSVGElement::createSVGMatrix()
    460 {
    461     return SVGMatrixTearOff::create(AffineTransform());
    462 }
    463 
    464 PassRefPtr<SVGRectTearOff> SVGSVGElement::createSVGRect()
    465 {
    466     return SVGRectTearOff::create(SVGRect::create(), 0, PropertyIsNotAnimVal);
    467 }
    468 
    469 PassRefPtr<SVGTransformTearOff> SVGSVGElement::createSVGTransform()
    470 {
    471     return SVGTransformTearOff::create(SVGTransform::create(SVG_TRANSFORM_MATRIX), 0, PropertyIsNotAnimVal);
    472 }
    473 
    474 PassRefPtr<SVGTransformTearOff> SVGSVGElement::createSVGTransformFromMatrix(PassRefPtr<SVGMatrixTearOff> matrix)
    475 {
    476     return SVGTransformTearOff::create(SVGTransform::create(matrix->value()), 0, PropertyIsNotAnimVal);
    477 }
    478 
    479 AffineTransform SVGSVGElement::localCoordinateSpaceTransform(SVGElement::CTMScope mode) const
    480 {
    481     AffineTransform viewBoxTransform;
    482     if (!hasEmptyViewBox()) {
    483         FloatSize size = currentViewportSize();
    484         viewBoxTransform = viewBoxToViewTransform(size.width(), size.height());
    485     }
    486 
    487     AffineTransform transform;
    488     if (!isOutermostSVGSVGElement()) {
    489         SVGLengthContext lengthContext(this);
    490         transform.translate(m_x->currentValue()->value(lengthContext), m_y->currentValue()->value(lengthContext));
    491     } else if (mode == SVGElement::ScreenScope) {
    492         if (RenderObject* renderer = this->renderer()) {
    493             FloatPoint location;
    494             float zoomFactor = 1;
    495 
    496             // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform
    497             // to map an element from SVG viewport coordinates to CSS box coordinates.
    498             // RenderSVGRoot's localToAbsolute method expects CSS box coordinates.
    499             // We also need to adjust for the zoom level factored into CSS coordinates (bug #96361).
    500             if (renderer->isSVGRoot()) {
    501                 location = toRenderSVGRoot(renderer)->localToBorderBoxTransform().mapPoint(location);
    502                 zoomFactor = 1 / renderer->style()->effectiveZoom();
    503             }
    504 
    505             // Translate in our CSS parent coordinate space
    506             // FIXME: This doesn't work correctly with CSS transforms.
    507             location = renderer->localToAbsolute(location, UseTransforms);
    508             location.scale(zoomFactor, zoomFactor);
    509 
    510             // Be careful here! localToBorderBoxTransform() included the x/y offset coming from the viewBoxToViewTransform(),
    511             // so we have to subtract it here (original cause of bug #27183)
    512             transform.translate(location.x() - viewBoxTransform.e(), location.y() - viewBoxTransform.f());
    513 
    514             // Respect scroll offset.
    515             if (FrameView* view = document().view()) {
    516                 LayoutSize scrollOffset = view->scrollOffset();
    517                 scrollOffset.scale(zoomFactor);
    518                 transform.translate(-scrollOffset.width(), -scrollOffset.height());
    519             }
    520         }
    521     }
    522 
    523     return transform.multiply(viewBoxTransform);
    524 }
    525 
    526 bool SVGSVGElement::rendererIsNeeded(const RenderStyle& style)
    527 {
    528     // FIXME: We should respect display: none on the documentElement svg element
    529     // but many things in FrameView and SVGImage depend on the RenderSVGRoot when
    530     // they should instead depend on the RenderView.
    531     // https://bugs.webkit.org/show_bug.cgi?id=103493
    532     if (document().documentElement() == this)
    533         return true;
    534     return Element::rendererIsNeeded(style);
    535 }
    536 
    537 RenderObject* SVGSVGElement::createRenderer(RenderStyle*)
    538 {
    539     if (isOutermostSVGSVGElement())
    540         return new RenderSVGRoot(this);
    541 
    542     return new RenderSVGViewportContainer(this);
    543 }
    544 
    545 Node::InsertionNotificationRequest SVGSVGElement::insertedInto(ContainerNode* rootParent)
    546 {
    547     if (rootParent->inDocument()) {
    548         UseCounter::count(document(), UseCounter::SVGSVGElementInDocument);
    549         if (rootParent->document().isXMLDocument())
    550             UseCounter::count(document(), UseCounter::SVGSVGElementInXMLDocument);
    551 
    552         document().accessSVGExtensions().addTimeContainer(this);
    553 
    554         // Animations are started at the end of document parsing and after firing the load event,
    555         // but if we miss that train (deferred programmatic element insertion for example) we need
    556         // to initialize the time container here.
    557         if (!document().parsing() && !document().processingLoadEvent() && document().loadEventFinished() && !timeContainer()->isStarted())
    558             timeContainer()->begin();
    559     }
    560     return SVGGraphicsElement::insertedInto(rootParent);
    561 }
    562 
    563 void SVGSVGElement::removedFrom(ContainerNode* rootParent)
    564 {
    565     if (rootParent->inDocument()) {
    566         SVGDocumentExtensions& svgExtensions = document().accessSVGExtensions();
    567         svgExtensions.removeTimeContainer(this);
    568         svgExtensions.removeSVGRootWithRelativeLengthDescendents(this);
    569     }
    570 
    571     SVGGraphicsElement::removedFrom(rootParent);
    572 }
    573 
    574 void SVGSVGElement::pauseAnimations()
    575 {
    576     if (!m_timeContainer->isPaused())
    577         m_timeContainer->pause();
    578 }
    579 
    580 void SVGSVGElement::unpauseAnimations()
    581 {
    582     if (m_timeContainer->isPaused())
    583         m_timeContainer->resume();
    584 }
    585 
    586 bool SVGSVGElement::animationsPaused() const
    587 {
    588     return m_timeContainer->isPaused();
    589 }
    590 
    591 float SVGSVGElement::getCurrentTime() const
    592 {
    593     return narrowPrecisionToFloat(m_timeContainer->elapsed().value());
    594 }
    595 
    596 void SVGSVGElement::setCurrentTime(float seconds)
    597 {
    598     if (std::isnan(seconds))
    599         return;
    600     seconds = max(seconds, 0.0f);
    601     m_timeContainer->setElapsed(seconds);
    602 }
    603 
    604 bool SVGSVGElement::selfHasRelativeLengths() const
    605 {
    606     return m_x->currentValue()->isRelative()
    607         || m_y->currentValue()->isRelative()
    608         || m_width->currentValue()->isRelative()
    609         || m_height->currentValue()->isRelative()
    610         || hasAttribute(SVGNames::viewBoxAttr);
    611 }
    612 
    613 FloatRect SVGSVGElement::currentViewBoxRect() const
    614 {
    615     if (m_useCurrentView)
    616         return m_viewSpec ? m_viewSpec->viewBox()->currentValue()->value() : FloatRect();
    617 
    618     FloatRect useViewBox = viewBox()->currentValue()->value();
    619     if (!useViewBox.isEmpty())
    620         return useViewBox;
    621     if (!renderer() || !renderer()->isSVGRoot())
    622         return FloatRect();
    623     if (!toRenderSVGRoot(renderer())->isEmbeddedThroughSVGImage())
    624         return FloatRect();
    625 
    626     // If no viewBox is specified but non-relative width/height values, then we
    627     // should always synthesize a viewBox if we're embedded through a SVGImage.
    628     return FloatRect(FloatPoint(), FloatSize(floatValueForLength(intrinsicWidth(), 0), floatValueForLength(intrinsicHeight(), 0)));
    629 }
    630 
    631 FloatSize SVGSVGElement::currentViewportSize() const
    632 {
    633     if (hasIntrinsicWidth() && hasIntrinsicHeight()) {
    634         Length intrinsicWidth = this->intrinsicWidth();
    635         Length intrinsicHeight = this->intrinsicHeight();
    636         return FloatSize(floatValueForLength(intrinsicWidth, 0), floatValueForLength(intrinsicHeight, 0));
    637     }
    638 
    639     if (!renderer())
    640         return FloatSize();
    641 
    642     if (renderer()->isSVGRoot()) {
    643         LayoutRect contentBoxRect = toRenderSVGRoot(renderer())->contentBoxRect();
    644         return FloatSize(contentBoxRect.width() / renderer()->style()->effectiveZoom(), contentBoxRect.height() / renderer()->style()->effectiveZoom());
    645     }
    646 
    647     FloatRect viewportRect = toRenderSVGViewportContainer(renderer())->viewport();
    648     return FloatSize(viewportRect.width(), viewportRect.height());
    649 }
    650 
    651 bool SVGSVGElement::hasIntrinsicWidth() const
    652 {
    653     return width()->currentValue()->unitType() != LengthTypePercentage;
    654 }
    655 
    656 bool SVGSVGElement::hasIntrinsicHeight() const
    657 {
    658     return height()->currentValue()->unitType() != LengthTypePercentage;
    659 }
    660 
    661 Length SVGSVGElement::intrinsicWidth() const
    662 {
    663     if (width()->currentValue()->unitType() == LengthTypePercentage)
    664         return Length(0, Fixed);
    665 
    666     SVGLengthContext lengthContext(this);
    667     return Length(width()->currentValue()->value(lengthContext), Fixed);
    668 }
    669 
    670 Length SVGSVGElement::intrinsicHeight() const
    671 {
    672     if (height()->currentValue()->unitType() == LengthTypePercentage)
    673         return Length(0, Fixed);
    674 
    675     SVGLengthContext lengthContext(this);
    676     return Length(height()->currentValue()->value(lengthContext), Fixed);
    677 }
    678 
    679 AffineTransform SVGSVGElement::viewBoxToViewTransform(float viewWidth, float viewHeight) const
    680 {
    681     if (!m_useCurrentView || !m_viewSpec)
    682         return SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), preserveAspectRatio()->currentValue(), viewWidth, viewHeight);
    683 
    684     AffineTransform ctm = SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), m_viewSpec->preserveAspectRatio()->currentValue(), viewWidth, viewHeight);
    685     RefPtr<SVGTransformList> transformList = m_viewSpec->transform();
    686     if (transformList->isEmpty())
    687         return ctm;
    688 
    689     AffineTransform transform;
    690     if (transformList->concatenate(transform))
    691         ctm *= transform;
    692 
    693     return ctm;
    694 }
    695 
    696 void SVGSVGElement::setupInitialView(const String& fragmentIdentifier, Element* anchorNode)
    697 {
    698     RenderObject* renderer = this->renderer();
    699     SVGViewSpec* view = m_viewSpec.get();
    700     if (view)
    701         view->reset();
    702 
    703     bool hadUseCurrentView = m_useCurrentView;
    704     m_useCurrentView = false;
    705 
    706     if (fragmentIdentifier.startsWith("xpointer(")) {
    707         // FIXME: XPointer references are ignored (https://bugs.webkit.org/show_bug.cgi?id=17491)
    708         if (renderer && hadUseCurrentView)
    709             RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
    710         return;
    711     }
    712 
    713     if (fragmentIdentifier.startsWith("svgView(")) {
    714         if (!view)
    715             view = currentView(); // Create the SVGViewSpec.
    716 
    717         if (view->parseViewSpec(fragmentIdentifier))
    718             m_useCurrentView = true;
    719         else
    720             view->reset();
    721 
    722         if (renderer && (hadUseCurrentView || m_useCurrentView))
    723             RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
    724         return;
    725     }
    726 
    727     // Spec: If the SVG fragment identifier addresses a view element within an SVG document (e.g., MyDrawing.svg#MyView
    728     // or MyDrawing.svg#xpointer(id('MyView'))) then the closest ancestor svg element is displayed in the viewport.
    729     // Any view specification attributes included on the given view element override the corresponding view specification
    730     // attributes on the closest ancestor svg element.
    731     if (isSVGViewElement(anchorNode)) {
    732         SVGViewElement& viewElement = toSVGViewElement(*anchorNode);
    733 
    734         if (SVGSVGElement* svg = viewElement.ownerSVGElement()) {
    735             svg->inheritViewAttributes(&viewElement);
    736 
    737             if (RenderObject* renderer = svg->renderer())
    738                 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
    739         }
    740     }
    741 
    742     // FIXME: We need to decide which <svg> to focus on, and zoom to it.
    743     // FIXME: We need to actually "highlight" the viewTarget(s).
    744 }
    745 
    746 void SVGSVGElement::inheritViewAttributes(SVGViewElement* viewElement)
    747 {
    748     SVGViewSpec* view = currentView();
    749     m_useCurrentView = true;
    750 
    751     if (viewElement->hasAttribute(SVGNames::viewBoxAttr))
    752         view->viewBox()->baseValue()->setValue(viewElement->viewBox()->currentValue()->value());
    753     else
    754         view->viewBox()->baseValue()->setValue(viewBox()->currentValue()->value());
    755 
    756     if (viewElement->hasAttribute(SVGNames::preserveAspectRatioAttr)) {
    757         view->preserveAspectRatio()->baseValue()->setAlign(viewElement->preserveAspectRatio()->currentValue()->align());
    758         view->preserveAspectRatio()->baseValue()->setMeetOrSlice(viewElement->preserveAspectRatio()->currentValue()->meetOrSlice());
    759     } else {
    760         view->preserveAspectRatio()->baseValue()->setAlign(preserveAspectRatio()->currentValue()->align());
    761         view->preserveAspectRatio()->baseValue()->setMeetOrSlice(preserveAspectRatio()->currentValue()->meetOrSlice());
    762     }
    763 
    764     if (viewElement->hasAttribute(SVGNames::zoomAndPanAttr))
    765         view->setZoomAndPan(viewElement->zoomAndPan());
    766     else
    767         view->setZoomAndPan(zoomAndPan());
    768 }
    769 
    770 void SVGSVGElement::finishParsingChildren()
    771 {
    772     SVGGraphicsElement::finishParsingChildren();
    773 
    774     // The outermost SVGSVGElement SVGLoad event is fired through Document::dispatchWindowLoadEvent.
    775     if (isOutermostSVGSVGElement())
    776         return;
    777 
    778     // finishParsingChildren() is called when the close tag is reached for an element (e.g. </svg>)
    779     // we send SVGLoad events here if we can, otherwise they'll be sent when any required loads finish
    780     sendSVGLoadEventIfPossible();
    781 }
    782 
    783 void SVGSVGElement::trace(Visitor* visitor)
    784 {
    785     visitor->trace(m_timeContainer);
    786     visitor->trace(m_viewSpec);
    787     SVGGraphicsElement::trace(visitor);
    788 }
    789 
    790 }
    791