|
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 "HitTestResult.h" |
|
30 #include "RenderSVGContainer.h" |
|
31 #include "RenderSVGResource.h" |
|
32 #include "RenderView.h" |
|
33 #include "SVGLength.h" |
|
34 #include "SVGRenderSupport.h" |
|
35 #include "SVGSVGElement.h" |
|
36 #include "SVGStyledElement.h" |
|
37 #include "TransformState.h" |
|
38 |
|
39 #if ENABLE(FILTERS) |
|
40 #include "RenderSVGResourceFilter.h" |
|
41 #endif |
|
42 |
|
43 using namespace std; |
|
44 |
|
45 namespace WebCore { |
|
46 |
|
47 RenderSVGRoot::RenderSVGRoot(SVGStyledElement* node) |
|
48 : RenderBox(node) |
|
49 , m_isLayoutSizeChanged(false) |
|
50 { |
|
51 setReplaced(true); |
|
52 } |
|
53 |
|
54 int RenderSVGRoot::lineHeight(bool, bool) const |
|
55 { |
|
56 return height() + marginTop() + marginBottom(); |
|
57 } |
|
58 |
|
59 int RenderSVGRoot::baselinePosition(bool, bool) const |
|
60 { |
|
61 return height() + marginTop() + marginBottom(); |
|
62 } |
|
63 |
|
64 void RenderSVGRoot::calcPrefWidths() |
|
65 { |
|
66 ASSERT(prefWidthsDirty()); |
|
67 |
|
68 int borderAndPadding = borderAndPaddingWidth(); |
|
69 int width = calcReplacedWidth(false) + borderAndPadding; |
|
70 |
|
71 if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) |
|
72 width = min(width, style()->maxWidth().value() + (style()->boxSizing() == CONTENT_BOX ? borderAndPadding : 0)); |
|
73 |
|
74 if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent())) { |
|
75 m_minPrefWidth = 0; |
|
76 m_maxPrefWidth = width; |
|
77 } else |
|
78 m_minPrefWidth = m_maxPrefWidth = width; |
|
79 |
|
80 setPrefWidthsDirty(false); |
|
81 } |
|
82 |
|
83 int RenderSVGRoot::calcReplacedWidth(bool includeMaxWidth) const |
|
84 { |
|
85 int replacedWidth = RenderBox::calcReplacedWidth(includeMaxWidth); |
|
86 if (!style()->width().isPercent()) |
|
87 return replacedWidth; |
|
88 |
|
89 // FIXME: Investigate in size rounding issues |
|
90 SVGSVGElement* svg = static_cast<SVGSVGElement*>(node()); |
|
91 return static_cast<int>(roundf(replacedWidth * svg->currentScale())); |
|
92 } |
|
93 |
|
94 int RenderSVGRoot::calcReplacedHeight() const |
|
95 { |
|
96 int replacedHeight = RenderBox::calcReplacedHeight(); |
|
97 if (!style()->height().isPercent()) |
|
98 return replacedHeight; |
|
99 |
|
100 // FIXME: Investigate in size rounding issues |
|
101 SVGSVGElement* svg = static_cast<SVGSVGElement*>(node()); |
|
102 return static_cast<int>(roundf(replacedHeight * svg->currentScale())); |
|
103 } |
|
104 |
|
105 void RenderSVGRoot::layout() |
|
106 { |
|
107 ASSERT(needsLayout()); |
|
108 |
|
109 // Arbitrary affine transforms are incompatible with LayoutState. |
|
110 view()->disableLayoutState(); |
|
111 |
|
112 bool needsLayout = selfNeedsLayout(); |
|
113 LayoutRepainter repainter(*this, needsLayout && m_everHadLayout && checkForRepaintDuringLayout()); |
|
114 |
|
115 IntSize oldSize(width(), height()); |
|
116 calcWidth(); |
|
117 calcHeight(); |
|
118 calcViewport(); |
|
119 |
|
120 SVGSVGElement* svg = static_cast<SVGSVGElement*>(node()); |
|
121 m_isLayoutSizeChanged = svg->hasRelativeLengths() && oldSize != size(); |
|
122 |
|
123 SVGRenderSupport::layoutChildren(this, needsLayout); |
|
124 m_isLayoutSizeChanged = false; |
|
125 |
|
126 repainter.repaintAfterLayout(); |
|
127 |
|
128 view()->enableLayoutState(); |
|
129 setNeedsLayout(false); |
|
130 } |
|
131 |
|
132 bool RenderSVGRoot::selfWillPaint() const |
|
133 { |
|
134 #if ENABLE(FILTERS) |
|
135 const SVGRenderStyle* svgStyle = style()->svgStyle(); |
|
136 RenderSVGResourceFilter* filter = getRenderSVGResourceById<RenderSVGResourceFilter>(document(), svgStyle->filterResource()); |
|
137 if (filter) |
|
138 return true; |
|
139 #endif |
|
140 return false; |
|
141 } |
|
142 |
|
143 void RenderSVGRoot::paint(PaintInfo& paintInfo, int parentX, int parentY) |
|
144 { |
|
145 if (paintInfo.context->paintingDisabled()) |
|
146 return; |
|
147 |
|
148 bool isVisible = style()->visibility() == VISIBLE; |
|
149 IntPoint parentOriginInContainer(parentX, parentY); |
|
150 IntPoint borderBoxOriginInContainer = parentOriginInContainer + parentOriginToBorderBox(); |
|
151 |
|
152 if (hasBoxDecorations() && (paintInfo.phase == PaintPhaseBlockBackground || paintInfo.phase == PaintPhaseChildBlockBackground) && isVisible) |
|
153 paintBoxDecorations(paintInfo, borderBoxOriginInContainer.x(), borderBoxOriginInContainer.y()); |
|
154 |
|
155 if (paintInfo.phase == PaintPhaseBlockBackground) |
|
156 return; |
|
157 |
|
158 // An empty viewport disables rendering. FIXME: Should we still render filters? |
|
159 if (m_viewportSize.isEmpty()) |
|
160 return; |
|
161 |
|
162 // Don't paint if we don't have kids, except if we have filters we should paint those. |
|
163 if (!firstChild() && !selfWillPaint()) |
|
164 return; |
|
165 |
|
166 // Make a copy of the PaintInfo because applyTransform will modify the damage rect. |
|
167 PaintInfo childPaintInfo(paintInfo); |
|
168 childPaintInfo.context->save(); |
|
169 |
|
170 // Apply initial viewport clip - not affected by overflow handling |
|
171 childPaintInfo.context->clip(overflowClipRect(borderBoxOriginInContainer.x(), borderBoxOriginInContainer.y())); |
|
172 |
|
173 // Convert from container offsets (html renderers) to a relative transform (svg renderers). |
|
174 // Transform from our paint container's coordinate system to our local coords. |
|
175 childPaintInfo.applyTransform(localToRepaintContainerTransform(parentOriginInContainer)); |
|
176 |
|
177 bool continueRendering = true; |
|
178 if (childPaintInfo.phase == PaintPhaseForeground) |
|
179 continueRendering = SVGRenderSupport::prepareToRenderSVGContent(this, childPaintInfo); |
|
180 |
|
181 if (continueRendering) |
|
182 RenderBox::paint(childPaintInfo, 0, 0); |
|
183 |
|
184 if (childPaintInfo.phase == PaintPhaseForeground) |
|
185 SVGRenderSupport::finishRenderSVGContent(this, childPaintInfo, paintInfo.context); |
|
186 |
|
187 childPaintInfo.context->restore(); |
|
188 |
|
189 if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth() && isVisible) |
|
190 paintOutline(paintInfo.context, borderBoxOriginInContainer.x(), borderBoxOriginInContainer.y(), width(), height()); |
|
191 } |
|
192 |
|
193 void RenderSVGRoot::destroy() |
|
194 { |
|
195 RenderSVGResource::invalidateAllResourcesOfRenderer(this); |
|
196 RenderBox::destroy(); |
|
197 } |
|
198 |
|
199 void RenderSVGRoot::calcViewport() |
|
200 { |
|
201 SVGSVGElement* svg = static_cast<SVGSVGElement*>(node()); |
|
202 |
|
203 if (!svg->hasSetContainerSize()) { |
|
204 // In the normal case of <svg> being stand-alone or in a CSSBoxModel object we use |
|
205 // RenderBox::width()/height() (which pulls data from RenderStyle) |
|
206 m_viewportSize = FloatSize(width(), height()); |
|
207 return; |
|
208 } |
|
209 |
|
210 // In the SVGImage case grab the SVGLength values off of SVGSVGElement and use |
|
211 // the special relativeWidthValue accessors which respect the specified containerSize |
|
212 // FIXME: Check how SVGImage + zooming is supposed to be handled? |
|
213 SVGLength width = svg->width(); |
|
214 SVGLength height = svg->height(); |
|
215 m_viewportSize = FloatSize(width.unitType() == LengthTypePercentage ? svg->relativeWidthValue() : width.value(svg), |
|
216 height.unitType() == LengthTypePercentage ? svg->relativeHeightValue() : height.value(svg)); |
|
217 } |
|
218 |
|
219 // RenderBox methods will expect coordinates w/o any transforms in coordinates |
|
220 // relative to our borderBox origin. This method gives us exactly that. |
|
221 AffineTransform RenderSVGRoot::localToBorderBoxTransform() const |
|
222 { |
|
223 IntSize borderAndPadding = borderOriginToContentBox(); |
|
224 SVGSVGElement* svg = static_cast<SVGSVGElement*>(node()); |
|
225 float scale = svg->currentScale(); |
|
226 FloatPoint translate = svg->currentTranslate(); |
|
227 AffineTransform ctm(scale, 0, 0, scale, borderAndPadding.width() + translate.x(), borderAndPadding.height() + translate.y()); |
|
228 return svg->viewBoxToViewTransform(width() / scale, height() / scale) * ctm; |
|
229 } |
|
230 |
|
231 IntSize RenderSVGRoot::parentOriginToBorderBox() const |
|
232 { |
|
233 return IntSize(x(), y()); |
|
234 } |
|
235 |
|
236 IntSize RenderSVGRoot::borderOriginToContentBox() const |
|
237 { |
|
238 return IntSize(borderLeft() + paddingLeft(), borderTop() + paddingTop()); |
|
239 } |
|
240 |
|
241 AffineTransform RenderSVGRoot::localToRepaintContainerTransform(const IntPoint& parentOriginInContainer) const |
|
242 { |
|
243 AffineTransform parentToContainer(localToParentTransform()); |
|
244 return parentToContainer.translateRight(parentOriginInContainer.x(), parentOriginInContainer.y()); |
|
245 } |
|
246 |
|
247 const AffineTransform& RenderSVGRoot::localToParentTransform() const |
|
248 { |
|
249 IntSize parentToBorderBoxOffset = parentOriginToBorderBox(); |
|
250 |
|
251 AffineTransform borderBoxOriginToParentOrigin(localToBorderBoxTransform()); |
|
252 borderBoxOriginToParentOrigin.translateRight(parentToBorderBoxOffset.width(), parentToBorderBoxOffset.height()); |
|
253 |
|
254 m_localToParentTransform = borderBoxOriginToParentOrigin; |
|
255 return m_localToParentTransform; |
|
256 } |
|
257 |
|
258 FloatRect RenderSVGRoot::objectBoundingBox() const |
|
259 { |
|
260 return SVGRenderSupport::computeContainerBoundingBox(this, SVGRenderSupport::ObjectBoundingBox); |
|
261 } |
|
262 |
|
263 FloatRect RenderSVGRoot::strokeBoundingBox() const |
|
264 { |
|
265 return SVGRenderSupport::computeContainerBoundingBox(this, SVGRenderSupport::StrokeBoundingBox); |
|
266 } |
|
267 |
|
268 FloatRect RenderSVGRoot::repaintRectInLocalCoordinates() const |
|
269 { |
|
270 FloatRect repaintRect = SVGRenderSupport::computeContainerBoundingBox(this, SVGRenderSupport::RepaintBoundingBox); |
|
271 style()->svgStyle()->inflateForShadow(repaintRect); |
|
272 repaintRect.inflate(borderAndPaddingWidth()); |
|
273 return repaintRect; |
|
274 } |
|
275 |
|
276 IntRect RenderSVGRoot::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer) |
|
277 { |
|
278 return SVGRenderSupport::clippedOverflowRectForRepaint(this, repaintContainer); |
|
279 } |
|
280 |
|
281 void RenderSVGRoot::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& repaintRect, bool fixed) |
|
282 { |
|
283 // Apply our local transforms (except for x/y translation), then our shadow, |
|
284 // and then call RenderBox's method to handle all the normal CSS Box model bits |
|
285 repaintRect = localToBorderBoxTransform().mapRect(repaintRect); |
|
286 |
|
287 // Apply initial viewport clip - not affected by overflow settings |
|
288 repaintRect.intersect(enclosingIntRect(FloatRect(FloatPoint(), m_viewportSize))); |
|
289 |
|
290 style()->svgStyle()->inflateForShadow(repaintRect); |
|
291 RenderBox::computeRectForRepaint(repaintContainer, repaintRect, fixed); |
|
292 } |
|
293 |
|
294 void RenderSVGRoot::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed , bool useTransforms, TransformState& transformState) const |
|
295 { |
|
296 ASSERT(!fixed); // We should have no fixed content in the SVG rendering tree. |
|
297 ASSERT(useTransforms); // mapping a point through SVG w/o respecting trasnforms is useless. |
|
298 |
|
299 // Transform to our border box and let RenderBox transform the rest of the way. |
|
300 transformState.applyTransform(localToBorderBoxTransform()); |
|
301 RenderBox::mapLocalToContainer(repaintContainer, fixed, useTransforms, transformState); |
|
302 } |
|
303 |
|
304 bool RenderSVGRoot::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction) |
|
305 { |
|
306 IntPoint pointInContainer(_x, _y); |
|
307 IntSize containerToParentOffset(_tx, _ty); |
|
308 |
|
309 IntPoint pointInParent = pointInContainer - containerToParentOffset; |
|
310 IntPoint pointInBorderBox = pointInParent - parentOriginToBorderBox(); |
|
311 |
|
312 // Note: For now, we're ignoring hits to border and padding for <svg> |
|
313 IntPoint pointInContentBox = pointInBorderBox - borderOriginToContentBox(); |
|
314 if (!contentBoxRect().contains(pointInContentBox)) |
|
315 return false; |
|
316 |
|
317 IntPoint localPoint = localToParentTransform().inverse().mapPoint(pointInParent); |
|
318 |
|
319 for (RenderObject* child = lastChild(); child; child = child->previousSibling()) { |
|
320 if (child->nodeAtFloatPoint(request, result, localPoint, hitTestAction)) { |
|
321 // FIXME: CSS/HTML assumes the local point is relative to the border box, right? |
|
322 updateHitTestResult(result, pointInBorderBox); |
|
323 // FIXME: nodeAtFloatPoint() doesn't handle rect-based hit tests yet. |
|
324 result.addNodeToRectBasedTestResult(child->node(), _x, _y); |
|
325 return true; |
|
326 } |
|
327 } |
|
328 |
|
329 // Spec: Only graphical elements can be targeted by the mouse, so we don't check self here. |
|
330 // 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." |
|
331 return false; |
|
332 } |
|
333 |
|
334 } |
|
335 |
|
336 #endif // ENABLE(SVG) |