d6e2adc11274ee641e620ca54371f118dcc248f9
[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     insertChildNode(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     removeChildNode(oldChild);
65 }
66
67 void RenderSVGContainer::destroy()
68 {
69     children()->destroyLeftoverChildren();
70     RenderObject::destroy();
71 }
72
73 RenderObject* RenderSVGContainer::removeChildNode(RenderObject* oldChild, bool fullRemove)
74 {
75     ASSERT(oldChild->parent() == this);
76
77     // So that we'll get the appropriate dirty bit set (either that a normal flow child got yanked or
78     // that a positioned child got yanked).  We also repaint, so that the area exposed when the child
79     // disappears gets repainted properly.
80     if (!documentBeingDestroyed() && fullRemove) {
81         oldChild->setNeedsLayoutAndPrefWidthsRecalc();
82         oldChild->repaint();
83     }
84
85     // If we have a line box wrapper, delete it.
86     oldChild->deleteLineBoxWrapper();
87
88     if (!documentBeingDestroyed() && fullRemove) {
89         // If oldChild is the start or end of the selection, then clear the selection to
90         // avoid problems of invalid pointers.
91         // FIXME: The SelectionController should be responsible for this when it
92         // is notified of DOM mutations.
93         if (oldChild->isSelectionBorder())
94             view()->clearSelection();
95     }
96
97     // remove the child
98     if (oldChild->previousSibling())
99         oldChild->previousSibling()->setNextSibling(oldChild->nextSibling());
100     if (oldChild->nextSibling())
101         oldChild->nextSibling()->setPreviousSibling(oldChild->previousSibling());
102
103     if (children()->firstChild() == oldChild)
104         children()->setFirstChild(oldChild->nextSibling());
105     if (children()->lastChild() == oldChild)
106         children()->setLastChild(oldChild->previousSibling());
107
108     oldChild->setPreviousSibling(0);
109     oldChild->setNextSibling(0);
110     oldChild->setParent(0);
111
112     if (AXObjectCache::accessibilityEnabled())
113         document()->axObjectCache()->childrenChanged(this);
114
115     return oldChild;
116 }
117
118 void RenderSVGContainer::appendChildNode(RenderObject* newChild, bool)
119 {
120     ASSERT(!newChild->parent());
121     ASSERT(newChild->element()->isSVGElement());
122
123     newChild->setParent(this);
124     RenderObject* lChild = children()->lastChild();
125
126     if (lChild) {
127         newChild->setPreviousSibling(lChild);
128         lChild->setNextSibling(newChild);
129     } else
130         children()->setFirstChild(newChild);
131
132     children()->setLastChild(newChild);
133
134     newChild->setNeedsLayoutAndPrefWidthsRecalc(); // Goes up the containing block hierarchy.
135     if (!normalChildNeedsLayout())
136         setChildNeedsLayout(true); // We may supply the static position for an absolute positioned child.
137
138     if (AXObjectCache::accessibilityEnabled())
139         document()->axObjectCache()->childrenChanged(this);
140 }
141
142 void RenderSVGContainer::insertChildNode(RenderObject* child, RenderObject* beforeChild, bool)
143 {
144     if (!beforeChild) {
145         appendChildNode(child);
146         return;
147     }
148
149     ASSERT(!child->parent());
150     ASSERT(beforeChild->parent() == this);
151     ASSERT(child->element()->isSVGElement());
152
153     if (beforeChild == children()->firstChild())
154         children()->setFirstChild(child);
155
156     RenderObject* prev = beforeChild->previousSibling();
157     child->setNextSibling(beforeChild);
158     beforeChild->setPreviousSibling(child);
159     if (prev)
160         prev->setNextSibling(child);
161     child->setPreviousSibling(prev);
162
163     child->setParent(this);
164
165     child->setNeedsLayoutAndPrefWidthsRecalc();
166     if (!normalChildNeedsLayout())
167         setChildNeedsLayout(true); // We may supply the static position for an absolute positioned child.
168
169     if (AXObjectCache::accessibilityEnabled())
170         document()->axObjectCache()->childrenChanged(this);
171 }
172
173 bool RenderSVGContainer::drawsContents() const
174 {
175     return m_drawsContents;
176 }
177
178 void RenderSVGContainer::setDrawsContents(bool drawsContents)
179 {
180     m_drawsContents = drawsContents;
181 }
182
183 TransformationMatrix RenderSVGContainer::localTransform() const
184 {
185     return m_localTransform;
186 }
187
188 int RenderSVGContainer::lineHeight(bool, bool) const
189 {
190     return height();
191 }
192
193 int RenderSVGContainer::baselinePosition(bool, bool) const
194 {
195     return height();
196 }
197
198 bool RenderSVGContainer::calculateLocalTransform()
199 {
200     // subclasses can override this to add transform support
201     return false;
202 }
203
204 void RenderSVGContainer::layout()
205 {
206     ASSERT(needsLayout());
207
208     // Arbitrary affine transforms are incompatible with LayoutState.
209     view()->disableLayoutState();
210
211     // FIXME: using m_absoluteBounds breaks if containerForRepaint() is not the root
212     LayoutRepainter repainter(*this, checkForRepaintDuringLayout() && selfWillPaint(), &m_absoluteBounds);
213     
214     calculateLocalTransform();
215
216     for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
217         // Only force our kids to layout if we're being asked to relayout as a result of a parent changing
218         // FIXME: We should be able to skip relayout of non-relative kids when only bounds size has changed
219         // that's a possible future optimization using LayoutState
220         // http://bugs.webkit.org/show_bug.cgi?id=15391
221         if (selfNeedsLayout())
222             child->setNeedsLayout(true);
223
224         child->layoutIfNeeded();
225         ASSERT(!child->needsLayout());
226     }
227
228     calcBounds();
229
230     repainter.repaintAfterLayout();
231
232     view()->enableLayoutState();
233     setNeedsLayout(false);
234 }
235
236 int RenderSVGContainer::calcReplacedWidth() const
237 {
238     switch (style()->width().type()) {
239     case Fixed:
240         return max(0, style()->width().value());
241     case Percent:
242     {
243         const int cw = containingBlockWidth();
244         return cw > 0 ? max(0, style()->width().calcMinValue(cw)) : 0;
245     }
246     default:
247         return 0;
248     }
249 }
250
251 int RenderSVGContainer::calcReplacedHeight() const
252 {
253     switch (style()->height().type()) {
254     case Fixed:
255         return max(0, style()->height().value());
256     case Percent:
257     {
258         RenderBlock* cb = containingBlock();
259         return style()->height().calcValue(cb->availableHeight());
260     }
261     default:
262         return 0;
263     }
264 }
265
266 void RenderSVGContainer::applyContentTransforms(PaintInfo& paintInfo)
267 {
268     if (!localTransform().isIdentity())
269         paintInfo.context->concatCTM(localTransform());
270 }
271
272 void RenderSVGContainer::applyAdditionalTransforms(PaintInfo&)
273 {
274     // no-op
275 }
276
277 void RenderSVGContainer::calcBounds()
278 {
279     m_width = calcReplacedWidth();
280     m_height = calcReplacedHeight();
281     m_absoluteBounds = absoluteClippedOverflowRect();
282 }
283
284 bool RenderSVGContainer::selfWillPaint() const
285 {
286 #if ENABLE(SVG_FILTERS)
287     const SVGRenderStyle* svgStyle = style()->svgStyle();
288     SVGResourceFilter* filter = getFilterById(document(), svgStyle->filter());
289     if (filter)
290         return true;
291 #endif
292     return false;
293 }
294
295 void RenderSVGContainer::paint(PaintInfo& paintInfo, int, int)
296 {
297     if (paintInfo.context->paintingDisabled() || !drawsContents())
298         return;
299
300      // Spec: groups w/o children still may render filter content.
301     if (!firstChild() && !selfWillPaint())
302         return;
303     
304     paintInfo.context->save();
305     applyContentTransforms(paintInfo);
306
307     SVGResourceFilter* filter = 0;
308     PaintInfo savedInfo(paintInfo);
309
310     FloatRect boundingBox = relativeBBox(true);
311     if (paintInfo.phase == PaintPhaseForeground)
312         prepareToRenderSVGContent(this, paintInfo, boundingBox, filter); 
313
314     applyAdditionalTransforms(paintInfo);
315
316     // default implementation. Just pass paint through to the children
317     PaintInfo childInfo(paintInfo);
318     childInfo.paintingRoot = paintingRootForChildren(paintInfo);
319     for (RenderObject* child = firstChild(); child; child = child->nextSibling())
320         child->paint(childInfo, 0, 0);
321
322     if (paintInfo.phase == PaintPhaseForeground)
323         finishRenderSVGContent(this, paintInfo, boundingBox, filter, savedInfo.context);
324
325     paintInfo.context->restore();
326     
327     if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth() && style()->visibility() == VISIBLE)
328         paintOutline(paintInfo.context, m_absoluteBounds.x(), m_absoluteBounds.y(), m_absoluteBounds.width(), m_absoluteBounds.height(), style());
329 }
330
331 TransformationMatrix RenderSVGContainer::viewportTransform() const
332 {
333      return TransformationMatrix();
334 }
335
336 IntRect RenderSVGContainer::clippedOverflowRectForRepaint(RenderBox* repaintContainer)
337 {
338     FloatRect repaintRect;
339
340     for (RenderObject* current = firstChild(); current != 0; current = current->nextSibling())
341         repaintRect.unite(current->clippedOverflowRectForRepaint(repaintContainer));
342
343 #if ENABLE(SVG_FILTERS)
344     // Filters can expand the bounding box
345     SVGResourceFilter* filter = getFilterById(document(), style()->svgStyle()->filter());
346     if (filter)
347         repaintRect.unite(filter->filterBBoxForItemBBox(repaintRect));
348 #endif
349
350     if (!repaintRect.isEmpty())
351         repaintRect.inflate(1); // inflate 1 pixel for antialiasing
352
353     return enclosingIntRect(repaintRect);
354 }
355
356 void RenderSVGContainer::addFocusRingRects(GraphicsContext* graphicsContext, int, int)
357 {
358     graphicsContext->addFocusRingRect(m_absoluteBounds);
359 }
360
361 void RenderSVGContainer::absoluteRects(Vector<IntRect>& rects, int, int, bool)
362 {
363     rects.append(absoluteClippedOverflowRect());
364 }
365
366 void RenderSVGContainer::absoluteQuads(Vector<FloatQuad>& quads, bool)
367 {
368     quads.append(absoluteClippedOverflowRect());
369 }
370
371 FloatRect RenderSVGContainer::relativeBBox(bool includeStroke) const
372 {
373     FloatRect rect;
374     
375     RenderObject* current = firstChild();
376     for (; current != 0; current = current->nextSibling()) {
377         FloatRect childBBox = current->relativeBBox(includeStroke);
378         FloatRect mappedBBox = current->localTransform().mapRect(childBBox);
379
380         // <svg> can have a viewBox contributing to the bbox
381         if (current->isSVGContainer())
382             mappedBBox = static_cast<RenderSVGContainer*>(current)->viewportTransform().mapRect(mappedBBox);
383
384         rect.unite(mappedBBox);
385     }
386
387     return rect;
388 }
389
390 bool RenderSVGContainer::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction)
391 {
392     for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
393         if (child->nodeAtPoint(request, result, _x, _y, _tx, _ty, hitTestAction)) {
394             updateHitTestResult(result, IntPoint(_x - _tx, _y - _ty));
395             return true;
396         }
397     }
398
399     // Spec: Only graphical elements can be targeted by the mouse, period.
400     // 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."
401     return false;
402 }
403
404 IntRect RenderSVGContainer::outlineBoundsForRepaint(RenderBox* /*repaintContainer*/) const
405 {
406     // FIXME: handle non-root repaintContainer
407     IntRect result = m_absoluteBounds;
408     adjustRectForOutlineAndShadow(result);
409     return result;
410 }
411
412 }
413
414 #endif // ENABLE(SVG)
415
416 // vim:ts=4:noet