AX: new lines in content editable elements don't notify accessibility
https://bugs.webkit.org/show_bug.cgi?id=153361

Reviewed by Ryosuke Niwa.

Relocate accessibility edit notification logic into higher level logic.
Typing notifications relocated into TypingCommand.
Cut & Paste notifications relocated into Editor.
Undo relocated into EditCommandComposition.

Tests: accessibility/mac/value-change/value-change-user-info-contenteditable.html
       accessibility/mac/value-change/value-change-user-info-textarea.html
       accessibility/mac/value-change/value-change-user-info-textfield.html

* CMakeLists.txt:
* WebCore.xcodeproj/project.pbxproj:
* accessibility/AXObjectCache.cpp:
(WebCore::AccessibilityReplacedText::AccessibilityReplacedText):
(WebCore::AccessibilityReplacedText::postTextStateChangeNotification):
(WebCore::AXObjectCache::postTextStateChangeNotification):
(WebCore::AXObjectCache::postTextReplacementNotification):
* accessibility/AXObjectCache.h:
(WebCore::VisiblePositionIndexRange::isNull):
(WebCore::AccessibilityReplacedText::AccessibilityReplacedText):
(WebCore::AccessibilityReplacedText::replacedRange):
* accessibility/AccessibilityObject.cpp:
(WebCore::AccessibilityObject::listMarkerTextForNodeAndPosition):
(WebCore::AccessibilityObject::stringForVisiblePositionRange):
* accessibility/AccessibilityObject.h:
(WebCore::VisiblePositionRange::VisiblePositionRange):
* accessibility/mac/AXObjectCacheMac.mm:
(WebCore::AXObjectCache::postTextStateChangePlatformNotification):
(WebCore::AXObjectCache::postTextReplacementPlatformNotification):
* editing/AppendNodeCommand.cpp:
(WebCore::AppendNodeCommand::doApply): Deleted.
(WebCore::AppendNodeCommand::doUnapply): Deleted.
* editing/CompositeEditCommand.cpp:
(WebCore::AccessibilityUndoReplacedText::indexForVisiblePosition):
(WebCore::AccessibilityUndoReplacedText::confgureTextToBeDeletedByUnapplyIndexesWithEditCommandEndingSelection):
(WebCore::AccessibilityUndoReplacedText::confgureTextToBeDeletedByUnapplyStartIndexWithEditCommandStartingSelection):
(WebCore::AccessibilityUndoReplacedText::setTextInsertedByUnapplyRange):
(WebCore::AccessibilityUndoReplacedText::captureTextToBeDeletedByUnapply):
(WebCore::AccessibilityUndoReplacedText::captureTextToBeDeletedByReapply):
(WebCore::stringForVisiblePositionIndexRange):
(WebCore::AccessibilityUndoReplacedText::textInsertedByUnapply):
(WebCore::AccessibilityUndoReplacedText::textInsertedByReapply):
(WebCore::postTextStateChangeNotification):
(WebCore::AccessibilityUndoReplacedText::postTextStateChangeNotificationForUnapply):
(WebCore::AccessibilityUndoReplacedText::postTextStateChangeNotificationForReapply):
(WebCore::EditCommandComposition::EditCommandComposition):
(WebCore::EditCommandComposition::unapply):
(WebCore::EditCommandComposition::reapply):
(WebCore::EditCommandComposition::setStartingSelection):
(WebCore::EditCommandComposition::setEndingSelection):
(WebCore::EditCommandComposition::setTextInsertedByUnapplyRange):
(WebCore::CompositeEditCommand::removeNode):
(WebCore::CompositeEditCommand::replaceTextInNode):
(WebCore::deleteSelectionEditingActionForEditingAction):
(WebCore::CompositeEditCommand::deleteSelection):
(WebCore::CompositeEditCommand::applyStyle): Deleted.
(WebCore::CompositeEditCommand::updatePositionForNodeRemovalPreservingChildren): Deleted.
(WebCore::CompositeEditCommand::inputText): Deleted.
* editing/CompositeEditCommand.h:
(WebCore::AccessibilityUndoReplacedText::AccessibilityUndoReplacedText):
* editing/DeleteFromTextNodeCommand.cpp:
(WebCore::DeleteFromTextNodeCommand::doApply): Deleted.
(WebCore::DeleteFromTextNodeCommand::getNodesInCommand): Deleted.
* editing/DeleteFromTextNodeCommand.h:
* editing/DictationCommand.cpp:
(WebCore::DictationCommand::doApply):
* editing/EditCommand.cpp:
(WebCore::EditCommand::postTextStateChangeNotification):
(WebCore::SimpleEditCommand::SimpleEditCommand): Deleted.
(WebCore::SimpleEditCommand::doReapply): Deleted.
(WebCore::SimpleEditCommand::addNodeAndDescendants): Deleted.
* editing/EditCommand.h:
* editing/EditingAllInOne.cpp:
* editing/Editor.cpp:
(WebCore::Editor::replaceSelectionWithFragment):
(WebCore::Editor::appliedEditing):
(WebCore::Editor::unappliedEditing):
(WebCore::Editor::postTextStateChangeNotificationForCut):
(WebCore::Editor::performCutOrCopy):
(WebCore::Editor::changeSelectionAfterCommand):
(WebCore::dispatchEditableContentChangedEvents): Deleted.
(WebCore::Editor::addTextToKillRing): Deleted.
* editing/Editor.h:
* editing/InsertIntoTextNodeCommand.cpp:
(WebCore::InsertIntoTextNodeCommand::doApply): Deleted.
(WebCore::InsertIntoTextNodeCommand::getNodesInCommand): Deleted.
* editing/InsertNodeBeforeCommand.cpp:
(WebCore::InsertNodeBeforeCommand::doApply): Deleted.
(WebCore::InsertNodeBeforeCommand::doUnapply): Deleted.
(WebCore::InsertNodeBeforeCommand::getNodesInCommand): Deleted.
* editing/RemoveNodeCommand.cpp:
(WebCore::RemoveNodeCommand::RemoveNodeCommand):
* editing/RemoveNodeCommand.h:
(WebCore::RemoveNodeCommand::create):
* editing/ReplaceDeleteFromTextNodeCommand.cpp: Removed.
* editing/ReplaceDeleteFromTextNodeCommand.h: Removed.
* editing/ReplaceInsertIntoTextNodeCommand.cpp: Removed.
* editing/ReplaceInsertIntoTextNodeCommand.h: Removed.
* editing/ReplaceSelectionCommand.cpp:
(WebCore::ReplaceSelectionCommand::doApply):
(WebCore::ReplaceSelectionCommand::completeHTMLReplacement):
(WebCore::ReplaceSelectionCommand::performTrivialReplace):
* editing/ReplaceSelectionCommand.h:
(WebCore::ReplaceSelectionCommand::visibleSelectionForInsertedText):
* editing/TextInsertionBaseCommand.cpp:
(WebCore::TextInsertionBaseCommand::TextInsertionBaseCommand):
* editing/TextInsertionBaseCommand.h:
* editing/TypingCommand.cpp:
(WebCore::TypingCommand::TypingCommand):
(WebCore::TypingCommand::insertText):
(WebCore::TypingCommand::insertLineBreak):
(WebCore::TypingCommand::insertParagraphSeparatorInQuotedContent):
(WebCore::TypingCommand::insertParagraphSeparator):
(WebCore::TypingCommand::postTextStateChangeNotificationForDeletion):
(WebCore::TypingCommand::doApply):
(WebCore::TypingCommand::insertTextAndNotifyAccessibility):
(WebCore::TypingCommand::insertLineBreakAndNotifyAccessibility):
(WebCore::TypingCommand::insertParagraphSeparatorAndNotifyAccessibility):
(WebCore::TypingCommand::insertParagraphSeparatorInQuotedContentAndNotifyAccessibility):
(WebCore::TypingCommand::deleteKeyPressed):
(WebCore::TypingCommand::forwardDeleteKeyPressed):
* editing/TypingCommand.h:

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@199030 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/editing/TypingCommand.cpp b/Source/WebCore/editing/TypingCommand.cpp
index 15b4f85..2198907 100644
--- a/Source/WebCore/editing/TypingCommand.cpp
+++ b/Source/WebCore/editing/TypingCommand.cpp
@@ -26,6 +26,7 @@
 #include "config.h"
 #include "TypingCommand.h"
 
