ca67370c6b7db555f1c44dcab13f9df4f5ee7315
[WebKit-https.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 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 "Document.h"
27 #include "Editor.h"
28 #include "Frame.h"
29 #include "GraphicsContext.h"
30 #include "HitTestResult.h"
31 #include "RenderArena.h"
32 #include "RenderBlock.h"
33 #include "RenderTheme.h"
34 #include "Text.h"
35 #include "FontStyle.h"
36 #include "break_lines.h"
37 #include <wtf/AlwaysInline.h>
38
39 using namespace std;
40
41 namespace WebCore {
42
43 int InlineTextBox::selectionTop()
44 {
45     return root()->selectionTop();
46 }
47
48 int InlineTextBox::selectionHeight()
49 {
50     return root()->selectionHeight();
51 }
52
53 bool InlineTextBox::isSelected(int startPos, int endPos) const
54 {
55     int sPos = max(startPos - m_start, 0);
56     int ePos = min(endPos - m_start, (int)m_len);
57     return (sPos < ePos);
58 }
59
60 RenderObject::SelectionState InlineTextBox::selectionState()
61 {
62     RenderObject::SelectionState state = object()->selectionState();
63     if (state == RenderObject::SelectionStart || state == RenderObject::SelectionEnd || state == RenderObject::SelectionBoth) {
64         int startPos, endPos;
65         object()->selectionStartEnd(startPos, endPos);
66         // The position after a hard line break is considered to be past its end.
67         int lastSelectable = start() + len() - (isLineBreak() ? 1 : 0);
68
69         bool start = (state != RenderObject::SelectionEnd && startPos >= m_start && startPos < m_start + m_len);
70         bool end = (state != RenderObject::SelectionStart && endPos > m_start && endPos <= lastSelectable);
71         if (start && end)
72             state = RenderObject::SelectionBoth;
73         else if (start)
74             state = RenderObject::SelectionStart;
75         else if (end)
76             state = RenderObject::SelectionEnd;
77         else if ((state == RenderObject::SelectionEnd || startPos < m_start) &&
78                  (state == RenderObject::SelectionStart || endPos > lastSelectable))
79             state = RenderObject::SelectionInside;
80         else if (state == RenderObject::SelectionBoth)
81             state = RenderObject::SelectionNone;
82     }
83     return state;
84 }
85
86 IntRect InlineTextBox::selectionRect(int tx, int ty, int startPos, int endPos)
87 {
88     int sPos = max(startPos - m_start, 0);
89     int ePos = min(endPos - m_start, (int)m_len);
90     
91     if (sPos >= ePos)
92         return IntRect();
93
94     RenderText* textObj = textObject();
95     int selTop = selectionTop();
96     int selHeight = selectionHeight();
97     const Font& f = textObj->style(m_firstLine)->font();
98
99     IntRect r = enclosingIntRect(f.selectionRectForText(TextRun(textObj->text()->characters() + m_start, m_len),
100                                                         FontStyle(textObj->allowTabs(), textPos(), m_toAdd, m_reversed, m_dirOverride),
101                                                         IntPoint(tx + m_x, ty + selTop), selHeight, sPos, ePos));
102     if (r.x() > tx + m_x + m_width)
103         r.setWidth(0);
104     else if (r.right() - 1 > tx + m_x + m_width)
105         r.setWidth(tx + m_x + m_width - r.x());
106     return r;
107 }
108
109 void InlineTextBox::deleteLine(RenderArena* arena)
110 {
111     static_cast<RenderText*>(m_object)->removeTextBox(this);
112     destroy(arena);
113 }
114
115 void InlineTextBox::extractLine()
116 {
117     if (m_extracted)
118         return;
119
120     static_cast<RenderText*>(m_object)->extractTextBox(this);
121 }
122
123 void InlineTextBox::attachLine()
124 {
125     if (!m_extracted)
126         return;
127     
128     static_cast<RenderText*>(m_object)->attachTextBox(this);
129 }
130
131 int InlineTextBox::placeEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth, bool& foundBox)
132 {
133     if (foundBox) {
134         m_truncation = cFullTruncation;
135         return -1;
136     }
137
138     int ellipsisX = ltr ? blockEdge - ellipsisWidth : blockEdge + ellipsisWidth;
139     
140     // For LTR, if the left edge of the ellipsis is to the left of our text run, then we are the run that will get truncated.
141     if (ltr) {
142         if (ellipsisX <= m_x) {
143             // Too far.  Just set full truncation, but return -1 and let the ellipsis just be placed at the edge of the box.
144             m_truncation = cFullTruncation;
145             foundBox = true;
146             return -1;
147         }
148
149         if (ellipsisX < m_x + m_width) {
150             if (m_reversed)
151                 return -1; // FIXME: Support LTR truncation when the last run is RTL someday.
152
153             foundBox = true;
154
155             int offset = offsetForPosition(ellipsisX, false);
156             if (offset == 0) {
157                 // No characters should be rendered.  Set ourselves to full truncation and place the ellipsis at the min of our start
158                 // and the ellipsis edge.
159                 m_truncation = cFullTruncation;
160                 return min(ellipsisX, m_x);
161             }
162             
163             // Set the truncation index on the text run.  The ellipsis needs to be placed just after the last visible character.
164             m_truncation = offset;
165             return m_x + static_cast<RenderText*>(m_object)->width(m_start, offset, textPos(), m_firstLine);
166         }
167     }
168     else {
169         // FIXME: Support RTL truncation someday, including both modes (when the leftmost run on the line is either RTL or LTR)
170     }
171     return -1;
172 }
173
174 Color correctedTextColor(Color textColor, Color backgroundColor) 
175 {
176     // Adjust the text color if it is too close to the background color,
177     // by darkening or lightening it to move it further away.
178     
179     int d = differenceSquared(textColor, backgroundColor);
180     // semi-arbitrarily chose 65025 (255^2) value here after a few tests; 
181     if (d > 65025) {
182         return textColor;
183     }
184     
185     int distanceFromWhite = differenceSquared(textColor, Color::white);
186     int distanceFromBlack = differenceSquared(textColor, Color::black);
187
188     if (distanceFromWhite < distanceFromBlack) {
189         return textColor.dark();
190     }
191     
192     return textColor.light();
193 }
194
195 void updateGraphicsContext(GraphicsContext* context, const Color& fillColor, const Color& strokeColor, float strokeThickness)
196 {
197     int mode = context->textDrawingMode();
198     if (strokeThickness > 0) {
199         int newMode = mode | cTextStroke;
200         if (mode != newMode) {
201             context->setTextDrawingMode(newMode);
202             mode = newMode;
203         }
204     }
205     
206     if (mode & cTextFill && fillColor != context->fillColor())
207         context->setFillColor(fillColor);
208
209     if (mode & cTextStroke) {
210         if (strokeColor != context->strokeColor())
211             context->setStrokeColor(strokeColor);
212         if (strokeThickness != context->strokeThickness())
213             context->setStrokeThickness(strokeThickness);
214     }
215 }
216
217 bool InlineTextBox::isLineBreak() const
218 {
219     return object()->isBR() || (object()->style()->preserveNewline() && len() == 1 && (*textObject()->text())[start()] == '\n');
220 }
221
222 bool InlineTextBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty)
223 {
224     if (isLineBreak())
225         return false;
226
227     IntRect rect(tx + m_x, ty + m_y, m_width, m_height);
228     if (m_truncation != cFullTruncation && object()->style()->visibility() == VISIBLE && rect.contains(x, y)) {
229         object()->updateHitTestResult(result, IntPoint(x - tx, y - ty));
230         return true;
231     }
232     return false;
233 }
234
235 void InlineTextBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty)
236 {
237     if (isLineBreak() || !object()->shouldPaintWithinRoot(paintInfo) || object()->style()->visibility() != VISIBLE ||
238             m_truncation == cFullTruncation || paintInfo.phase == PaintPhaseOutline)
239         return;
240     
241     ASSERT(paintInfo.phase != PaintPhaseSelfOutline && paintInfo.phase != PaintPhaseChildOutlines);
242
243     int xPos = tx + m_x - parent()->maxHorizontalVisualOverflow();
244     int w = width() + 2 * parent()->maxHorizontalVisualOverflow();
245     if (xPos >= paintInfo.rect.right() || xPos + w <= paintInfo.rect.x())
246         return;
247
248     bool isPrinting = textObject()->document()->printing();
249     
250     // Determine whether or not we're selected.
251     bool haveSelection = !isPrinting && selectionState() != RenderObject::SelectionNone;
252     if (!haveSelection && paintInfo.phase == PaintPhaseSelection)
253         // When only painting the selection, don't bother to paint if there is none.
254         return;
255
256     // Determine whether or not we have composition underlines to draw.
257     bool containsComposition = object()->document()->frame()->editor()->compositionNode() == object()->node();
258     bool useCustomUnderlines = containsComposition && object()->document()->frame()->editor()->compositionUsesCustomUnderlines();
259
260     // Set our font.
261     RenderStyle* styleToUse = object()->style(m_firstLine);
262     int d = styleToUse->textDecorationsInEffect();
263     const Font* font = &styleToUse->font();
264     if (*font != paintInfo.context->font())
265         paintInfo.context->setFont(*font);
266
267     // 1. Paint backgrounds behind text if needed. Examples of such backgrounds include selection
268     // and composition underlines.
269     if (paintInfo.phase != PaintPhaseSelection && !isPrinting) {
270 #if PLATFORM(MAC)
271         // Custom highlighters go behind everything else.
272         if (styleToUse->highlight() != nullAtom && !paintInfo.context->paintingDisabled())
273             paintCustomHighlight(tx, ty, styleToUse->highlight());
274 #endif
275
276         if (containsComposition && !useCustomUnderlines)
277             paintCompositionBackground(paintInfo.context, tx, ty, styleToUse, font,
278                 object()->document()->frame()->editor()->compositionStart(),
279                 object()->document()->frame()->editor()->compositionEnd());
280
281         paintDocumentMarkers(paintInfo.context, tx, ty, styleToUse, font, true);
282
283         if (haveSelection && !useCustomUnderlines)
284             paintSelection(paintInfo.context, tx, ty, styleToUse, font);
285     }
286
287     // 2. Now paint the foreground, including text and decorations like underline/overline (in quirks mode only).
288     if (m_len <= 0)
289         return;
290
291     Color textFillColor;
292     Color textStrokeColor;
293     float textStrokeWidth = styleToUse->textStrokeWidth();
294
295     if (paintInfo.forceBlackText) {
296         textFillColor = Color::black;
297         textStrokeColor = Color::black;
298     } else {
299         textFillColor = styleToUse->textFillColor();
300         if (!textFillColor.isValid())
301             textFillColor = styleToUse->color();
302         
303         // Make the text fill color legible against a white background
304         if (styleToUse->forceBackgroundsToWhite())
305             textFillColor = correctedTextColor(textFillColor, Color::white);
306             
307         textStrokeColor = styleToUse->textStrokeColor();
308         if (!textStrokeColor.isValid())
309             textStrokeColor = styleToUse->color();
310         
311         // Make the text stroke color legible against a white background
312         if (styleToUse->forceBackgroundsToWhite())
313             textStrokeColor = correctedTextColor(textStrokeColor, Color::white);
314     }
315
316     // For stroked painting, we have to change the text drawing mode.  It's probably dangerous to leave that mutated as a side
317     // effect, so only when we know we're stroking, do a save/restore.
318     if (textStrokeWidth > 0)
319         paintInfo.context->save();
320
321     updateGraphicsContext(paintInfo.context, textFillColor, textStrokeColor, textStrokeWidth);
322     
323     // Set a text shadow if we have one.
324     // FIXME: Support multiple shadow effects.  Need more from the CG API before
325     // we can do this.
326     bool setShadow = false;
327     if (styleToUse->textShadow()) {
328         paintInfo.context->setShadow(IntSize(styleToUse->textShadow()->x, styleToUse->textShadow()->y),
329                                      styleToUse->textShadow()->blur, styleToUse->textShadow()->color);
330         setShadow = true;
331     }
332
333     bool paintSelectedTextOnly = (paintInfo.phase == PaintPhaseSelection);
334     bool paintSelectedTextSeparately = false; // Whether or not we have to do multiple paints.  Only
335                                               // necessary when a custom ::selection foreground color is applied.
336     Color selectionFillColor = textFillColor;
337     Color selectionStrokeColor = textStrokeColor;
338     float selectionStrokeWidth = textStrokeWidth;
339     ShadowData* selectionTextShadow = 0;
340     if (haveSelection) {
341         // Check foreground color first.
342         Color foreground = object()->selectionForegroundColor();
343         if (foreground.isValid() && foreground != selectionFillColor) {
344             if (!paintSelectedTextOnly)
345                 paintSelectedTextSeparately = true;
346             selectionFillColor = foreground;
347         }
348         RenderStyle* pseudoStyle = object()->getPseudoStyle(RenderStyle::SELECTION);
349         if (pseudoStyle) {
350             if (pseudoStyle->textShadow()) {
351                 if (!paintSelectedTextOnly)
352                     paintSelectedTextSeparately = true;
353                 if (pseudoStyle->textShadow())
354                     selectionTextShadow = pseudoStyle->textShadow();
355             }
356             
357             float strokeWidth = pseudoStyle->textStrokeWidth();
358             if (strokeWidth != selectionStrokeWidth) {
359                 if (!paintSelectedTextOnly)
360                     paintSelectedTextSeparately = true;
361                 selectionStrokeWidth = strokeWidth;
362             }
363
364             Color stroke = pseudoStyle->textStrokeColor();
365             if (!stroke.isValid())
366                 stroke = pseudoStyle->color();
367             if (stroke != selectionStrokeColor) {
368                 if (!paintSelectedTextOnly)
369                     paintSelectedTextSeparately = true;
370                 selectionStrokeColor = stroke;
371             }
372         }
373     }
374
375     StringImpl* textStr = textObject()->text();
376
377     FontStyle fontStyle(textObject()->allowTabs(), textPos(), m_toAdd, m_reversed, m_dirOverride || styleToUse->visuallyOrdered());
378
379     if (!paintSelectedTextOnly && !paintSelectedTextSeparately) {
380         // paint all the text
381         // FIXME: Handle RTL direction, handle reversed strings.  For now truncation can only be turned on
382         // for non-reversed LTR strings.
383         int endPoint = m_len;
384         if (m_truncation != cNoTruncation)
385             endPoint = m_truncation;
386         paintInfo.context->drawText(TextRun(textStr->characters() + m_start, endPoint), IntPoint(m_x + tx, m_y + ty + m_baseline), fontStyle);
387     } else {
388         int sPos, ePos;
389         selectionStartEnd(sPos, ePos);
390         if (paintSelectedTextSeparately) {
391             // paint only the text that is not selected
392             if (sPos >= ePos)
393                 paintInfo.context->drawText(TextRun(textStr->characters() + m_start, m_len), IntPoint(m_x + tx, m_y + ty + m_baseline), fontStyle);
394             else {
395                 if (sPos - 1 >= 0)
396                     paintInfo.context->drawText(TextRun(textStr->characters() + m_start, m_len), IntPoint(m_x + tx, m_y + ty + m_baseline), fontStyle,  0, sPos);
397                 if (ePos < m_start + m_len)
398                     paintInfo.context->drawText(TextRun(textStr->characters() + m_start, m_len), IntPoint(m_x + tx, m_y + ty + m_baseline), fontStyle, ePos);
399             }
400         }
401
402         if (sPos < ePos) {
403             // paint only the text that is selected
404             if (selectionStrokeWidth > 0)
405                 paintInfo.context->save();
406         
407             updateGraphicsContext(paintInfo.context, selectionFillColor, selectionStrokeColor, selectionStrokeWidth);
408
409             if (selectionTextShadow)
410                 paintInfo.context->setShadow(IntSize(selectionTextShadow->x, selectionTextShadow->y),
411                                              selectionTextShadow->blur, selectionTextShadow->color);
412             paintInfo.context->drawText(TextRun(textStr->characters() + m_start, m_len), IntPoint(m_x + tx, m_y + ty + m_baseline), fontStyle, sPos, ePos);
413             if (selectionTextShadow)
414                 paintInfo.context->clearShadow();
415                 
416             if (selectionStrokeWidth > 0)
417                 paintInfo.context->restore();
418         }
419     }
420
421     // Paint decorations
422     if (d != TDNONE && paintInfo.phase != PaintPhaseSelection && styleToUse->htmlHacks()) {
423         paintInfo.context->setStrokeColor(styleToUse->color());
424         paintDecoration(paintInfo.context, tx, ty, d);
425     }
426
427     if (paintInfo.phase != PaintPhaseSelection) {
428         paintDocumentMarkers(paintInfo.context, tx, ty, styleToUse, font, false);
429
430         if (useCustomUnderlines) {
431             const Vector<CompositionUnderline>& underlines = object()->document()->frame()->editor()->customCompositionUnderlines();
432             size_t numUnderlines = underlines.size();
433
434             for (size_t index = 0; index < numUnderlines; ++index) {
435                 const CompositionUnderline& underline = underlines[index];
436
437                 if (underline.endOffset <= start())
438                     // underline is completely before this run.  This might be an underline that sits
439                     // before the first run we draw, or underlines that were within runs we skipped 
440                     // due to truncation.
441                     continue;
442                 
443                 if (underline.startOffset <= end()) {
444                     // underline intersects this run.  Paint it.
445                     paintCompositionUnderline(paintInfo.context, tx, ty, underline);
446                     if (underline.endOffset > end() + 1)
447                         // underline also runs into the next run. Bail now, no more marker advancement.
448                         break;
449                 } else
450                     // underline is completely after this run, bail.  A later run will paint it.
451                     break;
452             }
453         }
454     }
455
456     if (setShadow)
457         paintInfo.context->clearShadow();
458         
459     if (textStrokeWidth > 0)
460         paintInfo.context->restore();
461 }
462
463 void InlineTextBox::selectionStartEnd(int& sPos, int& ePos)
464 {
465     int startPos, endPos;
466     if (object()->selectionState() == RenderObject::SelectionInside) {
467         startPos = 0;
468         endPos = textObject()->textLength();
469     } else {
470         textObject()->selectionStartEnd(startPos, endPos);
471         if (object()->selectionState() == RenderObject::SelectionStart)
472             endPos = textObject()->textLength();
473         else if (object()->selectionState() == RenderObject::SelectionEnd)
474             startPos = 0;
475     }
476
477     sPos = max(startPos - m_start, 0);
478     ePos = min(endPos - m_start, (int)m_len);
479 }
480
481 void InlineTextBox::paintSelection(GraphicsContext* p, int tx, int ty, RenderStyle* style, const Font* f)
482 {
483     // See if we have a selection to paint at all.
484     int sPos, ePos;
485     selectionStartEnd(sPos, ePos);
486     if (sPos >= ePos)
487         return;
488
489     Color textColor = style->color();
490     Color c = object()->selectionBackgroundColor();
491     if (!c.isValid() || c.alpha() == 0)
492         return;
493
494     // If the text color ends up being the same as the selection background, invert the selection
495     // background.  This should basically never happen, since the selection has transparency.
496     if (textColor == c)
497         c = Color(0xff - c.red(), 0xff - c.green(), 0xff - c.blue());
498
499     p->save();
500     updateGraphicsContext(p, c, c, 0);  // Don't draw text at all!
501     int y = selectionTop();
502     int h = selectionHeight();
503     p->clip(IntRect(m_x + tx, y + ty, m_width, h));
504     p->drawHighlightForText(TextRun(textObject()->text()->characters() + m_start, m_len), IntPoint(m_x + tx, y + ty), h, 
505                             FontStyle(textObject()->allowTabs(), textPos(), m_toAdd, m_reversed, m_dirOverride || style->visuallyOrdered()), c,
506                             sPos, ePos);
507     p->restore();
508 }
509
510 void InlineTextBox::paintCompositionBackground(GraphicsContext* p, int tx, int ty, RenderStyle* style, const Font* f, int startPos, int endPos)
511 {
512     int offset = m_start;
513     int sPos = max(startPos - offset, 0);
514     int ePos = min(endPos - offset, (int)m_len);
515
516     if (sPos >= ePos)
517         return;
518
519     p->save();
520
521     Color c = Color(225, 221, 85);
522     
523     updateGraphicsContext(p, c, c, 0); // Don't draw text at all!
524
525     int y = selectionTop();
526     int h = selectionHeight();
527     p->drawHighlightForText(TextRun(textObject()->text()->characters() + m_start, m_len),
528                             IntPoint(m_x + tx, y + ty), h, 
529                             FontStyle(textObject()->allowTabs(), textPos(), m_toAdd, m_reversed, m_dirOverride || style->visuallyOrdered()), c, sPos, ePos);
530     p->restore();
531 }
532
533 #if PLATFORM(MAC)
534 void InlineTextBox::paintCustomHighlight(int tx, int ty, const AtomicString& type)
535 {
536     RootInlineBox* r = root();
537     FloatRect rootRect(tx + r->xPos(), ty + selectionTop(), r->width(), selectionHeight());
538     FloatRect textRect(tx + xPos(), rootRect.y(), width(), rootRect.height());
539
540     object()->document()->frame()->paintCustomHighlight(type, textRect, rootRect, true, false, object()->node());
541 }
542 #endif
543
544 void InlineTextBox::paintDecoration(GraphicsContext* context, int tx, int ty, int deco)
545 {
546     tx += m_x;
547     ty += m_y;
548
549     if (m_truncation == cFullTruncation)
550         return;
551     
552     int width = (m_truncation == cNoTruncation) ? m_width
553         : static_cast<RenderText*>(m_object)->width(m_start, m_truncation, textPos(), m_firstLine);
554     
555     // Get the text decoration colors.
556     Color underline, overline, linethrough;
557     object()->getTextDecorationColors(deco, underline, overline, linethrough, true);
558     
559     // Use a special function for underlines to get the positioning exactly right.
560     bool isPrinting = textObject()->document()->printing();
561     context->setStrokeThickness(1.0f); // FIXME: We should improve this rule and not always just assume 1.
562     if (deco & UNDERLINE) {
563         context->setStrokeColor(underline);
564         // Leave one pixel of white between the baseline and the underline.
565         context->drawLineForText(IntPoint(tx, ty + m_baseline + 1), width, isPrinting);
566     }
567     if (deco & OVERLINE) {
568         context->setStrokeColor(overline);
569         context->drawLineForText(IntPoint(tx, ty), width, isPrinting);
570     }
571     if (deco & LINE_THROUGH) {
572         context->setStrokeColor(linethrough);
573         context->drawLineForText(IntPoint(tx, ty + 2 * m_baseline / 3), width, isPrinting);
574     }
575 }
576
577 void InlineTextBox::paintSpellingOrGrammarMarker(GraphicsContext* pt, int tx, int ty, DocumentMarker marker, RenderStyle* style, const Font* f, bool grammar)
578 {
579     // Never print spelling/grammar markers (5327887)
580     if (textObject()->document()->printing())
581         return;
582
583     if (m_truncation == cFullTruncation)
584         return;
585
586     tx += m_x;
587     ty += m_y;
588         
589     int start = 0;                  // start of line to draw, relative to tx
590     int width = m_width;            // how much line to draw
591     bool useWholeWidth = true;
592     unsigned paintStart = m_start;
593     unsigned paintEnd = end()+1;      // end points at the last char, not past it
594     if (paintStart <= marker.startOffset) {
595         paintStart = marker.startOffset;
596         useWholeWidth = false;
597         start = static_cast<RenderText*>(m_object)->width(m_start, paintStart - m_start, textPos(), m_firstLine);
598     }
599     if (paintEnd != marker.endOffset) {      // end points at the last char, not past it
600         paintEnd = min(paintEnd, marker.endOffset);
601         useWholeWidth = false;
602     }
603     if (m_truncation != cNoTruncation) {
604         paintEnd = min(paintEnd, (unsigned)m_start + m_truncation);
605         useWholeWidth = false;
606     }
607     if (!useWholeWidth) {
608         width = static_cast<RenderText*>(m_object)->width(paintStart, paintEnd - paintStart, textPos() + start, m_firstLine);
609     }
610     
611     // Store rendered rects for bad grammar markers, so we can hit-test against it elsewhere in order to
612     // display a toolTip. We don't do this for misspelling markers.
613     if (grammar) {
614         int y = selectionTop();
615         IntPoint startPoint = IntPoint(m_x + tx, y + ty);
616         FontStyle fontStyle = FontStyle(textObject()->allowTabs(), textPos(), m_toAdd, m_reversed, m_dirOverride || style->visuallyOrdered());
617         int startPosition = max(marker.startOffset - m_start, (unsigned)0);
618         int endPosition = min(marker.endOffset - m_start, (unsigned)m_len);    
619         TextRun run = TextRun(textObject()->text()->characters() + m_start, m_len);
620         IntRect markerRect = enclosingIntRect(f->selectionRectForText(run, fontStyle, startPoint, selectionHeight(), startPosition, endPosition));
621         object()->document()->setRenderedRectForMarker(object()->node(), marker, markerRect);
622     }
623     
624     // IMPORTANT: The misspelling underline is not considered when calculating the text bounds, so we have to
625     // make sure to fit within those bounds.  This means the top pixel(s) of the underline will overlap the
626     // bottom pixel(s) of the glyphs in smaller font sizes.  The alternatives are to increase the line spacing (bad!!)
627     // or decrease the underline thickness.  The overlap is actually the most useful, and matches what AppKit does.
628     // So, we generally place the underline at the bottom of the text, but in larger fonts that's not so good so
629     // we pin to two pixels under the baseline.
630     int lineThickness = cMisspellingLineThickness;
631     int descent = m_height - m_baseline;
632     int underlineOffset;
633     if (descent <= (2 + lineThickness)) {
634         // place the underline at the very bottom of the text in small/medium fonts
635         underlineOffset = m_height - lineThickness;
636     } else {
637         // in larger fonts, tho, place the underline up near the baseline to prevent big gap
638         underlineOffset = m_baseline + 2;
639     }
640     pt->drawLineForMisspellingOrBadGrammar(IntPoint(tx + start, ty + underlineOffset), width, grammar);
641 }
642
643 void InlineTextBox::paintTextMatchMarker(GraphicsContext* pt, int tx, int ty, DocumentMarker marker, RenderStyle* style, const Font* f)
644 {
645    // Use same y positioning and height as for selection, so that when the selection and this highlight are on
646    // the same word there are no pieces sticking out.
647     int y = selectionTop();
648     int h = selectionHeight();
649     
650     int sPos = max(marker.startOffset - m_start, (unsigned)0);
651     int ePos = min(marker.endOffset - m_start, (unsigned)m_len);    
652     TextRun run = TextRun(textObject()->text()->characters() + m_start, m_len);
653     FontStyle renderStyle = FontStyle(textObject()->allowTabs(), textPos(), m_toAdd, m_reversed, m_dirOverride || style->visuallyOrdered());
654     IntPoint startPoint = IntPoint(m_x + tx, y + ty);
655     
656     // Always compute and store the rect associated with this marker
657     IntRect markerRect = enclosingIntRect(f->selectionRectForText(run, renderStyle, startPoint, h, sPos, ePos));
658     object()->document()->setRenderedRectForMarker(object()->node(), marker, markerRect);
659      
660     // Optionally highlight the text
661     if (object()->document()->frame()->markedTextMatchesAreHighlighted()) {
662         Color color = theme()->platformTextSearchHighlightColor();
663         pt->save();
664         updateGraphicsContext(pt, color, color, 0);  // Don't draw text at all!
665         pt->clip(IntRect(tx + m_x, ty + y, m_width, h));
666         pt->drawHighlightForText(run, startPoint, h, renderStyle, color, sPos, ePos);
667         pt->restore();
668     }
669 }
670
671 void InlineTextBox::paintDocumentMarkers(GraphicsContext* pt, int tx, int ty, RenderStyle* style, const Font* f, bool background)
672 {
673     Vector<DocumentMarker> markers = object()->document()->markersForNode(object()->node());
674     Vector<DocumentMarker>::iterator markerIt = markers.begin();
675
676     // Give any document markers that touch this run a chance to draw before the text has been drawn.
677     // Note end() points at the last char, not one past it like endOffset and ranges do.
678     for ( ; markerIt != markers.end(); markerIt++) {
679         DocumentMarker marker = *markerIt;
680         
681         // Paint either the background markers or the foreground markers, but not both
682         switch (marker.type) {
683             case DocumentMarker::Grammar:
684             case DocumentMarker::Spelling:
685                 if (background)
686                     continue;
687                 break;
688                 
689             case DocumentMarker::TextMatch:
690                 if (!background)
691                     continue;
692                 break;
693             
694             default:
695                 ASSERT_NOT_REACHED();
696         }
697
698         if (marker.endOffset <= start())
699             // marker is completely before this run.  This might be a marker that sits before the
700             // first run we draw, or markers that were within runs we skipped due to truncation.
701             continue;
702         
703         if (marker.startOffset > end())
704             // marker is completely after this run, bail.  A later run will paint it.
705             break;
706         
707         // marker intersects this run.  Paint it.
708         switch (marker.type) {
709             case DocumentMarker::Spelling:
710                 paintSpellingOrGrammarMarker(pt, tx, ty, marker, style, f, false);
711                 break;
712             case DocumentMarker::Grammar:
713                 paintSpellingOrGrammarMarker(pt, tx, ty, marker, style, f, true);
714                 break;
715             case DocumentMarker::TextMatch:
716                 paintTextMatchMarker(pt, tx, ty, marker, style, f);
717                 break;
718             default:
719                 ASSERT_NOT_REACHED();
720         }
721
722         if (marker.endOffset > end() + 1)
723             // marker also runs into the next run. Bail now, no more marker advancement.
724             break;
725     }
726 }
727
728
729 void InlineTextBox::paintCompositionUnderline(GraphicsContext* ctx, int tx, int ty, const CompositionUnderline& underline)
730 {
731     tx += m_x;
732     ty += m_y;
733
734     if (m_truncation == cFullTruncation)
735         return;
736     
737     int start = 0;                 // start of line to draw, relative to tx
738     int width = m_width;           // how much line to draw
739     bool useWholeWidth = true;
740     unsigned paintStart = m_start;
741     unsigned paintEnd = end() + 1; // end points at the last char, not past it
742     if (paintStart <= underline.startOffset) {
743         paintStart = underline.startOffset;
744         useWholeWidth = false;
745         start = static_cast<RenderText*>(m_object)->width(m_start, paintStart - m_start, textPos(), m_firstLine);
746     }
747     if (paintEnd != underline.endOffset) {      // end points at the last char, not past it
748         paintEnd = min(paintEnd, (unsigned)underline.endOffset);
749         useWholeWidth = false;
750     }
751     if (m_truncation != cNoTruncation) {
752         paintEnd = min(paintEnd, (unsigned)m_start + m_truncation);
753         useWholeWidth = false;
754     }
755     if (!useWholeWidth) {
756         width = static_cast<RenderText*>(m_object)->width(paintStart, paintEnd - paintStart, textPos() + start, m_firstLine);
757     }
758
759     // Thick marked text underlines are 2px thick as long as there is room for the 2px line under the baseline.
760     // All other marked text underlines are 1px thick.
761     // If there's not enough space the underline will touch or overlap characters.
762     int lineThickness = 1;
763     if (underline.thick && m_height - m_baseline >= 2)
764         lineThickness = 2;
765
766     ctx->setStrokeColor(underline.color);
767     ctx->setStrokeThickness(lineThickness);
768     ctx->drawLineForText(IntPoint(tx + start, ty + m_height - lineThickness), width, textObject()->document()->printing());
769 }
770
771 int InlineTextBox::caretMinOffset() const
772 {
773     return m_start;
774 }
775
776 int InlineTextBox::caretMaxOffset() const
777 {
778     return m_start + m_len;
779 }
780
781 unsigned InlineTextBox::caretMaxRenderedOffset() const
782 {
783     return m_start + m_len;
784 }
785
786 int InlineTextBox::textPos() const
787 {
788     if (xPos() == 0)
789         return 0;
790         
791     RenderBlock *blockElement = object()->containingBlock();
792     return m_reversed ? xPos() - blockElement->borderRight() - blockElement->paddingRight()
793                       : xPos() - blockElement->borderLeft() - blockElement->paddingLeft();
794 }
795
796 int InlineTextBox::offsetForPosition(int _x, bool includePartialGlyphs) const
797 {
798     if (isLineBreak())
799         return 0;
800
801     RenderText* text = static_cast<RenderText*>(m_object);
802     RenderStyle *style = text->style(m_firstLine);
803     const Font* f = &style->font();
804     return f->offsetForPosition(TextRun(textObject()->text()->characters() + m_start, m_len),
805                                 FontStyle(textObject()->allowTabs(), textPos(), m_toAdd, m_reversed, m_dirOverride || style->visuallyOrdered()),
806                                 _x - m_x, includePartialGlyphs);
807 }
808
809 int InlineTextBox::positionForOffset(int offset) const
810 {
811     if (isLineBreak())
812         return m_x;
813
814     RenderText* text = static_cast<RenderText*>(m_object);
815     const Font& f = text->style(m_firstLine)->font();
816     int from = m_reversed ? offset - m_start : 0;
817     int to = m_reversed ? m_len : offset - m_start;
818     // FIXME: Do we need to add rightBearing here?
819     return enclosingIntRect(f.selectionRectForText(TextRun(text->text()->characters() + m_start, m_len),
820                                                    FontStyle(textObject()->allowTabs(), textPos(), m_toAdd, m_reversed, m_dirOverride),
821                                                    IntPoint(m_x, 0), 0, from, to)).right();
822 }
823
824 bool InlineTextBox::containsCaretOffset(int offset) const
825 {
826     // Offsets before the box are never "in".
827     if (offset < m_start)
828         return false;
829
830     int pastEnd = m_start + m_len;
831
832     // Offsets inside the box (not at either edge) are always "in".
833     if (offset < pastEnd)
834         return true;
835
836     // Offsets outside the box are always "out".
837     if (offset > pastEnd)
838         return false;
839
840     // Offsets at the end are "out" for line breaks (they are on the next line).
841     if (isLineBreak())
842         return false;
843
844     // Offsets at the end are "in" for normal boxes (but the caller has to check affinity).
845     return true;
846 }
847
848 } // namespace WebCore