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  *
      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 #include "core/svg/SVGSVGElement.h"
     25 
     26 #include "HTMLNames.h"
     27 #include "SVGNames.h"
     28 #include "bindings/v8/ScriptEventListener.h"
     29 #include "core/css/CSSHelper.h"
     30 #include "core/dom/Document.h"
     31 #include "core/dom/ElementTraversal.h"
     32 #include "core/dom/NodeTraversal.h"
     33 #include "core/dom/StaticNodeList.h"
     34 #include "core/editing/FrameSelection.h"
     35 #include "core/events/EventListener.h"
     36 #include "core/events/ThreadLocalEventNames.h"
     37 #include "core/frame/Frame.h"
     38 #include "core/page/FrameTree.h"
     39 #include "core/frame/FrameView.h"
     40 #include "core/frame/UseCounter.h"
     41 #include "core/rendering/RenderObject.h"
     42 #include "core/rendering/RenderPart.h"
     43 #include "core/rendering/svg/RenderSVGModelObject.h"
     44 #include "core/rendering/svg/RenderSVGResource.h"
     45 #include "core/rendering/svg/RenderSVGRoot.h"
     46 #include "core/rendering/svg/RenderSVGViewportContainer.h"
     47 #include "core/svg/SVGAngle.h"
     48 #include "core/svg/SVGElementInstance.h"
     49 #include "core/svg/SVGPreserveAspectRatio.h"
     50 #include "core/svg/SVGTransform.h"
     51 #include "core/svg/SVGTransformList.h"
     52 #include "core/svg/SVGViewElement.h"
     53 #include "core/svg/SVGViewSpec.h"
     54 #include "core/svg/animation/SMILTimeContainer.h"
     55 #include "platform/FloatConversion.h"
     56 #include "platform/LengthFunctions.h"
     57 #include "platform/geometry/FloatRect.h"
     58 #include "platform/transforms/AffineTransform.h"
     59 #include "wtf/StdLibExtras.h"
     60 
     61 namespace WebCore {
     62 
     63 // Animated property definitions
     64 DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::xAttr, X, x)
     65 DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::yAttr, Y, y)
     66 DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::widthAttr, Width, width)
     67 DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::heightAttr, Height, height)
     68 DEFINE_ANIMATED_BOOLEAN(SVGSVGElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
     69 DEFINE_ANIMATED_PRESERVEASPECTRATIO(SVGSVGElement, SVGNames::preserveAspectRatioAttr, PreserveAspectRatio, preserveAspectRatio)
     70 DEFINE_ANIMATED_RECT(SVGSVGElement, SVGNames::viewBoxAttr, ViewBox, viewBox)
     71 
     72 BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGSVGElement)
     73     REGISTER_LOCAL_ANIMATED_PROPERTY(x)
     74     REGISTER_LOCAL_ANIMATED_PROPERTY(y)
     75     REGISTER_LOCAL_ANIMATED_PROPERTY(width)
     76     REGISTER_LOCAL_ANIMATED_PROPERTY(height)
     77     REGISTER_LOCAL_ANIMATED_PROPERTY(externalResourcesRequired)
     78     REGISTER_LOCAL_ANIMATED_PROPERTY(viewBox)
     79     REGISTER_LOCAL_ANIMATED_PROPERTY(preserveAspectRatio)
     80     REGISTER_PARENT_ANIMATED_PROPERTIES(SVGGraphicsElement)
     81 END_REGISTER_ANIMATED_PROPERTIES
     82 
     83 inline SVGSVGElement::SVGSVGElement(Document& doc)
     84     : SVGGraphicsElement(SVGNames::svgTag, doc)
     85     , m_x(LengthModeWidth)
     86     , m_y(LengthModeHeight)
     87     , m_width(LengthModeWidth, "100%")
     88     , m_height(LengthModeHeight, "100%")
     89     , m_useCurrentView(false)
     90     , m_zoomAndPan(SVGZoomAndPanMagnify)
     91     , m_timeContainer(SMILTimeContainer::create(this))
     92     , m_weakFactory(this)
     93 {
     94     ScriptWrappable::init(this);
     95     registerAnimatedPropertiesForSVGSVGElement();
     96 
     97     UseCounter::count(doc, UseCounter::SVGSVGElement);
     98 }
     99 
    100 PassRefPtr<SVGSVGElement> SVGSVGElement::create(Document& document)
    101 {
    102     return adoptRef(new SVGSVGElement(document));
    103 }
    104 
    105 SVGSVGElement::~SVGSVGElement()
    106 {
    107     // There are cases where removedFromDocument() is not called.
    108     // see ContainerNode::removeAllChildren, called by its destructor.
    109     document().accessSVGExtensions()->removeTimeContainer(this);
    110 
    111     ASSERT(inDocument() || !accessDocumentSVGExtensions()->isSVGRootWithRelativeLengthDescendents(this));
    112 }
    113 
    114 const AtomicString& SVGSVGElement::contentScriptType() const
    115 {
    116     DEFINE_STATIC_LOCAL(const AtomicString, defaultValue, ("text/ecmascript", AtomicString::ConstructFromLiteral));
    117     const AtomicString& n = fastGetAttribute(SVGNames::contentScriptTypeAttr);
    118     return n.isNull() ? defaultValue : n;
    119 }
    120 
    121 void SVGSVGElement::setContentScriptType(const AtomicString& type)
    122 {
    123     setAttribute(SVGNames::contentScriptTypeAttr, type);
    124 }
    125 
    126 const AtomicString& SVGSVGElement::contentStyleType() const
    127 {
    128     DEFINE_STATIC_LOCAL(const AtomicString, defaultValue, ("text/css", AtomicString::ConstructFromLiteral));
    129     const AtomicString& n = fastGetAttribute(SVGNames::contentStyleTypeAttr);
    130     return n.isNull() ? defaultValue : n;
    131 }
    132 
    133 void SVGSVGElement::setContentStyleType(const AtomicString& type)
    134 {
    135     setAttribute(SVGNames::contentStyleTypeAttr, type);
    136 }
    137 
    138 SVGRect SVGSVGElement::viewport() const
    139 {
    140     // FIXME: This method doesn't follow the spec and is basically untested. Parent documents are not considered here.
    141     // As we have no test coverage for this, we're going to disable it completly for now.
    142     return SVGRect();
    143 }
    144 
    145 float SVGSVGElement::pixelUnitToMillimeterX() const
    146 {
    147     return 1 / cssPixelsPerMillimeter;
    148 }
    149 
    150 float SVGSVGElement::pixelUnitToMillimeterY() const
    151 {
    152     return 1 / cssPixelsPerMillimeter;
    153 }
    154 
    155 float SVGSVGElement::screenPixelToMillimeterX() const
    156 {
    157     return pixelUnitToMillimeterX();
    158 }
    159 
    160 float SVGSVGElement::screenPixelToMillimeterY() const
    161 {
    162     return pixelUnitToMillimeterY();
    163 }
    164 
    165 SVGViewSpec* SVGSVGElement::currentView()
    166 {
    167     if (!m_viewSpec)
    168         m_viewSpec = SVGViewSpec::create(m_weakFactory.createWeakPtr());
    169     return m_viewSpec.get();
    170 }
    171 
    172 float SVGSVGElement::currentScale() const
    173 {
    174     if (!inDocument() || !isOutermostSVGSVGElement())
    175         return 1;
    176 
    177     Frame* frame = document().frame();
    178     if (!frame)
    179         return 1;
    180 
    181     const FrameTree& frameTree = frame->tree();
    182 
    183     // The behaviour of currentScale() is undefined, when we're dealing with non-standalone SVG documents.
    184     // If the svg is embedded, the scaling is handled by the host renderer, so when asking from inside
    185     // the SVG document, a scale value of 1 seems reasonable, as it doesn't know anything about the parent scale.
    186     return frameTree.parent() ? 1 : frame->pageZoomFactor();
    187 }
    188 
    189 void SVGSVGElement::setCurrentScale(float scale)
    190 {
    191     if (!inDocument() || !isOutermostSVGSVGElement())
    192         return;
    193 
    194     Frame* frame = document().frame();
    195     if (!frame)
    196         return;
    197 
    198     const FrameTree& frameTree = frame->tree();
    199 
    200     // The behaviour of setCurrentScale() is undefined, when we're dealing with non-standalone SVG documents.
    201     // We choose the ignore this call, it's pretty useless to support calling setCurrentScale() from within
    202     // an embedded SVG document, for the same reasons as in currentScale() - needs resolution by SVG WG.
    203     if (frameTree.parent())
    204         return;
    205 
    206     frame->setPageZoomFactor(scale);
    207 }
    208 
    209 void SVGSVGElement::setCurrentTranslate(const FloatPoint& translation)
    210 {
    211     m_translation = translation;
    212     updateCurrentTranslate();
    213 }
    214 
    215 void SVGSVGElement::updateCurrentTranslate()
    216 {
    217     if (RenderObject* object = renderer())
    218         object->setNeedsLayout();
    219 
    220     if (parentNode() == document() && document().renderer())
    221         document().renderer()->repaint();
    222 }
    223 
    224 void SVGSVGElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
    225 {
    226     SVGParsingError parseError = NoError;
    227 
    228     if (!nearestViewportElement()) {
    229         bool setListener = true;
    230 
    231         // Only handle events if we're the outermost <svg> element
    232         if (name == HTMLNames::onunloadAttr)
    233             document().setWindowAttributeEventListener(EventTypeNames::unload, createAttributeEventListener(document().frame(), name, value));
    234         else if (name == HTMLNames::onresizeAttr)
    235             document().setWindowAttributeEventListener(EventTypeNames::resize, createAttributeEventListener(document().frame(), name, value));
    236         else if (name == HTMLNames::onscrollAttr)
    237             document().setWindowAttributeEventListener(EventTypeNames::scroll, createAttributeEventListener(document().frame(), name, value));
    238         else if (name == SVGNames::onzoomAttr)
    239             document().setWindowAttributeEventListener(EventTypeNames::zoom, createAttributeEventListener(document().frame(), name, value));
    240         else
    241             setListener = false;
    242 
    243         if (setListener)
    244             return;
    245     }
    246 
    247     if (name == HTMLNames::onabortAttr)
    248         document().setWindowAttributeEventListener(EventTypeNames::abort, createAttributeEventListener(document().frame(), name, value));
    249     else if (name == HTMLNames::onerrorAttr)
    250         document().setWindowAttributeEventListener(EventTypeNames::error, createAttributeEventListener(document().frame(), name, value));
    251     else if (name == SVGNames::xAttr)
    252         setXBaseValue(SVGLength::construct(LengthModeWidth, value, parseError));
    253     else if (name == SVGNames::yAttr)
    254         setYBaseValue(SVGLength::construct(LengthModeHeight, value, parseError));
    255     else if (name == SVGNames::widthAttr)
    256         setWidthBaseValue(SVGLength::construct(LengthModeWidth, value, parseError, ForbidNegativeLengths));
    257     else if (name == SVGNames::heightAttr)
    258         setHeightBaseValue(SVGLength::construct(LengthModeHeight, value, parseError, ForbidNegativeLengths));
    259     else if (SVGExternalResourcesRequired::parseAttribute(name, value)
    260                || SVGFitToViewBox::parseAttribute(this, name, value)
    261                || SVGZoomAndPan::parseAttribute(this, name, value)) {
    262     } else
    263         SVGGraphicsElement::parseAttribute(name, value);
    264 
    265     reportAttributeParsingError(parseError, name, value);
    266 }
    267 
    268 void SVGSVGElement::svgAttributeChanged(const QualifiedName& attrName)
    269 {
    270     bool updateRelativeLengthsOrViewBox = false;
    271     bool widthChanged = attrName == SVGNames::widthAttr;
    272     if (widthChanged
    273         || attrName == SVGNames::heightAttr
    274         || attrName == SVGNames::xAttr
    275         || attrName == SVGNames::yAttr) {
    276         updateRelativeLengthsOrViewBox = true;
    277         updateRelativeLengthsInformation();
    278         invalidateRelativeLengthClients();
    279 
    280         // At the SVG/HTML boundary (aka RenderSVGRoot), the width attribute can
    281         // affect the replaced size so we need to mark it for updating.
    282         if (widthChanged) {
    283             RenderObject* renderObject = renderer();
    284             if (renderObject && renderObject->isSVGRoot())
    285                 toRenderSVGRoot(renderObject)->setNeedsLayoutAndPrefWidthsRecalc();
    286         }
    287     }
    288 
    289     if (SVGFitToViewBox::isKnownAttribute(attrName)) {
    290         updateRelativeLengthsOrViewBox = true;
    291         if (RenderObject* object = renderer())
    292             object->setNeedsTransformUpdate();
    293     }
    294 
    295     SVGElementInstance::InvalidationGuard invalidationGuard(this);
    296 
    297     if (updateRelativeLengthsOrViewBox
    298         || SVGExternalResourcesRequired::isKnownAttribute(attrName)
    299         || SVGZoomAndPan::isKnownAttribute(attrName)) {
    300         if (renderer())
    301             RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer());
    302         return;
    303     }
    304 
    305     SVGGraphicsElement::svgAttributeChanged(attrName);
    306 }
    307 
    308 unsigned SVGSVGElement::suspendRedraw(unsigned /* maxWaitMilliseconds */)
    309 {
    310     // FIXME: Implement me (see bug 11275)
    311     return 0;
    312 }
    313 
    314 void SVGSVGElement::unsuspendRedraw(unsigned /* suspendHandleId */)
    315 {
    316     // FIXME: Implement me (see bug 11275)
    317 }
    318 
    319 void SVGSVGElement::unsuspendRedrawAll()
    320 {
    321     // FIXME: Implement me (see bug 11275)
    322 }
    323 
    324 void SVGSVGElement::forceRedraw()
    325 {
    326     // FIXME: Implement me (see bug 11275)
    327 }
    328 
    329 PassRefPtr<NodeList> SVGSVGElement::collectIntersectionOrEnclosureList(const SVGRect& rect, SVGElement* referenceElement, CollectIntersectionOrEnclosure collect) const
    330 {
    331     Vector<RefPtr<Node> > nodes;
    332     Element* element = ElementTraversal::next(*(referenceElement ? referenceElement : this));
    333     while (element) {
    334         if (element->isSVGElement()) {
    335             SVGElement* svgElement = toSVGElement(element);
    336             if (collect == CollectIntersectionList) {
    337                 if (checkIntersection(svgElement, rect))
    338                     nodes.append(element);
    339             } else {
    340                 if (checkEnclosure(svgElement, rect))
    341                     nodes.append(element);
    342             }
    343         }
    344 
    345         element = ElementTraversal::next(*element, referenceElement ? referenceElement : this);
    346     }
    347     return StaticNodeList::adopt(nodes);
    348 }
    349 
    350 PassRefPtr<NodeList> SVGSVGElement::getIntersectionList(const SVGRect& rect, SVGElement* referenceElement) const
    351 {
    352     return collectIntersectionOrEnclosureList(rect, referenceElement, CollectIntersectionList);
    353 }
    354 
    355 PassRefPtr<NodeList> SVGSVGElement::getEnclosureList(const SVGRect& rect, SVGElement* referenceElement) const
    356 {
    357     return collectIntersectionOrEnclosureList(rect, referenceElement, CollectEnclosureList);
    358 }
    359 
    360 bool SVGSVGElement::checkIntersection(SVGElement* element, const SVGRect& rect) const
    361 {
    362     if (!element)
    363         return false;
    364     return RenderSVGModelObject::checkIntersection(element->renderer(), rect);
    365 }
    366 
    367 bool SVGSVGElement::checkEnclosure(SVGElement* element, const SVGRect& rect) const
    368 {
    369     if (!element)
    370         return false;
    371     return RenderSVGModelObject::checkEnclosure(element->renderer(), rect);
    372 }
    373 
    374 void SVGSVGElement::deselectAll()
    375 {
    376     if (Frame* frame = document().frame())
    377         frame->selection().clear();
    378 }
    379 
    380 float SVGSVGElement::createSVGNumber()
    381 {
    382     return 0.0f;
    383 }
    384 
    385 SVGLength SVGSVGElement::createSVGLength()
    386 {
    387     return SVGLength();
    388 }
    389 
    390 SVGAngle SVGSVGElement::createSVGAngle()
    391 {
    392     return SVGAngle();
    393 }
    394 
    395 SVGPoint SVGSVGElement::createSVGPoint()
    396 {
    397     return SVGPoint();
    398 }
    399 
    400 SVGMatrix SVGSVGElement::createSVGMatrix()
    401 {
    402     return SVGMatrix();
    403 }
    404 
    405 SVGRect SVGSVGElement::createSVGRect()
    406 {
    407     return SVGRect();
    408 }
    409 
    410 SVGTransform SVGSVGElement::createSVGTransform()
    411 {
    412     return SVGTransform(SVGTransform::SVG_TRANSFORM_MATRIX);
    413 }
    414 
    415 SVGTransform SVGSVGElement::createSVGTransformFromMatrix(const SVGMatrix& matrix)
    416 {
    417     return SVGTransform(static_cast<const AffineTransform&>(matrix));
    418 }
    419 
    420 AffineTransform SVGSVGElement::localCoordinateSpaceTransform(SVGElement::CTMScope mode) const
    421 {
    422     AffineTransform viewBoxTransform;
    423     if (!hasEmptyViewBox()) {
    424         FloatSize size = currentViewportSize();
    425         viewBoxTransform = viewBoxToViewTransform(size.width(), size.height());
    426     }
    427 
    428     AffineTransform transform;
    429     if (!isOutermostSVGSVGElement()) {
    430         SVGLengthContext lengthContext(this);
    431         transform.translate(xCurrentValue().value(lengthContext), yCurrentValue().value(lengthContext));
    432     } else if (mode == SVGElement::ScreenScope) {
    433         if (RenderObject* renderer = this->renderer()) {
    434             FloatPoint location;
    435             float zoomFactor = 1;
    436 
    437             // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform
    438             // to map an element from SVG viewport coordinates to CSS box coordinates.
    439             // RenderSVGRoot's localToAbsolute method expects CSS box coordinates.
    440             // We also need to adjust for the zoom level factored into CSS coordinates (bug #96361).
    441             if (renderer->isSVGRoot()) {
    442                 location = toRenderSVGRoot(renderer)->localToBorderBoxTransform().mapPoint(location);
    443                 zoomFactor = 1 / renderer->style()->effectiveZoom();
    444             }
    445 
    446             // Translate in our CSS parent coordinate space
    447             // FIXME: This doesn't work correctly with CSS transforms.
    448             location = renderer->localToAbsolute(location, UseTransforms);
    449             location.scale(zoomFactor, zoomFactor);
    450 
    451             // Be careful here! localToBorderBoxTransform() included the x/y offset coming from the viewBoxToViewTransform(),
    452             // so we have to subtract it here (original cause of bug #27183)
    453             transform.translate(location.x() - viewBoxTransform.e(), location.y() - viewBoxTransform.f());
    454 
    455             // Respect scroll offset.
    456             if (FrameView* view = document().view()) {
    457                 LayoutSize scrollOffset = view->scrollOffset();
    458                 scrollOffset.scale(zoomFactor);
    459                 transform.translate(-scrollOffset.width(), -scrollOffset.height());
    460             }
    461         }
    462     }
    463 
    464     return transform.multiply(viewBoxTransform);
    465 }
    466 
    467 bool SVGSVGElement::rendererIsNeeded(const RenderStyle& style)
    468 {
    469     // FIXME: We should respect display: none on the documentElement svg element
    470     // but many things in FrameView and SVGImage depend on the RenderSVGRoot when
    471     // they should instead depend on the RenderView.
    472     // https://bugs.webkit.org/show_bug.cgi?id=103493
    473     if (document().documentElement() == this)
    474         return true;
    475     return Element::rendererIsNeeded(style);
    476 }
    477 
    478 RenderObject* SVGSVGElement::createRenderer(RenderStyle*)
    479 {
    480     if (isOutermostSVGSVGElement())
    481         return new RenderSVGRoot(this);
    482 
    483     return new RenderSVGViewportContainer(this);
    484 }
    485 
    486 Node::InsertionNotificationRequest SVGSVGElement::insertedInto(ContainerNode* rootParent)
    487 {
    488     if (rootParent->inDocument()) {
    489         document().accessSVGExtensions()->addTimeContainer(this);
    490 
    491         // Animations are started at the end of document parsing and after firing the load event,
    492         // but if we miss that train (deferred programmatic element insertion for example) we need
    493         // to initialize the time container here.
    494         if (!document().parsing() && !document().processingLoadEvent() && document().loadEventFinished() && !timeContainer()->isStarted())
    495             timeContainer()->begin();
    496     }
    497     return SVGGraphicsElement::insertedInto(rootParent);
    498 }
    499 
    500 void SVGSVGElement::removedFrom(ContainerNode* rootParent)
    501 {
    502     if (rootParent->inDocument()) {
    503         SVGDocumentExtensions* svgExtensions = document().accessSVGExtensions();
    504         svgExtensions->removeTimeContainer(this);
    505         svgExtensions->removeSVGRootWithRelativeLengthDescendents(this);
    506     }
    507 
    508     SVGGraphicsElement::removedFrom(rootParent);
    509 }
    510 
    511 void SVGSVGElement::pauseAnimations()
    512 {
    513     if (!m_timeContainer->isPaused())
    514         m_timeContainer->pause();
    515 }
    516 
    517 void SVGSVGElement::unpauseAnimations()
    518 {
    519     if (m_timeContainer->isPaused())
    520         m_timeContainer->resume();
    521 }
    522 
    523 bool SVGSVGElement::animationsPaused() const
    524 {
    525     return m_timeContainer->isPaused();
    526 }
    527 
    528 float SVGSVGElement::getCurrentTime() const
    529 {
    530     return narrowPrecisionToFloat(m_timeContainer->elapsed().value());
    531 }
    532 
    533 void SVGSVGElement::setCurrentTime(float seconds)
    534 {
    535     if (std::isnan(seconds))
    536         return;
    537     seconds = max(seconds, 0.0f);
    538     m_timeContainer->setElapsed(seconds);
    539 }
    540 
    541 bool SVGSVGElement::selfHasRelativeLengths() const
    542 {
    543     return xCurrentValue().isRelative()
    544         || yCurrentValue().isRelative()
    545         || widthCurrentValue().isRelative()
    546         || heightCurrentValue().isRelative()
    547         || hasAttribute(SVGNames::viewBoxAttr);
    548 }
    549 
    550 SVGRect SVGSVGElement::currentViewBoxRect() const
    551 {
    552     if (m_useCurrentView)
    553         return m_viewSpec ? m_viewSpec->viewBoxCurrentValue() : SVGRect();
    554 
    555     FloatRect useViewBox = viewBoxCurrentValue();
    556     if (!useViewBox.isEmpty())
    557         return useViewBox;
    558     if (!renderer() || !renderer()->isSVGRoot())
    559         return SVGRect();
    560     if (!toRenderSVGRoot(renderer())->isEmbeddedThroughSVGImage())
    561         return SVGRect();
    562 
    563     Length intrinsicWidth = this->intrinsicWidth();
    564     Length intrinsicHeight = this->intrinsicHeight();
    565     if (!intrinsicWidth.isFixed() || !intrinsicHeight.isFixed())
    566         return SVGRect();
    567 
    568     // If no viewBox is specified but non-relative width/height values, then we
    569     // should always synthesize a viewBox if we're embedded through a SVGImage.
    570     return SVGRect(FloatPoint(), FloatSize(floatValueForLength(intrinsicWidth, 0, 0), floatValueForLength(intrinsicHeight, 0, 0)));
    571 }
    572 
    573 FloatSize SVGSVGElement::currentViewportSize() const
    574 {
    575     Length intrinsicWidth = this->intrinsicWidth();
    576     Length intrinsicHeight = this->intrinsicHeight();
    577     if (intrinsicWidth.isFixed() && intrinsicHeight.isFixed())
    578         return FloatSize(floatValueForLength(intrinsicWidth, 0), floatValueForLength(intrinsicHeight, 0));
    579 
    580     if (!renderer())
    581         return FloatSize();
    582 
    583     if (renderer()->isSVGRoot()) {
    584         LayoutRect contentBoxRect = toRenderSVGRoot(renderer())->contentBoxRect();
    585         return FloatSize(contentBoxRect.width() / renderer()->style()->effectiveZoom(), contentBoxRect.height() / renderer()->style()->effectiveZoom());
    586     }
    587 
    588     FloatRect viewportRect = toRenderSVGViewportContainer(renderer())->viewport();
    589     return FloatSize(viewportRect.width(), viewportRect.height());
    590 }
    591 
    592 bool SVGSVGElement::widthAttributeEstablishesViewport() const
    593 {
    594     if (!renderer() || renderer()->isSVGViewportContainer())
    595         return true;
    596 
    597     // Spec: http://www.w3.org/TR/SVG/coords.html#ViewportSpace
    598     // The width attribute on the outermost svg element establishes the viewport's width, unless the following conditions are met:
    599     // - the SVG content is a separately stored resource that is embedded by reference (such as the object element in XHTML [XHTML]), or
    600     //   the SVG content is embedded inline within a containing document;
    601     // - and the referencing element or containing document is styled using CSS [CSS2] or XSL [XSL];
    602     // - and there are CSS-compatible positioning properties ([CSS2], section 9.3) specified on the referencing element (e.g., the object element)
    603     //   or on the containing document's outermost svg element that are sufficient to establish the width of the viewport. Under these conditions,
    604     //   the positioning properties establish the viewport's width.
    605     RenderSVGRoot* root = toRenderSVGRoot(renderer());
    606 
    607     // SVG embedded through object/embed/iframe.
    608     if (root->isEmbeddedThroughFrameContainingSVGDocument())
    609         return !root->hasReplacedLogicalWidth() && !document().frame()->ownerRenderer()->hasReplacedLogicalWidth();
    610 
    611     // SVG embedded via SVGImage (background-image/border-image/etc) / Inline SVG.
    612     if (root->isEmbeddedThroughSVGImage() || document().documentElement() != this)
    613         return !root->hasReplacedLogicalWidth();
    614 
    615     return true;
    616 }
    617 
    618 bool SVGSVGElement::heightAttributeEstablishesViewport() const
    619 {
    620     if (!renderer() || renderer()->isSVGViewportContainer())
    621         return true;
    622 
    623     // Spec: http://www.w3.org/TR/SVG/coords.html#IntrinsicSizing
    624     // Similarly, if there are positioning properties specified on the referencing element or on the outermost svg element
    625     // that are sufficient to establish the height of the viewport, then these positioning properties establish the viewport's
    626     // height; otherwise, the height attribute on the outermost svg element establishes the viewport's height.
    627     RenderSVGRoot* root = toRenderSVGRoot(renderer());
    628 
    629     // SVG embedded through object/embed/iframe.
    630     if (root->isEmbeddedThroughFrameContainingSVGDocument())
    631         return !root->hasReplacedLogicalHeight() && !document().frame()->ownerRenderer()->hasReplacedLogicalHeight();
    632 
    633     // SVG embedded via SVGImage (background-image/border-image/etc) / Inline SVG.
    634     if (root->isEmbeddedThroughSVGImage() || document().documentElement() != this)
    635         return !root->hasReplacedLogicalHeight();
    636 
    637     return true;
    638 }
    639 
    640 Length SVGSVGElement::intrinsicWidth(ConsiderCSSMode mode) const
    641 {
    642     if (widthAttributeEstablishesViewport() || mode == IgnoreCSSProperties) {
    643         if (widthCurrentValue().unitType() == LengthTypePercentage)
    644             return Length(widthCurrentValue().valueAsPercentage() * 100, Percent);
    645 
    646         SVGLengthContext lengthContext(this);
    647         return Length(widthCurrentValue().value(lengthContext), Fixed);
    648     }
    649 
    650     ASSERT(renderer());
    651     return renderer()->style()->width();
    652 }
    653 
    654 Length SVGSVGElement::intrinsicHeight(ConsiderCSSMode mode) const
    655 {
    656     if (heightAttributeEstablishesViewport() || mode == IgnoreCSSProperties) {
    657         if (heightCurrentValue().unitType() == LengthTypePercentage)
    658             return Length(heightCurrentValue().valueAsPercentage() * 100, Percent);
    659 
    660         SVGLengthContext lengthContext(this);
    661         return Length(heightCurrentValue().value(lengthContext), Fixed);
    662     }
    663 
    664     ASSERT(renderer());
    665     return renderer()->style()->height();
    666 }
    667 
    668 AffineTransform SVGSVGElement::viewBoxToViewTransform(float viewWidth, float viewHeight) const
    669 {
    670     if (!m_useCurrentView || !m_viewSpec)
    671         return SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), preserveAspectRatioCurrentValue(), viewWidth, viewHeight);
    672 
    673     AffineTransform ctm = SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), m_viewSpec->preserveAspectRatioCurrentValue(), viewWidth, viewHeight);
    674     const SVGTransformList& transformList = m_viewSpec->transformBaseValue();
    675     if (transformList.isEmpty())
    676         return ctm;
    677 
    678     AffineTransform transform;
    679     if (transformList.concatenate(transform))
    680         ctm *= transform;
    681 
    682     return ctm;
    683 }
    684 
    685 void SVGSVGElement::setupInitialView(const String& fragmentIdentifier, Element* anchorNode)
    686 {
    687     RenderObject* renderer = this->renderer();
    688     SVGViewSpec* view = m_viewSpec.get();
    689     if (view)
    690         view->reset();
    691 
    692     bool hadUseCurrentView = m_useCurrentView;
    693     m_useCurrentView = false;
    694 
    695     if (fragmentIdentifier.startsWith("xpointer(")) {
    696         // FIXME: XPointer references are ignored (https://bugs.webkit.org/show_bug.cgi?id=17491)
    697         if (renderer && hadUseCurrentView)
    698             RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
    699         return;
    700     }
    701 
    702     if (fragmentIdentifier.startsWith("svgView(")) {
    703         if (!view)
    704             view = currentView(); // Create the SVGViewSpec.
    705 
    706         if (view->parseViewSpec(fragmentIdentifier))
    707             m_useCurrentView = true;
    708         else
    709             view->reset();
    710 
    711         if (renderer && (hadUseCurrentView || m_useCurrentView))
    712             RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
    713         return;
    714     }
    715 
    716     // Spec: If the SVG fragment identifier addresses a view element within an SVG document (e.g., MyDrawing.svg#MyView
    717     // or MyDrawing.svg#xpointer(id('MyView'))) then the closest ancestor svg element is displayed in the viewport.
    718     // Any view specification attributes included on the given view element override the corresponding view specification
    719     // attributes on the closest ancestor svg element.
    720     if (anchorNode && anchorNode->hasTagName(SVGNames::viewTag)) {
    721         SVGViewElement* viewElement = toSVGViewElement(anchorNode);
    722         if (!viewElement)
    723             return;
    724 
    725         if (SVGSVGElement* svg = viewElement->ownerSVGElement()) {
    726             svg->inheritViewAttributes(viewElement);
    727 
    728             if (RenderObject* renderer = svg->renderer())
    729                 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
    730         }
    731     }
    732 
    733     // FIXME: We need to decide which <svg> to focus on, and zoom to it.
    734     // FIXME: We need to actually "highlight" the viewTarget(s).
    735 }
    736 
    737 void SVGSVGElement::inheritViewAttributes(SVGViewElement* viewElement)
    738 {
    739     SVGViewSpec* view = currentView();
    740     m_useCurrentView = true;
    741 
    742     if (viewElement->hasAttribute(SVGNames::viewBoxAttr))
    743         view->setViewBoxBaseValue(viewElement->viewBoxCurrentValue());
    744     else
    745         view->setViewBoxBaseValue(viewBoxCurrentValue());
    746 
    747     if (viewElement->hasAttribute(SVGNames::preserveAspectRatioAttr))
    748         view->setPreserveAspectRatioBaseValue(viewElement->preserveAspectRatioBaseValue());
    749     else
    750         view->setPreserveAspectRatioBaseValue(preserveAspectRatioBaseValue());
    751 
    752     if (viewElement->hasAttribute(SVGNames::zoomAndPanAttr))
    753         view->setZoomAndPanBaseValue(viewElement->zoomAndPan());
    754     else
    755         view->setZoomAndPanBaseValue(zoomAndPan());
    756 }
    757 
    758 // getElementById on SVGSVGElement is restricted to only the child subtree defined by the <svg> element.
    759 // See http://www.w3.org/TR/SVG11/struct.html#InterfaceSVGSVGElement
    760 Element* SVGSVGElement::getElementById(const AtomicString& id) const
    761 {
    762     Element* element = treeScope().getElementById(id);
    763     if (element && element->isDescendantOf(this))
    764         return element;
    765 
    766     // Fall back to traversing our subtree. Duplicate ids are allowed, the first found will
    767     // be returned.
    768     for (Node* node = firstChild(); node; node = NodeTraversal::next(*node, this)) {
    769         if (!node->isElementNode())
    770             continue;
    771 
    772         Element* element = toElement(node);
    773         if (element->getIdAttribute() == id)
    774             return element;
    775     }
    776     return 0;
    777 }
    778 
    779 }
    780