Chromium Code Reviews| 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 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 52 #include "core/page/SpellCheckerClient.h" | 52 #include "core/page/SpellCheckerClient.h" |
| 53 #include "platform/text/TextBreakIterator.h" | 53 #include "platform/text/TextBreakIterator.h" |
| 54 #include "platform/text/TextCheckerClient.h" | 54 #include "platform/text/TextCheckerClient.h" |
| 55 | 55 |
| 56 namespace blink { | 56 namespace blink { |
| 57 | 57 |
| 58 using namespace HTMLNames; | 58 using namespace HTMLNames; |
| 59 | 59 |
| 60 namespace { | 60 namespace { |
| 61 | 61 |
| 62 bool isSelectionInTextField(const VisibleSelection& selection) { | 62 bool isSelectionInTextField(const Position& selectionStart) { |
|
Xiaocheng
2016/10/19 02:08:58
nit: the function should be better renamed to "isP
yosin_UTC9
2016/10/19 03:55:26
Done
| |
| 63 HTMLTextFormControlElement* textControl = | 63 HTMLTextFormControlElement* textControl = |
| 64 enclosingTextFormControl(selection.start()); | 64 enclosingTextFormControl(selectionStart); |
| 65 return isHTMLInputElement(textControl) && | 65 return isHTMLInputElement(textControl) && |
| 66 toHTMLInputElement(textControl)->isTextField(); | 66 toHTMLInputElement(textControl)->isTextField(); |
| 67 } | 67 } |
| 68 | 68 |
| 69 bool isSelectionInTextArea(const VisibleSelection& selection) { | 69 bool isSelectionInTextArea(const Position& selectionStart) { |
|
Xiaocheng
2016/10/19 02:08:58
nit: the function should be better renamed to "isP
yosin_UTC9
2016/10/19 03:55:26
Done.
| |
| 70 HTMLTextFormControlElement* textControl = | 70 HTMLTextFormControlElement* textControl = |
| 71 enclosingTextFormControl(selection.start()); | 71 enclosingTextFormControl(selectionStart); |
| 72 return isHTMLTextAreaElement(textControl); | 72 return isHTMLTextAreaElement(textControl); |
| 73 } | 73 } |
| 74 | 74 |
| 75 bool isSelectionInTextFormControl(const VisibleSelection& selection) { | 75 bool isSelectionInTextFormControl(const VisibleSelection& selection) { |
| 76 return !!enclosingTextFormControl(selection.start()); | 76 return !!enclosingTextFormControl(selection.start()); |
| 77 } | 77 } |
| 78 | 78 |
| 79 static bool isSpellCheckingEnabledFor(const VisibleSelection& selection) { | 79 static bool isSpellCheckingEnabledFor(const Position& position) { |
| 80 if (selection.isNone()) | 80 if (position.isNull()) |
| 81 return false; | 81 return false; |
| 82 // TODO(tkent): The following password type check should be done in | 82 // TODO(tkent): The following password type check should be done in |
| 83 // HTMLElement::spellcheck(). crbug.com/371567 | 83 // HTMLElement::spellcheck(). crbug.com/371567 |
| 84 if (HTMLTextFormControlElement* textControl = | 84 if (HTMLTextFormControlElement* textControl = |
| 85 enclosingTextFormControl(selection.start())) { | 85 enclosingTextFormControl(position)) { |
| 86 if (isHTMLInputElement(textControl) && | 86 if (isHTMLInputElement(textControl) && |
| 87 toHTMLInputElement(textControl)->type() == InputTypeNames::password) | 87 toHTMLInputElement(textControl)->type() == InputTypeNames::password) |
| 88 return false; | 88 return false; |
| 89 } | 89 } |
| 90 if (HTMLElement* element = Traversal<HTMLElement>::firstAncestorOrSelf( | 90 if (HTMLElement* element = |
| 91 *selection.start().anchorNode())) { | 91 Traversal<HTMLElement>::firstAncestorOrSelf(*position.anchorNode())) { |
| 92 if (element->isSpellCheckingEnabled()) | 92 if (element->isSpellCheckingEnabled()) |
| 93 return true; | 93 return true; |
| 94 } | 94 } |
| 95 return false; | 95 return false; |
| 96 } | 96 } |
| 97 | 97 |
| 98 static bool isSpellCheckingEnabledFor(const VisibleSelection& selection) { | |
| 99 if (selection.isNone()) | |
| 100 return false; | |
| 101 return isSpellCheckingEnabledFor(selection.start()); | |
| 102 } | |
| 103 | |
| 98 static EphemeralRange expandEndToSentenceBoundary(const EphemeralRange& range) { | 104 static EphemeralRange expandEndToSentenceBoundary(const EphemeralRange& range) { |
| 99 DCHECK(range.isNotNull()); | 105 DCHECK(range.isNotNull()); |
| 100 const VisiblePosition& visibleEnd = | 106 const VisiblePosition& visibleEnd = |
| 101 createVisiblePosition(range.endPosition()); | 107 createVisiblePosition(range.endPosition()); |
| 102 DCHECK(visibleEnd.isNotNull()); | 108 DCHECK(visibleEnd.isNotNull()); |
| 103 const Position& sentenceEnd = endOfSentence(visibleEnd).deepEquivalent(); | 109 const Position& sentenceEnd = endOfSentence(visibleEnd).deepEquivalent(); |
| 104 // TODO(xiaochengh): |sentenceEnd < range.endPosition()| is possible, | 110 // TODO(xiaochengh): |sentenceEnd < range.endPosition()| is possible, |
| 105 // which would trigger a DCHECK in EphemeralRange's constructor if we return | 111 // which would trigger a DCHECK in EphemeralRange's constructor if we return |
| 106 // it directly. However, this shouldn't happen and needs to be fixed. | 112 // it directly. However, this shouldn't happen and needs to be fixed. |
| 107 return EphemeralRange( | 113 return EphemeralRange( |
| (...skipping 686 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 794 frame().selection().setSelection(createVisibleSelection(markerRange), | 800 frame().selection().setSelection(createVisibleSelection(markerRange), |
| 795 CharacterGranularity); | 801 CharacterGranularity); |
| 796 | 802 |
| 797 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets | 803 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets |
| 798 // needs to be audited. See http://crbug.com/590369 for more details. | 804 // needs to be audited. See http://crbug.com/590369 for more details. |
| 799 frame().document()->updateStyleAndLayoutIgnorePendingStylesheets(); | 805 frame().document()->updateStyleAndLayoutIgnorePendingStylesheets(); |
| 800 | 806 |
| 801 frame().editor().replaceSelectionWithText(text, false, false); | 807 frame().editor().replaceSelectionWithText(text, false, false); |
| 802 } | 808 } |
| 803 | 809 |
| 804 static bool shouldCheckOldSelection(const VisibleSelection& oldSelection) { | 810 static bool shouldCheckOldSelection(const Position& oldSelectionStart) { |
| 805 if (!oldSelection.start().isConnected()) | 811 if (!oldSelectionStart.isConnected()) |
| 806 return false; | 812 return false; |
| 807 if (isSelectionInTextField(oldSelection)) | 813 if (isSelectionInTextField(oldSelectionStart)) |
| 808 return false; | 814 return false; |
| 809 if (isSelectionInTextArea(oldSelection)) | 815 if (isSelectionInTextArea(oldSelectionStart)) |
| 810 return true; | 816 return true; |
| 811 | 817 |
| 812 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets | 818 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets |
| 813 // needs to be audited. See http://crbug.com/590369 for more details. | 819 // needs to be audited. See http://crbug.com/590369 for more details. |
| 814 // In the long term we should use idle time spell checker to prevent | 820 // In the long term we should use idle time spell checker to prevent |
| 815 // synchronous layout caused by spell checking (see crbug.com/517298). | 821 // synchronous layout caused by spell checking (see crbug.com/517298). |
| 816 oldSelection.start() | 822 oldSelectionStart.document()->updateStyleAndLayoutIgnorePendingStylesheets(); |
| 817 .document() | |
| 818 ->updateStyleAndLayoutIgnorePendingStylesheets(); | |
| 819 | 823 |
| 820 return oldSelection.isContentEditable(); | 824 return isEditablePosition(oldSelectionStart); |
| 821 } | 825 } |
| 822 | 826 |
| 823 void SpellChecker::respondToChangedSelection( | 827 void SpellChecker::respondToChangedSelection( |
| 824 const VisibleSelection& oldSelection, | 828 const Position& oldSelectionStart, |
| 825 FrameSelection::SetSelectionOptions options) { | 829 FrameSelection::SetSelectionOptions options) { |
| 826 TRACE_EVENT0("blink", "SpellChecker::respondToChangedSelection"); | 830 TRACE_EVENT0("blink", "SpellChecker::respondToChangedSelection"); |
| 827 if (!isSpellCheckingEnabledFor(oldSelection)) | 831 if (!isSpellCheckingEnabledFor(oldSelectionStart)) |
| 828 return; | 832 return; |
| 829 | 833 |
| 830 // When spell checking is off, existing markers disappear after the selection | 834 // When spell checking is off, existing markers disappear after the selection |
| 831 // changes. | 835 // changes. |
| 832 if (!isSpellCheckingEnabled()) { | 836 if (!isSpellCheckingEnabled()) { |
| 833 frame().document()->markers().removeMarkers(DocumentMarker::Spelling); | 837 frame().document()->markers().removeMarkers(DocumentMarker::Spelling); |
| 834 frame().document()->markers().removeMarkers(DocumentMarker::Grammar); | 838 frame().document()->markers().removeMarkers(DocumentMarker::Grammar); |
| 835 return; | 839 return; |
| 836 } | 840 } |
| 837 | 841 |
| 838 if (!(options & FrameSelection::CloseTyping)) | 842 if (!(options & FrameSelection::CloseTyping)) |
| 839 return; | 843 return; |
| 840 if (!shouldCheckOldSelection(oldSelection)) | 844 if (!shouldCheckOldSelection(oldSelectionStart)) |
| 841 return; | 845 return; |
| 842 | 846 |
| 843 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets | 847 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets |
| 844 // needs to be audited. See http://crbug.com/590369 for more details. | 848 // needs to be audited. See http://crbug.com/590369 for more details. |
| 845 // In the long term we should use idle time spell checker to prevent | 849 // In the long term we should use idle time spell checker to prevent |
| 846 // synchronous layout caused by spell checking (see crbug.com/517298). | 850 // synchronous layout caused by spell checking (see crbug.com/517298). |
| 847 frame().document()->updateStyleAndLayoutIgnorePendingStylesheets(); | 851 frame().document()->updateStyleAndLayoutIgnorePendingStylesheets(); |
| 848 | 852 |
| 849 DocumentLifecycle::DisallowTransitionScope disallowTransition( | 853 DocumentLifecycle::DisallowTransitionScope disallowTransition( |
| 850 frame().document()->lifecycle()); | 854 frame().document()->lifecycle()); |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 863 createVisibleSelection(startOfWord(newStart, LeftWordIfOnBoundary), | 867 createVisibleSelection(startOfWord(newStart, LeftWordIfOnBoundary), |
| 864 endOfWord(newStart, RightWordIfOnBoundary)); | 868 endOfWord(newStart, RightWordIfOnBoundary)); |
| 865 } | 869 } |
| 866 } | 870 } |
| 867 | 871 |
| 868 // When typing we check spelling elsewhere, so don't redo it here. | 872 // When typing we check spelling elsewhere, so don't redo it here. |
| 869 // If this is a change in selection resulting from a delete operation, | 873 // If this is a change in selection resulting from a delete operation, |
| 870 // oldSelection may no longer be in the document. | 874 // oldSelection may no longer be in the document. |
| 871 // FIXME(http://crbug.com/382809): if oldSelection is on a textarea | 875 // FIXME(http://crbug.com/382809): if oldSelection is on a textarea |
| 872 // element, we cause synchronous layout. | 876 // element, we cause synchronous layout. |
| 873 spellCheckOldSelection(oldSelection, newAdjacentWords); | 877 spellCheckOldSelection(oldSelectionStart, newAdjacentWords); |
| 874 } | 878 } |
| 875 | 879 |
| 876 void SpellChecker::removeSpellingMarkers() { | 880 void SpellChecker::removeSpellingMarkers() { |
| 877 frame().document()->markers().removeMarkers( | 881 frame().document()->markers().removeMarkers( |
| 878 DocumentMarker::MisspellingMarkers()); | 882 DocumentMarker::MisspellingMarkers()); |
| 879 } | 883 } |
| 880 | 884 |
| 881 void SpellChecker::removeSpellingMarkersUnderWords( | 885 void SpellChecker::removeSpellingMarkersUnderWords( |
| 882 const Vector<String>& words) { | 886 const Vector<String>& words) { |
| 883 MarkerRemoverPredicate removerPredicate(words); | 887 MarkerRemoverPredicate removerPredicate(words); |
| 884 | 888 |
| 885 DocumentMarkerController& markerController = frame().document()->markers(); | 889 DocumentMarkerController& markerController = frame().document()->markers(); |
| 886 markerController.removeMarkers(removerPredicate); | 890 markerController.removeMarkers(removerPredicate); |
| 887 markerController.repaintMarkers(); | 891 markerController.repaintMarkers(); |
| 888 } | 892 } |
| 889 | 893 |
| 890 void SpellChecker::spellCheckAfterBlur() { | 894 void SpellChecker::spellCheckAfterBlur() { |
| 891 if (!frame().selection().selection().isContentEditable()) | 895 if (!frame().selection().selection().isContentEditable()) |
| 892 return; | 896 return; |
| 893 | 897 |
| 894 if (isSelectionInTextField(frame().selection().selection())) { | 898 if (isSelectionInTextField(frame().selection().selection().start())) { |
| 895 // textFieldDidEndEditing() and textFieldDidBeginEditing() handle this. | 899 // textFieldDidEndEditing() and textFieldDidBeginEditing() handle this. |
| 896 return; | 900 return; |
| 897 } | 901 } |
| 898 | 902 |
| 899 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets | 903 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets |
| 900 // needs to be audited. See http://crbug.com/590369 for more details. | 904 // needs to be audited. See http://crbug.com/590369 for more details. |
| 901 // In the long term we should use idle time spell checker to prevent | 905 // In the long term we should use idle time spell checker to prevent |
| 902 // synchronous layout caused by spell checking (see crbug.com/517298). | 906 // synchronous layout caused by spell checking (see crbug.com/517298). |
| 903 frame().document()->updateStyleAndLayoutIgnorePendingStylesheets(); | 907 frame().document()->updateStyleAndLayoutIgnorePendingStylesheets(); |
| 904 | 908 |
| 905 DocumentLifecycle::DisallowTransitionScope disallowTransition( | 909 DocumentLifecycle::DisallowTransitionScope disallowTransition( |
| 906 frame().document()->lifecycle()); | 910 frame().document()->lifecycle()); |
| 907 | 911 |
| 908 VisibleSelection empty; | 912 VisibleSelection empty; |
| 909 spellCheckOldSelection(frame().selection().selection(), empty); | 913 spellCheckOldSelection(frame().selection().selection().start(), empty); |
| 910 } | 914 } |
| 911 | 915 |
| 912 void SpellChecker::spellCheckOldSelection( | 916 void SpellChecker::spellCheckOldSelection( |
| 913 const VisibleSelection& oldSelection, | 917 const Position& oldSelectionStart, |
| 914 const VisibleSelection& newAdjacentWords) { | 918 const VisibleSelection& newAdjacentWords) { |
| 915 if (!isSpellCheckingEnabled()) | 919 if (!isSpellCheckingEnabled()) |
| 916 return; | 920 return; |
| 917 | 921 |
| 918 TRACE_EVENT0("blink", "SpellChecker::spellCheckOldSelection"); | 922 TRACE_EVENT0("blink", "SpellChecker::spellCheckOldSelection"); |
| 919 | 923 |
| 920 VisiblePosition oldStart(oldSelection.visibleStart()); | 924 VisiblePosition oldStart = createVisiblePosition(oldSelectionStart); |
| 921 VisibleSelection oldAdjacentWords = | 925 VisibleSelection oldAdjacentWords = |
| 922 createVisibleSelection(startOfWord(oldStart, LeftWordIfOnBoundary), | 926 createVisibleSelection(startOfWord(oldStart, LeftWordIfOnBoundary), |
| 923 endOfWord(oldStart, RightWordIfOnBoundary)); | 927 endOfWord(oldStart, RightWordIfOnBoundary)); |
| 924 if (oldAdjacentWords == newAdjacentWords) | 928 if (oldAdjacentWords == newAdjacentWords) |
| 925 return; | 929 return; |
| 926 markMisspellingsAndBadGrammar(oldAdjacentWords); | 930 markMisspellingsAndBadGrammar(oldAdjacentWords); |
| 927 } | 931 } |
| 928 | 932 |
| 929 static Node* findFirstMarkable(Node* node) { | 933 static Node* findFirstMarkable(Node* node) { |
| 930 while (node) { | 934 while (node) { |
| (...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1119 startOfNextParagraph(createVisiblePosition(paragraphEnd)); | 1123 startOfNextParagraph(createVisiblePosition(paragraphEnd)); |
| 1120 paragraphStart = newParagraphStart.toParentAnchoredPosition(); | 1124 paragraphStart = newParagraphStart.toParentAnchoredPosition(); |
| 1121 paragraphEnd = endOfParagraph(newParagraphStart).toParentAnchoredPosition(); | 1125 paragraphEnd = endOfParagraph(newParagraphStart).toParentAnchoredPosition(); |
| 1122 firstIteration = false; | 1126 firstIteration = false; |
| 1123 totalLengthProcessed += currentLength; | 1127 totalLengthProcessed += currentLength; |
| 1124 } | 1128 } |
| 1125 return std::make_pair(firstFoundItem, firstFoundOffset); | 1129 return std::make_pair(firstFoundItem, firstFoundOffset); |
| 1126 } | 1130 } |
| 1127 | 1131 |
| 1128 } // namespace blink | 1132 } // namespace blink |
| OLD | NEW |