• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2     Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org>
3                   2004, 2005, 2007, 2008, 2009 Rob Buis <buis@kde.org>
4                   2007 Eric Seidel <eric@webkit.org>
5                   2009 Google, Inc.
6 
7     This library is free software; you can redistribute it and/or
8     modify it under the terms of the GNU Library General Public
9     License as published by the Free Software Foundation; either
10     version 2 of the License, or (at your option) any later version.
11 
12     This library is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15     Library General Public License for more details.
16 
17     You should have received a copy of the GNU Library General Public License
18     aint with this library; see the file COPYING.LIB.  If not, write to
19     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20     Boston, MA 02110-1301, USA.
21 */
22 
23 #include "config.h"
24 
25 #if ENABLE(SVG)
26 #include "RenderSVGRoot.h"
27 
28 #include "GraphicsContext.h"
29 #include "RenderSVGContainer.h"
30 #include "RenderView.h"
31 #include "SVGLength.h"
32 #include "SVGRenderSupport.h"
33 #include "SVGSVGElement.h"
34 #include "SVGStyledElement.h"
35 #include "TransformState.h"
36 
37 #if ENABLE(FILTERS)
38 #include "SVGResourceFilter.h"
39 #endif
40 
41 using namespace std;
42 
43 namespace WebCore {
44 
RenderSVGRoot(SVGStyledElement * node)45 RenderSVGRoot::RenderSVGRoot(SVGStyledElement* node)
46     : RenderBox(node)
47 {
48     setReplaced(true);
49 }
50 
lineHeight(bool,bool) const51 int RenderSVGRoot::lineHeight(bool, bool) const
52 {
53     return height() + marginTop() + marginBottom();
54 }
55 
baselinePosition(bool,bool) const56 int RenderSVGRoot::baselinePosition(bool, bool) const
57 {
58     return height() + marginTop() + marginBottom();
59 }
60 
calcPrefWidths()61 void RenderSVGRoot::calcPrefWidths()
62 {
63     ASSERT(prefWidthsDirty());
64 
65     int paddingAndBorders = paddingLeft() + paddingRight() + borderLeft() + borderRight();
66     int width = calcReplacedWidth(false) + paddingAndBorders;
67 
68     if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength)
69         width = min(width, style()->maxWidth().value() + (style()->boxSizing() == CONTENT_BOX ? paddingAndBorders : 0));
70 
71     if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent())) {
72         m_minPrefWidth = 0;
73         m_maxPrefWidth = width;
74     } else
75         m_minPrefWidth = m_maxPrefWidth = width;
76 
77     setPrefWidthsDirty(false);
78 }
79 
layout()80 void RenderSVGRoot::layout()
81 {
82     ASSERT(needsLayout());
83 
84     // Arbitrary affine transforms are incompatible with LayoutState.
85     view()->disableLayoutState();
86 
87     LayoutRepainter repainter(*this, checkForRepaintDuringLayout() && selfNeedsLayout());
88 
89     int oldWidth = width();
90     calcWidth();
91 
92     int oldHeight = height();
93     calcHeight();
94 
95     SVGSVGElement* svg = static_cast<SVGSVGElement*>(node());
96     setWidth(static_cast<int>(width() * svg->currentScale()));
97     setHeight(static_cast<int>(height() * svg->currentScale()));
98     calcViewport();
99 
100     // RenderSVGRoot needs to take special care to propagate window size changes to the children,
101     // if the outermost <svg> is using relative x/y/width/height values. Hence the additonal parameters.
102     layoutChildren(this, selfNeedsLayout() || (svg->hasRelativeValues() && (width() != oldWidth || height() != oldHeight)));
103     repainter.repaintAfterLayout();
104 
105     view()->enableLayoutState();
106     setNeedsLayout(false);
107 }
108 
selfWillPaint() const109 bool RenderSVGRoot::selfWillPaint() const
110 {
111 #if ENABLE(FILTERS)
112     const SVGRenderStyle* svgStyle = style()->svgStyle();
113     SVGResourceFilter* filter = getFilterById(document(), svgStyle->filter(), this);
114     if (filter)
115         return true;
116 #endif
117     return false;
118 }
119 
paint(PaintInfo & paintInfo,int parentX,int parentY)120 void RenderSVGRoot::paint(PaintInfo& paintInfo, int parentX, int parentY)
121 {
122     if (paintInfo.context->paintingDisabled())
123         return;
124 
125     IntPoint parentOriginInContainer(parentX, parentY);
126     IntPoint borderBoxOriginInContainer = parentOriginInContainer + IntSize(x(), y());
127 
128     if (hasBoxDecorations() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection))
129         paintBoxDecorations(paintInfo, borderBoxOriginInContainer.x(), borderBoxOriginInContainer.y());
130 
131     // An empty viewport disables rendering.  FIXME: Should we still render filters?
132     if (m_viewportSize.isEmpty())
133         return;
134 
135     // Don't paint if we don't have kids, except if we have filters we should paint those.
136     if (!firstChild() && !selfWillPaint())
137         return;
138 
139     // Make a copy of the PaintInfo because applyTransformToPaintInfo will modify the damage rect.
140     RenderObject::PaintInfo childPaintInfo(paintInfo);
141     childPaintInfo.context->save();
142 
143     // Apply initial viewport clip - not affected by overflow handling
144     childPaintInfo.context->clip(overflowClipRect(borderBoxOriginInContainer.x(), borderBoxOriginInContainer.y()));
145 
146     // Convert from container offsets (html renderers) to a relative transform (svg renderers).
147     // Transform from our paint container's coordinate system to our local coords.
148     applyTransformToPaintInfo(childPaintInfo, localToRepaintContainerTransform(parentOriginInContainer));
149 
150     SVGResourceFilter* filter = 0;
151     FloatRect boundingBox = repaintRectInLocalCoordinates();
152 
153     bool continueRendering = true;
154     if (childPaintInfo.phase == PaintPhaseForeground)
155         continueRendering = prepareToRenderSVGContent(this, childPaintInfo, boundingBox, filter);
156 
157     if (continueRendering)
158         RenderBox::paint(childPaintInfo, 0, 0);
159 
160     if (childPaintInfo.phase == PaintPhaseForeground)
161         finishRenderSVGContent(this, childPaintInfo, filter, paintInfo.context);
162 
163     childPaintInfo.context->restore();
164 
165     if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth() && style()->visibility() == VISIBLE)
166         paintOutline(paintInfo.context, borderBoxOriginInContainer.x(), borderBoxOriginInContainer.y(), width(), height(), style());
167 }
168 
calcViewport()169 void RenderSVGRoot::calcViewport()
170 {
171     SVGSVGElement* svg = static_cast<SVGSVGElement*>(node());
172 
173     if (!selfNeedsLayout() && !svg->hasRelativeValues())
174         return;
175 
176     if (!svg->hasSetContainerSize()) {
177         // In the normal case of <svg> being stand-alone or in a CSSBoxModel object we use
178         // RenderBox::width()/height() (which pulls data from RenderStyle)
179         m_viewportSize = FloatSize(width(), height());
180     } else {
181         // In the SVGImage case grab the SVGLength values off of SVGSVGElement and use
182         // the special relativeWidthValue accessors which respect the specified containerSize
183         SVGLength width = svg->width();
184         SVGLength height = svg->height();
185         float viewportWidth = (width.unitType() == LengthTypePercentage) ? svg->relativeWidthValue() : width.value(svg);
186         float viewportHeight = (height.unitType() == LengthTypePercentage) ? svg->relativeHeightValue() : height.value(svg);
187         m_viewportSize = FloatSize(viewportWidth, viewportHeight);
188     }
189 }
190 
191 // RenderBox methods will expect coordinates w/o any transforms in coordinates
192 // relative to our borderBox origin.  This method gives us exactly that.
localToBorderBoxTransform() const193 AffineTransform RenderSVGRoot::localToBorderBoxTransform() const
194 {
195     IntSize borderAndPadding = borderOriginToContentBox();
196     SVGSVGElement* svg = static_cast<SVGSVGElement*>(node());
197     float scale = svg->currentScale();
198     AffineTransform ctm(scale, 0, 0, scale, borderAndPadding.width(), borderAndPadding.height());
199     ctm.translate(svg->currentTranslate().x(), svg->currentTranslate().y());
200     return svg->viewBoxToViewTransform(width(), height()) * ctm;
201 }
202 
parentOriginToBorderBox() const203 IntSize RenderSVGRoot::parentOriginToBorderBox() const
204 {
205     return IntSize(x(), y());
206 }
207 
borderOriginToContentBox() const208 IntSize RenderSVGRoot::borderOriginToContentBox() const
209 {
210     return IntSize(borderLeft() + paddingLeft(), borderTop() + paddingTop());
211 }
212 
localToRepaintContainerTransform(const IntPoint & parentOriginInContainer) const213 AffineTransform RenderSVGRoot::localToRepaintContainerTransform(const IntPoint& parentOriginInContainer) const
214 {
215     AffineTransform parentToContainer(localToParentTransform());
216     return parentToContainer.translateRight(parentOriginInContainer.x(), parentOriginInContainer.y());
217 }
218 
localToParentTransform() const219 const AffineTransform& RenderSVGRoot::localToParentTransform() const
220 {
221     IntSize parentToBorderBoxOffset = parentOriginToBorderBox();
222 
223     AffineTransform borderBoxOriginToParentOrigin(localToBorderBoxTransform());
224     borderBoxOriginToParentOrigin.translateRight(parentToBorderBoxOffset.width(), parentToBorderBoxOffset.height());
225 
226     m_localToParentTransform = borderBoxOriginToParentOrigin;
227     return m_localToParentTransform;
228 }
229 
objectBoundingBox() const230 FloatRect RenderSVGRoot::objectBoundingBox() const
231 {
232     return computeContainerBoundingBox(this, false);
233 }
234 
repaintRectInLocalCoordinates() const235 FloatRect RenderSVGRoot::repaintRectInLocalCoordinates() const
236 {
237     // FIXME: This does not include the border but it should!
238     FloatRect repaintRect = computeContainerBoundingBox(this, true);
239     style()->svgStyle()->inflateForShadow(repaintRect);
240     return repaintRect;
241 }
242 
localTransform() const243 AffineTransform RenderSVGRoot::localTransform() const
244 {
245     return AffineTransform();
246 }
247 
computeRectForRepaint(RenderBoxModelObject * repaintContainer,IntRect & repaintRect,bool fixed)248 void RenderSVGRoot::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& repaintRect, bool fixed)
249 {
250     // Apply our local transforms (except for x/y translation), then our shadow,
251     // and then call RenderBox's method to handle all the normal CSS Box model bits
252     repaintRect = localToBorderBoxTransform().mapRect(repaintRect);
253 
254     // Apply initial viewport clip - not affected by overflow settings
255     repaintRect.intersect(enclosingIntRect(FloatRect(FloatPoint(), m_viewportSize)));
256 
257     style()->svgStyle()->inflateForShadow(repaintRect);
258     RenderBox::computeRectForRepaint(repaintContainer, repaintRect, fixed);
259 }
260 
mapLocalToContainer(RenderBoxModelObject * repaintContainer,bool fixed,bool useTransforms,TransformState & transformState) const261 void RenderSVGRoot::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed , bool useTransforms, TransformState& transformState) const
262 {
263     ASSERT(!fixed); // We should have no fixed content in the SVG rendering tree.
264     ASSERT(useTransforms); // mapping a point through SVG w/o respecting trasnforms is useless.
265 
266     // Transform to our border box and let RenderBox transform the rest of the way.
267     transformState.applyTransform(localToBorderBoxTransform());
268     RenderBox::mapLocalToContainer(repaintContainer, fixed, useTransforms, transformState);
269 }
270 
nodeAtPoint(const HitTestRequest & request,HitTestResult & result,int _x,int _y,int _tx,int _ty,HitTestAction hitTestAction)271 bool RenderSVGRoot::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction)
272 {
273     IntPoint pointInContainer(_x, _y);
274     IntSize containerToParentOffset(_tx, _ty);
275 
276     IntPoint pointInParent = pointInContainer - containerToParentOffset;
277     IntPoint pointInBorderBox = pointInParent - parentOriginToBorderBox();
278 
279     // Note: For now, we're ignoring hits to border and padding for <svg>
280     IntPoint pointInContentBox = pointInBorderBox - borderOriginToContentBox();
281     if (!contentBoxRect().contains(pointInContentBox))
282         return false;
283 
284     IntPoint localPoint = localToParentTransform().inverse().mapPoint(pointInParent);
285 
286     for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
287         if (child->nodeAtFloatPoint(request, result, localPoint, hitTestAction)) {
288             // FIXME: CSS/HTML assumes the local point is relative to the border box, right?
289             updateHitTestResult(result, pointInBorderBox);
290             return true;
291         }
292     }
293 
294     // Spec: Only graphical elements can be targeted by the mouse, so we don't check self here.
295     // 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."
296     return false;
297 }
298 
299 }
300 
301 #endif // ENABLE(SVG)
302