1 /* 2 * Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann (at) kde.org> 3 * Copyright (C) 2004, 2005, 2007, 2008 Rob Buis <buis (at) kde.org> 4 * Copyright (C) 2007 Eric Seidel <eric (at) webkit.org> 5 * Copyright (C) 2009 Google, Inc. All rights reserved. 6 * Copyright (C) 2009 Dirk Schulze <krit (at) webkit.org> 7 * 8 * This library is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Library General Public 10 * License as published by the Free Software Foundation; either 11 * version 2 of the License, or (at your option) any later version. 12 * 13 * This library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Library General Public License for more details. 17 * 18 * You should have received a copy of the GNU Library General Public License 19 * along with this library; see the file COPYING.LIB. If not, write to 20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 21 * Boston, MA 02110-1301, USA. 22 */ 23 24 #include "config.h" 25 26 #if ENABLE(SVG) 27 #include "RenderSVGContainer.h" 28 29 #include "GraphicsContext.h" 30 #include "RenderSVGResource.h" 31 #include "RenderSVGResourceFilter.h" 32 #include "RenderView.h" 33 #include "SVGRenderSupport.h" 34 #include "SVGResources.h" 35 #include "SVGStyledElement.h" 36 37 namespace WebCore { 38 39 RenderSVGContainer::RenderSVGContainer(SVGStyledElement* node) 40 : RenderSVGModelObject(node) 41 , m_needsBoundariesUpdate(true) 42 { 43 } 44 45 RenderSVGContainer::~RenderSVGContainer() 46 { 47 } 48 49 void RenderSVGContainer::layout() 50 { 51 ASSERT(needsLayout()); 52 53 // RenderSVGRoot disables layoutState for the SVG rendering tree. 54 ASSERT(!view()->layoutStateEnabled()); 55 56 // Allow RenderSVGViewportContainer to update its viewport. 57 calcViewport(); 58 59 LayoutRepainter repainter(*this, checkForRepaintDuringLayout() || selfWillPaint()); 60 61 // Allow RenderSVGTransformableContainer to update its transform. 62 bool updatedTransform = calculateLocalTransform(); 63 64 SVGRenderSupport::layoutChildren(this, selfNeedsLayout()); 65 66 // Invalidate all resources of this client if our layout changed. 67 if (m_everHadLayout && selfNeedsLayout()) 68 SVGResourcesCache::clientLayoutChanged(this); 69 70 // At this point LayoutRepainter already grabbed the old bounds, 71 // recalculate them now so repaintAfterLayout() uses the new bounds. 72 if (m_needsBoundariesUpdate || updatedTransform) { 73 updateCachedBoundaries(); 74 m_needsBoundariesUpdate = false; 75 76 // If our bounds changed, notify the parents. 77 RenderSVGModelObject::setNeedsBoundariesUpdate(); 78 } 79 80 repainter.repaintAfterLayout(); 81 setNeedsLayout(false); 82 } 83 84 bool RenderSVGContainer::selfWillPaint() 85 { 86 #if ENABLE(FILTERS) 87 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this); 88 return resources && resources->filter(); 89 #else 90 return false; 91 #endif 92 } 93 94 void RenderSVGContainer::paint(PaintInfo& paintInfo, int, int) 95 { 96 if (paintInfo.context->paintingDisabled()) 97 return; 98 99 // Spec: groups w/o children still may render filter content. 100 if (!firstChild() && !selfWillPaint()) 101 return; 102 103 FloatRect repaintRect = repaintRectInLocalCoordinates(); 104 if (!SVGRenderSupport::paintInfoIntersectsRepaintRect(repaintRect, localToParentTransform(), paintInfo)) 105 return; 106 107 PaintInfo childPaintInfo(paintInfo); 108 childPaintInfo.context->save(); 109 110 // Let the RenderSVGViewportContainer subclass clip if necessary 111 applyViewportClip(childPaintInfo); 112 113 childPaintInfo.applyTransform(localToParentTransform()); 114 115 bool continueRendering = true; 116 if (childPaintInfo.phase == PaintPhaseForeground) 117 continueRendering = SVGRenderSupport::prepareToRenderSVGContent(this, childPaintInfo); 118 119 if (continueRendering) { 120 childPaintInfo.updatePaintingRootForChildren(this); 121 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) 122 child->paint(childPaintInfo, 0, 0); 123 } 124 125 if (paintInfo.phase == PaintPhaseForeground) 126 SVGRenderSupport::finishRenderSVGContent(this, childPaintInfo, paintInfo.context); 127 128 childPaintInfo.context->restore(); 129 130 // FIXME: This really should be drawn from local coordinates, but currently we hack it 131 // to avoid our clip killing our outline rect. Thus we translate our 132 // outline rect into parent coords before drawing. 133 // FIXME: This means our focus ring won't share our rotation like it should. 134 // We should instead disable our clip during PaintPhaseOutline 135 if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth() && style()->visibility() == VISIBLE) { 136 IntRect paintRectInParent = enclosingIntRect(localToParentTransform().mapRect(repaintRect)); 137 paintOutline(paintInfo.context, paintRectInParent.x(), paintRectInParent.y(), paintRectInParent.width(), paintRectInParent.height()); 138 } 139 } 140 141 // addFocusRingRects is called from paintOutline and needs to be in the same coordinates as the paintOuline call 142 void RenderSVGContainer::addFocusRingRects(Vector<IntRect>& rects, int, int) 143 { 144 IntRect paintRectInParent = enclosingIntRect(localToParentTransform().mapRect(repaintRectInLocalCoordinates())); 145 if (!paintRectInParent.isEmpty()) 146 rects.append(paintRectInParent); 147 } 148 149 void RenderSVGContainer::updateCachedBoundaries() 150 { 151 m_objectBoundingBox = FloatRect(); 152 m_strokeBoundingBox = FloatRect(); 153 m_repaintBoundingBox = FloatRect(); 154 155 SVGRenderSupport::computeContainerBoundingBoxes(this, m_objectBoundingBox, m_strokeBoundingBox, m_repaintBoundingBox); 156 SVGRenderSupport::intersectRepaintRectWithResources(this, m_repaintBoundingBox); 157 } 158 159 bool RenderSVGContainer::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction) 160 { 161 // Give RenderSVGViewportContainer a chance to apply its viewport clip 162 if (!pointIsInsideViewportClip(pointInParent)) 163 return false; 164 165 FloatPoint localPoint = localToParentTransform().inverse().mapPoint(pointInParent); 166 167 if (!SVGRenderSupport::pointInClippingArea(this, localPoint)) 168 return false; 169 170 for (RenderObject* child = lastChild(); child; child = child->previousSibling()) { 171 if (child->nodeAtFloatPoint(request, result, localPoint, hitTestAction)) { 172 updateHitTestResult(result, roundedIntPoint(localPoint)); 173 return true; 174 } 175 } 176 177 // Spec: Only graphical elements can be targeted by the mouse, period. 178 // 16.4: "If there are no graphics elements whose relevant graphics content is under the pointer (i.e., there is no target element), the event is not dispatched." 179 return false; 180 } 181 182 } 183 184 #endif // ENABLE(SVG) 185