1/*
2 * Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2010 Rob Buis <buis@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#if ENABLE(SVG)
25#include "SVGSVGElement.h"
26
27#include "AffineTransform.h"
28#include "Attribute.h"
29#include "CSSHelper.h"
30#include "CSSPropertyNames.h"
31#include "Document.h"
32#include "EventListener.h"
33#include "EventNames.h"
34#include "FloatConversion.h"
35#include "FloatRect.h"
36#include "FrameView.h"
37#include "HTMLNames.h"
38#include "RenderSVGResource.h"
39#include "RenderSVGRoot.h"
40#include "RenderSVGViewportContainer.h"
41#include "SMILTimeContainer.h"
42#include "SVGAngle.h"
43#include "SVGNames.h"
44#include "SVGPreserveAspectRatio.h"
45#include "SVGTransform.h"
46#include "SVGTransformList.h"
47#include "SVGViewElement.h"
48#include "SVGViewSpec.h"
49#include "SVGZoomEvent.h"
50#include "ScriptEventListener.h"
51#include "SelectionController.h"
52#include <wtf/StdLibExtras.h>
53
54namespace WebCore {
55
56// Animated property definitions
57DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::xAttr, X, x)
58DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::yAttr, Y, y)
59DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::widthAttr, Width, width)
60DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::heightAttr, Height, height)
61DEFINE_ANIMATED_BOOLEAN(SVGSVGElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
62DEFINE_ANIMATED_PRESERVEASPECTRATIO(SVGSVGElement, SVGNames::preserveAspectRatioAttr, PreserveAspectRatio, preserveAspectRatio)
63DEFINE_ANIMATED_RECT(SVGSVGElement, SVGNames::viewBoxAttr, ViewBox, viewBox)
64
65inline SVGSVGElement::SVGSVGElement(const QualifiedName& tagName, Document* doc)
66    : SVGStyledLocatableElement(tagName, doc)
67    , m_x(LengthModeWidth)
68    , m_y(LengthModeHeight)
69    , m_width(LengthModeWidth, "100%")
70    , m_height(LengthModeHeight, "100%")
71    , m_useCurrentView(false)
72    , m_timeContainer(SMILTimeContainer::create(this))
73    , m_scale(1)
74    , m_viewSpec(0)
75    , m_containerSize(300, 150)
76    , m_hasSetContainerSize(false)
77{
78    doc->registerForDocumentActivationCallbacks(this);
79}
80
81PassRefPtr<SVGSVGElement> SVGSVGElement::create(const QualifiedName& tagName, Document* document)
82{
83    return adoptRef(new SVGSVGElement(tagName, document));
84}
85
86SVGSVGElement::~SVGSVGElement()
87{
88    document()->unregisterForDocumentActivationCallbacks(this);
89    // There are cases where removedFromDocument() is not called.
90    // see ContainerNode::removeAllChildren, called by its destructor.
91    document()->accessSVGExtensions()->removeTimeContainer(this);
92}
93
94void SVGSVGElement::willMoveToNewOwnerDocument()
95{
96    document()->unregisterForDocumentActivationCallbacks(this);
97    SVGStyledLocatableElement::willMoveToNewOwnerDocument();
98}
99
100void SVGSVGElement::didMoveToNewOwnerDocument()
101{
102    document()->registerForDocumentActivationCallbacks(this);
103    SVGStyledLocatableElement::didMoveToNewOwnerDocument();
104}
105
106const AtomicString& SVGSVGElement::contentScriptType() const
107{
108    DEFINE_STATIC_LOCAL(const AtomicString, defaultValue, ("text/ecmascript"));
109    const AtomicString& n = getAttribute(SVGNames::contentScriptTypeAttr);
110    return n.isNull() ? defaultValue : n;
111}
112
113void SVGSVGElement::setContentScriptType(const AtomicString& type)
114{
115    setAttribute(SVGNames::contentScriptTypeAttr, type);
116}
117
118const AtomicString& SVGSVGElement::contentStyleType() const
119{
120    DEFINE_STATIC_LOCAL(const AtomicString, defaultValue, ("text/css"));
121    const AtomicString& n = getAttribute(SVGNames::contentStyleTypeAttr);
122    return n.isNull() ? defaultValue : n;
123}
124
125void SVGSVGElement::setContentStyleType(const AtomicString& type)
126{
127    setAttribute(SVGNames::contentStyleTypeAttr, type);
128}
129
130FloatRect SVGSVGElement::viewport() const
131{
132    FloatRect viewRectangle;
133    if (!isOutermostSVG())
134        viewRectangle.setLocation(FloatPoint(x().value(this), y().value(this)));
135
136    viewRectangle.setSize(FloatSize(width().value(this), height().value(this)));
137    return viewBoxToViewTransform(viewRectangle.width(), viewRectangle.height()).mapRect(viewRectangle);
138}
139
140int SVGSVGElement::relativeWidthValue() const
141{
142    SVGLength w = width();
143    if (w.unitType() != LengthTypePercentage)
144        return 0;
145
146    return static_cast<int>(w.valueAsPercentage() * m_containerSize.width());
147}
148
149int SVGSVGElement::relativeHeightValue() const
150{
151    SVGLength h = height();
152    if (h.unitType() != LengthTypePercentage)
153        return 0;
154
155    return static_cast<int>(h.valueAsPercentage() * m_containerSize.height());
156}
157
158float SVGSVGElement::pixelUnitToMillimeterX() const
159{
160    // 2.54 / cssPixelsPerInch gives CM.
161    return (2.54f / cssPixelsPerInch) * 10.0f;
162}
163
164float SVGSVGElement::pixelUnitToMillimeterY() const
165{
166    // 2.54 / cssPixelsPerInch gives CM.
167    return (2.54f / cssPixelsPerInch) * 10.0f;
168}
169
170float SVGSVGElement::screenPixelToMillimeterX() const
171{
172    return pixelUnitToMillimeterX();
173}
174
175float SVGSVGElement::screenPixelToMillimeterY() const
176{
177    return pixelUnitToMillimeterY();
178}
179
180bool SVGSVGElement::useCurrentView() const
181{
182    return m_useCurrentView;
183}
184
185void SVGSVGElement::setUseCurrentView(bool currentView)
186{
187    m_useCurrentView = currentView;
188}
189
190SVGViewSpec* SVGSVGElement::currentView() const
191{
192    if (!m_viewSpec)
193        m_viewSpec = adoptPtr(new SVGViewSpec(const_cast<SVGSVGElement*>(this)));
194    return m_viewSpec.get();
195}
196
197float SVGSVGElement::currentScale() const
198{
199    // Only the page zoom factor is relevant for SVG
200    if (Frame* frame = document()->frame())
201        return frame->pageZoomFactor();
202    return m_scale;
203}
204
205void SVGSVGElement::setCurrentScale(float scale)
206{
207    if (Frame* frame = document()->frame()) {
208        // Calling setCurrentScale() on the outermost <svg> element in a standalone SVG document
209        // is allowed to change the page zoom factor, influencing the document size, scrollbars etc.
210        if (parentNode() == document())
211            frame->setPageZoomFactor(scale);
212        return;
213    }
214
215    m_scale = scale;
216    if (RenderObject* object = renderer())
217        RenderSVGResource::markForLayoutAndParentResourceInvalidation(object);
218}
219
220void SVGSVGElement::setCurrentTranslate(const FloatPoint& translation)
221{
222    m_translation = translation;
223    updateCurrentTranslate();
224}
225
226void SVGSVGElement::updateCurrentTranslate()
227{
228    if (RenderObject* object = renderer())
229        object->setNeedsLayout(true);
230
231    if (parentNode() == document() && document()->renderer())
232        document()->renderer()->repaint();
233}
234
235void SVGSVGElement::parseMappedAttribute(Attribute* attr)
236{
237    if (!nearestViewportElement()) {
238        bool setListener = true;
239
240        // Only handle events if we're the outermost <svg> element
241        if (attr->name() == HTMLNames::onunloadAttr)
242            document()->setWindowAttributeEventListener(eventNames().unloadEvent, createAttributeEventListener(document()->frame(), attr));
243        else if (attr->name() == HTMLNames::onresizeAttr)
244            document()->setWindowAttributeEventListener(eventNames().resizeEvent, createAttributeEventListener(document()->frame(), attr));
245        else if (attr->name() == HTMLNames::onscrollAttr)
246            document()->setWindowAttributeEventListener(eventNames().scrollEvent, createAttributeEventListener(document()->frame(), attr));
247        else if (attr->name() == SVGNames::onzoomAttr)
248            document()->setWindowAttributeEventListener(eventNames().zoomEvent, createAttributeEventListener(document()->frame(), attr));
249        else
250            setListener = false;
251
252        if (setListener)
253            return;
254    }
255
256    if (attr->name() == HTMLNames::onabortAttr)
257        document()->setWindowAttributeEventListener(eventNames().abortEvent, createAttributeEventListener(document()->frame(), attr));
258    else if (attr->name() == HTMLNames::onerrorAttr)
259        document()->setWindowAttributeEventListener(eventNames().errorEvent, createAttributeEventListener(document()->frame(), attr));
260    else if (attr->name() == SVGNames::xAttr)
261        setXBaseValue(SVGLength(LengthModeWidth, attr->value()));
262    else if (attr->name() == SVGNames::yAttr)
263        setYBaseValue(SVGLength(LengthModeHeight, attr->value()));
264    else if (attr->name() == SVGNames::widthAttr) {
265        setWidthBaseValue(SVGLength(LengthModeWidth, attr->value()));
266        addCSSProperty(attr, CSSPropertyWidth, attr->value());
267        if (widthBaseValue().value(this) < 0.0)
268            document()->accessSVGExtensions()->reportError("A negative value for svg attribute <width> is not allowed");
269    } else if (attr->name() == SVGNames::heightAttr) {
270        setHeightBaseValue(SVGLength(LengthModeHeight, attr->value()));
271        addCSSProperty(attr, CSSPropertyHeight, attr->value());
272        if (heightBaseValue().value(this) < 0.0)
273            document()->accessSVGExtensions()->reportError("A negative value for svg attribute <height> is not allowed");
274    } else {
275        if (SVGTests::parseMappedAttribute(attr))
276            return;
277        if (SVGLangSpace::parseMappedAttribute(attr))
278            return;
279        if (SVGExternalResourcesRequired::parseMappedAttribute(attr))
280            return;
281        if (SVGFitToViewBox::parseMappedAttribute(document(), attr))
282            return;
283        if (SVGZoomAndPan::parseMappedAttribute(attr))
284            return;
285
286        SVGStyledLocatableElement::parseMappedAttribute(attr);
287    }
288}
289
290// This hack will not handle the case where we're setting a width/height
291// on a root <svg> via svg.width.baseValue = when it has none.
292static void updateCSSForAttribute(SVGSVGElement* element, const QualifiedName& attrName, CSSPropertyID property, const SVGLength& value)
293{
294    Attribute* attribute = element->attributes(false)->getAttributeItem(attrName);
295    if (!attribute || !attribute->isMappedAttribute())
296        return;
297    element->addCSSProperty(attribute, property, value.valueAsString());
298}
299
300void SVGSVGElement::svgAttributeChanged(const QualifiedName& attrName)
301{
302    SVGStyledElement::svgAttributeChanged(attrName);
303
304    // FIXME: Ugly, ugly hack to around that parseMappedAttribute is not called
305    // when svg.width.baseValue = 100 is evaluated.
306    // Thus the CSS length value for width is not updated, and width() computeLogicalWidth()
307    // calculations on RenderSVGRoot will be wrong.
308    // https://bugs.webkit.org/show_bug.cgi?id=25387
309    bool updateRelativeLengths = false;
310    if (attrName == SVGNames::widthAttr) {
311        updateCSSForAttribute(this, attrName, CSSPropertyWidth, widthBaseValue());
312        updateRelativeLengths = true;
313    } else if (attrName == SVGNames::heightAttr) {
314        updateCSSForAttribute(this, attrName, CSSPropertyHeight, heightBaseValue());
315        updateRelativeLengths = true;
316    }
317
318    if (updateRelativeLengths
319        || attrName == SVGNames::xAttr
320        || attrName == SVGNames::yAttr
321        || SVGFitToViewBox::isKnownAttribute(attrName)) {
322        updateRelativeLengths = true;
323        updateRelativeLengthsInformation();
324    }
325
326    if (SVGTests::handleAttributeChange(this, attrName))
327        return;
328
329    if (!renderer())
330        return;
331
332    if (updateRelativeLengths
333        || SVGLangSpace::isKnownAttribute(attrName)
334        || SVGExternalResourcesRequired::isKnownAttribute(attrName)
335        || SVGZoomAndPan::isKnownAttribute(attrName)
336        || SVGStyledLocatableElement::isKnownAttribute(attrName))
337        RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer());
338}
339
340void SVGSVGElement::synchronizeProperty(const QualifiedName& attrName)
341{
342    SVGStyledElement::synchronizeProperty(attrName);
343
344    if (attrName == anyQName()) {
345        synchronizeX();
346        synchronizeY();
347        synchronizeWidth();
348        synchronizeHeight();
349        synchronizeExternalResourcesRequired();
350        synchronizeViewBox();
351        synchronizePreserveAspectRatio();
352        SVGTests::synchronizeProperties(this, attrName);
353        return;
354    }
355
356    if (attrName == SVGNames::xAttr)
357        synchronizeX();
358    else if (attrName == SVGNames::yAttr)
359        synchronizeY();
360    else if (attrName == SVGNames::widthAttr)
361        synchronizeWidth();
362    else if (attrName == SVGNames::heightAttr)
363        synchronizeHeight();
364    else if (SVGExternalResourcesRequired::isKnownAttribute(attrName))
365        synchronizeExternalResourcesRequired();
366    else if (attrName == SVGNames::viewBoxAttr)
367        synchronizeViewBox();
368    else if (attrName == SVGNames::preserveAspectRatioAttr)
369        synchronizePreserveAspectRatio();
370    else if (SVGTests::isKnownAttribute(attrName))
371        SVGTests::synchronizeProperties(this, attrName);
372}
373
374AttributeToPropertyTypeMap& SVGSVGElement::attributeToPropertyTypeMap()
375{
376    DEFINE_STATIC_LOCAL(AttributeToPropertyTypeMap, s_attributeToPropertyTypeMap, ());
377    return s_attributeToPropertyTypeMap;
378}
379
380void SVGSVGElement::fillAttributeToPropertyTypeMap()
381{
382    AttributeToPropertyTypeMap& attributeToPropertyTypeMap = this->attributeToPropertyTypeMap();
383    attributeToPropertyTypeMap.set(SVGNames::xAttr, AnimatedLength);
384    attributeToPropertyTypeMap.set(SVGNames::yAttr, AnimatedLength);
385    attributeToPropertyTypeMap.set(SVGNames::widthAttr, AnimatedLength);
386    attributeToPropertyTypeMap.set(SVGNames::heightAttr, AnimatedLength);
387    attributeToPropertyTypeMap.set(SVGNames::viewBoxAttr, AnimatedRect);
388    attributeToPropertyTypeMap.set(SVGNames::preserveAspectRatioAttr, AnimatedPreserveAspectRatio);
389}
390
391unsigned SVGSVGElement::suspendRedraw(unsigned /* maxWaitMilliseconds */)
392{
393    // FIXME: Implement me (see bug 11275)
394    return 0;
395}
396
397void SVGSVGElement::unsuspendRedraw(unsigned /* suspendHandleId */)
398{
399    // FIXME: Implement me (see bug 11275)
400}
401
402void SVGSVGElement::unsuspendRedrawAll()
403{
404    // FIXME: Implement me (see bug 11275)
405}
406
407void SVGSVGElement::forceRedraw()
408{
409    // FIXME: Implement me (see bug 11275)
410}
411
412NodeList* SVGSVGElement::getIntersectionList(const FloatRect&, SVGElement*)
413{
414    // FIXME: Implement me (see bug 11274)
415    return 0;
416}
417
418NodeList* SVGSVGElement::getEnclosureList(const FloatRect&, SVGElement*)
419{
420    // FIXME: Implement me (see bug 11274)
421    return 0;
422}
423
424bool SVGSVGElement::checkIntersection(SVGElement*, const FloatRect& rect)
425{
426    // TODO : take into account pointer-events?
427    // FIXME: Why is element ignored??
428    // FIXME: Implement me (see bug 11274)
429    return rect.intersects(getBBox());
430}
431
432bool SVGSVGElement::checkEnclosure(SVGElement*, const FloatRect& rect)
433{
434    // TODO : take into account pointer-events?
435    // FIXME: Why is element ignored??
436    // FIXME: Implement me (see bug 11274)
437    return rect.contains(getBBox());
438}
439
440void SVGSVGElement::deselectAll()
441{
442    if (Frame* frame = document()->frame())
443        frame->selection()->clear();
444}
445
446float SVGSVGElement::createSVGNumber()
447{
448    return 0.0f;
449}
450
451SVGLength SVGSVGElement::createSVGLength()
452{
453    return SVGLength();
454}
455
456SVGAngle SVGSVGElement::createSVGAngle()
457{
458    return SVGAngle();
459}
460
461FloatPoint SVGSVGElement::createSVGPoint()
462{
463    return FloatPoint();
464}
465
466SVGMatrix SVGSVGElement::createSVGMatrix()
467{
468    return SVGMatrix();
469}
470
471FloatRect SVGSVGElement::createSVGRect()
472{
473    return FloatRect();
474}
475
476SVGTransform SVGSVGElement::createSVGTransform()
477{
478    return SVGTransform(SVGTransform::SVG_TRANSFORM_MATRIX);
479}
480
481SVGTransform SVGSVGElement::createSVGTransformFromMatrix(const SVGMatrix& matrix)
482{
483    return SVGTransform(static_cast<const AffineTransform&>(matrix));
484}
485
486AffineTransform SVGSVGElement::localCoordinateSpaceTransform(SVGLocatable::CTMScope mode) const
487{
488    AffineTransform viewBoxTransform;
489    if (attributes()->getAttributeItem(SVGNames::viewBoxAttr))
490        viewBoxTransform = viewBoxToViewTransform(width().value(this), height().value(this));
491
492    AffineTransform transform;
493    if (!isOutermostSVG())
494        transform.translate(x().value(this), y().value(this));
495    else if (mode == SVGLocatable::ScreenScope) {
496        if (RenderObject* renderer = this->renderer()) {
497            // Translate in our CSS parent coordinate space
498            // FIXME: This doesn't work correctly with CSS transforms.
499            FloatPoint location = renderer->localToAbsolute(FloatPoint(), false, true);
500
501            // Be careful here! localToAbsolute() includes the x/y offset coming from the viewBoxToViewTransform(), because
502            // RenderSVGRoot::localToBorderBoxTransform() (called through mapLocalToContainer(), called from localToAbsolute())
503            // also takes the viewBoxToViewTransform() into account, so we have to subtract it here (original cause of bug #27183)
504            transform.translate(location.x() - viewBoxTransform.e(), location.y() - viewBoxTransform.f());
505
506            // Respect scroll offset.
507            if (FrameView* view = document()->view()) {
508                IntSize scrollOffset = view->scrollOffset();
509                transform.translate(-scrollOffset.width(), -scrollOffset.height());
510            }
511        }
512    }
513
514    return transform.multiply(viewBoxTransform);
515}
516
517RenderObject* SVGSVGElement::createRenderer(RenderArena* arena, RenderStyle*)
518{
519    if (isOutermostSVG())
520        return new (arena) RenderSVGRoot(this);
521
522    return new (arena) RenderSVGViewportContainer(this);
523}
524
525void SVGSVGElement::insertedIntoDocument()
526{
527    document()->accessSVGExtensions()->addTimeContainer(this);
528    SVGStyledLocatableElement::insertedIntoDocument();
529}
530
531void SVGSVGElement::removedFromDocument()
532{
533    document()->accessSVGExtensions()->removeTimeContainer(this);
534    SVGStyledLocatableElement::removedFromDocument();
535}
536
537void SVGSVGElement::pauseAnimations()
538{
539    if (!m_timeContainer->isPaused())
540        m_timeContainer->pause();
541}
542
543void SVGSVGElement::unpauseAnimations()
544{
545    if (m_timeContainer->isPaused())
546        m_timeContainer->resume();
547}
548
549bool SVGSVGElement::animationsPaused() const
550{
551    return m_timeContainer->isPaused();
552}
553
554float SVGSVGElement::getCurrentTime() const
555{
556    return narrowPrecisionToFloat(m_timeContainer->elapsed().value());
557}
558
559void SVGSVGElement::setCurrentTime(float /* seconds */)
560{
561    // FIXME: Implement me, bug 12073
562}
563
564bool SVGSVGElement::selfHasRelativeLengths() const
565{
566    return x().isRelative()
567        || y().isRelative()
568        || width().isRelative()
569        || height().isRelative()
570        || hasAttribute(SVGNames::viewBoxAttr);
571}
572
573bool SVGSVGElement::isOutermostSVG() const
574{
575    // Element may not be in the document, pretend we're outermost for viewport(), getCTM(), etc.
576    if (!parentNode())
577        return true;
578
579#if ENABLE(SVG_FOREIGN_OBJECT)
580    // We act like an outermost SVG element, if we're a direct child of a <foreignObject> element.
581    if (parentNode()->hasTagName(SVGNames::foreignObjectTag))
582        return true;
583#endif
584
585    // This is true whenever this is the outermost SVG, even if there are HTML elements outside it
586    return !parentNode()->isSVGElement();
587}
588
589AffineTransform SVGSVGElement::viewBoxToViewTransform(float viewWidth, float viewHeight) const
590{
591    FloatRect viewBoxRect;
592    if (useCurrentView()) {
593        if (currentView()) // what if we should use it but it is not set?
594            viewBoxRect = currentView()->viewBox();
595    } else
596        viewBoxRect = viewBox();
597
598    AffineTransform ctm = SVGFitToViewBox::viewBoxToViewTransform(viewBoxRect, preserveAspectRatio(), viewWidth, viewHeight);
599
600    if (useCurrentView() && currentView()) {
601        AffineTransform transform;
602        if (currentView()->transform().concatenate(transform))
603            ctm *= transform;
604    }
605
606    return ctm;
607}
608
609void SVGSVGElement::inheritViewAttributes(SVGViewElement* viewElement)
610{
611    setUseCurrentView(true);
612    if (viewElement->hasAttribute(SVGNames::viewBoxAttr))
613        currentView()->setViewBoxBaseValue(viewElement->viewBox());
614    else
615        currentView()->setViewBoxBaseValue(viewBox());
616
617    SVGPreserveAspectRatio aspectRatio;
618    if (viewElement->hasAttribute(SVGNames::preserveAspectRatioAttr))
619        aspectRatio = viewElement->preserveAspectRatioBaseValue();
620    else
621        aspectRatio = preserveAspectRatioBaseValue();
622    currentView()->setPreserveAspectRatioBaseValue(aspectRatio);
623
624    if (viewElement->hasAttribute(SVGNames::zoomAndPanAttr))
625        currentView()->setZoomAndPan(viewElement->zoomAndPan());
626
627    if (RenderObject* object = renderer())
628        RenderSVGResource::markForLayoutAndParentResourceInvalidation(object);
629}
630
631void SVGSVGElement::documentWillBecomeInactive()
632{
633    pauseAnimations();
634}
635
636void SVGSVGElement::documentDidBecomeActive()
637{
638    unpauseAnimations();
639}
640
641// getElementById on SVGSVGElement is restricted to only the child subtree defined by the <svg> element.
642// See http://www.w3.org/TR/SVG11/struct.html#InterfaceSVGSVGElement
643Element* SVGSVGElement::getElementById(const AtomicString& id) const
644{
645    Element* element = document()->getElementById(id);
646    if (element && element->isDescendantOf(this))
647        return element;
648
649    // Fall back to traversing our subtree. Duplicate ids are allowed, the first found will
650    // be returned.
651    for (Node* node = traverseNextNode(this); node; node = node->traverseNextNode(this)) {
652        if (!node->isElementNode())
653            continue;
654
655        Element* element = static_cast<Element*>(node);
656        if (element->hasID() && element->getIdAttribute() == id)
657            return element;
658    }
659    return 0;
660}
661
662}
663
664#endif // ENABLE(SVG)
665