AX: new lines in content editable elements don't notify accessibility
authord_russell@apple.com <d_russell@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 4 Apr 2016 23:17:59 +0000 (23:17 +0000)
committerd_russell@apple.com <d_russell@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 4 Apr 2016 23:17:59 +0000 (23:17 +0000)
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: https://svn.webkit.org/repository/webkit/trunk@199030 268f45cc-cd09-0410-ab3c-d52691b4dbfc

44 files changed:
LayoutTests/ChangeLog
LayoutTests/accessibility/mac/selection-value-changes-for-aria-textbox-expected.txt
LayoutTests/accessibility/mac/value-change-userinfo-expected.txt [deleted file]
LayoutTests/accessibility/mac/value-change-userinfo.html [deleted file]
LayoutTests/accessibility/mac/value-change/value-change-helpers.js [new file with mode: 0644]
LayoutTests/accessibility/mac/value-change/value-change-user-info-contenteditable-expected.txt [new file with mode: 0644]
LayoutTests/accessibility/mac/value-change/value-change-user-info-contenteditable.html [new file with mode: 0644]
LayoutTests/accessibility/mac/value-change/value-change-user-info-textarea-expected.txt [new file with mode: 0644]
LayoutTests/accessibility/mac/value-change/value-change-user-info-textarea.html [new file with mode: 0644]
LayoutTests/accessibility/mac/value-change/value-change-user-info-textfield-expected.txt [new file with mode: 0644]
LayoutTests/accessibility/mac/value-change/value-change-user-info-textfield.html [new file with mode: 0644]
Source/WebCore/CMakeLists.txt
Source/WebCore/ChangeLog
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/accessibility/AXObjectCache.cpp
Source/WebCore/accessibility/AXObjectCache.h
Source/WebCore/accessibility/AccessibilityObject.cpp
Source/WebCore/accessibility/AccessibilityObject.h
Source/WebCore/accessibility/mac/AXObjectCacheMac.mm
Source/WebCore/editing/AppendNodeCommand.cpp
Source/WebCore/editing/CompositeEditCommand.cpp
Source/WebCore/editing/CompositeEditCommand.h
Source/WebCore/editing/DeleteFromTextNodeCommand.cpp
Source/WebCore/editing/DeleteFromTextNodeCommand.h
Source/WebCore/editing/DictationCommand.cpp
Source/WebCore/editing/EditCommand.cpp
Source/WebCore/editing/EditCommand.h
Source/WebCore/editing/EditingAllInOne.cpp
Source/WebCore/editing/Editor.cpp
Source/WebCore/editing/Editor.h
Source/WebCore/editing/InsertIntoTextNodeCommand.cpp
Source/WebCore/editing/InsertNodeBeforeCommand.cpp
Source/WebCore/editing/RemoveNodeCommand.cpp
Source/WebCore/editing/RemoveNodeCommand.h
Source/WebCore/editing/ReplaceDeleteFromTextNodeCommand.cpp [deleted file]
Source/WebCore/editing/ReplaceDeleteFromTextNodeCommand.h [deleted file]
Source/WebCore/editing/ReplaceInsertIntoTextNodeCommand.cpp [deleted file]
Source/WebCore/editing/ReplaceInsertIntoTextNodeCommand.h [deleted file]
Source/WebCore/editing/ReplaceSelectionCommand.cpp
Source/WebCore/editing/ReplaceSelectionCommand.h
Source/WebCore/editing/TextInsertionBaseCommand.cpp
Source/WebCore/editing/TextInsertionBaseCommand.h
Source/WebCore/editing/TypingCommand.cpp
Source/WebCore/editing/TypingCommand.h

index 8613519..6a5d3d6 100644 (file)
@@ -1,3 +1,37 @@
+2016-04-04  Doug Russell  <d_russell@apple.com>
+
+        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.
+
+        * accessibility/mac/selection-value-changes-for-aria-textbox-expected.txt:
+        * accessibility/mac/value-change-userinfo-expected.txt: Removed.
+        * accessibility/mac/value-change-userinfo.html: Removed.
+        * accessibility/mac/value-change/value-change-helpers.js: Added.
+        (stringForEditType):
+        (bump):
+        (shouldBeInsert):
+        (shouldBeTyping):
+        (shouldBeDelete):
+        (shouldBeCut):
+        (shouldBePaste):
+        (shouldBeReplace):
+        (shouldBePasteReplace):
+        (shouldBeTypingReplace):
+        (shouldBeInsertReplace):
+        * accessibility/mac/value-change/value-change-user-info-contenteditable-expected.txt: Added.
+        * accessibility/mac/value-change/value-change-user-info-contenteditable.html: Added.
+        * accessibility/mac/value-change/value-change-user-info-textarea-expected.txt: Added.
+        * accessibility/mac/value-change/value-change-user-info-textarea.html: Added.
+        * accessibility/mac/value-change/value-change-user-info-textfield-expected.txt: Added.
+        * accessibility/mac/value-change/value-change-user-info-textfield.html: Added.
+
 2016-04-04  Dean Jackson  <dino@apple.com>
 
         Add color-gamut media query support
index 1c32b7c..9be2480 100644 (file)
@@ -11,5 +11,6 @@ TEST COMPLETE
 Successfully received AXSelectedTextChanged
 Successfully received AXSelectedTextChanged
 Successfully received AXSelectedTextChanged
+Successfully received AXSelectedTextChanged
 Successfully received AXValueChanged
 
