2011-05-11 Jia Pu <jpu@apple.com>
Reviewed by Darin Adler.
Autocorrection persists after deleting and retyping the same word at same location.
https://bugs.webkit.org/show_bug.cgi?id=60555
<rdar://problem/9373915>
See WebCore/ChangeLog for detail.
* platform/mac-leopard/Skipped:
* platform/mac-snowleopard/Skipped:
* platform/mac/editing/spelling/delete-autocorrected-word-2-expected.png: Added.
* platform/mac/editing/spelling/delete-autocorrected-word-2-expected.txt: Added.
* platform/mac/editing/spelling/delete-autocorrected-word-2.html: Added.
2011-05-11 Jia Pu <jpu@apple.com>
Reviewed by Darin Adler.
Autocorrection persists after deleting and retyping the same word at same location.
https://bugs.webkit.org/show_bug.cgi?id=60555
<rdar://problem/9373915>
This patch intends to alleviate the issue of repetitively applying the same autocorrection
when user delete and retype the same word at the same location. This scenario is especially
common when autocorrection modifies the first letter of the word.
This patch consists following major changes:
1. Added a new marker type, DeletedAutocorrection. This marker is added to the whitespace that
precedes a deleted autocorrection. If the user later types the same original word at after
this whitespace, the autocorrection will not be applied again.
2. In DeleteSelectionCommand, added code to notify SpellingCorrectionController about the
autocorrection that has just been deleted.
3. In Editor and SpellingCorrectionController, added code to apply the marker and to suppress
autocorrection when necessary.
4. The change in CompositeEditCommand::replaceTextInNode is necessary for preserving markers.
Otherwise, we will loose the DeletedAutocorrection on the whitespace, when inserting text
after the whitespace.
Test: platform/mac/editing/spelling/delete-autocorrected-word-2.html
* dom/DocumentMarker.h: Added new marker type DeletedAutocorrection.
(WebCore::DocumentMarker::AllMarkers::AllMarkers):
* dom/DocumentMarkerController.cpp:
(WebCore::DocumentMarkerController::markersInRange): Support querying multiple marker types.
* dom/DocumentMarkerController.h:
* editing/CompositeEditCommand.cpp:
(WebCore::CompositeEditCommand::replaceTextInNodeAndPreserveMarkers):
(WebCore::CompositeEditCommand::rebalanceWhitespaceOnTextSubstring):
(WebCore::CompositeEditCommand::prepareWhitespaceAtPositionForSplit):
* editing/CompositeEditCommand.h:
* editing/DeleteSelectionCommand.cpp:
(WebCore::DeleteSelectionCommand::DeleteSelectionCommand):
(WebCore::DeleteSelectionCommand::fixupWhitespace):
(WebCore::DeleteSelectionCommand::originalStringForAutocorrectionAtBeginningOfSelection): Extracting
the original string if we are deleting an autocorrection.
(WebCore::DeleteSelectionCommand::doApply): Notify editor about the deleted autocorrection and its position.
* editing/DeleteSelectionCommand.h:
* editing/Editor.cpp:
(WebCore::Editor::markAllMisspellingsAndBadGrammarInRanges): Moved all logic of determining
when to suppress an autocorrection into SpellingCorrectionController.
(WebCore::Editor::deletedAutocorrectionAtPosition):
* editing/Editor.h:
* editing/InsertParagraphSeparatorCommand.cpp:
(WebCore::InsertParagraphSeparatorCommand::doApply):
* editing/SpellingCorrectionController.cpp:
(WebCore::SpellingCorrectionController::respondToAppliedEditing):
(WebCore::SpellingCorrectionController::deletedAutocorrectionAtPosition):
(WebCore::SpellingCorrectionController::markPrecedingWhitespaceForDeletedAutocorrectionAfterCommand):
(WebCore::SpellingCorrectionController::processMarkersOnTextToBeReplacedByResult):
* editing/SpellingCorrectionController.h:
(WebCore::SpellingCorrectionController::UNLESS_ENABLED):
* editing/visible_units.cpp:
(WebCore::isStartOfWord):
* editing/visible_units.h:
* manual-tests/autocorrection/spell-checking-after-reversion.html:
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@86295 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/editing/DeleteSelectionCommand.cpp b/Source/WebCore/editing/DeleteSelectionCommand.cpp
index 529c71d..3649d56 100644
--- a/Source/WebCore/editing/DeleteSelectionCommand.cpp
+++ b/Source/WebCore/editing/DeleteSelectionCommand.cpp
@@ -28,6 +28,7 @@
#include "Document.h"
#include "DocumentFragment.h"
+#include "DocumentMarkerController.h"
#include "EditingBoundary.h"
#include "Editor.h"
#include "EditorClient.h"
@@ -68,37 +69,37 @@
}
DeleteSelectionCommand::DeleteSelectionCommand(Document *document, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements)
- : CompositeEditCommand(document),
- m_hasSelectionToDelete(false),
- m_smartDelete(smartDelete),
- m_mergeBlocksAfterDelete(mergeBlocksAfterDelete),
- m_needPlaceholder(false),
- m_replace(replace),
- m_expandForSpecialElements(expandForSpecialElements),
- m_pruneStartBlockIfNecessary(false),
- m_startsAtEmptyLine(false),
- m_startBlock(0),
- m_endBlock(0),
- m_typingStyle(0),
- m_deleteIntoBlockquoteStyle(0)
+ : CompositeEditCommand(document)
+ , m_hasSelectionToDelete(false)
+ , m_smartDelete(smartDelete)
+ , m_mergeBlocksAfterDelete(mergeBlocksAfterDelete)
+ , m_needPlaceholder(false)
+ , m_replace(replace)
+ , m_expandForSpecialElements(expandForSpecialElements)
+ , m_pruneStartBlockIfNecessary(false)
+ , m_startsAtEmptyLine(false)
+ , m_startBlock(0)
+ , m_endBlock(0)
+ , m_typingStyle(0)
+ , m_deleteIntoBlockquoteStyle(0)
{
}
DeleteSelectionCommand::DeleteSelectionCommand(const VisibleSelection& selection, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements)
- : CompositeEditCommand(selection.start().anchorNode()->document()),
- m_hasSelectionToDelete(true),
- m_smartDelete(smartDelete),
- m_mergeBlocksAfterDelete(mergeBlocksAfterDelete),
- m_needPlaceholder(false),
- m_replace(replace),
- m_expandForSpecialElements(expandForSpecialElements),
- m_pruneStartBlockIfNecessary(false),
- m_startsAtEmptyLine(false),
- m_selectionToDelete(selection),
- m_startBlock(0),
- m_endBlock(0),
- m_typingStyle(0),
- m_deleteIntoBlockquoteStyle(0)
+ : CompositeEditCommand(selection.start().anchorNode()->document())
+ , m_hasSelectionToDelete(true)
+ , m_smartDelete(smartDelete)
+ , m_mergeBlocksAfterDelete(mergeBlocksAfterDelete)
+ , m_needPlaceholder(false)
+ , m_replace(replace)
+ , m_expandForSpecialElements(expandForSpecialElements)
+ , m_pruneStartBlockIfNecessary(false)
+ , m_startsAtEmptyLine(false)
+ , m_selectionToDelete(selection)
+ , m_startBlock(0)
+ , m_endBlock(0)
+ , m_typingStyle(0)
+ , m_deleteIntoBlockquoteStyle(0)
{
}
@@ -555,12 +556,12 @@
if (m_leadingWhitespace.isNotNull() && !m_leadingWhitespace.isRenderedCharacter() && m_leadingWhitespace.deprecatedNode()->isTextNode()) {
Text* textNode = static_cast<Text*>(m_leadingWhitespace.deprecatedNode());
ASSERT(!textNode->renderer() || textNode->renderer()->style()->collapseWhiteSpace());
- replaceTextInNode(textNode, m_leadingWhitespace.deprecatedEditingOffset(), 1, nonBreakingSpaceString());
+ replaceTextInNodePreservingMarkers(textNode, m_leadingWhitespace.deprecatedEditingOffset(), 1, nonBreakingSpaceString());
}
if (m_trailingWhitespace.isNotNull() && !m_trailingWhitespace.isRenderedCharacter() && m_trailingWhitespace.deprecatedNode()->isTextNode()) {
Text* textNode = static_cast<Text*>(m_trailingWhitespace.deprecatedNode());
ASSERT(!textNode->renderer() ||textNode->renderer()->style()->collapseWhiteSpace());
- replaceTextInNode(textNode, m_trailingWhitespace.deprecatedEditingOffset(), 1, nonBreakingSpaceString());
+ replaceTextInNodePreservingMarkers(textNode, m_trailingWhitespace.deprecatedEditingOffset(), 1, nonBreakingSpaceString());
}
}
@@ -743,6 +744,26 @@
m_leadingWhitespace.clear();
m_trailingWhitespace.clear();
}
+
+String DeleteSelectionCommand::originalStringForAutocorrectionAtBeginningOfSelection()
+{
+ if (!m_selectionToDelete.isRange())
+ return String();
+
+ VisiblePosition startOfSelection = m_selectionToDelete.start();
+ if (!isStartOfWord(startOfSelection))
+ return String();
+
+ RefPtr<Range> rangeOfFirstCharacter = Range::create(document(), startOfSelection.deepEquivalent(), startOfSelection.next().deepEquivalent());
+ Vector<DocumentMarker> markers = document()->markers()->markersInRange(rangeOfFirstCharacter.get(), DocumentMarker::Autocorrected);
+ for (size_t i = 0; i < markers.size(); ++i) {
+ const DocumentMarker& marker = markers[i];
+ int startOffset = marker.startOffset;
+ if (startOffset == startOfSelection.deepEquivalent().offsetInContainerNode())
+ return marker.description;
+ }
+ return String();
+}
void DeleteSelectionCommand::doApply()
{
@@ -754,6 +775,8 @@
if (!m_selectionToDelete.isNonOrphanedRange())
return;
+ String originalString = originalStringForAutocorrectionAtBeginningOfSelection();
+
// If the deletion is occurring in a text field, and we're not deleting to replace the selection, then let the frame call across the bridge to notify the form delegate.
if (!m_replace) {
Node* startNode = m_selectionToDelete.start().deprecatedNode();
@@ -815,7 +838,12 @@
rebalanceWhitespaceAt(m_endingPosition);
calculateTypingStyleAfterDelete();
-
+
+ if (!originalString.isEmpty()) {
+ if (Frame* frame = document()->frame())
+ frame->editor()->deletedAutocorrectionAtPosition(m_endingPosition, originalString);
+ }
+
setEndingSelection(VisibleSelection(m_endingPosition, affinity));
clearTransientState();
}