/* * 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 * David Baron * Christian Biesinger * Randall Jesup * Roland Mainz * Josh Soref * Boris Zbarsky * * 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 "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()); } 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())); } 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; // 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 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()); } 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(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(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; 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(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