+#include "AXObjectCache.h"
 #include "BreakBlockquoteCommand.h"
 #include "DeleteSelectionCommand.h"
 #include "Document.h"
@@ -75,7 +76,7 @@
 };
 
 TypingCommand::TypingCommand(Document& document, ETypingCommand commandType, const String &textToInsert, Options options, TextGranularity granularity, TextCompositionType compositionType)
-    : TextInsertionBaseCommand(document)
+    : TextInsertionBaseCommand(document, EditActionTyping)
     , m_commandType(commandType)
     , m_textToInsert(textToInsert)
     , m_openForMoreTyping(true)
@@ -182,7 +183,7 @@
         lastTypingCommand->setCompositionType(compositionType);
         lastTypingCommand->setShouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator);
         lastTypingCommand->setShouldPreventSpellChecking(options & PreventSpellChecking);
-        lastTypingCommand->insertText(newText, options & SelectInsertedText);
+        lastTypingCommand->insertTextAndNotifyAccessibility(newText, options & SelectInsertedText);
         return;
     }
 
@@ -194,7 +195,7 @@
 {
     if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*document.frame())) {
         lastTypingCommand->setShouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator);
-        lastTypingCommand->insertLineBreak();
+        lastTypingCommand->insertLineBreakAndNotifyAccessibility();
         return;
     }
 
