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