OSDN Git Service

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