예제 #1
0
bool isRangeTextAllWhitespace(VisiblePosition startPosition, VisiblePosition endPosition)
{
    while (isWhitespace(startPosition.characterAfter())) {
        startPosition = startPosition.next();

        if (startPosition == endPosition)
            return true;
    }

    return false;
}
예제 #2
0
void TypingCommand::markMisspellingsAfterTyping(ETypingCommand commandType)
{
    Frame& frame = this->frame();

#if PLATFORM(MAC)
    if (!frame.editor().isContinuousSpellCheckingEnabled()
        && !frame.editor().isAutomaticQuoteSubstitutionEnabled()
        && !frame.editor().isAutomaticLinkDetectionEnabled()
        && !frame.editor().isAutomaticDashSubstitutionEnabled()
        && !frame.editor().isAutomaticTextReplacementEnabled())
            return;
    if (frame.editor().isHandlingAcceptedCandidate())
        return;
#else
    if (!frame.editor().isContinuousSpellCheckingEnabled())
        return;
#endif
    // Take a look at the selection that results after typing and determine whether we need to spellcheck. 
    // Since the word containing the current selection is never marked, this does a check to
    // see if typing made a new word that is not in the current selection. Basically, you
    // get this by being at the end of a word and typing a space.    
    VisiblePosition start(endingSelection().start(), endingSelection().affinity());
    VisiblePosition previous = start.previous();
    if (previous.isNotNull()) {
#if !PLATFORM(IOS)
        VisiblePosition p1 = startOfWord(previous, LeftWordIfOnBoundary);
        VisiblePosition p2 = startOfWord(start, LeftWordIfOnBoundary);
        if (p1 != p2) {
            RefPtr<Range> range = makeRange(p1, p2);
            String strippedPreviousWord;
            if (range && (commandType == TypingCommand::InsertText || commandType == TypingCommand::InsertLineBreak || commandType == TypingCommand::InsertParagraphSeparator || commandType == TypingCommand::InsertParagraphSeparatorInQuotedContent))
                strippedPreviousWord = plainText(range.get()).stripWhiteSpace();
            frame.editor().markMisspellingsAfterTypingToWord(p1, endingSelection(), !strippedPreviousWord.isEmpty());
        } else if (commandType == TypingCommand::InsertText)
            frame.editor().startAlternativeTextUITimer();
#else
        UNUSED_PARAM(commandType);
        // If this bug gets fixed, this PLATFORM(IOS) code could be removed:
        // <rdar://problem/7259611> Word boundary code on iPhone gives different results than desktop
        EWordSide startWordSide = LeftWordIfOnBoundary;
        UChar32 c = previous.characterAfter();
        // FIXME: VisiblePosition::characterAfter() and characterBefore() do not emit newlines the same
        // way as TextIterator, so we do an isEndOfParagraph check here.
        if (isSpaceOrNewline(c) || c == 0xA0 || isEndOfParagraph(previous)) {
            startWordSide = RightWordIfOnBoundary;
        }
        VisiblePosition p1 = startOfWord(previous, startWordSide);
        VisiblePosition p2 = startOfWord(start, startWordSide);
        if (p1 != p2)
            frame.editor().markMisspellingsAfterTypingToWord(p1, endingSelection(), false);
#endif // !PLATFORM(IOS)
    }
}
예제 #3
0
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);
}
예제 #4
0
static VisiblePosition nextBoundary(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();

    RefPtr<Range> searchRange(d->createRange());
    Position start(rangeCompliantEquivalent(pos));

    Vector<UChar, 1024> string;
    unsigned prefixLength = 0;

    ExceptionCode ec = 0;
    if (requiresContextForWordBoundary(c.characterAfter())) {
        RefPtr<Range> backwardsScanRange(d->createRange());
        backwardsScanRange->setEnd(start.node(), start.deprecatedEditingOffset(), ec);
        SimplifiedBackwardsTextIterator backwardsIterator(backwardsScanRange.get());
        while (!backwardsIterator.atEnd()) {
            const UChar* characters = backwardsIterator.characters();
            int length = backwardsIterator.length();
            int i = startOfLastWordBoundaryContext(characters, length);
            string.prepend(characters + i, length - i);
            prefixLength += length - i;
            if (i > 0)
                break;
            backwardsIterator.advance();
        }
    }

    searchRange->selectNodeContents(boundary, ec);
    searchRange->setStart(start.node(), start.deprecatedEditingOffset(), ec);
    TextIterator it(searchRange.get(), true);
    unsigned next = 0;
    bool inTextSecurityMode = start.node() && start.node()->renderer() && start.node()->renderer()->style()->textSecurity() != TSNONE;
    bool needMoreContext = false;
    while (!it.atEnd()) {
        // Keep asking the iterator for chunks until the search function
        // returns an end value not equal to the length of the string passed to it.
        if (!inTextSecurityMode)
            string.append(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.append(iteratorString.characters(), iteratorString.length());
        }
        next = searchFunction(string.data(), string.size(), prefixLength, MayHaveMoreContext, needMoreContext);
        if (next != string.size())
            break;
        it.advance();
    }
    if (needMoreContext) {
        // The last search returned the end of the buffer and asked for more context,
        // but there is no further text. Force a search with what's available.
        next = searchFunction(string.data(), string.size(), prefixLength, DontHaveMoreContext, needMoreContext);
        ASSERT(!needMoreContext);
    }
    
    if (it.atEnd() && next == string.size()) {
        pos = it.range()->startPosition();
    } 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 - prefixLength - 1);
        pos = charIt.range()->endPosition();
        
        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
    return VisiblePosition(pos, VP_UPSTREAM_IF_POSSIBLE);
}