Index: Source/core/rendering/RenderLayer.cpp |
diff --git a/Source/core/rendering/RenderLayer.cpp b/Source/core/rendering/RenderLayer.cpp |
deleted file mode 100644 |
index 585336ca6bd21e49a3714fc8b50d998f5fde9823..0000000000000000000000000000000000000000 |
--- a/Source/core/rendering/RenderLayer.cpp |
+++ /dev/null |
@@ -1,2935 +0,0 @@ |
-/* |
- * 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 "config.h" |
-#include "core/rendering/RenderLayer.h" |
- |
-#include "core/CSSPropertyNames.h" |
-#include "core/HTMLNames.h" |
-#include "core/css/PseudoStyleRequest.h" |
-#include "core/dom/Document.h" |
-#include "core/dom/shadow/ShadowRoot.h" |
-#include "core/frame/DeprecatedScheduleStyleRecalcDuringLayout.h" |
-#include "core/frame/FrameView.h" |
-#include "core/frame/LocalFrame.h" |
-#include "core/html/HTMLFrameElement.h" |
-#include "core/layout/HitTestRequest.h" |
-#include "core/layout/HitTestResult.h" |
-#include "core/layout/HitTestingTransformState.h" |
-#include "core/layout/LayoutTreeAsText.h" |
-#include "core/layout/compositing/CompositedLayerMapping.h" |
-#include "core/layout/compositing/RenderLayerCompositor.h" |
-#include "core/page/Page.h" |
-#include "core/page/scrolling/ScrollingCoordinator.h" |
-#include "core/rendering/ColumnInfo.h" |
-#include "core/rendering/FilterEffectRenderer.h" |
-#include "core/rendering/RenderFlowThread.h" |
-#include "core/rendering/RenderGeometryMap.h" |
-#include "core/rendering/RenderInline.h" |
-#include "core/rendering/RenderPart.h" |
-#include "core/rendering/RenderReplica.h" |
-#include "core/rendering/RenderScrollbar.h" |
-#include "core/rendering/RenderScrollbarPart.h" |
-#include "core/rendering/RenderView.h" |
-#include "core/rendering/svg/ReferenceFilterBuilder.h" |
-#include "core/rendering/svg/RenderSVGRoot.h" |
-#include "platform/LengthFunctions.h" |
-#include "platform/Partitions.h" |
-#include "platform/RuntimeEnabledFeatures.h" |
-#include "platform/TraceEvent.h" |
-#include "platform/geometry/FloatPoint3D.h" |
-#include "platform/geometry/FloatRect.h" |
-#include "platform/geometry/TransformState.h" |
-#include "platform/graphics/filters/ReferenceFilter.h" |
-#include "platform/graphics/filters/SourceGraphic.h" |
-#include "platform/transforms/ScaleTransformOperation.h" |
-#include "platform/transforms/TransformationMatrix.h" |
-#include "platform/transforms/TranslateTransformOperation.h" |
-#include "public/platform/Platform.h" |
-#include "wtf/StdLibExtras.h" |
-#include "wtf/text/CString.h" |
- |
-namespace blink { |
- |
-namespace { |
- |
-static CompositingQueryMode gCompositingQueryMode = |
- CompositingQueriesAreOnlyAllowedInCertainDocumentLifecyclePhases; |
- |
-} // namespace |
- |
-using namespace HTMLNames; |
- |
-RenderLayer::RenderLayer(RenderLayerModelObject* renderer, LayerType type) |
- : m_layerType(type) |
- , m_hasSelfPaintingLayerDescendant(false) |
- , m_hasSelfPaintingLayerDescendantDirty(false) |
- , m_isRootLayer(renderer->isRenderView()) |
- , m_visibleContentStatusDirty(true) |
- , m_hasVisibleContent(false) |
- , m_visibleDescendantStatusDirty(false) |
- , m_hasVisibleDescendant(false) |
- , m_hasVisibleNonLayerContent(false) |
- , m_isPaginated(false) |
-#if ENABLE(ASSERT) |
- , m_needsPositionUpdate(true) |
-#endif |
- , m_3DTransformedDescendantStatusDirty(true) |
- , m_has3DTransformedDescendant(false) |
- , m_containsDirtyOverlayScrollbars(false) |
- , m_hasFilterInfo(false) |
- , m_needsAncestorDependentCompositingInputsUpdate(true) |
- , m_needsDescendantDependentCompositingInputsUpdate(true) |
- , m_childNeedsCompositingInputsUpdate(true) |
- , m_hasCompositingDescendant(false) |
- , m_hasNonCompositedChild(false) |
- , m_shouldIsolateCompositedDescendants(false) |
- , m_lostGroupedMapping(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_enclosingPaginationLayer(0) |
- , m_potentialCompositingReasonsFromStyle(CompositingReasonNone) |
- , m_compositingReasons(CompositingReasonNone) |
- , m_groupedMapping(0) |
- , m_clipper(*renderer) |
-{ |
- updateStackingNode(); |
- |
- m_isSelfPaintingLayer = shouldBeSelfPaintingLayer(); |
- |
- if (!renderer->slowFirstChild() && renderer->style()) { |
- m_visibleContentStatusDirty = false; |
- m_hasVisibleContent = renderer->style()->visibility() == VISIBLE; |
- } |
- |
- updateScrollableArea(); |
-} |
- |
-RenderLayer::~RenderLayer() |
-{ |
- if (renderer()->frame() && renderer()->frame()->page()) { |
- if (ScrollingCoordinator* scrollingCoordinator = renderer()->frame()->page()->scrollingCoordinator()) |
- scrollingCoordinator->willDestroyRenderLayer(this); |
- } |
- |
- removeFilterInfoIfNeeded(); |
- |
- if (groupedMapping()) { |
- DisableCompositingQueryAsserts disabler; |
- groupedMapping()->removeRenderLayerFromSquashingGraphicsLayer(this); |
- setGroupedMapping(0); |
- } |
- |
- // Child layers will be deleted by their corresponding render objects, so |
- // we don't need to delete them ourselves. |
- |
- clearCompositedLayerMapping(true); |
- |
- if (m_reflectionInfo) |
- m_reflectionInfo->destroy(); |
-} |
- |
-String RenderLayer::debugName() const |
-{ |
- if (isReflection()) { |
- return renderer()->parent()->debugName() + " (reflection)"; |
- } |
- return renderer()->debugName(); |
-} |
- |
-RenderLayerCompositor* RenderLayer::compositor() const |
-{ |
- if (!renderer()->view()) |
- return 0; |
- return renderer()->view()->compositor(); |
-} |
- |
-void RenderLayer::contentChanged(ContentChangeType changeType) |
-{ |
- // updateLayerCompositingState will query compositingReasons for accelerated overflow scrolling. |
- // This is tripped by LayoutTests/compositing/content-changed-chicken-egg.html |
- DisableCompositingQueryAsserts disabler; |
- |
- if (changeType == CanvasChanged) |
- compositor()->setNeedsCompositingUpdate(CompositingUpdateAfterCompositingInputChange); |
- |
- if (changeType == CanvasContextChanged) { |
- compositor()->setNeedsCompositingUpdate(CompositingUpdateAfterCompositingInputChange); |
- |
- // Although we're missing test coverage, we need to call |
- // GraphicsLayer::setContentsToPlatformLayer with the new platform |
- // layer for this canvas. |
- // See http://crbug.com/349195 |
- if (hasCompositedLayerMapping()) |
- compositedLayerMapping()->setNeedsGraphicsLayerUpdate(GraphicsLayerUpdateSubtree); |
- } |
- |
- if (m_compositedLayerMapping) |
- m_compositedLayerMapping->contentChanged(changeType); |
-} |
- |
-bool RenderLayer::paintsWithFilters() const |
-{ |
- if (!renderer()->hasFilter()) |
- return false; |
- |
- // https://code.google.com/p/chromium/issues/detail?id=343759 |
- DisableCompositingQueryAsserts disabler; |
- return !m_compositedLayerMapping || compositingState() != PaintsIntoOwnBacking; |
-} |
- |
-LayoutSize RenderLayer::subpixelAccumulation() const |
-{ |
- return m_subpixelAccumulation; |
-} |
- |
-void RenderLayer::setSubpixelAccumulation(const LayoutSize& size) |
-{ |
- m_subpixelAccumulation = size; |
-} |
- |
-void RenderLayer::updateLayerPositionsAfterLayout() |
-{ |
- TRACE_EVENT0("blink,benchmark", "RenderLayer::updateLayerPositionsAfterLayout"); |
- |
- m_clipper.clearClipRectsIncludingDescendants(); |
- updateLayerPositionRecursive(); |
- |
- { |
- // FIXME: Remove incremental compositing updates after fixing the chicken/egg issues |
- // https://code.google.com/p/chromium/issues/detail?id=343756 |
- DisableCompositingQueryAsserts disabler; |
- bool needsPaginationUpdate = isPaginated() || enclosingPaginationLayer(); |
- updatePaginationRecursive(needsPaginationUpdate); |
- } |
-} |
- |
-void RenderLayer::updateLayerPositionRecursive() |
-{ |
- updateLayerPosition(); |
- |
- if (m_reflectionInfo) |
- m_reflectionInfo->reflection()->layout(); |
- |
- // FIXME: We should be able to remove this call because we don't care about |
- // any descendant-dependent flags, but code somewhere else is reading these |
- // flags and depending on us to update them. |
- updateDescendantDependentFlags(); |
- |
- for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) |
- child->updateLayerPositionRecursive(); |
-} |
- |
-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; |
- } |
- } |
-} |
- |
-bool RenderLayer::scrollsWithViewport() const |
-{ |
- return renderer()->style()->position() == FixedPosition && renderer()->containerForFixedPosition() == renderer()->view(); |
-} |
- |
-bool RenderLayer::scrollsWithRespectTo(const RenderLayer* other) const |
-{ |
- if (scrollsWithViewport() != other->scrollsWithViewport()) |
- return true; |
- return ancestorScrollingLayer() != other->ancestorScrollingLayer(); |
-} |
- |
-void RenderLayer::updateLayerPositionsAfterOverflowScroll() |
-{ |
- m_clipper.clearClipRectsIncludingDescendants(); |
- updateLayerPositionsAfterScrollRecursive(); |
-} |
- |
-void RenderLayer::updateLayerPositionsAfterScrollRecursive() |
-{ |
- if (updateLayerPosition()) |
- m_renderer->setPreviousPaintInvalidationRect(m_renderer->boundsRectForPaintInvalidation(m_renderer->containerForPaintInvalidation())); |
- |
- for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) |
- child->updateLayerPositionsAfterScrollRecursive(); |
-} |
- |
-void RenderLayer::updateTransformationMatrix() |
-{ |
- if (m_transform) { |
- RenderBox* box = renderBox(); |
- ASSERT(box); |
- m_transform->makeIdentity(); |
- box->style()->applyTransform(*m_transform, LayoutSize(box->pixelSnappedSize()), RenderStyle::IncludeTransformOrigin); |
- makeMatrixRenderable(*m_transform, compositor()->hasAcceleratedCompositing()); |
- } |
-} |
- |
-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()->hasTransformRelatedProperty() && 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, LayoutSize(box->pixelSnappedSize()), RenderStyle::ExcludeTransformOrigin); |
- makeMatrixRenderable(currTransform, compositor()->hasAcceleratedCompositing()); |
- return currTransform; |
- } |
- |
- return *m_transform; |
-} |
- |
-TransformationMatrix RenderLayer::renderableTransform(PaintBehavior paintBehavior) const |
-{ |
- if (!m_transform) |
- return TransformationMatrix(); |
- |
- if (paintBehavior & PaintBehaviorFlattenCompositingLayers) { |
- TransformationMatrix matrix = *m_transform; |
- makeMatrixRenderable(matrix, false /* flatten 3d */); |
- return matrix; |
- } |
- |
- return *m_transform; |
-} |
- |
-static bool checkContainingBlockChainForPagination(RenderLayerModelObject* renderer, RenderBox* ancestorColumnsRenderer) |
-{ |
- RenderView* view = renderer->view(); |
- RenderLayerModelObject* prevBlock = renderer; |
- RenderBlock* containingBlock; |
- for (containingBlock = renderer->containingBlock(); |
- containingBlock && containingBlock != view && containingBlock != ancestorColumnsRenderer; |
- containingBlock = containingBlock->containingBlock()) |
- prevBlock = containingBlock; |
- |
- // If the columns block wasn't in our containing block chain, then we aren't paginated by it. |
- if (containingBlock != ancestorColumnsRenderer) |
- return false; |
- |
- // If the previous block is absolutely positioned, then we can't be paginated by the columns block. |
- if (prevBlock->isOutOfFlowPositioned()) |
- return false; |
- |
- // Otherwise we are paginated by the columns block. |
- return true; |
-} |
- |
-// Convert a bounding box from flow thread coordinates, relative to |layer|, to visual coordinates, relative to |ancestorLayer|. |
-// See http://www.chromium.org/developers/design-documents/multi-column-layout for more info on these coordinate types. |
-static void convertFromFlowThreadToVisualBoundingBoxInAncestor(const RenderLayer* layer, const RenderLayer* ancestorLayer, LayoutRect& rect) |
-{ |
- RenderLayer* paginationLayer = layer->enclosingPaginationLayer(); |
- ASSERT(paginationLayer); |
- RenderFlowThread* flowThread = toRenderFlowThread(paginationLayer->renderer()); |
- |
- // First make the flow thread rectangle relative to the flow thread, not to |layer|. |
- LayoutPoint offsetWithinPaginationLayer; |
- layer->convertToLayerCoords(paginationLayer, offsetWithinPaginationLayer); |
- rect.moveBy(offsetWithinPaginationLayer); |
- |
- // Then make the rectangle visual, relative to the fragmentation context. Split our box up into |
- // the actual fragment boxes that render in the columns/pages and unite those together to get |
- // our true bounding box. |
- rect = flowThread->fragmentsBoundingBox(rect); |
- |
- // Finally, make the visual rectangle relative to |ancestorLayer|. |
- if (ancestorLayer->enclosingPaginationLayer() != paginationLayer) { |
- rect.moveBy(paginationLayer->visualOffsetFromAncestor(ancestorLayer)); |
- return; |
- } |
- // The ancestor layer is inside the same pagination layer as |layer|, so we need to subtract |
- // the visual distance from the ancestor layer to the pagination layer. |
- rect.moveBy(-ancestorLayer->visualOffsetFromAncestor(paginationLayer)); |
-} |
- |
-bool RenderLayer::useRegionBasedColumns() const |
-{ |
- return renderer()->document().regionBasedColumnsEnabled(); |
-} |
- |
-void RenderLayer::updatePaginationRecursive(bool needsPaginationUpdate) |
-{ |
- m_isPaginated = false; |
- m_enclosingPaginationLayer = 0; |
- |
- if (useRegionBasedColumns() && renderer()->isRenderFlowThread()) |
- needsPaginationUpdate = true; |
- |
- if (needsPaginationUpdate) |
- updatePagination(); |
- |
- if (renderer()->hasColumns()) |
- needsPaginationUpdate = true; |
- |
- for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) |
- child->updatePaginationRecursive(needsPaginationUpdate); |
-} |
- |
-void RenderLayer::updatePagination() |
-{ |
- bool usesRegionBasedColumns = useRegionBasedColumns(); |
- if ((!usesRegionBasedColumns && compositingState() != NotComposited) || !parent()) |
- return; // FIXME: For now the RenderView can't be paginated. Eventually printing will move to a model where it is though. |
- |
- // The main difference between the paginated booleans for the old column code and the new column code |
- // is that each paginated layer has to paint on its own with the new code. There is no |
- // recurring into child layers. This means that the m_isPaginated bits for the new column code can't just be set on |
- // "roots" that get split and paint all their descendants. Instead each layer has to be checked individually and |
- // genuinely know if it is going to have to split itself up when painting only its contents (and not any other descendant |
- // layers). We track an enclosingPaginationLayer instead of using a simple bit, since we want to be able to get back |
- // to that layer easily. |
- if (usesRegionBasedColumns && renderer()->isRenderFlowThread()) { |
- m_enclosingPaginationLayer = this; |
- return; |
- } |
- |
- if (m_stackingNode->isNormalFlowOnly()) { |
- if (usesRegionBasedColumns) { |
- // Content inside a transform is not considered to be paginated, since we simply |
- // paint the transform multiple times in each column, so we don't have to use |
- // fragments for the transformed content. |
- m_enclosingPaginationLayer = parent()->enclosingPaginationLayer(); |
- if (m_enclosingPaginationLayer && m_enclosingPaginationLayer->hasTransformRelatedProperty()) |
- m_enclosingPaginationLayer = 0; |
- } else { |
- m_isPaginated = parent()->renderer()->hasColumns(); |
- } |
- return; |
- } |
- |
- // For the new columns code, we want to walk up our containing block chain looking for an enclosing layer. Once |
- // we find one, then we just check its pagination status. |
- if (usesRegionBasedColumns) { |
- RenderView* view = renderer()->view(); |
- RenderBlock* containingBlock; |
- for (containingBlock = renderer()->containingBlock(); |
- containingBlock && containingBlock != view; |
- containingBlock = containingBlock->containingBlock()) { |
- if (containingBlock->hasLayer()) { |
- // Content inside a transform is not considered to be paginated, since we simply |
- // paint the transform multiple times in each column, so we don't have to use |
- // fragments for the transformed content. |
- m_enclosingPaginationLayer = containingBlock->layer()->enclosingPaginationLayer(); |
- if (m_enclosingPaginationLayer && m_enclosingPaginationLayer->hasTransformRelatedProperty()) |
- m_enclosingPaginationLayer = 0; |
- return; |
- } |
- } |
- return; |
- } |
- |
- // If we're not normal flow, then we need to look for a multi-column object between us and our stacking container. |
- RenderLayerStackingNode* ancestorStackingContextNode = m_stackingNode->ancestorStackingContextNode(); |
- for (RenderLayer* curr = parent(); curr; curr = curr->parent()) { |
- if (curr->renderer()->hasColumns()) { |
- m_isPaginated = checkContainingBlockChainForPagination(renderer(), curr->renderBox()); |
- return; |
- } |
- if (curr->stackingNode() == ancestorStackingContextNode) |
- return; |
- } |
-} |
- |
-void RenderLayer::clearPaginationRecursive() |
-{ |
- m_enclosingPaginationLayer = 0; |
- for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) |
- child->clearPaginationRecursive(); |
-} |
- |
-LayoutPoint RenderLayer::positionFromPaintInvalidationBacking(const RenderObject* renderObject, const RenderLayerModelObject* paintInvalidationContainer, const PaintInvalidationState* paintInvalidationState) |
-{ |
- FloatPoint point = renderObject->localToContainerPoint(FloatPoint(), paintInvalidationContainer, 0, 0, paintInvalidationState); |
- |
- // FIXME: Eventually we are going to unify coordinates in GraphicsLayer space. |
- if (paintInvalidationContainer && paintInvalidationContainer->layer()->groupedMapping()) |
- mapPointToPaintBackingCoordinates(paintInvalidationContainer, point); |
- |
- return LayoutPoint(point); |
-} |
- |
-void RenderLayer::mapPointToPaintBackingCoordinates(const RenderLayerModelObject* paintInvalidationContainer, FloatPoint& point) |
-{ |
- RenderLayer* paintInvalidationLayer = paintInvalidationContainer->layer(); |
- if (!paintInvalidationLayer->groupedMapping()) { |
- point.move(paintInvalidationLayer->compositedLayerMapping()->contentOffsetInCompositingLayer()); |
- return; |
- } |
- |
- RenderLayerModelObject* transformedAncestor = paintInvalidationLayer->enclosingTransformedAncestor()->renderer(); |
- if (!transformedAncestor) |
- return; |
- |
- // |paintInvalidationContainer| may have a local 2D transform on it, so take that into account when mapping into the space of the |
- // transformed ancestor. |
- point = paintInvalidationContainer->localToContainerPoint(point, transformedAncestor); |
- |
- point.moveBy(-paintInvalidationLayer->groupedMapping()->squashingOffsetFromTransformedAncestor()); |
-} |
- |
-void RenderLayer::mapRectToPaintBackingCoordinates(const RenderLayerModelObject* paintInvalidationContainer, LayoutRect& rect) |
-{ |
- RenderLayer* paintInvalidationLayer = paintInvalidationContainer->layer(); |
- if (!paintInvalidationLayer->groupedMapping()) { |
- rect.move(paintInvalidationLayer->compositedLayerMapping()->contentOffsetInCompositingLayer()); |
- return; |
- } |
- |
- RenderLayerModelObject* transformedAncestor = paintInvalidationLayer->enclosingTransformedAncestor()->renderer(); |
- if (!transformedAncestor) |
- return; |
- |
- // |paintInvalidationContainer| may have a local 2D transform on it, so take that into account when mapping into the space of the |
- // transformed ancestor. |
- rect = LayoutRect(paintInvalidationContainer->localToContainerQuad(FloatRect(rect), transformedAncestor).boundingBox()); |
- |
- rect.moveBy(-paintInvalidationLayer->groupedMapping()->squashingOffsetFromTransformedAncestor()); |
-} |
- |
-void RenderLayer::mapRectToPaintInvalidationBacking(const RenderObject* renderObject, const RenderLayerModelObject* paintInvalidationContainer, LayoutRect& rect, const PaintInvalidationState* paintInvalidationState) |
-{ |
- if (!paintInvalidationContainer->layer()->groupedMapping()) { |
- renderObject->mapRectToPaintInvalidationBacking(paintInvalidationContainer, rect, paintInvalidationState); |
- return; |
- } |
- |
- // This code adjusts the paint invalidation rectangle to be in the space of the transformed ancestor of the grouped (i.e. squashed) |
- // layer. This is because all layers that squash together need to issue paint invalidations w.r.t. a single container that is |
- // an ancestor of all of them, in order to properly take into account any local transforms etc. |
- // FIXME: remove this special-case code that works around the paint invalidation code structure. |
- renderObject->mapRectToPaintInvalidationBacking(paintInvalidationContainer, rect, paintInvalidationState); |
- |
- mapRectToPaintBackingCoordinates(paintInvalidationContainer, rect); |
-} |
- |
-LayoutRect RenderLayer::computePaintInvalidationRect(const RenderObject* renderObject, const RenderLayer* paintInvalidationContainer, const PaintInvalidationState* paintInvalidationState) |
-{ |
- if (!paintInvalidationContainer->groupedMapping()) |
- return renderObject->computePaintInvalidationRect(paintInvalidationContainer->renderer(), paintInvalidationState); |
- |
- LayoutRect rect = renderObject->clippedOverflowRectForPaintInvalidation(paintInvalidationContainer->renderer(), paintInvalidationState); |
- mapRectToPaintBackingCoordinates(paintInvalidationContainer->renderer(), rect); |
- return rect; |
-} |
- |
-void RenderLayer::dirtyVisibleContentStatus() |
-{ |
- m_visibleContentStatusDirty = true; |
- if (parent()) |
- parent()->dirtyAncestorChainVisibleDescendantStatus(); |
-} |
- |
-void RenderLayer::potentiallyDirtyVisibleContentStatus(EVisibility visibility) |
-{ |
- if (m_visibleContentStatusDirty) |
- return; |
- if (hasVisibleContent() == (visibility == VISIBLE)) |
- return; |
- dirtyVisibleContentStatus(); |
-} |
- |
-void RenderLayer::dirtyAncestorChainVisibleDescendantStatus() |
-{ |
- for (RenderLayer* layer = this; layer; layer = layer->parent()) { |
- if (layer->m_visibleDescendantStatusDirty) |
- break; |
- |
- layer->m_visibleDescendantStatusDirty = true; |
- } |
-} |
- |
-// FIXME: this is quite brute-force. We could be more efficient if we were to |
-// track state and update it as appropriate as changes are made in the Render tree. |
-void RenderLayer::updateScrollingStateAfterCompositingChange() |
-{ |
- TRACE_EVENT0("blink", "RenderLayer::updateScrollingStateAfterCompositingChange"); |
- m_hasVisibleNonLayerContent = false; |
- for (RenderObject* r = renderer()->slowFirstChild(); r; r = r->nextSibling()) { |
- if (!r->hasLayer()) { |
- m_hasVisibleNonLayerContent = true; |
- break; |
- } |
- } |
- |
- m_hasNonCompositedChild = false; |
- for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) { |
- if (child->compositingState() == NotComposited) { |
- m_hasNonCompositedChild = true; |
- return; |
- } |
- } |
-} |
- |
-// The descendant-dependent flags system is badly broken because we clean dirty |
-// bits in upward tree walks, which means we need to call updateDescendantDependentFlags |
-// at every node in the tree to fully clean all the dirty bits. While we'll in |
-// the process of fixing this issue, updateDescendantDependentFlagsForEntireSubtree |
-// provides a big hammer for actually cleaning all the dirty bits in a subtree. |
-// |
-// FIXME: Remove this function once the descendant-dependent flags system keeps |
-// its dirty bits scoped to subtrees. |
-void RenderLayer::updateDescendantDependentFlagsForEntireSubtree() |
-{ |
- updateDescendantDependentFlags(); |
- |
- for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) |
- child->updateDescendantDependentFlagsForEntireSubtree(); |
-} |
- |
-void RenderLayer::updateDescendantDependentFlags() |
-{ |
- if (m_visibleDescendantStatusDirty) { |
- m_hasVisibleDescendant = false; |
- |
- for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) { |
- child->updateDescendantDependentFlags(); |
- |
- if (child->m_hasVisibleContent || child->m_hasVisibleDescendant) { |
- m_hasVisibleDescendant = true; |
- break; |
- } |
- } |
- |
- m_visibleDescendantStatusDirty = false; |
- } |
- |
- if (m_visibleContentStatusDirty) { |
- bool previouslyHasVisibleContent = m_hasVisibleContent; |
- if (renderer()->style()->visibility() == VISIBLE) |
- m_hasVisibleContent = true; |
- else { |
- // layer may be hidden but still have some visible content, check for this |
- m_hasVisibleContent = false; |
- RenderObject* r = renderer()->slowFirstChild(); |
- while (r) { |
- if (r->style()->visibility() == VISIBLE && !r->hasLayer()) { |
- m_hasVisibleContent = true; |
- break; |
- } |
- RenderObject* rendererFirstChild = r->slowFirstChild(); |
- if (rendererFirstChild && !r->hasLayer()) |
- r = rendererFirstChild; |
- else if (r->nextSibling()) |
- r = r->nextSibling(); |
- else { |
- do { |
- r = r->parent(); |
- if (r == renderer()) |
- r = 0; |
- } while (r && !r->nextSibling()); |
- if (r) |
- r = r->nextSibling(); |
- } |
- } |
- } |
- m_visibleContentStatusDirty = false; |
- |
- if (hasVisibleContent() != previouslyHasVisibleContent) { |
- setNeedsCompositingInputsUpdate(); |
- // We need to tell m_renderer to recheck its rect because we |
- // pretend that invisible RenderObjects have 0x0 rects. Changing |
- // visibility therefore changes our rect and we need to visit |
- // this RenderObject during the invalidateTreeIfNeeded walk. |
- m_renderer->setMayNeedPaintInvalidation(); |
- } |
- } |
-} |
- |
-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 | NegativeZOrderChildren); |
- 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(); |
-} |
- |
-bool RenderLayer::updateLayerPosition() |
-{ |
- LayoutPoint localPoint; |
- LayoutPoint 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(); |
- m_size = lineBox.size(); |
- inlineBoundingBoxOffset = lineBox.location(); |
- localPoint.moveBy(inlineBoundingBoxOffset); |
- } else if (RenderBox* box = renderBox()) { |
- m_size = pixelSnappedIntSize(box->size(), box->location()); |
- localPoint.moveBy(box->topLeftLocation()); |
- } |
- |
- if (!renderer()->isOutOfFlowPositioned() && !renderer()->isColumnSpanAll() && 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() && !curr->isTableRow()) { |
- // Rows and cells share the same coordinate space (that of the section). |
- // Omit them when computing our xpos/ypos. |
- localPoint.moveBy(toRenderBox(curr)->topLeftLocation()); |
- } |
- curr = curr->parent(); |
- } |
- if (curr->isBox() && curr->isTableRow()) { |
- // Put ourselves into the row coordinate space. |
- localPoint.moveBy(-toRenderBox(curr)->topLeftLocation()); |
- } |
- } |
- |
- // Subtract our parent's scroll offset. |
- if (renderer()->isOutOfFlowPositioned() && enclosingPositionedAncestor()) { |
- RenderLayer* positionedParent = enclosingPositionedAncestor(); |
- |
- // For positioned layers, we subtract out the enclosing positioned layer's scroll offset. |
- if (positionedParent->renderer()->hasOverflowClip()) { |
- IntSize offset = positionedParent->renderBox()->scrolledContentOffset(); |
- localPoint -= offset; |
- } |
- |
- if (positionedParent->renderer()->isRelPositioned() && positionedParent->renderer()->isRenderInline()) { |
- LayoutSize offset = toRenderInline(positionedParent->renderer())->offsetForInFlowPositionedInline(*toRenderBox(renderer())); |
- localPoint += offset; |
- } |
- } else if (parent()) { |
- // FIXME: This code is very wrong, but luckily only needed in the old/current multicol |
- // implementation. The compositing system doesn't understand columns and we're hacking |
- // around that fact by faking the position of the RenderLayers when we think we'll end up |
- // being composited. |
- if (hasStyleDeterminedDirectCompositingReasons() && !useRegionBasedColumns()) { |
- // FIXME: Composited layers ignore pagination, so about the best we can do is make sure they're offset into the appropriate column. |
- // They won't split across columns properly. |
- if (!parent()->renderer()->hasColumns() && parent()->renderer()->isDocumentElement() && renderer()->view()->hasColumns()) |
- localPoint += renderer()->view()->columnOffset(localPoint); |
- else |
- localPoint += parent()->renderer()->columnOffset(localPoint); |
- } |
- |
- if (parent()->renderer()->hasOverflowClip()) { |
- IntSize scrollOffset = parent()->renderBox()->scrolledContentOffset(); |
- localPoint -= scrollOffset; |
- } |
- } |
- |
- bool positionOrOffsetChanged = false; |
- if (renderer()->isRelPositioned()) { |
- LayoutSize newOffset = toRenderBoxModelObject(renderer())->offsetForInFlowPosition(); |
- positionOrOffsetChanged = newOffset != m_offsetForInFlowPosition; |
- m_offsetForInFlowPosition = newOffset; |
- localPoint.move(m_offsetForInFlowPosition); |
- } else { |
- m_offsetForInFlowPosition = LayoutSize(); |
- } |
- |
- // FIXME: We'd really like to just get rid of the concept of a layer rectangle and rely on the renderers. |
- localPoint.moveBy(-inlineBoundingBoxOffset); |
- |
- if (m_location != localPoint) |
- positionOrOffsetChanged = true; |
- m_location = localPoint; |
- |
-#if ENABLE(ASSERT) |
- m_needsPositionUpdate = false; |
-#endif |
- return positionOrOffsetChanged; |
-} |
- |
-TransformationMatrix RenderLayer::perspectiveTransform() const |
-{ |
- if (!renderer()->hasTransformRelatedProperty()) |
- 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()->hasTransformRelatedProperty()) |
- 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())); |
-} |
- |
-static inline bool isFixedPositionedContainer(RenderLayer* layer) |
-{ |
- return layer->isRootLayer() || layer->hasTransformRelatedProperty(); |
-} |
- |
-RenderLayer* RenderLayer::enclosingPositionedAncestor() const |
-{ |
- RenderLayer* curr = parent(); |
- while (curr && !curr->isPositionedContainer()) |
- curr = curr->parent(); |
- |
- return curr; |
-} |
- |
-RenderLayer* RenderLayer::enclosingTransformedAncestor() const |
-{ |
- RenderLayer* curr = parent(); |
- while (curr && !curr->isRootLayer() && !curr->renderer()->hasTransformRelatedProperty()) |
- curr = curr->parent(); |
- |
- return curr; |
-} |
- |
-LayoutPoint RenderLayer::computeOffsetFromTransformedAncestor() const |
-{ |
- const AncestorDependentCompositingInputs& properties = ancestorDependentCompositingInputs(); |
- |
- TransformState transformState(TransformState::ApplyTransformDirection, FloatPoint()); |
- // FIXME: add a test that checks flipped writing mode and ApplyContainerFlip are correct. |
- renderer()->mapLocalToContainer(properties.transformAncestor ? properties.transformAncestor->renderer() : 0, transformState, ApplyContainerFlip); |
- transformState.flatten(); |
- return LayoutPoint(transformState.lastPlanarPoint()); |
-} |
- |
-const RenderLayer* RenderLayer::compositingContainer() const |
-{ |
- if (stackingNode()->isNormalFlowOnly()) |
- return parent(); |
- if (RenderLayerStackingNode* ancestorStackingNode = stackingNode()->ancestorStackingContextNode()) |
- return ancestorStackingNode->layer(); |
- return 0; |
-} |
- |
-bool RenderLayer::isPaintInvalidationContainer() const |
-{ |
- return compositingState() == PaintsIntoOwnBacking || compositingState() == PaintsIntoGroupedBacking; |
-} |
- |
-// Note: enclosingCompositingLayer does not include squashed layers. Compositing stacking children of squashed layers |
-// receive graphics layers that are parented to the compositing ancestor of the squashed layer. |
-RenderLayer* RenderLayer::enclosingLayerWithCompositedLayerMapping(IncludeSelfOrNot includeSelf) const |
-{ |
- ASSERT(isAllowedToQueryCompositingState()); |
- |
- if ((includeSelf == IncludeSelf) && compositingState() != NotComposited && compositingState() != PaintsIntoGroupedBacking) |
- return const_cast<RenderLayer*>(this); |
- |
- for (const RenderLayer* curr = compositingContainer(); curr; curr = curr->compositingContainer()) { |
- if (curr->compositingState() != NotComposited && curr->compositingState() != PaintsIntoGroupedBacking) |
- return const_cast<RenderLayer*>(curr); |
- } |
- |
- return 0; |
-} |
- |
-// Return the enclosingCompositedLayerForPaintInvalidation for the given RenderLayer |
-// including crossing frame boundaries. |
-RenderLayer* RenderLayer::enclosingLayerForPaintInvalidationCrossingFrameBoundaries() const |
-{ |
- const RenderLayer* layer = this; |
- RenderLayer* compositedLayer = 0; |
- while (!compositedLayer) { |
- compositedLayer = layer->enclosingLayerForPaintInvalidation(); |
- if (!compositedLayer) { |
- RenderObject* owner = layer->renderer()->frame()->ownerRenderer(); |
- if (!owner) |
- break; |
- layer = owner->enclosingLayer(); |
- } |
- } |
- return compositedLayer; |
-} |
- |
-RenderLayer* RenderLayer::enclosingLayerForPaintInvalidation() const |
-{ |
- ASSERT(isAllowedToQueryCompositingState()); |
- |
- if (isPaintInvalidationContainer()) |
- return const_cast<RenderLayer*>(this); |
- |
- for (const RenderLayer* curr = compositingContainer(); curr; curr = curr->compositingContainer()) { |
- if (curr->isPaintInvalidationContainer()) |
- return const_cast<RenderLayer*>(curr); |
- } |
- |
- return 0; |
-} |
- |
-void RenderLayer::setNeedsCompositingInputsUpdate() |
-{ |
- m_needsAncestorDependentCompositingInputsUpdate = true; |
- m_needsDescendantDependentCompositingInputsUpdate = true; |
- |
- for (RenderLayer* current = this; current && !current->m_childNeedsCompositingInputsUpdate; current = current->parent()) |
- current->m_childNeedsCompositingInputsUpdate = true; |
- |
- compositor()->setNeedsCompositingUpdate(CompositingUpdateAfterCompositingInputChange); |
-} |
- |
-void RenderLayer::updateAncestorDependentCompositingInputs(const AncestorDependentCompositingInputs& compositingInputs) |
-{ |
- m_ancestorDependentCompositingInputs = compositingInputs; |
- m_needsAncestorDependentCompositingInputsUpdate = false; |
-} |
- |
-void RenderLayer::updateDescendantDependentCompositingInputs(const DescendantDependentCompositingInputs& compositingInputs) |
-{ |
- m_descendantDependentCompositingInputs = compositingInputs; |
- m_needsDescendantDependentCompositingInputsUpdate = false; |
-} |
- |
-void RenderLayer::didUpdateCompositingInputs() |
-{ |
- ASSERT(!needsCompositingInputsUpdate()); |
- m_childNeedsCompositingInputsUpdate = false; |
- if (m_scrollableArea) |
- m_scrollableArea->updateNeedsCompositedScrolling(); |
-} |
- |
-bool RenderLayer::hasNonIsolatedDescendantWithBlendMode() const |
-{ |
- if (descendantDependentCompositingInputs().hasNonIsolatedDescendantWithBlendMode) |
- return true; |
- if (renderer()->isSVGRoot()) |
- return toRenderSVGRoot(renderer())->hasNonIsolatedBlendingDescendants(); |
- return false; |
-} |
- |
-void RenderLayer::setCompositingReasons(CompositingReasons reasons, CompositingReasons mask) |
-{ |
- if ((compositingReasons() & mask) == (reasons & mask)) |
- return; |
- m_compositingReasons = (reasons & mask) | (compositingReasons() & ~mask); |
-} |
- |
-void RenderLayer::setHasCompositingDescendant(bool hasCompositingDescendant) |
-{ |
- if (m_hasCompositingDescendant == static_cast<unsigned>(hasCompositingDescendant)) |
- return; |
- |
- m_hasCompositingDescendant = hasCompositingDescendant; |
- |
- if (hasCompositedLayerMapping()) |
- compositedLayerMapping()->setNeedsGraphicsLayerUpdate(GraphicsLayerUpdateLocal); |
-} |
- |
-void RenderLayer::setShouldIsolateCompositedDescendants(bool shouldIsolateCompositedDescendants) |
-{ |
- if (m_shouldIsolateCompositedDescendants == static_cast<unsigned>(shouldIsolateCompositedDescendants)) |
- return; |
- |
- m_shouldIsolateCompositedDescendants = shouldIsolateCompositedDescendants; |
- |
- if (hasCompositedLayerMapping()) |
- compositedLayerMapping()->setNeedsGraphicsLayerUpdate(GraphicsLayerUpdateLocal); |
-} |
- |
-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; |
-} |
- |
-static void expandClipRectForDescendantsAndReflection(LayoutRect& clipRect, const RenderLayer* layer, const RenderLayer* rootLayer, |
- RenderLayer::TransparencyClipBoxBehavior transparencyBehavior, const LayoutSize& subPixelAccumulation, PaintBehavior paintBehavior) |
-{ |
- // If we have a mask, then the clip is limited to the border box area (and there is |
- // no need to examine child layers). |
- if (!layer->renderer()->hasMask()) { |
- // 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()) { |
- if (!layer->reflectionInfo() || layer->reflectionInfo()->reflectionLayer() != curr) |
- clipRect.unite(RenderLayer::transparencyClipBox(curr, rootLayer, transparencyBehavior, RenderLayer::DescendantsOfTransparencyClipBox, subPixelAccumulation, paintBehavior)); |
- } |
- } |
- |
- // If we have a reflection, then we need to account for that when we push the clip. Reflect our entire |
- // current transparencyClipBox to catch all child layers. |
- // FIXME: Accelerated compositing will eventually want to do something smart here to avoid incorporating this |
- // size into the parent layer. |
- if (layer->renderer()->hasReflection()) { |
- LayoutPoint delta; |
- layer->convertToLayerCoords(rootLayer, delta); |
- clipRect.move(-delta.x(), -delta.y()); |
- clipRect.unite(layer->renderBox()->reflectedRect(clipRect)); |
- clipRect.moveBy(delta); |
- } |
-} |
- |
-LayoutRect RenderLayer::transparencyClipBox(const RenderLayer* layer, const RenderLayer* rootLayer, TransparencyClipBoxBehavior transparencyBehavior, |
- TransparencyClipBoxMode transparencyMode, const LayoutSize& subPixelAccumulation, PaintBehavior paintBehavior) |
-{ |
- // 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(paintBehavior)) |
- || (transparencyBehavior == HitTestingTransparencyClipBox && layer->hasTransformRelatedProperty()))) { |
- // 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* paginationLayer = transparencyMode == DescendantsOfTransparencyClipBox ? layer->enclosingPaginationLayer() : 0; |
- const RenderLayer* rootLayerForTransform = paginationLayer ? paginationLayer : 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, paintBehavior); |
- clipRect.expand(layer->renderer()->style()->filterOutsets()); |
- LayoutRect result = transform.mapRect(clipRect); |
- if (!paginationLayer) |
- return result; |
- |
- // We have to break up the transformed extent across our columns. |
- // Split our box up into the actual fragment boxes that render in the columns/pages and unite those together to |
- // get our true bounding box. |
- RenderFlowThread* enclosingFlowThread = toRenderFlowThread(paginationLayer->renderer()); |
- result = enclosingFlowThread->fragmentsBoundingBox(result); |
- |
- LayoutPoint rootLayerDelta; |
- paginationLayer->convertToLayerCoords(rootLayer, rootLayerDelta); |
- result.moveBy(rootLayerDelta); |
- return result; |
- } |
- |
- LayoutRect clipRect = layer->fragmentsBoundingBox(rootLayer); |
- expandClipRectForDescendantsAndReflection(clipRect, layer, rootLayer, transparencyBehavior, subPixelAccumulation, paintBehavior); |
- clipRect.expand(layer->renderer()->style()->filterOutsets()); |
- clipRect.move(subPixelAccumulation); |
- return clipRect; |
-} |
- |
-LayoutRect RenderLayer::paintingExtent(const RenderLayer* rootLayer, const LayoutRect& paintDirtyRect, const LayoutSize& subPixelAccumulation, PaintBehavior paintBehavior) |
-{ |
- return intersection(transparencyClipBox(this, rootLayer, PaintingTransparencyClipBox, RootOfTransparencyClipBox, subPixelAccumulation, paintBehavior), paintDirtyRect); |
-} |
- |
-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; |
- |
- setNeedsCompositingInputsUpdate(); |
- |
- 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(); |
- } |
- |
- dirtyAncestorChainVisibleDescendantStatus(); |
- dirtyAncestorChainHasSelfPaintingLayerDescendantStatus(); |
- |
- child->updateDescendantDependentFlags(); |
-} |
- |
-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(); |
- } |
- |
- if (renderer()->style()->visibility() != VISIBLE) |
- dirtyVisibleContentStatus(); |
- |
- oldChild->setPreviousSibling(0); |
- oldChild->setNextSibling(0); |
- oldChild->m_parent = 0; |
- |
- dirtyAncestorChainHasSelfPaintingLayerDescendantStatus(); |
- |
- oldChild->updateDescendantDependentFlags(); |
- |
- if (oldChild->m_hasVisibleContent || oldChild->m_hasVisibleDescendant) |
- dirtyAncestorChainVisibleDescendantStatus(); |
- |
- if (oldChild->enclosingPaginationLayer()) |
- oldChild->clearPaginationRecursive(); |
- |
- return oldChild; |
-} |
- |
-void RenderLayer::removeOnlyThisLayer() |
-{ |
- if (!m_parent) |
- return; |
- |
- { |
- DisableCompositingQueryAsserts disabler; // We need the current compositing status. |
- if (isPaintInvalidationContainer()) { |
- // Our children will be reparented and contained by a new paint invalidation container, |
- // so need paint invalidation. CompositingUpdate can't see this layer (which has been |
- // removed) so won't do this for us. |
- setShouldDoFullPaintInvalidationIncludingNonCompositingDescendants(); |
- } |
- } |
- |
- m_clipper.clearClipRectsIncludingDescendants(); |
- |
- RenderLayer* nextSib = nextSibling(); |
- |
- // Remove the child reflection layer before moving other child layers. |
- // The reflection layer should not be moved to the parent. |
- if (m_reflectionInfo) |
- removeChild(m_reflectionInfo->reflectionLayer()); |
- |
- // 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 = !parentLayer->reflectionInfo() || parentLayer->reflectionInfo()->reflectionLayer() != this ? renderer()->parent()->findNextLayer(parentLayer, renderer()) : 0; |
- 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(); |
- |
- // FIXME: Positioning of out-of-flow(fixed, absolute) elements collected in a RenderFlowThread |
- // may need to be revisited in a future patch. |
- // If the fixed renderer is inside a RenderFlowThread, we should not compute location using localToAbsolute, |
- // since localToAbsolute maps the coordinates from flow thread to regions coordinates and regions can be |
- // positioned in a completely different place in the viewport (RenderView). |
- if (position == FixedPosition && (!ancestorLayer || ancestorLayer == renderer->view()->layer())) { |
- // If the fixed layer's container is the root, just add in the offset of the view. We can obtain this by calling |
- // localToAbsolute() on the RenderView. |
- FloatPoint absPos = renderer->localToAbsolute(FloatPoint(), IsFixed); |
- location += LayoutSize(absPos.x(), absPos.y()); |
- return ancestorLayer; |
- } |
- |
- // For the fixed positioned elements inside a render flow thread, we should also skip the code path below |
- // Otherwise, for the case of ancestorLayer == rootLayer and fixed positioned element child of a transformed |
- // element in render flow thread, we will hit the fixed positioned container before hitting the ancestor layer. |
- if (position == FixedPosition) { |
- // For a fixed layers, we need to walk up to the root to see if there's a fixed position container |
- // (e.g. a transformed layer). It's an error to call convertToLayerCoords() across a layer with a transform, |
- // so we should always find the ancestor at or before we find the fixed position container. |
- RenderLayer* fixedPositionContainerLayer = 0; |
- bool foundAncestor = false; |
- for (RenderLayer* currLayer = layer->parent(); currLayer; currLayer = currLayer->parent()) { |
- if (currLayer == ancestorLayer) |
- foundAncestor = true; |
- |
- if (isFixedPositionedContainer(currLayer)) { |
- fixedPositionContainerLayer = currLayer; |
- ASSERT_UNUSED(foundAncestor, foundAncestor); |
- break; |
- } |
- } |
- |
- ASSERT(fixedPositionContainerLayer); // We should have hit the RenderView's layer at least. |
- |
- if (fixedPositionContainerLayer != ancestorLayer) { |
- LayoutPoint fixedContainerCoords; |
- layer->convertToLayerCoords(fixedPositionContainerLayer, fixedContainerCoords); |
- |
- LayoutPoint ancestorCoords; |
- ancestorLayer->convertToLayerCoords(fixedPositionContainerLayer, ancestorCoords); |
- |
- location += (fixedContainerCoords - ancestorCoords); |
- } else { |
- // RenderView has been handled in the first top-level 'if' block above. |
- ASSERT(ancestorLayer != renderer->view()->layer()); |
- ASSERT(ancestorLayer->hasTransformRelatedProperty()); |
- |
- location += layer->location(); |
- |
- // The spec (http://dev.w3.org/csswg/css-transforms/#transform-rendering) doesn't say if a |
- // fixed-position element under a scrollable transformed element should scroll. However, |
- // other parts of blink scroll the fixed-position element, and the following keeps the consistency. |
- if (RenderLayerScrollableArea* scrollableArea = ancestorLayer->scrollableArea()) |
- location -= LayoutSize(scrollableArea->scrollOffset()); |
- } |
- return ancestorLayer; |
- } |
- |
- 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(); |
- } |
- |
- // We should not reach RenderView layer past the RenderFlowThread layer for any |
- // children of the RenderFlowThread. |
- ASSERT(!renderer->flowThreadContainingBlock() || parentLayer != renderer->view()->layer()); |
- |
- 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 if (renderer->isColumnSpanAll()) { |
- RenderBlock* multicolContainer = renderer->containingBlock(); |
- ASSERT(toRenderBlockFlow(multicolContainer)->multiColumnFlowThread()); |
- parentLayer = multicolContainer->layer(); |
- ASSERT(parentLayer); |
- } else { |
- parentLayer = layer->parent(); |
- } |
- |
- if (!parentLayer) |
- return 0; |
- |
- location += 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.moveBy(delta); |
-} |
- |
-LayoutPoint RenderLayer::visualOffsetFromAncestor(const RenderLayer* ancestorLayer) const |
-{ |
- LayoutPoint offset; |
- if (ancestorLayer == this) |
- return offset; |
- RenderLayer* paginationLayer = enclosingPaginationLayer(); |
- if (paginationLayer == this) |
- paginationLayer = parent()->enclosingPaginationLayer(); |
- if (!paginationLayer) { |
- convertToLayerCoords(ancestorLayer, offset); |
- return offset; |
- } |
- |
- RenderFlowThread* flowThread = toRenderFlowThread(paginationLayer->renderer()); |
- convertToLayerCoords(paginationLayer, offset); |
- offset = flowThread->flowThreadPointToVisualPoint(offset); |
- if (ancestorLayer == paginationLayer) |
- return offset; |
- |
- if (ancestorLayer->enclosingPaginationLayer() != paginationLayer) { |
- offset.moveBy(paginationLayer->visualOffsetFromAncestor(ancestorLayer)); |
- } else { |
- // The ancestor layer is also inside the pagination layer, so we need to subtract the visual |
- // distance from the ancestor layer to the pagination layer. |
- offset.moveBy(-ancestorLayer->visualOffsetFromAncestor(paginationLayer)); |
- } |
- return offset; |
-} |
- |
-void RenderLayer::didUpdateNeedsCompositedScrolling() |
-{ |
- updateSelfPaintingLayer(); |
-} |
- |
-void RenderLayer::updateReflectionInfo(const RenderStyle* oldStyle) |
-{ |
- ASSERT(!oldStyle || !renderer()->style()->reflectionDataEquivalent(oldStyle)); |
- if (renderer()->hasReflection()) { |
- if (!m_reflectionInfo) |
- m_reflectionInfo = adoptPtr(new RenderLayerReflectionInfo(*renderBox())); |
- m_reflectionInfo->updateAfterStyleChange(oldStyle); |
- } else if (m_reflectionInfo) { |
- m_reflectionInfo->destroy(); |
- m_reflectionInfo = nullptr; |
- } |
-} |
- |
-void RenderLayer::updateStackingNode() |
-{ |
- if (requiresStackingNode()) |
- m_stackingNode = adoptPtr(new RenderLayerStackingNode(this)); |
- else |
- m_stackingNode = nullptr; |
-} |
- |
-void RenderLayer::updateScrollableArea() |
-{ |
- if (requiresScrollableArea()) |
- m_scrollableArea = adoptPtr(new RenderLayerScrollableArea(*this)); |
- else |
- m_scrollableArea = nullptr; |
-} |
- |
-bool RenderLayer::hasOverflowControls() const |
-{ |
- return m_scrollableArea && (m_scrollableArea->hasScrollbar() || m_scrollableArea->scrollCorner() || renderer()->style()->resize() != RESIZE_NONE); |
-} |
- |
-void RenderLayer::collectFragments(LayerFragments& fragments, const RenderLayer* rootLayer, const LayoutRect& dirtyRect, |
- ClipRectsCacheSlot clipRectsCacheSlot, OverlayScrollbarSizeRelevancy inOverlayScrollbarSizeRelevancy, ShouldRespectOverflowClip respectOverflowClip, const LayoutPoint* offsetFromRoot, |
- const LayoutSize& subPixelAccumulation, const LayoutRect* layerBoundingBox) |
-{ |
- if (!enclosingPaginationLayer() || hasTransformRelatedProperty()) { |
- // For unpaginated layers, there is only one fragment. |
- LayerFragment fragment; |
- ClipRectsContext clipRectsContext(rootLayer, clipRectsCacheSlot, inOverlayScrollbarSizeRelevancy, subPixelAccumulation); |
- if (respectOverflowClip == IgnoreOverflowClip) |
- clipRectsContext.setIgnoreOverflowClip(); |
- clipper().calculateRects(clipRectsContext, dirtyRect, fragment.layerBounds, fragment.backgroundRect, fragment.foregroundRect, fragment.outlineRect, offsetFromRoot); |
- fragments.append(fragment); |
- return; |
- } |
- |
- // Compute our offset within the enclosing pagination layer. |
- LayoutPoint offsetWithinPaginatedLayer; |
- convertToLayerCoords(enclosingPaginationLayer(), offsetWithinPaginatedLayer); |
- |
- // Calculate clip rects relative to the enclosingPaginationLayer. The purpose of this call is to determine our bounds clipped to intermediate |
- // layers between us and the pagination context. It's important to minimize the number of fragments we need to create and this helps with that. |
- ClipRectsContext paginationClipRectsContext(enclosingPaginationLayer(), clipRectsCacheSlot, inOverlayScrollbarSizeRelevancy); |
- if (respectOverflowClip == IgnoreOverflowClip) |
- paginationClipRectsContext.setIgnoreOverflowClip(); |
- LayoutRect layerBoundsInFlowThread; |
- ClipRect backgroundRectInFlowThread; |
- ClipRect foregroundRectInFlowThread; |
- ClipRect outlineRectInFlowThread; |
- clipper().calculateRects(paginationClipRectsContext, LayoutRect::infiniteIntRect(), layerBoundsInFlowThread, backgroundRectInFlowThread, foregroundRectInFlowThread, |
- outlineRectInFlowThread, &offsetWithinPaginatedLayer); |
- |
- // Take our bounding box within the flow thread and clip it. |
- LayoutRect layerBoundingBoxInFlowThread = layerBoundingBox ? *layerBoundingBox : physicalBoundingBox(enclosingPaginationLayer(), &offsetWithinPaginatedLayer); |
- layerBoundingBoxInFlowThread.intersect(backgroundRectInFlowThread.rect()); |
- |
- // Make the dirty rect relative to the fragmentation context (multicol container, etc.). |
- RenderFlowThread* enclosingFlowThread = toRenderFlowThread(enclosingPaginationLayer()->renderer()); |
- LayoutPoint offsetOfPaginationLayerFromRoot; // Visual offset from the root layer to the nearest fragmentation context. |
- bool rootLayerIsInsidePaginationLayer = rootLayer->enclosingPaginationLayer() == enclosingPaginationLayer(); |
- if (rootLayerIsInsidePaginationLayer) { |
- // The root layer is in the same fragmentation context as this layer, so we need to look |
- // inside it and subtract the offset between the fragmentation context and the root layer. |
- offsetOfPaginationLayerFromRoot = -rootLayer->visualOffsetFromAncestor(enclosingPaginationLayer()); |
- } else { |
- offsetOfPaginationLayerFromRoot = enclosingPaginationLayer()->visualOffsetFromAncestor(rootLayer); |
- } |
- LayoutRect dirtyRectInFlowThread(dirtyRect); |
- dirtyRectInFlowThread.moveBy(-offsetOfPaginationLayerFromRoot); |
- |
- // Tell the flow thread to collect the fragments. We pass enough information to create a minimal number of fragments based off the pages/columns |
- // that intersect the actual dirtyRect as well as the pages/columns that intersect our layer's bounding box. |
- enclosingFlowThread->collectLayerFragments(fragments, layerBoundingBoxInFlowThread, dirtyRectInFlowThread); |
- |
- if (fragments.isEmpty()) |
- return; |
- |
- // Get the parent clip rects of the pagination layer, since we need to intersect with that when painting column contents. |
- ClipRect ancestorClipRect = dirtyRect; |
- if (const RenderLayer* paginationParentLayer = enclosingPaginationLayer()->parent()) { |
- const RenderLayer* ancestorLayer = rootLayerIsInsidePaginationLayer ? paginationParentLayer : rootLayer; |
- ClipRectsContext clipRectsContext(ancestorLayer, clipRectsCacheSlot, inOverlayScrollbarSizeRelevancy); |
- if (respectOverflowClip == IgnoreOverflowClip) |
- clipRectsContext.setIgnoreOverflowClip(); |
- ancestorClipRect = enclosingPaginationLayer()->clipper().backgroundClipRect(clipRectsContext); |
- if (rootLayerIsInsidePaginationLayer) |
- ancestorClipRect.moveBy(-rootLayer->visualOffsetFromAncestor(ancestorLayer)); |
- ancestorClipRect.intersect(dirtyRect); |
- } |
- |
- for (size_t i = 0; i < fragments.size(); ++i) { |
- LayerFragment& fragment = fragments.at(i); |
- |
- // Set our four rects with all clipping applied that was internal to the flow thread. |
- fragment.setRects(layerBoundsInFlowThread, backgroundRectInFlowThread, foregroundRectInFlowThread, outlineRectInFlowThread); |
- |
- // Shift to the root-relative physical position used when painting the flow thread in this fragment. |
- fragment.moveBy(fragment.paginationOffset + offsetOfPaginationLayerFromRoot); |
- |
- // Intersect the fragment with our ancestor's background clip so that e.g., columns in an overflow:hidden block are |
- // properly clipped by the overflow. |
- fragment.intersect(ancestorClipRect.rect()); |
- |
- // Now intersect with our pagination clip. This will typically mean we're just intersecting the dirty rect with the column |
- // clip, so the column clip ends up being all we apply. |
- fragment.intersect(fragment.paginationClip); |
- } |
-} |
- |
-static inline LayoutRect frameVisibleRect(RenderObject* renderer) |
-{ |
- FrameView* frameView = renderer->document().view(); |
- if (!frameView) |
- return LayoutRect(); |
- |
- return frameView->visibleContentRect(); |
-} |
- |
-bool RenderLayer::hitTest(const HitTestRequest& request, HitTestResult& result) |
-{ |
- return hitTest(request, result.hitTestLocation(), result); |
-} |
- |
-bool RenderLayer::hitTest(const HitTestRequest& request, const HitTestLocation& hitTestLocation, HitTestResult& result) |
-{ |
- ASSERT(isSelfPaintingLayer() || hasSelfPaintingLayerDescendant()); |
- |
- // RenderView should make sure to update layout before entering hit testing |
- ASSERT(!renderer()->frame()->view()->layoutPending()); |
- ASSERT(!renderer()->document().renderView()->needsLayout()); |
- |
- // Start with frameVisibleRect to ensure we include the scrollbars. |
- LayoutRect hitTestArea = frameVisibleRect(renderer()); |
- if (request.ignoreClipping()) |
- hitTestArea.unite(renderer()->view()->documentRect()); |
- |
- RenderLayer* insideLayer = hitTestLayer(this, 0, request, result, hitTestArea, hitTestLocation, false); |
- if (!insideLayer) { |
- // We didn't hit any layer. If we are the root layer and the mouse is -- or just was -- down, |
- // return ourselves. We do this so mouse events continue getting delivered after a drag has |
- // exited the WebView, and so hit testing over a scrollbar hits the content document. |
- // In addtion, it is possible for the mouse to stay in the document but there is no element. |
- // At that time, the events of the mouse should be fired. |
- LayoutPoint hitPoint = hitTestLocation.point(); |
- if (!request.isChildFrameHitTest() && ((request.active() || request.release()) || (request.move() && hitTestArea.contains(hitPoint.x(), hitPoint.y()))) && isRootLayer()) { |
- renderer()->updateHitTestResult(result, toRenderView(renderer())->flipForWritingMode(hitTestLocation.point())); |
- insideLayer = this; |
- } |
- } |
- |
- // Now determine if the result is inside an anchor - if the urlElement isn't already set. |
- Node* node = result.innerNode(); |
- if (node && !result.URLElement()) |
- result.setURLElement(node->enclosingLinkEventParentOrSelf()); |
- |
- // Now return whether we were inside this layer (this will always be true for the root |
- // layer). |
- return insideLayer; |
-} |
- |
-Node* RenderLayer::enclosingElement() const |
-{ |
- for (RenderObject* r = renderer(); r; r = r->parent()) { |
- if (Node* e = r->node()) |
- return e; |
- } |
- ASSERT_NOT_REACHED(); |
- return 0; |
-} |
- |
-bool RenderLayer::isInTopLayer() const |
-{ |
- Node* node = renderer()->node(); |
- return node && node->isElementNode() && toElement(node)->isInTopLayer(); |
-} |
- |
-// Compute the z-offset of the point in the transformState. |
-// This is effectively projecting a ray normal to the plane of ancestor, finding where that |
-// ray intersects target, and computing the z delta between those two points. |
-static double computeZOffset(const HitTestingTransformState& transformState) |
-{ |
- // We got an affine transform, so no z-offset |
- if (transformState.m_accumulatedTransform.isAffine()) |
- return 0; |
- |
- // Flatten the point into the target plane |
- FloatPoint targetPoint = transformState.mappedPoint(); |
- |
- // Now map the point back through the transform, which computes Z. |
- FloatPoint3D backmappedPoint = transformState.m_accumulatedTransform.mapPoint(FloatPoint3D(targetPoint)); |
- return backmappedPoint.z(); |
-} |
- |
-PassRefPtr<HitTestingTransformState> RenderLayer::createLocalTransformState(RenderLayer* rootLayer, RenderLayer* containerLayer, |
- const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation, |
- const HitTestingTransformState* containerTransformState, |
- const LayoutPoint& translationOffset) const |
-{ |
- RefPtr<HitTestingTransformState> transformState; |
- LayoutPoint offset; |
- if (containerTransformState) { |
- // If we're already computing transform state, then it's relative to the container (which we know is non-null). |
- transformState = HitTestingTransformState::create(*containerTransformState); |
- convertToLayerCoords(containerLayer, offset); |
- } else { |
- // If this is the first time we need to make transform state, then base it off of hitTestLocation, |
- // which is relative to rootLayer. |
- transformState = HitTestingTransformState::create(hitTestLocation.transformedPoint(), hitTestLocation.transformedRect(), FloatQuad(hitTestRect)); |
- convertToLayerCoords(rootLayer, offset); |
- } |
- offset.moveBy(translationOffset); |
- |
- RenderObject* containerRenderer = containerLayer ? containerLayer->renderer() : 0; |
- if (renderer()->shouldUseTransformFromContainer(containerRenderer)) { |
- TransformationMatrix containerTransform; |
- renderer()->getTransformFromContainer(containerRenderer, toLayoutSize(offset), containerTransform); |
- transformState->applyTransform(containerTransform, HitTestingTransformState::AccumulateTransform); |
- } else { |
- transformState->translate(offset.x(), offset.y(), HitTestingTransformState::AccumulateTransform); |
- } |
- |
- return transformState; |
-} |
- |
- |
-static bool isHitCandidate(const RenderLayer* hitLayer, bool canDepthSort, double* zOffset, const HitTestingTransformState* transformState) |
-{ |
- if (!hitLayer) |
- return false; |
- |
- // The hit layer is depth-sorting with other layers, so just say that it was hit. |
- if (canDepthSort) |
- return true; |
- |
- // We need to look at z-depth to decide if this layer was hit. |
- if (zOffset) { |
- ASSERT(transformState); |
- // This is actually computing our z, but that's OK because the hitLayer is coplanar with us. |
- double childZOffset = computeZOffset(*transformState); |
- if (childZOffset > *zOffset) { |
- *zOffset = childZOffset; |
- return true; |
- } |
- return false; |
- } |
- |
- return true; |
-} |
- |
-// hitTestLocation and hitTestRect are relative to rootLayer. |
-// A 'flattening' layer is one preserves3D() == false. |
-// transformState.m_accumulatedTransform holds the transform from the containing flattening layer. |
-// transformState.m_lastPlanarPoint is the hitTestLocation in the plane of the containing flattening layer. |
-// transformState.m_lastPlanarQuad is the hitTestRect as a quad in the plane of the containing flattening layer. |
-// |
-// If zOffset is non-null (which indicates that the caller wants z offset information), |
-// *zOffset on return is the z offset of the hit point relative to the containing flattening layer. |
-RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLayer* containerLayer, const HitTestRequest& request, HitTestResult& result, |
- const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation, bool appliedTransform, |
- const HitTestingTransformState* transformState, double* zOffset) |
-{ |
- if (!isSelfPaintingLayer() && !hasSelfPaintingLayerDescendant()) |
- return 0; |
- |
- // The natural thing would be to keep HitTestingTransformState on the stack, but it's big, so we heap-allocate. |
- |
- // Apply a transform if we have one. |
- if (transform() && !appliedTransform) { |
- if (enclosingPaginationLayer()) |
- return hitTestTransformedLayerInFragments(rootLayer, containerLayer, request, result, hitTestRect, hitTestLocation, transformState, zOffset); |
- |
- // Make sure the parent's clip rects have been calculated. |
- if (parent()) { |
- ClipRect clipRect = clipper().backgroundClipRect(ClipRectsContext(rootLayer, RootRelativeClipRects, IncludeOverlayScrollbarSize)); |
- // Go ahead and test the enclosing clip now. |
- if (!clipRect.intersects(hitTestLocation)) |
- return 0; |
- } |
- |
- return hitTestLayerByApplyingTransform(rootLayer, containerLayer, request, result, hitTestRect, hitTestLocation, transformState, zOffset); |
- } |
- |
- // Ensure our lists and 3d status are up-to-date. |
- m_stackingNode->updateLayerListsIfNeeded(); |
- update3DTransformedDescendantStatus(); |
- |
- RefPtr<HitTestingTransformState> localTransformState; |
- if (appliedTransform) { |
- // We computed the correct state in the caller (above code), so just reference it. |
- ASSERT(transformState); |
- localTransformState = const_cast<HitTestingTransformState*>(transformState); |
- } else if (transformState || m_has3DTransformedDescendant || preserves3D()) { |
- // We need transform state for the first time, or to offset the container state, so create it here. |
- localTransformState = createLocalTransformState(rootLayer, containerLayer, hitTestRect, hitTestLocation, transformState); |
- } |
- |
- // Check for hit test on backface if backface-visibility is 'hidden' |
- if (localTransformState && renderer()->style()->backfaceVisibility() == BackfaceVisibilityHidden) { |
- TransformationMatrix invertedMatrix = localTransformState->m_accumulatedTransform.inverse(); |
- // If the z-vector of the matrix is negative, the back is facing towards the viewer. |
- if (invertedMatrix.m33() < 0) |
- return 0; |
- } |
- |
- RefPtr<HitTestingTransformState> unflattenedTransformState = localTransformState; |
- if (localTransformState && !preserves3D()) { |
- // Keep a copy of the pre-flattening state, for computing z-offsets for the container |
- unflattenedTransformState = HitTestingTransformState::create(*localTransformState); |
- // This layer is flattening, so flatten the state passed to descendants. |
- localTransformState->flatten(); |
- } |
- |
- // The following are used for keeping track of the z-depth of the hit point of 3d-transformed |
- // descendants. |
- double localZOffset = -std::numeric_limits<double>::infinity(); |
- double* zOffsetForDescendantsPtr = 0; |
- double* zOffsetForContentsPtr = 0; |
- |
- bool depthSortDescendants = false; |
- if (preserves3D()) { |
- depthSortDescendants = true; |
- // Our layers can depth-test with our container, so share the z depth pointer with the container, if it passed one down. |
- zOffsetForDescendantsPtr = zOffset ? zOffset : &localZOffset; |
- zOffsetForContentsPtr = zOffset ? zOffset : &localZOffset; |
- } else if (zOffset) { |
- zOffsetForDescendantsPtr = 0; |
- // Container needs us to give back a z offset for the hit layer. |
- zOffsetForContentsPtr = zOffset; |
- } |
- |
- // This variable tracks which layer the mouse ends up being inside. |
- RenderLayer* candidateLayer = 0; |
- |
- // Begin by walking our list of positive layers from highest z-index down to the lowest z-index. |
- RenderLayer* hitLayer = hitTestChildren(PositiveZOrderChildren, rootLayer, request, result, hitTestRect, hitTestLocation, |
- localTransformState.get(), zOffsetForDescendantsPtr, zOffset, unflattenedTransformState.get(), depthSortDescendants); |
- if (hitLayer) { |
- if (!depthSortDescendants) |
- return hitLayer; |
- candidateLayer = hitLayer; |
- } |
- |
- // Now check our overflow objects. |
- hitLayer = hitTestChildren(NormalFlowChildren, rootLayer, request, result, hitTestRect, hitTestLocation, |
- localTransformState.get(), zOffsetForDescendantsPtr, zOffset, unflattenedTransformState.get(), depthSortDescendants); |
- if (hitLayer) { |
- if (!depthSortDescendants) |
- return hitLayer; |
- candidateLayer = hitLayer; |
- } |
- |
- // Collect the fragments. This will compute the clip rectangles for each layer fragment. |
- LayerFragments layerFragments; |
- collectFragments(layerFragments, rootLayer, hitTestRect, RootRelativeClipRects, IncludeOverlayScrollbarSize); |
- |
- if (m_scrollableArea && m_scrollableArea->hitTestResizerInFragments(layerFragments, hitTestLocation)) { |
- renderer()->updateHitTestResult(result, hitTestLocation.point()); |
- return this; |
- } |
- |
- // Next we want to see if the mouse pos is inside the child RenderObjects of the layer. Check |
- // every fragment in reverse order. |
- if (isSelfPaintingLayer()) { |
- // Hit test with a temporary HitTestResult, because we only want to commit to 'result' if we know we're frontmost. |
- HitTestResult tempResult(result.hitTestLocation()); |
- bool insideFragmentForegroundRect = false; |
- if (hitTestContentsForFragments(layerFragments, request, tempResult, hitTestLocation, HitTestDescendants, insideFragmentForegroundRect) |
- && isHitCandidate(this, false, zOffsetForContentsPtr, unflattenedTransformState.get())) { |
- if (result.isRectBasedTest()) |
- result.append(tempResult); |
- else |
- result = tempResult; |
- if (!depthSortDescendants) |
- return this; |
- // Foreground can depth-sort with descendant layers, so keep this as a candidate. |
- candidateLayer = this; |
- } else if (insideFragmentForegroundRect && result.isRectBasedTest()) |
- result.append(tempResult); |
- } |
- |
- // Now check our negative z-index children. |
- hitLayer = hitTestChildren(NegativeZOrderChildren, rootLayer, request, result, hitTestRect, hitTestLocation, |
- localTransformState.get(), zOffsetForDescendantsPtr, zOffset, unflattenedTransformState.get(), depthSortDescendants); |
- if (hitLayer) { |
- if (!depthSortDescendants) |
- return hitLayer; |
- candidateLayer = hitLayer; |
- } |
- |
- // If we found a layer, return. Child layers, and foreground always render in front of background. |
- if (candidateLayer) |
- return candidateLayer; |
- |
- if (isSelfPaintingLayer()) { |
- HitTestResult tempResult(result.hitTestLocation()); |
- bool insideFragmentBackgroundRect = false; |
- if (hitTestContentsForFragments(layerFragments, request, tempResult, hitTestLocation, HitTestSelf, insideFragmentBackgroundRect) |
- && isHitCandidate(this, false, zOffsetForContentsPtr, unflattenedTransformState.get())) { |
- if (result.isRectBasedTest()) |
- result.append(tempResult); |
- else |
- result = tempResult; |
- return this; |
- } |
- if (insideFragmentBackgroundRect && result.isRectBasedTest()) |
- result.append(tempResult); |
- } |
- |
- return 0; |
-} |
- |
-bool RenderLayer::hitTestContentsForFragments(const LayerFragments& layerFragments, const HitTestRequest& request, HitTestResult& result, |
- const HitTestLocation& hitTestLocation, HitTestFilter hitTestFilter, bool& insideClipRect) const |
-{ |
- if (layerFragments.isEmpty()) |
- return false; |
- |
- for (int i = layerFragments.size() - 1; i >= 0; --i) { |
- const LayerFragment& fragment = layerFragments.at(i); |
- if ((hitTestFilter == HitTestSelf && !fragment.backgroundRect.intersects(hitTestLocation)) |
- || (hitTestFilter == HitTestDescendants && !fragment.foregroundRect.intersects(hitTestLocation))) |
- continue; |
- insideClipRect = true; |
- if (hitTestContents(request, result, fragment.layerBounds, hitTestLocation, hitTestFilter)) |
- return true; |
- } |
- |
- return false; |
-} |
- |
-RenderLayer* RenderLayer::hitTestTransformedLayerInFragments(RenderLayer* rootLayer, RenderLayer* containerLayer, const HitTestRequest& request, HitTestResult& result, |
- const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation, const HitTestingTransformState* transformState, double* zOffset) |
-{ |
- LayerFragments enclosingPaginationFragments; |
- LayoutPoint offsetOfPaginationLayerFromRoot; |
- // FIXME: We're missing a sub-pixel offset here crbug.com/348728 |
- LayoutRect transformedExtent = transparencyClipBox(this, enclosingPaginationLayer(), HitTestingTransparencyClipBox, RenderLayer::RootOfTransparencyClipBox, LayoutSize()); |
- enclosingPaginationLayer()->collectFragments(enclosingPaginationFragments, rootLayer, hitTestRect, |
- RootRelativeClipRects, IncludeOverlayScrollbarSize, RespectOverflowClip, &offsetOfPaginationLayerFromRoot, LayoutSize(), &transformedExtent); |
- |
- for (int i = enclosingPaginationFragments.size() - 1; i >= 0; --i) { |
- const LayerFragment& fragment = enclosingPaginationFragments.at(i); |
- |
- // Apply the page/column clip for this fragment, as well as any clips established by layers in between us and |
- // the enclosing pagination layer. |
- LayoutRect clipRect = fragment.backgroundRect.rect(); |
- |
- // Now compute the clips within a given fragment |
- if (parent() != enclosingPaginationLayer()) { |
- enclosingPaginationLayer()->convertToLayerCoords(rootLayer, offsetOfPaginationLayerFromRoot); |
- LayoutRect parentClipRect = clipper().backgroundClipRect(ClipRectsContext(enclosingPaginationLayer(), RootRelativeClipRects, IncludeOverlayScrollbarSize)).rect(); |
- parentClipRect.moveBy(fragment.paginationOffset + offsetOfPaginationLayerFromRoot); |
- clipRect.intersect(parentClipRect); |
- } |
- |
- if (!hitTestLocation.intersects(clipRect)) |
- continue; |
- |
- RenderLayer* hitLayer = hitTestLayerByApplyingTransform(rootLayer, containerLayer, request, result, hitTestRect, hitTestLocation, |
- transformState, zOffset, fragment.paginationOffset); |
- if (hitLayer) |
- return hitLayer; |
- } |
- |
- return 0; |
-} |
- |
-RenderLayer* RenderLayer::hitTestLayerByApplyingTransform(RenderLayer* rootLayer, RenderLayer* containerLayer, const HitTestRequest& request, HitTestResult& result, |
- const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation, const HitTestingTransformState* transformState, double* zOffset, |
- const LayoutPoint& translationOffset) |
-{ |
- // Create a transform state to accumulate this transform. |
- RefPtr<HitTestingTransformState> newTransformState = createLocalTransformState(rootLayer, containerLayer, hitTestRect, hitTestLocation, transformState, translationOffset); |
- |
- // If the transform can't be inverted, then don't hit test this layer at all. |
- if (!newTransformState->m_accumulatedTransform.isInvertible()) |
- return 0; |
- |
- // Compute the point and the hit test rect in the coords of this layer by using the values |
- // from the transformState, which store the point and quad in the coords of the last flattened |
- // layer, and the accumulated transform which lets up map through preserve-3d layers. |
- // |
- // We can't just map hitTestLocation and hitTestRect because they may have been flattened (losing z) |
- // by our container. |
- FloatPoint localPoint = newTransformState->mappedPoint(); |
- FloatQuad localPointQuad = newTransformState->mappedQuad(); |
- LayoutRect localHitTestRect = newTransformState->boundsOfMappedArea(); |
- HitTestLocation newHitTestLocation; |
- if (hitTestLocation.isRectBasedTest()) |
- newHitTestLocation = HitTestLocation(localPoint, localPointQuad); |
- else |
- newHitTestLocation = HitTestLocation(localPoint); |
- |
- // Now do a hit test with the root layer shifted to be us. |
- return hitTestLayer(this, containerLayer, request, result, localHitTestRect, newHitTestLocation, true, newTransformState.get(), zOffset); |
-} |
- |
-bool RenderLayer::hitTestContents(const HitTestRequest& request, HitTestResult& result, const LayoutRect& layerBounds, const HitTestLocation& hitTestLocation, HitTestFilter hitTestFilter) const |
-{ |
- ASSERT(isSelfPaintingLayer() || hasSelfPaintingLayerDescendant()); |
- |
- if (!renderer()->hitTest(request, result, hitTestLocation, toLayoutPoint(layerBounds.location() - renderBoxLocation()), hitTestFilter)) { |
- // It's wrong to set innerNode, but then claim that you didn't hit anything, unless it is |
- // a rect-based test. |
- ASSERT(!result.innerNode() || (result.isRectBasedTest() && result.rectBasedTestResult().size())); |
- return false; |
- } |
- |
- // For positioned generated content, we might still not have a |
- // node by the time we get to the layer level, since none of |
- // the content in the layer has an element. So just walk up |
- // the tree. |
- if (!result.innerNode() || !result.innerNonSharedNode()) { |
- Node* e = enclosingElement(); |
- if (!result.innerNode()) |
- result.setInnerNode(e); |
- if (!result.innerNonSharedNode()) |
- result.setInnerNonSharedNode(e); |
- } |
- |
- return true; |
-} |
- |
-RenderLayer* RenderLayer::hitTestChildren(ChildrenIteration childrentoVisit, RenderLayer* rootLayer, |
- const HitTestRequest& request, HitTestResult& result, |
- const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation, |
- const HitTestingTransformState* transformState, |
- double* zOffsetForDescendants, double* zOffset, |
- const HitTestingTransformState* unflattenedTransformState, |
- bool depthSortDescendants) |
-{ |
- if (!hasSelfPaintingLayerDescendant()) |
- return 0; |
- |
- RenderLayer* resultLayer = 0; |
- RenderLayerStackingNodeReverseIterator iterator(*m_stackingNode, childrentoVisit); |
- while (RenderLayerStackingNode* child = iterator.next()) { |
- RenderLayer* childLayer = child->layer(); |
- RenderLayer* hitLayer = 0; |
- HitTestResult tempResult(result.hitTestLocation()); |
- if (childLayer->isPaginated()) |
- hitLayer = hitTestPaginatedChildLayer(childLayer, rootLayer, request, tempResult, hitTestRect, hitTestLocation, transformState, zOffsetForDescendants); |
- else |
- hitLayer = childLayer->hitTestLayer(rootLayer, this, request, tempResult, hitTestRect, hitTestLocation, false, transformState, zOffsetForDescendants); |
- |
- // If it a rect-based test, we can safely append the temporary result since it might had hit |
- // nodes but not necesserily had hitLayer set. |
- if (result.isRectBasedTest()) |
- result.append(tempResult); |
- |
- if (isHitCandidate(hitLayer, depthSortDescendants, zOffset, unflattenedTransformState)) { |
- resultLayer = hitLayer; |
- if (!result.isRectBasedTest()) |
- result = tempResult; |
- if (!depthSortDescendants) |
- break; |
- } |
- } |
- |
- return resultLayer; |
-} |
- |
-RenderLayer* RenderLayer::hitTestPaginatedChildLayer(RenderLayer* childLayer, RenderLayer* rootLayer, const HitTestRequest& request, HitTestResult& result, |
- const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation, const HitTestingTransformState* transformState, double* zOffset) |
-{ |
- Vector<RenderLayer*> columnLayers; |
- RenderLayerStackingNode* ancestorNode = m_stackingNode->isNormalFlowOnly() ? parent()->stackingNode() : m_stackingNode->ancestorStackingContextNode(); |
- for (RenderLayer* curr = childLayer->parent(); curr; curr = curr->parent()) { |
- if (curr->renderer()->hasColumns() && checkContainingBlockChainForPagination(childLayer->renderer(), curr->renderBox())) |
- columnLayers.append(curr); |
- if (curr->stackingNode() == ancestorNode) |
- break; |
- } |
- |
- ASSERT(columnLayers.size()); |
- return hitTestChildLayerColumns(childLayer, rootLayer, request, result, hitTestRect, hitTestLocation, transformState, zOffset, |
- columnLayers, columnLayers.size() - 1); |
-} |
- |
-RenderLayer* RenderLayer::hitTestChildLayerColumns(RenderLayer* childLayer, RenderLayer* rootLayer, const HitTestRequest& request, HitTestResult& result, |
- const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation, const HitTestingTransformState* transformState, double* zOffset, |
- const Vector<RenderLayer*>& columnLayers, size_t columnIndex) |
-{ |
- RenderBlock* columnBlock = toRenderBlock(columnLayers[columnIndex]->renderer()); |
- |
- ASSERT(columnBlock && columnBlock->hasColumns()); |
- if (!columnBlock || !columnBlock->hasColumns()) |
- return 0; |
- |
- LayoutPoint layerOffset; |
- columnBlock->layer()->convertToLayerCoords(rootLayer, layerOffset); |
- |
- ColumnInfo* colInfo = columnBlock->columnInfo(); |
- int colCount = columnBlock->columnCount(colInfo); |
- |
- // We have to go backwards from the last column to the first. |
- bool isHorizontal = columnBlock->style()->isHorizontalWritingMode(); |
- LayoutUnit logicalLeft = columnBlock->logicalLeftOffsetForContent(); |
- LayoutUnit currLogicalTopOffset = 0; |
- int i; |
- for (i = 0; i < colCount; i++) { |
- LayoutRect colRect = columnBlock->columnRectAt(colInfo, i); |
- LayoutUnit blockDelta = (isHorizontal ? colRect.height() : colRect.width()); |
- if (columnBlock->style()->isFlippedBlocksWritingMode()) |
- currLogicalTopOffset += blockDelta; |
- else |
- currLogicalTopOffset -= blockDelta; |
- } |
- for (i = colCount - 1; i >= 0; i--) { |
- // For each rect, we clip to the rect, and then we adjust our coords. |
- LayoutRect colRect = columnBlock->columnRectAt(colInfo, i); |
- columnBlock->flipForWritingMode(colRect); |
- LayoutUnit currLogicalLeftOffset = (isHorizontal ? colRect.x() : colRect.y()) - logicalLeft; |
- LayoutUnit blockDelta = (isHorizontal ? colRect.height() : colRect.width()); |
- if (columnBlock->style()->isFlippedBlocksWritingMode()) |
- currLogicalTopOffset -= blockDelta; |
- else |
- currLogicalTopOffset += blockDelta; |
- |
- LayoutSize offset; |
- if (isHorizontal) { |
- if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) |
- offset = LayoutSize(currLogicalLeftOffset, currLogicalTopOffset); |
- else |
- offset = LayoutSize(0, colRect.y() + currLogicalTopOffset - columnBlock->borderTop() - columnBlock->paddingTop()); |
- } else { |
- if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) |
- offset = LayoutSize(currLogicalTopOffset, currLogicalLeftOffset); |
- else |
- offset = LayoutSize(colRect.x() + currLogicalTopOffset - columnBlock->borderLeft() - columnBlock->paddingLeft(), 0); |
- } |
- |
- colRect.moveBy(layerOffset); |
- |
- LayoutRect localClipRect(hitTestRect); |
- localClipRect.intersect(colRect); |
- |
- if (!localClipRect.isEmpty() && hitTestLocation.intersects(localClipRect)) { |
- RenderLayer* hitLayer = 0; |
- if (!columnIndex) { |
- // Apply a translation transform to change where the layer paints. |
- TransformationMatrix oldTransform; |
- bool oldHasTransform = childLayer->transform(); |
- if (oldHasTransform) |
- oldTransform = *childLayer->transform(); |
- TransformationMatrix newTransform(oldTransform); |
- newTransform.translateRight(offset.width(), offset.height()); |
- |
- childLayer->m_transform = adoptPtr(new TransformationMatrix(newTransform)); |
- hitLayer = childLayer->hitTestLayer(rootLayer, columnLayers[0], request, result, localClipRect, hitTestLocation, false, transformState, zOffset); |
- if (oldHasTransform) |
- childLayer->m_transform = adoptPtr(new TransformationMatrix(oldTransform)); |
- else |
- childLayer->m_transform.clear(); |
- } else { |
- // Adjust the transform such that the renderer's upper left corner will be at (0,0) in user space. |
- // This involves subtracting out the position of the layer in our current coordinate space. |
- RenderLayer* nextLayer = columnLayers[columnIndex - 1]; |
- RefPtr<HitTestingTransformState> newTransformState = nextLayer->createLocalTransformState(rootLayer, nextLayer, localClipRect, hitTestLocation, transformState); |
- newTransformState->translate(offset.width(), offset.height(), HitTestingTransformState::AccumulateTransform); |
- FloatPoint localPoint = newTransformState->mappedPoint(); |
- FloatQuad localPointQuad = newTransformState->mappedQuad(); |
- LayoutRect localHitTestRect = newTransformState->mappedArea().enclosingBoundingBox(); |
- HitTestLocation newHitTestLocation; |
- if (hitTestLocation.isRectBasedTest()) |
- newHitTestLocation = HitTestLocation(localPoint, localPointQuad); |
- else |
- newHitTestLocation = HitTestLocation(localPoint); |
- newTransformState->flatten(); |
- |
- hitLayer = hitTestChildLayerColumns(childLayer, columnLayers[columnIndex - 1], request, result, localHitTestRect, newHitTestLocation, |
- newTransformState.get(), zOffset, columnLayers, columnIndex - 1); |
- } |
- |
- if (hitLayer) |
- return hitLayer; |
- } |
- } |
- |
- return 0; |
-} |
- |
-void RenderLayer::blockSelectionGapsBoundsChanged() |
-{ |
- setNeedsCompositingInputsUpdate(); |
-} |
- |
-void RenderLayer::addBlockSelectionGapsBounds(const LayoutRect& bounds) |
-{ |
- m_blockSelectionGapsBounds.unite(enclosingIntRect(bounds)); |
- blockSelectionGapsBoundsChanged(); |
-} |
- |
-void RenderLayer::clearBlockSelectionGapsBounds() |
-{ |
- m_blockSelectionGapsBounds = IntRect(); |
- for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) |
- child->clearBlockSelectionGapsBounds(); |
- blockSelectionGapsBoundsChanged(); |
-} |
- |
-void RenderLayer::invalidatePaintForBlockSelectionGaps() |
-{ |
- for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) |
- child->invalidatePaintForBlockSelectionGaps(); |
- |
- if (m_blockSelectionGapsBounds.isEmpty()) |
- return; |
- |
- LayoutRect rect = m_blockSelectionGapsBounds; |
- if (renderer()->hasOverflowClip()) { |
- RenderBox* box = renderBox(); |
- rect.move(-box->scrolledContentOffset()); |
- if (!scrollableArea()->usesCompositedScrolling()) |
- rect.intersect(box->overflowClipRect(LayoutPoint())); |
- } |
- if (renderer()->hasClip()) |
- rect.intersect(toRenderBox(renderer())->clipRect(LayoutPoint())); |
- if (!rect.isEmpty()) |
- renderer()->invalidatePaintRectangle(rect); |
-} |
- |
-IntRect RenderLayer::blockSelectionGapsBounds() const |
-{ |
- if (!renderer()->isRenderBlockFlow()) |
- return IntRect(); |
- |
- RenderBlockFlow* renderBlockFlow = toRenderBlockFlow(renderer()); |
- LayoutRect gapRects = renderBlockFlow->selectionGapRectsForPaintInvalidation(renderBlockFlow); |
- |
- return pixelSnappedIntRect(gapRects); |
-} |
- |
-bool RenderLayer::hasBlockSelectionGapBounds() const |
-{ |
- // FIXME: it would be more accurate to return !blockSelectionGapsBounds().isEmpty(), but this is impossible |
- // at the moment because it causes invalid queries to layout-dependent code (crbug.com/372802). |
- // ASSERT(renderer()->document().lifecycle().state() >= DocumentLifecycle::LayoutClean); |
- |
- if (!renderer()->isRenderBlock()) |
- return false; |
- |
- return toRenderBlock(renderer())->shouldPaintSelectionGaps(); |
-} |
- |
-bool RenderLayer::intersectsDamageRect(const LayoutRect& layerBounds, const LayoutRect& damageRect, const RenderLayer* rootLayer, const LayoutPoint* offsetFromRoot) const |
-{ |
- // Always examine the canvas and the root. |
- // FIXME: Could eliminate the isDocumentElement() check if we fix background painting so that the RenderView |
- // paints the root's background. |
- if (isRootLayer() || renderer()->isDocumentElement()) |
- 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 if (renderer()->isTableRow()) { |
- // Our bounding box is just the union of all of our cells' border/overflow rects. |
- for (RenderObject* child = renderer()->slowFirstChild(); child; child = child->nextSibling()) { |
- if (child->isTableCell()) { |
- LayoutRect bbox = toRenderBox(child)->borderBoxRect(); |
- result.unite(bbox); |
- LayoutRect overflowRect = renderBox()->visualOverflowRect(); |
- if (bbox != overflowRect) |
- result.unite(overflowRect); |
- } |
- } |
- } else { |
- RenderBox* box = renderBox(); |
- ASSERT(box); |
- result = box->borderBoxRect(); |
- result.unite(box->visualOverflowRect()); |
- } |
- |
- ASSERT(renderer()->view()); |
- return result; |
-} |
- |
-static inline LayoutRect flippedLogicalBoundingBox(LayoutRect boundingBox, RenderObject* renderer) |
-{ |
- LayoutRect result = boundingBox; |
- if (renderer->isBox()) |
- toRenderBox(renderer)->flipForWritingMode(result); |
- else |
- renderer->containingBlock()->flipForWritingMode(result); |
- return result; |
-} |
- |
-LayoutRect RenderLayer::physicalBoundingBox(const RenderLayer* ancestorLayer, const LayoutPoint* offsetFromRoot) const |
-{ |
- LayoutRect result = flippedLogicalBoundingBox(logicalBoundingBox(), renderer()); |
- if (offsetFromRoot) |
- result.moveBy(*offsetFromRoot); |
- else |
- convertToLayerCoords(ancestorLayer, result); |
- return result; |
-} |
- |
-LayoutRect RenderLayer::fragmentsBoundingBox(const RenderLayer* ancestorLayer) const |
-{ |
- if (!enclosingPaginationLayer()) |
- return physicalBoundingBox(ancestorLayer); |
- |
- LayoutRect result = flippedLogicalBoundingBox(logicalBoundingBox(), renderer()); |
- convertFromFlowThreadToVisualBoundingBoxInAncestor(this, ancestorLayer, result); |
- return result; |
-} |
- |
-LayoutRect RenderLayer::boundingBoxForCompositingOverlapTest() const |
-{ |
- return overlapBoundsIncludeChildren() ? boundingBoxForCompositing() : fragmentsBoundingBox(this); |
-} |
- |
-static void expandRectForReflectionAndStackingChildren(const RenderLayer* ancestorLayer, RenderLayer::CalculateBoundsOptions options, LayoutRect& result) |
-{ |
- if (ancestorLayer->reflectionInfo() && !ancestorLayer->reflectionInfo()->reflectionLayer()->hasCompositedLayerMapping()) |
- result.unite(ancestorLayer->reflectionInfo()->reflectionLayer()->boundingBoxForCompositing(ancestorLayer)); |
- |
- 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()) { |
- // Here we exclude both directly composited layers and squashing layers |
- // because those RenderLayers don't paint into the graphics layer |
- // for this RenderLayer. For example, the bounds of squashed RenderLayers |
- // will be included in the computation of the appropriate squashing |
- // GraphicsLayer. |
- if (options != RenderLayer::ApplyBoundsChickenEggHacks && node->layer()->compositingState() != NotComposited) |
- continue; |
- result.unite(node->layer()->boundingBoxForCompositing(ancestorLayer, options)); |
- } |
-} |
- |
-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, DoNotApplyBoundsChickenEggHacks, result); |
- |
- result.moveBy(offsetFromRoot); |
- return result; |
-} |
- |
-LayoutRect RenderLayer::boundingBoxForCompositing(const RenderLayer* ancestorLayer, CalculateBoundsOptions options) const |
-{ |
- if (!isSelfPaintingLayer()) |
- return LayoutRect(); |
- |
- if (!ancestorLayer) |
- ancestorLayer = this; |
- |
- // FIXME: This could be improved to do a check like hasVisibleNonCompositingDescendantLayers() (bug 92580). |
- if (this != ancestorLayer && !hasVisibleContent() && !hasVisibleDescendant()) |
- return LayoutRect(); |
- |
- // The root layer is always just the size of the document. |
- if (isRootLayer()) |
- return m_renderer->view()->unscaledDocumentRect(); |
- |
- // The layer created for the RenderFlowThread is just a helper for painting and hit-testing, |
- // and should not contribute to the bounding box. The RenderMultiColumnSets will contribute |
- // the correct size for the rendered content of the multicol container. |
- if (useRegionBasedColumns() && renderer()->isRenderFlowThread()) |
- return LayoutRect(); |
- |
- LayoutRect result = clipper().localClipRect(); |
- if (result == LayoutRect::infiniteIntRect()) { |
- LayoutPoint origin; |
- result = physicalBoundingBox(ancestorLayer, &origin); |
- |
- const_cast<RenderLayer*>(this)->stackingNode()->updateLayerListsIfNeeded(); |
- |
- // Reflections are implemented with RenderLayers that hang off of the reflected layer. However, |
- // the reflection layer subtree does not include the subtree of the parent RenderLayer, so |
- // a recursive computation of stacking children yields no results. This breaks cases when there are stacking |
- // children of the parent, that need to be included in reflected composited bounds. |
- // Fix this by including composited bounds of stacking children of the reflected RenderLayer. |
- if (hasCompositedLayerMapping() && parent() && parent()->reflectionInfo() && parent()->reflectionInfo()->reflectionLayer() == this) |
- expandRectForReflectionAndStackingChildren(parent(), options, result); |
- else |
- expandRectForReflectionAndStackingChildren(this, options, 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 |
- result.expand(m_renderer->style()->filterOutsets()); |
- } |
- |
- if (paintsWithTransform(PaintBehaviorNormal) || (options == ApplyBoundsChickenEggHacks && transform())) |
- result = transform()->mapRect(result); |
- |
- if (enclosingPaginationLayer()) { |
- convertFromFlowThreadToVisualBoundingBoxInAncestor(this, ancestorLayer, result); |
- return result; |
- } |
- LayoutPoint delta; |
- convertToLayerCoords(ancestorLayer, delta); |
- result.moveBy(delta); |
- return result; |
-} |
- |
-CompositingState RenderLayer::compositingState() const |
-{ |
- ASSERT(isAllowedToQueryCompositingState()); |
- |
- // This is computed procedurally so there is no redundant state variable that |
- // can get out of sync from the real actual compositing state. |
- |
- if (m_groupedMapping) { |
- ASSERT(!m_compositedLayerMapping); |
- return PaintsIntoGroupedBacking; |
- } |
- |
- if (!m_compositedLayerMapping) |
- return NotComposited; |
- |
- return PaintsIntoOwnBacking; |
-} |
- |
-bool RenderLayer::isAllowedToQueryCompositingState() const |
-{ |
- if (gCompositingQueryMode == CompositingQueriesAreAllowed) |
- return true; |
- return renderer()->document().lifecycle().state() >= DocumentLifecycle::InCompositingUpdate; |
-} |
- |
-CompositedLayerMapping* RenderLayer::compositedLayerMapping() const |
-{ |
- ASSERT(isAllowedToQueryCompositingState()); |
- return m_compositedLayerMapping.get(); |
-} |
- |
-GraphicsLayer* RenderLayer::graphicsLayerBacking() const |
-{ |
- switch (compositingState()) { |
- case NotComposited: |
- return 0; |
- case PaintsIntoGroupedBacking: |
- return groupedMapping()->squashingLayer(); |
- default: |
- return compositedLayerMapping()->mainGraphicsLayer(); |
- } |
-} |
- |
-GraphicsLayer* RenderLayer::graphicsLayerBackingForScrolling() const |
-{ |
- switch (compositingState()) { |
- case NotComposited: |
- return 0; |
- case PaintsIntoGroupedBacking: |
- return groupedMapping()->squashingLayer(); |
- default: |
- return compositedLayerMapping()->scrollingContentsLayer() ? compositedLayerMapping()->scrollingContentsLayer() : compositedLayerMapping()->mainGraphicsLayer(); |
- } |
-} |
- |
-void RenderLayer::ensureCompositedLayerMapping() |
-{ |
- if (m_compositedLayerMapping) |
- return; |
- |
- m_compositedLayerMapping = adoptPtr(new CompositedLayerMapping(*this)); |
- m_compositedLayerMapping->setNeedsGraphicsLayerUpdate(GraphicsLayerUpdateSubtree); |
- |
- updateOrRemoveFilterEffectRenderer(); |
-} |
- |
-void RenderLayer::clearCompositedLayerMapping(bool layerBeingDestroyed) |
-{ |
- if (!layerBeingDestroyed) { |
- // We need to make sure our decendants get a geometry update. In principle, |
- // we could call setNeedsGraphicsLayerUpdate on our children, but that would |
- // require walking the z-order lists to find them. Instead, we over-invalidate |
- // by marking our parent as needing a geometry update. |
- if (RenderLayer* compositingParent = enclosingLayerWithCompositedLayerMapping(ExcludeSelf)) |
- compositingParent->compositedLayerMapping()->setNeedsGraphicsLayerUpdate(GraphicsLayerUpdateSubtree); |
- } |
- |
- m_compositedLayerMapping.clear(); |
- |
- if (!layerBeingDestroyed) |
- updateOrRemoveFilterEffectRenderer(); |
-} |
- |
-void RenderLayer::setGroupedMapping(CompositedLayerMapping* groupedMapping, bool layerBeingDestroyed) |
-{ |
- if (groupedMapping == m_groupedMapping) |
- return; |
- |
- if (!layerBeingDestroyed && m_groupedMapping) { |
- m_groupedMapping->setNeedsGraphicsLayerUpdate(GraphicsLayerUpdateSubtree); |
- m_groupedMapping->removeRenderLayerFromSquashingGraphicsLayer(this); |
- } |
- m_groupedMapping = groupedMapping; |
- if (!layerBeingDestroyed && m_groupedMapping) |
- m_groupedMapping->setNeedsGraphicsLayerUpdate(GraphicsLayerUpdateSubtree); |
-} |
- |
-bool RenderLayer::hasCompositedMask() const |
-{ |
- return m_compositedLayerMapping && m_compositedLayerMapping->hasMaskLayer(); |
-} |
- |
-bool RenderLayer::hasCompositedClippingMask() const |
-{ |
- return m_compositedLayerMapping && m_compositedLayerMapping->hasChildClippingMaskLayer(); |
-} |
- |
-bool RenderLayer::paintsWithTransform(PaintBehavior paintBehavior) const |
-{ |
- return transform() && ((paintBehavior & PaintBehaviorFlattenCompositingLayers) || compositingState() != PaintsIntoOwnBacking); |
-} |
- |
-bool RenderLayer::backgroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect) const |
-{ |
- if (!isSelfPaintingLayer() && !hasSelfPaintingLayerDescendant()) |
- return false; |
- |
- if (paintsWithTransparency(PaintBehaviorNormal)) |
- return false; |
- |
- // We can't use hasVisibleContent(), because that will be true if our renderer is hidden, but some child |
- // is visible and that child doesn't cover the entire rect. |
- if (renderer()->style()->visibility() != VISIBLE) |
- return false; |
- |
- if (paintsWithFilters() && renderer()->style()->filter().hasFilterThatAffectsOpacity()) |
- return false; |
- |
- // FIXME: Handle simple transforms. |
- if (paintsWithTransform(PaintBehaviorNormal)) |
- return false; |
- |
- // FIXME: Remove this check. |
- // This function should not be called when layer-lists are dirty. |
- // It is somehow getting triggered during style update. |
- if (m_stackingNode->zOrderListsDirty() || m_stackingNode->normalFlowListDirty()) |
- return false; |
- |
- // FIXME: We currently only check the immediate renderer, |
- // which will miss many cases. |
- if (renderer()->backgroundIsKnownToBeOpaqueInRect(localRect)) |
- return true; |
- |
- // We can't consult child layers if we clip, since they might cover |
- // parts of the rect that are clipped out. |
- if (renderer()->hasOverflowClip()) |
- return false; |
- |
- return childBackgroundIsKnownToBeOpaqueInRect(localRect); |
-} |
- |
-bool RenderLayer::childBackgroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect) const |
-{ |
- RenderLayerStackingNodeReverseIterator revertseIterator(*m_stackingNode, PositiveZOrderChildren | NormalFlowChildren | NegativeZOrderChildren); |
- while (RenderLayerStackingNode* child = revertseIterator.next()) { |
- const RenderLayer* childLayer = child->layer(); |
- // Stop at composited paint boundaries. |
- if (childLayer->isPaintInvalidationContainer()) |
- continue; |
- |
- if (!childLayer->canUseConvertToLayerCoords()) |
- continue; |
- |
- LayoutPoint childOffset; |
- LayoutRect childLocalRect(localRect); |
- childLayer->convertToLayerCoords(this, childOffset); |
- childLocalRect.moveBy(-childOffset); |
- |
- if (childLayer->backgroundIsKnownToBeOpaqueInRect(childLocalRect)) |
- return true; |
- } |
- return false; |
-} |
- |
-bool RenderLayer::shouldBeSelfPaintingLayer() const |
-{ |
- if (renderer()->isRenderPart() && toRenderPart(renderer())->requiresAcceleratedCompositing()) |
- return true; |
- return m_layerType == NormalLayer |
- || (m_scrollableArea && m_scrollableArea->hasOverlayScrollbars()) |
- || needsCompositedScrolling(); |
-} |
- |
-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)->size().isEmpty()) |
- return true; |
- } |
- } |
- return false; |
-} |
- |
-bool RenderLayer::hasBoxDecorationsOrBackground() const |
-{ |
- return renderer()->style()->hasBoxDecorations() || renderer()->style()->hasBackground(); |
-} |
- |
-bool RenderLayer::hasVisibleBoxDecorations() const |
-{ |
- if (!hasVisibleContent()) |
- return false; |
- |
- return hasBoxDecorationsOrBackground() || hasOverflowControls(); |
-} |
- |
-void RenderLayer::updateFilters(const RenderStyle* oldStyle, const RenderStyle* newStyle) |
-{ |
- if (!newStyle->hasFilter() && (!oldStyle || !oldStyle->hasFilter())) |
- return; |
- |
- updateOrRemoveFilterClients(); |
- updateOrRemoveFilterEffectRenderer(); |
-} |
- |
-bool RenderLayer::attemptDirectCompositingUpdate(StyleDifference diff, const RenderStyle* oldStyle) |
-{ |
- CompositingReasons oldPotentialCompositingReasonsFromStyle = m_potentialCompositingReasonsFromStyle; |
- compositor()->updatePotentialCompositingReasonsFromStyle(this); |
- |
- // This function implements an optimization for transforms and opacity. |
- // A common pattern is for a touchmove handler to update the transform |
- // and/or an opacity of an element every frame while the user moves their |
- // finger across the screen. The conditions below recognize when the |
- // compositing state is set up to receive a direct transform or opacity |
- // update. |
- |
- if (!diff.hasAtMostPropertySpecificDifferences(StyleDifference::TransformChanged | StyleDifference::OpacityChanged)) |
- return false; |
- // The potentialCompositingReasonsFromStyle could have changed without |
- // a corresponding StyleDifference if an animation started or ended. |
- if (m_potentialCompositingReasonsFromStyle != oldPotentialCompositingReasonsFromStyle) |
- return false; |
- // We could add support for reflections if we updated the transform on |
- // the reflection layers. |
- if (renderer()->hasReflection()) |
- return false; |
- // If we're unwinding a scheduleSVGFilterLayerUpdateHack(), then we can't |
- // perform a direct compositing update because the filters code is going |
- // to produce different output this time around. We can remove this code |
- // once we fix the chicken/egg bugs in the filters code and delete the |
- // scheduleSVGFilterLayerUpdateHack(). |
- if (renderer()->node() && renderer()->node()->svgFilterNeedsLayerUpdate()) |
- return false; |
- if (!m_compositedLayerMapping) |
- return false; |
- |
- // To cut off almost all the work in the compositing update for |
- // this case, we treat inline transforms has having assumed overlap |
- // (similar to how we treat animated transforms). Notice that we read |
- // CompositingReasonInlineTransform from the m_compositingReasons, which |
- // means that the inline transform actually triggered assumed overlap in |
- // the overlap map. |
- if (diff.transformChanged() && !(m_compositingReasons & CompositingReasonInlineTransform)) |
- return false; |
- |
- // We composite transparent RenderLayers differently from non-transparent |
- // RenderLayers even when the non-transparent RenderLayers are already a |
- // stacking context. |
- if (diff.opacityChanged() && m_renderer->style()->hasOpacity() != oldStyle->hasOpacity()) |
- return false; |
- |
- updateTransform(oldStyle, renderer()->style()); |
- |
- // FIXME: Consider introducing a smaller graphics layer update scope |
- // that just handles transforms and opacity. GraphicsLayerUpdateLocal |
- // will also program bounds, clips, and many other properties that could |
- // not possibly have changed. |
- m_compositedLayerMapping->setNeedsGraphicsLayerUpdate(GraphicsLayerUpdateLocal); |
- compositor()->setNeedsCompositingUpdate(CompositingUpdateAfterGeometryChange); |
- return true; |
-} |
- |
-void RenderLayer::styleChanged(StyleDifference diff, const RenderStyle* oldStyle) |
-{ |
- if (attemptDirectCompositingUpdate(diff, oldStyle)) |
- return; |
- |
- m_stackingNode->updateIsNormalFlowOnly(); |
- m_stackingNode->updateStackingNodesAfterStyleChange(oldStyle); |
- |
- if (m_scrollableArea) |
- m_scrollableArea->updateAfterStyleChange(oldStyle); |
- |
- // Overlay scrollbars can make this layer self-painting so we need |
- // to recompute the bit once scrollbars have been updated. |
- updateSelfPaintingLayer(); |
- |
- if (!oldStyle || !renderer()->style()->reflectionDataEquivalent(oldStyle)) { |
- ASSERT(!oldStyle || diff.needsFullLayout()); |
- updateReflectionInfo(oldStyle); |
- } |
- |
- updateDescendantDependentFlags(); |
- |
- updateTransform(oldStyle, renderer()->style()); |
- updateFilters(oldStyle, renderer()->style()); |
- |
- setNeedsCompositingInputsUpdate(); |
-} |
- |
-bool RenderLayer::scrollsOverflow() const |
-{ |
- if (RenderLayerScrollableArea* scrollableArea = this->scrollableArea()) |
- return scrollableArea->scrollsOverflow(); |
- |
- return false; |
-} |
- |
-FilterOperations RenderLayer::computeFilterOperations(const RenderStyle* style) |
-{ |
- const FilterOperations& filters = style->filter(); |
- if (filters.hasReferenceFilter()) { |
- for (size_t i = 0; i < filters.size(); ++i) { |
- FilterOperation* filterOperation = filters.operations().at(i).get(); |
- if (filterOperation->type() != FilterOperation::REFERENCE) |
- continue; |
- ReferenceFilterOperation* referenceOperation = toReferenceFilterOperation(filterOperation); |
- // FIXME: Cache the ReferenceFilter if it didn't change. |
- RefPtrWillBeRawPtr<ReferenceFilter> referenceFilter = ReferenceFilter::create(style->effectiveZoom()); |
- referenceFilter->setLastEffect(ReferenceFilterBuilder::build(referenceFilter.get(), renderer(), referenceFilter->sourceGraphic(), |
- referenceOperation)); |
- referenceOperation->setFilter(referenceFilter.release()); |
- } |
- } |
- |
- return filters; |
-} |
- |
-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()) { |
- RefPtrWillBeRawPtr<FilterEffectRenderer> filterRenderer = FilterEffectRenderer::create(); |
- filterInfo->setRenderer(filterRenderer.release()); |
- } |
- |
- // 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); |
-} |
- |
-void RenderLayer::filterNeedsPaintInvalidation() |
-{ |
- { |
- DeprecatedScheduleStyleRecalcDuringLayout marker(renderer()->document().lifecycle()); |
- // It's possible for scheduleSVGFilterLayerUpdateHack to schedule a style recalc, which |
- // is a problem because this function can be called while performing layout. |
- // Presumably this represents an illegal data flow of layout or compositing |
- // information into the style system. |
- toElement(renderer()->node())->scheduleSVGFilterLayerUpdateHack(); |
- } |
- |
- renderer()->setShouldDoFullPaintInvalidation(); |
-} |
- |
-void RenderLayer::addLayerHitTestRects(LayerHitTestRects& rects) const |
-{ |
- computeSelfHitTestRects(rects); |
- for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) |
- child->addLayerHitTestRects(rects); |
-} |
- |
-void RenderLayer::computeSelfHitTestRects(LayerHitTestRects& rects) const |
-{ |
- if (!size().isEmpty()) { |
- Vector<LayoutRect> rect; |
- |
- if (renderBox() && renderBox()->scrollsOverflow()) { |
- // For scrolling layers, rects are taken to be in the space of the contents. |
- // We need to include the bounding box of the layer in the space of its parent |
- // (eg. for border / scroll bars) and if it's composited then the entire contents |
- // as well as they may be on another composited layer. Skip reporting contents |
- // for non-composited layers as they'll get projected to the same layer as the |
- // bounding box. |
- if (compositingState() != NotComposited) |
- rect.append(m_scrollableArea->overflowRect()); |
- |
- rects.set(this, rect); |
- if (const RenderLayer* parentLayer = parent()) { |
- LayerHitTestRects::iterator iter = rects.find(parentLayer); |
- if (iter == rects.end()) { |
- rects.add(parentLayer, Vector<LayoutRect>()).storedValue->value.append(physicalBoundingBox(parentLayer)); |
- } else { |
- iter->value.append(physicalBoundingBox(parentLayer)); |
- } |
- } |
- } else { |
- rect.append(logicalBoundingBox()); |
- rects.set(this, rect); |
- } |
- } |
-} |
- |
-void RenderLayer::setShouldDoFullPaintInvalidationIncludingNonCompositingDescendants() |
-{ |
- renderer()->setShouldDoFullPaintInvalidation(); |
- |
- // Disable for reading compositingState() in isPaintInvalidationContainer() below. |
- DisableCompositingQueryAsserts disabler; |
- |
- for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) { |
- if (!child->isPaintInvalidationContainer()) |
- child->setShouldDoFullPaintInvalidationIncludingNonCompositingDescendants(); |
- } |
-} |
- |
-DisableCompositingQueryAsserts::DisableCompositingQueryAsserts() |
- : m_disabler(gCompositingQueryMode, CompositingQueriesAreAllowed) { } |
- |
-} // 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::LayoutAsTextShowAllLayers | blink::LayoutAsTextShowLayerNesting | blink::LayoutAsTextShowCompositedLayers | blink::LayoutAsTextShowAddresses | blink::LayoutAsTextShowIDAndClass | blink::LayoutAsTextDontUpdateLayout | blink::LayoutAsTextShowLayoutState); |
- fprintf(stderr, "%s\n", output.utf8().data()); |
- } |
-} |
- |
-void showLayerTree(const blink::RenderObject* renderer) |
-{ |
- if (!renderer) |
- return; |
- showLayerTree(renderer->enclosingLayer()); |
-} |
-#endif |