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 * Copyright (C) 2014 Google, Inc.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
25 #include "core/svg/SVGSVGElement.h"
27 #include "bindings/core/v8/ScriptEventListener.h"
28 #include "core/HTMLNames.h"
29 #include "core/SVGNames.h"
30 #include "core/css/CSSHelper.h"
31 #include "core/dom/Document.h"
32 #include "core/dom/ElementTraversal.h"
33 #include "core/dom/StaticNodeList.h"
34 #include "core/editing/FrameSelection.h"
35 #include "core/events/EventListener.h"
36 #include "core/frame/LocalFrame.h"
37 #include "core/page/FrameTree.h"
38 #include "core/frame/FrameView.h"
39 #include "core/frame/UseCounter.h"
40 #include "core/layout/LayoutObject.h"
41 #include "core/layout/svg/LayoutSVGModelObject.h"
42 #include "core/layout/svg/LayoutSVGRoot.h"
43 #include "core/layout/svg/LayoutSVGViewportContainer.h"
44 #include "core/svg/SVGAngleTearOff.h"
45 #include "core/svg/SVGNumberTearOff.h"
46 #include "core/svg/SVGPreserveAspectRatio.h"
47 #include "core/svg/SVGRectTearOff.h"
48 #include "core/svg/SVGTransform.h"
49 #include "core/svg/SVGTransformList.h"
50 #include "core/svg/SVGTransformTearOff.h"
51 #include "core/svg/SVGViewElement.h"
52 #include "core/svg/SVGViewSpec.h"
53 #include "core/svg/animation/SMILTimeContainer.h"
54 #include "platform/FloatConversion.h"
55 #include "platform/LengthFunctions.h"
56 #include "platform/geometry/FloatRect.h"
57 #include "platform/transforms/AffineTransform.h"
58 #include "wtf/StdLibExtras.h"
62 inline SVGSVGElement::SVGSVGElement(Document
& doc
)
63 : SVGGraphicsElement(SVGNames::svgTag
, doc
)
64 , SVGFitToViewBox(this)
65 , m_x(SVGAnimatedLength::create(this, SVGNames::xAttr
, SVGLength::create(SVGLengthMode::Width
), AllowNegativeLengths
))
66 , m_y(SVGAnimatedLength::create(this, SVGNames::yAttr
, SVGLength::create(SVGLengthMode::Height
), AllowNegativeLengths
))
67 , m_width(SVGAnimatedLength::create(this, SVGNames::widthAttr
, SVGLength::create(SVGLengthMode::Width
), ForbidNegativeLengths
))
68 , m_height(SVGAnimatedLength::create(this, SVGNames::heightAttr
, SVGLength::create(SVGLengthMode::Height
), ForbidNegativeLengths
))
69 , m_useCurrentView(false)
70 , m_timeContainer(SMILTimeContainer::create(*this))
71 , m_translation(SVGPoint::create())
73 m_width
->setDefaultValueAsString("100%");
74 m_height
->setDefaultValueAsString("100%");
76 addToPropertyMap(m_x
);
77 addToPropertyMap(m_y
);
78 addToPropertyMap(m_width
);
79 addToPropertyMap(m_height
);
81 UseCounter::count(doc
, UseCounter::SVGSVGElement
);
84 DEFINE_NODE_FACTORY(SVGSVGElement
)
86 SVGSVGElement::~SVGSVGElement()
90 m_viewSpec
->detachContextElement();
92 // There are cases where removedFromDocument() is not called.
93 // see ContainerNode::removeAllChildren, called by its destructor.
94 // With Oilpan, either removedFrom is called or the document
95 // is dead as well and there is no reason to clear the extensions.
96 document().accessSVGExtensions().removeTimeContainer(this);
98 ASSERT(inDocument() || !accessDocumentSVGExtensions().isSVGRootWithRelativeLengthDescendents(this));
102 PassRefPtrWillBeRawPtr
<SVGRectTearOff
> SVGSVGElement::viewport() const
104 // FIXME: This method doesn't follow the spec and is basically untested. Parent documents are not considered here.
105 // As we have no test coverage for this, we're going to disable it completly for now.
106 return SVGRectTearOff::create(SVGRect::create(), 0, PropertyIsNotAnimVal
);
109 float SVGSVGElement::pixelUnitToMillimeterX() const
111 return 1 / cssPixelsPerMillimeter
;
114 float SVGSVGElement::pixelUnitToMillimeterY() const
116 return 1 / cssPixelsPerMillimeter
;
119 float SVGSVGElement::screenPixelToMillimeterX() const
121 return pixelUnitToMillimeterX();
124 float SVGSVGElement::screenPixelToMillimeterY() const
126 return pixelUnitToMillimeterY();
129 SVGViewSpec
* SVGSVGElement::currentView()
132 m_viewSpec
= SVGViewSpec::create(this);
133 return m_viewSpec
.get();
136 float SVGSVGElement::currentScale() const
138 if (!inDocument() || !isOutermostSVGSVGElement())
141 LocalFrame
* frame
= document().frame();
145 const FrameTree
& frameTree
= frame
->tree();
147 // The behaviour of currentScale() is undefined, when we're dealing with non-standalone SVG documents.
148 // If the svg is embedded, the scaling is handled by the host layoutObject, so when asking from inside
149 // the SVG document, a scale value of 1 seems reasonable, as it doesn't know anything about the parent scale.
150 return frameTree
.parent() ? 1 : frame
->pageZoomFactor();
153 void SVGSVGElement::setCurrentScale(float scale
)
155 ASSERT(std::isfinite(scale
));
156 if (!inDocument() || !isOutermostSVGSVGElement())
159 LocalFrame
* frame
= document().frame();
163 const FrameTree
& frameTree
= frame
->tree();
165 // The behaviour of setCurrentScale() is undefined, when we're dealing with non-standalone SVG documents.
166 // We choose the ignore this call, it's pretty useless to support calling setCurrentScale() from within
167 // an embedded SVG document, for the same reasons as in currentScale() - needs resolution by SVG WG.
168 if (frameTree
.parent())
171 frame
->setPageZoomFactor(scale
);
174 class SVGCurrentTranslateTearOff
: public SVGPointTearOff
{
176 static PassRefPtrWillBeRawPtr
<SVGCurrentTranslateTearOff
> create(SVGSVGElement
* contextElement
)
178 return adoptRefWillBeNoop(new SVGCurrentTranslateTearOff(contextElement
));
181 void commitChange() override
183 ASSERT(contextElement());
184 toSVGSVGElement(contextElement())->updateCurrentTranslate();
188 SVGCurrentTranslateTearOff(SVGSVGElement
* contextElement
)
189 : SVGPointTearOff(contextElement
->m_translation
, contextElement
, PropertyIsNotAnimVal
)
194 PassRefPtrWillBeRawPtr
<SVGPointTearOff
> SVGSVGElement::currentTranslateFromJavascript()
196 return SVGCurrentTranslateTearOff::create(this);
199 void SVGSVGElement::setCurrentTranslate(const FloatPoint
& point
)
201 m_translation
->setValue(point
);
202 updateCurrentTranslate();
205 void SVGSVGElement::updateCurrentTranslate()
207 if (LayoutObject
* object
= layoutObject())
208 object
->setNeedsLayoutAndFullPaintInvalidation(LayoutInvalidationReason::Unknown
);
211 bool SVGSVGElement::zoomAndPanEnabled() const
213 const SVGZoomAndPan
* currentViewSpec
= this;
214 if (m_useCurrentView
)
215 currentViewSpec
= m_viewSpec
.get();
216 return currentViewSpec
&& currentViewSpec
->zoomAndPan() == SVGZoomAndPanMagnify
;
219 void SVGSVGElement::parseAttribute(const QualifiedName
& name
, const AtomicString
& value
)
221 if (!nearestViewportElement()) {
222 bool setListener
= true;
224 // Only handle events if we're the outermost <svg> element
225 if (name
== HTMLNames::onunloadAttr
)
226 document().setWindowAttributeEventListener(EventTypeNames::unload
, createAttributeEventListener(document().frame(), name
, value
, eventParameterName()));
227 else if (name
== HTMLNames::onresizeAttr
)
228 document().setWindowAttributeEventListener(EventTypeNames::resize
, createAttributeEventListener(document().frame(), name
, value
, eventParameterName()));
229 else if (name
== HTMLNames::onscrollAttr
)
230 document().setWindowAttributeEventListener(EventTypeNames::scroll
, createAttributeEventListener(document().frame(), name
, value
, eventParameterName()));
231 else if (name
== SVGNames::onzoomAttr
)
232 document().setWindowAttributeEventListener(EventTypeNames::zoom
, createAttributeEventListener(document().frame(), name
, value
, eventParameterName()));
240 if (name
== HTMLNames::onabortAttr
) {
241 document().setWindowAttributeEventListener(EventTypeNames::abort
, createAttributeEventListener(document().frame(), name
, value
, eventParameterName()));
242 } else if (name
== HTMLNames::onerrorAttr
) {
243 document().setWindowAttributeEventListener(EventTypeNames::error
, createAttributeEventListener(document().frame(), name
, value
, eventParameterName()));
244 } else if (SVGZoomAndPan::parseAttribute(name
, value
)) {
246 SVGElement::parseAttribute(name
, value
);
250 bool SVGSVGElement::isPresentationAttribute(const QualifiedName
& name
) const
252 if (isOutermostSVGSVGElement() && (name
== SVGNames::widthAttr
|| name
== SVGNames::heightAttr
))
254 else if (name
== SVGNames::xAttr
|| name
== SVGNames::yAttr
)
257 return SVGGraphicsElement::isPresentationAttribute(name
);
260 bool SVGSVGElement::isPresentationAttributeWithSVGDOM(const QualifiedName
& attrName
) const
262 if (attrName
== SVGNames::xAttr
|| attrName
== SVGNames::yAttr
)
264 return SVGGraphicsElement::isPresentationAttributeWithSVGDOM(attrName
);
267 void SVGSVGElement::collectStyleForPresentationAttribute(const QualifiedName
& name
, const AtomicString
& value
, MutableStylePropertySet
* style
)
269 RefPtrWillBeRawPtr
<SVGAnimatedPropertyBase
> property
= propertyFromAttribute(name
);
270 if (property
== m_x
) {
271 addSVGLengthPropertyToPresentationAttributeStyle(style
, CSSPropertyX
, *m_x
->currentValue());
272 } else if (property
== m_y
) {
273 addSVGLengthPropertyToPresentationAttributeStyle(style
, CSSPropertyY
, *m_y
->currentValue());
274 } else if (isOutermostSVGSVGElement() && (property
== m_width
|| property
== m_height
)) {
275 if (property
== m_width
)
276 addSVGLengthPropertyToPresentationAttributeStyle(style
, CSSPropertyWidth
, *m_width
->currentValue());
277 else if (property
== m_height
)
278 addSVGLengthPropertyToPresentationAttributeStyle(style
, CSSPropertyHeight
, *m_height
->currentValue());
280 SVGGraphicsElement::collectStyleForPresentationAttribute(name
, value
, style
);
284 void SVGSVGElement::svgAttributeChanged(const QualifiedName
& attrName
)
286 bool updateRelativeLengthsOrViewBox
= false;
287 bool widthChanged
= attrName
== SVGNames::widthAttr
;
288 bool heightChanged
= attrName
== SVGNames::heightAttr
;
289 if (widthChanged
|| heightChanged
290 || attrName
== SVGNames::xAttr
291 || attrName
== SVGNames::yAttr
) {
292 updateRelativeLengthsOrViewBox
= true;
293 updateRelativeLengthsInformation();
294 invalidateRelativeLengthClients();
296 // At the SVG/HTML boundary (aka LayoutSVGRoot), the width and
297 // height attributes can affect the replaced size so we need
298 // to mark it for updating.
300 // FIXME: For width/height animated as XML attributes on SVG
301 // roots, there is an attribute synchronization missing. See
302 // http://crbug.com/364807
303 if (widthChanged
|| heightChanged
) {
304 LayoutObject
* layoutObject
= this->layoutObject();
305 if (layoutObject
&& layoutObject
->isSVGRoot()) {
306 invalidateSVGPresentationAttributeStyle();
307 setNeedsStyleRecalc(LocalStyleChange
, StyleChangeReasonForTracing::create(StyleChangeReason::SVGContainerSizeChange
));
310 invalidateSVGPresentationAttributeStyle();
311 setNeedsStyleRecalc(LocalStyleChange
,
312 StyleChangeReasonForTracing::fromAttribute(attrName
));
316 if (SVGFitToViewBox::isKnownAttribute(attrName
)) {
317 updateRelativeLengthsOrViewBox
= true;
318 invalidateRelativeLengthClients();
319 if (LayoutObject
* object
= layoutObject())
320 object
->setNeedsTransformUpdate();
323 if (updateRelativeLengthsOrViewBox
324 || SVGZoomAndPan::isKnownAttribute(attrName
)) {
325 SVGElement::InvalidationGuard
invalidationGuard(this);
327 markForLayoutAndParentResourceInvalidation(layoutObject());
331 SVGGraphicsElement::svgAttributeChanged(attrName
);
334 // FloatRect::intersects does not consider horizontal or vertical lines (because of isEmpty()).
335 static bool intersectsAllowingEmpty(const FloatRect
& r1
, const FloatRect
& r2
)
337 if (r1
.width() < 0 || r1
.height() < 0 || r2
.width() < 0 || r2
.height() < 0)
340 return r1
.x() < r2
.maxX() && r2
.x() < r1
.maxX()
341 && r1
.y() < r2
.maxY() && r2
.y() < r1
.maxY();
344 // One of the element types that can cause graphics to be drawn onto the target canvas.
345 // Specifically: circle, ellipse, image, line, path, polygon, polyline, rect, text and use.
346 static bool isIntersectionOrEnclosureTarget(LayoutObject
* layoutObject
)
348 return layoutObject
->isSVGShape()
349 || layoutObject
->isSVGText()
350 || layoutObject
->isSVGImage()
351 || isSVGUseElement(*layoutObject
->node());
354 bool SVGSVGElement::checkIntersectionOrEnclosure(const SVGElement
& element
, const FloatRect
& rect
,
355 CheckIntersectionOrEnclosure mode
) const
357 LayoutObject
* layoutObject
= element
.layoutObject();
358 ASSERT(!layoutObject
|| layoutObject
->style());
359 if (!layoutObject
|| layoutObject
->style()->pointerEvents() == PE_NONE
)
362 if (!isIntersectionOrEnclosureTarget(layoutObject
))
365 AffineTransform ctm
= toSVGGraphicsElement(element
).computeCTM(AncestorScope
, DisallowStyleUpdate
, this);
366 FloatRect mappedRepaintRect
= ctm
.mapRect(layoutObject
->paintInvalidationRectInLocalCoordinates());
370 case CheckIntersection
:
371 result
= intersectsAllowingEmpty(rect
, mappedRepaintRect
);
374 result
= rect
.contains(mappedRepaintRect
);
377 ASSERT_NOT_REACHED();
384 PassRefPtrWillBeRawPtr
<StaticNodeList
> SVGSVGElement::collectIntersectionOrEnclosureList(const FloatRect
& rect
,
385 SVGElement
* referenceElement
, CheckIntersectionOrEnclosure mode
) const
387 WillBeHeapVector
<RefPtrWillBeMember
<Node
>> nodes
;
389 const SVGElement
* root
= this;
390 if (referenceElement
) {
391 // Only the common subtree needs to be traversed.
392 if (contains(referenceElement
)) {
393 root
= referenceElement
;
394 } else if (!isDescendantOf(referenceElement
)) {
395 // No common subtree.
396 return StaticNodeList::adopt(nodes
);
400 for (SVGGraphicsElement
& element
: Traversal
<SVGGraphicsElement
>::descendantsOf(*root
)) {
401 if (checkIntersectionOrEnclosure(element
, rect
, mode
))
402 nodes
.append(&element
);
405 return StaticNodeList::adopt(nodes
);
408 PassRefPtrWillBeRawPtr
<StaticNodeList
> SVGSVGElement::getIntersectionList(PassRefPtrWillBeRawPtr
<SVGRectTearOff
> rect
, SVGElement
* referenceElement
) const
410 document().updateLayoutIgnorePendingStylesheets();
412 return collectIntersectionOrEnclosureList(rect
->target()->value(), referenceElement
, CheckIntersection
);
415 PassRefPtrWillBeRawPtr
<StaticNodeList
> SVGSVGElement::getEnclosureList(PassRefPtrWillBeRawPtr
<SVGRectTearOff
> rect
, SVGElement
* referenceElement
) const
417 document().updateLayoutIgnorePendingStylesheets();
419 return collectIntersectionOrEnclosureList(rect
->target()->value(), referenceElement
, CheckEnclosure
);
422 bool SVGSVGElement::checkIntersection(SVGElement
* element
, PassRefPtrWillBeRawPtr
<SVGRectTearOff
> rect
) const
425 document().updateLayoutIgnorePendingStylesheets();
427 return checkIntersectionOrEnclosure(*element
, rect
->target()->value(), CheckIntersection
);
430 bool SVGSVGElement::checkEnclosure(SVGElement
* element
, PassRefPtrWillBeRawPtr
<SVGRectTearOff
> rect
) const
433 document().updateLayoutIgnorePendingStylesheets();
435 return checkIntersectionOrEnclosure(*element
, rect
->target()->value(), CheckEnclosure
);
438 void SVGSVGElement::deselectAll()
440 if (LocalFrame
* frame
= document().frame())
441 frame
->selection().clear();
444 PassRefPtrWillBeRawPtr
<SVGNumberTearOff
> SVGSVGElement::createSVGNumber()
446 return SVGNumberTearOff::create(SVGNumber::create(0.0f
), 0, PropertyIsNotAnimVal
);
449 PassRefPtrWillBeRawPtr
<SVGLengthTearOff
> SVGSVGElement::createSVGLength()
451 return SVGLengthTearOff::create(SVGLength::create(), 0, PropertyIsNotAnimVal
);
454 PassRefPtrWillBeRawPtr
<SVGAngleTearOff
> SVGSVGElement::createSVGAngle()
456 return SVGAngleTearOff::create(SVGAngle::create(), 0, PropertyIsNotAnimVal
);
459 PassRefPtrWillBeRawPtr
<SVGPointTearOff
> SVGSVGElement::createSVGPoint()
461 return SVGPointTearOff::create(SVGPoint::create(), 0, PropertyIsNotAnimVal
);
464 PassRefPtrWillBeRawPtr
<SVGMatrixTearOff
> SVGSVGElement::createSVGMatrix()
466 return SVGMatrixTearOff::create(AffineTransform());
469 PassRefPtrWillBeRawPtr
<SVGRectTearOff
> SVGSVGElement::createSVGRect()
471 return SVGRectTearOff::create(SVGRect::create(), 0, PropertyIsNotAnimVal
);
474 PassRefPtrWillBeRawPtr
<SVGTransformTearOff
> SVGSVGElement::createSVGTransform()
476 return SVGTransformTearOff::create(SVGTransform::create(SVG_TRANSFORM_MATRIX
), 0, PropertyIsNotAnimVal
);
479 PassRefPtrWillBeRawPtr
<SVGTransformTearOff
> SVGSVGElement::createSVGTransformFromMatrix(PassRefPtrWillBeRawPtr
<SVGMatrixTearOff
> matrix
)
481 return SVGTransformTearOff::create(SVGTransform::create(matrix
->value()), 0, PropertyIsNotAnimVal
);
484 AffineTransform
SVGSVGElement::localCoordinateSpaceTransform(SVGElement::CTMScope mode
) const
486 AffineTransform viewBoxTransform
;
487 if (!hasEmptyViewBox()) {
488 FloatSize size
= currentViewportSize();
489 viewBoxTransform
= viewBoxToViewTransform(size
.width(), size
.height());
492 AffineTransform transform
;
493 if (!isOutermostSVGSVGElement()) {
494 SVGLengthContext
lengthContext(this);
495 transform
.translate(m_x
->currentValue()->value(lengthContext
), m_y
->currentValue()->value(lengthContext
));
496 } else if (mode
== SVGElement::ScreenScope
) {
497 if (LayoutObject
* layoutObject
= this->layoutObject()) {
499 float zoomFactor
= 1;
501 // At the SVG/HTML boundary (aka LayoutSVGRoot), we apply the localToBorderBoxTransform
502 // to map an element from SVG viewport coordinates to CSS box coordinates.
503 // LayoutSVGRoot's localToAbsolute method expects CSS box coordinates.
504 // We also need to adjust for the zoom level factored into CSS coordinates (bug #96361).
505 if (layoutObject
->isSVGRoot()) {
506 location
= toLayoutSVGRoot(layoutObject
)->localToBorderBoxTransform().mapPoint(location
);
507 zoomFactor
= 1 / layoutObject
->style()->effectiveZoom();
510 // Translate in our CSS parent coordinate space
511 // FIXME: This doesn't work correctly with CSS transforms.
512 location
= layoutObject
->localToAbsolute(location
, UseTransforms
);
513 location
.scale(zoomFactor
, zoomFactor
);
515 // Be careful here! localToBorderBoxTransform() included the x/y offset coming from the viewBoxToViewTransform(),
516 // so we have to subtract it here (original cause of bug #27183)
517 transform
.translate(location
.x() - viewBoxTransform
.e(), location
.y() - viewBoxTransform
.f());
519 // Respect scroll offset.
520 if (FrameView
* view
= document().view()) {
521 LayoutSize
scrollOffset(view
->scrollOffset());
522 scrollOffset
.scale(zoomFactor
);
523 transform
.translate(-scrollOffset
.width(), -scrollOffset
.height());
528 return transform
.multiply(viewBoxTransform
);
531 bool SVGSVGElement::layoutObjectIsNeeded(const ComputedStyle
& style
)
533 // FIXME: We should respect display: none on the documentElement svg element
534 // but many things in FrameView and SVGImage depend on the LayoutSVGRoot when
535 // they should instead depend on the LayoutView.
536 // https://bugs.webkit.org/show_bug.cgi?id=103493
537 if (document().documentElement() == this)
539 return Element::layoutObjectIsNeeded(style
);
542 LayoutObject
* SVGSVGElement::createLayoutObject(const ComputedStyle
&)
544 if (isOutermostSVGSVGElement())
545 return new LayoutSVGRoot(this);
547 return new LayoutSVGViewportContainer(this);
550 Node::InsertionNotificationRequest
SVGSVGElement::insertedInto(ContainerNode
* rootParent
)
552 if (rootParent
->inDocument()) {
553 UseCounter::count(document(), UseCounter::SVGSVGElementInDocument
);
554 if (rootParent
->document().isXMLDocument())
555 UseCounter::count(document(), UseCounter::SVGSVGElementInXMLDocument
);
557 if (RuntimeEnabledFeatures::smilEnabled()) {
558 document().accessSVGExtensions().addTimeContainer(this);
560 // Animations are started at the end of document parsing and after firing the load event,
561 // but if we miss that train (deferred programmatic element insertion for example) we need
562 // to initialize the time container here.
563 if (!document().parsing() && !document().processingLoadEvent() && document().loadEventFinished() && !timeContainer()->isStarted())
564 timeContainer()->begin();
567 return SVGGraphicsElement::insertedInto(rootParent
);
570 void SVGSVGElement::removedFrom(ContainerNode
* rootParent
)
572 if (rootParent
->inDocument()) {
573 SVGDocumentExtensions
& svgExtensions
= document().accessSVGExtensions();
574 svgExtensions
.removeTimeContainer(this);
575 svgExtensions
.removeSVGRootWithRelativeLengthDescendents(this);
578 SVGGraphicsElement::removedFrom(rootParent
);
581 void SVGSVGElement::pauseAnimations()
583 ASSERT(RuntimeEnabledFeatures::smilEnabled());
584 if (!m_timeContainer
->isPaused())
585 m_timeContainer
->pause();
588 void SVGSVGElement::unpauseAnimations()
590 ASSERT(RuntimeEnabledFeatures::smilEnabled());
591 if (m_timeContainer
->isPaused())
592 m_timeContainer
->resume();
595 bool SVGSVGElement::animationsPaused() const
597 ASSERT(RuntimeEnabledFeatures::smilEnabled());
598 return m_timeContainer
->isPaused();
601 float SVGSVGElement::getCurrentTime() const
603 ASSERT(RuntimeEnabledFeatures::smilEnabled());
604 return narrowPrecisionToFloat(m_timeContainer
->elapsed().value());
607 void SVGSVGElement::setCurrentTime(float seconds
)
609 ASSERT(RuntimeEnabledFeatures::smilEnabled());
610 ASSERT(std::isfinite(seconds
));
611 seconds
= max(seconds
, 0.0f
);
612 m_timeContainer
->setElapsed(seconds
);
615 bool SVGSVGElement::selfHasRelativeLengths() const
617 return m_x
->currentValue()->isRelative()
618 || m_y
->currentValue()->isRelative()
619 || m_width
->currentValue()->isRelative()
620 || m_height
->currentValue()->isRelative();
623 FloatRect
SVGSVGElement::currentViewBoxRect() const
625 if (m_useCurrentView
)
626 return m_viewSpec
? m_viewSpec
->viewBox()->currentValue()->value() : FloatRect();
628 FloatRect useViewBox
= viewBox()->currentValue()->value();
629 if (!useViewBox
.isEmpty())
631 if (!layoutObject() || !layoutObject()->isSVGRoot())
633 if (!toLayoutSVGRoot(layoutObject())->isEmbeddedThroughSVGImage())
636 // If no viewBox is specified but non-relative width/height values, then we
637 // should always synthesize a viewBox if we're embedded through a SVGImage.
638 return FloatRect(FloatPoint(), FloatSize(floatValueForLength(intrinsicWidth(), 0), floatValueForLength(intrinsicHeight(), 0)));
641 FloatSize
SVGSVGElement::currentViewportSize() const
646 if (layoutObject()->isSVGRoot()) {
647 LayoutRect contentBoxRect
= toLayoutSVGRoot(layoutObject())->contentBoxRect();
648 return FloatSize(contentBoxRect
.width() / layoutObject()->style()->effectiveZoom(), contentBoxRect
.height() / layoutObject()->style()->effectiveZoom());
651 FloatRect viewportRect
= toLayoutSVGViewportContainer(layoutObject())->viewport();
652 return FloatSize(viewportRect
.width(), viewportRect
.height());
655 bool SVGSVGElement::hasIntrinsicWidth() const
657 return width()->currentValue()->unitType() != LengthTypePercentage
;
660 bool SVGSVGElement::hasIntrinsicHeight() const
662 return height()->currentValue()->unitType() != LengthTypePercentage
;
665 Length
SVGSVGElement::intrinsicWidth() const
667 if (width()->currentValue()->unitType() == LengthTypePercentage
)
668 return Length(0, Fixed
);
670 SVGLengthContext
lengthContext(this);
671 return Length(width()->currentValue()->value(lengthContext
), Fixed
);
674 Length
SVGSVGElement::intrinsicHeight() const
676 if (height()->currentValue()->unitType() == LengthTypePercentage
)
677 return Length(0, Fixed
);
679 SVGLengthContext
lengthContext(this);
680 return Length(height()->currentValue()->value(lengthContext
), Fixed
);
683 AffineTransform
SVGSVGElement::viewBoxToViewTransform(float viewWidth
, float viewHeight
) const
685 if (!m_useCurrentView
|| !m_viewSpec
)
686 return SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), preserveAspectRatio()->currentValue(), viewWidth
, viewHeight
);
688 AffineTransform ctm
= SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), m_viewSpec
->preserveAspectRatio()->currentValue(), viewWidth
, viewHeight
);
689 RefPtrWillBeRawPtr
<SVGTransformList
> transformList
= m_viewSpec
->transform();
690 if (transformList
->isEmpty())
693 AffineTransform transform
;
694 if (transformList
->concatenate(transform
))
700 void SVGSVGElement::setupInitialView(const String
& fragmentIdentifier
, Element
* anchorNode
)
702 LayoutObject
* layoutObject
= this->layoutObject();
703 SVGViewSpec
* view
= m_viewSpec
.get();
707 bool hadUseCurrentView
= m_useCurrentView
;
708 m_useCurrentView
= false;
710 if (fragmentIdentifier
.startsWith("xpointer(")) {
711 // FIXME: XPointer references are ignored (https://bugs.webkit.org/show_bug.cgi?id=17491)
712 if (layoutObject
&& hadUseCurrentView
)
713 markForLayoutAndParentResourceInvalidation(layoutObject
);
717 if (fragmentIdentifier
.startsWith("svgView(")) {
719 view
= currentView(); // Create the SVGViewSpec.
721 view
->inheritViewAttributesFromElement(this);
723 if (view
->parseViewSpec(fragmentIdentifier
))
724 m_useCurrentView
= true;
728 if (layoutObject
&& (hadUseCurrentView
|| m_useCurrentView
))
729 markForLayoutAndParentResourceInvalidation(layoutObject
);
733 // Spec: If the SVG fragment identifier addresses a 'view' element within an SVG document (e.g., MyDrawing.svg#MyView
734 // or MyDrawing.svg#xpointer(id('MyView'))) then the closest ancestor 'svg' element is displayed in the viewport.
735 // Any view specification attributes included on the given 'view' element override the corresponding view specification
736 // attributes on the closest ancestor 'svg' element.
737 // TODO(ed): The spec text above is a bit unclear.
738 // Should the transform from outermost svg to nested svg be applied to "display"
739 // the inner svg in the viewport, then let the view element override the inner
740 // svg's view specification attributes. Should it fill/override the outer viewport?
741 if (isSVGViewElement(anchorNode
)) {
742 SVGViewElement
& viewElement
= toSVGViewElement(*anchorNode
);
744 if (SVGSVGElement
* svg
= viewElement
.ownerSVGElement()) {
745 svg
->inheritViewAttributes(&viewElement
);
747 if (LayoutObject
* layoutObject
= svg
->layoutObject())
748 markForLayoutAndParentResourceInvalidation(layoutObject
);
754 // If we previously had a view and didn't get a new one, we need to
756 if (layoutObject
&& hadUseCurrentView
)
757 markForLayoutAndParentResourceInvalidation(layoutObject
);
759 // FIXME: We need to decide which <svg> to focus on, and zoom to it.
760 // FIXME: We need to actually "highlight" the viewTarget(s).
763 void SVGSVGElement::inheritViewAttributes(SVGViewElement
* viewElement
)
765 SVGViewSpec
* view
= currentView();
766 m_useCurrentView
= true;
767 view
->inheritViewAttributesFromElement(this);
768 view
->inheritViewAttributesFromElement(viewElement
);
771 void SVGSVGElement::finishParsingChildren()
773 SVGGraphicsElement::finishParsingChildren();
775 // The outermost SVGSVGElement SVGLoad event is fired through Document::dispatchWindowLoadEvent.
776 if (isOutermostSVGSVGElement())
779 // finishParsingChildren() is called when the close tag is reached for an element (e.g. </svg>)
780 // we send SVGLoad events here if we can, otherwise they'll be sent when any required loads finish
781 sendSVGLoadEventIfPossible();
784 DEFINE_TRACE(SVGSVGElement
)
788 visitor
->trace(m_width
);
789 visitor
->trace(m_height
);
790 visitor
->trace(m_translation
);
791 visitor
->trace(m_timeContainer
);
792 visitor
->trace(m_viewSpec
);
793 SVGGraphicsElement::trace(visitor
);
794 SVGFitToViewBox::trace(visitor
);