Use lineageOfType to simplify two rendering helpers.
[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 "NodeRenderStyle.h"
31 #include "RenderElement.h"
32 #include "RenderGeometryMap.h"
33 #include "RenderIterator.h"
34 #include "RenderLayer.h"
35 #include "RenderSVGResource.h"
36 #include "RenderSVGResourceClipper.h"
37 #include "RenderSVGResourceFilter.h"
38 #include "RenderSVGResourceMarker.h"
39 #include "RenderSVGResourceMasker.h"
40 #include "RenderSVGRoot.h"
41 #include "RenderSVGText.h"
42 #include "RenderSVGViewportContainer.h"
43 #include "SVGElement.h"
44 #include "SVGResources.h"
45 #include "SVGResourcesCache.h"
46 #include "TransformState.h"
47
48 namespace WebCore {
49
50 FloatRect SVGRenderSupport::repaintRectForRendererInLocalCoordinatesExcludingSVGShadow(const RenderElement& renderer)
51 {
52     // FIXME: Add support for RenderSVGBlock.
53
54     if (renderer.isSVGShape() || renderer.isSVGImage() || renderer.isSVGContainer())
55         return toRenderSVGModelObject(renderer).repaintRectInLocalCoordinatesExcludingSVGShadow();
56
57     return renderer.repaintRectInLocalCoordinates();
58 }
59
60 LayoutRect SVGRenderSupport::clippedOverflowRectForRepaint(const RenderElement& renderer, const RenderLayerModelObject* repaintContainer)
61 {
62     // Return early for any cases where we don't actually paint
63     if (renderer.style().visibility() != VISIBLE && !renderer.enclosingLayer()->hasVisibleContent())
64         return LayoutRect();
65
66     // Pass our local paint rect to computeRectForRepaint() which will
67     // map to parent coords and recurse up the parent chain.
68     FloatRect repaintRect = repaintRectForRendererInLocalCoordinatesExcludingSVGShadow(renderer);
69     const SVGRenderStyle& svgStyle = renderer.style().svgStyle();
70     if (const ShadowData* shadow = svgStyle.shadow())
71         shadow->adjustRectForShadow(repaintRect);
72     renderer.computeFloatRectForRepaint(repaintContainer, repaintRect);
73     return enclosingLayoutRect(repaintRect);
74 }
75
76 void SVGRenderSupport::computeFloatRectForRepaint(const RenderElement& renderer, const RenderLayerModelObject* repaintContainer, FloatRect& repaintRect, bool fixed)
77 {
78     const SVGRenderStyle& svgStyle = renderer.style().svgStyle();
79     if (const ShadowData* shadow = svgStyle.shadow())
80         shadow->adjustRectForShadow(repaintRect);
81     repaintRect.inflate(renderer.style().outlineWidth());
82
83     // Translate to coords in our parent renderer, and then call computeFloatRectForRepaint() on our parent.
84     repaintRect = renderer.localToParentTransform().mapRect(repaintRect);
85     renderer.parent()->computeFloatRectForRepaint(repaintContainer, repaintRect, fixed);
86 }
87
88 void SVGRenderSupport::mapLocalToContainer(const RenderElement& renderer, const RenderLayerModelObject* repaintContainer, TransformState& transformState, bool* wasFixed)
89 {
90     transformState.applyTransform(renderer.localToParentTransform());
91
92     ASSERT(renderer.parent());
93     auto& parent = *renderer.parent();
94     
95     // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform 
96     // to map an element from SVG viewport coordinates to CSS box coordinates.
97     // RenderSVGRoot's mapLocalToContainer method expects CSS box coordinates.
98     if (parent.isSVGRoot())
99         transformState.applyTransform(toRenderSVGRoot(parent).localToBorderBoxTransform());
100
101     MapCoordinatesFlags mode = UseTransforms;
102     parent.mapLocalToContainer(repaintContainer, transformState, mode, wasFixed);
103 }
104
105 const RenderElement* SVGRenderSupport::pushMappingToContainer(const RenderElement& renderer, const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap)
106 {
107     ASSERT_UNUSED(ancestorToStopAt, ancestorToStopAt != &renderer);
108
109     ASSERT(renderer.parent());
110     auto& parent = *renderer.parent();
111
112     // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform 
113     // to map an element from SVG viewport coordinates to CSS box coordinates.
114     // RenderSVGRoot's mapLocalToContainer method expects CSS box coordinates.
115     if (parent.isSVGRoot()) {
116         TransformationMatrix matrix(renderer.localToParentTransform());
117         matrix.multiply(toRenderSVGRoot(parent).localToBorderBoxTransform());
118         geometryMap.push(&renderer, matrix);
119     } else
120         geometryMap.push(&renderer, renderer.localToParentTransform());
121
122     return &parent;
123 }
124
125 bool SVGRenderSupport::checkForSVGRepaintDuringLayout(const RenderElement& renderer)
126 {
127     if (!renderer.checkForRepaintDuringLayout())
128         return false;
129     // When a parent container is transformed in SVG, all children will be painted automatically
130     // so we are able to skip redundant repaint checks.
131     auto parent = renderer.parent();
132     return !(parent && parent->isSVGContainer() && toRenderSVGContainer(parent)->didTransformToRootUpdate());
133 }
134
135 // Update a bounding box taking into account the validity of the other bounding box.
136 static inline void updateObjectBoundingBox(FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, RenderObject* other, FloatRect otherBoundingBox)
137 {
138     bool otherValid = other->isSVGContainer() ? toRenderSVGContainer(other)->isObjectBoundingBoxValid() : true;
139     if (!otherValid)
140         return;
141
142     if (!objectBoundingBoxValid) {
143         objectBoundingBox = otherBoundingBox;
144         objectBoundingBoxValid = true;
145         return;
146     }
147
148     objectBoundingBox.uniteEvenIfEmpty(otherBoundingBox);
149 }
150
151 void SVGRenderSupport::computeContainerBoundingBoxes(const RenderElement& container, FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, FloatRect& strokeBoundingBox, FloatRect& repaintBoundingBox)
152 {
153     objectBoundingBox = FloatRect();
154     objectBoundingBoxValid = false;
155     strokeBoundingBox = FloatRect();
156
157     // When computing the strokeBoundingBox, we use the repaintRects of the container's children so that the container's stroke includes
158     // the resources applied to the children (such as clips and filters). This allows filters applied to containers to correctly bound
159     // the children, and also improves inlining of SVG content, as the stroke bound is used in that situation also.
160     for (RenderObject* current = container.firstChild(); current; current = current->nextSibling()) {
161         if (current->isSVGHiddenContainer())
162             continue;
163
164         const AffineTransform& transform = current->localToParentTransform();
165         if (transform.isIdentity()) {
166             updateObjectBoundingBox(objectBoundingBox, objectBoundingBoxValid, current, current->objectBoundingBox());
167             strokeBoundingBox.unite(current->repaintRectInLocalCoordinates());
168         } else {
169             updateObjectBoundingBox(objectBoundingBox, objectBoundingBoxValid, current, transform.mapRect(current->objectBoundingBox()));
170             strokeBoundingBox.unite(transform.mapRect(current->repaintRectInLocalCoordinates()));
171         }
172     }
173
174     repaintBoundingBox = strokeBoundingBox;
175 }
176
177 bool SVGRenderSupport::paintInfoIntersectsRepaintRect(const FloatRect& localRepaintRect, const AffineTransform& localTransform, const PaintInfo& paintInfo)
178 {
179     if (localTransform.isIdentity())
180         return localRepaintRect.intersects(paintInfo.rect);
181
182     return localTransform.mapRect(localRepaintRect).intersects(paintInfo.rect);
183 }
184
185 const RenderSVGRoot& SVGRenderSupport::findTreeRootObject(const RenderElement& start)
186 {
187     return *lineageOfType<RenderSVGRoot>(start).first();
188 }
189
190 static inline void invalidateResourcesOfChildren(RenderObject& start)
191 {
192     ASSERT(!start.needsLayout());
193     if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(start))
194         resources->removeClientFromCache(start, false);
195
196     for (RenderObject* child = start.firstChildSlow(); child; child = child->nextSibling())
197         invalidateResourcesOfChildren(*child);
198 }
199
200 static inline bool layoutSizeOfNearestViewportChanged(const RenderElement& renderer)
201 {
202     const RenderElement* start = &renderer;
203     while (start && !start->isSVGRoot() && !start->isSVGViewportContainer())
204         start = start->parent();
205
206     ASSERT(start);
207     ASSERT(start->isSVGRoot() || start->isSVGViewportContainer());
208     if (start->isSVGViewportContainer())
209         return toRenderSVGViewportContainer(start)->isLayoutSizeChanged();
210
211     return toRenderSVGRoot(start)->isLayoutSizeChanged();
212 }
213
214 bool SVGRenderSupport::transformToRootChanged(RenderElement* ancestor)
215 {
216     while (ancestor && !ancestor->isSVGRoot()) {
217         if (ancestor->isSVGTransformableContainer())
218             return toRenderSVGContainer(ancestor)->didTransformToRootUpdate();
219         if (ancestor->isSVGViewportContainer())
220             return toRenderSVGViewportContainer(ancestor)->didTransformToRootUpdate();
221         ancestor = ancestor->parent();
222     }
223
224     return false;
225 }
226
227 void SVGRenderSupport::layoutChildren(RenderElement& start, bool selfNeedsLayout)
228 {
229     bool layoutSizeChanged = layoutSizeOfNearestViewportChanged(start);
230     bool transformChanged = transformToRootChanged(&start);
231     bool hasSVGShadow = rendererHasSVGShadow(start);
232     bool needsBoundariesUpdate = start.needsBoundariesUpdate();
233     HashSet<RenderObject*> notlayoutedObjects;
234
235     for (RenderObject* child = start.firstChild(); child; child = child->nextSibling()) {
236         bool needsLayout = selfNeedsLayout;
237         bool childEverHadLayout = child->everHadLayout();
238
239         if (needsBoundariesUpdate && hasSVGShadow) {
240             // If we have a shadow, our shadow is baked into our children's cached boundaries,
241             // so they need to update.
242             child->setNeedsBoundariesUpdate();
243             needsLayout = true;
244         }
245
246         if (transformChanged) {
247             // If the transform changed we need to update the text metrics (note: this also happens for layoutSizeChanged=true).
248             if (child->isSVGText())
249                 toRenderSVGText(child)->setNeedsTextMetricsUpdate();
250             needsLayout = true;
251         }
252
253         if (layoutSizeChanged) {
254             // When selfNeedsLayout is false and the layout size changed, we have to check whether this child uses relative lengths
255             if (SVGElement* element = child->node()->isSVGElement() ? toSVGElement(child->node()) : 0) {
256                 if (element->hasRelativeLengths()) {
257                     // When the layout size changed and when using relative values tell the RenderSVGShape to update its shape object
258                     if (child->isSVGShape())
259                         toRenderSVGShape(child)->setNeedsShapeUpdate();
260                     else if (child->isSVGText()) {
261                         toRenderSVGText(child)->setNeedsTextMetricsUpdate();
262                         toRenderSVGText(child)->setNeedsPositioningValuesUpdate();
263                     }
264
265                     needsLayout = true;
266                 }
267             }
268         }
269
270         if (needsLayout)
271             child->setNeedsLayout(MarkOnlyThis);
272
273         if (child->needsLayout()) {
274             toRenderElement(child)->layout();
275             // Renderers are responsible for repainting themselves when changing, except
276             // for the initial paint to avoid potential double-painting caused by non-sensical "old" bounds.
277             // We could handle this in the individual objects, but for now it's easier to have
278             // parent containers call repaint().  (RenderBlock::layout* has similar logic.)
279             if (!childEverHadLayout)
280                 child->repaint();
281         } else if (layoutSizeChanged)
282             notlayoutedObjects.add(child);
283
284         ASSERT(!child->needsLayout());
285     }
286
287     if (!layoutSizeChanged) {
288         ASSERT(notlayoutedObjects.isEmpty());
289         return;
290     }
291
292     // If the layout size changed, invalidate all resources of all children that didn't go through the layout() code path.
293     for (auto child : notlayoutedObjects)
294         invalidateResourcesOfChildren(*child);
295 }
296
297 bool SVGRenderSupport::isOverflowHidden(const RenderElement& renderer)
298 {
299     // SVG doesn't support independent x/y overflow
300     ASSERT(renderer.style().overflowX() == renderer.style().overflowY());
301
302     // OSCROLL is never set for SVG - see StyleResolver::adjustRenderStyle
303     ASSERT(renderer.style().overflowX() != OSCROLL);
304
305     // RenderSVGRoot should never query for overflow state - it should always clip itself to the initial viewport size.
306     ASSERT(!renderer.isRoot());
307
308     return renderer.style().overflowX() == OHIDDEN;
309 }
310
311 bool SVGRenderSupport::rendererHasSVGShadow(const RenderObject& renderer)
312 {
313     // FIXME: Add support for RenderSVGBlock.
314
315     if (renderer.isSVGShape() || renderer.isSVGImage() || renderer.isSVGContainer())
316         return toRenderSVGModelObject(renderer).hasSVGShadow();
317
318     if (renderer.isSVGRoot())
319         return toRenderSVGRoot(renderer).hasSVGShadow();
320
321     return false;
322 }
323
324 void SVGRenderSupport::setRendererHasSVGShadow(RenderObject& renderer, bool hasShadow)
325 {
326     // FIXME: Add support for RenderSVGBlock.
327
328     if (renderer.isSVGShape() || renderer.isSVGImage() || renderer.isSVGContainer()) {
329         toRenderSVGModelObject(renderer).setHasSVGShadow(hasShadow);
330         return;
331     }
332
333     if (renderer.isSVGRoot())
334         toRenderSVGRoot(renderer).setHasSVGShadow(hasShadow);
335 }
336
337 void SVGRenderSupport::intersectRepaintRectWithShadows(const RenderElement& renderer, FloatRect& repaintRect)
338 {
339     // Since -webkit-svg-shadow enables shadow drawing for its children, but its children
340     // don't inherit the shadow in their SVGRenderStyle, we need to search our parents for
341     // shadows in order to correctly compute our repaint rect.
342
343     auto currentObject = &renderer;
344
345     AffineTransform localToRootTransform;
346
347     while (currentObject && rendererHasSVGShadow(*currentObject)) {
348         const RenderStyle& style = currentObject->style();
349         const SVGRenderStyle& svgStyle = style.svgStyle();
350         if (const ShadowData* shadow = svgStyle.shadow())
351             shadow->adjustRectForShadow(repaintRect);
352
353         repaintRect = currentObject->localToParentTransform().mapRect(repaintRect);
354         localToRootTransform *= currentObject->localToParentTransform();
355
356         currentObject = currentObject->parent();
357     };
358
359     if (localToRootTransform.isIdentity())
360         return;
361
362     AffineTransform rootToLocalTransform = localToRootTransform.inverse();
363     repaintRect = rootToLocalTransform.mapRect(repaintRect);
364 }
365
366 void SVGRenderSupport::intersectRepaintRectWithResources(const RenderElement& renderer, FloatRect& repaintRect)
367 {
368     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer);
369     if (!resources)
370         return;
371
372 #if ENABLE(FILTERS)
373     if (RenderSVGResourceFilter* filter = resources->filter())
374         repaintRect = filter->resourceBoundingBox(renderer);
375 #endif
376
377     if (RenderSVGResourceClipper* clipper = resources->clipper())
378         repaintRect.intersect(clipper->resourceBoundingBox(renderer));
379
380     if (RenderSVGResourceMasker* masker = resources->masker())
381         repaintRect.intersect(masker->resourceBoundingBox(renderer));
382 }
383
384 bool SVGRenderSupport::filtersForceContainerLayout(const RenderElement& renderer)
385 {
386     // If any of this container's children need to be laid out, and a filter is applied
387     // to the container, we need to repaint the entire container.
388     if (!renderer.normalChildNeedsLayout())
389         return false;
390
391     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer);
392     if (!resources || !resources->filter())
393         return false;
394
395     return true;
396 }
397
398 bool SVGRenderSupport::pointInClippingArea(const RenderElement& renderer, const FloatPoint& point)
399 {
400     // We just take clippers into account to determine if a point is on the node. The Specification may
401     // change later and we also need to check maskers.
402     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer);
403     if (!resources)
404         return true;
405
406     if (RenderSVGResourceClipper* clipper = resources->clipper())
407         return clipper->hitTestClipContent(renderer.objectBoundingBox(), point);
408
409     return true;
410 }
411
412 void SVGRenderSupport::applyStrokeStyleToContext(GraphicsContext* context, const RenderStyle& style, const RenderElement& renderer)
413 {
414     ASSERT(context);
415     ASSERT(renderer.element());
416     ASSERT(renderer.element()->isSVGElement());
417
418     const SVGRenderStyle& svgStyle = style.svgStyle();
419
420     SVGLengthContext lengthContext(toSVGElement(renderer.element()));
421     context->setStrokeThickness(svgStyle.strokeWidth().value(lengthContext));
422     context->setLineCap(svgStyle.capStyle());
423     context->setLineJoin(svgStyle.joinStyle());
424     if (svgStyle.joinStyle() == MiterJoin)
425         context->setMiterLimit(svgStyle.strokeMiterLimit());
426
427     const Vector<SVGLength>& dashes = svgStyle.strokeDashArray();
428     if (dashes.isEmpty())
429         context->setStrokeStyle(SolidStroke);
430     else {
431         DashArray dashArray;
432         dashArray.reserveInitialCapacity(dashes.size());
433         for (unsigned i = 0, size = dashes.size(); i < size; ++i)
434             dashArray.uncheckedAppend(dashes[i].value(lengthContext));
435
436         context->setLineDash(dashArray, svgStyle.strokeDashOffset().value(lengthContext));
437     }
438 }
439
440 void SVGRenderSupport::childAdded(RenderElement& parent, RenderObject& child)
441 {
442     SVGRenderSupport::setRendererHasSVGShadow(child, SVGRenderSupport::rendererHasSVGShadow(parent) || SVGRenderSupport::rendererHasSVGShadow(child));
443 }
444
445 void SVGRenderSupport::styleChanged(RenderElement& renderer)
446 {
447     auto parent = renderer.parent();
448     SVGRenderSupport::setRendererHasSVGShadow(renderer, (parent && SVGRenderSupport::rendererHasSVGShadow(*parent)) || renderer.style().svgStyle().shadow());
449 }
450
451 }
452
453 #endif