diff options
Diffstat (limited to 'Source/WebCore/editing/SpellChecker.cpp')
-rw-r--r-- | Source/WebCore/editing/SpellChecker.cpp | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/Source/WebCore/editing/SpellChecker.cpp b/Source/WebCore/editing/SpellChecker.cpp new file mode 100644 index 0000000..1807474 --- /dev/null +++ b/Source/WebCore/editing/SpellChecker.cpp @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "SpellChecker.h" + +#include "Document.h" +#include "DocumentMarkerController.h" +#include "EditorClient.h" +#include "Frame.h" +#include "HTMLInputElement.h" +#include "HTMLTextAreaElement.h" +#include "Node.h" +#include "PositionIterator.h" +#include "Range.h" +#include "RenderObject.h" +#include "Settings.h" +#include "TextIterator.h" +#include "htmlediting.h" + +namespace WebCore { + +SpellChecker::SpellChecker(Frame* frame, EditorClient* client) + : m_frame(frame) + , m_client(client) + , m_requestSequence(0) +{ +} + +SpellChecker::~SpellChecker() +{ +} + +bool SpellChecker::initRequest(Node* node) +{ + ASSERT(canCheckAsynchronously(node)); + + String text = node->textContent(); + if (!text.length()) + return false; + + m_requestNode = node; + m_requestText = text; + m_requestSequence++; + + return true; +} + +void SpellChecker::clearRequest() +{ + m_requestNode.clear(); + m_requestText = String(); +} + +bool SpellChecker::isAsynchronousEnabled() const +{ + return m_frame->settings() && m_frame->settings()->asynchronousSpellCheckingEnabled(); +} + +bool SpellChecker::canCheckAsynchronously(Node* node) const +{ + return isCheckable(node) && isAsynchronousEnabled() && !isBusy(); +} + +bool SpellChecker::isBusy() const +{ + return m_requestNode.get(); +} + +bool SpellChecker::isValid(int sequence) const +{ + return m_requestNode.get() && m_requestText.length() && m_requestSequence == sequence; +} + +bool SpellChecker::isCheckable(Node* node) const +{ + return node && node->renderer(); +} + +void SpellChecker::requestCheckingFor(Node* node) +{ + ASSERT(canCheckAsynchronously(node)); + + if (!initRequest(node)) + return; + m_client->requestCheckingOfString(this, m_requestSequence, m_requestText); +} + +static bool forwardIterator(PositionIterator& iterator, int distance) +{ + int remaining = distance; + while (!iterator.atEnd()) { + if (iterator.node()->isCharacterDataNode()) { + int length = lastOffsetForEditing(iterator.node()); + int last = length - iterator.offsetInLeafNode(); + if (remaining < last) { + iterator.setOffsetInLeafNode(iterator.offsetInLeafNode() + remaining); + return true; + } + + remaining -= last; + iterator.setOffsetInLeafNode(iterator.offsetInLeafNode() + last); + } + + iterator.increment(); + } + + return false; +} + +void SpellChecker::didCheck(int sequence, const Vector<SpellCheckingResult>& results) +{ + if (!isValid(sequence)) + return; + + if (!m_requestNode->renderer()) { + clearRequest(); + return; + } + + int startOffset = 0; + PositionIterator start = Position(m_requestNode, 0); + for (size_t i = 0; i < results.size(); ++i) { + if (results[i].type() != DocumentMarker::Spelling && results[i].type() != DocumentMarker::Grammar) + continue; + + // To avoid moving the position backward, we assume the given results are sorted with + // startOffset as the ones returned by [NSSpellChecker requestCheckingOfString:]. + ASSERT(startOffset <= results[i].location()); + if (!forwardIterator(start, results[i].location() - startOffset)) + break; + PositionIterator end = start; + if (!forwardIterator(end, results[i].length())) + break; + + // Users or JavaScript applications may change text while a spell-checker checks its + // spellings in the background. To avoid adding markers to the words modified by users or + // JavaScript applications, retrieve the words in the specified region and compare them with + // the original ones. + RefPtr<Range> range = Range::create(m_requestNode->document(), start, end); + // FIXME: Use textContent() compatible string conversion. + String destination = range->text(); + String source = m_requestText.substring(results[i].location(), results[i].length()); + if (destination == source) + m_requestNode->document()->markers()->addMarker(range.get(), results[i].type()); + + startOffset = results[i].location(); + } + + clearRequest(); +} + + +} // namespace WebCore |