blob: e985f85b306a47aecb89ce63ee49934bf555274d [file] [log] [blame]
// 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/paint_invalidator.h"
#include "base/optional.h"
#include "third_party/blink/renderer/core/editing/frame_selection.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/frame/settings.h"
#include "third_party/blink/renderer/core/layout/layout_block_flow.h"
#include "third_party/blink/renderer/core/layout/layout_table.h"
#include "third_party/blink/renderer/core/layout/layout_table_section.h"
#include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.h"
#include "third_party/blink/renderer/core/layout/svg/svg_layout_support.h"
#include "third_party/blink/renderer/core/page/chrome_client.h"
#include "third_party/blink/renderer/core/paint/clip_path_clipper.h"
#include "third_party/blink/renderer/core/paint/find_paint_offset_and_visual_rect_needing_update.h"
#include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h"
#include "third_party/blink/renderer/core/paint/object_paint_properties.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/core/paint/pre_paint_tree_walk.h"
#include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h"
namespace blink {
template <typename Rect>
static LayoutRect SlowMapToVisualRectInAncestorSpace(
const LayoutObject& object,
const LayoutBoxModelObject& ancestor,
const Rect& rect) {
if (object.IsSVGChild()) {
LayoutRect result;
SVGLayoutSupport::MapToVisualRectInAncestorSpace(object, &ancestor,
FloatRect(rect), result);
return result;
}
LayoutRect result(rect);
if (object.IsLayoutView()) {
ToLayoutView(object).MapToVisualRectInAncestorSpace(
&ancestor, result, kInputIsInFrameCoordinates, kDefaultVisualRectFlags);
} else {
object.MapToVisualRectInAncestorSpace(&ancestor, result);
}
return result;
}
// If needed, exclude composited layer's subpixel accumulation to avoid full
// layer raster invalidations during animation with subpixels.
// See crbug.com/833083 for details.
template <typename Rect, typename Point>
void PaintInvalidator::ExcludeCompositedLayerSubpixelAccumulation(
const LayoutObject& object,
const PaintInvalidatorContext& context,
Rect& rect) {
// TODO(wangxianzhu): How to handle sub-pixel location animation for SPv2?
if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled())
return;
// One of the following conditions happened in crbug.com/837226.
if (!context.paint_invalidation_container ||
!context.paint_invalidation_container->FirstFragment()
.HasLocalBorderBoxProperties() ||
!context.tree_builder_context_)
return;
if (!(context.paint_invalidation_container->Layer()->GetCompositingReasons() &
CompositingReason::kComboAllDirectReasons))
return;
if (object != context.paint_invalidation_container &&
context.paint_invalidation_container->FirstFragment()
.PostScrollTranslation() !=
context.tree_builder_context_->current.transform) {
// Subpixel accumulation doesn't propagate through non-translation
// transforms. Also skip all transforms, to avoid the runtime cost of
// verifying whether the transform is a translation.
return;
}
// Exclude the subpixel accumulation so that the paint invalidator won't
// see changed visual rects during composited animation with subpixels, to
// avoid full layer invalidation. The subpixel accumulation will be added
// back in ChunkToLayerMapper::AdjustVisualRectBySubpixelOffset(). Should
// make sure the code is synced.
// TODO(wangxianzhu): Avoid exposing subpixel accumulation to platform code.
rect.MoveBy(Point(LayoutPoint(
-context.paint_invalidation_container->Layer()->SubpixelAccumulation())));
}
// TODO(wangxianzhu): Combine this into
// PaintInvalidator::mapLocalRectToBacking() when removing
// PaintInvalidationState.
// This function is templatized to avoid FloatRect<->LayoutRect conversions
// which affect performance.
template <typename Rect, typename Point>
LayoutRect PaintInvalidator::MapLocalRectToVisualRectInBacking(
const LayoutObject& object,
const Rect& local_rect,
const PaintInvalidatorContext& context,
bool disable_flip) {
DCHECK(context.NeedsVisualRectUpdate(object));
if (local_rect.IsEmpty())
return LayoutRect();
bool is_svg_child = object.IsSVGChild();
// TODO(wkorman): The flip below is required because local visual rects are
// currently in "physical coordinates with flipped block-flow direction"
// (see LayoutBoxModelObject.h) but we need them to be in physical
// coordinates.
Rect rect = local_rect;
// Writing-mode flipping doesn't apply to non-root SVG.
if (!is_svg_child) {
if (!disable_flip) {
if (object.IsBox()) {
ToLayoutBox(object).FlipForWritingMode(rect);
} else if (!(context.subtree_flags &
PaintInvalidatorContext::kSubtreeSlowPathRect)) {
// For SPv2 and the GeometryMapper path, we also need to convert the
// rect for non-boxes into physical coordinates before applying paint
// offset. (Otherwise we'll call mapToVisualrectInAncestorSpace() which
// requires physical coordinates for boxes, but "physical coordinates
// with flipped block-flow direction" for non-boxes for which we don't
// need to flip.)
// TODO(wangxianzhu): Avoid containingBlock().
object.ContainingBlock()->FlipForWritingMode(rect);
}
}
// Unite visual rect with clip path bounding rect.
// It is because the clip path display items are owned by the layout object
// who has the clip path, and uses its visual rect as bounding rect too.
// Usually it is done at layout object level and included as a part of
// local visual overflow, but clip-path can be a reference to SVG, and we
// have to wait until pre-paint to ensure clean layout.
// Note: SVG children don't need this adjustment because their visual
// overflow rects are already adjusted by clip path.
if (base::Optional<FloatRect> clip_path_bounding_box =
ClipPathClipper::LocalClipPathBoundingBox(object)) {
Rect box(EnclosingIntRect(*clip_path_bounding_box));
rect.Unite(box);
}
}
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
// In SPv175, visual rects are in the space of their local transform node.
// For SVG, the input rect is in local SVG coordinates in which paint
// offset doesn't apply.
if (!is_svg_child)
rect.MoveBy(Point(context.fragment_data->PaintOffset()));
ExcludeCompositedLayerSubpixelAccumulation<Rect, Point>(object, context,
rect);
// Use EnclosingIntRect to ensure the final visual rect will cover the rect
// in source coordinates no matter if the painting will snap to pixels.
return LayoutRect(EnclosingIntRect(rect));
}
LayoutRect result;
if (context.subtree_flags & PaintInvalidatorContext::kSubtreeSlowPathRect) {
result = SlowMapToVisualRectInAncestorSpace(
object, *context.paint_invalidation_container, rect);
} else if (object == context.paint_invalidation_container) {
result = LayoutRect(rect);
} else {
// For non-root SVG, the input rect is in local SVG coordinates in which
// paint offset doesn't apply.
if (!is_svg_child)
rect.MoveBy(Point(context.fragment_data->PaintOffset()));
auto container_contents_properties =
context.paint_invalidation_container->FirstFragment()
.ContentsProperties();
DCHECK(
!context.paint_invalidation_container->FirstFragment().NextFragment());
if (context.tree_builder_context_->current.transform ==
container_contents_properties.Transform() &&
context.tree_builder_context_->current.clip ==
container_contents_properties.Clip() &&
context.tree_builder_context_->current_effect ==
container_contents_properties.Effect()) {
result = LayoutRect(rect);
} else {
// Use enclosingIntRect to ensure the final visual rect will cover the
// rect in source coordinates no matter if the painting will use pixel
// snapping, when transforms are applied. If there is no transform,
// enclosingIntRect is applied in the last step of paint invalidation
// (see CompositedLayerMapping::setContentsNeedDisplayInRect()).
if (!is_svg_child && context.tree_builder_context_->current.transform !=
container_contents_properties.Transform())
rect = Rect(EnclosingIntRect(rect));
PropertyTreeState current_tree_state(
context.tree_builder_context_->current.transform,
context.tree_builder_context_->current.clip,
context.tree_builder_context_->current_effect);
FloatClipRect float_rect((FloatRect(rect)));
GeometryMapper::LocalToAncestorVisualRect(
current_tree_state, container_contents_properties, float_rect);
result = LayoutRect(float_rect.Rect());
}
// Convert the result to the paint invalidation container's contents space.
// If the paint invalidation container has a transform node associated
// with it (due to scroll or transform), then its PaintOffset
// must be zero or equal to its subpixel accumulation, since in all
// such cases we allocate a paint offset translation transform.
result.MoveBy(
-context.paint_invalidation_container->FirstFragment().PaintOffset());
}
if (!result.IsEmpty())
result.Inflate(object.VisualRectOutsetForRasterEffects());
PaintLayer::MapRectInPaintInvalidationContainerToBacking(
*context.paint_invalidation_container, result);
result.Move(object.ScrollAdjustmentForPaintInvalidation(
*context.paint_invalidation_container));
return result;
}
void PaintInvalidatorContext::MapLocalRectToVisualRectInBacking(
const LayoutObject& object,
LayoutRect& rect) const {
rect = PaintInvalidator::MapLocalRectToVisualRectInBacking<LayoutRect,
LayoutPoint>(
object, rect, *this);
}
const PaintInvalidatorContext*
PaintInvalidatorContext::ParentContextAccessor::ParentContext() const {
return tree_walk_ ? &tree_walk_->ContextAt(parent_context_index_)
.paint_invalidator_context
: nullptr;
}
LayoutRect PaintInvalidator::ComputeVisualRectInBacking(
const LayoutObject& object,
const PaintInvalidatorContext& context) {
if (object.IsSVGChild()) {
FloatRect local_rect = SVGLayoutSupport::LocalVisualRect(object);
return MapLocalRectToVisualRectInBacking<FloatRect, FloatPoint>(
object, local_rect, context);
}
LayoutRect local_rect = object.LocalVisualRect();
return MapLocalRectToVisualRectInBacking<LayoutRect, LayoutPoint>(
object, local_rect, context);
}
static LayoutRect ComputeFragmentLocalSelectionRect(
const NGPaintFragment& fragment) {
if (!fragment.PhysicalFragment().IsText())
return LayoutRect();
const FrameSelection& frame_selection =
fragment.GetLayoutObject()->GetFrame()->Selection();
const LayoutSelectionStatus status =
frame_selection.ComputeLayoutSelectionStatus(fragment);
if (status.start == status.end)
return LayoutRect();
return fragment.ComputeLocalSelectionRect(status).ToLayoutRect();
}
LayoutRect PaintInvalidator::MapFragmentLocalRectToVisualRect(
const LayoutRect& local_rect,
const LayoutObject& object,
const NGPaintFragment& fragment,
const PaintInvalidatorContext& context) {
LayoutRect rect = local_rect;
if (!object.IsBox())
rect.Move(fragment.InlineOffsetToContainerBox().ToLayoutSize());
bool disable_flip = true;
return MapLocalRectToVisualRectInBacking<LayoutRect, LayoutPoint>(
object, rect, context, disable_flip);
}
LayoutPoint PaintInvalidator::ComputeLocationInBacking(
const LayoutObject& object,
const PaintInvalidatorContext& context) {
// In SPv2, locationInBacking is in the space of their local transform node.
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled())
return object.FirstFragment().PaintOffset();
LayoutPoint point;
if (object != context.paint_invalidation_container) {
point.MoveBy(context.fragment_data->PaintOffset());
const auto* container_transform =
context.paint_invalidation_container->FirstFragment()
.ContentsProperties()
.Transform();
if (context.tree_builder_context_->current.transform !=
container_transform) {
FloatRect rect = FloatRect(FloatPoint(point), FloatSize());
GeometryMapper::SourceToDestinationRect(
context.tree_builder_context_->current.transform, container_transform,
rect);
point = LayoutPoint(rect.Location());
}
// Convert the result to the paint invalidation container's contents space.
// If the paint invalidation container has a transform node associated
// with it (due to scroll or transform), then its PaintOffset
// must be zero or equal to its subpixel accumulation, since in all
// such cases we allocate a paint offset translation transform.
point.MoveBy(
-context.paint_invalidation_container->FirstFragment().PaintOffset());
}
if (context.paint_invalidation_container->Layer()->GroupedMapping()) {
FloatPoint float_point(point);
PaintLayer::MapPointInPaintInvalidationContainerToBacking(
*context.paint_invalidation_container, float_point);
point = LayoutPoint(float_point);
}
point.Move(object.ScrollAdjustmentForPaintInvalidation(
*context.paint_invalidation_container));
return point;
}
void PaintInvalidator::UpdatePaintingLayer(const LayoutObject& object,
PaintInvalidatorContext& context) {
if (object.HasLayer() &&
ToLayoutBoxModelObject(object).HasSelfPaintingLayer()) {
context.painting_layer = ToLayoutBoxModelObject(object).Layer();
} else if (object.IsColumnSpanAll() ||
object.IsFloatingWithNonContainingBlockParent()) {
// See LayoutObject::paintingLayer() for the special-cases of floating under
// inline and multicolumn.
context.painting_layer = object.PaintingLayer();
}
if (object.IsLayoutBlockFlow() && ToLayoutBlockFlow(object).ContainsFloats())
context.painting_layer->SetNeedsPaintPhaseFloat();
// Table collapsed borders are painted in PaintPhaseDescendantBlockBackgrounds
// on the table's layer.
if (object.IsTable() && ToLayoutTable(object).HasCollapsedBorders())
context.painting_layer->SetNeedsPaintPhaseDescendantBlockBackgrounds();
// The following flags are for descendants of the layer object only.
if (object == context.painting_layer->GetLayoutObject())
return;
if (object.IsTableSection()) {
const auto& section = ToLayoutTableSection(object);
if (section.Table()->HasColElements())
context.painting_layer->SetNeedsPaintPhaseDescendantBlockBackgrounds();
}
if (object.StyleRef().HasOutline())
context.painting_layer->SetNeedsPaintPhaseDescendantOutlines();
if (object.HasBoxDecorationBackground()
// We also paint overflow controls in background phase.
|| (object.HasOverflowClip() &&
ToLayoutBox(object).GetScrollableArea()->HasOverflowControls())) {
context.painting_layer->SetNeedsPaintPhaseDescendantBlockBackgrounds();
} else if (RuntimeEnabledFeatures::PaintTouchActionRectsEnabled()) {
// Hit testing rects for touch action paint in the background phase.
if (object.EffectiveWhitelistedTouchAction() !=
TouchAction::kTouchActionAuto) {
context.painting_layer->SetNeedsPaintPhaseDescendantBlockBackgrounds();
}
}
}
void PaintInvalidator::UpdatePaintInvalidationContainer(
const LayoutObject& object,
PaintInvalidatorContext& context) {
if (object.IsPaintInvalidationContainer()) {
context.paint_invalidation_container = ToLayoutBoxModelObject(&object);
if (object.StyleRef().IsStackingContext())
context.paint_invalidation_container_for_stacked_contents =
ToLayoutBoxModelObject(&object);
} else if (object.IsLayoutView()) {
// paintInvalidationContainerForStackedContents is only for stacked
// descendants in its own frame, because it doesn't establish stacking
// context for stacked contents in sub-frames.
// Contents stacked in the root stacking context in this frame should use
// this frame's paintInvalidationContainer.
context.paint_invalidation_container_for_stacked_contents =
context.paint_invalidation_container;
} else if (object.IsFloatingWithNonContainingBlockParent() ||
object.IsColumnSpanAll()) {
// In these cases, the object may belong to an ancestor of the current
// paint invalidation container, in paint order.
context.paint_invalidation_container =
&object.ContainerForPaintInvalidation();
} else if (object.StyleRef().IsStacked() &&
// This is to exclude some objects (e.g. LayoutText) inheriting
// stacked style from parent but aren't actually stacked.
object.HasLayer() &&
context.paint_invalidation_container !=
context.paint_invalidation_container_for_stacked_contents) {
// The current object is stacked, so we should use
// m_paintInvalidationContainerForStackedContents as its paint invalidation
// container on which the current object is painted.
context.paint_invalidation_container =
context.paint_invalidation_container_for_stacked_contents;
if (context.subtree_flags &
PaintInvalidatorContext::kSubtreeFullInvalidationForStackedContents) {
context.subtree_flags |=
PaintInvalidatorContext::kSubtreeFullInvalidation;
}
}
if (object == context.paint_invalidation_container) {
// When we hit a new paint invalidation container, we don't need to
// continue forcing a check for paint invalidation, since we're
// descending into a different invalidation container. (For instance if
// our parents were moved, the entire container will just move.)
if (object != context.paint_invalidation_container_for_stacked_contents) {
// However, we need to keep kSubtreeVisualRectUpdate and
// kSubtreeFullInvalidationForStackedContents flags if the current
// object isn't the paint invalidation container of stacked contents.
context.subtree_flags &=
(PaintInvalidatorContext::kSubtreeVisualRectUpdate |
PaintInvalidatorContext::kSubtreeFullInvalidationForStackedContents);
} else {
context.subtree_flags = 0;
}
}
DCHECK(context.paint_invalidation_container ==
object.ContainerForPaintInvalidation());
DCHECK(context.painting_layer == object.PaintingLayer());
}
void PaintInvalidator::UpdateVisualRect(const LayoutObject& object,
FragmentData& fragment_data,
PaintInvalidatorContext& context) {
if (!context.NeedsVisualRectUpdate(object))
return;
DCHECK(context.tree_builder_context_);
DCHECK(context.tree_builder_context_->current.paint_offset ==
fragment_data.PaintOffset());
LayoutRect new_visual_rect = ComputeVisualRectInBacking(object, context);
LayoutPoint new_location;
if (object.IsBoxModelObject()) {
new_location = ComputeLocationInBacking(object, context);
// Location of empty visual rect doesn't affect paint invalidation. Set it
// to newLocation to avoid saving the previous location separately in
// ObjectPaintInvalidator.
if (new_visual_rect.IsEmpty())
new_visual_rect.SetLocation(new_location);
} else {
// Use visual rect location for non-LayoutBoxModelObject because it suffices
// to check whether a visual rect changes for layout caused invalidation.
new_location = new_visual_rect.Location();
}
fragment_data.SetVisualRect(new_visual_rect);
fragment_data.SetLocationInBacking(new_location);
// For LayoutNG, update NGPaintFragments.
if (!RuntimeEnabledFeatures::LayoutNGEnabled())
return;
// TODO(kojii): multi-col needs additional logic. What's needed is to be
// figured out.
if (object.IsLayoutNGMixin()) {
if (NGPaintFragment* fragment = ToLayoutBlockFlow(object).PaintFragment())
fragment->SetVisualRect(new_visual_rect);
// Also check IsInline below. Inline block is LayoutBlockFlow but is also in
// an inline formatting context.
}
if (object.IsInline()) {
// An inline LayoutObject can produce multiple NGPaintFragment. Compute
// VisualRect for each fragment from |new_visual_rect|.
auto fragments = NGPaintFragment::InlineFragmentsFor(&object);
if (fragments.IsInLayoutNGInlineFormattingContext()) {
for (NGPaintFragment* fragment : fragments) {
LayoutRect local_selection_rect =
ComputeFragmentLocalSelectionRect(*fragment);
LayoutRect local_visual_rect =
UnionRect(fragment->SelfInkOverflow(), local_selection_rect);
fragment->SetVisualRect(MapFragmentLocalRectToVisualRect(
local_visual_rect, object, *fragment, context));
LayoutRect selection_visual_rect = MapFragmentLocalRectToVisualRect(
local_selection_rect, object, *fragment, context);
if (selection_visual_rect != fragment->SelectionVisualRect()) {
context.painting_layer->SetNeedsRepaint();
ObjectPaintInvalidator(object).InvalidateDisplayItemClient(
*fragment, PaintInvalidationReason::kSelection);
fragment->SetSelectionVisualRect(selection_visual_rect);
}
}
}
}
}
void PaintInvalidator::InvalidatePaint(
LocalFrameView& frame_view,
const PaintPropertyTreeBuilderContext* tree_builder_context,
PaintInvalidatorContext& context) {
LayoutView* layout_view = frame_view.GetLayoutView();
CHECK(layout_view);
context.paint_invalidation_container =
context.paint_invalidation_container_for_stacked_contents =
&layout_view->ContainerForPaintInvalidation();
context.painting_layer = layout_view->Layer();
context.fragment_data = &layout_view->FirstFragment();
if (tree_builder_context) {
context.tree_builder_context_ = &tree_builder_context->fragments[0];
#if DCHECK_IS_ON()
context.tree_builder_context_actually_needed_ =
tree_builder_context->is_actually_needed;
#endif
}
}
static void InvalidateChromeClient(
const LayoutBoxModelObject& paint_invalidation_container) {
if (paint_invalidation_container.GetDocument().Printing() &&
!RuntimeEnabledFeatures::PrintBrowserEnabled())
return;
DCHECK(paint_invalidation_container.IsLayoutView());
DCHECK(!paint_invalidation_container.IsPaintInvalidationContainer());
auto* frame_view = paint_invalidation_container.GetFrameView();
DCHECK(!frame_view->GetFrame().OwnerLayoutObject());
if (auto* client = ToChromeClient(frame_view->GetChromeClient())) {
client->InvalidateRect(frame_view->VisibleContentRect());
}
}
void PaintInvalidator::UpdateEmptyVisualRectFlag(
const LayoutObject& object,
PaintInvalidatorContext& context) {
bool is_paint_invalidation_container =
object == context.paint_invalidation_container;
// Content under transforms needs to invalidate, even if visual
// rects before and after update were the same. This is because
// we don't know whether this transform will end up composited in
// SPv2, so such transforms are painted even if not visible
// due to ancestor clips. This does not apply in SPv1 mode when
// crossing paint invalidation container boundaries.
if (is_paint_invalidation_container) {
// Remove the flag when crossing paint invalidation container boundaries.
context.subtree_flags &=
~PaintInvalidatorContext::kInvalidateEmptyVisualRect;
} else if (object.StyleRef().HasTransform()) {
context.subtree_flags |=
PaintInvalidatorContext::kInvalidateEmptyVisualRect;
}
}
void PaintInvalidator::InvalidatePaint(
const LayoutObject& object,
const PaintPropertyTreeBuilderContext* tree_builder_context,
PaintInvalidatorContext& context) {
TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("blink.invalidation"),
"PaintInvalidator::InvalidatePaint()", "object",
object.DebugName().Ascii());
if (object.IsSVGHiddenContainer()) {
context.subtree_flags |= PaintInvalidatorContext::kSubtreeNoInvalidation;
}
if (context.subtree_flags & PaintInvalidatorContext::kSubtreeNoInvalidation)
return;
object.GetMutableForPainting().EnsureIsReadyForPaintInvalidation();
UpdatePaintingLayer(object, context);
// TODO(chrishtr): refactor to remove these slow paths by expanding their
// LocalVisualRect to include repeated locations.
if (object.IsTableSection()) {
const auto& section = ToLayoutTableSection(object);
if (section.IsRepeatingHeaderGroup() || section.IsRepeatingFooterGroup())
context.subtree_flags |= PaintInvalidatorContext::kSubtreeSlowPathRect;
}
if (object.IsFixedPositionObjectInPagedMedia())
context.subtree_flags |= PaintInvalidatorContext::kSubtreeSlowPathRect;
UpdatePaintInvalidationContainer(object, context);
UpdateEmptyVisualRectFlag(object, context);
if (!object.ShouldCheckForPaintInvalidation() && !context.NeedsSubtreeWalk())
return;
unsigned tree_builder_index = 0;
for (auto *fragment_data = &object.GetMutableForPainting().FirstFragment();
fragment_data;
fragment_data = fragment_data->NextFragment(), tree_builder_index++) {
context.old_visual_rect = fragment_data->VisualRect();
context.old_location = fragment_data->LocationInBacking();
context.fragment_data = fragment_data;
DCHECK(!tree_builder_context ||
tree_builder_index < tree_builder_context->fragments.size());
{
#if DCHECK_IS_ON()
bool is_actually_needed =
tree_builder_context && tree_builder_context->is_actually_needed;
FindObjectVisualRectNeedingUpdateScope finder(
object, *fragment_data, context, is_actually_needed);
context.tree_builder_context_actually_needed_ = is_actually_needed;
#endif
if (tree_builder_context) {
context.tree_builder_context_ =
&tree_builder_context->fragments[tree_builder_index];
} else {
context.tree_builder_context_ = nullptr;
}
UpdateVisualRect(object, *fragment_data, context);
}
PaintInvalidationReason reason = object.InvalidatePaint(context);
switch (reason) {
case PaintInvalidationReason::kDelayedFull:
pending_delayed_paint_invalidations_.push_back(&object);
break;
case PaintInvalidationReason::kSubtree:
context.subtree_flags |=
(PaintInvalidatorContext::kSubtreeFullInvalidation |
PaintInvalidatorContext::
kSubtreeFullInvalidationForStackedContents);
break;
case PaintInvalidationReason::kSVGResource:
context.subtree_flags |=
PaintInvalidatorContext::kSubtreeSVGResourceChange;
break;
default:
break;
}
if (context.old_location != fragment_data->LocationInBacking() &&
!context.painting_layer->SubtreeIsInvisible()) {
context.subtree_flags |=
PaintInvalidatorContext::kSubtreeInvalidationChecking;
}
}
if (object.MayNeedPaintInvalidationSubtree()) {
context.subtree_flags |=
PaintInvalidatorContext::kSubtreeInvalidationChecking;
}
if (context.subtree_flags && context.NeedsVisualRectUpdate(object)) {
// If any subtree flag is set, we also need to pass needsVisualRectUpdate
// requirement to the subtree.
context.subtree_flags |= PaintInvalidatorContext::kSubtreeVisualRectUpdate;
}
if (context.NeedsVisualRectUpdate(object) &&
object.ContainsInlineWithOutlineAndContinuation()) {
// Force subtree visual rect update and invalidation checking to ensure
// invalidation of focus rings when continuation's geometry changes.
context.subtree_flags |=
PaintInvalidatorContext::kSubtreeVisualRectUpdate |
PaintInvalidatorContext::kSubtreeInvalidationChecking;
}
// The object is under a frame for WebViewPlugin, SVG images etc. Need to
// inform the chrome client of the invalidation so that the client will
// initiate painting of the contents. For SPv1 this is done by
// ObjectPaintInvalidator::InvalidatePaintUsingContainer().
// TODO(wangxianzhu): Do we need this for SPv2?
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled() &&
!RuntimeEnabledFeatures::SlimmingPaintV2Enabled() &&
!context.paint_invalidation_container->IsPaintInvalidationContainer() &&
object.GetPaintInvalidationReason() != PaintInvalidationReason::kNone)
InvalidateChromeClient(*context.paint_invalidation_container);
}
void PaintInvalidator::ProcessPendingDelayedPaintInvalidations() {
for (auto* target : pending_delayed_paint_invalidations_) {
target->GetMutableForPainting()
.SetShouldDoFullPaintInvalidationWithoutGeometryChange(
PaintInvalidationReason::kDelayedFull);
}
}
} // namespace blink