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