blob: e2f8cff6d6bc8579b253c7c57045699e32a13705 [file] [log] [blame]
// Copyright 2017 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_layer_property_updater.h"
#include "third_party/blink/renderer/core/layout/layout_box_model_object.h"
#include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h"
#include "third_party/blink/renderer/core/paint/fragment_data.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
namespace {
enum class ScrollbarOrCorner {
kHorizontalScrollbar,
kVerticalScrollbar,
kScrollbarCorner,
};
}
namespace blink {
void CompositingLayerPropertyUpdater::Update(const LayoutObject& object) {
if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled())
return;
if (!object.HasLayer())
return;
const auto* paint_layer = ToLayoutBoxModelObject(object).Layer();
const auto* mapping = paint_layer->GetCompositedLayerMapping();
if (!mapping)
return;
const FragmentData& fragment_data = object.FirstFragment();
DCHECK(fragment_data.HasLocalBorderBoxProperties());
// SPv1 compositing forces single fragment for composited elements.
DCHECK(!fragment_data.NextFragment() ||
// We create multiple fragments for composited repeating fixed-position
// during printing.
object.GetDocument().Printing());
LayoutPoint layout_snapped_paint_offset =
fragment_data.PaintOffset() - mapping->SubpixelAccumulation();
IntPoint snapped_paint_offset = RoundedIntPoint(layout_snapped_paint_offset);
// A layer without visible contents can be composited due to animation.
// Since the layer itself has no visible subtree, there is no guarantee
// that all of its ancestors have a visible subtree. An ancestor with no
// visible subtree can be non-composited despite we expected it to, this
// resulted in the paint offset used by CompositedLayerMapping to mismatch.
#if 0
// TODO(crbug.com/838018): Re-enable this check.
bool subpixel_accumulation_may_be_bogus = paint_layer->SubtreeIsInvisible();
DCHECK(layout_snapped_paint_offset == snapped_paint_offset ||
subpixel_accumulation_may_be_bogus);
#endif
base::Optional<PropertyTreeState> container_layer_state;
auto SetContainerLayerState =
[&fragment_data, &snapped_paint_offset,
&container_layer_state](GraphicsLayer* graphics_layer) {
if (graphics_layer) {
if (!container_layer_state) {
container_layer_state = fragment_data.LocalBorderBoxProperties();
if (const auto* properties = fragment_data.PaintProperties()) {
// CSS clip should be applied within the layer.
if (const auto* css_clip = properties->CssClip())
container_layer_state->SetClip(css_clip->Parent());
}
}
graphics_layer->SetLayerState(
*container_layer_state,
snapped_paint_offset + graphics_layer->OffsetFromLayoutObject());
}
};
SetContainerLayerState(mapping->MainGraphicsLayer());
SetContainerLayerState(mapping->DecorationOutlineLayer());
SetContainerLayerState(mapping->ChildClippingMaskLayer());
auto SetContainerLayerStateForScrollbars =
[&fragment_data, &snapped_paint_offset, &container_layer_state](
GraphicsLayer* graphics_layer,
ScrollbarOrCorner scrollbar_or_corner) {
if (!graphics_layer)
return;
PropertyTreeState scrollbar_layer_state =
container_layer_state.value_or(
fragment_data.LocalBorderBoxProperties());
// OverflowControlsClip should be applied within the scrollbar
// layers.
if (const auto* properties = fragment_data.PaintProperties()) {
if (const auto* clip = properties->OverflowControlsClip()) {
scrollbar_layer_state.SetClip(clip);
} else if (const auto* css_clip = properties->CssClip()) {
scrollbar_layer_state.SetClip(css_clip->Parent());
}
}
if (const auto* properties = fragment_data.PaintProperties()) {
if (scrollbar_or_corner == ScrollbarOrCorner::kHorizontalScrollbar) {
if (const auto* effect = properties->HorizontalScrollbarEffect()) {
scrollbar_layer_state.SetEffect(effect);
}
}
if (scrollbar_or_corner == ScrollbarOrCorner::kVerticalScrollbar) {
if (const auto* effect = properties->VerticalScrollbarEffect())
scrollbar_layer_state.SetEffect(effect);
}
}
graphics_layer->SetLayerState(
scrollbar_layer_state,
snapped_paint_offset + graphics_layer->OffsetFromLayoutObject());
};
SetContainerLayerStateForScrollbars(mapping->LayerForHorizontalScrollbar(),
ScrollbarOrCorner::kHorizontalScrollbar);
SetContainerLayerStateForScrollbars(mapping->LayerForVerticalScrollbar(),
ScrollbarOrCorner::kVerticalScrollbar);
SetContainerLayerStateForScrollbars(mapping->LayerForScrollCorner(),
ScrollbarOrCorner::kScrollbarCorner);
if (mapping->ScrollingContentsLayer()) {
auto SetContentsLayerState = [&fragment_data, &snapped_paint_offset](
GraphicsLayer* graphics_layer) {
if (graphics_layer) {
graphics_layer->SetLayerState(
fragment_data.ContentsProperties(),
snapped_paint_offset + graphics_layer->OffsetFromLayoutObject());
}
};
SetContentsLayerState(mapping->ScrollingContentsLayer());
SetContentsLayerState(mapping->ForegroundLayer());
} else {
SetContainerLayerState(mapping->ForegroundLayer());
}
auto* main_graphics_layer = mapping->MainGraphicsLayer();
if (const auto* contents_layer = main_graphics_layer->ContentsLayer()) {
auto position = contents_layer->position();
main_graphics_layer->SetContentsLayerState(
fragment_data.ContentsProperties(),
snapped_paint_offset + main_graphics_layer->OffsetFromLayoutObject() +
IntSize(position.x(), position.y()));
}
if (auto* squashing_layer = mapping->SquashingLayer()) {
auto state = fragment_data.PreEffectProperties();
// The squashing layer's ClippingContainer is the common ancestor of clip
// state of all squashed layers, so we should use its clip state. This skips
// any control clips on the squashing layer's object which should not apply
// on squashed layers.
const auto* clipping_container = paint_layer->ClippingContainer();
state.SetClip(
clipping_container
? clipping_container->FirstFragment().ContentsProperties().Clip()
: &ClipPaintPropertyNode::Root());
squashing_layer->SetLayerState(
state,
snapped_paint_offset + mapping->SquashingLayerOffsetFromLayoutObject());
}
if (auto* mask_layer = mapping->MaskLayer()) {
auto state = fragment_data.LocalBorderBoxProperties();
const auto* properties = fragment_data.PaintProperties();
DCHECK(properties && properties->Mask());
state.SetEffect(properties->Mask());
state.SetClip(properties->MaskClip());
mask_layer->SetLayerState(
state, snapped_paint_offset + mask_layer->OffsetFromLayoutObject());
}
if (auto* ancestor_clipping_mask_layer =
mapping->AncestorClippingMaskLayer()) {
PropertyTreeState state(
fragment_data.PreTransform(),
mapping->ClipInheritanceAncestor()
->GetLayoutObject()
.FirstFragment()
.PostOverflowClip(),
// This is a hack to incorporate mask-based clip-path. Really should be
// nullptr or some dummy.
fragment_data.PreFilter());
ancestor_clipping_mask_layer->SetLayerState(
state, snapped_paint_offset +
ancestor_clipping_mask_layer->OffsetFromLayoutObject());
}
if (auto* child_clipping_mask_layer = mapping->ChildClippingMaskLayer()) {
PropertyTreeState state = fragment_data.LocalBorderBoxProperties();
// Same hack as for ancestor_clipping_mask_layer.
state.SetEffect(fragment_data.PreFilter());
child_clipping_mask_layer->SetLayerState(
state, snapped_paint_offset +
child_clipping_mask_layer->OffsetFromLayoutObject());
}
}
} // namespace blink