f2522d90d926914d88d1bf7b0d1e2555a39be28f
[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, 2005, 2006, 2007, 2008, 2009, 2010, 2011 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 "FontCache.h"
34 #include "Frame.h"
35 #include "GraphicsContext.h"
36 #include "HitTestResult.h"
37 #include "ImageBuffer.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 "SVGTextRunRenderingContext.h"
50 #include "Text.h"
51 #include "TextPaintStyle.h"
52 #include "TextPainter.h"
53 #include "break_lines.h"
54 #include <wtf/text/CString.h>
55
56 namespace WebCore {
57
58 struct SameSizeAsInlineTextBox : public InlineBox {
59     unsigned variables[1];
60     unsigned short variables2[2];
61     void* pointers[2];
62 };
63
64 COMPILE_ASSERT(sizeof(InlineTextBox) == sizeof(SameSizeAsInlineTextBox), InlineTextBox_should_stay_small);
65
66 typedef WTF::HashMap<const InlineTextBox*, LayoutRect> InlineTextBoxOverflowMap;
67 static InlineTextBoxOverflowMap* gTextBoxesWithOverflow;
68
69 #if ENABLE(CSS3_TEXT_DECORATION_SKIP_INK)
70 static bool compareTuples(std::pair<float, float> l, std::pair<float, float> r)
71 {
72     return l.first < r.first;
73 }
74
75 static DashArray translateIntersectionPointsToSkipInkBoundaries(const DashArray& intersections, float dilationAmount, float totalWidth)
76 {
77     ASSERT(!(intersections.size() % 2));
78     
79     // Step 1: Make pairs so we can sort based on range starting-point. We dilate the ranges in this step as well.
80     Vector<std::pair<float, float>> tuples;
81     for (auto i = intersections.begin(); i != intersections.end(); i++, i++)
82         tuples.append(std::make_pair(*i - dilationAmount, *(i + 1) + dilationAmount));
83     std::sort(tuples.begin(), tuples.end(), &compareTuples);
84
85     // Step 2: Deal with intersecting ranges.
86     Vector<std::pair<float, float>> intermediateTuples;
87     if (tuples.size() >= 2) {
88         intermediateTuples.append(*tuples.begin());
89         auto lastIntermediate = intermediateTuples.begin();
90         for (auto i = tuples.begin() + 1; i != tuples.end(); i++) {
91             float& firstEnd = lastIntermediate->second;
92             float secondStart = i->first;
93             float secondEnd = i->second;
94             if (secondStart <= firstEnd && secondEnd <= firstEnd) {
95                 // Ignore this range completely
96             } else if (secondStart <= firstEnd)
97                 firstEnd = secondEnd;
98             else {
99                 intermediateTuples.append(*i);
100                 ++lastIntermediate;
101             }
102         }
103     } else
104         intermediateTuples = tuples;
105
106     // Step 3: Output the space between the ranges, but only if the space warrants an underline.
107     float previous = 0;
108     DashArray result;
109     for (auto i = intermediateTuples.begin(); i != intermediateTuples.end(); i++) {
110         if (i->first - previous > dilationAmount) {
111             result.append(previous);
112             result.append(i->first);
113         }
114         previous = i->second;
115     }
116     if (totalWidth - previous > dilationAmount) {
117         result.append(previous);
118         result.append(totalWidth);
119     }
120     
121     return result;
122 }
123
124 static void drawSkipInkUnderline(TextPainter& textPainter, GraphicsContext& context, FloatPoint localOrigin, float underlineOffset, float width, bool isPrinting, int m_start, int m_len)
125 {
126     FloatPoint adjustedLocalOrigin = localOrigin;
127     adjustedLocalOrigin.move(0, underlineOffset);
128     FloatRect underlineBoundingBox = context.computeLineBoundsForText(adjustedLocalOrigin, width, isPrinting);
129     DashArray intersections = textPainter.dashesForIntersectionsWithRect(underlineBoundingBox, m_start, m_start + m_len);
130     DashArray a = translateIntersectionPointsToSkipInkBoundaries(intersections, underlineBoundingBox.height(), width);
131
132     ASSERT(!(a.size() % 2));
133     for (auto i = a.begin(); i != a.end(); i++, i++)
134         context.drawLineForText(FloatPoint(localOrigin.x() + *i, localOrigin.y() + underlineOffset), *(i+1) - *i, isPrinting);
135 }
136 #endif
137
138 InlineTextBox::~InlineTextBox()
139 {
140     if (!knownToHaveNoOverflow() && gTextBoxesWithOverflow)
141         gTextBoxesWithOverflow->remove(this);
142 }
143
144 void InlineTextBox::markDirty(bool dirty)
145 {
146     if (dirty) {
147         m_len = 0;
148         m_start = 0;
149     }
150     InlineBox::markDirty(dirty);
151 }
152
153 LayoutRect InlineTextBox::logicalOverflowRect() const
154 {
155     if (knownToHaveNoOverflow() || !gTextBoxesWithOverflow)
156         return enclosingIntRect(logicalFrameRect());
157     return gTextBoxesWithOverflow->get(this);
158 }
159
160 void InlineTextBox::setLogicalOverflowRect(const LayoutRect& rect)
161 {
162     ASSERT(!knownToHaveNoOverflow());
163     if (!gTextBoxesWithOverflow)
164         gTextBoxesWithOverflow = new InlineTextBoxOverflowMap;
165     gTextBoxesWithOverflow->add(this, rect);
166 }
167
168 int InlineTextBox::baselinePosition(FontBaseline baselineType) const
169 {
170     if (!parent())
171         return 0;
172     if (&parent()->renderer() == renderer().parent())
173         return parent()->baselinePosition(baselineType);
174     return toRenderBoxModelObject(renderer().parent())->baselinePosition(baselineType, isFirstLine(), isHorizontal() ? HorizontalLine : VerticalLine, PositionOnContainingLine);
175 }
176
177 LayoutUnit InlineTextBox::lineHeight() const
178 {
179     if (!renderer().parent())
180         return 0;
181     if (&parent()->renderer() == renderer().parent())
182         return parent()->lineHeight();
183     return toRenderBoxModelObject(renderer().parent())->lineHeight(isFirstLine(), isHorizontal() ? HorizontalLine : VerticalLine, PositionOnContainingLine);
184 }
185
186 LayoutUnit InlineTextBox::selectionTop() const
187 {
188     return root().selectionTop();
189 }
190
191 LayoutUnit InlineTextBox::selectionBottom() const
192 {
193     return root().selectionBottom();
194 }
195
196 LayoutUnit InlineTextBox::selectionHeight() const
197 {
198     return root().selectionHeight();
199 }
200
201 bool InlineTextBox::isSelected(int startPos, int endPos) const
202 {
203     LayoutUnit sPos = std::max<LayoutUnit>(startPos - m_start, 0);
204     LayoutUnit ePos = std::min<LayoutUnit>(endPos - m_start, m_len);
205     return (sPos < ePos);
206 }
207
208 RenderObject::SelectionState InlineTextBox::selectionState()
209 {
210     RenderObject::SelectionState state = renderer().selectionState();
211     if (state == RenderObject::SelectionStart || state == RenderObject::SelectionEnd || state == RenderObject::SelectionBoth) {
212         int startPos, endPos;
213         renderer().selectionStartEnd(startPos, endPos);
214         // The position after a hard line break is considered to be past its end.
215         int lastSelectable = start() + len() - (isLineBreak() ? 1 : 0);
216
217         bool start = (state != RenderObject::SelectionEnd && startPos >= m_start && startPos < m_start + m_len);
218         bool end = (state != RenderObject::SelectionStart && endPos > m_start && endPos <= lastSelectable);
219         if (start && end)
220             state = RenderObject::SelectionBoth;
221         else if (start)
222             state = RenderObject::SelectionStart;
223         else if (end)
224             state = RenderObject::SelectionEnd;
225         else if ((state == RenderObject::SelectionEnd || startPos < m_start) &&
226                  (state == RenderObject::SelectionStart || endPos > lastSelectable))
227             state = RenderObject::SelectionInside;
228         else if (state == RenderObject::SelectionBoth)
229             state = RenderObject::SelectionNone;
230     }
231
232     // If there are ellipsis following, make sure their selection is updated.
233     if (m_truncation != cNoTruncation && root().ellipsisBox()) {
234         EllipsisBox* ellipsis = root().ellipsisBox();
235         if (state != RenderObject::SelectionNone) {
236             int start, end;
237             selectionStartEnd(start, end);
238             // The ellipsis should be considered to be selected if the end of
239             // the selection is past the beginning of the truncation and the
240             // beginning of the selection is before or at the beginning of the
241             // truncation.
242             ellipsis->setSelectionState(end >= m_truncation && start <= m_truncation ?
243                 RenderObject::SelectionInside : RenderObject::SelectionNone);
244         } else
245             ellipsis->setSelectionState(RenderObject::SelectionNone);
246     }
247
248     return state;
249 }
250
251 static void adjustCharactersAndLengthForHyphen(BufferForAppendingHyphen& charactersWithHyphen, const RenderStyle& style, String& string, int& length)
252 {
253     const AtomicString& hyphenString = style.hyphenString();
254     charactersWithHyphen.reserveCapacity(length + hyphenString.length());
255     charactersWithHyphen.append(string);
256     charactersWithHyphen.append(hyphenString);
257     string = charactersWithHyphen.toString();
258     length += hyphenString.length();
259 }
260
261 static const Font& fontToUse(const RenderStyle& style, const RenderText& renderer)
262 {
263     if (style.hasTextCombine() && renderer.isCombineText()) {
264         const RenderCombineText& textCombineRenderer = toRenderCombineText(renderer);
265         if (textCombineRenderer.isCombined())
266             return textCombineRenderer.textCombineFont();
267     }
268     return style.font();
269 }
270
271 LayoutRect InlineTextBox::localSelectionRect(int startPos, int endPos) const
272 {
273     int sPos = std::max(startPos - m_start, 0);
274     int ePos = std::min(endPos - m_start, (int)m_len);
275     
276     if (sPos > ePos)
277         return LayoutRect();
278
279     FontCachePurgePreventer fontCachePurgePreventer;
280
281     LayoutUnit selTop = selectionTop();
282     LayoutUnit selHeight = selectionHeight();
283     const RenderStyle& lineStyle = this->lineStyle();
284     const Font& font = fontToUse(lineStyle, renderer());
285
286     BufferForAppendingHyphen charactersWithHyphen;
287     bool respectHyphen = ePos == m_len && hasHyphen();
288     TextRun textRun = constructTextRun(lineStyle, font, respectHyphen ? &charactersWithHyphen : 0);
289     if (respectHyphen)
290         endPos = textRun.length();
291
292     FloatPoint startingPoint = FloatPoint(logicalLeft(), selTop);
293     LayoutRect r;
294     if (sPos || ePos != static_cast<int>(m_len))
295         r = enclosingIntRect(font.selectionRectForText(textRun, startingPoint, selHeight, sPos, ePos));
296     else // Avoid computing the font width when the entire line box is selected as an optimization.
297         r = enclosingIntRect(FloatRect(startingPoint, FloatSize(m_logicalWidth, selHeight)));
298
299     LayoutUnit logicalWidth = r.width();
300     if (r.x() > logicalRight())
301         logicalWidth  = 0;
302     else if (r.maxX() > logicalRight())
303         logicalWidth = logicalRight() - r.x();
304
305     LayoutPoint topPoint = isHorizontal() ? LayoutPoint(r.x(), selTop) : LayoutPoint(selTop, r.x());
306     LayoutUnit width = isHorizontal() ? logicalWidth : selHeight;
307     LayoutUnit height = isHorizontal() ? selHeight : logicalWidth;
308
309     return LayoutRect(topPoint, LayoutSize(width, height));
310 }
311
312 void InlineTextBox::deleteLine()
313 {
314     renderer().removeTextBox(*this);
315     delete this;
316 }
317
318 void InlineTextBox::extractLine()
319 {
320     if (extracted())
321         return;
322
323     renderer().extractTextBox(*this);
324 }
325
326 void InlineTextBox::attachLine()
327 {
328     if (!extracted())
329         return;
330     
331     renderer().attachTextBox(*this);
332 }
333
334 float InlineTextBox::placeEllipsisBox(bool flowIsLTR, float visibleLeftEdge, float visibleRightEdge, float ellipsisWidth, float &truncatedWidth, bool& foundBox)
335 {
336     if (foundBox) {
337         m_truncation = cFullTruncation;
338         return -1;
339     }
340
341     // For LTR this is the left edge of the box, for RTL, the right edge in parent coordinates.
342     float ellipsisX = flowIsLTR ? visibleRightEdge - ellipsisWidth : visibleLeftEdge + ellipsisWidth;
343     
344     // Criteria for full truncation:
345     // LTR: the left edge of the ellipsis is to the left of our text run.
346     // RTL: the right edge of the ellipsis is to the right of our text run.
347     bool ltrFullTruncation = flowIsLTR && ellipsisX <= left();
348     bool rtlFullTruncation = !flowIsLTR && ellipsisX >= left() + logicalWidth();
349     if (ltrFullTruncation || rtlFullTruncation) {
350         // Too far.  Just set full truncation, but return -1 and let the ellipsis just be placed at the edge of the box.
351         m_truncation = cFullTruncation;
352         foundBox = true;
353         return -1;
354     }
355
356     bool ltrEllipsisWithinBox = flowIsLTR && (ellipsisX < right());
357     bool rtlEllipsisWithinBox = !flowIsLTR && (ellipsisX > left());
358     if (ltrEllipsisWithinBox || rtlEllipsisWithinBox) {
359         foundBox = true;
360
361         // The inline box may have different directionality than it's parent.  Since truncation
362         // behavior depends both on both the parent and the inline block's directionality, we
363         // must keep track of these separately.
364         bool ltr = isLeftToRightDirection();
365         if (ltr != flowIsLTR) {
366           // Width in pixels of the visible portion of the box, excluding the ellipsis.
367           int visibleBoxWidth = visibleRightEdge - visibleLeftEdge  - ellipsisWidth;
368           ellipsisX = ltr ? left() + visibleBoxWidth : right() - visibleBoxWidth;
369         }
370
371         int offset = offsetForPosition(ellipsisX, false);
372         if (offset == 0) {
373             // No characters should be rendered.  Set ourselves to full truncation and place the ellipsis at the min of our start
374             // and the ellipsis edge.
375             m_truncation = cFullTruncation;
376             truncatedWidth += ellipsisWidth;
377             return flowIsLTR ? std::min(ellipsisX, x()) : std::max(ellipsisX, right() - ellipsisWidth);
378         }
379
380         // Set the truncation index on the text run.
381         m_truncation = offset;
382
383         // If we got here that means that we were only partially truncated and we need to return the pixel offset at which
384         // to place the ellipsis.
385         float widthOfVisibleText = renderer().width(m_start, offset, textPos(), isFirstLine());
386
387         // The ellipsis needs to be placed just after the last visible character.
388         // Where "after" is defined by the flow directionality, not the inline
389         // box directionality.
390         // e.g. In the case of an LTR inline box truncated in an RTL flow then we can
391         // have a situation such as |Hello| -> |...He|
392         truncatedWidth += widthOfVisibleText + ellipsisWidth;
393         if (flowIsLTR)
394             return left() + widthOfVisibleText;
395         else
396             return right() - widthOfVisibleText - ellipsisWidth;
397     }
398     truncatedWidth += logicalWidth();
399     return -1;
400 }
401
402
403
404 bool InlineTextBox::isLineBreak() const
405 {
406     return renderer().style().preserveNewline() && len() == 1 && (*renderer().text())[start()] == '\n';
407 }
408
409 bool InlineTextBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit /* lineTop */, LayoutUnit /*lineBottom*/)
410 {
411     if (!visibleToHitTesting())
412         return false;
413
414     if (isLineBreak())
415         return false;
416
417     if (m_truncation == cFullTruncation)
418         return false;
419
420     FloatRect rect(locationIncludingFlipping(), size());
421     // Make sure truncated text is ignored while hittesting.
422     if (m_truncation != cNoTruncation) {
423         LayoutUnit widthOfVisibleText = renderer().width(m_start, m_truncation, textPos(), isFirstLine());
424
425         if (isHorizontal())
426             renderer().style().isLeftToRightDirection() ? rect.setWidth(widthOfVisibleText) : rect.shiftXEdgeTo(right() - widthOfVisibleText);
427         else
428             rect.setHeight(widthOfVisibleText);
429     }
430
431     rect.moveBy(accumulatedOffset);
432
433     if (locationInContainer.intersects(rect)) {
434         renderer().updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - toLayoutSize(accumulatedOffset)));
435         if (!result.addNodeToRectBasedTestResult(renderer().textNode(), request, locationInContainer, rect))
436             return true;
437     }
438     return false;
439 }
440
441 FloatSize InlineTextBox::applyShadowToGraphicsContext(GraphicsContext* context, const ShadowData* shadow, const FloatRect& textRect, bool stroked, bool opaque, bool horizontal)
442 {
443     if (!shadow)
444         return FloatSize();
445
446     FloatSize extraOffset;
447     int shadowX = horizontal ? shadow->x() : shadow->y();
448     int shadowY = horizontal ? shadow->y() : -shadow->x();
449     FloatSize shadowOffset(shadowX, shadowY);
450     int shadowRadius = shadow->radius();
451     const Color& shadowColor = shadow->color();
452
453     if (shadow->next() || stroked || !opaque) {
454         FloatRect shadowRect(textRect);
455         shadowRect.inflate(shadow->paintingExtent());
456         shadowRect.move(shadowOffset);
457         context->save();
458         context->clip(shadowRect);
459
460         extraOffset = FloatSize(0, 2 * textRect.height() + std::max(0.0f, shadowOffset.height()) + shadowRadius);
461         shadowOffset -= extraOffset;
462     }
463
464     context->setShadow(shadowOffset, shadowRadius, shadowColor, context->fillColorSpace());
465     return extraOffset;
466 }
467
468 bool InlineTextBox::getEmphasisMarkPosition(const RenderStyle& style, TextEmphasisPosition& emphasisPosition) const
469 {
470     // This function returns true if there are text emphasis marks and they are suppressed by ruby text.
471     if (style.textEmphasisMark() == TextEmphasisMarkNone)
472         return false;
473
474     emphasisPosition = style.textEmphasisPosition();
475     if (emphasisPosition == TextEmphasisPositionUnder)
476         return true; // Ruby text is always over, so it cannot suppress emphasis marks under.
477
478     RenderBlock* containingBlock = renderer().containingBlock();
479     if (!containingBlock->isRubyBase())
480         return true; // This text is not inside a ruby base, so it does not have ruby text over it.
481
482     if (!containingBlock->parent()->isRubyRun())
483         return true; // Cannot get the ruby text.
484
485     RenderRubyText* rubyText = toRenderRubyRun(containingBlock->parent())->rubyText();
486
487     // The emphasis marks over are suppressed only if there is a ruby text box and it not empty.
488     return !rubyText || !rubyText->hasLines();
489 }
490
491 void InlineTextBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit /*lineTop*/, LayoutUnit /*lineBottom*/)
492 {
493     if (isLineBreak() || !paintInfo.shouldPaintWithinRoot(renderer()) || renderer().style().visibility() != VISIBLE
494         || m_truncation == cFullTruncation || paintInfo.phase == PaintPhaseOutline || !m_len)
495         return;
496
497     ASSERT(paintInfo.phase != PaintPhaseSelfOutline && paintInfo.phase != PaintPhaseChildOutlines);
498
499     LayoutUnit logicalLeftSide = logicalLeftVisualOverflow();
500     LayoutUnit logicalRightSide = logicalRightVisualOverflow();
501     LayoutUnit logicalStart = logicalLeftSide + (isHorizontal() ? paintOffset.x() : paintOffset.y());
502     LayoutUnit logicalExtent = logicalRightSide - logicalLeftSide;
503     
504     LayoutUnit paintEnd = isHorizontal() ? paintInfo.rect.maxX() : paintInfo.rect.maxY();
505     LayoutUnit paintStart = isHorizontal() ? paintInfo.rect.x() : paintInfo.rect.y();
506     
507     LayoutPoint adjustedPaintOffset = roundedIntPoint(paintOffset);
508     
509     if (logicalStart >= paintEnd || logicalStart + logicalExtent <= paintStart)
510         return;
511
512     bool isPrinting = renderer().document().printing();
513     
514     // Determine whether or not we're selected.
515     bool haveSelection = !isPrinting && paintInfo.phase != PaintPhaseTextClip && selectionState() != RenderObject::SelectionNone;
516     if (!haveSelection && paintInfo.phase == PaintPhaseSelection)
517         // When only painting the selection, don't bother to paint if there is none.
518         return;
519
520     if (m_truncation != cNoTruncation) {
521         if (renderer().containingBlock()->style().isLeftToRightDirection() != isLeftToRightDirection()) {
522             // Make the visible fragment of text hug the edge closest to the rest of the run by moving the origin
523             // at which we start drawing text.
524             // e.g. In the case of LTR text truncated in an RTL Context, the correct behavior is:
525             // |Hello|CBA| -> |...He|CBA|
526             // In order to draw the fragment "He" aligned to the right edge of it's box, we need to start drawing
527             // farther to the right.
528             // NOTE: WebKit's behavior differs from that of IE which appears to just overlay the ellipsis on top of the
529             // truncated string i.e.  |Hello|CBA| -> |...lo|CBA|
530             LayoutUnit widthOfVisibleText = renderer().width(m_start, m_truncation, textPos(), isFirstLine());
531             LayoutUnit widthOfHiddenText = m_logicalWidth - widthOfVisibleText;
532             LayoutSize truncationOffset(isLeftToRightDirection() ? widthOfHiddenText : -widthOfHiddenText, 0);
533             adjustedPaintOffset.move(isHorizontal() ? truncationOffset : truncationOffset.transposedSize());
534         }
535     }
536
537     GraphicsContext* context = paintInfo.context;
538
539     const RenderStyle& lineStyle = this->lineStyle();
540     
541     adjustedPaintOffset.move(0, lineStyle.isHorizontalWritingMode() ? 0 : -logicalHeight());
542
543     FloatPoint boxOrigin = locationIncludingFlipping();
544     boxOrigin.move(adjustedPaintOffset.x(), adjustedPaintOffset.y());
545     FloatRect boxRect(boxOrigin, FloatSize(logicalWidth(), logicalHeight()));
546
547     RenderCombineText* combinedText = lineStyle.hasTextCombine() && renderer().isCombineText() && toRenderCombineText(renderer()).isCombined() ? &toRenderCombineText(renderer()) : 0;
548
549     bool shouldRotate = !isHorizontal() && !combinedText;
550     if (shouldRotate)
551         context->concatCTM(rotation(boxRect, Clockwise));
552
553     // Determine whether or not we have composition underlines to draw.
554     bool containsComposition = renderer().textNode() && renderer().frame().editor().compositionNode() == renderer().textNode();
555     bool useCustomUnderlines = containsComposition && renderer().frame().editor().compositionUsesCustomUnderlines();
556
557     // Determine the text colors and selection colors.
558     TextPaintStyle textPaintStyle = computeTextPaintStyle(renderer(), lineStyle, paintInfo);
559
560     bool paintSelectedTextOnly;
561     bool paintSelectedTextSeparately;
562     const ShadowData* selectionShadow;
563     TextPaintStyle selectionPaintStyle = computeTextSelectionPaintStyle(textPaintStyle, renderer(), lineStyle, paintInfo, paintSelectedTextOnly, paintSelectedTextSeparately, selectionShadow);
564
565     // Set our font.
566     const Font& font = fontToUse(lineStyle, renderer());
567
568     FloatPoint textOrigin = FloatPoint(boxOrigin.x(), boxOrigin.y() + font.fontMetrics().ascent());
569
570     if (combinedText)
571         combinedText->adjustTextOrigin(textOrigin, boxRect);
572
573     // 1. Paint backgrounds behind text if needed. Examples of such backgrounds include selection
574     // and composition underlines.
575     if (paintInfo.phase != PaintPhaseSelection && paintInfo.phase != PaintPhaseTextClip && !isPrinting) {
576 #if PLATFORM(MAC)
577         // Custom highlighters go behind everything else.
578         if (lineStyle.highlight() != nullAtom && !context->paintingDisabled())
579             paintCustomHighlight(adjustedPaintOffset, lineStyle.highlight());
580 #endif
581
582         if (containsComposition && !useCustomUnderlines)
583             paintCompositionBackground(context, boxOrigin, lineStyle, font,
584                 renderer().frame().editor().compositionStart(),
585                 renderer().frame().editor().compositionEnd());
586
587         paintDocumentMarkers(context, boxOrigin, lineStyle, font, true);
588
589         if (haveSelection && !useCustomUnderlines)
590             paintSelection(context, boxOrigin, lineStyle, font, selectionPaintStyle.fillColor);
591     }
592
593     if (Page* page = renderer().frame().page()) {
594         // FIXME: Right now, InlineTextBoxes never call addRelevantUnpaintedObject() even though they might
595         // legitimately be unpainted if they are waiting on a slow-loading web font. We should fix that, and
596         // when we do, we will have to account for the fact the InlineTextBoxes do not always have unique
597         // renderers and Page currently relies on each unpainted object having a unique renderer.
598         if (paintInfo.phase == PaintPhaseForeground)
599             page->addRelevantRepaintedObject(&renderer(), IntRect(boxOrigin.x(), boxOrigin.y(), logicalWidth(), logicalHeight()));
600     }
601
602     // 2. Now paint the foreground, including text and decorations like underline/overline (in quirks mode only).
603     int length = m_len;
604     int maximumLength;
605     String string;
606     if (!combinedText) {
607         string = renderer().text();
608         if (static_cast<unsigned>(length) != string.length() || m_start) {
609             ASSERT_WITH_SECURITY_IMPLICATION(static_cast<unsigned>(m_start + length) <= string.length());
610             string = string.substringSharingImpl(m_start, length);
611         }
612         maximumLength = renderer().textLength() - m_start;
613     } else {
614         combinedText->getStringToRender(m_start, string, length);
615         maximumLength = length;
616     }
617
618     BufferForAppendingHyphen charactersWithHyphen;
619     TextRun textRun = constructTextRun(lineStyle, font, string, maximumLength, hasHyphen() ? &charactersWithHyphen : 0);
620     if (hasHyphen())
621         length = textRun.length();
622
623     int sPos = 0;
624     int ePos = 0;
625     if (haveSelection && (paintSelectedTextOnly || paintSelectedTextSeparately))
626         selectionStartEnd(sPos, ePos);
627
628     if (m_truncation != cNoTruncation) {
629         sPos = std::min<int>(sPos, m_truncation);
630         ePos = std::min<int>(ePos, m_truncation);
631         length = m_truncation;
632     }
633
634     int emphasisMarkOffset = 0;
635     TextEmphasisPosition emphasisMarkPosition;
636     bool hasTextEmphasis = getEmphasisMarkPosition(lineStyle, emphasisMarkPosition);
637     const AtomicString& emphasisMark = hasTextEmphasis ? lineStyle.textEmphasisMarkString() : nullAtom;
638     if (!emphasisMark.isEmpty())
639         emphasisMarkOffset = emphasisMarkPosition == TextEmphasisPositionOver ? -font.fontMetrics().ascent() - font.emphasisMarkDescent(emphasisMark) : font.fontMetrics().descent() + font.emphasisMarkAscent(emphasisMark);
640
641     const ShadowData* textShadow = paintInfo.forceBlackText() ? 0 : lineStyle.textShadow();
642
643     TextPainter textPainter(*context, paintSelectedTextOnly, paintSelectedTextSeparately, font, sPos, ePos, length, emphasisMark, combinedText, textRun, boxRect, textOrigin, emphasisMarkOffset, textShadow, selectionShadow, isHorizontal(), textPaintStyle, selectionPaintStyle);
644     textPainter.paintText();
645
646     // Paint decorations
647     TextDecoration textDecorations = lineStyle.textDecorationsInEffect();
648     if (textDecorations != TextDecorationNone && paintInfo.phase != PaintPhaseSelection) {
649         updateGraphicsContext(*context, textPaintStyle);
650         if (combinedText)
651             context->concatCTM(rotation(boxRect, Clockwise));
652         paintDecoration(*context, boxOrigin, textDecorations, lineStyle.textDecorationStyle(), textShadow, textPainter);
653         if (combinedText)
654             context->concatCTM(rotation(boxRect, Counterclockwise));
655     }
656
657     if (paintInfo.phase == PaintPhaseForeground) {
658         paintDocumentMarkers(context, boxOrigin, lineStyle, font, false);
659
660         if (useCustomUnderlines) {
661             const Vector<CompositionUnderline>& underlines = renderer().frame().editor().customCompositionUnderlines();
662             size_t numUnderlines = underlines.size();
663
664             for (size_t index = 0; index < numUnderlines; ++index) {
665                 const CompositionUnderline& underline = underlines[index];
666
667                 if (underline.endOffset <= start())
668                     // underline is completely before this run.  This might be an underline that sits
669                     // before the first run we draw, or underlines that were within runs we skipped 
670                     // due to truncation.
671                     continue;
672                 
673                 if (underline.startOffset <= end()) {
674                     // underline intersects this run.  Paint it.
675                     paintCompositionUnderline(context, boxOrigin, underline);
676                     if (underline.endOffset > end() + 1)
677                         // underline also runs into the next run. Bail now, no more marker advancement.
678                         break;
679                 } else
680                     // underline is completely after this run, bail.  A later run will paint it.
681                     break;
682             }
683         }
684     }
685     
686     if (shouldRotate)
687         context->concatCTM(rotation(boxRect, Counterclockwise));
688 }
689
690 void InlineTextBox::selectionStartEnd(int& sPos, int& ePos)
691 {
692     int startPos, endPos;
693     if (renderer().selectionState() == RenderObject::SelectionInside) {
694         startPos = 0;
695         endPos = renderer().textLength();
696     } else {
697         renderer().selectionStartEnd(startPos, endPos);
698         if (renderer().selectionState() == RenderObject::SelectionStart)
699             endPos = renderer().textLength();
700         else if (renderer().selectionState() == RenderObject::SelectionEnd)
701             startPos = 0;
702     }
703
704     sPos = std::max(startPos - m_start, 0);
705     ePos = std::min(endPos - m_start, (int)m_len);
706 }
707
708 void alignSelectionRectToDevicePixels(FloatRect& rect)
709 {
710     float maxX = floorf(rect.maxX());
711     rect.setX(floorf(rect.x()));
712     rect.setWidth(roundf(maxX - rect.x()));
713 }
714
715 void InlineTextBox::paintSelection(GraphicsContext* context, const FloatPoint& boxOrigin, const RenderStyle& style, const Font& font, Color textColor)
716 {
717 #if ENABLE(TEXT_SELECTION)
718     if (context->paintingDisabled())
719         return;
720
721     // See if we have a selection to paint at all.
722     int sPos, ePos;
723     selectionStartEnd(sPos, ePos);
724     if (sPos >= ePos)
725         return;
726
727     Color c = renderer().selectionBackgroundColor();
728     if (!c.isValid() || c.alpha() == 0)
729         return;
730
731     // If the text color ends up being the same as the selection background, invert the selection
732     // background.
733     if (textColor == c)
734         c = Color(0xff - c.red(), 0xff - c.green(), 0xff - c.blue());
735
736     GraphicsContextStateSaver stateSaver(*context);
737     updateGraphicsContext(*context, TextPaintStyle(c, style.colorSpace())); // Don't draw text at all!
738     
739     // If the text is truncated, let the thing being painted in the truncation
740     // draw its own highlight.
741     int length = m_truncation != cNoTruncation ? m_truncation : m_len;
742     String string = renderer().text();
743
744     if (string.length() != static_cast<unsigned>(length) || m_start) {
745         ASSERT_WITH_SECURITY_IMPLICATION(static_cast<unsigned>(m_start + length) <= string.length());
746         string = string.substringSharingImpl(m_start, length);
747     }
748
749     BufferForAppendingHyphen charactersWithHyphen;
750     bool respectHyphen = ePos == length && hasHyphen();
751     TextRun textRun = constructTextRun(style, font, string, renderer().textLength() - m_start, respectHyphen ? &charactersWithHyphen : 0);
752     if (respectHyphen)
753         ePos = textRun.length();
754
755     const RootInlineBox& rootBox = root();
756     LayoutUnit selectionBottom = rootBox.selectionBottom();
757     LayoutUnit selectionTop = rootBox.selectionTopAdjustedForPrecedingBlock();
758
759     int deltaY = roundToInt(renderer().style().isFlippedLinesWritingMode() ? selectionBottom - logicalBottom() : logicalTop() - selectionTop);
760     int selHeight = std::max(0, roundToInt(selectionBottom - selectionTop));
761
762     FloatPoint localOrigin(boxOrigin.x(), boxOrigin.y() - deltaY);
763     FloatRect clipRect(localOrigin, FloatSize(m_logicalWidth, selHeight));
764     alignSelectionRectToDevicePixels(clipRect);
765
766     context->clip(clipRect);
767
768     context->drawHighlightForText(font, textRun, localOrigin, selHeight, c, style.colorSpace(), sPos, ePos);
769 #else
770     UNUSED_PARAM(context);
771     UNUSED_PARAM(boxOrigin);
772     UNUSED_PARAM(style);
773     UNUSED_PARAM(font);
774     UNUSED_PARAM(textColor);
775 #endif
776 }
777
778 void InlineTextBox::paintCompositionBackground(GraphicsContext* context, const FloatPoint& boxOrigin, const RenderStyle& style, const Font& font, int startPos, int endPos)
779 {
780     int offset = m_start;
781     int sPos = std::max(startPos - offset, 0);
782     int ePos = std::min(endPos - offset, (int)m_len);
783
784     if (sPos >= ePos)
785         return;
786
787     GraphicsContextStateSaver stateSaver(*context);
788
789 #if !PLATFORM(IOS)
790     // FIXME: Is this color still applicable as of Mavericks? for non-Mac ports? We should
791     // be able to move this color information to RenderStyle.
792     Color c = Color(225, 221, 85);
793 #else
794     Color c = style.compositionFillColor();
795 #endif
796     
797     updateGraphicsContext(*context, TextPaintStyle(c, style.colorSpace())); // Don't draw text at all!
798
799     int deltaY = renderer().style().isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
800     int selHeight = selectionHeight();
801     FloatPoint localOrigin(boxOrigin.x(), boxOrigin.y() - deltaY);
802     context->drawHighlightForText(font, constructTextRun(style, font), localOrigin, selHeight, c, style.colorSpace(), sPos, ePos);
803 }
804
805 #if PLATFORM(MAC)
806
807 void InlineTextBox::paintCustomHighlight(const LayoutPoint& paintOffset, const AtomicString& type)
808 {
809     Page* page = renderer().frame().page();
810     if (!page)
811         return;
812
813     const RootInlineBox& rootBox = root();
814     FloatRect rootRect(paintOffset.x() + rootBox.x(), paintOffset.y() + selectionTop(), rootBox.logicalWidth(), selectionHeight());
815     FloatRect textRect(paintOffset.x() + x(), rootRect.y(), logicalWidth(), rootRect.height());
816
817     page->chrome().client().paintCustomHighlight(renderer().textNode(), type, textRect, rootRect, true, false);
818 }
819
820 #endif
821
822 static StrokeStyle textDecorationStyleToStrokeStyle(TextDecorationStyle decorationStyle)
823 {
824     StrokeStyle strokeStyle = SolidStroke;
825     switch (decorationStyle) {
826     case TextDecorationStyleSolid:
827         strokeStyle = SolidStroke;
828         break;
829 #if ENABLE(CSS3_TEXT_DECORATION)
830     case TextDecorationStyleDouble:
831         strokeStyle = DoubleStroke;
832         break;
833     case TextDecorationStyleDotted:
834         strokeStyle = DottedStroke;
835         break;
836     case TextDecorationStyleDashed:
837         strokeStyle = DashedStroke;
838         break;
839     case TextDecorationStyleWavy:
840         strokeStyle = WavyStroke;
841         break;
842 #endif // CSS3_TEXT_DECORATION
843     }
844
845     return strokeStyle;
846 }
847
848 #if ENABLE(CSS3_TEXT_DECORATION)
849 static int computeUnderlineOffset(const TextUnderlinePosition underlinePosition, const FontMetrics& fontMetrics, const InlineTextBox* inlineTextBox, const int textDecorationThickness)
850 {
851     // Compute the gap between the font and the underline. Use at least one
852     // pixel gap, if underline is thick then use a bigger gap.
853     const int gap = std::max<int>(1, ceilf(textDecorationThickness / 2.0));
854
855     // According to the specification TextUnderlinePositionAuto should default to 'alphabetic' for horizontal text
856     // and to 'under Left' for vertical text (e.g. japanese). We support only horizontal text for now.
857     switch (underlinePosition) {
858     case TextUnderlinePositionAlphabetic:
859     case TextUnderlinePositionAuto:
860         return fontMetrics.ascent() + gap; // Position underline near the alphabetic baseline.
861     case TextUnderlinePositionUnder: {
862         // Position underline relative to the under edge of the lowest element's content box.
863         const float offset = inlineTextBox->root().maxLogicalTop() - inlineTextBox->logicalTop();
864         return inlineTextBox->logicalHeight() + gap + std::max<float>(offset, 0);
865     }
866     }
867
868     ASSERT_NOT_REACHED();
869     return fontMetrics.ascent() + gap;
870 }
871
872 static void adjustStepToDecorationLength(float& step, float& controlPointDistance, float length)
873 {
874     ASSERT(step > 0);
875
876     if (length <= 0)
877         return;
878
879     unsigned stepCount = static_cast<unsigned>(length / step);
880
881     // Each Bezier curve starts at the same pixel that the previous one
882     // ended. We need to subtract (stepCount - 1) pixels when calculating the
883     // length covered to account for that.
884     float uncoveredLength = length - (stepCount * step - (stepCount - 1));
885     float adjustment = uncoveredLength / stepCount;
886     step += adjustment;
887     controlPointDistance += adjustment;
888 }
889
890 static void getWavyStrokeParameters(float strokeThickness, float& controlPointDistance, float& step)
891 {
892     // Distance between decoration's axis and Bezier curve's control points.
893     // The height of the curve is based on this distance. Use a minimum of 6 pixels distance since
894     // the actual curve passes approximately at half of that distance, that is 3 pixels.
895     // The minimum height of the curve is also approximately 3 pixels. Increases the curve's height
896     // as strockThickness increases to make the curve looks better.
897     controlPointDistance = 3 * std::max<float>(2, strokeThickness);
898
899     // Increment used to form the diamond shape between start point (p1), control
900     // points and end point (p2) along the axis of the decoration. Makes the
901     // curve wider as strockThickness increases to make the curve looks better.
902     step = 2 * std::max<float>(2, strokeThickness);
903 }
904
905 /*
906  * Draw one cubic Bezier curve and repeat the same pattern long the the decoration's axis.
907  * The start point (p1), controlPoint1, controlPoint2 and end point (p2) of the Bezier curve
908  * form a diamond shape:
909  *
910  *                              step
911  *                         |-----------|
912  *
913  *                   controlPoint1
914  *                         +
915  *
916  *
917  *                  . .
918  *                .     .
919  *              .         .
920  * (x1, y1) p1 +           .            + p2 (x2, y2) - <--- Decoration's axis
921  *                          .         .               |
922  *                            .     .                 |
923  *                              . .                   | controlPointDistance
924  *                                                    |
925  *                                                    |
926  *                         +                          -
927  *                   controlPoint2
928  *
929  *             |-----------|
930  *                 step
931  */
932 static void strokeWavyTextDecoration(GraphicsContext& context, FloatPoint& p1, FloatPoint& p2, float strokeThickness)
933 {
934     context.adjustLineToPixelBoundaries(p1, p2, strokeThickness, context.strokeStyle());
935
936     Path path;
937     path.moveTo(p1);
938
939     float controlPointDistance;
940     float step;
941     getWavyStrokeParameters(strokeThickness, controlPointDistance, step);
942
943     bool isVerticalLine = (p1.x() == p2.x());
944
945     if (isVerticalLine) {
946         ASSERT(p1.x() == p2.x());
947
948         float xAxis = p1.x();
949         float y1;
950         float y2;
951
952         if (p1.y() < p2.y()) {
953             y1 = p1.y();
954             y2 = p2.y();
955         } else {
956             y1 = p2.y();
957             y2 = p1.y();
958         }
959
960         adjustStepToDecorationLength(step, controlPointDistance, y2 - y1);
961         FloatPoint controlPoint1(xAxis + controlPointDistance, 0);
962         FloatPoint controlPoint2(xAxis - controlPointDistance, 0);
963
964         for (float y = y1; y + 2 * step <= y2;) {
965             controlPoint1.setY(y + step);
966             controlPoint2.setY(y + step);
967             y += 2 * step;
968             path.addBezierCurveTo(controlPoint1, controlPoint2, FloatPoint(xAxis, y));
969         }
970     } else {
971         ASSERT(p1.y() == p2.y());
972
973         float yAxis = p1.y();
974         float x1;
975         float x2;
976
977         if (p1.x() < p2.x()) {
978             x1 = p1.x();
979             x2 = p2.x();
980         } else {
981             x1 = p2.x();
982             x2 = p1.x();
983         }
984
985         adjustStepToDecorationLength(step, controlPointDistance, x2 - x1);
986         FloatPoint controlPoint1(0, yAxis + controlPointDistance);
987         FloatPoint controlPoint2(0, yAxis - controlPointDistance);
988
989         for (float x = x1; x + 2 * step <= x2;) {
990             controlPoint1.setX(x + step);
991             controlPoint2.setX(x + step);
992             x += 2 * step;
993             path.addBezierCurveTo(controlPoint1, controlPoint2, FloatPoint(x, yAxis));
994         }
995     }
996
997     context.setShouldAntialias(true);
998     context.strokePath(path);
999 }
1000
1001 // Because finding the bounding box of an underline is structurally similar to finding
1002 // the bounding box of a strikethrough, we can pull out the computation and parameterize
1003 // by the location of the decoration (yOffset, underlineOffset, and doubleOffset).
1004 static FloatRect boundingBoxForSingleDecoration(GraphicsContext& context, float textDecorationThickness,
1005     float width, FloatPoint localOrigin, TextDecorationStyle decorationStyle,
1006     bool isPrinting, float yOffset, float underlineOffset, float doubleOffset)
1007 {
1008     FloatRect boundingBox;
1009
1010     switch (decorationStyle) {
1011     case TextDecorationStyleWavy: {
1012         FloatPoint start(localOrigin.x(), localOrigin.y() + yOffset);
1013         FloatPoint end = start + FloatSize(width, 0);
1014         context.adjustLineToPixelBoundaries(start, end, textDecorationThickness, context.strokeStyle());
1015
1016         float controlPointDistance;
1017         float step;
1018         getWavyStrokeParameters(textDecorationThickness, controlPointDistance, step);
1019
1020         adjustStepToDecorationLength(step, controlPointDistance, width);
1021
1022         controlPointDistance += textDecorationThickness;
1023         FloatPoint boundingBoxOrigin = start - FloatSize(0, controlPointDistance);
1024         FloatSize boundingBoxSize = FloatSize(width, 2 * controlPointDistance);
1025         boundingBox = FloatRect(boundingBoxOrigin, boundingBoxSize);
1026         break;
1027     }
1028     default:
1029         boundingBox = context.computeLineBoundsForText(localOrigin + FloatSize(0, underlineOffset), width, isPrinting);
1030         if (decorationStyle == TextDecorationStyleDouble)
1031             boundingBox.unite(context.computeLineBoundsForText(localOrigin + FloatSize(0, doubleOffset), width, isPrinting));
1032     }
1033     return boundingBox;
1034 }
1035
1036 static FloatRect boundingBoxForAllActiveDecorations(InlineTextBox& inlineTextBox, GraphicsContext& context, TextDecoration decoration, float textDecorationThickness, float width, float doubleOffset, TextDecorationStyle decorationStyle, const FloatPoint localOrigin, const RenderStyle& lineStyle, bool isPrinting, int baseline)
1037 {
1038     FloatRect boundingBox;
1039     if (decoration & TextDecorationUnderline) {
1040         int underlineOffset = computeUnderlineOffset(lineStyle.textUnderlinePosition(), lineStyle.fontMetrics(), &inlineTextBox, textDecorationThickness);
1041         
1042         boundingBox.unite(boundingBoxForSingleDecoration(context, textDecorationThickness, width, localOrigin, decorationStyle, isPrinting, underlineOffset + doubleOffset, underlineOffset, baseline + 1));
1043     }
1044     if (decoration & TextDecorationOverline)
1045         boundingBox.unite(boundingBoxForSingleDecoration(context, textDecorationThickness, width, localOrigin, decorationStyle, isPrinting, -doubleOffset, 0, -doubleOffset));
1046     if (decoration & TextDecorationLineThrough)
1047         boundingBox.unite(boundingBoxForSingleDecoration(context, textDecorationThickness, width, localOrigin, decorationStyle, isPrinting, 2 * baseline / 3, 2 * baseline / 3, doubleOffset + 2 * baseline / 3));
1048     return boundingBox;
1049 }
1050 #endif // CSS3_TEXT_DECORATION
1051
1052 void InlineTextBox::paintDecoration(GraphicsContext& context, const FloatPoint& boxOrigin, TextDecoration decoration, TextDecorationStyle decorationStyle, const ShadowData* shadow, TextPainter& textPainter)
1053 {
1054 #if !ENABLE(CSS3_TEXT_DECORATION)
1055     UNUSED_PARAM(textPainter);
1056 #endif
1057     // FIXME: We should improve this rule and not always just assume 1.
1058     const float textDecorationThickness = 1.f;
1059
1060     if (m_truncation == cFullTruncation)
1061         return;
1062
1063     FloatPoint localOrigin = boxOrigin;
1064
1065     float width = m_logicalWidth;
1066     if (m_truncation != cNoTruncation) {
1067         width = renderer().width(m_start, m_truncation, textPos(), isFirstLine());
1068         if (!isLeftToRightDirection())
1069             localOrigin.move(m_logicalWidth - width, 0);
1070     }
1071     
1072     // Get the text decoration colors.
1073     Color underline, overline, linethrough;
1074     renderer().getTextDecorationColors(decoration, underline, overline, linethrough, true);
1075     if (isFirstLine())
1076         renderer().getTextDecorationColors(decoration, underline, overline, linethrough, true, true);
1077     
1078     // Use a special function for underlines to get the positioning exactly right.
1079     bool isPrinting = renderer().document().printing();
1080 #if !PLATFORM(IOS)
1081     context.setStrokeThickness(textDecorationThickness);
1082 #else
1083     // On iOS we want to draw crisp decorations. The function drawLineForText takes the context's
1084     // strokeThickness and renders that at device pixel scale (i.e. a strokeThickness of 1 will
1085     // produce a 1 device pixel line, so thinner on retina than non-retina). We will also scale
1086     // our thickness based on the size of the font. Since our default size is 16px we'll use that
1087     // as a scale reference.
1088     float pageScale = 1;
1089     if (Page* page = renderer().frame().page())
1090         pageScale = page->pageScaleFactor();
1091
1092     const float textDecorationBaseFontSize = 16;
1093     float fontSizeScaling = renderer().style().fontSize() / textDecorationBaseFontSize;
1094     float strokeThickness = roundf(textDecorationThickness * fontSizeScaling * pageScale);
1095     context.setStrokeThickness(strokeThickness);
1096 #endif
1097
1098     bool linesAreOpaque = !isPrinting && (!(decoration & TextDecorationUnderline) || underline.alpha() == 255) && (!(decoration & TextDecorationOverline) || overline.alpha() == 255) && (!(decoration & TextDecorationLineThrough) || linethrough.alpha() == 255);
1099
1100     const RenderStyle& lineStyle = this->lineStyle();
1101     int baseline = lineStyle.fontMetrics().ascent();
1102
1103     bool setClip = false;
1104     int extraOffset = 0;
1105     if (!linesAreOpaque && shadow && shadow->next()) {
1106         FloatRect clipRect(localOrigin, FloatSize(width, baseline + 2));
1107         for (const ShadowData* s = shadow; s; s = s->next()) {
1108             int shadowExtent = s->paintingExtent();
1109             FloatRect shadowRect(localOrigin, FloatSize(width, baseline + 2));
1110             shadowRect.inflate(shadowExtent);
1111             int shadowX = isHorizontal() ? s->x() : s->y();
1112             int shadowY = isHorizontal() ? s->y() : -s->x();
1113             shadowRect.move(shadowX, shadowY);
1114             clipRect.unite(shadowRect);
1115             extraOffset = std::max(extraOffset, std::max(0, shadowY) + shadowExtent);
1116         }
1117         context.save();
1118         context.clip(clipRect);
1119         extraOffset += baseline + 2;
1120         localOrigin.move(0, extraOffset);
1121         setClip = true;
1122     }
1123
1124     ColorSpace colorSpace = renderer().style().colorSpace();
1125     bool setShadow = false;
1126
1127     do {
1128         if (shadow) {
1129             if (!shadow->next()) {
1130                 // The last set of lines paints normally inside the clip.
1131                 localOrigin.move(0, -extraOffset);
1132                 extraOffset = 0;
1133             }
1134             int shadowX = isHorizontal() ? shadow->x() : shadow->y();
1135             int shadowY = isHorizontal() ? shadow->y() : -shadow->x();
1136             context.setShadow(FloatSize(shadowX, shadowY - extraOffset), shadow->radius(), shadow->color(), colorSpace);
1137             setShadow = true;
1138             shadow = shadow->next();
1139         }
1140
1141 #if ENABLE(CSS3_TEXT_DECORATION)
1142         // Offset between lines - always non-zero, so lines never cross each other.
1143         float doubleOffset = textDecorationThickness + 1;
1144         
1145         bool clipDecorationToMask = false;
1146         
1147         GraphicsContextStateSaver stateSaver(context, false);
1148         
1149         if (clipDecorationToMask) {
1150             const float skipInkGapWidth = 1;
1151
1152             stateSaver.save();
1153
1154             FloatRect underlineRect = boundingBoxForAllActiveDecorations(*this, context, decoration, textDecorationThickness, width, doubleOffset, decorationStyle, localOrigin, lineStyle, isPrinting, baseline);
1155             IntRect enclosingDeviceRect = enclosingIntRect(underlineRect);
1156             std::unique_ptr<ImageBuffer> imageBuffer = context.createCompatibleBuffer(enclosingDeviceRect.size());
1157
1158             if (imageBuffer.get()) {
1159                 GraphicsContext& maskContext = *imageBuffer->context();
1160                 maskContext.setFillColor(Color::black, ColorSpaceDeviceRGB);
1161                 maskContext.setLineJoin(RoundJoin);
1162                 maskContext.translate(FloatPoint() - enclosingDeviceRect.location());
1163
1164                 maskContext.fillRect(enclosingDeviceRect);
1165                 maskContext.setCompositeOperation(CompositeClear);
1166     
1167                 textPainter.paintTextInContext(maskContext, skipInkGapWidth);
1168
1169                 context.clipToImageBuffer(imageBuffer.get(), enclosingDeviceRect);
1170             }
1171         }
1172 #endif // CSS3_TEXT_DECORATION
1173         context.setStrokeStyle(textDecorationStyleToStrokeStyle(decorationStyle));
1174         if (decoration & TextDecorationUnderline) {
1175             context.setStrokeColor(underline, colorSpace);
1176 #if ENABLE(CSS3_TEXT_DECORATION)
1177             TextUnderlinePosition underlinePosition = lineStyle.textUnderlinePosition();
1178             const int underlineOffset = computeUnderlineOffset(underlinePosition, lineStyle.fontMetrics(), this, textDecorationThickness);
1179
1180             switch (decorationStyle) {
1181             case TextDecorationStyleWavy: {
1182                 FloatPoint start(localOrigin.x(), localOrigin.y() + underlineOffset + doubleOffset);
1183                 FloatPoint end(localOrigin.x() + width, localOrigin.y() + underlineOffset + doubleOffset);
1184                 strokeWavyTextDecoration(context, start, end, textDecorationThickness);
1185                 break;
1186             }
1187             default:
1188 #if ENABLE(CSS3_TEXT_DECORATION_SKIP_INK)
1189                 if (lineStyle.textDecorationSkip() == TextDecorationSkipInk) {
1190                     if (!context.paintingDisabled()) {
1191                         drawSkipInkUnderline(textPainter, context, localOrigin, underlineOffset, width, isPrinting, m_start, m_len);
1192
1193                         if (decorationStyle == TextDecorationStyleDouble)
1194                             drawSkipInkUnderline(textPainter, context, localOrigin, underlineOffset + doubleOffset, width, isPrinting, m_start, m_len);
1195                     }
1196                 } else {
1197 #endif // CSS3_TEXT_DECORATION_SKIP_INK
1198                     context.drawLineForText(FloatPoint(localOrigin.x(), localOrigin.y() + underlineOffset), width, isPrinting);
1199
1200                     if (decorationStyle == TextDecorationStyleDouble)
1201                         context.drawLineForText(FloatPoint(localOrigin.x(), localOrigin.y() + underlineOffset + doubleOffset), width, isPrinting);
1202 #if ENABLE(CSS3_TEXT_DECORATION_SKIP_INK)
1203                 }
1204 #endif
1205             }
1206 #else
1207             // Leave one pixel of white between the baseline and the underline.
1208             context.drawLineForText(FloatPoint(localOrigin.x(), localOrigin.y() + baseline + 1), width, isPrinting);
1209 #endif // CSS3_TEXT_DECORATION
1210         }
1211         if (decoration & TextDecorationOverline) {
1212             context.setStrokeColor(overline, colorSpace);
1213 #if ENABLE(CSS3_TEXT_DECORATION)
1214             switch (decorationStyle) {
1215             case TextDecorationStyleWavy: {
1216                 FloatPoint start(localOrigin.x(), localOrigin.y() - doubleOffset);
1217                 FloatPoint end(localOrigin.x() + width, localOrigin.y() - doubleOffset);
1218                 strokeWavyTextDecoration(context, start, end, textDecorationThickness);
1219                 break;
1220             }
1221             default:
1222 #if ENABLE(CSS3_TEXT_DECORATION_SKIP_INK)
1223                 if (lineStyle.textDecorationSkip() == TextDecorationSkipInk) {
1224                     if (!context.paintingDisabled()) {
1225                         drawSkipInkUnderline(textPainter, context, localOrigin, 0, width, isPrinting, m_start, m_len);
1226
1227                         if (decorationStyle == TextDecorationStyleDouble)
1228                             drawSkipInkUnderline(textPainter, context, localOrigin, -doubleOffset, width, isPrinting, m_start, m_len);
1229                     }
1230                 } else {
1231 #endif // CSS3_TEXT_DECORATION_SKIP_INK
1232 #endif // CSS3_TEXT_DECORATION
1233                     context.drawLineForText(localOrigin, width, isPrinting);
1234 #if ENABLE(CSS3_TEXT_DECORATION)
1235                     if (decorationStyle == TextDecorationStyleDouble)
1236                         context.drawLineForText(FloatPoint(localOrigin.x(), localOrigin.y() - doubleOffset), width, isPrinting);
1237 #if ENABLE(CSS3_TEXT_DECORATION_SKIP_INK)
1238                 }
1239 #endif
1240             }
1241 #endif // CSS3_TEXT_DECORATION
1242         }
1243 #if ENABLE(CSS3_TEXT_DECORATION)
1244         if (clipDecorationToMask)
1245             stateSaver.restore();
1246 #endif
1247         if (decoration & TextDecorationLineThrough) {
1248             context.setStrokeColor(linethrough, colorSpace);
1249 #if ENABLE(CSS3_TEXT_DECORATION)
1250             switch (decorationStyle) {
1251             case TextDecorationStyleWavy: {
1252                 FloatPoint start(localOrigin.x(), localOrigin.y() + 2 * baseline / 3);
1253                 FloatPoint end(localOrigin.x() + width, localOrigin.y() + 2 * baseline / 3);
1254                 strokeWavyTextDecoration(context, start, end, textDecorationThickness);
1255                 break;
1256             }
1257             default:
1258 #endif // CSS3_TEXT_DECORATION
1259                 context.drawLineForText(FloatPoint(localOrigin.x(), localOrigin.y() + 2 * baseline / 3), width, isPrinting);
1260 #if ENABLE(CSS3_TEXT_DECORATION)
1261                 if (decorationStyle == TextDecorationStyleDouble)
1262                     context.drawLineForText(FloatPoint(localOrigin.x(), localOrigin.y() + doubleOffset + 2 * baseline / 3), width, isPrinting);
1263             }
1264 #endif // CSS3_TEXT_DECORATION
1265         }
1266     } while (shadow);
1267
1268     if (setClip)
1269         context.restore();
1270     else if (setShadow)
1271         context.clearShadow();
1272 }
1273
1274 static GraphicsContext::DocumentMarkerLineStyle lineStyleForMarkerType(DocumentMarker::MarkerType markerType)
1275 {
1276     switch (markerType) {
1277     case DocumentMarker::Spelling:
1278         return GraphicsContext::DocumentMarkerSpellingLineStyle;
1279     case DocumentMarker::Grammar:
1280         return GraphicsContext::DocumentMarkerGrammarLineStyle;
1281     case DocumentMarker::CorrectionIndicator:
1282         return GraphicsContext::DocumentMarkerAutocorrectionReplacementLineStyle;
1283     case DocumentMarker::DictationAlternatives:
1284         return GraphicsContext::DocumentMarkerDictationAlternativesLineStyle;
1285 #if PLATFORM(IOS)
1286     case DocumentMarker::DictationPhraseWithAlternatives:
1287         // FIXME: Rename TextCheckingDictationPhraseWithAlternativesLineStyle and remove the PLATFORM(IOS)-guard.
1288         return GraphicsContext::TextCheckingDictationPhraseWithAlternativesLineStyle;
1289 #endif
1290     default:
1291         ASSERT_NOT_REACHED();
1292         return GraphicsContext::DocumentMarkerSpellingLineStyle;
1293     }
1294 }
1295
1296 void InlineTextBox::paintDocumentMarker(GraphicsContext* pt, const FloatPoint& boxOrigin, DocumentMarker* marker, const RenderStyle& style, const Font& font, bool grammar)
1297 {
1298     // Never print spelling/grammar markers (5327887)
1299     if (renderer().document().printing())
1300         return;
1301
1302     if (m_truncation == cFullTruncation)
1303         return;
1304
1305     float start = 0; // start of line to draw, relative to tx
1306     float width = m_logicalWidth; // how much line to draw
1307
1308     // Determine whether we need to measure text
1309     bool markerSpansWholeBox = true;
1310     if (m_start <= (int)marker->startOffset())
1311         markerSpansWholeBox = false;
1312     if ((end() + 1) != marker->endOffset()) // end points at the last char, not past it
1313         markerSpansWholeBox = false;
1314     if (m_truncation != cNoTruncation)
1315         markerSpansWholeBox = false;
1316
1317     bool isDictationMarker = marker->type() == DocumentMarker::DictationAlternatives;
1318     if (!markerSpansWholeBox || grammar || isDictationMarker) {
1319         int startPosition = std::max<int>(marker->startOffset() - m_start, 0);
1320         int endPosition = std::min<int>(marker->endOffset() - m_start, m_len);
1321         
1322         if (m_truncation != cNoTruncation)
1323             endPosition = std::min<int>(endPosition, m_truncation);
1324
1325         // Calculate start & width
1326         int deltaY = renderer().style().isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
1327         int selHeight = selectionHeight();
1328         FloatPoint startPoint(boxOrigin.x(), boxOrigin.y() - deltaY);
1329         TextRun run = constructTextRun(style, font);
1330
1331         // FIXME: Convert the document markers to float rects.
1332         IntRect markerRect = enclosingIntRect(font.selectionRectForText(run, startPoint, selHeight, startPosition, endPosition));
1333         start = markerRect.x() - startPoint.x();
1334         width = markerRect.width();
1335         
1336         // Store rendered rects for bad grammar markers, so we can hit-test against it elsewhere in order to
1337         // display a toolTip. We don't do this for misspelling markers.
1338         if (grammar || isDictationMarker) {
1339             markerRect.move(-boxOrigin.x(), -boxOrigin.y());
1340             markerRect = renderer().localToAbsoluteQuad(FloatRect(markerRect)).enclosingBoundingBox();
1341             toRenderedDocumentMarker(marker)->setRenderedRect(markerRect);
1342         }
1343     }
1344     
1345     // IMPORTANT: The misspelling underline is not considered when calculating the text bounds, so we have to
1346     // make sure to fit within those bounds.  This means the top pixel(s) of the underline will overlap the
1347     // bottom pixel(s) of the glyphs in smaller font sizes.  The alternatives are to increase the line spacing (bad!!)
1348     // or decrease the underline thickness.  The overlap is actually the most useful, and matches what AppKit does.
1349     // So, we generally place the underline at the bottom of the text, but in larger fonts that's not so good so
1350     // we pin to two pixels under the baseline.
1351     int lineThickness = cMisspellingLineThickness;
1352     int baseline = lineStyle().fontMetrics().ascent();
1353     int descent = logicalHeight() - baseline;
1354     int underlineOffset;
1355     if (descent <= (2 + lineThickness)) {
1356         // Place the underline at the very bottom of the text in small/medium fonts.
1357         underlineOffset = logicalHeight() - lineThickness;
1358     } else {
1359         // In larger fonts, though, place the underline up near the baseline to prevent a big gap.
1360         underlineOffset = baseline + 2;
1361     }
1362     pt->drawLineForDocumentMarker(FloatPoint(boxOrigin.x() + start, boxOrigin.y() + underlineOffset), width, lineStyleForMarkerType(marker->type()));
1363 }
1364
1365 void InlineTextBox::paintTextMatchMarker(GraphicsContext* pt, const FloatPoint& boxOrigin, DocumentMarker* marker, const RenderStyle& style, const Font& font)
1366 {
1367     // Use same y positioning and height as for selection, so that when the selection and this highlight are on
1368     // the same word there are no pieces sticking out.
1369     int deltaY = renderer().style().isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
1370     int selHeight = selectionHeight();
1371
1372     int sPos = std::max(marker->startOffset() - m_start, (unsigned)0);
1373     int ePos = std::min(marker->endOffset() - m_start, (unsigned)m_len);
1374     TextRun run = constructTextRun(style, font);
1375
1376     // Always compute and store the rect associated with this marker. The computed rect is in absolute coordinates.
1377     IntRect markerRect = enclosingIntRect(font.selectionRectForText(run, IntPoint(x(), selectionTop()), selHeight, sPos, ePos));
1378     markerRect = renderer().localToAbsoluteQuad(FloatRect(markerRect)).enclosingBoundingBox();
1379     toRenderedDocumentMarker(marker)->setRenderedRect(markerRect);
1380     
1381     // Optionally highlight the text
1382     if (renderer().frame().editor().markedTextMatchesAreHighlighted()) {
1383         Color color = marker->activeMatch() ? renderer().theme().platformActiveTextSearchHighlightColor() : renderer().theme().platformInactiveTextSearchHighlightColor();
1384         GraphicsContextStateSaver stateSaver(*pt);
1385         updateGraphicsContext(*pt, TextPaintStyle(color, style.colorSpace())); // Don't draw text at all!
1386         pt->clip(FloatRect(boxOrigin.x(), boxOrigin.y() - deltaY, m_logicalWidth, selHeight));
1387         pt->drawHighlightForText(font, run, FloatPoint(boxOrigin.x(), boxOrigin.y() - deltaY), selHeight, color, style.colorSpace(), sPos, ePos);
1388     }
1389 }
1390
1391 void InlineTextBox::computeRectForReplacementMarker(DocumentMarker* marker, const RenderStyle& style, const Font& font)
1392 {
1393     // Replacement markers are not actually drawn, but their rects need to be computed for hit testing.
1394     int top = selectionTop();
1395     int h = selectionHeight();
1396     
1397     int sPos = std::max(marker->startOffset() - m_start, (unsigned)0);
1398     int ePos = std::min(marker->endOffset() - m_start, (unsigned)m_len);
1399     TextRun run = constructTextRun(style, font);
1400     IntPoint startPoint = IntPoint(x(), top);
1401     
1402     // Compute and store the rect associated with this marker.
1403     IntRect markerRect = enclosingIntRect(font.selectionRectForText(run, startPoint, h, sPos, ePos));
1404     markerRect = renderer().localToAbsoluteQuad(FloatRect(markerRect)).enclosingBoundingBox();
1405     toRenderedDocumentMarker(marker)->setRenderedRect(markerRect);
1406 }
1407     
1408 void InlineTextBox::paintDocumentMarkers(GraphicsContext* pt, const FloatPoint& boxOrigin, const RenderStyle& style, const Font& font, bool background)
1409 {
1410     if (!renderer().textNode())
1411         return;
1412
1413     Vector<DocumentMarker*> markers = renderer().document().markers().markersFor(renderer().textNode());
1414     Vector<DocumentMarker*>::const_iterator markerIt = markers.begin();
1415
1416     // Give any document markers that touch this run a chance to draw before the text has been drawn.
1417     // Note end() points at the last char, not one past it like endOffset and ranges do.
1418     for ( ; markerIt != markers.end(); ++markerIt) {
1419         DocumentMarker* marker = *markerIt;
1420         
1421         // Paint either the background markers or the foreground markers, but not both
1422         switch (marker->type()) {
1423             case DocumentMarker::Grammar:
1424             case DocumentMarker::Spelling:
1425             case DocumentMarker::CorrectionIndicator:
1426             case DocumentMarker::Replacement:
1427             case DocumentMarker::DictationAlternatives:
1428 #if PLATFORM(IOS)
1429             // FIXME: Remove the PLATFORM(IOS)-guard.
1430             case DocumentMarker::DictationPhraseWithAlternatives:
1431 #endif
1432                 if (background)
1433                     continue;
1434                 break;
1435             case DocumentMarker::TextMatch:
1436                 if (!background)
1437                     continue;
1438                 break;
1439             default:
1440                 continue;
1441         }
1442
1443         if (marker->endOffset() <= start())
1444             // marker is completely before this run.  This might be a marker that sits before the
1445             // first run we draw, or markers that were within runs we skipped due to truncation.
1446             continue;
1447         
1448         if (marker->startOffset() > end())
1449             // marker is completely after this run, bail.  A later run will paint it.
1450             break;
1451         
1452         // marker intersects this run.  Paint it.
1453         switch (marker->type()) {
1454             case DocumentMarker::Spelling:
1455             case DocumentMarker::CorrectionIndicator:
1456             case DocumentMarker::DictationAlternatives:
1457                 paintDocumentMarker(pt, boxOrigin, marker, style, font, false);
1458                 break;
1459             case DocumentMarker::Grammar:
1460                 paintDocumentMarker(pt, boxOrigin, marker, style, font, true);
1461                 break;
1462 #if PLATFORM(IOS)
1463             // FIXME: See <rdar://problem/8933352>. Also, remove the PLATFORM(IOS)-guard.
1464             case DocumentMarker::DictationPhraseWithAlternatives:
1465                 paintDocumentMarker(pt, boxOrigin, marker, style, font, true);
1466                 break;
1467 #endif
1468             case DocumentMarker::TextMatch:
1469                 paintTextMatchMarker(pt, boxOrigin, marker, style, font);
1470                 break;
1471             case DocumentMarker::Replacement:
1472                 computeRectForReplacementMarker(marker, style, font);
1473                 break;
1474             default:
1475                 ASSERT_NOT_REACHED();
1476         }
1477
1478     }
1479 }
1480
1481 void InlineTextBox::paintCompositionUnderline(GraphicsContext* ctx, const FloatPoint& boxOrigin, const CompositionUnderline& underline)
1482 {
1483     if (m_truncation == cFullTruncation)
1484         return;
1485     
1486     float start = 0; // start of line to draw, relative to tx
1487     float width = m_logicalWidth; // how much line to draw
1488     bool useWholeWidth = true;
1489     unsigned paintStart = m_start;
1490     unsigned paintEnd = end() + 1; // end points at the last char, not past it
1491     if (paintStart <= underline.startOffset) {
1492         paintStart = underline.startOffset;
1493         useWholeWidth = false;
1494         start = renderer().width(m_start, paintStart - m_start, textPos(), isFirstLine());
1495     }
1496     if (paintEnd != underline.endOffset) {      // end points at the last char, not past it
1497         paintEnd = std::min(paintEnd, (unsigned)underline.endOffset);
1498         useWholeWidth = false;
1499     }
1500     if (m_truncation != cNoTruncation) {
1501         paintEnd = std::min(paintEnd, (unsigned)m_start + m_truncation);
1502         useWholeWidth = false;
1503     }
1504     if (!useWholeWidth) {
1505         width = renderer().width(paintStart, paintEnd - paintStart, textPos() + start, isFirstLine());
1506     }
1507
1508     // Thick marked text underlines are 2px thick as long as there is room for the 2px line under the baseline.
1509     // All other marked text underlines are 1px thick.
1510     // If there's not enough space the underline will touch or overlap characters.
1511     int lineThickness = 1;
1512     int baseline = lineStyle().fontMetrics().ascent();
1513     if (underline.thick && logicalHeight() - baseline >= 2)
1514         lineThickness = 2;
1515
1516     // We need to have some space between underlines of subsequent clauses, because some input methods do not use different underline styles for those.
1517     // We make each line shorter, which has a harmless side effect of shortening the first and last clauses, too.
1518     start += 1;
1519     width -= 2;
1520
1521     ctx->setStrokeColor(underline.color, renderer().style().colorSpace());
1522     ctx->setStrokeThickness(lineThickness);
1523     ctx->drawLineForText(FloatPoint(boxOrigin.x() + start, boxOrigin.y() + logicalHeight() - lineThickness), width, renderer().document().printing());
1524 }
1525
1526 int InlineTextBox::caretMinOffset() const
1527 {
1528     return m_start;
1529 }
1530
1531 int InlineTextBox::caretMaxOffset() const
1532 {
1533     return m_start + m_len;
1534 }
1535
1536 float InlineTextBox::textPos() const
1537 {
1538     // When computing the width of a text run, RenderBlock::computeInlineDirectionPositionsForLine() doesn't include the actual offset
1539     // from the containing block edge in its measurement. textPos() should be consistent so the text are rendered in the same width.
1540     if (logicalLeft() == 0)
1541         return 0;
1542     return logicalLeft() - root().logicalLeft();
1543 }
1544
1545 int InlineTextBox::offsetForPosition(float lineOffset, bool includePartialGlyphs) const
1546 {
1547     if (isLineBreak())
1548         return 0;
1549
1550     if (lineOffset - logicalLeft() > logicalWidth())
1551         return isLeftToRightDirection() ? len() : 0;
1552     if (lineOffset - logicalLeft() < 0)
1553         return isLeftToRightDirection() ? 0 : len();
1554
1555     FontCachePurgePreventer fontCachePurgePreventer;
1556
1557     const RenderStyle& lineStyle = this->lineStyle();
1558     const Font& font = fontToUse(lineStyle, renderer());
1559     return font.offsetForPosition(constructTextRun(lineStyle, font), lineOffset - logicalLeft(), includePartialGlyphs);
1560 }
1561
1562 float InlineTextBox::positionForOffset(int offset) const
1563 {
1564     ASSERT(offset >= m_start);
1565     ASSERT(offset <= m_start + m_len);
1566
1567     if (isLineBreak())
1568         return logicalLeft();
1569
1570     FontCachePurgePreventer fontCachePurgePreventer;
1571
1572     const RenderStyle& lineStyle = this->lineStyle();
1573     const Font& font = fontToUse(lineStyle, renderer());
1574     int from = !isLeftToRightDirection() ? offset - m_start : 0;
1575     int to = !isLeftToRightDirection() ? m_len : offset - m_start;
1576     // FIXME: Do we need to add rightBearing here?
1577     return font.selectionRectForText(constructTextRun(lineStyle, font), IntPoint(logicalLeft(), 0), 0, from, to).maxX();
1578 }
1579
1580 TextRun InlineTextBox::constructTextRun(const RenderStyle& style, const Font& font, BufferForAppendingHyphen* charactersWithHyphen) const
1581 {
1582     ASSERT(renderer().text());
1583
1584     String string = renderer().text();
1585     unsigned startPos = start();
1586     unsigned length = len();
1587
1588     if (string.length() != length || startPos)
1589         string = string.substringSharingImpl(startPos, length);
1590
1591     return constructTextRun(style, font, string, renderer().textLength() - startPos, charactersWithHyphen);
1592 }
1593
1594 TextRun InlineTextBox::constructTextRun(const RenderStyle& style, const Font& font, String string, int maximumLength, BufferForAppendingHyphen* charactersWithHyphen) const
1595 {
1596     int length = string.length();
1597
1598     if (charactersWithHyphen) {
1599         adjustCharactersAndLengthForHyphen(*charactersWithHyphen, style, string, length);
1600         maximumLength = length;
1601     }
1602
1603     ASSERT(maximumLength >= length);
1604
1605     TextRun run(string, textPos(), expansion(), expansionBehavior(), direction(), dirOverride() || style.rtlOrdering() == VisualOrder, !renderer().canUseSimpleFontCodePath());
1606     run.setTabSize(!style.collapseWhiteSpace(), style.tabSize());
1607     if (font.isSVGFont())
1608         run.setRenderingContext(SVGTextRunRenderingContext::create(renderer()));
1609
1610     // Propagate the maximum length of the characters buffer to the TextRun, even when we're only processing a substring.
1611     run.setCharactersLength(maximumLength);
1612     ASSERT(run.charactersLength() >= run.length());
1613     return run;
1614 }
1615
1616 #ifndef NDEBUG
1617
1618 const char* InlineTextBox::boxName() const
1619 {
1620     return "InlineTextBox";
1621 }
1622
1623 void InlineTextBox::showBox(int printedCharacters) const
1624 {
1625     String value = renderer().text();
1626     value = value.substring(start(), len());
1627     value.replaceWithLiteral('\\', "\\\\");
1628     value.replaceWithLiteral('\n', "\\n");
1629     printedCharacters += fprintf(stderr, "%s\t%p", boxName(), this);
1630     for (; printedCharacters < showTreeCharacterOffset; printedCharacters++)
1631         fputc(' ', stderr);
1632     printedCharacters = fprintf(stderr, "\t%s %p", renderer().renderName(), &renderer());
1633     const int rendererCharacterOffset = 24;
1634     for (; printedCharacters < rendererCharacterOffset; printedCharacters++)
1635         fputc(' ', stderr);
1636     fprintf(stderr, "(%d,%d) \"%s\"\n", start(), start() + len(), value.utf8().data());
1637 }
1638
1639 #endif
1640
1641 } // namespace WebCore