0dc107780f9b8bccd71b482f422f9b1e2a2645a2
[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         // Don't include elements in the union that do not render.
161         if (current->isSVGShape() && toRenderSVGShape(current)->isRenderingDisabled())
162             continue;
163
164         const AffineTransform& transform = current->localToParentTransform();
165         if (transform.isIdentity()) {
166             updateObjectBoundingBox(objectBoundingBox, objectBoundingBoxValid, current, current->objectBoundingBox());
167             strokeBoundingBox.unite(current->repaintRectInLocalCoordinates());
168         } else {
169             updateObjectBoundingBox(objectBoundingBox, objectBoundingBoxValid, current, transform.mapRect(current->objectBoundingBox()));
170             strokeBoundingBox.unite(transform.mapRect(current->repaintRectInLocalCoordinates()));
171         }
172     }
173
174     repaintBoundingBox = strokeBoundingBox;
175 }
176
177 bool SVGRenderSupport::paintInfoIntersectsRepaintRect(const FloatRect& localRepaintRect, const AffineTransform& localTransform, const PaintInfo& paintInfo)
178 {
179     if (localTransform.isIdentity())
180         return localRepaintRect.intersects(paintInfo.rect);
181
182     return localTransform.mapRect(localRepaintRect).intersects(paintInfo.rect);
183 }
184
185 const RenderSVGRoot& SVGRenderSupport::findTreeRootObject(const RenderElement& start)
186 {
187     return *lineageOfType<RenderSVGRoot>(start).first();
188 }
189
190 static inline void invalidateResourcesOfChildren(RenderElement& renderer)
191 {
192     ASSERT(!renderer.needsLayout());
193     if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer))
194         resources->removeClientFromCache(renderer, false);
195
196     for (auto& child : childrenOfType<RenderElement>(renderer))
197         invalidateResourcesOfChildren(child);
198 }
199
200 static inline bool layoutSizeOfNearestViewportChanged(const RenderElement& renderer)
201 {
202     const RenderElement* start = &renderer;
203     while (start && !start->isSVGRoot() && !start->isSVGViewportContainer())
204         start = start->parent();
205
206     ASSERT(start);
207     ASSERT(start->isSVGRoot() || start->isSVGViewportContainer());
208     if (start->isSVGViewportContainer())
209         return toRenderSVGViewportContainer(start)->isLayoutSizeChanged();
210
211     return toRenderSVGRoot(start)->isLayoutSizeChanged();
212 }
213
214 bool SVGRenderSupport::transformToRootChanged(RenderElement* ancestor)
215 {
216     while (ancestor && !ancestor->isSVGRoot()) {
217         if (ancestor->isSVGTransformableContainer())
218             return toRenderSVGContainer(ancestor)->didTransformToRootUpdate();
219         if (ancestor->isSVGViewportContainer())
220             return toRenderSVGViewportContainer(ancestor)->didTransformToRootUpdate();
221         ancestor = ancestor->parent();
222     }
223
224     return false;
225 }
226
227 void SVGRenderSupport::layoutChildren(RenderElement& start, bool selfNeedsLayout)
228 {
229     bool layoutSizeChanged = layoutSizeOfNearestViewportChanged(start);
230     bool transformChanged = transformToRootChanged(&start);
231     bool hasSVGShadow = rendererHasSVGShadow(start);
232     bool needsBoundariesUpdate = start.needsBoundariesUpdate();
233     HashSet<RenderElement*> elementsThatDidNotReceiveLayout;
234
235     for (RenderObject* child = start.firstChild(); child; child = child->nextSibling()) {
236         bool needsLayout = selfNeedsLayout;
237         bool childEverHadLayout = child->everHadLayout();
238
239         if (needsBoundariesUpdate && hasSVGShadow) {
240             // If we have a shadow, our shadow is baked into our children's cached boundaries,
241             // so they need to update.
242             child->setNeedsBoundariesUpdate();
243             needsLayout = true;
244         }
245
246         if (transformChanged) {
247             // If the transform changed we need to update the text metrics (note: this also happens for layoutSizeChanged=true).
248             if (child->isSVGText())
249                 toRenderSVGText(child)->setNeedsTextMetricsUpdate();
250             needsLayout = true;
251         }
252
253         if (layoutSizeChanged) {
254             // When selfNeedsLayout is false and the layout size changed, we have to check whether this child uses relative lengths
255             if (SVGElement* element = child->node()->isSVGElement() ? toSVGElement(child->node()) : 0) {
256                 if (element->hasRelativeLengths()) {
257                     // When the layout size changed and when using relative values tell the RenderSVGShape to update its shape object
258                     if (child->isSVGShape())
259                         toRenderSVGShape(child)->setNeedsShapeUpdate();
260                     else if (child->isSVGText()) {
261                         toRenderSVGText(child)->setNeedsTextMetricsUpdate();
262                         toRenderSVGText(child)->setNeedsPositioningValuesUpdate();
263                     }
264
265                     needsLayout = true;
266                 }
267             }
268         }
269
270         if (needsLayout)
271             child->setNeedsLayout(MarkOnlyThis);
272
273         if (child->needsLayout()) {
274             toRenderElement(child)->layout();
275             // Renderers are responsible for repainting themselves when changing, except
276             // for the initial paint to avoid potential double-painting caused by non-sensical "old" bounds.
277             // We could handle this in the individual objects, but for now it's easier to have
278             // parent containers call repaint().  (RenderBlock::layout* has similar logic.)
279             if (!childEverHadLayout)
280                 child->repaint();
281         } else if (layoutSizeChanged && child->isRenderElement())
282             elementsThatDidNotReceiveLayout.add(toRenderElement(child));
283
284         ASSERT(!child->needsLayout());
285     }
286
287     if (!layoutSizeChanged) {
288         ASSERT(elementsThatDidNotReceiveLayout.isEmpty());
289         return;
290     }
291
292     // If the layout size changed, invalidate all resources of all children that didn't go through the layout() code path.
293     for (auto* element : elementsThatDidNotReceiveLayout)
294         invalidateResourcesOfChildren(*element);
295 }
296
297 bool SVGRenderSupport::isOverflowHidden(const RenderElement& renderer)
298 {
299     // RenderSVGRoot should never query for overflow state - it should always clip itself to the initial viewport size.
300     ASSERT(!renderer.isRoot());
301
302     return renderer.style().overflowX() == OHIDDEN || renderer.style().overflowX() == OSCROLL;
303 }
304
305 bool SVGRenderSupport::rendererHasSVGShadow(const RenderObject& renderer)
306 {
307     // FIXME: Add support for RenderSVGBlock.
308
309     if (renderer.isSVGShape() || renderer.isSVGImage() || renderer.isSVGContainer())
310         return toRenderSVGModelObject(renderer).hasSVGShadow();
311
312     if (renderer.isSVGRoot())
313         return toRenderSVGRoot(renderer).hasSVGShadow();
314
315     return false;
316 }
317
318 void SVGRenderSupport::setRendererHasSVGShadow(RenderObject& renderer, bool hasShadow)
319 {
320     // FIXME: Add support for RenderSVGBlock.
321
322     if (renderer.isSVGShape() || renderer.isSVGImage() || renderer.isSVGContainer()) {
323         toRenderSVGModelObject(renderer).setHasSVGShadow(hasShadow);
324         return;
325     }
326
327     if (renderer.isSVGRoot())
328         toRenderSVGRoot(renderer).setHasSVGShadow(hasShadow);
329 }
330
331 void SVGRenderSupport::intersectRepaintRectWithShadows(const RenderElement& renderer, FloatRect& repaintRect)
332 {
333     // Since -webkit-svg-shadow enables shadow drawing for its children, but its children
334     // don't inherit the shadow in their SVGRenderStyle, we need to search our parents for
335     // shadows in order to correctly compute our repaint rect.
336
337     auto currentObject = &renderer;
338
339     AffineTransform localToRootTransform;
340
341     while (currentObject && rendererHasSVGShadow(*currentObject)) {
342         const RenderStyle& style = currentObject->style();
343         const SVGRenderStyle& svgStyle = style.svgStyle();
344         if (const ShadowData* shadow = svgStyle.shadow())
345             shadow->adjustRectForShadow(repaintRect);
346
347         repaintRect = currentObject->localToParentTransform().mapRect(repaintRect);
348         localToRootTransform *= currentObject->localToParentTransform();
349
350         currentObject = currentObject->parent();
351     };
352
353     if (localToRootTransform.isIdentity())
354         return;
355
356     AffineTransform rootToLocalTransform = localToRootTransform.inverse();
357     repaintRect = rootToLocalTransform.mapRect(repaintRect);
358 }
359
360 void SVGRenderSupport::intersectRepaintRectWithResources(const RenderElement& renderer, FloatRect& repaintRect)
361 {
362     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer);
363     if (!resources)
364         return;
365
366 #if ENABLE(FILTERS)
367     if (RenderSVGResourceFilter* filter = resources->filter())
368         repaintRect = filter->resourceBoundingBox(renderer);
369 #endif
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     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(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     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(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(toSVGElement(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.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 (!toSVGElement(ancestor)->isSVGGraphicsElement() || !isolatesBlending(*ancestor->computedStyle()))
466             continue;
467
468         if (ancestor->computedStyle()->svgStyle().hasMasker())
469             toSVGGraphicsElement(ancestor)->setShouldIsolateBlending(maskedAncestorShouldIsolateBlending);
470
471         return;
472     }
473 }
474 #endif
475 }