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