37aea45a0693e233cd2135121d2ea86b68d9ba63
[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)
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
761     }
762
763     return strokeStyle;
764 }
765
766 #if ENABLE(CSS3_TEXT)
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 #endif // CSS3_TEXT
792
793 #if ENABLE(CSS3_TEXT)
794 static void adjustStepToDecorationLength(float& step, float& controlPointDistance, float length)
795 {
796     ASSERT(step > 0);
797
798     if (length <= 0)
799         return;
800
801     unsigned stepCount = static_cast<unsigned>(length / step);
802
803     // Each Bezier curve starts at the same pixel that the previous one
804     // ended. We need to subtract (stepCount - 1) pixels when calculating the
805     // length covered to account for that.
806     float uncoveredLength = length - (stepCount * step - (stepCount - 1));
807     float adjustment = uncoveredLength / stepCount;
808     step += adjustment;
809     controlPointDistance += adjustment;
810 }
811
812 /*
813  * Draw one cubic Bezier curve and repeat the same pattern long the the decoration's axis.
814  * The start point (p1), controlPoint1, controlPoint2 and end point (p2) of the Bezier curve
815  * form a diamond shape:
816  *
817  *                              step
818  *                         |-----------|
819  *
820  *                   controlPoint1
821  *                         +
822  *
823  *
824  *                  . .
825  *                .     .
826  *              .         .
827  * (x1, y1) p1 +           .            + p2 (x2, y2) - <--- Decoration's axis
828  *                          .         .               |
829  *                            .     .                 |
830  *                              . .                   | controlPointDistance
831  *                                                    |
832  *                                                    |
833  *                         +                          -
834  *                   controlPoint2
835  *
836  *             |-----------|
837  *                 step
838  */
839 static void strokeWavyTextDecoration(GraphicsContext* context, FloatPoint& p1, FloatPoint& p2, float strokeThickness)
840 {
841     context->adjustLineToPixelBoundaries(p1, p2, strokeThickness, context->strokeStyle());
842
843     Path path;
844     path.moveTo(p1);
845
846     // Distance between decoration's axis and Bezier curve's control points.
847     // The height of the curve is based on this distance. Use a minimum of 6 pixels distance since
848     // the actual curve passes approximately at half of that distance, that is 3 pixels.
849     // The minimum height of the curve is also approximately 3 pixels. Increases the curve's height
850     // as strockThickness increases to make the curve looks better.
851     float controlPointDistance = 3 * max<float>(2, strokeThickness);
852
853     // Increment used to form the diamond shape between start point (p1), control
854     // points and end point (p2) along the axis of the decoration. Makes the
855     // curve wider as strockThickness increases to make the curve looks better.
856     float step = 2 * max<float>(2, strokeThickness);
857
858     bool isVerticalLine = (p1.x() == p2.x());
859
860     if (isVerticalLine) {
861         ASSERT(p1.x() == p2.x());
862
863         float xAxis = p1.x();
864         float y1;
865         float y2;
866
867         if (p1.y() < p2.y()) {
868             y1 = p1.y();
869             y2 = p2.y();
870         } else {
871             y1 = p2.y();
872             y2 = p1.y();
873         }
874
875         adjustStepToDecorationLength(step, controlPointDistance, y2 - y1);
876         FloatPoint controlPoint1(xAxis + controlPointDistance, 0);
877         FloatPoint controlPoint2(xAxis - controlPointDistance, 0);
878
879         for (float y = y1; y + 2 * step <= y2;) {
880             controlPoint1.setY(y + step);
881             controlPoint2.setY(y + step);
882             y += 2 * step;
883             path.addBezierCurveTo(controlPoint1, controlPoint2, FloatPoint(xAxis, y));
884         }
885     } else {
886         ASSERT(p1.y() == p2.y());
887
888         float yAxis = p1.y();
889         float x1;
890         float x2;
891
892         if (p1.x() < p2.x()) {
893             x1 = p1.x();
894             x2 = p2.x();
895         } else {
896             x1 = p2.x();
897             x2 = p1.x();
898         }
899
900         adjustStepToDecorationLength(step, controlPointDistance, x2 - x1);
901         FloatPoint controlPoint1(0, yAxis + controlPointDistance);
902         FloatPoint controlPoint2(0, yAxis - controlPointDistance);
903
904         for (float x = x1; x + 2 * step <= x2;) {
905             controlPoint1.setX(x + step);
906             controlPoint2.setX(x + step);
907             x += 2 * step;
908             path.addBezierCurveTo(controlPoint1, controlPoint2, FloatPoint(x, yAxis));
909         }
910     }
911
912     context->setShouldAntialias(true);
913     context->strokePath(path);
914 }
915 #endif // CSS3_TEXT
916
917 void InlineTextBox::paintDecoration(GraphicsContext* context, const FloatPoint& boxOrigin, TextDecoration deco, TextDecorationStyle decorationStyle, const ShadowData* shadow)
918 {
919     // FIXME: We should improve this rule and not always just assume 1.
920     const float textDecorationThickness = 1.f;
921
922     if (m_truncation == cFullTruncation)
923         return;
924
925     FloatPoint localOrigin = boxOrigin;
926
927     float width = m_logicalWidth;
928     if (m_truncation != cNoTruncation) {
929         width = renderer().width(m_start, m_truncation, textPos(), isFirstLine());
930         if (!isLeftToRightDirection())
931             localOrigin.move(m_logicalWidth - width, 0);
932     }
933     
934     // Get the text decoration colors.
935     Color underline, overline, linethrough;
936     renderer().getTextDecorationColors(deco, underline, overline, linethrough, true);
937     if (isFirstLine())
938         renderer().getTextDecorationColors(deco, underline, overline, linethrough, true, true);
939     
940     // Use a special function for underlines to get the positioning exactly right.
941     bool isPrinting = renderer().document().printing();
942     context->setStrokeThickness(textDecorationThickness);
943
944     bool linesAreOpaque = !isPrinting && (!(deco & TextDecorationUnderline) || underline.alpha() == 255) && (!(deco & TextDecorationOverline) || overline.alpha() == 255) && (!(deco & TextDecorationLineThrough) || linethrough.alpha() == 255);
945
946     const RenderStyle& lineStyle = this->lineStyle();
947     int baseline = lineStyle.fontMetrics().ascent();
948
949     bool setClip = false;
950     int extraOffset = 0;
951     if (!linesAreOpaque && shadow && shadow->next()) {
952         FloatRect clipRect(localOrigin, FloatSize(width, baseline + 2));
953         for (const ShadowData* s = shadow; s; s = s->next()) {
954             int shadowExtent = s->paintingExtent();
955             FloatRect shadowRect(localOrigin, FloatSize(width, baseline + 2));
956             shadowRect.inflate(shadowExtent);
957             int shadowX = isHorizontal() ? s->x() : s->y();
958             int shadowY = isHorizontal() ? s->y() : -s->x();
959             shadowRect.move(shadowX, shadowY);
960             clipRect.unite(shadowRect);
961             extraOffset = max(extraOffset, max(0, shadowY) + shadowExtent);
962         }
963         context->save();
964         context->clip(clipRect);
965         extraOffset += baseline + 2;
966         localOrigin.move(0, extraOffset);
967         setClip = true;
968     }
969
970     ColorSpace colorSpace = renderer().style().colorSpace();
971     bool setShadow = false;
972
973     do {
974         if (shadow) {
975             if (!shadow->next()) {
976                 // The last set of lines paints normally inside the clip.
977                 localOrigin.move(0, -extraOffset);
978                 extraOffset = 0;
979             }
980             int shadowX = isHorizontal() ? shadow->x() : shadow->y();
981             int shadowY = isHorizontal() ? shadow->y() : -shadow->x();
982             context->setShadow(FloatSize(shadowX, shadowY - extraOffset), shadow->radius(), shadow->color(), colorSpace);
983             setShadow = true;
984             shadow = shadow->next();
985         }
986
987 #if ENABLE(CSS3_TEXT)
988         // Offset between lines - always non-zero, so lines never cross each other.
989         float doubleOffset = textDecorationThickness + 1.f;
990 #endif // CSS3_TEXT
991         context->setStrokeStyle(textDecorationStyleToStrokeStyle(decorationStyle));
992         if (deco & TextDecorationUnderline) {
993             context->setStrokeColor(underline, colorSpace);
994 #if ENABLE(CSS3_TEXT)
995             TextUnderlinePosition underlinePosition = lineStyle.textUnderlinePosition();
996             const int underlineOffset = computeUnderlineOffset(underlinePosition, lineStyle.fontMetrics(), this, textDecorationThickness);
997
998             switch (decorationStyle) {
999             case TextDecorationStyleWavy: {
1000                 FloatPoint start(localOrigin.x(), localOrigin.y() + underlineOffset + doubleOffset);
1001                 FloatPoint end(localOrigin.x() + width, localOrigin.y() + underlineOffset + doubleOffset);
1002                 strokeWavyTextDecoration(context, start, end, textDecorationThickness);
1003                 break;
1004             }
1005             default:
1006                 context->drawLineForText(FloatPoint(localOrigin.x(), localOrigin.y() + underlineOffset), width, isPrinting);
1007
1008                 if (decorationStyle == TextDecorationStyleDouble)
1009                     context->drawLineForText(FloatPoint(localOrigin.x(), localOrigin.y() + underlineOffset + doubleOffset), width, isPrinting);
1010             }
1011 #else
1012             // Leave one pixel of white between the baseline and the underline.
1013             context->drawLineForText(FloatPoint(localOrigin.x(), localOrigin.y() + baseline + 1), width, isPrinting);
1014 #endif // CSS3_TEXT
1015         }
1016         if (deco & TextDecorationOverline) {
1017             context->setStrokeColor(overline, colorSpace);
1018 #if ENABLE(CSS3_TEXT)
1019             switch (decorationStyle) {
1020             case TextDecorationStyleWavy: {
1021                 FloatPoint start(localOrigin.x(), localOrigin.y() - doubleOffset);
1022                 FloatPoint end(localOrigin.x() + width, localOrigin.y() - doubleOffset);
1023                 strokeWavyTextDecoration(context, start, end, textDecorationThickness);
1024                 break;
1025             }
1026             default:
1027 #endif // CSS3_TEXT
1028                 context->drawLineForText(localOrigin, width, isPrinting);
1029 #if ENABLE(CSS3_TEXT)
1030                 if (decorationStyle == TextDecorationStyleDouble)
1031                     context->drawLineForText(FloatPoint(localOrigin.x(), localOrigin.y() - doubleOffset), width, isPrinting);
1032             }
1033 #endif // CSS3_TEXT
1034         }
1035         if (deco & TextDecorationLineThrough) {
1036             context->setStrokeColor(linethrough, colorSpace);
1037 #if ENABLE(CSS3_TEXT)
1038             switch (decorationStyle) {
1039             case TextDecorationStyleWavy: {
1040                 FloatPoint start(localOrigin.x(), localOrigin.y() + 2 * baseline / 3);
1041                 FloatPoint end(localOrigin.x() + width, localOrigin.y() + 2 * baseline / 3);
1042                 strokeWavyTextDecoration(context, start, end, textDecorationThickness);
1043                 break;
1044             }
1045             default:
1046 #endif // CSS3_TEXT
1047                 context->drawLineForText(FloatPoint(localOrigin.x(), localOrigin.y() + 2 * baseline / 3), width, isPrinting);
1048 #if ENABLE(CSS3_TEXT)
1049                 if (decorationStyle == TextDecorationStyleDouble)
1050                     context->drawLineForText(FloatPoint(localOrigin.x(), localOrigin.y() + doubleOffset + 2 * baseline / 3), width, isPrinting);
1051             }
1052 #endif // CSS3_TEXT
1053         }
1054     } while (shadow);
1055
1056     if (setClip)
1057         context->restore();
1058     else if (setShadow)
1059         context->clearShadow();
1060 }
1061
1062 static GraphicsContext::DocumentMarkerLineStyle lineStyleForMarkerType(DocumentMarker::MarkerType markerType)
1063 {
1064     switch (markerType) {
1065     case DocumentMarker::Spelling:
1066         return GraphicsContext::DocumentMarkerSpellingLineStyle;
1067     case DocumentMarker::Grammar:
1068         return GraphicsContext::DocumentMarkerGrammarLineStyle;
1069     case DocumentMarker::CorrectionIndicator:
1070         return GraphicsContext::DocumentMarkerAutocorrectionReplacementLineStyle;
1071     case DocumentMarker::DictationAlternatives:
1072         return GraphicsContext::DocumentMarkerDictationAlternativesLineStyle;
1073     default:
1074         ASSERT_NOT_REACHED();
1075         return GraphicsContext::DocumentMarkerSpellingLineStyle;
1076     }
1077 }
1078
1079 void InlineTextBox::paintDocumentMarker(GraphicsContext* pt, const FloatPoint& boxOrigin, DocumentMarker* marker, const RenderStyle& style, const Font& font, bool grammar)
1080 {
1081     // Never print spelling/grammar markers (5327887)
1082     if (renderer().document().printing())
1083         return;
1084
1085     if (m_truncation == cFullTruncation)
1086         return;
1087
1088     float start = 0; // start of line to draw, relative to tx
1089     float width = m_logicalWidth; // how much line to draw
1090
1091     // Determine whether we need to measure text
1092     bool markerSpansWholeBox = true;
1093     if (m_start <= (int)marker->startOffset())
1094         markerSpansWholeBox = false;
1095     if ((end() + 1) != marker->endOffset()) // end points at the last char, not past it
1096         markerSpansWholeBox = false;
1097     if (m_truncation != cNoTruncation)
1098         markerSpansWholeBox = false;
1099
1100     bool isDictationMarker = marker->type() == DocumentMarker::DictationAlternatives;
1101     if (!markerSpansWholeBox || grammar || isDictationMarker) {
1102         int startPosition = max<int>(marker->startOffset() - m_start, 0);
1103         int endPosition = min<int>(marker->endOffset() - m_start, m_len);
1104         
1105         if (m_truncation != cNoTruncation)
1106             endPosition = min<int>(endPosition, m_truncation);
1107
1108         // Calculate start & width
1109         int deltaY = renderer().style().isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
1110         int selHeight = selectionHeight();
1111         FloatPoint startPoint(boxOrigin.x(), boxOrigin.y() - deltaY);
1112         TextRun run = constructTextRun(style, font);
1113
1114         // FIXME: Convert the document markers to float rects.
1115         IntRect markerRect = enclosingIntRect(font.selectionRectForText(run, startPoint, selHeight, startPosition, endPosition));
1116         start = markerRect.x() - startPoint.x();
1117         width = markerRect.width();
1118         
1119         // Store rendered rects for bad grammar markers, so we can hit-test against it elsewhere in order to
1120         // display a toolTip. We don't do this for misspelling markers.
1121         if (grammar || isDictationMarker) {
1122             markerRect.move(-boxOrigin.x(), -boxOrigin.y());
1123             markerRect = renderer().localToAbsoluteQuad(FloatRect(markerRect)).enclosingBoundingBox();
1124             toRenderedDocumentMarker(marker)->setRenderedRect(markerRect);
1125         }
1126     }
1127     
1128     // IMPORTANT: The misspelling underline is not considered when calculating the text bounds, so we have to
1129     // make sure to fit within those bounds.  This means the top pixel(s) of the underline will overlap the
1130     // bottom pixel(s) of the glyphs in smaller font sizes.  The alternatives are to increase the line spacing (bad!!)
1131     // or decrease the underline thickness.  The overlap is actually the most useful, and matches what AppKit does.
1132     // So, we generally place the underline at the bottom of the text, but in larger fonts that's not so good so
1133     // we pin to two pixels under the baseline.
1134     int lineThickness = cMisspellingLineThickness;
1135     int baseline = lineStyle().fontMetrics().ascent();
1136     int descent = logicalHeight() - baseline;
1137     int underlineOffset;
1138     if (descent <= (2 + lineThickness)) {
1139         // Place the underline at the very bottom of the text in small/medium fonts.
1140         underlineOffset = logicalHeight() - lineThickness;
1141     } else {
1142         // In larger fonts, though, place the underline up near the baseline to prevent a big gap.
1143         underlineOffset = baseline + 2;
1144     }
1145     pt->drawLineForDocumentMarker(FloatPoint(boxOrigin.x() + start, boxOrigin.y() + underlineOffset), width, lineStyleForMarkerType(marker->type()));
1146 }
1147
1148 void InlineTextBox::paintTextMatchMarker(GraphicsContext* pt, const FloatPoint& boxOrigin, DocumentMarker* marker, const RenderStyle& style, const Font& font)
1149 {
1150     // Use same y positioning and height as for selection, so that when the selection and this highlight are on
1151     // the same word there are no pieces sticking out.
1152     int deltaY = renderer().style().isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
1153     int selHeight = selectionHeight();
1154
1155     int sPos = max(marker->startOffset() - m_start, (unsigned)0);
1156     int ePos = min(marker->endOffset() - m_start, (unsigned)m_len);
1157     TextRun run = constructTextRun(style, font);
1158
1159     // Always compute and store the rect associated with this marker. The computed rect is in absolute coordinates.
1160     IntRect markerRect = enclosingIntRect(font.selectionRectForText(run, IntPoint(x(), selectionTop()), selHeight, sPos, ePos));
1161     markerRect = renderer().localToAbsoluteQuad(FloatRect(markerRect)).enclosingBoundingBox();
1162     toRenderedDocumentMarker(marker)->setRenderedRect(markerRect);
1163     
1164     // Optionally highlight the text
1165     if (renderer().frame().editor().markedTextMatchesAreHighlighted()) {
1166         Color color = marker->activeMatch() ?
1167             renderer().theme()->platformActiveTextSearchHighlightColor() :
1168             renderer().theme()->platformInactiveTextSearchHighlightColor();
1169         GraphicsContextStateSaver stateSaver(*pt);
1170         updateGraphicsContext(*pt, TextPaintStyle(color, style.colorSpace())); // Don't draw text at all!
1171         pt->clip(FloatRect(boxOrigin.x(), boxOrigin.y() - deltaY, m_logicalWidth, selHeight));
1172         pt->drawHighlightForText(font, run, FloatPoint(boxOrigin.x(), boxOrigin.y() - deltaY), selHeight, color, style.colorSpace(), sPos, ePos);
1173     }
1174 }
1175
1176 void InlineTextBox::computeRectForReplacementMarker(DocumentMarker* marker, const RenderStyle& style, const Font& font)
1177 {
1178     // Replacement markers are not actually drawn, but their rects need to be computed for hit testing.
1179     int top = selectionTop();
1180     int h = selectionHeight();
1181     
1182     int sPos = max(marker->startOffset() - m_start, (unsigned)0);
1183     int ePos = min(marker->endOffset() - m_start, (unsigned)m_len);
1184     TextRun run = constructTextRun(style, font);
1185     IntPoint startPoint = IntPoint(x(), top);
1186     
1187     // Compute and store the rect associated with this marker.
1188     IntRect markerRect = enclosingIntRect(font.selectionRectForText(run, startPoint, h, sPos, ePos));
1189     markerRect = renderer().localToAbsoluteQuad(FloatRect(markerRect)).enclosingBoundingBox();
1190     toRenderedDocumentMarker(marker)->setRenderedRect(markerRect);
1191 }
1192     
1193 void InlineTextBox::paintDocumentMarkers(GraphicsContext* pt, const FloatPoint& boxOrigin, const RenderStyle& style, const Font& font, bool background)
1194 {
1195     if (!renderer().textNode())
1196         return;
1197
1198     Vector<DocumentMarker*> markers = renderer().document().markers().markersFor(renderer().textNode());
1199     Vector<DocumentMarker*>::const_iterator markerIt = markers.begin();
1200
1201     // Give any document markers that touch this run a chance to draw before the text has been drawn.
1202     // Note end() points at the last char, not one past it like endOffset and ranges do.
1203     for ( ; markerIt != markers.end(); ++markerIt) {
1204         DocumentMarker* marker = *markerIt;
1205         
1206         // Paint either the background markers or the foreground markers, but not both
1207         switch (marker->type()) {
1208             case DocumentMarker::Grammar:
1209             case DocumentMarker::Spelling:
1210             case DocumentMarker::CorrectionIndicator:
1211             case DocumentMarker::Replacement:
1212             case DocumentMarker::DictationAlternatives:
1213                 if (background)
1214                     continue;
1215                 break;
1216             case DocumentMarker::TextMatch:
1217                 if (!background)
1218                     continue;
1219                 break;
1220             default:
1221                 continue;
1222         }
1223
1224         if (marker->endOffset() <= start())
1225             // marker is completely before this run.  This might be a marker that sits before the
1226             // first run we draw, or markers that were within runs we skipped due to truncation.
1227             continue;
1228         
1229         if (marker->startOffset() > end())
1230             // marker is completely after this run, bail.  A later run will paint it.
1231             break;
1232         
1233         // marker intersects this run.  Paint it.
1234         switch (marker->type()) {
1235             case DocumentMarker::Spelling:
1236             case DocumentMarker::CorrectionIndicator:
1237             case DocumentMarker::DictationAlternatives:
1238                 paintDocumentMarker(pt, boxOrigin, marker, style, font, false);
1239                 break;
1240             case DocumentMarker::Grammar:
1241                 paintDocumentMarker(pt, boxOrigin, marker, style, font, true);
1242                 break;
1243             case DocumentMarker::TextMatch:
1244                 paintTextMatchMarker(pt, boxOrigin, marker, style, font);
1245                 break;
1246             case DocumentMarker::Replacement:
1247                 computeRectForReplacementMarker(marker, style, font);
1248                 break;
1249             default:
1250                 ASSERT_NOT_REACHED();
1251         }
1252
1253     }
1254 }
1255
1256 void InlineTextBox::paintCompositionUnderline(GraphicsContext* ctx, const FloatPoint& boxOrigin, const CompositionUnderline& underline)
1257 {
1258     if (m_truncation == cFullTruncation)
1259         return;
1260     
1261     float start = 0; // start of line to draw, relative to tx
1262     float width = m_logicalWidth; // how much line to draw
1263     bool useWholeWidth = true;
1264     unsigned paintStart = m_start;
1265     unsigned paintEnd = end() + 1; // end points at the last char, not past it
1266     if (paintStart <= underline.startOffset) {
1267         paintStart = underline.startOffset;
1268         useWholeWidth = false;
1269         start = renderer().width(m_start, paintStart - m_start, textPos(), isFirstLine());
1270     }
1271     if (paintEnd != underline.endOffset) {      // end points at the last char, not past it
1272         paintEnd = min(paintEnd, (unsigned)underline.endOffset);
1273         useWholeWidth = false;
1274     }
1275     if (m_truncation != cNoTruncation) {
1276         paintEnd = min(paintEnd, (unsigned)m_start + m_truncation);
1277         useWholeWidth = false;
1278     }
1279     if (!useWholeWidth) {
1280         width = renderer().width(paintStart, paintEnd - paintStart, textPos() + start, isFirstLine());
1281     }
1282
1283     // Thick marked text underlines are 2px thick as long as there is room for the 2px line under the baseline.
1284     // All other marked text underlines are 1px thick.
1285     // If there's not enough space the underline will touch or overlap characters.
1286     int lineThickness = 1;
1287     int baseline = lineStyle().fontMetrics().ascent();
1288     if (underline.thick && logicalHeight() - baseline >= 2)
1289         lineThickness = 2;
1290
1291     // We need to have some space between underlines of subsequent clauses, because some input methods do not use different underline styles for those.
1292     // We make each line shorter, which has a harmless side effect of shortening the first and last clauses, too.
1293     start += 1;
1294     width -= 2;
1295
1296     ctx->setStrokeColor(underline.color, renderer().style().colorSpace());
1297     ctx->setStrokeThickness(lineThickness);
1298     ctx->drawLineForText(FloatPoint(boxOrigin.x() + start, boxOrigin.y() + logicalHeight() - lineThickness), width, renderer().document().printing());
1299 }
1300
1301 int InlineTextBox::caretMinOffset() const
1302 {
1303     return m_start;
1304 }
1305
1306 int InlineTextBox::caretMaxOffset() const
1307 {
1308     return m_start + m_len;
1309 }
1310
1311 float InlineTextBox::textPos() const
1312 {
1313     // When computing the width of a text run, RenderBlock::computeInlineDirectionPositionsForLine() doesn't include the actual offset
1314     // from the containing block edge in its measurement. textPos() should be consistent so the text are rendered in the same width.
1315     if (logicalLeft() == 0)
1316         return 0;
1317     return logicalLeft() - root().logicalLeft();
1318 }
1319
1320 int InlineTextBox::offsetForPosition(float lineOffset, bool includePartialGlyphs) const
1321 {
1322     if (isLineBreak())
1323         return 0;
1324
1325     if (lineOffset - logicalLeft() > logicalWidth())
1326         return isLeftToRightDirection() ? len() : 0;
1327     if (lineOffset - logicalLeft() < 0)
1328         return isLeftToRightDirection() ? 0 : len();
1329
1330     FontCachePurgePreventer fontCachePurgePreventer;
1331
1332     const RenderStyle& lineStyle = this->lineStyle();
1333     const Font& font = fontToUse(lineStyle, renderer());
1334     return font.offsetForPosition(constructTextRun(lineStyle, font), lineOffset - logicalLeft(), includePartialGlyphs);
1335 }
1336
1337 float InlineTextBox::positionForOffset(int offset) const
1338 {
1339     ASSERT(offset >= m_start);
1340     ASSERT(offset <= m_start + m_len);
1341
1342     if (isLineBreak())
1343         return logicalLeft();
1344
1345     FontCachePurgePreventer fontCachePurgePreventer;
1346
1347     const RenderStyle& lineStyle = this->lineStyle();
1348     const Font& font = fontToUse(lineStyle, renderer());
1349     int from = !isLeftToRightDirection() ? offset - m_start : 0;
1350     int to = !isLeftToRightDirection() ? m_len : offset - m_start;
1351     // FIXME: Do we need to add rightBearing here?
1352     return font.selectionRectForText(constructTextRun(lineStyle, font), IntPoint(logicalLeft(), 0), 0, from, to).maxX();
1353 }
1354
1355 TextRun InlineTextBox::constructTextRun(const RenderStyle& style, const Font& font, BufferForAppendingHyphen* charactersWithHyphen) const
1356 {
1357     ASSERT(renderer().text());
1358
1359     String string = renderer().text();
1360     unsigned startPos = start();
1361     unsigned length = len();
1362
1363     if (string.length() != length || startPos)
1364         string = string.substringSharingImpl(startPos, length);
1365
1366     return constructTextRun(style, font, string, renderer().textLength() - startPos, charactersWithHyphen);
1367 }
1368
1369 TextRun InlineTextBox::constructTextRun(const RenderStyle& style, const Font& font, String string, int maximumLength, BufferForAppendingHyphen* charactersWithHyphen) const
1370 {
1371     int length = string.length();
1372
1373     if (charactersWithHyphen) {
1374         adjustCharactersAndLengthForHyphen(*charactersWithHyphen, style, string, length);
1375         maximumLength = length;
1376     }
1377
1378     ASSERT(maximumLength >= length);
1379
1380     TextRun run(string, textPos(), expansion(), expansionBehavior(), direction(), dirOverride() || style.rtlOrdering() == VisualOrder, !renderer().canUseSimpleFontCodePath());
1381     run.setTabSize(!style.collapseWhiteSpace(), style.tabSize());
1382     if (textRunNeedsRenderingContext(font))
1383         run.setRenderingContext(SVGTextRunRenderingContext::create(renderer()));
1384
1385     // Propagate the maximum length of the characters buffer to the TextRun, even when we're only processing a substring.
1386     run.setCharactersLength(maximumLength);
1387     ASSERT(run.charactersLength() >= run.length());
1388     return run;
1389 }
1390
1391 #ifndef NDEBUG
1392
1393 const char* InlineTextBox::boxName() const
1394 {
1395     return "InlineTextBox";
1396 }
1397
1398 void InlineTextBox::showBox(int printedCharacters) const
1399 {
1400     String value = renderer().text();
1401     value = value.substring(start(), len());
1402     value.replaceWithLiteral('\\', "\\\\");
1403     value.replaceWithLiteral('\n', "\\n");
1404     printedCharacters += fprintf(stderr, "%s\t%p", boxName(), this);
1405     for (; printedCharacters < showTreeCharacterOffset; printedCharacters++)
1406         fputc(' ', stderr);
1407     printedCharacters = fprintf(stderr, "\t%s %p", renderer().renderName(), &renderer());
1408     const int rendererCharacterOffset = 24;
1409     for (; printedCharacters < rendererCharacterOffset; printedCharacters++)
1410         fputc(' ', stderr);
1411     fprintf(stderr, "(%d,%d) \"%s\"\n", start(), start() + len(), value.utf8().data());
1412 }
1413
1414 #endif
1415
1416 } // namespace WebCore