viewBox on nested SVG causes wrong content size for relative values
[WebKit-https.git] / Source / WebCore / rendering / svg / SVGRenderSupport.cpp
1 /*
2  * Copyright (C) 2007, 2008 Rob Buis <buis@kde.org>
3  * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
4  * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
5  * Copyright (C) 2009 Google, Inc.  All rights reserved.
6  * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
7  * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public License
20  * along with this library; see the file COPYING.LIB.  If not, write to
21  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  * Boston, MA 02110-1301, USA.
23  */
24
25 #include "config.h"
26
27 #if ENABLE(SVG)
28 #include "SVGRenderSupport.h"
29
30 #include "Frame.h"
31 #include "FrameView.h"
32 #include "ImageBuffer.h"
33 #include "NodeRenderStyle.h"
34 #include "RenderLayer.h"
35 #include "RenderSVGPath.h"
36 #include "RenderSVGResource.h"
37 #include "RenderSVGResourceClipper.h"
38 #include "RenderSVGResourceFilter.h"
39 #include "RenderSVGResourceMarker.h"
40 #include "RenderSVGResourceMasker.h"
41 #include "RenderSVGRoot.h"
42 #include "RenderSVGViewportContainer.h"
43 #include "SVGResources.h"
44 #include "SVGResourcesCache.h"
45 #include "SVGStyledElement.h"
46 #include "TransformState.h"
47 #include <wtf/UnusedParam.h>
48
49 namespace WebCore {
50
51 LayoutRect SVGRenderSupport::clippedOverflowRectForRepaint(const RenderObject* object, RenderBoxModelObject* repaintContainer)
52 {
53     // Return early for any cases where we don't actually paint
54     if (object->style()->visibility() != VISIBLE && !object->enclosingLayer()->hasVisibleContent())
55         return LayoutRect();
56
57     // Pass our local paint rect to computeRectForRepaint() which will
58     // map to parent coords and recurse up the parent chain.
59     FloatRect repaintRect = object->repaintRectInLocalCoordinates();
60     object->computeFloatRectForRepaint(repaintContainer, repaintRect);
61     return enclosingLayoutRect(repaintRect);
62 }
63
64 void SVGRenderSupport::computeFloatRectForRepaint(const RenderObject* object, RenderBoxModelObject* repaintContainer, FloatRect& repaintRect, bool fixed)
65 {
66     const SVGRenderStyle* svgStyle = object->style()->svgStyle();
67     if (const ShadowData* shadow = svgStyle->shadow())
68         shadow->adjustRectForShadow(repaintRect);
69     repaintRect.inflate(object->style()->outlineWidth());
70
71     // Translate to coords in our parent renderer, and then call computeFloatRectForRepaint() on our parent.
72     repaintRect = object->localToParentTransform().mapRect(repaintRect);
73     object->parent()->computeFloatRectForRepaint(repaintContainer, repaintRect, fixed);
74 }
75
76 void SVGRenderSupport::mapLocalToContainer(const RenderObject* object, RenderBoxModelObject* repaintContainer, TransformState& transformState, bool* wasFixed)
77 {
78     transformState.applyTransform(object->localToParentTransform());
79     object->parent()->mapLocalToContainer(repaintContainer, false, true, transformState, wasFixed);
80 }
81
82 bool SVGRenderSupport::prepareToRenderSVGContent(RenderObject* object, PaintInfo& paintInfo)
83 {
84     ASSERT(object);
85
86     RenderStyle* style = object->style();
87     ASSERT(style);
88
89     const SVGRenderStyle* svgStyle = style->svgStyle();
90     ASSERT(svgStyle);
91
92     bool isRenderingMask = false;
93     if (object->frame() && object->frame()->view())
94         isRenderingMask = object->frame()->view()->paintBehavior() & PaintBehaviorRenderingSVGMask;
95
96     // Setup transparency layers before setting up SVG resources!
97     float opacity = isRenderingMask ? 1 : style->opacity();
98     const ShadowData* shadow = svgStyle->shadow();
99     if (opacity < 1 || shadow) {
100         FloatRect repaintRect = object->repaintRectInLocalCoordinates();
101
102         if (opacity < 1) {
103             paintInfo.context->clip(repaintRect);
104             paintInfo.context->beginTransparencyLayer(opacity);
105         }
106
107         if (shadow) {
108             paintInfo.context->clip(repaintRect);
109             paintInfo.context->setShadow(IntSize(shadow->x(), shadow->y()), shadow->blur(), shadow->color(), style->colorSpace());
110             paintInfo.context->beginTransparencyLayer(1);
111         }
112     }
113
114     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
115     if (!resources) {
116 #if ENABLE(FILTERS)
117         if (svgStyle->hasFilter())
118             return false;
119 #endif
120         return true;
121     }
122
123     if (!isRenderingMask) {
124         if (RenderSVGResourceMasker* masker = resources->masker()) {
125             if (!masker->applyResource(object, style, paintInfo.context, ApplyToDefaultMode))
126                 return false;
127         }
128     }
129
130     if (RenderSVGResourceClipper* clipper = resources->clipper()) {
131         if (!clipper->applyResource(object, style, paintInfo.context, ApplyToDefaultMode))
132             return false;
133     }
134
135 #if ENABLE(FILTERS)
136     if (!isRenderingMask) {
137         if (RenderSVGResourceFilter* filter = resources->filter()) {
138             if (!filter->applyResource(object, style, paintInfo.context, ApplyToDefaultMode))
139                 return false;
140         }
141     }
142 #endif
143
144     return true;
145 }
146
147 void SVGRenderSupport::finishRenderSVGContent(RenderObject* object, PaintInfo& paintInfo, GraphicsContext* savedContext)
148 {
149 #if !ENABLE(FILTERS)
150     UNUSED_PARAM(savedContext);
151 #endif
152
153     ASSERT(object);
154
155     const RenderStyle* style = object->style();
156     ASSERT(style);
157
158     const SVGRenderStyle* svgStyle = style->svgStyle();
159     ASSERT(svgStyle);
160
161 #if ENABLE(FILTERS)
162     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
163     if (resources) {
164         if (RenderSVGResourceFilter* filter = resources->filter()) {
165             filter->postApplyResource(static_cast<RenderSVGShape*>(object), paintInfo.context, ApplyToDefaultMode, 0, 0);
166             paintInfo.context = savedContext;
167         }
168     }
169 #endif
170
171     if (style->opacity() < 1)
172         paintInfo.context->endTransparencyLayer();
173
174     if (svgStyle->shadow())
175         paintInfo.context->endTransparencyLayer();
176 }
177
178 void SVGRenderSupport::computeContainerBoundingBoxes(const RenderObject* container, FloatRect& objectBoundingBox, FloatRect& strokeBoundingBox, FloatRect& repaintBoundingBox)
179 {
180     bool isFirstChild = true;
181
182     for (RenderObject* current = container->firstChild(); current; current = current->nextSibling()) {
183         if (current->isSVGHiddenContainer())
184             continue;
185
186         const AffineTransform& transform = current->localToParentTransform();
187         if (transform.isIdentity()) {
188             if (isFirstChild)
189                 objectBoundingBox = current->objectBoundingBox();
190             else
191                 objectBoundingBox.uniteEvenIfEmpty(current->objectBoundingBox());
192             strokeBoundingBox.unite(current->strokeBoundingBox());
193             repaintBoundingBox.unite(current->repaintRectInLocalCoordinates());
194         } else {
195             if (isFirstChild)
196                 objectBoundingBox = transform.mapRect(current->objectBoundingBox());
197             else
198                 objectBoundingBox.uniteEvenIfEmpty(transform.mapRect(current->objectBoundingBox()));
199             strokeBoundingBox.unite(transform.mapRect(current->strokeBoundingBox()));
200             repaintBoundingBox.unite(transform.mapRect(current->repaintRectInLocalCoordinates()));
201         }
202
203         isFirstChild = false;
204     }
205 }
206
207 bool SVGRenderSupport::paintInfoIntersectsRepaintRect(const FloatRect& localRepaintRect, const AffineTransform& localTransform, const PaintInfo& paintInfo)
208 {
209     if (localTransform.isIdentity())
210         return localRepaintRect.intersects(paintInfo.rect);
211
212     return localTransform.mapRect(localRepaintRect).intersects(paintInfo.rect);
213 }
214
215 const RenderSVGRoot* SVGRenderSupport::findTreeRootObject(const RenderObject* start)
216 {
217     while (start && !start->isSVGRoot())
218         start = start->parent();
219
220     ASSERT(start);
221     ASSERT(start->isSVGRoot());
222     return toRenderSVGRoot(start);
223 }
224
225 static inline void invalidateResourcesOfChildren(RenderObject* start)
226 {
227     ASSERT(!start->needsLayout());
228     if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(start))
229         resources->removeClientFromCache(start, false);
230
231     for (RenderObject* child = start->firstChild(); child; child = child->nextSibling())
232         invalidateResourcesOfChildren(child);
233 }
234
235 static inline bool layoutSizeOfNearestViewportChanged(const RenderObject* start)
236 {
237     while (start && !start->isSVGRoot() && !start->isSVGViewportContainer())
238         start = start->parent();
239
240     ASSERT(start);
241     ASSERT(start->isSVGRoot() || start->isSVGViewportContainer());
242     if (start->isSVGViewportContainer())
243         return toRenderSVGViewportContainer(start)->isLayoutSizeChanged();
244
245     return toRenderSVGRoot(start)->isLayoutSizeChanged();
246 }
247
248 void SVGRenderSupport::layoutChildren(RenderObject* start, bool selfNeedsLayout)
249 {
250     bool layoutSizeChanged = layoutSizeOfNearestViewportChanged(start);
251     HashSet<RenderObject*> notlayoutedObjects;
252
253     for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) {
254         bool needsLayout = selfNeedsLayout;
255
256         if (layoutSizeChanged) {
257             // When selfNeedsLayout is false and the layout size changed, we have to check whether this child uses relative lengths
258             if (SVGElement* element = child->node()->isSVGElement() ? static_cast<SVGElement*>(child->node()) : 0) {
259                 if (element->isStyled() && static_cast<SVGStyledElement*>(element)->hasRelativeLengths()) {
260                     // When the layout size changed and when using relative values tell the RenderSVGShape to update its shape object
261                     if (child->isSVGShape())
262                         toRenderSVGShape(child)->setNeedsShapeUpdate();
263
264                     needsLayout = true;
265                 }
266             }
267         }
268
269         if (needsLayout) {
270             child->setNeedsLayout(true, false);
271             child->layout();
272         } else {
273             if (child->needsLayout())
274                 child->layout();
275             else if (layoutSizeChanged)
276                 notlayoutedObjects.add(child);
277         }
278
279         ASSERT(!child->needsLayout());
280     }
281
282     if (!layoutSizeChanged) {
283         ASSERT(notlayoutedObjects.isEmpty());
284         return;
285     }
286
287     // If the layout size changed, invalidate all resources of all children that didn't go through the layout() code path.
288     HashSet<RenderObject*>::iterator end = notlayoutedObjects.end();
289     for (HashSet<RenderObject*>::iterator it = notlayoutedObjects.begin(); it != end; ++it)
290         invalidateResourcesOfChildren(*it);
291 }
292
293 bool SVGRenderSupport::isOverflowHidden(const RenderObject* object)
294 {
295     // SVG doesn't support independent x/y overflow
296     ASSERT(object->style()->overflowX() == object->style()->overflowY());
297
298     // OSCROLL is never set for SVG - see CSSStyleSelector::adjustRenderStyle
299     ASSERT(object->style()->overflowX() != OSCROLL);
300
301     // RenderSVGRoot should never query for overflow state - it should always clip itself to the initial viewport size.
302     ASSERT(!object->isRoot());
303
304     return object->style()->overflowX() == OHIDDEN;
305 }
306
307 void SVGRenderSupport::intersectRepaintRectWithResources(const RenderObject* object, FloatRect& repaintRect)
308 {
309     ASSERT(object);
310
311     RenderStyle* style = object->style();
312     ASSERT(style);
313
314     const SVGRenderStyle* svgStyle = style->svgStyle();
315     ASSERT(svgStyle);
316
317     RenderObject* renderer = const_cast<RenderObject*>(object);
318     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer);
319     if (!resources) {
320         if (const ShadowData* shadow = svgStyle->shadow())
321             shadow->adjustRectForShadow(repaintRect);
322         return;
323     }
324
325 #if ENABLE(FILTERS)
326     if (RenderSVGResourceFilter* filter = resources->filter())
327         repaintRect = filter->resourceBoundingBox(renderer);
328 #endif
329
330     if (RenderSVGResourceClipper* clipper = resources->clipper())
331         repaintRect.intersect(clipper->resourceBoundingBox(renderer));
332
333     if (RenderSVGResourceMasker* masker = resources->masker())
334         repaintRect.intersect(masker->resourceBoundingBox(renderer));
335
336     if (const ShadowData* shadow = svgStyle->shadow())
337         shadow->adjustRectForShadow(repaintRect);
338 }
339
340 bool SVGRenderSupport::filtersForceContainerLayout(RenderObject* object)
341 {
342     // If any of this container's children need to be laid out, and a filter is applied
343     // to the container, we need to repaint the entire container.
344     if (!object->normalChildNeedsLayout())
345         return false;
346
347     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
348     if (!resources || !resources->filter())
349         return false;
350
351     return true;
352 }
353
354 bool SVGRenderSupport::pointInClippingArea(RenderObject* object, const FloatPoint& point)
355 {
356     ASSERT(object);
357
358     // We just take clippers into account to determine if a point is on the node. The Specification may
359     // change later and we also need to check maskers.
360     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
361     if (!resources)
362         return true;
363
364     if (RenderSVGResourceClipper* clipper = resources->clipper())
365         return clipper->hitTestClipContent(object->objectBoundingBox(), point);
366
367     return true;
368 }
369
370 void SVGRenderSupport::applyStrokeStyleToContext(GraphicsContext* context, const RenderStyle* style, const RenderObject* object)
371 {
372     ASSERT(context);
373     ASSERT(style);
374     ASSERT(object);
375     ASSERT(object->node());
376     ASSERT(object->node()->isSVGElement());
377
378     const SVGRenderStyle* svgStyle = style->svgStyle();
379     ASSERT(svgStyle);
380
381     SVGLengthContext lengthContext(static_cast<SVGElement*>(object->node()));
382     context->setStrokeThickness(svgStyle->strokeWidth().value(lengthContext));
383     context->setLineCap(svgStyle->capStyle());
384     context->setLineJoin(svgStyle->joinStyle());
385     if (svgStyle->joinStyle() == MiterJoin)
386         context->setMiterLimit(svgStyle->strokeMiterLimit());
387
388     const Vector<SVGLength>& dashes = svgStyle->strokeDashArray();
389     if (dashes.isEmpty())
390         context->setStrokeStyle(SolidStroke);
391     else {
392         DashArray dashArray;
393         const Vector<SVGLength>::const_iterator end = dashes.end();
394         for (Vector<SVGLength>::const_iterator it = dashes.begin(); it != end; ++it)
395             dashArray.append((*it).value(lengthContext));
396
397         context->setLineDash(dashArray, svgStyle->strokeDashOffset().value(lengthContext));
398     }
399 }
400
401 }
402
403 #endif