| // Copyright 2018 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/ng/ng_fieldset_painter.h" |
| |
| #include "third_party/blink/renderer/core/layout/layout_box.h" |
| #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" |
| #include "third_party/blink/renderer/core/layout/ng/ng_relative_utils.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/fieldset_paint_info.h" |
| #include "third_party/blink/renderer/core/paint/ng/ng_box_fragment_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/rounded_border_geometry.h" |
| #include "third_party/blink/renderer/platform/geometry/layout_rect_outsets.h" |
| #include "third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h" |
| #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h" |
| |
| namespace blink { |
| |
| FieldsetPaintInfo NGFieldsetPainter::CreateFieldsetPaintInfo() const { |
| const NGLink* legend = nullptr; |
| if (!fieldset_.Children().empty()) { |
| const auto& first_child = fieldset_.Children().front(); |
| if (first_child->IsRenderedLegend()) |
| legend = &first_child; |
| } |
| const PhysicalSize fieldset_size(fieldset_.Size()); |
| const auto& fragment = fieldset_; |
| LayoutRectOutsets fieldset_borders = fragment.Borders().ToLayoutRectOutsets(); |
| const ComputedStyle& style = fieldset_.Style(); |
| PhysicalRect legend_border_box; |
| if (legend) { |
| legend_border_box.size = (*legend)->Size(); |
| // Unapply relative position of the legend. |
| // Note that legend->Offset() is the offset after applying |
| // position:relative, but the fieldset border painting needs to avoid |
| // the legend position with static position. |
| // |
| // See https://html.spec.whatwg.org/C/#the-fieldset-and-legend-elements |
| // > * If the element has a rendered legend, then the border is expected to |
| // > not be painted behind the rectangle defined as follows, using the |
| // > writing mode of the fieldset: ... |
| // > ... at its static position (ignoring transforms), ... |
| // |
| // The following logic produces wrong results for block direction offsets. |
| // However we don't need them. |
| const WritingDirectionMode writing_direction = style.GetWritingDirection(); |
| const LogicalSize logical_fieldset_content_size = |
| (fieldset_size - PhysicalSize(fieldset_borders.Size()) - |
| PhysicalSize(fragment.Padding().HorizontalSum(), |
| fragment.Padding().VerticalSum())) |
| .ConvertToLogical(writing_direction.GetWritingMode()); |
| LogicalOffset relative_offset = ComputeRelativeOffset( |
| (*legend)->Style(), writing_direction, logical_fieldset_content_size); |
| LogicalOffset legend_logical_offset = |
| legend->Offset().ConvertToLogical(writing_direction, fieldset_size, |
| (*legend)->Size()) - |
| relative_offset; |
| legend_border_box.offset = legend_logical_offset.ConvertToPhysical( |
| writing_direction, fieldset_size, legend_border_box.size); |
| } |
| return FieldsetPaintInfo(style, fieldset_size, fieldset_borders, |
| legend_border_box); |
| } |
| |
| // Paint the fieldset (background, other decorations, and) border, with the |
| // cutout hole for the legend. |
| void NGFieldsetPainter::PaintBoxDecorationBackground( |
| const PaintInfo& paint_info, |
| const PhysicalOffset& paint_offset) { |
| GraphicsContext& graphics_context = paint_info.context; |
| PhysicalSize fieldset_size(fieldset_.Size()); |
| PhysicalRect paint_rect(paint_offset, fieldset_size); |
| BoxDecorationData box_decoration_data(paint_info, fieldset_); |
| // TODO(crbug.com/786475): Fieldset should not scroll. |
| DCHECK(!box_decoration_data.IsPaintingScrollingBackground()); |
| if (!box_decoration_data.ShouldPaint()) |
| return; |
| |
| if (DrawingRecorder::UseCachedDrawingIfPossible( |
| graphics_context, *fieldset_.GetLayoutObject(), paint_info.phase)) |
| return; |
| |
| const ComputedStyle& style = fieldset_.Style(); |
| FieldsetPaintInfo fieldset_paint_info = CreateFieldsetPaintInfo(); |
| PhysicalRect contracted_rect(paint_rect); |
| contracted_rect.Contract(fieldset_paint_info.border_outsets); |
| |
| DrawingRecorder recorder( |
| graphics_context, *fieldset_.GetLayoutObject(), paint_info.phase, |
| NGBoxFragmentPainter(fieldset_).VisualRect(paint_offset)); |
| |
| NGBoxFragmentPainter fragment_painter(fieldset_); |
| if (box_decoration_data.ShouldPaintShadow()) { |
| fragment_painter.PaintNormalBoxShadow(paint_info, contracted_rect, style); |
| } |
| |
| GraphicsContextStateSaver state_saver(graphics_context, false); |
| bool needs_end_layer = false; |
| if (BleedAvoidanceIsClipping( |
| box_decoration_data.GetBackgroundBleedAvoidance())) { |
| state_saver.Save(); |
| FloatRoundedRect border = RoundedBorderGeometry::PixelSnappedRoundedBorder( |
| style, contracted_rect, fieldset_.SidesToInclude()); |
| graphics_context.ClipRoundedRect(border); |
| |
| if (box_decoration_data.GetBackgroundBleedAvoidance() == |
| kBackgroundBleedClipLayer) { |
| graphics_context.BeginLayer(); |
| needs_end_layer = true; |
| } |
| } |
| |
| if (box_decoration_data.ShouldPaintBackground()) { |
| // TODO(eae): Switch to LayoutNG version of BackgroundImageGeometry. |
| BackgroundImageGeometry geometry( |
| *static_cast<const LayoutBoxModelObject*>(fieldset_.GetLayoutObject())); |
| fragment_painter.PaintFillLayers( |
| paint_info, box_decoration_data.BackgroundColor(), |
| style.BackgroundLayers(), contracted_rect, geometry); |
| } |
| if (box_decoration_data.ShouldPaintShadow()) { |
| fragment_painter.PaintInsetBoxShadowWithBorderRect( |
| paint_info, contracted_rect, fieldset_.Style()); |
| } |
| if (box_decoration_data.ShouldPaintBorder()) { |
| // Create a clipping region around the legend and paint the border as |
| // normal. |
| PhysicalRect legend_cutout_rect = fieldset_paint_info.legend_cutout_rect; |
| legend_cutout_rect.Move(paint_rect.offset); |
| graphics_context.ClipOut(PixelSnappedIntRect(legend_cutout_rect)); |
| |
| const LayoutObject* layout_object = fieldset_.GetLayoutObject(); |
| Node* node = layout_object->GeneratingNode(); |
| fragment_painter.PaintBorder( |
| *fieldset_.GetLayoutObject(), layout_object->GetDocument(), node, |
| paint_info, contracted_rect, fieldset_.Style(), |
| box_decoration_data.GetBackgroundBleedAvoidance(), |
| fieldset_.SidesToInclude()); |
| } |
| |
| if (needs_end_layer) |
| graphics_context.EndLayer(); |
| } |
| |
| void NGFieldsetPainter::PaintMask(const PaintInfo& paint_info, |
| const PhysicalOffset& paint_offset) { |
| // TODO(eae): Switch to LayoutNG version of BackgroundImageGeometry. |
| const LayoutObject& layout_object = *fieldset_.GetLayoutObject(); |
| BackgroundImageGeometry geometry( |
| static_cast<const LayoutBoxModelObject&>(layout_object)); |
| |
| NGBoxFragmentPainter ng_box_painter(fieldset_); |
| DrawingRecorder recorder(paint_info.context, layout_object, paint_info.phase, |
| ng_box_painter.VisualRect(paint_offset)); |
| PhysicalRect paint_rect(paint_offset, fieldset_.Size()); |
| paint_rect.Contract(CreateFieldsetPaintInfo().border_outsets); |
| ng_box_painter.PaintMaskImages(paint_info, paint_rect, layout_object, |
| geometry, fieldset_.SidesToInclude()); |
| } |
| |
| } // namespace blink |