diff options
Diffstat (limited to 'WebCore/rendering/RenderText.cpp')
-rw-r--r-- | WebCore/rendering/RenderText.cpp | 253 |
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; } |