@@ -204,7 +205,7 @@
 void TypingCommand::insertParagraphSeparatorInQuotedContent(Document& document)
 {
     if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*document.frame())) {
-        lastTypingCommand->insertParagraphSeparatorInQuotedContent();
+        lastTypingCommand->insertParagraphSeparatorInQuotedContentAndNotifyAccessibility();
         return;
     }
 
@@ -215,7 +216,7 @@
 {
     if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*document.frame())) {
         lastTypingCommand->setShouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator);
-        lastTypingCommand->insertParagraphSeparator();
+        lastTypingCommand->insertParagraphSeparatorAndNotifyAccessibility();
         return;
     }
 
@@ -247,6 +248,17 @@
 }
 #endif
 
+void TypingCommand::postTextStateChangeNotificationForDeletion(const VisibleSelection& selection)
+{
+    if (!AXObjectCache::accessibilityEnabled())
+        return;
+    postTextStateChangeNotification(AXTextEditTypeDelete, AccessibilityObject::stringForVisiblePositionRange(selection), selection.start());
+    VisiblePositionIndexRange range;
+    range.startIndex.value = indexForVisiblePosition(selection.start(), range.startIndex.scope);
+    range.endIndex.value = indexForVisiblePosition(selection.end(), range.endIndex.scope);
+    composition()->setTextInsertedByUnapplyRange(range);
+}
+
 void TypingCommand::doApply()
 {
     if (!endingSelection().isNonOrphanedCaretOrRange())
@@ -267,27 +279,22 @@
         forwardDeleteKeyPressed(m_granularity, m_shouldAddToKillRing);
         return;
     case InsertLineBreak:
-        insertLineBreak();
+        insertLineBreakAndNotifyAccessibility();
         return;
     case InsertParagraphSeparator:
-        insertParagraphSeparator();
+        insertParagraphSeparatorAndNotifyAccessibility();
         return;
     case InsertParagraphSeparatorInQuotedContent:
-        insertParagraphSeparatorInQuotedContent();
+        insertParagraphSeparatorInQuotedContentAndNotifyAccessibility();
         return;
     case InsertText:
-        insertText(m_textToInsert, m_selectInsertedText);
+        insertTextAndNotifyAccessibility(m_textToInsert, m_selectInsertedText);
         return;
     }
 
     ASSERT_NOT_REACHED();
 }
 
-EditAction TypingCommand::editingAction() const
-{
-    return EditActionTyping;
-}
-
 void TypingCommand::markMisspellingsAfterTyping(ETypingCommand commandType)
 {
     Frame& frame = this->frame();
@@ -371,6 +378,14 @@
     forEachLineInString(text, operation);
 }
 
