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