blob: b3c80241f5f486cdaa0a7f503976e702e6f37fac [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_model_object_painter.h"
#include "third_party/blink/renderer/core/layout/layout_block.h"
#include "third_party/blink/renderer/core/layout/layout_box_model_object.h"
#include "third_party/blink/renderer/core/layout/line/root_inline_box.h"
#include "third_party/blink/renderer/core/paint/background_image_geometry.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.h"
#include "third_party/blink/renderer/platform/geometry/layout_point.h"
#include "third_party/blink/renderer/platform/geometry/layout_rect_outsets.h"
#include "third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h"
namespace blink {
namespace {
Node* GeneratingNodeForObject(const LayoutBoxModelObject& box_model) {
Node* node = nullptr;
const LayoutObject* layout_object = &box_model;
for (; layout_object && !node; layout_object = layout_object->Parent()) {
node = layout_object->GeneratingNode();
}
return node;
}
LayoutSize LogicalOffsetOnLine(const InlineFlowBox& flow_box) {
// Compute the offset of the passed flow box when seen as part of an
// unbroken continuous strip (c.f box-decoration-break: slice.)
LayoutUnit logical_offset_on_line;
if (flow_box.IsLeftToRightDirection()) {
for (const InlineFlowBox* curr = flow_box.PrevForSameLayoutObject(); curr;
curr = curr->PrevForSameLayoutObject())
logical_offset_on_line += curr->LogicalWidth();
} else {
for (const InlineFlowBox* curr = flow_box.NextForSameLayoutObject(); curr;
curr = curr->NextForSameLayoutObject())
logical_offset_on_line += curr->LogicalWidth();
}
LayoutSize logical_offset(logical_offset_on_line, LayoutUnit());
return flow_box.IsHorizontal() ? logical_offset
: logical_offset.TransposedSize();
}
} // anonymous namespace
BoxModelObjectPainter::BoxModelObjectPainter(const LayoutBoxModelObject& box,
const InlineFlowBox* flow_box)
: BoxPainterBase(&box.GetDocument(),
box.StyleRef(),
GeneratingNodeForObject(box)),
box_model_(box),
flow_box_(flow_box) {}
bool BoxModelObjectPainter::IsPaintingScrollingBackground(
const LayoutBoxModelObject* box_model_,
const PaintInfo& paint_info) {
if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
// TODO(wangxianzhu): For CAP, remove this method and let callers use
// PaintInfo::IsPaintScrollingBackground() directly.
return paint_info.IsPaintingScrollingBackground();
}
return paint_info.PaintFlags() & kPaintLayerPaintingOverflowContents &&
!(paint_info.PaintFlags() &
kPaintLayerPaintingCompositingBackgroundPhase) &&
box_model_ == paint_info.PaintContainer();
}
void BoxModelObjectPainter::PaintTextClipMask(GraphicsContext& context,
const IntRect& mask_rect,
const LayoutPoint& paint_offset,
bool object_has_multiple_boxes) {
PaintInfo paint_info(context, mask_rect, PaintPhase::kTextClip,
kGlobalPaintNormalPhase, 0);
if (flow_box_) {
LayoutSize local_offset = ToLayoutSize(flow_box_->Location());
if (object_has_multiple_boxes &&
box_model_.StyleRef().BoxDecorationBreak() ==
EBoxDecorationBreak::kSlice) {
local_offset -= LogicalOffsetOnLine(*flow_box_);
}
const RootInlineBox& root = flow_box_->Root();
flow_box_->Paint(paint_info, paint_offset - local_offset, root.LineTop(),
root.LineBottom());
} else if (box_model_.IsLayoutBlock()) {
ToLayoutBlock(box_model_).PaintObject(paint_info, paint_offset);
} else {
// We should go through the above path for LayoutInlines.
DCHECK(!box_model_.IsLayoutInline());
// Other types of objects don't have anything meaningful to paint for text
// clip mask.
}
}
LayoutRect BoxModelObjectPainter::AdjustRectForScrolledContent(
const PaintInfo& paint_info,
const BoxPainterBase::FillLayerInfo& info,
const LayoutRect& rect) {
LayoutRect scrolled_paint_rect = rect;
GraphicsContext& context = paint_info.context;
if (info.is_clipped_with_local_scrolling &&
!IsPaintingScrollingBackground(&box_model_, paint_info)) {
// Clip to the overflow area.
const LayoutBox& this_box = ToLayoutBox(box_model_);
// TODO(chrishtr): this should be pixel-snapped.
context.Clip(FloatRect(this_box.OverflowClipRect(rect.Location())));
// Adjust the paint rect to reflect a scrolled content box with borders at
// the ends.
IntSize offset = this_box.ScrolledContentOffset();
scrolled_paint_rect.Move(-offset);
LayoutRectOutsets border = AdjustedBorderOutsets(info);
scrolled_paint_rect.SetWidth(border.Left() + this_box.ScrollWidth() +
border.Right());
scrolled_paint_rect.SetHeight(this_box.BorderTop() +
this_box.ScrollHeight() +
this_box.BorderBottom());
}
return scrolled_paint_rect;
}
LayoutRectOutsets BoxModelObjectPainter::ComputeBorders() const {
return box_model_.BorderBoxOutsets();
}
LayoutRectOutsets BoxModelObjectPainter::ComputePadding() const {
return box_model_.PaddingOutsets();
}
BoxPainterBase::FillLayerInfo BoxModelObjectPainter::GetFillLayerInfo(
const Color& color,
const FillLayer& bg_layer,
BackgroundBleedAvoidance bleed_avoidance) const {
return BoxPainterBase::FillLayerInfo(
box_model_.GetDocument(), box_model_.StyleRef(),
box_model_.HasOverflowClip(), color, bg_layer, bleed_avoidance,
(flow_box_ ? flow_box_->IncludeLogicalLeftEdge() : true),
(flow_box_ ? flow_box_->IncludeLogicalRightEdge() : true));
}
} // namespace blink