Index: Source/core/rendering/svg/SVGRenderSupport.cpp |
diff --git a/Source/core/rendering/svg/SVGRenderSupport.cpp b/Source/core/rendering/svg/SVGRenderSupport.cpp |
index 722c7164e069689af4adc9f5fab26a3a1da5cd91..f5b9c4d64cce15be5ce9aba4654e1d30e228d43f 100644 |
--- a/Source/core/rendering/svg/SVGRenderSupport.cpp |
+++ b/Source/core/rendering/svg/SVGRenderSupport.cpp |
@@ -25,6 +25,7 @@ |
#include "config.h" |
#include "core/rendering/svg/SVGRenderSupport.h" |
+#include "core/rendering/PaintInfo.h" |
#include "core/rendering/RenderGeometryMap.h" |
#include "core/rendering/RenderLayer.h" |
#include "core/rendering/SubtreeLayoutScope.h" |
@@ -32,6 +33,11 @@ |
#include "core/rendering/svg/RenderSVGResourceClipper.h" |
#include "core/rendering/svg/RenderSVGResourceFilter.h" |
#include "core/rendering/svg/RenderSVGResourceMasker.h" |
+#include "core/rendering/svg/RenderSVGRoot.h" |
+#include "core/rendering/svg/RenderSVGText.h" |
+#include "core/rendering/svg/RenderSVGViewportContainer.h" |
+#include "core/rendering/svg/SVGResources.h" |
+#include "core/rendering/svg/SVGResourcesCache.h" |
#include "core/svg/SVGElement.h" |
#include "platform/geometry/TransformState.h" |
@@ -104,6 +110,44 @@ bool SVGRenderSupport::checkForSVGRepaintDuringLayout(RenderObject* object) |
return !(parent && parent->isSVGContainer() && toRenderSVGContainer(parent)->didTransformToRootUpdate()); |
} |
+// Update a bounding box taking into account the validity of the other bounding box. |
+inline void SVGRenderSupport::updateObjectBoundingBox(FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, RenderObject* other, FloatRect otherBoundingBox) |
+{ |
+ bool otherValid = other->isSVGContainer() ? toRenderSVGContainer(other)->isObjectBoundingBoxValid() : true; |
+ if (!otherValid) |
+ return; |
+ |
+ if (!objectBoundingBoxValid) { |
+ objectBoundingBox = otherBoundingBox; |
+ objectBoundingBoxValid = true; |
+ return; |
+ } |
+ |
+ objectBoundingBox.uniteEvenIfEmpty(otherBoundingBox); |
+} |
+ |
+void SVGRenderSupport::computeContainerBoundingBoxes(const RenderObject* container, FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, FloatRect& strokeBoundingBox, FloatRect& repaintBoundingBox) |
+{ |
+ objectBoundingBox = FloatRect(); |
+ objectBoundingBoxValid = false; |
+ strokeBoundingBox = FloatRect(); |
+ |
+ // When computing the strokeBoundingBox, we use the repaintRects of the container's children so that the container's stroke includes |
+ // the resources applied to the children (such as clips and filters). This allows filters applied to containers to correctly bound |
+ // the children, and also improves inlining of SVG content, as the stroke bound is used in that situation also. |
+ for (RenderObject* current = container->slowFirstChild(); current; current = current->nextSibling()) { |
+ if (current->isSVGHiddenContainer()) |
+ continue; |
+ |
+ const AffineTransform& transform = current->localToParentTransform(); |
+ updateObjectBoundingBox(objectBoundingBox, objectBoundingBoxValid, current, |
+ transform.mapRect(current->objectBoundingBox())); |
+ strokeBoundingBox.unite(transform.mapRect(current->repaintRectInLocalCoordinates())); |
+ } |
+ |
+ repaintBoundingBox = strokeBoundingBox; |
+} |
+ |
bool SVGRenderSupport::paintInfoIntersectsRepaintRect(const FloatRect& localRepaintRect, const AffineTransform& localTransform, const PaintInfo& paintInfo) |
{ |
return localTransform.mapRect(localRepaintRect).intersects(paintInfo.rect); |
@@ -119,6 +163,29 @@ const RenderSVGRoot* SVGRenderSupport::findTreeRootObject(const RenderObject* st |
return toRenderSVGRoot(start); |
} |
+inline void SVGRenderSupport::invalidateResourcesOfChildren(RenderObject* start) |
+{ |
+ ASSERT(!start->needsLayout()); |
+ if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(start)) |
+ resources->removeClientFromCache(start, false); |
+ |
+ for (RenderObject* child = start->slowFirstChild(); child; child = child->nextSibling()) |
+ invalidateResourcesOfChildren(child); |
+} |
+ |
+inline bool SVGRenderSupport::layoutSizeOfNearestViewportChanged(const RenderObject* start) |
+{ |
+ while (start && !start->isSVGRoot() && !start->isSVGViewportContainer()) |
+ start = start->parent(); |
+ |
+ ASSERT(start); |
+ ASSERT(start->isSVGRoot() || start->isSVGViewportContainer()); |
+ if (start->isSVGViewportContainer()) |
+ return toRenderSVGViewportContainer(start)->isLayoutSizeChanged(); |
+ |
+ return toRenderSVGRoot(start)->isLayoutSizeChanged(); |
+} |
+ |
bool SVGRenderSupport::transformToRootChanged(RenderObject* ancestor) |
{ |
while (ancestor && !ancestor->isSVGRoot()) { |
@@ -132,6 +199,74 @@ bool SVGRenderSupport::transformToRootChanged(RenderObject* ancestor) |
return false; |
} |
+void SVGRenderSupport::layoutChildren(RenderObject* start, bool selfNeedsLayout) |
+{ |
+ bool layoutSizeChanged = layoutSizeOfNearestViewportChanged(start); |
+ bool transformChanged = transformToRootChanged(start); |
+ HashSet<RenderObject*> notlayoutedObjects; |
+ |
+ for (RenderObject* child = start->slowFirstChild(); child; child = child->nextSibling()) { |
+ bool needsLayout = selfNeedsLayout; |
+ bool childEverHadLayout = child->everHadLayout(); |
+ |
+ if (transformChanged) { |
+ // If the transform changed we need to update the text metrics (note: this also happens for layoutSizeChanged=true). |
+ if (child->isSVGText()) |
+ toRenderSVGText(child)->setNeedsTextMetricsUpdate(); |
+ needsLayout = true; |
+ } |
+ |
+ if (layoutSizeChanged) { |
+ // When selfNeedsLayout is false and the layout size changed, we have to check whether this child uses relative lengths |
+ if (SVGElement* element = child->node()->isSVGElement() ? toSVGElement(child->node()) : 0) { |
+ if (element->hasRelativeLengths()) { |
+ // When the layout size changed and when using relative values tell the RenderSVGShape to update its shape object |
+ if (child->isSVGShape()) { |
+ toRenderSVGShape(child)->setNeedsShapeUpdate(); |
+ } else if (child->isSVGText()) { |
+ toRenderSVGText(child)->setNeedsTextMetricsUpdate(); |
+ toRenderSVGText(child)->setNeedsPositioningValuesUpdate(); |
+ } |
+ |
+ needsLayout = true; |
+ } |
+ } |
+ } |
+ |
+ SubtreeLayoutScope layoutScope(*child); |
+ // Resource containers are nasty: they can invalidate clients outside the current SubtreeLayoutScope. |
+ // Since they only care about viewport size changes (to resolve their relative lengths), we trigger |
+ // their invalidation directly from SVGSVGElement::svgAttributeChange() or at a higher |
+ // SubtreeLayoutScope (in RenderView::layout()). |
+ if (needsLayout && !child->isSVGResourceContainer()) |
+ layoutScope.setNeedsLayout(child); |
+ |
+ layoutResourcesIfNeeded(child); |
+ |
+ if (child->needsLayout()) { |
+ child->layout(); |
+ // Renderers are responsible for repainting themselves when changing, except |
+ // for the initial paint to avoid potential double-painting caused by non-sensical "old" bounds. |
+ // We could handle this in the individual objects, but for now it's easier to have |
+ // parent containers call repaint(). (RenderBlock::layout* has similar logic.) |
+ if (!childEverHadLayout && !RuntimeEnabledFeatures::repaintAfterLayoutEnabled()) |
+ child->repaint(); |
+ } else if (layoutSizeChanged) { |
+ notlayoutedObjects.add(child); |
+ } |
+ } |
+ |
+ if (!layoutSizeChanged) { |
+ ASSERT(notlayoutedObjects.isEmpty()); |
+ return; |
+ } |
+ |
+ // If the layout size changed, invalidate all resources of all children that didn't go through the layout() code path. |
+ HashSet<RenderObject*>::iterator end = notlayoutedObjects.end(); |
+ for (HashSet<RenderObject*>::iterator it = notlayoutedObjects.begin(); it != end; ++it) |
+ invalidateResourcesOfChildren(*it); |
+} |
+ |
void SVGRenderSupport::layoutResourcesIfNeeded(const RenderObject* object) |
{ |
ASSERT(object); |