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