+void TypingCommand::insertTextAndNotifyAccessibility(const String &text, bool selectInsertedText)
+{
+    AccessibilityReplacedText replacedText(frame().selection().selection());
+    insertText(text, selectInsertedText);
+    replacedText.postTextStateChangeNotification(document().existingAXObjectCache(), AXTextEditTypeTyping, text, frame().selection().selection());
+    composition()->setTextInsertedByUnapplyRange(replacedText.replacedRange());
+}
+
 void TypingCommand::insertTextRunWithoutNewlines(const String &text, bool selectInsertedText)
 {
     RefPtr<InsertTextCommand> command = InsertTextCommand::create(document(), text, selectInsertedText,
@@ -390,6 +405,14 @@
     typingAddedToOpenCommand(InsertLineBreak);
 }
 
+void TypingCommand::insertLineBreakAndNotifyAccessibility()
+{
+    AccessibilityReplacedText replacedText(frame().selection().selection());
+    insertLineBreak();
+    replacedText.postTextStateChangeNotification(document().existingAXObjectCache(), AXTextEditTypeTyping, "\n", frame().selection().selection());
+    composition()->setTextInsertedByUnapplyRange(replacedText.replacedRange());
+}
+
 void TypingCommand::insertParagraphSeparator()
 {
     if (!canAppendNewLineFeedToSelection(endingSelection()))
@@ -399,6 +422,14 @@
     typingAddedToOpenCommand(InsertParagraphSeparator);
 }
 
+void TypingCommand::insertParagraphSeparatorAndNotifyAccessibility()
+{
+    AccessibilityReplacedText replacedText(frame().selection().selection());
+    insertParagraphSeparator();
+    replacedText.postTextStateChangeNotification(document().existingAXObjectCache(), AXTextEditTypeTyping, "\n", frame().selection().selection());
+    composition()->setTextInsertedByUnapplyRange(replacedText.replacedRange());
+}
+
 void TypingCommand::insertParagraphSeparatorInQuotedContent()
 {
     // If the selection starts inside a table, just insert the paragraph separator normally
@@ -412,6 +443,14 @@
     typingAddedToOpenCommand(InsertParagraphSeparatorInQuotedContent);
 }
 
+void TypingCommand::insertParagraphSeparatorInQuotedContentAndNotifyAccessibility()
+{
+    AccessibilityReplacedText replacedText(frame().selection().selection());
+    insertParagraphSeparatorInQuotedContent();
+    replacedText.postTextStateChangeNotification(document().existingAXObjectCache(), AXTextEditTypeTyping, "\n", frame().selection().selection());
+    composition()->setTextInsertedByUnapplyRange(replacedText.replacedRange());
+}
+
 bool TypingCommand::makeEditableRootEmpty()
 {
     Element* root = endingSelection().rootEditableElement();
@@ -532,6 +571,10 @@
     
     if (shouldAddToKillRing)
         frame.editor().addRangeToKillRing(*selectionToDelete.toNormalizedRange().get(), Editor::KillRingInsertionMode::PrependText);
+
+    // Post the accessibility notification before actually deleting the content while selectionToDelete is still valid
+    postTextStateChangeNotificationForDeletion(selectionToDelete);
+
     // Make undo select everything that has been deleted, unless an undo will undo more than just this deletion.
     // FIXME: This behaves like TextEdit except for the case where you open with text insertion and then delete
     // more text than you insert.  In that case all of the text that was around originally should be selected.
@@ -628,6 +671,9 @@
     if (selectionToDelete.isCaret() || !frame.selection().shouldDeleteSelection(selectionToDelete))
         return;
         
+    // Post the accessibility notification before actually deleting the content while selectionToDelete is still valid
+    postTextStateChangeNotificationForDeletion(selectionToDelete);
+
     if (shouldAddToKillRing)
         frame.editor().addRangeToKillRing(*selectionToDelete.toNormalizedRange().get(), Editor::KillRingInsertionMode::AppendText);
     // make undo select what was deleted