diff --git a/LayoutTests/accessibility/mac/value-change-userinfo-expected.txt b/LayoutTests/accessibility/mac/value-change-userinfo-expected.txt
deleted file mode 100644 (file)
index 393aabe..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-0234567890
-This tests value change notifications user info data.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS addedNotification is true
-PASS results[resultIndex]["AXTextStateChangeType"] is AXTextStateChangeTypeEdit
-PASS results[resultIndex]["AXTextChangeValues"][0]["AXTextChangeValue"] is "0"
-PASS results[resultIndex]["AXTextChangeValues"][0]["AXTextEditType"] is AXTextEditTypeCut
-PASS results[resultIndex]["AXTextStateChangeType"] is AXTextStateChangeTypeEdit
-PASS results[resultIndex]["AXTextChangeValues"][0]["AXTextChangeValue"] is "1"
-PASS results[resultIndex]["AXTextChangeValues"][0]["AXTextEditType"] is AXTextEditTypeDelete
-PASS results[resultIndex]["AXTextStateChangeType"] is AXTextStateChangeTypeEdit
-PASS results[resultIndex]["AXTextChangeValues"][0]["AXTextChangeValue"] is "0"
-PASS results[resultIndex]["AXTextChangeValues"][0]["AXTextEditType"] is AXTextEditTypePaste
-PASS results[resultIndex]["AXTextStateChangeType"] is AXTextStateChangeTypeEdit
-PASS results[resultIndex]["AXTextChangeValues"][0]["AXTextChangeValue"] is "234567890"
-PASS results[resultIndex]["AXTextChangeValues"][0]["AXTextEditType"] is AXTextEditTypePaste
-PASS results[resultIndex]["AXTextStateChangeType"] is AXTextStateChangeTypeEdit
-PASS results[resultIndex]["AXTextChangeValues"][0]["AXTextChangeValue"] is "1"
-PASS results[resultIndex]["AXTextChangeValues"][0]["AXTextEditType"] is AXTextEditTypeTyping
-PASS results[resultIndex]["AXTextStateChangeType"] is AXTextStateChangeTypeEdit
-PASS results[resultIndex]["AXTextChangeValues"][0]["AXTextChangeValue"] is "1"
-PASS results[resultIndex]["AXTextChangeValues"][0]["AXTextEditType"] is AXTextEditTypeDelete
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/LayoutTests/accessibility/mac/value-change-userinfo.html b/LayoutTests/accessibility/mac/value-change-userinfo.html
deleted file mode 100644 (file)
index 2eab48f..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
-<html>
-<head>
-    <script src="../../resources/js-test-pre.js"></script>
-    <script src="../../editing/editing.js"></script>
-</head>
-<body id="body">
-
-    <div role="textbox" tabindex=0 id="textbox" contenteditable=true>01234567890</div>
-
-    <p id="description"></p>
-    <div id="console"></div>
-    <div id="notifications"></div>
-
-    <script>
-
-        description("This tests value change notifications user info data.");
-
-        var AXTextStateChangeTypeEdit = 1;
-
-        var AXTextEditTypeDelete = 1;
-        var AXTextEditTypeInsert = AXTextEditTypeDelete + 1;
-        var AXTextEditTypeTyping = AXTextEditTypeInsert + 1;
-        var AXTextEditTypeDictation = AXTextEditTypeTyping + 1;
-        var AXTextEditTypeCut = AXTextEditTypeDictation + 1
-        var AXTextEditTypePaste = AXTextEditTypeCut + 1;
-
-        var webArea = 0;
-        var count = 0;
-        var results = [];
-        var resultIndex = 0;
-        function notificationCallback(notification, userInfo) {
-            if (notification == "AXValueChanged") {
-                count++;
-                results.push(userInfo);
-                if (count == 6) {
-
-                    shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeEdit");
-                    shouldBe("results[resultIndex][\"AXTextChangeValues\"][0][\"AXTextChangeValue\"]", "\"0\"");
-                    shouldBe("results[resultIndex][\"AXTextChangeValues\"][0][\"AXTextEditType\"]", "AXTextEditTypeCut");
-
-                    resultIndex++;
-                    shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeEdit");
-                    shouldBe("results[resultIndex][\"AXTextChangeValues\"][0][\"AXTextChangeValue\"]", "\"1\"");
-                    shouldBe("results[resultIndex][\"AXTextChangeValues\"][0][\"AXTextEditType\"]", "AXTextEditTypeDelete");
-
-                    resultIndex++;
-                    shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeEdit");
-                    shouldBe("results[resultIndex][\"AXTextChangeValues\"][0][\"AXTextChangeValue\"]", "\"0\"");
-                    shouldBe("results[resultIndex][\"AXTextChangeValues\"][0][\"AXTextEditType\"]", "AXTextEditTypePaste");
-
-                    resultIndex++;
-                    shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeEdit");
-                    shouldBe("results[resultIndex][\"AXTextChangeValues\"][0][\"AXTextChangeValue\"]", "\"234567890\"");
-                    shouldBe("results[resultIndex][\"AXTextChangeValues\"][0][\"AXTextEditType\"]", "AXTextEditTypePaste");
-
-                    resultIndex++;
-                    shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeEdit");
-                    shouldBe("results[resultIndex][\"AXTextChangeValues\"][0][\"AXTextChangeValue\"]", "\"1\"");
-                    shouldBe("results[resultIndex][\"AXTextChangeValues\"][0][\"AXTextEditType\"]", "AXTextEditTypeTyping");
-
-                    resultIndex++;
-                    shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeEdit");
-                    shouldBe("results[resultIndex][\"AXTextChangeValues\"][0][\"AXTextChangeValue\"]", "\"1\"");
-                    shouldBe("results[resultIndex][\"AXTextChangeValues\"][0][\"AXTextEditType\"]", "AXTextEditTypeDelete");
-
-                    webArea.removeNotificationListener();
-                    window.testRunner.notifyDone();
-                }
-            }
-        }
-
-        if (window.accessibilityController) {
-             window.testRunner.waitUntilDone();
-
-            accessibilityController.enableEnhancedAccessibility(true);
-
-            webArea = accessibilityController.rootElement.childAtIndex(0);
-            var addedNotification = webArea.addNotificationListener(notificationCallback);
-            shouldBe("addedNotification", "true");
-
-            var textbox = document.getElementById("textbox");
-            textbox.focus();
-
-            execSetSelectionCommand(textbox, 0);
-            execExtendSelectionRightByCharacterCommand();
-            cutCommand();
-            execExtendSelectionRightByCharacterCommand();
-            pasteCommand();
-            eventSender.keyDown("1");
-            deleteCommand();
-        }
-    </script>
-
-    <script src="../../resources/js-test-post.js"></script>
-</body>
-</html>
diff --git a/LayoutTests/accessibility/mac/value-change/value-change-helpers.js b/LayoutTests/accessibility/mac/value-change/value-change-helpers.js
new file mode 100644 (file)
index 0000000..7777d21
--- /dev/null
@@ -0,0 +1,88 @@
+var AXTextStateChangeTypeEdit = 1;
+
+var AXTextEditTypeDelete = 1;
+var AXTextEditTypeInsert = AXTextEditTypeDelete + 1;
+var AXTextEditTypeTyping = AXTextEditTypeInsert + 1;
+var AXTextEditTypeDictation = AXTextEditTypeTyping + 1;
+var AXTextEditTypeCut = AXTextEditTypeDictation + 1
+var AXTextEditTypePaste = AXTextEditTypeCut + 1;
+
+function stringForEditType(editType) {
+    if (editType == AXTextEditTypeDelete) {
+        return "Delete";
+    } else if (editType == AXTextEditTypeInsert) {
+        return "Insert";
+    } else if (editType == AXTextEditTypeTyping) {
+        return "Typing";
+    } else if (editType == AXTextEditTypeDictation) {
+        return "Dictation";
+    } else if (editType == AXTextEditTypeCut) {
+        return "Cut";
+    } else if (editType == AXTextEditTypePaste) {
+        return "Paste";
+    }
+    return "Unknown";
+}
+
+function bump(value) {
+    resultIndex++;
+    expectedValues[resultIndex] = value
+}
+
+function shouldBeInsert(value) {
+    bump(value);
+    shouldBe("actualChangeTypes[resultIndex]", "AXTextStateChangeTypeEdit");
+    shouldBe("actualChangeValues[resultIndex]", "expectedValues[resultIndex]");
+    shouldBe("actualEditTypes[resultIndex]", "\"Insert\"");
+}
+
+function shouldBeTyping(value) {
+    bump(value);
+    shouldBe("actualChangeTypes[resultIndex]", "AXTextStateChangeTypeEdit");
+    shouldBe("actualChangeValues[resultIndex]", "expectedValues[resultIndex]");
+    shouldBe("actualEditTypes[resultIndex]", "\"Typing\"");
+}
+
+function shouldBeDelete(value) {
+    bump(value);
+    shouldBe("actualChangeTypes[resultIndex]", "AXTextStateChangeTypeEdit");
+    shouldBe("actualChangeValues[resultIndex]", "expectedValues[resultIndex]");
+    shouldBe("actualEditTypes[resultIndex]", "\"Delete\"");
+}
+
+function shouldBeCut(value) {
+    bump(value);
+    shouldBe("actualChangeTypes[resultIndex]", "AXTextStateChangeTypeEdit");
+    shouldBe("actualChangeValues[resultIndex]", "expectedValues[resultIndex]");
+    shouldBe("actualEditTypes[resultIndex]", "\"Cut\"");
+}
+
+function shouldBePaste(value) {
+    bump(value);
+    shouldBe("actualChangeTypes[resultIndex]", "AXTextStateChangeTypeEdit");
+    shouldBe("actualChangeValues[resultIndex]", "expectedValues[resultIndex]");
+    shouldBe("actualEditTypes[resultIndex]", "\"Paste\"");
+}
+
+function shouldBeReplace(deletedValue, insertedValue) {
+    bump([deletedValue, insertedValue]);
+    shouldBe("actualChangeTypes[resultIndex]", "AXTextStateChangeTypeEdit");
+    shouldBe("actualChangeValues[resultIndex][0]", "expectedValues[resultIndex][0]");
+    shouldBe("actualEditTypes[resultIndex][0]", "\"Delete\"");
+    shouldBe("actualChangeValues[resultIndex][1]", "expectedValues[resultIndex][1]");
+}
+
+function shouldBePasteReplace(deletedValue, insertedValue) {
+    shouldBeReplace(deletedValue, insertedValue);
+    shouldBe("actualEditTypes[resultIndex][1]", "\"Paste\"");
+}
+
+function shouldBeTypingReplace(deletedValue, insertedValue) {
+    shouldBeReplace(deletedValue, insertedValue);
+    shouldBe("actualEditTypes[resultIndex][1]", "\"Typing\"");
+}
+
+function shouldBeInsertReplace(deletedValue, insertedValue) {
+    shouldBeReplace(deletedValue, insertedValue);
+    shouldBe("actualEditTypes[resultIndex][1]", "\"Insert\"");
+}
\ No newline at end of file
diff --git a/LayoutTests/accessibility/mac/value-change/value-change-user-info-contenteditable-expected.txt b/LayoutTests/accessibility/mac/value-change/value-change-user-info-contenteditable-expected.txt
new file mode 100644 (file)
index 0000000..727bd09
--- /dev/null
@@ -0,0 +1,44 @@
+223
+This tests value change notifications user info data.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS addedNotification is true
+PASS actualChangeTypes[resultIndex] is AXTextStateChangeTypeEdit
+PASS actualChangeValues[resultIndex] is expectedValues[resultIndex]
+PASS actualEditTypes[resultIndex] is "Typing"
+PASS actualChangeTypes[resultIndex] is AXTextStateChangeTypeEdit
+PASS actualChangeValues[resultIndex] is expectedValues[resultIndex]
+PASS actualEditTypes[resultIndex] is "Delete"
+PASS actualChangeTypes[resultIndex] is AXTextStateChangeTypeEdit
+PASS actualChangeValues[resultIndex] is expectedValues[resultIndex]
+PASS actualEditTypes[resultIndex] is "Typing"
+PASS actualChangeTypes[resultIndex] is AXTextStateChangeTypeEdit
+PASS actualChangeValues[resultIndex] is expectedValues[resultIndex]
+PASS actualEditTypes[resultIndex] is "Delete"
+PASS actualChangeTypes[resultIndex] is AXTextStateChangeTypeEdit
+PASS actualChangeValues[resultIndex][0] is expectedValues[resultIndex][0]
+PASS actualEditTypes[resultIndex][0] is "Delete"
+PASS actualChangeValues[resultIndex][1] is expectedValues[resultIndex][1]
+PASS actualEditTypes[resultIndex][1] is "Typing"
+PASS actualChangeTypes[resultIndex] is AXTextStateChangeTypeEdit
+PASS actualChangeValues[resultIndex] is expectedValues[resultIndex]
+PASS actualEditTypes[resultIndex] is "Cut"
+PASS actualChangeTypes[resultIndex] is AXTextStateChangeTypeEdit
+PASS actualChangeValues[resultIndex] is expectedValues[resultIndex]
+PASS actualEditTypes[resultIndex] is "Paste"
+PASS actualChangeTypes[resultIndex] is AXTextStateChangeTypeEdit
+PASS actualChangeValues[resultIndex][0] is expectedValues[resultIndex][0]
+PASS actualEditTypes[resultIndex][0] is "Delete"
+PASS actualChangeValues[resultIndex][1] is expectedValues[resultIndex][1]
+PASS actualEditTypes[resultIndex][1] is "Paste"
+PASS actualChangeTypes[resultIndex] is AXTextStateChangeTypeEdit
+PASS actualChangeValues[resultIndex][0] is expectedValues[resultIndex][0]
+PASS actualEditTypes[resultIndex][0] is "Delete"
+PASS actualChangeValues[resultIndex][1] is expectedValues[resultIndex][1]
+PASS actualEditTypes[resultIndex][1] is "Typing"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/accessibility/mac/value-change/value-change-user-info-contenteditable.html b/LayoutTests/accessibility/mac/value-change/value-change-user-info-contenteditable.html
new file mode 100644 (file)
index 0000000..f1f8801
--- /dev/null
@@ -0,0 +1,102 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+    <script src="../../../resources/js-test-pre.js"></script>
+    <script src="../../../editing/editing.js"></script>
+    <script src="value-change-helpers.js"></script>
+</head>
+<body id="body">
+
+    <div role="textbox" id="textbox" contenteditable=true>123</div>
+
+    <p id="description"></p>
+    <div id="console"></div>
+    <div id="notifications"></div>
+
+    <script>
+
+        description("This tests value change notifications user info data.");
+
+        var webArea = 0;
+        var count = 0;
+
+        var resultIndex = -1;
+
+        var actualChangeTypes = [];
+        var actualChangeValues = [];
+        var actualEditTypes = [];
+
+        var expectedValues = [];
+
+        function notificationCallback(notification, userInfo) {
+            if (notification == "AXValueChanged") {
+
+                actualChangeTypes[count] = userInfo["AXTextStateChangeType"];
+                var changes = userInfo["AXTextChangeValues"];
+                if (changes && changes.length == 1) {
+                    var change = changes[0];
+                    actualChangeValues[count] = change["AXTextChangeValue"];
+                    actualEditTypes[count] = stringForEditType(change["AXTextEditType"])
+                } else if (changes && changes.length == 2) {
+                    var change1 = changes[0];
+                    var change2 = changes[1];
+                    actualChangeValues[count] = [change1["AXTextChangeValue"], change2["AXTextChangeValue"]];
+                    actualEditTypes[count] = [stringForEditType(change1["AXTextEditType"]), stringForEditType(change2["AXTextEditType"])];
+                }
+
+                count++;
+                if (count == 9) {
+
+                    shouldBeTyping("1");
+                    shouldBeDelete("1");
+                    shouldBeTyping("\n");
+                    shouldBeDelete("\n");
+                    shouldBeTypingReplace("1", "2");
+                    shouldBeCut("2");
+                    shouldBePaste("2");
+                    shouldBePasteReplace("3", "2");
+                    shouldBeTypingReplace("2", "3");
+
+                    webArea.removeNotificationListener();
+                    window.testRunner.notifyDone();
+                }
+            }
+        }
+
+        if (window.accessibilityController) {
+             window.testRunner.waitUntilDone();
+
+            accessibilityController.enableEnhancedAccessibility(true);
+
+            webArea = accessibilityController.rootElement.childAtIndex(0);
+            var addedNotification = webArea.addNotificationListener(notificationCallback);
+            shouldBe("addedNotification", "true");
+
+            var textbox = document.getElementById("textbox");
+            textbox.focus();
+
+            eventSender.keyDown("1");
+            deleteCommand();
+
+            eventSender.keyDown("\n");
+            deleteCommand();
+
+            execExtendSelectionRightByCharacterCommand();
+            eventSender.keyDown("2");
+
+            execExtendSelectionRightByCharacterCommand();
+            cutCommand();
+
+            pasteCommand();
+
+            execExtendSelectionRightByCharacterCommand();
+            pasteCommand();
+
+            execExtendSelectionLeftByCharacterCommand();
+            eventSender.keyDown("3");
+        }
+    </script>
+
+    <script src="../../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/accessibility/mac/value-change/value-change-user-info-textarea-expected.txt b/LayoutTests/accessibility/mac/value-change/value-change-user-info-textarea-expected.txt
new file mode 100644 (file)
index 0000000..a75973d
--- /dev/null
@@ -0,0 +1,44 @@
+
+This tests value change notifications user info data.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS addedNotification is true
+PASS actualChangeTypes[resultIndex] is AXTextStateChangeTypeEdit
+PASS actualChangeValues[resultIndex] is expectedValues[resultIndex]
+PASS actualEditTypes[resultIndex] is "Typing"
+PASS actualChangeTypes[resultIndex] is AXTextStateChangeTypeEdit
+PASS actualChangeValues[resultIndex] is expectedValues[resultIndex]
+PASS actualEditTypes[resultIndex] is "Delete"
+PASS actualChangeTypes[resultIndex] is AXTextStateChangeTypeEdit
+PASS actualChangeValues[resultIndex] is expectedValues[resultIndex]
+PASS actualEditTypes[resultIndex] is "Typing"
+PASS actualChangeTypes[resultIndex] is AXTextStateChangeTypeEdit
+PASS actualChangeValues[resultIndex] is expectedValues[resultIndex]
+PASS actualEditTypes[resultIndex] is "Delete"
+PASS actualChangeTypes[resultIndex] is AXTextStateChangeTypeEdit
+PASS actualChangeValues[resultIndex][0] is expectedValues[resultIndex][0]
+PASS actualEditTypes[resultIndex][0] is "Delete"
+PASS actualChangeValues[resultIndex][1] is expectedValues[resultIndex][1]
+PASS actualEditTypes[resultIndex][1] is "Typing"
+PASS actualChangeTypes[resultIndex] is AXTextStateChangeTypeEdit
+PASS actualChangeValues[resultIndex] is expectedValues[resultIndex]
+PASS actualEditTypes[resultIndex] is "Cut"
+PASS actualChangeTypes[resultIndex] is AXTextStateChangeTypeEdit
+PASS actualChangeValues[resultIndex] is expectedValues[resultIndex]
+PASS actualEditTypes[resultIndex] is "Paste"
+PASS actualChangeTypes[resultIndex] is AXTextStateChangeTypeEdit
+PASS actualChangeValues[resultIndex][0] is expectedValues[resultIndex][0]
+PASS actualEditTypes[resultIndex][0] is "Delete"
+PASS actualChangeValues[resultIndex][1] is expectedValues[resultIndex][1]
+PASS actualEditTypes[resultIndex][1] is "Paste"
+PASS actualChangeTypes[resultIndex] is AXTextStateChangeTypeEdit
+PASS actualChangeValues[resultIndex][0] is expectedValues[resultIndex][0]
+PASS actualEditTypes[resultIndex][0] is "Delete"
+PASS actualChangeValues[resultIndex][1] is expectedValues[resultIndex][1]
+PASS actualEditTypes[resultIndex][1] is "Typing"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/accessibility/mac/value-change/value-change-user-info-textarea.html b/LayoutTests/accessibility/mac/value-change/value-change-user-info-textarea.html
new file mode 100644 (file)
index 0000000..aea6e87
--- /dev/null
@@ -0,0 +1,104 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+    <script src="../../../resources/js-test-pre.js"></script>
+    <script src="../../../editing/editing.js"></script>
+    <script src="value-change-helpers.js"></script>
+</head>
+<body id="body">
+
+    <textarea id="textbox">456</textarea>
+
+    <p id="description"></p>
+    <div id="console"></div>
+    <div id="notifications"></div>
+
+    <script>
+
+        description("This tests value change notifications user info data.");
+
+        var webArea = 0;
+        var count = 0;
+
+        var resultIndex = -1;
+
+        var actualChangeTypes = [];
+        var actualChangeValues = [];
+        var actualEditTypes = [];
+
+        var expectedValues = [];
+
+        function notificationCallback(notification, userInfo) {
+            if (notification == "AXValueChanged") {
+
+                actualChangeTypes[count] = userInfo["AXTextStateChangeType"];
+                var changes = userInfo["AXTextChangeValues"];
+                if (changes && changes.length == 1) {
+                    var change = changes[0];
+                    actualChangeValues[count] = change["AXTextChangeValue"];
+                    actualEditTypes[count] = stringForEditType(change["AXTextEditType"])
+                } else if (changes && changes.length == 2) {
+                    var change1 = changes[0];
+                    var change2 = changes[1];
+                    actualChangeValues[count] = [change1["AXTextChangeValue"], change2["AXTextChangeValue"]];
+                    actualEditTypes[count] = [stringForEditType(change1["AXTextEditType"]), stringForEditType(change2["AXTextEditType"])];
+                }
+
+                count++;
+
+                var expectedCount = 9;
+                if (count == expectedCount) {
+
+                    shouldBeTyping("3");
+                    shouldBeDelete("3");
+                    shouldBeTyping("\n");
+                    shouldBeDelete("\n");
+                    shouldBeTypingReplace("4", "5");
+                    shouldBeCut("5");
+                    shouldBePaste("5");
+                    shouldBePasteReplace("6", "5");
+                    shouldBeTypingReplace("5", "6");
+
+                    webArea.removeNotificationListener();
+                    window.testRunner.notifyDone();
+                }
+            }
+        }
+
+        if (window.accessibilityController) {
+             window.testRunner.waitUntilDone();
+
+            accessibilityController.enableEnhancedAccessibility(true);
+
+            webArea = accessibilityController.rootElement.childAtIndex(0);
+            var addedNotification = webArea.addNotificationListener(notificationCallback);
+            shouldBe("addedNotification", "true");
+
+            var textbox = document.getElementById("textbox");
+            textbox.focus();
+
+            eventSender.keyDown("3");
+            deleteCommand();
+
+            eventSender.keyDown("\n");
+            deleteCommand();
+
+            execExtendSelectionRightByCharacterCommand();
+            eventSender.keyDown("5");
+
+            execExtendSelectionRightByCharacterCommand();
+            cutCommand();
+
+            pasteCommand();
+
+            execExtendSelectionRightByCharacterCommand();
+            pasteCommand();
+
+            execExtendSelectionLeftByCharacterCommand();
+            eventSender.keyDown("6");
+        }
+    </script>
+
+    <script src="../../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/accessibility/mac/value-change/value-change-user-info-textfield-expected.txt b/LayoutTests/accessibility/mac/value-change/value-change-user-info-textfield-expected.txt
new file mode 100644 (file)
index 0000000..90eafc6
--- /dev/null
@@ -0,0 +1,38 @@
+
+This tests value change notifications user info data.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS addedNotification is true
+PASS actualChangeTypes[resultIndex] is AXTextStateChangeTypeEdit
+PASS actualChangeValues[resultIndex] is expectedValues[resultIndex]
+PASS actualEditTypes[resultIndex] is "Typing"
+PASS actualChangeTypes[resultIndex] is AXTextStateChangeTypeEdit
+PASS actualChangeValues[resultIndex] is expectedValues[resultIndex]
+PASS actualEditTypes[resultIndex] is "Delete"
+PASS actualChangeTypes[resultIndex] is AXTextStateChangeTypeEdit
+PASS actualChangeValues[resultIndex][0] is expectedValues[resultIndex][0]
+PASS actualEditTypes[resultIndex][0] is "Delete"
+PASS actualChangeValues[resultIndex][1] is expectedValues[resultIndex][1]
+PASS actualEditTypes[resultIndex][1] is "Typing"
+PASS actualChangeTypes[resultIndex] is AXTextStateChangeTypeEdit
+PASS actualChangeValues[resultIndex] is expectedValues[resultIndex]
+PASS actualEditTypes[resultIndex] is "Cut"
+PASS actualChangeTypes[resultIndex] is AXTextStateChangeTypeEdit
+PASS actualChangeValues[resultIndex] is expectedValues[resultIndex]
+PASS actualEditTypes[resultIndex] is "Paste"
+PASS actualChangeTypes[resultIndex] is AXTextStateChangeTypeEdit
+PASS actualChangeValues[resultIndex][0] is expectedValues[resultIndex][0]
+PASS actualEditTypes[resultIndex][0] is "Delete"
+PASS actualChangeValues[resultIndex][1] is expectedValues[resultIndex][1]
+PASS actualEditTypes[resultIndex][1] is "Paste"
+PASS actualChangeTypes[resultIndex] is AXTextStateChangeTypeEdit
+PASS actualChangeValues[resultIndex][0] is expectedValues[resultIndex][0]
+PASS actualEditTypes[resultIndex][0] is "Delete"
+PASS actualChangeValues[resultIndex][1] is expectedValues[resultIndex][1]
+PASS actualEditTypes[resultIndex][1] is "Typing"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/accessibility/mac/value-change/value-change-user-info-textfield.html b/LayoutTests/accessibility/mac/value-change/value-change-user-info-textfield.html
new file mode 100644 (file)
index 0000000..63582c4
--- /dev/null
@@ -0,0 +1,100 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+    <script src="../../../resources/js-test-pre.js"></script>
+    <script src="../../../editing/editing.js"></script>
+    <script src="value-change-helpers.js"></script>
+</head>
+<body id="body">
+
+    <input type="text" id="textbox" value="789"></input>
+
+    <p id="description"></p>
+    <div id="console"></div>
+    <div id="notifications"></div>
+
+    <script>
+
+        description("This tests value change notifications user info data.");
+
+        var webArea = 0;
+        var count = 0;
+
+        var resultIndex = -1;
+
+        var actualChangeTypes = [];
+        var actualChangeValues = [];
+        var actualEditTypes = [];
+
+        var expectedValues = [];
+
+        function notificationCallback(notification, userInfo) {
+            if (notification == "AXValueChanged") {
+
+                actualChangeTypes[count] = userInfo["AXTextStateChangeType"];
+                var changes = userInfo["AXTextChangeValues"];
+                if (changes && changes.length == 1) {
+                    var change = changes[0];
+                    actualChangeValues[count] = change["AXTextChangeValue"];
+                    actualEditTypes[count] = stringForEditType(change["AXTextEditType"])
+                } else if (changes && changes.length == 2) {
+                    var change1 = changes[0];
+                    var change2 = changes[1];
+                    actualChangeValues[count] = [change1["AXTextChangeValue"], change2["AXTextChangeValue"]];
+                    actualEditTypes[count] = [stringForEditType(change1["AXTextEditType"]), stringForEditType(change2["AXTextEditType"])];
+                }
+
+                count++;
+
+                var expectedCount = 7;
+                if (count == expectedCount) {
+
+                    shouldBeTyping("6");
+                    shouldBeDelete("6");
+                    shouldBeTypingReplace("7", "8");
+                    shouldBeCut("8");
+                    shouldBePaste("8");
+                    shouldBePasteReplace("9", "8");
+                    shouldBeTypingReplace("8", "9");
+
+                    webArea.removeNotificationListener();
+                    window.testRunner.notifyDone();
+                }
+            }
+        }
+
+        if (window.accessibilityController) {
+             window.testRunner.waitUntilDone();
+
+            accessibilityController.enableEnhancedAccessibility(true);
+
+            webArea = accessibilityController.rootElement.childAtIndex(0);
+            var addedNotification = webArea.addNotificationListener(notificationCallback);
+            shouldBe("addedNotification", "true");
+
+            var textbox = document.getElementById("textbox");
+            textbox.focus();
+            execMoveSelectionLeftByCharacterCommand();
+
+            eventSender.keyDown("6");
+            deleteCommand();
+
+            execExtendSelectionRightByCharacterCommand();
+            eventSender.keyDown("8");
+
+            execExtendSelectionRightByCharacterCommand();
+            cutCommand();
+
+            pasteCommand();
+
+            execExtendSelectionRightByCharacterCommand();
+            pasteCommand();
+
+            execExtendSelectionLeftByCharacterCommand();
+            eventSender.keyDown("9");
+        }
+    </script>
+
+    <script src="../../../resources/js-test-post.js"></script>
+</body>
+</html>
index c62a9d2..7de8265 100644 (file)
@@ -1565,8 +1565,6 @@ set(WebCore_SOURCES
     editing/RemoveNodeCommand.cpp
     editing/RemoveNodePreservingChildrenCommand.cpp
     editing/RenderedPosition.cpp
-    editing/ReplaceDeleteFromTextNodeCommand.cpp
-    editing/ReplaceInsertIntoTextNodeCommand.cpp
     editing/ReplaceNodeWithSpanCommand.cpp
     editing/ReplaceSelectionCommand.cpp
     editing/SetNodeAttributeCommand.cpp
index adb016f..aa77425 100644 (file)
@@ -1,3 +1,132 @@
+2016-04-04  Doug Russell  <d_russell@apple.com>
+
+        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:
+
 2016-04-04  Dean Jackson  <dino@apple.com>
 
         Add color-gamut media query support
index f19d116..74f8207 100644 (file)
                F52AD5E41534245F0059FBE6 /* EmptyClients.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F52AD5E31534245F0059FBE6 /* EmptyClients.cpp */; };
                F544F78815CFB2A800AF33A8 /* PlatformLocale.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F544F78615CFB2A800AF33A8 /* PlatformLocale.cpp */; };
                F544F78915CFB2A800AF33A8 /* PlatformLocale.h in Headers */ = {isa = PBXBuildFile; fileRef = F544F78715CFB2A800AF33A8 /* PlatformLocale.h */; };
