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