| // Copyright 2017 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_text_fragment_painter.h" |
| |
| #include "third_party/blink/renderer/core/editing/editor.h" |
| #include "third_party/blink/renderer/core/editing/frame_selection.h" |
| #include "third_party/blink/renderer/core/editing/markers/composition_marker.h" |
| #include "third_party/blink/renderer/core/editing/markers/document_marker_controller.h" |
| #include "third_party/blink/renderer/core/editing/markers/text_match_marker.h" |
| #include "third_party/blink/renderer/core/frame/local_frame.h" |
| #include "third_party/blink/renderer/core/layout/geometry/logical_rect.h" |
| #include "third_party/blink/renderer/core/layout/layout_list_marker.h" |
| #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h" |
| #include "third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h" |
| #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h" |
| #include "third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment.h" |
| #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" |
| #include "third_party/blink/renderer/core/layout/ng/ng_text_decoration_offset.h" |
| #include "third_party/blink/renderer/core/paint/document_marker_painter.h" |
| #include "third_party/blink/renderer/core/paint/inline_text_box_painter.h" |
| #include "third_party/blink/renderer/core/paint/list_marker_painter.h" |
| #include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h" |
| #include "third_party/blink/renderer/core/paint/ng/ng_text_painter.h" |
| #include "third_party/blink/renderer/core/paint/paint_info.h" |
| #include "third_party/blink/renderer/core/paint/selection_painting_utils.h" |
| #include "third_party/blink/renderer/core/paint/text_painter_base.h" |
| #include "third_party/blink/renderer/core/style/applied_text_decoration.h" |
| #include "third_party/blink/renderer/core/style/computed_style.h" |
| #include "third_party/blink/renderer/platform/fonts/character_range.h" |
| #include "third_party/blink/renderer/platform/graphics/dom_node_id.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 { |
| |
| namespace { |
| |
| Color SelectionBackgroundColor(const Document& document, |
| const ComputedStyle& style, |
| Node* node, |
| Color text_color) { |
| const Color color = |
| SelectionPaintingUtils::SelectionBackgroundColor(document, style, node); |
| if (!color.Alpha()) |
| return Color(); |
| |
| // If the text color ends up being the same as the selection background, |
| // invert the selection background. |
| if (text_color == color) |
| return Color(0xff - color.Red(), 0xff - color.Green(), 0xff - color.Blue()); |
| return color; |
| } |
| |
| // TODO(yosin): Remove |AsDisplayItemClient| once the transition to |
| // |NGFragmentItem| is done. http://crbug.com/982194 |
| inline const NGFragmentItem& AsDisplayItemClient(const NGInlineCursor& cursor) { |
| return *cursor.CurrentItem(); |
| } |
| |
| inline const NGPaintFragment& AsDisplayItemClient( |
| const NGTextPainterCursor& cursor) { |
| return cursor.PaintFragment(); |
| } |
| |
| // TODO(yosin): Remove |GetTextFragmentPaintInfo| once the transition to |
| // |NGFragmentItem| is done. http://crbug.com/982194 |
| inline NGTextFragmentPaintInfo GetTextFragmentPaintInfo( |
| const NGInlineCursor& cursor) { |
| return cursor.CurrentItem()->TextPaintInfo(cursor.Items()); |
| } |
| |
| inline NGTextFragmentPaintInfo GetTextFragmentPaintInfo( |
| const NGTextPainterCursor& cursor) { |
| return cursor.CurrentItem()->PaintInfo(); |
| } |
| |
| // TODO(yosin): Remove |GetLineLeftAndRightForOffsets| once the transition to |
| // |NGFragmentItem| is done. http://crbug.com/982194 |
| inline std::pair<LayoutUnit, LayoutUnit> GetLineLeftAndRightForOffsets( |
| const NGFragmentItem& text_item, |
| StringView text, |
| unsigned start_offset, |
| unsigned end_offset) { |
| return text_item.LineLeftAndRightForOffsets(text, start_offset, end_offset); |
| } |
| |
| inline std::pair<LayoutUnit, LayoutUnit> GetLineLeftAndRightForOffsets( |
| const NGPhysicalTextFragment& text_fragment, |
| StringView text, |
| unsigned start_offset, |
| unsigned end_offset) { |
| return text_fragment.LineLeftAndRightForOffsets(start_offset, end_offset); |
| } |
| |
| // TODO(yosin): Remove |ComputeLayoutSelectionStatus| once the transition to |
| // |NGFragmentItem| is done. http://crbug.com/982194 |
| inline LayoutSelectionStatus ComputeLayoutSelectionStatus( |
| const NGInlineCursor& cursor) { |
| // TODO(yosin): We should implement |NGInlineCursor| version of |
| // ComputeLayoutSelectionStatus |
| return LayoutSelectionStatus(0, 0, SelectSoftLineBreak::kNotSelected); |
| } |
| |
| inline LayoutSelectionStatus ComputeLayoutSelectionStatus( |
| const NGTextPainterCursor& cursor) { |
| return cursor.CurrentItem() |
| ->GetLayoutObject() |
| ->GetDocument() |
| .GetFrame() |
| ->Selection() |
| .ComputeLayoutSelectionStatus(cursor.PaintFragment()); |
| } |
| |
| // TODO(yosin): Remove |ComputeLocalRect| once the transition to |
| // |NGFragmentItem| is done. http://crbug.com/982194 |
| inline PhysicalRect ComputeLocalRect(const NGFragmentItem& text_item, |
| StringView text, |
| unsigned start_offset, |
| unsigned end_offset) { |
| return text_item.LocalRect(text, start_offset, end_offset); |
| } |
| |
| inline PhysicalRect ComputeLocalRect( |
| const NGPhysicalTextFragment& text_fragment, |
| StringView text, |
| unsigned start_offset, |
| unsigned end_offset) { |
| return text_fragment.LocalRect(start_offset, end_offset); |
| } |
| |
| DocumentMarkerVector ComputeMarkersToPaint(Node* node, bool is_ellipsis) { |
| // TODO(yoichio): Handle first-letter |
| auto* text_node = DynamicTo<Text>(node); |
| if (!text_node) |
| return DocumentMarkerVector(); |
| // We don't paint any marker on ellipsis. |
| if (is_ellipsis) |
| return DocumentMarkerVector(); |
| |
| DocumentMarkerController& document_marker_controller = |
| node->GetDocument().Markers(); |
| return document_marker_controller.ComputeMarkersToPaint(*text_node); |
| } |
| |
| unsigned GetTextContentOffset(const Text& text, unsigned offset) { |
| // TODO(yoichio): Sanitize DocumentMarker around text length. |
| const Position position(text, std::min(offset, text.length())); |
| const NGOffsetMapping* const offset_mapping = |
| NGOffsetMapping::GetFor(position); |
| DCHECK(offset_mapping); |
| const base::Optional<unsigned>& ng_offset = |
| offset_mapping->GetTextContentOffset(position); |
| DCHECK(ng_offset.has_value()); |
| return ng_offset.value(); |
| } |
| |
| // ClampOffset modifies |offset| fixed in a range of |text_fragment| start/end |
| // offsets. |
| // |offset| points not each character but each span between character. |
| // With that concept, we can clear catch what is inside start / end. |
| // Suppose we have "foo_bar"('_' is a space). |
| // There are 8 offsets for that: |
| // f o o _ b a r |
| // 0 1 2 3 4 5 6 7 |
| // If "bar" is a TextFragment. That start(), end() {4, 7} correspond this |
| // offset. If a marker has StartOffset / EndOffset as {2, 6}, |
| // ClampOffset returns{ 4,6 }, which represents "ba" on "foo_bar". |
| template <typename TextItem> |
| unsigned ClampOffset(unsigned offset, const TextItem& text_fragment) { |
| return std::min(std::max(offset, text_fragment.StartOffset()), |
| text_fragment.EndOffset()); |
| } |
| |
| void PaintRect(GraphicsContext& context, |
| const PhysicalOffset& location, |
| const PhysicalRect& rect, |
| const Color color) { |
| if (!color.Alpha()) |
| return; |
| if (rect.size.IsEmpty()) |
| return; |
| const IntRect pixel_snapped_rect = |
| PixelSnappedIntRect(PhysicalRect(rect.offset + location, rect.size)); |
| if (!pixel_snapped_rect.IsEmpty()) |
| context.FillRect(pixel_snapped_rect, color); |
| } |
| |
| template <typename TextItem> |
| PhysicalRect MarkerRectForForeground(const TextItem& text_fragment, |
| StringView text, |
| unsigned start_offset, |
| unsigned end_offset) { |
| LayoutUnit start_position, end_position; |
| std::tie(start_position, end_position) = GetLineLeftAndRightForOffsets( |
| text_fragment, text, start_offset, end_offset); |
| |
| const LayoutUnit height = text_fragment.Size() |
| .ConvertToLogical(static_cast<WritingMode>( |
| text_fragment.Style().GetWritingMode())) |
| .block_size; |
| return {start_position, LayoutUnit(), end_position - start_position, height}; |
| } |
| |
| // Copied from InlineTextBoxPainter |
| template <typename TextItem> |
| void PaintDocumentMarkers(GraphicsContext& context, |
| const TextItem& text_fragment, |
| StringView text, |
| const DocumentMarkerVector& markers_to_paint, |
| const PhysicalOffset& box_origin, |
| const ComputedStyle& style, |
| DocumentMarkerPaintPhase marker_paint_phase, |
| NGTextPainter* text_painter) { |
| if (markers_to_paint.IsEmpty()) |
| return; |
| |
| DCHECK(text_fragment.GetNode()); |
| const auto& text_node = To<Text>(*text_fragment.GetNode()); |
| for (const DocumentMarker* marker : markers_to_paint) { |
| const unsigned marker_start_offset = |
| GetTextContentOffset(text_node, marker->StartOffset()); |
| const unsigned marker_end_offset = |
| GetTextContentOffset(text_node, marker->EndOffset()); |
| const unsigned paint_start_offset = |
| ClampOffset(marker_start_offset, text_fragment); |
| const unsigned paint_end_offset = |
| ClampOffset(marker_end_offset, text_fragment); |
| if (paint_start_offset == paint_end_offset) |
| continue; |
| |
| switch (marker->GetType()) { |
| case DocumentMarker::kSpelling: |
| case DocumentMarker::kGrammar: { |
| if (context.Printing()) |
| break; |
| if (marker_paint_phase == DocumentMarkerPaintPhase::kBackground) |
| continue; |
| DocumentMarkerPainter::PaintDocumentMarker( |
| context, box_origin, style, marker->GetType(), |
| MarkerRectForForeground(text_fragment, text, paint_start_offset, |
| paint_end_offset)); |
| } break; |
| |
| case DocumentMarker::kTextMatch: { |
| if (!text_fragment.GetNode() |
| ->GetDocument() |
| .GetFrame() |
| ->GetEditor() |
| .MarkedTextMatchesAreHighlighted()) |
| break; |
| const auto& text_match_marker = To<TextMatchMarker>(*marker); |
| if (marker_paint_phase == DocumentMarkerPaintPhase::kBackground) { |
| const Color color = |
| LayoutTheme::GetTheme().PlatformTextSearchHighlightColor( |
| text_match_marker.IsActiveMatch(), |
| text_fragment.GetNode()->GetDocument().InForcedColorsMode(), |
| style.UsedColorScheme()); |
| PaintRect(context, PhysicalOffset(box_origin), |
| ComputeLocalRect(text_fragment, text, paint_start_offset, |
| paint_end_offset), |
| color); |
| break; |
| } |
| |
| const TextPaintStyle text_style = |
| DocumentMarkerPainter::ComputeTextPaintStyleFrom( |
| style, text_match_marker, |
| text_fragment.GetNode()->GetDocument().InForcedColorsMode()); |
| if (text_style.current_color == Color::kTransparent) |
| break; |
| text_painter->Paint(paint_start_offset, paint_end_offset, |
| paint_end_offset - paint_start_offset, text_style, |
| kInvalidDOMNodeId); |
| } break; |
| |
| case DocumentMarker::kComposition: |
| case DocumentMarker::kActiveSuggestion: |
| case DocumentMarker::kSuggestion: { |
| const auto& styleable_marker = To<StyleableMarker>(*marker); |
| if (marker_paint_phase == DocumentMarkerPaintPhase::kBackground) { |
| PaintRect(context, PhysicalOffset(box_origin), |
| ComputeLocalRect(text_fragment, text, paint_start_offset, |
| paint_end_offset), |
| styleable_marker.BackgroundColor()); |
| break; |
| } |
| const SimpleFontData* font_data = style.GetFont().PrimaryFont(); |
| DocumentMarkerPainter::PaintStyleableMarkerUnderline( |
| context, box_origin, styleable_marker, style, |
| FloatRect(MarkerRectForForeground( |
| text_fragment, text, paint_start_offset, paint_end_offset)), |
| LayoutUnit(font_data->GetFontMetrics().Height())); |
| } break; |
| |
| default: |
| NOTREACHED(); |
| break; |
| } |
| } |
| } |
| |
| } // namespace |
| |
| template <typename Cursor> |
| NGTextFragmentPainter<Cursor>::NGTextFragmentPainter(const Cursor& cursor) |
| : cursor_(cursor) {} |
| |
| // Logic is copied from InlineTextBoxPainter::PaintSelection. |
| // |selection_start| and |selection_end| should be between |
| // [text_fragment.StartOffset(), text_fragment.EndOffset()]. |
| // TODO(yosin): We should implement |NGInlineCursor| version of |
| // |PaintSelection()| |
| static void PaintSelection(GraphicsContext& context, |
| const NGInlineCursor& cursor, |
| Node* node, |
| const Document& document, |
| const ComputedStyle& style, |
| Color text_color, |
| const PhysicalRect& box_rect, |
| const LayoutSelectionStatus& selection_status) {} |
| |
| static void PaintSelection(GraphicsContext& context, |
| const NGTextPainterCursor& cursor, |
| Node* node, |
| const Document& document, |
| const ComputedStyle& style, |
| Color text_color, |
| const PhysicalRect& box_rect, |
| const LayoutSelectionStatus& selection_status) { |
| const Color color = |
| SelectionBackgroundColor(document, style, node, text_color); |
| const PhysicalRect selection_rect = |
| cursor.PaintFragment().ComputeLocalSelectionRectForText(selection_status); |
| PaintRect(context, box_rect.offset, selection_rect, color); |
| } |
| |
| template <typename Cursor> |
| void NGTextFragmentPainter<Cursor>::PaintSymbol( |
| const LayoutObject* layout_object, |
| const ComputedStyle& style, |
| const PhysicalSize box_size, |
| const PaintInfo& paint_info, |
| const PhysicalOffset& paint_offset) { |
| PhysicalRect marker_rect( |
| LayoutListMarker::RelativeSymbolMarkerRect(style, box_size.width)); |
| marker_rect.Move(paint_offset); |
| IntRect rect = PixelSnappedIntRect(marker_rect); |
| |
| ListMarkerPainter::PaintSymbol(paint_info, layout_object, style, rect); |
| } |
| |
| // This is copied from InlineTextBoxPainter::PaintSelection() but lacks of |
| // ltr, expanding new line wrap or so which uses InlineTextBox functions. |
| template <typename Cursor> |
| void NGTextFragmentPainter<Cursor>::Paint(const PaintInfo& paint_info, |
| const PhysicalOffset& paint_offset) { |
| const auto& text_item = *cursor_.CurrentItem(); |
| // We can skip painting if the fragment (including selection) is invisible. |
| if (!text_item.TextLength()) |
| return; |
| const IntRect visual_rect = AsDisplayItemClient(cursor_).VisualRect(); |
| if (visual_rect.IsEmpty()) |
| return; |
| |
| if (!text_item.TextShapeResult() && |
| // A line break's selection tint is still visible. |
| !text_item.IsLineBreak()) |
| return; |
| |
| const NGTextFragmentPaintInfo& fragment_paint_info = |
| GetTextFragmentPaintInfo(cursor_); |
| const LayoutObject* layout_object = text_item.GetLayoutObject(); |
| const ComputedStyle& style = text_item.Style(); |
| PhysicalRect box_rect = AsDisplayItemClient(cursor_).Rect(); |
| const Document& document = layout_object->GetDocument(); |
| const bool is_printing = paint_info.IsPrinting(); |
| |
| // Determine whether or not we're selected. |
| bool have_selection = !is_printing && |
| paint_info.phase != PaintPhase::kTextClip && |
| layout_object->IsSelected(); |
| base::Optional<LayoutSelectionStatus> selection_status; |
| if (have_selection) { |
| selection_status = ComputeLayoutSelectionStatus(cursor_); |
| DCHECK_LE(selection_status->start, selection_status->end); |
| have_selection = selection_status->start < selection_status->end; |
| } |
| if (!have_selection) { |
| // When only painting the selection, don't bother to paint if there is none. |
| if (paint_info.phase == PaintPhase::kSelection) |
| return; |
| |
| // Flow controls (line break, tab, <wbr>) need only selection painting. |
| if (text_item.IsFlowControl()) |
| return; |
| } |
| |
| // The text clip phase already has a DrawingRecorder. Text clips are initiated |
| // only in BoxPainterBase::PaintFillLayer, which is already within a |
| // DrawingRecorder. |
| base::Optional<DrawingRecorder> recorder; |
| if (paint_info.phase != PaintPhase::kTextClip) { |
| if (DrawingRecorder::UseCachedDrawingIfPossible( |
| paint_info.context, AsDisplayItemClient(cursor_), paint_info.phase)) |
| return; |
| recorder.emplace(paint_info.context, AsDisplayItemClient(cursor_), |
| paint_info.phase); |
| } |
| |
| if (UNLIKELY(text_item.IsSymbolMarker())) { |
| // The NGInlineItem of marker might be Split(). So PaintSymbol only if the |
| // StartOffset is 0, or it might be painted several times. |
| if (!fragment_paint_info.from) { |
| PaintSymbol(layout_object, style, box_rect.size, paint_info, |
| paint_offset + box_rect.offset); |
| } |
| return; |
| } |
| |
| // We round the y-axis to ensure consistent line heights. |
| PhysicalOffset adjusted_paint_offset(paint_offset.left, |
| LayoutUnit(paint_offset.top.Round())); |
| box_rect.offset += adjusted_paint_offset; |
| |
| GraphicsContext& context = paint_info.context; |
| |
| // Determine text colors. |
| |
| Node* node = layout_object->GetNode(); |
| TextPaintStyle text_style = |
| TextPainterBase::TextPaintingStyle(document, style, paint_info); |
| TextPaintStyle selection_style = TextPainterBase::SelectionPaintingStyle( |
| document, style, node, have_selection, paint_info, text_style); |
| bool paint_selected_text_only = (paint_info.phase == PaintPhase::kSelection); |
| bool paint_selected_text_separately = |
| !paint_selected_text_only && text_style != selection_style; |
| |
| // Set our font. |
| const Font& font = style.GetFont(); |
| const SimpleFontData* font_data = font.PrimaryFont(); |
| DCHECK(font_data); |
| |
| base::Optional<GraphicsContextStateSaver> state_saver; |
| |
| // 1. Paint backgrounds behind text if needed. Examples of such backgrounds |
| // include selection and composition highlights. |
| // Since NGPaintFragment::ComputeLocalSelectionRectForText() returns |
| // PhysicalRect rather than LogicalRect, we should paint selection |
| // before GraphicsContext flip. |
| // TODO(yoichio): Make NGPhysicalTextFragment::LocalRect and |
| // NGPaintFragment::ComputeLocalSelectionRectForText logical so that we can |
| // paint selection in same fliped dimention as NGTextPainter. |
| const DocumentMarkerVector& markers_to_paint = |
| ComputeMarkersToPaint(node, text_item.IsEllipsis()); |
| if (paint_info.phase != PaintPhase::kSelection && |
| paint_info.phase != PaintPhase::kTextClip && !is_printing) { |
| PaintDocumentMarkers(context, text_item, fragment_paint_info.text, |
| markers_to_paint, box_rect.offset, style, |
| DocumentMarkerPaintPhase::kBackground, nullptr); |
| if (have_selection) { |
| PaintSelection(context, cursor_, node, document, style, |
| selection_style.fill_color, box_rect, *selection_status); |
| } |
| } |
| |
| const WritingMode writing_mode = style.GetWritingMode(); |
| const bool is_horizontal = IsHorizontalWritingMode(writing_mode); |
| if (!is_horizontal) { |
| state_saver.emplace(context); |
| // Because we rotate the GraphicsContext to match the logical direction, |
| // transpose the |box_rect| to match to it. |
| box_rect.size = PhysicalSize(box_rect.Height(), box_rect.Width()); |
| context.ConcatCTM(TextPainterBase::Rotation( |
| box_rect, writing_mode != WritingMode::kSidewaysLr |
| ? TextPainterBase::kClockwise |
| : TextPainterBase::kCounterclockwise)); |
| } |
| |
| // 2. Now paint the foreground, including text and decorations. |
| int ascent = font_data ? font_data->GetFontMetrics().Ascent() : 0; |
| PhysicalOffset text_origin(box_rect.offset.left, |
| box_rect.offset.top + ascent); |
| NGTextPainter text_painter(context, font, fragment_paint_info, visual_rect, |
| text_origin, box_rect, is_horizontal); |
| |
| if (style.GetTextEmphasisMark() != TextEmphasisMark::kNone) { |
| text_painter.SetEmphasisMark(style.TextEmphasisMarkString(), |
| style.GetTextEmphasisPosition()); |
| } |
| |
| DOMNodeId node_id = kInvalidDOMNodeId; |
| if (node) { |
| if (auto* layout_text = ToLayoutTextOrNull(node->GetLayoutObject())) |
| node_id = layout_text->EnsureNodeId(); |
| } |
| |
| const unsigned length = fragment_paint_info.to - fragment_paint_info.from; |
| if (!paint_selected_text_only) { |
| // Paint text decorations except line-through. |
| DecorationInfo decoration_info; |
| bool has_line_through_decoration = false; |
| if (style.TextDecorationsInEffect() != TextDecoration::kNone && |
| // Ellipsis should not have text decorations. This is not defined, but 4 |
| // impls do this. |
| !text_item.IsEllipsis()) { |
| PhysicalOffset local_origin = box_rect.offset; |
| LayoutUnit width = box_rect.Width(); |
| const NGPhysicalBoxFragment* decorating_box = nullptr; |
| const ComputedStyle* decorating_box_style = |
| decorating_box ? &decorating_box->Style() : nullptr; |
| |
| text_painter.ComputeDecorationInfo( |
| decoration_info, box_rect.offset, local_origin, width, |
| style.GetFontBaseline(), style, decorating_box_style); |
| |
| NGTextDecorationOffset decoration_offset( |
| *decoration_info.style, text_item.Style(), decorating_box); |
| text_painter.PaintDecorationsExceptLineThrough( |
| decoration_offset, decoration_info, paint_info, |
| style.AppliedTextDecorations(), text_style, |
| &has_line_through_decoration); |
| } |
| |
| unsigned start_offset = fragment_paint_info.from; |
| unsigned end_offset = fragment_paint_info.to; |
| |
| if (have_selection && paint_selected_text_separately) { |
| // Paint only the text that is not selected. |
| if (start_offset < selection_status->start) { |
| text_painter.Paint(start_offset, selection_status->start, length, |
| text_style, node_id); |
| } |
| if (selection_status->end < end_offset) { |
| text_painter.Paint(selection_status->end, end_offset, length, |
| text_style, node_id); |
| } |
| } else { |
| text_painter.Paint(start_offset, end_offset, length, text_style, node_id); |
| } |
| |
| // Paint line-through decoration if needed. |
| if (has_line_through_decoration) { |
| text_painter.PaintDecorationsOnlyLineThrough( |
| decoration_info, paint_info, style.AppliedTextDecorations(), |
| text_style); |
| } |
| } |
| |
| if (have_selection && |
| (paint_selected_text_only || paint_selected_text_separately)) { |
| // Paint only the text that is selected. |
| text_painter.Paint(selection_status->start, selection_status->end, length, |
| selection_style, node_id); |
| } |
| |
| if (paint_info.phase != PaintPhase::kForeground) |
| return; |
| PaintDocumentMarkers(context, text_item, fragment_paint_info.text, |
| markers_to_paint, box_rect.offset, style, |
| DocumentMarkerPaintPhase::kForeground, &text_painter); |
| } |
| |
| template class NGTextFragmentPainter<NGTextPainterCursor>; |
| template class NGTextFragmentPainter<NGInlineCursor>; |
| |
| } // namespace blink |