-               F5528DA51AC1033E000EF7AD /* ReplaceDeleteFromTextNodeCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = F5528DA31AC1033E000EF7AD /* ReplaceDeleteFromTextNodeCommand.h */; };
-               F5528DA61AC1033E000EF7AD /* ReplaceDeleteFromTextNodeCommand.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5528DA41AC1033E000EF7AD /* ReplaceDeleteFromTextNodeCommand.cpp */; };
-               F5528DA91AC109DF000EF7AD /* ReplaceInsertIntoTextNodeCommand.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5528DA71AC109DF000EF7AD /* ReplaceInsertIntoTextNodeCommand.cpp */; };
-               F5528DAA1AC109DF000EF7AD /* ReplaceInsertIntoTextNodeCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = F5528DA81AC109DF000EF7AD /* ReplaceInsertIntoTextNodeCommand.h */; };
                F55B3DAD1251F12D003EF269 /* BaseTextInputType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F55B3D791251F12D003EF269 /* BaseTextInputType.cpp */; };
                F55B3DAE1251F12D003EF269 /* BaseTextInputType.h in Headers */ = {isa = PBXBuildFile; fileRef = F55B3D7A1251F12D003EF269 /* BaseTextInputType.h */; };
                F55B3DAF1251F12D003EF269 /* ButtonInputType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F55B3D7B1251F12D003EF269 /* ButtonInputType.cpp */; };
                F52AD5E31534245F0059FBE6 /* EmptyClients.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EmptyClients.cpp; sourceTree = "<group>"; };
                F544F78615CFB2A800AF33A8 /* PlatformLocale.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PlatformLocale.cpp; sourceTree = "<group>"; };
                F544F78715CFB2A800AF33A8 /* PlatformLocale.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlatformLocale.h; sourceTree = "<group>"; };
-               F5528DA31AC1033E000EF7AD /* ReplaceDeleteFromTextNodeCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReplaceDeleteFromTextNodeCommand.h; sourceTree = "<group>"; };
-               F5528DA41AC1033E000EF7AD /* ReplaceDeleteFromTextNodeCommand.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ReplaceDeleteFromTextNodeCommand.cpp; sourceTree = "<group>"; };
-               F5528DA71AC109DF000EF7AD /* ReplaceInsertIntoTextNodeCommand.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ReplaceInsertIntoTextNodeCommand.cpp; sourceTree = "<group>"; };
-               F5528DA81AC109DF000EF7AD /* ReplaceInsertIntoTextNodeCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReplaceInsertIntoTextNodeCommand.h; sourceTree = "<group>"; };
                F55B3D791251F12D003EF269 /* BaseTextInputType.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BaseTextInputType.cpp; sourceTree = "<group>"; };
                F55B3D7A1251F12D003EF269 /* BaseTextInputType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BaseTextInputType.h; sourceTree = "<group>"; };
                F55B3D7B1251F12D003EF269 /* ButtonInputType.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ButtonInputType.cpp; sourceTree = "<group>"; };
                                93309DB8099E64910056E581 /* RemoveNodePreservingChildrenCommand.h */,
                                9B32CDA813DF7FA900F34D13 /* RenderedPosition.cpp */,
                                9B32CDA713DF7FA900F34D13 /* RenderedPosition.h */,
