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