Ojan Vafai 74290d5007 Move hit testing out of RenderLayer into RenderBox.
Change hit testing to walk over the render tree instead of
the RenderLayer tree. This is a step in the direction of
removing the RenderLayer tree entirely.

For now, there's a few calls back into RenderLayer that
will be removed in a followup patch. This patch also
breaks hit testing on transformed inlines. I'll be
removing the ability to transform inlines in a
followup patch anyways, so it's ok for hit testing
to give the wrong result temporarily here.

Almost all of this patch is just moving code from
RenderLayer to RenderBox. The primary substantive change
is in RenderBox::hitTestLayer. Instead of having
hitTestChildren calls, we call collectSelfPaintingLayers,
reverse sort by z-index (so we start at the top),
and then iterate over the result.

The test-case also exposes that we don't correctly hit
transformed elements inside inline-blocks. I went back as
far as 4153b8a515d54275934d4244aaf2d5a7a8fe3333 and the
bug still happened.

R=abarth@chromium.org

Review URL: https://codereview.chromium.org/945693002
2015-02-19 21:31:42 -08:00

1078 lines
39 KiB
C++

/*
* Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved.
*
* Portions are Copyright (C) 1998 Netscape Communications Corporation.
*
* Other contributors:
* Robert O'Callahan <roc+@cs.cmu.edu>
* David Baron <dbaron@fas.harvard.edu>
* Christian Biesinger <cbiesinger@web.de>
* Randall Jesup <rjesup@wgate.com>
* Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
* Josh Soref <timeless@mac.com>
* Boris Zbarsky <bzbarsky@mit.edu>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Alternatively, the contents of this file may be used under the terms
* of either the Mozilla Public License Version 1.1, found at
* http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
* License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
* (the "GPL"), in which case the provisions of the MPL or the GPL are
* applicable instead of those above. If you wish to allow use of your
* version of this file only under the terms of one of those two
* licenses (the MPL or the GPL) and not to allow others to use your
* version of this file under the LGPL, indicate your decision by
* deletingthe provisions above and replace them with the notice and
* other provisions required by the MPL or the GPL, as the case may be.
* If you do not delete the provisions above, a recipient may use your
* version of this file under any of the LGPL, the MPL or the GPL.
*/
#include "sky/engine/config.h"
#include "sky/engine/core/rendering/RenderLayer.h"
#include "gen/sky/core/CSSPropertyNames.h"
#include "gen/sky/platform/RuntimeEnabledFeatures.h"
#include "sky/engine/core/dom/Document.h"
#include "sky/engine/core/dom/shadow/ShadowRoot.h"
#include "sky/engine/core/frame/FrameView.h"
#include "sky/engine/core/frame/LocalFrame.h"
#include "sky/engine/core/frame/Settings.h"
#include "sky/engine/core/page/Page.h"
#include "sky/engine/core/rendering/FilterEffectRenderer.h"
#include "sky/engine/core/rendering/HitTestRequest.h"
#include "sky/engine/core/rendering/HitTestResult.h"
#include "sky/engine/core/rendering/HitTestingTransformState.h"
#include "sky/engine/core/rendering/RenderGeometryMap.h"
#include "sky/engine/core/rendering/RenderInline.h"
#include "sky/engine/core/rendering/RenderTreeAsText.h"
#include "sky/engine/core/rendering/RenderView.h"
#include "sky/engine/platform/LengthFunctions.h"
#include "sky/engine/platform/Partitions.h"
#include "sky/engine/platform/TraceEvent.h"
#include "sky/engine/platform/geometry/FloatPoint3D.h"
#include "sky/engine/platform/geometry/FloatRect.h"
#include "sky/engine/platform/geometry/TransformState.h"
#include "sky/engine/platform/graphics/GraphicsContextStateSaver.h"
#include "sky/engine/platform/graphics/filters/ReferenceFilter.h"
#include "sky/engine/platform/graphics/filters/SourceGraphic.h"
#include "sky/engine/platform/transforms/ScaleTransformOperation.h"
#include "sky/engine/platform/transforms/TransformationMatrix.h"
#include "sky/engine/platform/transforms/TranslateTransformOperation.h"
#include "sky/engine/public/platform/Platform.h"
#include "sky/engine/wtf/StdLibExtras.h"
#include "sky/engine/wtf/text/CString.h"
namespace blink {
RenderLayer::RenderLayer(RenderLayerModelObject* renderer, LayerType type)
: m_layerType(type)
, m_hasSelfPaintingLayerDescendant(false)
, m_hasSelfPaintingLayerDescendantDirty(false)
, m_isRootLayer(renderer->isRenderView())
, m_usedTransparency(false)
, m_3DTransformedDescendantStatusDirty(true)
, m_has3DTransformedDescendant(false)
, m_hasFilterInfo(false)
, m_renderer(renderer)
, m_parent(0)
, m_previous(0)
, m_next(0)
, m_first(0)
, m_last(0)
, m_staticInlinePosition(0)
, m_staticBlockPosition(0)
, m_clipper(*renderer)
{
updateStackingNode();
m_isSelfPaintingLayer = shouldBeSelfPaintingLayer();
}
RenderLayer::~RenderLayer()
{
removeFilterInfoIfNeeded();
}
String RenderLayer::debugName() const
{
return renderer()->debugName();
}
bool RenderLayer::paintsWithFilters() const
{
// FIXME(sky): Remove
return renderer()->hasFilter();
}
bool RenderLayer::requiresFullLayerImageForFilters() const
{
if (!paintsWithFilters())
return false;
FilterEffectRenderer* filter = filterRenderer();
return filter ? filter->hasFilterThatMovesPixels() : false;
}
LayoutSize RenderLayer::subpixelAccumulation() const
{
return m_subpixelAccumulation;
}
void RenderLayer::setSubpixelAccumulation(const LayoutSize& size)
{
m_subpixelAccumulation = size;
}
void RenderLayer::updateLayerPositionsAfterLayout()
{
TRACE_EVENT0("blink", "RenderLayer::updateLayerPositionsAfterLayout");
m_clipper.clearClipRectsIncludingDescendants();
}
void RenderLayer::updateHasSelfPaintingLayerDescendant() const
{
ASSERT(m_hasSelfPaintingLayerDescendantDirty);
m_hasSelfPaintingLayerDescendant = false;
for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) {
if (child->isSelfPaintingLayer() || child->hasSelfPaintingLayerDescendant()) {
m_hasSelfPaintingLayerDescendant = true;
break;
}
}
m_hasSelfPaintingLayerDescendantDirty = false;
}
void RenderLayer::dirtyAncestorChainHasSelfPaintingLayerDescendantStatus()
{
for (RenderLayer* layer = this; layer; layer = layer->parent()) {
layer->m_hasSelfPaintingLayerDescendantDirty = true;
// If we have reached a self-painting layer, we know our parent should have a self-painting descendant
// in this case, there is no need to dirty our ancestors further.
if (layer->isSelfPaintingLayer()) {
ASSERT(!parent() || parent()->m_hasSelfPaintingLayerDescendantDirty || parent()->m_hasSelfPaintingLayerDescendant);
break;
}
}
}
void RenderLayer::updateTransformationMatrix()
{
if (m_transform) {
RenderBox* box = renderBox();
ASSERT(box);
m_transform->makeIdentity();
box->style()->applyTransform(*m_transform, box->pixelSnappedBorderBoxRect().size(), RenderStyle::IncludeTransformOrigin);
makeMatrixRenderable(*m_transform);
}
}
void RenderLayer::updateTransform(const RenderStyle* oldStyle, RenderStyle* newStyle)
{
if (oldStyle && newStyle->transformDataEquivalent(*oldStyle))
return;
// hasTransform() on the renderer is also true when there is transform-style: preserve-3d or perspective set,
// so check style too.
bool hasTransform = renderer()->hasTransform() && newStyle->hasTransform();
bool had3DTransform = has3DTransform();
bool hadTransform = m_transform;
if (hasTransform != hadTransform) {
if (hasTransform)
m_transform = adoptPtr(new TransformationMatrix);
else
m_transform.clear();
// Layers with transforms act as clip rects roots, so clear the cached clip rects here.
m_clipper.clearClipRectsIncludingDescendants();
} else if (hasTransform) {
m_clipper.clearClipRectsIncludingDescendants(AbsoluteClipRects);
}
updateTransformationMatrix();
if (had3DTransform != has3DTransform())
dirty3DTransformedDescendantStatus();
}
static RenderLayer* enclosingLayerForContainingBlock(RenderLayer* layer)
{
if (RenderObject* containingBlock = layer->renderer()->containingBlock())
return containingBlock->enclosingLayer();
return 0;
}
RenderLayer* RenderLayer::renderingContextRoot()
{
RenderLayer* renderingContext = 0;
if (shouldPreserve3D())
renderingContext = this;
for (RenderLayer* current = enclosingLayerForContainingBlock(this); current && current->shouldPreserve3D(); current = enclosingLayerForContainingBlock(current))
renderingContext = current;
return renderingContext;
}
TransformationMatrix RenderLayer::currentTransform(RenderStyle::ApplyTransformOrigin applyOrigin) const
{
if (!m_transform)
return TransformationMatrix();
// m_transform includes transform-origin, so we need to recompute the transform here.
if (applyOrigin == RenderStyle::ExcludeTransformOrigin) {
RenderBox* box = renderBox();
TransformationMatrix currTransform;
box->style()->applyTransform(currTransform, box->pixelSnappedBorderBoxRect().size(), RenderStyle::ExcludeTransformOrigin);
makeMatrixRenderable(currTransform);
return currTransform;
}
return *m_transform;
}
TransformationMatrix RenderLayer::renderableTransform() const
{
return m_transform ? *m_transform : TransformationMatrix();
}
RenderLayer* RenderLayer::enclosingOverflowClipLayer(IncludeSelfOrNot includeSelf) const
{
const RenderLayer* layer = (includeSelf == IncludeSelf) ? this : parent();
while (layer) {
if (layer->renderer()->hasOverflowClip())
return const_cast<RenderLayer*>(layer);
layer = layer->parent();
}
return 0;
}
void RenderLayer::dirty3DTransformedDescendantStatus()
{
RenderLayerStackingNode* stackingNode = m_stackingNode->ancestorStackingContextNode();
if (!stackingNode)
return;
stackingNode->layer()->m_3DTransformedDescendantStatusDirty = true;
// This propagates up through preserve-3d hierarchies to the enclosing flattening layer.
// Note that preserves3D() creates stacking context, so we can just run up the stacking containers.
while (stackingNode && stackingNode->layer()->preserves3D()) {
stackingNode->layer()->m_3DTransformedDescendantStatusDirty = true;
stackingNode = stackingNode->ancestorStackingContextNode();
}
}
// Return true if this layer or any preserve-3d descendants have 3d.
bool RenderLayer::update3DTransformedDescendantStatus()
{
if (m_3DTransformedDescendantStatusDirty) {
m_has3DTransformedDescendant = false;
m_stackingNode->updateZOrderLists();
// Transformed or preserve-3d descendants can only be in the z-order lists, not
// in the normal flow list, so we only need to check those.
RenderLayerStackingNodeIterator iterator(*m_stackingNode.get(), PositiveZOrderChildren);
while (RenderLayerStackingNode* node = iterator.next())
m_has3DTransformedDescendant |= node->layer()->update3DTransformedDescendantStatus();
m_3DTransformedDescendantStatusDirty = false;
}
// If we live in a 3d hierarchy, then the layer at the root of that hierarchy needs
// the m_has3DTransformedDescendant set.
if (preserves3D())
return has3DTransform() || m_has3DTransformedDescendant;
return has3DTransform();
}
IntSize RenderLayer::size() const
{
if (renderer()->isInline() && renderer()->isRenderInline())
return toRenderInline(renderer())->linesBoundingBox().size();
// FIXME: Is snapping the size really needed here?
if (RenderBox* box = renderBox())
return pixelSnappedIntSize(box->size(), box->location());
return IntSize();
}
LayoutPoint RenderLayer::location() const
{
LayoutPoint localPoint;
LayoutSize inlineBoundingBoxOffset; // We don't put this into the RenderLayer x/y for inlines, so we need to subtract it out when done.
if (renderer()->isInline() && renderer()->isRenderInline()) {
RenderInline* inlineFlow = toRenderInline(renderer());
IntRect lineBox = inlineFlow->linesBoundingBox();
inlineBoundingBoxOffset = toSize(lineBox.location());
localPoint += inlineBoundingBoxOffset;
} else if (RenderBox* box = renderBox()) {
localPoint += box->locationOffset();
}
if (!renderer()->isOutOfFlowPositioned() && renderer()->parent()) {
// We must adjust our position by walking up the render tree looking for the
// nearest enclosing object with a layer.
RenderObject* curr = renderer()->parent();
while (curr && !curr->hasLayer()) {
if (curr->isBox()) {
// Rows and cells share the same coordinate space (that of the section).
// Omit them when computing our xpos/ypos.
localPoint += toRenderBox(curr)->locationOffset();
}
curr = curr->parent();
}
}
// Subtract our parent's scroll offset.
if (renderer()->isOutOfFlowPositioned() && enclosingPositionedAncestor()) {
RenderLayer* positionedParent = enclosingPositionedAncestor();
if (positionedParent->renderer()->isRelPositioned() && positionedParent->renderer()->isRenderInline()) {
LayoutSize offset = toRenderInline(positionedParent->renderer())->offsetForInFlowPositionedInline(*toRenderBox(renderer()));
localPoint += offset;
}
}
localPoint.move(offsetForInFlowPosition());
// FIXME: We'd really like to just get rid of the concept of a layer rectangle and rely on the renderers.
localPoint -= inlineBoundingBoxOffset;
return localPoint;
}
const LayoutSize RenderLayer::offsetForInFlowPosition() const
{
return renderer()->isRelPositioned() ? toRenderBoxModelObject(renderer())->offsetForInFlowPosition() : LayoutSize();
}
TransformationMatrix RenderLayer::perspectiveTransform() const
{
if (!renderer()->hasTransform())
return TransformationMatrix();
RenderStyle* style = renderer()->style();
if (!style->hasPerspective())
return TransformationMatrix();
// Maybe fetch the perspective from the backing?
const IntRect borderBox = toRenderBox(renderer())->pixelSnappedBorderBoxRect();
const float boxWidth = borderBox.width();
const float boxHeight = borderBox.height();
float perspectiveOriginX = floatValueForLength(style->perspectiveOriginX(), boxWidth);
float perspectiveOriginY = floatValueForLength(style->perspectiveOriginY(), boxHeight);
// A perspective origin of 0,0 makes the vanishing point in the center of the element.
// We want it to be in the top-left, so subtract half the height and width.
perspectiveOriginX -= boxWidth / 2.0f;
perspectiveOriginY -= boxHeight / 2.0f;
TransformationMatrix t;
t.translate(perspectiveOriginX, perspectiveOriginY);
t.applyPerspective(style->perspective());
t.translate(-perspectiveOriginX, -perspectiveOriginY);
return t;
}
FloatPoint RenderLayer::perspectiveOrigin() const
{
if (!renderer()->hasTransform())
return FloatPoint();
const LayoutRect borderBox = toRenderBox(renderer())->borderBoxRect();
RenderStyle* style = renderer()->style();
return FloatPoint(floatValueForLength(style->perspectiveOriginX(), borderBox.width().toFloat()), floatValueForLength(style->perspectiveOriginY(), borderBox.height().toFloat()));
}
RenderLayer* RenderLayer::enclosingPositionedAncestor() const
{
RenderLayer* curr = parent();
while (curr && !curr->isPositionedContainer())
curr = curr->parent();
return curr;
}
const RenderLayer* RenderLayer::compositingContainer() const
{
if (stackingNode()->isNormalFlowOnly())
return parent();
if (RenderLayerStackingNode* ancestorStackingNode = stackingNode()->ancestorStackingContextNode())
return ancestorStackingNode->layer();
return 0;
}
RenderLayer* RenderLayer::enclosingFilterLayer(IncludeSelfOrNot includeSelf) const
{
const RenderLayer* curr = (includeSelf == IncludeSelf) ? this : parent();
for (; curr; curr = curr->parent()) {
if (curr->requiresFullLayerImageForFilters())
return const_cast<RenderLayer*>(curr);
}
return 0;
}
bool RenderLayer::hasAncestorWithFilterOutsets() const
{
for (const RenderLayer* curr = this; curr; curr = curr->parent()) {
RenderLayerModelObject* renderer = curr->renderer();
if (renderer->style()->hasFilterOutsets())
return true;
}
return false;
}
RenderLayer* RenderLayer::transparentPaintingAncestor()
{
for (RenderLayer* curr = parent(); curr; curr = curr->parent()) {
if (curr->isTransparent())
return curr;
}
return 0;
}
enum TransparencyClipBoxBehavior {
PaintingTransparencyClipBox,
HitTestingTransparencyClipBox
};
enum TransparencyClipBoxMode {
DescendantsOfTransparencyClipBox,
RootOfTransparencyClipBox
};
static LayoutRect transparencyClipBox(const RenderLayer*, const RenderLayer* rootLayer, TransparencyClipBoxBehavior, TransparencyClipBoxMode, const LayoutSize& subPixelAccumulation);
static void expandClipRectForDescendantsAndReflection(LayoutRect& clipRect, const RenderLayer* layer, const RenderLayer* rootLayer,
TransparencyClipBoxBehavior transparencyBehavior, const LayoutSize& subPixelAccumulation)
{
// Note: we don't have to walk z-order lists since transparent elements always establish
// a stacking container. This means we can just walk the layer tree directly.
for (RenderLayer* curr = layer->firstChild(); curr; curr = curr->nextSibling())
clipRect.unite(transparencyClipBox(curr, rootLayer, transparencyBehavior, DescendantsOfTransparencyClipBox, subPixelAccumulation));
}
static LayoutRect transparencyClipBox(const RenderLayer* layer, const RenderLayer* rootLayer, TransparencyClipBoxBehavior transparencyBehavior,
TransparencyClipBoxMode transparencyMode, const LayoutSize& subPixelAccumulation)
{
// FIXME: Although this function completely ignores CSS-imposed clipping, we did already intersect with the
// paintDirtyRect, and that should cut down on the amount we have to paint. Still it
// would be better to respect clips.
if (rootLayer != layer && ((transparencyBehavior == PaintingTransparencyClipBox && layer->paintsWithTransform())
|| (transparencyBehavior == HitTestingTransparencyClipBox && layer->hasTransform()))) {
// The best we can do here is to use enclosed bounding boxes to establish a "fuzzy" enough clip to encompass
// the transformed layer and all of its children.
const RenderLayer* rootLayerForTransform = rootLayer;
LayoutPoint delta;
layer->convertToLayerCoords(rootLayerForTransform, delta);
delta.move(subPixelAccumulation);
IntPoint pixelSnappedDelta = roundedIntPoint(delta);
TransformationMatrix transform;
transform.translate(pixelSnappedDelta.x(), pixelSnappedDelta.y());
transform = transform * *layer->transform();
// We don't use fragment boxes when collecting a transformed layer's bounding box, since it always
// paints unfragmented.
LayoutRect clipRect = layer->physicalBoundingBox(layer);
expandClipRectForDescendantsAndReflection(clipRect, layer, layer, transparencyBehavior, subPixelAccumulation);
layer->renderer()->style()->filterOutsets().expandRect(clipRect);
LayoutRect result = transform.mapRect(clipRect);
return result;
}
LayoutRect clipRect = layer->physicalBoundingBox(rootLayer);
expandClipRectForDescendantsAndReflection(clipRect, layer, rootLayer, transparencyBehavior, subPixelAccumulation);
layer->renderer()->style()->filterOutsets().expandRect(clipRect);
clipRect.move(subPixelAccumulation);
return clipRect;
}
LayoutRect RenderLayer::paintingExtent(const RenderLayer* rootLayer, const LayoutRect& paintDirtyRect, const LayoutSize& subPixelAccumulation)
{
return intersection(transparencyClipBox(this, rootLayer, PaintingTransparencyClipBox, RootOfTransparencyClipBox, subPixelAccumulation), paintDirtyRect);
}
void RenderLayer::beginTransparencyLayers(GraphicsContext* context, const RenderLayer* rootLayer, const LayoutRect& paintDirtyRect, const LayoutSize& subPixelAccumulation)
{
if (isTransparent() && m_usedTransparency)
return;
RenderLayer* ancestor = transparentPaintingAncestor();
if (ancestor)
ancestor->beginTransparencyLayers(context, rootLayer, paintDirtyRect, subPixelAccumulation);
if (isTransparent()) {
m_usedTransparency = true;
context->save();
LayoutRect clipRect = paintingExtent(rootLayer, paintDirtyRect, subPixelAccumulation);
context->clip(clipRect);
context->beginTransparencyLayer(renderer()->opacity());
#ifdef REVEAL_TRANSPARENCY_LAYERS
context->setFillColor(Color(0.0f, 0.0f, 0.5f, 0.2f));
context->fillRect(clipRect);
#endif
}
}
void* RenderLayer::operator new(size_t sz)
{
return partitionAlloc(Partitions::getRenderingPartition(), sz);
}
void RenderLayer::operator delete(void* ptr)
{
partitionFree(ptr);
}
void RenderLayer::addChild(RenderLayer* child, RenderLayer* beforeChild)
{
RenderLayer* prevSibling = beforeChild ? beforeChild->previousSibling() : lastChild();
if (prevSibling) {
child->setPreviousSibling(prevSibling);
prevSibling->setNextSibling(child);
ASSERT(prevSibling != child);
} else
setFirstChild(child);
if (beforeChild) {
beforeChild->setPreviousSibling(child);
child->setNextSibling(beforeChild);
ASSERT(beforeChild != child);
} else
setLastChild(child);
child->m_parent = this;
if (child->stackingNode()->isNormalFlowOnly())
m_stackingNode->dirtyNormalFlowList();
if (!child->stackingNode()->isNormalFlowOnly() || child->firstChild()) {
// Dirty the z-order list in which we are contained. The ancestorStackingContextNode() can be null in the
// case where we're building up generated content layers. This is ok, since the lists will start
// off dirty in that case anyway.
child->stackingNode()->dirtyStackingContextZOrderLists();
}
dirtyAncestorChainHasSelfPaintingLayerDescendantStatus();
}
RenderLayer* RenderLayer::removeChild(RenderLayer* oldChild)
{
if (oldChild->previousSibling())
oldChild->previousSibling()->setNextSibling(oldChild->nextSibling());
if (oldChild->nextSibling())
oldChild->nextSibling()->setPreviousSibling(oldChild->previousSibling());
if (m_first == oldChild)
m_first = oldChild->nextSibling();
if (m_last == oldChild)
m_last = oldChild->previousSibling();
if (oldChild->stackingNode()->isNormalFlowOnly())
m_stackingNode->dirtyNormalFlowList();
if (!oldChild->stackingNode()->isNormalFlowOnly() || oldChild->firstChild()) {
// Dirty the z-order list in which we are contained. When called via the
// reattachment process in removeOnlyThisLayer, the layer may already be disconnected
// from the main layer tree, so we need to null-check the
// |stackingContext| value.
oldChild->stackingNode()->dirtyStackingContextZOrderLists();
}
oldChild->setPreviousSibling(0);
oldChild->setNextSibling(0);
oldChild->m_parent = 0;
dirtyAncestorChainHasSelfPaintingLayerDescendantStatus();
return oldChild;
}
void RenderLayer::removeOnlyThisLayer()
{
if (!m_parent)
return;
m_clipper.clearClipRectsIncludingDescendants();
RenderLayer* nextSib = nextSibling();
// Now walk our kids and reattach them to our parent.
RenderLayer* current = m_first;
while (current) {
RenderLayer* next = current->nextSibling();
removeChild(current);
m_parent->addChild(current, nextSib);
// FIXME: We should call a specialized version of this function.
current->updateLayerPositionsAfterLayout();
current = next;
}
// Remove us from the parent.
m_parent->removeChild(this);
m_renderer->destroyLayer();
}
void RenderLayer::insertOnlyThisLayer()
{
if (!m_parent && renderer()->parent()) {
// We need to connect ourselves when our renderer() has a parent.
// Find our enclosingLayer and add ourselves.
RenderLayer* parentLayer = renderer()->parent()->enclosingLayer();
ASSERT(parentLayer);
RenderLayer* beforeChild = renderer()->parent()->findNextLayer(parentLayer, renderer());
parentLayer->addChild(this, beforeChild);
}
// Remove all descendant layers from the hierarchy and add them to the new position.
for (RenderObject* curr = renderer()->slowFirstChild(); curr; curr = curr->nextSibling())
curr->moveLayers(m_parent, this);
// Clear out all the clip rects.
m_clipper.clearClipRectsIncludingDescendants();
}
// Returns the layer reached on the walk up towards the ancestor.
static inline const RenderLayer* accumulateOffsetTowardsAncestor(const RenderLayer* layer, const RenderLayer* ancestorLayer, LayoutPoint& location)
{
ASSERT(ancestorLayer != layer);
const RenderLayerModelObject* renderer = layer->renderer();
EPosition position = renderer->style()->position();
RenderLayer* parentLayer;
if (position == AbsolutePosition) {
// Do what enclosingPositionedAncestor() does, but check for ancestorLayer along the way.
parentLayer = layer->parent();
bool foundAncestorFirst = false;
while (parentLayer) {
// RenderFlowThread is a positioned container, child of RenderView, positioned at (0,0).
// This implies that, for out-of-flow positioned elements inside a RenderFlowThread,
// we are bailing out before reaching root layer.
if (parentLayer->isPositionedContainer())
break;
if (parentLayer == ancestorLayer) {
foundAncestorFirst = true;
break;
}
parentLayer = parentLayer->parent();
}
if (foundAncestorFirst) {
// Found ancestorLayer before the abs. positioned container, so compute offset of both relative
// to enclosingPositionedAncestor and subtract.
RenderLayer* positionedAncestor = parentLayer->enclosingPositionedAncestor();
LayoutPoint thisCoords;
layer->convertToLayerCoords(positionedAncestor, thisCoords);
LayoutPoint ancestorCoords;
ancestorLayer->convertToLayerCoords(positionedAncestor, ancestorCoords);
location += (thisCoords - ancestorCoords);
return ancestorLayer;
}
} else
parentLayer = layer->parent();
if (!parentLayer)
return 0;
location += toSize(layer->location());
return parentLayer;
}
void RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, LayoutPoint& location) const
{
if (ancestorLayer == this)
return;
const RenderLayer* currLayer = this;
while (currLayer && currLayer != ancestorLayer)
currLayer = accumulateOffsetTowardsAncestor(currLayer, ancestorLayer, location);
}
void RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, LayoutRect& rect) const
{
LayoutPoint delta;
convertToLayerCoords(ancestorLayer, delta);
rect.move(-delta.x(), -delta.y());
}
void RenderLayer::updateStackingNode()
{
if (requiresStackingNode())
m_stackingNode = adoptPtr(new RenderLayerStackingNode(this));
else
m_stackingNode = nullptr;
}
static bool inContainingBlockChain(RenderLayer* startLayer, RenderLayer* endLayer)
{
if (startLayer == endLayer)
return true;
RenderView* view = startLayer->renderer()->view();
for (RenderBlock* currentBlock = startLayer->renderer()->containingBlock(); currentBlock && currentBlock != view; currentBlock = currentBlock->containingBlock()) {
if (currentBlock->layer() == endLayer)
return true;
}
return false;
}
void RenderLayer::clipToRect(const LayerPaintingInfo& localPaintingInfo, GraphicsContext* context, const ClipRect& clipRect,
BorderRadiusClippingRule rule)
{
if (clipRect.rect() == localPaintingInfo.paintDirtyRect && !clipRect.hasRadius())
return;
context->save();
context->clip(pixelSnappedIntRect(clipRect.rect()));
if (!clipRect.hasRadius())
return;
// If the clip rect has been tainted by a border radius, then we have to walk up our layer chain applying the clips from
// any layers with overflow. The condition for being able to apply these clips is that the overflow object be in our
// containing block chain so we check that also.
for (RenderLayer* layer = rule == IncludeSelfForBorderRadius ? this : parent(); layer; layer = layer->parent()) {
if (layer->renderer()->hasOverflowClip() && layer->renderer()->style()->hasBorderRadius() && inContainingBlockChain(this, layer)) {
LayoutPoint delta;
layer->convertToLayerCoords(localPaintingInfo.rootLayer, delta);
context->clipRoundedRect(layer->renderer()->style()->getRoundedInnerBorderFor(LayoutRect(delta, layer->size())));
}
if (layer == localPaintingInfo.rootLayer)
break;
}
}
void RenderLayer::restoreClip(GraphicsContext* context, const LayoutRect& paintDirtyRect, const ClipRect& clipRect)
{
if (clipRect.rect() == paintDirtyRect && !clipRect.hasRadius())
return;
context->restore();
}
bool RenderLayer::intersectsDamageRect(const LayoutRect& layerBounds, const LayoutRect& damageRect, const RenderLayer* rootLayer, const LayoutPoint* offsetFromRoot) const
{
// Always examine the canvas and the root.
if (isRootLayer())
return true;
// If we aren't an inline flow, and our layer bounds do intersect the damage rect, then we
// can go ahead and return true.
RenderView* view = renderer()->view();
ASSERT(view);
if (view && !renderer()->isRenderInline()) {
if (layerBounds.intersects(damageRect))
return true;
}
// Otherwise we need to compute the bounding box of this single layer and see if it intersects
// the damage rect.
return physicalBoundingBox(rootLayer, offsetFromRoot).intersects(damageRect);
}
LayoutRect RenderLayer::logicalBoundingBox() const
{
// There are three special cases we need to consider.
// (1) Inline Flows. For inline flows we will create a bounding box that fully encompasses all of the lines occupied by the
// inline. In other words, if some <span> wraps to three lines, we'll create a bounding box that fully encloses the
// line boxes of all three lines (including overflow on those lines).
// (2) Left/Top Overflow. The width/height of layers already includes right/bottom overflow. However, in the case of left/top
// overflow, we have to create a bounding box that will extend to include this overflow.
// (3) Floats. When a layer has overhanging floats that it paints, we need to make sure to include these overhanging floats
// as part of our bounding box. We do this because we are the responsible layer for both hit testing and painting those
// floats.
LayoutRect result;
if (renderer()->isInline() && renderer()->isRenderInline()) {
result = toRenderInline(renderer())->linesVisualOverflowBoundingBox();
} else {
RenderBox* box = renderBox();
ASSERT(box);
result = box->borderBoxRect();
result.unite(box->visualOverflowRect());
}
ASSERT(renderer()->view());
return result;
}
LayoutRect RenderLayer::physicalBoundingBox(const RenderLayer* ancestorLayer, const LayoutPoint* offsetFromRoot) const
{
LayoutPoint delta;
if (offsetFromRoot)
delta = *offsetFromRoot;
else
convertToLayerCoords(ancestorLayer, delta);
LayoutRect result = logicalBoundingBox();
result.moveBy(delta);
return result;
}
static void expandRectForReflectionAndStackingChildren(const RenderLayer* ancestorLayer, LayoutRect& result)
{
ASSERT(ancestorLayer->stackingNode()->isStackingContext() || !ancestorLayer->stackingNode()->hasPositiveZOrderList());
#if ENABLE(ASSERT)
LayerListMutationDetector mutationChecker(const_cast<RenderLayer*>(ancestorLayer)->stackingNode());
#endif
RenderLayerStackingNodeIterator iterator(*ancestorLayer->stackingNode(), AllChildren);
while (RenderLayerStackingNode* node = iterator.next()) {
result.unite(node->layer()->boundingBoxForCompositing(ancestorLayer));
}
}
LayoutRect RenderLayer::physicalBoundingBoxIncludingReflectionAndStackingChildren(const RenderLayer* ancestorLayer, const LayoutPoint& offsetFromRoot) const
{
LayoutPoint origin;
LayoutRect result = physicalBoundingBox(ancestorLayer, &origin);
const_cast<RenderLayer*>(this)->stackingNode()->updateLayerListsIfNeeded();
expandRectForReflectionAndStackingChildren(this, result);
result.moveBy(offsetFromRoot);
return result;
}
LayoutRect RenderLayer::boundingBoxForCompositing(const RenderLayer* ancestorLayer) const
{
if (!isSelfPaintingLayer())
return LayoutRect();
if (!ancestorLayer)
ancestorLayer = this;
// The root layer is always just the size of the document.
if (isRootLayer())
return m_renderer->view()->unscaledDocumentRect();
const bool shouldIncludeTransform = paintsWithTransform();
LayoutRect localClipRect = clipper().localClipRect();
if (localClipRect != PaintInfo::infiniteRect()) {
if (shouldIncludeTransform)
localClipRect = transform()->mapRect(localClipRect);
LayoutPoint delta;
convertToLayerCoords(ancestorLayer, delta);
localClipRect.moveBy(delta);
return localClipRect;
}
LayoutPoint origin;
LayoutRect result = physicalBoundingBox(ancestorLayer, &origin);
const_cast<RenderLayer*>(this)->stackingNode()->updateLayerListsIfNeeded();
expandRectForReflectionAndStackingChildren(this, result);
// FIXME: We can optimize the size of the composited layers, by not enlarging
// filtered areas with the outsets if we know that the filter is going to render in hardware.
// https://bugs.webkit.org/show_bug.cgi?id=81239
m_renderer->style()->filterOutsets().expandRect(result);
if (shouldIncludeTransform)
result = transform()->mapRect(result);
LayoutPoint delta;
convertToLayerCoords(ancestorLayer, delta);
result.moveBy(delta);
return result;
}
bool RenderLayer::paintsWithTransform() const
{
// FIXME(sky): Remove
return transform();
}
bool RenderLayer::shouldBeSelfPaintingLayer() const
{
return m_layerType == NormalLayer;
}
void RenderLayer::updateSelfPaintingLayer()
{
bool isSelfPaintingLayer = shouldBeSelfPaintingLayer();
if (this->isSelfPaintingLayer() == isSelfPaintingLayer)
return;
m_isSelfPaintingLayer = isSelfPaintingLayer;
if (parent())
parent()->dirtyAncestorChainHasSelfPaintingLayerDescendantStatus();
}
bool RenderLayer::hasNonEmptyChildRenderers() const
{
// Some HTML can cause whitespace text nodes to have renderers, like:
// <div>
// <img src=...>
// </div>
// so test for 0x0 RenderTexts here
for (RenderObject* child = renderer()->slowFirstChild(); child; child = child->nextSibling()) {
if (!child->hasLayer()) {
if (child->isRenderInline() || !child->isBox())
return true;
if (toRenderBox(child)->width() > 0 || toRenderBox(child)->height() > 0)
return true;
}
}
return false;
}
bool RenderLayer::hasBoxDecorationsOrBackground() const
{
return renderer()->style()->hasBoxDecorations() || renderer()->style()->hasBackground();
}
bool RenderLayer::hasVisibleBoxDecorations() const
{
return hasBoxDecorationsOrBackground();
}
bool RenderLayer::isVisuallyNonEmpty() const
{
if (hasNonEmptyChildRenderers())
return true;
if (renderer()->isReplaced())
return true;
if (hasVisibleBoxDecorations())
return true;
return false;
}
void RenderLayer::updateFilters(const RenderStyle* oldStyle, const RenderStyle* newStyle)
{
if (!newStyle->hasFilter() && (!oldStyle || !oldStyle->hasFilter()))
return;
updateOrRemoveFilterClients();
updateOrRemoveFilterEffectRenderer();
}
void RenderLayer::styleChanged(StyleDifference diff, const RenderStyle* oldStyle)
{
m_stackingNode->updateIsNormalFlowOnly();
m_stackingNode->updateStackingNodesAfterStyleChange(oldStyle);
// Overlay scrollbars can make this layer self-painting so we need
// to recompute the bit once scrollbars have been updated.
updateSelfPaintingLayer();
updateTransform(oldStyle, renderer()->style());
updateFilters(oldStyle, renderer()->style());
}
FilterOperations RenderLayer::computeFilterOperations(const RenderStyle* style)
{
return style->filter();
}
void RenderLayer::updateOrRemoveFilterClients()
{
if (!hasFilter()) {
removeFilterInfoIfNeeded();
return;
}
if (renderer()->style()->filter().hasReferenceFilter())
ensureFilterInfo()->updateReferenceFilterClients(renderer()->style()->filter());
else if (hasFilterInfo())
filterInfo()->removeReferenceFilterClients();
}
void RenderLayer::updateOrRemoveFilterEffectRenderer()
{
// FilterEffectRenderer is only used to render the filters in software mode,
// so we always need to run updateOrRemoveFilterEffectRenderer after the composited
// mode might have changed for this layer.
if (!paintsWithFilters()) {
// Don't delete the whole filter info here, because we might use it
// for loading CSS shader files.
if (RenderLayerFilterInfo* filterInfo = this->filterInfo())
filterInfo->setRenderer(nullptr);
return;
}
RenderLayerFilterInfo* filterInfo = ensureFilterInfo();
if (!filterInfo->renderer()) {
RefPtr<FilterEffectRenderer> filterRenderer = FilterEffectRenderer::create();
filterInfo->setRenderer(filterRenderer.release());
// We can optimize away code paths in other places if we know that there are no software filters.
renderer()->document().view()->setHasSoftwareFilters(true);
}
// If the filter fails to build, remove it from the layer. It will still attempt to
// go through regular processing (e.g. compositing), but never apply anything.
if (!filterInfo->renderer()->build(renderer(), computeFilterOperations(renderer()->style())))
filterInfo->setRenderer(nullptr);
}
} // namespace blink
#ifndef NDEBUG
void showLayerTree(const blink::RenderLayer* layer)
{
if (!layer)
return;
if (blink::LocalFrame* frame = layer->renderer()->frame()) {
WTF::String output = externalRepresentation(frame, blink::RenderAsTextShowAllLayers | blink::RenderAsTextShowLayerNesting | blink::RenderAsTextShowCompositedLayers | blink::RenderAsTextShowAddresses | blink::RenderAsTextShowIDAndClass | blink::RenderAsTextDontUpdateLayout | blink::RenderAsTextShowLayoutState);
fprintf(stderr, "%s\n", output.utf8().data());
}
}
void showLayerTree(const blink::RenderObject* renderer)
{
if (!renderer)
return;
showLayerTree(renderer->enclosingLayer());
}
#endif