-                               F5528DA41AC1033E000EF7AD /* ReplaceDeleteFromTextNodeCommand.cpp */,
-                               F5528DA31AC1033E000EF7AD /* ReplaceDeleteFromTextNodeCommand.h */,
-                               F5528DA71AC109DF000EF7AD /* ReplaceInsertIntoTextNodeCommand.cpp */,
-                               F5528DA81AC109DF000EF7AD /* ReplaceInsertIntoTextNodeCommand.h */,
                                A89CCC500F44E98100B5DA10 /* ReplaceNodeWithSpanCommand.cpp */,
                                A89CCC510F44E98100B5DA10 /* ReplaceNodeWithSpanCommand.h */,
                                93309DBA099E64910056E581 /* ReplaceSelectionCommand.cpp */,
                                BCEA4868097D93020094C9E4 /* RenderView.h in Headers */,
                                BE20507E18A458C20080647E /* RenderVTTCue.h in Headers */,
                                A871DFE40A15376B00B12A68 /* RenderWidget.h in Headers */,
-                               F5528DA51AC1033E000EF7AD /* ReplaceDeleteFromTextNodeCommand.h in Headers */,
-                               F5528DAA1AC109DF000EF7AD /* ReplaceInsertIntoTextNodeCommand.h in Headers */,
                                A89CCC530F44E98100B5DA10 /* ReplaceNodeWithSpanCommand.h in Headers */,
                                93309E0A099E64920056E581 /* ReplaceSelectionCommand.h in Headers */,
                                99CC0B5418BE9849006CEBCC /* ReplayController.h in Headers */,
                                BCEA4867097D93020094C9E4 /* RenderView.cpp in Sources */,
                                BE20507D18A458BF0080647E /* RenderVTTCue.cpp in Sources */,
                                A871DFE50A15376B00B12A68 /* RenderWidget.cpp in Sources */,
-                               F5528DA61AC1033E000EF7AD /* ReplaceDeleteFromTextNodeCommand.cpp in Sources */,
-                               F5528DA91AC109DF000EF7AD /* ReplaceInsertIntoTextNodeCommand.cpp in Sources */,
                                A89CCC520F44E98100B5DA10 /* ReplaceNodeWithSpanCommand.cpp in Sources */,
                                93309E09099E64920056E581 /* ReplaceSelectionCommand.cpp in Sources */,
                                99CC0B5318BE9849006CEBCC /* ReplayController.cpp in Sources */,
index d1c8f32..a30c317 100644 (file)
@@ -119,7 +119,31 @@ void AXComputedObjectAttributeCache::setIgnored(AXID id, AccessibilityObjectIncl
         m_idMapping.set(id, attributes);
     }
 }
-    
+
+AccessibilityReplacedText::AccessibilityReplacedText(const VisibleSelection& selection)
+{
+    if (AXObjectCache::accessibilityEnabled() && selection.isRange()) {
+        m_replacedText = AccessibilityObject::stringForVisiblePositionRange(selection);
+        m_replacedRange.startIndex.value = indexForVisiblePosition(selection.start(), m_replacedRange.startIndex.scope);
+        m_replacedRange.endIndex.value = indexForVisiblePosition(selection.end(), m_replacedRange.endIndex.scope);
+    }
+}
+
+void AccessibilityReplacedText::postTextStateChangeNotification(AXObjectCache* cache, AXTextEditType type, const String& text, const VisibleSelection& selection)
+{
+    if (!cache)
+        return;
+    if (!AXObjectCache::accessibilityEnabled())
+        return;
+
+    VisiblePosition position = selection.start();
+    Node* node = highestEditableRoot(position.deepEquivalent(), HasEditableAXRole);
+    if (m_replacedText.length())
+        cache->postTextReplacementNotification(node, AXTextEditTypeDelete, m_replacedText, type, text, position);
+    else
+        cache->postTextStateChangeNotification(node, type, text, position);
+}
+
 bool AXObjectCache::gAccessibilityEnabled = false;
 bool AXObjectCache::gAccessibilityEnhancedUserInterfaceEnabled = false;
 
@@ -1197,7 +1221,8 @@ void AXObjectCache::postTextStateChangeNotification(Node* node, AXTextEditType t
 {
     if (!node)
         return;
-    ASSERT(type != AXTextEditTypeUnknown);
+    if (type == AXTextEditTypeUnknown)
+        return;
 
     stopCachingComputedObjectAttributes();
 
@@ -1219,8 +1244,10 @@ void AXObjectCache::postTextReplacementNotification(Node* node, AXTextEditType d
 {
     if (!node)
         return;
-    ASSERT(deletionType == AXTextEditTypeDelete);
-    ASSERT(insertionType == AXTextEditTypeInsert || insertionType == AXTextEditTypeTyping || insertionType == AXTextEditTypeDictation || insertionType == AXTextEditTypePaste);
+    if (deletionType != AXTextEditTypeDelete)
+        return;
+    if (!(insertionType == AXTextEditTypeInsert || insertionType == AXTextEditTypeTyping || insertionType == AXTextEditTypeDictation || insertionType == AXTextEditTypePaste))
+        return;
 
     stopCachingComputedObjectAttributes();
 
index 7699ed7..d5e1a14 100644 (file)
@@ -97,6 +97,28 @@ private:
     HashMap<AXID, CachedAXObjectAttributes> m_idMapping;
 };
 
+struct VisiblePositionIndex {
+    int value = -1;
+    RefPtr<ContainerNode> scope;
+};
+
+struct VisiblePositionIndexRange {
+    VisiblePositionIndex startIndex;
+    VisiblePositionIndex endIndex;
+    bool isNull() const { return startIndex.value == -1 || endIndex.value == -1; }
+};
+
+class AccessibilityReplacedText {
+public:
+    AccessibilityReplacedText() { }
+    AccessibilityReplacedText(const VisibleSelection&);
+    void postTextStateChangeNotification(AXObjectCache*, AXTextEditType, const String&, const VisibleSelection&);
+    const VisiblePositionIndexRange& replacedRange() { return m_replacedRange; }
+protected:
+    String m_replacedText;
+    VisiblePositionIndexRange m_replacedRange;
+};
+
 #if !PLATFORM(COCOA)
 enum AXTextChange { AXTextInserted, AXTextDeleted, AXTextAttributesChanged };
 #endif
index 9526728..eb8f91e 100644 (file)
@@ -1258,7 +1258,7 @@ static String listMarkerTextForNode(Node* node)
 }
 
 // Returns the text associated with a list marker if this node is contained within a list item.
-String AccessibilityObject::listMarkerTextForNodeAndPosition(Node* node, const VisiblePosition& visiblePositionStart) const
+String AccessibilityObject::listMarkerTextForNodeAndPosition(Node* node, const VisiblePosition& visiblePositionStart)
 {
     // If the range does not contain the start of the line, the list marker text should not be included.
     if (!isStartOfLine(visiblePositionStart))
@@ -1305,7 +1305,7 @@ String AccessibilityObject::stringForRange(RefPtr<Range> range) const
     return builder.toString();
 }
 
-String AccessibilityObject::stringForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange) const
+String AccessibilityObject::stringForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange)
 {
     if (visiblePositionRange.isNull())
         return String();
index 0272195..7ff66d6 100644 (file)
@@ -394,6 +394,11 @@ struct VisiblePositionRange {
         , end(e)
     { }
 
+    VisiblePositionRange(const VisibleSelection& selection)
+        : start(selection.start())
+        , end(selection.end())
+    { }
+
     bool isNull() const { return start.isNull() || end.isNull(); }
 };
 
@@ -843,7 +848,7 @@ public:
     
     RefPtr<Range> rangeForPlainTextRange(const PlainTextRange&) const;
 
-    String stringForVisiblePositionRange(const VisiblePositionRange&) const;
+    static String stringForVisiblePositionRange(const VisiblePositionRange&);
     String stringForRange(RefPtr<Range>) const;
     virtual IntRect boundsForVisiblePositionRange(const VisiblePositionRange&) const { return IntRect(); }
     virtual IntRect boundsForRange(const RefPtr<Range>) const { return IntRect(); }
@@ -881,7 +886,7 @@ public:
     virtual String doAXStringForRange(const PlainTextRange&) const { return String(); }
     virtual IntRect doAXBoundsForRange(const PlainTextRange&) const { return IntRect(); }
     virtual IntRect doAXBoundsForRangeUsingCharacterOffset(const PlainTextRange&) const { return IntRect(); }
-    String listMarkerTextForNodeAndPosition(Node*, const VisiblePosition&) const;
+    static String listMarkerTextForNodeAndPosition(Node*, const VisiblePosition&);
 
     unsigned doAXLineForIndex(unsigned);
 
index b0896da..f226ae6 100644 (file)
@@ -398,7 +398,8 @@ void AXObjectCache::postTextStateChangePlatformNotification(AccessibilityObject*
         [userInfo setObject:wrapper forKey:NSAccessibilityTextChangeElement];
 
     AXPostNotificationWithUserInfo(rootWebArea()->wrapper(), NSAccessibilitySelectedTextChangedNotification, userInfo);
-    AXPostNotificationWithUserInfo(object->wrapper(), NSAccessibilitySelectedTextChangedNotification, userInfo);
+    if (rootWebArea()->wrapper() != object->wrapper())
+        AXPostNotificationWithUserInfo(object->wrapper(), NSAccessibilitySelectedTextChangedNotification, userInfo);
 
     [userInfo release];
 }
@@ -455,7 +456,8 @@ void AXObjectCache::postTextReplacementPlatformNotification(AccessibilityObject*
         [userInfo setObject:wrapper forKey:NSAccessibilityTextChangeElement];
 
     AXPostNotificationWithUserInfo(rootWebArea()->wrapper(), NSAccessibilityValueChangedNotification, userInfo);
-    AXPostNotificationWithUserInfo(object->wrapper(), NSAccessibilityValueChangedNotification, userInfo);
+    if (rootWebArea()->wrapper() != object->wrapper())
+        AXPostNotificationWithUserInfo(object->wrapper(), NSAccessibilityValueChangedNotification, userInfo);
 
     [userInfo release];
 }
index 8aa8b71..50498e7 100644 (file)
@@ -46,31 +46,12 @@ AppendNodeCommand::AppendNodeCommand(PassRefPtr<ContainerNode> parent, Ref<Node>
     ASSERT(m_parent->hasEditableStyle() || !m_parent->renderer());
 }
 
-static void sendAXTextChangedIgnoringLineBreaks(Node* node, AXTextEditType type)
-{
-    if (!node)
-        return;
-
-    String text = node->nodeValue();
-    // Don't consider linebreaks in this command
-    if (text == "\n")
-      return;
-
-    if (AXObjectCache* cache = node->document().existingAXObjectCache()) {
-        Position position = is<Text>(node) ? Position(downcast<Text>(node), 0) : createLegacyEditingPosition(node, 0);
-        cache->postTextStateChangeNotification(node, type, text, VisiblePosition(position));
-    }
-}
-
 void AppendNodeCommand::doApply()
 {
     if (!m_parent->hasEditableStyle() && m_parent->renderer())
         return;
 
     m_parent->appendChild(m_node.copyRef(), IGNORE_EXCEPTION);
-
-    if (shouldPostAccessibilityNotification())
-        sendAXTextChangedIgnoringLineBreaks(m_node.ptr(), applyEditType());
 }
 
 void AppendNodeCommand::doUnapply()
@@ -78,10 +59,6 @@ void AppendNodeCommand::doUnapply()
     if (!m_parent->hasEditableStyle())
         return;
 
-    // Need to notify this before actually deleting the text
-    if (shouldPostAccessibilityNotification())
-        sendAXTextChangedIgnoringLineBreaks(m_node.ptr(), unapplyEditType());
-
     m_node->remove(IGNORE_EXCEPTION);
 }
 
