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