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