44abb64062e06e5d89bffab70c735a791cdc126a
[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 const RenderSVGRoot& SVGRenderSupport::findTreeRootObject(const RenderElement& start)
188 {
189     return *lineageOfType<RenderSVGRoot>(start).first();
190 }
191
192 static inline void invalidateResourcesOfChildren(RenderElement& renderer)
193 {
194     ASSERT(!renderer.needsLayout());
195     if (auto* resources = SVGResourcesCache::cachedResourcesForRenderer(renderer))
196         resources->removeClientFromCache(renderer, false);
197
198     for (auto& child : childrenOfType<RenderElement>(renderer))
199         invalidateResourcesOfChildren(child);
200 }
201
202 static inline bool layoutSizeOfNearestViewportChanged(const RenderElement& renderer)
203 {
204     const RenderElement* start = &renderer;
205     while (start && !is<RenderSVGRoot>(*start) && !is<RenderSVGViewportContainer>(*start))
206         start = start->parent();
207
208     ASSERT(start);
209     if (is<RenderSVGViewportContainer>(*start))
210         return downcast<RenderSVGViewportContainer>(*start).isLayoutSizeChanged();
211
212     return downcast<RenderSVGRoot>(*start).isLayoutSizeChanged();
213 }
214
215 bool SVGRenderSupport::transformToRootChanged(RenderElement* ancestor)
216 {
217     while (ancestor && !is<RenderSVGRoot>(*ancestor)) {
218         if (is<RenderSVGTransformableContainer>(*ancestor))
219             return downcast<RenderSVGTransformableContainer>(*ancestor).didTransformToRootUpdate();
220         if (is<RenderSVGViewportContainer>(*ancestor))
221             return downcast<RenderSVGViewportContainer>(*ancestor).didTransformToRootUpdate();
222         ancestor = ancestor->parent();
223     }
224
225     return false;
226 }
227
228 void SVGRenderSupport::layoutChildren(RenderElement& start, bool selfNeedsLayout)
229 {
230     bool layoutSizeChanged = layoutSizeOfNearestViewportChanged(start);
231     bool transformChanged = transformToRootChanged(&start);
232     bool hasSVGShadow = rendererHasSVGShadow(start);
233     bool needsBoundariesUpdate = start.needsBoundariesUpdate();
234     HashSet<RenderElement*> elementsThatDidNotReceiveLayout;
235
236     for (RenderObject* child = start.firstChild(); child; child = child->nextSibling()) {
237         bool needsLayout = selfNeedsLayout;
238         bool childEverHadLayout = child->everHadLayout();
239
240         if (needsBoundariesUpdate && hasSVGShadow) {
241             // If we have a shadow, our shadow is baked into our children's cached boundaries,
242             // so they need to update.
243             child->setNeedsBoundariesUpdate();
244             needsLayout = true;
245         }
246
247         if (transformChanged) {
248             // If the transform changed we need to update the text metrics (note: this also happens for layoutSizeChanged=true).
249             if (is<RenderSVGText>(*child))
250                 downcast<RenderSVGText>(*child).setNeedsTextMetricsUpdate();
251             needsLayout = true;
252         }
253
254         if (layoutSizeChanged) {
255             // When selfNeedsLayout is false and the layout size changed, we have to check whether this child uses relative lengths
256             if (SVGElement* element = is<SVGElement>(*child->node()) ? downcast<SVGElement>(child->node()) : nullptr) {
257                 if (element->hasRelativeLengths()) {
258                     // When the layout size changed and when using relative values tell the RenderSVGShape to update its shape object
259                     if (is<RenderSVGShape>(*child))
260                         downcast<RenderSVGShape>(*child).setNeedsShapeUpdate();
261                     else if (is<RenderSVGText>(*child)) {
262                         RenderSVGText& svgText = downcast<RenderSVGText>(*child);
263                         svgText.setNeedsTextMetricsUpdate();
264                         svgText.setNeedsPositioningValuesUpdate();
265                     }
266
267                     needsLayout = true;
268                 }
269             }
270         }
271
272         if (needsLayout)
273             child->setNeedsLayout(MarkOnlyThis);
274
275         if (child->needsLayout()) {
276             downcast<RenderElement>(*child).layout();
277             // Renderers are responsible for repainting themselves when changing, except
278             // for the initial paint to avoid potential double-painting caused by non-sensical "old" bounds.
279             // We could handle this in the individual objects, but for now it's easier to have
280             // parent containers call repaint().  (RenderBlock::layout* has similar logic.)
281             if (!childEverHadLayout)
282                 child->repaint();
283         } else if (layoutSizeChanged && is<RenderElement>(*child))
284             elementsThatDidNotReceiveLayout.add(downcast<RenderElement>(child));
285
286         ASSERT(!child->needsLayout());
287     }
288
289     if (!layoutSizeChanged) {
290         ASSERT(elementsThatDidNotReceiveLayout.isEmpty());
291         return;
292     }
293
294     // If the layout size changed, invalidate all resources of all children that didn't go through the layout() code path.
295     for (auto* element : elementsThatDidNotReceiveLayout)
296         invalidateResourcesOfChildren(*element);
297 }
298
299 bool SVGRenderSupport::isOverflowHidden(const RenderElement& renderer)
300 {
301     // RenderSVGRoot should never query for overflow state - it should always clip itself to the initial viewport size.
302     ASSERT(!renderer.isDocumentElementRenderer());
303
304     return renderer.style().overflowX() == OHIDDEN || renderer.style().overflowX() == OSCROLL;
305 }
306
307 bool SVGRenderSupport::rendererHasSVGShadow(const RenderObject& renderer)
308 {
309     // FIXME: Add support for RenderSVGBlock.
310
311     if (is<RenderSVGModelObject>(renderer))
312         return downcast<RenderSVGModelObject>(renderer).hasSVGShadow();
313
314     if (is<RenderSVGRoot>(renderer))
315         return downcast<RenderSVGRoot>(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 (is<RenderSVGModelObject>(renderer)) {
325         downcast<RenderSVGModelObject>(renderer).setHasSVGShadow(hasShadow);
326         return;
327     }
328
329     if (is<RenderSVGRoot>(renderer))
330         downcast<RenderSVGRoot>(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().valueOr(AffineTransform());
359     repaintRect = rootToLocalTransform.mapRect(repaintRect);
360 }
361
362 void SVGRenderSupport::intersectRepaintRectWithResources(const RenderElement& renderer, FloatRect& repaintRect)
363 {
364     auto* resources = SVGResourcesCache::cachedResourcesForRenderer(renderer);
365     if (!resources)
366         return;
367
368     if (RenderSVGResourceFilter* filter = resources->filter())
369         repaintRect = filter->resourceBoundingBox(renderer);
370
371     if (RenderSVGResourceClipper* clipper = resources->clipper())
372         repaintRect.intersect(clipper->resourceBoundingBox(renderer));
373
374     if (RenderSVGResourceMasker* masker = resources->masker())
375         repaintRect.intersect(masker->resourceBoundingBox(renderer));
376 }
377
378 bool SVGRenderSupport::filtersForceContainerLayout(const RenderElement& renderer)
379 {
380     // If any of this container's children need to be laid out, and a filter is applied
381     // to the container, we need to repaint the entire container.
382     if (!renderer.normalChildNeedsLayout())
383         return false;
384
385     auto* resources = SVGResourcesCache::cachedResourcesForRenderer(renderer);
386     if (!resources || !resources->filter())
387         return false;
388
389     return true;
390 }
391
392 bool SVGRenderSupport::pointInClippingArea(const RenderElement& renderer, const FloatPoint& point)
393 {
394     // We just take clippers into account to determine if a point is on the node. The Specification may
395     // change later and we also need to check maskers.
396     auto* resources = SVGResourcesCache::cachedResourcesForRenderer(renderer);
397     if (!resources)
398         return true;
399
400     if (RenderSVGResourceClipper* clipper = resources->clipper())
401         return clipper->hitTestClipContent(renderer.objectBoundingBox(), point);
402
403     return true;
404 }
405
406 void SVGRenderSupport::applyStrokeStyleToContext(GraphicsContext* context, const RenderStyle& style, const RenderElement& renderer)
407 {
408     ASSERT(context);
409     ASSERT(renderer.element());
410     ASSERT(renderer.element()->isSVGElement());
411
412     const SVGRenderStyle& svgStyle = style.svgStyle();
413
414     SVGLengthContext lengthContext(downcast<SVGElement>(renderer.element()));
415     context->setStrokeThickness(lengthContext.valueForLength(svgStyle.strokeWidth()));
416     context->setLineCap(svgStyle.capStyle());
417     context->setLineJoin(svgStyle.joinStyle());
418     if (svgStyle.joinStyle() == MiterJoin)
419         context->setMiterLimit(svgStyle.strokeMiterLimit());
420
421     const Vector<SVGLength>& dashes = svgStyle.strokeDashArray();
422     if (dashes.isEmpty())
423         context->setStrokeStyle(SolidStroke);
424     else {
425         DashArray dashArray;
426         dashArray.reserveInitialCapacity(dashes.size());
427         for (auto& dash : dashes)
428             dashArray.uncheckedAppend(dash.value(lengthContext));
429
430         context->setLineDash(dashArray, lengthContext.valueForLength(svgStyle.strokeDashOffset()));
431     }
432 }
433
434 void SVGRenderSupport::childAdded(RenderElement& parent, RenderObject& child)
435 {
436     SVGRenderSupport::setRendererHasSVGShadow(child, SVGRenderSupport::rendererHasSVGShadow(parent) || SVGRenderSupport::rendererHasSVGShadow(child));
437 }
438
439 void SVGRenderSupport::styleChanged(RenderElement& renderer, const RenderStyle* oldStyle)
440 {
441     auto parent = renderer.parent();
442     SVGRenderSupport::setRendererHasSVGShadow(renderer, (parent && SVGRenderSupport::rendererHasSVGShadow(*parent)) || renderer.style().svgStyle().shadow());
443
444 #if ENABLE(CSS_COMPOSITING)
445     if (renderer.element() && renderer.element()->isSVGElement() && (!oldStyle || renderer.style().hasBlendMode() != oldStyle->hasBlendMode()))
446         SVGRenderSupport::updateMaskedAncestorShouldIsolateBlending(renderer);
447 #else
448     UNUSED_PARAM(oldStyle);
449 #endif
450 }
451
452 #if ENABLE(CSS_COMPOSITING)
453 bool SVGRenderSupport::isolatesBlending(const RenderStyle& style)
454 {
455     return style.svgStyle().isolatesBlending() || style.hasFilter() || style.hasBlendMode() || style.opacity() < 1.0f;
456 }
457
458 void SVGRenderSupport::updateMaskedAncestorShouldIsolateBlending(const RenderElement& renderer)
459 {
460     ASSERT(renderer.element());
461     ASSERT(renderer.element()->isSVGElement());
462
463     bool maskedAncestorShouldIsolateBlending = renderer.style().hasBlendMode();
464     for (auto* ancestor = renderer.element()->parentElement(); ancestor && ancestor->isSVGElement(); ancestor = ancestor->parentElement()) {
465         if (!downcast<SVGElement>(*ancestor).isSVGGraphicsElement() || !isolatesBlending(*ancestor->computedStyle()))
466             continue;
467
468         if (ancestor->computedStyle()->svgStyle().hasMasker())
469             downcast<SVGGraphicsElement>(*ancestor).setShouldIsolateBlending(maskedAncestorShouldIsolateBlending);
470
471         return;
472     }
473 }
474 #endif
475 }