| /* |
| * 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 |