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.
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.
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.
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.
24 #include "core/svg/SVGSVGElement.h"
26 #include "HTMLNames.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/SVGNumberTearOff.h"
50 #include "core/svg/SVGPreserveAspectRatio.h"
51 #include "core/svg/SVGRectTearOff.h"
52 #include "core/svg/SVGTransform.h"
53 #include "core/svg/SVGTransformList.h"
54 #include "core/svg/SVGViewElement.h"
55 #include "core/svg/SVGViewSpec.h"
56 #include "core/svg/animation/SMILTimeContainer.h"
57 #include "platform/FloatConversion.h"
58 #include "platform/LengthFunctions.h"
59 #include "platform/geometry/FloatRect.h"
60 #include "platform/transforms/AffineTransform.h"
61 #include "wtf/StdLibExtras.h"
65 // Animated property definitions
67 BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGSVGElement)
68 REGISTER_PARENT_ANIMATED_PROPERTIES(SVGGraphicsElement)
69 END_REGISTER_ANIMATED_PROPERTIES
71 inline SVGSVGElement::SVGSVGElement(Document& doc)
72 : SVGGraphicsElement(SVGNames::svgTag, doc)
73 , m_x(SVGAnimatedLength::create(this, SVGNames::xAttr, SVGLength::create(LengthModeWidth)))
74 , m_y(SVGAnimatedLength::create(this, SVGNames::yAttr, SVGLength::create(LengthModeHeight)))
75 , m_width(SVGAnimatedLength::create(this, SVGNames::widthAttr, SVGLength::create(LengthModeWidth)))
76 , m_height(SVGAnimatedLength::create(this, SVGNames::heightAttr, SVGLength::create(LengthModeHeight)))
77 , m_viewBox(SVGAnimatedRect::create(this, SVGNames::viewBoxAttr))
78 , m_preserveAspectRatio(SVGAnimatedPreserveAspectRatio::create(this, SVGNames::preserveAspectRatioAttr, SVGPreserveAspectRatio::create()))
79 , m_useCurrentView(false)
80 , m_zoomAndPan(SVGZoomAndPanMagnify)
81 , m_timeContainer(SMILTimeContainer::create(this))
82 , m_translation(SVGPoint::create())
84 ScriptWrappable::init(this);
86 m_width->setDefaultValueAsString("100%");
87 m_height->setDefaultValueAsString("100%");
89 addToPropertyMap(m_x);
90 addToPropertyMap(m_y);
91 addToPropertyMap(m_width);
92 addToPropertyMap(m_height);
93 addToPropertyMap(m_viewBox);
94 addToPropertyMap(m_preserveAspectRatio);
95 registerAnimatedPropertiesForSVGSVGElement();
97 UseCounter::count(doc, UseCounter::SVGSVGElement);
100 PassRefPtr<SVGSVGElement> SVGSVGElement::create(Document& document)
102 return adoptRef(new SVGSVGElement(document));
105 SVGSVGElement::~SVGSVGElement()
108 m_viewSpec->detachContextElement();
110 // There are cases where removedFromDocument() is not called.
111 // see ContainerNode::removeAllChildren, called by its destructor.
112 document().accessSVGExtensions()->removeTimeContainer(this);
114 ASSERT(inDocument() || !accessDocumentSVGExtensions()->isSVGRootWithRelativeLengthDescendents(this));
117 const AtomicString& SVGSVGElement::contentScriptType() const
119 DEFINE_STATIC_LOCAL(const AtomicString, defaultValue, ("text/ecmascript", AtomicString::ConstructFromLiteral));
120 const AtomicString& n = fastGetAttribute(SVGNames::contentScriptTypeAttr);
121 return n.isNull() ? defaultValue : n;
124 void SVGSVGElement::setContentScriptType(const AtomicString& type)
126 setAttribute(SVGNames::contentScriptTypeAttr, type);
129 const AtomicString& SVGSVGElement::contentStyleType() const
131 DEFINE_STATIC_LOCAL(const AtomicString, defaultValue, ("text/css", AtomicString::ConstructFromLiteral));
132 const AtomicString& n = fastGetAttribute(SVGNames::contentStyleTypeAttr);
133 return n.isNull() ? defaultValue : n;
136 void SVGSVGElement::setContentStyleType(const AtomicString& type)
138 setAttribute(SVGNames::contentStyleTypeAttr, type);
141 PassRefPtr<SVGRectTearOff> SVGSVGElement::viewport() const
143 // FIXME: This method doesn't follow the spec and is basically untested. Parent documents are not considered here.
144 // As we have no test coverage for this, we're going to disable it completly for now.
145 return SVGRectTearOff::create(SVGRect::create(), 0, PropertyIsNotAnimVal);
148 float SVGSVGElement::pixelUnitToMillimeterX() const
150 return 1 / cssPixelsPerMillimeter;
153 float SVGSVGElement::pixelUnitToMillimeterY() const
155 return 1 / cssPixelsPerMillimeter;
158 float SVGSVGElement::screenPixelToMillimeterX() const
160 return pixelUnitToMillimeterX();
163 float SVGSVGElement::screenPixelToMillimeterY() const
165 return pixelUnitToMillimeterY();
168 SVGViewSpec* SVGSVGElement::currentView()
171 m_viewSpec = SVGViewSpec::create(this);
172 return m_viewSpec.get();
175 float SVGSVGElement::currentScale() const
177 if (!inDocument() || !isOutermostSVGSVGElement())
180 Frame* frame = document().frame();
184 const FrameTree& frameTree = frame->tree();
186 // The behaviour of currentScale() is undefined, when we're dealing with non-standalone SVG documents.
187 // If the svg is embedded, the scaling is handled by the host renderer, so when asking from inside
188 // the SVG document, a scale value of 1 seems reasonable, as it doesn't know anything about the parent scale.
189 return frameTree.parent() ? 1 : frame->pageZoomFactor();
192 void SVGSVGElement::setCurrentScale(float scale)
194 if (!inDocument() || !isOutermostSVGSVGElement())
197 Frame* frame = document().frame();
201 const FrameTree& frameTree = frame->tree();
203 // The behaviour of setCurrentScale() is undefined, when we're dealing with non-standalone SVG documents.
204 // We choose the ignore this call, it's pretty useless to support calling setCurrentScale() from within
205 // an embedded SVG document, for the same reasons as in currentScale() - needs resolution by SVG WG.
206 if (frameTree.parent())
209 frame->setPageZoomFactor(scale);
212 class SVGCurrentTranslateTearOff : public SVGPointTearOff {
214 static PassRefPtr<SVGCurrentTranslateTearOff> create(SVGSVGElement* contextElement)
216 return adoptRef(new SVGCurrentTranslateTearOff(contextElement));
219 virtual void commitChange() OVERRIDE
221 ASSERT(contextElement());
222 toSVGSVGElement(contextElement())->updateCurrentTranslate();
226 SVGCurrentTranslateTearOff(SVGSVGElement* contextElement)
227 : SVGPointTearOff(contextElement->m_translation, contextElement, PropertyIsNotAnimVal)
232 PassRefPtr<SVGPointTearOff> SVGSVGElement::currentTranslateFromJavascript()
234 return SVGCurrentTranslateTearOff::create(this);
237 void SVGSVGElement::setCurrentTranslate(const FloatPoint& point)
239 m_translation->setValue(point);
240 updateCurrentTranslate();
243 void SVGSVGElement::updateCurrentTranslate()
245 if (RenderObject* object = renderer())
246 object->setNeedsLayout();
248 if (parentNode() == document() && document().renderer())
249 document().renderer()->repaint();
252 void SVGSVGElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
254 SVGParsingError parseError = NoError;
256 if (!nearestViewportElement()) {
257 bool setListener = true;
259 // Only handle events if we're the outermost <svg> element
260 if (name == HTMLNames::onunloadAttr)
261 document().setWindowAttributeEventListener(EventTypeNames::unload, createAttributeEventListener(document().frame(), name, value));
262 else if (name == HTMLNames::onresizeAttr)
263 document().setWindowAttributeEventListener(EventTypeNames::resize, createAttributeEventListener(document().frame(), name, value));
264 else if (name == HTMLNames::onscrollAttr)
265 document().setWindowAttributeEventListener(EventTypeNames::scroll, createAttributeEventListener(document().frame(), name, value));
266 else if (name == SVGNames::onzoomAttr)
267 document().setWindowAttributeEventListener(EventTypeNames::zoom, createAttributeEventListener(document().frame(), name, value));
275 if (name == HTMLNames::onabortAttr)
276 document().setWindowAttributeEventListener(EventTypeNames::abort, createAttributeEventListener(document().frame(), name, value));
277 else if (name == HTMLNames::onerrorAttr)
278 document().setWindowAttributeEventListener(EventTypeNames::error, createAttributeEventListener(document().frame(), name, value));
279 else if (name == SVGNames::xAttr)
280 m_x->setBaseValueAsString(value, AllowNegativeLengths, parseError);
281 else if (name == SVGNames::yAttr)
282 m_y->setBaseValueAsString(value, AllowNegativeLengths, parseError);
283 else if (name == SVGNames::widthAttr)
284 m_width->setBaseValueAsString(value, ForbidNegativeLengths, parseError);
285 else if (name == SVGNames::heightAttr)
286 m_height->setBaseValueAsString(value, ForbidNegativeLengths, parseError);
287 else if (SVGFitToViewBox::parseAttribute(this, name, value)
288 || SVGZoomAndPan::parseAttribute(this, name, value)) {
290 SVGGraphicsElement::parseAttribute(name, value);
292 reportAttributeParsingError(parseError, name, value);
295 void SVGSVGElement::svgAttributeChanged(const QualifiedName& attrName)
297 bool updateRelativeLengthsOrViewBox = false;
298 bool widthChanged = attrName == SVGNames::widthAttr;
300 || attrName == SVGNames::heightAttr
301 || attrName == SVGNames::xAttr
302 || attrName == SVGNames::yAttr) {
303 updateRelativeLengthsOrViewBox = true;
304 updateRelativeLengthsInformation();
305 invalidateRelativeLengthClients();
307 // At the SVG/HTML boundary (aka RenderSVGRoot), the width attribute can
308 // affect the replaced size so we need to mark it for updating.
310 RenderObject* renderObject = renderer();
311 if (renderObject && renderObject->isSVGRoot())
312 toRenderSVGRoot(renderObject)->setNeedsLayoutAndPrefWidthsRecalc();
316 if (SVGFitToViewBox::isKnownAttribute(attrName)) {
317 updateRelativeLengthsOrViewBox = true;
318 if (RenderObject* object = renderer())
319 object->setNeedsTransformUpdate();
322 SVGElementInstance::InvalidationGuard invalidationGuard(this);
324 if (updateRelativeLengthsOrViewBox
325 || SVGZoomAndPan::isKnownAttribute(attrName)) {
327 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer());
331 SVGGraphicsElement::svgAttributeChanged(attrName);
334 unsigned SVGSVGElement::suspendRedraw(unsigned /* maxWaitMilliseconds */)
336 // FIXME: Implement me (see bug 11275)
340 void SVGSVGElement::unsuspendRedraw(unsigned /* suspendHandleId */)
342 // FIXME: Implement me (see bug 11275)
345 void SVGSVGElement::unsuspendRedrawAll()
347 // FIXME: Implement me (see bug 11275)
350 void SVGSVGElement::forceRedraw()
352 // FIXME: Implement me (see bug 11275)
355 PassRefPtr<NodeList> SVGSVGElement::collectIntersectionOrEnclosureList(const FloatRect& rect, SVGElement* referenceElement, CollectIntersectionOrEnclosure collect) const
357 Vector<RefPtr<Node> > nodes;
358 Element* element = ElementTraversal::next(*(referenceElement ? referenceElement : this));
360 if (element->isSVGElement()) {
361 SVGElement* svgElement = toSVGElement(element);
362 if (collect == CollectIntersectionList) {
363 if (RenderSVGModelObject::checkIntersection(svgElement->renderer(), rect))
364 nodes.append(element);
366 if (RenderSVGModelObject::checkEnclosure(svgElement->renderer(), rect))
367 nodes.append(element);
371 element = ElementTraversal::next(*element, referenceElement ? referenceElement : this);
373 return StaticNodeList::adopt(nodes);
376 PassRefPtr<NodeList> SVGSVGElement::getIntersectionList(PassRefPtr<SVGRectTearOff> passRect, SVGElement* referenceElement) const
378 RefPtr<SVGRectTearOff> rect = passRect;
379 return collectIntersectionOrEnclosureList(rect->target()->value(), referenceElement, CollectIntersectionList);
382 PassRefPtr<NodeList> SVGSVGElement::getEnclosureList(PassRefPtr<SVGRectTearOff> passRect, SVGElement* referenceElement) const
384 RefPtr<SVGRectTearOff> rect = passRect;
385 return collectIntersectionOrEnclosureList(rect->target()->value(), referenceElement, CollectEnclosureList);
388 bool SVGSVGElement::checkIntersection(SVGElement* element, PassRefPtr<SVGRectTearOff> passRect) const
392 RefPtr<SVGRectTearOff> rect = passRect;
393 return RenderSVGModelObject::checkIntersection(element->renderer(), rect->target()->value());
396 bool SVGSVGElement::checkEnclosure(SVGElement* element, PassRefPtr<SVGRectTearOff> passRect) const
400 RefPtr<SVGRectTearOff> rect = passRect;
401 return RenderSVGModelObject::checkEnclosure(element->renderer(), rect->target()->value());
404 void SVGSVGElement::deselectAll()
406 if (Frame* frame = document().frame())
407 frame->selection().clear();
410 PassRefPtr<SVGNumberTearOff> SVGSVGElement::createSVGNumber()
412 return SVGNumberTearOff::create(SVGNumber::create(0.0f), 0, PropertyIsNotAnimVal);
415 PassRefPtr<SVGLengthTearOff> SVGSVGElement::createSVGLength()
417 return SVGLengthTearOff::create(SVGLength::create(), 0, PropertyIsNotAnimVal);
420 SVGAngle SVGSVGElement::createSVGAngle()
425 PassRefPtr<SVGPointTearOff> SVGSVGElement::createSVGPoint()
427 return SVGPointTearOff::create(SVGPoint::create(), 0, PropertyIsNotAnimVal);
430 SVGMatrix SVGSVGElement::createSVGMatrix()
435 PassRefPtr<SVGRectTearOff> SVGSVGElement::createSVGRect()
437 return SVGRectTearOff::create(SVGRect::create(), 0, PropertyIsNotAnimVal);
440 SVGTransform SVGSVGElement::createSVGTransform()
442 return SVGTransform(SVGTransform::SVG_TRANSFORM_MATRIX);
445 SVGTransform SVGSVGElement::createSVGTransformFromMatrix(const SVGMatrix& matrix)
447 return SVGTransform(static_cast<const AffineTransform&>(matrix));
450 AffineTransform SVGSVGElement::localCoordinateSpaceTransform(SVGElement::CTMScope mode) const
452 AffineTransform viewBoxTransform;
453 if (!hasEmptyViewBox()) {
454 FloatSize size = currentViewportSize();
455 viewBoxTransform = viewBoxToViewTransform(size.width(), size.height());
458 AffineTransform transform;
459 if (!isOutermostSVGSVGElement()) {
460 SVGLengthContext lengthContext(this);
461 transform.translate(m_x->currentValue()->value(lengthContext), m_y->currentValue()->value(lengthContext));
462 } else if (mode == SVGElement::ScreenScope) {
463 if (RenderObject* renderer = this->renderer()) {
465 float zoomFactor = 1;
467 // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform
468 // to map an element from SVG viewport coordinates to CSS box coordinates.
469 // RenderSVGRoot's localToAbsolute method expects CSS box coordinates.
470 // We also need to adjust for the zoom level factored into CSS coordinates (bug #96361).
471 if (renderer->isSVGRoot()) {
472 location = toRenderSVGRoot(renderer)->localToBorderBoxTransform().mapPoint(location);
473 zoomFactor = 1 / renderer->style()->effectiveZoom();
476 // Translate in our CSS parent coordinate space
477 // FIXME: This doesn't work correctly with CSS transforms.
478 location = renderer->localToAbsolute(location, UseTransforms);
479 location.scale(zoomFactor, zoomFactor);
481 // Be careful here! localToBorderBoxTransform() included the x/y offset coming from the viewBoxToViewTransform(),
482 // so we have to subtract it here (original cause of bug #27183)
483 transform.translate(location.x() - viewBoxTransform.e(), location.y() - viewBoxTransform.f());
485 // Respect scroll offset.
486 if (FrameView* view = document().view()) {
487 LayoutSize scrollOffset = view->scrollOffset();
488 scrollOffset.scale(zoomFactor);
489 transform.translate(-scrollOffset.width(), -scrollOffset.height());
494 return transform.multiply(viewBoxTransform);
497 bool SVGSVGElement::rendererIsNeeded(const RenderStyle& style)
499 // FIXME: We should respect display: none on the documentElement svg element
500 // but many things in FrameView and SVGImage depend on the RenderSVGRoot when
501 // they should instead depend on the RenderView.
502 // https://bugs.webkit.org/show_bug.cgi?id=103493
503 if (document().documentElement() == this)
505 return Element::rendererIsNeeded(style);
508 RenderObject* SVGSVGElement::createRenderer(RenderStyle*)
510 if (isOutermostSVGSVGElement())
511 return new RenderSVGRoot(this);
513 return new RenderSVGViewportContainer(this);
516 Node::InsertionNotificationRequest SVGSVGElement::insertedInto(ContainerNode* rootParent)
518 if (rootParent->inDocument()) {
519 document().accessSVGExtensions()->addTimeContainer(this);
521 // Animations are started at the end of document parsing and after firing the load event,
522 // but if we miss that train (deferred programmatic element insertion for example) we need
523 // to initialize the time container here.
524 if (!document().parsing() && !document().processingLoadEvent() && document().loadEventFinished() && !timeContainer()->isStarted())
525 timeContainer()->begin();
527 return SVGGraphicsElement::insertedInto(rootParent);
530 void SVGSVGElement::removedFrom(ContainerNode* rootParent)
532 if (rootParent->inDocument()) {
533 SVGDocumentExtensions* svgExtensions = document().accessSVGExtensions();
534 svgExtensions->removeTimeContainer(this);
535 svgExtensions->removeSVGRootWithRelativeLengthDescendents(this);
538 SVGGraphicsElement::removedFrom(rootParent);
541 void SVGSVGElement::pauseAnimations()
543 if (!m_timeContainer->isPaused())
544 m_timeContainer->pause();
547 void SVGSVGElement::unpauseAnimations()
549 if (m_timeContainer->isPaused())
550 m_timeContainer->resume();
553 bool SVGSVGElement::animationsPaused() const
555 return m_timeContainer->isPaused();
558 float SVGSVGElement::getCurrentTime() const
560 return narrowPrecisionToFloat(m_timeContainer->elapsed().value());
563 void SVGSVGElement::setCurrentTime(float seconds)
565 if (std::isnan(seconds))
567 seconds = max(seconds, 0.0f);
568 m_timeContainer->setElapsed(seconds);
571 bool SVGSVGElement::selfHasRelativeLengths() const
573 return m_x->currentValue()->isRelative()
574 || m_y->currentValue()->isRelative()
575 || m_width->currentValue()->isRelative()
576 || m_height->currentValue()->isRelative()
577 || hasAttribute(SVGNames::viewBoxAttr);
580 FloatRect SVGSVGElement::currentViewBoxRect() const
582 if (m_useCurrentView)
583 return m_viewSpec ? m_viewSpec->viewBox()->currentValue()->value() : FloatRect();
585 FloatRect useViewBox = m_viewBox->currentValue()->value();
586 if (!useViewBox.isEmpty())
588 if (!renderer() || !renderer()->isSVGRoot())
590 if (!toRenderSVGRoot(renderer())->isEmbeddedThroughSVGImage())
593 Length intrinsicWidth = this->intrinsicWidth();
594 Length intrinsicHeight = this->intrinsicHeight();
595 if (!intrinsicWidth.isFixed() || !intrinsicHeight.isFixed())
598 // If no viewBox is specified but non-relative width/height values, then we
599 // should always synthesize a viewBox if we're embedded through a SVGImage.
600 return FloatRect(FloatPoint(), FloatSize(floatValueForLength(intrinsicWidth, 0), floatValueForLength(intrinsicHeight, 0)));
603 FloatSize SVGSVGElement::currentViewportSize() const
605 Length intrinsicWidth = this->intrinsicWidth();
606 Length intrinsicHeight = this->intrinsicHeight();
607 if (intrinsicWidth.isFixed() && intrinsicHeight.isFixed())
608 return FloatSize(floatValueForLength(intrinsicWidth, 0), floatValueForLength(intrinsicHeight, 0));
613 if (renderer()->isSVGRoot()) {
614 LayoutRect contentBoxRect = toRenderSVGRoot(renderer())->contentBoxRect();
615 return FloatSize(contentBoxRect.width() / renderer()->style()->effectiveZoom(), contentBoxRect.height() / renderer()->style()->effectiveZoom());
618 FloatRect viewportRect = toRenderSVGViewportContainer(renderer())->viewport();
619 return FloatSize(viewportRect.width(), viewportRect.height());
622 bool SVGSVGElement::widthAttributeEstablishesViewport() const
624 if (!renderer() || renderer()->isSVGViewportContainer())
627 // Spec: http://www.w3.org/TR/SVG/coords.html#ViewportSpace
628 // The ‘width’ attribute on the outermost svg element establishes the viewport's width, unless the following conditions are met:
629 // - the SVG content is a separately stored resource that is embedded by reference (such as the ‘object’ element in XHTML [XHTML]), or
630 // the SVG content is embedded inline within a containing document;
631 // - and the referencing element or containing document is styled using CSS [CSS2] or XSL [XSL];
632 // - and there are CSS-compatible positioning properties ([CSS2], section 9.3) specified on the referencing element (e.g., the ‘object’ element)
633 // or on the containing document's outermost svg element that are sufficient to establish the width of the viewport. Under these conditions,
634 // the positioning properties establish the viewport's width.
635 RenderSVGRoot* root = toRenderSVGRoot(renderer());
637 // SVG embedded through object/embed/iframe.
638 if (root->isEmbeddedThroughFrameContainingSVGDocument())
639 return !root->hasReplacedLogicalWidth() && !document().frame()->ownerRenderer()->hasReplacedLogicalWidth();
641 // SVG embedded via SVGImage (background-image/border-image/etc) / Inline SVG.
642 if (root->isEmbeddedThroughSVGImage() || document().documentElement() != this)
643 return !root->hasReplacedLogicalWidth();
648 bool SVGSVGElement::heightAttributeEstablishesViewport() const
650 if (!renderer() || renderer()->isSVGViewportContainer())
653 // Spec: http://www.w3.org/TR/SVG/coords.html#IntrinsicSizing
654 // Similarly, if there are positioning properties specified on the referencing element or on the outermost svg element
655 // that are sufficient to establish the height of the viewport, then these positioning properties establish the viewport's
656 // height; otherwise, the ‘height’ attribute on the outermost svg element establishes the viewport's height.
657 RenderSVGRoot* root = toRenderSVGRoot(renderer());
659 // SVG embedded through object/embed/iframe.
660 if (root->isEmbeddedThroughFrameContainingSVGDocument())
661 return !root->hasReplacedLogicalHeight() && !document().frame()->ownerRenderer()->hasReplacedLogicalHeight();
663 // SVG embedded via SVGImage (background-image/border-image/etc) / Inline SVG.
664 if (root->isEmbeddedThroughSVGImage() || document().documentElement() != this)
665 return !root->hasReplacedLogicalHeight();
670 Length SVGSVGElement::intrinsicWidth(ConsiderCSSMode mode) const
672 if (widthAttributeEstablishesViewport() || mode == IgnoreCSSProperties) {
673 if (m_width->currentValue()->unitType() == LengthTypePercentage)
674 return Length(m_width->currentValue()->valueAsPercentage() * 100, Percent);
676 SVGLengthContext lengthContext(this);
677 return Length(m_width->currentValue()->value(lengthContext), Fixed);
681 return renderer()->style()->width();
684 Length SVGSVGElement::intrinsicHeight(ConsiderCSSMode mode) const
686 if (heightAttributeEstablishesViewport() || mode == IgnoreCSSProperties) {
687 if (m_height->currentValue()->unitType() == LengthTypePercentage)
688 return Length(m_height->currentValue()->valueAsPercentage() * 100, Percent);
690 SVGLengthContext lengthContext(this);
691 return Length(m_height->currentValue()->value(lengthContext), Fixed);
695 return renderer()->style()->height();
698 AffineTransform SVGSVGElement::viewBoxToViewTransform(float viewWidth, float viewHeight) const
700 if (!m_useCurrentView || !m_viewSpec)
701 return SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), m_preserveAspectRatio->currentValue(), viewWidth, viewHeight);
703 AffineTransform ctm = SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), m_viewSpec->preserveAspectRatio()->currentValue(), viewWidth, viewHeight);
704 const SVGTransformList& transformList = m_viewSpec->transformBaseValue();
705 if (transformList.isEmpty())
708 AffineTransform transform;
709 if (transformList.concatenate(transform))
715 void SVGSVGElement::setupInitialView(const String& fragmentIdentifier, Element* anchorNode)
717 RenderObject* renderer = this->renderer();
718 SVGViewSpec* view = m_viewSpec.get();
722 bool hadUseCurrentView = m_useCurrentView;
723 m_useCurrentView = false;
725 if (fragmentIdentifier.startsWith("xpointer(")) {
726 // FIXME: XPointer references are ignored (https://bugs.webkit.org/show_bug.cgi?id=17491)
727 if (renderer && hadUseCurrentView)
728 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
732 if (fragmentIdentifier.startsWith("svgView(")) {
734 view = currentView(); // Create the SVGViewSpec.
736 if (view->parseViewSpec(fragmentIdentifier))
737 m_useCurrentView = true;
741 if (renderer && (hadUseCurrentView || m_useCurrentView))
742 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
746 // Spec: If the SVG fragment identifier addresses a ‘view’ element within an SVG document (e.g., MyDrawing.svg#MyView
747 // or MyDrawing.svg#xpointer(id('MyView'))) then the closest ancestor ‘svg’ element is displayed in the viewport.
748 // Any view specification attributes included on the given ‘view’ element override the corresponding view specification
749 // attributes on the closest ancestor ‘svg’ element.
750 if (anchorNode && anchorNode->hasTagName(SVGNames::viewTag)) {
751 SVGViewElement* viewElement = toSVGViewElement(anchorNode);
755 if (SVGSVGElement* svg = viewElement->ownerSVGElement()) {
756 svg->inheritViewAttributes(viewElement);
758 if (RenderObject* renderer = svg->renderer())
759 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
763 // FIXME: We need to decide which <svg> to focus on, and zoom to it.
764 // FIXME: We need to actually "highlight" the viewTarget(s).
767 void SVGSVGElement::inheritViewAttributes(SVGViewElement* viewElement)
769 SVGViewSpec* view = currentView();
770 m_useCurrentView = true;
772 if (viewElement->hasAttribute(SVGNames::viewBoxAttr))
773 view->viewBox()->baseValue()->setValue(viewElement->viewBox()->currentValue()->value());
775 view->viewBox()->baseValue()->setValue(m_viewBox->currentValue()->value());
777 if (viewElement->hasAttribute(SVGNames::preserveAspectRatioAttr)) {
778 view->preserveAspectRatio()->baseValue()->setAlign(viewElement->preserveAspectRatio()->currentValue()->align());
779 view->preserveAspectRatio()->baseValue()->setMeetOrSlice(viewElement->preserveAspectRatio()->currentValue()->meetOrSlice());
781 view->preserveAspectRatio()->baseValue()->setAlign(m_preserveAspectRatio->currentValue()->align());
782 view->preserveAspectRatio()->baseValue()->setMeetOrSlice(m_preserveAspectRatio->currentValue()->meetOrSlice());
785 if (viewElement->hasAttribute(SVGNames::zoomAndPanAttr))
786 view->setZoomAndPanBaseValue(viewElement->zoomAndPan());
788 view->setZoomAndPanBaseValue(zoomAndPan());
791 // getElementById on SVGSVGElement is restricted to only the child subtree defined by the <svg> element.
792 // See http://www.w3.org/TR/SVG11/struct.html#InterfaceSVGSVGElement
793 Element* SVGSVGElement::getElementById(const AtomicString& id) const
795 Element* element = treeScope().getElementById(id);
796 if (element && element->isDescendantOf(this))
799 // Fall back to traversing our subtree. Duplicate ids are allowed, the first found will
801 for (Node* node = firstChild(); node; node = NodeTraversal::next(*node, this)) {
802 if (!node->isElementNode())
805 Element* element = toElement(node);
806 if (element->getIdAttribute() == id)