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