diff options
Diffstat (limited to 'chromium/third_party/blink/renderer/core/editing/spellcheck/hot_mode_spell_check_requester.cc')
-rw-r--r-- | chromium/third_party/blink/renderer/core/editing/spellcheck/hot_mode_spell_check_requester.cc | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/chromium/third_party/blink/renderer/core/editing/spellcheck/hot_mode_spell_check_requester.cc b/chromium/third_party/blink/renderer/core/editing/spellcheck/hot_mode_spell_check_requester.cc new file mode 100644 index 00000000000..b9731680d1a --- /dev/null +++ b/chromium/third_party/blink/renderer/core/editing/spellcheck/hot_mode_spell_check_requester.cc @@ -0,0 +1,128 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/editing/spellcheck/hot_mode_spell_check_requester.h" + +#include "third_party/blink/renderer/core/editing/commands/composite_edit_command.h" +#include "third_party/blink/renderer/core/editing/commands/typing_command.h" +#include "third_party/blink/renderer/core/editing/editing_utilities.h" +#include "third_party/blink/renderer/core/editing/editor.h" +#include "third_party/blink/renderer/core/editing/ephemeral_range.h" +#include "third_party/blink/renderer/core/editing/frame_selection.h" +#include "third_party/blink/renderer/core/editing/iterators/backwards_character_iterator.h" +#include "third_party/blink/renderer/core/editing/iterators/character_iterator.h" +#include "third_party/blink/renderer/core/editing/markers/document_marker_controller.h" +#include "third_party/blink/renderer/core/editing/selection_template.h" +#include "third_party/blink/renderer/core/editing/spellcheck/spell_check_requester.h" +#include "third_party/blink/renderer/core/editing/spellcheck/spell_checker.h" +#include "third_party/blink/renderer/core/editing/visible_position.h" + +namespace blink { + +namespace { + +const int kHotModeCheckAllThreshold = 128; +const int kHotModeChunkSize = 1024; + +EphemeralRange AdjacentWordIfExists(const Position& pos) { + const VisiblePosition& visible_pos = CreateVisiblePosition(pos); + const VisiblePosition& word_start = PreviousWordPosition(visible_pos); + if (word_start.IsNull()) + return EphemeralRange(); + const VisiblePosition& word_end = EndOfWord(word_start); + if (word_end.IsNull()) + return EphemeralRange(); + if (ComparePositions(visible_pos, word_end) > 0) + return EphemeralRange(); + return EphemeralRange(word_start.DeepEquivalent(), word_end.DeepEquivalent()); +} + +EphemeralRange CurrentWordIfTypingInPartialWord(const Element& editable) { + const LocalFrame& frame = *editable.GetDocument().GetFrame(); + const SelectionInDOMTree& selection = + frame.Selection().GetSelectionInDOMTree(); + if (!selection.IsCaret()) + return EphemeralRange(); + if (RootEditableElementOf(selection.Base()) != &editable) + return EphemeralRange(); + + CompositeEditCommand* last_command = frame.GetEditor().LastEditCommand(); + if (!last_command || !last_command->IsTypingCommand()) + return EphemeralRange(); + if (!last_command->EndingSelection().IsValidFor(*frame.GetDocument())) + return EphemeralRange(); + if (last_command->EndingSelection().AsSelection() != selection) + return EphemeralRange(); + return AdjacentWordIfExists(selection.Base()); +} + +EphemeralRange CalculateHotModeCheckingRange(const Element& editable, + const Position& position) { + // Check everything in |editable| if its total length is short. + const EphemeralRange& full_range = EphemeralRange::RangeOfContents(editable); + const int full_length = TextIterator::RangeLength(full_range); + // TODO(xiaochengh): There is no need to check if |full_length <= 2|, since + // we don't consider two characters as misspelled. However, a lot of layout + // tests depend on "zz" as misspelled, which should be changed. + if (full_length <= kHotModeCheckAllThreshold) + return full_range; + + // Otherwise, if |position| is in a short paragraph, check the paragraph. + const EphemeralRange& paragraph_range = + ExpandToParagraphBoundary(EphemeralRange(position)); + const int paragraph_length = TextIterator::RangeLength(paragraph_range); + if (paragraph_length <= kHotModeChunkSize) + return paragraph_range; + + // Otherwise, check a chunk of text centered at |position|. + TextIteratorBehavior behavior = TextIteratorBehavior::Builder() + .SetEmitsObjectReplacementCharacter(true) + .Build(); + BackwardsCharacterIterator backward_iterator( + EphemeralRange(full_range.StartPosition(), position), behavior); + if (!backward_iterator.AtEnd()) + backward_iterator.Advance(kHotModeChunkSize / 2); + const Position& chunk_start = backward_iterator.EndPosition(); + CharacterIterator forward_iterator(position, full_range.EndPosition(), + behavior); + if (!forward_iterator.AtEnd()) + forward_iterator.Advance(kHotModeChunkSize / 2); + const Position& chunk_end = forward_iterator.EndPosition(); + return ExpandRangeToSentenceBoundary(EphemeralRange(chunk_start, chunk_end)); +} + +} // namespace + +HotModeSpellCheckRequester::HotModeSpellCheckRequester( + SpellCheckRequester& requester) + : requester_(requester) {} + +void HotModeSpellCheckRequester::CheckSpellingAt(const Position& position) { + const Element* root_editable = RootEditableElementOf(position); + if (!root_editable || !root_editable->isConnected()) + return; + + if (processed_root_editables_.Contains(root_editable)) + return; + processed_root_editables_.push_back(root_editable); + + if (!root_editable->IsSpellCheckingEnabled() && + !SpellChecker::IsSpellCheckingEnabledAt(position)) { + return; + } + + const EphemeralRange& current_word = + CurrentWordIfTypingInPartialWord(*root_editable); + if (current_word.IsNotNull()) { + root_editable->GetDocument().Markers().RemoveMarkersInRange( + current_word, DocumentMarker::MisspellingMarkers()); + return; + } + + const EphemeralRange& checking_range = + CalculateHotModeCheckingRange(*root_editable, position); + requester_->RequestCheckingFor(checking_range); +} + +} // namespace blink |