2011-04-06 MORITA Hajime <morrita@google.com>
Reviewed by Darin Adler.
[Refactoring] Auto correction panel should be handled by its own class.
https://bugs.webkit.org/show_bug.cgi?id=55571
Extracted code inside SUPPORT_AUTOCORRECTION_PANEL into
SpellingCorrectionController class.
This change also remove some SUPPORT_AUTOCORRECTION_PANEL guard if
code paths inside the never reached without autocorrection support.
Removing guards reduces unintentional build breakage.
No new tests, no behavior chagne.
* CMakeLists.txt:
* GNUmakefile.am:
* WebCore.gypi:
* WebCore.pro:
* WebCore.vcproj/WebCore.vcproj:
* WebCore.xcodeproj/project.pbxproj:
* editing/CorrectionPanelInfo.h: Removed.
* editing/EditingAllInOne.cpp:
* editing/Editor.cpp:
(WebCore::Editor::respondToChangedSelection):
(WebCore::Editor::respondToChangedContents):
(WebCore::Editor::appliedEditing):
(WebCore::Editor::Editor):
(WebCore::Editor::~Editor):
(WebCore::Editor::insertTextWithoutSendingTextEvent):
(WebCore::Editor::insertLineBreak):
(WebCore::Editor::insertParagraphSeparator):
(WebCore::Editor::cut):
(WebCore::Editor::paste):
(WebCore::Editor::pasteAsPlainText):
(WebCore::Editor::isAutomaticSpellingCorrectionEnabled):
(WebCore::Editor::markMisspellingsAfterTypingToWord):
(WebCore::Editor::markAllMisspellingsAndBadGrammarInRanges):
(WebCore::Editor::changeBackToReplacedString):
(WebCore::Editor::unappliedSpellCorrection):
(WebCore::Editor::updateMarkersForWordsAffectedByEditing):
(WebCore::Editor::startCorrectionPanelTimer):
(WebCore::Editor::handleCorrectionPanelResult):
(WebCore::Editor::dismissCorrectionPanelAsIgnored):
* editing/Editor.h:
* editing/SpellingCorrectionCommand.cpp:
* editing/SpellingCorrectionController.cpp: Added.
(WebCore::markerTypesForAutocorrection):
(WebCore::markerTypesForReplacement):
(WebCore::markersHaveIdenticalDescription):
(WebCore::SpellingCorrectionController::SpellingCorrectionController):
(WebCore::SpellingCorrectionController::~SpellingCorrectionController):
(WebCore::SpellingCorrectionController::startCorrectionPanelTimer):
(WebCore::SpellingCorrectionController::stopCorrectionPanelTimer):
(WebCore::SpellingCorrectionController::stopPendingCorrection):
(WebCore::SpellingCorrectionController::applyPendingCorrection):
(WebCore::SpellingCorrectionController::hasPendingCorrection):
(WebCore::SpellingCorrectionController::isSpellingMarkerAllowed):
(WebCore::SpellingCorrectionController::show):
(WebCore::SpellingCorrectionController::handleCancelOperation):
(WebCore::SpellingCorrectionController::dismiss):
(WebCore::SpellingCorrectionController::dismissSoon):
(WebCore::SpellingCorrectionController::applyCorrectionPanelInfo):
(WebCore::SpellingCorrectionController::applyAutocorrectionBeforeTypingIfAppropriate):
(WebCore::SpellingCorrectionController::respondToUnappliedSpellCorrection):
(WebCore::SpellingCorrectionController::correctionPanelTimerFired):
(WebCore::SpellingCorrectionController::handleCorrectionPanelResult):
(WebCore::SpellingCorrectionController::isAutomaticSpellingCorrectionEnabled):
(WebCore::SpellingCorrectionController::windowRectForRange):
(WebCore::SpellingCorrectionController::respondToChangedSelection):
(WebCore::SpellingCorrectionController::respondToAppliedEditing):
(WebCore::SpellingCorrectionController::client):
(WebCore::SpellingCorrectionController::textChecker):
(WebCore::SpellingCorrectionController::recordAutocorrectionResponseReversed):
(WebCore::SpellingCorrectionController::markReversed):
(WebCore::SpellingCorrectionController::markCorrection):
(WebCore::SpellingCorrectionController::recordSpellcheckerResponseForModifiedCorrection):
* editing/SpellingCorrectionController.h: Added.
(WebCore::SpellingCorrectionController::UNLESS_ENABLED):
(WebCore::SpellingCorrectionController::shouldStartTimeFor):
(WebCore::SpellingCorrectionController::shouldRemoveMarkersUponEditing):
* editing/TypingCommand.cpp:
(WebCore::TypingCommand::insertText):
(WebCore::TypingCommand::markMisspellingsAfterTyping):
(WebCore::TypingCommand::deleteKeyPressed):
(WebCore::TypingCommand::forwardDeleteKeyPressed):
* editing/htmlediting.h:
(WebCore::isAmbiguousBoundaryCharacter):
* page/EditorClient.h:
* page/Frame.cpp:
(WebCore::Frame::setPageAndTextZoomFactors):
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@83344 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/CMakeLists.txt b/Source/WebCore/CMakeLists.txt
index 6bce004..b281129 100644
--- a/Source/WebCore/CMakeLists.txt
+++ b/Source/WebCore/CMakeLists.txt
@@ -674,6 +674,7 @@
editing/SetNodeAttributeCommand.cpp
editing/SmartReplace.cpp
editing/SpellChecker.cpp
+ editing/SpellingCorrectionController.cpp
editing/SplitElementCommand.cpp
editing/SplitTextNodeCommand.cpp
editing/SplitTextNodeContainingElementCommand.cpp
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 3df4763..6b1ae1e 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,95 @@
+2011-04-06 MORITA Hajime <morrita@google.com>
+
+ Reviewed by Darin Adler.
+
+ [Refactoring] Auto correction panel should be handled by its own class.
+ https://bugs.webkit.org/show_bug.cgi?id=55571
+
+ Extracted code inside SUPPORT_AUTOCORRECTION_PANEL into
+ SpellingCorrectionController class.
+ This change also remove some SUPPORT_AUTOCORRECTION_PANEL guard if
+ code paths inside the never reached without autocorrection support.
+ Removing guards reduces unintentional build breakage.
+
+ No new tests, no behavior chagne.
+
+ * CMakeLists.txt:
+ * GNUmakefile.am:
+ * WebCore.gypi:
+ * WebCore.pro:
+ * WebCore.vcproj/WebCore.vcproj:
+ * WebCore.xcodeproj/project.pbxproj:
+ * editing/CorrectionPanelInfo.h: Removed.
+ * editing/EditingAllInOne.cpp:
+ * editing/Editor.cpp:
+ (WebCore::Editor::respondToChangedSelection):
+ (WebCore::Editor::respondToChangedContents):
+ (WebCore::Editor::appliedEditing):
+ (WebCore::Editor::Editor):
+ (WebCore::Editor::~Editor):
+ (WebCore::Editor::insertTextWithoutSendingTextEvent):
+ (WebCore::Editor::insertLineBreak):
+ (WebCore::Editor::insertParagraphSeparator):
+ (WebCore::Editor::cut):
+ (WebCore::Editor::paste):
+ (WebCore::Editor::pasteAsPlainText):
+ (WebCore::Editor::isAutomaticSpellingCorrectionEnabled):
+ (WebCore::Editor::markMisspellingsAfterTypingToWord):
+ (WebCore::Editor::markAllMisspellingsAndBadGrammarInRanges):
+ (WebCore::Editor::changeBackToReplacedString):
+ (WebCore::Editor::unappliedSpellCorrection):
+ (WebCore::Editor::updateMarkersForWordsAffectedByEditing):
+ (WebCore::Editor::startCorrectionPanelTimer):
+ (WebCore::Editor::handleCorrectionPanelResult):
+ (WebCore::Editor::dismissCorrectionPanelAsIgnored):
+ * editing/Editor.h:
+ * editing/SpellingCorrectionCommand.cpp:
+ * editing/SpellingCorrectionController.cpp: Added.
+ (WebCore::markerTypesForAutocorrection):
+ (WebCore::markerTypesForReplacement):
+ (WebCore::markersHaveIdenticalDescription):
+ (WebCore::SpellingCorrectionController::SpellingCorrectionController):
+ (WebCore::SpellingCorrectionController::~SpellingCorrectionController):
+ (WebCore::SpellingCorrectionController::startCorrectionPanelTimer):
+ (WebCore::SpellingCorrectionController::stopCorrectionPanelTimer):
+ (WebCore::SpellingCorrectionController::stopPendingCorrection):
+ (WebCore::SpellingCorrectionController::applyPendingCorrection):
+ (WebCore::SpellingCorrectionController::hasPendingCorrection):
+ (WebCore::SpellingCorrectionController::isSpellingMarkerAllowed):
+ (WebCore::SpellingCorrectionController::show):
+ (WebCore::SpellingCorrectionController::handleCancelOperation):
+ (WebCore::SpellingCorrectionController::dismiss):
+ (WebCore::SpellingCorrectionController::dismissSoon):
+ (WebCore::SpellingCorrectionController::applyCorrectionPanelInfo):
+ (WebCore::SpellingCorrectionController::applyAutocorrectionBeforeTypingIfAppropriate):
+ (WebCore::SpellingCorrectionController::respondToUnappliedSpellCorrection):
+ (WebCore::SpellingCorrectionController::correctionPanelTimerFired):
+ (WebCore::SpellingCorrectionController::handleCorrectionPanelResult):
+ (WebCore::SpellingCorrectionController::isAutomaticSpellingCorrectionEnabled):
+ (WebCore::SpellingCorrectionController::windowRectForRange):
+ (WebCore::SpellingCorrectionController::respondToChangedSelection):
+ (WebCore::SpellingCorrectionController::respondToAppliedEditing):
+ (WebCore::SpellingCorrectionController::client):
+ (WebCore::SpellingCorrectionController::textChecker):
+ (WebCore::SpellingCorrectionController::recordAutocorrectionResponseReversed):
+ (WebCore::SpellingCorrectionController::markReversed):
+ (WebCore::SpellingCorrectionController::markCorrection):
+ (WebCore::SpellingCorrectionController::recordSpellcheckerResponseForModifiedCorrection):
+ * editing/SpellingCorrectionController.h: Added.
+ (WebCore::SpellingCorrectionController::UNLESS_ENABLED):
+ (WebCore::SpellingCorrectionController::shouldStartTimeFor):
+ (WebCore::SpellingCorrectionController::shouldRemoveMarkersUponEditing):
+ * editing/TypingCommand.cpp:
+ (WebCore::TypingCommand::insertText):
+ (WebCore::TypingCommand::markMisspellingsAfterTyping):
+ (WebCore::TypingCommand::deleteKeyPressed):
+ (WebCore::TypingCommand::forwardDeleteKeyPressed):
+ * editing/htmlediting.h:
+ (WebCore::isAmbiguousBoundaryCharacter):
+ * page/EditorClient.h:
+ * page/Frame.cpp:
+ (WebCore::Frame::setPageAndTextZoomFactors):
+
2011-04-08 Alpha Lam <hclam@chromium.org>
Unreviewed, rolling out r83335.
diff --git a/Source/WebCore/GNUmakefile.list.am b/Source/WebCore/GNUmakefile.list.am
index 12750a4..aab92e3 100644
--- a/Source/WebCore/GNUmakefile.list.am
+++ b/Source/WebCore/GNUmakefile.list.am
@@ -1382,6 +1382,8 @@
Source/WebCore/editing/SmartReplaceICU.cpp \
Source/WebCore/editing/SpellChecker.cpp \
Source/WebCore/editing/SpellChecker.h \
+ Source/WebCore/editing/SpellingCorrectionController.cpp \
+ Source/WebCore/editing/SpellingCorrectionController.h \
Source/WebCore/editing/SpellingCorrectionCommand.h \
Source/WebCore/editing/SplitElementCommand.cpp \
Source/WebCore/editing/SplitElementCommand.h \
diff --git a/Source/WebCore/WebCore.gypi b/Source/WebCore/WebCore.gypi
index ad158e0..88fbb8a 100644
--- a/Source/WebCore/WebCore.gypi
+++ b/Source/WebCore/WebCore.gypi
@@ -2649,6 +2649,9 @@
'editing/SmartReplaceCF.cpp',
'editing/SmartReplaceICU.cpp',
'editing/SpellChecker.cpp',
+ 'editing/SpellChecker.h',
+ 'editing/SpellingCorrectionController.cpp',
+ 'editing/SpellingCorrectionController.h',
'editing/SpellingCorrectionCommand.cpp',
'editing/SpellingCorrectionCommand.h',
'editing/SplitElementCommand.cpp',
diff --git a/Source/WebCore/WebCore.pro b/Source/WebCore/WebCore.pro
index 7e81f52..8284bdb 100644
--- a/Source/WebCore/WebCore.pro
+++ b/Source/WebCore/WebCore.pro
@@ -600,6 +600,7 @@
editing/SetNodeAttributeCommand.cpp \
editing/SmartReplaceICU.cpp \
editing/SpellChecker.cpp \
+ editing/SpellingCorrectionController.cpp \
editing/SplitElementCommand.cpp \
editing/SplitTextNodeCommand.cpp \
editing/SplitTextNodeContainingElementCommand.cpp \
diff --git a/Source/WebCore/WebCore.vcproj/WebCore.vcproj b/Source/WebCore/WebCore.vcproj/WebCore.vcproj
index 12d94c2..006befd 100755
--- a/Source/WebCore/WebCore.vcproj/WebCore.vcproj
+++ b/Source/WebCore/WebCore.vcproj/WebCore.vcproj
@@ -50474,6 +50474,62 @@
>
</File>
<File
+ RelativePath="..\editing\SpellingCorrectionController.cpp"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug_Cairo_CFLite|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release_Cairo_CFLite|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug_All|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release_LTCG|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\editing\SpellingCorrectionController.h"
+ >
+ </File>
+ <File
RelativePath="..\editing\SplitElementCommand.cpp"
>
<FileConfiguration
diff --git a/Source/WebCore/WebCore.xcodeproj/project.pbxproj b/Source/WebCore/WebCore.xcodeproj/project.pbxproj
index dcdd197..a82332f 100644
--- a/Source/WebCore/WebCore.xcodeproj/project.pbxproj
+++ b/Source/WebCore/WebCore.xcodeproj/project.pbxproj
@@ -3198,6 +3198,8 @@
A622A8FF122C44A600A785B3 /* GenericBinding.h in Headers */ = {isa = PBXBuildFile; fileRef = A622A8F9122C44A600A785B3 /* GenericBinding.h */; };
A6D169621346B49B000EB770 /* ShadowRoot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A6D169611346B49B000EB770 /* ShadowRoot.cpp */; };
A6D169641346B4C1000EB770 /* ShadowRoot.h in Headers */ = {isa = PBXBuildFile; fileRef = A6D169631346B4C1000EB770 /* ShadowRoot.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ A7005CCC1330C4BA000CC0BA /* SpellingCorrectionController.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A7005CCA1330C4BA000CC0BA /* SpellingCorrectionController.cpp */; };
+ A7005CCD1330C4BA000CC0BA /* SpellingCorrectionController.h in Headers */ = {isa = PBXBuildFile; fileRef = A7005CCB1330C4BA000CC0BA /* SpellingCorrectionController.h */; settings = {ATTRIBUTES = (Private, ); }; };
A7151BD812F1558F005A0F64 /* TextCheckerClient.h in Headers */ = {isa = PBXBuildFile; fileRef = A7151BD712F1558F005A0F64 /* TextCheckerClient.h */; settings = {ATTRIBUTES = (Private, ); }; };
A715E652134BBBEC00D8E713 /* ProgressShadowElement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A715E650134BBBEC00D8E713 /* ProgressShadowElement.cpp */; };
A715E653134BBBEC00D8E713 /* ProgressShadowElement.h in Headers */ = {isa = PBXBuildFile; fileRef = A715E651134BBBEC00D8E713 /* ProgressShadowElement.h */; };
@@ -4808,7 +4810,6 @@
B776D43D1104527500BEB0EC /* PrintContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B776D43C1104527500BEB0EC /* PrintContext.cpp */; };
B885E8D411E06DD2009FFBF4 /* InspectorApplicationCacheAgent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B885E8D211E06DD2009FFBF4 /* InspectorApplicationCacheAgent.cpp */; };
B885E8D511E06DD2009FFBF4 /* InspectorApplicationCacheAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = B885E8D311E06DD2009FFBF4 /* InspectorApplicationCacheAgent.h */; settings = {ATTRIBUTES = (); }; };
- B8A6A6D5127B338D008673BA /* CorrectionPanelInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = B8A6A6D4127B338D008673BA /* CorrectionPanelInfo.h */; settings = {ATTRIBUTES = (Private, ); }; };
B8DBDB4B130B0F8A00F5CDB1 /* SetSelectionCommand.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B8DBDB47130B0F8A00F5CDB1 /* SetSelectionCommand.cpp */; };
B8DBDB4C130B0F8A00F5CDB1 /* SetSelectionCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = B8DBDB48130B0F8A00F5CDB1 /* SetSelectionCommand.h */; };
B8DBDB4D130B0F8A00F5CDB1 /* SpellingCorrectionCommand.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B8DBDB49130B0F8A00F5CDB1 /* SpellingCorrectionCommand.cpp */; };
@@ -9738,6 +9739,8 @@
A622A8F9122C44A600A785B3 /* GenericBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GenericBinding.h; path = generic/GenericBinding.h; sourceTree = "<group>"; };
A6D169611346B49B000EB770 /* ShadowRoot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ShadowRoot.cpp; sourceTree = "<group>"; };
A6D169631346B4C1000EB770 /* ShadowRoot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShadowRoot.h; sourceTree = "<group>"; };
+ A7005CCA1330C4BA000CC0BA /* SpellingCorrectionController.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SpellingCorrectionController.cpp; sourceTree = "<group>"; };
+ A7005CCB1330C4BA000CC0BA /* SpellingCorrectionController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SpellingCorrectionController.h; sourceTree = "<group>"; };
A7151BD712F1558F005A0F64 /* TextCheckerClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextCheckerClient.h; sourceTree = "<group>"; };
A715E650134BBBEC00D8E713 /* ProgressShadowElement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ProgressShadowElement.cpp; sourceTree = "<group>"; };
A715E651134BBBEC00D8E713 /* ProgressShadowElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProgressShadowElement.h; sourceTree = "<group>"; };
@@ -11258,7 +11261,6 @@
B776D43C1104527500BEB0EC /* PrintContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PrintContext.cpp; sourceTree = "<group>"; };
B885E8D211E06DD2009FFBF4 /* InspectorApplicationCacheAgent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InspectorApplicationCacheAgent.cpp; sourceTree = "<group>"; };
B885E8D311E06DD2009FFBF4 /* InspectorApplicationCacheAgent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InspectorApplicationCacheAgent.h; sourceTree = "<group>"; };
- B8A6A6D4127B338D008673BA /* CorrectionPanelInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CorrectionPanelInfo.h; sourceTree = "<group>"; };
B8DBDB47130B0F8A00F5CDB1 /* SetSelectionCommand.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SetSelectionCommand.cpp; sourceTree = "<group>"; };
B8DBDB48130B0F8A00F5CDB1 /* SetSelectionCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SetSelectionCommand.h; sourceTree = "<group>"; };
B8DBDB49130B0F8A00F5CDB1 /* SpellingCorrectionCommand.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SpellingCorrectionCommand.cpp; sourceTree = "<group>"; };
@@ -15574,7 +15576,6 @@
93309D8C099E64910056E581 /* BreakBlockquoteCommand.h */,
93309D8D099E64910056E581 /* CompositeEditCommand.cpp */,
93309D8E099E64910056E581 /* CompositeEditCommand.h */,
- B8A6A6D4127B338D008673BA /* CorrectionPanelInfo.h */,
D0B0556709C6700100307E43 /* CreateLinkCommand.cpp */,
D0B0556609C6700100307E43 /* CreateLinkCommand.h */,
1C4C8F630AD8655D009475CE /* DeleteButton.cpp */,
@@ -15656,6 +15657,8 @@
A78FE13A12366B1000ACE8D0 /* SpellChecker.h */,
B8DBDB49130B0F8A00F5CDB1 /* SpellingCorrectionCommand.cpp */,
B8DBDB4A130B0F8A00F5CDB1 /* SpellingCorrectionCommand.h */,
+ A7005CCA1330C4BA000CC0BA /* SpellingCorrectionController.cpp */,
+ A7005CCB1330C4BA000CC0BA /* SpellingCorrectionController.h */,
93309DC2099E64910056E581 /* SplitElementCommand.cpp */,
93309DC3099E64910056E581 /* SplitElementCommand.h */,
93309DC4099E64910056E581 /* SplitTextNodeCommand.cpp */,
@@ -20018,7 +20021,6 @@
33D0212D131DB37B004091A8 /* CookieStorage.h in Headers */,
7EE6846412D26E3800E79415 /* CookieStorageCFNet.h in Headers */,
FE6FD4880F676E5700092873 /* Coordinates.h in Headers */,
- B8A6A6D5127B338D008673BA /* CorrectionPanelInfo.h in Headers */,
A80E6D040A1989CA007FB8C5 /* Counter.h in Headers */,
BC5EB9790E82069200B25965 /* CounterContent.h in Headers */,
BC5EB9510E82056B00B25965 /* CounterDirectives.h in Headers */,
@@ -22341,6 +22343,7 @@
75415B0012958D5E003AD669 /* SpeechInputResultList.h in Headers */,
A78FE13C12366B1000ACE8D0 /* SpellChecker.h in Headers */,
B8DBDB4E130B0F8A00F5CDB1 /* SpellingCorrectionCommand.h in Headers */,
+ A7005CCD1330C4BA000CC0BA /* SpellingCorrectionController.h in Headers */,
93309E12099E64920056E581 /* SplitElementCommand.h in Headers */,
93309E14099E64920056E581 /* SplitTextNodeCommand.h in Headers */,
93309E16099E64920056E581 /* SplitTextNodeContainingElementCommand.h in Headers */,
@@ -25166,6 +25169,7 @@
75415C29129A9920003AD669 /* SpeechInputResultList.cpp in Sources */,
A78FE13B12366B1000ACE8D0 /* SpellChecker.cpp in Sources */,
B8DBDB4D130B0F8A00F5CDB1 /* SpellingCorrectionCommand.cpp in Sources */,
+ A7005CCC1330C4BA000CC0BA /* SpellingCorrectionController.cpp in Sources */,
93309E11099E64920056E581 /* SplitElementCommand.cpp in Sources */,
93309E13099E64920056E581 /* SplitTextNodeCommand.cpp in Sources */,
93309E15099E64920056E581 /* SplitTextNodeContainingElementCommand.cpp in Sources */,
diff --git a/Source/WebCore/editing/CorrectionPanelInfo.h b/Source/WebCore/editing/CorrectionPanelInfo.h
deleted file mode 100644
index 76099e1..0000000
--- a/Source/WebCore/editing/CorrectionPanelInfo.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2010 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef CorrectionPanelInfo_h
-#define CorrectionPanelInfo_h
-
-#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
-// Some platforms provide UI for suggesting autocorrection.
-#define SUPPORT_AUTOCORRECTION_PANEL 1
-// Some platforms use spelling and autocorrection markers to provide visual cue.
-// On such platform, if word with marker is edited, we need to remove the marker.
-#define REMOVE_MARKERS_UPON_EDITING 1
-#else
-#define SUPPORT_AUTOCORRECTION_PANEL 0
-#define REMOVE_MARKERS_UPON_EDITING 0
-#endif // #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
-
-#include "Range.h"
-
-namespace WebCore {
-
-struct CorrectionPanelInfo {
- enum PanelType {
- PanelTypeCorrection = 0,
- PanelTypeReversion,
- PanelTypeSpellingSuggestions
- };
-
- RefPtr<Range> rangeToBeReplaced;
- String replacedString;
- String replacementString;
- PanelType panelType;
- bool isActive;
-};
-
-enum ReasonForDismissingCorrectionPanel {
- ReasonForDismissingCorrectionPanelCancelled = 0,
- ReasonForDismissingCorrectionPanelIgnored,
- ReasonForDismissingCorrectionPanelAccepted
-};
-} // namespace WebCore
-
-#endif // CorrectionPanelInfo_h
diff --git a/Source/WebCore/editing/EditingAllInOne.cpp b/Source/WebCore/editing/EditingAllInOne.cpp
index e4e0bbb..f38cbe6 100644
--- a/Source/WebCore/editing/EditingAllInOne.cpp
+++ b/Source/WebCore/editing/EditingAllInOne.cpp
@@ -63,6 +63,7 @@
#include <SetNodeAttributeCommand.cpp>
#include <SmartReplace.cpp>
#include <SmartReplaceCF.cpp>
+#include <SpellingCorrectionController.cpp>
#include <SpellChecker.cpp>
#include <SplitElementCommand.cpp>
#include <SplitTextNodeCommand.cpp>
diff --git a/Source/WebCore/editing/Editor.cpp b/Source/WebCore/editing/Editor.cpp
index 8d5101c..d41577d 100644
--- a/Source/WebCore/editing/Editor.cpp
+++ b/Source/WebCore/editing/Editor.cpp
@@ -38,6 +38,7 @@
#include "CachedResourceLoader.h"
#include "ClipboardEvent.h"
#include "CompositionEvent.h"
+#include "SpellingCorrectionController.h"
#include "CreateLinkCommand.h"
#include "DeleteButtonController.h"
#include "DeleteSelectionCommand.h"
@@ -93,51 +94,6 @@
using namespace WTF;
using namespace Unicode;
-static inline bool isAmbiguousBoundaryCharacter(UChar character)
-{
- // These are characters that can behave as word boundaries, but can appear within words.
- // If they are just typed, i.e. if they are immediately followed by a caret, we want to delay text checking until the next character has been typed.
- // FIXME: this is required until 6853027 is fixed and text checking can do this for us.
- return character == '\'' || character == rightSingleQuotationMark || character == hebrewPunctuationGershayim;
-}
-
-static const Vector<DocumentMarker::MarkerType>& markerTypesForAutocorrection()
-{
- DEFINE_STATIC_LOCAL(Vector<DocumentMarker::MarkerType>, markerTypesForAutoCorrection, ());
- if (markerTypesForAutoCorrection.isEmpty()) {
- markerTypesForAutoCorrection.append(DocumentMarker::Replacement);
- markerTypesForAutoCorrection.append(DocumentMarker::CorrectionIndicator);
- markerTypesForAutoCorrection.append(DocumentMarker::SpellCheckingExemption);
- markerTypesForAutoCorrection.append(DocumentMarker::Autocorrected);
- }
- return markerTypesForAutoCorrection;
-}
-
-static const Vector<DocumentMarker::MarkerType>& markerTypesForReplacement()
-{
- DEFINE_STATIC_LOCAL(Vector<DocumentMarker::MarkerType>, markerTypesForReplacement, ());
- if (markerTypesForReplacement.isEmpty()) {
- markerTypesForReplacement.append(DocumentMarker::Replacement);
- markerTypesForReplacement.append(DocumentMarker::SpellCheckingExemption);
- }
- return markerTypesForReplacement;
-}
-
-#if SUPPORT_AUTOCORRECTION_PANEL
-static bool markersHaveIdenticalDescription(const Vector<DocumentMarker>& markers)
-{
- if (markers.isEmpty())
- return true;
-
- const String& description = markers[0].description;
- for (size_t i = 1; i < markers.size(); ++i) {
- if (description != markers[i].description)
- return false;
- }
- return true;
-}
-#endif
-
// When an event handler has moved the selection outside of a text control
// we should use the target control's selection for this editing operation.
VisibleSelection Editor::selectionForCommand(Event* event)
@@ -538,48 +494,7 @@
if (client())
client()->respondToChangedSelection();
m_deleteButtonController->respondToChangedSelection(oldSelection);
-
-#if SUPPORT_AUTOCORRECTION_PANEL
- VisibleSelection currentSelection(frame()->selection()->selection());
- // When user moves caret to the end of autocorrected word and pauses, we show the panel
- // containing the original pre-correction word so that user can quickly revert the
- // undesired autocorrection. Here, we start correction panel timer once we confirm that
- // the new caret position is at the end of a word.
- if (!currentSelection.isCaret() || currentSelection == oldSelection)
- return;
-
- VisiblePosition selectionPosition = currentSelection.start();
- VisiblePosition endPositionOfWord = endOfWord(selectionPosition, LeftWordIfOnBoundary);
- if (selectionPosition != endPositionOfWord)
- return;
-
- Position position = endPositionOfWord.deepEquivalent();
- if (position.anchorType() != Position::PositionIsOffsetInAnchor)
- return;
-
- Node* node = position.containerNode();
- int endOffset = position.offsetInContainerNode();
- Vector<DocumentMarker> markers = node->document()->markers()->markersForNode(node);
- size_t markerCount = markers.size();
- for (size_t i = 0; i < markerCount; ++i) {
- const DocumentMarker& marker = markers[i];
- if (((marker.type == DocumentMarker::Replacement && !marker.description.isNull()) || marker.type == DocumentMarker::Spelling) && static_cast<int>(marker.endOffset) == endOffset) {
- RefPtr<Range> wordRange = Range::create(frame()->document(), node, marker.startOffset, node, marker.endOffset);
- String currentWord = plainText(wordRange.get());
- if (currentWord.length()) {
- m_correctionPanelInfo.rangeToBeReplaced = wordRange;
- m_correctionPanelInfo.replacedString = currentWord;
- if (marker.type == DocumentMarker::Spelling)
- startCorrectionPanelTimer(CorrectionPanelInfo::PanelTypeSpellingSuggestions);
- else {
- m_correctionPanelInfo.replacementString = marker.description;
- startCorrectionPanelTimer(CorrectionPanelInfo::PanelTypeReversion);
- }
- }
- break;
- }
- }
-#endif // SUPPORT_AUTOCORRECTION_PANEL
+ m_spellingCorrector->respondToChangedSelection(oldSelection);
}
void Editor::respondToChangedContents(const VisibleSelection& endingSelection)
@@ -590,9 +505,7 @@
m_frame->document()->axObjectCache()->postNotification(node->renderer(), AXObjectCache::AXValueChanged, false);
}
-#if REMOVE_MARKERS_UPON_EDITING
- removeSpellAndCorrectionMarkersFromWordsToBeEdited(true);
-#endif
+ updateMarkersForWordsAffectedByEditing(true);
if (client())
client()->respondToChangedContents();
@@ -1079,10 +992,7 @@
dispatchEditableContentChangedEvents(*cmd);
VisibleSelection newSelection(cmd->endingSelection());
-#if SUPPORT_AUTOCORRECTION_PANEL
- if (cmd->isTopLevelCommand() && !cmd->shouldRetainAutocorrectionIndicator())
- m_frame->document()->markers()->removeMarkers(DocumentMarker::CorrectionIndicator);
-#endif
+ m_spellingCorrector->respondToAppliedEditing(cmd.get());
// Don't clear the typing style with this selection change. We do those things elsewhere if necessary.
changeSelectionAfterCommand(newSelection, false, false);
@@ -1141,17 +1051,14 @@
// This is off by default, since most editors want this behavior (this matches IE but not FF).
, m_shouldStyleWithCSS(false)
, m_killRing(adoptPtr(new KillRing))
- , m_spellChecker(new SpellChecker(frame, frame->page() ? frame->page()->editorClient()->textChecker() : 0))
- , m_correctionPanelTimer(this, &Editor::correctionPanelTimerFired)
+ , m_spellChecker(adoptPtr(new SpellChecker(frame, frame->page() ? frame->page()->editorClient()->textChecker() : 0)))
+ , m_spellingCorrector(adoptPtr(new SpellingCorrectionController(frame)))
, m_areMarkedTextMatchesHighlighted(false)
{
}
Editor::~Editor()
{
-#if SUPPORT_AUTOCORRECTION_PANEL
- dismissCorrectionPanel(ReasonForDismissingCorrectionPanelIgnored);
-#endif
}
void Editor::clear()
@@ -1184,10 +1091,8 @@
if (!shouldInsertText(text, range.get(), EditorInsertActionTyped))
return true;
-#if REMOVE_MARKERS_UPON_EDITING
if (!text.isEmpty())
- removeSpellAndCorrectionMarkersFromWordsToBeEdited(isSpaceOrNewline(text[0]));
-#endif
+ updateMarkersForWordsAffectedByEditing(text[0]);
bool shouldConsiderApplyingAutocorrection = false;
if (text == " " || text == "\t")
@@ -1196,7 +1101,7 @@
if (text.length() == 1 && isPunct(text[0]) && !isAmbiguousBoundaryCharacter(text[0]))
shouldConsiderApplyingAutocorrection = true;
- bool autocorrectionWasApplied = shouldConsiderApplyingAutocorrection && applyAutocorrectionBeforeTypingIfAppropriate();
+ bool autocorrectionWasApplied = shouldConsiderApplyingAutocorrection && m_spellingCorrector->applyAutocorrectionBeforeTypingIfAppropriate();
// Get the selection to use for the event that triggered this insertText.
// If the event handler changed the selection, we may want to use a different selection
@@ -1232,7 +1137,7 @@
if (!shouldInsertText("\n", m_frame->selection()->toNormalizedRange().get(), EditorInsertActionTyped))
return true;
- bool autocorrectionIsApplied = applyAutocorrectionBeforeTypingIfAppropriate();
+ bool autocorrectionIsApplied = m_spellingCorrector->applyAutocorrectionBeforeTypingIfAppropriate();
TypingCommand::insertLineBreak(m_frame->document(), autocorrectionIsApplied ? TypingCommand::RetainAutocorrectionIndicator : 0);
revealSelectionAfterEditingOperation();
@@ -1250,7 +1155,7 @@
if (!shouldInsertText("\n", m_frame->selection()->toNormalizedRange().get(), EditorInsertActionTyped))
return true;
- bool autocorrectionIsApplied = applyAutocorrectionBeforeTypingIfAppropriate();
+ bool autocorrectionIsApplied = m_spellingCorrector->applyAutocorrectionBeforeTypingIfAppropriate();
TypingCommand::insertParagraphSeparator(m_frame->document(), autocorrectionIsApplied ? TypingCommand::RetainAutocorrectionIndicator : 0);
revealSelectionAfterEditingOperation();
@@ -1267,9 +1172,7 @@
}
RefPtr<Range> selection = selectedRange();
if (shouldDeleteRange(selection.get())) {
-#if REMOVE_MARKERS_UPON_EDITING
- removeSpellAndCorrectionMarkersFromWordsToBeEdited(true);
-#endif
+ updateMarkersForWordsAffectedByEditing(true);
if (isNodeInTextFormControl(m_frame->selection()->start().deprecatedNode()))
Pasteboard::generalPasteboard()->writePlainText(selectedText());
else
@@ -1308,9 +1211,7 @@
return; // DHTML did the whole operation
if (!canPaste())
return;
-#if REMOVE_MARKERS_UPON_EDITING
- removeSpellAndCorrectionMarkersFromWordsToBeEdited(false);
-#endif
+ updateMarkersForWordsAffectedByEditing(false);
CachedResourceLoader* loader = m_frame->document()->cachedResourceLoader();
loader->setAllowStaleResources(true);
if (m_frame->selection()->isContentRichlyEditable())
@@ -1326,9 +1227,7 @@
return;
if (!canPaste())
return;
-#if REMOVE_MARKERS_UPON_EDITING
- removeSpellAndCorrectionMarkersFromWordsToBeEdited(false);
-#endif
+ updateMarkersForWordsAffectedByEditing(false);
pasteAsPlainTextWithPasteboard(Pasteboard::generalPasteboard());
}
@@ -1481,7 +1380,7 @@
bool Editor::isAutomaticSpellingCorrectionEnabled()
{
- return client() && client()->isAutomaticSpellingCorrectionEnabled();
+ return m_spellingCorrector->isAutomaticSpellingCorrectionEnabled();
}
void Editor::toggleAutomaticSpellingCorrection()
@@ -2073,23 +1972,8 @@
void Editor::markMisspellingsAfterTypingToWord(const VisiblePosition &wordStart, const VisibleSelection& selectionAfterTyping)
{
#if USE(UNIFIED_TEXT_CHECKING)
-#if SUPPORT_AUTOCORRECTION_PANEL
- // Apply pending autocorrection before next round of spell checking.
- bool doApplyCorrection = true;
- VisiblePosition startOfSelection = selectionAfterTyping.visibleStart();
- VisibleSelection currentWord = VisibleSelection(startOfWord(startOfSelection, LeftWordIfOnBoundary), endOfWord(startOfSelection, RightWordIfOnBoundary));
- if (currentWord.visibleEnd() == startOfSelection) {
- String wordText = plainText(currentWord.toNormalizedRange().get());
- if (wordText.length() > 0 && isAmbiguousBoundaryCharacter(wordText[wordText.length() - 1]))
- doApplyCorrection = false;
- }
- if (doApplyCorrection)
- handleCorrectionPanelResult(dismissCorrectionPanelSoon(ReasonForDismissingCorrectionPanelAccepted));
- else
- m_correctionPanelInfo.rangeToBeReplaced.clear();
-#else
- UNUSED_PARAM(selectionAfterTyping);
-#endif
+ m_spellingCorrector->applyPendingCorrection(selectionAfterTyping);
+
TextCheckingOptions textCheckingOptions = 0;
if (isContinuousSpellCheckingEnabled())
textCheckingOptions |= MarkSpelling;
@@ -2118,7 +2002,6 @@
}
#else
- UNUSED_PARAM(selectionAfterTyping);
if (!isContinuousSpellCheckingEnabled())
return;
@@ -2227,7 +2110,7 @@
{
#if USE(UNIFIED_TEXT_CHECKING)
// There shouldn't be pending autocorrection at this moment.
- ASSERT(!m_correctionPanelInfo.rangeToBeReplaced);
+ ASSERT(!m_spellingCorrector->hasPendingCorrection());
bool shouldMarkSpelling = textCheckingOptions & MarkSpelling;
bool shouldMarkGrammar = textCheckingOptions & MarkGrammar;
@@ -2286,11 +2169,9 @@
textCheckingTypeMaskFor(textCheckingOptions), results);
-#if SUPPORT_AUTOCORRECTION_PANEL
// If this checking is only for showing correction panel, we shouldn't bother to mark misspellings.
if (shouldShowCorrectionPanel)
shouldMarkSpelling = false;
-#endif
int offsetDueToReplacement = 0;
@@ -2309,10 +2190,8 @@
if (shouldMarkSpelling && result->type == TextCheckingTypeSpelling && resultLocation >= spellingParagraph.checkingStart() && resultLocation + resultLength <= spellingRangeEndOffset && !resultEndsAtAmbiguousBoundary) {
ASSERT(resultLength > 0 && resultLocation >= 0);
RefPtr<Range> misspellingRange = spellingParagraph.subrange(resultLocation, resultLength);
-#if SUPPORT_AUTOCORRECTION_PANEL
- if (m_frame->document()->markers()->hasMarkers(misspellingRange.get(), DocumentMarker::SpellCheckingExemption))
+ if (!m_spellingCorrector->isSpellingMarkerAllowed(misspellingRange))
continue;
-#endif // SUPPORT_AUTOCORRECTION_PANEL
misspellingRange->startContainer(ec)->document()->markers()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
} else if (shouldMarkGrammar && result->type == TextCheckingTypeGrammar && grammarParagraph.checkingRangeCovers(resultLocation, resultLength)) {
ASSERT(resultLength > 0 && resultLocation >= 0);
@@ -2358,7 +2237,7 @@
DocumentMarkerController* markers = m_frame->document()->markers();
if (markers->hasMarkers(rangeToReplace.get(), DocumentMarker::Replacement)) {
doReplacement = false;
- recordSpellcheckerResponseForModifiedCorrection(rangeToReplace.get(), replacedString, result->replacement);
+ m_spellingCorrector->recordSpellcheckerResponseForModifiedCorrection(rangeToReplace.get(), replacedString, result->replacement);
} else if (markers->hasMarkers(rangeToReplace.get(), DocumentMarker::RejectedCorrection))
doReplacement = false;
}
@@ -2366,24 +2245,17 @@
if (!(shouldPerformReplacement || shouldShowCorrectionPanel) || !doReplacement)
continue;
-#if SUPPORT_AUTOCORRECTION_PANEL
if (shouldShowCorrectionPanel) {
+ ASSERT(SUPPORT_AUTOCORRECTION_PANEL);
+ // shouldShowCorrectionPanel can be true only when the panel is available.
if (resultLocation + resultLength == spellingRangeEndOffset) {
// We only show the correction panel on the last word.
- FloatRect boundingBox = windowRectForRange(rangeToReplace.get());
- if (boundingBox.isEmpty())
- break;
- m_correctionPanelInfo.rangeToBeReplaced = rangeToReplace;
- m_correctionPanelInfo.replacedString = plainText(rangeToReplace.get());
- m_correctionPanelInfo.replacementString = result->replacement;
- m_correctionPanelInfo.isActive = true;
- client()->showCorrectionPanel(m_correctionPanelInfo.panelType, boundingBox, m_correctionPanelInfo.replacedString, result->replacement, Vector<String>());
+ m_spellingCorrector->show(rangeToReplace, result->replacement);
break;
}
// If this function is called for showing correction panel, we ignore other correction or replacement.
continue;
}
-#endif
if (selectionToReplace != m_frame->selection()->selection()) {
if (!m_frame->selection()->shouldChangeSelection(selectionToReplace))
@@ -2397,12 +2269,7 @@
if (canEditRichly())
applyCommand(CreateLinkCommand::create(m_frame->document(), result->replacement));
} else if (canEdit() && shouldInsertText(result->replacement, rangeToReplace.get(), EditorInsertActionTyped)) {
- bool useSpellingCorrectionCommand = false;
-#if SUPPORT_AUTOCORRECTION_PANEL
if (result->type == TextCheckingTypeCorrection)
- useSpellingCorrectionCommand = true;
-#endif
- if (useSpellingCorrectionCommand)
applyCommand(SpellingCorrectionCommand::create(rangeToReplace, result->replacement));
else {
m_frame->selection()->setSelection(selectionToReplace);
@@ -2422,19 +2289,9 @@
ambiguousBoundaryOffset = selectionOffset - 1;
}
- if (result->type == TextCheckingTypeCorrection) {
- // Add a marker so that corrections can easily be undone and won't be re-corrected.
- RefPtr<Range> replacedRange = spellingParagraph.subrange(resultLocation, replacementLength);
- Vector<DocumentMarker::MarkerType> markerTypesToAdd = markerTypesForAutocorrection();
- DocumentMarkerController* markers = replacedRange->startContainer()->document()->markers();
- for (size_t i = 0; i < markerTypesToAdd.size(); ++i) {
- DocumentMarker::MarkerType markerType = markerTypesToAdd[i];
- if (markerType == DocumentMarker::Replacement || markerType == DocumentMarker::Autocorrected)
- markers->addMarker(replacedRange.get(), markerType, replacedString);
- else
- markers->addMarker(replacedRange.get(), markerType);
- }
- }
+ // Add a marker so that corrections can easily be undone and won't be re-corrected.
+ if (result->type == TextCheckingTypeCorrection)
+ m_spellingCorrector->markCorrection(spellingParagraph.subrange(resultLocation, replacementLength), replacedString);
}
}
}
@@ -2461,30 +2318,6 @@
#endif // USE(UNIFIED_TEXT_CHECKING)
}
-void Editor::recordSpellcheckerResponseForModifiedCorrection(Range* rangeOfCorrection, const String& corrected, const String& correction)
-{
-#if SUPPORT_AUTOCORRECTION_PANEL
- if (!rangeOfCorrection)
- return;
- DocumentMarkerController* markers = rangeOfCorrection->startContainer()->document()->markers();
- Vector<DocumentMarker> correctedOnceMarkers = markers->markersInRange(rangeOfCorrection, DocumentMarker::Autocorrected);
- if (correctedOnceMarkers.isEmpty())
- return;
-
- // Spelling corrected text has been edited. We need to determine whether user has reverted it to original text or
- // edited it to something else, and notify spellchecker accordingly.
- if (markersHaveIdenticalDescription(correctedOnceMarkers) && correctedOnceMarkers[0].description == corrected)
- client()->recordAutocorrectionResponse(EditorClient::AutocorrectionReverted, corrected, correction);
- else
- client()->recordAutocorrectionResponse(EditorClient::AutocorrectionEdited, corrected, correction);
- markers->removeMarkers(rangeOfCorrection, DocumentMarker::Autocorrected, DocumentMarkerController::RemovePartiallyOverlappingMarker);
-#else
- UNUSED_PARAM(rangeOfCorrection);
- UNUSED_PARAM(corrected);
- UNUSED_PARAM(correction);
-#endif
-}
-
void Editor::changeBackToReplacedString(const String& replacedString)
{
#if USE(UNIFIED_TEXT_CHECKING)
@@ -2495,18 +2328,12 @@
if (!shouldInsertText(replacedString, selection.get(), EditorInsertActionPasted))
return;
-#if SUPPORT_AUTOCORRECTION_PANEL
- String replacement = plainText(selection.get());
- client()->recordAutocorrectionResponse(EditorClient::AutocorrectionReverted, replacedString, replacement);
-#endif
+ m_spellingCorrector->recordAutocorrectionResponseReversed(replacedString, selection);
TextCheckingParagraph paragraph(selection);
replaceSelectionWithText(replacedString, false, false);
RefPtr<Range> changedRange = paragraph.subrange(paragraph.checkingStart(), replacedString.length());
changedRange->startContainer()->document()->markers()->addMarker(changedRange.get(), DocumentMarker::Replacement, String());
-#if SUPPORT_AUTOCORRECTION_PANEL
- changedRange->startContainer()->document()->markers()->removeMarkers(changedRange.get(), DocumentMarker::Autocorrected, DocumentMarkerController::RemovePartiallyOverlappingMarker);
- changedRange->startContainer()->document()->markers()->addMarker(changedRange.get(), DocumentMarker::SpellCheckingExemption);
-#endif
+ m_spellingCorrector->markReversed(changedRange.get());
#else
ASSERT_NOT_REACHED();
UNUSED_PARAM(replacedString);
@@ -2531,140 +2358,16 @@
#endif
}
-void Editor::correctionPanelTimerFired(Timer<Editor>*)
+void Editor::unappliedSpellCorrection(const VisibleSelection& selectionOfCorrected, const String& corrected, const String& correction)
{
-#if SUPPORT_AUTOCORRECTION_PANEL
- m_correctionPanelIsDismissedByEditor = false;
- switch (m_correctionPanelInfo.panelType) {
- case CorrectionPanelInfo::PanelTypeCorrection: {
- VisibleSelection selection(frame()->selection()->selection());
- VisiblePosition start(selection.start(), selection.affinity());
- VisiblePosition p = startOfWord(start, LeftWordIfOnBoundary);
- VisibleSelection adjacentWords = VisibleSelection(p, start);
- markAllMisspellingsAndBadGrammarInRanges(MarkSpelling | ShowCorrectionPanel, adjacentWords.toNormalizedRange().get(), 0);
- }
- break;
- case CorrectionPanelInfo::PanelTypeReversion: {
- m_correctionPanelInfo.isActive = true;
- m_correctionPanelInfo.replacedString = plainText(m_correctionPanelInfo.rangeToBeReplaced.get());
- FloatRect boundingBox = windowRectForRange(m_correctionPanelInfo.rangeToBeReplaced.get());
- if (!boundingBox.isEmpty())
- client()->showCorrectionPanel(m_correctionPanelInfo.panelType, boundingBox, m_correctionPanelInfo.replacedString, m_correctionPanelInfo.replacementString, Vector<String>());
- }
- break;
- case CorrectionPanelInfo::PanelTypeSpellingSuggestions: {
- if (plainText(m_correctionPanelInfo.rangeToBeReplaced.get()) != m_correctionPanelInfo.replacedString)
- break;
- String paragraphText = plainText(TextCheckingParagraph(m_correctionPanelInfo.rangeToBeReplaced).paragraphRange().get());
- Vector<String> suggestions;
- textChecker()->getGuessesForWord(m_correctionPanelInfo.replacedString, paragraphText, suggestions);
- if (suggestions.isEmpty()) {
- m_correctionPanelInfo.rangeToBeReplaced.clear();
- break;
- }
- String topSuggestion = suggestions.first();
- suggestions.remove(0);
- m_correctionPanelInfo.isActive = true;
- FloatRect boundingBox = windowRectForRange(m_correctionPanelInfo.rangeToBeReplaced.get());
- if (!boundingBox.isEmpty())
- client()->showCorrectionPanel(m_correctionPanelInfo.panelType, boundingBox, m_correctionPanelInfo.replacedString, topSuggestion, suggestions);
- }
- break;
- }
-#endif
+ m_spellingCorrector->respondToUnappliedSpellCorrection(selectionOfCorrected, corrected, correction);
}
-void Editor::handleCorrectionPanelResult(const String& correction)
+void Editor::updateMarkersForWordsAffectedByEditing(bool doNotRemoveIfSelectionAtWordBoundary)
{
- Range* replacedRange = m_correctionPanelInfo.rangeToBeReplaced.get();
- if (!replacedRange || m_frame->document() != replacedRange->ownerDocument())
+ if (!m_spellingCorrector->shouldRemoveMarkersUponEditing())
return;
- String currentWord = plainText(m_correctionPanelInfo.rangeToBeReplaced.get());
- // Check to see if the word we are about to correct has been changed between timer firing and callback being triggered.
- if (currentWord != m_correctionPanelInfo.replacedString)
- return;
-
- m_correctionPanelInfo.isActive = false;
-
- switch (m_correctionPanelInfo.panelType) {
- case CorrectionPanelInfo::PanelTypeCorrection:
- if (correction.length()) {
- m_correctionPanelInfo.replacementString = correction;
- applyCorrectionPanelInfo(markerTypesForAutocorrection());
- } else {
- if (!m_correctionPanelIsDismissedByEditor)
- replacedRange->startContainer()->document()->markers()->addMarker(replacedRange, DocumentMarker::RejectedCorrection, m_correctionPanelInfo.replacedString);
- }
- break;
- case CorrectionPanelInfo::PanelTypeReversion:
- case CorrectionPanelInfo::PanelTypeSpellingSuggestions:
- if (correction.length()) {
- m_correctionPanelInfo.replacementString = correction;
- applyCorrectionPanelInfo(markerTypesForReplacement());
- }
- break;
- }
-
- m_correctionPanelInfo.rangeToBeReplaced.clear();
-}
-
-void Editor::startCorrectionPanelTimer(CorrectionPanelInfo::PanelType type)
-{
-#if SUPPORT_AUTOCORRECTION_PANEL
- const double correctionPanelTimerInterval = 0.3;
- if (isAutomaticSpellingCorrectionEnabled()) {
- if (type == CorrectionPanelInfo::PanelTypeCorrection)
- // If type is PanelTypeReversion, then the new range has been set. So we shouldn't clear it.
- m_correctionPanelInfo.rangeToBeReplaced.clear();
- m_correctionPanelInfo.panelType = type;
- m_correctionPanelTimer.startOneShot(correctionPanelTimerInterval);
- }
-#else
- UNUSED_PARAM(type);
-#endif
-}
-
-void Editor::stopCorrectionPanelTimer()
-{
-#if SUPPORT_AUTOCORRECTION_PANEL
- m_correctionPanelTimer.stop();
- m_correctionPanelInfo.rangeToBeReplaced.clear();
-#endif
-}
-
-void Editor::dismissCorrectionPanel(ReasonForDismissingCorrectionPanel reasonForDismissing)
-{
-#if SUPPORT_AUTOCORRECTION_PANEL
- if (!m_correctionPanelInfo.isActive)
- return;
- m_correctionPanelInfo.isActive = false;
- m_correctionPanelIsDismissedByEditor = true;
- if (client())
- client()->dismissCorrectionPanel(reasonForDismissing);
-#else
- UNUSED_PARAM(reasonForDismissing);
-#endif
-}
-
-String Editor::dismissCorrectionPanelSoon(ReasonForDismissingCorrectionPanel reasonForDismissing)
-{
-#if SUPPORT_AUTOCORRECTION_PANEL
- if (!m_correctionPanelInfo.isActive)
- return String();
- m_correctionPanelInfo.isActive = false;
- m_correctionPanelIsDismissedByEditor = true;
- if (!client())
- return String();
- return client()->dismissCorrectionPanelSoon(reasonForDismissing);
-#else
- UNUSED_PARAM(reasonForDismissing);
- return String();
-#endif
-}
-
-void Editor::removeSpellAndCorrectionMarkersFromWordsToBeEdited(bool doNotRemoveIfSelectionAtWordBoundary)
-{
// We want to remove the markers from a word if an editing command will change the word. This can happen in one of
// several scenarios:
// 1. Insert in the middle of a word.
@@ -2730,103 +2433,6 @@
document->markers()->clearDescriptionOnMarkersIntersectingRange(wordRange.get(), DocumentMarker::Replacement);
}
-void Editor::applyCorrectionPanelInfo(const Vector<DocumentMarker::MarkerType>& markerTypesToAdd)
-{
-#if SUPPORT_AUTOCORRECTION_PANEL
- if (!m_correctionPanelInfo.rangeToBeReplaced)
- return;
-
- ExceptionCode ec = 0;
- RefPtr<Range> paragraphRangeContainingCorrection = m_correctionPanelInfo.rangeToBeReplaced->cloneRange(ec);
- if (ec)
- return;
-
- setStart(paragraphRangeContainingCorrection.get(), startOfParagraph(m_correctionPanelInfo.rangeToBeReplaced->startPosition()));
- setEnd(paragraphRangeContainingCorrection.get(), endOfParagraph(m_correctionPanelInfo.rangeToBeReplaced->endPosition()));
-
- // After we replace the word at range rangeToBeReplaced, we need to add markers to that range.
- // However, once the replacement took place, the value of rangeToBeReplaced is not valid anymore.
- // So before we carry out the replacement, we need to store the start position of rangeToBeReplaced
- // relative to the start position of the containing paragraph. We use correctionStartOffsetInParagraph
- // to store this value. In order to obtain this offset, we need to first create a range
- // which spans from the start of paragraph to the start position of rangeToBeReplaced.
- RefPtr<Range> correctionStartOffsetInParagraphAsRange = Range::create(paragraphRangeContainingCorrection->startContainer(ec)->document(), paragraphRangeContainingCorrection->startPosition(), paragraphRangeContainingCorrection->startPosition());
- if (ec)
- return;
-
- Position startPositionOfRangeToBeReplaced = m_correctionPanelInfo.rangeToBeReplaced->startPosition();
- correctionStartOffsetInParagraphAsRange->setEnd(startPositionOfRangeToBeReplaced.containerNode(), startPositionOfRangeToBeReplaced.computeOffsetInContainerNode(), ec);
- if (ec)
- return;
-
- // Take note of the location of autocorrection so that we can add marker after the replacement took place.
- int correctionStartOffsetInParagraph = TextIterator::rangeLength(correctionStartOffsetInParagraphAsRange.get());
-
- // Clone the range, since the caller of this method may want to keep the original range around.
- RefPtr<Range> rangeToBeReplaced = m_correctionPanelInfo.rangeToBeReplaced->cloneRange(ec);
- applyCommand(SpellingCorrectionCommand::create(rangeToBeReplaced, m_correctionPanelInfo.replacementString));
- setEnd(paragraphRangeContainingCorrection.get(), m_frame->selection()->selection().start());
- RefPtr<Range> replacementRange = TextIterator::subrange(paragraphRangeContainingCorrection.get(), correctionStartOffsetInParagraph, m_correctionPanelInfo.replacementString.length());
- String newText = plainText(replacementRange.get());
-
- // Check to see if replacement succeeded.
- if (newText != m_correctionPanelInfo.replacementString)
- return;
-
- DocumentMarkerController* markers = replacementRange->startContainer()->document()->markers();
- size_t size = markerTypesToAdd.size();
- for (size_t i = 0; i < size; ++i) {
- DocumentMarker::MarkerType markerType = markerTypesToAdd[i];
- String description;
- if (m_correctionPanelInfo.panelType != CorrectionPanelInfo::PanelTypeReversion && (markerType == DocumentMarker::Replacement || markerType == DocumentMarker::Autocorrected))
- description = m_correctionPanelInfo.replacedString;
- markers->addMarker(replacementRange.get(), markerType, description);
- }
-#else // SUPPORT_AUTOCORRECTION_PANEL
- UNUSED_PARAM(markerTypesToAdd);
-#endif // SUPPORT_AUTOCORRECTION_PANEL
-}
-
-bool Editor::applyAutocorrectionBeforeTypingIfAppropriate()
-{
- if (!m_correctionPanelInfo.rangeToBeReplaced || !m_correctionPanelInfo.isActive)
- return false;
-
- if (m_correctionPanelInfo.panelType != CorrectionPanelInfo::PanelTypeCorrection)
- return false;
-
- Position caretPosition = m_frame->selection()->selection().start();
-
- if (m_correctionPanelInfo.rangeToBeReplaced->endPosition() == caretPosition) {
- handleCorrectionPanelResult(dismissCorrectionPanelSoon(ReasonForDismissingCorrectionPanelAccepted));
- return true;
- }
-
- // Pending correction should always be where caret is. But in case this is not always true, we still want to dismiss the panel without accepting the correction.
- ASSERT(m_correctionPanelInfo.rangeToBeReplaced->endPosition() == caretPosition);
- dismissCorrectionPanel(ReasonForDismissingCorrectionPanelIgnored);
- return false;
-}
-
-void Editor::unappliedSpellCorrection(const VisibleSelection& selectionOfCorrected, const String& corrected, const String& correction)
-{
-#if SUPPORT_AUTOCORRECTION_PANEL
- client()->recordAutocorrectionResponse(EditorClient::AutocorrectionReverted, corrected, correction);
- m_frame->document()->updateLayout();
- m_frame->selection()->setSelection(selectionOfCorrected, SelectionController::CloseTyping | SelectionController::ClearTypingStyle | SelectionController::SpellCorrectionTriggered);
- RefPtr<Range> range = Range::create(m_frame->document(), m_frame->selection()->selection().start(), m_frame->selection()->selection().end());
-
- DocumentMarkerController* markers = m_frame->document()->markers();
- markers->removeMarkers(range.get(), DocumentMarker::Spelling | DocumentMarker::Autocorrected, DocumentMarkerController::RemovePartiallyOverlappingMarker);
- markers->addMarker(range.get(), DocumentMarker::Replacement);
- markers->addMarker(range.get(), DocumentMarker::SpellCheckingExemption);
-#else // SUPPORT_AUTOCORRECTION_PANEL
- UNUSED_PARAM(selectionOfCorrected);
- UNUSED_PARAM(corrected);
- UNUSED_PARAM(correction);
-#endif // SUPPORT_AUTOCORRECTION_PANEL
-}
-
PassRefPtr<Range> Editor::rangeForPoint(const IntPoint& windowPoint)
{
Document* document = m_frame->documentAtPoint(windowPoint);
@@ -2949,6 +2555,22 @@
m_shouldStartNewKillRingSequence = false;
}
+void Editor::startCorrectionPanelTimer()
+{
+ m_spellingCorrector->startCorrectionPanelTimer(CorrectionPanelInfo::PanelTypeCorrection);
+}
+
+void Editor::handleCorrectionPanelResult(const String& correction)
+{
+ m_spellingCorrector->handleCorrectionPanelResult(correction);
+}
+
+
+void Editor::dismissCorrectionPanelAsIgnored()
+{
+ m_spellingCorrector->dismiss(ReasonForDismissingCorrectionPanelIgnored);
+}
+
bool Editor::insideVisibleArea(const IntPoint& point) const
{
if (m_frame->excludeFromTextSearch())
@@ -3521,14 +3143,7 @@
void Editor::respondToChangedSelection(const VisibleSelection& oldSelection, SelectionController::SetSelectionOptions options)
{
-#if SUPPORT_AUTOCORRECTION_PANEL
- // Make sure there's no pending autocorrection before we call markMisspellingsAndBadGrammar() below.
- VisibleSelection currentSelection(frame()->selection()->selection());
- if (currentSelection != oldSelection) {
- stopCorrectionPanelTimer();
- dismissCorrectionPanel(ReasonForDismissingCorrectionPanelIgnored);
- }
-#endif // SUPPORT_AUTOCORRECTION_PANEL
+ m_spellingCorrector->stopPendingCorrection(oldSelection);
bool closeTyping = options & SelectionController::CloseTyping;
bool isContinuousSpellCheckingEnabled = this->isContinuousSpellCheckingEnabled();
@@ -3544,11 +3159,8 @@
newSelectedSentence = VisibleSelection(startOfSentence(newStart), endOfSentence(newStart));
}
- bool shouldCheckSpellingAndGrammar = true;
-#if SUPPORT_AUTOCORRECTION_PANEL
// Don't check spelling and grammar if the change of selection is triggered by spelling correction itself.
- shouldCheckSpellingAndGrammar = !(options & SelectionController::SpellCorrectionTriggered);
-#endif
+ bool shouldCheckSpellingAndGrammar = !(options & SelectionController::SpellCorrectionTriggered);
// When typing we check spelling elsewhere, so don't redo it here.
// If this is a change in selection resulting from a delete operation,
diff --git a/Source/WebCore/editing/Editor.h b/Source/WebCore/editing/Editor.h
index f9aec40..8b91153 100644
--- a/Source/WebCore/editing/Editor.h
+++ b/Source/WebCore/editing/Editor.h
@@ -28,7 +28,6 @@
#include "ClipboardAccessPolicy.h"
#include "Color.h"
-#include "CorrectionPanelInfo.h"
#include "DocumentMarker.h"
#include "EditAction.h"
#include "EditingBehavior.h"
@@ -51,6 +50,7 @@
class CSSMutableStyleDeclaration;
class CSSStyleDeclaration;
class Clipboard;
+class SpellingCorrectionController;
class DeleteButtonController;
class EditCommand;
class EditorClient;
@@ -325,10 +325,10 @@
void addToKillRing(Range*, bool prepend);
- void startCorrectionPanelTimer(CorrectionPanelInfo::PanelType);
+ void startCorrectionPanelTimer();
// If user confirmed a correction in the correction panel, correction has non-zero length, otherwise it means that user has dismissed the panel.
void handleCorrectionPanelResult(const String& correction);
- void dismissCorrectionPanel(ReasonForDismissingCorrectionPanel);
+ void dismissCorrectionPanelAsIgnored();
void pasteAsFragment(PassRefPtr<DocumentFragment>, bool smartReplace, bool matchStyle);
void pasteAsPlainText(const String&, bool smartReplace);
@@ -384,7 +384,7 @@
#endif
bool selectionStartHasMarkerFor(DocumentMarker::MarkerType, int from, int length) const;
- void removeSpellAndCorrectionMarkersFromWordsToBeEdited(bool doNotRemoveIfSelectionAtWordBoundary);
+ void updateMarkersForWordsAffectedByEditing(bool onlyHandleWordsContainingSelection);
private:
Frame* m_frame;
@@ -399,10 +399,8 @@
bool m_shouldStartNewKillRingSequence;
bool m_shouldStyleWithCSS;
OwnPtr<KillRing> m_killRing;
- CorrectionPanelInfo m_correctionPanelInfo;
OwnPtr<SpellChecker> m_spellChecker;
- Timer<Editor> m_correctionPanelTimer;
- bool m_correctionPanelIsDismissedByEditor;
+ OwnPtr<SpellingCorrectionController> m_spellingCorrector;
VisibleSelection m_mark;
bool m_areMarkedTextMatchesHighlighted;
@@ -417,7 +415,6 @@
void revealSelectionAfterEditingOperation();
void markMisspellingsOrBadGrammar(const VisibleSelection&, bool checkSpelling, RefPtr<Range>& firstMisspellingRange);
TextCheckingTypeMask textCheckingTypeMaskFor(TextCheckingOptions);
- void recordSpellcheckerResponseForModifiedCorrection(Range*, const String& corrected, const String& correction);
void selectComposition();
void confirmComposition(const String&, bool preserveSelection);
@@ -428,10 +425,10 @@
PassRefPtr<Range> nextVisibleRange(Range*, const String&, FindOptions);
void changeSelectionAfterCommand(const VisibleSelection& newSelection, bool closeTyping, bool clearTypingStyle);
- void correctionPanelTimerFired(Timer<Editor>*);
+
Node* findEventTargetFromSelection() const;
void stopCorrectionPanelTimer();
- String dismissCorrectionPanelSoon(ReasonForDismissingCorrectionPanel);
+
void applyCorrectionPanelInfo(const Vector<DocumentMarker::MarkerType>& markerTypesToAdd);
// Return true if correction was applied, false otherwise.
bool applyAutocorrectionBeforeTypingIfAppropriate();
diff --git a/Source/WebCore/editing/SpellingCorrectionCommand.cpp b/Source/WebCore/editing/SpellingCorrectionCommand.cpp
index afe74a8..f0297c1 100644
--- a/Source/WebCore/editing/SpellingCorrectionCommand.cpp
+++ b/Source/WebCore/editing/SpellingCorrectionCommand.cpp
@@ -26,7 +26,7 @@
#include "config.h"
#include "SpellingCorrectionCommand.h"
-#include "CorrectionPanelInfo.h"
+#include "SpellingCorrectionController.h"
#include "DocumentFragment.h"
#include "Frame.h"
#include "ReplaceSelectionCommand.h"
diff --git a/Source/WebCore/editing/SpellingCorrectionController.cpp b/Source/WebCore/editing/SpellingCorrectionController.cpp
new file mode 100644
index 0000000..c377fe8
--- /dev/null
+++ b/Source/WebCore/editing/SpellingCorrectionController.cpp
@@ -0,0 +1,479 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "SpellingCorrectionController.h"
+
+#include "DocumentMarkerController.h"
+#include "EditCommand.h"
+#include "EditorClient.h"
+#include "Frame.h"
+#include "FrameView.h"
+#include "SpellingCorrectionCommand.h"
+#include "TextCheckerClient.h"
+#include "TextCheckingHelper.h"
+#include "TextIterator.h"
+#include "htmlediting.h"
+#include "markup.h"
+#include "visible_units.h"
+
+
+namespace WebCore {
+
+using namespace std;
+using namespace WTF;
+
+#if SUPPORT_AUTOCORRECTION_PANEL
+
+static const Vector<DocumentMarker::MarkerType>& markerTypesForAutocorrection()
+{
+ DEFINE_STATIC_LOCAL(Vector<DocumentMarker::MarkerType>, markerTypesForAutoCorrection, ());
+ if (markerTypesForAutoCorrection.isEmpty()) {
+ markerTypesForAutoCorrection.append(DocumentMarker::Replacement);
+ markerTypesForAutoCorrection.append(DocumentMarker::CorrectionIndicator);
+ markerTypesForAutoCorrection.append(DocumentMarker::SpellCheckingExemption);
+ markerTypesForAutoCorrection.append(DocumentMarker::Autocorrected);
+ }
+ return markerTypesForAutoCorrection;
+}
+
+static const Vector<DocumentMarker::MarkerType>& markerTypesForReplacement()
+{
+ DEFINE_STATIC_LOCAL(Vector<DocumentMarker::MarkerType>, markerTypesForReplacement, ());
+ if (markerTypesForReplacement.isEmpty()) {
+ markerTypesForReplacement.append(DocumentMarker::Replacement);
+ markerTypesForReplacement.append(DocumentMarker::SpellCheckingExemption);
+ }
+ return markerTypesForReplacement;
+}
+
+static bool markersHaveIdenticalDescription(const Vector<DocumentMarker>& markers)
+{
+ if (markers.isEmpty())
+ return true;
+
+ const String& description = markers[0].description;
+ for (size_t i = 1; i < markers.size(); ++i) {
+ if (description != markers[i].description)
+ return false;
+ }
+ return true;
+}
+
+SpellingCorrectionController::SpellingCorrectionController(Frame* frame)
+ : m_frame(frame)
+ , m_correctionPanelTimer(this, &SpellingCorrectionController::correctionPanelTimerFired)
+{
+}
+
+SpellingCorrectionController::~SpellingCorrectionController()
+{
+ dismiss(ReasonForDismissingCorrectionPanelIgnored);
+}
+
+void SpellingCorrectionController::startCorrectionPanelTimer(CorrectionPanelInfo::PanelType type)
+{
+ const double correctionPanelTimerInterval = 0.3;
+ if (!isAutomaticSpellingCorrectionEnabled())
+ return;
+
+ // If type is PanelTypeReversion, then the new range has been set. So we shouldn't clear it.
+ if (type == CorrectionPanelInfo::PanelTypeCorrection)
+ m_correctionPanelInfo.rangeToBeReplaced.clear();
+ m_correctionPanelInfo.panelType = type;
+ m_correctionPanelTimer.startOneShot(correctionPanelTimerInterval);
+}
+
+void SpellingCorrectionController::stopCorrectionPanelTimer()
+{
+ m_correctionPanelTimer.stop();
+ m_correctionPanelInfo.rangeToBeReplaced.clear();
+}
+
+void SpellingCorrectionController::stopPendingCorrection(const VisibleSelection& oldSelection)
+{
+ // Make sure there's no pending autocorrection before we call markMisspellingsAndBadGrammar() below.
+ VisibleSelection currentSelection(m_frame->selection()->selection());
+ if (currentSelection == oldSelection)
+ return;
+
+ stopCorrectionPanelTimer();
+ dismiss(ReasonForDismissingCorrectionPanelIgnored);
+}
+
+void SpellingCorrectionController::applyPendingCorrection(const VisibleSelection& selectionAfterTyping)
+{
+ // Apply pending autocorrection before next round of spell checking.
+ bool doApplyCorrection = true;
+ VisiblePosition startOfSelection = selectionAfterTyping.visibleStart();
+ VisibleSelection currentWord = VisibleSelection(startOfWord(startOfSelection, LeftWordIfOnBoundary), endOfWord(startOfSelection, RightWordIfOnBoundary));
+ if (currentWord.visibleEnd() == startOfSelection) {
+ String wordText = plainText(currentWord.toNormalizedRange().get());
+ if (wordText.length() > 0 && isAmbiguousBoundaryCharacter(wordText[wordText.length() - 1]))
+ doApplyCorrection = false;
+ }
+ if (doApplyCorrection)
+ handleCorrectionPanelResult(dismissSoon(ReasonForDismissingCorrectionPanelAccepted));
+ else
+ m_correctionPanelInfo.rangeToBeReplaced.clear();
+}
+
+bool SpellingCorrectionController::hasPendingCorrection() const
+{
+ return m_correctionPanelInfo.rangeToBeReplaced;
+}
+
+bool SpellingCorrectionController::isSpellingMarkerAllowed(PassRefPtr<Range> misspellingRange) const
+{
+ return !m_frame->document()->markers()->hasMarkers(misspellingRange.get(), DocumentMarker::SpellCheckingExemption);
+}
+
+void SpellingCorrectionController::show(PassRefPtr<Range> rangeToReplace, const String& replacement)
+{
+ FloatRect boundingBox = windowRectForRange(rangeToReplace.get());
+ if (boundingBox.isEmpty())
+ return;
+ m_correctionPanelInfo.replacedString = plainText(rangeToReplace.get());
+ m_correctionPanelInfo.rangeToBeReplaced = rangeToReplace;
+ m_correctionPanelInfo.replacementString = replacement;
+ m_correctionPanelInfo.isActive = true;
+ client()->showCorrectionPanel(m_correctionPanelInfo.panelType, boundingBox, m_correctionPanelInfo.replacedString, replacement, Vector<String>());
+}
+
+void SpellingCorrectionController::handleCancelOperation()
+{
+ if (!m_correctionPanelInfo.isActive)
+ return;
+ m_correctionPanelInfo.isActive = false;
+ dismiss(ReasonForDismissingCorrectionPanelCancelled);
+}
+
+void SpellingCorrectionController::dismiss(ReasonForDismissingCorrectionPanel reasonForDismissing)
+{
+ if (!m_correctionPanelInfo.isActive)
+ return;
+ m_correctionPanelInfo.isActive = false;
+ m_correctionPanelIsDismissedByEditor = true;
+ if (client())
+ client()->dismissCorrectionPanel(reasonForDismissing);
+}
+
+String SpellingCorrectionController::dismissSoon(ReasonForDismissingCorrectionPanel reasonForDismissing)
+{
+ if (!m_correctionPanelInfo.isActive)
+ return String();
+ m_correctionPanelInfo.isActive = false;
+ m_correctionPanelIsDismissedByEditor = true;
+ if (!client())
+ return String();
+ return client()->dismissCorrectionPanelSoon(reasonForDismissing);
+}
+
+void SpellingCorrectionController::applyCorrectionPanelInfo(const Vector<DocumentMarker::MarkerType>& markerTypesToAdd)
+{
+ if (!m_correctionPanelInfo.rangeToBeReplaced)
+ return;
+
+ ExceptionCode ec = 0;
+ RefPtr<Range> paragraphRangeContainingCorrection = m_correctionPanelInfo.rangeToBeReplaced->cloneRange(ec);
+ if (ec)
+ return;
+
+ setStart(paragraphRangeContainingCorrection.get(), startOfParagraph(m_correctionPanelInfo.rangeToBeReplaced->startPosition()));
+ setEnd(paragraphRangeContainingCorrection.get(), endOfParagraph(m_correctionPanelInfo.rangeToBeReplaced->endPosition()));
+
+ // After we replace the word at range rangeToBeReplaced, we need to add markers to that range.
+ // However, once the replacement took place, the value of rangeToBeReplaced is not valid anymore.
+ // So before we carry out the replacement, we need to store the start position of rangeToBeReplaced
+ // relative to the start position of the containing paragraph. We use correctionStartOffsetInParagraph
+ // to store this value. In order to obtain this offset, we need to first create a range
+ // which spans from the start of paragraph to the start position of rangeToBeReplaced.
+ RefPtr<Range> correctionStartOffsetInParagraphAsRange = Range::create(paragraphRangeContainingCorrection->startContainer(ec)->document(), paragraphRangeContainingCorrection->startPosition(), paragraphRangeContainingCorrection->startPosition());
+ if (ec)
+ return;
+
+ Position startPositionOfRangeToBeReplaced = m_correctionPanelInfo.rangeToBeReplaced->startPosition();
+ correctionStartOffsetInParagraphAsRange->setEnd(startPositionOfRangeToBeReplaced.containerNode(), startPositionOfRangeToBeReplaced.computeOffsetInContainerNode(), ec);
+ if (ec)
+ return;
+
+ // Take note of the location of autocorrection so that we can add marker after the replacement took place.
+ int correctionStartOffsetInParagraph = TextIterator::rangeLength(correctionStartOffsetInParagraphAsRange.get());
+
+ // Clone the range, since the caller of this method may want to keep the original range around.
+ RefPtr<Range> rangeToBeReplaced = m_correctionPanelInfo.rangeToBeReplaced->cloneRange(ec);
+ applyCommand(SpellingCorrectionCommand::create(rangeToBeReplaced, m_correctionPanelInfo.replacementString));
+ setEnd(paragraphRangeContainingCorrection.get(), m_frame->selection()->selection().start());
+ RefPtr<Range> replacementRange = TextIterator::subrange(paragraphRangeContainingCorrection.get(), correctionStartOffsetInParagraph, m_correctionPanelInfo.replacementString.length());
+ String newText = plainText(replacementRange.get());
+
+ // Check to see if replacement succeeded.
+ if (newText != m_correctionPanelInfo.replacementString)
+ return;
+
+ DocumentMarkerController* markers = replacementRange->startContainer()->document()->markers();
+ size_t size = markerTypesToAdd.size();
+ for (size_t i = 0; i < size; ++i) {
+ DocumentMarker::MarkerType markerType = markerTypesToAdd[i];
+ String description;
+ if (m_correctionPanelInfo.panelType != CorrectionPanelInfo::PanelTypeReversion && (markerType == DocumentMarker::Replacement || markerType == DocumentMarker::Autocorrected))
+ description = m_correctionPanelInfo.replacedString;
+ markers->addMarker(replacementRange.get(), markerType, description);
+ }
+}
+
+bool SpellingCorrectionController::applyAutocorrectionBeforeTypingIfAppropriate()
+{
+ if (!m_correctionPanelInfo.rangeToBeReplaced || !m_correctionPanelInfo.isActive)
+ return false;
+
+ if (m_correctionPanelInfo.panelType != CorrectionPanelInfo::PanelTypeCorrection)
+ return false;
+
+ Position caretPosition = m_frame->selection()->selection().start();
+
+ if (m_correctionPanelInfo.rangeToBeReplaced->endPosition() == caretPosition) {
+ handleCorrectionPanelResult(dismissSoon(ReasonForDismissingCorrectionPanelAccepted));
+ return true;
+ }
+
+ // Pending correction should always be where caret is. But in case this is not always true, we still want to dismiss the panel without accepting the correction.
+ ASSERT(m_correctionPanelInfo.rangeToBeReplaced->endPosition() == caretPosition);
+ dismiss(ReasonForDismissingCorrectionPanelIgnored);
+ return false;
+}
+
+void SpellingCorrectionController::respondToUnappliedSpellCorrection(const VisibleSelection& selectionOfCorrected, const String& corrected, const String& correction)
+{
+ client()->recordAutocorrectionResponse(EditorClient::AutocorrectionReverted, corrected, correction);
+ m_frame->document()->updateLayout();
+ m_frame->selection()->setSelection(selectionOfCorrected, SelectionController::CloseTyping | SelectionController::ClearTypingStyle | SelectionController::SpellCorrectionTriggered);
+ RefPtr<Range> range = Range::create(m_frame->document(), m_frame->selection()->selection().start(), m_frame->selection()->selection().end());
+
+ DocumentMarkerController* markers = m_frame->document()->markers();
+ markers->removeMarkers(range.get(), DocumentMarker::Spelling | DocumentMarker::Autocorrected, DocumentMarkerController::RemovePartiallyOverlappingMarker);
+ markers->addMarker(range.get(), DocumentMarker::Replacement);
+ markers->addMarker(range.get(), DocumentMarker::SpellCheckingExemption);
+}
+
+void SpellingCorrectionController::correctionPanelTimerFired(Timer<SpellingCorrectionController>*)
+{
+ m_correctionPanelIsDismissedByEditor = false;
+ switch (m_correctionPanelInfo.panelType) {
+ case CorrectionPanelInfo::PanelTypeCorrection: {
+ VisibleSelection selection(m_frame->selection()->selection());
+ VisiblePosition start(selection.start(), selection.affinity());
+ VisiblePosition p = startOfWord(start, LeftWordIfOnBoundary);
+ VisibleSelection adjacentWords = VisibleSelection(p, start);
+ m_frame->editor()->markAllMisspellingsAndBadGrammarInRanges(Editor::MarkSpelling | Editor::ShowCorrectionPanel, adjacentWords.toNormalizedRange().get(), 0);
+ }
+ break;
+ case CorrectionPanelInfo::PanelTypeReversion: {
+ m_correctionPanelInfo.isActive = true;
+ m_correctionPanelInfo.replacedString = plainText(m_correctionPanelInfo.rangeToBeReplaced.get());
+ FloatRect boundingBox = windowRectForRange(m_correctionPanelInfo.rangeToBeReplaced.get());
+ if (!boundingBox.isEmpty())
+ client()->showCorrectionPanel(m_correctionPanelInfo.panelType, boundingBox, m_correctionPanelInfo.replacedString, m_correctionPanelInfo.replacementString, Vector<String>());
+ }
+ break;
+ case CorrectionPanelInfo::PanelTypeSpellingSuggestions: {
+ if (plainText(m_correctionPanelInfo.rangeToBeReplaced.get()) != m_correctionPanelInfo.replacedString)
+ break;
+ String paragraphText = plainText(TextCheckingParagraph(m_correctionPanelInfo.rangeToBeReplaced).paragraphRange().get());
+ Vector<String> suggestions;
+ textChecker()->getGuessesForWord(m_correctionPanelInfo.replacedString, paragraphText, suggestions);
+ if (suggestions.isEmpty()) {
+ m_correctionPanelInfo.rangeToBeReplaced.clear();
+ break;
+ }
+ String topSuggestion = suggestions.first();
+ suggestions.remove(0);
+ m_correctionPanelInfo.isActive = true;
+ FloatRect boundingBox = windowRectForRange(m_correctionPanelInfo.rangeToBeReplaced.get());
+ if (!boundingBox.isEmpty())
+ client()->showCorrectionPanel(m_correctionPanelInfo.panelType, boundingBox, m_correctionPanelInfo.replacedString, topSuggestion, suggestions);
+ }
+ break;
+ }
+}
+
+void SpellingCorrectionController::handleCorrectionPanelResult(const String& correction)
+{
+ Range* replacedRange = m_correctionPanelInfo.rangeToBeReplaced.get();
+ if (!replacedRange || m_frame->document() != replacedRange->ownerDocument())
+ return;
+
+ String currentWord = plainText(m_correctionPanelInfo.rangeToBeReplaced.get());
+ // Check to see if the word we are about to correct has been changed between timer firing and callback being triggered.
+ if (currentWord != m_correctionPanelInfo.replacedString)
+ return;
+
+ m_correctionPanelInfo.isActive = false;
+
+ switch (m_correctionPanelInfo.panelType) {
+ case CorrectionPanelInfo::PanelTypeCorrection:
+ if (correction.length()) {
+ m_correctionPanelInfo.replacementString = correction;
+ applyCorrectionPanelInfo(markerTypesForAutocorrection());
+ } else if (!m_correctionPanelIsDismissedByEditor)
+ replacedRange->startContainer()->document()->markers()->addMarker(replacedRange, DocumentMarker::RejectedCorrection, m_correctionPanelInfo.replacedString);
+ break;
+ case CorrectionPanelInfo::PanelTypeReversion:
+ case CorrectionPanelInfo::PanelTypeSpellingSuggestions:
+ if (correction.length()) {
+ m_correctionPanelInfo.replacementString = correction;
+ applyCorrectionPanelInfo(markerTypesForReplacement());
+ }
+ break;
+ }
+
+ m_correctionPanelInfo.rangeToBeReplaced.clear();
+}
+
+bool SpellingCorrectionController::isAutomaticSpellingCorrectionEnabled()
+{
+ return client() && client()->isAutomaticSpellingCorrectionEnabled();
+}
+
+FloatRect SpellingCorrectionController::windowRectForRange(const Range* range) const
+{
+ FrameView* view = m_frame->view();
+ return view ? view->contentsToWindow(IntRect(range->boundingRect())) : FloatRect();
+}
+
+void SpellingCorrectionController::respondToChangedSelection(const VisibleSelection& oldSelection)
+{
+ VisibleSelection currentSelection(m_frame->selection()->selection());
+ // When user moves caret to the end of autocorrected word and pauses, we show the panel
+ // containing the original pre-correction word so that user can quickly revert the
+ // undesired autocorrection. Here, we start correction panel timer once we confirm that
+ // the new caret position is at the end of a word.
+ if (!currentSelection.isCaret() || currentSelection == oldSelection)
+ return;
+
+ VisiblePosition selectionPosition = currentSelection.start();
+ VisiblePosition endPositionOfWord = endOfWord(selectionPosition, LeftWordIfOnBoundary);
+ if (selectionPosition != endPositionOfWord)
+ return;
+
+ Position position = endPositionOfWord.deepEquivalent();
+ if (position.anchorType() != Position::PositionIsOffsetInAnchor)
+ return;
+
+ Node* node = position.containerNode();
+ int endOffset = position.offsetInContainerNode();
+ Vector<DocumentMarker> markers = node->document()->markers()->markersForNode(node);
+ size_t markerCount = markers.size();
+ for (size_t i = 0; i < markerCount; ++i) {
+ const DocumentMarker& marker = markers[i];
+ if (!shouldStartTimeFor(marker, endOffset))
+ continue;
+ RefPtr<Range> wordRange = Range::create(m_frame->document(), node, marker.startOffset, node, marker.endOffset);
+ String currentWord = plainText(wordRange.get());
+ if (!currentWord.length())
+ continue;
+
+ m_correctionPanelInfo.rangeToBeReplaced = wordRange;
+ m_correctionPanelInfo.replacedString = currentWord;
+ if (marker.type == DocumentMarker::Spelling)
+ startCorrectionPanelTimer(CorrectionPanelInfo::PanelTypeSpellingSuggestions);
+ else {
+ m_correctionPanelInfo.replacementString = marker.description;
+ startCorrectionPanelTimer(CorrectionPanelInfo::PanelTypeReversion);
+ }
+
+ break;
+ }
+}
+
+void SpellingCorrectionController::respondToAppliedEditing(PassRefPtr<EditCommand> command)
+{
+ if (command->isTopLevelCommand() && !command->shouldRetainAutocorrectionIndicator())
+ m_frame->document()->markers()->removeMarkers(DocumentMarker::CorrectionIndicator);
+}
+
+EditorClient* SpellingCorrectionController::client()
+{
+ return m_frame->page() ? m_frame->page()->editorClient() : 0;
+}
+
+TextCheckerClient* SpellingCorrectionController::textChecker()
+{
+ if (EditorClient* owner = client())
+ return owner->textChecker();
+ return 0;
+}
+
+void SpellingCorrectionController::recordAutocorrectionResponseReversed(const String& replacedString, const String& replacementString)
+{
+ client()->recordAutocorrectionResponse(EditorClient::AutocorrectionReverted, replacedString, replacementString);
+}
+
+void SpellingCorrectionController::recordAutocorrectionResponseReversed(const String& replacedString, PassRefPtr<Range> replacementRange)
+{
+ recordAutocorrectionResponseReversed(replacedString, plainText(replacementRange.get()));
+}
+
+void SpellingCorrectionController::markReversed(PassRefPtr<Range> changedRange)
+{
+ changedRange->startContainer()->document()->markers()->removeMarkers(changedRange.get(), DocumentMarker::Autocorrected, DocumentMarkerController::RemovePartiallyOverlappingMarker);
+ changedRange->startContainer()->document()->markers()->addMarker(changedRange.get(), DocumentMarker::SpellCheckingExemption);
+}
+
+void SpellingCorrectionController::markCorrection(PassRefPtr<Range> replacedRange, const String& replacedString)
+{
+ Vector<DocumentMarker::MarkerType> markerTypesToAdd = markerTypesForAutocorrection();
+ DocumentMarkerController* markers = replacedRange->startContainer()->document()->markers();
+ for (size_t i = 0; i < markerTypesToAdd.size(); ++i) {
+ DocumentMarker::MarkerType markerType = markerTypesToAdd[i];
+ if (markerType == DocumentMarker::Replacement || markerType == DocumentMarker::Autocorrected)
+ markers->addMarker(replacedRange.get(), markerType, replacedString);
+ else
+ markers->addMarker(replacedRange.get(), markerType);
+ }
+}
+
+void SpellingCorrectionController::recordSpellcheckerResponseForModifiedCorrection(Range* rangeOfCorrection, const String& corrected, const String& correction)
+{
+ if (!rangeOfCorrection)
+ return;
+ DocumentMarkerController* markers = rangeOfCorrection->startContainer()->document()->markers();
+ Vector<DocumentMarker> correctedOnceMarkers = markers->markersInRange(rangeOfCorrection, DocumentMarker::Autocorrected);
+ if (correctedOnceMarkers.isEmpty())
+ return;
+
+ // Spelling corrected text has been edited. We need to determine whether user has reverted it to original text or
+ // edited it to something else, and notify spellchecker accordingly.
+ if (markersHaveIdenticalDescription(correctedOnceMarkers) && correctedOnceMarkers[0].description == corrected)
+ client()->recordAutocorrectionResponse(EditorClient::AutocorrectionReverted, corrected, correction);
+ else
+ client()->recordAutocorrectionResponse(EditorClient::AutocorrectionEdited, corrected, correction);
+ markers->removeMarkers(rangeOfCorrection, DocumentMarker::Autocorrected, DocumentMarkerController::RemovePartiallyOverlappingMarker);
+}
+
+#endif
+
+} // namespace WebCore
diff --git a/Source/WebCore/editing/SpellingCorrectionController.h b/Source/WebCore/editing/SpellingCorrectionController.h
new file mode 100644
index 0000000..cf2ca5e
--- /dev/null
+++ b/Source/WebCore/editing/SpellingCorrectionController.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SpellingCorrectionController_h
+#define SpellingCorrectionController_h
+
+#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
+// Some platforms provide UI for suggesting autocorrection.
+#define SUPPORT_AUTOCORRECTION_PANEL 1
+// Some platforms use spelling and autocorrection markers to provide visual cue.
+// On such platform, if word with marker is edited, we need to remove the marker.
+#define REMOVE_MARKERS_UPON_EDITING 1
+#else
+#define SUPPORT_AUTOCORRECTION_PANEL 0
+#define REMOVE_MARKERS_UPON_EDITING 0
+#endif // #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
+
+#include "DocumentMarker.h"
+#include "EditCommand.h"
+#include "FloatRect.h"
+#include "Range.h"
+#include "Timer.h"
+#include <wtf/Noncopyable.h>
+#include <wtf/UnusedParam.h>
+
+namespace WebCore {
+
+class EditorClient;
+class Range;
+class TextCheckerClient;
+class VisibleSelection;
+
+struct CorrectionPanelInfo {
+ enum PanelType {
+ PanelTypeCorrection = 0,
+ PanelTypeReversion,
+ PanelTypeSpellingSuggestions
+ };
+
+ RefPtr<Range> rangeToBeReplaced;
+ String replacedString;
+ String replacementString;
+ PanelType panelType;
+ bool isActive;
+};
+
+enum ReasonForDismissingCorrectionPanel {
+ ReasonForDismissingCorrectionPanelCancelled = 0,
+ ReasonForDismissingCorrectionPanelIgnored,
+ ReasonForDismissingCorrectionPanelAccepted
+};
+
+#if SUPPORT_AUTOCORRECTION_PANEL
+#define UNLESS_ENABLED(functionBody) ;
+#else
+#define UNLESS_ENABLED(functionBody) { functionBody }
+#endif
+
+class SpellingCorrectionController {
+ WTF_MAKE_NONCOPYABLE(SpellingCorrectionController); WTF_MAKE_FAST_ALLOCATED;
+public:
+ SpellingCorrectionController(Frame*) UNLESS_ENABLED()
+ ~SpellingCorrectionController() UNLESS_ENABLED()
+
+ void startCorrectionPanelTimer(CorrectionPanelInfo::PanelType) UNLESS_ENABLED()
+ void stopCorrectionPanelTimer() UNLESS_ENABLED()
+
+ void dismiss(ReasonForDismissingCorrectionPanel) UNLESS_ENABLED()
+ String dismissSoon(ReasonForDismissingCorrectionPanel) UNLESS_ENABLED(return String();)
+ void show(PassRefPtr<Range> rangeToReplace, const String& replacement) UNLESS_ENABLED(UNUSED_PARAM(rangeToReplace); UNUSED_PARAM(replacement);)
+
+ void applyCorrectionPanelInfo(const Vector<DocumentMarker::MarkerType>&) UNLESS_ENABLED()
+ // Return true if correction was applied, false otherwise.
+ bool applyAutocorrectionBeforeTypingIfAppropriate() UNLESS_ENABLED(return false;)
+
+ void respondToUnappliedSpellCorrection(const VisibleSelection&, const String& corrected, const String& correction) UNLESS_ENABLED(UNUSED_PARAM(corrected); UNUSED_PARAM(correction);)
+ void respondToAppliedEditing(PassRefPtr<EditCommand>) UNLESS_ENABLED()
+ void respondToChangedSelection(const VisibleSelection& oldSelection) UNLESS_ENABLED(UNUSED_PARAM(oldSelection);)
+
+ void stopPendingCorrection(const VisibleSelection& oldSelection) UNLESS_ENABLED(UNUSED_PARAM(oldSelection);)
+ void applyPendingCorrection(const VisibleSelection& selectionAfterTyping) UNLESS_ENABLED(UNUSED_PARAM(selectionAfterTyping);)
+
+ void handleCorrectionPanelResult(const String& correction) UNLESS_ENABLED(UNUSED_PARAM(correction);)
+ void handleCancelOperation() UNLESS_ENABLED()
+
+ bool hasPendingCorrection() const UNLESS_ENABLED(return false;)
+ bool isSpellingMarkerAllowed(PassRefPtr<Range> misspellingRange) const UNLESS_ENABLED(UNUSED_PARAM(misspellingRange); return true;)
+ bool isAutomaticSpellingCorrectionEnabled() UNLESS_ENABLED(return false;)
+ bool shouldRemoveMarkersUponEditing() { return REMOVE_MARKERS_UPON_EDITING; }
+
+ void correctionPanelTimerFired(Timer<SpellingCorrectionController>*) UNLESS_ENABLED()
+ void recordAutocorrectionResponseReversed(const String& replacedString, PassRefPtr<Range> replacementRange) UNLESS_ENABLED(UNUSED_PARAM(replacedString); UNUSED_PARAM(replacementRange);)
+ void markReversed(PassRefPtr<Range> changedRange) UNLESS_ENABLED(UNUSED_PARAM(changedRange);)
+ void markCorrection(PassRefPtr<Range> replacedRange, const String& replacedString) UNLESS_ENABLED(UNUSED_PARAM(replacedRange); UNUSED_PARAM(replacedString);)
+ void recordSpellcheckerResponseForModifiedCorrection(Range* rangeOfCorrection, const String& corrected, const String& correction) UNLESS_ENABLED(UNUSED_PARAM(rangeOfCorrection); UNUSED_PARAM(corrected); UNUSED_PARAM(correction);)
+
+#if SUPPORT_AUTOCORRECTION_PANEL
+private:
+ void recordAutocorrectionResponseReversed(const String& replacedString, const String& replacementString);
+
+ bool shouldStartTimeFor(const DocumentMarker& marker, int endOffset) const
+ {
+ return (((marker.type == DocumentMarker::Replacement && !marker.description.isNull())
+ || marker.type == DocumentMarker::Spelling) && static_cast<int>(marker.endOffset) == endOffset);
+ }
+
+ EditorClient* client();
+ TextCheckerClient* textChecker();
+ FloatRect windowRectForRange(const Range*) const;
+
+ EditorClient* m_client;
+ Frame* m_frame;
+
+ Timer<SpellingCorrectionController> m_correctionPanelTimer;
+ CorrectionPanelInfo m_correctionPanelInfo;
+ bool m_correctionPanelIsDismissedByEditor;
+#endif
+};
+
+#undef UNLESS_ENABLED
+
+} // namespace WebCore
+
+#endif // SpellingCorrectionController_h
diff --git a/Source/WebCore/editing/TypingCommand.cpp b/Source/WebCore/editing/TypingCommand.cpp
index 1ff2524..3f95d0d 100644
--- a/Source/WebCore/editing/TypingCommand.cpp
+++ b/Source/WebCore/editing/TypingCommand.cpp
@@ -154,11 +154,9 @@
Frame* frame = document->frame();
ASSERT(frame);
-#if REMOVE_MARKERS_UPON_EDITING
if (!text.isEmpty())
- document->frame()->editor()->removeSpellAndCorrectionMarkersFromWordsToBeEdited(isSpaceOrNewline(text.characters()[0]));
-#endif
-
+ document->frame()->editor()->updateMarkersForWordsAffectedByEditing(isSpaceOrNewline(text.characters()[0]));
+
insertText(document, text, frame->selection()->selection(), options, composition);
}
@@ -344,12 +342,8 @@
VisiblePosition p2 = startOfWord(start, LeftWordIfOnBoundary);
if (p1 != p2)
document()->frame()->editor()->markMisspellingsAfterTypingToWord(p1, endingSelection());
-#if SUPPORT_AUTOCORRECTION_PANEL
else if (commandType == TypingCommand::InsertText)
- document()->frame()->editor()->startCorrectionPanelTimer(CorrectionPanelInfo::PanelTypeCorrection);
-#else
- UNUSED_PARAM(commandType);
-#endif
+ document()->frame()->editor()->startCorrectionPanelTimer();
}
}
@@ -468,9 +462,8 @@
void TypingCommand::deleteKeyPressed(TextGranularity granularity, bool killRing)
{
-#if REMOVE_MARKERS_UPON_EDITING
- document()->frame()->editor()->removeSpellAndCorrectionMarkersFromWordsToBeEdited(false);
-#endif
+ document()->frame()->editor()->updateMarkersForWordsAffectedByEditing(false);
+
VisibleSelection selectionToDelete;
VisibleSelection selectionAfterUndo;
@@ -567,9 +560,8 @@
void TypingCommand::forwardDeleteKeyPressed(TextGranularity granularity, bool killRing)
{
-#if REMOVE_MARKERS_UPON_EDITING
- document()->frame()->editor()->removeSpellAndCorrectionMarkersFromWordsToBeEdited(false);
-#endif
+ document()->frame()->editor()->updateMarkersForWordsAffectedByEditing(false);
+
VisibleSelection selectionToDelete;
VisibleSelection selectionAfterUndo;
diff --git a/Source/WebCore/editing/htmlediting.h b/Source/WebCore/editing/htmlediting.h
index b7c2bf7..a006da6 100644
--- a/Source/WebCore/editing/htmlediting.h
+++ b/Source/WebCore/editing/htmlediting.h
@@ -224,6 +224,15 @@
{
return c == noBreakSpace || c == ' ' || c == '\n' || c == '\t';
}
+
+inline bool isAmbiguousBoundaryCharacter(UChar character)
+{
+ // These are characters that can behave as word boundaries, but can appear within words.
+ // If they are just typed, i.e. if they are immediately followed by a caret, we want to delay text checking until the next character has been typed.
+ // FIXME: this is required until 6853027 is fixed and text checking can do this for us.
+ return character == '\'' || character == rightSingleQuotationMark || character == hebrewPunctuationGershayim;
+}
+
String stringWithRebalancedWhitespace(const String&, bool startIsStartOfParagraph, bool endIsEndOfParagraph);
const String& nonBreakingSpaceString();
diff --git a/Source/WebCore/page/EditorClient.h b/Source/WebCore/page/EditorClient.h
index aeda844..df24a5b 100644
--- a/Source/WebCore/page/EditorClient.h
+++ b/Source/WebCore/page/EditorClient.h
@@ -27,7 +27,7 @@
#ifndef EditorClient_h
#define EditorClient_h
-#include "CorrectionPanelInfo.h"
+#include "SpellingCorrectionController.h"
#include "EditorInsertAction.h"
#include "FloatRect.h"
#include "PlatformString.h"
diff --git a/Source/WebCore/page/Frame.cpp b/Source/WebCore/page/Frame.cpp
index 9d0b20b..b75efdf 100644
--- a/Source/WebCore/page/Frame.cpp
+++ b/Source/WebCore/page/Frame.cpp
@@ -968,7 +968,7 @@
if (!document)
return;
- m_editor.dismissCorrectionPanel(ReasonForDismissingCorrectionPanelIgnored);
+ m_editor.dismissCorrectionPanelAsIgnored();
#if ENABLE(SVG)
// Respect SVGs zoomAndPan="disabled" property in standalone SVG documents.