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