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