summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.cc')
-rw-r--r--chromium/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.cc230
1 files changed, 211 insertions, 19 deletions
diff --git a/chromium/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.cc b/chromium/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.cc
index 2b2fa38ac1d..ac5b860c2f5 100644
--- a/chromium/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.cc
+++ b/chromium/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.cc
@@ -4,11 +4,20 @@
#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/ng/geometry/ng_logical_rect.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/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"
@@ -36,6 +45,8 @@ inline bool ShouldPaintTextFragment(const NGPhysicalTextFragment& text_fragment,
// This code path is only called in PaintPhaseForeground whereas we would
// expect PaintPhaseSelection. The existing haveSelection logic in paint()
// tests for != PaintPhaseTextClip.
+ if (text_fragment.IsLineBreak())
+ return true;
if (!text_fragment.Length() || !text_fragment.TextShapeResult())
return false;
@@ -58,6 +69,171 @@ Color SelectionBackgroundColor(const Document& document,
return color;
}
+DocumentMarkerVector ComputeMarkersToPaint(
+ const NGPaintFragment& paint_fragment) {
+ // TODO(yoichio): Handle first-letter
+ Node* const node = paint_fragment.GetNode();
+ if (!node)
+ return DocumentMarkerVector();
+ // We don't paint any marker on ellipsis.
+ if (paint_fragment.PhysicalFragment().StyleVariant() ==
+ NGStyleVariant::kEllipsis)
+ return DocumentMarkerVector();
+
+ DocumentMarkerController& document_marker_controller =
+ node->GetDocument().Markers();
+ return document_marker_controller.ComputeMarkersToPaint(*node);
+}
+
+unsigned GetTextContentOffset(const Text& text, unsigned offset) {
+ const Position position(text, offset);
+ 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".
+unsigned ClampOffset(unsigned offset,
+ const NGPhysicalTextFragment& text_fragment) {
+ return std::min(std::max(offset, text_fragment.StartOffset()),
+ text_fragment.EndOffset());
+}
+
+void PaintRect(GraphicsContext& context,
+ const NGPhysicalOffset& location,
+ const NGPhysicalOffsetRect& rect,
+ const Color color) {
+ if (!color.Alpha())
+ return;
+ if (rect.size.IsEmpty())
+ return;
+ const NGPhysicalOffsetRect global_rect(rect.offset + location, rect.size);
+ context.FillRect(global_rect.ToFloatRect(), color);
+}
+
+LayoutRect MarkerRectForForeground(const NGPhysicalTextFragment& text_fragment,
+ unsigned start_offset,
+ unsigned end_offset) {
+ LayoutUnit start_position, end_position;
+ std::tie(start_position, end_position) =
+ text_fragment.LineLeftAndRightForOffsets(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
+void PaintDocumentMarkers(GraphicsContext& context,
+ const NGPaintFragment& paint_fragment,
+ const DocumentMarkerVector& markers_to_paint,
+ const LayoutPoint& box_origin,
+ const ComputedStyle& style,
+ DocumentMarkerPaintPhase marker_paint_phase,
+ NGTextPainter* text_painter) {
+ if (markers_to_paint.IsEmpty())
+ return;
+
+ const NGPhysicalTextFragment& text_fragment =
+ ToNGPhysicalTextFragmentOrDie(paint_fragment.PhysicalFragment());
+ DCHECK(text_fragment.GetNode());
+ const Text& text = ToTextOrDie(*text_fragment.GetNode());
+ for (const DocumentMarker* marker : markers_to_paint) {
+ const unsigned marker_start_offset =
+ GetTextContentOffset(text, marker->StartOffset());
+ const unsigned marker_end_offset =
+ GetTextContentOffset(text, 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, paint_start_offset,
+ paint_end_offset));
+ } break;
+
+ case DocumentMarker::kTextMatch: {
+ if (!text_fragment.GetNode()
+ ->GetDocument()
+ .GetFrame()
+ ->GetEditor()
+ .MarkedTextMatchesAreHighlighted())
+ break;
+ const TextMatchMarker& text_match_marker =
+ ToTextMatchMarkerOrDie(*marker);
+ if (marker_paint_phase == DocumentMarkerPaintPhase::kBackground) {
+ const Color color =
+ LayoutTheme::GetTheme().PlatformTextSearchHighlightColor(
+ text_match_marker.IsActiveMatch());
+ PaintRect(
+ context, NGPhysicalOffset(box_origin),
+ text_fragment.LocalRect(paint_start_offset, paint_end_offset),
+ color);
+ break;
+ }
+
+ const TextPaintStyle text_style =
+ DocumentMarkerPainter::ComputeTextPaintStyleFrom(style,
+ text_match_marker);
+ 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);
+ } break;
+
+ case DocumentMarker::kComposition:
+ case DocumentMarker::kActiveSuggestion:
+ case DocumentMarker::kSuggestion: {
+ const StyleableMarker& styleable_marker = ToStyleableMarker(*marker);
+ if (marker_paint_phase == DocumentMarkerPaintPhase::kBackground) {
+ PaintRect(
+ context, NGPhysicalOffset(box_origin),
+ text_fragment.LocalRect(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, paint_start_offset,
+ paint_end_offset)),
+ LayoutUnit(font_data->GetFontMetrics().Height()));
+ } break;
+
+ default:
+ NOTREACHED();
+ break;
+ }
+ }
+}
+
} // namespace
NGTextFragmentPainter::NGTextFragmentPainter(
@@ -80,16 +256,10 @@ static void PaintSelection(GraphicsContext& context,
ToNGPhysicalTextFragment(paint_fragment.PhysicalFragment());
const Color color =
SelectionBackgroundColor(document, style, text_fragment, text_color);
- if (!color.Alpha())
- return;
-
- GraphicsContextStateSaver state_saver(context);
const NGPhysicalOffsetRect selection_rect =
- paint_fragment.ComputeLocalSelectionRect(selection_status);
- const NGPhysicalOffsetRect global_rect(
- selection_rect.offset + NGPhysicalOffset(box_rect.Location()),
- selection_rect.size);
- context.FillRect(global_rect.ToFloatRect(), color);
+ paint_fragment.ComputeLocalSelectionRectForText(selection_status);
+ PaintRect(context, NGPhysicalOffset(box_rect.Location()), selection_rect,
+ color);
}
// This is copied from InlineTextBoxPainter::PaintSelection() but lacks of
@@ -146,7 +316,33 @@ void NGTextFragmentPainter::Paint(const PaintInfo& paint_info,
LayoutRect box_rect(box_origin, fragment_.Size().ToLayoutSize());
base::Optional<GraphicsContextStateSaver> state_saver;
- NGLineOrientation orientation = text_fragment.LineOrientation();
+
+ // 1. Paint backgrounds behind text if needed. Examples of such backgrounds
+ // include selection and composition highlights.
+ // Since NGPaintFragment::ComputeLocalSelectionRectForText() returns
+ // NGPhysicalOffsetRect rather than NGLogicalRect, 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(fragment_);
+ if (paint_info.phase != PaintPhase::kSelection &&
+ paint_info.phase != PaintPhase::kTextClip && !is_printing) {
+ PaintDocumentMarkers(context, fragment_, markers_to_paint, box_origin,
+ style, DocumentMarkerPaintPhase::kBackground, nullptr);
+
+ if (have_selection) {
+ PaintSelection(context, fragment_, document, style,
+ selection_style.fill_color, box_rect, selection_status);
+ }
+ }
+
+ // Line break needs only selection painting.
+ if (text_fragment.IsLineBreak())
+ return;
+
+ const NGLineOrientation orientation = text_fragment.LineOrientation();
if (orientation != NGLineOrientation::kHorizontal) {
state_saver.emplace(context);
// Because we rotate the GraphicsContext to be logical, flip the
@@ -159,15 +355,6 @@ void NGTextFragmentPainter::Paint(const PaintInfo& paint_info,
: TextPainterBase::kCounterclockwise));
}
- // 1. Paint backgrounds behind text if needed. Examples of such backgrounds
- // include selection and composition highlights.
- if (have_selection && paint_info.phase != PaintPhase::kSelection &&
- paint_info.phase != PaintPhase::kTextClip && !is_printing) {
- // TODO(yoichio): Implement composition highlights.
- PaintSelection(context, fragment_, document, style,
- selection_style.fill_color, box_rect, selection_status);
- }
-
// 2. Now paint the foreground, including text and decorations.
int ascent = font_data ? font_data->GetFontMetrics().Ascent() : 0;
LayoutPoint text_origin(box_origin.X(), box_origin.Y() + ascent);
@@ -234,6 +421,11 @@ void NGTextFragmentPainter::Paint(const PaintInfo& paint_info,
text_painter.Paint(selection_status.start, selection_status.end, length,
selection_style);
}
+
+ if (paint_info.phase != PaintPhase::kForeground)
+ return;
+ PaintDocumentMarkers(context, fragment_, markers_to_paint, box_origin, style,
+ DocumentMarkerPaintPhase::kForeground, &text_painter);
}
} // namespace blink