blob: 1fe75502941ff79698d21c7cea3cb5d73970b4ac [file] [log] [blame]
/*
* Copyright (C) 2003, 2009, 2012 Apple Inc. All rights reserved.
* Copyright (C) 2013 Intel Corporation. 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.
*/
#ifndef PaintLayer_h
#define PaintLayer_h
#include "core/CoreExport.h"
#include "core/layout/ClipRectsCache.h"
#include "core/layout/LayoutBox.h"
#include "core/paint/PaintLayerClipper.h"
#include "core/paint/PaintLayerFilterInfo.h"
#include "core/paint/PaintLayerFragment.h"
#include "core/paint/PaintLayerPainter.h"
#include "core/paint/PaintLayerScrollableArea.h"
#include "core/paint/PaintLayerStackingNode.h"
#include "core/paint/PaintLayerStackingNodeIterator.h"
#include "platform/graphics/CompositingReasons.h"
#include "platform/graphics/SquashingDisallowedReasons.h"
#include "wtf/Allocator.h"
#include "wtf/AutoReset.h"
#include "wtf/PtrUtil.h"
#include <memory>
namespace blink {
class CompositedLayerMapping;
class CompositorFilterOperations;
class ComputedStyle;
class FilterOperations;
class HitTestRequest;
class HitTestResult;
class HitTestingTransformState;
class PaintLayerCompositor;
class PaintTiming;
class TransformationMatrix;
enum IncludeSelfOrNot { IncludeSelf, ExcludeSelf };
enum CompositingQueryMode {
CompositingQueriesAreAllowed,
CompositingQueriesAreOnlyAllowedInCertainDocumentLifecyclePhases
};
// FIXME: remove this once the compositing query ASSERTS are no longer hit.
class CORE_EXPORT DisableCompositingQueryAsserts {
STACK_ALLOCATED();
WTF_MAKE_NONCOPYABLE(DisableCompositingQueryAsserts);
public:
DisableCompositingQueryAsserts();
private:
AutoReset<CompositingQueryMode> m_disabler;
};
struct PaintLayerRareData {
PaintLayerRareData();
~PaintLayerRareData();
// Our current relative position offset.
LayoutSize offsetForInFlowPosition;
std::unique_ptr<TransformationMatrix> transform;
// Pointer to the enclosing Layer that caused us to be paginated. It is 0 if
// we are not paginated.
//
// See LayoutMultiColumnFlowThread and
// https://sites.google.com/a/chromium.org/dev/developers/design-documents/multi-column-layout
// for more information about the multicol implementation. It's important to
// understand the difference between flow thread coordinates and visual
// coordinates when working with multicol in Layer, since Layer is one of the
// few places where we have to worry about the visual ones. Internally we try
// to use flow-thread coordinates whenever possible.
PaintLayer* enclosingPaginationLayer;
// These compositing reasons are updated whenever style changes, not while
// updating compositing layers. They should not be used to infer the
// compositing state of this layer.
CompositingReasons potentialCompositingReasonsFromStyle;
// Once computed, indicates all that a layer needs to become composited using
// the CompositingReasons enum bitfield.
CompositingReasons compositingReasons;
// This captures reasons why a paint layer might be forced to be separately
// composited rather than sharing a backing with another layer.
SquashingDisallowedReasons squashingDisallowedReasons;
// If the layer paints into its own backings, this keeps track of the
// backings. It's nullptr if the layer is not composited or paints into
// grouped backing.
std::unique_ptr<CompositedLayerMapping> compositedLayerMapping;
// If the layer paints into grouped backing (i.e. squashed), this points to
// the grouped CompositedLayerMapping. It's null if the layer is not
// composited or paints into its own backing.
CompositedLayerMapping* groupedMapping;
Persistent<PaintLayerFilterInfo> filterInfo;
// The accumulated subpixel offset of a composited layer's composited bounds
// compared to absolute coordinates.
LayoutSize subpixelAccumulation;
};
// PaintLayer is an old object that handles lots of unrelated operations.
//
// We want it to die at some point and be replaced by more focused objects,
// which would remove (or at least compartimentalize) a lot of complexity.
// See the STATUS OF PAINTLAYER section below.
//
// The class is central to painting and hit-testing. That's because it handles
// a lot of tasks (we included ones done by associated satellite objects for
// historical reasons):
// - Complex painting operations (opacity, clipping, filters, reflections, ...).
// - hardware acceleration (through PaintLayerCompositor).
// - scrolling (through PaintLayerScrollableArea).
// - some performance optimizations.
//
// The compositing code is also based on PaintLayer. The entry to it is the
// PaintLayerCompositor, which fills |m_compositedLayerMapping| for hardware
// accelerated layers.
//
// TODO(jchaffraix): Expand the documentation about hardware acceleration.
//
//
// ***** SELF-PAINTING LAYER *****
// One important concept about PaintLayer is "self-painting"
// (m_isSelfPaintingLayer).
// PaintLayer started as the implementation of a stacking context. This meant
// that we had to use PaintLayer's painting order (the code is now in
// PaintLayerPainter and PaintLayerStackingNode) instead of the LayoutObject's
// children order. Over the years, as more operations were handled by
// PaintLayer, some LayoutObjects that were not stacking context needed to have
// a PaintLayer for bookkeeping reasons. One such example is the overflow hidden
// case that wanted hardware acceleration and thus had to allocate a PaintLayer
// to get it. However overflow hidden is something LayoutObject can paint
// without a PaintLayer, which includes a lot of painting overhead. Thus the
// self-painting flag was introduced. The flag is a band-aid solution done for
// performance reason only. It just brush over the underlying problem, which is
// that its design doesn't match the system's requirements anymore.
//
// Note that the self-painting flag determines how we paint a LayoutObject:
// - If the flag is true, the LayoutObject is painted through its PaintLayer,
// which is required to apply complex paint operations. The paint order is
// handled by PaintLayerPainter::paintChildren, where we look at children
// PaintLayers.
// - If the flag is false, the LayoutObject is painted like normal children (ie
// as if it didn't have a PaintLayer). The paint order is handled by
// BlockPainter::paintChild that looks at children LayoutObjects.
// This means that the self-painting flag changes the painting order in a subtle
// way, which can potentially have visible consequences. Those bugs are called
// painting inversion as we invert the order of painting for 2 elements
// (painting one wrongly in front of the other).
// See https://crbug.com/370604 for an example.
//
//
// ***** STATUS OF PAINTLAYER *****
// We would like to remove this class in the future. The reasons for the removal
// are:
// - it has been a dumping ground for features for too long.
// - it is the wrong level of abstraction, bearing no correspondence to any CSS
// concept.
//
// Its features need to be migrated to helper objects. This was started with the
// introduction of satellite objects: PaintLayer*. Those helper objects then
// need to be moved to the appropriate LayoutObject class, probably to a rare
// data field to avoid growing all the LayoutObjects.
//
// A good example of this is PaintLayerScrollableArea, which can only happen
// be instanciated for LayoutBoxes. With the current design, it's hard to know
// that by reading the code.
class CORE_EXPORT PaintLayer : public DisplayItemClient {
WTF_MAKE_NONCOPYABLE(PaintLayer);
public:
PaintLayer(LayoutBoxModelObject*);
~PaintLayer() override;
// DisplayItemClient methods
String debugName() const final;
LayoutRect visualRect() const final;
LayoutBoxModelObject* layoutObject() const { return m_layoutObject; }
LayoutBox* layoutBox() const {
return m_layoutObject && m_layoutObject->isBox()
? toLayoutBox(m_layoutObject)
: 0;
}
PaintLayer* parent() const { return m_parent; }
PaintLayer* previousSibling() const { return m_previous; }
PaintLayer* nextSibling() const { return m_next; }
PaintLayer* firstChild() const { return m_first; }
PaintLayer* lastChild() const { return m_last; }
// TODO(wangxianzhu): Find a better name for it. 'paintContainer' might be
// good but we can't use it for now because it conflicts with
// PaintInfo::paintContainer.
PaintLayer* compositingContainer() const;
void addChild(PaintLayer* newChild, PaintLayer* beforeChild = 0);
PaintLayer* removeChild(PaintLayer*);
void removeOnlyThisLayerAfterStyleChange();
void insertOnlyThisLayerAfterStyleChange();
void styleDidChange(StyleDifference, const ComputedStyle* oldStyle);
// FIXME: Many people call this function while it has out-of-date information.
bool isSelfPaintingLayer() const { return m_isSelfPaintingLayer; }
// PaintLayers which represent LayoutParts may become self-painting due to
// being composited. If this is the case, this method returns true.
bool isSelfPaintingOnlyBecauseIsCompositedPart() const;
bool isTransparent() const {
return layoutObject()->isTransparent() ||
layoutObject()->style()->hasBlendMode() || layoutObject()->hasMask();
}
const PaintLayer* root() const {
const PaintLayer* curr = this;
while (curr->parent())
curr = curr->parent();
return curr;
}
const LayoutPoint& location() const {
ASSERT(!m_needsPositionUpdate);
return m_location;
}
// FIXME: size() should ASSERT(!m_needsPositionUpdate) as well, but that fails
// in some tests, for example, fast/repaint/clipped-relative.html.
const IntSize& size() const { return m_size; }
void setSizeHackForLayoutTreeAsText(const IntSize& size) { m_size = size; }
LayoutRect rect() const { return LayoutRect(location(), LayoutSize(size())); }
bool isRootLayer() const { return m_isRootLayer; }
PaintLayerCompositor* compositor() const;
// Notification from the layoutObject that its content changed (e.g. current
// frame of image changed). Allows updates of layer content without
// invalidating paint.
void contentChanged(ContentChangeType);
void updateLayerPosition();
void updateLayerPositionsAfterLayout();
void updateLayerPositionsAfterOverflowScroll(const DoubleSize& scrollDelta);
PaintLayer* enclosingPaginationLayer() const {
return m_rareData ? m_rareData->enclosingPaginationLayer : nullptr;
}
void updateTransformationMatrix();
PaintLayer* renderingContextRoot();
const PaintLayer* renderingContextRoot() const;
LayoutSize offsetForInFlowPosition() const {
return m_rareData ? m_rareData->offsetForInFlowPosition : LayoutSize();
}
PaintLayerStackingNode* stackingNode() { return m_stackingNode.get(); }
const PaintLayerStackingNode* stackingNode() const {
return m_stackingNode.get();
}
bool subtreeIsInvisible() const {
return !hasVisibleContent() && !hasVisibleDescendant();
}
// FIXME: hasVisibleContent() should call updateDescendantDependentFlags() if
// m_isVisibleContentDirty.
bool hasVisibleContent() const {
DCHECK(!m_isVisibleContentDirty);
return m_hasVisibleContent;
}
// FIXME: hasVisibleDescendant() should call updateDescendantDependentFlags()
// if m_isVisibleDescendantDirty.
bool hasVisibleDescendant() const {
DCHECK(!m_isVisibleDescendantDirty);
return m_hasVisibleDescendant;
}
void dirtyVisibleContentStatus();
void potentiallyDirtyVisibleContentStatus(EVisibility);
bool hasBoxDecorationsOrBackground() const;
bool hasVisibleBoxDecorations() const;
// True if this layer container layoutObjects that paint.
bool hasNonEmptyChildLayoutObjects() const;
// Will ensure that isAllScrollingContentComposited() is up to date.
void updateScrollingStateAfterCompositingChange();
bool isAllScrollingContentComposited() const {
return m_isAllScrollingContentComposited;
}
// Gets the ancestor layer that serves as the containing block of this layer.
// This is either another out of flow positioned layer, or one that contains
// paint. If |ancestor| is specified, |*skippedAncestor| will be set to true
// if |ancestor| is found in the ancestry chain between this layer and the
// containing block layer; if not found, it will be set to false. Either both
// |ancestor| and |skippedAncestor| should be nullptr, or none of them should.
PaintLayer* containingLayerForOutOfFlowPositioned(
const PaintLayer* ancestor = nullptr,
bool* skippedAncestor = nullptr) const;
bool isPaintInvalidationContainer() const;
// Do *not* call this method unless you know what you are dooing. You probably
// want to call enclosingCompositingLayerForPaintInvalidation() instead.
// If includeSelf is true, may return this.
PaintLayer* enclosingLayerWithCompositedLayerMapping(IncludeSelfOrNot) const;
// Returns the enclosing layer root into which this layer paints, inclusive of
// this one. Note that the enclosing layer may or may not have its own
// GraphicsLayer backing, but is nevertheless the root for a call to the
// Layer::paint*() methods.
PaintLayer* enclosingLayerForPaintInvalidation() const;
PaintLayer* enclosingLayerForPaintInvalidationCrossingFrameBoundaries() const;
bool hasAncestorWithFilterThatMovesPixels() const;
bool canUseConvertToLayerCoords() const {
// These LayoutObjects have an impact on their layers without the
// layoutObjects knowing about it.
return !layoutObject()->hasTransformRelatedProperty() &&
!layoutObject()->isSVGRoot();
}
void convertToLayerCoords(const PaintLayer* ancestorLayer,
LayoutPoint&) const;
void convertToLayerCoords(const PaintLayer* ancestorLayer, LayoutRect&) const;
// Does the same as convertToLayerCoords() when not in multicol. For multicol,
// however, convertToLayerCoords() calculates the offset in flow-thread
// coordinates (what the layout engine uses internally), while this method
// calculates the visual coordinates; i.e. it figures out which column the
// layer starts in and adds in the offset. See
// http://www.chromium.org/developers/design-documents/multi-column-layout for
// more info.
LayoutPoint visualOffsetFromAncestor(const PaintLayer* ancestorLayer) const;
// Convert a bounding box from flow thread coordinates, relative to |this|, to
// visual coordinates, relative to |ancestorLayer|.
// See http://www.chromium.org/developers/design-documents/multi-column-layout
// for more info on these coordinate types. This method requires this layer
// to be paginated; i.e. it must have an enclosingPaginationLayer().
void convertFromFlowThreadToVisualBoundingBoxInAncestor(
const PaintLayer* ancestorLayer,
LayoutRect&) const;
// The hitTest() method looks for mouse events by walking layers that
// intersect the point from front to back.
bool hitTest(HitTestResult&);
bool intersectsDamageRect(const LayoutRect& layerBounds,
const LayoutRect& damageRect,
const LayoutPoint& offsetFromRoot) const;
// MaybeIncludeTransformForAncestorLayer means that a transform on
// |ancestorLayer| may be applied to the bounding box, in particular if
// paintsWithTransform() is true.
enum CalculateBoundsOptions {
MaybeIncludeTransformForAncestorLayer,
NeverIncludeTransformForAncestorLayer,
IncludeTransformsAndCompositedChildLayers,
};
// Bounding box relative to some ancestor layer. Pass offsetFromRoot if known.
LayoutRect physicalBoundingBox(const LayoutPoint& offsetFromRoot) const;
LayoutRect physicalBoundingBox(const PaintLayer* ancestorLayer) const;
LayoutRect physicalBoundingBoxIncludingStackingChildren(
const LayoutPoint& offsetFromRoot,
CalculateBoundsOptions = MaybeIncludeTransformForAncestorLayer) const;
LayoutRect fragmentsBoundingBox(const PaintLayer* ancestorLayer) const;
FloatRect boxForFilter() const;
LayoutRect boxForClipPath() const;
LayoutRect boundingBoxForCompositingOverlapTest() const;
// If true, this layer's children are included in its bounds for overlap
// testing. We can't rely on the children's positions if this layer has a
// filter that could have moved the children's pixels around.
bool overlapBoundsIncludeChildren() const;
LayoutRect boundingBoxForCompositing(
const PaintLayer* ancestorLayer = 0,
CalculateBoundsOptions = MaybeIncludeTransformForAncestorLayer) const;
LayoutUnit staticInlinePosition() const { return m_staticInlinePosition; }
LayoutUnit staticBlockPosition() const { return m_staticBlockPosition; }
void setStaticInlinePosition(LayoutUnit position) {
m_staticInlinePosition = position;
}
void setStaticBlockPosition(LayoutUnit position) {
m_staticBlockPosition = position;
}
LayoutSize subpixelAccumulation() const;
void setSubpixelAccumulation(const LayoutSize&);
bool hasTransformRelatedProperty() const {
return layoutObject()->hasTransformRelatedProperty();
}
// Note that this transform has the transform-origin baked in.
TransformationMatrix* transform() const {
return m_rareData ? m_rareData->transform.get() : nullptr;
}
// currentTransform computes a transform which takes accelerated animations
// into account. The resulting transform has transform-origin baked in. If the
// layer does not have a transform, returns the identity matrix.
TransformationMatrix currentTransform() const;
TransformationMatrix renderableTransform(GlobalPaintFlags) const;
// Get the perspective transform, which is applied to transformed sublayers.
// Returns true if the layer has a -webkit-perspective.
// Note that this transform does not have the perspective-origin baked in.
TransformationMatrix perspectiveTransform() const;
FloatPoint perspectiveOrigin() const;
bool preserves3D() const { return layoutObject()->style()->preserves3D(); }
bool has3DTransform() const {
return m_rareData && m_rareData->transform &&
!m_rareData->transform->isAffine();
}
// FIXME: reflections should force transform-style to be flat in the style:
// https://bugs.webkit.org/show_bug.cgi?id=106959
bool shouldPreserve3D() const {
return !layoutObject()->hasReflection() &&
layoutObject()->style()->preserves3D();
}
void filterNeedsPaintInvalidation();
// Returns |true| if any property that renders using filter operations is
// used (including, but not limited to, 'filter' and 'box-reflect').
bool hasFilterInducingProperty() const {
return layoutObject()->hasFilterInducingProperty();
}
void* operator new(size_t);
// Only safe to call from LayoutBoxModelObject::destroyLayer()
void operator delete(void*);
CompositingState compositingState() const;
// This returns true if our document is in a phase of its lifestyle during
// which compositing state may legally be read.
bool isAllowedToQueryCompositingState() const;
// Don't null check this.
// FIXME: Rename.
CompositedLayerMapping* compositedLayerMapping() const;
GraphicsLayer* graphicsLayerBacking() const;
GraphicsLayer* graphicsLayerBackingForScrolling() const;
// Returns true for layers with scrollable overflow which have a background
// that can be painted into the composited scrolling contents layer when it
// exist (i.e. the background can scroll with the content). When the
// background is also opaque this allows us to composite the scroller even on
// low DPI as we can draw with subpixel anti-aliasing.
bool canPaintBackgroundOntoScrollingContentsLayer() const;
// NOTE: If you are using hasCompositedLayerMapping to determine the state of
// compositing for this layer, (and not just to do bookkeeping related to the
// mapping like, say, allocating or deallocating a mapping), then you may have
// incorrect logic. Use compositingState() instead.
// FIXME: This is identical to null checking compositedLayerMapping(), why not
// just call that?
bool hasCompositedLayerMapping() const {
return m_rareData && m_rareData->compositedLayerMapping;
}
void ensureCompositedLayerMapping();
void clearCompositedLayerMapping(bool layerBeingDestroyed = false);
CompositedLayerMapping* groupedMapping() const {
return m_rareData ? m_rareData->groupedMapping : nullptr;
}
enum SetGroupMappingOptions {
InvalidateLayerAndRemoveFromMapping,
DoNotInvalidateLayerAndRemoveFromMapping
};
void setGroupedMapping(CompositedLayerMapping*, SetGroupMappingOptions);
bool hasCompositedMask() const;
bool hasCompositedClippingMask() const;
bool needsCompositedScrolling() const {
return m_scrollableArea && m_scrollableArea->needsCompositedScrolling();
}
static void mapPointInPaintInvalidationContainerToBacking(
const LayoutBoxModelObject& paintInvalidationContainer,
FloatPoint&);
static void mapRectInPaintInvalidationContainerToBacking(
const LayoutBoxModelObject& paintInvalidationContainer,
LayoutRect&);
// Adjusts the given rect (in the coordinate space of the LayoutObject) to the
// coordinate space of |paintInvalidationContainer|'s GraphicsLayer backing.
// Should use PaintInvalidationState::mapRectToPaintInvalidationBacking()
// instead if PaintInvalidationState is available.
static void mapRectToPaintInvalidationBacking(
const LayoutObject&,
const LayoutBoxModelObject& paintInvalidationContainer,
LayoutRect&);
bool paintsWithTransparency(GlobalPaintFlags globalPaintFlags) const {
return isTransparent() &&
((globalPaintFlags & GlobalPaintFlattenCompositingLayers) ||
compositingState() != PaintsIntoOwnBacking);
}
bool paintsWithTransform(GlobalPaintFlags) const;
// Returns true if background phase is painted opaque in the given rect.
// The query rect is given in local coordinates.
bool backgroundIsKnownToBeOpaqueInRect(const LayoutRect&) const;
bool containsDirtyOverlayScrollbars() const {
return m_containsDirtyOverlayScrollbars;
}
void setContainsDirtyOverlayScrollbars(bool dirtyScrollbars) {
m_containsDirtyOverlayScrollbars = dirtyScrollbars;
}
CompositorFilterOperations createCompositorFilterOperationsForFilter(
const ComputedStyle&);
CompositorFilterOperations createCompositorFilterOperationsForBackdropFilter(
const ComputedStyle&);
bool paintsWithFilters() const;
bool paintsWithBackdropFilters() const;
FilterEffect* lastFilterEffect() const;
// Maps "forward" to determine which pixels in a destination rect are
// affected by pixels in the source rect.
// See also FilterEffect::mapRect.
FloatRect mapRectForFilter(const FloatRect&) const;
// Calls the above, rounding outwards.
LayoutRect mapLayoutRectForFilter(const LayoutRect&) const;
bool hasFilterThatMovesPixels() const;
PaintLayerFilterInfo* filterInfo() const {
return m_rareData ? m_rareData->filterInfo.get() : nullptr;
}
PaintLayerFilterInfo& ensureFilterInfo();
void updateFilters(const ComputedStyle* oldStyle,
const ComputedStyle& newStyle);
Node* enclosingNode() const;
bool isInTopLayer() const;
bool scrollsWithViewport() const;
bool scrollsWithRespectTo(const PaintLayer*) const;
void addLayerHitTestRects(LayerHitTestRects&) const;
// Compute rects only for this layer
void computeSelfHitTestRects(LayerHitTestRects&) const;
// FIXME: This should probably return a ScrollableArea but a lot of internal
// methods are mistakenly exposed.
PaintLayerScrollableArea* getScrollableArea() const {
return m_scrollableArea.get();
}
PaintLayerClipper clipper() const { return PaintLayerClipper(*this); }
bool scrollsOverflow() const;
CompositingReasons potentialCompositingReasonsFromStyle() const {
return m_rareData ? m_rareData->potentialCompositingReasonsFromStyle
: CompositingReasonNone;
}
void setPotentialCompositingReasonsFromStyle(CompositingReasons reasons) {
ASSERT(reasons ==
(reasons & CompositingReasonComboAllStyleDeterminedReasons));
if (m_rareData || reasons != CompositingReasonNone)
ensureRareData().potentialCompositingReasonsFromStyle = reasons;
}
bool hasStyleDeterminedDirectCompositingReasons() const {
return potentialCompositingReasonsFromStyle() &
CompositingReasonComboAllDirectStyleDeterminedReasons;
}
class AncestorDependentCompositingInputs {
DISALLOW_NEW();
public:
AncestorDependentCompositingInputs() : clippingContainer(nullptr) {}
IntRect clippedAbsoluteBoundingBox;
const LayoutObject* clippingContainer;
};
class RareAncestorDependentCompositingInputs {
public:
RareAncestorDependentCompositingInputs()
: opacityAncestor(nullptr),
transformAncestor(nullptr),
filterAncestor(nullptr),
ancestorScrollingLayer(nullptr),
nearestFixedPositionLayer(nullptr),
scrollParent(nullptr),
clipParent(nullptr) {}
bool isDefault() const {
return !opacityAncestor && !transformAncestor && !filterAncestor &&
!ancestorScrollingLayer && !nearestFixedPositionLayer &&
!scrollParent && !clipParent;
}
const PaintLayer* opacityAncestor;
const PaintLayer* transformAncestor;
const PaintLayer* filterAncestor;
// The fist ancestor which can scroll. This is a subset of the
// ancestorOverflowLayer chain where the scrolling layer is visible and
// has a larger scroll content than its bounds.
const PaintLayer* ancestorScrollingLayer;
const PaintLayer* nearestFixedPositionLayer;
// A scroll parent is a compositor concept. It's only needed in blink
// because we need to use it as a promotion trigger. A layer has a
// scroll parent if neither its compositor scrolling ancestor, nor any
// other layer scrolled by this ancestor, is a stacking ancestor of this
// layer. Layers with scroll parents must be scrolled with the main
// scrolling layer by the compositor.
const PaintLayer* scrollParent;
// A clip parent is another compositor concept that has leaked into
// blink so that it may be used as a promotion trigger. Layers with clip
// parents escape the clip of a stacking tree ancestor. The compositor
// needs to know about clip parents in order to circumvent its normal
// clipping logic.
const PaintLayer* clipParent;
};
void setNeedsCompositingInputsUpdate();
bool childNeedsCompositingInputsUpdate() const {
return m_childNeedsCompositingInputsUpdate;
}
bool needsCompositingInputsUpdate() const {
// While we're updating the compositing inputs, these values may differ.
// We should never be asking for this value when that is the case.
ASSERT(m_needsDescendantDependentCompositingInputsUpdate ==
m_needsAncestorDependentCompositingInputsUpdate);
return m_needsDescendantDependentCompositingInputsUpdate;
}
void updateAncestorOverflowLayer(const PaintLayer* ancestorOverflowLayer) {
m_ancestorOverflowLayer = ancestorOverflowLayer;
}
void updateAncestorDependentCompositingInputs(
const AncestorDependentCompositingInputs&,
const RareAncestorDependentCompositingInputs&,
bool hasAncestorWithClipPath);
void updateDescendantDependentCompositingInputs(
bool hasDescendantWithClipPath,
bool hasNonIsolatedDescendantWithBlendMode,
bool hasRootScrollerAsDescendant);
void didUpdateCompositingInputs();
IntRect clippedAbsoluteBoundingBox() const {
ASSERT(!m_needsAncestorDependentCompositingInputsUpdate);
return m_ancestorDependentCompositingInputs.clippedAbsoluteBoundingBox;
}
const PaintLayer* opacityAncestor() const {
ASSERT(!m_needsAncestorDependentCompositingInputsUpdate);
return m_rareAncestorDependentCompositingInputs
? m_rareAncestorDependentCompositingInputs->opacityAncestor
: nullptr;
}
const PaintLayer* transformAncestor() const {
ASSERT(!m_needsAncestorDependentCompositingInputsUpdate);
return m_rareAncestorDependentCompositingInputs
? m_rareAncestorDependentCompositingInputs->transformAncestor
: nullptr;
}
const PaintLayer* filterAncestor() const {
ASSERT(!m_needsAncestorDependentCompositingInputsUpdate);
return m_rareAncestorDependentCompositingInputs
? m_rareAncestorDependentCompositingInputs->filterAncestor
: nullptr;
}
const LayoutObject* clippingContainer() const {
ASSERT(!m_needsAncestorDependentCompositingInputsUpdate);
return m_ancestorDependentCompositingInputs.clippingContainer;
}
const PaintLayer* ancestorOverflowLayer() const {
return m_ancestorOverflowLayer;
}
const PaintLayer* ancestorScrollingLayer() const {
ASSERT(!m_needsAncestorDependentCompositingInputsUpdate);
return m_rareAncestorDependentCompositingInputs
? m_rareAncestorDependentCompositingInputs
->ancestorScrollingLayer
: nullptr;
}
const PaintLayer* nearestFixedPositionLayer() const {
ASSERT(!m_needsAncestorDependentCompositingInputsUpdate);
return m_rareAncestorDependentCompositingInputs
? m_rareAncestorDependentCompositingInputs
->nearestFixedPositionLayer
: nullptr;
}
const PaintLayer* scrollParent() const {
ASSERT(!m_needsAncestorDependentCompositingInputsUpdate);
return m_rareAncestorDependentCompositingInputs
? m_rareAncestorDependentCompositingInputs->scrollParent
: nullptr;
}
const PaintLayer* clipParent() const {
ASSERT(!m_needsAncestorDependentCompositingInputsUpdate);
return m_rareAncestorDependentCompositingInputs
? m_rareAncestorDependentCompositingInputs->clipParent
: nullptr;
}
bool hasAncestorWithClipPath() const {
ASSERT(!m_needsAncestorDependentCompositingInputsUpdate);
return m_hasAncestorWithClipPath;
}
bool hasDescendantWithClipPath() const {
ASSERT(!m_needsDescendantDependentCompositingInputsUpdate);
return m_hasDescendantWithClipPath;
}
bool hasNonIsolatedDescendantWithBlendMode() const;
bool hasRootScrollerAsDescendant() const {
ASSERT(!m_needsDescendantDependentCompositingInputsUpdate);
return m_hasRootScrollerAsDescendant;
}
bool lostGroupedMapping() const {
ASSERT(isAllowedToQueryCompositingState());
return m_lostGroupedMapping;
}
void setLostGroupedMapping(bool b) { m_lostGroupedMapping = b; }
CompositingReasons getCompositingReasons() const {
ASSERT(isAllowedToQueryCompositingState());
return m_rareData ? m_rareData->compositingReasons : CompositingReasonNone;
}
void setCompositingReasons(CompositingReasons,
CompositingReasons mask = CompositingReasonAll);
SquashingDisallowedReasons getSquashingDisallowedReasons() const {
ASSERT(isAllowedToQueryCompositingState());
return m_rareData ? m_rareData->squashingDisallowedReasons
: SquashingDisallowedReasonsNone;
}
void setSquashingDisallowedReasons(SquashingDisallowedReasons);
bool hasCompositingDescendant() const {
ASSERT(isAllowedToQueryCompositingState());
return m_hasCompositingDescendant;
}
void setHasCompositingDescendant(bool);
bool shouldIsolateCompositedDescendants() const {
ASSERT(isAllowedToQueryCompositingState());
return m_shouldIsolateCompositedDescendants;
}
void setShouldIsolateCompositedDescendants(bool);
void updateDescendantDependentFlags();
void updateOrRemoveFilterEffect();
void updateSelfPaintingLayer();
// This is O(depth) so avoid calling this in loops. Instead use optimizations
// like those in PaintInvalidationState.
PaintLayer* enclosingSelfPaintingLayer();
PaintLayer* enclosingTransformedAncestor() const;
LayoutPoint computeOffsetFromTransformedAncestor() const;
void didUpdateNeedsCompositedScrolling();
bool hasSelfPaintingLayerDescendant() const {
if (m_hasSelfPaintingLayerDescendantDirty)
updateHasSelfPaintingLayerDescendant();
ASSERT(!m_hasSelfPaintingLayerDescendantDirty);
return m_hasSelfPaintingLayerDescendant;
}
LayoutRect paintingExtent(const PaintLayer* rootLayer,
const LayoutSize& subPixelAccumulation,
GlobalPaintFlags);
void appendSingleFragmentIgnoringPagination(
PaintLayerFragments&,
const PaintLayer* rootLayer,
const LayoutRect& dirtyRect,
ClipRectsCacheSlot,
OverlayScrollbarClipBehavior = IgnoreOverlayScrollbarSize,
ShouldRespectOverflowClipType = RespectOverflowClip,
const LayoutPoint* offsetFromRoot = 0,
const LayoutSize& subPixelAccumulation = LayoutSize());
void collectFragments(
PaintLayerFragments&,
const PaintLayer* rootLayer,
const LayoutRect& dirtyRect,
ClipRectsCacheSlot,
OverlayScrollbarClipBehavior = IgnoreOverlayScrollbarSize,
ShouldRespectOverflowClipType = RespectOverflowClip,
const LayoutPoint* offsetFromRoot = 0,
const LayoutSize& subPixelAccumulation = LayoutSize(),
const LayoutRect* layerBoundingBox = 0);
LayoutPoint layoutBoxLocation() const {
return layoutObject()->isBox() ? toLayoutBox(layoutObject())->location()
: LayoutPoint();
}
enum TransparencyClipBoxBehavior {
PaintingTransparencyClipBox,
HitTestingTransparencyClipBox
};
enum TransparencyClipBoxMode {
DescendantsOfTransparencyClipBox,
RootOfTransparencyClipBox
};
static LayoutRect transparencyClipBox(
const PaintLayer*,
const PaintLayer* rootLayer,
TransparencyClipBoxBehavior transparencyBehavior,
TransparencyClipBoxMode transparencyMode,
const LayoutSize& subPixelAccumulation,
GlobalPaintFlags = GlobalPaintNormalPhase);
bool needsRepaint() const { return m_needsRepaint; }
void setNeedsRepaint();
void clearNeedsRepaintRecursively();
// These previousXXX() functions are for subsequence caching. They save the
// painting status of the layer during the previous painting with subsequence.
// A painting without subsequence [1] doesn't change this status. [1] See
// shouldCreateSubsequence() in PaintLayerPainter.cpp for the cases we use
// subsequence when painting a PaintLayer.
IntSize previousScrollOffsetAccumulationForPainting() const {
return m_previousScrollOffsetAccumulationForPainting;
}
void setPreviousScrollOffsetAccumulationForPainting(const IntSize& s) {
m_previousScrollOffsetAccumulationForPainting = s;
}
ClipRects* previousPaintingClipRects() const {
return m_previousPaintingClipRects.get();
}
void setPreviousPaintingClipRects(ClipRects& clipRects) {
m_previousPaintingClipRects = &clipRects;
}
LayoutRect previousPaintDirtyRect() const { return m_previousPaintDirtyRect; }
void setPreviousPaintDirtyRect(const LayoutRect& rect) {
m_previousPaintDirtyRect = rect;
}
PaintLayerPainter::PaintResult previousPaintResult() const {
return static_cast<PaintLayerPainter::PaintResult>(m_previousPaintResult);
}
void setPreviousPaintResult(PaintLayerPainter::PaintResult result) {
m_previousPaintResult = static_cast<unsigned>(result);
ASSERT(m_previousPaintResult == static_cast<unsigned>(result));
}
// Used to skip PaintPhaseDescendantOutlinesOnly for layers that have never
// had descendant outlines. The flag is set during paint invalidation on a
// self painting layer if any contained object has outline. It's cleared
// during painting if PaintPhaseDescendantOutlinesOnly painted nothing.
// For more details, see core/paint/REAME.md#Empty paint phase optimization.
bool needsPaintPhaseDescendantOutlines() const {
return m_needsPaintPhaseDescendantOutlines &&
!m_previousPaintPhaseDescendantOutlinesWasEmpty;
}
void setNeedsPaintPhaseDescendantOutlines() {
DCHECK(isSelfPaintingLayer());
m_needsPaintPhaseDescendantOutlines = true;
m_previousPaintPhaseDescendantOutlinesWasEmpty = false;
}
void setPreviousPaintPhaseDescendantOutlinesEmpty(bool isEmpty) {
m_previousPaintPhaseDescendantOutlinesWasEmpty = isEmpty;
}
// Similar to above, but for PaintPhaseFloat.
bool needsPaintPhaseFloat() const {
return m_needsPaintPhaseFloat && !m_previousPaintPhaseFloatWasEmpty;
}
void setNeedsPaintPhaseFloat() {
DCHECK(isSelfPaintingLayer());
m_needsPaintPhaseFloat = true;
m_previousPaintPhaseFloatWasEmpty = false;
}
void setPreviousPaintPhaseFloatEmpty(bool isEmpty) {
m_previousPaintPhaseFloatWasEmpty = isEmpty;
}
// Similar to above, but for PaintPhaseDescendantBlockBackgroundsOnly.
bool needsPaintPhaseDescendantBlockBackgrounds() const {
return m_needsPaintPhaseDescendantBlockBackgrounds &&
!m_previousPaintPhaseDescendantBlockBackgroundsWasEmpty;
}
void setNeedsPaintPhaseDescendantBlockBackgrounds() {
DCHECK(isSelfPaintingLayer());
m_needsPaintPhaseDescendantBlockBackgrounds = true;
m_previousPaintPhaseDescendantBlockBackgroundsWasEmpty = false;
}
void setPreviousPaintPhaseDescendantBlockBackgroundsEmpty(bool isEmpty) {
m_previousPaintPhaseDescendantBlockBackgroundsWasEmpty = isEmpty;
}
PaintTiming* paintTiming();
ClipRectsCache* clipRectsCache() const { return m_clipRectsCache.get(); }
ClipRectsCache& ensureClipRectsCache() const {
if (!m_clipRectsCache)
m_clipRectsCache = wrapUnique(new ClipRectsCache);
return *m_clipRectsCache;
}
void clearClipRectsCache() const { m_clipRectsCache.reset(); }
void dirty3DTransformedDescendantStatus();
// Both updates the status, and returns true if descendants of this have 3d.
bool update3DTransformedDescendantStatus();
bool has3DTransformedDescendant() const {
DCHECK(!m_is3DTransformedDescendantDirty);
return m_has3DTransformedDescendant;
}
#if CHECK_DISPLAY_ITEM_CLIENT_ALIVENESS
void endShouldKeepAliveAllClientsRecursive();
#endif
private:
// Bounding box in the coordinates of this layer.
LayoutRect logicalBoundingBox() const;
bool hasOverflowControls() const;
void dirtyAncestorChainHasSelfPaintingLayerDescendantStatus();
void updateLayerPositionRecursive();
void updateLayerPositionsAfterScrollRecursive(
const DoubleSize& scrollDelta,
bool paintInvalidationContainerWasScrolled);
void setNextSibling(PaintLayer* next) { m_next = next; }
void setPreviousSibling(PaintLayer* prev) { m_previous = prev; }
void setFirstChild(PaintLayer* first) { m_first = first; }
void setLastChild(PaintLayer* last) { m_last = last; }
void updateHasSelfPaintingLayerDescendant() const;
PaintLayer* hitTestLayer(PaintLayer* rootLayer,
PaintLayer* containerLayer,
HitTestResult&,
const LayoutRect& hitTestRect,
const HitTestLocation&,
bool appliedTransform,
const HitTestingTransformState* = 0,
double* zOffset = 0);
PaintLayer* hitTestLayerByApplyingTransform(
PaintLayer* rootLayer,
PaintLayer* containerLayer,
HitTestResult&,
const LayoutRect& hitTestRect,
const HitTestLocation&,
const HitTestingTransformState* = 0,
double* zOffset = 0,
const LayoutPoint& translationOffset = LayoutPoint());
PaintLayer* hitTestChildren(
ChildrenIteration,
PaintLayer* rootLayer,
HitTestResult&,
const LayoutRect& hitTestRect,
const HitTestLocation&,
const HitTestingTransformState*,
double* zOffsetForDescendants,
double* zOffset,
const HitTestingTransformState* unflattenedTransformState,
bool depthSortDescendants);
PassRefPtr<HitTestingTransformState> createLocalTransformState(
PaintLayer* rootLayer,
PaintLayer* containerLayer,
const LayoutRect& hitTestRect,
const HitTestLocation&,
const HitTestingTransformState* containerTransformState,
const LayoutPoint& translationOffset = LayoutPoint()) const;
bool hitTestContents(HitTestResult&,
const LayoutRect& layerBounds,
const HitTestLocation&,
HitTestFilter) const;
bool hitTestContentsForFragments(const PaintLayerFragments&,
HitTestResult&,
const HitTestLocation&,
HitTestFilter,
bool& insideClipRect) const;
PaintLayer* hitTestTransformedLayerInFragments(
PaintLayer* rootLayer,
PaintLayer* containerLayer,
HitTestResult&,
const LayoutRect& hitTestRect,
const HitTestLocation&,
const HitTestingTransformState*,
double* zOffset,
ClipRectsCacheSlot);
bool hitTestClippedOutByClipPath(PaintLayer* rootLayer,
const HitTestLocation&) const;
bool childBackgroundIsKnownToBeOpaqueInRect(const LayoutRect&) const;
bool shouldBeSelfPaintingLayer() const;
// FIXME: We should only create the stacking node if needed.
bool requiresStackingNode() const { return true; }
void updateStackingNode();
FilterOperations addReflectionToFilterOperations(const ComputedStyle&) const;
FilterEffect* updateFilterEffect() const;
// FIXME: We could lazily allocate our ScrollableArea based on style
// properties ('overflow', ...) but for now, we are always allocating it for
// LayoutBox as it's safer. crbug.com/467721.
bool requiresScrollableArea() const { return layoutBox(); }
void updateScrollableArea();
void dirtyAncestorChainVisibleDescendantStatus();
bool attemptDirectCompositingUpdate(StyleDifference,
const ComputedStyle* oldStyle);
void updateTransform(const ComputedStyle* oldStyle,
const ComputedStyle& newStyle);
void removeAncestorOverflowLayer(const PaintLayer* removedLayer);
void updateOrRemoveFilterClients();
void updatePaginationRecursive(bool needsPaginationUpdate = false);
void clearPaginationRecursive();
void setNeedsRepaintInternal();
void markCompositingContainerChainForNeedsRepaint();
PaintLayerRareData& ensureRareData() {
if (!m_rareData)
m_rareData = wrapUnique(new PaintLayerRareData);
return *m_rareData;
}
void mergeNeedsPaintPhaseFlagsFrom(const PaintLayer& layer) {
m_needsPaintPhaseDescendantOutlines |=
layer.m_needsPaintPhaseDescendantOutlines;
m_needsPaintPhaseFloat |= layer.m_needsPaintPhaseFloat;
m_needsPaintPhaseDescendantBlockBackgrounds |=
layer.m_needsPaintPhaseDescendantBlockBackgrounds;
}
bool isSelfPaintingLayerForIntrinsicOrScrollingReasons() const;
bool shouldFragmentCompositedBounds(const PaintLayer* compositingLayer) const;
// Self-painting layer is an optimization where we avoid the heavy Layer
// painting machinery for a Layer allocated only to handle the overflow clip
// case.
// FIXME(crbug.com/332791): Self-painting layer should be merged into the
// overflow-only concept.
unsigned m_isSelfPaintingLayer : 1;
// If have no self-painting descendants, we don't have to walk our children
// during painting. This can lead to significant savings, especially if the
// tree has lots of non-self-painting layers grouped together (e.g. table
// cells).
mutable unsigned m_hasSelfPaintingLayerDescendant : 1;
mutable unsigned m_hasSelfPaintingLayerDescendantDirty : 1;
const unsigned m_isRootLayer : 1;
unsigned m_isVisibleContentDirty : 1;
unsigned m_hasVisibleContent : 1;
unsigned m_isVisibleDescendantDirty : 1;
unsigned m_hasVisibleDescendant : 1;
#if ENABLE(ASSERT)
unsigned m_needsPositionUpdate : 1;
#endif
unsigned m_is3DTransformedDescendantDirty : 1;
// Set on a stacking context layer that has 3D descendants anywhere
// in a preserves3D hierarchy. Hint to do 3D-aware hit testing.
unsigned m_has3DTransformedDescendant : 1;
unsigned m_containsDirtyOverlayScrollbars : 1;
unsigned m_needsAncestorDependentCompositingInputsUpdate : 1;
unsigned m_needsDescendantDependentCompositingInputsUpdate : 1;
unsigned m_childNeedsCompositingInputsUpdate : 1;
// Used only while determining what layers should be composited. Applies to
// the tree of z-order lists.
unsigned m_hasCompositingDescendant : 1;
// True iff we have scrollable overflow and all children of m_layoutObject are
// known to paint exclusively into their own composited layers. Set by
// updateScrollingStateAfterCompositingChange().
unsigned m_isAllScrollingContentComposited : 1;
// Should be for stacking contexts having unisolated blending descendants.
unsigned m_shouldIsolateCompositedDescendants : 1;
// True if this layout layer just lost its grouped mapping due to the
// CompositedLayerMapping being destroyed, and we don't yet know to what
// graphics layer this Layer will be assigned.
unsigned m_lostGroupedMapping : 1;
unsigned m_needsRepaint : 1;
unsigned m_previousPaintResult : 1; // PaintLayerPainter::PaintResult
unsigned m_needsPaintPhaseDescendantOutlines : 1;
unsigned m_previousPaintPhaseDescendantOutlinesWasEmpty : 1;
unsigned m_needsPaintPhaseFloat : 1;
unsigned m_previousPaintPhaseFloatWasEmpty : 1;
unsigned m_needsPaintPhaseDescendantBlockBackgrounds : 1;
unsigned m_previousPaintPhaseDescendantBlockBackgroundsWasEmpty : 1;
// These bitfields are part of ancestor/descendant dependent compositing
// inputs.
unsigned m_hasDescendantWithClipPath : 1;
unsigned m_hasNonIsolatedDescendantWithBlendMode : 1;
unsigned m_hasAncestorWithClipPath : 1;
unsigned m_hasRootScrollerAsDescendant : 1;
LayoutBoxModelObject* m_layoutObject;
PaintLayer* m_parent;
PaintLayer* m_previous;
PaintLayer* m_next;
PaintLayer* m_first;
PaintLayer* m_last;
// Our (x,y) coordinates are in our parent layer's coordinate space.
LayoutPoint m_location;
// The layer's size.
//
// If the associated LayoutBoxModelObject is a LayoutBox, it's its border
// box. Otherwise, this is the LayoutInline's lines' bounding box.
IntSize m_size;
// Cached normal flow values for absolute positioned elements with static
// left/top values.
LayoutUnit m_staticInlinePosition;
LayoutUnit m_staticBlockPosition;
// The first ancestor having a non visible overflow.
const PaintLayer* m_ancestorOverflowLayer;
AncestorDependentCompositingInputs m_ancestorDependentCompositingInputs;
std::unique_ptr<RareAncestorDependentCompositingInputs>
m_rareAncestorDependentCompositingInputs;
Persistent<PaintLayerScrollableArea> m_scrollableArea;
mutable std::unique_ptr<ClipRectsCache> m_clipRectsCache;
std::unique_ptr<PaintLayerStackingNode> m_stackingNode;
IntSize m_previousScrollOffsetAccumulationForPainting;
RefPtr<ClipRects> m_previousPaintingClipRects;
LayoutRect m_previousPaintDirtyRect;
std::unique_ptr<PaintLayerRareData> m_rareData;
};
} // namespace blink
#ifndef NDEBUG
// Outside the WebCore namespace for ease of invocation from gdb.
CORE_EXPORT void showLayerTree(const blink::PaintLayer*);
CORE_EXPORT void showLayerTree(const blink::LayoutObject*);
#endif
#endif // Layer_h