index 6d6095d..649d849 100644 (file)
@@ -26,6 +26,7 @@
 #include "config.h"
 #include "CompositeEditCommand.h"
 
+#include "AXObjectCache.h"
 #include "AppendNodeCommand.h"
 #include "ApplyStyleCommand.h"
 #include "BreakBlockquoteCommand.h"
@@ -59,8 +60,6 @@
 #include "RenderBlockFlow.h"
 #include "RenderText.h"
 #include "RenderedDocumentMarker.h"
-#include "ReplaceDeleteFromTextNodeCommand.h"
-#include "ReplaceInsertIntoTextNodeCommand.h"
 #include "ReplaceNodeWithSpanCommand.h"
 #include "ReplaceSelectionCommand.h"
 #include "ScopedEventQueue.h"
@@ -79,6 +78,119 @@ namespace WebCore {
 
 using namespace HTMLNames;
 
+int AccessibilityUndoReplacedText::indexForVisiblePosition(const VisiblePosition& position, RefPtr<ContainerNode>& scope) const
+{
+    if (position.deepEquivalent().isNull())
+        return -1;
+    return WebCore::indexForVisiblePosition(position, scope);
+}
+
+void AccessibilityUndoReplacedText::confgureTextToBeDeletedByUnapplyIndexesWithEditCommandEndingSelection(const VisibleSelection& selection)
+{
+    if (!AXObjectCache::accessibilityEnabled())
+        return;
+    if (selection.isRange() && m_textDeletedByUnapplyRange.startIndex.value == -1)
+        m_textDeletedByUnapplyRange.startIndex.value = indexForVisiblePosition(selection.start(), m_textDeletedByUnapplyRange.startIndex.scope);
+    if (m_textDeletedByUnapplyRange.endIndex.value == -1)
+        m_textDeletedByUnapplyRange.endIndex.value = indexForVisiblePosition(selection.start(), m_textDeletedByUnapplyRange.endIndex.scope);
+}
+
+void AccessibilityUndoReplacedText::confgureTextToBeDeletedByUnapplyStartIndexWithEditCommandStartingSelection(const VisibleSelection& selection)
+{
+    if (!AXObjectCache::accessibilityEnabled())
+        return;
+    if (m_textDeletedByUnapplyRange.startIndex.value == -1)
+        m_textDeletedByUnapplyRange.startIndex.value = indexForVisiblePosition(selection.start(), m_textDeletedByUnapplyRange.startIndex.scope);
+    if (selection.isRange() && m_textDeletedByUnapplyRange.endIndex.value == -1)
+        m_textDeletedByUnapplyRange.endIndex.value = indexForVisiblePosition(selection.end(), m_textDeletedByUnapplyRange.endIndex.scope);
+}
+
+void AccessibilityUndoReplacedText::setTextInsertedByUnapplyRange(const VisiblePositionIndexRange& range)
+{
+    m_textInsertedByUnapplyRange = range;
+}
+
+void AccessibilityUndoReplacedText::captureTextToBeDeletedByUnapply()
+{
+    if (!AXObjectCache::accessibilityEnabled())
+        return;
+    m_replacedText = textInsertedByReapply();
+}
+
+void AccessibilityUndoReplacedText::captureTextToBeDeletedByReapply()
+{
+    if (!AXObjectCache::accessibilityEnabled())
+        return;
+    m_replacedText = textInsertedByUnapply();
+}
+
+static String stringForVisiblePositionIndexRange(const VisiblePositionIndexRange& range)
+{
+    if (range.isNull())
+        return String();
+    VisiblePosition start = visiblePositionForIndex(range.startIndex.value, range.startIndex.scope.get());
+    VisiblePosition end = visiblePositionForIndex(range.endIndex.value, range.endIndex.scope.get());
+    return AccessibilityObject::stringForVisiblePositionRange(VisiblePositionRange(start, end));
+}
+
+String AccessibilityUndoReplacedText::textInsertedByUnapply()
+{
+    if (!AXObjectCache::accessibilityEnabled())
+        return String();
+    return stringForVisiblePositionIndexRange(m_textInsertedByUnapplyRange);
+}
+
+String AccessibilityUndoReplacedText::textInsertedByReapply()
+{
+    if (!AXObjectCache::accessibilityEnabled())
+        return String();
+    return stringForVisiblePositionIndexRange(m_textDeletedByUnapplyRange);
+}
+
+static void postTextStateChangeNotification(AXObjectCache* cache, const VisiblePosition& position, const String& deletedText, const String& insertedText)
+{
+    ASSERT(cache);
+    Node* node = highestEditableRoot(position.deepEquivalent(), HasEditableAXRole);
+    if (!node)
+        return;
+    if (insertedText.length() && deletedText.length())
+        cache->postTextReplacementNotification(node, AXTextEditTypeDelete, insertedText, AXTextEditTypeInsert, deletedText, position);
+    else if (deletedText.length())
+        cache->postTextStateChangeNotification(node, AXTextEditTypeInsert, deletedText, position);
+    else if (insertedText.length())
+        cache->postTextStateChangeNotification(node, AXTextEditTypeDelete, insertedText, position);
+}
+
+void AccessibilityUndoReplacedText::postTextStateChangeNotificationForUnapply(AXObjectCache* cache)
+{
+    if (!cache)
+        return;
+    if (!AXObjectCache::accessibilityEnabled())
+        return;
+    if (m_textInsertedByUnapplyRange.isNull())
+        return;
+    VisiblePosition position = visiblePositionForIndex(m_textInsertedByUnapplyRange.endIndex.value, m_textInsertedByUnapplyRange.endIndex.scope.get());
+    if (position.isNull())
+        return;
+    postTextStateChangeNotification(cache, position, textInsertedByUnapply(), m_replacedText);
+    m_replacedText = String();
+}
+
+void AccessibilityUndoReplacedText::postTextStateChangeNotificationForReapply(AXObjectCache* cache)
+{
+    if (!cache)
+        return;
+    if (!AXObjectCache::accessibilityEnabled())
+        return;
+    if (m_textDeletedByUnapplyRange.isNull())
+        return;
+    VisiblePosition position = visiblePositionForIndex(m_textDeletedByUnapplyRange.startIndex.value, m_textDeletedByUnapplyRange.startIndex.scope.get());
+    if (position.isNull())
+        return;
+    postTextStateChangeNotification(cache, position, m_replacedText, textInsertedByReapply());
+    m_replacedText = String();
+}
+
 Ref<EditCommandComposition> EditCommandComposition::create(Document& document,
     const VisibleSelection& startingSelection, const VisibleSelection& endingSelection, EditAction editAction)
 {
@@ -93,6 +205,7 @@ EditCommandComposition::EditCommandComposition(Document& document, const Visible
     , m_endingRootEditableElement(endingSelection.rootEditableElement())
     , m_editAction(editAction)
 {
+    m_replacedText.confgureTextToBeDeletedByUnapplyStartIndexWithEditCommandStartingSelection(startingSelection);
 }
 
 void EditCommandComposition::unapply()
@@ -101,6 +214,8 @@ void EditCommandComposition::unapply()
     RefPtr<Frame> frame = m_document->frame();
     ASSERT(frame);
 
+    m_replacedText.captureTextToBeDeletedByUnapply();
+
     // Changes to the document may have been made since the last editing operation that require a layout, as in <rdar://problem/5658603>.
     // Low level operations, like RemoveNodeCommand, don't require a layout because the high level operations that use them perform one
     // if one is necessary (like for the creation of VisiblePositions).
@@ -119,6 +234,9 @@ void EditCommandComposition::unapply()
         m_commands[i - 1]->doUnapply();
 
     frame->editor().unappliedEditing(this);
+
+    if (AXObjectCache::accessibilityEnabled())
+        m_replacedText.postTextStateChangeNotificationForUnapply(m_document->existingAXObjectCache());
 }
 
 void EditCommandComposition::reapply()
@@ -127,6 +245,8 @@ void EditCommandComposition::reapply()
     RefPtr<Frame> frame = m_document->frame();
     ASSERT(frame);
 
+    m_replacedText.captureTextToBeDeletedByReapply();
+
     // Changes to the document may have been made since the last editing operation that require a layout, as in <rdar://problem/5658603>.
     // Low level operations, like RemoveNodeCommand, don't require a layout because the high level operations that use them perform one
     // if one is necessary (like for the creation of VisiblePositions).
@@ -136,6 +256,9 @@ void EditCommandComposition::reapply()
         command->doReapply();
 
     frame->editor().reappliedEditing(this);
+
+    if (AXObjectCache::accessibilityEnabled())
+        m_replacedText.postTextStateChangeNotificationForReapply(m_document->existingAXObjectCache());
 }
 
 void EditCommandComposition::append(SimpleEditCommand* command)
@@ -147,12 +270,19 @@ void EditCommandComposition::setStartingSelection(const VisibleSelection& select
 {
     m_startingSelection = selection;
     m_startingRootEditableElement = selection.rootEditableElement();
+    m_replacedText.confgureTextToBeDeletedByUnapplyStartIndexWithEditCommandStartingSelection(selection);
 }
 
 void EditCommandComposition::setEndingSelection(const VisibleSelection& selection)
 {
     m_endingSelection = selection;
     m_endingRootEditableElement = selection.rootEditableElement();
+    m_replacedText.confgureTextToBeDeletedByUnapplyIndexesWithEditCommandEndingSelection(selection);
+}
+
+void EditCommandComposition::setTextInsertedByUnapplyRange(const VisiblePositionIndexRange& range)
+{
+    m_replacedText.setTextInsertedByUnapplyRange(range);
 }
 
 #ifndef NDEBUG
@@ -163,24 +293,6 @@ void EditCommandComposition::getNodesInCommand(HashSet<Node*>& nodes)
 }
 #endif
 
-AXTextEditType EditCommandComposition::unapplyEditType() const
-{
-    switch (editingAction()) {
-    case EditActionCut:
-    case EditActionDelete:
-        return AXTextEditTypeInsert;
-    case EditActionDictation:
-    case EditActionInsert:
-    case EditActionPaste:
-    case EditActionTyping:
-        return AXTextEditTypeDelete;
-    // Include default case for unhandled EditAction cases.
-    default:
-        break;
-    }
-    return AXTextEditTypeUnknown;
-}
-
 void applyCommand(PassRefPtr<CompositeEditCommand> command)
 {
     command->apply();
@@ -414,7 +526,7 @@ void CompositeEditCommand::removeNode(PassRefPtr<Node> node, ShouldAssumeContent
 {
     if (!node || !node->nonShadowBoundaryParentNode())
         return;
-    applyCommandToComposite(RemoveNodeCommand::create(*node, shouldAssumeContentIsAlwaysEditable));
+    applyCommandToComposite(RemoveNodeCommand::create(*node, shouldAssumeContentIsAlwaysEditable, editingAction()));
 }
 
 void CompositeEditCommand::removeNodePreservingChildren(PassRefPtr<Node> node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
@@ -558,10 +670,9 @@ void CompositeEditCommand::deleteTextFromNode(PassRefPtr<Text> node, unsigned of
 void CompositeEditCommand::replaceTextInNode(PassRefPtr<Text> prpNode, unsigned offset, unsigned count, const String& replacementText)
 {
     RefPtr<Text> node(prpNode);
-    RefPtr<DeleteFromTextNodeCommand> deleteCommand = ReplaceDeleteFromTextNodeCommand::create(WTFMove(node), offset, count);
-    applyCommandToComposite(deleteCommand);
+    applyCommandToComposite(DeleteFromTextNodeCommand::create(WTFMove(node), offset, count));
     if (!replacementText.isEmpty())
-        applyCommandToComposite(ReplaceInsertIntoTextNodeCommand::create(WTFMove(node), offset, replacementText, deleteCommand->deletedText(), editingAction()));
+        applyCommandToComposite(InsertIntoTextNodeCommand::create(WTFMove(node), offset, replacementText, editingAction()));
 }
 
 Position CompositeEditCommand::replaceSelectedTextInNode(const String& text)
@@ -638,10 +749,20 @@ void CompositeEditCommand::insertNodeAtTabSpanPosition(PassRefPtr<Node> node, co
     insertNodeAt(node, positionOutsideTabSpan(pos));
 }
 
+static EditAction deleteSelectionEditingActionForEditingAction(EditAction editingAction)
+{
+    switch (editingAction) {
+    case EditActionCut:
+        return EditActionCut;
+    default:
+        return EditActionDelete;
+    }
+}
+
 void CompositeEditCommand::deleteSelection(bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements, bool sanitizeMarkup)
 {
     if (endingSelection().isRange())
-        applyCommandToComposite(DeleteSelectionCommand::create(document(), smartDelete, mergeBlocksAfterDelete, replace, expandForSpecialElements, sanitizeMarkup));
+        applyCommandToComposite(DeleteSelectionCommand::create(document(), smartDelete, mergeBlocksAfterDelete, replace, expandForSpecialElements, sanitizeMarkup, deleteSelectionEditingActionForEditingAction(editingAction())));
 }
 
 void CompositeEditCommand::deleteSelection(const VisibleSelection &selection, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements, bool sanitizeMarkup)
index df59307..913398d 100644 (file)
@@ -26,6 +26,7 @@
 #ifndef CompositeEditCommand_h
 #define CompositeEditCommand_h
 
+#include "AXObjectCache.h"
 #include "EditCommand.h"
 #include "CSSPropertyNames.h"
 #include "UndoStep.h"
@@ -38,9 +39,32 @@ class HTMLElement;
 class StyledElement;
 class Text;
 
+class AccessibilityUndoReplacedText {
+public:
+    AccessibilityUndoReplacedText() { }
+    void confgureTextToBeDeletedByUnapplyStartIndexWithEditCommandStartingSelection(const VisibleSelection&);
+    void confgureTextToBeDeletedByUnapplyIndexesWithEditCommandEndingSelection(const VisibleSelection&);
+    void setTextInsertedByUnapplyRange(const VisiblePositionIndexRange&);
+
+    void captureTextToBeDeletedByUnapply();
+    void captureTextToBeDeletedByReapply();
+
+    void postTextStateChangeNotificationForUnapply(AXObjectCache*);
+    void postTextStateChangeNotificationForReapply(AXObjectCache*);
+
+private:
+    int indexForVisiblePosition(const VisiblePosition&, RefPtr<ContainerNode>&) const;
+    String textInsertedByUnapply();
+    String textInsertedByReapply();
+
+    String m_replacedText;
+    VisiblePositionIndexRange m_textDeletedByUnapplyRange;
+    VisiblePositionIndexRange m_textInsertedByUnapplyRange;
+};
+
 class EditCommandComposition : public UndoStep {
 public:
-    static Ref<EditCommandComposition> create(Document&, const VisibleSelection&, const VisibleSelection&, EditAction);
+    static Ref<EditCommandComposition> create(Document&, const VisibleSelection& startingSelection, const VisibleSelection& endingSelection, EditAction);
 
     void unapply() override;
     void reapply() override;
@@ -54,13 +78,12 @@ public:
     void setEndingSelection(const VisibleSelection&);
     Element* startingRootEditableElement() const { return m_startingRootEditableElement.get(); }
     Element* endingRootEditableElement() const { return m_endingRootEditableElement.get(); }
+    void setTextInsertedByUnapplyRange(const VisiblePositionIndexRange&);
 
 #ifndef NDEBUG
     virtual void getNodesInCommand(HashSet<Node*>&);
 #endif
 
-    AXTextEditType unapplyEditType() const;
-
 private:
     EditCommandComposition(Document&, const VisibleSelection& startingSelection, const VisibleSelection& endingSelection, EditAction);
 
@@ -70,6 +93,7 @@ private:
     Vector<RefPtr<SimpleEditCommand>> m_commands;
     RefPtr<Element> m_startingRootEditableElement;
     RefPtr<Element> m_endingRootEditableElement;
+    AccessibilityUndoReplacedText m_replacedText;
     EditAction m_editAction;
 };
 
index 46bf900..cd3be85 100644 (file)
@@ -55,10 +55,6 @@ void DeleteFromTextNodeCommand::doApply()
     m_text = m_node->substringData(m_offset, m_count, ec);
     if (ec)
         return;
-    
-    // Need to notify this before actually deleting the text
-    if (shouldPostAccessibilityNotification())
-        notifyAccessibilityForTextChange(m_node.get(), applyEditType(), m_text, VisiblePosition(Position(m_node, m_offset)));
 
     m_node->deleteData(m_offset, m_count, ec);
 }
@@ -71,9 +67,6 @@ void DeleteFromTextNodeCommand::doUnapply()
         return;
 
     m_node->insertData(m_offset, m_text, IGNORE_EXCEPTION);
-
-    if (shouldPostAccessibilityNotification())
-        notifyAccessibilityForTextChange(m_node.get(), unapplyEditType(), m_text, VisiblePosition(Position(m_node, m_offset)));
 }
 
 #ifndef NDEBUG
index 6079b21..1e12ddd 100644 (file)
@@ -39,8 +39,6 @@ public:
         return adoptRef(*new DeleteFromTextNodeCommand(WTFMove(node), offset, count, editingAction));
     }
 
-    const String& deletedText();
-
 protected:
     DeleteFromTextNodeCommand(RefPtr<Text>&&, unsigned offset, unsigned count, EditAction);
 
@@ -58,11 +56,6 @@ private:
     String m_text;
 };
 
-inline const String& DeleteFromTextNodeCommand::deletedText()
-{
-    return m_text;
-}
-
 } // namespace WebCore
 
 #endif // DeleteFromTextNodeCommand_h
