18e96e8ea8b1cc4bc41809c0fcfad49e5e8327ab
[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 "break_lines.h"
52 #include <wtf/text/CString.h>
53
54 using namespace std;
55
56 namespace WebCore {
57
58 struct SameSizeAsInlineTextBox : public InlineBox {
59     unsigned variables[1];
60     unsigned short variables2[2];
61     void* pointers[2];
62 };
63
64 COMPILE_ASSERT(sizeof(InlineTextBox) == sizeof(SameSizeAsInlineTextBox), InlineTextBox_should_stay_small);
65
66 typedef WTF::HashMap<const InlineTextBox*, LayoutRect> InlineTextBoxOverflowMap;
67 static InlineTextBoxOverflowMap* gTextBoxesWithOverflow;
68
69 void InlineTextBox::destroy(RenderArena& arena)
70 {
71     if (!knownToHaveNoOverflow() && gTextBoxesWithOverflow)
72         gTextBoxesWithOverflow->remove(this);
73     InlineBox::destroy(arena);
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 (!behavesLikeText() || !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 (!behavesLikeText() || !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(RenderArena& arena)
245 {
246     renderer().removeTextBox(*this);
247     destroy(arena);
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 static void paintTextWithShadows(GraphicsContext* context, const Font& font, const TextRun& textRun, const AtomicString& emphasisMark, int emphasisMarkOffset, int startOffset, int endOffset, int truncationPoint, const FloatPoint& textOrigin,
401                                  const FloatRect& boxRect, const ShadowData* shadow, bool stroked, bool horizontal)
402 {
403     Color fillColor = context->fillColor();
404     ColorSpace fillColorSpace = context->fillColorSpace();
405     bool opaque = fillColor.alpha() == 255;
406     if (!opaque)
407         context->setFillColor(Color::black, fillColorSpace);
408
409     do {
410         IntSize extraOffset;
411         if (shadow)
412             extraOffset = roundedIntSize(InlineTextBox::applyShadowToGraphicsContext(context, shadow, boxRect, stroked, opaque, horizontal));
413         else if (!opaque)
414             context->setFillColor(fillColor, fillColorSpace);
415
416         if (startOffset <= endOffset) {
417             if (emphasisMark.isEmpty())
418                 context->drawText(font, textRun, textOrigin + extraOffset, startOffset, endOffset);
419             else
420                 context->drawEmphasisMarks(font, textRun, emphasisMark, textOrigin + extraOffset + IntSize(0, emphasisMarkOffset), startOffset, endOffset);
421         } else {
422             if (endOffset > 0) {
423                 if (emphasisMark.isEmpty())
424                     context->drawText(font, textRun, textOrigin + extraOffset,  0, endOffset);
425                 else
426                     context->drawEmphasisMarks(font, textRun, emphasisMark, textOrigin + extraOffset + IntSize(0, emphasisMarkOffset),  0, endOffset);
427             }
428             if (startOffset < truncationPoint) {
429                 if (emphasisMark.isEmpty())
430                     context->drawText(font, textRun, textOrigin + extraOffset, startOffset, truncationPoint);
431                 else
432                     context->drawEmphasisMarks(font, textRun, emphasisMark, textOrigin + extraOffset + IntSize(0, emphasisMarkOffset),  startOffset, truncationPoint);
433             }
434         }
435
436         if (!shadow)
437             break;
438
439         if (shadow->next() || stroked || !opaque)
440             context->restore();
441         else
442             context->clearShadow();
443
444         shadow = shadow->next();
445     } while (shadow || stroked || !opaque);
446 }
447
448 bool InlineTextBox::getEmphasisMarkPosition(const RenderStyle& style, TextEmphasisPosition& emphasisPosition) const
449 {
450     // This function returns true if there are text emphasis marks and they are suppressed by ruby text.
451     if (style.textEmphasisMark() == TextEmphasisMarkNone)
452         return false;
453
454     emphasisPosition = style.textEmphasisPosition();
455     if (emphasisPosition == TextEmphasisPositionUnder)
456         return true; // Ruby text is always over, so it cannot suppress emphasis marks under.
457
458     RenderBlock* containingBlock = renderer().containingBlock();
459     if (!containingBlock->isRubyBase())
460         return true; // This text is not inside a ruby base, so it does not have ruby text over it.
461
462     if (!containingBlock->parent()->isRubyRun())
463         return true; // Cannot get the ruby text.
464
465     RenderRubyText* rubyText = toRenderRubyRun(containingBlock->parent())->rubyText();
466
467     // The emphasis marks over are suppressed only if there is a ruby text box and it not empty.
468     return !rubyText || !rubyText->firstLineBox();
469 }
470
471 enum RotationDirection { Counterclockwise, Clockwise };
472
473 static inline AffineTransform rotation(const FloatRect& boxRect, RotationDirection clockwise)
474 {
475     return clockwise ? AffineTransform(0, 1, -1, 0, boxRect.x() + boxRect.maxY(), boxRect.maxY() - boxRect.x())
476         : AffineTransform(0, -1, 1, 0, boxRect.x() - boxRect.maxY(), boxRect.x() + boxRect.maxY());
477 }
478
479 void InlineTextBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit /*lineTop*/, LayoutUnit /*lineBottom*/)
480 {
481     if (isLineBreak() || !paintInfo.shouldPaintWithinRoot(renderer()) || renderer().style()->visibility() != VISIBLE
482         || m_truncation == cFullTruncation || paintInfo.phase == PaintPhaseOutline || !m_len)
483         return;
484
485     ASSERT(paintInfo.phase != PaintPhaseSelfOutline && paintInfo.phase != PaintPhaseChildOutlines);
486
487     LayoutUnit logicalLeftSide = logicalLeftVisualOverflow();
488     LayoutUnit logicalRightSide = logicalRightVisualOverflow();
489     LayoutUnit logicalStart = logicalLeftSide + (isHorizontal() ? paintOffset.x() : paintOffset.y());
490     LayoutUnit logicalExtent = logicalRightSide - logicalLeftSide;
491     
492     LayoutUnit paintEnd = isHorizontal() ? paintInfo.rect.maxX() : paintInfo.rect.maxY();
493     LayoutUnit paintStart = isHorizontal() ? paintInfo.rect.x() : paintInfo.rect.y();
494     
495     LayoutPoint adjustedPaintOffset = roundedIntPoint(paintOffset);
496     
497     if (logicalStart >= paintEnd || logicalStart + logicalExtent <= paintStart)
498         return;
499
500     bool isPrinting = renderer().document().printing();
501     
502     // Determine whether or not we're selected.
503     bool haveSelection = !isPrinting && paintInfo.phase != PaintPhaseTextClip && selectionState() != RenderObject::SelectionNone;
504     if (!haveSelection && paintInfo.phase == PaintPhaseSelection)
505         // When only painting the selection, don't bother to paint if there is none.
506         return;
507
508     if (m_truncation != cNoTruncation) {
509         if (renderer().containingBlock()->style()->isLeftToRightDirection() != isLeftToRightDirection()) {
510             // Make the visible fragment of text hug the edge closest to the rest of the run by moving the origin
511             // at which we start drawing text.
512             // e.g. In the case of LTR text truncated in an RTL Context, the correct behavior is:
513             // |Hello|CBA| -> |...He|CBA|
514             // In order to draw the fragment "He" aligned to the right edge of it's box, we need to start drawing
515             // farther to the right.
516             // NOTE: WebKit's behavior differs from that of IE which appears to just overlay the ellipsis on top of the
517             // truncated string i.e.  |Hello|CBA| -> |...lo|CBA|
518             LayoutUnit widthOfVisibleText = renderer().width(m_start, m_truncation, textPos(), isFirstLine());
519             LayoutUnit widthOfHiddenText = m_logicalWidth - widthOfVisibleText;
520             LayoutSize truncationOffset(isLeftToRightDirection() ? widthOfHiddenText : -widthOfHiddenText, 0);
521             adjustedPaintOffset.move(isHorizontal() ? truncationOffset : truncationOffset.transposedSize());
522         }
523     }
524
525     GraphicsContext* context = paintInfo.context;
526
527     const RenderStyle& lineStyle = this->lineStyle();
528     
529     adjustedPaintOffset.move(0, lineStyle.isHorizontalWritingMode() ? 0 : -logicalHeight());
530
531     FloatPoint boxOrigin = locationIncludingFlipping();
532     boxOrigin.move(adjustedPaintOffset.x(), adjustedPaintOffset.y());
533     FloatRect boxRect(boxOrigin, LayoutSize(logicalWidth(), logicalHeight()));
534
535     RenderCombineText* combinedText = lineStyle.hasTextCombine() && renderer().isCombineText() && toRenderCombineText(renderer()).isCombined() ? &toRenderCombineText(renderer()) : 0;
536
537     bool shouldRotate = !isHorizontal() && !combinedText;
538     if (shouldRotate)
539         context->concatCTM(rotation(boxRect, Clockwise));
540
541     // Determine whether or not we have composition underlines to draw.
542     bool containsComposition = renderer().textNode() && renderer().frame().editor().compositionNode() == renderer().textNode();
543     bool useCustomUnderlines = containsComposition && renderer().frame().editor().compositionUsesCustomUnderlines();
544
545     // Determine the text colors and selection colors.
546     TextPaintStyle textPaintStyle = computeTextPaintStyle(renderer(), lineStyle, paintInfo);
547
548     bool paintSelectedTextOnly;
549     bool paintSelectedTextSeparately;
550     const ShadowData* selectionShadow;
551     TextPaintStyle selectionPaintStyle = computeTextSelectionPaintStyle(textPaintStyle, renderer(), lineStyle, paintInfo, paintSelectedTextOnly, paintSelectedTextSeparately, selectionShadow);
552
553     // Set our font.
554     const Font& font = fontToUse(lineStyle, renderer());
555
556     FloatPoint textOrigin = FloatPoint(boxOrigin.x(), boxOrigin.y() + font.fontMetrics().ascent());
557
558     if (combinedText)
559         combinedText->adjustTextOrigin(textOrigin, boxRect);
560
561     // 1. Paint backgrounds behind text if needed. Examples of such backgrounds include selection
562     // and composition underlines.
563     if (paintInfo.phase != PaintPhaseSelection && paintInfo.phase != PaintPhaseTextClip && !isPrinting) {
564 #if PLATFORM(MAC)
565         // Custom highlighters go behind everything else.
566         if (lineStyle.highlight() != nullAtom && !context->paintingDisabled())
567             paintCustomHighlight(adjustedPaintOffset, lineStyle.highlight());
568 #endif
569
570         if (containsComposition && !useCustomUnderlines)
571             paintCompositionBackground(context, boxOrigin, lineStyle, font,
572                 renderer().frame().editor().compositionStart(),
573                 renderer().frame().editor().compositionEnd());
574
575         paintDocumentMarkers(context, boxOrigin, lineStyle, font, true);
576
577         if (haveSelection && !useCustomUnderlines)
578             paintSelection(context, boxOrigin, lineStyle, font, selectionPaintStyle.fillColor);
579     }
580
581     if (Page* page = renderer().frame().page()) {
582         // FIXME: Right now, InlineTextBoxes never call addRelevantUnpaintedObject() even though they might
583         // legitimately be unpainted if they are waiting on a slow-loading web font. We should fix that, and
584         // when we do, we will have to account for the fact the InlineTextBoxes do not always have unique
585         // renderers and Page currently relies on each unpainted object having a unique renderer.
586         if (paintInfo.phase == PaintPhaseForeground)
587             page->addRelevantRepaintedObject(&renderer(), IntRect(boxOrigin.x(), boxOrigin.y(), logicalWidth(), logicalHeight()));
588     }
589
590     // 2. Now paint the foreground, including text and decorations like underline/overline (in quirks mode only).
591     int length = m_len;
592     int maximumLength;
593     String string;
594     if (!combinedText) {
595         string = renderer().text();
596         if (static_cast<unsigned>(length) != string.length() || m_start) {
597             ASSERT_WITH_SECURITY_IMPLICATION(static_cast<unsigned>(m_start + length) <= string.length());
598             string = string.substringSharingImpl(m_start, length);
599         }
600         maximumLength = renderer().textLength() - m_start;
601     } else {
602         combinedText->getStringToRender(m_start, string, length);
603         maximumLength = length;
604     }
605
606     BufferForAppendingHyphen charactersWithHyphen;
607     TextRun textRun = constructTextRun(lineStyle, font, string, maximumLength, hasHyphen() ? &charactersWithHyphen : 0);
608     if (hasHyphen())
609         length = textRun.length();
610
611     int sPos = 0;
612     int ePos = 0;
613     if (paintSelectedTextOnly || paintSelectedTextSeparately)
614         selectionStartEnd(sPos, ePos);
615
616     if (m_truncation != cNoTruncation) {
617         sPos = min<int>(sPos, m_truncation);
618         ePos = min<int>(ePos, m_truncation);
619         length = m_truncation;
620     }
621
622     int emphasisMarkOffset = 0;
623     TextEmphasisPosition emphasisMarkPosition;
624     bool hasTextEmphasis = getEmphasisMarkPosition(lineStyle, emphasisMarkPosition);
625     const AtomicString& emphasisMark = hasTextEmphasis ? lineStyle.textEmphasisMarkString() : nullAtom;
626     if (!emphasisMark.isEmpty())
627         emphasisMarkOffset = emphasisMarkPosition == TextEmphasisPositionOver ? -font.fontMetrics().ascent() - font.emphasisMarkDescent(emphasisMark) : font.fontMetrics().descent() + font.emphasisMarkAscent(emphasisMark);
628
629     const ShadowData* textShadow = paintInfo.forceBlackText() ? 0 : lineStyle.textShadow();
630
631     if (!paintSelectedTextOnly) {
632         // For stroked painting, we have to change the text drawing mode.  It's probably dangerous to leave that mutated as a side
633         // effect, so only when we know we're stroking, do a save/restore.
634         GraphicsContextStateSaver stateSaver(*context, textPaintStyle.strokeWidth > 0);
635
636         updateGraphicsContext(*context, textPaintStyle);
637         if (!paintSelectedTextSeparately || ePos <= sPos) {
638             // FIXME: Truncate right-to-left text correctly.
639             paintTextWithShadows(context, font, textRun, nullAtom, 0, 0, length, length, textOrigin, boxRect, textShadow, textPaintStyle.strokeWidth > 0, isHorizontal());
640         } else
641             paintTextWithShadows(context, font, textRun, nullAtom, 0, ePos, sPos, length, textOrigin, boxRect, textShadow, textPaintStyle.strokeWidth > 0, isHorizontal());
642
643         if (!emphasisMark.isEmpty()) {
644             updateGraphicsContext(*context, textPaintStyle, UseEmphasisMarkColor);
645
646             DEFINE_STATIC_LOCAL(TextRun, objectReplacementCharacterTextRun, (&objectReplacementCharacter, 1));
647             TextRun& emphasisMarkTextRun = combinedText ? objectReplacementCharacterTextRun : textRun;
648             FloatPoint emphasisMarkTextOrigin = combinedText ? FloatPoint(boxOrigin.x() + boxRect.width() / 2, boxOrigin.y() + font.fontMetrics().ascent()) : textOrigin;
649             if (combinedText)
650                 context->concatCTM(rotation(boxRect, Clockwise));
651
652             if (!paintSelectedTextSeparately || ePos <= sPos) {
653                 // FIXME: Truncate right-to-left text correctly.
654                 paintTextWithShadows(context, combinedText ? combinedText->originalFont() : font, emphasisMarkTextRun, emphasisMark, emphasisMarkOffset, 0, length, length, emphasisMarkTextOrigin, boxRect, textShadow, textPaintStyle.strokeWidth > 0, isHorizontal());
655             } else
656                 paintTextWithShadows(context, combinedText ? combinedText->originalFont() : font, emphasisMarkTextRun, emphasisMark, emphasisMarkOffset, ePos, sPos, length, emphasisMarkTextOrigin, boxRect, textShadow, textPaintStyle.strokeWidth > 0, isHorizontal());
657
658             if (combinedText)
659                 context->concatCTM(rotation(boxRect, Counterclockwise));
660         }
661     }
662
663     if ((paintSelectedTextOnly || paintSelectedTextSeparately) && sPos < ePos) {
664         // paint only the text that is selected
665         GraphicsContextStateSaver stateSaver(*context, selectionPaintStyle.strokeWidth > 0);
666
667         updateGraphicsContext(*context, selectionPaintStyle);
668         paintTextWithShadows(context, font, textRun, nullAtom, 0, sPos, ePos, length, textOrigin, boxRect, selectionShadow, selectionPaintStyle.strokeWidth > 0, isHorizontal());
669         if (!emphasisMark.isEmpty()) {
670             updateGraphicsContext(*context, selectionPaintStyle, UseEmphasisMarkColor);
671
672             DEFINE_STATIC_LOCAL(TextRun, objectReplacementCharacterTextRun, (&objectReplacementCharacter, 1));
673             TextRun& emphasisMarkTextRun = combinedText ? objectReplacementCharacterTextRun : textRun;
674             FloatPoint emphasisMarkTextOrigin = combinedText ? FloatPoint(boxOrigin.x() + boxRect.width() / 2, boxOrigin.y() + font.fontMetrics().ascent()) : textOrigin;
675             if (combinedText)
676                 context->concatCTM(rotation(boxRect, Clockwise));
677
678             paintTextWithShadows(context, combinedText ? combinedText->originalFont() : font, emphasisMarkTextRun, emphasisMark, emphasisMarkOffset, sPos, ePos, length, emphasisMarkTextOrigin, boxRect, selectionShadow, selectionPaintStyle.strokeWidth > 0, isHorizontal());
679
680             if (combinedText)
681                 context->concatCTM(rotation(boxRect, Counterclockwise));
682         }
683     }
684
685     // Paint decorations
686     TextDecoration textDecorations = lineStyle.textDecorationsInEffect();
687     if (textDecorations != TextDecorationNone && paintInfo.phase != PaintPhaseSelection) {
688         updateGraphicsContext(*context, textPaintStyle);
689         if (combinedText)
690             context->concatCTM(rotation(boxRect, Clockwise));
691         paintDecoration(context, boxOrigin, textDecorations, lineStyle.textDecorationStyle(), textShadow);
692         if (combinedText)
693             context->concatCTM(rotation(boxRect, Counterclockwise));
694     }
695
696     if (paintInfo.phase == PaintPhaseForeground) {
697         paintDocumentMarkers(context, boxOrigin, lineStyle, font, false);
698
699         if (useCustomUnderlines) {
700             const Vector<CompositionUnderline>& underlines = renderer().frame().editor().customCompositionUnderlines();
701             size_t numUnderlines = underlines.size();
702
703             for (size_t index = 0; index < numUnderlines; ++index) {
704                 const CompositionUnderline& underline = underlines[index];
705
706                 if (underline.endOffset <= start())
707                     // underline is completely before this run.  This might be an underline that sits
708                     // before the first run we draw, or underlines that were within runs we skipped 
709                     // due to truncation.
710                     continue;
711                 
712                 if (underline.startOffset <= end()) {
713                     // underline intersects this run.  Paint it.
714                     paintCompositionUnderline(context, boxOrigin, underline);
715                     if (underline.endOffset > end() + 1)
716                         // underline also runs into the next run. Bail now, no more marker advancement.
717                         break;
718                 } else
719                     // underline is completely after this run, bail.  A later run will paint it.
720                     break;
721             }
722         }
723     }
724     
725     if (shouldRotate)
726         context->concatCTM(rotation(boxRect, Counterclockwise));
727 }
728
729 void InlineTextBox::selectionStartEnd(int& sPos, int& ePos)
730 {
731     int startPos, endPos;
732     if (renderer().selectionState() == RenderObject::SelectionInside) {
733         startPos = 0;
734         endPos = renderer().textLength();
735     } else {
736         renderer().selectionStartEnd(startPos, endPos);
737         if (renderer().selectionState() == RenderObject::SelectionStart)
738             endPos = renderer().textLength();
739         else if (renderer().selectionState() == RenderObject::SelectionEnd)
740             startPos = 0;
741     }
742
743     sPos = max(startPos - m_start, 0);
744     ePos = min(endPos - m_start, (int)m_len);
745 }
746
747 void alignSelectionRectToDevicePixels(FloatRect& rect)
748 {
749     float maxX = floorf(rect.maxX());
750     rect.setX(floorf(rect.x()));
751     rect.setWidth(roundf(maxX - rect.x()));
752 }
753
754 void InlineTextBox::paintSelection(GraphicsContext* context, const FloatPoint& boxOrigin, const RenderStyle& style, const Font& font, Color textColor)
755 {
756     if (context->paintingDisabled())
757         return;
758
759     // See if we have a selection to paint at all.
760     int sPos, ePos;
761     selectionStartEnd(sPos, ePos);
762     if (sPos >= ePos)
763         return;
764
765     Color c = renderer().selectionBackgroundColor();
766     if (!c.isValid() || c.alpha() == 0)
767         return;
768
769     // If the text color ends up being the same as the selection background, invert the selection
770     // background.
771     if (textColor == c)
772         c = Color(0xff - c.red(), 0xff - c.green(), 0xff - c.blue());
773
774     GraphicsContextStateSaver stateSaver(*context);
775     updateGraphicsContext(*context, TextPaintStyle(c, style.colorSpace())); // Don't draw text at all!
776     
777     // If the text is truncated, let the thing being painted in the truncation
778     // draw its own highlight.
779     int length = m_truncation != cNoTruncation ? m_truncation : m_len;
780     String string = renderer().text();
781
782     if (string.length() != static_cast<unsigned>(length) || m_start) {
783         ASSERT_WITH_SECURITY_IMPLICATION(static_cast<unsigned>(m_start + length) <= string.length());
784         string = string.substringSharingImpl(m_start, length);
785     }
786
787     BufferForAppendingHyphen charactersWithHyphen;
788     bool respectHyphen = ePos == length && hasHyphen();
789     TextRun textRun = constructTextRun(style, font, string, renderer().textLength() - m_start, respectHyphen ? &charactersWithHyphen : 0);
790     if (respectHyphen)
791         ePos = textRun.length();
792
793     const RootInlineBox& rootBox = root();
794     LayoutUnit selectionBottom = rootBox.selectionBottom();
795     LayoutUnit selectionTop = rootBox.selectionTopAdjustedForPrecedingBlock();
796
797     int deltaY = roundToInt(renderer().style()->isFlippedLinesWritingMode() ? selectionBottom - logicalBottom() : logicalTop() - selectionTop);
798     int selHeight = max(0, roundToInt(selectionBottom - selectionTop));
799
800     FloatPoint localOrigin(boxOrigin.x(), boxOrigin.y() - deltaY);
801     FloatRect clipRect(localOrigin, FloatSize(m_logicalWidth, selHeight));
802     alignSelectionRectToDevicePixels(clipRect);
803
804     context->clip(clipRect);
805
806     context->drawHighlightForText(font, textRun, localOrigin, selHeight, c, style.colorSpace(), sPos, ePos);
807 }
808
809 void InlineTextBox::paintCompositionBackground(GraphicsContext* context, const FloatPoint& boxOrigin, const RenderStyle& style, const Font& font, int startPos, int endPos)
810 {
811     int offset = m_start;
812     int sPos = max(startPos - offset, 0);
813     int ePos = min(endPos - offset, (int)m_len);
814
815     if (sPos >= ePos)
816         return;
817
818     GraphicsContextStateSaver stateSaver(*context);
819
820     Color c = Color(225, 221, 85);
821     
822     updateGraphicsContext(*context, TextPaintStyle(c, style.colorSpace())); // Don't draw text at all!
823
824     int deltaY = renderer().style()->isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
825     int selHeight = selectionHeight();
826     FloatPoint localOrigin(boxOrigin.x(), boxOrigin.y() - deltaY);
827     context->drawHighlightForText(font, constructTextRun(style, font), localOrigin, selHeight, c, style.colorSpace(), sPos, ePos);
828 }
829
830 #if PLATFORM(MAC)
831
832 void InlineTextBox::paintCustomHighlight(const LayoutPoint& paintOffset, const AtomicString& type)
833 {
834     Page* page = renderer().frame().page();
835     if (!page)
836         return;
837
838     const RootInlineBox& rootBox = root();
839     FloatRect rootRect(paintOffset.x() + rootBox.x(), paintOffset.y() + selectionTop(), rootBox.logicalWidth(), selectionHeight());
840     FloatRect textRect(paintOffset.x() + x(), rootRect.y(), logicalWidth(), rootRect.height());
841
842     page->chrome().client().paintCustomHighlight(renderer().textNode(), type, textRect, rootRect, true, false);
843 }
844
845 #endif
846
847 static StrokeStyle textDecorationStyleToStrokeStyle(TextDecorationStyle decorationStyle)
848 {
849     StrokeStyle strokeStyle = SolidStroke;
850     switch (decorationStyle) {
851     case TextDecorationStyleSolid:
852         strokeStyle = SolidStroke;
853         break;
854 #if ENABLE(CSS3_TEXT)
855     case TextDecorationStyleDouble:
856         strokeStyle = DoubleStroke;
857         break;
858     case TextDecorationStyleDotted:
859         strokeStyle = DottedStroke;
860         break;
861     case TextDecorationStyleDashed:
862         strokeStyle = DashedStroke;
863         break;
864     case TextDecorationStyleWavy:
865         strokeStyle = WavyStroke;
866         break;
867 #endif // CSS3_TEXT
868     }
869
870     return strokeStyle;
871 }
872
873 #if ENABLE(CSS3_TEXT)
874 static int computeUnderlineOffset(const TextUnderlinePosition underlinePosition, const FontMetrics& fontMetrics, const InlineTextBox* inlineTextBox, const int textDecorationThickness)
875 {
876     // Compute the gap between the font and the underline. Use at least one
877     // pixel gap, if underline is thick then use a bigger gap.
878     const int gap = max<int>(1, ceilf(textDecorationThickness / 2.0));
879
880     // According to the specification TextUnderlinePositionAuto should default to 'alphabetic' for horizontal text
881     // and to 'under Left' for vertical text (e.g. japanese). We support only horizontal text for now.
882     switch (underlinePosition) {
883     case TextUnderlinePositionAlphabetic:
884     case TextUnderlinePositionAuto:
885         return fontMetrics.ascent() + gap; // Position underline near the alphabetic baseline.
886     case TextUnderlinePositionUnder: {
887         // Position underline relative to the under edge of the lowest element's content box.
888         const float offset = inlineTextBox->root().maxLogicalTop() - inlineTextBox->logicalTop();
889         if (offset > 0)
890             return inlineTextBox->logicalHeight() + gap + offset;
891         return inlineTextBox->logicalHeight() + gap;
892     }
893     }
894
895     ASSERT_NOT_REACHED();
896     return fontMetrics.ascent() + gap;
897 }
898 #endif // CSS3_TEXT
899
900 #if ENABLE(CSS3_TEXT)
901 static void adjustStepToDecorationLength(float& step, float& controlPointDistance, float length)
902 {
903     ASSERT(step > 0);
904
905     if (length <= 0)
906         return;
907
908     unsigned stepCount = static_cast<unsigned>(length / step);
909
910     // Each Bezier curve starts at the same pixel that the previous one
911     // ended. We need to subtract (stepCount - 1) pixels when calculating the
912     // length covered to account for that.
913     float uncoveredLength = length - (stepCount * step - (stepCount - 1));
914     float adjustment = uncoveredLength / stepCount;
915     step += adjustment;
916     controlPointDistance += adjustment;
917 }
918
919 /*
920  * Draw one cubic Bezier curve and repeat the same pattern long the the decoration's axis.
921  * The start point (p1), controlPoint1, controlPoint2 and end point (p2) of the Bezier curve
922  * form a diamond shape:
923  *
924  *                              step
925  *                         |-----------|
926  *
927  *                   controlPoint1
928  *                         +
929  *
930  *
931  *                  . .
932  *                .     .
933  *              .         .
934  * (x1, y1) p1 +           .            + p2 (x2, y2) - <--- Decoration's axis
935  *                          .         .               |
936  *                            .     .                 |
937  *                              . .                   | controlPointDistance
938  *                                                    |
939  *                                                    |
940  *                         +                          -
941  *                   controlPoint2
942  *
943  *             |-----------|
944  *                 step
945  */
946 static void strokeWavyTextDecoration(GraphicsContext* context, FloatPoint& p1, FloatPoint& p2, float strokeThickness)
947 {
948     context->adjustLineToPixelBoundaries(p1, p2, strokeThickness, context->strokeStyle());
949
950     Path path;
951     path.moveTo(p1);
952
953     // Distance between decoration's axis and Bezier curve's control points.
954     // The height of the curve is based on this distance. Use a minimum of 6 pixels distance since
955     // the actual curve passes approximately at half of that distance, that is 3 pixels.
956     // The minimum height of the curve is also approximately 3 pixels. Increases the curve's height
957     // as strockThickness increases to make the curve looks better.
958     float controlPointDistance = 3 * max<float>(2, strokeThickness);
959
960     // Increment used to form the diamond shape between start point (p1), control
961     // points and end point (p2) along the axis of the decoration. Makes the
962     // curve wider as strockThickness increases to make the curve looks better.
963     float step = 2 * max<float>(2, strokeThickness);
964
965     bool isVerticalLine = (p1.x() == p2.x());
966
967     if (isVerticalLine) {
968         ASSERT(p1.x() == p2.x());
969
970         float xAxis = p1.x();
971         float y1;
972         float y2;
973
974         if (p1.y() < p2.y()) {
975             y1 = p1.y();
976             y2 = p2.y();
977         } else {
978             y1 = p2.y();
979             y2 = p1.y();
980         }
981
982         adjustStepToDecorationLength(step, controlPointDistance, y2 - y1);
983         FloatPoint controlPoint1(xAxis + controlPointDistance, 0);
984         FloatPoint controlPoint2(xAxis - controlPointDistance, 0);
985
986         for (float y = y1; y + 2 * step <= y2;) {
987             controlPoint1.setY(y + step);
988             controlPoint2.setY(y + step);
989             y += 2 * step;
990             path.addBezierCurveTo(controlPoint1, controlPoint2, FloatPoint(xAxis, y));
991         }
992     } else {
993         ASSERT(p1.y() == p2.y());
994
995         float yAxis = p1.y();
996         float x1;
997         float x2;
998
999         if (p1.x() < p2.x()) {
1000             x1 = p1.x();
1001             x2 = p2.x();
1002         } else {
1003             x1 = p2.x();
1004             x2 = p1.x();
1005         }
1006
1007         adjustStepToDecorationLength(step, controlPointDistance, x2 - x1);
1008         FloatPoint controlPoint1(0, yAxis + controlPointDistance);
1009         FloatPoint controlPoint2(0, yAxis - controlPointDistance);
1010
1011         for (float x = x1; x + 2 * step <= x2;) {
1012             controlPoint1.setX(x + step);
1013             controlPoint2.setX(x + step);
1014             x += 2 * step;
1015             path.addBezierCurveTo(controlPoint1, controlPoint2, FloatPoint(x, yAxis));
1016         }
1017     }
1018
1019     context->setShouldAntialias(true);
1020     context->strokePath(path);
1021 }
1022 #endif // CSS3_TEXT
1023
1024 void InlineTextBox::paintDecoration(GraphicsContext* context, const FloatPoint& boxOrigin, TextDecoration deco, TextDecorationStyle decorationStyle, const ShadowData* shadow)
1025 {
1026     // FIXME: We should improve this rule and not always just assume 1.
1027     const float textDecorationThickness = 1.f;
1028
1029     if (m_truncation == cFullTruncation)
1030         return;
1031
1032     FloatPoint localOrigin = boxOrigin;
1033
1034     float width = m_logicalWidth;
1035     if (m_truncation != cNoTruncation) {
1036         width = renderer().width(m_start, m_truncation, textPos(), isFirstLine());
1037         if (!isLeftToRightDirection())
1038             localOrigin.move(m_logicalWidth - width, 0);
1039     }
1040     
1041     // Get the text decoration colors.
1042     Color underline, overline, linethrough;
1043     renderer().getTextDecorationColors(deco, underline, overline, linethrough, true);
1044     if (isFirstLine())
1045         renderer().getTextDecorationColors(deco, underline, overline, linethrough, true, true);
1046     
1047     // Use a special function for underlines to get the positioning exactly right.
1048     bool isPrinting = renderer().document().printing();
1049     context->setStrokeThickness(textDecorationThickness);
1050
1051     bool linesAreOpaque = !isPrinting && (!(deco & TextDecorationUnderline) || underline.alpha() == 255) && (!(deco & TextDecorationOverline) || overline.alpha() == 255) && (!(deco & TextDecorationLineThrough) || linethrough.alpha() == 255);
1052
1053     const RenderStyle& lineStyle = this->lineStyle();
1054     int baseline = lineStyle.fontMetrics().ascent();
1055
1056     bool setClip = false;
1057     int extraOffset = 0;
1058     if (!linesAreOpaque && shadow && shadow->next()) {
1059         FloatRect clipRect(localOrigin, FloatSize(width, baseline + 2));
1060         for (const ShadowData* s = shadow; s; s = s->next()) {
1061             int shadowExtent = s->paintingExtent();
1062             FloatRect shadowRect(localOrigin, FloatSize(width, baseline + 2));
1063             shadowRect.inflate(shadowExtent);
1064             int shadowX = isHorizontal() ? s->x() : s->y();
1065             int shadowY = isHorizontal() ? s->y() : -s->x();
1066             shadowRect.move(shadowX, shadowY);
1067             clipRect.unite(shadowRect);
1068             extraOffset = max(extraOffset, max(0, shadowY) + shadowExtent);
1069         }
1070         context->save();
1071         context->clip(clipRect);
1072         extraOffset += baseline + 2;
1073         localOrigin.move(0, extraOffset);
1074         setClip = true;
1075     }
1076
1077     ColorSpace colorSpace = renderer().style()->colorSpace();
1078     bool setShadow = false;
1079
1080     do {
1081         if (shadow) {
1082             if (!shadow->next()) {
1083                 // The last set of lines paints normally inside the clip.
1084                 localOrigin.move(0, -extraOffset);
1085                 extraOffset = 0;
1086             }
1087             int shadowX = isHorizontal() ? shadow->x() : shadow->y();
1088             int shadowY = isHorizontal() ? shadow->y() : -shadow->x();
1089             context->setShadow(FloatSize(shadowX, shadowY - extraOffset), shadow->radius(), shadow->color(), colorSpace);
1090             setShadow = true;
1091             shadow = shadow->next();
1092         }
1093
1094 #if ENABLE(CSS3_TEXT)
1095         // Offset between lines - always non-zero, so lines never cross each other.
1096         float doubleOffset = textDecorationThickness + 1.f;
1097 #endif // CSS3_TEXT
1098         context->setStrokeStyle(textDecorationStyleToStrokeStyle(decorationStyle));
1099         if (deco & TextDecorationUnderline) {
1100             context->setStrokeColor(underline, colorSpace);
1101 #if ENABLE(CSS3_TEXT)
1102             TextUnderlinePosition underlinePosition = lineStyle.textUnderlinePosition();
1103             const int underlineOffset = computeUnderlineOffset(underlinePosition, lineStyle.fontMetrics(), this, textDecorationThickness);
1104
1105             switch (decorationStyle) {
1106             case TextDecorationStyleWavy: {
1107                 FloatPoint start(localOrigin.x(), localOrigin.y() + underlineOffset + doubleOffset);
1108                 FloatPoint end(localOrigin.x() + width, localOrigin.y() + underlineOffset + doubleOffset);
1109                 strokeWavyTextDecoration(context, start, end, textDecorationThickness);
1110                 break;
1111             }
1112             default:
1113                 context->drawLineForText(FloatPoint(localOrigin.x(), localOrigin.y() + underlineOffset), width, isPrinting);
1114
1115                 if (decorationStyle == TextDecorationStyleDouble)
1116                     context->drawLineForText(FloatPoint(localOrigin.x(), localOrigin.y() + underlineOffset + doubleOffset), width, isPrinting);
1117             }
1118 #else
1119             // Leave one pixel of white between the baseline and the underline.
1120             context->drawLineForText(FloatPoint(localOrigin.x(), localOrigin.y() + baseline + 1), width, isPrinting);
1121 #endif // CSS3_TEXT
1122         }
1123         if (deco & TextDecorationOverline) {
1124             context->setStrokeColor(overline, colorSpace);
1125 #if ENABLE(CSS3_TEXT)
1126             switch (decorationStyle) {
1127             case TextDecorationStyleWavy: {
1128                 FloatPoint start(localOrigin.x(), localOrigin.y() - doubleOffset);
1129                 FloatPoint end(localOrigin.x() + width, localOrigin.y() - doubleOffset);
1130                 strokeWavyTextDecoration(context, start, end, textDecorationThickness);
1131                 break;
1132             }
1133             default:
1134 #endif // CSS3_TEXT
1135                 context->drawLineForText(localOrigin, width, isPrinting);
1136 #if ENABLE(CSS3_TEXT)
1137                 if (decorationStyle == TextDecorationStyleDouble)
1138                     context->drawLineForText(FloatPoint(localOrigin.x(), localOrigin.y() - doubleOffset), width, isPrinting);
1139             }
1140 #endif // CSS3_TEXT
1141         }
1142         if (deco & TextDecorationLineThrough) {
1143             context->setStrokeColor(linethrough, colorSpace);
1144 #if ENABLE(CSS3_TEXT)
1145             switch (decorationStyle) {
1146             case TextDecorationStyleWavy: {
1147                 FloatPoint start(localOrigin.x(), localOrigin.y() + 2 * baseline / 3);
1148                 FloatPoint end(localOrigin.x() + width, localOrigin.y() + 2 * baseline / 3);
1149                 strokeWavyTextDecoration(context, start, end, textDecorationThickness);
1150                 break;
1151             }
1152             default:
1153 #endif // CSS3_TEXT
1154                 context->drawLineForText(FloatPoint(localOrigin.x(), localOrigin.y() + 2 * baseline / 3), width, isPrinting);
1155 #if ENABLE(CSS3_TEXT)
1156                 if (decorationStyle == TextDecorationStyleDouble)
1157                     context->drawLineForText(FloatPoint(localOrigin.x(), localOrigin.y() + doubleOffset + 2 * baseline / 3), width, isPrinting);
1158             }
1159 #endif // CSS3_TEXT
1160         }
1161     } while (shadow);
1162
1163     if (setClip)
1164         context->restore();
1165     else if (setShadow)
1166         context->clearShadow();
1167 }
1168
1169 static GraphicsContext::DocumentMarkerLineStyle lineStyleForMarkerType(DocumentMarker::MarkerType markerType)
1170 {
1171     switch (markerType) {
1172     case DocumentMarker::Spelling:
1173         return GraphicsContext::DocumentMarkerSpellingLineStyle;
1174     case DocumentMarker::Grammar:
1175         return GraphicsContext::DocumentMarkerGrammarLineStyle;
1176     case DocumentMarker::CorrectionIndicator:
1177         return GraphicsContext::DocumentMarkerAutocorrectionReplacementLineStyle;
1178     case DocumentMarker::DictationAlternatives:
1179         return GraphicsContext::DocumentMarkerDictationAlternativesLineStyle;
1180     default:
1181         ASSERT_NOT_REACHED();
1182         return GraphicsContext::DocumentMarkerSpellingLineStyle;
1183     }
1184 }
1185
1186 void InlineTextBox::paintDocumentMarker(GraphicsContext* pt, const FloatPoint& boxOrigin, DocumentMarker* marker, const RenderStyle& style, const Font& font, bool grammar)
1187 {
1188     // Never print spelling/grammar markers (5327887)
1189     if (renderer().document().printing())
1190         return;
1191
1192     if (m_truncation == cFullTruncation)
1193         return;
1194
1195     float start = 0; // start of line to draw, relative to tx
1196     float width = m_logicalWidth; // how much line to draw
1197
1198     // Determine whether we need to measure text
1199     bool markerSpansWholeBox = true;
1200     if (m_start <= (int)marker->startOffset())
1201         markerSpansWholeBox = false;
1202     if ((end() + 1) != marker->endOffset()) // end points at the last char, not past it
1203         markerSpansWholeBox = false;
1204     if (m_truncation != cNoTruncation)
1205         markerSpansWholeBox = false;
1206
1207     bool isDictationMarker = marker->type() == DocumentMarker::DictationAlternatives;
1208     if (!markerSpansWholeBox || grammar || isDictationMarker) {
1209         int startPosition = max<int>(marker->startOffset() - m_start, 0);
1210         int endPosition = min<int>(marker->endOffset() - m_start, m_len);
1211         
1212         if (m_truncation != cNoTruncation)
1213             endPosition = min<int>(endPosition, m_truncation);
1214
1215         // Calculate start & width
1216         int deltaY = renderer().style()->isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
1217         int selHeight = selectionHeight();
1218         FloatPoint startPoint(boxOrigin.x(), boxOrigin.y() - deltaY);
1219         TextRun run = constructTextRun(style, font);
1220
1221         // FIXME: Convert the document markers to float rects.
1222         IntRect markerRect = enclosingIntRect(font.selectionRectForText(run, startPoint, selHeight, startPosition, endPosition));
1223         start = markerRect.x() - startPoint.x();
1224         width = markerRect.width();
1225         
1226         // Store rendered rects for bad grammar markers, so we can hit-test against it elsewhere in order to
1227         // display a toolTip. We don't do this for misspelling markers.
1228         if (grammar || isDictationMarker) {
1229             markerRect.move(-boxOrigin.x(), -boxOrigin.y());
1230             markerRect = renderer().localToAbsoluteQuad(FloatRect(markerRect)).enclosingBoundingBox();
1231             toRenderedDocumentMarker(marker)->setRenderedRect(markerRect);
1232         }
1233     }
1234     
1235     // IMPORTANT: The misspelling underline is not considered when calculating the text bounds, so we have to
1236     // make sure to fit within those bounds.  This means the top pixel(s) of the underline will overlap the
1237     // bottom pixel(s) of the glyphs in smaller font sizes.  The alternatives are to increase the line spacing (bad!!)
1238     // or decrease the underline thickness.  The overlap is actually the most useful, and matches what AppKit does.
1239     // So, we generally place the underline at the bottom of the text, but in larger fonts that's not so good so
1240     // we pin to two pixels under the baseline.
1241     int lineThickness = cMisspellingLineThickness;
1242     int baseline = lineStyle().fontMetrics().ascent();
1243     int descent = logicalHeight() - baseline;
1244     int underlineOffset;
1245     if (descent <= (2 + lineThickness)) {
1246         // Place the underline at the very bottom of the text in small/medium fonts.
1247         underlineOffset = logicalHeight() - lineThickness;
1248     } else {
1249         // In larger fonts, though, place the underline up near the baseline to prevent a big gap.
1250         underlineOffset = baseline + 2;
1251     }
1252     pt->drawLineForDocumentMarker(FloatPoint(boxOrigin.x() + start, boxOrigin.y() + underlineOffset), width, lineStyleForMarkerType(marker->type()));
1253 }
1254
1255 void InlineTextBox::paintTextMatchMarker(GraphicsContext* pt, const FloatPoint& boxOrigin, DocumentMarker* marker, const RenderStyle& style, const Font& font)
1256 {
1257     // Use same y positioning and height as for selection, so that when the selection and this highlight are on
1258     // the same word there are no pieces sticking out.
1259     int deltaY = renderer().style()->isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
1260     int selHeight = selectionHeight();
1261
1262     int sPos = max(marker->startOffset() - m_start, (unsigned)0);
1263     int ePos = min(marker->endOffset() - m_start, (unsigned)m_len);
1264     TextRun run = constructTextRun(style, font);
1265
1266     // Always compute and store the rect associated with this marker. The computed rect is in absolute coordinates.
1267     IntRect markerRect = enclosingIntRect(font.selectionRectForText(run, IntPoint(x(), selectionTop()), selHeight, sPos, ePos));
1268     markerRect = renderer().localToAbsoluteQuad(FloatRect(markerRect)).enclosingBoundingBox();
1269     toRenderedDocumentMarker(marker)->setRenderedRect(markerRect);
1270     
1271     // Optionally highlight the text
1272     if (renderer().frame().editor().markedTextMatchesAreHighlighted()) {
1273         Color color = marker->activeMatch() ?
1274             renderer().theme()->platformActiveTextSearchHighlightColor() :
1275             renderer().theme()->platformInactiveTextSearchHighlightColor();
1276         GraphicsContextStateSaver stateSaver(*pt);
1277         updateGraphicsContext(*pt, TextPaintStyle(color, style.colorSpace())); // Don't draw text at all!
1278         pt->clip(FloatRect(boxOrigin.x(), boxOrigin.y() - deltaY, m_logicalWidth, selHeight));
1279         pt->drawHighlightForText(font, run, FloatPoint(boxOrigin.x(), boxOrigin.y() - deltaY), selHeight, color, style.colorSpace(), sPos, ePos);
1280     }
1281 }
1282
1283 void InlineTextBox::computeRectForReplacementMarker(DocumentMarker* marker, const RenderStyle& style, const Font& font)
1284 {
1285     // Replacement markers are not actually drawn, but their rects need to be computed for hit testing.
1286     int top = selectionTop();
1287     int h = selectionHeight();
1288     
1289     int sPos = max(marker->startOffset() - m_start, (unsigned)0);
1290     int ePos = min(marker->endOffset() - m_start, (unsigned)m_len);
1291     TextRun run = constructTextRun(style, font);
1292     IntPoint startPoint = IntPoint(x(), top);
1293     
1294     // Compute and store the rect associated with this marker.
1295     IntRect markerRect = enclosingIntRect(font.selectionRectForText(run, startPoint, h, sPos, ePos));
1296     markerRect = renderer().localToAbsoluteQuad(FloatRect(markerRect)).enclosingBoundingBox();
1297     toRenderedDocumentMarker(marker)->setRenderedRect(markerRect);
1298 }
1299     
1300 void InlineTextBox::paintDocumentMarkers(GraphicsContext* pt, const FloatPoint& boxOrigin, const RenderStyle& style, const Font& font, bool background)
1301 {
1302     if (!renderer().textNode())
1303         return;
1304
1305     Vector<DocumentMarker*> markers = renderer().document().markers().markersFor(renderer().textNode());
1306     Vector<DocumentMarker*>::const_iterator markerIt = markers.begin();
1307
1308     // Give any document markers that touch this run a chance to draw before the text has been drawn.
1309     // Note end() points at the last char, not one past it like endOffset and ranges do.
1310     for ( ; markerIt != markers.end(); ++markerIt) {
1311         DocumentMarker* marker = *markerIt;
1312         
1313         // Paint either the background markers or the foreground markers, but not both
1314         switch (marker->type()) {
1315             case DocumentMarker::Grammar:
1316             case DocumentMarker::Spelling:
1317             case DocumentMarker::CorrectionIndicator:
1318             case DocumentMarker::Replacement:
1319             case DocumentMarker::DictationAlternatives:
1320                 if (background)
1321                     continue;
1322                 break;
1323             case DocumentMarker::TextMatch:
1324                 if (!background)
1325                     continue;
1326                 break;
1327             default:
1328                 continue;
1329         }
1330
1331         if (marker->endOffset() <= start())
1332             // marker is completely before this run.  This might be a marker that sits before the
1333             // first run we draw, or markers that were within runs we skipped due to truncation.
1334             continue;
1335         
1336         if (marker->startOffset() > end())
1337             // marker is completely after this run, bail.  A later run will paint it.
1338             break;
1339         
1340         // marker intersects this run.  Paint it.
1341         switch (marker->type()) {
1342             case DocumentMarker::Spelling:
1343             case DocumentMarker::CorrectionIndicator:
1344             case DocumentMarker::DictationAlternatives:
1345                 paintDocumentMarker(pt, boxOrigin, marker, style, font, false);
1346                 break;
1347             case DocumentMarker::Grammar:
1348                 paintDocumentMarker(pt, boxOrigin, marker, style, font, true);
1349                 break;
1350             case DocumentMarker::TextMatch:
1351                 paintTextMatchMarker(pt, boxOrigin, marker, style, font);
1352                 break;
1353             case DocumentMarker::Replacement:
1354                 computeRectForReplacementMarker(marker, style, font);
1355                 break;
1356             default:
1357                 ASSERT_NOT_REACHED();
1358         }
1359
1360     }
1361 }
1362
1363 void InlineTextBox::paintCompositionUnderline(GraphicsContext* ctx, const FloatPoint& boxOrigin, const CompositionUnderline& underline)
1364 {
1365     if (m_truncation == cFullTruncation)
1366         return;
1367     
1368     float start = 0; // start of line to draw, relative to tx
1369     float width = m_logicalWidth; // how much line to draw
1370     bool useWholeWidth = true;
1371     unsigned paintStart = m_start;
1372     unsigned paintEnd = end() + 1; // end points at the last char, not past it
1373     if (paintStart <= underline.startOffset) {
1374         paintStart = underline.startOffset;
1375         useWholeWidth = false;
1376         start = renderer().width(m_start, paintStart - m_start, textPos(), isFirstLine());
1377     }
1378     if (paintEnd != underline.endOffset) {      // end points at the last char, not past it
1379         paintEnd = min(paintEnd, (unsigned)underline.endOffset);
1380         useWholeWidth = false;
1381     }
1382     if (m_truncation != cNoTruncation) {
1383         paintEnd = min(paintEnd, (unsigned)m_start + m_truncation);
1384         useWholeWidth = false;
1385     }
1386     if (!useWholeWidth) {
1387         width = renderer().width(paintStart, paintEnd - paintStart, textPos() + start, isFirstLine());
1388     }
1389
1390     // Thick marked text underlines are 2px thick as long as there is room for the 2px line under the baseline.
1391     // All other marked text underlines are 1px thick.
1392     // If there's not enough space the underline will touch or overlap characters.
1393     int lineThickness = 1;
1394     int baseline = lineStyle().fontMetrics().ascent();
1395     if (underline.thick && logicalHeight() - baseline >= 2)
1396         lineThickness = 2;
1397
1398     // We need to have some space between underlines of subsequent clauses, because some input methods do not use different underline styles for those.
1399     // We make each line shorter, which has a harmless side effect of shortening the first and last clauses, too.
1400     start += 1;
1401     width -= 2;
1402
1403     ctx->setStrokeColor(underline.color, renderer().style()->colorSpace());
1404     ctx->setStrokeThickness(lineThickness);
1405     ctx->drawLineForText(FloatPoint(boxOrigin.x() + start, boxOrigin.y() + logicalHeight() - lineThickness), width, renderer().document().printing());
1406 }
1407
1408 int InlineTextBox::caretMinOffset() const
1409 {
1410     return m_start;
1411 }
1412
1413 int InlineTextBox::caretMaxOffset() const
1414 {
1415     return m_start + m_len;
1416 }
1417
1418 float InlineTextBox::textPos() const
1419 {
1420     // When computing the width of a text run, RenderBlock::computeInlineDirectionPositionsForLine() doesn't include the actual offset
1421     // from the containing block edge in its measurement. textPos() should be consistent so the text are rendered in the same width.
1422     if (logicalLeft() == 0)
1423         return 0;
1424     return logicalLeft() - root().logicalLeft();
1425 }
1426
1427 int InlineTextBox::offsetForPosition(float lineOffset, bool includePartialGlyphs) const
1428 {
1429     if (isLineBreak())
1430         return 0;
1431
1432     if (lineOffset - logicalLeft() > logicalWidth())
1433         return isLeftToRightDirection() ? len() : 0;
1434     if (lineOffset - logicalLeft() < 0)
1435         return isLeftToRightDirection() ? 0 : len();
1436
1437     FontCachePurgePreventer fontCachePurgePreventer;
1438
1439     const RenderStyle& lineStyle = this->lineStyle();
1440     const Font& font = fontToUse(lineStyle, renderer());
1441     return font.offsetForPosition(constructTextRun(lineStyle, font), lineOffset - logicalLeft(), includePartialGlyphs);
1442 }
1443
1444 float InlineTextBox::positionForOffset(int offset) const
1445 {
1446     ASSERT(offset >= m_start);
1447     ASSERT(offset <= m_start + m_len);
1448
1449     if (isLineBreak())
1450         return logicalLeft();
1451
1452     FontCachePurgePreventer fontCachePurgePreventer;
1453
1454     const RenderStyle& lineStyle = this->lineStyle();
1455     const Font& font = fontToUse(lineStyle, renderer());
1456     int from = !isLeftToRightDirection() ? offset - m_start : 0;
1457     int to = !isLeftToRightDirection() ? m_len : offset - m_start;
1458     // FIXME: Do we need to add rightBearing here?
1459     return font.selectionRectForText(constructTextRun(lineStyle, font), IntPoint(logicalLeft(), 0), 0, from, to).maxX();
1460 }
1461
1462 bool InlineTextBox::containsCaretOffset(int offset) const
1463 {
1464     // Offsets before the box are never "in".
1465     if (offset < m_start)
1466         return false;
1467
1468     int pastEnd = m_start + m_len;
1469
1470     // Offsets inside the box (not at either edge) are always "in".
1471     if (offset < pastEnd)
1472         return true;
1473
1474     // Offsets outside the box are always "out".
1475     if (offset > pastEnd)
1476         return false;
1477
1478     // Offsets at the end are "out" for line breaks (they are on the next line).
1479     if (isLineBreak())
1480         return false;
1481
1482     // Offsets at the end are "in" for normal boxes (but the caller has to check affinity).
1483     return true;
1484 }
1485
1486 TextRun InlineTextBox::constructTextRun(const RenderStyle& style, const Font& font, BufferForAppendingHyphen* charactersWithHyphen) const
1487 {
1488     ASSERT(renderer().text());
1489
1490     String string = renderer().text();
1491     unsigned startPos = start();
1492     unsigned length = len();
1493
1494     if (string.length() != length || startPos)
1495         string = string.substringSharingImpl(startPos, length);
1496
1497     return constructTextRun(style, font, string, renderer().textLength() - startPos, charactersWithHyphen);
1498 }
1499
1500 TextRun InlineTextBox::constructTextRun(const RenderStyle& style, const Font& font, String string, int maximumLength, BufferForAppendingHyphen* charactersWithHyphen) const
1501 {
1502     int length = string.length();
1503
1504     if (charactersWithHyphen) {
1505         adjustCharactersAndLengthForHyphen(*charactersWithHyphen, style, string, length);
1506         maximumLength = length;
1507     }
1508
1509     ASSERT(maximumLength >= length);
1510
1511     TextRun run(string, textPos(), expansion(), expansionBehavior(), direction(), dirOverride() || style.rtlOrdering() == VisualOrder, !renderer().canUseSimpleFontCodePath());
1512     run.setTabSize(!style.collapseWhiteSpace(), style.tabSize());
1513     if (textRunNeedsRenderingContext(font))
1514         run.setRenderingContext(SVGTextRunRenderingContext::create(&renderer()));
1515
1516     // Propagate the maximum length of the characters buffer to the TextRun, even when we're only processing a substring.
1517     run.setCharactersLength(maximumLength);
1518     ASSERT(run.charactersLength() >= run.length());
1519     return run;
1520 }
1521
1522 #ifndef NDEBUG
1523
1524 const char* InlineTextBox::boxName() const
1525 {
1526     return "InlineTextBox";
1527 }
1528
1529 void InlineTextBox::showBox(int printedCharacters) const
1530 {
1531     String value = renderer().text();
1532     value = value.substring(start(), len());
1533     value.replaceWithLiteral('\\', "\\\\");
1534     value.replaceWithLiteral('\n', "\\n");
1535     printedCharacters += fprintf(stderr, "%s\t%p", boxName(), this);
1536     for (; printedCharacters < showTreeCharacterOffset; printedCharacters++)
1537         fputc(' ', stderr);
1538     printedCharacters = fprintf(stderr, "\t%s %p", renderer().renderName(), &renderer());
1539     const int rendererCharacterOffset = 24;
1540     for (; printedCharacters < rendererCharacterOffset; printedCharacters++)
1541         fputc(' ', stderr);
1542     fprintf(stderr, "(%d,%d) \"%s\"\n", start(), start() + len(), value.utf8().data());
1543 }
1544
1545 #endif
1546
1547 } // namespace WebCore