diff options
Diffstat (limited to 'WebCore/editing/visible_units.cpp')
-rw-r--r-- | WebCore/editing/visible_units.cpp | 207 |
1 files changed, 136 insertions, 71 deletions
diff --git a/WebCore/editing/visible_units.cpp b/WebCore/editing/visible_units.cpp index a50503d..1e8b05b 100644 --- a/WebCore/editing/visible_units.cpp +++ b/WebCore/editing/visible_units.cpp @@ -36,12 +36,32 @@ #include "TextIterator.h" #include "VisiblePosition.h" #include "htmlediting.h" +#include <wtf/unicode/Unicode.h> namespace WebCore { using namespace HTMLNames; +using namespace WTF::Unicode; -static VisiblePosition previousBoundary(const VisiblePosition &c, unsigned (*searchFunction)(const UChar *, unsigned)) +static int firstNonComplexContextLineBreak(const UChar* characters, int length) +{ + for (int i = 0; i < length; ++i) { + if (!hasLineBreakingPropertyComplexContext(characters[i])) + return i; + } + return length; +} + +static int lastNonComplexContextLineBreak(const UChar* characters, int length) +{ + for (int i = length - 1; i >= 0; --i) { + if (!hasLineBreakingPropertyComplexContext(characters[i])) + return i; + } + return -1; +} + +static VisiblePosition previousBoundary(const VisiblePosition &c, unsigned (*searchFunction)(const UChar *, unsigned, unsigned)) { Position pos = c.deepEquivalent(); Node *n = pos.node(); @@ -62,16 +82,35 @@ static VisiblePosition previousBoundary(const VisiblePosition &c, unsigned (*sea Position end = rangeCompliantEquivalent(pos); RefPtr<Range> searchRange = Range::create(d); - int exception = 0; - searchRange->setStart(start.node(), start.offset(), exception); - searchRange->setEnd(end.node(), end.offset(), exception); + Vector<UChar, 1024> string; + unsigned suffixLength = 0; + + ExceptionCode ec = 0; + if (hasLineBreakingPropertyComplexContext(c.characterBefore())) { + RefPtr<Range> forwardsScanRange(d->createRange()); + forwardsScanRange->setEndAfter(boundary, ec); + forwardsScanRange->setStart(end.node(), end.m_offset, ec); + TextIterator forwardsIterator(forwardsScanRange.get()); + while (!forwardsIterator.atEnd()) { + const UChar* characters = forwardsIterator.characters(); + int length = forwardsIterator.length(); + int i = firstNonComplexContextLineBreak(characters, length); + string.append(characters, i); + suffixLength += i; + if (i < length) + break; + forwardsIterator.advance(); + } + } + + searchRange->setStart(start.node(), start.m_offset, ec); + searchRange->setEnd(end.node(), end.m_offset, ec); - ASSERT(!exception); - if (exception) + ASSERT(!ec); + if (ec) return VisiblePosition(); - + SimplifiedBackwardsTextIterator it(searchRange.get()); - Vector<UChar, 1024> string; unsigned next = 0; bool inTextSecurityMode = start.node() && start.node()->renderer() && start.node()->renderer()->style()->textSecurity() != TSNONE; while (!it.atEnd()) { @@ -85,7 +124,7 @@ static VisiblePosition previousBoundary(const VisiblePosition &c, unsigned (*sea string.prepend(iteratorString.characters(), iteratorString.length()); } - next = searchFunction(string.data(), string.size()); + next = searchFunction(string.data(), string.size(), string.size() - suffixLength); if (next != 0) break; it.advance(); @@ -94,26 +133,22 @@ static VisiblePosition previousBoundary(const VisiblePosition &c, unsigned (*sea if (it.atEnd() && next == 0) { pos = it.range()->startPosition(); } else if (next != 0) { - Node *node = it.range()->startContainer(exception); - if (node->isTextNode() || (node->renderer() && node->renderer()->isBR())) + Node *node = it.range()->startContainer(ec); + if ((node->isTextNode() && static_cast<int>(next) <= node->maxCharacterOffset()) || (node->renderer() && node->renderer()->isBR() && !next)) // The next variable contains a usable index into a text node pos = Position(node, next); else { - // Use the end of the found range, the start is not guaranteed to - // be correct. - Position end = it.range()->endPosition(); - VisiblePosition boundary(end); - unsigned i = it.length() - next; - while (i--) - boundary = boundary.previous(); - return boundary; + // Use the character iterator to translate the next value into a DOM position. + BackwardsCharacterIterator charIt(searchRange.get()); + charIt.advance(string.size() - suffixLength - next); + pos = charIt.range()->endPosition(); } } return VisiblePosition(pos, DOWNSTREAM); } -static VisiblePosition nextBoundary(const VisiblePosition &c, unsigned (*searchFunction)(const UChar *, unsigned)) +static VisiblePosition nextBoundary(const VisiblePosition &c, unsigned (*searchFunction)(const UChar *, unsigned, unsigned)) { Position pos = c.deepEquivalent(); Node *n = pos.node(); @@ -132,11 +167,30 @@ static VisiblePosition nextBoundary(const VisiblePosition &c, unsigned (*searchF RefPtr<Range> searchRange(d->createRange()); Position start(rangeCompliantEquivalent(pos)); + + Vector<UChar, 1024> string; + unsigned prefixLength = 0; + ExceptionCode ec = 0; + if (hasLineBreakingPropertyComplexContext(c.characterAfter())) { + RefPtr<Range> backwardsScanRange(d->createRange()); + backwardsScanRange->setEnd(start.node(), start.m_offset, ec); + SimplifiedBackwardsTextIterator backwardsIterator(backwardsScanRange.get()); + while (!backwardsIterator.atEnd()) { + const UChar* characters = backwardsIterator.characters(); + int length = backwardsIterator.length(); + int i = lastNonComplexContextLineBreak(characters, length); + string.prepend(characters + i + 1, length - i - 1); + prefixLength += length - i - 1; + if (i > -1) + break; + backwardsIterator.advance(); + } + } + searchRange->selectNodeContents(boundary, ec); - searchRange->setStart(start.node(), start.offset(), ec); + searchRange->setStart(start.node(), start.m_offset, ec); TextIterator it(searchRange.get(), true); - Vector<UChar, 1024> string; unsigned next = 0; bool inTextSecurityMode = start.node() && start.node()->renderer() && start.node()->renderer()->style()->textSecurity() != TSNONE; while (!it.atEnd()) { @@ -151,7 +205,7 @@ static VisiblePosition nextBoundary(const VisiblePosition &c, unsigned (*searchF string.append(iteratorString.characters(), iteratorString.length()); } - next = searchFunction(string.data(), string.size()); + next = searchFunction(string.data(), string.size(), prefixLength); if (next != string.size()) break; it.advance(); @@ -159,16 +213,18 @@ static VisiblePosition nextBoundary(const VisiblePosition &c, unsigned (*searchF if (it.atEnd() && next == string.size()) { pos = it.range()->startPosition(); - } else if (next != 0) { + } else if (next != prefixLength) { // Use the character iterator to translate the next value into a DOM position. CharacterIterator charIt(searchRange.get(), true); - charIt.advance(next - 1); + charIt.advance(next - prefixLength - 1); pos = charIt.range()->endPosition(); - // FIXME: workaround for collapsed range (where only start position is correct) emitted for some emitted newlines (see rdar://5192593) - VisiblePosition visPos = VisiblePosition(pos); - if (visPos == VisiblePosition(charIt.range()->startPosition())) - pos = visPos.next(true).deepEquivalent(); + if (*charIt.characters() == '\n') { + // FIXME: workaround for collapsed range (where only start position is correct) emitted for some emitted newlines (see rdar://5192593) + VisiblePosition visPos = VisiblePosition(pos); + if (visPos == VisiblePosition(charIt.range()->startPosition())) + pos = visPos.next(true).deepEquivalent(); + } } // generate VisiblePosition, use UPSTREAM affinity if possible @@ -177,10 +233,13 @@ static VisiblePosition nextBoundary(const VisiblePosition &c, unsigned (*searchF // --------- -static unsigned startWordBoundary(const UChar* characters, unsigned length) +static unsigned startWordBoundary(const UChar* characters, unsigned length, unsigned offset) { + ASSERT(offset); + if (lastNonComplexContextLineBreak(characters, offset) == -1) + return 0; int start, end; - findWordBoundary(characters, length, length, &start, &end); + findWordBoundary(characters, length, offset - 1, &start, &end); return start; } @@ -201,10 +260,13 @@ VisiblePosition startOfWord(const VisiblePosition &c, EWordSide side) return previousBoundary(p, startWordBoundary); } -static unsigned endWordBoundary(const UChar* characters, unsigned length) +static unsigned endWordBoundary(const UChar* characters, unsigned length, unsigned offset) { + ASSERT(offset <= length); + if (firstNonComplexContextLineBreak(characters + offset, length - offset) == static_cast<int>(length - offset)) + return length; int start, end; - findWordBoundary(characters, length, 0, &start, &end); + findWordBoundary(characters, length, offset, &start, &end); return end; } @@ -224,9 +286,11 @@ VisiblePosition endOfWord(const VisiblePosition &c, EWordSide side) return nextBoundary(p, endWordBoundary); } -static unsigned previousWordPositionBoundary(const UChar* characters, unsigned length) +static unsigned previousWordPositionBoundary(const UChar* characters, unsigned length, unsigned offset) { - return findNextWordFromIndex(characters, length, length, false); + if (lastNonComplexContextLineBreak(characters, offset) == -1) + return 0; + return findNextWordFromIndex(characters, length, offset, false); } VisiblePosition previousWordPosition(const VisiblePosition &c) @@ -235,9 +299,11 @@ VisiblePosition previousWordPosition(const VisiblePosition &c) return c.honorEditableBoundaryAtOrAfter(prev); } -static unsigned nextWordPositionBoundary(const UChar* characters, unsigned length) +static unsigned nextWordPositionBoundary(const UChar* characters, unsigned length, unsigned offset) { - return findNextWordFromIndex(characters, length, 0, true); + if (firstNonComplexContextLineBreak(characters + offset, length - offset) == static_cast<int>(length - offset)) + return length; + return findNextWordFromIndex(characters, length, offset, true); } VisiblePosition nextWordPosition(const VisiblePosition &c) @@ -286,7 +352,7 @@ static VisiblePosition startPositionForLine(const VisiblePosition& c) // There are VisiblePositions at offset 0 in blocks without // RootInlineBoxes, like empty editable blocks and bordered blocks. Position p = c.deepEquivalent(); - if (p.node()->renderer() && p.node()->renderer()->isRenderBlock() && p.offset() == 0) + if (p.node()->renderer() && p.node()->renderer()->isRenderBlock() && p.m_offset == 0) return positionAvoidingFirstPositionInTable(c); return VisiblePosition(); @@ -301,11 +367,11 @@ static VisiblePosition startPositionForLine(const VisiblePosition& c) if (!startBox) return VisiblePosition(); - RenderObject *startRenderer = startBox->object(); + RenderObject *startRenderer = startBox->renderer(); if (!startRenderer) return VisiblePosition(); - startNode = startRenderer->element(); + startNode = startRenderer->node(); if (startNode) break; @@ -333,7 +399,7 @@ VisiblePosition startOfLine(const VisiblePosition& c) // greater than the input position. This fix is to account for the discrepancy between lines with webkit-line-break:after-white-space // style versus lines without that style, which would break before a space by default. Position p = visPos.deepEquivalent(); - if (p.offset() > c.deepEquivalent().offset() && p.node()->isSameNode(c.deepEquivalent().node())) { + if (p.m_offset > c.deepEquivalent().m_offset && p.node()->isSameNode(c.deepEquivalent().node())) { visPos = c.previous(); if (visPos.isNull()) return VisiblePosition(); @@ -354,7 +420,7 @@ static VisiblePosition endPositionForLine(const VisiblePosition& c) // There are VisiblePositions at offset 0 in blocks without // RootInlineBoxes, like empty editable blocks and bordered blocks. Position p = c.deepEquivalent(); - if (p.node()->renderer() && p.node()->renderer()->isRenderBlock() && p.offset() == 0) + if (p.node()->renderer() && p.node()->renderer()->isRenderBlock() && p.m_offset == 0) return c; return VisiblePosition(); } @@ -368,11 +434,11 @@ static VisiblePosition endPositionForLine(const VisiblePosition& c) if (!endBox) return VisiblePosition(); - RenderObject *endRenderer = endBox->object(); + RenderObject *endRenderer = endBox->renderer(); if (!endRenderer) return VisiblePosition(); - endNode = endRenderer->element(); + endNode = endRenderer->node(); if (endNode) break; @@ -498,7 +564,7 @@ VisiblePosition previousLinePosition(const VisiblePosition &visiblePosition, int FloatPoint absPos = containingBlock->localToAbsolute(FloatPoint()); if (containingBlock->hasOverflowClip()) absPos -= containingBlock->layer()->scrolledContentOffset(); - RenderObject *renderer = root->closestLeafChildForXPos(x - absPos.x(), isEditablePosition(p))->object(); + RenderObject* renderer = root->closestLeafChildForXPos(x - absPos.x(), isEditablePosition(p))->renderer(); Node* node = renderer->node(); if (editingIgnoresContent(node)) return Position(node->parent(), node->nodeIndex()); @@ -571,7 +637,7 @@ VisiblePosition nextLinePosition(const VisiblePosition &visiblePosition, int x) // Need to move forward to next containing editable block in this root editable // block and find the first root line box in that block. Node* startBlock = enclosingBlock(node); - Node* n = nextLeafWithSameEditability(node, p.offset()); + Node* n = nextLeafWithSameEditability(node, p.m_offset); while (n && startBlock == enclosingBlock(n)) n = nextLeafWithSameEditability(n); while (n) { @@ -599,7 +665,7 @@ VisiblePosition nextLinePosition(const VisiblePosition &visiblePosition, int x) FloatPoint absPos = containingBlock->localToAbsolute(FloatPoint()); if (containingBlock->hasOverflowClip()) absPos -= containingBlock->layer()->scrolledContentOffset(); - RenderObject *renderer = root->closestLeafChildForXPos(x - absPos.x(), isEditablePosition(p))->object(); + RenderObject* renderer = root->closestLeafChildForXPos(x - absPos.x(), isEditablePosition(p))->renderer(); Node* node = renderer->node(); if (editingIgnoresContent(node)) return Position(node->parent(), node->nodeIndex()); @@ -615,7 +681,7 @@ VisiblePosition nextLinePosition(const VisiblePosition &visiblePosition, int x) // --------- -static unsigned startSentenceBoundary(const UChar* characters, unsigned length) +static unsigned startSentenceBoundary(const UChar* characters, unsigned length, unsigned) { TextBreakIterator* iterator = sentenceBreakIterator(characters, length); // FIXME: The following function can return -1; we don't handle that. @@ -627,7 +693,7 @@ VisiblePosition startOfSentence(const VisiblePosition &c) return previousBoundary(c, startSentenceBoundary); } -static unsigned endSentenceBoundary(const UChar* characters, unsigned length) +static unsigned endSentenceBoundary(const UChar* characters, unsigned length, unsigned) { TextBreakIterator* iterator = sentenceBreakIterator(characters, length); return textBreakNext(iterator); @@ -639,7 +705,7 @@ VisiblePosition endOfSentence(const VisiblePosition &c) return nextBoundary(c, endSentenceBoundary); } -static unsigned previousSentencePositionBoundary(const UChar* characters, unsigned length) +static unsigned previousSentencePositionBoundary(const UChar* characters, unsigned length, unsigned) { // FIXME: This is identical to startSentenceBoundary. I'm pretty sure that's not right. TextBreakIterator* iterator = sentenceBreakIterator(characters, length); @@ -653,7 +719,7 @@ VisiblePosition previousSentencePosition(const VisiblePosition &c) return c.honorEditableBoundaryAtOrAfter(prev); } -static unsigned nextSentencePositionBoundary(const UChar* characters, unsigned length) +static unsigned nextSentencePositionBoundary(const UChar* characters, unsigned length, unsigned) { // FIXME: This is identical to endSentenceBoundary. This isn't right, it needs to // move to the equivlant position in the following sentence. @@ -667,8 +733,13 @@ VisiblePosition nextSentencePosition(const VisiblePosition &c) return c.honorEditableBoundaryAtOrBefore(next); } +static bool renderedAsNonInlineTableOrHR(RenderObject* renderer) +{ + return renderer && ((renderer->isTable() && !renderer->isInline()) || renderer->isHR()); +} + // FIXME: Broken for positions before/after images that aren't inline (5027702) -VisiblePosition startOfParagraph(const VisiblePosition &c) +VisiblePosition startOfParagraph(const VisiblePosition& c) { Position p = c.deepEquivalent(); Node *startNode = p.node(); @@ -676,16 +747,13 @@ VisiblePosition startOfParagraph(const VisiblePosition &c) if (!startNode) return VisiblePosition(); - if (startNode->renderer() - && ((startNode->renderer()->isTable() && !startNode->renderer()->isInline()) - || startNode->renderer()->isHR()) - && p.offset() == maxDeepOffset(startNode)) - return VisiblePosition(Position(startNode, 0)); + if (renderedAsNonInlineTableOrHR(startNode->renderer()) && p.atLastEditingPositionForNode()) + return firstDeepEditingPositionForNode(startNode); Node* startBlock = enclosingBlock(startNode); Node *node = startNode; - int offset = p.offset(); + int offset = p.m_offset; Node *n = startNode; while (n) { @@ -739,17 +807,14 @@ VisiblePosition endOfParagraph(const VisiblePosition &c) Position p = c.deepEquivalent(); Node* startNode = p.node(); - if (startNode->renderer() - && ((startNode->renderer()->isTable() && !startNode->renderer()->isInline()) - || startNode->renderer()->isHR()) - && p.offset() == 0) - return VisiblePosition(Position(startNode, maxDeepOffset(startNode))); + if (renderedAsNonInlineTableOrHR(startNode->renderer()) && p.atFirstEditingPositionForNode()) + return lastDeepEditingPositionForNode(startNode); Node* startBlock = enclosingBlock(startNode); Node *stayInsideBlock = startBlock; Node *node = startNode; - int offset = p.offset(); + int offset = p.m_offset; Node *n = startNode; while (n) { @@ -785,7 +850,7 @@ VisiblePosition endOfParagraph(const VisiblePosition &c) n = n->traverseNextNode(stayInsideBlock); } else if (editingIgnoresContent(n) || isTableElement(n)) { node = n; - offset = maxDeepOffset(n); + offset = lastOffsetForEditing(n); n = n->traverseNextSibling(stayInsideBlock); } else n = n->traverseNextNode(stayInsideBlock); @@ -820,25 +885,25 @@ bool isEndOfParagraph(const VisiblePosition &pos) return pos.isNotNull() && pos == endOfParagraph(pos); } -VisiblePosition previousParagraphPosition(const VisiblePosition &p, int x) +VisiblePosition previousParagraphPosition(const VisiblePosition& p, int x) { VisiblePosition pos = p; do { VisiblePosition n = previousLinePosition(pos, x); if (n.isNull() || n == pos) - return p; + break; pos = n; } while (inSameParagraph(p, pos)); return pos; } -VisiblePosition nextParagraphPosition(const VisiblePosition &p, int x) +VisiblePosition nextParagraphPosition(const VisiblePosition& p, int x) { VisiblePosition pos = p; do { VisiblePosition n = nextLinePosition(pos, x); if (n.isNull() || n == pos) - return p; + break; pos = n; } while (inSameParagraph(p, pos)); return pos; @@ -944,7 +1009,7 @@ VisiblePosition startOfEditableContent(const VisiblePosition& visiblePosition) if (!highestRoot) return VisiblePosition(); - return VisiblePosition(highestRoot, 0, DOWNSTREAM); + return firstDeepEditingPositionForNode(highestRoot); } VisiblePosition endOfEditableContent(const VisiblePosition& visiblePosition) @@ -953,7 +1018,7 @@ VisiblePosition endOfEditableContent(const VisiblePosition& visiblePosition) if (!highestRoot) return VisiblePosition(); - return VisiblePosition(highestRoot, maxDeepOffset(highestRoot), DOWNSTREAM); + return lastDeepEditingPositionForNode(highestRoot); } } |