2010-08-30 Eric Seidel <eric@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     Copyright (C) 2009 Google, Inc.  All rights reserved.
6                   2009 Dirk Schulze <krit@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     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 "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_drawsContents(true)
42 {
43 }
44
45 void RenderSVGContainer::layout()
46 {
47     ASSERT(needsLayout());
48
49     // RenderSVGRoot disables layoutState for the SVG rendering tree.
50     ASSERT(!view()->layoutStateEnabled());
51
52     // Allow RenderSVGViewportContainer to update its viewport.
53     calcViewport();
54
55     LayoutRepainter repainter(*this, m_everHadLayout && checkForRepaintDuringLayout());
56
57     // Allow RenderSVGTransformableContainer to update its transform.
58     calculateLocalTransform();
59
60     SVGRenderSupport::layoutChildren(this, selfNeedsLayout());
61
62     // Invalidate all resources of this client if our layout changed.
63     if (m_everHadLayout && selfNeedsLayout())
64         SVGResourcesCache::clientLayoutChanged(this);
65
66     repainter.repaintAfterLayout();
67     setNeedsLayout(false);
68 }
69
70 bool RenderSVGContainer::selfWillPaint()
71 {
72 #if ENABLE(FILTERS)
73     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this);
74     return resources && resources->filter();
75 #else
76     return false;
77 #endif
78 }
79
80 void RenderSVGContainer::paint(PaintInfo& paintInfo, int, int)
81 {
82     if (paintInfo.context->paintingDisabled() || !drawsContents())
83         return;
84
85     // Spec: groups w/o children still may render filter content.
86     if (!firstChild() && !selfWillPaint())
87         return;
88
89     PaintInfo childPaintInfo(paintInfo);
90     childPaintInfo.context->save();
91
92     // Let the RenderSVGViewportContainer subclass clip if necessary
93     applyViewportClip(childPaintInfo);
94
95     childPaintInfo.applyTransform(localToParentTransform());
96
97     bool continueRendering = true;
98     if (childPaintInfo.phase == PaintPhaseForeground)
99         continueRendering = SVGRenderSupport::prepareToRenderSVGContent(this, childPaintInfo);
100
101     if (continueRendering) {
102         childPaintInfo.updatePaintingRootForChildren(this);
103         for (RenderObject* child = firstChild(); child; child = child->nextSibling())
104             child->paint(childPaintInfo, 0, 0);
105     }
106
107     if (paintInfo.phase == PaintPhaseForeground)
108         SVGRenderSupport::finishRenderSVGContent(this, childPaintInfo, paintInfo.context);
109
110     childPaintInfo.context->restore();
111
112     // FIXME: This really should be drawn from local coordinates, but currently we hack it
113     // to avoid our clip killing our outline rect.  Thus we translate our
114     // outline rect into parent coords before drawing.
115     // FIXME: This means our focus ring won't share our rotation like it should.
116     // We should instead disable our clip during PaintPhaseOutline
117     if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth() && style()->visibility() == VISIBLE) {
118         IntRect paintRectInParent = enclosingIntRect(localToParentTransform().mapRect(repaintRectInLocalCoordinates()));
119         paintOutline(paintInfo.context, paintRectInParent.x(), paintRectInParent.y(), paintRectInParent.width(), paintRectInParent.height());
120     }
121 }
122
123 // addFocusRingRects is called from paintOutline and needs to be in the same coordinates as the paintOuline call
124 void RenderSVGContainer::addFocusRingRects(Vector<IntRect>& rects, int, int)
125 {
126     IntRect paintRectInParent = enclosingIntRect(localToParentTransform().mapRect(repaintRectInLocalCoordinates()));
127     if (!paintRectInParent.isEmpty())
128         rects.append(paintRectInParent);
129 }
130
131 FloatRect RenderSVGContainer::objectBoundingBox() const
132 {
133     return SVGRenderSupport::computeContainerBoundingBox(this, SVGRenderSupport::ObjectBoundingBox);
134 }
135
136 FloatRect RenderSVGContainer::strokeBoundingBox() const
137 {
138     return SVGRenderSupport::computeContainerBoundingBox(this, SVGRenderSupport::StrokeBoundingBox);
139 }
140
141 FloatRect RenderSVGContainer::repaintRectInLocalCoordinates() const
142 {
143     FloatRect repaintRect = SVGRenderSupport::computeContainerBoundingBox(this, SVGRenderSupport::RepaintBoundingBox);
144     SVGRenderSupport::intersectRepaintRectWithResources(this, repaintRect);
145     return repaintRect;
146 }
147
148 bool RenderSVGContainer::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
149 {
150     // Give RenderSVGViewportContainer a chance to apply its viewport clip
151     if (!pointIsInsideViewportClip(pointInParent))
152         return false;
153
154     FloatPoint localPoint = localToParentTransform().inverse().mapPoint(pointInParent);
155
156     if (!SVGRenderSupport::pointInClippingArea(this, localPoint))
157         return false;
158                 
159     for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
160         if (child->nodeAtFloatPoint(request, result, localPoint, hitTestAction)) {
161             updateHitTestResult(result, roundedIntPoint(localPoint));
162             return true;
163         }
164     }
165
166     // Spec: Only graphical elements can be targeted by the mouse, period.
167     // 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."
168     return false;
169 }
170
171 }
172
173 #endif // ENABLE(SVG)