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 , SVGFitToViewBox(this)
74 , m_x(SVGAnimatedLength::create(this, SVGNames::xAttr, SVGLength::create(LengthModeWidth)))
75 , m_y(SVGAnimatedLength::create(this, SVGNames::yAttr, SVGLength::create(LengthModeHeight)))
76 , m_width(SVGAnimatedLength::create(this, SVGNames::widthAttr, SVGLength::create(LengthModeWidth)))
77 , m_height(SVGAnimatedLength::create(this, SVGNames::heightAttr, SVGLength::create(LengthModeHeight)))
78 , m_useCurrentView(false)
79 , m_timeContainer(SMILTimeContainer::create(this))
80 , m_translation(SVGPoint::create())
82 ScriptWrappable::init(this);
84 m_width->setDefaultValueAsString("100%");
85 m_height->setDefaultValueAsString("100%");
87 addToPropertyMap(m_x);
88 addToPropertyMap(m_y);
89 addToPropertyMap(m_width);
90 addToPropertyMap(m_height);
91 registerAnimatedPropertiesForSVGSVGElement();
93 UseCounter::count(doc, UseCounter::SVGSVGElement);
96 PassRefPtr<SVGSVGElement> SVGSVGElement::create(Document& document)
98 return adoptRef(new SVGSVGElement(document));
101 SVGSVGElement::~SVGSVGElement()
104 m_viewSpec->detachContextElement();
106 // There are cases where removedFromDocument() is not called.
107 // see ContainerNode::removeAllChildren, called by its destructor.
108 document().accessSVGExtensions()->removeTimeContainer(this);
110 ASSERT(inDocument() || !accessDocumentSVGExtensions()->isSVGRootWithRelativeLengthDescendents(this));
113 const AtomicString& SVGSVGElement::contentScriptType() const
115 DEFINE_STATIC_LOCAL(const AtomicString, defaultValue, ("text/ecmascript", AtomicString::ConstructFromLiteral));
116 const AtomicString& n = fastGetAttribute(SVGNames::contentScriptTypeAttr);
117 return n.isNull() ? defaultValue : n;
120 void SVGSVGElement::setContentScriptType(const AtomicString& type)
122 setAttribute(SVGNames::contentScriptTypeAttr, type);
125 const AtomicString& SVGSVGElement::contentStyleType() const
127 DEFINE_STATIC_LOCAL(const AtomicString, defaultValue, ("text/css", AtomicString::ConstructFromLiteral));
128 const AtomicString& n = fastGetAttribute(SVGNames::contentStyleTypeAttr);
129 return n.isNull() ? defaultValue : n;
132 void SVGSVGElement::setContentStyleType(const AtomicString& type)
134 setAttribute(SVGNames::contentStyleTypeAttr, type);
137 PassRefPtr<SVGRectTearOff> SVGSVGElement::viewport() const
139 // FIXME: This method doesn't follow the spec and is basically untested. Parent documents are not considered here.
140 // As we have no test coverage for this, we're going to disable it completly for now.
141 return SVGRectTearOff::create(SVGRect::create(), 0, PropertyIsNotAnimVal);
144 float SVGSVGElement::pixelUnitToMillimeterX() const
146 return 1 / cssPixelsPerMillimeter;
149 float SVGSVGElement::pixelUnitToMillimeterY() const
151 return 1 / cssPixelsPerMillimeter;
154 float SVGSVGElement::screenPixelToMillimeterX() const
156 return pixelUnitToMillimeterX();
159 float SVGSVGElement::screenPixelToMillimeterY() const
161 return pixelUnitToMillimeterY();
164 SVGViewSpec* SVGSVGElement::currentView()
167 m_viewSpec = SVGViewSpec::create(this);
168 return m_viewSpec.get();
171 float SVGSVGElement::currentScale() const
173 if (!inDocument() || !isOutermostSVGSVGElement())
176 Frame* frame = document().frame();
180 const FrameTree& frameTree = frame->tree();
182 // The behaviour of currentScale() is undefined, when we're dealing with non-standalone SVG documents.
183 // If the svg is embedded, the scaling is handled by the host renderer, so when asking from inside
184 // the SVG document, a scale value of 1 seems reasonable, as it doesn't know anything about the parent scale.
185 return frameTree.parent() ? 1 : frame->pageZoomFactor();
188 void SVGSVGElement::setCurrentScale(float scale)
190 if (!inDocument() || !isOutermostSVGSVGElement())
193 Frame* frame = document().frame();
197 const FrameTree& frameTree = frame->tree();
199 // The behaviour of setCurrentScale() is undefined, when we're dealing with non-standalone SVG documents.
200 // We choose the ignore this call, it's pretty useless to support calling setCurrentScale() from within
201 // an embedded SVG document, for the same reasons as in currentScale() - needs resolution by SVG WG.
202 if (frameTree.parent())
205 frame->setPageZoomFactor(scale);
208 class SVGCurrentTranslateTearOff : public SVGPointTearOff {
210 static PassRefPtr<SVGCurrentTranslateTearOff> create(SVGSVGElement* contextElement)
212 return adoptRef(new SVGCurrentTranslateTearOff(contextElement));
215 virtual void commitChange() OVERRIDE
217 ASSERT(contextElement());
218 toSVGSVGElement(contextElement())->updateCurrentTranslate();
222 SVGCurrentTranslateTearOff(SVGSVGElement* contextElement)
223 : SVGPointTearOff(contextElement->m_translation, contextElement, PropertyIsNotAnimVal)
228 PassRefPtr<SVGPointTearOff> SVGSVGElement::currentTranslateFromJavascript()
230 return SVGCurrentTranslateTearOff::create(this);
233 void SVGSVGElement::setCurrentTranslate(const FloatPoint& point)
235 m_translation->setValue(point);
236 updateCurrentTranslate();
239 void SVGSVGElement::updateCurrentTranslate()
241 if (RenderObject* object = renderer())
242 object->setNeedsLayout();
244 if (parentNode() == document() && document().renderer())
245 document().renderer()->repaint();
248 void SVGSVGElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
250 SVGParsingError parseError = NoError;
252 if (!nearestViewportElement()) {
253 bool setListener = true;
255 // Only handle events if we're the outermost <svg> element
256 if (name == HTMLNames::onunloadAttr)
257 document().setWindowAttributeEventListener(EventTypeNames::unload, createAttributeEventListener(document().frame(), name, value));
258 else if (name == HTMLNames::onresizeAttr)
259 document().setWindowAttributeEventListener(EventTypeNames::resize, createAttributeEventListener(document().frame(), name, value));
260 else if (name == HTMLNames::onscrollAttr)
261 document().setWindowAttributeEventListener(EventTypeNames::scroll, createAttributeEventListener(document().frame(), name, value));
262 else if (name == SVGNames::onzoomAttr)
263 document().setWindowAttributeEventListener(EventTypeNames::zoom, createAttributeEventListener(document().frame(), name, value));
271 if (name == HTMLNames::onabortAttr) {
272 document().setWindowAttributeEventListener(EventTypeNames::abort, createAttributeEventListener(document().frame(), name, value));
273 } else if (name == HTMLNames::onerrorAttr) {
274 document().setWindowAttributeEventListener(EventTypeNames::error, createAttributeEventListener(document().frame(), name, value));
275 } else if (name == SVGNames::xAttr) {
276 m_x->setBaseValueAsString(value, AllowNegativeLengths, parseError);
277 } else if (name == SVGNames::yAttr) {
278 m_y->setBaseValueAsString(value, AllowNegativeLengths, parseError);
279 } else if (name == SVGNames::widthAttr) {
280 m_width->setBaseValueAsString(value, ForbidNegativeLengths, parseError);
281 } else if (name == SVGNames::heightAttr) {
282 m_height->setBaseValueAsString(value, ForbidNegativeLengths, parseError);
283 } else if (SVGFitToViewBox::parseAttribute(name, value, document(), parseError)) {
284 } else if (SVGZoomAndPan::parseAttribute(name, value)) {
286 SVGGraphicsElement::parseAttribute(name, value);
289 reportAttributeParsingError(parseError, name, value);
292 void SVGSVGElement::svgAttributeChanged(const QualifiedName& attrName)
294 bool updateRelativeLengthsOrViewBox = false;
295 bool widthChanged = attrName == SVGNames::widthAttr;
297 || attrName == SVGNames::heightAttr
298 || attrName == SVGNames::xAttr
299 || attrName == SVGNames::yAttr) {
300 updateRelativeLengthsOrViewBox = true;
301 updateRelativeLengthsInformation();
302 invalidateRelativeLengthClients();
304 // At the SVG/HTML boundary (aka RenderSVGRoot), the width attribute can
305 // affect the replaced size so we need to mark it for updating.
307 RenderObject* renderObject = renderer();
308 if (renderObject && renderObject->isSVGRoot())
309 toRenderSVGRoot(renderObject)->setNeedsLayoutAndPrefWidthsRecalc();
313 if (SVGFitToViewBox::isKnownAttribute(attrName)) {
314 updateRelativeLengthsOrViewBox = true;
315 if (RenderObject* object = renderer())
316 object->setNeedsTransformUpdate();
319 SVGElementInstance::InvalidationGuard invalidationGuard(this);
321 if (updateRelativeLengthsOrViewBox
322 || SVGZoomAndPan::isKnownAttribute(attrName)) {
324 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer());
328 SVGGraphicsElement::svgAttributeChanged(attrName);
331 PassRefPtr<NodeList> SVGSVGElement::collectIntersectionOrEnclosureList(const FloatRect& rect, SVGElement* referenceElement, CollectIntersectionOrEnclosure collect) const
333 Vector<RefPtr<Node> > nodes;
334 Element* element = ElementTraversal::next(*(referenceElement ? referenceElement : this));
336 if (element->isSVGElement()) {
337 SVGElement* svgElement = toSVGElement(element);
338 if (collect == CollectIntersectionList) {
339 if (RenderSVGModelObject::checkIntersection(svgElement->renderer(), rect))
340 nodes.append(element);
342 if (RenderSVGModelObject::checkEnclosure(svgElement->renderer(), rect))
343 nodes.append(element);
347 element = ElementTraversal::next(*element, referenceElement ? referenceElement : this);
349 return StaticNodeList::adopt(nodes);
352 PassRefPtr<NodeList> SVGSVGElement::getIntersectionList(PassRefPtr<SVGRectTearOff> passRect, SVGElement* referenceElement) const
354 RefPtr<SVGRectTearOff> rect = passRect;
355 return collectIntersectionOrEnclosureList(rect->target()->value(), referenceElement, CollectIntersectionList);
358 PassRefPtr<NodeList> SVGSVGElement::getEnclosureList(PassRefPtr<SVGRectTearOff> passRect, SVGElement* referenceElement) const
360 RefPtr<SVGRectTearOff> rect = passRect;
361 return collectIntersectionOrEnclosureList(rect->target()->value(), referenceElement, CollectEnclosureList);
364 bool SVGSVGElement::checkIntersection(SVGElement* element, PassRefPtr<SVGRectTearOff> passRect) const
366 RefPtr<SVGRectTearOff> rect = passRect;
367 return RenderSVGModelObject::checkIntersection(element->renderer(), rect->target()->value());
370 bool SVGSVGElement::checkEnclosure(SVGElement* element, PassRefPtr<SVGRectTearOff> passRect) const
372 RefPtr<SVGRectTearOff> rect = passRect;
373 return RenderSVGModelObject::checkEnclosure(element->renderer(), rect->target()->value());
376 void SVGSVGElement::deselectAll()
378 if (Frame* frame = document().frame())
379 frame->selection().clear();
382 PassRefPtr<SVGNumberTearOff> SVGSVGElement::createSVGNumber()
384 return SVGNumberTearOff::create(SVGNumber::create(0.0f), 0, PropertyIsNotAnimVal);
387 PassRefPtr<SVGLengthTearOff> SVGSVGElement::createSVGLength()
389 return SVGLengthTearOff::create(SVGLength::create(), 0, PropertyIsNotAnimVal);
392 SVGAngle SVGSVGElement::createSVGAngle()
397 PassRefPtr<SVGPointTearOff> SVGSVGElement::createSVGPoint()
399 return SVGPointTearOff::create(SVGPoint::create(), 0, PropertyIsNotAnimVal);
402 SVGMatrix SVGSVGElement::createSVGMatrix()
407 PassRefPtr<SVGRectTearOff> SVGSVGElement::createSVGRect()
409 return SVGRectTearOff::create(SVGRect::create(), 0, PropertyIsNotAnimVal);
412 SVGTransform SVGSVGElement::createSVGTransform()
414 return SVGTransform(SVGTransform::SVG_TRANSFORM_MATRIX);
417 SVGTransform SVGSVGElement::createSVGTransformFromMatrix(const SVGMatrix& matrix)
419 return SVGTransform(static_cast<const AffineTransform&>(matrix));
422 AffineTransform SVGSVGElement::localCoordinateSpaceTransform(SVGElement::CTMScope mode) const
424 AffineTransform viewBoxTransform;
425 if (!hasEmptyViewBox()) {
426 FloatSize size = currentViewportSize();
427 viewBoxTransform = viewBoxToViewTransform(size.width(), size.height());
430 AffineTransform transform;
431 if (!isOutermostSVGSVGElement()) {
432 SVGLengthContext lengthContext(this);
433 transform.translate(m_x->currentValue()->value(lengthContext), m_y->currentValue()->value(lengthContext));
434 } else if (mode == SVGElement::ScreenScope) {
435 if (RenderObject* renderer = this->renderer()) {
437 float zoomFactor = 1;
439 // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform
440 // to map an element from SVG viewport coordinates to CSS box coordinates.
441 // RenderSVGRoot's localToAbsolute method expects CSS box coordinates.
442 // We also need to adjust for the zoom level factored into CSS coordinates (bug #96361).
443 if (renderer->isSVGRoot()) {
444 location = toRenderSVGRoot(renderer)->localToBorderBoxTransform().mapPoint(location);
445 zoomFactor = 1 / renderer->style()->effectiveZoom();
448 // Translate in our CSS parent coordinate space
449 // FIXME: This doesn't work correctly with CSS transforms.
450 location = renderer->localToAbsolute(location, UseTransforms);
451 location.scale(zoomFactor, zoomFactor);
453 // Be careful here! localToBorderBoxTransform() included the x/y offset coming from the viewBoxToViewTransform(),
454 // so we have to subtract it here (original cause of bug #27183)
455 transform.translate(location.x() - viewBoxTransform.e(), location.y() - viewBoxTransform.f());
457 // Respect scroll offset.
458 if (FrameView* view = document().view()) {
459 LayoutSize scrollOffset = view->scrollOffset();
460 scrollOffset.scale(zoomFactor);
461 transform.translate(-scrollOffset.width(), -scrollOffset.height());
466 return transform.multiply(viewBoxTransform);
469 bool SVGSVGElement::rendererIsNeeded(const RenderStyle& style)
471 // FIXME: We should respect display: none on the documentElement svg element
472 // but many things in FrameView and SVGImage depend on the RenderSVGRoot when
473 // they should instead depend on the RenderView.
474 // https://bugs.webkit.org/show_bug.cgi?id=103493
475 if (document().documentElement() == this)
477 return Element::rendererIsNeeded(style);
480 RenderObject* SVGSVGElement::createRenderer(RenderStyle*)
482 if (isOutermostSVGSVGElement())
483 return new RenderSVGRoot(this);
485 return new RenderSVGViewportContainer(this);
488 Node::InsertionNotificationRequest SVGSVGElement::insertedInto(ContainerNode* rootParent)
490 if (rootParent->inDocument()) {
491 UseCounter::count(document(), UseCounter::SVGSVGElementInDocument);
493 document().accessSVGExtensions()->addTimeContainer(this);
495 // Animations are started at the end of document parsing and after firing the load event,
496 // but if we miss that train (deferred programmatic element insertion for example) we need
497 // to initialize the time container here.
498 if (!document().parsing() && !document().processingLoadEvent() && document().loadEventFinished() && !timeContainer()->isStarted())
499 timeContainer()->begin();
501 return SVGGraphicsElement::insertedInto(rootParent);
504 void SVGSVGElement::removedFrom(ContainerNode* rootParent)
506 if (rootParent->inDocument()) {
507 SVGDocumentExtensions* svgExtensions = document().accessSVGExtensions();
508 svgExtensions->removeTimeContainer(this);
509 svgExtensions->removeSVGRootWithRelativeLengthDescendents(this);
512 SVGGraphicsElement::removedFrom(rootParent);
515 void SVGSVGElement::pauseAnimations()
517 if (!m_timeContainer->isPaused())
518 m_timeContainer->pause();
521 void SVGSVGElement::unpauseAnimations()
523 if (m_timeContainer->isPaused())
524 m_timeContainer->resume();
527 bool SVGSVGElement::animationsPaused() const
529 return m_timeContainer->isPaused();
532 float SVGSVGElement::getCurrentTime() const
534 return narrowPrecisionToFloat(m_timeContainer->elapsed().value());
537 void SVGSVGElement::setCurrentTime(float seconds)
539 if (std::isnan(seconds))
541 seconds = max(seconds, 0.0f);
542 m_timeContainer->setElapsed(seconds);
545 bool SVGSVGElement::selfHasRelativeLengths() const
547 return m_x->currentValue()->isRelative()
548 || m_y->currentValue()->isRelative()
549 || m_width->currentValue()->isRelative()
550 || m_height->currentValue()->isRelative()
551 || hasAttribute(SVGNames::viewBoxAttr);
554 FloatRect SVGSVGElement::currentViewBoxRect() const
556 if (m_useCurrentView)
557 return m_viewSpec ? m_viewSpec->viewBox()->currentValue()->value() : FloatRect();
559 FloatRect useViewBox = viewBox()->currentValue()->value();
560 if (!useViewBox.isEmpty())
562 if (!renderer() || !renderer()->isSVGRoot())
564 if (!toRenderSVGRoot(renderer())->isEmbeddedThroughSVGImage())
567 Length intrinsicWidth = this->intrinsicWidth();
568 Length intrinsicHeight = this->intrinsicHeight();
569 if (!intrinsicWidth.isFixed() || !intrinsicHeight.isFixed())
572 // If no viewBox is specified but non-relative width/height values, then we
573 // should always synthesize a viewBox if we're embedded through a SVGImage.
574 return FloatRect(FloatPoint(), FloatSize(floatValueForLength(intrinsicWidth, 0), floatValueForLength(intrinsicHeight, 0)));
577 FloatSize SVGSVGElement::currentViewportSize() const
579 Length intrinsicWidth = this->intrinsicWidth();
580 Length intrinsicHeight = this->intrinsicHeight();
581 if (intrinsicWidth.isFixed() && intrinsicHeight.isFixed())
582 return FloatSize(floatValueForLength(intrinsicWidth, 0), floatValueForLength(intrinsicHeight, 0));
587 if (renderer()->isSVGRoot()) {
588 LayoutRect contentBoxRect = toRenderSVGRoot(renderer())->contentBoxRect();
589 return FloatSize(contentBoxRect.width() / renderer()->style()->effectiveZoom(), contentBoxRect.height() / renderer()->style()->effectiveZoom());
592 FloatRect viewportRect = toRenderSVGViewportContainer(renderer())->viewport();
593 return FloatSize(viewportRect.width(), viewportRect.height());
596 bool SVGSVGElement::widthAttributeEstablishesViewport() const
598 if (!renderer() || renderer()->isSVGViewportContainer())
601 // Spec: http://www.w3.org/TR/SVG/coords.html#ViewportSpace
602 // The ‘width’ attribute on the outermost svg element establishes the viewport's width, unless the following conditions are met:
603 // - the SVG content is a separately stored resource that is embedded by reference (such as the ‘object’ element in XHTML [XHTML]), or
604 // the SVG content is embedded inline within a containing document;
605 // - and the referencing element or containing document is styled using CSS [CSS2] or XSL [XSL];
606 // - and there are CSS-compatible positioning properties ([CSS2], section 9.3) specified on the referencing element (e.g., the ‘object’ element)
607 // or on the containing document's outermost svg element that are sufficient to establish the width of the viewport. Under these conditions,
608 // the positioning properties establish the viewport's width.
609 RenderSVGRoot* root = toRenderSVGRoot(renderer());
611 // SVG embedded through object/embed/iframe.
612 if (root->isEmbeddedThroughFrameContainingSVGDocument())
613 return !root->hasReplacedLogicalWidth() && !document().frame()->ownerRenderer()->hasReplacedLogicalWidth();
615 // SVG embedded via SVGImage (background-image/border-image/etc) / Inline SVG.
616 if (root->isEmbeddedThroughSVGImage() || document().documentElement() != this)
617 return !root->hasReplacedLogicalWidth();
622 bool SVGSVGElement::heightAttributeEstablishesViewport() const
624 if (!renderer() || renderer()->isSVGViewportContainer())
627 // Spec: http://www.w3.org/TR/SVG/coords.html#IntrinsicSizing
628 // Similarly, if there are positioning properties specified on the referencing element or on the outermost svg element
629 // that are sufficient to establish the height of the viewport, then these positioning properties establish the viewport's
630 // height; otherwise, the ‘height’ attribute on the outermost svg element establishes the viewport's height.
631 RenderSVGRoot* root = toRenderSVGRoot(renderer());
633 // SVG embedded through object/embed/iframe.
634 if (root->isEmbeddedThroughFrameContainingSVGDocument())
635 return !root->hasReplacedLogicalHeight() && !document().frame()->ownerRenderer()->hasReplacedLogicalHeight();
637 // SVG embedded via SVGImage (background-image/border-image/etc) / Inline SVG.
638 if (root->isEmbeddedThroughSVGImage() || document().documentElement() != this)
639 return !root->hasReplacedLogicalHeight();
644 Length SVGSVGElement::intrinsicWidth(ConsiderCSSMode mode) const
646 if (widthAttributeEstablishesViewport() || mode == IgnoreCSSProperties) {
647 if (m_width->currentValue()->unitType() == LengthTypePercentage)
648 return Length(m_width->currentValue()->valueAsPercentage() * 100, Percent);
650 SVGLengthContext lengthContext(this);
651 return Length(m_width->currentValue()->value(lengthContext), Fixed);
655 return renderer()->style()->width();
658 Length SVGSVGElement::intrinsicHeight(ConsiderCSSMode mode) const
660 if (heightAttributeEstablishesViewport() || mode == IgnoreCSSProperties) {
661 if (m_height->currentValue()->unitType() == LengthTypePercentage)
662 return Length(m_height->currentValue()->valueAsPercentage() * 100, Percent);
664 SVGLengthContext lengthContext(this);
665 return Length(m_height->currentValue()->value(lengthContext), Fixed);
669 return renderer()->style()->height();
672 AffineTransform SVGSVGElement::viewBoxToViewTransform(float viewWidth, float viewHeight) const
674 if (!m_useCurrentView || !m_viewSpec)
675 return SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), preserveAspectRatio()->currentValue(), viewWidth, viewHeight);
677 AffineTransform ctm = SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), m_viewSpec->preserveAspectRatio()->currentValue(), viewWidth, viewHeight);
678 const SVGTransformList& transformList = m_viewSpec->transformBaseValue();
679 if (transformList.isEmpty())
682 AffineTransform transform;
683 if (transformList.concatenate(transform))
689 void SVGSVGElement::setupInitialView(const String& fragmentIdentifier, Element* anchorNode)
691 RenderObject* renderer = this->renderer();
692 SVGViewSpec* view = m_viewSpec.get();
696 bool hadUseCurrentView = m_useCurrentView;
697 m_useCurrentView = false;
699 if (fragmentIdentifier.startsWith("xpointer(")) {
700 // FIXME: XPointer references are ignored (https://bugs.webkit.org/show_bug.cgi?id=17491)
701 if (renderer && hadUseCurrentView)
702 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
706 if (fragmentIdentifier.startsWith("svgView(")) {
708 view = currentView(); // Create the SVGViewSpec.
710 if (view->parseViewSpec(fragmentIdentifier))
711 m_useCurrentView = true;
715 if (renderer && (hadUseCurrentView || m_useCurrentView))
716 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
720 // Spec: If the SVG fragment identifier addresses a ‘view’ element within an SVG document (e.g., MyDrawing.svg#MyView
721 // or MyDrawing.svg#xpointer(id('MyView'))) then the closest ancestor ‘svg’ element is displayed in the viewport.
722 // Any view specification attributes included on the given ‘view’ element override the corresponding view specification
723 // attributes on the closest ancestor ‘svg’ element.
724 if (anchorNode && anchorNode->hasTagName(SVGNames::viewTag)) {
725 SVGViewElement* viewElement = toSVGViewElement(anchorNode);
729 if (SVGSVGElement* svg = viewElement->ownerSVGElement()) {
730 svg->inheritViewAttributes(viewElement);
732 if (RenderObject* renderer = svg->renderer())
733 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
737 // FIXME: We need to decide which <svg> to focus on, and zoom to it.
738 // FIXME: We need to actually "highlight" the viewTarget(s).
741 void SVGSVGElement::inheritViewAttributes(SVGViewElement* viewElement)
743 SVGViewSpec* view = currentView();
744 m_useCurrentView = true;
746 if (viewElement->hasAttribute(SVGNames::viewBoxAttr))
747 view->viewBox()->baseValue()->setValue(viewElement->viewBox()->currentValue()->value());
749 view->viewBox()->baseValue()->setValue(viewBox()->currentValue()->value());
751 if (viewElement->hasAttribute(SVGNames::preserveAspectRatioAttr)) {
752 view->preserveAspectRatio()->baseValue()->setAlign(viewElement->preserveAspectRatio()->currentValue()->align());
753 view->preserveAspectRatio()->baseValue()->setMeetOrSlice(viewElement->preserveAspectRatio()->currentValue()->meetOrSlice());
755 view->preserveAspectRatio()->baseValue()->setAlign(preserveAspectRatio()->currentValue()->align());
756 view->preserveAspectRatio()->baseValue()->setMeetOrSlice(preserveAspectRatio()->currentValue()->meetOrSlice());
759 if (viewElement->hasAttribute(SVGNames::zoomAndPanAttr))
760 view->setZoomAndPan(viewElement->zoomAndPan());
762 view->setZoomAndPan(zoomAndPan());
765 // getElementById on SVGSVGElement is restricted to only the child subtree defined by the <svg> element.
766 // See http://www.w3.org/TR/SVG11/struct.html#InterfaceSVGSVGElement
767 Element* SVGSVGElement::getElementById(const AtomicString& id) const
769 Element* element = treeScope().getElementById(id);
770 if (element && element->isDescendantOf(this))
773 // Fall back to traversing our subtree. Duplicate ids are allowed, the first found will
775 for (Node* node = firstChild(); node; node = NodeTraversal::next(*node, this)) {
776 if (!node->isElementNode())
779 Element* element = toElement(node);
780 if (element->getIdAttribute() == id)