Migrate from ints to unsigneds when referring to indices into strings
[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-2014 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 "Chrome.h"
27 #include "ChromeClient.h"
28 #include "DashArray.h"
29 #include "Document.h"
30 #include "DocumentMarkerController.h"
31 #include "Editor.h"
32 #include "EllipsisBox.h"
33 #include "Frame.h"
34 #include "GraphicsContext.h"
35 #include "HitTestResult.h"
36 #include "ImageBuffer.h"
37 #include "InlineTextBoxStyle.h"
38 #include "Page.h"
39 #include "PaintInfo.h"
40 #include "RenderedDocumentMarker.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 "Settings.h"
49 #include "Text.h"
50 #include "TextDecorationPainter.h"
51 #include "TextPaintStyle.h"
52 #include "TextPainter.h"
53 #include "break_lines.h"
54 #include <stdio.h>
55 #include <wtf/text/CString.h>
56
57 namespace WebCore {
58
59 struct SameSizeAsInlineTextBox : public InlineBox {
60     unsigned variables[1];
61     unsigned short variables2[2];
62     void* pointers[2];
63 };
64
65 COMPILE_ASSERT(sizeof(InlineTextBox) == sizeof(SameSizeAsInlineTextBox), InlineTextBox_should_stay_small);
66
67 typedef WTF::HashMap<const InlineTextBox*, LayoutRect> InlineTextBoxOverflowMap;
68 static InlineTextBoxOverflowMap* gTextBoxesWithOverflow;
69
70 InlineTextBox::~InlineTextBox()
71 {
72     if (!knownToHaveNoOverflow() && gTextBoxesWithOverflow)
73         gTextBoxesWithOverflow->remove(this);
74 }
75
76 void InlineTextBox::markDirty(bool dirty)
77 {
78     if (dirty) {
79         m_len = 0;
80         m_start = 0;
81     }
82     InlineBox::markDirty(dirty);
83 }
84
85 LayoutRect InlineTextBox::logicalOverflowRect() const
86 {
87     if (knownToHaveNoOverflow() || !gTextBoxesWithOverflow)
88         return enclosingIntRect(logicalFrameRect());
89     return gTextBoxesWithOverflow->get(this);
90 }
91
92 void InlineTextBox::setLogicalOverflowRect(const LayoutRect& rect)
93 {
94     ASSERT(!knownToHaveNoOverflow());
95     if (!gTextBoxesWithOverflow)
96         gTextBoxesWithOverflow = new InlineTextBoxOverflowMap;
97     gTextBoxesWithOverflow->add(this, rect);
98 }
99
100 int InlineTextBox::baselinePosition(FontBaseline baselineType) const
101 {
102     if (!parent())
103         return 0;
104     if (&parent()->renderer() == renderer().parent())
105         return parent()->baselinePosition(baselineType);
106     return downcast<RenderBoxModelObject>(*renderer().parent()).baselinePosition(baselineType, isFirstLine(), isHorizontal() ? HorizontalLine : VerticalLine, PositionOnContainingLine);
107 }
108
109 LayoutUnit InlineTextBox::lineHeight() const
110 {
111     if (!renderer().parent())
112         return 0;
113     if (&parent()->renderer() == renderer().parent())
114         return parent()->lineHeight();
115     return downcast<RenderBoxModelObject>(*renderer().parent()).lineHeight(isFirstLine(), isHorizontal() ? HorizontalLine : VerticalLine, PositionOnContainingLine);
116 }
117
118 LayoutUnit InlineTextBox::selectionTop() const
119 {
120     return root().selectionTop();
121 }
122
123 LayoutUnit InlineTextBox::selectionBottom() const
124 {
125     return root().selectionBottom();
126 }
127
128 LayoutUnit InlineTextBox::selectionHeight() const
129 {
130     return root().selectionHeight();
131 }
132
133 bool InlineTextBox::isSelected(unsigned startPos, unsigned endPos) const
134 {
135     int sPos = clampedOffset(startPos);
136     int ePos = clampedOffset(endPos);
137     // FIXME: https://bugs.webkit.org/show_bug.cgi?id=160786
138     // We should only be checking if sPos >= ePos here, because those are the
139     // indices used to actually generate the selection rect. Allowing us past this guard
140     // on any other condition creates zero-width selection rects.
141     return sPos < ePos || (startPos == endPos && startPos >= start() && startPos <= (start() + len()));
142 }
143
144 RenderObject::SelectionState InlineTextBox::selectionState()
145 {
146     RenderObject::SelectionState state = renderer().selectionState();
147     if (state == RenderObject::SelectionStart || state == RenderObject::SelectionEnd || state == RenderObject::SelectionBoth) {
148         unsigned startPos, endPos;
149         renderer().selectionStartEnd(startPos, endPos);
150         // The position after a hard line break is considered to be past its end.
151         ASSERT(start() + len() >= (isLineBreak() ? 1 : 0));
152         unsigned lastSelectable = start() + len() - (isLineBreak() ? 1 : 0);
153
154         bool start = (state != RenderObject::SelectionEnd && startPos >= m_start && startPos < m_start + m_len);
155         bool end = (state != RenderObject::SelectionStart && endPos > m_start && endPos <= lastSelectable);
156         if (start && end)
157             state = RenderObject::SelectionBoth;
158         else if (start)
159             state = RenderObject::SelectionStart;
160         else if (end)
161             state = RenderObject::SelectionEnd;
162         else if ((state == RenderObject::SelectionEnd || startPos < m_start) &&
163                  (state == RenderObject::SelectionStart || endPos > lastSelectable))
164             state = RenderObject::SelectionInside;
165         else if (state == RenderObject::SelectionBoth)
166             state = RenderObject::SelectionNone;
167     }
168
169     // If there are ellipsis following, make sure their selection is updated.
170     if (m_truncation != cNoTruncation && root().ellipsisBox()) {
171         EllipsisBox* ellipsis = root().ellipsisBox();
172         if (state != RenderObject::SelectionNone) {
173             unsigned selectionStart;
174             unsigned selectionEnd;
175             std::tie(selectionStart, selectionEnd) = selectionStartEnd();
176             // The ellipsis should be considered to be selected if the end of
177             // the selection is past the beginning of the truncation and the
178             // beginning of the selection is before or at the beginning of the
179             // truncation.
180             ellipsis->setSelectionState(selectionEnd >= m_truncation && selectionStart <= m_truncation ?
181                 RenderObject::SelectionInside : RenderObject::SelectionNone);
182         } else
183             ellipsis->setSelectionState(RenderObject::SelectionNone);
184     }
185
186     return state;
187 }
188
189 static const FontCascade& fontToUse(const RenderStyle& style, const RenderText& renderer)
190 {
191     if (style.hasTextCombine() && is<RenderCombineText>(renderer)) {
192         const auto& textCombineRenderer = downcast<RenderCombineText>(renderer);
193         if (textCombineRenderer.isCombined())
194             return textCombineRenderer.textCombineFont();
195     }
196     return style.fontCascade();
197 }
198
199 LayoutRect InlineTextBox::localSelectionRect(unsigned startPos, unsigned endPos) const
200 {
201     unsigned sPos = clampedOffset(startPos);
202     unsigned ePos = clampedOffset(endPos);
203
204     // FIXME: https://bugs.webkit.org/show_bug.cgi?id=160786
205     // We should only be checking if sPos >= ePos here, because those are the
206     // indices used to actually generate the selection rect. Allowing us past this guard
207     // on any other condition creates zero-width selection rects.
208     if (sPos >= ePos && !(startPos == endPos && startPos >= start() && startPos <= (start() + len())))
209         return LayoutRect();
210
211     LayoutUnit selectionTop = this->selectionTop();
212     LayoutUnit selectionHeight = this->selectionHeight();
213     const RenderStyle& lineStyle = this->lineStyle();
214     const FontCascade& font = fontToUse(lineStyle, renderer());
215
216     String hyphenatedString;
217     bool respectHyphen = ePos == m_len && hasHyphen();
218     if (respectHyphen)
219         hyphenatedString = hyphenatedStringForTextRun(lineStyle);
220     TextRun textRun = constructTextRun(lineStyle, hyphenatedString);
221
222     LayoutRect selectionRect = LayoutRect(LayoutPoint(logicalLeft(), selectionTop), LayoutSize(m_logicalWidth, selectionHeight));
223     // Avoid computing the font width when the entire line box is selected as an optimization.
224     if (sPos || ePos != m_len)
225         font.adjustSelectionRectForText(textRun, selectionRect, sPos, ePos);
226     IntRect snappedSelectionRect = enclosingIntRect(selectionRect);
227     LayoutUnit logicalWidth = snappedSelectionRect.width();
228     if (snappedSelectionRect.x() > logicalRight())
229         logicalWidth  = 0;
230     else if (snappedSelectionRect.maxX() > logicalRight())
231         logicalWidth = logicalRight() - snappedSelectionRect.x();
232
233     LayoutPoint topPoint = isHorizontal() ? LayoutPoint(snappedSelectionRect.x(), selectionTop) : LayoutPoint(selectionTop, snappedSelectionRect.x());
234     LayoutUnit width = isHorizontal() ? logicalWidth : selectionHeight;
235     LayoutUnit height = isHorizontal() ? selectionHeight : logicalWidth;
236
237     return LayoutRect(topPoint, LayoutSize(width, height));
238 }
239
240 void InlineTextBox::deleteLine()
241 {
242     renderer().removeTextBox(*this);
243     delete this;
244 }
245
246 void InlineTextBox::extractLine()
247 {
248     if (extracted())
249         return;
250
251     renderer().extractTextBox(*this);
252 }
253
254 void InlineTextBox::attachLine()
255 {
256     if (!extracted())
257         return;
258     
259     renderer().attachTextBox(*this);
260 }
261
262 float InlineTextBox::placeEllipsisBox(bool flowIsLTR, float visibleLeftEdge, float visibleRightEdge, float ellipsisWidth, float &truncatedWidth, bool& foundBox)
263 {
264     if (foundBox) {
265         m_truncation = cFullTruncation;
266         return -1;
267     }
268
269     // For LTR this is the left edge of the box, for RTL, the right edge in parent coordinates.
270     float ellipsisX = flowIsLTR ? visibleRightEdge - ellipsisWidth : visibleLeftEdge + ellipsisWidth;
271     
272     // Criteria for full truncation:
273     // LTR: the left edge of the ellipsis is to the left of our text run.
274     // RTL: the right edge of the ellipsis is to the right of our text run.
275     bool ltrFullTruncation = flowIsLTR && ellipsisX <= left();
276     bool rtlFullTruncation = !flowIsLTR && ellipsisX >= left() + logicalWidth();
277     if (ltrFullTruncation || rtlFullTruncation) {
278         // Too far.  Just set full truncation, but return -1 and let the ellipsis just be placed at the edge of the box.
279         m_truncation = cFullTruncation;
280         foundBox = true;
281         return -1;
282     }
283
284     bool ltrEllipsisWithinBox = flowIsLTR && (ellipsisX < right());
285     bool rtlEllipsisWithinBox = !flowIsLTR && (ellipsisX > left());
286     if (ltrEllipsisWithinBox || rtlEllipsisWithinBox) {
287         foundBox = true;
288
289         // The inline box may have different directionality than it's parent.  Since truncation
290         // behavior depends both on both the parent and the inline block's directionality, we
291         // must keep track of these separately.
292         bool ltr = isLeftToRightDirection();
293         if (ltr != flowIsLTR) {
294           // Width in pixels of the visible portion of the box, excluding the ellipsis.
295           int visibleBoxWidth = visibleRightEdge - visibleLeftEdge  - ellipsisWidth;
296           ellipsisX = ltr ? left() + visibleBoxWidth : right() - visibleBoxWidth;
297         }
298
299         int offset = offsetForPosition(ellipsisX, false);
300         if (offset == 0) {
301             // No characters should be rendered.  Set ourselves to full truncation and place the ellipsis at the min of our start
302             // and the ellipsis edge.
303             m_truncation = cFullTruncation;
304             truncatedWidth += ellipsisWidth;
305             return flowIsLTR ? std::min(ellipsisX, x()) : std::max(ellipsisX, right() - ellipsisWidth);
306         }
307
308         // Set the truncation index on the text run.
309         m_truncation = offset;
310
311         // If we got here that means that we were only partially truncated and we need to return the pixel offset at which
312         // to place the ellipsis.
313         float widthOfVisibleText = renderer().width(m_start, offset, textPos(), isFirstLine());
314
315         // The ellipsis needs to be placed just after the last visible character.
316         // Where "after" is defined by the flow directionality, not the inline
317         // box directionality.
318         // e.g. In the case of an LTR inline box truncated in an RTL flow then we can
319         // have a situation such as |Hello| -> |...He|
320         truncatedWidth += widthOfVisibleText + ellipsisWidth;
321         if (flowIsLTR)
322             return left() + widthOfVisibleText;
323         else
324             return right() - widthOfVisibleText - ellipsisWidth;
325     }
326     truncatedWidth += logicalWidth();
327     return -1;
328 }
329
330
331
332 bool InlineTextBox::isLineBreak() const
333 {
334     return renderer().style().preserveNewline() && len() == 1 && (*renderer().text())[start()] == '\n';
335 }
336
337 bool InlineTextBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit /* lineTop */, LayoutUnit /*lineBottom*/,
338     HitTestAction /*hitTestAction*/)
339 {
340     if (!visibleToHitTesting())
341         return false;
342
343     if (isLineBreak())
344         return false;
345
346     if (m_truncation == cFullTruncation)
347         return false;
348
349     FloatRect rect(locationIncludingFlipping(), size());
350     // Make sure truncated text is ignored while hittesting.
351     if (m_truncation != cNoTruncation) {
352         LayoutUnit widthOfVisibleText = renderer().width(m_start, m_truncation, textPos(), isFirstLine());
353
354         if (isHorizontal())
355             renderer().style().isLeftToRightDirection() ? rect.setWidth(widthOfVisibleText) : rect.shiftXEdgeTo(right() - widthOfVisibleText);
356         else
357             rect.setHeight(widthOfVisibleText);
358     }
359
360     rect.moveBy(accumulatedOffset);
361
362     if (locationInContainer.intersects(rect)) {
363         renderer().updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - toLayoutSize(accumulatedOffset)));
364         if (!result.addNodeToRectBasedTestResult(renderer().textNode(), request, locationInContainer, rect))
365             return true;
366     }
367     return false;
368 }
369
370 static inline bool emphasisPositionHasNeitherLeftNorRight(TextEmphasisPosition emphasisPosition)
371 {
372     return !(emphasisPosition & TextEmphasisPositionLeft) && !(emphasisPosition & TextEmphasisPositionRight);
373 }
374
375 bool InlineTextBox::emphasisMarkExistsAndIsAbove(const RenderStyle& style, bool& above) const
376 {
377     // This function returns true if there are text emphasis marks and they are suppressed by ruby text.
378     if (style.textEmphasisMark() == TextEmphasisMarkNone)
379         return false;
380
381     TextEmphasisPosition emphasisPosition = style.textEmphasisPosition();
382     ASSERT(!((emphasisPosition & TextEmphasisPositionOver) && (emphasisPosition & TextEmphasisPositionUnder)));
383     ASSERT(!((emphasisPosition & TextEmphasisPositionLeft) && (emphasisPosition & TextEmphasisPositionRight)));
384     
385     if (emphasisPositionHasNeitherLeftNorRight(emphasisPosition))
386         above = emphasisPosition & TextEmphasisPositionOver;
387     else if (style.isHorizontalWritingMode())
388         above = emphasisPosition & TextEmphasisPositionOver;
389     else
390         above = emphasisPosition & TextEmphasisPositionRight;
391     
392     if ((style.isHorizontalWritingMode() && (emphasisPosition & TextEmphasisPositionUnder))
393         || (!style.isHorizontalWritingMode() && (emphasisPosition & TextEmphasisPositionLeft)))
394         return true; // Ruby text is always over, so it cannot suppress emphasis marks under.
395
396     RenderBlock* containingBlock = renderer().containingBlock();
397     if (!containingBlock->isRubyBase())
398         return true; // This text is not inside a ruby base, so it does not have ruby text over it.
399
400     if (!is<RenderRubyRun>(*containingBlock->parent()))
401         return true; // Cannot get the ruby text.
402
403     RenderRubyText* rubyText = downcast<RenderRubyRun>(*containingBlock->parent()).rubyText();
404
405     // The emphasis marks over are suppressed only if there is a ruby text box and it not empty.
406     return !rubyText || !rubyText->hasLines();
407 }
408
409 void InlineTextBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit /*lineTop*/, LayoutUnit /*lineBottom*/)
410 {
411     if (isLineBreak() || !paintInfo.shouldPaintWithinRoot(renderer()) || renderer().style().visibility() != VISIBLE
412         || m_truncation == cFullTruncation || paintInfo.phase == PaintPhaseOutline || !m_len)
413         return;
414
415     ASSERT(paintInfo.phase != PaintPhaseSelfOutline && paintInfo.phase != PaintPhaseChildOutlines);
416
417     LayoutUnit logicalLeftSide = logicalLeftVisualOverflow();
418     LayoutUnit logicalRightSide = logicalRightVisualOverflow();
419     LayoutUnit logicalStart = logicalLeftSide + (isHorizontal() ? paintOffset.x() : paintOffset.y());
420     LayoutUnit logicalExtent = logicalRightSide - logicalLeftSide;
421     
422     LayoutUnit paintEnd = isHorizontal() ? paintInfo.rect.maxX() : paintInfo.rect.maxY();
423     LayoutUnit paintStart = isHorizontal() ? paintInfo.rect.x() : paintInfo.rect.y();
424     
425     FloatPoint localPaintOffset(paintOffset);
426     
427     if (logicalStart >= paintEnd || logicalStart + logicalExtent <= paintStart)
428         return;
429
430     bool isPrinting = renderer().document().printing();
431     
432     // Determine whether or not we're selected.
433     bool haveSelection = !isPrinting && paintInfo.phase != PaintPhaseTextClip && selectionState() != RenderObject::SelectionNone;
434     if (!haveSelection && paintInfo.phase == PaintPhaseSelection)
435         // When only painting the selection, don't bother to paint if there is none.
436         return;
437
438     if (m_truncation != cNoTruncation) {
439         if (renderer().containingBlock()->style().isLeftToRightDirection() != isLeftToRightDirection()) {
440             // Make the visible fragment of text hug the edge closest to the rest of the run by moving the origin
441             // at which we start drawing text.
442             // e.g. In the case of LTR text truncated in an RTL Context, the correct behavior is:
443             // |Hello|CBA| -> |...He|CBA|
444             // In order to draw the fragment "He" aligned to the right edge of it's box, we need to start drawing
445             // farther to the right.
446             // NOTE: WebKit's behavior differs from that of IE which appears to just overlay the ellipsis on top of the
447             // truncated string i.e.  |Hello|CBA| -> |...lo|CBA|
448             LayoutUnit widthOfVisibleText = renderer().width(m_start, m_truncation, textPos(), isFirstLine());
449             LayoutUnit widthOfHiddenText = m_logicalWidth - widthOfVisibleText;
450             LayoutSize truncationOffset(isLeftToRightDirection() ? widthOfHiddenText : -widthOfHiddenText, 0);
451             localPaintOffset.move(isHorizontal() ? truncationOffset : truncationOffset.transposedSize());
452         }
453     }
454
455     GraphicsContext& context = paintInfo.context();
456
457     const RenderStyle& lineStyle = this->lineStyle();
458     
459     localPaintOffset.move(0, lineStyle.isHorizontalWritingMode() ? 0 : -logicalHeight());
460
461     FloatPoint boxOrigin = locationIncludingFlipping();
462     boxOrigin.moveBy(localPaintOffset);
463     FloatRect boxRect(boxOrigin, FloatSize(logicalWidth(), logicalHeight()));
464
465     RenderCombineText* combinedText = lineStyle.hasTextCombine() && is<RenderCombineText>(renderer()) && downcast<RenderCombineText>(renderer()).isCombined() ? &downcast<RenderCombineText>(renderer()) : nullptr;
466
467     bool shouldRotate = !isHorizontal() && !combinedText;
468     if (shouldRotate)
469         context.concatCTM(rotation(boxRect, Clockwise));
470
471     // Determine whether or not we have composition underlines to draw.
472     bool containsComposition = renderer().textNode() && renderer().frame().editor().compositionNode() == renderer().textNode();
473     bool useCustomUnderlines = containsComposition && renderer().frame().editor().compositionUsesCustomUnderlines();
474
475     // Determine the text colors and selection colors.
476     TextPaintStyle textPaintStyle = computeTextPaintStyle(renderer().frame(), lineStyle, paintInfo);
477
478     bool paintSelectedTextOnly = false;
479     bool paintSelectedTextSeparately = false;
480     const ShadowData* selectionShadow = nullptr;
481     
482     // Text with custom underlines does not have selection background painted, so selection paint style is not appropriate for it.
483     TextPaintStyle selectionPaintStyle = haveSelection && !useCustomUnderlines ? computeTextSelectionPaintStyle(textPaintStyle, renderer(), lineStyle, paintInfo, paintSelectedTextOnly, paintSelectedTextSeparately, selectionShadow) : textPaintStyle;
484
485     // Set our font.
486     const FontCascade& font = fontToUse(lineStyle, renderer());
487     // 1. Paint backgrounds behind text if needed. Examples of such backgrounds include selection
488     // and composition underlines.
489     if (paintInfo.phase != PaintPhaseSelection && paintInfo.phase != PaintPhaseTextClip && !isPrinting) {
490         if (containsComposition && !useCustomUnderlines)
491             paintCompositionBackground(context, boxOrigin, lineStyle, font,
492                 renderer().frame().editor().compositionStart(),
493                 renderer().frame().editor().compositionEnd());
494
495         paintDocumentMarkers(context, boxOrigin, lineStyle, font, true);
496
497         if (haveSelection && !useCustomUnderlines)
498             paintSelection(context, boxOrigin, lineStyle, font, selectionPaintStyle.fillColor);
499     }
500
501     if (Page* page = renderer().frame().page()) {
502         // FIXME: Right now, InlineTextBoxes never call addRelevantUnpaintedObject() even though they might
503         // legitimately be unpainted if they are waiting on a slow-loading web font. We should fix that, and
504         // when we do, we will have to account for the fact the InlineTextBoxes do not always have unique
505         // renderers and Page currently relies on each unpainted object having a unique renderer.
506         if (paintInfo.phase == PaintPhaseForeground)
507             page->addRelevantRepaintedObject(&renderer(), IntRect(boxOrigin.x(), boxOrigin.y(), logicalWidth(), logicalHeight()));
508     }
509
510     // 2. Now paint the foreground, including text and decorations like underline/overline (in quirks mode only).
511     String alternateStringToRender;
512     if (combinedText)
513         alternateStringToRender = combinedText->combinedStringForRendering();
514     else if (hasHyphen())
515         alternateStringToRender = hyphenatedStringForTextRun(lineStyle);
516
517     TextRun textRun = constructTextRun(lineStyle, alternateStringToRender);
518     unsigned length = textRun.length();
519
520     unsigned selectionStart = 0;
521     unsigned selectionEnd = 0;
522     if (haveSelection && (paintSelectedTextOnly || paintSelectedTextSeparately))
523         std::tie(selectionStart, selectionEnd) = selectionStartEnd();
524
525     if (m_truncation != cNoTruncation) {
526         selectionStart = std::min(selectionStart, static_cast<unsigned>(m_truncation));
527         selectionEnd = std::min(selectionEnd, static_cast<unsigned>(m_truncation));
528         length = m_truncation;
529     }
530
531     float emphasisMarkOffset = 0;
532     bool emphasisMarkAbove;
533     bool hasTextEmphasis = emphasisMarkExistsAndIsAbove(lineStyle, emphasisMarkAbove);
534     const AtomicString& emphasisMark = hasTextEmphasis ? lineStyle.textEmphasisMarkString() : nullAtom;
535     if (!emphasisMark.isEmpty())
536         emphasisMarkOffset = emphasisMarkAbove ? -font.fontMetrics().ascent() - font.emphasisMarkDescent(emphasisMark) : font.fontMetrics().descent() + font.emphasisMarkAscent(emphasisMark);
537
538     const ShadowData* textShadow = (paintInfo.forceTextColor()) ? nullptr : lineStyle.textShadow();
539
540     FloatPoint textOrigin(boxOrigin.x(), boxOrigin.y() + font.fontMetrics().ascent());
541     if (combinedText) {
542         if (auto newOrigin = combinedText->computeTextOrigin(boxRect))
543             textOrigin = newOrigin.value();
544     }
545
546     if (isHorizontal())
547         textOrigin.setY(roundToDevicePixel(LayoutUnit(textOrigin.y()), renderer().document().deviceScaleFactor()));
548     else
549         textOrigin.setX(roundToDevicePixel(LayoutUnit(textOrigin.x()), renderer().document().deviceScaleFactor()));
550
551     TextPainter textPainter(context);
552     textPainter.setFont(font);
553     textPainter.setTextPaintStyle(textPaintStyle);
554     textPainter.setSelectionPaintStyle(selectionPaintStyle);
555     textPainter.setIsHorizontal(isHorizontal());
556     textPainter.addTextShadow(textShadow, selectionShadow);
557     textPainter.addEmphasis(emphasisMark, emphasisMarkOffset, combinedText);
558
559     textPainter.paintText(textRun, length, boxRect, textOrigin, selectionStart, selectionEnd, paintSelectedTextOnly, paintSelectedTextSeparately);
560
561     // Paint decorations
562     TextDecoration textDecorations = lineStyle.textDecorationsInEffect();
563     if (textDecorations != TextDecorationNone && paintInfo.phase != PaintPhaseSelection)
564         paintDecoration(context, font, combinedText, textRun, textOrigin, boxRect, textDecorations, textPaintStyle, textShadow);
565
566     if (paintInfo.phase == PaintPhaseForeground) {
567         paintDocumentMarkers(context, boxOrigin, lineStyle, font, false);
568
569         if (useCustomUnderlines) {
570             const Vector<CompositionUnderline>& underlines = renderer().frame().editor().customCompositionUnderlines();
571             size_t numUnderlines = underlines.size();
572
573             for (size_t index = 0; index < numUnderlines; ++index) {
574                 const CompositionUnderline& underline = underlines[index];
575
576                 if (underline.endOffset <= start())
577                     // underline is completely before this run.  This might be an underline that sits
578                     // before the first run we draw, or underlines that were within runs we skipped 
579                     // due to truncation.
580                     continue;
581                 
582                 if (underline.startOffset <= end()) {
583                     // underline intersects this run.  Paint it.
584                     paintCompositionUnderline(context, boxOrigin, underline);
585                     if (underline.endOffset > end() + 1)
586                         // underline also runs into the next run. Bail now, no more marker advancement.
587                         break;
588                 } else
589                     // underline is completely after this run, bail.  A later run will paint it.
590                     break;
591             }
592         }
593     }
594     
595     if (shouldRotate)
596         context.concatCTM(rotation(boxRect, Counterclockwise));
597 }
598
599 unsigned InlineTextBox::clampedOffset(unsigned x) const
600 {
601     return std::max(std::min(x, start() + len()), start()) - start();
602 }
603
604 std::pair<unsigned, unsigned> InlineTextBox::selectionStartEnd() const
605 {
606     auto selectionState = renderer().selectionState();
607     if (selectionState == RenderObject::SelectionInside)
608         return { 0, m_len };
609     
610     unsigned start;
611     unsigned end;
612     renderer().selectionStartEnd(start, end);
613     if (selectionState == RenderObject::SelectionStart)
614         end = renderer().textLength();
615     else if (selectionState == RenderObject::SelectionEnd)
616         start = 0;
617     return { clampedOffset(start), clampedOffset(end) };
618 }
619
620 void InlineTextBox::paintSelection(GraphicsContext& context, const FloatPoint& boxOrigin, const RenderStyle& style, const FontCascade& font, Color textColor)
621 {
622 #if ENABLE(TEXT_SELECTION)
623     if (context.paintingDisabled())
624         return;
625
626     // See if we have a selection to paint at all.
627     unsigned selectionStart;
628     unsigned selectionEnd;
629     std::tie(selectionStart, selectionEnd) = selectionStartEnd();
630     if (selectionStart >= selectionEnd)
631         return;
632
633     Color c = renderer().selectionBackgroundColor();
634     if (!c.isValid() || c.alpha() == 0)
635         return;
636
637     // If the text color ends up being the same as the selection background, invert the selection
638     // background.
639     if (textColor == c)
640         c = Color(0xff - c.red(), 0xff - c.green(), 0xff - c.blue());
641
642     GraphicsContextStateSaver stateSaver(context);
643     updateGraphicsContext(context, TextPaintStyle(c)); // Don't draw text at all!
644
645     // If the text is truncated, let the thing being painted in the truncation
646     // draw its own highlight.
647
648     unsigned length = m_truncation != cNoTruncation ? m_truncation : len();
649
650     String hyphenatedString;
651     bool respectHyphen = selectionEnd == length && hasHyphen();
652     if (respectHyphen)
653         hyphenatedString = hyphenatedStringForTextRun(style, length);
654     TextRun textRun = constructTextRun(style, hyphenatedString, Optional<unsigned>(length));
655     if (respectHyphen)
656         selectionEnd = textRun.length();
657
658     const RootInlineBox& rootBox = root();
659     LayoutUnit selectionBottom = rootBox.selectionBottom();
660     LayoutUnit selectionTop = rootBox.selectionTopAdjustedForPrecedingBlock();
661
662     LayoutUnit deltaY = renderer().style().isFlippedLinesWritingMode() ? selectionBottom - logicalBottom() : logicalTop() - selectionTop;
663     LayoutUnit selectionHeight = std::max<LayoutUnit>(0, selectionBottom - selectionTop);
664
665     LayoutRect selectionRect = LayoutRect(boxOrigin.x(), boxOrigin.y() - deltaY, m_logicalWidth, selectionHeight);
666     font.adjustSelectionRectForText(textRun, selectionRect, selectionStart, selectionEnd);
667     context.fillRect(snapRectToDevicePixelsWithWritingDirection(selectionRect, renderer().document().deviceScaleFactor(), textRun.ltr()), c);
668 #else
669     UNUSED_PARAM(context);
670     UNUSED_PARAM(boxOrigin);
671     UNUSED_PARAM(style);
672     UNUSED_PARAM(font);
673     UNUSED_PARAM(textColor);
674 #endif
675 }
676
677 void InlineTextBox::paintCompositionBackground(GraphicsContext& context, const FloatPoint& boxOrigin, const RenderStyle& style, const FontCascade& font, unsigned startPos, unsigned endPos)
678 {
679     unsigned selectionStart = clampedOffset(startPos);
680     unsigned selectionEnd = clampedOffset(endPos);
681     if (selectionStart >= selectionEnd)
682         return;
683
684     GraphicsContextStateSaver stateSaver(context);
685     Color compositionColor = Color::compositionFill;
686     updateGraphicsContext(context, TextPaintStyle(compositionColor)); // Don't draw text at all!
687
688     LayoutUnit deltaY = renderer().style().isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
689     LayoutRect selectionRect = LayoutRect(boxOrigin.x(), boxOrigin.y() - deltaY, 0, selectionHeight());
690     TextRun textRun = constructTextRun(style);
691     font.adjustSelectionRectForText(textRun, selectionRect, selectionStart, selectionEnd);
692     context.fillRect(snapRectToDevicePixelsWithWritingDirection(selectionRect, renderer().document().deviceScaleFactor(), textRun.ltr()), compositionColor);
693 }
694
695 static inline void mirrorRTLSegment(float logicalWidth, TextDirection direction, float& start, float width)
696 {
697     if (direction == LTR)
698         return;
699     start = logicalWidth - width - start;
700 }
701
702 void InlineTextBox::paintDecoration(GraphicsContext& context, const FontCascade& font, RenderCombineText* combinedText, const TextRun& textRun, const FloatPoint& textOrigin,
703     const FloatRect& boxRect, TextDecoration decoration, TextPaintStyle textPaintStyle, const ShadowData* shadow)
704 {
705     if (m_truncation == cFullTruncation)
706         return;
707
708     updateGraphicsContext(context, textPaintStyle);
709     if (combinedText)
710         context.concatCTM(rotation(boxRect, Clockwise));
711
712     float start = 0;
713     float width = m_logicalWidth;
714     if (m_truncation != cNoTruncation) {
715         width = renderer().width(m_start, m_truncation, textPos(), isFirstLine());
716         mirrorRTLSegment(m_logicalWidth, direction(), start, width);
717     }
718     
719     int baseline = lineStyle().fontMetrics().ascent();
720     TextDecorationPainter decorationPainter(context, decoration, renderer(), isFirstLine());
721     decorationPainter.setInlineTextBox(this);
722     decorationPainter.setFont(font);
723     decorationPainter.setWidth(width);
724     decorationPainter.setBaseline(baseline);
725     decorationPainter.setIsHorizontal(isHorizontal());
726     decorationPainter.addTextShadow(shadow);
727
728     FloatPoint localOrigin = boxRect.location();
729     localOrigin.move(start, 0);
730     decorationPainter.paintTextDecoration(textRun, textOrigin, localOrigin);
731
732     if (combinedText)
733         context.concatCTM(rotation(boxRect, Counterclockwise));
734 }
735
736 static GraphicsContext::DocumentMarkerLineStyle lineStyleForMarkerType(DocumentMarker::MarkerType markerType)
737 {
738     switch (markerType) {
739     case DocumentMarker::Spelling:
740         return GraphicsContext::DocumentMarkerSpellingLineStyle;
741     case DocumentMarker::Grammar:
742         return GraphicsContext::DocumentMarkerGrammarLineStyle;
743     case DocumentMarker::CorrectionIndicator:
744         return GraphicsContext::DocumentMarkerAutocorrectionReplacementLineStyle;
745     case DocumentMarker::DictationAlternatives:
746         return GraphicsContext::DocumentMarkerDictationAlternativesLineStyle;
747 #if PLATFORM(IOS)
748     case DocumentMarker::DictationPhraseWithAlternatives:
749         // FIXME: Rename TextCheckingDictationPhraseWithAlternativesLineStyle and remove the PLATFORM(IOS)-guard.
750         return GraphicsContext::TextCheckingDictationPhraseWithAlternativesLineStyle;
751 #endif
752     default:
753         ASSERT_NOT_REACHED();
754         return GraphicsContext::DocumentMarkerSpellingLineStyle;
755     }
756 }
757
758 void InlineTextBox::paintDocumentMarker(GraphicsContext& context, const FloatPoint& boxOrigin, RenderedDocumentMarker& marker, const RenderStyle& style, const FontCascade& font, bool grammar)
759 {
760     // Never print spelling/grammar markers (5327887)
761     if (renderer().document().printing())
762         return;
763
764     if (m_truncation == cFullTruncation)
765         return;
766
767     float start = 0; // start of line to draw, relative to tx
768     float width = m_logicalWidth; // how much line to draw
769
770     // Determine whether we need to measure text
771     bool markerSpansWholeBox = true;
772     if (m_start <= marker.startOffset())
773         markerSpansWholeBox = false;
774     if ((end() + 1) != marker.endOffset()) // end points at the last char, not past it
775         markerSpansWholeBox = false;
776     if (m_truncation != cNoTruncation)
777         markerSpansWholeBox = false;
778
779     bool isDictationMarker = marker.type() == DocumentMarker::DictationAlternatives;
780     if (!markerSpansWholeBox || grammar || isDictationMarker) {
781         unsigned startPosition = clampedOffset(marker.startOffset());
782         unsigned endPosition = clampedOffset(marker.endOffset());
783         
784         if (m_truncation != cNoTruncation)
785             endPosition = std::min(endPosition, static_cast<unsigned>(m_truncation));
786
787         // Calculate start & width
788         int deltaY = renderer().style().isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
789         int selHeight = selectionHeight();
790         FloatPoint startPoint(boxOrigin.x(), boxOrigin.y() - deltaY);
791         TextRun run = constructTextRun(style);
792
793         LayoutRect selectionRect = LayoutRect(startPoint, FloatSize(0, selHeight));
794         font.adjustSelectionRectForText(run, selectionRect, startPosition, endPosition);
795         IntRect markerRect = enclosingIntRect(selectionRect);
796         start = markerRect.x() - startPoint.x();
797         width = markerRect.width();
798     }
799     
800     // IMPORTANT: The misspelling underline is not considered when calculating the text bounds, so we have to
801     // make sure to fit within those bounds.  This means the top pixel(s) of the underline will overlap the
802     // bottom pixel(s) of the glyphs in smaller font sizes.  The alternatives are to increase the line spacing (bad!!)
803     // or decrease the underline thickness.  The overlap is actually the most useful, and matches what AppKit does.
804     // So, we generally place the underline at the bottom of the text, but in larger fonts that's not so good so
805     // we pin to two pixels under the baseline.
806     int lineThickness = cMisspellingLineThickness;
807     int baseline = lineStyle().fontMetrics().ascent();
808     int descent = logicalHeight() - baseline;
809     int underlineOffset;
810     if (descent <= (2 + lineThickness)) {
811         // Place the underline at the very bottom of the text in small/medium fonts.
812         underlineOffset = logicalHeight() - lineThickness;
813     } else {
814         // In larger fonts, though, place the underline up near the baseline to prevent a big gap.
815         underlineOffset = baseline + 2;
816     }
817     context.drawLineForDocumentMarker(FloatPoint(boxOrigin.x() + start, boxOrigin.y() + underlineOffset), width, lineStyleForMarkerType(marker.type()));
818 }
819
820 void InlineTextBox::paintTextMatchMarker(GraphicsContext& context, const FloatPoint& boxOrigin, RenderedDocumentMarker& marker, const RenderStyle& style, const FontCascade& font)
821 {
822     if (!renderer().frame().editor().markedTextMatchesAreHighlighted())
823         return;
824
825     Color color = marker.activeMatch() ? renderer().theme().platformActiveTextSearchHighlightColor() : renderer().theme().platformInactiveTextSearchHighlightColor();
826     GraphicsContextStateSaver stateSaver(context);
827     updateGraphicsContext(context, TextPaintStyle(color)); // Don't draw text at all!
828
829     // Use same y positioning and height as for selection, so that when the selection and this highlight are on
830     // the same word there are no pieces sticking out.
831     LayoutUnit deltaY = renderer().style().isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
832     LayoutRect selectionRect = LayoutRect(boxOrigin.x(), boxOrigin.y() - deltaY, 0, this->selectionHeight());
833
834     unsigned sPos = clampedOffset(marker.startOffset());
835     unsigned ePos = clampedOffset(marker.endOffset());
836     TextRun run = constructTextRun(style);
837     font.adjustSelectionRectForText(run, selectionRect, sPos, ePos);
838
839     if (selectionRect.isEmpty())
840         return;
841
842     context.fillRect(snapRectToDevicePixelsWithWritingDirection(selectionRect, renderer().document().deviceScaleFactor(), run.ltr()), color);
843 }
844     
845 void InlineTextBox::paintDocumentMarkers(GraphicsContext& context, const FloatPoint& boxOrigin, const RenderStyle& style, const FontCascade& font, bool background)
846 {
847     if (!renderer().textNode())
848         return;
849
850     Vector<RenderedDocumentMarker*> markers = renderer().document().markers().markersFor(renderer().textNode());
851
852     // Give any document markers that touch this run a chance to draw before the text has been drawn.
853     // Note end() points at the last char, not one past it like endOffset and ranges do.
854     for (auto* marker : markers) {
855         // Paint either the background markers or the foreground markers, but not both
856         switch (marker->type()) {
857             case DocumentMarker::Grammar:
858             case DocumentMarker::Spelling:
859             case DocumentMarker::CorrectionIndicator:
860             case DocumentMarker::Replacement:
861             case DocumentMarker::DictationAlternatives:
862 #if PLATFORM(IOS)
863             // FIXME: Remove the PLATFORM(IOS)-guard.
864             case DocumentMarker::DictationPhraseWithAlternatives:
865 #endif
866                 if (background)
867                     continue;
868                 break;
869             case DocumentMarker::TextMatch:
870 #if ENABLE(TELEPHONE_NUMBER_DETECTION)
871             case DocumentMarker::TelephoneNumber:
872 #endif
873                 if (!background)
874                     continue;
875                 break;
876             default:
877                 continue;
878         }
879
880         if (marker->endOffset() <= start())
881             // marker is completely before this run.  This might be a marker that sits before the
882             // first run we draw, or markers that were within runs we skipped due to truncation.
883             continue;
884         
885         if (marker->startOffset() > end())
886             // marker is completely after this run, bail.  A later run will paint it.
887             break;
888         
889         // marker intersects this run.  Paint it.
890         switch (marker->type()) {
891             case DocumentMarker::Spelling:
892             case DocumentMarker::CorrectionIndicator:
893             case DocumentMarker::DictationAlternatives:
894                 paintDocumentMarker(context, boxOrigin, *marker, style, font, false);
895                 break;
896             case DocumentMarker::Grammar:
897                 paintDocumentMarker(context, boxOrigin, *marker, style, font, true);
898                 break;
899 #if PLATFORM(IOS)
900             // FIXME: See <rdar://problem/8933352>. Also, remove the PLATFORM(IOS)-guard.
901             case DocumentMarker::DictationPhraseWithAlternatives:
902                 paintDocumentMarker(context, boxOrigin, *marker, style, font, true);
903                 break;
904 #endif
905             case DocumentMarker::TextMatch:
906                 paintTextMatchMarker(context, boxOrigin, *marker, style, font);
907                 break;
908             case DocumentMarker::Replacement:
909                 break;
910 #if ENABLE(TELEPHONE_NUMBER_DETECTION)
911             case DocumentMarker::TelephoneNumber:
912                 break;
913 #endif
914             default:
915                 ASSERT_NOT_REACHED();
916         }
917
918     }
919 }
920
921 void InlineTextBox::paintCompositionUnderline(GraphicsContext& context, const FloatPoint& boxOrigin, const CompositionUnderline& underline)
922 {
923     if (m_truncation == cFullTruncation)
924         return;
925     
926     float start = 0; // start of line to draw, relative to tx
927     float width = m_logicalWidth; // how much line to draw
928     bool useWholeWidth = true;
929     unsigned paintStart = m_start;
930     unsigned paintEnd = end() + 1; // end points at the last char, not past it
931     if (paintStart <= underline.startOffset) {
932         paintStart = underline.startOffset;
933         useWholeWidth = false;
934         start = renderer().width(m_start, paintStart - m_start, textPos(), isFirstLine());
935     }
936     if (paintEnd != underline.endOffset) {      // end points at the last char, not past it
937         paintEnd = std::min(paintEnd, (unsigned)underline.endOffset);
938         useWholeWidth = false;
939     }
940     if (m_truncation != cNoTruncation) {
941         paintEnd = std::min(paintEnd, (unsigned)m_start + m_truncation);
942         useWholeWidth = false;
943     }
944     if (!useWholeWidth) {
945         width = renderer().width(paintStart, paintEnd - paintStart, textPos() + start, isFirstLine());
946         mirrorRTLSegment(m_logicalWidth, direction(), start, width);
947     }
948
949     // Thick marked text underlines are 2px thick as long as there is room for the 2px line under the baseline.
950     // All other marked text underlines are 1px thick.
951     // If there's not enough space the underline will touch or overlap characters.
952     int lineThickness = 1;
953     int baseline = lineStyle().fontMetrics().ascent();
954     if (underline.thick && logicalHeight() - baseline >= 2)
955         lineThickness = 2;
956
957     // We need to have some space between underlines of subsequent clauses, because some input methods do not use different underline styles for those.
958     // We make each line shorter, which has a harmless side effect of shortening the first and last clauses, too.
959     start += 1;
960     width -= 2;
961
962     context.setStrokeColor(underline.color);
963     context.setStrokeThickness(lineThickness);
964     context.drawLineForText(FloatPoint(boxOrigin.x() + start, boxOrigin.y() + logicalHeight() - lineThickness), width, renderer().document().printing());
965 }
966
967 int InlineTextBox::caretMinOffset() const
968 {
969     return m_start;
970 }
971
972 int InlineTextBox::caretMaxOffset() const
973 {
974     return m_start + m_len;
975 }
976
977 float InlineTextBox::textPos() const
978 {
979     // When computing the width of a text run, RenderBlock::computeInlineDirectionPositionsForLine() doesn't include the actual offset
980     // from the containing block edge in its measurement. textPos() should be consistent so the text are rendered in the same width.
981     if (logicalLeft() == 0)
982         return 0;
983     return logicalLeft() - root().logicalLeft();
984 }
985
986 int InlineTextBox::offsetForPosition(float lineOffset, bool includePartialGlyphs) const
987 {
988     if (isLineBreak())
989         return 0;
990
991     if (lineOffset - logicalLeft() > logicalWidth())
992         return isLeftToRightDirection() ? len() : 0;
993     if (lineOffset - logicalLeft() < 0)
994         return isLeftToRightDirection() ? 0 : len();
995
996     const RenderStyle& lineStyle = this->lineStyle();
997     const FontCascade& font = fontToUse(lineStyle, renderer());
998     return font.offsetForPosition(constructTextRun(lineStyle), lineOffset - logicalLeft(), includePartialGlyphs);
999 }
1000
1001 float InlineTextBox::positionForOffset(unsigned offset) const
1002 {
1003     ASSERT(offset >= m_start);
1004     ASSERT(offset <= m_start + len());
1005
1006     if (isLineBreak())
1007         return logicalLeft();
1008
1009     const RenderStyle& lineStyle = this->lineStyle();
1010     const FontCascade& font = fontToUse(lineStyle, renderer());
1011     unsigned from = !isLeftToRightDirection() ? clampedOffset(offset) : 0;
1012     unsigned to = !isLeftToRightDirection() ? m_len : clampedOffset(offset);
1013     // FIXME: Do we need to add rightBearing here?
1014     LayoutRect selectionRect = LayoutRect(logicalLeft(), 0, 0, 0);
1015     TextRun run = constructTextRun(lineStyle);
1016     font.adjustSelectionRectForText(run, selectionRect, from, to);
1017     return snapRectToDevicePixelsWithWritingDirection(selectionRect, renderer().document().deviceScaleFactor(), run.ltr()).maxX();
1018 }
1019
1020 StringView InlineTextBox::substringToRender(Optional<unsigned> overridingLength) const
1021 {
1022     return StringView(renderer().text()).substring(start(), overridingLength.valueOr(len()));
1023 }
1024
1025 String InlineTextBox::hyphenatedStringForTextRun(const RenderStyle& style, Optional<unsigned> alternateLength) const
1026 {
1027     ASSERT(hasHyphen());
1028     return makeString(substringToRender(alternateLength), style.hyphenString());
1029 }
1030
1031 TextRun InlineTextBox::constructTextRun(const RenderStyle& style, StringView alternateStringToRender, Optional<unsigned> alternateLength) const
1032 {
1033     if (alternateStringToRender.isNull())
1034         return constructTextRun(style, substringToRender(alternateLength), renderer().textLength() - start());
1035     return constructTextRun(style, alternateStringToRender, alternateStringToRender.length());
1036 }
1037
1038 TextRun InlineTextBox::constructTextRun(const RenderStyle& style, StringView string, unsigned maximumLength) const
1039 {
1040     ASSERT(maximumLength >= string.length());
1041
1042     TextRun run(string, textPos(), expansion(), expansionBehavior(), direction(), dirOverride() || style.rtlOrdering() == VisualOrder, !renderer().canUseSimpleFontCodePath());
1043     run.setTabSize(!style.collapseWhiteSpace(), style.tabSize());
1044
1045     // Propagate the maximum length of the characters buffer to the TextRun, even when we're only processing a substring.
1046     run.setCharactersLength(maximumLength);
1047     ASSERT(run.charactersLength() >= run.length());
1048     return run;
1049 }
1050
1051 ExpansionBehavior InlineTextBox::expansionBehavior() const
1052 {
1053     ExpansionBehavior leadingBehavior;
1054     if (forceLeadingExpansion())
1055         leadingBehavior = ForceLeadingExpansion;
1056     else if (canHaveLeadingExpansion())
1057         leadingBehavior = AllowLeadingExpansion;
1058     else
1059         leadingBehavior = ForbidLeadingExpansion;
1060
1061     ExpansionBehavior trailingBehavior;
1062     if (forceTrailingExpansion())
1063         trailingBehavior = ForceTrailingExpansion;
1064     else if (expansion() && nextLeafChild() && !nextLeafChild()->isLineBreak())
1065         trailingBehavior = AllowTrailingExpansion;
1066     else
1067         trailingBehavior = ForbidTrailingExpansion;
1068
1069     return leadingBehavior | trailingBehavior;
1070 }
1071
1072 #if ENABLE(TREE_DEBUGGING)
1073
1074 const char* InlineTextBox::boxName() const
1075 {
1076     return "InlineTextBox";
1077 }
1078
1079 void InlineTextBox::showLineBox(bool mark, int depth) const
1080 {
1081     fprintf(stderr, "-------- %c-", isDirty() ? 'D' : '-');
1082
1083     int printedCharacters = 0;
1084     if (mark) {
1085         fprintf(stderr, "*");
1086         ++printedCharacters;
1087     }
1088     while (++printedCharacters <= depth * 2)
1089         fputc(' ', stderr);
1090
1091     String value = renderer().text();
1092     value = value.substring(start(), len());
1093     value.replaceWithLiteral('\\', "\\\\");
1094     value.replaceWithLiteral('\n', "\\n");
1095     fprintf(stderr, "%s  (%.2f, %.2f) (%.2f, %.2f) (%p) renderer->(%p) run(%d, %d) \"%s\"\n", boxName(), x(), y(), width(), height(), this, &renderer(), start(), start() + len(), value.utf8().data());
1096 }
1097
1098 #endif
1099
1100 } // namespace WebCore