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