361cc43573e05b0595d1c23f0ceac72a92c90c11
[WebKit-https.git] / Source / WebCore / rendering / InlineTextBox.cpp
1 /*
2  * (C) 1999 Lars Knoll (knoll@kde.org)
3  * (C) 2000 Dirk Mueller (mueller@kde.org)
4  * Copyright (C) 2004-2017 Apple Inc. All rights reserved.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  *
21  */
22
23 #include "config.h"
24 #include "InlineTextBox.h"
25
26 #include "BreakLines.h"
27 #include "DashArray.h"
28 #include "Document.h"
29 #include "DocumentMarkerController.h"
30 #include "Editor.h"
31 #include "EllipsisBox.h"
32 #include "Frame.h"
33 #include "GraphicsContext.h"
34 #include "HitTestResult.h"
35 #include "ImageBuffer.h"
36 #include "InlineTextBoxStyle.h"
37 #include "MarkerSubrange.h"
38 #include "Page.h"
39 #include "PaintInfo.h"
40 #include "RenderBlock.h"
41 #include "RenderCombineText.h"
42 #include "RenderLineBreak.h"
43 #include "RenderRubyRun.h"
44 #include "RenderRubyText.h"
45 #include "RenderTheme.h"
46 #include "RenderView.h"
47 #include "RenderedDocumentMarker.h"
48 #include "Text.h"
49 #include "TextDecorationPainter.h"
50 #include "TextPaintStyle.h"
51 #include "TextPainter.h"
52 #include <stdio.h>
53 #include <wtf/text/CString.h>
54 #include <wtf/text/TextStream.h>
55
56 namespace WebCore {
57
58 struct SameSizeAsInlineTextBox : public InlineBox {
59     unsigned variables[1];
60     unsigned short variables2[2];
61     void* pointers[2];
62 };
63
64 COMPILE_ASSERT(sizeof(InlineTextBox) == sizeof(SameSizeAsInlineTextBox), InlineTextBox_should_stay_small);
65
66 typedef WTF::HashMap<const InlineTextBox*, LayoutRect> InlineTextBoxOverflowMap;
67 static InlineTextBoxOverflowMap* gTextBoxesWithOverflow;
68
69 InlineTextBox::~InlineTextBox()
70 {
71     if (!knownToHaveNoOverflow() && gTextBoxesWithOverflow)
72         gTextBoxesWithOverflow->remove(this);
73 }
74
75 void InlineTextBox::markDirty(bool dirty)
76 {
77     if (dirty) {
78         m_len = 0;
79         m_start = 0;
80     }
81     InlineBox::markDirty(dirty);
82 }
83
84 LayoutRect InlineTextBox::logicalOverflowRect() const
85 {
86     if (knownToHaveNoOverflow() || !gTextBoxesWithOverflow)
87         return enclosingIntRect(logicalFrameRect());
88     return gTextBoxesWithOverflow->get(this);
89 }
90
91 void InlineTextBox::setLogicalOverflowRect(const LayoutRect& rect)
92 {
93     ASSERT(!knownToHaveNoOverflow());
94     if (!gTextBoxesWithOverflow)
95         gTextBoxesWithOverflow = new InlineTextBoxOverflowMap;
96     gTextBoxesWithOverflow->add(this, rect);
97 }
98
99 int InlineTextBox::baselinePosition(FontBaseline baselineType) const
100 {
101     if (!parent())
102         return 0;
103     if (&parent()->renderer() == renderer().parent())
104         return parent()->baselinePosition(baselineType);
105     return downcast<RenderBoxModelObject>(*renderer().parent()).baselinePosition(baselineType, isFirstLine(), isHorizontal() ? HorizontalLine : VerticalLine, PositionOnContainingLine);
106 }
107
108 LayoutUnit InlineTextBox::lineHeight() const
109 {
110     if (!renderer().parent())
111         return 0;
112     if (&parent()->renderer() == renderer().parent())
113         return parent()->lineHeight();
114     return downcast<RenderBoxModelObject>(*renderer().parent()).lineHeight(isFirstLine(), isHorizontal() ? HorizontalLine : VerticalLine, PositionOnContainingLine);
115 }
116
117 LayoutUnit InlineTextBox::selectionTop() const
118 {
119     return root().selectionTop();
120 }
121
122 LayoutUnit InlineTextBox::selectionBottom() const
123 {
124     return root().selectionBottom();
125 }
126
127 LayoutUnit InlineTextBox::selectionHeight() const
128 {
129     return root().selectionHeight();
130 }
131
132 bool InlineTextBox::isSelected(unsigned startPos, unsigned endPos) const
133 {
134     int sPos = clampedOffset(startPos);
135     int ePos = clampedOffset(endPos);
136     // FIXME: https://bugs.webkit.org/show_bug.cgi?id=160786
137     // We should only be checking if sPos >= ePos here, because those are the
138     // indices used to actually generate the selection rect. Allowing us past this guard
139     // on any other condition creates zero-width selection rects.
140     return sPos < ePos || (startPos == endPos && startPos >= start() && startPos <= (start() + len()));
141 }
142
143 RenderObject::SelectionState InlineTextBox::selectionState()
144 {
145     RenderObject::SelectionState state = renderer().selectionState();
146     if (state == RenderObject::SelectionStart || state == RenderObject::SelectionEnd || state == RenderObject::SelectionBoth) {
147         unsigned startPos, endPos;
148         renderer().selectionStartEnd(startPos, endPos);
149         // The position after a hard line break is considered to be past its end.
150         ASSERT(start() + len() >= (isLineBreak() ? 1 : 0));
151         unsigned lastSelectable = start() + len() - (isLineBreak() ? 1 : 0);
152
153         bool start = (state != RenderObject::SelectionEnd && startPos >= m_start && startPos < m_start + m_len);
154         bool end = (state != RenderObject::SelectionStart && endPos > m_start && endPos <= lastSelectable);
155         if (start && end)
156             state = RenderObject::SelectionBoth;
157         else if (start)
158             state = RenderObject::SelectionStart;
159         else if (end)
160             state = RenderObject::SelectionEnd;
161         else if ((state == RenderObject::SelectionEnd || startPos < m_start) &&
162                  (state == RenderObject::SelectionStart || endPos > lastSelectable))
163             state = RenderObject::SelectionInside;
164         else if (state == RenderObject::SelectionBoth)
165             state = RenderObject::SelectionNone;
166     }
167
168     // If there are ellipsis following, make sure their selection is updated.
169     if (m_truncation != cNoTruncation && root().ellipsisBox()) {
170         EllipsisBox* ellipsis = root().ellipsisBox();
171         if (state != RenderObject::SelectionNone) {
172             unsigned selectionStart;
173             unsigned selectionEnd;
174             std::tie(selectionStart, selectionEnd) = selectionStartEnd();
175             // The ellipsis should be considered to be selected if the end of
176             // the selection is past the beginning of the truncation and the
177             // beginning of the selection is before or at the beginning of the
178             // truncation.
179             ellipsis->setSelectionState(selectionEnd >= m_truncation && selectionStart <= m_truncation ?
180                 RenderObject::SelectionInside : RenderObject::SelectionNone);
181         } else
182             ellipsis->setSelectionState(RenderObject::SelectionNone);
183     }
184
185     return state;
186 }
187
188 inline const FontCascade& InlineTextBox::lineFont() const
189 {
190     return combinedText() ? combinedText()->textCombineFont() : lineStyle().fontCascade();
191 }
192
193 // FIXME: Share more code with paintSelection().
194 LayoutRect InlineTextBox::localSelectionRect(unsigned startPos, unsigned endPos) const
195 {
196     unsigned sPos = clampedOffset(startPos);
197     unsigned ePos = clampedOffset(endPos);
198
199     // FIXME: https://bugs.webkit.org/show_bug.cgi?id=160786
200     // We should only be checking if sPos >= ePos here, because those are the
201     // indices used to actually generate the selection rect. Allowing us past this guard
202     // on any other condition creates zero-width selection rects.
203     if (sPos >= ePos && !(startPos == endPos && startPos >= start() && startPos <= (start() + len())))
204         return LayoutRect();
205
206     LayoutUnit selectionTop = this->selectionTop();
207     LayoutUnit selectionHeight = this->selectionHeight();
208
209     // FIXME: Adjust selection rect with respect to combined text.
210     bool ignoreCombinedText = true;
211     auto text = this->text(ignoreCombinedText);
212     TextRun textRun = createTextRun(text);
213     // FIXME: Shouldn't we adjust ePos to textRun.length() if ePos == (m_truncation != cNoTruncation ? m_truncation : m_len)
214     // so that the selection spans the hypen/combined text?
215
216     LayoutRect selectionRect = LayoutRect(LayoutPoint(logicalLeft(), selectionTop), LayoutSize(m_logicalWidth, selectionHeight));
217     // Avoid computing the font width when the entire line box is selected as an optimization.
218     if (sPos || ePos != m_len)
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 paintSelection(). 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 static inline bool emphasisPositionHasNeitherLeftNorRight(TextEmphasisPosition emphasisPosition)
367 {
368     return !(emphasisPosition & TextEmphasisPositionLeft) && !(emphasisPosition & TextEmphasisPositionRight);
369 }
370
371 bool InlineTextBox::emphasisMarkExistsAndIsAbove(const RenderStyle& style, bool& above) const
372 {
373     // This function returns true if there are text emphasis marks and they are suppressed by ruby text.
374     if (style.textEmphasisMark() == TextEmphasisMarkNone)
375         return false;
376
377     TextEmphasisPosition emphasisPosition = style.textEmphasisPosition();
378     ASSERT(!((emphasisPosition & TextEmphasisPositionOver) && (emphasisPosition & TextEmphasisPositionUnder)));
379     ASSERT(!((emphasisPosition & TextEmphasisPositionLeft) && (emphasisPosition & TextEmphasisPositionRight)));
380     
381     if (emphasisPositionHasNeitherLeftNorRight(emphasisPosition))
382         above = emphasisPosition & TextEmphasisPositionOver;
383     else if (style.isHorizontalWritingMode())
384         above = emphasisPosition & TextEmphasisPositionOver;
385     else
386         above = emphasisPosition & TextEmphasisPositionRight;
387     
388     if ((style.isHorizontalWritingMode() && (emphasisPosition & TextEmphasisPositionUnder))
389         || (!style.isHorizontalWritingMode() && (emphasisPosition & TextEmphasisPositionLeft)))
390         return true; // Ruby text is always over, so it cannot suppress emphasis marks under.
391
392     RenderBlock* containingBlock = renderer().containingBlock();
393     if (!containingBlock->isRubyBase())
394         return true; // This text is not inside a ruby base, so it does not have ruby text over it.
395
396     if (!is<RenderRubyRun>(*containingBlock->parent()))
397         return true; // Cannot get the ruby text.
398
399     RenderRubyText* rubyText = downcast<RenderRubyRun>(*containingBlock->parent()).rubyText();
400
401     // The emphasis marks over are suppressed only if there is a ruby text box and it not empty.
402     return !rubyText || !rubyText->hasLines();
403 }
404
405 void InlineTextBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit /*lineTop*/, LayoutUnit /*lineBottom*/)
406 {
407     if (isLineBreak() || !paintInfo.shouldPaintWithinRoot(renderer()) || renderer().style().visibility() != VISIBLE
408         || m_truncation == cFullTruncation || paintInfo.phase == PaintPhaseOutline || !m_len)
409         return;
410
411     ASSERT(paintInfo.phase != PaintPhaseSelfOutline && paintInfo.phase != PaintPhaseChildOutlines);
412
413     LayoutUnit logicalLeftSide = logicalLeftVisualOverflow();
414     LayoutUnit logicalRightSide = logicalRightVisualOverflow();
415     LayoutUnit logicalStart = logicalLeftSide + (isHorizontal() ? paintOffset.x() : paintOffset.y());
416     LayoutUnit logicalExtent = logicalRightSide - logicalLeftSide;
417     
418     LayoutUnit paintEnd = isHorizontal() ? paintInfo.rect.maxX() : paintInfo.rect.maxY();
419     LayoutUnit paintStart = isHorizontal() ? paintInfo.rect.x() : paintInfo.rect.y();
420     
421     FloatPoint localPaintOffset(paintOffset);
422     
423     if (logicalStart >= paintEnd || logicalStart + logicalExtent <= paintStart)
424         return;
425
426     bool isPrinting = renderer().document().printing();
427     
428     // Determine whether or not we're selected.
429     bool haveSelection = !isPrinting && paintInfo.phase != PaintPhaseTextClip && selectionState() != RenderObject::SelectionNone;
430     if (!haveSelection && paintInfo.phase == PaintPhaseSelection)
431         // When only painting the selection, don't bother to paint if there is none.
432         return;
433
434     if (m_truncation != cNoTruncation) {
435         if (renderer().containingBlock()->style().isLeftToRightDirection() != isLeftToRightDirection()) {
436             // Make the visible fragment of text hug the edge closest to the rest of the run by moving the origin
437             // at which we start drawing text.
438             // e.g. In the case of LTR text truncated in an RTL Context, the correct behavior is:
439             // |Hello|CBA| -> |...He|CBA|
440             // In order to draw the fragment "He" aligned to the right edge of it's box, we need to start drawing
441             // farther to the right.
442             // NOTE: WebKit's behavior differs from that of IE which appears to just overlay the ellipsis on top of the
443             // truncated string i.e.  |Hello|CBA| -> |...lo|CBA|
444             LayoutUnit widthOfVisibleText = renderer().width(m_start, m_truncation, textPos(), isFirstLine());
445             LayoutUnit widthOfHiddenText = m_logicalWidth - widthOfVisibleText;
446             LayoutSize truncationOffset(isLeftToRightDirection() ? widthOfHiddenText : -widthOfHiddenText, 0);
447             localPaintOffset.move(isHorizontal() ? truncationOffset : truncationOffset.transposedSize());
448         }
449     }
450
451     GraphicsContext& context = paintInfo.context();
452
453     const RenderStyle& lineStyle = this->lineStyle();
454     
455     localPaintOffset.move(0, lineStyle.isHorizontalWritingMode() ? 0 : -logicalHeight());
456
457     FloatPoint boxOrigin = locationIncludingFlipping();
458     boxOrigin.moveBy(localPaintOffset);
459     FloatRect boxRect(boxOrigin, FloatSize(logicalWidth(), logicalHeight()));
460
461     auto* combinedText = this->combinedText();
462
463     bool shouldRotate = !isHorizontal() && !combinedText;
464     if (shouldRotate)
465         context.concatCTM(rotation(boxRect, Clockwise));
466
467     // Determine whether or not we have composition underlines to draw.
468     bool containsComposition = renderer().textNode() && renderer().frame().editor().compositionNode() == renderer().textNode();
469     bool useCustomUnderlines = containsComposition && renderer().frame().editor().compositionUsesCustomUnderlines();
470
471     // Determine the text colors and selection colors.
472     TextPaintStyle textPaintStyle = computeTextPaintStyle(renderer().frame(), lineStyle, paintInfo);
473
474     bool paintSelectedTextOnly = false;
475     bool paintSelectedTextSeparately = false;
476     bool paintNonSelectedTextOnly = false;
477     const ShadowData* selectionShadow = nullptr;
478     
479     // Text with custom underlines does not have selection background painted, so selection paint style is not appropriate for it.
480     TextPaintStyle selectionPaintStyle = haveSelection && !useCustomUnderlines ? computeTextSelectionPaintStyle(textPaintStyle, renderer(), lineStyle, paintInfo, paintSelectedTextOnly, paintSelectedTextSeparately, paintNonSelectedTextOnly, selectionShadow) : textPaintStyle;
481
482     // Set our font.
483     const FontCascade& font = lineFont();
484     // 1. Paint backgrounds behind text if needed. Examples of such backgrounds include selection
485     // and composition underlines.
486     if (paintInfo.phase != PaintPhaseSelection && paintInfo.phase != PaintPhaseTextClip && !isPrinting) {
487         if (containsComposition && !useCustomUnderlines)
488             paintCompositionBackground(context, boxOrigin, font);
489
490         paintDocumentMarkers(context, boxOrigin, font, true);
491
492         if (haveSelection && !useCustomUnderlines)
493             paintSelection(context, boxOrigin, font, selectionPaintStyle.fillColor);
494     }
495
496     // FIXME: Right now, InlineTextBoxes never call addRelevantUnpaintedObject() even though they might
497     // legitimately be unpainted if they are waiting on a slow-loading web font. We should fix that, and
498     // when we do, we will have to account for the fact the InlineTextBoxes do not always have unique
499     // renderers and Page currently relies on each unpainted object having a unique renderer.
500     if (paintInfo.phase == PaintPhaseForeground)
501         renderer().page().addRelevantRepaintedObject(&renderer(), IntRect(boxOrigin.x(), boxOrigin.y(), logicalWidth(), logicalHeight()));
502
503     // 2. Now paint the foreground, including text and decorations like underline/overline (in quirks mode only).
504     auto text = this->text();
505     TextRun textRun = createTextRun(text);
506     unsigned length = textRun.length();
507
508     unsigned selectionStart = 0;
509     unsigned selectionEnd = 0;
510     if (haveSelection && (paintSelectedTextOnly || paintSelectedTextSeparately))
511         std::tie(selectionStart, selectionEnd) = selectionStartEnd();
512
513     if (m_truncation != cNoTruncation) {
514         selectionStart = std::min(selectionStart, static_cast<unsigned>(m_truncation));
515         selectionEnd = std::min(selectionEnd, static_cast<unsigned>(m_truncation));
516         length = m_truncation;
517     }
518
519     float emphasisMarkOffset = 0;
520     bool emphasisMarkAbove;
521     bool hasTextEmphasis = emphasisMarkExistsAndIsAbove(lineStyle, emphasisMarkAbove);
522     const AtomicString& emphasisMark = hasTextEmphasis ? lineStyle.textEmphasisMarkString() : nullAtom();
523     if (!emphasisMark.isEmpty())
524         emphasisMarkOffset = emphasisMarkAbove ? -font.fontMetrics().ascent() - font.emphasisMarkDescent(emphasisMark) : font.fontMetrics().descent() + font.emphasisMarkAscent(emphasisMark);
525
526     const ShadowData* textShadow = (paintInfo.forceTextColor()) ? nullptr : lineStyle.textShadow();
527
528     FloatPoint textOrigin(boxOrigin.x(), boxOrigin.y() + font.fontMetrics().ascent());
529     if (combinedText) {
530         if (auto newOrigin = combinedText->computeTextOrigin(boxRect))
531             textOrigin = newOrigin.value();
532     }
533
534     if (isHorizontal())
535         textOrigin.setY(roundToDevicePixel(LayoutUnit(textOrigin.y()), renderer().document().deviceScaleFactor()));
536     else
537         textOrigin.setX(roundToDevicePixel(LayoutUnit(textOrigin.x()), renderer().document().deviceScaleFactor()));
538
539     TextPainter textPainter(context);
540     textPainter.setFont(font);
541     textPainter.setStyle(textPaintStyle);
542     textPainter.setSelectionStyle(selectionPaintStyle);
543     textPainter.setIsHorizontal(isHorizontal());
544     textPainter.setShadow(textShadow);
545     textPainter.setSelectionShadow(selectionShadow);
546     textPainter.setEmphasisMark(emphasisMark, emphasisMarkOffset, combinedText);
547
548     auto draggedContentRanges = renderer().draggedContentRangesBetweenOffsets(m_start, m_start + m_len);
549     if (!draggedContentRanges.isEmpty() && !paintSelectedTextOnly && !paintNonSelectedTextOnly) {
550         // FIXME: Painting with text effects ranges currently only works if we're not also painting the selection.
551         // In the future, we may want to support this capability, but in the meantime, this isn't required by anything.
552         unsigned currentEnd = 0;
553         for (size_t index = 0; index < draggedContentRanges.size(); ++index) {
554             unsigned previousEnd = index ? std::min(draggedContentRanges[index - 1].second, length) : 0;
555             unsigned currentStart = draggedContentRanges[index].first - m_start;
556             currentEnd = std::min(draggedContentRanges[index].second - m_start, length);
557
558             if (previousEnd < currentStart)
559                 textPainter.paintRange(textRun, boxRect, textOrigin, previousEnd, currentStart);
560
561             if (currentStart < currentEnd) {
562                 context.save();
563                 context.setAlpha(0.25);
564                 textPainter.paintRange(textRun, boxRect, textOrigin, currentStart, currentEnd);
565                 context.restore();
566             }
567         }
568         if (currentEnd < length)
569             textPainter.paintRange(textRun, boxRect, textOrigin, currentEnd, length);
570     } else
571         textPainter.paint(textRun, length, boxRect, textOrigin, selectionStart, selectionEnd, paintSelectedTextOnly, paintSelectedTextSeparately, paintNonSelectedTextOnly);
572
573     // Paint decorations
574     TextDecoration textDecorations = lineStyle.textDecorationsInEffect();
575     if (textDecorations != TextDecorationNone && paintInfo.phase != PaintPhaseSelection) {
576         FloatRect textDecorationSelectionClipOutRect;
577         if ((paintInfo.paintBehavior & PaintBehaviorExcludeSelection) && selectionStart < selectionEnd && selectionEnd <= length) {
578             textDecorationSelectionClipOutRect = logicalOverflowRect();
579             textDecorationSelectionClipOutRect.moveBy(localPaintOffset);
580             float logicalWidthBeforeRange;
581             float logicalWidthAfterRange;
582             float logicalSelectionWidth = font.widthOfTextRange(textRun, selectionStart, selectionEnd, nullptr, &logicalWidthBeforeRange, &logicalWidthAfterRange);
583             // FIXME: Do we need to handle vertical bottom to top text?
584             if (!isHorizontal()) {
585                 textDecorationSelectionClipOutRect.move(0, logicalWidthBeforeRange);
586                 textDecorationSelectionClipOutRect.setHeight(logicalSelectionWidth);
587             } else if (direction() == RTL) {
588                 textDecorationSelectionClipOutRect.move(logicalWidthAfterRange, 0);
589                 textDecorationSelectionClipOutRect.setWidth(logicalSelectionWidth);
590             } else {
591                 textDecorationSelectionClipOutRect.move(logicalWidthBeforeRange, 0);
592                 textDecorationSelectionClipOutRect.setWidth(logicalSelectionWidth);
593             }
594         }
595         paintDecoration(context, font, textRun, textOrigin, boxRect, textDecorations, textPaintStyle, textShadow, textDecorationSelectionClipOutRect);
596     }
597
598     if (paintInfo.phase == PaintPhaseForeground) {
599         paintDocumentMarkers(context, boxOrigin, font, false);
600
601         if (useCustomUnderlines) {
602             const Vector<CompositionUnderline>& underlines = renderer().frame().editor().customCompositionUnderlines();
603             size_t numUnderlines = underlines.size();
604
605             for (size_t index = 0; index < numUnderlines; ++index) {
606                 const CompositionUnderline& underline = underlines[index];
607
608                 if (underline.endOffset <= start())
609                     // underline is completely before this run.  This might be an underline that sits
610                     // before the first run we draw, or underlines that were within runs we skipped 
611                     // due to truncation.
612                     continue;
613                 
614                 if (underline.startOffset <= end()) {
615                     // underline intersects this run.  Paint it.
616                     paintCompositionUnderline(context, boxOrigin, underline);
617                     if (underline.endOffset > end() + 1)
618                         // underline also runs into the next run. Bail now, no more marker advancement.
619                         break;
620                 } else
621                     // underline is completely after this run, bail.  A later run will paint it.
622                     break;
623             }
624         }
625     }
626     
627     if (shouldRotate)
628         context.concatCTM(rotation(boxRect, Counterclockwise));
629 }
630
631 unsigned InlineTextBox::clampedOffset(unsigned x) const
632 {
633     return std::max(std::min(x, start() + len()), start()) - start();
634 }
635
636 std::pair<unsigned, unsigned> InlineTextBox::selectionStartEnd() const
637 {
638     auto selectionState = renderer().selectionState();
639     if (selectionState == RenderObject::SelectionInside)
640         return { 0, m_len };
641     
642     unsigned start;
643     unsigned end;
644     renderer().selectionStartEnd(start, end);
645     if (selectionState == RenderObject::SelectionStart)
646         end = renderer().textLength();
647     else if (selectionState == RenderObject::SelectionEnd)
648         start = 0;
649     return { clampedOffset(start), clampedOffset(end) };
650 }
651
652 void InlineTextBox::paintSelection(GraphicsContext& context, const FloatPoint& boxOrigin, const FontCascade& font, const Color& textColor)
653 {
654 #if ENABLE(TEXT_SELECTION)
655     if (context.paintingDisabled())
656         return;
657
658     // See if we have a selection to paint at all.
659     unsigned selectionStart;
660     unsigned selectionEnd;
661     std::tie(selectionStart, selectionEnd) = selectionStartEnd();
662     if (selectionStart >= selectionEnd)
663         return;
664
665     Color c = renderer().selectionBackgroundColor();
666     if (!c.isValid() || c.alpha() == 0)
667         return;
668
669     // If the text color ends up being the same as the selection background, invert the selection
670     // background.
671     if (textColor == c)
672         c = Color(0xff - c.red(), 0xff - c.green(), 0xff - c.blue());
673
674     GraphicsContextStateSaver stateSaver(context);
675     updateGraphicsContext(context, TextPaintStyle(c)); // Don't draw text at all!
676
677     // Note that if the text is truncated, we let the thing being painted in the truncation
678     // draw its own highlight.
679
680     // FIXME: Adjust text run for combined text.
681     bool ignoreCombinedText = true;
682     auto text = this->text(ignoreCombinedText);
683     TextRun textRun = createTextRun(text);
684     unsigned endOfLineIgnoringHyphenAndCombinedText = m_truncation != cNoTruncation ? m_truncation : m_len;
685     if (selectionEnd == endOfLineIgnoringHyphenAndCombinedText)
686         selectionEnd = textRun.length(); // Extend selection to include hyphen/combined text.
687
688     const RootInlineBox& rootBox = root();
689     LayoutUnit selectionBottom = rootBox.selectionBottom();
690     LayoutUnit selectionTop = rootBox.selectionTopAdjustedForPrecedingBlock();
691
692     LayoutUnit deltaY = renderer().style().isFlippedLinesWritingMode() ? selectionBottom - logicalBottom() : logicalTop() - selectionTop;
693     LayoutUnit selectionHeight = std::max<LayoutUnit>(0, selectionBottom - selectionTop);
694
695     LayoutRect selectionRect = LayoutRect(boxOrigin.x(), boxOrigin.y() - deltaY, m_logicalWidth, selectionHeight);
696     font.adjustSelectionRectForText(textRun, selectionRect, selectionStart, selectionEnd);
697     context.fillRect(snapRectToDevicePixelsWithWritingDirection(selectionRect, renderer().document().deviceScaleFactor(), textRun.ltr()), c);
698 #else
699     UNUSED_PARAM(context);
700     UNUSED_PARAM(boxOrigin);
701     UNUSED_PARAM(font);
702     UNUSED_PARAM(textColor);
703 #endif
704 }
705
706 inline void InlineTextBox::paintTextSubrangeBackground(GraphicsContext& context, const FloatPoint& boxOrigin, const FontCascade& font, const Color& color, unsigned startOffset, unsigned endOffset)
707 {
708     startOffset = clampedOffset(startOffset);
709     endOffset = clampedOffset(endOffset);
710     if (startOffset >= endOffset)
711         return;
712
713     GraphicsContextStateSaver stateSaver { context };
714     updateGraphicsContext(context, TextPaintStyle { color }); // Don't draw text at all!
715
716     // Use same y positioning and height as for selection, so that when the selection and this subrange are on
717     // the same word there are no pieces sticking out.
718     LayoutUnit deltaY = renderer().style().isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
719     LayoutRect selectionRect = LayoutRect(boxOrigin.x(), boxOrigin.y() - deltaY, 0, selectionHeight());
720
721     // FIXME: Adjust text run for combined text and hyphenation.
722     bool ignoreCombinedText = true;
723     bool ignoreHyphen = true;
724     auto text = this->text(ignoreCombinedText, ignoreHyphen);
725     TextRun textRun = createTextRun(text);
726     font.adjustSelectionRectForText(textRun, selectionRect, startOffset, endOffset);
727     context.fillRect(snapRectToDevicePixelsWithWritingDirection(selectionRect, renderer().document().deviceScaleFactor(), textRun.ltr()), color);
728 }
729
730 void InlineTextBox::paintCompositionBackground(GraphicsContext& context, const FloatPoint& boxOrigin, const FontCascade& font)
731 {
732     paintTextSubrangeBackground(context, boxOrigin, font, renderer().frame().editor().compositionStart(), renderer().frame().editor().compositionEnd(), Color::compositionFill);
733 }
734
735 void InlineTextBox::paintTextMatchMarker(GraphicsContext& context, const FloatPoint& boxOrigin, const FontCascade& font, const MarkerSubrange& subrange, bool isActiveMatch)
736 {
737     if (!renderer().frame().editor().markedTextMatchesAreHighlighted())
738         return;
739     auto highlightColor = isActiveMatch ? renderer().theme().platformActiveTextSearchHighlightColor() : renderer().theme().platformInactiveTextSearchHighlightColor();
740     paintTextSubrangeBackground(context, boxOrigin, font, highlightColor, subrange.startOffset, subrange.endOffset);
741 }
742
743 static inline void mirrorRTLSegment(float logicalWidth, TextDirection direction, float& start, float width)
744 {
745     if (direction == LTR)
746         return;
747     start = logicalWidth - width - start;
748 }
749
750 void InlineTextBox::paintDecoration(GraphicsContext& context, const FontCascade& font, const TextRun& textRun, const FloatPoint& textOrigin,
751     const FloatRect& boxRect, TextDecoration decoration, TextPaintStyle textPaintStyle, const ShadowData* shadow, const FloatRect& clipOutRect)
752 {
753     if (m_truncation == cFullTruncation)
754         return;
755
756     updateGraphicsContext(context, textPaintStyle);
757
758     bool isCombinedText = combinedText();
759     if (isCombinedText)
760         context.concatCTM(rotation(boxRect, Clockwise));
761
762     float start = 0;
763     float width = m_logicalWidth;
764     if (m_truncation != cNoTruncation) {
765         width = renderer().width(m_start, m_truncation, textPos(), isFirstLine());
766         mirrorRTLSegment(m_logicalWidth, direction(), start, width);
767     }
768
769     TextDecorationPainter decorationPainter(context, decoration, renderer(), isFirstLine());
770     decorationPainter.setInlineTextBox(this);
771     decorationPainter.setFont(font);
772     decorationPainter.setWidth(width);
773     decorationPainter.setBaseline(lineStyle().fontMetrics().ascent());
774     decorationPainter.setIsHorizontal(isHorizontal());
775     decorationPainter.addTextShadow(shadow);
776
777     FloatPoint localOrigin = boxRect.location();
778     localOrigin.move(start, 0);
779
780     {
781         GraphicsContextStateSaver stateSaver { context, false };
782         if (!clipOutRect.isEmpty()) {
783             stateSaver.save();
784             context.clipOut(clipOutRect);
785         }
786         decorationPainter.paintTextDecoration(textRun, textOrigin, localOrigin);
787     }
788
789     if (isCombinedText)
790         context.concatCTM(rotation(boxRect, Counterclockwise));
791 }
792
793 void InlineTextBox::paintDocumentMarker(GraphicsContext& context, const FloatPoint& boxOrigin, const FontCascade& font, const MarkerSubrange& subrange)
794 {
795     // Never print spelling/grammar markers (5327887)
796     if (renderer().document().printing())
797         return;
798
799     if (m_truncation == cFullTruncation)
800         return;
801
802     float start = 0; // start of line to draw, relative to tx
803     float width = m_logicalWidth; // how much line to draw
804
805     // Determine whether we need to measure text
806     bool markerSpansWholeBox = true;
807     if (m_start <= subrange.startOffset)
808         markerSpansWholeBox = false;
809     if ((end() + 1) != subrange.endOffset) // End points at the last char, not past it
810         markerSpansWholeBox = false;
811     if (m_truncation != cNoTruncation)
812         markerSpansWholeBox = false;
813
814     if (!markerSpansWholeBox) {
815         unsigned startPosition = clampedOffset(subrange.startOffset);
816         unsigned endPosition = clampedOffset(subrange.endOffset);
817         
818         if (m_truncation != cNoTruncation)
819             endPosition = std::min(endPosition, static_cast<unsigned>(m_truncation));
820
821         // Calculate start & width
822         // FIXME: Adjust text run for combined text and hyphenation.
823         bool ignoreCombinedText = true;
824         bool ignoreHyphen = true;
825         int deltaY = renderer().style().isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
826         int selHeight = selectionHeight();
827         FloatPoint startPoint(boxOrigin.x(), boxOrigin.y() - deltaY);
828         auto text = this->text(ignoreCombinedText, ignoreHyphen);
829         TextRun run = createTextRun(text);
830
831         LayoutRect selectionRect = LayoutRect(startPoint, FloatSize(0, selHeight));
832         font.adjustSelectionRectForText(run, selectionRect, startPosition, endPosition);
833         IntRect markerRect = enclosingIntRect(selectionRect);
834         start = markerRect.x() - startPoint.x();
835         width = markerRect.width();
836     }
837     
838     auto lineStyleForSubrangeType = [] (MarkerSubrange::Type type) {
839         switch (type) {
840         case MarkerSubrange::SpellingError:
841             return GraphicsContext::DocumentMarkerSpellingLineStyle;
842         case MarkerSubrange::GrammarError:
843             return GraphicsContext::DocumentMarkerGrammarLineStyle;
844         case MarkerSubrange::Correction:
845             return GraphicsContext::DocumentMarkerAutocorrectionReplacementLineStyle;
846         case MarkerSubrange::DictationAlternatives:
847             return GraphicsContext::DocumentMarkerDictationAlternativesLineStyle;
848 #if PLATFORM(IOS)
849         case MarkerSubrange::DictationPhraseWithAlternatives:
850             // FIXME: Rename TextCheckingDictationPhraseWithAlternativesLineStyle and remove the PLATFORM(IOS)-guard.
851             return GraphicsContext::TextCheckingDictationPhraseWithAlternativesLineStyle;
852 #endif
853         default:
854             ASSERT_NOT_REACHED();
855             return GraphicsContext::DocumentMarkerSpellingLineStyle;
856         }
857     };
858
859     // IMPORTANT: The misspelling underline is not considered when calculating the text bounds, so we have to
860     // make sure to fit within those bounds.  This means the top pixel(s) of the underline will overlap the
861     // bottom pixel(s) of the glyphs in smaller font sizes.  The alternatives are to increase the line spacing (bad!!)
862     // or decrease the underline thickness.  The overlap is actually the most useful, and matches what AppKit does.
863     // So, we generally place the underline at the bottom of the text, but in larger fonts that's not so good so
864     // we pin to two pixels under the baseline.
865     int lineThickness = cMisspellingLineThickness;
866     int baseline = lineStyle().fontMetrics().ascent();
867     int descent = logicalHeight() - baseline;
868     int underlineOffset;
869     if (descent <= (2 + lineThickness)) {
870         // Place the underline at the very bottom of the text in small/medium fonts.
871         underlineOffset = logicalHeight() - lineThickness;
872     } else {
873         // In larger fonts, though, place the underline up near the baseline to prevent a big gap.
874         underlineOffset = baseline + 2;
875     }
876     context.drawLineForDocumentMarker(FloatPoint(boxOrigin.x() + start, boxOrigin.y() + underlineOffset), width, lineStyleForSubrangeType(subrange.type));
877 }
878
879 void InlineTextBox::paintDocumentMarkers(GraphicsContext& context, const FloatPoint& boxOrigin, const FontCascade& font, bool background)
880 {
881     if (!renderer().textNode())
882         return;
883
884     Vector<RenderedDocumentMarker*> markers = renderer().document().markers().markersFor(renderer().textNode());
885
886     auto markerTypeForSubrangeType = [] (DocumentMarker::MarkerType type) {
887         switch (type) {
888         case DocumentMarker::Spelling:
889             return MarkerSubrange::SpellingError;
890         case DocumentMarker::Grammar:
891             return MarkerSubrange::GrammarError;
892         case DocumentMarker::CorrectionIndicator:
893             return MarkerSubrange::Correction;
894         case DocumentMarker::TextMatch:
895             return MarkerSubrange::TextMatch;
896         case DocumentMarker::DictationAlternatives:
897             return MarkerSubrange::DictationAlternatives;
898 #if PLATFORM(IOS)
899         case DocumentMarker::DictationPhraseWithAlternatives:
900             return MarkerSubrange::DictationPhraseWithAlternatives;
901 #endif
902         default:
903             return MarkerSubrange::Unmarked;
904         }
905     };
906
907     Vector<MarkerSubrange> subranges;
908     subranges.reserveInitialCapacity(markers.size());
909
910     // Give any document markers that touch this run a chance to draw before the text has been drawn.
911     // Note end() points at the last char, not one past it like endOffset and ranges do.
912     for (auto* marker : markers) {
913         // Paint either the background markers or the foreground markers, but not both
914         switch (marker->type()) {
915             case DocumentMarker::Grammar:
916             case DocumentMarker::Spelling:
917             case DocumentMarker::CorrectionIndicator:
918             case DocumentMarker::Replacement:
919             case DocumentMarker::DictationAlternatives:
920 #if PLATFORM(IOS)
921             // FIXME: Remove the PLATFORM(IOS)-guard.
922             case DocumentMarker::DictationPhraseWithAlternatives:
923 #endif
924                 if (background)
925                     continue;
926                 break;
927             case DocumentMarker::TextMatch:
928 #if ENABLE(TELEPHONE_NUMBER_DETECTION)
929             case DocumentMarker::TelephoneNumber:
930 #endif
931                 if (!background)
932                     continue;
933                 break;
934             default:
935                 continue;
936         }
937
938         if (marker->endOffset() <= start())
939             // marker is completely before this run.  This might be a marker that sits before the
940             // first run we draw, or markers that were within runs we skipped due to truncation.
941             continue;
942         
943         if (marker->startOffset() > end())
944             // marker is completely after this run, bail.  A later run will paint it.
945             break;
946         
947         // marker intersects this run.  Paint it.
948         switch (marker->type()) {
949             case DocumentMarker::Spelling:
950             case DocumentMarker::CorrectionIndicator:
951             case DocumentMarker::DictationAlternatives:
952             case DocumentMarker::Grammar:
953 #if PLATFORM(IOS)
954             // FIXME: See <rdar://problem/8933352>. Also, remove the PLATFORM(IOS)-guard.
955             case DocumentMarker::DictationPhraseWithAlternatives:
956 #endif
957             case DocumentMarker::TextMatch:
958                 subranges.uncheckedAppend({ marker->startOffset(), marker->endOffset(), markerTypeForSubrangeType(marker->type()), marker });
959                 break;
960             case DocumentMarker::Replacement:
961                 break;
962 #if ENABLE(TELEPHONE_NUMBER_DETECTION)
963             case DocumentMarker::TelephoneNumber:
964                 break;
965 #endif
966             default:
967                 ASSERT_NOT_REACHED();
968         }
969     }
970
971     for (auto& subrange : subdivide(subranges, OverlapStrategy::Frontmost)) {
972         if (subrange.type == MarkerSubrange::TextMatch)
973             paintTextMatchMarker(context, boxOrigin, font, subrange, subrange.marker->isActiveMatch());
974         else
975             paintDocumentMarker(context, boxOrigin, font, subrange);
976     }
977 }
978
979 void InlineTextBox::paintCompositionUnderline(GraphicsContext& context, const FloatPoint& boxOrigin, const CompositionUnderline& underline)
980 {
981     if (m_truncation == cFullTruncation)
982         return;
983     
984     float start = 0; // start of line to draw, relative to tx
985     float width = m_logicalWidth; // how much line to draw
986     bool useWholeWidth = true;
987     unsigned paintStart = m_start;
988     unsigned paintEnd = end() + 1; // end points at the last char, not past it
989     if (paintStart <= underline.startOffset) {
990         paintStart = underline.startOffset;
991         useWholeWidth = false;
992         start = renderer().width(m_start, paintStart - m_start, textPos(), isFirstLine());
993     }
994     if (paintEnd != underline.endOffset) {      // end points at the last char, not past it
995         paintEnd = std::min(paintEnd, (unsigned)underline.endOffset);
996         useWholeWidth = false;
997     }
998     if (m_truncation != cNoTruncation) {
999         paintEnd = std::min(paintEnd, (unsigned)m_start + m_truncation);
1000         useWholeWidth = false;
1001     }
1002     if (!useWholeWidth) {
1003         width = renderer().width(paintStart, paintEnd - paintStart, textPos() + start, isFirstLine());
1004         mirrorRTLSegment(m_logicalWidth, direction(), start, width);
1005     }
1006
1007     // Thick marked text underlines are 2px thick as long as there is room for the 2px line under the baseline.
1008     // All other marked text underlines are 1px thick.
1009     // If there's not enough space the underline will touch or overlap characters.
1010     int lineThickness = 1;
1011     int baseline = lineStyle().fontMetrics().ascent();
1012     if (underline.thick && logicalHeight() - baseline >= 2)
1013         lineThickness = 2;
1014
1015     // We need to have some space between underlines of subsequent clauses, because some input methods do not use different underline styles for those.
1016     // We make each line shorter, which has a harmless side effect of shortening the first and last clauses, too.
1017     start += 1;
1018     width -= 2;
1019
1020     context.setStrokeColor(underline.compositionUnderlineColor == CompositionUnderlineColor::TextColor ? renderer().style().visitedDependentColor(CSSPropertyWebkitTextFillColor) : underline.color);
1021     context.setStrokeThickness(lineThickness);
1022     context.drawLineForText(FloatPoint(boxOrigin.x() + start, boxOrigin.y() + logicalHeight() - lineThickness), width, renderer().document().printing());
1023 }
1024
1025 int InlineTextBox::caretMinOffset() const
1026 {
1027     return m_start;
1028 }
1029
1030 int InlineTextBox::caretMaxOffset() const
1031 {
1032     return m_start + m_len;
1033 }
1034
1035 float InlineTextBox::textPos() const
1036 {
1037     // When computing the width of a text run, RenderBlock::computeInlineDirectionPositionsForLine() doesn't include the actual offset
1038     // from the containing block edge in its measurement. textPos() should be consistent so the text are rendered in the same width.
1039     if (logicalLeft() == 0)
1040         return 0;
1041     return logicalLeft() - root().logicalLeft();
1042 }
1043
1044 int InlineTextBox::offsetForPosition(float lineOffset, bool includePartialGlyphs) const
1045 {
1046     if (isLineBreak())
1047         return 0;
1048     if (lineOffset - logicalLeft() > logicalWidth())
1049         return isLeftToRightDirection() ? len() : 0;
1050     if (lineOffset - logicalLeft() < 0)
1051         return isLeftToRightDirection() ? 0 : len();
1052     bool ignoreCombinedText = true;
1053     bool ignoreHyphen = true;
1054     auto text = this->text(ignoreCombinedText, ignoreHyphen);
1055     return lineFont().offsetForPosition(createTextRun(text), lineOffset - logicalLeft(), includePartialGlyphs);
1056 }
1057
1058 float InlineTextBox::positionForOffset(unsigned offset) const
1059 {
1060     ASSERT(offset >= m_start);
1061     ASSERT(offset <= m_start + len());
1062
1063     if (isLineBreak())
1064         return logicalLeft();
1065
1066     unsigned startOffset;
1067     unsigned endOffset;
1068     if (isLeftToRightDirection()) {
1069         startOffset = 0;
1070         endOffset = clampedOffset(offset);
1071     } else {
1072         startOffset = clampedOffset(offset);
1073         endOffset = m_len;
1074     }
1075
1076     // FIXME: Do we need to add rightBearing here?
1077     LayoutRect selectionRect = LayoutRect(logicalLeft(), 0, 0, 0);
1078     bool ignoreCombinedText = true;
1079     bool ignoreHyphen = true;
1080     auto text = this->text(ignoreCombinedText, ignoreHyphen);
1081     TextRun textRun = createTextRun(text);
1082     lineFont().adjustSelectionRectForText(textRun, selectionRect, startOffset, endOffset);
1083     return snapRectToDevicePixelsWithWritingDirection(selectionRect, renderer().document().deviceScaleFactor(), textRun.ltr()).maxX();
1084 }
1085
1086 TextRun InlineTextBox::createTextRun(String& string) const
1087 {
1088     const auto& style = lineStyle();
1089     TextRun textRun { string, textPos(), expansion(), expansionBehavior(), direction(), dirOverride() || style.rtlOrdering() == VisualOrder, !renderer().canUseSimpleFontCodePath() };
1090     textRun.setTabSize(!style.collapseWhiteSpace(), style.tabSize());
1091     return textRun;
1092 }
1093
1094 String InlineTextBox::text(bool ignoreCombinedText, bool ignoreHyphen) const
1095 {
1096     if (UNLIKELY(m_truncation == cFullTruncation))
1097         return emptyString();
1098     if (auto* combinedText = this->combinedText()) {
1099         if (ignoreCombinedText)
1100             return renderer().text()->substring(m_start, m_len);
1101         return combinedText->combinedStringForRendering();
1102     }
1103     if (hasHyphen()) {
1104         if (ignoreHyphen)
1105             return renderer().text()->substring(m_start, m_len);
1106         return makeString(StringView(renderer().text()).substring(m_start, m_len), lineStyle().hyphenString());
1107     }
1108     return renderer().text()->substring(m_start, m_truncation != cNoTruncation ? m_truncation : m_len);
1109 }
1110
1111 inline const RenderCombineText* InlineTextBox::combinedText() const
1112 {
1113     return lineStyle().hasTextCombine() && is<RenderCombineText>(renderer()) && downcast<RenderCombineText>(renderer()).isCombined() ? &downcast<RenderCombineText>(renderer()) : nullptr;
1114 }
1115
1116 ExpansionBehavior InlineTextBox::expansionBehavior() const
1117 {
1118     ExpansionBehavior leadingBehavior;
1119     if (forceLeadingExpansion())
1120         leadingBehavior = ForceLeadingExpansion;
1121     else if (canHaveLeadingExpansion())
1122         leadingBehavior = AllowLeadingExpansion;
1123     else
1124         leadingBehavior = ForbidLeadingExpansion;
1125
1126     ExpansionBehavior trailingBehavior;
1127     if (forceTrailingExpansion())
1128         trailingBehavior = ForceTrailingExpansion;
1129     else if (expansion() && nextLeafChild() && !nextLeafChild()->isLineBreak())
1130         trailingBehavior = AllowTrailingExpansion;
1131     else
1132         trailingBehavior = ForbidTrailingExpansion;
1133
1134     return leadingBehavior | trailingBehavior;
1135 }
1136
1137 #if ENABLE(TREE_DEBUGGING)
1138
1139 const char* InlineTextBox::boxName() const
1140 {
1141     return "InlineTextBox";
1142 }
1143
1144 void InlineTextBox::outputLineBox(TextStream& stream, bool mark, int depth) const
1145 {
1146     stream << "-------- " << (isDirty() ? "D" : "-") << "-";
1147
1148     int printedCharacters = 0;
1149     if (mark) {
1150         stream << "*";
1151         ++printedCharacters;
1152     }
1153     while (++printedCharacters <= depth * 2)
1154         stream << " ";
1155
1156     String value = renderer().text();
1157     value = value.substring(start(), len());
1158     value.replaceWithLiteral('\\', "\\\\");
1159     value.replaceWithLiteral('\n', "\\n");
1160     stream << boxName() << " " << FloatRect(x(), y(), width(), height()) << " (" << this << ") renderer->(" << &renderer() << ") run(" << start() << ", " << start() + len() << ") \"" << value.utf8().data() << "\"";
1161     stream.nextLine();
1162 }
1163
1164 #endif
1165
1166 } // namespace WebCore