| Index: Source/core/editing/SpellChecker.cpp
|
| diff --git a/Source/core/editing/SpellChecker.cpp b/Source/core/editing/SpellChecker.cpp
|
| deleted file mode 100644
|
| index a4cdad7786f549c7a6ae0842154143086a5f220b..0000000000000000000000000000000000000000
|
| --- a/Source/core/editing/SpellChecker.cpp
|
| +++ /dev/null
|
| @@ -1,946 +0,0 @@
|
| -/*
|
| - * Copyright (C) 2006, 2007, 2008, 2011 Apple Inc. All rights reserved.
|
| - * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
|
| - *
|
| - * 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 "core/editing/SpellChecker.h"
|
| -
|
| -#include "core/HTMLNames.h"
|
| -#include "core/dom/Document.h"
|
| -#include "core/dom/Element.h"
|
| -#include "core/dom/NodeTraversal.h"
|
| -#include "core/editing/EditingUtilities.h"
|
| -#include "core/editing/Editor.h"
|
| -#include "core/editing/EphemeralRange.h"
|
| -#include "core/editing/SpellCheckRequester.h"
|
| -#include "core/editing/TextCheckingHelper.h"
|
| -#include "core/editing/VisibleUnits.h"
|
| -#include "core/editing/iterators/CharacterIterator.h"
|
| -#include "core/editing/markers/DocumentMarkerController.h"
|
| -#include "core/frame/LocalFrame.h"
|
| -#include "core/frame/Settings.h"
|
| -#include "core/html/HTMLInputElement.h"
|
| -#include "core/layout/LayoutTextControl.h"
|
| -#include "core/loader/EmptyClients.h"
|
| -#include "core/page/Page.h"
|
| -#include "core/page/SpellCheckerClient.h"
|
| -#include "platform/text/TextCheckerClient.h"
|
| -
|
| -namespace blink {
|
| -
|
| -using namespace HTMLNames;
|
| -
|
| -namespace {
|
| -
|
| -bool isSelectionInTextField(const VisibleSelection& selection)
|
| -{
|
| - HTMLTextFormControlElement* textControl = enclosingTextFormControl(selection.start());
|
| - return isHTMLInputElement(textControl) && toHTMLInputElement(textControl)->isTextField();
|
| -}
|
| -
|
| -bool isSelectionInTextArea(const VisibleSelection& selection)
|
| -{
|
| - HTMLTextFormControlElement* textControl = enclosingTextFormControl(selection.start());
|
| - return isHTMLTextAreaElement(textControl);
|
| -}
|
| -
|
| -bool isSelectionInTextFormControl(const VisibleSelection& selection)
|
| -{
|
| - return !!enclosingTextFormControl(selection.start());
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -PassOwnPtrWillBeRawPtr<SpellChecker> SpellChecker::create(LocalFrame& frame)
|
| -{
|
| - return adoptPtrWillBeNoop(new SpellChecker(frame));
|
| -}
|
| -
|
| -static SpellCheckerClient& emptySpellCheckerClient()
|
| -{
|
| - DEFINE_STATIC_LOCAL(EmptySpellCheckerClient, client, ());
|
| - return client;
|
| -}
|
| -
|
| -SpellCheckerClient& SpellChecker::spellCheckerClient() const
|
| -{
|
| - if (Page* page = frame().page())
|
| - return page->spellCheckerClient();
|
| - return emptySpellCheckerClient();
|
| -}
|
| -
|
| -TextCheckerClient& SpellChecker::textChecker() const
|
| -{
|
| - return spellCheckerClient().textChecker();
|
| -}
|
| -
|
| -SpellChecker::SpellChecker(LocalFrame& frame)
|
| - : m_frame(&frame)
|
| - , m_spellCheckRequester(SpellCheckRequester::create(frame))
|
| -{
|
| -}
|
| -
|
| -SpellChecker::~SpellChecker()
|
| -{
|
| -}
|
| -
|
| -bool SpellChecker::isContinuousSpellCheckingEnabled() const
|
| -{
|
| - return spellCheckerClient().isContinuousSpellCheckingEnabled();
|
| -}
|
| -
|
| -void SpellChecker::toggleContinuousSpellChecking()
|
| -{
|
| - spellCheckerClient().toggleContinuousSpellChecking();
|
| - if (isContinuousSpellCheckingEnabled())
|
| - return;
|
| - for (Frame* frame = this->frame().page()->mainFrame(); frame; frame = frame->tree().traverseNext()) {
|
| - if (!frame->isLocalFrame())
|
| - continue;
|
| - for (Node& node : NodeTraversal::startsAt(&toLocalFrame(frame)->document()->rootNode()))
|
| - node.setAlreadySpellChecked(false);
|
| - }
|
| -}
|
| -
|
| -bool SpellChecker::isGrammarCheckingEnabled()
|
| -{
|
| - return spellCheckerClient().isGrammarCheckingEnabled();
|
| -}
|
| -
|
| -void SpellChecker::didBeginEditing(Element* element)
|
| -{
|
| - if (isContinuousSpellCheckingEnabled() && unifiedTextCheckerEnabled()) {
|
| - bool isTextField = false;
|
| - HTMLTextFormControlElement* enclosingHTMLTextFormControlElement = 0;
|
| - if (!isHTMLTextFormControlElement(*element))
|
| - enclosingHTMLTextFormControlElement = enclosingTextFormControl(firstPositionInNode(element));
|
| - element = enclosingHTMLTextFormControlElement ? enclosingHTMLTextFormControlElement : element;
|
| - Element* parent = element;
|
| - if (isHTMLTextFormControlElement(*element)) {
|
| - HTMLTextFormControlElement* textControl = toHTMLTextFormControlElement(element);
|
| - parent = textControl;
|
| - element = textControl->innerEditorElement();
|
| - if (!element)
|
| - return;
|
| - isTextField = isHTMLInputElement(*textControl) && toHTMLInputElement(*textControl).isTextField();
|
| - }
|
| -
|
| - if (isTextField || !parent->isAlreadySpellChecked()) {
|
| - // We always recheck textfields because markers are removed from them on blur.
|
| - VisibleSelection selection = VisibleSelection::selectionFromContentsOfNode(element);
|
| - markMisspellingsAndBadGrammar(selection);
|
| - if (!isTextField)
|
| - parent->setAlreadySpellChecked(true);
|
| - }
|
| - }
|
| -}
|
| -
|
| -void SpellChecker::ignoreSpelling()
|
| -{
|
| - removeMarkers(frame().selection().selection(), DocumentMarker::Spelling);
|
| -}
|
| -
|
| -void SpellChecker::advanceToNextMisspelling(bool startBeforeSelection)
|
| -{
|
| - // The basic approach is to search in two phases - from the selection end to the end of the doc, and
|
| - // then we wrap and search from the doc start to (approximately) where we started.
|
| -
|
| - // Start at the end of the selection, search to edge of document. Starting at the selection end makes
|
| - // repeated "check spelling" commands work.
|
| - VisibleSelection selection(frame().selection().selection());
|
| - Position spellingSearchStart, spellingSearchEnd;
|
| - Range::selectNodeContents(frame().document(), spellingSearchStart, spellingSearchEnd);
|
| -
|
| - bool startedWithSelection = false;
|
| - if (selection.start().anchorNode()) {
|
| - startedWithSelection = true;
|
| - if (startBeforeSelection) {
|
| - VisiblePosition start(selection.visibleStart());
|
| - // We match AppKit's rule: Start 1 character before the selection.
|
| - VisiblePosition oneBeforeStart = start.previous();
|
| - spellingSearchStart = (oneBeforeStart.isNotNull() ? oneBeforeStart : start).toParentAnchoredPosition();
|
| - } else {
|
| - spellingSearchStart = selection.visibleEnd().toParentAnchoredPosition();
|
| - }
|
| - }
|
| -
|
| - Position position = spellingSearchStart;
|
| - if (!isEditablePosition(position)) {
|
| - // This shouldn't happen in very often because the Spelling menu items aren't enabled unless the
|
| - // selection is editable.
|
| - // This can happen in Mail for a mix of non-editable and editable content (like Stationary),
|
| - // when spell checking the whole document before sending the message.
|
| - // In that case the document might not be editable, but there are editable pockets that need to be spell checked.
|
| -
|
| - position = firstEditableVisiblePositionAfterPositionInRoot(position, frame().document()->documentElement()).deepEquivalent();
|
| - if (position.isNull())
|
| - return;
|
| -
|
| - spellingSearchStart = position.parentAnchoredEquivalent();
|
| - startedWithSelection = false; // won't need to wrap
|
| - }
|
| -
|
| - // topNode defines the whole range we want to operate on
|
| - ContainerNode* topNode = highestEditableRoot(position);
|
| - // TODO(yosin): |lastOffsetForEditing()| is wrong here if
|
| - // |editingIgnoresContent(highestEditableRoot())| returns true, e.g. <table>
|
| - spellingSearchEnd = createLegacyEditingPosition(topNode, EditingStrategy::lastOffsetForEditing(topNode));
|
| -
|
| - // If spellingSearchRange starts in the middle of a word, advance to the next word so we start checking
|
| - // at a word boundary. Going back by one char and then forward by a word does the trick.
|
| - if (startedWithSelection) {
|
| - VisiblePosition oneBeforeStart = VisiblePosition(spellingSearchStart, DOWNSTREAM).previous();
|
| - if (oneBeforeStart.isNotNull())
|
| - spellingSearchStart = endOfWord(oneBeforeStart).toParentAnchoredPosition();
|
| - // else we were already at the start of the editable node
|
| - }
|
| -
|
| - if (spellingSearchStart == spellingSearchEnd)
|
| - return; // nothing to search in
|
| -
|
| - // We go to the end of our first range instead of the start of it, just to be sure
|
| - // we don't get foiled by any word boundary problems at the start. It means we might
|
| - // do a tiny bit more searching.
|
| - Node* searchEndNodeAfterWrap = spellingSearchEnd.computeContainerNode();
|
| - int searchEndOffsetAfterWrap = spellingSearchEnd.offsetInContainerNode();
|
| -
|
| - int misspellingOffset = 0;
|
| - GrammarDetail grammarDetail;
|
| - int grammarPhraseOffset = 0;
|
| - Position grammarSearchStart, grammarSearchEnd;
|
| - String badGrammarPhrase;
|
| - String misspelledWord;
|
| -
|
| - bool isSpelling = true;
|
| - int foundOffset = 0;
|
| - String foundItem;
|
| - RefPtrWillBeRawPtr<Range> firstMisspellingRange = nullptr;
|
| - if (unifiedTextCheckerEnabled()) {
|
| - grammarSearchStart = spellingSearchStart;
|
| - grammarSearchEnd = spellingSearchEnd;
|
| - foundItem = TextCheckingHelper(spellCheckerClient(), spellingSearchStart, spellingSearchEnd).findFirstMisspellingOrBadGrammar(isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail);
|
| - if (isSpelling) {
|
| - misspelledWord = foundItem;
|
| - misspellingOffset = foundOffset;
|
| - } else {
|
| - badGrammarPhrase = foundItem;
|
| - grammarPhraseOffset = foundOffset;
|
| - }
|
| - } else {
|
| - misspelledWord = TextCheckingHelper(spellCheckerClient(), spellingSearchStart, spellingSearchEnd).findFirstMisspelling(misspellingOffset, false, firstMisspellingRange);
|
| - grammarSearchStart = spellingSearchStart;
|
| - grammarSearchEnd = spellingSearchEnd;
|
| - if (!misspelledWord.isEmpty()) {
|
| - // Stop looking at start of next misspelled word
|
| - CharacterIterator chars(grammarSearchStart, grammarSearchEnd);
|
| - chars.advance(misspellingOffset);
|
| - grammarSearchEnd = chars.startPosition();
|
| - }
|
| -
|
| - if (isGrammarCheckingEnabled())
|
| - badGrammarPhrase = TextCheckingHelper(spellCheckerClient(), grammarSearchStart, grammarSearchEnd).findFirstBadGrammar(grammarDetail, grammarPhraseOffset, false);
|
| - }
|
| -
|
| - // If we found neither bad grammar nor a misspelled word, wrap and try again (but don't bother if we started at the beginning of the
|
| - // block rather than at a selection).
|
| - if (startedWithSelection && !misspelledWord && !badGrammarPhrase) {
|
| - spellingSearchStart = createLegacyEditingPosition(topNode, 0);
|
| - // going until the end of the very first chunk we tested is far enough
|
| - spellingSearchEnd = createLegacyEditingPosition(searchEndNodeAfterWrap, searchEndOffsetAfterWrap);
|
| -
|
| - if (unifiedTextCheckerEnabled()) {
|
| - grammarSearchStart = spellingSearchStart;
|
| - grammarSearchEnd = spellingSearchEnd;
|
| - foundItem = TextCheckingHelper(spellCheckerClient(), spellingSearchStart, spellingSearchEnd).findFirstMisspellingOrBadGrammar(isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail);
|
| - if (isSpelling) {
|
| - misspelledWord = foundItem;
|
| - misspellingOffset = foundOffset;
|
| - } else {
|
| - badGrammarPhrase = foundItem;
|
| - grammarPhraseOffset = foundOffset;
|
| - }
|
| - } else {
|
| - misspelledWord = TextCheckingHelper(spellCheckerClient(), spellingSearchStart, spellingSearchEnd).findFirstMisspelling(misspellingOffset, false, firstMisspellingRange);
|
| - grammarSearchStart = spellingSearchStart;
|
| - grammarSearchEnd = spellingSearchEnd;
|
| - if (!misspelledWord.isEmpty()) {
|
| - // Stop looking at start of next misspelled word
|
| - CharacterIterator chars(grammarSearchStart, grammarSearchEnd);
|
| - chars.advance(misspellingOffset);
|
| - grammarSearchEnd = chars.startPosition();
|
| - }
|
| -
|
| - if (isGrammarCheckingEnabled())
|
| - badGrammarPhrase = TextCheckingHelper(spellCheckerClient(), grammarSearchStart, grammarSearchEnd).findFirstBadGrammar(grammarDetail, grammarPhraseOffset, false);
|
| - }
|
| - }
|
| -
|
| - if (!badGrammarPhrase.isEmpty()) {
|
| - // We found bad grammar. Since we only searched for bad grammar up to the first misspelled word, the bad grammar
|
| - // takes precedence and we ignore any potential misspelled word. Select the grammar detail, update the spelling
|
| - // panel, and store a marker so we draw the green squiggle later.
|
| -
|
| - ASSERT(badGrammarPhrase.length() > 0);
|
| - ASSERT(grammarDetail.location != -1 && grammarDetail.length > 0);
|
| -
|
| - // FIXME 4859190: This gets confused with doubled punctuation at the end of a paragraph
|
| - const EphemeralRange badGrammarRange = calculateCharacterSubrange(EphemeralRange(grammarSearchStart, grammarSearchEnd), grammarPhraseOffset + grammarDetail.location, grammarDetail.length);
|
| - frame().selection().setSelection(VisibleSelection(badGrammarRange));
|
| - frame().selection().revealSelection();
|
| -
|
| - frame().document()->markers().addMarker(badGrammarRange.startPosition(), badGrammarRange.endPosition(), DocumentMarker::Grammar, grammarDetail.userDescription);
|
| - } else if (!misspelledWord.isEmpty()) {
|
| - // We found a misspelling, but not any earlier bad grammar. Select the misspelling, update the spelling panel, and store
|
| - // a marker so we draw the red squiggle later.
|
| -
|
| - const EphemeralRange misspellingRange = calculateCharacterSubrange(EphemeralRange(spellingSearchStart, spellingSearchEnd), misspellingOffset, misspelledWord.length());
|
| - frame().selection().setSelection(VisibleSelection(misspellingRange, DOWNSTREAM));
|
| - frame().selection().revealSelection();
|
| -
|
| - spellCheckerClient().updateSpellingUIWithMisspelledWord(misspelledWord);
|
| - frame().document()->markers().addMarker(misspellingRange.startPosition(), misspellingRange.endPosition(), DocumentMarker::Spelling);
|
| - }
|
| -}
|
| -
|
| -void SpellChecker::showSpellingGuessPanel()
|
| -{
|
| - if (spellCheckerClient().spellingUIIsShowing()) {
|
| - spellCheckerClient().showSpellingUI(false);
|
| - return;
|
| - }
|
| -
|
| - advanceToNextMisspelling(true);
|
| - spellCheckerClient().showSpellingUI(true);
|
| -}
|
| -
|
| -void SpellChecker::clearMisspellingsAndBadGrammar(const VisibleSelection &movingSelection)
|
| -{
|
| - removeMarkers(movingSelection, DocumentMarker::MisspellingMarkers());
|
| -}
|
| -
|
| -void SpellChecker::markMisspellingsAndBadGrammar(const VisibleSelection &movingSelection)
|
| -{
|
| - markMisspellingsAndBadGrammar(movingSelection, isContinuousSpellCheckingEnabled() && isGrammarCheckingEnabled(), movingSelection);
|
| -}
|
| -
|
| -void SpellChecker::markMisspellingsAfterLineBreak(const VisibleSelection& wordSelection)
|
| -{
|
| - TRACE_EVENT0("blink", "SpellChecker::markMisspellingsAfterLineBreak");
|
| -
|
| - if (unifiedTextCheckerEnabled()) {
|
| - TextCheckingTypeMask textCheckingOptions = 0;
|
| -
|
| - if (isContinuousSpellCheckingEnabled())
|
| - textCheckingOptions |= TextCheckingTypeSpelling;
|
| -
|
| - if (isGrammarCheckingEnabled())
|
| - textCheckingOptions |= TextCheckingTypeGrammar;
|
| -
|
| - VisibleSelection wholeParagraph(
|
| - startOfParagraph(wordSelection.visibleStart()),
|
| - endOfParagraph(wordSelection.visibleEnd()));
|
| -
|
| - markAllMisspellingsAndBadGrammarInRanges(
|
| - textCheckingOptions, wordSelection.toNormalizedRange().get(),
|
| - wholeParagraph.toNormalizedRange().get());
|
| - } else {
|
| - RefPtrWillBeRawPtr<Range> misspellingRange = nullptr;
|
| - markMisspellings(wordSelection, misspellingRange);
|
| - }
|
| -}
|
| -
|
| -void SpellChecker::markMisspellingsAfterTypingToWord(const VisiblePosition &wordStart, const VisibleSelection& selectionAfterTyping)
|
| -{
|
| - TRACE_EVENT0("blink", "SpellChecker::markMisspellingsAfterTypingToWord");
|
| -
|
| - if (unifiedTextCheckerEnabled()) {
|
| - TextCheckingTypeMask textCheckingOptions = 0;
|
| -
|
| - if (isContinuousSpellCheckingEnabled())
|
| - textCheckingOptions |= TextCheckingTypeSpelling;
|
| -
|
| - if (!(textCheckingOptions & TextCheckingTypeSpelling))
|
| - return;
|
| -
|
| - if (isGrammarCheckingEnabled())
|
| - textCheckingOptions |= TextCheckingTypeGrammar;
|
| -
|
| - VisibleSelection adjacentWords = VisibleSelection(startOfWord(wordStart, LeftWordIfOnBoundary), endOfWord(wordStart, RightWordIfOnBoundary));
|
| - if (textCheckingOptions & TextCheckingTypeGrammar) {
|
| - VisibleSelection selectedSentence = VisibleSelection(startOfSentence(wordStart), endOfSentence(wordStart));
|
| - markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, adjacentWords.toNormalizedRange().get(), selectedSentence.toNormalizedRange().get());
|
| - } else {
|
| - markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, adjacentWords.toNormalizedRange().get(), adjacentWords.toNormalizedRange().get());
|
| - }
|
| - return;
|
| - }
|
| -
|
| - if (!isContinuousSpellCheckingEnabled())
|
| - return;
|
| -
|
| - // Check spelling of one word
|
| - RefPtrWillBeRawPtr<Range> misspellingRange = nullptr;
|
| - markMisspellings(VisibleSelection(startOfWord(wordStart, LeftWordIfOnBoundary), endOfWord(wordStart, RightWordIfOnBoundary)), misspellingRange);
|
| -
|
| - if (!misspellingRange || !isGrammarCheckingEnabled())
|
| - return;
|
| -
|
| - // Check grammar of entire sentence
|
| - markBadGrammar(VisibleSelection(startOfSentence(wordStart), endOfSentence(wordStart)));
|
| -}
|
| -
|
| -void SpellChecker::markMisspellingsOrBadGrammar(const VisibleSelection& selection, bool checkSpelling, RefPtrWillBeRawPtr<Range>& firstMisspellingRange)
|
| -{
|
| - // This function is called with a selection already expanded to word boundaries.
|
| - // Might be nice to assert that here.
|
| -
|
| - // This function is used only for as-you-type checking, so if that's off we do nothing. Note that
|
| - // grammar checking can only be on if spell checking is also on.
|
| - if (!isContinuousSpellCheckingEnabled())
|
| - return;
|
| -
|
| - TRACE_EVENT0("blink", "SpellChecker::markMisspellingsOrBadGrammar");
|
| -
|
| - const EphemeralRange range = selection.toNormalizedEphemeralRange();
|
| - if (range.isNull())
|
| - return;
|
| -
|
| - // If we're not in an editable node, bail.
|
| - Node* editableNode = range.startPosition().computeContainerNode();
|
| - if (!editableNode || !editableNode->hasEditableStyle())
|
| - return;
|
| -
|
| - if (!isSpellCheckingEnabledFor(editableNode))
|
| - return;
|
| -
|
| - TextCheckingHelper checker(spellCheckerClient(), range.startPosition(), range.endPosition());
|
| - if (checkSpelling)
|
| - checker.markAllMisspellings(firstMisspellingRange);
|
| - else if (isGrammarCheckingEnabled())
|
| - checker.markAllBadGrammar();
|
| -}
|
| -
|
| -bool SpellChecker::isSpellCheckingEnabledFor(Node* node) const
|
| -{
|
| - if (!node)
|
| - return false;
|
| - const Element* focusedElement = node->isElementNode() ? toElement(node) : node->parentElement();
|
| - if (!focusedElement)
|
| - return false;
|
| - return focusedElement->isSpellCheckingEnabled();
|
| -}
|
| -
|
| -bool SpellChecker::isSpellCheckingEnabledInFocusedNode() const
|
| -{
|
| - return isSpellCheckingEnabledFor(frame().selection().start().anchorNode());
|
| -}
|
| -
|
| -void SpellChecker::markMisspellings(const VisibleSelection& selection, RefPtrWillBeRawPtr<Range>& firstMisspellingRange)
|
| -{
|
| - markMisspellingsOrBadGrammar(selection, true, firstMisspellingRange);
|
| -}
|
| -
|
| -void SpellChecker::markBadGrammar(const VisibleSelection& selection)
|
| -{
|
| - RefPtrWillBeRawPtr<Range> firstMisspellingRange = nullptr;
|
| - markMisspellingsOrBadGrammar(selection, false, firstMisspellingRange);
|
| -}
|
| -
|
| -void SpellChecker::markAllMisspellingsAndBadGrammarInRanges(TextCheckingTypeMask textCheckingOptions, Range* spellingRange, Range* grammarRange)
|
| -{
|
| - ASSERT(unifiedTextCheckerEnabled());
|
| -
|
| - bool shouldMarkGrammar = textCheckingOptions & TextCheckingTypeGrammar;
|
| -
|
| - // This function is called with selections already expanded to word boundaries.
|
| - if (!spellingRange || (shouldMarkGrammar && !grammarRange))
|
| - return;
|
| -
|
| - // If we're not in an editable node, bail.
|
| - Node* editableNode = spellingRange->startContainer();
|
| - if (!editableNode || !editableNode->hasEditableStyle())
|
| - return;
|
| -
|
| - if (!isSpellCheckingEnabledFor(editableNode))
|
| - return;
|
| -
|
| - Range* rangeToCheck = shouldMarkGrammar ? grammarRange : spellingRange;
|
| - TextCheckingParagraph fullParagraphToCheck(rangeToCheck);
|
| -
|
| - bool asynchronous = frame().settings() && frame().settings()->asynchronousSpellCheckingEnabled();
|
| - chunkAndMarkAllMisspellingsAndBadGrammar(textCheckingOptions, fullParagraphToCheck, asynchronous);
|
| -}
|
| -
|
| -void SpellChecker::chunkAndMarkAllMisspellingsAndBadGrammar(Node* node)
|
| -{
|
| - TRACE_EVENT0("blink", "SpellChecker::chunkAndMarkAllMisspellingsAndBadGrammar");
|
| - if (!node)
|
| - return;
|
| - RefPtrWillBeRawPtr<Range> rangeToCheck = Range::create(*frame().document(), firstPositionInNode(node), lastPositionInNode(node));
|
| - TextCheckingParagraph textToCheck(rangeToCheck, rangeToCheck);
|
| - bool asynchronous = true;
|
| - chunkAndMarkAllMisspellingsAndBadGrammar(resolveTextCheckingTypeMask(TextCheckingTypeSpelling | TextCheckingTypeGrammar), textToCheck, asynchronous);
|
| -}
|
| -
|
| -void SpellChecker::chunkAndMarkAllMisspellingsAndBadGrammar(TextCheckingTypeMask textCheckingOptions, const TextCheckingParagraph& fullParagraphToCheck, bool asynchronous)
|
| -{
|
| - if (fullParagraphToCheck.isRangeEmpty() || fullParagraphToCheck.isEmpty())
|
| - return;
|
| -
|
| - // Since the text may be quite big chunk it up and adjust to the sentence boundary.
|
| - const int kChunkSize = 16 * 1024;
|
| - int start = fullParagraphToCheck.checkingStart();
|
| - int end = fullParagraphToCheck.checkingEnd();
|
| - start = std::min(start, end);
|
| - end = std::max(start, end);
|
| - const int kNumChunksToCheck = asynchronous ? (end - start + kChunkSize - 1) / (kChunkSize) : 1;
|
| - int currentChunkStart = start;
|
| - RefPtrWillBeRawPtr<Range> checkRange = fullParagraphToCheck.checkingRange();
|
| - if (kNumChunksToCheck == 1 && asynchronous) {
|
| - markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, checkRange.get(), checkRange.get(), asynchronous, 0);
|
| - return;
|
| - }
|
| -
|
| - for (int iter = 0; iter < kNumChunksToCheck; ++iter) {
|
| - checkRange = fullParagraphToCheck.subrange(currentChunkStart, kChunkSize);
|
| - expandRangeToSentenceBoundary(*checkRange);
|
| -
|
| - int checkingLength = 0;
|
| - markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, checkRange.get(), checkRange.get(), asynchronous, iter, &checkingLength);
|
| - currentChunkStart += checkingLength;
|
| - }
|
| -}
|
| -
|
| -void SpellChecker::markAllMisspellingsAndBadGrammarInRanges(TextCheckingTypeMask textCheckingOptions, Range* checkRange, Range* paragraphRange, bool asynchronous, int requestNumber, int* checkingLength)
|
| -{
|
| - TextCheckingParagraph sentenceToCheck(checkRange, paragraphRange);
|
| - if (checkingLength)
|
| - *checkingLength = sentenceToCheck.checkingLength();
|
| -
|
| - RefPtrWillBeRawPtr<SpellCheckRequest> request = SpellCheckRequest::create(resolveTextCheckingTypeMask(textCheckingOptions), TextCheckingProcessBatch, checkRange, paragraphRange, requestNumber);
|
| - if (!request)
|
| - return;
|
| -
|
| - if (asynchronous) {
|
| - m_spellCheckRequester->requestCheckingFor(request);
|
| - } else {
|
| - Vector<TextCheckingResult> results;
|
| - checkTextOfParagraph(textChecker(), sentenceToCheck.text(), resolveTextCheckingTypeMask(textCheckingOptions), results);
|
| - markAndReplaceFor(request, results);
|
| - }
|
| -}
|
| -
|
| -void SpellChecker::markAndReplaceFor(PassRefPtrWillBeRawPtr<SpellCheckRequest> request, const Vector<TextCheckingResult>& results)
|
| -{
|
| - TRACE_EVENT0("blink", "SpellChecker::markAndReplaceFor");
|
| - ASSERT(request);
|
| -
|
| - TextCheckingTypeMask textCheckingOptions = request->data().mask();
|
| - TextCheckingParagraph paragraph(request->checkingRange(), request->paragraphRange());
|
| -
|
| - bool shouldMarkSpelling = textCheckingOptions & TextCheckingTypeSpelling;
|
| - bool shouldMarkGrammar = textCheckingOptions & TextCheckingTypeGrammar;
|
| -
|
| - // Expand the range to encompass entire paragraphs, since text checking needs that much context.
|
| - int selectionOffset = 0;
|
| - int ambiguousBoundaryOffset = -1;
|
| - bool selectionChanged = false;
|
| - bool restoreSelectionAfterChange = false;
|
| - bool adjustSelectionForParagraphBoundaries = false;
|
| -
|
| - if (shouldMarkSpelling) {
|
| - if (frame().selection().isCaret()) {
|
| - // Attempt to save the caret position so we can restore it later if needed
|
| - Position caretPosition = frame().selection().end();
|
| - selectionOffset = paragraph.offsetTo(caretPosition, ASSERT_NO_EXCEPTION);
|
| - restoreSelectionAfterChange = true;
|
| - if (selectionOffset > 0 && (static_cast<unsigned>(selectionOffset) > paragraph.text().length() || paragraph.textCharAt(selectionOffset - 1) == newlineCharacter))
|
| - adjustSelectionForParagraphBoundaries = true;
|
| - if (selectionOffset > 0 && static_cast<unsigned>(selectionOffset) <= paragraph.text().length() && isAmbiguousBoundaryCharacter(paragraph.textCharAt(selectionOffset - 1)))
|
| - ambiguousBoundaryOffset = selectionOffset - 1;
|
| - }
|
| - }
|
| -
|
| - for (unsigned i = 0; i < results.size(); i++) {
|
| - int spellingRangeEndOffset = paragraph.checkingEnd();
|
| - const TextCheckingResult* result = &results[i];
|
| - int resultLocation = result->location + paragraph.checkingStart();
|
| - int resultLength = result->length;
|
| - bool resultEndsAtAmbiguousBoundary = ambiguousBoundaryOffset >= 0 && resultLocation + resultLength == ambiguousBoundaryOffset;
|
| -
|
| - // Only mark misspelling if:
|
| - // 1. Current text checking isn't done for autocorrection, in which case shouldMarkSpelling is false.
|
| - // 2. Result falls within spellingRange.
|
| - // 3. The word in question doesn't end at an ambiguous boundary. For instance, we would not mark
|
| - // "wouldn'" as misspelled right after apostrophe is typed.
|
| - if (shouldMarkSpelling && result->decoration == TextDecorationTypeSpelling && resultLocation >= paragraph.checkingStart() && resultLocation + resultLength <= spellingRangeEndOffset && !resultEndsAtAmbiguousBoundary) {
|
| - ASSERT(resultLength > 0 && resultLocation >= 0);
|
| - const EphemeralRange misspellingRange = calculateCharacterSubrange(EphemeralRange(paragraph.paragraphRange().get()), resultLocation, resultLength);
|
| - frame().document()->markers().addMarker(misspellingRange.startPosition(), misspellingRange.endPosition(), DocumentMarker::Spelling, result->replacement, result->hash);
|
| - } else if (shouldMarkGrammar && result->decoration == TextDecorationTypeGrammar && paragraph.checkingRangeCovers(resultLocation, resultLength)) {
|
| - ASSERT(resultLength > 0 && resultLocation >= 0);
|
| - for (unsigned j = 0; j < result->details.size(); j++) {
|
| - const GrammarDetail* detail = &result->details[j];
|
| - ASSERT(detail->length > 0 && detail->location >= 0);
|
| - if (paragraph.checkingRangeCovers(resultLocation + detail->location, detail->length)) {
|
| - const EphemeralRange badGrammarRange = calculateCharacterSubrange(EphemeralRange(paragraph.paragraphRange().get()), resultLocation + detail->location, detail->length);
|
| - frame().document()->markers().addMarker(badGrammarRange.startPosition(), badGrammarRange.endPosition(), DocumentMarker::Grammar, detail->userDescription, result->hash);
|
| - }
|
| - }
|
| - } else if (result->decoration == TextDecorationTypeInvisibleSpellcheck && resultLocation >= paragraph.checkingStart() && resultLocation + resultLength <= spellingRangeEndOffset) {
|
| - ASSERT(resultLength > 0 && resultLocation >= 0);
|
| - const EphemeralRange invisibleSpellcheckRange = calculateCharacterSubrange(EphemeralRange(paragraph.paragraphRange().get()), resultLocation, resultLength);
|
| - frame().document()->markers().addMarker(invisibleSpellcheckRange.startPosition(), invisibleSpellcheckRange.endPosition(), DocumentMarker::InvisibleSpellcheck, result->replacement, result->hash);
|
| - }
|
| - }
|
| -
|
| - if (selectionChanged) {
|
| - TextCheckingParagraph extendedParagraph(paragraph);
|
| - // Restore the caret position if we have made any replacements
|
| - extendedParagraph.expandRangeToNextEnd();
|
| - if (restoreSelectionAfterChange && selectionOffset >= 0 && selectionOffset <= extendedParagraph.rangeLength()) {
|
| - RefPtrWillBeRawPtr<Range> selectionRange = extendedParagraph.subrange(0, selectionOffset);
|
| - frame().selection().moveTo(selectionRange->endPosition(), DOWNSTREAM);
|
| - if (adjustSelectionForParagraphBoundaries)
|
| - frame().selection().modify(FrameSelection::AlterationMove, DirectionForward, CharacterGranularity);
|
| - } else {
|
| - // If this fails for any reason, the fallback is to go one position beyond the last replacement
|
| - frame().selection().moveTo(frame().selection().selection().visibleEnd());
|
| - frame().selection().modify(FrameSelection::AlterationMove, DirectionForward, CharacterGranularity);
|
| - }
|
| - }
|
| -}
|
| -
|
| -void SpellChecker::markMisspellingsAndBadGrammar(const VisibleSelection& spellingSelection, bool markGrammar, const VisibleSelection& grammarSelection)
|
| -{
|
| - if (unifiedTextCheckerEnabled()) {
|
| - if (!isContinuousSpellCheckingEnabled())
|
| - return;
|
| -
|
| - // markMisspellingsAndBadGrammar() is triggered by selection change, in which case we check spelling and grammar, but don't autocorrect misspellings.
|
| - TextCheckingTypeMask textCheckingOptions = TextCheckingTypeSpelling;
|
| - if (markGrammar && isGrammarCheckingEnabled())
|
| - textCheckingOptions |= TextCheckingTypeGrammar;
|
| - markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, spellingSelection.toNormalizedRange().get(), grammarSelection.toNormalizedRange().get());
|
| - return;
|
| - }
|
| -
|
| - RefPtrWillBeRawPtr<Range> firstMisspellingRange = nullptr;
|
| - markMisspellings(spellingSelection, firstMisspellingRange);
|
| - if (markGrammar)
|
| - markBadGrammar(grammarSelection);
|
| -}
|
| -
|
| -void SpellChecker::updateMarkersForWordsAffectedByEditing(bool doNotRemoveIfSelectionAtWordBoundary)
|
| -{
|
| - if (textChecker().shouldEraseMarkersAfterChangeSelection(TextCheckingTypeSpelling))
|
| - return;
|
| -
|
| - TRACE_EVENT0("blink", "SpellChecker::updateMarkersForWordsAffectedByEditing");
|
| -
|
| - // We want to remove the markers from a word if an editing command will change the word. This can happen in one of
|
| - // several scenarios:
|
| - // 1. Insert in the middle of a word.
|
| - // 2. Appending non whitespace at the beginning of word.
|
| - // 3. Appending non whitespace at the end of word.
|
| - // Note that, appending only whitespaces at the beginning or end of word won't change the word, so we don't need to
|
| - // remove the markers on that word.
|
| - // Of course, if current selection is a range, we potentially will edit two words that fall on the boundaries of
|
| - // selection, and remove words between the selection boundaries.
|
| - //
|
| - VisiblePosition startOfSelection = frame().selection().selection().visibleStart();
|
| - VisiblePosition endOfSelection = frame().selection().selection().visibleEnd();
|
| - if (startOfSelection.isNull())
|
| - return;
|
| - // First word is the word that ends after or on the start of selection.
|
| - VisiblePosition startOfFirstWord = startOfWord(startOfSelection, LeftWordIfOnBoundary);
|
| - VisiblePosition endOfFirstWord = endOfWord(startOfSelection, LeftWordIfOnBoundary);
|
| - // Last word is the word that begins before or on the end of selection
|
| - VisiblePosition startOfLastWord = startOfWord(endOfSelection, RightWordIfOnBoundary);
|
| - VisiblePosition endOfLastWord = endOfWord(endOfSelection, RightWordIfOnBoundary);
|
| -
|
| - if (startOfFirstWord.isNull()) {
|
| - startOfFirstWord = startOfWord(startOfSelection, RightWordIfOnBoundary);
|
| - endOfFirstWord = endOfWord(startOfSelection, RightWordIfOnBoundary);
|
| - }
|
| -
|
| - if (endOfLastWord.isNull()) {
|
| - startOfLastWord = startOfWord(endOfSelection, LeftWordIfOnBoundary);
|
| - endOfLastWord = endOfWord(endOfSelection, LeftWordIfOnBoundary);
|
| - }
|
| -
|
| - // If doNotRemoveIfSelectionAtWordBoundary is true, and first word ends at the start of selection,
|
| - // we choose next word as the first word.
|
| - if (doNotRemoveIfSelectionAtWordBoundary && endOfFirstWord.deepEquivalent() == startOfSelection.deepEquivalent()) {
|
| - startOfFirstWord = nextWordPosition(startOfFirstWord);
|
| - endOfFirstWord = endOfWord(startOfFirstWord, RightWordIfOnBoundary);
|
| - if (startOfFirstWord.deepEquivalent() == endOfSelection.deepEquivalent())
|
| - return;
|
| - }
|
| -
|
| - // If doNotRemoveIfSelectionAtWordBoundary is true, and last word begins at the end of selection,
|
| - // we choose previous word as the last word.
|
| - if (doNotRemoveIfSelectionAtWordBoundary && startOfLastWord.deepEquivalent() == endOfSelection.deepEquivalent()) {
|
| - startOfLastWord = previousWordPosition(startOfLastWord);
|
| - endOfLastWord = endOfWord(startOfLastWord, RightWordIfOnBoundary);
|
| - if (endOfLastWord.deepEquivalent() == startOfSelection.deepEquivalent())
|
| - return;
|
| - }
|
| -
|
| - if (startOfFirstWord.isNull() || endOfFirstWord.isNull() || startOfLastWord.isNull() || endOfLastWord.isNull())
|
| - return;
|
| -
|
| - // Now we remove markers on everything between startOfFirstWord and endOfLastWord.
|
| - // However, if an autocorrection change a single word to multiple words, we want to remove correction mark from all the
|
| - // resulted words even we only edit one of them. For example, assuming autocorrection changes "avantgarde" to "avant
|
| - // garde", we will have CorrectionIndicator marker on both words and on the whitespace between them. If we then edit garde,
|
| - // we would like to remove the marker from word "avant" and whitespace as well. So we need to get the continous range of
|
| - // of marker that contains the word in question, and remove marker on that whole range.
|
| - Document* document = frame().document();
|
| - ASSERT(document);
|
| - Node* startNode = startOfFirstWord.deepEquivalent().computeContainerNode();
|
| - int startOffset = startOfFirstWord.deepEquivalent().computeOffsetInContainerNode();
|
| - int endOffset = endOfLastWord.deepEquivalent().computeOffsetInContainerNode();
|
| - document->markers().removeMarkers(startNode, startOffset, endOffset - startOffset, DocumentMarker::MisspellingMarkers(), DocumentMarkerController::RemovePartiallyOverlappingMarker);
|
| -}
|
| -
|
| -void SpellChecker::didEndEditingOnTextField(Element* e)
|
| -{
|
| - TRACE_EVENT0("blink", "SpellChecker::didEndEditingOnTextField");
|
| -
|
| - // Remove markers when deactivating a selection in an <input type="text"/>.
|
| - // Prevent new ones from appearing too.
|
| - m_spellCheckRequester->cancelCheck();
|
| - HTMLTextFormControlElement* textFormControlElement = toHTMLTextFormControlElement(e);
|
| - HTMLElement* innerEditor = textFormControlElement->innerEditorElement();
|
| - DocumentMarker::MarkerTypes markerTypes(DocumentMarker::Spelling);
|
| - if (isGrammarCheckingEnabled() || unifiedTextCheckerEnabled())
|
| - markerTypes.add(DocumentMarker::Grammar);
|
| - for (Node& node : NodeTraversal::inclusiveDescendantsOf(*innerEditor))
|
| - frame().document()->markers().removeMarkers(&node, markerTypes);
|
| -}
|
| -
|
| -void SpellChecker::replaceMisspelledRange(const String& text)
|
| -{
|
| - EphemeralRange caretRange = frame().selection().selection().toNormalizedEphemeralRange();
|
| - if (caretRange.isNull())
|
| - return;
|
| - DocumentMarkerVector markers = frame().document()->markers().markersInRange(caretRange, DocumentMarker::MisspellingMarkers());
|
| - if (markers.size() < 1 || markers[0]->startOffset() >= markers[0]->endOffset())
|
| - return;
|
| - EphemeralRange markerRange = EphemeralRange(Position(caretRange.startPosition().computeContainerNode(), markers[0]->startOffset()), Position(caretRange.endPosition().computeContainerNode(), markers[0]->endOffset()));
|
| - if (markerRange.isNull())
|
| - return;
|
| - frame().selection().setSelection(VisibleSelection(markerRange), CharacterGranularity);
|
| - frame().editor().replaceSelectionWithText(text, false, false);
|
| -}
|
| -
|
| -void SpellChecker::respondToChangedSelection(const VisibleSelection& oldSelection, FrameSelection::SetSelectionOptions options)
|
| -{
|
| - TRACE_EVENT0("blink", "SpellChecker::respondToChangedSelection");
|
| -
|
| - bool closeTyping = options & FrameSelection::CloseTyping;
|
| - bool isContinuousSpellCheckingEnabled = this->isContinuousSpellCheckingEnabled();
|
| - bool isContinuousGrammarCheckingEnabled = isContinuousSpellCheckingEnabled && isGrammarCheckingEnabled();
|
| - if (isContinuousSpellCheckingEnabled) {
|
| - VisibleSelection newAdjacentWords;
|
| - VisibleSelection newSelectedSentence;
|
| - bool caretBrowsing = frame().settings() && frame().settings()->caretBrowsingEnabled();
|
| - const VisibleSelection newSelection = frame().selection().selection();
|
| - if (isSelectionInTextFormControl(newSelection)) {
|
| - Position newStart = newSelection.start();
|
| - newAdjacentWords.setWithoutValidation(HTMLTextFormControlElement::startOfWord(newStart), HTMLTextFormControlElement::endOfWord(newStart));
|
| - if (isContinuousGrammarCheckingEnabled)
|
| - newSelectedSentence.setWithoutValidation(HTMLTextFormControlElement::startOfSentence(newStart), HTMLTextFormControlElement::endOfSentence(newStart));
|
| - } else if (newSelection.isContentEditable() || caretBrowsing) {
|
| - VisiblePosition newStart(newSelection.visibleStart());
|
| - newAdjacentWords = VisibleSelection(startOfWord(newStart, LeftWordIfOnBoundary), endOfWord(newStart, RightWordIfOnBoundary));
|
| - if (isContinuousGrammarCheckingEnabled)
|
| - newSelectedSentence = VisibleSelection(startOfSentence(newStart), endOfSentence(newStart));
|
| - }
|
| -
|
| - // Don't check spelling and grammar if the change of selection is triggered by spelling correction itself.
|
| - bool shouldCheckSpellingAndGrammar = !(options & FrameSelection::SpellCorrectionTriggered);
|
| -
|
| - // When typing we check spelling elsewhere, so don't redo it here.
|
| - // If this is a change in selection resulting from a delete operation,
|
| - // oldSelection may no longer be in the document.
|
| - // FIXME(http://crbug.com/382809): if oldSelection is on a textarea
|
| - // element, we cause synchronous layout.
|
| - if (shouldCheckSpellingAndGrammar
|
| - && closeTyping
|
| - && !isSelectionInTextField(oldSelection)
|
| - && (isSelectionInTextArea(oldSelection) || oldSelection.isContentEditable())
|
| - && oldSelection.start().inDocument()) {
|
| - spellCheckOldSelection(oldSelection, newAdjacentWords);
|
| - }
|
| -
|
| - // FIXME(http://crbug.com/382809):
|
| - // shouldEraseMarkersAfterChangeSelection is true, we cause synchronous
|
| - // layout.
|
| - if (textChecker().shouldEraseMarkersAfterChangeSelection(TextCheckingTypeSpelling))
|
| - removeMarkers(newAdjacentWords, DocumentMarker::Spelling);
|
| - if (textChecker().shouldEraseMarkersAfterChangeSelection(TextCheckingTypeGrammar))
|
| - removeMarkers(newSelectedSentence, DocumentMarker::Grammar);
|
| - }
|
| -
|
| - // When continuous spell checking is off, existing markers disappear after the selection changes.
|
| - if (!isContinuousSpellCheckingEnabled)
|
| - frame().document()->markers().removeMarkers(DocumentMarker::Spelling);
|
| - if (!isContinuousGrammarCheckingEnabled)
|
| - frame().document()->markers().removeMarkers(DocumentMarker::Grammar);
|
| -}
|
| -
|
| -void SpellChecker::removeSpellingMarkers()
|
| -{
|
| - frame().document()->markers().removeMarkers(DocumentMarker::MisspellingMarkers());
|
| -}
|
| -
|
| -void SpellChecker::removeSpellingMarkersUnderWords(const Vector<String>& words)
|
| -{
|
| - MarkerRemoverPredicate removerPredicate(words);
|
| -
|
| - DocumentMarkerController& markerController = frame().document()->markers();
|
| - markerController.removeMarkers(removerPredicate);
|
| - markerController.repaintMarkers();
|
| -}
|
| -
|
| -void SpellChecker::spellCheckAfterBlur()
|
| -{
|
| - if (!frame().selection().selection().isContentEditable())
|
| - return;
|
| -
|
| - if (isSelectionInTextField(frame().selection().selection())) {
|
| - // textFieldDidEndEditing() and textFieldDidBeginEditing() handle this.
|
| - return;
|
| - }
|
| -
|
| - VisibleSelection empty;
|
| - spellCheckOldSelection(frame().selection().selection(), empty);
|
| -}
|
| -
|
| -void SpellChecker::spellCheckOldSelection(const VisibleSelection& oldSelection, const VisibleSelection& newAdjacentWords)
|
| -{
|
| - TRACE_EVENT0("blink", "SpellChecker::spellCheckOldSelection");
|
| -
|
| - VisiblePosition oldStart(oldSelection.visibleStart());
|
| - VisibleSelection oldAdjacentWords = VisibleSelection(startOfWord(oldStart, LeftWordIfOnBoundary), endOfWord(oldStart, RightWordIfOnBoundary));
|
| - if (!VisibleSelection::InDOMTree::equalSelections(oldAdjacentWords, newAdjacentWords)) {
|
| - if (isContinuousSpellCheckingEnabled() && isGrammarCheckingEnabled()) {
|
| - VisibleSelection selectedSentence = VisibleSelection(startOfSentence(oldStart), endOfSentence(oldStart));
|
| - markMisspellingsAndBadGrammar(oldAdjacentWords, true, selectedSentence);
|
| - } else {
|
| - markMisspellingsAndBadGrammar(oldAdjacentWords, false, oldAdjacentWords);
|
| - }
|
| - }
|
| -}
|
| -
|
| -static Node* findFirstMarkable(Node* node)
|
| -{
|
| - while (node) {
|
| - if (!node->layoutObject())
|
| - return 0;
|
| - if (node->layoutObject()->isText())
|
| - return node;
|
| - if (node->layoutObject()->isTextControl())
|
| - node = toLayoutTextControl(node->layoutObject())->textFormControlElement()->visiblePositionForIndex(1).deepEquivalent().anchorNode();
|
| - else if (node->hasChildren())
|
| - node = node->firstChild();
|
| - else
|
| - node = node->nextSibling();
|
| - }
|
| -
|
| - return 0;
|
| -}
|
| -
|
| -bool SpellChecker::selectionStartHasMarkerFor(DocumentMarker::MarkerType markerType, int from, int length) const
|
| -{
|
| - Node* node = findFirstMarkable(frame().selection().start().anchorNode());
|
| - if (!node)
|
| - return false;
|
| -
|
| - unsigned startOffset = static_cast<unsigned>(from);
|
| - unsigned endOffset = static_cast<unsigned>(from + length);
|
| - DocumentMarkerVector markers = frame().document()->markers().markersFor(node);
|
| - for (size_t i = 0; i < markers.size(); ++i) {
|
| - DocumentMarker* marker = markers[i];
|
| - if (marker->startOffset() <= startOffset && endOffset <= marker->endOffset() && marker->type() == markerType)
|
| - return true;
|
| - }
|
| -
|
| - return false;
|
| -}
|
| -
|
| -bool SpellChecker::selectionStartHasSpellingMarkerFor(int from, int length) const
|
| -{
|
| - return selectionStartHasMarkerFor(DocumentMarker::Spelling, from, length);
|
| -}
|
| -
|
| -TextCheckingTypeMask SpellChecker::resolveTextCheckingTypeMask(TextCheckingTypeMask textCheckingOptions)
|
| -{
|
| - bool shouldMarkSpelling = textCheckingOptions & TextCheckingTypeSpelling;
|
| - bool shouldMarkGrammar = textCheckingOptions & TextCheckingTypeGrammar;
|
| -
|
| - TextCheckingTypeMask checkingTypes = 0;
|
| - if (shouldMarkSpelling)
|
| - checkingTypes |= TextCheckingTypeSpelling;
|
| - if (shouldMarkGrammar)
|
| - checkingTypes |= TextCheckingTypeGrammar;
|
| -
|
| - return checkingTypes;
|
| -}
|
| -
|
| -void SpellChecker::removeMarkers(const VisibleSelection& selection, DocumentMarker::MarkerTypes markerTypes)
|
| -{
|
| - const EphemeralRange range = selection.toNormalizedEphemeralRange();
|
| - if (range.isNull())
|
| - return;
|
| - frame().document()->markers().removeMarkers(range, markerTypes);
|
| -}
|
| -
|
| -bool SpellChecker::unifiedTextCheckerEnabled() const
|
| -{
|
| - return blink::unifiedTextCheckerEnabled(m_frame);
|
| -}
|
| -
|
| -void SpellChecker::cancelCheck()
|
| -{
|
| - m_spellCheckRequester->cancelCheck();
|
| -}
|
| -
|
| -void SpellChecker::requestTextChecking(const Element& element)
|
| -{
|
| - const EphemeralRange range = EphemeralRange::rangeOfContents(element);
|
| - RefPtrWillBeRawPtr<Range> rangeToCheck = Range::create(element.document(), range.startPosition(), range.endPosition());
|
| - m_spellCheckRequester->requestCheckingFor(SpellCheckRequest::create(TextCheckingTypeSpelling | TextCheckingTypeGrammar, TextCheckingProcessBatch, rangeToCheck, rangeToCheck));
|
| -}
|
| -
|
| -DEFINE_TRACE(SpellChecker)
|
| -{
|
| - visitor->trace(m_frame);
|
| - visitor->trace(m_spellCheckRequester);
|
| -}
|
| -
|
| -} // namespace blink
|
|
|