blob: 1bf13b16fa3e2e005f16332e49345f086274f9bd [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "config.h"
#include "core/rendering/compositing/CompositingInputsUpdater.h"
#include "core/rendering/RenderBlock.h"
#include "core/rendering/RenderLayer.h"
#include "core/rendering/compositing/CompositedLayerMapping.h"
#include "core/rendering/compositing/RenderLayerCompositor.h"
#include "platform/TraceEvent.h"
namespace blink {
CompositingInputsUpdater::CompositingInputsUpdater(RenderLayer* rootRenderLayer)
: m_geometryMap(UseTransforms)
, m_rootRenderLayer(rootRenderLayer)
{
}
CompositingInputsUpdater::~CompositingInputsUpdater()
{
}
void CompositingInputsUpdater::update()
{
TRACE_EVENT0("blink", "CompositingInputsUpdater::update");
updateRecursive(m_rootRenderLayer, DoNotForceUpdate, AncestorInfo());
}
static const RenderLayer* findParentLayerOnClippingContainerChain(const RenderLayer* layer)
{
RenderObject* current = layer->renderer();
while (current) {
if (current->style()->position() == FixedPosition) {
for (current = current->parent(); current && !current->canContainFixedPositionObjects(); current = current->parent()) {
// CSS clip applies to fixed position elements even for ancestors that are not what the
// fixed element is positioned with respect to.
if (current->hasClip()) {
ASSERT(current->hasLayer());
return static_cast<const RenderLayerModelObject*>(current)->layer();
}
}
} else {
current = current->containingBlock();
}
if (current->hasLayer())
return static_cast<const RenderLayerModelObject*>(current)->layer();
// Having clip or overflow clip forces the RenderObject to become a layer.
ASSERT(!current->hasClipOrOverflowClip());
}
ASSERT_NOT_REACHED();
return 0;
}
static const RenderLayer* findParentLayerOnContainingBlockChain(const RenderObject* object)
{
for (const RenderObject* current = object; current; current = current->containingBlock()) {
if (current->hasLayer())
return static_cast<const RenderLayerModelObject*>(current)->layer();
}
ASSERT_NOT_REACHED();
return 0;
}
static bool hasClippedStackingAncestor(const RenderLayer* layer, const RenderLayer* clippingLayer)
{
if (layer == clippingLayer)
return false;
const RenderObject* clippingRenderer = clippingLayer->renderer();
for (const RenderLayer* current = layer->compositingContainer(); current && current != clippingLayer; current = current->compositingContainer()) {
if (current->renderer()->hasClipOrOverflowClip() && !clippingRenderer->isDescendantOf(current->renderer()))
return true;
if (const RenderObject* container = current->clippingContainer()) {
if (clippingRenderer != container && !clippingRenderer->isDescendantOf(container))
return true;
}
}
return false;
}
void CompositingInputsUpdater::updateRecursive(RenderLayer* layer, UpdateType updateType, AncestorInfo info)
{
if (!layer->childNeedsCompositingInputsUpdate() && updateType != ForceUpdate)
return;
m_geometryMap.pushMappingsToAncestor(layer, layer->parent());
if (layer->hasCompositedLayerMapping())
info.enclosingCompositedLayer = layer;
if (layer->needsCompositingInputsUpdate()) {
if (info.enclosingCompositedLayer)
info.enclosingCompositedLayer->compositedLayerMapping()->setNeedsGraphicsLayerUpdate(GraphicsLayerUpdateSubtree);
updateType = ForceUpdate;
}
if (updateType == ForceUpdate) {
RenderLayer::AncestorDependentCompositingInputs properties;
if (!layer->isRootLayer()) {
properties.clippedAbsoluteBoundingBox = enclosingIntRect(m_geometryMap.absoluteRect(layer->boundingBoxForCompositingOverlapTest()));
// FIXME: Setting the absBounds to 1x1 instead of 0x0 makes very little sense,
// but removing this code will make JSGameBench sad.
// See https://codereview.chromium.org/13912020/
if (properties.clippedAbsoluteBoundingBox.isEmpty())
properties.clippedAbsoluteBoundingBox.setSize(IntSize(1, 1));
IntRect clipRect = pixelSnappedIntRect(layer->clipper().backgroundClipRect(ClipRectsContext(m_rootRenderLayer, AbsoluteClipRects)).rect());
properties.clippedAbsoluteBoundingBox.intersect(clipRect);
const RenderLayer* parent = layer->parent();
properties.opacityAncestor = parent->isTransparent() ? parent : parent->opacityAncestor();
properties.transformAncestor = parent->hasTransform() ? parent : parent->transformAncestor();
properties.filterAncestor = parent->hasFilter() ? parent : parent->filterAncestor();
if (info.hasAncestorWithClipOrOverflowClip) {
const RenderLayer* parentLayerOnClippingContainerChain = findParentLayerOnClippingContainerChain(layer);
const bool parentHasClipOrOverflowClip = parentLayerOnClippingContainerChain->renderer()->hasClipOrOverflowClip();
properties.clippingContainer = parentHasClipOrOverflowClip ? parentLayerOnClippingContainerChain->renderer() : parentLayerOnClippingContainerChain->clippingContainer();
}
if (info.lastScrollingAncestor) {
const RenderObject* containingBlock = layer->renderer()->containingBlock();
const RenderLayer* parentLayerOnContainingBlockChain = findParentLayerOnContainingBlockChain(containingBlock);
properties.ancestorScrollingLayer = parentLayerOnContainingBlockChain->ancestorScrollingLayer();
if (parentLayerOnContainingBlockChain->scrollsOverflow())
properties.ancestorScrollingLayer = parentLayerOnContainingBlockChain;
if (layer->renderer()->isOutOfFlowPositioned() && !layer->subtreeIsInvisible()) {
const RenderObject* lastScroller = info.lastScrollingAncestor->renderer();
const RenderLayer* clippingLayer = properties.clippingContainer ? properties.clippingContainer->enclosingLayer() : layer->compositor()->rootRenderLayer();
properties.isUnclippedDescendant = lastScroller != containingBlock && lastScroller->isDescendantOf(containingBlock);
if (hasClippedStackingAncestor(layer, clippingLayer))
properties.clipParent = clippingLayer;
}
if (!layer->stackingNode()->isNormalFlowOnly()
&& properties.ancestorScrollingLayer
&& !info.ancestorStackingContext->renderer()->isDescendantOf(properties.ancestorScrollingLayer->renderer()))
properties.scrollParent = properties.ancestorScrollingLayer;
}
}
properties.hasAncestorWithClipPath = info.hasAncestorWithClipPath;
layer->updateAncestorDependentCompositingInputs(properties);
}
if (layer->stackingNode()->isStackingContext())
info.ancestorStackingContext = layer;
if (layer->scrollsOverflow())
info.lastScrollingAncestor = layer;
if (layer->renderer()->hasClipOrOverflowClip())
info.hasAncestorWithClipOrOverflowClip = true;
if (layer->renderer()->hasClipPath())
info.hasAncestorWithClipPath = true;
RenderLayer::DescendantDependentCompositingInputs descendantProperties;
for (RenderLayer* child = layer->firstChild(); child; child = child->nextSibling()) {
updateRecursive(child, updateType, info);
descendantProperties.hasDescendantWithClipPath |= child->hasDescendantWithClipPath() || child->renderer()->hasClipPath();
descendantProperties.hasDescendantWithBlendMode |= child->hasDescendantWithBlendMode() || child->renderer()->hasBlendMode();
}
layer->updateDescendantDependentCompositingInputs(descendantProperties);
layer->didUpdateCompositingInputs();
m_geometryMap.popMappingsToAncestor(layer->parent());
}
#if ENABLE(ASSERT)
void CompositingInputsUpdater::assertNeedsCompositingInputsUpdateBitsCleared(RenderLayer* layer)
{
ASSERT(!layer->childNeedsCompositingInputsUpdate());
ASSERT(!layer->needsCompositingInputsUpdate());
for (RenderLayer* child = layer->firstChild(); child; child = child->nextSibling())
assertNeedsCompositingInputsUpdateBitsCleared(child);
}
#endif
} // namespace blink