blob: 9eaf5bc86717f4655c3209a7b523c6088afa86d3 [file] [log] [blame]
/*
* 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/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/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/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/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(RenderBox* renderer, LayerType type)
: m_layerType(type)
, m_isRootLayer(renderer->isRenderView())
, m_3DTransformedDescendantStatusDirty(true)
, m_has3DTransformedDescendant(false)
, m_renderer(renderer)
, m_parent(0)
, m_previous(0)
, m_next(0)
, m_first(0)
, m_last(0)
, m_clipper(*renderer)
{
m_stackingNode = adoptPtr(new RenderLayerStackingNode(this));
m_isSelfPaintingLayer = shouldBeSelfPaintingLayer();
}
RenderLayer::~RenderLayer()
{
}
void RenderLayer::updateLayerPositionsAfterLayout()
{
m_clipper.clearClipRectsIncludingDescendants();
}
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()->renderer()->style()->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 (renderer()->style()->preserves3D())
return renderer()->has3DTransform() || m_has3DTransformedDescendant;
return renderer()->has3DTransform();
}
IntSize RenderLayer::size() const
{
// FIXME: Is snapping the size really needed here?
RenderBox* box = renderer();
return pixelSnappedIntSize(box->size(), box->location());
}
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 {
localPoint += renderer()->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();
}
}
// 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;
}
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;
}
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();
}
}
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;
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 RenderBox* 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());
}
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 = renderer();
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();
LayoutRect localClipRect = clipper().localClipRect();
if (localClipRect != PaintInfo::infiniteRect()) {
if (renderer()->transform())
localClipRect = renderer()->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 (renderer()->transform())
result = renderer()->transform()->mapRect(result);
LayoutPoint delta;
convertToLayerCoords(ancestorLayer, delta);
result.moveBy(delta);
return result;
}
bool RenderLayer::shouldBeSelfPaintingLayer() const
{
return m_layerType == NormalLayer;
}
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.
m_isSelfPaintingLayer = shouldBeSelfPaintingLayer();
}
} // namespace blink