index 4a96156..5228230 100644 (file)
@@ -108,6 +108,7 @@ void DictationCommand::doApply()
 {
     DictationCommandLineOperation operation(this);
     forEachLineInString(m_textToInsert, operation);
+    postTextStateChangeNotification(AXTextEditTypeDictation, m_textToInsert);
 }
 
 void DictationCommand::insertTextRunWithoutNewlines(size_t lineStart, size_t lineLength)
index 9f04847..d058b0c 100644 (file)
@@ -112,80 +112,24 @@ void EditCommand::setParent(CompositeEditCommand* parent)
     }
 }
 
-AXTextEditType EditCommand::applyEditType() const
+void EditCommand::postTextStateChangeNotification(AXTextEditType type, const String& text)
 {
-    switch (editingAction()) {
-    case EditActionCut:
-        return AXTextEditTypeCut;
-    case EditActionDelete:
-        return AXTextEditTypeDelete;
-    case EditActionDictation:
-        return AXTextEditTypeDictation;
-    case EditActionInsert:
-        return AXTextEditTypeInsert;
-    case EditActionPaste:
-        return AXTextEditTypePaste;
-    case EditActionTyping:
-        return AXTextEditTypeTyping;
-    case EditActionSetColor:
-    case EditActionSetBackgroundColor:
-    case EditActionTurnOffKerning:
-    case EditActionTightenKerning:
-    case EditActionLoosenKerning:
-    case EditActionUseStandardKerning:
-    case EditActionTurnOffLigatures:
-    case EditActionUseStandardLigatures:
-    case EditActionUseAllLigatures:
-    case EditActionRaiseBaseline:
-    case EditActionLowerBaseline:
-    case EditActionSetTraditionalCharacterShape:
-    case EditActionSetFont:
-    case EditActionChangeAttributes:
-    case EditActionAlignLeft:
-    case EditActionAlignRight:
-    case EditActionCenter:
-    case EditActionJustify:
-    case EditActionSetWritingDirection:
-    case EditActionSubscript:
-    case EditActionSuperscript:
-    case EditActionUnderline:
-    case EditActionOutline:
-    case EditActionUnscript:
-    case EditActionBold:
-    case EditActionItalics:
-    case EditActionFormatBlock:
-    case EditActionIndent:
-    case EditActionOutdent:
-        return AXTextEditTypeAttributesChange;
-    // Include default case for unhandled EditAction cases.
-    default:
-        break;
-    }
-    return AXTextEditTypeUnknown;
-}
-
-AXTextEditType EditCommand::unapplyEditType() const
-{
-    switch (applyEditType()) {
-    case AXTextEditTypeUnknown:
-        return AXTextEditTypeUnknown;
-    case AXTextEditTypeDelete:
-    case AXTextEditTypeCut:
-        return AXTextEditTypeInsert;
-    case AXTextEditTypeInsert:
-    case AXTextEditTypeTyping:
-    case AXTextEditTypeDictation:
-    case AXTextEditTypePaste:
-        return AXTextEditTypeDelete;
-    case AXTextEditTypeAttributesChange:
-        return AXTextEditTypeAttributesChange;
-    }
-    return AXTextEditTypeUnknown;
+    if (!AXObjectCache::accessibilityEnabled())
+        return;
+    postTextStateChangeNotification(type, text, frame().selection().selection().start());
 }
 
-bool EditCommand::shouldPostAccessibilityNotification() const
+void EditCommand::postTextStateChangeNotification(AXTextEditType type, const String& text, const VisiblePosition& position)
 {
-    return AXObjectCache::accessibilityEnabled() && editingAction() != EditActionUnspecified;
+    if (!AXObjectCache::accessibilityEnabled())
+        return;
+    if (!text.length())
+        return;
+    AXObjectCache* cache = document().existingAXObjectCache();
+    if (!cache)
+        return;
+    Node* node = highestEditableRoot(position.deepEquivalent(), HasEditableAXRole);
+    cache->postTextStateChangeNotification(node, type, text, position);
 }
 
 SimpleEditCommand::SimpleEditCommand(Document& document, EditAction editingAction)
@@ -206,14 +150,4 @@ void SimpleEditCommand::addNodeAndDescendants(Node* startNode, HashSet<Node*>& n
 }
 #endif
 
-void SimpleEditCommand::notifyAccessibilityForTextChange(Node* node, AXTextEditType type, const String& text, const VisiblePosition& position)
-{
-    if (!AXObjectCache::accessibilityEnabled())
-        return;
-    AXObjectCache* cache = document().existingAXObjectCache();
-    if (!cache)
-        return;
-    cache->postTextStateChangeNotification(node, type, text, position);
-}
-
 } // namespace WebCore
index 5ce735a..d1657f3 100644 (file)
@@ -60,11 +60,6 @@ public:
 
     virtual void doApply() = 0;
 
-    AXTextEditType applyEditType() const;
-    AXTextEditType unapplyEditType() const;
-
-    bool shouldPostAccessibilityNotification() const;
-
 protected:
     explicit EditCommand(Document&, EditAction = EditActionUnspecified);
     EditCommand(Document&, const VisibleSelection&, const VisibleSelection&);
@@ -75,6 +70,9 @@ protected:
     void setStartingSelection(const VisibleSelection&);
     WEBCORE_EXPORT void setEndingSelection(const VisibleSelection&);
 
