| Index: third_party/WebKit/Source/core/editing/spellcheck/SpellChecker.cpp
|
| diff --git a/third_party/WebKit/Source/core/editing/spellcheck/SpellChecker.cpp b/third_party/WebKit/Source/core/editing/spellcheck/SpellChecker.cpp
|
| index 902073e0dd73acc496431ce33711eb029351e7f4..23ab64e216a0dfdaf8d140b35291032b94d34386 100644
|
| --- a/third_party/WebKit/Source/core/editing/spellcheck/SpellChecker.cpp
|
| +++ b/third_party/WebKit/Source/core/editing/spellcheck/SpellChecker.cpp
|
| @@ -43,6 +43,7 @@
|
| #include "core/editing/commands/TypingCommand.h"
|
| #include "core/editing/iterators/CharacterIterator.h"
|
| #include "core/editing/markers/DocumentMarkerController.h"
|
| +#include "core/editing/markers/SpellCheckMarker.h"
|
| #include "core/editing/spellcheck/IdleSpellCheckCallback.h"
|
| #include "core/editing/spellcheck/SpellCheckRequester.h"
|
| #include "core/editing/spellcheck/SpellCheckerClient.h"
|
| @@ -91,6 +92,45 @@ SelectionInDOMTree SelectWord(const VisiblePosition& position) {
|
| .Build();
|
| }
|
|
|
| +EphemeralRange ExpandSelectionRangeIfNecessary(
|
| + const VisibleSelection& selection) {
|
| + DCHECK(!selection.IsNone());
|
| +
|
| + // If some text is actually selected, we can use the selection range as-is to
|
| + // check for a marker. If no text is selected (we just have a caret
|
| + // somewhere), we need to expand one character on either side so we can find
|
| + // a spelling marker immediately before or after the caret.
|
| +
|
| + // (The spelling markers on these words may be anchored to a different node
|
| + // than the collapsed selection's Position is, but once we expand the
|
| + // selection, if we're next to a marker, either the start or end point should
|
| + // now be anchored relative to the same text node as that marker.)
|
| +
|
| + // Some text is actually selected
|
| + if (selection.IsRange())
|
| + return EphemeralRange(selection.Start(), selection.End());
|
| +
|
| + // No text is actually selected, need to expand the selection range. This is
|
| + // to make sure we can find a marker touching the caret. E.g. if we have:
|
| + // <span>word1 <b>word2</b></span>
|
| + // with a caret selection immediately before "word2", there's one text node
|
| + // immediately before the caret ("word1 ") and one immediately after
|
| + // ("word2"). Expanding the selection by one character on either side ensures
|
| + // we get a range that intersects both neighboring text nodes (if they exist).
|
| + const VisiblePosition& caret_position = selection.VisibleStart();
|
| +
|
| + const Position& previous_position =
|
| + PreviousPositionOf(caret_position).DeepEquivalent();
|
| +
|
| + const Position& next_position =
|
| + NextPositionOf(caret_position).DeepEquivalent();
|
| +
|
| + return EphemeralRange(
|
| + previous_position.IsNull() ? caret_position.DeepEquivalent()
|
| + : previous_position,
|
| + next_position.IsNull() ? caret_position.DeepEquivalent() : next_position);
|
| +}
|
| +
|
| } // namespace
|
|
|
| SpellChecker* SpellChecker::Create(LocalFrame& frame) {
|
| @@ -811,51 +851,57 @@ void SpellChecker::RemoveSpellingAndGrammarMarkers(const HTMLElement& element,
|
| }
|
| }
|
|
|
| -void SpellChecker::ReplaceMisspelledRange(const String& text) {
|
| - EphemeralRange caret_range = GetFrame()
|
| - .Selection()
|
| - .ComputeVisibleSelectionInDOMTree()
|
| - .ToNormalizedEphemeralRange();
|
| - if (caret_range.IsNull())
|
| - return;
|
| +Optional<std::pair<Node*, SpellCheckMarker*>>
|
| +SpellChecker::GetSpellCheckMarkerTouchingSelection() {
|
| + const VisibleSelection& selection =
|
| + GetFrame().Selection().ComputeVisibleSelectionInDOMTree();
|
| + if (selection.IsNone())
|
| + return Optional<std::pair<Node*, SpellCheckMarker*>>();
|
| +
|
| + const EphemeralRange& range_to_check =
|
| + ExpandSelectionRangeIfNecessary(selection);
|
| +
|
| + Node* const start_container =
|
| + range_to_check.StartPosition().ComputeContainerNode();
|
| + const unsigned start_offset =
|
| + range_to_check.StartPosition().ComputeOffsetInContainerNode();
|
| + Node* const end_container =
|
| + range_to_check.EndPosition().ComputeContainerNode();
|
| + const unsigned end_offset =
|
| + range_to_check.EndPosition().ComputeOffsetInContainerNode();
|
| +
|
| + for (Node& node : range_to_check.Nodes()) {
|
| + const DocumentMarkerVector& markers_in_node =
|
| + GetFrame().GetDocument()->Markers().MarkersFor(
|
| + &node, DocumentMarker::MisspellingMarkers());
|
| + for (DocumentMarker* marker : markers_in_node) {
|
| + if (node == start_container && marker->EndOffset() <= start_offset)
|
| + continue;
|
| + if (node == end_container && marker->StartOffset() >= end_offset)
|
| + continue;
|
|
|
| - Node* const caret_start_container =
|
| - caret_range.StartPosition().ComputeContainerNode();
|
| - Node* const caret_end_container =
|
| - caret_range.EndPosition().ComputeContainerNode();
|
| + return std::make_pair(&node, &ToSpellCheckMarker(*marker));
|
| + }
|
| + }
|
|
|
| - // We don't currently support the case where a misspelling spans multiple
|
| - // nodes
|
| - if (caret_start_container != caret_end_container)
|
| - return;
|
| + // No marker found
|
| + return Optional<std::pair<Node*, SpellCheckMarker*>>();
|
| +}
|
|
|
| - const unsigned caret_start_offset =
|
| - caret_range.StartPosition().ComputeOffsetInContainerNode();
|
| - const unsigned caret_end_offset =
|
| - caret_range.EndPosition().ComputeOffsetInContainerNode();
|
| -
|
| - const DocumentMarkerVector& markers_in_node =
|
| - GetFrame().GetDocument()->Markers().MarkersFor(
|
| - caret_start_container, DocumentMarker::MisspellingMarkers());
|
| -
|
| - const auto marker_it =
|
| - std::find_if(markers_in_node.begin(), markers_in_node.end(),
|
| - [=](const DocumentMarker* marker) {
|
| - return marker->StartOffset() < caret_end_offset &&
|
| - marker->EndOffset() > caret_start_offset;
|
| - });
|
| - if (marker_it == markers_in_node.end())
|
| +void SpellChecker::ReplaceMisspelledRange(const String& text) {
|
| + const Optional<std::pair<Node*, SpellCheckMarker*>>& node_and_marker =
|
| + GetSpellCheckMarkerTouchingSelection();
|
| + if (!node_and_marker)
|
| return;
|
|
|
| - const DocumentMarker* found_marker = *marker_it;
|
| - EphemeralRange marker_range = EphemeralRange(
|
| - Position(caret_start_container, found_marker->StartOffset()),
|
| - Position(caret_start_container, found_marker->EndOffset()));
|
| - if (marker_range.IsNull())
|
| - return;
|
| + Node* const container_node = node_and_marker.value().first;
|
| + const SpellCheckMarker* const marker = node_and_marker.value().second;
|
|
|
| GetFrame().Selection().SetSelection(
|
| - SelectionInDOMTree::Builder().SetBaseAndExtent(marker_range).Build());
|
| + SelectionInDOMTree::Builder()
|
| + .Collapse(Position(container_node, marker->StartOffset()))
|
| + .Extend(Position(container_node, marker->EndOffset()))
|
| + .Build());
|
|
|
| Document& current_document = *GetFrame().GetDocument();
|
|
|
|
|