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