blob: e302a2ee157a7491d8b2bda67a8e261982c015cd [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/paint/ReplacedPainter.h"
#include "core/layout/LayoutReplaced.h"
#include "core/layout/api/SelectionState.h"
#include "core/layout/svg/LayoutSVGRoot.h"
#include "core/paint/AdjustPaintOffsetScope.h"
#include "core/paint/BoxPainter.h"
#include "core/paint/ObjectPainter.h"
#include "core/paint/PaintInfo.h"
#include "core/paint/PaintLayer.h"
#include "core/paint/RoundedInnerRectClipper.h"
#include "core/paint/SelectionPaintingUtils.h"
#include "core/paint/compositing/CompositedLayerMapping.h"
#include "platform/graphics/paint/DrawingRecorder.h"
#include "platform/graphics/paint/ScopedPaintChunkProperties.h"
#include "platform/wtf/Optional.h"
namespace blink {
static bool ShouldApplyViewportClip(const LayoutReplaced& layout_replaced) {
return !layout_replaced.IsSVGRoot() ||
ToLayoutSVGRoot(&layout_replaced)->ShouldApplyViewportClip();
}
void ReplacedPainter::Paint(const PaintInfo& paint_info,
const LayoutPoint& paint_offset) {
AdjustPaintOffsetScope adjustment(layout_replaced_, paint_info, paint_offset);
const auto& local_paint_info = adjustment.GetPaintInfo();
auto adjusted_paint_offset = adjustment.AdjustedPaintOffset();
if (!ShouldPaint(local_paint_info, adjusted_paint_offset))
return;
LayoutRect border_rect(adjusted_paint_offset, layout_replaced_.Size());
if (ShouldPaintSelfBlockBackground(local_paint_info.phase)) {
if (layout_replaced_.Style()->Visibility() == EVisibility::kVisible &&
layout_replaced_.HasBoxDecorationBackground()) {
if (layout_replaced_.HasLayer() &&
layout_replaced_.Layer()->GetCompositingState() ==
kPaintsIntoOwnBacking &&
layout_replaced_.Layer()
->GetCompositedLayerMapping()
->DrawsBackgroundOntoContentLayer())
return;
layout_replaced_.PaintBoxDecorationBackground(local_paint_info,
adjusted_paint_offset);
}
// We're done. We don't bother painting any children.
if (local_paint_info.phase == PaintPhase::kSelfBlockBackgroundOnly)
return;
}
if (local_paint_info.phase == PaintPhase::kMask) {
layout_replaced_.PaintMask(local_paint_info, adjusted_paint_offset);
return;
}
if (local_paint_info.phase == PaintPhase::kClippingMask &&
(!layout_replaced_.HasLayer() ||
!layout_replaced_.Layer()->HasCompositedClippingMask()))
return;
if (ShouldPaintSelfOutline(local_paint_info.phase)) {
ObjectPainter(layout_replaced_)
.PaintOutline(local_paint_info, adjusted_paint_offset);
return;
}
if (local_paint_info.phase != PaintPhase::kForeground &&
local_paint_info.phase != PaintPhase::kSelection &&
!layout_replaced_.CanHaveChildren() &&
local_paint_info.phase != PaintPhase::kClippingMask)
return;
if (local_paint_info.phase == PaintPhase::kSelection &&
layout_replaced_.GetSelectionState() == SelectionState::kNone)
return;
{
Optional<RoundedInnerRectClipper> clipper;
Optional<ScopedPaintChunkProperties> chunk_properties;
bool completely_clipped_out = false;
if (layout_replaced_.Style()->HasBorderRadius()) {
if (border_rect.IsEmpty()) {
completely_clipped_out = true;
} else if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
if (!layout_replaced_.IsSVGRoot()) {
if (const auto* fragment =
paint_info.FragmentToPaint(layout_replaced_)) {
const auto* properties = fragment->PaintProperties();
DCHECK(properties && properties->InnerBorderRadiusClip());
chunk_properties.emplace(
local_paint_info.context.GetPaintController(),
properties->InnerBorderRadiusClip(), layout_replaced_,
DisplayItem::PaintPhaseToDrawingType(local_paint_info.phase));
}
}
} else if (ShouldApplyViewportClip(layout_replaced_)) {
// Push a clip if we have a border radius, since we want to round the
// foreground content that gets painted.
FloatRoundedRect rounded_inner_rect =
layout_replaced_.Style()->GetRoundedInnerBorderFor(
border_rect,
LayoutRectOutsets(-(layout_replaced_.PaddingTop() +
layout_replaced_.BorderTop()),
-(layout_replaced_.PaddingRight() +
layout_replaced_.BorderRight()),
-(layout_replaced_.PaddingBottom() +
layout_replaced_.BorderBottom()),
-(layout_replaced_.PaddingLeft() +
layout_replaced_.BorderLeft())),
true, true);
clipper.emplace(layout_replaced_, local_paint_info, border_rect,
rounded_inner_rect, kApplyToDisplayList);
}
}
if (!completely_clipped_out) {
if (local_paint_info.phase == PaintPhase::kClippingMask) {
BoxPainter(layout_replaced_)
.PaintClippingMask(local_paint_info, adjusted_paint_offset);
} else {
layout_replaced_.PaintReplaced(local_paint_info, adjusted_paint_offset);
}
}
}
// The selection tint never gets clipped by border-radius rounding, since we
// want it to run right up to the edges of surrounding content.
bool draw_selection_tint =
local_paint_info.phase == PaintPhase::kForeground &&
layout_replaced_.GetSelectionState() != SelectionState::kNone &&
!local_paint_info.IsPrinting();
if (draw_selection_tint && !DrawingRecorder::UseCachedDrawingIfPossible(
local_paint_info.context, layout_replaced_,
DisplayItem::kSelectionTint)) {
LayoutRect selection_painting_rect = layout_replaced_.LocalSelectionRect();
selection_painting_rect.MoveBy(adjusted_paint_offset);
IntRect selection_painting_int_rect =
PixelSnappedIntRect(selection_painting_rect);
DrawingRecorder recorder(local_paint_info.context, layout_replaced_,
DisplayItem::kSelectionTint);
Color selection_bg = SelectionPaintingUtils::SelectionBackgroundColor(
layout_replaced_.GetDocument(), layout_replaced_.StyleRef(),
layout_replaced_.GetNode());
local_paint_info.context.FillRect(selection_painting_int_rect,
selection_bg);
}
}
bool ReplacedPainter::ShouldPaint(
const PaintInfo& paint_info,
const LayoutPoint& adjusted_paint_offset) const {
if (paint_info.phase != PaintPhase::kForeground &&
!ShouldPaintSelfOutline(paint_info.phase) &&
paint_info.phase != PaintPhase::kSelection &&
paint_info.phase != PaintPhase::kMask &&
paint_info.phase != PaintPhase::kClippingMask &&
!ShouldPaintSelfBlockBackground(paint_info.phase))
return false;
if (layout_replaced_.IsTruncated())
return false;
// If we're invisible or haven't received a layout yet, just bail.
// But if it's an SVG root, there can be children, so we'll check visibility
// later.
if (!layout_replaced_.IsSVGRoot() &&
layout_replaced_.Style()->Visibility() != EVisibility::kVisible)
return false;
LayoutRect paint_rect(layout_replaced_.VisualOverflowRect());
paint_rect.Unite(layout_replaced_.LocalSelectionRect());
paint_rect.MoveBy(adjusted_paint_offset);
if (!paint_info.GetCullRect().IntersectsCullRect(paint_rect))
return false;
return true;
}
} // namespace blink