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