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 "InlineTextBox.h"
     25 
     26 #include "Chrome.h"
     27 #include "ChromeClient.h"
     28 #include "Document.h"
     29 #include "DocumentMarkerController.h"
     30 #include "Editor.h"
     31 #include "EllipsisBox.h"
     32 #include "Frame.h"
     33 #include "GraphicsContext.h"
     34 #include "HitTestResult.h"
     35 #include "Page.h"
     36 #include "PaintInfo.h"
     37 #include "RenderArena.h"
     38 #include "RenderBlock.h"
     39 #include "RenderCombineText.h"
     40 #include "RenderRubyRun.h"
     41 #include "RenderRubyText.h"
     42 #include "RenderTheme.h"
     43 #include "Text.h"
     44 #include "break_lines.h"
     45 #include <wtf/AlwaysInline.h>
     46 
     47 using namespace std;
     48 
     49 namespace WebCore {
     50 
     51 typedef WTF::HashMap<const InlineTextBox*, IntRect> InlineTextBoxOverflowMap;
     52 static InlineTextBoxOverflowMap* gTextBoxesWithOverflow;
     53 
     54 void InlineTextBox::destroy(RenderArena* arena)
     55 {
     56     if (!m_knownToHaveNoOverflow && gTextBoxesWithOverflow)
     57         gTextBoxesWithOverflow->remove(this);
     58     InlineBox::destroy(arena);
     59 }
     60 
     61 IntRect InlineTextBox::logicalOverflowRect() const
     62 {
     63     if (m_knownToHaveNoOverflow || !gTextBoxesWithOverflow)
     64         return enclosingIntRect(logicalFrameRect());
     65     return gTextBoxesWithOverflow->get(this);
     66 }
     67 
     68 void InlineTextBox::setLogicalOverflowRect(const IntRect& rect)
     69 {
     70     ASSERT(!m_knownToHaveNoOverflow);
     71     if (!gTextBoxesWithOverflow)
     72         gTextBoxesWithOverflow = new InlineTextBoxOverflowMap;
     73     gTextBoxesWithOverflow->add(this, rect);
     74 }
     75 
     76 int InlineTextBox::baselinePosition(FontBaseline baselineType) const
     77 {
     78     if (!isText() || !parent())
     79         return 0;
     80     if (parent()->renderer() == renderer()->parent())
     81         return parent()->baselinePosition(baselineType);
     82     return toRenderBoxModelObject(renderer()->parent())->baselinePosition(baselineType, m_firstLine, isHorizontal() ? HorizontalLine : VerticalLine, PositionOnContainingLine);
     83 }
     84 
     85 int InlineTextBox::lineHeight() const
     86 {
     87     if (!isText() || !renderer()->parent())
     88         return 0;
     89     if (m_renderer->isBR())
     90         return toRenderBR(m_renderer)->lineHeight(m_firstLine);
     91     if (parent()->renderer() == renderer()->parent())
     92         return parent()->lineHeight();
     93     return toRenderBoxModelObject(renderer()->parent())->lineHeight(m_firstLine, isHorizontal() ? HorizontalLine : VerticalLine, PositionOnContainingLine);
     94 }
     95 
     96 int InlineTextBox::selectionTop()
     97 {
     98     return root()->selectionTop();
     99 }
    100 
    101 int InlineTextBox::selectionBottom()
    102 {
    103     return root()->selectionBottom();
    104 }
    105 
    106 int InlineTextBox::selectionHeight()
    107 {
    108     return root()->selectionHeight();
    109 }
    110 
    111 bool InlineTextBox::isSelected(int startPos, int endPos) const
    112 {
    113     int sPos = max(startPos - m_start, 0);
    114     int ePos = min(endPos - m_start, (int)m_len);
    115     return (sPos < ePos);
    116 }
    117 
    118 RenderObject::SelectionState InlineTextBox::selectionState()
    119 {
    120     RenderObject::SelectionState state = renderer()->selectionState();
    121     if (state == RenderObject::SelectionStart || state == RenderObject::SelectionEnd || state == RenderObject::SelectionBoth) {
    122         int startPos, endPos;
    123         renderer()->selectionStartEnd(startPos, endPos);
    124         // The position after a hard line break is considered to be past its end.
    125         int lastSelectable = start() + len() - (isLineBreak() ? 1 : 0);
    126 
    127         bool start = (state != RenderObject::SelectionEnd && startPos >= m_start && startPos < m_start + m_len);
    128         bool end = (state != RenderObject::SelectionStart && endPos > m_start && endPos <= lastSelectable);
    129         if (start && end)
    130             state = RenderObject::SelectionBoth;
    131         else if (start)
    132             state = RenderObject::SelectionStart;
    133         else if (end)
    134             state = RenderObject::SelectionEnd;
    135         else if ((state == RenderObject::SelectionEnd || startPos < m_start) &&
    136                  (state == RenderObject::SelectionStart || endPos > lastSelectable))
    137             state = RenderObject::SelectionInside;
    138         else if (state == RenderObject::SelectionBoth)
    139             state = RenderObject::SelectionNone;
    140     }
    141 
    142     // If there are ellipsis following, make sure their selection is updated.
    143     if (m_truncation != cNoTruncation && root()->ellipsisBox()) {
    144         EllipsisBox* ellipsis = root()->ellipsisBox();
    145         if (state != RenderObject::SelectionNone) {
    146             int start, end;
    147             selectionStartEnd(start, end);
    148             // The ellipsis should be considered to be selected if the end of
    149             // the selection is past the beginning of the truncation and the
    150             // beginning of the selection is before or at the beginning of the
    151             // truncation.
    152             ellipsis->setSelectionState(end >= m_truncation && start <= m_truncation ?
    153                 RenderObject::SelectionInside : RenderObject::SelectionNone);
    154         } else
    155             ellipsis->setSelectionState(RenderObject::SelectionNone);
    156     }
    157 
    158     return state;
    159 }
    160 
    161 typedef Vector<UChar, 256> BufferForAppendingHyphen;
    162 
    163 static void adjustCharactersAndLengthForHyphen(BufferForAppendingHyphen& charactersWithHyphen, RenderStyle* style, const UChar*& characters, int& length)
    164 {
    165     const AtomicString& hyphenString = style->hyphenString();
    166     charactersWithHyphen.reserveCapacity(length + hyphenString.length());
    167     charactersWithHyphen.append(characters, length);
    168     charactersWithHyphen.append(hyphenString.characters(), hyphenString.length());
    169     characters = charactersWithHyphen.data();
    170     length += hyphenString.length();
    171 }
    172 
    173 IntRect InlineTextBox::selectionRect(int tx, int ty, int startPos, int endPos)
    174 {
    175     int sPos = max(startPos - m_start, 0);
    176     int ePos = min(endPos - m_start, (int)m_len);
    177 
    178     if (sPos > ePos)
    179         return IntRect();
    180 
    181     RenderText* textObj = textRenderer();
    182     int selTop = selectionTop();
    183     int selHeight = selectionHeight();
    184     RenderStyle* styleToUse = textObj->style(m_firstLine);
    185     const Font& f = styleToUse->font();
    186 
    187     const UChar* characters = textObj->text()->characters() + m_start;
    188     int len = m_len;
    189     BufferForAppendingHyphen charactersWithHyphen;
    190     if (ePos == len && hasHyphen()) {
    191         adjustCharactersAndLengthForHyphen(charactersWithHyphen, styleToUse, characters, len);
    192         ePos = len;
    193     }
    194 
    195     IntRect r = enclosingIntRect(f.selectionRectForText(TextRun(characters, len, textObj->allowTabs(), textPos(), m_expansion, expansionBehavior(), !isLeftToRightDirection(), m_dirOverride),
    196                                                         IntPoint(), selHeight, sPos, ePos));
    197 
    198     int logicalWidth = r.width();
    199     if (r.x() > m_logicalWidth)
    200         logicalWidth  = 0;
    201     else if (r.maxX() > m_logicalWidth)
    202         logicalWidth = m_logicalWidth - r.x();
    203 
    204     IntPoint topPoint = isHorizontal() ? IntPoint(tx + m_x + r.x(), ty + selTop) : IntPoint(tx + selTop, ty + m_y + r.x());
    205     int width = isHorizontal() ? logicalWidth : selHeight;
    206     int height = isHorizontal() ? selHeight : logicalWidth;
    207 
    208     return IntRect(topPoint, IntSize(width, height));
    209 }
    210 
    211 void InlineTextBox::deleteLine(RenderArena* arena)
    212 {
    213     toRenderText(renderer())->removeTextBox(this);
    214     destroy(arena);
    215 }
    216 
    217 void InlineTextBox::extractLine()
    218 {
    219     if (m_extracted)
    220         return;
    221 
    222     toRenderText(renderer())->extractTextBox(this);
    223 }
    224 
    225 void InlineTextBox::attachLine()
    226 {
    227     if (!m_extracted)
    228         return;
    229 
    230     toRenderText(renderer())->attachTextBox(this);
    231 }
    232 
    233 float InlineTextBox::placeEllipsisBox(bool flowIsLTR, float visibleLeftEdge, float visibleRightEdge, float ellipsisWidth, bool& foundBox)
    234 {
    235     if (foundBox) {
    236         m_truncation = cFullTruncation;
    237         return -1;
    238     }
    239 
    240     // For LTR this is the left edge of the box, for RTL, the right edge in parent coordinates.
    241     float ellipsisX = flowIsLTR ? visibleRightEdge - ellipsisWidth : visibleLeftEdge + ellipsisWidth;
    242 
    243     // Criteria for full truncation:
    244     // LTR: the left edge of the ellipsis is to the left of our text run.
    245     // RTL: the right edge of the ellipsis is to the right of our text run.
    246     bool ltrFullTruncation = flowIsLTR && ellipsisX <= m_x;
    247     bool rtlFullTruncation = !flowIsLTR && ellipsisX >= (m_x + m_logicalWidth);
    248     if (ltrFullTruncation || rtlFullTruncation) {
    249         // Too far.  Just set full truncation, but return -1 and let the ellipsis just be placed at the edge of the box.
    250         m_truncation = cFullTruncation;
    251         foundBox = true;
    252         return -1;
    253     }
    254 
    255     bool ltrEllipsisWithinBox = flowIsLTR && (ellipsisX < m_x + m_logicalWidth);
    256     bool rtlEllipsisWithinBox = !flowIsLTR && (ellipsisX > m_x);
    257     if (ltrEllipsisWithinBox || rtlEllipsisWithinBox) {
    258         foundBox = true;
    259 
    260         // The inline box may have different directionality than it's parent.  Since truncation
    261         // behavior depends both on both the parent and the inline block's directionality, we
    262         // must keep track of these separately.
    263         bool ltr = isLeftToRightDirection();
    264         if (ltr != flowIsLTR) {
    265           // Width in pixels of the visible portion of the box, excluding the ellipsis.
    266           int visibleBoxWidth = visibleRightEdge - visibleLeftEdge  - ellipsisWidth;
    267           ellipsisX = ltr ? m_x + visibleBoxWidth : m_x + m_logicalWidth - visibleBoxWidth;
    268         }
    269 
    270         int offset = offsetForPosition(ellipsisX, false);
    271         if (offset == 0) {
    272             // No characters should be rendered.  Set ourselves to full truncation and place the ellipsis at the min of our start
    273             // and the ellipsis edge.
    274             m_truncation = cFullTruncation;
    275             return min(ellipsisX, m_x);
    276         }
    277 
    278         // Set the truncation index on the text run.
    279         m_truncation = offset;
    280 
    281         // If we got here that means that we were only partially truncated and we need to return the pixel offset at which
    282         // to place the ellipsis.
    283         float widthOfVisibleText = toRenderText(renderer())->width(m_start, offset, textPos(), m_firstLine);
    284 
    285         // The ellipsis needs to be placed just after the last visible character.
    286         // Where "after" is defined by the flow directionality, not the inline
    287         // box directionality.
    288         // e.g. In the case of an LTR inline box truncated in an RTL flow then we can
    289         // have a situation such as |Hello| -> |...He|
    290         if (flowIsLTR)
    291             return m_x + widthOfVisibleText;
    292         else
    293             return (m_x + m_logicalWidth) - widthOfVisibleText - ellipsisWidth;
    294     }
    295     return -1;
    296 }
    297 
    298 Color correctedTextColor(Color textColor, Color backgroundColor)
    299 {
    300     // Adjust the text color if it is too close to the background color,
    301     // by darkening or lightening it to move it further away.
    302 
    303     int d = differenceSquared(textColor, backgroundColor);
    304     // semi-arbitrarily chose 65025 (255^2) value here after a few tests;
    305     if (d > 65025) {
    306         return textColor;
    307     }
    308 
    309     int distanceFromWhite = differenceSquared(textColor, Color::white);
    310     int distanceFromBlack = differenceSquared(textColor, Color::black);
    311 
    312     if (distanceFromWhite < distanceFromBlack) {
    313         return textColor.dark();
    314     }
    315 
    316     return textColor.light();
    317 }
    318 
    319 void updateGraphicsContext(GraphicsContext* context, const Color& fillColor, const Color& strokeColor, float strokeThickness, ColorSpace colorSpace)
    320 {
    321     TextDrawingModeFlags mode = context->textDrawingMode();
    322     if (strokeThickness > 0) {
    323         TextDrawingModeFlags newMode = mode | TextModeStroke;
    324         if (mode != newMode) {
    325             context->setTextDrawingMode(newMode);
    326             mode = newMode;
    327         }
    328     }
    329 
    330     if (mode & TextModeFill && (fillColor != context->fillColor() || colorSpace != context->fillColorSpace()))
    331         context->setFillColor(fillColor, colorSpace);
    332 
    333     if (mode & TextModeStroke) {
    334         if (strokeColor != context->strokeColor())
    335             context->setStrokeColor(strokeColor, colorSpace);
    336         if (strokeThickness != context->strokeThickness())
    337             context->setStrokeThickness(strokeThickness);
    338     }
    339 }
    340 
    341 bool InlineTextBox::isLineBreak() const
    342 {
    343     return renderer()->isBR() || (renderer()->style()->preserveNewline() && len() == 1 && (*textRenderer()->text())[start()] == '\n');
    344 }
    345 
    346 bool InlineTextBox::nodeAtPoint(const HitTestRequest&, HitTestResult& result, int x, int y, int tx, int ty, int /* lineTop */, int /*lineBottom*/)
    347 {
    348     if (isLineBreak())
    349         return false;
    350 
    351     FloatPoint boxOrigin = locationIncludingFlipping();
    352     boxOrigin.move(tx, ty);
    353     FloatRect rect(boxOrigin, IntSize(width(), height()));
    354     if (m_truncation != cFullTruncation && visibleToHitTesting() && rect.intersects(result.rectForPoint(x, y))) {
    355         renderer()->updateHitTestResult(result, flipForWritingMode(IntPoint(x - tx, y - ty)));
    356         if (!result.addNodeToRectBasedTestResult(renderer()->node(), x, y, rect))
    357             return true;
    358     }
    359     return false;
    360 }
    361 
    362 FloatSize InlineTextBox::applyShadowToGraphicsContext(GraphicsContext* context, const ShadowData* shadow, const FloatRect& textRect, bool stroked, bool opaque, bool horizontal)
    363 {
    364     if (!shadow)
    365         return FloatSize();
    366 
    367     FloatSize extraOffset;
    368     int shadowX = horizontal ? shadow->x() : shadow->y();
    369     int shadowY = horizontal ? shadow->y() : -shadow->x();
    370     FloatSize shadowOffset(shadowX, shadowY);
    371     int shadowBlur = shadow->blur();
    372     const Color& shadowColor = shadow->color();
    373 
    374     if (shadow->next() || stroked || !opaque) {
    375         FloatRect shadowRect(textRect);
    376         shadowRect.inflate(shadowBlur);
    377         shadowRect.move(shadowOffset);
    378         context->save();
    379         context->clip(shadowRect);
    380 
    381         extraOffset = FloatSize(0, 2 * textRect.height() + max(0.0f, shadowOffset.height()) + shadowBlur);
    382         shadowOffset -= extraOffset;
    383     }
    384 
    385     context->setShadow(shadowOffset, shadowBlur, shadowColor, context->fillColorSpace());
    386     return extraOffset;
    387 }
    388 
    389 static void paintTextWithShadows(GraphicsContext* context, const Font& font, const TextRun& textRun, const AtomicString& emphasisMark, int emphasisMarkOffset, int startOffset, int endOffset, int truncationPoint, const FloatPoint& textOrigin,
    390                                  const FloatRect& boxRect, const ShadowData* shadow, bool stroked, bool horizontal)
    391 {
    392     Color fillColor = context->fillColor();
    393     ColorSpace fillColorSpace = context->fillColorSpace();
    394     bool opaque = fillColor.alpha() == 255;
    395     if (!opaque)
    396         context->setFillColor(Color::black, fillColorSpace);
    397 
    398     do {
    399         IntSize extraOffset;
    400         if (shadow)
    401             extraOffset = roundedIntSize(InlineTextBox::applyShadowToGraphicsContext(context, shadow, boxRect, stroked, opaque, horizontal));
    402         else if (!opaque)
    403             context->setFillColor(fillColor, fillColorSpace);
    404 
    405         if (startOffset <= endOffset) {
    406             if (emphasisMark.isEmpty())
    407                 context->drawText(font, textRun, textOrigin + extraOffset, startOffset, endOffset);
    408             else
    409                 context->drawEmphasisMarks(font, textRun, emphasisMark, textOrigin + extraOffset + IntSize(0, emphasisMarkOffset), startOffset, endOffset);
    410         } else {
    411             if (endOffset > 0) {
    412                 if (emphasisMark.isEmpty())
    413                     context->drawText(font, textRun, textOrigin + extraOffset,  0, endOffset);
    414                 else
    415                     context->drawEmphasisMarks(font, textRun, emphasisMark, textOrigin + extraOffset + IntSize(0, emphasisMarkOffset),  0, endOffset);
    416             } if (startOffset < truncationPoint) {
    417                 if (emphasisMark.isEmpty())
    418                     context->drawText(font, textRun, textOrigin + extraOffset, startOffset, truncationPoint);
    419                 else
    420                     context->drawEmphasisMarks(font, textRun, emphasisMark, textOrigin + extraOffset + IntSize(0, emphasisMarkOffset),  startOffset, truncationPoint);
    421             }
    422         }
    423 
    424         if (!shadow)
    425             break;
    426 
    427         if (shadow->next() || stroked || !opaque)
    428             context->restore();
    429         else
    430             context->clearShadow();
    431 
    432         shadow = shadow->next();
    433     } while (shadow || stroked || !opaque);
    434 }
    435 
    436 bool InlineTextBox::getEmphasisMarkPosition(RenderStyle* style, TextEmphasisPosition& emphasisPosition) const
    437 {
    438     // This function returns true if there are text emphasis marks and they are suppressed by ruby text.
    439     if (style->textEmphasisMark() == TextEmphasisMarkNone)
    440         return false;
    441 
    442     emphasisPosition = style->textEmphasisPosition();
    443     if (emphasisPosition == TextEmphasisPositionUnder)
    444         return true; // Ruby text is always over, so it cannot suppress emphasis marks under.
    445 
    446     RenderBlock* containingBlock = renderer()->containingBlock();
    447     if (!containingBlock->isRubyBase())
    448         return true; // This text is not inside a ruby base, so it does not have ruby text over it.
    449 
    450     if (!containingBlock->parent()->isRubyRun())
    451         return true; // Cannot get the ruby text.
    452 
    453     RenderRubyText* rubyText = static_cast<RenderRubyRun*>(containingBlock->parent())->rubyText();
    454 
    455     // The emphasis marks over are suppressed only if there is a ruby text box and it not empty.
    456     return !rubyText || !rubyText->firstLineBox();
    457 }
    458 
    459 enum RotationDirection { Counterclockwise, Clockwise };
    460 
    461 static inline AffineTransform rotation(const FloatRect& boxRect, RotationDirection clockwise)
    462 {
    463     return clockwise ? AffineTransform(0, 1, -1, 0, boxRect.x() + boxRect.maxY(), boxRect.maxY() - boxRect.x())
    464         : AffineTransform(0, -1, 1, 0, boxRect.x() - boxRect.maxY(), boxRect.x() + boxRect.maxY());
    465 }
    466 
    467 void InlineTextBox::paint(PaintInfo& paintInfo, int tx, int ty, int /*lineTop*/, int /*lineBottom*/)
    468 {
    469     if (isLineBreak() || !paintInfo.shouldPaintWithinRoot(renderer()) || renderer()->style()->visibility() != VISIBLE ||
    470         m_truncation == cFullTruncation || paintInfo.phase == PaintPhaseOutline || !m_len)
    471         return;
    472 
    473     ASSERT(paintInfo.phase != PaintPhaseSelfOutline && paintInfo.phase != PaintPhaseChildOutlines);
    474 
    475     int logicalLeftSide = logicalLeftVisualOverflow();
    476     int logicalRightSide = logicalRightVisualOverflow();
    477     int logicalStart = logicalLeftSide + (isHorizontal() ? tx : ty);
    478     int logicalExtent = logicalRightSide - logicalLeftSide;
    479 
    480     int paintEnd = isHorizontal() ? paintInfo.rect.maxX() : paintInfo.rect.maxY();
    481     int paintStart = isHorizontal() ? paintInfo.rect.x() : paintInfo.rect.y();
    482 
    483     if (logicalStart >= paintEnd || logicalStart + logicalExtent <= paintStart)
    484         return;
    485 
    486     bool isPrinting = textRenderer()->document()->printing();
    487 
    488     // Determine whether or not we're selected.
    489     bool haveSelection = !isPrinting && paintInfo.phase != PaintPhaseTextClip && selectionState() != RenderObject::SelectionNone;
    490     if (!haveSelection && paintInfo.phase == PaintPhaseSelection)
    491         // When only painting the selection, don't bother to paint if there is none.
    492         return;
    493 
    494     if (m_truncation != cNoTruncation) {
    495         if (renderer()->containingBlock()->style()->isLeftToRightDirection() != isLeftToRightDirection()) {
    496             // Make the visible fragment of text hug the edge closest to the rest of the run by moving the origin
    497             // at which we start drawing text.
    498             // e.g. In the case of LTR text truncated in an RTL Context, the correct behavior is:
    499             // |Hello|CBA| -> |...He|CBA|
    500             // In order to draw the fragment "He" aligned to the right edge of it's box, we need to start drawing
    501             // farther to the right.
    502             // NOTE: WebKit's behavior differs from that of IE which appears to just overlay the ellipsis on top of the
    503             // truncated string i.e.  |Hello|CBA| -> |...lo|CBA|
    504             int widthOfVisibleText = toRenderText(renderer())->width(m_start, m_truncation, textPos(), m_firstLine);
    505             int widthOfHiddenText = m_logicalWidth - widthOfVisibleText;
    506             // FIXME: The hit testing logic also needs to take this translation int account.
    507             if (isHorizontal())
    508                 tx += isLeftToRightDirection() ? widthOfHiddenText : -widthOfHiddenText;
    509             else
    510                 ty += isLeftToRightDirection() ? widthOfHiddenText : -widthOfHiddenText;
    511         }
    512     }
    513 
    514     GraphicsContext* context = paintInfo.context;
    515 
    516     RenderStyle* styleToUse = renderer()->style(m_firstLine);
    517 
    518     ty -= styleToUse->isHorizontalWritingMode() ? 0 : logicalHeight();
    519 
    520     FloatPoint boxOrigin = locationIncludingFlipping();
    521     boxOrigin.move(tx, ty);
    522     FloatRect boxRect(boxOrigin, IntSize(logicalWidth(), logicalHeight()));
    523 
    524     RenderCombineText* combinedText = styleToUse->hasTextCombine() && textRenderer()->isCombineText() && toRenderCombineText(textRenderer())->isCombined() ? toRenderCombineText(textRenderer()) : 0;
    525 
    526     bool shouldRotate = !isHorizontal() && !combinedText;
    527     if (shouldRotate)
    528         context->concatCTM(rotation(boxRect, Clockwise));
    529 
    530     // Determine whether or not we have composition underlines to draw.
    531     bool containsComposition = renderer()->node() && renderer()->frame()->editor()->compositionNode() == renderer()->node();
    532     bool useCustomUnderlines = containsComposition && renderer()->frame()->editor()->compositionUsesCustomUnderlines();
    533 
    534     // Set our font.
    535     const Font& font = styleToUse->font();
    536 
    537     FloatPoint textOrigin = FloatPoint(boxOrigin.x(), boxOrigin.y() + font.fontMetrics().ascent());
    538 
    539     if (combinedText)
    540         combinedText->adjustTextOrigin(textOrigin, boxRect);
    541 
    542     // 1. Paint backgrounds behind text if needed. Examples of such backgrounds include selection
    543     // and composition underlines.
    544     if (paintInfo.phase != PaintPhaseSelection && paintInfo.phase != PaintPhaseTextClip && !isPrinting) {
    545 #if PLATFORM(MAC)
    546         // Custom highlighters go behind everything else.
    547         if (styleToUse->highlight() != nullAtom && !context->paintingDisabled())
    548             paintCustomHighlight(tx, ty, styleToUse->highlight());
    549 #endif
    550 
    551         if (containsComposition && !useCustomUnderlines)
    552             paintCompositionBackground(context, boxOrigin, styleToUse, font,
    553                 renderer()->frame()->editor()->compositionStart(),
    554                 renderer()->frame()->editor()->compositionEnd());
    555 
    556         paintDocumentMarkers(context, boxOrigin, styleToUse, font, true);
    557 
    558         if (haveSelection && !useCustomUnderlines)
    559             paintSelection(context, boxOrigin, styleToUse, font);
    560     }
    561 
    562     // 2. Now paint the foreground, including text and decorations like underline/overline (in quirks mode only).
    563     Color textFillColor;
    564     Color textStrokeColor;
    565     Color emphasisMarkColor;
    566     float textStrokeWidth = styleToUse->textStrokeWidth();
    567     const ShadowData* textShadow = paintInfo.forceBlackText ? 0 : styleToUse->textShadow();
    568 
    569     if (paintInfo.forceBlackText) {
    570         textFillColor = Color::black;
    571         textStrokeColor = Color::black;
    572         emphasisMarkColor = Color::black;
    573     } else {
    574         textFillColor = styleToUse->visitedDependentColor(CSSPropertyWebkitTextFillColor);
    575 
    576         // Make the text fill color legible against a white background
    577         if (styleToUse->forceBackgroundsToWhite())
    578             textFillColor = correctedTextColor(textFillColor, Color::white);
    579 
    580         textStrokeColor = styleToUse->visitedDependentColor(CSSPropertyWebkitTextStrokeColor);
    581 
    582         // Make the text stroke color legible against a white background
    583         if (styleToUse->forceBackgroundsToWhite())
    584             textStrokeColor = correctedTextColor(textStrokeColor, Color::white);
    585 
    586         emphasisMarkColor = styleToUse->visitedDependentColor(CSSPropertyWebkitTextEmphasisColor);
    587 
    588         // Make the text stroke color legible against a white background
    589         if (styleToUse->forceBackgroundsToWhite())
    590             emphasisMarkColor = correctedTextColor(emphasisMarkColor, Color::white);
    591     }
    592 
    593     bool paintSelectedTextOnly = (paintInfo.phase == PaintPhaseSelection);
    594     bool paintSelectedTextSeparately = false;
    595 
    596     Color selectionFillColor = textFillColor;
    597     Color selectionStrokeColor = textStrokeColor;
    598     Color selectionEmphasisMarkColor = emphasisMarkColor;
    599     float selectionStrokeWidth = textStrokeWidth;
    600     const ShadowData* selectionShadow = textShadow;
    601     if (haveSelection) {
    602         // Check foreground color first.
    603         Color foreground = paintInfo.forceBlackText ? Color::black : renderer()->selectionForegroundColor();
    604         if (foreground.isValid() && foreground != selectionFillColor) {
    605             if (!paintSelectedTextOnly)
    606                 paintSelectedTextSeparately = true;
    607             selectionFillColor = foreground;
    608         }
    609 
    610         Color emphasisMarkForeground = paintInfo.forceBlackText ? Color::black : renderer()->selectionEmphasisMarkColor();
    611         if (emphasisMarkForeground.isValid() && emphasisMarkForeground != selectionEmphasisMarkColor) {
    612             if (!paintSelectedTextOnly)
    613                 paintSelectedTextSeparately = true;
    614             selectionEmphasisMarkColor = emphasisMarkForeground;
    615         }
    616 
    617         if (RenderStyle* pseudoStyle = renderer()->getCachedPseudoStyle(SELECTION)) {
    618             const ShadowData* shadow = paintInfo.forceBlackText ? 0 : pseudoStyle->textShadow();
    619             if (shadow != selectionShadow) {
    620                 if (!paintSelectedTextOnly)
    621                     paintSelectedTextSeparately = true;
    622                 selectionShadow = shadow;
    623             }
    624 
    625             float strokeWidth = pseudoStyle->textStrokeWidth();
    626             if (strokeWidth != selectionStrokeWidth) {
    627                 if (!paintSelectedTextOnly)
    628                     paintSelectedTextSeparately = true;
    629                 selectionStrokeWidth = strokeWidth;
    630             }
    631 
    632             Color stroke = paintInfo.forceBlackText ? Color::black : pseudoStyle->visitedDependentColor(CSSPropertyWebkitTextStrokeColor);
    633             if (stroke != selectionStrokeColor) {
    634                 if (!paintSelectedTextOnly)
    635                     paintSelectedTextSeparately = true;
    636                 selectionStrokeColor = stroke;
    637             }
    638         }
    639     }
    640 
    641     int length = m_len;
    642     const UChar* characters;
    643     if (!combinedText)
    644         characters = textRenderer()->text()->characters() + m_start;
    645     else
    646         combinedText->charactersToRender(m_start, characters, length);
    647 
    648     BufferForAppendingHyphen charactersWithHyphen;
    649     if (hasHyphen())
    650         adjustCharactersAndLengthForHyphen(charactersWithHyphen, styleToUse, characters, length);
    651 
    652     TextRun textRun(characters, length, textRenderer()->allowTabs(), textPos(), m_expansion, expansionBehavior(), !isLeftToRightDirection(), m_dirOverride || styleToUse->visuallyOrdered());
    653 
    654     int sPos = 0;
    655     int ePos = 0;
    656     if (paintSelectedTextOnly || paintSelectedTextSeparately)
    657         selectionStartEnd(sPos, ePos);
    658 
    659     if (m_truncation != cNoTruncation) {
    660         sPos = min<int>(sPos, m_truncation);
    661         ePos = min<int>(ePos, m_truncation);
    662         length = m_truncation;
    663     }
    664 
    665     int emphasisMarkOffset = 0;
    666     TextEmphasisPosition emphasisMarkPosition;
    667     bool hasTextEmphasis = getEmphasisMarkPosition(styleToUse, emphasisMarkPosition);
    668     const AtomicString& emphasisMark = hasTextEmphasis ? styleToUse->textEmphasisMarkString() : nullAtom;
    669     if (!emphasisMark.isEmpty())
    670         emphasisMarkOffset = emphasisMarkPosition == TextEmphasisPositionOver ? -font.fontMetrics().ascent() - font.emphasisMarkDescent(emphasisMark) : font.fontMetrics().descent() + font.emphasisMarkAscent(emphasisMark);
    671 
    672     if (!paintSelectedTextOnly) {
    673         // For stroked painting, we have to change the text drawing mode.  It's probably dangerous to leave that mutated as a side
    674         // effect, so only when we know we're stroking, do a save/restore.
    675         if (textStrokeWidth > 0)
    676             context->save();
    677 
    678         updateGraphicsContext(context, textFillColor, textStrokeColor, textStrokeWidth, styleToUse->colorSpace());
    679         if (!paintSelectedTextSeparately || ePos <= sPos) {
    680             // FIXME: Truncate right-to-left text correctly.
    681             paintTextWithShadows(context, font, textRun, nullAtom, 0, 0, length, length, textOrigin, boxRect, textShadow, textStrokeWidth > 0, isHorizontal());
    682         } else
    683             paintTextWithShadows(context, font, textRun, nullAtom, 0, ePos, sPos, length, textOrigin, boxRect, textShadow, textStrokeWidth > 0, isHorizontal());
    684 
    685         if (!emphasisMark.isEmpty()) {
    686             updateGraphicsContext(context, emphasisMarkColor, textStrokeColor, textStrokeWidth, styleToUse->colorSpace());
    687 
    688             static TextRun objectReplacementCharacterTextRun(&objectReplacementCharacter, 1);
    689             TextRun& emphasisMarkTextRun = combinedText ? objectReplacementCharacterTextRun : textRun;
    690             FloatPoint emphasisMarkTextOrigin = combinedText ? FloatPoint(boxOrigin.x() + boxRect.width() / 2, boxOrigin.y() + font.fontMetrics().ascent()) : textOrigin;
    691             if (combinedText)
    692                 context->concatCTM(rotation(boxRect, Clockwise));
    693 
    694             if (!paintSelectedTextSeparately || ePos <= sPos) {
    695                 // FIXME: Truncate right-to-left text correctly.
    696                 paintTextWithShadows(context, combinedText ? combinedText->originalFont() : font, emphasisMarkTextRun, emphasisMark, emphasisMarkOffset, 0, length, length, emphasisMarkTextOrigin, boxRect, textShadow, textStrokeWidth > 0, isHorizontal());
    697             } else
    698                 paintTextWithShadows(context, combinedText ? combinedText->originalFont() : font, emphasisMarkTextRun, emphasisMark, emphasisMarkOffset, ePos, sPos, length, emphasisMarkTextOrigin, boxRect, textShadow, textStrokeWidth > 0, isHorizontal());
    699 
    700             if (combinedText)
    701                 context->concatCTM(rotation(boxRect, Counterclockwise));
    702         }
    703 
    704         if (textStrokeWidth > 0)
    705             context->restore();
    706     }
    707 
    708     if ((paintSelectedTextOnly || paintSelectedTextSeparately) && sPos < ePos) {
    709         // paint only the text that is selected
    710         if (selectionStrokeWidth > 0)
    711             context->save();
    712 
    713         updateGraphicsContext(context, selectionFillColor, selectionStrokeColor, selectionStrokeWidth, styleToUse->colorSpace());
    714         paintTextWithShadows(context, font, textRun, nullAtom, 0, sPos, ePos, length, textOrigin, boxRect, selectionShadow, selectionStrokeWidth > 0, isHorizontal());
    715         if (!emphasisMark.isEmpty()) {
    716             updateGraphicsContext(context, selectionEmphasisMarkColor, textStrokeColor, textStrokeWidth, styleToUse->colorSpace());
    717 
    718             static TextRun objectReplacementCharacterTextRun(&objectReplacementCharacter, 1);
    719             TextRun& emphasisMarkTextRun = combinedText ? objectReplacementCharacterTextRun : textRun;
    720             FloatPoint emphasisMarkTextOrigin = combinedText ? FloatPoint(boxOrigin.x() + boxRect.width() / 2, boxOrigin.y() + font.fontMetrics().ascent()) : textOrigin;
    721             if (combinedText)
    722                 context->concatCTM(rotation(boxRect, Clockwise));
    723 
    724             paintTextWithShadows(context, combinedText ? combinedText->originalFont() : font, emphasisMarkTextRun, emphasisMark, emphasisMarkOffset, sPos, ePos, length, emphasisMarkTextOrigin, boxRect, selectionShadow, selectionStrokeWidth > 0, isHorizontal());
    725 
    726             if (combinedText)
    727                 context->concatCTM(rotation(boxRect, Counterclockwise));
    728         }
    729         if (selectionStrokeWidth > 0)
    730             context->restore();
    731     }
    732 
    733     // Paint decorations
    734     int textDecorations = styleToUse->textDecorationsInEffect();
    735     if (textDecorations != TDNONE && paintInfo.phase != PaintPhaseSelection) {
    736         updateGraphicsContext(context, textFillColor, textStrokeColor, textStrokeWidth, styleToUse->colorSpace());
    737         paintDecoration(context, boxOrigin, textDecorations, textShadow);
    738     }
    739 
    740     if (paintInfo.phase == PaintPhaseForeground) {
    741         paintDocumentMarkers(context, boxOrigin, styleToUse, font, false);
    742 
    743         if (useCustomUnderlines) {
    744             const Vector<CompositionUnderline>& underlines = renderer()->frame()->editor()->customCompositionUnderlines();
    745             size_t numUnderlines = underlines.size();
    746 
    747             for (size_t index = 0; index < numUnderlines; ++index) {
    748                 const CompositionUnderline& underline = underlines[index];
    749 
    750                 if (underline.endOffset <= start())
    751                     // underline is completely before this run.  This might be an underline that sits
    752                     // before the first run we draw, or underlines that were within runs we skipped
    753                     // due to truncation.
    754                     continue;
    755 
    756                 if (underline.startOffset <= end()) {
    757                     // underline intersects this run.  Paint it.
    758                     paintCompositionUnderline(context, boxOrigin, underline);
    759                     if (underline.endOffset > end() + 1)
    760                         // underline also runs into the next run. Bail now, no more marker advancement.
    761                         break;
    762                 } else
    763                     // underline is completely after this run, bail.  A later run will paint it.
    764                     break;
    765             }
    766         }
    767     }
    768 
    769     if (shouldRotate)
    770         context->concatCTM(rotation(boxRect, Counterclockwise));
    771 }
    772 
    773 void InlineTextBox::selectionStartEnd(int& sPos, int& ePos)
    774 {
    775     int startPos, endPos;
    776     if (renderer()->selectionState() == RenderObject::SelectionInside) {
    777         startPos = 0;
    778         endPos = textRenderer()->textLength();
    779     } else {
    780         textRenderer()->selectionStartEnd(startPos, endPos);
    781         if (renderer()->selectionState() == RenderObject::SelectionStart)
    782             endPos = textRenderer()->textLength();
    783         else if (renderer()->selectionState() == RenderObject::SelectionEnd)
    784             startPos = 0;
    785     }
    786 
    787     sPos = max(startPos - m_start, 0);
    788     ePos = min(endPos - m_start, (int)m_len);
    789 }
    790 
    791 void InlineTextBox::paintSelection(GraphicsContext* context, const FloatPoint& boxOrigin, RenderStyle* style, const Font& font)
    792 {
    793     // See if we have a selection to paint at all.
    794     int sPos, ePos;
    795     selectionStartEnd(sPos, ePos);
    796     if (sPos >= ePos)
    797         return;
    798 
    799     Color textColor = style->visitedDependentColor(CSSPropertyColor);
    800     Color c = renderer()->selectionBackgroundColor();
    801     if (!c.isValid() || c.alpha() == 0)
    802         return;
    803 
    804     // If the text color ends up being the same as the selection background, invert the selection
    805     // background.  This should basically never happen, since the selection has transparency.
    806     if (textColor == c)
    807         c = Color(0xff - c.red(), 0xff - c.green(), 0xff - c.blue());
    808 
    809     context->save();
    810     updateGraphicsContext(context, c, c, 0, style->colorSpace());  // Don't draw text at all!
    811 
    812     // If the text is truncated, let the thing being painted in the truncation
    813     // draw its own highlight.
    814     int length = m_truncation != cNoTruncation ? m_truncation : m_len;
    815     const UChar* characters = textRenderer()->text()->characters() + m_start;
    816 
    817     BufferForAppendingHyphen charactersWithHyphen;
    818     if (ePos == length && hasHyphen()) {
    819         adjustCharactersAndLengthForHyphen(charactersWithHyphen, style, characters, length);
    820         ePos = length;
    821     }
    822 
    823     int deltaY = renderer()->style()->isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
    824     int selHeight = selectionHeight();
    825     FloatPoint localOrigin(boxOrigin.x(), boxOrigin.y() - deltaY);
    826     context->clip(FloatRect(localOrigin, FloatSize(m_logicalWidth, selHeight)));
    827     context->drawHighlightForText(font, TextRun(characters, length, textRenderer()->allowTabs(), textPos(), m_expansion, expansionBehavior(),
    828                                   !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered()),
    829                                   localOrigin, selHeight, c, style->colorSpace(), sPos, ePos);
    830     context->restore();
    831 }
    832 
    833 void InlineTextBox::paintCompositionBackground(GraphicsContext* context, const FloatPoint& boxOrigin, RenderStyle* style, const Font& font, int startPos, int endPos)
    834 {
    835     int offset = m_start;
    836     int sPos = max(startPos - offset, 0);
    837     int ePos = min(endPos - offset, (int)m_len);
    838 
    839     if (sPos >= ePos)
    840         return;
    841 
    842     context->save();
    843 
    844     Color c = Color(225, 221, 85);
    845 
    846     updateGraphicsContext(context, c, c, 0, style->colorSpace()); // Don't draw text at all!
    847 
    848     int deltaY = renderer()->style()->isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
    849     int selHeight = selectionHeight();
    850     FloatPoint localOrigin(boxOrigin.x(), boxOrigin.y() - deltaY);
    851     context->drawHighlightForText(font, TextRun(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_expansion, expansionBehavior(),
    852                                   !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered()),
    853                                   localOrigin, selHeight, c, style->colorSpace(), sPos, ePos);
    854     context->restore();
    855 }
    856 
    857 #if PLATFORM(MAC)
    858 
    859 void InlineTextBox::paintCustomHighlight(int tx, int ty, const AtomicString& type)
    860 {
    861     Frame* frame = renderer()->frame();
    862     if (!frame)
    863         return;
    864     Page* page = frame->page();
    865     if (!page)
    866         return;
    867 
    868     RootInlineBox* r = root();
    869     FloatRect rootRect(tx + r->x(), ty + selectionTop(), r->logicalWidth(), selectionHeight());
    870     FloatRect textRect(tx + x(), rootRect.y(), logicalWidth(), rootRect.height());
    871 
    872     page->chrome()->client()->paintCustomHighlight(renderer()->node(), type, textRect, rootRect, true, false);
    873 }
    874 
    875 #endif
    876 
    877 void InlineTextBox::paintDecoration(GraphicsContext* context, const FloatPoint& boxOrigin, int deco, const ShadowData* shadow)
    878 {
    879     if (m_truncation == cFullTruncation)
    880         return;
    881 
    882     FloatPoint localOrigin = boxOrigin;
    883 
    884     float width = m_logicalWidth;
    885     if (m_truncation != cNoTruncation) {
    886         width = toRenderText(renderer())->width(m_start, m_truncation, textPos(), m_firstLine);
    887         if (!isLeftToRightDirection())
    888             localOrigin.move(m_logicalWidth - width, 0);
    889     }
    890 
    891     // Get the text decoration colors.
    892     Color underline, overline, linethrough;
    893     renderer()->getTextDecorationColors(deco, underline, overline, linethrough, true);
    894 
    895     // Use a special function for underlines to get the positioning exactly right.
    896     bool isPrinting = textRenderer()->document()->printing();
    897     context->setStrokeThickness(1.0f); // FIXME: We should improve this rule and not always just assume 1.
    898 
    899     bool linesAreOpaque = !isPrinting && (!(deco & UNDERLINE) || underline.alpha() == 255) && (!(deco & OVERLINE) || overline.alpha() == 255) && (!(deco & LINE_THROUGH) || linethrough.alpha() == 255);
    900 
    901     RenderStyle* styleToUse = renderer()->style(m_firstLine);
    902     int baseline = styleToUse->fontMetrics().ascent();
    903 
    904     bool setClip = false;
    905     int extraOffset = 0;
    906     if (!linesAreOpaque && shadow && shadow->next()) {
    907         context->save();
    908         FloatRect clipRect(localOrigin, FloatSize(width, baseline + 2));
    909         for (const ShadowData* s = shadow; s; s = s->next()) {
    910             FloatRect shadowRect(localOrigin, FloatSize(width, baseline + 2));
    911             shadowRect.inflate(s->blur());
    912             int shadowX = isHorizontal() ? s->x() : s->y();
    913             int shadowY = isHorizontal() ? s->y() : -s->x();
    914             shadowRect.move(shadowX, shadowY);
    915             clipRect.unite(shadowRect);
    916             extraOffset = max(extraOffset, max(0, shadowY) + s->blur());
    917         }
    918         context->save();
    919         context->clip(clipRect);
    920         extraOffset += baseline + 2;
    921         localOrigin.move(0, extraOffset);
    922         setClip = true;
    923     }
    924 
    925     ColorSpace colorSpace = renderer()->style()->colorSpace();
    926     bool setShadow = false;
    927 
    928     do {
    929         if (shadow) {
    930             if (!shadow->next()) {
    931                 // The last set of lines paints normally inside the clip.
    932                 localOrigin.move(0, -extraOffset);
    933                 extraOffset = 0;
    934             }
    935             int shadowX = isHorizontal() ? shadow->x() : shadow->y();
    936             int shadowY = isHorizontal() ? shadow->y() : -shadow->x();
    937             context->setShadow(FloatSize(shadowX, shadowY - extraOffset), shadow->blur(), shadow->color(), colorSpace);
    938             setShadow = true;
    939             shadow = shadow->next();
    940         }
    941 
    942         if (deco & UNDERLINE) {
    943             context->setStrokeColor(underline, colorSpace);
    944             context->setStrokeStyle(SolidStroke);
    945             // Leave one pixel of white between the baseline and the underline.
    946             context->drawLineForText(FloatPoint(localOrigin.x(), localOrigin.y() + baseline + 1), width, isPrinting);
    947         }
    948         if (deco & OVERLINE) {
    949             context->setStrokeColor(overline, colorSpace);
    950             context->setStrokeStyle(SolidStroke);
    951             context->drawLineForText(localOrigin, width, isPrinting);
    952         }
    953         if (deco & LINE_THROUGH) {
    954             context->setStrokeColor(linethrough, colorSpace);
    955             context->setStrokeStyle(SolidStroke);
    956             context->drawLineForText(FloatPoint(localOrigin.x(), localOrigin.y() + 2 * baseline / 3), width, isPrinting);
    957         }
    958     } while (shadow);
    959 
    960     if (setClip)
    961         context->restore();
    962     else if (setShadow)
    963         context->clearShadow();
    964 }
    965 
    966 static GraphicsContext::TextCheckingLineStyle textCheckingLineStyleForMarkerType(DocumentMarker::MarkerType markerType)
    967 {
    968     switch (markerType) {
    969     case DocumentMarker::Spelling:
    970         return GraphicsContext::TextCheckingSpellingLineStyle;
    971     case DocumentMarker::Grammar:
    972         return GraphicsContext::TextCheckingGrammarLineStyle;
    973     case DocumentMarker::CorrectionIndicator:
    974         return GraphicsContext::TextCheckingReplacementLineStyle;
    975     default:
    976         ASSERT_NOT_REACHED();
    977         return GraphicsContext::TextCheckingSpellingLineStyle;
    978     }
    979 }
    980 
    981 void InlineTextBox::paintSpellingOrGrammarMarker(GraphicsContext* pt, const FloatPoint& boxOrigin, const DocumentMarker& marker, RenderStyle* style, const Font& font, bool grammar)
    982 {
    983     // Never print spelling/grammar markers (5327887)
    984     if (textRenderer()->document()->printing())
    985         return;
    986 
    987     if (m_truncation == cFullTruncation)
    988         return;
    989 
    990     float start = 0; // start of line to draw, relative to tx
    991     float width = m_logicalWidth; // how much line to draw
    992 
    993     // Determine whether we need to measure text
    994     bool markerSpansWholeBox = true;
    995     if (m_start <= (int)marker.startOffset)
    996         markerSpansWholeBox = false;
    997     if ((end() + 1) != marker.endOffset)      // end points at the last char, not past it
    998         markerSpansWholeBox = false;
    999     if (m_truncation != cNoTruncation)
   1000         markerSpansWholeBox = false;
   1001 
   1002     if (!markerSpansWholeBox || grammar) {
   1003         int startPosition = max<int>(marker.startOffset - m_start, 0);
   1004         int endPosition = min<int>(marker.endOffset - m_start, m_len);
   1005 
   1006         if (m_truncation != cNoTruncation)
   1007             endPosition = min<int>(endPosition, m_truncation);
   1008 
   1009         // Calculate start & width
   1010         int deltaY = renderer()->style()->isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
   1011         int selHeight = selectionHeight();
   1012         FloatPoint startPoint(boxOrigin.x(), boxOrigin.y() - deltaY);
   1013         TextRun run(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_expansion, expansionBehavior(), !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered());
   1014 
   1015         // FIXME: Convert the document markers to float rects.
   1016         IntRect markerRect = enclosingIntRect(font.selectionRectForText(run, startPoint, selHeight, startPosition, endPosition));
   1017         start = markerRect.x() - startPoint.x();
   1018         width = markerRect.width();
   1019 
   1020         // Store rendered rects for bad grammar markers, so we can hit-test against it elsewhere in order to
   1021         // display a toolTip. We don't do this for misspelling markers.
   1022         if (grammar) {
   1023             markerRect.move(-boxOrigin.x(), -boxOrigin.y());
   1024             markerRect = renderer()->localToAbsoluteQuad(FloatRect(markerRect)).enclosingBoundingBox();
   1025             renderer()->document()->markers()->setRenderedRectForMarker(renderer()->node(), marker, markerRect);
   1026         }
   1027     }
   1028 
   1029     // IMPORTANT: The misspelling underline is not considered when calculating the text bounds, so we have to
   1030     // make sure to fit within those bounds.  This means the top pixel(s) of the underline will overlap the
   1031     // bottom pixel(s) of the glyphs in smaller font sizes.  The alternatives are to increase the line spacing (bad!!)
   1032     // or decrease the underline thickness.  The overlap is actually the most useful, and matches what AppKit does.
   1033     // So, we generally place the underline at the bottom of the text, but in larger fonts that's not so good so
   1034     // we pin to two pixels under the baseline.
   1035     int lineThickness = cMisspellingLineThickness;
   1036     int baseline = renderer()->style(m_firstLine)->fontMetrics().ascent();
   1037     int descent = logicalHeight() - baseline;
   1038     int underlineOffset;
   1039     if (descent <= (2 + lineThickness)) {
   1040         // Place the underline at the very bottom of the text in small/medium fonts.
   1041         underlineOffset = logicalHeight() - lineThickness;
   1042     } else {
   1043         // In larger fonts, though, place the underline up near the baseline to prevent a big gap.
   1044         underlineOffset = baseline + 2;
   1045     }
   1046     pt->drawLineForTextChecking(FloatPoint(boxOrigin.x() + start, boxOrigin.y() + underlineOffset), width, textCheckingLineStyleForMarkerType(marker.type));
   1047 }
   1048 
   1049 void InlineTextBox::paintTextMatchMarker(GraphicsContext* pt, const FloatPoint& boxOrigin, const DocumentMarker& marker, RenderStyle* style, const Font& font)
   1050 {
   1051     // Use same y positioning and height as for selection, so that when the selection and this highlight are on
   1052     // the same word there are no pieces sticking out.
   1053     int deltaY = renderer()->style()->isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
   1054     int selHeight = selectionHeight();
   1055 
   1056     int sPos = max(marker.startOffset - m_start, (unsigned)0);
   1057     int ePos = min(marker.endOffset - m_start, (unsigned)m_len);
   1058     TextRun run(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_expansion, expansionBehavior(), !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered());
   1059 
   1060     // Always compute and store the rect associated with this marker. The computed rect is in absolute coordinates.
   1061     IntRect markerRect = enclosingIntRect(font.selectionRectForText(run, IntPoint(m_x, selectionTop()), selHeight, sPos, ePos));
   1062     markerRect = renderer()->localToAbsoluteQuad(FloatRect(markerRect)).enclosingBoundingBox();
   1063     renderer()->document()->markers()->setRenderedRectForMarker(renderer()->node(), marker, markerRect);
   1064 
   1065     // Optionally highlight the text
   1066     if (renderer()->frame()->editor()->markedTextMatchesAreHighlighted()) {
   1067         Color color = marker.activeMatch ?
   1068             renderer()->theme()->platformActiveTextSearchHighlightColor() :
   1069             renderer()->theme()->platformInactiveTextSearchHighlightColor();
   1070         pt->save();
   1071         updateGraphicsContext(pt, color, color, 0, style->colorSpace());  // Don't draw text at all!
   1072         pt->clip(FloatRect(boxOrigin.x(), boxOrigin.y() - deltaY, m_logicalWidth, selHeight));
   1073         pt->drawHighlightForText(font, run, FloatPoint(boxOrigin.x(), boxOrigin.y() - deltaY), selHeight, color, style->colorSpace(), sPos, ePos);
   1074         pt->restore();
   1075     }
   1076 }
   1077 
   1078 void InlineTextBox::computeRectForReplacementMarker(const DocumentMarker& marker, RenderStyle* style, const Font& font)
   1079 {
   1080     // Replacement markers are not actually drawn, but their rects need to be computed for hit testing.
   1081     int y = selectionTop();
   1082     int h = selectionHeight();
   1083 
   1084     int sPos = max(marker.startOffset - m_start, (unsigned)0);
   1085     int ePos = min(marker.endOffset - m_start, (unsigned)m_len);
   1086     TextRun run(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_expansion, expansionBehavior(), !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered());
   1087     IntPoint startPoint = IntPoint(m_x, y);
   1088 
   1089     // Compute and store the rect associated with this marker.
   1090     IntRect markerRect = enclosingIntRect(font.selectionRectForText(run, startPoint, h, sPos, ePos));
   1091     markerRect = renderer()->localToAbsoluteQuad(FloatRect(markerRect)).enclosingBoundingBox();
   1092     renderer()->document()->markers()->setRenderedRectForMarker(renderer()->node(), marker, markerRect);
   1093 }
   1094 
   1095 void InlineTextBox::paintDocumentMarkers(GraphicsContext* pt, const FloatPoint& boxOrigin, RenderStyle* style, const Font& font, bool background)
   1096 {
   1097     if (!renderer()->node())
   1098         return;
   1099 
   1100     Vector<DocumentMarker> markers = renderer()->document()->markers()->markersForNode(renderer()->node());
   1101     Vector<DocumentMarker>::iterator markerIt = markers.begin();
   1102 
   1103     // Give any document markers that touch this run a chance to draw before the text has been drawn.
   1104     // Note end() points at the last char, not one past it like endOffset and ranges do.
   1105     for ( ; markerIt != markers.end(); markerIt++) {
   1106         const DocumentMarker& marker = *markerIt;
   1107 
   1108         // Paint either the background markers or the foreground markers, but not both
   1109         switch (marker.type) {
   1110             case DocumentMarker::Grammar:
   1111             case DocumentMarker::Spelling:
   1112             case DocumentMarker::CorrectionIndicator:
   1113             case DocumentMarker::Replacement:
   1114                 if (background)
   1115                     continue;
   1116                 break;
   1117             case DocumentMarker::TextMatch:
   1118                 if (!background)
   1119                     continue;
   1120                 break;
   1121             default:
   1122                 continue;
   1123         }
   1124 
   1125         if (marker.endOffset <= start())
   1126             // marker is completely before this run.  This might be a marker that sits before the
   1127             // first run we draw, or markers that were within runs we skipped due to truncation.
   1128             continue;
   1129 
   1130         if (marker.startOffset > end())
   1131             // marker is completely after this run, bail.  A later run will paint it.
   1132             break;
   1133 
   1134         // marker intersects this run.  Paint it.
   1135         switch (marker.type) {
   1136             case DocumentMarker::Spelling:
   1137                 paintSpellingOrGrammarMarker(pt, boxOrigin, marker, style, font, false);
   1138                 break;
   1139             case DocumentMarker::Grammar:
   1140                 paintSpellingOrGrammarMarker(pt, boxOrigin, marker, style, font, true);
   1141                 break;
   1142             case DocumentMarker::TextMatch:
   1143                 paintTextMatchMarker(pt, boxOrigin, marker, style, font);
   1144                 break;
   1145             case DocumentMarker::CorrectionIndicator:
   1146                 paintSpellingOrGrammarMarker(pt, boxOrigin, marker, style, font, false);
   1147                 break;
   1148             case DocumentMarker::Replacement:
   1149                 computeRectForReplacementMarker(marker, style, font);
   1150                 break;
   1151             default:
   1152                 ASSERT_NOT_REACHED();
   1153         }
   1154 
   1155     }
   1156 }
   1157 
   1158 void InlineTextBox::paintCompositionUnderline(GraphicsContext* ctx, const FloatPoint& boxOrigin, const CompositionUnderline& underline)
   1159 {
   1160     if (m_truncation == cFullTruncation)
   1161         return;
   1162 
   1163     float start = 0; // start of line to draw, relative to tx
   1164     float width = m_logicalWidth; // how much line to draw
   1165     bool useWholeWidth = true;
   1166     unsigned paintStart = m_start;
   1167     unsigned paintEnd = end() + 1; // end points at the last char, not past it
   1168     if (paintStart <= underline.startOffset) {
   1169         paintStart = underline.startOffset;
   1170         useWholeWidth = false;
   1171         start = toRenderText(renderer())->width(m_start, paintStart - m_start, textPos(), m_firstLine);
   1172     }
   1173     if (paintEnd != underline.endOffset) {      // end points at the last char, not past it
   1174         paintEnd = min(paintEnd, (unsigned)underline.endOffset);
   1175         useWholeWidth = false;
   1176     }
   1177     if (m_truncation != cNoTruncation) {
   1178         paintEnd = min(paintEnd, (unsigned)m_start + m_truncation);
   1179         useWholeWidth = false;
   1180     }
   1181     if (!useWholeWidth) {
   1182         width = toRenderText(renderer())->width(paintStart, paintEnd - paintStart, textPos() + start, m_firstLine);
   1183     }
   1184 
   1185     // Thick marked text underlines are 2px thick as long as there is room for the 2px line under the baseline.
   1186     // All other marked text underlines are 1px thick.
   1187     // If there's not enough space the underline will touch or overlap characters.
   1188     int lineThickness = 1;
   1189     int baseline = renderer()->style(m_firstLine)->fontMetrics().ascent();
   1190     if (underline.thick && logicalHeight() - baseline >= 2)
   1191         lineThickness = 2;
   1192 
   1193     // We need to have some space between underlines of subsequent clauses, because some input methods do not use different underline styles for those.
   1194     // We make each line shorter, which has a harmless side effect of shortening the first and last clauses, too.
   1195     start += 1;
   1196     width -= 2;
   1197 
   1198     ctx->setStrokeColor(underline.color, renderer()->style()->colorSpace());
   1199     ctx->setStrokeThickness(lineThickness);
   1200     ctx->drawLineForText(FloatPoint(boxOrigin.x() + start, boxOrigin.y() + logicalHeight() - lineThickness), width, textRenderer()->document()->printing());
   1201 }
   1202 
   1203 int InlineTextBox::caretMinOffset() const
   1204 {
   1205     return m_start;
   1206 }
   1207 
   1208 int InlineTextBox::caretMaxOffset() const
   1209 {
   1210     return m_start + m_len;
   1211 }
   1212 
   1213 unsigned InlineTextBox::caretMaxRenderedOffset() const
   1214 {
   1215     return m_start + m_len;
   1216 }
   1217 
   1218 float InlineTextBox::textPos() const
   1219 {
   1220     // When computing the width of a text run, RenderBlock::computeInlineDirectionPositionsForLine() doesn't include the actual offset
   1221     // from the containing block edge in its measurement. textPos() should be consistent so the text are rendered in the same width.
   1222     if (logicalLeft() == 0)
   1223         return 0;
   1224     return logicalLeft() - root()->logicalLeft();
   1225 }
   1226 
   1227 int InlineTextBox::offsetForPosition(float lineOffset, bool includePartialGlyphs) const
   1228 {
   1229     if (isLineBreak())
   1230         return 0;
   1231 
   1232     int leftOffset = isLeftToRightDirection() ? 0 : m_len;
   1233     int rightOffset = isLeftToRightDirection() ? m_len : 0;
   1234     bool blockIsInOppositeDirection = renderer()->containingBlock()->style()->isLeftToRightDirection() != isLeftToRightDirection();
   1235     if (blockIsInOppositeDirection)
   1236         swap(leftOffset, rightOffset);
   1237 
   1238     if (lineOffset - logicalLeft() > logicalWidth())
   1239         return rightOffset;
   1240     if (lineOffset - logicalLeft() < 0)
   1241         return leftOffset;
   1242 
   1243     RenderText* text = toRenderText(renderer());
   1244     RenderStyle* style = text->style(m_firstLine);
   1245     const Font* f = &style->font();
   1246     int offset = f->offsetForPosition(TextRun(textRenderer()->text()->characters() + m_start, m_len,
   1247         textRenderer()->allowTabs(), textPos(), m_expansion, expansionBehavior(), !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered()),
   1248         lineOffset - logicalLeft(), includePartialGlyphs);
   1249     if (blockIsInOppositeDirection && (!offset || offset == m_len))
   1250         return !offset ? m_len : 0;
   1251     return offset;
   1252 }
   1253 
   1254 float InlineTextBox::positionForOffset(int offset) const
   1255 {
   1256     ASSERT(offset >= m_start);
   1257     ASSERT(offset <= m_start + m_len);
   1258 
   1259     if (isLineBreak())
   1260         return logicalLeft();
   1261 
   1262     RenderText* text = toRenderText(renderer());
   1263     const Font& f = text->style(m_firstLine)->font();
   1264     int from = !isLeftToRightDirection() ? offset - m_start : 0;
   1265     int to = !isLeftToRightDirection() ? m_len : offset - m_start;
   1266     // FIXME: Do we need to add rightBearing here?
   1267     return f.selectionRectForText(TextRun(text->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_expansion, expansionBehavior(), !isLeftToRightDirection(), m_dirOverride),
   1268                                   IntPoint(logicalLeft(), 0), 0, from, to).maxX();
   1269 }
   1270 
   1271 bool InlineTextBox::containsCaretOffset(int offset) const
   1272 {
   1273     // Offsets before the box are never "in".
   1274     if (offset < m_start)
   1275         return false;
   1276 
   1277     int pastEnd = m_start + m_len;
   1278 
   1279     // Offsets inside the box (not at either edge) are always "in".
   1280     if (offset < pastEnd)
   1281         return true;
   1282 
   1283     // Offsets outside the box are always "out".
   1284     if (offset > pastEnd)
   1285         return false;
   1286 
   1287     // Offsets at the end are "out" for line breaks (they are on the next line).
   1288     if (isLineBreak())
   1289         return false;
   1290 
   1291     // Offsets at the end are "in" for normal boxes (but the caller has to check affinity).
   1292     return true;
   1293 }
   1294 
   1295 } // namespace WebCore
   1296