+    void postTextStateChangeNotification(AXTextEditType, const String&);
+    void postTextStateChangeNotification(AXTextEditType, const String&, const VisiblePosition&);
+
 private:
     Ref<Document> m_document;
     VisibleSelection m_startingSelection;
@@ -104,8 +102,6 @@ protected:
     void addNodeAndDescendants(Node*, HashSet<Node*>&);
 #endif
 
-    virtual void notifyAccessibilityForTextChange(Node*, AXTextEditType, const String&, const VisiblePosition&);
-
 private:
     bool isSimpleEditCommand() const override { return true; }
 };
index 23bc6b1..dc3ae33 100644 (file)
@@ -59,8 +59,6 @@
 #include <RemoveNodeCommand.cpp>
 #include <RemoveNodePreservingChildrenCommand.cpp>
 #include <RenderedPosition.cpp>
-#include <ReplaceDeleteFromTextNodeCommand.cpp>
-#include <ReplaceInsertIntoTextNodeCommand.cpp>
 #include <ReplaceNodeWithSpanCommand.cpp>
 #include <ReplaceSelectionCommand.cpp>
 #include <SetNodeAttributeCommand.cpp>
index d0ed619..64bde72 100644 (file)
@@ -525,6 +525,10 @@ void Editor::replaceSelectionWithFragment(PassRefPtr<DocumentFragment> fragment,
     if (selection.isNone() || !selection.isContentEditable() || !fragment)
         return;
 
+    AccessibilityReplacedText replacedText;
+    if (AXObjectCache::accessibilityEnabled() && editingAction == EditActionPaste)
+        replacedText = AccessibilityReplacedText(selection);
+
     ReplaceSelectionCommand::CommandOptions options = ReplaceSelectionCommand::PreventNesting | ReplaceSelectionCommand::SanitizeFragment;
     if (selectReplacement)
         options |= ReplaceSelectionCommand::SelectReplacement;
@@ -535,12 +539,23 @@ void Editor::replaceSelectionWithFragment(PassRefPtr<DocumentFragment> fragment,
     if (mailBlockquoteHandling == MailBlockquoteHandling::IgnoreBlockquote)
         options |= ReplaceSelectionCommand::IgnoreMailBlockquote;
 
-    applyCommand(ReplaceSelectionCommand::create(document(), fragment, options, editingAction));
+    RefPtr<ReplaceSelectionCommand> command = ReplaceSelectionCommand::create(document(), fragment, options, editingAction);
+    applyCommand(command);
     revealSelectionAfterEditingOperation();
 
     selection = m_frame.selection().selection();
-    if (selection.isInPasswordField() || !isContinuousSpellCheckingEnabled())
+    if (selection.isInPasswordField())
+        return;
+
+    if (AXObjectCache::accessibilityEnabled() && editingAction == EditActionPaste) {
+        String text = AccessibilityObject::stringForVisiblePositionRange(command->visibleSelectionForInsertedText());
+        replacedText.postTextStateChangeNotification(document().existingAXObjectCache(), AXTextEditTypePaste, text, m_frame.selection().selection());
+        command->composition()->setTextInsertedByUnapplyRange(replacedText.replacedRange());
+    }
+
+    if (!isContinuousSpellCheckingEnabled())
         return;
+
     Node* nodeToCheck = selection.rootEditableElement();
     if (!nodeToCheck)
         return;
@@ -1030,7 +1045,7 @@ void Editor::appliedEditing(PassRefPtr<CompositeEditCommand> cmd)
     // Don't clear the typing style with this selection change.  We do those things elsewhere if necessary.
     FrameSelection::SetSelectionOptions options = cmd->isDictationCommand() ? FrameSelection::DictationTriggered : 0;
     
-    changeSelectionAfterCommand(newSelection, options, cmd->applyEditType());
+    changeSelectionAfterCommand(newSelection, options);
     dispatchEditableContentChangedEvents(composition->startingRootEditableElement(), composition->endingRootEditableElement());
 
     updateEditorUINowIfScheduled();
@@ -1061,7 +1076,7 @@ void Editor::unappliedEditing(PassRefPtr<EditCommandComposition> cmd)
     notifyTextFromControls(cmd->startingRootEditableElement(), cmd->endingRootEditableElement());
 
     VisibleSelection newSelection(cmd->startingSelection());
-    changeSelectionAfterCommand(newSelection, FrameSelection::defaultSetSelectionOptions(), cmd->unapplyEditType());
+    changeSelectionAfterCommand(newSelection, FrameSelection::defaultSetSelectionOptions());
     dispatchEditableContentChangedEvents(cmd->startingRootEditableElement(), cmd->endingRootEditableElement());
 
     updateEditorUINowIfScheduled();
@@ -1266,6 +1281,18 @@ void Editor::copy()
     performCutOrCopy(CopyAction);
 }
 
+void Editor::postTextStateChangeNotificationForCut(const String& text, const VisibleSelection& selection)
+{
+    if (!AXObjectCache::accessibilityEnabled())
+        return;
+    if (!text.length())
+        return;
+    AXObjectCache* cache = document().existingAXObjectCache();
+    if (!cache)
+        return;
+    cache->postTextStateChangeNotification(selection.start().anchorNode(), AXTextEditTypeCut, text, selection.start());
+}
+
 void Editor::performCutOrCopy(EditorActionSpecifier action)
 {
     RefPtr<Range> selection = selectedRange();
@@ -1301,8 +1328,14 @@ void Editor::performCutOrCopy(EditorActionSpecifier action)
     }
 
     didWriteSelectionToPasteboard();
-    if (action == CutAction)
+    if (action == CutAction) {
+        String text;
+        if (AXObjectCache::accessibilityEnabled())
+            text = AccessibilityObject::stringForVisiblePositionRange(m_frame.selection().selection());
         deleteSelectionWithSmartDelete(canSmartCopyOrDelete(), EditActionCut);
+        if (AXObjectCache::accessibilityEnabled())
+            postTextStateChangeNotificationForCut(text, m_frame.selection().selection());
+    }
 }
 
 void Editor::paste()
@@ -2895,7 +2928,7 @@ void Editor::dismissCorrectionPanelAsIgnored()
     m_alternativeTextController->dismiss(ReasonForDismissingAlternativeTextIgnored);
 }
 
-void Editor::changeSelectionAfterCommand(const VisibleSelection& newSelection, FrameSelection::SetSelectionOptions options, AXTextStateChangeIntent intent)
+void Editor::changeSelectionAfterCommand(const VisibleSelection& newSelection, FrameSelection::SetSelectionOptions options)
 {
     // If the new selection is orphaned, then don't update the selection.
     if (newSelection.start().isOrphan() || newSelection.end().isOrphan())
@@ -2907,7 +2940,7 @@ void Editor::changeSelectionAfterCommand(const VisibleSelection& newSelection, F
     // See <rdar://problem/5729315> Some shouldChangeSelectedDOMRange contain Ranges for selections that are no longer valid
     bool selectionDidNotChangeDOMPosition = newSelection == m_frame.selection().selection();
     if (selectionDidNotChangeDOMPosition || m_frame.selection().shouldChangeSelection(newSelection))
-        m_frame.selection().setSelection(newSelection, options, intent);
+        m_frame.selection().setSelection(newSelection, options);
 
     // Some editing operations change the selection visually without affecting its position within the DOM.
     // For example when you press return in the following (the caret is marked by ^):
index 885e69f..aceedca 100644 (file)
@@ -482,7 +482,7 @@ private:
     enum SetCompositionMode { ConfirmComposition, CancelComposition };
     void setComposition(const String&, SetCompositionMode);
 
-    void changeSelectionAfterCommand(const VisibleSelection& newSelection, FrameSelection::SetSelectionOptions, AXTextStateChangeIntent = AXTextStateChangeIntent());
+    void changeSelectionAfterCommand(const VisibleSelection& newSelection, FrameSelection::SetSelectionOptions);
 
     enum EditorActionSpecifier { CutAction, CopyAction };
     void performCutOrCopy(EditorActionSpecifier);
@@ -501,6 +501,8 @@ private:
     void fillInUserVisibleForm(PasteboardURL&);
 #endif
 
+    void postTextStateChangeNotificationForCut(const String&, const VisibleSelection&);
+
     Frame& m_frame;
     RefPtr<CompositeEditCommand> m_lastEditCommand;
     RefPtr<Text> m_compositionNode;
index 0348b24..79a81ce 100644 (file)
@@ -64,9 +64,6 @@ void InsertIntoTextNodeCommand::doApply()
     }
 
     m_node->insertData(m_offset, m_text, IGNORE_EXCEPTION);
-
-    if (shouldPostAccessibilityNotification())
-        notifyAccessibilityForTextChange(m_node.get(), applyEditType(), m_text, VisiblePosition(Position(m_node, m_offset)));
 }
 
 #if PLATFORM(IOS)
@@ -81,10 +78,6 @@ void InsertIntoTextNodeCommand::doUnapply()
 {
     if (!m_node->hasEditableStyle())
         return;
-        
-    // Need to notify this before actually deleting the text
-    if (shouldPostAccessibilityNotification())
-        notifyAccessibilityForTextChange(m_node.get(), unapplyEditType(), m_text, VisiblePosition(Position(m_node, m_offset)));
 
     m_node->deleteData(m_offset, m_text.length(), IGNORE_EXCEPTION);
 }
index 50f6545..de653d6 100644 (file)
@@ -56,11 +56,6 @@ void InsertNodeBeforeCommand::doApply()
     ASSERT(isEditableNode(*parent));
 
     parent->insertBefore(*m_insertChild, m_refChild.get(), IGNORE_EXCEPTION);
-
-    if (shouldPostAccessibilityNotification()) {
-        Position position = is<Text>(m_insertChild.get()) ? Position(downcast<Text>(m_insertChild.get()), 0) : createLegacyEditingPosition(m_insertChild.get(), 0);
-        notifyAccessibilityForTextChange(m_insertChild.get(), applyEditType(), m_insertChild->nodeValue(), VisiblePosition(position));
-    }
 }
 
 void InsertNodeBeforeCommand::doUnapply()
@@ -68,12 +63,6 @@ void InsertNodeBeforeCommand::doUnapply()
     if (!isEditableNode(*m_insertChild))
         return;
 
-    // Need to notify this before actually deleting the text
-    if (shouldPostAccessibilityNotification()) {
-        Position position = is<Text>(m_insertChild.get()) ? Position(downcast<Text>(m_insertChild.get()), 0) : createLegacyEditingPosition(m_insertChild.get(), 0);
-        notifyAccessibilityForTextChange(m_insertChild.get(), unapplyEditType(), m_insertChild->nodeValue(), VisiblePosition(position));
-    }
-
     m_insertChild->remove(IGNORE_EXCEPTION);
 }
 
