Implement stroke-miterlimit.
[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 "RenderChildIterator.h"
30 #include "RenderElement.h"
31 #include "RenderGeometryMap.h"
32 #include "RenderIterator.h"
33 #include "RenderLayer.h"
34 #include "RenderSVGImage.h"
35 #include "RenderSVGResourceClipper.h"
36 #include "RenderSVGResourceFilter.h"
37 #include "RenderSVGResourceMarker.h"
38 #include "RenderSVGResourceMasker.h"
39 #include "RenderSVGRoot.h"
40 #include "RenderSVGText.h"
41 #include "RenderSVGTransformableContainer.h"
42 #include "RenderSVGViewportContainer.h"
43 #include "SVGResources.h"
44 #include "SVGResourcesCache.h"
45 #include "TransformState.h"
46
47 namespace WebCore {
48
49 FloatRect SVGRenderSupport::repaintRectForRendererInLocalCoordinatesExcludingSVGShadow(const RenderElement& renderer)
50 {
51     // FIXME: Add support for RenderSVGBlock.
52
53     if (is<RenderSVGModelObject>(renderer))
54         return downcast<RenderSVGModelObject>(renderer).repaintRectInLocalCoordinatesExcludingSVGShadow();
55
56     return renderer.repaintRectInLocalCoordinates();
57 }
58
59 LayoutRect SVGRenderSupport::clippedOverflowRectForRepaint(const RenderElement& renderer, const RenderLayerModelObject* repaintContainer)
60 {
61     // Return early for any cases where we don't actually paint
62     if (renderer.style().visibility() != VISIBLE && !renderer.enclosingLayer()->hasVisibleContent())
63         return LayoutRect();
64
65     // Pass our local paint rect to computeRectForRepaint() which will
66     // map to parent coords and recurse up the parent chain.
67     FloatRect repaintRect = repaintRectForRendererInLocalCoordinatesExcludingSVGShadow(renderer);
68     const SVGRenderStyle& svgStyle = renderer.style().svgStyle();
69     if (const ShadowData* shadow = svgStyle.shadow())
70         shadow->adjustRectForShadow(repaintRect);
71     return enclosingLayoutRect(renderer.computeFloatRectForRepaint(repaintRect, repaintContainer));
72 }
73
74 FloatRect SVGRenderSupport::computeFloatRectForRepaint(const RenderElement& renderer, const FloatRect& repaintRect, const RenderLayerModelObject* repaintContainer, bool fixed)
75 {
76     FloatRect adjustedRect = repaintRect;
77     const SVGRenderStyle& svgStyle = renderer.style().svgStyle();
78     if (const ShadowData* shadow = svgStyle.shadow())
79         shadow->adjustRectForShadow(adjustedRect);
80     adjustedRect.inflate(renderer.style().outlineWidth());
81
82     // Translate to coords in our parent renderer, and then call computeFloatRectForRepaint() on our parent.
83     adjustedRect = renderer.localToParentTransform().mapRect(adjustedRect);
84     return renderer.parent()->computeFloatRectForRepaint(adjustedRect, repaintContainer, fixed);
85 }
86
87 const RenderElement& SVGRenderSupport::localToParentTransform(const RenderElement& renderer, AffineTransform &transform)
88 {
89     ASSERT(renderer.parent());
90     auto& parent = *renderer.parent();
91
92     // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform
93     // to map an element from SVG viewport coordinates to CSS box coordinates.
94     if (is<RenderSVGRoot>(parent))
95         transform = downcast<RenderSVGRoot>(parent).localToBorderBoxTransform() * renderer.localToParentTransform();
96     else
97         transform = renderer.localToParentTransform();
98
99     return parent;
100 }
101
102 void SVGRenderSupport::mapLocalToContainer(const RenderElement& renderer, const RenderLayerModelObject* repaintContainer, TransformState& transformState, bool* wasFixed)
103 {
104     AffineTransform transform;
105     auto& parent = localToParentTransform(renderer, transform);
106
107     transformState.applyTransform(transform);
108
109     MapCoordinatesFlags mode = UseTransforms;
110     parent.mapLocalToContainer(repaintContainer, transformState, mode, wasFixed);
111 }
112
113 const RenderElement* SVGRenderSupport::pushMappingToContainer(const RenderElement& renderer, const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap)
114 {
115     ASSERT_UNUSED(ancestorToStopAt, ancestorToStopAt != &renderer);
116
117     AffineTransform transform;
118     auto& parent = localToParentTransform(renderer, transform);
119
120     geometryMap.push(&renderer, transform);
121     return &parent;
122 }
123
124 bool SVGRenderSupport::checkForSVGRepaintDuringLayout(const RenderElement& renderer)
125 {
126     if (!renderer.checkForRepaintDuringLayout())
127         return false;
128     // When a parent container is transformed in SVG, all children will be painted automatically
129     // so we are able to skip redundant repaint checks.
130     auto parent = renderer.parent();
131     return !(is<RenderSVGContainer>(parent) && downcast<RenderSVGContainer>(*parent).didTransformToRootUpdate());
132 }
133
134 // Update a bounding box taking into account the validity of the other bounding box.
135 static inline void updateObjectBoundingBox(FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, const RenderObject* other, FloatRect otherBoundingBox)
136 {
137     bool otherValid = is<RenderSVGContainer>(*other) ? downcast<RenderSVGContainer>(*other).isObjectBoundingBoxValid() : true;
138     if (!otherValid)
139         return;
140
141     if (!objectBoundingBoxValid) {
142         objectBoundingBox = otherBoundingBox;
143         objectBoundingBoxValid = true;
144         return;
145     }
146
147     objectBoundingBox.uniteEvenIfEmpty(otherBoundingBox);
148 }
149
150 void SVGRenderSupport::computeContainerBoundingBoxes(const RenderElement& container, FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, FloatRect& strokeBoundingBox, FloatRect& repaintBoundingBox)
151 {
152     objectBoundingBox = FloatRect();
153     objectBoundingBoxValid = false;
154     strokeBoundingBox = FloatRect();
155
156     // When computing the strokeBoundingBox, we use the repaintRects of the container's children so that the container's stroke includes
157     // the resources applied to the children (such as clips and filters). This allows filters applied to containers to correctly bound
158     // the children, and also improves inlining of SVG content, as the stroke bound is used in that situation also.
159     for (auto& current : childrenOfType<RenderObject>(container)) {
160         if (current.isSVGHiddenContainer())
161             continue;
162
163         // Don't include elements in the union that do not render.
164         if (is<RenderSVGShape>(current) && downcast<RenderSVGShape>(current).isRenderingDisabled())
165             continue;
166
167         const AffineTransform& transform = current.localToParentTransform();
168         if (transform.isIdentity()) {
169             updateObjectBoundingBox(objectBoundingBox, objectBoundingBoxValid, &current, current.objectBoundingBox());
170             strokeBoundingBox.unite(current.repaintRectInLocalCoordinates());
171         } else {
172             updateObjectBoundingBox(objectBoundingBox, objectBoundingBoxValid, &current, transform.mapRect(current.objectBoundingBox()));
173             strokeBoundingBox.unite(transform.mapRect(current.repaintRectInLocalCoordinates()));
174         }
175     }
176
177     repaintBoundingBox = strokeBoundingBox;
178 }
179
180 bool SVGRenderSupport::paintInfoIntersectsRepaintRect(const FloatRect& localRepaintRect, const AffineTransform& localTransform, const PaintInfo& paintInfo)
181 {
182     if (localTransform.isIdentity())
183         return localRepaintRect.intersects(paintInfo.rect);
184
185     return localTransform.mapRect(localRepaintRect).intersects(paintInfo.rect);
186 }
187
188 RenderSVGRoot* SVGRenderSupport::findTreeRootObject(RenderElement& start)
189 {
190     return lineageOfType<RenderSVGRoot>(start).first();
191 }
192
193 const RenderSVGRoot* SVGRenderSupport::findTreeRootObject(const RenderElement& start)
194 {
195     return lineageOfType<RenderSVGRoot>(start).first();
196 }
197
198 static inline void invalidateResourcesOfChildren(RenderElement& renderer)
199 {
200     ASSERT(!renderer.needsLayout());
201     if (auto* resources = SVGResourcesCache::cachedResourcesForRenderer(renderer))
202         resources->removeClientFromCache(renderer, false);
203
204     for (auto& child : childrenOfType<RenderElement>(renderer))
205         invalidateResourcesOfChildren(child);
206 }
207
208 static inline bool layoutSizeOfNearestViewportChanged(const RenderElement& renderer)
209 {
210     const RenderElement* start = &renderer;
211     while (start && !is<RenderSVGRoot>(*start) && !is<RenderSVGViewportContainer>(*start))
212         start = start->parent();
213
214     ASSERT(start);
215     if (is<RenderSVGViewportContainer>(*start))
216         return downcast<RenderSVGViewportContainer>(*start).isLayoutSizeChanged();
217
218     return downcast<RenderSVGRoot>(*start).isLayoutSizeChanged();
219 }
220
221 bool SVGRenderSupport::transformToRootChanged(RenderElement* ancestor)
222 {
223     while (ancestor && !is<RenderSVGRoot>(*ancestor)) {
224         if (is<RenderSVGTransformableContainer>(*ancestor))
225             return downcast<RenderSVGTransformableContainer>(*ancestor).didTransformToRootUpdate();
226         if (is<RenderSVGViewportContainer>(*ancestor))
227             return downcast<RenderSVGViewportContainer>(*ancestor).didTransformToRootUpdate();
228         ancestor = ancestor->parent();
229     }
230
231     return false;
232 }
233
234 void SVGRenderSupport::layoutDifferentRootIfNeeded(const RenderElement& renderer)
235 {
236     if (auto* resources = SVGResourcesCache::cachedResourcesForRenderer(renderer)) {
237         auto* svgRoot = SVGRenderSupport::findTreeRootObject(renderer);
238         ASSERT(svgRoot);
239         resources->layoutDifferentRootIfNeeded(svgRoot);
240     }
241 }
242
243 void SVGRenderSupport::layoutChildren(RenderElement& start, bool selfNeedsLayout)
244 {
245     bool layoutSizeChanged = layoutSizeOfNearestViewportChanged(start);
246     bool transformChanged = transformToRootChanged(&start);
247     bool hasSVGShadow = rendererHasSVGShadow(start);
248     bool needsBoundariesUpdate = start.needsBoundariesUpdate();
249     HashSet<RenderElement*> elementsThatDidNotReceiveLayout;
250
251     for (auto& child : childrenOfType<RenderObject>(start)) {
252         bool needsLayout = selfNeedsLayout;
253         bool childEverHadLayout = child.everHadLayout();
254
255         if (needsBoundariesUpdate && hasSVGShadow) {
256             // If we have a shadow, our shadow is baked into our children's cached boundaries,
257             // so they need to update.
258             child.setNeedsBoundariesUpdate();
259             needsLayout = true;
260         }
261
262         if (transformChanged) {
263             // If the transform changed we need to update the text metrics (note: this also happens for layoutSizeChanged=true).
264             if (is<RenderSVGText>(child))
265                 downcast<RenderSVGText>(child).setNeedsTextMetricsUpdate();
266             needsLayout = true;
267         }
268
269         if (layoutSizeChanged && is<SVGElement>(*child.node())) {
270             // When selfNeedsLayout is false and the layout size changed, we have to check whether this child uses relative lengths
271             auto& element = downcast<SVGElement>(*child.node());
272             if (element.hasRelativeLengths()) {
273                 // When the layout size changed and when using relative values tell the RenderSVGShape to update its shape object
274                 if (is<RenderSVGShape>(child))
275                     downcast<RenderSVGShape>(child).setNeedsShapeUpdate();
276                 else if (is<RenderSVGText>(child)) {
277                     auto& svgText = downcast<RenderSVGText>(child);
278                     svgText.setNeedsTextMetricsUpdate();
279                     svgText.setNeedsPositioningValuesUpdate();
280                 }
281
282                 needsLayout = true;
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().value_or(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(style.strokeWidth()));
431     context->setLineCap(style.capStyle());
432     context->setLineJoin(style.joinStyle());
433     if (style.joinStyle() == MiterJoin)
434         context->setMiterLimit(style.strokeMiterLimit());
435
436     const Vector<SVGLengthValue>& dashes = svgStyle.strokeDashArray();
437     if (dashes.isEmpty())
438         context->setStrokeStyle(SolidStroke);
439     else {
440         DashArray dashArray;
441         dashArray.reserveInitialCapacity(dashes.size());
442         bool canSetLineDash = false;
443
444         for (auto& dash : dashes) {
445             dashArray.uncheckedAppend(dash.value(lengthContext));
446             if (dashArray.last() > 0)
447                 canSetLineDash = true;
448         }
449
450         if (canSetLineDash)
451             context->setLineDash(dashArray, lengthContext.valueForLength(svgStyle.strokeDashOffset()));
452         else
453             context->setStrokeStyle(SolidStroke);
454     }
455 }
456
457 void SVGRenderSupport::childAdded(RenderElement& parent, RenderObject& child)
458 {
459     SVGRenderSupport::setRendererHasSVGShadow(child, SVGRenderSupport::rendererHasSVGShadow(parent) || SVGRenderSupport::rendererHasSVGShadow(child));
460 }
461
462 void SVGRenderSupport::styleChanged(RenderElement& renderer, const RenderStyle* oldStyle)
463 {
464     auto parent = renderer.parent();
465     SVGRenderSupport::setRendererHasSVGShadow(renderer, (parent && SVGRenderSupport::rendererHasSVGShadow(*parent)) || renderer.style().svgStyle().shadow());
466
467 #if ENABLE(CSS_COMPOSITING)
468     if (renderer.element() && renderer.element()->isSVGElement() && (!oldStyle || renderer.style().hasBlendMode() != oldStyle->hasBlendMode()))
469         SVGRenderSupport::updateMaskedAncestorShouldIsolateBlending(renderer);
470 #else
471     UNUSED_PARAM(oldStyle);
472 #endif
473 }
474
475 #if ENABLE(CSS_COMPOSITING)
476 bool SVGRenderSupport::isolatesBlending(const RenderStyle& style)
477 {
478     return style.svgStyle().isolatesBlending() || style.hasFilter() || style.hasBlendMode() || style.opacity() < 1.0f;
479 }
480
481 void SVGRenderSupport::updateMaskedAncestorShouldIsolateBlending(const RenderElement& renderer)
482 {
483     ASSERT(renderer.element());
484     ASSERT(renderer.element()->isSVGElement());
485
486     bool maskedAncestorShouldIsolateBlending = renderer.style().hasBlendMode();
487     for (auto* ancestor = renderer.element()->parentElement(); ancestor && ancestor->isSVGElement(); ancestor = ancestor->parentElement()) {
488         if (!downcast<SVGElement>(*ancestor).isSVGGraphicsElement() || !isolatesBlending(*ancestor->computedStyle()))
489             continue;
490
491         if (ancestor->computedStyle()->svgStyle().hasMasker())
492             downcast<SVGGraphicsElement>(*ancestor).setShouldIsolateBlending(maskedAncestorShouldIsolateBlending);
493
494         return;
495     }
496 }
497 #endif
498 }