PassRefPtr<Range> trimWhitespaceFromRange(VisiblePosition startPosition, VisiblePosition endPosition) { if (startPosition == endPosition || isRangeTextAllWhitespace(startPosition, endPosition)) return 0; while (isWhitespace(startPosition.characterAfter())) startPosition = startPosition.next(); while (isWhitespace(endPosition.characterBefore())) endPosition = endPosition.previous(); return makeRange(startPosition, endPosition); }
static VisibleSelection wordAtPositionForAtkBoundary(const AccessibilityObject* /*coreObject*/, const VisiblePosition& position, AtkTextBoundary boundaryType) { VisiblePosition startPosition; VisiblePosition endPosition; switch (boundaryType) { case ATK_TEXT_BOUNDARY_WORD_START: // isStartOfWord() returns true both when at the beginning of a "real" word // as when at the beginning of a whitespace range between two "real" words, // since that whitespace is considered a "word" as well. And in case we are // already at the beginning of a "real" word we do not need to look backwards. if (isStartOfWord(position) && isWhitespace(position.characterBefore())) startPosition = position; else startPosition = previousWordPosition(position); endPosition = nextWordStartPosition(startPosition); // We need to make sure that we look for the word in the current line when // we are at the beginning of a new line, and not look into the previous one // at all, which might happen when lines belong to different nodes. if (isStartOfLine(position) && isStartOfLine(endPosition)) { startPosition = endPosition; endPosition = nextWordStartPosition(startPosition); } break; case ATK_TEXT_BOUNDARY_WORD_END: startPosition = previousWordEndPosition(position); endPosition = nextWordPosition(startPosition); break; default: ASSERT_NOT_REACHED(); } VisibleSelection selectedWord(startPosition, endPosition); // We mark the selection as 'upstream' so we can use that information later, // when finding the actual offsets in getSelectionOffsetsForObject(). if (boundaryType == ATK_TEXT_BOUNDARY_WORD_END) selectedWord.setAffinity(UPSTREAM); return selectedWord; }
static VisiblePosition previousBoundary(const VisiblePosition& c, BoundarySearchFunction searchFunction) { Position pos = c.deepEquivalent(); Node *n = pos.node(); if (!n) return VisiblePosition(); Document *d = n->document(); Node *de = d->documentElement(); if (!de) return VisiblePosition(); Node *boundary = n->enclosingBlockFlowElement(); if (!boundary) return VisiblePosition(); bool isContentEditable = boundary->isContentEditable(); while (boundary && boundary != de && boundary->parentNode() && isContentEditable == boundary->parentNode()->isContentEditable()) boundary = boundary->parentNode(); Position start = rangeCompliantEquivalent(Position(boundary, 0)); Position end = rangeCompliantEquivalent(pos); RefPtr<Range> searchRange = Range::create(d); Vector<UChar, 1024> string; unsigned suffixLength = 0; ExceptionCode ec = 0; if (requiresContextForWordBoundary(c.characterBefore())) { RefPtr<Range> forwardsScanRange(d->createRange()); forwardsScanRange->setEndAfter(boundary, ec); forwardsScanRange->setStart(end.node(), end.deprecatedEditingOffset(), ec); TextIterator forwardsIterator(forwardsScanRange.get()); while (!forwardsIterator.atEnd()) { const UChar* characters = forwardsIterator.characters(); int length = forwardsIterator.length(); int i = endOfFirstWordBoundaryContext(characters, length); string.append(characters, i); suffixLength += i; if (i < length) break; forwardsIterator.advance(); } } searchRange->setStart(start.node(), start.deprecatedEditingOffset(), ec); searchRange->setEnd(end.node(), end.deprecatedEditingOffset(), ec); ASSERT(!ec); if (ec) return VisiblePosition(); SimplifiedBackwardsTextIterator it(searchRange.get()); unsigned next = 0; bool inTextSecurityMode = start.node() && start.node()->renderer() && start.node()->renderer()->style()->textSecurity() != TSNONE; bool needMoreContext = false; while (!it.atEnd()) { // iterate to get chunks until the searchFunction returns a non-zero value. if (!inTextSecurityMode) string.prepend(it.characters(), it.length()); else { // Treat bullets used in the text security mode as regular characters when looking for boundaries String iteratorString(it.characters(), it.length()); iteratorString = iteratorString.impl()->secure('x'); string.prepend(iteratorString.characters(), iteratorString.length()); } next = searchFunction(string.data(), string.size(), string.size() - suffixLength, MayHaveMoreContext, needMoreContext); if (next != 0) break; it.advance(); } if (needMoreContext) { // The last search returned the beginning of the buffer and asked for more context, // but there is no earlier text. Force a search with what's available. next = searchFunction(string.data(), string.size(), string.size() - suffixLength, DontHaveMoreContext, needMoreContext); ASSERT(!needMoreContext); } if (it.atEnd() && next == 0) { pos = it.range()->startPosition(); } else if (next != 0) { 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 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); }