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