blob: 4d712def8d1bf93e6ec60e9e57cf398221648278 [file] [log] [blame]
// Copyright 2014 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/compositing/compositing_inputs_updater.h"
#include "third_party/blink/renderer/core/display_lock/display_lock_utilities.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/layout/layout_block.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/paint/compositing/composited_layer_mapping.h"
#include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h"
#include "third_party/blink/renderer/core/paint/paint_layer.h"
#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
namespace blink {
static const LayoutBoxModelObject* ClippingContainerFromClipChainParent(
const PaintLayer* clip_chain_parent) {
return clip_chain_parent->GetLayoutObject().HasClipRelatedProperty()
? &clip_chain_parent->GetLayoutObject()
: clip_chain_parent->ClippingContainer();
}
CompositingInputsUpdater::CompositingInputsUpdater(
PaintLayer* root_layer,
PaintLayer* compositing_inputs_root)
: root_layer_(root_layer),
compositing_inputs_root_(compositing_inputs_root) {
if (!RuntimeEnabledFeatures::CompositingOptimizationsEnabled())
geometry_map_.emplace();
}
CompositingInputsUpdater::~CompositingInputsUpdater() = default;
bool CompositingInputsUpdater::LayerOrDescendantShouldBeComposited(
PaintLayer* layer) {
if (auto* layout_view = DynamicTo<LayoutView>(layer->GetLayoutObject())) {
if (layout_view->AdditionalCompositingReasons())
return true;
// The containing frame may call this function for the root layer of a
// throttled frame. In that case, look for a pre-existing root GraphicsLayer
// in the iframe's compositor.
if (layout_view->GetFrameView()->ShouldThrottleRendering()) {
DisableCompositingQueryAsserts disabler;
if (auto* inner_compositor = layout_view->Compositor()) {
if (inner_compositor->RootGraphicsLayer())
return true;
}
return false;
}
}
DCHECK(!layer->GetLayoutObject().GetFrameView()->ShouldThrottleRendering());
PaintLayerCompositor* compositor =
layer->GetLayoutObject().View()->Compositor();
return layer->DescendantHasDirectOrScrollingCompositingReason() ||
layer->NeedsCompositedScrolling() ||
(compositor->CanBeComposited(layer) &&
layer->DirectCompositingReasons());
}
void CompositingInputsUpdater::Update() {
TRACE_EVENT0("blink", "CompositingInputsUpdater::update");
AncestorInfo info;
UpdateType update_type = kDoNotForceUpdate;
PaintLayer* layer =
compositing_inputs_root_ ? compositing_inputs_root_ : root_layer_;
// We don't need to do anything if the layer is under a locked display lock
// that prevents updates.
if (DisplayLockUtilities::LockedAncestorPreventingPrePaint(
layer->GetLayoutObject())) {
return;
}
CompositingReasons initial_compositing_reasons =
layer->DirectCompositingReasons();
ApplyAncestorInfoToSelfAndAncestorsRecursively(layer, update_type, info);
UpdateSelfAndDescendantsRecursively(layer, update_type, info);
// The layer has changed from non-compositing to compositing
if (initial_compositing_reasons == CompositingReason::kNone &&
LayerOrDescendantShouldBeComposited(layer)) {
// Update all parent layers
PaintLayer* parent_layer = layer->Parent();
while (parent_layer) {
parent_layer->SetDescendantHasDirectOrScrollingCompositingReason(true);
parent_layer = parent_layer->Parent();
}
}
}
void CompositingInputsUpdater::ApplyAncestorInfoToSelfAndAncestorsRecursively(
PaintLayer* layer,
UpdateType& update_type,
AncestorInfo& info) {
if (!layer)
return;
// We first recursively call ApplyAncestorInfoToSelfAndAncestorsRecursively()
// to ensure that we start to compute the geometry_map_ and AncestorInfo from
// the root layer (as we need to do a top-down tree walk to incrementally
// update this information).
ApplyAncestorInfoToSelfAndAncestorsRecursively(layer->Parent(), update_type,
info);
if (!RuntimeEnabledFeatures::CompositingOptimizationsEnabled())
geometry_map_->PushMappingsToAncestor(layer, layer->Parent());
UpdateAncestorInfo(layer, update_type, info);
}
void CompositingInputsUpdater::UpdateSelfAndDescendantsRecursively(
PaintLayer* layer,
UpdateType update_type,
AncestorInfo info) {
LayoutBoxModelObject& layout_object = layer->GetLayoutObject();
// geometry_map_ has been already updated in ApplyAncestorInfo() and
// UpdateAncestorInfo has been already computed in ApplyAncestorInfo() for
// layers from root_layer_ down to compositing_inputs_root_ both included.
if (layer != root_layer_ && layer != compositing_inputs_root_) {
if (!RuntimeEnabledFeatures::CompositingOptimizationsEnabled())
geometry_map_->PushMappingsToAncestor(layer, layer->Parent());
UpdateAncestorInfo(layer, update_type, info);
}
PaintLayerCompositor* compositor =
layer->GetLayoutObject().View()->Compositor();
// The sequence of updates to compositing triggers goes like this:
// 1. Apply all triggers from kComboAllDirectNonStyleDeterminedReasons for
// |layer|. This may depend on ancestor composited scrolling (i.e. step
// 2 for an ancestor PaintLayer).
// 2. Put |layer| in composited scrolling mode if needed.
// 3. Reset DescendantHasDirectCompositingReason to false for |layer|.
// 4. Recurse into child PaintLayers.
// 5. Set DescendantHasDirectCompositingReason to true if it was for any
// child.
// 6. If |layer| is the root, composite if
// DescendantHasDirectCompositingReason is true for |layer|.
layer->SetPotentialCompositingReasonsFromNonStyle(
CompositingReasonFinder::NonStyleDeterminedDirectReasons(*layer));
if (layer->GetScrollableArea()) {
layer->GetScrollableArea()->UpdateNeedsCompositedScrolling(
compositor->CanBeComposited(layer) &&
layer->DirectCompositingReasons());
}
// Note that prepaint may use the compositing information, so only skip
// recursing it if we're skipping prepaint.
bool recursion_blocked_by_display_lock =
layer->GetLayoutObject().ChildPrePaintBlockedByDisplayLock();
bool should_recurse = (layer->ChildNeedsCompositingInputsUpdate() ||
update_type == kForceUpdate);
layer->SetDescendantHasDirectOrScrollingCompositingReason(false);
bool descendant_has_direct_compositing_reason = false;
auto* first_child =
recursion_blocked_by_display_lock ? nullptr : layer->FirstChild();
for (PaintLayer* child = first_child; child; child = child->NextSibling()) {
if (should_recurse)
UpdateSelfAndDescendantsRecursively(child, update_type, info);
descendant_has_direct_compositing_reason |=
LayerOrDescendantShouldBeComposited(child);
}
if (!descendant_has_direct_compositing_reason &&
layer->GetLayoutObject().IsLayoutEmbeddedContent()) {
if (LayoutView* embedded_layout_view =
To<LayoutEmbeddedContent>(layer->GetLayoutObject())
.ChildLayoutView()) {
descendant_has_direct_compositing_reason |=
LayerOrDescendantShouldBeComposited(embedded_layout_view->Layer());
}
}
layer->SetDescendantHasDirectOrScrollingCompositingReason(
descendant_has_direct_compositing_reason);
if ((layer->IsRootLayer() || layer->NeedsReorderOverlayOverflowControls()) &&
layer->ScrollsOverflow() &&
layer->DescendantHasDirectOrScrollingCompositingReason() &&
!layer->NeedsCompositedScrolling())
layer->GetScrollableArea()->UpdateNeedsCompositedScrolling(true);
// If display lock blocked this recursion, then keep the dirty bit around
// since it is a breadcrumb that will allow us to recurse later when we unlock
// the element.
if (!recursion_blocked_by_display_lock)
layer->ClearChildNeedsCompositingInputsUpdate();
if (!RuntimeEnabledFeatures::CompositingOptimizationsEnabled())
geometry_map_->PopMappingsToAncestor(layer->Parent());
if (layer->SelfPaintingStatusChanged()) {
layer->ClearSelfPaintingStatusChanged();
// If the floating object becomes non-self-painting, so some ancestor should
// paint it; if it becomes self-painting, it should paint itself and no
// ancestor should paint it.
if (layout_object.IsFloating()) {
LayoutBlockFlow::UpdateAncestorShouldPaintFloatingObject(
*layer->GetLayoutBox());
}
}
compositor->ClearCompositingInputsRoot();
DisableCompositingQueryAsserts disabler;
bool previously_needed_paint_offset_translation =
layer->NeedsPaintOffsetTranslationForCompositing();
layer->SetNeedsPaintOffsetTranslationForCompositing(
NeedsPaintOffsetTranslationForCompositing(layer));
// Invalidate if needed to affect NeedsPaintOffsetTranslation().
if (previously_needed_paint_offset_translation !=
layer->NeedsPaintOffsetTranslationForCompositing())
layout_object.SetNeedsPaintPropertyUpdate();
}
bool CompositingInputsUpdater::NeedsPaintOffsetTranslationForCompositing(
PaintLayer* layer) {
PaintLayerCompositor* compositor =
layer->GetLayoutObject().View()->Compositor();
/// Allocate when the developer indicated compositing via a direct
// method.
if ((compositor->CanBeComposited(layer) &&
layer->DirectCompositingReasons()) ||
layer->NeedsCompositedScrolling())
return true;
// Allocate when there is a need for a cc effect that applies to
// descendants.
// TODO(chrishtr): this should not be necessary, but currently at least
// cc mask layers don't apply correctly otherwise.
// compositing/clip-path-with-composited-descendants.html is one test
// that demonstrates this.
if ((layer->PotentialCompositingReasonsFromStyle() &
CompositingReason::kComboCompositedDescendants) &&
layer->DescendantHasDirectOrScrollingCompositingReason())
return true;
return false;
}
void CompositingInputsUpdater::UpdateAncestorInfo(PaintLayer* const layer,
UpdateType& update_type,
AncestorInfo& info) {
LayoutBoxModelObject& layout_object = layer->GetLayoutObject();
const ComputedStyle& style = layout_object.StyleRef();
PaintLayer* enclosing_stacking_composited_layer =
info.enclosing_stacking_composited_layer;
PaintLayer* enclosing_squashing_composited_layer =
info.enclosing_squashing_composited_layer;
DisableCompositingQueryAsserts disabler;
if (layer->NeedsCompositingInputsUpdate()) {
if (enclosing_stacking_composited_layer) {
enclosing_stacking_composited_layer->GetCompositedLayerMapping()
->SetNeedsGraphicsLayerUpdate(kGraphicsLayerUpdateSubtree);
}
if (enclosing_squashing_composited_layer) {
enclosing_squashing_composited_layer->GetCompositedLayerMapping()
->SetNeedsGraphicsLayerUpdate(kGraphicsLayerUpdateSubtree);
}
update_type = kForceUpdate;
}
switch (layer->GetCompositingState()) {
case kNotComposited:
break;
case kPaintsIntoOwnBacking:
if (layout_object.IsStackingContext())
enclosing_stacking_composited_layer = layer;
break;
case kPaintsIntoGroupedBacking:
enclosing_squashing_composited_layer =
&layer->GroupedMapping()->OwningLayer();
break;
}
// invalidate again after the switch, in case
// enclosing_stacking_composited_layer or
// enclosing_squashing_composited_layer was previously null.
if (layer->NeedsCompositingInputsUpdate()) {
if (enclosing_stacking_composited_layer) {
enclosing_stacking_composited_layer->GetCompositedLayerMapping()
->SetNeedsGraphicsLayerUpdate(kGraphicsLayerUpdateSubtree);
}
if (enclosing_squashing_composited_layer) {
enclosing_squashing_composited_layer->GetCompositedLayerMapping()
->SetNeedsGraphicsLayerUpdate(kGraphicsLayerUpdateSubtree);
}
}
if (style.GetPosition() == EPosition::kAbsolute) {
info.escape_clip_to = info.escape_clip_to_for_absolute;
info.scrolling_ancestor = info.scrolling_ancestor_for_absolute;
info.needs_reparent_scroll = info.needs_reparent_scroll_for_absolute;
} else if (style.GetPosition() == EPosition::kFixed) {
info.escape_clip_to = info.escape_clip_to_for_fixed;
info.scrolling_ancestor = info.scrolling_ancestor_for_fixed;
info.needs_reparent_scroll = info.needs_reparent_scroll_for_fixed;
}
if (layout_object.ShouldApplyLayoutContainment())
info.nearest_contained_layout_layer = layer;
if (update_type == kForceUpdate)
UpdateAncestorDependentCompositingInputs(layer, info);
info.enclosing_stacking_composited_layer =
enclosing_stacking_composited_layer;
info.enclosing_squashing_composited_layer =
enclosing_squashing_composited_layer;
// Handles sibling scroll problem, i.e. a non-stacking context scroller
// needs to propagate scroll to its descendants that are siblings in
// paint order. For example:
// <div style="overflow:scroll;">
// <div style="position:relative;">Paint sibling.</div>
// </div>
if (layer->ScrollsOverflow()) {
info.scrolling_ancestor = layer;
info.needs_reparent_scroll = true;
}
if (layout_object.CanContainAbsolutePositionObjects()) {
info.clip_chain_parent_for_absolute = layer;
info.escape_clip_to_for_absolute = info.escape_clip_to;
info.scrolling_ancestor_for_absolute = info.scrolling_ancestor;
info.needs_reparent_scroll_for_absolute = info.needs_reparent_scroll;
}
// LayoutView isn't really the containing block for fixed-pos descendants
// in the sense that they don't scroll along with its in-flow contents.
// However LayoutView does clip them.
if (layout_object.CanContainFixedPositionObjects() &&
!IsA<LayoutView>(layout_object)) {
info.clip_chain_parent_for_fixed = layer;
info.escape_clip_to_for_fixed = info.escape_clip_to;
info.scrolling_ancestor_for_fixed = info.scrolling_ancestor;
info.needs_reparent_scroll_for_fixed = info.needs_reparent_scroll;
}
if (IsA<LayoutView>(layout_object))
info.clip_chain_parent_for_fixed = layer;
// CSS clip affects all descendants, not just containing-block descendants.
// We don't have to set clip_chain_parent_for_absolute here because CSS clip
// requires position:absolute, so the element must contain absolute-positioned
// descendants.
// However it is incorrect to let fixed-positioned descendants to inherit the
// clip state from this element either, because the overflow clip and the
// inherited clip of the current element shouldn't apply to them if the
// current element is not a fixed-pos container. This is a known bug but too
// difficult to fix in SPv1 compositing.
if (layout_object.HasClip())
info.clip_chain_parent_for_fixed = layer;
if (layout_object.IsStackingContext()) {
info.escape_clip_to = nullptr;
const LayoutBoxModelObject* clipping_container =
ClippingContainerFromClipChainParent(layer);
info.escape_clip_to_for_absolute =
ClippingContainerFromClipChainParent(
info.clip_chain_parent_for_absolute) != clipping_container
? info.clip_chain_parent_for_absolute
: nullptr;
info.escape_clip_to_for_fixed =
ClippingContainerFromClipChainParent(
info.clip_chain_parent_for_fixed) != clipping_container
? info.clip_chain_parent_for_fixed
: nullptr;
// Workaround crbug.com/817175
// We can't escape clip to a layer that paints after us, because in SPv1
// cc needs to reverse engineer clip tree from the layer tree, and we
// can't refer to a clip node that hasn't been built yet.
// This will result in wrong clip in some rare cases, for example:
// <div style="display:grid;">
// <div style="z-index:-1; overflow:hidden;">
// <div style="position:absolute;"></div>
// </div>
// </div>
if (info.escape_clip_to_for_absolute && style.EffectiveZIndex() < 0 &&
!info.escape_clip_to_for_absolute->GetLayoutObject()
.IsStackingContext())
info.escape_clip_to_for_absolute = nullptr;
if (info.escape_clip_to_for_fixed && style.EffectiveZIndex() < 0 &&
!info.escape_clip_to_for_fixed->GetLayoutObject().IsStackingContext())
info.escape_clip_to_for_fixed = nullptr;
info.needs_reparent_scroll = info.needs_reparent_scroll_for_absolute =
info.needs_reparent_scroll_for_fixed = false;
}
if (layout_object.IsStickyPositioned())
info.is_under_position_sticky = true;
}
void CompositingInputsUpdater::UpdateAncestorDependentCompositingInputs(
PaintLayer* layer,
const AncestorInfo& info) {
if (layer->IsRootLayer()) {
layer->UpdateAncestorDependentCompositingInputs(
PaintLayer::AncestorDependentCompositingInputs());
return;
}
PaintLayer::AncestorDependentCompositingInputs properties;
LayoutBoxModelObject& layout_object = layer->GetLayoutObject();
if (!RuntimeEnabledFeatures::CompositingOptimizationsEnabled()) {
// The final value for |unclipped_absolute_bounding_box| needs to be
// in absolute, unscrolled space, without any scroll applied.
properties.unclipped_absolute_bounding_box =
EnclosingIntRect(geometry_map_->AbsoluteRect(
layer->LocalBoundingBoxForCompositingOverlapTest()));
bool affected_by_scroll = root_layer_->GetScrollableArea() &&
layer->IsAffectedByScrollOf(root_layer_);
// At this point, |unclipped_absolute_bounding_box| is in viewport space.
// To convert to absolute space, add scroll offset for non-fixed layers.
// Content that is not affected by scroll, e.g. fixed-pos content and
// children of that content, stays in viewport space so we can expand its
// bounds during overlap testing without having a dependency on the scroll
// offset at the time these properties are calculated.
if (affected_by_scroll) {
properties.unclipped_absolute_bounding_box.Move(
RoundedIntSize(root_layer_->GetScrollableArea()->GetScrollOffset()));
}
// For sticky-positioned elements, the scroll offset is sometimes included
// and sometimes not, depending on whether the sticky element is affixed or
// still scrolling. This makes caching difficult, as compared to Fixed
// position elements which have consistent behavior. So we disable caching
// for sticky-positioned subtrees.
ClipRectsCacheSlot cache_slot =
info.is_under_position_sticky ? kUncachedClipRects
: kAbsoluteClipRectsIgnoringViewportClip;
ClipRect clip_rect;
layer->Clipper(PaintLayer::GeometryMapperOption::kDoNotUseGeometryMapper)
.CalculateBackgroundClipRect(
ClipRectsContext(root_layer_,
&root_layer_->GetLayoutObject().FirstFragment(),
cache_slot, kIgnoreOverlayScrollbarSize,
kIgnoreOverflowClipAndScroll),
clip_rect);
IntRect snapped_clip_rect = PixelSnappedIntRect(clip_rect.Rect());
// |snapped_clip_rect| is in absolute space space, but with scroll applied.
// To convert to absolute, unscrolled space, subtract scroll offsets for
// fixed layers.
if (root_layer_->GetScrollableArea() && !affected_by_scroll) {
snapped_clip_rect.Move(
RoundedIntSize(-root_layer_->GetScrollableArea()->GetScrollOffset()));
}
properties.clipped_absolute_bounding_box =
properties.unclipped_absolute_bounding_box;
properties.clipped_absolute_bounding_box.Intersect(snapped_clip_rect);
}
const PaintLayer* parent = layer->Parent();
properties.opacity_ancestor =
parent->IsTransparent() ? parent : parent->OpacityAncestor();
properties.transform_ancestor =
parent->Transform() ? parent : parent->TransformAncestor();
properties.filter_ancestor =
parent->HasFilterInducingProperty() ? parent : parent->FilterAncestor();
properties.clip_path_ancestor = parent->GetLayoutObject().HasClipPath()
? parent
: parent->ClipPathAncestor();
properties.mask_ancestor =
parent->GetLayoutObject().HasMask() ? parent : parent->MaskAncestor();
EPosition position = layout_object.StyleRef().GetPosition();
properties.nearest_fixed_position_layer =
position == EPosition::kFixed ? layer
: parent->NearestFixedPositionLayer();
PaintLayer* clip_chain_parent = layer->Parent();
if (position == EPosition::kAbsolute)
clip_chain_parent = info.clip_chain_parent_for_absolute;
else if (position == EPosition::kFixed)
clip_chain_parent = info.clip_chain_parent_for_fixed;
properties.clipping_container =
ClippingContainerFromClipChainParent(clip_chain_parent);
properties.clip_parent = info.escape_clip_to;
properties.ancestor_scrolling_layer = info.scrolling_ancestor;
if (info.needs_reparent_scroll && layout_object.IsStacked())
properties.scroll_parent = info.scrolling_ancestor;
properties.nearest_contained_layout_layer =
info.nearest_contained_layout_layer;
layer->UpdateAncestorDependentCompositingInputs(properties);
}
#if DCHECK_IS_ON()
void CompositingInputsUpdater::AssertNeedsCompositingInputsUpdateBitsCleared(
PaintLayer* layer) {
bool recursion_blocked_by_display_lock =
layer->GetLayoutObject().ChildPrePaintBlockedByDisplayLock();
DCHECK(recursion_blocked_by_display_lock ||
!layer->ChildNeedsCompositingInputsUpdate());
DCHECK(!layer->NeedsCompositingInputsUpdate());
if (recursion_blocked_by_display_lock)
return;
for (PaintLayer* child = layer->FirstChild(); child;
child = child->NextSibling())
AssertNeedsCompositingInputsUpdateBitsCleared(child);
}
#endif
} // namespace blink