OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2006, 2007, 2008, 2011 Apple Inc. All rights reserved. | 2 * Copyright (C) 2006, 2007, 2008, 2011 Apple Inc. All rights reserved. |
3 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) | 3 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) |
4 * | 4 * |
5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
6 * modification, are permitted provided that the following conditions | 6 * modification, are permitted provided that the following conditions |
7 * are met: | 7 * are met: |
8 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * 2. Redistributions in binary form must reproduce the above copyright | 10 * 2. Redistributions in binary form must reproduce the above copyright |
(...skipping 25 matching lines...) Expand all Loading... |
36 #include "core/dom/Range.h" | 36 #include "core/dom/Range.h" |
37 #include "core/editing/EditingUtilities.h" | 37 #include "core/editing/EditingUtilities.h" |
38 #include "core/editing/Editor.h" | 38 #include "core/editing/Editor.h" |
39 #include "core/editing/EphemeralRange.h" | 39 #include "core/editing/EphemeralRange.h" |
40 #include "core/editing/VisibleUnits.h" | 40 #include "core/editing/VisibleUnits.h" |
41 #include "core/editing/commands/CompositeEditCommand.h" | 41 #include "core/editing/commands/CompositeEditCommand.h" |
42 #include "core/editing/commands/ReplaceSelectionCommand.h" | 42 #include "core/editing/commands/ReplaceSelectionCommand.h" |
43 #include "core/editing/commands/TypingCommand.h" | 43 #include "core/editing/commands/TypingCommand.h" |
44 #include "core/editing/iterators/CharacterIterator.h" | 44 #include "core/editing/iterators/CharacterIterator.h" |
45 #include "core/editing/markers/DocumentMarkerController.h" | 45 #include "core/editing/markers/DocumentMarkerController.h" |
| 46 #include "core/editing/markers/SpellCheckMarker.h" |
46 #include "core/editing/spellcheck/IdleSpellCheckCallback.h" | 47 #include "core/editing/spellcheck/IdleSpellCheckCallback.h" |
47 #include "core/editing/spellcheck/SpellCheckRequester.h" | 48 #include "core/editing/spellcheck/SpellCheckRequester.h" |
48 #include "core/editing/spellcheck/SpellCheckerClient.h" | 49 #include "core/editing/spellcheck/SpellCheckerClient.h" |
49 #include "core/editing/spellcheck/TextCheckingParagraph.h" | 50 #include "core/editing/spellcheck/TextCheckingParagraph.h" |
50 #include "core/frame/LocalFrame.h" | 51 #include "core/frame/LocalFrame.h" |
51 #include "core/frame/Settings.h" | 52 #include "core/frame/Settings.h" |
52 #include "core/html/HTMLInputElement.h" | 53 #include "core/html/HTMLInputElement.h" |
53 #include "core/layout/LayoutTextControl.h" | 54 #include "core/layout/LayoutTextControl.h" |
54 #include "core/loader/EmptyClients.h" | 55 #include "core/loader/EmptyClients.h" |
55 #include "core/page/Page.h" | 56 #include "core/page/Page.h" |
(...skipping 28 matching lines...) Expand all Loading... |
84 // TODO(yosin): We should fix |startOfWord()| and |endOfWord()| not to return | 85 // TODO(yosin): We should fix |startOfWord()| and |endOfWord()| not to return |
85 // null position. | 86 // null position. |
86 const VisiblePosition& start = StartOfWord(position, kLeftWordIfOnBoundary); | 87 const VisiblePosition& start = StartOfWord(position, kLeftWordIfOnBoundary); |
87 const VisiblePosition& end = EndOfWord(position, kRightWordIfOnBoundary); | 88 const VisiblePosition& end = EndOfWord(position, kRightWordIfOnBoundary); |
88 return SelectionInDOMTree::Builder() | 89 return SelectionInDOMTree::Builder() |
89 .SetBaseAndExtentDeprecated(start.DeepEquivalent(), end.DeepEquivalent()) | 90 .SetBaseAndExtentDeprecated(start.DeepEquivalent(), end.DeepEquivalent()) |
90 .SetAffinity(start.Affinity()) | 91 .SetAffinity(start.Affinity()) |
91 .Build(); | 92 .Build(); |
92 } | 93 } |
93 | 94 |
| 95 EphemeralRange ExpandSelectionRangeIfNecessary( |
| 96 const VisibleSelection& selection) { |
| 97 DCHECK(!selection.IsNone()); |
| 98 |
| 99 // If some text is actually selected, we can use the selection range as-is to |
| 100 // check for a marker. If no text is selected (we just have a caret |
| 101 // somewhere), we need to expand one character on either side so we can find |
| 102 // a spelling marker immediately before or after the caret. |
| 103 |
| 104 // (The spelling markers on these words may be anchored to a different node |
| 105 // than the collapsed selection's Position is, but once we expand the |
| 106 // selection, if we're next to a marker, either the start or end point should |
| 107 // now be anchored relative to the same text node as that marker.) |
| 108 |
| 109 // Some text is actually selected |
| 110 if (selection.IsRange()) |
| 111 return EphemeralRange(selection.Start(), selection.End()); |
| 112 |
| 113 // No text is actually selected, need to expand the selection range. This is |
| 114 // to make sure we can find a marker touching the caret. E.g. if we have: |
| 115 // <span>word1 <b>word2</b></span> |
| 116 // with a caret selection immediately before "word2", there's one text node |
| 117 // immediately before the caret ("word1 ") and one immediately after |
| 118 // ("word2"). Expanding the selection by one character on either side ensures |
| 119 // we get a range that intersects both neighboring text nodes (if they exist). |
| 120 const VisiblePosition& caret_position = selection.VisibleStart(); |
| 121 |
| 122 const Position& previous_position = |
| 123 PreviousPositionOf(caret_position).DeepEquivalent(); |
| 124 |
| 125 const Position& next_position = |
| 126 NextPositionOf(caret_position).DeepEquivalent(); |
| 127 |
| 128 return EphemeralRange( |
| 129 previous_position.IsNull() ? caret_position.DeepEquivalent() |
| 130 : previous_position, |
| 131 next_position.IsNull() ? caret_position.DeepEquivalent() : next_position); |
| 132 } |
| 133 |
94 } // namespace | 134 } // namespace |
95 | 135 |
96 SpellChecker* SpellChecker::Create(LocalFrame& frame) { | 136 SpellChecker* SpellChecker::Create(LocalFrame& frame) { |
97 return new SpellChecker(frame); | 137 return new SpellChecker(frame); |
98 } | 138 } |
99 | 139 |
100 static SpellCheckerClient& GetEmptySpellCheckerClient() { | 140 static SpellCheckerClient& GetEmptySpellCheckerClient() { |
101 DEFINE_STATIC_LOCAL(EmptySpellCheckerClient, client, ()); | 141 DEFINE_STATIC_LOCAL(EmptySpellCheckerClient, client, ()); |
102 return client; | 142 return client; |
103 } | 143 } |
(...skipping 700 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
804 DocumentMarker::MarkerTypes marker_types(DocumentMarker::kSpelling); | 844 DocumentMarker::MarkerTypes marker_types(DocumentMarker::kSpelling); |
805 marker_types.Add(DocumentMarker::kGrammar); | 845 marker_types.Add(DocumentMarker::kGrammar); |
806 for (Node& node : NodeTraversal::InclusiveDescendantsOf(element)) { | 846 for (Node& node : NodeTraversal::InclusiveDescendantsOf(element)) { |
807 if (elements_type == ElementsType::kAll || !HasEditableStyle(node)) { | 847 if (elements_type == ElementsType::kAll || !HasEditableStyle(node)) { |
808 GetFrame().GetDocument()->Markers().RemoveMarkersForNode(&node, | 848 GetFrame().GetDocument()->Markers().RemoveMarkersForNode(&node, |
809 marker_types); | 849 marker_types); |
810 } | 850 } |
811 } | 851 } |
812 } | 852 } |
813 | 853 |
| 854 Optional<std::pair<Node*, SpellCheckMarker*>> |
| 855 SpellChecker::GetSpellCheckMarkerTouchingSelection() { |
| 856 const VisibleSelection& selection = |
| 857 GetFrame().Selection().ComputeVisibleSelectionInDOMTree(); |
| 858 if (selection.IsNone()) |
| 859 return Optional<std::pair<Node*, SpellCheckMarker*>>(); |
| 860 |
| 861 const EphemeralRange& range_to_check = |
| 862 ExpandSelectionRangeIfNecessary(selection); |
| 863 |
| 864 Node* const start_container = |
| 865 range_to_check.StartPosition().ComputeContainerNode(); |
| 866 const unsigned start_offset = |
| 867 range_to_check.StartPosition().ComputeOffsetInContainerNode(); |
| 868 Node* const end_container = |
| 869 range_to_check.EndPosition().ComputeContainerNode(); |
| 870 const unsigned end_offset = |
| 871 range_to_check.EndPosition().ComputeOffsetInContainerNode(); |
| 872 |
| 873 for (Node& node : range_to_check.Nodes()) { |
| 874 const DocumentMarkerVector& markers_in_node = |
| 875 GetFrame().GetDocument()->Markers().MarkersFor( |
| 876 &node, DocumentMarker::MisspellingMarkers()); |
| 877 for (DocumentMarker* marker : markers_in_node) { |
| 878 if (node == start_container && marker->EndOffset() <= start_offset) |
| 879 continue; |
| 880 if (node == end_container && marker->StartOffset() >= end_offset) |
| 881 continue; |
| 882 |
| 883 return std::make_pair(&node, &ToSpellCheckMarker(*marker)); |
| 884 } |
| 885 } |
| 886 |
| 887 // No marker found |
| 888 return Optional<std::pair<Node*, SpellCheckMarker*>>(); |
| 889 } |
| 890 |
814 void SpellChecker::ReplaceMisspelledRange(const String& text) { | 891 void SpellChecker::ReplaceMisspelledRange(const String& text) { |
815 EphemeralRange caret_range = GetFrame() | 892 const Optional<std::pair<Node*, SpellCheckMarker*>>& node_and_marker = |
816 .Selection() | 893 GetSpellCheckMarkerTouchingSelection(); |
817 .ComputeVisibleSelectionInDOMTree() | 894 if (!node_and_marker) |
818 .ToNormalizedEphemeralRange(); | |
819 if (caret_range.IsNull()) | |
820 return; | 895 return; |
821 | 896 |
822 Node* const caret_start_container = | 897 Node* const container_node = node_and_marker.value().first; |
823 caret_range.StartPosition().ComputeContainerNode(); | 898 const SpellCheckMarker* const marker = node_and_marker.value().second; |
824 Node* const caret_end_container = | |
825 caret_range.EndPosition().ComputeContainerNode(); | |
826 | |
827 // We don't currently support the case where a misspelling spans multiple | |
828 // nodes | |
829 if (caret_start_container != caret_end_container) | |
830 return; | |
831 | |
832 const unsigned caret_start_offset = | |
833 caret_range.StartPosition().ComputeOffsetInContainerNode(); | |
834 const unsigned caret_end_offset = | |
835 caret_range.EndPosition().ComputeOffsetInContainerNode(); | |
836 | |
837 const DocumentMarkerVector& markers_in_node = | |
838 GetFrame().GetDocument()->Markers().MarkersFor( | |
839 caret_start_container, DocumentMarker::MisspellingMarkers()); | |
840 | |
841 const auto marker_it = | |
842 std::find_if(markers_in_node.begin(), markers_in_node.end(), | |
843 [=](const DocumentMarker* marker) { | |
844 return marker->StartOffset() < caret_end_offset && | |
845 marker->EndOffset() > caret_start_offset; | |
846 }); | |
847 if (marker_it == markers_in_node.end()) | |
848 return; | |
849 | |
850 const DocumentMarker* found_marker = *marker_it; | |
851 EphemeralRange marker_range = EphemeralRange( | |
852 Position(caret_start_container, found_marker->StartOffset()), | |
853 Position(caret_start_container, found_marker->EndOffset())); | |
854 if (marker_range.IsNull()) | |
855 return; | |
856 | 899 |
857 GetFrame().Selection().SetSelection( | 900 GetFrame().Selection().SetSelection( |
858 SelectionInDOMTree::Builder().SetBaseAndExtent(marker_range).Build()); | 901 SelectionInDOMTree::Builder() |
| 902 .Collapse(Position(container_node, marker->StartOffset())) |
| 903 .Extend(Position(container_node, marker->EndOffset())) |
| 904 .Build()); |
859 | 905 |
860 Document& current_document = *GetFrame().GetDocument(); | 906 Document& current_document = *GetFrame().GetDocument(); |
861 | 907 |
862 // Dispatch 'beforeinput'. | 908 // Dispatch 'beforeinput'. |
863 Element* const target = GetFrame().GetEditor().FindEventTargetFromSelection(); | 909 Element* const target = GetFrame().GetEditor().FindEventTargetFromSelection(); |
864 DataTransfer* const data_transfer = DataTransfer::Create( | 910 DataTransfer* const data_transfer = DataTransfer::Create( |
865 DataTransfer::DataTransferType::kInsertReplacementText, | 911 DataTransfer::DataTransferType::kInsertReplacementText, |
866 DataTransferAccessPolicy::kDataTransferReadable, | 912 DataTransferAccessPolicy::kDataTransferReadable, |
867 DataObject::CreateFromString(text)); | 913 DataObject::CreateFromString(text)); |
868 | 914 |
(...skipping 370 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1239 if (!input.IsFocusedElementInDocument()) | 1285 if (!input.IsFocusedElementInDocument()) |
1240 return false; | 1286 return false; |
1241 } | 1287 } |
1242 } | 1288 } |
1243 HTMLElement* element = | 1289 HTMLElement* element = |
1244 Traversal<HTMLElement>::FirstAncestorOrSelf(*position.AnchorNode()); | 1290 Traversal<HTMLElement>::FirstAncestorOrSelf(*position.AnchorNode()); |
1245 return element && element->IsSpellCheckingEnabled(); | 1291 return element && element->IsSpellCheckingEnabled(); |
1246 } | 1292 } |
1247 | 1293 |
1248 } // namespace blink | 1294 } // namespace blink |
OLD | NEW |