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#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
61namespace WebCore {
62
63// Animated property definitions
64DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::xAttr, X, x)
65DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::yAttr, Y, y)
66DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::widthAttr, Width, width)
67DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::heightAttr, Height, height)
68DEFINE_ANIMATED_BOOLEAN(SVGSVGElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
69DEFINE_ANIMATED_PRESERVEASPECTRATIO(SVGSVGElement, SVGNames::preserveAspectRatioAttr, PreserveAspectRatio, preserveAspectRatio)
70DEFINE_ANIMATED_RECT(SVGSVGElement, SVGNames::viewBoxAttr, ViewBox, viewBox)
71
72BEGIN_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)
81END_REGISTER_ANIMATED_PROPERTIES
82
83inline 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
100PassRefPtr<SVGSVGElement> SVGSVGElement::create(Document& document)
101{
102    return adoptRef(new SVGSVGElement(document));
103}
104
105SVGSVGElement::~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
114const 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
121void SVGSVGElement::setContentScriptType(const AtomicString& type)
122{
123    setAttribute(SVGNames::contentScriptTypeAttr, type);
124}
125
126const 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
133void SVGSVGElement::setContentStyleType(const AtomicString& type)
134{
135    setAttribute(SVGNames::contentStyleTypeAttr, type);
136}
137
138SVGRect 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
145float SVGSVGElement::pixelUnitToMillimeterX() const
146{
147    return 1 / cssPixelsPerMillimeter;
148}
149
150float SVGSVGElement::pixelUnitToMillimeterY() const
151{
152    return 1 / cssPixelsPerMillimeter;
153}
154
155float SVGSVGElement::screenPixelToMillimeterX() const
156{
157    return pixelUnitToMillimeterX();
158}
159
160float SVGSVGElement::screenPixelToMillimeterY() const
161{
162    return pixelUnitToMillimeterY();
163}
164
165SVGViewSpec* SVGSVGElement::currentView()
166{
167    if (!m_viewSpec)
168        m_viewSpec = SVGViewSpec::create(m_weakFactory.createWeakPtr());
169    return m_viewSpec.get();
170}
171
172float 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
189void 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
209void SVGSVGElement::setCurrentTranslate(const FloatPoint& translation)
210{
211    m_translation = translation;
212    updateCurrentTranslate();
213}
214
215void SVGSVGElement::updateCurrentTranslate()
216{
217    if (RenderObject* object = renderer())
218        object->setNeedsLayout();
219
220    if (parentNode() == document() && document().renderer())
221        document().renderer()->repaint();
222}
223
224void 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
268void 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
308unsigned SVGSVGElement::suspendRedraw(unsigned /* maxWaitMilliseconds */)
309{
310    // FIXME: Implement me (see bug 11275)
311    return 0;
312}
313
314void SVGSVGElement::unsuspendRedraw(unsigned /* suspendHandleId */)
315{
316    // FIXME: Implement me (see bug 11275)
317}
318
319void SVGSVGElement::unsuspendRedrawAll()
320{
321    // FIXME: Implement me (see bug 11275)
322}
323
324void SVGSVGElement::forceRedraw()
325{
326    // FIXME: Implement me (see bug 11275)
327}
328
329PassRefPtr<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
350PassRefPtr<NodeList> SVGSVGElement::getIntersectionList(const SVGRect& rect, SVGElement* referenceElement) const
351{
352    return collectIntersectionOrEnclosureList(rect, referenceElement, CollectIntersectionList);
353}
354
355PassRefPtr<NodeList> SVGSVGElement::getEnclosureList(const SVGRect& rect, SVGElement* referenceElement) const
356{
357    return collectIntersectionOrEnclosureList(rect, referenceElement, CollectEnclosureList);
358}
359
360bool SVGSVGElement::checkIntersection(SVGElement* element, const SVGRect& rect) const
361{
362    if (!element)
363        return false;
364    return RenderSVGModelObject::checkIntersection(element->renderer(), rect);
365}
366
367bool SVGSVGElement::checkEnclosure(SVGElement* element, const SVGRect& rect) const
368{
369    if (!element)
370        return false;
371    return RenderSVGModelObject::checkEnclosure(element->renderer(), rect);
372}
373
374void SVGSVGElement::deselectAll()
375{
376    if (Frame* frame = document().frame())
377        frame->selection().clear();
378}
379
380float SVGSVGElement::createSVGNumber()
381{
382    return 0.0f;
383}
384
385SVGLength SVGSVGElement::createSVGLength()
386{
387    return SVGLength();
388}
389
390SVGAngle SVGSVGElement::createSVGAngle()
391{
392    return SVGAngle();
393}
394
395SVGPoint SVGSVGElement::createSVGPoint()
396{
397    return SVGPoint();
398}
399
400SVGMatrix SVGSVGElement::createSVGMatrix()
401{
402    return SVGMatrix();
403}
404
405SVGRect SVGSVGElement::createSVGRect()
406{
407    return SVGRect();
408}
409
410SVGTransform SVGSVGElement::createSVGTransform()
411{
412    return SVGTransform(SVGTransform::SVG_TRANSFORM_MATRIX);
413}
414
415SVGTransform SVGSVGElement::createSVGTransformFromMatrix(const SVGMatrix& matrix)
416{
417    return SVGTransform(static_cast<const AffineTransform&>(matrix));
418}
419
420AffineTransform 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
467bool 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
478RenderObject* SVGSVGElement::createRenderer(RenderStyle*)
479{
480    if (isOutermostSVGSVGElement())
481        return new RenderSVGRoot(this);
482
483    return new RenderSVGViewportContainer(this);
484}
485
486Node::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
500void 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
511void SVGSVGElement::pauseAnimations()
512{
513    if (!m_timeContainer->isPaused())
514        m_timeContainer->pause();
515}
516
517void SVGSVGElement::unpauseAnimations()
518{
519    if (m_timeContainer->isPaused())
520        m_timeContainer->resume();
521}
522
523bool SVGSVGElement::animationsPaused() const
524{
525    return m_timeContainer->isPaused();
526}
527
528float SVGSVGElement::getCurrentTime() const
529{
530    return narrowPrecisionToFloat(m_timeContainer->elapsed().value());
531}
532
533void 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
541bool SVGSVGElement::selfHasRelativeLengths() const
542{
543    return xCurrentValue().isRelative()
544        || yCurrentValue().isRelative()
545        || widthCurrentValue().isRelative()
546        || heightCurrentValue().isRelative()
547        || hasAttribute(SVGNames::viewBoxAttr);
548}
549
550SVGRect 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
573FloatSize 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
592bool 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
618bool 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
640Length 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
654Length 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
668AffineTransform 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
685void 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
737void 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
760Element* 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