// 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/box_painter.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/blink/renderer/core/display_lock/display_lock_context.h" #include "third_party/blink/renderer/core/layout/background_bleed_avoidance.h" #include "third_party/blink/renderer/core/layout/layout_box.h" #include "third_party/blink/renderer/core/layout/layout_object.h" #include "third_party/blink/renderer/core/layout/layout_theme.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.h" #include "third_party/blink/renderer/core/paint/background_image_geometry.h" #include "third_party/blink/renderer/core/paint/box_decoration_data.h" #include "third_party/blink/renderer/core/paint/box_model_object_painter.h" #include "third_party/blink/renderer/core/paint/box_painter_base.h" #include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h" #include "third_party/blink/renderer/core/paint/nine_piece_image_painter.h" #include "third_party/blink/renderer/core/paint/object_painter.h" #include "third_party/blink/renderer/core/paint/paint_info.h" #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h" #include "third_party/blink/renderer/core/paint/rounded_border_geometry.h" #include "third_party/blink/renderer/core/paint/scoped_paint_state.h" #include "third_party/blink/renderer/core/paint/scrollable_area_painter.h" #include "third_party/blink/renderer/core/paint/svg_foreign_object_painter.h" #include "third_party/blink/renderer/core/paint/theme_painter.h" #include "third_party/blink/renderer/platform/geometry/layout_point.h" #include "third_party/blink/renderer/platform/geometry/length_functions.h" #include "third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h" #include "third_party/blink/renderer/platform/graphics/paint/display_item_cache_skipper.h" #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h" #include "third_party/blink/renderer/platform/graphics/paint/scoped_paint_chunk_properties.h" namespace blink { void BoxPainter::Paint(const PaintInfo& paint_info) { // Default implementation. Just pass paint through to the children. ScopedPaintState paint_state(layout_box_, paint_info); PaintChildren(paint_state.GetPaintInfo()); } void BoxPainter::PaintChildren(const PaintInfo& paint_info) { if (paint_info.DescendantPaintingBlocked()) return; PaintInfo child_info(paint_info); for (LayoutObject* child = layout_box_.SlowFirstChild(); child; child = child->NextSibling()) { if (auto* foreign_object = DynamicTo(*child)) { SVGForeignObjectPainter(*foreign_object).PaintLayer(paint_info); } else { child->Paint(child_info); } } } void BoxPainter::PaintBoxDecorationBackground( const PaintInfo& paint_info, const PhysicalOffset& paint_offset) { if (layout_box_.StyleRef().Visibility() != EVisibility::kVisible) return; PhysicalRect paint_rect; const DisplayItemClient* background_client = nullptr; absl::optional contents_paint_state; bool painting_scrolling_background = BoxDecorationData::IsPaintingScrollingBackground(paint_info, layout_box_); IntRect visual_rect; if (painting_scrolling_background) { // For the case where we are painting the background into the scrolling // contents layer of a composited scroller we need to include the entire // overflow rect. paint_rect = layout_box_.PhysicalLayoutOverflowRect(); contents_paint_state.emplace(paint_info, paint_offset, layout_box_); paint_rect.Move(contents_paint_state->PaintOffset()); // The background painting code assumes that the borders are part of the // paint_rect so we expand the paint_rect by the border size when painting // the background into the scrolling contents layer. paint_rect.Expand(layout_box_.BorderBoxOutsets()); background_client = &layout_box_.GetScrollableArea() ->GetScrollingBackgroundDisplayItemClient(); visual_rect = layout_box_.GetScrollableArea()->ScrollingBackgroundVisualRect( paint_offset); } else { paint_rect = layout_box_.PhysicalBorderBoxRect(); paint_rect.Move(paint_offset); background_client = &layout_box_; visual_rect = VisualRect(paint_offset); } // Paint the background if we're visible and this block has a box decoration // (background, border, appearance, or box shadow). const ComputedStyle& style = layout_box_.StyleRef(); if (style.Visibility() == EVisibility::kVisible && layout_box_.HasBoxDecorationBackground()) { PaintBoxDecorationBackgroundWithRect( contents_paint_state ? contents_paint_state->GetPaintInfo() : paint_info, visual_rect, paint_rect, *background_client); } RecordHitTestData(paint_info, paint_rect, *background_client); bool needs_scroll_hit_test = true; if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { // Pre-CompositeAfterPaint, there is no need to emit scroll hit test // display items for composited scrollers because these display items are // only used to create non-fast scrollable regions for non-composited // scrollers. With CompositeAfterPaint, we always paint the scroll hit // test display items but ignore the non-fast region if the scroll was // composited in PaintArtifactCompositor::UpdateNonFastScrollableRegions. if (layout_box_.HasLayer() && layout_box_.Layer()->GetCompositedLayerMapping() && layout_box_.Layer() ->GetCompositedLayerMapping() ->ScrollingContentsLayer()) { needs_scroll_hit_test = false; } } // Record the scroll hit test after the non-scrolling background so // background squashing is not affected. Hit test order would be equivalent // if this were immediately before the non-scrolling background. if (!painting_scrolling_background && needs_scroll_hit_test) RecordScrollHitTestData(paint_info, *background_client); } void BoxPainter::PaintBoxDecorationBackgroundWithRect( const PaintInfo& paint_info, const IntRect& visual_rect, const PhysicalRect& paint_rect, const DisplayItemClient& background_client) { const ComputedStyle& style = layout_box_.StyleRef(); absl::optional cache_skipper; if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() && BoxPainterBase::ShouldSkipPaintUnderInvalidationChecking(layout_box_)) cache_skipper.emplace(paint_info.context); BoxDecorationData box_decoration_data(paint_info, layout_box_); if (!box_decoration_data.ShouldPaint()) return; if (DrawingRecorder::UseCachedDrawingIfPossible( paint_info.context, background_client, DisplayItem::kBoxDecorationBackground)) return; DrawingRecorder recorder(paint_info.context, background_client, DisplayItem::kBoxDecorationBackground, visual_rect); GraphicsContextStateSaver state_saver(paint_info.context, false); bool needs_end_layer = false; // FIXME: Should eventually give the theme control over whether the box // shadow should paint, since controls could have custom shadows of their // own. if (box_decoration_data.ShouldPaintShadow()) { BoxPainterBase::PaintNormalBoxShadow( paint_info, paint_rect, style, PhysicalBoxSides(), !box_decoration_data.ShouldPaintBackground()); } if (BleedAvoidanceIsClipping( box_decoration_data.GetBackgroundBleedAvoidance())) { state_saver.Save(); FloatRoundedRect border = RoundedBorderGeometry::PixelSnappedRoundedBorder(style, paint_rect); paint_info.context.ClipRoundedRect(border); if (box_decoration_data.GetBackgroundBleedAvoidance() == kBackgroundBleedClipLayer) { paint_info.context.BeginLayer(); needs_end_layer = true; } } // If we have a native theme appearance, paint that before painting our // background. The theme will tell us whether or not we should also paint the // CSS background. IntRect snapped_paint_rect(PixelSnappedIntRect(paint_rect)); ThemePainter& theme_painter = LayoutTheme::GetTheme().Painter(); bool theme_painted = box_decoration_data.HasAppearance() && !theme_painter.Paint(layout_box_, paint_info, snapped_paint_rect); if (!theme_painted) { if (box_decoration_data.ShouldPaintBackground()) { PaintBackground(paint_info, paint_rect, box_decoration_data.BackgroundColor(), box_decoration_data.GetBackgroundBleedAvoidance()); } if (box_decoration_data.HasAppearance()) { theme_painter.PaintDecorations(layout_box_.GetNode(), layout_box_.GetDocument(), style, paint_info, snapped_paint_rect); } } if (box_decoration_data.ShouldPaintShadow()) { BoxPainterBase::PaintInsetBoxShadowWithBorderRect(paint_info, paint_rect, style); } // The theme will tell us whether or not we should also paint the CSS // border. if (box_decoration_data.ShouldPaintBorder()) { if (!theme_painted) { theme_painted = box_decoration_data.HasAppearance() && !theme_painter.PaintBorderOnly(layout_box_.GetNode(), style, paint_info, snapped_paint_rect); } if (!theme_painted) { BoxPainterBase::PaintBorder( layout_box_, layout_box_.GetDocument(), layout_box_.GeneratingNode(), paint_info, paint_rect, style, box_decoration_data.GetBackgroundBleedAvoidance()); } } if (needs_end_layer) paint_info.context.EndLayer(); } void BoxPainter::PaintBackground(const PaintInfo& paint_info, const PhysicalRect& paint_rect, const Color& background_color, BackgroundBleedAvoidance bleed_avoidance) { if (layout_box_.BackgroundTransfersToView()) return; if (layout_box_.BackgroundIsKnownToBeObscured()) return; BackgroundImageGeometry geometry(layout_box_); BoxModelObjectPainter box_model_painter(layout_box_); box_model_painter.PaintFillLayers(paint_info, background_color, layout_box_.StyleRef().BackgroundLayers(), paint_rect, geometry, bleed_avoidance); } void BoxPainter::PaintMask(const PaintInfo& paint_info, const PhysicalOffset& paint_offset) { DCHECK_EQ(PaintPhase::kMask, paint_info.phase); if (!layout_box_.HasMask() || layout_box_.StyleRef().Visibility() != EVisibility::kVisible) return; if (DrawingRecorder::UseCachedDrawingIfPossible( paint_info.context, layout_box_, paint_info.phase)) return; PhysicalRect paint_rect(paint_offset, layout_box_.Size()); BoxDrawingRecorder recorder(paint_info.context, layout_box_, paint_info.phase, paint_offset); PaintMaskImages(paint_info, paint_rect); } void BoxPainter::PaintMaskImages(const PaintInfo& paint_info, const PhysicalRect& paint_rect) { // For mask images legacy layout painting handles multi-line boxes by giving // the full width of the element, not the current line box, thereby clipping // the offending edges. BackgroundImageGeometry geometry(layout_box_); BoxModelObjectPainter painter(layout_box_); painter.PaintMaskImages(paint_info, paint_rect, layout_box_, geometry, PhysicalBoxSides()); } void BoxPainter::RecordHitTestData(const PaintInfo& paint_info, const PhysicalRect& paint_rect, const DisplayItemClient& background_client) { // Hit test data are only needed for compositing. This flag is used for for // printing and drag images which do not need hit testing. if (paint_info.GetGlobalPaintFlags() & kGlobalPaintFlattenCompositingLayers) return; // If an object is not visible, it does not participate in hit testing. if (layout_box_.StyleRef().Visibility() != EVisibility::kVisible) return; if (!paint_info.FragmentToPaint(layout_box_)) return; paint_info.context.GetPaintController().RecordHitTestData( background_client, PixelSnappedIntRect(paint_rect), layout_box_.EffectiveAllowedTouchAction(), layout_box_.InsideBlockingWheelEventHandler()); } void BoxPainter::RecordScrollHitTestData( const PaintInfo& paint_info, const DisplayItemClient& background_client) { // Scroll hit test data are only needed for compositing. This flag is used for // printing and drag images which do not need hit testing. if (paint_info.GetGlobalPaintFlags() & kGlobalPaintFlattenCompositingLayers) return; // If an object is not visible, it does not scroll. if (layout_box_.StyleRef().Visibility() != EVisibility::kVisible) return; if (!layout_box_.GetScrollableArea()) return; const auto* fragment = paint_info.FragmentToPaint(layout_box_); if (!fragment) return; // If there is an associated scroll node, emit scroll hit test data. const auto* properties = fragment->PaintProperties(); if (properties && properties->Scroll()) { DCHECK(properties->ScrollTranslation()); // We record scroll hit test data in the local border box properties // instead of the contents properties so that the scroll hit test is not // clipped or scrolled. auto& paint_controller = paint_info.context.GetPaintController(); DCHECK_EQ(fragment->LocalBorderBoxProperties(), paint_controller.CurrentPaintChunkProperties()); paint_controller.RecordScrollHitTestData( background_client, DisplayItem::kScrollHitTest, properties->ScrollTranslation(), VisualRect(fragment->PaintOffset())); } ScrollableAreaPainter(*layout_box_.GetScrollableArea()) .RecordResizerScrollHitTestData(paint_info.context, fragment->PaintOffset()); } IntRect BoxPainter::VisualRect(const PhysicalOffset& paint_offset) { DCHECK(!layout_box_.VisualRectRespectsVisibility() || layout_box_.StyleRef().Visibility() == EVisibility::kVisible); PhysicalRect rect = layout_box_.PhysicalSelfVisualOverflowRect(); rect.Move(paint_offset); return EnclosingIntRect(rect); } } // namespace blink