| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "third_party/blink/renderer/core/paint/object_paint_invalidator.h" |
| |
| #include "third_party/blink/renderer/core/frame/local_frame.h" |
| #include "third_party/blink/renderer/core/frame/local_frame_view.h" |
| #include "third_party/blink/renderer/core/layout/layout_embedded_content.h" |
| #include "third_party/blink/renderer/core/layout/layout_view.h" |
| #include "third_party/blink/renderer/core/page/chrome_client.h" |
| #include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h" |
| #include "third_party/blink/renderer/core/paint/paint_invalidator.h" |
| #include "third_party/blink/renderer/core/paint/paint_layer.h" |
| #include "third_party/blink/renderer/platform/graphics/graphics_layer.h" |
| |
| namespace blink { |
| |
| template <typename Functor> |
| static void TraverseNonCompositingDescendantsInPaintOrder(const LayoutObject&, |
| const Functor&); |
| |
| static bool MayBeSkippedContainerForFloating(const LayoutObject& object) { |
| return !object.IsInLayoutNGInlineFormattingContext() && |
| !object.IsLayoutBlock(); |
| } |
| |
| template <typename Functor> |
| static void |
| TraverseNonCompositingDescendantsBelongingToAncestorPaintInvalidationContainer( |
| const LayoutObject& object, |
| const Functor& functor) { |
| // |object| is a paint invalidation container, but is not a stacking context |
| // (legacy layout only: or is a non-block), so the paint invalidation |
| // container of stacked descendants may not belong to |object| but belong to |
| // an ancestor. This function traverses all such descendants. See (legacy |
| // layout only: Case 1a and) Case 2 below for details. |
| DCHECK(object.IsPaintInvalidationContainer() && |
| (!object.IsStackingContext() || |
| MayBeSkippedContainerForFloating(object))); |
| |
| LayoutObject* descendant = object.NextInPreOrder(&object); |
| while (descendant) { |
| if (!descendant->HasLayer() || !descendant->IsStacked()) { |
| // Case 1: The descendant is not stacked (or is stacked but has not been |
| // allocated a layer yet during style change), so either it's a paint |
| // invalidation container in the same situation as |object|, or its paint |
| // invalidation container is in such situation. Keep searching until a |
| // stacked layer is found. |
| if (MayBeSkippedContainerForFloating(object) && |
| descendant->IsFloating()) { |
| // The following is for legacy layout only because LayoutNG allows an |
| // inline to contain floats. |
| // Case 1a (rare): However, if the descendant is a floating object below |
| // a composited non-block object, the subtree may belong to an ancestor |
| // in paint order, thus recur into the subtree. Note that for |
| // performance, we don't check whether the floating object's container |
| // is above or under |object|, so we may traverse more than expected. |
| // Example: |
| // <span id="object" class="position: relative; will-change: transform"> |
| // <div id="descendant" class="float: left"></div>" |
| // </span> |
| TraverseNonCompositingDescendantsInPaintOrder(*descendant, functor); |
| descendant = descendant->NextInPreOrderAfterChildren(&object); |
| } else { |
| descendant = descendant->NextInPreOrder(&object); |
| } |
| } else if (!descendant->IsPaintInvalidationContainer()) { |
| // Case 2: The descendant is stacked and is not composited. |
| // The invalidation container of its subtree is our ancestor, |
| // thus recur into the subtree. |
| TraverseNonCompositingDescendantsInPaintOrder(*descendant, functor); |
| descendant = descendant->NextInPreOrderAfterChildren(&object); |
| } else if (descendant->IsStackingContext() && |
| !MayBeSkippedContainerForFloating(*descendant)) { |
| // Case 3: The descendant is an invalidation container and is a stacking |
| // context. No objects in the subtree can have invalidation container |
| // outside of it, thus skip the whole subtree. |
| // Legacy layout only: This excludes non-block because there might be |
| // floating objects under the descendant belonging to some ancestor in |
| // paint order (Case 1a). |
| descendant = descendant->NextInPreOrderAfterChildren(&object); |
| } else { |
| // Case 4: The descendant is an invalidation container but not a stacking |
| // context, or the descendant is a non-block stacking context. |
| // This is the same situation as |object|, thus keep searching. |
| descendant = descendant->NextInPreOrder(&object); |
| } |
| } |
| } |
| |
| template <typename Functor> |
| static void TraverseNonCompositingDescendantsInPaintOrder( |
| const LayoutObject& object, |
| const Functor& functor) { |
| functor(object); |
| LayoutObject* descendant = object.NextInPreOrder(&object); |
| while (descendant) { |
| if (!descendant->IsPaintInvalidationContainer()) { |
| functor(*descendant); |
| descendant = descendant->NextInPreOrder(&object); |
| } else if (descendant->IsStackingContext() && |
| !MayBeSkippedContainerForFloating(*descendant)) { |
| // The descendant is an invalidation container and is a stacking context. |
| // No objects in the subtree can have invalidation container outside of |
| // it, thus skip the whole subtree. |
| // Legacy layout only: This excludes non-blocks because there might be |
| // floating objects under the descendant belonging to some ancestor in |
| // paint order (Case 1a). |
| descendant = descendant->NextInPreOrderAfterChildren(&object); |
| } else { |
| // If a paint invalidation container is not a stacking context, or the |
| // descendant is a non-block stacking context, some of its descendants may |
| // belong to the parent container. |
| TraverseNonCompositingDescendantsBelongingToAncestorPaintInvalidationContainer( |
| *descendant, functor); |
| descendant = descendant->NextInPreOrderAfterChildren(&object); |
| } |
| } |
| } |
| |
| static void SetPaintingLayerNeedsRepaintDuringTraverse( |
| const LayoutObject& object) { |
| if (object.HasLayer() && |
| To<LayoutBoxModelObject>(object).HasSelfPaintingLayer()) { |
| To<LayoutBoxModelObject>(object).Layer()->SetNeedsRepaint(); |
| } else if (object.IsFloating() && object.Parent() && |
| MayBeSkippedContainerForFloating(*object.Parent())) { |
| // The following is for legacy layout only because LayoutNG allows an |
| // inline to contain floats. |
| object.PaintingLayer()->SetNeedsRepaint(); |
| } |
| } |
| |
| void ObjectPaintInvalidator:: |
| InvalidatePaintIncludingNonCompositingDescendants() { |
| DCHECK(!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()); |
| SlowSetPaintingLayerNeedsRepaint(); |
| // This method may be used to invalidate paint of objects changing paint |
| // invalidation container. |
| // TODO(vmpstr): After paint containment isolation is in place, we might not |
| // have to recurse past the paint containment boundary. |
| TraverseNonCompositingDescendantsInPaintOrder( |
| object_, [](const LayoutObject& object) { |
| SetPaintingLayerNeedsRepaintDuringTraverse(object); |
| }); |
| } |
| |
| #if DCHECK_IS_ON() |
| void ObjectPaintInvalidator::CheckPaintLayerNeedsRepaint() { |
| DCHECK(!object_.PaintingLayer() || |
| object_.PaintingLayer()->SelfNeedsRepaint()); |
| } |
| #endif |
| |
| void ObjectPaintInvalidator::SlowSetPaintingLayerNeedsRepaint() { |
| if (PaintLayer* painting_layer = object_.PaintingLayer()) |
| painting_layer->SetNeedsRepaint(); |
| } |
| |
| DISABLE_CFI_PERF |
| PaintInvalidationReason |
| ObjectPaintInvalidatorWithContext::ComputePaintInvalidationReason() { |
| // This is before any early return to ensure the previous visibility status is |
| // saved. |
| bool previous_visibility_visible = object_.PreviousVisibilityVisible(); |
| object_.GetMutableForPainting().UpdatePreviousVisibilityVisible(); |
| if (object_.VisualRectRespectsVisibility() && !previous_visibility_visible && |
| object_.StyleRef().Visibility() != EVisibility::kVisible) |
| return PaintInvalidationReason::kNone; |
| |
| if (!object_.ShouldCheckForPaintInvalidation() && !context_.subtree_flags) { |
| // No paint invalidation flag. No paint invalidation is needed. |
| return PaintInvalidationReason::kNone; |
| } |
| |
| if (object_.ShouldDoFullPaintInvalidation()) |
| return object_.FullPaintInvalidationReason(); |
| |
| if (context_.subtree_flags & |
| PaintInvalidatorContext::kSubtreeFullInvalidation) |
| return PaintInvalidationReason::kSubtree; |
| |
| if (context_.fragment_data->PaintOffset() != context_.old_paint_offset) |
| return PaintInvalidationReason::kGeometry; |
| |
| if (object_.GetDocument().InForcedColorsMode() && object_.IsLayoutBlockFlow()) |
| return PaintInvalidationReason::kBackplate; |
| |
| // Force full paint invalidation if the object has background-clip:text to |
| // update the background on any change in the subtree. |
| if (object_.StyleRef().BackgroundClip() == EFillBox::kText) |
| return PaintInvalidationReason::kBackground; |
| |
| // Incremental invalidation is only applicable to LayoutBoxes. Return |
| // kIncremental. BoxPaintInvalidator may override this reason with a full |
| // paint invalidation reason if needed. |
| if (object_.IsBox()) |
| return PaintInvalidationReason::kIncremental; |
| |
| return PaintInvalidationReason::kNone; |
| } |
| |
| DISABLE_CFI_PERF |
| void ObjectPaintInvalidatorWithContext::InvalidatePaintWithComputedReason( |
| PaintInvalidationReason reason) { |
| DCHECK(!(context_.subtree_flags & |
| PaintInvalidatorContext::kSubtreeNoInvalidation)); |
| |
| if (reason == PaintInvalidationReason::kNone) { |
| if (!object_.ShouldInvalidateSelection()) |
| return; |
| // See layout_selection.cc SetShouldInvalidateIfNeeded() for the reason |
| // for the IsSVGText() condition here. |
| if (!object_.CanBeSelectionLeaf() && !object_.IsSVGText()) |
| return; |
| |
| reason = PaintInvalidationReason::kSelection; |
| if (const auto* selection_client = |
| object_.GetSelectionDisplayItemClient()) { |
| // Invalidate the selection display item client only. |
| context_.painting_layer->SetNeedsRepaint(); |
| selection_client->Invalidate(reason); |
| return; |
| } |
| } |
| |
| context_.painting_layer->SetNeedsRepaint(); |
| object_.InvalidateDisplayItemClients(reason); |
| } |
| |
| } // namespace blink |