1 /* 2 * Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann (at) kde.org> 3 * Copyright (C) 2004, 2005, 2007 Rob Buis <buis (at) kde.org> 4 * Copyright (C) 2007 Eric Seidel <eric (at) webkit.org> 5 * Copyright (C) 2009 Google, Inc. 6 * 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. 11 * 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. 16 * 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. 21 */ 22 23 #include "config.h" 24 25 #include "core/rendering/svg/RenderSVGViewportContainer.h" 26 27 #include "SVGNames.h" 28 #include "core/svg/SVGElementInstance.h" 29 #include "core/svg/SVGSVGElement.h" 30 #include "core/svg/SVGUseElement.h" 31 #include "platform/graphics/GraphicsContext.h" 32 33 namespace WebCore { 34 35 RenderSVGViewportContainer::RenderSVGViewportContainer(SVGElement* node) 36 : RenderSVGContainer(node) 37 , m_didTransformToRootUpdate(false) 38 , m_isLayoutSizeChanged(false) 39 , m_needsTransformUpdate(true) 40 { 41 } 42 43 void RenderSVGViewportContainer::determineIfLayoutSizeChanged() 44 { 45 if (!element()->hasTagName(SVGNames::svgTag)) 46 return; 47 48 m_isLayoutSizeChanged = toSVGSVGElement(element())->hasRelativeLengths() && selfNeedsLayout(); 49 } 50 51 void RenderSVGViewportContainer::applyViewportClip(PaintInfo& paintInfo) 52 { 53 if (SVGRenderSupport::isOverflowHidden(this)) 54 paintInfo.context->clip(m_viewport); 55 } 56 57 void RenderSVGViewportContainer::calcViewport() 58 { 59 SVGElement* element = this->element(); 60 if (!element->hasTagName(SVGNames::svgTag)) 61 return; 62 SVGSVGElement* svg = toSVGSVGElement(element); 63 FloatRect oldViewport = m_viewport; 64 65 SVGLengthContext lengthContext(element); 66 m_viewport = FloatRect(svg->xCurrentValue().value(lengthContext), svg->yCurrentValue().value(lengthContext), svg->widthCurrentValue().value(lengthContext), svg->heightCurrentValue().value(lengthContext)); 67 68 SVGElement* correspondingElement = svg->correspondingElement(); 69 if (correspondingElement && svg->isInShadowTree()) { 70 const HashSet<SVGElementInstance*>& instances = correspondingElement->instancesForElement(); 71 ASSERT(!instances.isEmpty()); 72 73 SVGUseElement* useElement = 0; 74 const HashSet<SVGElementInstance*>::const_iterator end = instances.end(); 75 for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) { 76 const SVGElementInstance* instance = (*it); 77 ASSERT(instance->correspondingElement()->hasTagName(SVGNames::svgTag) || instance->correspondingElement()->hasTagName(SVGNames::symbolTag)); 78 if (instance->shadowTreeElement() == svg) { 79 ASSERT(correspondingElement == instance->correspondingElement()); 80 useElement = instance->directUseElement(); 81 if (!useElement) 82 useElement = instance->correspondingUseElement(); 83 break; 84 } 85 } 86 87 ASSERT(useElement); 88 bool isSymbolElement = correspondingElement->hasTagName(SVGNames::symbolTag); 89 90 // Spec (<use> on <symbol>): This generated 'svg' will always have explicit values for attributes width and height. 91 // If attributes width and/or height are provided on the 'use' element, then these attributes 92 // will be transferred to the generated 'svg'. If attributes width and/or height are not specified, 93 // the generated 'svg' element will use values of 100% for these attributes. 94 95 // Spec (<use> on <svg>): If attributes width and/or height are provided on the 'use' element, then these 96 // values will override the corresponding attributes on the 'svg' in the generated tree. 97 98 SVGLengthContext lengthContext(element); 99 if (useElement->hasAttribute(SVGNames::widthAttr)) 100 m_viewport.setWidth(useElement->widthCurrentValue().value(lengthContext)); 101 else if (isSymbolElement && svg->hasAttribute(SVGNames::widthAttr)) { 102 SVGLength containerWidth(LengthModeWidth, "100%"); 103 m_viewport.setWidth(containerWidth.value(lengthContext)); 104 } 105 106 if (useElement->hasAttribute(SVGNames::heightAttr)) 107 m_viewport.setHeight(useElement->heightCurrentValue().value(lengthContext)); 108 else if (isSymbolElement && svg->hasAttribute(SVGNames::heightAttr)) { 109 SVGLength containerHeight(LengthModeHeight, "100%"); 110 m_viewport.setHeight(containerHeight.value(lengthContext)); 111 } 112 } 113 114 if (oldViewport != m_viewport) { 115 setNeedsBoundariesUpdate(); 116 setNeedsTransformUpdate(); 117 } 118 } 119 120 bool RenderSVGViewportContainer::calculateLocalTransform() 121 { 122 m_didTransformToRootUpdate = m_needsTransformUpdate || SVGRenderSupport::transformToRootChanged(parent()); 123 if (!m_needsTransformUpdate) 124 return false; 125 126 m_localToParentTransform = AffineTransform::translation(m_viewport.x(), m_viewport.y()) * viewportTransform(); 127 m_needsTransformUpdate = false; 128 return true; 129 } 130 131 AffineTransform RenderSVGViewportContainer::viewportTransform() const 132 { 133 if (element()->hasTagName(SVGNames::svgTag)) { 134 SVGSVGElement* svg = toSVGSVGElement(element()); 135 return svg->viewBoxToViewTransform(m_viewport.width(), m_viewport.height()); 136 } 137 138 return AffineTransform(); 139 } 140 141 bool RenderSVGViewportContainer::pointIsInsideViewportClip(const FloatPoint& pointInParent) 142 { 143 // Respect the viewport clip (which is in parent coords) 144 if (!SVGRenderSupport::isOverflowHidden(this)) 145 return true; 146 147 return m_viewport.contains(pointInParent); 148 } 149 150 void RenderSVGViewportContainer::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 151 { 152 // An empty viewBox disables rendering. 153 if (element()->hasTagName(SVGNames::svgTag)) { 154 if (toSVGSVGElement(element())->hasEmptyViewBox()) 155 return; 156 } 157 158 RenderSVGContainer::paint(paintInfo, paintOffset); 159 } 160 161 } 162