| /* |
| * 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@dbaron.org> |
| * 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 THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_LAYER_H_ |
| #define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_LAYER_H_ |
| |
| #include <memory> |
| |
| #include "base/auto_reset.h" |
| #include "base/dcheck_is_on.h" |
| #include "base/gtest_prod_util.h" |
| #include "third_party/blink/renderer/core/core_export.h" |
| #include "third_party/blink/renderer/core/layout/geometry/static_position.h" |
| #include "third_party/blink/renderer/core/layout/layout_box.h" |
| #include "third_party/blink/renderer/core/paint/paint_layer_clipper.h" |
| #include "third_party/blink/renderer/core/paint/paint_layer_fragment.h" |
| #include "third_party/blink/renderer/core/paint/paint_layer_resource_info.h" |
| #include "third_party/blink/renderer/core/paint/paint_layer_stacking_node.h" |
| #include "third_party/blink/renderer/core/paint/paint_result.h" |
| #include "third_party/blink/renderer/platform/graphics/overlay_scrollbar_clip_behavior.h" |
| #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" |
| |
| namespace blink { |
| |
| class CompositorFilterOperations; |
| class ComputedStyle; |
| class FilterEffect; |
| class FilterOperations; |
| class HitTestResult; |
| class HitTestingTransformState; |
| class PaintLayerScrollableArea; |
| |
| enum IncludeSelfOrNot { kIncludeSelf, kExcludeSelf }; |
| |
| // Used in PaintLayerPaintOrderIterator. |
| enum PaintLayerIteration { |
| kNegativeZOrderChildren = 1, |
| // Normal flow children are not mandated by CSS 2.1 but are an artifact of |
| // our implementation: we allocate PaintLayers for elements that |
| // are not treated as stacking contexts and thus we need to walk them |
| // during painting and hit-testing. |
| kNormalFlowChildren = 1 << 1, |
| kPositiveZOrderChildren = 1 << 2, |
| |
| kStackedChildren = kNegativeZOrderChildren | kPositiveZOrderChildren, |
| kNormalFlowAndPositiveZOrderChildren = |
| kNormalFlowChildren | kPositiveZOrderChildren, |
| kAllChildren = |
| kNegativeZOrderChildren | kNormalFlowChildren | kPositiveZOrderChildren |
| }; |
| |
| // TODO(crbug.com/332933527): Support anchors-valid. |
| // If the size of this enum changes, make sure to update the bits needed for |
| // `invisible_for_position_visibility_`. |
| enum class LayerPositionVisibility : uint8_t { |
| // anchors-visible, anchor intersection. |
| kAnchorsIntersectionVisible = 1, |
| // anchors-visible, anchor CSS visibility. |
| kAnchorsCssVisible = 1 << 1, |
| // anchors-visible, chained anchor visibility. |
| kChainedAnchorsVisible = 1 << 2, |
| // no-overflow. |
| kNoOverflow = 1 << 3, |
| }; |
| |
| // 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 compartmentalize) 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 |composited_layer_mapping| for hardware |
| // accelerated layers. |
| // |
| // TODO(jchaffraix): Expand the documentation about hardware acceleration. |
| // |
| // |
| // ***** SELF-PAINTING LAYER ***** |
| // One important concept about PaintLayer is "self-painting" |
| // (is_self_painting_layer_). |
| // 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 |
| // BoxFragmentPainter. |
| // 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 GarbageCollected<PaintLayer>, |
| public DisplayItemClient { |
| public: |
| explicit PaintLayer(LayoutBoxModelObject*); |
| PaintLayer(const PaintLayer&) = delete; |
| PaintLayer& operator=(const PaintLayer&) = delete; |
| ~PaintLayer() override; |
| |
| void Destroy(); |
| |
| // DisplayItemClient methods |
| String DebugName() const final; |
| DOMNodeId OwnerNodeId() const final; |
| |
| LayoutBoxModelObject& GetLayoutObject() const { return *layout_object_; } |
| LayoutBox* GetLayoutBox() const { |
| return DynamicTo<LayoutBox>(layout_object_.Get()); |
| } |
| // Returns |GetLayoutBox()| if it exists and has fragments. |
| const LayoutBox* GetLayoutBoxWithBlockFragments() const; |
| |
| PaintLayer* Parent() const { return parent_.Get(); } |
| PaintLayer* PreviousSibling() const { return previous_.Get(); } |
| PaintLayer* NextSibling() const { return next_.Get(); } |
| PaintLayer* FirstChild() const { return first_.Get(); } |
| PaintLayer* LastChild() const { return last_.Get(); } |
| |
| // 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; |
| PaintLayer* AncestorStackingContext() const; |
| |
| void AddChild(PaintLayer* new_child, PaintLayer* before_child = nullptr); |
| void RemoveChild(PaintLayer*); |
| |
| void RemoveOnlyThisLayerAfterStyleChange(const ComputedStyle* old_style); |
| void InsertOnlyThisLayerAfterStyleChange(); |
| |
| void StyleDidChange(StyleDifference, const ComputedStyle* old_style); |
| |
| // FIXME: Many people call this function while it has out-of-date information. |
| bool IsSelfPaintingLayer() const { return is_self_painting_layer_; } |
| |
| bool IsTransparent() const { |
| return GetLayoutObject().StyleRef().HasOpacity() || |
| GetLayoutObject().StyleRef().HasBlendMode() || |
| GetLayoutObject().HasMask(); |
| } |
| |
| const PaintLayer* Root() const { |
| const PaintLayer* curr = this; |
| while (curr->Parent()) |
| curr = curr->Parent(); |
| return curr; |
| } |
| |
| // This is the scroll offset that's actually used to display to the screen. |
| // It should only be used in paint/compositing type use cases (includes hit |
| // testing, intersection observer). Most other cases should use the unsnapped |
| // offset from LayoutBox (for layout) or the source offset from the |
| // ScrollableArea. |
| gfx::Vector2d PixelSnappedScrolledContentOffset() const; |
| |
| bool IsRootLayer() const { return is_root_layer_; } |
| |
| void UpdateScrollingAfterLayout(); |
| |
| void UpdateTransform(); |
| |
| bool HasVisibleContent() const { |
| DCHECK(!needs_descendant_dependent_flags_update_); |
| return has_visible_content_; |
| } |
| |
| bool HasVisibleSelfPaintingDescendant() const { |
| DCHECK(!needs_descendant_dependent_flags_update_); |
| return has_visible_self_painting_descendant_; |
| } |
| |
| void DirtyVisibleContentStatus(); |
| |
| // Gets the ancestor layer that serves as the containing block (in the sense |
| // of LayoutObject::container() instead of LayoutObject::containingBlock()) |
| // of this layer. Normally the parent layer is the containing layer, except |
| // for out of flow positioned, floating and multicol spanner layers whose |
| // containing layer might be an ancestor of the parent layer. |
| PaintLayer* ContainingLayer() const; |
| |
| // The hitTest() method looks for mouse events by walking layers that |
| // intersect the point from front to back. |
| // |hit_test_area| is the rect in the space of this PaintLayer's |
| // LayoutObject to consider for hit testing. |
| bool HitTest(const HitTestLocation& location, |
| HitTestResult&, |
| const PhysicalRect& hit_test_area); |
| |
| // Static position is set in parent's coordinate space. |
| LayoutUnit StaticBlockPosition() const { return static_block_position_; } |
| |
| void SetStaticBlockPosition(LayoutUnit position) { |
| static_block_position_ = position; |
| } |
| |
| using InlineEdge = LogicalStaticPosition::InlineEdge; |
| using BlockEdge = LogicalStaticPosition::BlockEdge; |
| InlineEdge StaticInlineEdge() const { |
| return static_cast<InlineEdge>(static_inline_edge_); |
| } |
| BlockEdge StaticBlockEdge() const { |
| return static_cast<BlockEdge>(static_block_edge_); |
| } |
| |
| void SetStaticPositionFromNG(const LogicalStaticPosition& position) { |
| static_inline_position_ = position.offset.inline_offset; |
| static_block_position_ = position.offset.block_offset; |
| static_inline_edge_ = position.inline_edge; |
| static_block_edge_ = position.block_edge; |
| } |
| |
| LogicalStaticPosition GetStaticPosition() const { |
| LogicalStaticPosition position; |
| position.offset.inline_offset = static_inline_position_; |
| position.offset.block_offset = static_block_position_; |
| position.inline_edge = StaticInlineEdge(); |
| position.block_edge = StaticBlockEdge(); |
| return position; |
| } |
| |
| // Note that this transform has the transform-origin baked in. Due to this |
| // fact, this transform is pretty useless if we're fragmented, since each |
| // fragment has its own origin. Avoid calling this method if a box is |
| // fragmented. Ideally, we should have a DCHECK for being non-fragmented here, |
| // but that's going to fail currently. LayoutBox::MapVisualRectToContainer() |
| // calls this function without any checks, for instance. |
| gfx::Transform* Transform() const { return transform_.get(); } |
| |
| // Returns *Transform(), or identity matrix if Transform() is nullptr. |
| gfx::Transform CurrentTransform() const; |
| |
| bool Preserves3D() const { return GetLayoutObject().Preserves3D(); } |
| bool Has3DTransform() const { |
| return transform_ && !transform_->Is2dTransform(); |
| } |
| |
| // 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 GetLayoutObject().HasFilterInducingProperty(); |
| } |
| |
| bool SupportsSubsequenceCaching() const; |
| |
| // If the input CompositorFilterOperation is not empty, it will be populated |
| // only if |filter_on_effect_node_dirty_| is true or the reference box has |
| // changed. Otherwise it will be populated unconditionally. |
| // |filter_on_effect_node_dirty_| will be cleared. |
| void UpdateCompositorFilterOperationsForFilter( |
| CompositorFilterOperations& operations); |
| void SetFilterOnEffectNodeDirty() { filter_on_effect_node_dirty_ = true; } |
| |
| // |backdrop_filter_bounds| represents the clipping bounds for the filtered |
| // backdrop image only. This rect lives in the local transform space of the |
| // containing EffectPaintPropertyNode. If the input CompositorFilterOperation |
| // is not empty, it will be populated only if |
| // |backdrop_filter_on_effect_node_dirty_| is true or the reference box has |
| // changed. Otherwise it will be populated unconditionally. |
| // |backdrop_filter_on_effect_node_dirty_| will be cleared. |
| void UpdateCompositorFilterOperationsForBackdropFilter( |
| CompositorFilterOperations& operations, |
| gfx::RRectF& backdrop_filter_bounds); |
| void SetBackdropFilterOnEffectNodeDirty() { |
| backdrop_filter_on_effect_node_dirty_ = true; |
| } |
| |
| void SetIsUnderSVGHiddenContainer(bool value) { |
| is_under_svg_hidden_container_ = value; |
| } |
| bool IsUnderSVGHiddenContainer() const { |
| return is_under_svg_hidden_container_; |
| } |
| |
| bool PaintsWithFilters() const; |
| |
| // Maps "forward" to determine which pixels in a destination rect are |
| // affected by pixels in the source rect. |
| // See also FilterEffect::mapRect. |
| gfx::RectF MapRectForFilter(const gfx::RectF&) const; |
| |
| // Calls the above, rounding outwards. |
| PhysicalRect MapRectForFilter(const PhysicalRect&) const; |
| |
| bool HasFilterThatMovesPixels() const { |
| return has_filter_that_moves_pixels_; |
| } |
| |
| PaintLayerResourceInfo* ResourceInfo() const { return resource_info_.Get(); } |
| PaintLayerResourceInfo& EnsureResourceInfo(); |
| |
| // Filter reference box is the area over which the filter is computed, in the |
| // local coordinate system of the effect node containing the filter. |
| gfx::RectF FilterReferenceBox() const; |
| gfx::RectF BackdropFilterReferenceBox() const; |
| gfx::RRectF BackdropFilterBounds() const; |
| |
| void UpdateFilterReferenceBox(); |
| void UpdateFilters(const ComputedStyle* old_style, |
| const ComputedStyle& new_style); |
| void UpdateBackdropFilters(const ComputedStyle* old_style, |
| const ComputedStyle& new_style); |
| void UpdateClipPath(const ComputedStyle* old_style, |
| const ComputedStyle& new_style); |
| void UpdateOffsetPath(const ComputedStyle* old_style, |
| const ComputedStyle& new_style); |
| |
| Node* EnclosingNode() const; |
| |
| bool IsInTopOrViewTransitionLayer() const; |
| |
| // FIXME: This should probably return a ScrollableArea but a lot of internal |
| // methods are mistakenly exposed. |
| PaintLayerScrollableArea* GetScrollableArea() const { |
| return scrollable_area_.Get(); |
| } |
| |
| PaintLayerClipper Clipper() const; |
| |
| bool ScrollsOverflow() const; |
| |
| bool NeedsVisualOverflowRecalc() const { |
| return needs_visual_overflow_recalc_; |
| } |
| void SetNeedsVisualOverflowRecalc(); |
| void SetNeedsCompositingInputsUpdate(); |
| void ScrollContainerStatusChanged(); |
| |
| // Returns the nearest ancestor layer (in containing block hierarchy, |
| // not including this layer) that is a scroll container. It's nullptr for |
| // the root layer. If not null, the value of |is_fixed_to_view| will be set to |
| // true if the result of this function is the root layer and the current layer |
| // is fixed to the view due to fixed-position ancestors. |
| const PaintLayer* ContainingScrollContainerLayer( |
| bool* is_fixed_to_view = nullptr) const; |
| |
| bool HasFixedPositionDescendant() const { |
| DCHECK(!needs_descendant_dependent_flags_update_); |
| return has_fixed_position_descendant_; |
| } |
| bool HasNonContainedAbsolutePositionDescendant() const { |
| DCHECK(!needs_descendant_dependent_flags_update_); |
| return has_non_contained_absolute_position_descendant_; |
| } |
| bool HasSelfPaintingLayerDescendant() const { |
| DCHECK(!needs_descendant_dependent_flags_update_); |
| return has_self_painting_layer_descendant_; |
| } |
| |
| // See |
| // PaintLayerStackingNode::layer_to_overlay_overflow_controls_painting_after_. |
| bool NeedsReorderOverlayOverflowControls() const { |
| return needs_reorder_overlay_overflow_controls_; |
| } |
| |
| // Returns true if there is a descendant with blend-mode that is |
| // not contained within another enclosing stacking context other |
| // than the stacking context blend-mode creates, or the stacking |
| // context this PaintLayer might create. This is needed because |
| // blend-mode content needs to blend with the containing stacking |
| // context's painted output, but not the content in any grandparent |
| // stacking contexts. |
| bool HasNonIsolatedDescendantWithBlendMode() const; |
| |
| CompositingReasons GetCompositingReasons() const { |
| // TODO(pdr): Remove this. |
| return CompositingReason::kNone; |
| } |
| |
| void UpdateDescendantDependentFlags(); |
| |
| void UpdateSelfPaintingLayer(); |
| // This is O(depth) so avoid calling this in loops. Instead use optimizations |
| // like those in PaintInvalidatorContext. |
| PaintLayer* EnclosingSelfPaintingLayer(); |
| |
| void DidUpdateScrollsOverflow(); |
| |
| bool SelfNeedsRepaint() const { return self_needs_repaint_; } |
| bool DescendantNeedsRepaint() const { return descendant_needs_repaint_; } |
| bool SelfOrDescendantNeedsRepaint() const { |
| return self_needs_repaint_ || descendant_needs_repaint_; |
| } |
| void SetNeedsRepaint(); |
| void SetDescendantNeedsRepaint(); |
| void ClearNeedsRepaintRecursively(); |
| |
| bool NeedsCullRectUpdate() const { return needs_cull_rect_update_; } |
| bool ForcesChildrenCullRectUpdate() const { |
| return forces_children_cull_rect_update_; |
| } |
| bool DescendantNeedsCullRectUpdate() const { |
| return descendant_needs_cull_rect_update_; |
| } |
| bool SelfOrDescendantNeedsCullRectUpdate() const { |
| return needs_cull_rect_update_ || descendant_needs_cull_rect_update_; |
| } |
| void SetNeedsCullRectUpdate(); |
| void SetForcesChildrenCullRectUpdate(); |
| void SetDescendantNeedsCullRectUpdate(); |
| void ClearNeedsCullRectUpdate() { |
| needs_cull_rect_update_ = false; |
| forces_children_cull_rect_update_ = false; |
| descendant_needs_cull_rect_update_ = false; |
| } |
| |
| // The paint result of this layer during the previous painting with |
| // subsequence. A painting without subsequence [1] doesn't change this flag. |
| // [1] See ShouldCreateSubsequence() in paint_layer_painter.cc for the cases |
| // we use subsequence when painting a PaintLayer. |
| PaintResult PreviousPaintResult() const { |
| return static_cast<PaintResult>(previous_paint_result_); |
| } |
| void SetPreviousPaintResult(PaintResult 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. |
| // For more details, see core/paint/REAME.md#Empty paint phase optimization. |
| bool NeedsPaintPhaseDescendantOutlines() const { |
| return needs_paint_phase_descendant_outlines_; |
| } |
| void SetNeedsPaintPhaseDescendantOutlines() { |
| DCHECK(IsSelfPaintingLayer()); |
| needs_paint_phase_descendant_outlines_ = true; |
| } |
| |
| // Similar to above, but for PaintPhaseFloat. |
| bool NeedsPaintPhaseFloat() const { return needs_paint_phase_float_; } |
| void SetNeedsPaintPhaseFloat() { |
| DCHECK(IsSelfPaintingLayer()); |
| needs_paint_phase_float_ = true; |
| } |
| |
| bool Has3DTransformedDescendant() const { |
| DCHECK(!needs_descendant_dependent_flags_update_); |
| return has3d_transformed_descendant_; |
| } |
| |
| // See |
| // https://chromium.googlesource.com/chromium/src.git/+/main/third_party/blink/renderer/core/paint/README.md |
| // for the definition of a replaced normal-flow stacking element. |
| bool IsReplacedNormalFlowStacking() const; |
| |
| #if DCHECK_IS_ON() |
| bool IsInStackingParentZOrderLists() const; |
| bool LayerListMutationAllowed() const { return layer_list_mutation_allowed_; } |
| #endif |
| |
| void DirtyStackingContextZOrderLists(); |
| |
| bool KnownToClipSubtreeToPaddingBox() const; |
| |
| void Trace(Visitor*) const override; |
| |
| PhysicalRect LocalBoundingBoxIncludingSelfPaintingDescendants() const; |
| |
| // If `invisible` is true, the whole subtree will be omitted in painting and |
| // hit-testing. The invisible status of each LayerPositionVisibility value is |
| // tracked separately. |
| void SetInvisibleForPositionVisibility(LayerPositionVisibility visibility, |
| bool invisible); |
| // Returns true if any bit of the flag is set. |
| bool InvisibleForPositionVisibility() const { |
| return invisible_for_position_visibility_; |
| } |
| bool HasAncestorInvisibleForPositionVisibility() const; |
| |
| private: |
| void Update3DTransformedDescendantStatus(); |
| |
| // Bounding box in the coordinates of this layer. |
| PhysicalRect LocalBoundingBox() const; |
| |
| void SetNextSibling(PaintLayer* next) { next_ = next; } |
| void SetPreviousSibling(PaintLayer* prev) { previous_ = prev; } |
| void SetFirstChild(PaintLayer* first) { first_ = first; } |
| void SetLastChild(PaintLayer* last) { last_ = last; } |
| |
| void UpdateHasSelfPaintingLayerDescendant() const; |
| |
| void AppendSingleFragmentForHitTesting( |
| PaintLayerFragments&, |
| const PaintLayerFragment* container_fragment, |
| ShouldRespectOverflowClipType) const; |
| |
| void CollectFragments(PaintLayerFragments&, |
| const PaintLayer* root_layer, |
| ShouldRespectOverflowClipType = kRespectOverflowClip, |
| const FragmentData* root_fragment = nullptr) const; |
| |
| struct HitTestRecursionData { |
| STACK_ALLOCATED(); |
| |
| public: |
| const PhysicalRect& rect; |
| // Whether location.Intersects(rect) returns true. |
| const HitTestLocation& location; |
| const HitTestLocation& original_location; |
| const bool intersects_location; |
| HitTestRecursionData(const PhysicalRect& rect_arg, |
| const HitTestLocation& location_arg, |
| const HitTestLocation& original_location_arg); |
| }; |
| |
| PaintLayer* HitTestLayer(const PaintLayer& transform_container, |
| const PaintLayerFragment* container_fragment, |
| HitTestResult&, |
| const HitTestRecursionData& recursion_data, |
| bool applied_transform = false, |
| HitTestingTransformState* = nullptr, |
| double* z_offset = nullptr, |
| bool overflow_controls_only = false); |
| PaintLayer* HitTestLayerByApplyingTransform( |
| const PaintLayer& transform_container, |
| const PaintLayerFragment* container_fragment, |
| const PaintLayerFragment& local_fragment, |
| HitTestResult&, |
| const HitTestRecursionData& recursion_data, |
| HitTestingTransformState*, |
| double* z_offset, |
| bool overflow_controls_only, |
| const PhysicalOffset& translation_offset = PhysicalOffset()); |
| PaintLayer* HitTestChildren( |
| PaintLayerIteration, |
| const PaintLayer& transform_container, |
| const PaintLayerFragment* container_fragment, |
| HitTestResult&, |
| const HitTestRecursionData& recursion_data, |
| HitTestingTransformState* container_transform_state, |
| double* z_offset_for_descendants, |
| double* z_offset, |
| HitTestingTransformState* local_transform_state, |
| bool depth_sort_descendants); |
| |
| HitTestingTransformState CreateLocalTransformState( |
| const PaintLayer& transform_container, |
| const FragmentData& container_fragment, |
| const FragmentData& local_fragment, |
| const HitTestRecursionData& recursion_data, |
| const HitTestingTransformState* root_transform_state) const; |
| |
| bool HitTestFragmentWithPhase(HitTestResult&, |
| const PhysicalBoxFragment*, |
| const PhysicalOffset& fragment_offset, |
| const HitTestLocation&, |
| HitTestPhase phase) const; |
| bool HitTestFragmentsWithPhase(const PaintLayerFragments&, |
| HitTestResult&, |
| const HitTestLocation&, |
| HitTestPhase, |
| bool& inside_clip_rect) const; |
| bool HitTestForegroundForFragments(const PaintLayerFragments&, |
| HitTestResult&, |
| const HitTestLocation&, |
| bool& inside_clip_rect) const; |
| PaintLayer* HitTestTransformedLayerInFragments( |
| const PaintLayer& transform_container, |
| const PaintLayerFragment* container_fragment, |
| HitTestResult&, |
| const HitTestRecursionData&, |
| HitTestingTransformState*, |
| double* z_offset, |
| bool overflow_controls_only, |
| ShouldRespectOverflowClipType); |
| bool HitTestClippedOutByClipPath(const PaintLayer& root_layer, |
| const HitTestLocation&) const; |
| |
| bool ShouldBeSelfPaintingLayer() const; |
| |
| void UpdateStackingNode(); |
| |
| FilterOperations FilterOperationsIncludingReflection() const; |
| |
| bool RequiresScrollableArea() const; |
| void UpdateScrollableArea(); |
| |
| // Indicates whether the descendant-dependent tree walk bit should also |
| // be set. |
| enum DescendantDependentFlagsUpdateFlag { |
| kNeedsDescendantDependentUpdate, |
| kDoesNotNeedDescendantDependentUpdate |
| }; |
| |
| // Marks the ancestor chain for paint property update, and if |
| // the flag is set, the descendant-dependent tree walk as well. |
| void MarkAncestorChainForFlagsUpdate( |
| DescendantDependentFlagsUpdateFlag = kNeedsDescendantDependentUpdate); |
| |
| void SetNeedsDescendantDependentFlagsUpdate(); |
| |
| void UpdateTransformAfterStyleChange(const ComputedStyle* old_style, |
| const ComputedStyle& new_style); |
| |
| void MarkCompositingContainerChainForNeedsRepaint(); |
| |
| void MergeNeedsPaintPhaseFlagsFrom(const PaintLayer& layer) { |
| needs_paint_phase_descendant_outlines_ |= |
| layer.needs_paint_phase_descendant_outlines_; |
| needs_paint_phase_float_ |= layer.needs_paint_phase_float_; |
| } |
| |
| void ExpandRectForSelfPaintingDescendants(PhysicalRect& result) const; |
| |
| // This is private because PaintLayerStackingNode is only for PaintLayer and |
| // PaintLayerPaintOrderIterator. |
| PaintLayerStackingNode* StackingNode() const { return stacking_node_.Get(); } |
| |
| void SetNeedsReorderOverlayOverflowControls(bool); |
| |
| bool ComputeHasFilterThatMovesPixels() 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 is_self_painting_layer_ : 1; |
| |
| const unsigned is_root_layer_ : 1; |
| |
| unsigned has_visible_content_ : 1; |
| |
| unsigned needs_descendant_dependent_flags_update_ : 1; |
| unsigned needs_visual_overflow_recalc_ : 1; |
| |
| unsigned has_visible_self_painting_descendant_ : 1; |
| |
| // Set on a stacking context layer that has 3D descendants anywhere |
| // in a preserves3D hierarchy. Hint to do 3D-aware hit testing. |
| unsigned has3d_transformed_descendant_ : 1; |
| |
| unsigned self_needs_repaint_ : 1; |
| unsigned descendant_needs_repaint_ : 1; |
| |
| unsigned needs_cull_rect_update_ : 1; |
| unsigned forces_children_cull_rect_update_ : 1; |
| // True if any descendant needs cull rect update. |
| unsigned descendant_needs_cull_rect_update_ : 1; |
| |
| unsigned previous_paint_result_ : 1; // PaintResult |
| static_assert(kMaxPaintResult < 2, |
| "Should update number of bits of previous_paint_result_"); |
| |
| unsigned needs_paint_phase_descendant_outlines_ : 1; |
| unsigned needs_paint_phase_float_ : 1; |
| |
| // These bitfields are part of ancestor/descendant dependent compositing |
| // inputs. |
| unsigned has_non_isolated_descendant_with_blend_mode_ : 1; |
| unsigned has_fixed_position_descendant_ : 1; |
| unsigned has_non_contained_absolute_position_descendant_ : 1; |
| unsigned has_stacked_descendant_in_current_stacking_context_ : 1; |
| |
| // These are set to true when filter style or filter resource changes, |
| // indicating that we need to update the filter (or backdrop_filter) field of |
| // the effect paint property node. They are cleared when the effect paint |
| // property node is updated. |
| unsigned filter_on_effect_node_dirty_ : 1; |
| unsigned backdrop_filter_on_effect_node_dirty_ : 1; |
| |
| // Caches |ComputeHasFilterThatMovesPixels()|, updated on style changes. |
| unsigned has_filter_that_moves_pixels_ : 1; |
| |
| // True if the current subtree is underneath a LayoutSVGHiddenContainer |
| // ancestor. |
| unsigned is_under_svg_hidden_container_ : 1; |
| |
| unsigned has_self_painting_layer_descendant_ : 1; |
| |
| unsigned needs_reorder_overlay_overflow_controls_ : 1; |
| unsigned static_inline_edge_ : 2; |
| unsigned static_block_edge_ : 2; |
| |
| unsigned invisible_for_position_visibility_ : 4 = 0; |
| unsigned descendant_needs_check_position_visibility_ : 1 = false; |
| |
| #if DCHECK_IS_ON() |
| mutable unsigned layer_list_mutation_allowed_ : 1; |
| bool is_destroyed_ = false; |
| #endif |
| |
| const Member<LayoutBoxModelObject> layout_object_; |
| |
| Member<PaintLayer> parent_; |
| Member<PaintLayer> previous_; |
| Member<PaintLayer> next_; |
| Member<PaintLayer> first_; |
| Member<PaintLayer> last_; |
| |
| Member<PaintLayerScrollableArea> scrollable_area_; |
| Member<PaintLayerStackingNode> stacking_node_; |
| Member<PaintLayerResourceInfo> resource_info_; |
| |
| // Cached normal flow values for absolute positioned elements with static |
| // left/top values. |
| LayoutUnit static_inline_position_; |
| LayoutUnit static_block_position_; |
| |
| std::unique_ptr<gfx::Transform> transform_; |
| |
| // For layer_list_mutation_allowed_. |
| friend class PaintLayerListMutationDetector; |
| |
| // For stacking_node_ to avoid exposing it publicly. |
| friend class PaintLayerPaintOrderIterator; |
| friend class PaintLayerPaintOrderReverseIterator; |
| friend class PaintLayerStackingNode; |
| friend class CheckAncestorPositionVisibilityScope; |
| |
| FRIEND_TEST_ALL_PREFIXES(PaintLayerTest, |
| DescendantDependentFlagsStopsAtThrottledFrames); |
| FRIEND_TEST_ALL_PREFIXES(PaintLayerTest, |
| PaintLayerTransformUpdatedOnStyleTransformAnimation); |
| FRIEND_TEST_ALL_PREFIXES( |
| PaintLayerOverlapTest, |
| FixedUnderTransformDoesNotExpandBoundingBoxForOverlap); |
| FRIEND_TEST_ALL_PREFIXES(PaintLayerOverlapTest, |
| FixedUsesExpandedBoundingBoxForOverlap); |
| FRIEND_TEST_ALL_PREFIXES(PaintLayerOverlapTest, |
| FixedInScrollerUsesExpandedBoundingBoxForOverlap); |
| FRIEND_TEST_ALL_PREFIXES(PaintLayerOverlapTest, |
| NestedFixedUsesExpandedBoundingBoxForOverlap); |
| FRIEND_TEST_ALL_PREFIXES(PaintLayerOverlapTest, |
| FixedWithExpandedBoundsForChild); |
| FRIEND_TEST_ALL_PREFIXES(PaintLayerOverlapTest, |
| FixedWithClippedExpandedBoundsForChild); |
| FRIEND_TEST_ALL_PREFIXES(PaintLayerOverlapTest, |
| FixedWithExpandedBoundsForGrandChild); |
| FRIEND_TEST_ALL_PREFIXES(PaintLayerOverlapTest, |
| FixedWithExpandedBoundsForFixedChild); |
| }; |
| |
| // This scope should be instantiated for each stacking context during hit-test |
| // and paint. In rare cases, a non-stacking-context PaintLayer containing |
| // self-painting descendants sets descendant_needs_check_position_visibility_ |
| // to true on the containing stacking context to let descendants (not across |
| // descendant stacking contexts) check if they need to hide due to the position |
| // visibility hidden flag on that layer. |
| class CheckAncestorPositionVisibilityScope { |
| STACK_ALLOCATED(); |
| |
| public: |
| explicit CheckAncestorPositionVisibilityScope( |
| const PaintLayer& stacking_context) |
| : reset_(&should_check_, |
| stacking_context.descendant_needs_check_position_visibility_) { |
| CHECK(stacking_context.GetLayoutObject().IsStackingContext()); |
| } |
| |
| static bool ShouldCheck() { return should_check_; } |
| |
| private: |
| static bool should_check_; |
| base::AutoReset<bool> reset_; |
| }; |
| |
| #if DCHECK_IS_ON() |
| class PaintLayerListMutationDetector { |
| STACK_ALLOCATED(); |
| |
| public: |
| explicit PaintLayerListMutationDetector(const PaintLayer* layer) |
| : layer_(layer), |
| previous_mutation_allowed_state_(layer->layer_list_mutation_allowed_) { |
| layer->layer_list_mutation_allowed_ = false; |
| } |
| |
| ~PaintLayerListMutationDetector() { |
| layer_->layer_list_mutation_allowed_ = previous_mutation_allowed_state_; |
| } |
| |
| private: |
| const PaintLayer* layer_; |
| bool previous_mutation_allowed_state_; |
| }; |
| #endif |
| |
| } // namespace blink |
| |
| #if DCHECK_IS_ON() |
| // Outside the blink namespace for ease of invocation from gdb. |
| CORE_EXPORT void ShowLayerTree(const blink::PaintLayer*); |
| CORE_EXPORT void ShowLayerTree(const blink::LayoutObject*); |
| #endif |
| |
| namespace cppgc { |
| // Assign PaintLayer to be allocated on custom LayoutObjectSpace. |
| template <typename T> |
| struct SpaceTrait< |
| T, |
| std::enable_if_t<std::is_base_of<blink::PaintLayer, T>::value>> { |
| using Space = blink::LayoutObjectSpace; |
| }; |
| } // namespace cppgc |
| |
| #endif // THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_LAYER_H_ |