blob: 83282cf03166e82dc4d26069100924fb2850e570 [file] [log] [blame]
/*
* Copyright (C) 2007, 2008 Rob Buis <buis@kde.org>
* Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
* Copyright (C) 2007 Eric Seidel <eric@webkit.org>
* Copyright (C) 2009 Google, Inc. All rights reserved.
* Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
* Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
* Copyright (C) 2018 Adobe Systems Incorporated. All rights reserved.
* Copyright (C) 2020 Apple Inc. All rights reserved.
* Copyright (C) 2021 Igalia S.L.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "config.h"
#include "SVGContainerLayout.h"
#if ENABLE(LAYER_BASED_SVG_ENGINE)
#include "Logging.h"
#include "RenderAncestorIterator.h"
#include "RenderChildIterator.h"
#include "RenderSVGForeignObject.h"
#include "RenderSVGHiddenContainer.h"
#include "RenderSVGInline.h"
#include "RenderSVGModelObject.h"
#include "RenderSVGRoot.h"
#include "RenderSVGShape.h"
#include "RenderSVGText.h"
#include "RenderSVGTransformableContainer.h"
#include "RenderSVGViewportContainer.h"
#include "SVGRenderSupport.h"
#include "SVGRenderingContext.h"
#include "SVGResources.h"
#include "SVGResourcesCache.h"
namespace WebCore {
SVGContainerLayout::SVGContainerLayout(RenderLayerModelObject& container)
: m_container(container)
{
}
void SVGContainerLayout::layoutChildren(bool containerNeedsLayout)
{
bool layoutSizeChanged = layoutSizeOfNearestViewportChanged();
bool transformChanged = transformToRootChanged(&m_container);
HashSet<RenderElement*> elementsThatDidNotReceiveLayout;
m_positionedChildren.clear();
for (auto& child : childrenOfType<RenderObject>(m_container)) {
if (child.isSVGLayerAwareRenderer()) {
ASSERT(child.hasLayer());
m_positionedChildren.append(downcast<RenderLayerModelObject>(child));
}
bool needsLayout = containerNeedsLayout;
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 (is<RenderSVGText>(child))
downcast<RenderSVGText>(child).setNeedsTextMetricsUpdate();
needsLayout = true;
}
if (layoutSizeChanged && child.node() && is<SVGElement>(*child.node())) {
// When containerNeedsLayout is false and the layout size changed, we have to check whether this child uses relative lengths
auto& element = downcast<SVGElement>(*child.node());
if (element.hasRelativeLengths() && 1) {
// When the layout size changed and when using relative values tell the RenderSVGShape to update its shape object
if (is<RenderSVGShape>(child))
downcast<RenderSVGShape>(child).setNeedsShapeUpdate();
else if (is<RenderSVGText>(child)) {
auto& svgText = downcast<RenderSVGText>(child);
svgText.setNeedsTextMetricsUpdate();
svgText.setNeedsPositioningValuesUpdate();
}
needsLayout = true;
}
}
if (needsLayout)
child.setNeedsLayout(MarkOnlyThis);
bool childNeededLayout = child.needsLayout();
if (is<RenderElement>(child)) {
auto& element = downcast<RenderElement>(child);
if (childNeededLayout) {
layoutDifferentRootIfNeeded(element);
element.layout();
} else if (layoutSizeChanged)
elementsThatDidNotReceiveLayout.add(&element);
if (!childEverHadLayout && element.checkForRepaintDuringLayout())
child.repaint();
}
ASSERT(!child.needsLayout());
}
if (layoutSizeChanged) {
// If the layout size changed, invalidate all resources of all children that didn't go through the layout() code path.
for (auto* element : elementsThatDidNotReceiveLayout)
invalidateResourcesOfChildren(*element);
} else
ASSERT(elementsThatDidNotReceiveLayout.isEmpty());
}
void SVGContainerLayout::positionChildrenRelativeToContainer()
{
if (m_positionedChildren.isEmpty())
return;
auto verifyPositionedChildRendererExpectation = [](RenderObject& renderer) {
#if !defined(NDEBUG)
ASSERT(renderer.isSVGLayerAwareRenderer()); // Pre-condition to enter m_positionedChildren
ASSERT(!renderer.isSVGRoot()); // There is only one outermost RenderSVGRoot object
ASSERT(!renderer.isSVGInline()); // Inlines are only allowed within a RenderSVGText tree
if (is<RenderSVGModelObject>(renderer) || is<RenderSVGBlock>(renderer))
return;
ASSERT_NOT_REACHED();
return;
#else
UNUSED_PARAM(renderer);
#endif
};
// Arrange layout location for all child renderers relative to the container layout location.
auto parentLayoutLocation = m_container.nominalSVGLayoutLocation();
for (RenderLayerModelObject& child : m_positionedChildren) {
verifyPositionedChildRendererExpectation(child);
auto desiredLayoutLocation = toLayoutPoint(child.nominalSVGLayoutLocation() - parentLayoutLocation);
if (child.currentSVGLayoutLocation() != desiredLayoutLocation)
child.setCurrentSVGLayoutLocation(desiredLayoutLocation);
}
}
void SVGContainerLayout::verifyLayoutLocationConsistency(const RenderLayerModelObject& renderer)
{
if (renderer.isSVGLayerAwareRenderer() && !renderer.isSVGRoot()) {
auto currentLayoutLocation = renderer.currentSVGLayoutLocation();
auto expectedLayoutLocation = currentLayoutLocation;
for (auto& ancestor : ancestorsOfType<RenderLayerModelObject>(renderer)) {
ASSERT(ancestor.isSVGLayerAwareRenderer());
if (ancestor.isSVGRoot())
break;
expectedLayoutLocation.moveBy(ancestor.currentSVGLayoutLocation());
}
auto initialLayoutLocation = renderer.nominalSVGLayoutLocation();
if (expectedLayoutLocation == initialLayoutLocation) {
LOG_WITH_STREAM(SVG, stream << "--> SVGContainerLayout renderer " << &renderer << " (" << renderer.renderName().characters() << ")"
<< " - verifyLayoutLocationConsistency() currentSVGLayoutLocation / nominalSVGLayoutLocation are in sync.");
} else {
LOG_WITH_STREAM(SVG, stream << "--> SVGContainerLayout renderer " << &renderer << " (" << renderer.renderName().characters() << ")"
<< " - verifyLayoutLocationConsistency() currentSVGLayoutLocation / nominalSVGLayoutLocation invariant violated -- out of sync due to partial layout?"
<< " currentLayoutLocation=" << currentLayoutLocation
<< " (expectedLayoutLocation=" << expectedLayoutLocation
<< " != initialLayoutLocation=" << initialLayoutLocation << ")"
<< " -> aborting with a render tree dump");
#if ENABLE(TREE_DEBUGGING)
showRenderTree(&renderer);
#endif
ASSERT_NOT_REACHED();
}
}
for (auto& child : childrenOfType<RenderLayerModelObject>(renderer)) {
if (child.isSVGLayerAwareRenderer())
verifyLayoutLocationConsistency(child);
}
#if !defined(NDEBUG)
if (renderer.isSVGRoot()) {
LOG_WITH_STREAM(SVG, stream << "--> SVGContainerLayout renderer " << &renderer << " (" << renderer.renderName().characters() << ")"
<< " - verifyLayoutLocationConsistency() end");
}
#endif
}
void SVGContainerLayout::layoutDifferentRootIfNeeded(const RenderElement& renderer)
{
if (auto* resources = SVGResourcesCache::cachedResourcesForRenderer(renderer)) {
auto* svgRoot = SVGRenderSupport::findTreeRootObject(renderer);
ASSERT(svgRoot);
resources->layoutDifferentRootIfNeeded(svgRoot);
}
}
void SVGContainerLayout::invalidateResourcesOfChildren(RenderElement& renderer)
{
ASSERT(!renderer.needsLayout());
if (auto* resources = SVGResourcesCache::cachedResourcesForRenderer(renderer))
resources->removeClientFromCache(renderer, false);
for (auto& child : childrenOfType<RenderElement>(renderer))
invalidateResourcesOfChildren(child);
}
bool SVGContainerLayout::layoutSizeOfNearestViewportChanged() const
{
RenderElement* ancestor = &m_container;
while (ancestor && !is<RenderSVGRoot>(*ancestor) && !is<RenderSVGViewportContainer>(*ancestor))
ancestor = ancestor->parent();
ASSERT(ancestor);
if (is<RenderSVGViewportContainer>(*ancestor))
return downcast<RenderSVGViewportContainer>(*ancestor).isLayoutSizeChanged();
return downcast<RenderSVGRoot>(*ancestor).isLayoutSizeChanged();
}
bool SVGContainerLayout::transformToRootChanged(const RenderObject* ancestor)
{
while (ancestor) {
if (is<RenderSVGTransformableContainer>(*ancestor))
return downcast<const RenderSVGTransformableContainer>(*ancestor).didTransformToRootUpdate();
// FIXME: [LBSE] Upstream RenderSVGViewportContainer changes
// if (is<RenderSVGViewportContainer>(*ancestor))
// return downcast<const RenderSVGViewportContainer>(*ancestor).didTransformToRootUpdate();
if (is<RenderSVGRoot>(*ancestor))
return downcast<const RenderSVGRoot>(*ancestor).didTransformToRootUpdate();
ancestor = ancestor->parent();
}
return false;
}
}
#endif // ENABLE(LAYER_BASED_SVG_ENGINE)