Find on page selection color isn't adapted for dark mode.
[WebKit-https.git] / Source / WebCore / rendering / InlineTextBox.cpp
1 /*
2  * (C) 1999 Lars Knoll (knoll@kde.org)
3  * (C) 2000 Dirk Mueller (mueller@kde.org)
4  * Copyright (C) 2004-2017 Apple Inc. All rights reserved.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  *
21  */
22
23 #include "config.h"
24 #include "InlineTextBox.h"
25
26 #include "BreakLines.h"
27 #include "DashArray.h"
28 #include "Document.h"
29 #include "DocumentMarkerController.h"
30 #include "Editor.h"
31 #include "EllipsisBox.h"
32 #include "Frame.h"
33 #include "GraphicsContext.h"
34 #include "HitTestResult.h"
35 #include "ImageBuffer.h"
36 #include "InlineTextBoxStyle.h"
37 #include "MarkedText.h"
38 #include "Page.h"
39 #include "PaintInfo.h"
40 #include "RenderBlock.h"
41 #include "RenderCombineText.h"
42 #include "RenderLineBreak.h"
43 #include "RenderRubyRun.h"
44 #include "RenderRubyText.h"
45 #include "RenderTheme.h"
46 #include "RenderView.h"
47 #include "RenderedDocumentMarker.h"
48 #include "Text.h"
49 #include "TextDecorationPainter.h"
50 #include "TextPaintStyle.h"
51 #include "TextPainter.h"
52 #include <stdio.h>
53 #include <wtf/IsoMallocInlines.h>
54 #include <wtf/text/CString.h>
55 #include <wtf/text/TextStream.h>
56
57 namespace WebCore {
58
59 WTF_MAKE_ISO_ALLOCATED_IMPL(InlineTextBox);
60
61 struct SameSizeAsInlineTextBox : public InlineBox {
62     unsigned variables[1];
63     unsigned short variables2[2];
64     void* pointers[2];
65 };
66
67 COMPILE_ASSERT(sizeof(InlineTextBox) == sizeof(SameSizeAsInlineTextBox), InlineTextBox_should_stay_small);
68
69 typedef WTF::HashMap<const InlineTextBox*, LayoutRect> InlineTextBoxOverflowMap;
70 static InlineTextBoxOverflowMap* gTextBoxesWithOverflow;
71
72 InlineTextBox::~InlineTextBox()
73 {
74     if (!knownToHaveNoOverflow() && gTextBoxesWithOverflow)
75         gTextBoxesWithOverflow->remove(this);
76     TextPainter::removeGlyphDisplayList(*this);
77 }
78
79 void InlineTextBox::markDirty(bool dirty)
80 {
81     if (dirty) {
82         m_len = 0;
83         m_start = 0;
84     }
85     InlineBox::markDirty(dirty);
86 }
87
88 LayoutRect InlineTextBox::logicalOverflowRect() const
89 {
90     if (knownToHaveNoOverflow() || !gTextBoxesWithOverflow)
91         return enclosingIntRect(logicalFrameRect());
92     return gTextBoxesWithOverflow->get(this);
93 }
94
95 void InlineTextBox::setLogicalOverflowRect(const LayoutRect& rect)
96 {
97     ASSERT(!knownToHaveNoOverflow());
98     if (!gTextBoxesWithOverflow)
99         gTextBoxesWithOverflow = new InlineTextBoxOverflowMap;
100     gTextBoxesWithOverflow->add(this, rect);
101 }
102
103 int InlineTextBox::baselinePosition(FontBaseline baselineType) const
104 {
105     if (!parent())
106         return 0;
107     if (&parent()->renderer() == renderer().parent())
108         return parent()->baselinePosition(baselineType);
109     return downcast<RenderBoxModelObject>(*renderer().parent()).baselinePosition(baselineType, isFirstLine(), isHorizontal() ? HorizontalLine : VerticalLine, PositionOnContainingLine);
110 }
111
112 LayoutUnit InlineTextBox::lineHeight() const
113 {
114     if (!renderer().parent())
115         return 0;
116     if (&parent()->renderer() == renderer().parent())
117         return parent()->lineHeight();
118     return downcast<RenderBoxModelObject>(*renderer().parent()).lineHeight(isFirstLine(), isHorizontal() ? HorizontalLine : VerticalLine, PositionOnContainingLine);
119 }
120
121 LayoutUnit InlineTextBox::selectionTop() const
122 {
123     return root().selectionTop();
124 }
125
126 LayoutUnit InlineTextBox::selectionBottom() const
127 {
128     return root().selectionBottom();
129 }
130
131 LayoutUnit InlineTextBox::selectionHeight() const
132 {
133     return root().selectionHeight();
134 }
135
136 bool InlineTextBox::isSelected(unsigned startPosition, unsigned endPosition) const
137 {
138     return clampedOffset(startPosition) < clampedOffset(endPosition);
139 }
140
141 RenderObject::SelectionState InlineTextBox::selectionState()
142 {
143     RenderObject::SelectionState state = renderer().selectionState();
144     if (state == RenderObject::SelectionStart || state == RenderObject::SelectionEnd || state == RenderObject::SelectionBoth) {
145         auto& selection = renderer().view().selection();
146         auto startPos = selection.startPosition();
147         auto endPos = selection.endPosition();
148         // The position after a hard line break is considered to be past its end.
149         ASSERT(start() + len() >= (isLineBreak() ? 1 : 0));
150         unsigned lastSelectable = start() + len() - (isLineBreak() ? 1 : 0);
151
152         bool start = (state != RenderObject::SelectionEnd && startPos >= m_start && startPos < m_start + m_len);
153         bool end = (state != RenderObject::SelectionStart && endPos > m_start && endPos <= lastSelectable);
154         if (start && end)
155             state = RenderObject::SelectionBoth;
156         else if (start)
157             state = RenderObject::SelectionStart;
158         else if (end)
159             state = RenderObject::SelectionEnd;
160         else if ((state == RenderObject::SelectionEnd || startPos < m_start) &&
161                  (state == RenderObject::SelectionStart || endPos > lastSelectable))
162             state = RenderObject::SelectionInside;
163         else if (state == RenderObject::SelectionBoth)
164             state = RenderObject::SelectionNone;
165     }
166
167     // If there are ellipsis following, make sure their selection is updated.
168     if (m_truncation != cNoTruncation && root().ellipsisBox()) {
169         EllipsisBox* ellipsis = root().ellipsisBox();
170         if (state != RenderObject::SelectionNone) {
171             unsigned selectionStart;
172             unsigned selectionEnd;
173             std::tie(selectionStart, selectionEnd) = selectionStartEnd();
174             // The ellipsis should be considered to be selected if the end of
175             // the selection is past the beginning of the truncation and the
176             // beginning of the selection is before or at the beginning of the
177             // truncation.
178             ellipsis->setSelectionState(selectionEnd >= m_truncation && selectionStart <= m_truncation ?
179                 RenderObject::SelectionInside : RenderObject::SelectionNone);
180         } else
181             ellipsis->setSelectionState(RenderObject::SelectionNone);
182     }
183
184     return state;
185 }
186
187 inline const FontCascade& InlineTextBox::lineFont() const
188 {
189     return combinedText() ? combinedText()->textCombineFont() : lineStyle().fontCascade();
190 }
191
192 // FIXME: Share more code with paintMarkedTextBackground().
193 LayoutRect InlineTextBox::localSelectionRect(unsigned startPos, unsigned endPos) const
194 {
195     unsigned sPos = clampedOffset(startPos);
196     unsigned ePos = clampedOffset(endPos);
197
198     if (sPos >= ePos && !(startPos == endPos && startPos >= start() && startPos <= (start() + len())))
199         return { };
200
201     LayoutUnit selectionTop = this->selectionTop();
202     LayoutUnit selectionHeight = this->selectionHeight();
203
204     TextRun textRun = createTextRun();
205
206     LayoutRect selectionRect = LayoutRect(LayoutPoint(logicalLeft(), selectionTop), LayoutSize(m_logicalWidth, selectionHeight));
207     // Avoid measuring the text when the entire line box is selected as an optimization.
208     if (sPos || ePos != textRun.length())
209         lineFont().adjustSelectionRectForText(textRun, selectionRect, sPos, ePos);
210     // FIXME: The computation of the snapped selection rect differs from the computation of this rect
211     // in paintMarkedTextBackground(). See <https://bugs.webkit.org/show_bug.cgi?id=138913>.
212     IntRect snappedSelectionRect = enclosingIntRect(selectionRect);
213     LayoutUnit logicalWidth = snappedSelectionRect.width();
214     if (snappedSelectionRect.x() > logicalRight())
215         logicalWidth  = 0;
216     else if (snappedSelectionRect.maxX() > logicalRight())
217         logicalWidth = logicalRight() - snappedSelectionRect.x();
218
219     LayoutPoint topPoint = isHorizontal() ? LayoutPoint(snappedSelectionRect.x(), selectionTop) : LayoutPoint(selectionTop, snappedSelectionRect.x());
220     LayoutUnit width = isHorizontal() ? logicalWidth : selectionHeight;
221     LayoutUnit height = isHorizontal() ? selectionHeight : logicalWidth;
222
223     return LayoutRect(topPoint, LayoutSize(width, height));
224 }
225
226 void InlineTextBox::deleteLine()
227 {
228     renderer().removeTextBox(*this);
229     delete this;
230 }
231
232 void InlineTextBox::extractLine()
233 {
234     if (extracted())
235         return;
236
237     renderer().extractTextBox(*this);
238 }
239
240 void InlineTextBox::attachLine()
241 {
242     if (!extracted())
243         return;
244     
245     renderer().attachTextBox(*this);
246 }
247
248 float InlineTextBox::placeEllipsisBox(bool flowIsLTR, float visibleLeftEdge, float visibleRightEdge, float ellipsisWidth, float &truncatedWidth, bool& foundBox)
249 {
250     if (foundBox) {
251         m_truncation = cFullTruncation;
252         return -1;
253     }
254
255     // For LTR this is the left edge of the box, for RTL, the right edge in parent coordinates.
256     float ellipsisX = flowIsLTR ? visibleRightEdge - ellipsisWidth : visibleLeftEdge + ellipsisWidth;
257     
258     // Criteria for full truncation:
259     // LTR: the left edge of the ellipsis is to the left of our text run.
260     // RTL: the right edge of the ellipsis is to the right of our text run.
261     bool ltrFullTruncation = flowIsLTR && ellipsisX <= left();
262     bool rtlFullTruncation = !flowIsLTR && ellipsisX >= left() + logicalWidth();
263     if (ltrFullTruncation || rtlFullTruncation) {
264         // Too far.  Just set full truncation, but return -1 and let the ellipsis just be placed at the edge of the box.
265         m_truncation = cFullTruncation;
266         foundBox = true;
267         return -1;
268     }
269
270     bool ltrEllipsisWithinBox = flowIsLTR && (ellipsisX < right());
271     bool rtlEllipsisWithinBox = !flowIsLTR && (ellipsisX > left());
272     if (ltrEllipsisWithinBox || rtlEllipsisWithinBox) {
273         foundBox = true;
274
275         // The inline box may have different directionality than it's parent.  Since truncation
276         // behavior depends both on both the parent and the inline block's directionality, we
277         // must keep track of these separately.
278         bool ltr = isLeftToRightDirection();
279         if (ltr != flowIsLTR) {
280           // Width in pixels of the visible portion of the box, excluding the ellipsis.
281           int visibleBoxWidth = visibleRightEdge - visibleLeftEdge  - ellipsisWidth;
282           ellipsisX = ltr ? left() + visibleBoxWidth : right() - visibleBoxWidth;
283         }
284
285         int offset = offsetForPosition(ellipsisX, false);
286         if (offset == 0) {
287             // No characters should be rendered.  Set ourselves to full truncation and place the ellipsis at the min of our start
288             // and the ellipsis edge.
289             m_truncation = cFullTruncation;
290             truncatedWidth += ellipsisWidth;
291             return flowIsLTR ? std::min(ellipsisX, x()) : std::max(ellipsisX, right() - ellipsisWidth);
292         }
293
294         // Set the truncation index on the text run.
295         m_truncation = offset;
296
297         // If we got here that means that we were only partially truncated and we need to return the pixel offset at which
298         // to place the ellipsis.
299         float widthOfVisibleText = renderer().width(m_start, offset, textPos(), isFirstLine());
300
301         // The ellipsis needs to be placed just after the last visible character.
302         // Where "after" is defined by the flow directionality, not the inline
303         // box directionality.
304         // e.g. In the case of an LTR inline box truncated in an RTL flow then we can
305         // have a situation such as |Hello| -> |...He|
306         truncatedWidth += widthOfVisibleText + ellipsisWidth;
307         if (flowIsLTR)
308             return left() + widthOfVisibleText;
309         else
310             return right() - widthOfVisibleText - ellipsisWidth;
311     }
312     truncatedWidth += logicalWidth();
313     return -1;
314 }
315
316
317
318 bool InlineTextBox::isLineBreak() const
319 {
320     return renderer().style().preserveNewline() && len() == 1 && renderer().text()[start()] == '\n';
321 }
322
323 bool InlineTextBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit /* lineTop */, LayoutUnit /*lineBottom*/,
324     HitTestAction /*hitTestAction*/)
325 {
326     if (!visibleToHitTesting())
327         return false;
328
329     if (isLineBreak())
330         return false;
331
332     if (m_truncation == cFullTruncation)
333         return false;
334
335     FloatRect rect(locationIncludingFlipping(), size());
336     // Make sure truncated text is ignored while hittesting.
337     if (m_truncation != cNoTruncation) {
338         LayoutUnit widthOfVisibleText = renderer().width(m_start, m_truncation, textPos(), isFirstLine());
339
340         if (isHorizontal())
341             renderer().style().isLeftToRightDirection() ? rect.setWidth(widthOfVisibleText) : rect.shiftXEdgeTo(right() - widthOfVisibleText);
342         else
343             rect.setHeight(widthOfVisibleText);
344     }
345
346     rect.moveBy(accumulatedOffset);
347
348     if (locationInContainer.intersects(rect)) {
349         renderer().updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - toLayoutSize(accumulatedOffset)));
350         if (result.addNodeToListBasedTestResult(renderer().textNode(), request, locationInContainer, rect) == HitTestProgress::Stop)
351             return true;
352     }
353     return false;
354 }
355
356 static inline bool emphasisPositionHasNeitherLeftNorRight(OptionSet<TextEmphasisPosition> emphasisPosition)
357 {
358     return !(emphasisPosition & TextEmphasisPosition::Left) && !(emphasisPosition & TextEmphasisPosition::Right);
359 }
360
361 bool InlineTextBox::emphasisMarkExistsAndIsAbove(const RenderStyle& style, std::optional<bool>& above) const
362 {
363     // This function returns true if there are text emphasis marks and they are suppressed by ruby text.
364     if (style.textEmphasisMark() == TextEmphasisMark::None)
365         return false;
366
367     auto emphasisPosition = style.textEmphasisPosition();
368     ASSERT(!((emphasisPosition & TextEmphasisPosition::Over) && (emphasisPosition & TextEmphasisPosition::Under)));
369     ASSERT(!((emphasisPosition & TextEmphasisPosition::Left) && (emphasisPosition & TextEmphasisPosition::Right)));
370     
371     if (emphasisPositionHasNeitherLeftNorRight(emphasisPosition))
372         above = emphasisPosition.contains(TextEmphasisPosition::Over);
373     else if (style.isHorizontalWritingMode())
374         above = emphasisPosition.contains(TextEmphasisPosition::Over);
375     else
376         above = emphasisPosition.contains(TextEmphasisPosition::Right);
377     
378     if ((style.isHorizontalWritingMode() && (emphasisPosition & TextEmphasisPosition::Under))
379         || (!style.isHorizontalWritingMode() && (emphasisPosition & TextEmphasisPosition::Left)))
380         return true; // Ruby text is always over, so it cannot suppress emphasis marks under.
381
382     RenderBlock* containingBlock = renderer().containingBlock();
383     if (!containingBlock->isRubyBase())
384         return true; // This text is not inside a ruby base, so it does not have ruby text over it.
385
386     if (!is<RenderRubyRun>(*containingBlock->parent()))
387         return true; // Cannot get the ruby text.
388
389     RenderRubyText* rubyText = downcast<RenderRubyRun>(*containingBlock->parent()).rubyText();
390
391     // The emphasis marks over are suppressed only if there is a ruby text box and it not empty.
392     return !rubyText || !rubyText->hasLines();
393 }
394
395 struct InlineTextBox::MarkedTextStyle {
396     static bool areBackgroundMarkedTextStylesEqual(const MarkedTextStyle& a, const MarkedTextStyle& b)
397     {
398         return a.backgroundColor == b.backgroundColor;
399     }
400     static bool areForegroundMarkedTextStylesEqual(const MarkedTextStyle& a, const MarkedTextStyle& b)
401     {
402         return a.textStyles == b.textStyles && a.textShadow == b.textShadow && a.alpha == b.alpha;
403     }
404     static bool areDecorationMarkedTextStylesEqual(const MarkedTextStyle& a, const MarkedTextStyle& b)
405     {
406         return a.textDecorationStyles == b.textDecorationStyles && a.textShadow == b.textShadow && a.alpha == b.alpha;
407     }
408
409     Color backgroundColor;
410     TextPaintStyle textStyles;
411     TextDecorationPainter::Styles textDecorationStyles;
412     std::optional<ShadowData> textShadow;
413     float alpha;
414 };
415
416 struct InlineTextBox::StyledMarkedText : MarkedText {
417     StyledMarkedText(const MarkedText& marker)
418         : MarkedText { marker }
419     {
420     }
421
422     MarkedTextStyle style;
423 };
424
425 static MarkedText createMarkedTextFromSelectionInBox(const InlineTextBox& box)
426 {
427     unsigned selectionStart;
428     unsigned selectionEnd;
429     std::tie(selectionStart, selectionEnd) = box.selectionStartEnd();
430     if (selectionStart < selectionEnd)
431         return { selectionStart, selectionEnd, MarkedText::Selection };
432     return { };
433 }
434
435 void InlineTextBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit /*lineTop*/, LayoutUnit /*lineBottom*/)
436 {
437     if (isLineBreak() || !paintInfo.shouldPaintWithinRoot(renderer()) || renderer().style().visibility() != Visibility::Visible
438         || m_truncation == cFullTruncation || paintInfo.phase == PaintPhaseOutline || !m_len)
439         return;
440
441     ASSERT(paintInfo.phase != PaintPhaseSelfOutline && paintInfo.phase != PaintPhaseChildOutlines);
442
443     LayoutUnit logicalLeftSide = logicalLeftVisualOverflow();
444     LayoutUnit logicalRightSide = logicalRightVisualOverflow();
445     LayoutUnit logicalStart = logicalLeftSide + (isHorizontal() ? paintOffset.x() : paintOffset.y());
446     LayoutUnit logicalExtent = logicalRightSide - logicalLeftSide;
447     
448     LayoutUnit paintEnd = isHorizontal() ? paintInfo.rect.maxX() : paintInfo.rect.maxY();
449     LayoutUnit paintStart = isHorizontal() ? paintInfo.rect.x() : paintInfo.rect.y();
450     
451     FloatPoint localPaintOffset(paintOffset);
452     
453     if (logicalStart >= paintEnd || logicalStart + logicalExtent <= paintStart)
454         return;
455
456     bool isPrinting = renderer().document().printing();
457     
458     // Determine whether or not we're selected.
459     bool haveSelection = !isPrinting && paintInfo.phase != PaintPhaseTextClip && selectionState() != RenderObject::SelectionNone;
460     if (!haveSelection && paintInfo.phase == PaintPhaseSelection)
461         // When only painting the selection, don't bother to paint if there is none.
462         return;
463
464     if (m_truncation != cNoTruncation) {
465         if (renderer().containingBlock()->style().isLeftToRightDirection() != isLeftToRightDirection()) {
466             // Make the visible fragment of text hug the edge closest to the rest of the run by moving the origin
467             // at which we start drawing text.
468             // e.g. In the case of LTR text truncated in an RTL Context, the correct behavior is:
469             // |Hello|CBA| -> |...He|CBA|
470             // In order to draw the fragment "He" aligned to the right edge of it's box, we need to start drawing
471             // farther to the right.
472             // NOTE: WebKit's behavior differs from that of IE which appears to just overlay the ellipsis on top of the
473             // truncated string i.e.  |Hello|CBA| -> |...lo|CBA|
474             LayoutUnit widthOfVisibleText = renderer().width(m_start, m_truncation, textPos(), isFirstLine());
475             LayoutUnit widthOfHiddenText = m_logicalWidth - widthOfVisibleText;
476             LayoutSize truncationOffset(isLeftToRightDirection() ? widthOfHiddenText : -widthOfHiddenText, 0);
477             localPaintOffset.move(isHorizontal() ? truncationOffset : truncationOffset.transposedSize());
478         }
479     }
480
481     GraphicsContext& context = paintInfo.context();
482
483     const RenderStyle& lineStyle = this->lineStyle();
484     
485     localPaintOffset.move(0, lineStyle.isHorizontalWritingMode() ? 0 : -logicalHeight());
486
487     FloatPoint boxOrigin = locationIncludingFlipping();
488     boxOrigin.moveBy(localPaintOffset);
489     FloatRect boxRect(boxOrigin, FloatSize(logicalWidth(), logicalHeight()));
490
491     auto* combinedText = this->combinedText();
492
493     bool shouldRotate = !isHorizontal() && !combinedText;
494     if (shouldRotate)
495         context.concatCTM(rotation(boxRect, Clockwise));
496
497     // Determine whether or not we have composition underlines to draw.
498     bool containsComposition = renderer().textNode() && renderer().frame().editor().compositionNode() == renderer().textNode();
499     bool useCustomUnderlines = containsComposition && renderer().frame().editor().compositionUsesCustomUnderlines();
500
501     MarkedTextStyle unmarkedStyle = computeStyleForUnmarkedMarkedText(paintInfo);
502
503     // 1. Paint backgrounds behind text if needed. Examples of such backgrounds include selection
504     // and composition underlines.
505     if (paintInfo.phase != PaintPhaseSelection && paintInfo.phase != PaintPhaseTextClip && !isPrinting) {
506         if (containsComposition && !useCustomUnderlines)
507             paintCompositionBackground(paintInfo, boxOrigin);
508
509         Vector<MarkedText> markedTexts = collectMarkedTextsForDocumentMarkers(TextPaintPhase::Background);
510 #if ENABLE(TEXT_SELECTION)
511         if (haveSelection && !useCustomUnderlines && !context.paintingDisabled()) {
512             auto selectionMarkedText = createMarkedTextFromSelectionInBox(*this);
513             if (!selectionMarkedText.isEmpty())
514                 markedTexts.append(WTFMove(selectionMarkedText));
515         }
516 #endif
517         auto styledMarkedTexts = subdivideAndResolveStyle(markedTexts, unmarkedStyle, paintInfo);
518
519         // Coalesce styles of adjacent marked texts to minimize the number of drawing commands.
520         auto coalescedStyledMarkedTexts = coalesceAdjacentMarkedTexts(styledMarkedTexts, &MarkedTextStyle::areBackgroundMarkedTextStylesEqual);
521
522         paintMarkedTexts(paintInfo, TextPaintPhase::Background, boxRect, coalescedStyledMarkedTexts);
523     }
524
525     // FIXME: Right now, InlineTextBoxes never call addRelevantUnpaintedObject() even though they might
526     // legitimately be unpainted if they are waiting on a slow-loading web font. We should fix that, and
527     // when we do, we will have to account for the fact the InlineTextBoxes do not always have unique
528     // renderers and Page currently relies on each unpainted object having a unique renderer.
529     if (paintInfo.phase == PaintPhaseForeground)
530         renderer().page().addRelevantRepaintedObject(&renderer(), IntRect(boxOrigin.x(), boxOrigin.y(), logicalWidth(), logicalHeight()));
531
532     // 2. Now paint the foreground, including text and decorations like underline/overline (in quirks mode only).
533     bool shouldPaintSelectionForeground = haveSelection && !useCustomUnderlines;
534     Vector<MarkedText> markedTexts;
535     if (paintInfo.phase != PaintPhaseSelection) {
536         // The marked texts for the gaps between document markers and selection are implicitly created by subdividing the entire line.
537         markedTexts.append({ clampedOffset(m_start), clampedOffset(end() + 1), MarkedText::Unmarked });
538         if (!isPrinting) {
539             markedTexts.appendVector(collectMarkedTextsForDocumentMarkers(TextPaintPhase::Foreground));
540
541             bool shouldPaintDraggedContent = !(paintInfo.paintBehavior & PaintBehaviorExcludeSelection);
542             if (shouldPaintDraggedContent) {
543                 auto markedTextsForDraggedContent = collectMarkedTextsForDraggedContent();
544                 if (!markedTextsForDraggedContent.isEmpty()) {
545                     shouldPaintSelectionForeground = false;
546                     markedTexts.appendVector(markedTextsForDraggedContent);
547                 }
548             }
549         }
550     }
551     // The selection marked text acts as a placeholder when computing the marked texts for the gaps...
552     if (shouldPaintSelectionForeground) {
553         ASSERT(!isPrinting);
554         auto selectionMarkedText = createMarkedTextFromSelectionInBox(*this);
555         if (!selectionMarkedText.isEmpty())
556             markedTexts.append(WTFMove(selectionMarkedText));
557     }
558
559     auto styledMarkedTexts = subdivideAndResolveStyle(markedTexts, unmarkedStyle, paintInfo);
560
561     // ... now remove the selection marked text if we are excluding selection.
562     if (!isPrinting && paintInfo.paintBehavior & PaintBehaviorExcludeSelection)
563         styledMarkedTexts.removeAllMatching([] (const StyledMarkedText& markedText) { return markedText.type == MarkedText::Selection; });
564
565     // Coalesce styles of adjacent marked texts to minimize the number of drawing commands.
566     auto coalescedStyledMarkedTexts = coalesceAdjacentMarkedTexts(styledMarkedTexts, &MarkedTextStyle::areForegroundMarkedTextStylesEqual);
567
568     paintMarkedTexts(paintInfo, TextPaintPhase::Foreground, boxRect, coalescedStyledMarkedTexts);
569
570     // Paint decorations
571     auto textDecorations = lineStyle.textDecorationsInEffect();
572     if (!textDecorations.isEmpty() && paintInfo.phase != PaintPhaseSelection) {
573         TextRun textRun = createTextRun();
574         unsigned length = textRun.length();
575         if (m_truncation != cNoTruncation)
576             length = m_truncation;
577         unsigned selectionStart = 0;
578         unsigned selectionEnd = 0;
579         if (haveSelection)
580             std::tie(selectionStart, selectionEnd) = selectionStartEnd();
581
582         FloatRect textDecorationSelectionClipOutRect;
583         if ((paintInfo.paintBehavior & PaintBehaviorExcludeSelection) && selectionStart < selectionEnd && selectionEnd <= length) {
584             textDecorationSelectionClipOutRect = logicalOverflowRect();
585             textDecorationSelectionClipOutRect.moveBy(localPaintOffset);
586             float logicalWidthBeforeRange;
587             float logicalWidthAfterRange;
588             float logicalSelectionWidth = lineFont().widthOfTextRange(textRun, selectionStart, selectionEnd, nullptr, &logicalWidthBeforeRange, &logicalWidthAfterRange);
589             // FIXME: Do we need to handle vertical bottom to top text?
590             if (!isHorizontal()) {
591                 textDecorationSelectionClipOutRect.move(0, logicalWidthBeforeRange);
592                 textDecorationSelectionClipOutRect.setHeight(logicalSelectionWidth);
593             } else if (direction() == RTL) {
594                 textDecorationSelectionClipOutRect.move(logicalWidthAfterRange, 0);
595                 textDecorationSelectionClipOutRect.setWidth(logicalSelectionWidth);
596             } else {
597                 textDecorationSelectionClipOutRect.move(logicalWidthBeforeRange, 0);
598                 textDecorationSelectionClipOutRect.setWidth(logicalSelectionWidth);
599             }
600         }
601
602         // Coalesce styles of adjacent marked texts to minimize the number of drawing commands.
603         auto coalescedStyledMarkedTexts = coalesceAdjacentMarkedTexts(styledMarkedTexts, &MarkedTextStyle::areDecorationMarkedTextStylesEqual);
604
605         paintMarkedTexts(paintInfo, TextPaintPhase::Decoration, boxRect, coalescedStyledMarkedTexts, textDecorationSelectionClipOutRect);
606     }
607
608     // 3. Paint fancy decorations, including composition underlines and platform-specific underlines for spelling errors, grammar errors, et cetera.
609     if (paintInfo.phase == PaintPhaseForeground) {
610         paintPlatformDocumentMarkers(context, boxOrigin);
611
612         if (useCustomUnderlines)
613             paintCompositionUnderlines(paintInfo, boxOrigin);
614     }
615     
616     if (shouldRotate)
617         context.concatCTM(rotation(boxRect, Counterclockwise));
618 }
619
620 unsigned InlineTextBox::clampedOffset(unsigned x) const
621 {
622     unsigned offset = std::max(std::min(x, m_start + m_len), m_start) - m_start;
623     if (m_truncation == cFullTruncation)
624         return offset;
625     if (m_truncation != cNoTruncation)
626         offset = std::min<unsigned>(offset, m_truncation);
627     else if (offset == m_len) {
628         // Fix up the offset if we are combined text or have a hyphen because we manage these embellishments.
629         // That is, they are not reflected in renderer().text(). We treat combined text as a single unit.
630         // We also treat the last codepoint in this box and the hyphen as a single unit.
631         if (auto* combinedText = this->combinedText())
632             offset = combinedText->combinedStringForRendering().length();
633         else if (hasHyphen())
634             offset += lineStyle().hyphenString().length();
635     }
636     return offset;
637 }
638
639 std::pair<unsigned, unsigned> InlineTextBox::selectionStartEnd() const
640 {
641     auto selectionState = renderer().selectionState();
642     if (selectionState == RenderObject::SelectionInside)
643         return { 0, clampedOffset(m_start + m_len) };
644     
645     auto start = renderer().view().selection().startPosition();
646     auto end = renderer().view().selection().endPosition();
647     if (selectionState == RenderObject::SelectionStart)
648         end = renderer().text().length();
649     else if (selectionState == RenderObject::SelectionEnd)
650         start = 0;
651     return { clampedOffset(start), clampedOffset(end) };
652 }
653
654 void InlineTextBox::paintPlatformDocumentMarkers(GraphicsContext& context, const FloatPoint& boxOrigin)
655 {
656     for (auto& markedText : subdivide(collectMarkedTextsForDocumentMarkers(TextPaintPhase::Decoration), OverlapStrategy::Frontmost))
657         paintPlatformDocumentMarker(context, boxOrigin, markedText);
658 }
659
660 void InlineTextBox::paintPlatformDocumentMarker(GraphicsContext& context, const FloatPoint& boxOrigin, const MarkedText& markedText)
661 {
662     // Never print spelling/grammar markers (5327887)
663     if (renderer().document().printing())
664         return;
665
666     if (m_truncation == cFullTruncation)
667         return;
668
669     float start = 0; // start of line to draw, relative to tx
670     float width = m_logicalWidth; // how much line to draw
671
672     // Avoid measuring the text when the entire line box is selected as an optimization.
673     if (markedText.startOffset || markedText.endOffset != clampedOffset(end() + 1)) {
674         // Calculate start & width
675         int deltaY = renderer().style().isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
676         int selHeight = selectionHeight();
677         FloatPoint startPoint(boxOrigin.x(), boxOrigin.y() - deltaY);
678         TextRun run = createTextRun();
679
680         LayoutRect selectionRect = LayoutRect(startPoint, FloatSize(0, selHeight));
681         lineFont().adjustSelectionRectForText(run, selectionRect, markedText.startOffset, markedText.endOffset);
682         IntRect markerRect = enclosingIntRect(selectionRect);
683         start = markerRect.x() - startPoint.x();
684         width = markerRect.width();
685     }
686
687     auto lineStyleForMarkedTextType = [] (MarkedText::Type type) {
688         switch (type) {
689         case MarkedText::SpellingError:
690             return GraphicsContext::DocumentMarkerSpellingLineStyle;
691         case MarkedText::GrammarError:
692             return GraphicsContext::DocumentMarkerGrammarLineStyle;
693         case MarkedText::Correction:
694             return GraphicsContext::DocumentMarkerAutocorrectionReplacementLineStyle;
695         case MarkedText::DictationAlternatives:
696             return GraphicsContext::DocumentMarkerDictationAlternativesLineStyle;
697 #if PLATFORM(IOS)
698         case MarkedText::DictationPhraseWithAlternatives:
699             // FIXME: Rename TextCheckingDictationPhraseWithAlternativesLineStyle and remove the PLATFORM(IOS)-guard.
700             return GraphicsContext::TextCheckingDictationPhraseWithAlternativesLineStyle;
701 #endif
702         default:
703             ASSERT_NOT_REACHED();
704             return GraphicsContext::DocumentMarkerSpellingLineStyle;
705         }
706     };
707
708     // IMPORTANT: The misspelling underline is not considered when calculating the text bounds, so we have to
709     // make sure to fit within those bounds.  This means the top pixel(s) of the underline will overlap the
710     // bottom pixel(s) of the glyphs in smaller font sizes.  The alternatives are to increase the line spacing (bad!!)
711     // or decrease the underline thickness.  The overlap is actually the most useful, and matches what AppKit does.
712     // So, we generally place the underline at the bottom of the text, but in larger fonts that's not so good so
713     // we pin to two pixels under the baseline.
714     int lineThickness = cMisspellingLineThickness;
715     int baseline = lineStyle().fontMetrics().ascent();
716     int descent = logicalHeight() - baseline;
717     int underlineOffset;
718     if (descent <= (2 + lineThickness)) {
719         // Place the underline at the very bottom of the text in small/medium fonts.
720         underlineOffset = logicalHeight() - lineThickness;
721     } else {
722         // In larger fonts, though, place the underline up near the baseline to prevent a big gap.
723         underlineOffset = baseline + 2;
724     }
725     context.drawLineForDocumentMarker(FloatPoint(boxOrigin.x() + start, boxOrigin.y() + underlineOffset), width, lineStyleForMarkedTextType(markedText.type));
726 }
727
728 auto InlineTextBox::computeStyleForUnmarkedMarkedText(const PaintInfo& paintInfo) const -> MarkedTextStyle
729 {
730     auto& lineStyle = this->lineStyle();
731
732     MarkedTextStyle style;
733     style.textDecorationStyles = TextDecorationPainter::stylesForRenderer(renderer(), lineStyle.textDecorationsInEffect(), isFirstLine());
734     style.textStyles = computeTextPaintStyle(renderer().frame(), lineStyle, paintInfo);
735     style.textShadow = ShadowData::clone(paintInfo.forceTextColor() ? nullptr : lineStyle.textShadow());
736     style.alpha = 1;
737     return style;
738 }
739
740 auto InlineTextBox::resolveStyleForMarkedText(const MarkedText& markedText, const MarkedTextStyle& baseStyle, const PaintInfo& paintInfo) -> StyledMarkedText
741 {
742     MarkedTextStyle style = baseStyle;
743     switch (markedText.type) {
744     case MarkedText::Correction:
745     case MarkedText::DictationAlternatives:
746 #if PLATFORM(IOS)
747     // FIXME: See <rdar://problem/8933352>. Also, remove the PLATFORM(IOS)-guard.
748     case MarkedText::DictationPhraseWithAlternatives:
749 #endif
750     case MarkedText::GrammarError:
751     case MarkedText::SpellingError:
752     case MarkedText::Unmarked:
753         break;
754     case MarkedText::DraggedContent:
755         style.alpha = 0.25;
756         break;
757     case MarkedText::Selection: {
758         style.textStyles = computeTextSelectionPaintStyle(style.textStyles, renderer(), lineStyle(), paintInfo, style.textShadow);
759
760         Color selectionBackgroundColor = renderer().selectionBackgroundColor();
761         style.backgroundColor = selectionBackgroundColor;
762         if (selectionBackgroundColor.isValid() && selectionBackgroundColor.alpha() && style.textStyles.fillColor == selectionBackgroundColor)
763             style.backgroundColor = { 0xff - selectionBackgroundColor.red(), 0xff - selectionBackgroundColor.green(), 0xff - selectionBackgroundColor.blue() };
764         break;
765     }
766     case MarkedText::TextMatch: {
767         // Text matches always use the light system appearance.
768         OptionSet<StyleColor::Options> styleColorOptions = { StyleColor::Options::UseSystemAppearance, StyleColor::Options::UseDefaultAppearance };
769 #if PLATFORM(MAC)
770         style.textStyles.fillColor = renderer().theme().systemColor(CSSValueAppleSystemLabel, styleColorOptions);
771 #endif
772         style.backgroundColor = markedText.marker->isActiveMatch() ? renderer().theme().activeTextSearchHighlightColor(styleColorOptions) : renderer().theme().inactiveTextSearchHighlightColor(styleColorOptions);
773         break;
774     }
775     }
776     StyledMarkedText styledMarkedText = markedText;
777     styledMarkedText.style = WTFMove(style);
778     return styledMarkedText;
779 }
780
781 auto InlineTextBox::subdivideAndResolveStyle(const Vector<MarkedText>& textsToSubdivide, const MarkedTextStyle& baseStyle, const PaintInfo& paintInfo) -> Vector<StyledMarkedText>
782 {
783     if (textsToSubdivide.isEmpty())
784         return { };
785
786     auto markedTexts = subdivide(textsToSubdivide);
787
788     // Compute frontmost overlapping styled marked texts.
789     Vector<StyledMarkedText> frontmostMarkedTexts;
790     frontmostMarkedTexts.reserveInitialCapacity(markedTexts.size());
791     frontmostMarkedTexts.uncheckedAppend(resolveStyleForMarkedText(markedTexts[0], baseStyle, paintInfo));
792     for (auto it = markedTexts.begin() + 1, end = markedTexts.end(); it != end; ++it) {
793         StyledMarkedText& previousStyledMarkedText = frontmostMarkedTexts.last();
794         if (previousStyledMarkedText.startOffset == it->startOffset && previousStyledMarkedText.endOffset == it->endOffset) {
795             // Marked texts completely cover each other.
796             previousStyledMarkedText = resolveStyleForMarkedText(*it, previousStyledMarkedText.style, paintInfo);
797             continue;
798         }
799         frontmostMarkedTexts.uncheckedAppend(resolveStyleForMarkedText(*it, baseStyle, paintInfo));
800     }
801
802     return frontmostMarkedTexts;
803 }
804
805 auto InlineTextBox::coalesceAdjacentMarkedTexts(const Vector<StyledMarkedText>& textsToCoalesce, MarkedTextStylesEqualityFunction areMarkedTextStylesEqual) -> Vector<StyledMarkedText>
806 {
807     if (textsToCoalesce.isEmpty())
808         return { };
809
810     auto areAdjacentMarkedTextsWithSameStyle = [&] (const StyledMarkedText& a, const StyledMarkedText& b) {
811         return a.endOffset == b.startOffset && areMarkedTextStylesEqual(a.style, b.style);
812     };
813
814     Vector<StyledMarkedText> styledMarkedTexts;
815     styledMarkedTexts.reserveInitialCapacity(textsToCoalesce.size());
816     styledMarkedTexts.uncheckedAppend(textsToCoalesce[0]);
817     for (auto it = textsToCoalesce.begin() + 1, end = textsToCoalesce.end(); it != end; ++it) {
818         StyledMarkedText& previousStyledMarkedText = styledMarkedTexts.last();
819         if (areAdjacentMarkedTextsWithSameStyle(previousStyledMarkedText, *it)) {
820             previousStyledMarkedText.endOffset = it->endOffset;
821             continue;
822         }
823         styledMarkedTexts.uncheckedAppend(*it);
824     }
825
826     return styledMarkedTexts;
827 }
828
829 Vector<MarkedText> InlineTextBox::collectMarkedTextsForDraggedContent()
830 {
831     using DraggendContentRange = std::pair<unsigned, unsigned>;
832     auto draggedContentRanges = renderer().draggedContentRangesBetweenOffsets(m_start, m_start + m_len);
833     Vector<MarkedText> result = draggedContentRanges.map([this] (const DraggendContentRange& range) -> MarkedText {
834         return { clampedOffset(range.first), clampedOffset(range.second), MarkedText::DraggedContent };
835     });
836     return result;
837 }
838
839 Vector<MarkedText> InlineTextBox::collectMarkedTextsForDocumentMarkers(TextPaintPhase phase)
840 {
841     ASSERT_ARG(phase, phase == TextPaintPhase::Background || phase == TextPaintPhase::Foreground || phase == TextPaintPhase::Decoration);
842
843     if (!renderer().textNode())
844         return { };
845
846     Vector<RenderedDocumentMarker*> markers = renderer().document().markers().markersFor(renderer().textNode());
847
848     auto markedTextTypeForMarkerType = [] (DocumentMarker::MarkerType type) {
849         switch (type) {
850         case DocumentMarker::Spelling:
851             return MarkedText::SpellingError;
852         case DocumentMarker::Grammar:
853             return MarkedText::GrammarError;
854         case DocumentMarker::CorrectionIndicator:
855             return MarkedText::Correction;
856         case DocumentMarker::TextMatch:
857             return MarkedText::TextMatch;
858         case DocumentMarker::DictationAlternatives:
859             return MarkedText::DictationAlternatives;
860 #if PLATFORM(IOS)
861         case DocumentMarker::DictationPhraseWithAlternatives:
862             return MarkedText::DictationPhraseWithAlternatives;
863 #endif
864         default:
865             return MarkedText::Unmarked;
866         }
867     };
868
869     Vector<MarkedText> markedTexts;
870     markedTexts.reserveInitialCapacity(markers.size());
871
872     // Give any document markers that touch this run a chance to draw before the text has been drawn.
873     // Note end() points at the last char, not one past it like endOffset and ranges do.
874     for (auto* marker : markers) {
875         // Collect either the background markers or the foreground markers, but not both
876         switch (marker->type()) {
877         case DocumentMarker::Grammar:
878         case DocumentMarker::Spelling:
879         case DocumentMarker::CorrectionIndicator:
880         case DocumentMarker::Replacement:
881         case DocumentMarker::DictationAlternatives:
882 #if PLATFORM(IOS)
883         // FIXME: Remove the PLATFORM(IOS)-guard.
884         case DocumentMarker::DictationPhraseWithAlternatives:
885 #endif
886             if (phase != TextPaintPhase::Decoration)
887                 continue;
888             break;
889         case DocumentMarker::TextMatch:
890             if (!renderer().frame().editor().markedTextMatchesAreHighlighted())
891                 continue;
892             if (phase == TextPaintPhase::Decoration)
893                 continue;
894             break;
895 #if ENABLE(TELEPHONE_NUMBER_DETECTION)
896         case DocumentMarker::TelephoneNumber:
897             if (!renderer().frame().editor().markedTextMatchesAreHighlighted())
898                 continue;
899             if (phase != TextPaintPhase::Background)
900                 continue;
901             break;
902 #endif
903         default:
904             continue;
905         }
906
907         if (marker->endOffset() <= start()) {
908             // Marker is completely before this run. This might be a marker that sits before the
909             // first run we draw, or markers that were within runs we skipped due to truncation.
910             continue;
911         }
912
913         if (marker->startOffset() > end()) {
914             // Marker is completely after this run, bail. A later run will paint it.
915             break;
916         }
917
918         // Marker intersects this run. Collect it.
919         switch (marker->type()) {
920         case DocumentMarker::Spelling:
921         case DocumentMarker::CorrectionIndicator:
922         case DocumentMarker::DictationAlternatives:
923         case DocumentMarker::Grammar:
924 #if PLATFORM(IOS)
925         // FIXME: See <rdar://problem/8933352>. Also, remove the PLATFORM(IOS)-guard.
926         case DocumentMarker::DictationPhraseWithAlternatives:
927 #endif
928         case DocumentMarker::TextMatch:
929             markedTexts.uncheckedAppend({ clampedOffset(marker->startOffset()), clampedOffset(marker->endOffset()), markedTextTypeForMarkerType(marker->type()), marker });
930             break;
931         case DocumentMarker::Replacement:
932             break;
933 #if ENABLE(TELEPHONE_NUMBER_DETECTION)
934         case DocumentMarker::TelephoneNumber:
935             break;
936 #endif
937         default:
938             ASSERT_NOT_REACHED();
939         }
940     }
941     return markedTexts;
942 }
943
944 FloatPoint InlineTextBox::textOriginFromBoxRect(const FloatRect& boxRect) const
945 {
946     FloatPoint textOrigin { boxRect.x(), boxRect.y() + lineFont().fontMetrics().ascent() };
947     if (auto* combinedText = this->combinedText()) {
948         if (auto newOrigin = combinedText->computeTextOrigin(boxRect))
949             textOrigin = newOrigin.value();
950     }
951     if (isHorizontal())
952         textOrigin.setY(roundToDevicePixel(LayoutUnit { textOrigin.y() }, renderer().document().deviceScaleFactor()));
953     else
954         textOrigin.setX(roundToDevicePixel(LayoutUnit { textOrigin.x() }, renderer().document().deviceScaleFactor()));
955     return textOrigin;
956 }
957
958 void InlineTextBox::paintMarkedTexts(PaintInfo& paintInfo, TextPaintPhase phase, const FloatRect& boxRect, const Vector<StyledMarkedText>& markedTexts, const FloatRect& decorationClipOutRect)
959 {
960     switch (phase) {
961     case TextPaintPhase::Background:
962         for (auto& markedText : markedTexts)
963             paintMarkedTextBackground(paintInfo, boxRect.location(), markedText.style.backgroundColor, markedText.startOffset, markedText.endOffset);
964         return;
965     case TextPaintPhase::Foreground:
966         for (auto& markedText : markedTexts)
967             paintMarkedTextForeground(paintInfo, boxRect, markedText);
968         return;
969     case TextPaintPhase::Decoration:
970         for (auto& markedText : markedTexts)
971             paintMarkedTextDecoration(paintInfo, boxRect, decorationClipOutRect, markedText);
972         return;
973     }
974 }
975
976 void InlineTextBox::paintMarkedTextBackground(PaintInfo& paintInfo, const FloatPoint& boxOrigin, const Color& color, unsigned clampedStartOffset, unsigned clampedEndOffset)
977 {
978     if (clampedStartOffset >= clampedEndOffset)
979         return;
980
981     GraphicsContext& context = paintInfo.context();
982     GraphicsContextStateSaver stateSaver { context };
983     updateGraphicsContext(context, TextPaintStyle { color }); // Don't draw text at all!
984
985     // Note that if the text is truncated, we let the thing being painted in the truncation
986     // draw its own highlight.
987     TextRun textRun = createTextRun();
988
989     const RootInlineBox& rootBox = root();
990     LayoutUnit selectionBottom = rootBox.selectionBottom();
991     LayoutUnit selectionTop = rootBox.selectionTopAdjustedForPrecedingBlock();
992
993     // Use same y positioning and height as for selection, so that when the selection and this subrange are on
994     // the same word there are no pieces sticking out.
995     LayoutUnit deltaY = renderer().style().isFlippedLinesWritingMode() ? selectionBottom - logicalBottom() : logicalTop() - selectionTop;
996     LayoutUnit selectionHeight = std::max<LayoutUnit>(0, selectionBottom - selectionTop);
997
998     LayoutRect selectionRect = LayoutRect(boxOrigin.x(), boxOrigin.y() - deltaY, m_logicalWidth, selectionHeight);
999     lineFont().adjustSelectionRectForText(textRun, selectionRect, clampedStartOffset, clampedEndOffset);
1000
1001     // FIXME: Support painting combined text. See <https://bugs.webkit.org/show_bug.cgi?id=180993>.
1002     context.fillRect(snapRectToDevicePixelsWithWritingDirection(selectionRect, renderer().document().deviceScaleFactor(), textRun.ltr()), color);
1003 }
1004
1005 void InlineTextBox::paintMarkedTextForeground(PaintInfo& paintInfo, const FloatRect& boxRect, const StyledMarkedText& markedText)
1006 {
1007     if (markedText.startOffset >= markedText.endOffset)
1008         return;
1009
1010     GraphicsContext& context = paintInfo.context();
1011     const FontCascade& font = lineFont();
1012     const RenderStyle& lineStyle = this->lineStyle();
1013
1014     float emphasisMarkOffset = 0;
1015     std::optional<bool> emphasisMarkAbove;
1016     bool hasTextEmphasis = emphasisMarkExistsAndIsAbove(lineStyle, emphasisMarkAbove);
1017     const AtomicString& emphasisMark = hasTextEmphasis ? lineStyle.textEmphasisMarkString() : nullAtom();
1018     if (!emphasisMark.isEmpty())
1019         emphasisMarkOffset = (emphasisMarkAbove && *emphasisMarkAbove) ? -font.fontMetrics().ascent() - font.emphasisMarkDescent(emphasisMark) : font.fontMetrics().descent() + font.emphasisMarkAscent(emphasisMark);
1020
1021     TextPainter textPainter { context };
1022     textPainter.setFont(font);
1023     textPainter.setStyle(markedText.style.textStyles);
1024     textPainter.setIsHorizontal(isHorizontal());
1025     if (markedText.style.textShadow) {
1026         textPainter.setShadow(&markedText.style.textShadow.value());
1027         if (lineStyle.hasAppleColorFilter())
1028             textPainter.setShadowColorFilter(&lineStyle.appleColorFilter());
1029     }
1030     textPainter.setEmphasisMark(emphasisMark, emphasisMarkOffset, combinedText());
1031
1032     TextRun textRun = createTextRun();
1033     textPainter.setGlyphDisplayListIfNeeded(*this, paintInfo, font, context, textRun);
1034
1035     GraphicsContextStateSaver stateSaver { context, false };
1036     if (markedText.type == MarkedText::DraggedContent) {
1037         stateSaver.save();
1038         context.setAlpha(markedText.style.alpha);
1039     }
1040     // TextPainter wants the box rectangle and text origin of the entire line box.
1041     textPainter.paintRange(textRun, boxRect, textOriginFromBoxRect(boxRect), markedText.startOffset, markedText.endOffset);
1042 }
1043
1044 void InlineTextBox::paintMarkedTextDecoration(PaintInfo& paintInfo, const FloatRect& boxRect, const FloatRect& clipOutRect, const StyledMarkedText& markedText)
1045 {
1046     if (m_truncation == cFullTruncation)
1047         return;
1048
1049     GraphicsContext& context = paintInfo.context();
1050     updateGraphicsContext(context, markedText.style.textStyles);
1051
1052     bool isCombinedText = combinedText();
1053     if (isCombinedText)
1054         context.concatCTM(rotation(boxRect, Clockwise));
1055
1056     // 1. Compute text selection
1057     unsigned startOffset = markedText.startOffset;
1058     unsigned endOffset = markedText.endOffset;
1059     if (startOffset >= endOffset)
1060         return;
1061
1062     // Note that if the text is truncated, we let the thing being painted in the truncation
1063     // draw its own decoration.
1064     TextRun textRun = createTextRun();
1065
1066     // Avoid measuring the text when the entire line box is selected as an optimization.
1067     FloatRect snappedSelectionRect = boxRect;
1068     if (startOffset || endOffset != textRun.length()) {
1069         LayoutRect selectionRect = { boxRect.x(), boxRect.y(), boxRect.width(), boxRect.height() };
1070         lineFont().adjustSelectionRectForText(textRun, selectionRect, startOffset, endOffset);
1071         snappedSelectionRect = snapRectToDevicePixelsWithWritingDirection(selectionRect, renderer().document().deviceScaleFactor(), textRun.ltr());
1072     }
1073
1074     // 2. Paint
1075     TextDecorationPainter decorationPainter { context, lineStyle().textDecorationsInEffect(), renderer(), isFirstLine(), markedText.style.textDecorationStyles };
1076     decorationPainter.setInlineTextBox(this);
1077     decorationPainter.setFont(lineFont());
1078     decorationPainter.setWidth(snappedSelectionRect.width());
1079     decorationPainter.setBaseline(lineStyle().fontMetrics().ascent());
1080     decorationPainter.setIsHorizontal(isHorizontal());
1081     if (markedText.style.textShadow) {
1082         decorationPainter.setTextShadow(&markedText.style.textShadow.value());
1083         if (lineStyle().hasAppleColorFilter())
1084             decorationPainter.setShadowColorFilter(&lineStyle().appleColorFilter());
1085     }
1086
1087     {
1088         GraphicsContextStateSaver stateSaver { context, false };
1089         bool isDraggedContent = markedText.type == MarkedText::DraggedContent;
1090         if (isDraggedContent || !clipOutRect.isEmpty()) {
1091             stateSaver.save();
1092             if (isDraggedContent)
1093                 context.setAlpha(markedText.style.alpha);
1094             if (!clipOutRect.isEmpty())
1095                 context.clipOut(clipOutRect);
1096         }
1097         decorationPainter.paintTextDecoration(textRun.subRun(startOffset, endOffset - startOffset), textOriginFromBoxRect(snappedSelectionRect), snappedSelectionRect.location());
1098     }
1099
1100     if (isCombinedText)
1101         context.concatCTM(rotation(boxRect, Counterclockwise));
1102 }
1103
1104 void InlineTextBox::paintCompositionBackground(PaintInfo& paintInfo, const FloatPoint& boxOrigin)
1105 {
1106     paintMarkedTextBackground(paintInfo, boxOrigin, Color::compositionFill, clampedOffset(renderer().frame().editor().compositionStart()), clampedOffset(renderer().frame().editor().compositionEnd()));
1107 }
1108
1109 void InlineTextBox::paintCompositionUnderlines(PaintInfo& paintInfo, const FloatPoint& boxOrigin) const
1110 {
1111     if (m_truncation == cFullTruncation)
1112         return;
1113
1114     for (auto& underline : renderer().frame().editor().customCompositionUnderlines()) {
1115         if (underline.endOffset <= m_start) {
1116             // Underline is completely before this run. This might be an underline that sits
1117             // before the first run we draw, or underlines that were within runs we skipped
1118             // due to truncation.
1119             continue;
1120         }
1121
1122         if (underline.startOffset > end())
1123             break; // Underline is completely after this run, bail. A later run will paint it.
1124
1125         // Underline intersects this run. Paint it.
1126         paintCompositionUnderline(paintInfo, boxOrigin, underline);
1127
1128         if (underline.endOffset > end() + 1)
1129             break; // Underline also runs into the next run. Bail now, no more marker advancement.
1130     }
1131 }
1132
1133 static inline void mirrorRTLSegment(float logicalWidth, TextDirection direction, float& start, float width)
1134 {
1135     if (direction == LTR)
1136         return;
1137     start = logicalWidth - width - start;
1138 }
1139
1140 void InlineTextBox::paintCompositionUnderline(PaintInfo& paintInfo, const FloatPoint& boxOrigin, const CompositionUnderline& underline) const
1141 {
1142     if (m_truncation == cFullTruncation)
1143         return;
1144     
1145     float start = 0; // start of line to draw, relative to tx
1146     float width = m_logicalWidth; // how much line to draw
1147     bool useWholeWidth = true;
1148     unsigned paintStart = m_start;
1149     unsigned paintEnd = end() + 1; // end points at the last char, not past it
1150     if (paintStart <= underline.startOffset) {
1151         paintStart = underline.startOffset;
1152         useWholeWidth = false;
1153         start = renderer().width(m_start, paintStart - m_start, textPos(), isFirstLine());
1154     }
1155     if (paintEnd != underline.endOffset) {      // end points at the last char, not past it
1156         paintEnd = std::min(paintEnd, (unsigned)underline.endOffset);
1157         useWholeWidth = false;
1158     }
1159     if (m_truncation != cNoTruncation) {
1160         paintEnd = std::min(paintEnd, (unsigned)m_start + m_truncation);
1161         useWholeWidth = false;
1162     }
1163     if (!useWholeWidth) {
1164         width = renderer().width(paintStart, paintEnd - paintStart, textPos() + start, isFirstLine());
1165         mirrorRTLSegment(m_logicalWidth, direction(), start, width);
1166     }
1167
1168     // Thick marked text underlines are 2px thick as long as there is room for the 2px line under the baseline.
1169     // All other marked text underlines are 1px thick.
1170     // If there's not enough space the underline will touch or overlap characters.
1171     int lineThickness = 1;
1172     int baseline = lineStyle().fontMetrics().ascent();
1173     if (underline.thick && logicalHeight() - baseline >= 2)
1174         lineThickness = 2;
1175
1176     // We need to have some space between underlines of subsequent clauses, because some input methods do not use different underline styles for those.
1177     // We make each line shorter, which has a harmless side effect of shortening the first and last clauses, too.
1178     start += 1;
1179     width -= 2;
1180
1181     GraphicsContext& context = paintInfo.context();
1182     Color underlineColor = underline.compositionUnderlineColor == CompositionUnderlineColor::TextColor ? renderer().style().visitedDependentColorWithColorFilter(CSSPropertyWebkitTextFillColor) : renderer().style().colorByApplyingColorFilter(underline.color);
1183     context.setStrokeColor(underlineColor);
1184     context.setStrokeThickness(lineThickness);
1185     context.drawLineForText(FloatPoint(boxOrigin.x() + start, boxOrigin.y() + logicalHeight() - lineThickness), width, renderer().document().printing());
1186 }
1187
1188 int InlineTextBox::caretMinOffset() const
1189 {
1190     return m_start;
1191 }
1192
1193 int InlineTextBox::caretMaxOffset() const
1194 {
1195     return m_start + m_len;
1196 }
1197
1198 float InlineTextBox::textPos() const
1199 {
1200     // When computing the width of a text run, RenderBlock::computeInlineDirectionPositionsForLine() doesn't include the actual offset
1201     // from the containing block edge in its measurement. textPos() should be consistent so the text are rendered in the same width.
1202     if (logicalLeft() == 0)
1203         return 0;
1204     return logicalLeft() - root().logicalLeft();
1205 }
1206
1207 int InlineTextBox::offsetForPosition(float lineOffset, bool includePartialGlyphs) const
1208 {
1209     if (isLineBreak())
1210         return 0;
1211     if (lineOffset - logicalLeft() > logicalWidth())
1212         return isLeftToRightDirection() ? len() : 0;
1213     if (lineOffset - logicalLeft() < 0)
1214         return isLeftToRightDirection() ? 0 : len();
1215     bool ignoreCombinedText = true;
1216     bool ignoreHyphen = true;
1217     return lineFont().offsetForPosition(createTextRun(ignoreCombinedText, ignoreHyphen), lineOffset - logicalLeft(), includePartialGlyphs);
1218 }
1219
1220 float InlineTextBox::positionForOffset(unsigned offset) const
1221 {
1222     ASSERT(offset >= m_start);
1223     ASSERT(offset <= m_start + len());
1224
1225     if (isLineBreak())
1226         return logicalLeft();
1227
1228     unsigned startOffset;
1229     unsigned endOffset;
1230     if (isLeftToRightDirection()) {
1231         startOffset = 0;
1232         endOffset = clampedOffset(offset);
1233     } else {
1234         startOffset = clampedOffset(offset);
1235         endOffset = m_len;
1236     }
1237
1238     // FIXME: Do we need to add rightBearing here?
1239     LayoutRect selectionRect = LayoutRect(logicalLeft(), 0, 0, 0);
1240     bool ignoreCombinedText = true;
1241     bool ignoreHyphen = true;
1242     TextRun textRun = createTextRun(ignoreCombinedText, ignoreHyphen);
1243     lineFont().adjustSelectionRectForText(textRun, selectionRect, startOffset, endOffset);
1244     return snapRectToDevicePixelsWithWritingDirection(selectionRect, renderer().document().deviceScaleFactor(), textRun.ltr()).maxX();
1245 }
1246
1247 TextRun InlineTextBox::createTextRun(bool ignoreCombinedText, bool ignoreHyphen) const
1248 {
1249     const auto& style = lineStyle();
1250     TextRun textRun { text(ignoreCombinedText, ignoreHyphen), textPos(), expansion(), expansionBehavior(), direction(), dirOverride() || style.rtlOrdering() == Order::Visual, !renderer().canUseSimpleFontCodePath() };
1251     textRun.setTabSize(!style.collapseWhiteSpace(), style.tabSize());
1252     return textRun;
1253 }
1254
1255 String InlineTextBox::text(bool ignoreCombinedText, bool ignoreHyphen) const
1256 {
1257     if (auto* combinedText = this->combinedText()) {
1258         if (ignoreCombinedText)
1259             return renderer().text().substring(m_start, m_len);
1260         return combinedText->combinedStringForRendering();
1261     }
1262     if (hasHyphen()) {
1263         if (ignoreHyphen)
1264             return renderer().text().substring(m_start, m_len);
1265         return makeString(StringView(renderer().text()).substring(m_start, m_len), lineStyle().hyphenString());
1266     }
1267     return renderer().text().substring(m_start, m_len);
1268 }
1269
1270 inline const RenderCombineText* InlineTextBox::combinedText() const
1271 {
1272     return lineStyle().hasTextCombine() && is<RenderCombineText>(renderer()) && downcast<RenderCombineText>(renderer()).isCombined() ? &downcast<RenderCombineText>(renderer()) : nullptr;
1273 }
1274
1275 ExpansionBehavior InlineTextBox::expansionBehavior() const
1276 {
1277     ExpansionBehavior leadingBehavior;
1278     if (forceLeadingExpansion())
1279         leadingBehavior = ForceLeadingExpansion;
1280     else if (canHaveLeadingExpansion())
1281         leadingBehavior = AllowLeadingExpansion;
1282     else
1283         leadingBehavior = ForbidLeadingExpansion;
1284
1285     ExpansionBehavior trailingBehavior;
1286     if (forceTrailingExpansion())
1287         trailingBehavior = ForceTrailingExpansion;
1288     else if (expansion() && nextLeafChild() && !nextLeafChild()->isLineBreak())
1289         trailingBehavior = AllowTrailingExpansion;
1290     else
1291         trailingBehavior = ForbidTrailingExpansion;
1292
1293     return leadingBehavior | trailingBehavior;
1294 }
1295
1296 #if ENABLE(TREE_DEBUGGING)
1297
1298 const char* InlineTextBox::boxName() const
1299 {
1300     return "InlineTextBox";
1301 }
1302
1303 void InlineTextBox::outputLineBox(TextStream& stream, bool mark, int depth) const
1304 {
1305     stream << "-------- " << (isDirty() ? "D" : "-") << "-";
1306
1307     int printedCharacters = 0;
1308     if (mark) {
1309         stream << "*";
1310         ++printedCharacters;
1311     }
1312     while (++printedCharacters <= depth * 2)
1313         stream << " ";
1314
1315     String value = renderer().text();
1316     value = value.substring(start(), len());
1317     value.replaceWithLiteral('\\', "\\\\");
1318     value.replaceWithLiteral('\n', "\\n");
1319     stream << boxName() << " " << FloatRect(x(), y(), width(), height()) << " (" << this << ") renderer->(" << &renderer() << ") run(" << start() << ", " << start() + len() << ") \"" << value.utf8().data() << "\"";
1320     stream.nextLine();
1321 }
1322
1323 #endif
1324
1325 } // namespace WebCore