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