2009-02-02 Sam Weinig <sam@webkit.org>
[WebKit-https.git] / WebCore / rendering / RenderSVGContainer.cpp
1 /*
2     Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org>
3                   2004, 2005, 2007, 2008 Rob Buis <buis@kde.org>
4                   2007 Eric Seidel <eric@webkit.org>
5
6     This file is part of the KDE project
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     aint 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 "AXObjectCache.h"
30 #include "FloatQuad.h"
31 #include "GraphicsContext.h"
32 #include "RenderView.h"
33 #include "SVGRenderSupport.h"
34 #include "SVGResourceFilter.h"
35 #include "SVGStyledElement.h"
36 #include "SVGURIReference.h"
37
38 namespace WebCore {
39
40 RenderSVGContainer::RenderSVGContainer(SVGStyledElement* node)
41     : RenderObject(node)
42     , m_width(0)
43     , m_height(0)
44     , m_drawsContents(true)
45 {
46 }
47
48 RenderSVGContainer::~RenderSVGContainer()
49 {
50 }
51
52 void RenderSVGContainer::addChild(RenderObject* newChild, RenderObject* beforeChild)
53 {
54     children()->insertChildNode(this, newChild, beforeChild);
55 }
56
57 void RenderSVGContainer::removeChild(RenderObject* oldChild)
58 {
59     // We do this here instead of in removeChildNode, since the only extremely low-level uses of remove/appendChildNode
60     // cannot affect the positioned object list, and the floating object list is irrelevant (since the list gets cleared on
61     // layout anyway).
62     oldChild->removeFromObjectLists();
63
64     children()->removeChildNode(this, oldChild);
65 }
66
67 void RenderSVGContainer::destroy()
68 {
69     children()->destroyLeftoverChildren();
70     RenderObject::destroy();
71 }
72
73 bool RenderSVGContainer::drawsContents() const
74 {
75     return m_drawsContents;
76 }
77
78 void RenderSVGContainer::setDrawsContents(bool drawsContents)
79 {
80     m_drawsContents = drawsContents;
81 }
82
83 TransformationMatrix RenderSVGContainer::localTransform() const
84 {
85     return m_localTransform;
86 }
87
88 int RenderSVGContainer::lineHeight(bool, bool) const
89 {
90     return height();
91 }
92
93 int RenderSVGContainer::baselinePosition(bool, bool) const
94 {
95     return height();
96 }
97
98 bool RenderSVGContainer::calculateLocalTransform()
99 {
100     // subclasses can override this to add transform support
101     return false;
102 }
103
104 void RenderSVGContainer::layout()
105 {
106     ASSERT(needsLayout());
107
108     // Arbitrary affine transforms are incompatible with LayoutState.
109     view()->disableLayoutState();
110
111     // FIXME: using m_absoluteBounds breaks if containerForRepaint() is not the root
112     LayoutRepainter repainter(*this, checkForRepaintDuringLayout() && selfWillPaint(), &m_absoluteBounds);
113     
114     calculateLocalTransform();
115
116     for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
117         // Only force our kids to layout if we're being asked to relayout as a result of a parent changing
118         // FIXME: We should be able to skip relayout of non-relative kids when only bounds size has changed
119         // that's a possible future optimization using LayoutState
120         // http://bugs.webkit.org/show_bug.cgi?id=15391
121         if (selfNeedsLayout())
122             child->setNeedsLayout(true);
123
124         child->layoutIfNeeded();
125         ASSERT(!child->needsLayout());
126     }
127
128     calcBounds();
129
130     repainter.repaintAfterLayout();
131
132     view()->enableLayoutState();
133     setNeedsLayout(false);
134 }
135
136 int RenderSVGContainer::calcReplacedWidth() const
137 {
138     switch (style()->width().type()) {
139     case Fixed:
140         return max(0, style()->width().value());
141     case Percent:
142     {
143         const int cw = containingBlockWidth();
144         return cw > 0 ? max(0, style()->width().calcMinValue(cw)) : 0;
145     }
146     default:
147         return 0;
148     }
149 }
150
151 int RenderSVGContainer::calcReplacedHeight() const
152 {
153     switch (style()->height().type()) {
154     case Fixed:
155         return max(0, style()->height().value());
156     case Percent:
157     {
158         RenderBlock* cb = containingBlock();
159         return style()->height().calcValue(cb->availableHeight());
160     }
161     default:
162         return 0;
163     }
164 }
165
166 void RenderSVGContainer::applyContentTransforms(PaintInfo& paintInfo)
167 {
168     if (!localTransform().isIdentity())
169         paintInfo.context->concatCTM(localTransform());
170 }
171
172 void RenderSVGContainer::applyAdditionalTransforms(PaintInfo&)
173 {
174     // no-op
175 }
176
177 void RenderSVGContainer::calcBounds()
178 {
179     m_width = calcReplacedWidth();
180     m_height = calcReplacedHeight();
181     m_absoluteBounds = absoluteClippedOverflowRect();
182 }
183
184 bool RenderSVGContainer::selfWillPaint() const
185 {
186 #if ENABLE(SVG_FILTERS)
187     const SVGRenderStyle* svgStyle = style()->svgStyle();
188     SVGResourceFilter* filter = getFilterById(document(), svgStyle->filter());
189     if (filter)
190         return true;
191 #endif
192     return false;
193 }
194
195 void RenderSVGContainer::paint(PaintInfo& paintInfo, int, int)
196 {
197     if (paintInfo.context->paintingDisabled() || !drawsContents())
198         return;
199
200      // Spec: groups w/o children still may render filter content.
201     if (!firstChild() && !selfWillPaint())
202         return;
203     
204     paintInfo.context->save();
205     applyContentTransforms(paintInfo);
206
207     SVGResourceFilter* filter = 0;
208     PaintInfo savedInfo(paintInfo);
209
210     FloatRect boundingBox = relativeBBox(true);
211     if (paintInfo.phase == PaintPhaseForeground)
212         prepareToRenderSVGContent(this, paintInfo, boundingBox, filter); 
213
214     applyAdditionalTransforms(paintInfo);
215
216     // default implementation. Just pass paint through to the children
217     PaintInfo childInfo(paintInfo);
218     childInfo.paintingRoot = paintingRootForChildren(paintInfo);
219     for (RenderObject* child = firstChild(); child; child = child->nextSibling())
220         child->paint(childInfo, 0, 0);
221
222     if (paintInfo.phase == PaintPhaseForeground)
223         finishRenderSVGContent(this, paintInfo, boundingBox, filter, savedInfo.context);
224
225     paintInfo.context->restore();
226     
227     if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth() && style()->visibility() == VISIBLE)
228         paintOutline(paintInfo.context, m_absoluteBounds.x(), m_absoluteBounds.y(), m_absoluteBounds.width(), m_absoluteBounds.height(), style());
229 }
230
231 TransformationMatrix RenderSVGContainer::viewportTransform() const
232 {
233      return TransformationMatrix();
234 }
235
236 IntRect RenderSVGContainer::clippedOverflowRectForRepaint(RenderBox* repaintContainer)
237 {
238     FloatRect repaintRect;
239
240     for (RenderObject* current = firstChild(); current != 0; current = current->nextSibling())
241         repaintRect.unite(current->clippedOverflowRectForRepaint(repaintContainer));
242
243 #if ENABLE(SVG_FILTERS)
244     // Filters can expand the bounding box
245     SVGResourceFilter* filter = getFilterById(document(), style()->svgStyle()->filter());
246     if (filter)
247         repaintRect.unite(filter->filterBBoxForItemBBox(repaintRect));
248 #endif
249
250     if (!repaintRect.isEmpty())
251         repaintRect.inflate(1); // inflate 1 pixel for antialiasing
252
253     return enclosingIntRect(repaintRect);
254 }
255
256 void RenderSVGContainer::addFocusRingRects(GraphicsContext* graphicsContext, int, int)
257 {
258     graphicsContext->addFocusRingRect(m_absoluteBounds);
259 }
260
261 void RenderSVGContainer::absoluteRects(Vector<IntRect>& rects, int, int, bool)
262 {
263     rects.append(absoluteClippedOverflowRect());
264 }
265
266 void RenderSVGContainer::absoluteQuads(Vector<FloatQuad>& quads, bool)
267 {
268     quads.append(absoluteClippedOverflowRect());
269 }
270
271 FloatRect RenderSVGContainer::relativeBBox(bool includeStroke) const
272 {
273     FloatRect rect;
274     
275     RenderObject* current = firstChild();
276     for (; current != 0; current = current->nextSibling()) {
277         FloatRect childBBox = current->relativeBBox(includeStroke);
278         FloatRect mappedBBox = current->localTransform().mapRect(childBBox);
279
280         // <svg> can have a viewBox contributing to the bbox
281         if (current->isSVGContainer())
282             mappedBBox = static_cast<RenderSVGContainer*>(current)->viewportTransform().mapRect(mappedBBox);
283
284         rect.unite(mappedBBox);
285     }
286
287     return rect;
288 }
289
290 bool RenderSVGContainer::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction)
291 {
292     for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
293         if (child->nodeAtPoint(request, result, _x, _y, _tx, _ty, hitTestAction)) {
294             updateHitTestResult(result, IntPoint(_x - _tx, _y - _ty));
295             return true;
296         }
297     }
298
299     // Spec: Only graphical elements can be targeted by the mouse, period.
300     // 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."
301     return false;
302 }
303
304 IntRect RenderSVGContainer::outlineBoundsForRepaint(RenderBox* /*repaintContainer*/) const
305 {
306     // FIXME: handle non-root repaintContainer
307     IntRect result = m_absoluteBounds;
308     adjustRectForOutlineAndShadow(result);
309     return result;
310 }
311
312 }
313
314 #endif // ENABLE(SVG)
315
316 // vim:ts=4:noet