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