summaryrefslogtreecommitdiffstats
path: root/WebCore/rendering/RenderText.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/rendering/RenderText.cpp')
-rw-r--r--WebCore/rendering/RenderText.cpp253
1 files changed, 179 insertions, 74 deletions
diff --git a/WebCore/rendering/RenderText.cpp b/WebCore/rendering/RenderText.cpp
index 084be71..2dfdb27 100644
--- a/WebCore/rendering/RenderText.cpp
+++ b/WebCore/rendering/RenderText.cpp
@@ -36,6 +36,7 @@
#include "RenderView.h"
#include "Text.h"
#include "TextBreakIterator.h"
+#include "VisiblePosition.h"
#include "break_lines.h"
#include <wtf/AlwaysInline.h>
@@ -60,7 +61,6 @@ RenderText::RenderText(Node* node, PassRefPtr<StringImpl> str)
, m_maxWidth(-1)
, m_beginMinWidth(0)
, m_endMinWidth(0)
- , m_selectionState(SelectionNone)
, m_hasTab(false)
, m_linesDirty(false)
, m_containsReversedText(false)
@@ -98,13 +98,13 @@ bool RenderText::isWordBreak() const
return false;
}
-void RenderText::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle)
+void RenderText::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
{
// There is no need to ever schedule repaints from a style change of a text run, since
// we already did this for the parent of the text run.
// We do have to schedule layouts, though, since a style change can force us to
// need to relayout.
- if (diff == RenderStyle::Layout)
+ if (diff == StyleDifferenceLayout)
setNeedsLayoutAndPrefWidthsRecalc();
ETextTransform oldTransform = oldStyle ? oldStyle->textTransform() : TTNONE;
@@ -200,17 +200,17 @@ void RenderText::deleteTextBoxes()
PassRefPtr<StringImpl> RenderText::originalText() const
{
- Node* e = element();
+ Node* e = node();
return e ? static_cast<Text*>(e)->string() : 0;
}
void RenderText::absoluteRects(Vector<IntRect>& rects, int tx, int ty, bool)
{
for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
- rects.append(IntRect(tx + box->xPos(), ty + box->yPos(), box->width(), box->height()));
+ rects.append(IntRect(tx + box->x(), ty + box->y(), box->width(), box->height()));
}
-void RenderText::addLineBoxRects(Vector<IntRect>& rects, unsigned start, unsigned end, bool useSelectionHeight)
+void RenderText::absoluteRectsForRange(Vector<IntRect>& rects, unsigned start, unsigned end, bool useSelectionHeight)
{
// Work around signed/unsigned issues. This function takes unsigneds, and is often passed UINT_MAX
// to mean "all the way to the end". InlineTextBox coordinates are unsigneds, so changing this
@@ -227,7 +227,7 @@ void RenderText::addLineBoxRects(Vector<IntRect>& rects, unsigned start, unsigne
for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
// Note: box->end() returns the index of the last character, not the index past it
if (start <= box->start() && box->end() < end) {
- IntRect r = IntRect(absPos.x() + box->xPos(), absPos.y() + box->yPos(), box->width(), box->height());
+ IntRect r = IntRect(absPos.x() + box->x(), absPos.y() + box->y(), box->width(), box->height());
if (useSelectionHeight) {
IntRect selectionRect = box->selectionRect(absPos.x(), absPos.y(), start, end);
r.setHeight(selectionRect.height());
@@ -241,7 +241,7 @@ void RenderText::addLineBoxRects(Vector<IntRect>& rects, unsigned start, unsigne
if (!useSelectionHeight) {
// change the height and y position because selectionRect uses selection-specific values
r.setHeight(box->height());
- r.setY(absPos.y() + box->yPos());
+ r.setY(absPos.y() + box->y());
}
rects.append(r);
}
@@ -252,10 +252,10 @@ void RenderText::addLineBoxRects(Vector<IntRect>& rects, unsigned start, unsigne
void RenderText::absoluteQuads(Vector<FloatQuad>& quads, bool)
{
for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
- quads.append(localToAbsoluteQuad(FloatRect(box->xPos(), box->yPos(), box->width(), box->height())));
+ quads.append(localToAbsoluteQuad(FloatRect(box->x(), box->y(), box->width(), box->height())));
}
-void RenderText::collectAbsoluteLineBoxQuads(Vector<FloatQuad>& quads, unsigned start, unsigned end, bool useSelectionHeight)
+void RenderText::absoluteQuadsForRange(Vector<FloatQuad>& quads, unsigned start, unsigned end, bool useSelectionHeight)
{
// Work around signed/unsigned issues. This function takes unsigneds, and is often passed UINT_MAX
// to mean "all the way to the end". InlineTextBox coordinates are unsigneds, so changing this
@@ -270,7 +270,7 @@ void RenderText::collectAbsoluteLineBoxQuads(Vector<FloatQuad>& quads, unsigned
for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
// Note: box->end() returns the index of the last character, not the index past it
if (start <= box->start() && box->end() < end) {
- IntRect r = IntRect(box->xPos(), box->yPos(), box->width(), box->height());
+ IntRect r = IntRect(box->x(), box->y(), box->width(), box->height());
if (useSelectionHeight) {
IntRect selectionRect = box->selectionRect(0, 0, start, end);
r.setHeight(selectionRect.height());
@@ -284,7 +284,7 @@ void RenderText::collectAbsoluteLineBoxQuads(Vector<FloatQuad>& quads, unsigned
if (!useSelectionHeight) {
// change the height and y position because selectionRect uses selection-specific values
r.setHeight(box->height());
- r.setY(box->yPos());
+ r.setY(box->y());
}
quads.append(localToAbsoluteQuad(FloatRect(r)));
}
@@ -313,61 +313,61 @@ InlineTextBox* RenderText::findNextInlineTextBox(int offset, int& pos) const
return s;
}
-VisiblePosition RenderText::positionForCoordinates(int x, int y)
+VisiblePosition RenderText::positionForPoint(const IntPoint& point)
{
if (!firstTextBox() || textLength() == 0)
- return VisiblePosition(element(), 0, DOWNSTREAM);
+ return createVisiblePosition(0, DOWNSTREAM);
// Get the offset for the position, since this will take rtl text into account.
int offset;
// FIXME: We should be able to roll these special cases into the general cases in the loop below.
- if (firstTextBox() && y < firstTextBox()->root()->bottomOverflow() && x < firstTextBox()->m_x) {
+ if (firstTextBox() && point.y() < firstTextBox()->root()->bottomOverflow() && point.x() < firstTextBox()->m_x) {
// at the y coordinate of the first line or above
// and the x coordinate is to the left of the first text box left edge
- offset = firstTextBox()->offsetForPosition(x);
- return VisiblePosition(element(), offset + firstTextBox()->start(), DOWNSTREAM);
+ offset = firstTextBox()->offsetForPosition(point.x());
+ return createVisiblePosition(offset + firstTextBox()->start(), DOWNSTREAM);
}
- if (lastTextBox() && y >= lastTextBox()->root()->topOverflow() && x >= lastTextBox()->m_x + lastTextBox()->m_width) {
+ if (lastTextBox() && point.y() >= lastTextBox()->root()->topOverflow() && point.x() >= lastTextBox()->m_x + lastTextBox()->m_width) {
// at the y coordinate of the last line or below
// and the x coordinate is to the right of the last text box right edge
- offset = lastTextBox()->offsetForPosition(x);
- return VisiblePosition(element(), offset + lastTextBox()->start(), DOWNSTREAM);
+ offset = lastTextBox()->offsetForPosition(point.x());
+ return createVisiblePosition(offset + lastTextBox()->start(), DOWNSTREAM);
}
InlineTextBox* lastBoxAbove = 0;
for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
- if (y >= box->root()->topOverflow()) {
+ if (point.y() >= box->root()->topOverflow()) {
int bottom = box->root()->nextRootBox() ? box->root()->nextRootBox()->topOverflow() : box->root()->bottomOverflow();
- if (y < bottom) {
- offset = box->offsetForPosition(x);
+ if (point.y() < bottom) {
+ offset = box->offsetForPosition(point.x());
- if (x == box->m_x)
+ if (point.x() == box->m_x)
// the x coordinate is equal to the left edge of this box
// the affinity must be downstream so the position doesn't jump back to the previous line
- return VisiblePosition(element(), offset + box->start(), DOWNSTREAM);
+ return createVisiblePosition(offset + box->start(), DOWNSTREAM);
- if (x < box->m_x + box->m_width)
+ if (point.x() < box->m_x + box->m_width)
// and the x coordinate is to the left of the right edge of this box
// check to see if position goes in this box
- return VisiblePosition(element(), offset + box->start(), offset > 0 ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM);
+ return createVisiblePosition(offset + box->start(), offset > 0 ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM);
- if (!box->prevOnLine() && x < box->m_x)
+ if (!box->prevOnLine() && point.x() < box->m_x)
// box is first on line
// and the x coordinate is to the left of the first text box left edge
- return VisiblePosition(element(), offset + box->start(), DOWNSTREAM);
+ return createVisiblePosition(offset + box->start(), DOWNSTREAM);
if (!box->nextOnLine())
// box is last on line
// and the x coordinate is to the right of the last text box right edge
// generate VisiblePosition, use UPSTREAM affinity if possible
- return VisiblePosition(element(), offset + box->start(), offset > 0 ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM);
+ return createVisiblePosition(offset + box->start(), offset > 0 ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM);
}
lastBoxAbove = box;
}
}
- return VisiblePosition(element(), lastBoxAbove ? lastBoxAbove->start() + lastBoxAbove->len() : 0, DOWNSTREAM);
+ return createVisiblePosition(lastBoxAbove ? lastBoxAbove->start() + lastBoxAbove->len() : 0, DOWNSTREAM);
}
IntRect RenderText::localCaretRect(InlineBox* inlineBox, int caretOffset, int* extraWidthToEndOfLine)
@@ -386,7 +386,7 @@ IntRect RenderText::localCaretRect(InlineBox* inlineBox, int caretOffset, int* e
int left = box->positionForOffset(caretOffset);
- int rootLeft = box->root()->xPos();
+ int rootLeft = box->root()->x();
// FIXME: should we use the width of the root inline box or the
// width of the containing block for this?
if (extraWidthToEndOfLine)
@@ -394,14 +394,13 @@ IntRect RenderText::localCaretRect(InlineBox* inlineBox, int caretOffset, int* e
RenderBlock* cb = containingBlock();
if (style()->autoWrap()) {
- int availableWidth = cb->lineWidth(top);
+ int availableWidth = cb->lineWidth(top, false);
if (box->direction() == LTR)
left = min(left, rootLeft + availableWidth - 1);
else
left = max(left, rootLeft);
}
- const int caretWidth = 1;
return IntRect(left, top, caretWidth, height);
}
@@ -709,7 +708,7 @@ void RenderText::calcPrefWidths(int leadWidth)
}
}
- if (needsWordSpacing && len > 1 || ignoringSpaces && !firstWord)
+ if ((needsWordSpacing && len > 1) || (ignoringSpaces && !firstWord))
currMaxWidth += wordSpacing;
m_minWidth = max(currMinWidth, m_minWidth);
@@ -750,7 +749,7 @@ void RenderText::setSelectionState(SelectionState state)
{
InlineTextBox* box;
- m_selectionState = state;
+ RenderObject::setSelectionState(state);
if (state == SelectionStart || state == SelectionEnd || state == SelectionBoth) {
int startPos, endPos;
selectionStartEnd(startPos, endPos);
@@ -968,7 +967,7 @@ int RenderText::lineHeight(bool firstLine, bool) const
return parent()->lineHeight(firstLine, true);
}
-void RenderText::dirtyLineBoxes(bool fullLayout, bool)
+void RenderText::dirtyLineBoxes(bool fullLayout)
{
if (fullLayout)
deleteTextBoxes();
@@ -979,16 +978,14 @@ void RenderText::dirtyLineBoxes(bool fullLayout, bool)
m_linesDirty = false;
}
-InlineTextBox* RenderText::createInlineTextBox()
+InlineTextBox* RenderText::createTextBox()
{
return new (renderArena()) InlineTextBox(this);
}
-InlineBox* RenderText::createInlineBox(bool, bool unusedIsRootLineBox, bool)
+InlineTextBox* RenderText::createInlineTextBox()
{
- ASSERT_UNUSED(unusedIsRootLineBox, !unusedIsRootLineBox);
-
- InlineTextBox* textBox = createInlineTextBox();
+ InlineTextBox* textBox = createTextBox();
if (!m_firstTextBox)
m_firstTextBox = m_lastTextBox = textBox;
else {
@@ -999,7 +996,7 @@ InlineBox* RenderText::createInlineBox(bool, bool unusedIsRootLineBox, bool)
return textBox;
}
-void RenderText::position(InlineBox* box)
+void RenderText::positionLineBox(InlineBox* box)
{
InlineTextBox* s = static_cast<InlineTextBox*>(box);
@@ -1015,7 +1012,7 @@ void RenderText::position(InlineBox* box)
m_containsReversedText |= s->direction() == RTL;
}
-unsigned int RenderText::width(unsigned int from, unsigned int len, int xPos, bool firstLine) const
+unsigned RenderText::width(unsigned from, unsigned len, int xPos, bool firstLine) const
{
if (from >= textLength())
return 0;
@@ -1026,14 +1023,12 @@ unsigned int RenderText::width(unsigned int from, unsigned int len, int xPos, bo
return width(from, len, style(firstLine)->font(), xPos);
}
-unsigned int RenderText::width(unsigned int from, unsigned int len, const Font& f, int xPos) const
+unsigned RenderText::width(unsigned from, unsigned len, const Font& f, int xPos) const
{
- if (!characters() || from > textLength())
+ ASSERT(from + len <= textLength());
+ if (!characters())
return 0;
- if (from + len > textLength())
- len = textLength() - from;
-
int w;
if (&f == &style()->font()) {
if (!style()->preserveNewline() && !from && len == textLength())
@@ -1056,36 +1051,35 @@ IntRect RenderText::linesBoundingBox() const
int leftSide = 0;
int rightSide = 0;
for (InlineTextBox* curr = firstTextBox(); curr; curr = curr->nextTextBox()) {
- if (curr == firstTextBox() || curr->xPos() < leftSide)
- leftSide = curr->xPos();
- if (curr == firstTextBox() || curr->xPos() + curr->width() > rightSide)
- rightSide = curr->xPos() + curr->width();
+ if (curr == firstTextBox() || curr->x() < leftSide)
+ leftSide = curr->x();
+ if (curr == firstTextBox() || curr->x() + curr->width() > rightSide)
+ rightSide = curr->x() + curr->width();
}
result.setWidth(rightSide - leftSide);
result.setX(leftSide);
- result.setHeight(lastTextBox()->yPos() + lastTextBox()->height() - firstTextBox()->yPos());
- result.setY(firstTextBox()->yPos());
+ result.setHeight(lastTextBox()->y() + lastTextBox()->height() - firstTextBox()->y());
+ result.setY(firstTextBox()->y());
}
return result;
}
-IntRect RenderText::clippedOverflowRectForRepaint(RenderBox* repaintContainer)
+IntRect RenderText::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer)
{
RenderObject* cb = containingBlock();
return cb->clippedOverflowRectForRepaint(repaintContainer);
}
-IntRect RenderText::selectionRect(bool clipToVisibleContent)
+IntRect RenderText::selectionRectForRepaint(RenderBoxModelObject* repaintContainer, bool clipToVisibleContent)
{
ASSERT(!needsLayout());
- IntRect rect;
if (selectionState() == SelectionNone)
- return rect;
+ return IntRect();
RenderBlock* cb = containingBlock();
if (!cb)
- return rect;
+ return IntRect();
// Now calculate startPos and endPos for painting selection.
// We include a selection while endPos > 0
@@ -1103,31 +1097,24 @@ IntRect RenderText::selectionRect(bool clipToVisibleContent)
}
if (startPos == endPos)
- return rect;
+ return IntRect();
+ IntRect rect;
for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
rect.unite(box->selectionRect(0, 0, startPos, endPos));
if (clipToVisibleContent)
- computeAbsoluteRepaintRect(rect);
+ computeRectForRepaint(repaintContainer, rect);
else {
if (cb->hasColumns())
cb->adjustRectForColumns(rect);
- // FIXME: This doesn't work correctly with transforms.
- FloatPoint absPos = localToAbsolute();
- rect.move(absPos.x(), absPos.y());
+
+ rect = localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox();
}
return rect;
}
-int RenderText::verticalPositionHint(bool firstLine) const
-{
- if (parent()->isReplaced())
- return 0; // Treat inline blocks just like blocks. There can't be any vertical position hint.
- return parent()->verticalPositionHint(firstLine);
-}
-
int RenderText::caretMinOffset() const
{
InlineTextBox* box = firstTextBox();
@@ -1161,7 +1148,7 @@ unsigned RenderText::caretMaxRenderedOffset() const
int RenderText::previousOffset(int current) const
{
StringImpl* si = m_text.get();
- TextBreakIterator* iterator = characterBreakIterator(si->characters(), si->length());
+ TextBreakIterator* iterator = cursorMovementIterator(si->characters(), si->length());
if (!iterator)
return current - 1;
@@ -1169,13 +1156,122 @@ int RenderText::previousOffset(int current) const
if (result == TextBreakDone)
result = current - 1;
+#ifdef BUILDING_ON_TIGER
+ // ICU 3.2 allows character breaks before a half-width Katakana voiced mark.
+ if (static_cast<unsigned>(result) < si->length()) {
+ UChar character = (*si)[result];
+ if (character == 0xFF9E || character == 0xFF9F)
+ --result;
+ }
+#endif
+
return result;
}
+#define HANGUL_CHOSEONG_START (0x1100)
+#define HANGUL_CHOSEONG_END (0x115F)
+#define HANGUL_JUNGSEONG_START (0x1160)
+#define HANGUL_JUNGSEONG_END (0x11A2)
+#define HANGUL_JONGSEONG_START (0x11A8)
+#define HANGUL_JONGSEONG_END (0x11F9)
+#define HANGUL_SYLLABLE_START (0xAC00)
+#define HANGUL_SYLLABLE_END (0xD7AF)
+#define HANGUL_JONGSEONG_COUNT (28)
+
+enum HangulState {
+ HangulStateL,
+ HangulStateV,
+ HangulStateT,
+ HangulStateLV,
+ HangulStateLVT,
+ HangulStateBreak
+};
+
+inline bool isHangulLVT(UChar32 character)
+{
+ return (character - HANGUL_SYLLABLE_START) % HANGUL_JONGSEONG_COUNT;
+}
+
+int RenderText::previousOffsetForBackwardDeletion(int current) const
+{
+#if PLATFORM(MAC)
+ UChar32 character;
+ while (current > 0) {
+ if (U16_IS_TRAIL((*m_text)[--current]))
+ --current;
+ if (current < 0)
+ break;
+
+ UChar32 character = m_text->characterStartingAt(current);
+
+ // We don't combine characters in Armenian ... Limbu range for backward deletion.
+ if ((character >= 0x0530) && (character < 0x1950))
+ break;
+
+ if (u_isbase(character) && (character != 0xFF9E) && (character != 0xFF9F))
+ break;
+ }
+
+ if (current <= 0)
+ return current;
+
+ // Hangul
+ character = m_text->characterStartingAt(current);
+ if (((character >= HANGUL_CHOSEONG_START) && (character <= HANGUL_JONGSEONG_END)) || ((character >= HANGUL_SYLLABLE_START) && (character <= HANGUL_SYLLABLE_END))) {
+ HangulState state;
+ HangulState initialState;
+
+ if (character < HANGUL_JUNGSEONG_START)
+ state = HangulStateL;
+ else if (character < HANGUL_JONGSEONG_START)
+ state = HangulStateV;
+ else if (character < HANGUL_SYLLABLE_START)
+ state = HangulStateT;
+ else
+ state = isHangulLVT(character) ? HangulStateLVT : HangulStateLV;
+
+ initialState = state;
+
+ while (current > 0 && ((character = m_text->characterStartingAt(current - 1)) >= HANGUL_CHOSEONG_START) && (character <= HANGUL_SYLLABLE_END) && ((character <= HANGUL_JONGSEONG_END) || (character >= HANGUL_SYLLABLE_START))) {
+ switch (state) {
+ case HangulStateV:
+ if (character <= HANGUL_CHOSEONG_END)
+ state = HangulStateL;
+ else if ((character >= HANGUL_SYLLABLE_START) && (character <= HANGUL_SYLLABLE_END) && !isHangulLVT(character))
+ state = HangulStateLV;
+ else if (character > HANGUL_JUNGSEONG_END)
+ state = HangulStateBreak;
+ break;
+ case HangulStateT:
+ if ((character >= HANGUL_JUNGSEONG_START) && (character <= HANGUL_JUNGSEONG_END))
+ state = HangulStateV;
+ else if ((character >= HANGUL_SYLLABLE_START) && (character <= HANGUL_SYLLABLE_END))
+ state = (isHangulLVT(character) ? HangulStateLVT : HangulStateLV);
+ else if (character < HANGUL_JUNGSEONG_START)
+ state = HangulStateBreak;
+ break;
+ default:
+ state = (character < HANGUL_JUNGSEONG_START) ? HangulStateL : HangulStateBreak;
+ break;
+ }
+ if (state == HangulStateBreak)
+ break;
+
+ --current;
+ }
+ }
+
+ return current;
+#else
+ // Platforms other than Mac delete by one code point.
+ return current - 1;
+#endif
+}
+
int RenderText::nextOffset(int current) const
{
StringImpl* si = m_text.get();
- TextBreakIterator* iterator = characterBreakIterator(si->characters(), si->length());
+ TextBreakIterator* iterator = cursorMovementIterator(si->characters(), si->length());
if (!iterator)
return current + 1;
@@ -1183,6 +1279,15 @@ int RenderText::nextOffset(int current) const
if (result == TextBreakDone)
result = current + 1;
+#ifdef BUILDING_ON_TIGER
+ // ICU 3.2 allows character breaks before a half-width Katakana voiced mark.
+ if (static_cast<unsigned>(result) < si->length()) {
+ UChar character = (*si)[result];
+ if (character == 0xFF9E || character == 0xFF9F)
+ ++result;
+ }
+#endif
+
return result;
}