summaryrefslogtreecommitdiffstats
path: root/WebCore/rendering/InlineTextBox.cpp
diff options
context:
space:
mode:
authorSteve Block <steveblock@google.com>2011-05-06 11:45:16 +0100
committerSteve Block <steveblock@google.com>2011-05-12 13:44:10 +0100
commitcad810f21b803229eb11403f9209855525a25d57 (patch)
tree29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /WebCore/rendering/InlineTextBox.cpp
parent121b0cf4517156d0ac5111caf9830c51b69bae8f (diff)
downloadexternal_webkit-cad810f21b803229eb11403f9209855525a25d57.zip
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.gz
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.bz2
Merge WebKit at r75315: Initial merge by git.
Change-Id: I570314b346ce101c935ed22a626b48c2af266b84
Diffstat (limited to 'WebCore/rendering/InlineTextBox.cpp')
-rw-r--r--WebCore/rendering/InlineTextBox.cpp1258
1 files changed, 0 insertions, 1258 deletions
diff --git a/WebCore/rendering/InlineTextBox.cpp b/WebCore/rendering/InlineTextBox.cpp
deleted file mode 100644
index e357e82..0000000
--- a/WebCore/rendering/InlineTextBox.cpp
+++ /dev/null
@@ -1,1258 +0,0 @@
-/*
- * (C) 1999 Lars Knoll (knoll@kde.org)
- * (C) 2000 Dirk Mueller (mueller@kde.org)
- * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public License
- * along with this library; see the file COPYING.LIB. If not, write to
- * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- *
- */
-
-#include "config.h"
-#include "InlineTextBox.h"
-
-#include "Chrome.h"
-#include "ChromeClient.h"
-#include "Document.h"
-#include "Editor.h"
-#include "EllipsisBox.h"
-#include "Frame.h"
-#include "GraphicsContext.h"
-#include "HitTestResult.h"
-#include "Page.h"
-#include "RenderArena.h"
-#include "RenderBlock.h"
-#include "RenderTheme.h"
-#include "Text.h"
-#include "break_lines.h"
-#include <wtf/AlwaysInline.h>
-
-using namespace std;
-
-namespace WebCore {
-
-int InlineTextBox::baselinePosition(FontBaseline baselineType) const
-{
- if (!isText() || !parent())
- return 0;
- return parent()->baselinePosition(baselineType);
-}
-
-int InlineTextBox::lineHeight() const
-{
- if (!isText() || !parent())
- return 0;
- if (m_renderer->isBR())
- return toRenderBR(m_renderer)->lineHeight(m_firstLine);
- return parent()->lineHeight();
-}
-
-int InlineTextBox::selectionTop()
-{
- return root()->selectionTop();
-}
-
-int InlineTextBox::selectionBottom()
-{
- return root()->selectionBottom();
-}
-
-int InlineTextBox::selectionHeight()
-{
- return root()->selectionHeight();
-}
-
-bool InlineTextBox::isSelected(int startPos, int endPos) const
-{
- int sPos = max(startPos - m_start, 0);
- int ePos = min(endPos - m_start, (int)m_len);
- return (sPos < ePos);
-}
-
-RenderObject::SelectionState InlineTextBox::selectionState()
-{
- RenderObject::SelectionState state = renderer()->selectionState();
- if (state == RenderObject::SelectionStart || state == RenderObject::SelectionEnd || state == RenderObject::SelectionBoth) {
- int startPos, endPos;
- renderer()->selectionStartEnd(startPos, endPos);
- // The position after a hard line break is considered to be past its end.
- int lastSelectable = start() + len() - (isLineBreak() ? 1 : 0);
-
- bool start = (state != RenderObject::SelectionEnd && startPos >= m_start && startPos < m_start + m_len);
- bool end = (state != RenderObject::SelectionStart && endPos > m_start && endPos <= lastSelectable);
- if (start && end)
- state = RenderObject::SelectionBoth;
- else if (start)
- state = RenderObject::SelectionStart;
- else if (end)
- state = RenderObject::SelectionEnd;
- else if ((state == RenderObject::SelectionEnd || startPos < m_start) &&
- (state == RenderObject::SelectionStart || endPos > lastSelectable))
- state = RenderObject::SelectionInside;
- else if (state == RenderObject::SelectionBoth)
- state = RenderObject::SelectionNone;
- }
-
- // If there are ellipsis following, make sure their selection is updated.
- if (m_truncation != cNoTruncation && root()->ellipsisBox()) {
- EllipsisBox* ellipsis = root()->ellipsisBox();
- if (state != RenderObject::SelectionNone) {
- int start, end;
- selectionStartEnd(start, end);
- // The ellipsis should be considered to be selected if the end of
- // the selection is past the beginning of the truncation and the
- // beginning of the selection is before or at the beginning of the
- // truncation.
- ellipsis->setSelectionState(end >= m_truncation && start <= m_truncation ?
- RenderObject::SelectionInside : RenderObject::SelectionNone);
- } else
- ellipsis->setSelectionState(RenderObject::SelectionNone);
- }
-
- return state;
-}
-
-typedef Vector<UChar, 256> BufferForAppendingHyphen;
-
-static void adjustCharactersAndLengthForHyphen(BufferForAppendingHyphen& charactersWithHyphen, RenderStyle* style, const UChar*& characters, int& length)
-{
- const AtomicString& hyphenString = style->hyphenString();
- charactersWithHyphen.reserveCapacity(length + hyphenString.length());
- charactersWithHyphen.append(characters, length);
- charactersWithHyphen.append(hyphenString.characters(), hyphenString.length());
- characters = charactersWithHyphen.data();
- length += hyphenString.length();
-}
-
-IntRect InlineTextBox::selectionRect(int tx, int ty, int startPos, int endPos)
-{
- int sPos = max(startPos - m_start, 0);
- int ePos = min(endPos - m_start, (int)m_len);
-
- if (sPos > ePos)
- return IntRect();
-
- RenderText* textObj = textRenderer();
- int selTop = selectionTop();
- int selHeight = selectionHeight();
- RenderStyle* styleToUse = textObj->style(m_firstLine);
- const Font& f = styleToUse->font();
-
- const UChar* characters = textObj->text()->characters() + m_start;
- int len = m_len;
- BufferForAppendingHyphen charactersWithHyphen;
- if (ePos == len && hasHyphen()) {
- adjustCharactersAndLengthForHyphen(charactersWithHyphen, styleToUse, characters, len);
- ePos = len;
- }
-
-#ifdef ANDROID_DISABLE_ROUNDING_HACKS
- TextRun textRun = TextRun(characters, len, textObj->allowTabs(), textPos(), m_toAdd, !isLeftToRightDirection(), m_dirOverride);
- if (m_disableRoundingHacks)
- textRun.disableRoundingHacks();
- IntRect r = enclosingIntRect(f.selectionRectForText(textRun, IntPoint(), selHeight, sPos, ePos));
-#else
- IntRect r = enclosingIntRect(f.selectionRectForText(TextRun(characters, len, textObj->allowTabs(), textPos(), m_toAdd, !isLeftToRightDirection(), m_dirOverride),
- IntPoint(), selHeight, sPos, ePos));
-#endif
-
- int logicalWidth = r.width();
- if (r.x() > m_logicalWidth)
- logicalWidth = 0;
- else if (r.right() > m_logicalWidth)
- logicalWidth = m_logicalWidth - r.x();
-
- IntPoint topPoint = isHorizontal() ? IntPoint(tx + m_x + r.x(), ty + selTop) : IntPoint(tx + selTop, ty + m_y + r.x());
- int width = isHorizontal() ? logicalWidth : selHeight;
- int height = isHorizontal() ? selHeight : logicalWidth;
-
- return IntRect(topPoint, IntSize(width, height));
-}
-
-void InlineTextBox::deleteLine(RenderArena* arena)
-{
- toRenderText(renderer())->removeTextBox(this);
- destroy(arena);
-}
-
-void InlineTextBox::extractLine()
-{
- if (m_extracted)
- return;
-
- toRenderText(renderer())->extractTextBox(this);
-}
-
-void InlineTextBox::attachLine()
-{
- if (!m_extracted)
- return;
-
- toRenderText(renderer())->attachTextBox(this);
-}
-
-int InlineTextBox::placeEllipsisBox(bool flowIsLTR, int visibleLeftEdge, int visibleRightEdge, int ellipsisWidth, bool& foundBox)
-{
- if (foundBox) {
- m_truncation = cFullTruncation;
- return -1;
- }
-
- // For LTR this is the left edge of the box, for RTL, the right edge in parent coordinates.
- int ellipsisX = flowIsLTR ? visibleRightEdge - ellipsisWidth : visibleLeftEdge + ellipsisWidth;
-
- // Criteria for full truncation:
- // LTR: the left edge of the ellipsis is to the left of our text run.
- // RTL: the right edge of the ellipsis is to the right of our text run.
- bool ltrFullTruncation = flowIsLTR && ellipsisX <= m_x;
- bool rtlFullTruncation = !flowIsLTR && ellipsisX >= (m_x + m_logicalWidth);
- if (ltrFullTruncation || rtlFullTruncation) {
- // Too far. Just set full truncation, but return -1 and let the ellipsis just be placed at the edge of the box.
- m_truncation = cFullTruncation;
- foundBox = true;
- return -1;
- }
-
- bool ltrEllipsisWithinBox = flowIsLTR && (ellipsisX < m_x + m_logicalWidth);
- bool rtlEllipsisWithinBox = !flowIsLTR && (ellipsisX > m_x);
- if (ltrEllipsisWithinBox || rtlEllipsisWithinBox) {
- foundBox = true;
-
- // The inline box may have different directionality than it's parent. Since truncation
- // behavior depends both on both the parent and the inline block's directionality, we
- // must keep track of these separately.
- bool ltr = isLeftToRightDirection();
- if (ltr != flowIsLTR) {
- // Width in pixels of the visible portion of the box, excluding the ellipsis.
- int visibleBoxWidth = visibleRightEdge - visibleLeftEdge - ellipsisWidth;
- ellipsisX = ltr ? m_x + visibleBoxWidth : m_x + m_logicalWidth - visibleBoxWidth;
- }
-
- int offset = offsetForPosition(ellipsisX, false);
- if (offset == 0) {
- // No characters should be rendered. Set ourselves to full truncation and place the ellipsis at the min of our start
- // and the ellipsis edge.
- m_truncation = cFullTruncation;
- return min(ellipsisX, m_x);
- }
-
- // Set the truncation index on the text run.
- m_truncation = offset;
-
- // If we got here that means that we were only partially truncated and we need to return the pixel offset at which
- // to place the ellipsis.
- int widthOfVisibleText = toRenderText(renderer())->width(m_start, offset, textPos(), m_firstLine);
-
- // The ellipsis needs to be placed just after the last visible character.
- // Where "after" is defined by the flow directionality, not the inline
- // box directionality.
- // e.g. In the case of an LTR inline box truncated in an RTL flow then we can
- // have a situation such as |Hello| -> |...He|
- if (flowIsLTR)
- return m_x + widthOfVisibleText;
- else
- return (m_x + m_logicalWidth) - widthOfVisibleText - ellipsisWidth;
- }
- return -1;
-}
-
-Color correctedTextColor(Color textColor, Color backgroundColor)
-{
- // Adjust the text color if it is too close to the background color,
- // by darkening or lightening it to move it further away.
-
- int d = differenceSquared(textColor, backgroundColor);
- // semi-arbitrarily chose 65025 (255^2) value here after a few tests;
- if (d > 65025) {
- return textColor;
- }
-
- int distanceFromWhite = differenceSquared(textColor, Color::white);
- int distanceFromBlack = differenceSquared(textColor, Color::black);
-
- if (distanceFromWhite < distanceFromBlack) {
- return textColor.dark();
- }
-
- return textColor.light();
-}
-
-void updateGraphicsContext(GraphicsContext* context, const Color& fillColor, const Color& strokeColor, float strokeThickness, ColorSpace colorSpace)
-{
- TextDrawingModeFlags mode = context->textDrawingMode();
- if (strokeThickness > 0) {
- TextDrawingModeFlags newMode = mode | TextModeStroke;
- if (mode != newMode) {
- context->setTextDrawingMode(newMode);
- mode = newMode;
- }
- }
-
- if (mode & TextModeFill && (fillColor != context->fillColor() || colorSpace != context->fillColorSpace()))
- context->setFillColor(fillColor, colorSpace);
-
- if (mode & TextModeStroke) {
- if (strokeColor != context->strokeColor())
- context->setStrokeColor(strokeColor, colorSpace);
- if (strokeThickness != context->strokeThickness())
- context->setStrokeThickness(strokeThickness);
- }
-}
-
-bool InlineTextBox::isLineBreak() const
-{
- return renderer()->isBR() || (renderer()->style()->preserveNewline() && len() == 1 && (*textRenderer()->text())[start()] == '\n');
-}
-
-bool InlineTextBox::nodeAtPoint(const HitTestRequest&, HitTestResult& result, int x, int y, int tx, int ty)
-{
- if (isLineBreak())
- return false;
-
- IntPoint boxOrigin = locationIncludingFlipping();
- boxOrigin.move(tx, ty);
- IntRect rect(boxOrigin, IntSize(width(), height()));
- if (m_truncation != cFullTruncation && visibleToHitTesting() && rect.intersects(result.rectForPoint(x, y))) {
- renderer()->updateHitTestResult(result, flipForWritingMode(IntPoint(x - tx, y - ty)));
- if (!result.addNodeToRectBasedTestResult(renderer()->node(), x, y, rect))
- return true;
- }
- return false;
-}
-
-FloatSize InlineTextBox::applyShadowToGraphicsContext(GraphicsContext* context, const ShadowData* shadow, const FloatRect& textRect, bool stroked, bool opaque, bool horizontal)
-{
- if (!shadow)
- return FloatSize();
-
- FloatSize extraOffset;
- int shadowX = horizontal ? shadow->x() : shadow->y();
- int shadowY = horizontal ? shadow->y() : -shadow->x();
- FloatSize shadowOffset(shadowX, shadowY);
- int shadowBlur = shadow->blur();
- const Color& shadowColor = shadow->color();
-
- if (shadow->next() || stroked || !opaque) {
- FloatRect shadowRect(textRect);
- shadowRect.inflate(shadowBlur);
- shadowRect.move(shadowOffset);
- context->save();
- context->clip(shadowRect);
-
- extraOffset = FloatSize(0, 2 * textRect.height() + max(0.0f, shadowOffset.height()) + shadowBlur);
- shadowOffset -= extraOffset;
- }
-
- context->setShadow(shadowOffset, shadowBlur, shadowColor, context->fillColorSpace());
- return extraOffset;
-}
-
-static void paintTextWithShadows(GraphicsContext* context, const Font& font, const TextRun& textRun, const AtomicString& emphasisMark, int emphasisMarkOffset, int startOffset, int endOffset, int truncationPoint, const IntPoint& textOrigin,
- const IntRect& boxRect, const ShadowData* shadow, bool stroked, bool horizontal)
-{
- Color fillColor = context->fillColor();
- ColorSpace fillColorSpace = context->fillColorSpace();
- bool opaque = fillColor.alpha() == 255;
- if (!opaque)
- context->setFillColor(Color::black, fillColorSpace);
-
- do {
- IntSize extraOffset;
- if (shadow)
- extraOffset = roundedIntSize(InlineTextBox::applyShadowToGraphicsContext(context, shadow, boxRect, stroked, opaque, horizontal));
- else if (!opaque)
- context->setFillColor(fillColor, fillColorSpace);
-
- if (startOffset <= endOffset) {
- if (emphasisMark.isEmpty())
- context->drawText(font, textRun, textOrigin + extraOffset, startOffset, endOffset);
- else
- context->drawEmphasisMarks(font, textRun, emphasisMark, textOrigin + extraOffset + IntSize(0, emphasisMarkOffset), startOffset, endOffset);
- } else {
- if (endOffset > 0) {
- if (emphasisMark.isEmpty())
- context->drawText(font, textRun, textOrigin + extraOffset, 0, endOffset);
- else
- context->drawEmphasisMarks(font, textRun, emphasisMark, textOrigin + extraOffset + IntSize(0, emphasisMarkOffset), 0, endOffset);
- } if (startOffset < truncationPoint) {
- if (emphasisMark.isEmpty())
- context->drawText(font, textRun, textOrigin + extraOffset, startOffset, truncationPoint);
- else
- context->drawEmphasisMarks(font, textRun, emphasisMark, textOrigin + extraOffset + IntSize(0, emphasisMarkOffset), startOffset, truncationPoint);
- }
- }
-
- if (!shadow)
- break;
-
- if (shadow->next() || stroked || !opaque)
- context->restore();
- else
- context->clearShadow();
-
- shadow = shadow->next();
- } while (shadow || stroked || !opaque);
-}
-
-void InlineTextBox::paint(PaintInfo& paintInfo, int tx, int ty)
-{
- if (isLineBreak() || !paintInfo.shouldPaintWithinRoot(renderer()) || renderer()->style()->visibility() != VISIBLE ||
- m_truncation == cFullTruncation || paintInfo.phase == PaintPhaseOutline || !m_len)
- return;
-
- ASSERT(paintInfo.phase != PaintPhaseSelfOutline && paintInfo.phase != PaintPhaseChildOutlines);
-
- // FIXME: Technically we're potentially incorporating other visual overflow that had nothing to do with us.
- // Would it be simpler to just check our own shadow and stroke overflow by hand here?
- int logicalLeftOverflow = parent()->logicalLeft() - parent()->logicalLeftVisualOverflow();
- int logicalRightOverflow = parent()->logicalRightVisualOverflow() - (parent()->logicalLeft() + parent()->logicalWidth());
- int logicalStart = logicalLeft() - logicalLeftOverflow + (isHorizontal() ? tx : ty);
- int logicalExtent = logicalWidth() + logicalLeftOverflow + logicalRightOverflow;
-
- int paintEnd = isHorizontal() ? paintInfo.rect.right() : paintInfo.rect.bottom();
- int paintStart = isHorizontal() ? paintInfo.rect.x() : paintInfo.rect.y();
-
- if (logicalStart >= paintEnd || logicalStart + logicalExtent <= paintStart)
- return;
-
- bool isPrinting = textRenderer()->document()->printing();
-
- // Determine whether or not we're selected.
- bool haveSelection = !isPrinting && paintInfo.phase != PaintPhaseTextClip && selectionState() != RenderObject::SelectionNone;
- if (!haveSelection && paintInfo.phase == PaintPhaseSelection)
- // When only painting the selection, don't bother to paint if there is none.
- return;
-
- if (m_truncation != cNoTruncation) {
- if (renderer()->containingBlock()->style()->isLeftToRightDirection() != isLeftToRightDirection()) {
- // Make the visible fragment of text hug the edge closest to the rest of the run by moving the origin
- // at which we start drawing text.
- // e.g. In the case of LTR text truncated in an RTL Context, the correct behavior is:
- // |Hello|CBA| -> |...He|CBA|
- // In order to draw the fragment "He" aligned to the right edge of it's box, we need to start drawing
- // farther to the right.
- // NOTE: WebKit's behavior differs from that of IE which appears to just overlay the ellipsis on top of the
- // truncated string i.e. |Hello|CBA| -> |...lo|CBA|
- int widthOfVisibleText = toRenderText(renderer())->width(m_start, m_truncation, textPos(), m_firstLine);
- int widthOfHiddenText = m_logicalWidth - widthOfVisibleText;
- // FIXME: The hit testing logic also needs to take this translation int account.
- if (isHorizontal())
- tx += isLeftToRightDirection() ? widthOfHiddenText : -widthOfHiddenText;
- else
- ty += isLeftToRightDirection() ? widthOfHiddenText : -widthOfHiddenText;
- }
- }
-
- GraphicsContext* context = paintInfo.context;
-
- RenderStyle* styleToUse = renderer()->style(m_firstLine);
-
- ty -= styleToUse->isHorizontalWritingMode() ? 0 : logicalHeight();
-
- IntPoint boxOrigin = locationIncludingFlipping();
- boxOrigin.move(tx, ty);
- IntRect boxRect(boxOrigin, IntSize(logicalWidth(), logicalHeight()));
- IntPoint textOrigin = IntPoint(boxOrigin.x(), boxOrigin.y() + styleToUse->font().ascent());
-
- if (!isHorizontal()) {
- context->save();
- context->translate(boxRect.x(), boxRect.bottom());
- context->rotate(static_cast<float>(deg2rad(90.)));
- context->translate(-boxRect.x(), -boxRect.bottom());
- }
-
-
- // Determine whether or not we have composition underlines to draw.
- bool containsComposition = renderer()->node() && renderer()->frame()->editor()->compositionNode() == renderer()->node();
- bool useCustomUnderlines = containsComposition && renderer()->frame()->editor()->compositionUsesCustomUnderlines();
-
- // Set our font.
- int d = styleToUse->textDecorationsInEffect();
- const Font& font = styleToUse->font();
-
- // 1. Paint backgrounds behind text if needed. Examples of such backgrounds include selection
- // and composition underlines.
- if (paintInfo.phase != PaintPhaseSelection && paintInfo.phase != PaintPhaseTextClip && !isPrinting) {
-#if PLATFORM(MAC)
- // Custom highlighters go behind everything else.
- if (styleToUse->highlight() != nullAtom && !context->paintingDisabled())
- paintCustomHighlight(tx, ty, styleToUse->highlight());
-#endif
-
- if (containsComposition && !useCustomUnderlines)
- paintCompositionBackground(context, boxOrigin, styleToUse, font,
- renderer()->frame()->editor()->compositionStart(),
- renderer()->frame()->editor()->compositionEnd());
-
- paintDocumentMarkers(context, boxOrigin, styleToUse, font, true);
-
- if (haveSelection && !useCustomUnderlines)
- paintSelection(context, boxOrigin, styleToUse, font);
- }
-
- // 2. Now paint the foreground, including text and decorations like underline/overline (in quirks mode only).
- Color textFillColor;
- Color textStrokeColor;
- Color emphasisMarkColor;
- float textStrokeWidth = styleToUse->textStrokeWidth();
- const ShadowData* textShadow = paintInfo.forceBlackText ? 0 : styleToUse->textShadow();
-
- if (paintInfo.forceBlackText) {
- textFillColor = Color::black;
- textStrokeColor = Color::black;
- emphasisMarkColor = Color::black;
- } else {
- textFillColor = styleToUse->visitedDependentColor(CSSPropertyWebkitTextFillColor);
-
- // Make the text fill color legible against a white background
- if (styleToUse->forceBackgroundsToWhite())
- textFillColor = correctedTextColor(textFillColor, Color::white);
-
- textStrokeColor = styleToUse->visitedDependentColor(CSSPropertyWebkitTextStrokeColor);
-
- // Make the text stroke color legible against a white background
- if (styleToUse->forceBackgroundsToWhite())
- textStrokeColor = correctedTextColor(textStrokeColor, Color::white);
-
- emphasisMarkColor = styleToUse->visitedDependentColor(CSSPropertyWebkitTextEmphasisColor);
-
- // Make the text stroke color legible against a white background
- if (styleToUse->forceBackgroundsToWhite())
- emphasisMarkColor = correctedTextColor(emphasisMarkColor, Color::white);
- }
-
- bool paintSelectedTextOnly = (paintInfo.phase == PaintPhaseSelection);
- bool paintSelectedTextSeparately = false;
-
- Color selectionFillColor = textFillColor;
- Color selectionStrokeColor = textStrokeColor;
- Color selectionEmphasisMarkColor = emphasisMarkColor;
- float selectionStrokeWidth = textStrokeWidth;
- const ShadowData* selectionShadow = textShadow;
- if (haveSelection) {
- // Check foreground color first.
- Color foreground = paintInfo.forceBlackText ? Color::black : renderer()->selectionForegroundColor();
- if (foreground.isValid() && foreground != selectionFillColor) {
- if (!paintSelectedTextOnly)
- paintSelectedTextSeparately = true;
- selectionFillColor = foreground;
- }
-
- Color emphasisMarkForeground = paintInfo.forceBlackText ? Color::black : renderer()->selectionEmphasisMarkColor();
- if (emphasisMarkForeground.isValid() && emphasisMarkForeground != selectionEmphasisMarkColor) {
- if (!paintSelectedTextOnly)
- paintSelectedTextSeparately = true;
- selectionEmphasisMarkColor = emphasisMarkForeground;
- }
-
- if (RenderStyle* pseudoStyle = renderer()->getCachedPseudoStyle(SELECTION)) {
- const ShadowData* shadow = paintInfo.forceBlackText ? 0 : pseudoStyle->textShadow();
- if (shadow != selectionShadow) {
- if (!paintSelectedTextOnly)
- paintSelectedTextSeparately = true;
- selectionShadow = shadow;
- }
-
- float strokeWidth = pseudoStyle->textStrokeWidth();
- if (strokeWidth != selectionStrokeWidth) {
- if (!paintSelectedTextOnly)
- paintSelectedTextSeparately = true;
- selectionStrokeWidth = strokeWidth;
- }
-
- Color stroke = paintInfo.forceBlackText ? Color::black : pseudoStyle->visitedDependentColor(CSSPropertyWebkitTextStrokeColor);
- if (stroke != selectionStrokeColor) {
- if (!paintSelectedTextOnly)
- paintSelectedTextSeparately = true;
- selectionStrokeColor = stroke;
- }
- }
- }
-
- const UChar* characters = textRenderer()->text()->characters() + m_start;
- int length = m_len;
- BufferForAppendingHyphen charactersWithHyphen;
- if (hasHyphen())
- adjustCharactersAndLengthForHyphen(charactersWithHyphen, styleToUse, characters, length);
-
- TextRun textRun(characters, length, textRenderer()->allowTabs(), textPos(), m_toAdd, !isLeftToRightDirection(), m_dirOverride || styleToUse->visuallyOrdered());
-#ifdef ANDROID_DISABLE_ROUNDING_HACKS
- if (m_disableRoundingHacks)
- textRun.disableRoundingHacks();
-#endif
-
- int sPos = 0;
- int ePos = 0;
- if (paintSelectedTextOnly || paintSelectedTextSeparately)
- selectionStartEnd(sPos, ePos);
-
- if (m_truncation != cNoTruncation) {
- sPos = min<int>(sPos, m_truncation);
- ePos = min<int>(ePos, m_truncation);
- length = m_truncation;
- }
-
- int emphasisMarkOffset = 0;
- const AtomicString& emphasisMark = styleToUse->textEmphasisMarkString();
- if (!emphasisMark.isEmpty()) {
- if (styleToUse->textEmphasisPosition() == TextEmphasisPositionOver)
- emphasisMarkOffset = -font.ascent() - font.emphasisMarkDescent(emphasisMark);
- else
- emphasisMarkOffset = font.descent() + font.emphasisMarkAscent(emphasisMark);
- }
-
- if (!paintSelectedTextOnly) {
- // For stroked painting, we have to change the text drawing mode. It's probably dangerous to leave that mutated as a side
- // effect, so only when we know we're stroking, do a save/restore.
- if (textStrokeWidth > 0)
- context->save();
-
- updateGraphicsContext(context, textFillColor, textStrokeColor, textStrokeWidth, styleToUse->colorSpace());
- if (!paintSelectedTextSeparately || ePos <= sPos) {
- // FIXME: Truncate right-to-left text correctly.
- paintTextWithShadows(context, font, textRun, nullAtom, 0, 0, length, length, textOrigin, boxRect, textShadow, textStrokeWidth > 0, isHorizontal());
- } else
- paintTextWithShadows(context, font, textRun, nullAtom, 0, ePos, sPos, length, textOrigin, boxRect, textShadow, textStrokeWidth > 0, isHorizontal());
-
- if (!emphasisMark.isEmpty()) {
- updateGraphicsContext(context, emphasisMarkColor, textStrokeColor, textStrokeWidth, styleToUse->colorSpace());
- if (!paintSelectedTextSeparately || ePos <= sPos) {
- // FIXME: Truncate right-to-left text correctly.
- paintTextWithShadows(context, font, textRun, emphasisMark, emphasisMarkOffset, 0, length, length, textOrigin, boxRect, textShadow, textStrokeWidth > 0, isHorizontal());
- } else
- paintTextWithShadows(context, font, textRun, emphasisMark, emphasisMarkOffset, ePos, sPos, length, textOrigin, boxRect, textShadow, textStrokeWidth > 0, isHorizontal());
- }
-
- if (textStrokeWidth > 0)
- context->restore();
- }
-
- if ((paintSelectedTextOnly || paintSelectedTextSeparately) && sPos < ePos) {
- // paint only the text that is selected
- if (selectionStrokeWidth > 0)
- context->save();
-
- updateGraphicsContext(context, selectionFillColor, selectionStrokeColor, selectionStrokeWidth, styleToUse->colorSpace());
- paintTextWithShadows(context, font, textRun, nullAtom, 0, sPos, ePos, length, textOrigin, boxRect, selectionShadow, selectionStrokeWidth > 0, isHorizontal());
- if (!emphasisMark.isEmpty()) {
- updateGraphicsContext(context, selectionEmphasisMarkColor, textStrokeColor, textStrokeWidth, styleToUse->colorSpace());
- paintTextWithShadows(context, font, textRun, emphasisMark, emphasisMarkOffset, sPos, ePos, length, textOrigin, boxRect, selectionShadow, selectionStrokeWidth > 0, isHorizontal());
- }
- if (selectionStrokeWidth > 0)
- context->restore();
- }
-
- // Paint decorations
- if (d != TDNONE && paintInfo.phase != PaintPhaseSelection) {
- updateGraphicsContext(context, textFillColor, textStrokeColor, textStrokeWidth, styleToUse->colorSpace());
- paintDecoration(context, boxOrigin, d, textShadow);
- }
-
- if (paintInfo.phase == PaintPhaseForeground) {
- paintDocumentMarkers(context, boxOrigin, styleToUse, font, false);
-
- if (useCustomUnderlines) {
- const Vector<CompositionUnderline>& underlines = renderer()->frame()->editor()->customCompositionUnderlines();
- size_t numUnderlines = underlines.size();
-
- for (size_t index = 0; index < numUnderlines; ++index) {
- const CompositionUnderline& underline = underlines[index];
-
- if (underline.endOffset <= start())
- // underline is completely before this run. This might be an underline that sits
- // before the first run we draw, or underlines that were within runs we skipped
- // due to truncation.
- continue;
-
- if (underline.startOffset <= end()) {
- // underline intersects this run. Paint it.
- paintCompositionUnderline(context, boxOrigin, underline);
- if (underline.endOffset > end() + 1)
- // underline also runs into the next run. Bail now, no more marker advancement.
- break;
- } else
- // underline is completely after this run, bail. A later run will paint it.
- break;
- }
- }
- }
-
- if (!isHorizontal())
- context->restore();
-}
-
-void InlineTextBox::selectionStartEnd(int& sPos, int& ePos)
-{
- int startPos, endPos;
- if (renderer()->selectionState() == RenderObject::SelectionInside) {
- startPos = 0;
- endPos = textRenderer()->textLength();
- } else {
- textRenderer()->selectionStartEnd(startPos, endPos);
- if (renderer()->selectionState() == RenderObject::SelectionStart)
- endPos = textRenderer()->textLength();
- else if (renderer()->selectionState() == RenderObject::SelectionEnd)
- startPos = 0;
- }
-
- sPos = max(startPos - m_start, 0);
- ePos = min(endPos - m_start, (int)m_len);
-}
-
-void InlineTextBox::paintSelection(GraphicsContext* context, const IntPoint& boxOrigin, RenderStyle* style, const Font& font)
-{
- // See if we have a selection to paint at all.
- int sPos, ePos;
- selectionStartEnd(sPos, ePos);
- if (sPos >= ePos)
- return;
-
- Color textColor = style->visitedDependentColor(CSSPropertyColor);
- Color c = renderer()->selectionBackgroundColor();
- if (!c.isValid() || c.alpha() == 0)
- return;
-
- // If the text color ends up being the same as the selection background, invert the selection
- // background. This should basically never happen, since the selection has transparency.
- if (textColor == c)
- c = Color(0xff - c.red(), 0xff - c.green(), 0xff - c.blue());
-
- context->save();
- updateGraphicsContext(context, c, c, 0, style->colorSpace()); // Don't draw text at all!
-
- // If the text is truncated, let the thing being painted in the truncation
- // draw its own highlight.
- int length = m_truncation != cNoTruncation ? m_truncation : m_len;
- const UChar* characters = textRenderer()->text()->characters() + m_start;
-
- BufferForAppendingHyphen charactersWithHyphen;
- if (ePos == length && hasHyphen()) {
- adjustCharactersAndLengthForHyphen(charactersWithHyphen, style, characters, length);
- ePos = length;
- }
-
- int deltaY = renderer()->style()->isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
- int selHeight = selectionHeight();
- IntPoint localOrigin(boxOrigin.x(), boxOrigin.y() - deltaY);
- context->clip(IntRect(localOrigin, IntSize(m_logicalWidth, selHeight)));
-#ifdef ANDROID_DISABLE_ROUNDING_HACKS
- TextRun textRun = TextRun(characters, length, textRenderer()->allowTabs(), textPos(), m_toAdd,
- !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered());
- if (m_disableRoundingHacks)
- textRun.disableRoundingHacks();
- context->drawHighlightForText(font, textRun, localOrigin, selHeight, c, style->colorSpace(), sPos, ePos);
-#else
- context->drawHighlightForText(font, TextRun(characters, length, textRenderer()->allowTabs(), textPos(), m_toAdd,
- !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered()),
- localOrigin, selHeight, c, style->colorSpace(), sPos, ePos);
-#endif
- context->restore();
-}
-
-void InlineTextBox::paintCompositionBackground(GraphicsContext* context, const IntPoint& boxOrigin, RenderStyle* style, const Font& font, int startPos, int endPos)
-{
- int offset = m_start;
- int sPos = max(startPos - offset, 0);
- int ePos = min(endPos - offset, (int)m_len);
-
- if (sPos >= ePos)
- return;
-
- context->save();
-
- Color c = Color(225, 221, 85);
-
- updateGraphicsContext(context, c, c, 0, style->colorSpace()); // Don't draw text at all!
-
- int deltaY = renderer()->style()->isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
- int selHeight = selectionHeight();
- IntPoint localOrigin(boxOrigin.x(), boxOrigin.y() - deltaY);
-#ifdef ANDROID_DISABLE_ROUNDING_HACKS
- TextRun textRun = TextRun(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd,
- !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered());
- if (m_disableRoundingHacks)
- textRun.disableRoundingHacks();
- context->drawHighlightForText(font, textRun, localOrigin, selHeight, c, style->colorSpace(), sPos, ePos);
-#else
- context->drawHighlightForText(font, TextRun(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd,
- !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered()),
- localOrigin, selHeight, c, style->colorSpace(), sPos, ePos);
-#endif
- context->restore();
-}
-
-#if PLATFORM(MAC)
-
-void InlineTextBox::paintCustomHighlight(int tx, int ty, const AtomicString& type)
-{
- Frame* frame = renderer()->frame();
- if (!frame)
- return;
- Page* page = frame->page();
- if (!page)
- return;
-
- RootInlineBox* r = root();
- FloatRect rootRect(tx + r->x(), ty + selectionTop(), r->logicalWidth(), selectionHeight());
- FloatRect textRect(tx + x(), rootRect.y(), logicalWidth(), rootRect.height());
-
- page->chrome()->client()->paintCustomHighlight(renderer()->node(), type, textRect, rootRect, true, false);
-}
-
-#endif
-
-void InlineTextBox::paintDecoration(GraphicsContext* context, const IntPoint& boxOrigin, int deco, const ShadowData* shadow)
-{
- if (m_truncation == cFullTruncation)
- return;
-
- IntPoint localOrigin = boxOrigin;
-
- int width = m_logicalWidth;
- if (m_truncation != cNoTruncation) {
- width = toRenderText(renderer())->width(m_start, m_truncation, textPos(), m_firstLine);
- if (!isLeftToRightDirection())
- localOrigin.move(m_logicalWidth - width, 0);
- }
-
- // Get the text decoration colors.
- Color underline, overline, linethrough;
- renderer()->getTextDecorationColors(deco, underline, overline, linethrough, true);
-
- // Use a special function for underlines to get the positioning exactly right.
- bool isPrinting = textRenderer()->document()->printing();
- context->setStrokeThickness(1.0f); // FIXME: We should improve this rule and not always just assume 1.
-
- bool linesAreOpaque = !isPrinting && (!(deco & UNDERLINE) || underline.alpha() == 255) && (!(deco & OVERLINE) || overline.alpha() == 255) && (!(deco & LINE_THROUGH) || linethrough.alpha() == 255);
-
- RenderStyle* styleToUse = renderer()->style(m_firstLine);
- int baseline = styleToUse->font().ascent();
-
- bool setClip = false;
- int extraOffset = 0;
- if (!linesAreOpaque && shadow && shadow->next()) {
- context->save();
- IntRect clipRect(localOrigin, IntSize(width, baseline + 2));
- for (const ShadowData* s = shadow; s; s = s->next()) {
- IntRect shadowRect(localOrigin, IntSize(width, baseline + 2));
- shadowRect.inflate(s->blur());
- int shadowX = isHorizontal() ? s->x() : s->y();
- int shadowY = isHorizontal() ? s->y() : -s->x();
- shadowRect.move(shadowX, shadowY);
- clipRect.unite(shadowRect);
- extraOffset = max(extraOffset, max(0, shadowY) + s->blur());
- }
- context->save();
- context->clip(clipRect);
- extraOffset += baseline + 2;
- localOrigin.move(0, extraOffset);
- setClip = true;
- }
-
- ColorSpace colorSpace = renderer()->style()->colorSpace();
- bool setShadow = false;
-
- do {
- if (shadow) {
- if (!shadow->next()) {
- // The last set of lines paints normally inside the clip.
- localOrigin.move(0, -extraOffset);
- extraOffset = 0;
- }
- int shadowX = isHorizontal() ? shadow->x() : shadow->y();
- int shadowY = isHorizontal() ? shadow->y() : -shadow->x();
- context->setShadow(IntSize(shadowX, shadowY - extraOffset), shadow->blur(), shadow->color(), colorSpace);
- setShadow = true;
- shadow = shadow->next();
- }
-
- if (deco & UNDERLINE) {
- context->setStrokeColor(underline, colorSpace);
- context->setStrokeStyle(SolidStroke);
- // Leave one pixel of white between the baseline and the underline.
- context->drawLineForText(IntPoint(localOrigin.x(), localOrigin.y() + baseline + 1), width, isPrinting);
- }
- if (deco & OVERLINE) {
- context->setStrokeColor(overline, colorSpace);
- context->setStrokeStyle(SolidStroke);
- context->drawLineForText(localOrigin, width, isPrinting);
- }
- if (deco & LINE_THROUGH) {
- context->setStrokeColor(linethrough, colorSpace);
- context->setStrokeStyle(SolidStroke);
- context->drawLineForText(IntPoint(localOrigin.x(), localOrigin.y() + 2 * baseline / 3), width, isPrinting);
- }
- } while (shadow);
-
- if (setClip)
- context->restore();
- else if (setShadow)
- context->clearShadow();
-}
-
-static GraphicsContext::TextCheckingLineStyle textCheckingLineStyleForMarkerType(DocumentMarker::MarkerType markerType)
-{
- switch (markerType) {
- case DocumentMarker::Spelling:
- return GraphicsContext::TextCheckingSpellingLineStyle;
- case DocumentMarker::Grammar:
- return GraphicsContext::TextCheckingGrammarLineStyle;
- case DocumentMarker::CorrectionIndicator:
- return GraphicsContext::TextCheckingReplacementLineStyle;
- default:
- ASSERT_NOT_REACHED();
- return GraphicsContext::TextCheckingSpellingLineStyle;
- }
-}
-
-void InlineTextBox::paintSpellingOrGrammarMarker(GraphicsContext* pt, const IntPoint& boxOrigin, const DocumentMarker& marker, RenderStyle* style, const Font& font, bool grammar)
-{
- // Never print spelling/grammar markers (5327887)
- if (textRenderer()->document()->printing())
- return;
-
- if (m_truncation == cFullTruncation)
- return;
-
- int start = 0; // start of line to draw, relative to tx
- int width = m_logicalWidth; // how much line to draw
-
- // Determine whether we need to measure text
- bool markerSpansWholeBox = true;
- if (m_start <= (int)marker.startOffset)
- markerSpansWholeBox = false;
- if ((end() + 1) != marker.endOffset) // end points at the last char, not past it
- markerSpansWholeBox = false;
- if (m_truncation != cNoTruncation)
- markerSpansWholeBox = false;
-
- if (!markerSpansWholeBox || grammar) {
- int startPosition = max<int>(marker.startOffset - m_start, 0);
- int endPosition = min<int>(marker.endOffset - m_start, m_len);
-
- if (m_truncation != cNoTruncation)
- endPosition = min<int>(endPosition, m_truncation);
-
- // Calculate start & width
- int deltaY = renderer()->style()->isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
- int selHeight = selectionHeight();
- IntPoint startPoint(boxOrigin.x(), boxOrigin.y() - deltaY);
- TextRun run(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered());
-#ifdef ANDROID_DISABLE_ROUNDING_HACKS
- if (m_disableRoundingHacks)
- run.disableRoundingHacks();
-#endif
-
- IntRect markerRect = enclosingIntRect(font.selectionRectForText(run, startPoint, selHeight, startPosition, endPosition));
- start = markerRect.x() - startPoint.x();
- width = markerRect.width();
-
- // Store rendered rects for bad grammar markers, so we can hit-test against it elsewhere in order to
- // display a toolTip. We don't do this for misspelling markers.
- if (grammar) {
- markerRect.move(-boxOrigin.x(), -boxOrigin.y());
- markerRect = renderer()->localToAbsoluteQuad(FloatRect(markerRect)).enclosingBoundingBox();
- renderer()->document()->markers()->setRenderedRectForMarker(renderer()->node(), marker, markerRect);
- }
- }
-
- // IMPORTANT: The misspelling underline is not considered when calculating the text bounds, so we have to
- // make sure to fit within those bounds. This means the top pixel(s) of the underline will overlap the
- // bottom pixel(s) of the glyphs in smaller font sizes. The alternatives are to increase the line spacing (bad!!)
- // or decrease the underline thickness. The overlap is actually the most useful, and matches what AppKit does.
- // So, we generally place the underline at the bottom of the text, but in larger fonts that's not so good so
- // we pin to two pixels under the baseline.
- int lineThickness = cMisspellingLineThickness;
- int baseline = renderer()->style(m_firstLine)->font().ascent();
- int descent = logicalHeight() - baseline;
- int underlineOffset;
- if (descent <= (2 + lineThickness)) {
- // Place the underline at the very bottom of the text in small/medium fonts.
- underlineOffset = logicalHeight() - lineThickness;
- } else {
- // In larger fonts, though, place the underline up near the baseline to prevent a big gap.
- underlineOffset = baseline + 2;
- }
- pt->drawLineForTextChecking(IntPoint(boxOrigin.x() + start, boxOrigin.y() + underlineOffset), width, textCheckingLineStyleForMarkerType(marker.type));
-}
-
-void InlineTextBox::paintTextMatchMarker(GraphicsContext* pt, const IntPoint& boxOrigin, const DocumentMarker& marker, RenderStyle* style, const Font& font)
-{
- // Use same y positioning and height as for selection, so that when the selection and this highlight are on
- // the same word there are no pieces sticking out.
- int deltaY = renderer()->style()->isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
- int selHeight = selectionHeight();
-
- int sPos = max(marker.startOffset - m_start, (unsigned)0);
- int ePos = min(marker.endOffset - m_start, (unsigned)m_len);
- TextRun run(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered());
-#ifdef ANDROID_DISABLE_ROUNDING_HACKS
- if (m_disableRoundingHacks)
- run.disableRoundingHacks();
-#endif
-
- // Always compute and store the rect associated with this marker. The computed rect is in absolute coordinates.
- IntRect markerRect = enclosingIntRect(font.selectionRectForText(run, IntPoint(m_x, selectionTop()), selHeight, sPos, ePos));
- markerRect = renderer()->localToAbsoluteQuad(FloatRect(markerRect)).enclosingBoundingBox();
- renderer()->document()->markers()->setRenderedRectForMarker(renderer()->node(), marker, markerRect);
-
- // Optionally highlight the text
- if (renderer()->frame()->editor()->markedTextMatchesAreHighlighted()) {
- Color color = marker.activeMatch ?
- renderer()->theme()->platformActiveTextSearchHighlightColor() :
- renderer()->theme()->platformInactiveTextSearchHighlightColor();
- pt->save();
- updateGraphicsContext(pt, color, color, 0, style->colorSpace()); // Don't draw text at all!
- pt->clip(IntRect(boxOrigin.x(), boxOrigin.y() - deltaY, m_logicalWidth, selHeight));
- pt->drawHighlightForText(font, run, IntPoint(boxOrigin.x(), boxOrigin.y() - deltaY), selHeight, color, style->colorSpace(), sPos, ePos);
- pt->restore();
- }
-}
-
-void InlineTextBox::computeRectForReplacementMarker(const DocumentMarker& marker, RenderStyle* style, const Font& font)
-{
- // Replacement markers are not actually drawn, but their rects need to be computed for hit testing.
- int y = selectionTop();
- int h = selectionHeight();
-
- int sPos = max(marker.startOffset - m_start, (unsigned)0);
- int ePos = min(marker.endOffset - m_start, (unsigned)m_len);
- TextRun run(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered());
-#ifdef ANDROID_DISABLE_ROUNDING_HACKS
- if (m_disableRoundingHacks)
- run.disableRoundingHacks();
-#endif
- IntPoint startPoint = IntPoint(m_x, y);
-
- // Compute and store the rect associated with this marker.
- IntRect markerRect = enclosingIntRect(font.selectionRectForText(run, startPoint, h, sPos, ePos));
- markerRect = renderer()->localToAbsoluteQuad(FloatRect(markerRect)).enclosingBoundingBox();
- renderer()->document()->markers()->setRenderedRectForMarker(renderer()->node(), marker, markerRect);
-}
-
-void InlineTextBox::paintDocumentMarkers(GraphicsContext* pt, const IntPoint& boxOrigin, RenderStyle* style, const Font& font, bool background)
-{
- if (!renderer()->node())
- return;
-
- Vector<DocumentMarker> markers = renderer()->document()->markers()->markersForNode(renderer()->node());
- Vector<DocumentMarker>::iterator markerIt = markers.begin();
-
- // Give any document markers that touch this run a chance to draw before the text has been drawn.
- // Note end() points at the last char, not one past it like endOffset and ranges do.
- for ( ; markerIt != markers.end(); markerIt++) {
- const DocumentMarker& marker = *markerIt;
-
- // Paint either the background markers or the foreground markers, but not both
- switch (marker.type) {
- case DocumentMarker::Grammar:
- case DocumentMarker::Spelling:
- case DocumentMarker::Replacement:
- case DocumentMarker::CorrectionIndicator:
- case DocumentMarker::RejectedCorrection:
- if (background)
- continue;
- break;
- case DocumentMarker::TextMatch:
- if (!background)
- continue;
- break;
-
- default:
- ASSERT_NOT_REACHED();
- }
-
- if (marker.endOffset <= start())
- // marker is completely before this run. This might be a marker that sits before the
- // first run we draw, or markers that were within runs we skipped due to truncation.
- continue;
-
- if (marker.startOffset > end())
- // marker is completely after this run, bail. A later run will paint it.
- break;
-
- // marker intersects this run. Paint it.
- switch (marker.type) {
- case DocumentMarker::Spelling:
- paintSpellingOrGrammarMarker(pt, boxOrigin, marker, style, font, false);
- break;
- case DocumentMarker::Grammar:
- paintSpellingOrGrammarMarker(pt, boxOrigin, marker, style, font, true);
- break;
- case DocumentMarker::TextMatch:
- paintTextMatchMarker(pt, boxOrigin, marker, style, font);
- break;
- case DocumentMarker::CorrectionIndicator:
- computeRectForReplacementMarker(marker, style, font);
- paintSpellingOrGrammarMarker(pt, boxOrigin, marker, style, font, false);
- break;
- case DocumentMarker::Replacement:
- case DocumentMarker::RejectedCorrection:
- break;
- default:
- ASSERT_NOT_REACHED();
- }
-
- }
-}
-
-
-void InlineTextBox::paintCompositionUnderline(GraphicsContext* ctx, const IntPoint& boxOrigin, const CompositionUnderline& underline)
-{
- if (m_truncation == cFullTruncation)
- return;
-
- int start = 0; // start of line to draw, relative to tx
- int width = m_logicalWidth; // how much line to draw
- bool useWholeWidth = true;
- unsigned paintStart = m_start;
- unsigned paintEnd = end() + 1; // end points at the last char, not past it
- if (paintStart <= underline.startOffset) {
- paintStart = underline.startOffset;
- useWholeWidth = false;
- start = toRenderText(renderer())->width(m_start, paintStart - m_start, textPos(), m_firstLine);
- }
- if (paintEnd != underline.endOffset) { // end points at the last char, not past it
- paintEnd = min(paintEnd, (unsigned)underline.endOffset);
- useWholeWidth = false;
- }
- if (m_truncation != cNoTruncation) {
- paintEnd = min(paintEnd, (unsigned)m_start + m_truncation);
- useWholeWidth = false;
- }
- if (!useWholeWidth) {
- width = toRenderText(renderer())->width(paintStart, paintEnd - paintStart, textPos() + start, m_firstLine);
- }
-
- // Thick marked text underlines are 2px thick as long as there is room for the 2px line under the baseline.
- // All other marked text underlines are 1px thick.
- // If there's not enough space the underline will touch or overlap characters.
- int lineThickness = 1;
- int baseline = renderer()->style(m_firstLine)->font().ascent();
- if (underline.thick && logicalHeight() - baseline >= 2)
- lineThickness = 2;
-
- // We need to have some space between underlines of subsequent clauses, because some input methods do not use different underline styles for those.
- // We make each line shorter, which has a harmless side effect of shortening the first and last clauses, too.
- start += 1;
- width -= 2;
-
- ctx->setStrokeColor(underline.color, renderer()->style()->colorSpace());
- ctx->setStrokeThickness(lineThickness);
- ctx->drawLineForText(IntPoint(boxOrigin.x() + start, boxOrigin.y() + logicalHeight() - lineThickness), width, textRenderer()->document()->printing());
-}
-
-int InlineTextBox::caretMinOffset() const
-{
- return m_start;
-}
-
-int InlineTextBox::caretMaxOffset() const
-{
- return m_start + m_len;
-}
-
-unsigned InlineTextBox::caretMaxRenderedOffset() const
-{
- return m_start + m_len;
-}
-
-int InlineTextBox::textPos() const
-{
- // When computing the width of a text run, RenderBlock::computeInlineDirectionPositionsForLine() doesn't include the actual offset
- // from the containing block edge in its measurement. textPos() should be consistent so the text are rendered in the same width.
- if (logicalLeft() == 0)
- return 0;
- return logicalLeft() - root()->logicalLeft();
-}
-
-int InlineTextBox::offsetForPosition(int lineOffset, bool includePartialGlyphs) const
-{
- if (isLineBreak())
- return 0;
-
- int leftOffset = isLeftToRightDirection() ? 0 : m_len;
- int rightOffset = isLeftToRightDirection() ? m_len : 0;
- if (renderer()->containingBlock()->style()->isLeftToRightDirection() != isLeftToRightDirection())
- swap(leftOffset, rightOffset);
-
- if (lineOffset - logicalLeft() > logicalWidth())
- return rightOffset;
- if (lineOffset - logicalLeft() < 0)
- return leftOffset;
-
- RenderText* text = toRenderText(renderer());
- RenderStyle* style = text->style(m_firstLine);
- const Font* f = &style->font();
-#ifdef ANDROID_DISABLE_ROUNDING_HACKS
- TextRun textRun = TextRun(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered());
- if (m_disableRoundingHacks)
- textRun.disableRoundingHacks();
- return f->offsetForPosition(textRun, lineOffset - logicalLeft(), includePartialGlyphs);
-#else
- return f->offsetForPosition(TextRun(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered()),
- lineOffset - logicalLeft(), includePartialGlyphs);
-#endif
-}
-
-int InlineTextBox::positionForOffset(int offset) const
-{
- ASSERT(offset >= m_start);
- ASSERT(offset <= m_start + m_len);
-
- if (isLineBreak())
- return logicalLeft();
-
- RenderText* text = toRenderText(renderer());
- const Font& f = text->style(m_firstLine)->font();
- int from = !isLeftToRightDirection() ? offset - m_start : 0;
- int to = !isLeftToRightDirection() ? m_len : offset - m_start;
- // FIXME: Do we need to add rightBearing here?
-#ifdef ANDROID_DISABLE_ROUNDING_HACKS
- TextRun textRun = TextRun(text->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, !isLeftToRightDirection(), m_dirOverride);
- if (m_disableRoundingHacks)
- textRun.disableRoundingHacks();
- return enclosingIntRect(f.selectionRectForText(textRun, IntPoint(logicalLeft(), 0), 0, from, to)).right();
-#else
- return enclosingIntRect(f.selectionRectForText(TextRun(text->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, !isLeftToRightDirection(), m_dirOverride),
- IntPoint(logicalLeft(), 0), 0, from, to)).right();
-#endif
-}
-
-bool InlineTextBox::containsCaretOffset(int offset) const
-{
- // Offsets before the box are never "in".
- if (offset < m_start)
- return false;
-
- int pastEnd = m_start + m_len;
-
- // Offsets inside the box (not at either edge) are always "in".
- if (offset < pastEnd)
- return true;
-
- // Offsets outside the box are always "out".
- if (offset > pastEnd)
- return false;
-
- // Offsets at the end are "out" for line breaks (they are on the next line).
- if (isLineBreak())
- return false;
-
- // Offsets at the end are "in" for normal boxes (but the caller has to check affinity).
- return true;
-}
-
-} // namespace WebCore