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