A crash reproducible in Path::isEmpty() under RenderSVGShape::paint()
[WebKit-https.git] / Source / WebCore / rendering / svg / RenderSVGShape.cpp
1 /*
2  * Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org>
3  * Copyright (C) 2004, 2005, 2008 Rob Buis <buis@kde.org>
4  * Copyright (C) 2005, 2007 Eric Seidel <eric@webkit.org>
5  * Copyright (C) 2009 Google, Inc.
6  * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
7  * Copyright (C) Research In Motion Limited 2010. All rights reserved.
8  * Copyright (C) 2009 Jeff Schiller <codedread@gmail.com>
9  * Copyright (C) 2011 Renata Hodovan <reni@webkit.org>
10  * Copyright (C) 2011 University of Szeged
11  *
12  * This library is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Library General Public
14  * License as published by the Free Software Foundation; either
15  * version 2 of the License, or (at your option) any later version.
16  *
17  * This library is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Library General Public License for more details.
21  *
22  * You should have received a copy of the GNU Library General Public License
23  * along with this library; see the file COPYING.LIB.  If not, write to
24  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25  * Boston, MA 02110-1301, USA.
26  */
27
28 #include "config.h"
29 #include "RenderSVGShape.h"
30
31 #include "FloatPoint.h"
32 #include "FloatQuad.h"
33 #include "GraphicsContext.h"
34 #include "HitTestRequest.h"
35 #include "LayoutRepainter.h"
36 #include "PointerEventsHitRules.h"
37 #include "RenderSVGResourceMarker.h"
38 #include "RenderSVGResourceSolidColor.h"
39 #include "SVGPathData.h"
40 #include "SVGRenderingContext.h"
41 #include "SVGResources.h"
42 #include "SVGResourcesCache.h"
43 #include "SVGTransformList.h"
44 #include "SVGURIReference.h"
45 #include "StrokeStyleApplier.h"
46 #include <wtf/StackStats.h>
47
48 namespace WebCore {
49
50 class BoundingRectStrokeStyleApplier final : public StrokeStyleApplier {
51 public:
52     BoundingRectStrokeStyleApplier(const RenderSVGShape& renderer)
53         : m_renderer(renderer)
54     {
55     }
56
57     virtual void strokeStyle(GraphicsContext* context) override
58     {
59         SVGRenderSupport::applyStrokeStyleToContext(context, m_renderer.style(), m_renderer);
60     }
61
62 private:
63     const RenderSVGShape& m_renderer;
64 };
65
66 RenderSVGShape::RenderSVGShape(SVGGraphicsElement& element, Ref<RenderStyle>&& style)
67     : RenderSVGModelObject(element, WTFMove(style))
68     , m_needsBoundariesUpdate(false) // Default is false, the cached rects are empty from the beginning.
69     , m_needsShapeUpdate(true) // Default is true, so we grab a Path object once from SVGGraphicsElement.
70     , m_needsTransformUpdate(true) // Default is true, so we grab a AffineTransform object once from SVGGraphicsElement.
71 {
72 }
73
74 RenderSVGShape::~RenderSVGShape()
75 {
76 }
77
78 void RenderSVGShape::updateShapeFromElement()
79 {
80     m_path = std::make_unique<Path>();
81     ASSERT(RenderSVGShape::isEmpty());
82
83     updatePathFromGraphicsElement(&graphicsElement(), path());
84     processMarkerPositions();
85
86     m_fillBoundingBox = calculateObjectBoundingBox();
87     m_strokeBoundingBox = calculateStrokeBoundingBox();
88 }
89
90 bool RenderSVGShape::isEmpty() const
91 {
92     // This function should never be called before assigning a new Path to m_path.
93     // But this bug can happen if this renderer was created and its layout was not
94     // done before painting. Assert this did not happen but do not crash.
95     ASSERT(hasPath());
96     return !hasPath() || path().isEmpty();
97 }
98
99 void RenderSVGShape::fillShape(GraphicsContext& context) const
100 {
101     context.fillPath(path());
102 }
103
104 void RenderSVGShape::strokeShape(GraphicsContext& context) const
105 {
106     ASSERT(m_path);
107     Path* usePath = m_path.get();
108
109     if (hasNonScalingStroke())
110         usePath = nonScalingStrokePath(usePath, nonScalingStrokeTransform());
111
112     context.strokePath(*usePath);
113 }
114
115 bool RenderSVGShape::shapeDependentStrokeContains(const FloatPoint& point)
116 {
117     ASSERT(m_path);
118     BoundingRectStrokeStyleApplier applier(*this);
119
120     if (hasNonScalingStroke()) {
121         AffineTransform nonScalingTransform = nonScalingStrokeTransform();
122         Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTransform);
123
124         return usePath->strokeContains(&applier, nonScalingTransform.mapPoint(point));
125     }
126
127     return m_path->strokeContains(&applier, point);
128 }
129
130 bool RenderSVGShape::shapeDependentFillContains(const FloatPoint& point, const WindRule fillRule) const
131 {
132     return path().contains(point, fillRule);
133 }
134
135 bool RenderSVGShape::fillContains(const FloatPoint& point, bool requiresFill, const WindRule fillRule)
136 {
137     if (!m_fillBoundingBox.contains(point))
138         return false;
139
140     Color fallbackColor;
141     if (requiresFill && !RenderSVGResource::fillPaintingResource(*this, style(), fallbackColor))
142         return false;
143
144     return shapeDependentFillContains(point, fillRule);
145 }
146
147 bool RenderSVGShape::strokeContains(const FloatPoint& point, bool requiresStroke)
148 {
149     if (!strokeBoundingBox().contains(point))
150         return false;
151
152     Color fallbackColor;
153     if (requiresStroke && !RenderSVGResource::strokePaintingResource(*this, style(), fallbackColor))
154         return false;
155
156     return shapeDependentStrokeContains(point);
157 }
158
159 void RenderSVGShape::layout()
160 {
161     StackStats::LayoutCheckPoint layoutCheckPoint;
162     LayoutRepainter repainter(*this, SVGRenderSupport::checkForSVGRepaintDuringLayout(*this) && selfNeedsLayout());
163
164     bool updateCachedBoundariesInParents = false;
165
166     if (m_needsShapeUpdate || m_needsBoundariesUpdate) {
167         updateShapeFromElement();
168         m_needsShapeUpdate = false;
169         updateRepaintBoundingBox();
170         m_needsBoundariesUpdate = false;
171         updateCachedBoundariesInParents = true;
172     }
173
174     if (m_needsTransformUpdate) {
175         m_localTransform = graphicsElement().animatedLocalTransform();
176         m_needsTransformUpdate = false;
177         updateCachedBoundariesInParents = true;
178     }
179
180     // Invalidate all resources of this client if our layout changed.
181     if (everHadLayout() && selfNeedsLayout())
182         SVGResourcesCache::clientLayoutChanged(*this);
183
184     // If our bounds changed, notify the parents.
185     if (updateCachedBoundariesInParents)
186         RenderSVGModelObject::setNeedsBoundariesUpdate();
187
188     repainter.repaintAfterLayout();
189     clearNeedsLayout();
190 }
191
192 Path* RenderSVGShape::nonScalingStrokePath(const Path* path, const AffineTransform& strokeTransform) const
193 {
194     static NeverDestroyed<Path> tempPath;
195
196     tempPath.get() = *path;
197     tempPath.get().transform(strokeTransform);
198
199     return &tempPath.get();
200 }
201
202 bool RenderSVGShape::setupNonScalingStrokeContext(AffineTransform& strokeTransform, GraphicsContextStateSaver& stateSaver)
203 {
204     Optional<AffineTransform> inverse = strokeTransform.inverse();
205     if (!inverse)
206         return false;
207
208     stateSaver.save();
209     stateSaver.context()->concatCTM(inverse.value());
210     return true;
211 }
212
213 AffineTransform RenderSVGShape::nonScalingStrokeTransform() const
214 {
215     return graphicsElement().getScreenCTM(SVGLocatable::DisallowStyleUpdate);
216 }
217
218 bool RenderSVGShape::shouldGenerateMarkerPositions() const
219 {
220     if (!style().svgStyle().hasMarkers())
221         return false;
222
223     if (!graphicsElement().supportsMarkers())
224         return false;
225
226     auto* resources = SVGResourcesCache::cachedResourcesForRenderer(*this);
227     if (!resources)
228         return false;
229
230     return resources->markerStart() || resources->markerMid() || resources->markerEnd();
231 }
232
233 void RenderSVGShape::fillShape(const RenderStyle& style, GraphicsContext& originalContext)
234 {
235     GraphicsContext* context = &originalContext;
236     Color fallbackColor;
237     if (RenderSVGResource* fillPaintingResource = RenderSVGResource::fillPaintingResource(*this, style, fallbackColor)) {
238         if (fillPaintingResource->applyResource(*this, style, context, ApplyToFillMode))
239             fillPaintingResource->postApplyResource(*this, context, ApplyToFillMode, 0, this);
240         else if (fallbackColor.isValid()) {
241             RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
242             fallbackResource->setColor(fallbackColor);
243             if (fallbackResource->applyResource(*this, style, context, ApplyToFillMode))
244                 fallbackResource->postApplyResource(*this, context, ApplyToFillMode, 0, this);
245         }
246     }
247 }
248
249 void RenderSVGShape::strokeShape(const RenderStyle& style, GraphicsContext& originalContext)
250 {
251     GraphicsContext* context = &originalContext;
252     Color fallbackColor;
253     if (RenderSVGResource* strokePaintingResource = RenderSVGResource::strokePaintingResource(*this, style, fallbackColor)) {
254         if (strokePaintingResource->applyResource(*this, style, context, ApplyToStrokeMode))
255             strokePaintingResource->postApplyResource(*this, context, ApplyToStrokeMode, 0, this);
256         else if (fallbackColor.isValid()) {
257             RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
258             fallbackResource->setColor(fallbackColor);
259             if (fallbackResource->applyResource(*this, style, context, ApplyToStrokeMode))
260                 fallbackResource->postApplyResource(*this, context, ApplyToStrokeMode, 0, this);
261         }
262     }
263 }
264
265 void RenderSVGShape::strokeShape(GraphicsContext& context)
266 {
267     if (!style().svgStyle().hasVisibleStroke())
268         return;
269
270     GraphicsContextStateSaver stateSaver(context, false);
271     if (hasNonScalingStroke()) {
272         AffineTransform nonScalingTransform = nonScalingStrokeTransform();
273         if (!setupNonScalingStrokeContext(nonScalingTransform, stateSaver))
274             return;
275     }
276     strokeShape(style(), context);
277 }
278
279 void RenderSVGShape::fillStrokeMarkers(PaintInfo& childPaintInfo)
280 {
281     auto paintOrder = style().svgStyle().paintTypesForPaintOrder();
282     for (unsigned i = 0; i < paintOrder.size(); ++i) {
283         switch (paintOrder.at(i)) {
284         case PaintTypeFill:
285             fillShape(style(), childPaintInfo.context());
286             break;
287         case PaintTypeStroke:
288             strokeShape(childPaintInfo.context());
289             break;
290         case PaintTypeMarkers:
291             if (!m_markerPositions.isEmpty())
292                 drawMarkers(childPaintInfo);
293             break;
294         }
295     }
296 }
297
298 void RenderSVGShape::paint(PaintInfo& paintInfo, const LayoutPoint&)
299 {
300     if (paintInfo.context().paintingDisabled() || paintInfo.phase != PaintPhaseForeground
301         || style().visibility() == HIDDEN || isEmpty())
302         return;
303     FloatRect boundingBox = repaintRectInLocalCoordinates();
304     if (!SVGRenderSupport::paintInfoIntersectsRepaintRect(boundingBox, m_localTransform, paintInfo))
305         return;
306
307     PaintInfo childPaintInfo(paintInfo);
308     GraphicsContextStateSaver stateSaver(childPaintInfo.context());
309     childPaintInfo.applyTransform(m_localTransform);
310
311     if (childPaintInfo.phase == PaintPhaseForeground) {
312         SVGRenderingContext renderingContext(*this, childPaintInfo);
313
314         if (renderingContext.isRenderingPrepared()) {
315             const SVGRenderStyle& svgStyle = style().svgStyle();
316             if (svgStyle.shapeRendering() == SR_CRISPEDGES)
317                 childPaintInfo.context().setShouldAntialias(false);
318
319             fillStrokeMarkers(childPaintInfo);
320         }
321     }
322
323     if (style().outlineWidth())
324         paintOutline(childPaintInfo, IntRect(boundingBox));
325 }
326
327 // This method is called from inside paintOutline() since we call paintOutline()
328 // while transformed to our coord system, return local coords
329 void RenderSVGShape::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint&, const RenderLayerModelObject*)
330 {
331     IntRect rect = enclosingIntRect(repaintRectInLocalCoordinates());
332     if (!rect.isEmpty())
333         rects.append(rect);
334 }
335
336 bool RenderSVGShape::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
337 {
338     // We only draw in the forground phase, so we only hit-test then.
339     if (hitTestAction != HitTestForeground)
340         return false;
341
342     FloatPoint localPoint = m_localTransform.inverse().valueOr(AffineTransform()).mapPoint(pointInParent);
343
344     if (!SVGRenderSupport::pointInClippingArea(*this, localPoint))
345         return false;
346
347     PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_PATH_HITTESTING, request, style().pointerEvents());
348     bool isVisible = (style().visibility() == VISIBLE);
349     if (isVisible || !hitRules.requireVisible) {
350         const SVGRenderStyle& svgStyle = style().svgStyle();
351         WindRule fillRule = svgStyle.fillRule();
352         if (request.svgClipContent())
353             fillRule = svgStyle.clipRule();
354         if ((hitRules.canHitStroke && (svgStyle.hasStroke() || !hitRules.requireStroke) && strokeContains(localPoint, hitRules.requireStroke))
355             || (hitRules.canHitFill && (svgStyle.hasFill() || !hitRules.requireFill) && fillContains(localPoint, hitRules.requireFill, fillRule))) {
356             updateHitTestResult(result, LayoutPoint(localPoint));
357             return true;
358         }
359     }
360     return false;
361 }
362
363 static inline RenderSVGResourceMarker* markerForType(SVGMarkerType type, RenderSVGResourceMarker* markerStart, RenderSVGResourceMarker* markerMid, RenderSVGResourceMarker* markerEnd)
364 {
365     switch (type) {
366     case StartMarker:
367         return markerStart;
368     case MidMarker:
369         return markerMid;
370     case EndMarker:
371         return markerEnd;
372     }
373
374     ASSERT_NOT_REACHED();
375     return 0;
376 }
377
378 FloatRect RenderSVGShape::markerRect(float strokeWidth) const
379 {
380     ASSERT(!m_markerPositions.isEmpty());
381
382     auto* resources = SVGResourcesCache::cachedResourcesForRenderer(*this);
383     ASSERT(resources);
384
385     RenderSVGResourceMarker* markerStart = resources->markerStart();
386     RenderSVGResourceMarker* markerMid = resources->markerMid();
387     RenderSVGResourceMarker* markerEnd = resources->markerEnd();
388     ASSERT(markerStart || markerMid || markerEnd);
389
390     FloatRect boundaries;
391     unsigned size = m_markerPositions.size();
392     for (unsigned i = 0; i < size; ++i) {
393         if (RenderSVGResourceMarker* marker = markerForType(m_markerPositions[i].type, markerStart, markerMid, markerEnd))
394             boundaries.unite(marker->markerBoundaries(marker->markerTransformation(m_markerPositions[i].origin, m_markerPositions[i].angle, strokeWidth)));
395     }
396     return boundaries;
397 }
398
399 FloatRect RenderSVGShape::calculateObjectBoundingBox() const
400 {
401     return path().fastBoundingRect();
402 }
403
404 FloatRect RenderSVGShape::calculateStrokeBoundingBox() const
405 {
406     ASSERT(m_path);
407     FloatRect strokeBoundingBox = m_fillBoundingBox;
408
409     const SVGRenderStyle& svgStyle = style().svgStyle();
410     if (svgStyle.hasStroke()) {
411         BoundingRectStrokeStyleApplier strokeStyle(*this);
412         if (hasNonScalingStroke()) {
413             AffineTransform nonScalingTransform = nonScalingStrokeTransform();
414             if (Optional<AffineTransform> inverse = nonScalingTransform.inverse()) {
415                 Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTransform);
416                 FloatRect strokeBoundingRect = usePath->strokeBoundingRect(&strokeStyle);
417                 strokeBoundingRect = inverse.value().mapRect(strokeBoundingRect);
418                 strokeBoundingBox.unite(strokeBoundingRect);
419             }
420         } else
421             strokeBoundingBox.unite(path().strokeBoundingRect(&strokeStyle));
422     }
423
424     if (!m_markerPositions.isEmpty())
425         strokeBoundingBox.unite(markerRect(strokeWidth()));
426
427     return strokeBoundingBox;
428 }
429
430 void RenderSVGShape::updateRepaintBoundingBox()
431 {
432     m_repaintBoundingBoxExcludingShadow = strokeBoundingBox();
433     SVGRenderSupport::intersectRepaintRectWithResources(*this, m_repaintBoundingBoxExcludingShadow);
434
435     m_repaintBoundingBox = m_repaintBoundingBoxExcludingShadow;
436     SVGRenderSupport::intersectRepaintRectWithShadows(*this, m_repaintBoundingBox);
437 }
438
439 float RenderSVGShape::strokeWidth() const
440 {
441     SVGLengthContext lengthContext(&graphicsElement());
442     return lengthContext.valueForLength(style().svgStyle().strokeWidth());
443 }
444
445 bool RenderSVGShape::hasSmoothStroke() const
446 {
447     const SVGRenderStyle& svgStyle = style().svgStyle();
448     return svgStyle.strokeDashArray().isEmpty()
449         && svgStyle.strokeMiterLimit() == svgStyle.initialStrokeMiterLimit()
450         && svgStyle.joinStyle() == svgStyle.initialJoinStyle()
451         && svgStyle.capStyle() == svgStyle.initialCapStyle();
452 }
453
454 void RenderSVGShape::drawMarkers(PaintInfo& paintInfo)
455 {
456     ASSERT(!m_markerPositions.isEmpty());
457
458     auto* resources = SVGResourcesCache::cachedResourcesForRenderer(*this);
459     if (!resources)
460         return;
461
462     RenderSVGResourceMarker* markerStart = resources->markerStart();
463     RenderSVGResourceMarker* markerMid = resources->markerMid();
464     RenderSVGResourceMarker* markerEnd = resources->markerEnd();
465     if (!markerStart && !markerMid && !markerEnd)
466         return;
467
468     float strokeWidth = this->strokeWidth();
469     unsigned size = m_markerPositions.size();
470     for (unsigned i = 0; i < size; ++i) {
471         if (RenderSVGResourceMarker* marker = markerForType(m_markerPositions[i].type, markerStart, markerMid, markerEnd))
472             marker->draw(paintInfo, marker->markerTransformation(m_markerPositions[i].origin, m_markerPositions[i].angle, strokeWidth));
473     }
474 }
475
476 void RenderSVGShape::processMarkerPositions()
477 {
478     m_markerPositions.clear();
479
480     if (!shouldGenerateMarkerPositions())
481         return;
482
483     ASSERT(m_path);
484
485     SVGMarkerData markerData(m_markerPositions);
486     m_path->apply([&markerData](const PathElement& pathElement) {
487         SVGMarkerData::updateFromPathElement(markerData, pathElement);
488     });
489     markerData.pathIsDone();
490 }
491
492 }