blob: 2cccce8b5846640c20ce154db91e2c6c8cfcb982 [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 "core/layout/compositing/CompositingReasonFinder.h"
#include "core/CSSPropertyNames.h"
#include "core/dom/Document.h"
#include "core/frame/LocalFrameView.h"
#include "core/frame/Settings.h"
#include "core/layout/LayoutView.h"
#include "core/page/Page.h"
#include "core/paint/PaintLayer.h"
namespace blink {
CompositingReasonFinder::CompositingReasonFinder(LayoutView& layout_view)
: layout_view_(layout_view),
compositing_triggers_(
static_cast<CompositingTriggerFlags>(kAllCompositingTriggers)) {
UpdateTriggers();
}
void CompositingReasonFinder::UpdateTriggers() {
compositing_triggers_ = 0;
Settings& settings = layout_view_.GetDocument().GetPage()->GetSettings();
if (settings.GetPreferCompositingToLCDTextEnabled()) {
compositing_triggers_ |= kScrollableInnerFrameTrigger;
compositing_triggers_ |= kOverflowScrollTrigger;
compositing_triggers_ |= kViewportConstrainedPositionedTrigger;
}
}
bool CompositingReasonFinder::IsMainFrame() const {
return layout_view_.GetDocument().IsInMainFrame();
}
CompositingReasons CompositingReasonFinder::DirectReasons(
const PaintLayer* layer,
bool ignore_lcd_text) const {
if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled())
return kCompositingReasonNone;
DCHECK_EQ(PotentialCompositingReasonsFromStyle(layer->GetLayoutObject()),
layer->PotentialCompositingReasonsFromStyle());
CompositingReasons style_determined_direct_compositing_reasons =
layer->PotentialCompositingReasonsFromStyle() &
kCompositingReasonComboAllDirectStyleDeterminedReasons;
return style_determined_direct_compositing_reasons |
NonStyleDeterminedDirectReasons(layer, ignore_lcd_text);
}
// This information doesn't appear to be incorporated into CompositingReasons.
bool CompositingReasonFinder::RequiresCompositingForScrollableFrame() const {
// Need this done first to determine overflow.
DCHECK(!layout_view_.NeedsLayout());
if (IsMainFrame())
return false;
if (!(compositing_triggers_ & kScrollableInnerFrameTrigger))
return false;
return layout_view_.GetFrameView()->IsScrollable();
}
CompositingReasons
CompositingReasonFinder::PotentialCompositingReasonsFromStyle(
LayoutObject& layout_object) const {
if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled())
return kCompositingReasonNone;
CompositingReasons reasons = kCompositingReasonNone;
const ComputedStyle& style = layout_object.StyleRef();
if (RequiresCompositingForTransform(layout_object))
reasons |= kCompositingReason3DTransform;
if (style.BackfaceVisibility() == kBackfaceVisibilityHidden)
reasons |= kCompositingReasonBackfaceVisibilityHidden;
if (RequiresCompositingForAnimation(style))
reasons |= kCompositingReasonActiveAnimation;
if (style.HasWillChangeCompositingHint() &&
!style.SubtreeWillChangeContents())
reasons |= kCompositingReasonWillChangeCompositingHint;
if (style.HasInlineTransform())
reasons |= kCompositingReasonInlineTransform;
if (style.UsedTransformStyle3D() == ETransformStyle3D::kPreserve3d)
reasons |= kCompositingReasonPreserve3DWith3DDescendants;
if (style.HasPerspective())
reasons |= kCompositingReasonPerspectiveWith3DDescendants;
if (style.HasCompositorProxy())
reasons |= kCompositingReasonCompositorProxy;
// If the implementation of createsGroup changes, we need to be aware of that
// in this part of code.
DCHECK((layout_object.IsTransparent() || layout_object.HasMask() ||
layout_object.HasFilterInducingProperty() || style.HasBlendMode()) ==
layout_object.CreatesGroup());
if (style.HasMask())
reasons |= kCompositingReasonMaskWithCompositedDescendants;
if (style.HasFilterInducingProperty())
reasons |= kCompositingReasonFilterWithCompositedDescendants;
if (style.HasBackdropFilter())
reasons |= kCompositingReasonBackdropFilter;
// See Layer::updateTransform for an explanation of why we check both.
if (layout_object.HasTransformRelatedProperty() && style.HasTransform())
reasons |= kCompositingReasonTransformWithCompositedDescendants;
if (layout_object.IsTransparent())
reasons |= kCompositingReasonOpacityWithCompositedDescendants;
if (style.HasBlendMode())
reasons |= kCompositingReasonBlendingWithCompositedDescendants;
if (layout_object.HasReflection())
reasons |= kCompositingReasonReflectionWithCompositedDescendants;
DCHECK(!(reasons & ~kCompositingReasonComboAllStyleDeterminedReasons));
return reasons;
}
bool CompositingReasonFinder::RequiresCompositingForTransform(
const LayoutObject& layout_object) {
// Note that we ask the layoutObject if it has a transform, because the style
// may have transforms, but the layoutObject may be an inline that doesn't
// support them.
return layout_object.HasTransformRelatedProperty() &&
layout_object.StyleRef().Has3DTransform();
}
CompositingReasons CompositingReasonFinder::NonStyleDeterminedDirectReasons(
const PaintLayer* layer,
bool ignore_lcd_text) const {
CompositingReasons direct_reasons = kCompositingReasonNone;
LayoutObject& layout_object = layer->GetLayoutObject();
if (layer->ClipParent())
direct_reasons |= kCompositingReasonOutOfFlowClipping;
if (layer->NeedsCompositedScrolling())
direct_reasons |= kCompositingReasonOverflowScrollingTouch;
// Composite |layer| if it is inside of an ancestor scrolling layer, but that
// scrolling layer is not on the stacking context ancestor chain of |layer|.
// See the definition of the scrollParent property in Layer for more detail.
if (const PaintLayer* scrolling_ancestor = layer->AncestorScrollingLayer()) {
if (scrolling_ancestor->NeedsCompositedScrolling() && layer->ScrollParent())
direct_reasons |= kCompositingReasonOverflowScrollingParent;
}
if (RequiresCompositingForScrollDependentPosition(layer, ignore_lcd_text))
direct_reasons |= kCompositingReasonScrollDependentPosition;
direct_reasons |= layout_object.AdditionalCompositingReasons();
DCHECK(!(direct_reasons & kCompositingReasonComboAllStyleDeterminedReasons));
return direct_reasons;
}
bool CompositingReasonFinder::RequiresCompositingForAnimation(
const ComputedStyle& style) {
if (style.SubtreeWillChangeContents())
return style.IsRunningAnimationOnCompositor();
return style.ShouldCompositeForCurrentAnimations();
}
bool CompositingReasonFinder::RequiresCompositingForOpacityAnimation(
const ComputedStyle& style) {
return style.SubtreeWillChangeContents()
? style.IsRunningOpacityAnimationOnCompositor()
: style.HasCurrentOpacityAnimation();
}
bool CompositingReasonFinder::RequiresCompositingForFilterAnimation(
const ComputedStyle& style) {
return style.SubtreeWillChangeContents()
? style.IsRunningFilterAnimationOnCompositor()
: style.HasCurrentFilterAnimation();
}
bool CompositingReasonFinder::RequiresCompositingForBackdropFilterAnimation(
const ComputedStyle& style) {
return style.SubtreeWillChangeContents()
? style.IsRunningBackdropFilterAnimationOnCompositor()
: style.HasCurrentBackdropFilterAnimation();
}
bool CompositingReasonFinder::RequiresCompositingForEffectAnimation(
const ComputedStyle& style) {
return RequiresCompositingForOpacityAnimation(style) ||
RequiresCompositingForFilterAnimation(style) ||
RequiresCompositingForBackdropFilterAnimation(style);
}
bool CompositingReasonFinder::RequiresCompositingForTransformAnimation(
const ComputedStyle& style) {
return style.SubtreeWillChangeContents()
? style.IsRunningTransformAnimationOnCompositor()
: style.HasCurrentTransformAnimation();
}
bool CompositingReasonFinder::RequiresCompositingForScrollDependentPosition(
const PaintLayer* layer,
bool ignore_lcd_text) const {
if (layer->GetLayoutObject().Style()->GetPosition() != EPosition::kFixed &&
layer->GetLayoutObject().Style()->GetPosition() != EPosition::kSticky)
return false;
if (!(ignore_lcd_text ||
(compositing_triggers_ & kViewportConstrainedPositionedTrigger)) &&
(!RuntimeEnabledFeatures::CompositeOpaqueFixedPositionEnabled() ||
!layer->BackgroundIsKnownToBeOpaqueInRect(
LayoutRect(layer->BoundingBoxForCompositing())) ||
layer->CompositesWithTransform() || layer->CompositesWithOpacity())) {
return false;
}
// Don't promote fixed position elements that are descendants of a non-view
// container, e.g. transformed elements. They will stay fixed wrt the
// container rather than the enclosing frame.
EPosition position = layer->GetLayoutObject().Style()->GetPosition();
if (position == EPosition::kFixed)
return layer->FixedToViewport() &&
layout_view_.GetFrameView()->IsScrollable();
DCHECK_EQ(position, EPosition::kSticky);
// Don't promote sticky position elements that cannot move with scrolls.
if (!layer->SticksToScroller())
return false;
if (layer->AncestorOverflowLayer()->IsRootLayer())
return layout_view_.GetFrameView()->IsScrollable();
return layer->AncestorOverflowLayer()->ScrollsOverflow();
}
} // namespace blink