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