| 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);
|
|
|