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