index f98d03a..b3df186 100644 (file)
@@ -34,8 +34,8 @@
 
 namespace WebCore {
 
-RemoveNodeCommand::RemoveNodeCommand(Ref<Node>&& node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
-    : SimpleEditCommand(node->document())
+RemoveNodeCommand::RemoveNodeCommand(Ref<Node>&& node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable, EditAction editingAction)
+    : SimpleEditCommand(node->document(), editingAction)
     , m_node(WTFMove(node))
     , m_shouldAssumeContentIsAlwaysEditable(shouldAssumeContentIsAlwaysEditable)
 {
index 17135f7..77ea82b 100644 (file)
@@ -32,13 +32,13 @@ namespace WebCore {
 
 class RemoveNodeCommand : public SimpleEditCommand {
 public:
-    static Ref<RemoveNodeCommand> create(Ref<Node>&& node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
+    static Ref<RemoveNodeCommand> create(Ref<Node>&& node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable, EditAction editingAction = EditActionUnspecified)
     {
-        return adoptRef(*new RemoveNodeCommand(WTFMove(node), shouldAssumeContentIsAlwaysEditable));
+        return adoptRef(*new RemoveNodeCommand(WTFMove(node), shouldAssumeContentIsAlwaysEditable, editingAction));
     }
 
 private:
-    RemoveNodeCommand(Ref<Node>&&, ShouldAssumeContentIsAlwaysEditable);
+    RemoveNodeCommand(Ref<Node>&&, ShouldAssumeContentIsAlwaysEditable, EditAction);
 
     void doApply() override;
     void doUnapply() override;
diff --git a/Source/WebCore/editing/ReplaceDeleteFromTextNodeCommand.cpp b/Source/WebCore/editing/ReplaceDeleteFromTextNodeCommand.cpp
deleted file mode 100644 (file)
index 1464c73..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2015 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 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 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 "ReplaceDeleteFromTextNodeCommand.h"
-
-#include "Text.h"
-
-namespace WebCore {
-    
-ReplaceDeleteFromTextNodeCommand::ReplaceDeleteFromTextNodeCommand(RefPtr<Text>&& text, unsigned offset, unsigned count)
-    : DeleteFromTextNodeCommand(WTFMove(text), offset, count, EditActionDelete)
-{
-}
-
-void ReplaceDeleteFromTextNodeCommand::notifyAccessibilityForTextChange(Node*, AXTextEditType, const String&, const VisiblePosition&)
-{
-}
-
-} // namespace WebCore
diff --git a/Source/WebCore/editing/ReplaceDeleteFromTextNodeCommand.h b/Source/WebCore/editing/ReplaceDeleteFromTextNodeCommand.h
deleted file mode 100644 (file)
index 5228283..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2015 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 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 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 ReplaceDeleteFromTextNodeCommand_h
-#define ReplaceDeleteFromTextNodeCommand_h
-
-#include "DeleteFromTextNodeCommand.h"
-
-namespace WebCore {
-
-class ReplaceDeleteFromTextNodeCommand final : public DeleteFromTextNodeCommand {
-public:
-    static Ref<ReplaceDeleteFromTextNodeCommand> create(RefPtr<Text>&& text, unsigned offset, unsigned count)
-    {
-        return adoptRef(*new ReplaceDeleteFromTextNodeCommand(WTFMove(text), offset, count));
-    }
-
-private:
-    ReplaceDeleteFromTextNodeCommand(RefPtr<Text>&&, unsigned, unsigned);
-    void notifyAccessibilityForTextChange(Node*, AXTextEditType, const String&, const VisiblePosition&) override;
-};
-
-} // namespace WebCore
-
-#endif // ReplaceDeleteFromTextNodeCommand_h
diff --git a/Source/WebCore/editing/ReplaceInsertIntoTextNodeCommand.cpp b/Source/WebCore/editing/ReplaceInsertIntoTextNodeCommand.cpp
deleted file mode 100644 (file)
index af05255..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2015 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 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 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 "ReplaceInsertIntoTextNodeCommand.h"
-
-#include "AXObjectCache.h"
-#include "Document.h"
-#include "Text.h"
-
-namespace WebCore {
-
-ReplaceInsertIntoTextNodeCommand::ReplaceInsertIntoTextNodeCommand(RefPtr<Text>&& node, unsigned offset, const String& text, const String& deletedText, EditAction editingAction)
-    : InsertIntoTextNodeCommand(WTFMove(node), offset, text, editingAction)
-    , m_deletedText(deletedText)
-{
-}
-
-void ReplaceInsertIntoTextNodeCommand::notifyAccessibilityForTextChange(Node* node, AXTextEditType type, const String& text, const VisiblePosition& position)
-{
-    if (!shouldPostAccessibilityNotification())
-        return;
-    AXObjectCache* cache = document().existingAXObjectCache();
-    if (!cache)
-        return;
-    switch (type) {
-    case AXTextEditTypeAttributesChange:
-    case AXTextEditTypeCut:
-    case AXTextEditTypeUnknown:
-        break;
-    case AXTextEditTypeDelete:
-        cache->postTextReplacementNotification(node, AXTextEditTypeDelete, text, AXTextEditTypeInsert, m_deletedText, position);
-        break;
-    case AXTextEditTypeDictation:
-    case AXTextEditTypeInsert:
-    case AXTextEditTypePaste:
-    case AXTextEditTypeTyping:
-        cache->postTextReplacementNotification(node, AXTextEditTypeDelete, m_deletedText, type, text, position);
-        break;
-    }
-}
-
-} // namespace WebCore
diff --git a/Source/WebCore/editing/ReplaceInsertIntoTextNodeCommand.h b/Source/WebCore/editing/ReplaceInsertIntoTextNodeCommand.h
deleted file mode 100644 (file)
index 80717e9..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2015 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 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 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 ReplaceInsertIntoTextNodeCommand_h
-#define ReplaceInsertIntoTextNodeCommand_h
-
-#include "InsertIntoTextNodeCommand.h"
-
-namespace WebCore {
-
-class ReplaceInsertIntoTextNodeCommand final : public InsertIntoTextNodeCommand {
-public:
-    static Ref<ReplaceInsertIntoTextNodeCommand> create(RefPtr<Text>&& node, unsigned offset, const String& text, const String& deletedText, EditAction editingAction)
-    {
-        return adoptRef(*new ReplaceInsertIntoTextNodeCommand(WTFMove(node), offset, text, deletedText, editingAction));
-    }
-
-private:
-    ReplaceInsertIntoTextNodeCommand(RefPtr<Text>&&, unsigned, const String&, const String&, EditAction);
-    void notifyAccessibilityForTextChange(Node*, AXTextEditType, const String&, const VisiblePosition&) override;
-
-    String m_deletedText;
-};
-
-} // namespace WebCore
-
-#endif // ReplaceInsertIntoTextNodeCommand_h
index bafb39e..d12cc97 100644 (file)
@@ -27,6 +27,7 @@
 #include "config.h"
 #include "ReplaceSelectionCommand.h"
 
+#include "AXObjectCache.h"
 #include "ApplyStyleCommand.h"
 #include "BeforeTextInsertedEvent.h"
 #include "BreakBlockquoteCommand.h"
@@ -47,8 +48,6 @@
 #include "RenderInline.h"
 #include "RenderObject.h"
 #include "RenderText.h"
-#include "ReplaceDeleteFromTextNodeCommand.h"
-#include "ReplaceInsertIntoTextNodeCommand.h"
 #include "SimplifyMarkupCommand.h"
 #include "SmartReplace.h"
 #include "StyleProperties.h"
@@ -960,7 +959,8 @@ void ReplaceSelectionCommand::doApply()
         bool mergeBlocksAfterDelete = shouldHandleMailBlockquote || isEndOfParagraph(visibleEnd) || isStartOfBlock(visibleStart);
         // FIXME: We should only expand to include fully selected special elements if we are copying a 
         // selection and pasting it on top of itself.
-        deleteSelection(false, mergeBlocksAfterDelete, true, false);
+        // FIXME: capturing the content of this delete would allow a replace accessibility notification instead of a simple insert
+        deleteSelection(false, mergeBlocksAfterDelete, true, false, true);
         visibleStart = endingSelection().visibleStart();
         if (fragment.hasInterchangeNewlineAtStart()) {
             if (isEndOfParagraph(visibleStart) && !isStartOfParagraph(visibleStart)) {
@@ -1367,6 +1367,9 @@ void ReplaceSelectionCommand::completeHTMLReplacement(const Position &lastPositi
     else
         return;
 
+    if (AXObjectCache::accessibilityEnabled() && editingAction() == EditActionPaste)
+        m_visibleSelectionForInsertedText = VisibleSelection(start, end);
+
     if (m_selectReplacement)
         setEndingSelection(VisibleSelection(start, end, SEL_DEFAULT_AFFINITY, endingSelection().isDirectional()));
     else
@@ -1512,6 +1515,9 @@ bool ReplaceSelectionCommand::performTrivialReplace(const ReplacementFragment& f
 
     VisibleSelection selectionAfterReplace(m_selectReplacement ? start : end, end);
 
+    if (AXObjectCache::accessibilityEnabled() && editingAction() == EditActionPaste)
+        m_visibleSelectionForInsertedText = VisibleSelection(start, end);
+
     setEndingSelection(selectionAfterReplace);
 
     return true;
index 7aa5809..08a810e 100644 (file)
@@ -53,6 +53,8 @@ public:
         return adoptRef(*new ReplaceSelectionCommand(document, WTFMove(fragment), options, editingAction));
     }
 
+    VisibleSelection visibleSelectionForInsertedText() const { return m_visibleSelectionForInsertedText; }
+
 private:
     ReplaceSelectionCommand(Document&, RefPtr<DocumentFragment>&&, CommandOptions, EditAction);
 
@@ -110,6 +112,7 @@ private:
 
     bool performTrivialReplace(const ReplacementFragment&);
 
+    VisibleSelection m_visibleSelectionForInsertedText;
     Position m_startOfInsertedContent;
     Position m_endOfInsertedContent;
     RefPtr<EditingStyle> m_insertionStyle;
index c5bdbbe..cf9c262 100644 (file)
@@ -35,8 +35,8 @@
 
 namespace WebCore {
 
-TextInsertionBaseCommand::TextInsertionBaseCommand(Document& document)
-    : CompositeEditCommand(document)
+TextInsertionBaseCommand::TextInsertionBaseCommand(Document& document, EditAction editingAction)
+    : CompositeEditCommand(document, editingAction)
 {
 }
 
index 875c587..6c723ac 100644 (file)
@@ -39,7 +39,7 @@ public:
     virtual ~TextInsertionBaseCommand() { };
 
 protected:
-    explicit TextInsertionBaseCommand(Document&);
+    explicit TextInsertionBaseCommand(Document&, EditAction = EditActionUnspecified);
     static void applyTextInsertionCommand(Frame*, TextInsertionBaseCommand&, const VisibleSelection& selectionForInsertion, const VisibleSelection& endingSelection);
 };
 
index 15b4f85..2198907 100644 (file)
@@ -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 @@ private:
 };
 
 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 @@ void TypingCommand::insertText(Document& document, const String& text, const Vis
         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 @@ void TypingCommand::insertLineBreak(Document& document, Options options)
 {
     if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*document.frame())) {
         lastTypingCommand->setShouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator);
-        lastTypingCommand->insertLineBreak();
+        lastTypingCommand->insertLineBreakAndNotifyAccessibility();
         return;
     }
 
@@ -204,7 +205,7 @@ void TypingCommand::insertLineBreak(Document& document, Options options)
 void TypingCommand::insertParagraphSeparatorInQuotedContent(Document& document)
 {
     if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*document.frame())) {
-        lastTypingCommand->insertParagraphSeparatorInQuotedContent();
+        lastTypingCommand->insertParagraphSeparatorInQuotedContentAndNotifyAccessibility();
         return;
     }
 
@@ -215,7 +216,7 @@ void TypingCommand::insertParagraphSeparator(Document& document, Options options
 {
     if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*document.frame())) {
         lastTypingCommand->setShouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator);
-        lastTypingCommand->insertParagraphSeparator();
+        lastTypingCommand->insertParagraphSeparatorAndNotifyAccessibility();
         return;
     }
 
@@ -247,6 +248,17 @@ void TypingCommand::ensureLastEditCommandHasCurrentSelectionIfOpenForMoreTyping(
 }
 #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 @@ void TypingCommand::doApply()
         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 @@ void TypingCommand::insertText(const String &text, bool selectInsertedText)
     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 @@ void TypingCommand::insertLineBreak()
     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 @@ void TypingCommand::insertParagraphSeparator()
     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 @@ void TypingCommand::insertParagraphSeparatorInQuotedContent()
     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 @@ void TypingCommand::deleteKeyPressed(TextGranularity granularity, bool shouldAdd
     
     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 @@ void TypingCommand::forwardDeleteKeyPressed(TextGranularity granularity, bool sh
     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
index b43e1eb..d3b89e3 100644 (file)
@@ -105,7 +105,6 @@ private:
     static RefPtr<TypingCommand> lastTypingCommandIfStillOpenForTyping(Frame&);
 
     virtual void doApply();
-    virtual EditAction editingAction() const;
     virtual bool isTypingCommand() const;
     virtual bool preservesTypingStyle() const { return m_preservesTypingStyle; }
     virtual bool shouldRetainAutocorrectionIndicator() const
@@ -124,6 +123,12 @@ private:
     void typingAddedToOpenCommand(ETypingCommand);
     bool makeEditableRootEmpty();
 
+    void postTextStateChangeNotificationForDeletion(const VisibleSelection&);
+    void insertTextAndNotifyAccessibility(const String &text, bool selectInsertedText);
+    void insertLineBreakAndNotifyAccessibility();
+    void insertParagraphSeparatorInQuotedContentAndNotifyAccessibility();
+    void insertParagraphSeparatorAndNotifyAccessibility();
+
     ETypingCommand m_commandType;
     String m_textToInsert;
     bool m_openForMoreTyping;