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.