blob: 96af95a4556950e8126b95373534d476dae3563b [file] [log] [blame]
/*
* Copyright (C) 2019 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "LayerOverlapMap.h"
#include "RenderLayer.h"
#include <wtf/text/TextStream.h>
namespace WebCore {
struct RectList {
Vector<LayoutRect> rects;
LayoutRect boundingRect;
void append(const LayoutRect& rect)
{
rects.append(rect);
boundingRect.unite(rect);
}
void append(const RectList& rectList)
{
rects.appendVector(rectList.rects);
boundingRect.unite(rectList.boundingRect);
}
bool intersects(const LayoutRect& rect) const
{
if (!rects.size() || !rect.intersects(boundingRect))
return false;
for (const auto& currentRect : rects) {
if (currentRect.intersects(rect))
return true;
}
return false;
}
};
static TextStream& operator<<(TextStream& ts, const RectList& rectList)
{
ts << "bounds " << rectList.boundingRect << " (" << rectList.rects << " rects)";
return ts;
}
// Used to store overlap rects in a way that takes overflow into account.
// It stores a tree whose nodes are layers with composited scrolling. The tree is built lazily as layers are added whose containing block
// chains contain composited scrollers. The tree always starts at the root layer.
// Checking for overlap involves finding the node for the clipping layer enclosing the given layer (or the root),
// and comparing against the bounds of earlier siblings.
class OverlapMapContainer {
WTF_MAKE_FAST_ALLOCATED;
public:
OverlapMapContainer(const RenderLayer& rootLayer)
: m_rootScope(rootLayer)
{
}
// Layers are added in z-order, lazily creating clipping scopes as necessary.
void add(const RenderLayer&, const LayoutRect& bounds, const Vector<LayerOverlapMap::LayerAndBounds>& enclosingClippingLayers);
bool overlapsLayers(const RenderLayer&, const LayoutRect& bounds, const Vector<LayerOverlapMap::LayerAndBounds>& enclosingClippingLayers) const;
void append(std::unique_ptr<OverlapMapContainer>&&);
String dump(unsigned) const;
private:
struct ClippingScope {
ClippingScope(const RenderLayer& inLayer)
: layer(inLayer)
{
}
ClippingScope(const LayerOverlapMap::LayerAndBounds& layerAndBounds)
: layer(layerAndBounds.layer)
, bounds(layerAndBounds.bounds)
{
}
ClippingScope* childWithLayer(const RenderLayer& layer) const
{
for (auto& child : children) {
if (&child.layer == &layer)
return const_cast<ClippingScope*>(&child);
}
return nullptr;
}
ClippingScope* addChildWithLayerAndBounds(const LayerOverlapMap::LayerAndBounds& layerAndBounds)
{
children.append({ layerAndBounds });
return &children.last();
}
ClippingScope* addChild(const ClippingScope& child)
{
ASSERT(&layer != &child.layer);
children.append(child);
return &children.last();
}
void appendRect(const LayoutRect& bounds)
{
rectList.append(bounds);
}
const RenderLayer& layer;
LayoutRect bounds; // Bounds of the composited clip.
Vector<ClippingScope> children;
RectList rectList;
};
static ClippingScope* clippingScopeContainingLayerChildRecursive(const ClippingScope& currNode, const RenderLayer& layer)
{
for (auto& child : currNode.children) {
if (&layer == &child.layer)
return const_cast<ClippingScope*>(&currNode);
if (auto* foundNode = clippingScopeContainingLayerChildRecursive(child, layer))
return foundNode;
}
return nullptr;
}
ClippingScope* scopeContainingLayer(const RenderLayer& layer) const
{
return clippingScopeContainingLayerChildRecursive(m_rootScope, layer);
}
static void mergeClippingScopesRecursive(const ClippingScope& sourceScope, ClippingScope& destScope);
ClippingScope* ensureClippingScopeForLayers(const Vector<LayerOverlapMap::LayerAndBounds>& enclosingClippingLayers);
ClippingScope* findClippingScopeForLayers(const Vector<LayerOverlapMap::LayerAndBounds>& enclosingClippingLayers) const;
void recursiveOutputToStream(TextStream&, const ClippingScope&, unsigned depth) const;
const ClippingScope& rootScope() const { return m_rootScope; }
ClippingScope& rootScope() { return m_rootScope; }
ClippingScope m_rootScope;
};
void OverlapMapContainer::add(const RenderLayer&, const LayoutRect& bounds, const Vector<LayerOverlapMap::LayerAndBounds>& enclosingClippingLayers)
{
auto* layerScope = ensureClippingScopeForLayers(enclosingClippingLayers);
layerScope->appendRect(bounds);
}
bool OverlapMapContainer::overlapsLayers(const RenderLayer&, const LayoutRect& bounds, const Vector<LayerOverlapMap::LayerAndBounds>& enclosingClippingLayers) const
{
if (m_rootScope.rectList.intersects(bounds))
return true;
if (m_rootScope.children.isEmpty())
return false;
// Find the ClippingScope for which this layer is a child.
auto* clippingScope = findClippingScopeForLayers(enclosingClippingLayers);
if (!clippingScope)
return false;
if (clippingScope->rectList.intersects(bounds))
return true;
// FIXME: In some cases do we have to walk up the ancestor clipping scope chain?
return false;
}
void OverlapMapContainer::mergeClippingScopesRecursive(const ClippingScope& sourceScope, ClippingScope& destScope)
{
ASSERT(&sourceScope.layer == &destScope.layer);
destScope.rectList.append(sourceScope.rectList);
for (auto& sourceChildScope : sourceScope.children) {
ClippingScope* destChild = destScope.childWithLayer(sourceChildScope.layer);
if (destChild) {
destChild->rectList.append(sourceChildScope.rectList);
mergeClippingScopesRecursive(sourceChildScope, *destChild);
} else {
// New child, just copy the whole subtree.
destScope.addChild(sourceChildScope);
}
}
}
void OverlapMapContainer::append(std::unique_ptr<OverlapMapContainer>&& otherContainer)
{
mergeClippingScopesRecursive(otherContainer->rootScope(), m_rootScope);
}
OverlapMapContainer::ClippingScope* OverlapMapContainer::ensureClippingScopeForLayers(const Vector<LayerOverlapMap::LayerAndBounds>& enclosingClippingLayers)
{
ASSERT(enclosingClippingLayers.size());
ASSERT(enclosingClippingLayers[0].layer.isRenderViewLayer());
auto* currScope = &m_rootScope;
for (unsigned i = 1; i < enclosingClippingLayers.size(); ++i) {
auto& scopeLayerAndBounds = enclosingClippingLayers[i];
auto* childScope = currScope->childWithLayer(scopeLayerAndBounds.layer);
if (!childScope) {
currScope = currScope->addChildWithLayerAndBounds(scopeLayerAndBounds);
break;
}
currScope = childScope;
}
return const_cast<ClippingScope*>(currScope);
}
OverlapMapContainer::ClippingScope* OverlapMapContainer::findClippingScopeForLayers(const Vector<LayerOverlapMap::LayerAndBounds>& enclosingClippingLayers) const
{
ASSERT(enclosingClippingLayers.size());
ASSERT(enclosingClippingLayers[0].layer.isRenderViewLayer());
const auto* currScope = &m_rootScope;
for (unsigned i = 1; i < enclosingClippingLayers.size(); ++i) {
auto& scopeLayerAndBounds = enclosingClippingLayers[i];
auto* childScope = currScope->childWithLayer(scopeLayerAndBounds.layer);
if (!childScope)
return nullptr;
currScope = childScope;
}
return const_cast<ClippingScope*>(currScope);
}
void OverlapMapContainer::recursiveOutputToStream(TextStream& ts, const ClippingScope& scope, unsigned depth) const
{
ts << "\n" << indent << TextStream::Repeat { 2 * depth, ' ' } << " scope for layer " << &scope.layer << " rects " << scope.rectList;
for (auto& childScope : scope.children)
recursiveOutputToStream(ts, childScope, depth + 1);
}
String OverlapMapContainer::dump(unsigned indent) const
{
TextStream multilineStream;
multilineStream.increaseIndent(indent);
multilineStream << "overlap container - root scope layer " << &m_rootScope.layer << " rects " << m_rootScope.rectList;
for (auto& childScope : m_rootScope.children)
recursiveOutputToStream(multilineStream, childScope, 1);
return multilineStream.release();
}
LayerOverlapMap::LayerOverlapMap(const RenderLayer& rootLayer)
: m_geometryMap(UseTransforms)
, m_rootLayer(rootLayer)
{
// Begin assuming the root layer will be composited so that there is
// something on the stack. The root layer should also never get an
// popCompositingContainer call.
pushCompositingContainer();
}
LayerOverlapMap::~LayerOverlapMap() = default;
void LayerOverlapMap::add(const RenderLayer& layer, const LayoutRect& bounds, const Vector<LayerAndBounds>& enclosingClippingLayers)
{
// Layers do not contribute to overlap immediately--instead, they will
// contribute to overlap as soon as their composited ancestor has been
// recursively processed and popped off the stack.
ASSERT(m_overlapStack.size() >= 2);
m_overlapStack[m_overlapStack.size() - 2]->add(layer, bounds, enclosingClippingLayers);
m_isEmpty = false;
}
bool LayerOverlapMap::overlapsLayers(const RenderLayer& layer, const LayoutRect& bounds, const Vector<LayerAndBounds>& enclosingClippingLayers) const
{
return m_overlapStack.last()->overlapsLayers(layer, bounds, enclosingClippingLayers);
}
void LayerOverlapMap::pushCompositingContainer()
{
m_overlapStack.append(makeUnique<OverlapMapContainer>(m_rootLayer));
}
void LayerOverlapMap::popCompositingContainer()
{
m_overlapStack[m_overlapStack.size() - 2]->append(WTFMove(m_overlapStack.last()));
m_overlapStack.removeLast();
}
static TextStream& operator<<(TextStream& ts, const OverlapMapContainer& container)
{
ts << container.dump(ts.indent());
return ts;
}
TextStream& operator<<(TextStream& ts, const LayerOverlapMap& overlapMap)
{
TextStream multilineStream;
TextStream::GroupScope scope(ts);
multilineStream << "LayerOverlapMap\n";
multilineStream.increaseIndent(2);
bool needNewline = false;
for (auto& container : overlapMap.overlapStack()) {
if (needNewline)
multilineStream << "\n";
else
needNewline = true;
multilineStream << indent << *container;
}
ts << multilineStream.release();
return ts;
}
} // namespace WebCore