blob: a6b0a5b0fa8963117a1916e544f5123340c20dbc [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/box_painter.h"
#include "base/optional.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_table.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/scoped_paint_state.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/hit_test_display_item.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) {
PaintInfo child_info(paint_info);
for (LayoutObject* child = layout_box_.SlowFirstChild(); child;
child = child->NextSibling()) {
if (child->IsSVGForeignObject()) {
SVGForeignObjectPainter(ToLayoutSVGForeignObject(*child))
.PaintLayer(paint_info);
} else {
child->Paint(child_info);
}
}
}
void BoxPainter::PaintBoxDecorationBackground(const PaintInfo& paint_info,
const LayoutPoint& paint_offset) {
LayoutRect paint_rect;
const DisplayItemClient* background_client = nullptr;
base::Optional<ScopedBoxContentsPaintState> contents_paint_state;
if (BoxModelObjectPainter::IsPaintingScrollingBackground(&layout_box_,
paint_info)) {
// 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.MoveBy(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();
} else {
paint_rect = layout_box_.BorderBoxRect();
paint_rect.MoveBy(paint_offset);
background_client = &layout_box_;
}
// 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,
paint_rect, *background_client);
}
if (RuntimeEnabledFeatures::PaintTouchActionRectsEnabled())
RecordHitTestData(paint_info, paint_rect, *background_client);
}
bool BoxPainter::BackgroundIsKnownToBeOpaque(const PaintInfo& paint_info) {
LayoutRect bounds = BoxModelObjectPainter::IsPaintingScrollingBackground(
&layout_box_, paint_info)
? layout_box_.LayoutOverflowRect()
: layout_box_.SelfVisualOverflowRect();
return layout_box_.BackgroundIsKnownToBeOpaqueInRect(bounds);
}
void BoxPainter::PaintBoxDecorationBackgroundWithRect(
const PaintInfo& paint_info,
const LayoutRect& paint_rect,
const DisplayItemClient& background_client) {
bool painting_scrolling_background =
BoxModelObjectPainter::IsPaintingScrollingBackground(&layout_box_,
paint_info);
const ComputedStyle& style = layout_box_.StyleRef();
base::Optional<DisplayItemCacheSkipper> cache_skipper;
// Disable cache in under-invalidation checking mode for MediaSliderPart
// because we always paint using the latest data (buffered ranges, current
// time and duration) which may be different from the cached data, and for
// delayed-invalidation object because it may change before it's actually
// invalidated. Note that we still report harmless under-invalidation of
// non-delayed-invalidation animated background, which should be ignored.
if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() &&
(style.Appearance() == kMediaSliderPart ||
layout_box_.ShouldDelayFullPaintInvalidation())) {
cache_skipper.emplace(paint_info.context);
}
if (DrawingRecorder::UseCachedDrawingIfPossible(
paint_info.context, background_client,
DisplayItem::kBoxDecorationBackground))
return;
DrawingRecorder recorder(paint_info.context, background_client,
DisplayItem::kBoxDecorationBackground);
BoxDecorationData box_decoration_data(layout_box_);
GraphicsContextStateSaver state_saver(paint_info.context, false);
if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() &&
LayoutRect(EnclosingIntRect(paint_rect)) == paint_rect &&
BackgroundIsKnownToBeOpaque(paint_info))
recorder.SetKnownToBeOpaque();
const auto skip_background = layout_box_.BackgroundTransfersToView() ||
(paint_info.SkipRootBackground() &&
paint_info.PaintContainer() == &layout_box_);
bool needs_end_layer = false;
if (!painting_scrolling_background) {
// FIXME: Should eventually give the theme control over whether the box
// shadow should paint, since controls could have custom shadows of their
// own.
BoxPainterBase::PaintNormalBoxShadow(paint_info, paint_rect, style, true,
true, skip_background);
if (BleedAvoidanceIsClipping(box_decoration_data.bleed_avoidance)) {
state_saver.Save();
FloatRoundedRect border = style.GetRoundedBorderFor(paint_rect);
paint_info.context.ClipRoundedRect(border);
if (box_decoration_data.bleed_avoidance == 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.has_appearance &&
!theme_painter.Paint(layout_box_, paint_info, snapped_paint_rect);
if (!theme_painted && !skip_background) {
PaintBackground(paint_info, paint_rect,
box_decoration_data.background_color,
box_decoration_data.bleed_avoidance);
if (box_decoration_data.has_appearance) {
theme_painter.PaintDecorations(layout_box_.GetNode(),
layout_box_.GetDocument(), style,
paint_info, snapped_paint_rect);
}
}
if (!painting_scrolling_background) {
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.has_border_decoration &&
(!box_decoration_data.has_appearance ||
(!theme_painted &&
LayoutTheme::GetTheme().Painter().PaintBorderOnly(
layout_box_.GetNode(), style, paint_info, snapped_paint_rect))) &&
!(layout_box_.IsTable() &&
ToLayoutTable(&layout_box_)->ShouldCollapseBorders())) {
BoxPainterBase::PaintBorder(
layout_box_, layout_box_.GetDocument(), layout_box_.GeneratingNode(),
paint_info, paint_rect, style, box_decoration_data.bleed_avoidance);
}
}
if (needs_end_layer)
paint_info.context.EndLayer();
}
void BoxPainter::PaintBackground(const PaintInfo& paint_info,
const LayoutRect& 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 LayoutPoint& 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;
DrawingRecorder recorder(paint_info.context, layout_box_, paint_info.phase);
LayoutRect paint_rect = LayoutRect(paint_offset, layout_box_.Size());
PaintMaskImages(paint_info, paint_rect);
}
void BoxPainter::PaintMaskImages(const PaintInfo& paint_info,
const LayoutRect& 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.
bool include_logical_left_edge = true;
bool include_logical_right_edge = true;
BackgroundImageGeometry geometry(layout_box_);
BoxModelObjectPainter painter(layout_box_);
painter.PaintMaskImages(paint_info, paint_rect, layout_box_, geometry,
include_logical_left_edge,
include_logical_right_edge);
}
void BoxPainter::RecordHitTestData(const PaintInfo& paint_info,
const LayoutRect& paint_rect,
const DisplayItemClient& background_client) {
// Hit test display items 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;
auto touch_action = layout_box_.EffectiveWhitelistedTouchAction();
if (touch_action == TouchAction::kTouchActionAuto)
return;
HitTestDisplayItem::Record(paint_info.context, background_client,
HitTestRect(paint_rect, touch_action));
}
} // namespace blink